]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/sleep/sleep.c
tree-wide: remove Lennart's copyright lines
[thirdparty/systemd.git] / src / sleep / sleep.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
6edd7d0a 2/***
19adb8a3 3 Copyright 2013 Zbigniew Jędrzejewski-Szmek
17c40b3a 4 Copyright 2010-2017 Canonical
c58493c0 5 Copyright 2018 Dell Inc.
6edd7d0a
LP
6***/
7
6edd7d0a 8#include <errno.h>
19adb8a3 9#include <getopt.h>
17c40b3a 10#include <linux/fiemap.h>
3f6fd1ba 11#include <stdio.h>
6edd7d0a 12
aa62a893 13#include "sd-messages.h"
3f6fd1ba 14
c58493c0 15#include "parse-util.h"
3f6fd1ba 16#include "def.h"
89711996 17#include "exec-util.h"
3ffd4af2 18#include "fd-util.h"
a5c32cff 19#include "fileio.h"
3f6fd1ba 20#include "log.h"
19adb8a3 21#include "sleep-config.h"
c58493c0 22#include "stdio-util.h"
07630cea 23#include "string-util.h"
3f6fd1ba
LP
24#include "strv.h"
25#include "util.h"
19adb8a3
ZJS
26
27static char* arg_verb = NULL;
28
17c40b3a
ML
29static int write_hibernate_location_info(void) {
30 _cleanup_free_ char *device = NULL, *type = NULL;
31 _cleanup_free_ struct fiemap *fiemap = NULL;
32 char offset_str[DECIMAL_STR_MAX(uint64_t)];
33 char device_str[DECIMAL_STR_MAX(uint64_t)];
34 _cleanup_close_ int fd = -1;
35 struct stat stb;
36 uint64_t offset;
37 int r;
38
39 r = find_hibernate_location(&device, &type, NULL, NULL);
40 if (r < 0)
41 return log_debug_errno(r, "Unable to find hibernation location: %m");
42
43 /* if it's a swap partition, we just write the disk to /sys/power/resume */
44 if (streq(type, "partition"))
45 return write_string_file("/sys/power/resume", device, 0);
46 else if (!streq(type, "file"))
47 return log_debug_errno(EINVAL, "Invalid hibernate type %s: %m",
48 type);
49
50 /* Only available in 4.17+ */
51 if (access("/sys/power/resume_offset", F_OK) < 0) {
52 if (errno == ENOENT)
53 return 0;
54 return log_debug_errno(errno, "/sys/power/resume_offset unavailable: %m");
55 }
56
57 r = access("/sys/power/resume_offset", W_OK);
58 if (r < 0)
59 return log_debug_errno(errno, "/sys/power/resume_offset not writeable: %m");
60
61 fd = open(device, O_RDONLY | O_CLOEXEC | O_NONBLOCK);
62 if (fd < 0)
63 return log_debug_errno(errno, "Unable to open '%s': %m", device);
64 r = fstat(fd, &stb);
65 if (r < 0)
66 return log_debug_errno(errno, "Unable to stat %s: %m", device);
67 r = read_fiemap(fd, &fiemap);
68 if (r < 0)
69 return log_debug_errno(r, "Unable to read extent map for '%s': %m",
70 device);
71 if (fiemap->fm_mapped_extents == 0) {
72 log_debug("No extents found in '%s'", device);
73 return -EINVAL;
74 }
75 offset = fiemap->fm_extents[0].fe_physical / page_size();
76 xsprintf(offset_str, "%" PRIu64, offset);
77 r = write_string_file("/sys/power/resume_offset", offset_str, 0);
78 if (r < 0)
79 return log_debug_errno(r, "Failed to write offset '%s': %m",
80 offset_str);
81
82 xsprintf(device_str, "%lx", (unsigned long)stb.st_dev);
83 r = write_string_file("/sys/power/resume", device_str, 0);
84 if (r < 0)
85 return log_debug_errno(r, "Failed to write device '%s': %m",
86 device_str);
87 return 0;
88}
89
19adb8a3
ZJS
90static int write_mode(char **modes) {
91 int r = 0;
92 char **mode;
93
94 STRV_FOREACH(mode, modes) {
aa62a893
LP
95 int k;
96
4c1fc3e4 97 k = write_string_file("/sys/power/disk", *mode, 0);
19adb8a3
ZJS
98 if (k == 0)
99 return 0;
aa62a893 100
c33b3297
MS
101 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m",
102 *mode);
19adb8a3
ZJS
103 if (r == 0)
104 r = k;
105 }
6edd7d0a 106
19adb8a3
ZJS
107 return r;
108}
6edd7d0a 109
2fd069b1 110static int write_state(FILE **f, char **states) {
19adb8a3
ZJS
111 char **state;
112 int r = 0;
113
114 STRV_FOREACH(state, states) {
115 int k;
116
b1837133 117 k = write_string_stream(*f, *state, 0);
19adb8a3
ZJS
118 if (k == 0)
119 return 0;
c33b3297
MS
120 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
121 *state);
19adb8a3
ZJS
122 if (r == 0)
123 r = k;
124
2fd069b1
ZJS
125 fclose(*f);
126 *f = fopen("/sys/power/state", "we");
4a62c710 127 if (!*f)
0ca906ac 128 return -errno;
6edd7d0a
LP
129 }
130
19adb8a3
ZJS
131 return r;
132}
6edd7d0a 133
19adb8a3 134static int execute(char **modes, char **states) {
e2cc6eca
LP
135
136 char *arguments[] = {
137 NULL,
138 (char*) "pre",
139 arg_verb,
140 NULL
141 };
b5084605
LP
142 static const char* const dirs[] = {
143 SYSTEM_SLEEP_PATH,
144 NULL
145 };
e2cc6eca 146
19adb8a3 147 int r;
2fd069b1 148 _cleanup_fclose_ FILE *f = NULL;
6524990f 149
19adb8a3 150 /* This file is opened first, so that if we hit an error,
09692409 151 * we can abort before modifying any state. */
6edd7d0a 152 f = fopen("/sys/power/state", "we");
4a62c710
MS
153 if (!f)
154 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
6edd7d0a 155
19adb8a3 156 /* Configure the hibernation mode */
17c40b3a
ML
157 if (!strv_isempty(modes)) {
158 r = write_hibernate_location_info();
159 if (r < 0)
160 return log_error_errno(r, "Failed to write hibernation disk offset: %m");
161 r = write_mode(modes);
162 if (r < 0)
0ca906ac 163 return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;
17c40b3a 164 }
19adb8a3 165
c6e47247 166 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
6edd7d0a 167
19adb8a3 168 log_struct(LOG_INFO,
2b044526 169 "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
e2cc6eca 170 LOG_MESSAGE("Suspending system..."),
a1230ff9 171 "SLEEP=%s", arg_verb);
19adb8a3 172
2fd069b1 173 r = write_state(&f, states);
19adb8a3 174 if (r < 0)
0ca906ac 175 return log_error_errno(r, "Failed to write /sys/power/state: %m");
19adb8a3
ZJS
176
177 log_struct(LOG_INFO,
2b044526 178 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
a5ccdb98 179 LOG_MESSAGE("System resumed."),
a1230ff9 180 "SLEEP=%s", arg_verb);
eb267289 181
6edd7d0a 182 arguments[1] = (char*) "post";
c6e47247 183 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
6edd7d0a 184
19adb8a3
ZJS
185 return r;
186}
6edd7d0a 187
c58493c0
ML
188static int read_wakealarm(uint64_t *result) {
189 _cleanup_free_ char *t = NULL;
190
191 if (read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t) >= 0)
192 return safe_atou64(t, result);
193 return -EBADF;
194}
195
196static int write_wakealarm(const char *str) {
197
198 _cleanup_fclose_ FILE *f = NULL;
199 int r;
200
201 f = fopen("/sys/class/rtc/rtc0/wakealarm", "we");
202 if (!f)
203 return log_error_errno(errno, "Failed to open /sys/class/rtc/rtc0/wakealarm: %m");
204
205 r = write_string_stream(f, str, 0);
206 if (r < 0)
207 return log_error_errno(r, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", str);
208
209 return 0;
210}
211
212static int execute_s2h(usec_t hibernate_delay_sec) {
213
214 _cleanup_strv_free_ char **hibernate_modes = NULL, **hibernate_states = NULL,
215 **suspend_modes = NULL, **suspend_states = NULL;
216 usec_t orig_time, cmp_time;
217 char time_str[DECIMAL_STR_MAX(uint64_t)];
218 int r;
219
220 r = parse_sleep_config("suspend", &suspend_modes, &suspend_states,
221 NULL);
222 if (r < 0)
223 return r;
224
225 r = parse_sleep_config("hibernate", &hibernate_modes,
226 &hibernate_states, NULL);
227 if (r < 0)
228 return r;
229
230 r = read_wakealarm(&orig_time);
231 if (r < 0)
232 return log_error_errno(errno, "Failed to read time: %d", r);
233
234 orig_time += hibernate_delay_sec / USEC_PER_SEC;
235 xsprintf(time_str, "%" PRIu64, orig_time);
236
237 r = write_wakealarm(time_str);
238 if (r < 0)
239 return r;
240
241 log_debug("Set RTC wake alarm for %s", time_str);
242
243 r = execute(suspend_modes, suspend_states);
244 if (r < 0)
245 return r;
246
247 r = read_wakealarm(&cmp_time);
248 if (r < 0)
249 return log_error_errno(errno, "Failed to read time: %d", r);
250
251 /* reset RTC */
252 r = write_wakealarm("0");
253 if (r < 0)
254 return r;
255
256 log_debug("Woke up at %"PRIu64, cmp_time);
257
258 /* if woken up after alarm time, hibernate */
259 if (cmp_time >= orig_time)
260 r = execute(hibernate_modes, hibernate_states);
261
262 return r;
263}
264
601185b4 265static void help(void) {
19adb8a3
ZJS
266 printf("%s COMMAND\n\n"
267 "Suspend the system, hibernate the system, or both.\n\n"
268 "Commands:\n"
269 " -h --help Show this help and exit\n"
270 " --version Print version string and exit\n"
271 " suspend Suspend the system\n"
272 " hibernate Hibernate the system\n"
273 " hybrid-sleep Both hibernate and suspend the system\n"
e68c79db 274 " suspend-then-hibernate Initially suspend and then hibernate\n"
c58493c0 275 " the system after a fixed period of time\n"
601185b4 276 , program_invocation_short_name);
19adb8a3 277}
6edd7d0a 278
19adb8a3
ZJS
279static int parse_argv(int argc, char *argv[]) {
280 enum {
281 ARG_VERSION = 0x100,
282 };
283
284 static const struct option options[] = {
285 { "help", no_argument, NULL, 'h' },
286 { "version", no_argument, NULL, ARG_VERSION },
eb9da376 287 {}
19adb8a3
ZJS
288 };
289
290 int c;
291
292 assert(argc >= 0);
293 assert(argv);
294
601185b4 295 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
19adb8a3
ZJS
296 switch(c) {
297 case 'h':
601185b4
ZJS
298 help();
299 return 0; /* done */
19adb8a3
ZJS
300
301 case ARG_VERSION:
3f6fd1ba 302 return version();
19adb8a3
ZJS
303
304 case '?':
305 return -EINVAL;
306
307 default:
eb9da376 308 assert_not_reached("Unhandled option");
19adb8a3
ZJS
309 }
310
311 if (argc - optind != 1) {
312 log_error("Usage: %s COMMAND",
313 program_invocation_short_name);
314 return -EINVAL;
315 }
316
317 arg_verb = argv[optind];
6edd7d0a 318
19adb8a3
ZJS
319 if (!streq(arg_verb, "suspend") &&
320 !streq(arg_verb, "hibernate") &&
c58493c0 321 !streq(arg_verb, "hybrid-sleep") &&
e68c79db 322 !streq(arg_verb, "suspend-then-hibernate")) {
19adb8a3
ZJS
323 log_error("Unknown command '%s'.", arg_verb);
324 return -EINVAL;
325 }
326
327 return 1 /* work to do */;
328}
329
330int main(int argc, char *argv[]) {
331 _cleanup_strv_free_ char **modes = NULL, **states = NULL;
c58493c0 332 usec_t delay = 0;
19adb8a3
ZJS
333 int r;
334
335 log_set_target(LOG_TARGET_AUTO);
336 log_parse_environment();
337 log_open();
338
339 r = parse_argv(argc, argv);
340 if (r <= 0)
341 goto finish;
342
c58493c0 343 r = parse_sleep_config(arg_verb, &modes, &states, &delay);
19adb8a3
ZJS
344 if (r < 0)
345 goto finish;
346
e68c79db 347 if (streq(arg_verb, "suspend-then-hibernate"))
c58493c0
ML
348 r = execute_s2h(delay);
349 else
350 r = execute(modes, states);
19adb8a3
ZJS
351finish:
352 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
6edd7d0a 353}