mirror of
https://github.com/systemd/systemd
synced 2024-07-21 10:17:21 +00:00
logind: introduce Sleep() call and action that automatically choose a sleep operation
This commit is contained in:
parent
0f15d14c76
commit
cd4dd90b99
7
TODO
7
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
|
||||
|
|
|
@ -143,18 +143,10 @@
|
|||
<term><varname>IdleAction=</varname></term>
|
||||
|
||||
<listitem><para>Configures the action to take when the system
|
||||
is idle. Takes one of
|
||||
<literal>ignore</literal>,
|
||||
<literal>poweroff</literal>,
|
||||
<literal>reboot</literal>,
|
||||
<literal>halt</literal>,
|
||||
<literal>kexec</literal>,
|
||||
<literal>suspend</literal>,
|
||||
<literal>hibernate</literal>,
|
||||
<literal>hybrid-sleep</literal>,
|
||||
<literal>suspend-then-hibernate</literal>, and
|
||||
<literal>lock</literal>.
|
||||
Defaults to <literal>ignore</literal>.</para>
|
||||
is idle. Takes one of <literal>ignore</literal>, <literal>poweroff</literal>, <literal>reboot</literal>,
|
||||
<literal>halt</literal>, <literal>kexec</literal>, <literal>suspend</literal>, <literal>hibernate</literal>,
|
||||
<literal>hybrid-sleep</literal>, <literal>suspend-then-hibernate</literal>, <literal>sleep</literal>,
|
||||
and <literal>lock</literal>. Defaults to <literal>ignore</literal>.</para>
|
||||
|
||||
<para>Note that this requires that user sessions correctly
|
||||
report the idle status to the system. The system will execute
|
||||
|
@ -200,6 +192,20 @@
|
|||
<xi:include href="version-info.xml" xpointer="v240"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SleepOperation=</varname></term>
|
||||
|
||||
<listitem><para>Takes a list of sleep operations. Possible values are <literal>suspend</literal>,
|
||||
<literal>hibernate</literal>, <literal>hybrid-sleep</literal>, and <literal>suspend-then-hibernate</literal>.
|
||||
Controls the candidate sleep operations for the <literal>sleep</literal> action. When <literal>sleep</literal>
|
||||
action is performed, the specified sleep operations are checked in a fixed order (<literal>suspend-then-hibernate</literal>
|
||||
→ <literal>hybrid-sleep</literal> → <literal>suspend</literal> → <literal>hibernate</literal>), and
|
||||
the first one supported by the machine is used to put the system into sleep. Defaults to
|
||||
<literal>suspend-then-hibernate suspend hibernate</literal>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>HandlePowerKey=</varname></term>
|
||||
<term><varname>HandlePowerKeyLongPress=</varname></term>
|
||||
|
@ -215,10 +221,9 @@
|
|||
|
||||
<listitem><para>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
|
||||
<literal>ignore</literal>, <literal>poweroff</literal>, <literal>reboot</literal>,
|
||||
<literal>halt</literal>, <literal>kexec</literal>, <literal>suspend</literal>,
|
||||
<literal>hibernate</literal>, <literal>hybrid-sleep</literal>,
|
||||
<literal>suspend-then-hibernate</literal>, <literal>lock</literal>, and
|
||||
<literal>ignore</literal>, <literal>poweroff</literal>, <literal>reboot</literal>, <literal>halt</literal>,
|
||||
<literal>kexec</literal>, <literal>suspend</literal>, <literal>hibernate</literal>, <literal>hybrid-sleep</literal>,
|
||||
<literal>suspend-then-hibernate</literal>, <literal>sleep</literal>, <literal>lock</literal>, and
|
||||
<literal>factory-reset</literal>. If <literal>ignore</literal>, <command>systemd-logind</command>
|
||||
will never handle these keys. If <literal>lock</literal>, all running sessions will be screen-locked;
|
||||
otherwise, the specified action will be taken in the respective event. Only input devices with the
|
||||
|
|
|
@ -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 {
|
|||
};
|
||||
</programlisting>
|
||||
|
||||
<!--property SleepOperation is not documented!-->
|
||||
|
||||
<!--property HandlePowerKeyLongPress is not documented!-->
|
||||
|
||||
<!--property HandleRebootKey is not documented!-->
|
||||
|
@ -378,6 +384,8 @@ node /org/freedesktop/login1 {
|
|||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="SuspendThenHibernateWithFlags()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="Sleep()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="CanPowerOff()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="CanReboot()"/>
|
||||
|
@ -392,6 +400,8 @@ node /org/freedesktop/login1 {
|
|||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="CanSuspendThenHibernate()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="CanSleep()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="ScheduleShutdown()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="CancelScheduledShutdown()"/>
|
||||
|
@ -470,6 +480,8 @@ node /org/freedesktop/login1 {
|
|||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UserStopDelayUSec"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SleepOperation"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="HandlePowerKey"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="HandlePowerKeyLongPress"/>
|
||||
|
@ -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). <function>HybridSleep()</function> results in the system entering a hybrid-sleep mode,
|
||||
i.e. the system is both hibernated and suspended. <function>SuspendThenHibernate()</function> results
|
||||
i.e. the system is both hibernated and suspended. <function>SuspendThenHibernate()</function> results
|
||||
in the system being suspended, then later woken using an RTC timer and hibernated. The only argument is
|
||||
the polkit interactivity boolean <varname>interactive</varname> (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.
|
||||
<function>Sleep()</function> automatically selects the most suitable sleep operation supported by the
|
||||
machine. The candidate sleep operations to check for support can be configured through <varname>SleepOperation=</varname>
|
||||
setting in <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
UIs should expose these calls as the primary mechanism to poweroff/reboot/suspend/hibernate the machine. Methods
|
||||
<function>PowerOffWithFlags()</function>, <function>RebootWithFlags()</function>,
|
||||
<function>HaltWithFlags()</function>, <function>SuspendWithFlags()</function>,
|
||||
<function>HibernateWithFlags()</function>, <function>HybridSleepWithFlags()</function> and
|
||||
<function>SuspendThenHibernateWithFlags()</function> add <varname>flags</varname> to allow for
|
||||
extendability, defined as follows:</para>
|
||||
<function>HibernateWithFlags()</function>, <function>HybridSleepWithFlags()</function>,
|
||||
<function>SuspendThenHibernateWithFlags()</function>, and <function>Sleep()</function> take
|
||||
<varname>flags</varname> to allow for extendability, defined as follows:</para>
|
||||
<programlisting>
|
||||
#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 {
|
|||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for the
|
||||
corresponding command line interface.</para>
|
||||
|
||||
<para><function>CanPowerOff()</function>, <function>CanReboot()</function>,
|
||||
<function>CanHalt()</function>, <function>CanSuspend()</function>, <function>CanHibernate()</function>,
|
||||
<function>CanHybridSleep()</function>, <function>CanSuspendThenHibernate()</function>,
|
||||
<para><function>CanPowerOff()</function>, <function>CanReboot()</function>, <function>CanHalt()</function>,
|
||||
<function>CanSuspend()</function>, <function>CanHibernate()</function>, <function>CanHybridSleep()</function>,
|
||||
<function>CanSuspendThenHibernate()</function>, <function>CanSleep()</function>,
|
||||
<function>CanRebootParameter()</function>, <function>CanRebootToFirmwareSetup()</function>,
|
||||
<function>CanRebootToBootLoaderMenu()</function>, and
|
||||
<function>CanRebootToBootLoaderEntry()</function> test whether the system supports the respective
|
||||
operation and whether the calling user is allowed to execute it. Returns one of <literal>na</literal>,
|
||||
<literal>yes</literal>, <literal>no</literal>, and <literal>challenge</literal>. If
|
||||
<literal>na</literal> is returned, the operation is not available because hardware, kernel, or drivers
|
||||
do not support it. If <literal>yes</literal> is returned, the operation is supported and the user may
|
||||
execute the operation without further authentication. If <literal>no</literal> is returned, the
|
||||
operation is available but the user is not allowed to execute the operation. If
|
||||
<literal>challenge</literal> is returned, the operation is available but only after
|
||||
authorization.</para>
|
||||
<function>CanRebootToBootLoaderMenu()</function>, and <function>CanRebootToBootLoaderEntry()</function>
|
||||
test whether the system supports the respective operation and whether the calling user is allowed to
|
||||
execute it. Returns one of <literal>na</literal>, <literal>yes</literal>, <literal>no</literal>, and
|
||||
<literal>challenge</literal>. If <literal>na</literal> is returned, the operation is not available because
|
||||
hardware, kernel, or drivers do not support it. If <literal>yes</literal> is returned, the operation is
|
||||
supported and the user may execute the operation without further authentication. If <literal>no</literal>
|
||||
is returned, the operation is available but the user is not allowed to execute the operation. If
|
||||
<literal>challenge</literal> is returned, the operation is available but only after authorization.</para>
|
||||
|
||||
<para><function>ScheduleShutdown()</function> schedules a shutdown operation <varname>type</varname> at
|
||||
time <varname>usec</varname> in microseconds since the UNIX epoch. <varname>type</varname> can be one
|
||||
|
@ -819,8 +832,8 @@ node /org/freedesktop/login1 {
|
|||
<interfacename>org.freedesktop.login1.hibernate-ignore-inhibit</interfacename>,
|
||||
respectively depending on whether there are other sessions around or active inhibits are present.
|
||||
<function>HybridSleep()</function> and <function>SuspendThenHibernate()</function>
|
||||
use the same privileges as <function>Hibernate()</function>.
|
||||
<function>SetRebootParameter()</function> requires
|
||||
use the same privileges as <function>Hibernate()</function>. <function>Sleep()</function> uses
|
||||
the inhibits of the auto-selected sleep operation. <function>SetRebootParameter()</function> requires
|
||||
<interfacename>org.freedesktop.login1.set-reboot-parameter</interfacename>.</para>
|
||||
|
||||
<para><function>SetRebootToFirmwareSetup</function> requires
|
||||
|
@ -1546,6 +1559,9 @@ node /org/freedesktop/login1/session/1 {
|
|||
<para><varname>StopIdleSessionUSec</varname> was added in version 252.</para>
|
||||
<para><function>PrepareForShutdownWithMetadata</function> and
|
||||
<function>CreateSessionWithPIDFD()</function> were added in version 255.</para>
|
||||
<para><function>Sleep()</function>,
|
||||
<function>CanSleep()</function>, and
|
||||
<varname>SleepOperation</varname> were added in version 256.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Session Objects</title>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
if (a->sleep_operation >= 0) {
|
||||
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));
|
||||
|
||||
} 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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#KillExcludeUsers=root
|
||||
#InhibitDelayMaxSec=5
|
||||
#UserStopDelaySec=10
|
||||
#SleepOperation=suspend-then-hibernate suspend
|
||||
#HandlePowerKey=poweroff
|
||||
#HandlePowerKeyLongPress=ignore
|
||||
#HandleRebootKey=reboot
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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 <<EOF
|
||||
[Login]
|
||||
SleepOperation=suspend hybrid-sleep
|
||||
EOF
|
||||
|
||||
systemctl restart systemd-logind.service
|
||||
|
||||
assert_eq "$(busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager SleepOperation)" 'as 2 "hybrid-sleep" "suspend"'
|
||||
|
||||
busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanSleep
|
||||
|
||||
rm -rf /run/systemd/logind.conf.d
|
||||
}
|
||||
|
||||
testcase_started() {
|
||||
local pid
|
||||
|
||||
|
|
Loading…
Reference in a new issue