1
0
mirror of https://github.com/systemd/systemd synced 2026-03-16 18:14:46 +01:00

Compare commits

..

No commits in common. "83a04afc06eb37e765e23a3af4333f681619794e" and "86204ae145e38a4557981a92ce91a8ce4318e181" have entirely different histories.

10 changed files with 496 additions and 579 deletions

View File

@ -1,28 +0,0 @@
#!/usr/bin/python
import ast
import re
def read_os_release():
try:
f = open('/etc/os-release')
except FileNotFoundError:
f = open('/usr/lib/os-release')
for line_number, line in enumerate(f):
if m := re.match(r'([A-Z][A-Z_0-9]+)=(.*?)\s*$', line):
name, val = m.groups()
if val and val[0] in '"\'':
val = ast.literal_eval(val)
yield name, val
else:
print(f'Warning: bad line {line_number}: {line}', file=sys.stderr)
os_release = dict(read_os_release())
pretty_name = os_release.get('PRETTY_NAME', 'Linux')
print(f'Running on {pretty_name}')
if (os_release.get('ID', 'linux') == 'debian' or
os_release.get('ID_LIKE', None) == 'debian'):
print('Looks like Debian!')

View File

@ -1,10 +0,0 @@
#!/bin/sh -eu
test -e /etc/os-release && os_release='/etc/os-release' || os_release='/usr/lib/os-release'
. "${os_release}"
echo "Running on ${PRETTY_NAME:-Linux}"
if [ "${ID:-linux}" = "debian" ] || [ "${ID_LIKE:-}" = "debian" ]; then
echo "Looks like Debian!"
fi

View File

