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.
This commit is contained in:
Luca Boccassi 2023-08-30 19:51:13 +01:00 committed by Luca Boccassi
parent fd0a804271
commit e4aab5cf1a
4 changed files with 67 additions and 17 deletions

View 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>

View 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",

View 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));
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;

View File

@ -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