]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-action.c
NEWS: fix typo
[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-messages.h"
6
7 #include "alloc-util.h"
8 #include "bus-error.h"
9 #include "bus-unit-util.h"
10 #include "bus-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"
17 #include "special.h"
18 #include "string-table.h"
19 #include "terminal-util.h"
20 #include "user-util.h"
21
22 static const HandleActionData handle_action_data_table[_HANDLE_ACTION_MAX] = {
23 [HANDLE_POWEROFF] = {
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",
34 },
35 [HANDLE_REBOOT] = {
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",
45 .log_verb = "reboot",
46 },
47 [HANDLE_HALT] = {
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",
57 .log_verb = "halt",
58 },
59 [HANDLE_KEXEC] = {
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",
69 .log_verb = "kexec",
70 },
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",
82 },
83 [HANDLE_SUSPEND] = {
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,
91 },
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,
100 },
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,
109 },
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,
118 },
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",
126 },
127 };
128
129 const HandleActionData* handle_action_lookup(HandleAction action) {
130
131 if (action < 0 || (size_t) action >= ELEMENTSOF(handle_action_data_table))
132 return NULL;
133
134 return &handle_action_data_table[action];
135 }
136
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;
140 }
141
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,
148 HANDLE_HYBRID_SLEEP,
149 HANDLE_SUSPEND,
150 HANDLE_HIBERNATE,
151 };
152
153 int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***ret) {
154 _cleanup_strv_free_ char **actions = NULL;
155 int r;
156
157 assert(ret);
158
159 FOREACH_ELEMENT(i, sleep_actions)
160 if (FLAGS_SET(mask, 1U << *i)) {
161 r = strv_extend(&actions, handle_action_to_string(*i));
162 if (r < 0)
163 return r;
164 }
165
166 *ret = TAKE_PTR(actions);
167 return 0;
168 }
169
170 HandleAction handle_action_sleep_select(Manager *m) {
171 assert(m);
172
173 FOREACH_ELEMENT(i, sleep_actions) {
174 HandleActionSleepMask action_mask = 1U << *i;
175 const HandleActionData *a;
176 _cleanup_free_ char *load_state = NULL;
177
178 if (!FLAGS_SET(m->handle_action_sleep_mask, action_mask))
179 continue;
180
181 a = ASSERT_PTR(handle_action_lookup(*i));
182
183 if (sleep_supported(a->sleep_operation) <= 0)
184 continue;
185
186 (void) unit_load_state(m->bus, a->target, &load_state);
187 if (streq_ptr(load_state, "loaded"))
188 return *i;
189 }
190
191 return _HANDLE_ACTION_INVALID;
192 }
193
194 static int handle_action_execute(
195 Manager *m,
196 HandleAction handle,
197 bool ignore_inhibited,
198 bool is_edge) {
199
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...",
211 };
212
213 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
214 InhibitWhat inhibit_operation;
215 Inhibitor *offending = NULL;
216 int r;
217
218 assert(m);
219 assert(!IN_SET(handle, HANDLE_IGNORE, HANDLE_LOCK, HANDLE_SLEEP));
220
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));
224
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));
230
231 inhibit_operation = ASSERT_PTR(handle_action_lookup(handle))->inhibit_what;
232
233 /* If the actual operation is inhibited, warn and fail */
234 if (inhibit_what_is_valid(inhibit_operation) &&
235 !ignore_inhibited &&
236 manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) {
237 _cleanup_free_ char *comm = NULL, *u = NULL;
238
239 (void) pidref_get_comm(&offending->pid, &comm);
240 u = uid_to_name(offending->uid);
241
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));
249
250 return is_edge ? -EPERM : 0;
251 }
252
253 log_info("%s", message_table[handle]);
254
255 r = bus_manager_shutdown_or_sleep_now_or_later(m, handle_action_lookup(handle), &error);
256 if (r < 0)
257 return log_error_errno(r, "Failed to execute %s operation: %s",
258 handle_action_to_string(handle),
259 bus_error_message(&error, r));
260
261 return 1;
262 }
263
264 static int handle_action_sleep_execute(
265 Manager *m,
266 HandleAction handle,
267 bool ignore_inhibited,
268 bool is_edge) {
269
270 assert(m);
271 assert(HANDLE_ACTION_IS_SLEEP(handle));
272
273 if (handle == HANDLE_SLEEP) {
274 HandleAction a;
275
276 a = handle_action_sleep_select(m);
277 if (a < 0)
278 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
279 "None of the configured sleep operations are supported, ignoring.");
280
281 return handle_action_sleep_execute(m, a, ignore_inhibited, is_edge);
282 }
283
284 bool supported = handle_action_sleep_supported(handle);
285
286 if (!supported && handle != HANDLE_SUSPEND) {
287 supported = sleep_supported(SLEEP_SUSPEND) > 0;
288 if (supported) {
289 log_notice("Requested %s operation is not supported, using regular suspend instead.",
290 handle_action_to_string(handle));
291 handle = HANDLE_SUSPEND;
292 }
293 }
294
295 if (!supported)
296 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
297 "Requested %s operation not supported, ignoring.", handle_action_to_string(handle));
298
299 return handle_action_execute(m, handle, ignore_inhibited, is_edge);
300 }
301
302 int manager_handle_action(
303 Manager *m,
304 InhibitWhat inhibit_key,
305 HandleAction handle,
306 bool ignore_inhibited,
307 bool is_edge) {
308
309 assert(m);
310 assert(handle_action_valid(handle));
311
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");
317 return 0;
318 }
319
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.");
325 return 0;
326 }
327 }
328
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));
335 return 0;
336 }
337 }
338
339 /* Locking is handled differently from the rest. */
340 if (handle == HANDLE_LOCK) {
341 if (!is_edge)
342 return 0;
343
344 log_info("Locking sessions...");
345 session_send_lock_all(m, true);
346 return 1;
347 }
348
349 if (HANDLE_ACTION_IS_SLEEP(handle))
350 return handle_action_sleep_execute(m, handle, ignore_inhibited, is_edge);
351
352 return handle_action_execute(m, handle, ignore_inhibited, is_edge);
353 }
354
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",
369 };
370
371 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(handle_action_verb, HandleAction);
372
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",
389 };
390
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");
393
394 int config_parse_handle_action_sleep(
395 const char *unit,
396 const char *filename,
397 unsigned line,
398 const char *section,
399 unsigned section_line,
400 const char *lvalue,
401 int ltype,
402 const char *rvalue,
403 void *data,
404 void *userdata) {
405
406 HandleActionSleepMask *mask = ASSERT_PTR(data);
407 _cleanup_strv_free_ char **actions = NULL;
408
409 assert(filename);
410 assert(lvalue);
411 assert(rvalue);
412
413 if (isempty(rvalue))
414 goto empty;
415
416 if (strv_split_full(&actions, rvalue, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE) < 0)
417 return log_oom();
418
419 *mask = 0;
420
421 STRV_FOREACH(action, actions) {
422 HandleAction a;
423
424 a = handle_action_from_string(*action);
425 if (a < 0) {
426 log_syntax(unit, LOG_WARNING, filename, line, a,
427 "Failed to parse SleepOperation '%s', ignoring: %m", *action);
428 continue;
429 }
430
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);
434 continue;
435 }
436
437 *mask |= 1U << a;
438 }
439
440 if (*mask == 0)
441 goto empty;
442
443 return 0;
444
445 empty:
446 *mask = HANDLE_ACTION_SLEEP_MASK_DEFAULT;
447 return 0;
448 }