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