]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sleep/sleep.c
sleep: properly pass verb to sleep script
[thirdparty/systemd.git] / src / sleep / sleep.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 Copyright © 2010-2017 Canonical
4 Copyright © 2018 Dell Inc.
5 ***/
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <linux/fiemap.h>
11 #include <poll.h>
12 #include <stdio.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <sys/timerfd.h>
16 #include <unistd.h>
17
18 #include "sd-messages.h"
19
20 #include "def.h"
21 #include "exec-util.h"
22 #include "fd-util.h"
23 #include "format-util.h"
24 #include "fileio.h"
25 #include "log.h"
26 #include "main-func.h"
27 #include "parse-util.h"
28 #include "pretty-print.h"
29 #include "sleep-config.h"
30 #include "stdio-util.h"
31 #include "string-util.h"
32 #include "strv.h"
33 #include "time-util.h"
34 #include "util.h"
35
36 static char* arg_verb = NULL;
37
38 STATIC_DESTRUCTOR_REGISTER(arg_verb, freep);
39
40 static int write_hibernate_location_info(void) {
41 _cleanup_free_ char *device = NULL, *type = NULL;
42 _cleanup_free_ struct fiemap *fiemap = NULL;
43 char offset_str[DECIMAL_STR_MAX(uint64_t)];
44 char device_str[DECIMAL_STR_MAX(uint64_t)];
45 _cleanup_close_ int fd = -1;
46 struct stat stb;
47 uint64_t offset;
48 int r;
49
50 r = find_hibernate_location(&device, &type, NULL, NULL);
51 if (r < 0)
52 return log_debug_errno(r, "Unable to find hibernation location: %m");
53
54 /* if it's a swap partition, we just write the disk to /sys/power/resume */
55 if (streq(type, "partition")) {
56 r = write_string_file("/sys/power/resume", device, WRITE_STRING_FILE_DISABLE_BUFFER);
57 if (r < 0)
58 return log_debug_errno(r, "Failed to write partition device to /sys/power/resume: %m");
59
60 return r;
61 }
62 if (!streq(type, "file"))
63 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
64 "Invalid hibernate type: %s", type);
65
66 /* Only available in 4.17+ */
67 if (access("/sys/power/resume_offset", W_OK) < 0) {
68 if (errno == ENOENT) {
69 log_debug("Kernel too old, can't configure resume offset, ignoring.");
70 return 0;
71 }
72
73 return log_debug_errno(errno, "/sys/power/resume_offset not writeable: %m");
74 }
75
76 fd = open(device, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
77 if (fd < 0)
78 return log_debug_errno(errno, "Unable to open '%s': %m", device);
79 r = fstat(fd, &stb);
80 if (r < 0)
81 return log_debug_errno(errno, "Unable to stat %s: %m", device);
82
83 r = read_fiemap(fd, &fiemap);
84 if (r < 0)
85 return log_debug_errno(r, "Unable to read extent map for '%s': %m", device);
86 if (fiemap->fm_mapped_extents == 0)
87 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
88 "No extents found in '%s'", device);
89
90 offset = fiemap->fm_extents[0].fe_physical / page_size();
91 xsprintf(offset_str, "%" PRIu64, offset);
92 r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER);
93 if (r < 0)
94 return log_debug_errno(r, "Failed to write offset '%s': %m", offset_str);
95
96 xsprintf(device_str, "%lx", (unsigned long)stb.st_dev);
97 r = write_string_file("/sys/power/resume", device_str, WRITE_STRING_FILE_DISABLE_BUFFER);
98 if (r < 0)
99 return log_debug_errno(r, "Failed to write device '%s': %m", device_str);
100
101 return 0;
102 }
103
104 static int write_mode(char **modes) {
105 int r = 0;
106 char **mode;
107
108 STRV_FOREACH(mode, modes) {
109 int k;
110
111 k = write_string_file("/sys/power/disk", *mode, WRITE_STRING_FILE_DISABLE_BUFFER);
112 if (k >= 0)
113 return 0;
114
115 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", *mode);
116 if (r >= 0)
117 r = k;
118 }
119
120 return r;
121 }
122
123 static int write_state(FILE **f, char **states) {
124 char **state;
125 int r = 0;
126
127 STRV_FOREACH(state, states) {
128 int k;
129
130 k = write_string_stream(*f, *state, WRITE_STRING_FILE_DISABLE_BUFFER);
131 if (k >= 0)
132 return 0;
133 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", *state);
134 if (r >= 0)
135 r = k;
136
137 fclose(*f);
138 *f = fopen("/sys/power/state", "we");
139 if (!*f)
140 return -errno;
141 }
142
143 return r;
144 }
145
146 static int execute(char **modes, char **states) {
147 char *arguments[] = {
148 NULL,
149 (char*) "pre",
150 arg_verb,
151 NULL
152 };
153 static const char* const dirs[] = {
154 SYSTEM_SLEEP_PATH,
155 NULL
156 };
157
158 int r;
159 _cleanup_fclose_ FILE *f = NULL;
160
161 /* This file is opened first, so that if we hit an error,
162 * we can abort before modifying any state. */
163 f = fopen("/sys/power/state", "we");
164 if (!f)
165 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
166
167 setvbuf(f, NULL, _IONBF, 0);
168
169 /* Configure the hibernation mode */
170 if (!strv_isempty(modes)) {
171 r = write_hibernate_location_info();
172 if (r < 0)
173 return log_error_errno(r, "Failed to write hibernation disk offset: %m");
174 r = write_mode(modes);
175 if (r < 0)
176 return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;
177 }
178
179 (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
180
181 log_struct(LOG_INFO,
182 "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
183 LOG_MESSAGE("Suspending system..."),
184 "SLEEP=%s", arg_verb);
185
186 r = write_state(&f, states);
187 if (r < 0)
188 log_struct_errno(LOG_ERR, r,
189 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
190 LOG_MESSAGE("Failed to suspend system. System resumed again: %m"),
191 "SLEEP=%s", arg_verb);
192 else
193 log_struct(LOG_INFO,
194 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
195 LOG_MESSAGE("System resumed."),
196 "SLEEP=%s", arg_verb);
197
198 arguments[1] = (char*) "post";
199 (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
200
201 return r;
202 }
203
204 static int execute_s2h(const SleepConfig *sleep_config) {
205 _cleanup_close_ int tfd = -1;
206 char buf[FORMAT_TIMESPAN_MAX];
207 struct itimerspec ts = {};
208 struct pollfd fds;
209 int r;
210
211 assert(sleep_config);
212
213 tfd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK|TFD_CLOEXEC);
214 if (tfd < 0)
215 return log_error_errno(errno, "Error creating timerfd: %m");
216
217 log_debug("Set timerfd wake alarm for %s",
218 format_timespan(buf, sizeof(buf), sleep_config->hibernate_delay_sec, USEC_PER_SEC));
219
220 timespec_store(&ts.it_value, sleep_config->hibernate_delay_sec);
221
222 r = timerfd_settime(tfd, 0, &ts, NULL);
223 if (r < 0)
224 return log_error_errno(errno, "Error setting hibernate timer: %m");
225
226 r = execute(sleep_config->suspend_modes, sleep_config->suspend_states);
227 if (r < 0)
228 return r;
229
230 fds = (struct pollfd) {
231 .fd = tfd,
232 .events = POLLIN,
233 };
234 r = poll(&fds, 1, 0);
235 if (r < 0)
236 return log_error_errno(errno, "Error polling timerfd: %m");
237
238 tfd = safe_close(tfd);
239
240 if (!FLAGS_SET(fds.revents, POLLIN)) /* We woke up before the alarm time, we are done. */
241 return 0;
242
243 /* If woken up after alarm time, hibernate */
244 log_debug("Attempting to hibernate after waking from %s timer",
245 format_timespan(buf, sizeof(buf), sleep_config->hibernate_delay_sec, USEC_PER_SEC));
246
247 r = execute(sleep_config->hibernate_modes, sleep_config->hibernate_states);
248 if (r < 0) {
249 log_notice("Couldn't hibernate, will try to suspend again.");
250 r = execute(sleep_config->suspend_modes, sleep_config->suspend_states);
251 if (r < 0) {
252 log_notice("Could neither hibernate nor suspend again, giving up.");
253 return r;
254 }
255 }
256
257 return 0;
258 }
259
260 static int help(void) {
261 _cleanup_free_ char *link = NULL;
262 int r;
263
264 r = terminal_urlify_man("systemd-suspend.service", "8", &link);
265 if (r < 0)
266 return log_oom();
267
268 printf("%s COMMAND\n\n"
269 "Suspend the system, hibernate the system, or both.\n\n"
270 " -h --help Show this help and exit\n"
271 " --version Print version string and exit\n"
272 "\nCommands:\n"
273 " suspend Suspend the system\n"
274 " hibernate Hibernate the system\n"
275 " hybrid-sleep Both hibernate and suspend the system\n"
276 " suspend-then-hibernate Initially suspend and then hibernate\n"
277 " the system after a fixed period of time\n"
278 "\nSee the %s for details.\n"
279 , program_invocation_short_name
280 , link
281 );
282
283 return 0;
284 }
285
286 static int parse_argv(int argc, char *argv[]) {
287 enum {
288 ARG_VERSION = 0x100,
289 };
290
291 static const struct option options[] = {
292 { "help", no_argument, NULL, 'h' },
293 { "version", no_argument, NULL, ARG_VERSION },
294 {}
295 };
296
297 int c;
298
299 assert(argc >= 0);
300 assert(argv);
301
302 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
303 switch(c) {
304 case 'h':
305 return help();
306
307 case ARG_VERSION:
308 return version();
309
310 case '?':
311 return -EINVAL;
312
313 default:
314 assert_not_reached("Unhandled option");
315 }
316
317 if (argc - optind != 1)
318 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
319 "Usage: %s COMMAND",
320 program_invocation_short_name);
321
322 arg_verb = strdup(argv[optind]);
323 if (!arg_verb)
324 return log_oom();
325
326 if (!STR_IN_SET(arg_verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"))
327 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
328 "Unknown command '%s'.", arg_verb);
329
330 return 1 /* work to do */;
331 }
332
333 static int run(int argc, char *argv[]) {
334 bool allow;
335 char **modes = NULL, **states = NULL;
336 _cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL;
337 int r;
338
339 log_setup_service();
340
341 r = parse_argv(argc, argv);
342 if (r <= 0)
343 return r;
344
345 r = parse_sleep_config(&sleep_config);
346 if (r < 0)
347 return r;
348
349 r = sleep_settings(arg_verb, sleep_config, &allow, &modes, &states);
350 if (r < 0)
351 return r;
352
353 if (!allow)
354 return log_error_errno(SYNTHETIC_ERRNO(EACCES),
355 "Sleep mode \"%s\" is disabled by configuration, refusing.",
356 arg_verb);
357
358 if (streq(arg_verb, "suspend-then-hibernate"))
359 return execute_s2h(sleep_config);
360 else
361 return execute(modes, states);
362 }
363
364 DEFINE_MAIN_FUNCTION(run);