1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Zbigniew Jędrzejewski-Szmek
6 Copyright 2018 Dell Inc.
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include "alloc-util.h"
32 #include "conf-parser.h"
39 #include "parse-util.h"
40 #include "sleep-config.h"
41 #include "string-util.h"
44 int parse_sleep_config(const char *verb
, char ***_modes
, char ***_states
, usec_t
*_delay
) {
46 _cleanup_strv_free_
char
47 **suspend_mode
= NULL
, **suspend_state
= NULL
,
48 **hibernate_mode
= NULL
, **hibernate_state
= NULL
,
49 **hybrid_mode
= NULL
, **hybrid_state
= NULL
;
50 char **modes
, **states
;
51 usec_t delay
= 180 * USEC_PER_MINUTE
;
53 const ConfigTableItem items
[] = {
54 { "Sleep", "SuspendMode", config_parse_strv
, 0, &suspend_mode
},
55 { "Sleep", "SuspendState", config_parse_strv
, 0, &suspend_state
},
56 { "Sleep", "HibernateMode", config_parse_strv
, 0, &hibernate_mode
},
57 { "Sleep", "HibernateState", config_parse_strv
, 0, &hibernate_state
},
58 { "Sleep", "HybridSleepMode", config_parse_strv
, 0, &hybrid_mode
},
59 { "Sleep", "HybridSleepState", config_parse_strv
, 0, &hybrid_state
},
60 { "Sleep", "HibernateDelaySec", config_parse_sec
, 0, &delay
},
64 (void) config_parse_many_nulstr(PKGSYSCONFDIR
"/sleep.conf",
65 CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
66 "Sleep\0", config_item_table_lookup
, items
,
67 CONFIG_PARSE_WARN
, NULL
);
69 if (streq(verb
, "suspend")) {
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")) {
80 modes
= TAKE_PTR(hibernate_mode
);
82 modes
= strv_new("platform", "shutdown", NULL
);
85 states
= TAKE_PTR(hibernate_state
);
87 states
= strv_new("disk", NULL
);
89 } else if (streq(verb
, "hybrid-sleep")) {
91 modes
= TAKE_PTR(hybrid_mode
);
93 modes
= strv_new("suspend", "platform", "shutdown", NULL
);
96 states
= TAKE_PTR(hybrid_state
);
98 states
= strv_new("disk", NULL
);
100 } else if (streq(verb
, "suspend-then-hibernate"))
101 modes
= states
= NULL
;
103 assert_not_reached("what verb");
105 if ((!modes
&& STR_IN_SET(verb
, "hibernate", "hybrid-sleep")) ||
106 (!states
&& !streq(verb
, "suspend-then-hibernate"))) {
122 int can_sleep_state(char **types
) {
125 _cleanup_free_
char *p
= NULL
;
127 if (strv_isempty(types
))
130 /* If /sys is read-only we cannot sleep */
131 if (access("/sys/power/state", W_OK
) < 0)
134 r
= read_one_line_file("/sys/power/state", &p
);
138 STRV_FOREACH(type
, types
) {
139 const char *word
, *state
;
143 FOREACH_WORD_SEPARATOR(word
, l
, p
, WHITESPACE
, state
)
144 if (l
== k
&& memcmp(word
, *type
, l
) == 0)
151 int can_sleep_disk(char **types
) {
154 _cleanup_free_
char *p
= NULL
;
156 if (strv_isempty(types
))
159 /* If /sys is read-only we cannot sleep */
160 if (access("/sys/power/disk", W_OK
) < 0)
163 r
= read_one_line_file("/sys/power/disk", &p
);
167 STRV_FOREACH(type
, types
) {
168 const char *word
, *state
;
172 FOREACH_WORD_SEPARATOR(word
, l
, p
, WHITESPACE
, state
) {
173 if (l
== k
&& memcmp(word
, *type
, l
) == 0)
178 memcmp(word
+ 1, *type
, l
- 2) == 0 &&
187 #define HIBERNATION_SWAP_THRESHOLD 0.98
189 int find_hibernate_location(char **device
, char **type
, size_t *size
, size_t *used
) {
190 _cleanup_fclose_
FILE *f
;
193 f
= fopen("/proc/swaps", "re");
195 log_full(errno
== ENOENT
? LOG_DEBUG
: LOG_WARNING
,
196 "Failed to retrieve open /proc/swaps: %m");
201 (void) fscanf(f
, "%*s %*s %*s %*s %*s\n");
204 _cleanup_free_
char *dev_field
= NULL
, *type_field
= NULL
;
205 size_t size_field
, used_field
;
209 "%ms " /* device/file */
210 "%ms " /* type of swap */
211 "%zu " /* swap size */
213 "%*i\n", /* priority */
214 &dev_field
, &type_field
, &size_field
, &used_field
);
219 log_warning("Failed to parse /proc/swaps:%u", i
);
223 if (streq(type_field
, "partition") && endswith(dev_field
, "\\040(deleted)")) {
224 log_warning("Ignoring deleted swapfile '%s'.", dev_field
);
228 *device
= TAKE_PTR(dev_field
);
230 *type
= TAKE_PTR(type_field
);
238 log_debug("No swap partitions were found.");
242 static bool enough_memory_for_hibernation(void) {
243 _cleanup_free_
char *active
= NULL
;
244 unsigned long long act
= 0;
245 size_t size
= 0, used
= 0;
248 if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
251 r
= find_hibernate_location(NULL
, NULL
, &size
, &used
);
255 r
= get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE
, &active
);
257 log_error_errno(r
, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
261 r
= safe_atollu(active
, &act
);
263 log_error_errno(r
, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
268 r
= act
<= (size
- used
) * HIBERNATION_SWAP_THRESHOLD
;
269 log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
270 r
? "" : "im", act
, size
, used
, 100*HIBERNATION_SWAP_THRESHOLD
);
275 int read_fiemap(int fd
, struct fiemap
**ret
) {
276 _cleanup_free_
struct fiemap
*fiemap
= NULL
, *result_fiemap
= NULL
;
278 struct stat statinfo
;
279 uint32_t result_extents
= 0;
280 uint64_t fiemap_start
= 0, fiemap_length
;
281 size_t fiemap_size
= 1, result_fiemap_size
= 1;
283 if (fstat(fd
, &statinfo
) < 0)
284 return log_debug_errno(errno
, "Cannot determine file size: %m");
285 if (!S_ISREG(statinfo
.st_mode
))
287 fiemap_length
= statinfo
.st_size
;
289 /* zero this out in case we run on a file with no extents */
290 fiemap
= new0(struct fiemap
, 1);
294 result_fiemap
= new(struct fiemap
, 1);
298 /* XFS filesystem has incorrect implementation of fiemap ioctl and
299 * returns extents for only one block-group at a time, so we need
300 * to handle it manually, starting the next fiemap call from the end
303 while (fiemap_start
< fiemap_length
) {
304 *fiemap
= (struct fiemap
) {
305 .fm_start
= fiemap_start
,
306 .fm_length
= fiemap_length
,
307 .fm_flags
= FIEMAP_FLAG_SYNC
,
310 /* Find out how many extents there are */
311 if (ioctl(fd
, FS_IOC_FIEMAP
, fiemap
) < 0)
312 return log_debug_errno(errno
, "Failed to read extents: %m");
314 /* Nothing to process */
315 if (fiemap
->fm_mapped_extents
== 0)
318 /* Result fiemap has to hold all the extents for the whole file */
319 extents_size
= DIV_ROUND_UP(sizeof(struct fiemap_extent
) * fiemap
->fm_mapped_extents
,
320 sizeof(struct fiemap
));
322 /* Resize fiemap to allow us to read in the extents */
323 if (!GREEDY_REALLOC0(fiemap
, fiemap_size
, extents_size
))
326 fiemap
->fm_extent_count
= fiemap
->fm_mapped_extents
;
327 fiemap
->fm_mapped_extents
= 0;
329 if (ioctl(fd
, FS_IOC_FIEMAP
, fiemap
) < 0)
330 return log_debug_errno(errno
, "Failed to read extents: %m");
332 extents_size
= DIV_ROUND_UP(sizeof(struct fiemap_extent
) * (result_extents
+ fiemap
->fm_mapped_extents
),
333 sizeof(struct fiemap
));
335 /* Resize result_fiemap to allow us to read in the extents */
336 if (!GREEDY_REALLOC(result_fiemap
, result_fiemap_size
,
340 memcpy(result_fiemap
->fm_extents
+ result_extents
,
342 sizeof(struct fiemap_extent
) * fiemap
->fm_mapped_extents
);
344 result_extents
+= fiemap
->fm_mapped_extents
;
346 /* Highly unlikely that it is zero */
347 if (fiemap
->fm_mapped_extents
> 0) {
348 uint32_t i
= fiemap
->fm_mapped_extents
- 1;
350 fiemap_start
= fiemap
->fm_extents
[i
].fe_logical
+
351 fiemap
->fm_extents
[i
].fe_length
;
353 if (fiemap
->fm_extents
[i
].fe_flags
& FIEMAP_EXTENT_LAST
)
358 memcpy(result_fiemap
, fiemap
, sizeof(struct fiemap
));
359 result_fiemap
->fm_mapped_extents
= result_extents
;
360 *ret
= TAKE_PTR(result_fiemap
);
364 static bool can_s2h(void) {
367 r
= access("/sys/class/rtc/rtc0/wakealarm", W_OK
);
369 log_full(errno
== ENOENT
? LOG_DEBUG
: LOG_WARNING
,
370 "/sys/class/rct/rct0/wakealarm is not writable %m");
374 r
= can_sleep("suspend");
376 log_debug_errno(r
, "Unable to suspend system.");
380 r
= can_sleep("hibernate");
382 log_debug_errno(r
, "Unable to hibernate system.");
389 int can_sleep(const char *verb
) {
390 _cleanup_strv_free_
char **modes
= NULL
, **states
= NULL
;
393 assert(STR_IN_SET(verb
, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
395 if (streq(verb
, "suspend-then-hibernate"))
398 r
= parse_sleep_config(verb
, &modes
, &states
, NULL
);
402 if (!can_sleep_state(states
) || !can_sleep_disk(modes
))
405 return streq(verb
, "suspend") || enough_memory_for_hibernation();