chiark / gitweb /
d2fd8ddfbdfb8f5bb9a99ac5aed12a61af822851
[elogind.git] / src / login / logind-action.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2012 Lennart Poettering
6
7   elogind is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   elogind is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with elogind; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <unistd.h>
22
23 #include "alloc-util.h"
24 #include "bus-error.h"
25 #include "bus-util.h"
26 #include "conf-parser.h"
27 #include "format-util.h"
28 #include "logind-action.h"
29 #include "process-util.h"
30 #include "sleep-config.h"
31 //#include "special.h"
32 #include "string-table.h"
33 #include "terminal-util.h"
34 #include "user-util.h"
35
36 /// Additional includes needed by elogind
37 #include "fd-util.h"
38 #include "fileio.h"
39 #include "sd-messages.h"
40 #include "strv.h"
41 const char* manager_target_for_action(HandleAction handle) {
42         static const char * const target_table[_HANDLE_ACTION_MAX] = {
43                 [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
44                 [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
45                 [HANDLE_HALT] = SPECIAL_HALT_TARGET,
46                 [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
47                 [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
48                 [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
49                 [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
50                 [HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
51         };
52
53         assert(handle >= 0);
54         if (handle < (ssize_t) ELEMENTSOF(target_table))
55                 return target_table[handle];
56         return NULL;
57 }
58
59 int manager_handle_action(
60                 Manager *m,
61                 InhibitWhat inhibit_key,
62                 HandleAction handle,
63                 bool ignore_inhibited,
64                 bool is_edge) {
65
66         static const char * const message_table[_HANDLE_ACTION_MAX] = {
67                 [HANDLE_POWEROFF] = "Powering Off...",
68                 [HANDLE_REBOOT] = "Rebooting...",
69                 [HANDLE_HALT] = "Halting...",
70                 [HANDLE_KEXEC] = "Rebooting via kexec...",
71                 [HANDLE_SUSPEND] = "Suspending...",
72                 [HANDLE_HIBERNATE] = "Hibernating...",
73                 [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...",
74                 [HANDLE_SUSPEND_THEN_HIBERNATE] = "Suspending, then hibernating...",
75         };
76
77 #if 0 /// elogind does this itself. No target table required
78 #endif // 0
79         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
80         InhibitWhat inhibit_operation;
81         Inhibitor *offending = NULL;
82         bool supported;
83         const char *target;
84         int r;
85
86         assert(m);
87
88         /* If the key handling is turned off, don't do anything */
89         if (handle == HANDLE_IGNORE) {
90                 log_debug("Refusing operation, as it is turned off.");
91                 return 0;
92         }
93
94         if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) {
95                 /* If the last system suspend or startup is too close,
96                  * let's not suspend for now, to give USB docking
97                  * stations some time to settle so that we can
98                  * properly watch its displays. */
99                 if (m->lid_switch_ignore_event_source) {
100                         log_debug("Ignoring lid switch request, system startup or resume too close.");
101                         return 0;
102                 }
103         }
104
105         /* If the key handling is inhibited, don't do anything */
106         if (inhibit_key > 0) {
107                 if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) {
108                         log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key));
109                         return 0;
110                 }
111         }
112
113         /* Locking is handled differently from the rest. */
114         if (handle == HANDLE_LOCK) {
115                 if (!is_edge)
116                         return 0;
117
118                 log_info("Locking sessions...");
119                 session_send_lock_all(m, true);
120                 return 1;
121         }
122
123 #if 0 /// elogind needs its own can_sleep() variant.
124         if (handle == HANDLE_SUSPEND)
125                 supported = can_sleep("suspend") > 0;
126         else if (handle == HANDLE_HIBERNATE)
127                 supported = can_sleep("hibernate") > 0;
128         else if (handle == HANDLE_HYBRID_SLEEP)
129                 supported = can_sleep("hybrid-sleep") > 0;
130         else if (handle == HANDLE_SUSPEND_THEN_HIBERNATE)
131                 supported = can_sleep("suspend-then-hibernate") > 0;
132 #else
133         if (handle == HANDLE_SUSPEND)
134                 supported = can_sleep(m, "suspend") > 0;
135         else if (handle == HANDLE_HIBERNATE)
136                 supported = can_sleep(m, "hibernate") > 0;
137         else if (handle == HANDLE_HYBRID_SLEEP)
138                 supported = can_sleep(m, "hybrid-sleep") > 0;
139 #endif // 0
140         else if (handle == HANDLE_KEXEC)
141                 supported = access(KEXEC, X_OK) >= 0;
142         else
143                 supported = true;
144
145         if (!supported) {
146                 log_warning("Requested operation not supported, ignoring.");
147                 return -EOPNOTSUPP;
148         }
149
150         if (m->action_what) {
151                 log_debug("Action already in progress, ignoring.");
152                 return -EALREADY;
153         }
154
155         assert_se(target = manager_target_for_action(handle));
156
157         inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE,
158                                            HANDLE_HYBRID_SLEEP,
159                                            HANDLE_SUSPEND_THEN_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
160
161         /* If the actual operation is inhibited, warn and fail */
162         if (!ignore_inhibited &&
163             manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) {
164                 _cleanup_free_ char *comm = NULL, *u = NULL;
165
166                 get_process_comm(offending->pid, &comm);
167                 u = uid_to_name(offending->uid);
168
169                 /* If this is just a recheck of the lid switch then don't warn about anything */
170                 if (!is_edge) {
171                         log_debug("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
172                                   inhibit_what_to_string(inhibit_operation),
173                                   offending->uid, strna(u),
174                                   offending->pid, strna(comm));
175                         return 0;
176                 }
177
178                 log_error("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
179                           inhibit_what_to_string(inhibit_operation),
180                           offending->uid, strna(u),
181                           offending->pid, strna(comm));
182
183                 return -EPERM;
184         }
185
186         log_info("%s", message_table[handle]);
187
188 #if 0 /// elogind uses its own variant, which can use the handle directly.
189         r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error);
190 #else
191         r = bus_manager_shutdown_or_sleep_now_or_later(m, handle, inhibit_operation, &error);
192 #endif // 0
193         if (r < 0) {
194                 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
195                 return r;
196         }
197
198         return 1;
199 }
200
201 static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
202         [HANDLE_IGNORE] = "ignore",
203         [HANDLE_POWEROFF] = "poweroff",
204         [HANDLE_REBOOT] = "reboot",
205         [HANDLE_HALT] = "halt",
206         [HANDLE_KEXEC] = "kexec",
207         [HANDLE_SUSPEND] = "suspend",
208         [HANDLE_HIBERNATE] = "hibernate",
209         [HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
210         [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate",
211         [HANDLE_LOCK] = "lock",
212 };
213
214 DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction);
215 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting");