1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "sd-messages.h"
6
7 #include "alloc-util.h"
8 #include "bus-error.h"
9 #include "bus-util.h"
10 #include "conf-parser.h"
11 #include "format-util.h"
12 #include "logind-action.h"
13 #include "logind-dbus.h"
14 #include "logind-session-dbus.h"
15 #include "process-util.h"
16 #include "special.h"
17 #include "string-table.h"
18 #include "terminal-util.h"
19 #include "user-util.h"
20
21 static const HandleActionData handle_action_data_table[_HANDLE_ACTION_MAX] = {
22 [HANDLE_POWEROFF] = {
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",
32 .log_verb = "power-off",
33 },
34 [HANDLE_REBOOT] = {
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",
44 .log_verb = "reboot",
45 },
46 [HANDLE_HALT] = {
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",
56 .log_verb = "halt",
57 },
58 [HANDLE_KEXEC] = {
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",
68 .log_verb = "kexec",
69 },
70 [HANDLE_SUSPEND] = {
71 .handle = HANDLE_SUSPEND,
72 .target = SPECIAL_SUSPEND_TARGET,
73 .inhibit_what = INHIBIT_SLEEP,
74 .polkit_action = "org.freedesktop.login1.suspend",
75 .polkit_action_multiple_sessions = "org.freedesktop.login1.suspend-multiple-sessions",
76 .polkit_action_ignore_inhibit = "org.freedesktop.login1.suspend-ignore-inhibit",
77 .sleep_operation = SLEEP_SUSPEND,
78 },
79 [HANDLE_HIBERNATE] = {
80 .handle = HANDLE_HIBERNATE,
81 .target = SPECIAL_HIBERNATE_TARGET,
82 .inhibit_what = INHIBIT_SLEEP,
83 .polkit_action = "org.freedesktop.login1.hibernate",
84 .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions",
85 .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit",
86 .sleep_operation = SLEEP_HIBERNATE,
87 },
88 [HANDLE_HYBRID_SLEEP] = {
89 .handle = HANDLE_HYBRID_SLEEP,
90 .target = SPECIAL_HYBRID_SLEEP_TARGET,
91 .inhibit_what = INHIBIT_SLEEP,
92 .polkit_action = "org.freedesktop.login1.hibernate",
93 .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions",
94 .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit",
95 .sleep_operation = SLEEP_HYBRID_SLEEP,
96 },
97 [HANDLE_SUSPEND_THEN_HIBERNATE] = {
98 .handle = HANDLE_SUSPEND_THEN_HIBERNATE,
99 .target = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
100 .inhibit_what = INHIBIT_SLEEP,
101 .polkit_action = "org.freedesktop.login1.hibernate",
102 .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions",
103 .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit",
104 .sleep_operation = SLEEP_SUSPEND_THEN_HIBERNATE,
105 },
106 [HANDLE_FACTORY_RESET] = {
107 .handle = HANDLE_FACTORY_RESET,
108 .target = SPECIAL_FACTORY_RESET_TARGET,
109 .inhibit_what = _INHIBIT_WHAT_INVALID,
110 .sleep_operation = _SLEEP_OPERATION_INVALID,
111 .message_id = SD_MESSAGE_FACTORY_RESET_STR,
112 .message = "System is performing factory reset",
113 },
114 };
115
handle_action_lookup(HandleAction action)116 const HandleActionData* handle_action_lookup(HandleAction action) {
117
118 if (action < 0 || (size_t) action >= ELEMENTSOF(handle_action_data_table))
119 return NULL;
120
121 return &handle_action_data_table[action];
122 }
123
manager_handle_action(Manager * m,InhibitWhat inhibit_key,HandleAction handle,bool ignore_inhibited,bool is_edge)124 int manager_handle_action(
125 Manager *m,
126 InhibitWhat inhibit_key,
127 HandleAction handle,
128 bool ignore_inhibited,
129 bool is_edge) {
130
131 static const char * const message_table[_HANDLE_ACTION_MAX] = {
132 [HANDLE_POWEROFF] = "Powering Off...",
133 [HANDLE_REBOOT] = "Rebooting...",
134 [HANDLE_HALT] = "Halting...",
135 [HANDLE_KEXEC] = "Rebooting via kexec...",
136 [HANDLE_SUSPEND] = "Suspending...",
137 [HANDLE_HIBERNATE] = "Hibernating...",
138 [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...",
139 [HANDLE_SUSPEND_THEN_HIBERNATE] = "Suspending, then hibernating...",
140 [HANDLE_FACTORY_RESET] = "Performing factory reset...",
141 };
142
143 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
144 InhibitWhat inhibit_operation;
145 Inhibitor *offending = NULL;
146 bool supported;
147 int r;
148
149 assert(m);
150
151 /* If the key handling is turned off, don't do anything */
152 if (handle == HANDLE_IGNORE) {
153 log_debug("Handling of %s (%s) is disabled, taking no action.",
154 inhibit_key == 0 ? "idle timeout" : inhibit_what_to_string(inhibit_key),
155 is_edge ? "edge" : "level");
156 return 0;
157 }
158
159 if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) {
160 /* If the last system suspend or startup is too close,
161 * let's not suspend for now, to give USB docking
162 * stations some time to settle so that we can
163 * properly watch its displays. */
164 if (m->lid_switch_ignore_event_source) {
165 log_debug("Ignoring lid switch request, system startup or resume too close.");
166 return 0;
167 }
168 }
169
170 /* If the key handling is inhibited, don't do anything */
171 if (inhibit_key > 0) {
172 if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) {
173 log_debug("Refusing %s operation, %s is inhibited.",
174 handle_action_to_string(handle),
175 inhibit_what_to_string(inhibit_key));
176 return 0;
177 }
178 }
179
180 /* Locking is handled differently from the rest. */
181 if (handle == HANDLE_LOCK) {
182 if (!is_edge)
183 return 0;
184
185 log_info("Locking sessions...");
186 session_send_lock_all(m, true);
187 return 1;
188 }
189
190 if (handle == HANDLE_SUSPEND)
191 supported = can_sleep(SLEEP_SUSPEND) > 0;
192 else if (handle == HANDLE_HIBERNATE)
193 supported = can_sleep(SLEEP_HIBERNATE) > 0;
194 else if (handle == HANDLE_HYBRID_SLEEP)
195 supported = can_sleep(SLEEP_HYBRID_SLEEP) > 0;
196 else if (handle == HANDLE_SUSPEND_THEN_HIBERNATE)
197 supported = can_sleep(SLEEP_SUSPEND_THEN_HIBERNATE) > 0;
198 else if (handle == HANDLE_KEXEC)
199 supported = access(KEXEC, X_OK) >= 0;
200 else
201 supported = true;
202
203 if (!supported && IN_SET(handle, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP, HANDLE_SUSPEND_THEN_HIBERNATE)) {
204 supported = can_sleep(SLEEP_SUSPEND) > 0;
205 if (supported) {
206 log_notice("Requested %s operation is not supported, using regular suspend instead.",
207 handle_action_to_string(handle));
208 handle = HANDLE_SUSPEND;
209 }
210 }
211
212 if (!supported)
213 return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
214 "Requested %s operation not supported, ignoring.", handle_action_to_string(handle));
215
216 if (m->delayed_action)
217 return log_debug_errno(SYNTHETIC_ERRNO(EALREADY),
218 "Action already in progress (%s), ignoring requested %s operation.",
219 inhibit_what_to_string(m->delayed_action->inhibit_what),
220 handle_action_to_string(handle));
221
222 inhibit_operation = handle_action_lookup(handle)->inhibit_what;
223
224 /* If the actual operation is inhibited, warn and fail */
225 if (!ignore_inhibited &&
226 manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) {
227 _cleanup_free_ char *comm = NULL, *u = NULL;
228
229 (void) get_process_comm(offending->pid, &comm);
230 u = uid_to_name(offending->uid);
231
232 /* If this is just a recheck of the lid switch then don't warn about anything */
233 log_full(is_edge ? LOG_ERR : LOG_DEBUG,
234 "Refusing %s operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
235 handle_action_to_string(handle),
236 inhibit_what_to_string(inhibit_operation),
237 offending->uid, strna(u),
238 offending->pid, strna(comm));
239
240 return is_edge ? -EPERM : 0;
241 }
242
243 log_info("%s", message_table[handle]);
244
245 r = bus_manager_shutdown_or_sleep_now_or_later(m, handle_action_lookup(handle), &error);
246 if (r < 0)
247 return log_error_errno(r, "Failed to execute %s operation: %s",
248 handle_action_to_string(handle),
249 bus_error_message(&error, r));
250
251 return 1;
252 }
253
254 static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
255 [HANDLE_IGNORE] = "ignore",
256 [HANDLE_POWEROFF] = "poweroff",
257 [HANDLE_REBOOT] = "reboot",
258 [HANDLE_HALT] = "halt",
259 [HANDLE_KEXEC] = "kexec",
260 [HANDLE_SUSPEND] = "suspend",
261 [HANDLE_HIBERNATE] = "hibernate",
262 [HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
263 [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate",
264 [HANDLE_FACTORY_RESET] = "factory-reset",
265 [HANDLE_LOCK] = "lock",
266 };
267
268 DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction);
269 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting");
270