]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
daf71ef6 LP |
2 | |
3 | #include <unistd.h> | |
4 | ||
5 | #include "sd-login.h" | |
6 | ||
7 | #include "bus-error.h" | |
8 | #include "bus-locator.h" | |
8885fed4 | 9 | #include "login-util.h" |
daf71ef6 LP |
10 | #include "process-util.h" |
11 | #include "systemctl-logind.h" | |
12 | #include "systemctl-start-unit.h" | |
13 | #include "systemctl-util.h" | |
14 | #include "systemctl.h" | |
15 | #include "terminal-util.h" | |
16 | #include "user-util.h" | |
17 | ||
346840b1 | 18 | static int logind_set_wall_message(sd_bus *bus) { |
daf71ef6 LP |
19 | #if ENABLE_LOGIND |
20 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
daf71ef6 LP |
21 | _cleanup_free_ char *m = NULL; |
22 | int r; | |
23 | ||
9071eea0 MY |
24 | assert(bus); |
25 | ||
daf71ef6 LP |
26 | m = strv_join(arg_wall, " "); |
27 | if (!m) | |
28 | return log_oom(); | |
29 | ||
30 | log_debug("%s wall message \"%s\".", arg_dry_run ? "Would set" : "Setting", m); | |
31 | if (arg_dry_run) | |
32 | return 0; | |
33 | ||
34 | r = bus_call_method(bus, bus_login_mgr, "SetWallMessage", &error, NULL, "sb", m, !arg_no_wall); | |
35 | if (r < 0) | |
36 | return log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r)); | |
37 | #endif | |
38 | return 0; | |
39 | } | |
40 | ||
41 | /* Ask systemd-logind, which might grant access to unprivileged users through polkit */ | |
42 | int logind_reboot(enum action a) { | |
43 | #if ENABLE_LOGIND | |
1cc11a09 ZJS |
44 | static const char* actions[_ACTION_MAX] = { |
45 | [ACTION_POWEROFF] = "PowerOff", | |
46 | [ACTION_REBOOT] = "Reboot", | |
47 | [ACTION_KEXEC] = "Reboot", | |
34f21ff6 | 48 | [ACTION_SOFT_REBOOT] = "Reboot", |
1cc11a09 ZJS |
49 | [ACTION_HALT] = "Halt", |
50 | [ACTION_SUSPEND] = "Suspend", | |
51 | [ACTION_HIBERNATE] = "Hibernate", | |
52 | [ACTION_HYBRID_SLEEP] = "HybridSleep", | |
53 | [ACTION_SUSPEND_THEN_HIBERNATE] = "SuspendThenHibernate", | |
5b356289 | 54 | [ACTION_SLEEP] = "Sleep", |
daf71ef6 LP |
55 | }; |
56 | ||
57 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
8885fed4 | 58 | uint64_t flags = 0; |
daf71ef6 LP |
59 | sd_bus *bus; |
60 | int r; | |
61 | ||
9071eea0 MY |
62 | assert(a >= 0); |
63 | assert(a < _ACTION_MAX); | |
64 | ||
65 | if (!actions[a]) | |
daf71ef6 LP |
66 | return -EINVAL; |
67 | ||
68 | r = acquire_bus(BUS_FULL, &bus); | |
69 | if (r < 0) | |
70 | return r; | |
71 | ||
72 | polkit_agent_open_maybe(); | |
346840b1 | 73 | (void) logind_set_wall_message(bus); |
daf71ef6 | 74 | |
5b356289 | 75 | const char *method_with_flags = a == ACTION_SLEEP ? actions[a] : strjoina(actions[a], "WithFlags"); |
1cc11a09 ZJS |
76 | |
77 | log_debug("%s org.freedesktop.login1.Manager %s dbus call.", | |
78 | arg_dry_run ? "Would execute" : "Executing", method_with_flags); | |
daf71ef6 LP |
79 | |
80 | if (arg_dry_run) | |
81 | return 0; | |
82 | ||
8885fed4 | 83 | SET_FLAG(flags, SD_LOGIND_ROOT_CHECK_INHIBITORS, arg_check_inhibitors > 0); |
665a3d6d LB |
84 | SET_FLAG(flags, |
85 | SD_LOGIND_REBOOT_VIA_KEXEC, | |
86 | a == ACTION_KEXEC || (a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_KEXEC") <= 0)); | |
87 | SET_FLAG(flags, | |
88 | SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP, | |
89 | a == ACTION_REBOOT && getenv_bool("SYSTEMCTL_SKIP_AUTO_SOFT_REBOOT") <= 0); | |
34f21ff6 | 90 | SET_FLAG(flags, SD_LOGIND_SOFT_REBOOT, a == ACTION_SOFT_REBOOT); |
8885fed4 | 91 | |
8885fed4 | 92 | r = bus_call_method(bus, bus_login_mgr, method_with_flags, &error, NULL, "t", flags); |
d334c121 LB |
93 | if (r < 0 && FLAGS_SET(flags, SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP) && |
94 | sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) { | |
95 | sd_bus_error_free(&error); | |
96 | r = bus_call_method( | |
97 | bus, | |
98 | bus_login_mgr, | |
99 | method_with_flags, | |
100 | &error, | |
101 | NULL, | |
102 | "t", | |
103 | flags & ~SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP); | |
104 | } | |
8885fed4 DR |
105 | if (r >= 0) |
106 | return 0; | |
5b356289 | 107 | if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD) || a == ACTION_SLEEP) |
1cc11a09 | 108 | return log_error_errno(r, "Call to %s failed: %s", actions[a], bus_error_message(&error, r)); |
8885fed4 | 109 | |
1cc11a09 ZJS |
110 | /* Fall back to original methods in case there is an older version of systemd-logind */ |
111 | log_debug("Method %s not available: %s. Falling back to %s", method_with_flags, bus_error_message(&error, r), actions[a]); | |
8885fed4 DR |
112 | sd_bus_error_free(&error); |
113 | ||
1cc11a09 | 114 | r = bus_call_method(bus, bus_login_mgr, actions[a], &error, NULL, "b", arg_ask_password); |
daf71ef6 | 115 | if (r < 0) |
1cc11a09 | 116 | return log_error_errno(r, "Call to %s failed: %s", actions[a], bus_error_message(&error, r)); |
daf71ef6 LP |
117 | |
118 | return 0; | |
119 | #else | |
120 | return -ENOSYS; | |
121 | #endif | |
122 | } | |
123 | ||
124 | int logind_check_inhibitors(enum action a) { | |
125 | #if ENABLE_LOGIND | |
126 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
127 | _cleanup_strv_free_ char **sessions = NULL; | |
128 | const char *what, *who, *why, *mode; | |
129 | uint32_t uid, pid; | |
130 | sd_bus *bus; | |
131 | unsigned c = 0; | |
daf71ef6 LP |
132 | int r; |
133 | ||
9071eea0 MY |
134 | assert(a >= 0); |
135 | assert(a < _ACTION_MAX); | |
136 | ||
4327574f | 137 | if (arg_check_inhibitors == 0 || arg_force > 0) |
daf71ef6 LP |
138 | return 0; |
139 | ||
140 | if (arg_when > 0) | |
141 | return 0; | |
142 | ||
4327574f FS |
143 | if (arg_check_inhibitors < 0) { |
144 | if (geteuid() == 0) | |
145 | return 0; | |
daf71ef6 | 146 | |
4327574f FS |
147 | if (!on_tty()) |
148 | return 0; | |
149 | } | |
daf71ef6 LP |
150 | |
151 | if (arg_transport != BUS_TRANSPORT_LOCAL) | |
152 | return 0; | |
153 | ||
154 | r = acquire_bus(BUS_FULL, &bus); | |
155 | if (r < 0) | |
156 | return r; | |
157 | ||
158 | r = bus_call_method(bus, bus_login_mgr, "ListInhibitors", NULL, &reply, NULL); | |
159 | if (r < 0) | |
160 | /* If logind is not around, then there are no inhibitors... */ | |
161 | return 0; | |
162 | ||
163 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); | |
164 | if (r < 0) | |
165 | return bus_log_parse_error(r); | |
166 | ||
167 | while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { | |
168 | _cleanup_free_ char *comm = NULL, *user = NULL; | |
169 | _cleanup_strv_free_ char **sv = NULL; | |
170 | ||
171 | if (!streq(mode, "block")) | |
172 | continue; | |
173 | ||
174 | sv = strv_split(what, ":"); | |
175 | if (!sv) | |
176 | return log_oom(); | |
177 | ||
178 | if (!pid_is_valid((pid_t) pid)) | |
179 | return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Invalid PID "PID_FMT".", (pid_t) pid); | |
180 | ||
181 | if (!strv_contains(sv, | |
182 | IN_SET(a, | |
183 | ACTION_HALT, | |
184 | ACTION_POWEROFF, | |
185 | ACTION_REBOOT, | |
186 | ACTION_KEXEC) ? "shutdown" : "sleep")) | |
187 | continue; | |
188 | ||
d7d74854 | 189 | (void) pid_get_comm(pid, &comm); |
daf71ef6 LP |
190 | user = uid_to_name(uid); |
191 | ||
192 | log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".", | |
193 | who, (pid_t) pid, strna(comm), strna(user), why); | |
194 | ||
195 | c++; | |
196 | } | |
197 | if (r < 0) | |
198 | return bus_log_parse_error(r); | |
199 | ||
200 | r = sd_bus_message_exit_container(reply); | |
201 | if (r < 0) | |
202 | return bus_log_parse_error(r); | |
203 | ||
204 | /* Check for current sessions */ | |
205 | sd_get_sessions(&sessions); | |
206 | STRV_FOREACH(s, sessions) { | |
207 | _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL; | |
208 | ||
209 | if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid()) | |
210 | continue; | |
211 | ||
212 | if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user")) | |
213 | continue; | |
214 | ||
215 | if (sd_session_get_type(*s, &type) < 0 || !STR_IN_SET(type, "x11", "wayland", "tty", "mir")) | |
216 | continue; | |
217 | ||
218 | sd_session_get_tty(*s, &tty); | |
219 | sd_session_get_seat(*s, &seat); | |
220 | sd_session_get_service(*s, &service); | |
221 | user = uid_to_name(uid); | |
222 | ||
223 | log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty); | |
224 | c++; | |
225 | } | |
226 | ||
227 | if (c <= 0) | |
228 | return 0; | |
229 | ||
d7a0f1f4 FS |
230 | return log_error_errno(SYNTHETIC_ERRNO(EPERM), |
231 | "Please retry operation after closing inhibitors and logging out other users.\n" | |
232 | "Alternatively, ignore inhibitors and users with 'systemctl %s -i'.", | |
233 | action_table[a].verb); | |
daf71ef6 LP |
234 | #else |
235 | return 0; | |
236 | #endif | |
237 | } | |
238 | ||
239 | int prepare_firmware_setup(void) { | |
240 | ||
241 | if (!arg_firmware_setup) | |
242 | return 0; | |
243 | ||
244 | #if ENABLE_LOGIND | |
245 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
246 | sd_bus *bus; | |
247 | int r; | |
248 | ||
249 | r = acquire_bus(BUS_FULL, &bus); | |
250 | if (r < 0) | |
251 | return r; | |
252 | ||
253 | r = bus_call_method(bus, bus_login_mgr, "SetRebootToFirmwareSetup", &error, NULL, "b", true); | |
254 | if (r < 0) | |
255 | return log_error_errno(r, "Cannot indicate to EFI to boot into setup mode: %s", bus_error_message(&error, r)); | |
256 | ||
257 | return 0; | |
258 | #else | |
259 | return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), | |
260 | "Booting into firmware setup not supported."); | |
261 | #endif | |
262 | } | |
263 | ||
264 | int prepare_boot_loader_menu(void) { | |
265 | ||
266 | if (arg_boot_loader_menu == USEC_INFINITY) | |
267 | return 0; | |
268 | ||
269 | #if ENABLE_LOGIND | |
270 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
271 | sd_bus *bus; | |
272 | int r; | |
273 | ||
274 | r = acquire_bus(BUS_FULL, &bus); | |
275 | if (r < 0) | |
276 | return r; | |
277 | ||
278 | r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderMenu", &error, NULL, "t", arg_boot_loader_menu); | |
279 | if (r < 0) | |
280 | return log_error_errno(r, "Cannot indicate to boot loader to enter boot loader entry menu: %s", bus_error_message(&error, r)); | |
281 | ||
282 | return 0; | |
283 | #else | |
284 | return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), | |
285 | "Booting into boot loader menu not supported."); | |
286 | #endif | |
287 | } | |
288 | ||
289 | int prepare_boot_loader_entry(void) { | |
290 | ||
291 | if (!arg_boot_loader_entry) | |
292 | return 0; | |
293 | ||
294 | #if ENABLE_LOGIND | |
295 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
296 | sd_bus *bus; | |
297 | int r; | |
298 | ||
299 | r = acquire_bus(BUS_FULL, &bus); | |
300 | if (r < 0) | |
301 | return r; | |
302 | ||
303 | r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderEntry", &error, NULL, "s", arg_boot_loader_entry); | |
304 | if (r < 0) | |
305 | return log_error_errno(r, "Cannot set boot into loader entry '%s': %s", arg_boot_loader_entry, bus_error_message(&error, r)); | |
306 | ||
307 | return 0; | |
308 | #else | |
309 | return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), | |
310 | "Booting into boot loader entry not supported."); | |
311 | #endif | |
312 | } | |
313 | ||
92b00e86 | 314 | int logind_schedule_shutdown(enum action a) { |
daf71ef6 LP |
315 | #if ENABLE_LOGIND |
316 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
daf71ef6 | 317 | const char *action; |
daf71ef6 LP |
318 | sd_bus *bus; |
319 | int r; | |
320 | ||
92b00e86 MY |
321 | assert(a >= 0); |
322 | assert(a < _ACTION_MAX); | |
323 | ||
daf71ef6 LP |
324 | r = acquire_bus(BUS_FULL, &bus); |
325 | if (r < 0) | |
326 | return r; | |
327 | ||
92b00e86 | 328 | action = action_table[a].verb; |
00886e06 LN |
329 | if (!action) |
330 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Scheduling not supported for this action."); | |
daf71ef6 LP |
331 | |
332 | if (arg_dry_run) | |
333 | action = strjoina("dry-", action); | |
334 | ||
346840b1 | 335 | (void) logind_set_wall_message(bus); |
daf71ef6 LP |
336 | |
337 | r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when); | |
338 | if (r < 0) | |
38d55bf2 | 339 | return log_warning_errno(r, "Failed to schedule shutdown: %s", bus_error_message(&error, r)); |
daf71ef6 LP |
340 | |
341 | if (!arg_quiet) | |
8e985681 LN |
342 | logind_show_shutdown(); |
343 | ||
daf71ef6 LP |
344 | return 0; |
345 | #else | |
346 | return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), | |
347 | "Cannot schedule shutdown without logind support, proceeding with immediate shutdown."); | |
348 | #endif | |
349 | } | |
350 | ||
351 | int logind_cancel_shutdown(void) { | |
352 | #if ENABLE_LOGIND | |
353 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
354 | sd_bus *bus; | |
355 | int r; | |
356 | ||
357 | r = acquire_bus(BUS_FULL, &bus); | |
358 | if (r < 0) | |
359 | return r; | |
360 | ||
346840b1 | 361 | (void) logind_set_wall_message(bus); |
daf71ef6 LP |
362 | |
363 | r = bus_call_method(bus, bus_login_mgr, "CancelScheduledShutdown", &error, NULL, NULL); | |
364 | if (r < 0) | |
365 | return log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s", bus_error_message(&error, r)); | |
366 | ||
367 | return 0; | |
368 | #else | |
369 | return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), | |
370 | "Not compiled with logind support, cannot cancel scheduled shutdowns."); | |
371 | #endif | |
372 | } | |
373 | ||
a9c3cc8d LN |
374 | int logind_show_shutdown(void) { |
375 | #if ENABLE_LOGIND | |
376 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
377 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; | |
378 | sd_bus *bus; | |
1433e1f9 | 379 | const char *action, *pretty_action; |
a9c3cc8d LN |
380 | uint64_t elapse; |
381 | int r; | |
382 | ||
383 | r = acquire_bus(BUS_FULL, &bus); | |
384 | if (r < 0) | |
385 | return r; | |
386 | ||
387 | r = bus_get_property(bus, bus_login_mgr, "ScheduledShutdown", &error, &reply, "(st)"); | |
388 | if (r < 0) | |
389 | return log_error_errno(r, "Failed to query scheduled shutdown: %s", bus_error_message(&error, r)); | |
390 | ||
391 | r = sd_bus_message_read(reply, "(st)", &action, &elapse); | |
392 | if (r < 0) | |
393 | return r; | |
394 | ||
395 | if (isempty(action)) | |
396 | return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "No scheduled shutdown."); | |
397 | ||
f80c3d5c | 398 | if (STR_IN_SET(action, "halt", "poweroff", "exit")) |
1433e1f9 | 399 | pretty_action = "Shutdown"; |
8e985681 | 400 | else if (streq(action, "kexec")) |
1433e1f9 | 401 | pretty_action = "Reboot via kexec"; |
8e985681 | 402 | else if (streq(action, "reboot")) |
1433e1f9 MY |
403 | pretty_action = "Reboot"; |
404 | else /* If we don't recognize the action string, we'll show it as-is */ | |
405 | pretty_action = action; | |
406 | ||
407 | if (arg_action == ACTION_SYSTEMCTL) | |
408 | log_info("%s scheduled for %s, use 'systemctl %s --when=cancel' to cancel.", | |
409 | pretty_action, | |
410 | FORMAT_TIMESTAMP_STYLE(elapse, arg_timestamp_style), | |
411 | action); | |
412 | else | |
413 | log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", | |
414 | pretty_action, | |
415 | FORMAT_TIMESTAMP_STYLE(elapse, arg_timestamp_style)); | |
a9c3cc8d LN |
416 | |
417 | return 0; | |
418 | #else | |
419 | return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), | |
420 | "Not compiled with logind support, cannot show scheduled shutdowns."); | |
421 | #endif | |
422 | } | |
423 | ||
daf71ef6 LP |
424 | int help_boot_loader_entry(void) { |
425 | #if ENABLE_LOGIND | |
426 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
427 | _cleanup_strv_free_ char **l = NULL; | |
428 | sd_bus *bus; | |
daf71ef6 LP |
429 | int r; |
430 | ||
431 | r = acquire_bus(BUS_FULL, &bus); | |
432 | if (r < 0) | |
433 | return r; | |
434 | ||
435 | r = bus_get_property_strv(bus, bus_login_mgr, "BootLoaderEntries", &error, &l); | |
436 | if (r < 0) | |
437 | return log_error_errno(r, "Failed to enumerate boot loader entries: %s", bus_error_message(&error, r)); | |
438 | ||
439 | if (strv_isempty(l)) | |
440 | return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "No boot loader entries discovered."); | |
441 | ||
442 | STRV_FOREACH(i, l) | |
443 | puts(*i); | |
444 | ||
445 | return 0; | |
446 | #else | |
447 | return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), | |
448 | "Not compiled with logind support, cannot display boot loader entries."); | |
449 | #endif | |
450 | } |