chiark / gitweb /
shared: add formats-util.h
[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 #include "formats-util.h"
39
40 int manager_handle_action(
41                 Manager *m,
42                 InhibitWhat inhibit_key,
43                 HandleAction handle,
44                 bool ignore_inhibited,
45                 bool is_edge) {
46
47         static const char * const message_table[_HANDLE_ACTION_MAX] = {
48                 [HANDLE_POWEROFF] = "Powering Off...",
49                 [HANDLE_REBOOT] = "Rebooting...",
50                 [HANDLE_HALT] = "Halting...",
51                 [HANDLE_KEXEC] = "Rebooting via kexec...",
52                 [HANDLE_SUSPEND] = "Suspending...",
53                 [HANDLE_HIBERNATE] = "Hibernating...",
54                 [HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..."
55         };
56
57         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
58         InhibitWhat inhibit_operation;
59         Inhibitor *offending = NULL;
60         bool supported;
61         int r;
62
63         assert(m);
64
65         /* If the key handling is turned off, don't do anything */
66         if (handle == HANDLE_IGNORE) {
67                 log_debug("Refusing operation, as it is turned off.");
68                 return 0;
69         }
70
71         if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) {
72                 /* If the last system suspend or startup is too close,
73                  * let's not suspend for now, to give USB docking
74                  * stations some time to settle so that we can
75                  * properly watch its displays. */
76                 if (m->lid_switch_ignore_event_source) {
77                         log_debug("Ignoring lid switch request, system startup or resume too close.");
78                         return 0;
79                 }
80         }
81
82         /* If the key handling is inhibited, don't do anything */
83         if (inhibit_key > 0) {
84                 if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) {
85                         log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key));
86                         return 0;
87                 }
88         }
89
90         /* Locking is handled differently from the rest. */
91         if (handle == HANDLE_LOCK) {
92
93                 if (!is_edge)
94                         return 0;
95
96                 log_info("Locking sessions...");
97                 session_send_lock_all(m, true);
98                 return 1;
99         }
100
101         if (handle == HANDLE_SUSPEND)
102                 supported = can_sleep("suspend") > 0;
103         else if (handle == HANDLE_HIBERNATE)
104                 supported = can_sleep("hibernate") > 0;
105         else if (handle == HANDLE_HYBRID_SLEEP)
106                 supported = can_sleep("hybrid-sleep") > 0;
107         else if (handle == HANDLE_KEXEC)
108                 supported = access(KEXEC, X_OK) >= 0;
109         else
110                 supported = true;
111
112         if (!supported) {
113                 log_warning("Requested operation not supported, ignoring.");
114                 return -EOPNOTSUPP;
115         }
116
117         if (m->action_what) {
118                 log_debug("Action already in progress, ignoring.");
119                 return -EALREADY;
120         }
121
122         inhibit_operation = handle == HANDLE_SUSPEND || handle == HANDLE_HIBERNATE || handle == HANDLE_HYBRID_SLEEP ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
123
124         /* If the actual operation is inhibited, warn and fail */
125         if (!ignore_inhibited &&
126             manager_is_inhibited(m, inhibit_operation, INHIBIT_BLOCK, NULL, false, false, 0, &offending)) {
127                 _cleanup_free_ char *comm = NULL, *u = NULL;
128
129                 get_process_comm(offending->pid, &comm);
130                 u = uid_to_name(offending->uid);
131
132                 /* If this is just a recheck of the lid switch then don't warn about anything */
133                 if (!is_edge) {
134                         log_debug("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
135                                   inhibit_what_to_string(inhibit_operation),
136                                   offending->uid, strna(u),
137                                   offending->pid, strna(comm));
138                         return 0;
139                 }
140
141                 log_error("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
142                           inhibit_what_to_string(inhibit_operation),
143                           offending->uid, strna(u),
144                           offending->pid, strna(comm));
145
146                 warn_melody();
147                 return -EPERM;
148         }
149
150         log_info("%s", message_table[handle]);
151
152         r = bus_manager_shutdown_or_sleep_now_or_later(m, handle, inhibit_operation, &error);
153         if (r < 0) {
154                 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
155                 return r;
156         }
157
158         return 1;
159 }
160
161 static int run_helper(const char *helper) {
162         int pid = fork();
163         if (pid < 0) {
164                 return log_error_errno(errno, "Failed to fork: %m");
165         }
166
167         if (pid == 0) {
168                 /* Child */
169
170                 close_all_fds(NULL, 0);
171
172                 execlp(helper, helper, NULL);
173                 log_error_errno(errno, "Failed to execute %s: %m", helper);
174                 _exit(EXIT_FAILURE);
175         }
176
177         return wait_for_terminate_and_warn(helper, pid, true);
178 }
179
180 static int write_mode(char **modes) {
181         int r = 0;
182         char **mode;
183
184         STRV_FOREACH(mode, modes) {
185                 int k;
186
187                 k = write_string_file("/sys/power/disk", *mode);
188                 if (k == 0)
189                         return 0;
190
191                 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m",
192                                 *mode);
193                 if (r == 0)
194                         r = k;
195         }
196
197         if (r < 0)
198                 log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
199
200         return r;
201 }
202
203 static int write_state(FILE **f, char **states) {
204         char **state;
205         int r = 0;
206
207         STRV_FOREACH(state, states) {
208                 int k;
209
210                 k = write_string_stream(*f, *state);
211                 if (k == 0)
212                         return 0;
213                 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
214                                 *state);
215                 if (r == 0)
216                         r = k;
217
218                 fclose(*f);
219                 *f = fopen("/sys/power/state", "we");
220                 if (!*f)
221                         return log_error_errno(errno, "Failed to open /sys/power/state: %m");
222         }
223
224         return r;
225 }
226
227 static int do_sleep(const char *arg_verb, const char **modes, const char **states) {
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         /* This file is opened first, so that if we hit an error,
239          * we can abort before modifying any state. */
240         f = fopen("/sys/power/state", "we");
241         if (!f)
242                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
243
244         /* Configure the hibernation mode */
245         r = write_mode(modes);
246         if (r < 0)
247                 return r;
248
249         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
250
251         log_struct(LOG_INFO,
252                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START),
253                    LOG_MESSAGE("Suspending system..."),
254                    "SLEEP=%s", arg_verb,
255                    NULL);
256
257         r = write_state(&f, states);
258         if (r < 0)
259                 return r;
260
261         log_struct(LOG_INFO,
262                    LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP),
263                    LOG_MESSAGE("System resumed."),
264                    "SLEEP=%s", arg_verb,
265                    NULL);
266
267         arguments[1] = (char*) "post";
268         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments);
269
270         return r;
271 }
272
273 int shutdown_or_sleep(Manager *m, HandleAction action) {
274         switch (action) {
275         case HANDLE_POWEROFF:
276                 return run_helper(HALT);
277         case HANDLE_REBOOT:
278                 return run_helper(REBOOT);
279         case HANDLE_HALT:
280                 return run_helper(HALT);
281         case HANDLE_KEXEC:
282                 return run_helper(KEXEC);
283         case HANDLE_SUSPEND:
284                 return do_sleep("suspend", m->suspend_mode, m->suspend_state);
285         case HANDLE_HIBERNATE:
286                 return do_sleep("hibernate", m->hibernate_mode, m->hibernate_state);
287         case HANDLE_HYBRID_SLEEP:
288                 return do_sleep("hybrid-sleep", m->hybrid_sleep_mode, m->hybrid_sleep_state);
289         default:
290                 return -EINVAL;
291         }
292 }
293
294 static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
295         [HANDLE_IGNORE] = "ignore",
296         [HANDLE_POWEROFF] = "poweroff",
297         [HANDLE_REBOOT] = "reboot",
298         [HANDLE_HALT] = "halt",
299         [HANDLE_KEXEC] = "kexec",
300         [HANDLE_SUSPEND] = "suspend",
301         [HANDLE_HIBERNATE] = "hibernate",
302         [HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
303         [HANDLE_LOCK] = "lock"
304 };
305
306 DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction);
307 DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting");