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