mirror of
https://github.com/systemd/systemd
synced 2024-07-21 10:17:21 +00:00
Merge pull request #29853 from YHNdnzj/sleep-automated
logind: support Sleep() that automatically choose a sleep operation
This commit is contained in:
commit
d1f9a39ef4
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>
|
||||
|
|
|
@ -1755,6 +1755,24 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
|||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>sleep</command></term>
|
||||
|
||||
<listitem>
|
||||
<para>Put the system to sleep, through <command>suspend</command>, <command>hibernate</command>,
|
||||
<command>hybrid-sleep</command>, or <command>suspend-then-hibernate</command>. The sleep operation
|
||||
to use is automatically selected by <citerefentry>
|
||||
<refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
By default, <command>suspend-then-hibernate</command> is used, and falls back to <command>suspend</command>
|
||||
and then <command>hibernate</command> if not supported. Refer to <varname>SleepOperation=</varname>
|
||||
setting in <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for more details. This command is asynchronous, and will return after the sleep operation is
|
||||
successfully enqueued. It will not wait for the sleep/resume cycle to complete.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><command>suspend</command></term>
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -361,9 +384,10 @@ static int property_get_scheduled_shutdown(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(reply, "st",
|
||||
m->scheduled_shutdown_action ? handle_action_to_string(m->scheduled_shutdown_action->handle) : NULL,
|
||||
m->scheduled_shutdown_timeout);
|
||||
r = sd_bus_message_append(
|
||||
reply, "st",
|
||||
handle_action_to_string(m->scheduled_shutdown_action),
|
||||
m->scheduled_shutdown_timeout);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -1993,7 +2017,7 @@ static int setup_wall_message_timer(Manager *m, sd_bus_message* message) {
|
|||
static int method_do_shutdown_or_sleep(
|
||||
Manager *m,
|
||||
sd_bus_message *message,
|
||||
const HandleActionData *a,
|
||||
HandleAction action,
|
||||
bool with_flags,
|
||||
sd_bus_error *error) {
|
||||
|
||||
|
@ -2002,7 +2026,7 @@ static int method_do_shutdown_or_sleep(
|
|||
|
||||
assert(m);
|
||||
assert(message);
|
||||
assert(a);
|
||||
assert(HANDLE_ACTION_IS_SHUTDOWN(action) || HANDLE_ACTION_IS_SLEEP(action));
|
||||
|
||||
if (with_flags) {
|
||||
/* New style method: with flags parameter (and interactive bool in the bus message header) */
|
||||
|
@ -2017,7 +2041,7 @@ static int method_do_shutdown_or_sleep(
|
|||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Both reboot via kexec and soft reboot selected, which is not supported");
|
||||
|
||||
if (a->handle != HANDLE_REBOOT) {
|
||||
if (action != HANDLE_REBOOT) {
|
||||
if (flags & SD_LOGIND_REBOOT_VIA_KEXEC)
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Reboot via kexec option is only applicable with reboot operations");
|
||||
|
@ -2038,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;
|
||||
|
@ -2082,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)
|
||||
|
@ -2093,7 +2130,7 @@ static int method_do_shutdown_or_sleep(
|
|||
reset_scheduled_shutdown(m);
|
||||
|
||||
m->scheduled_shutdown_timeout = 0;
|
||||
m->scheduled_shutdown_action = a;
|
||||
m->scheduled_shutdown_action = action;
|
||||
|
||||
(void) setup_wall_message_timer(m, message);
|
||||
|
||||
|
@ -2109,7 +2146,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
|
|||
|
||||
return method_do_shutdown_or_sleep(
|
||||
m, message,
|
||||
handle_action_lookup(HANDLE_POWEROFF),
|
||||
HANDLE_POWEROFF,
|
||||
sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"),
|
||||
error);
|
||||
}
|
||||
|
@ -2119,7 +2156,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
|
|||
|
||||
return method_do_shutdown_or_sleep(
|
||||
m, message,
|
||||
handle_action_lookup(HANDLE_REBOOT),
|
||||
HANDLE_REBOOT,
|
||||
sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"),
|
||||
error);
|
||||
}
|
||||
|
@ -2129,7 +2166,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
|
|||
|
||||
return method_do_shutdown_or_sleep(
|
||||
m, message,
|
||||
handle_action_lookup(HANDLE_HALT),
|
||||
HANDLE_HALT,
|
||||
sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"),
|
||||
error);
|
||||
}
|
||||
|
@ -2139,7 +2176,7 @@ static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error
|
|||
|
||||
return method_do_shutdown_or_sleep(
|
||||
m, message,
|
||||
handle_action_lookup(HANDLE_SUSPEND),
|
||||
HANDLE_SUSPEND,
|
||||
sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"),
|
||||
error);
|
||||
}
|
||||
|
@ -2149,7 +2186,7 @@ static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_erro
|
|||
|
||||
return method_do_shutdown_or_sleep(
|
||||
m, message,
|
||||
handle_action_lookup(HANDLE_HIBERNATE),
|
||||
HANDLE_HIBERNATE,
|
||||
sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"),
|
||||
error);
|
||||
}
|
||||
|
@ -2159,7 +2196,7 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e
|
|||
|
||||
return method_do_shutdown_or_sleep(
|
||||
m, message,
|
||||
handle_action_lookup(HANDLE_HYBRID_SLEEP),
|
||||
HANDLE_HYBRID_SLEEP,
|
||||
sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"),
|
||||
error);
|
||||
}
|
||||
|
@ -2169,11 +2206,21 @@ static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata
|
|||
|
||||
return method_do_shutdown_or_sleep(
|
||||
m, message,
|
||||
handle_action_lookup(HANDLE_SUSPEND_THEN_HIBERNATE),
|
||||
HANDLE_SUSPEND_THEN_HIBERNATE,
|
||||
sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"),
|
||||
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,
|
||||
|
@ -2232,7 +2279,7 @@ void manager_load_scheduled_shutdown(Manager *m) {
|
|||
return;
|
||||
|
||||
/* assign parsed type only after we know usec is also valid */
|
||||
m->scheduled_shutdown_action = handle_action_lookup(handle);
|
||||
m->scheduled_shutdown_action = handle;
|
||||
|
||||
if (warn_wall) {
|
||||
r = parse_boolean(warn_wall);
|
||||
|
@ -2275,7 +2322,7 @@ static int update_schedule_file(Manager *m) {
|
|||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(m->scheduled_shutdown_action);
|
||||
assert(handle_action_valid(m->scheduled_shutdown_action));
|
||||
|
||||
r = mkdir_parents_label(SHUTDOWN_SCHEDULE_FILE, 0755);
|
||||
if (r < 0)
|
||||
|
@ -2289,7 +2336,7 @@ static int update_schedule_file(Manager *m) {
|
|||
|
||||
serialize_usec(f, "USEC", m->scheduled_shutdown_timeout);
|
||||
serialize_item_format(f, "WARN_WALL", "%s", one_zero(m->enable_wall_messages));
|
||||
serialize_item_format(f, "MODE", "%s", handle_action_to_string(m->scheduled_shutdown_action->handle));
|
||||
serialize_item_format(f, "MODE", "%s", handle_action_to_string(m->scheduled_shutdown_action));
|
||||
serialize_item_format(f, "UID", UID_FMT, m->scheduled_shutdown_uid);
|
||||
|
||||
if (m->scheduled_shutdown_tty)
|
||||
|
@ -2326,7 +2373,7 @@ static void reset_scheduled_shutdown(Manager *m) {
|
|||
m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
|
||||
m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source);
|
||||
|
||||
m->scheduled_shutdown_action = NULL;
|
||||
m->scheduled_shutdown_action = _HANDLE_ACTION_INVALID;
|
||||
m->scheduled_shutdown_timeout = USEC_INFINITY;
|
||||
m->scheduled_shutdown_uid = UID_INVALID;
|
||||
m->scheduled_shutdown_tty = mfree(m->scheduled_shutdown_tty);
|
||||
|
@ -2345,13 +2392,12 @@ static int manager_scheduled_shutdown_handler(
|
|||
uint64_t usec,
|
||||
void *userdata) {
|
||||
|
||||
const HandleActionData *a = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
const HandleActionData *a;
|
||||
int r;
|
||||
|
||||
a = m->scheduled_shutdown_action;
|
||||
assert(a);
|
||||
assert_se(a = handle_action_lookup(m->scheduled_shutdown_action));
|
||||
|
||||
/* Don't allow multiple jobs being executed at the same time */
|
||||
if (m->delayed_action) {
|
||||
|
@ -2373,7 +2419,7 @@ static int manager_scheduled_shutdown_handler(
|
|||
return 0;
|
||||
}
|
||||
|
||||
r = bus_manager_shutdown_or_sleep_now_or_later(m, m->scheduled_shutdown_action, &error);
|
||||
r = bus_manager_shutdown_or_sleep_now_or_later(m, a, &error);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Scheduled shutdown to %s failed: %m", a->target);
|
||||
goto error;
|
||||
|
@ -2410,15 +2456,14 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
|
|||
if (!HANDLE_ACTION_IS_SHUTDOWN(handle))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type: %s", type);
|
||||
|
||||
a = handle_action_lookup(handle);
|
||||
assert(a);
|
||||
assert_se(a = handle_action_lookup(handle));
|
||||
assert(a->polkit_action);
|
||||
|
||||
r = verify_shutdown_creds(m, message, a, 0, error);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
m->scheduled_shutdown_action = a;
|
||||
m->scheduled_shutdown_action = handle;
|
||||
m->shutdown_dry_run = dry_run;
|
||||
m->scheduled_shutdown_timeout = elapse;
|
||||
|
||||
|
@ -2474,12 +2519,11 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
|
|||
|
||||
assert(message);
|
||||
|
||||
cancelled = m->scheduled_shutdown_action
|
||||
&& !IN_SET(m->scheduled_shutdown_action->handle, HANDLE_IGNORE, _HANDLE_ACTION_INVALID);
|
||||
cancelled = handle_action_valid(m->scheduled_shutdown_action) && m->scheduled_shutdown_action != HANDLE_IGNORE;
|
||||
if (!cancelled)
|
||||
return sd_bus_reply_method_return(message, "b", false);
|
||||
|
||||
a = m->scheduled_shutdown_action;
|
||||
assert_se(a = handle_action_lookup(m->scheduled_shutdown_action));
|
||||
if (!a->polkit_action)
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_AUTH_FAILED, "Unsupported shutdown type");
|
||||
|
||||
|
@ -2528,28 +2572,44 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
|
|||
static int method_can_shutdown_or_sleep(
|
||||
Manager *m,
|
||||
sd_bus_message *message,
|
||||
const HandleActionData *a,
|
||||
HandleAction action,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
||||
bool multiple_sessions, challenge, blocked;
|
||||
const HandleActionData *a;
|
||||
const char *result = NULL;
|
||||
uid_t uid;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(message);
|
||||
assert(a);
|
||||
assert(HANDLE_ACTION_IS_SHUTDOWN(action) || HANDLE_ACTION_IS_SLEEP(action));
|
||||
|
||||
if (a->sleep_operation >= 0) {
|
||||
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));
|
||||
|
||||
} 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)
|
||||
|
@ -2566,22 +2626,16 @@ static int method_can_shutdown_or_sleep(
|
|||
multiple_sessions = r > 0;
|
||||
blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
|
||||
|
||||
HandleAction handle = handle_action_from_string(sleep_operation_to_string(a->sleep_operation));
|
||||
if (handle >= 0) {
|
||||
const char *target;
|
||||
if (a->target) {
|
||||
_cleanup_free_ char *load_state = NULL;
|
||||
|
||||
target = handle_action_lookup(handle)->target;
|
||||
if (target) {
|
||||
_cleanup_free_ char *load_state = NULL;
|
||||
r = unit_load_state(m->bus, a->target, &load_state);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = unit_load_state(m->bus, target, &load_state);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!streq(load_state, "loaded")) {
|
||||
result = "no";
|
||||
goto finish;
|
||||
}
|
||||
if (!streq(load_state, "loaded")) {
|
||||
result = "no";
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2636,57 +2690,49 @@ static int method_can_shutdown_or_sleep(
|
|||
static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
|
||||
return method_can_shutdown_or_sleep(
|
||||
m, message, handle_action_lookup(HANDLE_POWEROFF),
|
||||
error);
|
||||
return method_can_shutdown_or_sleep(m, message, HANDLE_POWEROFF, error);
|
||||
}
|
||||
|
||||
static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
|
||||
return method_can_shutdown_or_sleep(
|
||||
m, message, handle_action_lookup(HANDLE_REBOOT),
|
||||
error);
|
||||
return method_can_shutdown_or_sleep(m, message, HANDLE_REBOOT, error);
|
||||
}
|
||||
|
||||
static int method_can_halt(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
|
||||
return method_can_shutdown_or_sleep(
|
||||
m, message, handle_action_lookup(HANDLE_HALT),
|
||||
error);
|
||||
return method_can_shutdown_or_sleep(m, message, HANDLE_HALT, error);
|
||||
}
|
||||
|
||||
static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
|
||||
return method_can_shutdown_or_sleep(
|
||||
m, message, handle_action_lookup(HANDLE_SUSPEND),
|
||||
error);
|
||||
return method_can_shutdown_or_sleep(m, message, HANDLE_SUSPEND, error);
|
||||
}
|
||||
|
||||
static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
|
||||
return method_can_shutdown_or_sleep(
|
||||
m, message, handle_action_lookup(HANDLE_HIBERNATE),
|
||||
error);
|
||||
return method_can_shutdown_or_sleep(m, message, HANDLE_HIBERNATE, error);
|
||||
}
|
||||
|
||||
static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
|
||||
return method_can_shutdown_or_sleep(
|
||||
m, message, handle_action_lookup(HANDLE_HYBRID_SLEEP),
|
||||
error);
|
||||
return method_can_shutdown_or_sleep(m, message, HANDLE_HYBRID_SLEEP, error);
|
||||
}
|
||||
|
||||
static int method_can_suspend_then_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Manager *m = userdata;
|
||||
|
||||
return method_can_shutdown_or_sleep(
|
||||
m, message, handle_action_lookup(HANDLE_SUSPEND_THEN_HIBERNATE),
|
||||
error);
|
||||
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(
|
||||
|
@ -3521,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),
|
||||
|
@ -3792,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),
|
||||
|
@ -3827,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)
|
||||
|
|
|
@ -42,7 +42,7 @@ static usec_t when_wall(usec_t n, usec_t elapse) {
|
|||
bool logind_wall_tty_filter(const char *tty, bool is_local, void *userdata) {
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
|
||||
assert(m->scheduled_shutdown_action);
|
||||
assert(handle_action_valid(m->scheduled_shutdown_action));
|
||||
|
||||
const char *p = path_startswith(tty, "/dev/");
|
||||
if (!p)
|
||||
|
@ -52,7 +52,7 @@ bool logind_wall_tty_filter(const char *tty, bool is_local, void *userdata) {
|
|||
* can assume that if the system enters sleep or hibernation, this will be visible in an obvious way
|
||||
* for any local user. And once the systems exits sleep or hibernation, the notification would be
|
||||
* just noise, in particular for auto-suspend. */
|
||||
if (is_local && HANDLE_ACTION_IS_SLEEP(m->scheduled_shutdown_action->handle))
|
||||
if (is_local && HANDLE_ACTION_IS_SLEEP(m->scheduled_shutdown_action))
|
||||
return false;
|
||||
|
||||
return !streq_ptr(p, m->scheduled_shutdown_tty);
|
||||
|
@ -61,7 +61,7 @@ bool logind_wall_tty_filter(const char *tty, bool is_local, void *userdata) {
|
|||
static int warn_wall(Manager *m, usec_t n) {
|
||||
assert(m);
|
||||
|
||||
if (!m->scheduled_shutdown_action)
|
||||
if (!handle_action_valid(m->scheduled_shutdown_action))
|
||||
return 0;
|
||||
|
||||
bool left = m->scheduled_shutdown_timeout > n;
|
||||
|
@ -70,7 +70,7 @@ static int warn_wall(Manager *m, usec_t n) {
|
|||
if (asprintf(&l, "%s%sThe system will %s %s%s!",
|
||||
strempty(m->wall_message),
|
||||
isempty(m->wall_message) ? "" : "\n",
|
||||
handle_action_verb_to_string(m->scheduled_shutdown_action->handle),
|
||||
handle_action_verb_to_string(m->scheduled_shutdown_action),
|
||||
left ? "at " : "now",
|
||||
left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : "") < 0) {
|
||||
|
||||
|
@ -84,7 +84,7 @@ static int warn_wall(Manager *m, usec_t n) {
|
|||
|
||||
log_struct(level,
|
||||
LOG_MESSAGE("%s", l),
|
||||
"ACTION=%s", handle_action_to_string(m->scheduled_shutdown_action->handle),
|
||||
"ACTION=%s", handle_action_to_string(m->scheduled_shutdown_action),
|
||||
"MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_SCHEDULED_STR,
|
||||
username ? "OPERATOR=%s" : NULL, username);
|
||||
|
||||
|
@ -134,7 +134,7 @@ int manager_setup_wall_message_timer(Manager *m) {
|
|||
|
||||
/* wall message handling */
|
||||
|
||||
if (!m->scheduled_shutdown_action)
|
||||
if (!handle_action_valid(m->scheduled_shutdown_action))
|
||||
return 0;
|
||||
|
||||
if (elapse > 0 && elapse < n)
|
||||
|
|
|
@ -65,18 +65,19 @@ static int manager_new(Manager **ret) {
|
|||
.reserve_vt_fd = -EBADF,
|
||||
.enable_wall_messages = true,
|
||||
.idle_action_not_before_usec = now(CLOCK_MONOTONIC),
|
||||
.scheduled_shutdown_action = _HANDLE_ACTION_INVALID,
|
||||
|
||||
.devices = hashmap_new(&device_hash_ops),
|
||||
.seats = hashmap_new(&seat_hash_ops),
|
||||
.sessions = hashmap_new(&session_hash_ops),
|
||||
.users = hashmap_new(&user_hash_ops),
|
||||
.inhibitors = hashmap_new(&inhibitor_hash_ops),
|
||||
.buttons = hashmap_new(&button_hash_ops),
|
||||
|
||||
.user_units = hashmap_new(&string_hash_ops),
|
||||
.session_units = hashmap_new(&string_hash_ops),
|
||||
};
|
||||
|
||||
m->devices = hashmap_new(&device_hash_ops);
|
||||
m->seats = hashmap_new(&seat_hash_ops);
|
||||
m->sessions = hashmap_new(&session_hash_ops);
|
||||
m->users = hashmap_new(&user_hash_ops);
|
||||
m->inhibitors = hashmap_new(&inhibitor_hash_ops);
|
||||
m->buttons = hashmap_new(&button_hash_ops);
|
||||
|
||||
m->user_units = hashmap_new(&string_hash_ops);
|
||||
m->session_units = hashmap_new(&string_hash_ops);
|
||||
|
||||
if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#KillExcludeUsers=root
|
||||
#InhibitDelayMaxSec=5
|
||||
#UserStopDelaySec=10
|
||||
#SleepOperation=suspend-then-hibernate suspend
|
||||
#HandlePowerKey=poweroff
|
||||
#HandlePowerKeyLongPress=ignore
|
||||
#HandleRebootKey=reboot
|
||||
|
|
|
@ -76,7 +76,7 @@ struct Manager {
|
|||
char *action_job;
|
||||
sd_event_source *inhibit_timeout_source;
|
||||
|
||||
const HandleActionData *scheduled_shutdown_action;
|
||||
HandleAction scheduled_shutdown_action;
|
||||
usec_t scheduled_shutdown_timeout;
|
||||
sd_event_source *scheduled_shutdown_timeout_source;
|
||||
uid_t scheduled_shutdown_uid;
|
||||
|
@ -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;
|
||||
|
|
|
@ -2,9 +2,27 @@
|
|||
|
||||
#include "logind-action.h"
|
||||
#include "logind-session.h"
|
||||
#include "sleep-config.h"
|
||||
#include "test-tables.h"
|
||||
#include "tests.h"
|
||||
|
||||
static void test_sleep_handle_action(void) {
|
||||
for (HandleAction action = _HANDLE_ACTION_SLEEP_FIRST; action < _HANDLE_ACTION_SLEEP_LAST; action++) {
|
||||
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));
|
||||
assert_se(sleep_operation_str = sleep_operation_to_string(data->sleep_operation));
|
||||
|
||||
assert_se(streq(handle_action_str, sleep_operation_str));
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
|
@ -16,5 +34,7 @@ int main(int argc, char **argv) {
|
|||
test_table(session_type, SESSION_TYPE);
|
||||
test_table(user_state, USER_STATE);
|
||||
|
||||
test_sleep_handle_action();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ int logind_reboot(enum action a) {
|
|||
[ACTION_HIBERNATE] = "Hibernate",
|
||||
[ACTION_HYBRID_SLEEP] = "HybridSleep",
|
||||
[ACTION_SUSPEND_THEN_HIBERNATE] = "SuspendThenHibernate",
|
||||
[ACTION_SLEEP] = "Sleep",
|
||||
};
|
||||
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
@ -71,7 +72,7 @@ int logind_reboot(enum action a) {
|
|||
polkit_agent_open_maybe();
|
||||
(void) logind_set_wall_message(bus);
|
||||
|
||||
const char *method_with_flags = strjoina(actions[a], "WithFlags");
|
||||
const char *method_with_flags = a == ACTION_SLEEP ? actions[a] : strjoina(actions[a], "WithFlags");
|
||||
|
||||
log_debug("%s org.freedesktop.login1.Manager %s dbus call.",
|
||||
arg_dry_run ? "Would execute" : "Executing", method_with_flags);
|
||||
|
@ -103,7 +104,7 @@ int logind_reboot(enum action a) {
|
|||
}
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD))
|
||||
if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD) || a == ACTION_SLEEP)
|
||||
return log_error_errno(r, "Call to %s failed: %s", actions[a], bus_error_message(&error, r));
|
||||
|
||||
/* Fall back to original methods in case there is an older version of systemd-logind */
|
||||
|
|
|
@ -229,6 +229,9 @@ int verb_start_special(int argc, char *argv[], void *userdata) {
|
|||
arg_no_block = true;
|
||||
break;
|
||||
|
||||
case ACTION_SLEEP:
|
||||
return logind_reboot(a);
|
||||
|
||||
case ACTION_EXIT:
|
||||
/* Since exit is so close in behaviour to power-off/reboot, let's also make
|
||||
* it asynchronous, in order to not confuse the user needlessly with unexpected
|
||||
|
|
|
@ -236,6 +236,7 @@ const struct action_metadata action_table[_ACTION_MAX] = {
|
|||
[ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" },
|
||||
[ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" },
|
||||
[ACTION_SUSPEND_THEN_HIBERNATE] = { SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, "suspend-then-hibernate", "replace-irreversibly" },
|
||||
[ACTION_SLEEP] = { NULL, /* handled only by logind */ "sleep", NULL },
|
||||
};
|
||||
|
||||
enum action verb_to_action(const char *verb) {
|
||||
|
@ -294,6 +295,8 @@ int verb_start(int argc, char *argv[], void *userdata) {
|
|||
|
||||
action = verb_to_action(argv[0]);
|
||||
|
||||
assert(action != ACTION_SLEEP);
|
||||
|
||||
if (action != _ACTION_INVALID) {
|
||||
/* A command in style "systemctl reboot", "systemctl poweroff", … */
|
||||
method = "StartUnit";
|
||||
|
|
|
@ -249,6 +249,8 @@ static int systemctl_help(void) {
|
|||
" soft-reboot Shut down and reboot userspace\n"
|
||||
" exit [EXIT_CODE] Request user instance or container exit\n"
|
||||
" switch-root [ROOT [INIT]] Change to a different root file system\n"
|
||||
" sleep Put the system to sleep (through one of\n"
|
||||
" the operations below)\n"
|
||||
" suspend Suspend the system\n"
|
||||
" hibernate Hibernate the system\n"
|
||||
" hybrid-sleep Hibernate and suspend the system\n"
|
||||
|
@ -1179,6 +1181,7 @@ static int systemctl_main(int argc, char *argv[]) {
|
|||
{ "reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
|
||||
{ "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
|
||||
{ "soft-reboot", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
|
||||
{ "sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
|
||||
{ "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
|
||||
{ "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
|
||||
{ "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special },
|
||||
|
@ -1323,6 +1326,7 @@ static int run(int argc, char *argv[]) {
|
|||
break;
|
||||
|
||||
case ACTION_EXIT:
|
||||
case ACTION_SLEEP:
|
||||
case ACTION_SUSPEND:
|
||||
case ACTION_HIBERNATE:
|
||||
case ACTION_HYBRID_SLEEP:
|
||||
|
|
|
@ -18,6 +18,7 @@ enum action {
|
|||
ACTION_KEXEC,
|
||||
ACTION_SOFT_REBOOT,
|
||||
ACTION_EXIT,
|
||||
ACTION_SLEEP,
|
||||
ACTION_SUSPEND,
|
||||
ACTION_HIBERNATE,
|
||||
ACTION_HYBRID_SLEEP,
|
||||
|
|
|
@ -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