1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-29 21:55:36 +03:00

Merge pull request #22662 from yuwata/udev-trigger-priority

udevadm trigger: add --prioritized-subsystem option
This commit is contained in:
Lennart Poettering 2022-03-22 12:56:32 +01:00 committed by GitHub
commit c0f6a6a542
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 460 additions and 173 deletions

View File

@ -280,9 +280,9 @@
<term><option>-t</option></term>
<term><option>--type=<replaceable>TYPE</replaceable></option></term>
<listitem>
<para>Trigger a specific type of devices. Valid types are:
<command>devices</command>, <command>subsystems</command>.
The default value is <command>devices</command>.</para>
<para>Trigger a specific type of devices. Valid types are <literal>all</literal>,
<literal>devices</literal>, and <literal>subsystems</literal>. The default value is
<literal>devices</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
@ -297,6 +297,19 @@
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--prioritized-subsystem=<replaceable>SUBSYSTEM<optional>,<replaceable>SUBSYSTEM</replaceable></optional></replaceable></option></term>
<listitem>
<para>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 <option>--prioritized-subsystem=block,net</option>, 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,
<option>--prioritized-subsystem=block --prioritized-subsystem=net</option> is equivalent to
<option>--prioritized-subsystem=block,net</option>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-s</option></term>
<term><option>--subsystem-match=<replaceable>SUBSYSTEM</replaceable></option></term>
@ -382,6 +395,35 @@
then each matching result is ORed, that is, all children of each specified device are triggered.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--initialized-match</option></term>
<term><option>--initialized-nomatch</option></term>
<listitem>
<para>When <option>--initialized-match</option> is specified, trigger events for devices
that are already initialized by <command>systemd-udevd</command>, and skip devices that
are not initialized yet.</para>
<para>When <option>--initialized-nomatch</option> is specified, trigger events for devices
that are not initialized by <command>systemd-udevd</command> yet, and skip devices that
are already initialized.</para>
<para>Here, initialized devices are those for which at least one udev rule already
completed execution for any action but <literal>remove</literal> — 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 <literal>remove</literal> 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.</para>
<para>WARNING: <option>--initialized-nomatch</option> 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.
<literal>ATTRS</literal> or <literal>IMPORT{parent}</literal> keys, see
<citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for more details), the final state of the device becomes easily unstable with this option.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-w</option></term>
<term><option>--settle</option></term>

View File

@ -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 )

View File

@ -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] )) ||

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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