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,235 +87,106 @@
<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>
<title>General information identifying the operating system</title>
<variablelist class='environment-variables'> <variablelist class='environment-variables'>
<varlistentry> <varlistentry>
<term><varname>NAME=</varname></term> <term><varname>NAME=</varname></term>
<listitem><para>A string identifying the operating system, without a version component, and <listitem><para>A string identifying the operating system,
suitable for presentation to the user. If not set, a default of <literal>NAME=Linux</literal> may without a version component, and suitable for presentation to
be used.</para> the user. If not set, defaults to
<literal>NAME=Linux</literal>. Example:
<literal>NAME=Fedora</literal> or <literal>NAME="Debian
GNU/Linux"</literal>.</para></listitem>
</varlistentry>
<para>Examples: <literal>NAME=Fedora</literal>, <literal>NAME="Debian GNU/Linux"</literal>. <varlistentry>
</para></listitem> <term><varname>VERSION=</varname></term>
<listitem><para>A string identifying the operating system
version, excluding any OS name information, possibly including
a release code name, and suitable for presentation to the
user. This field is optional. Example:
<literal>VERSION=17</literal> or <literal>VERSION="17 (Beefy
Miracle)"</literal>.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>ID=</varname></term> <term><varname>ID=</varname></term>
<listitem><para>A lower-case string (no spaces or other characters outside of 09, az, ".", "_" <listitem><para>A lower-case string (no spaces or other
and "-") identifying the operating system, excluding any version information and suitable for characters outside of 09, az, ".", "_" and "-") identifying
processing by scripts or usage in generated filenames. If not set, a default of the operating system, excluding any version information and
<literal>ID=linux</literal> may be used.</para> suitable for processing by scripts or usage in generated
filenames. If not set, defaults to
<para>Examples: <literal>ID=fedora</literal>, <literal>ID=debian</literal>.</para></listitem> <literal>ID=linux</literal>. Example:
<literal>ID=fedora</literal> or
<literal>ID=debian</literal>.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>ID_LIKE=</varname></term> <term><varname>ID_LIKE=</varname></term>
<listitem><para>A space-separated list of operating system identifiers in the same syntax as the <listitem><para>A space-separated list of operating system
<varname>ID=</varname> setting. It should list identifiers of operating systems that are closely identifiers in the same syntax as the <varname>ID=</varname>
related to the local operating system in regards to packaging and programming interfaces, for setting. It should list identifiers of operating systems that
example listing one or more OS identifiers the local OS is a derivative from. An OS should are closely related to the local operating system in regards
generally only list other OS identifiers it itself is a derivative of, and not any OSes that are to packaging and programming interfaces, for example listing
derived from it, though symmetric relationships are possible. Build scripts and similar should one or more OS identifiers the local OS is a derivative from.
check this variable if they need to identify the local operating system and the value of An OS should generally only list other OS identifiers it
<varname>ID=</varname> is not recognized. Operating systems should be listed in order of how itself is a derivative of, and not any OSes that are derived
closely the local operating system relates to the listed ones, starting with the closest. This from it, though symmetric relationships are possible. Build
field is optional.</para> scripts and similar should check this variable if they need to
identify the local operating system and the value of
<para>Examples: for an operating system with <literal>ID=centos</literal>, an assignment of <varname>ID=</varname> is not recognized. Operating systems
<literal>ID_LIKE="rhel fedora"</literal> would be appropriate. For an operating system with should be listed in order of how closely the local operating
<literal>ID=ubuntu</literal>, an assignment of <literal>ID_LIKE=debian</literal> is appropriate. system relates to the listed ones, starting with the closest.
</para></listitem> This field is optional. Example: for an operating system with
<literal>ID=centos</literal>, an assignment of
<literal>ID_LIKE="rhel fedora"</literal> would be appropriate.
For an operating system with <literal>ID=ubuntu</literal>, an
assignment of <literal>ID_LIKE=debian</literal> is
appropriate.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>PRETTY_NAME=</varname></term> <term><varname>VERSION_CODENAME=</varname></term>
<listitem><para>A pretty operating system name in a format suitable for presentation to the <listitem><para>
user. May or may not contain a release code name or OS version of some kind, as suitable. If not A lower-case string (no spaces or other characters outside of
set, a default of <literal>PRETTY_NAME="Linux"</literal> may be used</para> 09, az, ".", "_" and "-") identifying the operating system
release code name, excluding any OS name information or
<para>Example: <literal>PRETTY_NAME="Fedora 17 (Beefy Miracle)"</literal>.</para></listitem> release version, and suitable for processing by scripts or
</varlistentry> usage in generated filenames. This field is optional and may
not be implemented on all systems.
<varlistentry> Examples:
<term><varname>CPE_NAME=</varname></term> <literal>VERSION_CODENAME=buster</literal>,
<literal>VERSION_CODENAME=xenial</literal>
<listitem><para>A CPE name for the operating system, in URI binding syntax, following the <ulink
url="http://scap.nist.gov/specifications/cpe/">Common Platform Enumeration Specification</ulink> as
proposed by the NIST. This field is optional.</para>
<para>Example: <literal>CPE_NAME="cpe:/o:fedoraproject:fedora:17"</literal></para></listitem>
</varlistentry>
<varlistentry>
<term><varname>VARIANT=</varname></term>
<listitem><para>A string identifying a specific variant or edition of the operating system suitable
for presentation to the user. This field may be used to inform the user that the configuration of
this system is subject to a specific divergent set of rules or default configuration settings. This
field is optional and may not be implemented on all systems.</para>
<para>Examples: <literal>VARIANT="Server Edition"</literal>, <literal>VARIANT="Smart Refrigerator
Edition"</literal>.</para>
<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>VARIANT_ID=</varname></term>
<listitem><para>A lower-case string (no spaces or other characters outside of 09, az, ".", "_" and
"-"), identifying a specific variant or edition of the operating system. This may be interpreted by
other packages in order to determine a divergent default configuration. This field is optional and
may not be implemented on all systems.</para>
<para>Examples: <literal>VARIANT_ID=server</literal>, <literal>VARIANT_ID=embedded</literal>.
</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
<refsect2>
<title>Information about the version of the operating system</title>
<variablelist class='environment-variables'>
<varlistentry>
<term><varname>VERSION=</varname></term>
<listitem><para>A string identifying the operating system version, excluding any OS name
information, possibly including a release code name, and suitable for presentation to the
user. This field is optional.</para>
<para>Examples: <literal>VERSION=17</literal>, <literal>VERSION="17 (Beefy Miracle)"</literal>.
</para></listitem> </para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>VERSION_ID=</varname></term> <term><varname>VERSION_ID=</varname></term>
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09, <listitem><para>A lower-case string (mostly numeric, no spaces
az, ".", "_" and "-") identifying the operating system version, excluding any OS name information or other characters outside of 09, az, ".", "_" and "-")
or release code name, and suitable for processing by scripts or usage in generated filenames. This identifying the operating system version, excluding any OS
field is optional.</para> name information or release code name, and suitable for
processing by scripts or usage in generated filenames. This
<para>Examples: <literal>VERSION_ID=17</literal>, <literal>VERSION_ID=11.04</literal>. field is optional. Example: <literal>VERSION_ID=17</literal>
</para></listitem> or <literal>VERSION_ID=11.04</literal>.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>VERSION_CODENAME=</varname></term> <term><varname>PRETTY_NAME=</varname></term>
<listitem><para>A lower-case string (no spaces or other characters outside of 09, az, ".", "_" <listitem><para>A pretty operating system name in a format
and "-") identifying the operating system release code name, excluding any OS name information or suitable for presentation to the user. May or may not contain
release version, and suitable for processing by scripts or usage in generated filenames. This field a release code name or OS version of some kind, as suitable.
is optional and may not be implemented on all systems.</para> If not set, defaults to
<literal>PRETTY_NAME="Linux"</literal>. Example:
<para>Examples: <literal>VERSION_CODENAME=buster</literal>, <literal>PRETTY_NAME="Fedora 17 (Beefy
<literal>VERSION_CODENAME=xenial</literal>.</para></listitem> Miracle)"</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>BUILD_ID=</varname></term>
<listitem><para>A string uniquely identifying the system image originally used as the installation
base. In most cases, <varname>VERSION_ID</varname> or
<varname>IMAGE_ID</varname>+<varname>IMAGE_VERSION</varname> are updated when the entire system
image is replaced during an update. <varname>BUILD_ID</varname> may be used in distributions where
the original installation image version is important: <varname>VERSION_ID</varname> would change
during incremental system updates, but <varname>BUILD_ID</varname> would not. This field is
optional.</para>
<para>Examples: <literal>BUILD_ID="2013-03-20.3"</literal>, <literal>BUILD_ID=201303203</literal>.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>IMAGE_ID=</varname></term>
<listitem><para> A lower-case string (no spaces or other characters outside of 09, az, ".", "_"
and "-"), identifying a specific image of the operating system. This is supposed to be used for
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 that are not managed via images but put together and updated from individual packages and on
the local system.</para>
<para>Examples: <literal>IMAGE_ID=vendorx-cashier-system</literal>,
<literal>IMAGE_ID=netbook-image</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>IMAGE_VERSION=</varname></term>
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09,
az, ".", "_" and "-") identifying the OS image version. This is supposed to be used together with
<varname>IMAGE_ID</varname> described above, to discern different versions of the same image.
</para>
<para>Examples: <literal>IMAGE_VERSION=33</literal>, <literal>IMAGE_VERSION=47.1rc1</literal>.
</para></listitem>
</varlistentry>
</variablelist>
<para>To summarize: if the image updates are built and shipped as comprehensive units,
<varname>IMAGE_ID</varname>+<varname>IMAGE_VERSION</varname> is the best fit. Otherwise, if updates
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.
<varname>BUILD_ID</varname> may be used instead or in addition to <varname>VERSION_ID</varname> when
the original system image version is important.</para>
</refsect2>
<refsect2>
<title>Presentation information and links</title>
<variablelist class='environment-variables'>
<varlistentry>
<term><varname>HOME_URL=</varname></term>
<term><varname>DOCUMENTATION_URL=</varname></term>
<term><varname>SUPPORT_URL=</varname></term>
<term><varname>BUG_REPORT_URL=</varname></term>
<term><varname>PRIVACY_POLICY_URL=</varname></term>
<listitem><para>Links to resources on the Internet related to the operating system.
<varname>HOME_URL=</varname> should refer to the homepage of the operating system, or alternatively
some homepage of the specific version of the operating system.
<varname>DOCUMENTATION_URL=</varname> should refer to the main documentation page for this
operating system. <varname>SUPPORT_URL=</varname> should refer to the main support page for the
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
for the operating system, if there is any. This is primarily intended for operating systems that
rely on community QA. <varname>PRIVACY_POLICY_URL=</varname> should refer to the main privacy
policy page for the operating system, if there is any. These settings are optional, and providing
only some of these settings is common. These URLs are intended to be exposed in "About this system"
UIs behind links with captions such as "About this Operating System", "Obtain Support", "Report a
Bug", or "Privacy Policy". The values should be in <ulink
url="https://tools.ietf.org/html/rfc3986">RFC3986 format</ulink>, and should be
<literal>http:</literal> or <literal>https:</literal> URLs, and possibly <literal>mailto:</literal>
or <literal>tel:</literal>. Only one URL shall be listed in each setting. If multiple resources
need to be referenced, it is recommended to provide an online landing page linking all available
resources.</para>
<para>Examples: <literal>HOME_URL="https://fedoraproject.org/"</literal>,
<literal>BUG_REPORT_URL="https://bugzilla.redhat.com/"</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>LOGO=</varname></term>
<listitem><para>A string, specifying the name of an icon as defined by <ulink
url="http://standards.freedesktop.org/icon-theme-spec/latest">freedesktop.org Icon Theme
Specification</ulink>. This can be used by graphical applications to display an operating system's
or distributor's logo. This field is optional and may not necessarily be implemented on all
systems.</para>
<para>Examples: <literal>LOGO=fedora-logo</literal>, <literal>LOGO=distributor-logo-opensuse</literal>
</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -338,31 +194,142 @@
<listitem><para>A suggested presentation color when showing the OS name on the console. This should <listitem><para>A suggested presentation color when showing the OS name on the console. This should
be specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for setting be specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for setting
graphical rendition. This field is optional.</para> graphical rendition. This field is optional. Example: <literal>ANSI_COLOR="0;31"</literal> for red,
<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>
<para>Examples: <literal>ANSI_COLOR="0;31"</literal> for red, <literal>ANSI_COLOR="1;34"</literal> <varlistentry>
for light blue, or <literal>ANSI_COLOR="0;38;2;60;110;180"</literal> for Fedora blue. <term><varname>CPE_NAME=</varname></term>
<listitem><para>A CPE name for the operating system, in URI
binding syntax, following the
<ulink url="http://scap.nist.gov/specifications/cpe/">Common
Platform Enumeration Specification</ulink> as proposed by the
NIST. This field is optional. Example:
<literal>CPE_NAME="cpe:/o:fedoraproject:fedora:17"</literal>
</para></listitem> </para></listitem>
</varlistentry> </varlistentry>
</variablelist>
</refsect2>
<refsect2> <varlistentry>
<title>Distribution-level defaults and metadata</title> <term><varname>HOME_URL=</varname></term>
<term><varname>DOCUMENTATION_URL=</varname></term>
<term><varname>SUPPORT_URL=</varname></term>
<term><varname>BUG_REPORT_URL=</varname></term>
<term><varname>PRIVACY_POLICY_URL=</varname></term>
<listitem><para>Links to resources on the Internet related to
the operating system.
<varname>HOME_URL=</varname> should refer to the homepage of
the operating system, or alternatively some homepage of the
specific version of the operating system.
<varname>DOCUMENTATION_URL=</varname> should refer to the main
documentation page for this operating system.
<varname>SUPPORT_URL=</varname> should refer to the main
support page for the 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 for the operating system,
if there is any. This is primarily intended for operating
systems that rely on community QA.
<varname>PRIVACY_POLICY_URL=</varname> should refer to the
main privacy policy page for the operating system, if there is
any. These settings are optional, and providing only some of
these settings is common. These URLs are intended to be
exposed in "About this system" UIs behind links with captions
such as "About this Operating System", "Obtain Support",
"Report a Bug", or "Privacy Policy". The values should be in
<ulink url="https://tools.ietf.org/html/rfc3986">RFC3986
format</ulink>, and should be <literal>http:</literal> or
<literal>https:</literal> URLs, and possibly
<literal>mailto:</literal> or <literal>tel:</literal>. Only
one URL shall be listed in each setting. If multiple resources
need to be referenced, it is recommended to provide an online
landing page linking all available resources. Examples:
<literal>HOME_URL="https://fedoraproject.org/"</literal> and
<literal>BUG_REPORT_URL="https://bugzilla.redhat.com/"</literal></para></listitem>
</varlistentry>
<varlistentry>
<term><varname>BUILD_ID=</varname></term>
<listitem><para>A string uniquely identifying the system image
used as the origin for a distribution (it is not updated with
system updates). The field can be identical between different
VERSION_IDs as BUILD_ID is an only a unique identifier to a
specific version. Distributions that release each update as a
new version would only need to use VERSION_ID as each build is
already distinct based on the VERSION_ID. This field is
optional. Example: <literal>BUILD_ID="2013-03-20.3"</literal>
or <literal>BUILD_ID=201303203</literal>.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>VARIANT=</varname></term>
<listitem><para>
A string identifying a specific variant or edition of the
operating system suitable for presentation to the user. This
field may be used to inform the user that the configuration of
this system is subject to a specific divergent set of rules or
default configuration settings. This field is optional and may
not be implemented on all systems.
Examples:
<literal>VARIANT="Server Edition"</literal>,
<literal>VARIANT="Smart Refrigerator Edition"</literal>
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>VARIANT_ID=</varname></term>
<listitem><para>
A lower-case string (no spaces or other characters outside of
09, az, ".", "_" and "-"), identifying a specific variant or
edition of the operating system. This may be interpreted by
other packages in order to determine a divergent default
configuration. This field is optional and may not be
implemented on all systems.
Examples:
<literal>VARIANT_ID=server</literal>,
<literal>VARIANT_ID=embedded</literal>
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>LOGO=</varname></term>
<listitem><para>
A string, specifying the name of an icon as defined by <ulink
url="http://standards.freedesktop.org/icon-theme-spec/latest">
freedesktop.org Icon Theme Specification</ulink>. This can be
used by graphical applications to display an operating
system's or distributor's logo. This field is optional and
may not necessarily be implemented on all systems.
Examples:
<literal>LOGO=fedora-logo</literal>,
<literal>LOGO=distributor-logo-opensuse</literal>
</para></listitem>
</varlistentry>
<variablelist class='environment-variables'>
<varlistentry> <varlistentry>
<term><varname>DEFAULT_HOSTNAME=</varname></term> <term><varname>DEFAULT_HOSTNAME=</varname></term>
<listitem><para>A string specifying the hostname if <listitem><para>A string specifying the hostname if
<citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry> is not <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry> is not
present and no other configuration source specifies the hostname. Must be either a single DNS label present and no other configuration source specifies the hostname. Must be either a single DNS label
(a string composed of 7-bit ASCII lower-case characters and no spaces or dots, limited to the (a string composed of 7-bit ASCII lower-case characters and no spaces or dots, limited to the format
format allowed for DNS domain name labels), or a sequence of such labels separated by single dots allowed for DNS domain name labels), or a sequence of such labels separated by single dots that forms
that forms a valid DNS FQDN. The hostname must be at most 64 characters, which is a Linux a valid DNS FQDN. The hostname must be at most 64 characters, which is a Linux limitation (DNS allows
limitation (DNS allows longer names).</para> longer names).</para>
<para>See <citerefentry><refentrytitle>org.freedesktop.hostname1</refentrytitle><manvolnum>5</manvolnum></citerefentry> <para>See
<citerefentry><refentrytitle>org.freedesktop.hostname1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for a description of how for a description of how
<citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
determines the fallback hostname.</para></listitem> determines the fallback hostname.</para></listitem>
@ -373,47 +340,66 @@
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09, <listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09,
az, ".", "_" and "-") identifying the operating system extensions support level, to indicate which az, ".", "_" and "-") identifying the operating system extensions support level, to indicate which
extension images are supported. See: extension images are supported (See:
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>) <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
for more information.</para> Example: <literal>SYSEXT_LEVEL=2</literal> or
<literal>SYSEXT_LEVEL=15.14</literal>.</para></listitem>
<para>Examples: <literal>SYSEXT_LEVEL=2</literal>, <literal>SYSEXT_LEVEL=15.14</literal>.
</para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>IMAGE_ID=</varname></term>
<listitem><para> A lower-case string (no spaces or other characters outside of 09, az, ".", "_" and
"-"), identifying a specific image of the operating system. This is supposed to be used for
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
that are not managed via images but put together and updated from individual packages and on the
local system. Examples: <literal>IMAGE_ID=vendorx-cashier-system</literal>,
<literal>IMAGE_ID=netbook-image</literal> </para></listitem>
</varlistentry>
<varlistentry>
<term><varname>IMAGE_VERSION=</varname></term>
<listitem><para>A lower-case string (mostly numeric, no spaces or other characters outside of 09,
az, ".", "_" and "-") identifying the OS image version. This is supposed to be used together with
<varname>IMAGE_ID</varname> described above, to discern different versions of the same
image. Examples: <literal>IMAGE_VERSION=33</literal>,
<literal>IMAGE_VERSION=47.1rc1</literal> </para></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect2>
<refsect2> <para>If you are reading this file from C code or a shell script
<title>Notes</title> 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_LIKE</varname> as fallback for
<varname>ID</varname>. When looking for an OS identification
string for presentation to the user use the
<varname>PRETTY_NAME</varname> field.</para>
<para>If you are using this file to determine the OS or a specific version of it, use the <para>Note that operating system vendors may choose not to provide
<varname>ID</varname> and <varname>VERSION_ID</varname> fields, possibly with version information, for example to accommodate for rolling
<varname>ID_LIKE</varname> as fallback for <varname>ID</varname>. When looking for an OS identification releases. In this case, <varname>VERSION</varname> and
string for presentation to the user use the <varname>PRETTY_NAME</varname> field.</para> <varname>VERSION_ID</varname> may be unset. Applications should
not rely on these fields to be set.</para>
<para>Note that operating system vendors may choose not to provide version information, for example to <para>Operating system vendors may extend the file
accommodate for rolling releases. In this case, <varname>VERSION</varname> and format and introduce new fields. It is highly
<varname>VERSION_ID</varname> may be unset. Applications should not rely on these fields to be recommended to prefix new fields with an OS specific
set.</para> name in order to avoid name clashes. Applications
reading this file must ignore unknown fields. Example:
<literal>DEBIAN_BTS="debbugs://bugs.debian.org/"</literal></para>
<para>Operating system vendors may extend the file format and introduce new fields. It is highly <para>Container and sandbox runtime managers may make the host's
recommended to prefix new fields with an OS specific name in order to avoid name clashes. Applications identification data available to applications by providing the host's
reading this file must ignore unknown fields.</para> <filename>/etc/os-release</filename> (if available, otherwise
<para>Example: <literal>DEBIAN_BTS="debbugs://bugs.debian.org/"</literal>.</para>
<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>/usr/lib/os-release</filename> as a fallback) as
<filename>/run/host/os-release</filename>.</para> <filename>/run/host/os-release</filename>.</para>
</refsect2>
</refsect1> </refsect1>
<refsect1> <refsect1>
<title>Examples</title> <title>Example</title>
<example>
<title><filename>os-release</filename> file for Fedora Workstation</title>
<programlisting>NAME=Fedora <programlisting>NAME=Fedora
VERSION="32 (Workstation Edition)" VERSION="32 (Workstation Edition)"
@ -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"
@ -49,12 +48,12 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) {
{ "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);
return log_error_errno(SYNTHETIC_ERRNO(EACCES), if (r < 0)
"Sleep operation \"%s\" is disabled by configuration, refusing.",
sleep_operation_to_string(arg_operation));
switch (arg_operation) {
case SLEEP_SUSPEND_THEN_HIBERNATE:
r = execute_s2h(sleep_config);
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; return r;
if (!allow)
return log_error_errno(SYNTHETIC_ERRNO(EACCES),
"Sleep mode \"%s\" is disabled by configuration, refusing.",
arg_verb);
if (streq(arg_verb, "suspend-then-hibernate"))
return execute_s2h(sleep_config);
else
return execute(modes, states, arg_verb);
} }
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));
} }