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

Compare commits

..

11 Commits

Author SHA1 Message Date
Yu Watanabe
3ff23e8c4b man: do not manually update man/rules/meson.build
Follow-up for 25393c7c907b2c460a8a34d7dc6a1bdbcac8e9d4.
2026-01-06 22:50:47 +01:00
Yu Watanabe
17ea504efb
core: change mount options settings so that last defined wins (#39449) 2026-01-07 04:11:29 +09:00
Yu Watanabe
a6d553c4f4
Drop support for sysvinit scripts (#39770)
As announced by a few releases now, finally drop support for sysvinit scripts.
2026-01-07 02:51:53 +09:00
Luca Boccassi
b86a5a2dbc NEWS: note mount image options rule changes 2026-01-06 18:00:20 +01:00
Luca Boccassi
9de41f677c core: change mount options settings so that last defined wins
Currently mount options are handled in such a way that the first
definition for a given partition wins, and documented as such.
Change them so that they behave like other options, and the
last specified wins.
Applies to RootImageOptions=, MountImages= and ExtensionImages=.
Switch from a linked list to an array indexed by the partition
specifier to store them.
2026-01-06 17:59:10 +01:00
Yu Watanabe
0ab02b67d4
core: add support for disabling THPs (#39085)
Transparent Hugepages (THP) 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
/sys/kernel/mm/transparent_hugepage/enabled.
However, it can be overridden for individual workloads via prctl(2) call.
MemoryTHP= is used to disable THPs at exec-invoke to stop providing THPs
for workloads where the drawbacks outweigh the advantages. When set to
"disable", MemoryTHP= disables THPs completely for the process,
irrespective of global THP controls.

[1] https://man7.org/linux/man-pages/man2/PR_SET_THP_DISABLE.2const.html
[2] https://man7.org/linux/man-pages/man2/madvise.2.html
[3] 9dc21bbd62
2026-01-07 01:36:20 +09:00
francescoza6
fcc6b29921 hwdb: add matrix for ASUS 2-in-1 T101HA
The matrix tested working in monitor-sensor (06 gen 2026),
corrects accel values and not just display output.
2026-01-07 01:10:42 +09:00
Usama Arif
4d16530b83 src/test: add unittest for MemoryTHP=
This checks if the prctl is set correctly when the property name
is passed to systemd-run.
2026-01-06 03:26:15 -08:00
Usama Arif
32614b9aab core: introduce MemoryTHP= unit file setting
Transparent Hugepages (THP) 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 /sys/kernel/mm/transparent_hugepage/enabled.
However, it can be overridden for individual workloads via prctl(2)
call.
MemoryTHP= is used to disable THPs at exec-invoke to stop
providing THPs for workloads where the drawbacks outweigh the advantages.
When set to "disable", MemoryTHP= disables THPs completely for the
process, irrespecitive of global THP controls.
When set to "madvise", MemoryTHP= disables THPs for the process except
when specifically madvised by the process with MADV_HUGEPAGE or MADV_COLLAPSE.
2026-01-06 03:26:14 -08:00
Luca Boccassi
986fee6217 Drop support for sysvinit scripts
As announced by a few releases now, finally drop support for
sysvinit scripts.
Keep rc-local generator for now, as it's really a distinct
feature even though from the same era.
2026-01-06 10:34:02 +01:00
Luca Boccassi
5b3a86f6c3 rc-local and sysvinit are independent, adjust meson/units/docs
They are separate and independent settings, so adjust meson rules
and unit files accordingly. It is possible to enable support for
rc-local script without support for sysvinit scripts, and viceversa.
This will become useful later when sysvinit scripts support is
removed.
2026-01-06 10:34:00 +01:00
64 changed files with 585 additions and 2268 deletions

View File

@ -133,7 +133,6 @@ body:
- 'systemd-sysctl'
- 'systemd-sysext'
- 'systemd-sysusers'
- 'systemd-sysv-generator'
- 'systemd-timedate'
- 'systemd-timesync'
- 'systemd-tmpfiles'

View File

@ -74,7 +74,6 @@ body:
- 'systemd-sysctl'
- 'systemd-sysext'
- 'systemd-sysusers'
- 'systemd-sysv-generator'
- 'systemd-timedate'
- 'systemd-timesync'
- 'systemd-tmpfiles'

View File

@ -56,7 +56,6 @@ 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
View File

@ -11,6 +11,12 @@ 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:

View File

@ -13,8 +13,6 @@ 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=`

View File

@ -449,14 +449,6 @@ 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

View File

@ -1,27 +0,0 @@
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

View File

@ -1,8 +0,0 @@
# 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)

View File

@ -160,7 +160,8 @@ 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 T103HAF
# Asus Transformer Mini T101HA & T103HAF
sensor:modalias:platform:HID-SENSOR-200073*:dmi:*svn*ASUSTeK*:pnT101HA:*
sensor:modalias:platform:HID-SENSOR-200073*:dmi:*svn*ASUSTeK*:pnT103HAF:*
ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, -1

View File

@ -3421,6 +3421,8 @@ 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 = '...';
@ -4036,6 +4038,8 @@ 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!-->
@ -4784,6 +4788,8 @@ 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"/>
@ -5684,6 +5690,8 @@ 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 = '...';
@ -6317,6 +6325,8 @@ 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!-->
@ -7041,6 +7051,8 @@ 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"/>
@ -7765,6 +7777,8 @@ 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 = '...';
@ -8322,6 +8336,8 @@ 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!-->
@ -8954,6 +8970,8 @@ 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"/>
@ -9811,6 +9829,8 @@ 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 = '...';
@ -10350,6 +10370,8 @@ 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!-->
@ -10964,6 +10986,8 @@ 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"/>
@ -12598,7 +12622,8 @@ $ 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> was added in version 260.</para>
<para><varname>BindNetworkInterface</varname>, and
<varname>MemoryTHP</varname> were added in version 260.</para>
</refsect2>
<refsect2>
<title>Socket Unit Objects</title>
@ -12667,7 +12692,8 @@ $ 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> was added in version 260.</para>
<para><varname>BindNetworkInterface</varname>, and
<varname>MemoryTHP</varname> were added in version 260.</para>
</refsect2>
<refsect2>
<title>Mount Unit Objects</title>
@ -12731,7 +12757,8 @@ $ 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> was added in version 260.</para>
<para><varname>BindNetworkInterface</varname>, and
<varname>MemoryTHP</varname> were added in version 260.</para>
</refsect2>
<refsect2>
<title>Swap Unit Objects</title>
@ -12793,7 +12820,8 @@ $ 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> was added in version 260.</para>
<para><varname>BindNetworkInterface</varname>, and
<varname>MemoryTHP</varname> were added in version 260.</para>
</refsect2>
<refsect2>
<title>Slice Unit Objects</title>

View File

@ -1133,7 +1133,7 @@ manpages = [
['systemd-rc-local-generator',
'8',
['rc-local.service'],
'HAVE_SYSV_COMPAT HAVE_SYSV_RC_LOCAL'],
'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,7 +1190,6 @@ 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'],

View File

@ -6,7 +6,7 @@
]>
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-rc-local-generator" conditional='HAVE_SYSV_COMPAT HAVE_SYSV_RC_LOCAL'>
<refentry id="systemd-rc-local-generator" conditional='HAVE_SYSV_RC_LOCAL'>
<refentryinfo>
<title>systemd-rc-local-generator</title>
<productname>systemd</productname>

View File

@ -1,68 +0,0 @@
<?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>

View File

@ -6,7 +6,7 @@
%entities;
]>
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-tpm2-generator">
<refentry id="systemd-tpm2-generator" conditional='ENABLE_BOOTLOADER'>
<refentryinfo>
<title>systemd-tpm2-generator</title>

View File

@ -213,8 +213,7 @@
<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. Duplicated options are ignored. For a list of valid mount options, please
refer to
string removes previous assignments. For a list of valid mount options, please refer to
<citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
</para>
@ -2134,6 +2133,41 @@ 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>

View File

@ -407,7 +407,6 @@ 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>

View File

@ -50,12 +50,6 @@
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>

View File

@ -90,10 +90,6 @@ 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'))
@ -303,8 +299,6 @@ 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)
@ -2431,7 +2425,6 @@ 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')
@ -2792,7 +2785,6 @@ subdir('test')
#####################################################################
subdir('docs/sysvinit')
subdir('docs/var-log')
subdir('hwdb.d')
subdir('man')
@ -3023,8 +3015,6 @@ 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,

View File

@ -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',
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('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('rc-local', type : 'string', value : '/etc/rc.local',
description : 'path to SysV rc.local script')
option('initrd', type : 'boolean',

View File

@ -59,6 +59,8 @@ 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"

View File

@ -26,6 +26,7 @@ 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.*")

View File

@ -232,12 +232,6 @@ const char* const systemd_features =
" -UTMP"
#endif
#if HAVE_SYSV_COMPAT
" +SYSVINIT"
#else
" -SYSVINIT"
#endif
#if HAVE_LIBARCHIVE
" +LIBARCHIVE"
#else

View File

@ -58,6 +58,7 @@ 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);
@ -793,6 +794,33 @@ 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,
@ -803,25 +831,12 @@ 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);
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);
return bus_append_mount_options(reply, c->root_image_options);
}
static int property_get_mount_images(
@ -857,19 +872,7 @@ static int property_get_mount_images(
if (r < 0)
return r;
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);
r = bus_append_mount_options(reply, i->mount_options);
if (r < 0)
return r;
@ -913,19 +916,7 @@ static int property_get_extension_images(
if (r < 0)
return r;
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);
r = bus_append_mount_options(reply, i->mount_options);
if (r < 0)
return r;
@ -1407,6 +1398,7 @@ 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),
@ -1845,6 +1837,7 @@ 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);
@ -1899,7 +1892,17 @@ int bus_exec_context_set_transient_property(
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (options) {
LIST_JOIN(mount_options, c->root_image_options, 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]);
}
unit_write_settingf(
u, flags|UNIT_ESCAPE_SPECIFIERS, name,
"%s=%s",
@ -2333,6 +2336,9 @@ 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);

View File

@ -256,12 +256,13 @@ int bus_verify_bypass_dump_ratelimit_async(Manager *m, sd_bus_message *call, sd_
reterr_error);
}
/* ret_format_str is an accumulator, so if it has any pre-existing content, new options will be appended to it */
/* 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 */
int bus_read_mount_options(
sd_bus_message *message,
sd_bus_error *reterr_error,
MountOptions **ret_options,
char **ret_format_str,
char **in_out_format_str,
const char *separator) {
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
@ -279,7 +280,6 @@ 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,19 +295,21 @@ int bus_read_mount_options(
if (!escaped)
return -ENOMEM;
if (!isempty(escaped)) {
if (!strextend_with_separator(&format_str, separator, partition, ":", escaped))
return -ENOMEM;
o = new(MountOptions, 1);
if (!o)
if (!options) {
options = new0(MountOptions, 1);
if (!options)
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));
}
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]);
}
if (r < 0)
return r;
@ -317,13 +319,9 @@ int bus_read_mount_options(
return r;
if (options) {
if (ret_format_str) {
char *final = strjoin(*ret_format_str, !isempty(*ret_format_str) ? separator : "", format_str);
if (!final)
if (in_out_format_str && !strextend_with_separator(in_out_format_str, separator, format_str))
return -ENOMEM;
free_and_replace(*ret_format_str, final);
}
LIST_JOIN(mount_options, *ret_options, options);
*ret_options = TAKE_PTR(options);
}
return 0;

View File

@ -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 **ret_format_str, const char *separator);
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_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);

View File

@ -4866,6 +4866,32 @@ 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,
@ -5550,6 +5576,16 @@ 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;

View File

@ -1547,6 +1547,77 @@ 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;
@ -1594,22 +1665,9 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
if (c->root_image_options) {
_cleanup_free_ char *options = NULL;
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_mount_options(c->root_image_options, &options);
if (r < 0)
return r;
r = serialize_item(f, "exec-context-root-image-options", options);
if (r < 0)
@ -1664,6 +1722,10 @@ 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;
@ -2398,23 +2460,9 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
if (!s)
return log_oom_debug();
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_mount_options(mount->mount_options, &s);
if (r < 0)
return r;
r = serialize_item(f, "exec-context-mount-image", s);
if (r < 0)
@ -2433,23 +2481,9 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
if (!s)
return log_oom_debug();
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_mount_options(mount->mount_options, &s);
if (r < 0)
return r;
r = serialize_item(f, "exec-context-extension-image", s);
if (r < 0)
@ -2565,38 +2599,13 @@ 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="))) {
for (;;) {
_cleanup_free_ char *word = NULL, *mount_options = NULL, *partition = NULL;
PartitionDesignator partition_designator;
MountOptions *o = NULL;
const char *p;
_cleanup_(mount_options_free_allp) MountOptions *options = NULL;
r = extract_first_word(&val, &word, NULL, 0);
r = deserialize_mount_options(val, &options);
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;
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);
}
free_and_replace_full(c->root_image_options, options, mount_options_free_all);
} else if ((val = startswith(l, "exec-context-root-verity="))) {
r = free_and_strdup(&c->root_verity, val);
if (r < 0)
@ -2649,6 +2658,10 @@ 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)
@ -3538,54 +3551,9 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
if (isempty(destination))
continue;
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);
r = deserialize_mount_options(val, &options);
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) {
@ -3618,54 +3586,9 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
s++;
}
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);
r = deserialize_mount_options(val, &options);
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) {

View File

@ -1117,7 +1117,8 @@ 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",
"%sPrivateBPF: %s\n"
"%sMemoryTHP: %s\n",
prefix, c->umask,
prefix, empty_to_root(c->working_directory),
prefix, empty_to_root(c->root_directory),
@ -1145,7 +1146,8 @@ 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, private_bpf_to_string(c->private_bpf),
prefix, memory_thp_to_string(c->memory_thp));
if (c->private_bpf == PRIVATE_BPF_YES) {
_cleanup_free_ char
@ -1167,13 +1169,10 @@ 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) {
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");
_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);
}
if (iovec_is_set(&c->root_hash)) {
@ -1580,10 +1579,12 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
mount->ignore_enoent ? "-": "",
mount->source,
mount->destination);
LIST_FOREACH(mount_options, o, mount->mount_options)
fprintf(f, ":%s:%s",
partition_designator_to_string(o->partition_designator),
strempty(o->options));
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);
}
fprintf(f, "\n");
}
@ -1591,10 +1592,12 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
fprintf(f, "%sExtensionImages: %s%s", prefix,
mount->ignore_enoent ? "-": "",
mount->source);
LIST_FOREACH(mount_options, o, mount->mount_options)
fprintf(f, ":%s:%s",
partition_designator_to_string(o->partition_designator),
strempty(o->options));
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);
}
fprintf(f, "\n");
}

View File

@ -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;
LIST_HEAD(MountOptions, root_image_options);
MountOptions *root_image_options;
bool root_ephemeral;
bool working_directory_missing_ok:1;
bool working_directory_home:1;
@ -311,6 +311,7 @@ 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(). */

View File

@ -195,6 +195,7 @@
{% 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) -%}

View File

@ -163,6 +163,7 @@ 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;
@ -1662,7 +1663,6 @@ 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,23 +1686,12 @@ int config_parse_root_image_options(
continue;
}
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));
r = mount_options_set_and_consume(&options, partition_designator, TAKE_PTR(mount_options_resolved));
if (r < 0)
return r;
}
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;
return free_and_replace_full(c->root_image_options, options, mount_options_free_all);
}
int config_parse_exec_root_hash(
@ -5272,7 +5261,6 @@ 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);
@ -5292,14 +5280,9 @@ int config_parse_mount_images(
continue;
}
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);
r = mount_options_set_and_consume(&options, PARTITION_ROOT, TAKE_PTR(mount_options_resolved));
if (r < 0)
return r;
break;
}
@ -5316,14 +5299,9 @@ int config_parse_mount_images(
continue;
}
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_options_set_and_consume(&options, partition_designator, TAKE_PTR(mount_options_resolved));
if (r < 0)
return r;
}
r = mount_image_add(&c->mount_images, &c->n_mount_images,
@ -5414,7 +5392,6 @@ 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);
@ -5434,14 +5411,12 @@ int config_parse_extension_images(
continue;
}
o = new(MountOptions, 1);
if (!o)
if (!options) {
options = new0(MountOptions, 1);
if (!options)
return log_oom();
*o = (MountOptions) {
.partition_designator = PARTITION_ROOT,
.options = TAKE_PTR(mount_options_resolved),
};
LIST_APPEND(mount_options, options, o);
}
free_and_replace(options->options[PARTITION_ROOT], mount_options_resolved);
break;
}
@ -5457,14 +5432,12 @@ int config_parse_extension_images(
continue;
}
o = new(MountOptions, 1);
if (!o)
if (!options) {
options = new0(MountOptions, 1);
if (!options)
return log_oom();
*o = (MountOptions) {
.partition_designator = partition_designator,
.options = TAKE_PTR(mount_options_resolved),
};
LIST_APPEND(mount_options, options, o);
}
free_and_replace(options->options[partition_designator], mount_options_resolved);
}
r = mount_image_add(&c->extension_images, &c->n_extension_images,

View File

@ -168,6 +168,7 @@ 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);

View File

@ -27,7 +27,6 @@
#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"
@ -117,7 +116,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;
LIST_HEAD(MountOptions, image_options_const);
MountOptions *image_options_const;
char **overlay_layers;
VeritySettings verity;
ImageClass filter_class; /* Used for live updates to skip inapplicable images */
@ -3128,7 +3127,8 @@ 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 *options = NULL;
_cleanup_(mount_options_free_allp) MountOptions *o = NULL;
int r;
assert(m);
assert(n);
@ -3144,21 +3144,10 @@ int mount_image_add(MountImage **m, size_t *n, const MountImage *item) {
return -ENOMEM;
}
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 (item->mount_options) {
r = mount_options_dup(item->mount_options, &o);
if (r < 0)
return r;
}
if (!GREEDY_REALLOC(*m, *n + 1))
@ -3167,7 +3156,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(options),
.mount_options = TAKE_PTR(o),
.ignore_enoent = item->ignore_enoent,
.type = item->type,
};
@ -4047,6 +4036,15 @@ 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);

View File

@ -6,7 +6,6 @@
***/
#include "core-forward.h"
#include "list.h"
#include "runtime-scope.h"
typedef enum ProtectHome {
@ -91,6 +90,24 @@ 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;
@ -120,7 +137,7 @@ typedef enum MountImageType {
typedef struct MountImage {
char *source;
char *destination; /* Unused if MountImageType == MOUNT_IMAGE_EXTENSION */
LIST_HEAD(MountOptions, mount_options);
MountOptions *mount_options;
bool ignore_enoent;
MountImageType type;
} MountImage;
@ -233,6 +250,8 @@ 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);

View File

@ -45,27 +45,43 @@ 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 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;
static int json_append_mount_options(sd_json_variant **v, MountOptions *options) {
int r;
assert(ret);
assert(name);
assert(v);
if (!options)
return 0;
for (PartitionDesignator j = 0; j < _PARTITION_DESIGNATOR_MAX; j++) {
if (!options->options[j])
continue;
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));
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;
}
*ret = TAKE_PTR(v);
return 0;
}
static int root_image_options_build_json(sd_json_variant **ret, const char *name, void *userdata) {
MountOptions *root_image_options = userdata;
assert(ret);
assert(name);
if (!root_image_options) {
*ret = NULL;
return 0;
}
return json_append_mount_options(ret, root_image_options);
}
static int image_policy_build_json(sd_json_variant **ret, const char *name, void *userdata) {
_cleanup_free_ char *s = NULL;
ImagePolicy *policy = userdata;
@ -119,14 +135,9 @@ 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;
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));
r = json_append_mount_options(&mo, i->mount_options);
if (r < 0)
return r;
}
r = sd_json_variant_append_arraybo(
&v,
@ -153,14 +164,9 @@ 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;
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));
r = json_append_mount_options(&mo, i->mount_options);
if (r < 0)
return r;
}
r = sd_json_variant_append_arraybo(
&v,
@ -908,6 +914,7 @@ 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),

View File

@ -17,3 +17,7 @@
#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

View File

@ -5,7 +5,6 @@ executables += [
'name' : 'systemd-rc-local-generator',
'sources' : files('rc-local-generator.c'),
'conditions' : [
'HAVE_SYSV_COMPAT',
'HAVE_SYSV_RC_LOCAL',
],
},

View File

@ -2494,6 +2494,7 @@ 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 },

View File

@ -4619,23 +4619,78 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
return k >= 0 && image->partitions[k].found;
}
MountOptions* mount_options_free_all(MountOptions *options) {
MountOptions *m;
int mount_options_set_and_consume(MountOptions **options, PartitionDesignator d, char *s) {
assert(options);
assert(d >= 0);
while ((m = LIST_POP(mount_options, options))) {
free(m->options);
free(m);
if (!*options) {
*options = new0(MountOptions, 1);
if (!*options) {
free(s);
return log_oom();
}
}
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);
}
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator) {
LIST_FOREACH(mount_options, m, options)
if (designator == m->partition_designator && !isempty(m->options))
return m->options;
assert(designator >= 0 && designator < _PARTITION_DESIGNATOR_MAX);
if (!options)
return NULL;
return options->options[designator];
}
int mount_image_privately_interactively(
@ -4754,8 +4809,7 @@ 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(options->options, ",", "x-systemd.relax-extension-release-check");
string_contains_word(mount_options_from_designator(options, PARTITION_USR), ",", "x-systemd.relax-extension-release-check");
}
int verity_dissect_and_mount(

View File

@ -6,7 +6,6 @@
#include "architecture.h"
#include "gpt.h"
#include "iovec-util.h"
#include "list.h"
#include "shared-forward.h"
typedef struct DecryptedImage DecryptedImage;
@ -114,9 +113,7 @@ typedef struct DissectedImage {
} DissectedImage;
typedef struct MountOptions {
PartitionDesignator partition_designator;
char *options;
LIST_FIELDS(MountOptions, mount_options);
char *options[_PARTITION_DESIGNATOR_MAX];
} MountOptions;
typedef struct VeritySettings {
@ -154,6 +151,9 @@ 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) {

View File

@ -71,6 +71,7 @@ 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 },

View File

@ -70,6 +70,7 @@ 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 */
};

View File

@ -626,6 +626,8 @@ 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 */

View File

@ -36,7 +36,6 @@ systemctl_extract_sources = files(
'systemctl-daemon-reload.c',
'systemctl-logind.c',
'systemctl-start-unit.c',
'systemctl-sysv-compat.c',
'systemctl-util.c',
'systemctl.c',
)

View File

@ -19,7 +19,6 @@
#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"
@ -97,10 +96,6 @@ 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)

View File

@ -12,7 +12,6 @@
#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) {
@ -68,20 +67,13 @@ 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, enabled;
bool not_found = true, enabled = false;
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;

View File

@ -1,167 +0,0 @@
/* 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;
}

View File

@ -1,10 +0,0 @@
/* 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);

View File

@ -1,51 +0,0 @@
#!/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

View File

@ -1,9 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
executables += [
generator_template + {
'name' : 'systemd-sysv-generator',
'conditions' : ['HAVE_SYSV_COMPAT'],
'sources' : files('sysv-generator.c'),
},
]

View File

@ -1,937 +0,0 @@
/* 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);

View File

@ -528,6 +528,11 @@ 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'),
},

56
src/test/test-thp.c Normal file
View File

@ -0,0 +1,56 @@
/* 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();
}

View File

@ -65,16 +65,6 @@ 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',

View File

@ -1,413 +0,0 @@
#!/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))

View File

@ -0,0 +1,25 @@
#!/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

View File

@ -449,118 +449,6 @@ 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]

View File

@ -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:nosuid,dev home:ro,dev ro,noatime" \
-p RootImageOptions="root:noatime,dev home:ro,dev ro,nosuid" \
mount | grep -F "squashfs" | grep -F "nosuid" >/dev/null
systemd-run -P \
-p RootImage="$MINIMAL_IMAGE.gpt" \
-p RootImageOptions="root:ro,noatime root:ro,dev" \
-p RootImageOptions="root:ro,dev root:ro,noatime" \
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:ro,noatime home:ro,dev relatime,dev
RootImageOptions=nosuid,dev
RootImageOptions=root:relatime,dev home:ro,dev nosuid,dev
RootImageOptions=ro,noatime
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=root:ro,noatime,nosuid home:ro,dev nosuid,dev
RootImageOptions=home:ro,dev nosuid,dev,%%foo
RootImageOptions=nosuid,dev home:ro,dev nosuid,dev
RootImageOptions=home:ro,dev,%%foo root:ro,noatime,nosuid
# 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 "nosuid,dev,%foo"
org.freedesktop.systemd1.Service RootImageOptions | grep -F "ro,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 \

View File

@ -16,9 +16,3 @@ 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 %}

View File

@ -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_COMPAT %}
{% if HAVE_SYSV_RC_LOCAL %}
After=rc-local.service
{% endif %}
Before=getty.target

View File

@ -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_COMPAT %}
{% if HAVE_SYSV_RC_LOCAL %}
After=rc-local.service
{% endif %}
Before=getty.target

View File

@ -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_COMPAT %}
{% if HAVE_SYSV_RC_LOCAL %}
After=rc-local.service
{% endif %}

View File

@ -176,7 +176,7 @@ units = [
},
{
'file' : 'rc-local.service.in',
'conditions' : ['HAVE_SYSV_COMPAT'],
'conditions' : ['HAVE_SYSV_RC_LOCAL'],
},
{
'file' : 'reboot.target',

View File

@ -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_COMPAT %}
{% if HAVE_SYSV_RC_LOCAL %}
After=rc-local.service
{% endif %}