]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-action.c
ci: re-enable uefi secure boot
[thirdparty/systemd.git] / src / login / logind-action.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "sd-bus.h"
6 #include "sd-messages.h"
7
8 #include "alloc-util.h"
9 #include "bitfield.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"
15 #include "hashmap.h"
16 #include "logind.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"
22 #include "special.h"
23 #include "string-table.h"
24 #include "strv.h"
25 #include "user-util.h"
26
27 static const HandleActionData handle_action_data_table[_HANDLE_ACTION_MAX] = {
28 [HANDLE_POWEROFF] = {
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",
39 },
40 [HANDLE_REBOOT] = {
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",
50 .log_verb = "reboot",
51 },
52 [HANDLE_HALT] = {
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",
62 .log_verb = "halt",
63 },
64 [HANDLE_KEXEC] = {
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",
74 .log_verb = "kexec",
75 },
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",
87 },
88 [HANDLE_SUSPEND] = {
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,
96 },
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,
105 },
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,
114 },
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,
123 },
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",
131 },
132 };
133
134 const HandleActionData* handle_action_lookup(HandleAction action) {
135
136 if (action < 0 || (size_t) action >= ELEMENTSOF(handle_action_data_table))
137 return NULL;
138
139 return &handle_action_data_table[action];
140 }
141
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;
145 }
146
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,
153 HANDLE_HYBRID_SLEEP,
154 HANDLE_SUSPEND,
155 HANDLE_HIBERNATE,
156 };
157
158 int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***ret) {
159 _cleanup_strv_free_ char **actions = NULL;
160 int r;
161
162 assert(ret);
163
164 FOREACH_ELEMENT(i, sleep_actions)
165 if (BIT_SET(mask, *i)) {
166 r = strv_extend(&actions, handle_action_to_string(*i));
167 if (r < 0)
168 return r;
169 }
170
171 *ret = TAKE_PTR(actions);
172 return 0;
173 }
174
175 HandleAction handle_action_sleep_select(Manager *m) {
176 assert(m);
177
178 FOREACH_ELEMENT(i, sleep_actions) {
179 const HandleActionData *a;
180 _cleanup_free_ char *load_state = NULL;
181
182 if (!BIT_SET(m->handle_action_sleep_mask, *i))
183 continue;
184
185 a = ASSERT_PTR(handle_action_lookup(*i));
186
187 if (sleep_supported(a->sleep_operation) <= 0)
188 continue;
189
190 (void) unit_load_state(m->bus, a->target, &load_state);
191 if (streq_ptr(load_state, "loaded"))
192 return *i;
193 }
194
195 return _HANDLE_ACTION_INVALID;
196 }
197
198 static int handle_action_execute(
199 Manager *m,
200 HandleAction handle,
201 bool ignore_inhibited,
202 bool is_edge) {
203
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...",
215 };
216
217 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
218 InhibitWhat inhibit_operation;
219 Inhibitor *offending = NULL;
220 int r;
221
222 assert(m);
223 assert(!IN_SET(handle, HANDLE_IGNORE, HANDLE_LOCK, HANDLE_SLEEP));
224
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));
228
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));
234
235 inhibit_operation = ASSERT_PTR(handle_action_lookup(handle))->inhibit_what;
236
237 /* If the actual operation is inhibited, warn and fail */
238 if (inhibit_what_is_valid(inhibit_operation) &&
239 !ignore_inhibited &&
240 manager_is_inhibited(m, inhibit_operation, NULL, /* flags= */ 0, UID_INVALID, &offending)) {
241 _cleanup_free_ char *comm = NULL, *u = NULL;
242
243 (void) pidref_get_comm(&offending->pid, &comm);
244 u = uid_to_name(offending->uid);
245
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));
253
254 return is_edge ? -EPERM : 0;
255 }
256
257 log_info("%s", message_table[handle]);
258
259 r = bus_manager_shutdown_or_sleep_now_or_later(m, handle_action_lookup(handle), &error);
260 if (r < 0)
261 return log_error_errno(r, "Failed to execute %s operation: %s",
262 handle_action_to_string(handle),
263 bus_error_message(&error, r));
264
265 return 1;
266 }
267
268 static int handle_action_sleep_execute(
269 Manager *m,
270 HandleAction handle,
271 bool ignore_inhibited,
272 bool is_edge) {
273
274 assert(m);
275 assert(HANDLE_ACTION_IS_SLEEP(handle));
276
277 if (handle == HANDLE_SLEEP) {
278 HandleAction a;
279
280 a = handle_action_sleep_select(m);
281 if (a < 0)
282 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
283 "None of the configured sleep operations are supported, ignoring.");
284
285 return handle_action_sleep_execute(m, a, ignore_inhibited, is_edge);
286 }
287
288 bool supported = handle_action_sleep_supported(handle);
289
290 if (!supported && handle != HANDLE_SUSPEND) {
291 supported = sleep_supported(SLEEP_SUSPEND) > 0;
292 if (supported) {
293 log_notice("Requested %s operation is not supported, using regular suspend instead.",
294 handle_action_to_string(handle));
295 handle = HANDLE_SUSPEND;
296 }
297 }
298
299 if (!supported)
300 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
301 "Requested %s operation not supported, ignoring.", handle_action_to_string(handle));
302
303 return handle_action_execute(m, handle, ignore_inhibited, is_edge);
304 }
305
306 static int manager_handle_action_secure_attention_key(
307 Manager *m,
308 bool is_edge,
309 const char *seat) {
310
311 int r;
312 Seat *o;
313 _cleanup_free_ char *p = NULL;
314
315 assert(m);
316
317 if (!is_edge)
318 return 0;
319
320 if (!seat)
321 return 0;
322
323 o = hashmap_get(m->seats, seat);
324 if (!o)
325 return 0;
326
327 p = seat_bus_path(o);
328 if (!p)
329 return log_oom();
330
331 log_struct(LOG_INFO,
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));
335
336 r = sd_bus_emit_signal(
337 m->bus,
338 "/org/freedesktop/login1",
339 "org.freedesktop.login1.Manager",
340 "SecureAttentionKey",
341 "so", seat, p);
342 if (r < 0)
343 log_warning_errno(r, "Failed to emit SecureAttentionKey signal, ignoring: %m");
344
345 return 0;
346 }
347
348 int manager_handle_action(
349 Manager *m,
350 InhibitWhat inhibit_key,
351 HandleAction handle,
352 bool ignore_inhibited,
353 bool is_edge,
354 const char *action_seat) {
355
356 assert(m);
357 assert(handle_action_valid(handle));
358
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");
364 return 0;
365 }
366
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.");
372 return 0;
373 }
374 }
375
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));
382 return 0;
383 }
384 }
385
386 /* Locking and greeter activation is handled differently from the rest. */
387 if (handle == HANDLE_LOCK) {
388 if (!is_edge)
389 return 0;
390
391 log_info("Locking sessions...");
392 session_send_lock_all(m, true);
393 return 1;
394 }
395
396 if (handle == HANDLE_SECURE_ATTENTION_KEY)
397 return manager_handle_action_secure_attention_key(m, is_edge, action_seat);
398
399 if (HANDLE_ACTION_IS_SLEEP(handle))
400 return handle_action_sleep_execute(m, handle, ignore_inhibited, is_edge);
401
402 return handle_action_execute(m, handle, ignore_inhibited, is_edge);
403 }
404
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",
420 };
421
422 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(handle_action_verb, HandleAction);
423
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",
441 };
442
443 DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction);
444 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction);
445
446 int config_parse_handle_action_sleep(
447 const char *unit,
448 const char *filename,
449 unsigned line,
450 const char *section,
451 unsigned section_line,
452 const char *lvalue,
453 int ltype,
454 const char *rvalue,
455 void *data,
456 void *userdata) {
457
458 HandleActionSleepMask *mask = ASSERT_PTR(data);
459 _cleanup_strv_free_ char **actions = NULL;
460
461 assert(filename);
462 assert(lvalue);
463 assert(rvalue);
464
465 if (isempty(rvalue))
466 goto empty;
467
468 if (strv_split_full(&actions, rvalue, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE) < 0)
469 return log_oom();
470
471 *mask = 0;
472
473 STRV_FOREACH(action, actions) {
474 HandleAction a;
475
476 a = handle_action_from_string(*action);
477 if (a < 0) {
478 log_syntax(unit, LOG_WARNING, filename, line, a,
479 "Failed to parse SleepOperation '%s', ignoring: %m", *action);
480 continue;
481 }
482
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);
486 continue;
487 }
488
489 SET_BIT(*mask, a);
490 }
491
492 if (*mask == 0)
493 goto empty;
494
495 return 0;
496
497 empty:
498 *mask = HANDLE_ACTION_SLEEP_MASK_DEFAULT;
499 return 0;
500 }