X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=libudev%2Flibudev-enumerate.c;h=f14d5c8f57cf168a806c4438624b47c1e7f4dae4;hp=da831449dcaf5e936a14409e8e68ab12d30a98e2;hb=7c2dee4a4d7f1b264031daaee786a8fe429884e1;hpb=28460195c2ae90892bf556aff2b80705a8f37795 diff --git a/libudev/libudev-enumerate.c b/libudev/libudev-enumerate.c index da831449d..f14d5c8f5 100644 --- a/libudev/libudev-enumerate.c +++ b/libudev/libudev-enumerate.c @@ -45,18 +45,20 @@ struct syspath { struct udev_enumerate { struct udev *udev; int refcount; - struct udev_list_node sysattr_match_list; - struct udev_list_node sysattr_nomatch_list; - struct udev_list_node subsystem_match_list; - struct udev_list_node subsystem_nomatch_list; - struct udev_list_node sysname_match_list; - struct udev_list_node properties_match_list; - struct udev_list_node tags_match_list; - struct udev_list_node devices_list; + struct udev_list sysattr_match_list; + struct udev_list sysattr_nomatch_list; + struct udev_list subsystem_match_list; + struct udev_list subsystem_nomatch_list; + struct udev_list sysname_match_list; + struct udev_list properties_match_list; + struct udev_list tags_match_list; + struct udev_device *parent_match; + struct udev_list devices_list; struct syspath *devices; unsigned int devices_cur; unsigned int devices_max; bool devices_uptodate:1; + bool match_is_initialized; }; /** @@ -65,7 +67,7 @@ struct udev_enumerate { * * Returns: an enumeration context **/ -struct udev_enumerate *udev_enumerate_new(struct udev *udev) +UDEV_EXPORT struct udev_enumerate *udev_enumerate_new(struct udev *udev) { struct udev_enumerate *udev_enumerate; @@ -74,14 +76,14 @@ struct udev_enumerate *udev_enumerate_new(struct udev *udev) return NULL; udev_enumerate->refcount = 1; udev_enumerate->udev = udev; - udev_list_init(&udev_enumerate->sysattr_match_list); - udev_list_init(&udev_enumerate->sysattr_nomatch_list); - udev_list_init(&udev_enumerate->subsystem_match_list); - udev_list_init(&udev_enumerate->subsystem_nomatch_list); - udev_list_init(&udev_enumerate->sysname_match_list); - udev_list_init(&udev_enumerate->properties_match_list); - udev_list_init(&udev_enumerate->tags_match_list); - udev_list_init(&udev_enumerate->devices_list); + udev_list_init(udev, &udev_enumerate->sysattr_match_list, false); + udev_list_init(udev, &udev_enumerate->sysattr_nomatch_list, false); + udev_list_init(udev, &udev_enumerate->subsystem_match_list, true); + udev_list_init(udev, &udev_enumerate->subsystem_nomatch_list, true); + udev_list_init(udev, &udev_enumerate->sysname_match_list, true); + udev_list_init(udev, &udev_enumerate->properties_match_list, false); + udev_list_init(udev, &udev_enumerate->tags_match_list, true); + udev_list_init(udev, &udev_enumerate->devices_list, false); return udev_enumerate; } @@ -93,7 +95,7 @@ struct udev_enumerate *udev_enumerate_new(struct udev *udev) * * Returns: the passed enumeration context **/ -struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) +UDEV_EXPORT struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) { if (udev_enumerate == NULL) return NULL; @@ -108,7 +110,7 @@ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) * Drop a reference of an enumeration context. If the refcount reaches zero, * all resources of the enumeration context will be released. **/ -void udev_enumerate_unref(struct udev_enumerate *udev_enumerate) +UDEV_EXPORT void udev_enumerate_unref(struct udev_enumerate *udev_enumerate) { unsigned int i; @@ -117,14 +119,15 @@ void udev_enumerate_unref(struct udev_enumerate *udev_enumerate) udev_enumerate->refcount--; if (udev_enumerate->refcount > 0) return; - udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->sysattr_match_list); - udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->sysattr_nomatch_list); - udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->subsystem_match_list); - udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->subsystem_nomatch_list); - udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->sysname_match_list); - udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->properties_match_list); - udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->tags_match_list); - udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->devices_list); + udev_list_cleanup(&udev_enumerate->sysattr_match_list); + udev_list_cleanup(&udev_enumerate->sysattr_nomatch_list); + udev_list_cleanup(&udev_enumerate->subsystem_match_list); + udev_list_cleanup(&udev_enumerate->subsystem_nomatch_list); + udev_list_cleanup(&udev_enumerate->sysname_match_list); + udev_list_cleanup(&udev_enumerate->properties_match_list); + udev_list_cleanup(&udev_enumerate->tags_match_list); + udev_device_unref(udev_enumerate->parent_match); + udev_list_cleanup(&udev_enumerate->devices_list); for (i = 0; i < udev_enumerate->devices_cur; i++) free(udev_enumerate->devices[i].syspath); free(udev_enumerate->devices); @@ -137,7 +140,7 @@ void udev_enumerate_unref(struct udev_enumerate *udev_enumerate) * * Returns: the udev library context. */ -struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) +UDEV_EXPORT struct udev *udev_enumerate_get_udev(struct udev_enumerate *udev_enumerate) { if (udev_enumerate == NULL) return NULL; @@ -246,7 +249,7 @@ static size_t devices_delay_later(struct udev *udev, const char *syspath) * * Returns: the first entry of the sorted list of device paths. */ -struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) +UDEV_EXPORT struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *udev_enumerate) { if (udev_enumerate == NULL) return NULL; @@ -256,7 +259,7 @@ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *ude struct syspath *prev = NULL, *move_later = NULL; size_t move_later_prefix = 0; - udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->devices_list); + udev_list_cleanup(&udev_enumerate->devices_list); qsort(udev_enumerate->devices, udev_enumerate->devices_cur, sizeof(struct syspath), syspath_cmp); max = udev_enumerate->devices_cur; @@ -273,6 +276,8 @@ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *ude /* skip to be delayed devices, and add them to the end of the list */ if (devices_delay_end(udev_enumerate->udev, entry->syspath)) { syspath_add(udev_enumerate, entry->syspath); + /* need to update prev here for the case realloc() gives a different address */ + prev = &udev_enumerate->devices[i]; continue; } @@ -291,25 +296,21 @@ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *ude if (move_later && strncmp(entry->syspath, move_later->syspath, move_later_prefix) != 0) { - udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list, - move_later->syspath, NULL, 0, 0); + udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); move_later = NULL; } - udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list, - entry->syspath, NULL, 0, 0); + udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); } if (move_later) - udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list, - move_later->syspath, NULL, 0, 0); + udev_list_entry_add(&udev_enumerate->devices_list, move_later->syspath, NULL); /* add and cleanup delayed devices from end of list */ for (i = max; i < udev_enumerate->devices_cur; i++) { struct syspath *entry = &udev_enumerate->devices[i]; - udev_list_entry_add(udev_enumerate->udev, &udev_enumerate->devices_list, - entry->syspath, NULL, 0, 0); + udev_list_entry_add(&udev_enumerate->devices_list, entry->syspath, NULL); free(entry->syspath); } udev_enumerate->devices_cur = max; @@ -326,14 +327,13 @@ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enumerate *ude * * Returns: 0 on success, otherwise a negative error value. */ -int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) +UDEV_EXPORT int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { if (udev_enumerate == NULL) return -EINVAL; if (subsystem == NULL) return 0; - if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate), - &udev_enumerate->subsystem_match_list, subsystem, NULL, 1, 0) == NULL) + if (udev_list_entry_add(&udev_enumerate->subsystem_match_list, subsystem, NULL) == NULL) return -ENOMEM; return 0; } @@ -345,14 +345,13 @@ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, co * * Returns: 0 on success, otherwise a negative error value. */ -int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) +UDEV_EXPORT int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) { if (udev_enumerate == NULL) return -EINVAL; if (subsystem == NULL) return 0; - if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate), - &udev_enumerate->subsystem_nomatch_list, subsystem, NULL, 1, 0) == NULL) + if (udev_list_entry_add(&udev_enumerate->subsystem_nomatch_list, subsystem, NULL) == NULL) return -ENOMEM; return 0; } @@ -365,14 +364,13 @@ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, * * Returns: 0 on success, otherwise a negative error value. */ -int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) +UDEV_EXPORT int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { if (udev_enumerate == NULL) return -EINVAL; if (sysattr == NULL) return 0; - if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate), - &udev_enumerate->sysattr_match_list, sysattr, value, 0, 0) == NULL) + if (udev_list_entry_add(&udev_enumerate->sysattr_match_list, sysattr, value) == NULL) return -ENOMEM; return 0; } @@ -385,14 +383,13 @@ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, cons * * Returns: 0 on success, otherwise a negative error value. */ -int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) +UDEV_EXPORT int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) { if (udev_enumerate == NULL) return -EINVAL; if (sysattr == NULL) return 0; - if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate), - &udev_enumerate->sysattr_nomatch_list, sysattr, value, 0, 0) == NULL) + if (udev_list_entry_add(&udev_enumerate->sysattr_nomatch_list, sysattr, value) == NULL) return -ENOMEM; return 0; } @@ -425,14 +422,13 @@ exit: * * Returns: 0 on success, otherwise a negative error value. */ -int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) +UDEV_EXPORT int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) { if (udev_enumerate == NULL) return -EINVAL; if (property == NULL) return 0; - if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate), - &udev_enumerate->properties_match_list, property, value, 0, 0) == NULL) + if (udev_list_entry_add(&udev_enumerate->properties_match_list, property, value) == NULL) return -ENOMEM; return 0; } @@ -444,18 +440,68 @@ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, con * * Returns: 0 on success, otherwise a negative error value. */ -int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) +UDEV_EXPORT int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) { if (udev_enumerate == NULL) return -EINVAL; if (tag == NULL) return 0; - if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate), - &udev_enumerate->tags_match_list, tag, NULL, 1, 0) == NULL) + if (udev_list_entry_add(&udev_enumerate->tags_match_list, tag, NULL) == NULL) return -ENOMEM; return 0; } +/** + * udev_enumerate_add_match_parent: + * @udev_enumerate: context + * @parent: parent device where to start searching + * + * Return the devices on the subtree of one given device. The parent + * itself is included in the list. + * + * A reference for the device is held until the udev_enumerate context + * is cleaned up. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) +{ + if (udev_enumerate == NULL) + return -EINVAL; + if (parent == NULL) + return 0; + if (udev_enumerate->parent_match != NULL) + udev_device_unref(udev_enumerate->parent_match); + udev_enumerate->parent_match = udev_device_ref(parent); + return 0; +} + +/** + * udev_enumerate_add_match_is_initialized: + * @udev_enumerate: context + * + * Match only devices which udev has set up already. This makes + * sure, that the device node permissions and context are properly set + * and that network devices are fully renamed. + * + * Usually, devices which are found in the kernel but not already + * handled by udev, have still pending events. Services should subscribe + * to monitor events and wait for these devices to become ready, instead + * of using uninitialized devices. + * + * For now, this will not affect devices which do not have a device node + * and are not network interfaces. + * + * Returns: 0 on success, otherwise a negative error value. + */ +UDEV_EXPORT int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) +{ + if (udev_enumerate == NULL) + return -EINVAL; + udev_enumerate->match_is_initialized = true; + return 0; +} + /** * udev_enumerate_add_match_sysname: * @udev_enumerate: context @@ -463,14 +509,13 @@ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const ch * * Returns: 0 on success, otherwise a negative error value. */ -int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) +UDEV_EXPORT int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) { if (udev_enumerate == NULL) return -EINVAL; if (sysname == NULL) return 0; - if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate), - &udev_enumerate->sysname_match_list, sysname, NULL, 1, 0) == NULL) + if (udev_list_entry_add(&udev_enumerate->sysname_match_list, sysname, NULL) == NULL) return -ENOMEM; return 0; } @@ -552,6 +597,17 @@ static bool match_tag(struct udev_enumerate *udev_enumerate, struct udev_device return true; } +static bool match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *dev) +{ + const char *parent; + + if (udev_enumerate->parent_match == NULL) + return true; + + parent = udev_device_get_devpath(udev_enumerate->parent_match); + return strncmp(parent, udev_device_get_devpath(dev), strlen(parent)) == 0; +} + static bool match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) { struct udev_list_entry *list_entry; @@ -601,6 +657,23 @@ static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate, if (dev == NULL) continue; + if (udev_enumerate->match_is_initialized) { + /* + * All devices with a device node or network interfaces + * possibly need udev to adjust the device node permission + * or context, or rename the interface before it can be + * reliably used from other processes. + * + * For now, we can only check these types of devices, we + * might not store a database, and have no way to find out + * for all other types of devices. + */ + if (!udev_device_get_is_initialized(dev) && + (major(udev_device_get_devnum(dev)) > 0 || udev_device_get_ifindex(dev) > 0)) + goto nomatch; + } + if (!match_parent(udev_enumerate, dev)) + goto nomatch; if (!match_tag(udev_enumerate, dev)) goto nomatch; if (!match_property(udev_enumerate, dev)) @@ -666,7 +739,7 @@ static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, * * Returns: 0 on success, otherwise a negative error value. */ -int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) +UDEV_EXPORT int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath) { struct udev_device *udev_device; @@ -683,83 +756,161 @@ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char return 0; } -/** - * udev_enumerate_scan_devices: - * @udev_enumerate: udev enumeration context - * - * Returns: 0 on success, otherwise a negative error value. - **/ -int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) +static int scan_devices_tags(struct udev_enumerate *udev_enumerate) { struct udev *udev = udev_enumerate_get_udev(udev_enumerate); - char base[UTIL_PATH_SIZE]; - struct stat statbuf; - - if (udev_enumerate == NULL) - return -EINVAL; + struct udev_list_entry *list_entry; - if (udev_list_get_entry(&udev_enumerate->tags_match_list) != NULL) { - struct udev_list_entry *list_entry; + /* scan only tagged devices, use tags reverse-index, instead of searching all devices in /sys */ + udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) { + DIR *dir; + struct dirent *dent; + char path[UTIL_PATH_SIZE]; - /* scan only tagged devices, use tags reverse-index, instead of searching all devices in /sys */ - udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) { - DIR *dir; - struct dirent *dent; - char path[UTIL_PATH_SIZE]; + util_strscpyl(path, sizeof(path), udev_get_run_path(udev), "/tags/", + udev_list_entry_get_name(list_entry), NULL); + dir = opendir(path); + if (dir == NULL) + continue; + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { + struct udev_device *dev; - util_strscpyl(path, sizeof(path), udev_get_dev_path(udev), "/.udev/tags/", - udev_list_entry_get_name(list_entry), NULL); - dir = opendir(path); - if (dir == NULL) + if (dent->d_name[0] == '.') continue; - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct udev_device *dev; - char syspath[UTIL_PATH_SIZE]; - char *s; - size_t l; - ssize_t len; - - if (dent->d_name[0] == '.') - continue; - s = syspath; - l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev), NULL); - len = readlinkat(dirfd(dir), dent->d_name, s, l); - if (len <= 0 || (size_t)len == l) - continue; - s[len] = '\0'; + dev = udev_device_new_from_id_filename(udev_enumerate->udev, dent->d_name); + if (dev == NULL) + continue; - dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath); - if (dev == NULL) - continue; - syspath_add(udev_enumerate, udev_device_get_syspath(dev)); - udev_device_unref(dev); - } + if (!match_subsystem(udev_enumerate, udev_device_get_subsystem(dev))) + goto nomatch; + if (!match_sysname(udev_enumerate, udev_device_get_sysname(dev))) + goto nomatch; + if (!match_parent(udev_enumerate, dev)) + goto nomatch; + if (!match_property(udev_enumerate, dev)) + goto nomatch; + if (!match_sysattr(udev_enumerate, dev)) + goto nomatch; + + syspath_add(udev_enumerate, udev_device_get_syspath(dev)); +nomatch: + udev_device_unref(dev); } + closedir(dir); + } + return 0; +} + +static int parent_add_child(struct udev_enumerate *enumerate, const char *path) +{ + struct udev_device *dev; + + dev = udev_device_new_from_syspath(enumerate->udev, path); + if (dev == NULL) + return -ENODEV; + + if (!match_subsystem(enumerate, udev_device_get_subsystem(dev))) + return 0; + if (!match_sysname(enumerate, udev_device_get_sysname(dev))) + return 0; + if (!match_property(enumerate, dev)) + return 0; + if (!match_sysattr(enumerate, dev)) + return 0; + + syspath_add(enumerate, udev_device_get_syspath(dev)); + udev_device_unref(dev); + return 1; +} + +static int parent_crawl_children(struct udev_enumerate *enumerate, const char *path, int maxdepth) +{ + DIR *d; + struct dirent *dent; + + d = opendir(path); + if (d == NULL) + return -errno; + + for (dent = readdir(d); dent != NULL; dent = readdir(d)) { + char *child; + + if (dent->d_name[0] == '.') + continue; + if (dent->d_type != DT_DIR) + continue; + if (asprintf(&child, "%s/%s", path, dent->d_name) < 0) + continue; + parent_add_child(enumerate, child); + if (maxdepth > 0) + parent_crawl_children(enumerate, child, maxdepth-1); + free(child); + } + + closedir(d); + return 0; +} + +static int scan_devices_children(struct udev_enumerate *enumerate) +{ + const char *path; + + path = udev_device_get_syspath(enumerate->parent_match); + parent_add_child(enumerate, path); + return parent_crawl_children(enumerate, path, 256); +} + +static int scan_devices_all(struct udev_enumerate *udev_enumerate) +{ + struct udev *udev = udev_enumerate_get_udev(udev_enumerate); + char base[UTIL_PATH_SIZE]; + struct stat statbuf; + + util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL); + if (stat(base, &statbuf) == 0) { + /* we have /subsystem/, forget all the old stuff */ + dbg(udev, "searching '/subsystem/*/devices/*' dir\n"); + scan_dir(udev_enumerate, "subsystem", "devices", NULL); } else { - util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL); - if (stat(base, &statbuf) == 0) { - /* we have /subsystem/, forget all the old stuff */ - dbg(udev, "searching '/subsystem/*/devices/*' dir\n"); - scan_dir(udev_enumerate, "subsystem", "devices", NULL); - } else { dbg(udev, "searching '/bus/*/devices/*' dir\n"); - scan_dir(udev_enumerate, "bus", "devices", NULL); - dbg(udev, "searching '/class/*' dir\n"); - scan_dir(udev_enumerate, "class", NULL, NULL); - } + scan_dir(udev_enumerate, "bus", "devices", NULL); + dbg(udev, "searching '/class/*' dir\n"); + scan_dir(udev_enumerate, "class", NULL, NULL); } - return 0; } +/** + * udev_enumerate_scan_devices: + * @udev_enumerate: udev enumeration context + * + * Returns: 0 on success, otherwise a negative error value. + **/ +UDEV_EXPORT int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate) +{ + if (udev_enumerate == NULL) + return -EINVAL; + + /* efficiently lookup tags only, we maintain a reverse-index */ + if (udev_list_get_entry(&udev_enumerate->tags_match_list) != NULL) + return scan_devices_tags(udev_enumerate); + + /* walk the subtree of one parent device only */ + if (udev_enumerate->parent_match != NULL) + return scan_devices_children(udev_enumerate); + + /* scan devices of all subsystems */ + return scan_devices_all(udev_enumerate); +} + /** * udev_enumerate_scan_subsystems: * @udev_enumerate: udev enumeration context * * Returns: 0 on success, otherwise a negative error value. **/ -int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) +UDEV_EXPORT int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) { struct udev *udev = udev_enumerate_get_udev(udev_enumerate); char base[UTIL_PATH_SIZE]; @@ -769,15 +920,25 @@ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate) if (udev_enumerate == NULL) return -EINVAL; + /* all kernel modules */ + if (match_subsystem(udev_enumerate, "module")) { + dbg(udev, "searching '%s/modules/*' dir\n", subsysdir); + scan_dir_and_add_devices(udev_enumerate, "module", NULL, NULL); + } + util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL); if (stat(base, &statbuf) == 0) subsysdir = "subsystem"; else subsysdir = "bus"; + + /* all subsystems (only buses support coldplug) */ if (match_subsystem(udev_enumerate, "subsystem")) { dbg(udev, "searching '%s/*' dir\n", subsysdir); scan_dir_and_add_devices(udev_enumerate, subsysdir, NULL, NULL); } + + /* all subsystem drivers */ if (match_subsystem(udev_enumerate, "drivers")) { dbg(udev, "searching '%s/*/drivers/*' dir\n", subsysdir); scan_dir(udev_enumerate, subsysdir, "drivers", "drivers");