From: Luca Boccassi Date: Wed, 30 Aug 2023 18:51:13 +0000 (+0100) Subject: logind: add PrepareForShutdownWithMetadata signal X-Git-Tag: v255-rc1~533 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e4aab5cf1a00bbb73f325f4f785dd4171ccdae77;p=thirdparty%2Fsystemd.git logind: add PrepareForShutdownWithMetadata signal 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. --- diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml index 867662e057a..5f7b711aaab 100644 --- a/man/org.freedesktop.login1.xml +++ b/man/org.freedesktop.login1.xml @@ -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 { + + @@ -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. - The PrepareForShutdown() and PrepareForSleep() signals - are sent right before (with the argument true) or after (with the argument + The PrepareForShutdown, + PrepareForShutdownWithMetadata, and PrepareForSleep + signals are sent right before (with the argument true) or after (with the argument false) 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 - Inhibitor Locks. - + Inhibitor Locks. The + PrepareForShutdownWithMetadata() signal additionally sends a list of key/value + pair metadata fields. Currently it sends a type string which defines the type of + shutdown. The type can be one of power-off, reboot, + halt, kexec or soft-reboot. This signal is + sent first, followed by PrepareForShutdown (for backward compatibility). diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 650fce6a7af..4eaf6a78178 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -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", diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 1e453205aaa..e35005e2624 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -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; diff --git a/test/units/testsuite-82.sh b/test/units/testsuite-82.sh index 0bbab330f4e..d13fe1b76f7 100755 --- a/test/units/testsuite-82.sh +++ b/test/units/testsuite-82.sh @@ -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 "$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