@ -3,7 +3,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later --> <!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="os-release" xmlns:xi="http://www.w3.org/2001/XInclude"> <refentry id="os-release">
<refentryinfo> <refentryinfo>
<title>os-release</title> <title>os-release</title>
<productname>systemd</productname> <productname>systemd</productname>
@ -16,14 +16,12 @@
<refnamediv> <refnamediv>
<refname>os-release</refname> <refname>os-release</refname>
<refname>initrd-release</refname>
<refpurpose>Operating system identification</refpurpose> <refpurpose>Operating system identification</refpurpose>
</refnamediv> </refnamediv>
<refsynopsisdiv> <refsynopsisdiv>
<para><filename>/etc/os-release</filename></para> <para><filename>/etc/os-release</filename></para>
<para><filename>/usr/lib/os-release</filename></para> <para><filename>/usr/lib/os-release</filename></para>
<para><filename>/etc/initrd-release</filename></para>
</refsynopsisdiv> </refsynopsisdiv>
<refsect1> <refsect1>
@ -81,19 +79,6 @@
<para>For a longer rationale for <filename>os-release</filename> <para>For a longer rationale for <filename>os-release</filename>
please refer to the <ulink please refer to the <ulink
url="http://0pointer.de/blog/projects/os-release">Announcement of <filename>/etc/os-release</filename></ulink>.</para> url="http://0pointer.de/blog/projects/os-release">Announcement of <filename>/etc/os-release</filename></ulink>.</para>
<refsect2>
<title><filename>/etc/initrd-release</filename></title>
<para>In the <ulink
url="https://www.kernel.org/doc/html/latest/admin-guide/initrd.html">initrd</ulink>,
<filename>/etc/initrd-release</filename> plays the same role as <filename>os-release</filename> in the
main system. Additionally, the presence of that file means that the system is in the initrd phase.
<filename>/etc/os-release</filename> should be symlinked to <filename>/etc/initrd-release</filename>
(or vice versa), so programs that only look for <filename>/etc/os-release</filename> (as described
above) work correctly. The rest of this document that talks about <filename>os-release</filename>
should be understood to apply to <filename>initrd-release</filename> too.</para>
</refsect2>
</refsect1> </refsect1>
<refsect1> <refsect1>
@ -102,320 +87,321 @@
<para>The following OS identifications parameters may be set using <para>The following OS identifications parameters may be set using
<filename>os-release</filename>:</para> <filename>os-release</filename>:</para>
<refsect2> <variablelist class='environment-variables'>
<title>General information identifying the operating system</title>
<varlistentry>
<variablelist class='environment-variables'> <term><varname>NAME=</varname></term>
<varlistentry>
<term><varname>NAME=</varname></term> <listitem><para>A string identifying the operating system,
without a version component, and suitable for presentation to
<listitem><para>A string identifying the operating system, without a version component, and the user. If not set, defaults to
suitable for presentation to the user. If not set, a default of <literal>NAME=Linux</literal> may <literal>NAME=Linux</literal>. Example:
be used.</para> <literal>NAME=Fedora</literal> or <literal>NAME="Debian
GNU/Linux"</literal>.</para></listitem>
<para>Examples: <literal>NAME=Fedora</literal>, <literal>NAME="Debian GNU/Linux"</literal>. </varlistentry>
</para></listitem>
</varlistentry> <varlistentry>
<term><varname>VERSION=</varname></term>
<varlistentry>
<term><varname>ID=</varname></term> <listitem><para>A string identifying the operating system
version, excluding any OS name information, possibly including
<listitem><para>A lower-case string (no spaces or other characters outside of 09, az, ".", "_" a release code name, and suitable for presentation to the
and "-") identifying the operating system, excluding any version information and suitable for user. This field is optional. Example:
processing by scripts or usage in generated filenames. If not set, a default of <literal>VERSION=17</literal> or <literal>VERSION="17 (Beefy
<literal>ID=linux</literal> may be used.</para> Miracle)"</literal>.</para></listitem>
</varlistentry>
<para>Examples: <literal>ID=fedora</literal>, <literal>ID=debian</literal>.</para></listitem>
</varlistentry> <varlistentry>
<term><varname>ID=</varname></term>
<varlistentry>
<term><varname>ID_LIKE=</varname></term> <listitem><para>A lower-case string (no spaces or other
characters outside of 09, az, ".", "_" and "-") identifying
<listitem><para>A space-separated list of operating system identifiers in the same syntax as the the operating system, excluding any version information and
<varname>ID=</varname> setting. It should list identifiers of operating systems that are closely suitable for processing by scripts or usage in generated
related to the local operating system in regards to packaging and programming interfaces, for filenames. If not set, defaults to
example listing one or more OS identifiers the local OS is a derivative from. An OS should <literal>ID=linux</literal>. Example:
generally only list other OS identifiers it itself is a derivative of, and not any OSes that are <literal>ID=fedora</literal> or
derived from it, though symmetric relationships are possible. Build scripts and similar should <literal>ID=debian</literal>.</para></listitem>
check this variable if they need to identify the local operating system and the value of </varlistentry>
<varname>ID=</varname> is not recognized. Operating systems should be listed in order of how
closely the local operating system relates to the listed ones, starting with the closest. This <varlistentry>
field is optional.</para> <term><varname>ID_LIKE=</varname></term>
<para>Examples: for an operating system with <literal>ID=centos</literal>, an assignment of <listitem><para>A space-separated list of operating system
<literal>ID_LIKE="rhel fedora"</literal> would be appropriate. For an operating system with identifiers in the same syntax as the <varname>ID=</varname>
<literal>ID=ubuntu</literal>, an assignment of <literal>ID_LIKE=debian</literal> is appropriate. setting. It should list identifiers of operating systems that
</para></listitem> are closely related to the local operating system in regards
</varlistentry> to packaging and programming interfaces, for example listing
one or more OS identifiers the local OS is a derivative from.
<varlistentry> An OS should generally only list other OS identifiers it
<term><varname>PRETTY_NAME=</varname></term> itself is a derivative of, and not any OSes that are derived
from it, though symmetric relationships are possible. Build
<listitem><para>A pretty operating system name in a format suitable for presentation to the scripts and similar should check this variable if they need to
user. May or may not contain a release code name or OS version of some kind, as suitable. If not identify the local operating system and the value of
set, a default of <literal>PRETTY_NAME="Linux"</literal> may be used</para> <varname>ID=</varname> is not recognized. Operating systems
should be listed in order of how closely the local operating
<para>Example: <literal>PRETTY_NAME="Fedora 17 (Beefy Miracle)"</literal>.</para></listitem> system relates to the listed ones, starting with the closest.
</varlistentry> This field is optional. Example: for an operating system with
<literal>ID=centos</literal>, an assignment of
<varlistentry> <literal>ID_LIKE="rhel fedora"</literal> would be appropriate.
<term><varname>CPE_NAME=</varname></term> For an operating system with <literal>ID=ubuntu</literal>, an
assignment of <literal>ID_LIKE=debian</literal> is
<listitem><para>A CPE name for the operating system, in URI binding syntax, following the <ulink appropriate.</para></listitem>
url="http://scap.nist.gov/specifications/cpe/">Common Platform Enumeration Specification</ulink> as </varlistentry>
proposed by the NIST. This field is optional.</para>
<varlistentry>
<para>Example: <literal>CPE_NAME="cpe:/o:fedoraproject:fedora:17"</literal></para></listitem> <term><varname>VERSION_CODENAME=</varname></term>
</varlistentry>
<listitem><para>
<varlistentry> A lower-case string (no spaces or other characters outside of
<term><varname>VARIANT=</varname></term> 09, az, ".", "_" and "-") identifying the operating system
release code name, excluding any OS name information or
<listitem><para>A string identifying a specific variant or edition of the operating system suitable release version, and suitable for processing by scripts or
for presentation to the user. This field may be used to inform the user that the configuration of usage in generated filenames. This field is optional and may
this system is subject to a specific divergent set of rules or default configuration settings. This not be implemented on all systems.
field is optional and may not be implemented on all systems.</para> Examples:
<literal>VERSION_CODENAME=buster</literal>,
<para>Examples: <literal>VARIANT="Server Edition"</literal>, <literal>VARIANT="Smart Refrigerator <literal>VERSION_CODENAME=xenial</literal>
Edition"</literal>.</para> </para></listitem>
</varlistentry>
<para>Note: this field is for display purposes only. The <varname>VARIANT_ID</varname> field should
be used for making programmatic decisions.</para></listitem> <varlistentry>
</varlistentry> <term><varname>VERSION_ID=</varname></term>
<varlistentry> <listitem><para>A lower-case string (mostly numeric, no spaces
<term><varname>VARIANT_ID=</varname></term> or other characters outside of 09, az, ".", "_" and "-")
identifying the operating system version, excluding any OS
<listitem><para>A lower-case string (no spaces or other characters outside of 09, az, ".", "_" and name information or release code name, and suitable for
"-"), identifying a specific variant or edition of the operating system. This may be interpreted by processing by scripts or usage in generated filenames. This
other packages in order to determine a divergent default configuration. This field is optional and field is optional. Example: <literal>VERSION_ID=17</literal>
may not be implemented on all systems.</para> or <literal>VERSION_ID=11.04</literal>.</para></listitem>
</varlistentry>
<para>Examples: <literal>VARIANT_ID=server</literal>, <literal>VARIANT_ID=embedded</literal>.
</para></listitem> <varlistentry>
</varlistentry> <term><varname>PRETTY_NAME=</varname></term>
</variablelist>
</refsect2> <listitem><para>A pretty operating system name in a format
suitable for presentation to the user. May or may not contain
<refsect2> a release code name or OS version of some kind, as suitable.
<title>Information about the version of the operating system</title> If not set, defaults to
<literal>PRETTY_NAME="Linux"</literal>. Example:
<variablelist class='environment-variables'> <literal>PRETTY_NAME="Fedora 17 (Beefy
<varlistentry> Miracle)"</literal>.</para></listitem>
<term><varname>VERSION=</varname></term> </varlistentry>
<listitem><para>A string identifying the operating system version, excluding any OS name <varlistentry>
information, possibly including a release code name, and suitable for presentation to the <term><varname>ANSI_COLOR=</varname></term>
user. This field is optional.</para>
<listitem><para>A suggested presentation color when showing the OS name on the console. This should
<para>Examples: <literal>VERSION=17</literal>, <literal>VERSION="17 (Beefy Miracle)"</literal>. be specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for setting
</para></listitem> graphical rendition. This field is optional. Example: <literal>ANSI_COLOR="0;31"</literal> for red,
</varlistentry> <literal>ANSI_COLOR="1;34"</literal> for light blue, or
<literal>ANSI_COLOR="0;38;2;60;110;180"</literal> for Fedora blue.</para></listitem>
<varlistentry> </varlistentry>
<term><varname>VERSION_ID=</varname></term>
<varlistentry>
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09, <term><varname>CPE_NAME=</varname></term>
az, ".", "_" and "-") identifying the operating system version, excluding any OS name information
or release code name, and suitable for processing by scripts or usage in generated filenames. This <listitem><para>A CPE name for the operating system, in URI
field is optional.</para> binding syntax, following the
<ulink url="http://scap.nist.gov/specifications/cpe/">Common
<para>Examples: <literal>VERSION_ID=17</literal>, <literal>VERSION_ID=11.04</literal>. Platform Enumeration Specification</ulink> as proposed by the
</para></listitem> NIST. This field is optional. Example:
</varlistentry> <literal>CPE_NAME="cpe:/o:fedoraproject:fedora:17"</literal>
</para></listitem>
<varlistentry> </varlistentry>
<term><varname>VERSION_CODENAME=</varname></term>
<varlistentry>
<listitem><para>A lower-case string (no spaces or other characters outside of 09, az, ".", "_" <term><varname>HOME_URL=</varname></term>
and "-") identifying the operating system release code name, excluding any OS name information or <term><varname>DOCUMENTATION_URL=</varname></term>
release version, and suitable for processing by scripts or usage in generated filenames. This field <term><varname>SUPPORT_URL=</varname></term>
is optional and may not be implemented on all systems.</para> <term><varname>BUG_REPORT_URL=</varname></term>
<term><varname>PRIVACY_POLICY_URL=</varname></term>
<para>Examples: <literal>VERSION_CODENAME=buster</literal>,
<literal>VERSION_CODENAME=xenial</literal>.</para></listitem> <listitem><para>Links to resources on the Internet related to
</varlistentry> the operating system.
<varname>HOME_URL=</varname> should refer to the homepage of
<varlistentry> the operating system, or alternatively some homepage of the
<term><varname>BUILD_ID=</varname></term> specific version of the operating system.
<varname>DOCUMENTATION_URL=</varname> should refer to the main
<listitem><para>A string uniquely identifying the system image originally used as the installation documentation page for this operating system.
base. In most cases, <varname>VERSION_ID</varname> or <varname>SUPPORT_URL=</varname> should refer to the main
<varname>IMAGE_ID</varname>+<varname>IMAGE_VERSION</varname> are updated when the entire system support page for the operating system, if there is any. This
image is replaced during an update. <varname>BUILD_ID</varname> may be used in distributions where is primarily intended for operating systems which vendors
the original installation image version is important: <varname>VERSION_ID</varname> would change provide support for. <varname>BUG_REPORT_URL=</varname> should
during incremental system updates, but <varname>BUILD_ID</varname> would not. This field is refer to the main bug reporting page for the operating system,
optional.</para> if there is any. This is primarily intended for operating
systems that rely on community QA.
<para>Examples: <literal>BUILD_ID="2013-03-20.3"</literal>, <literal>BUILD_ID=201303203</literal>. <varname>PRIVACY_POLICY_URL=</varname> should refer to the
</para></listitem> main privacy policy page for the operating system, if there is
</varlistentry> any. These settings are optional, and providing only some of
these settings is common. These URLs are intended to be
<varlistentry> exposed in "About this system" UIs behind links with captions
<term><varname>IMAGE_ID=</varname></term> such as "About this Operating System", "Obtain Support",
"Report a Bug", or "Privacy Policy". The values should be in
<listitem><para> A lower-case string (no spaces or other characters outside of 09, az, ".", "_" <ulink url="https://tools.ietf.org/html/rfc3986">RFC3986
and "-"), identifying a specific image of the operating system. This is supposed to be used for format</ulink>, and should be <literal>http:</literal> or
environments where OS images are prepared, built, shipped and updated as comprehensive, consistent <literal>https:</literal> URLs, and possibly
OS images. This field is optional and may not be implemented on all systems, in particularly not on <literal>mailto:</literal> or <literal>tel:</literal>. Only
those that are not managed via images but put together and updated from individual packages and on one URL shall be listed in each setting. If multiple resources
the local system.</para> need to be referenced, it is recommended to provide an online
landing page linking all available resources. Examples:
<para>Examples: <literal>IMAGE_ID=vendorx-cashier-system</literal>, <literal>HOME_URL="https://fedoraproject.org/"</literal> and
<literal>IMAGE_ID=netbook-image</literal>.</para></listitem> <literal>BUG_REPORT_URL="https://bugzilla.redhat.com/"</literal></para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>IMAGE_VERSION=</varname></term> <term><varname>BUILD_ID=</varname></term>
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09, <listitem><para>A string uniquely identifying the system image
az, ".", "_" and "-") identifying the OS image version. This is supposed to be used together with used as the origin for a distribution (it is not updated with
<varname>IMAGE_ID</varname> described above, to discern different versions of the same image. system updates). The field can be identical between different
</para> VERSION_IDs as BUILD_ID is an only a unique identifier to a
specific version. Distributions that release each update as a
<para>Examples: <literal>IMAGE_VERSION=33</literal>, <literal>IMAGE_VERSION=47.1rc1</literal>. new version would only need to use VERSION_ID as each build is
</para></listitem> already distinct based on the VERSION_ID. This field is
</varlistentry> optional. Example: <literal>BUILD_ID="2013-03-20.3"</literal>
</variablelist> or <literal>BUILD_ID=201303203</literal>.
<para>To summarize: if the image updates are built and shipped as comprehensive units, </para></listitem>
<varname>IMAGE_ID</varname>+<varname>IMAGE_VERSION</varname> is the best fit. Otherwise, if updates </varlistentry>
eventually completely replace previously installed contents, as in a typical binary distribution,
<varname>VERSION_ID</varname> should be used to identify major releases of the operating system. <varlistentry>
<varname>BUILD_ID</varname> may be used instead or in addition to <varname>VERSION_ID</varname> when <term><varname>VARIANT=</varname></term>
the original system image version is important.</para>
</refsect2> <listitem><para>
A string identifying a specific variant or edition of the
<refsect2> operating system suitable for presentation to the user. This
<title>Presentation information and links</title> field may be used to inform the user that the configuration of
this system is subject to a specific divergent set of rules or
<variablelist class='environment-variables'> default configuration settings. This field is optional and may
<varlistentry> not be implemented on all systems.
<term><varname>HOME_URL=</varname></term> Examples:
<term><varname>DOCUMENTATION_URL=</varname></term> <literal>VARIANT="Server Edition"</literal>,
<term><varname>SUPPORT_URL=</varname></term> <literal>VARIANT="Smart Refrigerator Edition"</literal>
<term><varname>BUG_REPORT_URL=</varname></term> Note: this field is for display purposes only. The
<term><varname>PRIVACY_POLICY_URL=</varname></term> <varname>VARIANT_ID</varname> field should be used for making
programmatic decisions.
<listitem><para>Links to resources on the Internet related to the operating system. </para></listitem>
<varname>HOME_URL=</varname> should refer to the homepage of the operating system, or alternatively </varlistentry>
some homepage of the specific version of the operating system.
<varname>DOCUMENTATION_URL=</varname> should refer to the main documentation page for this <varlistentry>
operating system. <varname>SUPPORT_URL=</varname> should refer to the main support page for the <term><varname>VARIANT_ID=</varname></term>
operating system, if there is any. This is primarily intended for operating systems which vendors
provide support for. <varname>BUG_REPORT_URL=</varname> should refer to the main bug reporting page <listitem><para>
for the operating system, if there is any. This is primarily intended for operating systems that A lower-case string (no spaces or other characters outside of
rely on community QA. <varname>PRIVACY_POLICY_URL=</varname> should refer to the main privacy 09, az, ".", "_" and "-"), identifying a specific variant or
policy page for the operating system, if there is any. These settings are optional, and providing edition of the operating system. This may be interpreted by
only some of these settings is common. These URLs are intended to be exposed in "About this system" other packages in order to determine a divergent default
UIs behind links with captions such as "About this Operating System", "Obtain Support", "Report a configuration. This field is optional and may not be
Bug", or "Privacy Policy". The values should be in <ulink implemented on all systems.
url="https://tools.ietf.org/html/rfc3986">RFC3986 format</ulink>, and should be Examples:
<literal>http:</literal> or <literal>https:</literal> URLs, and possibly <literal>mailto:</literal> <literal>VARIANT_ID=server</literal>,
or <literal>tel:</literal>. Only one URL shall be listed in each setting. If multiple resources <literal>VARIANT_ID=embedded</literal>
need to be referenced, it is recommended to provide an online landing page linking all available </para></listitem>
resources.</para> </varlistentry>
<para>Examples: <literal>HOME_URL="https://fedoraproject.org/"</literal>, <varlistentry>
<literal>BUG_REPORT_URL="https://bugzilla.redhat.com/"</literal>.</para></listitem> <term><varname>LOGO=</varname></term>
</varlistentry>
<listitem><para>
<varlistentry> A string, specifying the name of an icon as defined by <ulink
<term><varname>LOGO=</varname></term> url="http://standards.freedesktop.org/icon-theme-spec/latest">
freedesktop.org Icon Theme Specification</ulink>. This can be
<listitem><para>A string, specifying the name of an icon as defined by <ulink used by graphical applications to display an operating
url="http://standards.freedesktop.org/icon-theme-spec/latest">freedesktop.org Icon Theme system's or distributor's logo. This field is optional and
Specification</ulink>. This can be used by graphical applications to display an operating system's may not necessarily be implemented on all systems.
or distributor's logo. This field is optional and may not necessarily be implemented on all Examples:
systems.</para> <literal>LOGO=fedora-logo</literal>,
<literal>LOGO=distributor-logo-opensuse</literal>
<para>Examples: <literal>LOGO=fedora-logo</literal>, <literal>LOGO=distributor-logo-opensuse</literal> </para></listitem>
</para></listitem> </varlistentry>
</varlistentry>
<varlistentry>
<varlistentry> <term><varname>DEFAULT_HOSTNAME=</varname></term>
<term><varname>ANSI_COLOR=</varname></term>
<listitem><para>A string specifying the hostname if
<listitem><para>A suggested presentation color when showing the OS name on the console. This should <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry> is not
be specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for setting present and no other configuration source specifies the hostname. Must be either a single DNS label
graphical rendition. This field is optional.</para> (a string composed of 7-bit ASCII lower-case characters and no spaces or dots, limited to the format
allowed for DNS domain name labels), or a sequence of such labels separated by single dots that forms
<para>Examples: <literal>ANSI_COLOR="0;31"</literal> for red, <literal>ANSI_COLOR="1;34"</literal> a valid DNS FQDN. The hostname must be at most 64 characters, which is a Linux limitation (DNS allows
for light blue, or <literal>ANSI_COLOR="0;38;2;60;110;180"</literal> for Fedora blue. longer names).</para>
</para></listitem>
</varlistentry> <para>See
</variablelist> <citerefentry><refentrytitle>org.freedesktop.hostname1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
</refsect2> for a description of how
<citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
<refsect2> determines the fallback hostname.</para></listitem>
<title>Distribution-level defaults and metadata</title> </varlistentry>
<variablelist class='environment-variables'> <varlistentry>
<varlistentry> <term><varname>SYSEXT_LEVEL=</varname></term>
<term><varname>DEFAULT_HOSTNAME=</varname></term>
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09,
<listitem><para>A string specifying the hostname if az, ".", "_" and "-") identifying the operating system extensions support level, to indicate which
<citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry> is not extension images are supported (See:
present and no other configuration source specifies the hostname. Must be either a single DNS label <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
(a string composed of 7-bit ASCII lower-case characters and no spaces or dots, limited to the Example: <literal>SYSEXT_LEVEL=2</literal> or
format allowed for DNS domain name labels), or a sequence of such labels separated by single dots <literal>SYSEXT_LEVEL=15.14</literal>.</para></listitem>
that forms a valid DNS FQDN. The hostname must be at most 64 characters, which is a Linux </varlistentry>
limitation (DNS allows longer names).</para>
<varlistentry>
<para>See <citerefentry><refentrytitle>org.freedesktop.hostname1</refentrytitle><manvolnum>5</manvolnum></citerefentry> <term><varname>IMAGE_ID=</varname></term>
for a description of how
<citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> <listitem><para> A lower-case string (no spaces or other characters outside of 09, az, ".", "_" and
determines the fallback hostname.</para></listitem> "-"), identifying a specific image of the operating system. This is supposed to be used for
</varlistentry> environments where OS images are prepared, built, shipped and updated as comprehensive, consistent OS
images. This field is optional and may not be implemented on all systems, in particularly not on those
<varlistentry> that are not managed via images but put together and updated from individual packages and on the
<term><varname>SYSEXT_LEVEL=</varname></term> local system. Examples: <literal>IMAGE_ID=vendorx-cashier-system</literal>,
<literal>IMAGE_ID=netbook-image</literal> </para></listitem>
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09, </varlistentry>
az, ".", "_" and "-") identifying the operating system extensions support level, to indicate which
extension images are supported. See: <varlistentry>
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>) <term><varname>IMAGE_VERSION=</varname></term>
for more information.</para>
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09,
<para>Examples: <literal>SYSEXT_LEVEL=2</literal>, <literal>SYSEXT_LEVEL=15.14</literal>. az, ".", "_" and "-") identifying the OS image version. This is supposed to be used together with
</para></listitem> <varname>IMAGE_ID</varname> described above, to discern different versions of the same
</varlistentry> image. Examples: <literal>IMAGE_VERSION=33</literal>,
</variablelist> <literal>IMAGE_VERSION=47.1rc1</literal> </para></listitem>
</refsect2> </varlistentry>
<refsect2> </variablelist>
<title>Notes</title>
<para>If you are reading this file from C code or a shell script
<para>If you are using this file to determine the OS or a specific version of it, use the to determine the OS or a specific version of it, use the
<varname>ID</varname> and <varname>VERSION_ID</varname> fields, possibly with <varname>ID</varname> and <varname>VERSION_ID</varname> fields,
<varname>ID_LIKE</varname> as fallback for <varname>ID</varname>. When looking for an OS identification possibly with <varname>ID_LIKE</varname> as fallback for
string for presentation to the user use the <varname>PRETTY_NAME</varname> field.</para> <varname>ID</varname>. When looking for an OS identification
string for presentation to the user use the
<para>Note that operating system vendors may choose not to provide version information, for example to <varname>PRETTY_NAME</varname> field.</para>
accommodate for rolling releases. In this case, <varname>VERSION</varname> and
<varname>VERSION_ID</varname> may be unset. Applications should not rely on these fields to be <para>Note that operating system vendors may choose not to provide
set.</para> version information, for example to accommodate for rolling
releases. In this case, <varname>VERSION</varname> and
<para>Operating system vendors may extend the file format and introduce new fields. It is highly <varname>VERSION_ID</varname> may be unset. Applications should
recommended to prefix new fields with an OS specific name in order to avoid name clashes. Applications not rely on these fields to be set.</para>
reading this file must ignore unknown fields.</para>
<para>Operating system vendors may extend the file
<para>Example: <literal>DEBIAN_BTS="debbugs://bugs.debian.org/"</literal>.</para> format and introduce new fields. It is highly
recommended to prefix new fields with an OS specific
<para>Container and sandbox runtime managers may make the host's identification data available to name in order to avoid name clashes. Applications
applications by providing the host's <filename>/etc/os-release</filename> (if available, otherwise reading this file must ignore unknown fields. Example:
<filename>/usr/lib/os-release</filename> as a fallback) as <literal>DEBIAN_BTS="debbugs://bugs.debian.org/"</literal></para>
<filename>/run/host/os-release</filename>.</para>
</refsect2> <para>Container and sandbox runtime managers may make the host's
identification data available to applications by providing the host's
<filename>/etc/os-release</filename> (if available, otherwise
<filename>/usr/lib/os-release</filename> as a fallback) as
<filename>/run/host/os-release</filename>.</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
<title>Examples</title> <title>Example</title>
<example> <programlisting>NAME=Fedora
<title><filename>os-release</filename> file for Fedora Workstation</title>
<programlisting>NAME=Fedora
VERSION="32 (Workstation Edition)" VERSION="32 (Workstation Edition)"
ID=fedora ID=fedora
VERSION_ID=32 VERSION_ID=32
@ -434,22 +420,6 @@ REDHAT_SUPPORT_PRODUCT_VERSION=32
PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy" PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
VARIANT="Workstation Edition" VARIANT="Workstation Edition"
VARIANT_ID=workstation</programlisting> VARIANT_ID=workstation</programlisting>
</example>
<example>
<title>Reading <filename>os-release</filename> in
<citerefentry><refentrytitle>sh</refentrytitle><manvolnum>1</manvolnum></citerefentry></title>
<programlisting><xi:include href="check-os-release.sh" parse="text" /></programlisting>
</example>
<example>
<title>Reading <filename>os-release</filename> in
<citerefentry><refentrytitle>python</refentrytitle><manvolnum>1</manvolnum></citerefentry></title>
<programlisting><xi:include href="check-os-release.py" parse="text" /></programlisting>
</example>
</refsect1> </refsect1>
<refsect1> <refsect1>

