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