diff options
Diffstat (limited to 'src/login/logind-action.c')
-rw-r--r-- | src/login/logind-action.c | 145 |
1 files changed, 131 insertions, 14 deletions
diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 8269f52..9325d91 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "conf-parser.h" #include "format-util.h" @@ -133,6 +134,63 @@ const HandleActionData* handle_action_lookup(HandleAction action) { return &handle_action_data_table[action]; } +static bool handle_action_sleep_supported(HandleAction action) { + assert(HANDLE_ACTION_IS_SLEEP(action) && action != HANDLE_SLEEP); + return sleep_supported(ASSERT_PTR(handle_action_lookup(action))->sleep_operation) > 0; +} + +/* The order in which we try each sleep operation. We should typically prefer operations without a delay, + * i.e. s2h and suspend, and use hibernation at last since it requires minimum hardware support. + * hybrid-sleep is disabled by default, and thus should be ordered before suspend if manually chosen by user, + * since it implies suspend and will probably never be selected by us otherwise. */ +static const HandleAction sleep_actions[] = { + HANDLE_SUSPEND_THEN_HIBERNATE, + HANDLE_HYBRID_SLEEP, + HANDLE_SUSPEND, + HANDLE_HIBERNATE, +}; + +int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***ret) { + _cleanup_strv_free_ char **actions = NULL; + int r; + + assert(ret); + + FOREACH_ELEMENT(i, sleep_actions) + if (FLAGS_SET(mask, 1U << *i)) { + r = strv_extend(&actions, handle_action_to_string(*i)); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(actions); + return 0; +} + +HandleAction handle_action_sleep_select(Manager *m) { + assert(m); + + FOREACH_ELEMENT(i, sleep_actions) { + HandleActionSleepMask action_mask = 1U << *i; + const HandleActionData *a; + _cleanup_free_ char *load_state = NULL; + + if (!FLAGS_SET(m->handle_action_sleep_mask, action_mask)) + continue; + + a = ASSERT_PTR(handle_action_lookup(*i)); + + if (sleep_supported(a->sleep_operation) <= 0) + continue; + + (void) unit_load_state(m->bus, a->target, &load_state); + if (streq_ptr(load_state, "loaded")) + return *i; + } + + return _HANDLE_ACTION_INVALID; +} + static int handle_action_execute( Manager *m, HandleAction handle, @@ -158,6 +216,7 @@ static int handle_action_execute( int r; assert(m); + assert(!IN_SET(handle, HANDLE_IGNORE, HANDLE_LOCK, HANDLE_SLEEP)); if (handle == HANDLE_KEXEC && access(KEXEC, X_OK) < 0) return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), @@ -169,7 +228,7 @@ static int handle_action_execute( handle_action_to_string(m->delayed_action->handle), handle_action_to_string(handle)); - inhibit_operation = handle_action_lookup(handle)->inhibit_what; + inhibit_operation = ASSERT_PTR(handle_action_lookup(handle))->inhibit_what; /* If the actual operation is inhibited, warn and fail */ if (inhibit_what_is_valid(inhibit_operation) && @@ -208,21 +267,21 @@ static int handle_action_sleep_execute( bool ignore_inhibited, bool is_edge) { - bool supported; - assert(m); assert(HANDLE_ACTION_IS_SLEEP(handle)); - if (handle == HANDLE_SUSPEND) - supported = sleep_supported(SLEEP_SUSPEND) > 0; - else if (handle == HANDLE_HIBERNATE) - supported = sleep_supported(SLEEP_HIBERNATE) > 0; - else if (handle == HANDLE_HYBRID_SLEEP) - supported = sleep_supported(SLEEP_HYBRID_SLEEP) > 0; - else if (handle == HANDLE_SUSPEND_THEN_HIBERNATE) - supported = sleep_supported(SLEEP_SUSPEND_THEN_HIBERNATE) > 0; - else - assert_not_reached(); + if (handle == HANDLE_SLEEP) { + HandleAction a; + + a = handle_action_sleep_select(m); + if (a < 0) + return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "None of the configured sleep operations are supported, ignoring."); + + return handle_action_sleep_execute(m, a, ignore_inhibited, is_edge); + } + + bool supported = handle_action_sleep_supported(handle); if (!supported && handle != HANDLE_SUSPEND) { supported = sleep_supported(SLEEP_SUSPEND) > 0; @@ -302,8 +361,9 @@ static const char* const handle_action_verb_table[_HANDLE_ACTION_MAX] = { [HANDLE_SOFT_REBOOT] = "soft-reboot", [HANDLE_SUSPEND] = "suspend", [HANDLE_HIBERNATE] = "hibernate", - [HANDLE_HYBRID_SLEEP] = "enter hybrid sleep", + [HANDLE_HYBRID_SLEEP] = "hybrid sleep", [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend and later hibernate", + [HANDLE_SLEEP] = "sleep", [HANDLE_FACTORY_RESET] = "perform a factory reset", [HANDLE_LOCK] = "be locked", }; @@ -323,9 +383,66 @@ static const char* const handle_action_table[_HANDLE_ACTION_MAX] = { [HANDLE_HIBERNATE] = "hibernate", [HANDLE_HYBRID_SLEEP] = "hybrid-sleep", [HANDLE_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate", + [HANDLE_SLEEP] = "sleep", [HANDLE_FACTORY_RESET] = "factory-reset", [HANDLE_LOCK] = "lock", }; DEFINE_STRING_TABLE_LOOKUP(handle_action, HandleAction); DEFINE_CONFIG_PARSE_ENUM(config_parse_handle_action, handle_action, HandleAction, "Failed to parse handle action setting"); + +int config_parse_handle_action_sleep( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + HandleActionSleepMask *mask = ASSERT_PTR(data); + _cleanup_strv_free_ char **actions = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) + goto empty; + + if (strv_split_full(&actions, rvalue, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE) < 0) + return log_oom(); + + *mask = 0; + + STRV_FOREACH(action, actions) { + HandleAction a; + + a = handle_action_from_string(*action); + if (a < 0) { + log_syntax(unit, LOG_WARNING, filename, line, a, + "Failed to parse SleepOperation '%s', ignoring: %m", *action); + continue; + } + + if (!HANDLE_ACTION_IS_SLEEP(a) || a == HANDLE_SLEEP) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "HandleAction '%s' is not a sleep operation, ignoring: %m", *action); + continue; + } + + *mask |= 1U << a; + } + + if (*mask == 0) + goto empty; + + return 0; + +empty: + *mask = HANDLE_ACTION_SLEEP_MASK_DEFAULT; + return 0; +} |