1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "conf-parser.h"
8 #include "device-util.h"
9 #include "devnum-util.h"
10 #include "errno-util.h"
13 #include "hibernate-util.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"
23 #include "time-util.h"
25 #define DEFAULT_SUSPEND_ESTIMATION_USEC (1 * USEC_PER_HOUR)
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",
34 DEFINE_STRING_TABLE_LOOKUP(sleep_operation
, SleepOperation
);
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"),
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"),
48 SleepConfig
* sleep_config_free(SleepConfig
*sc
) {
52 for (SleepOperation i
= 0; i
< _SLEEP_OPERATION_CONFIG_MAX
; i
++) {
53 strv_free(sc
->states
[i
]);
54 strv_free(sc
->modes
[i
]);
60 static void sleep_config_validate_state_and_mode(SleepConfig
*sc
) {
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
));
72 assert(!sc
->modes
[SLEEP_SUSPEND
]);
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
));
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
));
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;
91 sc
= new(SleepConfig
, 1);
96 .hibernate_delay_usec
= USEC_INFINITY
,
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
},
105 { "Sleep", "SuspendState", config_parse_strv
, 0, sc
->states
+ SLEEP_SUSPEND
},
106 { "Sleep", "SuspendMode", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
108 { "Sleep", "HibernateState", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
109 { "Sleep", "HibernateMode", config_parse_strv
, 0, sc
->modes
+ SLEEP_HIBERNATE
},
111 { "Sleep", "HybridSleepState", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
112 { "Sleep", "HybridSleepMode", config_parse_strv
, 0, sc
->modes
+ SLEEP_HYBRID_SLEEP
},
114 { "Sleep", "HibernateDelaySec", config_parse_sec
, 0, &sc
->hibernate_delay_usec
},
115 { "Sleep", "SuspendEstimationSec", config_parse_sec
, 0, &sc
->suspend_estimation_usec
},
119 (void) config_parse_config_file("sleep.conf", "Sleep\0",
120 config_item_table_lookup
, items
,
121 CONFIG_PARSE_WARN
, NULL
);
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);
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
]);
138 if (!sc
->modes
[i
] && sleep_default_mode_table
[i
]) {
139 sc
->modes
[i
] = strv_copy(sleep_default_mode_table
[i
]);
145 if (sc
->suspend_estimation_usec
== 0)
146 sc
->suspend_estimation_usec
= DEFAULT_SUSPEND_ESTIMATION_USEC
;
148 sleep_config_validate_state_and_mode(sc
);
154 int sleep_state_supported(char **states
) {
155 _cleanup_free_
char *supported_sysfs
= NULL
;
159 if (strv_isempty(states
))
160 return log_debug_errno(SYNTHETIC_ERRNO(ENOMSG
), "No sleep state configured.");
162 if (access("/sys/power/state", W_OK
) < 0)
163 return log_debug_errno(errno
, "/sys/power/state is not writable: %m");
165 r
= read_one_line_file("/sys/power/state", &supported_sysfs
);
167 return log_debug_errno(r
, "Failed to read /sys/power/state: %m");
169 r
= string_contains_word_strv(supported_sysfs
, NULL
, states
, &found
);
171 return log_debug_errno(r
, "Failed to parse /sys/power/state: %m");
173 log_debug("Sleep state '%s' is supported by kernel.", found
);
178 _cleanup_free_
char *joined
= strv_join(states
, " ");
179 log_debug("None of the configured sleep states are supported by kernel: %s", strnull(joined
));
184 int sleep_mode_supported(char **modes
) {
185 _cleanup_free_
char *supported_sysfs
= NULL
;
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.");
194 if (access("/sys/power/disk", W_OK
) < 0)
195 return log_debug_errno(errno
, "/sys/power/disk is not writable: %m");
197 r
= read_one_line_file("/sys/power/disk", &supported_sysfs
);
199 return log_debug_errno(r
, "Failed to read /sys/power/disk: %m");
201 for (const char *p
= supported_sysfs
;;) {
202 _cleanup_free_
char *word
= NULL
;
206 r
= extract_first_word(&p
, &word
, NULL
, 0);
208 return log_debug_errno(r
, "Failed to parse /sys/power/disk: %m");
215 if (mode
[0] == '[' && mode
[l
- 1] == ']') {
220 if (strv_contains(modes
, mode
)) {
221 log_debug("Disk sleep mode '%s' is supported by kernel.", mode
);
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
));
233 static int sleep_supported_internal(
234 const SleepConfig
*sleep_config
,
235 SleepOperation operation
,
237 SleepSupport
*ret_support
);
239 static int s2h_supported(const SleepConfig
*sleep_config
, SleepSupport
*ret_support
) {
241 static const SleepOperation operations
[] = {
246 SleepSupport support
;
249 assert(sleep_config
);
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
;
258 FOREACH_ARRAY(i
, operations
, ELEMENTSOF(operations
)) {
259 r
= sleep_supported_internal(sleep_config
, *i
, /* check_allowed = */ false, &support
);
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
;
270 assert(support
== SLEEP_SUPPORTED
);
271 *ret_support
= support
;
276 static int sleep_supported_internal(
277 const SleepConfig
*sleep_config
,
278 SleepOperation operation
,
280 SleepSupport
*ret_support
) {
284 assert(sleep_config
);
285 assert(operation
>= 0);
286 assert(operation
< _SLEEP_OPERATION_MAX
);
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
;
295 if (operation
== SLEEP_SUSPEND_THEN_HIBERNATE
)
296 return s2h_supported(sleep_config
, ret_support
);
298 assert(operation
< _SLEEP_OPERATION_CONFIG_MAX
);
300 r
= sleep_state_supported(sleep_config
->states
[operation
]);
302 *ret_support
= SLEEP_NOT_CONFIGURED
;
308 *ret_support
= SLEEP_STATE_OR_MODE_NOT_SUPPORTED
;
312 if (IN_SET(operation
, SLEEP_HIBERNATE
, SLEEP_HYBRID_SLEEP
)) {
313 r
= sleep_mode_supported(sleep_config
->modes
[operation
]);
317 *ret_support
= SLEEP_STATE_OR_MODE_NOT_SUPPORTED
;
321 r
= hibernation_is_safe();
322 if (r
== -ENOTRECOVERABLE
) {
323 *ret_support
= SLEEP_RESUME_NOT_SUPPORTED
;
327 *ret_support
= SLEEP_NOT_ENOUGH_SWAP_SPACE
;
333 assert(!sleep_config
->modes
[operation
]);
335 *ret_support
= SLEEP_SUPPORTED
;
339 int sleep_supported_full(SleepOperation operation
, SleepSupport
*ret_support
) {
340 _cleanup_(sleep_config_freep
) SleepConfig
*sleep_config
= NULL
;
341 SleepSupport support
;
344 assert(operation
>= 0);
345 assert(operation
< _SLEEP_OPERATION_MAX
);
347 r
= parse_sleep_config(&sleep_config
);
351 r
= sleep_supported_internal(sleep_config
, operation
, /* check_allowed = */ true, &support
);
355 assert((r
> 0) == (support
== SLEEP_SUPPORTED
));
358 *ret_support
= support
;