1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
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/>.
24 #include "conf-parser.h"
28 #include "sleep-config.h"
29 #include "string-util.h"
32 #include "parse-util.h"
34 #define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0)
36 int parse_sleep_config(const char *verb
, char ***_modes
, char ***_states
) {
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 char **modes
, **states
;
44 const ConfigTableItem items
[] = {
45 { "Sleep", "SuspendMode", config_parse_strv
, 0, &suspend_mode
},
46 { "Sleep", "SuspendState", config_parse_strv
, 0, &suspend_state
},
47 { "Sleep", "HibernateMode", config_parse_strv
, 0, &hibernate_mode
},
48 { "Sleep", "HibernateState", config_parse_strv
, 0, &hibernate_state
},
49 { "Sleep", "HybridSleepMode", config_parse_strv
, 0, &hybrid_mode
},
50 { "Sleep", "HybridSleepState", config_parse_strv
, 0, &hybrid_state
},
54 config_parse_many(PKGSYSCONFDIR
"/sleep.conf",
55 CONF_DIRS_NULSTR("systemd/sleep.conf"),
56 "Sleep\0", config_item_table_lookup
, items
,
59 if (streq(verb
, "suspend")) {
60 /* empty by default */
61 USE(modes
, suspend_mode
);
64 USE(states
, suspend_state
);
66 states
= strv_new("mem", "standby", "freeze", NULL
);
68 } else if (streq(verb
, "hibernate")) {
70 USE(modes
, hibernate_mode
);
72 modes
= strv_new("platform", "shutdown", NULL
);
75 USE(states
, hibernate_state
);
77 states
= strv_new("disk", NULL
);
79 } else if (streq(verb
, "hybrid-sleep")) {
81 USE(modes
, hybrid_mode
);
83 modes
= strv_new("suspend", "platform", "shutdown", NULL
);
86 USE(states
, hybrid_state
);
88 states
= strv_new("disk", NULL
);
91 assert_not_reached("what verb");
93 if ((!modes
&& !streq(verb
, "suspend")) || !states
) {
104 int can_sleep_state(char **types
) {
107 _cleanup_free_
char *p
= NULL
;
109 if (strv_isempty(types
))
112 /* If /sys is read-only we cannot sleep */
113 if (access("/sys/power/state", W_OK
) < 0)
116 r
= read_one_line_file("/sys/power/state", &p
);
120 STRV_FOREACH(type
, types
) {
121 const char *word
, *state
;
125 FOREACH_WORD_SEPARATOR(word
, l
, p
, WHITESPACE
, state
)
126 if (l
== k
&& memcmp(word
, *type
, l
) == 0)
133 int can_sleep_disk(char **types
) {
136 _cleanup_free_
char *p
= NULL
;
138 if (strv_isempty(types
))
141 /* If /sys is read-only we cannot sleep */
142 if (access("/sys/power/disk", W_OK
) < 0)
145 r
= read_one_line_file("/sys/power/disk", &p
);
149 STRV_FOREACH(type
, types
) {
150 const char *word
, *state
;
154 FOREACH_WORD_SEPARATOR(word
, l
, p
, WHITESPACE
, state
) {
155 if (l
== k
&& memcmp(word
, *type
, l
) == 0)
160 memcmp(word
+ 1, *type
, l
- 2) == 0 &&
169 #define HIBERNATION_SWAP_THRESHOLD 0.98
171 static int hibernation_partition_size(size_t *size
, size_t *used
) {
172 _cleanup_fclose_
FILE *f
;
178 f
= fopen("/proc/swaps", "re");
180 log_full(errno
== ENOENT
? LOG_DEBUG
: LOG_WARNING
,
181 "Failed to retrieve open /proc/swaps: %m");
186 (void) fscanf(f
, "%*s %*s %*s %*s %*s\n");
189 _cleanup_free_
char *dev
= NULL
, *type
= NULL
;
190 size_t size_field
, used_field
;
194 "%ms " /* device/file */
195 "%ms " /* type of swap */
196 "%zu " /* swap size */
198 "%*i\n", /* priority */
199 &dev
, &type
, &size_field
, &used_field
);
204 log_warning("Failed to parse /proc/swaps:%u", i
);
208 if (streq(type
, "partition") && endswith(dev
, "\\040(deleted)")) {
209 log_warning("Ignoring deleted swapfile '%s'.", dev
);
218 log_debug("No swap partitions were found.");
222 static bool enough_memory_for_hibernation(void) {
223 _cleanup_free_
char *active
= NULL
;
224 unsigned long long act
= 0;
225 size_t size
= 0, used
= 0;
228 r
= hibernation_partition_size(&size
, &used
);
232 r
= get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE
, &active
);
234 log_error_errno(r
, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
238 r
= safe_atollu(active
, &act
);
240 log_error_errno(r
, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
245 r
= act
<= (size
- used
) * HIBERNATION_SWAP_THRESHOLD
;
246 log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
247 r
? "" : "im", act
, size
, used
, 100*HIBERNATION_SWAP_THRESHOLD
);
252 int can_sleep(const char *verb
) {
253 _cleanup_strv_free_
char **modes
= NULL
, **states
= NULL
;
256 assert(streq(verb
, "suspend") ||
257 streq(verb
, "hibernate") ||
258 streq(verb
, "hybrid-sleep"));
260 r
= parse_sleep_config(verb
, &modes
, &states
);
264 if (!can_sleep_state(states
) || !can_sleep_disk(modes
))
267 return streq(verb
, "suspend") || enough_memory_for_hibernation();