X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=namedev.c;h=cff338f1a719b635c06a33c765d2376f46869f4b;hp=212d6bb68d93d5774b95ef38cf3806a8dd5a690f;hb=8ed89229bc8afed84ad6d587a6df29329bbbf440;hpb=b1c5e3339deb97f4a30053111b444489a3bc9562 diff --git a/namedev.c b/namedev.c index 212d6bb68..cff338f1a 100644 --- a/namedev.c +++ b/namedev.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "list.h" #include "udev.h" @@ -101,40 +102,6 @@ static int strcmp_pattern(const char *p, const char *s) if (strlen(b->var)) \ strcpy(a->var, b->var); -int add_config_dev(struct config_device *new_dev) -{ - struct list_head *tmp; - struct config_device *tmp_dev; - - /* update the values if we already have the device */ - list_for_each(tmp, &config_device_list) { - struct config_device *dev = list_entry(tmp, struct config_device, node); - if (strcmp_pattern(new_dev->name, dev->name)) - continue; - if (strncmp(dev->bus, new_dev->bus, sizeof(dev->name))) - continue; - copy_var(dev, new_dev, type); - copy_string(dev, new_dev, bus); - copy_string(dev, new_dev, sysfs_file); - copy_string(dev, new_dev, sysfs_value); - copy_string(dev, new_dev, id); - copy_string(dev, new_dev, place); - copy_string(dev, new_dev, kernel_name); - copy_string(dev, new_dev, exec_program); - copy_string(dev, new_dev, symlink); - return 0; - } - - /* not found, add new structure to the device list */ - tmp_dev = malloc(sizeof(*tmp_dev)); - if (!tmp_dev) - return -ENOMEM; - memcpy(tmp_dev, new_dev, sizeof(*tmp_dev)); - list_add_tail(&tmp_dev->node, &config_device_list); - //dump_config_dev(tmp_dev); - return 0; -} - int add_perm_dev(struct perm_device *new_dev) { struct list_head *tmp; @@ -185,18 +152,6 @@ static mode_t get_default_mode(struct sysfs_class_device *class_dev) return mode; } -static void build_kernel_number(struct sysfs_class_device *class_dev, struct udevice *udev) -{ - char *dig; - - /* FIXME, figure out how to handle stuff like sdaj which will not work right now. */ - dig = class_dev->name + strlen(class_dev->name); - while (isdigit(*(dig-1))) - dig--; - strfieldcpy(udev->kernel_number, dig); - dbg("kernel_number='%s'", udev->kernel_number); -} - static void apply_format(struct udevice *udev, unsigned char *string) { char name[NAME_SIZE]; @@ -230,6 +185,12 @@ static void apply_format(struct udevice *udev, unsigned char *string) strcat(pos, udev->bus_id); dbg("substitute bus_id '%s'", udev->bus_id); break; + case 'k': + if (strlen(udev->kernel_name) == 0) + break; + strcat(pos, udev->kernel_name); + dbg("substitute kernel name '%s'", udev->kernel_name); + break; case 'n': if (strlen(udev->kernel_number) == 0) break; @@ -239,11 +200,12 @@ static void apply_format(struct udevice *udev, unsigned char *string) case 'D': if (strlen(udev->kernel_number) == 0) { strcat(pos, "disc"); + dbg("substitute devfs disc"); break; } strcat(pos, "part"); strcat(pos, udev->kernel_number); - dbg("substitute kernel number '%s'", udev->kernel_number); + dbg("substitute devfs part '%s'", udev->kernel_number); break; case 'm': sprintf(pos, "%u", udev->minor); @@ -268,8 +230,10 @@ static void apply_format(struct udevice *udev, unsigned char *string) break; } } - strcat(pos, pos3); - dbg("substitute partial callout output '%s'", pos3); + if (pos3) { + strcat(pos, pos3); + dbg("substitute partial callout output '%s'", pos3); + } } else { strcat(pos, udev->callout_value); dbg("substitute callout output '%s'", udev->callout_value); @@ -285,6 +249,57 @@ static void apply_format(struct udevice *udev, unsigned char *string) } } +static struct bus_file { + char *bus; + char *file; +} bus_files[] = { + { .bus = "scsi", .file = "vendor" }, + { .bus = "usb", .file = "idVendor" }, + { .bus = "ide", .file = "detach_state" }, + { .bus = "pci", .file = "vendor" }, + {} +}; + +#define SECONDS_TO_WAIT_FOR_FILE 10 +static void wait_for_device_to_initialize(struct sysfs_device *sysfs_device) +{ + /* sleep until we see the file for this specific bus type show up this + * is needed because we can easily out-run the kernel in looking for + * these files before the paticular subsystem has created them in the + * sysfs tree properly. + * + * And people thought that the /sbin/hotplug event system was going to + * be slow, poo on you for arguing that before even testing it... + */ + struct bus_file *b = &bus_files[0]; + struct sysfs_attribute *tmpattr; + int loop; + + while (1) { + if (b->bus == NULL) + break; + if (strcmp(sysfs_device->bus, b->bus) == 0) { + tmpattr = NULL; + loop = SECONDS_TO_WAIT_FOR_FILE; + while (loop--) { + dbg("looking for file '%s' on bus '%s'", b->file, b->bus); + tmpattr = sysfs_get_device_attr(sysfs_device, b->file); + if (tmpattr) { + /* found it! */ + goto exit; + } + /* sleep to give the kernel a chance to create the file */ + sleep(1); + } + dbg("Timed out waiting for '%s' file, continuing on anyway...", b->file); + goto exit; + } + b++; + } + dbg("Did not find bus type '%s' on list of bus_id_files, contact greg@kroah.com", sysfs_device->bus); +exit: + return; /* here to prevent compiler warning... */ +} static int exec_callout(struct config_device *dev, char *value, int len) { @@ -388,7 +403,10 @@ static int do_callout(struct sysfs_class_device *class_dev, struct udevice *udev if (dev->type != CALLOUT) continue; - if (sysfs_device) { + if (dev->bus[0] != '\0') { + /* as the user specified a bus, we must match it up */ + if (!sysfs_device) + continue; dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus); if (strcasecmp(dev->bus, sysfs_device->bus) != 0) continue; @@ -409,52 +427,83 @@ static int do_callout(struct sysfs_class_device *class_dev, struct udevice *udev return -ENODEV; } -static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) +static int match_pair(struct sysfs_class_device *class_dev, struct sysfs_device *sysfs_device, struct sysfs_pair *pair) { struct sysfs_attribute *tmpattr = NULL; + char *c; + + if ((pair == NULL) || (pair->file[0] == '\0') || (pair->value == '\0')) + return -ENODEV; + + dbg("look for device attribute '%s'", pair->file); + /* try to find the attribute in the class device directory */ + tmpattr = sysfs_get_classdev_attr(class_dev, pair->file); + if (tmpattr) + goto label_found; + + /* look in the class device directory if present */ + if (sysfs_device) { + tmpattr = sysfs_get_device_attr(sysfs_device, pair->file); + if (tmpattr) + goto label_found; + } + + return -ENODEV; + +label_found: + c = tmpattr->value + strlen(tmpattr->value)-1; + if (*c == '\n') + *c = 0x00; + dbg("compare attribute '%s' value '%s' with '%s'", + pair->file, tmpattr->value, pair->value); + if (strcmp_pattern(pair->value, tmpattr->value) != 0) + return -ENODEV; + + dbg("found matching attribute '%s' with value '%s'", + pair->file, pair->value); + return 0; +} + +static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) +{ + struct sysfs_pair *pair; struct config_device *dev; struct list_head *tmp; - char *c; + int i; + int match; list_for_each(tmp, &config_device_list) { dev = list_entry(tmp, struct config_device, node); if (dev->type != LABEL) continue; - if (sysfs_device) { + if (dev->bus[0] != '\0') { + /* as the user specified a bus, we must match it up */ + if (!sysfs_device) + continue; dbg("dev->bus='%s' sysfs_device->bus='%s'", dev->bus, sysfs_device->bus); if (strcasecmp(dev->bus, sysfs_device->bus) != 0) continue; } - dbg("look for device attribute '%s'", dev->sysfs_file); - /* try to find the attribute in the class device directory */ - tmpattr = sysfs_get_classdev_attr(class_dev, dev->sysfs_file); - if (tmpattr) - goto label_found; - - /* look in the class device directory if present */ - if (sysfs_device) { - tmpattr = sysfs_get_device_attr(sysfs_device, dev->sysfs_file); - if (tmpattr) - goto label_found; + match = 1; + for (i = 0; i < MAX_SYSFS_PAIRS; ++i) { + pair = &dev->sysfs_pair[i]; + if ((pair->file[0] == '\0') || (pair->value[0] == '\0')) + break; + if (match_pair(class_dev, sysfs_device, pair) != 0) { + match = 0; + break; + } } - - continue; - -label_found: - c = tmpattr->value + strlen(tmpattr->value)-1; - if (*c == '\n') - *c = 0x00; - dbg("compare attribute '%s' value '%s' with '%s'", - dev->sysfs_file, tmpattr->value, dev->sysfs_value); - if (strcmp_pattern(dev->sysfs_value, tmpattr->value) != 0) + if (match == 0) continue; + /* found match */ strfieldcpy(udev->name, dev->name); strfieldcpy(udev->symlink, dev->symlink); - dbg("found matching attribute '%s', '%s' becomes '%s' ", - dev->sysfs_file, class_dev->name, udev->name); + dbg("found matching attribute, '%s' becomes '%s' ", + class_dev->name, udev->name); return 0; } @@ -581,49 +630,121 @@ static void do_kernelname(struct sysfs_class_device *class_dev, struct udevice * strfieldcpy(udev->name, class_dev->name); } +static struct sysfs_device *get_sysfs_device(struct sysfs_class_device *class_dev) +{ + struct sysfs_device *sysfs_device; + struct sysfs_class_device *class_dev_parent; + int loop; + char filename[SYSFS_PATH_MAX + 6]; + int retval; + char *temp; + int partition = 0; + + /* Figure out where the device symlink is at. For char devices this will + * always be in the class_dev->path. But for block devices, it's different. + * The main block device will have the device symlink in it's path, but + * all partitions have the symlink in its parent directory. + * But we need to watch out for block devices that do not have parents, yet + * look like a partition (fd0, loop0, etc.) They all do not have a device + * symlink yet. We do sit and spin on waiting for them right now, we should + * possibly have a whitelist for these devices here... + */ + strcpy(filename, class_dev->path); + dbg("filename = %s", filename); + if (strcmp(class_dev->classname, SYSFS_BLOCK_NAME) == 0) { + if (isdigit(class_dev->path[strlen(class_dev->path)-1])) { + temp = strrchr(filename, '/'); + if (temp) { + char *temp2 = strrchr(filename, '/'); + partition = 1; + *temp = 0x00; + dbg("temp2 = %s", temp2); + if (temp2 && (strcmp(temp2, "/block") == 0)) { + /* oops, we have no parent block device, so go back to original directory */ + strcpy(filename, class_dev->path); + partition = 0; + } + } + } + } + strcat(filename, "/device"); + + loop = 2; + while (loop--) { + struct stat buf; + dbg("looking for '%s'", filename); + retval = stat(filename, &buf); + if (!retval) + break; + /* sleep to give the kernel a chance to create the device file */ + sleep(1); + } + + loop = 1; /* FIXME put a real value in here for when everything is fixed... */ + while (loop--) { + /* find the sysfs_device for this class device */ + /* Wouldn't it really be nice if libsysfs could do this for us? */ + sysfs_device = sysfs_get_classdev_device(class_dev); + if (sysfs_device != NULL) + goto exit; + + /* if it's a partition, we need to get the parent device */ + if (partition) { + /* FIXME HACK HACK HACK HACK + * for some reason partitions need this extra sleep here, in order + * to wait for the device properly. Once the libsysfs code is + * fixed properly, this sleep should go away, and we can just loop above. + */ + sleep(1); + dbg("really is a partition"); + class_dev_parent = sysfs_get_classdev_parent(class_dev); + if (class_dev_parent == NULL) { + dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev->name); + } else { + dbg("class_dev_parent->name='%s'", class_dev_parent->name); + sysfs_device = sysfs_get_classdev_device(class_dev_parent); + if (sysfs_device != NULL) + goto exit; + } + } + /* sleep to give the kernel a chance to create the link */ + /* FIXME remove comment... + sleep(1); */ + } + dbg("Timed out waiting for device symlink, continuing on anyway..."); +exit: + return sysfs_device; +} + int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *udev) { struct sysfs_device *sysfs_device = NULL; - struct sysfs_class_device *class_dev_parent = NULL; int retval = 0; struct perm_device *perm; + char *pos; udev->mode = 0; - /* find the sysfs_device for this class device */ - /* Wouldn't it really be nice if libsysfs could do this for us? */ - sysfs_device = sysfs_get_classdev_device(class_dev); - if (sysfs_device == NULL) { - /* bah, let's go backwards up a level to see if the device is there, - * as block partitions don't point to the physical device. Need to fix that - * up in the kernel... - */ - if (strcmp(class_dev->classname, SYSFS_BLOCK_NAME) == 0) { - dbg("looking at block device"); - if (isdigit(class_dev->path[strlen(class_dev->path)-1])) { - dbg("really is a partition"); - class_dev_parent = sysfs_get_classdev_parent - (class_dev); - if (class_dev_parent == NULL) { - dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev->name); - } else { - dbg("class_dev_parent->name='%s'", class_dev_parent->name); - sysfs_device = sysfs_get_classdev_device(class_dev_parent); - } - } - } - } - + /* find the sysfs_device associated with this class device */ + sysfs_device = get_sysfs_device(class_dev); if (sysfs_device) { dbg("sysfs_device->path='%s'", sysfs_device->path); dbg("sysfs_device->bus_id='%s'", sysfs_device->bus_id); dbg("sysfs_device->bus='%s'", sysfs_device->bus); strfieldcpy(udev->bus_id, sysfs_device->bus_id); + wait_for_device_to_initialize(sysfs_device); } else { dbg("class_dev->name = '%s'", class_dev->name); } - build_kernel_number(class_dev, udev); + strfieldcpy(udev->kernel_name, class_dev->name); + + /* get kernel number */ + pos = class_dev->name + strlen(class_dev->name); + while (isdigit(*(pos-1))) + pos--; + strfieldcpy(udev->kernel_number, pos); + dbg("kernel_number='%s'", udev->kernel_number); /* rules are looked at in priority order */ retval = do_callout(class_dev, udev, sysfs_device);