From 8be66a9c1241b99c9ebac81e620ea79666389d1e Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Nov 05 2020 08:09:57 +0000 Subject: import systemd-239-42.el8 --- 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/SPECS/systemd.spec b/SPECS/systemd.spec index a8be922..5147231 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: 40%{?dist} +Release: 42%{?dist} # For a breakdown of the licensing, see README License: LGPLv2+ and MIT and GPLv2+ Summary: System and Service Manager @@ -484,6 +484,46 @@ 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 %ifarch %{ix86} x86_64 aarch64 @@ -1112,6 +1152,50 @@ fi %files tests -f .file-list-tests %changelog +* 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)