summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clusterfuzzlite/Dockerfile5
-rw-r--r--.github/dependabot.yml7
-rw-r--r--.github/workflows/cflite_pr.yml39
-rw-r--r--hwdb.d/60-evdev.hwdb18
-rw-r--r--hwdb.d/60-input-id.hwdb4
-rw-r--r--hwdb.d/70-mouse.hwdb11
-rw-r--r--hwdb.d/70-pda.hwdb39
-rw-r--r--hwdb.d/meson.build1
-rwxr-xr-xhwdb.d/parse_hwdb.py3
-rw-r--r--man/rules/meson.build2
-rw-r--r--man/systemd-networkd-wait-online.service.xml13
-rw-r--r--man/systemd.network.xml76
-rw-r--r--meson.build36
-rw-r--r--src/basic/filesystems-gperf.gperf4
-rw-r--r--src/basic/log.h6
-rw-r--r--src/basic/missing_magic.h11
-rw-r--r--src/core/bpf-devices.c2
-rw-r--r--src/core/bpf-firewall.c21
-rw-r--r--src/core/bpf/meson.build24
-rw-r--r--src/core/execute.c2
-rw-r--r--src/core/main.c2
-rw-r--r--src/core/namespace.c4
-rw-r--r--src/fstab-generator/fstab-generator.c4
-rw-r--r--src/libsystemd-network/fuzz-dhcp-client.c79
-rw-r--r--src/libsystemd-network/meson.build4
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c8
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c3
-rw-r--r--src/libsystemd/sd-device/sd-device.c7
-rw-r--r--src/login/logind-action.c135
-rw-r--r--src/login/logind-action.h20
-rw-r--r--src/login/logind-button.c3
-rw-r--r--src/login/logind-dbus.c508
-rw-r--r--src/login/logind-dbus.h3
-rw-r--r--src/login/logind-utmp.c10
-rw-r--r--src/login/logind.c2
-rw-r--r--src/login/logind.h10
-rw-r--r--src/network/networkd-link.c10
-rw-r--r--src/resolve/test-resolved-stream.c2
-rw-r--r--src/shared/copy.c6
-rw-r--r--src/shared/main-func.h1
-rw-r--r--src/shared/selinux-util.c2
-rw-r--r--src/shared/smack-util.c4
-rw-r--r--src/systemctl/systemctl-compat-halt.c47
-rw-r--r--src/systemctl/systemctl-logind.c2
-rw-r--r--src/systemctl/systemctl-start-special.c4
-rw-r--r--src/test/test-load-fragment.c2
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/test.sh19
l---------test/TEST-69-SHUTDOWN/Makefile1
-rwxr-xr-xtest/TEST-69-SHUTDOWN/test.sh33
-rw-r--r--test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50abin0 -> 243 bytes
-rw-r--r--test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980bin0 -> 243 bytes
-rw-r--r--test/fuzz/fuzz-dhcp-server-relay-message/clusterfuzz-testcase-minimized-fuzz-dhcp-server-relay-message-4972399731277824bin0 -> 65508 bytes
-rw-r--r--test/test-functions36
-rwxr-xr-xtest/test-network/systemd-networkd-tests.py38
-rwxr-xr-xtest/test-shutdown.py114
-rwxr-xr-xtest/units/testsuite-29.sh45
-rwxr-xr-xtest/units/testsuite-46.sh7
-rwxr-xr-xtest/units/testsuite-50.sh6
-rwxr-xr-xtest/units/testsuite-64.sh12
-rw-r--r--test/units/testsuite-69.service7
-rwxr-xr-xtools/oss-fuzz.sh16
-rw-r--r--units/meson.build1
-rw-r--r--units/systemd-networkd-wait-online@.service.in25
63 files changed, 1071 insertions, 495 deletions
diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile
new file mode 100644
index 0000000000..47f238c785
--- /dev/null
+++ b/.clusterfuzzlite/Dockerfile
@@ -0,0 +1,5 @@
+FROM gcr.io/oss-fuzz-base/base-builder@sha256:14b332de0e18683f37386eaedbf735bc6e8d81f9c0e1138d620f2178e20cd30a
+ENV MERGE_WITH_OSS_FUZZ_CORPORA=yes
+COPY . $SRC/systemd
+WORKDIR $SRC/systemd
+COPY tools/oss-fuzz.sh $SRC/build.sh
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index a23d6374c1..3e067c176f 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -5,10 +5,15 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
- interval: "weekly"
+ interval: "monthly"
open-pull-requests-limit: 2
- package-ecosystem: "pip"
directory: "/.github/workflows"
schedule:
interval: "monthly"
open-pull-requests-limit: 2
+ - package-ecosystem: "docker"
+ directory: "/.clusterfuzzlite"
+ schedule:
+ interval: "monthly"
+ open-pull-requests-limit: 2
diff --git a/.github/workflows/cflite_pr.yml b/.github/workflows/cflite_pr.yml
new file mode 100644
index 0000000000..3fe2bac618
--- /dev/null
+++ b/.github/workflows/cflite_pr.yml
@@ -0,0 +1,39 @@
+---
+# vi: ts=2 sw=2 et:
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+name: ClusterFuzzLite PR fuzzing
+on:
+ pull_request:
+ branches:
+ - main
+ - v[0-9]+-stable
+
+permissions: read-all
+
+jobs:
+ PR:
+ runs-on: ubuntu-latest
+ if: github.repository != 'systemd/systemd' || github.event.pull_request.user.login == 'dependabot[bot]'
+ concurrency:
+ group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }}
+ cancel-in-progress: true
+ strategy:
+ fail-fast: false
+ matrix:
+ sanitizer: [address, undefined, memory]
+ steps:
+ - name: Build Fuzzers (${{ matrix.sanitizer }})
+ id: build
+ uses: google/clusterfuzzlite/actions/build_fuzzers@41dccd0566905e2a7d1724e7883edbfa66d78877
+ with:
+ sanitizer: ${{ matrix.sanitizer }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: Run Fuzzers (${{ matrix.sanitizer }})
+ id: run
+ uses: google/clusterfuzzlite/actions/run_fuzzers@41dccd0566905e2a7d1724e7883edbfa66d78877
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ fuzz-seconds: 1200
+ mode: 'code-change'
+ sanitizer: ${{ matrix.sanitizer }}
diff --git a/hwdb.d/60-evdev.hwdb b/hwdb.d/60-evdev.hwdb
index f896dde357..9fcb4a3ddf 100644
--- a/hwdb.d/60-evdev.hwdb
+++ b/hwdb.d/60-evdev.hwdb
@@ -592,6 +592,24 @@ evdev:name:MSFT0001:02 04F3:304B Touchpad:dmi:*svnLENOVO:*pvrLenovoLegionY9000X2
EVDEV_ABS_36=::30
#########################################
+# Microsoft
+#########################################
+
+# Surface Laptop 2 (13")
+evdev:name:Microsoft Surface 045E:0933 Touchpad:dmi:*svnMicrosoftCorporation:*pnSurfaceLaptop2**
+ EVDEV_ABS_00=::38
+ EVDEV_ABS_01=::38
+ EVDEV_ABS_35=::38
+ EVDEV_ABS_36=::38
+
+# Surface Laptop 3 (15")
+evdev:name:Microsoft Surface 045E:09AF Touchpad:dmi:*svnMicrosoftCorporation:*pnSurfaceLaptop3**
+ EVDEV_ABS_00=::39
+ EVDEV_ABS_01=::37
+ EVDEV_ABS_35=::39
+ EVDEV_ABS_36=::37
+
+#########################################
# NEWYES
#########################################
diff --git a/hwdb.d/60-input-id.hwdb b/hwdb.d/60-input-id.hwdb
index f7b61fb980..2d5681dea6 100644
--- a/hwdb.d/60-input-id.hwdb
+++ b/hwdb.d/60-input-id.hwdb
@@ -72,6 +72,10 @@ id-input:modalias:input:b0005v046DpB00De0700*
id-input:modalias:input:b0003v046Dp408Ae0111*
ID_INPUT_MOUSE=0
+# Logitech Craft Keyboard
+id-input:modalias:input:b0003v046Dp4066e0111*
+ ID_INPUT_MOUSE=0
+
# CH Products Pro Pedals
id-input:modalias:input:b0003v068Ep00F2e0100*
ID_INPUT_ACCELEROMETER=0
diff --git a/hwdb.d/70-mouse.hwdb b/hwdb.d/70-mouse.hwdb
index 6302312456..66e74f667c 100644
--- a/hwdb.d/70-mouse.hwdb
+++ b/hwdb.d/70-mouse.hwdb
@@ -198,6 +198,17 @@ mouse:usb:v0461p4d46:name:USB Optical Mouse:*
mouse:usb:v056ep010d:name:ELECOM TrackBall Mouse HUGE TrackBall:*
MOUSE_DPI=500@125 *1000@125 1500@125
+# Elecom DEFT Pro TrackBall (M-DPT1MR)
+mouse:usb:v056ep0131:name:ELECOM TrackBall Mouse DEFT Pro TrackBall Mouse:*
+ MOUSE_DPI=*500 1000 1500
+ MOUSE_WHEEL_CLICK_ANGLE=10
+
+# Elecom Relacon (M-RT1DR)
+mouse:usb:v056ep0155:name:ELECOM ELECOM Relacon:*
+ ID_INPUT_TRACKBALL=1
+ MOUSE_DPI=*500 1000 1500
+ MOUSE_WHEEL_CLICK_ANGLE=30
+
##########################################
# Fujitsu Siemens
##########################################
diff --git a/hwdb.d/70-pda.hwdb b/hwdb.d/70-pda.hwdb
new file mode 100644
index 0000000000..e122acca6f
--- /dev/null
+++ b/hwdb.d/70-pda.hwdb
@@ -0,0 +1,39 @@
+# This file is part of systemd.
+#
+# Database for handhelds (PDAs, calculators, etc.) that should be accessible
+# the seat owner.
+#
+# Permitted keys:
+# Specify if a device is a signal analyzer
+# ID_PDA=1|0
+
+###########################################################
+# Texas Instruments
+###########################################################
+# SilverLink
+usb:v0451pE001*
+ ID_PDA=1
+
+# TI-84 Plus DirectLink
+usb:v0451pE003*
+ ID_PDA=1
+
+# TI-89 Titanium DirectLink
+usb:v0451pE004*
+ ID_PDA=1
+
+# TI-84 Plus Silver Edition DirectLink
+usb:v0451pE008*
+ ID_PDA=1
+
+# TI-Nspire DirectLink
+usb:v0451pE012*
+ ID_PDA=1
+
+# TI-Nspire Lab Cradle
+usb:v0451pE01C*
+ ID_PDA=1
+
+# TI-Nspire CX II DirectLink
+usb:v0451pE022*
+ ID_PDA=1
diff --git a/hwdb.d/meson.build b/hwdb.d/meson.build
index 8ff044131c..fc72ebb2bd 100644
--- a/hwdb.d/meson.build
+++ b/hwdb.d/meson.build
@@ -31,6 +31,7 @@ hwdb_files_test = files('''
70-cameras.hwdb
70-joystick.hwdb
70-mouse.hwdb
+ 70-pda.hwdb
70-pointingstick.hwdb
70-touchpad.hwdb
80-ieee1394-unit-function.hwdb
diff --git a/hwdb.d/parse_hwdb.py b/hwdb.d/parse_hwdb.py
index 0268bf9580..194a71ac08 100755
--- a/hwdb.d/parse_hwdb.py
+++ b/hwdb.d/parse_hwdb.py
@@ -121,7 +121,7 @@ def hwdb_grammar():
def property_grammar():
ParserElement.setDefaultWhitespaceChars(' ')
- dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ'))('SETTINGS*')
+ dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Optional(Suppress('@') + INTEGER('HZ')))('SETTINGS*')
mount_matrix_row = SIGNED_REAL + ',' + SIGNED_REAL + ',' + SIGNED_REAL
mount_matrix = Group(mount_matrix_row + ';' + mount_matrix_row + ';' + mount_matrix_row)('MOUNT_MATRIX')
xkb_setting = Optional(Word(alphanums + '+-/@._'))
@@ -136,6 +136,7 @@ def property_grammar():
('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER),
('ID_AUTOSUSPEND', Or((Literal('0'), Literal('1')))),
('ID_PERSIST', Or((Literal('0'), Literal('1')))),
+ ('ID_PDA', Or((Literal('0'), Literal('1')))),
('ID_INPUT', Or((Literal('0'), Literal('1')))),
('ID_INPUT_ACCELEROMETER', Or((Literal('0'), Literal('1')))),
('ID_INPUT_JOYSTICK', Or((Literal('0'), Literal('1')))),
diff --git a/man/rules/meson.build b/man/rules/meson.build
index b689b1c1af..7200ecf681 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -932,7 +932,7 @@ manpages = [
['systemd-network-generator.service', '8', ['systemd-network-generator'], ''],
['systemd-networkd-wait-online.service',
'8',
- ['systemd-networkd-wait-online'],
+ ['systemd-networkd-wait-online', 'systemd-networkd-wait-online@.service'],
'ENABLE_NETWORKD'],
['systemd-networkd.service', '8', ['systemd-networkd'], 'ENABLE_NETWORKD'],
['systemd-notify', '1', [], ''],
diff --git a/man/systemd-networkd-wait-online.service.xml b/man/systemd-networkd-wait-online.service.xml
index 9c6b02ac1c..a3a70db209 100644
--- a/man/systemd-networkd-wait-online.service.xml
+++ b/man/systemd-networkd-wait-online.service.xml
@@ -18,12 +18,14 @@
<refnamediv>
<refname>systemd-networkd-wait-online.service</refname>
+ <refname>systemd-networkd-wait-online@.service</refname>
<refname>systemd-networkd-wait-online</refname>
<refpurpose>Wait for network to come online</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-networkd-wait-online.service</filename></para>
+ <para><filename>systemd-networkd-wait-online@.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-networkd-wait-online</filename></para>
</refsynopsisdiv>
@@ -38,6 +40,17 @@
to be fully configured or failed, and for at least one link to be online. Here, online means that
the link's operational state is equal or higher than <literal>degraded</literal>. The threshold
can be configured by <option>--operational-state=</option> option.</para>
+
+ <para>The service <filename>systemd-networkd-wait-online.service</filename> invokes
+ <command>systemd-networkd-wait-online</command> without any options. Thus, it waits for all managed
+ interfaces to be configured or failed, and for at least one to be online.</para>
+
+ <para>The service <filename>systemd-networkd-wait-online@.service</filename> takes an interface
+ name, and invokes <command>systemd-networkd-wait-online</command> with <option>-i</option> and the
+ specified interface name. Thus, wait for the specified interface to be configured and online. For
+ example, <filename>systemd-networkd-wait-online@eth0.service</filename> waits for
+ <filename>eth0</filename> to be configured by <command>systemd-networkd</command> and online.
+ </para>
</refsect1>
<refsect1>
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 4f41394682..44be11de19 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -4410,22 +4410,48 @@ DHCP=yes</programlisting>
</example>
<example>
- <title>IPv6 Prefix Delegation</title>
+ <title>IPv6 Prefix Delegation (DHCPv6 PD)</title>
- <programlisting># /etc/systemd/network/55-ipv6-pd-upstream.network
+ <programlisting># /etc/systemd/network/55-dhcpv6-pd-upstream.network
[Match]
Name=enp1s0
[Network]
-DHCP=ipv6</programlisting>
+DHCP=ipv6
+
+# The below setting is optional, to also assign an address in the delegated prefix
+# to the upstream interface. If not necessary, then comment out the line below and
+# the [DHCPPrefixDelegation] section.
+DHCPPrefixDelegation=yes
+
+# If the upstream network provides Router Advertisement with Managed bit set,
+# then comment out the line below and WithoutRA= setting in the [DHCPv6] section.
+IPv6AcceptRA=no
+
+[DHCPv6]
+WithoutRA=solicit
- <programlisting># /etc/systemd/network/56-ipv6-pd-downstream.network
+[DHCPPrefixDelegation]
+UplinkInterface=:self
+SubnetId=0
+Announce=no</programlisting>
+
+ <programlisting># /etc/systemd/network/55-dhcpv6-pd-downstream.network
[Match]
Name=enp2s0
[Network]
+DHCPPrefixDelegation=yes
IPv6SendRA=yes
-DHCPPrefixDelegation=yes</programlisting>
+
+# It is expected that the host is acting as a router. So, usually it is not
+# necessary to receive Router Advertisement from other hosts in the downstream network.
+IPv6AcceptRA=no
+
+[DHCPPrefixDelegation]
+UplinkInterface=enp1s0
+SubnetId=1
+Announce=yes</programlisting>
<para>This will enable DHCPv6-PD on the interface enp1s0 as an upstream interface where the
DHCPv6 client is running and enp2s0 as a downstream interface where the prefix is delegated to.
@@ -4434,6 +4460,46 @@ DHCPPrefixDelegation=yes</programlisting>
</example>
<example>
+ <title>IPv6 Prefix Delegation (DHCPv4 6RD)</title>
+
+ <programlisting># /etc/systemd/network/55-dhcpv4-6rd-upstream.network
+[Match]
+Name=enp1s0
+
+[Network]
+DHCP=ipv4
+
+# When DHCPv4-6RD is used, the upstream network does not support IPv6.
+# Hence, it is not necessary to wait for Router Advertisement, which is enabled by default.
+IPv6AcceptRA=no
+
+[DHCPv4]
+Use6RD=yes</programlisting>
+
+ <programlisting># /etc/systemd/network/55-dhcpv4-6rd-downstream.network
+[Match]
+Name=enp2s0
+
+[Network]
+DHCPPrefixDelegation=yes
+IPv6SendRA=yes
+
+# It is expected that the host is acting as a router. So, usually it is not
+# necessary to receive Router Advertisement from other hosts in the downstream network.
+IPv6AcceptRA=no
+
+[DHCPPrefixDelegation]
+UplinkInterface=enp1s0
+SubnetId=1
+Announce=yes</programlisting>
+
+ <para>This will enable DHCPv4-6RD on the interface enp1s0 as an upstream interface where the
+ DHCPv4 client is running and enp2s0 as a downstream interface where the prefix is delegated to.
+ The delegated prefixes are distributed by IPv6 Router Advertisement on the downstream network.
+ </para>
+ </example>
+
+ <example>
<title>A bridge with two enslaved links</title>
<programlisting># /etc/systemd/network/25-bridge-static.network
diff --git a/meson.build b/meson.build
index 9faa6583a0..b8b01e0deb 100644
--- a/meson.build
+++ b/meson.build
@@ -988,7 +988,7 @@ else
# Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu
# (like clang-10/llvm-strip-10)
if meson.is_cross_build() or cc.get_id() != 'clang' or cc.cmd_array()[0].contains('afl-clang') or cc.cmd_array()[0].contains('hfuzz-clang')
- r = find_program('clang', required : bpf_framework_required)
+ r = find_program('clang', required : bpf_framework_required, version : '>= 10.0.0')
clang_found = r.found()
if clang_found
if meson.version().version_compare('>= 0.55')
@@ -1012,23 +1012,35 @@ else
clang_supports_bpf = false
endif
- if not meson.is_cross_build() and clang_found
- llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
- check : true).stdout().strip()
- else
- llvm_strip_bin = 'llvm-strip'
- endif
- llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required)
-
# Debian installs this in /usr/sbin/ which is not in $PATH.
# We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian.
# We use 'bpftool gen' subcommand, it was added by 985ead416df39d6fe8e89580cc1db6aa273e0175 (v5.6).
bpftool = find_program('bpftool',
'/usr/sbin/bpftool',
- required : bpf_framework_required,
- version : '>= 5.6')
+ required : false,
+ version : '>= 5.13.0')
+
+ if bpftool.found()
+ bpftool_strip = true
+ else
+ bpftool_strip = false
+ bpftool = find_program('bpftool',
+ '/usr/sbin/bpftool',
+ required : bpf_framework_required,
+ version : '>= 5.6.0')
+ endif
+
+ if not bpftool_strip
+ if not meson.is_cross_build() and clang_found
+ llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip',
+ check : true).stdout().strip()
+ else
+ llvm_strip_bin = 'llvm-strip'
+ endif
+ llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required, version : '>= 10.0.0')
+ endif
- deps_found = clang_found and clang_supports_bpf and clang_supports_flags and llvm_strip.found() and bpftool.found()
+ deps_found = clang_found and clang_supports_bpf and clang_supports_flags and (bpftool_strip or llvm_strip.found()) and bpftool.found()
# Can build BPF program from source code in restricted C
conf.set10('BPF_FRAMEWORK', deps_found)
diff --git a/src/basic/filesystems-gperf.gperf b/src/basic/filesystems-gperf.gperf
index 08c8c44510..e8c5357f91 100644
--- a/src/basic/filesystems-gperf.gperf
+++ b/src/basic/filesystems-gperf.gperf
@@ -40,7 +40,7 @@ ceph, {CEPH_SUPER_MAGIC}
cgroup2, {CGROUP2_SUPER_MAGIC}
# note that the cgroupfs magic got reassigned from cpuset
cgroup, {CGROUP_SUPER_MAGIC}
-cifs, {CIFS_MAGIC_NUMBER}
+cifs, {CIFS_SUPER_MAGIC, SMB2_SUPER_MAGIC}
coda, {CODA_SUPER_MAGIC}
configfs, {CONFIGFS_MAGIC}
cramfs, {CRAMFS_MAGIC}
@@ -109,7 +109,7 @@ selinuxfs, {SELINUX_MAGIC}
shiftfs, {SHIFTFS_MAGIC}
smackfs, {SMACK_MAGIC}
# smb3 is an alias for cifs
-smb3, {CIFS_MAGIC_NUMBER}
+smb3, {CIFS_SUPER_MAGIC}
# smbfs was removed from the kernel in 2010, the magic remains
smbfs, {SMB_SUPER_MAGIC}
sockfs, {SOCKFS_MAGIC}
diff --git a/src/basic/log.h b/src/basic/log.h
index 1e2bec1646..0d927bfce9 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -273,9 +273,11 @@ int log_emergency_level(void);
})
#if LOG_TRACE
-# define log_trace(...) log_debug(__VA_ARGS__)
+# define log_trace(...) log_debug(__VA_ARGS__)
+# define log_trace_errno(...) log_debug_errno(__VA_ARGS__)
#else
-# define log_trace(...) do {} while (0)
+# define log_trace(...) do {} while (0)
+# define log_trace_errno(e, ...) (-ERRNO_VALUE(e))
#endif
/* Structured logging */
diff --git a/src/basic/missing_magic.h b/src/basic/missing_magic.h
index 7d9320bb6d..c104fcfba3 100644
--- a/src/basic/missing_magic.h
+++ b/src/basic/missing_magic.h
@@ -38,9 +38,14 @@
#define XFS_SB_MAGIC 0x58465342
#endif
-/* Not exposed yet. Defined at fs/cifs/cifsglob.h */
-#ifndef CIFS_MAGIC_NUMBER
-#define CIFS_MAGIC_NUMBER 0xFF534D42
+/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
+#ifndef CIFS_SUPER_MAGIC
+#define CIFS_SUPER_MAGIC 0xFF534D42
+#endif
+
+/* dea2903719283c156b53741126228c4a1b40440f (5.17) */
+#ifndef SMB2_SUPER_MAGIC
+#define SMB2_SUPER_MAGIC 0xFE534D42
#endif
/* 257f871993474e2bde6c497b54022c362cf398e1 (4.5) */
diff --git a/src/core/bpf-devices.c b/src/core/bpf-devices.c
index e3100b862b..f62c6f1931 100644
--- a/src/core/bpf-devices.c
+++ b/src/core/bpf-devices.c
@@ -306,7 +306,7 @@ int bpf_devices_supported(void) {
return supported = 0;
}
- r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, NULL, &program);
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, "sd_devices", &program);
if (r < 0) {
log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m");
return supported = 0;
diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c
index 8158fafc8e..0297053add 100644
--- a/src/core/bpf-firewall.c
+++ b/src/core/bpf-firewall.c
@@ -145,6 +145,7 @@ static int add_instructions_for_ip_any(
static int bpf_firewall_compile_bpf(
Unit *u,
+ const char *prog_name,
bool is_ingress,
BPFProgram **ret,
bool ip_allow_any,
@@ -193,7 +194,6 @@ static int bpf_firewall_compile_bpf(
};
_cleanup_(bpf_program_freep) BPFProgram *p = NULL;
- const char *prog_name = is_ingress ? "sd_fw_ingress" : "sd_fw_egress";
int accounting_map_fd, r;
bool access_enabled;
@@ -527,9 +527,10 @@ static int bpf_firewall_prepare_accounting_maps(Unit *u, bool enabled, int *fd_i
}
int bpf_firewall_compile(Unit *u) {
+ const char *ingress_name = NULL, *egress_name = NULL;
+ bool ip_allow_any = false, ip_deny_any = false;
CGroupContext *cc;
int r, supported;
- bool ip_allow_any = false, ip_deny_any = false;
assert(u);
@@ -552,6 +553,13 @@ int bpf_firewall_compile(Unit *u) {
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
+ /* If BPF_F_ALLOW_MULTI flag is supported program name is also supported (both were added to v4.15
+ * kernel). */
+ if (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
+ ingress_name = "sd_fw_ingress";
+ egress_name = "sd_fw_egress";
+ }
+
/* Note that when we compile a new firewall we first flush out the access maps and the BPF programs themselves,
* but we reuse the accounting maps. That way the firewall in effect always maps to the actual
* configuration, but we don't flush out the accounting unnecessarily */
@@ -585,11 +593,11 @@ int bpf_firewall_compile(Unit *u) {
if (r < 0)
return log_unit_error_errno(u, r, "Preparation of eBPF accounting maps failed: %m");
- r = bpf_firewall_compile_bpf(u, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
+ r = bpf_firewall_compile_bpf(u, ingress_name, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "Compilation for ingress BPF program failed: %m");
- r = bpf_firewall_compile_bpf(u, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
+ r = bpf_firewall_compile_bpf(u, egress_name, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "Compilation for egress BPF program failed: %m");
@@ -826,6 +834,7 @@ int bpf_firewall_supported(void) {
return supported = BPF_FIREWALL_UNSUPPORTED;
}
+ /* prog_name is NULL since it is supported only starting from v4.15 kernel. */
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &program);
if (r < 0) {
bpf_firewall_unsupported_reason =
@@ -883,7 +892,9 @@ int bpf_firewall_supported(void) {
/* So now we know that the BPF program is generally available, let's see if BPF_F_ALLOW_MULTI is also supported
* (which was added in kernel 4.15). We use a similar logic as before, but this time we use the BPF_PROG_ATTACH
* bpf() call and the BPF_F_ALLOW_MULTI flags value. Since the flags are checked early in the system call we'll
- * get EINVAL if it's not supported, and EBADF as before if it is available. */
+ * get EINVAL if it's not supported, and EBADF as before if it is available.
+ * Use probe result as the indicator that program name is also supported since they both were
+ * added in kernel 4.15. */
zero(attr);
attr.attach_type = BPF_CGROUP_INET_EGRESS;
diff --git a/src/core/bpf/meson.build b/src/core/bpf/meson.build
index c2465a845f..57a4c5393c 100644
--- a/src/core/bpf/meson.build
+++ b/src/core/bpf/meson.build
@@ -65,13 +65,23 @@ bpf_o_unstripped_cmd += [
'@OUTPUT@'
]
-bpf_o_cmd = [
- llvm_strip,
- '-g',
- '@INPUT@',
- '-o',
- '@OUTPUT@'
-]
+if bpftool_strip
+ bpf_o_cmd = [
+ bpftool,
+ 'g',
+ 'o',
+ '@OUTPUT@',
+ '@INPUT@'
+ ]
+else
+ bpf_o_cmd = [
+ llvm_strip,
+ '-g',
+ '@INPUT@',
+ '-o',
+ '@OUTPUT@'
+ ]
+endif
skel_h_cmd = [
bpftool,
diff --git a/src/core/execute.c b/src/core/execute.c
index acc59ef9db..d3266a9ab5 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -3373,7 +3373,7 @@ static int compile_symlinks(
return r;
}
- if (!exec_directory_is_private(context, dt))
+ if (!exec_directory_is_private(context, dt) || exec_context_with_rootfs(context))
continue;
private_path = path_join(params->prefix[dt], "private", context->directories[dt].items[i].path);
diff --git a/src/core/main.c b/src/core/main.c
index 57aedb9b93..f347b74b11 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -2726,6 +2726,8 @@ int main(int argc, char *argv[]) {
Manager *m = NULL;
FDSet *fds = NULL;
+ assert_se(argc > 0 && !isempty(argv[0]));
+
/* SysV compatibility: redirect init → telinit */
redirect_telinit(argc, argv);
diff --git a/src/core/namespace.c b/src/core/namespace.c
index f3c6b58f86..e75e003e71 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -1482,7 +1482,7 @@ static int apply_one_mount(
(void) mkdir_parents(mount_entry_path(m), 0755);
q = make_mount_point_inode_from_path(what, mount_entry_path(m), 0755);
- if (q < 0)
+ if (q < 0 && q != -EEXIST)
log_error_errno(q, "Failed to create destination mount point node '%s': %m",
mount_entry_path(m));
else
@@ -1804,7 +1804,7 @@ static int apply_mounts(
* exist, which means this will be a no-op. */
r = create_symlinks_from_tuples(root, exec_dir_symlinks);
if (r < 0)
- return r;
+ return log_debug_errno(r, "Failed to set up ExecDirectories symlinks inside mount namespace: %m");
/* Create a deny list we can pass to bind_mount_recursive() */
deny_list = new(char*, (*n_mounts)+1);
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 9b32383a76..ca9b045e85 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -721,7 +721,7 @@ static int sysroot_is_nfsroot(void) {
if (!sep)
return -EINVAL;
- a = strndupa(arg_root_what + 1, sep - arg_root_what - 1);
+ a = strndupa_safe(arg_root_what + 1, sep - arg_root_what - 1);
r = in_addr_from_string(AF_INET6, a, &u);
if (r < 0)
@@ -733,7 +733,7 @@ static int sysroot_is_nfsroot(void) {
/* IPv4 address */
sep = strchr(arg_root_what, ':');
if (sep) {
- a = strndupa(arg_root_what, sep - arg_root_what);
+ a = strndupa_safe(arg_root_what, sep - arg_root_what);
if (in_addr_from_string(AF_INET, a, &u) >= 0)
return true;
diff --git a/src/libsystemd-network/fuzz-dhcp-client.c b/src/libsystemd-network/fuzz-dhcp-client.c
new file mode 100644
index 0000000000..1812a61950
--- /dev/null
+++ b/src/libsystemd-network/fuzz-dhcp-client.c
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "fuzz.h"
+#include "sd-event.h"
+
+#include "sd-dhcp-client.c"
+
+int dhcp_network_bind_raw_socket(
+ int ifindex,
+ union sockaddr_union *link,
+ uint32_t id,
+ const uint8_t *addr, size_t addr_len,
+ const uint8_t *bcaddr, size_t bcaddr_len,
+ uint16_t arp_type, uint16_t port) {
+
+ int fd;
+ fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) {
+ return len;
+}
+
+int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
+ int fd;
+
+ fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
+int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) {
+ return len;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'};
+ uint8_t bcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ int res, r;
+
+ if (!getenv("SYSTEMD_LOG_LEVEL"))
+ log_set_max_level(LOG_CRIT);
+
+ r = sd_dhcp_client_new(&client, false);
+ assert_se(r >= 0);
+ assert_se(client);
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ r = sd_dhcp_client_attach_event(client, e, 0);
+ assert_se(r >= 0);
+
+ assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0);
+ assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0);
+ dhcp_client_set_test_mode(client, true);
+
+ res = sd_dhcp_client_start(client);
+ assert_se(IN_SET(res, 0, -EINPROGRESS));
+ client->xid = 2;
+
+ (void) client_handle_offer(client, (DHCPMessage*) data, size);
+
+ assert_se(sd_dhcp_client_stop(client) >= 0);
+
+ return 0;
+}
diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build
index 3f5e11e7f5..853401d5be 100644
--- a/src/libsystemd-network/meson.build
+++ b/src/libsystemd-network/meson.build
@@ -105,6 +105,10 @@ tests += [
]
fuzzers += [
+ [files('fuzz-dhcp-client.c'),
+ [libshared,
+ libsystemd_network]],
+
[files('fuzz-dhcp6-client.c'),
[libshared,
libsystemd_network]],
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index ab131701fb..5a40eb94d3 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -492,10 +492,8 @@ static int lease_parse_routes(
route->option = SD_DHCP_OPTION_STATIC_ROUTE;
r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
- if (r < 0) {
- log_debug("Failed to determine destination prefix length from class based IP, ignoring");
- continue;
- }
+ if (r < 0)
+ return -EINVAL;
assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
@@ -907,7 +905,7 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d
pos = next_chunk;
}
- *domains = TAKE_PTR(names);
+ strv_free_and_replace(*domains, names);
return cnt;
}
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index ec9202d02e..1d27d28959 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -319,6 +319,9 @@ static int dhcp_server_send_unicast_raw(
memcpy(link.ll.sll_addr, chaddr, hlen);
+ if (len > UINT16_MAX)
+ return -EOVERFLOW;
+
dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
packet->dhcp.yiaddr,
DHCP_PORT_CLIENT, len, -1);
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index 92a7ededf6..27c91ea724 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -188,8 +188,11 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
path = strjoina(syspath, "/uevent");
if (access(path, F_OK) < 0) {
if (errno == ENOENT)
- /* this is not a valid device */
- return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+ /* This is not a valid device.
+ * Note, this condition is quite often satisfied when
+ * enumerating devices or finding a parent device.
+ * Hence, use log_trace_errno() here. */
+ return log_trace_errno(SYNTHETIC_ERRNO(ENODEV),
"sd-device: the uevent file \"%s\" does not exist.", path);
return log_debug_errno(errno, "sd-device: cannot access uevent file for %s: %m", syspath);
diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index e172910948..45f77bc090 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -2,6 +2,8 @@
#include <unistd.h>
+#include "sd-messages.h"
+
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
@@ -11,29 +13,119 @@
#include "logind-dbus.h"
#include "logind-session-dbus.h"
#include "process-util.h"
-#include "sleep-config.h"
#include "special.h"
#include "string-table.h"
#include "terminal-util.h"
#include "user-util.h"
+static const ActionTableItem action_table[_HANDLE_ACTION_MAX] = {
+ [HANDLE_POWEROFF] = {
+ SPECIAL_POWEROFF_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.power-off",
+ "org.freedesktop.login1.power-off-multiple-sessions",
+ "org.freedesktop.login1.power-off-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is powering down",
+ "power-off",
+ },
+ [HANDLE_REBOOT] = {
+ SPECIAL_REBOOT_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.reboot",
+ "org.freedesktop.login1.reboot-multiple-sessions",
+ "org.freedesktop.login1.reboot-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is rebooting",
+ "reboot",
+ },
+ [HANDLE_HALT] = {
+ SPECIAL_HALT_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.halt",
+ "org.freedesktop.login1.halt-multiple-sessions",
+ "org.freedesktop.login1.halt-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is halting",
+ "halt",
+ },
+ [HANDLE_KEXEC] = {
+ SPECIAL_KEXEC_TARGET,
+ INHIBIT_SHUTDOWN,
+ "org.freedesktop.login1.reboot",
+ "org.freedesktop.login1.reboot-multiple-sessions",
+ "org.freedesktop.login1.reboot-ignore-inhibit",
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_SHUTDOWN_STR,
+ "System is rebooting with kexec",
+ "kexec",
+ },
+ [HANDLE_SUSPEND] = {
+ SPECIAL_SUSPEND_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.suspend",
+ "org.freedesktop.login1.suspend-multiple-sessions",
+ "org.freedesktop.login1.suspend-ignore-inhibit",
+ SLEEP_SUSPEND,
+ },
+ [HANDLE_HIBERNATE] = {
+ SPECIAL_HIBERNATE_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ SLEEP_HIBERNATE,
+ },
+ [HANDLE_HYBRID_SLEEP] = {
+ SPECIAL_HYBRID_SLEEP_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ SLEEP_HYBRID_SLEEP,
+ },
+ [HANDLE_SUSPEND_THEN_HIBERNATE] = {
+ SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
+ INHIBIT_SLEEP,
+ "org.freedesktop.login1.hibernate",
+ "org.freedesktop.login1.hibernate-multiple-sessions",
+ "org.freedesktop.login1.hibernate-ignore-inhibit",
+ SLEEP_SUSPEND_THEN_HIBERNATE,
+ },
+ [HANDLE_FACTORY_RESET] = {
+ SPECIAL_FACTORY_RESET_TARGET,
+ _INHIBIT_WHAT_INVALID,
+ NULL,
+ NULL,
+ NULL,
+ _SLEEP_OPERATION_INVALID,
+ SD_MESSAGE_FACTORY_RESET_STR,
+ "System is performing factory reset",
+ NULL
+ },
+};
+
const char* manager_target_for_action(HandleAction handle) {
- static const char * const target_table[_HANDLE_ACTION_MAX] = {
- [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET,
- [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET,
- [HANDLE_HALT] = SPECIAL_HALT_TARGET,
- [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
- [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
- [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
- [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
- [HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
- [HANDLE_FACTORY_RESET] = SPECIAL_FACTORY_RESET_TARGET,
- };
+ assert(handle >= 0);
+ assert(handle < (ssize_t) ELEMENTSOF(action_table));
+ return action_table[handle].target;
+}
+
+const ActionTableItem* manager_item_for_handle(HandleAction handle) {
assert(handle >= 0);
- if (handle < (ssize_t) ELEMENTSOF(target_table))
- return target_table[handle];
- return NULL;
+ assert(handle < (ssize_t) ELEMENTSOF(action_table));
+
+ return &action_table[handle];
+}
+
+HandleAction manager_handle_for_item(const ActionTableItem* a) {
+ if (a && a < action_table + ELEMENTSOF(action_table))
+ return a - action_table;
+ return _HANDLE_ACTION_INVALID;
}
int manager_handle_action(
@@ -59,7 +151,6 @@ int manager_handle_action(
InhibitWhat inhibit_operation;
Inhibitor *offending = NULL;
bool supported;
- const char *target;
int r;
assert(m);
@@ -129,17 +220,13 @@ int manager_handle_action(
return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Requested %s operation not supported, ignoring.", handle_action_to_string(handle));
- if (m->action_what > 0)
+ if (m->delayed_action)
return log_debug_errno(SYNTHETIC_ERRNO(EALREADY),
"Action already in progress (%s), ignoring requested %s operation.",
- inhibit_what_to_string(m->action_what),
+ inhibit_what_to_string(m->delayed_action->inhibit_what),
handle_action_to_string(handle));
- assert_se(target = manager_target_for_action(handle));
-
- inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE,
- HANDLE_HYBRID_SLEEP,
- HANDLE_SUSPEND_THEN_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
+ inhibit_operation = manager_item_for_handle(handle)->inhibit_what;
/* If the actual operation is inhibited, warn and fail */
if (!ignore_inhibited &&
@@ -162,7 +249,7 @@ int manager_handle_action(
log_info("%s", message_table[handle]);
- r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error);
+ r = bus_manager_shutdown_or_sleep_now_or_later(m, manager_item_for_handle(handle), &error);
if (r < 0)
return log_error_errno(r, "Failed to execute %s operation: %s",
handle_action_to_string(handle),
diff --git a/src/login/logind-action.h b/src/login/logind-action.h
index ec2fece2b7..e6d3047743 100644
--- a/src/login/logind-action.h
+++ b/src/login/logind-action.h
@@ -19,8 +19,26 @@ typedef enum HandleAction {
_HANDLE_ACTION_INVALID = -EINVAL,
} HandleAction;
+typedef struct ActionTableItem ActionTableItem;
+
+#define handle_action_valid(x) (x && (x < _HANDLE_ACTION_MAX))
+
#include "logind-inhibit.h"
#include "logind.h"
+#include "sleep-config.h"
+
+struct ActionTableItem {
+ const char *target;
+ InhibitWhat inhibit_what;
+ const char *polkit_action;
+ const char *polkit_action_multiple_sessions;
+ const char *polkit_action_ignore_inhibit;
+ SleepOperation sleep_operation;
+ const char* message_id;
+ const char* message;
+ const char* log_str;
+
+};
int manager_handle_action(
Manager *m,
@@ -33,5 +51,7 @@ const char* handle_action_to_string(HandleAction h) _const_;
HandleAction handle_action_from_string(const char *s) _pure_;
const char* manager_target_for_action(HandleAction handle);
+const ActionTableItem* manager_item_for_handle(HandleAction handle);
+HandleAction manager_handle_for_item(const ActionTableItem* a);
CONFIG_PARSER_PROTOTYPE(config_parse_handle_action);
diff --git a/src/login/logind-button.c b/src/login/logind-button.c
index 7fb8114639..0f4e1f1b41 100644
--- a/src/login/logind-button.c
+++ b/src/login/logind-button.c
@@ -84,8 +84,7 @@ static void button_lid_switch_handle_action(Manager *manager, bool is_edge) {
* differently */
if (manager_is_docked_or_external_displays(manager))
handle_action = manager->handle_lid_switch_docked;
- else if (manager->handle_lid_switch_ep != _HANDLE_ACTION_INVALID &&
- manager_is_on_external_power())
+ else if (!handle_action_valid(manager->handle_lid_switch_ep) && manager_is_on_external_power())
handle_action = manager->handle_lid_switch_ep;
else
handle_action = manager->handle_lid_switch;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index f38f0629a8..5c4341df2b 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -29,6 +29,7 @@
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
+#include "logind-action.h"
#include "logind-dbus.h"
#include "logind-polkit.h"
#include "logind-seat-dbus.h"
@@ -53,6 +54,8 @@
#include "utmp-wtmp.h"
#include "virt.h"
+static void reset_scheduled_shutdown(Manager *m);
+
static int get_sender_session(
Manager *m,
sd_bus_message *message,
@@ -309,16 +312,18 @@ static int property_get_preparing(
sd_bus_error *error) {
Manager *m = userdata;
- bool b;
+ bool b = false;
assert(bus);
assert(reply);
assert(m);
- if (streq(property, "PreparingForShutdown"))
- b = m->action_what & INHIBIT_SHUTDOWN;
- else
- b = m->action_what & INHIBIT_SLEEP;
+ if (m->delayed_action) {
+ if (streq(property, "PreparingForShutdown"))
+ b = m->delayed_action->inhibit_what & INHIBIT_SHUTDOWN;
+ else
+ b = m->delayed_action->inhibit_what & INHIBIT_SLEEP;
+ }
return sd_bus_message_append(reply, "b", b);
}
@@ -343,7 +348,9 @@ static int property_get_scheduled_shutdown(
if (r < 0)
return r;
- r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout);
+ r = sd_bus_message_append(reply, "st",
+ handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)),
+ m->scheduled_shutdown_timeout);
if (r < 0)
return r;
@@ -1488,59 +1495,35 @@ static int have_multiple_sessions(
return false;
}
-_printf_(2, 0)
-static int log_with_wall_message(Manager *m, const char *d, const char *p, const char *q) {
+static int bus_manager_log_shutdown(
+ Manager *m,
+ const ActionTableItem *a) {
+
+ const char *message, *log_str;
+
assert(m);
+ assert(a);
+
+ message = a->message;
+ log_str = a->log_str;
+
+ if (message)
+ message = strjoina("MESSAGE=", message);
+ else
+ message = "MESSAGE=System is shutting down";
if (isempty(m->wall_message))
- p = strjoina(p, ".");
+ message = strjoina(message, ".");
else
- p = strjoina(p, " (", m->wall_message, ").");
+ message = strjoina(message, " (", m->wall_message, ").");
- return log_struct(LOG_NOTICE, d, p, q);
-}
+ if (log_str)
+ log_str = strjoina("SHUTDOWN=", log_str);
-static int bus_manager_log_shutdown(
- Manager *m,
- const char *unit_name) {
-
- assert(m);
- assert(unit_name);
-
- if (streq(unit_name, SPECIAL_POWEROFF_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is powering down",
- "SHUTDOWN=power-off");
-
- if (streq(unit_name, SPECIAL_REBOOT_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is rebooting",
- "SHUTDOWN=reboot");
-
- if (streq(unit_name, SPECIAL_HALT_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is halting",
- "SHUTDOWN=halt");
-
- if (streq(unit_name, SPECIAL_KEXEC_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is rebooting with kexec",
- "SHUTDOWN=kexec");
-
- if (streq(unit_name, SPECIAL_FACTORY_RESET_TARGET))
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_FACTORY_RESET_STR,
- "MESSAGE=System is performing factory reset",
- NULL);
-
- return log_with_wall_message(m,
- "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR,
- "MESSAGE=System is shutting down",
- NULL);
+ return log_struct(LOG_NOTICE,
+ "MESSAGE_ID=%s", a->message_id ? a->message_id : SD_MESSAGE_SHUTDOWN_STR,
+ message,
+ log_str);
}
static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) {
@@ -1604,8 +1587,7 @@ static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) {
static int execute_shutdown_or_sleep(
Manager *m,
- InhibitWhat w,
- const char *unit_name,
+ const ActionTableItem *a,
sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
@@ -1613,12 +1595,10 @@ static int execute_shutdown_or_sleep(
int r;
assert(m);
- assert(w > 0);
- assert(w < _INHIBIT_WHAT_MAX);
- assert(unit_name);
+ assert(a);
- if (w == INHIBIT_SHUTDOWN)
- bus_manager_log_shutdown(m, unit_name);
+ if (a->inhibit_what == INHIBIT_SHUTDOWN)
+ bus_manager_log_shutdown(m, a);
r = bus_call_method(
m->bus,
@@ -1626,7 +1606,7 @@ static int execute_shutdown_or_sleep(
"StartUnit",
error,
&reply,
- "ss", unit_name, "replace-irreversibly");
+ "ss", a->target, "replace-irreversibly");
if (r < 0)
goto error;
@@ -1638,8 +1618,7 @@ static int execute_shutdown_or_sleep(
if (r < 0)
goto error;
- m->action_unit = unit_name;
- m->action_what = w;
+ m->delayed_action = a;
/* Make sure the lid switch is ignored for a while */
manager_set_lid_switch_ignore(m, usec_add(now(CLOCK_MONOTONIC), m->holdoff_timeout_usec));
@@ -1648,7 +1627,7 @@ static int execute_shutdown_or_sleep(
error:
/* Tell people that they now may take a lock again */
- (void) send_prepare_for(m, w, false);
+ (void) send_prepare_for(m, a->inhibit_what, false);
return r;
}
@@ -1660,10 +1639,10 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) {
assert(manager);
- if (manager->action_what == 0 || manager->action_job)
+ if (!manager->delayed_action || manager->action_job)
return 0;
- if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
+ if (manager_is_inhibited(manager, manager->delayed_action->inhibit_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) {
_cleanup_free_ char *comm = NULL, *u = NULL;
if (!timeout)
@@ -1678,13 +1657,12 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) {
}
/* Actually do the operation */
- r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error);
+ r = execute_shutdown_or_sleep(manager, manager->delayed_action, &error);
if (r < 0) {
log_warning("Error during inhibitor-delayed operation (already returned success to client): %s",
bus_error_message(&error, r));
- manager->action_unit = NULL;
- manager->action_what = 0;
+ manager->delayed_action = NULL;
}
return 1; /* We did some work. */
@@ -1705,15 +1683,12 @@ static int manager_inhibit_timeout_handler(
static int delay_shutdown_or_sleep(
Manager *m,
- InhibitWhat w,
- const char *unit_name) {
+ const ActionTableItem *a) {
int r;
assert(m);
- assert(w >= 0);
- assert(w < _INHIBIT_WHAT_MAX);
- assert(unit_name);
+ assert(a);
if (m->inhibit_timeout_source) {
r = sd_event_source_set_time_relative(m->inhibit_timeout_source, m->inhibit_delay_max);
@@ -1733,16 +1708,14 @@ static int delay_shutdown_or_sleep(
return r;
}
- m->action_unit = unit_name;
- m->action_what = w;
+ m->delayed_action = a;
return 0;
}
int bus_manager_shutdown_or_sleep_now_or_later(
Manager *m,
- const char *unit_name,
- InhibitWhat w,
+ const ActionTableItem *a,
sd_bus_error *error) {
_cleanup_free_ char *load_state = NULL;
@@ -1750,35 +1723,33 @@ int bus_manager_shutdown_or_sleep_now_or_later(
int r;
assert(m);
- assert(unit_name);
- assert(w > 0);
- assert(w < _INHIBIT_WHAT_MAX);
+ assert(a);
assert(!m->action_job);
- r = unit_load_state(m->bus, unit_name, &load_state);
+ r = unit_load_state(m->bus, a->target, &load_state);
if (r < 0)
return r;
if (!streq(load_state, "loaded"))
return log_notice_errno(SYNTHETIC_ERRNO(EACCES),
"Unit %s is %s, refusing operation.",
- unit_name, load_state);
+ a->target, load_state);
/* Tell everybody to prepare for shutdown/sleep */
- (void) send_prepare_for(m, w, true);
+ (void) send_prepare_for(m, a->inhibit_what, true);
delayed =
m->inhibit_delay_max > 0 &&
- manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL);
+ manager_is_inhibited(m, a->inhibit_what, INHIBIT_DELAY, NULL, false, false, 0, NULL);
if (delayed)
/* Shutdown is delayed, keep in mind what we
* want to do, and start a timeout */
- r = delay_shutdown_or_sleep(m, w, unit_name);
+ r = delay_shutdown_or_sleep(m, a);
else
/* Shutdown is not delayed, execute it
* immediately */
- r = execute_shutdown_or_sleep(m, w, unit_name, error);
+ r = execute_shutdown_or_sleep(m, a, error);
return r;
}
@@ -1786,10 +1757,7 @@ int bus_manager_shutdown_or_sleep_now_or_later(
static int verify_shutdown_creds(
Manager *m,
sd_bus_message *message,
- InhibitWhat w,
- const char *action,
- const char *action_multiple_sessions,
- const char *action_ignore_inhibit,
+ const ActionTableItem *a,
uint64_t flags,
sd_bus_error *error) {
@@ -1799,12 +1767,8 @@ static int verify_shutdown_creds(
int r;
assert(m);
+ assert(a);
assert(message);
- assert(w >= 0);
- assert(w <= _INHIBIT_WHAT_MAX);
- assert(action);
- assert(action_multiple_sessions);
- assert(action_ignore_inhibit);
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
@@ -1819,11 +1783,19 @@ static int verify_shutdown_creds(
return r;
multiple_sessions = r > 0;
- blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
+ blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
interactive = flags & SD_LOGIND_INTERACTIVE;
if (multiple_sessions) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_BOOT,
+ a->polkit_action_multiple_sessions,
+ NULL,
+ interactive,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -1836,7 +1808,14 @@ static int verify_shutdown_creds(
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED,
"Access denied to root due to active block inhibitor");
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(message,
+ CAP_SYS_BOOT,
+ a->polkit_action_ignore_inhibit,
+ NULL,
+ interactive,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -1844,7 +1823,14 @@ static int verify_shutdown_creds(
}
if (!multiple_sessions && !blocked) {
- r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error);
+ r = bus_verify_polkit_async(message,
+ CAP_SYS_BOOT,
+ a->polkit_action,
+ NULL,
+ interactive,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
if (r < 0)
return r;
if (r == 0)
@@ -1854,15 +1840,33 @@ static int verify_shutdown_creds(
return 0;
}
+static int setup_wall_message_timer(Manager *m, sd_bus_message* message) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ int r;
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
+ if (r >= 0) {
+ const char *tty = NULL;
+
+ (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
+ (void) sd_bus_creds_get_tty(creds, &tty);
+
+ r = free_and_strdup(&m->scheduled_shutdown_tty, tty);
+ if (r < 0)
+ return log_oom();
+ }
+
+ r = manager_setup_wall_message_timer(m);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int method_do_shutdown_or_sleep(
Manager *m,
sd_bus_message *message,
- const char *unit_name,
- InhibitWhat w,
- const char *action,
- const char *action_multiple_sessions,
- const char *action_ignore_inhibit,
- SleepOperation sleep_operation,
+ const ActionTableItem *a,
bool with_flags,
sd_bus_error *error) {
@@ -1871,9 +1875,7 @@ static int method_do_shutdown_or_sleep(
assert(m);
assert(message);
- assert(unit_name);
- assert(w >= 0);
- assert(w <= _INHIBIT_WHAT_MAX);
+ assert(a);
if (with_flags) {
/* New style method: with flags parameter (and interactive bool in the bus message header) */
@@ -1882,7 +1884,7 @@ static int method_do_shutdown_or_sleep(
return r;
if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
- if (!streq(unit_name, SPECIAL_REBOOT_TARGET) && (flags & SD_LOGIND_REBOOT_VIA_KEXEC))
+ if (manager_handle_for_item(a) != HANDLE_REBOOT && (flags & SD_LOGIND_REBOOT_VIA_KEXEC))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec is only applicable with reboot operations");
} else {
/* Old style method: no flags parameter, but interactive bool passed as boolean in
@@ -1898,31 +1900,39 @@ static int method_do_shutdown_or_sleep(
}
if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded())
- unit_name = SPECIAL_KEXEC_TARGET;
+ a = manager_item_for_handle(HANDLE_KEXEC);
/* Don't allow multiple jobs being executed at the same time */
- if (m->action_what > 0)
+ if (m->delayed_action)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
"There's already a shutdown or sleep operation in progress");
- if (sleep_operation >= 0) {
- r = can_sleep(sleep_operation);
+ if (a->sleep_operation >= 0) {
+ r = can_sleep(a->sleep_operation);
if (r == -ENOSPC)
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Not enough swap space for hibernation");
if (r == 0)
return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
- "Sleep verb \"%s\" not supported", sleep_operation_to_string(sleep_operation));
+ "Sleep verb \"%s\" not supported", sleep_operation_to_string(a->sleep_operation));
if (r < 0)
return r;
}
- r = verify_shutdown_creds(m, message, w, action, action_multiple_sessions,
- action_ignore_inhibit, flags, error);
+ r = verify_shutdown_creds(m, message, a, flags, error);
if (r != 0)
return r;
- r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error);
+ /* reset case we're shorting a scheduled shutdown */
+ m->unlink_nologin = false;
+ reset_scheduled_shutdown(m);
+
+ m->scheduled_shutdown_timeout = 0;
+ m->scheduled_shutdown_type = a;
+
+ (void) setup_wall_message_timer(m, message);
+
+ r = bus_manager_shutdown_or_sleep_now_or_later(m, a, error);
if (r < 0)
return r;
@@ -1934,12 +1944,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_POWEROFF_TARGET,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.power-off",
- "org.freedesktop.login1.power-off-multiple-sessions",
- "org.freedesktop.login1.power-off-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ manager_item_for_handle(HANDLE_POWEROFF),
sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"),
error);
}
@@ -1949,12 +1954,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_REBOOT_TARGET,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.reboot",
- "org.freedesktop.login1.reboot-multiple-sessions",
- "org.freedesktop.login1.reboot-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ manager_item_for_handle(HANDLE_REBOOT),
sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"),
error);
}
@@ -1964,12 +1964,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_HALT_TARGET,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.halt",
- "org.freedesktop.login1.halt-multiple-sessions",
- "org.freedesktop.login1.halt-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ manager_item_for_handle(HANDLE_HALT),
sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"),
error);
}
@@ -1979,12 +1974,7 @@ static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_SUSPEND_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.suspend",
- "org.freedesktop.login1.suspend-multiple-sessions",
- "org.freedesktop.login1.suspend-ignore-inhibit",
- SLEEP_SUSPEND,
+ manager_item_for_handle(HANDLE_SUSPEND),
sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"),
error);
}
@@ -1994,12 +1984,7 @@ static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_erro
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_HIBERNATE_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HIBERNATE,
+ manager_item_for_handle(HANDLE_HIBERNATE),
sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"),
error);
}
@@ -2009,12 +1994,7 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_HYBRID_SLEEP_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HYBRID_SLEEP,
+ manager_item_for_handle(HANDLE_HYBRID_SLEEP),
sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"),
error);
}
@@ -2024,12 +2004,7 @@ static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata
return method_do_shutdown_or_sleep(
m, message,
- SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_SUSPEND_THEN_HIBERNATE,
+ manager_item_for_handle(HANDLE_SUSPEND_THEN_HIBERNATE),
sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"),
error);
}
@@ -2077,7 +2052,7 @@ static int update_schedule_file(Manager *m) {
"MODE=%s\n",
m->scheduled_shutdown_timeout,
m->enable_wall_messages,
- m->scheduled_shutdown_type);
+ handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)));
if (!isempty(m->wall_message)) {
_cleanup_free_ char *t = NULL;
@@ -2116,8 +2091,7 @@ static void reset_scheduled_shutdown(Manager *m) {
m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source);
m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source);
- m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type);
- m->scheduled_shutdown_timeout = 0;
+ m->scheduled_shutdown_type = NULL;
m->shutdown_dry_run = false;
if (m->unlink_nologin) {
@@ -2133,31 +2107,20 @@ static int manager_scheduled_shutdown_handler(
uint64_t usec,
void *userdata) {
+ const ActionTableItem *a = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Manager *m = userdata;
- const char *target;
int r;
assert(m);
- if (isempty(m->scheduled_shutdown_type))
- return 0;
-
- if (streq(m->scheduled_shutdown_type, "poweroff"))
- target = SPECIAL_POWEROFF_TARGET;
- else if (streq(m->scheduled_shutdown_type, "reboot"))
- target = SPECIAL_REBOOT_TARGET;
- else if (streq(m->scheduled_shutdown_type, "kexec"))
- target = SPECIAL_KEXEC_TARGET;
- else if (streq(m->scheduled_shutdown_type, "halt"))
- target = SPECIAL_HALT_TARGET;
- else
- assert_not_reached();
+ a = m->scheduled_shutdown_type;
+ assert(a);
/* Don't allow multiple jobs being executed at the same time */
- if (m->action_what > 0) {
+ if (m->delayed_action) {
r = -EALREADY;
- log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", target);
+ log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", a->target);
goto error;
}
@@ -2167,16 +2130,16 @@ static int manager_scheduled_shutdown_handler(
* above) for some seconds after our admin has seen the final
* wall message. */
- bus_manager_log_shutdown(m, target);
+ bus_manager_log_shutdown(m, a);
log_info("Running in dry run, suppressing action.");
reset_scheduled_shutdown(m);
return 0;
}
- r = bus_manager_shutdown_or_sleep_now_or_later(m, target, INHIBIT_SHUTDOWN, &error);
+ r = bus_manager_shutdown_or_sleep_now_or_later(m, m->scheduled_shutdown_type, &error);
if (r < 0) {
- log_error_errno(r, "Scheduled shutdown to %s failed: %m", target);
+ log_error_errno(r, "Scheduled shutdown to %s failed: %m", a->target);
goto error;
}
@@ -2189,10 +2152,8 @@ error:
static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
- _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- const char *action_multiple_sessions = NULL;
- const char *action_ignore_inhibit = NULL;
- const char *action = NULL;
+ HandleAction handle;
+ const ActionTableItem *a;
uint64_t elapse;
char *type;
int r;
@@ -2210,23 +2171,15 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
dry_run = true;
}
- if (streq(type, "poweroff")) {
- action = "org.freedesktop.login1.power-off";
- action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions";
- action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit";
- } else if (STR_IN_SET(type, "reboot", "kexec")) {
- action = "org.freedesktop.login1.reboot";
- action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions";
- action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit";
- } else if (streq(type, "halt")) {
- action = "org.freedesktop.login1.halt";
- action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions";
- action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit";
- } else
+ handle = handle_action_from_string(type);
+ if (!IN_SET(handle, HANDLE_POWEROFF, HANDLE_REBOOT, HANDLE_HALT, HANDLE_KEXEC))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type");
- r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, action, action_multiple_sessions,
- action_ignore_inhibit, 0, error);
+ a = manager_item_for_handle(handle);
+ assert(a);
+ assert(a->polkit_action);
+
+ r = verify_shutdown_creds(m, message, a, 0, error);
if (r != 0)
return r;
@@ -2245,12 +2198,7 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
return log_error_errno(r, "sd_event_add_time() failed: %m");
}
- r = free_and_strdup(&m->scheduled_shutdown_type, type);
- if (r < 0) {
- m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
- return log_oom();
- }
-
+ m->scheduled_shutdown_type = a;
m->shutdown_dry_run = dry_run;
if (m->nologin_timeout_source) {
@@ -2270,23 +2218,11 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
m->scheduled_shutdown_timeout = elapse;
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
- if (r >= 0) {
- const char *tty = NULL;
-
- (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid);
- (void) sd_bus_creds_get_tty(creds, &tty);
-
- r = free_and_strdup(&m->scheduled_shutdown_tty, tty);
- if (r < 0) {
- m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
- return log_oom();
- }
- }
-
- r = manager_setup_wall_message_timer(m);
- if (r < 0)
+ r = setup_wall_message_timer(m, message);
+ if (r < 0) {
+ m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source);
return r;
+ }
r = update_schedule_file(m);
if (r < 0)
@@ -2297,20 +2233,42 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_
static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
+ const ActionTableItem *a;
bool cancelled;
+ int r;
assert(m);
assert(message);
- cancelled = m->scheduled_shutdown_type != NULL;
+ cancelled = !IN_SET(manager_handle_for_item(m->scheduled_shutdown_type), HANDLE_IGNORE, _HANDLE_ACTION_INVALID);
+ if (!cancelled)
+ goto done;
+
+ a = m->scheduled_shutdown_type;
+ if (!a->polkit_action)
+ return sd_bus_error_set(error, SD_BUS_ERROR_AUTH_FAILED, "Unsupported shutdown type");
+
+ r = bus_verify_polkit_async(
+ message,
+ CAP_SYS_BOOT,
+ a->polkit_action,
+ NULL,
+ false,
+ UID_INVALID,
+ &m->polkit_registry,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
reset_scheduled_shutdown(m);
- if (cancelled && m->enable_wall_messages) {
+ if (m->enable_wall_messages) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
_cleanup_free_ char *username = NULL;
const char *tty = NULL;
uid_t uid = 0;
- int r;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds);
if (r >= 0) {
@@ -2323,21 +2281,17 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
username, tty, logind_wall_tty_filter, m);
}
+done:
return sd_bus_reply_method_return(message, "b", cancelled);
}
static int method_can_shutdown_or_sleep(
Manager *m,
sd_bus_message *message,
- InhibitWhat w,
- const char *action,
- const char *action_multiple_sessions,
- const char *action_ignore_inhibit,
- SleepOperation sleep_operation,
+ const ActionTableItem *a,
sd_bus_error *error) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- HandleAction handle;
bool multiple_sessions, challenge, blocked;
const char *result = NULL;
uid_t uid;
@@ -2345,14 +2299,10 @@ static int method_can_shutdown_or_sleep(
assert(m);
assert(message);
- assert(w >= 0);
- assert(w <= _INHIBIT_WHAT_MAX);
- assert(action);
- assert(action_multiple_sessions);
- assert(action_ignore_inhibit);
-
- if (sleep_operation >= 0) {
- r = can_sleep(sleep_operation);
+ assert(a);
+
+ if (a->sleep_operation >= 0) {
+ r = can_sleep(a->sleep_operation);
if (IN_SET(r, 0, -ENOSPC))
return sd_bus_reply_method_return(message, "s", "na");
if (r < 0)
@@ -2372,9 +2322,9 @@ static int method_can_shutdown_or_sleep(
return r;
multiple_sessions = r > 0;
- blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
+ blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL);
- handle = handle_action_from_string(sleep_operation_to_string(sleep_operation));
+ HandleAction handle = handle_action_from_string(sleep_operation_to_string(a->sleep_operation));
if (handle >= 0) {
const char *target;
@@ -2394,7 +2344,7 @@ static int method_can_shutdown_or_sleep(
}
if (multiple_sessions) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_multiple_sessions, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
@@ -2407,7 +2357,7 @@ static int method_can_shutdown_or_sleep(
}
if (blocked) {
- r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_ignore_inhibit, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
@@ -2425,7 +2375,7 @@ static int method_can_shutdown_or_sleep(
/* If neither inhibit nor multiple sessions
* apply then just check the normal policy */
- r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error);
+ r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action, NULL, UID_INVALID, &challenge, error);
if (r < 0)
return r;
@@ -2445,12 +2395,7 @@ static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_e
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.power-off",
- "org.freedesktop.login1.power-off-multiple-sessions",
- "org.freedesktop.login1.power-off-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ m, message, manager_item_for_handle(HANDLE_POWEROFF),
error);
}
@@ -2458,12 +2403,7 @@ static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_err
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.reboot",
- "org.freedesktop.login1.reboot-multiple-sessions",
- "org.freedesktop.login1.reboot-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ m, message, manager_item_for_handle(HANDLE_REBOOT),
error);
}
@@ -2471,12 +2411,7 @@ static int method_can_halt(sd_bus_message *message, void *userdata, sd_bus_error
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SHUTDOWN,
- "org.freedesktop.login1.halt",
- "org.freedesktop.login1.halt-multiple-sessions",
- "org.freedesktop.login1.halt-ignore-inhibit",
- _SLEEP_OPERATION_INVALID,
+ m, message, manager_item_for_handle(HANDLE_HALT),
error);
}
@@ -2484,12 +2419,7 @@ static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_er
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.suspend",
- "org.freedesktop.login1.suspend-multiple-sessions",
- "org.freedesktop.login1.suspend-ignore-inhibit",
- SLEEP_SUSPEND,
+ m, message, manager_item_for_handle(HANDLE_SUSPEND),
error);
}
@@ -2497,12 +2427,7 @@ static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HIBERNATE,
+ m, message, manager_item_for_handle(HANDLE_HIBERNATE),
error);
}
@@ -2510,12 +2435,7 @@ static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_b
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_HYBRID_SLEEP,
+ m, message, manager_item_for_handle(HANDLE_HYBRID_SLEEP),
error);
}
@@ -2523,12 +2443,7 @@ static int method_can_suspend_then_hibernate(sd_bus_message *message, void *user
Manager *m = userdata;
return method_can_shutdown_or_sleep(
- m, message,
- INHIBIT_SLEEP,
- "org.freedesktop.login1.hibernate",
- "org.freedesktop.login1.hibernate-multiple-sessions",
- "org.freedesktop.login1.hibernate-ignore-inhibit",
- SLEEP_SUSPEND_THEN_HIBERNATE,
+ m, message, manager_item_for_handle(HANDLE_SUSPEND_THEN_HIBERNATE),
error);
}
@@ -3200,6 +3115,15 @@ static int method_set_wall_message(
if (r < 0)
return r;
+ /* sysvinit has a 252 (256-(strlen(" \r\n")+1)) character
+ * limit for the wall message. There is no real technical
+ * need for that but doesn't make sense to store arbitrary
+ * armounts either.
+ * https://git.savannah.nongnu.org/cgit/sysvinit.git/tree/src/shutdown.c#n72)
+ */
+ if (strlen(wall_message) > 252)
+ return -EMSGSIZE;
+
/* Short-circuit the operation if the desired state is already in place, to
* avoid an unnecessary polkit permission check. */
if (streq_ptr(m->wall_message, empty_to_null(wall_message)) &&
@@ -3267,7 +3191,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error
* executing the operation. We shouldn't create the impression
* that the lock was successful if the machine is about to go
* down/suspend any moment. */
- if (m->action_what & w)
+ if (m->delayed_action && m->delayed_action->inhibit_what & w)
return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS,
"The operation inhibition has been requested for is already running");
@@ -3774,14 +3698,14 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
}
if (m->action_job && streq(m->action_job, path)) {
- log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what));
+ assert(m->delayed_action);
+ log_info("Operation '%s' finished.", inhibit_what_to_string(m->delayed_action->inhibit_what));
/* Tell people that they now may take a lock again */
- (void) send_prepare_for(m, m->action_what, false);
+ (void) send_prepare_for(m, m->delayed_action->inhibit_what, false);
m->action_job = mfree(m->action_job);
- m->action_unit = NULL;
- m->action_what = 0;
+ m->delayed_action = NULL;
return 0;
}
diff --git a/src/login/logind-dbus.h b/src/login/logind-dbus.h
index 6b5d3abcd6..13aff11bba 100644
--- a/src/login/logind-dbus.h
+++ b/src/login/logind-dbus.h
@@ -4,6 +4,7 @@
#include "sd-bus.h"
#include "bus-object.h"
+#include "logind-action.h"
#include "logind-session.h"
#include "logind-user.h"
#include "logind.h"
@@ -14,7 +15,7 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char
int manager_dispatch_delayed(Manager *manager, bool timeout);
-int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error);
+int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const ActionTableItem *a, sd_bus_error *error);
int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c
index 5533836473..03817f9c1a 100644
--- a/src/login/logind-utmp.c
+++ b/src/login/logind-utmp.c
@@ -72,7 +72,7 @@ static int warn_wall(Manager *m, usec_t n) {
r = asprintf(&l, "%s%sThe system is going down for %s %s%s!",
strempty(m->wall_message),
isempty(m->wall_message) ? "" : "\n",
- m->scheduled_shutdown_type,
+ handle_action_to_string(manager_handle_for_item(m->scheduled_shutdown_type)),
left ? "at " : "NOW",
left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : "");
if (r < 0) {
@@ -130,16 +130,14 @@ int manager_setup_wall_message_timer(Manager *m) {
/* wall message handling */
- if (isempty(m->scheduled_shutdown_type)) {
- warn_wall(m, n);
+ if (!m->scheduled_shutdown_type)
return 0;
- }
- if (elapse < n)
+ if (elapse > 0 && elapse < n)
return 0;
/* Warn immediately if less than 15 minutes are left */
- if (elapse - n < 15 * USEC_PER_MINUTE) {
+ if (elapse == 0 || elapse - n < 15 * USEC_PER_MINUTE) {
r = warn_wall(m, n);
if (r == 0)
return 0;
diff --git a/src/login/logind.c b/src/login/logind.c
index 52b1d95034..c561b75951 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -54,6 +54,7 @@ static int manager_new(Manager **ret) {
*m = (Manager) {
.console_active_fd = -1,
.reserve_vt_fd = -1,
+ .enable_wall_messages = true,
.idle_action_not_before_usec = now(CLOCK_MONOTONIC),
};
@@ -167,7 +168,6 @@ static Manager* manager_unref(Manager *m) {
strv_free(m->kill_only_users);
strv_free(m->kill_exclude_users);
- free(m->scheduled_shutdown_type);
free(m->scheduled_shutdown_tty);
free(m->wall_message);
free(m->action_job);
diff --git a/src/login/logind.h b/src/login/logind.h
index 730c14a46a..5647e5069c 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -68,21 +68,17 @@ struct Manager {
usec_t inhibit_delay_max;
usec_t user_stop_delay;
- /* If an action is currently being executed or is delayed,
- * this is != 0 and encodes what is being done */
- InhibitWhat action_what;
-
/* If a shutdown/suspend was delayed due to an inhibitor this
- contains the unit name we are supposed to start after the
+ contains the action we are supposed to start after the
delay is over */
- const char *action_unit;
+ const ActionTableItem *delayed_action;
/* If a shutdown/suspend is currently executed, then this is
* the job of it */
char *action_job;
sd_event_source *inhibit_timeout_source;
- char *scheduled_shutdown_type;
+ const ActionTableItem *scheduled_shutdown_type;
usec_t scheduled_shutdown_timeout;
sd_event_source *scheduled_shutdown_timeout_source;
uid_t scheduled_shutdown_uid;
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 1f5f4bd41a..ab4f321b00 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1026,8 +1026,7 @@ static Link *link_drop(Link *link) {
hashmap_remove(link->manager->links_by_name, link->ifname);
/* bonding master and its slaves have the same hardware address. */
- if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link)
- hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr);
+ hashmap_remove_value(link->manager->links_by_hw_addr, &link->hw_addr, link);
/* The following must be called at last. */
assert_se(hashmap_remove(link->manager->links_by_index, INT_TO_PTR(link->ifindex)) == link);
@@ -1775,7 +1774,7 @@ static int link_admin_state_up(Link *link) {
return 0;
if (link->activated && link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN) {
- log_link_info(link, "ActivationPolicy is \"always-off\", forcing link down.");
+ log_link_info(link, "Activation policy is \"always-down\", forcing link down.");
return link_request_to_bring_up_or_down(link, /* up = */ false);
}
@@ -1795,7 +1794,7 @@ static int link_admin_state_down(Link *link) {
return 0;
if (link->activated && link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) {
- log_link_info(link, "ActivationPolicy is \"always-on\", forcing link up.");
+ log_link_info(link, "Activation policy is \"always-up\", forcing link up.");
return link_request_to_bring_up_or_down(link, /* up = */ true);
}
@@ -2148,8 +2147,7 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message)
log_link_debug(link, "Hardware address is changed: %s → %s",
HW_ADDR_TO_STR(&link->hw_addr), HW_ADDR_TO_STR(&addr));
- if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link)
- hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr);
+ hashmap_remove_value(link->manager->links_by_hw_addr, &link->hw_addr, link);
}
link->hw_addr = addr;
diff --git a/src/resolve/test-resolved-stream.c b/src/resolve/test-resolved-stream.c
index 8a01460a0e..f9428989f0 100644
--- a/src/resolve/test-resolved-stream.c
+++ b/src/resolve/test-resolved-stream.c
@@ -130,7 +130,7 @@ static void *tls_dns_server(void *p) {
assert_se(in_addr_to_string(SERVER_ADDRESS.sin_family,
&(union in_addr_union){.in = SERVER_ADDRESS.sin_addr},
&ip_str) >= 0);
- asprintf(&bind_str, "%s:%d", ip_str, be16toh(SERVER_ADDRESS.sin_port));
+ assert_se(asprintf(&bind_str, "%s:%d", ip_str, be16toh(SERVER_ADDRESS.sin_port)) >= 0);
/* We will hook one of the socketpair ends to OpenSSL's TLS server
* stdin/stdout, so we will be able to read and write plaintext
diff --git a/src/shared/copy.c b/src/shared/copy.c
index a92cfc0f61..0941706356 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -119,9 +119,11 @@ static int create_hole(int fd, off_t size) {
if (end < 0)
return -errno;
- /* If we're not at the end of the target file, punch a hole in the existing space using fallocate(). */
+ /* If we're not at the end of the target file, try to punch a hole in the existing space using fallocate(). */
- if (offset < end && fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, MIN(size, end - offset)) < 0)
+ if (offset < end &&
+ fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, MIN(size, end - offset)) < 0 &&
+ !ERRNO_IS_NOT_SUPPORTED(errno))
return -errno;
if (end - offset >= size) {
diff --git a/src/shared/main-func.h b/src/shared/main-func.h
index 05cdffeec0..81a5c1813c 100644
--- a/src/shared/main-func.h
+++ b/src/shared/main-func.h
@@ -15,6 +15,7 @@
#define _DEFINE_MAIN_FUNCTION(intro, impl, ret) \
int main(int argc, char *argv[]) { \
int r; \
+ assert_se(argc > 0 && !isempty(argv[0])); \
save_argc_argv(argc, argv); \
intro; \
r = impl; \
diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c
index a1359a5bfd..67ea858142 100644
--- a/src/shared/selinux-util.c
+++ b/src/shared/selinux-util.c
@@ -346,7 +346,7 @@ int mac_selinux_apply_fd(int fd, const char *path, const char *label) {
assert(label);
- if (fsetfilecon(fd, label) < 0)
+ if (setfilecon(FORMAT_PROC_FD_PATH(fd), label) < 0)
return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, strna(path));
#endif
return 0;
diff --git a/src/shared/smack-util.c b/src/shared/smack-util.c
index b8434b068c..0df1778cb2 100644
--- a/src/shared/smack-util.c
+++ b/src/shared/smack-util.c
@@ -95,9 +95,9 @@ int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) {
return 0;
if (label)
- r = fsetxattr(fd, smack_attr_to_string(attr), label, strlen(label), 0);
+ r = setxattr(FORMAT_PROC_FD_PATH(fd), smack_attr_to_string(attr), label, strlen(label), 0);
else
- r = fremovexattr(fd, smack_attr_to_string(attr));
+ r = removexattr(FORMAT_PROC_FD_PATH(fd), smack_attr_to_string(attr));
if (r < 0)
return -errno;
diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c
index 760758322f..a7d3ffadf4 100644
--- a/src/systemctl/systemctl-compat-halt.c
+++ b/src/systemctl/systemctl-compat-halt.c
@@ -144,35 +144,23 @@ int halt_parse_argv(int argc, char *argv[]) {
int halt_main(void) {
int r;
- r = logind_check_inhibitors(arg_action);
- if (r < 0)
- return r;
-
- /* Delayed shutdown requested, and was successful */
- if (arg_when > 0 && logind_schedule_shutdown() == 0)
- return 0;
-
- /* No delay, or logind failed or is not at all available */
- if (geteuid() != 0) {
- if (arg_dry_run || arg_force > 0) {
- (void) must_be_root();
- return -EPERM;
- }
+ /* always try logind first */
+ if (arg_when > 0)
+ r = logind_schedule_shutdown();
+ else {
+ r = logind_check_inhibitors(arg_action);
+ if (r < 0)
+ return r;
- /* Try logind if we are a normal user and no special mode applies. Maybe polkit allows us to
- * shutdown the machine. */
- if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT, ACTION_KEXEC, ACTION_HALT)) {
- r = logind_reboot(arg_action);
- if (r >= 0)
- return r;
- if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
- /* Requested operation is not supported on the local system or already in
- * progress */
- return r;
-
- /* on all other errors, try low-level operation */
- }
+ r = logind_reboot(arg_action);
}
+ if (r >= 0)
+ return r;
+ if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
+ /* Requested operation requires auth, is not supported on the local system or already in
+ * progress */
+ return r;
+ /* on all other errors, try low-level operation */
/* In order to minimize the difference between operation with and without logind, we explicitly
* enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */
@@ -181,7 +169,10 @@ int halt_main(void) {
if (!arg_dry_run && !arg_force)
return start_with_fallback();
- assert(geteuid() == 0);
+ if (geteuid() != 0) {
+ (void) must_be_root();
+ return -EPERM;
+ }
if (!arg_no_wtmp) {
if (sd_booted() > 0)
diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c
index 9eae59ca31..9bf24ed554 100644
--- a/src/systemctl/systemctl-logind.c
+++ b/src/systemctl/systemctl-logind.c
@@ -330,7 +330,7 @@ int logind_schedule_shutdown(void) {
r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when);
if (r < 0)
- return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r));
+ return log_warning_errno(r, "Failed to schedule shutdown: %s", bus_error_message(&error, r));
if (!arg_quiet)
logind_show_shutdown();
diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c
index 6ece700a9b..08eefc473e 100644
--- a/src/systemctl/systemctl-start-special.c
+++ b/src/systemctl/systemctl-start-special.c
@@ -213,8 +213,8 @@ int start_special(int argc, char *argv[], void *userdata) {
r = logind_reboot(a);
if (r >= 0)
return r;
- if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS))
- /* Requested operation is not supported or already in progress */
+ if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
+ /* Requested operation requires auth, is not supported or already in progress */
return r;
/* On all other errors, try low-level operation. In order to minimize the difference
diff --git a/src/test/test-load-fragment.c b/src/test/test-load-fragment.c
index 68172be43e..9e5ae85597 100644
--- a/src/test/test-load-fragment.c
+++ b/src/test/test-load-fragment.c
@@ -803,7 +803,7 @@ TEST(config_parse_unit_env_file) {
"EnvironmentFile", 0, "not-absolute",
&files, u);
assert_se(r == 0);
- assert_se(strv_length(files) == 0);
+ assert_se(strv_isempty(files));
r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1,
"EnvironmentFile", 0, "/absolute1",
diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh
index 0f26eaafeb..c360c8b661 100755
--- a/test/TEST-64-UDEV-STORAGE/test.sh
+++ b/test/TEST-64-UDEV-STORAGE/test.sh
@@ -119,7 +119,6 @@ test_run() {
# Execute each currently defined function starting with "testcase_"
for testcase in "${TESTCASES[@]}"; do
- _image_cleanup
echo "------ $testcase: BEGIN ------"
# Note for my future frustrated self: `fun && xxx` (as well as ||, if, while,
# until, etc.) _DISABLES_ the `set -e` behavior in _ALL_ nested function
@@ -130,8 +129,14 @@ test_run() {
# So, be careful when adding clean up snippets in the testcase_*() functions -
# if the `test_run_one()` function isn't the last command, you have propagate
# the exit code correctly (e.g. `test_run_one() || return $?`, see below).
- ec=0
- "$testcase" "$test_id" || ec=$?
+
+ # FIXME: temporary workaround for intermittent fails in certain tests
+ # See: https://github.com/systemd/systemd/issues/21819
+ for ((_i = 0; _i < 3; _i++)); do
+ _image_cleanup
+ ec=0
+ "$testcase" "$test_id" && break || ec=$?
+ done
case $ec in
0)
passed+=("$testcase")
@@ -166,6 +171,7 @@ testcase_megasas2_basic() {
return 77
fi
+ local i
local qemu_opts=(
"-device megasas-gen2,id=scsi0"
"-device megasas-gen2,id=scsi1"
@@ -192,6 +198,9 @@ testcase_nvme_basic() {
return 77
fi
+ local i
+ local qemu_opts=()
+
for i in {0..27}; do
qemu_opts+=(
"-device nvme,drive=nvme$i,serial=deadbeef$i,num_queues=8"
@@ -215,7 +224,7 @@ testcase_virtio_scsi_identically_named_partitions() {
# and attach them to a virtio-scsi controller
local qemu_opts=("-device virtio-scsi-pci,id=scsi0,num_queues=4")
local diskpath="${TESTDIR:?}/namedpart0.img"
- local lodev qemu_timeout
+ local i lodev qemu_timeout
dd if=/dev/zero of="$diskpath" bs=1M count=18
lodev="$(losetup --show -f -P "$diskpath")"
@@ -325,7 +334,7 @@ testcase_lvm_basic() {
fi
local qemu_opts=("-device ahci,id=ahci0")
- local diskpath
+ local diskpath i
# Attach 4 SATA disks to the VM (and set their model and serial fields
# to something predictable, so we can refer to them later)
diff --git a/test/TEST-69-SHUTDOWN/Makefile b/test/TEST-69-SHUTDOWN/Makefile
new file mode 120000
index 0000000000..e9f93b1104
--- /dev/null
+++ b/test/TEST-69-SHUTDOWN/Makefile
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile \ No newline at end of file
diff --git a/test/TEST-69-SHUTDOWN/test.sh b/test/TEST-69-SHUTDOWN/test.sh
new file mode 100755
index 0000000000..42a600ec18
--- /dev/null
+++ b/test/TEST-69-SHUTDOWN/test.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -e
+
+TEST_DESCRIPTION="shutdown testing"
+IMAGE_NAME="shutdown"
+TEST_NO_QEMU=1
+
+# shellcheck source=test/test-functions
+. "${TEST_BASE_DIR:?}/test-functions"
+
+_ORIG_NSPAWN="$SYSTEMD_NSPAWN"
+SYSTEMD_NSPAWN="$STATEDIR/run-nspawn"
+
+setup_nspawn_root_hook() {
+ cat > "$STATEDIR"/run-nspawn <<-EOF
+ #!/bin/bash
+ exec "$TEST_BASE_DIR"/test-shutdown.py -- "$_ORIG_NSPAWN" "\$@"
+ exit 1
+ EOF
+ chmod 755 "$STATEDIR"/run-nspawn
+}
+
+test_append_files() {
+ # prevent shutdown in test suite, the expect script does that manually.
+ rm "$1"/usr/lib/systemd/tests/testdata/units/end.service
+ inst /usr/bin/screen
+ echo "PS1='screen\$WINDOW # '" > "$1"/etc/bash.bashrc
+ echo 'startup_message off' > "$1"/etc/screenrc
+ echo 'bell_msg ""' >> "$1"/etc/screenrc
+}
+
+do_test "$@"
diff --git a/test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50a b/test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50a
new file mode 100644
index 0000000000..87345bf0ec
--- /dev/null
+++ b/test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50a
Binary files differ
diff --git a/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 b/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980
new file mode 100644
index 0000000000..a51cf70dd0
--- /dev/null
+++ b/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980
Binary files differ
diff --git a/test/fuzz/fuzz-dhcp-server-relay-message/clusterfuzz-testcase-minimized-fuzz-dhcp-server-relay-message-4972399731277824 b/test/fuzz/fuzz-dhcp-server-relay-message/clusterfuzz-testcase-minimized-fuzz-dhcp-server-relay-message-4972399731277824
new file mode 100644
index 0000000000..e902b6989b
--- /dev/null
+++ b/test/fuzz/fuzz-dhcp-server-relay-message/clusterfuzz-testcase-minimized-fuzz-dhcp-server-relay-message-4972399731277824
Binary files differ
diff --git a/test/test-functions b/test/test-functions
index ba18a0ea2d..ac8bf8883b 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -576,7 +576,8 @@ install_verity_minimal() {
oldinitdir="$initdir"
rm -rfv "$TESTDIR/minimal"
export initdir="$TESTDIR/minimal"
- mkdir -p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt"
+ # app0 will use TemporaryFileSystem=/var/lib, app1 will need the mount point in the base image
+ mkdir -p "$initdir/usr/lib/systemd/system" "$initdir/usr/lib/extension-release.d" "$initdir/etc" "$initdir/var/tmp" "$initdir/opt" "$initdir/var/lib/app1"
setup_basic_dirs
install_basic_tools
# Shellcheck treats [[ -v VAR ]] as an assignment to avoid a different
@@ -598,19 +599,23 @@ install_verity_minimal() {
touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf"
touch "$initdir/opt/some_file"
echo MARKER=1 >>"$initdir/usr/lib/os-release"
- echo "PORTABLE_PREFIXES=app0 minimal" >>"$initdir/usr/lib/os-release"
- echo -e "[Service]\nExecStartPre=cat /usr/lib/os-release\nExecStart=sleep 120" >"$initdir/usr/lib/systemd/system/app0.service"
- cp "$initdir/usr/lib/systemd/system/app0.service" "$initdir/usr/lib/systemd/system/app0-foo.service"
+ echo "PORTABLE_PREFIXES=app0 minimal minimal-app0" >>"$initdir/usr/lib/os-release"
+ cat >"$initdir/usr/lib/systemd/system/minimal-app0.service" <<EOF
+[Service]
+ExecStartPre=cat /usr/lib/os-release
+ExecStart=sleep 120
+EOF
+ cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
- mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_0.raw"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_0.raw" -noappend
veritysetup format "$oldinitdir/usr/share/minimal_0.raw" "$oldinitdir/usr/share/minimal_0.verity" | \
grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_0.roothash"
sed -i "s/MARKER=1/MARKER=2/g" "$initdir/usr/lib/os-release"
- rm "$initdir/usr/lib/systemd/system/app0-foo.service"
- cp "$initdir/usr/lib/systemd/system/app0.service" "$initdir/usr/lib/systemd/system/app0-bar.service"
+ rm "$initdir/usr/lib/systemd/system/minimal-app0-foo.service"
+ cp "$initdir/usr/lib/systemd/system/minimal-app0.service" "$initdir/usr/lib/systemd/system/minimal-app0-bar.service"
- mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/minimal_1.raw" -noappend
veritysetup format "$oldinitdir/usr/share/minimal_1.raw" "$oldinitdir/usr/share/minimal_1.verity" | \
grep '^Root hash:' | cut -f2 | tr -d '\n' >"$oldinitdir/usr/share/minimal_1.roothash"
@@ -629,16 +634,20 @@ install_verity_minimal() {
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/script0.sh
+TemporaryFileSystem=/var/lib
+StateDirectory=app0
+RuntimeDirectory=app0
EOF
cat >"$initdir/opt/script0.sh" <<EOF
#!/bin/bash
set -e
test -e /usr/lib/os-release
+echo bar > \${STATE_DIRECTORY}/foo
cat /usr/lib/extension-release.d/extension-release.app0
EOF
chmod +x "$initdir/opt/script0.sh"
echo MARKER=1 >"$initdir/usr/lib/systemd/system/some_file"
- mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/app0.raw" -noappend
export initdir="$TESTDIR/app1"
mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
@@ -652,16 +661,19 @@ EOF
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/script1.sh
+StateDirectory=app1
+RuntimeDirectory=app1
EOF
cat >"$initdir/opt/script1.sh" <<EOF
#!/bin/bash
set -e
test -e /usr/lib/os-release
+echo baz > \${STATE_DIRECTORY}/foo
cat /usr/lib/extension-release.d/extension-release.app2
EOF
chmod +x "$initdir/opt/script1.sh"
echo MARKER=1 >"$initdir/usr/lib/systemd/system/other_file"
- mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw"
+ mksquashfs "$initdir" "$oldinitdir/usr/share/app1.raw" -noappend
)
}
@@ -1884,6 +1896,8 @@ has_user_dbus_socket() {
fi
}
+setup_nspawn_root_hook() { :;}
+
setup_nspawn_root() {
if [ -z "${initdir}" ]; then
dfatal "\$initdir not defined"
@@ -1896,6 +1910,8 @@ setup_nspawn_root() {
ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
cp -ar "$initdir" "$TESTDIR/unprivileged-nspawn-root"
fi
+
+ setup_nspawn_root_hook
}
setup_basic_dirs() {
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index 0233c4dd1f..ba16170393 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -594,6 +594,21 @@ class Utilities():
def check_link_attr(self, *args):
self.assertEqual(read_link_attr(*args[:-1]), args[-1]);
+ def wait_activated(self, link, state='down', timeout=20, fail_assert=True):
+ # wait for the interface is activated.
+ invocation_id = check_output('systemctl show systemd-networkd -p InvocationID --value')
+ needle = f'{link}: Bringing link {state}'
+ flag = state.upper()
+ for iteration in range(timeout+1):
+ output = check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id)
+ if needle in output and flag in check_output(f'ip link show {link}'):
+ return True
+ if iteration < timeout:
+ time.sleep(1)
+ if fail_assert:
+ self.fail(f'Timed out waiting for {link} activated.')
+ return False
+
def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
"""Wait for the link to reach the specified operstate and/or setup state.
@@ -3053,7 +3068,6 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.wait_operstate('test1', 'routable')
def _test_activation_policy(self, test):
- self.setUp()
conffile = '25-activation-policy.network'
if test:
conffile = f'{conffile}.d/{test}.conf'
@@ -3061,13 +3075,13 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
start_networkd()
always = test.startswith('always')
- if test == 'manual':
- initial_up = 'UP' in check_output('ip link show test1')
- else:
- initial_up = not test.endswith('down') # note: default is up
+ initial_up = test != 'manual' and not test.endswith('down') # note: default is up
expect_up = initial_up
next_up = not expect_up
+ if test.endswith('down'):
+ self.wait_activated('test1')
+
for iteration in range(4):
with self.subTest(iteration=iteration, expect_up=expect_up):
operstate = 'routable' if expect_up else 'off'
@@ -3087,16 +3101,17 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
check_output('ip link set dev test1 down')
expect_up = initial_up if always else next_up
next_up = not next_up
-
- self.tearDown()
+ if always:
+ time.sleep(1)
def test_activation_policy(self):
for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
with self.subTest(test=test):
+ self.setUp()
self._test_activation_policy(test)
+ self.tearDown()
def _test_activation_policy_required_for_online(self, policy, required):
- self.setUp()
conffile = '25-activation-policy.network'
units = ['11-dummy.netdev', '12-dummy.netdev', '12-dummy.network', conffile]
if policy:
@@ -3106,6 +3121,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
copy_unit_to_networkd_unit_path(*units, dropins=False)
start_networkd()
+ if policy.endswith('down'):
+ self.wait_activated('test1')
+
if policy.endswith('down') or policy == 'manual':
self.wait_operstate('test1', 'off', setup_state='configuring')
else:
@@ -3131,13 +3149,13 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
yesno = 'yes' if expected else 'no'
self.assertRegex(output, f'Required For Online: {yesno}')
- self.tearDown()
-
def test_activation_policy_required_for_online(self):
for policy in ['up', 'always-up', 'manual', 'always-down', 'down', 'bound', '']:
for required in ['yes', 'no', '']:
with self.subTest(policy=policy, required=required):
+ self.setUp()
self._test_activation_policy_required_for_online(policy, required)
+ self.tearDown()
def test_domain(self):
copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network')
diff --git a/test/test-shutdown.py b/test/test-shutdown.py
new file mode 100755
index 0000000000..d34e224942
--- /dev/null
+++ b/test/test-shutdown.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+
+import argparse
+import logging
+import pexpect
+import sys
+
+
+def run(args):
+
+ ret = 1
+ logger = logging.getLogger("test-shutdown")
+
+ logger.info("spawning test")
+ console = pexpect.spawn(args.command, args.arg, env={
+ "TERM": "linux",
+ }, encoding='utf-8', timeout=30)
+
+ if args.verbose:
+ console.logfile = sys.stdout
+
+ logger.debug("child pid %d" % console.pid)
+
+ try:
+ logger.info("waiting for login prompt")
+ console.expect('H login: ', 10)
+
+ logger.info("log in and start screen")
+ console.sendline('root')
+ console.expect('bash.*# ', 10)
+ console.sendline('screen')
+ console.expect('screen0 ', 10)
+ console.sendcontrol('a')
+ console.send('c')
+ console.expect('screen1 ', 10)
+
+# console.interact()
+
+ console.sendline('tty')
+ console.expect(r'/dev/(pts/\d+)')
+ pty = console.match.group(1)
+ logger.info("window 1 at line %s", pty)
+
+ logger.info("schedule reboot")
+ console.sendline('shutdown -r')
+ console.expect("Reboot scheduled for (?P<date>.*), use 'shutdown -c' to cancel", 2)
+ date = console.match.group('date')
+ logger.info("reboot scheduled for %s", date)
+
+ console.sendcontrol('a')
+ console.send('0')
+ logger.info("verify broadcast message")
+ console.expect('Broadcast message from root@H on %s' % pty, 2)
+ console.expect('The system is going down for reboot at %s' % date, 2)
+
+ logger.info("check show output")
+ console.sendline('shutdown --show')
+ console.expect("Reboot scheduled for %s, use 'shutdown -c' to cancel" % date, 2)
+
+ logger.info("cancel shutdown")
+ console.sendline('shutdown -c')
+ console.sendcontrol('a')
+ console.send('1')
+ console.expect('The system shutdown has been cancelled', 2)
+
+ logger.info("call for reboot")
+ console.sendline('sleep 10; shutdown -r now')
+ console.sendcontrol('a')
+ console.send('0')
+ console.expect("The system is going down for reboot NOW!", 12)
+
+ logger.info("waiting for reboot")
+
+ console.expect('H login: ', 10)
+ console.sendline('root')
+ console.expect('bash.*# ', 10)
+
+ console.sendline('> /testok')
+
+ logger.info("power off")
+ console.sendline('poweroff')
+
+ logger.info("expect termination now")
+ console.expect(pexpect.EOF)
+
+ ret = 0
+ except Exception as e:
+ logger.error(e)
+ logger.info("killing child pid %d" % console.pid)
+ console.terminate()
+
+ return ret
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='test logind shutdown feature')
+ parser.add_argument("-v", "--verbose", action="store_true", help="verbose")
+ parser.add_argument("command", help="command to run")
+ parser.add_argument("arg", nargs='*', help="args for command")
+
+ args = parser.parse_args()
+
+ if args.verbose:
+ level = logging.DEBUG
+ else:
+ level = logging.INFO
+
+ logging.basicConfig(level=level)
+
+ sys.exit(run(args))
+
+# vim: sw=4 et
diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh
index fdb84b6b45..1b927a8305 100755
--- a/test/units/testsuite-29.sh
+++ b/test/units/testsuite-29.sh
@@ -6,10 +6,13 @@ set -eux
set -o pipefail
ARGS=()
+state_directory=/var/lib/private/
if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
# If we're running under sanitizers, we need to use a less restrictive
# profile, otherwise LSan syscall would get blocked by seccomp
ARGS+=(--profile=trusted)
+ # With the trusted profile DynamicUser is disabled, so the storage is not in private/
+ state_directory=/var/lib/
fi
systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service'
@@ -24,29 +27,29 @@ cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
Environment=SYSTEMD_LOG_LEVEL=debug
EOF
-portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw app0
+portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0
-systemctl is-active app0.service
-systemctl is-active app0-foo.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
set +o pipefail
set +e
-systemctl is-active app0-bar.service && exit 1
+systemctl is-active minimal-app0-bar.service && exit 1
set -e
set -o pipefail
-portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw app0
+portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
-systemctl is-active app0.service
-systemctl is-active app0-bar.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
set +o pipefail
set +e
-systemctl is-active app0-foo.service && exit 1
+systemctl is-active minimal-app0-foo.service && exit 1
set -e
set -o pipefail
portablectl list | grep -q -F "minimal_1"
-portablectl detach --now --runtime /usr/share/minimal_1.raw app0
+portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl list | grep -q -F "No images."
@@ -55,29 +58,29 @@ portablectl list | grep -q -F "No images."
unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw
unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw
-portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 app0
+portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
-systemctl is-active app0.service
-systemctl is-active app0-foo.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-foo.service
set +o pipefail
set +e
-systemctl is-active app0-bar.service && exit 1
+systemctl is-active minimal-app0-bar.service && exit 1
set -e
set -o pipefail
-portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 app0
+portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
-systemctl is-active app0.service
-systemctl is-active app0-bar.service
+systemctl is-active minimal-app0.service
+systemctl is-active minimal-app0-bar.service
set +o pipefail
set +e
-systemctl is-active app0-foo.service && exit 1
+systemctl is-active minimal-app0-foo.service && exit 1
set -e
set -o pipefail
portablectl list | grep -q -F "minimal_1"
-portablectl detach --now --enable --runtime /tmp/minimal_1 app0
+portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
portablectl list | grep -q -F "No images."
@@ -109,6 +112,12 @@ status="$(portablectl is-attached --extension app1 minimal_1)"
portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1
+# Ensure that the combination of read-only images, state directory and dynamic user works, and that
+# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
+# after the service is attached before the file appears.
+grep -q -F bar "${state_directory}/app0/foo"
+grep -q -F baz "${state_directory}/app1/foo"
+
# portablectl also works with directory paths rather than images
mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
diff --git a/test/units/testsuite-46.sh b/test/units/testsuite-46.sh
index c3e57cec95..d0bedc63d5 100755
--- a/test/units/testsuite-46.sh
+++ b/test/units/testsuite-46.sh
@@ -153,20 +153,13 @@ if ! systemd-detect-virt -cq ; then
inspect test-user2
fi
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \
&& { echo 'unexpected success'; exit 1; }
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
-wait_for_state test-user inactive
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \
&& { echo 'unexpected success'; exit 1; }
diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh
index 7ddb20601a..6aabbd139c 100755
--- a/test/units/testsuite-50.sh
+++ b/test/units/testsuite-50.sh
@@ -308,7 +308,8 @@ systemd-run -P --property ExtensionImages="/usr/share/app0.raw /usr/share/app1.r
cat >/run/systemd/system/testservice-50e.service <<EOF
[Service]
MountAPIVFS=yes
-TemporaryFileSystem=/run
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
RootImage=${image}.raw
ExtensionImages=/usr/share/app0.raw /usr/share/app1.raw:nosuid
# Relevant only for sanitizer runs
@@ -336,7 +337,8 @@ systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/a
cat >/run/systemd/system/testservice-50f.service <<EOF
[Service]
MountAPIVFS=yes
-TemporaryFileSystem=/run
+TemporaryFileSystem=/run /var/lib
+StateDirectory=app0
RootImage=${image}.raw
ExtensionDirectories=${image_dir}/app0 ${image_dir}/app1
# Relevant only for sanitizer runs
diff --git a/test/units/testsuite-64.sh b/test/units/testsuite-64.sh
index 9ee3699ea8..f75382d90a 100755
--- a/test/units/testsuite-64.sh
+++ b/test/units/testsuite-64.sh
@@ -390,10 +390,6 @@ testcase_btrfs_basic() {
uuid="deadbeef-dead-dead-beef-000000000000"
label="btrfs_root"
mkfs.btrfs -L "$label" -U "$uuid" "${devices[0]}"
- # We need to do some active waiting anyway, as it may take kernel a bit
- # to trigger the newly created btrfs
- helper_wait_for_dev "/dev/disk/by-uuid/$uuid"
- helper_wait_for_dev "/dev/disk/by-label/$label"
udevadm settle
btrfs filesystem show
test -e "/dev/disk/by-uuid/$uuid"
@@ -413,8 +409,6 @@ name="diskpart4", size=85M
EOF
udevadm settle
mkfs.btrfs -d single -m raid1 -L "$label" -U "$uuid" /dev/disk/by-partlabel/diskpart{1..4}
- helper_wait_for_dev "/dev/disk/by-uuid/$uuid"
- helper_wait_for_dev "/dev/disk/by-label/$label"
udevadm settle
btrfs filesystem show
test -e "/dev/disk/by-uuid/$uuid"
@@ -426,8 +420,6 @@ EOF
uuid="deadbeef-dead-dead-beef-000000000002"
label="btrfs_mdisk"
mkfs.btrfs -M -d raid10 -m raid10 -L "$label" -U "$uuid" "${devices[@]}"
- helper_wait_for_dev "/dev/disk/by-uuid/$uuid"
- helper_wait_for_dev "/dev/disk/by-label/$label"
udevadm settle
btrfs filesystem show
test -e "/dev/disk/by-uuid/$uuid"
@@ -465,8 +457,6 @@ EOF
ls -l /dev/mapper/encbtrfs{0..3}
# Create a multi-device btrfs filesystem on the LUKS devices
mkfs.btrfs -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3}
- helper_wait_for_dev "/dev/disk/by-uuid/$uuid"
- helper_wait_for_dev "/dev/disk/by-label/$label"
udevadm settle
btrfs filesystem show
test -e "/dev/disk/by-uuid/$uuid"
@@ -493,8 +483,6 @@ EOF
# Start the corresponding mount unit and check if the btrfs device was reconstructed
# correctly
systemctl start "${mpoint##*/}.mount"
- helper_wait_for_dev "/dev/disk/by-uuid/$uuid"
- helper_wait_for_dev "/dev/disk/by-label/$label"
btrfs filesystem show
test -e "/dev/disk/by-uuid/$uuid"
test -e "/dev/disk/by-label/$label"
diff --git a/test/units/testsuite-69.service b/test/units/testsuite-69.service
new file mode 100644
index 0000000000..3b2b81edc8
--- /dev/null
+++ b/test/units/testsuite-69.service
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=TEST-69-SHUTDOWN
+
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
diff --git a/tools/oss-fuzz.sh b/tools/oss-fuzz.sh
index 1d7412a81b..8ff3abefb7 100755
--- a/tools/oss-fuzz.sh
+++ b/tools/oss-fuzz.sh
@@ -35,7 +35,7 @@ else
apt-get update
apt-get install -y gperf m4 gettext python3-pip \
libcap-dev libmount-dev libkmod-dev \
- pkg-config wget python3-jinja2
+ pkg-config wget python3-jinja2 zipmerge
# gnu-efi is installed here to enable -Dgnu-efi behind which fuzz-bcd
# is hidden. It isn't linked against efi. It doesn't
@@ -80,7 +80,7 @@ rm -rf "$hosts"
# The seed corpus is a separate flat archive for each fuzzer,
# with a fixed name ${fuzzer}_seed_corpus.zip.
-for d in "$(dirname "$0")/../test/fuzz/fuzz-"*; do
+for d in test/fuzz/fuzz-*; do
zip -jqr "$OUT/$(basename "$d")_seed_corpus.zip" "$d"
done
@@ -98,3 +98,15 @@ wget -O "$OUT/fuzz-json.dict" https://raw.githubusercontent.com/rc0r/afl-fuzz/ma
find "$build" -maxdepth 1 -type f -executable -name "fuzz-*" -exec mv {} "$OUT" \;
find src -type f -name "fuzz-*.dict" -exec cp {} "$OUT" \;
cp src/fuzz/*.options "$OUT"
+
+if [[ "$MERGE_WITH_OSS_FUZZ_CORPORA" == "yes" ]]; then
+ for f in "$OUT/"fuzz-*; do
+ [[ -x "$f" ]] || continue
+ fuzzer=$(basename "$f")
+ t=$(mktemp)
+ if wget -O "$t" "https://storage.googleapis.com/systemd-backup.clusterfuzz-external.appspot.com/corpus/libFuzzer/systemd_${fuzzer}/public.zip"; then
+ zipmerge "$OUT/${fuzzer}_seed_corpus.zip" "$t"
+ fi
+ rm -rf "$t"
+ done
+fi
diff --git a/units/meson.build b/units/meson.build
index 3184433b8f..2bb0a8e845 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -220,6 +220,7 @@ in_units = [
['systemd-network-generator.service', ''],
['systemd-networkd.service', 'ENABLE_NETWORKD'],
['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD'],
+ ['systemd-networkd-wait-online@.service','ENABLE_NETWORKD'],
['systemd-nspawn@.service', ''],
['systemd-oomd.service', 'ENABLE_OOMD'],
['systemd-portabled.service', 'ENABLE_PORTABLED',
diff --git a/units/systemd-networkd-wait-online@.service.in b/units/systemd-networkd-wait-online@.service.in
new file mode 100644
index 0000000000..949695f53e
--- /dev/null
+++ b/units/systemd-networkd-wait-online@.service.in
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Wait for Network Interface %i to be Configured
+Documentation=man:systemd-networkd-wait-online.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+Requires=systemd-networkd.service
+After=systemd-networkd.service
+Before=network-online.target shutdown.target
+
+[Service]
+Type=oneshot
+ExecStart={{ROOTLIBEXECDIR}}/systemd-networkd-wait-online -i %i
+RemainAfterExit=yes
+
+[Install]
+WantedBy=network-online.target