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"
33 #define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0)
35 int parse_sleep_config(const char *verb
, char ***_modes
, char ***_states
) {
37 _cleanup_strv_free_
char
38 **suspend_mode
= NULL
, **suspend_state
= NULL
,
39 **hibernate_mode
= NULL
, **hibernate_state
= NULL
,
40 **hybrid_mode
= NULL
, **hybrid_state
= NULL
;
41 char **modes
, **states
;
43 const ConfigTableItem items
[] = {
44 { "Sleep", "SuspendMode", config_parse_strv
, 0, &suspend_mode
},
45 { "Sleep", "SuspendState", config_parse_strv
, 0, &suspend_state
},
46 { "Sleep", "HibernateMode", config_parse_strv
, 0, &hibernate_mode
},
47 { "Sleep", "HibernateState", config_parse_strv
, 0, &hibernate_state
},
48 { "Sleep", "HybridSleepMode", config_parse_strv
, 0, &hybrid_mode
},
49 { "Sleep", "HybridSleepState", config_parse_strv
, 0, &hybrid_state
},
53 config_parse_many(PKGSYSCONFDIR
"/sleep.conf",
54 CONF_DIRS_NULSTR("systemd/sleep.conf"),
55 "Sleep\0", config_item_table_lookup
, items
,
58 if (streq(verb
, "suspend")) {
59 /* empty by default */
60 USE(modes
, suspend_mode
);
63 USE(states
, suspend_state
);
65 states
= strv_new("mem", "standby", "freeze", NULL
);
67 } else if (streq(verb
, "hibernate")) {
69 USE(modes
, hibernate_mode
);
71 modes
= strv_new("platform", "shutdown", NULL
);
74 USE(states
, hibernate_state
);
76 states
= strv_new("disk", NULL
);
78 } else if (streq(verb
, "hybrid-sleep")) {
80 USE(modes
, hybrid_mode
);
82 modes
= strv_new("suspend", "platform", "shutdown", NULL
);
85 USE(states
, hybrid_state
);
87 states
= strv_new("disk", NULL
);
90 assert_not_reached("what verb");
92 if ((!modes
&& !streq(verb
, "suspend")) || !states
) {
103 int can_sleep_state(char **types
) {
106 _cleanup_free_
char *p
= NULL
;
108 if (strv_isempty(types
))
111 /* If /sys is read-only we cannot sleep */
112 if (access("/sys/power/state", W_OK
) < 0)
115 r
= read_one_line_file("/sys/power/state", &p
);
119 STRV_FOREACH(type
, types
) {
120 const char *word
, *state
;
124 FOREACH_WORD_SEPARATOR(word
, l
, p
, WHITESPACE
, state
)
125 if (l
== k
&& memcmp(word
, *type
, l
) == 0)
132 int can_sleep_disk(char **types
) {
135 _cleanup_free_
char *p
= NULL
;
137 if (strv_isempty(types
))
140 /* If /sys is read-only we cannot sleep */
141 if (access("/sys/power/disk", W_OK
) < 0)
144 r
= read_one_line_file("/sys/power/disk", &p
);
148 STRV_FOREACH(type
, types
) {
149 const char *word
, *state
;
153 FOREACH_WORD_SEPARATOR(word
, l
, p
, WHITESPACE
, state
) {
154 if (l
== k
&& memcmp(word
, *type
, l
) == 0)
159 memcmp(word
+ 1, *type
, l
- 2) == 0 &&
168 #define HIBERNATION_SWAP_THRESHOLD 0.98
170 static int hibernation_partition_size(size_t *size
, size_t *used
) {
171 _cleanup_fclose_
FILE *f
;
177 f
= fopen("/proc/swaps", "re");
179 log_full(errno
== ENOENT
? LOG_DEBUG
: LOG_WARNING
,
180 "Failed to retrieve open /proc/swaps: %m");
185 (void) fscanf(f
, "%*s %*s %*s %*s %*s\n");
188 _cleanup_free_
char *dev
= NULL
, *type
= NULL
;
189 size_t size_field
, used_field
;
193 "%ms " /* device/file */
194 "%ms " /* type of swap */
195 "%zu " /* swap size */
197 "%*i\n", /* priority */
198 &dev
, &type
, &size_field
, &used_field
);
203 log_warning("Failed to parse /proc/swaps:%u", i
);
207 if (streq(type
, "partition") && endswith(dev
, "\\040(deleted)")) {
208 log_warning("Ignoring deleted swapfile '%s'.", dev
);
217 log_debug("No swap partitions were found.");
221 static bool enough_memory_for_hibernation(void) {
222 _cleanup_free_
char *active
= NULL
;
223 unsigned long long act
= 0;
224 size_t size
= 0, used
= 0;
227 r
= hibernation_partition_size(&size
, &used
);
231 r
= get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE
, &active
);
233 log_error_errno(r
, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
237 r
= safe_atollu(active
, &act
);
239 log_error_errno(r
, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
244 r
= act
<= (size
- used
) * HIBERNATION_SWAP_THRESHOLD
;
245 log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
246 r
? "" : "im", act
, size
, used
, 100*HIBERNATION_SWAP_THRESHOLD
);
251 int can_sleep(const char *verb
) {
252 _cleanup_strv_free_
char **modes
= NULL
, **states
= NULL
;
255 assert(streq(verb
, "suspend") ||
256 streq(verb
, "hibernate") ||
257 streq(verb
, "hybrid-sleep"));
259 r
= parse_sleep_config(verb
, &modes
, &states
);
263 if (!can_sleep_state(states
) || !can_sleep_disk(modes
))
266 return streq(verb
, "suspend") || enough_memory_for_hibernation();