]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/sleep-config.c
test-network: wait for the state file being updated
[thirdparty/systemd.git] / src / shared / sleep-config.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "alloc-util.h"
6 #include "conf-parser.h"
7 #include "constants.h"
8 #include "device-util.h"
9 #include "devnum-util.h"
10 #include "errno-util.h"
11 #include "fd-util.h"
12 #include "fileio.h"
13 #include "hibernate-util.h"
14 #include "log.h"
15 #include "macro.h"
16 #include "path-util.h"
17 #include "sleep-config.h"
18 #include "stat-util.h"
19 #include "stdio-util.h"
20 #include "string-table.h"
21 #include "string-util.h"
22 #include "strv.h"
23 #include "time-util.h"
24
25 #define DEFAULT_SUSPEND_ESTIMATION_USEC (1 * USEC_PER_HOUR)
26
27 static const char* const sleep_operation_table[_SLEEP_OPERATION_MAX] = {
28 [SLEEP_SUSPEND] = "suspend",
29 [SLEEP_HIBERNATE] = "hibernate",
30 [SLEEP_HYBRID_SLEEP] = "hybrid-sleep",
31 [SLEEP_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate",
32 };
33
34 DEFINE_STRING_TABLE_LOOKUP(sleep_operation, SleepOperation);
35
36 static char* const* const sleep_default_state_table[_SLEEP_OPERATION_CONFIG_MAX] = {
37 [SLEEP_SUSPEND] = STRV_MAKE("mem", "standby", "freeze"),
38 [SLEEP_HIBERNATE] = STRV_MAKE("disk"),
39 [SLEEP_HYBRID_SLEEP] = STRV_MAKE("disk"),
40 };
41
42 static char* const* const sleep_default_mode_table[_SLEEP_OPERATION_CONFIG_MAX] = {
43 /* Not used by SLEEP_SUSPEND */
44 [SLEEP_HIBERNATE] = STRV_MAKE("platform", "shutdown"),
45 [SLEEP_HYBRID_SLEEP] = STRV_MAKE("suspend", "platform", "shutdown"),
46 };
47
48 SleepConfig* sleep_config_free(SleepConfig *sc) {
49 if (!sc)
50 return NULL;
51
52 for (SleepOperation i = 0; i < _SLEEP_OPERATION_CONFIG_MAX; i++) {
53 strv_free(sc->states[i]);
54 strv_free(sc->modes[i]);
55 }
56
57 return mfree(sc);
58 }
59
60 static void sleep_config_validate_state_and_mode(SleepConfig *sc) {
61 assert(sc);
62
63 /* So we should really not allow setting SuspendState= to 'disk', which means hibernation. We have
64 * SLEEP_HIBERNATE for proper hibernation support, which includes checks for resume support (through
65 * EFI variable or resume= kernel command line option). It's simply not sensible to call the suspend
66 * operation but eventually do an unsafe hibernation. */
67 if (strv_contains(sc->states[SLEEP_SUSPEND], "disk")) {
68 strv_remove(sc->states[SLEEP_SUSPEND], "disk");
69 log_warning("Sleep state 'disk' is not supported by operation %s, ignoring.",
70 sleep_operation_to_string(SLEEP_SUSPEND));
71 }
72 assert(!sc->modes[SLEEP_SUSPEND]);
73
74 /* People should use hybrid-sleep instead of setting HibernateMode=suspend. Warn about it but don't
75 * drop it in this case. */
76 if (strv_contains(sc->modes[SLEEP_HIBERNATE], "suspend"))
77 log_warning("Sleep mode 'suspend' should not be used by operation %s. Please use %s instead.",
78 sleep_operation_to_string(SLEEP_HIBERNATE), sleep_operation_to_string(SLEEP_HYBRID_SLEEP));
79
80 if (!strv_contains(sc->modes[SLEEP_HYBRID_SLEEP], "suspend"))
81 log_warning("Sleep mode 'suspend' is not set for operation %s. This would likely result in a plain hibernation.",
82 sleep_operation_to_string(SLEEP_HYBRID_SLEEP));
83 }
84
85 int parse_sleep_config(SleepConfig **ret) {
86 _cleanup_(sleep_config_freep) SleepConfig *sc = NULL;
87 int allow_suspend = -1, allow_hibernate = -1, allow_s2h = -1, allow_hybrid_sleep = -1;
88
89 assert(ret);
90
91 sc = new(SleepConfig, 1);
92 if (!sc)
93 return log_oom();
94
95 *sc = (SleepConfig) {
96 .hibernate_delay_usec = USEC_INFINITY,
97 };
98
99 const ConfigTableItem items[] = {
100 { "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend },
101 { "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate },
102 { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h },
103 { "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep },
104
105 { "Sleep", "SuspendState", config_parse_strv, 0, sc->states + SLEEP_SUSPEND },
106 { "Sleep", "SuspendMode", config_parse_warn_compat, DISABLED_LEGACY, NULL },
107
108 { "Sleep", "HibernateState", config_parse_warn_compat, DISABLED_LEGACY, NULL },
109 { "Sleep", "HibernateMode", config_parse_strv, 0, sc->modes + SLEEP_HIBERNATE },
110
111 { "Sleep", "HybridSleepState", config_parse_warn_compat, DISABLED_LEGACY, NULL },
112 { "Sleep", "HybridSleepMode", config_parse_strv, 0, sc->modes + SLEEP_HYBRID_SLEEP },
113
114 { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &sc->hibernate_delay_usec },
115 { "Sleep", "SuspendEstimationSec", config_parse_sec, 0, &sc->suspend_estimation_usec },
116 {}
117 };
118
119 (void) config_parse_config_file("sleep.conf", "Sleep\0",
120 config_item_table_lookup, items,
121 CONFIG_PARSE_WARN, NULL);
122
123 /* use default values unless set */
124 sc->allow[SLEEP_SUSPEND] = allow_suspend != 0;
125 sc->allow[SLEEP_HIBERNATE] = allow_hibernate != 0;
126 sc->allow[SLEEP_HYBRID_SLEEP] = allow_hybrid_sleep >= 0 ? allow_hybrid_sleep
127 : (allow_suspend != 0 && allow_hibernate != 0);
128 sc->allow[SLEEP_SUSPEND_THEN_HIBERNATE] = allow_s2h >= 0 ? allow_s2h
129 : (allow_suspend != 0 && allow_hibernate != 0);
130
131 for (SleepOperation i = 0; i < _SLEEP_OPERATION_CONFIG_MAX; i++) {
132 if (!sc->states[i] && sleep_default_state_table[i]) {
133 sc->states[i] = strv_copy(sleep_default_state_table[i]);
134 if (!sc->states[i])
135 return log_oom();
136 }
137
138 if (!sc->modes[i] && sleep_default_mode_table[i]) {
139 sc->modes[i] = strv_copy(sleep_default_mode_table[i]);
140 if (!sc->modes[i])
141 return log_oom();
142 }
143 }
144
145 if (sc->suspend_estimation_usec == 0)
146 sc->suspend_estimation_usec = DEFAULT_SUSPEND_ESTIMATION_USEC;
147
148 sleep_config_validate_state_and_mode(sc);
149
150 *ret = TAKE_PTR(sc);
151 return 0;
152 }
153
154 int sleep_state_supported(char **states) {
155 _cleanup_free_ char *supported_sysfs = NULL;
156 const char *found;
157 int r;
158
159 if (strv_isempty(states))
160 return log_debug_errno(SYNTHETIC_ERRNO(ENOMSG), "No sleep state configured.");
161
162 if (access("/sys/power/state", W_OK) < 0)
163 return log_debug_errno(errno, "/sys/power/state is not writable: %m");
164
165 r = read_one_line_file("/sys/power/state", &supported_sysfs);
166 if (r < 0)
167 return log_debug_errno(r, "Failed to read /sys/power/state: %m");
168
169 r = string_contains_word_strv(supported_sysfs, NULL, states, &found);
170 if (r < 0)
171 return log_debug_errno(r, "Failed to parse /sys/power/state: %m");
172 if (r > 0) {
173 log_debug("Sleep state '%s' is supported by kernel.", found);
174 return true;
175 }
176
177 if (DEBUG_LOGGING) {
178 _cleanup_free_ char *joined = strv_join(states, " ");
179 log_debug("None of the configured sleep states are supported by kernel: %s", strnull(joined));
180 }
181 return false;
182 }
183
184 int sleep_mode_supported(char **modes) {
185 _cleanup_free_ char *supported_sysfs = NULL;
186 int r;
187
188 /* Unlike state, kernel has its own default choice if not configured */
189 if (strv_isempty(modes)) {
190 log_debug("No sleep mode configured, using kernel default.");
191 return true;
192 }
193
194 if (access("/sys/power/disk", W_OK) < 0)
195 return log_debug_errno(errno, "/sys/power/disk is not writable: %m");
196
197 r = read_one_line_file("/sys/power/disk", &supported_sysfs);
198 if (r < 0)
199 return log_debug_errno(r, "Failed to read /sys/power/disk: %m");
200
201 for (const char *p = supported_sysfs;;) {
202 _cleanup_free_ char *word = NULL;
203 char *mode;
204 size_t l;
205
206 r = extract_first_word(&p, &word, NULL, 0);
207 if (r < 0)
208 return log_debug_errno(r, "Failed to parse /sys/power/disk: %m");
209 if (r == 0)
210 break;
211
212 mode = word;
213 l = strlen(word);
214
215 if (mode[0] == '[' && mode[l - 1] == ']') {
216 mode[l - 1] = '\0';
217 mode++;
218 }
219
220 if (strv_contains(modes, mode)) {
221 log_debug("Disk sleep mode '%s' is supported by kernel.", mode);
222 return true;
223 }
224 }
225
226 if (DEBUG_LOGGING) {
227 _cleanup_free_ char *joined = strv_join(modes, " ");
228 log_debug("None of the configured hibernation power modes are supported by kernel: %s", strnull(joined));
229 }
230 return false;
231 }
232
233 static int sleep_supported_internal(
234 const SleepConfig *sleep_config,
235 SleepOperation operation,
236 bool check_allowed,
237 SleepSupport *ret_support);
238
239 static int s2h_supported(const SleepConfig *sleep_config, SleepSupport *ret_support) {
240
241 static const SleepOperation operations[] = {
242 SLEEP_SUSPEND,
243 SLEEP_HIBERNATE,
244 };
245
246 SleepSupport support;
247 int r;
248
249 assert(sleep_config);
250 assert(ret_support);
251
252 if (!clock_supported(CLOCK_BOOTTIME_ALARM)) {
253 log_debug("CLOCK_BOOTTIME_ALARM is not supported, can't perform %s.", sleep_operation_to_string(SLEEP_SUSPEND_THEN_HIBERNATE));
254 *ret_support = SLEEP_ALARM_NOT_SUPPORTED;
255 return false;
256 }
257
258 FOREACH_ARRAY(i, operations, ELEMENTSOF(operations)) {
259 r = sleep_supported_internal(sleep_config, *i, /* check_allowed = */ false, &support);
260 if (r < 0)
261 return r;
262 if (r == 0) {
263 log_debug("Sleep operation %s is not supported, can't perform %s.",
264 sleep_operation_to_string(*i), sleep_operation_to_string(SLEEP_SUSPEND_THEN_HIBERNATE));
265 *ret_support = support;
266 return false;
267 }
268 }
269
270 assert(support == SLEEP_SUPPORTED);
271 *ret_support = support;
272
273 return true;
274 }
275
276 static int sleep_supported_internal(
277 const SleepConfig *sleep_config,
278 SleepOperation operation,
279 bool check_allowed,
280 SleepSupport *ret_support) {
281
282 int r;
283
284 assert(sleep_config);
285 assert(operation >= 0);
286 assert(operation < _SLEEP_OPERATION_MAX);
287 assert(ret_support);
288
289 if (check_allowed && !sleep_config->allow[operation]) {
290 log_debug("Sleep operation %s is disabled by configuration.", sleep_operation_to_string(operation));
291 *ret_support = SLEEP_DISABLED;
292 return false;
293 }
294
295 if (operation == SLEEP_SUSPEND_THEN_HIBERNATE)
296 return s2h_supported(sleep_config, ret_support);
297
298 assert(operation < _SLEEP_OPERATION_CONFIG_MAX);
299
300 r = sleep_state_supported(sleep_config->states[operation]);
301 if (r == -ENOMSG) {
302 *ret_support = SLEEP_NOT_CONFIGURED;
303 return false;
304 }
305 if (r < 0)
306 return r;
307 if (r == 0) {
308 *ret_support = SLEEP_STATE_OR_MODE_NOT_SUPPORTED;
309 return false;
310 }
311
312 if (IN_SET(operation, SLEEP_HIBERNATE, SLEEP_HYBRID_SLEEP)) {
313 r = sleep_mode_supported(sleep_config->modes[operation]);
314 if (r < 0)
315 return r;
316 if (r == 0) {
317 *ret_support = SLEEP_STATE_OR_MODE_NOT_SUPPORTED;
318 return false;
319 }
320
321 r = hibernation_is_safe();
322 if (r == -ENOTRECOVERABLE) {
323 *ret_support = SLEEP_RESUME_NOT_SUPPORTED;
324 return false;
325 }
326 if (r == -ENOSPC) {
327 *ret_support = SLEEP_NOT_ENOUGH_SWAP_SPACE;
328 return false;
329 }
330 if (r < 0)
331 return r;
332 } else
333 assert(!sleep_config->modes[operation]);
334
335 *ret_support = SLEEP_SUPPORTED;
336 return true;
337 }
338
339 int sleep_supported_full(SleepOperation operation, SleepSupport *ret_support) {
340 _cleanup_(sleep_config_freep) SleepConfig *sleep_config = NULL;
341 SleepSupport support;
342 int r;
343
344 assert(operation >= 0);
345 assert(operation < _SLEEP_OPERATION_MAX);
346
347 r = parse_sleep_config(&sleep_config);
348 if (r < 0)
349 return r;
350
351 r = sleep_supported_internal(sleep_config, operation, /* check_allowed = */ true, &support);
352 if (r < 0)
353 return r;
354
355 assert((r > 0) == (support == SLEEP_SUPPORTED));
356
357 if (ret_support)
358 *ret_support = support;
359
360 return r;
361 }