diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml index 867662e057..5f7b711aaa 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 650fce6a7a..4eaf6a7817 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 1e453205aa..e35005e262 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)); + assert(a); + assert(IN_SET(a->inhibit_what, 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); + /* 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 0bbab330f4..d13fe1b76f 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