diff --git a/man/udevadm.xml b/man/udevadm.xml index 1f872111e56..af485711222 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -280,9 +280,9 @@ - Trigger a specific type of devices. Valid types are: - devices, subsystems. - The default value is devices. + Trigger a specific type of devices. Valid types are all, + devices, and subsystems. The default value is + devices. @@ -297,6 +297,19 @@ + + + + Takes a comma separated list of subsystems. When triggering events for devices, the + devices from the specified subsystems and their parents are triggered first. For example, + if , then firstly all block devices and + their parents are triggered, in the next all network devices and their parents are + triggered, and lastly the other devices are triggered. This option can be specified + multiple times, and in that case the lists of the subsystems will be merged. That is, + is equivalent to + . + + @@ -382,6 +395,35 @@ then each matching result is ORed, that is, all children of each specified device are triggered. + + + + + When is specified, trigger events for devices + that are already initialized by systemd-udevd, and skip devices that + are not initialized yet. + When is specified, trigger events for devices + that are not initialized by systemd-udevd yet, and skip devices that + are already initialized. + Here, initialized devices are those for which at least one udev rule already + completed execution – for any action but remove — that set a property + or other device setting (and thus has an entry in the udev device database). Devices are + no longer considered initialized if a remove action is seen for them + (which removes their entry in the udev device database). Note that devices that have no + udev rules are never considered initialized, but might still be announced via the sd-device + API (or similar). Typically, it is thus essential that applications which intend to use + such a match, make sure a suitable udev rule is installed that sets at least one property + on devices that shall be matched. + WARNING: can potentially save a significant + amount of time compared to re-triggering all devices in the system and e.g. can be used to + optimize boot time. However, this is not safe to be used in a boot sequence in general. + Especially, when udev rules for a device depend on its parent devices (e.g. + ATTRS or IMPORT{parent} keys, see + udev7 + for more details), the final state of the device becomes easily unstable with this option. + + + diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm index 08b4ab43a0e..23ce02365c7 100644 --- a/shell-completion/bash/udevadm +++ b/shell-completion/bash/udevadm @@ -51,10 +51,12 @@ _udevadm() { [INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db -w --wait-for-initialization --value' [INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file --property' - [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid' + [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid + --initialized-match --initialized-nomatch' [TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch -a --attr-match -A --attr-nomatch -p --property-match - -g --tag-match -y --sysname-match --name-match -b --parent-match' + -g --tag-match -y --sysname-match --name-match -b --parent-match + --prioritized-subsystem' [SETTLE]='-t --timeout -E --exit-if-exists' [CONTROL_STANDALONE]='-e --exit -s --stop-exec-queue -S --start-exec-queue -R --reload --ping' [CONTROL_ARG]='-l --log-priority -p --property -m --children-max -t --timeout' @@ -117,7 +119,7 @@ _udevadm() { if __contains_word "$prev" ${OPTS[TRIGGER_ARG]}; then case $prev in -t|--type) - comps='devices subsystems' + comps='all devices subsystems' ;; -c|--action) comps=$( udevadm trigger --action help ) diff --git a/shell-completion/zsh/_udevadm b/shell-completion/zsh/_udevadm index 14efe248086..63df8b7c9ec 100644 --- a/shell-completion/zsh/_udevadm +++ b/shell-completion/zsh/_udevadm @@ -24,7 +24,7 @@ _udevadm_trigger(){ '--verbose[Print the list of devices which will be triggered.]' \ '--dry-run[Do not actually trigger the event.]' \ '--quiet[Suppress error logging in triggering events.]' \ - '--type=[Trigger a specific type of devices.]:types:(devices subsystems failed)' \ + '--type=[Trigger a specific type of devices.]:types:(all devices subsystems failed)' \ '--action=[Type of event to be triggered.]:actions:(add change remove move online offline bind unbind)' \ '--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]' \ '--subsystem-nomatch=[Do not trigger events for devices which belong to a matching subsystem.]' \ @@ -34,7 +34,10 @@ _udevadm_trigger(){ '--tag-match=property[Trigger events for devices with a matching tag.]' \ '--sysname-match=[Trigger events for devices with a matching sys device name.]' \ '--parent-match=[Trigger events for all children of a given device.]' \ - '--uuid[Print synthetic uevent UUID.]' + '--initialized-match[Trigger events for devices that are already initialized.]' \ + '--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \ + '--uuid[Print synthetic uevent UUID.]' \ + '--prioritized-subsystem=[Trigger events for devices which belong to a matching subsystem earlier.]' } (( $+functions[_udevadm_settle] )) || diff --git a/src/libsystemd/sd-device/device-enumerator-private.h b/src/libsystemd/sd-device/device-enumerator-private.h index 9c6437dbed3..54fc13c43b2 100644 --- a/src/libsystemd/sd-device/device-enumerator-private.h +++ b/src/libsystemd/sd-device/device-enumerator-private.h @@ -3,11 +3,22 @@ #include "sd-device.h" +typedef enum MatchInitializedType { + MATCH_INITIALIZED_NO, /* only devices without a db entry */ + MATCH_INITIALIZED_YES, /* only devices with a db entry */ + MATCH_INITIALIZED_ALL, /* all devices */ + MATCH_INITIALIZED_COMPAT, /* only devices that have no devnode/ifindex or have a db entry */ + _MATCH_INITIALIZED_MAX, + _MATCH_INITIALIZED_INVALID = -EINVAL, +} MatchInitializedType; + int device_enumerator_scan_devices(sd_device_enumerator *enumeartor); int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor); +int device_enumerator_scan_devices_and_subsystems(sd_device_enumerator *enumerator); int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device); -int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator); +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type); int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent); +int device_enumerator_add_prioritized_subsystem(sd_device_enumerator *enumerator, const char *subsystem); sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator); sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator); sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size_t *ret_n_devices); diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index 4942ee43a87..1379764156e 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -20,6 +20,7 @@ typedef enum DeviceEnumerationType { DEVICE_ENUMERATION_TYPE_DEVICES, DEVICE_ENUMERATION_TYPE_SUBSYSTEMS, + DEVICE_ENUMERATION_TYPE_ALL, _DEVICE_ENUMERATION_TYPE_MAX, _DEVICE_ENUMERATION_TYPE_INVALID = -EINVAL, } DeviceEnumerationType; @@ -28,10 +29,13 @@ struct sd_device_enumerator { unsigned n_ref; DeviceEnumerationType type; + Hashmap *devices_by_syspath; sd_device **devices; size_t n_devices, current_device_index; bool scan_uptodate; + bool sorted; + char **prioritized_subsystems; Set *match_subsystem; Set *nomatch_subsystem; Hashmap *match_sysattr; @@ -40,7 +44,7 @@ struct sd_device_enumerator { Set *match_sysname; Set *match_tag; Set *match_parent; - bool match_allow_uninitialized; + MatchInitializedType match_initialized; }; _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { @@ -55,6 +59,7 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { *enumerator = (sd_device_enumerator) { .n_ref = 1, .type = _DEVICE_ENUMERATION_TYPE_INVALID, + .match_initialized = MATCH_INITIALIZED_COMPAT, }; *ret = TAKE_PTR(enumerator); @@ -62,13 +67,29 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { return 0; } +static void device_unref_many(sd_device **devices, size_t n) { + assert(devices || n == 0); + + for (size_t i = 0; i < n; i++) + sd_device_unref(devices[i]); +} + +static void device_enumerator_unref_devices(sd_device_enumerator *enumerator) { + assert(enumerator); + + hashmap_clear_with_destructor(enumerator->devices_by_syspath, sd_device_unref); + device_unref_many(enumerator->devices, enumerator->n_devices); + enumerator->devices = mfree(enumerator->devices); + enumerator->n_devices = 0; +} + static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumerator) { assert(enumerator); - for (size_t i = 0; i < enumerator->n_devices; i++) - sd_device_unref(enumerator->devices[i]); + device_enumerator_unref_devices(enumerator); - free(enumerator->devices); + hashmap_free(enumerator->devices_by_syspath); + strv_free(enumerator->prioritized_subsystems); set_free(enumerator->match_subsystem); set_free(enumerator->nomatch_subsystem); hashmap_free(enumerator->match_sysattr); @@ -83,6 +104,24 @@ static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumer DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_enumerator, sd_device_enumerator, device_enumerator_free); +int device_enumerator_add_prioritized_subsystem(sd_device_enumerator *enumerator, const char *subsystem) { + int r; + + assert(enumerator); + assert(subsystem); + + if (strv_contains(enumerator->prioritized_subsystems, subsystem)) + return 0; + + r = strv_extend(&enumerator->prioritized_subsystems, subsystem); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 1; +} + _public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) { Set **set; int r; @@ -206,82 +245,214 @@ _public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumera _public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) { assert_return(enumerator, -EINVAL); - enumerator->match_allow_uninitialized = true; + enumerator->match_initialized = MATCH_INITIALIZED_ALL; enumerator->scan_uptodate = false; return 1; } -int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) { +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type) { assert_return(enumerator, -EINVAL); + assert_return(type >= 0 && type < _MATCH_INITIALIZED_MAX, -EINVAL); - enumerator->match_allow_uninitialized = false; + enumerator->match_initialized = type; enumerator->scan_uptodate = false; return 1; } -static int device_compare(sd_device * const *_a, sd_device * const *_b) { - sd_device *a = *(sd_device **)_a, *b = *(sd_device **)_b; - const char *devpath_a, *devpath_b, *sound_a; - bool delay_a, delay_b; - int r; +static int sound_device_compare(const char *devpath_a, const char *devpath_b) { + const char *sound_a, *sound_b; + size_t prefix_len; - assert_se(sd_device_get_devpath(a, &devpath_a) >= 0); - assert_se(sd_device_get_devpath(b, &devpath_b) >= 0); + assert(devpath_a); + assert(devpath_b); + + /* For sound cards the control device must be enumerated last to make sure it's the final + * device node that gets ACLs applied. Applications rely on this fact and use ACL changes on + * the control node as an indicator that the ACL change of the entire sound card completed. The + * kernel makes this guarantee when creating those devices, and hence we should too when + * enumerating them. */ sound_a = strstr(devpath_a, "/sound/card"); - if (sound_a) { - /* For sound cards the control device must be enumerated last to - * make sure it's the final device node that gets ACLs applied. - * Applications rely on this fact and use ACL changes on the - * control node as an indicator that the ACL change of the - * entire sound card completed. The kernel makes this guarantee - * when creating those devices, and hence we should too when - * enumerating them. */ - sound_a += STRLEN("/sound/card"); - sound_a = strchr(sound_a, '/'); + if (!sound_a) + return 0; - if (sound_a) { - unsigned prefix_len; + sound_a += STRLEN("/sound/card"); + sound_a = strchr(devpath_a, '/'); + if (!sound_a) + return 0; - prefix_len = sound_a - devpath_a; + prefix_len = sound_a - devpath_a; - if (strneq(devpath_a, devpath_b, prefix_len)) { - const char *sound_b; + if (!strneq(devpath_a, devpath_b, prefix_len)) + return 0; - sound_b = devpath_b + prefix_len; + sound_b = devpath_b + prefix_len; - r = CMP(!!startswith(sound_a, "/controlC"), - !!startswith(sound_b, "/controlC")); - if (r != 0) - return r; - } - } - } + return CMP(!!startswith(sound_a, "/controlC"), + !!startswith(sound_b, "/controlC")); +} + +static bool devpath_is_late_block(const char *devpath) { + assert(devpath); + + return strstr(devpath, "/block/md") || strstr(devpath, "/block/dm-"); +} + +static int device_compare(sd_device * const *a, sd_device * const *b) { + const char *devpath_a, *devpath_b; + int r; + + assert(a); + assert(b); + assert(*a); + assert(*b); + + assert_se(sd_device_get_devpath(*(sd_device**) a, &devpath_a) >= 0); + assert_se(sd_device_get_devpath(*(sd_device**) b, &devpath_b) >= 0); + + r = sound_device_compare(devpath_a, devpath_b); + if (r != 0) + return r; /* md and dm devices are enumerated after all other devices */ - delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-"); - delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-"); - r = CMP(delay_a, delay_b); + r = CMP(devpath_is_late_block(devpath_a), devpath_is_late_block(devpath_b)); if (r != 0) return r; return strcmp(devpath_a, devpath_b); } +static int enumerator_sort_devices(sd_device_enumerator *enumerator) { + size_t n_sorted = 0, n = 0; + sd_device **devices; + sd_device *device; + int r; + + assert(enumerator); + + if (enumerator->sorted) + return 0; + + devices = new(sd_device*, hashmap_size(enumerator->devices_by_syspath)); + if (!devices) + return -ENOMEM; + + STRV_FOREACH(prioritized_subsystem, enumerator->prioritized_subsystems) { + + for (;;) { + const char *syspath; + size_t m = n; + + HASHMAP_FOREACH_KEY(device, syspath, enumerator->devices_by_syspath) { + _cleanup_free_ char *p = NULL; + const char *subsys; + + if (sd_device_get_subsystem(device, &subsys) < 0) + continue; + + if (!streq(subsys, *prioritized_subsystem)) + continue; + + devices[n++] = sd_device_ref(device); + + for (;;) { + _cleanup_free_ char *q = NULL; + + r = path_extract_directory(p ?: syspath, &q); + if (r == -EADDRNOTAVAIL) + break; + if (r < 0) + goto failed; + + device = hashmap_get(enumerator->devices_by_syspath, q); + if (device) + devices[n++] = sd_device_ref(device); + + free_and_replace(p, q); + } + + break; + } + + /* We cannot remove multiple entries in the loop HASHMAP_FOREACH_KEY() above. */ + for (size_t i = m; i < n; i++) { + r = sd_device_get_syspath(devices[i], &syspath); + if (r < 0) + goto failed; + + assert_se(hashmap_remove(enumerator->devices_by_syspath, syspath) == devices[i]); + sd_device_unref(devices[i]); + } + + if (m == n) + break; + } + + typesafe_qsort(devices + n_sorted, n - n_sorted, device_compare); + n_sorted = n; + } + + HASHMAP_FOREACH(device, enumerator->devices_by_syspath) + devices[n++] = sd_device_ref(device); + + /* Move all devices back to the hashmap. Otherwise, devices added by + * udev_enumerate_add_syspath() -> device_enumerator_add_device() may not be listed. */ + for (size_t i = 0; i < n_sorted; i++) { + const char *syspath; + + r = sd_device_get_syspath(devices[i], &syspath); + if (r < 0) + goto failed; + + r = hashmap_put(enumerator->devices_by_syspath, syspath, devices[i]); + if (r < 0) + goto failed; + assert(r > 0); + + sd_device_ref(devices[i]); + } + + typesafe_qsort(devices + n_sorted, n - n_sorted, device_compare); + + device_unref_many(enumerator->devices, enumerator->n_devices); + + enumerator->n_devices = n; + free_and_replace(enumerator->devices, devices); + + enumerator->sorted = true; + return 0; + +failed: + device_unref_many(devices, n); + free(devices); + return r; +} + int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) { + const char *syspath; + int r; + assert_return(enumerator, -EINVAL); assert_return(device, -EINVAL); - if (!GREEDY_REALLOC(enumerator->devices, enumerator->n_devices + 1)) - return -ENOMEM; + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; - enumerator->devices[enumerator->n_devices++] = sd_device_ref(device); + r = hashmap_ensure_put(&enumerator->devices_by_syspath, &string_hash_ops, syspath, device); + if (IN_SET(r, -EEXIST, 0)) + return 0; + if (r < 0) + return r; - return 0; + sd_device_ref(device); + + enumerator->sorted = false; + return 1; } static bool match_property(sd_device_enumerator *enumerator, sd_device *device) { @@ -344,10 +515,42 @@ static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) return false; } +static int match_initialized(sd_device_enumerator *enumerator, sd_device *device) { + int r; + + assert(enumerator); + assert(device); + + if (enumerator->match_initialized == MATCH_INITIALIZED_ALL) + return true; + + r = sd_device_get_is_initialized(device); + if (r == -ENOENT) /* this is necessarily racey, so ignore missing devices */ + return false; + if (r < 0) + return r; + + if (enumerator->match_initialized == MATCH_INITIALIZED_COMPAT) { + /* only devices that have no devnode/ifindex or have a db entry are accepted. */ + if (r > 0) + return true; + + if (sd_device_get_devnum(device, NULL) >= 0) + return true; + + if (sd_device_get_ifindex(device, NULL) >= 0) + return true; + + return false; + } + + return (enumerator->match_initialized == MATCH_INITIALIZED_NO) == (r == 0); +} + static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) { _cleanup_closedir_ DIR *dir = NULL; char *path; - int r = 0; + int k, r = 0; assert(enumerator); assert(basedir); @@ -368,7 +571,6 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, FOREACH_DIRENT_ALL(de, dir, return -errno) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; char syspath[strlen(path) + 1 + strlen(de->d_name) + 1]; - int initialized, k; if (de->d_name[0] == '.') continue; @@ -387,31 +589,13 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, continue; } - initialized = sd_device_get_is_initialized(device); - if (initialized < 0) { - if (initialized != -ENOENT) - /* this is necessarily racey, so ignore missing devices */ - r = initialized; - + k = match_initialized(enumerator, device); + if (k <= 0) { + if (k < 0) + r = k; continue; } - /* - * 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 (!enumerator->match_allow_uninitialized && - !initialized && - (sd_device_get_devnum(device, NULL) >= 0 || - sd_device_get_ifindex(device, NULL) >= 0)) - continue; - if (!device_match_parent(device, enumerator->match_parent, NULL)) continue; @@ -670,57 +854,21 @@ static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) { } static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) { - int r = 0; + int k, r = 0; log_debug("sd-device-enumerator: Scan all dirs"); - if (access("/sys/subsystem", F_OK) >= 0) { - /* we have /subsystem/, forget all the old stuff */ - r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL); - if (r < 0) - return log_debug_errno(r, "sd-device-enumerator: Failed to scan /sys/subsystem: %m"); - } else { - int k; + k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m"); - k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); - if (k < 0) - r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m"); - - k = enumerator_scan_dir(enumerator, "class", NULL, NULL); - if (k < 0) - r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m"); - } + k = enumerator_scan_dir(enumerator, "class", NULL, NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m"); return r; } -static void device_enumerator_dedup_devices(sd_device_enumerator *enumerator) { - sd_device **a, **b, **end; - - assert(enumerator); - - if (enumerator->n_devices <= 1) - return; - - a = enumerator->devices + 1; - b = enumerator->devices; - end = enumerator->devices + enumerator->n_devices; - - for (; a < end; a++) { - const char *devpath_a, *devpath_b; - - assert_se(sd_device_get_devpath(*a, &devpath_a) >= 0); - assert_se(sd_device_get_devpath(*b, &devpath_b) >= 0); - - if (path_equal(devpath_a, devpath_b)) - sd_device_unref(*a); - else - *(++b) = *a; - } - - enumerator->n_devices = b - enumerator->devices + 1; -} - int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { int r = 0, k; @@ -730,10 +878,7 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES) return 0; - for (size_t i = 0; i < enumerator->n_devices; i++) - sd_device_unref(enumerator->devices[i]); - - enumerator->n_devices = 0; + device_enumerator_unref_devices(enumerator); if (!set_isempty(enumerator->match_tag)) { k = enumerator_scan_devices_tags(enumerator); @@ -749,9 +894,6 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { r = k; } - typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare); - device_enumerator_dedup_devices(enumerator); - enumerator->scan_uptodate = true; enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES; @@ -759,12 +901,12 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { } _public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) { - int r; - assert_return(enumerator, NULL); - r = device_enumerator_scan_devices(enumerator); - if (r < 0) + if (device_enumerator_scan_devices(enumerator) < 0) + return NULL; + + if (enumerator_sort_devices(enumerator) < 0) return NULL; enumerator->current_device_index = 0; @@ -779,6 +921,7 @@ _public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *e assert_return(enumerator, NULL); if (!enumerator->scan_uptodate || + !enumerator->sorted || enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES || enumerator->current_device_index + 1 >= enumerator->n_devices) return NULL; @@ -787,7 +930,6 @@ _public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *e } int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { - const char *subsysdir; int r = 0, k; assert(enumerator); @@ -796,10 +938,7 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) return 0; - for (size_t i = 0; i < enumerator->n_devices; i++) - sd_device_unref(enumerator->devices[i]); - - enumerator->n_devices = 0; + device_enumerator_unref_devices(enumerator); /* modules */ if (match_subsystem(enumerator, "module")) { @@ -808,28 +947,20 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { r = log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m"); } - if (access("/sys/subsystem", F_OK) >= 0) - subsysdir = "subsystem"; - else - subsysdir = "bus"; - /* subsystems (only buses support coldplug) */ if (match_subsystem(enumerator, "subsystem")) { - k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL); + k = enumerator_scan_dir_and_add_devices(enumerator, "bus", NULL, NULL); if (k < 0) r = log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m"); } /* subsystem drivers */ if (match_subsystem(enumerator, "drivers")) { - k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers"); + k = enumerator_scan_dir(enumerator, "bus", "drivers", "drivers"); if (k < 0) r = log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m"); } - typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare); - device_enumerator_dedup_devices(enumerator); - enumerator->scan_uptodate = true; enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS; @@ -837,12 +968,12 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { } _public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) { - int r; - assert_return(enumerator, NULL); - r = device_enumerator_scan_subsystems(enumerator); - if (r < 0) + if (device_enumerator_scan_subsystems(enumerator) < 0) + return NULL; + + if (enumerator_sort_devices(enumerator) < 0) return NULL; enumerator->current_device_index = 0; @@ -857,6 +988,7 @@ _public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator assert_return(enumerator, NULL); if (!enumerator->scan_uptodate || + !enumerator->sorted || enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS || enumerator->current_device_index + 1 >= enumerator->n_devices) return NULL; @@ -864,12 +996,67 @@ _public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator return enumerator->devices[++enumerator->current_device_index]; } +int device_enumerator_scan_devices_and_subsystems(sd_device_enumerator *enumerator) { + int r = 0, k; + + assert(enumerator); + + if (enumerator->scan_uptodate && + enumerator->type == DEVICE_ENUMERATION_TYPE_ALL) + return 0; + + device_enumerator_unref_devices(enumerator); + + if (!set_isempty(enumerator->match_tag)) { + k = enumerator_scan_devices_tags(enumerator); + if (k < 0) + r = k; + } else if (enumerator->match_parent) { + k = enumerator_scan_devices_children(enumerator); + if (k < 0) + r = k; + } else { + k = enumerator_scan_dir(enumerator, "class", NULL, NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m"); + + k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m"); + + if (match_subsystem(enumerator, "module")) { + k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m"); + } + if (match_subsystem(enumerator, "subsystem")) { + k = enumerator_scan_dir_and_add_devices(enumerator, "bus", NULL, NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m"); + } + + if (match_subsystem(enumerator, "drivers")) { + k = enumerator_scan_dir(enumerator, "bus", "drivers", "drivers"); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m"); + } + } + + enumerator->scan_uptodate = true; + enumerator->type = DEVICE_ENUMERATION_TYPE_ALL; + + return r; +} + sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) { assert_return(enumerator, NULL); if (!enumerator->scan_uptodate) return NULL; + if (enumerator_sort_devices(enumerator) < 0) + return NULL; + enumerator->current_device_index = 0; if (enumerator->n_devices == 0) @@ -882,6 +1069,7 @@ sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) { assert_return(enumerator, NULL); if (!enumerator->scan_uptodate || + !enumerator->sorted || enumerator->current_device_index + 1 >= enumerator->n_devices) return NULL; @@ -895,6 +1083,9 @@ sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size if (!enumerator->scan_uptodate) return NULL; + if (enumerator_sort_devices(enumerator) < 0) + return NULL; + *ret_n_devices = enumerator->n_devices; return enumerator->devices; } diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c index 2dc695bd03f..d71a31c566a 100644 --- a/src/libudev/libudev-enumerate.c +++ b/src/libudev/libudev-enumerate.c @@ -365,7 +365,7 @@ _public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev assert_return(udev_enumerate, -EINVAL); - r = device_enumerator_add_match_is_initialized(udev_enumerate->enumerator); + r = device_enumerator_add_match_is_initialized(udev_enumerate->enumerator, MATCH_INITIALIZED_COMPAT); if (r < 0) return r; diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index a88823cc21a..56921e2cc62 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -11,6 +11,7 @@ #include "device-util.h" #include "fd-util.h" #include "fileio.h" +#include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "set.h" @@ -226,10 +227,14 @@ static int help(void) { " -y --sysname-match=NAME Trigger devices with this /sys path\n" " --name-match=NAME Trigger devices with this /dev name\n" " -b --parent-match=NAME Trigger devices with that parent device\n" + " --initialized-match Trigger devices that are already initialized\n" + " --initialized-nomatch Trigger devices that are not initialized yet\n" " -w --settle Wait for the triggered events to complete\n" " --wait-daemon[=SECONDS] Wait for udevd daemon to be initialized\n" " before triggering uevents\n" - " --uuid Print synthetic uevent UUID\n", + " --uuid Print synthetic uevent UUID\n" + " --prioritized-subsystem=SUBSYSTEM[,SUBSYSTEM…]\n" + " Trigger devices from a matching subsystem first\n", program_invocation_short_name); return 0; @@ -240,33 +245,40 @@ int trigger_main(int argc, char *argv[], void *userdata) { ARG_NAME = 0x100, ARG_PING, ARG_UUID, + ARG_PRIORITIZED_SUBSYSTEM, + ARG_INITIALIZED_MATCH, + ARG_INITIALIZED_NOMATCH, }; static const struct option options[] = { - { "verbose", no_argument, NULL, 'v' }, - { "dry-run", no_argument, NULL, 'n' }, - { "quiet", no_argument, NULL, 'q' }, - { "type", required_argument, NULL, 't' }, - { "action", required_argument, NULL, 'c' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "subsystem-nomatch", required_argument, NULL, 'S' }, - { "attr-match", required_argument, NULL, 'a' }, - { "attr-nomatch", required_argument, NULL, 'A' }, - { "property-match", required_argument, NULL, 'p' }, - { "tag-match", required_argument, NULL, 'g' }, - { "sysname-match", required_argument, NULL, 'y' }, - { "name-match", required_argument, NULL, ARG_NAME }, - { "parent-match", required_argument, NULL, 'b' }, - { "settle", no_argument, NULL, 'w' }, - { "wait-daemon", optional_argument, NULL, ARG_PING }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, - { "uuid", no_argument, NULL, ARG_UUID }, + { "verbose", no_argument, NULL, 'v' }, + { "dry-run", no_argument, NULL, 'n' }, + { "quiet", no_argument, NULL, 'q' }, + { "type", required_argument, NULL, 't' }, + { "action", required_argument, NULL, 'c' }, + { "subsystem-match", required_argument, NULL, 's' }, + { "subsystem-nomatch", required_argument, NULL, 'S' }, + { "attr-match", required_argument, NULL, 'a' }, + { "attr-nomatch", required_argument, NULL, 'A' }, + { "property-match", required_argument, NULL, 'p' }, + { "tag-match", required_argument, NULL, 'g' }, + { "sysname-match", required_argument, NULL, 'y' }, + { "name-match", required_argument, NULL, ARG_NAME }, + { "parent-match", required_argument, NULL, 'b' }, + { "initialized-match", no_argument, NULL, ARG_INITIALIZED_MATCH }, + { "initialized-nomatch", no_argument, NULL, ARG_INITIALIZED_NOMATCH }, + { "settle", no_argument, NULL, 'w' }, + { "wait-daemon", optional_argument, NULL, ARG_PING }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { "uuid", no_argument, NULL, ARG_UUID }, + { "prioritized-subsystem", required_argument, NULL, ARG_PRIORITIZED_SUBSYSTEM }, {} }; enum { TYPE_DEVICES, TYPE_SUBSYSTEMS, + TYPE_ALL, } device_type = TYPE_DEVICES; sd_device_action_t action = SD_DEVICE_CHANGE; _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; @@ -309,6 +321,8 @@ int trigger_main(int argc, char *argv[], void *userdata) { device_type = TYPE_DEVICES; else if (streq(optarg, "subsystems")) device_type = TYPE_SUBSYSTEMS; + else if (streq(optarg, "all")) + device_type = TYPE_ALL; else return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg); break; @@ -405,6 +419,26 @@ int trigger_main(int argc, char *argv[], void *userdata) { arg_uuid = true; break; + case ARG_PRIORITIZED_SUBSYSTEM: { + _cleanup_strv_free_ char **subsystems = NULL; + + subsystems = strv_split(optarg, ","); + if (!subsystems) + return log_error_errno(r, "Failed to parse prioritized subsystem '%s': %m", optarg); + + STRV_FOREACH(p, subsystems) { + r = device_enumerator_add_prioritized_subsystem(e, *p); + if (r < 0) + return log_error_errno(r, "Failed to add prioritized subsystem '%s': %m", *p); + } + break; + } + case ARG_INITIALIZED_MATCH: + case ARG_INITIALIZED_NOMATCH: + r = device_enumerator_add_match_is_initialized(e, c == ARG_INITIALIZED_MATCH ? MATCH_INITIALIZED_YES : MATCH_INITIALIZED_NO); + if (r < 0) + return log_error_errno(r, "Failed to set initialized filter: %m"); + break; case 'V': return print_version(); case 'h': @@ -477,6 +511,11 @@ int trigger_main(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to scan devices: %m"); break; + case TYPE_ALL: + r = device_enumerator_scan_devices_and_subsystems(e); + if (r < 0) + return log_error_errno(r, "Failed to scan devices and subsystems: %m"); + break; default: assert_not_reached(); } diff --git a/units/systemd-udev-trigger.service b/units/systemd-udev-trigger.service index 3d0f7b4f203..5e919950600 100644 --- a/units/systemd-udev-trigger.service +++ b/units/systemd-udev-trigger.service @@ -19,5 +19,4 @@ ConditionPathIsReadWrite=/sys [Service] Type=oneshot RemainAfterExit=yes -ExecStart=-udevadm trigger --type=subsystems --action=add -ExecStart=-udevadm trigger --type=devices --action=add +ExecStart=-udevadm trigger --type=all --action=add --prioritized-subsystem=block,tpmrm