View File

@ -61,7 +61,7 @@ manpages = [
['org.freedesktop.resolve1', '5', [], 'ENABLE_RESOLVE'], ['org.freedesktop.resolve1', '5', [], 'ENABLE_RESOLVE'],
['org.freedesktop.systemd1', '5', [], ''], ['org.freedesktop.systemd1', '5', [], ''],
['org.freedesktop.timedate1', '5', [], 'ENABLE_TIMEDATED'], ['org.freedesktop.timedate1', '5', [], 'ENABLE_TIMEDATED'],
['os-release', '5', ['initrd-release'], ''], ['os-release', '5', [], ''],
['pam_systemd', '8', [], 'HAVE_PAM'], ['pam_systemd', '8', [], 'HAVE_PAM'],
['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'], ['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'],
['portablectl', '1', [], 'ENABLE_PORTABLED'], ['portablectl', '1', [], 'ENABLE_PORTABLED'],

View File

@ -102,20 +102,20 @@ int manager_handle_action(
} }
if (handle == HANDLE_SUSPEND) if (handle == HANDLE_SUSPEND)
supported = can_sleep(SLEEP_SUSPEND) > 0; supported = can_sleep("suspend") > 0;
else if (handle == HANDLE_HIBERNATE) else if (handle == HANDLE_HIBERNATE)
supported = can_sleep(SLEEP_HIBERNATE) > 0; supported = can_sleep("hibernate") > 0;
else if (handle == HANDLE_HYBRID_SLEEP) else if (handle == HANDLE_HYBRID_SLEEP)
supported = can_sleep(SLEEP_HYBRID_SLEEP) > 0; supported = can_sleep("hybrid-sleep") > 0;
else if (handle == HANDLE_SUSPEND_THEN_HIBERNATE) else if (handle == HANDLE_SUSPEND_THEN_HIBERNATE)
supported = can_sleep(SLEEP_SUSPEND_THEN_HIBERNATE) > 0; supported = can_sleep("suspend-then-hibernate") > 0;
else if (handle == HANDLE_KEXEC) else if (handle == HANDLE_KEXEC)
supported = access(KEXEC, X_OK) >= 0; supported = access(KEXEC, X_OK) >= 0;
else else
supported = true; supported = true;
if (!supported && IN_SET(handle, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP, HANDLE_SUSPEND_THEN_HIBERNATE)) { if (!supported && IN_SET(handle, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP, HANDLE_SUSPEND_THEN_HIBERNATE)) {
supported = can_sleep(SLEEP_SUSPEND) > 0; supported = can_sleep("suspend") > 0;
if (supported) { if (supported) {
log_notice("Requested %s operation is not supported, using regular suspend instead.", log_notice("Requested %s operation is not supported, using regular suspend instead.",
handle_action_to_string(handle)); handle_action_to_string(handle));

View File

@ -1851,7 +1851,7 @@ static int method_do_shutdown_or_sleep(
const char *action, const char *action,
const char *action_multiple_sessions, const char *action_multiple_sessions,
const char *action_ignore_inhibit, const char *action_ignore_inhibit,
SleepOperation sleep_operation, const char *sleep_verb,
bool with_flags, bool with_flags,
sd_bus_error *error) { sd_bus_error *error) {
@ -1894,14 +1894,14 @@ static int method_do_shutdown_or_sleep(
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
"There's already a shutdown or sleep operation in progress"); "There's already a shutdown or sleep operation in progress");
if (sleep_operation >= 0) { if (sleep_verb) {
r = can_sleep(sleep_operation); r = can_sleep(sleep_verb);
if (r == -ENOSPC) if (r == -ENOSPC)
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Not enough swap space for hibernation"); "Not enough swap space for hibernation");
if (r == 0) if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Sleep verb \"%s\" not supported", sleep_operation_to_string(sleep_operation)); "Sleep verb \"%s\" not supported", sleep_verb);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -1928,7 +1928,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
"org.freedesktop.login1.power-off", "org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions", "org.freedesktop.login1.power-off-multiple-sessions",
"org.freedesktop.login1.power-off-ignore-inhibit", "org.freedesktop.login1.power-off-ignore-inhibit",
_SLEEP_OPERATION_INVALID, NULL,
sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"), sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"),
error); error);
} }
@ -1943,7 +1943,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
"org.freedesktop.login1.reboot", "org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions", "org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.reboot-ignore-inhibit", "org.freedesktop.login1.reboot-ignore-inhibit",
_SLEEP_OPERATION_INVALID, NULL,
sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"), sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"),
error); error);
} }
@ -1958,7 +1958,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
"org.freedesktop.login1.halt", "org.freedesktop.login1.halt",
"org.freedesktop.login1.halt-multiple-sessions", "org.freedesktop.login1.halt-multiple-sessions",
"org.freedesktop.login1.halt-ignore-inhibit", "org.freedesktop.login1.halt-ignore-inhibit",
_SLEEP_OPERATION_INVALID, NULL,
sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"), sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"),
error); error);
} }
@ -1973,7 +1973,7 @@ static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error
"org.freedesktop.login1.suspend", "org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions", "org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.suspend-ignore-inhibit", "org.freedesktop.login1.suspend-ignore-inhibit",
SLEEP_SUSPEND, "suspend",
sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"), sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"),
error); error);
} }
@ -1988,7 +1988,7 @@ static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_erro
"org.freedesktop.login1.hibernate", "org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit", "org.freedesktop.login1.hibernate-ignore-inhibit",
SLEEP_HIBERNATE, "hibernate",
sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"), sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"),
error); error);
} }
@ -2003,7 +2003,7 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e
"org.freedesktop.login1.hibernate", "org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit", "org.freedesktop.login1.hibernate-ignore-inhibit",
SLEEP_HYBRID_SLEEP, "hybrid-sleep",
sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"), sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"),
error); error);
} }
@ -2018,7 +2018,7 @@ static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata
"org.freedesktop.login1.hibernate", "org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit", "org.freedesktop.login1.hibernate-ignore-inhibit",
SLEEP_SUSPEND_THEN_HIBERNATE, "hybrid-sleep",
sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"), sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"),
error); error);
} }
@ -2317,7 +2317,7 @@ static int method_can_shutdown_or_sleep(
const char *action, const char *action,
const char *action_multiple_sessions, const char *action_multiple_sessions,
const char *action_ignore_inhibit, const char *action_ignore_inhibit,
SleepOperation sleep_operation, const char *sleep_verb,
sd_bus_error *error) { sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
@ -2335,8 +2335,8 @@ static int method_can_shutdown_or_sleep(
assert(action_multiple_sessions); assert(action_multiple_sessions);
assert(action_ignore_inhibit); assert(action_ignore_inhibit);
if (sleep_operation >= 0) { if (sleep_verb) {
r = can_sleep(sleep_operation); r = can_sleep(sleep_verb);
if (IN_SET(r, 0, -ENOSPC)) if (IN_SET(r, 0, -ENOSPC))
return sd_bus_reply_method_return(message, "s", "na"); return sd_bus_reply_method_return(message, "s", "na");
if (r < 0) if (r < 0)
@ -2358,7 +2358,7 @@ static int method_can_shutdown_or_sleep(
multiple_sessions = r > 0; multiple_sessions = r > 0;
blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
handle = handle_action_from_string(sleep_operation_to_string(sleep_operation)); handle = handle_action_from_string(sleep_verb);
if (handle >= 0) { if (handle >= 0) {
const char *target; const char *target;
@ -2434,7 +2434,7 @@ static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_e
"org.freedesktop.login1.power-off", "org.freedesktop.login1.power-off",
"org.freedesktop.login1.power-off-multiple-sessions", "org.freedesktop.login1.power-off-multiple-sessions",
"org.freedesktop.login1.power-off-ignore-inhibit", "org.freedesktop.login1.power-off-ignore-inhibit",
_SLEEP_OPERATION_INVALID, NULL,
error); error);
} }
@ -2447,7 +2447,7 @@ static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_err
"org.freedesktop.login1.reboot", "org.freedesktop.login1.reboot",
"org.freedesktop.login1.reboot-multiple-sessions", "org.freedesktop.login1.reboot-multiple-sessions",
"org.freedesktop.login1.reboot-ignore-inhibit", "org.freedesktop.login1.reboot-ignore-inhibit",
_SLEEP_OPERATION_INVALID, NULL,
error); error);
} }
@ -2460,7 +2460,7 @@ static int method_can_halt(sd_bus_message *message, void *userdata, sd_bus_error
"org.freedesktop.login1.halt", "org.freedesktop.login1.halt",
"org.freedesktop.login1.halt-multiple-sessions", "org.freedesktop.login1.halt-multiple-sessions",
"org.freedesktop.login1.halt-ignore-inhibit", "org.freedesktop.login1.halt-ignore-inhibit",
_SLEEP_OPERATION_INVALID, NULL,
error); error);
} }
@ -2473,7 +2473,7 @@ static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_er
"org.freedesktop.login1.suspend", "org.freedesktop.login1.suspend",
"org.freedesktop.login1.suspend-multiple-sessions", "org.freedesktop.login1.suspend-multiple-sessions",
"org.freedesktop.login1.suspend-ignore-inhibit", "org.freedesktop.login1.suspend-ignore-inhibit",
SLEEP_SUSPEND, "suspend",
error); error);
} }
@ -2486,7 +2486,7 @@ static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_
"org.freedesktop.login1.hibernate", "org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit", "org.freedesktop.login1.hibernate-ignore-inhibit",
SLEEP_HIBERNATE, "hibernate",
error); error);
} }
@ -2499,7 +2499,7 @@ static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_b
"org.freedesktop.login1.hibernate", "org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit", "org.freedesktop.login1.hibernate-ignore-inhibit",
SLEEP_HYBRID_SLEEP, "hybrid-sleep",
error); error);
} }
@ -2512,7 +2512,7 @@ static int method_can_suspend_then_hibernate(sd_bus_message *message, void *user
"org.freedesktop.login1.hibernate", "org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions", "org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit", "org.freedesktop.login1.hibernate-ignore-inhibit",
SLEEP_SUSPEND_THEN_HIBERNATE, "suspend-then-hibernate",
error); error);
} }

