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 /// Additional includes needed by elogind
31 static char* arg_verb = NULL;
33 static int write_mode(char **modes) {
37 STRV_FOREACH(mode, modes) {
40 k = write_string_file("/sys/power/disk", *mode, 0);
44 log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m",
51 log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");
56 static int write_state(FILE **f, char **states) {
60 STRV_FOREACH(state, states) {
63 k = write_string_stream(*f, *state, 0);
66 log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m",
72 *f = fopen("/sys/power/state", "we");
74 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
80 static int execute(char **modes, char **states) {
88 static const char* const dirs[] = {
94 _cleanup_fclose_ FILE *f = NULL;
96 /* This file is opened first, so that if we hit an error,
97 * we can abort before modifying any state. */
98 f = fopen("/sys/power/state", "we");
100 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
102 /* Configure the hibernation mode */
103 r = write_mode(modes);
107 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
110 "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
111 LOG_MESSAGE("Suspending system..."),
112 "SLEEP=%s", arg_verb,
115 r = write_state(&f, states);
120 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
121 LOG_MESSAGE("System resumed."),
122 "SLEEP=%s", arg_verb,
125 arguments[1] = (char*) "post";
126 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments);
131 static int read_wakealarm(uint64_t *result) {
132 _cleanup_free_ char *t = NULL;
134 if (read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t) >= 0)
135 return safe_atou64(t, result);
139 static int write_wakealarm(const char *str) {
141 _cleanup_fclose_ FILE *f = NULL;
144 f = fopen("/sys/class/rtc/rtc0/wakealarm", "we");
146 return log_error_errno(errno, "Failed to open /sys/class/rtc/rtc0/wakealarm: %m");
148 r = write_string_stream(f, str, 0);
150 return log_error_errno(r, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", str);
155 static int execute_s2h(usec_t hibernate_delay_sec) {
157 _cleanup_strv_free_ char **hibernate_modes = NULL, **hibernate_states = NULL,
158 **suspend_modes = NULL, **suspend_states = NULL;
159 usec_t orig_time, cmp_time;
160 char time_str[DECIMAL_STR_MAX(uint64_t)];
163 r = parse_sleep_config("suspend", &suspend_modes, &suspend_states,
168 r = parse_sleep_config("hibernate", &hibernate_modes,
169 &hibernate_states, NULL);
173 r = read_wakealarm(&orig_time);
175 return log_error_errno(errno, "Failed to read time: %d", r);
177 orig_time += hibernate_delay_sec / USEC_PER_SEC;
178 xsprintf(time_str, "%" PRIu64, orig_time);
180 r = write_wakealarm(time_str);
184 log_debug("Set RTC wake alarm for %s", time_str);
186 r = execute(suspend_modes, suspend_states);
190 r = read_wakealarm(&cmp_time);
192 return log_error_errno(errno, "Failed to read time: %d", r);
195 r = write_wakealarm("0");
199 log_debug("Woke up at %"PRIu64, cmp_time);
201 /* if woken up after alarm time, hibernate */
202 if (cmp_time >= orig_time)
203 r = execute(hibernate_modes, hibernate_states);
208 #if 0 /// elogind calls execute() by itself and does not need another binary
209 static void help(void) {
210 printf("%s COMMAND\n\n"
211 "Suspend the system, hibernate the system, or both.\n\n"
213 " -h --help Show this help and exit\n"
214 " --version Print version string and exit\n"
215 " suspend Suspend the system\n"
216 " hibernate Hibernate the system\n"
217 " hybrid-sleep Both hibernate and suspend the system\n"
218 " suspend-then-hibernate Initially suspend and then hibernate\n"
219 " the system after a fixed period of time\n"
220 , program_invocation_short_name);
223 static int parse_argv(int argc, char *argv[]) {
228 static const struct option options[] = {
229 { "help", no_argument, NULL, 'h' },
230 { "version", no_argument, NULL, ARG_VERSION },
239 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
252 assert_not_reached("Unhandled option");
255 if (argc - optind != 1) {
256 log_error("Usage: %s COMMAND",
257 program_invocation_short_name);
261 arg_verb = argv[optind];
263 if (!streq(arg_verb, "suspend") &&
264 !streq(arg_verb, "hibernate") &&
265 !streq(arg_verb, "hybrid-sleep") &&
266 !streq(arg_verb, "suspend-then-hibernate")) {
267 log_error("Unknown command '%s'.", arg_verb);
271 return 1 /* work to do */;
274 int main(int argc, char *argv[]) {
275 _cleanup_strv_free_ char **modes = NULL, **states = NULL;
279 log_set_target(LOG_TARGET_AUTO);
280 log_parse_environment();
283 r = parse_argv(argc, argv);
287 r = parse_sleep_config(arg_verb, &modes, &states, &delay);
291 if (streq(arg_verb, "suspend-then-hibernate"))
292 r = execute_s2h(delay);
294 r = execute(modes, states);
296 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
299 int do_sleep(const char *verb, char **modes, char **states) {
301 arg_verb = (char*)verb;
302 return execute(modes, states);