]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/sleep-config.c
NEWS: Mention the new Personality= switch in unit 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"
25#include "sleep-config.h"
26#include "fileio.h"
27#include "log.h"
28#include "strv.h"
29#include "util.h"
30
dabeaa46
ZJS
31#define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0)
32
33int parse_sleep_config(const char *verb, char ***_modes, char ***_states) {
19adb8a3
ZJS
34 _cleanup_strv_free_ char
35 **suspend_mode = NULL, **suspend_state = NULL,
36 **hibernate_mode = NULL, **hibernate_state = NULL,
37 **hybrid_mode = NULL, **hybrid_state = NULL;
dabeaa46 38 char **modes, **states;
19adb8a3
ZJS
39
40 const ConfigTableItem items[] = {
41 { "Sleep", "SuspendMode", config_parse_strv, 0, &suspend_mode },
42 { "Sleep", "SuspendState", config_parse_strv, 0, &suspend_state },
43 { "Sleep", "HibernateMode", config_parse_strv, 0, &hibernate_mode },
44 { "Sleep", "HibernateState", config_parse_strv, 0, &hibernate_state },
45 { "Sleep", "HybridSleepMode", config_parse_strv, 0, &hybrid_mode },
46 { "Sleep", "HybridSleepState", config_parse_strv, 0, &hybrid_state },
47 {}};
48
49 int r;
50 FILE _cleanup_fclose_ *f;
51
52 f = fopen(PKGSYSCONFDIR "/sleep.conf", "re");
0ee8d636
ZJS
53 if (!f)
54 log_full(errno == ENOENT ? LOG_DEBUG: LOG_WARNING,
55 "Failed to open configuration file " PKGSYSCONFDIR "/sleep.conf: %m");
56 else {
57 r = config_parse(NULL, PKGSYSCONFDIR "/sleep.conf", f, "Sleep\0",
58 config_item_table_lookup, (void*) items, false, false, NULL);
59 if (r < 0)
60 log_warning("Failed to parse configuration file: %s", strerror(-r));
19adb8a3
ZJS
61 }
62
19adb8a3
ZJS
63 if (streq(verb, "suspend")) {
64 /* empty by default */
dabeaa46 65 USE(modes, suspend_mode);
19adb8a3
ZJS
66
67 if (suspend_state)
dabeaa46 68 USE(states, suspend_state);
19adb8a3 69 else
dabeaa46 70 states = strv_new("mem", "standby", "freeze", NULL);
19adb8a3 71
19adb8a3
ZJS
72 } else if (streq(verb, "hibernate")) {
73 if (hibernate_mode)
dabeaa46 74 USE(modes, hibernate_mode);
19adb8a3 75 else
dabeaa46 76 modes = strv_new("platform", "shutdown", NULL);
19adb8a3
ZJS
77
78 if (hibernate_state)
dabeaa46 79 USE(states, hibernate_state);
19adb8a3 80 else
dabeaa46 81 states = strv_new("disk", NULL);
19adb8a3 82
19adb8a3
ZJS
83 } else if (streq(verb, "hybrid-sleep")) {
84 if (hybrid_mode)
dabeaa46 85 USE(modes, hybrid_mode);
19adb8a3 86 else
dabeaa46 87 modes = strv_new("suspend", "platform", "shutdown", NULL);
19adb8a3
ZJS
88
89 if (hybrid_state)
dabeaa46 90 USE(states, hybrid_state);
19adb8a3 91 else
dabeaa46 92 states = strv_new("disk", NULL);
19adb8a3 93
19adb8a3
ZJS
94 } else
95 assert_not_reached("what verb");
96
dabeaa46
ZJS
97 if ((!modes && !streq(verb, "suspend")) || !states) {
98 strv_free(modes);
99 strv_free(states);
19adb8a3
ZJS
100 return log_oom();
101 }
102
dabeaa46
ZJS
103 *_modes = modes;
104 *_states = states;
19adb8a3
ZJS
105 return 0;
106}
107
108int can_sleep_state(char **types) {
109 char *w, *state, **type;
110 int r;
111 _cleanup_free_ char *p = NULL;
112
113 if (strv_isempty(types))
114 return true;
115
116 /* If /sys is read-only we cannot sleep */
117 if (access("/sys/power/state", W_OK) < 0)
118 return false;
119
120 r = read_one_line_file("/sys/power/state", &p);
121 if (r < 0)
122 return false;
123
124 STRV_FOREACH(type, types) {
125 size_t l, k;
126
127 k = strlen(*type);
128 FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
129 if (l == k && memcmp(w, *type, l) == 0)
130 return true;
131 }
132
133 return false;
134}
135
136int can_sleep_disk(char **types) {
137 char *w, *state, **type;
138 int r;
139 _cleanup_free_ char *p = NULL;
140
141 if (strv_isempty(types))
142 return true;
143
144 /* If /sys is read-only we cannot sleep */
145 if (access("/sys/power/disk", W_OK) < 0)
146 return false;
147
148 r = read_one_line_file("/sys/power/disk", &p);
149 if (r < 0)
150 return false;
151
152 STRV_FOREACH(type, types) {
153 size_t l, k;
154
155 k = strlen(*type);
156 FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state) {
157 if (l == k && memcmp(w, *type, l) == 0)
158 return true;
159
160 if (l == k + 2 && w[0] == '[' && memcmp(w + 1, *type, l - 2) == 0 && w[l-1] == ']')
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;
172 int i;
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 */
195 "%zd " /* swap size */
196 "%zd " /* used */
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;
223 unsigned long long act;
224 size_t size, used;
225 int r;
226
227 r = hibernation_partition_size(&size, &used);
228 if (r < 0)
69ab8088 229 return false;
69ab8088
ZJS
230
231 r = get_status_field("/proc/meminfo", "\nActive(anon):", &active);
232 if (r < 0) {
233 log_error("Failed to retrieve Active(anon) from /proc/meminfo: %s", strerror(-r));
234 return false;
235 }
236
237 r = safe_atollu(active, &act);
238 if (r < 0) {
239 log_error("Failed to parse Active(anon) from /proc/meminfo: %s: %s",
240 active, strerror(-r));
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}