eudev/udev-add.c

289 lines
6.2 KiB
C
Raw Normal View History

/*
* udev-add.c
*
* Userspace devfs
*
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <grp.h>
#ifndef __KLIBC__
#include <pwd.h>
#endif
#include "udev.h"
#include "udev_version.h"
#include "namedev.h"
[PATCH] udevdb prototype Here's an "idea" of what I had in mind for udevdb. Let me preface the code with a few remarks: 1) I was expecting to write this udevdb for udev to keep track of devices. I was planning an external package that depends upon udev to provide an external API to the udevdb database. The calls for the interface would be read only access. Not sure how you want to do packaging, if having a separate package is ok or having it included in udev. 2) I created it as it is because udev isn't a daemon. So, the open database call doesn't take any parameters. My plan was to create a udevdb_init function that took arguments for initializing the db to start, where you could specify in memory only or a file location. This can all be filled in. 3) I hacked the Makefile to get it to work. Not sure how you'd want that in the future. 4) This assumes TDB has been installed elsewhere, you would need to edit your Makefile and point it to the header and library locations. How do you want to do TDB in udev? Do you want to just reference it and make udev dependent on that package being installed. Or should we do what samba does and include a limited tdb version in udev? 5) Again, I hacked udev into your existing code. In the future, I'd probably make a function around the filling out the udevice before calling the store command. Didn't know if you wanted to change your add device function to use struct udevice rather than having everything separate. 6) Not sure what we should include in the udevice structure that's stored by udev. I made a stab at a first shot - we can add and remove of course, this was a first pass. I've come to realize - with you including libsysfs in udev, the "external" interface that references udevdb could make use of getting information from through libsysfs from sysfs and doesn't need to be in udevdb. 7) I could write a namedevdb for namedev's device management if you wanted.
2003-08-06 13:57:23 +07:00
#include "udevdb.h"
#include "libsysfs/libsysfs.h"
#include "klibc_fixups.h"
/*
* Right now the major/minor of a device is stored in a file called
* "dev" in sysfs.
* The number is stored as:
* MM:mm
* MM is the major
* mm is the minor
* The value is in decimal.
*/
static int get_major_minor(struct sysfs_class_device *class_dev, struct udevice *udev)
{
int retval = -ENODEV;
char *dev;
dev = sysfs_get_value_from_attributes(class_dev->directory->attributes, "dev");
if (dev == NULL)
goto exit;
dbg("dev = %s", dev);
if (sscanf(dev, "%u:%u", &udev->major, &udev->minor) != 2)
goto exit;
dbg("found major = %d, minor = %d", udev->major, udev->minor);
retval = 0;
exit:
return retval;
}
/*
* we possibly want to add some symlinks here
* only numeric owner/group id's are supported
*/
static int create_node(struct udevice *dev)
{
char filename[255];
int retval = 0;
uid_t uid = 0;
gid_t gid = 0;
2003-10-23 13:48:55 +07:00
dev_t res;
strncpy(filename, udev_root, sizeof(filename));
strncat(filename, dev->name, sizeof(filename));
2003-10-23 13:48:55 +07:00
#ifdef __KLIBC__
res = (dev->major << 8) | (dev->minor);
#else
res = makedev(dev->major, dev->minor);
#endif
switch (dev->type) {
2003-08-05 11:59:50 +07:00
case 'b':
dev->mode |= S_IFBLK;
2003-08-05 11:59:50 +07:00
break;
case 'c':
case 'u':
dev->mode |= S_IFCHR;
2003-08-05 11:59:50 +07:00
break;
case 'p':
dev->mode |= S_IFIFO;
2003-08-05 11:59:50 +07:00
break;
default:
dbg("unknown node type %c\n", dev->type);
2003-08-05 11:59:50 +07:00
return -EINVAL;
}
/* create subdirectories if requested */
if (strchr(dev->name, '/')) {
char path[255];
char *pos;
struct stat stats;
strncpy(path, filename, sizeof(path));
pos = strchr(path+1, '/');
while (1) {
pos = strchr(pos+1, '/');
if (pos == NULL)
break;
*pos = 0x00;
if (stat(path, &stats)) {
retval = mkdir(path, 0755);
if (retval) {
dbg("mkdir(%s) failed with error '%s'",
path, strerror(errno));
return retval;
}
dbg("created %s", path);
}
*pos = '/';
}
}
dbg("mknod(%s, %#o, %u, %u)", filename, dev->mode, dev->major, dev->minor);
2003-10-23 13:48:55 +07:00
retval = mknod(filename, dev->mode, res);
2003-08-05 11:59:50 +07:00
if (retval)
dbg("mknod(%s, %#o, %u, %u) failed with error '%s'",
filename, dev->mode, dev->major, dev->minor, strerror(errno));
dbg("chmod(%s, %#o)", filename, dev->mode);
retval = chmod(filename, dev->mode);
if (retval)
dbg("chmod(%s, %#o) failed with error '%s'",
filename, dev->mode, strerror(errno));
if (*dev->owner) {
char *endptr;
unsigned long id = strtoul(dev->owner, &endptr, 10);
if (*endptr == 0x00)
uid = (uid_t) id;
else {
struct passwd *pw = getpwnam(dev->owner);
if (!pw)
dbg("user unknown: %s", dev->owner);
else
uid = pw->pw_uid;
}
}
if (*dev->group) {
char *endptr;
unsigned long id = strtoul(dev->group, &endptr, 10);
if (*endptr == 0x00)
gid = (gid_t) id;
else {
struct group *gr = getgrnam(dev->group);
if (!gr)
dbg("group unknown: %s", dev->group);
else
gid = gr->gr_gid;
}
}
if (uid || gid) {
dbg("chown(%s, %u, %u)", filename, uid, gid);
retval = chown(filename, uid, gid);
if (retval)
dbg("chown(%s, %u, %u) failed with error '%s'", filename,
uid, gid, strerror(errno));
}
return retval;
}
static struct sysfs_class_device *get_class_dev(char *device_name)
{
char dev_path[SYSFS_PATH_MAX];
2003-08-05 12:13:35 +07:00
struct sysfs_class_device *class_dev = NULL;
strcpy(dev_path, sysfs_path);
strcat(dev_path, device_name);
dbg("looking at %s", dev_path);
/* open up the sysfs class device for this thing... */
class_dev = sysfs_open_class_device(dev_path);
if (class_dev == NULL) {
dbg ("sysfs_open_class_device failed");
2003-08-05 12:13:35 +07:00
goto exit;
}
dbg("class_dev->name = %s", class_dev->name);
2003-08-05 12:13:35 +07:00
exit:
return class_dev;
}
/* wait for the "dev" file to show up in the directory in sysfs.
* If it doesn't happen in about 10 seconds, give up.
*/
#define SECONDS_TO_WAIT_FOR_DEV 10
static int sleep_for_dev(char *path)
{
char filename[SYSFS_PATH_MAX + 6];
int loop = SECONDS_TO_WAIT_FOR_DEV;
int retval;
strcpy(filename, sysfs_path);
strcat(filename, path);
strcat(filename, "/dev");
while (loop--) {
struct stat buf;
dbg("looking for %s", filename);
retval = stat(filename, &buf);
if (!retval)
goto exit;
/* sleep for a second or two to give the kernel a chance to
* create the dev file */
sleep(1);
}
retval = -ENODEV;
exit:
return retval;
}
int udev_add_device(char *path, char *subsystem)
{
struct sysfs_class_device *class_dev = NULL;
struct udevice dev;
int retval = -EINVAL;
memset(&dev, 0x00, sizeof(dev));
/* for now, the block layer is the only place where block devices are */
if (strcmp(subsystem, "block") == 0)
dev.type = 'b';
else
dev.type = 'c';
retval = sleep_for_dev(path);
if (retval)
goto exit;
2003-08-05 12:13:35 +07:00
class_dev = get_class_dev(path);
if (class_dev == NULL)
goto exit;
retval = get_major_minor(class_dev, &dev);
if (retval) {
dbg("get_major_minor failed");
goto exit;
}
retval = namedev_name_device(class_dev, &dev);
if (retval)
goto exit;
retval = udevdb_add_dev(path, &dev);
[PATCH] udevdb prototype Here's an "idea" of what I had in mind for udevdb. Let me preface the code with a few remarks: 1) I was expecting to write this udevdb for udev to keep track of devices. I was planning an external package that depends upon udev to provide an external API to the udevdb database. The calls for the interface would be read only access. Not sure how you want to do packaging, if having a separate package is ok or having it included in udev. 2) I created it as it is because udev isn't a daemon. So, the open database call doesn't take any parameters. My plan was to create a udevdb_init function that took arguments for initializing the db to start, where you could specify in memory only or a file location. This can all be filled in. 3) I hacked the Makefile to get it to work. Not sure how you'd want that in the future. 4) This assumes TDB has been installed elsewhere, you would need to edit your Makefile and point it to the header and library locations. How do you want to do TDB in udev? Do you want to just reference it and make udev dependent on that package being installed. Or should we do what samba does and include a limited tdb version in udev? 5) Again, I hacked udev into your existing code. In the future, I'd probably make a function around the filling out the udevice before calling the store command. Didn't know if you wanted to change your add device function to use struct udevice rather than having everything separate. 6) Not sure what we should include in the udevice structure that's stored by udev. I made a stab at a first shot - we can add and remove of course, this was a first pass. I've come to realize - with you including libsysfs in udev, the "external" interface that references udevdb could make use of getting information from through libsysfs from sysfs and doesn't need to be in udevdb. 7) I could write a namedevdb for namedev's device management if you wanted.
2003-08-06 13:57:23 +07:00
if (retval != 0)
dbg("udevdb_add_dev failed, but we are going to try to create the node anyway. "
"But remove might not work properly for this device.");
dbg("name = %s", dev.name);
retval = create_node(&dev);
exit:
if (class_dev)
sysfs_close_class_device(class_dev);
return retval;
}