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