summaryrefslogtreecommitdiffstats
path: root/src/login/logind-action.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/login/logind-action.c')
-rw-r--r--src/login/logind-action.c145
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;
+}