View File

@ -29,7 +29,6 @@
#include "path-util.h" #include "path-util.h"
#include "sleep-config.h" #include "sleep-config.h"
#include "stdio-util.h" #include "stdio-util.h"
#include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "time-util.h" #include "time-util.h"
@ -44,19 +43,19 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) {
return log_oom(); return log_oom();
const ConfigTableItem items[] = { const ConfigTableItem items[] = {
{ "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend }, { "Sleep", "AllowSuspend", config_parse_tristate, 0, &allow_suspend },
{ "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate }, { "Sleep", "AllowHibernation", config_parse_tristate, 0, &allow_hibernate },
{ "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h }, { "Sleep", "AllowSuspendThenHibernate", config_parse_tristate, 0, &allow_s2h },
{ "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep }, { "Sleep", "AllowHybridSleep", config_parse_tristate, 0, &allow_hybrid_sleep },
{ "Sleep", "SuspendMode", config_parse_strv, 0, sc->modes + SLEEP_SUSPEND }, { "Sleep", "SuspendMode", config_parse_strv, 0, &sc->suspend_modes },
{ "Sleep", "SuspendState", config_parse_strv, 0, sc->states + SLEEP_SUSPEND }, { "Sleep", "SuspendState", config_parse_strv, 0, &sc->suspend_states },
{ "Sleep", "HibernateMode", config_parse_strv, 0, sc->modes + SLEEP_HIBERNATE }, { "Sleep", "HibernateMode", config_parse_strv, 0, &sc->hibernate_modes },
{ "Sleep", "HibernateState", config_parse_strv, 0, sc->states + SLEEP_HIBERNATE }, { "Sleep", "HibernateState", config_parse_strv, 0, &sc->hibernate_states },
{ "Sleep", "HybridSleepMode", config_parse_strv, 0, sc->modes + SLEEP_HYBRID_SLEEP }, { "Sleep", "HybridSleepMode", config_parse_strv, 0, &sc->hybrid_modes },
{ "Sleep", "HybridSleepState", config_parse_strv, 0, sc->states + SLEEP_HYBRID_SLEEP }, { "Sleep", "HybridSleepState", config_parse_strv, 0, &sc->hybrid_states },
{ "Sleep", "HibernateDelaySec", config_parse_sec, 0, &sc->hibernate_delay_sec }, { "Sleep", "HibernateDelaySec", config_parse_sec, 0, &sc->hibernate_delay_sec},
{} {}
}; };
@ -70,29 +69,29 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) {
NULL); NULL);
/* use default values unless set */ /* use default values unless set */
sc->allow[SLEEP_SUSPEND] = allow_suspend != 0; sc->allow_suspend = allow_suspend != 0;
sc->allow[SLEEP_HIBERNATE] = allow_hibernate != 0; sc->allow_hibernate = allow_hibernate != 0;
sc->allow[SLEEP_HYBRID_SLEEP] = allow_hybrid_sleep >= 0 ? allow_hybrid_sleep sc->allow_hybrid_sleep = allow_hybrid_sleep >= 0 ? allow_hybrid_sleep
: (allow_suspend != 0 && allow_hibernate != 0); : (allow_suspend != 0 && allow_hibernate != 0);
sc->allow[SLEEP_SUSPEND_THEN_HIBERNATE] = allow_s2h >= 0 ? allow_s2h sc->allow_s2h = allow_s2h >= 0 ? allow_s2h
: (allow_suspend != 0 && allow_hibernate != 0); : (allow_suspend != 0 && allow_hibernate != 0);
if (!sc->states[SLEEP_SUSPEND]) if (!sc->suspend_states)
sc->states[SLEEP_SUSPEND] = strv_new("mem", "standby", "freeze"); sc->suspend_states = strv_new("mem", "standby", "freeze");
if (!sc->modes[SLEEP_HIBERNATE]) if (!sc->hibernate_modes)
sc->modes[SLEEP_HIBERNATE] = strv_new("platform", "shutdown"); sc->hibernate_modes = strv_new("platform", "shutdown");
if (!sc->states[SLEEP_HIBERNATE]) if (!sc->hibernate_states)
sc->states[SLEEP_HIBERNATE] = strv_new("disk"); sc->hibernate_states = strv_new("disk");
if (!sc->modes[SLEEP_HYBRID_SLEEP]) if (!sc->hybrid_modes)
sc->modes[SLEEP_HYBRID_SLEEP] = strv_new("suspend", "platform", "shutdown"); sc->hybrid_modes = strv_new("suspend", "platform", "shutdown");
if (!sc->states[SLEEP_HYBRID_SLEEP]) if (!sc->hybrid_states)
sc->states[SLEEP_HYBRID_SLEEP] = strv_new("disk"); sc->hybrid_states = strv_new("disk");
if (sc->hibernate_delay_sec == 0) if (sc->hibernate_delay_sec == 0)
sc->hibernate_delay_sec = 2 * USEC_PER_HOUR; sc->hibernate_delay_sec = 2 * USEC_PER_HOUR;
/* ensure values set for all required fields */ /* ensure values set for all required fields */
if (!sc->states[SLEEP_SUSPEND] || !sc->modes[SLEEP_HIBERNATE] if (!sc->suspend_states || !sc->hibernate_modes
|| !sc->states[SLEEP_HIBERNATE] || !sc->modes[SLEEP_HYBRID_SLEEP] || !sc->states[SLEEP_HYBRID_SLEEP]) || !sc->hibernate_states || !sc->hybrid_modes || !sc->hybrid_states)
return log_oom(); return log_oom();
*ret_sleep_config = TAKE_PTR(sc); *ret_sleep_config = TAKE_PTR(sc);
@ -589,15 +588,10 @@ int read_fiemap(int fd, struct fiemap **ret) {
return 0; return 0;
} }
static int can_sleep_internal(const SleepConfig *sleep_config, SleepOperation operation, bool check_allowed); static int can_sleep_internal(const char *verb, bool check_allowed, const SleepConfig *sleep_config);
static bool can_s2h(const SleepConfig *sleep_config) { static bool can_s2h(const SleepConfig *sleep_config) {
const char *p;
static const SleepOperation operations[] = {
SLEEP_SUSPEND,
SLEEP_HIBERNATE,
};
int r; int r;
if (!clock_supported(CLOCK_BOOTTIME_ALARM)) { if (!clock_supported(CLOCK_BOOTTIME_ALARM)) {
@ -605,40 +599,42 @@ static bool can_s2h(const SleepConfig *sleep_config) {
return false; return false;
} }
for (size_t i = 0; i < ELEMENTSOF(operations); i++) { FOREACH_STRING(p, "suspend", "hibernate") {
r = can_sleep_internal(sleep_config, operations[i], false); r = can_sleep_internal(p, false, sleep_config);
if (IN_SET(r, 0, -ENOSPC)) { if (IN_SET(r, 0, -ENOSPC, -EADV)) {
log_debug("Unable to %s system.", sleep_operation_to_string(operations[i])); log_debug("Unable to %s system.", p);
return false; return false;
} }
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to check if %s is possible: %m", sleep_operation_to_string(operations[i])); return log_debug_errno(r, "Failed to check if %s is possible: %m", p);
} }
return true; return true;
} }
static int can_sleep_internal( static int can_sleep_internal(const char *verb, bool check_allowed, const SleepConfig *sleep_config) {
const SleepConfig *sleep_config, bool allow;
SleepOperation operation, char **modes = NULL, **states = NULL;
bool check_allowed) { int r;
assert(operation >= 0); assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
assert(operation < _SLEEP_OPERATION_MAX);
if (check_allowed && !sleep_config->allow[operation]) { r = sleep_settings(verb, sleep_config, &allow, &modes, &states);
log_debug("Sleep mode \"%s\" is disabled by configuration.", sleep_operation_to_string(operation)); if (r < 0)
return false;
if (check_allowed && !allow) {
log_debug("Sleep mode \"%s\" is disabled by configuration.", verb);
return false; return false;
} }
if (operation == SLEEP_SUSPEND_THEN_HIBERNATE) if (streq(verb, "suspend-then-hibernate"))
return can_s2h(sleep_config); return can_s2h(sleep_config);
if (can_sleep_state(sleep_config->states[operation]) <= 0 || if (!can_sleep_state(states) || !can_sleep_disk(modes))
can_sleep_disk(sleep_config->modes[operation]) <= 0)
return false; return false;
if (operation == SLEEP_SUSPEND) if (streq(verb, "suspend"))
return true; return true;
if (!enough_swap_for_hibernation()) if (!enough_swap_for_hibernation())
@ -647,7 +643,7 @@ static int can_sleep_internal(
return true; return true;
} }
int can_sleep(SleepOperation operation) { int can_sleep(const char *verb) {
_cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL; _cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL;
int r; int r;
@ -655,26 +651,51 @@ int can_sleep(SleepOperation operation) {
if (r < 0) if (r < 0)
return r; return r;
return can_sleep_internal(sleep_config, operation, true); return can_sleep_internal(verb, true, sleep_config);
}
int sleep_settings(const char *verb, const SleepConfig *sleep_config, bool *ret_allow, char ***ret_modes, char ***ret_states) {
assert(verb);
assert(sleep_config);
assert(STR_IN_SET(verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"));
if (streq(verb, "suspend")) {
*ret_allow = sleep_config->allow_suspend;
*ret_modes = sleep_config->suspend_modes;
*ret_states = sleep_config->suspend_states;
} else if (streq(verb, "hibernate")) {
*ret_allow = sleep_config->allow_hibernate;
*ret_modes = sleep_config->hibernate_modes;
*ret_states = sleep_config->hibernate_states;
} else if (streq(verb, "hybrid-sleep")) {
*ret_allow = sleep_config->allow_hybrid_sleep;
*ret_modes = sleep_config->hybrid_modes;
*ret_states = sleep_config->hybrid_states;
} else if (streq(verb, "suspend-then-hibernate")) {
*ret_allow = sleep_config->allow_s2h;
*ret_modes = *ret_states = NULL;
}
/* suspend modes empty by default */
if ((!ret_modes && !streq(verb, "suspend")) || !ret_states)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No modes or states set for %s; Check sleep.conf", verb);
return 0;
} }
SleepConfig* free_sleep_config(SleepConfig *sc) { SleepConfig* free_sleep_config(SleepConfig *sc) {
if (!sc) if (!sc)
return NULL; return NULL;
for (SleepOperation i = 0; i < _SLEEP_OPERATION_MAX; i++) { strv_free(sc->suspend_modes);
strv_free(sc->modes[i]); strv_free(sc->suspend_states);
strv_free(sc->states[i]);
} strv_free(sc->hibernate_modes);
strv_free(sc->hibernate_states);
strv_free(sc->hybrid_modes);
strv_free(sc->hybrid_states);
return mfree(sc); return mfree(sc);
} }
static const char* const sleep_operation_table[_SLEEP_OPERATION_MAX] = {
[SLEEP_SUSPEND] = "suspend",
[SLEEP_HIBERNATE] = "hibernate",
[SLEEP_HYBRID_SLEEP] = "hybrid-sleep",
[SLEEP_SUSPEND_THEN_HIBERNATE] = "suspend-then-hibernate",
};
DEFINE_STRING_TABLE_LOOKUP(sleep_operation, SleepOperation);

View File

@ -4,20 +4,20 @@
#include <linux/fiemap.h> #include <linux/fiemap.h>
#include "time-util.h" #include "time-util.h"
typedef enum SleepOperation {
SLEEP_SUSPEND,
SLEEP_HIBERNATE,
SLEEP_HYBRID_SLEEP,
SLEEP_SUSPEND_THEN_HIBERNATE,
_SLEEP_OPERATION_MAX,
_SLEEP_OPERATION_INVALID = -EINVAL,
} SleepOperation;
typedef struct SleepConfig { typedef struct SleepConfig {
bool allow[_SLEEP_OPERATION_MAX]; bool allow_suspend; /* AllowSuspend */
char **modes[_SLEEP_OPERATION_MAX]; bool allow_hibernate; /* AllowHibernation */
char **states[_SLEEP_OPERATION_MAX]; bool allow_s2h; /* AllowSuspendThenHibernate */
usec_t hibernate_delay_sec; bool allow_hybrid_sleep; /* AllowHybridSleep */
char **suspend_modes; /* SuspendMode */
char **suspend_states; /* SuspendState */
char **hibernate_modes; /* HibernateMode */
char **hibernate_states; /* HibernateState */
char **hybrid_modes; /* HybridSleepMode */
char **hybrid_states; /* HybridSleepState */
usec_t hibernate_delay_sec; /* HibernateDelaySec */
} SleepConfig; } SleepConfig;
SleepConfig* free_sleep_config(SleepConfig *sc); SleepConfig* free_sleep_config(SleepConfig *sc);
@ -48,13 +48,12 @@ typedef struct HibernateLocation {
HibernateLocation* hibernate_location_free(HibernateLocation *hl); HibernateLocation* hibernate_location_free(HibernateLocation *hl);
DEFINE_TRIVIAL_CLEANUP_FUNC(HibernateLocation*, hibernate_location_free); DEFINE_TRIVIAL_CLEANUP_FUNC(HibernateLocation*, hibernate_location_free);
int sleep_settings(const char *verb, const SleepConfig *sleep_config, bool *ret_allow, char ***ret_modes, char ***ret_states);
int read_fiemap(int fd, struct fiemap **ret); int read_fiemap(int fd, struct fiemap **ret);
int parse_sleep_config(SleepConfig **sleep_config); int parse_sleep_config(SleepConfig **sleep_config);
int find_hibernate_location(HibernateLocation **ret_hibernate_location); int find_hibernate_location(HibernateLocation **ret_hibernate_location);
int can_sleep(SleepOperation operation); int can_sleep(const char *verb);
int can_sleep_disk(char **types); int can_sleep_disk(char **types);
int can_sleep_state(char **types); int can_sleep_state(char **types);
const char* sleep_operation_to_string(SleepOperation s) _const_;
SleepOperation sleep_operation_from_string(const char *s) _pure_;

View File

@ -35,7 +35,9 @@
#include "time-util.h" #include "time-util.h"
#include "util.h" #include "util.h"
static SleepOperation arg_operation = _SLEEP_OPERATION_INVALID; static char* arg_verb = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_verb, freep);
static int write_hibernate_location_info(const HibernateLocation *hibernate_location) { static int write_hibernate_location_info(const HibernateLocation *hibernate_location) {
char offset_str[DECIMAL_STR_MAX(uint64_t)]; char offset_str[DECIMAL_STR_MAX(uint64_t)];
@ -167,17 +169,11 @@ static int lock_all_homes(void) {
return 0; return 0;
} }
static int execute( static int execute(char **modes, char **states, const char *action) {
const SleepConfig *sleep_config,
SleepOperation operation,
const char *action) {
char *arguments[] = { char *arguments[] = {
NULL, NULL,
(char*) "pre", (char*) "pre",
/* NB: we use 'arg_operation' instead of 'operation' here, as we want to communicate the overall arg_verb,
* operation here, not the specific one, in case of s2h. */
(char*) sleep_operation_to_string(arg_operation),
NULL NULL
}; };
static const char* const dirs[] = { static const char* const dirs[] = {
@ -185,24 +181,10 @@ static int execute(
NULL NULL
}; };
_cleanup_(hibernate_location_freep) HibernateLocation *hibernate_location = NULL;
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
char **modes, **states; _cleanup_(hibernate_location_freep) HibernateLocation *hibernate_location = NULL;
int r; int r;
assert(sleep_config);
assert(operation >= 0);
assert(operation < _SLEEP_OPERATION_MAX);
assert(operation != SLEEP_SUSPEND_THEN_HIBERNATE); /* Handled by execute_s2h() instead */
states = sleep_config->states[operation];
modes = sleep_config->modes[operation];
if (strv_isempty(states))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"No sleep states configured for sleep operation %s, can't sleep.",
sleep_operation_to_string(operation));
/* This file is opened first, so that if we hit an error, /* This file is opened first, so that if we hit an error,
* we can abort before modifying any state. */ * we can abort before modifying any state. */
f = fopen("/sys/power/state", "we"); f = fopen("/sys/power/state", "we");
@ -229,34 +211,29 @@ static int execute(
return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");; return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;
} }
/* Pass an action string to the call-outs. This is mostly our operation string, except if the
* hibernate step of s-t-h fails, in which case we communicate that with a separate action. */
if (!action)
action = sleep_operation_to_string(operation);
r = setenv("SYSTEMD_SLEEP_ACTION", action, 1); r = setenv("SYSTEMD_SLEEP_ACTION", action, 1);
if (r != 0) if (r != 0)
log_warning_errno(errno, "Error setting SYSTEMD_SLEEP_ACTION=%s, ignoring: %m", action); log_warning_errno(errno, "Error setting SYSTEMD_SLEEP_ACTION=%s: %m", action);
(void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
(void) lock_all_homes(); (void) lock_all_homes();
log_struct(LOG_INFO, log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR, "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR,
LOG_MESSAGE("Entering sleep state '%s'...", sleep_operation_to_string(operation)), LOG_MESSAGE("Suspending system..."),
"SLEEP=%s", sleep_operation_to_string(arg_operation)); "SLEEP=%s", arg_verb);
r = write_state(&f, states); r = write_state(&f, states);
if (r < 0) if (r < 0)
log_struct_errno(LOG_ERR, r, log_struct_errno(LOG_ERR, r,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR, "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
LOG_MESSAGE("Failed to put system to sleep. System resumed again: %m"), LOG_MESSAGE("Failed to suspend system. System resumed again: %m"),
"SLEEP=%s", sleep_operation_to_string(arg_operation)); "SLEEP=%s", arg_verb);
else else
log_struct(LOG_INFO, log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR, "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR,
LOG_MESSAGE("System returned from sleep state."), LOG_MESSAGE("System resumed."),
"SLEEP=%s", sleep_operation_to_string(arg_operation)); "SLEEP=%s", arg_verb);
arguments[1] = (char*) "post"; arguments[1] = (char*) "post";
(void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
@ -285,7 +262,7 @@ static int execute_s2h(const SleepConfig *sleep_config) {
if (r < 0) if (r < 0)
return log_error_errno(errno, "Error setting hibernate timer: %m"); return log_error_errno(errno, "Error setting hibernate timer: %m");
r = execute(sleep_config, SLEEP_SUSPEND, NULL); r = execute(sleep_config->suspend_modes, sleep_config->suspend_states, "suspend");
if (r < 0) if (r < 0)
return r; return r;
@ -301,13 +278,13 @@ static int execute_s2h(const SleepConfig *sleep_config) {
log_debug("Attempting to hibernate after waking from %s timer", log_debug("Attempting to hibernate after waking from %s timer",
format_timespan(buf, sizeof(buf), sleep_config->hibernate_delay_sec, USEC_PER_SEC)); format_timespan(buf, sizeof(buf), sleep_config->hibernate_delay_sec, USEC_PER_SEC));
r = execute(sleep_config, SLEEP_HIBERNATE, NULL); r = execute(sleep_config->hibernate_modes, sleep_config->hibernate_states, "hibernate");
if (r < 0) { if (r < 0) {
log_notice("Couldn't hibernate, will try to suspend again."); log_notice_errno(r, "Couldn't hibernate, will try to suspend again: %m");
r = execute(sleep_config, SLEEP_SUSPEND, "suspend-after-failed-hibernate"); r = execute(sleep_config->suspend_modes, sleep_config->suspend_states, "suspend-after-failed-hibernate");
if (r < 0) if (r < 0)
return r; return log_error_errno(r, "Could neither hibernate nor suspend, giving up: %m");
} }
return 0; return 0;
@ -374,14 +351,20 @@ static int parse_argv(int argc, char *argv[]) {
"Usage: %s COMMAND", "Usage: %s COMMAND",
program_invocation_short_name); program_invocation_short_name);
arg_operation = sleep_operation_from_string(argv[optind]); arg_verb = strdup(argv[optind]);
if (arg_operation < 0) if (!arg_verb)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown command '%s'.", argv[optind]); return log_oom();
if (!STR_IN_SET(arg_verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unknown command '%s'.", arg_verb);
return 1 /* work to do */; return 1 /* work to do */;
} }
static int run(int argc, char *argv[]) { static int run(int argc, char *argv[]) {
bool allow;
char **modes = NULL, **states = NULL;
_cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL; _cleanup_(free_sleep_configp) SleepConfig *sleep_config = NULL;
int r; int r;
@ -395,37 +378,19 @@ static int run(int argc, char *argv[]) {
if (r < 0) if (r < 0)
return r; return r;
if (!sleep_config->allow[arg_operation]) r = sleep_settings(arg_verb, sleep_config, &allow, &modes, &states);
if (r < 0)
return r;
if (!allow)
return log_error_errno(SYNTHETIC_ERRNO(EACCES), return log_error_errno(SYNTHETIC_ERRNO(EACCES),
"Sleep operation \"%s\" is disabled by configuration, refusing.", "Sleep mode \"%s\" is disabled by configuration, refusing.",
sleep_operation_to_string(arg_operation)); arg_verb);
switch (arg_operation) { if (streq(arg_verb, "suspend-then-hibernate"))
return execute_s2h(sleep_config);
case SLEEP_SUSPEND_THEN_HIBERNATE: else
r = execute_s2h(sleep_config); return execute(modes, states, arg_verb);
break;
case SLEEP_HYBRID_SLEEP:
r = execute(sleep_config, SLEEP_HYBRID_SLEEP, NULL);
if (r < 0) {
/* If we can't hybrid sleep, then let's try to suspend at least. After all, the user
* asked us to do both: suspend + hibernate, and it's almost certainly the
* hibernation that failed, hence still do the other thing, the suspend. */
log_notice("Couldn't hybrid sleep, will try to suspend instead.");
r = execute(sleep_config, SLEEP_SUSPEND, "suspend-after-failed-hybrid-sleep");
}
break;
default:
r = execute(sleep_config, arg_operation, NULL);
break;
}
return r;
} }
DEFINE_MAIN_FUNCTION(run); DEFINE_MAIN_FUNCTION(run);

View File

@ -25,16 +25,16 @@ static void test_parse_sleep_config(void) {
_cleanup_free_ char *sum, *sus, *him, *his, *hym, *hys; _cleanup_free_ char *sum, *sus, *him, *his, *hym, *hys;
sum = strv_join(sleep_config->modes[SLEEP_SUSPEND], ", "); sum = strv_join(sleep_config->suspend_modes, ", ");
sus = strv_join(sleep_config->states[SLEEP_SUSPEND], ", "); sus = strv_join(sleep_config->suspend_states, ", ");
him = strv_join(sleep_config->modes[SLEEP_HIBERNATE], ", "); him = strv_join(sleep_config->hibernate_modes, ", ");
his = strv_join(sleep_config->states[SLEEP_HIBERNATE], ", "); his = strv_join(sleep_config->hibernate_states, ", ");
hym = strv_join(sleep_config->modes[SLEEP_HYBRID_SLEEP], ", "); hym = strv_join(sleep_config->hybrid_modes, ", ");
hys = strv_join(sleep_config->states[SLEEP_HYBRID_SLEEP], ", "); hys = strv_join(sleep_config->hybrid_states, ", ");
log_debug(" allow_suspend: %u", sleep_config->allow[SLEEP_SUSPEND]); log_debug(" allow_suspend: %u", sleep_config->allow_suspend);
log_debug(" allow_hibernate: %u", sleep_config->allow[SLEEP_HIBERNATE]); log_debug(" allow_hibernate: %u", sleep_config->allow_hibernate);
log_debug(" allow_s2h: %u", sleep_config->allow[SLEEP_SUSPEND_THEN_HIBERNATE]); log_debug(" allow_s2h: %u", sleep_config->allow_s2h);
log_debug(" allow_hybrid_sleep: %u", sleep_config->allow[SLEEP_HYBRID_SLEEP]); log_debug(" allow_hybrid_sleep: %u", sleep_config->allow_hybrid_sleep);
log_debug(" suspend modes: %s", sum); log_debug(" suspend modes: %s", sum);
log_debug(" states: %s", sus); log_debug(" states: %s", sus);
log_debug(" hibernate modes: %s", him); log_debug(" hibernate modes: %s", him);
@ -98,13 +98,13 @@ static void test_sleep(void) {
log_info("Freeze configured: %s", yes_no(can_sleep_state(freeze) > 0)); log_info("Freeze configured: %s", yes_no(can_sleep_state(freeze) > 0));
log_info("/= high-level sleep verbs =/"); log_info("/= high-level sleep verbs =/");
r = can_sleep(SLEEP_SUSPEND); r = can_sleep("suspend");
log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r)); log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r));
r = can_sleep(SLEEP_HIBERNATE); r = can_sleep("hibernate");
log_info("Hibernation configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r)); log_info("Hibernation configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r));
r = can_sleep(SLEEP_HYBRID_SLEEP); r = can_sleep("hybrid-sleep");
log_info("Hybrid-sleep configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r)); log_info("Hybrid-sleep configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r));
r = can_sleep(SLEEP_SUSPEND_THEN_HIBERNATE); r = can_sleep("suspend-then-hibernate");
log_info("Suspend-then-Hibernate configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r)); log_info("Suspend-then-Hibernate configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r));
} }