From b05211fa3e19608f96db53f388959ab57f88c566 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 8 Jul 2011 00:42:35 +0200 Subject: [PATCH] libudev: enumerate - add udev_enumerate_add_match_parent() --- libudev/docs/libudev-sections.txt | 1 + libudev/libudev-enumerate.c | 212 ++++++++++++++++++++++-------- libudev/libudev.h | 1 + udev/udevadm-trigger.c | 25 +++- udev/udevadm.xml | 6 + 5 files changed, 190 insertions(+), 55 deletions(-) diff --git a/libudev/docs/libudev-sections.txt b/libudev/docs/libudev-sections.txt index 6ccd02543..4cd383b4f 100644 --- a/libudev/docs/libudev-sections.txt +++ b/libudev/docs/libudev-sections.txt @@ -94,6 +94,7 @@ udev_enumerate_add_match_sysattr udev_enumerate_add_nomatch_sysattr udev_enumerate_add_match_property udev_enumerate_add_match_tag +udev_enumerate_add_match_parent udev_enumerate_add_match_is_initialized udev_enumerate_add_match_sysname udev_enumerate_add_syspath diff --git a/libudev/libudev-enumerate.c b/libudev/libudev-enumerate.c index 0b028bab8..320ab6664 100644 --- a/libudev/libudev-enumerate.c +++ b/libudev/libudev-enumerate.c @@ -52,6 +52,7 @@ struct udev_enumerate { struct udev_list_node sysname_match_list; struct udev_list_node properties_match_list; struct udev_list_node tags_match_list; + struct udev_device *parent_match; struct udev_list_node devices_list; struct syspath *devices; unsigned int devices_cur; @@ -125,6 +126,7 @@ UDEV_EXPORT void udev_enumerate_unref(struct udev_enumerate *udev_enumerate) 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_device_unref(udev_enumerate->parent_match); udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->devices_list); for (i = 0; i < udev_enumerate->devices_cur; i++) free(udev_enumerate->devices[i].syspath); @@ -459,6 +461,30 @@ UDEV_EXPORT int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumera return 0; } +/** + * udev_enumerate_add_match_parent: + * @udev_enumerate: context + * @parent: filter for the parent device + * + * Return only the children of a given device. + * + * 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 @@ -581,6 +607,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; @@ -645,6 +682,8 @@ static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate, (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)) @@ -727,77 +766,142 @@ UDEV_EXPORT int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate 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) +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_run_path(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; - - if (dent->d_name[0] == '.') - continue; - dev = udev_device_new_from_id_filename(udev_enumerate->udev, dent->d_name); - if (dev == NULL) - continue; + dev = udev_device_new_from_id_filename(udev_enumerate->udev, dent->d_name); + if (dev == NULL) + continue; - 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_property(udev_enumerate, dev)) - goto nomatch; - if (!match_sysattr(udev_enumerate, dev)) - goto nomatch; + 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)); + syspath_add(udev_enumerate, udev_device_get_syspath(dev)); nomatch: - udev_device_unref(dev); - } - closedir(dir); + 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_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; + + if (udev_list_get_entry(&udev_enumerate->tags_match_list) != NULL) + return scan_devices_tags(udev_enumerate); + + if (udev_enumerate->parent_match != NULL) + return parent_crawl_children(udev_enumerate, udev_device_get_syspath(udev_enumerate->parent_match), 256); + + return scan_devices_all(udev_enumerate); +} + /** * udev_enumerate_scan_subsystems: * @udev_enumerate: udev enumeration context diff --git a/libudev/libudev.h b/libudev/libudev.h index 1d4a4ae52..d8eb7dff7 100644 --- a/libudev/libudev.h +++ b/libudev/libudev.h @@ -146,6 +146,7 @@ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, co int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value); int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname); int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag); +int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent); int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate); int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath); /* run enumeration with active filters */ diff --git a/udev/udevadm-trigger.c b/udev/udevadm-trigger.c index d31b4fbd8..3b53be564 100644 --- a/udev/udevadm-trigger.c +++ b/udev/udevadm-trigger.c @@ -103,6 +103,7 @@ int udevadm_trigger(struct udev *udev, int argc, char *argv[]) { "property-match", required_argument, NULL, 'p' }, { "tag-match", required_argument, NULL, 'g' }, { "sysname-match", required_argument, NULL, 'y' }, + { "parent-match", required_argument, NULL, 'b' }, { "help", no_argument, NULL, 'h' }, {} }; @@ -128,7 +129,7 @@ int udevadm_trigger(struct udev *udev, int argc, char *argv[]) const char *val; char buf[UTIL_PATH_SIZE]; - option = getopt_long(argc, argv, "vng:o:t:hc:p:s:S:a:A:y:", options, NULL); + option = getopt_long(argc, argv, "vng:o:t:hc:p:s:S:a:A:y:b:", options, NULL); if (option == -1) break; @@ -179,6 +180,27 @@ int udevadm_trigger(struct udev *udev, int argc, char *argv[]) case 'y': udev_enumerate_add_match_sysname(udev_enumerate, optarg); break; + case 'b': { + char path[UTIL_PATH_SIZE]; + struct udev_device *dev; + + /* add sys dir if needed */ + if (strncmp(optarg, udev_get_sys_path(udev), strlen(udev_get_sys_path(udev))) != 0) + util_strscpyl(path, sizeof(path), udev_get_sys_path(udev), optarg, NULL); + else + util_strscpy(path, sizeof(path), optarg); + util_remove_trailing_chars(path, '/'); + dev = udev_device_new_from_syspath(udev, path); + if (dev == NULL) { + err(udev, "unable to open the device '%s'\n", optarg); + rc = 2; + goto exit; + } + udev_enumerate_add_match_parent(udev_enumerate, dev); + /* drop reference immediately, enumerate pins the device as long as needed */ + udev_device_unref(dev); + break; + } case 'h': printf("Usage: udevadm trigger OPTIONS\n" " --verbose print the list of devices while running\n" @@ -196,6 +218,7 @@ int udevadm_trigger(struct udev *udev, int argc, char *argv[]) " --property-match== trigger devices with a matching property\n" " --tag-match== trigger devices with a matching property\n" " --sysname-match= trigger devices with a matching name\n" + " --parent-match= trigger devices with that parent device\n" " --help\n\n"); goto exit; default: diff --git a/udev/udevadm.xml b/udev/udevadm.xml index b5fb971f3..a10609356 100644 --- a/udev/udevadm.xml +++ b/udev/udevadm.xml @@ -252,6 +252,12 @@ specified multiple times and supports shell style pattern matching. + + + + Trigger events for all children of a given device. + + -- 2.30.2