]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
systemctl: add support for --wait to is-system-running
authorFilipe Brandenburger <filbranden@google.com>
Sat, 4 Aug 2018 06:10:54 +0000 (23:10 -0700)
committerLennart Poettering <lennart@poettering.net>
Tue, 7 Aug 2018 07:33:25 +0000 (09:33 +0200)
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.

man/systemctl.xml
src/systemctl/systemctl.c

index 850135cbc00f1f3c6e38a95bf42451ea4aea4f96..318311a4c016451b74c0d54b36f4a5ffd6f3fd6e 100644 (file)
           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'>
index abd4b92a24d9850c7212b7784f7be306e32a1ea8..046bffdf05536a0f2ab61174766e1272137561cb 100644 (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"