diff --git a/man/systemctl.xml b/man/systemctl.xml index 850135cbc0..318311a4c0 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -372,6 +372,9 @@ 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. @@ -1636,6 +1639,15 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err output, see the table below. Use to suppress this output. + Use to wait until the boot + process is completed before printing the current state and + returning the appropriate error status. If + is in use, states initializing or + starting will not be reported, instead + the command will block until a later state (such as + running or degraded) + is reached. + <command>is-system-running</command> output diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index abd4b92a24..046bffdf05 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -18,6 +18,7 @@ #include "sd-bus.h" #include "sd-daemon.h" +#include "sd-event.h" #include "sd-login.h" #include "alloc-util.h" @@ -6629,8 +6630,29 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { return enabled ? EXIT_SUCCESS : EXIT_FAILURE; } +static int match_startup_finished(sd_bus_message *m, void *userdata, sd_bus_error *error) { + char **state = userdata; + int r; + + assert(state); + + r = sd_bus_get_property_string( + sd_bus_message_get_bus(m), + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SystemState", + NULL, + state); + + sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), r); + return 0; +} + static int is_system_running(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_startup_finished = NULL; + _cleanup_(sd_event_unrefp) sd_event* event = NULL; _cleanup_free_ char *state = NULL; sd_bus *bus; int r; @@ -6645,6 +6667,25 @@ static int is_system_running(int argc, char *argv[], void *userdata) { if (r < 0) return r; + if (arg_wait) { + r = sd_event_default(&event); + if (r >= 0) + r = sd_bus_attach_event(bus, event, 0); + if (r >= 0) + r = sd_bus_match_signal_async( + bus, + &slot_startup_finished, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartupFinished", + match_startup_finished, NULL, &state); + if (r < 0) { + log_warning_errno(r, "Failed to request match for StartupFinished: %m"); + arg_wait = false; + } + } + r = sd_bus_get_property_string( bus, "org.freedesktop.systemd1", @@ -6654,13 +6695,23 @@ static int is_system_running(int argc, char *argv[], void *userdata) { &error, &state); if (r < 0) { - log_debug_errno(r, "Failed to query system state: %s", bus_error_message(&error, r)); + log_warning_errno(r, "Failed to query system state: %s", bus_error_message(&error, r)); if (!arg_quiet) puts("unknown"); return EXIT_FAILURE; } + if (arg_wait && STR_IN_SET(state, "initializing", "starting")) { + r = sd_event_loop(event); + if (r < 0) { + log_warning_errno(r, "Failed to get property from event loop: %m"); + if (!arg_quiet) + puts("unknown"); + return EXIT_FAILURE; + } + } + if (!arg_quiet) puts(state); @@ -7082,6 +7133,7 @@ static void systemctl_help(void) { " --dry-run Only print what would be done\n" " -q --quiet Suppress output\n" " --wait For (re)start, wait until service stopped again\n" + " For is-system-running, wait until startup is completed\n" " --no-block Do not wait until operation finished\n" " --no-wall Don't send wall message before halt/power-off/reboot\n" " --no-reload Don't reload daemon after en-/dis-abling unit files\n"