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/>.
23 //#include <stdbool.h>
30 #include "alloc-util.h"
31 //#include "conf-parser.h"
33 //#include "env-util.h"
38 #include "parse-util.h"
39 #include "sleep-config.h"
40 #include "string-util.h"
43 #if 0 /// UNNEEDED by elogind
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("elogind/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"))) {
123 #if 1 /// Only available in this file for elogind
126 int can_sleep_state(char **types) {
129 _cleanup_free_ char *p = NULL;
131 if (strv_isempty(types))
134 /* If /sys is read-only we cannot sleep */
135 if (access("/sys/power/state", W_OK) < 0)
138 r = read_one_line_file("/sys/power/state", &p);
142 STRV_FOREACH(type, types) {
143 const char *word, *state;
147 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
148 if (l == k && memcmp(word, *type, l) == 0)
155 #if 1 /// Only available in this file for elogind
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)
170 r = read_one_line_file("/sys/power/disk", &p);
174 STRV_FOREACH(type, types) {
175 const char *word, *state;
179 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) {
180 if (l == k && memcmp(word, *type, l) == 0)
185 memcmp(word + 1, *type, l - 2) == 0 &&
194 #define HIBERNATION_SWAP_THRESHOLD 0.98
196 static int hibernation_partition_size(size_t *size, size_t *used) {
197 _cleanup_fclose_ FILE *f;
203 f = fopen("/proc/swaps", "re");
205 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
206 "Failed to retrieve open /proc/swaps: %m");
211 (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
214 _cleanup_free_ char *dev = NULL, *type = NULL;
215 size_t size_field, used_field;
219 "%ms " /* device/file */
220 "%ms " /* type of swap */
221 "%zu " /* swap size */
223 "%*i\n", /* priority */
224 &dev, &type, &size_field, &used_field);
229 log_warning("Failed to parse /proc/swaps:%u", i);
233 if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) {
234 log_warning("Ignoring deleted swapfile '%s'.", dev);
243 log_debug("No swap partitions were found.");
247 static bool enough_memory_for_hibernation(void) {
248 _cleanup_free_ char *active = NULL;
249 unsigned long long act = 0;
250 size_t size = 0, used = 0;
253 #if 0 /// elogind does not allow any bypass, we are never init!
254 if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
258 r = hibernation_partition_size(&size, &used);
262 r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
264 log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
268 r = safe_atollu(active, &act);
270 log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
275 r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
276 log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
277 r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
282 #if 0 /// elogind has to do, or better, *can* do it differently
283 static bool can_s2h(void) {
286 r = access("/sys/class/rtc/rtc0/wakealarm", W_OK);
288 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
289 "/sys/class/rct/rct0/wakealarm is not writable %m");
293 r = can_sleep("suspend");
295 log_debug_errno(r, "Unable to suspend system.");
299 r = can_sleep("hibernate");
301 log_debug_errno(r, "Unable to hibernate system.");
308 int can_sleep(Manager *m, const char *verb) {
310 assert(streq(verb, "suspend") ||
311 streq(verb, "hibernate") ||
312 streq(verb, "hybrid-sleep"));
313 int can_sleep(const char *verb) {
314 _cleanup_strv_free_ char **modes = NULL, **states = NULL;
317 if ( streq(verb, "suspend")
318 && ( !can_sleep_state(m->suspend_state)
319 || !can_sleep_disk(m->suspend_mode) ) )
321 assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-to-hibernate"));
322 assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
324 if ( streq(verb, "hibernate")
325 && ( !can_sleep_state(m->hibernate_state)
326 || !can_sleep_disk(m->hibernate_mode) ) )
328 if (streq(verb, "suspend-to-hibernate"))
329 if (streq(verb, "suspend-then-hibernate"))
332 if ( streq(verb, "hybrid-sleep")
333 && ( !can_sleep_state(m->hybrid_sleep_state)
334 || !can_sleep_disk(m->hybrid_sleep_mode) ) )
335 r = parse_sleep_config(verb, &modes, &states, NULL);
339 if (!can_sleep_state(states) || !can_sleep_disk(modes))
342 return streq(verb, "suspend") || enough_memory_for_hibernation();