]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/sleep-config.c
util-lib: split out globbing related calls into glob-util.[ch]
[thirdparty/systemd.git] / src / shared / sleep-config.c
CommitLineData
19adb8a3
ZJS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7
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.
12
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.
17
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/>.
20***/
21
22#include <stdio.h>
23
24#include "conf-parser.h"
3ffd4af2 25#include "fd-util.h"
19adb8a3
ZJS
26#include "fileio.h"
27#include "log.h"
3ffd4af2 28#include "sleep-config.h"
07630cea 29#include "string-util.h"
19adb8a3
ZJS
30#include "strv.h"
31#include "util.h"
6bedfcbb 32#include "parse-util.h"
19adb8a3 33
dabeaa46
ZJS
34#define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0)
35
36int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
34c10968 37
19adb8a3
ZJS
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;
dabeaa46 42 char **modes, **states;
19adb8a3
ZJS
43
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 },
34c10968
LP
51 {}
52 };
19adb8a3 53
778b6a3f
JT
54 config_parse_many(PKGSYSCONFDIR "/sleep.conf",
55 CONF_DIRS_NULSTR("systemd/sleep.conf"),
56 "Sleep\0", config_item_table_lookup, items,
57 false, NULL);
19adb8a3 58
19adb8a3
ZJS
59 if (streq(verb, "suspend")) {
60 /* empty by default */
dabeaa46 61 USE(modes, suspend_mode);
19adb8a3
ZJS
62
63 if (suspend_state)
dabeaa46 64 USE(states, suspend_state);
19adb8a3 65 else
dabeaa46 66 states = strv_new("mem", "standby", "freeze", NULL);
19adb8a3 67
19adb8a3
ZJS
68 } else if (streq(verb, "hibernate")) {
69 if (hibernate_mode)
dabeaa46 70 USE(modes, hibernate_mode);
19adb8a3 71 else
dabeaa46 72 modes = strv_new("platform", "shutdown", NULL);
19adb8a3
ZJS
73
74 if (hibernate_state)
dabeaa46 75 USE(states, hibernate_state);
19adb8a3 76 else
dabeaa46 77 states = strv_new("disk", NULL);
19adb8a3 78
19adb8a3
ZJS
79 } else if (streq(verb, "hybrid-sleep")) {
80 if (hybrid_mode)
dabeaa46 81 USE(modes, hybrid_mode);
19adb8a3 82 else
dabeaa46 83 modes = strv_new("suspend", "platform", "shutdown", NULL);
19adb8a3
ZJS
84
85 if (hybrid_state)
dabeaa46 86 USE(states, hybrid_state);
19adb8a3 87 else
dabeaa46 88 states = strv_new("disk", NULL);
19adb8a3 89
19adb8a3
ZJS
90 } else
91 assert_not_reached("what verb");
92
dabeaa46
ZJS
93 if ((!modes && !streq(verb, "suspend")) || !states) {
94 strv_free(modes);
95 strv_free(states);
19adb8a3
ZJS
96 return log_oom();
97 }
98
dabeaa46
ZJS
99 *_modes = modes;
100 *_states = states;
19adb8a3
ZJS
101 return 0;
102}
103
104int can_sleep_state(char **types) {
a2a5291b 105 char **type;
19adb8a3
ZJS
106 int r;
107 _cleanup_free_ char *p = NULL;
108
109 if (strv_isempty(types))
110 return true;
111
112 /* If /sys is read-only we cannot sleep */
113 if (access("/sys/power/state", W_OK) < 0)
114 return false;
115
116 r = read_one_line_file("/sys/power/state", &p);
117 if (r < 0)
118 return false;
119
120 STRV_FOREACH(type, types) {
a2a5291b 121 const char *word, *state;
19adb8a3
ZJS
122 size_t l, k;
123
124 k = strlen(*type);
a2a5291b
ZJS
125 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
126 if (l == k && memcmp(word, *type, l) == 0)
19adb8a3
ZJS
127 return true;
128 }
129
130 return false;
131}
132
133int can_sleep_disk(char **types) {
a2a5291b 134 char **type;
19adb8a3
ZJS
135 int r;
136 _cleanup_free_ char *p = NULL;
137
138 if (strv_isempty(types))
139 return true;
140
141 /* If /sys is read-only we cannot sleep */
142 if (access("/sys/power/disk", W_OK) < 0)
143 return false;
144
145 r = read_one_line_file("/sys/power/disk", &p);
146 if (r < 0)
147 return false;
148
149 STRV_FOREACH(type, types) {
a2a5291b 150 const char *word, *state;
19adb8a3
ZJS
151 size_t l, k;
152
153 k = strlen(*type);
a2a5291b
ZJS
154 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) {
155 if (l == k && memcmp(word, *type, l) == 0)
19adb8a3
ZJS
156 return true;
157
a2a5291b
ZJS
158 if (l == k + 2 &&
159 word[0] == '[' &&
160 memcmp(word + 1, *type, l - 2) == 0 &&
161 word[l-1] == ']')
19adb8a3
ZJS
162 return true;
163 }
164 }
165
166 return false;
167}
168
69ab8088
ZJS
169#define HIBERNATION_SWAP_THRESHOLD 0.98
170
9fb3675e
ZJS
171static int hibernation_partition_size(size_t *size, size_t *used) {
172 _cleanup_fclose_ FILE *f;
1fa2f38f 173 unsigned i;
9fb3675e
ZJS
174
175 assert(size);
176 assert(used);
177
c8a202b7 178 f = fopen("/proc/swaps", "re");
9fb3675e
ZJS
179 if (!f) {
180 log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
181 "Failed to retrieve open /proc/swaps: %m");
182 assert(errno > 0);
183 return -errno;
184 }
69ab8088 185
9fb3675e
ZJS
186 (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
187
188 for (i = 1;; i++) {
db69869f 189 _cleanup_free_ char *dev = NULL, *type = NULL;
9fb3675e
ZJS
190 size_t size_field, used_field;
191 int k;
192
193 k = fscanf(f,
194 "%ms " /* device/file */
195 "%ms " /* type of swap */
1fa2f38f
ZJS
196 "%zu " /* swap size */
197 "%zu " /* used */
9fb3675e
ZJS
198 "%*i\n", /* priority */
199 &dev, &type, &size_field, &used_field);
200 if (k != 4) {
201 if (k == EOF)
202 break;
203
204 log_warning("Failed to parse /proc/swaps:%u", i);
205 continue;
206 }
207
db69869f
DR
208 if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) {
209 log_warning("Ignoring deleted swapfile '%s'.", dev);
9fb3675e
ZJS
210 continue;
211 }
212
213 *size = size_field;
214 *used = used_field;
215 return 0;
69ab8088
ZJS
216 }
217
9fb3675e
ZJS
218 log_debug("No swap partitions were found.");
219 return -ENOSYS;
220}
221
222static bool enough_memory_for_hibernation(void) {
223 _cleanup_free_ char *active = NULL;
39883f62
LP
224 unsigned long long act = 0;
225 size_t size = 0, used = 0;
9fb3675e
ZJS
226 int r;
227
228 r = hibernation_partition_size(&size, &used);
229 if (r < 0)
69ab8088 230 return false;
69ab8088 231
c4cd1d4d 232 r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active);
69ab8088 233 if (r < 0) {
da927ba9 234 log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m");
69ab8088
ZJS
235 return false;
236 }
237
238 r = safe_atollu(active, &act);
239 if (r < 0) {
c33b3297
MS
240 log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m",
241 active);
69ab8088
ZJS
242 return false;
243 }
244
9fb3675e
ZJS
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);
69ab8088
ZJS
248
249 return r;
250}
251
19adb8a3
ZJS
252int can_sleep(const char *verb) {
253 _cleanup_strv_free_ char **modes = NULL, **states = NULL;
254 int r;
255
256 assert(streq(verb, "suspend") ||
257 streq(verb, "hibernate") ||
258 streq(verb, "hybrid-sleep"));
259
260 r = parse_sleep_config(verb, &modes, &states);
261 if (r < 0)
262 return false;
263
69ab8088
ZJS
264 if (!can_sleep_state(states) || !can_sleep_disk(modes))
265 return false;
266
267 return streq(verb, "suspend") || enough_memory_for_hibernation();
19adb8a3 268}