diff --git a/man/udevadm.xml b/man/udevadm.xml index 44be7b3f894..1aed86a164a 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -319,6 +319,14 @@ the same command to finish. + + + + Before triggering uevents, wait for systemd-udevd daemon to be initialized. + Optionally takes timeout value. Default timeout is 5 seconds. This is equivalent to invoke + invoking udevadm control --ping before udevadm trigger. + + @@ -419,6 +427,13 @@ same time. + + + + Send a ping message to systemd-udevd and wait for the reply. This may be useful to check that + systemd-udevd daemon is running. + + seconds diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm index 7b33839b1e9..49db2904a48 100644 --- a/shell-completion/bash/udevadm +++ b/shell-completion/bash/udevadm @@ -50,12 +50,12 @@ _udevadm() { [DEBUG]='-d --debug' [INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db' [INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file' - [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -w --settle' + [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -w --settle --wait-daemon' [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' [SETTLE]='-t --timeout -E --exit-if-exists' - [CONTROL_STANDALONE]='-e --exit -s --stop-exec-queue -S --start-exec-queue -R --reload' + [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' [MONITOR_STANDALONE]='-k --kernel -u --udev -p --property' [MONITOR_ARG]='-s --subsystem-match -t --tag-match' diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index cb36c7e537d..c217815ac69 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -213,7 +213,7 @@ static struct udev_ctrl_connection *udev_ctrl_connection_free(struct udev_ctrl_c DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl_connection, udev_ctrl_connection, udev_ctrl_connection_free); -static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, int timeout) { +static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf, usec_t timeout) { struct udev_ctrl_msg_wire ctrl_msg_wire = { .version = "udev-" STRINGIFY(PROJECT_VERSION), .magic = UDEV_CTRL_MAGIC, @@ -241,7 +241,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int }; int r; - r = poll(&pfd, 1, timeout * MSEC_PER_SEC); + r = poll(&pfd, 1, DIV_ROUND_UP(timeout, USEC_PER_MSEC)); if (r < 0) { if (errno == EINTR) continue; @@ -255,35 +255,35 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int } } -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout) { +int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, usec_t timeout) { return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); } -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout) { +int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, usec_t timeout) { return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); } -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout) { +int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, usec_t timeout) { return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); } -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout) { +int udev_ctrl_send_reload(struct udev_ctrl *uctrl, usec_t timeout) { return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); } -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout) { +int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, usec_t timeout) { return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); } -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout) { +int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, usec_t timeout) { return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); } -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout) { +int udev_ctrl_send_ping(struct udev_ctrl *uctrl, usec_t timeout) { return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); } -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout) { +int udev_ctrl_send_exit(struct udev_ctrl *uctrl, usec_t timeout) { return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); } diff --git a/src/udev/udev-ctrl.h b/src/udev/udev-ctrl.h index 87021cb880e..8e452a42497 100644 --- a/src/udev/udev-ctrl.h +++ b/src/udev/udev-ctrl.h @@ -2,6 +2,7 @@ #pragma once #include "macro.h" +#include "time-util.h" struct udev_ctrl; struct udev_ctrl *udev_ctrl_new(void); @@ -10,14 +11,14 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); struct udev_ctrl *udev_ctrl_unref(struct udev_ctrl *uctrl); int udev_ctrl_cleanup(struct udev_ctrl *uctrl); int udev_ctrl_get_fd(struct udev_ctrl *uctrl); -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, int timeout); -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, int timeout); -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, int timeout); -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, int timeout); +int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, usec_t timeout); +int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, usec_t timeout); +int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, usec_t timeout); +int udev_ctrl_send_reload(struct udev_ctrl *uctrl, usec_t timeout); +int udev_ctrl_send_ping(struct udev_ctrl *uctrl, usec_t timeout); +int udev_ctrl_send_exit(struct udev_ctrl *uctrl, usec_t timeout); +int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, usec_t timeout); +int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, usec_t timeout); struct udev_ctrl_connection; struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl); diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index cb9b4cc89ee..7cfc4c929d1 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -40,6 +40,7 @@ static int help(void) { " -R --reload Reload rules and databases\n" " -p --property=KEY=VALUE Set a global property for all events\n" " -m --children-max=N Maximum number of children\n" + " --ping Wait for udev to respond to a ping message\n" " -t --timeout=SECONDS Maximum time to block for a reply\n" , program_invocation_short_name); @@ -48,22 +49,27 @@ static int help(void) { int control_main(int argc, char *argv[], void *userdata) { _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL; - int timeout = 60; + usec_t timeout = 60 * USEC_PER_SEC; int c, r; + enum { + ARG_PING = 0x100, + }; + static const struct option options[] = { - { "exit", no_argument, NULL, 'e' }, - { "log-priority", required_argument, NULL, 'l' }, - { "stop-exec-queue", no_argument, NULL, 's' }, - { "start-exec-queue", no_argument, NULL, 'S' }, - { "reload", no_argument, NULL, 'R' }, - { "reload-rules", no_argument, NULL, 'R' }, /* alias for -R */ - { "property", required_argument, NULL, 'p' }, - { "env", required_argument, NULL, 'p' }, /* alias for -p */ - { "children-max", required_argument, NULL, 'm' }, - { "timeout", required_argument, NULL, 't' }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, + { "exit", no_argument, NULL, 'e' }, + { "log-priority", required_argument, NULL, 'l' }, + { "stop-exec-queue", no_argument, NULL, 's' }, + { "start-exec-queue", no_argument, NULL, 'S' }, + { "reload", no_argument, NULL, 'R' }, + { "reload-rules", no_argument, NULL, 'R' }, /* alias for -R */ + { "property", required_argument, NULL, 'p' }, + { "env", required_argument, NULL, 'p' }, /* alias for -p */ + { "children-max", required_argument, NULL, 'm' }, + { "ping", no_argument, NULL, ARG_PING }, + { "timeout", required_argument, NULL, 't' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, {} }; @@ -77,11 +83,12 @@ int control_main(int argc, char *argv[], void *userdata) { } if (argc <= 1) - log_error("Option missing"); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "This command expects one or more options."); uctrl = udev_ctrl_new(); if (!uctrl) - return -ENOMEM; + return log_oom(); while ((c = getopt_long(argc, argv, "el:sSRp:m:t:Vh", options, NULL)) >= 0) switch (c) { @@ -115,10 +122,9 @@ int control_main(int argc, char *argv[], void *userdata) { return r; break; case 'p': - if (!strchr(optarg, '=')) { - log_error("expect = instead of '%s'", optarg); - return -EINVAL; - } + if (!strchr(optarg, '=')) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "expect = instead of '%s'", optarg); + r = udev_ctrl_send_set_env(uctrl, optarg, timeout); if (r < 0) return r; @@ -135,19 +141,16 @@ int control_main(int argc, char *argv[], void *userdata) { return r; break; } - case 't': { - usec_t s; - - r = parse_sec(optarg, &s); + case ARG_PING: + r = udev_ctrl_send_ping(uctrl, timeout); if (r < 0) - return log_error_errno(r, "Failed to parse timeout value '%s'.", optarg); - - if (DIV_ROUND_UP(s, USEC_PER_SEC) > INT_MAX) - log_error("Timeout value is out of range, ignoring."); - else - timeout = s != USEC_INFINITY ? (int) DIV_ROUND_UP(s, USEC_PER_SEC) : INT_MAX; + return log_error_errno(r, "Failed to connect to udev daemon: %m"); + break; + case 't': + r = parse_sec(optarg, &timeout); + if (r < 0) + return log_error_errno(r, "Failed to parse timeout value '%s': %m", optarg); break; - } case 'V': return print_version(); case 'h': @@ -158,13 +161,9 @@ int control_main(int argc, char *argv[], void *userdata) { assert_not_reached("Unknown option."); } - if (optind < argc) { - log_error("Extraneous argument: %s", argv[optind]); - return -EINVAL; - } else if (optind == 1) { - log_error("Option missing"); - return -EINVAL; - } + if (optind < argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Extraneous argument: %s", argv[optind]); return 0; } diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 140950df620..ae38e77a83c 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -387,10 +387,8 @@ int info_main(int argc, char *argv[], void *userdata) { query = QUERY_PATH; else if (streq(optarg, "all")) query = QUERY_ALL; - else { - log_error("unknown query type"); - return -EINVAL; - } + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "unknown query type"); break; case 'r': arg_root = true; diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c index 9b05e9acf06..8d9c4509ff8 100644 --- a/src/udev/udevadm-settle.c +++ b/src/udev/udevadm-settle.c @@ -102,9 +102,9 @@ int settle_main(int argc, char *argv[], void *userdata) { uctrl = udev_ctrl_new(); if (uctrl) { - r = udev_ctrl_send_ping(uctrl, MAX(5U, arg_timeout / USEC_PER_SEC)); + r = udev_ctrl_send_ping(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout)); if (r < 0) { - log_debug_errno(r, "Failed to connect to udev daemon."); + log_debug_errno(r, "Failed to connect to udev daemon: %m"); return 0; } } diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index aa9ebd5ac57..95329469e38 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -10,11 +10,13 @@ #include "fd-util.h" #include "fileio.h" #include "path-util.h" +#include "process-util.h" #include "set.h" #include "string-util.h" #include "strv.h" #include "udevadm.h" #include "udevadm-util.h" +#include "udev-ctrl.h" #include "virt.h" static bool arg_verbose = false; @@ -118,6 +120,8 @@ static int help(void) { " --name-match=NAME Trigger devices with this /dev name\n" " -b --parent-match=NAME Trigger devices with that parent device\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" , program_invocation_short_name); return 0; @@ -126,6 +130,7 @@ static int help(void) { int trigger_main(int argc, char *argv[], void *userdata) { enum { ARG_NAME = 0x100, + ARG_PING, }; static const struct option options[] = { @@ -143,6 +148,7 @@ int trigger_main(int argc, char *argv[], void *userdata) { { "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' }, {} @@ -156,7 +162,8 @@ int trigger_main(int argc, char *argv[], void *userdata) { _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL; _cleanup_set_free_free_ Set *settle_set = NULL; - bool settle = false; + usec_t ping_timeout_usec = 5 * USEC_PER_SEC; + bool settle = false, ping = false; int c, r; if (running_in_chroot() > 0) { @@ -188,18 +195,14 @@ int trigger_main(int argc, char *argv[], void *userdata) { device_type = TYPE_DEVICES; else if (streq(optarg, "subsystems")) device_type = TYPE_SUBSYSTEMS; - else { - log_error("Unknown type --type=%s", optarg); - return -EINVAL; - } + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg); break; case 'c': if (STR_IN_SET(optarg, "add", "remove", "change")) action = optarg; - else { - log_error("Unknown action '%s'", optarg); - return -EINVAL; - } + else + log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown action '%s'", optarg); break; case 's': @@ -275,6 +278,16 @@ int trigger_main(int argc, char *argv[], void *userdata) { break; } + case ARG_PING: { + ping = true; + if (optarg) { + r = parse_sec(optarg, &ping_timeout_usec); + if (r < 0) + log_error_errno(r, "Failed to parse timeout value '%s', ignoring: %m", optarg); + } + break; + } + case 'V': return print_version(); case 'h': @@ -286,6 +299,24 @@ int trigger_main(int argc, char *argv[], void *userdata) { } } + if (!arg_dry_run || ping) { + r = must_be_root(); + if (r < 0) + return r; + } + + if (ping) { + _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL; + + uctrl = udev_ctrl_new(); + if (!uctrl) + return log_oom(); + + r = udev_ctrl_send_ping(uctrl, ping_timeout_usec); + if (r < 0) + return log_error_errno(r, "Failed to connect to udev daemon: %m"); + } + for (; optind < argc; optind++) { _cleanup_(sd_device_unrefp) sd_device *dev = NULL; diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 1125c54b9f5..28194314de4 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -113,12 +113,13 @@ static int run(int argc, char *argv[]) { udev_parse_config(); log_parse_environment(); log_open(); - log_set_max_level_realm(LOG_REALM_SYSTEMD, log_get_max_level()); r = parse_argv(argc, argv); if (r <= 0) return r; + log_set_max_level_realm(LOG_REALM_SYSTEMD, log_get_max_level()); + mac_selinux_init(); return udevadm_main(argc, argv); } diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 51f9bfdd3f6..c637b98c7c7 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1027,7 +1027,8 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd i = udev_ctrl_get_set_log_level(ctrl_msg); if (i >= 0) { log_debug("Received udev control message (SET_LOG_LEVEL), setting log_priority=%i", i); - log_set_max_level(i); + log_set_max_level_realm(LOG_REALM_UDEV, i); + log_set_max_level_realm(LOG_REALM_SYSTEMD, i); manager_kill_workers(manager); } @@ -1112,7 +1113,7 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd } if (udev_ctrl_get_ping(ctrl_msg) > 0) - log_debug("Received udev control message (SYNC)"); + log_debug("Received udev control message (PING)"); if (udev_ctrl_get_exit(ctrl_msg) > 0) { log_debug("Received udev control message (EXIT)");