1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2010-2017 Canonical
4 Copyright © 2018 Dell Inc.
9 #include <linux/fiemap.h>
12 #include "sd-messages.h"
15 #include "exec-util.h"
19 #include "parse-util.h"
20 #include "sleep-config.h"
21 #include "stdio-util.h"
22 #include "string-util.h"
24 #include "terminal-util.h"
27 static char* arg_verb
= NULL
;
29 static 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;
39 r
= find_hibernate_location(&device
, &type
, NULL
, NULL
);
41 return log_debug_errno(r
, "Unable to find hibernation location: %m");
43 /* if it's a swap partition, we just write the disk to /sys/power/resume */
44 if (streq(type
, "partition")) {
45 r
= write_string_file("/sys/power/resume", device
, 0);
47 return log_debug_errno(r
, "Faileed to write partitoin device to /sys/power/resume: %m");
51 if (!streq(type
, "file")) {
52 log_debug("Invalid hibernate type: %s", type
);
56 /* Only available in 4.17+ */
57 if (access("/sys/power/resume_offset", F_OK
) < 0) {
61 return log_debug_errno(errno
, "/sys/power/resume_offset unavailable: %m");
64 if (access("/sys/power/resume_offset", W_OK
) < 0)
65 return log_debug_errno(errno
, "/sys/power/resume_offset not writeable: %m");
67 fd
= open(device
, O_RDONLY
| O_CLOEXEC
| O_NONBLOCK
);
69 return log_debug_errno(errno
, "Unable to open '%s': %m", device
);
72 return log_debug_errno(errno
, "Unable to stat %s: %m", device
);
74 r
= read_fiemap(fd
, &fiemap
);
76 return log_debug_errno(r
, "Unable to read extent map for '%s': %m", device
);
77 if (fiemap
->fm_mapped_extents
== 0) {
78 log_debug("No extents found in '%s'", device
);
82 offset
= fiemap
->fm_extents
[0].fe_physical
/ page_size();
83 xsprintf(offset_str
, "%" PRIu64
, offset
);
84 r
= write_string_file("/sys/power/resume_offset", offset_str
, 0);
86 return log_debug_errno(r
, "Failed to write offset '%s': %m", offset_str
);
88 xsprintf(device_str
, "%lx", (unsigned long)stb
.st_dev
);
89 r
= write_string_file("/sys/power/resume", device_str
, 0);
91 return log_debug_errno(r
, "Failed to write device '%s': %m", device_str
);
96 static int write_mode(char **modes
) {
100 STRV_FOREACH(mode
, modes
) {
103 k
= write_string_file("/sys/power/disk", *mode
, 0);
107 log_debug_errno(k
, "Failed to write '%s' to /sys/power/disk: %m", *mode
);
115 static int write_state(FILE **f
, char **states
) {
119 STRV_FOREACH(state
, states
) {
122 k
= write_string_stream(*f
, *state
, 0);
125 log_debug_errno(k
, "Failed to write '%s' to /sys/power/state: %m", *state
);
130 *f
= fopen("/sys/power/state", "we");
138 static int execute(char **modes
, char **states
) {
140 char *arguments
[] = {
146 static const char* const dirs
[] = {
152 _cleanup_fclose_
FILE *f
= NULL
;
154 /* This file is opened first, so that if we hit an error,
155 * we can abort before modifying any state. */
156 f
= fopen("/sys/power/state", "we");
158 return log_error_errno(errno
, "Failed to open /sys/power/state: %m");
160 /* Configure the hibernation mode */
161 if (!strv_isempty(modes
)) {
162 r
= write_hibernate_location_info();
164 return log_error_errno(r
, "Failed to write hibernation disk offset: %m");
165 r
= write_mode(modes
);
167 return log_error_errno(r
, "Failed to write mode to /sys/power/disk: %m");;
170 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, arguments
, NULL
);
173 "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR
,
174 LOG_MESSAGE("Suspending system..."),
175 "SLEEP=%s", arg_verb
);
177 r
= write_state(&f
, states
);
179 return log_error_errno(r
, "Failed to write /sys/power/state: %m");
182 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR
,
183 LOG_MESSAGE("System resumed."),
184 "SLEEP=%s", arg_verb
);
186 arguments
[1] = (char*) "post";
187 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, arguments
, NULL
);
192 static int read_wakealarm(uint64_t *result
) {
193 _cleanup_free_
char *t
= NULL
;
195 if (read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t
) >= 0)
196 return safe_atou64(t
, result
);
200 static int write_wakealarm(const char *str
) {
202 _cleanup_fclose_
FILE *f
= NULL
;
205 f
= fopen("/sys/class/rtc/rtc0/wakealarm", "we");
207 return log_error_errno(errno
, "Failed to open /sys/class/rtc/rtc0/wakealarm: %m");
209 r
= write_string_stream(f
, str
, 0);
211 return log_error_errno(r
, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", str
);
216 static int execute_s2h(usec_t hibernate_delay_sec
) {
218 _cleanup_strv_free_
char **hibernate_modes
= NULL
, **hibernate_states
= NULL
,
219 **suspend_modes
= NULL
, **suspend_states
= NULL
;
220 usec_t orig_time
, cmp_time
;
221 char time_str
[DECIMAL_STR_MAX(uint64_t)];
224 r
= parse_sleep_config("suspend", &suspend_modes
, &suspend_states
,
229 r
= parse_sleep_config("hibernate", &hibernate_modes
,
230 &hibernate_states
, NULL
);
234 r
= read_wakealarm(&orig_time
);
236 return log_error_errno(errno
, "Failed to read time: %d", r
);
238 orig_time
+= hibernate_delay_sec
/ USEC_PER_SEC
;
239 xsprintf(time_str
, "%" PRIu64
, orig_time
);
241 r
= write_wakealarm(time_str
);
245 log_debug("Set RTC wake alarm for %s", time_str
);
247 r
= execute(suspend_modes
, suspend_states
);
251 r
= read_wakealarm(&cmp_time
);
253 return log_error_errno(errno
, "Failed to read time: %d", r
);
256 r
= write_wakealarm("0");
260 log_debug("Woke up at %"PRIu64
, cmp_time
);
262 /* if woken up after alarm time, hibernate */
263 if (cmp_time
>= orig_time
)
264 r
= execute(hibernate_modes
, hibernate_states
);
269 static int help(void) {
270 _cleanup_free_
char *link
= NULL
;
273 r
= terminal_urlify_man("systemd-suspend.service", "8", &link
);
277 printf("%s COMMAND\n\n"
278 "Suspend the system, hibernate the system, or both.\n\n"
279 " -h --help Show this help and exit\n"
280 " --version Print version string and exit\n"
282 " suspend Suspend the system\n"
283 " hibernate Hibernate the system\n"
284 " hybrid-sleep Both hibernate and suspend the system\n"
285 " suspend-then-hibernate Initially suspend and then hibernate\n"
286 " the system after a fixed period of time\n"
287 "\nSee the %s for details.\n"
288 , program_invocation_short_name
295 static int parse_argv(int argc
, char *argv
[]) {
300 static const struct option options
[] = {
301 { "help", no_argument
, NULL
, 'h' },
302 { "version", no_argument
, NULL
, ARG_VERSION
},
311 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
323 assert_not_reached("Unhandled option");
326 if (argc
- optind
!= 1) {
327 log_error("Usage: %s COMMAND",
328 program_invocation_short_name
);
332 arg_verb
= argv
[optind
];
334 if (!STR_IN_SET(arg_verb
, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")) {
335 log_error("Unknown command '%s'.", arg_verb
);
339 return 1 /* work to do */;
342 int main(int argc
, char *argv
[]) {
343 _cleanup_strv_free_
char **modes
= NULL
, **states
= NULL
;
347 log_set_target(LOG_TARGET_AUTO
);
348 log_parse_environment();
351 r
= parse_argv(argc
, argv
);
355 r
= parse_sleep_config(arg_verb
, &modes
, &states
, &delay
);
359 if (streq(arg_verb
, "suspend-then-hibernate"))
360 r
= execute_s2h(delay
);
362 r
= execute(modes
, states
);
365 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;