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