From 6be4dab09578dc593d539fd03b07bf82a977e910 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 30 Apr 2024 22:03:00 +0200 Subject: [PATCH] systemctl: Implement --wait for kill command TEST-26-SYSTEMCTL is racy as we call systemctl is-active immediately after systemctl kill. Let's implement --wait for systemctl kill and use it in TEST-26-SYSTEMCTL to avoid the race. --- NEWS | 3 +++ man/systemctl.xml | 16 +++++++++------- src/systemctl/systemctl-kill.c | 27 ++++++++++++++++++++++++--- src/systemctl/systemctl.c | 1 + test/units/testsuite-26.sh | 2 +- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index ba206afdfbc..0259d79d763 100644 --- a/NEWS +++ b/NEWS @@ -258,6 +258,9 @@ CHANGES WITH 256-rc1: that configures what to do after the system manager (PID 1) crashes. This can also be configured through CrashAction= in systemd.conf. + * "systemctl kill" now supports --wait which will make the command wait + until the signalled services terminate. + Journal: * systemd-journald can now forward journal entries to a socket diff --git a/man/systemctl.xml b/man/systemctl.xml index a68ff5c958b..287decffb21 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -2377,14 +2377,16 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - Synchronously wait for started units to terminate again. - This option may not be combined with . - Note that this will wait forever if any given unit never terminates - (by itself or by getting stopped explicitly); particularly services - which use RemainAfterExit=yes. + When used with start or restart, synchronously wait for + started units to terminate again. This option may not be combined with . + Note that this will wait forever if any given unit never terminates (by itself or by getting + stopped explicitly); particularly services which use RemainAfterExit=yes. - When used with is-system-running, wait - until the boot process is completed before returning. + When used with is-system-running, wait until the boot process is completed + before returning. + + When used with kill, wait until the signalled units terminate. Note that + this will wait forever if any given unit never terminates. diff --git a/src/systemctl/systemctl-kill.c b/src/systemctl/systemctl-kill.c index c4c6096dc3d..4c1829e0c68 100644 --- a/src/systemctl/systemctl-kill.c +++ b/src/systemctl/systemctl-kill.c @@ -2,11 +2,13 @@ #include "bus-error.h" #include "bus-locator.h" +#include "bus-wait-for-units.h" #include "systemctl-kill.h" #include "systemctl-util.h" #include "systemctl.h" int verb_kill(int argc, char *argv[], void *userdata) { + _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL; _cleanup_strv_free_ char **names = NULL; const char *kill_whom; sd_bus *bus; @@ -16,6 +18,12 @@ int verb_kill(int argc, char *argv[], void *userdata) { if (r < 0) return r; + if (arg_wait) { + r = bus_wait_for_units_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Failed to allocate unit watch context: %m"); + } + polkit_agent_open_maybe(); kill_whom = arg_kill_whom ?: "all"; @@ -48,10 +56,23 @@ int verb_kill(int argc, char *argv[], void *userdata) { NULL, "ssi", *name, kill_whom, arg_signal); if (q < 0) { - log_error_errno(q, "Failed to kill unit %s: %s", *name, bus_error_message(&error, q)); - if (r == 0) - r = q; + RET_GATHER(r, log_error_errno(q, "Failed to kill unit %s: %s", *name, bus_error_message(&error, q))); + continue; } + + if (w) { + q = bus_wait_for_units_add_unit(w, *name, BUS_WAIT_FOR_INACTIVE|BUS_WAIT_NO_JOB, NULL, NULL); + if (q < 0) + RET_GATHER(r, log_error_errno(q, "Failed to watch unit %s: %m", *name)); + } + } + + if (w) { + q = bus_wait_for_units_run(w); + if (q < 0) + return log_error_errno(q, "Failed to wait for units: %m"); + if (q == BUS_WAIT_FAILURE) + RET_GATHER(r, -EIO); } return r; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index f159844c5eb..0ca76ac23db 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -305,6 +305,7 @@ static int systemctl_help(void) { " --no-warn Suppress several warnings shown by default\n" " --wait For (re)start, wait until service stopped again\n" " For is-system-running, wait until startup is completed\n" + " For kill, wait until service stopped\n" " --no-block Do not wait until operation finished\n" " --no-wall Don't send wall message before halt/power-off/reboot\n" " --message=MESSAGE Specify human readable reason for system shutdown\n" diff --git a/test/units/testsuite-26.sh b/test/units/testsuite-26.sh index 61b0ffd8126..117ffb81f31 100755 --- a/test/units/testsuite-26.sh +++ b/test/units/testsuite-26.sh @@ -141,7 +141,7 @@ systemctl reload -T "$UNIT_NAME" systemctl restart -T "$UNIT_NAME" systemctl try-restart --show-transaction "$UNIT_NAME" systemctl try-reload-or-restart --show-transaction "$UNIT_NAME" -systemctl kill "$UNIT_NAME" +timeout 10 systemctl kill --wait "$UNIT_NAME" (! systemctl is-active "$UNIT_NAME") systemctl restart "$UNIT_NAME" systemctl is-active "$UNIT_NAME"