]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/sleep-config.c
hashmap, set: remove unused functions
[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) {
34c10968 34
19adb8a3
ZJS
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;
dabeaa46 39 char **modes, **states;
19adb8a3
ZJS
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 },
34c10968
LP
48 {}
49 };
19adb8a3 50
36f822c4
ZJS
51 config_parse(NULL, PKGSYSCONFDIR "/sleep.conf", NULL,
52 "Sleep\0",
53 config_item_table_lookup, items, false, false, true, NULL);
19adb8a3 54
19adb8a3
ZJS
55 if (streq(verb, "suspend")) {
56 /* empty by default */
dabeaa46 57 USE(modes, suspend_mode);
19adb8a3
ZJS
58
59 if (suspend_state)
dabeaa46 60 USE(states, suspend_state);
19adb8a3 61 else
dabeaa46 62 states = strv_new("mem", "standby", "freeze", NULL);
19adb8a3 63
19adb8a3
ZJS
64 } else if (streq(verb, "hibernate")) {
65 if (hibernate_mode)
dabeaa46 66 USE(modes, hibernate_mode);
19adb8a3 67 else
dabeaa46 68 modes = strv_new("platform", "shutdown", NULL);
19adb8a3
ZJS
69
70 if (hibernate_state)
dabeaa46 71 USE(states, hibernate_state);
19adb8a3 72 else
dabeaa46 73 states = strv_new("disk", NULL);
19adb8a3 74
19adb8a3
ZJS
75 } else if (streq(verb, "hybrid-sleep")) {
76 if (hybrid_mode)
dabeaa46 77 USE(modes, hybrid_mode);
19adb8a3 78 else
dabeaa46 79 modes = strv_new("suspend", "platform", "shutdown", NULL);
19adb8a3
ZJS
80
81 if (hybrid_state)
dabeaa46 82 USE(states, hybrid_state);
19adb8a3 83 else
dabeaa46 84 states = strv_new("disk", NULL);
19adb8a3 85
19adb8a3
ZJS
86 } else
87 assert_not_reached("what verb");
88
dabeaa46
ZJS
89 if ((!modes && !streq(verb, "suspend")) || !states) {
90 strv_free(modes);
91 strv_free(states);
19adb8a3
ZJS
92 return log_oom();
93 }
94
dabeaa46
ZJS
95 *_modes = modes;
96 *_states = states;
19adb8a3
ZJS
97 return 0;
98}
99
100int can_sleep_state(char **types) {
a2a5291b 101 char **type;
19adb8a3
ZJS
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) {
a2a5291b 117 const char *word, *state;
19adb8a3
ZJS
118 size_t l, k;
119
120 k = strlen(*type);
a2a5291b
ZJS
121 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
122 if (l == k && memcmp(word, *type, l) == 0)
19adb8a3
ZJS
123 return true;
124 }
125
126 return false;
127}
128
129int can_sleep_disk(char **types) {
a2a5291b 130 char **type;
19adb8a3
ZJS
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) {
a2a5291b 146 const char *word, *state;
19adb8a3
ZJS
147 size_t l, k;
148
149 k = strlen(*type);
a2a5291b
ZJS
150 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) {
151 if (l == k && memcmp(word, *type, l) == 0)
19adb8a3
ZJS
152 return true;
153
a2a5291b
ZJS
154 if (l == k + 2 &&
155 word[0] == '[' &&
156 memcmp(word + 1, *type, l - 2) == 0 &&
157 word[l-1] == ']')
19adb8a3
ZJS
158 return true;
159 }
160 }
161
162 return false;
163}
164
69ab8088
ZJS
165#define HIBERNATION_SWAP_THRESHOLD 0.98
166
9fb3675e
ZJS
167static 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
c8a202b7 174 f = fopen("/proc/swaps", "re");
9fb3675e
ZJS
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 }
69ab8088 181
9fb3675e
ZJS
182 (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
183
184 for (i = 1;; i++) {
db69869f 185 _cleanup_free_ char *dev = NULL, *type = NULL;
9fb3675e
ZJS
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
db69869f
DR
204 if (streq(type, "partition") && endswith(dev, "\\040(deleted)")) {
205 log_warning("Ignoring deleted swapfile '%s'.", dev);
9fb3675e
ZJS
206 continue;
207 }
208
209 *size = size_field;
210 *used = used_field;
211 return 0;
69ab8088
ZJS
212 }
213
9fb3675e
ZJS
214 log_debug("No swap partitions were found.");
215 return -ENOSYS;
216}
217
218static bool enough_memory_for_hibernation(void) {
219 _cleanup_free_ char *active = NULL;
39883f62
LP
220 unsigned long long act = 0;
221 size_t size = 0, used = 0;
9fb3675e
ZJS
222 int r;
223
224 r = hibernation_partition_size(&size, &used);
225 if (r < 0)
69ab8088 226 return false;
69ab8088
ZJS
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
9fb3675e
ZJS
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);
69ab8088
ZJS
244
245 return r;
246}
247
19adb8a3
ZJS
248int 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
69ab8088
ZJS
260 if (!can_sleep_state(states) || !can_sleep_disk(modes))
261 return false;
262
263 return streq(verb, "suspend") || enough_memory_for_hibernation();
19adb8a3 264}