1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2018 Dell Inc.
12 #include <sys/utsname.h>
18 #include "alloc-util.h"
20 #include "conf-parser.h"
27 #include "parse-util.h"
28 #include "path-util.h"
29 #include "proc-cmdline.h"
30 #include "sleep-config.h"
31 #include "string-util.h"
34 int parse_sleep_config(const char *verb
, bool *ret_allow
, char ***ret_modes
, char ***ret_states
, usec_t
*ret_delay
) {
35 int allow_suspend
= -1, allow_hibernate
= -1,
36 allow_s2h
= -1, allow_hybrid_sleep
= -1;
38 _cleanup_strv_free_
char
39 **suspend_mode
= NULL
, **suspend_state
= NULL
,
40 **hibernate_mode
= NULL
, **hibernate_state
= NULL
,
41 **hybrid_mode
= NULL
, **hybrid_state
= NULL
;
42 _cleanup_strv_free_
char **modes
, **states
; /* always initialized below */
43 usec_t delay
= 180 * USEC_PER_MINUTE
;
45 const ConfigTableItem items
[] = {
46 { "Sleep", "AllowSuspend", config_parse_tristate
, 0, &allow_suspend
},
47 { "Sleep", "AllowHibernation", config_parse_tristate
, 0, &allow_hibernate
},
48 { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate
, 0, &allow_s2h
},
49 { "Sleep", "AllowHybridSleep", config_parse_tristate
, 0, &allow_hybrid_sleep
},
51 { "Sleep", "SuspendMode", config_parse_strv
, 0, &suspend_mode
},
52 { "Sleep", "SuspendState", config_parse_strv
, 0, &suspend_state
},
53 { "Sleep", "HibernateMode", config_parse_strv
, 0, &hibernate_mode
},
54 { "Sleep", "HibernateState", config_parse_strv
, 0, &hibernate_state
},
55 { "Sleep", "HybridSleepMode", config_parse_strv
, 0, &hybrid_mode
},
56 { "Sleep", "HybridSleepState", config_parse_strv
, 0, &hybrid_state
},
58 { "Sleep", "HibernateDelaySec", config_parse_sec
, 0, &delay
},
62 (void) config_parse_many_nulstr(PKGSYSCONFDIR
"/sleep.conf",
63 CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
64 "Sleep\0", config_item_table_lookup
, items
,
65 CONFIG_PARSE_WARN
, NULL
);
67 if (streq(verb
, "suspend")) {
68 allow
= allow_suspend
!= 0;
70 /* empty by default */
71 modes
= TAKE_PTR(suspend_mode
);
74 states
= TAKE_PTR(suspend_state
);
76 states
= strv_new("mem", "standby", "freeze", NULL
);
78 } else if (streq(verb
, "hibernate")) {
79 allow
= allow_hibernate
!= 0;
82 modes
= TAKE_PTR(hibernate_mode
);
84 modes
= strv_new("platform", "shutdown", NULL
);
87 states
= TAKE_PTR(hibernate_state
);
89 states
= strv_new("disk", NULL
);
91 } else if (streq(verb
, "hybrid-sleep")) {
92 allow
= allow_hybrid_sleep
> 0 ||
93 (allow_suspend
!= 0 && allow_hibernate
!= 0);
96 modes
= TAKE_PTR(hybrid_mode
);
98 modes
= strv_new("suspend", "platform", "shutdown", NULL
);
101 states
= TAKE_PTR(hybrid_state
);
103 states
= strv_new("disk", NULL
);
105 } else if (streq(verb
, "suspend-then-hibernate")) {
106 allow
= allow_s2h
> 0 ||
107 (allow_suspend
!= 0 && allow_hibernate
!= 0);
109 modes
= states
= NULL
;
111 assert_not_reached("what verb");
113 if ((!modes
&& STR_IN_SET(verb
, "hibernate", "hybrid-sleep")) ||
114 (!states
&& !streq(verb
, "suspend-then-hibernate")))
120 *ret_modes
= TAKE_PTR(modes
);
122 *ret_states
= TAKE_PTR(states
);
129 int can_sleep_state(char **types
) {
132 _cleanup_free_
char *p
= NULL
;
134 if (strv_isempty(types
))
137 /* If /sys is read-only we cannot sleep */
138 if (access("/sys/power/state", W_OK
) < 0)
141 r
= read_one_line_file("/sys/power/state", &p
);
145 STRV_FOREACH(type
, types
) {
146 const char *word
, *state
;
150 FOREACH_WORD_SEPARATOR(word
, l
, p
, WHITESPACE
, state
)
151 if (l
== k
&& memcmp(word
, *type
, l
) == 0)
158 int can_sleep_disk(char **types
) {
161 _cleanup_free_
char *p
= NULL
;
163 if (strv_isempty(types
))
166 /* If /sys is read-only we cannot sleep */
167 if (access("/sys/power/disk", W_OK
) < 0) {
168 log_debug_errno(errno
, "/sys/power/disk is not writable: %m");
172 r
= read_one_line_file("/sys/power/disk", &p
);
174 log_debug_errno(r
, "Couldn't read /sys/power/disk: %m");
178 STRV_FOREACH(type
, types
) {
179 const char *word
, *state
;
183 FOREACH_WORD_SEPARATOR(word
, l
, p
, WHITESPACE
, state
) {
184 if (l
== k
&& memcmp(word
, *type
, l
) == 0)
189 memcmp(word
+ 1, *type
, l
- 2) == 0 &&
198 #define HIBERNATION_SWAP_THRESHOLD 0.98
200 int find_hibernate_location(char **device
, char **type
, size_t *size
, size_t *used
) {
201 _cleanup_fclose_
FILE *f
;
204 f
= fopen("/proc/swaps", "re");
206 log_full(errno
== ENOENT
? LOG_DEBUG
: LOG_WARNING
,
207 "Failed to retrieve open /proc/swaps: %m");
212 (void) fscanf(f
, "%*s %*s %*s %*s %*s\n");
215 _cleanup_free_
char *dev_field
= NULL
, *type_field
= NULL
;
216 size_t size_field
, used_field
;
220 "%ms " /* device/file */
221 "%ms " /* type of swap */
222 "%zu " /* swap size */
224 "%*i\n", /* priority */
225 &dev_field
, &type_field
, &size_field
, &used_field
);
229 log_warning("Failed to parse /proc/swaps:%u", i
);
233 if (streq(type_field
, "file")) {
235 if (endswith(dev_field
, "\\040(deleted)")) {
236 log_warning("Ignoring deleted swap file '%s'.", dev_field
);
240 } else if (streq(type_field
, "partition")) {
243 fn
= path_startswith(dev_field
, "/dev/");
244 if (fn
&& startswith(fn
, "zram")) {
245 log_debug("Ignoring compressed RAM swap device '%s'.", dev_field
);
251 *device
= TAKE_PTR(dev_field
);
253 *type
= TAKE_PTR(type_field
);
261 log_debug("No swap partitions were found.");
265 static bool enough_swap_for_hibernation(void) {
266 _cleanup_free_
char *active
= NULL
;
267 unsigned long long act
= 0;
268 size_t size
= 0, used
= 0;
271 if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
274 r
= find_hibernate_location(NULL
, NULL
, &size
, &used
);
278 r
= get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE
, &active
);
280 log_debug_errno(r
, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
284 r
= safe_atollu(active
, &act
);
286 log_debug_errno(r
, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", active
);
290 r
= act
<= (size
- used
) * HIBERNATION_SWAP_THRESHOLD
;
291 log_debug("%s swap for hibernation, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
292 r
? "Enough" : "Not enough", act
, size
, used
, 100*HIBERNATION_SWAP_THRESHOLD
);
297 static int check_resume_keys(const char *key
, const char *value
, void *data
) {
304 /* Exit if we already know we can't resume. */
307 if (streq(key
, "noresume")) {
308 log_debug("Found \"noresume\" on the kernel command line, hibernation is disabled.");
311 } else if (streq(key
, "resume")) {
312 log_debug("Found resume= option on the kernel command line, hibernation is possible.");
319 static int resume_configured_in_options(const char *options
) {
322 /* We don't use PROC_CMDLINE_STRIP_RD_PREFIX here, so rd.resume is *not* supported. */
323 r
= proc_cmdline_parse_given(options
, check_resume_keys
, &resume
, 0);
328 log_debug("Couldn't find resume= option, hibernation is disabled.");
332 static int resume_configured(void) {
333 _cleanup_(boot_config_free
) BootConfig config
= {};
337 /* Check whether a valid resume= option is present. If possible, we query the boot options
338 * for the default kernel. If the system is not using sd-boot, fall back to checking the
339 * current kernel command line. This is not perfect, but should suffice for most cases. */
341 r
= find_default_boot_entry(NULL
, NULL
, &config
, &e
);
343 log_debug_errno(r
, "Cannot find the ESP partition mount point, falling back to other checks.");
345 return log_debug_errno(r
, "Cannot read boot configuration from ESP, assuming hibernation is not possible.");
347 _cleanup_free_
char *options
= NULL
;
349 options
= strv_join(e
->options
, " ");
353 r
= resume_configured_in_options(options
);
355 return log_error_errno(r
, "Failed to parse kernel options in \"%s\": %m",
360 /* If we can't figure out the default boot entry, let's fall back to current kernel cmdline */
361 _cleanup_free_
char *line
= NULL
;
362 r
= proc_cmdline(&line
);
363 if (IN_SET(r
, -EPERM
, -EACCES
, -ENOENT
))
364 log_debug_errno(r
, "Cannot access /proc/cmdline: %m");
366 return log_error_errno(r
, "Failed to query /proc/cmdline: %m");
368 r
= resume_configured_in_options(line
);
370 return log_error_errno(r
, "Failed to parse kernel proc cmdline: %m");
375 log_debug("Couldn't detect any resume mechanism, hibernation is disabled.");
379 static int kernel_exists(void) {
384 /* Do some superficial checks whether the kernel we are currently running is still around. If it isn't we
385 * shouldn't offer hibernation as we couldn't possible resume from hibernation again. Of course, this check is
386 * very superficial, as the kernel's mere existance is hardly enough to know whether the hibernate/resume cycle
387 * will succeed. However, the common case of kernel updates can be caught this way, and it's definitely worth
391 _cleanup_free_
char *path
= NULL
;
396 /* First, let's look in /lib/modules/`uname -r`/vmlinuz. This is where current Fedora places
397 * its RPM-managed kernels. It's a good place, as it means compiled vendor code is monopolized
398 * in /usr, and then the kernel image is stored along with its modules in the same
399 * hierarchy. It's also what our 'kernel-install' script is written for. */
401 return log_debug_errno(errno
, "Failed to acquire kernel release: %m");
403 path
= strjoin("/lib/modules/", u
.release
, "/vmlinuz");
407 /* Secondly, let's look in /boot/vmlinuz-`uname -r`. This is where older Fedora and other
408 * distributions tend to place the kernel. */
409 path
= strjoin("/boot/vmlinuz-", u
.release
);
413 /* For the other cases, we look in the EFI/boot partition, at the place where our
414 * "kernel-install" script copies the kernel on install by default. */
415 r
= sd_id128_get_machine(&m
);
417 return log_debug_errno(r
, "Failed to read machine ID: %m");
419 (void) asprintf(&path
, "/efi/" SD_ID128_FORMAT_STR
"/%s/linux", SD_ID128_FORMAT_VAL(m
), u
.release
);
422 (void) asprintf(&path
, "/boot/" SD_ID128_FORMAT_STR
"/%s/linux", SD_ID128_FORMAT_VAL(m
), u
.release
);
425 (void) asprintf(&path
, "/boot/efi/" SD_ID128_FORMAT_STR
"/%s/linux", SD_ID128_FORMAT_VAL(m
), u
.release
);
435 log_debug("Testing whether %s exists.", path
);
437 if (access(path
, F_OK
) >= 0)
441 log_debug_errno(errno
, "Failed to determine whether '%s' exists, ignoring: %m", path
);
445 int read_fiemap(int fd
, struct fiemap
**ret
) {
446 _cleanup_free_
struct fiemap
*fiemap
= NULL
, *result_fiemap
= NULL
;
447 struct stat statinfo
;
448 uint32_t result_extents
= 0;
449 uint64_t fiemap_start
= 0, fiemap_length
;
450 const size_t n_extra
= DIV_ROUND_UP(sizeof(struct fiemap
), sizeof(struct fiemap_extent
));
451 size_t fiemap_allocated
= n_extra
, result_fiemap_allocated
= n_extra
;
453 if (fstat(fd
, &statinfo
) < 0)
454 return log_debug_errno(errno
, "Cannot determine file size: %m");
455 if (!S_ISREG(statinfo
.st_mode
))
457 fiemap_length
= statinfo
.st_size
;
459 /* Zero this out in case we run on a file with no extents */
460 fiemap
= calloc(n_extra
, sizeof(struct fiemap_extent
));
464 result_fiemap
= malloc_multiply(n_extra
, sizeof(struct fiemap_extent
));
468 /* XFS filesystem has incorrect implementation of fiemap ioctl and
469 * returns extents for only one block-group at a time, so we need
470 * to handle it manually, starting the next fiemap call from the end
473 while (fiemap_start
< fiemap_length
) {
474 *fiemap
= (struct fiemap
) {
475 .fm_start
= fiemap_start
,
476 .fm_length
= fiemap_length
,
477 .fm_flags
= FIEMAP_FLAG_SYNC
,
480 /* Find out how many extents there are */
481 if (ioctl(fd
, FS_IOC_FIEMAP
, fiemap
) < 0)
482 return log_debug_errno(errno
, "Failed to read extents: %m");
484 /* Nothing to process */
485 if (fiemap
->fm_mapped_extents
== 0)
488 /* Resize fiemap to allow us to read in the extents, result fiemap has to hold all
489 * the extents for the whole file. Add space for the initial struct fiemap. */
490 if (!greedy_realloc0((void**) &fiemap
, &fiemap_allocated
,
491 n_extra
+ fiemap
->fm_mapped_extents
, sizeof(struct fiemap_extent
)))
494 fiemap
->fm_extent_count
= fiemap
->fm_mapped_extents
;
495 fiemap
->fm_mapped_extents
= 0;
497 if (ioctl(fd
, FS_IOC_FIEMAP
, fiemap
) < 0)
498 return log_debug_errno(errno
, "Failed to read extents: %m");
500 /* Resize result_fiemap to allow us to copy in the extents */
501 if (!greedy_realloc((void**) &result_fiemap
, &result_fiemap_allocated
,
502 n_extra
+ result_extents
+ fiemap
->fm_mapped_extents
, sizeof(struct fiemap_extent
)))
505 memcpy(result_fiemap
->fm_extents
+ result_extents
,
507 sizeof(struct fiemap_extent
) * fiemap
->fm_mapped_extents
);
509 result_extents
+= fiemap
->fm_mapped_extents
;
511 /* Highly unlikely that it is zero */
512 if (_likely_(fiemap
->fm_mapped_extents
> 0)) {
513 uint32_t i
= fiemap
->fm_mapped_extents
- 1;
515 fiemap_start
= fiemap
->fm_extents
[i
].fe_logical
+
516 fiemap
->fm_extents
[i
].fe_length
;
518 if (fiemap
->fm_extents
[i
].fe_flags
& FIEMAP_EXTENT_LAST
)
523 memcpy(result_fiemap
, fiemap
, sizeof(struct fiemap
));
524 result_fiemap
->fm_mapped_extents
= result_extents
;
525 *ret
= TAKE_PTR(result_fiemap
);
529 static int can_sleep_internal(const char *verb
, bool check_allowed
);
531 static bool can_s2h(void) {
535 r
= access("/sys/class/rtc/rtc0/wakealarm", W_OK
);
537 log_full(errno
== ENOENT
? LOG_DEBUG
: LOG_WARNING
,
538 "/sys/class/rct/rct0/wakealarm is not writable %m");
542 FOREACH_STRING(p
, "suspend", "hibernate") {
543 r
= can_sleep_internal(p
, false);
544 if (IN_SET(r
, 0, -ENOSPC
, -ENOMEDIUM
, -EADV
)) {
545 log_debug("Unable to %s system.", p
);
549 return log_debug_errno(r
, "Failed to check if %s is possible: %m", p
);
555 static int can_sleep_internal(const char *verb
, bool check_allowed
) {
557 _cleanup_strv_free_
char **modes
= NULL
, **states
= NULL
;
560 assert(STR_IN_SET(verb
, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
562 r
= parse_sleep_config(verb
, &allow
, &modes
, &states
, NULL
);
566 if (check_allowed
&& !allow
) {
567 log_debug("Sleep mode \"%s\" is disabled by configuration.", verb
);
571 if (streq(verb
, "suspend-then-hibernate"))
574 if (!can_sleep_state(states
) || !can_sleep_disk(modes
))
577 if (streq(verb
, "suspend"))
580 if (kernel_exists() <= 0) {
581 log_debug_errno(errno
, "Couldn't find kernel, not offering hibernation.");
585 if (!enough_swap_for_hibernation())
588 r
= resume_configured();
590 /* We squash all errors (e.g. EPERM) into a single value for reporting. */
596 int can_sleep(const char *verb
) {
597 return can_sleep_internal(verb
, true);