1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "sd-messages.h"
7 #include "alloc-util.h"
9 #include "bus-unit-util.h"
11 #include "conf-parser.h"
12 #include "format-util.h"
13 #include "logind-action.h"
14 #include "logind-dbus.h"
15 #include "logind-session-dbus.h"
16 #include "process-util.h"
18 #include "string-table.h"
19 #include "terminal-util.h"
20 #include "user-util.h"
22 static const HandleActionData handle_action_data_table
[_HANDLE_ACTION_MAX
] = {
24 .handle
= HANDLE_POWEROFF
,
25 .target
= SPECIAL_POWEROFF_TARGET
,
26 .inhibit_what
= INHIBIT_SHUTDOWN
,
27 .polkit_action
= "org.freedesktop.login1.power-off",
28 .polkit_action_multiple_sessions
= "org.freedesktop.login1.power-off-multiple-sessions",
29 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.power-off-ignore-inhibit",
30 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
31 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
32 .message
= "System is powering down",
33 .log_verb
= "power-off",
36 .handle
= HANDLE_REBOOT
,
37 .target
= SPECIAL_REBOOT_TARGET
,
38 .inhibit_what
= INHIBIT_SHUTDOWN
,
39 .polkit_action
= "org.freedesktop.login1.reboot",
40 .polkit_action_multiple_sessions
= "org.freedesktop.login1.reboot-multiple-sessions",
41 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.reboot-ignore-inhibit",
42 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
43 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
44 .message
= "System is rebooting",
48 .handle
= HANDLE_HALT
,
49 .target
= SPECIAL_HALT_TARGET
,
50 .inhibit_what
= INHIBIT_SHUTDOWN
,
51 .polkit_action
= "org.freedesktop.login1.halt",
52 .polkit_action_multiple_sessions
= "org.freedesktop.login1.halt-multiple-sessions",
53 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.halt-ignore-inhibit",
54 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
55 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
56 .message
= "System is halting",
60 .handle
= HANDLE_KEXEC
,
61 .target
= SPECIAL_KEXEC_TARGET
,
62 .inhibit_what
= INHIBIT_SHUTDOWN
,
63 .polkit_action
= "org.freedesktop.login1.reboot",
64 .polkit_action_multiple_sessions
= "org.freedesktop.login1.reboot-multiple-sessions",
65 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.reboot-ignore-inhibit",
66 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
67 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
68 .message
= "System is rebooting with kexec",
71 [HANDLE_SOFT_REBOOT
] = {
72 .handle
= HANDLE_SOFT_REBOOT
,
73 .target
= SPECIAL_SOFT_REBOOT_TARGET
,
74 .inhibit_what
= INHIBIT_SHUTDOWN
,
75 .polkit_action
= "org.freedesktop.login1.reboot",
76 .polkit_action_multiple_sessions
= "org.freedesktop.login1.reboot-multiple-sessions",
77 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.reboot-ignore-inhibit",
78 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
79 .message_id
= SD_MESSAGE_SHUTDOWN_STR
,
80 .message
= "System userspace is rebooting",
81 .log_verb
= "soft-reboot",
84 .handle
= HANDLE_SUSPEND
,
85 .target
= SPECIAL_SUSPEND_TARGET
,
86 .inhibit_what
= INHIBIT_SLEEP
,
87 .polkit_action
= "org.freedesktop.login1.suspend",
88 .polkit_action_multiple_sessions
= "org.freedesktop.login1.suspend-multiple-sessions",
89 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.suspend-ignore-inhibit",
90 .sleep_operation
= SLEEP_SUSPEND
,
92 [HANDLE_HIBERNATE
] = {
93 .handle
= HANDLE_HIBERNATE
,
94 .target
= SPECIAL_HIBERNATE_TARGET
,
95 .inhibit_what
= INHIBIT_SLEEP
,
96 .polkit_action
= "org.freedesktop.login1.hibernate",
97 .polkit_action_multiple_sessions
= "org.freedesktop.login1.hibernate-multiple-sessions",
98 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.hibernate-ignore-inhibit",
99 .sleep_operation
= SLEEP_HIBERNATE
,
101 [HANDLE_HYBRID_SLEEP
] = {
102 .handle
= HANDLE_HYBRID_SLEEP
,
103 .target
= SPECIAL_HYBRID_SLEEP_TARGET
,
104 .inhibit_what
= INHIBIT_SLEEP
,
105 .polkit_action
= "org.freedesktop.login1.hibernate",
106 .polkit_action_multiple_sessions
= "org.freedesktop.login1.hibernate-multiple-sessions",
107 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.hibernate-ignore-inhibit",
108 .sleep_operation
= SLEEP_HYBRID_SLEEP
,
110 [HANDLE_SUSPEND_THEN_HIBERNATE
] = {
111 .handle
= HANDLE_SUSPEND_THEN_HIBERNATE
,
112 .target
= SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET
,
113 .inhibit_what
= INHIBIT_SLEEP
,
114 .polkit_action
= "org.freedesktop.login1.hibernate",
115 .polkit_action_multiple_sessions
= "org.freedesktop.login1.hibernate-multiple-sessions",
116 .polkit_action_ignore_inhibit
= "org.freedesktop.login1.hibernate-ignore-inhibit",
117 .sleep_operation
= SLEEP_SUSPEND_THEN_HIBERNATE
,
119 [HANDLE_FACTORY_RESET
] = {
120 .handle
= HANDLE_FACTORY_RESET
,
121 .target
= SPECIAL_FACTORY_RESET_TARGET
,
122 .inhibit_what
= _INHIBIT_WHAT_INVALID
,
123 .sleep_operation
= _SLEEP_OPERATION_INVALID
,
124 .message_id
= SD_MESSAGE_FACTORY_RESET_STR
,
125 .message
= "System is performing factory reset",
129 const HandleActionData
* handle_action_lookup(HandleAction action
) {
131 if (action
< 0 || (size_t) action
>= ELEMENTSOF(handle_action_data_table
))
134 return &handle_action_data_table
[action
];
137 static bool handle_action_sleep_supported(HandleAction action
) {
138 assert(HANDLE_ACTION_IS_SLEEP(action
) && action
!= HANDLE_SLEEP
);
139 return sleep_supported(ASSERT_PTR(handle_action_lookup(action
))->sleep_operation
) > 0;
142 /* The order in which we try each sleep operation. We should typically prefer operations without a delay,
143 * i.e. s2h and suspend, and use hibernation at last since it requires minimum hardware support.
144 * hybrid-sleep is disabled by default, and thus should be ordered before suspend if manually chosen by user,
145 * since it implies suspend and will probably never be selected by us otherwise. */
146 static const HandleAction sleep_actions
[] = {
147 HANDLE_SUSPEND_THEN_HIBERNATE
,
153 int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask
, char ***ret
) {
154 _cleanup_strv_free_
char **actions
= NULL
;
159 FOREACH_ARRAY(i
, sleep_actions
, ELEMENTSOF(sleep_actions
))
160 if (FLAGS_SET(mask
, 1U << *i
)) {
161 r
= strv_extend(&actions
, handle_action_to_string(*i
));
166 *ret
= TAKE_PTR(actions
);
170 HandleAction
handle_action_sleep_select(Manager
*m
) {
173 FOREACH_ARRAY(i
, sleep_actions
, ELEMENTSOF(sleep_actions
)) {
174 HandleActionSleepMask action_mask
= 1U << *i
;
175 const HandleActionData
*a
;
176 _cleanup_free_
char *load_state
= NULL
;
178 if (!FLAGS_SET(m
->handle_action_sleep_mask
, action_mask
))
181 a
= ASSERT_PTR(handle_action_lookup(*i
));
183 if (sleep_supported(a
->sleep_operation
) <= 0)
186 (void) unit_load_state(m
->bus
, a
->target
, &load_state
);
187 if (streq_ptr(load_state
, "loaded"))
191 return _HANDLE_ACTION_INVALID
;
194 static int handle_action_execute(
197 bool ignore_inhibited
,
200 static const char * const message_table
[_HANDLE_ACTION_MAX
] = {
201 [HANDLE_POWEROFF
] = "Powering off...",
202 [HANDLE_REBOOT
] = "Rebooting...",
203 [HANDLE_HALT
] = "Halting...",
204 [HANDLE_KEXEC
] = "Rebooting via kexec...",
205 [HANDLE_SOFT_REBOOT
] = "Rebooting userspace...",
206 [HANDLE_SUSPEND
] = "Suspending...",
207 [HANDLE_HIBERNATE
] = "Hibernating...",
208 [HANDLE_HYBRID_SLEEP
] = "Hibernating and suspending...",
209 [HANDLE_SUSPEND_THEN_HIBERNATE
] = "Suspending, then hibernating...",
210 [HANDLE_FACTORY_RESET
] = "Performing factory reset...",
213 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
214 InhibitWhat inhibit_operation
;
215 Inhibitor
*offending
= NULL
;
219 assert(!IN_SET(handle
, HANDLE_IGNORE
, HANDLE_LOCK
, HANDLE_SLEEP
));
221 if (handle
== HANDLE_KEXEC
&& access(KEXEC
, X_OK
) < 0)
222 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
223 "Requested %s operation not supported, ignoring.", handle_action_to_string(handle
));
225 if (m
->delayed_action
)
226 return log_debug_errno(SYNTHETIC_ERRNO(EALREADY
),
227 "Action %s already in progress, ignoring requested %s operation.",
228 handle_action_to_string(m
->delayed_action
->handle
),
229 handle_action_to_string(handle
));
231 inhibit_operation
= ASSERT_PTR(handle_action_lookup(handle
))->inhibit_what
;
233 /* If the actual operation is inhibited, warn and fail */
234 if (inhibit_what_is_valid(inhibit_operation
) &&
236 manager_is_inhibited(m
, inhibit_operation
, INHIBIT_BLOCK
, NULL
, false, false, 0, &offending
)) {
237 _cleanup_free_
char *comm
= NULL
, *u
= NULL
;
239 (void) pidref_get_comm(&offending
->pid
, &comm
);
240 u
= uid_to_name(offending
->uid
);
242 /* If this is just a recheck of the lid switch then don't warn about anything */
243 log_full(is_edge
? LOG_ERR
: LOG_DEBUG
,
244 "Refusing %s operation, %s is inhibited by UID "UID_FMT
"/%s, PID "PID_FMT
"/%s.",
245 handle_action_to_string(handle
),
246 inhibit_what_to_string(inhibit_operation
),
247 offending
->uid
, strna(u
),
248 offending
->pid
.pid
, strna(comm
));
250 return is_edge
? -EPERM
: 0;
253 log_info("%s", message_table
[handle
]);
255 r
= bus_manager_shutdown_or_sleep_now_or_later(m
, handle_action_lookup(handle
), &error
);
257 return log_error_errno(r
, "Failed to execute %s operation: %s",
258 handle_action_to_string(handle
),
259 bus_error_message(&error
, r
));
264 static int handle_action_sleep_execute(
267 bool ignore_inhibited
,
271 assert(HANDLE_ACTION_IS_SLEEP(handle
));
273 if (handle
== HANDLE_SLEEP
) {
276 a
= handle_action_sleep_select(m
);
278 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
279 "None of the configured sleep operations are supported, ignoring.");
281 return handle_action_sleep_execute(m
, a
, ignore_inhibited
, is_edge
);
284 bool supported
= handle_action_sleep_supported(handle
);
286 if (!supported
&& handle
!= HANDLE_SUSPEND
) {
287 supported
= sleep_supported(SLEEP_SUSPEND
) > 0;
289 log_notice("Requested %s operation is not supported, using regular suspend instead.",
290 handle_action_to_string(handle
));
291 handle
= HANDLE_SUSPEND
;
296 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
297 "Requested %s operation not supported, ignoring.", handle_action_to_string(handle
));
299 return handle_action_execute(m
, handle
, ignore_inhibited
, is_edge
);
302 int manager_handle_action(
304 InhibitWhat inhibit_key
,
306 bool ignore_inhibited
,
310 assert(handle_action_valid(handle
));
312 /* If the key handling is turned off, don't do anything */
313 if (handle
== HANDLE_IGNORE
) {
314 log_debug("Handling of %s (%s) is disabled, taking no action.",
315 inhibit_key
== 0 ? "idle timeout" : inhibit_what_to_string(inhibit_key
),
316 is_edge
? "edge" : "level");
320 if (inhibit_key
== INHIBIT_HANDLE_LID_SWITCH
) {
321 /* If the last system suspend or startup is too close, let's not suspend for now, to give
322 * USB docking stations some time to settle so that we can properly watch its displays. */
323 if (m
->lid_switch_ignore_event_source
) {
324 log_debug("Ignoring lid switch request, system startup or resume too close.");
329 /* If the key handling is inhibited, don't do anything */
330 if (inhibit_key
> 0) {
331 if (manager_is_inhibited(m
, inhibit_key
, INHIBIT_BLOCK
, NULL
, true, false, 0, NULL
)) {
332 log_debug("Refusing %s operation, %s is inhibited.",
333 handle_action_to_string(handle
),
334 inhibit_what_to_string(inhibit_key
));
339 /* Locking is handled differently from the rest. */
340 if (handle
== HANDLE_LOCK
) {
344 log_info("Locking sessions...");
345 session_send_lock_all(m
, true);
349 if (HANDLE_ACTION_IS_SLEEP(handle
))
350 return handle_action_sleep_execute(m
, handle
, ignore_inhibited
, is_edge
);
352 return handle_action_execute(m
, handle
, ignore_inhibited
, is_edge
);
355 static const char* const handle_action_verb_table
[_HANDLE_ACTION_MAX
] = {
356 [HANDLE_IGNORE
] = "do nothing",
357 [HANDLE_POWEROFF
] = "power off",
358 [HANDLE_REBOOT
] = "reboot",
359 [HANDLE_HALT
] = "halt",
360 [HANDLE_KEXEC
] = "kexec",
361 [HANDLE_SOFT_REBOOT
] = "soft-reboot",
362 [HANDLE_SUSPEND
] = "suspend",
363 [HANDLE_HIBERNATE
] = "hibernate",
364 [HANDLE_HYBRID_SLEEP
] = "hybrid sleep",
365 [HANDLE_SUSPEND_THEN_HIBERNATE
] = "suspend and later hibernate",
366 [HANDLE_SLEEP
] = "sleep",
367 [HANDLE_FACTORY_RESET
] = "perform a factory reset",
368 [HANDLE_LOCK
] = "be locked",
371 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(handle_action_verb
, HandleAction
);
373 /* These strings are sent out by PrepareForShutdownWithMetadata signals as metadata, so the values cannot
374 * change as they are public APIs. */
375 static const char* const handle_action_table
[_HANDLE_ACTION_MAX
] = {
376 [HANDLE_IGNORE
] = "ignore",
377 [HANDLE_POWEROFF
] = "poweroff",
378 [HANDLE_REBOOT
] = "reboot",
379 [HANDLE_HALT
] = "halt",
380 [HANDLE_KEXEC
] = "kexec",
381 [HANDLE_SOFT_REBOOT
] = "soft-reboot",
382 [HANDLE_SUSPEND
] = "suspend",
383 [HANDLE_HIBERNATE
] = "hibernate",
384 [HANDLE_HYBRID_SLEEP
] = "hybrid-sleep",
385 [HANDLE_SUSPEND_THEN_HIBERNATE
] = "suspend-then-hibernate",
386 [HANDLE_SLEEP
] = "sleep",
387 [HANDLE_FACTORY_RESET
] = "factory-reset",
388 [HANDLE_LOCK
] = "lock",
391 DEFINE_STRING_TABLE_LOOKUP(handle_action
, HandleAction
);
392 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action
, handle_action
, HandleAction
, "Failed to parse handle action setting");
394 int config_parse_handle_action_sleep(
396 const char *filename
,
399 unsigned section_line
,
406 HandleActionSleepMask
*mask
= ASSERT_PTR(data
);
407 _cleanup_strv_free_
char **actions
= NULL
;
416 if (strv_split_full(&actions
, rvalue
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RETAIN_ESCAPE
) < 0)
421 STRV_FOREACH(action
, actions
) {
424 a
= handle_action_from_string(*action
);
426 log_syntax(unit
, LOG_WARNING
, filename
, line
, a
,
427 "Failed to parse SleepOperation '%s', ignoring: %m", *action
);
431 if (!HANDLE_ACTION_IS_SLEEP(a
) || a
== HANDLE_SLEEP
) {
432 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
433 "HandleAction '%s' is not a sleep operation, ignoring: %m", *action
);
446 *mask
= HANDLE_ACTION_SLEEP_MASK_DEFAULT
;