1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 //#include <stdbool.h>
29 #include "alloc-util.h"
30 //#include "conf-parser.h"
32 //#include "env-util.h"
37 #include "parse-util.h"
38 #include "sleep-config.h"
39 #include "string-util.h"
42 #if 0 /// UNNEEDED by elogind
43 #define USE(x, y) do { (x) = (y); (y) = NULL; } while (0)
45 int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
47 _cleanup_strv_free_ char
48 **suspend_mode = NULL, **suspend_state = NULL,
49 **hibernate_mode = NULL, **hibernate_state = NULL,
50 **hybrid_mode = NULL, **hybrid_state = NULL;
51 char **modes, **states;
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 },
63 (void) config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf",
64 CONF_PATHS_NULSTR("elogind/sleep.conf.d"),
65 "Sleep\0", config_item_table_lookup, items,
66 CONFIG_PARSE_WARN, NULL);
68 if (streq(verb, "suspend")) {
69 /* empty by default */
70 USE(modes, suspend_mode);
73 USE(states, suspend_state);
75 states = strv_new("mem", "standby", "freeze", NULL);
77 } else if (streq(verb, "hibernate")) {
79 USE(modes, hibernate_mode);
81 modes = strv_new("platform", "shutdown", NULL);
84 USE(states, hibernate_state);
86 states = strv_new("disk", NULL);
88 } else if (streq(verb, "hybrid-sleep")) {
90 USE(modes, hybrid_mode);
92 modes = strv_new("suspend", "platform", "shutdown", NULL);
95 USE(states, hybrid_state);
97 states = strv_new("disk", NULL);
100 assert_not_reached("what verb");
102 if ((!modes && !streq(verb, "suspend")) || !states) {
114 #if 1 /// Only available in this file for elogind
117 int can_sleep_state(char **types) {
120 _cleanup_free_ char *p = NULL;
122 if (strv_isempty(types))
125 /* If /sys is read-only we cannot sleep */
126 if (access("/sys/power/state", W_OK) < 0)
129 r = read_one_line_file("/sys/power/state", &p);
133 STRV_FOREACH(type, types) {
134 const char *word, *state;
138 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
139 if (l == k && memcmp(word, *type, l) == 0)
146 #if 1 /// Only available in this file for elogind
149 int can_sleep_disk(char **types) {
152 _cleanup_free_ char *p = NULL;
154 if (strv_isempty(types))
157 /* If /sys is read-only we cannot sleep */
158 if (access("/sys/power/disk", W_OK) < 0)
161 r = read_one_line_file("/sys/power/disk", &p);
165 STRV_FOREACH(type, types) {
166 const char *word, *state;
170 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) {
171 if (l == k && memcmp(word, *type, l) == 0)
176 memcmp(word + 1, *type, l - 2) == 0 &&
185 #define HIBERNATION_SWAP_THRESHOLD 0.98
187 static int hibernation_partition_size(size_t *size, size_t *used) {
188 _cleanup_fclose_ FILE *f;
194 f = fopen("/proc/swaps", "re");
196 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
197 "Failed to retrieve open /proc/swaps: %m");
202 (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
205 _cleanup_free_ char *dev = NULL, *type = NULL;
206 size_t size_field, used_field;
210 "%ms " /* device/file */
211 "%ms " /* type of swap */
212 "%zu " /* swap size */
214 "%*i\n", /* priority */
215 &dev, &type, &size_field, &used_field);
220 log_warning("Failed to parse /proc/swaps:%u", i);
224 if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) {
225 log_warning("Ignoring deleted swapfile '%s'.", dev);
234 log_debug("No swap partitions were found.");
238 static bool enough_memory_for_hibernation(void) {
239 _cleanup_free_ char *active = NULL;
240 unsigned long long act = 0;
241 size_t size = 0, used = 0;
244 #if 0 /// elogind does not allow any bypass, we are never init!
245 if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
249 r = hibernation_partition_size(&size, &used);
253 r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
255 log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
259 r = safe_atollu(active, &act);
261 log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
266 r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
267 log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
268 r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
273 #if 0 /// elogind has to do, or better, *can* do it differently
274 int can_sleep(const char *verb) {
275 _cleanup_strv_free_ char **modes = NULL, **states = NULL;
278 assert(streq(verb, "suspend") ||
279 streq(verb, "hibernate") ||
280 streq(verb, "hybrid-sleep"));
282 r = parse_sleep_config(verb, &modes, &states);
286 if (!can_sleep_state(states) || !can_sleep_disk(modes))
289 return streq(verb, "suspend") || enough_memory_for_hibernation();
292 int can_sleep(Manager *m, const char *verb) {
294 assert(streq(verb, "suspend") ||
295 streq(verb, "hibernate") ||
296 streq(verb, "hybrid-sleep"));
298 if ( streq(verb, "suspend")
299 && ( !can_sleep_state(m->suspend_state)
300 || !can_sleep_disk(m->suspend_mode) ) )
303 if ( streq(verb, "hibernate")
304 && ( !can_sleep_state(m->hibernate_state)
305 || !can_sleep_disk(m->hibernate_mode) ) )
308 if ( streq(verb, "hybrid-sleep")
309 && ( !can_sleep_state(m->hybrid_sleep_state)
310 || !can_sleep_disk(m->hybrid_sleep_mode) ) )
314 return streq(verb, "suspend") || enough_memory_for_hibernation();