diff options
Diffstat (limited to 'src/sleep/sleep.c')
-rw-r--r-- | src/sleep/sleep.c | 246 |
1 files changed, 136 insertions, 110 deletions
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index f26aa453c9..5b7984a6f2 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -11,12 +11,14 @@ #include "sd-messages.h" -#include "parse-util.h" #include "def.h" #include "exec-util.h" #include "fd-util.h" #include "fileio.h" #include "log.h" +#include "main-func.h" +#include "parse-util.h" +#include "pretty-print.h" #include "sleep-config.h" #include "stdio-util.h" #include "string-util.h" @@ -40,22 +42,26 @@ static int write_hibernate_location_info(void) { return log_debug_errno(r, "Unable to find hibernation location: %m"); /* if it's a swap partition, we just write the disk to /sys/power/resume */ - if (streq(type, "partition")) - return write_string_file("/sys/power/resume", device, 0); - else if (!streq(type, "file")) - return log_debug_errno(EINVAL, "Invalid hibernate type %s: %m", - type); + if (streq(type, "partition")) { + r = write_string_file("/sys/power/resume", device, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_debug_errno(r, "Faileed to write partitoin device to /sys/power/resume: %m"); + + return r; + } + if (!streq(type, "file")) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid hibernate type: %s", type); /* Only available in 4.17+ */ - if (access("/sys/power/resume_offset", F_OK) < 0) { - if (errno == ENOENT) + if (access("/sys/power/resume_offset", W_OK) < 0) { + if (errno == ENOENT) { + log_debug("Kernel too old, can't configure resume offset, ignoring."); return 0; - return log_debug_errno(errno, "/sys/power/resume_offset unavailable: %m"); - } + } - r = access("/sys/power/resume_offset", W_OK); - if (r < 0) return log_debug_errno(errno, "/sys/power/resume_offset not writeable: %m"); + } fd = open(device, O_RDONLY | O_CLOEXEC | O_NONBLOCK); if (fd < 0) @@ -63,26 +69,25 @@ static int write_hibernate_location_info(void) { r = fstat(fd, &stb); if (r < 0) return log_debug_errno(errno, "Unable to stat %s: %m", device); + r = read_fiemap(fd, &fiemap); if (r < 0) - return log_debug_errno(r, "Unable to read extent map for '%s': %m", - device); - if (fiemap->fm_mapped_extents == 0) { - log_debug("No extents found in '%s'", device); - return -EINVAL; - } + return log_debug_errno(r, "Unable to read extent map for '%s': %m", device); + if (fiemap->fm_mapped_extents == 0) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "No extents found in '%s'", device); + offset = fiemap->fm_extents[0].fe_physical / page_size(); xsprintf(offset_str, "%" PRIu64, offset); - r = write_string_file("/sys/power/resume_offset", offset_str, 0); + r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) - return log_debug_errno(r, "Failed to write offset '%s': %m", - offset_str); + return log_debug_errno(r, "Failed to write offset '%s': %m", offset_str); xsprintf(device_str, "%lx", (unsigned long)stb.st_dev); - r = write_string_file("/sys/power/resume", device_str, 0); + r = write_string_file("/sys/power/resume", device_str, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) - return log_debug_errno(r, "Failed to write device '%s': %m", - device_str); + return log_debug_errno(r, "Failed to write device '%s': %m", device_str); + return 0; } @@ -93,13 +98,12 @@ static int write_mode(char **modes) { STRV_FOREACH(mode, modes) { int k; - k = write_string_file("/sys/power/disk", *mode, 0); - if (k == 0) + k = write_string_file("/sys/power/disk", *mode, WRITE_STRING_FILE_DISABLE_BUFFER); + if (k >= 0) return 0; - log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", - *mode); - if (r == 0) + log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", *mode); + if (r >= 0) r = k; } @@ -113,12 +117,11 @@ static int write_state(FILE **f, char **states) { STRV_FOREACH(state, states) { int k; - k = write_string_stream(*f, *state, 0); - if (k == 0) + k = write_string_stream(*f, *state, WRITE_STRING_FILE_DISABLE_BUFFER); + if (k >= 0) return 0; - log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", - *state); - if (r == 0) + log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", *state); + if (r >= 0) r = k; fclose(*f); @@ -131,7 +134,6 @@ static int write_state(FILE **f, char **states) { } static int execute(char **modes, char **states) { - char *arguments[] = { NULL, (char*) "pre", @@ -152,6 +154,8 @@ static int execute(char **modes, char **states) { if (!f) return log_error_errno(errno, "Failed to open /sys/power/state: %m"); + setvbuf(f, NULL, _IONBF, 0); + /* Configure the hibernation mode */ if (!strv_isempty(modes)) { r = write_hibernate_location_info(); @@ -162,7 +166,7 @@ static int execute(char **modes, char **states) { return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");; } - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL); log_struct(LOG_INFO, "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR, @@ -171,39 +175,46 @@ static int execute(char **modes, char **states) { r = write_state(&f, states); if (r < 0) - return log_error_errno(r, "Failed to write /sys/power/state: %m"); - - log_struct(LOG_INFO, - "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR, - LOG_MESSAGE("System resumed."), - "SLEEP=%s", arg_verb); + log_struct_errno(LOG_ERR, r, + "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR, + LOG_MESSAGE("Failed to suspend system. System resumed again: %m"), + "SLEEP=%s", arg_verb); + else + log_struct(LOG_INFO, + "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR, + LOG_MESSAGE("System resumed."), + "SLEEP=%s", arg_verb); arguments[1] = (char*) "post"; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL); return r; } -static int read_wakealarm(uint64_t *result) { +static int rtc_read_time(uint64_t *ret_sec) { _cleanup_free_ char *t = NULL; + int r; - if (read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t) >= 0) - return safe_atou64(t, result); - return -EBADF; -} + r = read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t); + if (r < 0) + return log_error_errno(r, "Failed to read RTC time: %m"); + + r = safe_atou64(t, ret_sec); + if (r < 0) + return log_error_errno(r, "Failed to parse RTC time '%s': %m", t); -static int write_wakealarm(const char *str) { + return 0; +} - _cleanup_fclose_ FILE *f = NULL; +static int rtc_write_wake_alarm(uint64_t sec) { + char buf[DECIMAL_STR_MAX(uint64_t)]; int r; - f = fopen("/sys/class/rtc/rtc0/wakealarm", "we"); - if (!f) - return log_error_errno(errno, "Failed to open /sys/class/rtc/rtc0/wakealarm: %m"); + xsprintf(buf, "%" PRIu64, sec); - r = write_string_stream(f, str, 0); + r = write_string_file("/sys/class/rtc/rtc0/wakealarm", buf, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) - return log_error_errno(r, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", str); + return log_error_errno(r, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", buf); return 0; } @@ -212,67 +223,84 @@ static int execute_s2h(usec_t hibernate_delay_sec) { _cleanup_strv_free_ char **hibernate_modes = NULL, **hibernate_states = NULL, **suspend_modes = NULL, **suspend_states = NULL; - usec_t orig_time, cmp_time; - char time_str[DECIMAL_STR_MAX(uint64_t)]; + usec_t original_time, wake_time, cmp_time; int r; - r = parse_sleep_config("suspend", &suspend_modes, &suspend_states, - NULL); + r = parse_sleep_config("suspend", NULL, &suspend_modes, &suspend_states, NULL); if (r < 0) return r; - r = parse_sleep_config("hibernate", &hibernate_modes, - &hibernate_states, NULL); + r = parse_sleep_config("hibernate", NULL, &hibernate_modes, &hibernate_states, NULL); if (r < 0) return r; - r = read_wakealarm(&orig_time); + r = rtc_read_time(&original_time); if (r < 0) - return log_error_errno(errno, "Failed to read time: %d", r); - - orig_time += hibernate_delay_sec / USEC_PER_SEC; - xsprintf(time_str, "%" PRIu64, orig_time); + return r; - r = write_wakealarm(time_str); + wake_time = original_time + DIV_ROUND_UP(hibernate_delay_sec, USEC_PER_SEC); + r = rtc_write_wake_alarm(wake_time); if (r < 0) return r; - log_debug("Set RTC wake alarm for %s", time_str); + log_debug("Set RTC wake alarm for %" PRIu64, wake_time); r = execute(suspend_modes, suspend_states); if (r < 0) return r; - r = read_wakealarm(&cmp_time); + /* Reset RTC right-away */ + r = rtc_write_wake_alarm(0); if (r < 0) - return log_error_errno(errno, "Failed to read time: %d", r); + return r; - /* reset RTC */ - r = write_wakealarm("0"); + r = rtc_read_time(&cmp_time); if (r < 0) return r; log_debug("Woke up at %"PRIu64, cmp_time); - /* if woken up after alarm time, hibernate */ - if (cmp_time >= orig_time) - r = execute(hibernate_modes, hibernate_states); + if (cmp_time < wake_time) /* We woke up before the alarm time, we are done. */ + return 0; + + /* If woken up after alarm time, hibernate */ + r = execute(hibernate_modes, hibernate_states); + if (r < 0) { + log_notice("Couldn't hibernate, will try to suspend again."); + r = execute(suspend_modes, suspend_states); + if (r < 0) { + log_notice("Could neither hibernate nor suspend again, giving up."); + return r; + } + } - return r; + return 0; } -static void help(void) { +static int help(void) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("systemd-suspend.service", "8", &link); + if (r < 0) + return log_oom(); + printf("%s COMMAND\n\n" "Suspend the system, hibernate the system, or both.\n\n" - "Commands:\n" - " -h --help Show this help and exit\n" - " --version Print version string and exit\n" - " suspend Suspend the system\n" - " hibernate Hibernate the system\n" - " hybrid-sleep Both hibernate and suspend the system\n" + " -h --help Show this help and exit\n" + " --version Print version string and exit\n" + "\nCommands:\n" + " suspend Suspend the system\n" + " hibernate Hibernate the system\n" + " hybrid-sleep Both hibernate and suspend the system\n" " suspend-then-hibernate Initially suspend and then hibernate\n" - " the system after a fixed period of time\n" - , program_invocation_short_name); + " the system after a fixed period of time\n" + "\nSee the %s for details.\n" + , program_invocation_short_name + , link + ); + + return 0; } static int parse_argv(int argc, char *argv[]) { @@ -294,8 +322,7 @@ static int parse_argv(int argc, char *argv[]) { while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) switch(c) { case 'h': - help(); - return 0; /* done */ + return help(); case ARG_VERSION: return version(); @@ -307,46 +334,45 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (argc - optind != 1) { - log_error("Usage: %s COMMAND", - program_invocation_short_name); - return -EINVAL; - } + if (argc - optind != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Usage: %s COMMAND", + program_invocation_short_name); arg_verb = argv[optind]; - if (!streq(arg_verb, "suspend") && - !streq(arg_verb, "hibernate") && - !streq(arg_verb, "hybrid-sleep") && - !streq(arg_verb, "suspend-then-hibernate")) { - log_error("Unknown command '%s'.", arg_verb); - return -EINVAL; - } + if (!STR_IN_SET(arg_verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate")) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown command '%s'.", arg_verb); return 1 /* work to do */; } -int main(int argc, char *argv[]) { +static int run(int argc, char *argv[]) { + bool allow; _cleanup_strv_free_ char **modes = NULL, **states = NULL; usec_t delay = 0; int r; - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); + log_setup_service(); r = parse_argv(argc, argv); if (r <= 0) - goto finish; + return r; - r = parse_sleep_config(arg_verb, &modes, &states, &delay); + r = parse_sleep_config(arg_verb, &allow, &modes, &states, &delay); if (r < 0) - goto finish; + return r; + + if (!allow) + return log_error_errno(SYNTHETIC_ERRNO(EACCES), + "Sleep mode \"%s\" is disabled by configuration, refusing.", + arg_verb); if (streq(arg_verb, "suspend-then-hibernate")) - r = execute_s2h(delay); + return execute_s2h(delay); else - r = execute(modes, states); -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + return execute(modes, states); } + +DEFINE_MAIN_FUNCTION(run); |