]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/sleep-config.c
ci: restrict x86-only packages to x86 configs (#38056)
[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 "extract-word.h"
8 #include "fileio.h"
9 #include "hibernate-util.h"
10 #include "log.h"
11 #include "sleep-config.h"
12 #include "string-table.h"
13 #include "string-util.h"
14 #include "strv.h"
15 #include "time-util.h"
16
17 #define DEFAULT_SUSPEND_ESTIMATION_USEC (1 * USEC_PER_HOUR)
18
19 static const char* const sleep_operation_table[_SLEEP_OPERATION_MAX] = {
20 [SLEEP_SUSPEND] = "suspend",
21 [SLEEP_HIBERNATE] = "hibernate",
22 [SLEEP_HYBRID_SLEEP] = "hybrid-sleep",
23 [SLEEP_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate",
24 };
25
26 DEFINE_STRING_TABLE_LOOKUP(sleep_operation, SleepOperation);
27
28 static char* const* const sleep_default_state_table[_SLEEP_OPERATION_CONFIG_MAX] = {
29 [SLEEP_SUSPEND] = STRV_MAKE("mem", "standby", "freeze"),
30 [SLEEP_HIBERNATE] = STRV_MAKE("disk"),
31 [SLEEP_HYBRID_SLEEP] = STRV_MAKE("disk"),
32 };
33
34 static char* const* const sleep_default_mode_table[_SLEEP_OPERATION_CONFIG_MAX] = {
35 /* Not used by SLEEP_SUSPEND */
36 [SLEEP_HIBERNATE] = STRV_MAKE("platform", "shutdown"),
37 [SLEEP_HYBRID_SLEEP] = STRV_MAKE("suspend"),
38 };
39
40 SleepConfig* sleep_config_free(SleepConfig *sc) {
41 if (!sc)
42 return NULL;
43
44 for (SleepOperation i = 0; i < _SLEEP_OPERATION_CONFIG_MAX; i++) {
45 strv_free(sc->states[i]);
46 strv_free(sc->modes[i]);
47 }
48
49 strv_free(sc->mem_modes);
50
51 return mfree(sc);
52 }
53
54 static int config_parse_sleep_mode(
55 const char *unit,
56 const char *filename,
57 unsigned line,
58 const char *section,
59 unsigned section_line,
60 const char *lvalue,
61 int ltype,
62 const char *rvalue,
63 void *data,
64 void *userdata) {
65
66 char ***sv = ASSERT_PTR(data);
67 _cleanup_strv_free_ char **modes = NULL;
68 int r;
69
70 assert(filename);
71 assert(lvalue);
72 assert(rvalue);
73
74 if (isempty(rvalue)) {
75 modes = strv_new(NULL);
76 if (!modes)
77 return log_oom();
78 } else {
79 r = strv_split_full(&modes, rvalue, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
80 if (r < 0)
81 return log_oom();
82 }
83
84 return strv_free_and_replace(*sv, modes);
85 }
86
87 static void sleep_config_validate_state_and_mode(SleepConfig *sc) {
88 assert(sc);
89
90 /* So we should really not allow setting SuspendState= to 'disk', which means hibernation. We have
91 * SLEEP_HIBERNATE for proper hibernation support, which includes checks for resume support (through
92 * EFI variable or resume= kernel command line option). It's simply not sensible to call the suspend
93 * operation but eventually do an unsafe hibernation. */
94 if (strv_contains(sc->states[SLEEP_SUSPEND], "disk")) {
95 strv_remove(sc->states[SLEEP_SUSPEND], "disk");
96 log_warning("Sleep state 'disk' is not supported by operation %s, ignoring.",
97 sleep_operation_to_string(SLEEP_SUSPEND));
98 }
99 assert(!sc->modes[SLEEP_SUSPEND]);
100
101 /* People should use hybrid-sleep instead of setting HibernateMode=suspend. Warn about it but don't
102 * drop it in this case. */
103 if (strv_contains(sc->modes[SLEEP_HIBERNATE], "suspend"))
104 log_warning("Sleep mode 'suspend' should not be used by operation %s. Please use %s instead.",
105 sleep_operation_to_string(SLEEP_HIBERNATE), sleep_operation_to_string(SLEEP_HYBRID_SLEEP));
106 }
107
108 int parse_sleep_config(SleepConfig **ret) {
109 _cleanup_(sleep_config_freep) SleepConfig *sc = NULL;
110 int allow_suspend = -1, allow_hibernate = -1, allow_s2h = -1, allow_hybrid_sleep = -1;
111
112 assert(ret);
113
114 sc = new(SleepConfig, 1);
115 if (!sc)
116 return log_oom();
117
118 *sc = (SleepConfig) {
119 .hibernate_delay_usec = USEC_INFINITY,
120 .hibernate_on_ac_power = true,
121 };
122
123 const ConfigTableItem items[] = {
124 { "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend },
125 { "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate },
126 { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h },
127 { "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep },
128
129 { "Sleep", "SuspendState", config_parse_strv, 0, sc->states + SLEEP_SUSPEND },
130 { "Sleep", "SuspendMode", config_parse_warn_compat, DISABLED_LEGACY, NULL },
131
132 { "Sleep", "HibernateState", config_parse_warn_compat, DISABLED_LEGACY, NULL },
133 { "Sleep", "HibernateMode", config_parse_sleep_mode, 0, sc->modes + SLEEP_HIBERNATE },
134
135 { "Sleep", "HybridSleepState", config_parse_warn_compat, DISABLED_LEGACY, NULL },
136 { "Sleep", "HybridSleepMode", config_parse_warn_compat, DISABLED_LEGACY, NULL },
137
138 { "Sleep", "MemorySleepMode", config_parse_sleep_mode, 0, &sc->mem_modes },
139
140 { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &sc->hibernate_delay_usec },
141 { "Sleep", "HibernateOnACPower", config_parse_bool, 0, &sc->hibernate_on_ac_power },
142 { "Sleep", "SuspendEstimationSec", config_parse_sec, 0, &sc->suspend_estimation_usec },
143 {}
144 };
145
146 (void) config_parse_standard_file_with_dropins(
147 "systemd/sleep.conf",
148 "Sleep\0",
149 config_item_table_lookup, items,
150 CONFIG_PARSE_WARN,
151 /* userdata= */ NULL);
152
153 /* use default values unless set */
154 sc->allow[SLEEP_SUSPEND] = allow_suspend != 0;
155 sc->allow[SLEEP_HIBERNATE] = allow_hibernate != 0;
156 sc->allow[SLEEP_HYBRID_SLEEP] = allow_hybrid_sleep >= 0 ? allow_hybrid_sleep
157 : (allow_suspend != 0 && allow_hibernate != 0);
158 sc->allow[SLEEP_SUSPEND_THEN_HIBERNATE] = allow_s2h >= 0 ? allow_s2h
159 : (allow_suspend != 0 && allow_hibernate != 0);
160
161 for (SleepOperation i = 0; i < _SLEEP_OPERATION_CONFIG_MAX; i++) {
162 if (!sc->states[i] && sleep_default_state_table[i]) {
163 sc->states[i] = strv_copy(sleep_default_state_table[i]);
164 if (!sc->states[i])
165 return log_oom();
166 }
167
168 if (!sc->modes[i] && sleep_default_mode_table[i]) {
169 sc->modes[i] = strv_copy(sleep_default_mode_table[i]);
170 if (!sc->modes[i])
171 return log_oom();
172 }
173 }
174
175 if (sc->suspend_estimation_usec == 0)
176 sc->suspend_estimation_usec = DEFAULT_SUSPEND_ESTIMATION_USEC;
177
178 sleep_config_validate_state_and_mode(sc);
179
180 *ret = TAKE_PTR(sc);
181 return 0;
182 }
183
184 bool sleep_needs_mem_sleep(const SleepConfig *sc, SleepOperation operation) {
185 assert(sc);
186 assert(operation >= 0 && operation < _SLEEP_OPERATION_CONFIG_MAX);
187
188 /* As per https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation,
189 * /sys/power/mem_sleep is honored if /sys/power/state is set to "mem" (common for suspend)
190 * or /sys/power/disk is set to "suspend" (hybrid-sleep). */
191
192 return strv_contains(sc->states[operation], "mem") ||
193 strv_contains(sc->modes[operation], "suspend");
194 }
195
196 int sleep_state_supported(char * const *states) {
197 _cleanup_free_ char *supported_sysfs = NULL;
198 const char *found;
199 int r;
200
201 if (strv_isempty(states))
202 return log_debug_errno(SYNTHETIC_ERRNO(ENOMSG), "No sleep state configured.");
203
204 if (access("/sys/power/state", W_OK) < 0)
205 return log_debug_errno(errno, "/sys/power/state is not writable: %m");
206
207 r = read_one_line_file("/sys/power/state", &supported_sysfs);
208 if (r < 0)
209 return log_debug_errno(r, "Failed to read /sys/power/state: %m");
210
211 r = string_contains_word_strv(supported_sysfs, NULL, states, &found);
212 if (r < 0)
213 return log_debug_errno(r, "Failed to parse /sys/power/state: %m");
214 if (r > 0) {
215 log_debug("Sleep state '%s' is supported by kernel.", found);
216 return true;
217 }
218
219 if (DEBUG_LOGGING) {
220 _cleanup_free_ char *joined = strv_join(states, " ");
221 log_debug("None of the configured sleep states are supported by kernel: %s", strnull(joined));
222 }
223 return false;
224 }
225
226 int sleep_mode_supported(const char *path, char * const *modes) {
227 _cleanup_free_ char *supported_sysfs = NULL;
228 int r;
229
230 assert(path);
231
232 /* Unlike state, kernel has its own default choice if not configured */
233 if (strv_isempty(modes)) {
234 log_debug("No sleep mode configured, using kernel default for %s.", path);
235 return true;
236 }
237
238 if (access(path, W_OK) < 0)
239 return log_debug_errno(errno, "%s is not writable: %m", path);
240
241 r = read_one_line_file(path, &supported_sysfs);
242 if (r < 0)
243 return log_debug_errno(r, "Failed to read %s: %m", path);
244
245 for (const char *p = supported_sysfs;;) {
246 _cleanup_free_ char *word = NULL;
247 char *mode;
248 size_t l;
249
250 r = extract_first_word(&p, &word, NULL, 0);
251 if (r < 0)
252 return log_debug_errno(r, "Failed to parse %s: %m", path);
253 if (r == 0)
254 break;
255
256 mode = word;
257 l = strlen(word);
258
259 if (mode[0] == '[' && mode[l - 1] == ']') {
260 mode[l - 1] = '\0';
261 mode++;
262 }
263
264 if (strv_contains(modes, mode)) {
265 log_debug("Sleep mode '%s' is supported by kernel (%s).", mode, path);
266 return true;
267 }
268 }
269
270 if (DEBUG_LOGGING) {
271 _cleanup_free_ char *joined = strv_join(modes, " ");
272 log_debug("None of the configured modes are supported by kernel (%s): %s",
273 path, strnull(joined));
274 }
275 return false;
276 }
277
278 static int sleep_supported_internal(
279 const SleepConfig *sleep_config,
280 SleepOperation operation,
281 bool check_allowed,
282 SleepSupport *ret_support);
283
284 static int s2h_supported(const SleepConfig *sleep_config, SleepSupport *ret_support) {
285
286 static const SleepOperation operations[] = {
287 SLEEP_SUSPEND,
288 SLEEP_HIBERNATE,
289 };
290
291 SleepSupport support;
292 int r;
293
294 assert(sleep_config);
295 assert(ret_support);
296
297 if (!clock_supported(CLOCK_BOOTTIME_ALARM)) {
298 log_debug("CLOCK_BOOTTIME_ALARM is not supported, can't perform %s.", sleep_operation_to_string(SLEEP_SUSPEND_THEN_HIBERNATE));
299 *ret_support = SLEEP_ALARM_NOT_SUPPORTED;
300 return false;
301 }
302
303 FOREACH_ELEMENT(i, operations) {
304 r = sleep_supported_internal(sleep_config, *i, /* check_allowed = */ false, &support);
305 if (r < 0)
306 return r;
307 if (r == 0) {
308 log_debug("Sleep operation %s is not supported, can't perform %s.",
309 sleep_operation_to_string(*i), sleep_operation_to_string(SLEEP_SUSPEND_THEN_HIBERNATE));
310 *ret_support = support;
311 return false;
312 }
313 }
314
315 assert(support == SLEEP_SUPPORTED);
316 *ret_support = support;
317
318 return true;
319 }
320
321 static int sleep_supported_internal(
322 const SleepConfig *sleep_config,
323 SleepOperation operation,
324 bool check_allowed,
325 SleepSupport *ret_support) {
326
327 int r;
328
329 assert(sleep_config);
330 assert(operation >= 0);
331 assert(operation < _SLEEP_OPERATION_MAX);
332 assert(ret_support);
333
334 if (check_allowed && !sleep_config->allow[operation]) {
335 log_debug("Sleep operation %s is disabled by configuration.", sleep_operation_to_string(operation));
336 *ret_support = SLEEP_DISABLED;
337 return false;
338 }
339
340 if (operation == SLEEP_SUSPEND_THEN_HIBERNATE)
341 return s2h_supported(sleep_config, ret_support);
342
343 assert(operation < _SLEEP_OPERATION_CONFIG_MAX);
344
345 r = sleep_state_supported(sleep_config->states[operation]);
346 if (r == -ENOMSG) {
347 *ret_support = SLEEP_NOT_CONFIGURED;
348 return false;
349 }
350 if (r < 0)
351 return r;
352 if (r == 0) {
353 *ret_support = SLEEP_STATE_OR_MODE_NOT_SUPPORTED;
354 return false;
355 }
356
357 if (sleep_needs_mem_sleep(sleep_config, operation)) {
358 r = sleep_mode_supported("/sys/power/mem_sleep", sleep_config->mem_modes);
359 if (r < 0)
360 return r;
361 if (r == 0) {
362 *ret_support = SLEEP_STATE_OR_MODE_NOT_SUPPORTED;
363 return false;
364 }
365 }
366
367 if (SLEEP_OPERATION_IS_HIBERNATION(operation)) {
368 r = sleep_mode_supported("/sys/power/disk", sleep_config->modes[operation]);
369 if (r < 0)
370 return r;
371 if (r == 0) {
372 *ret_support = SLEEP_STATE_OR_MODE_NOT_SUPPORTED;
373 return false;
374 }
375
376 r = hibernation_is_safe();
377 switch (r) {
378
379 case -ENOTRECOVERABLE:
380 *ret_support = SLEEP_RESUME_NOT_SUPPORTED;
381 return false;
382
383 case -ESTALE:
384 *ret_support = SLEEP_RESUME_DEVICE_MISSING;
385 return false;
386
387 case -ENOMEDIUM:
388 *ret_support = SLEEP_RESUME_MISCONFIGURED;
389 return false;
390
391 case -ENOSPC:
392 *ret_support = SLEEP_NOT_ENOUGH_SWAP_SPACE;
393 return false;
394
395 default:
396 if (r < 0)
397 return r;
398 }
399 } else
400 assert(!sleep_config->modes[operation]);
401
402 *ret_support = SLEEP_SUPPORTED;
403 return true;
404 }
405
406 int sleep_supported_full(SleepOperation operation, SleepSupport *ret_support) {
407 _cleanup_(sleep_config_freep) SleepConfig *sleep_config = NULL;
408 SleepSupport support;
409 int r;
410
411 assert(operation >= 0);
412 assert(operation < _SLEEP_OPERATION_MAX);
413
414 r = parse_sleep_config(&sleep_config);
415 if (r < 0)
416 return r;
417
418 r = sleep_supported_internal(sleep_config, operation, /* check_allowed = */ true, &support);
419 if (r < 0)
420 return r;
421
422 assert((r > 0) == (support == SLEEP_SUPPORTED));
423
424 if (ret_support)
425 *ret_support = support;
426
427 return r;
428 }