1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2010-2017 Canonical
4 Copyright © 2018 Dell Inc.
10 #include <linux/fiemap.h>
13 #include <sys/types.h>
14 #include <sys/timerfd.h>
17 #include "sd-messages.h"
19 #include "btrfs-util.h"
20 #include "bus-error.h"
22 #include "exec-util.h"
25 #include "format-util.h"
28 #include "main-func.h"
29 #include "parse-util.h"
30 #include "pretty-print.h"
31 #include "sleep-config.h"
32 #include "stdio-util.h"
33 #include "string-util.h"
35 #include "time-util.h"
38 static char* arg_verb
= NULL
;
40 STATIC_DESTRUCTOR_REGISTER(arg_verb
, freep
);
42 static int write_hibernate_location_info(const HibernateLocation
*hibernate_location
) {
43 char offset_str
[DECIMAL_STR_MAX(uint64_t)];
44 char resume_str
[DECIMAL_STR_MAX(unsigned) * 2 + STRLEN(":")];
47 assert(hibernate_location
);
48 assert(hibernate_location
->swap
);
50 xsprintf(resume_str
, "%u:%u", major(hibernate_location
->devno
), minor(hibernate_location
->devno
));
51 r
= write_string_file("/sys/power/resume", resume_str
, WRITE_STRING_FILE_DISABLE_BUFFER
);
53 return log_debug_errno(r
, "Failed to write partition device to /sys/power/resume for '%s': '%s': %m",
54 hibernate_location
->swap
->device
, resume_str
);
56 log_debug("Wrote resume= value for %s to /sys/power/resume: %s", hibernate_location
->swap
->device
, resume_str
);
58 /* if it's a swap partition, we're done */
59 if (streq(hibernate_location
->swap
->type
, "partition"))
62 if (!streq(hibernate_location
->swap
->type
, "file"))
63 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL
),
64 "Invalid hibernate type: %s", hibernate_location
->swap
->type
);
66 /* Only available in 4.17+ */
67 if (hibernate_location
->offset
> 0 && access("/sys/power/resume_offset", W_OK
) < 0) {
68 if (errno
== ENOENT
) {
69 log_debug("Kernel too old, can't configure resume_offset for %s, ignoring: %" PRIu64
,
70 hibernate_location
->swap
->device
, hibernate_location
->offset
);
74 return log_debug_errno(errno
, "/sys/power/resume_offset not writable: %m");
77 xsprintf(offset_str
, "%" PRIu64
, hibernate_location
->offset
);
78 r
= write_string_file("/sys/power/resume_offset", offset_str
, WRITE_STRING_FILE_DISABLE_BUFFER
);
80 return log_debug_errno(r
, "Failed to write swap file offset to /sys/power/resume_offset for '%s': '%s': %m",
81 hibernate_location
->swap
->device
, offset_str
);
83 log_debug("Wrote resume_offset= value for %s to /sys/power/resume_offset: %s", hibernate_location
->swap
->device
, offset_str
);
88 static int write_mode(char **modes
) {
92 STRV_FOREACH(mode
, modes
) {
95 k
= write_string_file("/sys/power/disk", *mode
, WRITE_STRING_FILE_DISABLE_BUFFER
);
99 log_debug_errno(k
, "Failed to write '%s' to /sys/power/disk: %m", *mode
);
107 static int write_state(FILE **f
, char **states
) {
114 STRV_FOREACH(state
, states
) {
117 k
= write_string_stream(*f
, *state
, WRITE_STRING_FILE_DISABLE_BUFFER
);
120 log_debug_errno(k
, "Failed to write '%s' to /sys/power/state: %m", *state
);
125 *f
= fopen("/sys/power/state", "we");
133 static int lock_all_homes(void) {
134 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
135 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
136 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
139 /* Let's synchronously lock all home directories managed by homed that have been marked for it. This
140 * way the key material required to access these volumes is hopefully removed from memory. */
142 r
= sd_bus_open_system(&bus
);
144 return log_warning_errno(r
, "Failed to connect to system bus, ignoring: %m");
146 r
= sd_bus_message_new_method_call(
149 "org.freedesktop.home1",
150 "/org/freedesktop/home1",
151 "org.freedesktop.home1.Manager",
154 return bus_log_create_error(r
);
156 /* If homed is not running it can't have any home directories active either. */
157 r
= sd_bus_message_set_auto_start(m
, false);
159 return log_error_errno(r
, "Failed to disable auto-start of LockAllHomes() message: %m");
161 r
= sd_bus_call(bus
, m
, DEFAULT_TIMEOUT_USEC
, &error
, NULL
);
163 if (!bus_error_is_unknown_service(&error
))
164 return log_error_errno(r
, "Failed to lock home directories: %s", bus_error_message(&error
, r
));
166 return log_debug("systemd-homed is not running, locking of home directories skipped.");
169 return log_debug("Successfully requested locking of all home directories.");
172 static int execute(char **modes
, char **states
) {
173 char *arguments
[] = {
179 static const char* const dirs
[] = {
184 _cleanup_fclose_
FILE *f
= NULL
;
185 _cleanup_(hibernate_location_freep
) HibernateLocation
*hibernate_location
= NULL
;
188 /* This file is opened first, so that if we hit an error,
189 * we can abort before modifying any state. */
190 f
= fopen("/sys/power/state", "we");
192 return log_error_errno(errno
, "Failed to open /sys/power/state: %m");
194 setvbuf(f
, NULL
, _IONBF
, 0);
196 /* Configure hibernation settings if we are supposed to hibernate */
197 if (!strv_isempty(modes
)) {
198 r
= find_hibernate_location(&hibernate_location
);
200 return log_error_errno(r
, "Failed to find location to hibernate to: %m");
201 if (r
== 0) { /* 0 means: no hibernation location was configured in the kernel so far, let's
202 * do it ourselves then. > 0 means: kernel already had a configured hibernation
203 * location which we shouldn't touch. */
204 r
= write_hibernate_location_info(hibernate_location
);
206 return log_error_errno(r
, "Failed to prepare for hibernation: %m");
209 r
= write_mode(modes
);
211 return log_error_errno(r
, "Failed to write mode to /sys/power/disk: %m");;
214 (void) execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, arguments
, NULL
, EXEC_DIR_PARALLEL
| EXEC_DIR_IGNORE_ERRORS
);
215 (void) lock_all_homes();
218 "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR
,
219 LOG_MESSAGE("Suspending system..."),
220 "SLEEP=%s", arg_verb
);
222 r
= write_state(&f
, states
);
224 log_struct_errno(LOG_ERR
, r
,
225 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR
,
226 LOG_MESSAGE("Failed to suspend system. System resumed again: %m"),
227 "SLEEP=%s", arg_verb
);
230 "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR
,
231 LOG_MESSAGE("System resumed."),
232 "SLEEP=%s", arg_verb
);
234 arguments
[1] = (char*) "post";
235 (void) execute_directories(dirs
, DEFAULT_TIMEOUT_USEC
, NULL
, NULL
, arguments
, NULL
, EXEC_DIR_PARALLEL
| EXEC_DIR_IGNORE_ERRORS
);
240 static int execute_s2h(const SleepConfig
*sleep_config
) {
241 _cleanup_close_
int tfd
= -1;
242 char buf
[FORMAT_TIMESPAN_MAX
];
243 struct itimerspec ts
= {};
246 assert(sleep_config
);
248 tfd
= timerfd_create(CLOCK_BOOTTIME_ALARM
, TFD_NONBLOCK
|TFD_CLOEXEC
);
250 return log_error_errno(errno
, "Error creating timerfd: %m");
252 log_debug("Set timerfd wake alarm for %s",
253 format_timespan(buf
, sizeof(buf
), sleep_config
->hibernate_delay_sec
, USEC_PER_SEC
));
255 timespec_store(&ts
.it_value
, sleep_config
->hibernate_delay_sec
);
257 r
= timerfd_settime(tfd
, 0, &ts
, NULL
);
259 return log_error_errno(errno
, "Error setting hibernate timer: %m");
261 r
= execute(sleep_config
->suspend_modes
, sleep_config
->suspend_states
);
265 r
= fd_wait_for_event(tfd
, POLLIN
, 0);
267 return log_error_errno(r
, "Error polling timerfd: %m");
268 if (!FLAGS_SET(r
, POLLIN
)) /* We woke up before the alarm time, we are done. */
271 tfd
= safe_close(tfd
);
273 /* If woken up after alarm time, hibernate */
274 log_debug("Attempting to hibernate after waking from %s timer",
275 format_timespan(buf
, sizeof(buf
), sleep_config
->hibernate_delay_sec
, USEC_PER_SEC
));
277 r
= execute(sleep_config
->hibernate_modes
, sleep_config
->hibernate_states
);
279 log_notice_errno(r
, "Couldn't hibernate, will try to suspend again: %m");
281 r
= execute(sleep_config
->suspend_modes
, sleep_config
->suspend_states
);
283 return log_error_errno(r
, "Could neither hibernate nor suspend, giving up: %m");
289 static int help(void) {
290 _cleanup_free_
char *link
= NULL
;
293 r
= terminal_urlify_man("systemd-suspend.service", "8", &link
);
297 printf("%s COMMAND\n\n"
298 "Suspend the system, hibernate the system, or both.\n\n"
299 " -h --help Show this help and exit\n"
300 " --version Print version string and exit\n"
302 " suspend Suspend the system\n"
303 " hibernate Hibernate the system\n"
304 " hybrid-sleep Both hibernate and suspend the system\n"
305 " suspend-then-hibernate Initially suspend and then hibernate\n"
306 " the system after a fixed period of time\n"
307 "\nSee the %s for details.\n"
308 , program_invocation_short_name
315 static int parse_argv(int argc
, char *argv
[]) {
320 static const struct option options
[] = {
321 { "help", no_argument
, NULL
, 'h' },
322 { "version", no_argument
, NULL
, ARG_VERSION
},
331 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
343 assert_not_reached("Unhandled option");
346 if (argc
- optind
!= 1)
347 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
349 program_invocation_short_name
);
351 arg_verb
= strdup(argv
[optind
]);
355 if (!STR_IN_SET(arg_verb
, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"))
356 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
357 "Unknown command '%s'.", arg_verb
);
359 return 1 /* work to do */;
362 static int run(int argc
, char *argv
[]) {
364 char **modes
= NULL
, **states
= NULL
;
365 _cleanup_(free_sleep_configp
) SleepConfig
*sleep_config
= NULL
;
370 r
= parse_argv(argc
, argv
);
374 r
= parse_sleep_config(&sleep_config
);
378 r
= sleep_settings(arg_verb
, sleep_config
, &allow
, &modes
, &states
);
383 return log_error_errno(SYNTHETIC_ERRNO(EACCES
),
384 "Sleep mode \"%s\" is disabled by configuration, refusing.",
387 if (streq(arg_verb
, "suspend-then-hibernate"))
388 return execute_s2h(sleep_config
);
390 return execute(modes
, states
);
393 DEFINE_MAIN_FUNCTION(run
);