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