1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "sd-messages.h"
8 #include "alloc-util.h"
10 #include "bus-error.h"
11 #include "bus-unit-util.h"
12 #include "conf-parser.h"
13 #include "extract-word.h"
14 #include "format-util.h"
17 #include "logind-action.h"
18 #include "logind-dbus.h"
19 #include "logind-seat-dbus.h"
20 #include "logind-session-dbus.h"
21 #include "process-util.h"
23 #include "string-table.h"
25 #include "user-util.h"
27 static const HandleActionData handle_action_data_table
[_HANDLE_ACTION_MAX
] = {
29 .handle
= HANDLE_POWEROFF
,
30 .target
= SPECIAL_POWEROFF_TARGET
,
31 .inhibit_what
= INHIBIT_SHUTDOWN
,
32 .polkit_action
= "org.freedesktop.login1.power-off",
33 .polkit_action_multiple_sessions
= "org.freedesktop.login1.power-off-multiple-sessions",
34 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.power-off-ignore-inhibit",
35 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
36 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
37 .message
= "System is powering down",
38 .log_verb
= "power-off",
41 .handle
= HANDLE_REBOOT
,
42 .target
= SPECIAL_REBOOT_TARGET
,
43 .inhibit_what
= INHIBIT_SHUTDOWN
,
44 .polkit_action
= "org.freedesktop.login1.reboot",
45 .polkit_action_multiple_sessions
= "org.freedesktop.login1.reboot-multiple-sessions",
46 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.reboot-ignore-inhibit",
47 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
48 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
49 .message
= "System is rebooting",
53 .handle
= HANDLE_HALT
,
54 .target
= SPECIAL_HALT_TARGET
,
55 .inhibit_what
= INHIBIT_SHUTDOWN
,
56 .polkit_action
= "org.freedesktop.login1.halt",
57 .polkit_action_multiple_sessions
= "org.freedesktop.login1.halt-multiple-sessions",
58 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.halt-ignore-inhibit",
59 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
60 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
61 .message
= "System is halting",
65 .handle
= HANDLE_KEXEC
,
66 .target
= SPECIAL_KEXEC_TARGET
,
67 .inhibit_what
= INHIBIT_SHUTDOWN
,
68 .polkit_action
= "org.freedesktop.login1.reboot",
69 .polkit_action_multiple_sessions
= "org.freedesktop.login1.reboot-multiple-sessions",
70 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.reboot-ignore-inhibit",
71 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
72 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
73 .message
= "System is rebooting with kexec",
76 [HANDLE_SOFT_REBOOT
] = {
77 .handle
= HANDLE_SOFT_REBOOT
,
78 .target
= SPECIAL_SOFT_REBOOT_TARGET
,
79 .inhibit_what
= INHIBIT_SHUTDOWN
,
80 .polkit_action
= "org.freedesktop.login1.reboot",
81 .polkit_action_multiple_sessions
= "org.freedesktop.login1.reboot-multiple-sessions",
82 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.reboot-ignore-inhibit",
83 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
84 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
85 .message
= "System userspace is rebooting",
86 .log_verb
= "soft-reboot",
89 .handle
= HANDLE_SUSPEND
,
90 .target
= SPECIAL_SUSPEND_TARGET
,
91 .inhibit_what
= INHIBIT_SLEEP
,
92 .polkit_action
= "org.freedesktop.login1.suspend",
93 .polkit_action_multiple_sessions
= "org.freedesktop.login1.suspend-multiple-sessions",
94 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.suspend-ignore-inhibit",
95 .sleep_operation
= SLEEP_SUSPEND
,
97 [HANDLE_HIBERNATE
] = {
98 .handle
= HANDLE_HIBERNATE
,
99 .target
= SPECIAL_HIBERNATE_TARGET
,
100 .inhibit_what
= INHIBIT_SLEEP
,
101 .polkit_action
= "org.freedesktop.login1.hibernate",
102 .polkit_action_multiple_sessions
= "org.freedesktop.login1.hibernate-multiple-sessions",
103 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.hibernate-ignore-inhibit",
104 .sleep_operation
= SLEEP_HIBERNATE
,
106 [HANDLE_HYBRID_SLEEP
] = {
107 .handle
= HANDLE_HYBRID_SLEEP
,
108 .target
= SPECIAL_HYBRID_SLEEP_TARGET
,
109 .inhibit_what
= INHIBIT_SLEEP
,
110 .polkit_action
= "org.freedesktop.login1.hibernate",
111 .polkit_action_multiple_sessions
= "org.freedesktop.login1.hibernate-multiple-sessions",
112 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.hibernate-ignore-inhibit",
113 .sleep_operation
= SLEEP_HYBRID_SLEEP
,
115 [HANDLE_SUSPEND_THEN_HIBERNATE
] = {
116 .handle
= HANDLE_SUSPEND_THEN_HIBERNATE
,
117 .target
= SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET
,
118 .inhibit_what
= INHIBIT_SLEEP
,
119 .polkit_action
= "org.freedesktop.login1.hibernate",
120 .polkit_action_multiple_sessions
= "org.freedesktop.login1.hibernate-multiple-sessions",
121 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.hibernate-ignore-inhibit",
122 .sleep_operation
= SLEEP_SUSPEND_THEN_HIBERNATE
,
124 [HANDLE_FACTORY_RESET
] = {
125 .handle
= HANDLE_FACTORY_RESET
,
126 .target
= SPECIAL_FACTORY_RESET_TARGET
,
127 .inhibit_what
= _INHIBIT_WHAT_INVALID
,
128 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
129 .message_id
= SD_MESSAGE_FACTORY_RESET_STR
,
130 .message
= "System is performing factory reset",
134 const HandleActionData
* handle_action_lookup(HandleAction action
) {
136 if (action
< 0 || (size_t) action
>= ELEMENTSOF(handle_action_data_table
))
139 return &handle_action_data_table
[action
];
142 static bool handle_action_sleep_supported(HandleAction action
) {
143 assert(HANDLE_ACTION_IS_SLEEP(action
) && action
!= HANDLE_SLEEP
);
144 return sleep_supported(ASSERT_PTR(handle_action_lookup(action
))->sleep_operation
) > 0;
147 /* The order in which we try each sleep operation. We should typically prefer operations without a delay,
148 * i.e. s2h and suspend, and use hibernation at last since it requires minimum hardware support.
149 * hybrid-sleep is disabled by default, and thus should be ordered before suspend if manually chosen by user,
150 * since it implies suspend and will probably never be selected by us otherwise. */
151 static const HandleAction sleep_actions
[] = {
152 HANDLE_SUSPEND_THEN_HIBERNATE
,
158 int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask
, char ***ret
) {
159 _cleanup_strv_free_
char **actions
= NULL
;
164 FOREACH_ELEMENT(i
, sleep_actions
)
165 if (BIT_SET(mask
, *i
)) {
166 r
= strv_extend(&actions
, handle_action_to_string(*i
));
171 *ret
= TAKE_PTR(actions
);
175 HandleAction
handle_action_sleep_select(Manager
*m
) {
178 FOREACH_ELEMENT(i
, sleep_actions
) {
179 const HandleActionData
*a
;
180 _cleanup_free_
char *load_state
= NULL
;
182 if (!BIT_SET(m
->handle_action_sleep_mask
, *i
))
185 a
= ASSERT_PTR(handle_action_lookup(*i
));
187 if (sleep_supported(a
->sleep_operation
) <= 0)
190 (void) unit_load_state(m
->bus
, a
->target
, &load_state
);
191 if (streq_ptr(load_state
, "loaded"))
195 return _HANDLE_ACTION_INVALID
;
198 static int handle_action_execute(
201 bool ignore_inhibited
,
204 static const char * const message_table
[_HANDLE_ACTION_MAX
] = {
205 [HANDLE_POWEROFF
] = "Powering off...",
206 [HANDLE_REBOOT
] = "Rebooting...",
207 [HANDLE_HALT
] = "Halting...",
208 [HANDLE_KEXEC
] = "Rebooting via kexec...",
209 [HANDLE_SOFT_REBOOT
] = "Rebooting userspace...",
210 [HANDLE_SUSPEND
] = "Suspending...",
211 [HANDLE_HIBERNATE
] = "Hibernating...",
212 [HANDLE_HYBRID_SLEEP
] = "Hibernating and suspending...",
213 [HANDLE_SUSPEND_THEN_HIBERNATE
] = "Suspending, then hibernating...",
214 [HANDLE_FACTORY_RESET
] = "Performing factory reset...",
217 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
218 InhibitWhat inhibit_operation
;
219 Inhibitor
*offending
= NULL
;
223 assert(!IN_SET(handle
, HANDLE_IGNORE
, HANDLE_LOCK
, HANDLE_SLEEP
));
225 if (handle
== HANDLE_KEXEC
&& access(KEXEC
, X_OK
) < 0)
226 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
227 "Requested %s operation not supported, ignoring.", handle_action_to_string(handle
));
229 if (m
->delayed_action
)
230 return log_debug_errno(SYNTHETIC_ERRNO(EALREADY
),
231 "Action %s already in progress, ignoring requested %s operation.",
232 handle_action_to_string(m
->delayed_action
->handle
),
233 handle_action_to_string(handle
));
235 inhibit_operation
= ASSERT_PTR(handle_action_lookup(handle
))->inhibit_what
;
237 /* If the actual operation is inhibited, warn and fail */
238 if (inhibit_what_is_valid(inhibit_operation
) &&
240 manager_is_inhibited(m
, inhibit_operation
, NULL
, /* flags= */ 0, UID_INVALID
, &offending
)) {
241 _cleanup_free_
char *comm
= NULL
, *u
= NULL
;
243 (void) pidref_get_comm(&offending
->pid
, &comm
);
244 u
= uid_to_name(offending
->uid
);
246 /* If this is just a recheck of the lid switch then don't warn about anything */
247 log_full(is_edge
? LOG_ERR
: LOG_DEBUG
,
248 "Refusing %s operation, %s is inhibited by UID "UID_FMT
"/%s, PID "PID_FMT
"/%s.",
249 handle_action_to_string(handle
),
250 inhibit_what_to_string(inhibit_operation
),
251 offending
->uid
, strna(u
),
252 offending
->pid
.pid
, strna(comm
));
254 return is_edge
? -EPERM
: 0;
257 log_info("%s", message_table
[handle
]);
259 r
= bus_manager_shutdown_or_sleep_now_or_later(m
, handle_action_lookup(handle
), &error
);
261 return log_error_errno(r
, "Failed to execute %s operation: %s",
262 handle_action_to_string(handle
),
263 bus_error_message(&error
, r
));
268 static int handle_action_sleep_execute(
271 bool ignore_inhibited
,
275 assert(HANDLE_ACTION_IS_SLEEP(handle
));
277 if (handle
== HANDLE_SLEEP
) {
280 a
= handle_action_sleep_select(m
);
282 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
283 "None of the configured sleep operations are supported, ignoring.");
285 return handle_action_sleep_execute(m
, a
, ignore_inhibited
, is_edge
);
288 bool supported
= handle_action_sleep_supported(handle
);
290 if (!supported
&& handle
!= HANDLE_SUSPEND
) {
291 supported
= sleep_supported(SLEEP_SUSPEND
) > 0;
293 log_notice("Requested %s operation is not supported, using regular suspend instead.",
294 handle_action_to_string(handle
));
295 handle
= HANDLE_SUSPEND
;
300 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
301 "Requested %s operation not supported, ignoring.", handle_action_to_string(handle
));
303 return handle_action_execute(m
, handle
, ignore_inhibited
, is_edge
);
306 static int manager_handle_action_secure_attention_key(
313 _cleanup_free_
char *p
= NULL
;
323 o
= hashmap_get(m
->seats
, seat
);
327 p
= seat_bus_path(o
);
332 LOG_MESSAGE("Secure Attention Key sequence pressed on seat %s", seat
),
333 LOG_MESSAGE_ID(SD_MESSAGE_SECURE_ATTENTION_KEY_PRESS_STR
),
334 LOG_ITEM("SEAT_ID=%s", seat
));
336 r
= sd_bus_emit_signal(
338 "/org/freedesktop/login1",
339 "org.freedesktop.login1.Manager",
340 "SecureAttentionKey",
343 log_warning_errno(r
, "Failed to emit SecureAttentionKey signal, ignoring: %m");
348 int manager_handle_action(
350 InhibitWhat inhibit_key
,
352 bool ignore_inhibited
,
354 const char *action_seat
) {
357 assert(handle_action_valid(handle
));
359 /* If the key handling is turned off, don't do anything */
360 if (handle
== HANDLE_IGNORE
) {
361 log_debug("Handling of %s (%s) is disabled, taking no action.",
362 inhibit_key
== 0 ? "idle timeout" : inhibit_what_to_string(inhibit_key
),
363 is_edge
? "edge" : "level");
367 if (inhibit_key
== INHIBIT_HANDLE_LID_SWITCH
) {
368 /* If the last system suspend or startup is too close, let's not suspend for now, to give
369 * USB docking stations some time to settle so that we can properly watch its displays. */
370 if (m
->lid_switch_ignore_event_source
) {
371 log_debug("Ignoring lid switch request, system startup or resume too close.");
376 /* If the key handling is inhibited, don't do anything */
377 if (inhibit_key
> 0) {
378 if (manager_is_inhibited(m
, inhibit_key
, NULL
, MANAGER_IS_INHIBITED_IGNORE_INACTIVE
, UID_INVALID
, NULL
)) {
379 log_debug("Refusing %s operation, %s is inhibited.",
380 handle_action_to_string(handle
),
381 inhibit_what_to_string(inhibit_key
));
386 /* Locking and greeter activation is handled differently from the rest. */
387 if (handle
== HANDLE_LOCK
) {
391 log_info("Locking sessions...");
392 session_send_lock_all(m
, true);
396 if (handle
== HANDLE_SECURE_ATTENTION_KEY
)
397 return manager_handle_action_secure_attention_key(m
, is_edge
, action_seat
);
399 if (HANDLE_ACTION_IS_SLEEP(handle
))
400 return handle_action_sleep_execute(m
, handle
, ignore_inhibited
, is_edge
);
402 return handle_action_execute(m
, handle
, ignore_inhibited
, is_edge
);
405 static const char* const handle_action_verb_table
[_HANDLE_ACTION_MAX
] = {
406 [HANDLE_IGNORE
] = "do nothing",
407 [HANDLE_POWEROFF
] = "power off",
408 [HANDLE_REBOOT
] = "reboot",
409 [HANDLE_HALT
] = "halt",
410 [HANDLE_KEXEC
] = "kexec",
411 [HANDLE_SOFT_REBOOT
] = "soft-reboot",
412 [HANDLE_SUSPEND
] = "suspend",
413 [HANDLE_HIBERNATE
] = "hibernate",
414 [HANDLE_HYBRID_SLEEP
] = "hybrid sleep",
415 [HANDLE_SUSPEND_THEN_HIBERNATE
] = "suspend and later hibernate",
416 [HANDLE_SLEEP
] = "sleep",
417 [HANDLE_FACTORY_RESET
] = "perform a factory reset",
418 [HANDLE_LOCK
] = "be locked",
419 [HANDLE_SECURE_ATTENTION_KEY
] = "handle the secure attention key",
422 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(handle_action_verb
, HandleAction
);
424 /* These strings are sent out by PrepareForShutdownWithMetadata signals as metadata, so the values cannot
425 * change as they are public APIs. */
426 static const char* const handle_action_table
[_HANDLE_ACTION_MAX
] = {
427 [HANDLE_IGNORE
] = "ignore",
428 [HANDLE_POWEROFF
] = "poweroff",
429 [HANDLE_REBOOT
] = "reboot",
430 [HANDLE_HALT
] = "halt",
431 [HANDLE_KEXEC
] = "kexec",
432 [HANDLE_SOFT_REBOOT
] = "soft-reboot",
433 [HANDLE_SUSPEND
] = "suspend",
434 [HANDLE_HIBERNATE
] = "hibernate",
435 [HANDLE_HYBRID_SLEEP
] = "hybrid-sleep",
436 [HANDLE_SUSPEND_THEN_HIBERNATE
] = "suspend-then-hibernate",
437 [HANDLE_SLEEP
] = "sleep",
438 [HANDLE_FACTORY_RESET
] = "factory-reset",
439 [HANDLE_LOCK
] = "lock",
440 [HANDLE_SECURE_ATTENTION_KEY
] = "secure-attention-key",
443 DEFINE_STRING_TABLE_LOOKUP(handle_action
, HandleAction
);
444 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action
, handle_action
, HandleAction
);
446 int config_parse_handle_action_sleep(
448 const char *filename
,
451 unsigned section_line
,
458 HandleActionSleepMask
*mask
= ASSERT_PTR(data
);
459 _cleanup_strv_free_
char **actions
= NULL
;
468 if (strv_split_full(&actions
, rvalue
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RETAIN_ESCAPE
) < 0)
473 STRV_FOREACH(action
, actions
) {
476 a
= handle_action_from_string(*action
);
478 log_syntax(unit
, LOG_WARNING
, filename
, line
, a
,
479 "Failed to parse SleepOperation '%s', ignoring: %m", *action
);
483 if (!HANDLE_ACTION_IS_SLEEP(a
) || a
== HANDLE_SLEEP
) {
484 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
485 "HandleAction '%s' is not a sleep operation, ignoring: %m", *action
);
498 *mask
= HANDLE_ACTION_SLEEP_MASK_DEFAULT
;