chiark / gitweb /
d7b1538a33b624f929586bf19a919aa9d99434b0
[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, const char **modes, const char **states) {
227         char *arguments[] = {
228                 NULL,
229                 (char*) "pre",
230                 (char*) arg_verb,
231                 NULL
232         };
233         static const char* const dirs[] = { SYSTEM_SLEEP_PATH, NULL};
234         int r;
235         _cleanup_fclose_ FILE *f = NULL;
236
237         /* This file is opened first, so that if we hit an error,
238          * we can abort before modifying any state. */
239         f = fopen("/sys/power/state", "we");
240         if (!f)
241                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
242
243         /* Configure the hibernation mode */
244         r = write_mode(modes);
245         if (r < 0)
246                 return r;
247
248         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
249
250         log_struct(LOG_INFO,
251                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
252                    LOG_MESSAGE("Suspending system..."),
253                    "SLEEP=%s", arg_verb,
254                    NULL);
255
256         r = write_state(&f, states);
257         if (r < 0)
258                 return r;
259
260         log_struct(LOG_INFO,
261                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
262                    LOG_MESSAGE("System resumed."),
263                    "SLEEP=%s", arg_verb,
264                    NULL);
265
266         arguments[1] = (char*) "post";
267         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
268
269         return r;
270 }
271
272 int shutdown_or_sleep(Manager *m, HandleAction action) {
273         switch (action) {
274         case HANDLE_POWEROFF:
275                 return run_helper(HALT);
276         case HANDLE_REBOOT:
277                 return run_helper(REBOOT);
278         case HANDLE_HALT:
279                 return run_helper(HALT);
280         case HANDLE_KEXEC:
281                 return run_helper(KEXEC);
282         case HANDLE_SUSPEND:
283                 return do_sleep("suspend", m->suspend_mode, m->suspend_state);
284         case HANDLE_HIBERNATE:
285                 return do_sleep("hibernate", m->hibernate_mode, m->hibernate_state);
286         case HANDLE_HYBRID_SLEEP:
287                 return do_sleep("hybrid-sleep", m->hybrid_sleep_mode, m->hybrid_sleep_state);
288         default:
289                 return -EINVAL;
290         }
291 }
292
293 static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
294         [HANDLE_IGNORE] = "ignore",
295         [HANDLE_POWEROFF] = "poweroff",
296         [HANDLE_REBOOT] = "reboot",
297         [HANDLE_HALT] = "halt",
298         [HANDLE_KEXEC] = "kexec",
299         [HANDLE_SUSPEND] = "suspend",
300         [HANDLE_HIBERNATE] = "hibernate",
301         [HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
302         [HANDLE_LOCK] = "lock"
303 };
304
305 DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction);
306 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting");