mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 04:03:36 +08:00
udevadm: introduce new 'wait' command
Prompted by https://github.com/systemd/systemd/pull/22717#issuecomment-1067348496. The new command 'udevadm wait' waits for device or device symlink being created. This may be useful to wait for a device is processed by udevd after e.g. formatting or partitioning the device.
This commit is contained in:
parent
209294ad24
commit
aa2b0d8d29
@ -48,6 +48,9 @@
|
||||
<cmdsynopsis>
|
||||
<command>udevadm test-builtin <optional>options</optional> <replaceable>command</replaceable> <replaceable>devpath</replaceable></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>udevadm wait <optional>options</optional> <replaceable>device|syspath</replaceable></command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1><title>Description</title>
|
||||
@ -405,15 +408,9 @@
|
||||
<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>Typically, it is 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. See also Initialized Devices section below for more details.</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.
|
||||
@ -694,6 +691,73 @@
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>udevadm wait
|
||||
<arg choice="opt"><replaceable>options</replaceable></arg>
|
||||
<arg choice="opt"><replaceable>device|syspath</replaceable></arg>
|
||||
…
|
||||
</title>
|
||||
|
||||
<para>Wait for devices or device symlinks being created and initialized by
|
||||
<command>systemd-udevd</command>. Each device path must start with
|
||||
<literal>/dev/</literal> or <literal>/sys/</literal>, e.g. <literal>/dev/sda</literal>,
|
||||
<literal>/dev/disk/by-path/pci-0000:3c:00.0-nvme-1-part1</literal>,
|
||||
<literal>/sys/devices/pci0000:00/0000:00:1f.6/net/eth0</literal>, or
|
||||
<literal>/sys/class/net/eth0</literal>. This can take multiple devices. This may be useful for
|
||||
waiting for devices being processed by <command>systemd-udevd</command> after e.g. partitioning
|
||||
or formatting the devices.</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>-t</option></term>
|
||||
<term><option>--timeout=<replaceable>SECONDS</replaceable></option></term>
|
||||
<listitem>
|
||||
<para>Maximum number of seconds to wait for the specified devices or device symlinks being
|
||||
created, initialized, or removed. The default value is <literal>infinity</literal>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--initialized=<replaceable>BOOL</replaceable></option></term>
|
||||
<listitem>
|
||||
<para>Check if <command>systemd-udevd</command> initialized devices. Defaults to true. When
|
||||
false, the command only checks if the specified devices exist. Set false to this setting if
|
||||
there is no udev rules for the specified devices, as the devices will never be considered
|
||||
as initialized in that case. See Initialized Devices section below for more details.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--removed</option></term>
|
||||
<listitem>
|
||||
<para>When specified, the command wait for devices being removed instead of created or
|
||||
initialized. If this is specified, <option>--initialized=</option> will be ignored.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--settle</option></term>
|
||||
<listitem>
|
||||
<para>When specified, also watches the udev event queue, and wait for all queued events
|
||||
being processed by <command>systemd-udevd</command>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Initialized Devices</title>
|
||||
|
||||
<para>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).</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -32,7 +32,7 @@ __get_all_sysdevs() {
|
||||
|
||||
__get_all_devs() {
|
||||
local i
|
||||
for i in /dev/* /dev/*/*; do
|
||||
for i in /dev/* /dev/*/* /dev/*/*/*; do
|
||||
echo $i
|
||||
done
|
||||
}
|
||||
@ -64,9 +64,10 @@ _udevadm() {
|
||||
[MONITOR_ARG]='-s --subsystem-match -t --tag-match'
|
||||
[TEST]='-a --action -N --resolve-names'
|
||||
[TEST_BUILTIN]='-a --action'
|
||||
[WAIT]='-t --timeout --initialized=no --removed --settle'
|
||||
)
|
||||
|
||||
local verbs=(info trigger settle control monitor test-builtin test)
|
||||
local verbs=(info trigger settle control monitor test-builtin test wait)
|
||||
local builtins=(blkid btrfs hwdb input_id keyboard kmod net_id net_setup_link path_id usb_id uaccess)
|
||||
|
||||
for ((i=0; i < COMP_CWORD; i++)); do
|
||||
@ -245,6 +246,25 @@ _udevadm() {
|
||||
fi
|
||||
;;
|
||||
|
||||
'wait')
|
||||
if __contains_word "$prev" ${OPTS[WAIT]}; then
|
||||
case $prev in
|
||||
*)
|
||||
comps=''
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ $cur = -* ]]; then
|
||||
comps="${OPTS[COMMON]} ${OPTS[WAIT]}"
|
||||
else
|
||||
comps=$( __get_all_devs )
|
||||
local IFS=$'\n'
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
comps=${VERBS[*]}
|
||||
;;
|
||||
|
@ -104,6 +104,17 @@ _udevadm_test-builtin(){
|
||||
fi
|
||||
}
|
||||
|
||||
(( $+functions[_udevadm_wait] )) ||
|
||||
_udevadm_wait(){
|
||||
_arguments \
|
||||
'--timeout=[Maximum number of seconds to wait for the devices being created.]' \
|
||||
'--initialized=[Wait for devices being initialized by systemd-udevd.]:boolean:(yes no)' \
|
||||
'--removed[Wait for devices being removed.]' \
|
||||
'--settle[Also wait for udev queue being empty.]' \
|
||||
'--help[Print help text.]' \
|
||||
'*::devpath:_files -P /dev/ -W /dev'
|
||||
}
|
||||
|
||||
(( $+functions[_udevadm_mounts] )) ||
|
||||
_udevadm_mounts(){
|
||||
local dev_tmp dpath_tmp mp_tmp mline
|
||||
|
@ -13,6 +13,7 @@ udevadm_sources = files(
|
||||
'udevadm-trigger.c',
|
||||
'udevadm-util.c',
|
||||
'udevadm-util.h',
|
||||
'udevadm-wait.c',
|
||||
'udevd.c',
|
||||
)
|
||||
|
||||
|
378
src/udev/udevadm-wait.c
Normal file
378
src/udev/udevadm-wait.c
Normal file
@ -0,0 +1,378 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chase-symlinks.h"
|
||||
#include "device-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "inotify-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "static-destruct.h"
|
||||
#include "string-table.h"
|
||||
#include "strv.h"
|
||||
#include "udev-util.h"
|
||||
#include "udevadm.h"
|
||||
|
||||
typedef enum WaitUntil {
|
||||
WAIT_UNTIL_INITIALIZED,
|
||||
WAIT_UNTIL_ADDED,
|
||||
WAIT_UNTIL_REMOVED,
|
||||
_WAIT_UNTIL_MAX,
|
||||
_WAIT_UNTIL_INVALID = -EINVAL,
|
||||
} WaitUntil;
|
||||
|
||||
static WaitUntil arg_wait_until = WAIT_UNTIL_INITIALIZED;
|
||||
static usec_t arg_timeout_usec = USEC_INFINITY;
|
||||
static bool arg_settle = false;
|
||||
static char **arg_devices = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep);
|
||||
|
||||
static const char * const wait_until_table[_WAIT_UNTIL_MAX] = {
|
||||
[WAIT_UNTIL_INITIALIZED] = "initialized",
|
||||
[WAIT_UNTIL_ADDED] = "added",
|
||||
[WAIT_UNTIL_REMOVED] = "removed",
|
||||
};
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wait_until, WaitUntil);
|
||||
|
||||
static int check_device(const char *path) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
r = sd_device_new_from_path(&dev, path);
|
||||
if (r == -ENODEV)
|
||||
return arg_wait_until == WAIT_UNTIL_REMOVED;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (arg_wait_until) {
|
||||
case WAIT_UNTIL_INITIALIZED:
|
||||
return sd_device_get_is_initialized(dev);
|
||||
case WAIT_UNTIL_ADDED:
|
||||
return true;
|
||||
case WAIT_UNTIL_REMOVED:
|
||||
return false;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static bool check(void) {
|
||||
int r;
|
||||
|
||||
if (arg_settle) {
|
||||
r = udev_queue_is_empty();
|
||||
if (r == 0)
|
||||
return false;
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to check if udev queue is empty, assuming empty: %m");
|
||||
}
|
||||
|
||||
STRV_FOREACH(p, arg_devices) {
|
||||
r = check_device(*p);
|
||||
if (r <= 0) {
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to check if device \"%s\" is %s, assuming not %s: %m",
|
||||
*p,
|
||||
wait_until_to_string(arg_wait_until),
|
||||
wait_until_to_string(arg_wait_until));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int check_and_exit(sd_event *event) {
|
||||
assert(event);
|
||||
|
||||
if (check())
|
||||
return sd_event_exit(event, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
assert(monitor);
|
||||
assert(device);
|
||||
|
||||
if (device_for_action(device, SD_DEVICE_REMOVE) != (arg_wait_until == WAIT_UNTIL_REMOVED))
|
||||
return 0;
|
||||
|
||||
if (arg_wait_until == WAIT_UNTIL_REMOVED)
|
||||
/* On removed event, the received device may not contain enough information.
|
||||
* Let's unconditionally check all requested devices are removed. */
|
||||
return check_and_exit(sd_device_monitor_get_event(monitor));
|
||||
|
||||
/* For other events, at first check if the received device matches with the requested devices,
|
||||
* to avoid calling check() so many times within a short time. */
|
||||
|
||||
r = sd_device_get_sysname(device, &name);
|
||||
if (r < 0) {
|
||||
log_device_warning_errno(device, r, "Failed to get sysname of received device, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
STRV_FOREACH(p, arg_devices) {
|
||||
const char *s;
|
||||
|
||||
if (!path_startswith(*p, "/sys"))
|
||||
continue;
|
||||
|
||||
r = path_find_last_component(*p, false, NULL, &s);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to extract filename from \"%s\", ignoring: %m", *p);
|
||||
continue;
|
||||
}
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
if (strneq(s, name, r))
|
||||
return check_and_exit(sd_device_monitor_get_event(monitor));
|
||||
}
|
||||
|
||||
r = sd_device_get_devname(device, &name);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_device_warning_errno(device, r, "Failed to get devname of received device, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (path_strv_contains(arg_devices, name))
|
||||
return check_and_exit(sd_device_monitor_get_event(monitor));
|
||||
|
||||
STRV_FOREACH(p, arg_devices) {
|
||||
const char *link;
|
||||
|
||||
if (!path_startswith(*p, "/dev"))
|
||||
continue;
|
||||
|
||||
FOREACH_DEVICE_DEVLINK(device, link)
|
||||
if (path_equal(*p, link))
|
||||
return check_and_exit(sd_device_monitor_get_event(monitor));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_monitor(sd_event *event, sd_device_monitor **ret) {
|
||||
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
|
||||
int r;
|
||||
|
||||
assert(event);
|
||||
assert(ret);
|
||||
|
||||
r = sd_device_monitor_new(&monitor);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sd_device_monitor_set_receive_buffer_size(monitor, 128*1024*1024);
|
||||
|
||||
r = sd_device_monitor_attach_event(monitor, event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_device_monitor_start(monitor, device_monitor_handler, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_description(sd_device_monitor_get_event_source(monitor),
|
||||
"device-monitor-event-source");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(monitor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) {
|
||||
return check_and_exit(sd_event_source_get_event(s));
|
||||
}
|
||||
|
||||
static int setup_inotify(sd_event *event) {
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(event);
|
||||
|
||||
if (!arg_settle)
|
||||
return 0;
|
||||
|
||||
r = sd_event_add_inotify(event, &s, "/run/udev" , IN_CREATE | IN_DELETE, on_inotify, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_description(s, "inotify-event-source");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_event_source_set_floating(s, true);
|
||||
}
|
||||
|
||||
static int setup_timer(sd_event *event) {
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(event);
|
||||
|
||||
if (arg_timeout_usec == USEC_INFINITY)
|
||||
return 0;
|
||||
|
||||
r = sd_event_add_time_relative(event, &s, CLOCK_BOOTTIME, arg_timeout_usec, 0,
|
||||
NULL, INT_TO_PTR(-ETIMEDOUT));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_description(s, "timeout-event-source");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_event_source_set_floating(s, true);
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
printf("%s wait [OPTIONS] DEVICE [DEVICE…]\n\n"
|
||||
"Wait for devices or device symlinks being created.\n\n"
|
||||
" -h --help Print this message\n"
|
||||
" -V --version Print version of the program\n"
|
||||
" -t --timeout=SEC Maximum time to wait for the device\n"
|
||||
" --initialized=BOOL Wait for devices being initialized by systemd-udevd\n"
|
||||
" --removed Wait for devices being removed\n"
|
||||
" --settle Also wait for all queued events being processed\n",
|
||||
program_invocation_short_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_INITIALIZED = 0x100,
|
||||
ARG_REMOVED,
|
||||
ARG_SETTLE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "timeout", required_argument, NULL, 't' },
|
||||
{ "initialized", required_argument, NULL, ARG_INITIALIZED },
|
||||
{ "removed", no_argument, NULL, ARG_REMOVED },
|
||||
{ "settle", no_argument, NULL, ARG_SETTLE },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "t:hV", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
case 't':
|
||||
r = parse_sec(optarg, &arg_timeout_usec);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse -t/--timeout= parameter: %s", optarg);
|
||||
break;
|
||||
|
||||
case ARG_INITIALIZED:
|
||||
r = parse_boolean(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --initialized= parameter: %s", optarg);
|
||||
arg_wait_until = r ? WAIT_UNTIL_INITIALIZED : WAIT_UNTIL_ADDED;
|
||||
break;
|
||||
|
||||
case ARG_REMOVED:
|
||||
arg_wait_until = WAIT_UNTIL_REMOVED;
|
||||
break;
|
||||
|
||||
case ARG_SETTLE:
|
||||
arg_settle = true;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
return print_version();
|
||||
|
||||
case 'h':
|
||||
return help();
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Too few arguments, expected at least one device path or device symlink.");
|
||||
|
||||
arg_devices = strv_copy(argv + optind);
|
||||
if (!arg_devices)
|
||||
return log_oom();
|
||||
|
||||
return 1; /* work to do */
|
||||
}
|
||||
|
||||
int wait_main(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
int r;
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(p, arg_devices) {
|
||||
path_simplify(*p);
|
||||
|
||||
if (!path_is_safe(*p))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Device path cannot contain \"..\".");
|
||||
|
||||
if (!is_device_path(*p))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Specified path \"%s\" does not start with \"/dev/\" or \"/sys/\".", *p);
|
||||
}
|
||||
|
||||
/* Check before configuring event sources, as devices may be already initialized. */
|
||||
if (check())
|
||||
return 0;
|
||||
|
||||
r = sd_event_default(&event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to initialize sd-event: %m");
|
||||
|
||||
r = setup_timer(event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up timeout: %m");
|
||||
|
||||
r = setup_inotify(event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up inotify: %m");
|
||||
|
||||
r = setup_monitor(event, &monitor);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up device monitor: %m");
|
||||
|
||||
/* Check before entering the event loop, as devices may be initialized during setting up event sources. */
|
||||
if (check())
|
||||
return 0;
|
||||
|
||||
r = sd_event_loop(event);
|
||||
if (r == -ETIMEDOUT)
|
||||
return log_error_errno(r, "Timed out for waiting devices being %s.",
|
||||
wait_until_to_string(arg_wait_until));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Event loop failed: %m");
|
||||
|
||||
return 0;
|
||||
}
|
@ -19,13 +19,14 @@
|
||||
|
||||
static int help(void) {
|
||||
static const char *const short_descriptions[][2] = {
|
||||
{ "info", "Query sysfs or the udev database" },
|
||||
{ "trigger", "Request events from the kernel" },
|
||||
{ "settle", "Wait for pending udev events" },
|
||||
{ "control", "Control the udev daemon" },
|
||||
{ "monitor", "Listen to kernel and udev events" },
|
||||
{ "test", "Test an event run" },
|
||||
{ "test-builtin", "Test a built-in command" },
|
||||
{ "info", "Query sysfs or the udev database" },
|
||||
{ "trigger", "Request events from the kernel" },
|
||||
{ "settle", "Wait for pending udev events" },
|
||||
{ "control", "Control the udev daemon" },
|
||||
{ "monitor", "Listen to kernel and udev events" },
|
||||
{ "test", "Test an event run" },
|
||||
{ "test-builtin", "Test a built-in command" },
|
||||
{ "wait", "Wait for device or device symlink" },
|
||||
};
|
||||
|
||||
_cleanup_free_ char *link = NULL;
|
||||
@ -101,6 +102,7 @@ static int udevadm_main(int argc, char *argv[]) {
|
||||
{ "hwdb", VERB_ANY, VERB_ANY, 0, hwdb_main },
|
||||
{ "test", VERB_ANY, VERB_ANY, 0, test_main },
|
||||
{ "test-builtin", VERB_ANY, VERB_ANY, 0, builtin_main },
|
||||
{ "wait", VERB_ANY, VERB_ANY, 0, wait_main },
|
||||
{ "version", VERB_ANY, VERB_ANY, 0, version_main },
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, help_main },
|
||||
{}
|
||||
|
@ -13,6 +13,7 @@ int monitor_main(int argc, char *argv[], void *userdata);
|
||||
int hwdb_main(int argc, char *argv[], void *userdata);
|
||||
int test_main(int argc, char *argv[], void *userdata);
|
||||
int builtin_main(int argc, char *argv[], void *userdata);
|
||||
int wait_main(int argc, char *argv[], void *userdata);
|
||||
|
||||
static inline int print_version(void) {
|
||||
/* Dracut relies on the version being a single integer */
|
||||
|
Loading…
Reference in New Issue
Block a user