chiark / gitweb /
b0ff774dfd615b6ba3e3b91cd871008067afaaf1
[elogind.git] / src / login / logind-action.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <errno.h>
25
26 #include "sd-messages.h"
27 #include "log.h"
28 #include "util.h"
29 #include "strv.h"
30 #include "fileio.h"
31 #include "build.h"
32 #include "def.h"
33 #include "conf-parser.h"
34 #include "sleep-config.h"
35 #include "bus-error.h"
36 #include "bus-util.h"
37 #include "logind-action.h"
38
39 int manager_handle_action(
40                 Manager *m,
41                 InhibitWhat inhibit_key,
42                 HandleAction handle,
43                 bool ignore_inhibited,
44                 bool is_edge) {
45
46         static const char * const message_table[_HANDLE_ACTION_MAX] = {
47                 [HANDLE_POWEROFF] = "Powering Off...",
48                 [HANDLE_REBOOT] = "Rebooting...",
49                 [HANDLE_HALT] = "Halting...",
50                 [HANDLE_KEXEC] = "Rebooting via kexec...",
51                 [HANDLE_SUSPEND] = "Suspending...",
52                 [HANDLE_HIBERNATE] = "Hibernating...",
53                 [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..."
54         };
55
56         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
57         InhibitWhat inhibit_operation;
58         Inhibitor *offending = NULL;
59         bool supported;
60         int r;
61
62         assert(m);
63
64         /* If the key handling is turned off, don't do anything */
65         if (handle == HANDLE_IGNORE) {
66                 log_debug("Refusing operation, as it is turned off.");
67                 return 0;
68         }
69
70         if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) {
71                 /* If the last system suspend or startup is too close,
72                  * let's not suspend for now, to give USB docking
73                  * stations some time to settle so that we can
74                  * properly watch its displays. */
75                 if (m->lid_switch_ignore_event_source) {
76                         log_debug("Ignoring lid switch request, system startup or resume too close.");
77                         return 0;
78                 }
79         }
80
81         /* If the key handling is inhibited, don't do anything */
82         if (inhibit_key > 0) {
83                 if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) {
84                         log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key));
85                         return 0;
86                 }
87         }
88
89         /* Locking is handled differently from the rest. */
90         if (handle == HANDLE_LOCK) {
91
92                 if (!is_edge)
93                         return 0;
94
95                 log_info("Locking sessions...");
96                 session_send_lock_all(m, true);
97                 return 1;
98         }
99
100         if (handle == HANDLE_SUSPEND)
101                 supported = can_sleep("suspend") > 0;
102         else if (handle == HANDLE_HIBERNATE)
103                 supported = can_sleep("hibernate") > 0;
104         else if (handle == HANDLE_HYBRID_SLEEP)
105                 supported = can_sleep("hybrid-sleep") > 0;
106         else if (handle == HANDLE_KEXEC)
107                 supported = access(KEXEC, X_OK) >= 0;
108         else
109                 supported = true;
110
111         if (!supported) {
112                 log_warning("Requested operation not supported, ignoring.");
113                 return -EOPNOTSUPP;
114         }
115
116         if (m->action_what) {
117                 log_debug("Action already in progress, ignoring.");
118                 return -EALREADY;
119         }
120
121         inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
122
123         /* If the actual operation is inhibited, warn and fail */
124         if (!ignore_inhibited &&
125             manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) {
126                 _cleanup_free_ char *comm = NULL, *u = NULL;
127
128                 get_process_comm(offending->pid, &comm);
129                 u = uid_to_name(offending->uid);
130
131                 /* If this is just a recheck of the lid switch then don't warn about anything */
132                 if (!is_edge) {
133                         log_debug("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
134                                   inhibit_what_to_string(inhibit_operation),
135                                   offending->uid, strna(u),
136                                   offending->pid, strna(comm));
137                         return 0;
138                 }
139
140                 log_error("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
141                           inhibit_what_to_string(inhibit_operation),
142                           offending->uid, strna(u),
143                           offending->pid, strna(comm));
144
145                 warn_melody();
146                 return -EPERM;
147         }
148
149         log_info("%s", message_table[handle]);
150
151         r = bus_manager_shutdown_or_sleep_now_or_later(m, handle, inhibit_operation, &error);
152         if (r < 0) {
153                 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
154                 return r;
155         }
156
157         return 1;
158 }
159
160 static int run_helper(const char *helper) {
161         int pid = fork();
162         if (pid < 0) {
163                 return log_error_errno(errno, "Failed to fork: %m");
164         }
165
166         if (pid == 0) {
167                 /* Child */
168
169                 close_all_fds(NULL, 0);
170
171                 execlp(helper, helper, NULL);
172                 log_error_errno(errno, "Failed to execute %s: %m", helper);
173                 _exit(EXIT_FAILURE);
174         }
175
176         return wait_for_terminate_and_warn(helper, pid, true);
177 }
178
179 static int write_mode(char **modes) {
180         int r = 0;
181         char **mode;
182
183         STRV_FOREACH(mode, modes) {
184                 int k;
185
186                 k = write_string_file("/sys/power/disk", *mode);
187                 if (k == 0)
188                         return 0;
189
190                 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m",
191                                 *mode);
192                 if (r == 0)
193                         r = k;
194         }
195
196         if (r < 0)
197                 log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
198
199         return r;
200 }
201
202 static int write_state(FILE **f, char **states) {
203         char **state;
204         int r = 0;
205
206         STRV_FOREACH(state, states) {
207                 int k;
208
209                 k = write_string_stream(*f, *state);
210                 if (k == 0)
211                         return 0;
212                 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
213                                 *state);
214                 if (r == 0)
215                         r = k;
216
217                 fclose(*f);
218                 *f = fopen("/sys/power/state", "we");
219                 if (!*f)
220                         return log_error_errno(errno, "Failed to open /sys/power/state: %m");
221         }
222
223         return r;
224 }
225
226 static int do_sleep(const char *arg_verb) {
227         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
228         char *arguments[] = {
229                 NULL,
230                 (char*) "pre",
231                 (char*) arg_verb,
232                 NULL
233         };
234         static const char* const dirs[] = { SYSTEM_SLEEP_PATH, NULL};
235         int r;
236         _cleanup_fclose_ FILE *f = NULL;
237
238         r = parse_sleep_config(arg_verb, &modes, &states);
239         if (r < 0)
240                 return r;
241
242         /* This file is opened first, so that if we hit an error,
243          * we can abort before modifying any state. */
244         f = fopen("/sys/power/state", "we");
245         if (!f)
246                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
247
248         /* Configure the hibernation mode */
249         r = write_mode(modes);
250         if (r < 0)
251                 return r;
252
253         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
254
255         log_struct(LOG_INFO,
256                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
257                    LOG_MESSAGE("Suspending system..."),
258                    "SLEEP=%s", arg_verb,
259                    NULL);
260
261         r = write_state(&f, states);
262         if (r < 0)
263                 return r;
264
265         log_struct(LOG_INFO,
266                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
267                    LOG_MESSAGE("System resumed."),
268                    "SLEEP=%s", arg_verb,
269                    NULL);
270
271         arguments[1] = (char*) "post";
272         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
273
274         return r;
275 }
276
277 int shutdown_or_sleep(HandleAction action) {
278         switch (action) {
279         case HANDLE_POWEROFF:
280                 return run_helper(HALT);
281         case HANDLE_REBOOT:
282                 return run_helper(REBOOT);
283         case HANDLE_HALT:
284                 return run_helper(HALT);
285         case HANDLE_KEXEC:
286                 return run_helper(KEXEC);
287         case HANDLE_SUSPEND:
288                 return do_sleep("suspend");
289         case HANDLE_HIBERNATE:
290                 return do_sleep("hibernate");
291         case HANDLE_HYBRID_SLEEP:
292                 return do_sleep("hybrid-sleep");
293         default:
294                 return -EINVAL;
295         }
296 }
297
298 static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
299         [HANDLE_IGNORE] = "ignore",
300         [HANDLE_POWEROFF] = "poweroff",
301         [HANDLE_REBOOT] = "reboot",
302         [HANDLE_HALT] = "halt",
303         [HANDLE_KEXEC] = "kexec",
304         [HANDLE_SUSPEND] = "suspend",
305         [HANDLE_HIBERNATE] = "hibernate",
306         [HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
307         [HANDLE_LOCK] = "lock"
308 };
309
310 DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction);
311 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting");