From bd1529e51cd3af287b9523231aae4c49ca76c124 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Mar 30 2021 13:56:54 +0000 Subject: import systemd-239-44.el8 --- diff --git a/SOURCES/0430-device-make-sure-we-emit-PropertiesChanged-signal-on.patch b/SOURCES/0430-device-make-sure-we-emit-PropertiesChanged-signal-on.patch new file mode 100644 index 0000000..8d202a4 --- /dev/null +++ b/SOURCES/0430-device-make-sure-we-emit-PropertiesChanged-signal-on.patch @@ -0,0 +1,26 @@ +From 91dddaafe0b6fcc9c0a57d2feef599b82ce2a146 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Thu, 26 Mar 2020 13:34:20 +0100 +Subject: [PATCH] device: make sure we emit PropertiesChanged signal once we + set sysfs + +(cherry picked from commit 7c4d139485139eae95b17a1d54cb51ae958abd70) + +Related: #1793533 +--- + src/core/device.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/core/device.c b/src/core/device.c +index a2d00a0fbe..21fe3802bd 100644 +--- a/src/core/device.c ++++ b/src/core/device.c +@@ -81,6 +81,8 @@ static int device_set_sysfs(Device *d, const char *sysfs) { + } + + d->sysfs = TAKE_PTR(copy); ++ unit_add_to_dbus_queue(UNIT(d)); ++ + return 0; + } + diff --git a/SOURCES/0431-device-don-t-emit-PropetiesChanged-needlessly.patch b/SOURCES/0431-device-don-t-emit-PropetiesChanged-needlessly.patch new file mode 100644 index 0000000..e0986d5 --- /dev/null +++ b/SOURCES/0431-device-don-t-emit-PropetiesChanged-needlessly.patch @@ -0,0 +1,44 @@ +From a4cefc9f8bf24b2fdcc62cc0d2685698814374d4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Thu, 26 Mar 2020 13:35:11 +0100 +Subject: [PATCH] device: don't emit PropetiesChanged needlessly +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Functions called from device_setup_unit() already make sure that unit is +enqueued in case it is a new unit or properties exported on the bus have +changed. + +This should prevent unnecessary DBus wakeups and associated DBus traffic +when device_setup_unit() was called while reparsing /proc/self/mountinfo +due to the mountinfo notifications. Note that we parse +/proc/self/mountinfo quite often on the busy systems (e.g. k8s container +hosts) but majority of the time mounts didn't change, only some mount +got added. Thus we don't need to generate PropertiesChanged for devices +associated with the mounts that didn't change. + +Thanks to Renaud Métrich for debugging the +problem and providing draft version of the patch. + +(cherry picked from commit 2e129d5d6bd6bd8be4b5359e81a880cbf72a44b8) + +Resolves: #1793533 +--- + src/core/device.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/src/core/device.c b/src/core/device.c +index 21fe3802bd..021c28dfbd 100644 +--- a/src/core/device.c ++++ b/src/core/device.c +@@ -549,9 +549,6 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa + if (dev && device_is_bound_by_mounts(DEVICE(u), dev)) + device_upgrade_mount_deps(u); + +- /* Note that this won't dispatch the load queue, the caller has to do that if needed and appropriate */ +- unit_add_to_dbus_queue(u); +- + return 0; + + fail: diff --git a/SOURCES/0432-units-add-generic-boot-complete.target.patch b/SOURCES/0432-units-add-generic-boot-complete.target.patch new file mode 100644 index 0000000..912b230 --- /dev/null +++ b/SOURCES/0432-units-add-generic-boot-complete.target.patch @@ -0,0 +1,46 @@ +From dd573e5fbac858c20628052acfa19401d3e0d135 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 22 Jun 2018 12:52:28 +0200 +Subject: [PATCH] units: add generic boot-complete.target + +(cherry picked from commit 329d20db3cb02d789473b8f7e4a59526fcbf5728) + +Resolves: #1872243 +--- + units/boot-complete.target | 14 ++++++++++++++ + units/meson.build | 1 + + 2 files changed, 15 insertions(+) + create mode 100644 units/boot-complete.target + +diff --git a/units/boot-complete.target b/units/boot-complete.target +new file mode 100644 +index 0000000000..f0b9e57e7c +--- /dev/null ++++ b/units/boot-complete.target +@@ -0,0 +1,14 @@ ++# SPDX-License-Identifier: LGPL-2.1+ ++# ++# 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=Boot Completion Check ++Documentation=man:systemd.special(7) ++Requires=sysinit.target ++After=sysinit.target +diff --git a/units/meson.build b/units/meson.build +index e118d81888..a1cd2524dc 100644 +--- a/units/meson.build ++++ b/units/meson.build +@@ -3,6 +3,7 @@ + units = [ + ['basic.target', ''], + ['bluetooth.target', ''], ++ ['boot-complete.target', ''], + ['cryptsetup-pre.target', 'HAVE_LIBCRYPTSETUP'], + ['cryptsetup.target', 'HAVE_LIBCRYPTSETUP', + 'sysinit.target.wants/'], diff --git a/SOURCES/0433-man-document-new-boot-complete.target-unit.patch b/SOURCES/0433-man-document-new-boot-complete.target-unit.patch new file mode 100644 index 0000000..376958c --- /dev/null +++ b/SOURCES/0433-man-document-new-boot-complete.target-unit.patch @@ -0,0 +1,53 @@ +From 8ad89170001c9aba8849630ddb5da81d9e24a1bc Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 25 Jun 2018 17:21:34 +0200 +Subject: [PATCH] man: document new "boot-complete.target" unit + +(cherry picked from commit 82ea38258c0f4964c2f3ad3691c6e4554c4f0bb0) + +Related: #1872243 +--- + man/systemd.special.xml | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/man/systemd.special.xml b/man/systemd.special.xml +index fb12805fff..c9d4345016 100644 +--- a/man/systemd.special.xml ++++ b/man/systemd.special.xml +@@ -29,6 +29,7 @@ + cryptsetup-pre.target, + cryptsetup.target, + ctrl-alt-del.target, ++ boot-complete.target, + default.target, + emergency.target, + exit.target, +@@ -646,6 +647,28 @@ + + + ++ ++ boot-complete.target ++ ++ This target is intended as generic synchronization point for services that shall determine or act on ++ whether the boot process completed successfully. Order units that are required to succeed for a boot process ++ to be considered successful before this unit, and add a Requires= dependency from the ++ target unit to them. Order units that shall only run when the boot process is considered successful after the ++ target unit and pull in the target from it, also with Requires=. Note that by default this ++ target unit is not part of the initial boot transaction, but is supposed to be pulled in only if required by ++ units that want to run only on successful boots. ++ ++ See ++ systemd-boot-check-no-failures.service8 ++ for a service that implements a generic system health check and orders itself before ++ boot-complete.target. ++ ++ See ++ systemd-bless-boot.service8 ++ for a service that propagates boot success information to the boot loader, and orders itself after ++ boot-complete.target. ++ ++ + + syslog.socket + diff --git a/SOURCES/0434-core-make-sure-to-restore-the-control-command-id-too.patch b/SOURCES/0434-core-make-sure-to-restore-the-control-command-id-too.patch new file mode 100644 index 0000000..c5bd3e1 --- /dev/null +++ b/SOURCES/0434-core-make-sure-to-restore-the-control-command-id-too.patch @@ -0,0 +1,30 @@ +From 37f2576684d7494c916fd1f13275982f3c43f44f Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 22 Apr 2020 20:34:02 +0200 +Subject: [PATCH] core: make sure to restore the control command id, too + +Fixes: #15356 +(cherry picked from commit e9da62b18af647bfa73807e1c7fc3bfa4bb4b2ac) + +Resolves: #1829867 +--- + src/core/service.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index 89b41f6783..7cff419e4e 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -2703,9 +2703,10 @@ static int service_deserialize_exec_command(Unit *u, const char *key, const char + break; + } + +- if (command && control) ++ if (command && control) { + s->control_command = command; +- else if (command) ++ s->control_command_id = id; ++ } else if (command) + s->main_command = command; + else + log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed."); diff --git a/SOURCES/0435-cgroup-freezer-action-must-be-NOP-when-cgroup-v2-fre.patch b/SOURCES/0435-cgroup-freezer-action-must-be-NOP-when-cgroup-v2-fre.patch new file mode 100644 index 0000000..04c1355 --- /dev/null +++ b/SOURCES/0435-cgroup-freezer-action-must-be-NOP-when-cgroup-v2-fre.patch @@ -0,0 +1,48 @@ +From 45d093a37b6f8c2ceae9bfd090c5265f35413b46 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Tue, 8 Sep 2020 14:51:39 +0200 +Subject: [PATCH] cgroup: freezer action must be NOP when cgroup v2 freezer is + not available + +Low-level cgroup freezer state manipulation is invoked directly from the +job engine when we are about to execute the job in order to make sure +the unit is not frozen and job execution is not blocked because of +that. + +Currently with cgroup v1 we would needlessly do a bunch of work in the +function and even falsely update the freezer state. Don't do any of this +and skip the function silently when v2 freezer is not available. + +Following bug is fixed by this commit, + +$ systemd-run --unit foo.service /bin/sleep infinity +$ systemctl restart foo.service +$ systemctl show -p FreezerState foo.service + +Before (cgroup v1, i.e. full "legacy" mode): +FreezerState=thawing + +After: +FreezerState=running + +(cherry picked from commit 9a1e90aee556b7a30d87553a891a4175ae77ed68) + +Resolves: #1868831 +--- + src/core/cgroup.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index e0eb184fd2..f1ce070f9a 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -2936,6 +2936,9 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) { + assert(u); + assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW)); + ++ if (!cg_freezer_supported()) ++ return 0; ++ + if (!u->cgroup_realized) + return -EBUSY; + diff --git a/SOURCES/0436-logind-don-t-print-warning-when-user-.service-templa.patch b/SOURCES/0436-logind-don-t-print-warning-when-user-.service-templa.patch new file mode 100644 index 0000000..961f814 --- /dev/null +++ b/SOURCES/0436-logind-don-t-print-warning-when-user-.service-templa.patch @@ -0,0 +1,32 @@ +From 65e96327360ab41d44d5383dcecc82a19fad198c Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Fri, 22 Feb 2019 15:50:55 +0100 +Subject: [PATCH] logind: don't print warning when user@.service template is + masked + +User instance of systemd is optional feature and if user@.service +template is masked then administrator most likely doesn't want --user +instances of systemd for logged in users. We don't need to be verbose +about it. + +(cherry picked from commit 03b6fa0c5b51b0d39334ff6ba183a3391443bcf6) + +Resolves: #1880270 +--- + src/login/logind-user.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/login/logind-user.c b/src/login/logind-user.c +index 8c4cd54a29..56b8066f12 100644 +--- a/src/login/logind-user.c ++++ b/src/login/logind-user.c +@@ -326,7 +326,8 @@ static int user_start_service(User *u) { + &job); + if (r < 0) + /* we don't fail due to this, let's try to continue */ +- log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r)); ++ log_full_errno(sd_bus_error_has_name(&error, BUS_ERROR_UNIT_MASKED) ? LOG_DEBUG : LOG_WARNING, r, ++ "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r)); + else + u->service_job = job; + diff --git a/SOURCES/0437-build-use-simple-project-version-in-pkgconfig-files.patch b/SOURCES/0437-build-use-simple-project-version-in-pkgconfig-files.patch new file mode 100644 index 0000000..6ea481d --- /dev/null +++ b/SOURCES/0437-build-use-simple-project-version-in-pkgconfig-files.patch @@ -0,0 +1,80 @@ +From a6d76bf2d21e01a2e031e204966d946925ecc3f6 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Mon, 17 Aug 2020 14:29:04 +0200 +Subject: [PATCH] build: use simple project version in pkgconfig files + +Loosely based on commit a67c318df8800ba98d7361308937ed276dc73982. + +Resolves: #1862714 +--- + meson.build | 2 ++ + src/core/systemd.pc.in | 2 +- + src/libsystemd/libsystemd.pc.in | 2 +- + src/libudev/libudev.pc.in | 2 +- + src/udev/udev.pc.in | 2 +- + 5 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/meson.build b/meson.build +index 0ba3f924ea..65c1d0785e 100644 +--- a/meson.build ++++ b/meson.build +@@ -27,12 +27,14 @@ endif + # names, sometimes. Not all variables are included in every + # set. Ugh, ugh, ugh! + conf = configuration_data() ++conf.set_quoted('PROJECT_VERSION', meson.project_version()) + conf.set_quoted('PACKAGE_STRING', meson.project_name() + ' ' + dist_version) + conf.set_quoted('PACKAGE_VERSION', dist_version) + + substs = configuration_data() + substs.set('PACKAGE_URL', 'https://www.freedesktop.org/wiki/Software/systemd') + substs.set('PACKAGE_VERSION', dist_version) ++substs.set('PROJECT_VERSION', meson.project_version()) + + ##################################################################### + +diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in +index 655773ea8a..a350737cf2 100644 +--- a/src/core/systemd.pc.in ++++ b/src/core/systemd.pc.in +@@ -37,4 +37,4 @@ containeruidbasemax=@containeruidbasemax@ + Name: systemd + Description: systemd System and Service Manager + URL: @PACKAGE_URL@ +-Version: @PACKAGE_VERSION@ ++Version: @PROJECT_VERSION@ +diff --git a/src/libsystemd/libsystemd.pc.in b/src/libsystemd/libsystemd.pc.in +index c861905b67..85d6ebf293 100644 +--- a/src/libsystemd/libsystemd.pc.in ++++ b/src/libsystemd/libsystemd.pc.in +@@ -15,6 +15,6 @@ includedir=@includedir@ + Name: systemd + Description: systemd Library + URL: @PACKAGE_URL@ +-Version: @PACKAGE_VERSION@ ++Version: @PROJECT_VERSION@ + Libs: -L${libdir} -lsystemd + Cflags: -I${includedir} +diff --git a/src/libudev/libudev.pc.in b/src/libudev/libudev.pc.in +index 69f5c6463e..40b340362e 100644 +--- a/src/libudev/libudev.pc.in ++++ b/src/libudev/libudev.pc.in +@@ -14,6 +14,6 @@ includedir=@includedir@ + + Name: libudev + Description: Library to access udev device information +-Version: @PACKAGE_VERSION@ ++Version: @PROJECT_VERSION@ + Libs: -L${libdir} -ludev + Cflags: -I${includedir} +diff --git a/src/udev/udev.pc.in b/src/udev/udev.pc.in +index e384a6f7c9..5acbb2d01a 100644 +--- a/src/udev/udev.pc.in ++++ b/src/udev/udev.pc.in +@@ -1,5 +1,5 @@ + Name: udev + Description: udev +-Version: @PACKAGE_VERSION@ ++Version: @PROJECT_VERSION@ + + udevdir=@udevlibexecdir@ diff --git a/SOURCES/0438-basic-virt-try-the-proc-1-sched-hack-also-for-PID1.patch b/SOURCES/0438-basic-virt-try-the-proc-1-sched-hack-also-for-PID1.patch new file mode 100644 index 0000000..4237fa1 --- /dev/null +++ b/SOURCES/0438-basic-virt-try-the-proc-1-sched-hack-also-for-PID1.patch @@ -0,0 +1,57 @@ +From 2f584bd93d64a75ab11b5a5aa31d0b7145da5a86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 26 Apr 2019 13:37:31 +0200 +Subject: [PATCH] basic/virt: try the /proc/1/sched hack also for PID1 + +If a container manager does not set $container, we could end up +in a strange situation when detect-virt returns container-other when +run as non-pid-1 and none when run as pid-1. + +(cherry picked from commit 342bed02084c4396dd2f1054bd559bfb2699cfcb) +Resolves: #1868877 +--- + src/basic/virt.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/src/basic/virt.c b/src/basic/virt.c +index e05b3e6d99..dfa1525219 100644 +--- a/src/basic/virt.c ++++ b/src/basic/virt.c +@@ -427,7 +427,6 @@ finish: + } + + int detect_container(void) { +- + static const struct { + const char *value; + int id; +@@ -456,9 +455,15 @@ int detect_container(void) { + } + + if (getpid_cached() == 1) { +- /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */ +- ++ /* If we are PID 1 we can just check our own environment variable, and that's authoritative. ++ * We distinguish three cases: ++ * - the variable is not defined → we jump to other checks ++ * - the variable is defined to an empty value → we are not in a container ++ * - anything else → some container, either one of the known ones or "container-other" ++ */ + e = getenv("container"); ++ if (!e) ++ goto check_sched; + if (isempty(e)) { + r = VIRTUALIZATION_NONE; + goto finish; +@@ -486,8 +491,9 @@ int detect_container(void) { + if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */ + log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m"); + +- /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown +- * there is not 1, we know we are in a PID namespace. and hence a container. */ ++ /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. If the PID ++ * shown there is not 1, we know we are in a PID namespace and hence a container. */ ++ check_sched: + r = read_one_line_file("/proc/1/sched", &m); + if (r >= 0) { + const char *t; diff --git a/SOURCES/0439-seccomp-rework-how-the-S-UG-ID-filter-is-installed.patch b/SOURCES/0439-seccomp-rework-how-the-S-UG-ID-filter-is-installed.patch new file mode 100644 index 0000000..f824c4d --- /dev/null +++ b/SOURCES/0439-seccomp-rework-how-the-S-UG-ID-filter-is-installed.patch @@ -0,0 +1,287 @@ +From 8cc497e735104080f6830a8f468b2724ae372990 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 3 Apr 2019 13:11:00 +0200 +Subject: [PATCH] seccomp: rework how the S[UG]ID filter is installed + +If we know that a syscall is undefined on the given architecture, don't +even try to add it. + +Try to install the filter even if some syscalls fail. Also use a helper +function to make the whole a bit less magic. + +This allows the S[UG]ID test to pass on arm64. + +(cherry picked from commit da4dc9a6748797e804b6bc92ad513d509abf581c) + +Resolves: #1860374 +--- + src/shared/seccomp-util.c | 244 +++++++++++++++++++++----------------- + 1 file changed, 138 insertions(+), 106 deletions(-) + +diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c +index fd46b9f88d..d91fb4e269 100644 +--- a/src/shared/seccomp-util.c ++++ b/src/shared/seccomp-util.c +@@ -1750,9 +1750,139 @@ int seccomp_lock_personality(unsigned long personality) { + return 0; + } + ++static int seccomp_restrict_sxid(scmp_filter_ctx seccomp, mode_t m) { ++ /* Checks the mode_t parameter of the following system calls: ++ * ++ * → chmod() + fchmod() + fchmodat() ++ * → open() + creat() + openat() ++ * → mkdir() + mkdirat() ++ * → mknod() + mknodat() ++ * ++ * Returns error if *everything* failed, and 0 otherwise. ++ */ ++ int r = 0; ++ bool any = false; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(chmod), ++ 1, ++ SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for chmod: %m"); ++ else ++ any = true; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(fchmod), ++ 1, ++ SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for fchmod: %m"); ++ else ++ any = true; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(fchmodat), ++ 1, ++ SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for fchmodat: %m"); ++ else ++ any = true; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(mkdir), ++ 1, ++ SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for mkdir: %m"); ++ else ++ any = true; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(mkdirat), ++ 1, ++ SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for mkdirat: %m"); ++ else ++ any = true; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(mknod), ++ 1, ++ SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for mknod: %m"); ++ else ++ any = true; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(mknodat), ++ 1, ++ SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for mknodat: %m"); ++ else ++ any = true; ++ ++#if SCMP_SYS(open) > 0 ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(open), ++ 2, ++ SCMP_A1(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT), ++ SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for open: %m"); ++ else ++ any = true; ++#endif ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(openat), ++ 2, ++ SCMP_A2(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT), ++ SCMP_A3(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for openat: %m"); ++ else ++ any = true; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(creat), ++ 1, ++ SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add filter for creat: %m"); ++ else ++ any = true; ++ ++ return any ? 0 : r; ++} ++ + int seccomp_restrict_suid_sgid(void) { + uint32_t arch; +- int r; ++ int r, k; + + SECCOMP_FOREACH_LOCAL_ARCH(arch) { + _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; +@@ -1761,114 +1891,16 @@ int seccomp_restrict_suid_sgid(void) { + if (r < 0) + return r; + +- /* Checks the mode_t parameter of the following system calls: +- * +- * → chmod() + fchmod() + fchmodat() +- * → open() + creat() + openat() +- * → mkdir() + mkdirat() +- * → mknod() + mknodat() +- */ +- +- for (unsigned bit = 0; bit < 2; bit ++) { +- /* Block S_ISUID in the first iteration, S_ISGID in the second */ +- mode_t m = bit == 0 ? S_ISUID : S_ISGID; +- +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(chmod), +- 1, +- SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; +- +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(fchmod), +- 1, +- SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; +- +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(fchmodat), +- 1, +- SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; +- +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(mkdir), +- 1, +- SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; +- +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(mkdirat), +- 1, +- SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; +- +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(mknod), +- 1, +- SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; +- +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(mknodat), +- 1, +- SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; +- +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(open), +- 2, +- SCMP_A1(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT), +- SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; ++ r = seccomp_restrict_sxid(seccomp, S_ISUID); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add suid rule for architecture %s, ignoring: %m", seccomp_arch_to_string(arch)); + +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(openat), +- 2, +- SCMP_A2(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT), +- SCMP_A3(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; ++ k = seccomp_restrict_sxid(seccomp, S_ISGID); ++ if (k < 0) ++ log_debug_errno(r, "Failed to add sgid rule for architecture %s, ignoring: %m", seccomp_arch_to_string(arch)); + +- r = seccomp_rule_add_exact( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(creat), +- 1, +- SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); +- if (r < 0) +- break; +- } +- if (r < 0) { +- log_debug_errno(r, "Failed to add suid/sgid rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ if (r < 0 && k < 0) + continue; +- } + + r = seccomp_load(seccomp); + if (IN_SET(r, -EPERM, -EACCES)) diff --git a/SOURCES/0440-vconsole-setup-downgrade-log-message-when-setting-fo.patch b/SOURCES/0440-vconsole-setup-downgrade-log-message-when-setting-fo.patch new file mode 100644 index 0000000..09874a7 --- /dev/null +++ b/SOURCES/0440-vconsole-setup-downgrade-log-message-when-setting-fo.patch @@ -0,0 +1,91 @@ +From 860749038f508617c8fc31b8292b4019b1e621ba Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Thu, 16 Jul 2020 21:22:37 +0200 +Subject: [PATCH] vconsole-setup: downgrade log message when setting font fails + on dummy console + +Since commit 883eb9be985fd86d9cabe967eeeab91cdd396a81, vconsole-setup might be +called again to operate on dummy console where font operations are not +supported but where it's still important to have the correct keymap set [0][1]. + +vconsole-setup is mainly called by udev but can also be run via a dependency of +an early service. Both cases might end up calling vconsole-setup on the dummy +console. + +The first case can happen during early boot even on systems that use (instead +of the dummy console) a "simple" video console driver supporting font +operations (such as vgacon) until a more specific driver (such as i915) takes +the console over. While this is happening vgacon is deactivated and temporarly +replaced by the dummy console [2]. + +There are also other cases where systemd-vconsole-setup might be called on +dummy console especially during (very) early boot. Indeed +systemd-vconsole-setup.service might be pulled in by early interactive services +such as 'dracut-cmdline-ask.service` which is run before udev. + +If that happens on platforms with no grapical HWs (such as embedded ARM) or +with dummy console initially installed until a driver takes over (like Xen and +xen-fbfront) then setting font will fail. + +Therefore this patch downgrades the log message emitted when setting font fails +to LOG_DEBUG and when font operations is not implemented like it's the case for +the dummy console. + +Fixes: #16406. + +[0] https://github.com/systemd/systemd/issues/10826 +[1] https://bugzilla.redhat.com/show_bug.cgi?id=1652473 +[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/vga/vgaarb.c?h=v5.7#n204 + +(cherry picked from commit 0ef1adf51274960358e852d3bc36ae6c288a70d9) + +Resolves: #1889996 +--- + src/vconsole/vconsole-setup.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c +index f162d29220..1b406c0bc5 100644 +--- a/src/vconsole/vconsole-setup.c ++++ b/src/vconsole/vconsole-setup.c +@@ -222,6 +222,7 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { + _cleanup_free_ struct unipair* unipairs = NULL; + _cleanup_free_ void *fontbuf = NULL; + unsigned i; ++ int log_level; + int r; + + unipairs = new(struct unipair, USHRT_MAX); +@@ -230,11 +231,20 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { + return; + } + ++ log_level = LOG_WARNING; ++ + /* get metadata of the current font (width, height, count) */ + r = ioctl(src_fd, KDFONTOP, &cfo); +- if (r < 0) +- log_warning_errno(errno, "KD_FONT_OP_GET failed while trying to get the font metadata: %m"); +- else { ++ if (r < 0) { ++ /* We might be called to operate on the dummy console (to setup keymap ++ * mainly) when fbcon deferred takeover is used for example. In such case, ++ * setting font is not supported and is expected to fail. */ ++ if (errno == ENOSYS) ++ log_level = LOG_DEBUG; ++ ++ log_full_errno(log_level, errno, ++ "KD_FONT_OP_GET failed while trying to get the font metadata: %m"); ++ } else { + /* verify parameter sanity first */ + if (cfo.width > 32 || cfo.height > 32 || cfo.charcount > 512) + log_warning("Invalid font metadata - width: %u (max 32), height: %u (max 32), count: %u (max 512)", +@@ -269,7 +279,7 @@ static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { + } + + if (cfo.op != KD_FONT_OP_SET) +- log_warning("Fonts will not be copied to remaining consoles"); ++ log_full(log_level, "Fonts will not be copied to remaining consoles"); + + for (i = 1; i <= 63; i++) { + char ttyname[sizeof("/dev/tty63")]; diff --git a/SOURCES/0441-units-fix-systemd.special-man-page-reference-in-syst.patch b/SOURCES/0441-units-fix-systemd.special-man-page-reference-in-syst.patch new file mode 100644 index 0000000..4d6bd2e --- /dev/null +++ b/SOURCES/0441-units-fix-systemd.special-man-page-reference-in-syst.patch @@ -0,0 +1,26 @@ +From 46fa8ff1a62e3334582a971cc6bbd9b8a16680d5 Mon Sep 17 00:00:00 2001 +From: Michael Biebl +Date: Thu, 7 Mar 2019 12:02:53 +0100 +Subject: [PATCH] units: fix systemd.special man page reference in + system-update-cleanup.service + +(cherry picked from commit faab72d16b310c17be4b908cfe15eca122d16ae4) + +Resolves: #1871827 +--- + units/system-update-cleanup.service | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/units/system-update-cleanup.service b/units/system-update-cleanup.service +index 58baab3023..d5eca2546b 100644 +--- a/units/system-update-cleanup.service ++++ b/units/system-update-cleanup.service +@@ -9,7 +9,7 @@ + + [Unit] + Description=Remove the Offline System Updates symlink +-Documentation=man:systemd.special(5) man:systemd.offline-updates(7) ++Documentation=man:systemd.special(7) man:systemd.offline-updates(7) + After=system-update.target + DefaultDependencies=no + Conflicts=shutdown.target diff --git a/SOURCES/0442-units-drop-reference-to-sushell-man-page.patch b/SOURCES/0442-units-drop-reference-to-sushell-man-page.patch new file mode 100644 index 0000000..d998724 --- /dev/null +++ b/SOURCES/0442-units-drop-reference-to-sushell-man-page.patch @@ -0,0 +1,27 @@ +From 5aa59d172189adcbd7f9dedb3b909c6bf9b609f2 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 29 Apr 2019 16:10:51 +0200 +Subject: [PATCH] units: drop reference to sushell man page + +sushell was a Fedoraism, and has been removed since. Hence our upstream +unit files shouldn't reference it either. + +(cherry picked from commit 6dc14d73664390682d47d7e5bcbdbb362d04f623) + +Resolves: #1871827 +--- + units/debug-shell.service.in | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/units/debug-shell.service.in b/units/debug-shell.service.in +index 1127e68b63..9f3868e106 100644 +--- a/units/debug-shell.service.in ++++ b/units/debug-shell.service.in +@@ -9,7 +9,6 @@ + + [Unit] + Description=Early root shell on @DEBUGTTY@ FOR DEBUGGING ONLY +-Documentation=man:sushell(8) + Documentation=man:systemd-debug-generator(8) + DefaultDependencies=no + IgnoreOnIsolate=yes diff --git a/SOURCES/0443-sd-bus-break-the-loop-in-bus_ensure_running-if-the-b.patch b/SOURCES/0443-sd-bus-break-the-loop-in-bus_ensure_running-if-the-b.patch new file mode 100644 index 0000000..a53c450 --- /dev/null +++ b/SOURCES/0443-sd-bus-break-the-loop-in-bus_ensure_running-if-the-b.patch @@ -0,0 +1,44 @@ +From 6a50c735a3bbf98d06fbfa7815f7bdc14ea96f9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 14 Oct 2020 14:03:13 +0200 +Subject: [PATCH] sd-bus: break the loop in bus_ensure_running() if the bus is + not connecting + +This might fix #17025: +> the call trace is +> bus_ensure_running -> sd_bus_process -> bus_process_internal -> process_closeing --> sd_bus_close +> | +> \-> process_match + +We ended doing callouts to the Disconnected matches from bus_ensure_running() +and shouldn't. bus_ensure_running() should never do callouts. This change +should fix this however: once we notice that the connection is going down we +will now fail instantly with ENOTOCONN instead of calling any callbacks. + +(cherry picked from commit 93a59b1ae5d3bcb0ec1488ebc13d0d1ff4d1729a) + +Resolves: #1885553 +--- + src/libsystemd/sd-bus/sd-bus.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index a3509f7e89..c65e24b2d1 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -2059,12 +2059,13 @@ int bus_ensure_running(sd_bus *bus) { + + assert(bus); + +- if (IN_SET(bus->state, BUS_UNSET, BUS_CLOSED, BUS_CLOSING)) +- return -ENOTCONN; + if (bus->state == BUS_RUNNING) + return 1; + + for (;;) { ++ if (IN_SET(bus->state, BUS_UNSET, BUS_CLOSED, BUS_CLOSING)) ++ return -ENOTCONN; ++ + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; diff --git a/SOURCES/0444-core-add-new-API-for-enqueing-a-job-with-returning-t.patch b/SOURCES/0444-core-add-new-API-for-enqueing-a-job-with-returning-t.patch new file mode 100644 index 0000000..ccd3ae1 --- /dev/null +++ b/SOURCES/0444-core-add-new-API-for-enqueing-a-job-with-returning-t.patch @@ -0,0 +1,831 @@ +From 7155c010ef8c620295d230c284849636c07b40c0 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 22 Mar 2019 20:57:30 +0100 +Subject: [PATCH] core: add new API for enqueing a job with returning the + transaction data + +(cherry picked from commit 50cbaba4fe5a32850998682699322d012e597e4a) + +Related: #846319 +--- + src/analyze/analyze-verify.c | 2 +- + src/core/automount.c | 4 +- + src/core/dbus-manager.c | 23 +++++- + src/core/dbus-unit.c | 153 +++++++++++++++++++++++++++++++---- + src/core/dbus-unit.h | 8 +- + src/core/dbus.c | 2 +- + src/core/device.c | 2 +- + src/core/emergency-action.c | 5 +- + src/core/main.c | 4 +- + src/core/manager.c | 38 +++++---- + src/core/manager.h | 6 +- + src/core/path.c | 2 +- + src/core/service.c | 2 +- + src/core/socket.c | 4 +- + src/core/timer.c | 2 +- + src/core/transaction.c | 22 ++++- + src/core/transaction.h | 2 +- + src/core/unit.c | 16 ++-- + src/test/test-engine.c | 20 ++--- + 19 files changed, 244 insertions(+), 73 deletions(-) + +diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c +index ed369532d4..1e143511b2 100644 +--- a/src/analyze/analyze-verify.c ++++ b/src/analyze/analyze-verify.c +@@ -205,7 +205,7 @@ static int verify_unit(Unit *u, bool check_man) { + unit_dump(u, stdout, "\t"); + + log_unit_debug(u, "Creating %s/start job", u->id); +- r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, &err, NULL); ++ r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, NULL, &err, NULL); + if (r < 0) + log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r)); + +diff --git a/src/core/automount.c b/src/core/automount.c +index b1a155d8d4..76e70f4dac 100644 +--- a/src/core/automount.c ++++ b/src/core/automount.c +@@ -776,7 +776,7 @@ static void automount_enter_running(Automount *a) { + goto fail; + } + +- r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); ++ r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL); + if (r < 0) { + log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r)); + goto fail; +@@ -1032,7 +1032,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo + goto fail; + } + +- r = manager_add_job(UNIT(a)->manager, JOB_STOP, trigger, JOB_REPLACE, &error, NULL); ++ r = manager_add_job(UNIT(a)->manager, JOB_STOP, trigger, JOB_REPLACE, NULL, &error, NULL); + if (r < 0) { + log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r)); + goto fail; +diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c +index a0777f63d5..0a1d3df42f 100644 +--- a/src/core/dbus-manager.c ++++ b/src/core/dbus-manager.c +@@ -549,6 +549,26 @@ static int method_reload_or_try_restart_unit(sd_bus_message *message, void *user + return method_start_unit_generic(message, userdata, JOB_TRY_RESTART, true, error); + } + ++static int method_enqueue_unit_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { ++ Manager *m = userdata; ++ const char *name; ++ Unit *u; ++ int r; ++ ++ assert(message); ++ assert(m); ++ ++ r = sd_bus_message_read(message, "s", &name); ++ if (r < 0) ++ return r; ++ ++ r = manager_load_unit(m, name, NULL, error, &u); ++ if (r < 0) ++ return r; ++ ++ return bus_unit_method_enqueue_job(message, u, error); ++} ++ + static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *old_name; +@@ -978,7 +998,7 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata, + return r; + + /* Finally, start it */ +- return bus_unit_queue_job(message, u, JOB_START, mode, false, error); ++ return bus_unit_queue_job(message, u, JOB_START, mode, 0, error); + } + + static int method_get_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { +@@ -2547,6 +2567,7 @@ const sd_bus_vtable bus_manager_vtable[] = { + SD_BUS_METHOD("TryRestartUnit", "ss", "o", method_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReloadOrRestartUnit", "ss", "o", method_reload_or_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReloadOrTryRestartUnit", "ss", "o", method_reload_or_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("EnqueueUnitJob", "sss", "uososa(uosos)", method_enqueue_unit_job, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("FreezeUnit", "s", NULL, method_freeze_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ThawUnit", "s", NULL, method_thaw_unit, SD_BUS_VTABLE_UNPRIVILEGED), +diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c +index ce81103e92..549a166abc 100644 +--- a/src/core/dbus-unit.c ++++ b/src/core/dbus-unit.c +@@ -314,6 +314,14 @@ static int bus_verify_manage_units_async_full( + error); + } + ++static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = { ++ [JOB_START] = N_("Authentication is required to start '$(unit)'."), ++ [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."), ++ [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."), ++ [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."), ++ [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."), ++}; ++ + int bus_unit_method_start_generic( + sd_bus_message *message, + Unit *u, +@@ -324,13 +332,6 @@ int bus_unit_method_start_generic( + const char *smode; + JobMode mode; + _cleanup_free_ char *verb = NULL; +- static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = { +- [JOB_START] = N_("Authentication is required to start '$(unit)'."), +- [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."), +- [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."), +- [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."), +- [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."), +- }; + int r; + + assert(message); +@@ -372,7 +373,8 @@ int bus_unit_method_start_generic( + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + +- return bus_unit_queue_job(message, u, job_type, mode, reload_if_possible, error); ++ return bus_unit_queue_job(message, u, job_type, mode, ++ reload_if_possible ? BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE : 0, error); + } + + static int method_start(sd_bus_message *message, void *userdata, sd_bus_error *error) { +@@ -403,6 +405,62 @@ static int method_reload_or_try_restart(sd_bus_message *message, void *userdata, + return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, true, error); + } + ++int bus_unit_method_enqueue_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { ++ BusUnitQueueFlags flags = BUS_UNIT_QUEUE_VERBOSE_REPLY; ++ const char *jtype, *smode; ++ Unit *u = userdata; ++ JobType type; ++ JobMode mode; ++ int r; ++ ++ assert(message); ++ assert(u); ++ ++ r = sd_bus_message_read(message, "ss", &jtype, &smode); ++ if (r < 0) ++ return r; ++ ++ /* Parse the two magic reload types "reload-or-…" manually */ ++ if (streq(jtype, "reload-or-restart")) { ++ type = JOB_RESTART; ++ flags |= BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE; ++ } else if (streq(jtype, "reload-or-try-restart")) { ++ type = JOB_TRY_RESTART; ++ flags |= BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE; ++ } else { ++ /* And the rest generically */ ++ type = job_type_from_string(jtype); ++ if (type < 0) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job type %s invalid", jtype); ++ } ++ ++ mode = job_mode_from_string(smode); ++ if (mode < 0) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode); ++ ++ r = mac_selinux_unit_access_check( ++ u, message, ++ job_type_to_access_method(type), ++ error); ++ if (r < 0) ++ return r; ++ ++ r = bus_verify_manage_units_async_full( ++ u, ++ jtype, ++ CAP_SYS_ADMIN, ++ polkit_message_for_job[type], ++ true, ++ message, ++ 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 */ ++ ++ return bus_unit_queue_job(message, u, type, mode, flags, error); ++} ++ + int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Unit *u = userdata; + const char *swho; +@@ -722,6 +780,7 @@ const sd_bus_vtable bus_unit_vtable[] = { + SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), ++ SD_BUS_METHOD("EnqueueJob", "ss", "uososa(uosos)", bus_unit_method_enqueue_job, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED), +@@ -1354,11 +1413,14 @@ int bus_unit_queue_job( + Unit *u, + JobType type, + JobMode mode, +- bool reload_if_possible, ++ BusUnitQueueFlags flags, + sd_bus_error *error) { + +- _cleanup_free_ char *path = NULL; +- Job *j; ++ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; ++ _cleanup_free_ char *job_path = NULL, *unit_path = NULL; ++ _cleanup_(set_freep) Set *affected = NULL; ++ Iterator i; ++ Job *j, *a; + int r; + + assert(message); +@@ -1373,7 +1435,7 @@ int bus_unit_queue_job( + if (r < 0) + return r; + +- if (reload_if_possible && unit_can_reload(u)) { ++ if (FLAGS_SET(flags, BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE) && unit_can_reload(u)) { + if (type == JOB_RESTART) + type = JOB_RELOAD_OR_START; + else if (type == JOB_TRY_RESTART) +@@ -1391,7 +1453,13 @@ int bus_unit_queue_job( + (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start)) + return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id); + +- r = manager_add_job(u->manager, type, u, mode, error, &j); ++ if (FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY)) { ++ affected = set_new(NULL); ++ if (!affected) ++ return -ENOMEM; ++ } ++ ++ r = manager_add_job(u->manager, type, u, mode, affected, error, &j); + if (r < 0) + return r; + +@@ -1399,11 +1467,64 @@ int bus_unit_queue_job( + if (r < 0) + return r; + +- path = job_dbus_path(j); +- if (!path) ++ job_path = job_dbus_path(j); ++ if (!job_path) + return -ENOMEM; + +- return sd_bus_reply_method_return(message, "o", path); ++ /* The classic response is just a job object path */ ++ if (!FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY)) ++ return sd_bus_reply_method_return(message, "o", job_path); ++ ++ /* In verbose mode respond with the anchor job plus everything that has been affected */ ++ r = sd_bus_message_new_method_return(message, &reply); ++ if (r < 0) ++ return r; ++ ++ unit_path = unit_dbus_path(j->unit); ++ if (!unit_path) ++ return -ENOMEM; ++ ++ r = sd_bus_message_append(reply, "uosos", ++ j->id, job_path, ++ j->unit->id, unit_path, ++ job_type_to_string(j->type)); ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_message_open_container(reply, 'a', "(uosos)"); ++ if (r < 0) ++ return r; ++ ++ SET_FOREACH(a, affected, i) { ++ ++ if (a->id == j->id) ++ continue; ++ ++ /* Free paths from previous iteration */ ++ job_path = mfree(job_path); ++ unit_path = mfree(unit_path); ++ ++ job_path = job_dbus_path(a); ++ if (!job_path) ++ return -ENOMEM; ++ ++ unit_path = unit_dbus_path(a->unit); ++ if (!unit_path) ++ return -ENOMEM; ++ ++ r = sd_bus_message_append(reply, "(uosos)", ++ a->id, job_path, ++ a->unit->id, unit_path, ++ job_type_to_string(a->type)); ++ if (r < 0) ++ return r; ++ } ++ ++ r = sd_bus_message_close_container(reply); ++ if (r < 0) ++ return r; ++ ++ return sd_bus_send(NULL, reply, NULL); + } + + static int bus_unit_set_live_property( +diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h +index 39aa1bb53c..d298fcc99e 100644 +--- a/src/core/dbus-unit.h ++++ b/src/core/dbus-unit.h +@@ -15,6 +15,7 @@ int bus_unit_send_pending_freezer_message(Unit *u); + void bus_unit_send_removed_signal(Unit *u); + + int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error); ++int bus_unit_method_enqueue_job(sd_bus_message *message, void *userdata, sd_bus_error *error); + int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); + int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error); + +@@ -27,7 +28,12 @@ int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error + int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error); + int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error); + +-int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error); ++typedef enum BusUnitQueueFlags { ++ BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0, ++ BUS_UNIT_QUEUE_VERBOSE_REPLY = 1 << 1, ++} BusUnitQueueFlags; ++ ++int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, BusUnitQueueFlags flags, sd_bus_error *error); + int bus_unit_validate_load_state(Unit *u, sd_bus_error *error); + + int bus_unit_track_add_name(Unit *u, const char *name); +diff --git a/src/core/dbus.c b/src/core/dbus.c +index b69c11c519..584a8a1b01 100644 +--- a/src/core/dbus.c ++++ b/src/core/dbus.c +@@ -176,7 +176,7 @@ static int signal_activation_request(sd_bus_message *message, void *userdata, sd + goto failed; + } + +- r = manager_add_job(m, JOB_START, u, JOB_REPLACE, &error, NULL); ++ r = manager_add_job(m, JOB_START, u, JOB_REPLACE, NULL, &error, NULL); + if (r < 0) + goto failed; + +diff --git a/src/core/device.c b/src/core/device.c +index 021c28dfbd..cb8b66dfc5 100644 +--- a/src/core/device.c ++++ b/src/core/device.c +@@ -419,7 +419,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) { + if (strv_contains(d->wants_property, *i)) /* Was this unit already listed before? */ + continue; + +- r = manager_add_job_by_name(u->manager, JOB_START, *i, JOB_FAIL, &error, NULL); ++ r = manager_add_job_by_name(u->manager, JOB_START, *i, JOB_FAIL, NULL, &error, NULL); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to enqueue SYSTEMD_WANTS= job, ignoring: %s", bus_error_message(&error, r)); + } +diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c +index 76e1124cff..766a3b4d2b 100644 +--- a/src/core/emergency-action.c ++++ b/src/core/emergency-action.c +@@ -54,8 +54,7 @@ int emergency_action( + log_and_status(m, "Rebooting", reason); + + (void) update_reboot_parameter_and_warn(reboot_arg); +- (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); +- ++ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL); + break; + + case EMERGENCY_ACTION_REBOOT_FORCE: +@@ -83,7 +82,7 @@ int emergency_action( + + case EMERGENCY_ACTION_POWEROFF: + log_and_status(m, "Powering off", reason); +- (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); ++ (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL); + break; + + case EMERGENCY_ACTION_POWEROFF_FORCE: +diff --git a/src/core/main.c b/src/core/main.c +index 25536054b3..d897155644 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1952,13 +1952,13 @@ static int do_queue_default_job( + + assert(target->load_state == UNIT_LOADED); + +- r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, &error, &default_unit_job); ++ r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, NULL, &error, &default_unit_job); + if (r == -EPERM) { + log_debug_errno(r, "Default target could not be isolated, starting instead: %s", bus_error_message(&error, r)); + + sd_bus_error_free(&error); + +- r = manager_add_job(m, JOB_START, target, JOB_REPLACE, &error, &default_unit_job); ++ r = manager_add_job(m, JOB_START, target, JOB_REPLACE, NULL, &error, &default_unit_job); + if (r < 0) { + *ret_error_message = "Failed to start default target"; + return log_emergency_errno(r, "Failed to start default target: %s", bus_error_message(&error, r)); +diff --git a/src/core/manager.c b/src/core/manager.c +index 4c04896aaa..012615e537 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -1242,7 +1242,7 @@ static unsigned manager_dispatch_stop_when_unneeded_queue(Manager *m) { + } + + /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ +- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); ++ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, NULL, &error, NULL); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); + } +@@ -1685,9 +1685,17 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { + return 0; + } + +-int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) { +- int r; ++int manager_add_job( ++ Manager *m, ++ JobType type, ++ Unit *unit, ++ JobMode mode, ++ Set *affected_jobs, ++ sd_bus_error *error, ++ Job **ret) { ++ + Transaction *tr; ++ int r; + + assert(m); + assert(type < _JOB_TYPE_MAX); +@@ -1695,10 +1703,10 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_e + assert(mode < _JOB_MODE_MAX); + + if (mode == JOB_ISOLATE && type != JOB_START) +- return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start."); ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start."); + + if (mode == JOB_ISOLATE && !unit->allow_isolate) +- return sd_bus_error_setf(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); ++ return sd_bus_error_setf(error, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); + + log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode)); + +@@ -1710,7 +1718,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_e + + r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, false, + IN_SET(mode, JOB_IGNORE_DEPENDENCIES, JOB_IGNORE_REQUIREMENTS), +- mode == JOB_IGNORE_DEPENDENCIES, e); ++ mode == JOB_IGNORE_DEPENDENCIES, error); + if (r < 0) + goto tr_abort; + +@@ -1720,7 +1728,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_e + goto tr_abort; + } + +- r = transaction_activate(tr, m, mode, e); ++ r = transaction_activate(tr, m, mode, affected_jobs, error); + if (r < 0) + goto tr_abort; + +@@ -1728,8 +1736,8 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_e + "Enqueued job %s/%s as %u", unit->id, + job_type_to_string(type), (unsigned) tr->anchor_job->id); + +- if (_ret) +- *_ret = tr->anchor_job; ++ if (ret) ++ *ret = tr->anchor_job; + + transaction_free(tr); + return 0; +@@ -1740,7 +1748,7 @@ tr_abort: + return r; + } + +-int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) { ++int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, sd_bus_error *e, Job **ret) { + Unit *unit = NULL; /* just to appease gcc, initialization is not really necessary */ + int r; + +@@ -1754,10 +1762,10 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode + return r; + assert(unit); + +- return manager_add_job(m, type, unit, mode, e, ret); ++ return manager_add_job(m, type, unit, mode, affected_jobs, e, ret); + } + +-int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret) { ++int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, Job **ret) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + +@@ -1766,7 +1774,7 @@ int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, + assert(name); + assert(mode < _JOB_MODE_MAX); + +- r = manager_add_job_by_name(m, type, name, mode, &error, ret); ++ r = manager_add_job_by_name(m, type, name, mode, affected_jobs, &error, ret); + if (r < 0) + return log_warning_errno(r, "Failed to enqueue %s job for %s: %s", job_mode_to_string(mode), name, bus_error_message(&error, r)); + +@@ -1794,7 +1802,7 @@ int manager_propagate_reload(Manager *m, Unit *unit, JobMode mode, sd_bus_error + /* Failure in adding individual dependencies is ignored, so this always succeeds. */ + transaction_add_propagate_reload_jobs(tr, unit, tr->anchor_job, mode == JOB_IGNORE_DEPENDENCIES, e); + +- r = transaction_activate(tr, m, mode, e); ++ r = transaction_activate(tr, m, mode, NULL, e); + if (r < 0) + goto tr_abort; + +@@ -2512,7 +2520,7 @@ static void manager_start_target(Manager *m, const char *name, JobMode mode) { + + log_debug("Activating special unit %s", name); + +- r = manager_add_job_by_name(m, JOB_START, name, mode, &error, NULL); ++ r = manager_add_job_by_name(m, JOB_START, name, mode, NULL, &error, NULL); + if (r < 0) + log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r)); + } +diff --git a/src/core/manager.h b/src/core/manager.h +index 40568d3c8b..c4b8e80093 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -397,9 +397,9 @@ int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_err + int manager_load_startable_unit_or_warn(Manager *m, const char *name, const char *path, Unit **ret); + int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u); + +-int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret); +-int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **_ret); +-int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret); ++int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, Set *affected_jobs, sd_bus_error *e, Job **_ret); ++int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, sd_bus_error *e, Job **_ret); ++int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, Job **ret); + int manager_propagate_reload(Manager *m, Unit *unit, JobMode mode, sd_bus_error *e); + + void manager_dump_units(Manager *s, FILE *f, const char *prefix); +diff --git a/src/core/path.c b/src/core/path.c +index dda4a3036b..ed40bc6c19 100644 +--- a/src/core/path.c ++++ b/src/core/path.c +@@ -474,7 +474,7 @@ static void path_enter_running(Path *p) { + return; + } + +- r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); ++ r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL); + if (r < 0) + goto fail; + +diff --git a/src/core/service.c b/src/core/service.c +index 7cff419e4e..5e3e75b5ae 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -2176,7 +2176,7 @@ static void service_enter_restart(Service *s) { + * restarted. We use JOB_RESTART (instead of the more obvious + * JOB_START) here so that those dependency jobs will be added + * as well. */ +- r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_REPLACE, &error, NULL); ++ r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_REPLACE, NULL, &error, NULL); + if (r < 0) + goto fail; + +diff --git a/src/core/socket.c b/src/core/socket.c +index 7c6d3dfad1..fe061eb73b 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -2274,7 +2274,7 @@ static void socket_enter_running(Socket *s, int cfd) { + goto fail; + } + +- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, &error, NULL); ++ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, NULL, &error, NULL); + if (r < 0) + goto fail; + } +@@ -2349,7 +2349,7 @@ static void socket_enter_running(Socket *s, int cfd) { + + service->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */ + +- r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); ++ r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, NULL, &error, NULL); + if (r < 0) { + /* We failed to activate the new service, but it still exists. Let's make sure the service + * closes and forgets the connection fd again, immediately. */ +diff --git a/src/core/timer.c b/src/core/timer.c +index 2876d54a59..281ac7f97f 100644 +--- a/src/core/timer.c ++++ b/src/core/timer.c +@@ -566,7 +566,7 @@ static void timer_enter_running(Timer *t) { + return; + } + +- r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); ++ r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL); + if (r < 0) + goto fail; + +diff --git a/src/core/transaction.c b/src/core/transaction.c +index 045930838b..cdaaff4f55 100644 +--- a/src/core/transaction.c ++++ b/src/core/transaction.c +@@ -585,7 +585,12 @@ rescan: + } + } + +-static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { ++static int transaction_apply( ++ Transaction *tr, ++ Manager *m, ++ JobMode mode, ++ Set *affected_jobs) { ++ + Iterator i; + Job *j; + int r; +@@ -642,6 +647,11 @@ static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { + job_add_to_dbus_queue(j); + job_start_timer(j, false); + job_shutdown_magic(j); ++ ++ /* When 'affected' is specified, let's track all in it all jobs that were touched because of ++ * this transaction. */ ++ if (affected_jobs) ++ (void) set_put(affected_jobs, j); + } + + return 0; +@@ -654,7 +664,13 @@ rollback: + return r; + } + +-int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error *e) { ++int transaction_activate( ++ Transaction *tr, ++ Manager *m, ++ JobMode mode, ++ Set *affected_jobs, ++ sd_bus_error *e) { ++ + Iterator i; + Job *j; + int r; +@@ -731,7 +747,7 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error + return log_notice_errno(r, "Requested transaction contradicts existing jobs: %s", bus_error_message(e, r)); + + /* Tenth step: apply changes */ +- r = transaction_apply(tr, m, mode); ++ r = transaction_apply(tr, m, mode, affected_jobs); + if (r < 0) + return log_warning_errno(r, "Failed to apply transaction: %m"); + +diff --git a/src/core/transaction.h b/src/core/transaction.h +index 70d74a4ccb..4b5620f5c8 100644 +--- a/src/core/transaction.h ++++ b/src/core/transaction.h +@@ -29,6 +29,6 @@ int transaction_add_job_and_dependencies( + bool ignore_requirements, + bool ignore_order, + sd_bus_error *e); +-int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error *e); ++int transaction_activate(Transaction *tr, Manager *m, JobMode mode, Set *affected, sd_bus_error *e); + int transaction_add_isolate_jobs(Transaction *tr, Manager *m); + void transaction_abort(Transaction *tr); +diff --git a/src/core/unit.c b/src/core/unit.c +index 29ce6c1fb7..ffbf3cfd48 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -2033,7 +2033,7 @@ static void unit_check_binds_to(Unit *u) { + log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->id); + + /* A unit we need to run is gone. Sniff. Let's stop this. */ +- r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); ++ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, NULL, &error, NULL); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); + } +@@ -2049,25 +2049,25 @@ static void retroactively_start_dependencies(Unit *u) { + HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRES], i) + if (!hashmap_get(u->dependencies[UNIT_AFTER], other) && + !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) +- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL); ++ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL, NULL); + + HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], i) + if (!hashmap_get(u->dependencies[UNIT_AFTER], other) && + !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) +- manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL); ++ manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL, NULL); + + HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_WANTS], i) + if (!hashmap_get(u->dependencies[UNIT_AFTER], other) && + !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) +- manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL); ++ manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL, NULL); + + HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTS], i) + if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); ++ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL); + + HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTED_BY], i) + if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); ++ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL); + } + + static void retroactively_stop_dependencies(Unit *u) { +@@ -2081,7 +2081,7 @@ static void retroactively_stop_dependencies(Unit *u) { + /* Pull down units which are bound to us recursively if enabled */ + HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BOUND_BY], i) + if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) +- manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); ++ manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL); + } + + void unit_start_on_failure(Unit *u) { +@@ -2100,7 +2100,7 @@ void unit_start_on_failure(Unit *u) { + HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_ON_FAILURE], i) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + +- r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, &error, NULL); ++ r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, &error, NULL); + if (r < 0) + log_unit_warning_errno(u, r, "Failed to enqueue OnFailure= job, ignoring: %s", bus_error_message(&error, r)); + } +diff --git a/src/test/test-engine.c b/src/test/test-engine.c +index 0f3e244dc1..f2e327b3f5 100644 +--- a/src/test/test-engine.c ++++ b/src/test/test-engine.c +@@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { + manager_dump_units(m, stdout, "\t"); + + printf("Test1: (Trivial)\n"); +- r = manager_add_job(m, JOB_START, c, JOB_REPLACE, &err, &j); ++ r = manager_add_job(m, JOB_START, c, JOB_REPLACE, NULL, &err, &j); + if (sd_bus_error_is_set(&err)) + log_error("error: %s: %s", err.name, err.message); + assert_se(r == 0); +@@ -59,15 +59,15 @@ int main(int argc, char *argv[]) { + manager_dump_units(m, stdout, "\t"); + + printf("Test2: (Cyclic Order, Unfixable)\n"); +- assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, NULL, &j) == -EDEADLK); ++ assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK); + manager_dump_jobs(m, stdout, "\t"); + + printf("Test3: (Cyclic Order, Fixable, Garbage Collector)\n"); +- assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, NULL, &j) == 0); ++ assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, NULL, NULL, &j) == 0); + manager_dump_jobs(m, stdout, "\t"); + + printf("Test4: (Identical transaction)\n"); +- assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, NULL, &j) == 0); ++ assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, NULL, NULL, &j) == 0); + manager_dump_jobs(m, stdout, "\t"); + + printf("Load3:\n"); +@@ -75,21 +75,21 @@ int main(int argc, char *argv[]) { + manager_dump_units(m, stdout, "\t"); + + printf("Test5: (Colliding transaction, fail)\n"); +- assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, NULL, &j) == -EDEADLK); ++ assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, NULL, NULL, &j) == -EDEADLK); + + printf("Test6: (Colliding transaction, replace)\n"); +- assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, NULL, &j) == 0); ++ assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, NULL, NULL, &j) == 0); + manager_dump_jobs(m, stdout, "\t"); + + printf("Test7: (Unmergeable job type, fail)\n"); +- assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, NULL, &j) == -EDEADLK); ++ assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, NULL, NULL, &j) == -EDEADLK); + + printf("Test8: (Mergeable job type, fail)\n"); +- assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, NULL, &j) == 0); ++ assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, NULL, NULL, &j) == 0); + manager_dump_jobs(m, stdout, "\t"); + + printf("Test9: (Unmergeable job type, replace)\n"); +- assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, NULL, &j) == 0); ++ assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, NULL, NULL, &j) == 0); + manager_dump_jobs(m, stdout, "\t"); + + printf("Load4:\n"); +@@ -97,7 +97,7 @@ int main(int argc, char *argv[]) { + manager_dump_units(m, stdout, "\t"); + + printf("Test10: (Unmergeable job type of auxiliary job, fail)\n"); +- assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, &j) == 0); ++ assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, NULL, &j) == 0); + manager_dump_jobs(m, stdout, "\t"); + + assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b)); diff --git a/SOURCES/0445-systemctl-replace-switch-statement-by-table-of-struc.patch b/SOURCES/0445-systemctl-replace-switch-statement-by-table-of-struc.patch new file mode 100644 index 0000000..b95326c --- /dev/null +++ b/SOURCES/0445-systemctl-replace-switch-statement-by-table-of-struc.patch @@ -0,0 +1,115 @@ +From 8b34041ee97069bee8bf03ae5ba651b34b1b8460 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 15:20:26 +0100 +Subject: [PATCH] systemctl: replace switch statement by table of structures + +(cherry picked from commit c45e5fb877033c9e3f9b79121644ed71032af379) + +Related: #846319 +--- + src/systemctl/systemctl.c | 68 ++++++++++++--------------------------- + 1 file changed, 21 insertions(+), 47 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index e963f19b0a..04e24691d8 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3179,64 +3179,38 @@ static int logind_set_wall_message(void) { + } + #endif + +-/* Ask systemd-logind, which might grant access to unprivileged users +- * through PolicyKit */ ++/* Ask systemd-logind, which might grant access to unprivileged users through polkit */ + static int logind_reboot(enum action a) { + #if ENABLE_LOGIND ++ static const struct { ++ const char *method; ++ const char *description; ++ } actions[_ACTION_MAX] = { ++ [ACTION_POWEROFF] = { "PowerOff", "power off system" }, ++ [ACTION_REBOOT] = { "Reboot", "reboot system" }, ++ [ACTION_HALT] = { "Halt", "halt system" }, ++ [ACTION_SUSPEND] = { "Suspend", "suspend system" }, ++ [ACTION_HIBERNATE] = { "Hibernate", "hibernate system" }, ++ [ACTION_HYBRID_SLEEP] = { "HybridSleep", "put system into hybrid sleep" }, ++ [ACTION_SUSPEND_THEN_HIBERNATE] = { "SuspendThenHibernate", "suspend system, hibernate later" }, ++ }; ++ + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- const char *method, *description; + sd_bus *bus; + int r; + ++ if (a < 0 || a >= _ACTION_MAX || !actions[a].method) ++ return -EINVAL; ++ + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + +- switch (a) { +- +- case ACTION_POWEROFF: +- method = "PowerOff"; +- description = "power off system"; +- break; +- +- case ACTION_REBOOT: +- method = "Reboot"; +- description = "reboot system"; +- break; +- +- case ACTION_HALT: +- method = "Halt"; +- description = "halt system"; +- break; +- +- case ACTION_SUSPEND: +- method = "Suspend"; +- description = "suspend system"; +- break; +- +- case ACTION_HIBERNATE: +- method = "Hibernate"; +- description = "hibernate system"; +- break; +- +- case ACTION_HYBRID_SLEEP: +- method = "HybridSleep"; +- description = "put system into hybrid sleep"; +- break; +- +- case ACTION_SUSPEND_THEN_HIBERNATE: +- method = "SuspendThenHibernate"; +- description = "put system into suspend followed by hibernate"; +- break; +- +- default: +- return -EINVAL; +- } +- + polkit_agent_open_maybe(); + (void) logind_set_wall_message(); + +- log_debug("%s org.freedesktop.login1.Manager %s dbus call.", arg_dry_run ? "Would execute" : "Executing", method); ++ log_debug("%s org.freedesktop.login1.Manager %s dbus call.", arg_dry_run ? "Would execute" : "Executing", actions[a].method); ++ + if (arg_dry_run) + return 0; + +@@ -3245,12 +3219,12 @@ static int logind_reboot(enum action a) { + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", +- method, ++ actions[a].method, + &error, + NULL, + "b", arg_ask_password); + if (r < 0) +- return log_error_errno(r, "Failed to %s via logind: %s", description, bus_error_message(&error, r)); ++ return log_error_errno(r, "Failed to %s via logind: %s", actions[a].description, bus_error_message(&error, r)); + + return 0; + #else diff --git a/SOURCES/0446-systemctl-reindent-table.patch b/SOURCES/0446-systemctl-reindent-table.patch new file mode 100644 index 0000000..22ad788 --- /dev/null +++ b/SOURCES/0446-systemctl-reindent-table.patch @@ -0,0 +1,53 @@ +From 26d2d89c6216672cedf6abfe1b73081adebbdcf8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 15:49:52 +0100 +Subject: [PATCH] systemctl: reindent table + +(cherry picked from commit 5fd77930ad9980af5257f9f871556d6973db736c) + +Related: #846319 +--- + src/systemctl/systemctl.c | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 04e24691d8..f057dc829c 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -2974,21 +2974,21 @@ static const struct { + const char *verb; + const char *mode; + } action_table[_ACTION_MAX] = { +- [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" }, +- [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" }, +- [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" }, +- [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" }, +- [ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, +- [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, +- [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, +- [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" }, +- [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" }, +- [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" }, +- [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" }, +- [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" }, +- [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" }, +- [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" }, +- [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" }, ++ [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" }, ++ [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" }, ++ [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" }, ++ [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" }, ++ [ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, ++ [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, ++ [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, ++ [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" }, ++ [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" }, ++ [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" }, ++ [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" }, ++ [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" }, ++ [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" }, ++ [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" }, ++ [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" }, + [ACTION_SUSPEND_THEN_HIBERNATE] = { SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, "suspend-then-hibernate", "replace-irreversibly" }, + }; + diff --git a/SOURCES/0447-systemctl-Only-wait-when-there-s-something-to-wait-f.patch b/SOURCES/0447-systemctl-Only-wait-when-there-s-something-to-wait-f.patch new file mode 100644 index 0000000..2cd38d4 --- /dev/null +++ b/SOURCES/0447-systemctl-Only-wait-when-there-s-something-to-wait-f.patch @@ -0,0 +1,29 @@ +From 91c83bde0904581fbc33eb7821119e665b9505ce Mon Sep 17 00:00:00 2001 +From: Filipe Brandenburger +Date: Fri, 20 Jul 2018 11:32:55 -0700 +Subject: [PATCH] systemctl: Only wait when there's something to wait for. + +Tested: +- `systemctl --wait start i-do-not-exist.service` does not wait. +- `systemctl --wait start i-do-not-exist.service valid-unit.service` does. + +(cherry picked from commit 46f2579c2ac9f6780d5afec1000764defc6b581e) + +Related: #846319 +--- + src/systemctl/systemctl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index f057dc829c..1929692480 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3130,7 +3130,7 @@ static int start_unit(int argc, char *argv[], void *userdata) { + check_triggering_units(bus, *name); + } + +- if (r >= 0 && arg_wait) { ++ if (r >= 0 && arg_wait && !set_isempty(wait_context.unit_paths)) { + int q; + q = sd_event_loop(wait_context.event); + if (q < 0) diff --git a/SOURCES/0448-systemctl-clean-up-start_unit_one-error-handling.patch b/SOURCES/0448-systemctl-clean-up-start_unit_one-error-handling.patch new file mode 100644 index 0000000..ffd9a76 --- /dev/null +++ b/SOURCES/0448-systemctl-clean-up-start_unit_one-error-handling.patch @@ -0,0 +1,99 @@ +From 7569756d005d4f780fffd2504eb6f461982833f2 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Sat, 13 Oct 2018 14:38:46 +0200 +Subject: [PATCH] systemctl: clean up start_unit_one() error handling + +Let's split exit code handling in two: "r" is only used for errno-style +errors, and "ret" is used for exit() codes. Then, let's use EXIT_SUCCESS +for checking whether the latter is already used. + +This way it should always be clear what kind of error we are processing, +and when we propaate one into the other. + +Moreover this allows us to drop "q" form all inner loops, avoiding +confusion when to use "q" and when "r" to store received errors. + +Fixes: #9704 +(cherry picked from commit 0e8d9c0c4d7e71487c486f626c59853cfb031d16) + +Related: #846319 +--- + src/systemctl/systemctl.c | 32 +++++++++++++++----------------- + 1 file changed, 15 insertions(+), 17 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 1929692480..4af9deb98d 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3004,12 +3004,12 @@ static enum action verb_to_action(const char *verb) { + + static int start_unit(int argc, char *argv[], void *userdata) { + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; ++ _cleanup_(wait_context_free) WaitContext wait_context = {}; + const char *method, *mode, *one_name, *suffix = NULL; + _cleanup_strv_free_ char **names = NULL; ++ int r, ret = EXIT_SUCCESS; + sd_bus *bus; +- _cleanup_(wait_context_free) WaitContext wait_context = {}; + char **name; +- int r = 0; + + if (arg_wait && !STR_IN_SET(argv[0], "start", "restart")) { + log_error("--wait may only be used with the 'start' or 'restart' commands."); +@@ -3096,16 +3096,15 @@ static int start_unit(int argc, char *argv[], void *userdata) { + + STRV_FOREACH(name, names) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; +- int q; + +- q = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL); +- if (r >= 0 && q < 0) +- r = translate_bus_error_to_exit_status(q, &error); ++ r = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL); ++ if (ret == EXIT_SUCCESS && r < 0) ++ ret = translate_bus_error_to_exit_status(r, &error); + } + + if (!arg_no_block) { +- int q, arg_count = 0; + const char* extra_args[4] = {}; ++ int arg_count = 0; + + if (arg_scope != UNIT_FILE_SYSTEM) + extra_args[arg_count++] = "--user"; +@@ -3119,9 +3118,9 @@ static int start_unit(int argc, char *argv[], void *userdata) { + extra_args[arg_count++] = arg_host; + } + +- q = bus_wait_for_jobs(w, arg_quiet, extra_args); +- if (q < 0) +- return q; ++ r = bus_wait_for_jobs(w, arg_quiet, extra_args); ++ if (r < 0) ++ return r; + + /* When stopping units, warn if they can still be triggered by + * another active unit (socket, path, timer) */ +@@ -3130,16 +3129,15 @@ static int start_unit(int argc, char *argv[], void *userdata) { + check_triggering_units(bus, *name); + } + +- if (r >= 0 && arg_wait && !set_isempty(wait_context.unit_paths)) { +- int q; +- q = sd_event_loop(wait_context.event); +- if (q < 0) +- return log_error_errno(q, "Failed to run event loop: %m"); ++ if (ret == EXIT_SUCCESS && arg_wait && !set_isempty(wait_context.unit_paths)) { ++ r = sd_event_loop(wait_context.event); ++ if (r < 0) ++ return log_error_errno(r, "Failed to run event loop: %m"); + if (wait_context.any_failed) +- r = EXIT_FAILURE; ++ ret = EXIT_FAILURE; + } + +- return r; ++ return ret; + } + + #if ENABLE_LOGIND diff --git a/SOURCES/0449-systemctl-split-out-extra-args-generation-into-helpe.patch b/SOURCES/0449-systemctl-split-out-extra-args-generation-into-helpe.patch new file mode 100644 index 0000000..a580c7a --- /dev/null +++ b/SOURCES/0449-systemctl-split-out-extra-args-generation-into-helpe.patch @@ -0,0 +1,68 @@ +From cfb124260a0a9e68102a373c0d136f792e2d4ea7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 16:19:35 +0100 +Subject: [PATCH] systemctl: split out extra args generation into helper + function of its own + +(cherry picked from commit 94369fc0663255bbd327f97dba288ececf51a514) + +Related: #846319 +--- + src/systemctl/systemctl.c | 36 +++++++++++++++++++++--------------- + 1 file changed, 21 insertions(+), 15 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 4af9deb98d..e7a8fd559f 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -3002,6 +3002,25 @@ static enum action verb_to_action(const char *verb) { + return _ACTION_INVALID; + } + ++static const char** make_extra_args(const char *extra_args[static 4]) { ++ size_t n = 0; ++ ++ if (arg_scope != UNIT_FILE_SYSTEM) ++ extra_args[n++] = "--user"; ++ ++ if (arg_transport == BUS_TRANSPORT_REMOTE) { ++ extra_args[n++] = "-H"; ++ extra_args[n++] = arg_host; ++ } else if (arg_transport == BUS_TRANSPORT_MACHINE) { ++ extra_args[n++] = "-M"; ++ extra_args[n++] = arg_host; ++ } else ++ assert(arg_transport == BUS_TRANSPORT_LOCAL); ++ ++ extra_args[n] = NULL; ++ return extra_args; ++} ++ + static int start_unit(int argc, char *argv[], void *userdata) { + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; + _cleanup_(wait_context_free) WaitContext wait_context = {}; +@@ -3103,22 +3122,9 @@ static int start_unit(int argc, char *argv[], void *userdata) { + } + + if (!arg_no_block) { +- const char* extra_args[4] = {}; +- int arg_count = 0; +- +- if (arg_scope != UNIT_FILE_SYSTEM) +- extra_args[arg_count++] = "--user"; +- +- assert(IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_REMOTE, BUS_TRANSPORT_MACHINE)); +- if (arg_transport == BUS_TRANSPORT_REMOTE) { +- extra_args[arg_count++] = "-H"; +- extra_args[arg_count++] = arg_host; +- } else if (arg_transport == BUS_TRANSPORT_MACHINE) { +- extra_args[arg_count++] = "-M"; +- extra_args[arg_count++] = arg_host; +- } ++ const char* extra_args[4]; + +- r = bus_wait_for_jobs(w, arg_quiet, extra_args); ++ r = bus_wait_for_jobs(w, arg_quiet, make_extra_args(extra_args)); + if (r < 0) + return r; + diff --git a/SOURCES/0450-systemctl-add-new-show-transaction-switch.patch b/SOURCES/0450-systemctl-add-new-show-transaction-switch.patch new file mode 100644 index 0000000..1e804b0 --- /dev/null +++ b/SOURCES/0450-systemctl-add-new-show-transaction-switch.patch @@ -0,0 +1,362 @@ +From cacf7a619cdccd9a6da6c2fe7361eac121b9ea0b Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 22 Mar 2019 20:58:13 +0100 +Subject: [PATCH] systemctl: add new --show-transaction switch + +This new switch uses the new method call EnqueueUnitJob() for enqueuing +a job and showing the jobs it enqueued. + +Fixes: #2297 +(cherry picked from commit 85d9b5981ba6b7ee3955f95fa6cf3bb8cdf3444d) + +Resolves: #846319 +--- + src/systemctl/systemctl.c | 183 ++++++++++++++++++++++++++------------ + 1 file changed, 128 insertions(+), 55 deletions(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index e7a8fd559f..8bec798373 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -126,6 +126,7 @@ static bool arg_dry_run = false; + static bool arg_quiet = false; + static bool arg_full = false; + static bool arg_recursive = false; ++static bool arg_show_transaction = false; + static int arg_force = 0; + static bool arg_ask_password = false; + static bool arg_runtime = false; +@@ -734,7 +735,6 @@ static int get_unit_list_recursive( + *_machines = NULL; + + *_unit_infos = TAKE_PTR(unit_infos); +- + *_replies = TAKE_PTR(replies); + + return c; +@@ -2688,25 +2688,26 @@ static int check_triggering_units(sd_bus *bus, const char *name) { + } + + static const struct { +- const char *verb; +- const char *method; ++ const char *verb; /* systemctl verb */ ++ const char *method; /* Name of the specific D-Bus method */ ++ const char *job_type; /* Job type when passing to the generic EnqueueUnitJob() method */ + } unit_actions[] = { +- { "start", "StartUnit" }, +- { "stop", "StopUnit" }, +- { "condstop", "StopUnit" }, +- { "reload", "ReloadUnit" }, +- { "restart", "RestartUnit" }, +- { "try-restart", "TryRestartUnit" }, +- { "condrestart", "TryRestartUnit" }, +- { "reload-or-restart", "ReloadOrRestartUnit" }, +- { "try-reload-or-restart", "ReloadOrTryRestartUnit" }, +- { "reload-or-try-restart", "ReloadOrTryRestartUnit" }, +- { "condreload", "ReloadOrTryRestartUnit" }, +- { "force-reload", "ReloadOrTryRestartUnit" } ++ { "start", "StartUnit", "start" }, ++ { "stop", "StopUnit", "stop" }, ++ { "condstop", "StopUnit", "stop" }, /* legacy alias */ ++ { "reload", "ReloadUnit", "reload" }, ++ { "restart", "RestartUnit", "restart" }, ++ { "try-restart", "TryRestartUnit", "try-restart" }, ++ { "condrestart", "TryRestartUnit", "try-restart" }, /* legacy alias */ ++ { "reload-or-restart", "ReloadOrRestartUnit", "reload-or-restart" }, ++ { "try-reload-or-restart", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, ++ { "reload-or-try-restart", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */ ++ { "condreload", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */ ++ { "force-reload", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */ + }; + + static const char *verb_to_method(const char *verb) { +- uint i; ++ size_t i; + + for (i = 0; i < ELEMENTSOF(unit_actions); i++) + if (streq_ptr(unit_actions[i].verb, verb)) +@@ -2715,14 +2716,14 @@ static const char *verb_to_method(const char *verb) { + return "StartUnit"; + } + +-static const char *method_to_verb(const char *method) { +- uint i; ++static const char *verb_to_job_type(const char *verb) { ++ size_t i; + + for (i = 0; i < ELEMENTSOF(unit_actions); i++) +- if (streq_ptr(unit_actions[i].method, method)) +- return unit_actions[i].verb; ++ if (streq_ptr(unit_actions[i].verb, verb)) ++ return unit_actions[i].job_type; + +- return "n/a"; ++ return "start"; + } + + typedef struct { +@@ -2805,7 +2806,8 @@ static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error + + static int start_unit_one( + sd_bus *bus, +- const char *method, ++ const char *method, /* When using classic per-job bus methods */ ++ const char *job_type, /* When using new-style EnqueueUnitJob() */ + const char *name, + const char *mode, + sd_bus_error *error, +@@ -2814,6 +2816,7 @@ static int start_unit_one( + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *path; ++ bool done = false; + int r; + + assert(method); +@@ -2859,44 +2862,81 @@ static int start_unit_one( + log_debug("%s dbus call org.freedesktop.systemd1.Manager %s(%s, %s)", + arg_dry_run ? "Would execute" : "Executing", + method, name, mode); ++ + if (arg_dry_run) + return 0; + +- r = sd_bus_call_method( +- bus, +- "org.freedesktop.systemd1", +- "/org/freedesktop/systemd1", +- "org.freedesktop.systemd1.Manager", +- method, +- error, +- &reply, +- "ss", name, mode); +- if (r < 0) { +- const char *verb; ++ if (arg_show_transaction) { ++ _cleanup_(sd_bus_error_free) sd_bus_error enqueue_error = SD_BUS_ERROR_NULL; + +- /* There's always a fallback possible for legacy actions. */ +- if (arg_action != ACTION_SYSTEMCTL) +- return r; ++ /* Use the new, fancy EnqueueUnitJob() API if the user wants us to print the transaction */ ++ r = sd_bus_call_method( ++ bus, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ "EnqueueUnitJob", ++ &enqueue_error, ++ &reply, ++ "sss", ++ name, job_type, mode); ++ if (r < 0) { ++ if (!sd_bus_error_has_name(&enqueue_error, SD_BUS_ERROR_UNKNOWN_METHOD)) { ++ (void) sd_bus_error_copy(error, &enqueue_error); ++ sd_bus_error_free(&enqueue_error); ++ goto fail; ++ } ++ ++ /* Hmm, the API is not yet available. Let's use the classic API instead (see below). */ ++ log_notice("--show-transaction not supported by this service manager, proceeding without."); ++ } else { ++ const char *u, *jt; ++ uint32_t id; + +- verb = method_to_verb(method); ++ r = sd_bus_message_read(reply, "uosos", &id, &path, &u, NULL, &jt); ++ if (r < 0) ++ return bus_log_parse_error(r); + +- log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r)); ++ log_info("Enqueued anchor job %" PRIu32 " %s/%s.", id, u, jt); + +- if (!sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) && +- !sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED) && +- !sd_bus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE)) +- log_error("See %s logs and 'systemctl%s status%s %s' for details.", +- arg_scope == UNIT_FILE_SYSTEM ? "system" : "user", +- arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", +- name[0] == '-' ? " --" : "", +- name); ++ r = sd_bus_message_enter_container(reply, 'a', "(uosos)"); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ for (;;) { ++ r = sd_bus_message_read(reply, "(uosos)", &id, NULL, &u, NULL, &jt); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ if (r == 0) ++ break; + +- return r; ++ log_info("Enqueued auxiliary job %" PRIu32 " %s/%s.", id, u, jt); ++ } ++ ++ r = sd_bus_message_exit_container(reply); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ done = true; ++ } + } + +- r = sd_bus_message_read(reply, "o", &path); +- if (r < 0) +- return bus_log_parse_error(r); ++ if (!done) { ++ r = sd_bus_call_method( ++ bus, ++ "org.freedesktop.systemd1", ++ "/org/freedesktop/systemd1", ++ "org.freedesktop.systemd1.Manager", ++ method, ++ error, ++ &reply, ++ "ss", name, mode); ++ if (r < 0) ++ goto fail; ++ ++ r = sd_bus_message_read(reply, "o", &path); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ } + + if (need_daemon_reload(bus, name) > 0) + warn_unit_file_changed(name); +@@ -2909,6 +2949,24 @@ static int start_unit_one( + } + + return 0; ++ ++fail: ++ /* There's always a fallback possible for legacy actions. */ ++ if (arg_action != ACTION_SYSTEMCTL) ++ return r; ++ ++ log_error_errno(r, "Failed to %s %s: %s", job_type, name, bus_error_message(error, r)); ++ ++ if (!sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) && ++ !sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED) && ++ !sd_bus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE)) ++ log_error("See %s logs and 'systemctl%s status%s %s' for details.", ++ arg_scope == UNIT_FILE_SYSTEM ? "system" : "user", ++ arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", ++ name[0] == '-' ? " --" : "", ++ name); ++ ++ return r; + } + + static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) { +@@ -2965,7 +3023,6 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r + } + + *ret = TAKE_PTR(mangled); +- + return 0; + } + +@@ -3024,7 +3081,7 @@ static const char** make_extra_args(const char *extra_args[static 4]) { + static int start_unit(int argc, char *argv[], void *userdata) { + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; + _cleanup_(wait_context_free) WaitContext wait_context = {}; +- const char *method, *mode, *one_name, *suffix = NULL; ++ const char *method, *job_type, *mode, *one_name, *suffix = NULL; + _cleanup_strv_free_ char **names = NULL; + int r, ret = EXIT_SUCCESS; + sd_bus *bus; +@@ -3050,27 +3107,34 @@ static int start_unit(int argc, char *argv[], void *userdata) { + action = verb_to_action(argv[0]); + + if (action != _ACTION_INVALID) { ++ /* A command in style "systemctl reboot", "systemctl poweroff", … */ + method = "StartUnit"; ++ job_type = "start"; + mode = action_table[action].mode; + one_name = action_table[action].target; + } else { + if (streq(argv[0], "isolate")) { ++ /* A "systemctl isolate …" command */ + method = "StartUnit"; ++ job_type = "start"; + mode = "isolate"; +- + suffix = ".target"; + } else { ++ /* A command in style of "systemctl start …", "sysemctl stop …" and so on */ + method = verb_to_method(argv[0]); ++ job_type = verb_to_job_type(argv[0]); + mode = arg_job_mode; + } + one_name = NULL; + } + } else { ++ /* A SysV legacy command such as "halt", "reboot", "poweroff", … */ + assert(arg_action >= 0 && arg_action < _ACTION_MAX); + assert(action_table[arg_action].target); + assert(action_table[arg_action].mode); + + method = "StartUnit"; ++ job_type = "start"; + mode = action_table[arg_action].mode; + one_name = action_table[arg_action].target; + } +@@ -3105,9 +3169,11 @@ static int start_unit(int argc, char *argv[], void *userdata) { + NULL); + if (r < 0) + return log_error_errno(r, "Failed to enable subscription: %m"); ++ + r = sd_event_default(&wait_context.event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); ++ + r = sd_bus_attach_event(bus, wait_context.event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); +@@ -3116,7 +3182,7 @@ static int start_unit(int argc, char *argv[], void *userdata) { + STRV_FOREACH(name, names) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + +- r = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL); ++ r = start_unit_one(bus, method, job_type, *name, mode, &error, w, arg_wait ? &wait_context : NULL); + if (ret == EXIT_SUCCESS && r < 0) + ret = translate_bus_error_to_exit_status(r, &error); + } +@@ -7167,6 +7233,8 @@ static void systemctl_help(void) { + " --reverse Show reverse dependencies with 'list-dependencies'\n" + " --job-mode=MODE Specify how to deal with already queued jobs, when\n" + " queueing a new job\n" ++ " -T --show-transaction\n" ++ " When enqueuing a unit job, show full transaction\n" + " --show-types When showing sockets, explicitly show their type\n" + " --value When showing properties, only print the value\n" + " -i --ignore-inhibitors\n" +@@ -7482,6 +7550,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { + { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, + { "now", no_argument, NULL, ARG_NOW }, + { "message", required_argument, NULL, ARG_MESSAGE }, ++ { "show-transaction", no_argument, NULL, 'T' }, + {} + }; + +@@ -7494,7 +7563,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { + /* we default to allowing interactive authorization only in systemctl (not in the legacy commands) */ + arg_ask_password = true; + +- while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir", options, NULL)) >= 0) ++ while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:iTr", options, NULL)) >= 0) + + switch (c) { + +@@ -7815,6 +7884,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { + return log_oom(); + break; + ++ case 'T': ++ arg_show_transaction = true; ++ break; ++ + case '?': + return -EINVAL; + diff --git a/SOURCES/0451-test-add-some-basic-testing-that-systemctl-start-T-d.patch b/SOURCES/0451-test-add-some-basic-testing-that-systemctl-start-T-d.patch new file mode 100644 index 0000000..13c3808 --- /dev/null +++ b/SOURCES/0451-test-add-some-basic-testing-that-systemctl-start-T-d.patch @@ -0,0 +1,31 @@ +From f31afbfd2fa68e20a10a8432fb4714a6d4e1170a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 17:39:36 +0100 +Subject: [PATCH] test: add some basic testing that "systemctl start -T" does + something + +(cherry picked from commit f087c7e072bb338d5c7c0781c9fbc900612efd18) + +Related: #846319 +--- + test/TEST-03-JOBS/test-jobs.sh | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/test/TEST-03-JOBS/test-jobs.sh b/test/TEST-03-JOBS/test-jobs.sh +index e66ea53621..42190cf478 100755 +--- a/test/TEST-03-JOBS/test-jobs.sh ++++ b/test/TEST-03-JOBS/test-jobs.sh +@@ -26,6 +26,13 @@ grep 'sleep\.service.*running' /root/list-jobs.txt + grep 'hello\.service' /root/list-jobs.txt && exit 1 + systemctl stop sleep.service hello-after-sleep.target + ++# Some basic testing that --show-transaction does something useful ++! systemctl is-active systemd-importd ++systemctl -T start systemd-importd ++systemctl is-active systemd-importd ++systemctl --show-transaction stop systemd-importd ++! systemctl is-active systemd-importd ++ + # Test for a crash when enqueuing a JOB_NOP when other job already exists + systemctl start --no-block hello-after-sleep.target + # hello.service should still be waiting, so these try-restarts will collapse diff --git a/SOURCES/0452-man-document-the-new-systemctl-show-transaction-opti.patch b/SOURCES/0452-man-document-the-new-systemctl-show-transaction-opti.patch new file mode 100644 index 0000000..1f8b8ce --- /dev/null +++ b/SOURCES/0452-man-document-the-new-systemctl-show-transaction-opti.patch @@ -0,0 +1,37 @@ +From 588e3e8008d24021ec025d54318fb07d2212293c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 26 Mar 2019 18:02:49 +0100 +Subject: [PATCH] man: document the new systemctl --show-transaction option + +(cherry picked from commit df4a7cb7323c8cf00553d766913312c5b7ccd508) + +Related: #846319 +--- + man/systemctl.xml | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index 6145486123..fa08ab6c0a 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -298,6 +298,20 @@ + + + ++ ++ ++ ++ ++ ++ When enqueuing a unit job (for example as effect of a systemctl start ++ invocation or similar), show brief information about all jobs enqueued, covering both the requested ++ job and any added because of unit dependencies. Note that the output will only include jobs ++ immediately part of the transaction requested. It is possible that service start-up program code ++ run as effect of the enqueued jobs might request further jobs to be pulled in. This means that ++ completion of the listed jobs might ultimately entail more jobs than the listed ones. ++ ++ ++ + + + diff --git a/SOURCES/0453-socket-New-option-FlushPending-boolean-to-flush-sock.patch b/SOURCES/0453-socket-New-option-FlushPending-boolean-to-flush-sock.patch new file mode 100644 index 0000000..357f076 --- /dev/null +++ b/SOURCES/0453-socket-New-option-FlushPending-boolean-to-flush-sock.patch @@ -0,0 +1,153 @@ +From 262544a451c11c38e92c45047ec2adeaeb2a0a7e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Renaud=20M=C3=A9trich?= +Date: Thu, 20 Aug 2020 13:00:37 +0200 +Subject: [PATCH] socket: New option 'FlushPending' (boolean) to flush socket + before entering listening state + +Disabled by default. When Enabled, before listening on the socket, flush the content. +Applies when Accept=no only. + +(cherry picked from commit 3e5f04bf6468fcb79c080f02b0eab08f258bff0c) + +Resolves: #1870638 +--- + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.socket.xml | 12 ++++++++++++ + src/core/dbus-socket.c | 4 ++++ + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/core/socket.c | 11 +++++++++++ + src/core/socket.h | 1 + + src/shared/bus-unit-util.c | 3 ++- + 7 files changed, 32 insertions(+), 1 deletion(-) + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 1a4e79190a..995b8797ef 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -388,6 +388,7 @@ Most socket unit settings are available to transient units. + ✓ SocketMode= + ✓ DirectoryMode= + ✓ Accept= ++✓ FlushPending= + ✓ Writable= + ✓ MaxConnections= + ✓ MaxConnectionsPerSource= +diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml +index 19c2ca9907..8676b4e03f 100644 +--- a/man/systemd.socket.xml ++++ b/man/systemd.socket.xml +@@ -425,6 +425,18 @@ + false, in read-only mode. Defaults to false. + + ++ ++ FlushPending= ++ Takes a boolean argument. May only be used when ++ . If yes, the socket's buffers are cleared after the ++ triggered service exited. This causes any pending data to be ++ flushed and any pending incoming connections to be rejected. If no, the ++ socket's buffers won't be cleared, permitting the service to handle any ++ pending connections after restart, which is the usually expected behaviour. ++ Defaults to . ++ ++ ++ + + MaxConnections= + The maximum number of connections to +diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c +index 913cc74918..bb77539030 100644 +--- a/src/core/dbus-socket.c ++++ b/src/core/dbus-socket.c +@@ -85,6 +85,7 @@ const sd_bus_vtable bus_socket_vtable[] = { + SD_BUS_PROPERTY("SocketMode", "u", bus_property_get_mode, offsetof(Socket, socket_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DirectoryMode", "u", bus_property_get_mode, offsetof(Socket, directory_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Accept", "b", bus_property_get_bool, offsetof(Socket, accept), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("FlushPending", "b", bus_property_get_bool, offsetof(Socket, flush_pending), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Writable", "b", bus_property_get_bool, offsetof(Socket, writable), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KeepAlive", "b", bus_property_get_bool, offsetof(Socket, keep_alive), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KeepAliveTimeUSec", "t", bus_property_get_usec, offsetof(Socket, keep_alive_time), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -177,6 +178,9 @@ static int bus_socket_set_transient_property( + if (streq(name, "Accept")) + return bus_set_transient_bool(u, name, &s->accept, message, flags, error); + ++ if (streq(name, "FlushPending")) ++ return bus_set_transient_bool(u, name, &s->flush_pending, message, flags, error); ++ + if (streq(name, "Writable")) + return bus_set_transient_bool(u, name, &s->writable, message, flags, error); + +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 6d21b2e433..24ee5ae6fe 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -359,6 +359,7 @@ Socket.SocketGroup, config_parse_user_group, 0, + Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) + Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) + Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) ++Socket.FlushPending, config_parse_bool, 0, offsetof(Socket, flush_pending) + Socket.Writable, config_parse_bool, 0, offsetof(Socket, writable) + Socket.MaxConnections, config_parse_unsigned, 0, offsetof(Socket, max_connections) + Socket.MaxConnectionsPerSource, config_parse_unsigned, 0, offsetof(Socket, max_connections_per_source) +diff --git a/src/core/socket.c b/src/core/socket.c +index fe061eb73b..97c3a7fc9a 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -70,6 +70,7 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { + + static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); + static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); ++static void flush_ports(Socket *s); + + static void socket_init(Unit *u) { + Socket *s = SOCKET(u); +@@ -703,6 +704,11 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { + prefix, s->n_connections, + prefix, s->max_connections, + prefix, s->max_connections_per_source); ++ else ++ fprintf(f, ++ "%sFlushPending: %s\n", ++ prefix, yes_no(s->flush_pending)); ++ + + if (s->priority >= 0) + fprintf(f, +@@ -2111,6 +2117,11 @@ static void socket_enter_listening(Socket *s) { + int r; + assert(s); + ++ if (!s->accept && s->flush_pending) { ++ log_unit_debug(UNIT(s), "Flushing socket before listening."); ++ flush_ports(s); ++ } ++ + r = socket_watch_fds(s); + if (r < 0) { + log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m"); +diff --git a/src/core/socket.h b/src/core/socket.h +index c4e25db1fc..b7a25d91fd 100644 +--- a/src/core/socket.h ++++ b/src/core/socket.h +@@ -109,6 +109,7 @@ struct Socket { + bool accept; + bool remove_on_stop; + bool writable; ++ bool flush_pending; + + int socket_protocol; + +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 77788f0fe2..7029aa5615 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -1468,7 +1468,8 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons + + if (STR_IN_SET(field, + "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast", +- "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet")) ++ "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet", ++ "FlushPending")) + + return bus_append_parse_boolean(m, field, eq); + diff --git a/SOURCES/0454-core-remove-support-for-API-bus-started-outside-our-.patch b/SOURCES/0454-core-remove-support-for-API-bus-started-outside-our-.patch new file mode 100644 index 0000000..2d454d8 --- /dev/null +++ b/SOURCES/0454-core-remove-support-for-API-bus-started-outside-our-.patch @@ -0,0 +1,62 @@ +From 9c0ed82f661a2296784970678746b0b4f130870e Mon Sep 17 00:00:00 2001 +From: Alan Jenkins +Date: Thu, 21 Jun 2018 14:12:30 +0100 +Subject: [PATCH] core: remove support for API bus "started outside our own + logic" + +Looking at a recent Bad Day, my log contains over 100 lines of + + systemd[23895]: Failed to connect to API bus: Connection refused + +It is due to "systemd --user" retrying to connect to an API bus.[*] I +would prefer to avoid spamming the logs. I don't think it is good for us +to retry so much like this. + +systemd was mislead by something setting DBUS_SESSION_BUS_ADDRESS. My best +guess is an unfortunate series of events caused gdm to set this. gdm has +code to start a session dbus if there is not a bus available already (and +in this case it exports the environment variable). I believe it does not +normally do this when running under systemd, because "systemd --user" and +hence "dbus.service" would already have been started by pam_systemd. + +I see two possibilities + +1. Rip out the check for DBUS_SESSION_BUS_ADDRESS entirely. +2. Only check for DBUS_SESSION_BUS_ADDRESS on startup. Not in the + "recheck" logic. + +The justification for 2), is that the recheck is called from unit_notify(), +this is used to check whether the service just started (or stopped) was +"dbus.service". This reason for rechecking does not apply if we think +the session bus was started outside our logic. + +But I think we can justify 1). dbus-daemon ships a statically-enabled +/usr/lib/systemd/user/dbus.service, which would conflict with an attempt to +use an external dbus. Also "systemd --user" is started from user@.service; +if you try to start it manually so that it inherits an environment +variable, it will conflict if user@.service was started by pam_systemd +(or loginctl enable-linger). + +(cherry picked from commit d3243f55ca9b5f305306ba4105ab29768e372a78) + +Resolves: #1764282 +--- + src/core/manager.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/src/core/manager.c b/src/core/manager.c +index 012615e537..3c44ad3dbc 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -1519,11 +1519,6 @@ static bool manager_dbus_is_running(Manager *m, bool deserialized) { + if (m->test_run_flags != 0) + return false; + +- /* If we are in the user instance, and the env var is already set for us, then this means D-Bus is ran +- * somewhere outside of our own logic. Let's use it */ +- if (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS")) +- return true; +- + u = manager_get_unit(m, SPECIAL_DBUS_SOCKET); + if (!u) + return false; diff --git a/SOURCES/0455-mount-setup-fix-segfault-in-mount_cgroup_controllers.patch b/SOURCES/0455-mount-setup-fix-segfault-in-mount_cgroup_controllers.patch new file mode 100644 index 0000000..a99e7de --- /dev/null +++ b/SOURCES/0455-mount-setup-fix-segfault-in-mount_cgroup_controllers.patch @@ -0,0 +1,56 @@ +From 1c8d1c3bbdf8bdfb2ee4f85b9f559f54c6e4cac4 Mon Sep 17 00:00:00 2001 +From: Wen Yang +Date: Wed, 1 Jul 2020 04:45:33 +0800 +Subject: [PATCH] mount-setup: fix segfault in mount_cgroup_controllers when + using gcc9 compiler + +According to the documentation: +https://gcc.gnu.org/gcc-9/porting_to.html#complit + +The 'join_controllers' that relied on the extended lifetime needs +to be fixed, move the compound literals to the function scope it +need to accessible in. + +Resolves: #1868877 +--- + src/core/mount-setup.c | 24 +++++++++++++----------- + 1 file changed, 13 insertions(+), 11 deletions(-) + +diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c +index 16880e6157..a6594580e5 100644 +--- a/src/core/mount-setup.c ++++ b/src/core/mount-setup.c +@@ -237,20 +237,22 @@ int mount_cgroup_controllers(char ***join_controllers) { + if (!cg_is_legacy_wanted()) + return 0; + ++ /* The defaults: ++ * mount "cpu" + "cpuacct" together, and "net_cls" + "net_prio". ++ * ++ * We'd like to add "cpuset" to the mix, but "cpuset" doesn't really ++ * work for groups with no initialized attributes. ++ */ ++ char ***default_join_controllers = (char**[]) { ++ STRV_MAKE("cpu", "cpuacct"), ++ STRV_MAKE("net_cls", "net_prio"), ++ NULL, ++ }; ++ + /* Mount all available cgroup controllers that are built into the kernel. */ + + if (!has_argument) +- /* The defaults: +- * mount "cpu" + "cpuacct" together, and "net_cls" + "net_prio". +- * +- * We'd like to add "cpuset" to the mix, but "cpuset" doesn't really +- * work for groups with no initialized attributes. +- */ +- join_controllers = (char**[]) { +- STRV_MAKE("cpu", "cpuacct"), +- STRV_MAKE("net_cls", "net_prio"), +- NULL, +- }; ++ join_controllers = default_join_controllers; + + r = cg_kernel_controllers(&controllers); + if (r < 0) diff --git a/SOURCES/0456-dbus-execute-make-transfer-of-CPUAffinity-endian-saf.patch b/SOURCES/0456-dbus-execute-make-transfer-of-CPUAffinity-endian-saf.patch new file mode 100644 index 0000000..71758ee --- /dev/null +++ b/SOURCES/0456-dbus-execute-make-transfer-of-CPUAffinity-endian-saf.patch @@ -0,0 +1,39 @@ +From 1730f7bb306e13689a7684fd93ae5b8383a28d2f Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Fri, 31 May 2019 15:23:23 +0200 +Subject: [PATCH] dbus-execute: make transfer of CPUAffinity endian safe + (#12711) + +We store the affinity mask in the native endian. However, over D-Bus we +must transfer the mask in little endian byte order. + +This is the second part of c367f996f5f091a63f812f0140b304c649be77fc. + +(cherry picked from commit 75e40119a471454516ad0acc96f6f4094e7fb652) + +Related: #1740657 +--- + src/core/dbus-execute.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index f9527e56b2..d5acca384f 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -215,12 +215,15 @@ static int property_get_cpu_affinity( + sd_bus_error *error) { + + ExecContext *c = userdata; ++ _cleanup_free_ uint8_t *array = NULL; ++ size_t allocated; + + assert(bus); + assert(reply); + assert(c); + +- return sd_bus_message_append_array(reply, 'y', c->cpu_set.set, c->cpu_set.allocated); ++ (void) cpu_set_to_dbus(&c->cpu_set, &array, &allocated); ++ return sd_bus_message_append_array(reply, 'y', array, allocated); + } + + static int property_get_numa_mask( diff --git a/SOURCES/0457-core-add-support-for-setting-CPUAffinity-to-special-.patch b/SOURCES/0457-core-add-support-for-setting-CPUAffinity-to-special-.patch new file mode 100644 index 0000000..3118171 --- /dev/null +++ b/SOURCES/0457-core-add-support-for-setting-CPUAffinity-to-special-.patch @@ -0,0 +1,426 @@ +From 1a822dbe19ab6634ffb2c0d3ce92b27b503e1612 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Mon, 17 Feb 2020 13:50:31 +0100 +Subject: [PATCH] core: add support for setting CPUAffinity= to special "numa" + value + +systemd will automatically derive CPU affinity mask from NUMA node +mask. + +Fixes #13248 + +(cherry picked from commit e2b2fb7f566d13a3de61952b5356cd4d2eaee917) + +Resolves: #1740657 +--- + man/systemd.exec.xml | 9 +++--- + src/basic/cpu-set-util.c | 43 ++++++++++++++++++++++++-- + src/basic/cpu-set-util.h | 1 + + src/core/dbus-execute.c | 30 +++++++++++++++++- + src/core/execute.c | 46 ++++++++++++++++++++++++++-- + src/core/execute.h | 3 ++ + src/core/load-fragment.c | 14 ++++++++- + src/shared/bus-unit-util.c | 9 ++++++ + src/test/test-cpu-set-util.c | 6 ++-- + test/TEST-36-NUMAPOLICY/testsuite.sh | 18 +++++++++++ + 10 files changed, 166 insertions(+), 13 deletions(-) + +diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml +index e2a5ede968..696438c4ef 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -706,10 +706,11 @@ CapabilityBoundingSet=~CAP_B CAP_C + CPUAffinity= + + Controls the CPU affinity of the executed processes. Takes a list of CPU indices or ranges +- separated by either whitespace or commas. CPU ranges are specified by the lower and upper CPU indices separated +- by a dash. This option may be specified more than once, in which case the specified CPU affinity masks are +- merged. If the empty string is assigned, the mask is reset, all assignments prior to this will have no +- effect. See ++ separated by either whitespace or commas. Alternatively, takes a special "numa" value in which case systemd ++ automatically derives allowed CPU range based on the value of NUMAMask= option. CPU ranges ++ are specified by the lower and upper CPU indices separated by a dash. This option may be specified more than ++ once, in which case the specified CPU affinity masks are merged. If the empty string is assigned, the mask ++ is reset, all assignments prior to this will have no effect. See + sched_setaffinity2 for + details. + +diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c +index 51752ad1a6..1922c95864 100644 +--- a/src/basic/cpu-set-util.c ++++ b/src/basic/cpu-set-util.c +@@ -12,12 +12,14 @@ + #include "cpu-set-util.h" + #include "dirent-util.h" + #include "extract-word.h" ++#include "fileio.h" + #include "fd-util.h" + #include "log.h" + #include "macro.h" + #include "missing.h" + #include "parse-util.h" + #include "stat-util.h" ++#include "stdio-util.h" + #include "string-util.h" + #include "string-table.h" + #include "strv.h" +@@ -179,7 +181,7 @@ int cpu_set_add_all(CPUSet *a, const CPUSet *b) { + return r; + } + +- return 0; ++ return 1; + } + + int parse_cpu_set_full( +@@ -264,7 +266,7 @@ int parse_cpu_set_extend( + if (!old->set) { + *old = cpuset; + cpuset = (CPUSet) {}; +- return 0; ++ return 1; + } + + return cpu_set_add_all(old, &cpuset); +@@ -417,6 +419,43 @@ int apply_numa_policy(const NUMAPolicy *policy) { + return 0; + } + ++int numa_to_cpu_set(const NUMAPolicy *policy, CPUSet *ret) { ++ int r; ++ size_t i; ++ _cleanup_(cpu_set_reset) CPUSet s = {}; ++ ++ assert(policy); ++ assert(ret); ++ ++ for (i = 0; i < policy->nodes.allocated * 8; i++) { ++ _cleanup_free_ char *l = NULL; ++ char p[STRLEN("/sys/devices/system/node/node//cpulist") + DECIMAL_STR_MAX(size_t) + 1]; ++ _cleanup_(cpu_set_reset) CPUSet part = {}; ++ ++ if (!CPU_ISSET_S(i, policy->nodes.allocated, policy->nodes.set)) ++ continue; ++ ++ xsprintf(p, "/sys/devices/system/node/node%zu/cpulist", i); ++ ++ r = read_one_line_file(p, &l); ++ if (r < 0) ++ return r; ++ ++ r = parse_cpu_set(l, &part); ++ if (r < 0) ++ return r; ++ ++ r = cpu_set_add_all(&s, &part); ++ if (r < 0) ++ return r; ++ } ++ ++ *ret = s; ++ s = (CPUSet) {}; ++ ++ return 0; ++} ++ + static const char* const mpol_table[] = { + [MPOL_DEFAULT] = "default", + [MPOL_PREFERRED] = "preferred", +diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h +index 8519a9b6c8..795be807af 100644 +--- a/src/basic/cpu-set-util.h ++++ b/src/basic/cpu-set-util.h +@@ -78,6 +78,7 @@ static inline void numa_policy_reset(NUMAPolicy *p) { + } + + int apply_numa_policy(const NUMAPolicy *policy); ++int numa_to_cpu_set(const NUMAPolicy *policy, CPUSet *ret); + + const char* mpol_to_string(int i) _const_; + int mpol_from_string(const char *s) _pure_; +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index d5acca384f..0fe4c14e48 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -58,6 +58,8 @@ static BUS_DEFINE_PROPERTY_GET2(property_get_ioprio_priority, "i", ExecContext, + static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_string, "s", NULL); + static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI); + static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC); ++static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa); ++ + + static int property_get_environment_files( + sd_bus *bus, +@@ -215,6 +217,7 @@ static int property_get_cpu_affinity( + sd_bus_error *error) { + + ExecContext *c = userdata; ++ _cleanup_(cpu_set_reset) CPUSet s = {}; + _cleanup_free_ uint8_t *array = NULL; + size_t allocated; + +@@ -222,7 +225,16 @@ static int property_get_cpu_affinity( + assert(reply); + assert(c); + +- (void) cpu_set_to_dbus(&c->cpu_set, &array, &allocated); ++ if (c->cpu_affinity_from_numa) { ++ int r; ++ ++ r = numa_to_cpu_set(&c->numa_policy, &s); ++ if (r < 0) ++ return r; ++ } ++ ++ (void) cpu_set_to_dbus(c->cpu_affinity_from_numa ? &s : &c->cpu_set, &array, &allocated); ++ + return sd_bus_message_append_array(reply, 'y', array, allocated); + } + +@@ -743,6 +755,7 @@ const sd_bus_vtable bus_exec_vtable[] = { + SD_BUS_PROPERTY("CPUSchedulingPolicy", "i", property_get_cpu_sched_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUSchedulingPriority", "i", property_get_cpu_sched_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("CPUAffinity", "ay", property_get_cpu_affinity, 0, SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("CPUAffinityFromNUMA", "b", property_get_cpu_affinity_from_numa, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NUMAPolicy", "i", property_get_numa_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NUMAMask", "ay", property_get_numa_mask, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST), +@@ -1639,6 +1652,20 @@ int bus_exec_context_set_transient_property( + + return 1; + ++ } else if (streq(name, "CPUAffinityFromNUMA")) { ++ int q; ++ ++ r = sd_bus_message_read_basic(message, 'b', &q); ++ if (r < 0) ++ return r; ++ ++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { ++ c->cpu_affinity_from_numa = q; ++ unit_write_settingf(u, flags, name, "%s=%s", "CPUAffinity", "numa"); ++ } ++ ++ return 1; ++ + } else if (streq(name, "NUMAPolicy")) { + int32_t type; + +@@ -1653,6 +1680,7 @@ int bus_exec_context_set_transient_property( + c->numa_policy.type = type; + + return 1; ++ + } else if (streq(name, "IOSchedulingClass")) { + int32_t q; + +diff --git a/src/core/execute.c b/src/core/execute.c +index 3c54ac1110..d528d08830 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -2750,6 +2750,33 @@ static int compile_suggested_paths(const ExecContext *c, const ExecParameters *p + + static char *exec_command_line(char **argv); + ++static int exec_context_cpu_affinity_from_numa(const ExecContext *c, CPUSet *ret) { ++ _cleanup_(cpu_set_reset) CPUSet s = {}; ++ int r; ++ ++ assert(c); ++ assert(ret); ++ ++ if (!c->numa_policy.nodes.set) { ++ log_debug("Can't derive CPU affinity mask from NUMA mask because NUMA mask is not set, ignoring"); ++ return 0; ++ } ++ ++ r = numa_to_cpu_set(&c->numa_policy, &s); ++ if (r < 0) ++ return r; ++ ++ cpu_set_reset(ret); ++ ++ return cpu_set_add_all(ret, &s); ++} ++ ++bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c) { ++ assert(c); ++ ++ return c->cpu_affinity_from_numa; ++} ++ + static int exec_child( + Unit *unit, + const ExecCommand *command, +@@ -3012,11 +3039,26 @@ static int exec_child( + } + } + +- if (context->cpu_set.set) +- if (sched_setaffinity(0, context->cpu_set.allocated, context->cpu_set.set) < 0) { ++ if (context->cpu_affinity_from_numa || context->cpu_set.set) { ++ _cleanup_(cpu_set_reset) CPUSet converted_cpu_set = {}; ++ const CPUSet *cpu_set; ++ ++ if (context->cpu_affinity_from_numa) { ++ r = exec_context_cpu_affinity_from_numa(context, &converted_cpu_set); ++ if (r < 0) { ++ *exit_status = EXIT_CPUAFFINITY; ++ return log_unit_error_errno(unit, r, "Failed to derive CPU affinity mask from NUMA mask: %m"); ++ } ++ ++ cpu_set = &converted_cpu_set; ++ } else ++ cpu_set = &context->cpu_set; ++ ++ if (sched_setaffinity(0, cpu_set->allocated, cpu_set->set) < 0) { + *exit_status = EXIT_CPUAFFINITY; + return log_unit_error_errno(unit, errno, "Failed to set up CPU affinity: %m"); + } ++ } + + if (mpol_is_valid(numa_policy_get_type(&context->numa_policy))) { + r = apply_numa_policy(&context->numa_policy); +diff --git a/src/core/execute.h b/src/core/execute.h +index 86c1cee84c..62c6229621 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -152,6 +152,7 @@ struct ExecContext { + + CPUSet cpu_set; + NUMAPolicy numa_policy; ++ bool cpu_affinity_from_numa; + + ExecInput std_input; + ExecOutput std_output; +@@ -375,6 +376,8 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, + void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds); + void exec_runtime_vacuum(Manager *m); + ++bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c); ++ + const char* exec_output_to_string(ExecOutput i) _const_; + ExecOutput exec_output_from_string(const char *s) _pure_; + +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 33fdb82754..740401a582 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -1251,13 +1251,25 @@ int config_parse_exec_cpu_affinity(const char *unit, + void *userdata) { + + ExecContext *c = data; ++ int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + +- return parse_cpu_set_extend(rvalue, &c->cpu_set, true, unit, filename, line, lvalue); ++ if (streq(rvalue, "numa")) { ++ c->cpu_affinity_from_numa = true; ++ cpu_set_reset(&c->cpu_set); ++ ++ return 0; ++ } ++ ++ r = parse_cpu_set_extend(rvalue, &c->cpu_set, true, unit, filename, line, lvalue); ++ if (r >= 0) ++ c->cpu_affinity_from_numa = false; ++ ++ return r; + } + + int config_parse_capability_set( +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index 7029aa5615..daa2c2dce5 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -26,6 +26,8 @@ + #include "securebits-util.h" + #include "signal-util.h" + #include "socket-protocol-list.h" ++#include "socket-util.h" ++#include "stdio-util.h" + #include "string-util.h" + #include "syslog-util.h" + #include "terminal-util.h" +@@ -997,6 +999,13 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con + _cleanup_free_ uint8_t *array = NULL; + size_t allocated; + ++ if (eq && streq(eq, "numa")) { ++ r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true); ++ if (r < 0) ++ return bus_log_create_error(r); ++ return r; ++ } ++ + r = parse_cpu_set(eq, &cpuset); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); +diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c +index 136eaca82d..1b7be5df4e 100644 +--- a/src/test/test-cpu-set-util.c ++++ b/src/test/test-cpu-set-util.c +@@ -218,12 +218,12 @@ static void test_parse_cpu_set_extend(void) { + + log_info("/* %s */", __func__); + +- assert_se(parse_cpu_set_extend("1 3", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(parse_cpu_set_extend("1 3", &c, true, NULL, "fake", 1, "CPUAffinity") == 1); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 2); + assert_se(s1 = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", s1); + +- assert_se(parse_cpu_set_extend("4", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(parse_cpu_set_extend("4", &c, true, NULL, "fake", 1, "CPUAffinity") == 1); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 3); + assert_se(s2 = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", s2); +@@ -240,7 +240,7 @@ static void test_cpu_set_to_from_dbus(void) { + + log_info("/* %s */", __func__); + +- assert_se(parse_cpu_set_extend("1 3 8 100-200", &c, true, NULL, "fake", 1, "CPUAffinity") == 0); ++ assert_se(parse_cpu_set_extend("1 3 8 100-200", &c, true, NULL, "fake", 1, "CPUAffinity") == 1); + assert_se(s = cpu_set_to_string(&c)); + log_info("cpu_set_to_string: %s", s); + assert_se(CPU_COUNT_S(c.allocated, c.set) == 104); +diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh +index bffac4ffe6..7ccaa5b412 100755 +--- a/test/TEST-36-NUMAPOLICY/testsuite.sh ++++ b/test/TEST-36-NUMAPOLICY/testsuite.sh +@@ -279,6 +279,18 @@ else + # Maks must be ignored + grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog + ++ echo "Unit file CPUAffinity=NUMA support" ++ writeTestUnitNUMAPolicy "bind" "0" ++ echo "CPUAffinity=numa" >> $testUnitNUMAConf ++ systemctl daemon-reload ++ systemctl start $testUnit ++ systemctlCheckNUMAProperties $testUnit "bind" "0" ++ pid=$(systemctl show --value -p MainPID $testUnit) ++ cpulist=$(cat /sys/devices/system/node/node0/cpulist) ++ affinity_systemd=$(systemctl show --value -p CPUAffinity $testUnit) ++ [ $cpulist = $affinity_systemd ] ++ pid1StopUnit $testUnit ++ + echo "systemd-run NUMAPolicy support" + runUnit='numa-systemd-run-test.service' + +@@ -309,6 +321,12 @@ else + systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit $runUnit sleep 1000 + systemctlCheckNUMAProperties $runUnit "local" "" + pid1StopUnit $runUnit ++ ++ systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit $runUnit sleep 1000 ++ systemctlCheckNUMAProperties $runUnit "local" "" ++ systemctl cat $runUnit | grep -q 'CPUAffinity=numa' ++ pid1StopUnit $runUnit ++ + fi + + # Cleanup diff --git a/SOURCES/0458-basic-user-util-always-use-base-10-for-user-group-nu.patch b/SOURCES/0458-basic-user-util-always-use-base-10-for-user-group-nu.patch new file mode 100644 index 0000000..59daa5f --- /dev/null +++ b/SOURCES/0458-basic-user-util-always-use-base-10-for-user-group-nu.patch @@ -0,0 +1,93 @@ +From 57d2e6e64ba490054f8de1a2aad4ffae7778eddc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 31 May 2020 18:21:09 +0200 +Subject: [PATCH] basic/user-util: always use base 10 for user/group numbers + +We would parse numbers with base prefixes as user identifiers. For example, +"0x2b3bfa0" would be interpreted as UID==45334432 and "01750" would be +interpreted as UID==1000. This parsing was used also in cases where either a +user/group name or number may be specified. This means that names like +0x2b3bfa0 would be ambiguous: they are a valid user name according to our +documented relaxed rules, but they would also be parsed as numeric uids. + +This behaviour is definitely not expected by users, since tools generally only +accept decimal numbers (e.g. id, getent passwd), while other tools only accept +user names and thus will interpret such strings as user names without even +attempting to convert them to numbers (su, ssh). So let's follow suit and only +accept numbers in decimal notation. Effectively this means that we will reject +such strings as a username/uid/groupname/gid where strict mode is used, and try +to look up a user/group with such a name in relaxed mode. + +Since the function changed is fairly low-level and fairly widely used, this +affects multiple tools: loginctl show-user/enable-linger/disable-linger foo', +the third argument in sysusers.d, fourth and fifth arguments in tmpfiles.d, +etc. + +Fixes #15985. + +(cherry picked from commit 156a5fd297b61bce31630d7a52c15614bf784843) + +Resolves: #1848373 +--- + src/basic/parse-util.h | 8 ++++++-- + src/basic/user-util.c | 2 +- + src/test/test-user-util.c | 10 ++++++++++ + 3 files changed, 17 insertions(+), 3 deletions(-) + +diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h +index f3267f4cfe..1fc1af7615 100644 +--- a/src/basic/parse-util.h ++++ b/src/basic/parse-util.h +@@ -50,9 +50,13 @@ static inline int safe_atoux16(const char *s, uint16_t *ret) { + + int safe_atoi16(const char *s, int16_t *ret); + +-static inline int safe_atou32(const char *s, uint32_t *ret_u) { ++static inline int safe_atou32_full(const char *s, unsigned base, uint32_t *ret_u) { + assert_cc(sizeof(uint32_t) == sizeof(unsigned)); +- return safe_atou(s, (unsigned*) ret_u); ++ return safe_atou_full(s, base, (unsigned*) ret_u); ++} ++ ++static inline int safe_atou32(const char *s, uint32_t *ret_u) { ++ return safe_atou32_full(s, 0, (unsigned*) ret_u); + } + + static inline int safe_atoi32(const char *s, int32_t *ret_i) { +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index d92969c966..10eeb256cd 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -49,7 +49,7 @@ int parse_uid(const char *s, uid_t *ret) { + assert(s); + + assert_cc(sizeof(uid_t) == sizeof(uint32_t)); +- r = safe_atou32(s, &uid); ++ r = safe_atou32_full(s, 10, &uid); + if (r < 0) + return r; + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 9114d30b8c..8bf3dcd567 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -46,9 +46,19 @@ static void test_parse_uid(void) { + + r = parse_uid("65535", &uid); + assert_se(r == -ENXIO); ++ assert_se(uid == 100); ++ ++ r = parse_uid("0x1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("01234", &uid); ++ assert_se(r == 0); ++ assert_se(uid == 1234); + + r = parse_uid("asdsdas", &uid); + assert_se(r == -EINVAL); ++ assert_se(uid == 1234); + } + + static void test_uid_ptr(void) { diff --git a/SOURCES/0459-parse-util-sometimes-it-is-useful-to-check-if-a-stri.patch b/SOURCES/0459-parse-util-sometimes-it-is-useful-to-check-if-a-stri.patch new file mode 100644 index 0000000..7d41bb9 --- /dev/null +++ b/SOURCES/0459-parse-util-sometimes-it-is-useful-to-check-if-a-stri.patch @@ -0,0 +1,149 @@ +From 2fd9a21a8b6a93c4fb2747839766adca15faa008 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 14 Nov 2019 14:49:40 +0100 +Subject: [PATCH] parse-util: sometimes it is useful to check if a string is a + valid integer, but not actually parse it + +(cherry picked from commit 22810041c2200fe72b0e0c985d0e404f8b80f9e2) + +Related: #1848373 +--- + src/basic/parse-util.c | 34 ++++++++++++++++++++-------------- + 1 file changed, 20 insertions(+), 14 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 6becf85878..056e56765e 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -383,7 +383,6 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + unsigned long l; + + assert(s); +- assert(ret_u); + assert(base <= 16); + + /* strtoul() is happy to parse negative values, and silently +@@ -407,7 +406,9 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + if ((unsigned long) (unsigned) l != l) + return -ERANGE; + +- *ret_u = (unsigned) l; ++ if (ret_u) ++ *ret_u = (unsigned) l; ++ + return 0; + } + +@@ -416,7 +417,6 @@ int safe_atoi(const char *s, int *ret_i) { + long l; + + assert(s); +- assert(ret_i); + + errno = 0; + l = strtol(s, &x, 0); +@@ -427,7 +427,9 @@ int safe_atoi(const char *s, int *ret_i) { + if ((long) (int) l != l) + return -ERANGE; + +- *ret_i = (int) l; ++ if (ret_i) ++ *ret_i = (int) l; ++ + return 0; + } + +@@ -436,7 +438,6 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { + unsigned long long l; + + assert(s); +- assert(ret_llu); + + s += strspn(s, WHITESPACE); + +@@ -449,7 +450,9 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { + if (*s == '-') + return -ERANGE; + +- *ret_llu = l; ++ if (ret_llu) ++ *ret_llu = l; ++ + return 0; + } + +@@ -458,7 +461,6 @@ int safe_atolli(const char *s, long long int *ret_lli) { + long long l; + + assert(s); +- assert(ret_lli); + + errno = 0; + l = strtoll(s, &x, 0); +@@ -467,7 +469,9 @@ int safe_atolli(const char *s, long long int *ret_lli) { + if (!x || x == s || *x != 0) + return -EINVAL; + +- *ret_lli = l; ++ if (ret_lli) ++ *ret_lli = l; ++ + return 0; + } + +@@ -476,7 +480,6 @@ int safe_atou8(const char *s, uint8_t *ret) { + unsigned long l; + + assert(s); +- assert(ret); + + s += strspn(s, WHITESPACE); + +@@ -491,7 +494,8 @@ int safe_atou8(const char *s, uint8_t *ret) { + if ((unsigned long) (uint8_t) l != l) + return -ERANGE; + +- *ret = (uint8_t) l; ++ if (ret) ++ *ret = (uint8_t) l; + return 0; + } + +@@ -525,7 +529,6 @@ int safe_atoi16(const char *s, int16_t *ret) { + long l; + + assert(s); +- assert(ret); + + errno = 0; + l = strtol(s, &x, 0); +@@ -536,7 +539,9 @@ int safe_atoi16(const char *s, int16_t *ret) { + if ((long) (int16_t) l != l) + return -ERANGE; + +- *ret = (int16_t) l; ++ if (ret) ++ *ret = (int16_t) l; ++ + return 0; + } + +@@ -546,7 +551,6 @@ int safe_atod(const char *s, double *ret_d) { + double d = 0; + + assert(s); +- assert(ret_d); + + loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); + if (loc == (locale_t) 0) +@@ -559,7 +563,9 @@ int safe_atod(const char *s, double *ret_d) { + if (!x || x == s || *x != 0) + return -EINVAL; + +- *ret_d = (double) d; ++ if (ret_d) ++ *ret_d = (double) d; ++ + return 0; + } + diff --git a/SOURCES/0460-basic-parse-util-add-safe_atoux64.patch b/SOURCES/0460-basic-parse-util-add-safe_atoux64.patch new file mode 100644 index 0000000..0405582 --- /dev/null +++ b/SOURCES/0460-basic-parse-util-add-safe_atoux64.patch @@ -0,0 +1,130 @@ +From bd47a98d3ce2c5e1d74deb7bc384e416a9070b96 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 9 Apr 2020 11:18:26 +0200 +Subject: [PATCH] basic/parse-util: add safe_atoux64() + +(cherry picked from commit ce51632a357d347737bf40d3817df331cd8874cb) + +Related: #1848373 +--- + src/basic/parse-util.c | 4 ++-- + src/basic/parse-util.h | 12 +++++++++++- + src/test/test-parse-util.c | 39 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 52 insertions(+), 3 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 056e56765e..67056c0434 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -433,7 +433,7 @@ int safe_atoi(const char *s, int *ret_i) { + return 0; + } + +-int safe_atollu(const char *s, long long unsigned *ret_llu) { ++int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) { + char *x = NULL; + unsigned long long l; + +@@ -442,7 +442,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { + s += strspn(s, WHITESPACE); + + errno = 0; +- l = strtoull(s, &x, 0); ++ l = strtoull(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h +index 1fc1af7615..8a49257050 100644 +--- a/src/basic/parse-util.h ++++ b/src/basic/parse-util.h +@@ -33,7 +33,6 @@ static inline int safe_atou(const char *s, unsigned *ret_u) { + } + + int safe_atoi(const char *s, int *ret_i); +-int safe_atollu(const char *s, unsigned long long *ret_u); + int safe_atolli(const char *s, long long int *ret_i); + + int safe_atou8(const char *s, uint8_t *ret); +@@ -64,6 +63,12 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) { + return safe_atoi(s, (int*) ret_i); + } + ++int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu); ++ ++static inline int safe_atollu(const char *s, long long unsigned *ret_llu) { ++ return safe_atollu_full(s, 0, ret_llu); ++} ++ + static inline int safe_atou64(const char *s, uint64_t *ret_u) { + assert_cc(sizeof(uint64_t) == sizeof(unsigned long long)); + return safe_atollu(s, (unsigned long long*) ret_u); +@@ -74,6 +79,11 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) { + return safe_atolli(s, (long long int*) ret_i); + } + ++static inline int safe_atoux64(const char *s, uint64_t *ret) { ++ assert_cc(sizeof(int64_t) == sizeof(long long unsigned)); ++ return safe_atollu_full(s, 16, (long long unsigned*) ret); ++} ++ + #if LONG_MAX == INT_MAX + static inline int safe_atolu(const char *s, unsigned long *ret_u) { + assert_cc(sizeof(unsigned long) == sizeof(unsigned)); +diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c +index e9aef5e882..8b182d9bdc 100644 +--- a/src/test/test-parse-util.c ++++ b/src/test/test-parse-util.c +@@ -561,6 +561,44 @@ static void test_safe_atoi64(void) { + assert_se(r == -EINVAL); + } + ++static void test_safe_atoux64(void) { ++ int r; ++ uint64_t l; ++ ++ r = safe_atoux64("12345", &l); ++ assert_se(r == 0); ++ assert_se(l == 0x12345); ++ ++ r = safe_atoux64(" 12345", &l); ++ assert_se(r == 0); ++ assert_se(l == 0x12345); ++ ++ r = safe_atoux64("0x12345", &l); ++ assert_se(r == 0); ++ assert_se(l == 0x12345); ++ ++ r = safe_atoux64("18446744073709551617", &l); ++ assert_se(r == -ERANGE); ++ ++ r = safe_atoux64("-1", &l); ++ assert_se(r == -ERANGE); ++ ++ r = safe_atoux64(" -1", &l); ++ assert_se(r == -ERANGE); ++ ++ r = safe_atoux64("junk", &l); ++ assert_se(r == -EINVAL); ++ ++ r = safe_atoux64("123x", &l); ++ assert_se(r == -EINVAL); ++ ++ r = safe_atoux64("12.3", &l); ++ assert_se(r == -EINVAL); ++ ++ r = safe_atoux64("", &l); ++ assert_se(r == -EINVAL); ++} ++ + static void test_safe_atod(void) { + int r; + double d; +@@ -836,6 +874,7 @@ int main(int argc, char *argv[]) { + test_safe_atoux16(); + test_safe_atou64(); + test_safe_atoi64(); ++ test_safe_atoux64(); + test_safe_atod(); + test_parse_percent(); + test_parse_percent_unbounded(); diff --git a/SOURCES/0461-parse-util-allow-tweaking-how-to-parse-integers.patch b/SOURCES/0461-parse-util-allow-tweaking-how-to-parse-integers.patch new file mode 100644 index 0000000..3e97f8b --- /dev/null +++ b/SOURCES/0461-parse-util-allow-tweaking-how-to-parse-integers.patch @@ -0,0 +1,137 @@ +From 1d11e79fefea34b4395043e8e951414c5b7817ba Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:06:19 +0200 +Subject: [PATCH] parse-util: allow tweaking how to parse integers + +This allows disabling a few alternative ways to decode integers +formatted as strings, for safety reasons. + +See: #15991 +(cherry picked from commit 707e93aff8f358f8a62117e54b857530d6594e4b) + +Related: #1848373 +--- + src/basic/parse-util.c | 65 +++++++++++++++++++++++++++++++++--------- + src/basic/parse-util.h | 6 ++++ + 2 files changed, 58 insertions(+), 13 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 67056c0434..6cc4fc3e57 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -383,20 +383,35 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + unsigned long l; + + assert(s); +- assert(base <= 16); ++ assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + +- /* strtoul() is happy to parse negative values, and silently +- * converts them to unsigned values without generating an +- * error. We want a clean error, hence let's look for the "-" +- * prefix on our own, and generate an error. But let's do so +- * only after strtoul() validated that the string is clean +- * otherwise, so that we return EINVAL preferably over +- * ERANGE. */ ++ /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without ++ * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and ++ * generate an error. But let's do so only after strtoul() validated that the string is clean ++ * otherwise, so that we return EINVAL preferably over ERANGE. */ ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && ++ strchr(WHITESPACE, s[0])) ++ return -EINVAL; + + s += strspn(s, WHITESPACE); + ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && ++ IN_SET(s[0], '+', '-')) ++ return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a ++ * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we ++ * blanket refuse +/- prefixed integers, while if it is missing we'll just ++ * return ERANGE, because the string actually parses correctly, but doesn't ++ * fit in the return type. */ ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && ++ s[0] == '0' && !streq(s, "0")) ++ return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal ++ * notation and assumed-to-be-decimal integers with a leading zero. */ ++ + errno = 0; +- l = strtoul(s, &x, base); ++ l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual ++ * base is left */); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -438,11 +453,24 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) + unsigned long long l; + + assert(s); ++ assert(SAFE_ATO_MASK_FLAGS(base) <= 16); ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && ++ strchr(WHITESPACE, s[0])) ++ return -EINVAL; + + s += strspn(s, WHITESPACE); + ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && ++ IN_SET(s[0], '+', '-')) ++ return -EINVAL; ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && ++ s[0] == '0' && s[1] != 0) ++ return -EINVAL; ++ + errno = 0; +- l = strtoull(s, &x, base); ++ l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base)); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -504,13 +532,24 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + unsigned long l; + + assert(s); +- assert(ret); +- assert(base <= 16); ++ assert(SAFE_ATO_MASK_FLAGS(base) <= 16); ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && ++ strchr(WHITESPACE, s[0])) ++ return -EINVAL; + + s += strspn(s, WHITESPACE); + ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && ++ IN_SET(s[0], '+', '-')) ++ return -EINVAL; ++ ++ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && ++ s[0] == '0' && s[1] != 0) ++ return -EINVAL; ++ + errno = 0; +- l = strtoul(s, &x, base); ++ l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h +index 8a49257050..c6bbc98dff 100644 +--- a/src/basic/parse-util.h ++++ b/src/basic/parse-util.h +@@ -26,6 +26,12 @@ int parse_syscall_and_errno(const char *in, char **name, int *error); + #define FORMAT_BYTES_MAX 8 + char *format_bytes(char *buf, size_t l, uint64_t t); + ++#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) ++#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) ++#define SAFE_ATO_REFUSE_LEADING_WHITESPACE (1U << 28) ++#define SAFE_ATO_ALL_FLAGS (SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO|SAFE_ATO_REFUSE_LEADING_WHITESPACE) ++#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS) ++ + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); + + static inline int safe_atou(const char *s, unsigned *ret_u) { diff --git a/SOURCES/0462-parse-util-allow-0-as-alternative-to-0-and-0.patch b/SOURCES/0462-parse-util-allow-0-as-alternative-to-0-and-0.patch new file mode 100644 index 0000000..29884dc --- /dev/null +++ b/SOURCES/0462-parse-util-allow-0-as-alternative-to-0-and-0.patch @@ -0,0 +1,60 @@ +From 1c8e5070d8a88f35b5577e091de66727fa785ef7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:08:38 +0200 +Subject: [PATCH] parse-util: allow '-0' as alternative to '0' and '+0' + +Let's allow "-0" as alternative to "+0" and "0" when parsing integers, +unless the new SAFE_ATO_REFUSE_PLUS_MINUS flag is specified. + +In cases where allowing the +/- syntax shall not be allowed +SAFE_ATO_REFUSE_PLUS_MINUS is the right flag to use, but this also means +that -0 as only negative integer that fits into an unsigned value should +be acceptable if the flag is not specified. + +(cherry picked from commit c78eefc13562a8fc0c22c00a6d3001af89860258) + +Related: #1848373 +--- + src/basic/parse-util.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 6cc4fc3e57..53d181dd60 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -416,7 +416,7 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + return -errno; + if (!x || x == s || *x != 0) + return -EINVAL; +- if (s[0] == '-') ++ if (l != 0 && s[0] == '-') + return -ERANGE; + if ((unsigned long) (unsigned) l != l) + return -ERANGE; +@@ -475,7 +475,7 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) + return -errno; + if (!x || x == s || *x != 0) + return -EINVAL; +- if (*s == '-') ++ if (l != 0 && s[0] == '-') + return -ERANGE; + + if (ret_llu) +@@ -517,7 +517,7 @@ int safe_atou8(const char *s, uint8_t *ret) { + return -errno; + if (!x || x == s || *x != 0) + return -EINVAL; +- if (s[0] == '-') ++ if (l != 0 && s[0] == '-') + return -ERANGE; + if ((unsigned long) (uint8_t) l != l) + return -ERANGE; +@@ -554,7 +554,7 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + return -errno; + if (!x || x == s || *x != 0) + return -EINVAL; +- if (s[0] == '-') ++ if (l != 0 && s[0] == '-') + return -ERANGE; + if ((unsigned long) (uint16_t) l != l) + return -ERANGE; diff --git a/SOURCES/0463-parse-util-make-return-parameter-optional-in-safe_at.patch b/SOURCES/0463-parse-util-make-return-parameter-optional-in-safe_at.patch new file mode 100644 index 0000000..8cb4a65 --- /dev/null +++ b/SOURCES/0463-parse-util-make-return-parameter-optional-in-safe_at.patch @@ -0,0 +1,31 @@ +From 91ed5edcdea79773f6918e739637521e47129b07 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:10:27 +0200 +Subject: [PATCH] parse-util: make return parameter optional in + safe_atou16_full() + +All other safe_atoXYZ_full() functions have the parameter optional, +let's make it optoinal here, too. + +(cherry picked from commit aa85e4d3cef8ca8436e480bce9fa4ce72876b636) + +Related: #1848373 +--- + src/basic/parse-util.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 53d181dd60..7a7cefe6ff 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -559,7 +559,9 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + if ((unsigned long) (uint16_t) l != l) + return -ERANGE; + +- *ret = (uint16_t) l; ++ if (ret) ++ *ret = (uint16_t) l; ++ + return 0; + } + diff --git a/SOURCES/0464-parse-util-rewrite-parse_mode-on-top-of-safe_atou_fu.patch b/SOURCES/0464-parse-util-rewrite-parse_mode-on-top-of-safe_atou_fu.patch new file mode 100644 index 0000000..318e18b --- /dev/null +++ b/SOURCES/0464-parse-util-rewrite-parse_mode-on-top-of-safe_atou_fu.patch @@ -0,0 +1,59 @@ +From 147a3696b45a872e0e21fb74e1497f02543ce871 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:16:04 +0200 +Subject: [PATCH] parse-util: rewrite parse_mode() on top of safe_atou_full() + +Parsing is hard, hence let's use our own careful wrappers wherever +possible. + +(cherry picked from commit c44702a8bd8cc8b7f2f1df21db9308d9af7dda5b) + +Related: #1848373 +--- + src/basic/parse-util.c | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 7a7cefe6ff..68c156c543 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -54,26 +54,24 @@ int parse_pid(const char *s, pid_t* ret_pid) { + } + + int parse_mode(const char *s, mode_t *ret) { +- char *x; +- long l; ++ unsigned m; ++ int r; + + assert(s); +- assert(ret); + +- s += strspn(s, WHITESPACE); +- if (s[0] == '-') +- return -ERANGE; +- +- errno = 0; +- l = strtol(s, &x, 8); +- if (errno > 0) +- return -errno; +- if (!x || x == s || *x != 0) +- return -EINVAL; +- if (l < 0 || l > 07777) ++ r = safe_atou_full(s, 8 | ++ SAFE_ATO_REFUSE_PLUS_MINUS, /* Leading '+' or even '-' char? that's just weird, ++ * refuse. User might have wanted to add mode flags or ++ * so, but this parser doesn't allow that, so let's ++ * better be safe. */ ++ &m); ++ if (r < 0) ++ return r; ++ if (m > 07777) + return -ERANGE; + +- *ret = (mode_t) l; ++ if (ret) ++ *ret = m; + return 0; + } + diff --git a/SOURCES/0465-user-util-be-stricter-in-parse_uid.patch b/SOURCES/0465-user-util-be-stricter-in-parse_uid.patch new file mode 100644 index 0000000..79cb570 --- /dev/null +++ b/SOURCES/0465-user-util-be-stricter-in-parse_uid.patch @@ -0,0 +1,78 @@ +From 87c22d3bb794118d25bc138108fd5bdd607365ef Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:16:46 +0200 +Subject: [PATCH] user-util: be stricter in parse_uid() + +Let's refuse "+" and "-" prefixed UIDs. Let's refuse whitespace-prefixed +UIDS, Let's refuse zero-prefixed UIDs. Let's be safe than sorry. + +(cherry picked from commit f5979b63cc305ba217dfd174b1bf0583bcf75a73) + +Related: #1848373 +--- + src/basic/user-util.c | 10 +++++++++- + src/test/test-user-util.c | 26 +++++++++++++++++++++++--- + 2 files changed, 32 insertions(+), 4 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 10eeb256cd..40f4e45db6 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -49,7 +49,15 @@ int parse_uid(const char *s, uid_t *ret) { + assert(s); + + assert_cc(sizeof(uid_t) == sizeof(uint32_t)); +- r = safe_atou32_full(s, 10, &uid); ++ ++ /* We are very strict when parsing UIDs, and prohibit +/- as prefix, leading zero as prefix, and ++ * whitespace. We do this, since this call is often used in a context where we parse things as UID ++ * first, and if that doesn't work we fall back to NSS. Thus we really want to make sure that UIDs ++ * are parsed as UIDs only if they really really look like UIDs. */ ++ r = safe_atou32_full(s, 10 ++ | SAFE_ATO_REFUSE_PLUS_MINUS ++ | SAFE_ATO_REFUSE_LEADING_ZERO ++ | SAFE_ATO_REFUSE_LEADING_WHITESPACE, &uid); + if (r < 0) + return r; + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 8bf3dcd567..99203f7e48 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -52,13 +52,33 @@ static void test_parse_uid(void) { + assert_se(r == -EINVAL); + assert_se(uid == 100); + ++ r = parse_uid("+1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("-1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid(" 1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ + r = parse_uid("01234", &uid); +- assert_se(r == 0); +- assert_se(uid == 1234); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("-0", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("+0", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); + + r = parse_uid("asdsdas", &uid); + assert_se(r == -EINVAL); +- assert_se(uid == 1234); ++ assert_se(uid == 100); + } + + static void test_uid_ptr(void) { diff --git a/SOURCES/0466-strv-add-new-macro-STARTSWITH_SET.patch b/SOURCES/0466-strv-add-new-macro-STARTSWITH_SET.patch new file mode 100644 index 0000000..58a4b5a --- /dev/null +++ b/SOURCES/0466-strv-add-new-macro-STARTSWITH_SET.patch @@ -0,0 +1,75 @@ +From 50b103a982dfd6f1b2bf98bbc98a8063fa153e89 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 23 Nov 2018 16:27:15 +0100 +Subject: [PATCH] strv: add new macro STARTSWITH_SET() + +This is to startswith() what PATH_STARTSWITH_SET() is to +path_startswith(). + +Or in other words, checks if the specified string has any of the listed +prefixes, and if so, returns the remainder of the string. + +(cherry picked from commit 52f1552073047195d51901f7e5a5a4fa3189034e) + +Related: #1848373 +--- + src/basic/strv.h | 12 ++++++++++++ + src/test/test-strv.c | 15 +++++++++++++++ + 2 files changed, 27 insertions(+) + +diff --git a/src/basic/strv.h b/src/basic/strv.h +index 51d03db940..c1e4c973b6 100644 +--- a/src/basic/strv.h ++++ b/src/basic/strv.h +@@ -136,6 +136,18 @@ void strv_print(char **l); + _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ + }) + ++#define STARTSWITH_SET(p, ...) \ ++ ({ \ ++ const char *_p = (p); \ ++ char *_found = NULL, **_i; \ ++ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ ++ _found = startswith(_p, *_i); \ ++ if (_found) \ ++ break; \ ++ } \ ++ _found; \ ++ }) ++ + #define FOREACH_STRING(x, ...) \ + for (char **_l = ({ \ + char **_ll = STRV_MAKE(__VA_ARGS__); \ +diff --git a/src/test/test-strv.c b/src/test/test-strv.c +index 1c192239a2..79d999d3ed 100644 +--- a/src/test/test-strv.c ++++ b/src/test/test-strv.c +@@ -56,6 +56,20 @@ static void test_strptr_in_set(void) { + assert_se(!STRPTR_IN_SET(NULL, NULL)); + } + ++static void test_startswith_set(void) { ++ assert_se(!STARTSWITH_SET("foo", "bar", "baz", "waldo")); ++ assert_se(!STARTSWITH_SET("foo", "bar")); ++ ++ assert_se(STARTSWITH_SET("abc", "a", "ab", "abc")); ++ assert_se(STARTSWITH_SET("abc", "ax", "ab", "abc")); ++ assert_se(STARTSWITH_SET("abc", "ax", "abx", "abc")); ++ assert_se(!STARTSWITH_SET("abc", "ax", "abx", "abcx")); ++ ++ assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "foo", "zzz"), "bar")); ++ assert_se(streq_ptr(STARTSWITH_SET("foobar", "hhh", "kkk", "", "zzz"), "foobar")); ++ assert_se(streq_ptr(STARTSWITH_SET("", "hhh", "kkk", "zzz", ""), "")); ++} ++ + static const char* const input_table_multiple[] = { + "one", + "two", +@@ -700,6 +714,7 @@ int main(int argc, char *argv[]) { + test_specifier_printf(); + test_str_in_set(); + test_strptr_in_set(); ++ test_startswith_set(); + test_strv_foreach(); + test_strv_foreach_backwards(); + test_strv_foreach_pair(); diff --git a/SOURCES/0467-parse-util-also-parse-integers-prefixed-with-0b-and-.patch b/SOURCES/0467-parse-util-also-parse-integers-prefixed-with-0b-and-.patch new file mode 100644 index 0000000..d088a5a --- /dev/null +++ b/SOURCES/0467-parse-util-also-parse-integers-prefixed-with-0b-and-.patch @@ -0,0 +1,164 @@ +From e67e29d91a1ef90af545e4130c7b4c4cfde6202a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:31:51 +0200 +Subject: [PATCH] parse-util: also parse integers prefixed with 0b and 0o + +Let's adopt Python 3 style 0b and 0x syntaxes, because it makes a ton of +sense, in particular in bitmask settings. + +(cherry picked from commit fc80cabcf584a8b486bdff5be0c074fec4059cdc) + +Related: #1848373 +--- + src/basic/parse-util.c | 56 ++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 51 insertions(+), 5 deletions(-) + +diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c +index 68c156c543..992ea3605b 100644 +--- a/src/basic/parse-util.c ++++ b/src/basic/parse-util.c +@@ -17,6 +17,7 @@ + #include "parse-util.h" + #include "process-util.h" + #include "string-util.h" ++#include "strv.h" + + int parse_boolean(const char *v) { + assert(v); +@@ -373,7 +374,32 @@ char *format_bytes(char *buf, size_t l, uint64_t t) { + finish: + buf[l-1] = 0; + return buf; ++} ++ ++static const char *mangle_base(const char *s, unsigned *base) { ++ const char *k; ++ ++ assert(s); ++ assert(base); ++ ++ /* Base already explicitly specified, then don't do anything. */ ++ if (SAFE_ATO_MASK_FLAGS(*base) != 0) ++ return s; + ++ /* Support Python 3 style "0b" and 0x" prefixes, because they truly make sense, much more than C's "0" prefix for octal. */ ++ k = STARTSWITH_SET(s, "0b", "0B"); ++ if (k) { ++ *base = 2 | (*base & SAFE_ATO_ALL_FLAGS); ++ return k; ++ } ++ ++ k = STARTSWITH_SET(s, "0o", "0O"); ++ if (k) { ++ *base = 8 | (*base & SAFE_ATO_ALL_FLAGS); ++ return k; ++ } ++ ++ return s; + } + + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { +@@ -407,6 +433,8 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal + * notation and assumed-to-be-decimal integers with a leading zero. */ + ++ s = mangle_base(s, &base); ++ + errno = 0; + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual + * base is left */); +@@ -426,13 +454,17 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { + } + + int safe_atoi(const char *s, int *ret_i) { ++ unsigned base = 0; + char *x = NULL; + long l; + + assert(s); + ++ s += strspn(s, WHITESPACE); ++ s = mangle_base(s, &base); ++ + errno = 0; +- l = strtol(s, &x, 0); ++ l = strtol(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -467,6 +499,8 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) + s[0] == '0' && s[1] != 0) + return -EINVAL; + ++ s = mangle_base(s, &base); ++ + errno = 0; + l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base)); + if (errno > 0) +@@ -483,13 +517,17 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) + } + + int safe_atolli(const char *s, long long int *ret_lli) { ++ unsigned base = 0; + char *x = NULL; + long long l; + + assert(s); + ++ s += strspn(s, WHITESPACE); ++ s = mangle_base(s, &base); ++ + errno = 0; +- l = strtoll(s, &x, 0); ++ l = strtoll(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -502,15 +540,17 @@ int safe_atolli(const char *s, long long int *ret_lli) { + } + + int safe_atou8(const char *s, uint8_t *ret) { +- char *x = NULL; ++ unsigned base = 0; + unsigned long l; ++ char *x = NULL; + + assert(s); + + s += strspn(s, WHITESPACE); ++ s = mangle_base(s, &base); + + errno = 0; +- l = strtoul(s, &x, 0); ++ l = strtoul(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) +@@ -546,6 +586,8 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + s[0] == '0' && s[1] != 0) + return -EINVAL; + ++ s = mangle_base(s, &base); ++ + errno = 0; + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); + if (errno > 0) +@@ -564,13 +606,17 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { + } + + int safe_atoi16(const char *s, int16_t *ret) { ++ unsigned base = 0; + char *x = NULL; + long l; + + assert(s); + ++ s += strspn(s, WHITESPACE); ++ s = mangle_base(s, &base); ++ + errno = 0; +- l = strtol(s, &x, 0); ++ l = strtol(s, &x, base); + if (errno > 0) + return -errno; + if (!x || x == s || *x != 0) diff --git a/SOURCES/0468-tests-beef-up-integer-parsing-tests.patch b/SOURCES/0468-tests-beef-up-integer-parsing-tests.patch new file mode 100644 index 0000000..c33c38f --- /dev/null +++ b/SOURCES/0468-tests-beef-up-integer-parsing-tests.patch @@ -0,0 +1,204 @@ +From 457eada27f606e39f0efc6adc226542fd11eb815 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 1 Jun 2020 17:48:41 +0200 +Subject: [PATCH] tests: beef up integer parsing tests + +(cherry picked from commit 53c6db99fa4b52f97e19977f21d3133f8ceb3dcd) + +Related: #1848373 +--- + src/test/test-parse-util.c | 58 ++++++++++++++++++++++++++++++++++++++ + src/test/test-user-util.c | 40 ++++++++++++++++++++++++++ + 2 files changed, 98 insertions(+) + +diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c +index 8b182d9bdc..699499b665 100644 +--- a/src/test/test-parse-util.c ++++ b/src/test/test-parse-util.c +@@ -75,14 +75,22 @@ static void test_parse_mode(void) { + mode_t m; + + assert_se(parse_mode("-1", &m) < 0); ++ assert_se(parse_mode("+1", &m) < 0); + assert_se(parse_mode("", &m) < 0); + assert_se(parse_mode("888", &m) < 0); + assert_se(parse_mode("77777", &m) < 0); + + assert_se(parse_mode("544", &m) >= 0 && m == 0544); ++ assert_se(parse_mode("0544", &m) >= 0 && m == 0544); ++ assert_se(parse_mode("00544", &m) >= 0 && m == 0544); + assert_se(parse_mode("777", &m) >= 0 && m == 0777); ++ assert_se(parse_mode("0777", &m) >= 0 && m == 0777); ++ assert_se(parse_mode("00777", &m) >= 0 && m == 0777); + assert_se(parse_mode("7777", &m) >= 0 && m == 07777); ++ assert_se(parse_mode("07777", &m) >= 0 && m == 07777); ++ assert_se(parse_mode("007777", &m) >= 0 && m == 07777); + assert_se(parse_mode("0", &m) >= 0 && m == 0); ++ assert_se(parse_mode(" 1", &m) >= 0 && m == 1); + } + + static void test_parse_size(void) { +@@ -358,6 +366,18 @@ static void test_safe_atolli(void) { + assert_se(r == 0); + assert_se(l == -12345); + ++ r = safe_atolli("0x5", &l); ++ assert_se(r == 0); ++ assert_se(l == 5); ++ ++ r = safe_atolli("0o6", &l); ++ assert_se(r == 0); ++ assert_se(l == 6); ++ ++ r = safe_atolli("0B101", &l); ++ assert_se(r == 0); ++ assert_se(l == 5); ++ + r = safe_atolli("12345678901234567890", &l); + assert_se(r == -ERANGE); + +@@ -431,6 +451,14 @@ static void test_safe_atoi16(void) { + assert_se(r == 0); + assert_se(l == 32767); + ++ r = safe_atoi16("0o11", &l); ++ assert_se(r == 0); ++ assert_se(l == 9); ++ ++ r = safe_atoi16("0B110", &l); ++ assert_se(r == 0); ++ assert_se(l == 6); ++ + r = safe_atoi16("36536", &l); + assert_se(r == -ERANGE); + +@@ -475,6 +503,13 @@ static void test_safe_atoux16(void) { + r = safe_atoux16(" -1", &l); + assert_se(r == -ERANGE); + ++ r = safe_atoux16("0b1", &l); ++ assert_se(r == 0); ++ assert_se(l == 177); ++ ++ r = safe_atoux16("0o70", &l); ++ assert_se(r == -EINVAL); ++ + r = safe_atoux16("junk", &l); + assert_se(r == -EINVAL); + +@@ -500,6 +535,14 @@ static void test_safe_atou64(void) { + assert_se(r == 0); + assert_se(l == 12345); + ++ r = safe_atou64("0o11", &l); ++ assert_se(r == 0); ++ assert_se(l == 9); ++ ++ r = safe_atou64("0b11", &l); ++ assert_se(r == 0); ++ assert_se(l == 3); ++ + r = safe_atou64("18446744073709551617", &l); + assert_se(r == -ERANGE); + +@@ -542,6 +585,14 @@ static void test_safe_atoi64(void) { + assert_se(r == 0); + assert_se(l == 32767); + ++ r = safe_atoi64(" 0o20", &l); ++ assert_se(r == 0); ++ assert_se(l == 16); ++ ++ r = safe_atoi64(" 0b01010", &l); ++ assert_se(r == 0); ++ assert_se(l == 10); ++ + r = safe_atoi64("9223372036854775813", &l); + assert_se(r == -ERANGE); + +@@ -577,6 +628,13 @@ static void test_safe_atoux64(void) { + assert_se(r == 0); + assert_se(l == 0x12345); + ++ r = safe_atoux64("0b11011", &l); ++ assert_se(r == 0); ++ assert_se(l == 11603985); ++ ++ r = safe_atoux64("0o11011", &l); ++ assert_se(r == -EINVAL); ++ + r = safe_atoux64("18446744073709551617", &l); + assert_se(r == -ERANGE); + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 99203f7e48..04e86f5ac3 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -40,6 +40,22 @@ static void test_parse_uid(void) { + + log_info("/* %s */", __func__); + ++ r = parse_uid("0", &uid); ++ assert_se(r == 0); ++ assert_se(uid == 0); ++ ++ r = parse_uid("1", &uid); ++ assert_se(r == 0); ++ assert_se(uid == 1); ++ ++ r = parse_uid("01", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 1); ++ ++ r = parse_uid("001", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 1); ++ + r = parse_uid("100", &uid); + assert_se(r == 0); + assert_se(uid == 100); +@@ -52,6 +68,14 @@ static void test_parse_uid(void) { + assert_se(r == -EINVAL); + assert_se(uid == 100); + ++ r = parse_uid("0o1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("0b1234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ + r = parse_uid("+1234", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); +@@ -68,6 +92,14 @@ static void test_parse_uid(void) { + assert_se(r == -EINVAL); + assert_se(uid == 100); + ++ r = parse_uid("001234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("0001234", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ + r = parse_uid("-0", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); +@@ -76,6 +108,14 @@ static void test_parse_uid(void) { + assert_se(r == -EINVAL); + assert_se(uid == 100); + ++ r = parse_uid("00", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ ++ r = parse_uid("000", &uid); ++ assert_se(r == -EINVAL); ++ assert_se(uid == 100); ++ + r = parse_uid("asdsdas", &uid); + assert_se(r == -EINVAL); + assert_se(uid == 100); diff --git a/SOURCES/0469-shared-user-util-add-compat-forms-of-user-name-check.patch b/SOURCES/0469-shared-user-util-add-compat-forms-of-user-name-check.patch new file mode 100644 index 0000000..eefc47b --- /dev/null +++ b/SOURCES/0469-shared-user-util-add-compat-forms-of-user-name-check.patch @@ -0,0 +1,270 @@ +From 1e4ec1b29d15684a305bbc9ab54c6c8321504e7b Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Tue, 27 Oct 2020 10:31:05 +0100 +Subject: [PATCH] shared/user-util: add compat forms of user name checking + functions + +New functions are called valid_user_group_name_compat() and +valid_user_group_name_or_id_compat() and accept dots in the user +or group name. No functional change except the tests. + +(cherry picked from commit 1a29610f5fa1bcb2eeb37d2c6b79d8d1a6dbb865) + +This completes previous partial cherry-pick of the same commit (commit +76176de0889c3e8b9b3a176da24e4f8dbbd380a3). + +Related: #1848373 +--- + src/basic/user-util.c | 32 +++++++------- + src/basic/user-util.h | 16 ++++++- + src/test/test-user-util.c | 91 +++++++++++++++++++++++++++++++++++++-- + 3 files changed, 117 insertions(+), 22 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 40f4e45db6..03cbbc2503 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -576,7 +576,7 @@ int take_etc_passwd_lock(const char *root) { + return fd; + } + +-bool valid_user_group_name(const char *u) { ++bool valid_user_group_name_full(const char *u, bool strict) { + const char *i; + long sz; + +@@ -585,12 +585,12 @@ bool valid_user_group_name(const char *u) { + * + * - We require that names fit into the appropriate utmp field + * - We don't allow empty user names ++ * - No dots or digits in the first character + * +- * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. ++ * If strict==true, additionally: ++ * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) + * +- * jsynacek: We now allow dots in user names. The checks are not exhaustive as user names like "..." are allowed +- * and valid according to POSIX, but can't be created using useradd. However, ".user" can be created. Let's not +- * complicate the code by adding additional checks for weird corner cases like these, as they don't really matter here. ++ * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. + */ + + if (isempty(u)) +@@ -598,16 +598,16 @@ bool valid_user_group_name(const char *u) { + + if (!(u[0] >= 'a' && u[0] <= 'z') && + !(u[0] >= 'A' && u[0] <= 'Z') && +- u[0] != '_' && u[0] != '.') ++ u[0] != '_') + return false; + +- for (i = u+1; *i; i++) { +- if (!(*i >= 'a' && *i <= 'z') && +- !(*i >= 'A' && *i <= 'Z') && +- !(*i >= '0' && *i <= '9') && +- !IN_SET(*i, '_', '-', '.')) ++ for (i = u+1; *i; i++) ++ if (!((*i >= 'a' && *i <= 'z') || ++ (*i >= 'A' && *i <= 'Z') || ++ (*i >= '0' && *i <= '9') || ++ IN_SET(*i, '_', '-') || ++ (!strict && *i == '.'))) + return false; +- } + + sz = sysconf(_SC_LOGIN_NAME_MAX); + assert_se(sz > 0); +@@ -621,15 +621,15 @@ bool valid_user_group_name(const char *u) { + return true; + } + +-bool valid_user_group_name_or_id(const char *u) { ++bool valid_user_group_name_or_id_full(const char *u, bool strict) { + +- /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right +- * range, and not the invalid user ids. */ ++ /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the ++ * right range, and not the invalid user ids. */ + + if (isempty(u)) + return false; + +- if (valid_user_group_name(u)) ++ if (valid_user_group_name_full(u, strict)) + return true; + + return parse_uid(u, NULL) >= 0; +diff --git a/src/basic/user-util.h b/src/basic/user-util.h +index b74f168859..5ad0b2a2f9 100644 +--- a/src/basic/user-util.h ++++ b/src/basic/user-util.h +@@ -78,8 +78,20 @@ static inline bool userns_supported(void) { + return access("/proc/self/uid_map", F_OK) >= 0; + } + +-bool valid_user_group_name(const char *u); +-bool valid_user_group_name_or_id(const char *u); ++bool valid_user_group_name_full(const char *u, bool strict); ++bool valid_user_group_name_or_id_full(const char *u, bool strict); ++static inline bool valid_user_group_name(const char *u) { ++ return valid_user_group_name_full(u, true); ++} ++static inline bool valid_user_group_name_or_id(const char *u) { ++ return valid_user_group_name_or_id_full(u, true); ++} ++static inline bool valid_user_group_name_compat(const char *u) { ++ return valid_user_group_name_full(u, false); ++} ++static inline bool valid_user_group_name_or_id_compat(const char *u) { ++ return valid_user_group_name_or_id_full(u, false); ++} + bool valid_gecos(const char *d); + bool valid_home(const char *p); + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 04e86f5ac3..3a4211655d 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -131,6 +131,43 @@ static void test_uid_ptr(void) { + assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000); + } + ++static void test_valid_user_group_name_compat(void) { ++ log_info("/* %s */", __func__); ++ ++ assert_se(!valid_user_group_name_compat(NULL)); ++ assert_se(!valid_user_group_name_compat("")); ++ assert_se(!valid_user_group_name_compat("1")); ++ assert_se(!valid_user_group_name_compat("65535")); ++ assert_se(!valid_user_group_name_compat("-1")); ++ assert_se(!valid_user_group_name_compat("-kkk")); ++ assert_se(!valid_user_group_name_compat("rööt")); ++ assert_se(!valid_user_group_name_compat(".")); ++ assert_se(!valid_user_group_name_compat(".eff")); ++ assert_se(!valid_user_group_name_compat("foo\nbar")); ++ assert_se(!valid_user_group_name_compat("0123456789012345678901234567890123456789")); ++ assert_se(!valid_user_group_name_or_id_compat("aaa:bbb")); ++ assert_se(!valid_user_group_name_compat(".")); ++ assert_se(!valid_user_group_name_compat(".1")); ++ assert_se(!valid_user_group_name_compat(".65535")); ++ assert_se(!valid_user_group_name_compat(".-1")); ++ assert_se(!valid_user_group_name_compat(".-kkk")); ++ assert_se(!valid_user_group_name_compat(".rööt")); ++ assert_se(!valid_user_group_name_or_id_compat(".aaa:bbb")); ++ ++ assert_se(valid_user_group_name_compat("root")); ++ assert_se(valid_user_group_name_compat("lennart")); ++ assert_se(valid_user_group_name_compat("LENNART")); ++ assert_se(valid_user_group_name_compat("_kkk")); ++ assert_se(valid_user_group_name_compat("kkk-")); ++ assert_se(valid_user_group_name_compat("kk-k")); ++ assert_se(valid_user_group_name_compat("eff.eff")); ++ assert_se(valid_user_group_name_compat("eff.")); ++ ++ assert_se(valid_user_group_name_compat("some5")); ++ assert_se(!valid_user_group_name_compat("5some")); ++ assert_se(valid_user_group_name_compat("INNER5NUMBER")); ++} ++ + static void test_valid_user_group_name(void) { + log_info("/* %s */", __func__); + +@@ -141,9 +178,18 @@ static void test_valid_user_group_name(void) { + assert_se(!valid_user_group_name("-1")); + assert_se(!valid_user_group_name("-kkk")); + assert_se(!valid_user_group_name("rööt")); ++ assert_se(!valid_user_group_name(".")); ++ assert_se(!valid_user_group_name(".eff")); + assert_se(!valid_user_group_name("foo\nbar")); + assert_se(!valid_user_group_name("0123456789012345678901234567890123456789")); + assert_se(!valid_user_group_name_or_id("aaa:bbb")); ++ assert_se(!valid_user_group_name(".")); ++ assert_se(!valid_user_group_name(".1")); ++ assert_se(!valid_user_group_name(".65535")); ++ assert_se(!valid_user_group_name(".-1")); ++ assert_se(!valid_user_group_name(".-kkk")); ++ assert_se(!valid_user_group_name(".rööt")); ++ assert_se(!valid_user_group_name_or_id(".aaa:bbb")); + + assert_se(valid_user_group_name("root")); + assert_se(valid_user_group_name("lennart")); +@@ -151,14 +197,47 @@ static void test_valid_user_group_name(void) { + assert_se(valid_user_group_name("_kkk")); + assert_se(valid_user_group_name("kkk-")); + assert_se(valid_user_group_name("kk-k")); +- assert_se(valid_user_group_name(".moo")); +- assert_se(valid_user_group_name("eff.eff")); ++ assert_se(!valid_user_group_name("eff.eff")); ++ assert_se(!valid_user_group_name("eff.")); + + assert_se(valid_user_group_name("some5")); + assert_se(!valid_user_group_name("5some")); + assert_se(valid_user_group_name("INNER5NUMBER")); + } + ++static void test_valid_user_group_name_or_id_compat(void) { ++ log_info("/* %s */", __func__); ++ ++ assert_se(!valid_user_group_name_or_id_compat(NULL)); ++ assert_se(!valid_user_group_name_or_id_compat("")); ++ assert_se(valid_user_group_name_or_id_compat("0")); ++ assert_se(valid_user_group_name_or_id_compat("1")); ++ assert_se(valid_user_group_name_or_id_compat("65534")); ++ assert_se(!valid_user_group_name_or_id_compat("65535")); ++ assert_se(valid_user_group_name_or_id_compat("65536")); ++ assert_se(!valid_user_group_name_or_id_compat("-1")); ++ assert_se(!valid_user_group_name_or_id_compat("-kkk")); ++ assert_se(!valid_user_group_name_or_id_compat("rööt")); ++ assert_se(!valid_user_group_name_or_id_compat(".")); ++ assert_se(!valid_user_group_name_or_id_compat(".eff")); ++ assert_se(valid_user_group_name_or_id_compat("eff.eff")); ++ assert_se(valid_user_group_name_or_id_compat("eff.")); ++ assert_se(!valid_user_group_name_or_id_compat("foo\nbar")); ++ assert_se(!valid_user_group_name_or_id_compat("0123456789012345678901234567890123456789")); ++ assert_se(!valid_user_group_name_or_id_compat("aaa:bbb")); ++ ++ assert_se(valid_user_group_name_or_id_compat("root")); ++ assert_se(valid_user_group_name_or_id_compat("lennart")); ++ assert_se(valid_user_group_name_or_id_compat("LENNART")); ++ assert_se(valid_user_group_name_or_id_compat("_kkk")); ++ assert_se(valid_user_group_name_or_id_compat("kkk-")); ++ assert_se(valid_user_group_name_or_id_compat("kk-k")); ++ ++ assert_se(valid_user_group_name_or_id_compat("some5")); ++ assert_se(!valid_user_group_name_or_id_compat("5some")); ++ assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER")); ++} ++ + static void test_valid_user_group_name_or_id(void) { + log_info("/* %s */", __func__); + +@@ -172,6 +251,10 @@ static void test_valid_user_group_name_or_id(void) { + assert_se(!valid_user_group_name_or_id("-1")); + assert_se(!valid_user_group_name_or_id("-kkk")); + assert_se(!valid_user_group_name_or_id("rööt")); ++ assert_se(!valid_user_group_name_or_id(".")); ++ assert_se(!valid_user_group_name_or_id(".eff")); ++ assert_se(!valid_user_group_name_or_id("eff.eff")); ++ assert_se(!valid_user_group_name_or_id("eff.")); + assert_se(!valid_user_group_name_or_id("foo\nbar")); + assert_se(!valid_user_group_name_or_id("0123456789012345678901234567890123456789")); + assert_se(!valid_user_group_name_or_id("aaa:bbb")); +@@ -182,8 +265,6 @@ static void test_valid_user_group_name_or_id(void) { + assert_se(valid_user_group_name_or_id("_kkk")); + assert_se(valid_user_group_name_or_id("kkk-")); + assert_se(valid_user_group_name_or_id("kk-k")); +- assert_se(valid_user_group_name_or_id(".moo")); +- assert_se(valid_user_group_name_or_id("eff.eff")); + + assert_se(valid_user_group_name_or_id("some5")); + assert_se(!valid_user_group_name_or_id("5some")); +@@ -286,7 +367,9 @@ int main(int argc, char*argv[]) { + test_parse_uid(); + test_uid_ptr(); + ++ test_valid_user_group_name_compat(); + test_valid_user_group_name(); ++ test_valid_user_group_name_or_id_compat(); + test_valid_user_group_name_or_id(); + test_valid_gecos(); + test_valid_home(); diff --git a/SOURCES/0470-shared-user-util-emit-a-warning-on-names-with-dots.patch b/SOURCES/0470-shared-user-util-emit-a-warning-on-names-with-dots.patch new file mode 100644 index 0000000..83422bc --- /dev/null +++ b/SOURCES/0470-shared-user-util-emit-a-warning-on-names-with-dots.patch @@ -0,0 +1,50 @@ +From fa1fa19951fdadd63f2b5df6224678f91753f260 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 28 Aug 2019 12:05:52 +0200 +Subject: [PATCH] shared/user-util: emit a warning on names with dots + +(cherry picked from commit 88e2ed0b5bf6f08f5a2d4d64b1fefdc7192b9aac) + +Related: #1848373 +--- + src/basic/user-util.c | 27 ++++++++++++++++++++------- + 1 file changed, 20 insertions(+), 7 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 03cbbc2503..359da08a83 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -601,13 +601,26 @@ bool valid_user_group_name_full(const char *u, bool strict) { + u[0] != '_') + return false; + +- for (i = u+1; *i; i++) +- if (!((*i >= 'a' && *i <= 'z') || +- (*i >= 'A' && *i <= 'Z') || +- (*i >= '0' && *i <= '9') || +- IN_SET(*i, '_', '-') || +- (!strict && *i == '.'))) +- return false; ++ bool warned = false; ++ ++ for (i = u+1; *i; i++) { ++ if (((*i >= 'a' && *i <= 'z') || ++ (*i >= 'A' && *i <= 'Z') || ++ (*i >= '0' && *i <= '9') || ++ IN_SET(*i, '_', '-'))) ++ continue; ++ ++ if (*i == '.' && !strict) { ++ if (!warned) { ++ log_warning("Bad user or group name \"%s\", accepting for compatibility.", u); ++ warned = true; ++ } ++ ++ continue; ++ } ++ ++ return false; ++ } + + sz = sysconf(_SC_LOGIN_NAME_MAX); + assert_se(sz > 0); diff --git a/SOURCES/0471-user-util-Allow-names-starting-with-a-digit.patch b/SOURCES/0471-user-util-Allow-names-starting-with-a-digit.patch new file mode 100644 index 0000000..52a315a --- /dev/null +++ b/SOURCES/0471-user-util-Allow-names-starting-with-a-digit.patch @@ -0,0 +1,103 @@ +From f06434cc51eedd72f7d4a640a1fa118f57a5e68e Mon Sep 17 00:00:00 2001 +From: Balint Reczey +Date: Wed, 18 Mar 2020 18:29:02 +0100 +Subject: [PATCH] user-util: Allow names starting with a digit + +In 1a29610f5fa1bcb2eeb37d2c6b79d8d1a6dbb865 the change inadvertedly +disabled names with digit as the first character. This follow-up change +allows a digit as the first character in compat mode. + +Fixes: #15141 +(cherry picked from commit 93c23c9297e48e594785e0bb9c51504aae5fbe3e) + +Related: #1848373 +--- + src/basic/user-util.c | 20 +++++++++++++++++--- + src/test/test-user-util.c | 4 ++-- + 2 files changed, 19 insertions(+), 5 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 359da08a83..7dd2bb2c84 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -579,16 +579,18 @@ int take_etc_passwd_lock(const char *root) { + bool valid_user_group_name_full(const char *u, bool strict) { + const char *i; + long sz; ++ bool warned = false; + + /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, + * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules: + * + * - We require that names fit into the appropriate utmp field + * - We don't allow empty user names +- * - No dots or digits in the first character ++ * - No dots in the first character + * + * If strict==true, additionally: + * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) ++ * - We don't allow a digit as the first character + * + * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. + */ +@@ -598,17 +600,26 @@ bool valid_user_group_name_full(const char *u, bool strict) { + + if (!(u[0] >= 'a' && u[0] <= 'z') && + !(u[0] >= 'A' && u[0] <= 'Z') && ++ !(u[0] >= '0' && u[0] <= '9' && !strict) && + u[0] != '_') + return false; + +- bool warned = false; ++ bool only_digits_seen = u[0] >= '0' && u[0] <= '9'; ++ ++ if (only_digits_seen) { ++ log_warning("User or group name \"%s\" starts with a digit, accepting for compatibility.", u); ++ warned = true; ++ } + + for (i = u+1; *i; i++) { + if (((*i >= 'a' && *i <= 'z') || + (*i >= 'A' && *i <= 'Z') || + (*i >= '0' && *i <= '9') || +- IN_SET(*i, '_', '-'))) ++ IN_SET(*i, '_', '-'))) { ++ if (!(*i >= '0' && *i <= '9')) ++ only_digits_seen = false; + continue; ++ } + + if (*i == '.' && !strict) { + if (!warned) { +@@ -622,6 +633,9 @@ bool valid_user_group_name_full(const char *u, bool strict) { + return false; + } + ++ if (only_digits_seen) ++ return false; ++ + sz = sysconf(_SC_LOGIN_NAME_MAX); + assert_se(sz > 0); + +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 3a4211655d..56079f1486 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -164,7 +164,7 @@ static void test_valid_user_group_name_compat(void) { + assert_se(valid_user_group_name_compat("eff.")); + + assert_se(valid_user_group_name_compat("some5")); +- assert_se(!valid_user_group_name_compat("5some")); ++ assert_se(valid_user_group_name_compat("5some")); + assert_se(valid_user_group_name_compat("INNER5NUMBER")); + } + +@@ -234,7 +234,7 @@ static void test_valid_user_group_name_or_id_compat(void) { + assert_se(valid_user_group_name_or_id_compat("kk-k")); + + assert_se(valid_user_group_name_or_id_compat("some5")); +- assert_se(!valid_user_group_name_or_id_compat("5some")); ++ assert_se(valid_user_group_name_or_id_compat("5some")); + assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER")); + } + diff --git a/SOURCES/0472-shared-user-util-allow-usernames-with-dots-in-specif.patch b/SOURCES/0472-shared-user-util-allow-usernames-with-dots-in-specif.patch new file mode 100644 index 0000000..37544d6 --- /dev/null +++ b/SOURCES/0472-shared-user-util-allow-usernames-with-dots-in-specif.patch @@ -0,0 +1,193 @@ +From 40dff18947fa198810db4cd3e5291349fc84a0e8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 1 Aug 2019 10:02:14 +0200 +Subject: [PATCH] shared/user-util: allow usernames with dots in specific + fields + +People do have usernames with dots, and it makes them very unhappy that systemd +doesn't like their that. It seems that there is no actual problem with allowing +dots in the username. In particular chown declares ":" as the official +separator, and internally in systemd we never rely on "." as the seperator +between user and group (nor do we call chown directly). Using dots in the name +is probably not a very good idea, but we don't need to care. Debian tools +(adduser) do not allow users with dots to be created. + +This patch allows *existing* names with dots to be used in User, Group, +SupplementaryGroups, SocketUser, SocketGroup fields, both in unit files and on +the command line. DynamicUsers and sysusers still follow the strict policy. +user@.service and tmpfiles already allowed arbitrary user names, and this +remains unchanged. + +Fixes #12754. + +(cherry picked from commit ae480f0b09aec815b64579bb1828ea935d8ee236) + +Related: #1848373 +--- + src/core/dbus-execute.c | 12 ++++++------ + src/core/dbus-socket.c | 4 ++-- + src/core/dbus-util.c | 2 +- + src/core/dbus-util.h | 2 +- + src/core/load-fragment-gperf.gperf.m4 | 10 +++++----- + src/core/load-fragment.c | 8 ++++---- + src/core/load-fragment.h | 4 ++-- + 7 files changed, 21 insertions(+), 21 deletions(-) + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index 0fe4c14e48..e004fb55c9 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -1113,10 +1113,10 @@ int bus_exec_context_set_transient_property( + flags |= UNIT_PRIVATE; + + if (streq(name, "User")) +- return bus_set_transient_user(u, name, &c->user, message, flags, error); ++ return bus_set_transient_user_compat(u, name, &c->user, message, flags, error); + + if (streq(name, "Group")) +- return bus_set_transient_user(u, name, &c->group, message, flags, error); ++ return bus_set_transient_user_compat(u, name, &c->group, message, flags, error); + + if (streq(name, "TTYPath")) + return bus_set_transient_path(u, name, &c->tty_path, message, flags, error); +@@ -1297,10 +1297,10 @@ int bus_exec_context_set_transient_property( + if (r < 0) + return r; + +- STRV_FOREACH(p, l) { +- if (!isempty(*p) && !valid_user_group_name_or_id(*p)) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid supplementary group names"); +- } ++ STRV_FOREACH(p, l) ++ if (!isempty(*p) && !valid_user_group_name_or_id_compat(*p)) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, ++ "Invalid supplementary group names"); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (strv_isempty(l)) { +diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c +index bb77539030..8fdbc05409 100644 +--- a/src/core/dbus-socket.c ++++ b/src/core/dbus-socket.c +@@ -281,10 +281,10 @@ static int bus_socket_set_transient_property( + return bus_set_transient_fdname(u, name, &s->fdname, message, flags, error); + + if (streq(name, "SocketUser")) +- return bus_set_transient_user(u, name, &s->user, message, flags, error); ++ return bus_set_transient_user_compat(u, name, &s->user, message, flags, error); + + if (streq(name, "SocketGroup")) +- return bus_set_transient_user(u, name, &s->group, message, flags, error); ++ return bus_set_transient_user_compat(u, name, &s->group, message, flags, error); + + if (streq(name, "BindIPv6Only")) + return bus_set_transient_bind_ipv6_only(u, name, &s->bind_ipv6_only, message, flags, error); +diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c +index f4fbb72cb9..7862beaacb 100644 +--- a/src/core/dbus-util.c ++++ b/src/core/dbus-util.c +@@ -30,7 +30,7 @@ int bus_property_get_triggered_unit( + + BUS_DEFINE_SET_TRANSIENT(mode_t, "u", uint32_t, mode_t, "%040o"); + BUS_DEFINE_SET_TRANSIENT(unsigned, "u", uint32_t, unsigned, "%" PRIu32); +-BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user, valid_user_group_name_or_id); ++BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_compat, valid_user_group_name_or_id_compat); + BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(path, path_is_absolute); + + int bus_set_transient_string( +diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h +index 12b055e4ac..a3316c6701 100644 +--- a/src/core/dbus-util.h ++++ b/src/core/dbus-util.h +@@ -235,7 +235,7 @@ int bus_property_get_triggered_unit(sd_bus *bus, const char *path, const char *i + + int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +-int bus_set_transient_user(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); ++int bus_set_transient_user_compat(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 24ee5ae6fe..156a4d0a6d 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -25,9 +25,9 @@ m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', + `$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context) + $1.RootDirectory, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_directory) + $1.RootImage, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_image) +-$1.User, config_parse_user_group, 0, offsetof($1, exec_context.user) +-$1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group) +-$1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups) ++$1.User, config_parse_user_group_compat, 0, offsetof($1, exec_context.user) ++$1.Group, config_parse_user_group_compat, 0, offsetof($1, exec_context.group) ++$1.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof($1, exec_context.supplementary_groups) + $1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context) + $1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context) + $1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context) +@@ -354,8 +354,8 @@ Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC + Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) + Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) + Socket.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Socket, timeout_usec) +-Socket.SocketUser, config_parse_user_group, 0, offsetof(Socket, user) +-Socket.SocketGroup, config_parse_user_group, 0, offsetof(Socket, group) ++Socket.SocketUser, config_parse_user_group_compat, 0, offsetof(Socket, user) ++Socket.SocketGroup, config_parse_user_group_compat, 0, offsetof(Socket, group) + Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) + Socket.DirectoryMode, config_parse_mode, 0, offsetof(Socket, directory_mode) + Socket.Accept, config_parse_bool, 0, offsetof(Socket, accept) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 740401a582..ba81d94504 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -1899,7 +1899,7 @@ int config_parse_sec_fix_0( + return 0; + } + +-int config_parse_user_group( ++int config_parse_user_group_compat( + const char *unit, + const char *filename, + unsigned line, +@@ -1932,7 +1932,7 @@ int config_parse_user_group( + return -ENOEXEC; + } + +- if (!valid_user_group_name_or_id(k)) { ++ if (!valid_user_group_name_or_id_compat(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; + } +@@ -1940,7 +1940,7 @@ int config_parse_user_group( + return free_and_replace(*user, k); + } + +-int config_parse_user_group_strv( ++int config_parse_user_group_strv_compat( + const char *unit, + const char *filename, + unsigned line, +@@ -1986,7 +1986,7 @@ int config_parse_user_group_strv( + return -ENOEXEC; + } + +- if (!valid_user_group_name_or_id(k)) { ++ if (!valid_user_group_name_or_id_compat(k)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; + } +diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h +index 65a94d53cc..f9d34d484d 100644 +--- a/src/core/load-fragment.h ++++ b/src/core/load-fragment.h +@@ -96,8 +96,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_utmp_mode); + CONFIG_PARSER_PROTOTYPE(config_parse_working_directory); + CONFIG_PARSER_PROTOTYPE(config_parse_fdname); + CONFIG_PARSER_PROTOTYPE(config_parse_sec_fix_0); +-CONFIG_PARSER_PROTOTYPE(config_parse_user_group); +-CONFIG_PARSER_PROTOTYPE(config_parse_user_group_strv); ++CONFIG_PARSER_PROTOTYPE(config_parse_user_group_compat); ++CONFIG_PARSER_PROTOTYPE(config_parse_user_group_strv_compat); + CONFIG_PARSER_PROTOTYPE(config_parse_restrict_namespaces); + CONFIG_PARSER_PROTOTYPE(config_parse_bind_paths); + CONFIG_PARSER_PROTOTYPE(config_parse_exec_keyring_mode); diff --git a/SOURCES/0473-user-util-switch-order-of-checks-in-valid_user_group.patch b/SOURCES/0473-user-util-switch-order-of-checks-in-valid_user_group.patch new file mode 100644 index 0000000..b982f8c --- /dev/null +++ b/SOURCES/0473-user-util-switch-order-of-checks-in-valid_user_group.patch @@ -0,0 +1,38 @@ +From 7569168bea3d7e11cd3afe6167fcf4a3ac65a1a6 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 30 Mar 2020 21:46:01 +0200 +Subject: [PATCH] user-util: switch order of checks in + valid_user_group_name_or_id_full() + +When we are supposed to accept numeric UIDs formatted as string, then +let's check that first, before passing things on to +valid_user_group_name_full(), since that might log about, and not the +other way round. + +See: #15201 +Follow-up for: 93c23c9297e48e594785e0bb9c51504aae5fbe3e + +(cherry picked from commit a85daa0dfb3eb03be9845760e90e54b9af8fb00e) + +Related: #1848373 +--- + src/basic/user-util.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 7dd2bb2c84..68a924770b 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -656,10 +656,10 @@ bool valid_user_group_name_or_id_full(const char *u, bool strict) { + if (isempty(u)) + return false; + +- if (valid_user_group_name_full(u, strict)) ++ if (parse_uid(u, NULL) >= 0) + return true; + +- return parse_uid(u, NULL) >= 0; ++ return valid_user_group_name_full(u, strict); + } + + bool valid_gecos(const char *d) { diff --git a/SOURCES/0474-user-util-rework-how-we-validate-user-names.patch b/SOURCES/0474-user-util-rework-how-we-validate-user-names.patch new file mode 100644 index 0000000..9607f81 --- /dev/null +++ b/SOURCES/0474-user-util-rework-how-we-validate-user-names.patch @@ -0,0 +1,803 @@ +From 33b851f0c30e47fe71a293e2c990ef26573efe86 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Sat, 4 Apr 2020 12:23:02 +0200 +Subject: [PATCH] user-util: rework how we validate user names +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reworks the user validation infrastructure. There are now two +modes. In regular mode we are strict and test against a strict set of +valid chars. And in "relaxed" mode we just filter out some really +obvious, dangerous stuff. i.e. strict is whitelisting what is OK, but +"relaxed" is blacklisting what is really not OK. + +The idea is that we use strict mode whenver we allocate a new user +(i.e. in sysusers.d or homed), while "relaxed" mode is when we process +users registered elsewhere, (i.e. userdb, logind, …) + +The requirements on user name validity vary wildly. SSSD thinks its fine +to embedd "@" for example, while the suggested NAME_REGEX field on +Debian does not even allow uppercase chars… + +This effectively liberaralizes a lot what we expect from usernames. + +The code that warns about questionnable user names is now optional and +only used at places such as unit file parsing, so that it doesn't show +up on every userdb query, but only when processing configuration files +that know better. + +Fixes: #15149 #15090 +(cherry picked from commit 7a8867abfab10e5bbca10590ec2aa40c5b27d8fb) + +Resolves: #1848373 +--- + src/basic/user-util.c | 185 +++++++++++++---------- + src/basic/user-util.h | 21 +-- + src/core/dbus-execute.c | 6 +- + src/core/dbus-manager.c | 2 +- + src/core/dbus-socket.c | 4 +- + src/core/dbus-util.c | 7 +- + src/core/dbus-util.h | 2 +- + src/core/dynamic-user.c | 2 +- + src/core/load-fragment.c | 4 +- + src/core/unit.c | 2 +- + src/nss-systemd/nss-systemd.c | 6 +- + src/systemd/sd-messages.h | 3 + + src/sysusers/sysusers.c | 4 +- + src/test/test-user-util.c | 271 ++++++++++++++++++---------------- + 14 files changed, 287 insertions(+), 232 deletions(-) + +diff --git a/src/basic/user-util.c b/src/basic/user-util.c +index 68a924770b..cd870c4361 100644 +--- a/src/basic/user-util.c ++++ b/src/basic/user-util.c +@@ -14,6 +14,8 @@ + #include + #include + ++#include "sd-messages.h" ++ + #include "alloc-util.h" + #include "fd-util.h" + #include "fileio.h" +@@ -576,92 +578,125 @@ int take_etc_passwd_lock(const char *root) { + return fd; + } + +-bool valid_user_group_name_full(const char *u, bool strict) { ++bool valid_user_group_name(const char *u, ValidUserFlags flags) { + const char *i; +- long sz; +- bool warned = false; + +- /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, +- * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules: +- * +- * - We require that names fit into the appropriate utmp field +- * - We don't allow empty user names +- * - No dots in the first character ++ /* Checks if the specified name is a valid user/group name. There are two flavours of this call: ++ * strict mode is the default which is POSIX plus some extra rules; and relaxed mode where we accept ++ * pretty much everything except the really worst offending names. + * +- * If strict==true, additionally: +- * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) +- * - We don't allow a digit as the first character +- * +- * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. +- */ ++ * Whenever we synthesize users ourselves we should use the strict mode. But when we process users ++ * created by other stuff, let's be more liberal. */ + +- if (isempty(u)) ++ if (isempty(u)) /* An empty user name is never valid */ + return false; + +- if (!(u[0] >= 'a' && u[0] <= 'z') && +- !(u[0] >= 'A' && u[0] <= 'Z') && +- !(u[0] >= '0' && u[0] <= '9' && !strict) && +- u[0] != '_') +- return false; +- +- bool only_digits_seen = u[0] >= '0' && u[0] <= '9'; +- +- if (only_digits_seen) { +- log_warning("User or group name \"%s\" starts with a digit, accepting for compatibility.", u); +- warned = true; +- } +- +- for (i = u+1; *i; i++) { +- if (((*i >= 'a' && *i <= 'z') || +- (*i >= 'A' && *i <= 'Z') || +- (*i >= '0' && *i <= '9') || +- IN_SET(*i, '_', '-'))) { +- if (!(*i >= '0' && *i <= '9')) +- only_digits_seen = false; +- continue; +- } +- +- if (*i == '.' && !strict) { +- if (!warned) { +- log_warning("Bad user or group name \"%s\", accepting for compatibility.", u); +- warned = true; +- } +- +- continue; +- } +- +- return false; ++ if (parse_uid(u, NULL) >= 0) /* Something that parses as numeric UID string is valid exactly when the ++ * flag for it is set */ ++ return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC); ++ ++ if (FLAGS_SET(flags, VALID_USER_RELAX)) { ++ ++ /* In relaxed mode we just check very superficially. Apparently SSSD and other stuff is ++ * extremely liberal (way too liberal if you ask me, even inserting "@" in user names, which ++ * is bound to cause problems for example when used with an MTA), hence only filter the most ++ * obvious cases, or where things would result in an invalid entry if such a user name would ++ * show up in /etc/passwd (or equivalent getent output). ++ * ++ * Note that we stepped far out of POSIX territory here. It's not our fault though, but ++ * SSSD's, Samba's and everybody else who ignored POSIX on this. (I mean, I am happy to step ++ * outside of POSIX' bounds any day, but I must say in this case I probably wouldn't ++ * have...) */ ++ ++ if (startswith(u, " ") || endswith(u, " ")) /* At least expect whitespace padding is removed ++ * at front and back (accept in the middle, since ++ * that's apparently a thing on Windows). Note ++ * that this also blocks usernames consisting of ++ * whitespace only. */ ++ return false; ++ ++ if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */ ++ return false; ++ ++ if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the ++ * record separator in /etc/passwd), so we can't allow that. */ ++ return false; ++ ++ if (strpbrk(u, ":/")) /* Colons are the field separator in /etc/passwd, we can't allow ++ * that. Slashes are special to file systems paths and user names ++ * typically show up in the file system as home directories, hence ++ * don't allow slashes. */ ++ return false; ++ ++ if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused ++ * with with UIDs (note that this test is more broad than ++ * the parse_uid() test above, as it will cover more than ++ * the 32bit range, and it will detect 65535 (which is in ++ * invalid UID, even though in the unsigned 32 bit range) */ ++ return false; ++ ++ if (u[0] == '-' && in_charset(u + 1, "0123456789")) /* Don't allow negative fully numeric ++ * strings either. After all some people ++ * write 65535 as -1 (even though that's ++ * not even true on 32bit uid_t ++ * anyway) */ ++ return false; ++ ++ if (dot_or_dot_dot(u)) /* User names typically become home directory names, and these two are ++ * special in that context, don't allow that. */ ++ return false; ++ ++ /* Compare with strict result and warn if result doesn't match */ ++ if (FLAGS_SET(flags, VALID_USER_WARN) && !valid_user_group_name(u, 0)) ++ log_struct(LOG_NOTICE, ++ "MESSAGE=Accepting user/group name '%s', which does not match strict user/group name rules.", u, ++ "USER_GROUP_NAME=%s", u, ++ "MESSAGE_ID=" SD_MESSAGE_UNSAFE_USER_NAME_STR); ++ ++ /* Note that we make no restrictions on the length in relaxed mode! */ ++ } else { ++ long sz; ++ size_t l; ++ ++ /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.437. We are a bit stricter here ++ * however. Specifically we deviate from POSIX rules: ++ * ++ * - We don't allow empty user names (see above) ++ * - We require that names fit into the appropriate utmp field ++ * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator) ++ * - We don't allow dashes or digit as the first character ++ * ++ * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters. ++ */ ++ ++ if (!(u[0] >= 'a' && u[0] <= 'z') && ++ !(u[0] >= 'A' && u[0] <= 'Z') && ++ u[0] != '_') ++ return false; ++ ++ for (i = u+1; *i; i++) ++ if (!(*i >= 'a' && *i <= 'z') && ++ !(*i >= 'A' && *i <= 'Z') && ++ !(*i >= '0' && *i <= '9') && ++ !IN_SET(*i, '_', '-')) ++ return false; ++ ++ l = i - u; ++ ++ sz = sysconf(_SC_LOGIN_NAME_MAX); ++ assert_se(sz > 0); ++ ++ if (l > (size_t) sz) ++ return false; ++ if (l > FILENAME_MAX) ++ return false; ++ if (l > UT_NAMESIZE - 1) ++ return false; + } + +- if (only_digits_seen) +- return false; +- +- sz = sysconf(_SC_LOGIN_NAME_MAX); +- assert_se(sz > 0); +- +- if ((size_t) (i-u) > (size_t) sz) +- return false; +- +- if ((size_t) (i-u) > UT_NAMESIZE - 1) +- return false; +- + return true; + } + +-bool valid_user_group_name_or_id_full(const char *u, bool strict) { +- +- /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the +- * right range, and not the invalid user ids. */ +- +- if (isempty(u)) +- return false; +- +- if (parse_uid(u, NULL) >= 0) +- return true; +- +- return valid_user_group_name_full(u, strict); +-} +- + bool valid_gecos(const char *d) { + + if (!d) +diff --git a/src/basic/user-util.h b/src/basic/user-util.h +index 5ad0b2a2f9..939bded40d 100644 +--- a/src/basic/user-util.h ++++ b/src/basic/user-util.h +@@ -78,20 +78,13 @@ static inline bool userns_supported(void) { + return access("/proc/self/uid_map", F_OK) >= 0; + } + +-bool valid_user_group_name_full(const char *u, bool strict); +-bool valid_user_group_name_or_id_full(const char *u, bool strict); +-static inline bool valid_user_group_name(const char *u) { +- return valid_user_group_name_full(u, true); +-} +-static inline bool valid_user_group_name_or_id(const char *u) { +- return valid_user_group_name_or_id_full(u, true); +-} +-static inline bool valid_user_group_name_compat(const char *u) { +- return valid_user_group_name_full(u, false); +-} +-static inline bool valid_user_group_name_or_id_compat(const char *u) { +- return valid_user_group_name_or_id_full(u, false); +-} ++typedef enum ValidUserFlags { ++ VALID_USER_RELAX = 1 << 0, ++ VALID_USER_WARN = 1 << 1, ++ VALID_USER_ALLOW_NUMERIC = 1 << 2, ++} ValidUserFlags; ++ ++bool valid_user_group_name(const char *u, ValidUserFlags flags); + bool valid_gecos(const char *d); + bool valid_home(const char *p); + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index e004fb55c9..8348663000 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -1113,10 +1113,10 @@ int bus_exec_context_set_transient_property( + flags |= UNIT_PRIVATE; + + if (streq(name, "User")) +- return bus_set_transient_user_compat(u, name, &c->user, message, flags, error); ++ return bus_set_transient_user_relaxed(u, name, &c->user, message, flags, error); + + if (streq(name, "Group")) +- return bus_set_transient_user_compat(u, name, &c->group, message, flags, error); ++ return bus_set_transient_user_relaxed(u, name, &c->group, message, flags, error); + + if (streq(name, "TTYPath")) + return bus_set_transient_path(u, name, &c->tty_path, message, flags, error); +@@ -1298,7 +1298,7 @@ int bus_exec_context_set_transient_property( + return r; + + STRV_FOREACH(p, l) +- if (!isempty(*p) && !valid_user_group_name_or_id_compat(*p)) ++ if (!isempty(*p) && !valid_user_group_name(*p, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid supplementary group names"); + +diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c +index 0a1d3df42f..7488f22116 100644 +--- a/src/core/dbus-manager.c ++++ b/src/core/dbus-manager.c +@@ -1762,7 +1762,7 @@ static int method_lookup_dynamic_user_by_name(sd_bus_message *message, void *use + + if (!MANAGER_IS_SYSTEM(m)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance."); +- if (!valid_user_group_name(name)) ++ if (!valid_user_group_name(name, VALID_USER_RELAX)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name invalid: %s", name); + + r = dynamic_user_lookup_name(m, name, &uid); +diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c +index 8fdbc05409..fa6bbe2c6f 100644 +--- a/src/core/dbus-socket.c ++++ b/src/core/dbus-socket.c +@@ -281,10 +281,10 @@ static int bus_socket_set_transient_property( + return bus_set_transient_fdname(u, name, &s->fdname, message, flags, error); + + if (streq(name, "SocketUser")) +- return bus_set_transient_user_compat(u, name, &s->user, message, flags, error); ++ return bus_set_transient_user_relaxed(u, name, &s->user, message, flags, error); + + if (streq(name, "SocketGroup")) +- return bus_set_transient_user_compat(u, name, &s->group, message, flags, error); ++ return bus_set_transient_user_relaxed(u, name, &s->group, message, flags, error); + + if (streq(name, "BindIPv6Only")) + return bus_set_transient_bind_ipv6_only(u, name, &s->bind_ipv6_only, message, flags, error); +diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c +index 7862beaacb..951450e53d 100644 +--- a/src/core/dbus-util.c ++++ b/src/core/dbus-util.c +@@ -30,7 +30,12 @@ int bus_property_get_triggered_unit( + + BUS_DEFINE_SET_TRANSIENT(mode_t, "u", uint32_t, mode_t, "%040o"); + BUS_DEFINE_SET_TRANSIENT(unsigned, "u", uint32_t, unsigned, "%" PRIu32); +-BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_compat, valid_user_group_name_or_id_compat); ++ ++static inline bool valid_user_group_name_or_id_relaxed(const char *u) { ++ return valid_user_group_name(u, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX); ++} ++ ++BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user_relaxed, valid_user_group_name_or_id_relaxed); + BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(path, path_is_absolute); + + int bus_set_transient_string( +diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h +index a3316c6701..713b464dd9 100644 +--- a/src/core/dbus-util.h ++++ b/src/core/dbus-util.h +@@ -235,7 +235,7 @@ int bus_property_get_triggered_unit(sd_bus *bus, const char *path, const char *i + + int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +-int bus_set_transient_user_compat(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); ++int bus_set_transient_user_relaxed(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c +index 021fd93a76..548b3cc9df 100644 +--- a/src/core/dynamic-user.c ++++ b/src/core/dynamic-user.c +@@ -108,7 +108,7 @@ static int dynamic_user_acquire(Manager *m, const char *name, DynamicUser** ret) + return 0; + } + +- if (!valid_user_group_name_or_id(name)) ++ if (!valid_user_group_name(name, VALID_USER_ALLOW_NUMERIC)) + return -EINVAL; + + if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, storage_socket) < 0) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index ba81d94504..e0d7b8f7f8 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -1932,7 +1932,7 @@ int config_parse_user_group_compat( + return -ENOEXEC; + } + +- if (!valid_user_group_name_or_id_compat(k)) { ++ if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; + } +@@ -1986,7 +1986,7 @@ int config_parse_user_group_strv_compat( + return -ENOEXEC; + } + +- if (!valid_user_group_name_or_id_compat(k)) { ++ if (!valid_user_group_name(k, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX|VALID_USER_WARN)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; + } +diff --git a/src/core/unit.c b/src/core/unit.c +index ffbf3cfd48..cd3e7c806d 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -4088,7 +4088,7 @@ static int user_from_unit_name(Unit *u, char **ret) { + if (r < 0) + return r; + +- if (valid_user_group_name(n)) { ++ if (valid_user_group_name(n, 0)) { + *ret = TAKE_PTR(n); + return 0; + } +diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c +index f8db27ae27..615c710257 100644 +--- a/src/nss-systemd/nss-systemd.c ++++ b/src/nss-systemd/nss-systemd.c +@@ -123,7 +123,7 @@ static int direct_lookup_uid(uid_t uid, char **ret) { + r = readlink_malloc(path, &s); + if (r < 0) + return r; +- if (!valid_user_group_name(s)) { /* extra safety check */ ++ if (!valid_user_group_name(s, VALID_USER_RELAX)) { /* extra safety check */ + free(s); + return -EINVAL; + } +@@ -153,7 +153,7 @@ enum nss_status _nss_systemd_getpwnam_r( + + /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't + * generate EINVAL here, because it isn't really out business to complain about invalid user names. */ +- if (!valid_user_group_name(name)) ++ if (!valid_user_group_name(name, VALID_USER_RELAX)) + return NSS_STATUS_NOTFOUND; + + /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */ +@@ -356,7 +356,7 @@ enum nss_status _nss_systemd_getgrnam_r( + assert(name); + assert(gr); + +- if (!valid_user_group_name(name)) ++ if (!valid_user_group_name(name, VALID_USER_RELAX)) + return NSS_STATUS_NOTFOUND; + + /* Synthesize records for root and nobody, in case they are missing form /etc/group */ +diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h +index bdd4fd3974..847b698ba4 100644 +--- a/src/systemd/sd-messages.h ++++ b/src/systemd/sd-messages.h +@@ -152,6 +152,9 @@ _SD_BEGIN_DECLARATIONS; + #define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) + #define SD_MESSAGE_DNSSEC_DOWNGRADE_STR SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) + ++#define SD_MESSAGE_UNSAFE_USER_NAME SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f) ++#define SD_MESSAGE_UNSAFE_USER_NAME_STR SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f) ++ + _SD_END_DECLARATIONS; + + #endif +diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c +index 33959d3c11..a374ebaaf4 100644 +--- a/src/sysusers/sysusers.c ++++ b/src/sysusers/sysusers.c +@@ -1413,7 +1413,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { + return r; + } + +- if (!valid_user_group_name(resolved_name)) { ++ if (!valid_user_group_name(resolved_name, 0)) { + log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name); + return -EINVAL; + } +@@ -1524,7 +1524,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { + return -EINVAL; + } + +- if (!valid_user_group_name(resolved_id)) { ++ if (!valid_user_group_name(resolved_id, 0)) { + log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id); + return -EINVAL; + } +diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c +index 56079f1486..31ac018da9 100644 +--- a/src/test/test-user-util.c ++++ b/src/test/test-user-util.c +@@ -131,144 +131,163 @@ static void test_uid_ptr(void) { + assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000); + } + +-static void test_valid_user_group_name_compat(void) { ++static void test_valid_user_group_name_relaxed(void) { + log_info("/* %s */", __func__); + +- assert_se(!valid_user_group_name_compat(NULL)); +- assert_se(!valid_user_group_name_compat("")); +- assert_se(!valid_user_group_name_compat("1")); +- assert_se(!valid_user_group_name_compat("65535")); +- assert_se(!valid_user_group_name_compat("-1")); +- assert_se(!valid_user_group_name_compat("-kkk")); +- assert_se(!valid_user_group_name_compat("rööt")); +- assert_se(!valid_user_group_name_compat(".")); +- assert_se(!valid_user_group_name_compat(".eff")); +- assert_se(!valid_user_group_name_compat("foo\nbar")); +- assert_se(!valid_user_group_name_compat("0123456789012345678901234567890123456789")); +- assert_se(!valid_user_group_name_or_id_compat("aaa:bbb")); +- assert_se(!valid_user_group_name_compat(".")); +- assert_se(!valid_user_group_name_compat(".1")); +- assert_se(!valid_user_group_name_compat(".65535")); +- assert_se(!valid_user_group_name_compat(".-1")); +- assert_se(!valid_user_group_name_compat(".-kkk")); +- assert_se(!valid_user_group_name_compat(".rööt")); +- assert_se(!valid_user_group_name_or_id_compat(".aaa:bbb")); +- +- assert_se(valid_user_group_name_compat("root")); +- assert_se(valid_user_group_name_compat("lennart")); +- assert_se(valid_user_group_name_compat("LENNART")); +- assert_se(valid_user_group_name_compat("_kkk")); +- assert_se(valid_user_group_name_compat("kkk-")); +- assert_se(valid_user_group_name_compat("kk-k")); +- assert_se(valid_user_group_name_compat("eff.eff")); +- assert_se(valid_user_group_name_compat("eff.")); +- +- assert_se(valid_user_group_name_compat("some5")); +- assert_se(valid_user_group_name_compat("5some")); +- assert_se(valid_user_group_name_compat("INNER5NUMBER")); ++ assert_se(!valid_user_group_name(NULL, VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("1", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("65535", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("-1", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("foo\nbar", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".aaa:bbb", VALID_USER_RELAX|VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".", VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("..", VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("root", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("lennart", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("LENNART", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("_kkk", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("kkk-", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("kk-k", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("eff.eff", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("eff.", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("-kkk", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("rööt", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".eff", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".1", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".65535", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".-1", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".-kkk", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".rööt", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("...", VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("some5", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("5some", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("Dāvis", VALID_USER_RELAX)); + } + + static void test_valid_user_group_name(void) { + log_info("/* %s */", __func__); + +- assert_se(!valid_user_group_name(NULL)); +- assert_se(!valid_user_group_name("")); +- assert_se(!valid_user_group_name("1")); +- assert_se(!valid_user_group_name("65535")); +- assert_se(!valid_user_group_name("-1")); +- assert_se(!valid_user_group_name("-kkk")); +- assert_se(!valid_user_group_name("rööt")); +- assert_se(!valid_user_group_name(".")); +- assert_se(!valid_user_group_name(".eff")); +- assert_se(!valid_user_group_name("foo\nbar")); +- assert_se(!valid_user_group_name("0123456789012345678901234567890123456789")); +- assert_se(!valid_user_group_name_or_id("aaa:bbb")); +- assert_se(!valid_user_group_name(".")); +- assert_se(!valid_user_group_name(".1")); +- assert_se(!valid_user_group_name(".65535")); +- assert_se(!valid_user_group_name(".-1")); +- assert_se(!valid_user_group_name(".-kkk")); +- assert_se(!valid_user_group_name(".rööt")); +- assert_se(!valid_user_group_name_or_id(".aaa:bbb")); +- +- assert_se(valid_user_group_name("root")); +- assert_se(valid_user_group_name("lennart")); +- assert_se(valid_user_group_name("LENNART")); +- assert_se(valid_user_group_name("_kkk")); +- assert_se(valid_user_group_name("kkk-")); +- assert_se(valid_user_group_name("kk-k")); +- assert_se(!valid_user_group_name("eff.eff")); +- assert_se(!valid_user_group_name("eff.")); +- +- assert_se(valid_user_group_name("some5")); +- assert_se(!valid_user_group_name("5some")); +- assert_se(valid_user_group_name("INNER5NUMBER")); ++ assert_se(!valid_user_group_name(NULL, 0)); ++ assert_se(!valid_user_group_name("", 0)); ++ assert_se(!valid_user_group_name("1", 0)); ++ assert_se(!valid_user_group_name("65535", 0)); ++ assert_se(!valid_user_group_name("-1", 0)); ++ assert_se(!valid_user_group_name("-kkk", 0)); ++ assert_se(!valid_user_group_name("rööt", 0)); ++ assert_se(!valid_user_group_name(".", 0)); ++ assert_se(!valid_user_group_name(".eff", 0)); ++ assert_se(!valid_user_group_name("foo\nbar", 0)); ++ assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", 0)); ++ assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".", 0)); ++ assert_se(!valid_user_group_name("..", 0)); ++ assert_se(!valid_user_group_name("...", 0)); ++ assert_se(!valid_user_group_name(".1", 0)); ++ assert_se(!valid_user_group_name(".65535", 0)); ++ assert_se(!valid_user_group_name(".-1", 0)); ++ assert_se(!valid_user_group_name(".-kkk", 0)); ++ assert_se(!valid_user_group_name(".rööt", 0)); ++ assert_se(!valid_user_group_name(".aaa:bbb", VALID_USER_ALLOW_NUMERIC)); ++ ++ assert_se(valid_user_group_name("root", 0)); ++ assert_se(valid_user_group_name("lennart", 0)); ++ assert_se(valid_user_group_name("LENNART", 0)); ++ assert_se(valid_user_group_name("_kkk", 0)); ++ assert_se(valid_user_group_name("kkk-", 0)); ++ assert_se(valid_user_group_name("kk-k", 0)); ++ assert_se(!valid_user_group_name("eff.eff", 0)); ++ assert_se(!valid_user_group_name("eff.", 0)); ++ ++ assert_se(valid_user_group_name("some5", 0)); ++ assert_se(!valid_user_group_name("5some", 0)); ++ assert_se(valid_user_group_name("INNER5NUMBER", 0)); ++ ++ assert_se(!valid_user_group_name("piff.paff@ad.domain.example", 0)); ++ assert_se(!valid_user_group_name("Dāvis", 0)); + } + +-static void test_valid_user_group_name_or_id_compat(void) { ++static void test_valid_user_group_name_or_numeric_relaxed(void) { + log_info("/* %s */", __func__); + +- assert_se(!valid_user_group_name_or_id_compat(NULL)); +- assert_se(!valid_user_group_name_or_id_compat("")); +- assert_se(valid_user_group_name_or_id_compat("0")); +- assert_se(valid_user_group_name_or_id_compat("1")); +- assert_se(valid_user_group_name_or_id_compat("65534")); +- assert_se(!valid_user_group_name_or_id_compat("65535")); +- assert_se(valid_user_group_name_or_id_compat("65536")); +- assert_se(!valid_user_group_name_or_id_compat("-1")); +- assert_se(!valid_user_group_name_or_id_compat("-kkk")); +- assert_se(!valid_user_group_name_or_id_compat("rööt")); +- assert_se(!valid_user_group_name_or_id_compat(".")); +- assert_se(!valid_user_group_name_or_id_compat(".eff")); +- assert_se(valid_user_group_name_or_id_compat("eff.eff")); +- assert_se(valid_user_group_name_or_id_compat("eff.")); +- assert_se(!valid_user_group_name_or_id_compat("foo\nbar")); +- assert_se(!valid_user_group_name_or_id_compat("0123456789012345678901234567890123456789")); +- assert_se(!valid_user_group_name_or_id_compat("aaa:bbb")); +- +- assert_se(valid_user_group_name_or_id_compat("root")); +- assert_se(valid_user_group_name_or_id_compat("lennart")); +- assert_se(valid_user_group_name_or_id_compat("LENNART")); +- assert_se(valid_user_group_name_or_id_compat("_kkk")); +- assert_se(valid_user_group_name_or_id_compat("kkk-")); +- assert_se(valid_user_group_name_or_id_compat("kk-k")); +- +- assert_se(valid_user_group_name_or_id_compat("some5")); +- assert_se(valid_user_group_name_or_id_compat("5some")); +- assert_se(valid_user_group_name_or_id_compat("INNER5NUMBER")); ++ assert_se(!valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(!valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ ++ assert_se(valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); ++ assert_se(valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC|VALID_USER_RELAX)); + } + +-static void test_valid_user_group_name_or_id(void) { ++static void test_valid_user_group_name_or_numeric(void) { + log_info("/* %s */", __func__); + +- assert_se(!valid_user_group_name_or_id(NULL)); +- assert_se(!valid_user_group_name_or_id("")); +- assert_se(valid_user_group_name_or_id("0")); +- assert_se(valid_user_group_name_or_id("1")); +- assert_se(valid_user_group_name_or_id("65534")); +- assert_se(!valid_user_group_name_or_id("65535")); +- assert_se(valid_user_group_name_or_id("65536")); +- assert_se(!valid_user_group_name_or_id("-1")); +- assert_se(!valid_user_group_name_or_id("-kkk")); +- assert_se(!valid_user_group_name_or_id("rööt")); +- assert_se(!valid_user_group_name_or_id(".")); +- assert_se(!valid_user_group_name_or_id(".eff")); +- assert_se(!valid_user_group_name_or_id("eff.eff")); +- assert_se(!valid_user_group_name_or_id("eff.")); +- assert_se(!valid_user_group_name_or_id("foo\nbar")); +- assert_se(!valid_user_group_name_or_id("0123456789012345678901234567890123456789")); +- assert_se(!valid_user_group_name_or_id("aaa:bbb")); +- +- assert_se(valid_user_group_name_or_id("root")); +- assert_se(valid_user_group_name_or_id("lennart")); +- assert_se(valid_user_group_name_or_id("LENNART")); +- assert_se(valid_user_group_name_or_id("_kkk")); +- assert_se(valid_user_group_name_or_id("kkk-")); +- assert_se(valid_user_group_name_or_id("kk-k")); +- +- assert_se(valid_user_group_name_or_id("some5")); +- assert_se(!valid_user_group_name_or_id("5some")); +- assert_se(valid_user_group_name_or_id("INNER5NUMBER")); ++ assert_se(!valid_user_group_name(NULL, VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("0", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("1", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("65534", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("65535", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("65536", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("-1", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("-kkk", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("rööt", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("..", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("...", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name(".eff", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("eff.eff", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("eff.", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("foo\nbar", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("0123456789012345678901234567890123456789", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("aaa:bbb", VALID_USER_ALLOW_NUMERIC)); ++ ++ assert_se(valid_user_group_name("root", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("lennart", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("LENNART", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("_kkk", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("kkk-", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("kk-k", VALID_USER_ALLOW_NUMERIC)); ++ ++ assert_se(valid_user_group_name("some5", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("5some", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(valid_user_group_name("INNER5NUMBER", VALID_USER_ALLOW_NUMERIC)); ++ ++ assert_se(!valid_user_group_name("piff.paff@ad.domain.example", VALID_USER_ALLOW_NUMERIC)); ++ assert_se(!valid_user_group_name("Dāvis", VALID_USER_ALLOW_NUMERIC)); + } + + static void test_valid_gecos(void) { +@@ -367,10 +386,10 @@ int main(int argc, char*argv[]) { + test_parse_uid(); + test_uid_ptr(); + +- test_valid_user_group_name_compat(); ++ test_valid_user_group_name_relaxed(); + test_valid_user_group_name(); +- test_valid_user_group_name_or_id_compat(); +- test_valid_user_group_name_or_id(); ++ test_valid_user_group_name_or_numeric_relaxed(); ++ test_valid_user_group_name_or_numeric(); + test_valid_gecos(); + test_valid_home(); + diff --git a/SOURCES/0475-man-mention-System-Administrator-s-Guide-in-systemct.patch b/SOURCES/0475-man-mention-System-Administrator-s-Guide-in-systemct.patch new file mode 100644 index 0000000..0fed0ab --- /dev/null +++ b/SOURCES/0475-man-mention-System-Administrator-s-Guide-in-systemct.patch @@ -0,0 +1,35 @@ +From 11a9ea82827d7b57dbce307b77ef8233a4cc028a Mon Sep 17 00:00:00 2001 +From: Lukas Nykryn +Date: Thu, 28 Aug 2014 15:12:10 +0200 +Subject: [PATCH] man: mention System Administrator's Guide in systemctl + manpage + +(cherry picked from commit d4582346f47064de24470b5f92e418966004925f) + +Resolves: #1623116 +--- + man/systemctl.xml | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index fa08ab6c0a..56f94d084c 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -2000,6 +2000,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + ++ ++ Examples ++ ++ For examples how to use systemctl in comparsion ++ with old service and chkconfig command please see: ++ ++ Managing System Services ++ ++ ++ ++ + + See Also + diff --git a/SOURCES/0476-udev-introduce-udev-net_id-naming-schemes.patch b/SOURCES/0476-udev-introduce-udev-net_id-naming-schemes.patch new file mode 100644 index 0000000..d218efb --- /dev/null +++ b/SOURCES/0476-udev-introduce-udev-net_id-naming-schemes.patch @@ -0,0 +1,257 @@ +From 08ac9f7f55c138678c6415139e7510a05a75b81d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Wed, 14 Oct 2020 16:57:44 +0200 +Subject: [PATCH] udev: introduce udev net_id "naming schemes" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +With this we can stabilize how naming works for network interfaces. A +user can request through a kernel cmdline option or an env var which +scheme to follow. The idea is that installers use this to set into stone +(a very soft stone though) the scheme used during installation so that +interface naming doesn't change afterwards anymore. + +Why use env vars and kernel cmdline options, and not a config file of +its own? + +Well, first of all there's no obvious existing one to use. But more +importantly: I have the feeling that this logic is kind of an incomplete +hack, and I simply don't want to do advertise this as a perfectly +working solution. So far we used env vars for the non-so-official +options and proper config files for the official stuff. Given how +incomplete this logic is (i.e. the big variable for naming remains the +kernel, which might expose sysfs attributes in newer versions that we +check for and didn't exist in older versions — and other problems like +this), I am simply not confident in giving this first-class exposure in +a primary configuration file. + +Fixes: #10448 + +(cherry-picked from commit f7e81fd96fdfe0ac6dcdb72de43f7cb4720e363a) + +Related: #1827462 + +[msekleta: note that we are introducing our own naming schemes based on +RHEL-8 minor versions. Also we are not backporting all naming scheme +features that appeared in the original commit. We are backporting only +features relevant for v239 while original commit also converted +changes introduced in v240 into naming scheme flags.] +--- + doc/ENVIRONMENT.md | 9 +++ + man/kernel-command-line.xml | 1 + + man/systemd-udevd.service.xml | 16 +++++ + src/udev/udev-builtin-net_id.c | 106 ++++++++++++++++++++++++++++++++- + 4 files changed, 130 insertions(+), 2 deletions(-) + +diff --git a/doc/ENVIRONMENT.md b/doc/ENVIRONMENT.md +index 39a36a52cc..1a4aa01ef4 100644 +--- a/doc/ENVIRONMENT.md ++++ b/doc/ENVIRONMENT.md +@@ -76,6 +76,15 @@ systemd-logind: + hibernation is available even if the swap devices do not provide enough room + for it. + ++* `$NET_NAMING_SCHEME=` – if set, takes a network naming scheme (i.e. one of ++ v238, v239, v240 …) as parameter. If specified udev's net_id builtin will ++ follow the specified naming scheme when determining stable network interface ++ names. This may be used to revert to naming schemes of older udev versions, ++ in order to provide more stable naming across updates. This environment ++ variable takes precedence over the kernel command line option ++ `net.naming-scheme=`, except if the value is prefixed with `:` in which case ++ the kernel command line option takes precedence, if it is specified as well. ++ + installed systemd tests: + + * `$SYSTEMD_TEST_DATA` — override the location of test data. This is useful if +diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml +index 4d8cb4e50e..b753d0592c 100644 +--- a/man/kernel-command-line.xml ++++ b/man/kernel-command-line.xml +@@ -246,6 +246,7 @@ + udev.event_timeout= + rd.udev.event_timeout= + net.ifnames= ++ net.naming-scheme= + + + Parameters understood by the device event managing +diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml +index 73c77ea690..6449103441 100644 +--- a/man/systemd-udevd.service.xml ++++ b/man/systemd-udevd.service.xml +@@ -170,6 +170,22 @@ + when possible. It is enabled by default; specifying 0 disables it. + + ++ ++ net.naming-scheme= ++ ++ Network interfaces are renamed to give them predictable names when possible (unless ++ net.ifnames=0 is specified, see above). The names are derived from various device metadata ++ fields. Newer versions of systemd-udevd.service take more of these fields into account, ++ improving (and thus possibly changing) the names used for the same devices. With this kernel command line ++ option it is possible to pick a specific version of this algorithm. It expects a naming scheme identifier as ++ argument. Currently the following identifiers are known: v238, v239, ++ v240 which each implement the naming scheme that was the default in the indicated systemd ++ version. Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: the ++ naming is generally derived from driver attributes exposed by the kernel. As the kernel is updated, ++ previously missing attributes systemd-udevd.service is checking might appear, which ++ affects older name derivation algorithms, too. ++ ++ + + +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index 147e04ab8c..148696183e 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -96,6 +96,7 @@ + #include "fileio.h" + #include "fs-util.h" + #include "parse-util.h" ++#include "proc-cmdline.h" + #include "stdio-util.h" + #include "string-util.h" + #include "udev.h" +@@ -103,6 +104,52 @@ + + #define ONBOARD_INDEX_MAX (16*1024-1) + ++/* So here's the deal: net_id is supposed to be an excercise in providing stable names for network devices. However, we ++ * also want to keep updating the naming scheme used in future versions of net_id. These two goals of course are ++ * contradictory: on one hand we want things to not change and on the other hand we want them to improve. Our way out ++ * of this dilemma is to introduce the "naming scheme" concept: each time we improve the naming logic we define a new ++ * flag for it. Then, we keep a list of schemes, each identified by a name associated with the flags it implements. Via ++ * a kernel command line and environment variable we then allow the user to pick the scheme they want us to follow: ++ * installers could "freeze" the used scheme at the moment of installation this way. ++ * ++ * Developers: each time you tweak the naming logic here, define a new flag below, and condition the tweak with ++ * it. Each time we do a release we'll then add a new scheme entry and include all newly defined flags. ++ * ++ * Note that this is only half a solution to the problem though: not only udev/net_id gets updated all the time, the ++ * kernel gets too. And thus a kernel that previously didn't expose some sysfs attribute we look for might eventually ++ * do, and thus affect our naming scheme too. Thus, enforcing a naming scheme will make interfacing more stable across ++ * OS versions, but not fully stabilize them. */ ++typedef enum NamingSchemeFlags { ++ /* First, the individual features */ ++ NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a40008b8299529c978ed8e11de8f6*/ ++ NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6eab35d1cb9fa73889892702c27be09 */ ++ ++ /* And now the masks that combine the features above */ ++ NAMING_V238 = 0, ++ NAMING_V239 = NAMING_V238|NAMING_SR_IOV_V|NAMING_NPAR_ARI, ++ NAMING_RHEL_8_0 = NAMING_V239, ++ NAMING_RHEL_8_1 = NAMING_V239, ++ NAMING_RHEL_8_2 = NAMING_V239, ++ NAMING_RHEL_8_3 = NAMING_V239, ++ ++ _NAMING_SCHEME_FLAGS_INVALID = -1, ++} NamingSchemeFlags; ++ ++typedef struct NamingScheme { ++ const char *name; ++ NamingSchemeFlags flags; ++} NamingScheme; ++ ++static const NamingScheme naming_schemes[] = { ++ { "v238", NAMING_V238 }, ++ { "v239", NAMING_V239 }, ++ { "rhel-8.0", NAMING_RHEL_8_0 }, ++ { "rhel-8.1", NAMING_RHEL_8_1 }, ++ { "rhel-8.2", NAMING_RHEL_8_2 }, ++ { "rhel-8.3", NAMING_RHEL_8_3 }, ++ /* … add more schemes here, as the logic to name devices is updated … */ ++}; ++ + enum netname_type{ + NET_UNDEF, + NET_PCI, +@@ -138,6 +185,56 @@ struct virtfn_info { + char suffix[IFNAMSIZ]; + }; + ++static const NamingScheme* naming_scheme(void) { ++ static const NamingScheme *cache = NULL; ++ _cleanup_free_ char *buffer = NULL; ++ const char *e, *k; ++ ++ if (cache) ++ return cache; ++ ++ /* Acquire setting from the kernel command line */ ++ (void) proc_cmdline_get_key("net.naming-scheme", 0, &buffer); ++ ++ /* Also acquire it from an env var */ ++ e = getenv("NET_NAMING_SCHEME"); ++ if (e) { ++ if (*e == ':') { ++ /* If prefixed with ':' the kernel cmdline takes precedence */ ++ k = buffer ?: e + 1; ++ } else ++ k = e; /* Otherwise the env var takes precedence */ ++ } else ++ k = buffer; ++ ++ if (k) { ++ size_t i; ++ ++ for (i = 0; i < ELEMENTSOF(naming_schemes); i++) ++ if (streq(naming_schemes[i].name, k)) { ++ cache = naming_schemes + i; ++ break; ++ } ++ ++ if (!cache) ++ log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k); ++ } ++ ++ if (cache) ++ log_info("Using interface naming scheme '%s'.", cache->name); ++ else { ++ /* RHEL-only: here we differ from the upstream and if no naming scheme was selected we default to naming from systemd-239 */ ++ cache = &naming_schemes[2]; ++ log_info("Using default interface naming scheme '%s'.", cache->name); ++ } ++ ++ return cache; ++} ++ ++static bool naming_scheme_has(NamingSchemeFlags flags) { ++ return FLAGS_SET(naming_scheme()->flags, flags); ++} ++ + /* skip intermediate virtio devices */ + static struct udev_device *skip_virtio(struct udev_device *dev) { + struct udev_device *parent = dev; +@@ -299,7 +396,9 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + + if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4) + return -ENOENT; +- if (is_pci_ari_enabled(names->pcidev)) ++ ++ if (naming_scheme_has(NAMING_NPAR_ARI) && ++ is_pci_ari_enabled(names->pcidev)) + /* ARI devices support up to 256 functions on a single device ("slot"), and interpret the + * traditional 5-bit slot and 3-bit function number as a single 8-bit function number, + * where the slot makes up the upper 5 bits. */ +@@ -494,7 +593,8 @@ static int names_pci(struct udev_device *dev, struct netnames *names) { + return -ENOENT; + } + +- if (get_virtfn_info(dev, names, &vf_info) >= 0) { ++ if (naming_scheme_has(NAMING_SR_IOV_V) && ++ get_virtfn_info(dev, names, &vf_info) >= 0) { + /* If this is an SR-IOV virtual device, get base name using physical device and add virtfn suffix. */ + vf_names.pcidev = vf_info.physfn_pcidev; + dev_pci_onboard(dev, &vf_names); +@@ -741,6 +841,8 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool + prefix = "ww"; + } + ++ udev_builtin_add_property(dev, test, "ID_NET_NAMING_SCHEME", naming_scheme()->name); ++ + err = names_mac(dev, &names); + if (err >= 0 && names.mac_valid) { + char str[IFNAMSIZ]; diff --git a/SOURCES/0477-meson-make-net.naming-scheme-default-configurable.patch b/SOURCES/0477-meson-make-net.naming-scheme-default-configurable.patch new file mode 100644 index 0000000..0c9f850 --- /dev/null +++ b/SOURCES/0477-meson-make-net.naming-scheme-default-configurable.patch @@ -0,0 +1,188 @@ +From 8c263758fe196624005f19bd6f46d63e3841c5be Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 11 Dec 2018 23:28:29 +0100 +Subject: [PATCH] meson: make net.naming-scheme= default configurable + +This is useful for distributions, where the stability of interface names should +be preseved after an upgrade of systemd. So when some specific release of the +distro is made available, systemd defaults to the latest & greatest naming +scheme, and subsequent updates set the same default. This default may still +be overriden through the kernel and env var options. + +A special value "latest" is also allowed. Without a specific name, it is harder +to verride from meson. In case of 'combo' options, meson reads the default +during the initial configuration, and "remembers" this choice. When systemd is +updated, old build/ directories could keep the old default, which would be +annoying. Hence, "latest" is introduced to make it explicit, yet follow the +upstream. This is actually useful for the user too, because it may be used +as an override, without having to actually specify a version. + +(cherry picked from commit 06da5c63dd697ea4087e76c6d809b60b5780b87c) + +Related: #1827462 + +[msekleta: note that our default is not latest but rhel-8.0] +--- + doc/ENVIRONMENT.md | 15 +++++++------- + man/systemd-udevd.service.xml | 24 ++++++++++++--------- + meson.build | 4 ++++ + meson_options.txt | 3 +++ + src/udev/udev-builtin-net_id.c | 38 ++++++++++++++++++++-------------- + 5 files changed, 51 insertions(+), 33 deletions(-) + +diff --git a/doc/ENVIRONMENT.md b/doc/ENVIRONMENT.md +index 1a4aa01ef4..0e763b6302 100644 +--- a/doc/ENVIRONMENT.md ++++ b/doc/ENVIRONMENT.md +@@ -77,13 +77,14 @@ systemd-logind: + for it. + + * `$NET_NAMING_SCHEME=` – if set, takes a network naming scheme (i.e. one of +- v238, v239, v240 …) as parameter. If specified udev's net_id builtin will +- follow the specified naming scheme when determining stable network interface +- names. This may be used to revert to naming schemes of older udev versions, +- in order to provide more stable naming across updates. This environment +- variable takes precedence over the kernel command line option +- `net.naming-scheme=`, except if the value is prefixed with `:` in which case +- the kernel command line option takes precedence, if it is specified as well. ++ "rhel-8.0", "rhel-8.1", "rhel-8.2"…, or the special value "latest") as ++ parameter. If specified udev's net_id builtin will follow the specified ++ naming scheme when determining stable network interface names. This may be ++ used to revert to naming schemes of older udev versions, in order to provide ++ more stable naming across updates. This environment variable takes precedence ++ over the kernel command line option `net.naming-scheme=`, except if the value ++ is prefixed with `:` in which case the kernel command line option takes ++ precedence, if it is specified as well. + + installed systemd tests: + +diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml +index 6449103441..b738591c93 100644 +--- a/man/systemd-udevd.service.xml ++++ b/man/systemd-udevd.service.xml +@@ -174,16 +174,20 @@ + net.naming-scheme= + + Network interfaces are renamed to give them predictable names when possible (unless +- net.ifnames=0 is specified, see above). The names are derived from various device metadata +- fields. Newer versions of systemd-udevd.service take more of these fields into account, +- improving (and thus possibly changing) the names used for the same devices. With this kernel command line +- option it is possible to pick a specific version of this algorithm. It expects a naming scheme identifier as +- argument. Currently the following identifiers are known: v238, v239, +- v240 which each implement the naming scheme that was the default in the indicated systemd +- version. Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: the +- naming is generally derived from driver attributes exposed by the kernel. As the kernel is updated, +- previously missing attributes systemd-udevd.service is checking might appear, which +- affects older name derivation algorithms, too. ++ net.ifnames=0 is specified, see above). The names are derived from various ++ device metadata fields. Newer versions of systemd-udevd.service take more of ++ these fields into account, improving (and thus possibly changing) the names used for the same ++ devices. With this kernel command line option it is possible to pick a specific version of this ++ algorithm. It expects a naming scheme identifier as argument. Currently the following identifiers ++ are known: rhel-8.0, rhel-8.1, rhel-8.2, ++ rhel-8.3 which each implement the naming scheme that was the default in the ++ indicated Red Hat Enterprise Linux minor version. In addition, latest may be ++ used to designate the latest scheme known (to this particular version of ++ systemd-udevd.service). ++ Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: ++ the naming is generally derived from driver attributes exposed by the kernel. As the kernel is ++ updated, previously missing attributes systemd-udevd.service is checking might ++ appear, which affects older name derivation algorithms, too. + + + +diff --git a/meson.build b/meson.build +index 65c1d0785e..57de947367 100644 +--- a/meson.build ++++ b/meson.build +@@ -639,6 +639,9 @@ else + conf.set('DEFAULT_HIERARCHY', 'CGROUP_UNIFIED_ALL') + endif + ++default_net_naming_scheme = get_option('default-net-naming-scheme') ++conf.set_quoted('DEFAULT_NET_NAMING_SCHEME', default_net_naming_scheme) ++ + time_epoch = get_option('time-epoch') + if time_epoch == '' + NEWS = files('NEWS') +@@ -2925,6 +2928,7 @@ status = [ + 'default DNSSEC mode: @0@'.format(default_dnssec), + 'default DNS-over-TLS mode: @0@'.format(default_dns_over_tls), + 'default cgroup hierarchy: @0@'.format(default_hierarchy), ++ 'default net.naming-scheme setting: @0@'.format(default_net_naming_scheme), + 'default KillUserProcesses setting: @0@'.format(kill_user_processes)] + + alt_dns_servers = '\n '.join(dns_servers.split(' ')) +diff --git a/meson_options.txt b/meson_options.txt +index 0996891177..213079ac15 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -158,6 +158,9 @@ option('default-hierarchy', type : 'combo', + description : 'default cgroup hierarchy') + option('time-epoch', type : 'string', + description : 'time epoch for time clients') ++option('default-net-naming-scheme', type : 'combo', ++ choices : ['rhel-8.0', 'rhel-8.1', 'rhel-8.2', 'rhel-8.3', 'latest'], ++ description : 'default net.naming-scheme= value') + option('system-uid-max', type : 'string', + description : 'maximum system UID') + option('system-gid-max', type : 'string', +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index 148696183e..d85dc2848b 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -185,6 +185,19 @@ struct virtfn_info { + char suffix[IFNAMSIZ]; + }; + ++static const NamingScheme* naming_scheme_from_name(const char *name) { ++ size_t i; ++ ++ if (streq(name, "latest")) ++ return naming_schemes + ELEMENTSOF(naming_schemes) - 1; ++ ++ for (i = 0; i < ELEMENTSOF(naming_schemes); i++) ++ if (streq(naming_schemes[i].name, name)) ++ return naming_schemes + i; ++ ++ return NULL; ++} ++ + static const NamingScheme* naming_scheme(void) { + static const NamingScheme *cache = NULL; + _cleanup_free_ char *buffer = NULL; +@@ -208,25 +221,18 @@ static const NamingScheme* naming_scheme(void) { + k = buffer; + + if (k) { +- size_t i; +- +- for (i = 0; i < ELEMENTSOF(naming_schemes); i++) +- if (streq(naming_schemes[i].name, k)) { +- cache = naming_schemes + i; +- break; +- } ++ cache = naming_scheme_from_name(k); ++ if (cache) { ++ log_info("Using interface naming scheme '%s'.", cache->name); ++ return cache; ++ } + +- if (!cache) +- log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k); ++ log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k); + } + +- if (cache) +- log_info("Using interface naming scheme '%s'.", cache->name); +- else { +- /* RHEL-only: here we differ from the upstream and if no naming scheme was selected we default to naming from systemd-239 */ +- cache = &naming_schemes[2]; +- log_info("Using default interface naming scheme '%s'.", cache->name); +- } ++ cache = naming_scheme_from_name(DEFAULT_NET_NAMING_SCHEME); ++ assert(cache); ++ log_info("Using default interface naming scheme '%s'.", cache->name); + + return cache; + } diff --git a/SOURCES/0478-man-describe-naming-schemes-in-a-new-man-page.patch b/SOURCES/0478-man-describe-naming-schemes-in-a-new-man-page.patch new file mode 100644 index 0000000..be9f964 --- /dev/null +++ b/SOURCES/0478-man-describe-naming-schemes-in-a-new-man-page.patch @@ -0,0 +1,522 @@ +From af528dcffaab1efea760395cc6676fe4b01e89b5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 9 May 2019 12:34:30 +0200 +Subject: [PATCH] man: describe naming schemes in a new man page + +I decided to make this a separate man page because it is freakin' long. +This content could equally well go in systemd-udevd.service(8), systemd.link(5), +or a new man page for the net_id builtin. + +v2: +- rename to systemd.net-naming-scheme +- add udevadm test-builtin net_id example + +(cherry picked from commit 0b1e5b6ed8c6b9a2bc53709eb75e381d360f05bf) + +Related: #1827462 + +[msekleta: I've removed parts that describe features which are not +available in RHEL-8] +--- + man/rules/meson.build | 1 + + man/systemd-udevd.service.xml | 19 +- + man/systemd.link.xml | 10 +- + man/systemd.net-naming-scheme.xml | 385 ++++++++++++++++++++++++++++++ + src/udev/udev-builtin-net_id.c | 1 + + 5 files changed, 402 insertions(+), 14 deletions(-) + create mode 100644 man/systemd.net-naming-scheme.xml + +diff --git a/man/rules/meson.build b/man/rules/meson.build +index 7ae94ea265..e6c0a99bbd 100644 +--- a/man/rules/meson.build ++++ b/man/rules/meson.build +@@ -714,6 +714,7 @@ manpages = [ + ['systemd.kill', '5', [], ''], + ['systemd.link', '5', [], ''], + ['systemd.mount', '5', [], ''], ++ ['systemd.net-naming-scheme', '7', [], ''], + ['systemd.netdev', '5', [], 'ENABLE_NETWORKD'], + ['systemd.network', '5', [], 'ENABLE_NETWORKD'], + ['systemd.nspawn', '5', [], ''], +diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml +index b738591c93..f4cdb2f1e7 100644 +--- a/man/systemd-udevd.service.xml ++++ b/man/systemd-udevd.service.xml +@@ -174,15 +174,11 @@ + net.naming-scheme= + + Network interfaces are renamed to give them predictable names when possible (unless +- net.ifnames=0 is specified, see above). The names are derived from various +- device metadata fields. Newer versions of systemd-udevd.service take more of +- these fields into account, improving (and thus possibly changing) the names used for the same +- devices. With this kernel command line option it is possible to pick a specific version of this +- algorithm. It expects a naming scheme identifier as argument. Currently the following identifiers +- are known: rhel-8.0, rhel-8.1, rhel-8.2, +- rhel-8.3 which each implement the naming scheme that was the default in the +- indicated Red Hat Enterprise Linux minor version. In addition, latest may be +- used to designate the latest scheme known (to this particular version of ++ net.ifnames=0 is specified, see above). With this kernel command line option it ++ is possible to pick a specific version of this algorithm and override the default chosen at ++ compilation time. Expects one of the naming scheme identifiers listed in ++ systemd.net-naming-scheme7, ++ or latest to select the latest scheme known (to this particular version of + systemd-udevd.service). + Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: + the naming is generally derived from driver attributes exposed by the kernel. As the kernel is +@@ -191,9 +187,8 @@ + + + +- +- ++ ++ + + + See Also +diff --git a/man/systemd.link.xml b/man/systemd.link.xml +index 6708753e82..32657308d0 100644 +--- a/man/systemd.link.xml ++++ b/man/systemd.link.xml +@@ -286,6 +286,7 @@ + The name is set based on information given by + the firmware for on-board devices, as exported by the + udev property ID_NET_NAME_ONBOARD. ++ See systemd.net-naming-scheme7. + + + +@@ -295,6 +296,7 @@ + The name is set based on information given by + the firmware for hot-plug devices, as exported by the + udev property ID_NET_NAME_SLOT. ++ See systemd.net-naming-scheme7. + + + +@@ -303,7 +305,9 @@ + + The name is set based on the device's physical + location, as exported by the udev property +- ID_NET_NAME_PATH. ++ ID_NET_NAME_PATH. ++ See systemd.net-naming-scheme7. ++ + + + +@@ -311,7 +315,9 @@ + + The name is set based on the device's persistent + MAC address, as exported by the udev property +- ID_NET_NAME_MAC. ++ ID_NET_NAME_MAC. ++ See systemd.net-naming-scheme7. ++ + + + +diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml +new file mode 100644 +index 0000000000..a12cc3c460 +--- /dev/null ++++ b/man/systemd.net-naming-scheme.xml +@@ -0,0 +1,385 @@ ++ ++ ++ ++ ++ ++ ++ systemd.net-naming-scheme ++ systemd ++ ++ ++ ++ systemd.net-naming-scheme ++ 7 ++ ++ ++ ++ systemd.net-naming-scheme ++ Network device naming schemes ++ ++ ++ ++ Description ++ ++ Network interfaces may be renamed to give them predictable names when there's enough information to ++ generate appropriate names and the use of certain types of names is configured. This page describes the ++ first part, i.e. what possible names may be generated. Those names are generated by the ++ systemd-udevd.service8 ++ builtin net_id and exported as udev properties ++ (ID_NET_NAME_ONBOARD=, ID_NET_LABEL_ONBOARD=, ++ ID_NET_NAME_PATH=, ID_NET_NAME_SLOT=). ++ ++ Names are derived from various device metadata attributes. Newer versions of udev take more of ++ these attributes into account, improving (and thus possibly changing) the names used for the same ++ devices. Differents version of the naming rules are called "naming schemes". The default naming scheme is ++ chosen at compilation time. Usually this will be the latest implemented version, but it is also possible ++ to set one of the older versions to preserve compatibility. This may be useful for example for ++ distributions, which may introduce new versions of systemd in stable releases without changing the naming ++ scheme. The naming scheme may also be overriden using the net.naming-scheme= kernel ++ command line switch, see ++ systemd-udevd.service8. ++ Available naming schemes are described below. ++ ++ After the udev proprties have been generated, appropriate udev rules may be used to actually rename ++ devices based on those properties. See the description of NamePolicy= in ++ systemd.link5. ++ ++ ++ ++ ++ Naming ++ ++ All names start with a two-character prefix that signifies the interface type. ++ ++ ++ Two character prefixes based on the type of interface ++ ++ ++ ++ ++ Prefix ++ Description ++ ++ ++ ++ ++ en ++ Ethernet ++ ++ ++ sl ++ serial line IP (slip) ++ ++ ++ wl ++ Wireless local area network (WLAN) ++ ++ ++ ww ++ Wireless wide area network (WWAN) ++ ++ ++ ++
++ ++ The udev net_id builtin exports the following udev device properties: ++ ++ ++ ++ ID_NET_NAME_ONBOARD=prefixonumber ++ ++ This name is set based on the ordering information given by the firmware for ++ on-board devices. The name consists of the prefix, letter o, and a number ++ specified by the firmware. This is only available for PCI devices. ++ ++ ++ ++ ++ ID_NET_LABEL_ONBOARD=prefix label ++ ++ This property is set based on label given by the firmware for on-board devices. The ++ name consists of the prefix concatenated with the label. This is only available for PCI devices. ++ ++ ++ ++ ++ ++ ID_NET_NAME_MAC=prefixxAABBCCDDEEFF ++ ++ This name consists of the prefix, letter x, and 12 hexadecimal ++ digits of the MAC address. It is available if the device has a fixed MAC address. Because this name ++ is based on an attribute of the card itself, it remains "stable" when the device is moved (even ++ between machines), but will change when the hardware is replaced. ++ ++ ++ ++ ++ ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port] ++ ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]bnumber ++ ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]uport…[cconfig][iinterface] ++ ID_NET_NAME_SLOT=prefix[Pdomain]sslot[ffunction][nport_name|ddev_port]vslot ++ ++ This property describes the slot position. Different schemes are used depending on ++ the bus type, as described in the table below. In all cases, PCI slot information must be known. In ++ case of USB, BCMA, and SR-VIO devices, the full name consists of the prefix, PCI slot identifier, ++ and USB or BCMA or SR-VIO slot identifier. The first two parts are denoted as "…" in the table ++ below. ++ ++ ++ Slot naming schemes ++ ++ ++ ++ ++ Format ++ Description ++ ++ ++ ++ ++ ++ prefix [Pdomainsslot [ffunction] [nport_name | ddev_port] ++ PCI slot number ++ ++ ++ ++ … bnumber ++ Broadcom bus (BCMA) core number ++ ++ ++ ++ … uport… [cconfig] [iinterface] ++ USB port number chain ++ ++ ++ ++ … vslot ++ SR-VIO slot number ++ ++ ++ ++
++ ++ The PCI domain is only prepended when it is not 0. All multi-function PCI devices will carry ++ the ffunction number in the device name, including ++ the function 0 device. For non-multi-function devices, the number is suppressed if 0. The port name ++ port_name is used, or the port number ++ ddev_port if the name is not known. ++ ++ For BCMA devices, the core number is suppressed when 0. ++ ++ For USB devices the full chain of port numbers of hubs is composed. If the name gets longer ++ than the maximum number of 15 characters, the name is not exported. The usual USB configuration ++ number 1 and interface number 0 values are suppressed. ++
++ ++ SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of ++ v and the virtual device number, with any leading zeros removed. The bus ++ number is ignored. This device type is found in IBM PowerVMs. ++
++ ++ ++ ID_NET_NAME_PATH=prefixcbus_id ++ ID_NET_NAME_PATH=prefixavendormodeliinstance ++ ID_NET_NAME_PATH=prefixiaddressnport_name ++ ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port] ++ ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port]bnumber ++ ID_NET_NAME_PATH=prefix[Pdomain]pbussslot[ffunction][nphys_port_name|ddev_port]uport…[cconfig][iinterface] ++ ++ This property describes the device installation location. Different schemes are ++ used depending on the bus type, as described in the table below. For BCMA and USB devices, PCI path ++ information must known, and the full name consists of the prefix, PCI slot identifier, and USB or ++ BCMA location. The first two parts are denoted as "…" in the table below. ++ ++ ++ Path naming schemes ++ ++ ++ ++ ++ Format ++ Description ++ ++ ++ ++ ++ ++ prefix cbus_id ++ CCW or grouped CCW device identifier ++ ++ ++ ++ prefix avendor model iinstance ++ ACPI path names for ARM64 platform devices ++ ++ ++ ++ prefix [Pdomainpbus sslot [ffunction] [nphys_port_name | ddev_port] ++ PCI geographical location ++ ++ ++ ++ … bnumber ++ Broadcom bus (BCMA) core number ++ ++ ++ ++ … uport… [cconfig] [iinterface] ++ USB port number chain ++ ++ ++ ++ ++
++ ++ CCW and grouped CCW devices are found in IBM System Z mainframes. Any leading zeros and ++ dots are suppressed. ++ ++ For PCI, BCMA, and USB devices, the same rules as described above for slot naming are ++ used. ++
++
++
++
++ ++ ++ History ++ ++ The following "naming schemes" have been defined: ++ ++ ++ ++ rhel-8.0 ++ ++ Naming was changed for virtual network interfaces created with SR-IOV and NPAR and ++ for devices where the PCI network controller device does not have a slot number associated. ++ ++ SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of ++ vport, where port is the ++ virtual device number. Previously those virtual devices were named as if completely independent. ++ ++ ++ The ninth and later NPAR virtual devices are named following the scheme used for the first ++ eight NPAR partitions. Previously those devices were not renamed and the kernel default ++ ("ethN") was used. ++ ++ Names are also generated for PCI devices where the PCI network controller device does not ++ have an associated slot number itself, but one of its parents does. Previously those devices were ++ not renamed and the kernel default was used. ++ ++ ++ ++ ++ rhel-8.1 ++ ++ Same as naming scheme rhel-8.0. ++ ++ ++ ++ rhel-8.2 ++ ++ Same as naming scheme rhel-8.0. ++ ++ ++ ++ rhel-8.3 ++ ++ Same as naming scheme rhel-8.0. ++ ++ ++ Note that latest may be used to denote the latest scheme known (to this ++ particular version of systemd. ++ ++ ++ ++ ++ Examples ++ ++ ++ Using <command>udevadm test-builtin</command> to display device properties ++ ++ $ udevadm test-builtin net_id /sys/class/net/enp0s31f6 ++... ++Using default interface naming scheme 'rhel-8.3'. ++ID_NET_NAMING_SCHEME=rhel-8.3 ++ID_NET_NAME_MAC=enx54ee75cb1dc0 ++ID_OUI_FROM_DATABASE=Wistron InfoComm(Kunshan)Co.,Ltd. ++ID_NET_NAME_PATH=enp0s31f6 ++... ++ ++ ++ ++ PCI Ethernet card with firmware index "1" ++ ++ ID_NET_NAME_ONBOARD=eno1 ++ID_NET_NAME_ONBOARD_LABEL=enEthernet Port 1 ++ ++ ++ ++ ++ ++ PCI Ethernet card in hotplug slot with firmware index number ++ ++ # /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1 ++ID_NET_NAME_MAC=enx000000000466 ++ID_NET_NAME_PATH=enp5s0 ++ID_NET_NAME_SLOT=ens1 ++ ++ ++ ++ PCI Ethernet multi-function card with 2 ports ++ ++ # /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0 ++ID_NET_NAME_MAC=enx78e7d1ea46da ++ID_NET_NAME_PATH=enp2s0f0 ++ ++# /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1 ++ID_NET_NAME_MAC=enx78e7d1ea46dc ++ID_NET_NAME_PATH=enp2s0f1 ++ ++ ++ ++ PCI WLAN card ++ ++ # /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0 ++ID_NET_NAME_MAC=wlx0024d7e31130 ++ID_NET_NAME_PATH=wlp3s0 ++ ++ ++ ++ USB built-in 3G modem ++ ++ # /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6 ++ID_NET_NAME_MAC=wwx028037ec0200 ++ID_NET_NAME_PATH=wwp0s29u1u4i6 ++ ++ ++ ++ USB Android phone ++ ++ # /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2 ++ID_NET_NAME_MAC=enxd626b3450fb5 ++ID_NET_NAME_PATH=enp0s29u1u2 ++ ++ ++ ++ s390 grouped CCW interface ++ ++ # /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0 ++ID_NET_NAME_MAC=enx026d3c00000a ++ID_NET_NAME_PATH=encf5f0 ++ ++ ++ ++ ++ See Also ++ ++ udev7, ++ udevadm8, ++ the ++ original page describing stable interface names ++ ++ ++ ++
+diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index d85dc2848b..aa553d5ade 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -78,6 +78,7 @@ + * /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0 + * ID_NET_NAME_MAC=enx026d3c00000a + * ID_NET_NAME_PATH=encf5f0 ++ * When the code here is changed, man/systemd.net-naming-scheme.xml must be updated too. + */ + + #include diff --git a/SOURCES/0479-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch b/SOURCES/0479-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch new file mode 100644 index 0000000..ea6dfa2 --- /dev/null +++ b/SOURCES/0479-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch @@ -0,0 +1,51 @@ +From 462420bc7ea22a05bfc2d021d395aade2b8ee7dc Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Mon, 19 Oct 2020 10:56:11 +0200 +Subject: [PATCH] udev/net_id: parse _SUN ACPI index as a signed integer + +Negative value means there is no match between a PCI device and any of +the slots. In the following commit we will extend this and value of 0 +will indicate that there is a match between some slot and PCI device, +but that device is a PCI bridge. + +(cherry picked from commit 3e545ae5abcf258791eacbee60c829c100a33274) + +Related: #1827462 +--- + src/udev/udev-builtin-net_id.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index aa553d5ade..ede24dee41 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -391,7 +391,8 @@ static bool is_pci_ari_enabled(struct udev_device *dev) { + + static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + struct udev *udev = udev_device_get_udev(names->pcidev); +- unsigned domain, bus, slot, func, dev_port = 0, hotplug_slot = 0; ++ unsigned domain, bus, slot, func, dev_port = 0; ++ int hotplug_slot = -1; + size_t l; + char *s; + const char *attr, *port_name; +@@ -449,15 +450,15 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + hotplug_slot_dev = names->pcidev; + while (hotplug_slot_dev) { + FOREACH_DIRENT_ALL(dent, dir, break) { +- unsigned i; +- int r; ++ int i, r; + char str[PATH_MAX]; + _cleanup_free_ char *address = NULL; + + if (dent->d_name[0] == '.') + continue; +- r = safe_atou_full(dent->d_name, 10, &i); +- if (i < 1 || r < 0) ++ ++ r = safe_atoi(dent->d_name, &i); ++ if (r < 0 || i <= 0) + continue; + + if (snprintf_ok(str, sizeof str, "%s/%s/address", slots, dent->d_name) && diff --git a/SOURCES/0480-udev-net_id-don-t-generate-slot-based-names-if-multi.patch b/SOURCES/0480-udev-net_id-don-t-generate-slot-based-names-if-multi.patch new file mode 100644 index 0000000..2bd2478 --- /dev/null +++ b/SOURCES/0480-udev-net_id-don-t-generate-slot-based-names-if-multi.patch @@ -0,0 +1,124 @@ +From bb6114af097da0cd9c5081e42db718559130687f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Mon, 19 Oct 2020 11:10:31 +0200 +Subject: [PATCH] udev/net_id: don't generate slot based names if multiple + devices might claim the same slot + +(cherry picked from commit 2c8ec0095e6fd2e72879d4915ff8a9e5c0664d0b) + +Resolves: #1827462 +--- + man/systemd.net-naming-scheme.xml | 15 ++++++++++- + src/udev/udev-builtin-net_id.c | 41 ++++++++++++++++++++++++++----- + 2 files changed, 49 insertions(+), 7 deletions(-) + +diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml +index a12cc3c460..10e71dcb15 100644 +--- a/man/systemd.net-naming-scheme.xml ++++ b/man/systemd.net-naming-scheme.xml +@@ -176,7 +176,10 @@ + + SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of + v and the virtual device number, with any leading zeros removed. The bus +- number is ignored. This device type is found in IBM PowerVMs. ++ number is ignored. ++ ++ In some configurations a parent PCI bridge of a given network controller may be associated ++ with a slot. In such case we don't generate this device property to avoid possible naming conflicts. + + + +@@ -288,6 +291,16 @@ + Same as naming scheme rhel-8.0. + + ++ ++ rhel-8.4 ++ ++ If the PCI slot is assocated with PCI bridge and that has multiple child network ++ controllers then all of them might derive the same value of ID_NET_NAME_SLOT ++ property. That could cause naming conflict if the property is selected as a device name. Now, we detect the ++ situation, slot - bridge relation, and we don't produce the ID_NET_NAME_SLOT property to ++ avoid possible naming conflict. ++ ++ + Note that latest may be used to denote the latest scheme known (to this + particular version of systemd. + +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index ede24dee41..d8c56b62bb 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -124,6 +124,7 @@ typedef enum NamingSchemeFlags { + /* First, the individual features */ + NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a40008b8299529c978ed8e11de8f6*/ + NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6eab35d1cb9fa73889892702c27be09 */ ++ NAMING_BRIDGE_NO_SLOT = 1 << 9, /* Don't use PCI hotplug slot information if the corresponding device is a PCI bridge */ + + /* And now the masks that combine the features above */ + NAMING_V238 = 0, +@@ -132,6 +133,7 @@ typedef enum NamingSchemeFlags { + NAMING_RHEL_8_1 = NAMING_V239, + NAMING_RHEL_8_2 = NAMING_V239, + NAMING_RHEL_8_3 = NAMING_V239, ++ NAMING_RHEL_8_4 = NAMING_V239|NAMING_BRIDGE_NO_SLOT, + + _NAMING_SCHEME_FLAGS_INVALID = -1, + } NamingSchemeFlags; +@@ -389,6 +391,26 @@ static bool is_pci_ari_enabled(struct udev_device *dev) { + return streq_ptr(udev_device_get_sysattr_value(dev, "ari_enabled"), "1"); + } + ++static bool is_pci_bridge(struct udev_device *dev) { ++ const char *v, *p; ++ ++ v = udev_device_get_sysattr_value(dev, "modalias"); ++ if (!v) ++ return false; ++ ++ if (!startswith(v, "pci:")) ++ return false; ++ ++ p = strrchr(v, 's'); ++ if (!p) ++ return false; ++ if (p[1] != 'c') ++ return false; ++ ++ /* PCI device subclass 04 corresponds to PCI bridge */ ++ return strneq(p + 2, "04", 2); ++} ++ + static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + struct udev *udev = udev_device_get_udev(names->pcidev); + unsigned domain, bus, slot, func, dev_port = 0; +@@ -461,16 +483,23 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { + if (r < 0 || i <= 0) + continue; + ++ /* match slot address with device by stripping the function */ + if (snprintf_ok(str, sizeof str, "%s/%s/address", slots, dent->d_name) && +- read_one_line_file(str, &address) >= 0) +- /* match slot address with device by stripping the function */ +- if (startswith(udev_device_get_sysname(hotplug_slot_dev), address)) +- hotplug_slot = i; ++ read_one_line_file(str, &address) >= 0 && ++ startswith(udev_device_get_sysname(hotplug_slot_dev), address)) { ++ hotplug_slot = i; ++ ++ /* We found the match between PCI device and slot. However, we won't use the ++ * slot index if the device is a PCI bridge, because it can have other child ++ * devices that will try to claim the same index and that would create name ++ * collision. */ ++ if (naming_scheme_has(NAMING_BRIDGE_NO_SLOT) && is_pci_bridge(hotplug_slot_dev)) ++ hotplug_slot = 0; + +- if (hotplug_slot > 0) + break; ++ } + } +- if (hotplug_slot > 0) ++ if (hotplug_slot >= 0) + break; + rewinddir(dir); + hotplug_slot_dev = udev_device_get_parent_with_subsystem_devtype(hotplug_slot_dev, "pci", NULL); diff --git a/SOURCES/0481-fix-typo-in-ProtectSystem-option.patch b/SOURCES/0481-fix-typo-in-ProtectSystem-option.patch new file mode 100644 index 0000000..06f5f4c --- /dev/null +++ b/SOURCES/0481-fix-typo-in-ProtectSystem-option.patch @@ -0,0 +1,25 @@ +From 573229efeb2c5ade25794deee8cfe2f967414ef7 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Fri, 6 Nov 2020 10:13:19 +0100 +Subject: [PATCH] fix typo in ProtectSystem= option + +This was introduced by commit d9ae3222cfbd5d2a48e6dbade6617085cc76f1c1 . + +Resolves: #1871139 +--- + units/systemd-resolved.service.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in +index aad1a53a5f..f10f1d1690 100644 +--- a/units/systemd-resolved.service.in ++++ b/units/systemd-resolved.service.in +@@ -30,7 +30,7 @@ CapabilityBoundingSet=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE + AmbientCapabilities=CAP_SETPCAP CAP_NET_RAW CAP_NET_BIND_SERVICE + PrivateTmp=yes + PrivateDevices=yes +-ProtectSystems=strict ++ProtectSystem=strict + ProtectHome=yes + ProtectControlGroups=yes + ProtectKernelTunables=yes diff --git a/SOURCES/0482-remove-references-of-non-existent-man-pages.patch b/SOURCES/0482-remove-references-of-non-existent-man-pages.patch new file mode 100644 index 0000000..31ae5e0 --- /dev/null +++ b/SOURCES/0482-remove-references-of-non-existent-man-pages.patch @@ -0,0 +1,34 @@ +From 5e74048399c4610da27b5f7fbbb53784030aeb70 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Mon, 9 Nov 2020 09:27:02 +0100 +Subject: [PATCH] remove references of non-existent man pages + +This is a follow-up to commit 8ad89170001c9aba8849630ddb5da81d9e24a1bc, +which introduced the man page change. + +Resolves: #1876807 +--- + man/systemd.special.xml | 10 ---------- + 1 file changed, 10 deletions(-) + +diff --git a/man/systemd.special.xml b/man/systemd.special.xml +index c9d4345016..fe6324a4a0 100644 +--- a/man/systemd.special.xml ++++ b/man/systemd.special.xml +@@ -657,16 +657,6 @@ + target unit and pull in the target from it, also with Requires=. Note that by default this + target unit is not part of the initial boot transaction, but is supposed to be pulled in only if required by + units that want to run only on successful boots. +- +- See +- systemd-boot-check-no-failures.service8 +- for a service that implements a generic system health check and orders itself before +- boot-complete.target. +- +- See +- systemd-bless-boot.service8 +- for a service that propagates boot success information to the boot loader, and orders itself after +- boot-complete.target. + + + diff --git a/SOURCES/0483-log-Prefer-logging-to-CLI-unless-JOURNAL_STREAM-is-s.patch b/SOURCES/0483-log-Prefer-logging-to-CLI-unless-JOURNAL_STREAM-is-s.patch new file mode 100644 index 0000000..72141ba --- /dev/null +++ b/SOURCES/0483-log-Prefer-logging-to-CLI-unless-JOURNAL_STREAM-is-s.patch @@ -0,0 +1,91 @@ +From b14c82dd9f9fcc42810614cf02efe8651897d36f Mon Sep 17 00:00:00 2001 +From: Daan De Meyer +Date: Wed, 10 Jun 2020 20:19:41 +0200 +Subject: [PATCH] log: Prefer logging to CLI unless JOURNAL_STREAM is set + +(cherry picked from commit bc694c06e60505efeb09e5278a7b22cdfa23975e) + +Resolves: #1865840 +--- + src/basic/log.c | 32 +++++++++++++++++++++++++++++--- + test/TEST-21-SYSUSERS/test.sh | 3 +-- + 2 files changed, 30 insertions(+), 5 deletions(-) + +diff --git a/src/basic/log.c b/src/basic/log.c +index 48c094b548..9387e56a57 100644 +--- a/src/basic/log.c ++++ b/src/basic/log.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -19,6 +20,7 @@ + #include "sd-messages.h" + + #include "alloc-util.h" ++#include "extract-word.h" + #include "fd-util.h" + #include "format-util.h" + #include "io-util.h" +@@ -220,6 +222,32 @@ fail: + return r; + } + ++static bool stderr_is_journal(void) { ++ _cleanup_free_ char *w = NULL; ++ const char *e; ++ uint64_t dev, ino; ++ struct stat st; ++ ++ e = getenv("JOURNAL_STREAM"); ++ if (!e) ++ return false; ++ ++ if (extract_first_word(&e, &w, ":", EXTRACT_DONT_COALESCE_SEPARATORS) <= 0) ++ return false; ++ if (!e) ++ return false; ++ ++ if (safe_atou64(w, &dev) < 0) ++ return false; ++ if (safe_atou64(e, &ino) < 0) ++ return false; ++ ++ if (fstat(STDERR_FILENO, &st) < 0) ++ return false; ++ ++ return st.st_dev == dev && st.st_ino == ino; ++} ++ + int log_open(void) { + int r; + +@@ -239,9 +267,7 @@ int log_open(void) { + return 0; + } + +- if (log_target != LOG_TARGET_AUTO || +- getpid_cached() == 1 || +- isatty(STDERR_FILENO) <= 0) { ++ if (log_target != LOG_TARGET_AUTO || getpid_cached() == 1 || stderr_is_journal()) { + + if (!prohibit_ipc && + IN_SET(log_target, LOG_TARGET_AUTO, +diff --git a/test/TEST-21-SYSUSERS/test.sh b/test/TEST-21-SYSUSERS/test.sh +index b1049e720d..3460d71f22 100755 +--- a/test/TEST-21-SYSUSERS/test.sh ++++ b/test/TEST-21-SYSUSERS/test.sh +@@ -108,8 +108,7 @@ test_run() { + echo "*** Running test $f" + prepare_testdir ${f%.input} + cp $f $TESTDIR/usr/lib/sysusers.d/test.conf +- systemd-sysusers --root=$TESTDIR 2> /dev/null +- journalctl -t systemd-sysusers -o cat | tail -n1 > $TESTDIR/tmp/err ++ systemd-sysusers --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/tmp/err + if ! diff -u $TESTDIR/tmp/err ${f%.*}.expected-err; then + echo "**** Unexpected error output for $f" + cat $TESTDIR/tmp/err diff --git a/SOURCES/0484-locale-util-add-new-helper-locale_is_installed.patch b/SOURCES/0484-locale-util-add-new-helper-locale_is_installed.patch new file mode 100644 index 0000000..902a685 --- /dev/null +++ b/SOURCES/0484-locale-util-add-new-helper-locale_is_installed.patch @@ -0,0 +1,58 @@ +From f0d9e0cb24958bc11c8d83f0a3de651def2aa1d6 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 30 Apr 2020 18:30:56 +0200 +Subject: [PATCH] locale-util: add new helper locale_is_installed() + +This new helper checks whether the specified locale is installed. It's +distinct from locale_is_valid() which just superficially checks if a +string looks like something that could be a valid locale. + +Heavily inspired by @jsynacek's #13964. + +Replaces: #13964 +(cherry picked from commit 23fa786ca67ed3a32930ff1a7b175ac823db187c) + +Related: #1755287 +--- + src/basic/locale-util.c | 15 +++++++++++++++ + src/basic/locale-util.h | 1 + + 2 files changed, 16 insertions(+) + +diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c +index 7cd143ea6f..42ef309ebd 100644 +--- a/src/basic/locale-util.c ++++ b/src/basic/locale-util.c +@@ -204,6 +204,21 @@ bool locale_is_valid(const char *name) { + return true; + } + ++int locale_is_installed(const char *name) { ++ if (!locale_is_valid(name)) ++ return false; ++ ++ if (STR_IN_SET(name, "C", "POSIX")) /* These ones are always OK */ ++ return true; ++ ++ _cleanup_(freelocalep) locale_t loc = ++ newlocale(LC_ALL_MASK, name, 0); ++ if (loc == (locale_t) 0) ++ return errno == ENOMEM ? -ENOMEM : false; ++ ++ return true; ++} ++ + void init_gettext(void) { + setlocale(LC_ALL, ""); + textdomain(GETTEXT_PACKAGE); +diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h +index 368675f286..b40f9c641a 100644 +--- a/src/basic/locale-util.h ++++ b/src/basic/locale-util.h +@@ -31,6 +31,7 @@ typedef enum LocaleVariable { + + int get_locales(char ***l); + bool locale_is_valid(const char *name); ++int locale_is_installed(const char *name); + + #define _(String) gettext(String) + #define N_(String) String diff --git a/SOURCES/0485-test-add-test-case-for-locale_is_installed.patch b/SOURCES/0485-test-add-test-case-for-locale_is_installed.patch new file mode 100644 index 0000000..57abbe8 --- /dev/null +++ b/SOURCES/0485-test-add-test-case-for-locale_is_installed.patch @@ -0,0 +1,53 @@ +From 3d08c7971a80370f60dd14b068779851e0f82c24 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 30 Apr 2020 18:32:55 +0200 +Subject: [PATCH] test: add test case for locale_is_installed() + +(cherry picked from commit b45b0a69bb7ef3e6e66d443eae366b6d1c387cab) + +Related: #1755287 +--- + src/test/test-locale-util.c | 23 +++++++++++++++++++++++ + 1 file changed, 23 insertions(+) + +diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c +index 0c3f6a62ed..0d50c33ce5 100644 +--- a/src/test/test-locale-util.c ++++ b/src/test/test-locale-util.c +@@ -34,6 +34,28 @@ static void test_locale_is_valid(void) { + assert_se(!locale_is_valid("\x01gar\x02 bage\x03")); + } + ++static void test_locale_is_installed(void) { ++ log_info("/* %s */", __func__); ++ ++ /* Always available */ ++ assert_se(locale_is_installed("POSIX") > 0); ++ assert_se(locale_is_installed("C") > 0); ++ ++ /* Might, or might not be installed. */ ++ assert_se(locale_is_installed("en_EN.utf8") >= 0); ++ assert_se(locale_is_installed("fr_FR.utf8") >= 0); ++ assert_se(locale_is_installed("fr_FR@euro") >= 0); ++ assert_se(locale_is_installed("fi_FI") >= 0); ++ ++ /* Definitely not valid */ ++ assert_se(locale_is_installed("") == 0); ++ assert_se(locale_is_installed("/usr/bin/foo") == 0); ++ assert_se(locale_is_installed("\x01gar\x02 bage\x03") == 0); ++ ++ /* Definitely not installed */ ++ assert_se(locale_is_installed("zz_ZZ") == 0); ++} ++ + static void test_keymaps(void) { + _cleanup_strv_free_ char **kmaps = NULL; + char **p; +@@ -95,6 +117,7 @@ static void dump_special_glyphs(void) { + int main(int argc, char *argv[]) { + test_get_locales(); + test_locale_is_valid(); ++ test_locale_is_installed(); + test_keymaps(); + + dump_special_glyphs(); diff --git a/SOURCES/0486-tree-wide-port-various-bits-over-to-locale_is_instal.patch b/SOURCES/0486-tree-wide-port-various-bits-over-to-locale_is_instal.patch new file mode 100644 index 0000000..cb4ec8d --- /dev/null +++ b/SOURCES/0486-tree-wide-port-various-bits-over-to-locale_is_instal.patch @@ -0,0 +1,232 @@ +From 5813180a75aa1ef90f6d3459fc5beb099b815cfb Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 30 Apr 2020 18:32:44 +0200 +Subject: [PATCH] tree-wide: port various bits over to locale_is_installed() + +(cherry picked from commit a00a78b84e2ab352b3144bfae8bc578d172303be) + +Resolves: #1755287 +--- + src/firstboot/firstboot.c | 30 ++++++++------ + src/locale/localed.c | 87 ++++++++++++++++++++++++--------------- + 2 files changed, 71 insertions(+), 46 deletions(-) + +diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c +index a98e53b3a3..7e177a50fa 100644 +--- a/src/firstboot/firstboot.c ++++ b/src/firstboot/firstboot.c +@@ -192,6 +192,14 @@ static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char * + } + } + ++static bool locale_is_ok(const char *name) { ++ ++ if (arg_root) ++ return locale_is_valid(name); ++ ++ return locale_is_installed(name) > 0; ++} ++ + static int prompt_locale(void) { + _cleanup_strv_free_ char **locales = NULL; + int r; +@@ -215,14 +223,14 @@ static int prompt_locale(void) { + + putchar('\n'); + +- r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale); ++ r = prompt_loop("Please enter system locale name or number", locales, locale_is_ok, &arg_locale); + if (r < 0) + return r; + + if (isempty(arg_locale)) + return 0; + +- r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages); ++ r = prompt_loop("Please enter system message locale name or number", locales, locale_is_ok, &arg_locale_messages); + if (r < 0) + return r; + +@@ -780,11 +788,6 @@ static int parse_argv(int argc, char *argv[]) { + break; + + case ARG_LOCALE: +- if (!locale_is_valid(optarg)) { +- log_error("Locale %s is not valid.", optarg); +- return -EINVAL; +- } +- + r = free_and_strdup(&arg_locale, optarg); + if (r < 0) + return log_oom(); +@@ -792,11 +795,6 @@ static int parse_argv(int argc, char *argv[]) { + break; + + case ARG_LOCALE_MESSAGES: +- if (!locale_is_valid(optarg)) { +- log_error("Locale %s is not valid.", optarg); +- return -EINVAL; +- } +- + r = free_and_strdup(&arg_locale_messages, optarg); + if (r < 0) + return log_oom(); +@@ -922,6 +920,14 @@ static int parse_argv(int argc, char *argv[]) { + assert_not_reached("Unhandled option"); + } + ++ /* We check if the specified locale strings are valid down here, so that we can take --root= into ++ * account when looking for the locale files. */ ++ ++ if (arg_locale && !locale_is_ok(arg_locale)) ++ return log_error_errno(EINVAL, "Locale %s is not installed.", arg_locale); ++ if (arg_locale_messages && !locale_is_ok(arg_locale_messages)) ++ return log_error_errno(EINVAL, "Locale %s is not installed.", arg_locale_messages); ++ + return 1; + } + +diff --git a/src/locale/localed.c b/src/locale/localed.c +index 253973fd49..d6ed40babe 100644 +--- a/src/locale/localed.c ++++ b/src/locale/localed.c +@@ -259,18 +259,57 @@ static void locale_free(char ***l) { + (*l)[p] = mfree((*l)[p]); + } + ++static int process_locale_list_item( ++ const char *assignment, ++ char *new_locale[static _VARIABLE_LC_MAX], ++ sd_bus_error *error) { ++ ++ assert(assignment); ++ assert(new_locale); ++ ++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) { ++ const char *name, *e; ++ ++ assert_se(name = locale_variable_to_string(p)); ++ ++ e = startswith(assignment, name); ++ if (!e) ++ continue; ++ ++ if (*e != '=') ++ continue; ++ ++ e++; ++ ++ if (!locale_is_valid(e)) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e); ++ if (locale_is_installed(e) <= 0) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e); ++ if (new_locale[p]) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name); ++ ++ new_locale[p] = strdup(e); ++ if (!new_locale[p]) ++ return -ENOMEM; ++ ++ return 0; ++ } ++ ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment); ++} ++ + static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) { + Context *c = userdata; + _cleanup_strv_free_ char **settings = NULL, **l = NULL; + char *new_locale[_VARIABLE_LC_MAX] = {}, **i; + _cleanup_(locale_free) _unused_ char **dummy = new_locale; + bool modified = false; +- int interactive, p, r; ++ int interactive, r; + + assert(m); + assert(c); + +- r = bus_message_read_strv_extend(m, &l); ++ r = sd_bus_message_read_strv(m, &l); + if (r < 0) + return r; + +@@ -279,11 +318,13 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + return r; + + /* If single locale without variable name is provided, then we assume it is LANG=. */ +- if (strv_length(l) == 1 && !strchr(*l, '=')) { +- if (!locale_is_valid(*l)) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data."); ++ if (strv_length(l) == 1 && !strchr(l[0], '=')) { ++ if (!locale_is_valid(l[0])) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]); ++ if (locale_is_installed(l[0]) <= 0) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]); + +- new_locale[VARIABLE_LANG] = strdup(*l); ++ new_locale[VARIABLE_LANG] = strdup(l[0]); + if (!new_locale[VARIABLE_LANG]) + return -ENOMEM; + +@@ -292,31 +333,9 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + + /* Check whether a variable is valid */ + STRV_FOREACH(i, l) { +- bool valid = false; +- +- for (p = 0; p < _VARIABLE_LC_MAX; p++) { +- size_t k; +- const char *name; +- +- name = locale_variable_to_string(p); +- assert(name); +- +- k = strlen(name); +- if (startswith(*i, name) && +- (*i)[k] == '=' && +- locale_is_valid((*i) + k + 1)) { +- valid = true; +- +- new_locale[p] = strdup((*i) + k + 1); +- if (!new_locale[p]) +- return -ENOMEM; +- +- break; +- } +- } +- +- if (!valid) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data."); ++ r = process_locale_list_item(*i, new_locale, error); ++ if (r < 0) ++ return r; + } + + /* If LANG was specified, but not LANGUAGE, check if we should +@@ -339,7 +358,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + } + + /* Merge with the current settings */ +- for (p = 0; p < _VARIABLE_LC_MAX; p++) ++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) + if (!isempty(c->locale[p]) && isempty(new_locale[p])) { + new_locale[p] = strdup(c->locale[p]); + if (!new_locale[p]) +@@ -348,7 +367,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + + locale_simplify(new_locale); + +- for (p = 0; p < _VARIABLE_LC_MAX; p++) ++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) + if (!streq_ptr(c->locale[p], new_locale[p])) { + modified = true; + break; +@@ -373,7 +392,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + +- for (p = 0; p < _VARIABLE_LC_MAX; p++) ++ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) + free_and_replace(c->locale[p], new_locale[p]); + + r = locale_write_data(c, &settings); diff --git a/SOURCES/0487-install-allow-instantiated-units-to-be-enabled-via-p.patch b/SOURCES/0487-install-allow-instantiated-units-to-be-enabled-via-p.patch new file mode 100644 index 0000000..363f424 --- /dev/null +++ b/SOURCES/0487-install-allow-instantiated-units-to-be-enabled-via-p.patch @@ -0,0 +1,339 @@ +From 4c41ad9418058aefb2d2732b0b65da9c7cdf5151 Mon Sep 17 00:00:00 2001 +From: Ruixin Bao +Date: Tue, 21 Aug 2018 20:40:56 +0000 +Subject: [PATCH] install: allow instantiated units to be enabled via presets + +This patch implements https://github.com/systemd/systemd/issues/9421. + +The .preset file now is able to take a rule in the format of:(e.g) +enable foo@.service bar0 bar1 bar2 + +In the above example, when preset-all is called, all three instances of +foo@bar0.service, foo@bar1.service and foo@bar2.service will be enabled. + +When preset is called on a single service(e.g: foo@bar1.service), only +the mentioned one(foo@bar1.service) will be enabled. + +Tests are added for future regression. + +(cherry picked from commit 4c9565eea534cd233a913c8c21f7920dba229743) + +Resolves: #1812972 +--- + src/shared/install.c | 155 ++++++++++++++++++++++++++++++----- + src/test/test-install-root.c | 57 +++++++++++++ + 2 files changed, 193 insertions(+), 19 deletions(-) + +diff --git a/src/shared/install.c b/src/shared/install.c +index 77ae812878..1d4beaa83b 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -60,6 +60,7 @@ typedef enum { + typedef struct { + char *pattern; + PresetAction action; ++ char **instances; + } PresetRule; + + typedef struct { +@@ -87,8 +88,10 @@ static inline void presets_freep(Presets *p) { + if (!p) + return; + +- for (i = 0; i < p->n_rules; i++) ++ for (i = 0; i < p->n_rules; i++) { + free(p->rules[i].pattern); ++ strv_free(p->rules[i].instances); ++ } + + free(p->rules); + p->n_rules = 0; +@@ -2755,6 +2758,39 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char * + return 1; + } + ++static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) { ++ _cleanup_strv_free_ char **instances = NULL; ++ _cleanup_free_ char *unit_name = NULL; ++ int r; ++ ++ assert(pattern); ++ assert(out_instances); ++ assert(out_unit_name); ++ ++ r = extract_first_word(&pattern, &unit_name, NULL, 0); ++ if (r < 0) ++ return r; ++ ++ /* We handle the instances logic when unit name is extracted */ ++ if (pattern) { ++ /* We only create instances when a rule of templated unit ++ * is seen. A rule like enable foo@.service a b c will ++ * result in an array of (a, b, c) as instance names */ ++ if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) ++ return -EINVAL; ++ ++ instances = strv_split(pattern, WHITESPACE); ++ if (!instances) ++ return -ENOMEM; ++ ++ *out_instances = TAKE_PTR(instances); ++ } ++ ++ *out_unit_name = TAKE_PTR(unit_name); ++ ++ return 0; ++} ++ + static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) { + _cleanup_(presets_freep) Presets ps = {}; + size_t n_allocated = 0; +@@ -2824,15 +2860,20 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres + + parameter = first_word(l, "enable"); + if (parameter) { +- char *pattern; ++ char *unit_name; ++ char **instances = NULL; + +- pattern = strdup(parameter); +- if (!pattern) +- return -ENOMEM; ++ /* Unit_name will remain the same as parameter when no instances are specified */ ++ r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances); ++ if (r < 0) { ++ log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line); ++ continue; ++ } + + rule = (PresetRule) { +- .pattern = pattern, ++ .pattern = unit_name, + .action = PRESET_ENABLE, ++ .instances = instances, + }; + } + +@@ -2868,15 +2909,71 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres + return 0; + } + +-static int query_presets(const char *name, const Presets presets) { ++static int pattern_match_multiple_instances( ++ const PresetRule rule, ++ const char *unit_name, ++ char ***ret) { ++ ++ _cleanup_free_ char *templated_name = NULL; ++ int r; ++ ++ /* If no ret is needed or the rule itself does not have instances ++ * initalized, we return not matching */ ++ if (!ret || !rule.instances) ++ return 0; ++ ++ r = unit_name_template(unit_name, &templated_name); ++ if (r < 0) ++ return r; ++ if (!streq(rule.pattern, templated_name)) ++ return 0; ++ ++ /* Compose a list of specified instances when unit name is a template */ ++ if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { ++ _cleanup_free_ char *prefix = NULL; ++ _cleanup_strv_free_ char **out_strv = NULL; ++ char **iter; ++ ++ r = unit_name_to_prefix(unit_name, &prefix); ++ if (r < 0) ++ return r; ++ ++ STRV_FOREACH(iter, rule.instances) { ++ _cleanup_free_ char *name = NULL; ++ r = unit_name_build(prefix, *iter, ".service", &name); ++ if (r < 0) ++ return r; ++ r = strv_extend(&out_strv, name); ++ if (r < 0) ++ return r; ++ } ++ ++ *ret = TAKE_PTR(out_strv); ++ return 1; ++ } else { ++ /* We now know the input unit name is an instance name */ ++ _cleanup_free_ char *instance_name = NULL; ++ ++ r = unit_name_to_instance(unit_name, &instance_name); ++ if (r < 0) ++ return r; ++ ++ if (strv_find(rule.instances, instance_name)) ++ return 1; ++ } ++ return 0; ++} ++ ++static int query_presets(const char *name, const Presets presets, char ***instance_name_list) { + PresetAction action = PRESET_UNKNOWN; + size_t i; +- ++ char **s; + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + for (i = 0; i < presets.n_rules; i++) +- if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { ++ if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 || ++ fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { + action = presets.rules[i].action; + break; + } +@@ -2886,7 +2983,11 @@ static int query_presets(const char *name, const Presets presets) { + log_debug("Preset files don't specify rule for %s. Enabling.", name); + return 1; + case PRESET_ENABLE: +- log_debug("Preset files say enable %s.", name); ++ if (instance_name_list && *instance_name_list) ++ STRV_FOREACH(s, *instance_name_list) ++ log_debug("Preset files say enable %s.", *s); ++ else ++ log_debug("Preset files say enable %s.", name); + return 1; + case PRESET_DISABLE: + log_debug("Preset files say disable %s.", name); +@@ -2904,7 +3005,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char + if (r < 0) + return r; + +- return query_presets(name, presets); ++ return query_presets(name, presets, NULL); + } + + static int execute_preset( +@@ -2964,6 +3065,7 @@ static int preset_prepare_one( + size_t *n_changes) { + + _cleanup_(install_context_done) InstallContext tmp = {}; ++ _cleanup_strv_free_ char **instance_name_list = NULL; + UnitFileInstallInfo *i; + int r; + +@@ -2979,19 +3081,34 @@ static int preset_prepare_one( + return 0; + } + +- r = query_presets(name, presets); ++ r = query_presets(name, presets, &instance_name_list); + if (r < 0) + return r; + + if (r > 0) { +- r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; ++ if (instance_name_list) { ++ char **s; ++ STRV_FOREACH(s, instance_name_list) { ++ r = install_info_discover(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); ++ if (r < 0) ++ return r; ++ ++ r = install_info_may_process(i, paths, changes, n_changes); ++ if (r < 0) ++ return r; ++ } ++ } else { ++ r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); ++ if (r < 0) ++ return r; ++ ++ r = install_info_may_process(i, paths, changes, n_changes); ++ if (r < 0) ++ return r; ++ } + +- r = install_info_may_process(i, paths, changes, n_changes); +- if (r < 0) +- return r; + } else + r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &i, changes, n_changes); +diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c +index 15dd3c6966..dbbcfe4297 100644 +--- a/src/test/test-install-root.c ++++ b/src/test/test-install-root.c +@@ -983,6 +983,62 @@ static void test_with_dropin_template(const char *root) { + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + } + ++static void test_preset_multiple_instances(const char *root) { ++ UnitFileChange *changes = NULL; ++ size_t n_changes = 0; ++ const char *p; ++ UnitFileState state; ++ ++ /* Set up template service files and preset file */ ++ p = strjoina(root, "/usr/lib/systemd/system/foo@.service"); ++ assert_se(write_string_file(p, ++ "[Install]\n" ++ "DefaultInstance=def\n" ++ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); ++ ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ ++ p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset"); ++ assert_se(write_string_file(p, ++ "enable foo@.service bar0 bar1 bartest\n" ++ "enable emptylist@.service\n" /* This line ensures the old functionality for templated unit still works */ ++ "disable *\n" , WRITE_STRING_FILE_CREATE) >= 0); ++ ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ ++ /* Preset a single instantiated unit specified in the list */ ++ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); ++ assert_se(n_changes == 1); ++ assert_se(changes[0].type == UNIT_FILE_SYMLINK); ++ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service"); ++ assert_se(streq(changes[0].path, p)); ++ unit_file_changes_free(changes, n_changes); ++ changes = NULL; n_changes = 0; ++ ++ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0); ++ assert_se(n_changes == 1); ++ assert_se(changes[0].type == UNIT_FILE_UNLINK); ++ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service"); ++ assert_se(streq(changes[0].path, p)); ++ unit_file_changes_free(changes, n_changes); ++ changes = NULL; n_changes = 0; ++ ++ /* Check for preset-all case, only instances on the list should be enabled, not including the default instance */ ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ ++ assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); ++ assert_se(n_changes > 0); ++ ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); ++ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED); ++ ++} ++ + int main(int argc, char *argv[]) { + char root[] = "/tmp/rootXXXXXX"; + const char *p; +@@ -1012,6 +1068,7 @@ int main(int argc, char *argv[]) { + test_indirect(root); + test_preset_and_list(root); + test_preset_order(root); ++ test_preset_multiple_instances(root); + test_revert(root); + test_static_instance(root); + test_with_dropin(root); diff --git a/SOURCES/0488-install-small-refactor-to-combine-two-function-calls.patch b/SOURCES/0488-install-small-refactor-to-combine-two-function-calls.patch new file mode 100644 index 0000000..68e7ae2 --- /dev/null +++ b/SOURCES/0488-install-small-refactor-to-combine-two-function-calls.patch @@ -0,0 +1,127 @@ +From eacb511fc0d1e3c5857cb041ad162fb78b4381cc Mon Sep 17 00:00:00 2001 +From: Ruixin Bao +Date: Sun, 26 Aug 2018 20:00:03 +0000 +Subject: [PATCH] install: small refactor to combine two function calls into + one function + +Combine consecutive function calls of install_info_discover and +install_info_may_process into one short helper function. + +(cherry picked from commit 1e475a0ab4c46eb07f3df3fb24f5a7c3e1fa20b1) + +Related: #1812972 +--- + src/shared/install.c | 61 ++++++++++++++++++++++---------------------- + 1 file changed, 30 insertions(+), 31 deletions(-) + +diff --git a/src/shared/install.c b/src/shared/install.c +index 1d4beaa83b..263b239f10 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -1676,6 +1676,25 @@ static int install_info_discover( + return r; + } + ++static int install_info_discover_and_check( ++ UnitFileScope scope, ++ InstallContext *c, ++ const LookupPaths *paths, ++ const char *name, ++ SearchFlags flags, ++ UnitFileInstallInfo **ret, ++ UnitFileChange **changes, ++ size_t *n_changes) { ++ ++ int r; ++ ++ r = install_info_discover(scope, c, paths, name, flags, ret, changes, n_changes); ++ if (r < 0) ++ return r; ++ ++ return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes); ++} ++ + static int install_info_symlink_alias( + UnitFileInstallInfo *i, + const LookupPaths *paths, +@@ -2399,11 +2418,8 @@ int unit_file_add_dependency( + if (!config_path) + return -ENXIO; + +- r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &target_info, changes, n_changes); +- if (r < 0) +- return r; +- r = install_info_may_process(target_info, &paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &target_info, changes, n_changes); + if (r < 0) + return r; + +@@ -2412,11 +2428,8 @@ int unit_file_add_dependency( + STRV_FOREACH(f, files) { + char ***l; + +- r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; +- r = install_info_may_process(i, &paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); + if (r < 0) + return r; + +@@ -2467,11 +2480,8 @@ int unit_file_enable( + return -ENXIO; + + STRV_FOREACH(f, files) { +- r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; +- r = install_info_may_process(i, &paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); + if (r < 0) + return r; + +@@ -2585,10 +2595,7 @@ int unit_file_set_default( + if (r < 0) + return r; + +- r = install_info_discover(scope, &c, &paths, name, 0, &i, changes, n_changes); +- if (r < 0) +- return r; +- r = install_info_may_process(i, &paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, &c, &paths, name, 0, &i, changes, n_changes); + if (r < 0) + return r; + +@@ -3089,22 +3096,14 @@ static int preset_prepare_one( + if (instance_name_list) { + char **s; + STRV_FOREACH(s, instance_name_list) { +- r = install_info_discover(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; +- +- r = install_info_may_process(i, paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); + if (r < 0) + return r; + } + } else { +- r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, +- &i, changes, n_changes); +- if (r < 0) +- return r; +- +- r = install_info_may_process(i, paths, changes, n_changes); ++ r = install_info_discover_and_check(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, ++ &i, changes, n_changes); + if (r < 0) + return r; + } diff --git a/SOURCES/0489-test-fix-a-memleak.patch b/SOURCES/0489-test-fix-a-memleak.patch new file mode 100644 index 0000000..d01dea9 --- /dev/null +++ b/SOURCES/0489-test-fix-a-memleak.patch @@ -0,0 +1,28 @@ +From 7444c6ed3628484dfed2f204c5b78a06a50f4bd8 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 29 Aug 2018 23:27:42 +0900 +Subject: [PATCH] test: fix a memleak + +Follow-up for #9901. + +Fixes #9968. + +(cherry picked from commit efa146369398fdb73f1cd177eb2522822ebf559c) + +Related: #1812972 +--- + src/test/test-install-root.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c +index dbbcfe4297..fe1ca5b16f 100644 +--- a/src/test/test-install-root.c ++++ b/src/test/test-install-root.c +@@ -1037,6 +1037,7 @@ static void test_preset_multiple_instances(const char *root) { + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + ++ unit_file_changes_free(changes, n_changes); + } + + int main(int argc, char *argv[]) { diff --git a/SOURCES/0490-docs-Add-syntax-for-templated-units-to-systemd.prese.patch b/SOURCES/0490-docs-Add-syntax-for-templated-units-to-systemd.prese.patch new file mode 100644 index 0000000..2f726d0 --- /dev/null +++ b/SOURCES/0490-docs-Add-syntax-for-templated-units-to-systemd.prese.patch @@ -0,0 +1,55 @@ +From 55df2fd634f900419b718ed354132cc86cd533dd Mon Sep 17 00:00:00 2001 +From: Joerg Behrmann +Date: Tue, 10 Mar 2020 16:34:13 +0100 +Subject: [PATCH] docs: Add syntax for templated units to systemd.preset man + page + +This documents the syntax + + enable template@.service foo bar baz + +that was introduced in #9901 to preset templated units. + +(cherry picked from commit 1f667d8a7cff4355cd23ebebeb4d7179e3498eb8) + +Related: #1812972 +--- + man/systemd.preset.xml | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml +index cf807bd4c8..df401f00f3 100644 +--- a/man/systemd.preset.xml ++++ b/man/systemd.preset.xml +@@ -71,8 +71,11 @@ + either the word enable or + disable followed by a space and a unit name + (possibly with shell style wildcards), separated by newlines. +- Empty lines and lines whose first non-whitespace character is # or +- ; are ignored. ++ Empty lines and lines whose first non-whitespace character is # or ++ ; are ignored. Multiple instance names for unit ++ templates may be specified as a space separated list at the end of ++ the line instead of the customary position between @ ++ and the unit suffix. + + Presets must refer to the "real" unit file, and not to any aliases. See + systemd.unit5 +@@ -124,6 +127,17 @@ disable * + 99-, it will be read last and hence can easily + be overridden by spin or administrator preset policy. + ++ ++ Enable multiple template instances ++ ++ # /usr/lib/systemd/system-preset/80-dirsrv.preset ++ ++enable dirsrv@.service foo bar baz ++ ++ ++ This enables all three of dirsrv@foo.service, ++ dirsrv@bar.service and dirsrv@baz.service. ++ + + A GNOME spin + diff --git a/SOURCES/0491-shared-install-fix-preset-operations-for-non-service.patch b/SOURCES/0491-shared-install-fix-preset-operations-for-non-service.patch new file mode 100644 index 0000000..ea936c9 --- /dev/null +++ b/SOURCES/0491-shared-install-fix-preset-operations-for-non-service.patch @@ -0,0 +1,45 @@ +From db2816ee32fc81ba339175469e46b5dca7af8833 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 22 Aug 2020 11:58:15 +0200 +Subject: [PATCH] shared/install: fix preset operations for non-service + instantiated units + +Fixes https://github.com/coreos/ignition/issues/1064. + +(cherry picked from commit 47ab95fe4315b3f7ee5a3694460a744bb88c52fd) + +Related: #1812972 +--- + src/shared/install.c | 13 +++++-------- + 1 file changed, 5 insertions(+), 8 deletions(-) + +diff --git a/src/shared/install.c b/src/shared/install.c +index 263b239f10..c2847df3f8 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -2937,20 +2937,17 @@ static int pattern_match_multiple_instances( + + /* Compose a list of specified instances when unit name is a template */ + if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { +- _cleanup_free_ char *prefix = NULL; + _cleanup_strv_free_ char **out_strv = NULL; +- char **iter; +- +- r = unit_name_to_prefix(unit_name, &prefix); +- if (r < 0) +- return r; + ++ char **iter; + STRV_FOREACH(iter, rule.instances) { + _cleanup_free_ char *name = NULL; +- r = unit_name_build(prefix, *iter, ".service", &name); ++ ++ r = unit_name_replace_instance(unit_name, *iter, &name); + if (r < 0) + return r; +- r = strv_extend(&out_strv, name); ++ ++ r = strv_consume(&out_strv, TAKE_PTR(name)); + if (r < 0) + return r; + } diff --git a/SOURCES/0492-introduce-setsockopt_int-helper.patch b/SOURCES/0492-introduce-setsockopt_int-helper.patch new file mode 100644 index 0000000..7991184 --- /dev/null +++ b/SOURCES/0492-introduce-setsockopt_int-helper.patch @@ -0,0 +1,30 @@ +From 8cff80d7fc28ca04bd6c8e2257b46d96bea338ce Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 18 Oct 2018 19:48:18 +0200 +Subject: [PATCH] introduce setsockopt_int() helper + +As suggested by @heftig: + +https://github.com/systemd/systemd/commit/6d5e65f6454212cd400d0ebda34978a9f20cc26a#commitcomment-30938667 +(cherry picked from commit 2ff48e981e6cd1ccbfae49943274d9c8319a5e5d) + +Related: #1887181 +--- + src/basic/socket-util.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h +index 82781a0de1..616f2e0d05 100644 +--- a/src/basic/socket-util.h ++++ b/src/basic/socket-util.h +@@ -183,3 +183,10 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng + }) + + int socket_ioctl_fd(void); ++ ++static inline int setsockopt_int(int fd, int level, int optname, int value) { ++ if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) ++ return -errno; ++ ++ return 0; ++} diff --git a/SOURCES/0493-socket-util-add-generic-socket_pass_pktinfo-helper.patch b/SOURCES/0493-socket-util-add-generic-socket_pass_pktinfo-helper.patch new file mode 100644 index 0000000..aa0679b --- /dev/null +++ b/SOURCES/0493-socket-util-add-generic-socket_pass_pktinfo-helper.patch @@ -0,0 +1,57 @@ +From 96681723232e9eb0182279086fef291283004806 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 27 May 2020 19:27:51 +0200 +Subject: [PATCH] socket-util: add generic socket_pass_pktinfo() helper + +The helper turns on the protocol specific "packet info" structure cmsg +for three relevant protocols we know. + +(cherry picked from commit 35a3eb9bdc95d1e6ba25bc65c78959ea104e45a1) + +Related: #1887181 +--- + src/basic/socket-util.c | 23 +++++++++++++++++++++++ + src/basic/socket-util.h | 2 ++ + 2 files changed, 25 insertions(+) + +diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c +index 986bc6e67f..053bcba670 100644 +--- a/src/basic/socket-util.c ++++ b/src/basic/socket-util.c +@@ -1246,3 +1246,26 @@ int socket_ioctl_fd(void) { + + return fd; + } ++ ++int socket_pass_pktinfo(int fd, bool b) { ++ int af; ++ socklen_t sl = sizeof(af); ++ ++ if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0) ++ return -errno; ++ ++ switch (af) { ++ ++ case AF_INET: ++ return setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, b); ++ ++ case AF_INET6: ++ return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, b); ++ ++ case AF_NETLINK: ++ return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b); ++ ++ default: ++ return -EAFNOSUPPORT; ++ } ++} +diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h +index 616f2e0d05..c7c9ad34d6 100644 +--- a/src/basic/socket-util.h ++++ b/src/basic/socket-util.h +@@ -190,3 +190,5 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) { + + return 0; + } ++ ++int socket_pass_pktinfo(int fd, bool b); diff --git a/SOURCES/0494-core-add-new-PassPacketInfo-socket-unit-property.patch b/SOURCES/0494-core-add-new-PassPacketInfo-socket-unit-property.patch new file mode 100644 index 0000000..561e77c --- /dev/null +++ b/SOURCES/0494-core-add-new-PassPacketInfo-socket-unit-property.patch @@ -0,0 +1,156 @@ +From 905a97ce65352d80af7260d34b74fd8342792c35 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 27 May 2020 19:36:56 +0200 +Subject: [PATCH] core: add new PassPacketInfo= socket unit property + +(cherry picked from commit a3d19f5d99c44940831a33df8b5bece4aaf749f7) + +Resolves: #1887181 +--- + doc/TRANSIENT-SETTINGS.md | 1 + + man/systemd.socket.xml | 9 +++++++++ + src/core/dbus-socket.c | 4 ++++ + src/core/load-fragment-gperf.gperf.m4 | 1 + + src/core/socket.c | 8 ++++++++ + src/core/socket.h | 1 + + src/shared/bus-unit-util.c | 3 +-- + test/fuzz/fuzz-unit-file/directives.service | 1 + + 8 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md +index 995b8797ef..de0ef9cc49 100644 +--- a/doc/TRANSIENT-SETTINGS.md ++++ b/doc/TRANSIENT-SETTINGS.md +@@ -410,6 +410,7 @@ Most socket unit settings are available to transient units. + ✓ Broadcast= + ✓ PassCredentials= + ✓ PassSecurity= ++✓ PassPacketInfo= + ✓ TCPCongestion= + ✓ ReusePort= + ✓ MessageQueueMaxMessages= +diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml +index 8676b4e03f..a908d5b6d8 100644 +--- a/man/systemd.socket.xml ++++ b/man/systemd.socket.xml +@@ -712,6 +712,15 @@ + Defaults to . + + ++ ++ PassPacketInfo= ++ Takes a boolean value. This controls the IP_PKTINFO, ++ IPV6_RECVPKTINFO and NETLINK_PKTINFO socket options, which ++ enable reception of additional per-packet metadata as ancillary message, on ++ AF_INET, AF_INET6 and AF_UNIX sockets. ++ Defaults to . ++ ++ + + TCPCongestion= + Takes a string value. Controls the TCP +diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c +index fa6bbe2c6f..17494b80c8 100644 +--- a/src/core/dbus-socket.c ++++ b/src/core/dbus-socket.c +@@ -104,6 +104,7 @@ const sd_bus_vtable bus_socket_vtable[] = { + SD_BUS_PROPERTY("Broadcast", "b", bus_property_get_bool, offsetof(Socket, broadcast), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PassCredentials", "b", bus_property_get_bool, offsetof(Socket, pass_cred), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("PassPacketInfo", "b", bus_property_get_bool, offsetof(Socket, pass_pktinfo), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -205,6 +206,9 @@ static int bus_socket_set_transient_property( + if (streq(name, "PassSecurity")) + return bus_set_transient_bool(u, name, &s->pass_sec, message, flags, error); + ++ if (streq(name, "PassPacketInfo")) ++ return bus_set_transient_bool(u, name, &s->pass_pktinfo, message, flags, error); ++ + if (streq(name, "ReusePort")) + return bus_set_transient_bool(u, name, &s->reuse_port, message, flags, error); + +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 156a4d0a6d..7d683cc84b 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -381,6 +381,7 @@ Socket.Transparent, config_parse_bool, 0, + Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast) + Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred) + Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec) ++Socket.PassPacketInfo, config_parse_bool, 0, offsetof(Socket, pass_pktinfo) + Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion) + Socket.ReusePort, config_parse_bool, 0, offsetof(Socket, reuse_port) + Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg) +diff --git a/src/core/socket.c b/src/core/socket.c +index 97c3a7fc9a..50c32ed8f4 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -660,6 +660,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { + "%sBroadcast: %s\n" + "%sPassCredentials: %s\n" + "%sPassSecurity: %s\n" ++ "%sPassPacketInfo: %s\n" + "%sTCPCongestion: %s\n" + "%sRemoveOnStop: %s\n" + "%sWritable: %s\n" +@@ -678,6 +679,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { + prefix, yes_no(s->broadcast), + prefix, yes_no(s->pass_cred), + prefix, yes_no(s->pass_sec), ++ prefix, yes_no(s->pass_pktinfo), + prefix, strna(s->tcp_congestion), + prefix, yes_no(s->remove_on_stop), + prefix, yes_no(s->writable), +@@ -1099,6 +1101,12 @@ static void socket_apply_socket_options(Socket *s, int fd) { + log_unit_warning_errno(UNIT(s), errno, "SO_PASSSEC failed: %m"); + } + ++ if (s->pass_pktinfo) { ++ r = socket_pass_pktinfo(fd, true); ++ if (r < 0) ++ log_unit_warning_errno(UNIT(s), r, "Failed to enable packet info socket option: %m"); ++ } ++ + if (s->priority >= 0) + if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0) + log_unit_warning_errno(UNIT(s), errno, "SO_PRIORITY failed: %m"); +diff --git a/src/core/socket.h b/src/core/socket.h +index b7a25d91fd..2409dbf2a0 100644 +--- a/src/core/socket.h ++++ b/src/core/socket.h +@@ -121,6 +121,7 @@ struct Socket { + bool broadcast; + bool pass_cred; + bool pass_sec; ++ bool pass_pktinfo; + + /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */ + SocketAddressBindIPv6Only bind_ipv6_only; +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index daa2c2dce5..9010448aaf 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -1478,8 +1478,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons + if (STR_IN_SET(field, + "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast", + "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet", +- "FlushPending")) +- ++ "FlushPending", "PassPacketInfo")) + return bus_append_parse_boolean(m, field, eq); + + if (STR_IN_SET(field, "Priority", "IPTTL", "Mark")) +diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service +index 9d0530df72..8fde27fc90 100644 +--- a/test/fuzz/fuzz-unit-file/directives.service ++++ b/test/fuzz/fuzz-unit-file/directives.service +@@ -161,6 +161,7 @@ PIDFile= + PartOf= + PassCredentials= + PassSecurity= ++PassPacketInfo= + PathChanged= + PathExists= + PathExistsGlob= diff --git a/SOURCES/0495-resolved-tweak-cmsg-calculation.patch b/SOURCES/0495-resolved-tweak-cmsg-calculation.patch new file mode 100644 index 0000000..5ce13b6 --- /dev/null +++ b/SOURCES/0495-resolved-tweak-cmsg-calculation.patch @@ -0,0 +1,30 @@ +From 6ece87bef14ac5741fc870644504737b00607546 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 27 May 2020 19:38:38 +0200 +Subject: [PATCH] resolved: tweak cmsg calculation + +We ask for the TTL, then have enough space for it. + +We probably can drop the extra cmsg space now, but let's figure that out +another time, since the extra cmsg space is used elsewhere in resolved +as well. + +(cherry picked from commit 08ab18618ec59022582f1513c0718ba369f5ba85) + +Related: #1887181 +--- + src/resolve/resolved-dns-stream.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c +index 066daef96e..555e200a23 100644 +--- a/src/resolve/resolved-dns-stream.c ++++ b/src/resolve/resolved-dns-stream.c +@@ -70,6 +70,7 @@ static int dns_stream_identify(DnsStream *s) { + union { + struct cmsghdr header; /* For alignment */ + uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo)) ++ + CMSG_SPACE(int) + /* for the TTL */ + + EXTRA_CMSG_SPACE /* kernel appears to require extra space */]; + } control; + struct msghdr mh = {}; diff --git a/SOURCES/0496-ci-PowerTools-repo-was-renamed-to-powertools-in-RHEL.patch b/SOURCES/0496-ci-PowerTools-repo-was-renamed-to-powertools-in-RHEL.patch new file mode 100644 index 0000000..cd054ce --- /dev/null +++ b/SOURCES/0496-ci-PowerTools-repo-was-renamed-to-powertools-in-RHEL.patch @@ -0,0 +1,26 @@ +From 07b154fbc817e93f58c597644570a633c38d1c72 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 15 Jan 2021 12:51:02 +0100 +Subject: [PATCH] ci: PowerTools repo was renamed to powertools in RHEL 8.3 + +See: https://wiki.centos.org/Manuals/ReleaseNotes/CentOS8.2011#Yum_repo_file_and_repoid_changes + +rhel-only +Related: #1871827 +--- + ci/travis-centos-rhel8.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index cd0857fd29..43e2cb2585 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -95,7 +95,7 @@ for phase in "${PHASES[@]}"; do + # Upgrade the container to get the most recent environment + $DOCKER_EXEC dnf -y upgrade + # Install systemd's build dependencies +- $DOCKER_EXEC dnf -q -y --enablerepo "PowerTools" builddep systemd ++ $DOCKER_EXEC dnf -q -y --enablerepo "powertools" builddep systemd + ;; + RUN) + info "Run phase" diff --git a/SOURCES/0497-ci-use-quay.io-instead-of-Docker-Hub-to-avoid-rate-l.patch b/SOURCES/0497-ci-use-quay.io-instead-of-Docker-Hub-to-avoid-rate-l.patch new file mode 100644 index 0000000..db85595 --- /dev/null +++ b/SOURCES/0497-ci-use-quay.io-instead-of-Docker-Hub-to-avoid-rate-l.patch @@ -0,0 +1,33 @@ +From 2dd82aad646bde5a0d49df8562e2578c8b3d04f4 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 15 Jan 2021 13:00:33 +0100 +Subject: [PATCH] ci: use quay.io instead of Docker Hub to avoid rate limits + +Docker Hub introduced rate limits for anonymous users (100 requests per +six hours), which break our CI in the busier periods. Let's try to use +the quay.io CentOS image to mitigate this. + +rhel-only +Related: #1871827 +--- + ci/travis-centos-rhel8.sh | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh +index 43e2cb2585..ffe5813b1a 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/ci/travis-centos-rhel8.sh +@@ -81,11 +81,11 @@ for phase in "${PHASES[@]}"; do + info "Setup phase" + info "Using Travis $CENTOS_RELEASE" + # Pull a Docker image and start a new container +- docker pull centos:$CENTOS_RELEASE ++ docker pull quay.io/centos/centos:$CENTOS_RELEASE + info "Starting container $CONT_NAME" + $DOCKER_RUN -v $REPO_ROOT:/build:rw \ + -w /build --privileged=true --name $CONT_NAME \ +- -dit --net=host centos:$CENTOS_RELEASE /sbin/init ++ -dit --net=host quay.io/centos/centos:$CENTOS_RELEASE /sbin/init + # Beautiful workaround for Fedora's version of Docker + sleep 1 + $DOCKER_EXEC dnf makecache diff --git a/SOURCES/0498-ci-move-jobs-from-Travis-CI-to-GH-Actions.patch b/SOURCES/0498-ci-move-jobs-from-Travis-CI-to-GH-Actions.patch new file mode 100644 index 0000000..70ba33f --- /dev/null +++ b/SOURCES/0498-ci-move-jobs-from-Travis-CI-to-GH-Actions.patch @@ -0,0 +1,338 @@ +From 88ac207cc619935c64923e6f8fdef324a5b733d8 Mon Sep 17 00:00:00 2001 +From: Frantisek Sumsal +Date: Fri, 15 Jan 2021 15:13:53 +0100 +Subject: [PATCH] ci: move jobs from Travis CI to GH Actions + +The OSS version of Travis CI is going to be merged with the commercial +one soon, essentially dropping the free tier, so let's move the CI jobs +to GitHub Actions to keep them up. + +rhel-only +Related: #1871827 +--- + .../workflows/unit_tests.sh | 28 +++---- + .github/workflows/unit_tests.yml | 28 +++++++ + .travis.yml | 48 ------------ + ci/travis-centos-rhel7.sh | 73 ------------------- + ci/travis_wait.bash | 61 ---------------- + 5 files changed, 37 insertions(+), 201 deletions(-) + rename ci/travis-centos-rhel8.sh => .github/workflows/unit_tests.sh (82%) + create mode 100644 .github/workflows/unit_tests.yml + delete mode 100644 .travis.yml + delete mode 100755 ci/travis-centos-rhel7.sh + delete mode 100644 ci/travis_wait.bash + +diff --git a/ci/travis-centos-rhel8.sh b/.github/workflows/unit_tests.sh +similarity index 82% +rename from ci/travis-centos-rhel8.sh +rename to .github/workflows/unit_tests.sh +index ffe5813b1a..ea4f7e7592 100755 +--- a/ci/travis-centos-rhel8.sh ++++ b/.github/workflows/unit_tests.sh +@@ -1,18 +1,9 @@ + #!/bin/bash + +-# Run this script from the root of the systemd's git repository +-# or set REPO_ROOT to a correct path. +-# +-# Example execution on Fedora: +-# dnf install docker +-# systemctl start docker +-# export CONT_NAME="my-fancy-container" +-# ci/travis-centos.sh SETUP RUN CLEANUP +- + PHASES=(${@:-SETUP RUN CLEANUP}) + CENTOS_RELEASE="${CENTOS_RELEASE:-latest}" + CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}" +-DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}" ++DOCKER_EXEC="${DOCKER_EXEC:-docker exec $CONT_NAME}" + DOCKER_RUN="${DOCKER_RUN:-docker run}" + REPO_ROOT="${REPO_ROOT:-$PWD}" + ADDITIONAL_DEPS=(libasan libubsan net-tools strace nc e2fsprogs quota dnsmasq diffutils) +@@ -71,9 +62,7 @@ function info() { + echo -e "\033[33;1m$1\033[0m" + } + +-set -e +- +-source "$(dirname $0)/travis_wait.bash" ++set -ex + + for phase in "${PHASES[@]}"; do + case $phase in +@@ -86,6 +75,7 @@ for phase in "${PHASES[@]}"; do + $DOCKER_RUN -v $REPO_ROOT:/build:rw \ + -w /build --privileged=true --name $CONT_NAME \ + -dit --net=host quay.io/centos/centos:$CENTOS_RELEASE /sbin/init ++ + # Beautiful workaround for Fedora's version of Docker + sleep 1 + $DOCKER_EXEC dnf makecache +@@ -97,10 +87,10 @@ for phase in "${PHASES[@]}"; do + # Install systemd's build dependencies + $DOCKER_EXEC dnf -q -y --enablerepo "powertools" builddep systemd + ;; +- RUN) ++ RUN|RUN_GCC) + info "Run phase" + # Build systemd +- docker exec -it -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build -Dtests=unsafe -Dslow-tests=true "${CONFIGURE_OPTS[@]}" ++ docker exec -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build -Dtests=unsafe -Dslow-tests=true "${CONFIGURE_OPTS[@]}" + $DOCKER_EXEC ninja -v -C build + # Let's install the new systemd and "reboot" the container to avoid + # unexpected fails due to incompatibilities with older systemd +@@ -108,16 +98,16 @@ for phase in "${PHASES[@]}"; do + docker restart $CONT_NAME + $DOCKER_EXEC ninja -C build test + ;; +- RUN_ASAN|RUN_CLANG_ASAN) ++ RUN_ASAN|RUN_GCC_ASAN|RUN_CLANG_ASAN) + if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then + ENV_VARS="-e CC=clang -e CXX=clang++" + MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764 + fi +- docker exec $ENV_VARS -it $CONT_NAME meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS "${CONFIGURE_OPTS[@]}" +- docker exec -it $CONT_NAME ninja -v -C build ++ docker exec $ENV_VARS $CONT_NAME meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS "${CONFIGURE_OPTS[@]}" ++ docker exec $CONT_NAME ninja -v -C build + + # Never remove halt_on_error from UBSAN_OPTIONS. See https://github.com/systemd/systemd/commit/2614d83aa06592aedb. +- travis_wait docker exec --interactive=false \ ++ docker exec --interactive=false \ + -e UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 \ + -e ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 \ + -e "TRAVIS=$TRAVIS" \ +diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml +new file mode 100644 +index 0000000000..15f5127a75 +--- /dev/null ++++ b/.github/workflows/unit_tests.yml +@@ -0,0 +1,28 @@ ++--- ++# vi: ts=2 sw=2 et: ++# ++name: Unit tests ++on: ++ pull_request: ++ branches: ++ - master ++ ++jobs: ++ build: ++ runs-on: ubuntu-20.04 ++ env: ++ CENTOS_RELEASE: "centos8" ++ CONT_NAME: "systemd-centos8-ci" ++ strategy: ++ fail-fast: false ++ matrix: ++ run_phase: [GCC, GCC_ASAN] ++ steps: ++ - name: Repository checkout ++ uses: actions/checkout@v1 ++ - name: Install build dependencies ++ run: sudo -E .github/workflows/unit_tests.sh SETUP ++ - name: Build & test (${{ matrix.run_phase }}) ++ run: sudo -E .github/workflows/unit_tests.sh RUN_${{ matrix.run_phase }} ++ - name: Cleanup ++ run: sudo -E .github/workflows/unit_tests.sh CLEANUP +diff --git a/.travis.yml b/.travis.yml +deleted file mode 100644 +index 70c60cf24e..0000000000 +--- a/.travis.yml ++++ /dev/null +@@ -1,48 +0,0 @@ +-sudo: required +-dist: xenial +-services: +- - docker +- +-env: +- global: +- - CI_ROOT="$TRAVIS_BUILD_DIR/ci/" +- +-jobs: +- include: +- - name: CentOS 8 +- language: bash +- env: +- - CENTOS_RELEASE="centos8" +- - CONT_NAME="systemd-centos-$CENTOS_RELEASE" +- - DOCKER_EXEC="docker exec -ti $CONT_NAME" +- before_install: +- - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce +- - docker --version +- install: +- - $CI_ROOT/travis-centos-rhel8.sh SETUP +- script: +- - set -e +- # Build systemd +- - $CI_ROOT/travis-centos-rhel8.sh RUN +- - set +e +- after_script: +- - $CI_ROOT/travis-centos-rhel8.sh CLEANUP +- +- - name: CentOS 8 (ASan+UBSan) +- language: bash +- env: +- - CENTOS_RELEASE="centos8" +- - CONT_NAME="systemd-centos-$CENTOS_RELEASE" +- - DOCKER_EXEC="docker exec -ti $CONT_NAME" +- before_install: +- - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce +- - docker --version +- install: +- - $CI_ROOT/travis-centos-rhel8.sh SETUP +- script: +- - set -e +- # Build systemd +- - $CI_ROOT/travis-centos-rhel8.sh RUN_ASAN +- - set +e +- after_script: +- - $CI_ROOT/travis-centos-rhel8.sh CLEANUP +diff --git a/ci/travis-centos-rhel7.sh b/ci/travis-centos-rhel7.sh +deleted file mode 100755 +index 73416798ed..0000000000 +--- a/ci/travis-centos-rhel7.sh ++++ /dev/null +@@ -1,73 +0,0 @@ +-#!/bin/bash +- +-# Run this script from the root of the systemd's git repository +-# or set REPO_ROOT to a correct path. +-# +-# Example execution on Fedora: +-# dnf install docker +-# systemctl start docker +-# export CONT_NAME="my-fancy-container" +-# ci/travis-centos.sh SETUP RUN CLEANUP +- +-PHASES=(${@:-SETUP RUN CLEANUP}) +-CENTOS_RELEASE="${CENTOS_RELEASE:-latest}" +-CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}" +-DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}" +-DOCKER_RUN="${DOCKER_RUN:-docker run}" +-REPO_ROOT="${REPO_ROOT:-$PWD}" +-ADDITIONAL_DEPS=(yum-utils iputils hostname libasan libubsan clang llvm) +- +-function info() { +- echo -e "\033[33;1m$1\033[0m" +-} +- +-set -e +- +-source "$(dirname $0)/travis_wait.bash" +- +-for phase in "${PHASES[@]}"; do +- case $phase in +- SETUP) +- info "Setup phase" +- info "Using Travis $CENTOS_RELEASE" +- # Pull a Docker image and start a new container +- docker pull centos:$CENTOS_RELEASE +- info "Starting container $CONT_NAME" +- $DOCKER_RUN -v $REPO_ROOT:/build:rw \ +- -w /build --privileged=true --name $CONT_NAME \ +- -dit --net=host centos:$CENTOS_RELEASE /sbin/init +- # Beautiful workaround for Fedora's version of Docker +- sleep 1 +- $DOCKER_EXEC yum makecache +- # Install necessary build/test requirements +- $DOCKER_EXEC yum -y upgrade +- $DOCKER_EXEC yum -y install "${ADDITIONAL_DEPS[@]}" +- $DOCKER_EXEC yum-builddep -y systemd +- ;; +- RUN) +- info "Run phase" +- # Build systemd +- $DOCKER_EXEC ./autogen.sh +- $DOCKER_EXEC ./configure --disable-timesyncd --disable-kdbus --disable-terminal \ +- --enable-gtk-doc --enable-compat-libs --disable-sysusers \ +- --disable-ldconfig --enable-lz4 --with-sysvinit-path=/etc/rc.d/init.d +- $DOCKER_EXEC make +- # Let's install the new systemd and "reboot" the container to avoid +- # unexpected fails due to incompatibilities with older systemd +- $DOCKER_EXEC make install +- docker restart $CONT_NAME +- if ! $DOCKER_EXEC make check; then +- $DOCKER_EXEC cat test-suite.log +- exit 1 +- fi +- ;; +- CLEANUP) +- info "Cleanup phase" +- docker stop $CONT_NAME +- docker rm -f $CONT_NAME +- ;; +- *) +- echo >&2 "Unknown phase '$phase'" +- exit 1 +- esac +-done +diff --git a/ci/travis_wait.bash b/ci/travis_wait.bash +deleted file mode 100644 +index acf6ad15e4..0000000000 +--- a/ci/travis_wait.bash ++++ /dev/null +@@ -1,61 +0,0 @@ +-# This was borrowed from https://github.com/travis-ci/travis-build/tree/master/lib/travis/build/bash +-# to get around https://github.com/travis-ci/travis-ci/issues/9979. It should probably be removed +-# as soon as Travis CI has started to provide an easy way to export the functions to bash scripts. +- +-travis_jigger() { +- local cmd_pid="${1}" +- shift +- local timeout="${1}" +- shift +- local count=0 +- +- echo -e "\\n" +- +- while [[ "${count}" -lt "${timeout}" ]]; do +- count="$((count + 1))" +- echo -ne "Still running (${count} of ${timeout}): ${*}\\r" +- sleep 60 +- done +- +- echo -e "\\n${ANSI_RED}Timeout (${timeout} minutes) reached. Terminating \"${*}\"${ANSI_RESET}\\n" +- kill -9 "${cmd_pid}" +-} +- +-travis_wait() { +- local timeout="${1}" +- +- if [[ "${timeout}" =~ ^[0-9]+$ ]]; then +- shift +- else +- timeout=20 +- fi +- +- local cmd=("${@}") +- local log_file="travis_wait_${$}.log" +- +- "${cmd[@]}" &>"${log_file}" & +- local cmd_pid="${!}" +- +- travis_jigger "${!}" "${timeout}" "${cmd[@]}" & +- local jigger_pid="${!}" +- local result +- +- { +- set +e +- wait "${cmd_pid}" 2>/dev/null +- result="${?}" +- ps -p"${jigger_pid}" &>/dev/null && kill "${jigger_pid}" +- set -e +- } +- +- if [[ "${result}" -eq 0 ]]; then +- echo -e "\\n${ANSI_GREEN}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}" +- else +- echo -e "\\n${ANSI_RED}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}" +- fi +- +- echo -e "\\n${ANSI_GREEN}Log:${ANSI_RESET}\\n" +- cat "${log_file}" +- +- return "${result}" +-} diff --git a/SOURCES/0499-unit-make-UNIT-cast-function-deal-with-NULL-pointers.patch b/SOURCES/0499-unit-make-UNIT-cast-function-deal-with-NULL-pointers.patch new file mode 100644 index 0000000..9f2143b --- /dev/null +++ b/SOURCES/0499-unit-make-UNIT-cast-function-deal-with-NULL-pointers.patch @@ -0,0 +1,31 @@ +From a11334f0eae67b5159a416193e2e37634281000a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Nov 2018 09:33:31 +0100 +Subject: [PATCH] unit: make UNIT() cast function deal with NULL pointers + +Fixes: #10681 +(cherry picked from commit bbf11206230d1b089118971f98a047151cb5c4fa) + +Related: #1871827 +--- + src/core/unit.h | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/core/unit.h b/src/core/unit.h +index 6e37fd6f5a..ec45b5fb48 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -597,7 +597,12 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; + } + + /* For casting the various unit types into a unit */ +-#define UNIT(u) (&(u)->meta) ++#define UNIT(u) \ ++ ({ \ ++ typeof(u) _u_ = (u); \ ++ Unit *_w_ = _u_ ? &(_u_)->meta : NULL; \ ++ _w_; \ ++ }) + + #define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0) + #define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0) diff --git a/SOURCES/0500-use-link-to-RHEL-8-docs.patch b/SOURCES/0500-use-link-to-RHEL-8-docs.patch new file mode 100644 index 0000000..ecc4968 --- /dev/null +++ b/SOURCES/0500-use-link-to-RHEL-8-docs.patch @@ -0,0 +1,25 @@ +From 6fb6c218fda0d5c3404049243b9392e9b0c7d537 Mon Sep 17 00:00:00 2001 +From: David Tardon +Date: Fri, 11 Dec 2020 09:34:19 +0100 +Subject: [PATCH] use link to RHEL-8 docs + +RHEL-only + +Related: #1623116 +--- + man/systemctl.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index 56f94d084c..ed60a0739f 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -2005,7 +2005,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + For examples how to use systemctl in comparsion + with old service and chkconfig command please see: +- ++ + Managing System Services + + diff --git a/SOURCES/0501-cgroup-Also-set-blkio.bfq.weight.patch b/SOURCES/0501-cgroup-Also-set-blkio.bfq.weight.patch new file mode 100644 index 0000000..88ad896 --- /dev/null +++ b/SOURCES/0501-cgroup-Also-set-blkio.bfq.weight.patch @@ -0,0 +1,37 @@ +From af9f03ba48dd75be8c6a923f70da9804b3a3a2c3 Mon Sep 17 00:00:00 2001 +From: Pavel Hrdina +Date: Wed, 25 Nov 2020 09:05:36 +0100 +Subject: [PATCH] cgroup: Also set blkio.bfq.weight + +Commit [1] added a workaround when unified cgroups are used but missed +legacy cgroups where there is the same issue. + +[1] + +Signed-off-by: Pavel Hrdina +(cherry picked from commit 35e7a62ca32a30169a94693b831e53c832251984) + +Resolves: #1657810 +--- + src/core/cgroup.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index f1ce070f9a..71e30fd4db 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -1063,6 +1063,14 @@ static void cgroup_context_apply( + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.weight: %m"); + ++ /* FIXME: drop this when distro kernels properly support BFQ through "blkio.weight" ++ * See also: https://github.com/systemd/systemd/pull/13335 */ ++ xsprintf(buf, "%" PRIu64 "\n", weight); ++ r = cg_set_attribute("blkio", path, "blkio.bfq.weight", buf); ++ if (r < 0) ++ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, ++ "Failed to set blkio.bfq.weight: %m"); ++ + if (has_io) { + CGroupIODeviceWeight *w; + diff --git a/SOURCES/0502-units-make-sure-initrd-cleanup.service-terminates-be.patch b/SOURCES/0502-units-make-sure-initrd-cleanup.service-terminates-be.patch new file mode 100644 index 0000000..3fdb46f --- /dev/null +++ b/SOURCES/0502-units-make-sure-initrd-cleanup.service-terminates-be.patch @@ -0,0 +1,37 @@ +From ea425381a675a2ce4d9519d534fe27c1012ac92e Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Mon, 28 Jan 2019 12:07:37 +0100 +Subject: [PATCH] units: make sure initrd-cleanup.service terminates before + switching to rootfs + +A follow-up for commit a8cb1dc3e0fa81aff. + +Commit a8cb1dc3e0fa81aff made sure that initrd-cleanup.service won't be stopped +when initrd-switch-root.target is isolated. + +However even with this change, it might happen that initrd-cleanup.service +survives the switch to rootfs (since it has no ordering constraints against +initrd-switch-root.target) and is stopped right after when default.target is +isolated. This led to initrd-cleanup.service entering in failed state as it +happens when oneshot services are stopped. + +This patch along with a8cb1dc3e0fa81aff should fix issue #4343. + +Fixes: #4343 +(cherry picked from commit e2c7c94ea35fe7e669afb51bfc2251158b522ea5) + +Related: #1657810 +--- + units/initrd-switch-root.target | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/units/initrd-switch-root.target b/units/initrd-switch-root.target +index ad82245121..ea4f02618f 100644 +--- a/units/initrd-switch-root.target ++++ b/units/initrd-switch-root.target +@@ -15,4 +15,4 @@ Requires=initrd-switch-root.service + Before=initrd-switch-root.service + AllowIsolate=yes + Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service initrd-cleanup.service +-After=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target emergency.service emergency.target ++After=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target emergency.service emergency.target initrd-cleanup.service diff --git a/SOURCES/0503-core-reload-SELinux-label-cache-on-daemon-reload.patch b/SOURCES/0503-core-reload-SELinux-label-cache-on-daemon-reload.patch new file mode 100644 index 0000000..5e5d3d4 --- /dev/null +++ b/SOURCES/0503-core-reload-SELinux-label-cache-on-daemon-reload.patch @@ -0,0 +1,73 @@ +From c67be1c7d69a0662ab85720aa0209110c39479f9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Wed, 27 Nov 2019 19:43:47 +0100 +Subject: [PATCH] core: reload SELinux label cache on daemon-reload + +Reloading the SELinux label cache here enables a light-wight follow-up of a SELinux policy change, e.g. adding a label for a RuntimeDirectory. + +Closes: #13363 +(cherry picked from commit a9dfac21ec850eb5dcaf1ae9ef729389e4c12802) + +Resolves: #1888912 +--- + src/basic/selinux-util.c | 20 ++++++++++++++++++++ + src/basic/selinux-util.h | 1 + + src/core/main.c | 2 ++ + 3 files changed, 23 insertions(+) + +diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c +index e15bd7e1fa..f69d88eb1e 100644 +--- a/src/basic/selinux-util.c ++++ b/src/basic/selinux-util.c +@@ -105,6 +105,26 @@ void mac_selinux_finish(void) { + #endif + } + ++void mac_selinux_reload(void) { ++ ++#if HAVE_SELINUX ++ struct selabel_handle *backup_label_hnd; ++ ++ if (!label_hnd) ++ return; ++ ++ backup_label_hnd = TAKE_PTR(label_hnd); ++ ++ /* try to initialize new handle ++ * on success close backup ++ * on failure restore backup */ ++ if (mac_selinux_init() == 0) ++ selabel_close(backup_label_hnd); ++ else ++ label_hnd = backup_label_hnd; ++#endif ++} ++ + int mac_selinux_fix(const char *path, LabelFixFlags flags) { + + #if HAVE_SELINUX +diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h +index 08314057fb..abcfabe777 100644 +--- a/src/basic/selinux-util.h ++++ b/src/basic/selinux-util.h +@@ -13,6 +13,7 @@ void mac_selinux_retest(void); + + int mac_selinux_init(void); + void mac_selinux_finish(void); ++void mac_selinux_reload(void); + + int mac_selinux_fix(const char *path, LabelFixFlags flags); + int mac_selinux_apply(const char *path, const char *label); +diff --git a/src/core/main.c b/src/core/main.c +index d897155644..d5c41da0c4 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1682,6 +1682,8 @@ static int invoke_main_loop( + saved_log_level = m->log_level_overridden ? log_get_max_level() : -1; + saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID; + ++ mac_selinux_reload(); ++ + (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); + + set_manager_defaults(m); diff --git a/SOURCES/0504-selinux-introduce-mac_selinux_create_file_prepare_at.patch b/SOURCES/0504-selinux-introduce-mac_selinux_create_file_prepare_at.patch new file mode 100644 index 0000000..4b51123 --- /dev/null +++ b/SOURCES/0504-selinux-introduce-mac_selinux_create_file_prepare_at.patch @@ -0,0 +1,140 @@ +From 4f4e8bbd9ad46fc146a36f52790bc4920f42ef1f Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Mon, 2 Jul 2018 10:22:56 +0200 +Subject: [PATCH] selinux: introduce mac_selinux_create_file_prepare_at() + +(cherry picked from commit 7e531a5265687aef5177b070c36ca4ceab42e768) + +Related: #1888912 +--- + src/basic/selinux-util.c | 83 ++++++++++++++++++++++++++++++---------- + src/basic/selinux-util.h | 1 + + 2 files changed, 63 insertions(+), 21 deletions(-) + +diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c +index f69d88eb1e..a078ce23ef 100644 +--- a/src/basic/selinux-util.c ++++ b/src/basic/selinux-util.c +@@ -336,48 +336,89 @@ char* mac_selinux_free(char *label) { + return NULL; + } + +-int mac_selinux_create_file_prepare(const char *path, mode_t mode) { +- + #if HAVE_SELINUX ++static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) { + _cleanup_freecon_ char *filecon = NULL; ++ _cleanup_free_ char *path = NULL; + int r; + +- assert(path); +- +- if (!label_hnd) +- return 0; +- +- if (path_is_absolute(path)) +- r = selabel_lookup_raw(label_hnd, &filecon, path, mode); +- else { +- _cleanup_free_ char *newpath = NULL; +- +- r = path_make_absolute_cwd(path, &newpath); +- if (r < 0) +- return r; +- +- r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode); +- } ++ assert(abspath); ++ assert(path_is_absolute(abspath)); + ++ r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode); + if (r < 0) { + /* No context specified by the policy? Proceed without setting it. */ + if (errno == ENOENT) + return 0; + +- log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path); ++ log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath); + } else { + if (setfscreatecon_raw(filecon) >= 0) + return 0; /* Success! */ + +- log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, path); ++ log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath); + } + + if (security_getenforce() > 0) + return -errno; + +-#endif + return 0; + } ++#endif ++ ++int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) { ++ int r = 0; ++ ++#if HAVE_SELINUX ++ _cleanup_free_ char *abspath = NULL; ++ _cleanup_close_ int fd = -1; ++ ++ assert(path); ++ ++ if (!label_hnd) ++ return 0; ++ ++ if (!path_is_absolute(path)) { ++ _cleanup_free_ char *p = NULL; ++ ++ if (dirfd == AT_FDCWD) ++ r = safe_getcwd(&p); ++ else ++ r = fd_get_path(dirfd, &p); ++ if (r < 0) ++ return r; ++ ++ abspath = path_join(NULL, p, path); ++ if (!abspath) ++ return -ENOMEM; ++ ++ path = abspath; ++ } ++ ++ r = selinux_create_file_prepare_abspath(path, mode); ++#endif ++ return r; ++} ++ ++int mac_selinux_create_file_prepare(const char *path, mode_t mode) { ++ int r = 0; ++ ++#if HAVE_SELINUX ++ _cleanup_free_ char *abspath = NULL; ++ ++ assert(path); ++ ++ if (!label_hnd) ++ return 0; ++ ++ r = path_make_absolute_cwd(path, &abspath); ++ if (r < 0) ++ return r; ++ ++ r = selinux_create_file_prepare_abspath(abspath, mode); ++#endif ++ return r; ++} + + void mac_selinux_create_file_clear(void) { + +diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h +index abcfabe777..639c35b687 100644 +--- a/src/basic/selinux-util.h ++++ b/src/basic/selinux-util.h +@@ -24,6 +24,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * + char* mac_selinux_free(char *label); + + int mac_selinux_create_file_prepare(const char *path, mode_t mode); ++int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode); + void mac_selinux_create_file_clear(void); + + int mac_selinux_create_socket_prepare(const char *label); diff --git a/SOURCES/0505-selinux-add-trigger-for-policy-reload-to-refresh-int.patch b/SOURCES/0505-selinux-add-trigger-for-policy-reload-to-refresh-int.patch new file mode 100644 index 0000000..4bba960 --- /dev/null +++ b/SOURCES/0505-selinux-add-trigger-for-policy-reload-to-refresh-int.patch @@ -0,0 +1,135 @@ +From 4e48673172b012a06575e4f5b681d3554eded2e2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= +Date: Mon, 6 Jan 2020 15:27:23 +0100 +Subject: [PATCH] selinux: add trigger for policy reload to refresh internal + selabel cache + +Reload the internal selabel cache automatically on SELinux policy reloads so non pid-1 daemons are participating. + +Run the reload function `mac_selinux_reload()` not manually on daemon-reload, but rather pass it as callback to libselinux. +Trigger the callback prior usage of the systemd internal selabel cache by depleting the selinux netlink socket via `avc_netlink_check_nb()`. + +Improves: a9dfac21ec85 ("core: reload SELinux label cache on daemon-reload") +Improves: #13363 +(cherry picked from commit 61f3e897f13101f29fb8027e8839498a469ad58e) + +Related: #1888912 +--- + src/basic/selinux-util.c | 23 +++++++++++++++++++---- + src/basic/selinux-util.h | 1 - + src/core/main.c | 2 -- + 3 files changed, 19 insertions(+), 7 deletions(-) + +diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c +index a078ce23ef..bfe3d015aa 100644 +--- a/src/basic/selinux-util.c ++++ b/src/basic/selinux-util.c +@@ -10,6 +10,7 @@ + #include + + #if HAVE_SELINUX ++#include + #include + #include + #include +@@ -32,6 +33,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free); + #define _cleanup_freecon_ _cleanup_(freeconp) + #define _cleanup_context_free_ _cleanup_(context_freep) + ++static int mac_selinux_reload(int seqno); ++ + static int cached_use = -1; + static struct selabel_handle *label_hnd = NULL; + +@@ -63,6 +66,8 @@ int mac_selinux_init(void) { + usec_t before_timestamp, after_timestamp; + struct mallinfo before_mallinfo, after_mallinfo; + ++ selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload); ++ + if (label_hnd) + return 0; + +@@ -105,13 +110,12 @@ void mac_selinux_finish(void) { + #endif + } + +-void mac_selinux_reload(void) { +- + #if HAVE_SELINUX ++static int mac_selinux_reload(int seqno) { + struct selabel_handle *backup_label_hnd; + + if (!label_hnd) +- return; ++ return 0; + + backup_label_hnd = TAKE_PTR(label_hnd); + +@@ -122,8 +126,10 @@ void mac_selinux_reload(void) { + selabel_close(backup_label_hnd); + else + label_hnd = backup_label_hnd; +-#endif ++ ++ return 0; + } ++#endif + + int mac_selinux_fix(const char *path, LabelFixFlags flags) { + +@@ -152,6 +158,9 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) { + if (fstat(fd, &st) < 0) + return -errno; + ++ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ ++ (void) avc_netlink_check_nb(); ++ + if (selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode) < 0) { + r = -errno; + +@@ -345,6 +354,9 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) + assert(abspath); + assert(path_is_absolute(abspath)); + ++ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ ++ (void) avc_netlink_check_nb(); ++ + r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode); + if (r < 0) { + /* No context specified by the policy? Proceed without setting it. */ +@@ -496,6 +508,9 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { + + path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path)); + ++ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */ ++ (void) avc_netlink_check_nb(); ++ + if (path_is_absolute(path)) + r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK); + else { +diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h +index 639c35b687..bd5207c318 100644 +--- a/src/basic/selinux-util.h ++++ b/src/basic/selinux-util.h +@@ -13,7 +13,6 @@ void mac_selinux_retest(void); + + int mac_selinux_init(void); + void mac_selinux_finish(void); +-void mac_selinux_reload(void); + + int mac_selinux_fix(const char *path, LabelFixFlags flags); + int mac_selinux_apply(const char *path, const char *label); +diff --git a/src/core/main.c b/src/core/main.c +index d5c41da0c4..d897155644 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1682,8 +1682,6 @@ static int invoke_main_loop( + saved_log_level = m->log_level_overridden ? log_get_max_level() : -1; + saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID; + +- mac_selinux_reload(); +- + (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); + + set_manager_defaults(m); diff --git a/SOURCES/0506-udev-net_id-give-RHEL-8.4-naming-scheme-a-name.patch b/SOURCES/0506-udev-net_id-give-RHEL-8.4-naming-scheme-a-name.patch new file mode 100644 index 0000000..0ce9150 --- /dev/null +++ b/SOURCES/0506-udev-net_id-give-RHEL-8.4-naming-scheme-a-name.patch @@ -0,0 +1,24 @@ +From fb58a56c6c1c2749ba634abd9ad76f4e718269a1 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Tue, 5 Jan 2021 12:30:15 +0100 +Subject: [PATCH] udev/net_id: give RHEL-8.4 naming scheme a name + +Follow-up for bb6114af097da0cd9c5081e42db718559130687f + +Related: #1827462 +--- + src/udev/udev-builtin-net_id.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c +index d8c56b62bb..7c153f0aef 100644 +--- a/src/udev/udev-builtin-net_id.c ++++ b/src/udev/udev-builtin-net_id.c +@@ -150,6 +150,7 @@ static const NamingScheme naming_schemes[] = { + { "rhel-8.1", NAMING_RHEL_8_1 }, + { "rhel-8.2", NAMING_RHEL_8_2 }, + { "rhel-8.3", NAMING_RHEL_8_3 }, ++ { "rhel-8.4", NAMING_RHEL_8_4 }, + /* … add more schemes here, as the logic to name devices is updated … */ + }; + diff --git a/SOURCES/0507-basic-stat-util-make-mtime-check-stricter-and-use-en.patch b/SOURCES/0507-basic-stat-util-make-mtime-check-stricter-and-use-en.patch new file mode 100644 index 0000000..e9ac657 --- /dev/null +++ b/SOURCES/0507-basic-stat-util-make-mtime-check-stricter-and-use-en.patch @@ -0,0 +1,63 @@ +From 29c5b8dd6228c4401f034ca0aa85f99ac42cf8dd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= +Date: Thu, 5 Nov 2020 17:55:25 +0100 +Subject: [PATCH] basic/stat-util: make mtime check stricter and use entire + timestamp + +Note that st_mtime member of struct stat is defined as follows, + + #define st_mtime st_mtim.tv_sec + +Hence we omitted checking nanosecond part of the timestamp (struct +timespec) and possibly would miss modifications that happened within the +same second. + +(cherry picked from commit a59b0a9f768f6e27b25f4f1bab6de08842e78d74) + +Related: #1642728 +--- + src/basic/stat-util.c | 22 ++++++++++++++++++++++ + src/basic/stat-util.h | 2 ++ + 2 files changed, 24 insertions(+) + +diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c +index 26aee9bad6..c61c4c0517 100644 +--- a/src/basic/stat-util.c ++++ b/src/basic/stat-util.c +@@ -287,3 +287,25 @@ int fd_verify_regular(int fd) { + + return stat_verify_regular(&st); + } ++ ++bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { ++ ++ /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to ++ * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file ++ * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file ++ * size, backing device, inode type and if this refers to a device not the major/minor. ++ * ++ * Note that we don't care if file attributes such as ownership or access mode change, this here is ++ * about contents of the file. The purpose here is to detect file contents changes, and nothing ++ * else. */ ++ ++ return a && b && ++ (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ ++ ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ ++ a->st_mtim.tv_sec == b->st_mtim.tv_sec && ++ a->st_mtim.tv_nsec == b->st_mtim.tv_nsec && ++ (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ ++ a->st_dev == b->st_dev && ++ a->st_ino == b->st_ino && ++ (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ ++} +\ No newline at end of file +diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h +index f8014ed30b..9e1a2b70da 100644 +--- a/src/basic/stat-util.h ++++ b/src/basic/stat-util.h +@@ -58,3 +58,5 @@ int path_is_temporary_fs(const char *path); + + int stat_verify_regular(const struct stat *st); + int fd_verify_regular(int fd); ++ ++bool stat_inode_unmodified(const struct stat *a, const struct stat *b); diff --git a/SOURCES/0508-udev-make-algorithm-that-selects-highest-priority-de.patch b/SOURCES/0508-udev-make-algorithm-that-selects-highest-priority-de.patch new file mode 100644 index 0000000..e20137b --- /dev/null +++ b/SOURCES/0508-udev-make-algorithm-that-selects-highest-priority-de.patch @@ -0,0 +1,457 @@ +From 1d5f966c1758eb620755fcae54abd07a1ac36d3d Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +Date: Wed, 6 Jan 2021 11:43:50 +0100 +Subject: [PATCH] udev: make algorithm that selects highest priority devlink + less susceptible to race conditions + +Previously it was very likely, when multiple contenders for the symlink +appear in parallel, that algorithm would select wrong symlink (i.e. one +with lower-priority). + +Now the algorithm is much more defensive and when we detect change in +set of contenders for the symlink we reevaluate the selection. Same +happens when new symlink replaces already existing symlink that points +to different device node. + +Resolves: #1642728 +--- + src/udev/udev-event.c | 71 +++++++----- + src/udev/udev-node.c | 244 ++++++++++++++++++++++++++++++------------ + 2 files changed, 216 insertions(+), 99 deletions(-) + +diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c +index fd8406d959..9004634f65 100644 +--- a/src/udev/udev-event.c ++++ b/src/udev/udev-event.c +@@ -833,6 +833,41 @@ static int rename_netif(struct udev_event *event) { + return 0; + } + ++static void update_devnode(struct udev_event *event) { ++ struct udev_device *dev = event->dev; ++ ++ if (major(udev_device_get_devnum(dev)) > 0) { ++ bool apply; ++ ++ /* remove/update possible left-over symlinks from old database entry */ ++ if (event->dev_db != NULL) ++ udev_node_update_old_links(dev, event->dev_db); ++ ++ if (!event->owner_set) ++ event->uid = udev_device_get_devnode_uid(dev); ++ ++ if (!event->group_set) ++ event->gid = udev_device_get_devnode_gid(dev); ++ ++ if (!event->mode_set) { ++ if (udev_device_get_devnode_mode(dev) > 0) { ++ /* kernel supplied value */ ++ event->mode = udev_device_get_devnode_mode(dev); ++ } else if (event->gid > 0) { ++ /* default 0660 if a group is assigned */ ++ event->mode = 0660; ++ } ++ else { ++ /* default 0600 */ ++ event->mode = 0600; ++ } ++ } ++ ++ apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set; ++ udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list); ++ } ++} ++ + void udev_event_execute_rules(struct udev_event *event, + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list, +@@ -891,35 +926,7 @@ void udev_event_execute_rules(struct udev_event *event, + } + } + +- if (major(udev_device_get_devnum(dev)) > 0) { +- bool apply; +- +- /* remove/update possible left-over symlinks from old database entry */ +- if (event->dev_db != NULL) +- udev_node_update_old_links(dev, event->dev_db); +- +- if (!event->owner_set) +- event->uid = udev_device_get_devnode_uid(dev); +- +- if (!event->group_set) +- event->gid = udev_device_get_devnode_gid(dev); +- +- if (!event->mode_set) { +- if (udev_device_get_devnode_mode(dev) > 0) { +- /* kernel supplied value */ +- event->mode = udev_device_get_devnode_mode(dev); +- } else if (event->gid > 0) { +- /* default 0660 if a group is assigned */ +- event->mode = 0660; +- } else { +- /* default 0600 */ +- event->mode = 0600; +- } +- } +- +- apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set; +- udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list); +- } ++ update_devnode(event); + + /* preserve old, or get new initialization timestamp */ + udev_device_ensure_usec_initialized(event->dev, event->dev_db); +@@ -927,6 +934,12 @@ void udev_event_execute_rules(struct udev_event *event, + /* (re)write database file */ + udev_device_tag_index(dev, event->dev_db, true); + udev_device_update_db(dev); ++ ++ /* Yes, we run update_devnode() twice, because in the first invocation, that is before update of udev database, ++ * it could happen that two contenders are replacing each other's symlink. Hence we run it again to make sure ++ * symlinks point to devices that claim them with the highest priority. */ ++ update_devnode(event); ++ + udev_device_set_is_initialized(dev); + + event->dev_db = udev_device_unref(event->dev_db); +diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c +index 333dcae6b9..2eeeccdd3a 100644 +--- a/src/udev/udev-node.c ++++ b/src/udev/udev-node.c +@@ -13,19 +13,27 @@ + #include + + #include "device-nodes.h" ++#include "device-private.h" + #include "dirent-util.h" ++#include "fd-util.h" + #include "format-util.h" + #include "fs-util.h" ++#include "sd-device.h" + #include "selinux-util.h" + #include "smack-util.h" ++#include "stat-util.h" + #include "stdio-util.h" + #include "string-util.h" + #include "udev.h" ++#include "libudev-device-internal.h" + +-static int node_symlink(struct udev_device *dev, const char *node, const char *slink) { ++#define LINK_UPDATE_MAX_RETRIES 128 ++ ++static int node_symlink(sd_device *dev, const char *node, const char *slink) { + struct stat stats; + char target[UTIL_PATH_SIZE]; + char *s; ++ const char *id_filename; + size_t l; + char slink_tmp[UTIL_PATH_SIZE + 32]; + int i = 0; +@@ -89,7 +97,10 @@ static int node_symlink(struct udev_device *dev, const char *node, const char *s + } + + log_debug("atomically replace '%s'", slink); +- strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL); ++ err = device_get_id_filename(dev, &id_filename); ++ if (err < 0) ++ return log_error_errno(err, "Failed to get id_filename: %m"); ++ strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", id_filename, NULL); + unlink(slink_tmp); + do { + err = mkdir_parents_label(slink_tmp, 0755); +@@ -109,104 +120,187 @@ static int node_symlink(struct udev_device *dev, const char *node, const char *s + if (err != 0) { + log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink); + unlink(slink_tmp); +- } ++ } else ++ /* Tell caller that we replaced already existing symlink. */ ++ return 1; + exit: + return err; + } + + /* find device node of device with highest priority */ +-static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) { +- struct udev *udev = udev_device_get_udev(dev); +- DIR *dir; ++static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) { ++ _cleanup_closedir_ DIR *dir = NULL; ++ _cleanup_free_ char *target = NULL; + struct dirent *dent; +- int priority = 0; +- const char *target = NULL; ++ int r, priority = 0; ++ ++ assert(!add || dev); ++ assert(stackdir); ++ assert(ret); + + if (add) { +- priority = udev_device_get_devlink_priority(dev); +- strscpy(buf, bufsize, udev_device_get_devnode(dev)); +- target = buf; ++ const char *devnode; ++ ++ r = device_get_devlink_priority(dev, &priority); ++ if (r < 0) ++ return r; ++ ++ r = sd_device_get_devname(dev, &devnode); ++ if (r < 0) ++ return r; ++ ++ target = strdup(devnode); ++ if (!target) ++ return -ENOMEM; + } + + dir = opendir(stackdir); +- if (dir == NULL) +- return target; ++ if (!dir) { ++ if (target) { ++ *ret = TAKE_PTR(target); ++ return 0; ++ } ++ ++ return -errno; ++ } ++ + FOREACH_DIRENT_ALL(dent, dir, break) { +- struct udev_device *dev_db; ++ _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL; ++ const char *devnode, *id_filename; ++ int db_prio = 0; + + if (dent->d_name[0] == '\0') + break; + if (dent->d_name[0] == '.') + continue; + +- log_debug("found '%s' claiming '%s'", dent->d_name, stackdir); ++ log_debug("Found '%s' claiming '%s'", dent->d_name, stackdir); ++ ++ if (device_get_id_filename(dev, &id_filename) < 0) ++ continue; + + /* did we find ourself? */ +- if (streq(dent->d_name, udev_device_get_id_filename(dev))) ++ if (streq(dent->d_name, id_filename)) + continue; + +- dev_db = udev_device_new_from_device_id(udev, dent->d_name); +- if (dev_db != NULL) { +- const char *devnode; +- +- devnode = udev_device_get_devnode(dev_db); +- if (devnode != NULL) { +- if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) { +- log_debug("'%s' claims priority %i for '%s'", +- udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir); +- priority = udev_device_get_devlink_priority(dev_db); +- strscpy(buf, bufsize, devnode); +- target = buf; +- } +- } +- udev_device_unref(dev_db); ++ if (sd_device_new_from_device_id(&dev_db, dent->d_name) < 0) ++ continue; ++ ++ if (sd_device_get_devname(dev_db, &devnode) < 0) ++ continue; ++ ++ if (device_get_devlink_priority(dev_db, &db_prio) < 0) ++ continue; ++ ++ if (target && db_prio <= priority) ++ continue; ++ ++ if (DEBUG_LOGGING) { ++ const char *syspath = NULL; ++ ++ (void) sd_device_get_syspath(dev_db, &syspath); ++ log_debug("Device '%s' claims priority %i for '%s'", strnull(syspath), db_prio, stackdir); + } ++ ++ r = free_and_strdup(&target, devnode); ++ if (r < 0) ++ return r; ++ priority = db_prio; + } +- closedir(dir); +- return target; ++ ++ if (!target) ++ return -ENOENT; ++ ++ *ret = TAKE_PTR(target); ++ return 0; + } + ++ + /* manage "stack of names" with possibly specified device priorities */ +-static void link_update(struct udev_device *dev, const char *slink, bool add) { +- char name_enc[UTIL_PATH_SIZE]; +- char filename[UTIL_PATH_SIZE * 2]; +- char dirname[UTIL_PATH_SIZE]; +- const char *target; +- char buf[UTIL_PATH_SIZE]; ++static int link_update(sd_device *dev, const char *slink, bool add) { ++ _cleanup_free_ char *filename = NULL, *dirname = NULL; ++ char name_enc[PATH_MAX]; ++ const char *id_filename; ++ int i, r, retries; ++ ++ assert(dev); ++ assert(slink); ++ ++ r = device_get_id_filename(dev, &id_filename); ++ if (r < 0) ++ return log_debug_errno(r, "Failed to get id_filename: %m"); + + util_path_encode(slink + STRLEN("/dev"), name_enc, sizeof(name_enc)); +- strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL); +- strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL); ++ dirname = path_join(NULL, "/run/udev/links/", name_enc); ++ if (!dirname) ++ return log_oom(); ++ filename = path_join(NULL, dirname, id_filename); ++ if (!filename) ++ return log_oom(); ++ ++ if (!add) { ++ if (unlink(filename) == 0) ++ (void) rmdir(dirname); ++ } else ++ for (;;) { ++ _cleanup_close_ int fd = -1; ++ ++ r = mkdir_parents(filename, 0755); ++ if (!IN_SET(r, 0, -ENOENT)) ++ return r; + +- if (!add && unlink(filename) == 0) +- rmdir(dirname); ++ fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); ++ if (fd >= 0) ++ break; ++ if (errno != ENOENT) ++ return -errno; ++ } + +- target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf)); +- if (target == NULL) { +- log_debug("no reference left, remove '%s'", slink); +- if (unlink(slink) == 0) +- rmdir_parents(slink, "/"); +- } else { +- log_debug("creating link '%s' to '%s'", slink, target); +- node_symlink(dev, target, slink); +- } ++ /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink ++ * will be fixed in the second invocation. */ ++ (void) sd_device_get_is_initialized(dev, &r); ++ retries = r > 0 ? LINK_UPDATE_MAX_RETRIES : 1; + +- if (add) { +- int err; ++ for (i = 0; i < retries; i++) { ++ _cleanup_free_ char *target = NULL; ++ struct stat st1 = {}, st2 = {}; + +- do { +- int fd; ++ r = stat(dirname, &st1); ++ if (r < 0 && errno != ENOENT) ++ return -errno; + +- err = mkdir_parents(filename, 0755); +- if (!IN_SET(err, 0, -ENOENT)) ++ r = link_find_prioritized(dev, add, dirname, &target); ++ if (r == -ENOENT) { ++ log_debug("No reference left, removing '%s'", slink); ++ if (unlink(slink) == 0) ++ (void) rmdir_parents(slink, "/"); ++ ++ break; ++ } else if (r < 0) ++ return log_error_errno(r, "Failed to determine highest priority symlink: %m"); ++ ++ r = node_symlink(dev, target, slink); ++ if (r < 0) { ++ (void) unlink(filename); ++ break; ++ } else if (r == 1) ++ /* We have replaced already existing symlink, possibly there is some other device trying ++ * to claim the same symlink. Let's do one more iteration to give us a chance to fix ++ * the error if other device actually claims the symlink with higher priority. */ ++ continue; ++ ++ /* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */ ++ if ((st1.st_mode & S_IFMT) != 0) { ++ r = stat(dirname, &st2); ++ if (r < 0 && errno != ENOENT) ++ return -errno; ++ ++ if (stat_inode_unmodified(&st1, &st2)) + break; +- fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444); +- if (fd >= 0) +- close(fd); +- else +- err = -errno; +- } while (err == -ENOENT); ++ } + } ++ ++ return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP; + } + + void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) { +@@ -233,7 +327,7 @@ void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev + + log_debug("update old name, '%s' no longer belonging to '%s'", + name, udev_device_get_devpath(dev)); +- link_update(dev, name, false); ++ link_update(dev->device, name, false); + } + } + +@@ -338,11 +432,16 @@ void udev_node_add(struct udev_device *dev, bool apply, + xsprintf_dev_num_path(filename, + streq(udev_device_get_subsystem(dev), "block") ? "block" : "char", + udev_device_get_devnum(dev)); +- node_symlink(dev, udev_device_get_devnode(dev), filename); ++ node_symlink(dev->device, udev_device_get_devnode(dev), filename); + + /* create/update symlinks, add symlinks to name index */ +- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) +- link_update(dev, udev_list_entry_get_name(list_entry), true); ++ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { ++ int r; ++ ++ r = link_update(dev->device, udev_list_entry_get_name(list_entry), true); ++ if (r < 0) ++ log_info_errno(r, "Failed to update device symlinks: %m"); ++ } + } + + void udev_node_remove(struct udev_device *dev) { +@@ -350,8 +449,13 @@ void udev_node_remove(struct udev_device *dev) { + char filename[DEV_NUM_PATH_MAX]; + + /* remove/update symlinks, remove symlinks from name index */ +- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) +- link_update(dev, udev_list_entry_get_name(list_entry), false); ++ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) { ++ int r; ++ ++ r = link_update(dev->device, udev_list_entry_get_name(list_entry), false); ++ if (r < 0) ++ log_info_errno(r, "Failed to update device symlinks: %m"); ++ } + + /* remove /dev/{block,char}/$major:$minor */ + xsprintf_dev_num_path(filename, diff --git a/SOURCES/0509-test-create-dev-null-in-test-udev.pl.patch b/SOURCES/0509-test-create-dev-null-in-test-udev.pl.patch new file mode 100644 index 0000000..4dccdbc --- /dev/null +++ b/SOURCES/0509-test-create-dev-null-in-test-udev.pl.patch @@ -0,0 +1,32 @@ +From 6a908a38135d050b7c271fdea9c061d7e7ad8ef7 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 23 Oct 2018 07:23:01 +0900 +Subject: [PATCH] test: create /dev/null in test-udev.pl + +(cherry picked from commit a41ff38b0999fb83464309a29b8f39450b8d4b85) + +Related: #1642728 +--- + test/udev-test.pl | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 0433629c7c..a1c24f49b4 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1537,13 +1537,14 @@ sub udev_setup { + system("umount", $udev_tmpfs); + rmdir($udev_tmpfs); + mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n"; +- system("mount", "-o", "rw,mode=755,nosuid,noexec,nodev", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs"; ++ system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs"; + + mkdir($udev_dev) || die "unable to create udev_dev: $udev_dev\n"; + # setting group and mode of udev_dev ensures the tests work + # even if the parent directory has setgid bit enabled. + chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n"; + chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n"; ++ system("mknod", $udev_dev . "/null", "c", "1", "3") && "unable to create $udev_dev/null"; + + system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys"; + diff --git a/SOURCES/0510-test-missing-die.patch b/SOURCES/0510-test-missing-die.patch new file mode 100644 index 0000000..959a689 --- /dev/null +++ b/SOURCES/0510-test-missing-die.patch @@ -0,0 +1,27 @@ +From 70bf708d5360372aa541e25ff512609230781dd6 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 7 Nov 2018 14:56:20 +0900 +Subject: [PATCH] test: missing "die" + +Follow-up for a41ff38b0999fb83464309a29b8f39450b8d4b85. + +(cherry picked from commit 11d93952ea806de2b6e9fb381153115cccc7b5e8) + +Related: #1642728 +--- + test/udev-test.pl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index a1c24f49b4..61bd3d703a 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1544,7 +1544,7 @@ sub udev_setup { + # even if the parent directory has setgid bit enabled. + chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n"; + chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n"; +- system("mknod", $udev_dev . "/null", "c", "1", "3") && "unable to create $udev_dev/null"; ++ system("mknod", $udev_dev . "/null", "c", "1", "3") && die "unable to create $udev_dev/null"; + + system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys"; + diff --git a/SOURCES/0511-udev-test-remove-a-check-for-whether-the-test-is-run.patch b/SOURCES/0511-udev-test-remove-a-check-for-whether-the-test-is-run.patch new file mode 100644 index 0000000..eeadbda --- /dev/null +++ b/SOURCES/0511-udev-test-remove-a-check-for-whether-the-test-is-run.patch @@ -0,0 +1,33 @@ +From 1b133f2ca15f0a15b05407b2c04521d7de88dfa2 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 9 Nov 2018 03:14:04 +0100 +Subject: [PATCH] udev-test: remove a check for whether the test is run in a + container + +It's too broad a check that prevents the test from running on Travis CI. + +(cherry picked from commit 881886ef08d50951159633248b0f73977c5d6924) + +Related: #1642728 +--- + test/udev-test.pl | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 61bd3d703a..05b3e17188 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1646,13 +1646,6 @@ if ($? >> 8 == 0) { + exit($EXIT_TEST_SKIP); + } + +-# skip the test when running in a container +-system("systemd-detect-virt", "-c", "-q"); +-if ($? >> 8 == 0) { +- print "Running in a container, skipping the test.\n"; +- exit($EXIT_TEST_SKIP); +-} +- + udev_setup(); + + my $test_num = 1; diff --git a/SOURCES/0512-udev-test-skip-the-test-only-if-it-can-t-setup-its-e.patch b/SOURCES/0512-udev-test-skip-the-test-only-if-it-can-t-setup-its-e.patch new file mode 100644 index 0000000..37970ab --- /dev/null +++ b/SOURCES/0512-udev-test-skip-the-test-only-if-it-can-t-setup-its-e.patch @@ -0,0 +1,94 @@ +From 8c82f3a4aa2d029dcc303cbf95a71194aa5ac9c3 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Fri, 9 Nov 2018 04:01:15 +0100 +Subject: [PATCH] udev-test: skip the test only if it can't setup its + environment + +This is basically a replacement for 0eb3cc88504b5d8f74. + +(cherry picked from commit 110a13202eab6d92678abcde08372d4afac1cc45) + +Related: #1642728 +--- + src/test/test-udev.c | 8 ++++++++ + test/udev-test.pl | 24 +++++++++++++++++++++--- + 2 files changed, 29 insertions(+), 3 deletions(-) + +diff --git a/src/test/test-udev.c b/src/test/test-udev.c +index bed51c1270..f098fab721 100644 +--- a/src/test/test-udev.c ++++ b/src/test/test-udev.c +@@ -65,6 +65,11 @@ int main(int argc, char *argv[]) { + log_parse_environment(); + log_open(); + ++ if (!IN_SET(argc, 2, 3)) { ++ log_error("This program needs one or two arguments, %d given", argc - 1); ++ return EXIT_FAILURE; ++ } ++ + err = fake_filesystems(); + if (err < 0) + return EXIT_FAILURE; +@@ -73,6 +78,9 @@ int main(int argc, char *argv[]) { + if (udev == NULL) + return EXIT_FAILURE; + ++ if (argc == 2) ++ return EXIT_SUCCESS; ++ + log_debug("version %s", PACKAGE_VERSION); + mac_selinux_init(); + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 05b3e17188..aa38bae0b1 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1537,18 +1537,28 @@ sub udev_setup { + system("umount", $udev_tmpfs); + rmdir($udev_tmpfs); + mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n"; +- system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs"; ++ ++ if (system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs)) { ++ warn "unable to mount tmpfs"; ++ return 0; ++ } + + mkdir($udev_dev) || die "unable to create udev_dev: $udev_dev\n"; + # setting group and mode of udev_dev ensures the tests work + # even if the parent directory has setgid bit enabled. + chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n"; + chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n"; +- system("mknod", $udev_dev . "/null", "c", "1", "3") && die "unable to create $udev_dev/null"; ++ ++ if (system("mknod", $udev_dev . "/null", "c", "1", "3")) { ++ warn "unable to create $udev_dev/null"; ++ return 0; ++ } + + system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys"; + + system("rm", "-rf", "$udev_run"); ++ ++ return 1; + } + + sub run_test { +@@ -1646,7 +1656,15 @@ if ($? >> 8 == 0) { + exit($EXIT_TEST_SKIP); + } + +-udev_setup(); ++if (!udev_setup()) { ++ warn "Failed to set up the environment, skipping the test"; ++ exit($EXIT_TEST_SKIP); ++} ++ ++if (!system($udev_bin, "check")) { ++ warn "$udev_bin failed to set up the environment, skipping the test"; ++ exit($EXIT_TEST_SKIP); ++} + + my $test_num = 1; + my @list; diff --git a/SOURCES/0513-udev-test-fix-test-skip-condition.patch b/SOURCES/0513-udev-test-fix-test-skip-condition.patch new file mode 100644 index 0000000..165aadb --- /dev/null +++ b/SOURCES/0513-udev-test-fix-test-skip-condition.patch @@ -0,0 +1,33 @@ +From f44fcdde656036f0388fc8244b8960c1873a3a08 Mon Sep 17 00:00:00 2001 +From: Alexey Bogdanenko +Date: Sat, 8 Dec 2018 11:02:30 +0300 +Subject: [PATCH] udev-test: fix test skip condition + +When there is a failure to setup the environment, the following happens: + +1. Command "./test-udev check" exits with non-zero code. +2. Perl function "system" returns the code. +3. The code is evaluated as true by Perl. + +Then we stop the test. + +(cherry picked from commit 7935dae547caf164d807237f1009a9e9fa510337) + +Related: #1642728 +--- + test/udev-test.pl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index aa38bae0b1..3517feab15 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1661,7 +1661,7 @@ if (!udev_setup()) { + exit($EXIT_TEST_SKIP); + } + +-if (!system($udev_bin, "check")) { ++if (system($udev_bin, "check")) { + warn "$udev_bin failed to set up the environment, skipping the test"; + exit($EXIT_TEST_SKIP); + } diff --git a/SOURCES/0514-udev-test-fix-missing-directory-test-run.patch b/SOURCES/0514-udev-test-fix-missing-directory-test-run.patch new file mode 100644 index 0000000..5714a16 --- /dev/null +++ b/SOURCES/0514-udev-test-fix-missing-directory-test-run.patch @@ -0,0 +1,35 @@ +From 974431a70775d5127cd973c4b4705d2cf8884011 Mon Sep 17 00:00:00 2001 +From: Alexey Bogdanenko +Date: Sat, 8 Dec 2018 15:35:30 +0300 +Subject: [PATCH] udev-test: fix missing directory test/run + +Fixes the following error: + + Failed to mount test /run: No such file or directory + +By the time command "./test-udev check" calls function "fake_filesystems", +directory "test/run" must be present. + +(cherry picked from commit 1e5548c0e0962424b6ca5fdfd35c866b70760c8f) + +Related: #1642728 +--- + test/udev-test.pl | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 3517feab15..eb76ebd72e 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1558,6 +1558,11 @@ sub udev_setup { + + system("rm", "-rf", "$udev_run"); + ++ if (!mkdir($udev_run)) { ++ warn "unable to create directory $udev_run"; ++ return 0; ++ } ++ + return 1; + } + diff --git a/SOURCES/0515-udev-test-check-if-permitted-to-create-block-device-.patch b/SOURCES/0515-udev-test-check-if-permitted-to-create-block-device-.patch new file mode 100644 index 0000000..67037a2 --- /dev/null +++ b/SOURCES/0515-udev-test-check-if-permitted-to-create-block-device-.patch @@ -0,0 +1,31 @@ +From 57e9ee0f19098d56995955f6692437affdf94041 Mon Sep 17 00:00:00 2001 +From: Alexey Bogdanenko +Date: Tue, 11 Dec 2018 16:55:34 +0300 +Subject: [PATCH] udev-test: check if permitted to create block device nodes + +(cherry picked from commit dbfbc6c4e34366033cb340e8b0c3cbca683ff6f5) + +Related: #1642728 +--- + test/udev-test.pl | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index eb76ebd72e..957cda541c 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1554,6 +1554,14 @@ sub udev_setup { + return 0; + } + ++ # check if we are permitted to create block device nodes ++ my $block_device_filename = $udev_dev . "/sda"; ++ if (system("mknod", $block_device_filename, "b", "8", "0")) { ++ warn "unable to create $block_device_filename"; ++ return 0; ++ } ++ unlink $block_device_filename; ++ + system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys"; + + system("rm", "-rf", "$udev_run"); diff --git a/SOURCES/0516-test-udev-add-a-testcase-of-too-long-line.patch b/SOURCES/0516-test-udev-add-a-testcase-of-too-long-line.patch new file mode 100644 index 0000000..7f712d8 --- /dev/null +++ b/SOURCES/0516-test-udev-add-a-testcase-of-too-long-line.patch @@ -0,0 +1,45 @@ +From 527d43064a93fae9a4490e5d152b120e91f5eade Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 18 Feb 2019 10:38:29 +0900 +Subject: [PATCH] test-udev: add a testcase of too long line + +(cherry picked from commit 1e797cf596df50a6bdd8cbf8e9b2467a3a934171) + +Related: #1642728 +--- + test/udev-test.pl | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 957cda541c..3a50694fa9 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -39,6 +39,11 @@ for (my $i = 1; $i <= 10000; ++$i) { + $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n"; + } + ++my $rules_10k_tags_continuation = ""; ++for (my $i = 1; $i <= 10000; ++$i) { ++ $rules_10k_tags_continuation .= 'KERNEL=="sda", TAG+="test' . $i . "\"\\\n"; ++} ++ + my @tests = ( + { + desc => "no rules", +@@ -1444,6 +1449,16 @@ EOF + exp_name => "found", + rules => $rules_10k_tags . < "don't crash with lots of tags with continuation", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad" , ++ rules => $rules_10k_tags_continuation . < +Date: Tue, 19 Feb 2019 09:21:42 +0900 +Subject: [PATCH] test-udev: use proper semantics for too long line with + continuation + +Follow-up for 1e797cf596df50a6bdd8cbf8e9b2467a3a934171. + +(cherry picked from commit e37a5d90b0c624b95f8d0c3400288fec60417ec4) + +Related: #1642728 +--- + test/udev-test.pl | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 3a50694fa9..58b5dc85e1 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -39,10 +39,11 @@ for (my $i = 1; $i <= 10000; ++$i) { + $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n"; + } + +-my $rules_10k_tags_continuation = ""; +-for (my $i = 1; $i <= 10000; ++$i) { +- $rules_10k_tags_continuation .= 'KERNEL=="sda", TAG+="test' . $i . "\"\\\n"; ++my $rules_10k_tags_continuation = "KERNEL==\"sda\", \\\n"; ++for (my $i = 1; $i < 10000; ++$i) { ++ $rules_10k_tags_continuation .= 'TAG+="test' . $i . "\",\\\n"; + } ++$rules_10k_tags_continuation .= "TAG+=\"test10000\"\\n"; + + my @tests = ( + { diff --git a/SOURCES/0518-test-udev-add-more-tests-for-line-continuations-and-.patch b/SOURCES/0518-test-udev-add-more-tests-for-line-continuations-and-.patch new file mode 100644 index 0000000..9689d81 --- /dev/null +++ b/SOURCES/0518-test-udev-add-more-tests-for-line-continuations-and-.patch @@ -0,0 +1,40 @@ +From 66c41fbbeb472563993724352b1984aa3e7e47db Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Tue, 19 Feb 2019 09:22:45 +0900 +Subject: [PATCH] test-udev: add more tests for line continuations and comments + +(cherry picked from commit d35976c670b0e5c2d4081b781e5af88c0689ff00) + +Related: #1642728 +--- + test/udev-test.pl | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 58b5dc85e1..a5e1f8cda3 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1453,13 +1453,21 @@ TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test1 + EOF + }, + { +- desc => "don't crash with lots of tags with continuation", ++ desc => "continuations", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "found", + not_exp_name => "bad" , + rules => $rules_10k_tags_continuation . < +Date: Thu, 21 Feb 2019 18:03:32 +0900 +Subject: [PATCH] test-udev: add more tests for line continuation + +(cherry picked from commit 84a0819c9d89a2ddb195a5d975ae1fd5c62fde3c) + +Related: #1642728 +--- + test/udev-test.pl | 34 ++++++++++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index a5e1f8cda3..002fabd9fd 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1466,8 +1466,42 @@ TAG+="hoge1",\\ + TAG+="hoge2",\\ + # spaces before and after token are dropped + TAG+="hoge3", \\ ++\\ ++ \\ + TAG+="hoge4" + TAGS=="hoge1", TAGS=="hoge2", TAGS=="hoge3", TAGS=="hoge4", SYMLINK+="found" ++EOF ++ }, ++ { ++ desc => "continuations with empty line", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ rules => < "continuations with white only line", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ rules => < +Date: Thu, 21 Feb 2019 18:04:12 +0900 +Subject: [PATCH] test-udev: fix alignment and drop unnecessary white spaces + +(cherry picked from commit 3dd2d524141d09d57443ae339e1a77d7ce40f847) + +Related: #1642728 +--- + test/udev-test.pl | 114 +++++++++++++++++++++++----------------------- + 1 file changed, 57 insertions(+), 57 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 002fabd9fd..122359e377 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -49,7 +49,7 @@ my @tests = ( + { + desc => "no rules", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda" , ++ exp_name => "sda", + exp_rem_error => "yes", + rules => < "label test of scsi disc", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk" , ++ exp_name => "boot_disk", + rules => < "label test of scsi disc", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk" , ++ exp_name => "boot_disk", + rules => < "label test of scsi disc", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk" , ++ exp_name => "boot_disk", + rules => < "label test of scsi partition", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_name => "boot_disk1", + rules => < "label test of pattern match", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_name => "boot_disk1", + rules => < "label test of multiple sysfs files", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_name => "boot_disk1", + rules => < "label test of max sysfs files (skip invalid rule)", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_name => "boot_disk1", + rules => < "catch device by *", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_name => "modem/0", + rules => < "catch device by * - take 2", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_name => "modem/0", + rules => < "catch device by ?", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_name => "modem/0", + rules => < "catch device by character class", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_name => "modem/0", + rules => < "replace kernel name", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_name => "modem", + rules => < "Handle comment lines in config file (and replace kernel name)", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_name => "modem", + rules => < "Handle comment lines in config file with whitespace (and replace kernel name)", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_name => "modem", + rules => < "Handle whitespace only lines (and replace kernel name)", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "whitespace" , ++ exp_name => "whitespace", + rules => < "Handle empty lines in config file (and replace kernel name)", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_name => "modem", + rules => < "Handle backslashed multi lines in config file (and replace kernel name)", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_name => "modem", + rules => < "Handle stupid backslashed multi lines in config file (and replace kernel name)", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_name => "modem", + rules => < "subdirectory handling", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "sub/direct/ory/modem" , ++ exp_name => "sub/direct/ory/modem", + rules => < "parent device name match of scsi partition", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "first_disk5" , ++ exp_name => "first_disk5", + rules => < "test substitution chars", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0" , ++ exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0", + rules => < "sustitution of sysfs value (%s{file})", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "disk-ATA-sda" , ++ exp_name => "disk-ATA-sda", + rules => < "program result substitution", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "special-device-5" , +- not_exp_name => "not" , ++ exp_name => "special-device-5", ++ not_exp_name => "not", + rules => < "program result substitution (newline removal)", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "newline_removed" , ++ exp_name => "newline_removed", + rules => < "program result substitution", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "test-0:0:0:0" , ++ exp_name => "test-0:0:0:0", + rules => < "program with lots of arguments", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo9" , ++ exp_name => "foo9", + rules => < "program with subshell", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "bar9" , ++ exp_name => "bar9", + rules => < "program arguments combined with apostrophes", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo7" , ++ exp_name => "foo7", + rules => < "program arguments combined with escaped double quotes, part 1", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2" , ++ exp_name => "foo2", + rules => < "program arguments combined with escaped double quotes, part 2", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2" , ++ exp_name => "foo2", + rules => < "program arguments combined with escaped double quotes, part 3", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2" , ++ exp_name => "foo2", + rules => < "characters before the %c{N} substitution", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "my-foo9" , ++ exp_name => "my-foo9", + rules => < "substitute the second to last argument", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "my-foo8" , ++ exp_name => "my-foo8", + rules => < "test substitution by variable name 3", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "850:0:0:05" , ++ exp_name => "850:0:0:05", + rules => < "test substitution by variable name 4", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "855" , ++ exp_name => "855", + rules => < "test substitution by variable name 5", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "8550:0:0:0" , ++ exp_name => "8550:0:0:0", + rules => < "non matching SUBSYSTEMS", + devpath => "/devices/virtual/tty/console", +- exp_name => "TTY" , ++ exp_name => "TTY", + rules => < "ATTRS match", + devpath => "/devices/virtual/tty/console", +- exp_name => "foo" , ++ exp_name => "foo", + rules => < "ATTR (empty file)", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "empty" , ++ exp_name => "empty", + rules => < "ATTR (non-existent file)", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "non-existent" , ++ exp_name => "non-existent", + rules => < "program and bus type match", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0" , ++ exp_name => "scsi-0:0:0:0", + rules => < "sysfs parent hierarchy", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_name => "modem", + rules => < "name test with ! in the name", + devpath => "/devices/virtual/block/fake!blockdev0", +- exp_name => "is/a/fake/blockdev0" , ++ exp_name => "is/a/fake/blockdev0", + rules => < "name test with ! in the name, but no matching rule", + devpath => "/devices/virtual/block/fake!blockdev0", +- exp_name => "fake/blockdev0" , ++ exp_name => "fake/blockdev0", + exp_rem_error => "yes", + rules => < "KERNELS wildcard partial 2", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "scsi-0:0:0:0", +- rules => < < "/devices/virtual/misc/misc-fake1", + exp_name => "node", + exp_majorminor => "4095:1", +- rules => < < "multiple symlinks with a lot of s p a c e s", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", + exp_name => "one", +- not_exp_name => " ", ++ not_exp_name => " ", + rules => < "multiple symlinks", + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "second-0" , ++ exp_name => "second-0", + rules => < "symlink name '.'", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => ".", +- exp_add_error => "yes", +- exp_rem_error => "yes", ++ exp_add_error => "yes", ++ exp_rem_error => "yes", + rules => < "symlink node to itself", + devpath => "/devices/virtual/tty/tty0", + exp_name => "link", +- exp_add_error => "yes", +- exp_rem_error => "yes", +- option => "clean", ++ exp_add_error => "yes", ++ exp_rem_error => "yes", ++ option => "clean", + rules => < "add and match tag", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "found", +- not_exp_name => "bad" , ++ not_exp_name => "bad", + rules => < "continuations", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "found", +- not_exp_name => "bad" , ++ not_exp_name => "bad", + rules => $rules_10k_tags_continuation . < +Date: Fri, 5 Jul 2019 11:24:55 -0400 +Subject: [PATCH] test/udev-test.pl: cleanup if skipping test + +In Ubuntu CI, udev-test.pl is run from the debian/test/udev script, +in a test dir created for it; but udev-test.pl setup mounts a +dir, so if it doesn't cleanup/unmount before exiting, the test dir +autopkgtest created for it can't be removed, and autopkgtest +aborts the entire test suite, for example this output (from a +test run inside an armhf container): + +autopkgtest [12:45:36]: test udev: [----------------------- +umount: test/tmpfs: no mount point specified. +mknod: test/tmpfs/dev/null: Operation not permitted +unable to create test/tmpfs/dev/null at ./udev-test.pl line 1611. +Failed to set up the environment, skipping the test at ./udev-test.pl line 1731. +autopkgtest [12:45:41]: test udev: -----------------------] +autopkgtest [12:45:44]: test udev: - - - - - - - - - - results - - - - - - - - - - +udev FAIL non-zero exit status 77 +rm: cannot remove '/tmp/autopkgtest.ocPFA6/autopkgtest_tmp/test/tmpfs': Device or resource busy +autopkgtest [12:46:22]: ERROR: "rm -rf /tmp/autopkgtest.ocPFA6/udev-artifacts /tmp/autopkgtest.ocPFA6/autopkgtest_tmp" failed with stderr "rm: + +(cherry picked from commit abb9cc50afb3949c442849f43301fb33578f3888) + +Related: #1642728 +--- + test/udev-test.pl | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 122359e377..2fea72875b 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1713,6 +1713,12 @@ sub run_test { + + } + ++sub cleanup { ++ system("rm", "-rf", "$udev_run"); ++ system("umount", "$udev_tmpfs"); ++ rmdir($udev_tmpfs); ++} ++ + # only run if we have root permissions + # due to mknod restrictions + if (!($<==0)) { +@@ -1729,11 +1735,13 @@ if ($? >> 8 == 0) { + + if (!udev_setup()) { + warn "Failed to set up the environment, skipping the test"; ++ cleanup(); + exit($EXIT_TEST_SKIP); + } + + if (system($udev_bin, "check")) { + warn "$udev_bin failed to set up the environment, skipping the test"; ++ cleanup(); + exit($EXIT_TEST_SKIP); + } + +@@ -1776,10 +1784,7 @@ if ($list[0]) { + + print "$error errors occurred\n\n"; + +-# cleanup +-system("rm", "-rf", "$udev_run"); +-system("umount", "$udev_tmpfs"); +-rmdir($udev_tmpfs); ++cleanup(); + + if ($error > 0) { + exit(1); diff --git a/SOURCES/0522-test-add-test-cases-for-empty-string-match.patch b/SOURCES/0522-test-add-test-cases-for-empty-string-match.patch new file mode 100644 index 0000000..acf7479 --- /dev/null +++ b/SOURCES/0522-test-add-test-cases-for-empty-string-match.patch @@ -0,0 +1,89 @@ +From 03bc565e6e3249385c4e1ca0ae27670ca2ad9a41 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 11 Sep 2019 09:06:15 +0900 +Subject: [PATCH] test: add test cases for empty string match + +(cherry picked from commit 48d26c90852c22ec94be961f5fbdcf462bb9a6e8) + +Related: #1642728 +--- + test/udev-test.pl | 66 +++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 66 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 2fea72875b..50d978391b 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1256,6 +1256,72 @@ KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1" + KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2" + KERNEL=="all|dontknow|ttyACM0", SYMLINK+="right" + KERNEL=="ttyACM0a|nothing", SYMLINK+="wrong3" ++EOF ++ }, ++ { ++ desc => "test multi matches 5", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ rules => < "test multi matches 6", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ rules => < "test multi matches 7", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ rules => < "test multi matches 8", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ rules => < "test multi matches 9", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ rules => < "test multi matches 10", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ rules => < +Date: Sun, 12 Jul 2020 03:27:45 -0400 +Subject: [PATCH] test: add test case for multi matches when use "||" + +Signed-off-by: gaoyi +(cherry picked from commit 0d3a8bc7ebd76591e14f7098b4266fd2065ac4db) + +Related: #1642728 +--- + test/udev-test.pl | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 50d978391b..4bf97d82bb 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1322,6 +1322,17 @@ EOF + KERNEL=="sda", TAG="" + TAGS=="foo|", SYMLINK+="found" + TAGS=="aaa|bbb", SYMLINK+="bad" ++EOF ++ }, ++ { ++ desc => "test multi matches 11", ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ rules => < +Date: Fri, 4 Sep 2020 18:09:20 +0200 +Subject: [PATCH] udev-test: do not rely on "mail" group being defined + +"audio" should be there, at least we declare it. "mail" nowadays is less +likely to exist than in the past. + +Fixes one of the items in #16942. + +(cherry picked from commit a9030b81c154c3ec92227d04cad6b13cc1125608) + +Related: #1642728 +--- + test/udev-test.pl | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 4bf97d82bb..a4deffacb9 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -629,9 +629,9 @@ EOF + desc => "textual user/group id", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "node", +- exp_perms => "root:mail:0660", ++ exp_perms => "root:audio:0660", + rules => < +Date: Fri, 20 Apr 2018 22:38:30 +0200 +Subject: [PATCH] test/udev-test.pl: allow multiple devices per test + +Allow testing cases where multiple devices are added and removed. +This implies a change of the data structure: every test allows +for multiple devices to be added, and "exp_name" etc. are now properties +of the device, not of the test. + +(cherry picked from commit 255c05b72455dcad1b5552d12a813b31f68201a7) + +Related: #1642728 +--- + test/udev-test.pl | 1352 +++++++++++++++++++++++++++++++-------------- + 1 file changed, 929 insertions(+), 423 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index a4deffacb9..bd5401da75 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -48,17 +48,28 @@ $rules_10k_tags_continuation .= "TAG+=\"test10000\"\\n"; + my @tests = ( + { + desc => "no rules", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", +- exp_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda" , ++ exp_rem_error => "yes", ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "sda1" , ++ exp_rem_error => "yes", ++ }], + rules => < "label test of scsi disc", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "boot_disk" , ++ }], + rules => < "label test of scsi disc", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "boot_disk" , ++ }], + rules => < "label test of scsi disc", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "boot_disk" , ++ }], + rules => < "label test of scsi partition", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "boot_disk1" , ++ }], + rules => < "label test of pattern match", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "boot_disk1" , ++ }], + rules => < "label test of multiple sysfs files", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "boot_disk1" , ++ }], + rules => < "label test of max sysfs files (skip invalid rule)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "boot_disk1" , ++ }], + rules => < "catch device by *", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem/0" , ++ }], + rules => < "catch device by * - take 2", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem/0" , ++ }], + rules => < "catch device by ?", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem/0" , ++ }], + rules => < "catch device by character class", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem/0" , ++ }], + rules => < "replace kernel name", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem" , ++ }], + rules => < "Handle comment lines in config file (and replace kernel name)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem" , ++ }], + rules => < "Handle comment lines in config file with whitespace (and replace kernel name)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem" , ++ }], + rules => < "Handle whitespace only lines (and replace kernel name)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "whitespace", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "whitespace" , ++ }], + rules => < "Handle empty lines in config file (and replace kernel name)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem" , ++ }], + rules => < "Handle backslashed multi lines in config file (and replace kernel name)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem" , ++ }], + rules => < "preserve backslashes, if they are not for a newline", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "aaa", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "aaa", ++ }], + rules => < "Handle stupid backslashed multi lines in config file (and replace kernel name)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem" , ++ }], + rules => < "subdirectory handling", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "sub/direct/ory/modem", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "sub/direct/ory/modem" , ++ }], + rules => < "parent device name match of scsi partition", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "first_disk5", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "first_disk5" , ++ }], + rules => < "test substitution chars", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0" , ++ }], + rules => < "import of shell-value returned from program", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node12345678", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node12345678", ++ }], + rules => < "sustitution of sysfs value (%s{file})", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "disk-ATA-sda", ++ desc => "substitution of sysfs value (%s{file})", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "disk-ATA-sda" , ++ }], + rules => < "program result substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "special-device-5", +- not_exp_name => "not", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "special-device-5" , ++ not_exp_name => "not" , ++ }], + rules => < "program result substitution (newline removal)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "newline_removed", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "newline_removed" , ++ }], + rules => < "program result substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "test-0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "test-0:0:0:0" , ++ }], + rules => < "program with lots of arguments", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo9", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "foo9" , ++ }], + rules => < "program with subshell", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "bar9", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "bar9" , ++ }], + rules => < "program arguments combined with apostrophes", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo7", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "foo7" , ++ }], + rules => < "program arguments combined with escaped double quotes, part 1", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "foo2" , ++ }], + rules => < "program arguments combined with escaped double quotes, part 2", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "foo2" , ++ }], + rules => < "program arguments combined with escaped double quotes, part 3", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "foo2" , ++ }], + rules => < "characters before the %c{N} substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "my-foo9", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "my-foo9" , ++ }], + rules => < "substitute the second to last argument", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "my-foo8", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "my-foo8" , ++ }], + rules => < "test substitution by variable name", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", ++ }], + rules => < "test substitution by variable name 2", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", ++ }], + rules => < "test substitution by variable name 3", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "850:0:0:05", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "850:0:0:05" , ++ }], + rules => < "test substitution by variable name 4", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "855", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "855" , ++ }], + rules => < "test substitution by variable name 5", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "8550:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "8550:0:0:0" , ++ }], + rules => < "non matching SUBSYSTEMS for device with no parent", +- devpath => "/devices/virtual/tty/console", +- exp_name => "TTY", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/console", ++ exp_name => "TTY", ++ }], + rules => < "non matching SUBSYSTEMS", +- devpath => "/devices/virtual/tty/console", +- exp_name => "TTY", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/console", ++ exp_name => "TTY" , ++ }], + rules => < "ATTRS match", +- devpath => "/devices/virtual/tty/console", +- exp_name => "foo", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/console", ++ exp_name => "foo" , ++ }], + rules => < "ATTR (empty file)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "empty", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "empty" , ++ }], + rules => < "ATTR (non-existent file)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "non-existent", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "non-existent" , ++ }], + rules => < "program and bus type match", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "scsi-0:0:0:0" , ++ }], + rules => < "sysfs parent hierarchy", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem" , ++ }], + rules => < "name test with ! in the name", +- devpath => "/devices/virtual/block/fake!blockdev0", +- exp_name => "is/a/fake/blockdev0", ++ devices => [ ++ { ++ devpath => "/devices/virtual/block/fake!blockdev0", ++ exp_name => "is/a/fake/blockdev0" , ++ }], + rules => < "name test with ! in the name, but no matching rule", +- devpath => "/devices/virtual/block/fake!blockdev0", +- exp_name => "fake/blockdev0", +- exp_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/virtual/block/fake!blockdev0", ++ exp_name => "fake/blockdev0" , ++ exp_rem_error => "yes", ++ }], + rules => < "KERNELS rule", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "scsi-0:0:0:0", ++ }], + rules => < "KERNELS wildcard all", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "scsi-0:0:0:0", ++ }], + rules => < "KERNELS wildcard partial", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "scsi-0:0:0:0", ++ }], + rules => < "KERNELS wildcard partial 2", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0", +- rules => < [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "scsi-0:0:0:0", ++ }], ++ rules => < "substitute attr with link target value (first match)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "driver-is-sd", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "driver-is-sd", ++ }], + rules => < "substitute attr with link target value (currently selected device)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "driver-is-ahci", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "driver-is-ahci", ++ }], + rules => < "ignore ATTRS attribute whitespace", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ignored", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ignored", ++ }], + rules => < "do not ignore ATTRS attribute whitespace", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "matched-with-space", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "matched-with-space", ++ }], + rules => < "permissions USER=bad GROUP=name", +- devpath => "/devices/virtual/tty/tty33", +- exp_name => "tty33", +- exp_perms => "0:0:0600", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/tty33", ++ exp_name => "tty33", ++ exp_perms => "0:0:0600", ++ }], + rules => < "permissions OWNER=1", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "1::0600", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "1::0600", ++ }], + rules => < "permissions GROUP=1", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => ":1:0660", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => ":1:0660", ++ }], + rules => < "textual user id", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "daemon::0600", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "daemon::0600", ++ }], + rules => < "textual group id", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => ":daemon:0660", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => ":daemon:0660", ++ }], + rules => < "textual user/group id", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "root:audio:0660", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "root:audio:0660", ++ }], + rules => < "permissions MODE=0777", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "::0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "::0777", ++ }], + rules => < "permissions OWNER=1 GROUP=1 MODE=0777", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_perms => "1:1:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_perms => "1:1:0777", ++ }], + rules => < "permissions OWNER to 1", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1::", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1::", ++ }], + rules => < "permissions GROUP to 1", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => ":1:0660", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => ":1:0660", ++ }], + rules => < "permissions MODE to 0060", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "::0060", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "::0060", ++ }], + rules => < "permissions OWNER, GROUP, MODE", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1:1:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1:1:0777", ++ }], + rules => < "permissions only rule", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1:1:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1:1:0777", ++ }], + rules => < "multiple permissions only rule", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1:1:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1:1:0777", ++ }], + rules => < "permissions only rule with override at SYMLINK+ rule", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", +- exp_perms => "1:2:0777", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "ttyACM0", ++ exp_perms => "1:2:0777", ++ }], + rules => < "major/minor number test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", +- exp_majorminor => "8:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ exp_majorminor => "8:0", ++ }], + rules => < "big major number test", +- devpath => "/devices/virtual/misc/misc-fake1", +- exp_name => "node", +- exp_majorminor => "4095:1", +- rules => < [ ++ { ++ devpath => "/devices/virtual/misc/misc-fake1", ++ exp_name => "node", ++ exp_majorminor => "4095:1", ++ }], ++ rules => < "big major and big minor number test", +- devpath => "/devices/virtual/misc/misc-fake89999", +- exp_name => "node", +- exp_majorminor => "4095:89999", ++ devices => [ ++ { ++ devpath => "/devices/virtual/misc/misc-fake89999", ++ exp_name => "node", ++ exp_majorminor => "4095:89999", ++ }], + rules => < "multiple symlinks with format char", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink2-ttyACM0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "symlink2-ttyACM0", ++ }], + rules => < "multiple symlinks with a lot of s p a c e s", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "one", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "one", ++ not_exp_name => " ", ++ }], + rules => < "symlink with spaces in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with leading space in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with trailing space in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with lots of space in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with multiple spaces in substituted variable", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with space and var with space, part 1", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "first", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "first", ++ not_exp_name => " ", ++ }], + rules => < "symlink with space and var with space, part 2", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "name-one_two_three-end", ++ not_exp_name => " ", ++ }], + rules => < "symlink with space and var with space, part 3", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "another_symlink", +- not_exp_name => " ", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "another_symlink", ++ not_exp_name => " ", ++ }], + rules => < "symlink creation (same directory)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "modem0", ++ }], + rules => < "multiple symlinks", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "second-0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "second-0" , ++ }], + rules => < "symlink name '.'", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => ".", +- exp_add_error => "yes", +- exp_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => ".", ++ exp_add_error => "yes", ++ exp_rem_error => "yes", ++ }], + rules => < "symlink node to itself", +- devpath => "/devices/virtual/tty/tty0", +- exp_name => "link", +- exp_add_error => "yes", +- exp_rem_error => "yes", +- option => "clean", ++ devices => [ ++ { ++ devpath => "/devices/virtual/tty/tty0", ++ exp_name => "link", ++ exp_add_error => "yes", ++ exp_rem_error => "yes", ++ }], ++ option => "clean", + rules => < "symlink %n substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "symlink0", ++ }], + rules => < "symlink %k substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink-ttyACM0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "symlink-ttyACM0", ++ }], + rules => < "symlink %M:%m substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "major-166:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "major-166:0", ++ }], + rules => < "symlink %b substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "symlink-0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "symlink-0:0:0:0", ++ }], + rules => < "symlink %c substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "test", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "test", ++ }], + rules => < "symlink %c{N} substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "test", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "test", ++ }], + rules => < "symlink %c{N+} substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "this", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "this", ++ }], + rules => < "symlink only rule with %c{N+}", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "test", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "test", ++ }], + rules => < "symlink %s{filename} substitution", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "166:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "166:0", ++ }], + rules => < "program result substitution (numbered part of)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "link1", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "link1", ++ }], + rules => < "program result substitution (numbered part of+)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "link4", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_name => "link4", ++ }], + rules => < "SUBSYSTEM match test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ }], + rules => < "DRIVERS match test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ }], + rules => < "devnode substitution test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node", ++ }], + rules => < "parent node name substitution test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sda-part-1", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "sda-part-1", ++ }], + rules => < "udev_root substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "start-/dev-end", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "start-/dev-end", ++ }], + rules => < "last_rule option", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "last", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "last", ++ }], + rules => < "negation KERNEL!=", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "match", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "match", ++ }], + rules => < "negation SUBSYSTEM!=", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "not-anything", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "not-anything", ++ }], + rules => < "negation PROGRAM!= exit code", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "nonzero-program", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "nonzero-program", ++ }], + rules => < "ENV{} test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "true", ++ }], + rules => < "ENV{} test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "true", ++ }], + rules => < "ENV{} test (assign)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "true", ++ }], + rules => < "ENV{} test (assign 2 times)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "true", ++ }], + rules => < "ENV{} test (assign2)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "part", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "part", ++ }], + rules => < "untrusted string sanitize", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sane", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "sane", ++ }], + rules => < "untrusted string sanitize (don't replace utf8)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "uber", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "uber", ++ }], + rules => < "untrusted string sanitize (replace invalid utf8)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "replaced", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "replaced", ++ }], + rules => < "read sysfs value from parent device", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "serial-354172020305000", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "serial-354172020305000", ++ }], + rules => < "match against empty key string", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ok", ++ }], + rules => < "check ACTION value", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ok", ++ }], + rules => < "final assignment", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", +- exp_perms => "root:tty:0640", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ok", ++ exp_perms => "root:tty:0640", ++ }], + rules => < "final assignment 2", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", +- exp_perms => "root:tty:0640", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "ok", ++ exp_perms => "root:tty:0640", ++ }], + rules => < "env substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node-add-me", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "node-add-me", ++ }], + rules => < "reset list to current value", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "three", +- not_exp_name => "two", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "three", ++ not_exp_name => "two", ++ }], + rules => < "test empty SYMLINK+ (empty override)", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", +- not_exp_name => "wrong", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ not_exp_name => "wrong", ++ }], + rules => < "test multi matches", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ }], + rules => < "test multi matches 2", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ }], + rules => < "test multi matches 3", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ }], + rules => < "test multi matches 4", +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", ++ exp_name => "right", ++ }], + rules => < "test multi matches 5", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ desc => "test multi matches 5", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ }], + rules => < "test multi matches 6", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ }], + rules => < "test multi matches 7", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ }], + rules => < "test multi matches 8", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ }], + rules => < "test multi matches 9", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ }], + rules => < "test multi matches 10", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ }], + rules => < "test multi matches 11", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ }], + rules => < "IMPORT parent test sequence 1/2 (keep)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "parent", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "parent", ++ }], + option => "keep", + rules => < "IMPORT parent test sequence 2/2 (keep)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "parentenv-parent_right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "parentenv-parent_right", ++ }], + option => "clean", + rules => < "GOTO test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "right", ++ }], + rules => < "GOTO label does not exist", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "right", ++ }], + rules => < "SYMLINK+ compare test", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", +- not_exp_name => "wrong", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "right", ++ not_exp_name => "wrong", ++ }], + rules => < "invalid key operation", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "yes", ++ }], + rules => < "operator chars in attribute", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "yes", ++ }], + rules => < "overlong comment line", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_name => "yes", ++ }], + rules => < "magic subsys/kernel lookup", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "00:16:41:e2:8d:ff", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "00:16:41:e2:8d:ff", ++ }], + rules => < "TEST absolute path", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "there", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "there", ++ }], + rules => < "TEST subsys/kernel lookup", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "yes", ++ }], + rules => < "TEST relative path", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "relative", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "relative", ++ }], + rules => < "TEST wildcard substitution (find queue/nr_requests)", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found-subdir", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found-subdir", ++ }], + rules => < "TEST MODE=0000", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", +- exp_perms => "0:0:0000", +- exp_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda", ++ exp_perms => "0:0:0000", ++ exp_rem_error => "yes", ++ }], + rules => < "TEST PROGRAM feeds OWNER, GROUP, MODE", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", +- exp_perms => "1:1:0400", +- exp_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda", ++ exp_perms => "1:1:0400", ++ exp_rem_error => "yes", ++ }], + rules => < "TEST PROGRAM feeds MODE with overflow", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", +- exp_perms => "0:0:0440", +- exp_rem_error => "yes", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda", ++ exp_perms => "0:0:0440", ++ exp_rem_error => "yes", ++ }], + rules => < "magic [subsys/sysname] attribute substitution", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda-8741C4G-end", +- exp_perms => "0:0:0600", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "sda-8741C4G-end", ++ exp_perms => "0:0:0600", ++ }], + rules => < "builtin path_id", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0", ++ }], + rules => < "add and match tag", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad" , ++ }], + rules => < "don't crash with lots of tags", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ }], + rules => $rules_10k_tags . < "continuations", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ }], + rules => $rules_10k_tags_continuation . < "continuations with empty line", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ ++ }], + rules => < "continuations with white only line", +- devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_name => "found", ++ not_exp_name => "bad", ++ }], + rules => <{desc}\n"; +- print "device \'$rules->{devpath}\' expecting node/link \'$rules->{exp_name}\'\n"; ++sub check_add { ++ my ($device) = @_; + +- $rc = udev("add", $rules->{devpath}, \$rules->{rules}); +- if ($rc != 0) { +- print "$udev_bin add failed with code $rc\n"; +- $error++; +- } +- if (defined($rules->{not_exp_name})) { +- if ((-e "$udev_dev/$rules->{not_exp_name}") || +- (-l "$udev_dev/$rules->{not_exp_name}")) { +- print "nonexistent: error \'$rules->{not_exp_name}\' not expected to be there\n"; ++ if (defined($device->{not_exp_name})) { ++ if ((-e "$udev_dev/$device->{not_exp_name}") || ++ (-l "$udev_dev/$device->{not_exp_name}")) { ++ print "nonexistent: error \'$device->{not_exp_name}\' not expected to be there\n"; + $error++; + sleep(1); + } + } +- +- if ((-e "$udev_dev/$rules->{exp_name}") || +- (-l "$udev_dev/$rules->{exp_name}")) { ++ if ((-e "$udev_dev/$device->{exp_name}") || ++ (-l "$udev_dev/$device->{exp_name}")) { + + my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, +- $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$rules->{exp_name}"); ++ $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$device->{exp_name}"); + +- if (defined($rules->{exp_perms})) { +- permissions_test($rules, $uid, $gid, $mode); ++ if (defined($device->{exp_perms})) { ++ permissions_test($device, $uid, $gid, $mode); + } +- if (defined($rules->{exp_majorminor})) { +- major_minor_test($rules, $rdev); ++ if (defined($device->{exp_majorminor})) { ++ major_minor_test($device, $rdev); + } +- print "add: ok\n"; ++ print "add $device->{devpath}: ok\n"; + } else { +- print "add: error"; +- if ($rules->{exp_add_error}) { ++ print "add $device->{devpath}: error"; ++ if ($device->{exp_add_error}) { + print " as expected\n"; + } else { + print "\n"; +@@ -1755,21 +2231,15 @@ sub run_test { + sleep(1); + } + } ++} + +- if (defined($rules->{option}) && $rules->{option} eq "keep") { +- print "\n\n"; +- return; +- } ++sub check_remove { ++ my ($device) = @_; + +- $rc = udev("remove", $rules->{devpath}, \$rules->{rules}); +- if ($rc != 0) { +- print "$udev_bin remove failed with code $rc\n"; +- $error++; +- } +- if ((-e "$udev_dev/$rules->{exp_name}") || +- (-l "$udev_dev/$rules->{exp_name}")) { +- print "remove: error"; +- if ($rules->{exp_rem_error}) { ++ if ((-e "$udev_dev/$device->{exp_name}") || ++ (-l "$udev_dev/$device->{exp_name}")) { ++ print "remove $device->{devpath}: error"; ++ if ($device->{exp_rem_error}) { + print " as expected\n"; + } else { + print "\n"; +@@ -1779,7 +2249,43 @@ sub run_test { + sleep(1); + } + } else { +- print "remove: ok\n"; ++ print "remove $device->{devpath}: ok\n"; ++ } ++} ++ ++sub run_test { ++ my ($rules, $number) = @_; ++ my $rc; ++ my @devices = @{$rules->{devices}}; ++ ++ print "TEST $number: $rules->{desc}\n"; ++ foreach my $dev (@devices) { ++ print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n"; ++ $rc = udev("add", $dev->{devpath}, \$rules->{rules}); ++ if ($rc != 0) { ++ print "$udev_bin add failed with code $rc\n"; ++ $error++; ++ } ++ } ++ ++ foreach my $dev (@devices) { ++ check_add($dev); ++ } ++ ++ if (defined($rules->{option}) && $rules->{option} eq "keep") { ++ print "\n\n"; ++ return; ++ } ++ ++ foreach my $dev (@devices) { ++ $rc = udev("remove", $dev->{devpath}, \$rules->{rules}); ++ if ($rc != 0) { ++ print "$udev_bin remove failed with code $rc\n"; ++ $error++; ++ } ++ } ++ foreach my $dev (@devices) { ++ check_remove($dev); + } + + print "\n"; diff --git a/SOURCES/0526-test-udev-test.pl-create-rules-only-once.patch b/SOURCES/0526-test-udev-test.pl-create-rules-only-once.patch new file mode 100644 index 0000000..b394455 --- /dev/null +++ b/SOURCES/0526-test-udev-test.pl-create-rules-only-once.patch @@ -0,0 +1,61 @@ +From 9aa12f2f564c208c4c1eaef613d18d1c0b481a16 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Mon, 23 Apr 2018 21:58:12 +0200 +Subject: [PATCH] test/udev-test.pl: create rules only once + +It's not necessary to write the rules for every udev run, as we +now may have many (rather than just 2) per test. + +(cherry picked from commit af7ee3eae689f9c31b49ea13758ad9c901918ce3) + +Related: #1642728 +--- + test/udev-test.pl | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index bd5401da75..8b5a97ad61 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2069,14 +2069,18 @@ EOF + }, + ); + +-sub udev { +- my ($action, $devpath, $rules) = @_; ++sub create_rules { ++ my ($rules) = @_; + + # create temporary rules + system("mkdir", "-p", "$udev_rules_dir"); + open CONF, ">$udev_rules" || die "unable to create rules file: $udev_rules"; + print CONF $$rules; + close CONF; ++} ++ ++sub udev { ++ my ($action, $devpath) = @_; + + if ($valgrind > 0) { + return system("$udev_bin_valgrind $action $devpath"); +@@ -2259,9 +2263,10 @@ sub run_test { + my @devices = @{$rules->{devices}}; + + print "TEST $number: $rules->{desc}\n"; ++ create_rules(\$rules->{rules}); + foreach my $dev (@devices) { + print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n"; +- $rc = udev("add", $dev->{devpath}, \$rules->{rules}); ++ $rc = udev("add", $dev->{devpath}); + if ($rc != 0) { + print "$udev_bin add failed with code $rc\n"; + $error++; +@@ -2278,7 +2283,7 @@ sub run_test { + } + + foreach my $dev (@devices) { +- $rc = udev("remove", $dev->{devpath}, \$rules->{rules}); ++ $rc = udev("remove", $dev->{devpath}); + if ($rc != 0) { + print "$udev_bin remove failed with code $rc\n"; + $error++; diff --git a/SOURCES/0527-test-udev-test.pl-allow-concurrent-additions-and-rem.patch b/SOURCES/0527-test-udev-test.pl-allow-concurrent-additions-and-rem.patch new file mode 100644 index 0000000..f87307f --- /dev/null +++ b/SOURCES/0527-test-udev-test.pl-allow-concurrent-additions-and-rem.patch @@ -0,0 +1,169 @@ +From 618d56c7ac8bd8cd701344a0eaca8373a78dea95 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Mon, 23 Apr 2018 21:59:05 +0200 +Subject: [PATCH] test/udev-test.pl: allow concurrent additions and removals + +Allow testing cases where multiple devices are added and removed +simultaneously. Tests are started as synchronously as possible using a +semaphore, in order to test possible race conditions. If this isn't desired, +the test parameter "sleep_us" can be set to the number of microseconds to wait +between udev invocations. + +(cherry picked from commit 09a4062d70b3a10d022e40066e2adf09df05bbbc) + +Related: #1642728 +--- + test/udev-test.pl | 90 +++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 72 insertions(+), 18 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 8b5a97ad61..db25ef13c1 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -18,6 +18,10 @@ + + use warnings; + use strict; ++use POSIX qw(WIFEXITED WEXITSTATUS); ++use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); ++use IPC::Semaphore; ++use Time::HiRes qw(usleep); + + my $udev_bin = "./test-udev"; + my $valgrind = 0; +@@ -2210,6 +2214,8 @@ sub check_add { + sleep(1); + } + } ++ ++ print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n"; + if ((-e "$udev_dev/$device->{exp_name}") || + (-l "$udev_dev/$device->{exp_name}")) { + +@@ -2257,21 +2263,72 @@ sub check_remove { + } + } + ++sub run_udev { ++ my ($action, $dev, $sleep_us, $sema) = @_; ++ ++ # Notify main process that this worker has started ++ $sema->op(0, 1, 0); ++ ++ # Wait for start ++ $sema->op(0, 0, 0); ++ usleep($sleep_us) if defined ($sleep_us); ++ my $rc = udev($action, $dev->{devpath}); ++ exit $rc; ++} ++ ++sub fork_and_run_udev { ++ my ($action, $rules, $sema) = @_; ++ my @devices = @{$rules->{devices}}; ++ my $dev; ++ my $k = 0; ++ ++ $sema->setval(0, 1); ++ foreach $dev (@devices) { ++ my $pid = fork(); ++ ++ if (!$pid) { ++ run_udev($action, $dev, ++ defined($rules->{sleep_us}) ? $k * $rules->{sleep_us} : undef, ++ $sema); ++ } else { ++ $dev->{pid} = $pid; ++ } ++ $k++; ++ } ++ ++ # This operation waits for all workers to become ready, and ++ # starts them off when that's the case. ++ $sema->op(0, -($#devices + 2), 0); ++ ++ foreach $dev (@devices) { ++ my $rc; ++ my $pid; ++ ++ $pid = waitpid($dev->{pid}, 0); ++ if ($pid == -1) { ++ print "error waiting for pid dev->{pid}\n"; ++ $error += 1; ++ } ++ if (WIFEXITED($?)) { ++ $rc = WEXITSTATUS($?); ++ ++ if ($rc) { ++ print "$udev_bin $action for $dev->{devpath} failed with code $rc\n"; ++ $error += 1; ++ } ++ } ++ } ++} ++ + sub run_test { +- my ($rules, $number) = @_; ++ my ($rules, $number, $sema) = @_; + my $rc; + my @devices = @{$rules->{devices}}; + + print "TEST $number: $rules->{desc}\n"; + create_rules(\$rules->{rules}); +- foreach my $dev (@devices) { +- print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n"; +- $rc = udev("add", $dev->{devpath}); +- if ($rc != 0) { +- print "$udev_bin add failed with code $rc\n"; +- $error++; +- } +- } ++ ++ fork_and_run_udev("add", $rules, $sema); + + foreach my $dev (@devices) { + check_add($dev); +@@ -2282,13 +2339,8 @@ sub run_test { + return; + } + +- foreach my $dev (@devices) { +- $rc = udev("remove", $dev->{devpath}); +- if ($rc != 0) { +- print "$udev_bin remove failed with code $rc\n"; +- $error++; +- } +- } ++ fork_and_run_udev("remove", $rules, $sema); ++ + foreach my $dev (@devices) { + check_remove($dev); + } +@@ -2350,12 +2402,13 @@ foreach my $arg (@ARGV) { + push(@list, $arg); + } + } ++my $sema = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR | IPC_CREAT); + + if ($list[0]) { + foreach my $arg (@list) { + if (defined($tests[$arg-1]->{desc})) { + print "udev-test will run test number $arg:\n\n"; +- run_test($tests[$arg-1], $arg); ++ run_test($tests[$arg-1], $arg, $sema); + } else { + print "test does not exist.\n"; + } +@@ -2365,11 +2418,12 @@ if ($list[0]) { + print "\nudev-test will run ".($#tests + 1)." tests:\n\n"; + + foreach my $rules (@tests) { +- run_test($rules, $test_num); ++ run_test($rules, $test_num, $sema); + $test_num++; + } + } + ++$sema->remove; + print "$error errors occurred\n\n"; + + cleanup(); diff --git a/SOURCES/0528-test-udev-test.pl-use-computed-devnode-name.patch b/SOURCES/0528-test-udev-test.pl-use-computed-devnode-name.patch new file mode 100644 index 0000000..a20ebbd --- /dev/null +++ b/SOURCES/0528-test-udev-test.pl-use-computed-devnode-name.patch @@ -0,0 +1,260 @@ +From 5f34ea55a8c6723240eb1641a655db7df3c428a2 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 09:38:26 +0200 +Subject: [PATCH] test/udev-test.pl: use computed devnode name + +More often than not, the created devnode is the basename of the +sysfs entry. The "devnode" device may be used to override the +auto-detected node name. + +Permissions and major/minor number are now verified on the devnode +itself, not on symlinks. + +For those tests where exp_name is set to the computed devnode name, +the explicit "exp_name" can be removed. "exp_name" is only required for +symlinks. + +This allows separate testing for devnodes and symlinks an a follow-up +patch. + +(cherry picked from commit f0dccf01a7b4e72278e14effd74782ea83d0a73b) + +Related: #1642728 +--- + test/udev-test.pl | 92 +++++++++++++++++++++++++++++++++-------------- + 1 file changed, 66 insertions(+), 26 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index db25ef13c1..aa9a8dc2ff 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -55,12 +55,10 @@ my @tests = ( + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda" , + exp_rem_error => "yes", + }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sda1" , + exp_rem_error => "yes", + }], + rules => < [ + { + devpath => "/devices/virtual/block/fake!blockdev0", ++ devnode => "fake/blockdev0", + exp_name => "is/a/fake/blockdev0" , + }], + rules => < [ + { + devpath => "/devices/virtual/block/fake!blockdev0", +- exp_name => "fake/blockdev0" , ++ devnode => "fake/blockdev0", + exp_rem_error => "yes", + }], + rules => < [ + { + devpath => "/devices/virtual/tty/tty33", +- exp_name => "tty33", + exp_perms => "0:0:0600", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1::", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => ":1:0660", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "::0060", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1:1:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1:1:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1:1:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "ttyACM0", + exp_perms => "1:2:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", + exp_perms => "0:0:0000", + exp_rem_error => "yes", + }], +@@ -1935,7 +1925,6 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", + exp_perms => "1:1:0400", + exp_rem_error => "yes", + }], +@@ -1949,7 +1938,6 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda", + exp_perms => "0:0:0440", + exp_rem_error => "yes", + }], +@@ -2203,6 +2191,44 @@ sub udev_setup { + return 1; + } + ++sub get_devnode { ++ my ($device) = @_; ++ my $devnode; ++ ++ if (defined($device->{devnode})) { ++ $devnode = "$udev_dev/$device->{devnode}"; ++ } else { ++ $devnode = "$device->{devpath}"; ++ $devnode =~ s!.*/!$udev_dev/!; ++ } ++ return $devnode; ++} ++ ++sub check_devnode { ++ my ($device) = @_; ++ my $devnode = get_devnode($device); ++ ++ my @st = lstat("$devnode"); ++ if (! (-b _ || -c _)) { ++ print "add $devnode: error\n"; ++ system("tree", "$udev_dev"); ++ $error++; ++ return undef; ++ } ++ ++ my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, ++ $atime, $mtime, $ctime, $blksize, $blocks) = @st; ++ ++ if (defined($device->{exp_perms})) { ++ permissions_test($device, $uid, $gid, $mode); ++ } ++ if (defined($device->{exp_majorminor})) { ++ major_minor_test($device, $rdev); ++ } ++ print "add $devnode: ok\n"; ++ return $devnode; ++} ++ + sub check_add { + my ($device) = @_; + +@@ -2215,19 +2241,13 @@ sub check_add { + } + } + ++ my $devnode = check_devnode($device); ++ + print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n"; ++ return if (!defined($device->{exp_name})); ++ + if ((-e "$udev_dev/$device->{exp_name}") || + (-l "$udev_dev/$device->{exp_name}")) { +- +- my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, +- $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$device->{exp_name}"); +- +- if (defined($device->{exp_perms})) { +- permissions_test($device, $uid, $gid, $mode); +- } +- if (defined($device->{exp_majorminor})) { +- major_minor_test($device, $rdev); +- } + print "add $device->{devpath}: ok\n"; + } else { + print "add $device->{devpath}: error"; +@@ -2243,12 +2263,32 @@ sub check_add { + } + } + ++sub check_remove_devnode { ++ my ($device) = @_; ++ my $devnode = get_devnode($device); ++ ++ if (-e "$devnode") { ++ print "remove $devnode: error"; ++ print "\n"; ++ system("tree", "$udev_dev"); ++ print "\n"; ++ $error++; ++ sleep(1); ++ } else { ++ print "remove $devnode: ok\n"; ++ } ++} ++ + sub check_remove { + my ($device) = @_; + ++ check_remove_devnode($device); ++ ++ return if (!defined($device->{exp_name})); ++ + if ((-e "$udev_dev/$device->{exp_name}") || + (-l "$udev_dev/$device->{exp_name}")) { +- print "remove $device->{devpath}: error"; ++ print "remove $device->{exp_name}: error"; + if ($device->{exp_rem_error}) { + print " as expected\n"; + } else { +@@ -2259,7 +2299,7 @@ sub check_remove { + sleep(1); + } + } else { +- print "remove $device->{devpath}: ok\n"; ++ print "remove $device->{exp_name}: ok\n"; + } + } + diff --git a/SOURCES/0529-test-udev-test.pl-test-correctness-of-symlink-target.patch b/SOURCES/0529-test-udev-test.pl-test-correctness-of-symlink-target.patch new file mode 100644 index 0000000..3c6e624 --- /dev/null +++ b/SOURCES/0529-test-udev-test.pl-test-correctness-of-symlink-target.patch @@ -0,0 +1,61 @@ +From 8ee1cc626f616a2022d641a464fbde9108dd8ad9 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 10:50:24 +0200 +Subject: [PATCH] test/udev-test.pl: test correctness of symlink targets + +Test if symlinks are created correctly by comparing the symlink +targets to the devnode path. This implies (for the symlink) that +major/minor numbers and permissions are correct, as we have tested +that on the devnode already. + +(cherry picked from commit 997683c8f152e1c139a7ce537de81a0aeae4627f) + +Related: #1642728 +--- + test/udev-test.pl | 23 ++++++++++++++++++----- + 1 file changed, 18 insertions(+), 5 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index aa9a8dc2ff..2e3089c5e0 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -22,6 +22,7 @@ use POSIX qw(WIFEXITED WEXITSTATUS); + use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); + use IPC::Semaphore; + use Time::HiRes qw(usleep); ++use Cwd qw(getcwd abs_path); + + my $udev_bin = "./test-udev"; + my $valgrind = 0; +@@ -2243,14 +2244,26 @@ sub check_add { + + my $devnode = check_devnode($device); + +- print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n"; + return if (!defined($device->{exp_name})); + +- if ((-e "$udev_dev/$device->{exp_name}") || +- (-l "$udev_dev/$device->{exp_name}")) { +- print "add $device->{devpath}: ok\n"; ++ my @st = lstat("$udev_dev/$device->{exp_name}"); ++ if (-l _) { ++ my $cwd = getcwd(); ++ my $dir = "$udev_dev/$device->{exp_name}"; ++ $dir =~ s!/[^/]*$!!; ++ my $tgt = readlink("$udev_dev/$device->{exp_name}"); ++ $tgt = abs_path("$dir/$tgt"); ++ $tgt =~ s!^$cwd/!!; ++ ++ if ($tgt ne $devnode) { ++ print "symlink $device->{exp_name}: error, found -> $tgt\n"; ++ $error++; ++ system("tree", "$udev_dev"); ++ } else { ++ print "symlink $device->{exp_name}: ok\n"; ++ } + } else { +- print "add $device->{devpath}: error"; ++ print "symlink $device->{exp_name}: error"; + if ($device->{exp_add_error}) { + print " as expected\n"; + } else { diff --git a/SOURCES/0530-test-udev-test.pl-allow-checking-multiple-symlinks.patch b/SOURCES/0530-test-udev-test.pl-allow-checking-multiple-symlinks.patch new file mode 100644 index 0000000..f004054 --- /dev/null +++ b/SOURCES/0530-test-udev-test.pl-allow-checking-multiple-symlinks.patch @@ -0,0 +1,1607 @@ +From fb8d10456d7d5a085e1adb5bfd45f1cda813ac22 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 17:15:58 +0200 +Subject: [PATCH] test/udev-test.pl: allow checking multiple symlinks + +Instead of testing the existence or non-exisitence of just a single +symlink, allow testing of several links per device. + +Change the test definitions accordingly. + +(cherry picked from commit e62acc3159935781f05fa59c48e5a74e85c61ce2) + +Related: #1642728 +--- + test/udev-test.pl | 495 +++++++++++++++++++++++++++------------------- + 1 file changed, 296 insertions(+), 199 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 2e3089c5e0..f5edecefd0 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -71,7 +71,7 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk" , ++ exp_links => ["boot_disk"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk" , ++ exp_links => ["boot_disk"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "boot_disk" , ++ exp_links => ["boot_disk"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_links => ["boot_disk1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_links => ["boot_disk1", "boot_disk1-4", "boot_disk1-5"], ++ not_exp_links => ["boot_disk1-1", "boot_disk1-2", "boot_disk1-3"] + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_links => ["boot_disk1"], ++ not_exp_links => ["boot_diskX1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "boot_disk1" , ++ exp_links => ["boot_disk1", "boot_diskXY1"], ++ not_exp_links => ["boot_diskXX1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_links => ["modem/0", "catch-all"], + }], + rules => < "catch device by * - take 2", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_links => ["modem/0"], ++ not_exp_links => ["bad"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_links => ["modem/0"], ++ not_exp_links => ["modem/0-1", "modem/0-2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem/0" , ++ exp_links => ["modem/0"], ++ not_exp_links => ["modem/0-1", "modem/0-2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "whitespace" , ++ exp_links => ["whitespace"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "aaa", ++ exp_links => ["aaa"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "sub/direct/ory/modem" , ++ exp_links => ["sub/direct/ory/modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "first_disk5" , ++ exp_links => ["first_disk5"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0" , ++ exp_links => ["Major:8:minor:5:kernelnumber:5:id:0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node12345678", ++ exp_links => ["node12345678"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "disk-ATA-sda" , ++ exp_links => ["disk-ATA-sda"], ++ not_exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "special-device-5" , +- not_exp_name => "not" , ++ exp_links => ["special-device-5"], ++ not_exp_links => ["not"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "newline_removed" , ++ exp_links => ["newline_removed"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "test-0:0:0:0" , ++ exp_links => ["test-0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo9" , ++ exp_links => ["foo9"], ++ not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "bar9" , ++ exp_links => ["bar9"], ++ not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo7", "foo8"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo7" , ++ exp_links => ["foo7"], ++ not_exp_links => ["foo3", "foo4", "foo5", "foo6", "foo8"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2" , ++ exp_links => ["foo2"], ++ not_exp_links => ["foo1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2" , ++ exp_links => ["foo2"], ++ not_exp_links => ["foo1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "foo2" , ++ exp_links => ["foo2"], ++ not_exp_links => ["foo1", "foo3"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "my-foo9" , ++ exp_links => ["my-foo9"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "my-foo8" , ++ exp_links => ["my-foo8"], ++ not_exp_links => ["my-foo3", "my-foo4", "my-foo5", "my-foo6", "my-foo7", "my-foo9"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", ++ exp_links => ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "Major:8-minor:5-kernelnumber:5-id:0:0:0:0", ++ exp_links => ["Major:8-minor:5-kernelnumber:5-id:0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "850:0:0:05" , ++ exp_links => ["850:0:0:05"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "855" , ++ exp_links => ["855"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "8550:0:0:0" , ++ exp_links => ["8550:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/virtual/tty/console", +- exp_name => "TTY", ++ exp_links => ["TTY"], ++ not_exp_links => ["foo"], + }], + rules => < [ + { + devpath => "/devices/virtual/tty/console", +- exp_name => "TTY" , ++ exp_links => ["TTY"], ++ not_exp_links => ["foo"], + }], + rules => < [ + { + devpath => "/devices/virtual/tty/console", +- exp_name => "foo" , ++ exp_links => ["foo", "TTY"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "empty" , ++ exp_links => ["empty", "not-something"], ++ not_exp_links => ["something", "not-empty"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "non-existent" , ++ exp_links => ["non-existent", "wrong"], ++ not_exp_links => ["something", "empty", "not-empty", ++ "not-something", "something"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0" , ++ exp_links => ["scsi-0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem" , ++ exp_links => ["modem"], + }], + rules => < "/devices/virtual/block/fake!blockdev0", + devnode => "fake/blockdev0", +- exp_name => "is/a/fake/blockdev0" , ++ exp_links => ["is/a/fake/blockdev0"], ++ not_exp_links => ["is/not/a/fake/blockdev0", "modem"], + }], + rules => < "/devices/virtual/block/fake!blockdev0", + devnode => "fake/blockdev0", +- exp_rem_error => "yes", ++ not_exp_links => ["modem"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0", ++ exp_links => ["scsi-0:0:0:0"], ++ not_exp_links => ["no-match", "short-id", "not-scsi"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0", ++ exp_links => ["scsi-0:0:0:0"], ++ not_exp_links => ["no-match", "before"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0", ++ exp_links => ["scsi-0:0:0:0", "before"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "scsi-0:0:0:0", ++ exp_links => ["scsi-0:0:0:0", "before"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "driver-is-sd", ++ exp_links => ["driver-is-sd"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "driver-is-ahci", ++ exp_links => ["driver-is-ahci"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ignored", ++ exp_links => ["ignored"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "matched-with-space", ++ exp_links => ["matched-with-space"], ++ not_exp_links => ["wrong-to-ignore"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "1::0600", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => ":1:0660", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "daemon::0600", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => ":daemon:0660", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "root:audio:0660", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "::0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_perms => "1:1:0777", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + exp_majorminor => "8:0", + }], + rules => < [ + { + devpath => "/devices/virtual/misc/misc-fake1", +- exp_name => "node", ++ exp_links => ["node"], + exp_majorminor => "4095:1", + }], + rules => < [ + { + devpath => "/devices/virtual/misc/misc-fake89999", +- exp_name => "node", ++ exp_links => ["node"], + exp_majorminor => "4095:89999", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink2-ttyACM0", ++ exp_links => ["symlink1-0", "symlink2-ttyACM0", "symlink3-"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "one", +- not_exp_name => " ", ++ exp_links => ["one", "two"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < "symlink with space and var with space, part 1", ++ desc => "symlink with space and var with space", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "first", +- not_exp_name => " ", ++ exp_links => ["first"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "name-one_two_three-end", +- not_exp_name => " ", ++ exp_links => ["name-one_two_three-end"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "another_symlink", +- not_exp_name => " ", ++ exp_links => ["another_symlink"], ++ not_exp_links => [" "], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "modem0", ++ exp_links => ["modem0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "second-0" , ++ exp_links => ["first-0", "second-0", "third-0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => ".", ++ exp_links => ["."], + exp_add_error => "yes", + exp_rem_error => "yes", + }], +@@ -1148,7 +1176,7 @@ EOF + devices => [ + { + devpath => "/devices/virtual/tty/tty0", +- exp_name => "link", ++ exp_links => ["link"], + exp_add_error => "yes", + exp_rem_error => "yes", + }], +@@ -1162,7 +1190,7 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink0", ++ exp_links => ["symlink0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "symlink-ttyACM0", ++ exp_links => ["symlink-ttyACM0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "major-166:0", ++ exp_links => ["major-166:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "symlink-0:0:0:0", ++ exp_links => ["symlink-0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "test", ++ exp_links => ["test"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "test", ++ exp_links => ["test"], ++ not_exp_links => ["symlink", "this"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "this", ++ exp_links => ["test", "this"], ++ not_exp_links => ["symlink"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "test", ++ exp_links => ["test", "this"], ++ not_exp_links => ["symlink"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "166:0", ++ exp_links => ["166:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "link1", ++ exp_links => ["link1", "link2"], ++ not_exp_links => ["node"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", +- exp_name => "link4", ++ exp_links => ["link1", "link2", "link3", "link4"], ++ not_exp_links => ["node"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], ++ not_exp_links => ["should_not_match", "should_not_match2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], ++ not_exp_links => ["should_not_match"] + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node", ++ exp_links => ["node"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sda-part-1", ++ exp_links => ["sda-part-1"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "start-/dev-end", ++ exp_links => ["start-/dev-end"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "last", ++ exp_links => ["last"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "match", ++ exp_links => ["match", "before"], ++ not_exp_links => ["matches-but-is-negated"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "not-anything", ++ exp_links => ["before", "not-anything"], ++ not_exp_links => ["matches-but-is-negated"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "nonzero-program", ++ exp_links => ["before", "nonzero-program"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ exp_links => ["true"], ++ not_exp_links => ["bad", "wrong"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ exp_links => ["true"], ++ not_exp_links => ["bad", "wrong", "no"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ exp_links => ["true", "before"], ++ not_exp_links => ["no"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "true", ++ exp_links => ["true", "before"], ++ not_exp_links => ["no", "bad"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "part", +- }], ++ exp_links => ["part"], ++ not_exp_links => ["disk"], ++ }, ++ ], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "sane", ++ exp_links => ["sane"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "uber", ++ exp_links => ["uber"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "replaced", ++ exp_links => ["replaced"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "serial-354172020305000", ++ exp_links => ["serial-354172020305000"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ exp_links => ["ok"], ++ not_exp_links => ["not-1-ok", "not-2-ok", "not-3-ok"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ exp_links => ["ok"], ++ not_exp_links => ["unknown-not-ok"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ exp_links => ["ok"], + exp_perms => "root:tty:0640", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "ok", ++ exp_links => ["ok"], + exp_perms => "root:tty:0640", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "node-add-me", ++ exp_links => ["node-add-me"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "three", +- not_exp_name => "two", ++ exp_links => ["three"], ++ not_exp_links => ["two", "one"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", +- not_exp_name => "wrong", ++ exp_links => ["right"], ++ not_exp_links => ["wrong"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ exp_links => ["right", "before"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ exp_links => ["right", "before"], ++ not_exp_links => ["nomatch"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ exp_links => ["right"], ++ not_exp_links => ["nomatch", "wrong1", "wrong2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_name => "right", ++ exp_links => ["right"], ++ not_exp_links => ["nomatch", "wrong1", "wrong2", "wrong3"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "parent", ++ exp_links => ["parent"], + }], + option => "keep", + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "parentenv-parent_right", ++ exp_links => ["parentenv-parent_right"], + }], + option => "clean", + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", ++ exp_links => ["right"], ++ not_exp_test => ["wrong", "wrong2"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", ++ exp_links => ["right"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "right", +- not_exp_name => "wrong", ++ exp_links => ["right", "link"], ++ not_exp_links => ["wrong"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "yes", ++ exp_links => ["yes"], ++ not_exp_links => ["no"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "yes", ++ exp_links => ["yes"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +- exp_name => "yes", ++ exp_links => ["yes"], ++ not_exp_links => ["no"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "00:16:41:e2:8d:ff", ++ exp_links => ["00:16:41:e2:8d:ff"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "there", ++ exp_links => ["there"], ++ not_exp_links => ["notthere"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "yes", ++ exp_links => ["yes"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "relative", ++ exp_links => ["relative"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found-subdir", ++ exp_links => ["found-subdir"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "sda-8741C4G-end", ++ exp_links => ["sda-8741C4G-end"], + exp_perms => "0:0:0600", + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0", ++ exp_links => ["disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", +- not_exp_name => "bad" , ++ exp_links => ["found"], ++ not_exp_links => ["bad"], + }], + rules => < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + }], + rules => $rules_10k_tags . < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => $rules_10k_tags_continuation . < [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + + }], +@@ -2046,7 +2098,7 @@ TAGS=="aaa", SYMLINK+="bad" + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", +- exp_name => "found", ++ exp_links => ["found"], + not_exp_name => "bad", + }], + rules => <{not_exp_name})) { +- if ((-e "$udev_dev/$device->{not_exp_name}") || +- (-l "$udev_dev/$device->{not_exp_name}")) { +- print "nonexistent: error \'$device->{not_exp_name}\' not expected to be there\n"; +- $error++; +- sleep(1); +- } +- } ++sub get_link_target { ++ my ($link) = @_; + +- my $devnode = check_devnode($device); ++ my $cwd = getcwd(); ++ my $dir = "$udev_dev/$link"; ++ my $tgt = readlink("$udev_dev/$link"); ++ $dir =~ s!/[^/]*$!!; ++ $tgt = abs_path("$dir/$tgt"); ++ $tgt =~ s!^$cwd/!!; ++ return $tgt; ++} + +- return if (!defined($device->{exp_name})); ++sub check_link_add { ++ my ($link, $devnode, $err_expected) = @_; + +- my @st = lstat("$udev_dev/$device->{exp_name}"); ++ my @st = lstat("$udev_dev/$link"); + if (-l _) { +- my $cwd = getcwd(); +- my $dir = "$udev_dev/$device->{exp_name}"; +- $dir =~ s!/[^/]*$!!; +- my $tgt = readlink("$udev_dev/$device->{exp_name}"); +- $tgt = abs_path("$dir/$tgt"); +- $tgt =~ s!^$cwd/!!; ++ my $tgt = get_link_target($link); + + if ($tgt ne $devnode) { +- print "symlink $device->{exp_name}: error, found -> $tgt\n"; ++ print "symlink $link: error, found -> $tgt\n"; + $error++; + system("tree", "$udev_dev"); + } else { +- print "symlink $device->{exp_name}: ok\n"; ++ print "symlink $link: ok\n"; + } + } else { +- print "symlink $device->{exp_name}: error"; +- if ($device->{exp_add_error}) { ++ print "symlink $link: error"; ++ if ($err_expected) { + print " as expected\n"; + } else { + print "\n"; +@@ -2276,6 +2322,49 @@ sub check_add { + } + } + ++sub check_link_nonexistent { ++ my ($link, $devnode, $err_expected) = @_; ++ ++ if ((-e "$udev_dev/$link") || (-l "$udev_dev/$link")) { ++ my $tgt = get_link_target($link); ++ ++ if ($tgt ne $devnode) { ++ print "nonexistent: '$link' points to other device (ok)\n"; ++ } else { ++ print "nonexistent: error \'$link\' should not be there"; ++ if ($err_expected) { ++ print " (as expected)\n"; ++ } else { ++ print "\n"; ++ system("tree", "$udev_dev"); ++ print "\n"; ++ $error++; ++ sleep(1); ++ } ++ } ++ } else { ++ print "nonexistent $link: ok\n"; ++ } ++} ++ ++sub check_add { ++ my ($device) = @_; ++ my $devnode = check_devnode($device); ++ ++ if (defined($device->{exp_links})) { ++ foreach my $link (@{$device->{exp_links}}) { ++ check_link_add($link, $devnode, ++ $device->{exp_add_error}); ++ } ++ } ++ if (defined $device->{not_exp_links}) { ++ foreach my $link (@{$device->{not_exp_links}}) { ++ check_link_nonexistent($link, $devnode, ++ $device->{exp_nodev_error}); ++ } ++ } ++} ++ + sub check_remove_devnode { + my ($device) = @_; + my $devnode = get_devnode($device); +@@ -2292,17 +2381,13 @@ sub check_remove_devnode { + } + } + +-sub check_remove { +- my ($device) = @_; ++sub check_link_remove { ++ my ($link, $err_expected) = @_; + +- check_remove_devnode($device); +- +- return if (!defined($device->{exp_name})); +- +- if ((-e "$udev_dev/$device->{exp_name}") || +- (-l "$udev_dev/$device->{exp_name}")) { +- print "remove $device->{exp_name}: error"; +- if ($device->{exp_rem_error}) { ++ if ((-e "$udev_dev/$link") || ++ (-l "$udev_dev/$link")) { ++ print "remove $link: error"; ++ if ($err_expected) { + print " as expected\n"; + } else { + print "\n"; +@@ -2312,7 +2397,19 @@ sub check_remove { + sleep(1); + } + } else { +- print "remove $device->{exp_name}: ok\n"; ++ print "remove $link: ok\n"; ++ } ++} ++ ++sub check_remove { ++ my ($device) = @_; ++ ++ check_remove_devnode($device); ++ ++ return if (!defined($device->{exp_links})); ++ ++ foreach my $link (@{$device->{exp_links}}) { ++ check_link_remove($link, $device->{exp_rem_error}); + } + } + diff --git a/SOURCES/0531-test-udev-test.pl-fix-wrong-test-descriptions.patch b/SOURCES/0531-test-udev-test.pl-fix-wrong-test-descriptions.patch new file mode 100644 index 0000000..9c2ee94 --- /dev/null +++ b/SOURCES/0531-test-udev-test.pl-fix-wrong-test-descriptions.patch @@ -0,0 +1,83 @@ +From 0e0b90ffcf0731865846bfa2754a809cc2b8c53e Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 17:57:47 +0200 +Subject: [PATCH] test/udev-test.pl: fix wrong test descriptions + +udev hasn't supported renaming device nodes for some time. + +(cherry picked from commit 46bc71b2b73f8a1e27dc5e142730e9877dd05e3e) + +Related: #1642728 +--- + test/udev-test.pl | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index f5edecefd0..d5d0e130e3 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -212,7 +212,7 @@ KERNEL=="ttyACM[0-9]*", SYMLINK+="modem/%n" + EOF + }, + { +- desc => "replace kernel name", ++ desc => "don't replace kernel name", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -223,7 +223,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem" + EOF + }, + { +- desc => "Handle comment lines in config file (and replace kernel name)", ++ desc => "Handle comment lines in config file (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -236,7 +236,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem" + EOF + }, + { +- desc => "Handle comment lines in config file with whitespace (and replace kernel name)", ++ desc => "Handle comment lines in config file with whitespace (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -249,7 +249,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem" + EOF + }, + { +- desc => "Handle whitespace only lines (and replace kernel name)", ++ desc => "Handle whitespace only lines (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -267,7 +267,7 @@ KERNEL=="ttyACM0", SYMLINK+="whitespace" + EOF + }, + { +- desc => "Handle empty lines in config file (and replace kernel name)", ++ desc => "Handle empty lines in config file (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -280,7 +280,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem" + EOF + }, + { +- desc => "Handle backslashed multi lines in config file (and replace kernel name)", ++ desc => "Handle backslashed multi lines in config file (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +@@ -303,8 +303,9 @@ EOF + KERNEL=="ttyACM0", PROGRAM=="/bin/echo -e \\101", RESULT=="A", SYMLINK+="aaa" + EOF + }, ++ # 20 + { +- desc => "Handle stupid backslashed multi lines in config file (and replace kernel name)", ++ desc => "Handle stupid backslashed multi lines in config file (and don't replace kernel name)", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", diff --git a/SOURCES/0532-test-udev-test.pl-last_rule-is-unsupported.patch b/SOURCES/0532-test-udev-test.pl-last_rule-is-unsupported.patch new file mode 100644 index 0000000..72021fb --- /dev/null +++ b/SOURCES/0532-test-udev-test.pl-last_rule-is-unsupported.patch @@ -0,0 +1,35 @@ +From 2d0b828715e67f7accda6f73481deb74febebcb6 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 18:08:18 +0200 +Subject: [PATCH] test/udev-test.pl: last_rule is unsupported + +the "last_rule" option hasn't been supported for some time. +Therefore this test fails if a "not_exp_links" attribute is added, +as it should be. Mark it appropriately. + +(cherry picked from commit 17cce031531a5d3f38a27374c99d1bdba5959dbd) + +Related: #1642728 +--- + test/udev-test.pl | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index d5d0e130e3..a9c2dd95f1 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1373,11 +1373,14 @@ SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="start-%r-end" + EOF + }, + { ++ # This is not supported any more + desc => "last_rule option", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_links => ["last"], ++ not_exp_links => ["very-last"], ++ exp_nodev_error => "yes", + }], + rules => < +Date: Tue, 24 Apr 2018 18:09:50 +0200 +Subject: [PATCH] test/udev-test.pl: Make some tests a little harder + +Add some rules that make it a bit harder to pass, mainly the +non-existence checks. + +(cherry picked from commit 06d4d4e24e7d0b51120b165e540d278842e8b1a3) + +Related: #1642728 +--- + test/udev-test.pl | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index a9c2dd95f1..7465b5859e 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1358,7 +1358,7 @@ EOF + exp_links => ["sda-part-1"], + }], + rules => < ["part"], + not_exp_links => ["disk"], + }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", ++ exp_links => ["disk"], ++ not_exp_links => ["part"], ++ }, + ], + rules => < < < "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_perms => "1:1:0400", +- exp_rem_error => "yes", + }], + rules => < +Date: Tue, 24 Apr 2018 18:16:59 +0200 +Subject: [PATCH] test/udev-test.pl: remove bogus rules from magic subsys test + +These rules have survived from an ancient version of the code +and save no purpose any more. + +(cherry picked from commit 86634df43b715f3f77c7de73a3ef6566e5cdf571) + +Related: #1642728 +--- + test/udev-test.pl | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 7465b5859e..6928439d14 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2017,8 +2017,6 @@ EOF + exp_perms => "0:0:0600", + }], + rules => < +Date: Tue, 24 Apr 2018 18:27:25 +0200 +Subject: [PATCH] test/udev-test.pl: merge "space and var with space" tests + +As we can check multiple links in a single test now, these 3 +tests can be merged into one. + +(cherry picked from commit 2084fe0d3290c525ecb9faa07d07c3abc2488e59) + +Related: #1642728 +--- + test/udev-test.pl | 31 +++---------------------------- + 1 file changed, 3 insertions(+), 28 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 6928439d14..880a73b10b 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1103,34 +1103,9 @@ EOF + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_links => ["first"], +- not_exp_links => [" "], +- }], +- rules => < "symlink with space and var with space, part 2", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_links => ["name-one_two_three-end"], +- not_exp_links => [" "], +- }], +- rules => < "symlink with space and var with space, part 3", +- devices => [ +- { +- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", +- exp_links => ["another_symlink"], +- not_exp_links => [" "], ++ exp_links => ["first", "name-one_two_three-end", ++ "another_symlink", "a", "b", "c"], ++ not_exp_links => [" "], + }], + rules => < +Date: Tue, 24 Apr 2018 18:30:09 +0200 +Subject: [PATCH] test/udev-test.pl: merge import parent tests into one + +As we can test multiple devices and multiple links per device +in one test now, these two tests can be merged into one. + +(cherry picked from commit a96cd21d31cb7af211862768e133b50b085634e7) + +Related: #1642728 +--- + test/udev-test.pl | 17 +++++------------ + 1 file changed, 5 insertions(+), 12 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 880a73b10b..0344d2e89c 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -1781,28 +1781,21 @@ TAGS=="aaa||bbb||ccc", SYMLINK+="bad" + EOF + }, + { +- desc => "IMPORT parent test sequence 1/2 (keep)", ++ desc => "IMPORT parent test", + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_links => ["parent"], +- }], +- option => "keep", +- rules => < "IMPORT parent test sequence 2/2 (keep)", +- devices => [ ++ }, + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", + exp_links => ["parentenv-parent_right"], + }], +- option => "clean", ++ sleep_us => 500000, # Serialized! We need to sleep here after adding sda + rules => < +Date: Tue, 24 Apr 2018 20:55:01 +0200 +Subject: [PATCH] test/udev-test.pl: count "good" results + +This is helpful to catch possible regressions in the test. +Also, don't count wait() errors, they are likely not udev errors. + +(cherry picked from commit b95c43982ab7d0253b552ad56cffb3d68fcbb4f6) + +Related: #1642728 +--- + test/udev-test.pl | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 0344d2e89c..813be70739 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2114,6 +2114,7 @@ sub udev { + } + + my $error = 0; ++my $good = 0; + + sub permissions_test { + my($rules, $uid, $gid, $mode) = @_; +@@ -2144,6 +2145,7 @@ sub permissions_test { + } + if ($wrong == 0) { + print "permissions: ok\n"; ++ $good++; + } else { + printf " expected permissions are: %s:%s:%#o\n", $1, $2, oct($3); + printf " created permissions are : %i:%i:%#o\n", $uid, $gid, $mode & 07777; +@@ -2169,6 +2171,7 @@ sub major_minor_test { + } + if ($wrong == 0) { + print "major:minor: ok\n"; ++ $good++; + } else { + printf " expected major:minor is: %i:%i\n", $1, $2; + printf " created major:minor is : %i:%i\n", $major, $minor; +@@ -2254,6 +2257,7 @@ sub check_devnode { + major_minor_test($device, $rdev); + } + print "add $devnode: ok\n"; ++ $good++; + return $devnode; + } + +@@ -2282,11 +2286,13 @@ sub check_link_add { + system("tree", "$udev_dev"); + } else { + print "symlink $link: ok\n"; ++ $good++; + } + } else { + print "symlink $link: error"; + if ($err_expected) { + print " as expected\n"; ++ $good++; + } else { + print "\n"; + system("tree", "$udev_dev"); +@@ -2305,10 +2311,12 @@ sub check_link_nonexistent { + + if ($tgt ne $devnode) { + print "nonexistent: '$link' points to other device (ok)\n"; ++ $good++; + } else { + print "nonexistent: error \'$link\' should not be there"; + if ($err_expected) { + print " (as expected)\n"; ++ $good++; + } else { + print "\n"; + system("tree", "$udev_dev"); +@@ -2319,6 +2327,7 @@ sub check_link_nonexistent { + } + } else { + print "nonexistent $link: ok\n"; ++ $good++; + } + } + +@@ -2353,6 +2362,7 @@ sub check_remove_devnode { + sleep(1); + } else { + print "remove $devnode: ok\n"; ++ $good++; + } + } + +@@ -2364,6 +2374,7 @@ sub check_link_remove { + print "remove $link: error"; + if ($err_expected) { + print " as expected\n"; ++ $good++; + } else { + print "\n"; + system("tree", "$udev_dev"); +@@ -2373,6 +2384,7 @@ sub check_link_remove { + } + } else { + print "remove $link: ok\n"; ++ $good++; + } + } + +@@ -2432,7 +2444,6 @@ sub fork_and_run_udev { + $pid = waitpid($dev->{pid}, 0); + if ($pid == -1) { + print "error waiting for pid dev->{pid}\n"; +- $error += 1; + } + if (WIFEXITED($?)) { + $rc = WEXITSTATUS($?); +@@ -2440,6 +2451,8 @@ sub fork_and_run_udev { + if ($rc) { + print "$udev_bin $action for $dev->{devpath} failed with code $rc\n"; + $error += 1; ++ } else { ++ $good++; + } + } + } +@@ -2549,7 +2562,7 @@ if ($list[0]) { + } + + $sema->remove; +-print "$error errors occurred\n\n"; ++print "$error errors occurred. $good good results.\n\n"; + + cleanup(); + diff --git a/SOURCES/0538-tests-udev-test.pl-add-multiple-device-test.patch b/SOURCES/0538-tests-udev-test.pl-add-multiple-device-test.patch new file mode 100644 index 0000000..e01add4 --- /dev/null +++ b/SOURCES/0538-tests-udev-test.pl-add-multiple-device-test.patch @@ -0,0 +1,199 @@ +From 8ab9d11b925e7f39b350ce69a1e28752de411b35 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Tue, 24 Apr 2018 22:04:55 +0200 +Subject: [PATCH] tests/udev-test.pl: add multiple device test + +Add 4 new tests using multiple devices. Number 2-4 use many +devices claiming the same symlink, where only one device has +a higher priority thatn the others. They fail sporadically with +the current code, if a race condition causes the symlink to point +to the wrong device. Test 4 is like test 2 with sleeps in between, +it's much less likely to fail. + +(cherry picked from commit 4a0ec82daf32446519e1d86329bb802325b82104) + +Related: #1642728 +--- + test/udev-test.pl | 169 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 169 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 813be70739..d964c664b6 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2085,6 +2085,175 @@ KERNEL=="sda", TAG+="aaa" \\ + KERNEL=="sdb", TAG+="bbb" + TAGS=="foo", SYMLINK+="found" + TAGS=="aaa", SYMLINK+="bad" ++EOF ++ }, ++ { ++ desc => "multiple devices", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_links => ["part-1"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_links => ["part-5"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", ++ exp_links => ["part-6"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", ++ exp_links => ["part-7"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", ++ exp_links => ["part-8"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", ++ exp_links => ["part-9"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", ++ exp_links => ["part-10"], ++ }, ++ ], ++ rules => < "multiple devices, same link name, positive prio", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_links => ["part-1"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_links => ["part-5"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", ++ not_exp_links => ["partition"], ++ exp_links => ["part-6"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", ++ exp_links => ["part-7", "partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", ++ not_exp_links => ["partition"], ++ exp_links => ["part-8"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", ++ not_exp_links => ["partition"], ++ exp_links => ["part-9"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", ++ not_exp_links => ["partition"], ++ exp_links => ["part-10"], ++ }, ++ ], ++ rules => < "multiple devices, same link name, negative prio", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_links => ["part-1"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_links => ["part-5"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", ++ not_exp_links => ["partition"], ++ exp_links => ["part-6"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", ++ exp_links => ["part-7", "partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", ++ not_exp_links => ["partition"], ++ exp_links => ["part-8"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", ++ not_exp_links => ["partition"], ++ exp_links => ["part-9"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", ++ not_exp_links => ["partition"], ++ exp_links => ["part-10"], ++ }, ++ ], ++ rules => < "multiple devices, same link name, positive prio, sleep", ++ devices => [ ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", ++ exp_links => ["part-1"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", ++ exp_links => ["part-5"], ++ not_exp_links => ["partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda6", ++ not_exp_links => ["partition"], ++ exp_links => ["part-6"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda7", ++ exp_links => ["part-7", "partition"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda8", ++ not_exp_links => ["partition"], ++ exp_links => ["part-8"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda9", ++ not_exp_links => ["partition"], ++ exp_links => ["part-9"], ++ }, ++ { ++ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda10", ++ not_exp_links => ["partition"], ++ exp_links => ["part-10"], ++ }, ++ ], ++ sleep_us => 10000, ++ rules => < +Date: Tue, 24 Apr 2018 22:24:43 +0200 +Subject: [PATCH] test/udev-test.pl: add repeat count + +for easier reproduction of sporadic test failures. + +(cherry picked from commit 2ab0a8d00bc48d3531e953d938db889d8a932d65) + +Related: #1642728 +--- + test/udev-test.pl | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index d964c664b6..8b1ab3c06c 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2125,6 +2125,7 @@ EOF + }, + { + desc => "multiple devices, same link name, positive prio", ++ repeat => 100, + devices => [ + { + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", +@@ -2635,6 +2636,7 @@ sub run_test { + print "TEST $number: $rules->{desc}\n"; + create_rules(\$rules->{rules}); + ++ REPEAT: + fork_and_run_udev("add", $rules, $sema); + + foreach my $dev (@devices) { +@@ -2653,6 +2655,9 @@ sub run_test { + } + + print "\n"; ++ if (defined($rules->{repeat}) && --($rules->{repeat}) > 0) { ++ goto REPEAT; ++ } + + if (defined($rules->{option}) && $rules->{option} eq "clean") { + udev_setup(); diff --git a/SOURCES/0540-test-udev-test.pl-generator-for-large-list-of-block-.patch b/SOURCES/0540-test-udev-test.pl-generator-for-large-list-of-block-.patch new file mode 100644 index 0000000..02e407d --- /dev/null +++ b/SOURCES/0540-test-udev-test.pl-generator-for-large-list-of-block-.patch @@ -0,0 +1,101 @@ +From 6c3191e979165700f98903b76621c214186a110c Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Wed, 25 Apr 2018 09:54:26 +0200 +Subject: [PATCH] test/udev-test.pl: generator for large list of block devices + +Manually listing all devices in the test definition becomes cumbersome with +lots of devices. Add a function that scans on all block devices in +the test sysfs and generates a list of devices to test. + +(cherry picked from commit eb44d715ebee2fe11288433b99f8e1dc5fdac84a) + +Related: #1642728 +--- + test/udev-test.pl | 60 ++++++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 59 insertions(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 8b1ab3c06c..2866fdb77a 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -50,6 +50,50 @@ for (my $i = 1; $i < 10000; ++$i) { + } + $rules_10k_tags_continuation .= "TAG+=\"test10000\"\\n"; + ++# Create a device list with all block devices under /sys ++# (except virtual devices and cd-roms) ++# the optional argument exp_func returns expected and non-expected ++# symlinks for the device. ++sub all_block_devs { ++ my ($exp_func) = @_; ++ my @devices; ++ ++ foreach my $bd (glob "$udev_sys/dev/block/*") { ++ my $tgt = readlink($bd); ++ my ($exp, $notexp) = (undef, undef); ++ ++ next if ($tgt =~ m!/virtual/! || $tgt =~ m!/sr[0-9]*$!); ++ ++ $tgt =~ s!^\.\./\.\.!!; ++ ($exp, $notexp) = $exp_func->($tgt) if defined($exp_func); ++ my $device = { ++ devpath => $tgt, ++ exp_links => $exp, ++ not_exp_links => $notexp, ++ }; ++ push(@devices, $device); ++ } ++ return \@devices; ++} ++ ++# This generator returns a suitable exp_func for use with ++# all_block_devs(). ++sub expect_for_some { ++ my ($pattern, $links, $donot) = @_; ++ my $_expect = sub { ++ my ($name) = @_; ++ ++ if ($name =~ /$pattern/) { ++ return ($links, undef); ++ } elsif ($donot) { ++ return (undef, $links); ++ } else { ++ return (undef, undef); ++ } ++ }; ++ return $_expect; ++} ++ + my @tests = ( + { + desc => "no rules", +@@ -2257,6 +2301,15 @@ SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sda?*", ENV{DEVTYPE}=="partitio + KERNEL=="*7", OPTIONS+="link_priority=10" + EOF + }, ++ { ++ desc => 'all_block_devs', ++ generator => expect_for_some("\\/sda6\$", ["blockdev"]), ++ repeat => 10, ++ rules => <{devices}}; ++ my @devices; ++ ++ if (!defined $rules->{devices}) { ++ $rules->{devices} = all_block_devs($rules->{generator}); ++ } ++ @devices = @{$rules->{devices}}; + + print "TEST $number: $rules->{desc}\n"; + create_rules(\$rules->{rules}); diff --git a/SOURCES/0541-test-udev-test.pl-suppress-umount-error-message-at-s.patch b/SOURCES/0541-test-udev-test.pl-suppress-umount-error-message-at-s.patch new file mode 100644 index 0000000..fbe9d4b --- /dev/null +++ b/SOURCES/0541-test-udev-test.pl-suppress-umount-error-message-at-s.patch @@ -0,0 +1,29 @@ +From 453df9eb2bbfa34f3e4b78e917812f0ac6958010 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Thu, 26 Apr 2018 13:25:11 +0200 +Subject: [PATCH] test/udev-test.pl: suppress umount error message at startup + +umount emits an error message "no mount point specified" if the +tmpfs isn't mounted yet, which is the normal case. +Suppress that by redirecting stderr. + +(cherry picked from commit f1cb0860549e775be5f91237b5a3b97698dd14dd) + +Related: #1642728 +--- + test/udev-test.pl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 2866fdb77a..33a76ad292 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2405,7 +2405,7 @@ sub major_minor_test { + } + + sub udev_setup { +- system("umount", $udev_tmpfs); ++ system("umount \"$udev_tmpfs\" 2>/dev/null"); + rmdir($udev_tmpfs); + mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n"; + diff --git a/SOURCES/0542-test-udev_test.pl-add-expected-good-count.patch b/SOURCES/0542-test-udev_test.pl-add-expected-good-count.patch new file mode 100644 index 0000000..d1f61ca --- /dev/null +++ b/SOURCES/0542-test-udev_test.pl-add-expected-good-count.patch @@ -0,0 +1,78 @@ +From e0cee95e0cc401ce120a1b56cdb7a8b9afbd6bcf Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Thu, 26 Apr 2018 14:07:27 +0200 +Subject: [PATCH] test/udev_test.pl: add "expected good" count + +Since 'test/udev-test.pl: count "good" results', we know how many +checks succeeded. Add an "expected good" count to make that number +more meaningful. + +(cherry picked from commit cbeb23d863d540408cd1fb274d78213f59639df2) + +Related: #1642728 +--- + test/udev-test.pl | 21 +++++++++++++++++++-- + 1 file changed, 19 insertions(+), 2 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index 33a76ad292..cf6ca6b80c 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -2338,6 +2338,7 @@ sub udev { + + my $error = 0; + my $good = 0; ++my $exp_good = 0; + + sub permissions_test { + my($rules, $uid, $gid, $mode) = @_; +@@ -2685,12 +2686,27 @@ sub run_test { + my ($rules, $number, $sema) = @_; + my $rc; + my @devices; ++ my $ntests; ++ my $cur_good = $good; ++ my $cur_error = $error; + + if (!defined $rules->{devices}) { + $rules->{devices} = all_block_devs($rules->{generator}); + } + @devices = @{$rules->{devices}}; ++ # For each device: exit status and devnode test for add & remove ++ $ntests += 4 * ($#devices + 1); + ++ foreach my $dev (@devices) { ++ $ntests += 2 * ($#{$dev->{exp_links}} + 1) ++ + ($#{$dev->{not_exp_links}} + 1) ++ + (defined $dev->{exp_perms} ? 1 : 0) ++ + (defined $dev->{exp_majorminor} ? 1 : 0); ++ } ++ if (defined $rules->{repeat}) { ++ $ntests *= $rules->{repeat}; ++ } ++ $exp_good += $ntests; + print "TEST $number: $rules->{desc}\n"; + create_rules(\$rules->{rules}); + +@@ -2712,10 +2728,11 @@ sub run_test { + check_remove($dev); + } + +- print "\n"; + if (defined($rules->{repeat}) && --($rules->{repeat}) > 0) { + goto REPEAT; + } ++ printf "TEST $number: errors: %d good: %d/%d\n\n", $error-$cur_error, ++ $good-$cur_good, $ntests; + + if (defined($rules->{option}) && $rules->{option} eq "clean") { + udev_setup(); +@@ -2794,7 +2811,7 @@ if ($list[0]) { + } + + $sema->remove; +-print "$error errors occurred. $good good results.\n\n"; ++print "$error errors occurred. $good/$exp_good good results.\n\n"; + + cleanup(); + diff --git a/SOURCES/0543-test-udev-test-gracefully-exit-when-imports-fail.patch b/SOURCES/0543-test-udev-test-gracefully-exit-when-imports-fail.patch new file mode 100644 index 0000000..3e5c348 --- /dev/null +++ b/SOURCES/0543-test-udev-test-gracefully-exit-when-imports-fail.patch @@ -0,0 +1,48 @@ +From 2e50a00f6930f1c65ca804b78f4a853e2ae2d2c0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 17 Nov 2020 17:13:31 +0100 +Subject: [PATCH] test/udev-test: gracefully exit when imports fail + +In Fedora rawhide various perl modules are now available as separate +packages that are not pulled in by dependencies. If we don't have some +package, skip the tests. + +This ugly code is apparently the way to do conditional imports: +https://www.cs.ait.ac.th/~on/O/oreilly/perl/cookbook/ch12_03.htm. + +(cherry picked from commit d40763838278246e2073d15ca927ee700e583afc) + +Related: #1642728 +--- + test/udev-test.pl | 18 +++++++++++++----- + 1 file changed, 13 insertions(+), 5 deletions(-) + +diff --git a/test/udev-test.pl b/test/udev-test.pl +index cf6ca6b80c..5b1e33504e 100755 +--- a/test/udev-test.pl ++++ b/test/udev-test.pl +@@ -18,11 +18,19 @@ + + use warnings; + use strict; +-use POSIX qw(WIFEXITED WEXITSTATUS); +-use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); +-use IPC::Semaphore; +-use Time::HiRes qw(usleep); +-use Cwd qw(getcwd abs_path); ++ ++BEGIN { ++ my $EXIT_TEST_SKIP = 77; ++ ++ unless (eval "use POSIX qw(WIFEXITED WEXITSTATUS); ++ use Cwd qw(getcwd abs_path); ++ use IPC::Semaphore; ++ use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT); ++ use Time::HiRes qw(usleep); 1") { ++ warn "Failed to import dependencies, skipping the test: $@"; ++ exit($EXIT_TEST_SKIP); ++ } ++} + + my $udev_bin = "./test-udev"; + my $valgrind = 0; diff --git a/SPECS/systemd.spec b/SPECS/systemd.spec index 8faead7..a3c4fe1 100644 --- a/SPECS/systemd.spec +++ b/SPECS/systemd.spec @@ -13,7 +13,7 @@ Name: systemd Url: http://www.freedesktop.org/wiki/Software/systemd Version: 239 -Release: 36%{?dist} +Release: 44%{?dist} # For a breakdown of the licensing, see README License: LGPLv2+ and MIT and GPLv2+ Summary: System and Service Manager @@ -479,6 +479,120 @@ Patch0426: 0426-core-fix-the-return-value-in-order-to-make-sure-we-d.patch Patch0427: 0427-test-add-test-for-cgroup-v2-freezer-support.patch Patch0428: 0428-fix-mis-merge.patch Patch0429: 0429-tests-sleep-a-bit-and-give-kernel-time-to-perform-th.patch +Patch0430: 0430-device-make-sure-we-emit-PropertiesChanged-signal-on.patch +Patch0431: 0431-device-don-t-emit-PropetiesChanged-needlessly.patch +Patch0432: 0432-units-add-generic-boot-complete.target.patch +Patch0433: 0433-man-document-new-boot-complete.target-unit.patch +Patch0434: 0434-core-make-sure-to-restore-the-control-command-id-too.patch +Patch0435: 0435-cgroup-freezer-action-must-be-NOP-when-cgroup-v2-fre.patch +Patch0436: 0436-logind-don-t-print-warning-when-user-.service-templa.patch +Patch0437: 0437-build-use-simple-project-version-in-pkgconfig-files.patch +Patch0438: 0438-basic-virt-try-the-proc-1-sched-hack-also-for-PID1.patch +Patch0439: 0439-seccomp-rework-how-the-S-UG-ID-filter-is-installed.patch +Patch0440: 0440-vconsole-setup-downgrade-log-message-when-setting-fo.patch +Patch0441: 0441-units-fix-systemd.special-man-page-reference-in-syst.patch +Patch0442: 0442-units-drop-reference-to-sushell-man-page.patch +Patch0443: 0443-sd-bus-break-the-loop-in-bus_ensure_running-if-the-b.patch +Patch0444: 0444-core-add-new-API-for-enqueing-a-job-with-returning-t.patch +Patch0445: 0445-systemctl-replace-switch-statement-by-table-of-struc.patch +Patch0446: 0446-systemctl-reindent-table.patch +Patch0447: 0447-systemctl-Only-wait-when-there-s-something-to-wait-f.patch +Patch0448: 0448-systemctl-clean-up-start_unit_one-error-handling.patch +Patch0449: 0449-systemctl-split-out-extra-args-generation-into-helpe.patch +Patch0450: 0450-systemctl-add-new-show-transaction-switch.patch +Patch0451: 0451-test-add-some-basic-testing-that-systemctl-start-T-d.patch +Patch0452: 0452-man-document-the-new-systemctl-show-transaction-opti.patch +Patch0453: 0453-socket-New-option-FlushPending-boolean-to-flush-sock.patch +Patch0454: 0454-core-remove-support-for-API-bus-started-outside-our-.patch +Patch0455: 0455-mount-setup-fix-segfault-in-mount_cgroup_controllers.patch +Patch0456: 0456-dbus-execute-make-transfer-of-CPUAffinity-endian-saf.patch +Patch0457: 0457-core-add-support-for-setting-CPUAffinity-to-special-.patch +Patch0458: 0458-basic-user-util-always-use-base-10-for-user-group-nu.patch +Patch0459: 0459-parse-util-sometimes-it-is-useful-to-check-if-a-stri.patch +Patch0460: 0460-basic-parse-util-add-safe_atoux64.patch +Patch0461: 0461-parse-util-allow-tweaking-how-to-parse-integers.patch +Patch0462: 0462-parse-util-allow-0-as-alternative-to-0-and-0.patch +Patch0463: 0463-parse-util-make-return-parameter-optional-in-safe_at.patch +Patch0464: 0464-parse-util-rewrite-parse_mode-on-top-of-safe_atou_fu.patch +Patch0465: 0465-user-util-be-stricter-in-parse_uid.patch +Patch0466: 0466-strv-add-new-macro-STARTSWITH_SET.patch +Patch0467: 0467-parse-util-also-parse-integers-prefixed-with-0b-and-.patch +Patch0468: 0468-tests-beef-up-integer-parsing-tests.patch +Patch0469: 0469-shared-user-util-add-compat-forms-of-user-name-check.patch +Patch0470: 0470-shared-user-util-emit-a-warning-on-names-with-dots.patch +Patch0471: 0471-user-util-Allow-names-starting-with-a-digit.patch +Patch0472: 0472-shared-user-util-allow-usernames-with-dots-in-specif.patch +Patch0473: 0473-user-util-switch-order-of-checks-in-valid_user_group.patch +Patch0474: 0474-user-util-rework-how-we-validate-user-names.patch +Patch0475: 0475-man-mention-System-Administrator-s-Guide-in-systemct.patch +Patch0476: 0476-udev-introduce-udev-net_id-naming-schemes.patch +Patch0477: 0477-meson-make-net.naming-scheme-default-configurable.patch +Patch0478: 0478-man-describe-naming-schemes-in-a-new-man-page.patch +Patch0479: 0479-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch +Patch0480: 0480-udev-net_id-don-t-generate-slot-based-names-if-multi.patch +Patch0481: 0481-fix-typo-in-ProtectSystem-option.patch +Patch0482: 0482-remove-references-of-non-existent-man-pages.patch +Patch0483: 0483-log-Prefer-logging-to-CLI-unless-JOURNAL_STREAM-is-s.patch +Patch0484: 0484-locale-util-add-new-helper-locale_is_installed.patch +Patch0485: 0485-test-add-test-case-for-locale_is_installed.patch +Patch0486: 0486-tree-wide-port-various-bits-over-to-locale_is_instal.patch +Patch0487: 0487-install-allow-instantiated-units-to-be-enabled-via-p.patch +Patch0488: 0488-install-small-refactor-to-combine-two-function-calls.patch +Patch0489: 0489-test-fix-a-memleak.patch +Patch0490: 0490-docs-Add-syntax-for-templated-units-to-systemd.prese.patch +Patch0491: 0491-shared-install-fix-preset-operations-for-non-service.patch +Patch0492: 0492-introduce-setsockopt_int-helper.patch +Patch0493: 0493-socket-util-add-generic-socket_pass_pktinfo-helper.patch +Patch0494: 0494-core-add-new-PassPacketInfo-socket-unit-property.patch +Patch0495: 0495-resolved-tweak-cmsg-calculation.patch +Patch0496: 0496-ci-PowerTools-repo-was-renamed-to-powertools-in-RHEL.patch +Patch0497: 0497-ci-use-quay.io-instead-of-Docker-Hub-to-avoid-rate-l.patch +Patch0498: 0498-ci-move-jobs-from-Travis-CI-to-GH-Actions.patch +Patch0499: 0499-unit-make-UNIT-cast-function-deal-with-NULL-pointers.patch +Patch0500: 0500-use-link-to-RHEL-8-docs.patch +Patch0501: 0501-cgroup-Also-set-blkio.bfq.weight.patch +Patch0502: 0502-units-make-sure-initrd-cleanup.service-terminates-be.patch +Patch0503: 0503-core-reload-SELinux-label-cache-on-daemon-reload.patch +Patch0504: 0504-selinux-introduce-mac_selinux_create_file_prepare_at.patch +Patch0505: 0505-selinux-add-trigger-for-policy-reload-to-refresh-int.patch +Patch0506: 0506-udev-net_id-give-RHEL-8.4-naming-scheme-a-name.patch +Patch0507: 0507-basic-stat-util-make-mtime-check-stricter-and-use-en.patch +Patch0508: 0508-udev-make-algorithm-that-selects-highest-priority-de.patch +Patch0509: 0509-test-create-dev-null-in-test-udev.pl.patch +Patch0510: 0510-test-missing-die.patch +Patch0511: 0511-udev-test-remove-a-check-for-whether-the-test-is-run.patch +Patch0512: 0512-udev-test-skip-the-test-only-if-it-can-t-setup-its-e.patch +Patch0513: 0513-udev-test-fix-test-skip-condition.patch +Patch0514: 0514-udev-test-fix-missing-directory-test-run.patch +Patch0515: 0515-udev-test-check-if-permitted-to-create-block-device-.patch +Patch0516: 0516-test-udev-add-a-testcase-of-too-long-line.patch +Patch0517: 0517-test-udev-use-proper-semantics-for-too-long-line-wit.patch +Patch0518: 0518-test-udev-add-more-tests-for-line-continuations-and-.patch +Patch0519: 0519-test-udev-add-more-tests-for-line-continuation.patch +Patch0520: 0520-test-udev-fix-alignment-and-drop-unnecessary-white-s.patch +Patch0521: 0521-test-udev-test.pl-cleanup-if-skipping-test.patch +Patch0522: 0522-test-add-test-cases-for-empty-string-match.patch +Patch0523: 0523-test-add-test-case-for-multi-matches-when-use.patch +Patch0524: 0524-udev-test-do-not-rely-on-mail-group-being-defined.patch +Patch0525: 0525-test-udev-test.pl-allow-multiple-devices-per-test.patch +Patch0526: 0526-test-udev-test.pl-create-rules-only-once.patch +Patch0527: 0527-test-udev-test.pl-allow-concurrent-additions-and-rem.patch +Patch0528: 0528-test-udev-test.pl-use-computed-devnode-name.patch +Patch0529: 0529-test-udev-test.pl-test-correctness-of-symlink-target.patch +Patch0530: 0530-test-udev-test.pl-allow-checking-multiple-symlinks.patch +Patch0531: 0531-test-udev-test.pl-fix-wrong-test-descriptions.patch +Patch0532: 0532-test-udev-test.pl-last_rule-is-unsupported.patch +Patch0533: 0533-test-udev-test.pl-Make-some-tests-a-little-harder.patch +Patch0534: 0534-test-udev-test.pl-remove-bogus-rules-from-magic-subs.patch +Patch0535: 0535-test-udev-test.pl-merge-space-and-var-with-space-tes.patch +Patch0536: 0536-test-udev-test.pl-merge-import-parent-tests-into-one.patch +Patch0537: 0537-test-udev-test.pl-count-good-results.patch +Patch0538: 0538-tests-udev-test.pl-add-multiple-device-test.patch +Patch0539: 0539-test-udev-test.pl-add-repeat-count.patch +Patch0540: 0540-test-udev-test.pl-generator-for-large-list-of-block-.patch +Patch0541: 0541-test-udev-test.pl-suppress-umount-error-message-at-s.patch +Patch0542: 0542-test-udev_test.pl-add-expected-good-count.patch +Patch0543: 0543-test-udev-test-gracefully-exit-when-imports-fail.patch %ifarch %{ix86} x86_64 aarch64 @@ -849,6 +963,9 @@ install -D -t %{buildroot}/usr/lib/systemd/ %{SOURCE3} # No tmp-on-tmpfs by default in RHEL. bz#876122 bz#1578772 rm -f %{buildroot}%{_prefix}/lib/systemd/system/local-fs.target.wants/tmp.mount +# bz#1844465 +rm -f %{buildroot}/etc/systemd/system/dbus-org.freedesktop.resolve1.service + %find_lang %{name} # Split files in build root into rpms. See split-files.py for the @@ -856,7 +973,7 @@ rm -f %{buildroot}%{_prefix}/lib/systemd/system/local-fs.target.wants/tmp.mount # here. python3 %{SOURCE2} %buildroot < - 239-44 +- ci: PowerTools repo was renamed to powertools in RHEL 8.3 (#1871827) +- ci: use quay.io instead of Docker Hub to avoid rate limits (#1871827) +- ci: move jobs from Travis CI to GH Actions (#1871827) +- unit: make UNIT() cast function deal with NULL pointers (#1871827) +- use link to RHEL-8 docs (#1623116) +- cgroup: Also set blkio.bfq.weight (#1657810) +- units: make sure initrd-cleanup.service terminates before switching to rootfs (#1657810) +- core: reload SELinux label cache on daemon-reload (#1888912) +- selinux: introduce mac_selinux_create_file_prepare_at() (#1888912) +- selinux: add trigger for policy reload to refresh internal selabel cache (#1888912) +- udev/net_id: give RHEL-8.4 naming scheme a name (#1827462) +- basic/stat-util: make mtime check stricter and use entire timestamp (#1642728) +- udev: make algorithm that selects highest priority devlink less susceptible to race conditions (#1642728) +- test: create /dev/null in test-udev.pl (#1642728) +- test: missing "die" (#1642728) +- udev-test: remove a check for whether the test is run in a container (#1642728) +- udev-test: skip the test only if it can't setup its environment (#1642728) +- udev-test: fix test skip condition (#1642728) +- udev-test: fix missing directory test/run (#1642728) +- udev-test: check if permitted to create block device nodes (#1642728) +- test-udev: add a testcase of too long line (#1642728) +- test-udev: use proper semantics for too long line with continuation (#1642728) +- test-udev: add more tests for line continuations and comments (#1642728) +- test-udev: add more tests for line continuation (#1642728) +- test-udev: fix alignment and drop unnecessary white spaces (#1642728) +- test/udev-test.pl: cleanup if skipping test (#1642728) +- test: add test cases for empty string match (#1642728) +- test: add test case for multi matches when use "||" (#1642728) +- udev-test: do not rely on "mail" group being defined (#1642728) +- test/udev-test.pl: allow multiple devices per test (#1642728) +- test/udev-test.pl: create rules only once (#1642728) +- test/udev-test.pl: allow concurrent additions and removals (#1642728) +- test/udev-test.pl: use computed devnode name (#1642728) +- test/udev-test.pl: test correctness of symlink targets (#1642728) +- test/udev-test.pl: allow checking multiple symlinks (#1642728) +- test/udev-test.pl: fix wrong test descriptions (#1642728) +- test/udev-test.pl: last_rule is unsupported (#1642728) +- test/udev-test.pl: Make some tests a little harder (#1642728) +- test/udev-test.pl: remove bogus rules from magic subsys test (#1642728) +- test/udev-test.pl: merge "space and var with space" tests (#1642728) +- test/udev-test.pl: merge import parent tests into one (#1642728) +- test/udev-test.pl: count "good" results (#1642728) +- tests/udev-test.pl: add multiple device test (#1642728) +- test/udev-test.pl: add repeat count (#1642728) +- test/udev-test.pl: generator for large list of block devices (#1642728) +- test/udev-test.pl: suppress umount error message at startup (#1642728) +- test/udev_test.pl: add "expected good" count (#1642728) +- test/udev-test: gracefully exit when imports fail (#1642728) + +* Thu Nov 26 2020 systemd maintenance team - 239-43 +- man: mention System Administrator's Guide in systemctl manpage (#1623116) +- udev: introduce udev net_id "naming schemes" (#1827462) +- meson: make net.naming-scheme= default configurable (#1827462) +- man: describe naming schemes in a new man page (#1827462) +- udev/net_id: parse _SUN ACPI index as a signed integer (#1827462) +- udev/net_id: don't generate slot based names if multiple devices might claim the same slot (#1827462) +- fix typo in ProtectSystem= option (#1871139) +- remove references of non-existent man pages (#1876807) +- log: Prefer logging to CLI unless JOURNAL_STREAM is set (#1865840) +- locale-util: add new helper locale_is_installed() (#1755287) +- test: add test case for locale_is_installed() (#1755287) +- tree-wide: port various bits over to locale_is_installed() (#1755287) +- install: allow instantiated units to be enabled via presets (#1812972) +- install: small refactor to combine two function calls into one function (#1812972) +- test: fix a memleak (#1812972) +- docs: Add syntax for templated units to systemd.preset man page (#1812972) +- shared/install: fix preset operations for non-service instantiated units (#1812972) +- introduce setsockopt_int() helper (#1887181) +- socket-util: add generic socket_pass_pktinfo() helper (#1887181) +- core: add new PassPacketInfo= socket unit property (#1887181) +- resolved: tweak cmsg calculation (#1887181) + +* Tue Nov 03 2020 systemd maintenance team - 239-42 +- logind: don't print warning when user@.service template is masked (#1880270) +- build: use simple project version in pkgconfig files (#1862714) +- basic/virt: try the /proc/1/sched hack also for PID1 (#1868877) +- seccomp: rework how the S[UG]ID filter is installed (#1860374) +- vconsole-setup: downgrade log message when setting font fails on dummy console (#1889996) +- units: fix systemd.special man page reference in system-update-cleanup.service (#1871827) +- units: drop reference to sushell man page (#1871827) +- sd-bus: break the loop in bus_ensure_running() if the bus is not connecting (#1885553) +- core: add new API for enqueing a job with returning the transaction data (#846319) +- systemctl: replace switch statement by table of structures (#846319) +- systemctl: reindent table (#846319) +- systemctl: Only wait when there's something to wait for. (#846319) +- systemctl: clean up start_unit_one() error handling (#846319) +- systemctl: split out extra args generation into helper function of its own (#846319) +- systemctl: add new --show-transaction switch (#846319) +- test: add some basic testing that "systemctl start -T" does something (#846319) +- man: document the new systemctl --show-transaction option (#846319) +- socket: New option 'FlushPending' (boolean) to flush socket before entering listening state (#1870638) +- core: remove support for API bus "started outside our own logic" (#1764282) +- mount-setup: fix segfault in mount_cgroup_controllers when using gcc9 compiler (#1868877) +- dbus-execute: make transfer of CPUAffinity endian safe (#12711) (#1740657) +- core: add support for setting CPUAffinity= to special "numa" value (#1740657) +- basic/user-util: always use base 10 for user/group numbers (#1848373) +- parse-util: sometimes it is useful to check if a string is a valid integer, but not actually parse it (#1848373) +- basic/parse-util: add safe_atoux64() (#1848373) +- parse-util: allow tweaking how to parse integers (#1848373) +- parse-util: allow '-0' as alternative to '0' and '+0' (#1848373) +- parse-util: make return parameter optional in safe_atou16_full() (#1848373) +- parse-util: rewrite parse_mode() on top of safe_atou_full() (#1848373) +- user-util: be stricter in parse_uid() (#1848373) +- strv: add new macro STARTSWITH_SET() (#1848373) +- parse-util: also parse integers prefixed with 0b and 0o (#1848373) +- tests: beef up integer parsing tests (#1848373) +- shared/user-util: add compat forms of user name checking functions (#1848373) +- shared/user-util: emit a warning on names with dots (#1848373) +- user-util: Allow names starting with a digit (#1848373) +- shared/user-util: allow usernames with dots in specific fields (#1848373) +- user-util: switch order of checks in valid_user_group_name_or_id_full() (#1848373) +- user-util: rework how we validate user names (#1848373) + +* Wed Oct 07 2020 systemd maintenance team - 239-41 +- cgroup: freezer action must be NOP when cgroup v2 freezer is not available (#1868831) + +* Fri Aug 28 2020 systemd maintenance team - 239-40 +- units: add generic boot-complete.target (#1872243) +- man: document new "boot-complete.target" unit (#1872243) +- core: make sure to restore the control command id, too (#1829867) + +* Thu Aug 06 2020 systemd maintenance team - 239-39 +- device: make sure we emit PropertiesChanged signal once we set sysfs (#1793533) +- device: don't emit PropetiesChanged needlessly (#1793533) + +* Tue Aug 04 2020 systemd maintenance team - 239-38 +- spec: fix rpm verification (#1702300) + +* Wed Jul 08 2020 systemd maintenance team - 239-37 +- spec: don't package /etc/systemd/system/dbus-org.freedesktop.resolve1.service (#1844465) + * Fri Jun 26 2020 systemd maintenance team - 239-36 - core: don't consider SERVICE_SKIP_CONDITION for abnormal or failure restarts (#1737283) - selinux: do preprocessor check only in selinux-access.c (#1830861)