1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "conf-parser.h"
7 #include "extract-word.h"
9 #include "hibernate-util.h"
11 #include "sleep-config.h"
12 #include "string-table.h"
13 #include "string-util.h"
15 #include "time-util.h"
17 #define DEFAULT_SUSPEND_ESTIMATION_USEC (1 * USEC_PER_HOUR)
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",
26 DEFINE_STRING_TABLE_LOOKUP(sleep_operation
, SleepOperation
);
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"),
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"),
40 SleepConfig
* sleep_config_free(SleepConfig
*sc
) {
44 for (SleepOperation i
= 0; i
< _SLEEP_OPERATION_CONFIG_MAX
; i
++) {
45 strv_free(sc
->states
[i
]);
46 strv_free(sc
->modes
[i
]);
49 strv_free(sc
->mem_modes
);
54 static int config_parse_sleep_mode(
59 unsigned section_line
,
66 char ***sv
= ASSERT_PTR(data
);
67 _cleanup_strv_free_
char **modes
= NULL
;
74 if (isempty(rvalue
)) {
75 modes
= strv_new(NULL
);
79 r
= strv_split_full(&modes
, rvalue
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RETAIN_ESCAPE
);
84 return strv_free_and_replace(*sv
, modes
);
87 static void sleep_config_validate_state_and_mode(SleepConfig
*sc
) {
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
));
99 assert(!sc
->modes
[SLEEP_SUSPEND
]);
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
));
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;
114 sc
= new(SleepConfig
, 1);
118 *sc
= (SleepConfig
) {
119 .hibernate_delay_usec
= USEC_INFINITY
,
120 .hibernate_on_ac_power
= true,
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
},
129 { "Sleep", "SuspendState", config_parse_strv
, 0, sc
->states
+ SLEEP_SUSPEND
},
130 { "Sleep", "SuspendMode", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
132 { "Sleep", "HibernateState", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
133 { "Sleep", "HibernateMode", config_parse_sleep_mode
, 0, sc
->modes
+ SLEEP_HIBERNATE
},
135 { "Sleep", "HybridSleepState", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
136 { "Sleep", "HybridSleepMode", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
138 { "Sleep", "MemorySleepMode", config_parse_sleep_mode
, 0, &sc
->mem_modes
},
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
},
146 (void) config_parse_standard_file_with_dropins(
147 "systemd/sleep.conf",
149 config_item_table_lookup
, items
,
151 /* userdata= */ NULL
);
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);
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
]);
168 if (!sc
->modes
[i
] && sleep_default_mode_table
[i
]) {
169 sc
->modes
[i
] = strv_copy(sleep_default_mode_table
[i
]);
175 if (sc
->suspend_estimation_usec
== 0)
176 sc
->suspend_estimation_usec
= DEFAULT_SUSPEND_ESTIMATION_USEC
;
178 sleep_config_validate_state_and_mode(sc
);
184 bool sleep_needs_mem_sleep(const SleepConfig
*sc
, SleepOperation operation
) {
186 assert(operation
>= 0 && operation
< _SLEEP_OPERATION_CONFIG_MAX
);
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). */
192 return strv_contains(sc
->states
[operation
], "mem") ||
193 strv_contains(sc
->modes
[operation
], "suspend");
196 int sleep_state_supported(char * const *states
) {
197 _cleanup_free_
char *supported_sysfs
= NULL
;
201 if (strv_isempty(states
))
202 return log_debug_errno(SYNTHETIC_ERRNO(ENOMSG
), "No sleep state configured.");
204 if (access("/sys/power/state", W_OK
) < 0)
205 return log_debug_errno(errno
, "/sys/power/state is not writable: %m");
207 r
= read_one_line_file("/sys/power/state", &supported_sysfs
);
209 return log_debug_errno(r
, "Failed to read /sys/power/state: %m");
211 r
= string_contains_word_strv(supported_sysfs
, NULL
, states
, &found
);
213 return log_debug_errno(r
, "Failed to parse /sys/power/state: %m");
215 log_debug("Sleep state '%s' is supported by kernel.", found
);
220 _cleanup_free_
char *joined
= strv_join(states
, " ");
221 log_debug("None of the configured sleep states are supported by kernel: %s", strnull(joined
));
226 int sleep_mode_supported(const char *path
, char * const *modes
) {
227 _cleanup_free_
char *supported_sysfs
= NULL
;
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
);
238 if (access(path
, W_OK
) < 0)
239 return log_debug_errno(errno
, "%s is not writable: %m", path
);
241 r
= read_one_line_file(path
, &supported_sysfs
);
243 return log_debug_errno(r
, "Failed to read %s: %m", path
);
245 for (const char *p
= supported_sysfs
;;) {
246 _cleanup_free_
char *word
= NULL
;
250 r
= extract_first_word(&p
, &word
, NULL
, 0);
252 return log_debug_errno(r
, "Failed to parse %s: %m", path
);
259 if (mode
[0] == '[' && mode
[l
- 1] == ']') {
264 if (strv_contains(modes
, mode
)) {
265 log_debug("Sleep mode '%s' is supported by kernel (%s).", mode
, path
);
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
));
278 static int sleep_supported_internal(
279 const SleepConfig
*sleep_config
,
280 SleepOperation operation
,
282 SleepSupport
*ret_support
);
284 static int s2h_supported(const SleepConfig
*sleep_config
, SleepSupport
*ret_support
) {
286 static const SleepOperation operations
[] = {
291 SleepSupport support
;
294 assert(sleep_config
);
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
;
303 FOREACH_ELEMENT(i
, operations
) {
304 r
= sleep_supported_internal(sleep_config
, *i
, /* check_allowed = */ false, &support
);
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
;
315 assert(support
== SLEEP_SUPPORTED
);
316 *ret_support
= support
;
321 static int sleep_supported_internal(
322 const SleepConfig
*sleep_config
,
323 SleepOperation operation
,
325 SleepSupport
*ret_support
) {
329 assert(sleep_config
);
330 assert(operation
>= 0);
331 assert(operation
< _SLEEP_OPERATION_MAX
);
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
;
340 if (operation
== SLEEP_SUSPEND_THEN_HIBERNATE
)
341 return s2h_supported(sleep_config
, ret_support
);
343 assert(operation
< _SLEEP_OPERATION_CONFIG_MAX
);
345 r
= sleep_state_supported(sleep_config
->states
[operation
]);
347 *ret_support
= SLEEP_NOT_CONFIGURED
;
353 *ret_support
= SLEEP_STATE_OR_MODE_NOT_SUPPORTED
;
357 if (sleep_needs_mem_sleep(sleep_config
, operation
)) {
358 r
= sleep_mode_supported("/sys/power/mem_sleep", sleep_config
->mem_modes
);
362 *ret_support
= SLEEP_STATE_OR_MODE_NOT_SUPPORTED
;
367 if (SLEEP_OPERATION_IS_HIBERNATION(operation
)) {
368 r
= sleep_mode_supported("/sys/power/disk", sleep_config
->modes
[operation
]);
372 *ret_support
= SLEEP_STATE_OR_MODE_NOT_SUPPORTED
;
376 r
= hibernation_is_safe();
379 case -ENOTRECOVERABLE
:
380 *ret_support
= SLEEP_RESUME_NOT_SUPPORTED
;
384 *ret_support
= SLEEP_RESUME_DEVICE_MISSING
;
388 *ret_support
= SLEEP_RESUME_MISCONFIGURED
;
392 *ret_support
= SLEEP_NOT_ENOUGH_SWAP_SPACE
;
400 assert(!sleep_config
->modes
[operation
]);
402 *ret_support
= SLEEP_SUPPORTED
;
406 int sleep_supported_full(SleepOperation operation
, SleepSupport
*ret_support
) {
407 _cleanup_(sleep_config_freep
) SleepConfig
*sleep_config
= NULL
;
408 SleepSupport support
;
411 assert(operation
>= 0);
412 assert(operation
< _SLEEP_OPERATION_MAX
);
414 r
= parse_sleep_config(&sleep_config
);
418 r
= sleep_supported_internal(sleep_config
, operation
, /* check_allowed = */ true, &support
);
422 assert((r
> 0) == (support
== SLEEP_SUPPORTED
));
425 *ret_support
= support
;