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