systemctl: add support for --wait to is-system-running

This makes it possible to wait until boot is finished without having to poll
for this command repeatedly, instead using the syntax:

  $ systemctl is-system-running --wait

Waiting is implemented by waiting for the StartupFinished signal to be posted
on the bus.

Register the matcher before checking for the property to avoid race conditions.

Tested by artificially delaying startup with a oneshot service and calling this
command, checked that it emitted `running` and exited with a 0 return code as
soon as the delay service completed startup.

Also tested that booting to degraded state unblocks the command.

Inserted a delay between getting the property and waiting for the signal and
confirmed this seems to work free of race conditions.

Updated the --help text (under --wait) and the man page to document the new
feature.
This commit is contained in:
Filipe Brandenburger
2018-08-07 09:33:25 +02:00
committed by Lennart Poettering
parent 4027f96aa0
commit adb6cd9be2
2 changed files with 65 additions and 1 deletions
+53 -1
View File
@@ -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"