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"),
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
));
81 int parse_sleep_config(SleepConfig
**ret
) {
82 _cleanup_(sleep_config_freep
) SleepConfig
*sc
= NULL
;
83 int allow_suspend
= -1, allow_hibernate
= -1, allow_s2h
= -1, allow_hybrid_sleep
= -1;
87 sc
= new(SleepConfig
, 1);
92 .hibernate_delay_usec
= USEC_INFINITY
,
95 const ConfigTableItem items
[] = {
96 { "Sleep", "AllowSuspend", config_parse_tristate
, 0, &allow_suspend
},
97 { "Sleep", "AllowHibernation", config_parse_tristate
, 0, &allow_hibernate
},
98 { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate
, 0, &allow_s2h
},
99 { "Sleep", "AllowHybridSleep", config_parse_tristate
, 0, &allow_hybrid_sleep
},
101 { "Sleep", "SuspendState", config_parse_strv
, 0, sc
->states
+ SLEEP_SUSPEND
},
102 { "Sleep", "SuspendMode", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
104 { "Sleep", "HibernateState", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
105 { "Sleep", "HibernateMode", config_parse_strv
, 0, sc
->modes
+ SLEEP_HIBERNATE
},
107 { "Sleep", "HybridSleepState", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
108 { "Sleep", "HybridSleepMode", config_parse_warn_compat
, DISABLED_LEGACY
, NULL
},
110 { "Sleep", "HibernateDelaySec", config_parse_sec
, 0, &sc
->hibernate_delay_usec
},
111 { "Sleep", "SuspendEstimationSec", config_parse_sec
, 0, &sc
->suspend_estimation_usec
},
115 (void) config_parse_config_file("sleep.conf", "Sleep\0",
116 config_item_table_lookup
, items
,
117 CONFIG_PARSE_WARN
, NULL
);
119 /* use default values unless set */
120 sc
->allow
[SLEEP_SUSPEND
] = allow_suspend
!= 0;
121 sc
->allow
[SLEEP_HIBERNATE
] = allow_hibernate
!= 0;
122 sc
->allow
[SLEEP_HYBRID_SLEEP
] = allow_hybrid_sleep
>= 0 ? allow_hybrid_sleep
123 : (allow_suspend
!= 0 && allow_hibernate
!= 0);
124 sc
->allow
[SLEEP_SUSPEND_THEN_HIBERNATE
] = allow_s2h
>= 0 ? allow_s2h
125 : (allow_suspend
!= 0 && allow_hibernate
!= 0);
127 for (SleepOperation i
= 0; i
< _SLEEP_OPERATION_CONFIG_MAX
; i
++) {
128 if (!sc
->states
[i
] && sleep_default_state_table
[i
]) {
129 sc
->states
[i
] = strv_copy(sleep_default_state_table
[i
]);
134 if (!sc
->modes
[i
] && sleep_default_mode_table
[i
]) {
135 sc
->modes
[i
] = strv_copy(sleep_default_mode_table
[i
]);
141 if (sc
->suspend_estimation_usec
== 0)
142 sc
->suspend_estimation_usec
= DEFAULT_SUSPEND_ESTIMATION_USEC
;
144 sleep_config_validate_state_and_mode(sc
);
150 int sleep_state_supported(char **states
) {
151 _cleanup_free_
char *supported_sysfs
= NULL
;
155 if (strv_isempty(states
))
156 return log_debug_errno(SYNTHETIC_ERRNO(ENOMSG
), "No sleep state configured.");
158 if (access("/sys/power/state", W_OK
) < 0)
159 return log_debug_errno(errno
, "/sys/power/state is not writable: %m");
161 r
= read_one_line_file("/sys/power/state", &supported_sysfs
);
163 return log_debug_errno(r
, "Failed to read /sys/power/state: %m");
165 r
= string_contains_word_strv(supported_sysfs
, NULL
, states
, &found
);
167 return log_debug_errno(r
, "Failed to parse /sys/power/state: %m");
169 log_debug("Sleep state '%s' is supported by kernel.", found
);
174 _cleanup_free_
char *joined
= strv_join(states
, " ");
175 log_debug("None of the configured sleep states are supported by kernel: %s", strnull(joined
));
180 int sleep_mode_supported(char **modes
) {
181 _cleanup_free_
char *supported_sysfs
= NULL
;
184 /* Unlike state, kernel has its own default choice if not configured */
185 if (strv_isempty(modes
)) {
186 log_debug("No sleep mode configured, using kernel default.");
190 if (access("/sys/power/disk", W_OK
) < 0)
191 return log_debug_errno(errno
, "/sys/power/disk is not writable: %m");
193 r
= read_one_line_file("/sys/power/disk", &supported_sysfs
);
195 return log_debug_errno(r
, "Failed to read /sys/power/disk: %m");
197 for (const char *p
= supported_sysfs
;;) {
198 _cleanup_free_
char *word
= NULL
;
202 r
= extract_first_word(&p
, &word
, NULL
, 0);
204 return log_debug_errno(r
, "Failed to parse /sys/power/disk: %m");
211 if (mode
[0] == '[' && mode
[l
- 1] == ']') {
216 if (strv_contains(modes
, mode
)) {
217 log_debug("Disk sleep mode '%s' is supported by kernel.", mode
);
223 _cleanup_free_
char *joined
= strv_join(modes
, " ");
224 log_debug("None of the configured hibernation power modes are supported by kernel: %s", strnull(joined
));
229 static int sleep_supported_internal(
230 const SleepConfig
*sleep_config
,
231 SleepOperation operation
,
233 SleepSupport
*ret_support
);
235 static int s2h_supported(const SleepConfig
*sleep_config
, SleepSupport
*ret_support
) {
237 static const SleepOperation operations
[] = {
242 SleepSupport support
;
245 assert(sleep_config
);
248 if (!clock_supported(CLOCK_BOOTTIME_ALARM
)) {
249 log_debug("CLOCK_BOOTTIME_ALARM is not supported, can't perform %s.", sleep_operation_to_string(SLEEP_SUSPEND_THEN_HIBERNATE
));
250 *ret_support
= SLEEP_ALARM_NOT_SUPPORTED
;
254 FOREACH_ARRAY(i
, operations
, ELEMENTSOF(operations
)) {
255 r
= sleep_supported_internal(sleep_config
, *i
, /* check_allowed = */ false, &support
);
259 log_debug("Sleep operation %s is not supported, can't perform %s.",
260 sleep_operation_to_string(*i
), sleep_operation_to_string(SLEEP_SUSPEND_THEN_HIBERNATE
));
261 *ret_support
= support
;
266 assert(support
== SLEEP_SUPPORTED
);
267 *ret_support
= support
;
272 static int sleep_supported_internal(
273 const SleepConfig
*sleep_config
,
274 SleepOperation operation
,
276 SleepSupport
*ret_support
) {
280 assert(sleep_config
);
281 assert(operation
>= 0);
282 assert(operation
< _SLEEP_OPERATION_MAX
);
285 if (check_allowed
&& !sleep_config
->allow
[operation
]) {
286 log_debug("Sleep operation %s is disabled by configuration.", sleep_operation_to_string(operation
));
287 *ret_support
= SLEEP_DISABLED
;
291 if (operation
== SLEEP_SUSPEND_THEN_HIBERNATE
)
292 return s2h_supported(sleep_config
, ret_support
);
294 assert(operation
< _SLEEP_OPERATION_CONFIG_MAX
);
296 r
= sleep_state_supported(sleep_config
->states
[operation
]);
298 *ret_support
= SLEEP_NOT_CONFIGURED
;
304 *ret_support
= SLEEP_STATE_OR_MODE_NOT_SUPPORTED
;
308 if (IN_SET(operation
, SLEEP_HIBERNATE
, SLEEP_HYBRID_SLEEP
)) {
309 r
= sleep_mode_supported(sleep_config
->modes
[operation
]);
313 *ret_support
= SLEEP_STATE_OR_MODE_NOT_SUPPORTED
;
317 r
= hibernation_is_safe();
318 if (r
== -ENOTRECOVERABLE
) {
319 *ret_support
= SLEEP_RESUME_NOT_SUPPORTED
;
323 *ret_support
= SLEEP_NOT_ENOUGH_SWAP_SPACE
;
329 assert(!sleep_config
->modes
[operation
]);
331 *ret_support
= SLEEP_SUPPORTED
;
335 int sleep_supported_full(SleepOperation operation
, SleepSupport
*ret_support
) {
336 _cleanup_(sleep_config_freep
) SleepConfig
*sleep_config
= NULL
;
337 SleepSupport support
;
340 assert(operation
>= 0);
341 assert(operation
< _SLEEP_OPERATION_MAX
);
343 r
= parse_sleep_config(&sleep_config
);
347 r
= sleep_supported_internal(sleep_config
, operation
, /* check_allowed = */ true, &support
);
351 assert((r
> 0) == (support
== SLEEP_SUPPORTED
));
354 *ret_support
= support
;