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