1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2012 Lennart Poettering
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7 Copyright 2018 Dell Inc.
14 #include "sd-messages.h"
16 #include "parse-util.h"
18 #include "exec-util.h"
22 #include "sleep-config.h"
23 #include "stdio-util.h"
24 #include "string-util.h"
28 static char* arg_verb
= NULL
;
30 static int write_mode(char **modes
) {
34 STRV_FOREACH(mode
, modes
) {
37 k
= write_string_file("/sys/power/disk", *mode
, 0);
41 log_debug_errno(k
, "Failed to write '%s' to /sys/power/disk: %m",
48 log_error_errno(r
, "Failed to write mode to /sys/power/disk: %m");
53 static int write_state(FILE **f
, char **states
) {
57 STRV_FOREACH(state
, states
) {
60 k
= write_string_stream(*f
, *state
, 0);
63 log_debug_errno(k
, "Failed to write '%s' to /sys/power/state: %m",
69 *f
= fopen("/sys/power/state", "we");
71 return log_error_errno(errno
, "Failed to open /sys/power/state: %m");
77 static int execute(char **modes
, char **states
) {
85 static const char* const dirs
[] = {
91 _cleanup_fclose_
FILE *f
= NULL
;
93 /* This file is opened first, so that if we hit an error,
94 * we can abort before modifying any state. */
95 f
= fopen("/sys/power/state", "we");
97 return log_error_errno(errno
, "Failed to open /sys/power/state: %m");
99 /* Configure the hibernation mode */
100 r
= write_mode(modes
);
104 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, arguments
);
107 "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR
,
108 LOG_MESSAGE("Suspending system..."),
109 "SLEEP=%s", arg_verb
,
112 r
= write_state(&f
, states
);
117 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR
,
118 LOG_MESSAGE("System resumed."),
119 "SLEEP=%s", arg_verb
,
122 arguments
[1] = (char*) "post";
123 execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, arguments
);
128 static int read_wakealarm(uint64_t *result
) {
129 _cleanup_free_
char *t
= NULL
;
131 if (read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t
) >= 0)
132 return safe_atou64(t
, result
);
136 static int write_wakealarm(const char *str
) {
138 _cleanup_fclose_
FILE *f
= NULL
;
141 f
= fopen("/sys/class/rtc/rtc0/wakealarm", "we");
143 return log_error_errno(errno
, "Failed to open /sys/class/rtc/rtc0/wakealarm: %m");
145 r
= write_string_stream(f
, str
, 0);
147 return log_error_errno(r
, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", str
);
152 static int execute_s2h(usec_t hibernate_delay_sec
) {
154 _cleanup_strv_free_
char **hibernate_modes
= NULL
, **hibernate_states
= NULL
,
155 **suspend_modes
= NULL
, **suspend_states
= NULL
;
156 usec_t orig_time
, cmp_time
;
157 char time_str
[DECIMAL_STR_MAX(uint64_t)];
160 r
= parse_sleep_config("suspend", &suspend_modes
, &suspend_states
,
165 r
= parse_sleep_config("hibernate", &hibernate_modes
,
166 &hibernate_states
, NULL
);
170 r
= read_wakealarm(&orig_time
);
172 return log_error_errno(errno
, "Failed to read time: %d", r
);
174 orig_time
+= hibernate_delay_sec
/ USEC_PER_SEC
;
175 xsprintf(time_str
, "%" PRIu64
, orig_time
);
177 r
= write_wakealarm(time_str
);
181 log_debug("Set RTC wake alarm for %s", time_str
);
183 r
= execute(suspend_modes
, suspend_states
);
187 r
= read_wakealarm(&cmp_time
);
189 return log_error_errno(errno
, "Failed to read time: %d", r
);
192 r
= write_wakealarm("0");
196 log_debug("Woke up at %"PRIu64
, cmp_time
);
198 /* if woken up after alarm time, hibernate */
199 if (cmp_time
>= orig_time
)
200 r
= execute(hibernate_modes
, hibernate_states
);
205 static void help(void) {
206 printf("%s COMMAND\n\n"
207 "Suspend the system, hibernate the system, or both.\n\n"
209 " -h --help Show this help and exit\n"
210 " --version Print version string and exit\n"
211 " suspend Suspend the system\n"
212 " hibernate Hibernate the system\n"
213 " hybrid-sleep Both hibernate and suspend the system\n"
214 " suspend-then-hibernate Initially suspend and then hibernate\n"
215 " the system after a fixed period of time\n"
216 , program_invocation_short_name
);
219 static int parse_argv(int argc
, char *argv
[]) {
224 static const struct option options
[] = {
225 { "help", no_argument
, NULL
, 'h' },
226 { "version", no_argument
, NULL
, ARG_VERSION
},
235 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
248 assert_not_reached("Unhandled option");
251 if (argc
- optind
!= 1) {
252 log_error("Usage: %s COMMAND",
253 program_invocation_short_name
);
257 arg_verb
= argv
[optind
];
259 if (!streq(arg_verb
, "suspend") &&
260 !streq(arg_verb
, "hibernate") &&
261 !streq(arg_verb
, "hybrid-sleep") &&
262 !streq(arg_verb
, "suspend-then-hibernate")) {
263 log_error("Unknown command '%s'.", arg_verb
);
267 return 1 /* work to do */;
270 int main(int argc
, char *argv
[]) {
271 _cleanup_strv_free_
char **modes
= NULL
, **states
= NULL
;
275 log_set_target(LOG_TARGET_AUTO
);
276 log_parse_environment();
279 r
= parse_argv(argc
, argv
);
283 r
= parse_sleep_config(arg_verb
, &modes
, &states
, &delay
);
287 if (streq(arg_verb
, "suspend-then-hibernate"))
288 r
= execute_s2h(delay
);
290 r
= execute(modes
, states
);
292 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;