]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
23406ce5 | 2 | |
dc3a1b76 LP |
3 | #include <unistd.h> |
4 | ||
5ed73478 LN |
5 | #include "sd-messages.h" |
6 | ||
b5efdb8a | 7 | #include "alloc-util.h" |
cc377381 | 8 | #include "bus-error.h" |
8b43440b LP |
9 | #include "bus-util.h" |
10 | #include "conf-parser.h" | |
f97b34a6 | 11 | #include "format-util.h" |
8b43440b | 12 | #include "logind-action.h" |
6ecda0fb LP |
13 | #include "logind-dbus.h" |
14 | #include "logind-session-dbus.h" | |
0b452006 | 15 | #include "process-util.h" |
8b43440b LP |
16 | #include "special.h" |
17 | #include "string-table.h" | |
288a74cc | 18 | #include "terminal-util.h" |
b1d4f8e1 | 19 | #include "user-util.h" |
23406ce5 | 20 | |
48bccaa9 | 21 | static const HandleActionData handle_action_data_table[_HANDLE_ACTION_MAX] = { |
5ed73478 | 22 | [HANDLE_POWEROFF] = { |
54141d8d LN |
23 | .handle = HANDLE_POWEROFF, |
24 | .target = SPECIAL_POWEROFF_TARGET, | |
25 | .inhibit_what = INHIBIT_SHUTDOWN, | |
26 | .polkit_action = "org.freedesktop.login1.power-off", | |
27 | .polkit_action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions", | |
28 | .polkit_action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit", | |
29 | .sleep_operation = _SLEEP_OPERATION_INVALID, | |
30 | .message_id = SD_MESSAGE_SHUTDOWN_STR, | |
31 | .message = "System is powering down", | |
153d1579 | 32 | .log_verb = "power-off", |
54141d8d | 33 | }, |
5ed73478 | 34 | [HANDLE_REBOOT] = { |
54141d8d LN |
35 | .handle = HANDLE_REBOOT, |
36 | .target = SPECIAL_REBOOT_TARGET, | |
37 | .inhibit_what = INHIBIT_SHUTDOWN, | |
38 | .polkit_action = "org.freedesktop.login1.reboot", | |
39 | .polkit_action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions", | |
40 | .polkit_action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit", | |
41 | .sleep_operation = _SLEEP_OPERATION_INVALID, | |
42 | .message_id = SD_MESSAGE_SHUTDOWN_STR, | |
43 | .message = "System is rebooting", | |
153d1579 | 44 | .log_verb = "reboot", |
54141d8d | 45 | }, |
510eccab | 46 | [HANDLE_HALT] = { |
54141d8d LN |
47 | .handle = HANDLE_HALT, |
48 | .target = SPECIAL_HALT_TARGET, | |
49 | .inhibit_what = INHIBIT_SHUTDOWN, | |
50 | .polkit_action = "org.freedesktop.login1.halt", | |
51 | .polkit_action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions", | |
52 | .polkit_action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit", | |
53 | .sleep_operation = _SLEEP_OPERATION_INVALID, | |
54 | .message_id = SD_MESSAGE_SHUTDOWN_STR, | |
55 | .message = "System is halting", | |
153d1579 | 56 | .log_verb = "halt", |
54141d8d | 57 | }, |
510eccab | 58 | [HANDLE_KEXEC] = { |
54141d8d LN |
59 | .handle = HANDLE_KEXEC, |
60 | .target = SPECIAL_KEXEC_TARGET, | |
61 | .inhibit_what = INHIBIT_SHUTDOWN, | |
62 | .polkit_action = "org.freedesktop.login1.reboot", | |
63 | .polkit_action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions", | |
64 | .polkit_action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit", | |
65 | .sleep_operation = _SLEEP_OPERATION_INVALID, | |
66 | .message_id = SD_MESSAGE_SHUTDOWN_STR, | |
67 | .message = "System is rebooting with kexec", | |
153d1579 | 68 | .log_verb = "kexec", |
54141d8d | 69 | }, |
9edf5af5 LP |
70 | [HANDLE_SOFT_REBOOT] = { |
71 | .handle = HANDLE_SOFT_REBOOT, | |
72 | .target = SPECIAL_SOFT_REBOOT_TARGET, | |
73 | .inhibit_what = INHIBIT_SHUTDOWN, | |
74 | .polkit_action = "org.freedesktop.login1.reboot", | |
75 | .polkit_action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions", | |
76 | .polkit_action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit", | |
77 | .sleep_operation = _SLEEP_OPERATION_INVALID, | |
78 | .message_id = SD_MESSAGE_SHUTDOWN_STR, | |
79 | .message = "System userspace is rebooting", | |
80 | .log_verb = "soft-reboot", | |
81 | }, | |
510eccab | 82 | [HANDLE_SUSPEND] = { |
54141d8d LN |
83 | .handle = HANDLE_SUSPEND, |
84 | .target = SPECIAL_SUSPEND_TARGET, | |
85 | .inhibit_what = INHIBIT_SLEEP, | |
86 | .polkit_action = "org.freedesktop.login1.suspend", | |
87 | .polkit_action_multiple_sessions = "org.freedesktop.login1.suspend-multiple-sessions", | |
88 | .polkit_action_ignore_inhibit = "org.freedesktop.login1.suspend-ignore-inhibit", | |
89 | .sleep_operation = SLEEP_SUSPEND, | |
90 | }, | |
510eccab | 91 | [HANDLE_HIBERNATE] = { |
54141d8d LN |
92 | .handle = HANDLE_HIBERNATE, |
93 | .target = SPECIAL_HIBERNATE_TARGET, | |
94 | .inhibit_what = INHIBIT_SLEEP, | |
95 | .polkit_action = "org.freedesktop.login1.hibernate", | |
96 | .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions", | |
97 | .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit", | |
98 | .sleep_operation = SLEEP_HIBERNATE, | |
99 | }, | |
510eccab | 100 | [HANDLE_HYBRID_SLEEP] = { |
54141d8d LN |
101 | .handle = HANDLE_HYBRID_SLEEP, |
102 | .target = SPECIAL_HYBRID_SLEEP_TARGET, | |
103 | .inhibit_what = INHIBIT_SLEEP, | |
104 | .polkit_action = "org.freedesktop.login1.hibernate", | |
105 | .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions", | |
106 | .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit", | |
107 | .sleep_operation = SLEEP_HYBRID_SLEEP, | |
108 | }, | |
510eccab | 109 | [HANDLE_SUSPEND_THEN_HIBERNATE] = { |
54141d8d LN |
110 | .handle = HANDLE_SUSPEND_THEN_HIBERNATE, |
111 | .target = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, | |
112 | .inhibit_what = INHIBIT_SLEEP, | |
113 | .polkit_action = "org.freedesktop.login1.hibernate", | |
114 | .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions", | |
115 | .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit", | |
116 | .sleep_operation = SLEEP_SUSPEND_THEN_HIBERNATE, | |
117 | }, | |
510eccab | 118 | [HANDLE_FACTORY_RESET] = { |
54141d8d LN |
119 | .handle = HANDLE_FACTORY_RESET, |
120 | .target = SPECIAL_FACTORY_RESET_TARGET, | |
121 | .inhibit_what = _INHIBIT_WHAT_INVALID, | |
122 | .sleep_operation = _SLEEP_OPERATION_INVALID, | |
123 | .message_id = SD_MESSAGE_FACTORY_RESET_STR, | |
124 | .message = "System is performing factory reset", | |
125 | }, | |
5ed73478 LN |
126 | }; |
127 | ||
48bccaa9 | 128 | const HandleActionData* handle_action_lookup(HandleAction action) { |
5ed73478 | 129 | |
48bccaa9 LP |
130 | if (action < 0 || (size_t) action >= ELEMENTSOF(handle_action_data_table)) |
131 | return NULL; | |
132 | ||
133 | return &handle_action_data_table[action]; | |
5ed73478 LN |
134 | } |
135 | ||
23406ce5 LP |
136 | int manager_handle_action( |
137 | Manager *m, | |
138 | InhibitWhat inhibit_key, | |
139 | HandleAction handle, | |
140 | bool ignore_inhibited, | |
141 | bool is_edge) { | |
142 | ||
143 | static const char * const message_table[_HANDLE_ACTION_MAX] = { | |
978af07f | 144 | [HANDLE_POWEROFF] = "Powering off...", |
3c98bdce ZJS |
145 | [HANDLE_REBOOT] = "Rebooting...", |
146 | [HANDLE_HALT] = "Halting...", | |
147 | [HANDLE_KEXEC] = "Rebooting via kexec...", | |
9edf5af5 | 148 | [HANDLE_SOFT_REBOOT] = "Rebooting userspace...", |
3c98bdce ZJS |
149 | [HANDLE_SUSPEND] = "Suspending...", |
150 | [HANDLE_HIBERNATE] = "Hibernating...", | |
151 | [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...", | |
e68c79db | 152 | [HANDLE_SUSPEND_THEN_HIBERNATE] = "Suspending, then hibernating...", |
3c98bdce | 153 | [HANDLE_FACTORY_RESET] = "Performing factory reset...", |
23406ce5 LP |
154 | }; |
155 | ||
4afd3348 | 156 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
23406ce5 | 157 | InhibitWhat inhibit_operation; |
85a428c6 | 158 | Inhibitor *offending = NULL; |
af9792ac | 159 | bool supported; |
cc377381 | 160 | int r; |
23406ce5 LP |
161 | |
162 | assert(m); | |
18f689b1 YW |
163 | |
164 | /* If the key handling is turned off, don't do anything */ | |
165 | if (handle == HANDLE_IGNORE) { | |
bf135d82 ZJS |
166 | log_debug("Handling of %s (%s) is disabled, taking no action.", |
167 | inhibit_key == 0 ? "idle timeout" : inhibit_what_to_string(inhibit_key), | |
168 | is_edge ? "edge" : "level"); | |
18f689b1 YW |
169 | return 0; |
170 | } | |
23406ce5 | 171 | |
2d62c530 | 172 | if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) { |
f9cd6be1 LP |
173 | /* If the last system suspend or startup is too close, |
174 | * let's not suspend for now, to give USB docking | |
175 | * stations some time to settle so that we can | |
176 | * properly watch its displays. */ | |
177 | if (m->lid_switch_ignore_event_source) { | |
178 | log_debug("Ignoring lid switch request, system startup or resume too close."); | |
179 | return 0; | |
180 | } | |
2d62c530 LP |
181 | } |
182 | ||
cc377381 | 183 | /* If the key handling is inhibited, don't do anything */ |
06a70b91 | 184 | if (inhibit_key > 0) { |
85a428c6 | 185 | if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) { |
b81b40d4 ZJS |
186 | log_debug("Refusing %s operation, %s is inhibited.", |
187 | handle_action_to_string(handle), | |
188 | inhibit_what_to_string(inhibit_key)); | |
cc377381 LP |
189 | return 0; |
190 | } | |
191 | } | |
192 | ||
193 | /* Locking is handled differently from the rest. */ | |
194 | if (handle == HANDLE_LOCK) { | |
ed4ba7e4 LP |
195 | if (!is_edge) |
196 | return 0; | |
197 | ||
cc377381 LP |
198 | log_info("Locking sessions..."); |
199 | session_send_lock_all(m, true); | |
200 | return 1; | |
201 | } | |
202 | ||
dc3a1b76 | 203 | if (handle == HANDLE_SUSPEND) |
c8cd8ca3 | 204 | supported = can_sleep(SLEEP_SUSPEND) > 0; |
dc3a1b76 | 205 | else if (handle == HANDLE_HIBERNATE) |
c8cd8ca3 | 206 | supported = can_sleep(SLEEP_HIBERNATE) > 0; |
dc3a1b76 | 207 | else if (handle == HANDLE_HYBRID_SLEEP) |
c8cd8ca3 | 208 | supported = can_sleep(SLEEP_HYBRID_SLEEP) > 0; |
e68c79db | 209 | else if (handle == HANDLE_SUSPEND_THEN_HIBERNATE) |
c8cd8ca3 | 210 | supported = can_sleep(SLEEP_SUSPEND_THEN_HIBERNATE) > 0; |
dc3a1b76 | 211 | else if (handle == HANDLE_KEXEC) |
78013564 | 212 | supported = access(KEXEC, X_OK) >= 0; |
af9792ac LP |
213 | else |
214 | supported = true; | |
dc3a1b76 | 215 | |
cb88da82 | 216 | if (!supported && HANDLE_ACTION_IS_SLEEP(handle) && handle != HANDLE_SUSPEND) { |
c8cd8ca3 | 217 | supported = can_sleep(SLEEP_SUSPEND) > 0; |
c282daed | 218 | if (supported) { |
b81b40d4 ZJS |
219 | log_notice("Requested %s operation is not supported, using regular suspend instead.", |
220 | handle_action_to_string(handle)); | |
c282daed LP |
221 | handle = HANDLE_SUSPEND; |
222 | } | |
223 | } | |
224 | ||
b81b40d4 ZJS |
225 | if (!supported) |
226 | return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), | |
227 | "Requested %s operation not supported, ignoring.", handle_action_to_string(handle)); | |
dc3a1b76 | 228 | |
5ed73478 | 229 | if (m->delayed_action) |
b81b40d4 ZJS |
230 | return log_debug_errno(SYNTHETIC_ERRNO(EALREADY), |
231 | "Action already in progress (%s), ignoring requested %s operation.", | |
5ed73478 | 232 | inhibit_what_to_string(m->delayed_action->inhibit_what), |
b81b40d4 | 233 | handle_action_to_string(handle)); |
23406ce5 | 234 | |
48bccaa9 | 235 | inhibit_operation = handle_action_lookup(handle)->inhibit_what; |
23406ce5 LP |
236 | |
237 | /* If the actual operation is inhibited, warn and fail */ | |
238 | if (!ignore_inhibited && | |
85a428c6 LP |
239 | manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) { |
240 | _cleanup_free_ char *comm = NULL, *u = NULL; | |
241 | ||
89bad70f | 242 | (void) get_process_comm(offending->pid.pid, &comm); |
85a428c6 | 243 | u = uid_to_name(offending->uid); |
23406ce5 LP |
244 | |
245 | /* If this is just a recheck of the lid switch then don't warn about anything */ | |
b81b40d4 ZJS |
246 | log_full(is_edge ? LOG_ERR : LOG_DEBUG, |
247 | "Refusing %s operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.", | |
248 | handle_action_to_string(handle), | |
249 | inhibit_what_to_string(inhibit_operation), | |
250 | offending->uid, strna(u), | |
89bad70f | 251 | offending->pid.pid, strna(comm)); |
b81b40d4 ZJS |
252 | |
253 | return is_edge ? -EPERM : 0; | |
23406ce5 LP |
254 | } |
255 | ||
256 | log_info("%s", message_table[handle]); | |
257 | ||
48bccaa9 | 258 | r = bus_manager_shutdown_or_sleep_now_or_later(m, handle_action_lookup(handle), &error); |
4ae25393 | 259 | if (r < 0) |
b81b40d4 ZJS |
260 | return log_error_errno(r, "Failed to execute %s operation: %s", |
261 | handle_action_to_string(handle), | |
262 | bus_error_message(&error, r)); | |
23406ce5 LP |
263 | |
264 | return 1; | |
265 | } | |
266 | ||
3dbb9bc5 ZJS |
267 | static const char* const handle_action_verb_table[_HANDLE_ACTION_MAX] = { |
268 | [HANDLE_IGNORE] = "do nothing", | |
269 | [HANDLE_POWEROFF] = "power off", | |
270 | [HANDLE_REBOOT] = "reboot", | |
271 | [HANDLE_HALT] = "halt", | |
272 | [HANDLE_KEXEC] = "kexec", | |
9edf5af5 | 273 | [HANDLE_SOFT_REBOOT] = "soft-reboot", |
3dbb9bc5 ZJS |
274 | [HANDLE_SUSPEND] = "suspend", |
275 | [HANDLE_HIBERNATE] = "hibernate", | |
276 | [HANDLE_HYBRID_SLEEP] = "enter hybrid sleep", | |
277 | [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend and later hibernate", | |
278 | [HANDLE_FACTORY_RESET] = "perform a factory reset", | |
279 | [HANDLE_LOCK] = "be locked", | |
280 | }; | |
281 | ||
282 | DEFINE_STRING_TABLE_LOOKUP_TO_STRING(handle_action_verb, HandleAction); | |
283 | ||
e4aab5cf LB |
284 | /* These strings are sent out by PrepareForShutdownWithMetadata signals as metadata, so the values cannot |
285 | * change as they are public APIs. */ | |
23406ce5 | 286 | static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { |
3c98bdce ZJS |
287 | [HANDLE_IGNORE] = "ignore", |
288 | [HANDLE_POWEROFF] = "poweroff", | |
289 | [HANDLE_REBOOT] = "reboot", | |
290 | [HANDLE_HALT] = "halt", | |
291 | [HANDLE_KEXEC] = "kexec", | |
9edf5af5 | 292 | [HANDLE_SOFT_REBOOT] = "soft-reboot", |
3c98bdce ZJS |
293 | [HANDLE_SUSPEND] = "suspend", |
294 | [HANDLE_HIBERNATE] = "hibernate", | |
295 | [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", | |
e68c79db | 296 | [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate", |
3c98bdce ZJS |
297 | [HANDLE_FACTORY_RESET] = "factory-reset", |
298 | [HANDLE_LOCK] = "lock", | |
23406ce5 LP |
299 | }; |
300 | ||
301 | DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); | |
302 | DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting"); |