mirror of
https://github.com/systemd/systemd
synced 2026-03-18 11:04:46 +01:00
Compare commits
No commits in common. "3ff23e8c4bad409f9df50cd225093fcf96716c5d" and "9573ad4e76d28b459123cfb4b5cffab474738ab3" have entirely different histories.
3ff23e8c4b
...
9573ad4e76
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -133,6 +133,7 @@ body:
|
||||
- 'systemd-sysctl'
|
||||
- 'systemd-sysext'
|
||||
- 'systemd-sysusers'
|
||||
- 'systemd-sysv-generator'
|
||||
- 'systemd-timedate'
|
||||
- 'systemd-timesync'
|
||||
- 'systemd-tmpfiles'
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
1
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -74,6 +74,7 @@ body:
|
||||
- 'systemd-sysctl'
|
||||
- 'systemd-sysext'
|
||||
- 'systemd-sysusers'
|
||||
- 'systemd-sysv-generator'
|
||||
- 'systemd-timedate'
|
||||
- 'systemd-timesync'
|
||||
- 'systemd-tmpfiles'
|
||||
|
||||
@ -56,6 +56,7 @@ The following exceptions apply:
|
||||
- src/basic/siphash24.h
|
||||
* the following sources are licensed under the **MIT-0** license:
|
||||
- all examples under man/
|
||||
- src/systemctl/systemd-sysv-install.SKELETON
|
||||
- config files and examples under /network
|
||||
* the following sources are under **Public Domain** (LicenseRef-murmurhash2-public-domain):
|
||||
- src/basic/MurmurHash2.c
|
||||
|
||||
6
NEWS
6
NEWS
@ -11,12 +11,6 @@ CHANGES WITH 260 in spe:
|
||||
libidn2. The corresponding meson option '-Dlibidn=' is deprecated
|
||||
too and will be dropped in a future release.
|
||||
|
||||
* The parsing of RootImageOptions= and the mount image parameters of
|
||||
ExtensionImages= and MountImages= has been changed so that the last
|
||||
duplicated definition for a given partition wins and is applied,
|
||||
rather than the first, to keep these options coherent with other
|
||||
unit settings.
|
||||
|
||||
CHANGES WITH 259:
|
||||
|
||||
Announcements of Future Feature Removals and Incompatible Changes:
|
||||
|
||||
@ -13,6 +13,8 @@ You need to make the follow changes to adapt systemd to your distribution:
|
||||
|
||||
1. Find the right configure parameters for:
|
||||
|
||||
* `-Dsysvinit-path=`
|
||||
* `-Dsysvrcnd-path=`
|
||||
* `-Drc-local=`
|
||||
* `-Dloadkeys-path=`
|
||||
* `-Dsetfont-path=`
|
||||
|
||||
@ -449,6 +449,14 @@ All tools:
|
||||
as a child process by another tool, such as package managers running it in a
|
||||
postinstall script.
|
||||
|
||||
`systemd-sysv-generator`:
|
||||
|
||||
* `$SYSTEMD_SYSVINIT_PATH` — Controls where `systemd-sysv-generator` looks for
|
||||
SysV init scripts.
|
||||
|
||||
* `$SYSTEMD_SYSVRCND_PATH` — Controls where `systemd-sysv-generator` looks for
|
||||
SysV init script runlevel link farms.
|
||||
|
||||
systemd tests:
|
||||
|
||||
* `$SYSTEMD_TEST_DATA` — override the location of test data. This is useful if
|
||||
|
||||
27
docs/sysvinit/README.in
Normal file
27
docs/sysvinit/README.in
Normal file
@ -0,0 +1,27 @@
|
||||
You are looking for the traditional init scripts in {{ SYSTEM_SYSVINIT_PATH }},
|
||||
and they are gone?
|
||||
|
||||
Here's an explanation on what's going on:
|
||||
|
||||
You are running a systemd-based OS where traditional init scripts have
|
||||
been replaced by native systemd services files. Service files provide
|
||||
very similar functionality to init scripts. To make use of service
|
||||
files simply invoke "systemctl", which will output a list of all
|
||||
currently running services (and other units). Use "systemctl
|
||||
list-unit-files" to get a listing of all known unit files, including
|
||||
stopped, disabled and masked ones. Use "systemctl start
|
||||
foobar.service" and "systemctl stop foobar.service" to start or stop a
|
||||
service, respectively. For further details, please refer to
|
||||
systemctl(1).
|
||||
|
||||
Note that traditional init scripts continue to function on a systemd
|
||||
system. An init script {{ SYSTEM_SYSVINIT_PATH }}/foobar is implicitly mapped
|
||||
into a service unit foobar.service during system initialization.
|
||||
|
||||
Thank you!
|
||||
|
||||
Further reading:
|
||||
man:systemctl(1)
|
||||
man:systemd(1)
|
||||
https://0pointer.de/blog/projects/systemd-for-admins-3.html
|
||||
https://systemd.io/INCOMPATIBILITIES
|
||||
8
docs/sysvinit/meson.build
Normal file
8
docs/sysvinit/meson.build
Normal file
@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
custom_target(
|
||||
input : 'README.in',
|
||||
output : 'README',
|
||||
command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
|
||||
install : conf.get('HAVE_SYSV_COMPAT') == 1,
|
||||
install_dir : sysvinit_path)
|
||||
@ -160,8 +160,7 @@ sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pnT100TAS:*
|
||||
sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:pnT200TA:*
|
||||
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
|
||||
|
||||
# Asus Transformer Mini T101HA & T103HAF
|
||||
sensor:modalias:platform:HID-SENSOR-200073*:dmi:*svn*ASUSTeK*:pnT101HA:*
|
||||
# Asus Transformer Mini T103HAF
|
||||
sensor:modalias:platform:HID-SENSOR-200073*:dmi:*svn*ASUSTeK*:pnT103HAF:*
|
||||
ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, -1
|
||||
|
||||
|
||||
@ -3421,8 +3421,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b MemoryKSM = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s MemoryTHP = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s UserNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s NetworkNamespacePath = '...';
|
||||
@ -4038,8 +4036,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<!--property MemoryKSM is not documented!-->
|
||||
|
||||
<!--property MemoryTHP is not documented!-->
|
||||
|
||||
<!--property UserNamespacePath is not documented!-->
|
||||
|
||||
<!--property NetworkNamespacePath is not documented!-->
|
||||
@ -4788,8 +4784,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryTHP"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UserNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
|
||||
@ -5690,8 +5684,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b MemoryKSM = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s MemoryTHP = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s UserNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s NetworkNamespacePath = '...';
|
||||
@ -6325,8 +6317,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<!--property MemoryKSM is not documented!-->
|
||||
|
||||
<!--property MemoryTHP is not documented!-->
|
||||
|
||||
<!--property UserNamespacePath is not documented!-->
|
||||
|
||||
<!--property NetworkNamespacePath is not documented!-->
|
||||
@ -7051,8 +7041,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryTHP"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UserNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
|
||||
@ -7777,8 +7765,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b MemoryKSM = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s MemoryTHP = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s UserNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s NetworkNamespacePath = '...';
|
||||
@ -8336,8 +8322,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<!--property MemoryKSM is not documented!-->
|
||||
|
||||
<!--property MemoryTHP is not documented!-->
|
||||
|
||||
<!--property UserNamespacePath is not documented!-->
|
||||
|
||||
<!--property NetworkNamespacePath is not documented!-->
|
||||
@ -8970,8 +8954,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryTHP"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UserNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
|
||||
@ -9829,8 +9811,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b MemoryKSM = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s MemoryTHP = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s UserNamespacePath = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s NetworkNamespacePath = '...';
|
||||
@ -10370,8 +10350,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<!--property MemoryKSM is not documented!-->
|
||||
|
||||
<!--property MemoryTHP is not documented!-->
|
||||
|
||||
<!--property UserNamespacePath is not documented!-->
|
||||
|
||||
<!--property NetworkNamespacePath is not documented!-->
|
||||
@ -10986,8 +10964,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryKSM"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="MemoryTHP"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="UserNamespacePath"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
|
||||
@ -12622,8 +12598,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>ManagedOOMKills</varname>,
|
||||
<varname>ExecReloadPost</varname>, and
|
||||
<varname>ExecReloadPostEx</varname> were added in version 259.</para>
|
||||
<para><varname>BindNetworkInterface</varname>, and
|
||||
<varname>MemoryTHP</varname> were added in version 260.</para>
|
||||
<para><varname>BindNetworkInterface</varname> was added in version 260.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Socket Unit Objects</title>
|
||||
@ -12692,8 +12667,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<para><varname>UserNamespacePath</varname>,
|
||||
<varname>OOMKills</varname>, and
|
||||
<varname>ManagedOOMKills</varname> were added in 259.</para>
|
||||
<para><varname>BindNetworkInterface</varname>, and
|
||||
<varname>MemoryTHP</varname> were added in version 260.</para>
|
||||
<para><varname>BindNetworkInterface</varname> was added in version 260.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Mount Unit Objects</title>
|
||||
@ -12757,8 +12731,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<para><varname>UserNamespacePath</varname>,
|
||||
<varname>OOMKills</varname>, and
|
||||
<varname>ManagedOOMKills</varname> were added in 259.</para>
|
||||
<para><varname>BindNetworkInterface</varname>, and
|
||||
<varname>MemoryTHP</varname> were added in version 260.</para>
|
||||
<para><varname>BindNetworkInterface</varname> was added in version 260.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Swap Unit Objects</title>
|
||||
@ -12820,8 +12793,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<para><varname>UserNamespacePath</varname>,
|
||||
<varname>OOMKills</varname>, and
|
||||
<varname>ManagedOOMKills</varname> were added in 259.</para>
|
||||
<para><varname>BindNetworkInterface</varname>, and
|
||||
<varname>MemoryTHP</varname> were added in version 260.</para>
|
||||
<para><varname>BindNetworkInterface</varname> was added in version 260.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Slice Unit Objects</title>
|
||||
|
||||
@ -1133,7 +1133,7 @@ manpages = [
|
||||
['systemd-rc-local-generator',
|
||||
'8',
|
||||
['rc-local.service'],
|
||||
'HAVE_SYSV_RC_LOCAL'],
|
||||
'HAVE_SYSV_COMPAT HAVE_SYSV_RC_LOCAL'],
|
||||
['systemd-remount-fs.service', '8', ['systemd-remount-fs'], ''],
|
||||
['systemd-repart', '8', ['systemd-repart.service'], 'ENABLE_REPART'],
|
||||
['systemd-resolved.service', '8', ['systemd-resolved'], 'ENABLE_RESOLVE'],
|
||||
@ -1190,6 +1190,7 @@ manpages = [
|
||||
['systemd-sysupdated'],
|
||||
'ENABLE_SYSUPDATED'],
|
||||
['systemd-sysusers', '8', ['systemd-sysusers.service'], ''],
|
||||
['systemd-sysv-generator', '8', [], 'HAVE_SYSV_COMPAT'],
|
||||
['systemd-time-wait-sync.service',
|
||||
'8',
|
||||
['systemd-time-wait-sync'],
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
]>
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
|
||||
<refentry id="systemd-rc-local-generator" conditional='HAVE_SYSV_RC_LOCAL'>
|
||||
<refentry id="systemd-rc-local-generator" conditional='HAVE_SYSV_COMPAT HAVE_SYSV_RC_LOCAL'>
|
||||
<refentryinfo>
|
||||
<title>systemd-rc-local-generator</title>
|
||||
<productname>systemd</productname>
|
||||
|
||||
68
man/systemd-sysv-generator.xml
Normal file
68
man/systemd-sysv-generator.xml
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
<refentry id="systemd-sysv-generator" conditional="HAVE_SYSV_COMPAT">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-sysv-generator</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-sysv-generator</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-sysv-generator</refname>
|
||||
<refpurpose>Unit generator for SysV init scripts</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>/usr/lib/systemd/system-generators/systemd-sysv-generator</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><emphasis>Note: this component is deprecated and scheduled for removal. Please replace remaining
|
||||
SysV init scripts with native unit files.</emphasis></para>
|
||||
|
||||
<para><filename>systemd-sysv-generator</filename> is a generator that creates wrapper .service units for
|
||||
<ulink url="https://savannah.nongnu.org/projects/sysvinit">System V init</ulink> scripts in
|
||||
<filename>/etc/init.d/*</filename> at boot and when configuration of the system manager is reloaded. This
|
||||
allows <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
|
||||
support them similarly to native units.</para>
|
||||
|
||||
<para><ulink
|
||||
url="http://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html">LSB
|
||||
headers</ulink> in SysV init scripts are interpreted, and the ordering specified in the header is turned
|
||||
into dependencies between the generated unit and other units. The LSB facilities
|
||||
<literal>$remote_fs</literal>, <literal>$network</literal>, <literal>$named</literal>,
|
||||
<literal>$portmap</literal>, <literal>$time</literal> are supported and will be turned into dependencies
|
||||
on specific native systemd targets. See
|
||||
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
|
||||
more details.</para>
|
||||
|
||||
<para>Note that compatibility is quite comprehensive but not 100%, for more details see <ulink
|
||||
url="https://systemd.io/INCOMPATIBILITIES">Compatibility with SysV</ulink>.</para>
|
||||
|
||||
<para><command>systemd</command> does not support SysV scripts as part of early boot, so all wrapper
|
||||
units are ordered after <filename>basic.target</filename>.</para>
|
||||
|
||||
<para><filename>systemd-sysv-generator</filename> implements
|
||||
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.target</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@ -6,7 +6,7 @@
|
||||
%entities;
|
||||
]>
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
<refentry id="systemd-tpm2-generator" conditional='ENABLE_BOOTLOADER'>
|
||||
<refentry id="systemd-tpm2-generator">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-tpm2-generator</title>
|
||||
|
||||
@ -213,7 +213,8 @@
|
||||
<varname>RootImage=</varname>. Optionally a partition name can be prefixed, followed by colon, in
|
||||
case the image has multiple partitions, otherwise partition name <literal>root</literal> is implied.
|
||||
Options for multiple partitions can be specified in a single line with space separators. Assigning an empty
|
||||
string removes previous assignments. For a list of valid mount options, please refer to
|
||||
string removes previous assignments. Duplicated options are ignored. For a list of valid mount options, please
|
||||
refer to
|
||||
<citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
@ -2133,41 +2134,6 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>MemoryTHP=</varname></term>
|
||||
|
||||
<listitem><para>Transparent Hugepages (THPs) is a Linux kernel feature that manages memory
|
||||
using larger pages (2MB on x86, compared to the default 4KB). The main goal is to improve memory management
|
||||
efficiency and system performance, especially for memory-intensive applications.
|
||||
However, it can cause drawbacks in some scenarios, such as memory regression and latency spikes.
|
||||
THP policy is governed for the entire system via <filename>/sys/kernel/mm/transparent_hugepage/enabled</filename>.
|
||||
However, it can be overridden for individual workloads via
|
||||
<citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
|
||||
<varname>MemoryTHP=</varname> may be used to disable THPs at process invocation time to stop providing
|
||||
THPs for workloads where the drawbacks outweigh the advantages.
|
||||
When <varname>MemoryTHP=</varname> is set to <literal>inherit</literal> or not set at all, systemd
|
||||
inherits THP settings from the process that starts it and no
|
||||
<citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
<constant>PR_SET_THP_DISABLE</constant> call is made.
|
||||
When set to <literal>disable</literal>, <varname>MemoryTHP=</varname> disables THPs completely for the process,
|
||||
irrespecitive of global THP controls.
|
||||
When set to <literal>madvise</literal>, <varname>MemoryTHP=</varname> disables THPs for the process except when
|
||||
specifically requested via <citerefentry><refentrytitle>madvise</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
by the process with <constant>MADV_HUGEPAGE</constant> or <constant>MADV_COLLAPSE</constant>.
|
||||
When set to <literal>system</literal>, <varname>MemoryTHP=</varname> resets the THP policy to system wide policy.
|
||||
This can be used when the process that starts systemd has already disabled THPs via
|
||||
<constant>PR_SET_THP_DISABLE</constant>, and we want to restore the system default THP setting at
|
||||
process invokation time. For details, see
|
||||
<ulink url="https://docs.kernel.org/admin-guide/mm/transhuge.html">Transparent Hugepage Support</ulink>
|
||||
in the kernel documentation.</para>
|
||||
<para>Note that this functionality might not be available, for example if THP is disabled in the
|
||||
kernel, or the kernel does not support controlling THP at the process level through
|
||||
<citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v260"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>PrivatePIDs=</varname></term>
|
||||
|
||||
|
||||
@ -407,6 +407,7 @@ find $dir</programlisting>
|
||||
<member><citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-rc-local-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-system-update-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-sysv-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-xdg-autostart-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
|
||||
@ -50,6 +50,12 @@
|
||||
which configure resource control settings for the processes of the
|
||||
service.</para>
|
||||
|
||||
<para>If SysV init compat is enabled, systemd automatically creates service units that wrap SysV init
|
||||
scripts (the service name is the same as the name of the script, with a <literal>.service</literal>
|
||||
suffix added); see
|
||||
<citerefentry><refentrytitle>systemd-sysv-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<para>The <citerefentry><refentrytitle>systemd-run</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
command allows creating <filename>.service</filename> and <filename>.scope</filename> units dynamically
|
||||
and transiently from the command line.</para>
|
||||
|
||||
10
meson.build
10
meson.build
@ -90,6 +90,10 @@ conf.set10('HAVE_SPLIT_BIN', split_bin,
|
||||
|
||||
have_standalone_binaries = get_option('standalone-binaries')
|
||||
|
||||
sysvinit_path = get_option('sysvinit-path')
|
||||
sysvrcnd_path = get_option('sysvrcnd-path')
|
||||
conf.set10('HAVE_SYSV_COMPAT', sysvinit_path != '' and sysvrcnd_path != '',
|
||||
description : 'SysV init scripts and rcN.d links are supported')
|
||||
sysvrclocal_path = get_option('rc-local')
|
||||
conf.set10('HAVE_SYSV_RC_LOCAL', sysvrclocal_path != '')
|
||||
conf.set10('CREATE_LOG_DIRS', get_option('create-log-dirs'))
|
||||
@ -299,6 +303,8 @@ conf.set_quoted('SYSTEM_GENERATOR_DIR', systemgeneratordir
|
||||
conf.set_quoted('SYSTEM_PRESET_DIR', systempresetdir)
|
||||
conf.set_quoted('SYSTEM_SHUTDOWN_PATH', systemshutdowndir)
|
||||
conf.set_quoted('SYSTEM_SLEEP_PATH', systemsleepdir)
|
||||
conf.set_quoted('SYSTEM_SYSVINIT_PATH', sysvinit_path)
|
||||
conf.set_quoted('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
|
||||
conf.set_quoted('SYSTEM_SYSVRCLOCAL_PATH', sysvrclocal_path)
|
||||
conf.set_quoted('SYSUSERS_DIR', sysusersdir)
|
||||
conf.set_quoted('TMPFILES_DIR', tmpfilesdir)
|
||||
@ -2425,6 +2431,7 @@ subdir('src/system-update-generator')
|
||||
subdir('src/systemctl')
|
||||
subdir('src/sysupdate')
|
||||
subdir('src/sysusers')
|
||||
subdir('src/sysv-generator')
|
||||
subdir('src/timedate')
|
||||
subdir('src/timesync')
|
||||
subdir('src/tmpfiles')
|
||||
@ -2785,6 +2792,7 @@ subdir('test')
|
||||
|
||||
#####################################################################
|
||||
|
||||
subdir('docs/sysvinit')
|
||||
subdir('docs/var-log')
|
||||
subdir('hwdb.d')
|
||||
subdir('man')
|
||||
@ -3015,6 +3023,8 @@ summary({
|
||||
'sysconf directory' : sysconfdir,
|
||||
'include directory' : includedir,
|
||||
'lib directory' : libdir,
|
||||
'SysV init scripts' : sysvinit_path,
|
||||
'SysV rc?.d directories' : sysvrcnd_path,
|
||||
'SysV rc.local script' : sysvrclocal_path,
|
||||
'PAM modules directory' : pamlibdir,
|
||||
'PAM configuration directory' : pamconfdir,
|
||||
|
||||
@ -41,10 +41,10 @@ option('static-libudev', type : 'combo',
|
||||
option('standalone-binaries', type : 'boolean', value : false,
|
||||
description : 'also build standalone versions of supported binaries')
|
||||
|
||||
option('sysvinit-path', type : 'string', value : '/etc/init.d', deprecated : true,
|
||||
description : 'This option is deprecated and will be removed in a future release')
|
||||
option('sysvrcnd-path', type : 'string', value : '/etc/rc.d', deprecated : true,
|
||||
description : 'This option is deprecated and will be removed in a future release')
|
||||
option('sysvinit-path', type : 'string', value : '/etc/init.d',
|
||||
description : 'the directory where the SysV init scripts are located')
|
||||
option('sysvrcnd-path', type : 'string', value : '/etc/rc.d',
|
||||
description : 'the base directory for SysV rcN.d directories')
|
||||
option('rc-local', type : 'string', value : '/etc/rc.local',
|
||||
description : 'path to SysV rc.local script')
|
||||
option('initrd', type : 'boolean',
|
||||
|
||||
@ -59,8 +59,6 @@ EOF
|
||||
TS="${SOURCE_DATE_EPOCH:-$(date +%s)}"
|
||||
|
||||
sed "pkg/$PKG_SUBDIR/PKGBUILD" \
|
||||
--expression "/-Dsysvinit-path=/d" \
|
||||
--expression "/-Dsysvrcnd-path=/d" \
|
||||
--expression "s/^pkgver=.*/pkgver=$(cat meson.version)/" \
|
||||
--expression "s/^pkgrel=.*/pkgrel=$(date "+%Y%m%d%H%M%S" --date "@$TS")/" >/tmp/PKGBUILD
|
||||
mount --bind /tmp/PKGBUILD "pkg/$PKG_SUBDIR/PKGBUILD"
|
||||
|
||||
@ -26,7 +26,6 @@ while read -r filelist; do
|
||||
-e 's/\.gz$//; /systemd-cgroups-agent/d; s/import-pubring.gpg/import-pubring.pgp/' \
|
||||
-e '/(initctl|runlevel|telinit)/ d' \
|
||||
-e 's/systemd-quotacheck.service.8/systemd-quotacheck@.service.8/' \
|
||||
-e '/systemd-sysv-generator/d' \
|
||||
"$filelist" >"/tmp/$(basename "$filelist")"
|
||||
mount --bind "/tmp/$(basename "$filelist")" "$filelist"
|
||||
done < <(find "pkg/$PKG_SUBDIR${GIT_SUBDIR:+/$GIT_SUBDIR}" -name "files.*")
|
||||
|
||||
@ -232,6 +232,12 @@ const char* const systemd_features =
|
||||
" -UTMP"
|
||||
#endif
|
||||
|
||||
#if HAVE_SYSV_COMPAT
|
||||
" +SYSVINIT"
|
||||
#else
|
||||
" -SYSVINIT"
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBARCHIVE
|
||||
" +LIBARCHIVE"
|
||||
#else
|
||||
|
||||
@ -58,7 +58,6 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_private_bpf, private_bpf, Priva
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_home, protect_home, ProtectHome);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_system, protect_system, ProtectSystem);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_personality, personality, unsigned long);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_memory_thp, memory_thp, MemoryTHP);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_ioprio, "i", ExecContext, exec_context_get_effective_ioprio);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_mount_apivfs, "b", ExecContext, exec_context_get_effective_mount_apivfs);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_bind_log_sockets, "b", ExecContext, exec_context_get_effective_bind_log_sockets);
|
||||
@ -794,33 +793,6 @@ static int property_get_root_hash_sig(
|
||||
return sd_bus_message_append_array(reply, 'y', c->root_hash_sig.iov_base, c->root_hash_sig.iov_len);
|
||||
}
|
||||
|
||||
static int bus_append_mount_options(
|
||||
sd_bus_message *reply,
|
||||
MountOptions *options) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(reply);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(ss)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (options)
|
||||
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
if (isempty(options->options[i]))
|
||||
continue;
|
||||
|
||||
r = sd_bus_message_append(reply, "(ss)",
|
||||
partition_designator_to_string(i),
|
||||
options->options[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_root_image_options(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@ -831,12 +803,25 @@ static int property_get_root_image_options(
|
||||
sd_bus_error *reterr_error) {
|
||||
|
||||
ExecContext *c = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(property);
|
||||
assert(reply);
|
||||
|
||||
return bus_append_mount_options(reply, c->root_image_options);
|
||||
r = sd_bus_message_open_container(reply, 'a', "(ss)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
LIST_FOREACH(mount_options, m, c->root_image_options) {
|
||||
r = sd_bus_message_append(reply, "(ss)",
|
||||
partition_designator_to_string(m->partition_designator),
|
||||
m->options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_mount_images(
|
||||
@ -872,7 +857,19 @@ static int property_get_mount_images(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = bus_append_mount_options(reply, i->mount_options);
|
||||
r = sd_bus_message_open_container(reply, 'a', "(ss)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
LIST_FOREACH(mount_options, m, i->mount_options) {
|
||||
r = sd_bus_message_append(reply, "(ss)",
|
||||
partition_designator_to_string(m->partition_designator),
|
||||
m->options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -916,7 +913,19 @@ static int property_get_extension_images(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = bus_append_mount_options(reply, i->mount_options);
|
||||
r = sd_bus_message_open_container(reply, 'a', "(ss)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
LIST_FOREACH(mount_options, m, i->mount_options) {
|
||||
r = sd_bus_message_append(reply, "(ss)",
|
||||
partition_designator_to_string(m->partition_designator),
|
||||
m->options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1398,7 +1407,6 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||||
SD_BUS_PROPERTY("BPFDelegatePrograms", "s", property_get_bpf_delegate_programs, offsetof(ExecContext, bpf_delegate_programs), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BPFDelegateAttachments", "s", property_get_bpf_delegate_attachments, offsetof(ExecContext, bpf_delegate_attachments), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("MemoryKSM", "b", bus_property_get_tristate, offsetof(ExecContext, memory_ksm), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("MemoryTHP", "s", property_get_memory_thp, offsetof(ExecContext, memory_thp), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("UserNamespacePath", "s", NULL, offsetof(ExecContext, user_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("NetworkNamespacePath", "s", NULL, offsetof(ExecContext, network_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("IPCNamespacePath", "s", NULL, offsetof(ExecContext, ipc_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -1837,7 +1845,6 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(keyring_mode, ExecKeyringMode, exec_keyrin
|
||||
static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_proc, ProtectProc, protect_proc_from_string);
|
||||
static BUS_DEFINE_SET_TRANSIENT_PARSE(proc_subset, ProcSubset, proc_subset_from_string);
|
||||
static BUS_DEFINE_SET_TRANSIENT_PARSE(private_bpf, PrivateBPF, private_bpf_from_string);
|
||||
static BUS_DEFINE_SET_TRANSIENT_PARSE(memory_thp, MemoryTHP, memory_thp_from_string);
|
||||
static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(bpf_delegate_commands, uint64_t, bpf_delegate_commands_from_string);
|
||||
static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(bpf_delegate_maps, uint64_t, bpf_delegate_maps_from_string);
|
||||
static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(bpf_delegate_programs, uint64_t, bpf_delegate_programs_from_string);
|
||||
@ -1892,17 +1899,7 @@ int bus_exec_context_set_transient_property(
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
if (options) {
|
||||
if (!c->root_image_options)
|
||||
c->root_image_options = TAKE_PTR(options);
|
||||
else
|
||||
for (PartitionDesignator j = 0; j < _PARTITION_DESIGNATOR_MAX; j++) {
|
||||
if (isempty(options->options[j])) {
|
||||
if (options->options[j]) /* Free current value if "" is passed */
|
||||
c->root_image_options->options[j] = mfree(c->root_image_options->options[j]);
|
||||
continue;
|
||||
}
|
||||
free_and_replace(c->root_image_options->options[j], options->options[j]);
|
||||
}
|
||||
LIST_JOIN(mount_options, c->root_image_options, options);
|
||||
unit_write_settingf(
|
||||
u, flags|UNIT_ESCAPE_SPECIFIERS, name,
|
||||
"%s=%s",
|
||||
@ -2336,9 +2333,6 @@ int bus_exec_context_set_transient_property(
|
||||
if (streq(name, "MemoryKSM"))
|
||||
return bus_set_transient_tristate(u, name, &c->memory_ksm, message, flags, reterr_error);
|
||||
|
||||
if (streq(name, "MemoryTHP"))
|
||||
return bus_set_transient_memory_thp(u, name, &c->memory_thp, message, flags, reterr_error);
|
||||
|
||||
if (streq(name, "UtmpIdentifier"))
|
||||
return bus_set_transient_string(u, name, &c->utmp_id, message, flags, reterr_error);
|
||||
|
||||
|
||||
@ -256,13 +256,12 @@ int bus_verify_bypass_dump_ratelimit_async(Manager *m, sd_bus_message *call, sd_
|
||||
reterr_error);
|
||||
}
|
||||
|
||||
/* in_out_format_str is an accumulator, so if it has any pre-existing content it will be read, and new
|
||||
* options will be appended to it */
|
||||
/* ret_format_str is an accumulator, so if it has any pre-existing content, new options will be appended to it */
|
||||
int bus_read_mount_options(
|
||||
sd_bus_message *message,
|
||||
sd_bus_error *reterr_error,
|
||||
MountOptions **ret_options,
|
||||
char **in_out_format_str,
|
||||
char **ret_format_str,
|
||||
const char *separator) {
|
||||
|
||||
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
|
||||
@ -280,6 +279,7 @@ int bus_read_mount_options(
|
||||
|
||||
while ((r = sd_bus_message_read(message, "(ss)", &partition, &mount_options)) > 0) {
|
||||
_cleanup_free_ char *escaped = NULL;
|
||||
_cleanup_free_ MountOptions *o = NULL;
|
||||
PartitionDesignator partition_designator;
|
||||
|
||||
if (chars_intersect(mount_options, WHITESPACE))
|
||||
@ -295,21 +295,19 @@ int bus_read_mount_options(
|
||||
if (!escaped)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!isempty(escaped)) {
|
||||
if (!strextend_with_separator(&format_str, separator, partition, ":", escaped))
|
||||
return -ENOMEM;
|
||||
if (!strextend_with_separator(&format_str, separator, partition, ":", escaped))
|
||||
return -ENOMEM;
|
||||
|
||||
if (!options) {
|
||||
options = new0(MountOptions, 1);
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = free_and_strdup(&options->options[partition_designator], mount_options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (options)
|
||||
options->options[partition_designator] = mfree(options->options[partition_designator]);
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return -ENOMEM;
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = partition_designator,
|
||||
.options = strdup(mount_options),
|
||||
};
|
||||
if (!o->options)
|
||||
return -ENOMEM;
|
||||
LIST_APPEND(mount_options, options, TAKE_PTR(o));
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -319,9 +317,13 @@ int bus_read_mount_options(
|
||||
return r;
|
||||
|
||||
if (options) {
|
||||
if (in_out_format_str && !strextend_with_separator(in_out_format_str, separator, format_str))
|
||||
return -ENOMEM;
|
||||
*ret_options = TAKE_PTR(options);
|
||||
if (ret_format_str) {
|
||||
char *final = strjoin(*ret_format_str, !isempty(*ret_format_str) ? separator : "", format_str);
|
||||
if (!final)
|
||||
return -ENOMEM;
|
||||
free_and_replace(*ret_format_str, final);
|
||||
}
|
||||
LIST_JOIN(mount_options, *ret_options, options);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -261,6 +261,6 @@ int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_erro
|
||||
int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *reterr_error);
|
||||
int bus_verify_bypass_dump_ratelimit_async(Manager *m, sd_bus_message *call, sd_bus_error *reterr_error);
|
||||
|
||||
int bus_read_mount_options(sd_bus_message *message, sd_bus_error *reterr_error, MountOptions **ret_options, char **in_out_format_str, const char *separator);
|
||||
int bus_read_mount_options(sd_bus_message *message, sd_bus_error *reterr_error, MountOptions **ret_options, char **ret_format_str, const char *separator);
|
||||
|
||||
int bus_property_get_activation_details(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *reterr_error);
|
||||
|
||||
@ -4866,32 +4866,6 @@ static int exec_fd_mark_hot(
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int set_memory_thp(MemoryTHP thp) {
|
||||
switch (thp) {
|
||||
|
||||
case MEMORY_THP_INHERIT:
|
||||
return 0;
|
||||
|
||||
case MEMORY_THP_DISABLE:
|
||||
if (prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0) < 0)
|
||||
return errno == EINVAL ? -EOPNOTSUPP : -errno;
|
||||
return 0;
|
||||
|
||||
case MEMORY_THP_MADVISE:
|
||||
if (prctl(PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, 0, 0) < 0)
|
||||
return errno == EINVAL ? -EOPNOTSUPP : -errno;
|
||||
return 0;
|
||||
|
||||
case MEMORY_THP_SYSTEM:
|
||||
if (prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0) < 0)
|
||||
return errno == EINVAL ? -EOPNOTSUPP : -errno;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
static int send_handoff_timestamp(
|
||||
const ExecContext *c,
|
||||
ExecParameters *p,
|
||||
@ -5576,16 +5550,6 @@ int exec_invoke(
|
||||
}
|
||||
}
|
||||
|
||||
r = set_memory_thp(context->memory_thp);
|
||||
if (r == -EOPNOTSUPP)
|
||||
log_debug_errno(r, "Setting MemoryTHP=%s is not supported, ignoring: %m",
|
||||
memory_thp_to_string(context->memory_thp));
|
||||
else if (r < 0) {
|
||||
*exit_status = EXIT_MEMORY_THP;
|
||||
return log_error_errno(r, "Failed to set MemoryTHP=%s: %m",
|
||||
memory_thp_to_string(context->memory_thp));
|
||||
}
|
||||
|
||||
#if ENABLE_UTMP
|
||||
if (context->utmp_id) {
|
||||
_cleanup_free_ char *username_alloc = NULL;
|
||||
|
||||
@ -1547,77 +1547,6 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int serialize_mount_options(const MountOptions *mount_options, char **s) {
|
||||
assert(s);
|
||||
|
||||
if (!mount_options)
|
||||
return 0;
|
||||
|
||||
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
_cleanup_free_ char *escaped = NULL;
|
||||
|
||||
if (isempty(mount_options->options[i]))
|
||||
continue;
|
||||
|
||||
escaped = shell_escape(mount_options->options[i], ":");
|
||||
if (!escaped)
|
||||
return log_oom_debug();
|
||||
|
||||
if (!strextend(s,
|
||||
" ",
|
||||
partition_designator_to_string(i),
|
||||
":",
|
||||
escaped))
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int deserialize_mount_options(const char *s, MountOptions **ret_mount_options) {
|
||||
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret_mount_options);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL, *mount_options = NULL, *partition = NULL;
|
||||
PartitionDesignator partition_designator;
|
||||
const char *p;
|
||||
|
||||
r = extract_first_word(&s, &word, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
p = word;
|
||||
r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
if (r != 2) {
|
||||
log_warning("Failed to parse mount options entry '%s', ignoring.", word);
|
||||
continue;
|
||||
}
|
||||
|
||||
partition_designator = partition_designator_from_string(partition);
|
||||
if (partition_designator < 0) {
|
||||
log_warning_errno(partition_designator, "Unknown partition designator '%s' in exec-context-root-image-options= entry, ignoring.", partition);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = mount_options_set_and_consume(&options, partition_designator, TAKE_PTR(mount_options));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret_mount_options = TAKE_PTR(options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exec_context_serialize(const ExecContext *c, FILE *f) {
|
||||
int r;
|
||||
|
||||
@ -1665,9 +1594,22 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
|
||||
if (c->root_image_options) {
|
||||
_cleanup_free_ char *options = NULL;
|
||||
|
||||
r = serialize_mount_options(c->root_image_options, &options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
LIST_FOREACH(mount_options, o, c->root_image_options) {
|
||||
if (isempty(o->options))
|
||||
continue;
|
||||
|
||||
_cleanup_free_ char *escaped = NULL;
|
||||
escaped = shell_escape(o->options, ":");
|
||||
if (!escaped)
|
||||
return log_oom_debug();
|
||||
|
||||
if (!strextend(&options,
|
||||
" ",
|
||||
partition_designator_to_string(o->partition_designator),
|
||||
":",
|
||||
escaped))
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
r = serialize_item(f, "exec-context-root-image-options", options);
|
||||
if (r < 0)
|
||||
@ -1722,10 +1664,6 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = serialize_item(f, "exec-context-memory-thp", memory_thp_to_string(c->memory_thp));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = serialize_item(f, "exec-context-private-tmp", private_tmp_to_string(c->private_tmp));
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -2460,9 +2398,23 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
|
||||
if (!s)
|
||||
return log_oom_debug();
|
||||
|
||||
r = serialize_mount_options(mount->mount_options, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
LIST_FOREACH(mount_options, o, mount->mount_options) {
|
||||
_cleanup_free_ char *escaped = NULL;
|
||||
|
||||
if (isempty(o->options))
|
||||
continue;
|
||||
|
||||
escaped = shell_escape(o->options, ":");
|
||||
if (!escaped)
|
||||
return log_oom_debug();
|
||||
|
||||
if (!strextend(&s,
|
||||
" ",
|
||||
partition_designator_to_string(o->partition_designator),
|
||||
":",
|
||||
escaped))
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
r = serialize_item(f, "exec-context-mount-image", s);
|
||||
if (r < 0)
|
||||
@ -2481,9 +2433,23 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
|
||||
if (!s)
|
||||
return log_oom_debug();
|
||||
|
||||
r = serialize_mount_options(mount->mount_options, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
LIST_FOREACH(mount_options, o, mount->mount_options) {
|
||||
_cleanup_free_ char *escaped = NULL;
|
||||
|
||||
if (isempty(o->options))
|
||||
continue;
|
||||
|
||||
escaped = shell_escape(o->options, ":");
|
||||
if (!escaped)
|
||||
return log_oom_debug();
|
||||
|
||||
if (!strextend(&s,
|
||||
" ",
|
||||
partition_designator_to_string(o->partition_designator),
|
||||
":",
|
||||
escaped))
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
r = serialize_item(f, "exec-context-extension-image", s);
|
||||
if (r < 0)
|
||||
@ -2599,13 +2565,38 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
|
||||
return k;
|
||||
free_and_replace(c->root_image, p);
|
||||
} else if ((val = startswith(l, "exec-context-root-image-options="))) {
|
||||
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL, *mount_options = NULL, *partition = NULL;
|
||||
PartitionDesignator partition_designator;
|
||||
MountOptions *o = NULL;
|
||||
const char *p;
|
||||
|
||||
r = deserialize_mount_options(val, &options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = extract_first_word(&val, &word, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
free_and_replace_full(c->root_image_options, options, mount_options_free_all);
|
||||
p = word;
|
||||
r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
partition_designator = partition_designator_from_string(partition);
|
||||
if (partition_designator < 0)
|
||||
return -EINVAL;
|
||||
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom_debug();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = partition_designator,
|
||||
.options = TAKE_PTR(mount_options),
|
||||
};
|
||||
LIST_APPEND(mount_options, c->root_image_options, o);
|
||||
}
|
||||
} else if ((val = startswith(l, "exec-context-root-verity="))) {
|
||||
r = free_and_strdup(&c->root_verity, val);
|
||||
if (r < 0)
|
||||
@ -2658,10 +2649,6 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
|
||||
r = safe_atoi(val, &c->memory_ksm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if ((val = startswith(l, "exec-context-memory-thp="))) {
|
||||
c->memory_thp = memory_thp_from_string(val);
|
||||
if (c->memory_thp < 0)
|
||||
return c->memory_thp;
|
||||
} else if ((val = startswith(l, "exec-context-private-tmp="))) {
|
||||
c->private_tmp = private_tmp_from_string(val);
|
||||
if (c->private_tmp < 0)
|
||||
@ -3551,9 +3538,54 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
|
||||
if (isempty(destination))
|
||||
continue;
|
||||
|
||||
r = deserialize_mount_options(val, &options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *tuple = NULL, *partition = NULL, *opts = NULL;
|
||||
PartitionDesignator partition_designator;
|
||||
MountOptions *o = NULL;
|
||||
const char *p;
|
||||
|
||||
r = extract_first_word(&val, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
p = tuple;
|
||||
r = extract_many_words(&p,
|
||||
":",
|
||||
EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
|
||||
&partition,
|
||||
&opts);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
if (r == 1) {
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom_debug();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = PARTITION_ROOT,
|
||||
.options = TAKE_PTR(partition),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, o);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
partition_designator = partition_designator_from_string(partition);
|
||||
if (partition_designator < 0)
|
||||
continue;
|
||||
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom_debug();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = partition_designator,
|
||||
.options = TAKE_PTR(opts),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, o);
|
||||
}
|
||||
|
||||
r = mount_image_add(&c->mount_images, &c->n_mount_images,
|
||||
&(MountImage) {
|
||||
@ -3586,9 +3618,54 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
|
||||
s++;
|
||||
}
|
||||
|
||||
r = deserialize_mount_options(val, &options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *tuple = NULL, *partition = NULL, *opts = NULL;
|
||||
PartitionDesignator partition_designator;
|
||||
MountOptions *o = NULL;
|
||||
const char *p;
|
||||
|
||||
r = extract_first_word(&val, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
p = tuple;
|
||||
r = extract_many_words(&p,
|
||||
":",
|
||||
EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS,
|
||||
&partition,
|
||||
&opts);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
if (r == 1) {
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom_debug();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = PARTITION_ROOT,
|
||||
.options = TAKE_PTR(partition),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, o);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
partition_designator = partition_designator_from_string(partition);
|
||||
if (partition_designator < 0)
|
||||
continue;
|
||||
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom_debug();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = partition_designator,
|
||||
.options = TAKE_PTR(opts),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, o);
|
||||
}
|
||||
|
||||
r = mount_image_add(&c->extension_images, &c->n_extension_images,
|
||||
&(MountImage) {
|
||||
|
||||
@ -1117,8 +1117,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
"%sProtectHostname: %s%s%s\n"
|
||||
"%sProtectProc: %s\n"
|
||||
"%sProcSubset: %s\n"
|
||||
"%sPrivateBPF: %s\n"
|
||||
"%sMemoryTHP: %s\n",
|
||||
"%sPrivateBPF: %s\n",
|
||||
prefix, c->umask,
|
||||
prefix, empty_to_root(c->working_directory),
|
||||
prefix, empty_to_root(c->root_directory),
|
||||
@ -1146,8 +1145,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
prefix, protect_hostname_to_string(c->protect_hostname), c->private_hostname ? ":" : "", strempty(c->private_hostname),
|
||||
prefix, protect_proc_to_string(c->protect_proc),
|
||||
prefix, proc_subset_to_string(c->proc_subset),
|
||||
prefix, private_bpf_to_string(c->private_bpf),
|
||||
prefix, memory_thp_to_string(c->memory_thp));
|
||||
prefix, private_bpf_to_string(c->private_bpf));
|
||||
|
||||
if (c->private_bpf == PRIVATE_BPF_YES) {
|
||||
_cleanup_free_ char
|
||||
@ -1169,10 +1167,13 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
|
||||
|
||||
if (c->root_image_options) {
|
||||
_cleanup_free_ char *opts_str = NULL;
|
||||
|
||||
if (mount_options_to_string(c->root_image_options, &opts_str) >= 0 && !isempty(opts_str))
|
||||
fprintf(f, "%sRootImageOptions: %s\n", prefix, opts_str);
|
||||
fprintf(f, "%sRootImageOptions:", prefix);
|
||||
LIST_FOREACH(mount_options, o, c->root_image_options)
|
||||
if (!isempty(o->options))
|
||||
fprintf(f, " %s:%s",
|
||||
partition_designator_to_string(o->partition_designator),
|
||||
o->options);
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
if (iovec_is_set(&c->root_hash)) {
|
||||
@ -1579,12 +1580,10 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
mount->ignore_enoent ? "-": "",
|
||||
mount->source,
|
||||
mount->destination);
|
||||
if (mount->mount_options) {
|
||||
_cleanup_free_ char *opts = NULL;
|
||||
|
||||
if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
|
||||
fprintf(f, " %s", opts);
|
||||
}
|
||||
LIST_FOREACH(mount_options, o, mount->mount_options)
|
||||
fprintf(f, ":%s:%s",
|
||||
partition_designator_to_string(o->partition_designator),
|
||||
strempty(o->options));
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
@ -1592,12 +1591,10 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
fprintf(f, "%sExtensionImages: %s%s", prefix,
|
||||
mount->ignore_enoent ? "-": "",
|
||||
mount->source);
|
||||
if (mount->mount_options) {
|
||||
_cleanup_free_ char *opts = NULL;
|
||||
|
||||
if (mount_options_to_string(mount->mount_options, &opts) >= 0 && !isempty(opts))
|
||||
fprintf(f, " %s", opts);
|
||||
}
|
||||
LIST_FOREACH(mount_options, o, mount->mount_options)
|
||||
fprintf(f, ":%s:%s",
|
||||
partition_designator_to_string(o->partition_designator),
|
||||
strempty(o->options));
|
||||
fprintf(f, "\n");
|
||||
}
|
||||
|
||||
|
||||
@ -183,7 +183,7 @@ typedef struct ExecContext {
|
||||
struct rlimit *rlimit[_RLIMIT_MAX];
|
||||
char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
|
||||
struct iovec root_hash, root_hash_sig;
|
||||
MountOptions *root_image_options;
|
||||
LIST_HEAD(MountOptions, root_image_options);
|
||||
bool root_ephemeral;
|
||||
bool working_directory_missing_ok:1;
|
||||
bool working_directory_home:1;
|
||||
@ -311,7 +311,6 @@ typedef struct ExecContext {
|
||||
int mount_apivfs;
|
||||
int bind_log_sockets;
|
||||
int memory_ksm;
|
||||
MemoryTHP memory_thp;
|
||||
PrivateTmp private_tmp;
|
||||
PrivateTmp private_var_tmp; /* This is not an independent parameter, but calculated from other
|
||||
* parameters in unit_patch_contexts(). */
|
||||
|
||||
@ -195,7 +195,6 @@
|
||||
{% endif %}
|
||||
{{type}}.ProtectHostname, config_parse_protect_hostname, 0, offsetof({{type}}, exec_context)
|
||||
{{type}}.MemoryKSM, config_parse_tristate, 0, offsetof({{type}}, exec_context.memory_ksm)
|
||||
{{type}}.MemoryTHP, config_parse_memory_thp, 0, offsetof({{type}}, exec_context.memory_thp)
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro KILL_CONTEXT_CONFIG_ITEMS(type) -%}
|
||||
|
||||
@ -163,7 +163,6 @@ DEFINE_CONFIG_PARSE_PTR(config_parse_bpf_delegate_commands, bpf_delegate_command
|
||||
DEFINE_CONFIG_PARSE_PTR(config_parse_bpf_delegate_maps, bpf_delegate_maps_from_string, uint64_t);
|
||||
DEFINE_CONFIG_PARSE_PTR(config_parse_bpf_delegate_programs, bpf_delegate_programs_from_string, uint64_t);
|
||||
DEFINE_CONFIG_PARSE_PTR(config_parse_bpf_delegate_attachments, bpf_delegate_attachments_from_string, uint64_t);
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_memory_thp, memory_thp, MemoryTHP);
|
||||
|
||||
bool contains_instance_specifier_superset(const char *s) {
|
||||
const char *p, *q;
|
||||
@ -1663,6 +1662,7 @@ int config_parse_root_image_options(
|
||||
}
|
||||
|
||||
STRV_FOREACH_PAIR(first, second, l) {
|
||||
MountOptions *o = NULL;
|
||||
_cleanup_free_ char *mount_options_resolved = NULL;
|
||||
const char *mount_options = NULL, *partition = "root";
|
||||
PartitionDesignator partition_designator;
|
||||
@ -1686,12 +1686,23 @@ int config_parse_root_image_options(
|
||||
continue;
|
||||
}
|
||||
|
||||
r = mount_options_set_and_consume(&options, partition_designator, TAKE_PTR(mount_options_resolved));
|
||||
if (r < 0)
|
||||
return r;
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = partition_designator,
|
||||
.options = TAKE_PTR(mount_options_resolved),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, TAKE_PTR(o));
|
||||
}
|
||||
|
||||
return free_and_replace_full(c->root_image_options, options, mount_options_free_all);
|
||||
if (options)
|
||||
LIST_JOIN(mount_options, c->root_image_options, options);
|
||||
else
|
||||
/* empty spaces/separators only */
|
||||
c->root_image_options = mount_options_free_all(c->root_image_options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_exec_root_hash(
|
||||
@ -5261,6 +5272,7 @@ int config_parse_mount_images(
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
|
||||
MountOptions *o = NULL;
|
||||
PartitionDesignator partition_designator;
|
||||
|
||||
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
|
||||
@ -5280,9 +5292,14 @@ int config_parse_mount_images(
|
||||
continue;
|
||||
}
|
||||
|
||||
r = mount_options_set_and_consume(&options, PARTITION_ROOT, TAKE_PTR(mount_options_resolved));
|
||||
if (r < 0)
|
||||
return r;
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = PARTITION_ROOT,
|
||||
.options = TAKE_PTR(mount_options_resolved),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, o);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -5299,9 +5316,14 @@ int config_parse_mount_images(
|
||||
continue;
|
||||
}
|
||||
|
||||
r = mount_options_set_and_consume(&options, partition_designator, TAKE_PTR(mount_options_resolved));
|
||||
if (r < 0)
|
||||
return r;
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = partition_designator,
|
||||
.options = TAKE_PTR(mount_options_resolved),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, o);
|
||||
}
|
||||
|
||||
r = mount_image_add(&c->mount_images, &c->n_mount_images,
|
||||
@ -5392,6 +5414,7 @@ int config_parse_extension_images(
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *partition = NULL, *mount_options = NULL, *mount_options_resolved = NULL;
|
||||
MountOptions *o = NULL;
|
||||
PartitionDesignator partition_designator;
|
||||
|
||||
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options);
|
||||
@ -5411,12 +5434,14 @@ int config_parse_extension_images(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!options) {
|
||||
options = new0(MountOptions, 1);
|
||||
if (!options)
|
||||
return log_oom();
|
||||
}
|
||||
free_and_replace(options->options[PARTITION_ROOT], mount_options_resolved);
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = PARTITION_ROOT,
|
||||
.options = TAKE_PTR(mount_options_resolved),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, o);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -5432,12 +5457,14 @@ int config_parse_extension_images(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!options) {
|
||||
options = new0(MountOptions, 1);
|
||||
if (!options)
|
||||
return log_oom();
|
||||
}
|
||||
free_and_replace(options->options[partition_designator], mount_options_resolved);
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return log_oom();
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = partition_designator,
|
||||
.options = TAKE_PTR(mount_options_resolved),
|
||||
};
|
||||
LIST_APPEND(mount_options, options, o);
|
||||
}
|
||||
|
||||
r = mount_image_add(&c->extension_images, &c->n_extension_images,
|
||||
|
||||
@ -168,7 +168,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_nft_set);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_mount_node);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_concurrency_max);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_bind_network_interface);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_memory_thp);
|
||||
|
||||
/* gperf prototypes */
|
||||
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "glyph-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "label-util.h"
|
||||
#include "list.h"
|
||||
#include "lock-util.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
@ -116,7 +117,7 @@ typedef struct MountEntry {
|
||||
char *options_malloc;
|
||||
unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */
|
||||
unsigned n_followed;
|
||||
MountOptions *image_options_const;
|
||||
LIST_HEAD(MountOptions, image_options_const);
|
||||
char **overlay_layers;
|
||||
VeritySettings verity;
|
||||
ImageClass filter_class; /* Used for live updates to skip inapplicable images */
|
||||
@ -3127,8 +3128,7 @@ MountImage* mount_image_free_many(MountImage *m, size_t *n) {
|
||||
|
||||
int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
|
||||
_cleanup_free_ char *s = NULL, *d = NULL;
|
||||
_cleanup_(mount_options_free_allp) MountOptions *o = NULL;
|
||||
int r;
|
||||
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
|
||||
|
||||
assert(m);
|
||||
assert(n);
|
||||
@ -3144,10 +3144,21 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (item->mount_options) {
|
||||
r = mount_options_dup(item->mount_options, &o);
|
||||
if (r < 0)
|
||||
return r;
|
||||
LIST_FOREACH(mount_options, i, item->mount_options) {
|
||||
_cleanup_(mount_options_free_allp) MountOptions *o = NULL;
|
||||
|
||||
o = new(MountOptions, 1);
|
||||
if (!o)
|
||||
return -ENOMEM;
|
||||
|
||||
*o = (MountOptions) {
|
||||
.partition_designator = i->partition_designator,
|
||||
.options = strdup(i->options),
|
||||
};
|
||||
if (!o->options)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_APPEND(mount_options, options, TAKE_PTR(o));
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC(*m, *n + 1))
|
||||
@ -3156,7 +3167,7 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
|
||||
(*m)[(*n)++] = (MountImage) {
|
||||
.source = TAKE_PTR(s),
|
||||
.destination = TAKE_PTR(d),
|
||||
.mount_options = TAKE_PTR(o),
|
||||
.mount_options = TAKE_PTR(options),
|
||||
.ignore_enoent = item->ignore_enoent,
|
||||
.type = item->type,
|
||||
};
|
||||
@ -4036,15 +4047,6 @@ DEFINE_STRING_TABLE_LOOKUP(bpf_delegate_map_type, uint64_t);
|
||||
DEFINE_STRING_TABLE_LOOKUP(bpf_delegate_prog_type, uint64_t);
|
||||
DEFINE_STRING_TABLE_LOOKUP(bpf_delegate_attach_type, uint64_t);
|
||||
|
||||
static const char* const memory_thp_table[_MEMORY_THP_MAX] = {
|
||||
[MEMORY_THP_INHERIT] = "inherit",
|
||||
[MEMORY_THP_DISABLE] = "disable",
|
||||
[MEMORY_THP_MADVISE] = "madvise",
|
||||
[MEMORY_THP_SYSTEM] = "system",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(memory_thp, MemoryTHP);
|
||||
|
||||
char* bpf_delegate_to_string(uint64_t u, const char * (*parser)(uint64_t) _const_ ) {
|
||||
assert(parser);
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
***/
|
||||
|
||||
#include "core-forward.h"
|
||||
#include "list.h"
|
||||
#include "runtime-scope.h"
|
||||
|
||||
typedef enum ProtectHome {
|
||||
@ -90,24 +91,6 @@ typedef enum PrivatePIDs {
|
||||
_PRIVATE_PIDS_INVALID = -EINVAL,
|
||||
} PrivatePIDs;
|
||||
|
||||
typedef enum MemoryTHP {
|
||||
/*
|
||||
* Inherit default from process that starts systemd, i.e. do not make
|
||||
* any PR_SET_THP_DISABLE call.
|
||||
*/
|
||||
MEMORY_THP_INHERIT,
|
||||
MEMORY_THP_DISABLE, /* Disable THPs completely for the prcess */
|
||||
MEMORY_THP_MADVISE, /* Disable THPs for the process except when madvised */
|
||||
/*
|
||||
* Use system default THP setting. this can be used when the process that
|
||||
* starts systemd has already disabled THPs via PR_SET_THP_DISABLE, and we
|
||||
* want to restore the system default THP setting at process invokation time.
|
||||
*/
|
||||
MEMORY_THP_SYSTEM,
|
||||
_MEMORY_THP_MAX,
|
||||
_MEMORY_THP_INVALID = -EINVAL,
|
||||
} MemoryTHP;
|
||||
|
||||
typedef struct BindMount {
|
||||
char *source;
|
||||
char *destination;
|
||||
@ -137,7 +120,7 @@ typedef enum MountImageType {
|
||||
typedef struct MountImage {
|
||||
char *source;
|
||||
char *destination; /* Unused if MountImageType == MOUNT_IMAGE_EXTENSION */
|
||||
MountOptions *mount_options;
|
||||
LIST_HEAD(MountOptions, mount_options);
|
||||
bool ignore_enoent;
|
||||
MountImageType type;
|
||||
} MountImage;
|
||||
@ -250,8 +233,6 @@ DECLARE_STRING_TABLE_LOOKUP(proc_subset, ProcSubset);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(private_bpf, PrivateBPF);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(memory_thp, MemoryTHP);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(bpf_delegate_cmd, uint64_t);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(bpf_delegate_map_type, uint64_t);
|
||||
|
||||
@ -45,41 +45,25 @@ static int working_directory_build_json(sd_json_variant **ret, const char *name,
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("missingOK", c->working_directory_missing_ok));
|
||||
}
|
||||
|
||||
static int json_append_mount_options(sd_json_variant **v, MountOptions *options) {
|
||||
int r;
|
||||
|
||||
assert(v);
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
|
||||
for (PartitionDesignator j = 0; j < _PARTITION_DESIGNATOR_MAX; j++) {
|
||||
if (!options->options[j])
|
||||
continue;
|
||||
|
||||
r = sd_json_variant_append_arraybo(
|
||||
v,
|
||||
SD_JSON_BUILD_PAIR_STRING("partitionDesignator", partition_designator_to_string(j)),
|
||||
SD_JSON_BUILD_PAIR_STRING("options", options->options[j]));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int root_image_options_build_json(sd_json_variant **ret, const char *name, void *userdata) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
MountOptions *root_image_options = userdata;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(name);
|
||||
|
||||
if (!root_image_options) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
LIST_FOREACH(mount_options, m, root_image_options) {
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&v,
|
||||
SD_JSON_BUILD_PAIR_STRING("partitionDesignator", partition_designator_to_string(m->partition_designator)),
|
||||
SD_JSON_BUILD_PAIR_STRING("options", m->options));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return json_append_mount_options(ret, root_image_options);
|
||||
*ret = TAKE_PTR(v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int image_policy_build_json(sd_json_variant **ret, const char *name, void *userdata) {
|
||||
@ -135,9 +119,14 @@ static int mount_images_build_json(sd_json_variant **ret, const char *name, void
|
||||
FOREACH_ARRAY(i, c->mount_images, c->n_mount_images) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *mo = NULL;
|
||||
|
||||
r = json_append_mount_options(&mo, i->mount_options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
LIST_FOREACH(mount_options, m, i->mount_options) {
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&mo,
|
||||
SD_JSON_BUILD_PAIR_STRING("partitionDesignator", partition_designator_to_string(m->partition_designator)),
|
||||
SD_JSON_BUILD_PAIR_STRING("options", m->options));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&v,
|
||||
@ -164,9 +153,14 @@ static int extension_images_build_json(sd_json_variant **ret, const char *name,
|
||||
FOREACH_ARRAY(i, c->extension_images, c->n_extension_images) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *mo = NULL;
|
||||
|
||||
r = json_append_mount_options(&mo, i->mount_options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
LIST_FOREACH(mount_options, m, i->mount_options) {
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&mo,
|
||||
SD_JSON_BUILD_PAIR_STRING("partitionDesignator", partition_designator_to_string(m->partition_designator)),
|
||||
SD_JSON_BUILD_PAIR_STRING("options", m->options));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_json_variant_append_arraybo(
|
||||
&v,
|
||||
@ -914,7 +908,6 @@ int unit_exec_context_build_json(sd_json_variant **ret, const char *name, void *
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("RemoveIPC", c->remove_ipc),
|
||||
JSON_BUILD_PAIR_TRISTATE_NON_NULL("PrivateMounts", c->private_mounts),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("MountFlags", mount_propagation_flag_to_string(c->mount_propagation_flag)),
|
||||
SD_JSON_BUILD_PAIR_STRING("MemoryTHP", memory_thp_to_string(c->memory_thp)),
|
||||
|
||||
/* System Call Filtering */
|
||||
JSON_BUILD_PAIR_CALLBACK_NON_NULL("SystemCallFilter", syscall_filter_build_json, c),
|
||||
|
||||
@ -17,7 +17,3 @@
|
||||
#ifndef PR_SET_MEMORY_MERGE
|
||||
#define PR_SET_MEMORY_MERGE 67
|
||||
#endif
|
||||
|
||||
#ifndef PR_THP_DISABLE_EXCEPT_ADVISED
|
||||
#define PR_THP_DISABLE_EXCEPT_ADVISED (1 << 1)
|
||||
#endif
|
||||
|
||||
@ -5,6 +5,7 @@ executables += [
|
||||
'name' : 'systemd-rc-local-generator',
|
||||
'sources' : files('rc-local-generator.c'),
|
||||
'conditions' : [
|
||||
'HAVE_SYSV_COMPAT',
|
||||
'HAVE_SYSV_RC_LOCAL',
|
||||
],
|
||||
},
|
||||
|
||||
@ -2494,7 +2494,6 @@ static const BusProperty execute_properties[] = {
|
||||
{ "LogRateLimitBurst", bus_append_safe_atou },
|
||||
{ "TTYRows", bus_append_safe_atou },
|
||||
{ "TTYColumns", bus_append_safe_atou },
|
||||
{ "MemoryTHP", bus_append_string },
|
||||
{ "MountFlags", bus_append_mount_propagation_flag_from_string },
|
||||
{ "Environment", bus_append_strv_cunescape },
|
||||
{ "UnsetEnvironment", bus_append_strv_cunescape },
|
||||
|
||||
@ -4619,78 +4619,23 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
|
||||
return k >= 0 && image->partitions[k].found;
|
||||
}
|
||||
|
||||
int mount_options_set_and_consume(MountOptions **options, PartitionDesignator d, char *s) {
|
||||
assert(options);
|
||||
assert(d >= 0);
|
||||
MountOptions* mount_options_free_all(MountOptions *options) {
|
||||
MountOptions *m;
|
||||
|
||||
if (!*options) {
|
||||
*options = new0(MountOptions, 1);
|
||||
if (!*options) {
|
||||
free(s);
|
||||
return log_oom();
|
||||
}
|
||||
while ((m = LIST_POP(mount_options, options))) {
|
||||
free(m->options);
|
||||
free(m);
|
||||
}
|
||||
|
||||
free_and_replace((*options)->options[d], s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mount_options_dup(const MountOptions *source, MountOptions **ret) {
|
||||
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
|
||||
|
||||
assert(source);
|
||||
assert(ret);
|
||||
|
||||
options = new0(MountOptions, 1);
|
||||
if (!options)
|
||||
return log_oom();
|
||||
|
||||
for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++)
|
||||
if (source->options[d]) {
|
||||
options->options[d] = strdup(source->options[d]);
|
||||
if (!options->options[d])
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(options);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mount_options_to_string(const MountOptions *mount_options, char **ret) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
assert(mount_options);
|
||||
assert(ret);
|
||||
|
||||
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
|
||||
if (!isempty(mount_options->options[i]))
|
||||
if (!strextend_with_separator(&s, ":", "%s:%s",
|
||||
partition_designator_to_string(i),
|
||||
mount_options->options[i]))
|
||||
return log_oom_debug();
|
||||
|
||||
*ret = TAKE_PTR(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MountOptions* mount_options_free_all(MountOptions *options) {
|
||||
if (!options)
|
||||
return NULL;
|
||||
|
||||
free_many_charp(options->options, _PARTITION_DESIGNATOR_MAX);
|
||||
|
||||
return mfree(options);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator) {
|
||||
assert(designator >= 0 && designator < _PARTITION_DESIGNATOR_MAX);
|
||||
LIST_FOREACH(mount_options, m, options)
|
||||
if (designator == m->partition_designator && !isempty(m->options))
|
||||
return m->options;
|
||||
|
||||
if (!options)
|
||||
return NULL;
|
||||
|
||||
return options->options[designator];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int mount_image_privately_interactively(
|
||||
@ -4809,7 +4754,8 @@ static bool mount_options_relax_extension_release_checks(const MountOptions *opt
|
||||
return false;
|
||||
|
||||
return string_contains_word(mount_options_from_designator(options, PARTITION_ROOT), ",", "x-systemd.relax-extension-release-check") ||
|
||||
string_contains_word(mount_options_from_designator(options, PARTITION_USR), ",", "x-systemd.relax-extension-release-check");
|
||||
string_contains_word(mount_options_from_designator(options, PARTITION_USR), ",", "x-systemd.relax-extension-release-check") ||
|
||||
string_contains_word(options->options, ",", "x-systemd.relax-extension-release-check");
|
||||
}
|
||||
|
||||
int verity_dissect_and_mount(
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "architecture.h"
|
||||
#include "gpt.h"
|
||||
#include "iovec-util.h"
|
||||
#include "list.h"
|
||||
#include "shared-forward.h"
|
||||
|
||||
typedef struct DecryptedImage DecryptedImage;
|
||||
@ -113,7 +114,9 @@ typedef struct DissectedImage {
|
||||
} DissectedImage;
|
||||
|
||||
typedef struct MountOptions {
|
||||
char *options[_PARTITION_DESIGNATOR_MAX];
|
||||
PartitionDesignator partition_designator;
|
||||
char *options;
|
||||
LIST_FIELDS(MountOptions, mount_options);
|
||||
} MountOptions;
|
||||
|
||||
typedef struct VeritySettings {
|
||||
@ -151,9 +154,6 @@ typedef struct ExtensionReleaseData {
|
||||
MountOptions* mount_options_free_all(MountOptions *options);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
|
||||
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator);
|
||||
int mount_options_set_and_consume(MountOptions **options, PartitionDesignator d, char *s);
|
||||
int mount_options_dup(const MountOptions *source, MountOptions **ret);
|
||||
int mount_options_to_string(const MountOptions *mount_options, char **ret);
|
||||
|
||||
int probe_filesystem_full(int fd, const char *path, uint64_t offset, uint64_t size, bool restrict_fstypes, char **ret_fstype);
|
||||
static inline int probe_filesystem(const char *path, char **ret_fstype) {
|
||||
|
||||
@ -71,7 +71,6 @@ const ExitStatusMapping exit_status_mappings[256] = {
|
||||
[EXIT_CREDENTIALS] = { "CREDENTIALS", EXIT_STATUS_SYSTEMD },
|
||||
[EXIT_BPF] = { "BPF", EXIT_STATUS_SYSTEMD },
|
||||
[EXIT_KSM] = { "KSM", EXIT_STATUS_SYSTEMD },
|
||||
[EXIT_MEMORY_THP] = { "MEMORY_THP", EXIT_STATUS_SYSTEMD },
|
||||
|
||||
[EXIT_EXCEPTION] = { "EXCEPTION", EXIT_STATUS_SYSTEMD },
|
||||
|
||||
|
||||
@ -70,7 +70,6 @@ enum {
|
||||
EXIT_CREDENTIALS,
|
||||
EXIT_BPF,
|
||||
EXIT_KSM,
|
||||
EXIT_MEMORY_THP,
|
||||
|
||||
EXIT_EXCEPTION = 255, /* Whenever we want to propagate an abnormal/signal exit, in line with bash */
|
||||
};
|
||||
|
||||
@ -626,8 +626,6 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
SD_VARLINK_DEFINE_FIELD(PrivateMounts, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#MountFlags="),
|
||||
SD_VARLINK_DEFINE_FIELD(MountFlags, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("https://www.freedesktop.org/software/systemd/man"PROJECT_VERSION_STR"systemd.exec.html#MemoryTHP="),
|
||||
SD_VARLINK_DEFINE_FIELD(MemoryTHP, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
|
||||
/* System Call Filtering
|
||||
* https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#System%20Call%20Filtering */
|
||||
|
||||
@ -36,6 +36,7 @@ systemctl_extract_sources = files(
|
||||
'systemctl-daemon-reload.c',
|
||||
'systemctl-logind.c',
|
||||
'systemctl-start-unit.c',
|
||||
'systemctl-sysv-compat.c',
|
||||
'systemctl-util.c',
|
||||
'systemctl.c',
|
||||
)
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "systemctl-daemon-reload.h"
|
||||
#include "systemctl-enable.h"
|
||||
#include "systemctl-start-unit.h"
|
||||
#include "systemctl-sysv-compat.h"
|
||||
#include "systemctl-util.h"
|
||||
#include "unit-name.h"
|
||||
#include "verbs.h"
|
||||
@ -96,6 +97,10 @@ int verb_enable(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = enable_sysv_units(verb, names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* If the operation was fully executed by the SysV compat, let's finish early */
|
||||
if (strv_isempty(names)) {
|
||||
if (arg_no_reload || install_client_side() != INSTALL_CLIENT_SIDE_NO)
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "strv.h"
|
||||
#include "systemctl.h"
|
||||
#include "systemctl-is-enabled.h"
|
||||
#include "systemctl-sysv-compat.h"
|
||||
#include "systemctl-util.h"
|
||||
|
||||
static int show_installation_targets_client_side(const char *name) {
|
||||
@ -67,13 +68,20 @@ static int show_installation_targets(sd_bus *bus, const char *name) {
|
||||
|
||||
int verb_is_enabled(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
bool not_found = true, enabled = false;
|
||||
bool not_found, enabled;
|
||||
int r;
|
||||
|
||||
r = mangle_names("to check", strv_skip(argv, 1), &names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = enable_sysv_units(argv[0], names);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
not_found = r == 0; /* Doesn't have SysV support or SYSV_UNIT_NOT_FOUND */
|
||||
enabled = r == SYSV_UNIT_ENABLED;
|
||||
|
||||
if (install_client_side() != INSTALL_CLIENT_SIDE_NO)
|
||||
STRV_FOREACH(name, names) {
|
||||
UnitFileState state;
|
||||
|
||||
167
src/systemctl/systemctl-sysv-compat.c
Normal file
167
src/systemctl/systemctl-sysv-compat.c
Normal file
@ -0,0 +1,167 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "env-util.h"
|
||||
#include "install.h"
|
||||
#include "log.h"
|
||||
#include "path-lookup.h"
|
||||
#include "path-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "systemctl.h"
|
||||
#include "systemctl-sysv-compat.h"
|
||||
|
||||
int enable_sysv_units(const char *verb, char **args) {
|
||||
int r = 0;
|
||||
|
||||
#if HAVE_SYSV_COMPAT
|
||||
_cleanup_(lookup_paths_done) LookupPaths paths = {};
|
||||
unsigned f = 0;
|
||||
SysVUnitEnableState enable_state = SYSV_UNIT_NOT_FOUND;
|
||||
|
||||
/* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */
|
||||
|
||||
if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM)
|
||||
return 0;
|
||||
|
||||
if (getenv_bool("SYSTEMCTL_SKIP_SYSV") > 0)
|
||||
return 0;
|
||||
|
||||
if (!STR_IN_SET(verb,
|
||||
"enable",
|
||||
"disable",
|
||||
"is-enabled"))
|
||||
return 0;
|
||||
|
||||
r = lookup_paths_init_or_warn(&paths, arg_runtime_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = 0;
|
||||
while (args[f]) {
|
||||
|
||||
const char *argv[] = {
|
||||
LIBEXECDIR "/systemd-sysv-install",
|
||||
NULL, /* --root= */
|
||||
NULL, /* verb */
|
||||
NULL, /* service */
|
||||
NULL,
|
||||
};
|
||||
|
||||
_cleanup_free_ char *p = NULL, *q = NULL, *l = NULL, *v = NULL, *b = NULL;
|
||||
bool found_native = false, found_sysv;
|
||||
const char *name;
|
||||
unsigned c = 1;
|
||||
int j;
|
||||
|
||||
name = args[f++];
|
||||
|
||||
if (!endswith(name, ".service"))
|
||||
continue;
|
||||
|
||||
if (path_is_absolute(name))
|
||||
continue;
|
||||
|
||||
j = unit_file_exists(arg_runtime_scope, &paths, name);
|
||||
if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL))
|
||||
return log_error_errno(j, "Failed to look up unit file state: %m");
|
||||
found_native = j != 0;
|
||||
|
||||
/* If we have both a native unit and a SysV script, enable/disable them both (below); for
|
||||
* is-enabled, prefer the native unit */
|
||||
if (found_native && streq(verb, "is-enabled"))
|
||||
continue;
|
||||
|
||||
p = path_join(arg_root, SYSTEM_SYSVINIT_PATH, name);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
p[strlen(p) - STRLEN(".service")] = 0;
|
||||
found_sysv = access(p, F_OK) >= 0;
|
||||
if (!found_sysv)
|
||||
continue;
|
||||
|
||||
if (!arg_quiet) {
|
||||
if (found_native)
|
||||
log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]);
|
||||
else
|
||||
log_info("%s is not a native service, redirecting to systemd-sysv-install.", name);
|
||||
}
|
||||
|
||||
if (!isempty(arg_root)) {
|
||||
q = strjoin("--root=", arg_root);
|
||||
if (!q)
|
||||
return log_oom();
|
||||
|
||||
argv[c++] = q;
|
||||
}
|
||||
|
||||
/* Let's copy the verb, since it's still pointing directly into the original argv[] array we
|
||||
* got passed, but safe_fork() is likely going to rewrite that for the new child */
|
||||
v = strdup(verb);
|
||||
if (!v)
|
||||
return log_oom();
|
||||
|
||||
j = path_extract_filename(p, &b);
|
||||
if (j < 0)
|
||||
return log_error_errno(j, "Failed to extract file name from '%s': %m", p);
|
||||
|
||||
argv[c++] = v;
|
||||
argv[c++] = b;
|
||||
argv[c] = NULL;
|
||||
|
||||
l = strv_join((char**)argv, " ");
|
||||
if (!l)
|
||||
return log_oom();
|
||||
|
||||
if (!arg_quiet)
|
||||
log_info("Executing: %s", l);
|
||||
|
||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
j = pidref_safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pidref);
|
||||
if (j < 0)
|
||||
return j;
|
||||
if (j == 0) {
|
||||
/* Child */
|
||||
execv(argv[0], (char**) argv);
|
||||
log_error_errno(errno, "Failed to execute %s: %m", argv[0]);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
j = pidref_wait_for_terminate_and_check("sysv-install", &pidref, WAIT_LOG_ABNORMAL);
|
||||
if (j < 0)
|
||||
return j;
|
||||
if (streq(verb, "is-enabled")) {
|
||||
if (j == EXIT_SUCCESS) {
|
||||
if (!arg_quiet)
|
||||
puts("enabled");
|
||||
enable_state = SYSV_UNIT_ENABLED;
|
||||
} else {
|
||||
if (!arg_quiet)
|
||||
puts("disabled");
|
||||
if (enable_state != SYSV_UNIT_ENABLED)
|
||||
enable_state = SYSV_UNIT_DISABLED;
|
||||
}
|
||||
|
||||
} else if (j != EXIT_SUCCESS)
|
||||
return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */
|
||||
|
||||
if (found_native)
|
||||
continue;
|
||||
|
||||
/* Remove this entry, so that we don't try enabling it as native unit */
|
||||
assert(f > 0);
|
||||
f--;
|
||||
assert(args[f] == name);
|
||||
strv_remove(args + f, name);
|
||||
}
|
||||
|
||||
if (streq(verb, "is-enabled"))
|
||||
return enable_state;
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
10
src/systemctl/systemctl-sysv-compat.h
Normal file
10
src/systemctl/systemctl-sysv-compat.h
Normal file
@ -0,0 +1,10 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
typedef enum SysVUnitEnableState {
|
||||
SYSV_UNIT_NOT_FOUND = 0,
|
||||
SYSV_UNIT_DISABLED,
|
||||
SYSV_UNIT_ENABLED,
|
||||
} SysVUnitEnableState;
|
||||
|
||||
int enable_sysv_units(const char *verb, char **args);
|
||||
51
src/systemctl/systemd-sysv-install.SKELETON
Executable file
51
src/systemctl/systemd-sysv-install.SKELETON
Executable file
@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: MIT-0
|
||||
#
|
||||
# This script is called by "systemctl enable/disable" when the given unit is a
|
||||
# SysV init.d script. It needs to call the distribution's mechanism for
|
||||
# enabling/disabling those, such as chkconfig, update-rc.d, or similar. This
|
||||
# can optionally take a --root argument for enabling a SysV init script
|
||||
# in a chroot or similar.
|
||||
set -e
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [--root=path] enable|disable|is-enabled <sysv script name>" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
unset ROOT
|
||||
|
||||
# parse options
|
||||
eval set -- "$(getopt -o r: --long root: -- "$@")"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-r|--root)
|
||||
ROOT="$2"
|
||||
shift 2 ;;
|
||||
--) shift ; break ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
NAME="$2"
|
||||
[ -n "$NAME" ] || usage
|
||||
|
||||
case "$1" in
|
||||
enable)
|
||||
# call the command to enable SysV init script $NAME here
|
||||
# (consider optional $ROOT)
|
||||
echo "IMPLEMENT ME: enabling SysV init.d script $NAME"
|
||||
;;
|
||||
disable)
|
||||
# call the command to disable SysV init script $NAME here
|
||||
# (consider optional $ROOT)
|
||||
echo "IMPLEMENT ME: disabling SysV init.d script $NAME"
|
||||
;;
|
||||
is-enabled)
|
||||
# exit with 0 if $NAME is enabled, non-zero if it is disabled
|
||||
# (consider optional $ROOT)
|
||||
echo "IMPLEMENT ME: checking SysV init.d script $NAME"
|
||||
;;
|
||||
*)
|
||||
usage ;;
|
||||
esac
|
||||
9
src/sysv-generator/meson.build
Normal file
9
src/sysv-generator/meson.build
Normal file
@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
executables += [
|
||||
generator_template + {
|
||||
'name' : 'systemd-sysv-generator',
|
||||
'conditions' : ['HAVE_SYSV_COMPAT'],
|
||||
'sources' : files('sysv-generator.c'),
|
||||
},
|
||||
]
|
||||
937
src/sysv-generator/sysv-generator.c
Normal file
937
src/sysv-generator/sysv-generator.c
Normal file
@ -0,0 +1,937 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-messages.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "exit-status.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "generator.h"
|
||||
#include "glyph-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "initrd-util.h"
|
||||
#include "install.h"
|
||||
#include "log.h"
|
||||
#include "path-lookup.h"
|
||||
#include "path-util.h"
|
||||
#include "set.h"
|
||||
#include "special.h"
|
||||
#include "specifier.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "unit-name.h"
|
||||
|
||||
/* 🚨 Note: this generator is deprecated! Please do not add new features! Instead, please port remaining SysV
|
||||
* scripts over to native unit files! Thank you! 🚨 */
|
||||
|
||||
static const struct {
|
||||
const char *path;
|
||||
const char *target;
|
||||
} rcnd_table[] = {
|
||||
/* Standard SysV runlevels for start-up */
|
||||
{ "rc1.d", SPECIAL_RESCUE_TARGET },
|
||||
{ "rc2.d", SPECIAL_MULTI_USER_TARGET },
|
||||
{ "rc3.d", SPECIAL_MULTI_USER_TARGET },
|
||||
{ "rc4.d", SPECIAL_MULTI_USER_TARGET },
|
||||
{ "rc5.d", SPECIAL_GRAPHICAL_TARGET },
|
||||
|
||||
/* We ignore the SysV runlevels for shutdown here, as SysV services get default dependencies anyway, and that
|
||||
* means they are shut down anyway at system power off if running. */
|
||||
};
|
||||
|
||||
static const char *arg_dest = NULL;
|
||||
|
||||
typedef struct SysvStub {
|
||||
char *name;
|
||||
char *path;
|
||||
char *description;
|
||||
int sysv_start_priority;
|
||||
char *pid_file;
|
||||
char **before;
|
||||
char **after;
|
||||
char **wants;
|
||||
char **wanted_by;
|
||||
bool has_lsb;
|
||||
bool reload;
|
||||
bool loaded;
|
||||
} SysvStub;
|
||||
|
||||
static SysvStub* sysvstub_free(SysvStub *s) {
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
free(s->name);
|
||||
free(s->path);
|
||||
free(s->description);
|
||||
free(s->pid_file);
|
||||
strv_free(s->before);
|
||||
strv_free(s->after);
|
||||
strv_free(s->wants);
|
||||
strv_free(s->wanted_by);
|
||||
return mfree(s);
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(SysvStub*, sysvstub_free);
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
sysvstub_hash_ops,
|
||||
char, string_hash_func, string_compare_func,
|
||||
SysvStub, sysvstub_free);
|
||||
|
||||
static int add_alias(const char *service, const char *alias) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
|
||||
assert(service);
|
||||
assert(alias);
|
||||
|
||||
link = path_join(arg_dest, alias);
|
||||
if (!link)
|
||||
return -ENOMEM;
|
||||
|
||||
if (symlink(service, link) < 0) {
|
||||
if (errno == EEXIST)
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int generate_unit_file(SysvStub *s) {
|
||||
_cleanup_free_ char *path_escaped = NULL, *unit = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (!s->loaded)
|
||||
return 0;
|
||||
|
||||
path_escaped = specifier_escape(s->path);
|
||||
if (!path_escaped)
|
||||
return log_oom();
|
||||
|
||||
unit = path_join(arg_dest, s->name);
|
||||
if (!unit)
|
||||
return log_oom();
|
||||
|
||||
/* We might already have a symlink with the same name from a Provides:,
|
||||
* or from backup files like /etc/init.d/foo.bak. Real scripts always win,
|
||||
* so remove an existing link */
|
||||
if (is_symlink(unit) > 0) {
|
||||
log_warning("Overwriting existing symlink %s with real service.", unit);
|
||||
(void) unlink(unit);
|
||||
}
|
||||
|
||||
f = fopen(unit, "wxe");
|
||||
if (!f)
|
||||
return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
|
||||
|
||||
fprintf(f,
|
||||
"# Automatically generated by systemd-sysv-generator\n\n"
|
||||
"[Unit]\n"
|
||||
"Documentation=man:systemd-sysv-generator(8)\n"
|
||||
"SourcePath=%s\n",
|
||||
path_escaped);
|
||||
|
||||
if (s->description) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
t = specifier_escape(s->description);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
fprintf(f, "Description=%s\n", t);
|
||||
}
|
||||
|
||||
STRV_FOREACH(p, s->before)
|
||||
fprintf(f, "Before=%s\n", *p);
|
||||
STRV_FOREACH(p, s->after)
|
||||
fprintf(f, "After=%s\n", *p);
|
||||
STRV_FOREACH(p, s->wants)
|
||||
fprintf(f, "Wants=%s\n", *p);
|
||||
|
||||
fprintf(f,
|
||||
"\n[Service]\n"
|
||||
"Type=forking\n"
|
||||
"Restart=no\n"
|
||||
"TimeoutSec=5min\n"
|
||||
"IgnoreSIGPIPE=no\n"
|
||||
"KillMode=process\n"
|
||||
"GuessMainPID=no\n"
|
||||
"RemainAfterExit=%s\n",
|
||||
yes_no(!s->pid_file));
|
||||
|
||||
if (s->pid_file) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
t = specifier_escape(s->pid_file);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
fprintf(f, "PIDFile=%s\n", t);
|
||||
}
|
||||
|
||||
/* Consider two special LSB exit codes a clean exit */
|
||||
if (s->has_lsb)
|
||||
fprintf(f,
|
||||
"SuccessExitStatus=%i %i\n",
|
||||
EXIT_NOTINSTALLED,
|
||||
EXIT_NOTCONFIGURED);
|
||||
|
||||
fprintf(f,
|
||||
"ExecStart=%s start\n"
|
||||
"ExecStop=%s stop\n",
|
||||
path_escaped, path_escaped);
|
||||
|
||||
if (s->reload)
|
||||
fprintf(f, "ExecReload=%s reload\n", path_escaped);
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write unit %s: %m", unit);
|
||||
|
||||
STRV_FOREACH(p, s->wanted_by)
|
||||
(void) generator_add_symlink(arg_dest, *p, "wants", s->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool usage_contains_reload(const char *line) {
|
||||
return (strcasestr(line, "{reload|") ||
|
||||
strcasestr(line, "{reload}") ||
|
||||
strcasestr(line, "{reload\"") ||
|
||||
strcasestr(line, "|reload|") ||
|
||||
strcasestr(line, "|reload}") ||
|
||||
strcasestr(line, "|reload\""));
|
||||
}
|
||||
|
||||
static char *sysv_translate_name(const char *name) {
|
||||
_cleanup_free_ char *c = NULL;
|
||||
char *res;
|
||||
|
||||
c = strdup(name);
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
res = endswith(c, ".sh");
|
||||
if (res)
|
||||
*res = 0;
|
||||
|
||||
if (unit_name_mangle(c, 0, &res) < 0)
|
||||
return NULL;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int sysv_translate_facility(SysvStub *s, unsigned line, const char *name, char **ret) {
|
||||
|
||||
/* We silently ignore the $ prefix here. According to the LSB
|
||||
* spec it simply indicates whether something is a
|
||||
* standardized name or a distribution-specific one. Since we
|
||||
* just follow what already exists and do not introduce new
|
||||
* uses or names we don't care who introduced a new name. */
|
||||
|
||||
static const char * const table[] = {
|
||||
/* LSB defined facilities */
|
||||
"local_fs", NULL,
|
||||
"network", SPECIAL_NETWORK_ONLINE_TARGET,
|
||||
"named", SPECIAL_NSS_LOOKUP_TARGET,
|
||||
"portmap", SPECIAL_RPCBIND_TARGET,
|
||||
"remote_fs", SPECIAL_REMOTE_FS_TARGET,
|
||||
"syslog", NULL,
|
||||
"time", SPECIAL_TIME_SYNC_TARGET,
|
||||
};
|
||||
|
||||
_cleanup_free_ char *filename = NULL;
|
||||
const char *n;
|
||||
char *e, *m;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(s);
|
||||
assert(ret);
|
||||
|
||||
r = path_extract_filename(s->path, &filename);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract file name from path '%s': %m", s->path);
|
||||
|
||||
n = *name == '$' ? name + 1 : name;
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(table); i += 2) {
|
||||
if (!streq(table[i], n))
|
||||
continue;
|
||||
|
||||
if (!table[i+1]) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
m = strdup(table[i+1]);
|
||||
if (!m)
|
||||
return log_oom();
|
||||
|
||||
*ret = m;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we don't know this name, fallback heuristics to figure
|
||||
* out whether something is a target or a service alias. */
|
||||
|
||||
/* Facilities starting with $ are most likely targets */
|
||||
if (*name == '$') {
|
||||
r = unit_name_build(n, NULL, ".target", ret);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "[%s:%u] Could not build name for facility %s: %m", s->path, line, name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Strip ".sh" suffix from file name for comparison */
|
||||
e = endswith(filename, ".sh");
|
||||
if (e)
|
||||
*e = '\0';
|
||||
|
||||
/* Names equaling the file name of the services are redundant */
|
||||
if (streq_ptr(n, filename)) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Everything else we assume to be normal service names */
|
||||
m = sysv_translate_name(n);
|
||||
if (!m)
|
||||
return log_oom();
|
||||
|
||||
*ret = m;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int handle_provides(SysvStub *s, unsigned line, const char *full_text, const char *text) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(full_text);
|
||||
assert(text);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL, *m = NULL;
|
||||
|
||||
r = extract_first_word(&text, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = sysv_translate_facility(s, line, word, &m);
|
||||
if (r <= 0) /* continue on error */
|
||||
continue;
|
||||
|
||||
switch (unit_name_to_type(m)) {
|
||||
|
||||
case UNIT_SERVICE:
|
||||
log_debug("Adding Provides: alias '%s' for '%s'", m, s->name);
|
||||
r = add_alias(s->name, m);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "[%s:%u] Failed to add LSB Provides name %s, ignoring: %m", s->path, line, m);
|
||||
break;
|
||||
|
||||
case UNIT_TARGET:
|
||||
|
||||
/* NB: SysV targets which are provided by a
|
||||
* service are pulled in by the services, as
|
||||
* an indication that the generic service is
|
||||
* now available. This is strictly one-way.
|
||||
* The targets do NOT pull in SysV services! */
|
||||
|
||||
r = strv_extend(&s->before, m);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend(&s->wants, m);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) {
|
||||
r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
r = strv_extend(&s->wants, SPECIAL_NETWORK_TARGET);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case _UNIT_TYPE_INVALID:
|
||||
log_warning("Unit name '%s' is invalid", m);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("Unknown unit type for unit '%s'", m);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text, const char *text) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(full_text);
|
||||
assert(text);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *word = NULL, *m = NULL;
|
||||
bool is_before;
|
||||
|
||||
r = extract_first_word(&text, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = sysv_translate_facility(s, line, word, &m);
|
||||
if (r <= 0) /* continue on error */
|
||||
continue;
|
||||
|
||||
is_before = startswith_no_case(full_text, "X-Start-Before:");
|
||||
|
||||
if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET) && !is_before) {
|
||||
/* the network-online target is special, as it needs to be actively pulled in */
|
||||
r = strv_extend(&s->after, m);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend(&s->wants, m);
|
||||
} else
|
||||
r = strv_extend(is_before ? &s->before : &s->after, m);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_sysv(SysvStub *s) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
unsigned line = 0;
|
||||
int r;
|
||||
enum {
|
||||
NORMAL,
|
||||
DESCRIPTION,
|
||||
LSB,
|
||||
LSB_DESCRIPTION,
|
||||
USAGE_CONTINUATION
|
||||
} state = NORMAL;
|
||||
_cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL;
|
||||
char *description;
|
||||
bool supports_reload = false;
|
||||
|
||||
assert(s);
|
||||
|
||||
f = fopen(s->path, "re");
|
||||
if (!f) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return log_error_errno(errno, "Failed to open %s: %m", s->path);
|
||||
}
|
||||
|
||||
log_debug("Loading SysV script %s", s->path);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *l = NULL;
|
||||
|
||||
r = read_stripped_line(f, LONG_LINE_MAX, &l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read configuration file '%s': %m", s->path);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
line++;
|
||||
|
||||
if (l[0] != '#') {
|
||||
/* Try to figure out whether this init script supports
|
||||
* the reload operation. This heuristic looks for
|
||||
* "Usage" lines which include the reload option. */
|
||||
if (state == USAGE_CONTINUATION ||
|
||||
(state == NORMAL && strcasestr(l, "usage"))) {
|
||||
if (usage_contains_reload(l)) {
|
||||
supports_reload = true;
|
||||
state = NORMAL;
|
||||
} else if (endswith(l, "\\"))
|
||||
state = USAGE_CONTINUATION;
|
||||
else
|
||||
state = NORMAL;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state == NORMAL && streq(l, "### BEGIN INIT INFO")) {
|
||||
state = LSB;
|
||||
s->has_lsb = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IN_SET(state, LSB_DESCRIPTION, LSB) && streq(l, "### END INIT INFO")) {
|
||||
state = NORMAL;
|
||||
continue;
|
||||
}
|
||||
|
||||
char *t = l + 1;
|
||||
t += strspn(t, WHITESPACE);
|
||||
|
||||
if (state == NORMAL) {
|
||||
|
||||
/* Try to parse Red Hat style description */
|
||||
|
||||
if (startswith_no_case(t, "description:")) {
|
||||
|
||||
size_t k;
|
||||
const char *j;
|
||||
|
||||
k = strlen(t);
|
||||
if (k > 0 && t[k-1] == '\\') {
|
||||
state = DESCRIPTION;
|
||||
t[k-1] = 0;
|
||||
}
|
||||
|
||||
j = empty_to_null(strstrip(t+12));
|
||||
|
||||
r = free_and_strdup(&chkconfig_description, j);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
} else if (startswith_no_case(t, "pidfile:")) {
|
||||
const char *fn;
|
||||
|
||||
state = NORMAL;
|
||||
|
||||
fn = strstrip(t+8);
|
||||
if (!path_is_absolute(fn)) {
|
||||
log_error("[%s:%u] PID file not absolute. Ignoring.", s->path, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = free_and_strdup(&s->pid_file, fn);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
} else if (state == DESCRIPTION) {
|
||||
|
||||
/* Try to parse Red Hat style description
|
||||
* continuation */
|
||||
|
||||
size_t k;
|
||||
const char *j;
|
||||
|
||||
k = strlen(t);
|
||||
if (k > 0 && t[k-1] == '\\')
|
||||
t[k-1] = 0;
|
||||
else
|
||||
state = NORMAL;
|
||||
|
||||
j = strstrip(t);
|
||||
if (!isempty(j) && !strextend_with_separator(&chkconfig_description, " ", j))
|
||||
return log_oom();
|
||||
|
||||
} else if (IN_SET(state, LSB, LSB_DESCRIPTION)) {
|
||||
|
||||
if (startswith_no_case(t, "Provides:")) {
|
||||
state = LSB;
|
||||
|
||||
r = handle_provides(s, line, t, t + 9);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (startswith_no_case(t, "Required-Start:") ||
|
||||
startswith_no_case(t, "Should-Start:") ||
|
||||
startswith_no_case(t, "X-Start-Before:") ||
|
||||
startswith_no_case(t, "X-Start-After:")) {
|
||||
|
||||
state = LSB;
|
||||
|
||||
r = handle_dependencies(s, line, t, strchr(t, ':') + 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} else if (startswith_no_case(t, "Description:")) {
|
||||
const char *j;
|
||||
|
||||
state = LSB_DESCRIPTION;
|
||||
|
||||
j = empty_to_null(strstrip(t+12));
|
||||
|
||||
r = free_and_strdup(&long_description, j);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
} else if (startswith_no_case(t, "Short-Description:")) {
|
||||
const char *j;
|
||||
|
||||
state = LSB;
|
||||
|
||||
j = empty_to_null(strstrip(t+18));
|
||||
|
||||
r = free_and_strdup(&short_description, j);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
} else if (state == LSB_DESCRIPTION) {
|
||||
|
||||
if (startswith(l, "#\t") || startswith(l, "# ")) {
|
||||
const char *j;
|
||||
|
||||
j = strstrip(t);
|
||||
if (!isempty(j) && !strextend_with_separator(&long_description, " ", j))
|
||||
return log_oom();
|
||||
} else
|
||||
state = LSB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s->reload = supports_reload;
|
||||
|
||||
/* We use the long description only if
|
||||
* no short description is set. */
|
||||
|
||||
if (short_description)
|
||||
description = short_description;
|
||||
else if (chkconfig_description)
|
||||
description = chkconfig_description;
|
||||
else if (long_description)
|
||||
description = long_description;
|
||||
else
|
||||
description = NULL;
|
||||
|
||||
if (description) {
|
||||
char *d;
|
||||
|
||||
d = strjoin(s->has_lsb ? "LSB: " : "SYSV: ", description);
|
||||
if (!d)
|
||||
return log_oom();
|
||||
|
||||
s->description = d;
|
||||
}
|
||||
|
||||
s->loaded = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fix_order(SysvStub *s, Hashmap *all_services) {
|
||||
SysvStub *other;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (!s->loaded)
|
||||
return 0;
|
||||
|
||||
if (s->sysv_start_priority < 0)
|
||||
return 0;
|
||||
|
||||
HASHMAP_FOREACH(other, all_services) {
|
||||
if (s == other)
|
||||
continue;
|
||||
|
||||
if (!other->loaded)
|
||||
continue;
|
||||
|
||||
if (other->sysv_start_priority < 0)
|
||||
continue;
|
||||
|
||||
/* If both units have modern headers we don't care
|
||||
* about the priorities */
|
||||
if (s->has_lsb && other->has_lsb)
|
||||
continue;
|
||||
|
||||
if (other->sysv_start_priority < s->sysv_start_priority) {
|
||||
r = strv_extend(&s->after, other->name);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
} else if (other->sysv_start_priority > s->sysv_start_priority) {
|
||||
r = strv_extend(&s->before, other->name);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
} else
|
||||
continue;
|
||||
|
||||
/* FIXME: Maybe we should compare the name here lexicographically? */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_search_path(const char *def, const char *envvar, char ***ret) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
assert(def);
|
||||
assert(envvar);
|
||||
|
||||
e = getenv(envvar);
|
||||
if (e) {
|
||||
r = path_split_and_make_absolute(e, &l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make $%s search path absolute: %m", envvar);
|
||||
}
|
||||
|
||||
if (strv_isempty(l)) {
|
||||
strv_free(l);
|
||||
|
||||
l = strv_new(def);
|
||||
if (!l)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (!path_strv_resolve_uniq(l, NULL))
|
||||
return log_oom();
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
|
||||
_cleanup_strv_free_ char **sysvinit_path = NULL;
|
||||
int r;
|
||||
|
||||
assert(lp);
|
||||
|
||||
r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(path, sysvinit_path) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
|
||||
d = opendir(*path);
|
||||
if (!d) {
|
||||
if (errno != ENOENT)
|
||||
log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path);
|
||||
continue;
|
||||
}
|
||||
|
||||
FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) {
|
||||
_cleanup_free_ char *fpath = NULL, *name = NULL;
|
||||
_cleanup_(sysvstub_freep) SysvStub *service = NULL;
|
||||
struct stat st;
|
||||
|
||||
if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
|
||||
log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(st.st_mode & S_IXUSR))
|
||||
continue;
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
continue;
|
||||
|
||||
name = sysv_translate_name(de->d_name);
|
||||
if (!name)
|
||||
return log_oom();
|
||||
|
||||
if (hashmap_contains(all_services, name))
|
||||
continue;
|
||||
|
||||
r = unit_file_exists(RUNTIME_SCOPE_SYSTEM, lp, name);
|
||||
if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) {
|
||||
log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name);
|
||||
continue;
|
||||
} else if (r != 0) {
|
||||
log_debug("Native unit for %s already exists, skipping.", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
fpath = path_join(*path, de->d_name);
|
||||
if (!fpath)
|
||||
return log_oom();
|
||||
|
||||
log_struct(LOG_WARNING,
|
||||
LOG_MESSAGE("SysV service '%s' lacks a native systemd unit file, "
|
||||
"automatically generating a unit file for compatibility.\n"
|
||||
"Please update package to include a native systemd unit file.\n"
|
||||
"%s This compatibility logic is deprecated, expect removal soon. %s",
|
||||
fpath,
|
||||
glyph(GLYPH_WARNING_SIGN),
|
||||
glyph(GLYPH_WARNING_SIGN)),
|
||||
LOG_MESSAGE_ID(SD_MESSAGE_SYSV_GENERATOR_DEPRECATED_STR),
|
||||
LOG_ITEM("SYSVSCRIPT=%s", fpath),
|
||||
LOG_ITEM("UNIT=%s", name));
|
||||
|
||||
service = new(SysvStub, 1);
|
||||
if (!service)
|
||||
return log_oom();
|
||||
|
||||
*service = (SysvStub) {
|
||||
.sysv_start_priority = -1,
|
||||
.name = TAKE_PTR(name),
|
||||
.path = TAKE_PTR(fpath),
|
||||
};
|
||||
|
||||
r = hashmap_put(all_services, service->name, service);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
TAKE_PTR(service);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) {
|
||||
Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
|
||||
_cleanup_strv_free_ char **sysvrcnd_path = NULL;
|
||||
SysvStub *service;
|
||||
int r;
|
||||
|
||||
assert(lp);
|
||||
|
||||
r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(p, sysvrcnd_path)
|
||||
for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
|
||||
path = path_join(*p, rcnd_table[i].path);
|
||||
if (!path) {
|
||||
r = log_oom();
|
||||
goto finish;
|
||||
}
|
||||
|
||||
d = opendir(path);
|
||||
if (!d) {
|
||||
if (errno != ENOENT)
|
||||
log_warning_errno(errno, "Opening %s failed, ignoring: %m", path);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) {
|
||||
_cleanup_free_ char *name = NULL, *fpath = NULL;
|
||||
int a, b;
|
||||
|
||||
if (de->d_name[0] != 'S')
|
||||
continue;
|
||||
|
||||
if (strlen(de->d_name) < 4)
|
||||
continue;
|
||||
|
||||
a = undecchar(de->d_name[1]);
|
||||
b = undecchar(de->d_name[2]);
|
||||
|
||||
if (a < 0 || b < 0)
|
||||
continue;
|
||||
|
||||
fpath = path_join(*p, de->d_name);
|
||||
if (!fpath) {
|
||||
r = log_oom();
|
||||
goto finish;
|
||||
}
|
||||
|
||||
name = sysv_translate_name(de->d_name + 3);
|
||||
if (!name) {
|
||||
r = log_oom();
|
||||
goto finish;
|
||||
}
|
||||
|
||||
service = hashmap_get(all_services, name);
|
||||
if (!service) {
|
||||
log_debug("Ignoring %s symlink in %s, not generating %s.", de->d_name, rcnd_table[i].path, name);
|
||||
continue;
|
||||
}
|
||||
|
||||
service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority);
|
||||
|
||||
r = set_ensure_put(&runlevel_services[i], NULL, service);
|
||||
if (r < 0) {
|
||||
log_oom();
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++)
|
||||
SET_FOREACH(service, runlevel_services[i]) {
|
||||
r = strv_extend(&service->before, rcnd_table[i].target);
|
||||
if (r < 0) {
|
||||
log_oom();
|
||||
goto finish;
|
||||
}
|
||||
r = strv_extend(&service->wanted_by, rcnd_table[i].target);
|
||||
if (r < 0) {
|
||||
log_oom();
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++)
|
||||
set_free(runlevel_services[i]);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int run(const char *dest, const char *dest_early, const char *dest_late) {
|
||||
_cleanup_hashmap_free_ Hashmap *all_services = NULL;
|
||||
_cleanup_(lookup_paths_done) LookupPaths lp = {};
|
||||
SysvStub *service;
|
||||
int r;
|
||||
|
||||
if (in_initrd()) {
|
||||
log_debug("Skipping generator, running in the initrd.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
assert_se(arg_dest = dest_late);
|
||||
|
||||
r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
all_services = hashmap_new(&sysvstub_hash_ops);
|
||||
if (!all_services)
|
||||
return log_oom();
|
||||
|
||||
r = enumerate_sysv(&lp, all_services);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = set_dependencies_from_rcnd(&lp, all_services);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
HASHMAP_FOREACH(service, all_services)
|
||||
(void) load_sysv(service);
|
||||
|
||||
HASHMAP_FOREACH(service, all_services) {
|
||||
(void) fix_order(service, all_services);
|
||||
(void) generate_unit_file(service);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_MAIN_GENERATOR_FUNCTION(run);
|
||||
@ -528,11 +528,6 @@ executables += [
|
||||
'conditions' : ['BPF_FRAMEWORK'],
|
||||
'type' : 'manual',
|
||||
},
|
||||
core_test_template + {
|
||||
'sources' : files('test-thp.c'),
|
||||
'dependencies' : common_test_dependencies,
|
||||
'type' : 'manual',
|
||||
},
|
||||
core_test_template + {
|
||||
'sources' : files('test-cgroup-cpu.c'),
|
||||
},
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
#define PR_THP_DISABLE_NOT_SET 0
|
||||
#define PR_THP_DISABLE 1
|
||||
|
||||
static const char *arg_mode = NULL;
|
||||
|
||||
static int intro(void) {
|
||||
int r;
|
||||
|
||||
r = prctl(PR_GET_THP_DISABLE, 0, 0, 0, 0);
|
||||
if (streq_ptr(arg_mode, "no-disable")) {
|
||||
/* Test case: THPs should not be disabled */
|
||||
if (r != PR_THP_DISABLE_NOT_SET) {
|
||||
log_error("THPs disabled for the process r = %d: %m", r);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else if (streq_ptr(arg_mode, "disable")) {
|
||||
/* Test case: THPs should be completely disabled */
|
||||
if (r == PR_THP_DISABLE_NOT_SET)
|
||||
return log_tests_skipped("Disabling THPs completely for the process not supported");
|
||||
|
||||
if (r != PR_THP_DISABLE) {
|
||||
log_error("THPs not completely disabled for the process r = %d: %m", r);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else if (streq_ptr(arg_mode, "madvise")) {
|
||||
/* Test case: THPs should be only enabled on a madvise basis */
|
||||
if (r == PR_THP_DISABLE_NOT_SET)
|
||||
return log_tests_skipped("Disabling THPs except for madvise not supported");
|
||||
|
||||
if (r != (PR_THP_DISABLE | PR_THP_DISABLE_EXCEPT_ADVISED)) {
|
||||
log_error("THPs (except madvise) not completely disabled for the process r = %d: %m", r);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
log_error("Invalid mode: %s (expected: no-disable, disable, or madvise)", strna(arg_mode));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
log_error("Invalid number of args passed to the test %d", argc);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
arg_mode = argv[1];
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
return intro();
|
||||
}
|
||||
@ -65,6 +65,16 @@ endif
|
||||
|
||||
############################################################
|
||||
|
||||
if want_tests != 'false' and conf.get('HAVE_SYSV_COMPAT') == 1
|
||||
exe = executables_by_name.get('systemd-sysv-generator')
|
||||
test('sysv-generator-test',
|
||||
files('sysv-generator-test.py'),
|
||||
depends : exe,
|
||||
suite : 'sysv')
|
||||
endif
|
||||
|
||||
############################################################
|
||||
|
||||
if want_tests != 'false' and conf.get('HAVE_BLKID') == 1
|
||||
exe = executables_by_name.get('bootctl')
|
||||
test('test-bootctl-json',
|
||||
|
||||
413
test/sysv-generator-test.py
Executable file
413
test/sysv-generator-test.py
Executable file
@ -0,0 +1,413 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# systemd-sysv-generator integration test
|
||||
#
|
||||
# © 2015 Canonical Ltd.
|
||||
# Author: Martin Pitt <martin.pitt@ubuntu.com>
|
||||
|
||||
import collections
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from configparser import RawConfigParser
|
||||
from glob import glob
|
||||
|
||||
sysv_generator = './systemd-sysv-generator'
|
||||
|
||||
class MultiDict(collections.OrderedDict):
|
||||
def __setitem__(self, key, value):
|
||||
if isinstance(value, list) and key in self:
|
||||
self[key].extend(value)
|
||||
else:
|
||||
super(MultiDict, self).__setitem__(key, value)
|
||||
|
||||
class SysvGeneratorTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.workdir = tempfile.mkdtemp(prefix='sysv-gen-test.')
|
||||
self.init_d_dir = os.path.join(self.workdir, 'init.d')
|
||||
os.mkdir(self.init_d_dir)
|
||||
self.rcnd_dir = self.workdir
|
||||
self.unit_dir = os.path.join(self.workdir, 'systemd')
|
||||
os.mkdir(self.unit_dir)
|
||||
self.out_dir = os.path.join(self.workdir, 'output')
|
||||
os.mkdir(self.out_dir)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.workdir)
|
||||
|
||||
#
|
||||
# Helper methods
|
||||
#
|
||||
|
||||
def run_generator(self, expect_error=False):
|
||||
'''Run sysv-generator.
|
||||
|
||||
Fail if stderr contains any "Fail", unless expect_error is True.
|
||||
Return (stderr, filename -> ConfigParser) pair with output to stderr and
|
||||
parsed generated units.
|
||||
'''
|
||||
env = os.environ.copy()
|
||||
# We might debug log about errors that aren't actually fatal so let's bump the log level to info to
|
||||
# prevent those logs from interfering with the test.
|
||||
env['SYSTEMD_LOG_LEVEL'] = 'info'
|
||||
env['SYSTEMD_LOG_TARGET'] = 'console'
|
||||
env['SYSTEMD_SYSVINIT_PATH'] = self.init_d_dir
|
||||
env['SYSTEMD_SYSVRCND_PATH'] = self.rcnd_dir
|
||||
env['SYSTEMD_UNIT_PATH'] = self.unit_dir
|
||||
gen = subprocess.Popen(
|
||||
[sysv_generator, 'ignored', 'ignored', self.out_dir],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
universal_newlines=True, env=env)
|
||||
(out, err) = gen.communicate()
|
||||
if not expect_error:
|
||||
self.assertFalse('Fail' in err, err)
|
||||
self.assertEqual(gen.returncode, 0, err)
|
||||
|
||||
results = {}
|
||||
for service in glob(self.out_dir + '/*.service'):
|
||||
if os.path.islink(service):
|
||||
continue
|
||||
try:
|
||||
# for python3 we need here strict=False to parse multiple
|
||||
# lines with the same key
|
||||
cp = RawConfigParser(dict_type=MultiDict, strict=False)
|
||||
except TypeError:
|
||||
# RawConfigParser in python2 does not have the strict option
|
||||
# but it allows multiple lines with the same key by default
|
||||
cp = RawConfigParser(dict_type=MultiDict)
|
||||
cp.optionxform = lambda o: o # don't lower-case option names
|
||||
with open(service) as f:
|
||||
cp.read_file(f)
|
||||
results[os.path.basename(service)] = cp
|
||||
|
||||
return (err, results)
|
||||
|
||||
def add_sysv(self, fname, keys, enable=False, prio=1):
|
||||
'''Create a SysV init script with the given keys in the LSB header
|
||||
|
||||
There are sensible default values for all fields.
|
||||
If enable is True, links will be created in the rcN.d dirs. In that
|
||||
case, the priority can be given with "prio" (default to 1).
|
||||
|
||||
Return path of generated script.
|
||||
'''
|
||||
name_without_sh = fname.endswith('.sh') and fname[:-3] or fname
|
||||
keys.setdefault('Provides', name_without_sh)
|
||||
keys.setdefault('Required-Start', '$local_fs')
|
||||
keys.setdefault('Required-Stop', keys['Required-Start'])
|
||||
keys.setdefault('Default-Start', '2 3 4 5')
|
||||
keys.setdefault('Default-Stop', '0 1 6')
|
||||
keys.setdefault('Short-Description', 'test {} service'.format(name_without_sh))
|
||||
keys.setdefault('Description', 'long description for test {} service'.format(name_without_sh))
|
||||
script = os.path.join(self.init_d_dir, fname)
|
||||
with open(script, 'w') as f:
|
||||
f.write('#!/bin/init-d-interpreter\n### BEGIN INIT INFO\n')
|
||||
for k, v in keys.items():
|
||||
if v is not None:
|
||||
f.write('#{:>20} {}\n'.format(k + ':', v))
|
||||
f.write('### END INIT INFO\ncode --goes here\n')
|
||||
os.chmod(script, 0o755)
|
||||
|
||||
if enable:
|
||||
def make_link(prefix, runlevel):
|
||||
d = os.path.join(self.rcnd_dir, 'rc{}.d'.format(runlevel))
|
||||
if not os.path.isdir(d):
|
||||
os.mkdir(d)
|
||||
os.symlink('../init.d/' + fname, os.path.join(d, prefix + fname))
|
||||
|
||||
for rl in keys['Default-Start'].split():
|
||||
make_link('S%02i' % prio, rl)
|
||||
for rl in keys['Default-Stop'].split():
|
||||
make_link('K%02i' % (99 - prio), rl)
|
||||
|
||||
return script
|
||||
|
||||
def assert_enabled(self, unit, targets):
|
||||
'''assert that a unit is enabled in precisely the given targets'''
|
||||
|
||||
all_targets = ['multi-user', 'graphical']
|
||||
|
||||
# should be enabled
|
||||
for target in all_targets:
|
||||
link = os.path.join(self.out_dir, '{}.target.wants'.format(target), unit)
|
||||
if target in targets:
|
||||
unit_file = os.readlink(link)
|
||||
# os.path.exists() will fail on a dangling symlink
|
||||
self.assertTrue(os.path.exists(link))
|
||||
self.assertEqual(os.path.basename(unit_file), unit)
|
||||
else:
|
||||
self.assertFalse(os.path.exists(link),
|
||||
'{} unexpectedly exists'.format(link))
|
||||
|
||||
#
|
||||
# test cases
|
||||
#
|
||||
|
||||
def test_nothing(self):
|
||||
'''no input files'''
|
||||
|
||||
results = self.run_generator()[1]
|
||||
self.assertEqual(results, {})
|
||||
self.assertEqual(os.listdir(self.out_dir), [])
|
||||
|
||||
def test_simple_disabled(self):
|
||||
'''simple service without dependencies, disabled'''
|
||||
|
||||
self.add_sysv('foo', {}, enable=False)
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(len(results), 1)
|
||||
|
||||
# no enablement links or other stuff
|
||||
self.assertEqual(os.listdir(self.out_dir), ['foo.service'])
|
||||
|
||||
s = results['foo.service']
|
||||
self.assertEqual(s.sections(), ['Unit', 'Service'])
|
||||
self.assertEqual(s.get('Unit', 'Description'), 'LSB: test foo service')
|
||||
# $local_fs does not need translation, don't expect any dependency
|
||||
# fields here
|
||||
self.assertEqual(set(s.options('Unit')),
|
||||
set(['Documentation', 'SourcePath', 'Description']))
|
||||
|
||||
self.assertEqual(s.get('Service', 'Type'), 'forking')
|
||||
init_script = os.path.join(self.init_d_dir, 'foo')
|
||||
self.assertEqual(s.get('Service', 'ExecStart'),
|
||||
'{} start'.format(init_script))
|
||||
self.assertEqual(s.get('Service', 'ExecStop'),
|
||||
'{} stop'.format(init_script))
|
||||
|
||||
self.assertNotIn('Overwriting', err)
|
||||
|
||||
def test_simple_enabled_all(self):
|
||||
'''simple service without dependencies, enabled in all runlevels'''
|
||||
|
||||
self.add_sysv('foo', {}, enable=True)
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(list(results), ['foo.service'])
|
||||
self.assert_enabled('foo.service', ['multi-user', 'graphical'])
|
||||
self.assertNotIn('Overwriting', err)
|
||||
|
||||
def test_simple_escaped(self):
|
||||
'''simple service without dependencies, that requires escaping the name'''
|
||||
|
||||
self.add_sysv('foo+', {})
|
||||
self.add_sysv('foo-admin', {})
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(set(results), {'foo-admin.service', 'foo\\x2b.service'})
|
||||
self.assertNotIn('Overwriting', err)
|
||||
|
||||
def test_simple_enabled_some(self):
|
||||
'''simple service without dependencies, enabled in some runlevels'''
|
||||
|
||||
self.add_sysv('foo', {'Default-Start': '2 4'}, enable=True)
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(list(results), ['foo.service'])
|
||||
self.assert_enabled('foo.service', ['multi-user'])
|
||||
|
||||
def test_lsb_macro_dep_single(self):
|
||||
'''single LSB macro dependency: $network'''
|
||||
|
||||
self.add_sysv('foo', {'Required-Start': '$network'})
|
||||
s = self.run_generator()[1]['foo.service']
|
||||
self.assertEqual(set(s.options('Unit')),
|
||||
set(['Documentation', 'SourcePath', 'Description', 'After', 'Wants']))
|
||||
self.assertEqual(s.get('Unit', 'After'), 'network-online.target')
|
||||
self.assertEqual(s.get('Unit', 'Wants'), 'network-online.target')
|
||||
|
||||
def test_lsb_macro_dep_multi(self):
|
||||
'''multiple LSB macro dependencies'''
|
||||
|
||||
self.add_sysv('foo', {'Required-Start': '$named $portmap'})
|
||||
s = self.run_generator()[1]['foo.service']
|
||||
self.assertEqual(set(s.options('Unit')),
|
||||
set(['Documentation', 'SourcePath', 'Description', 'After']))
|
||||
self.assertEqual(s.get('Unit', 'After').split(), ['nss-lookup.target', 'rpcbind.target'])
|
||||
|
||||
def test_lsb_deps(self):
|
||||
'''LSB header dependencies to other services'''
|
||||
|
||||
# also give symlink priorities here; they should be ignored
|
||||
self.add_sysv('foo', {'Required-Start': 'must1 must2',
|
||||
'Should-Start': 'may1 ne_may2'},
|
||||
enable=True, prio=40)
|
||||
self.add_sysv('must1', {}, enable=True, prio=10)
|
||||
self.add_sysv('must2', {}, enable=True, prio=15)
|
||||
self.add_sysv('may1', {}, enable=True, prio=20)
|
||||
# do not create ne_may2
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(sorted(results),
|
||||
['foo.service', 'may1.service', 'must1.service', 'must2.service'])
|
||||
|
||||
# foo should depend on all of them
|
||||
self.assertEqual(sorted(results['foo.service'].get('Unit', 'After').split()),
|
||||
['may1.service', 'must1.service', 'must2.service', 'ne_may2.service'])
|
||||
|
||||
# other services should not depend on each other
|
||||
self.assertFalse(results['must1.service'].has_option('Unit', 'After'))
|
||||
self.assertFalse(results['must2.service'].has_option('Unit', 'After'))
|
||||
self.assertFalse(results['may1.service'].has_option('Unit', 'After'))
|
||||
|
||||
def test_symlink_prio_deps(self):
|
||||
'''script without LSB headers use rcN.d priority'''
|
||||
|
||||
# create two init.d scripts without LSB header and enable them with
|
||||
# startup priorities
|
||||
for prio, name in [(10, 'provider'), (15, 'consumer')]:
|
||||
with open(os.path.join(self.init_d_dir, name), 'w') as f:
|
||||
f.write('#!/bin/init-d-interpreter\ncode --goes here\n')
|
||||
os.fchmod(f.fileno(), 0o755)
|
||||
|
||||
d = os.path.join(self.rcnd_dir, 'rc2.d')
|
||||
if not os.path.isdir(d):
|
||||
os.mkdir(d)
|
||||
os.symlink('../init.d/' + name, os.path.join(d, 'S{:>2}{}'.format(prio, name)))
|
||||
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(sorted(results), ['consumer.service', 'provider.service'])
|
||||
self.assertFalse(results['provider.service'].has_option('Unit', 'After'))
|
||||
self.assertEqual(results['consumer.service'].get('Unit', 'After'),
|
||||
'provider.service')
|
||||
|
||||
def test_multiple_provides(self):
|
||||
'''multiple Provides: names'''
|
||||
|
||||
self.add_sysv('foo', {'Provides': 'foo bar baz'})
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(list(results), ['foo.service'])
|
||||
self.assertEqual(set(results['foo.service'].options('Unit')),
|
||||
set(['Documentation', 'SourcePath', 'Description']))
|
||||
# should create symlinks for the alternative names
|
||||
for f in ['bar.service', 'baz.service']:
|
||||
self.assertEqual(os.readlink(os.path.join(self.out_dir, f)),
|
||||
'foo.service')
|
||||
self.assertNotIn('Overwriting', err)
|
||||
|
||||
def test_provides_escaped(self):
|
||||
'''a script that Provides: a name that requires escaping'''
|
||||
|
||||
self.add_sysv('foo', {'Provides': 'foo foo+'})
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(list(results), ['foo.service'])
|
||||
self.assertEqual(os.readlink(os.path.join(self.out_dir, 'foo\\x2b.service')),
|
||||
'foo.service')
|
||||
self.assertNotIn('Overwriting', err)
|
||||
|
||||
def test_same_provides_in_multiple_scripts(self):
|
||||
'''multiple init.d scripts provide the same name'''
|
||||
|
||||
self.add_sysv('foo', {'Provides': 'foo common'}, enable=True, prio=1)
|
||||
self.add_sysv('bar', {'Provides': 'bar common'}, enable=True, prio=2)
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(sorted(results), ['bar.service', 'foo.service'])
|
||||
# should create symlink for the alternative name for either unit
|
||||
self.assertIn(os.readlink(os.path.join(self.out_dir, 'common.service')),
|
||||
['foo.service', 'bar.service'])
|
||||
|
||||
def test_provide_other_script(self):
|
||||
'''init.d scripts provides the name of another init.d script'''
|
||||
|
||||
self.add_sysv('foo', {'Provides': 'foo bar'}, enable=True)
|
||||
self.add_sysv('bar', {'Provides': 'bar'}, enable=True)
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(sorted(results), ['bar.service', 'foo.service'])
|
||||
# we do expect an overwrite here, bar.service should overwrite the
|
||||
# alias link from foo.service
|
||||
self.assertIn('Overwriting', err)
|
||||
|
||||
def test_nonexecutable_script(self):
|
||||
'''ignores non-executable init.d script'''
|
||||
|
||||
os.chmod(self.add_sysv('foo', {}), 0o644)
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(results, {})
|
||||
|
||||
def test_sh_suffix(self):
|
||||
'''init.d script with .sh suffix'''
|
||||
|
||||
self.add_sysv('foo.sh', {}, enable=True)
|
||||
err, results = self.run_generator()
|
||||
s = results['foo.service']
|
||||
|
||||
self.assertEqual(s.sections(), ['Unit', 'Service'])
|
||||
# should not have a .sh
|
||||
self.assertEqual(s.get('Unit', 'Description'), 'LSB: test foo service')
|
||||
|
||||
# calls correct script with .sh
|
||||
init_script = os.path.join(self.init_d_dir, 'foo.sh')
|
||||
self.assertEqual(s.get('Service', 'ExecStart'),
|
||||
'{} start'.format(init_script))
|
||||
self.assertEqual(s.get('Service', 'ExecStop'),
|
||||
'{} stop'.format(init_script))
|
||||
|
||||
self.assert_enabled('foo.service', ['multi-user', 'graphical'])
|
||||
|
||||
def test_sh_suffix_with_provides(self):
|
||||
'''init.d script with .sh suffix and Provides:'''
|
||||
|
||||
self.add_sysv('foo.sh', {'Provides': 'foo bar'})
|
||||
err, results = self.run_generator()
|
||||
# ensure we don't try to create a symlink to itself
|
||||
self.assertNotIn('itself', err)
|
||||
self.assertEqual(list(results), ['foo.service'])
|
||||
self.assertEqual(results['foo.service'].get('Unit', 'Description'),
|
||||
'LSB: test foo service')
|
||||
|
||||
# should create symlink for the alternative name
|
||||
self.assertEqual(os.readlink(os.path.join(self.out_dir, 'bar.service')),
|
||||
'foo.service')
|
||||
|
||||
def test_hidden_files(self):
|
||||
'''init.d script with hidden file suffix'''
|
||||
|
||||
script = self.add_sysv('foo', {}, enable=True)
|
||||
# backup files (not enabled in rcN.d/)
|
||||
shutil.copy(script, script + '.dpkg-new')
|
||||
shutil.copy(script, script + '.dpkg-dist')
|
||||
shutil.copy(script, script + '.swp')
|
||||
shutil.copy(script, script + '.rpmsave')
|
||||
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(list(results), ['foo.service'])
|
||||
|
||||
self.assert_enabled('foo.service', ['multi-user', 'graphical'])
|
||||
|
||||
def test_backup_file(self):
|
||||
'''init.d script with backup file'''
|
||||
|
||||
script = self.add_sysv('foo', {}, enable=True)
|
||||
# backup files (not enabled in rcN.d/)
|
||||
shutil.copy(script, script + '.bak')
|
||||
shutil.copy(script, script + '.old')
|
||||
shutil.copy(script, script + '.tmp')
|
||||
shutil.copy(script, script + '.new')
|
||||
|
||||
err, results = self.run_generator()
|
||||
print(err)
|
||||
self.assertEqual(sorted(results), ['foo.service', 'foo.tmp.service'])
|
||||
|
||||
# ensure we don't try to create a symlink to itself
|
||||
self.assertNotIn('itself', err)
|
||||
|
||||
self.assert_enabled('foo.service', ['multi-user', 'graphical'])
|
||||
self.assert_enabled('foo.bak.service', [])
|
||||
self.assert_enabled('foo.old.service', [])
|
||||
|
||||
def test_existing_native_unit(self):
|
||||
'''existing native unit'''
|
||||
|
||||
with open(os.path.join(self.unit_dir, 'foo.service'), 'w') as f:
|
||||
f.write('[Unit]\n')
|
||||
|
||||
self.add_sysv('foo.sh', {'Provides': 'foo bar'}, enable=True)
|
||||
err, results = self.run_generator()
|
||||
self.assertEqual(list(results), [])
|
||||
# no enablement or alias links, as native unit is disabled
|
||||
self.assertEqual(os.listdir(self.out_dir), [])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))
|
||||
@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
systemd-run --wait \
|
||||
/usr/lib/systemd/tests/unit-tests/manual/test-thp no-disable
|
||||
|
||||
systemd-run --wait \
|
||||
-p MemoryTHP=disable \
|
||||
/usr/lib/systemd/tests/unit-tests/manual/test-thp disable
|
||||
|
||||
# The following test will always return 77 if at compile time the kernel version
|
||||
# is less than 6.18. If it happens don't let the whole test fail
|
||||
set +e
|
||||
|
||||
systemd-run --wait \
|
||||
-p MemoryTHP=madvise \
|
||||
/usr/lib/systemd/tests/unit-tests/manual/test-thp madvise
|
||||
|
||||
if [ $? -eq 77 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
set -e
|
||||
@ -449,6 +449,118 @@ systemctl unset-environment IMPORT_THIS IMPORT_THIS_TOO
|
||||
(! systemctl show-environment | grep "^IMPORT_THIS=")
|
||||
(! systemctl show-environment | grep "^IMPORT_THIS_TOO=")
|
||||
|
||||
# test for sysv-generator (issue #24990)
|
||||
if [[ -x /usr/lib/systemd/system-generators/systemd-sysv-generator ]]; then
|
||||
# This is configurable via -Dsysvinit-path=, but we can't get the value
|
||||
# at runtime, so let's just support the two most common paths for now.
|
||||
[[ -d /etc/rc.d/init.d ]] && SYSVINIT_PATH="/etc/rc.d/init.d" || SYSVINIT_PATH="/etc/init.d"
|
||||
|
||||
# OpenSUSE leaves sysvinit-path enabled, which means systemd-sysv-generator is built
|
||||
# but may not create the directory if there's no services that use it.
|
||||
mkdir -p "$SYSVINIT_PATH"
|
||||
|
||||
# invalid dependency
|
||||
cat >"${SYSVINIT_PATH:?}/issue-24990" <<\EOF
|
||||
#!/usr/bin/env bash
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides:test1 test2
|
||||
# Required-Start:test1 $remote_fs $network
|
||||
# Required-Stop:test1 $remote_fs $network
|
||||
# Description:Test
|
||||
# Short-Description: Test
|
||||
### END INIT INFO
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo "Starting issue-24990.service"
|
||||
sleep 1000 &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping issue-24990.service"
|
||||
sleep 10 &
|
||||
;;
|
||||
*)
|
||||
echo "Usage: service test {start|stop|restart|status}"
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
|
||||
chmod +x "$SYSVINIT_PATH/issue-24990"
|
||||
systemctl daemon-reload
|
||||
[[ -L /run/systemd/generator.late/test1.service ]]
|
||||
[[ -L /run/systemd/generator.late/test2.service ]]
|
||||
assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
|
||||
assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
|
||||
output=$(systemctl cat issue-24990)
|
||||
assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
|
||||
assert_in "Description=LSB: Test" "$output"
|
||||
assert_in "After=test1.service" "$output"
|
||||
assert_in "After=remote-fs.target" "$output"
|
||||
assert_in "After=network-online.target" "$output"
|
||||
assert_in "Wants=network-online.target" "$output"
|
||||
assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
|
||||
assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
|
||||
systemctl status issue-24990 || :
|
||||
systemctl show issue-24990
|
||||
assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
|
||||
assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
|
||||
|
||||
if ! systemctl is-active network-online.target; then
|
||||
systemctl start network-online.target
|
||||
fi
|
||||
|
||||
systemctl restart issue-24990
|
||||
systemctl stop issue-24990
|
||||
|
||||
# valid dependency
|
||||
cat >"$SYSVINIT_PATH/issue-24990" <<\EOF
|
||||
#!/usr/bin/env bash
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides:test1 test2
|
||||
# Required-Start:$remote_fs
|
||||
# Required-Stop:$remote_fs
|
||||
# Description:Test
|
||||
# Short-Description: Test
|
||||
### END INIT INFO
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo "Starting issue-24990.service"
|
||||
sleep 1000 &
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping issue-24990.service"
|
||||
sleep 10 &
|
||||
;;
|
||||
*)
|
||||
echo "Usage: service test {start|stop|restart|status}"
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
|
||||
chmod +x "$SYSVINIT_PATH/issue-24990"
|
||||
systemctl daemon-reload
|
||||
[[ -L /run/systemd/generator.late/test1.service ]]
|
||||
[[ -L /run/systemd/generator.late/test2.service ]]
|
||||
assert_eq "$(readlink -f /run/systemd/generator.late/test1.service)" "/run/systemd/generator.late/issue-24990.service"
|
||||
assert_eq "$(readlink -f /run/systemd/generator.late/test2.service)" "/run/systemd/generator.late/issue-24990.service"
|
||||
output=$(systemctl cat issue-24990)
|
||||
assert_in "SourcePath=$SYSVINIT_PATH/issue-24990" "$output"
|
||||
assert_in "Description=LSB: Test" "$output"
|
||||
assert_in "After=remote-fs.target" "$output"
|
||||
assert_in "ExecStart=$SYSVINIT_PATH/issue-24990 start" "$output"
|
||||
assert_in "ExecStop=$SYSVINIT_PATH/issue-24990 stop" "$output"
|
||||
systemctl status issue-24990 || :
|
||||
systemctl show issue-24990
|
||||
assert_not_in "issue-24990.service" "$(systemctl show --property=After --value)"
|
||||
assert_not_in "issue-24990.service" "$(systemctl show --property=Before --value)"
|
||||
|
||||
systemctl restart issue-24990
|
||||
systemctl stop issue-24990
|
||||
fi
|
||||
|
||||
# %J in WantedBy= causes ABRT (#26467)
|
||||
cat >/run/systemd/system/test-WantedBy.service <<EOF
|
||||
[Service]
|
||||
|
||||
@ -270,11 +270,11 @@ systemd-run -P \
|
||||
cat /usr/lib/os-release | grep -F "MARKER=1" >/dev/null
|
||||
systemd-run -P \
|
||||
-p RootImage="$MINIMAL_IMAGE.raw" \
|
||||
-p RootImageOptions="root:noatime,dev home:ro,dev ro,nosuid" \
|
||||
-p RootImageOptions="root:nosuid,dev home:ro,dev ro,noatime" \
|
||||
mount | grep -F "squashfs" | grep -F "nosuid" >/dev/null
|
||||
systemd-run -P \
|
||||
-p RootImage="$MINIMAL_IMAGE.gpt" \
|
||||
-p RootImageOptions="root:ro,dev root:ro,noatime" \
|
||||
-p RootImageOptions="root:ro,noatime root:ro,dev" \
|
||||
mount | grep -F "squashfs" | grep -F "noatime" >/dev/null
|
||||
|
||||
mkdir -p "$IMAGE_DIR/result"
|
||||
@ -285,8 +285,8 @@ ExecStart=bash -c "mount >/run/result/a"
|
||||
BindPaths=$IMAGE_DIR/result:/run/result
|
||||
TemporaryFileSystem=/run
|
||||
RootImage=$MINIMAL_IMAGE.raw
|
||||
RootImageOptions=root:relatime,dev home:ro,dev nosuid,dev
|
||||
RootImageOptions=ro,noatime
|
||||
RootImageOptions=root:ro,noatime home:ro,dev relatime,dev
|
||||
RootImageOptions=nosuid,dev
|
||||
EOF
|
||||
systemctl start testservice-50a.service
|
||||
grep -F "squashfs" "$IMAGE_DIR/result/a" | grep -F "noatime" >/dev/null
|
||||
@ -299,8 +299,8 @@ ExecStart=bash -c "mount >/run/result/b"
|
||||
BindPaths=$IMAGE_DIR/result:/run/result
|
||||
TemporaryFileSystem=/run
|
||||
RootImage=$MINIMAL_IMAGE.gpt
|
||||
RootImageOptions=nosuid,dev home:ro,dev nosuid,dev
|
||||
RootImageOptions=home:ro,dev,%%foo root:ro,noatime,nosuid
|
||||
RootImageOptions=root:ro,noatime,nosuid home:ro,dev nosuid,dev
|
||||
RootImageOptions=home:ro,dev nosuid,dev,%%foo
|
||||
# this is the default, but let's specify once to test the parser
|
||||
MountAPIVFS=yes
|
||||
EOF
|
||||
@ -310,7 +310,7 @@ grep -F "squashfs" "$IMAGE_DIR/result/b" | grep -F "noatime" >/dev/null
|
||||
# Check that specifier escape is applied %%foo → %foo
|
||||
busctl get-property org.freedesktop.systemd1 \
|
||||
/org/freedesktop/systemd1/unit/testservice_2d50b_2eservice \
|
||||
org.freedesktop.systemd1.Service RootImageOptions | grep -F "ro,dev,%foo"
|
||||
org.freedesktop.systemd1.Service RootImageOptions | grep -F "nosuid,dev,%foo"
|
||||
|
||||
# Now do some checks with MountImages, both by itself, with options and in combination with RootImage, and as single FS or GPT image
|
||||
systemd-run -P \
|
||||
|
||||
@ -16,3 +16,9 @@ L /var/lock - - - - ../run/lock
|
||||
{% if CREATE_LOG_DIRS %}
|
||||
L$ /var/log/README - - - - ../..{{DOC_DIR}}/README.logs
|
||||
{% endif %}
|
||||
|
||||
{% if HAVE_SYSV_COMPAT %}
|
||||
# /run/lock/subsys is used for serializing SysV service execution, and
|
||||
# hence without use on SysV-less systems.
|
||||
d /run/lock/subsys 0755 root root -
|
||||
{% endif %}
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
Description=Console Getty
|
||||
Documentation=man:agetty(8) man:systemd-getty-generator(8)
|
||||
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
|
||||
{% if HAVE_SYSV_RC_LOCAL %}
|
||||
{% if HAVE_SYSV_COMPAT %}
|
||||
After=rc-local.service
|
||||
{% endif %}
|
||||
Before=getty.target
|
||||
|
||||
@ -12,7 +12,7 @@ Description=Container Getty on /dev/pts/%I
|
||||
Documentation=man:agetty(8) man:systemd-getty-generator(8)
|
||||
Documentation=man:machinectl(1)
|
||||
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
|
||||
{% if HAVE_SYSV_RC_LOCAL %}
|
||||
{% if HAVE_SYSV_COMPAT %}
|
||||
After=rc-local.service
|
||||
{% endif %}
|
||||
Before=getty.target
|
||||
|
||||
@ -12,7 +12,7 @@ Description=Getty on %I
|
||||
Documentation=man:agetty(8) man:systemd-getty-generator(8)
|
||||
Documentation=https://0pointer.de/blog/projects/serial-console.html
|
||||
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
|
||||
{% if HAVE_SYSV_RC_LOCAL %}
|
||||
{% if HAVE_SYSV_COMPAT %}
|
||||
After=rc-local.service
|
||||
{% endif %}
|
||||
|
||||
|
||||
@ -176,7 +176,7 @@ units = [
|
||||
},
|
||||
{
|
||||
'file' : 'rc-local.service.in',
|
||||
'conditions' : ['HAVE_SYSV_RC_LOCAL'],
|
||||
'conditions' : ['HAVE_SYSV_COMPAT'],
|
||||
},
|
||||
{
|
||||
'file' : 'reboot.target',
|
||||
|
||||
@ -13,7 +13,7 @@ Documentation=man:agetty(8) man:systemd-getty-generator(8)
|
||||
Documentation=https://0pointer.de/blog/projects/serial-console.html
|
||||
BindsTo=dev-%i.device
|
||||
After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
|
||||
{% if HAVE_SYSV_RC_LOCAL %}
|
||||
{% if HAVE_SYSV_COMPAT %}
|
||||
After=rc-local.service
|
||||
{% endif %}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user