The implicit caching in sysfs is unsuitable for long-lived programs; it would fill up with stale data. Fortunately libudev doesn't use the relevant functions, but this is not very clear. Add an explicit sysfs_cache parameter to the relevant functions (sysfs_attr_get_value, sysfs_device_get and variants). The parameter can be NULL for no caching. sysfs_device objects returned by these functions must now be explicitly released by calling sysfs_device_cleanup(). This makes traversing parent devices quite hairy, so we hide the details in udev_device_parent_iter_*. diff --git a/extras/usb_id/usb_id.c b/extras/usb_id/usb_id.c index fd4d2f2..f8e38f0 100644 --- a/extras/usb_id/usb_id.c +++ b/extras/usb_id/usb_id.c @@ -228,33 +228,34 @@ static int usb_id(const char *devpath) char if_subclass[NAME_SIZE]; int if_class_num; int protocol = 0; + int retval = 1; int status; dbg("devpath %s\n", devpath); /* get all usb specific information: dev_interface, if_class, dev_usb */ - dev = sysfs_device_get(devpath); + dev = sysfs_device_get(NULL, devpath); if (dev == NULL) { err("unable to access '%s'\n", devpath); - return 1; + goto out; } /* usb interface directory */ - dev_interface = sysfs_device_get_parent_with_subsystem(dev, "usb"); + dev_interface = sysfs_device_get_parent_with_subsystem(NULL, dev, "usb"); if (dev_interface == NULL) { info("unable to access usb_interface device of '%s'\n", devpath); - return 1; + goto out; } - status = sysfs_attr_get_value(dev_interface->devpath, "bInterfaceClass", + status = sysfs_attr_get_value(NULL, dev_interface->devpath, "bInterfaceClass", if_class, sizeof(if_class)); if (!status) { info("%s: cannot get bInterfaceClass attribute\n", dev_interface->kernel); - return 1; + goto out; } if_class_num = strtoul(if_class, NULL, 16); if (if_class_num == 8) { - status = sysfs_attr_get_value(dev_interface->devpath, "bInterfaceSubClass", + status = sysfs_attr_get_value(NULL, dev_interface->devpath, "bInterfaceSubClass", if_subclass, sizeof(if_subclass)); if (!status) protocol = set_usb_mass_storage_ifsubtype(type_str, if_subclass, sizeof(type_str)-1); @@ -264,10 +265,10 @@ static int usb_id(const char *devpath) info("%s: if_class %d protocol %d\n", dev_interface->devpath, if_class_num, protocol); /* usb device directory */ - dev_usb = sysfs_device_get_parent_with_subsystem(dev_interface, "usb"); + dev_usb = sysfs_device_get_parent_with_subsystem(NULL, dev_interface, "usb"); if (!dev_usb) { info("unable to find parent 'usb' device of '%s'\n", devpath); - return 1; + goto out; } /* mass storage */ @@ -280,7 +281,7 @@ static int usb_id(const char *devpath) int host, bus, target, lun; /* get scsi device */ - dev_scsi = sysfs_device_get_parent_with_subsystem(dev, "scsi"); + dev_scsi = sysfs_device_get_parent_with_subsystem(NULL, dev, "scsi"); if (dev_scsi == NULL) { info("unable to find parent 'scsi' device of '%s'\n", devpath); goto fallback; @@ -291,7 +292,7 @@ static int usb_id(const char *devpath) } /* Generic SPC-2 device */ - status = sysfs_attr_get_value(dev_scsi->devpath, "vendor", + status = sysfs_attr_get_value(NULL, dev_scsi->devpath, "vendor", scsi_vendor, sizeof(scsi_vendor)); if (!status) { info("%s: cannot get SCSI vendor attribute\n", dev_scsi->kernel); @@ -299,7 +300,7 @@ static int usb_id(const char *devpath) } set_str(vendor_str, scsi_vendor, sizeof(vendor_str)-1); - status = sysfs_attr_get_value(dev_scsi->devpath, "model", + status = sysfs_attr_get_value(NULL, dev_scsi->devpath, "model", scsi_model, sizeof(scsi_model)); if (!status) { info("%s: cannot get SCSI model attribute\n", dev_scsi->kernel); @@ -307,7 +308,7 @@ static int usb_id(const char *devpath) } set_str(model_str, scsi_model, sizeof(model_str)-1); - status = sysfs_attr_get_value(dev_scsi->devpath, "type", + status = sysfs_attr_get_value(NULL, dev_scsi->devpath, "type", scsi_type, sizeof(scsi_type)); if (!status) { info("%s: cannot get SCSI type attribute\n", dev_scsi->kernel); @@ -315,7 +316,7 @@ static int usb_id(const char *devpath) } set_scsi_type(type_str, scsi_type, sizeof(type_str)-1); - status = sysfs_attr_get_value(dev_scsi->devpath, "rev", + status = sysfs_attr_get_value(NULL, dev_scsi->devpath, "rev", scsi_rev, sizeof(scsi_rev)); if (!status) { info("%s: cannot get SCSI revision attribute\n", dev_scsi->kernel); @@ -337,16 +338,16 @@ fallback: status = 0; if (!use_num_info) - status = sysfs_attr_get_value(dev_usb->devpath, "manufacturer", + status = sysfs_attr_get_value(NULL, dev_usb->devpath, "manufacturer", usb_vendor, sizeof(usb_vendor)); if (!status) - status = sysfs_attr_get_value(dev_usb->devpath, "idVendor", + status = sysfs_attr_get_value(NULL, dev_usb->devpath, "idVendor", usb_vendor, sizeof(usb_vendor)); if (!status) { info("No USB vendor information available\n"); - return 1; + goto out; } set_str(vendor_str, usb_vendor, sizeof(vendor_str)-1); } @@ -356,16 +357,16 @@ fallback: status = 0; if (!use_num_info) - status = sysfs_attr_get_value(dev_usb->devpath, "product", + status = sysfs_attr_get_value(NULL, dev_usb->devpath, "product", usb_model, sizeof(usb_model)); if (!status) - status = sysfs_attr_get_value(dev_usb->devpath, "idProduct", + status = sysfs_attr_get_value(NULL, dev_usb->devpath, "idProduct", usb_model, sizeof(usb_model)); if (!status) { dbg("No USB model information available\n"); - return 1; + goto out; } set_str(model_str, usb_model, sizeof(model_str)-1); } @@ -373,7 +374,7 @@ fallback: if (revision_str[0] == '\0') { char usb_rev[NAME_SIZE]; - status = sysfs_attr_get_value(dev_usb->devpath, "bcdDevice", + status = sysfs_attr_get_value(NULL, dev_usb->devpath, "bcdDevice", usb_rev, sizeof(usb_rev)); if (status) set_str(revision_str, usb_rev, sizeof(revision_str)-1); @@ -382,12 +383,20 @@ fallback: if (serial_str[0] == '\0') { char usb_serial[NAME_SIZE]; - status = sysfs_attr_get_value(dev_usb->devpath, "serial", + status = sysfs_attr_get_value(NULL, dev_usb->devpath, "serial", usb_serial, sizeof(usb_serial)); if (status) set_str(serial_str, usb_serial, sizeof(serial_str)-1); } - return 0; + + /* success */ + retval = 0; + +out: + sysfs_device_cleanup(dev); + sysfs_device_cleanup(dev_interface); + sysfs_device_cleanup(dev_usb); + return retval; } int main(int argc, char **argv) diff --git a/udev/test-udev.c b/udev/test-udev.c index 07628f7..878c477 100644 --- a/udev/test-udev.c +++ b/udev/test-udev.c @@ -62,7 +62,6 @@ static void asmlinkage sig_handler(int signum) int main(int argc, char *argv[]) { - struct sysfs_device *dev; struct udevice *udev; const char *maj, *min; struct udev_rules rules; @@ -133,18 +132,23 @@ int main(int argc, char *argv[]) sysfs_init(); udev_rules_init(&rules, 0); - dev = sysfs_device_get(devpath); - if (dev == NULL) { - info("unable to open '%s'\n", devpath); - goto fail; - } - udev = udev_device_init(); if (udev == NULL) goto fail; + /* valgrind can only verify sysfs_device_cleanup() calls if cache is disabled */ +#if 0 + udev->cache = sysfs_cache_init(); + if (udev->cache == NULL) + goto fail; +#endif + /* override built-in sysfs device */ - udev->dev = dev; + udev->dev = sysfs_device_get(udev->cache, devpath); + if (udev->dev == NULL) { + info("unable to open '%s'\n", devpath); + goto fail; + } strlcpy(udev->action, action, sizeof(udev->action)); /* get dev_t from environment, which is needed for "remove" to work, "add" works also from sysfs */ @@ -164,8 +168,8 @@ int main(int argc, char *argv[]) if (retval == 0 && !udev->ignore_device && udev_run) udev_rules_run(udev); - udev_device_cleanup(udev); fail: + udev_device_cleanup(udev); udev_rules_cleanup(&rules); sysfs_cleanup(); selinux_exit(); diff --git a/udev/udev.h b/udev/udev.h index 28b59db..1037734 100644 --- a/udev/udev.h +++ b/udev/udev.h @@ -48,9 +48,13 @@ struct udev_rules; +struct sysfs_cache { + struct list_head dev_list; /* device cache */ + struct list_head attr_list; /* attribute value cache */ +}; + struct sysfs_device { struct list_head node; /* for device cache */ - struct sysfs_device *parent; /* already cached parent*/ char devpath[PATH_SIZE]; char subsystem[NAME_SIZE]; /* $class, $bus, drivers, module */ char kernel[NAME_SIZE]; /* device instance name */ @@ -63,6 +67,7 @@ struct udevice { struct sysfs_device *dev; /* points to dev_local by default */ struct sysfs_device dev_local; struct sysfs_device *dev_parent; /* current parent device used for matching */ + struct sysfs_cache *cache; char action[NAME_SIZE]; char *devpath_old; @@ -103,6 +108,9 @@ extern void udev_config_init(void); /* udev_device.c */ extern struct udevice *udev_device_init(void); extern void udev_device_cleanup(struct udevice *udev); +extern void udev_device_parents_iter_init(struct udevice *udev, struct sysfs_device **iter); +extern void udev_device_parents_iter_next(struct udevice *udev, struct sysfs_device **iter); +extern void udev_device_parents_iter_cleanup(struct udevice *udev, struct sysfs_device **iter); extern dev_t udev_device_get_devt(struct udevice *udev); /* udev_device_event.c */ @@ -112,12 +120,17 @@ extern int udev_device_event(struct udev_rules *rules, struct udevice *udev); extern char sysfs_path[PATH_SIZE]; extern int sysfs_init(void); extern void sysfs_cleanup(void); +extern struct sysfs_cache *sysfs_cache_init(void); +extern void sysfs_cache_cleanup(struct sysfs_cache *cache); extern void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, const char *subsystem, const char *driver); -extern struct sysfs_device *sysfs_device_get(const char *devpath); -extern struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev); -extern struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem); -extern int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value, size_t len); +extern struct sysfs_device *sysfs_device_get(struct sysfs_cache *cache, const char *devpath); +extern struct sysfs_device *sysfs_device_get_parent(struct sysfs_cache *cache, struct sysfs_device *dev); +extern struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_cache *cache, + struct sysfs_device *dev, const char *subsystem); +extern void sysfs_device_cleanup(struct sysfs_device *dev); +int sysfs_attr_get_value(struct sysfs_cache *cache, const char *devpath, const char *attr_name, + char *value, size_t len); extern int sysfs_resolve_link(char *path, size_t size); extern int sysfs_lookup_devpath_by_subsys_id(char *devpath, size_t len, const char *subsystem, const char *id); diff --git a/udev/udev_device.c b/udev/udev_device.c index 13000b0..a81d80e 100644 --- a/udev/udev_device.c +++ b/udev/udev_device.c @@ -63,19 +63,46 @@ void udev_device_cleanup(struct udevice *udev) { if (udev == NULL) return; + if (udev->dev != &udev->dev_local) + sysfs_device_cleanup(udev->dev); + udev_device_parents_iter_cleanup(udev, &udev->dev_parent); + sysfs_cache_cleanup(udev->cache); name_list_cleanup(&udev->symlink_list); name_list_cleanup(&udev->run_list); name_list_cleanup(&udev->env_list); free(udev); } +void udev_device_parents_iter_init(struct udevice *udev, struct sysfs_device **iter) +{ + *iter = udev->dev; +} + +void udev_device_parents_iter_next(struct udevice *udev, struct sysfs_device **iter) +{ + struct sysfs_device *dev; + + dev = *iter; + *iter = sysfs_device_get_parent(udev->cache, dev); + + if (dev != udev->dev) + sysfs_device_cleanup(dev); +} + +void udev_device_parents_iter_cleanup(struct udevice *udev, struct sysfs_device **iter) +{ + if (*iter != udev->dev) + sysfs_device_cleanup(*iter); + *iter = NULL; +} + dev_t udev_device_get_devt(struct udevice *udev) { char attr[NAME_SIZE]; unsigned int maj, min; /* read it from sysfs */ - if (sysfs_attr_get_value(udev->dev->devpath, "dev", attr, sizeof(attr))) { + if (sysfs_attr_get_value(udev->cache, udev->dev->devpath, "dev", attr, sizeof(attr))) { if (sscanf(attr, "%u:%u", &maj, &min) == 2) return makedev(maj, min); } diff --git a/udev/udev_node.c b/udev/udev_node.c index a29c4b7..f489b67 100644 --- a/udev/udev_node.c +++ b/udev/udev_node.c @@ -380,7 +380,7 @@ int udev_node_add(struct udevice *udev) int range; /* take the maximum registered minor range */ - if (sysfs_attr_get_value(udev->dev->devpath, "range", attr, sizeof(attr))) { + if (sysfs_attr_get_value(udev->cache, udev->dev->devpath, "range", attr, sizeof(attr))) { range = atoi(attr); if (range > 1) udev->partitions = range-1; diff --git a/udev/udev_rules.c b/udev/udev_rules.c index 53c483a..da7a62a 100644 --- a/udev/udev_rules.c +++ b/udev/udev_rules.c @@ -416,43 +416,45 @@ static int import_program_into_env(struct udevice *udev, const char *program) static int import_parent_into_env(struct udevice *udev, const char *filter) { struct sysfs_device *dev_parent; + struct udevice *udev_parent; + struct name_entry *name_loop; int rc = -1; - dev_parent = sysfs_device_get_parent(udev->dev); - if (dev_parent != NULL) { - struct udevice *udev_parent; - struct name_entry *name_loop; + dev_parent = sysfs_device_get_parent(udev->cache, udev->dev); + if (dev_parent == NULL) + goto out; - dbg("found parent '%s', get the node name\n", dev_parent->devpath); - udev_parent = udev_device_init(); - if (udev_parent == NULL) - return -1; - /* import the udev_db of the parent */ - if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) { - dbg("import stored parent env '%s'\n", udev_parent->name); - list_for_each_entry(name_loop, &udev_parent->env_list, node) { - char name[NAME_SIZE]; - char *pos; - - strlcpy(name, name_loop->name, sizeof(name)); - pos = strchr(name, '='); - if (pos) { - pos[0] = '\0'; - pos++; - if (fnmatch(filter, name, 0) == 0) { - dbg("import key '%s'\n", name_loop->name); - name_list_add(&udev->env_list, name_loop->name, 0); - setenv(name, pos, 1); - } else - dbg("skip key '%s'\n", name_loop->name); - } + dbg("found parent '%s', get the node name\n", dev_parent->devpath); + udev_parent = udev_device_init(); + if (udev_parent == NULL) + goto out; + /* import the udev_db of the parent */ + if (udev_db_get_device(udev_parent, dev_parent->devpath) == 0) { + dbg("import stored parent env '%s'\n", udev_parent->name); + list_for_each_entry(name_loop, &udev_parent->env_list, node) { + char name[NAME_SIZE]; + char *pos; + + strlcpy(name, name_loop->name, sizeof(name)); + pos = strchr(name, '='); + if (pos) { + pos[0] = '\0'; + pos++; + if (fnmatch(filter, name, 0) == 0) { + dbg("import key '%s'\n", name_loop->name); + name_list_add(&udev->env_list, name_loop->name, 0); + setenv(name, pos, 1); + } else + dbg("skip key '%s'\n", name_loop->name); } - rc = 0; - } else - dbg("parent not found in database\n"); - udev_device_cleanup(udev_parent); - } + } + rc = 0; + } else + dbg("parent not found in database\n"); + udev_device_cleanup(udev_parent); +out: + sysfs_device_cleanup(dev_parent); return rc; } @@ -834,7 +836,7 @@ found: if (attr_get_by_subsys_id(attr, devpath, sizeof(devpath), &attrib)) { if (attrib != NULL) - found = sysfs_attr_get_value(devpath, attrib, + found = sysfs_attr_get_value(udev->cache, devpath, attrib, temp2, sizeof(temp2)); else break; @@ -842,20 +844,23 @@ found: /* try the current device, other matches may have selected */ if (!found && udev->dev_parent != NULL && udev->dev_parent != udev->dev) - found = sysfs_attr_get_value(udev->dev_parent->devpath, attr, + found = sysfs_attr_get_value(udev->cache, udev->dev_parent->devpath, attr, temp2, sizeof(temp2)); /* look at all devices along the chain of parents */ if (!found) { - struct sysfs_device *dev_parent = udev->dev; + struct sysfs_device *dev_parent; + udev_device_parents_iter_init(udev, &dev_parent); do { dbg("looking at '%s'\n", dev_parent->devpath); - found = sysfs_attr_get_value(dev_parent->devpath, attr, + found = sysfs_attr_get_value(udev->cache, dev_parent->devpath, attr, temp2, sizeof(temp2)); if (found) break; - dev_parent = sysfs_device_get_parent(dev_parent); + udev_device_parents_iter_next(udev, &dev_parent); } while (dev_parent != NULL); + + udev_device_parents_iter_cleanup(udev, &dev_parent); } if (!found) @@ -876,7 +881,7 @@ found: { struct sysfs_device *dev_parent; - dev_parent = sysfs_device_get_parent(udev->dev); + dev_parent = sysfs_device_get_parent(udev->cache, udev->dev); if (dev_parent != NULL) { struct udevice *udev_parent; @@ -891,6 +896,7 @@ found: dbg("parent not found in database\n"); udev_device_cleanup(udev_parent); } + sysfs_device_cleanup(dev_parent); } } break; @@ -1142,13 +1148,13 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule) if (attr_get_by_subsys_id(key_name, devpath, sizeof(devpath), &attrib)) { if (attrib != NULL) - found = sysfs_attr_get_value(devpath, attrib, + found = sysfs_attr_get_value(udev->cache, devpath, attrib, val, sizeof(val)); else goto nomatch; } if (!found) - found = sysfs_attr_get_value(udev->dev->devpath, key_name, + found = sysfs_attr_get_value(udev->cache, udev->dev->devpath, key_name, val, sizeof(val)); if (!found) goto nomatch; @@ -1168,7 +1174,8 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule) } /* walk up the chain of parent devices and find a match */ - udev->dev_parent = udev->dev; + udev_device_parents_iter_cleanup(udev, &udev->dev_parent); + udev_device_parents_iter_init(udev, &udev->dev_parent); while (1) { /* check for matching kernel device name */ if (match_key("KERNELS", rule, &rule->kernels, udev->dev_parent->kernel)) @@ -1194,10 +1201,10 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule) size_t len; int found; - found = sysfs_attr_get_value(udev->dev_parent->devpath, key_name, + found = sysfs_attr_get_value(udev->cache, udev->dev_parent->devpath, key_name, val, sizeof(val)); if (!found) - found = sysfs_attr_get_value(udev->dev->devpath, key_name, + found = sysfs_attr_get_value(udev->cache, udev->dev->devpath, key_name, val, sizeof(val)); if (!found) goto try_parent; @@ -1221,7 +1228,8 @@ static int match_rule(struct udevice *udev, struct udev_rule *rule) try_parent: /* move to parent device */ dbg("try parent sysfs device\n"); - udev->dev_parent = sysfs_device_get_parent(udev->dev_parent); + udev_device_parents_iter_next(udev, &udev->dev_parent); + if (udev->dev_parent == NULL) goto nomatch; dbg("looking at dev_parent->devpath='%s'\n", udev->dev_parent->devpath); diff --git a/udev/udev_sysfs.c b/udev/udev_sysfs.c index 2d2457f..4028f7e 100644 --- a/udev/udev_sysfs.c +++ b/udev/udev_sysfs.c @@ -31,11 +31,6 @@ char sysfs_path[PATH_SIZE]; -/* device cache */ -static LIST_HEAD(dev_list); - -/* attribute value cache */ -static LIST_HEAD(attr_list); struct sysfs_attr { struct list_head node; char path[PATH_SIZE]; @@ -55,27 +50,48 @@ int sysfs_init(void) strlcpy(sysfs_path, "/sys", sizeof(sysfs_path)); dbg("sysfs_path='%s'\n", sysfs_path); - INIT_LIST_HEAD(&dev_list); - INIT_LIST_HEAD(&attr_list); return 0; } void sysfs_cleanup(void) { +} + +struct sysfs_cache *sysfs_cache_init() +{ + struct sysfs_cache *cache; + + cache = malloc(sizeof(struct sysfs_cache)); + if (cache == NULL) + return NULL; + + INIT_LIST_HEAD(&cache->dev_list); + INIT_LIST_HEAD(&cache->attr_list); + + return cache; +} + +void sysfs_cache_cleanup(struct sysfs_cache *cache) +{ struct sysfs_attr *attr_loop; struct sysfs_attr *attr_temp; struct sysfs_device *dev_loop; struct sysfs_device *dev_temp; - list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) { + if (cache == NULL) + return; + + list_for_each_entry_safe(attr_loop, attr_temp, &cache->attr_list, node) { list_del(&attr_loop->node); free(attr_loop); } - list_for_each_entry_safe(dev_loop, dev_temp, &dev_list, node) { + list_for_each_entry_safe(dev_loop, dev_temp, &cache->dev_list, node) { list_del(&dev_loop->node); free(dev_loop); } + + free(cache); } void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath, @@ -144,7 +160,7 @@ int sysfs_resolve_link(char *devpath, size_t size) return 0; } -struct sysfs_device *sysfs_device_get(const char *devpath) +struct sysfs_device *sysfs_device_get(struct sysfs_cache *cache, const char *devpath) { char path[PATH_SIZE]; char devpath_real[PATH_SIZE]; @@ -173,12 +189,14 @@ struct sysfs_device *sysfs_device_get(const char *devpath) return NULL; /* look for device already in cache (we never put an untranslated path in the cache) */ - list_for_each_entry(dev_loop, &dev_list, node) { + if (cache != NULL) { + list_for_each_entry(dev_loop, &cache->dev_list, node) { if (strcmp(dev_loop->devpath, devpath_real) == 0) { dbg("found in cache '%s'\n", dev_loop->devpath); return dev_loop; } } + } /* if we got a link, resolve it to the real device */ strlcpy(path, sysfs_path, sizeof(path)); @@ -192,13 +210,15 @@ struct sysfs_device *sysfs_device_get(const char *devpath) return NULL; /* now look for device in cache after path translation */ - list_for_each_entry(dev_loop, &dev_list, node) { + if (cache != NULL) { + list_for_each_entry(dev_loop, &cache->dev_list, node) { if (strcmp(dev_loop->devpath, devpath_real) == 0) { dbg("found in cache '%s'\n", dev_loop->devpath); return dev_loop; } } } + } /* it is a new device */ dbg("new uncached device '%s'\n", devpath_real); @@ -252,23 +272,22 @@ struct sysfs_device *sysfs_device_get(const char *devpath) strlcpy(dev->driver, &pos[1], sizeof(dev->driver)); } - dbg("add to cache 'devpath=%s', subsystem='%s', driver='%s'\n", dev->devpath, dev->subsystem, dev->driver); - list_add(&dev->node, &dev_list); + dbg("cache 'devpath=%s', subsystem='%s', driver='%s'\n", dev->devpath, dev->subsystem, dev->driver); + if (cache != NULL) + list_add(&dev->node, &cache->dev_list); + else + INIT_LIST_HEAD(&dev->node); return dev; } -struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) +struct sysfs_device *sysfs_device_get_parent(struct sysfs_cache *cache, struct sysfs_device *dev) { char parent_devpath[PATH_SIZE]; char *pos; dbg("open '%s'\n", dev->devpath); - /* look if we already know the parent */ - if (dev->parent != NULL) - return dev->parent; - strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); dbg("'%s'\n", parent_devpath); @@ -295,9 +314,8 @@ struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev) if (pos == NULL || pos == parent_devpath) return NULL; - /* get parent and remember it */ - dev->parent = sysfs_device_get(parent_devpath); - return dev->parent; + /* get parent */ + return sysfs_device_get(cache, parent_devpath); device_link: strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath)); @@ -305,25 +323,38 @@ device_link: if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0) return NULL; - /* get parent and remember it */ - dev->parent = sysfs_device_get(parent_devpath); - return dev->parent; + /* get parent */ + return sysfs_device_get(cache, parent_devpath); } -struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem) +struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_cache *cache, + struct sysfs_device *dev, const char *subsystem) { struct sysfs_device *dev_parent; - dev_parent = sysfs_device_get_parent(dev); + dev_parent = sysfs_device_get_parent(cache, dev); while (dev_parent != NULL) { if (strcmp(dev_parent->subsystem, subsystem) == 0) return dev_parent; - dev_parent = sysfs_device_get_parent(dev_parent); + dev_parent = sysfs_device_get_parent(cache, dev_parent); } return NULL; } -int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value, size_t len) +void sysfs_device_cleanup(struct sysfs_device *dev) +{ + if (dev == NULL) + return; + + /* device is on a dev_list - will be freed by sysfs_cache_cleanup() */ + if (!list_empty(&dev->node)) + return; + + free(dev); +} + +int sysfs_attr_get_value(struct sysfs_cache *cache, const char *devpath, const char *attr_name, + char *value, size_t len) { char path_full[PATH_SIZE]; const char *path; @@ -345,7 +376,8 @@ int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value strlcat(path_full, attr_name, sizeof(path_full)); /* look for attribute in cache */ - list_for_each_entry(attr_loop, &attr_list, node) { + if (cache != NULL) { + list_for_each_entry(attr_loop, &cache->attr_list, node) { if (strcmp(attr_loop->path, path) == 0) { dbg("found in cache '%s'\n", attr_loop->path); if (!attr_loop->value) @@ -364,7 +396,8 @@ int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value memset(attr, 0x00, sizeof(struct sysfs_attr)); strlcpy(attr->path, path, sizeof(attr->path)); dbg("add to cache '%s'\n", path_full); - list_add(&attr->node, &attr_list); + list_add(&attr->node, &cache->attr_list); + } if (lstat(path_full, &statbuf) != 0) { dbg("stat '%s' failed: %s\n", path_full, strerror(errno)); @@ -417,9 +450,11 @@ int sysfs_attr_get_value(const char *devpath, const char *attr_name, char *value remove_trailing_chars(val, '\n'); out: - strlcpy(attr->value_local, val, sizeof(attr->value_local)); - attr->value = attr->value_local; - dbg("cache '%s' with link value '%s'\n", path_full, attr->value); + if (cache != NULL) { + strlcpy(attr->value_local, val, sizeof(attr->value_local)); + attr->value = attr->value_local; + dbg("cache '%s' with link value '%s'\n", path_full, attr->value); + } if (strlcpy(value, val, len) > len-1) return 0; diff --git a/udev/udevinfo.c b/udev/udevinfo.c index b76d11d..50d1bef 100644 --- a/udev/udevinfo.c +++ b/udev/udevinfo.c @@ -66,7 +66,7 @@ static void print_all_attributes(const char *devpath, const char *key) if (S_ISLNK(statbuf.st_mode)) continue; - if (!sysfs_attr_get_value(devpath, dent->d_name, value, sizeof(value))) + if (!sysfs_attr_get_value(NULL, devpath, dent->d_name, value, sizeof(value))) continue; len = strlen(value); dbg("attr '%s'='%s'(%zi)\n", dent->d_name, value, len); @@ -89,7 +89,7 @@ static int print_device_chain(const char *devpath) { struct sysfs_device *dev; - dev = sysfs_device_get(devpath); + dev = sysfs_device_get(NULL, devpath); if (dev == NULL) return -1; @@ -109,9 +109,14 @@ static int print_device_chain(const char *devpath) /* walk up the chain of devices */ while (1) { - dev = sysfs_device_get_parent(dev); - if (dev == NULL) + struct sysfs_device *dev_parent; + + dev_parent = sysfs_device_get_parent(NULL, dev); + if (dev_parent == NULL) break; + sysfs_device_cleanup(dev); + dev = dev_parent; + printf(" looking at parent device '%s':\n", dev->devpath); printf(" KERNELS==\"%s\"\n", dev->kernel); printf(" SUBSYSTEMS==\"%s\"\n", dev->subsystem); @@ -120,6 +125,7 @@ static int print_device_chain(const char *devpath) print_all_attributes(dev->devpath, "ATTRS"); } + sysfs_device_cleanup(dev); return 0; } diff --git a/udev/udevtest.c b/udev/udevtest.c index 63603aa..cf96019 100644 --- a/udev/udevtest.c +++ b/udev/udevtest.c @@ -78,7 +78,6 @@ int udevtest(int argc, char *argv[]) const char *subsystem = NULL; const char *devpath = NULL; struct udevice *udev; - struct sysfs_device *dev; struct udev_rules rules = {}; int retval; int rc = 0; @@ -150,25 +149,30 @@ int udevtest(int argc, char *argv[]) if (strncmp(devpath, sysfs_path, strlen(sysfs_path)) == 0) devpath = &devpath[strlen(sysfs_path)]; - dev = sysfs_device_get(devpath); - if (dev == NULL) { - fprintf(stderr, "unable to open device '%s'\n", devpath); + udev = udev_device_init(); + if (udev == NULL) { + fprintf(stderr, "error initializing device\n"); rc = 2; goto exit; } - udev = udev_device_init(); - if (udev == NULL) { - fprintf(stderr, "error initializing device\n"); + udev->cache = sysfs_cache_init(); + if (udev->cache == NULL) { + fprintf(stderr, "error initializing sysfs cache\n"); rc = 3; goto exit; } + /* override built-in sysfs device */ + udev->dev = sysfs_device_get(udev->cache, devpath); + if (udev->dev == NULL) { + fprintf(stderr, "unable to open device '%s'\n", devpath); + rc = 4; + goto exit; + } if (subsystem != NULL) - strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem)); + strlcpy(udev->dev->subsystem, subsystem, sizeof(udev->dev->subsystem)); - /* override built-in sysfs device */ - udev->dev = dev; strlcpy(udev->action, action, sizeof(udev->action)); udev->devt = udev_device_get_devt(udev); @@ -198,9 +202,9 @@ int udevtest(int argc, char *argv[]) info("run: '%s'\n", program); } } - udev_device_cleanup(udev); exit: + udev_device_cleanup(udev); udev_rules_cleanup(&rules); sysfs_cleanup(); return rc; -- To unsubscribe from this list: send the line "unsubscribe linux-hotplug" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html