udevadm-wait: send notification (#44550)

This commit is contained in:
Yu Watanabe authored and GitHub committed 2026-11-01 08:28:04 +09:00
commit f57b0b3104
5 files changed
+100 -6

No files matched your search

+13
View File
@@ -1178,6 +1178,19 @@
<xi:include href="version-info.xml" xpointer="v251"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--notify</option></term>
<listitem>
<para>When specified, udevadm notifies the calling service manager with a
<literal>READY=1</literal> message when it is ready to wait for devices to be initialized (or
added when <option>--initialized=no</option>, or removed when <option>--removed</option>), and
notifies with a <literal>STOPPING=1</literal> message when all devices are initialized. This may
be useful when the command is called in a service with <varname>Type=notify</varname>.</para>
<xi:include href="version-info.xml" xpointer="v263"/>
</listitem>
</varlistentry>
</variablelist>
</refsect2>
+1 -1
View File
@@ -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'
)
+1
View File
@@ -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'
}
+37 -2
View File
@@ -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;
}
+48 -3
View File
@@ -4,15 +4,60 @@ set -ex
set -o pipefail
IFNAME=test-udev-aaa
UNIT_ADD="TEST-17-UDEV-database-wait-add.service"
UNIT_DEL="TEST-17-UDEV-database-wait-del.service"
WORKDIR="$(mktemp -d)"
# shellcheck disable=SC2329
at_exit() {
set +e
systemctl stop "$UNIT_ADD" "$UNIT_DEL"
systemctl reset-failed "$UNIT_ADD" "$UNIT_DEL"
ip link del "$IFNAME"
rm -rf "$WORKDIR"
}
trap at_exit EXIT
cat >"$WORKDIR/marker.sh" <<'EOF'
#!/usr/bin/env bash
[[ "$SERVICE_RESULT" == success ]] || exit 0
touch "$1"
EOF
chmod +x "$WORKDIR/marker.sh"
start_wait() {
local unit="$1"
shift
systemd-run --unit="$unit" \
--service-type=notify \
--property="ExecStopPost=$WORKDIR/marker.sh $WORKDIR/$unit" \
udevadm wait --timeout=10 --notify "$@"
}
finish_wait() {
local unit="$1"
# shellcheck disable=SC2016
timeout 30 bash -c 'until [[ -e "$1" ]] || systemctl is-failed --quiet "$2"; do sleep 0.1; done' _ "$WORKDIR/$unit" "$unit"
[[ -e "$WORKDIR/$unit" ]]
}
# Start watching before the interface is created, so that the creation cannot be missed.
start_wait "$UNIT_ADD" "/sys/class/net/$IFNAME"
ip link add "$IFNAME" type dummy
IFINDEX=$(ip -json link show "$IFNAME" | jq '.[].ifindex')
udevadm wait --timeout=10 "/sys/class/net/$IFNAME"
finish_wait "$UNIT_ADD"
# Check if the database file is created.
[[ -e "/run/udev/data/n$IFINDEX" ]]
# Start watching before the interface is removed, so that the removal cannot be missed.
start_wait "$UNIT_DEL" --removed --settle "/sys/class/net/$IFNAME"
ip link del "$IFNAME"
udevadm wait --timeout=10 --removed --settle "/sys/class/net/$IFNAME"
# CHeck if the database file is removed.
finish_wait "$UNIT_DEL"
# Check if the database file is removed.
[[ ! -e "/run/udev/data/n$IFINDEX" ]]
unshare --mount --propagation private bash -eux <<'EOF'