In order to get a complete list of sysfs attributes for a given udev_device a complete list of all sysfs attributes is created on first access (with values being empty). Values themselves are filled in, as soon as a particular sysfs attribute is read. Signed-off-by: Thomas Egerer <thomas.egerer@xxxxxxxxxxx> --- libudev/libudev-device.c | 145 ++++++++++++++++++++++++++++++++-------------- 1 files changed, 101 insertions(+), 44 deletions(-) diff --git a/libudev/libudev-device.c b/libudev/libudev-device.c index 16bee19..5baff49 100644 --- a/libudev/libudev-device.c +++ b/libudev/libudev-device.c @@ -78,6 +78,7 @@ struct udev_device { bool devlinks_uptodate; bool envp_uptodate; bool tags_uptodate; + bool sysattrs_cached; bool driver_set; bool info_loaded; bool db_loaded; @@ -1136,6 +1137,79 @@ void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned udev_device->usec_initialized = usec_initialized; } +static int cache_sysattrs(struct udev_device *udev_device) +{ + struct udev_list_entry *list_entry; + struct dirent *entry; + DIR *dir; + int num = 0; + + if (udev_device == NULL) + return -1; + /* caching already done? */ + if (true == udev_device->sysattrs_cached) + return 0; + + dir = opendir(udev_device_get_syspath(udev_device)); + if (!dir) { + dbg(udev_device->udev, "sysfs dir '%s' can not be opened\n", + udev_device_get_syspath(udev_device)); + return -1; + } + + while (NULL != (entry = readdir(dir))) { + char path[UTIL_PATH_SIZE]; + struct stat statbuf; + + /* only handle symlinks and regular files */ + if (DT_LNK != entry->d_type && DT_REG != entry->d_type) + continue; + + util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), + "/", entry->d_name, NULL); + if (0 != lstat(path, &statbuf)) + continue; + + if (0 == (statbuf.st_mode & S_IRUSR)) + continue; + + if (DT_LNK == entry->d_type) { + /* symlink */ + char target[UTIL_NAME_SIZE]; + int len; + char *pos; + + /* these values are cached immediately */ + if (strcmp(entry->d_name, "driver") != 0 && + strcmp(entry->d_name, "subsystem") != 0 && + strcmp(entry->d_name, "module") != 0) + continue; + + len = readlinkat(dirfd(dir), entry->d_name, target, sizeof(target)); + if (len <= 0 || len == sizeof(target)) + continue; + target[len] = '\0'; + + pos = strrchr(target, '/'); + if (pos != NULL) { + pos = &pos[1]; + dbg(udev_device->udev, "cache '%s' with link value '%s'\n", entry->d_name, pos); + list_entry = udev_list_entry_add(udev_device->udev, + &udev_device->sysattr_list, entry->d_name, pos, 1, 0); + } + } else { + /* regular file */ + dbg(udev_device->udev, "cache '%s' empty sysattr\n", entry->d_name); + list_entry = udev_list_entry_add(udev_device->udev, + &udev_device->sysattr_list, entry->d_name, NULL, 0, 0); + } + ++num; + } + + udev_device->sysattrs_cached = true; + return num; +} + /** * udev_device_get_sysattr_value: * @udev_device: udev device @@ -1143,6 +1217,9 @@ void udev_device_set_usec_initialized(struct udev_device *udev_device, unsigned * * The retrieved value is cached in the device. Repeated calls will return the same * value and not open the attribute again. + * Upon first call a list of available sysfs attributes is assembled and saved in + * sysattr_list -- while the actual value of the sysattr is -- except for symlinks -- + * not read. * * Returns: the content of a sys attribute file, or #NULL if there is no sys attribute value. **/ @@ -1151,66 +1228,46 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const struct udev_list_entry *list_entry; char path[UTIL_PATH_SIZE]; char value[4096]; - struct stat statbuf; int fd; ssize_t size; const char *val = NULL; + bool found = false; if (udev_device == NULL) return NULL; if (sysattr == NULL) return NULL; - /* look for possibly already cached result */ + /* perform initial caching of sysattr list */ + if (!udev_device->sysattrs_cached) { + int ret; + ret = cache_sysattrs(udev_device); + if (0 > ret) + return NULL; + } + + /* does the desired sysattr exist? */ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_device->sysattr_list)) { if (strcmp(udev_list_entry_get_name(list_entry), sysattr) == 0) { - dbg(udev_device->udev, "got '%s' (%s) from cache\n", - sysattr, udev_list_entry_get_value(list_entry)); - return udev_list_entry_get_value(list_entry); + found = true; + val = udev_list_entry_get_value(list_entry); + break; } } - util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL); - if (lstat(path, &statbuf) != 0) { - dbg(udev_device->udev, "no attribute '%s', keep negative entry\n", path); - udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, NULL, 0, 0); - goto out; + if (false == found) { + dbg(udev_device->udev, "no attribute '%s/%s' found\n", + udev_device_get_syspath(udev_device), sysattr); + return NULL; } - if (S_ISLNK(statbuf.st_mode)) { - char target[UTIL_NAME_SIZE]; - int len; - char *pos; - - /* some core links return the last element of the target path */ - if (strcmp(sysattr, "driver") != 0 && - strcmp(sysattr, "subsystem") != 0 && - strcmp(sysattr, "module") != 0) - goto out; - - len = readlink(path, target, sizeof(target)); - if (len <= 0 || len == sizeof(target)) - goto out; - target[len] = '\0'; - - pos = strrchr(target, '/'); - if (pos != NULL) { - pos = &pos[1]; - dbg(udev_device->udev, "cache '%s' with link value '%s'\n", sysattr, pos); - list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, pos, 0, 0); - val = udev_list_entry_get_value(list_entry); - } - - goto out; + if (NULL != val) { + dbg(udev_device->udev, "got '%s' (%s) from cache\n", + sysattr, udev_list_entry_get_value(list_entry)); + return val; } - /* skip directories */ - if (S_ISDIR(statbuf.st_mode)) - goto out; - - /* skip non-readable files */ - if ((statbuf.st_mode & S_IRUSR) == 0) - goto out; + util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/", sysattr, NULL); /* read attribute value */ fd = open(path, O_RDONLY|O_CLOEXEC); @@ -1225,11 +1282,11 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const if (size == sizeof(value)) goto out; - /* got a valid value, store it in cache and return it */ + /* got a valid value, update in cache and return it */ value[size] = '\0'; util_remove_trailing_chars(value, '\n'); dbg(udev_device->udev, "'%s' has attribute value '%s'\n", path, value); - list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, value, 0, 0); + list_entry = udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr, value, 1, 0); val = udev_list_entry_get_value(list_entry); out: return val; -- 1.7.2.2 -- 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