From 07111a11ede9f994e08109e1daebf1cb5e2abef0 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 31 Oct 2026 15:17:27 +0900 Subject: [PATCH] udevadm-wait: add --notify option to send notifications With this, 'udevadm wait' can be used in a Type=notify service, and the caller can know when it has started watching the devices. --- man/udevadm.xml | 13 ++++++++++++ shell-completion/bash/udevadm | 2 +- shell-completion/zsh/_udevadm | 1 + src/udev/udevadm-wait.c | 39 +++++++++++++++++++++++++++++++++-- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/man/udevadm.xml b/man/udevadm.xml index fc09a6dfcf0..e8fcdb58f53 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -1178,6 +1178,19 @@ + + + + + When specified, udevadm notifies the calling service manager with a + READY=1 message when it is ready to wait for devices to be initialized (or + added when , or removed when ), and + notifies with a STOPPING=1 message when all devices are initialized. This may + be useful when the command is called in a service with Type=notify. + + + + diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm index adb06a40ee3..34393dc6233 100644 --- a/shell-completion/bash/udevadm +++ b/shell-completion/bash/udevadm @@ -105,7 +105,7 @@ _udevadm() { [VERIFY_ARG]='-N --resolve-names --root' [CAT_STANDALONE]='--tldr --config' [CAT_ARG]='--root' - [WAIT]='-t --timeout --initialized=no --removed --settle' + [WAIT]='-t --timeout --initialized=no --removed --settle --notify' [LOCK]='-t --timeout -d --device -b --backing -p --print' ) diff --git a/shell-completion/zsh/_udevadm b/shell-completion/zsh/_udevadm index e0112b3ed56..63e18e59bdd 100644 --- a/shell-completion/zsh/_udevadm +++ b/shell-completion/zsh/_udevadm @@ -151,6 +151,7 @@ _udevadm_wait(){ '--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.]' \ + '--notify[Send notifications to service manager.]' \ '*::devpath:_files -W /dev' } diff --git a/src/udev/udevadm-wait.c b/src/udev/udevadm-wait.c index 229b3dab01a..ae365b02098 100644 --- a/src/udev/udevadm-wait.c +++ b/src/udev/udevadm-wait.c @@ -7,6 +7,7 @@ #include "sd-json.h" #include "build.h" +#include "daemon-util.h" #include "device-monitor-private.h" #include "device-util.h" #include "event-util.h" @@ -15,6 +16,7 @@ #include "parse-util.h" #include "path-util.h" #include "static-destruct.h" +#include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" @@ -35,6 +37,7 @@ static WaitUntil arg_wait_until = WAIT_UNTIL_INITIALIZED; static usec_t arg_timeout_usec = USEC_INFINITY; static bool arg_removed = false; static bool arg_settle = false; +static bool arg_notify = false; static char **arg_devices = NULL; STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep); @@ -340,6 +343,10 @@ static int parse_argv(int argc, char *argv[]) { arg_settle = true; break; + OPTION_LONG("notify", NULL, "Send notifications to service manager"): + arg_notify = true; + break; + OPTION_COMMON_INTROSPECT_CLI: return introspect_cli(SD_JSON_FORMAT_OFF); } @@ -380,9 +387,31 @@ int verb_wait_main(int argc, char *argv[], uintptr_t _data, void *userdata) { "Specified path \"%s\" does not start with \"/dev/\" or \"/sys/\".", *p); } + _cleanup_free_ char *notify_message_start = NULL, *notify_message_stop = NULL, *notify_message_fail = NULL; + if (arg_notify) { + notify_message_start = asprintf_safe( + "READY=1\n" + "STATUS=Waiting for devices to be %s...", + wait_until_to_string(arg_wait_until)); + notify_message_stop = asprintf_safe( + "STOPPING=1\n" + "STATUS=All devices are %s.", + wait_until_to_string(arg_wait_until)); + notify_message_fail = asprintf_safe( + "STATUS=Failed to wait for devices to be %s.", + wait_until_to_string(arg_wait_until)); + if (!notify_message_start || !notify_message_stop || !notify_message_fail) + return log_oom(); + } + /* Check before configuring event sources, as devices may be already initialized. */ - if (check()) + if (check()) { + if (arg_notify) { + (void) sd_notify(/* unset_environment= */ false, notify_message_start); + (void) sd_notify(/* unset_environment= */ false, notify_message_stop); + } return 0; + } r = sd_event_default(&event); if (r < 0) @@ -423,9 +452,13 @@ int verb_wait_main(int argc, char *argv[], uintptr_t _data, void *userdata) { return log_error_errno(r, "Failed to set up periodic timer: %m"); } + _unused_ _cleanup_(notify_on_cleanup) const char *notify_message = NULL; + if (arg_notify) + notify_message = notify_start(notify_message_start, notify_message_fail); + /* Check before entering the event loop, as devices may be initialized during setting up event sources. */ if (check()) - return 0; + goto success; r = sd_event_loop(event); if (r == -ETIMEDOUT) @@ -434,5 +467,7 @@ int verb_wait_main(int argc, char *argv[], uintptr_t _data, void *userdata) { if (r < 0) return log_error_errno(r, "Event loop failed: %m"); +success: + notify_message = notify_message_stop; return 0; }