]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
logind: add PrepareForShutdownWithMetadata signal
authorLuca Boccassi <bluca@debian.org>
Wed, 30 Aug 2023 18:51:13 +0000 (19:51 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Mon, 11 Sep 2023 11:56:00 +0000 (12:56 +0100)
The existing signal doesn't say which type of shutdown is going to happen.
With the introduction of soft-reboot, it is useful to have this information
broadcasted, so that clients can choose to do different things based on the
reboot type.
Add a{sv} as the payload so that more metadata can be added later if
needed, without needing to add yet another signal.
Send both old and new signal for backward compatibility, and send the new
one first so that clients can just wait for the first one on both old and
new systems.

man/org.freedesktop.login1.xml
src/login/logind-action.c
src/login/logind-dbus.c
test/units/testsuite-82.sh

index 867662e057a39bb119e3343088448be653855d1e..5f7b711aaab734bfb80ae7410a70344f135bdaa9 100644 (file)
@@ -156,6 +156,8 @@ node /org/freedesktop/login1 {
       SeatRemoved(s seat_id,
                   o object_path);
       PrepareForShutdown(b start);
+      PrepareForShutdownWithMetadata(b start,
+                                     a{sv} metadata);
       PrepareForSleep(b start);
     properties:
       @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
@@ -402,6 +404,8 @@ node /org/freedesktop/login1 {
 
     <variablelist class="dbus-signal" generated="True" extra-ref="PrepareForShutdown"/>
 
+    <variablelist class="dbus-signal" generated="True" extra-ref="PrepareForShutdownWithMetadata"/>
+
     <variablelist class="dbus-signal" generated="True" extra-ref="PrepareForSleep"/>
 
     <variablelist class="dbus-property" generated="True" extra-ref="EnableWallMessages"/>
@@ -674,15 +678,20 @@ node /org/freedesktop/login1 {
       logs in or out, or a seat is added or removed. They each contain the ID of the object plus the object
       path.</para>
 
-      <para>The <function>PrepareForShutdown()</function> and <function>PrepareForSleep()</function> signals
-      are sent right before (with the argument <literal>true</literal>) or after (with the argument
+      <para>The <function>PrepareForShutdown</function>,
+      <function>PrepareForShutdownWithMetadata</function>, and <function>PrepareForSleep</function>
+      signals are sent right before (with the argument <literal>true</literal>) or after (with the argument
       <literal>false</literal>) the system goes down for reboot/poweroff and suspend/hibernate,
       respectively. This may be used by applications to save data on disk, release memory, or do other jobs
       that should be done shortly before shutdown/sleep, in conjunction with delay inhibitor locks. After
       completion of this work they should release their inhibition locks in order to not delay the operation
       any further. For more information see
-      <ulink url="https://www.freedesktop.org/wiki/Software/systemd/inhibit">Inhibitor Locks</ulink>.
-      </para>
+      <ulink url="https://www.freedesktop.org/wiki/Software/systemd/inhibit">Inhibitor Locks</ulink>. The
+      <function>PrepareForShutdownWithMetadata()</function> signal additionally sends a list of key/value
+      pair metadata fields. Currently it sends a <varname>type</varname> string which defines the type of
+      shutdown. The type can be one of <literal>power-off</literal>, <literal>reboot</literal>,
+      <literal>halt</literal>, <literal>kexec</literal> or <literal>soft-reboot</literal>. This signal is
+      sent first, followed by <function>PrepareForShutdown</function> (for backward compatibility).</para>
     </refsect2>
 
     <refsect2>
index 650fce6a7af6da622627e4a96fcb3954fd972d7b..4eaf6a781787de3746f3cd7ede0af69775fc3131 100644 (file)
@@ -281,6 +281,8 @@ static const char* const handle_action_verb_table[_HANDLE_ACTION_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(handle_action_verb, HandleAction);
 
+/* These strings are sent out by PrepareForShutdownWithMetadata signals as metadata, so the values cannot
+ * change as they are public APIs. */
 static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
         [HANDLE_IGNORE]                 = "ignore",
         [HANDLE_POWEROFF]               = "poweroff",
index 1e453205aaac2be371a753fbe969fac1f4db2208..e35005e2624c6eca3cdc310ddfbf75263d0f1ae7 100644 (file)
@@ -1548,18 +1548,43 @@ int manager_set_lid_switch_ignore(Manager *m, usec_t until) {
         return r;
 }
 
-static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) {
-        int active = _active;
+static int send_prepare_for(Manager *m, const HandleActionData *a, bool _active) {
+        int k = 0, r, active = _active;
 
         assert(m);
-        assert(IN_SET(w, INHIBIT_SHUTDOWN, INHIBIT_SLEEP));
-
-        return sd_bus_emit_signal(m->bus,
-                                  "/org/freedesktop/login1",
-                                  "org.freedesktop.login1.Manager",
-                                  w == INHIBIT_SHUTDOWN ? "PrepareForShutdown" : "PrepareForSleep",
-                                  "b",
-                                  active);
+        assert(a);
+        assert(IN_SET(a->inhibit_what, INHIBIT_SHUTDOWN, INHIBIT_SLEEP));
+
+        /* We need to send both old and new signal for backward compatibility. The newer one allows clients
+         * to know which type of reboot is going to happen, as they might be doing different actions (e.g.:
+         * on soft-reboot), and it is sent first, so that clients know that if they receive the old one
+         * first then they don't have to wait for the new one, as it means it's not supported. So, do not
+         * change the order here, as it is an API. */
+        if (a->inhibit_what == INHIBIT_SHUTDOWN) {
+                k = sd_bus_emit_signal(m->bus,
+                                       "/org/freedesktop/login1",
+                                       "org.freedesktop.login1.Manager",
+                                       "PrepareForShutdownWithMetadata",
+                                       "ba{sv}",
+                                       active,
+                                       1,
+                                       "type",
+                                       "s",
+                                       handle_action_to_string(a->handle));
+                if (k < 0)
+                        log_debug_errno(k, "Failed to emit PrepareForShutdownWithMetadata(): %m");
+        }
+
+        r = sd_bus_emit_signal(m->bus,
+                               "/org/freedesktop/login1",
+                               "org.freedesktop.login1.Manager",
+                               a->inhibit_what == INHIBIT_SHUTDOWN ? "PrepareForShutdown" : "PrepareForSleep",
+                               "b",
+                               active);
+        if (r < 0)
+                log_debug_errno(r, "Failed to emit PrepareForShutdown(): %m");
+
+        return RET_GATHER(k, r);
 }
 
 static int execute_shutdown_or_sleep(
@@ -1604,7 +1629,7 @@ static int execute_shutdown_or_sleep(
 
 error:
         /* Tell people that they now may take a lock again */
-        (void) send_prepare_for(m, a->inhibit_what, false);
+        (void) send_prepare_for(m, a, false);
 
         return r;
 }
@@ -1712,7 +1737,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(
                                         a->target, load_state);
 
         /* Tell everybody to prepare for shutdown/sleep */
-        (void) send_prepare_for(m, a->inhibit_what, true);
+        (void) send_prepare_for(m, a, true);
 
         delayed =
                 m->inhibit_delay_max > 0 &&
@@ -3708,6 +3733,9 @@ static const sd_bus_vtable manager_vtable[] = {
         SD_BUS_SIGNAL_WITH_ARGS("PrepareForShutdown",
                                 SD_BUS_ARGS("b", start),
                                 0),
+        SD_BUS_SIGNAL_WITH_ARGS("PrepareForShutdownWithMetadata",
+                                SD_BUS_ARGS("b", start, "a{sv}", metadata),
+                                0),
         SD_BUS_SIGNAL_WITH_ARGS("PrepareForSleep",
                                 SD_BUS_ARGS("b", start),
                                 0),
@@ -3763,7 +3791,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
                 log_info("Operation '%s' finished.", inhibit_what_to_string(m->delayed_action->inhibit_what));
 
                 /* Tell people that they now may take a lock again */
-                (void) send_prepare_for(m, m->delayed_action->inhibit_what, false);
+                (void) send_prepare_for(m, m->delayed_action, false);
 
                 m->action_job = mfree(m->action_job);
                 m->delayed_action = NULL;
index 0bbab330f4e3d203e7df6c6c414a3f5fbfcad51d..d13fe1b76f7c47862235b67ea14271708e880a1b 100755 (executable)
@@ -76,6 +76,9 @@ elif [ -f /run/testsuite82.touch ]; then
     read -r x <&3
     test "$x" = "wuffwuff"
 
+    # Check that we got a PrepareForShutdownWithMetadata signal with the right type
+    test "$(jq .payload.data[1].type.data </run/testsuite82.signal)" = "\"soft-reboot\""
+
     # Upload another entry
     T="/dev/shm/fdstore.$RANDOM"
     echo "miaumiau" >"$T"
@@ -138,9 +141,17 @@ EOF
     systemd-run -p Type=notify -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-survive.service "$T"
     systemd-run -p Type=exec -p DefaultDependencies=no -p IgnoreOnIsolate=yes --unit=testsuite-82-nosurvive.service sleep infinity
 
+    # Check that we can set up an inhibitor, and that busctl monitor sees the
+    # PrepareForShutdownWithMetadata signal and that it says 'soft-reboot'.
+    systemd-run --unit busctl.service --property StandardOutput=file:/run/testsuite82.signal \
+        busctl monitor --json=pretty --match 'sender=org.freedesktop.login1,path=/org/freedesktop/login1,interface=org.freedesktop.login1.Manager,member=PrepareForShutdownWithMetadata,type=signal'
+    systemd-run --unit inhibit.service \
+        systemd-inhibit --what=shutdown --who=test --why=test --mode=delay \
+            sleep infinity
+
     # Now issue the soft reboot. We should be right back soon.
     touch /run/testsuite82.touch
-    systemctl --no-block soft-reboot
+    systemctl --no-block --check-inhibitors=yes soft-reboot
 
     # Now block until the soft-boot killing spree kills us
     exec sleep infinity