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