diff options
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 Binary files differnew file mode 100644 index 0000000000..87345bf0ec --- /dev/null +++ b/test/fuzz/fuzz-dhcp-client/minimized-from-555a2b073b8d208655b68c294f8dfd592a11e50a diff --git a/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 b/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 Binary files differnew file mode 100644 index 0000000000..a51cf70dd0 --- /dev/null +++ b/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 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 Binary files differnew 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 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 |