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-03 23:10:54 -07:00 committed by Lennart Poettering
parent 4027f96aa0
commit adb6cd9be2
2 changed files with 65 additions and 1 deletions

View File

@ -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 <literal>RemainAfterExit=yes</literal>.</para>
<para>When used with <command>is-system-running</command>, wait
until the boot process is completed before returning.</para>
</listitem>
</varlistentry>
@ -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 <option>--quiet</option> to
suppress this output.</para>
<para>Use <option>--wait</option> to wait until the boot
process is completed before printing the current state and
returning the appropriate error status. If <option>--wait</option>
is in use, states <varname>initializing</varname> or
<varname>starting</varname> will not be reported, instead
the command will block until a later state (such as
<varname>running</varname> or <varname>degraded</varname>)
is reached.</para>
<table>
<title><command>is-system-running</command> output</title>
<tgroup cols='3'>

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"