From: Mike Yuan Date: Thu, 2 Nov 2023 17:11:55 +0000 (+0800) Subject: logind: introduce Sleep() call and action that automatically choose a sleep operation X-Git-Tag: v256-rc1~1570^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cd4dd90b99ac5617431ce4f0ca1e9089197c85fd;p=thirdparty%2Fsystemd.git logind: introduce Sleep() call and action that automatically choose a sleep operation --- diff --git a/TODO b/TODO index e8b62bfe1fd..fc375c9460d 100644 --- a/TODO +++ b/TODO @@ -233,13 +233,6 @@ Features: dir. Similar for $XDG_RUNTIME_DIR. Start project@%i.target. Use LogField= to add a field identifying the project. -* logind: add a new dbus call Sleep() which automatically redirects to one of - Suspend(), Hibernate(), SuspendThenHibernate() depending on what is - available, and also subject to some local configuration in - logind.conf. Should default to SuspendThenHibernate() if available, and then - fallback to Suspend() and finally Hibernate() if not. Then expose this as - "systemctl sleep", and tell DEs to default to this. - * in sd-boot and sd-stub measure the SMBIOS vendor strings to some PCR (at least some subset of them that look like systemd stuff), because apparently some firmware does not, but systemd honours it. avoid duplicate measurement diff --git a/man/logind.conf.xml b/man/logind.conf.xml index 72f657ced4e..2b5c11b9166 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -143,18 +143,10 @@ IdleAction= Configures the action to take when the system - is idle. Takes one of - ignore, - poweroff, - reboot, - halt, - kexec, - suspend, - hibernate, - hybrid-sleep, - suspend-then-hibernate, and - lock. - Defaults to ignore. + is idle. Takes one of ignore, poweroff, reboot, + halt, kexec, suspend, hibernate, + hybrid-sleep, suspend-then-hibernate, sleep, + and lock. Defaults to ignore. Note that this requires that user sessions correctly report the idle status to the system. The system will execute @@ -200,6 +192,20 @@ + + SleepOperation= + + Takes a list of sleep operations. Possible values are suspend, + hibernate, hybrid-sleep, and suspend-then-hibernate. + Controls the candidate sleep operations for the sleep action. When sleep + action is performed, the specified sleep operations are checked in a fixed order (suspend-then-hibernate + → hybrid-sleep → suspend → hibernate), and + the first one supported by the machine is used to put the system into sleep. Defaults to + suspend-then-hibernate suspend hibernate. + + + + HandlePowerKey= HandlePowerKeyLongPress= @@ -215,10 +221,9 @@ Controls how logind shall handle the system power, reboot and sleep keys and the lid switch to trigger actions such as system power-off, reboot or suspend. Can be one of - ignore, poweroff, reboot, - halt, kexec, suspend, - hibernate, hybrid-sleep, - suspend-then-hibernate, lock, and + ignore, poweroff, reboot, halt, + kexec, suspend, hibernate, hybrid-sleep, + suspend-then-hibernate, sleep, lock, and factory-reset. If ignore, systemd-logind will never handle these keys. If lock, all running sessions will be screen-locked; otherwise, the specified action will be taken in the respective event. Only input devices with the diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml index 877bf463a07..12d8695c27d 100644 --- a/man/org.freedesktop.login1.xml +++ b/man/org.freedesktop.login1.xml @@ -141,6 +141,7 @@ node /org/freedesktop/login1 { HybridSleepWithFlags(in t flags); SuspendThenHibernate(in b interactive); SuspendThenHibernateWithFlags(in t flags); + Sleep(in t flags); CanPowerOff(out s result); CanReboot(out s result); CanHalt(out s result); @@ -148,6 +149,7 @@ node /org/freedesktop/login1 { CanHibernate(out s result); CanHybridSleep(out s result); CanSuspendThenHibernate(out s result); + CanSleep(out s result); ScheduleShutdown(in s type, in t usec); CancelScheduledShutdown(out b cancelled); @@ -218,6 +220,8 @@ node /org/freedesktop/login1 { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly t UserStopDelayUSec = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as SleepOperation = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s HandlePowerKey = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s HandlePowerKeyLongPress = '...'; @@ -280,6 +284,8 @@ node /org/freedesktop/login1 { }; + + @@ -378,6 +384,8 @@ node /org/freedesktop/login1 { + + @@ -392,6 +400,8 @@ node /org/freedesktop/login1 { + + @@ -470,6 +480,8 @@ node /org/freedesktop/login1 { + + @@ -609,17 +621,20 @@ node /org/freedesktop/login1 { off, rebooted, halted (shut down without turning off power), suspended (the system state is saved to RAM and the CPU is turned off), or hibernated (the system state is saved to disk and the machine is powered down). HybridSleep() results in the system entering a hybrid-sleep mode, - i.e. the system is both hibernated and suspended. SuspendThenHibernate() results + i.e. the system is both hibernated and suspended. SuspendThenHibernate() results in the system being suspended, then later woken using an RTC timer and hibernated. The only argument is the polkit interactivity boolean interactive (see below). The main purpose of these calls is that they enforce polkit policy and hence allow powering off/rebooting/suspending/hibernating - even by unprivileged users. They also enforce inhibition locks for non-privileged users. UIs should - expose these calls as the primary mechanism to poweroff/reboot/suspend/hibernate the machine. Methods + even by unprivileged users. They also enforce inhibition locks for non-privileged users. + Sleep() automatically selects the most suitable sleep operation supported by the + machine. The candidate sleep operations to check for support can be configured through SleepOperation= + setting in logind.conf5. + UIs should expose these calls as the primary mechanism to poweroff/reboot/suspend/hibernate the machine. Methods PowerOffWithFlags(), RebootWithFlags(), HaltWithFlags(), SuspendWithFlags(), - HibernateWithFlags(), HybridSleepWithFlags() and - SuspendThenHibernateWithFlags() add flags to allow for - extendability, defined as follows: + HibernateWithFlags(), HybridSleepWithFlags(), + SuspendThenHibernateWithFlags(), and Sleep() take + flags to allow for extendability, defined as follows: #define SD_LOGIND_ROOT_CHECK_INHIBITORS (UINT64_C(1) << 0) #define SD_LOGIND_KEXEC_REBOOT (UINT64_C(1) << 1) @@ -649,20 +664,18 @@ node /org/freedesktop/login1 { systemctl1 for the corresponding command line interface. - CanPowerOff(), CanReboot(), - CanHalt(), CanSuspend(), CanHibernate(), - CanHybridSleep(), CanSuspendThenHibernate(), + CanPowerOff(), CanReboot(), CanHalt(), + CanSuspend(), CanHibernate(), CanHybridSleep(), + CanSuspendThenHibernate(), CanSleep(), CanRebootParameter(), CanRebootToFirmwareSetup(), - CanRebootToBootLoaderMenu(), and - CanRebootToBootLoaderEntry() test whether the system supports the respective - operation and whether the calling user is allowed to execute it. Returns one of na, - yes, no, and challenge. If - na is returned, the operation is not available because hardware, kernel, or drivers - do not support it. If yes is returned, the operation is supported and the user may - execute the operation without further authentication. If no is returned, the - operation is available but the user is not allowed to execute the operation. If - challenge is returned, the operation is available but only after - authorization. + CanRebootToBootLoaderMenu(), and CanRebootToBootLoaderEntry() + test whether the system supports the respective operation and whether the calling user is allowed to + execute it. Returns one of na, yes, no, and + challenge. If na is returned, the operation is not available because + hardware, kernel, or drivers do not support it. If yes is returned, the operation is + supported and the user may execute the operation without further authentication. If no + is returned, the operation is available but the user is not allowed to execute the operation. If + challenge is returned, the operation is available but only after authorization. ScheduleShutdown() schedules a shutdown operation type at time usec in microseconds since the UNIX epoch. type can be one @@ -819,8 +832,8 @@ node /org/freedesktop/login1 { org.freedesktop.login1.hibernate-ignore-inhibit, respectively depending on whether there are other sessions around or active inhibits are present. HybridSleep() and SuspendThenHibernate() - use the same privileges as Hibernate(). - SetRebootParameter() requires + use the same privileges as Hibernate(). Sleep() uses + the inhibits of the auto-selected sleep operation. SetRebootParameter() requires org.freedesktop.login1.set-reboot-parameter. SetRebootToFirmwareSetup requires @@ -1546,6 +1559,9 @@ node /org/freedesktop/login1/session/1 { StopIdleSessionUSec was added in version 252. PrepareForShutdownWithMetadata and CreateSessionWithPIDFD() were added in version 255. + Sleep(), + CanSleep(), and + SleepOperation were added in version 256. Session Objects diff --git a/src/login/logind-action.c b/src/login/logind-action.c index e678edd66fb..b9cb9b818dc 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -133,6 +133,49 @@ const HandleActionData* handle_action_lookup(HandleAction action) { return &handle_action_data_table[action]; } +/* 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_ARRAY(i, sleep_actions, ELEMENTSOF(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(HandleActionSleepMask mask) { + + FOREACH_ARRAY(i, sleep_actions, ELEMENTSOF(sleep_actions)) { + HandleActionSleepMask a = 1U << *i; + + if (!FLAGS_SET(mask, a)) + continue; + + if (sleep_supported(handle_action_lookup(*i)->sleep_operation) > 0) + return *i; + } + + return _HANDLE_ACTION_INVALID; +} + static int handle_action_execute( Manager *m, HandleAction handle, @@ -158,6 +201,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), @@ -208,11 +252,22 @@ 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_SLEEP) { + HandleAction a; + + a = handle_action_sleep_select(m->handle_action_sleep_mask); + 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; + if (handle == HANDLE_SUSPEND) supported = sleep_supported(SLEEP_SUSPEND) > 0; else if (handle == HANDLE_HIBERNATE) @@ -302,8 +357,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 +379,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; +} diff --git a/src/login/logind-action.h b/src/login/logind-action.h index dbca963f2d5..5ee86486ec3 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -5,6 +5,7 @@ typedef enum HandleAction { HANDLE_IGNORE, + HANDLE_POWEROFF, _HANDLE_ACTION_SHUTDOWN_FIRST = HANDLE_POWEROFF, HANDLE_REBOOT, @@ -12,20 +13,33 @@ typedef enum HandleAction { HANDLE_KEXEC, HANDLE_SOFT_REBOOT, _HANDLE_ACTION_SHUTDOWN_LAST = HANDLE_SOFT_REBOOT, + HANDLE_SUSPEND, _HANDLE_ACTION_SLEEP_FIRST = HANDLE_SUSPEND, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP, HANDLE_SUSPEND_THEN_HIBERNATE, - _HANDLE_ACTION_SLEEP_LAST = HANDLE_SUSPEND_THEN_HIBERNATE, + HANDLE_SLEEP, /* A "high-level" action that automatically choose an appropriate low-level sleep action */ + _HANDLE_ACTION_SLEEP_LAST = HANDLE_SLEEP, + HANDLE_LOCK, HANDLE_FACTORY_RESET, + _HANDLE_ACTION_MAX, _HANDLE_ACTION_INVALID = -EINVAL, } HandleAction; typedef struct HandleActionData HandleActionData; +typedef enum HandleActionSleepMask { + HANDLE_SLEEP_SUSPEND_MASK = 1U << HANDLE_SUSPEND, + HANDLE_SLEEP_HIBERNATE_MASK = 1U << HANDLE_HIBERNATE, + HANDLE_SLEEP_HYBRID_SLEEP_MASK = 1U << HANDLE_HYBRID_SLEEP, + HANDLE_SLEEP_SUSPEND_THEN_HIBERNATE_MASK = 1U << HANDLE_SUSPEND_THEN_HIBERNATE, +} HandleActionSleepMask; + +#define HANDLE_ACTION_SLEEP_MASK_DEFAULT (HANDLE_SLEEP_SUSPEND_THEN_HIBERNATE_MASK|HANDLE_SLEEP_SUSPEND_MASK|HANDLE_SLEEP_HIBERNATE_MASK) + #include "logind-inhibit.h" #include "logind.h" #include "sleep-config.h" @@ -55,6 +69,9 @@ struct HandleActionData { const char* log_verb; }; +int handle_action_get_enabled_sleep_actions(HandleActionSleepMask mask, char ***ret); +HandleAction handle_action_sleep_select(HandleActionSleepMask mask); + int manager_handle_action( Manager *m, InhibitWhat inhibit_key, @@ -70,3 +87,5 @@ HandleAction handle_action_from_string(const char *s) _pure_; const HandleActionData* handle_action_lookup(HandleAction handle); CONFIG_PARSER_PROTOTYPE(config_parse_handle_action); + +CONFIG_PARSER_PROTOTYPE(config_parse_handle_action_sleep); diff --git a/src/login/logind-core.c b/src/login/logind-core.c index f15008e0df2..becc21b0de6 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -40,6 +40,8 @@ void manager_reset_config(Manager *m) { m->inhibit_delay_max = 5 * USEC_PER_SEC; m->user_stop_delay = 10 * USEC_PER_SEC; + m->handle_action_sleep_mask = HANDLE_ACTION_SLEEP_MASK_DEFAULT; + m->handle_power_key = HANDLE_POWEROFF; m->handle_power_key_long_press = HANDLE_IGNORE; m->handle_reboot_key = HANDLE_REBOOT; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 22b4ce77ca8..34992b56810 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -342,6 +342,29 @@ static int property_get_preparing( return sd_bus_message_append(reply, "b", b); } +static int property_get_sleep_operations( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = ASSERT_PTR(userdata); + _cleanup_strv_free_ char **actions = NULL; + int r; + + assert(bus); + assert(reply); + + r = handle_action_get_enabled_sleep_actions(m->handle_action_sleep_mask, &actions); + if (r < 0) + return r; + + return sd_bus_message_append_strv(reply, actions); +} + static int property_get_scheduled_shutdown( sd_bus *bus, const char *path, @@ -1998,7 +2021,6 @@ static int method_do_shutdown_or_sleep( bool with_flags, sd_bus_error *error) { - const HandleActionData *a; uint64_t flags; int r; @@ -2006,8 +2028,6 @@ static int method_do_shutdown_or_sleep( assert(message); assert(HANDLE_ACTION_IS_SHUTDOWN(action) || HANDLE_ACTION_IS_SLEEP(action)); - assert_se(a = handle_action_lookup(action)); - if (with_flags) { /* New style method: with flags parameter (and interactive bool in the bus message header) */ r = sd_bus_message_read(message, "t", &flags); @@ -2042,20 +2062,32 @@ static int method_do_shutdown_or_sleep( flags = interactive ? SD_LOGIND_INTERACTIVE : 0; } + const HandleActionData *a = NULL; + if ((flags & SD_LOGIND_SOFT_REBOOT) || ((flags & SD_LOGIND_SOFT_REBOOT_IF_NEXTROOT_SET_UP) && path_is_os_tree("/run/nextroot") > 0)) a = handle_action_lookup(HANDLE_SOFT_REBOOT); else if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded()) a = handle_action_lookup(HANDLE_KEXEC); - /* Don't allow multiple jobs being executed at the same time */ - if (m->delayed_action) - return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, - "There's already a shutdown or sleep operation in progress"); + if (action == HANDLE_SLEEP) { + HandleAction selected; - if (a->sleep_operation >= 0) { + selected = handle_action_sleep_select(m->handle_action_sleep_mask); + if (selected < 0) + return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, + "None of the configured sleep operations are supported"); + + assert_se(a = handle_action_lookup(selected)); + + } else if (HANDLE_ACTION_IS_SLEEP(action)) { SleepSupport support; + assert_se(a = handle_action_lookup(action)); + + assert(a->sleep_operation >= 0); + assert(a->sleep_operation < _SLEEP_OPERATION_MAX); + r = sleep_supported_full(a->sleep_operation, &support); if (r < 0) return r; @@ -2086,7 +2118,8 @@ static int method_do_shutdown_or_sleep( assert_not_reached(); } - } + } else if (!a) + assert_se(a = handle_action_lookup(action)); r = verify_shutdown_creds(m, message, a, flags, error); if (r != 0) @@ -2178,6 +2211,16 @@ static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata error); } +static int method_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_do_shutdown_or_sleep( + m, message, + HANDLE_SLEEP, + /* with_flags = */ true, + error); +} + static int nologin_timeout_handler( sd_event_source *s, uint64_t usec, @@ -2543,17 +2586,30 @@ static int method_can_shutdown_or_sleep( assert(message); assert(HANDLE_ACTION_IS_SHUTDOWN(action) || HANDLE_ACTION_IS_SLEEP(action)); - assert_se(a = handle_action_lookup(action)); + if (action == HANDLE_SLEEP) { + HandleAction selected; + + selected = handle_action_sleep_select(m->handle_action_sleep_mask); + if (selected < 0) + return sd_bus_reply_method_return(message, "s", "na"); + + assert_se(a = handle_action_lookup(selected)); - if (a->sleep_operation >= 0) { + } else if (HANDLE_ACTION_IS_SLEEP(action)) { SleepSupport support; + assert_se(a = handle_action_lookup(action)); + + assert(a->sleep_operation >= 0); + assert(a->sleep_operation < _SLEEP_OPERATION_MAX); + r = sleep_supported_full(a->sleep_operation, &support); if (r < 0) return r; if (r == 0) return sd_bus_reply_method_return(message, "s", support == SLEEP_DISABLED ? "no" : "na"); - } + } else + assert_se(a = handle_action_lookup(action)); r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); if (r < 0) @@ -2673,6 +2729,12 @@ static int method_can_suspend_then_hibernate(sd_bus_message *message, void *user return method_can_shutdown_or_sleep(m, message, HANDLE_SUSPEND_THEN_HIBERNATE, error); } +static int method_can_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + + return method_can_shutdown_or_sleep(m, message, HANDLE_SLEEP, error); +} + static int property_get_reboot_parameter( sd_bus *bus, const char *path, @@ -3505,6 +3567,7 @@ static const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("DelayInhibited", "s", property_get_inhibited, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("InhibitDelayMaxUSec", "t", NULL, offsetof(Manager, inhibit_delay_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UserStopDelayUSec", "t", NULL, offsetof(Manager, user_stop_delay), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SleepOperation", "as", property_get_sleep_operations, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("HandlePowerKey", "s", property_get_handle_action, offsetof(Manager, handle_power_key), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("HandlePowerKeyLongPress", "s", property_get_handle_action, offsetof(Manager, handle_power_key_long_press), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("HandleRebootKey", "s", property_get_handle_action, offsetof(Manager, handle_reboot_key), SD_BUS_VTABLE_PROPERTY_CONST), @@ -3776,6 +3839,11 @@ static const sd_bus_vtable manager_vtable[] = { SD_BUS_NO_RESULT, method_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("Sleep", + SD_BUS_ARGS("t", flags), + SD_BUS_NO_RESULT, + method_sleep, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("CanPowerOff", SD_BUS_NO_ARGS, SD_BUS_RESULT("s", result), @@ -3811,6 +3879,11 @@ static const sd_bus_vtable manager_vtable[] = { SD_BUS_RESULT("s", result), method_can_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("CanSleep", + SD_BUS_NO_ARGS, + SD_BUS_RESULT("s", result), + method_can_sleep, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("ScheduleShutdown", SD_BUS_ARGS("s", type, "t", usec), SD_BUS_NO_RESULT, diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index c95a3b2dc3a..7d1520b469f 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -25,6 +25,7 @@ Login.KillOnlyUsers, config_parse_strv, 0, offse Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users) Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max) Login.UserStopDelaySec, config_parse_sec, 0, offsetof(Manager, user_stop_delay) +Login.SleepOperation, config_parse_handle_action_sleep, 0, offsetof(Manager, handle_action_sleep_mask) Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key) Login.HandlePowerKeyLongPress, config_parse_handle_action, 0, offsetof(Manager, handle_power_key_long_press) Login.HandleRebootKey, config_parse_handle_action, 0, offsetof(Manager, handle_reboot_key) diff --git a/src/login/logind.conf.in b/src/login/logind.conf.in index e5fe9246811..b62458ec3ca 100644 --- a/src/login/logind.conf.in +++ b/src/login/logind.conf.in @@ -24,6 +24,7 @@ #KillExcludeUsers=root #InhibitDelayMaxSec=5 #UserStopDelaySec=10 +#SleepOperation=suspend-then-hibernate suspend #HandlePowerKey=poweroff #HandlePowerKeyLongPress=ignore #HandleRebootKey=reboot diff --git a/src/login/logind.h b/src/login/logind.h index 9f2d8964b4a..cac6a303573 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -98,6 +98,8 @@ struct Manager { usec_t stop_idle_session_usec; + HandleActionSleepMask handle_action_sleep_mask; + HandleAction handle_power_key; HandleAction handle_power_key_long_press; HandleAction handle_reboot_key; diff --git a/src/login/test-login-tables.c b/src/login/test-login-tables.c index 1278af7504c..e9ff3085ecb 100644 --- a/src/login/test-login-tables.c +++ b/src/login/test-login-tables.c @@ -11,6 +11,9 @@ static void test_sleep_handle_action(void) { const HandleActionData *data; const char *sleep_operation_str, *handle_action_str; + if (action == HANDLE_SLEEP) + continue; + assert_se(data = handle_action_lookup(action)); assert_se(handle_action_str = handle_action_to_string(action)); diff --git a/test/units/testsuite-21.sh b/test/units/testsuite-21.sh index 02673ab29e1..0b0dfc6c831 100755 --- a/test/units/testsuite-21.sh +++ b/test/units/testsuite-21.sh @@ -28,7 +28,9 @@ systemctl log-level info # FIXME: systemd-run doesn't play well with daemon-reexec # See: https://github.com/systemd/systemd/issues/27204 sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:Reexecute FIXME' /etc/dfuzzer.conf + sed -i '/\[org.freedesktop.systemd1\]/aorg.freedesktop.systemd1.Manager:SoftReboot destructive' /etc/dfuzzer.conf +sed -i '/\[org.freedesktop.login1\]/aSleep destructive' /etc/dfuzzer.conf # TODO # * check for possibly newly introduced buses? diff --git a/test/units/testsuite-35.sh b/test/units/testsuite-35.sh index 36e26da885a..b250a5c18fb 100755 --- a/test/units/testsuite-35.sh +++ b/test/units/testsuite-35.sh @@ -57,6 +57,25 @@ EOF rm -rf /run/systemd/logind.conf.d } +testcase_sleep_automated() { + assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 3 "suspend-then-hibernate" "suspend" "hibernate"' + + mkdir -p /run/systemd/logind.conf.d + + cat >/run/systemd/logind.conf.d/sleep-operations.conf <