import systemd-239-45.el8

This commit is contained in:
CentOS Sources 2021-05-18 02:38:04 -04:00 committed by Andrew Lukoshko
parent e39ce11207
commit 77354762f2
129 changed files with 19157 additions and 13 deletions

View File

@ -1,4 +1,4 @@
From 37a4f8256410e3ba1bf1eb7523ebc9f1a68b65e1 Mon Sep 17 00:00:00 2001
From 65e96327360ab41d44d5383dcecc82a19fad198c Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Fri, 22 Feb 2019 15:50:55 +0100
Subject: [PATCH] logind: don't print warning when user@.service template is
@ -10,9 +10,8 @@ instances of systemd for logged in users. We don't need to be verbose
about it.
(cherry picked from commit 03b6fa0c5b51b0d39334ff6ba183a3391443bcf6)
(cherry picked from commit 65e96327360ab41d44d5383dcecc82a19fad198c)
Resolves: #1894152
Resolves: #1880270
---
src/login/logind-user.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

View File

@ -0,0 +1,80 @@
From a6d76bf2d21e01a2e031e204966d946925ecc3f6 Mon Sep 17 00:00:00 2001
From: Jan Synacek <jsynacek@redhat.com>
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@

View File

@ -0,0 +1,57 @@
From 2f584bd93d64a75ab11b5a5aa31d0b7145da5a86 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
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;

View File

@ -0,0 +1,287 @@
From 8cc497e735104080f6830a8f468b2724ae372990 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
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))

View File

@ -0,0 +1,91 @@
From 860749038f508617c8fc31b8292b4019b1e621ba Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
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")];

View File

@ -0,0 +1,26 @@
From 46fa8ff1a62e3334582a971cc6bbd9b8a16680d5 Mon Sep 17 00:00:00 2001
From: Michael Biebl <biebl@debian.org>
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

View File

@ -0,0 +1,27 @@
From 5aa59d172189adcbd7f9dedb3b909c6bf9b609f2 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,44 @@
From 6a50c735a3bbf98d06fbfa7815f7bdc14ea96f9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
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;

View File

@ -0,0 +1,831 @@
From 7155c010ef8c620295d230c284849636c07b40c0 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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));

View File

@ -0,0 +1,115 @@
From 8b34041ee97069bee8bf03ae5ba651b34b1b8460 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,53 @@
From 26d2d89c6216672cedf6abfe1b73081adebbdcf8 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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" },
};

View File

@ -0,0 +1,29 @@
From 91c83bde0904581fbc33eb7821119e665b9505ce Mon Sep 17 00:00:00 2001
From: Filipe Brandenburger <filbranden@google.com>
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)

View File

@ -0,0 +1,99 @@
From 7569756d005d4f780fffd2504eb6f461982833f2 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,68 @@
From cfb124260a0a9e68102a373c0d136f792e2d4ea7 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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;

View File

@ -0,0 +1,362 @@
From cacf7a619cdccd9a6da6c2fe7361eac121b9ea0b Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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 <unit1> <unit2> …" command */
method = "StartUnit";
+ job_type = "start";
mode = "isolate";
-
suffix = ".target";
} else {
+ /* A command in style of "systemctl start <unit1> <unit2> …", "sysemctl stop <unit1> <unit2> …" 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;

View File

@ -0,0 +1,31 @@
From f31afbfd2fa68e20a10a8432fb4714a6d4e1170a Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,37 @@
From 588e3e8008d24021ec025d54318fb07d2212293c Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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 @@
</varlistentry>
+ <varlistentry>
+ <term><option>-T</option></term>
+ <term><option>--show-transaction</option></term>
+
+ <listitem>
+ <para>When enqueuing a unit job (for example as effect of a <command>systemctl start</command>
+ 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.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--fail</option></term>

View File

@ -0,0 +1,153 @@
From 262544a451c11c38e92c45047ec2adeaeb2a0a7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
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.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>FlushPending=</varname></term>
+ <listitem><para>Takes a boolean argument. May only be used when
+ <option>Accept=no</option>. 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 <option>no</option>.
+ </para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>MaxConnections=</varname></term>
<listitem><para>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);

View File

@ -0,0 +1,62 @@
From 9c0ed82f661a2296784970678746b0b4f130870e Mon Sep 17 00:00:00 2001
From: Alan Jenkins <alan.christopher.jenkins@gmail.com>
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;

View File

@ -0,0 +1,56 @@
From 1c8d1c3bbdf8bdfb2ee4f85b9f559f54c6e4cac4 Mon Sep 17 00:00:00 2001
From: Wen Yang <wenyang@linux.alibaba.com>
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)

View File

@ -0,0 +1,39 @@
From 1730f7bb306e13689a7684fd93ae5b8383a28d2f Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekletar@users.noreply.github.com>
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(

View File

@ -0,0 +1,426 @@
From 1a822dbe19ab6634ffb2c0d3ce92b27b503e1612 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= <msekleta@redhat.com>
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</programlisting>
<term><varname>CPUAffinity=</varname></term>
<listitem><para>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 <varname>NUMAMask=</varname> 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
<citerefentry><refentrytitle>sched_setaffinity</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
details.</para></listitem>
</varlistentry>
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

View File

@ -0,0 +1,93 @@
From 57d2e6e64ba490054f8de1a2aad4ffae7778eddc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
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) {

View File

@ -0,0 +1,149 @@
From 2fd9a21a8b6a93c4fb2747839766adca15faa008 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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;
}

View File

@ -0,0 +1,130 @@
From bd47a98d3ce2c5e1d74deb7bc384e416a9070b96 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
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();

View File

@ -0,0 +1,137 @@
From 1d11e79fefea34b4395043e8e951414c5b7817ba Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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) {

View File

@ -0,0 +1,60 @@
From 1c8e5070d8a88f35b5577e091de66727fa785ef7 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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;

View File

@ -0,0 +1,31 @@
From 91ed5edcdea79773f6918e739637521e47129b07 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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;
}

View File

@ -0,0 +1,59 @@
From 147a3696b45a872e0e21fb74e1497f02543ce871 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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;
}

View File

@ -0,0 +1,78 @@
From 87c22d3bb794118d25bc138108fd5bdd607365ef Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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) {

View File

@ -0,0 +1,75 @@
From 50b103a982dfd6f1b2bf98bbc98a8063fa153e89 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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();

View File

@ -0,0 +1,164 @@
From e67e29d91a1ef90af545e4130c7b4c4cfde6202a Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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)

View File

@ -0,0 +1,204 @@
From 457eada27f606e39f0efc6adc226542fd11eb815 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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);

View File

@ -0,0 +1,270 @@
From 1e4ec1b29d15684a305bbc9ab54c6c8321504e7b Mon Sep 17 00:00:00 2001
From: David Tardon <dtardon@redhat.com>
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();

View File

@ -0,0 +1,50 @@
From fa1fa19951fdadd63f2b5df6224678f91753f260 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
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);

View File

@ -0,0 +1,103 @@
From f06434cc51eedd72f7d4a640a1fa118f57a5e68e Mon Sep 17 00:00:00 2001
From: Balint Reczey <balint.reczey@canonical.com>
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"));
}

View File

@ -0,0 +1,193 @@
From 40dff18947fa198810db4cd3e5291349fc84a0e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
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);

View File

@ -0,0 +1,38 @@
From 7569168bea3d7e11cd3afe6167fcf4a3ac65a1a6 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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) {

View File

@ -0,0 +1,803 @@
From 33b851f0c30e47fe71a293e2c990ef26573efe86 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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 <unistd.h>
#include <utmp.h>
+#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();

View File

@ -0,0 +1,35 @@
From 11a9ea82827d7b57dbce307b77ef8233a4cc028a Mon Sep 17 00:00:00 2001
From: Lukas Nykryn <lnykryn@redhat.com>
Date: Thu, 28 Aug 2014 15:12:10 +0200
Subject: [PATCH] man: mention System Administrator's Guide in systemctl
manpage
(cherry picked from commit d4582346f47064de24470b5f92e418966004925f)
Resolves: #1623116
---
man/systemctl.xml | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/man/systemctl.xml b/man/systemctl.xml
index fa08ab6c0a..56f94d084c 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -2000,6 +2000,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<xi:include href="less-variables.xml" xpointer="lesscharset"/>
</refsect1>
+ <refsect1>
+ <title>Examples</title>
+ <para>
+ For examples how to use systemctl in comparsion
+ with old service and chkconfig command please see:
+ <ulink url="https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System_Administrators_Guide/sect-Managing_Services_with_systemd-Services.html">
+ Managing System Services
+ </ulink>
+ </para>
+ </refsect1>
+
<refsect1>
<title>See Also</title>
<para>

View File

@ -0,0 +1,257 @@
From 08ac9f7f55c138678c6415139e7510a05a75b81d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= <msekleta@redhat.com>
Date: Wed, 14 Oct 2020 16:57:44 +0200
Subject: [PATCH] udev: introduce udev net_id "naming schemes"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
With this we can stabilize how naming works for network interfaces. A
user can request through a kernel cmdline option or an env var which
scheme to follow. The idea is that installers use this to set into stone
(a very soft stone though) the scheme used during installation so that
interface naming doesn't change afterwards anymore.
Why use env vars and kernel cmdline options, and not a config file of
its own?
Well, first of all there's no obvious existing one to use. But more
importantly: I have the feeling that this logic is kind of an incomplete
hack, and I simply don't want to do advertise this as a perfectly
working solution. So far we used env vars for the non-so-official
options and proper config files for the official stuff. Given how
incomplete this logic is (i.e. the big variable for naming remains the
kernel, which might expose sysfs attributes in newer versions that we
check for and didn't exist in older versions — and other problems like
this), I am simply not confident in giving this first-class exposure in
a primary configuration file.
Fixes: #10448
(cherry-picked from commit f7e81fd96fdfe0ac6dcdb72de43f7cb4720e363a)
Related: #1827462
[msekleta: note that we are introducing our own naming schemes based on
RHEL-8 minor versions. Also we are not backporting all naming scheme
features that appeared in the original commit. We are backporting only
features relevant for v239 while original commit also converted
changes introduced in v240 into naming scheme flags.]
---
doc/ENVIRONMENT.md | 9 +++
man/kernel-command-line.xml | 1 +
man/systemd-udevd.service.xml | 16 +++++
src/udev/udev-builtin-net_id.c | 106 ++++++++++++++++++++++++++++++++-
4 files changed, 130 insertions(+), 2 deletions(-)
diff --git a/doc/ENVIRONMENT.md b/doc/ENVIRONMENT.md
index 39a36a52cc..1a4aa01ef4 100644
--- a/doc/ENVIRONMENT.md
+++ b/doc/ENVIRONMENT.md
@@ -76,6 +76,15 @@ systemd-logind:
hibernation is available even if the swap devices do not provide enough room
for it.
+* `$NET_NAMING_SCHEME=` if set, takes a network naming scheme (i.e. one of
+ v238, v239, v240 …) as parameter. If specified udev's net_id builtin will
+ follow the specified naming scheme when determining stable network interface
+ names. This may be used to revert to naming schemes of older udev versions,
+ in order to provide more stable naming across updates. This environment
+ variable takes precedence over the kernel command line option
+ `net.naming-scheme=`, except if the value is prefixed with `:` in which case
+ the kernel command line option takes precedence, if it is specified as well.
+
installed systemd tests:
* `$SYSTEMD_TEST_DATA` — override the location of test data. This is useful if
diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml
index 4d8cb4e50e..b753d0592c 100644
--- a/man/kernel-command-line.xml
+++ b/man/kernel-command-line.xml
@@ -246,6 +246,7 @@
<term><varname>udev.event_timeout=</varname></term>
<term><varname>rd.udev.event_timeout=</varname></term>
<term><varname>net.ifnames=</varname></term>
+ <term><varname>net.naming-scheme=</varname></term>
<listitem>
<para>Parameters understood by the device event managing
diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml
index 73c77ea690..6449103441 100644
--- a/man/systemd-udevd.service.xml
+++ b/man/systemd-udevd.service.xml
@@ -170,6 +170,22 @@
when possible. It is enabled by default; specifying 0 disables it.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>net.naming-scheme=</varname></term>
+ <listitem>
+ <para>Network interfaces are renamed to give them predictable names when possible (unless
+ <varname>net.ifnames=0</varname> is specified, see above). The names are derived from various device metadata
+ fields. Newer versions of <filename>systemd-udevd.service</filename> take more of these fields into account,
+ improving (and thus possibly changing) the names used for the same devices. With this kernel command line
+ option it is possible to pick a specific version of this algorithm. It expects a naming scheme identifier as
+ argument. Currently the following identifiers are known: <literal>v238</literal>, <literal>v239</literal>,
+ <literal>v240</literal> which each implement the naming scheme that was the default in the indicated systemd
+ version. Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: the
+ naming is generally derived from driver attributes exposed by the kernel. As the kernel is updated,
+ previously missing attributes <filename>systemd-udevd.service</filename> is checking might appear, which
+ affects older name derivation algorithms, too.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
<!-- when adding entries here, consider also adding them
in kernel-command-line.xml -->
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 147e04ab8c..148696183e 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -96,6 +96,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "parse-util.h"
+#include "proc-cmdline.h"
#include "stdio-util.h"
#include "string-util.h"
#include "udev.h"
@@ -103,6 +104,52 @@
#define ONBOARD_INDEX_MAX (16*1024-1)
+/* So here's the deal: net_id is supposed to be an excercise in providing stable names for network devices. However, we
+ * also want to keep updating the naming scheme used in future versions of net_id. These two goals of course are
+ * contradictory: on one hand we want things to not change and on the other hand we want them to improve. Our way out
+ * of this dilemma is to introduce the "naming scheme" concept: each time we improve the naming logic we define a new
+ * flag for it. Then, we keep a list of schemes, each identified by a name associated with the flags it implements. Via
+ * a kernel command line and environment variable we then allow the user to pick the scheme they want us to follow:
+ * installers could "freeze" the used scheme at the moment of installation this way.
+ *
+ * Developers: each time you tweak the naming logic here, define a new flag below, and condition the tweak with
+ * it. Each time we do a release we'll then add a new scheme entry and include all newly defined flags.
+ *
+ * Note that this is only half a solution to the problem though: not only udev/net_id gets updated all the time, the
+ * kernel gets too. And thus a kernel that previously didn't expose some sysfs attribute we look for might eventually
+ * do, and thus affect our naming scheme too. Thus, enforcing a naming scheme will make interfacing more stable across
+ * OS versions, but not fully stabilize them. */
+typedef enum NamingSchemeFlags {
+ /* First, the individual features */
+ NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a40008b8299529c978ed8e11de8f6*/
+ NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6eab35d1cb9fa73889892702c27be09 */
+
+ /* And now the masks that combine the features above */
+ NAMING_V238 = 0,
+ NAMING_V239 = NAMING_V238|NAMING_SR_IOV_V|NAMING_NPAR_ARI,
+ NAMING_RHEL_8_0 = NAMING_V239,
+ NAMING_RHEL_8_1 = NAMING_V239,
+ NAMING_RHEL_8_2 = NAMING_V239,
+ NAMING_RHEL_8_3 = NAMING_V239,
+
+ _NAMING_SCHEME_FLAGS_INVALID = -1,
+} NamingSchemeFlags;
+
+typedef struct NamingScheme {
+ const char *name;
+ NamingSchemeFlags flags;
+} NamingScheme;
+
+static const NamingScheme naming_schemes[] = {
+ { "v238", NAMING_V238 },
+ { "v239", NAMING_V239 },
+ { "rhel-8.0", NAMING_RHEL_8_0 },
+ { "rhel-8.1", NAMING_RHEL_8_1 },
+ { "rhel-8.2", NAMING_RHEL_8_2 },
+ { "rhel-8.3", NAMING_RHEL_8_3 },
+ /* … add more schemes here, as the logic to name devices is updated … */
+};
+
enum netname_type{
NET_UNDEF,
NET_PCI,
@@ -138,6 +185,56 @@ struct virtfn_info {
char suffix[IFNAMSIZ];
};
+static const NamingScheme* naming_scheme(void) {
+ static const NamingScheme *cache = NULL;
+ _cleanup_free_ char *buffer = NULL;
+ const char *e, *k;
+
+ if (cache)
+ return cache;
+
+ /* Acquire setting from the kernel command line */
+ (void) proc_cmdline_get_key("net.naming-scheme", 0, &buffer);
+
+ /* Also acquire it from an env var */
+ e = getenv("NET_NAMING_SCHEME");
+ if (e) {
+ if (*e == ':') {
+ /* If prefixed with ':' the kernel cmdline takes precedence */
+ k = buffer ?: e + 1;
+ } else
+ k = e; /* Otherwise the env var takes precedence */
+ } else
+ k = buffer;
+
+ if (k) {
+ size_t i;
+
+ for (i = 0; i < ELEMENTSOF(naming_schemes); i++)
+ if (streq(naming_schemes[i].name, k)) {
+ cache = naming_schemes + i;
+ break;
+ }
+
+ if (!cache)
+ log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k);
+ }
+
+ if (cache)
+ log_info("Using interface naming scheme '%s'.", cache->name);
+ else {
+ /* RHEL-only: here we differ from the upstream and if no naming scheme was selected we default to naming from systemd-239 */
+ cache = &naming_schemes[2];
+ log_info("Using default interface naming scheme '%s'.", cache->name);
+ }
+
+ return cache;
+}
+
+static bool naming_scheme_has(NamingSchemeFlags flags) {
+ return FLAGS_SET(naming_scheme()->flags, flags);
+}
+
/* skip intermediate virtio devices */
static struct udev_device *skip_virtio(struct udev_device *dev) {
struct udev_device *parent = dev;
@@ -299,7 +396,9 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (sscanf(udev_device_get_sysname(names->pcidev), "%x:%x:%x.%u", &domain, &bus, &slot, &func) != 4)
return -ENOENT;
- if (is_pci_ari_enabled(names->pcidev))
+
+ if (naming_scheme_has(NAMING_NPAR_ARI) &&
+ is_pci_ari_enabled(names->pcidev))
/* ARI devices support up to 256 functions on a single device ("slot"), and interpret the
* traditional 5-bit slot and 3-bit function number as a single 8-bit function number,
* where the slot makes up the upper 5 bits. */
@@ -494,7 +593,8 @@ static int names_pci(struct udev_device *dev, struct netnames *names) {
return -ENOENT;
}
- if (get_virtfn_info(dev, names, &vf_info) >= 0) {
+ if (naming_scheme_has(NAMING_SR_IOV_V) &&
+ get_virtfn_info(dev, names, &vf_info) >= 0) {
/* If this is an SR-IOV virtual device, get base name using physical device and add virtfn suffix. */
vf_names.pcidev = vf_info.physfn_pcidev;
dev_pci_onboard(dev, &vf_names);
@@ -741,6 +841,8 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool
prefix = "ww";
}
+ udev_builtin_add_property(dev, test, "ID_NET_NAMING_SCHEME", naming_scheme()->name);
+
err = names_mac(dev, &names);
if (err >= 0 && names.mac_valid) {
char str[IFNAMSIZ];

View File

@ -0,0 +1,188 @@
From 8c263758fe196624005f19bd6f46d63e3841c5be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Tue, 11 Dec 2018 23:28:29 +0100
Subject: [PATCH] meson: make net.naming-scheme= default configurable
This is useful for distributions, where the stability of interface names should
be preseved after an upgrade of systemd. So when some specific release of the
distro is made available, systemd defaults to the latest & greatest naming
scheme, and subsequent updates set the same default. This default may still
be overriden through the kernel and env var options.
A special value "latest" is also allowed. Without a specific name, it is harder
to verride from meson. In case of 'combo' options, meson reads the default
during the initial configuration, and "remembers" this choice. When systemd is
updated, old build/ directories could keep the old default, which would be
annoying. Hence, "latest" is introduced to make it explicit, yet follow the
upstream. This is actually useful for the user too, because it may be used
as an override, without having to actually specify a version.
(cherry picked from commit 06da5c63dd697ea4087e76c6d809b60b5780b87c)
Related: #1827462
[msekleta: note that our default is not latest but rhel-8.0]
---
doc/ENVIRONMENT.md | 15 +++++++-------
man/systemd-udevd.service.xml | 24 ++++++++++++---------
meson.build | 4 ++++
meson_options.txt | 3 +++
src/udev/udev-builtin-net_id.c | 38 ++++++++++++++++++++--------------
5 files changed, 51 insertions(+), 33 deletions(-)
diff --git a/doc/ENVIRONMENT.md b/doc/ENVIRONMENT.md
index 1a4aa01ef4..0e763b6302 100644
--- a/doc/ENVIRONMENT.md
+++ b/doc/ENVIRONMENT.md
@@ -77,13 +77,14 @@ systemd-logind:
for it.
* `$NET_NAMING_SCHEME=` if set, takes a network naming scheme (i.e. one of
- v238, v239, v240 …) as parameter. If specified udev's net_id builtin will
- follow the specified naming scheme when determining stable network interface
- names. This may be used to revert to naming schemes of older udev versions,
- in order to provide more stable naming across updates. This environment
- variable takes precedence over the kernel command line option
- `net.naming-scheme=`, except if the value is prefixed with `:` in which case
- the kernel command line option takes precedence, if it is specified as well.
+ "rhel-8.0", "rhel-8.1", "rhel-8.2"…, or the special value "latest") as
+ parameter. If specified udev's net_id builtin will follow the specified
+ naming scheme when determining stable network interface names. This may be
+ used to revert to naming schemes of older udev versions, in order to provide
+ more stable naming across updates. This environment variable takes precedence
+ over the kernel command line option `net.naming-scheme=`, except if the value
+ is prefixed with `:` in which case the kernel command line option takes
+ precedence, if it is specified as well.
installed systemd tests:
diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml
index 6449103441..b738591c93 100644
--- a/man/systemd-udevd.service.xml
+++ b/man/systemd-udevd.service.xml
@@ -174,16 +174,20 @@
<term><varname>net.naming-scheme=</varname></term>
<listitem>
<para>Network interfaces are renamed to give them predictable names when possible (unless
- <varname>net.ifnames=0</varname> is specified, see above). The names are derived from various device metadata
- fields. Newer versions of <filename>systemd-udevd.service</filename> take more of these fields into account,
- improving (and thus possibly changing) the names used for the same devices. With this kernel command line
- option it is possible to pick a specific version of this algorithm. It expects a naming scheme identifier as
- argument. Currently the following identifiers are known: <literal>v238</literal>, <literal>v239</literal>,
- <literal>v240</literal> which each implement the naming scheme that was the default in the indicated systemd
- version. Note that selecting a specific scheme is not sufficient to fully stabilize interface naming: the
- naming is generally derived from driver attributes exposed by the kernel. As the kernel is updated,
- previously missing attributes <filename>systemd-udevd.service</filename> is checking might appear, which
- affects older name derivation algorithms, too.</para>
+ <varname>net.ifnames=0</varname> is specified, see above). The names are derived from various
+ device metadata fields. Newer versions of <filename>systemd-udevd.service</filename> take more of
+ these fields into account, improving (and thus possibly changing) the names used for the same
+ devices. With this kernel command line option it is possible to pick a specific version of this
+ algorithm. It expects a naming scheme identifier as argument. Currently the following identifiers
+ are known: <literal>rhel-8.0</literal>, <literal>rhel-8.1</literal>, <literal>rhel-8.2</literal>,
+ <literal>rhel-8.3</literal> which each implement the naming scheme that was the default in the
+ indicated Red Hat Enterprise Linux minor version. In addition, <literal>latest</literal> may be
+ used to designate the latest scheme known (to this particular version of
+ <filename>systemd-udevd.service</filename>).</para>
+ <para>Note that selecting a specific scheme is not sufficient to fully stabilize interface naming:
+ the naming is generally derived from driver attributes exposed by the kernel. As the kernel is
+ updated, previously missing attributes <filename>systemd-udevd.service</filename> is checking might
+ appear, which affects older name derivation algorithms, too.</para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/meson.build b/meson.build
index 65c1d0785e..57de947367 100644
--- a/meson.build
+++ b/meson.build
@@ -639,6 +639,9 @@ else
conf.set('DEFAULT_HIERARCHY', 'CGROUP_UNIFIED_ALL')
endif
+default_net_naming_scheme = get_option('default-net-naming-scheme')
+conf.set_quoted('DEFAULT_NET_NAMING_SCHEME', default_net_naming_scheme)
+
time_epoch = get_option('time-epoch')
if time_epoch == ''
NEWS = files('NEWS')
@@ -2925,6 +2928,7 @@ status = [
'default DNSSEC mode: @0@'.format(default_dnssec),
'default DNS-over-TLS mode: @0@'.format(default_dns_over_tls),
'default cgroup hierarchy: @0@'.format(default_hierarchy),
+ 'default net.naming-scheme setting: @0@'.format(default_net_naming_scheme),
'default KillUserProcesses setting: @0@'.format(kill_user_processes)]
alt_dns_servers = '\n '.join(dns_servers.split(' '))
diff --git a/meson_options.txt b/meson_options.txt
index 0996891177..213079ac15 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -158,6 +158,9 @@ option('default-hierarchy', type : 'combo',
description : 'default cgroup hierarchy')
option('time-epoch', type : 'string',
description : 'time epoch for time clients')
+option('default-net-naming-scheme', type : 'combo',
+ choices : ['rhel-8.0', 'rhel-8.1', 'rhel-8.2', 'rhel-8.3', 'latest'],
+ description : 'default net.naming-scheme= value')
option('system-uid-max', type : 'string',
description : 'maximum system UID')
option('system-gid-max', type : 'string',
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 148696183e..d85dc2848b 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -185,6 +185,19 @@ struct virtfn_info {
char suffix[IFNAMSIZ];
};
+static const NamingScheme* naming_scheme_from_name(const char *name) {
+ size_t i;
+
+ if (streq(name, "latest"))
+ return naming_schemes + ELEMENTSOF(naming_schemes) - 1;
+
+ for (i = 0; i < ELEMENTSOF(naming_schemes); i++)
+ if (streq(naming_schemes[i].name, name))
+ return naming_schemes + i;
+
+ return NULL;
+}
+
static const NamingScheme* naming_scheme(void) {
static const NamingScheme *cache = NULL;
_cleanup_free_ char *buffer = NULL;
@@ -208,25 +221,18 @@ static const NamingScheme* naming_scheme(void) {
k = buffer;
if (k) {
- size_t i;
-
- for (i = 0; i < ELEMENTSOF(naming_schemes); i++)
- if (streq(naming_schemes[i].name, k)) {
- cache = naming_schemes + i;
- break;
- }
+ cache = naming_scheme_from_name(k);
+ if (cache) {
+ log_info("Using interface naming scheme '%s'.", cache->name);
+ return cache;
+ }
- if (!cache)
- log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k);
+ log_warning("Unknown interface naming scheme '%s' requested, ignoring.", k);
}
- if (cache)
- log_info("Using interface naming scheme '%s'.", cache->name);
- else {
- /* RHEL-only: here we differ from the upstream and if no naming scheme was selected we default to naming from systemd-239 */
- cache = &naming_schemes[2];
- log_info("Using default interface naming scheme '%s'.", cache->name);
- }
+ cache = naming_scheme_from_name(DEFAULT_NET_NAMING_SCHEME);
+ assert(cache);
+ log_info("Using default interface naming scheme '%s'.", cache->name);
return cache;
}

View File

@ -0,0 +1,522 @@
From af528dcffaab1efea760395cc6676fe4b01e89b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Thu, 9 May 2019 12:34:30 +0200
Subject: [PATCH] man: describe naming schemes in a new man page
I decided to make this a separate man page because it is freakin' long.
This content could equally well go in systemd-udevd.service(8), systemd.link(5),
or a new man page for the net_id builtin.
v2:
- rename to systemd.net-naming-scheme
- add udevadm test-builtin net_id example
(cherry picked from commit 0b1e5b6ed8c6b9a2bc53709eb75e381d360f05bf)
Related: #1827462
[msekleta: I've removed parts that describe features which are not
available in RHEL-8]
---
man/rules/meson.build | 1 +
man/systemd-udevd.service.xml | 19 +-
man/systemd.link.xml | 10 +-
man/systemd.net-naming-scheme.xml | 385 ++++++++++++++++++++++++++++++
src/udev/udev-builtin-net_id.c | 1 +
5 files changed, 402 insertions(+), 14 deletions(-)
create mode 100644 man/systemd.net-naming-scheme.xml
diff --git a/man/rules/meson.build b/man/rules/meson.build
index 7ae94ea265..e6c0a99bbd 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -714,6 +714,7 @@ manpages = [
['systemd.kill', '5', [], ''],
['systemd.link', '5', [], ''],
['systemd.mount', '5', [], ''],
+ ['systemd.net-naming-scheme', '7', [], ''],
['systemd.netdev', '5', [], 'ENABLE_NETWORKD'],
['systemd.network', '5', [], 'ENABLE_NETWORKD'],
['systemd.nspawn', '5', [], ''],
diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml
index b738591c93..f4cdb2f1e7 100644
--- a/man/systemd-udevd.service.xml
+++ b/man/systemd-udevd.service.xml
@@ -174,15 +174,11 @@
<term><varname>net.naming-scheme=</varname></term>
<listitem>
<para>Network interfaces are renamed to give them predictable names when possible (unless
- <varname>net.ifnames=0</varname> is specified, see above). The names are derived from various
- device metadata fields. Newer versions of <filename>systemd-udevd.service</filename> take more of
- these fields into account, improving (and thus possibly changing) the names used for the same
- devices. With this kernel command line option it is possible to pick a specific version of this
- algorithm. It expects a naming scheme identifier as argument. Currently the following identifiers
- are known: <literal>rhel-8.0</literal>, <literal>rhel-8.1</literal>, <literal>rhel-8.2</literal>,
- <literal>rhel-8.3</literal> which each implement the naming scheme that was the default in the
- indicated Red Hat Enterprise Linux minor version. In addition, <literal>latest</literal> may be
- used to designate the latest scheme known (to this particular version of
+ <varname>net.ifnames=0</varname> is specified, see above). With this kernel command line option it
+ is possible to pick a specific version of this algorithm and override the default chosen at
+ compilation time. Expects one of the naming scheme identifiers listed in
+ <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ or <literal>latest</literal> to select the latest scheme known (to this particular version of
<filename>systemd-udevd.service</filename>).</para>
<para>Note that selecting a specific scheme is not sufficient to fully stabilize interface naming:
the naming is generally derived from driver attributes exposed by the kernel. As the kernel is
@@ -191,9 +187,8 @@
</listitem>
</varlistentry>
</variablelist>
- <!-- when adding entries here, consider also adding them
- in kernel-command-line.xml -->
- </refsect1>
+ <!-- when adding entries here, consider also adding them in kernel-command-line.xml -->
+ </refsect1>
<refsect1>
<title>See Also</title>
diff --git a/man/systemd.link.xml b/man/systemd.link.xml
index 6708753e82..32657308d0 100644
--- a/man/systemd.link.xml
+++ b/man/systemd.link.xml
@@ -286,6 +286,7 @@
<para>The name is set based on information given by
the firmware for on-board devices, as exported by the
udev property <literal>ID_NET_NAME_ONBOARD</literal>.
+ See <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
</listitem>
</varlistentry>
@@ -295,6 +296,7 @@
<para>The name is set based on information given by
the firmware for hot-plug devices, as exported by the
udev property <literal>ID_NET_NAME_SLOT</literal>.
+ See <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
</listitem>
</varlistentry>
@@ -303,7 +305,9 @@
<listitem>
<para>The name is set based on the device's physical
location, as exported by the udev property
- <literal>ID_NET_NAME_PATH</literal>.</para>
+ <literal>ID_NET_NAME_PATH</literal>.
+ See <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
@@ -311,7 +315,9 @@
<listitem>
<para>The name is set based on the device's persistent
MAC address, as exported by the udev property
- <literal>ID_NET_NAME_MAC</literal>.</para>
+ <literal>ID_NET_NAME_MAC</literal>.
+ See <citerefentry><refentrytitle>systemd.net-naming-scheme</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ </para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml
new file mode 100644
index 0000000000..a12cc3c460
--- /dev/null
+++ b/man/systemd.net-naming-scheme.xml
@@ -0,0 +1,385 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="systemd.net-naming-scheme">
+ <refentryinfo>
+ <title>systemd.net-naming-scheme</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd.net-naming-scheme</refentrytitle>
+ <manvolnum>7</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd.net-naming-scheme</refname>
+ <refpurpose>Network device naming schemes</refpurpose>
+ </refnamediv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>Network interfaces may be renamed to give them predictable names when there's enough information to
+ generate appropriate names and the use of certain types of names is configured. This page describes the
+ first part, i.e. what possible names may be generated. Those names are generated by the
+ <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ builtin <command>net_id</command> and exported as udev properties
+ (<varname>ID_NET_NAME_ONBOARD=</varname>, <varname>ID_NET_LABEL_ONBOARD=</varname>,
+ <varname>ID_NET_NAME_PATH=</varname>, <varname>ID_NET_NAME_SLOT=</varname>).</para>
+
+ <para>Names are derived from various device metadata attributes. Newer versions of udev take more of
+ these attributes into account, improving (and thus possibly changing) the names used for the same
+ devices. Differents version of the naming rules are called "naming schemes". The default naming scheme is
+ chosen at compilation time. Usually this will be the latest implemented version, but it is also possible
+ to set one of the older versions to preserve compatibility. This may be useful for example for
+ distributions, which may introduce new versions of systemd in stable releases without changing the naming
+ scheme. The naming scheme may also be overriden using the <varname>net.naming-scheme=</varname> kernel
+ command line switch, see
+ <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ Available naming schemes are described below.</para>
+
+ <para>After the udev proprties have been generated, appropriate udev rules may be used to actually rename
+ devices based on those properties. See the description of <varname>NamePolicy=</varname> in
+ <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Naming</title>
+
+ <para>All names start with a two-character prefix that signifies the interface type.</para>
+
+ <table>
+ <title>Two character prefixes based on the type of interface</title>
+
+ <tgroup cols='2'>
+ <thead>
+ <row>
+ <entry>Prefix</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><constant>en</constant></entry>
+ <entry>Ethernet</entry>
+ </row>
+ <row>
+ <entry><constant>sl</constant></entry>
+ <entry>serial line IP (slip)</entry>
+ </row>
+ <row>
+ <entry><constant>wl</constant></entry>
+ <entry>Wireless local area network (WLAN)</entry>
+ </row>
+ <row>
+ <entry><constant>ww</constant></entry>
+ <entry>Wireless wide area network (WWAN)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>The udev <command>net_id</command> builtin exports the following udev device properties:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>ID_NET_NAME_ONBOARD=<replaceable>prefix</replaceable><constant>o</constant><replaceable>number</replaceable></varname></term>
+
+ <listitem><para>This name is set based on the ordering information given by the firmware for
+ on-board devices. The name consists of the prefix, letter <constant>o</constant>, and a number
+ specified by the firmware. This is only available for PCI devices.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ID_NET_LABEL_ONBOARD=<replaceable>prefix</replaceable> <replaceable>label</replaceable></varname></term>
+
+ <listitem><para>This property is set based on label given by the firmware for on-board devices. The
+ name consists of the prefix concatenated with the label. This is only available for PCI devices.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ID_NET_NAME_MAC=<replaceable>prefix</replaceable><constant>x</constant><replaceable>AABBCCDDEEFF</replaceable></varname></term>
+
+ <listitem><para>This name consists of the prefix, letter <constant>x</constant>, and 12 hexadecimal
+ digits of the MAC address. It is available if the device has a fixed MAC address. Because this name
+ is based on an attribute of the card itself, it remains "stable" when the device is moved (even
+ between machines), but will change when the hardware is replaced.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ID_NET_NAME_SLOT=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]</varname></term>
+ <term><varname>ID_NET_NAME_SLOT=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>b</constant><replaceable>number</replaceable></varname></term>
+ <term><varname>ID_NET_NAME_SLOT=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>u</constant><replaceable>port</replaceable>…[<constant>c</constant><replaceable>config</replaceable>][<constant>i</constant><replaceable>interface</replaceable>]</varname></term>
+ <term><varname>ID_NET_NAME_SLOT=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>v</constant><replaceable>slot</replaceable></varname></term>
+
+ <listitem><para>This property describes the slot position. Different schemes are used depending on
+ the bus type, as described in the table below. In all cases, PCI slot information must be known. In
+ case of USB, BCMA, and SR-VIO devices, the full name consists of the prefix, PCI slot identifier,
+ and USB or BCMA or SR-VIO slot identifier. The first two parts are denoted as "…" in the table
+ below.</para>
+
+ <table>
+ <title>Slot naming schemes</title>
+
+ <tgroup cols='2'>
+ <thead>
+ <row>
+ <entry>Format</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><replaceable>prefix</replaceable> [<constant>P</constant><replaceable>domain</replaceable>] <constant>s</constant><replaceable>slot</replaceable> [<constant>f</constant><replaceable>function</replaceable>] [<constant>n</constant><replaceable>port_name</replaceable> | <constant>d</constant><replaceable>dev_port</replaceable>]</entry>
+ <entry>PCI slot number</entry>
+ </row>
+
+ <row>
+ <entry>… <constant>b</constant><replaceable>number</replaceable></entry>
+ <entry>Broadcom bus (BCMA) core number</entry>
+ </row>
+
+ <row>
+ <entry>… <constant>u</constant><replaceable>port</replaceable>… [<constant>c</constant><replaceable>config</replaceable>] [<constant>i</constant><replaceable>interface</replaceable>]</entry>
+ <entry>USB port number chain</entry>
+ </row>
+
+ <row>
+ <entry>… <constant>v</constant><replaceable>slot</replaceable></entry>
+ <entry>SR-VIO slot number</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>The PCI domain is only prepended when it is not 0. All multi-function PCI devices will carry
+ the <constant>f<replaceable>function</replaceable></constant> number in the device name, including
+ the function 0 device. For non-multi-function devices, the number is suppressed if 0. The port name
+ <replaceable>port_name</replaceable> is used, or the port number
+ <constant>d</constant><replaceable>dev_port</replaceable> if the name is not known.</para>
+
+ <para>For BCMA devices, the core number is suppressed when 0.</para>
+
+ <para>For USB devices the full chain of port numbers of hubs is composed. If the name gets longer
+ than the maximum number of 15 characters, the name is not exported. The usual USB configuration
+ number 1 and interface number 0 values are suppressed.</para>
+ </listitem>
+
+ <para>SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of
+ <constant>v</constant> and the virtual device number, with any leading zeros removed. The bus
+ number is ignored. This device type is found in IBM PowerVMs.</para>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable><constant>c</constant><replaceable>bus_id</replaceable></varname></term>
+ <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable><constant>a</constant><replaceable>vendor</replaceable><replaceable>model</replaceable><constant>i</constant><replaceable>instance</replaceable></varname></term>
+ <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable><constant>i</constant><replaceable>address</replaceable><constant>n</constant><replaceable>port_name</replaceable></varname></term>
+ <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]</varname></term>
+ <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>b</constant><replaceable>number</replaceable></varname></term>
+ <term><varname>ID_NET_NAME_PATH=<replaceable>prefix</replaceable>[<constant>P</constant><replaceable>domain</replaceable>]<constant>p</constant><replaceable>bus</replaceable><constant>s</constant><replaceable>slot</replaceable>[<constant>f</constant><replaceable>function</replaceable>][<constant>n</constant><replaceable>phys_port_name</replaceable>|<constant>d</constant><replaceable>dev_port</replaceable>]<constant>u</constant><replaceable>port</replaceable>…[<constant>c</constant><replaceable>config</replaceable>][<constant>i</constant><replaceable>interface</replaceable>]</varname></term>
+
+ <listitem><para>This property describes the device installation location. Different schemes are
+ used depending on the bus type, as described in the table below. For BCMA and USB devices, PCI path
+ information must known, and the full name consists of the prefix, PCI slot identifier, and USB or
+ BCMA location. The first two parts are denoted as "…" in the table below.</para>
+
+ <table>
+ <title>Path naming schemes</title>
+
+ <tgroup cols='2'>
+ <thead>
+ <row>
+ <entry>Format</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><replaceable>prefix</replaceable> <constant>c</constant><replaceable>bus_id</replaceable></entry>
+ <entry>CCW or grouped CCW device identifier</entry>
+ </row>
+
+ <row>
+ <entry><replaceable>prefix</replaceable> <constant>a</constant><replaceable>vendor</replaceable> <replaceable>model</replaceable> <constant>i</constant><replaceable>instance</replaceable></entry>
+ <entry>ACPI path names for ARM64 platform devices</entry>
+ </row>
+
+ <row>
+ <entry><replaceable>prefix</replaceable> [<constant>P</constant><replaceable>domain</replaceable>] <constant>p</constant><replaceable>bus</replaceable> <constant>s</constant><replaceable>slot</replaceable> [<constant>f</constant><replaceable>function</replaceable>] [<constant>n</constant><replaceable>phys_port_name</replaceable> | <constant>d</constant><replaceable>dev_port</replaceable>]</entry>
+ <entry>PCI geographical location</entry>
+ </row>
+
+ <row>
+ <entry>… <constant>b</constant><replaceable>number</replaceable></entry>
+ <entry>Broadcom bus (BCMA) core number</entry>
+ </row>
+
+ <row>
+ <entry>… <constant>u</constant><replaceable>port</replaceable>… [<constant>c</constant><replaceable>config</replaceable>] [<constant>i</constant><replaceable>interface</replaceable>]</entry>
+ <entry>USB port number chain</entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>CCW and grouped CCW devices are found in IBM System Z mainframes. Any leading zeros and
+ dots are suppressed.</para>
+
+ <para>For PCI, BCMA, and USB devices, the same rules as described above for slot naming are
+ used.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>History</title>
+
+ <para>The following "naming schemes" have been defined:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>rhel-8.0</constant></term>
+
+ <listitem><para>Naming was changed for virtual network interfaces created with SR-IOV and NPAR and
+ for devices where the PCI network controller device does not have a slot number associated.</para>
+
+ <para>SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of
+ <literal>v<replaceable>port</replaceable></literal>, where <replaceable>port</replaceable> is the
+ virtual device number. Previously those virtual devices were named as if completely independent.
+ </para>
+
+ <para>The ninth and later NPAR virtual devices are named following the scheme used for the first
+ eight NPAR partitions. Previously those devices were not renamed and the kernel default
+ ("eth<replaceable>N</replaceable>") was used.</para>
+
+ <para>Names are also generated for PCI devices where the PCI network controller device does not
+ have an associated slot number itself, but one of its parents does. Previously those devices were
+ not renamed and the kernel default was used.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>rhel-8.1</constant></term>
+
+ <para>Same as naming scheme <constant>rhel-8.0</constant>.</para>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>rhel-8.2</constant></term>
+
+ <para>Same as naming scheme <constant>rhel-8.0</constant>.</para>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>rhel-8.3</constant></term>
+
+ <para>Same as naming scheme <constant>rhel-8.0</constant>.</para>
+ </varlistentry>
+
+ <para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
+ particular version of systemd.</para>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Using <command>udevadm test-builtin</command> to display device properties</title>
+
+ <programlisting>$ udevadm test-builtin net_id /sys/class/net/enp0s31f6
+...
+Using default interface naming scheme 'rhel-8.3'.
+ID_NET_NAMING_SCHEME=rhel-8.3
+ID_NET_NAME_MAC=enx54ee75cb1dc0
+ID_OUI_FROM_DATABASE=Wistron InfoComm(Kunshan)Co.,Ltd.
+ID_NET_NAME_PATH=enp0s31f6
+...</programlisting>
+ </example>
+
+ <example>
+ <title>PCI Ethernet card with firmware index "1"</title>
+
+ <programlisting>ID_NET_NAME_ONBOARD=eno1
+ID_NET_NAME_ONBOARD_LABEL=enEthernet Port 1
+ </programlisting>
+ <!-- FIXME: nuke the prefix! -->
+ </example>
+
+ <example>
+ <title>PCI Ethernet card in hotplug slot with firmware index number</title>
+
+ <programlisting># /sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
+ID_NET_NAME_MAC=enx000000000466
+ID_NET_NAME_PATH=enp5s0
+ID_NET_NAME_SLOT=ens1</programlisting>
+ </example>
+
+ <example>
+ <title>PCI Ethernet multi-function card with 2 ports</title>
+
+ <programlisting># /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0
+ID_NET_NAME_MAC=enx78e7d1ea46da
+ID_NET_NAME_PATH=enp2s0f0
+
+# /sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1
+ID_NET_NAME_MAC=enx78e7d1ea46dc
+ID_NET_NAME_PATH=enp2s0f1</programlisting>
+ </example>
+
+ <example>
+ <title>PCI WLAN card</title>
+
+ <programlisting># /sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0
+ID_NET_NAME_MAC=wlx0024d7e31130
+ID_NET_NAME_PATH=wlp3s0</programlisting>
+ </example>
+
+ <example>
+ <title>USB built-in 3G modem</title>
+
+ <programlisting># /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6
+ID_NET_NAME_MAC=wwx028037ec0200
+ID_NET_NAME_PATH=wwp0s29u1u4i6</programlisting>
+ </example>
+
+ <example>
+ <title>USB Android phone</title>
+
+ <programlisting># /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2
+ID_NET_NAME_MAC=enxd626b3450fb5
+ID_NET_NAME_PATH=enp0s29u1u2</programlisting>
+ </example>
+
+ <example>
+ <title>s390 grouped CCW interface</title>
+
+ <programlisting># /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0
+ID_NET_NAME_MAC=enx026d3c00000a
+ID_NET_NAME_PATH=encf5f0</programlisting>
+ </example>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <ulink url="https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames">the
+ original page describing stable interface names</ulink>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index d85dc2848b..aa553d5ade 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -78,6 +78,7 @@
* /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0
* ID_NET_NAME_MAC=enx026d3c00000a
* ID_NET_NAME_PATH=encf5f0
+ * When the code here is changed, man/systemd.net-naming-scheme.xml must be updated too.
*/
#include <errno.h>

View File

@ -0,0 +1,51 @@
From 462420bc7ea22a05bfc2d021d395aade2b8ee7dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= <msekleta@redhat.com>
Date: Mon, 19 Oct 2020 10:56:11 +0200
Subject: [PATCH] udev/net_id: parse _SUN ACPI index as a signed integer
Negative value means there is no match between a PCI device and any of
the slots. In the following commit we will extend this and value of 0
will indicate that there is a match between some slot and PCI device,
but that device is a PCI bridge.
(cherry picked from commit 3e545ae5abcf258791eacbee60c829c100a33274)
Related: #1827462
---
src/udev/udev-builtin-net_id.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index aa553d5ade..ede24dee41 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -391,7 +391,8 @@ static bool is_pci_ari_enabled(struct udev_device *dev) {
static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
struct udev *udev = udev_device_get_udev(names->pcidev);
- unsigned domain, bus, slot, func, dev_port = 0, hotplug_slot = 0;
+ unsigned domain, bus, slot, func, dev_port = 0;
+ int hotplug_slot = -1;
size_t l;
char *s;
const char *attr, *port_name;
@@ -449,15 +450,15 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
hotplug_slot_dev = names->pcidev;
while (hotplug_slot_dev) {
FOREACH_DIRENT_ALL(dent, dir, break) {
- unsigned i;
- int r;
+ int i, r;
char str[PATH_MAX];
_cleanup_free_ char *address = NULL;
if (dent->d_name[0] == '.')
continue;
- r = safe_atou_full(dent->d_name, 10, &i);
- if (i < 1 || r < 0)
+
+ r = safe_atoi(dent->d_name, &i);
+ if (r < 0 || i <= 0)
continue;
if (snprintf_ok(str, sizeof str, "%s/%s/address", slots, dent->d_name) &&

View File

@ -0,0 +1,124 @@
From bb6114af097da0cd9c5081e42db718559130687f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= <msekleta@redhat.com>
Date: Mon, 19 Oct 2020 11:10:31 +0200
Subject: [PATCH] udev/net_id: don't generate slot based names if multiple
devices might claim the same slot
(cherry picked from commit 2c8ec0095e6fd2e72879d4915ff8a9e5c0664d0b)
Resolves: #1827462
---
man/systemd.net-naming-scheme.xml | 15 ++++++++++-
src/udev/udev-builtin-net_id.c | 41 ++++++++++++++++++++++++++-----
2 files changed, 49 insertions(+), 7 deletions(-)
diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml
index a12cc3c460..10e71dcb15 100644
--- a/man/systemd.net-naming-scheme.xml
+++ b/man/systemd.net-naming-scheme.xml
@@ -176,7 +176,10 @@
<para>SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of
<constant>v</constant> and the virtual device number, with any leading zeros removed. The bus
- number is ignored. This device type is found in IBM PowerVMs.</para>
+ number is ignored.</para>
+
+ <para>In some configurations a parent PCI bridge of a given network controller may be associated
+ with a slot. In such case we don't generate this device property to avoid possible naming conflicts.</para>
</varlistentry>
<varlistentry>
@@ -288,6 +291,16 @@
<para>Same as naming scheme <constant>rhel-8.0</constant>.</para>
</varlistentry>
+ <varlistentry>
+ <term><constant>rhel-8.4</constant></term>
+
+ <listitem><para>If the PCI slot is assocated with PCI bridge and that has multiple child network
+ controllers then all of them might derive the same value of <varname>ID_NET_NAME_SLOT</varname>
+ property. That could cause naming conflict if the property is selected as a device name. Now, we detect the
+ situation, slot - bridge relation, and we don't produce the <varname>ID_NET_NAME_SLOT</varname> property to
+ avoid possible naming conflict.</para></listitem>
+ </varlistentry>
+
<para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
particular version of systemd.</para>
</variablelist>
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index ede24dee41..d8c56b62bb 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -124,6 +124,7 @@ typedef enum NamingSchemeFlags {
/* First, the individual features */
NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a40008b8299529c978ed8e11de8f6*/
NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6eab35d1cb9fa73889892702c27be09 */
+ NAMING_BRIDGE_NO_SLOT = 1 << 9, /* Don't use PCI hotplug slot information if the corresponding device is a PCI bridge */
/* And now the masks that combine the features above */
NAMING_V238 = 0,
@@ -132,6 +133,7 @@ typedef enum NamingSchemeFlags {
NAMING_RHEL_8_1 = NAMING_V239,
NAMING_RHEL_8_2 = NAMING_V239,
NAMING_RHEL_8_3 = NAMING_V239,
+ NAMING_RHEL_8_4 = NAMING_V239|NAMING_BRIDGE_NO_SLOT,
_NAMING_SCHEME_FLAGS_INVALID = -1,
} NamingSchemeFlags;
@@ -389,6 +391,26 @@ static bool is_pci_ari_enabled(struct udev_device *dev) {
return streq_ptr(udev_device_get_sysattr_value(dev, "ari_enabled"), "1");
}
+static bool is_pci_bridge(struct udev_device *dev) {
+ const char *v, *p;
+
+ v = udev_device_get_sysattr_value(dev, "modalias");
+ if (!v)
+ return false;
+
+ if (!startswith(v, "pci:"))
+ return false;
+
+ p = strrchr(v, 's');
+ if (!p)
+ return false;
+ if (p[1] != 'c')
+ return false;
+
+ /* PCI device subclass 04 corresponds to PCI bridge */
+ return strneq(p + 2, "04", 2);
+}
+
static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
struct udev *udev = udev_device_get_udev(names->pcidev);
unsigned domain, bus, slot, func, dev_port = 0;
@@ -461,16 +483,23 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) {
if (r < 0 || i <= 0)
continue;
+ /* match slot address with device by stripping the function */
if (snprintf_ok(str, sizeof str, "%s/%s/address", slots, dent->d_name) &&
- read_one_line_file(str, &address) >= 0)
- /* match slot address with device by stripping the function */
- if (startswith(udev_device_get_sysname(hotplug_slot_dev), address))
- hotplug_slot = i;
+ read_one_line_file(str, &address) >= 0 &&
+ startswith(udev_device_get_sysname(hotplug_slot_dev), address)) {
+ hotplug_slot = i;
+
+ /* We found the match between PCI device and slot. However, we won't use the
+ * slot index if the device is a PCI bridge, because it can have other child
+ * devices that will try to claim the same index and that would create name
+ * collision. */
+ if (naming_scheme_has(NAMING_BRIDGE_NO_SLOT) && is_pci_bridge(hotplug_slot_dev))
+ hotplug_slot = 0;
- if (hotplug_slot > 0)
break;
+ }
}
- if (hotplug_slot > 0)
+ if (hotplug_slot >= 0)
break;
rewinddir(dir);
hotplug_slot_dev = udev_device_get_parent_with_subsystem_devtype(hotplug_slot_dev, "pci", NULL);

View File

@ -1,13 +1,11 @@
From bf1b76b47629a7f0eacce4f26d01f3eea46e7bd6 Mon Sep 17 00:00:00 2001
From 573229efeb2c5ade25794deee8cfe2f967414ef7 Mon Sep 17 00:00:00 2001
From: David Tardon <dtardon@redhat.com>
Date: Fri, 6 Nov 2020 10:13:19 +0100
Subject: [PATCH] fix typo in ProtectSystem= option
This was introduced by commit d9ae3222cfbd5d2a48e6dbade6617085cc76f1c1 .
(cherry picked from commit 573229efeb2c5ade25794deee8cfe2f967414ef7)
Resolves: #1934500
Resolves: #1871139
---
units/systemd-resolved.service.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

View File

@ -0,0 +1,34 @@
From 5e74048399c4610da27b5f7fbbb53784030aeb70 Mon Sep 17 00:00:00 2001
From: David Tardon <dtardon@redhat.com>
Date: Mon, 9 Nov 2020 09:27:02 +0100
Subject: [PATCH] remove references of non-existent man pages
This is a follow-up to commit 8ad89170001c9aba8849630ddb5da81d9e24a1bc,
which introduced the man page change.
Resolves: #1876807
---
man/systemd.special.xml | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/man/systemd.special.xml b/man/systemd.special.xml
index c9d4345016..fe6324a4a0 100644
--- a/man/systemd.special.xml
+++ b/man/systemd.special.xml
@@ -657,16 +657,6 @@
target unit and pull in the target from it, also with <varname>Requires=</varname>. Note that by default this
target unit is not part of the initial boot transaction, but is supposed to be pulled in only if required by
units that want to run only on successful boots.</para>
-
- <para>See
- <citerefentry><refentrytitle>systemd-boot-check-no-failures.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- for a service that implements a generic system health check and orders itself before
- <filename>boot-complete.target</filename>.</para>
-
- <para>See
- <citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- for a service that propagates boot success information to the boot loader, and orders itself after
- <filename>boot-complete.target</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>

View File

@ -0,0 +1,91 @@
From b14c82dd9f9fcc42810614cf02efe8651897d36f Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Wed, 10 Jun 2020 20:19:41 +0200
Subject: [PATCH] log: Prefer logging to CLI unless JOURNAL_STREAM is set
(cherry picked from commit bc694c06e60505efeb09e5278a7b22cdfa23975e)
Resolves: #1865840
---
src/basic/log.c | 32 +++++++++++++++++++++++++++++---
test/TEST-21-SYSUSERS/test.sh | 3 +--
2 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/src/basic/log.c b/src/basic/log.c
index 48c094b548..9387e56a57 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -10,6 +10,7 @@
#include <string.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/un.h>
@@ -19,6 +20,7 @@
#include "sd-messages.h"
#include "alloc-util.h"
+#include "extract-word.h"
#include "fd-util.h"
#include "format-util.h"
#include "io-util.h"
@@ -220,6 +222,32 @@ fail:
return r;
}
+static bool stderr_is_journal(void) {
+ _cleanup_free_ char *w = NULL;
+ const char *e;
+ uint64_t dev, ino;
+ struct stat st;
+
+ e = getenv("JOURNAL_STREAM");
+ if (!e)
+ return false;
+
+ if (extract_first_word(&e, &w, ":", EXTRACT_DONT_COALESCE_SEPARATORS) <= 0)
+ return false;
+ if (!e)
+ return false;
+
+ if (safe_atou64(w, &dev) < 0)
+ return false;
+ if (safe_atou64(e, &ino) < 0)
+ return false;
+
+ if (fstat(STDERR_FILENO, &st) < 0)
+ return false;
+
+ return st.st_dev == dev && st.st_ino == ino;
+}
+
int log_open(void) {
int r;
@@ -239,9 +267,7 @@ int log_open(void) {
return 0;
}
- if (log_target != LOG_TARGET_AUTO ||
- getpid_cached() == 1 ||
- isatty(STDERR_FILENO) <= 0) {
+ if (log_target != LOG_TARGET_AUTO || getpid_cached() == 1 || stderr_is_journal()) {
if (!prohibit_ipc &&
IN_SET(log_target, LOG_TARGET_AUTO,
diff --git a/test/TEST-21-SYSUSERS/test.sh b/test/TEST-21-SYSUSERS/test.sh
index b1049e720d..3460d71f22 100755
--- a/test/TEST-21-SYSUSERS/test.sh
+++ b/test/TEST-21-SYSUSERS/test.sh
@@ -108,8 +108,7 @@ test_run() {
echo "*** Running test $f"
prepare_testdir ${f%.input}
cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
- systemd-sysusers --root=$TESTDIR 2> /dev/null
- journalctl -t systemd-sysusers -o cat | tail -n1 > $TESTDIR/tmp/err
+ systemd-sysusers --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/tmp/err
if ! diff -u $TESTDIR/tmp/err ${f%.*}.expected-err; then
echo "**** Unexpected error output for $f"
cat $TESTDIR/tmp/err

View File

@ -0,0 +1,58 @@
From f0d9e0cb24958bc11c8d83f0a3de651def2aa1d6 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 30 Apr 2020 18:30:56 +0200
Subject: [PATCH] locale-util: add new helper locale_is_installed()
This new helper checks whether the specified locale is installed. It's
distinct from locale_is_valid() which just superficially checks if a
string looks like something that could be a valid locale.
Heavily inspired by @jsynacek's #13964.
Replaces: #13964
(cherry picked from commit 23fa786ca67ed3a32930ff1a7b175ac823db187c)
Related: #1755287
---
src/basic/locale-util.c | 15 +++++++++++++++
src/basic/locale-util.h | 1 +
2 files changed, 16 insertions(+)
diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c
index 7cd143ea6f..42ef309ebd 100644
--- a/src/basic/locale-util.c
+++ b/src/basic/locale-util.c
@@ -204,6 +204,21 @@ bool locale_is_valid(const char *name) {
return true;
}
+int locale_is_installed(const char *name) {
+ if (!locale_is_valid(name))
+ return false;
+
+ if (STR_IN_SET(name, "C", "POSIX")) /* These ones are always OK */
+ return true;
+
+ _cleanup_(freelocalep) locale_t loc =
+ newlocale(LC_ALL_MASK, name, 0);
+ if (loc == (locale_t) 0)
+ return errno == ENOMEM ? -ENOMEM : false;
+
+ return true;
+}
+
void init_gettext(void) {
setlocale(LC_ALL, "");
textdomain(GETTEXT_PACKAGE);
diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h
index 368675f286..b40f9c641a 100644
--- a/src/basic/locale-util.h
+++ b/src/basic/locale-util.h
@@ -31,6 +31,7 @@ typedef enum LocaleVariable {
int get_locales(char ***l);
bool locale_is_valid(const char *name);
+int locale_is_installed(const char *name);
#define _(String) gettext(String)
#define N_(String) String

View File

@ -0,0 +1,53 @@
From 3d08c7971a80370f60dd14b068779851e0f82c24 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 30 Apr 2020 18:32:55 +0200
Subject: [PATCH] test: add test case for locale_is_installed()
(cherry picked from commit b45b0a69bb7ef3e6e66d443eae366b6d1c387cab)
Related: #1755287
---
src/test/test-locale-util.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c
index 0c3f6a62ed..0d50c33ce5 100644
--- a/src/test/test-locale-util.c
+++ b/src/test/test-locale-util.c
@@ -34,6 +34,28 @@ static void test_locale_is_valid(void) {
assert_se(!locale_is_valid("\x01gar\x02 bage\x03"));
}
+static void test_locale_is_installed(void) {
+ log_info("/* %s */", __func__);
+
+ /* Always available */
+ assert_se(locale_is_installed("POSIX") > 0);
+ assert_se(locale_is_installed("C") > 0);
+
+ /* Might, or might not be installed. */
+ assert_se(locale_is_installed("en_EN.utf8") >= 0);
+ assert_se(locale_is_installed("fr_FR.utf8") >= 0);
+ assert_se(locale_is_installed("fr_FR@euro") >= 0);
+ assert_se(locale_is_installed("fi_FI") >= 0);
+
+ /* Definitely not valid */
+ assert_se(locale_is_installed("") == 0);
+ assert_se(locale_is_installed("/usr/bin/foo") == 0);
+ assert_se(locale_is_installed("\x01gar\x02 bage\x03") == 0);
+
+ /* Definitely not installed */
+ assert_se(locale_is_installed("zz_ZZ") == 0);
+}
+
static void test_keymaps(void) {
_cleanup_strv_free_ char **kmaps = NULL;
char **p;
@@ -95,6 +117,7 @@ static void dump_special_glyphs(void) {
int main(int argc, char *argv[]) {
test_get_locales();
test_locale_is_valid();
+ test_locale_is_installed();
test_keymaps();
dump_special_glyphs();

View File

@ -0,0 +1,232 @@
From 5813180a75aa1ef90f6d3459fc5beb099b815cfb Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 30 Apr 2020 18:32:44 +0200
Subject: [PATCH] tree-wide: port various bits over to locale_is_installed()
(cherry picked from commit a00a78b84e2ab352b3144bfae8bc578d172303be)
Resolves: #1755287
---
src/firstboot/firstboot.c | 30 ++++++++------
src/locale/localed.c | 87 ++++++++++++++++++++++++---------------
2 files changed, 71 insertions(+), 46 deletions(-)
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index a98e53b3a3..7e177a50fa 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -192,6 +192,14 @@ static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char *
}
}
+static bool locale_is_ok(const char *name) {
+
+ if (arg_root)
+ return locale_is_valid(name);
+
+ return locale_is_installed(name) > 0;
+}
+
static int prompt_locale(void) {
_cleanup_strv_free_ char **locales = NULL;
int r;
@@ -215,14 +223,14 @@ static int prompt_locale(void) {
putchar('\n');
- r = prompt_loop("Please enter system locale name or number", locales, locale_is_valid, &arg_locale);
+ r = prompt_loop("Please enter system locale name or number", locales, locale_is_ok, &arg_locale);
if (r < 0)
return r;
if (isempty(arg_locale))
return 0;
- r = prompt_loop("Please enter system message locale name or number", locales, locale_is_valid, &arg_locale_messages);
+ r = prompt_loop("Please enter system message locale name or number", locales, locale_is_ok, &arg_locale_messages);
if (r < 0)
return r;
@@ -780,11 +788,6 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_LOCALE:
- if (!locale_is_valid(optarg)) {
- log_error("Locale %s is not valid.", optarg);
- return -EINVAL;
- }
-
r = free_and_strdup(&arg_locale, optarg);
if (r < 0)
return log_oom();
@@ -792,11 +795,6 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_LOCALE_MESSAGES:
- if (!locale_is_valid(optarg)) {
- log_error("Locale %s is not valid.", optarg);
- return -EINVAL;
- }
-
r = free_and_strdup(&arg_locale_messages, optarg);
if (r < 0)
return log_oom();
@@ -922,6 +920,14 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached("Unhandled option");
}
+ /* We check if the specified locale strings are valid down here, so that we can take --root= into
+ * account when looking for the locale files. */
+
+ if (arg_locale && !locale_is_ok(arg_locale))
+ return log_error_errno(EINVAL, "Locale %s is not installed.", arg_locale);
+ if (arg_locale_messages && !locale_is_ok(arg_locale_messages))
+ return log_error_errno(EINVAL, "Locale %s is not installed.", arg_locale_messages);
+
return 1;
}
diff --git a/src/locale/localed.c b/src/locale/localed.c
index 253973fd49..d6ed40babe 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -259,18 +259,57 @@ static void locale_free(char ***l) {
(*l)[p] = mfree((*l)[p]);
}
+static int process_locale_list_item(
+ const char *assignment,
+ char *new_locale[static _VARIABLE_LC_MAX],
+ sd_bus_error *error) {
+
+ assert(assignment);
+ assert(new_locale);
+
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) {
+ const char *name, *e;
+
+ assert_se(name = locale_variable_to_string(p));
+
+ e = startswith(assignment, name);
+ if (!e)
+ continue;
+
+ if (*e != '=')
+ continue;
+
+ e++;
+
+ if (!locale_is_valid(e))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e);
+ if (locale_is_installed(e) <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e);
+ if (new_locale[p])
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name);
+
+ new_locale[p] = strdup(e);
+ if (!new_locale[p])
+ return -ENOMEM;
+
+ return 0;
+ }
+
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment);
+}
+
static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
Context *c = userdata;
_cleanup_strv_free_ char **settings = NULL, **l = NULL;
char *new_locale[_VARIABLE_LC_MAX] = {}, **i;
_cleanup_(locale_free) _unused_ char **dummy = new_locale;
bool modified = false;
- int interactive, p, r;
+ int interactive, r;
assert(m);
assert(c);
- r = bus_message_read_strv_extend(m, &l);
+ r = sd_bus_message_read_strv(m, &l);
if (r < 0)
return r;
@@ -279,11 +318,13 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
return r;
/* If single locale without variable name is provided, then we assume it is LANG=. */
- if (strv_length(l) == 1 && !strchr(*l, '=')) {
- if (!locale_is_valid(*l))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
+ if (strv_length(l) == 1 && !strchr(l[0], '=')) {
+ if (!locale_is_valid(l[0]))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]);
+ if (locale_is_installed(l[0]) <= 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]);
- new_locale[VARIABLE_LANG] = strdup(*l);
+ new_locale[VARIABLE_LANG] = strdup(l[0]);
if (!new_locale[VARIABLE_LANG])
return -ENOMEM;
@@ -292,31 +333,9 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
/* Check whether a variable is valid */
STRV_FOREACH(i, l) {
- bool valid = false;
-
- for (p = 0; p < _VARIABLE_LC_MAX; p++) {
- size_t k;
- const char *name;
-
- name = locale_variable_to_string(p);
- assert(name);
-
- k = strlen(name);
- if (startswith(*i, name) &&
- (*i)[k] == '=' &&
- locale_is_valid((*i) + k + 1)) {
- valid = true;
-
- new_locale[p] = strdup((*i) + k + 1);
- if (!new_locale[p])
- return -ENOMEM;
-
- break;
- }
- }
-
- if (!valid)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
+ r = process_locale_list_item(*i, new_locale, error);
+ if (r < 0)
+ return r;
}
/* If LANG was specified, but not LANGUAGE, check if we should
@@ -339,7 +358,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
}
/* Merge with the current settings */
- for (p = 0; p < _VARIABLE_LC_MAX; p++)
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
if (!isempty(c->locale[p]) && isempty(new_locale[p])) {
new_locale[p] = strdup(c->locale[p]);
if (!new_locale[p])
@@ -348,7 +367,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
locale_simplify(new_locale);
- for (p = 0; p < _VARIABLE_LC_MAX; p++)
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
if (!streq_ptr(c->locale[p], new_locale[p])) {
modified = true;
break;
@@ -373,7 +392,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
if (r == 0)
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
- for (p = 0; p < _VARIABLE_LC_MAX; p++)
+ for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
free_and_replace(c->locale[p], new_locale[p]);
r = locale_write_data(c, &settings);

View File

@ -0,0 +1,339 @@
From 4c41ad9418058aefb2d2732b0b65da9c7cdf5151 Mon Sep 17 00:00:00 2001
From: Ruixin Bao <rubao@redhat.com>
Date: Tue, 21 Aug 2018 20:40:56 +0000
Subject: [PATCH] install: allow instantiated units to be enabled via presets
This patch implements https://github.com/systemd/systemd/issues/9421.
The .preset file now is able to take a rule in the format of:(e.g)
enable foo@.service bar0 bar1 bar2
In the above example, when preset-all is called, all three instances of
foo@bar0.service, foo@bar1.service and foo@bar2.service will be enabled.
When preset is called on a single service(e.g: foo@bar1.service), only
the mentioned one(foo@bar1.service) will be enabled.
Tests are added for future regression.
(cherry picked from commit 4c9565eea534cd233a913c8c21f7920dba229743)
Resolves: #1812972
---
src/shared/install.c | 155 ++++++++++++++++++++++++++++++-----
src/test/test-install-root.c | 57 +++++++++++++
2 files changed, 193 insertions(+), 19 deletions(-)
diff --git a/src/shared/install.c b/src/shared/install.c
index 77ae812878..1d4beaa83b 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -60,6 +60,7 @@ typedef enum {
typedef struct {
char *pattern;
PresetAction action;
+ char **instances;
} PresetRule;
typedef struct {
@@ -87,8 +88,10 @@ static inline void presets_freep(Presets *p) {
if (!p)
return;
- for (i = 0; i < p->n_rules; i++)
+ for (i = 0; i < p->n_rules; i++) {
free(p->rules[i].pattern);
+ strv_free(p->rules[i].instances);
+ }
free(p->rules);
p->n_rules = 0;
@@ -2755,6 +2758,39 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *
return 1;
}
+static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) {
+ _cleanup_strv_free_ char **instances = NULL;
+ _cleanup_free_ char *unit_name = NULL;
+ int r;
+
+ assert(pattern);
+ assert(out_instances);
+ assert(out_unit_name);
+
+ r = extract_first_word(&pattern, &unit_name, NULL, 0);
+ if (r < 0)
+ return r;
+
+ /* We handle the instances logic when unit name is extracted */
+ if (pattern) {
+ /* We only create instances when a rule of templated unit
+ * is seen. A rule like enable foo@.service a b c will
+ * result in an array of (a, b, c) as instance names */
+ if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE))
+ return -EINVAL;
+
+ instances = strv_split(pattern, WHITESPACE);
+ if (!instances)
+ return -ENOMEM;
+
+ *out_instances = TAKE_PTR(instances);
+ }
+
+ *out_unit_name = TAKE_PTR(unit_name);
+
+ return 0;
+}
+
static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
_cleanup_(presets_freep) Presets ps = {};
size_t n_allocated = 0;
@@ -2824,15 +2860,20 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
parameter = first_word(l, "enable");
if (parameter) {
- char *pattern;
+ char *unit_name;
+ char **instances = NULL;
- pattern = strdup(parameter);
- if (!pattern)
- return -ENOMEM;
+ /* Unit_name will remain the same as parameter when no instances are specified */
+ r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances);
+ if (r < 0) {
+ log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
+ continue;
+ }
rule = (PresetRule) {
- .pattern = pattern,
+ .pattern = unit_name,
.action = PRESET_ENABLE,
+ .instances = instances,
};
}
@@ -2868,15 +2909,71 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
return 0;
}
-static int query_presets(const char *name, const Presets presets) {
+static int pattern_match_multiple_instances(
+ const PresetRule rule,
+ const char *unit_name,
+ char ***ret) {
+
+ _cleanup_free_ char *templated_name = NULL;
+ int r;
+
+ /* If no ret is needed or the rule itself does not have instances
+ * initalized, we return not matching */
+ if (!ret || !rule.instances)
+ return 0;
+
+ r = unit_name_template(unit_name, &templated_name);
+ if (r < 0)
+ return r;
+ if (!streq(rule.pattern, templated_name))
+ return 0;
+
+ /* Compose a list of specified instances when unit name is a template */
+ if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
+ _cleanup_free_ char *prefix = NULL;
+ _cleanup_strv_free_ char **out_strv = NULL;
+ char **iter;
+
+ r = unit_name_to_prefix(unit_name, &prefix);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(iter, rule.instances) {
+ _cleanup_free_ char *name = NULL;
+ r = unit_name_build(prefix, *iter, ".service", &name);
+ if (r < 0)
+ return r;
+ r = strv_extend(&out_strv, name);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(out_strv);
+ return 1;
+ } else {
+ /* We now know the input unit name is an instance name */
+ _cleanup_free_ char *instance_name = NULL;
+
+ r = unit_name_to_instance(unit_name, &instance_name);
+ if (r < 0)
+ return r;
+
+ if (strv_find(rule.instances, instance_name))
+ return 1;
+ }
+ return 0;
+}
+
+static int query_presets(const char *name, const Presets presets, char ***instance_name_list) {
PresetAction action = PRESET_UNKNOWN;
size_t i;
-
+ char **s;
if (!unit_name_is_valid(name, UNIT_NAME_ANY))
return -EINVAL;
for (i = 0; i < presets.n_rules; i++)
- if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
+ if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 ||
+ fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
action = presets.rules[i].action;
break;
}
@@ -2886,7 +2983,11 @@ static int query_presets(const char *name, const Presets presets) {
log_debug("Preset files don't specify rule for %s. Enabling.", name);
return 1;
case PRESET_ENABLE:
- log_debug("Preset files say enable %s.", name);
+ if (instance_name_list && *instance_name_list)
+ STRV_FOREACH(s, *instance_name_list)
+ log_debug("Preset files say enable %s.", *s);
+ else
+ log_debug("Preset files say enable %s.", name);
return 1;
case PRESET_DISABLE:
log_debug("Preset files say disable %s.", name);
@@ -2904,7 +3005,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
if (r < 0)
return r;
- return query_presets(name, presets);
+ return query_presets(name, presets, NULL);
}
static int execute_preset(
@@ -2964,6 +3065,7 @@ static int preset_prepare_one(
size_t *n_changes) {
_cleanup_(install_context_done) InstallContext tmp = {};
+ _cleanup_strv_free_ char **instance_name_list = NULL;
UnitFileInstallInfo *i;
int r;
@@ -2979,19 +3081,34 @@ static int preset_prepare_one(
return 0;
}
- r = query_presets(name, presets);
+ r = query_presets(name, presets, &instance_name_list);
if (r < 0)
return r;
if (r > 0) {
- r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &i, changes, n_changes);
- if (r < 0)
- return r;
+ if (instance_name_list) {
+ char **s;
+ STRV_FOREACH(s, instance_name_list) {
+ r = install_info_discover(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
+ if (r < 0)
+ return r;
+
+ r = install_info_may_process(i, paths, changes, n_changes);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
+ if (r < 0)
+ return r;
+
+ r = install_info_may_process(i, paths, changes, n_changes);
+ if (r < 0)
+ return r;
+ }
- r = install_info_may_process(i, paths, changes, n_changes);
- if (r < 0)
- return r;
} else
r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
&i, changes, n_changes);
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index 15dd3c6966..dbbcfe4297 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -983,6 +983,62 @@ static void test_with_dropin_template(const char *root) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
}
+static void test_preset_multiple_instances(const char *root) {
+ UnitFileChange *changes = NULL;
+ size_t n_changes = 0;
+ const char *p;
+ UnitFileState state;
+
+ /* Set up template service files and preset file */
+ p = strjoina(root, "/usr/lib/systemd/system/foo@.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "DefaultInstance=def\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
+ assert_se(write_string_file(p,
+ "enable foo@.service bar0 bar1 bartest\n"
+ "enable emptylist@.service\n" /* This line ensures the old functionality for templated unit still works */
+ "disable *\n" , WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ /* Preset a single instantiated unit specified in the list */
+ assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_SYMLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0);
+ assert_se(n_changes == 1);
+ assert_se(changes[0].type == UNIT_FILE_UNLINK);
+ p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service");
+ assert_se(streq(changes[0].path, p));
+ unit_file_changes_free(changes, n_changes);
+ changes = NULL; n_changes = 0;
+
+ /* Check for preset-all case, only instances on the list should be enabled, not including the default instance */
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
+ assert_se(n_changes > 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+
+}
+
int main(int argc, char *argv[]) {
char root[] = "/tmp/rootXXXXXX";
const char *p;
@@ -1012,6 +1068,7 @@ int main(int argc, char *argv[]) {
test_indirect(root);
test_preset_and_list(root);
test_preset_order(root);
+ test_preset_multiple_instances(root);
test_revert(root);
test_static_instance(root);
test_with_dropin(root);

View File

@ -0,0 +1,127 @@
From eacb511fc0d1e3c5857cb041ad162fb78b4381cc Mon Sep 17 00:00:00 2001
From: Ruixin Bao <rubao@redhat.com>
Date: Sun, 26 Aug 2018 20:00:03 +0000
Subject: [PATCH] install: small refactor to combine two function calls into
one function
Combine consecutive function calls of install_info_discover and
install_info_may_process into one short helper function.
(cherry picked from commit 1e475a0ab4c46eb07f3df3fb24f5a7c3e1fa20b1)
Related: #1812972
---
src/shared/install.c | 61 ++++++++++++++++++++++----------------------
1 file changed, 30 insertions(+), 31 deletions(-)
diff --git a/src/shared/install.c b/src/shared/install.c
index 1d4beaa83b..263b239f10 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -1676,6 +1676,25 @@ static int install_info_discover(
return r;
}
+static int install_info_discover_and_check(
+ UnitFileScope scope,
+ InstallContext *c,
+ const LookupPaths *paths,
+ const char *name,
+ SearchFlags flags,
+ UnitFileInstallInfo **ret,
+ UnitFileChange **changes,
+ size_t *n_changes) {
+
+ int r;
+
+ r = install_info_discover(scope, c, paths, name, flags, ret, changes, n_changes);
+ if (r < 0)
+ return r;
+
+ return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes);
+}
+
static int install_info_symlink_alias(
UnitFileInstallInfo *i,
const LookupPaths *paths,
@@ -2399,11 +2418,8 @@ int unit_file_add_dependency(
if (!config_path)
return -ENXIO;
- r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &target_info, changes, n_changes);
- if (r < 0)
- return r;
- r = install_info_may_process(target_info, &paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &target_info, changes, n_changes);
if (r < 0)
return r;
@@ -2412,11 +2428,8 @@ int unit_file_add_dependency(
STRV_FOREACH(f, files) {
char ***l;
- r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &i, changes, n_changes);
- if (r < 0)
- return r;
- r = install_info_may_process(i, &paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
@@ -2467,11 +2480,8 @@ int unit_file_enable(
return -ENXIO;
STRV_FOREACH(f, files) {
- r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &i, changes, n_changes);
- if (r < 0)
- return r;
- r = install_info_may_process(i, &paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
@@ -2585,10 +2595,7 @@ int unit_file_set_default(
if (r < 0)
return r;
- r = install_info_discover(scope, &c, &paths, name, 0, &i, changes, n_changes);
- if (r < 0)
- return r;
- r = install_info_may_process(i, &paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, &c, &paths, name, 0, &i, changes, n_changes);
if (r < 0)
return r;
@@ -3089,22 +3096,14 @@ static int preset_prepare_one(
if (instance_name_list) {
char **s;
STRV_FOREACH(s, instance_name_list) {
- r = install_info_discover(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &i, changes, n_changes);
- if (r < 0)
- return r;
-
- r = install_info_may_process(i, paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
}
} else {
- r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
- &i, changes, n_changes);
- if (r < 0)
- return r;
-
- r = install_info_may_process(i, paths, changes, n_changes);
+ r = install_info_discover_and_check(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
+ &i, changes, n_changes);
if (r < 0)
return r;
}

View File

@ -0,0 +1,28 @@
From 7444c6ed3628484dfed2f204c5b78a06a50f4bd8 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 29 Aug 2018 23:27:42 +0900
Subject: [PATCH] test: fix a memleak
Follow-up for #9901.
Fixes #9968.
(cherry picked from commit efa146369398fdb73f1cd177eb2522822ebf559c)
Related: #1812972
---
src/test/test-install-root.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index dbbcfe4297..fe1ca5b16f 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -1037,6 +1037,7 @@ static void test_preset_multiple_instances(const char *root) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+ unit_file_changes_free(changes, n_changes);
}
int main(int argc, char *argv[]) {

View File

@ -0,0 +1,55 @@
From 55df2fd634f900419b718ed354132cc86cd533dd Mon Sep 17 00:00:00 2001
From: Joerg Behrmann <behrmann@physik.fu-berlin.de>
Date: Tue, 10 Mar 2020 16:34:13 +0100
Subject: [PATCH] docs: Add syntax for templated units to systemd.preset man
page
This documents the syntax
enable template@.service foo bar baz
that was introduced in #9901 to preset templated units.
(cherry picked from commit 1f667d8a7cff4355cd23ebebeb4d7179e3498eb8)
Related: #1812972
---
man/systemd.preset.xml | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml
index cf807bd4c8..df401f00f3 100644
--- a/man/systemd.preset.xml
+++ b/man/systemd.preset.xml
@@ -71,8 +71,11 @@
either the word <literal>enable</literal> or
<literal>disable</literal> followed by a space and a unit name
(possibly with shell style wildcards), separated by newlines.
- Empty lines and lines whose first non-whitespace character is # or
- ; are ignored.</para>
+ Empty lines and lines whose first non-whitespace character is <literal>#</literal> or
+ <literal>;</literal> are ignored. Multiple instance names for unit
+ templates may be specified as a space separated list at the end of
+ the line instead of the customary position between <literal>@</literal>
+ and the unit suffix.</para>
<para>Presets must refer to the "real" unit file, and not to any aliases. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
@@ -124,6 +127,17 @@ disable *</programlisting>
<literal>99-</literal>, it will be read last and hence can easily
be overridden by spin or administrator preset policy.</para>
+ <example>
+ <title>Enable multiple template instances</title>
+
+ <programlisting># /usr/lib/systemd/system-preset/80-dirsrv.preset
+
+enable dirsrv@.service foo bar baz</programlisting>
+ </example>
+
+ <para>This enables all three of <filename>dirsrv@foo.service</filename>,
+ <filename>dirsrv@bar.service</filename> and <filename>dirsrv@baz.service</filename>.</para>
+
<example>
<title>A GNOME spin</title>

View File

@ -0,0 +1,45 @@
From db2816ee32fc81ba339175469e46b5dca7af8833 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Sat, 22 Aug 2020 11:58:15 +0200
Subject: [PATCH] shared/install: fix preset operations for non-service
instantiated units
Fixes https://github.com/coreos/ignition/issues/1064.
(cherry picked from commit 47ab95fe4315b3f7ee5a3694460a744bb88c52fd)
Related: #1812972
---
src/shared/install.c | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/src/shared/install.c b/src/shared/install.c
index 263b239f10..c2847df3f8 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -2937,20 +2937,17 @@ static int pattern_match_multiple_instances(
/* Compose a list of specified instances when unit name is a template */
if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
- _cleanup_free_ char *prefix = NULL;
_cleanup_strv_free_ char **out_strv = NULL;
- char **iter;
-
- r = unit_name_to_prefix(unit_name, &prefix);
- if (r < 0)
- return r;
+ char **iter;
STRV_FOREACH(iter, rule.instances) {
_cleanup_free_ char *name = NULL;
- r = unit_name_build(prefix, *iter, ".service", &name);
+
+ r = unit_name_replace_instance(unit_name, *iter, &name);
if (r < 0)
return r;
- r = strv_extend(&out_strv, name);
+
+ r = strv_consume(&out_strv, TAKE_PTR(name));
if (r < 0)
return r;
}

View File

@ -0,0 +1,30 @@
From 8cff80d7fc28ca04bd6c8e2257b46d96bea338ce Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 18 Oct 2018 19:48:18 +0200
Subject: [PATCH] introduce setsockopt_int() helper
As suggested by @heftig:
https://github.com/systemd/systemd/commit/6d5e65f6454212cd400d0ebda34978a9f20cc26a#commitcomment-30938667
(cherry picked from commit 2ff48e981e6cd1ccbfae49943274d9c8319a5e5d)
Related: #1887181
---
src/basic/socket-util.h | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 82781a0de1..616f2e0d05 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -183,3 +183,10 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
})
int socket_ioctl_fd(void);
+
+static inline int setsockopt_int(int fd, int level, int optname, int value) {
+ if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0)
+ return -errno;
+
+ return 0;
+}

View File

@ -0,0 +1,57 @@
From 96681723232e9eb0182279086fef291283004806 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 27 May 2020 19:27:51 +0200
Subject: [PATCH] socket-util: add generic socket_pass_pktinfo() helper
The helper turns on the protocol specific "packet info" structure cmsg
for three relevant protocols we know.
(cherry picked from commit 35a3eb9bdc95d1e6ba25bc65c78959ea104e45a1)
Related: #1887181
---
src/basic/socket-util.c | 23 +++++++++++++++++++++++
src/basic/socket-util.h | 2 ++
2 files changed, 25 insertions(+)
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 986bc6e67f..053bcba670 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -1246,3 +1246,26 @@ int socket_ioctl_fd(void) {
return fd;
}
+
+int socket_pass_pktinfo(int fd, bool b) {
+ int af;
+ socklen_t sl = sizeof(af);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0)
+ return -errno;
+
+ switch (af) {
+
+ case AF_INET:
+ return setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, b);
+
+ case AF_INET6:
+ return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, b);
+
+ case AF_NETLINK:
+ return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b);
+
+ default:
+ return -EAFNOSUPPORT;
+ }
+}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 616f2e0d05..c7c9ad34d6 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -190,3 +190,5 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) {
return 0;
}
+
+int socket_pass_pktinfo(int fd, bool b);

View File

@ -0,0 +1,156 @@
From 905a97ce65352d80af7260d34b74fd8342792c35 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 27 May 2020 19:36:56 +0200
Subject: [PATCH] core: add new PassPacketInfo= socket unit property
(cherry picked from commit a3d19f5d99c44940831a33df8b5bece4aaf749f7)
Resolves: #1887181
---
doc/TRANSIENT-SETTINGS.md | 1 +
man/systemd.socket.xml | 9 +++++++++
src/core/dbus-socket.c | 4 ++++
src/core/load-fragment-gperf.gperf.m4 | 1 +
src/core/socket.c | 8 ++++++++
src/core/socket.h | 1 +
src/shared/bus-unit-util.c | 3 +--
test/fuzz/fuzz-unit-file/directives.service | 1 +
8 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md
index 995b8797ef..de0ef9cc49 100644
--- a/doc/TRANSIENT-SETTINGS.md
+++ b/doc/TRANSIENT-SETTINGS.md
@@ -410,6 +410,7 @@ Most socket unit settings are available to transient units.
✓ Broadcast=
✓ PassCredentials=
✓ PassSecurity=
+✓ PassPacketInfo=
✓ TCPCongestion=
✓ ReusePort=
✓ MessageQueueMaxMessages=
diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml
index 8676b4e03f..a908d5b6d8 100644
--- a/man/systemd.socket.xml
+++ b/man/systemd.socket.xml
@@ -712,6 +712,15 @@
Defaults to <option>false</option>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>PassPacketInfo=</varname></term>
+ <listitem><para>Takes a boolean value. This controls the <constant>IP_PKTINFO</constant>,
+ <constant>IPV6_RECVPKTINFO</constant> and <constant>NETLINK_PKTINFO</constant> socket options, which
+ enable reception of additional per-packet metadata as ancillary message, on
+ <constant>AF_INET</constant>, <constant>AF_INET6</constant> and <constant>AF_UNIX</constant> sockets.
+ Defaults to <option>false</option>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>TCPCongestion=</varname></term>
<listitem><para>Takes a string value. Controls the TCP
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index fa6bbe2c6f..17494b80c8 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -104,6 +104,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("Broadcast", "b", bus_property_get_bool, offsetof(Socket, broadcast), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PassCredentials", "b", bus_property_get_bool, offsetof(Socket, pass_cred), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("PassPacketInfo", "b", bus_property_get_bool, offsetof(Socket, pass_pktinfo), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -205,6 +206,9 @@ static int bus_socket_set_transient_property(
if (streq(name, "PassSecurity"))
return bus_set_transient_bool(u, name, &s->pass_sec, message, flags, error);
+ if (streq(name, "PassPacketInfo"))
+ return bus_set_transient_bool(u, name, &s->pass_pktinfo, message, flags, error);
+
if (streq(name, "ReusePort"))
return bus_set_transient_bool(u, name, &s->reuse_port, message, flags, error);
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 156a4d0a6d..7d683cc84b 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -381,6 +381,7 @@ Socket.Transparent, config_parse_bool, 0,
Socket.Broadcast, config_parse_bool, 0, offsetof(Socket, broadcast)
Socket.PassCredentials, config_parse_bool, 0, offsetof(Socket, pass_cred)
Socket.PassSecurity, config_parse_bool, 0, offsetof(Socket, pass_sec)
+Socket.PassPacketInfo, config_parse_bool, 0, offsetof(Socket, pass_pktinfo)
Socket.TCPCongestion, config_parse_string, 0, offsetof(Socket, tcp_congestion)
Socket.ReusePort, config_parse_bool, 0, offsetof(Socket, reuse_port)
Socket.MessageQueueMaxMessages, config_parse_long, 0, offsetof(Socket, mq_maxmsg)
diff --git a/src/core/socket.c b/src/core/socket.c
index 97c3a7fc9a..50c32ed8f4 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -660,6 +660,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
"%sBroadcast: %s\n"
"%sPassCredentials: %s\n"
"%sPassSecurity: %s\n"
+ "%sPassPacketInfo: %s\n"
"%sTCPCongestion: %s\n"
"%sRemoveOnStop: %s\n"
"%sWritable: %s\n"
@@ -678,6 +679,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(s->broadcast),
prefix, yes_no(s->pass_cred),
prefix, yes_no(s->pass_sec),
+ prefix, yes_no(s->pass_pktinfo),
prefix, strna(s->tcp_congestion),
prefix, yes_no(s->remove_on_stop),
prefix, yes_no(s->writable),
@@ -1099,6 +1101,12 @@ static void socket_apply_socket_options(Socket *s, int fd) {
log_unit_warning_errno(UNIT(s), errno, "SO_PASSSEC failed: %m");
}
+ if (s->pass_pktinfo) {
+ r = socket_pass_pktinfo(fd, true);
+ if (r < 0)
+ log_unit_warning_errno(UNIT(s), r, "Failed to enable packet info socket option: %m");
+ }
+
if (s->priority >= 0)
if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0)
log_unit_warning_errno(UNIT(s), errno, "SO_PRIORITY failed: %m");
diff --git a/src/core/socket.h b/src/core/socket.h
index b7a25d91fd..2409dbf2a0 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -121,6 +121,7 @@ struct Socket {
bool broadcast;
bool pass_cred;
bool pass_sec;
+ bool pass_pktinfo;
/* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
SocketAddressBindIPv6Only bind_ipv6_only;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index daa2c2dce5..9010448aaf 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -1478,8 +1478,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
if (STR_IN_SET(field,
"Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast",
"PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet",
- "FlushPending"))
-
+ "FlushPending", "PassPacketInfo"))
return bus_append_parse_boolean(m, field, eq);
if (STR_IN_SET(field, "Priority", "IPTTL", "Mark"))
diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
index 9d0530df72..8fde27fc90 100644
--- a/test/fuzz/fuzz-unit-file/directives.service
+++ b/test/fuzz/fuzz-unit-file/directives.service
@@ -161,6 +161,7 @@ PIDFile=
PartOf=
PassCredentials=
PassSecurity=
+PassPacketInfo=
PathChanged=
PathExists=
PathExistsGlob=

View File

@ -0,0 +1,30 @@
From 6ece87bef14ac5741fc870644504737b00607546 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 27 May 2020 19:38:38 +0200
Subject: [PATCH] resolved: tweak cmsg calculation
We ask for the TTL, then have enough space for it.
We probably can drop the extra cmsg space now, but let's figure that out
another time, since the extra cmsg space is used elsewhere in resolved
as well.
(cherry picked from commit 08ab18618ec59022582f1513c0718ba369f5ba85)
Related: #1887181
---
src/resolve/resolved-dns-stream.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c
index 066daef96e..555e200a23 100644
--- a/src/resolve/resolved-dns-stream.c
+++ b/src/resolve/resolved-dns-stream.c
@@ -70,6 +70,7 @@ static int dns_stream_identify(DnsStream *s) {
union {
struct cmsghdr header; /* For alignment */
uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
+ + CMSG_SPACE(int) + /* for the TTL */
+ EXTRA_CMSG_SPACE /* kernel appears to require extra space */];
} control;
struct msghdr mh = {};

View File

@ -0,0 +1,26 @@
From 07b154fbc817e93f58c597644570a633c38d1c72 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Fri, 15 Jan 2021 12:51:02 +0100
Subject: [PATCH] ci: PowerTools repo was renamed to powertools in RHEL 8.3
See: https://wiki.centos.org/Manuals/ReleaseNotes/CentOS8.2011#Yum_repo_file_and_repoid_changes
rhel-only
Related: #1871827
---
ci/travis-centos-rhel8.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh
index cd0857fd29..43e2cb2585 100755
--- a/ci/travis-centos-rhel8.sh
+++ b/ci/travis-centos-rhel8.sh
@@ -95,7 +95,7 @@ for phase in "${PHASES[@]}"; do
# Upgrade the container to get the most recent environment
$DOCKER_EXEC dnf -y upgrade
# Install systemd's build dependencies
- $DOCKER_EXEC dnf -q -y --enablerepo "PowerTools" builddep systemd
+ $DOCKER_EXEC dnf -q -y --enablerepo "powertools" builddep systemd
;;
RUN)
info "Run phase"

View File

@ -0,0 +1,33 @@
From 2dd82aad646bde5a0d49df8562e2578c8b3d04f4 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Fri, 15 Jan 2021 13:00:33 +0100
Subject: [PATCH] ci: use quay.io instead of Docker Hub to avoid rate limits
Docker Hub introduced rate limits for anonymous users (100 requests per
six hours), which break our CI in the busier periods. Let's try to use
the quay.io CentOS image to mitigate this.
rhel-only
Related: #1871827
---
ci/travis-centos-rhel8.sh | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ci/travis-centos-rhel8.sh b/ci/travis-centos-rhel8.sh
index 43e2cb2585..ffe5813b1a 100755
--- a/ci/travis-centos-rhel8.sh
+++ b/ci/travis-centos-rhel8.sh
@@ -81,11 +81,11 @@ for phase in "${PHASES[@]}"; do
info "Setup phase"
info "Using Travis $CENTOS_RELEASE"
# Pull a Docker image and start a new container
- docker pull centos:$CENTOS_RELEASE
+ docker pull quay.io/centos/centos:$CENTOS_RELEASE
info "Starting container $CONT_NAME"
$DOCKER_RUN -v $REPO_ROOT:/build:rw \
-w /build --privileged=true --name $CONT_NAME \
- -dit --net=host centos:$CENTOS_RELEASE /sbin/init
+ -dit --net=host quay.io/centos/centos:$CENTOS_RELEASE /sbin/init
# Beautiful workaround for Fedora's version of Docker
sleep 1
$DOCKER_EXEC dnf makecache

View File

@ -0,0 +1,338 @@
From 88ac207cc619935c64923e6f8fdef324a5b733d8 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Fri, 15 Jan 2021 15:13:53 +0100
Subject: [PATCH] ci: move jobs from Travis CI to GH Actions
The OSS version of Travis CI is going to be merged with the commercial
one soon, essentially dropping the free tier, so let's move the CI jobs
to GitHub Actions to keep them up.
rhel-only
Related: #1871827
---
.../workflows/unit_tests.sh | 28 +++----
.github/workflows/unit_tests.yml | 28 +++++++
.travis.yml | 48 ------------
ci/travis-centos-rhel7.sh | 73 -------------------
ci/travis_wait.bash | 61 ----------------
5 files changed, 37 insertions(+), 201 deletions(-)
rename ci/travis-centos-rhel8.sh => .github/workflows/unit_tests.sh (82%)
create mode 100644 .github/workflows/unit_tests.yml
delete mode 100644 .travis.yml
delete mode 100755 ci/travis-centos-rhel7.sh
delete mode 100644 ci/travis_wait.bash
diff --git a/ci/travis-centos-rhel8.sh b/.github/workflows/unit_tests.sh
similarity index 82%
rename from ci/travis-centos-rhel8.sh
rename to .github/workflows/unit_tests.sh
index ffe5813b1a..ea4f7e7592 100755
--- a/ci/travis-centos-rhel8.sh
+++ b/.github/workflows/unit_tests.sh
@@ -1,18 +1,9 @@
#!/bin/bash
-# Run this script from the root of the systemd's git repository
-# or set REPO_ROOT to a correct path.
-#
-# Example execution on Fedora:
-# dnf install docker
-# systemctl start docker
-# export CONT_NAME="my-fancy-container"
-# ci/travis-centos.sh SETUP RUN CLEANUP
-
PHASES=(${@:-SETUP RUN CLEANUP})
CENTOS_RELEASE="${CENTOS_RELEASE:-latest}"
CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}"
-DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}"
+DOCKER_EXEC="${DOCKER_EXEC:-docker exec $CONT_NAME}"
DOCKER_RUN="${DOCKER_RUN:-docker run}"
REPO_ROOT="${REPO_ROOT:-$PWD}"
ADDITIONAL_DEPS=(libasan libubsan net-tools strace nc e2fsprogs quota dnsmasq diffutils)
@@ -71,9 +62,7 @@ function info() {
echo -e "\033[33;1m$1\033[0m"
}
-set -e
-
-source "$(dirname $0)/travis_wait.bash"
+set -ex
for phase in "${PHASES[@]}"; do
case $phase in
@@ -86,6 +75,7 @@ for phase in "${PHASES[@]}"; do
$DOCKER_RUN -v $REPO_ROOT:/build:rw \
-w /build --privileged=true --name $CONT_NAME \
-dit --net=host quay.io/centos/centos:$CENTOS_RELEASE /sbin/init
+
# Beautiful workaround for Fedora's version of Docker
sleep 1
$DOCKER_EXEC dnf makecache
@@ -97,10 +87,10 @@ for phase in "${PHASES[@]}"; do
# Install systemd's build dependencies
$DOCKER_EXEC dnf -q -y --enablerepo "powertools" builddep systemd
;;
- RUN)
+ RUN|RUN_GCC)
info "Run phase"
# Build systemd
- docker exec -it -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build -Dtests=unsafe -Dslow-tests=true "${CONFIGURE_OPTS[@]}"
+ docker exec -e CFLAGS='-g -O0 -ftrapv' $CONT_NAME meson build -Dtests=unsafe -Dslow-tests=true "${CONFIGURE_OPTS[@]}"
$DOCKER_EXEC ninja -v -C build
# Let's install the new systemd and "reboot" the container to avoid
# unexpected fails due to incompatibilities with older systemd
@@ -108,16 +98,16 @@ for phase in "${PHASES[@]}"; do
docker restart $CONT_NAME
$DOCKER_EXEC ninja -C build test
;;
- RUN_ASAN|RUN_CLANG_ASAN)
+ RUN_ASAN|RUN_GCC_ASAN|RUN_CLANG_ASAN)
if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then
ENV_VARS="-e CC=clang -e CXX=clang++"
MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764
fi
- docker exec $ENV_VARS -it $CONT_NAME meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS "${CONFIGURE_OPTS[@]}"
- docker exec -it $CONT_NAME ninja -v -C build
+ docker exec $ENV_VARS $CONT_NAME meson build --werror -Dtests=unsafe -Db_sanitize=address,undefined $MESON_ARGS "${CONFIGURE_OPTS[@]}"
+ docker exec $CONT_NAME ninja -v -C build
# Never remove halt_on_error from UBSAN_OPTIONS. See https://github.com/systemd/systemd/commit/2614d83aa06592aedb.
- travis_wait docker exec --interactive=false \
+ docker exec --interactive=false \
-e UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1 \
-e ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 \
-e "TRAVIS=$TRAVIS" \
diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml
new file mode 100644
index 0000000000..15f5127a75
--- /dev/null
+++ b/.github/workflows/unit_tests.yml
@@ -0,0 +1,28 @@
+---
+# vi: ts=2 sw=2 et:
+#
+name: Unit tests
+on:
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ env:
+ CENTOS_RELEASE: "centos8"
+ CONT_NAME: "systemd-centos8-ci"
+ strategy:
+ fail-fast: false
+ matrix:
+ run_phase: [GCC, GCC_ASAN]
+ steps:
+ - name: Repository checkout
+ uses: actions/checkout@v1
+ - name: Install build dependencies
+ run: sudo -E .github/workflows/unit_tests.sh SETUP
+ - name: Build & test (${{ matrix.run_phase }})
+ run: sudo -E .github/workflows/unit_tests.sh RUN_${{ matrix.run_phase }}
+ - name: Cleanup
+ run: sudo -E .github/workflows/unit_tests.sh CLEANUP
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 70c60cf24e..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-sudo: required
-dist: xenial
-services:
- - docker
-
-env:
- global:
- - CI_ROOT="$TRAVIS_BUILD_DIR/ci/"
-
-jobs:
- include:
- - name: CentOS 8
- language: bash
- env:
- - CENTOS_RELEASE="centos8"
- - CONT_NAME="systemd-centos-$CENTOS_RELEASE"
- - DOCKER_EXEC="docker exec -ti $CONT_NAME"
- before_install:
- - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
- - docker --version
- install:
- - $CI_ROOT/travis-centos-rhel8.sh SETUP
- script:
- - set -e
- # Build systemd
- - $CI_ROOT/travis-centos-rhel8.sh RUN
- - set +e
- after_script:
- - $CI_ROOT/travis-centos-rhel8.sh CLEANUP
-
- - name: CentOS 8 (ASan+UBSan)
- language: bash
- env:
- - CENTOS_RELEASE="centos8"
- - CONT_NAME="systemd-centos-$CENTOS_RELEASE"
- - DOCKER_EXEC="docker exec -ti $CONT_NAME"
- before_install:
- - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
- - docker --version
- install:
- - $CI_ROOT/travis-centos-rhel8.sh SETUP
- script:
- - set -e
- # Build systemd
- - $CI_ROOT/travis-centos-rhel8.sh RUN_ASAN
- - set +e
- after_script:
- - $CI_ROOT/travis-centos-rhel8.sh CLEANUP
diff --git a/ci/travis-centos-rhel7.sh b/ci/travis-centos-rhel7.sh
deleted file mode 100755
index 73416798ed..0000000000
--- a/ci/travis-centos-rhel7.sh
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/bash
-
-# Run this script from the root of the systemd's git repository
-# or set REPO_ROOT to a correct path.
-#
-# Example execution on Fedora:
-# dnf install docker
-# systemctl start docker
-# export CONT_NAME="my-fancy-container"
-# ci/travis-centos.sh SETUP RUN CLEANUP
-
-PHASES=(${@:-SETUP RUN CLEANUP})
-CENTOS_RELEASE="${CENTOS_RELEASE:-latest}"
-CONT_NAME="${CONT_NAME:-centos-$CENTOS_RELEASE-$RANDOM}"
-DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}"
-DOCKER_RUN="${DOCKER_RUN:-docker run}"
-REPO_ROOT="${REPO_ROOT:-$PWD}"
-ADDITIONAL_DEPS=(yum-utils iputils hostname libasan libubsan clang llvm)
-
-function info() {
- echo -e "\033[33;1m$1\033[0m"
-}
-
-set -e
-
-source "$(dirname $0)/travis_wait.bash"
-
-for phase in "${PHASES[@]}"; do
- case $phase in
- SETUP)
- info "Setup phase"
- info "Using Travis $CENTOS_RELEASE"
- # Pull a Docker image and start a new container
- docker pull centos:$CENTOS_RELEASE
- info "Starting container $CONT_NAME"
- $DOCKER_RUN -v $REPO_ROOT:/build:rw \
- -w /build --privileged=true --name $CONT_NAME \
- -dit --net=host centos:$CENTOS_RELEASE /sbin/init
- # Beautiful workaround for Fedora's version of Docker
- sleep 1
- $DOCKER_EXEC yum makecache
- # Install necessary build/test requirements
- $DOCKER_EXEC yum -y upgrade
- $DOCKER_EXEC yum -y install "${ADDITIONAL_DEPS[@]}"
- $DOCKER_EXEC yum-builddep -y systemd
- ;;
- RUN)
- info "Run phase"
- # Build systemd
- $DOCKER_EXEC ./autogen.sh
- $DOCKER_EXEC ./configure --disable-timesyncd --disable-kdbus --disable-terminal \
- --enable-gtk-doc --enable-compat-libs --disable-sysusers \
- --disable-ldconfig --enable-lz4 --with-sysvinit-path=/etc/rc.d/init.d
- $DOCKER_EXEC make
- # Let's install the new systemd and "reboot" the container to avoid
- # unexpected fails due to incompatibilities with older systemd
- $DOCKER_EXEC make install
- docker restart $CONT_NAME
- if ! $DOCKER_EXEC make check; then
- $DOCKER_EXEC cat test-suite.log
- exit 1
- fi
- ;;
- CLEANUP)
- info "Cleanup phase"
- docker stop $CONT_NAME
- docker rm -f $CONT_NAME
- ;;
- *)
- echo >&2 "Unknown phase '$phase'"
- exit 1
- esac
-done
diff --git a/ci/travis_wait.bash b/ci/travis_wait.bash
deleted file mode 100644
index acf6ad15e4..0000000000
--- a/ci/travis_wait.bash
+++ /dev/null
@@ -1,61 +0,0 @@
-# This was borrowed from https://github.com/travis-ci/travis-build/tree/master/lib/travis/build/bash
-# to get around https://github.com/travis-ci/travis-ci/issues/9979. It should probably be removed
-# as soon as Travis CI has started to provide an easy way to export the functions to bash scripts.
-
-travis_jigger() {
- local cmd_pid="${1}"
- shift
- local timeout="${1}"
- shift
- local count=0
-
- echo -e "\\n"
-
- while [[ "${count}" -lt "${timeout}" ]]; do
- count="$((count + 1))"
- echo -ne "Still running (${count} of ${timeout}): ${*}\\r"
- sleep 60
- done
-
- echo -e "\\n${ANSI_RED}Timeout (${timeout} minutes) reached. Terminating \"${*}\"${ANSI_RESET}\\n"
- kill -9 "${cmd_pid}"
-}
-
-travis_wait() {
- local timeout="${1}"
-
- if [[ "${timeout}" =~ ^[0-9]+$ ]]; then
- shift
- else
- timeout=20
- fi
-
- local cmd=("${@}")
- local log_file="travis_wait_${$}.log"
-
- "${cmd[@]}" &>"${log_file}" &
- local cmd_pid="${!}"
-
- travis_jigger "${!}" "${timeout}" "${cmd[@]}" &
- local jigger_pid="${!}"
- local result
-
- {
- set +e
- wait "${cmd_pid}" 2>/dev/null
- result="${?}"
- ps -p"${jigger_pid}" &>/dev/null && kill "${jigger_pid}"
- set -e
- }
-
- if [[ "${result}" -eq 0 ]]; then
- echo -e "\\n${ANSI_GREEN}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}"
- else
- echo -e "\\n${ANSI_RED}The command ${cmd[*]} exited with ${result}.${ANSI_RESET}"
- fi
-
- echo -e "\\n${ANSI_GREEN}Log:${ANSI_RESET}\\n"
- cat "${log_file}"
-
- return "${result}"
-}

View File

@ -0,0 +1,31 @@
From a11334f0eae67b5159a416193e2e37634281000a Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 8 Nov 2018 09:33:31 +0100
Subject: [PATCH] unit: make UNIT() cast function deal with NULL pointers
Fixes: #10681
(cherry picked from commit bbf11206230d1b089118971f98a047151cb5c4fa)
Related: #1871827
---
src/core/unit.h | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/core/unit.h b/src/core/unit.h
index 6e37fd6f5a..ec45b5fb48 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -597,7 +597,12 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
}
/* For casting the various unit types into a unit */
-#define UNIT(u) (&(u)->meta)
+#define UNIT(u) \
+ ({ \
+ typeof(u) _u_ = (u); \
+ Unit *_w_ = _u_ ? &(_u_)->meta : NULL; \
+ _w_; \
+ })
#define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0)
#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0)

View File

@ -0,0 +1,25 @@
From 6fb6c218fda0d5c3404049243b9392e9b0c7d537 Mon Sep 17 00:00:00 2001
From: David Tardon <dtardon@redhat.com>
Date: Fri, 11 Dec 2020 09:34:19 +0100
Subject: [PATCH] use link to RHEL-8 docs
RHEL-only
Related: #1623116
---
man/systemctl.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/man/systemctl.xml b/man/systemctl.xml
index 56f94d084c..ed60a0739f 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -2005,7 +2005,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<para>
For examples how to use systemctl in comparsion
with old service and chkconfig command please see:
- <ulink url="https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System_Administrators_Guide/sect-Managing_Services_with_systemd-Services.html">
+ <ulink url="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/configuring_basic_system_settings/managing-services-with-systemd_configuring-basic-system-settings">
Managing System Services
</ulink>
</para>

View File

@ -0,0 +1,37 @@
From af9f03ba48dd75be8c6a923f70da9804b3a3a2c3 Mon Sep 17 00:00:00 2001
From: Pavel Hrdina <phrdina@redhat.com>
Date: Wed, 25 Nov 2020 09:05:36 +0100
Subject: [PATCH] cgroup: Also set blkio.bfq.weight
Commit [1] added a workaround when unified cgroups are used but missed
legacy cgroups where there is the same issue.
[1] <https://github.com/systemd/systemd/commit/2dbc45aea747f25cc1c3848fded2ec0062f96bcf>
Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
(cherry picked from commit 35e7a62ca32a30169a94693b831e53c832251984)
Resolves: #1657810
---
src/core/cgroup.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index f1ce070f9a..71e30fd4db 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -1063,6 +1063,14 @@ static void cgroup_context_apply(
log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to set blkio.weight: %m");
+ /* FIXME: drop this when distro kernels properly support BFQ through "blkio.weight"
+ * See also: https://github.com/systemd/systemd/pull/13335 */
+ xsprintf(buf, "%" PRIu64 "\n", weight);
+ r = cg_set_attribute("blkio", path, "blkio.bfq.weight", buf);
+ if (r < 0)
+ log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r,
+ "Failed to set blkio.bfq.weight: %m");
+
if (has_io) {
CGroupIODeviceWeight *w;

View File

@ -0,0 +1,37 @@
From ea425381a675a2ce4d9519d534fe27c1012ac92e Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Mon, 28 Jan 2019 12:07:37 +0100
Subject: [PATCH] units: make sure initrd-cleanup.service terminates before
switching to rootfs
A follow-up for commit a8cb1dc3e0fa81aff.
Commit a8cb1dc3e0fa81aff made sure that initrd-cleanup.service won't be stopped
when initrd-switch-root.target is isolated.
However even with this change, it might happen that initrd-cleanup.service
survives the switch to rootfs (since it has no ordering constraints against
initrd-switch-root.target) and is stopped right after when default.target is
isolated. This led to initrd-cleanup.service entering in failed state as it
happens when oneshot services are stopped.
This patch along with a8cb1dc3e0fa81aff should fix issue #4343.
Fixes: #4343
(cherry picked from commit e2c7c94ea35fe7e669afb51bfc2251158b522ea5)
Related: #1657810
---
units/initrd-switch-root.target | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/units/initrd-switch-root.target b/units/initrd-switch-root.target
index ad82245121..ea4f02618f 100644
--- a/units/initrd-switch-root.target
+++ b/units/initrd-switch-root.target
@@ -15,4 +15,4 @@ Requires=initrd-switch-root.service
Before=initrd-switch-root.service
AllowIsolate=yes
Wants=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target systemd-journald.service initrd-cleanup.service
-After=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target emergency.service emergency.target
+After=initrd-udevadm-cleanup-db.service initrd-root-fs.target initrd-fs.target emergency.service emergency.target initrd-cleanup.service

View File

@ -0,0 +1,73 @@
From c67be1c7d69a0662ab85720aa0209110c39479f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgzones@googlemail.com>
Date: Wed, 27 Nov 2019 19:43:47 +0100
Subject: [PATCH] core: reload SELinux label cache on daemon-reload
Reloading the SELinux label cache here enables a light-wight follow-up of a SELinux policy change, e.g. adding a label for a RuntimeDirectory.
Closes: #13363
(cherry picked from commit a9dfac21ec850eb5dcaf1ae9ef729389e4c12802)
Resolves: #1888912
---
src/basic/selinux-util.c | 20 ++++++++++++++++++++
src/basic/selinux-util.h | 1 +
src/core/main.c | 2 ++
3 files changed, 23 insertions(+)
diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
index e15bd7e1fa..f69d88eb1e 100644
--- a/src/basic/selinux-util.c
+++ b/src/basic/selinux-util.c
@@ -105,6 +105,26 @@ void mac_selinux_finish(void) {
#endif
}
+void mac_selinux_reload(void) {
+
+#if HAVE_SELINUX
+ struct selabel_handle *backup_label_hnd;
+
+ if (!label_hnd)
+ return;
+
+ backup_label_hnd = TAKE_PTR(label_hnd);
+
+ /* try to initialize new handle
+ * on success close backup
+ * on failure restore backup */
+ if (mac_selinux_init() == 0)
+ selabel_close(backup_label_hnd);
+ else
+ label_hnd = backup_label_hnd;
+#endif
+}
+
int mac_selinux_fix(const char *path, LabelFixFlags flags) {
#if HAVE_SELINUX
diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h
index 08314057fb..abcfabe777 100644
--- a/src/basic/selinux-util.h
+++ b/src/basic/selinux-util.h
@@ -13,6 +13,7 @@ void mac_selinux_retest(void);
int mac_selinux_init(void);
void mac_selinux_finish(void);
+void mac_selinux_reload(void);
int mac_selinux_fix(const char *path, LabelFixFlags flags);
int mac_selinux_apply(const char *path, const char *label);
diff --git a/src/core/main.c b/src/core/main.c
index d897155644..d5c41da0c4 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1682,6 +1682,8 @@ static int invoke_main_loop(
saved_log_level = m->log_level_overridden ? log_get_max_level() : -1;
saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID;
+ mac_selinux_reload();
+
(void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
set_manager_defaults(m);

View File

@ -0,0 +1,140 @@
From 4f4e8bbd9ad46fc146a36f52790bc4920f42ef1f Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Mon, 2 Jul 2018 10:22:56 +0200
Subject: [PATCH] selinux: introduce mac_selinux_create_file_prepare_at()
(cherry picked from commit 7e531a5265687aef5177b070c36ca4ceab42e768)
Related: #1888912
---
src/basic/selinux-util.c | 83 ++++++++++++++++++++++++++++++----------
src/basic/selinux-util.h | 1 +
2 files changed, 63 insertions(+), 21 deletions(-)
diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
index f69d88eb1e..a078ce23ef 100644
--- a/src/basic/selinux-util.c
+++ b/src/basic/selinux-util.c
@@ -336,48 +336,89 @@ char* mac_selinux_free(char *label) {
return NULL;
}
-int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
-
#if HAVE_SELINUX
+static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode) {
_cleanup_freecon_ char *filecon = NULL;
+ _cleanup_free_ char *path = NULL;
int r;
- assert(path);
-
- if (!label_hnd)
- return 0;
-
- if (path_is_absolute(path))
- r = selabel_lookup_raw(label_hnd, &filecon, path, mode);
- else {
- _cleanup_free_ char *newpath = NULL;
-
- r = path_make_absolute_cwd(path, &newpath);
- if (r < 0)
- return r;
-
- r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode);
- }
+ assert(abspath);
+ assert(path_is_absolute(abspath));
+ r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
if (r < 0) {
/* No context specified by the policy? Proceed without setting it. */
if (errno == ENOENT)
return 0;
- log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
+ log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
} else {
if (setfscreatecon_raw(filecon) >= 0)
return 0; /* Success! */
- log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, path);
+ log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
}
if (security_getenforce() > 0)
return -errno;
-#endif
return 0;
}
+#endif
+
+int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
+ int r = 0;
+
+#if HAVE_SELINUX
+ _cleanup_free_ char *abspath = NULL;
+ _cleanup_close_ int fd = -1;
+
+ assert(path);
+
+ if (!label_hnd)
+ return 0;
+
+ if (!path_is_absolute(path)) {
+ _cleanup_free_ char *p = NULL;
+
+ if (dirfd == AT_FDCWD)
+ r = safe_getcwd(&p);
+ else
+ r = fd_get_path(dirfd, &p);
+ if (r < 0)
+ return r;
+
+ abspath = path_join(NULL, p, path);
+ if (!abspath)
+ return -ENOMEM;
+
+ path = abspath;
+ }
+
+ r = selinux_create_file_prepare_abspath(path, mode);
+#endif
+ return r;
+}
+
+int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
+ int r = 0;
+
+#if HAVE_SELINUX
+ _cleanup_free_ char *abspath = NULL;
+
+ assert(path);
+
+ if (!label_hnd)
+ return 0;
+
+ r = path_make_absolute_cwd(path, &abspath);
+ if (r < 0)
+ return r;
+
+ r = selinux_create_file_prepare_abspath(abspath, mode);
+#endif
+ return r;
+}
void mac_selinux_create_file_clear(void) {
diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h
index abcfabe777..639c35b687 100644
--- a/src/basic/selinux-util.h
+++ b/src/basic/selinux-util.h
@@ -24,6 +24,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
char* mac_selinux_free(char *label);
int mac_selinux_create_file_prepare(const char *path, mode_t mode);
+int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode);
void mac_selinux_create_file_clear(void);
int mac_selinux_create_socket_prepare(const char *label);

View File

@ -0,0 +1,135 @@
From 4e48673172b012a06575e4f5b681d3554eded2e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgzones@googlemail.com>
Date: Mon, 6 Jan 2020 15:27:23 +0100
Subject: [PATCH] selinux: add trigger for policy reload to refresh internal
selabel cache
Reload the internal selabel cache automatically on SELinux policy reloads so non pid-1 daemons are participating.
Run the reload function `mac_selinux_reload()` not manually on daemon-reload, but rather pass it as callback to libselinux.
Trigger the callback prior usage of the systemd internal selabel cache by depleting the selinux netlink socket via `avc_netlink_check_nb()`.
Improves: a9dfac21ec85 ("core: reload SELinux label cache on daemon-reload")
Improves: #13363
(cherry picked from commit 61f3e897f13101f29fb8027e8839498a469ad58e)
Related: #1888912
---
src/basic/selinux-util.c | 23 +++++++++++++++++++----
src/basic/selinux-util.h | 1 -
src/core/main.c | 2 --
3 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c
index a078ce23ef..bfe3d015aa 100644
--- a/src/basic/selinux-util.c
+++ b/src/basic/selinux-util.c
@@ -10,6 +10,7 @@
#include <syslog.h>
#if HAVE_SELINUX
+#include <selinux/avc.h>
#include <selinux/context.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
@@ -32,6 +33,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
#define _cleanup_freecon_ _cleanup_(freeconp)
#define _cleanup_context_free_ _cleanup_(context_freep)
+static int mac_selinux_reload(int seqno);
+
static int cached_use = -1;
static struct selabel_handle *label_hnd = NULL;
@@ -63,6 +66,8 @@ int mac_selinux_init(void) {
usec_t before_timestamp, after_timestamp;
struct mallinfo before_mallinfo, after_mallinfo;
+ selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload);
+
if (label_hnd)
return 0;
@@ -105,13 +110,12 @@ void mac_selinux_finish(void) {
#endif
}
-void mac_selinux_reload(void) {
-
#if HAVE_SELINUX
+static int mac_selinux_reload(int seqno) {
struct selabel_handle *backup_label_hnd;
if (!label_hnd)
- return;
+ return 0;
backup_label_hnd = TAKE_PTR(label_hnd);
@@ -122,8 +126,10 @@ void mac_selinux_reload(void) {
selabel_close(backup_label_hnd);
else
label_hnd = backup_label_hnd;
-#endif
+
+ return 0;
}
+#endif
int mac_selinux_fix(const char *path, LabelFixFlags flags) {
@@ -152,6 +158,9 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) {
if (fstat(fd, &st) < 0)
return -errno;
+ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+ (void) avc_netlink_check_nb();
+
if (selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode) < 0) {
r = -errno;
@@ -345,6 +354,9 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode)
assert(abspath);
assert(path_is_absolute(abspath));
+ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+ (void) avc_netlink_check_nb();
+
r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
if (r < 0) {
/* No context specified by the policy? Proceed without setting it. */
@@ -496,6 +508,9 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
+ /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+ (void) avc_netlink_check_nb();
+
if (path_is_absolute(path))
r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
else {
diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h
index 639c35b687..bd5207c318 100644
--- a/src/basic/selinux-util.h
+++ b/src/basic/selinux-util.h
@@ -13,7 +13,6 @@ void mac_selinux_retest(void);
int mac_selinux_init(void);
void mac_selinux_finish(void);
-void mac_selinux_reload(void);
int mac_selinux_fix(const char *path, LabelFixFlags flags);
int mac_selinux_apply(const char *path, const char *label);
diff --git a/src/core/main.c b/src/core/main.c
index d5c41da0c4..d897155644 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -1682,8 +1682,6 @@ static int invoke_main_loop(
saved_log_level = m->log_level_overridden ? log_get_max_level() : -1;
saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID;
- mac_selinux_reload();
-
(void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
set_manager_defaults(m);

View File

@ -0,0 +1,24 @@
From fb58a56c6c1c2749ba634abd9ad76f4e718269a1 Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Tue, 5 Jan 2021 12:30:15 +0100
Subject: [PATCH] udev/net_id: give RHEL-8.4 naming scheme a name
Follow-up for bb6114af097da0cd9c5081e42db718559130687f
Related: #1827462
---
src/udev/udev-builtin-net_id.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index d8c56b62bb..7c153f0aef 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -150,6 +150,7 @@ static const NamingScheme naming_schemes[] = {
{ "rhel-8.1", NAMING_RHEL_8_1 },
{ "rhel-8.2", NAMING_RHEL_8_2 },
{ "rhel-8.3", NAMING_RHEL_8_3 },
+ { "rhel-8.4", NAMING_RHEL_8_4 },
/* … add more schemes here, as the logic to name devices is updated … */
};

View File

@ -0,0 +1,63 @@
From 29c5b8dd6228c4401f034ca0aa85f99ac42cf8dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Sekleta=CC=81r?= <msekleta@redhat.com>
Date: Thu, 5 Nov 2020 17:55:25 +0100
Subject: [PATCH] basic/stat-util: make mtime check stricter and use entire
timestamp
Note that st_mtime member of struct stat is defined as follows,
#define st_mtime st_mtim.tv_sec
Hence we omitted checking nanosecond part of the timestamp (struct
timespec) and possibly would miss modifications that happened within the
same second.
(cherry picked from commit a59b0a9f768f6e27b25f4f1bab6de08842e78d74)
Related: #1642728
---
src/basic/stat-util.c | 22 ++++++++++++++++++++++
src/basic/stat-util.h | 2 ++
2 files changed, 24 insertions(+)
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index 26aee9bad6..c61c4c0517 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -287,3 +287,25 @@ int fd_verify_regular(int fd) {
return stat_verify_regular(&st);
}
+
+bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
+
+ /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
+ * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file
+ * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file
+ * size, backing device, inode type and if this refers to a device not the major/minor.
+ *
+ * Note that we don't care if file attributes such as ownership or access mode change, this here is
+ * about contents of the file. The purpose here is to detect file contents changes, and nothing
+ * else. */
+
+ return a && b &&
+ (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
+ ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */
+ a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
+ a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
+ (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
+ a->st_dev == b->st_dev &&
+ a->st_ino == b->st_ino &&
+ (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
+}
\ No newline at end of file
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
index f8014ed30b..9e1a2b70da 100644
--- a/src/basic/stat-util.h
+++ b/src/basic/stat-util.h
@@ -58,3 +58,5 @@ int path_is_temporary_fs(const char *path);
int stat_verify_regular(const struct stat *st);
int fd_verify_regular(int fd);
+
+bool stat_inode_unmodified(const struct stat *a, const struct stat *b);

View File

@ -0,0 +1,457 @@
From 1d5f966c1758eb620755fcae54abd07a1ac36d3d Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Wed, 6 Jan 2021 11:43:50 +0100
Subject: [PATCH] udev: make algorithm that selects highest priority devlink
less susceptible to race conditions
Previously it was very likely, when multiple contenders for the symlink
appear in parallel, that algorithm would select wrong symlink (i.e. one
with lower-priority).
Now the algorithm is much more defensive and when we detect change in
set of contenders for the symlink we reevaluate the selection. Same
happens when new symlink replaces already existing symlink that points
to different device node.
Resolves: #1642728
---
src/udev/udev-event.c | 71 +++++++-----
src/udev/udev-node.c | 244 ++++++++++++++++++++++++++++++------------
2 files changed, 216 insertions(+), 99 deletions(-)
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index fd8406d959..9004634f65 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -833,6 +833,41 @@ static int rename_netif(struct udev_event *event) {
return 0;
}
+static void update_devnode(struct udev_event *event) {
+ struct udev_device *dev = event->dev;
+
+ if (major(udev_device_get_devnum(dev)) > 0) {
+ bool apply;
+
+ /* remove/update possible left-over symlinks from old database entry */
+ if (event->dev_db != NULL)
+ udev_node_update_old_links(dev, event->dev_db);
+
+ if (!event->owner_set)
+ event->uid = udev_device_get_devnode_uid(dev);
+
+ if (!event->group_set)
+ event->gid = udev_device_get_devnode_gid(dev);
+
+ if (!event->mode_set) {
+ if (udev_device_get_devnode_mode(dev) > 0) {
+ /* kernel supplied value */
+ event->mode = udev_device_get_devnode_mode(dev);
+ } else if (event->gid > 0) {
+ /* default 0660 if a group is assigned */
+ event->mode = 0660;
+ }
+ else {
+ /* default 0600 */
+ event->mode = 0600;
+ }
+ }
+
+ apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set;
+ udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list);
+ }
+}
+
void udev_event_execute_rules(struct udev_event *event,
usec_t timeout_usec, usec_t timeout_warn_usec,
struct udev_list *properties_list,
@@ -891,35 +926,7 @@ void udev_event_execute_rules(struct udev_event *event,
}
}
- if (major(udev_device_get_devnum(dev)) > 0) {
- bool apply;
-
- /* remove/update possible left-over symlinks from old database entry */
- if (event->dev_db != NULL)
- udev_node_update_old_links(dev, event->dev_db);
-
- if (!event->owner_set)
- event->uid = udev_device_get_devnode_uid(dev);
-
- if (!event->group_set)
- event->gid = udev_device_get_devnode_gid(dev);
-
- if (!event->mode_set) {
- if (udev_device_get_devnode_mode(dev) > 0) {
- /* kernel supplied value */
- event->mode = udev_device_get_devnode_mode(dev);
- } else if (event->gid > 0) {
- /* default 0660 if a group is assigned */
- event->mode = 0660;
- } else {
- /* default 0600 */
- event->mode = 0600;
- }
- }
-
- apply = streq(udev_device_get_action(dev), "add") || event->owner_set || event->group_set || event->mode_set;
- udev_node_add(dev, apply, event->mode, event->uid, event->gid, &event->seclabel_list);
- }
+ update_devnode(event);
/* preserve old, or get new initialization timestamp */
udev_device_ensure_usec_initialized(event->dev, event->dev_db);
@@ -927,6 +934,12 @@ void udev_event_execute_rules(struct udev_event *event,
/* (re)write database file */
udev_device_tag_index(dev, event->dev_db, true);
udev_device_update_db(dev);
+
+ /* Yes, we run update_devnode() twice, because in the first invocation, that is before update of udev database,
+ * it could happen that two contenders are replacing each other's symlink. Hence we run it again to make sure
+ * symlinks point to devices that claim them with the highest priority. */
+ update_devnode(event);
+
udev_device_set_is_initialized(dev);
event->dev_db = udev_device_unref(event->dev_db);
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index 333dcae6b9..2eeeccdd3a 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -13,19 +13,27 @@
#include <unistd.h>
#include "device-nodes.h"
+#include "device-private.h"
#include "dirent-util.h"
+#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
+#include "sd-device.h"
#include "selinux-util.h"
#include "smack-util.h"
+#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "udev.h"
+#include "libudev-device-internal.h"
-static int node_symlink(struct udev_device *dev, const char *node, const char *slink) {
+#define LINK_UPDATE_MAX_RETRIES 128
+
+static int node_symlink(sd_device *dev, const char *node, const char *slink) {
struct stat stats;
char target[UTIL_PATH_SIZE];
char *s;
+ const char *id_filename;
size_t l;
char slink_tmp[UTIL_PATH_SIZE + 32];
int i = 0;
@@ -89,7 +97,10 @@ static int node_symlink(struct udev_device *dev, const char *node, const char *s
}
log_debug("atomically replace '%s'", slink);
- strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", udev_device_get_id_filename(dev), NULL);
+ err = device_get_id_filename(dev, &id_filename);
+ if (err < 0)
+ return log_error_errno(err, "Failed to get id_filename: %m");
+ strscpyl(slink_tmp, sizeof(slink_tmp), slink, ".tmp-", id_filename, NULL);
unlink(slink_tmp);
do {
err = mkdir_parents_label(slink_tmp, 0755);
@@ -109,104 +120,187 @@ static int node_symlink(struct udev_device *dev, const char *node, const char *s
if (err != 0) {
log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink);
unlink(slink_tmp);
- }
+ } else
+ /* Tell caller that we replaced already existing symlink. */
+ return 1;
exit:
return err;
}
/* find device node of device with highest priority */
-static const char *link_find_prioritized(struct udev_device *dev, bool add, const char *stackdir, char *buf, size_t bufsize) {
- struct udev *udev = udev_device_get_udev(dev);
- DIR *dir;
+static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) {
+ _cleanup_closedir_ DIR *dir = NULL;
+ _cleanup_free_ char *target = NULL;
struct dirent *dent;
- int priority = 0;
- const char *target = NULL;
+ int r, priority = 0;
+
+ assert(!add || dev);
+ assert(stackdir);
+ assert(ret);
if (add) {
- priority = udev_device_get_devlink_priority(dev);
- strscpy(buf, bufsize, udev_device_get_devnode(dev));
- target = buf;
+ const char *devnode;
+
+ r = device_get_devlink_priority(dev, &priority);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_devname(dev, &devnode);
+ if (r < 0)
+ return r;
+
+ target = strdup(devnode);
+ if (!target)
+ return -ENOMEM;
}
dir = opendir(stackdir);
- if (dir == NULL)
- return target;
+ if (!dir) {
+ if (target) {
+ *ret = TAKE_PTR(target);
+ return 0;
+ }
+
+ return -errno;
+ }
+
FOREACH_DIRENT_ALL(dent, dir, break) {
- struct udev_device *dev_db;
+ _cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
+ const char *devnode, *id_filename;
+ int db_prio = 0;
if (dent->d_name[0] == '\0')
break;
if (dent->d_name[0] == '.')
continue;
- log_debug("found '%s' claiming '%s'", dent->d_name, stackdir);
+ log_debug("Found '%s' claiming '%s'", dent->d_name, stackdir);
+
+ if (device_get_id_filename(dev, &id_filename) < 0)
+ continue;
/* did we find ourself? */
- if (streq(dent->d_name, udev_device_get_id_filename(dev)))
+ if (streq(dent->d_name, id_filename))
continue;
- dev_db = udev_device_new_from_device_id(udev, dent->d_name);
- if (dev_db != NULL) {
- const char *devnode;
-
- devnode = udev_device_get_devnode(dev_db);
- if (devnode != NULL) {
- if (target == NULL || udev_device_get_devlink_priority(dev_db) > priority) {
- log_debug("'%s' claims priority %i for '%s'",
- udev_device_get_syspath(dev_db), udev_device_get_devlink_priority(dev_db), stackdir);
- priority = udev_device_get_devlink_priority(dev_db);
- strscpy(buf, bufsize, devnode);
- target = buf;
- }
- }
- udev_device_unref(dev_db);
+ if (sd_device_new_from_device_id(&dev_db, dent->d_name) < 0)
+ continue;
+
+ if (sd_device_get_devname(dev_db, &devnode) < 0)
+ continue;
+
+ if (device_get_devlink_priority(dev_db, &db_prio) < 0)
+ continue;
+
+ if (target && db_prio <= priority)
+ continue;
+
+ if (DEBUG_LOGGING) {
+ const char *syspath = NULL;
+
+ (void) sd_device_get_syspath(dev_db, &syspath);
+ log_debug("Device '%s' claims priority %i for '%s'", strnull(syspath), db_prio, stackdir);
}
+
+ r = free_and_strdup(&target, devnode);
+ if (r < 0)
+ return r;
+ priority = db_prio;
}
- closedir(dir);
- return target;
+
+ if (!target)
+ return -ENOENT;
+
+ *ret = TAKE_PTR(target);
+ return 0;
}
+
/* manage "stack of names" with possibly specified device priorities */
-static void link_update(struct udev_device *dev, const char *slink, bool add) {
- char name_enc[UTIL_PATH_SIZE];
- char filename[UTIL_PATH_SIZE * 2];
- char dirname[UTIL_PATH_SIZE];
- const char *target;
- char buf[UTIL_PATH_SIZE];
+static int link_update(sd_device *dev, const char *slink, bool add) {
+ _cleanup_free_ char *filename = NULL, *dirname = NULL;
+ char name_enc[PATH_MAX];
+ const char *id_filename;
+ int i, r, retries;
+
+ assert(dev);
+ assert(slink);
+
+ r = device_get_id_filename(dev, &id_filename);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get id_filename: %m");
util_path_encode(slink + STRLEN("/dev"), name_enc, sizeof(name_enc));
- strscpyl(dirname, sizeof(dirname), "/run/udev/links/", name_enc, NULL);
- strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL);
+ dirname = path_join(NULL, "/run/udev/links/", name_enc);
+ if (!dirname)
+ return log_oom();
+ filename = path_join(NULL, dirname, id_filename);
+ if (!filename)
+ return log_oom();
+
+ if (!add) {
+ if (unlink(filename) == 0)
+ (void) rmdir(dirname);
+ } else
+ for (;;) {
+ _cleanup_close_ int fd = -1;
+
+ r = mkdir_parents(filename, 0755);
+ if (!IN_SET(r, 0, -ENOENT))
+ return r;
- if (!add && unlink(filename) == 0)
- rmdir(dirname);
+ fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
+ if (fd >= 0)
+ break;
+ if (errno != ENOENT)
+ return -errno;
+ }
- target = link_find_prioritized(dev, add, dirname, buf, sizeof(buf));
- if (target == NULL) {
- log_debug("no reference left, remove '%s'", slink);
- if (unlink(slink) == 0)
- rmdir_parents(slink, "/");
- } else {
- log_debug("creating link '%s' to '%s'", slink, target);
- node_symlink(dev, target, slink);
- }
+ /* If the database entry is not written yet we will just do one iteration and possibly wrong symlink
+ * will be fixed in the second invocation. */
+ (void) sd_device_get_is_initialized(dev, &r);
+ retries = r > 0 ? LINK_UPDATE_MAX_RETRIES : 1;
- if (add) {
- int err;
+ for (i = 0; i < retries; i++) {
+ _cleanup_free_ char *target = NULL;
+ struct stat st1 = {}, st2 = {};
- do {
- int fd;
+ r = stat(dirname, &st1);
+ if (r < 0 && errno != ENOENT)
+ return -errno;
- err = mkdir_parents(filename, 0755);
- if (!IN_SET(err, 0, -ENOENT))
+ r = link_find_prioritized(dev, add, dirname, &target);
+ if (r == -ENOENT) {
+ log_debug("No reference left, removing '%s'", slink);
+ if (unlink(slink) == 0)
+ (void) rmdir_parents(slink, "/");
+
+ break;
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to determine highest priority symlink: %m");
+
+ r = node_symlink(dev, target, slink);
+ if (r < 0) {
+ (void) unlink(filename);
+ break;
+ } else if (r == 1)
+ /* We have replaced already existing symlink, possibly there is some other device trying
+ * to claim the same symlink. Let's do one more iteration to give us a chance to fix
+ * the error if other device actually claims the symlink with higher priority. */
+ continue;
+
+ /* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */
+ if ((st1.st_mode & S_IFMT) != 0) {
+ r = stat(dirname, &st2);
+ if (r < 0 && errno != ENOENT)
+ return -errno;
+
+ if (stat_inode_unmodified(&st1, &st2))
break;
- fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
- if (fd >= 0)
- close(fd);
- else
- err = -errno;
- } while (err == -ENOENT);
+ }
}
+
+ return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP;
}
void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev_old) {
@@ -233,7 +327,7 @@ void udev_node_update_old_links(struct udev_device *dev, struct udev_device *dev
log_debug("update old name, '%s' no longer belonging to '%s'",
name, udev_device_get_devpath(dev));
- link_update(dev, name, false);
+ link_update(dev->device, name, false);
}
}
@@ -338,11 +432,16 @@ void udev_node_add(struct udev_device *dev, bool apply,
xsprintf_dev_num_path(filename,
streq(udev_device_get_subsystem(dev), "block") ? "block" : "char",
udev_device_get_devnum(dev));
- node_symlink(dev, udev_device_get_devnode(dev), filename);
+ node_symlink(dev->device, udev_device_get_devnode(dev), filename);
/* create/update symlinks, add symlinks to name index */
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev))
- link_update(dev, udev_list_entry_get_name(list_entry), true);
+ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) {
+ int r;
+
+ r = link_update(dev->device, udev_list_entry_get_name(list_entry), true);
+ if (r < 0)
+ log_info_errno(r, "Failed to update device symlinks: %m");
+ }
}
void udev_node_remove(struct udev_device *dev) {
@@ -350,8 +449,13 @@ void udev_node_remove(struct udev_device *dev) {
char filename[DEV_NUM_PATH_MAX];
/* remove/update symlinks, remove symlinks from name index */
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev))
- link_update(dev, udev_list_entry_get_name(list_entry), false);
+ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(dev)) {
+ int r;
+
+ r = link_update(dev->device, udev_list_entry_get_name(list_entry), false);
+ if (r < 0)
+ log_info_errno(r, "Failed to update device symlinks: %m");
+ }
/* remove /dev/{block,char}/$major:$minor */
xsprintf_dev_num_path(filename,

View File

@ -0,0 +1,32 @@
From 6a908a38135d050b7c271fdea9c061d7e7ad8ef7 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 23 Oct 2018 07:23:01 +0900
Subject: [PATCH] test: create /dev/null in test-udev.pl
(cherry picked from commit a41ff38b0999fb83464309a29b8f39450b8d4b85)
Related: #1642728
---
test/udev-test.pl | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 0433629c7c..a1c24f49b4 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1537,13 +1537,14 @@ sub udev_setup {
system("umount", $udev_tmpfs);
rmdir($udev_tmpfs);
mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n";
- system("mount", "-o", "rw,mode=755,nosuid,noexec,nodev", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs";
+ system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs";
mkdir($udev_dev) || die "unable to create udev_dev: $udev_dev\n";
# setting group and mode of udev_dev ensures the tests work
# even if the parent directory has setgid bit enabled.
chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n";
chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n";
+ system("mknod", $udev_dev . "/null", "c", "1", "3") && "unable to create $udev_dev/null";
system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys";

View File

@ -0,0 +1,27 @@
From 70bf708d5360372aa541e25ff512609230781dd6 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 7 Nov 2018 14:56:20 +0900
Subject: [PATCH] test: missing "die"
Follow-up for a41ff38b0999fb83464309a29b8f39450b8d4b85.
(cherry picked from commit 11d93952ea806de2b6e9fb381153115cccc7b5e8)
Related: #1642728
---
test/udev-test.pl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index a1c24f49b4..61bd3d703a 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1544,7 +1544,7 @@ sub udev_setup {
# even if the parent directory has setgid bit enabled.
chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n";
chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n";
- system("mknod", $udev_dev . "/null", "c", "1", "3") && "unable to create $udev_dev/null";
+ system("mknod", $udev_dev . "/null", "c", "1", "3") && die "unable to create $udev_dev/null";
system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys";

View File

@ -0,0 +1,33 @@
From 1b133f2ca15f0a15b05407b2c04521d7de88dfa2 Mon Sep 17 00:00:00 2001
From: Evgeny Vereshchagin <evvers@ya.ru>
Date: Fri, 9 Nov 2018 03:14:04 +0100
Subject: [PATCH] udev-test: remove a check for whether the test is run in a
container
It's too broad a check that prevents the test from running on Travis CI.
(cherry picked from commit 881886ef08d50951159633248b0f73977c5d6924)
Related: #1642728
---
test/udev-test.pl | 7 -------
1 file changed, 7 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 61bd3d703a..05b3e17188 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1646,13 +1646,6 @@ if ($? >> 8 == 0) {
exit($EXIT_TEST_SKIP);
}
-# skip the test when running in a container
-system("systemd-detect-virt", "-c", "-q");
-if ($? >> 8 == 0) {
- print "Running in a container, skipping the test.\n";
- exit($EXIT_TEST_SKIP);
-}
-
udev_setup();
my $test_num = 1;

View File

@ -0,0 +1,94 @@
From 8c82f3a4aa2d029dcc303cbf95a71194aa5ac9c3 Mon Sep 17 00:00:00 2001
From: Evgeny Vereshchagin <evvers@ya.ru>
Date: Fri, 9 Nov 2018 04:01:15 +0100
Subject: [PATCH] udev-test: skip the test only if it can't setup its
environment
This is basically a replacement for 0eb3cc88504b5d8f74.
(cherry picked from commit 110a13202eab6d92678abcde08372d4afac1cc45)
Related: #1642728
---
src/test/test-udev.c | 8 ++++++++
test/udev-test.pl | 24 +++++++++++++++++++++---
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index bed51c1270..f098fab721 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -65,6 +65,11 @@ int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
+ if (!IN_SET(argc, 2, 3)) {
+ log_error("This program needs one or two arguments, %d given", argc - 1);
+ return EXIT_FAILURE;
+ }
+
err = fake_filesystems();
if (err < 0)
return EXIT_FAILURE;
@@ -73,6 +78,9 @@ int main(int argc, char *argv[]) {
if (udev == NULL)
return EXIT_FAILURE;
+ if (argc == 2)
+ return EXIT_SUCCESS;
+
log_debug("version %s", PACKAGE_VERSION);
mac_selinux_init();
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 05b3e17188..aa38bae0b1 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1537,18 +1537,28 @@ sub udev_setup {
system("umount", $udev_tmpfs);
rmdir($udev_tmpfs);
mkdir($udev_tmpfs) || die "unable to create udev_tmpfs: $udev_tmpfs\n";
- system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs) && die "unable to mount tmpfs";
+
+ if (system("mount", "-o", "rw,mode=755,nosuid,noexec", "-t", "tmpfs", "tmpfs", $udev_tmpfs)) {
+ warn "unable to mount tmpfs";
+ return 0;
+ }
mkdir($udev_dev) || die "unable to create udev_dev: $udev_dev\n";
# setting group and mode of udev_dev ensures the tests work
# even if the parent directory has setgid bit enabled.
chown (0, 0, $udev_dev) || die "unable to chown $udev_dev\n";
chmod (0755, $udev_dev) || die "unable to chmod $udev_dev\n";
- system("mknod", $udev_dev . "/null", "c", "1", "3") && die "unable to create $udev_dev/null";
+
+ if (system("mknod", $udev_dev . "/null", "c", "1", "3")) {
+ warn "unable to create $udev_dev/null";
+ return 0;
+ }
system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys";
system("rm", "-rf", "$udev_run");
+
+ return 1;
}
sub run_test {
@@ -1646,7 +1656,15 @@ if ($? >> 8 == 0) {
exit($EXIT_TEST_SKIP);
}
-udev_setup();
+if (!udev_setup()) {
+ warn "Failed to set up the environment, skipping the test";
+ exit($EXIT_TEST_SKIP);
+}
+
+if (!system($udev_bin, "check")) {
+ warn "$udev_bin failed to set up the environment, skipping the test";
+ exit($EXIT_TEST_SKIP);
+}
my $test_num = 1;
my @list;

View File

@ -0,0 +1,33 @@
From f44fcdde656036f0388fc8244b8960c1873a3a08 Mon Sep 17 00:00:00 2001
From: Alexey Bogdanenko <alexey@bogdanenko.com>
Date: Sat, 8 Dec 2018 11:02:30 +0300
Subject: [PATCH] udev-test: fix test skip condition
When there is a failure to setup the environment, the following happens:
1. Command "./test-udev check" exits with non-zero code.
2. Perl function "system" returns the code.
3. The code is evaluated as true by Perl.
Then we stop the test.
(cherry picked from commit 7935dae547caf164d807237f1009a9e9fa510337)
Related: #1642728
---
test/udev-test.pl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index aa38bae0b1..3517feab15 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1661,7 +1661,7 @@ if (!udev_setup()) {
exit($EXIT_TEST_SKIP);
}
-if (!system($udev_bin, "check")) {
+if (system($udev_bin, "check")) {
warn "$udev_bin failed to set up the environment, skipping the test";
exit($EXIT_TEST_SKIP);
}

View File

@ -0,0 +1,35 @@
From 974431a70775d5127cd973c4b4705d2cf8884011 Mon Sep 17 00:00:00 2001
From: Alexey Bogdanenko <alexey@bogdanenko.com>
Date: Sat, 8 Dec 2018 15:35:30 +0300
Subject: [PATCH] udev-test: fix missing directory test/run
Fixes the following error:
Failed to mount test /run: No such file or directory
By the time command "./test-udev check" calls function "fake_filesystems",
directory "test/run" must be present.
(cherry picked from commit 1e5548c0e0962424b6ca5fdfd35c866b70760c8f)
Related: #1642728
---
test/udev-test.pl | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 3517feab15..eb76ebd72e 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1558,6 +1558,11 @@ sub udev_setup {
system("rm", "-rf", "$udev_run");
+ if (!mkdir($udev_run)) {
+ warn "unable to create directory $udev_run";
+ return 0;
+ }
+
return 1;
}

View File

@ -0,0 +1,31 @@
From 57e9ee0f19098d56995955f6692437affdf94041 Mon Sep 17 00:00:00 2001
From: Alexey Bogdanenko <alexey@bogdanenko.com>
Date: Tue, 11 Dec 2018 16:55:34 +0300
Subject: [PATCH] udev-test: check if permitted to create block device nodes
(cherry picked from commit dbfbc6c4e34366033cb340e8b0c3cbca683ff6f5)
Related: #1642728
---
test/udev-test.pl | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index eb76ebd72e..957cda541c 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1554,6 +1554,14 @@ sub udev_setup {
return 0;
}
+ # check if we are permitted to create block device nodes
+ my $block_device_filename = $udev_dev . "/sda";
+ if (system("mknod", $block_device_filename, "b", "8", "0")) {
+ warn "unable to create $block_device_filename";
+ return 0;
+ }
+ unlink $block_device_filename;
+
system("cp", "-r", "test/sys/", $udev_sys) && die "unable to copy test/sys";
system("rm", "-rf", "$udev_run");

View File

@ -0,0 +1,45 @@
From 527d43064a93fae9a4490e5d152b120e91f5eade Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Mon, 18 Feb 2019 10:38:29 +0900
Subject: [PATCH] test-udev: add a testcase of too long line
(cherry picked from commit 1e797cf596df50a6bdd8cbf8e9b2467a3a934171)
Related: #1642728
---
test/udev-test.pl | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 957cda541c..3a50694fa9 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -39,6 +39,11 @@ for (my $i = 1; $i <= 10000; ++$i) {
$rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n";
}
+my $rules_10k_tags_continuation = "";
+for (my $i = 1; $i <= 10000; ++$i) {
+ $rules_10k_tags_continuation .= 'KERNEL=="sda", TAG+="test' . $i . "\"\\\n";
+}
+
my @tests = (
{
desc => "no rules",
@@ -1444,6 +1449,16 @@ EOF
exp_name => "found",
rules => $rules_10k_tags . <<EOF
TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="found"
+EOF
+ },
+ {
+ desc => "don't crash with lots of tags with continuation",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad" ,
+ rules => $rules_10k_tags_continuation . <<EOF
+TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="bad"
+KERNEL=="sda", SYMLINK+="found"
EOF
},
);

View File

@ -0,0 +1,34 @@
From 4fb6b699b3d69341093830e92838336c0dbd7ea9 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 19 Feb 2019 09:21:42 +0900
Subject: [PATCH] test-udev: use proper semantics for too long line with
continuation
Follow-up for 1e797cf596df50a6bdd8cbf8e9b2467a3a934171.
(cherry picked from commit e37a5d90b0c624b95f8d0c3400288fec60417ec4)
Related: #1642728
---
test/udev-test.pl | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 3a50694fa9..58b5dc85e1 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -39,10 +39,11 @@ for (my $i = 1; $i <= 10000; ++$i) {
$rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n";
}
-my $rules_10k_tags_continuation = "";
-for (my $i = 1; $i <= 10000; ++$i) {
- $rules_10k_tags_continuation .= 'KERNEL=="sda", TAG+="test' . $i . "\"\\\n";
+my $rules_10k_tags_continuation = "KERNEL==\"sda\", \\\n";
+for (my $i = 1; $i < 10000; ++$i) {
+ $rules_10k_tags_continuation .= 'TAG+="test' . $i . "\",\\\n";
}
+$rules_10k_tags_continuation .= "TAG+=\"test10000\"\\n";
my @tests = (
{

View File

@ -0,0 +1,40 @@
From 66c41fbbeb472563993724352b1984aa3e7e47db Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Tue, 19 Feb 2019 09:22:45 +0900
Subject: [PATCH] test-udev: add more tests for line continuations and comments
(cherry picked from commit d35976c670b0e5c2d4081b781e5af88c0689ff00)
Related: #1642728
---
test/udev-test.pl | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 58b5dc85e1..a5e1f8cda3 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1453,13 +1453,21 @@ TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test1
EOF
},
{
- desc => "don't crash with lots of tags with continuation",
+ desc => "continuations",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
exp_name => "found",
not_exp_name => "bad" ,
rules => $rules_10k_tags_continuation . <<EOF
TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="bad"
-KERNEL=="sda", SYMLINK+="found"
+KERNEL=="sda",\\
+# comment in continuation
+TAG+="hoge1",\\
+ # space before comment
+TAG+="hoge2",\\
+# spaces before and after token are dropped
+ TAG+="hoge3", \\
+TAG+="hoge4"
+TAGS=="hoge1", TAGS=="hoge2", TAGS=="hoge3", TAGS=="hoge4", SYMLINK+="found"
EOF
},
);

View File

@ -0,0 +1,59 @@
From ac0def8fb2b51a17b7ef256c5c0edf786fffff2a Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Thu, 21 Feb 2019 18:03:32 +0900
Subject: [PATCH] test-udev: add more tests for line continuation
(cherry picked from commit 84a0819c9d89a2ddb195a5d975ae1fd5c62fde3c)
Related: #1642728
---
test/udev-test.pl | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index a5e1f8cda3..002fabd9fd 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1466,8 +1466,42 @@ TAG+="hoge1",\\
TAG+="hoge2",\\
# spaces before and after token are dropped
TAG+="hoge3", \\
+\\
+ \\
TAG+="hoge4"
TAGS=="hoge1", TAGS=="hoge2", TAGS=="hoge3", TAGS=="hoge4", SYMLINK+="found"
+EOF
+ },
+ {
+ desc => "continuations with empty line",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad",
+ rules => <<EOF
+# empty line finishes continuation
+KERNEL=="sda", TAG+="foo" \\
+
+KERNEL=="sdb", TAG+="hoge"
+KERNEL=="sda", TAG+="aaa" \\
+KERNEL=="sdb", TAG+="bbb"
+TAGS=="foo", SYMLINK+="found"
+TAGS=="aaa", SYMLINK+="bad"
+EOF
+ },
+ {
+ desc => "continuations with white only line",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad",
+ rules => <<EOF
+# space only line finishes continuation
+KERNEL=="sda", TAG+="foo" \\
+ \t
+KERNEL=="sdb", TAG+="hoge"
+KERNEL=="sda", TAG+="aaa" \\
+KERNEL=="sdb", TAG+="bbb"
+TAGS=="foo", SYMLINK+="found"
+TAGS=="aaa", SYMLINK+="bad"
EOF
},
);

View File

@ -0,0 +1,501 @@
From 7898cd7e75f40627651cec134e3ac3a80176759a Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Thu, 21 Feb 2019 18:04:12 +0900
Subject: [PATCH] test-udev: fix alignment and drop unnecessary white spaces
(cherry picked from commit 3dd2d524141d09d57443ae339e1a77d7ce40f847)
Related: #1642728
---
test/udev-test.pl | 114 +++++++++++++++++++++++-----------------------
1 file changed, 57 insertions(+), 57 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 002fabd9fd..122359e377 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -49,7 +49,7 @@ my @tests = (
{
desc => "no rules",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "sda" ,
+ exp_name => "sda",
exp_rem_error => "yes",
rules => <<EOF
#
@@ -58,7 +58,7 @@ EOF
{
desc => "label test of scsi disc",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "boot_disk" ,
+ exp_name => "boot_disk",
rules => <<EOF
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
KERNEL=="ttyACM0", SYMLINK+="modem"
@@ -67,7 +67,7 @@ EOF
{
desc => "label test of scsi disc",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "boot_disk" ,
+ exp_name => "boot_disk",
rules => <<EOF
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
KERNEL=="ttyACM0", SYMLINK+="modem"
@@ -76,7 +76,7 @@ EOF
{
desc => "label test of scsi disc",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "boot_disk" ,
+ exp_name => "boot_disk",
rules => <<EOF
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
KERNEL=="ttyACM0", SYMLINK+="modem"
@@ -85,7 +85,7 @@ EOF
{
desc => "label test of scsi partition",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
- exp_name => "boot_disk1" ,
+ exp_name => "boot_disk1",
rules => <<EOF
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="boot_disk%n"
EOF
@@ -93,7 +93,7 @@ EOF
{
desc => "label test of pattern match",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
- exp_name => "boot_disk1" ,
+ exp_name => "boot_disk1",
rules => <<EOF
SUBSYSTEMS=="scsi", ATTRS{vendor}=="?ATA", SYMLINK+="boot_disk%n-1"
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA?", SYMLINK+="boot_disk%n-2"
@@ -104,7 +104,7 @@ EOF
{
desc => "label test of multiple sysfs files",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
- exp_name => "boot_disk1" ,
+ exp_name => "boot_disk1",
rules => <<EOF
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS X ", SYMLINK+="boot_diskX%n"
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", SYMLINK+="boot_disk%n"
@@ -113,7 +113,7 @@ EOF
{
desc => "label test of max sysfs files (skip invalid rule)",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
- exp_name => "boot_disk1" ,
+ exp_name => "boot_disk1",
rules => <<EOF
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", ATTRS{queue_depth}=="32", SYMLINK+="boot_diskXX%n"
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", ATTRS{model}=="ST910021AS", ATTRS{scsi_level}=="6", ATTRS{rev}=="4.06", ATTRS{type}=="0", SYMLINK+="boot_disk%n"
@@ -122,7 +122,7 @@ EOF
{
desc => "catch device by *",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem/0" ,
+ exp_name => "modem/0",
rules => <<EOF
KERNEL=="ttyACM*", SYMLINK+="modem/%n"
EOF
@@ -130,7 +130,7 @@ EOF
{
desc => "catch device by * - take 2",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem/0" ,
+ exp_name => "modem/0",
rules => <<EOF
KERNEL=="*ACM1", SYMLINK+="bad"
KERNEL=="*ACM0", SYMLINK+="modem/%n"
@@ -139,7 +139,7 @@ EOF
{
desc => "catch device by ?",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem/0" ,
+ exp_name => "modem/0",
rules => <<EOF
KERNEL=="ttyACM??*", SYMLINK+="modem/%n-1"
KERNEL=="ttyACM??", SYMLINK+="modem/%n-2"
@@ -149,7 +149,7 @@ EOF
{
desc => "catch device by character class",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem/0" ,
+ exp_name => "modem/0",
rules => <<EOF
KERNEL=="ttyACM[A-Z]*", SYMLINK+="modem/%n-1"
KERNEL=="ttyACM?[0-9]", SYMLINK+="modem/%n-2"
@@ -159,7 +159,7 @@ EOF
{
desc => "replace kernel name",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem" ,
+ exp_name => "modem",
rules => <<EOF
KERNEL=="ttyACM0", SYMLINK+="modem"
EOF
@@ -167,7 +167,7 @@ EOF
{
desc => "Handle comment lines in config file (and replace kernel name)",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem" ,
+ exp_name => "modem",
rules => <<EOF
# this is a comment
KERNEL=="ttyACM0", SYMLINK+="modem"
@@ -177,7 +177,7 @@ EOF
{
desc => "Handle comment lines in config file with whitespace (and replace kernel name)",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem" ,
+ exp_name => "modem",
rules => <<EOF
# this is a comment with whitespace before the comment
KERNEL=="ttyACM0", SYMLINK+="modem"
@@ -187,7 +187,7 @@ EOF
{
desc => "Handle whitespace only lines (and replace kernel name)",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "whitespace" ,
+ exp_name => "whitespace",
rules => <<EOF
@@ -202,7 +202,7 @@ EOF
{
desc => "Handle empty lines in config file (and replace kernel name)",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem" ,
+ exp_name => "modem",
rules => <<EOF
KERNEL=="ttyACM0", SYMLINK+="modem"
@@ -212,7 +212,7 @@ EOF
{
desc => "Handle backslashed multi lines in config file (and replace kernel name)",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem" ,
+ exp_name => "modem",
rules => <<EOF
KERNEL=="ttyACM0", \\
SYMLINK+="modem"
@@ -230,7 +230,7 @@ EOF
{
desc => "Handle stupid backslashed multi lines in config file (and replace kernel name)",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem" ,
+ exp_name => "modem",
rules => <<EOF
#
@@ -248,7 +248,7 @@ EOF
{
desc => "subdirectory handling",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "sub/direct/ory/modem" ,
+ exp_name => "sub/direct/ory/modem",
rules => <<EOF
KERNEL=="ttyACM0", SYMLINK+="sub/direct/ory/modem"
EOF
@@ -256,7 +256,7 @@ EOF
{
desc => "parent device name match of scsi partition",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "first_disk5" ,
+ exp_name => "first_disk5",
rules => <<EOF
SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="first_disk%n"
EOF
@@ -264,7 +264,7 @@ EOF
{
desc => "test substitution chars",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0" ,
+ exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0",
rules => <<EOF
SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="Major:%M:minor:%m:kernelnumber:%n:id:%b"
EOF
@@ -281,7 +281,7 @@ EOF
{
desc => "sustitution of sysfs value (%s{file})",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "disk-ATA-sda" ,
+ exp_name => "disk-ATA-sda",
rules => <<EOF
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", SYMLINK+="disk-%s{vendor}-%k"
KERNEL=="ttyACM0", SYMLINK+="modem"
@@ -290,8 +290,8 @@ EOF
{
desc => "program result substitution",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "special-device-5" ,
- not_exp_name => "not" ,
+ exp_name => "special-device-5",
+ not_exp_name => "not",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n special-device", RESULT=="-special-*", SYMLINK+="not"
SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n special-device", RESULT=="special-*", SYMLINK+="%c-%n"
@@ -300,7 +300,7 @@ EOF
{
desc => "program result substitution (newline removal)",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "newline_removed" ,
+ exp_name => "newline_removed",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo test", RESULT=="test", SYMLINK+="newline_removed"
EOF
@@ -308,7 +308,7 @@ EOF
{
desc => "program result substitution",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "test-0:0:0:0" ,
+ exp_name => "test-0:0:0:0",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n test-%b", RESULT=="test-0:0*", SYMLINK+="%c"
EOF
@@ -316,7 +316,7 @@ EOF
{
desc => "program with lots of arguments",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "foo9" ,
+ exp_name => "foo9",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="%c{7}"
EOF
@@ -324,7 +324,7 @@ EOF
{
desc => "program with subshell",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "bar9" ,
+ exp_name => "bar9",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c 'echo foo3 foo4 foo5 foo6 foo7 foo8 foo9 | sed s/foo9/bar9/'", KERNEL=="sda5", SYMLINK+="%c{7}"
EOF
@@ -332,7 +332,7 @@ EOF
{
desc => "program arguments combined with apostrophes",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "foo7" ,
+ exp_name => "foo7",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n 'foo3 foo4' 'foo5 foo6 foo7 foo8'", KERNEL=="sda5", SYMLINK+="%c{5}"
EOF
@@ -340,7 +340,7 @@ EOF
{
desc => "program arguments combined with escaped double quotes, part 1",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "foo2" ,
+ exp_name => "foo2",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c 'printf %%s \\\"foo1 foo2\\\" | grep \\\"foo1 foo2\\\"'", KERNEL=="sda5", SYMLINK+="%c{2}"
EOF
@@ -348,7 +348,7 @@ EOF
{
desc => "program arguments combined with escaped double quotes, part 2",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "foo2" ,
+ exp_name => "foo2",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c \\\"printf %%s 'foo1 foo2' | grep 'foo1 foo2'\\\"", KERNEL=="sda5", SYMLINK+="%c{2}"
EOF
@@ -356,7 +356,7 @@ EOF
{
desc => "program arguments combined with escaped double quotes, part 3",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "foo2" ,
+ exp_name => "foo2",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c 'printf \\\"%%s %%s\\\" \\\"foo1 foo2\\\" \\\"foo3\\\"| grep \\\"foo1 foo2\\\"'", KERNEL=="sda5", SYMLINK+="%c{2}"
EOF
@@ -364,7 +364,7 @@ EOF
{
desc => "characters before the %c{N} substitution",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "my-foo9" ,
+ exp_name => "my-foo9",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="my-%c{7}"
EOF
@@ -372,7 +372,7 @@ EOF
{
desc => "substitute the second to last argument",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "my-foo8" ,
+ exp_name => "my-foo8",
rules => <<EOF
SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9", KERNEL=="sda5", SYMLINK+="my-%c{6}"
EOF
@@ -396,7 +396,7 @@ EOF
{
desc => "test substitution by variable name 3",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "850:0:0:05" ,
+ exp_name => "850:0:0:05",
rules => <<EOF
SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="%M%m%b%n"
EOF
@@ -404,7 +404,7 @@ EOF
{
desc => "test substitution by variable name 4",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "855" ,
+ exp_name => "855",
rules => <<EOF
SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="\$major\$minor\$number"
EOF
@@ -412,7 +412,7 @@ EOF
{
desc => "test substitution by variable name 5",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5",
- exp_name => "8550:0:0:0" ,
+ exp_name => "8550:0:0:0",
rules => <<EOF
SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", DEVPATH=="*/sda/*", SYMLINK+="\$major%m%n\$id"
EOF
@@ -429,7 +429,7 @@ EOF
{
desc => "non matching SUBSYSTEMS",
devpath => "/devices/virtual/tty/console",
- exp_name => "TTY" ,
+ exp_name => "TTY",
rules => <<EOF
SUBSYSTEMS=="foo", ATTRS{dev}=="5:1", SYMLINK+="foo"
KERNEL=="console", SYMLINK+="TTY"
@@ -438,7 +438,7 @@ EOF
{
desc => "ATTRS match",
devpath => "/devices/virtual/tty/console",
- exp_name => "foo" ,
+ exp_name => "foo",
rules => <<EOF
KERNEL=="console", SYMLINK+="TTY"
ATTRS{dev}=="5:1", SYMLINK+="foo"
@@ -447,7 +447,7 @@ EOF
{
desc => "ATTR (empty file)",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "empty" ,
+ exp_name => "empty",
rules => <<EOF
KERNEL=="sda", ATTR{test_empty_file}=="?*", SYMLINK+="something"
KERNEL=="sda", ATTR{test_empty_file}!="", SYMLINK+="not-empty"
@@ -458,7 +458,7 @@ EOF
{
desc => "ATTR (non-existent file)",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "non-existent" ,
+ exp_name => "non-existent",
rules => <<EOF
KERNEL=="sda", ATTR{nofile}=="?*", SYMLINK+="something"
KERNEL=="sda", ATTR{nofile}!="", SYMLINK+="not-empty"
@@ -471,7 +471,7 @@ EOF
{
desc => "program and bus type match",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "scsi-0:0:0:0" ,
+ exp_name => "scsi-0:0:0:0",
rules => <<EOF
SUBSYSTEMS=="usb", PROGRAM=="/bin/echo -n usb-%b", SYMLINK+="%c"
SUBSYSTEMS=="scsi", PROGRAM=="/bin/echo -n scsi-%b", SYMLINK+="%c"
@@ -481,7 +481,7 @@ EOF
{
desc => "sysfs parent hierarchy",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "modem" ,
+ exp_name => "modem",
rules => <<EOF
ATTRS{idProduct}=="007b", SYMLINK+="modem"
EOF
@@ -489,7 +489,7 @@ EOF
{
desc => "name test with ! in the name",
devpath => "/devices/virtual/block/fake!blockdev0",
- exp_name => "is/a/fake/blockdev0" ,
+ exp_name => "is/a/fake/blockdev0",
rules => <<EOF
SUBSYSTEMS=="scsi", SYMLINK+="is/not/a/%k"
SUBSYSTEM=="block", SYMLINK+="is/a/%k"
@@ -499,7 +499,7 @@ EOF
{
desc => "name test with ! in the name, but no matching rule",
devpath => "/devices/virtual/block/fake!blockdev0",
- exp_name => "fake/blockdev0" ,
+ exp_name => "fake/blockdev0",
exp_rem_error => "yes",
rules => <<EOF
KERNEL=="ttyACM0", SYMLINK+="modem"
@@ -542,7 +542,7 @@ EOF
desc => "KERNELS wildcard partial 2",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
exp_name => "scsi-0:0:0:0",
- rules => <<EOF
+ rules => <<EOF
SUBSYSTEMS=="scsi", KERNELS=="0:0:0:0", SYMLINK+="before"
SUBSYSTEMS=="scsi", KERNELS=="*:0:0:0", SYMLINK+="scsi-0:0:0:0"
EOF
@@ -739,7 +739,7 @@ EOF
devpath => "/devices/virtual/misc/misc-fake1",
exp_name => "node",
exp_majorminor => "4095:1",
- rules => <<EOF
+ rules => <<EOF
KERNEL=="misc-fake1", SYMLINK+="node"
EOF
},
@@ -764,7 +764,7 @@ EOF
desc => "multiple symlinks with a lot of s p a c e s",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
exp_name => "one",
- not_exp_name => " ",
+ not_exp_name => " ",
rules => <<EOF
KERNEL=="ttyACM[0-9]*", SYMLINK=" one two "
EOF
@@ -860,7 +860,7 @@ EOF
{
desc => "multiple symlinks",
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "second-0" ,
+ exp_name => "second-0",
rules => <<EOF
KERNEL=="ttyACM0", SYMLINK="first-%n second-%n third-%n"
EOF
@@ -869,8 +869,8 @@ EOF
desc => "symlink name '.'",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
exp_name => ".",
- exp_add_error => "yes",
- exp_rem_error => "yes",
+ exp_add_error => "yes",
+ exp_rem_error => "yes",
rules => <<EOF
SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="."
EOF
@@ -879,9 +879,9 @@ EOF
desc => "symlink node to itself",
devpath => "/devices/virtual/tty/tty0",
exp_name => "link",
- exp_add_error => "yes",
- exp_rem_error => "yes",
- option => "clean",
+ exp_add_error => "yes",
+ exp_rem_error => "yes",
+ option => "clean",
rules => <<EOF
KERNEL=="tty0", SYMLINK+="tty0"
EOF
@@ -1437,7 +1437,7 @@ EOF
desc => "add and match tag",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
exp_name => "found",
- not_exp_name => "bad" ,
+ not_exp_name => "bad",
rules => <<EOF
SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", TAG+="green"
TAGS=="green", SYMLINK+="found"
@@ -1456,7 +1456,7 @@ EOF
desc => "continuations",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
exp_name => "found",
- not_exp_name => "bad" ,
+ not_exp_name => "bad",
rules => $rules_10k_tags_continuation . <<EOF
TAGS=="test1", TAGS=="test500", TAGS=="test1234", TAGS=="test9999", TAGS=="test10000", SYMLINK+="bad"
KERNEL=="sda",\\

View File

@ -0,0 +1,73 @@
From dc50d1cc5bf445f1a26dbc646ff52421563e677f Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@canonical.com>
Date: Fri, 5 Jul 2019 11:24:55 -0400
Subject: [PATCH] test/udev-test.pl: cleanup if skipping test
In Ubuntu CI, udev-test.pl is run from the debian/test/udev script,
in a test dir created for it; but udev-test.pl setup mounts a
dir, so if it doesn't cleanup/unmount before exiting, the test dir
autopkgtest created for it can't be removed, and autopkgtest
aborts the entire test suite, for example this output (from a
test run inside an armhf container):
autopkgtest [12:45:36]: test udev: [-----------------------
umount: test/tmpfs: no mount point specified.
mknod: test/tmpfs/dev/null: Operation not permitted
unable to create test/tmpfs/dev/null at ./udev-test.pl line 1611.
Failed to set up the environment, skipping the test at ./udev-test.pl line 1731.
autopkgtest [12:45:41]: test udev: -----------------------]
autopkgtest [12:45:44]: test udev: - - - - - - - - - - results - - - - - - - - - -
udev FAIL non-zero exit status 77
rm: cannot remove '/tmp/autopkgtest.ocPFA6/autopkgtest_tmp/test/tmpfs': Device or resource busy
autopkgtest [12:46:22]: ERROR: "rm -rf /tmp/autopkgtest.ocPFA6/udev-artifacts /tmp/autopkgtest.ocPFA6/autopkgtest_tmp" failed with stderr "rm:
(cherry picked from commit abb9cc50afb3949c442849f43301fb33578f3888)
Related: #1642728
---
test/udev-test.pl | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 122359e377..2fea72875b 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1713,6 +1713,12 @@ sub run_test {
}
+sub cleanup {
+ system("rm", "-rf", "$udev_run");
+ system("umount", "$udev_tmpfs");
+ rmdir($udev_tmpfs);
+}
+
# only run if we have root permissions
# due to mknod restrictions
if (!($<==0)) {
@@ -1729,11 +1735,13 @@ if ($? >> 8 == 0) {
if (!udev_setup()) {
warn "Failed to set up the environment, skipping the test";
+ cleanup();
exit($EXIT_TEST_SKIP);
}
if (system($udev_bin, "check")) {
warn "$udev_bin failed to set up the environment, skipping the test";
+ cleanup();
exit($EXIT_TEST_SKIP);
}
@@ -1776,10 +1784,7 @@ if ($list[0]) {
print "$error errors occurred\n\n";
-# cleanup
-system("rm", "-rf", "$udev_run");
-system("umount", "$udev_tmpfs");
-rmdir($udev_tmpfs);
+cleanup();
if ($error > 0) {
exit(1);

View File

@ -0,0 +1,89 @@
From 03bc565e6e3249385c4e1ca0ae27670ca2ad9a41 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 11 Sep 2019 09:06:15 +0900
Subject: [PATCH] test: add test cases for empty string match
(cherry picked from commit 48d26c90852c22ec94be961f5fbdcf462bb9a6e8)
Related: #1642728
---
test/udev-test.pl | 66 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 2fea72875b..50d978391b 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1256,6 +1256,72 @@ KERNEL=="dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong1"
KERNEL=="X|attyACM0|dontknow|ttyACM0a|nothing|attyACM0", SYMLINK+="wrong2"
KERNEL=="all|dontknow|ttyACM0", SYMLINK+="right"
KERNEL=="ttyACM0a|nothing", SYMLINK+="wrong3"
+EOF
+ },
+ {
+ desc => "test multi matches 5",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad",
+ rules => <<EOF
+KERNEL=="sda", TAG="foo"
+TAGS=="|foo", SYMLINK+="found"
+TAGS=="|aaa", SYMLINK+="bad"
+EOF
+ },
+ {
+ desc => "test multi matches 6",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad",
+ rules => <<EOF
+KERNEL=="sda", TAG=""
+TAGS=="|foo", SYMLINK+="found"
+TAGS=="aaa|bbb", SYMLINK+="bad"
+EOF
+ },
+ {
+ desc => "test multi matches 7",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad",
+ rules => <<EOF
+KERNEL=="sda", TAG="foo"
+TAGS=="foo||bar", SYMLINK+="found"
+TAGS=="aaa||bbb", SYMLINK+="bad"
+EOF
+ },
+ {
+ desc => "test multi matches 8",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad",
+ rules => <<EOF
+KERNEL=="sda", TAG=""
+TAGS=="foo||bar", SYMLINK+="found"
+TAGS=="aaa|bbb", SYMLINK+="bad"
+EOF
+ },
+ {
+ desc => "test multi matches 9",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad",
+ rules => <<EOF
+KERNEL=="sda", TAG="foo"
+TAGS=="foo|", SYMLINK+="found"
+TAGS=="aaa|", SYMLINK+="bad"
+EOF
+ },
+ {
+ desc => "test multi matches 10",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad",
+ rules => <<EOF
+KERNEL=="sda", TAG=""
+TAGS=="foo|", SYMLINK+="found"
+TAGS=="aaa|bbb", SYMLINK+="bad"
EOF
},
{

View File

@ -0,0 +1,35 @@
From 03b766cc937ffa4dcb7cfb25b2ac20d8a00cb6db Mon Sep 17 00:00:00 2001
From: gaoyi <ymuemc@163.com>
Date: Sun, 12 Jul 2020 03:27:45 -0400
Subject: [PATCH] test: add test case for multi matches when use "||"
Signed-off-by: gaoyi <ymuemc@163.com>
(cherry picked from commit 0d3a8bc7ebd76591e14f7098b4266fd2065ac4db)
Related: #1642728
---
test/udev-test.pl | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 50d978391b..4bf97d82bb 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1322,6 +1322,17 @@ EOF
KERNEL=="sda", TAG=""
TAGS=="foo|", SYMLINK+="found"
TAGS=="aaa|bbb", SYMLINK+="bad"
+EOF
+ },
+ {
+ desc => "test multi matches 11",
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_name => "found",
+ not_exp_name => "bad",
+ rules => <<EOF
+KERNEL=="sda", TAG="c"
+TAGS=="foo||bar||c", SYMLINK+="found"
+TAGS=="aaa||bbb||ccc", SYMLINK+="bad"
EOF
},
{

View File

@ -0,0 +1,33 @@
From c68da72231d5c502acd4e79791d0810790f3231b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Fri, 4 Sep 2020 18:09:20 +0200
Subject: [PATCH] udev-test: do not rely on "mail" group being defined
"audio" should be there, at least we declare it. "mail" nowadays is less
likely to exist than in the past.
Fixes one of the items in #16942.
(cherry picked from commit a9030b81c154c3ec92227d04cad6b13cc1125608)
Related: #1642728
---
test/udev-test.pl | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 4bf97d82bb..a4deffacb9 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -629,9 +629,9 @@ EOF
desc => "textual user/group id",
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
exp_name => "node",
- exp_perms => "root:mail:0660",
+ exp_perms => "root:audio:0660",
rules => <<EOF
-SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="root", GROUP="mail"
+SUBSYSTEMS=="scsi", KERNEL=="sda", SYMLINK+="node", OWNER="root", GROUP="audio"
EOF
},
{

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
From 9aa12f2f564c208c4c1eaef613d18d1c0b481a16 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Mon, 23 Apr 2018 21:58:12 +0200
Subject: [PATCH] test/udev-test.pl: create rules only once
It's not necessary to write the rules for every udev run, as we
now may have many (rather than just 2) per test.
(cherry picked from commit af7ee3eae689f9c31b49ea13758ad9c901918ce3)
Related: #1642728
---
test/udev-test.pl | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index bd5401da75..8b5a97ad61 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -2069,14 +2069,18 @@ EOF
},
);
-sub udev {
- my ($action, $devpath, $rules) = @_;
+sub create_rules {
+ my ($rules) = @_;
# create temporary rules
system("mkdir", "-p", "$udev_rules_dir");
open CONF, ">$udev_rules" || die "unable to create rules file: $udev_rules";
print CONF $$rules;
close CONF;
+}
+
+sub udev {
+ my ($action, $devpath) = @_;
if ($valgrind > 0) {
return system("$udev_bin_valgrind $action $devpath");
@@ -2259,9 +2263,10 @@ sub run_test {
my @devices = @{$rules->{devices}};
print "TEST $number: $rules->{desc}\n";
+ create_rules(\$rules->{rules});
foreach my $dev (@devices) {
print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n";
- $rc = udev("add", $dev->{devpath}, \$rules->{rules});
+ $rc = udev("add", $dev->{devpath});
if ($rc != 0) {
print "$udev_bin add failed with code $rc\n";
$error++;
@@ -2278,7 +2283,7 @@ sub run_test {
}
foreach my $dev (@devices) {
- $rc = udev("remove", $dev->{devpath}, \$rules->{rules});
+ $rc = udev("remove", $dev->{devpath});
if ($rc != 0) {
print "$udev_bin remove failed with code $rc\n";
$error++;

View File

@ -0,0 +1,169 @@
From 618d56c7ac8bd8cd701344a0eaca8373a78dea95 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Mon, 23 Apr 2018 21:59:05 +0200
Subject: [PATCH] test/udev-test.pl: allow concurrent additions and removals
Allow testing cases where multiple devices are added and removed
simultaneously. Tests are started as synchronously as possible using a
semaphore, in order to test possible race conditions. If this isn't desired,
the test parameter "sleep_us" can be set to the number of microseconds to wait
between udev invocations.
(cherry picked from commit 09a4062d70b3a10d022e40066e2adf09df05bbbc)
Related: #1642728
---
test/udev-test.pl | 90 +++++++++++++++++++++++++++++++++++++----------
1 file changed, 72 insertions(+), 18 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 8b5a97ad61..db25ef13c1 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -18,6 +18,10 @@
use warnings;
use strict;
+use POSIX qw(WIFEXITED WEXITSTATUS);
+use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT);
+use IPC::Semaphore;
+use Time::HiRes qw(usleep);
my $udev_bin = "./test-udev";
my $valgrind = 0;
@@ -2210,6 +2214,8 @@ sub check_add {
sleep(1);
}
}
+
+ print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n";
if ((-e "$udev_dev/$device->{exp_name}") ||
(-l "$udev_dev/$device->{exp_name}")) {
@@ -2257,21 +2263,72 @@ sub check_remove {
}
}
+sub run_udev {
+ my ($action, $dev, $sleep_us, $sema) = @_;
+
+ # Notify main process that this worker has started
+ $sema->op(0, 1, 0);
+
+ # Wait for start
+ $sema->op(0, 0, 0);
+ usleep($sleep_us) if defined ($sleep_us);
+ my $rc = udev($action, $dev->{devpath});
+ exit $rc;
+}
+
+sub fork_and_run_udev {
+ my ($action, $rules, $sema) = @_;
+ my @devices = @{$rules->{devices}};
+ my $dev;
+ my $k = 0;
+
+ $sema->setval(0, 1);
+ foreach $dev (@devices) {
+ my $pid = fork();
+
+ if (!$pid) {
+ run_udev($action, $dev,
+ defined($rules->{sleep_us}) ? $k * $rules->{sleep_us} : undef,
+ $sema);
+ } else {
+ $dev->{pid} = $pid;
+ }
+ $k++;
+ }
+
+ # This operation waits for all workers to become ready, and
+ # starts them off when that's the case.
+ $sema->op(0, -($#devices + 2), 0);
+
+ foreach $dev (@devices) {
+ my $rc;
+ my $pid;
+
+ $pid = waitpid($dev->{pid}, 0);
+ if ($pid == -1) {
+ print "error waiting for pid dev->{pid}\n";
+ $error += 1;
+ }
+ if (WIFEXITED($?)) {
+ $rc = WEXITSTATUS($?);
+
+ if ($rc) {
+ print "$udev_bin $action for $dev->{devpath} failed with code $rc\n";
+ $error += 1;
+ }
+ }
+ }
+}
+
sub run_test {
- my ($rules, $number) = @_;
+ my ($rules, $number, $sema) = @_;
my $rc;
my @devices = @{$rules->{devices}};
print "TEST $number: $rules->{desc}\n";
create_rules(\$rules->{rules});
- foreach my $dev (@devices) {
- print "device \'$dev->{devpath}\' expecting node/link \'$dev->{exp_name}\'\n";
- $rc = udev("add", $dev->{devpath});
- if ($rc != 0) {
- print "$udev_bin add failed with code $rc\n";
- $error++;
- }
- }
+
+ fork_and_run_udev("add", $rules, $sema);
foreach my $dev (@devices) {
check_add($dev);
@@ -2282,13 +2339,8 @@ sub run_test {
return;
}
- foreach my $dev (@devices) {
- $rc = udev("remove", $dev->{devpath});
- if ($rc != 0) {
- print "$udev_bin remove failed with code $rc\n";
- $error++;
- }
- }
+ fork_and_run_udev("remove", $rules, $sema);
+
foreach my $dev (@devices) {
check_remove($dev);
}
@@ -2350,12 +2402,13 @@ foreach my $arg (@ARGV) {
push(@list, $arg);
}
}
+my $sema = IPC::Semaphore->new(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR | IPC_CREAT);
if ($list[0]) {
foreach my $arg (@list) {
if (defined($tests[$arg-1]->{desc})) {
print "udev-test will run test number $arg:\n\n";
- run_test($tests[$arg-1], $arg);
+ run_test($tests[$arg-1], $arg, $sema);
} else {
print "test does not exist.\n";
}
@@ -2365,11 +2418,12 @@ if ($list[0]) {
print "\nudev-test will run ".($#tests + 1)." tests:\n\n";
foreach my $rules (@tests) {
- run_test($rules, $test_num);
+ run_test($rules, $test_num, $sema);
$test_num++;
}
}
+$sema->remove;
print "$error errors occurred\n\n";
cleanup();

View File

@ -0,0 +1,260 @@
From 5f34ea55a8c6723240eb1641a655db7df3c428a2 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 24 Apr 2018 09:38:26 +0200
Subject: [PATCH] test/udev-test.pl: use computed devnode name
More often than not, the created devnode is the basename of the
sysfs entry. The "devnode" device may be used to override the
auto-detected node name.
Permissions and major/minor number are now verified on the devnode
itself, not on symlinks.
For those tests where exp_name is set to the computed devnode name,
the explicit "exp_name" can be removed. "exp_name" is only required for
symlinks.
This allows separate testing for devnodes and symlinks an a follow-up
patch.
(cherry picked from commit f0dccf01a7b4e72278e14effd74782ea83d0a73b)
Related: #1642728
---
test/udev-test.pl | 92 +++++++++++++++++++++++++++++++++--------------
1 file changed, 66 insertions(+), 26 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index db25ef13c1..aa9a8dc2ff 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -55,12 +55,10 @@ my @tests = (
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "sda" ,
exp_rem_error => "yes",
},
{
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
- exp_name => "sda1" ,
exp_rem_error => "yes",
}],
rules => <<EOF
@@ -644,6 +642,7 @@ EOF
devices => [
{
devpath => "/devices/virtual/block/fake!blockdev0",
+ devnode => "fake/blockdev0",
exp_name => "is/a/fake/blockdev0" ,
}],
rules => <<EOF
@@ -657,7 +656,7 @@ EOF
devices => [
{
devpath => "/devices/virtual/block/fake!blockdev0",
- exp_name => "fake/blockdev0" ,
+ devnode => "fake/blockdev0",
exp_rem_error => "yes",
}],
rules => <<EOF
@@ -768,7 +767,6 @@ EOF
devices => [
{
devpath => "/devices/virtual/tty/tty33",
- exp_name => "tty33",
exp_perms => "0:0:0600",
}],
rules => <<EOF
@@ -864,7 +862,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "ttyACM0",
exp_perms => "1::",
}],
rules => <<EOF
@@ -876,7 +873,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "ttyACM0",
exp_perms => ":1:0660",
}],
rules => <<EOF
@@ -888,7 +884,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "ttyACM0",
exp_perms => "::0060",
}],
rules => <<EOF
@@ -900,7 +895,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "ttyACM0",
exp_perms => "1:1:0777",
}],
rules => <<EOF
@@ -912,7 +906,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "ttyACM0",
exp_perms => "1:1:0777",
}],
rules => <<EOF
@@ -926,7 +919,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "ttyACM0",
exp_perms => "1:1:0777",
}],
rules => <<EOF
@@ -942,7 +934,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_name => "ttyACM0",
exp_perms => "1:2:0777",
}],
rules => <<EOF
@@ -1922,7 +1913,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "sda",
exp_perms => "0:0:0000",
exp_rem_error => "yes",
}],
@@ -1935,7 +1925,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "sda",
exp_perms => "1:1:0400",
exp_rem_error => "yes",
}],
@@ -1949,7 +1938,6 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
- exp_name => "sda",
exp_perms => "0:0:0440",
exp_rem_error => "yes",
}],
@@ -2203,6 +2191,44 @@ sub udev_setup {
return 1;
}
+sub get_devnode {
+ my ($device) = @_;
+ my $devnode;
+
+ if (defined($device->{devnode})) {
+ $devnode = "$udev_dev/$device->{devnode}";
+ } else {
+ $devnode = "$device->{devpath}";
+ $devnode =~ s!.*/!$udev_dev/!;
+ }
+ return $devnode;
+}
+
+sub check_devnode {
+ my ($device) = @_;
+ my $devnode = get_devnode($device);
+
+ my @st = lstat("$devnode");
+ if (! (-b _ || -c _)) {
+ print "add $devnode: error\n";
+ system("tree", "$udev_dev");
+ $error++;
+ return undef;
+ }
+
+ my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
+ $atime, $mtime, $ctime, $blksize, $blocks) = @st;
+
+ if (defined($device->{exp_perms})) {
+ permissions_test($device, $uid, $gid, $mode);
+ }
+ if (defined($device->{exp_majorminor})) {
+ major_minor_test($device, $rdev);
+ }
+ print "add $devnode: ok\n";
+ return $devnode;
+}
+
sub check_add {
my ($device) = @_;
@@ -2215,19 +2241,13 @@ sub check_add {
}
}
+ my $devnode = check_devnode($device);
+
print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n";
+ return if (!defined($device->{exp_name}));
+
if ((-e "$udev_dev/$device->{exp_name}") ||
(-l "$udev_dev/$device->{exp_name}")) {
-
- my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
- $atime, $mtime, $ctime, $blksize, $blocks) = stat("$udev_dev/$device->{exp_name}");
-
- if (defined($device->{exp_perms})) {
- permissions_test($device, $uid, $gid, $mode);
- }
- if (defined($device->{exp_majorminor})) {
- major_minor_test($device, $rdev);
- }
print "add $device->{devpath}: ok\n";
} else {
print "add $device->{devpath}: error";
@@ -2243,12 +2263,32 @@ sub check_add {
}
}
+sub check_remove_devnode {
+ my ($device) = @_;
+ my $devnode = get_devnode($device);
+
+ if (-e "$devnode") {
+ print "remove $devnode: error";
+ print "\n";
+ system("tree", "$udev_dev");
+ print "\n";
+ $error++;
+ sleep(1);
+ } else {
+ print "remove $devnode: ok\n";
+ }
+}
+
sub check_remove {
my ($device) = @_;
+ check_remove_devnode($device);
+
+ return if (!defined($device->{exp_name}));
+
if ((-e "$udev_dev/$device->{exp_name}") ||
(-l "$udev_dev/$device->{exp_name}")) {
- print "remove $device->{devpath}: error";
+ print "remove $device->{exp_name}: error";
if ($device->{exp_rem_error}) {
print " as expected\n";
} else {
@@ -2259,7 +2299,7 @@ sub check_remove {
sleep(1);
}
} else {
- print "remove $device->{devpath}: ok\n";
+ print "remove $device->{exp_name}: ok\n";
}
}

View File

@ -0,0 +1,61 @@
From 8ee1cc626f616a2022d641a464fbde9108dd8ad9 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 24 Apr 2018 10:50:24 +0200
Subject: [PATCH] test/udev-test.pl: test correctness of symlink targets
Test if symlinks are created correctly by comparing the symlink
targets to the devnode path. This implies (for the symlink) that
major/minor numbers and permissions are correct, as we have tested
that on the devnode already.
(cherry picked from commit 997683c8f152e1c139a7ce537de81a0aeae4627f)
Related: #1642728
---
test/udev-test.pl | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index aa9a8dc2ff..2e3089c5e0 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -22,6 +22,7 @@ use POSIX qw(WIFEXITED WEXITSTATUS);
use IPC::SysV qw(IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT);
use IPC::Semaphore;
use Time::HiRes qw(usleep);
+use Cwd qw(getcwd abs_path);
my $udev_bin = "./test-udev";
my $valgrind = 0;
@@ -2243,14 +2244,26 @@ sub check_add {
my $devnode = check_devnode($device);
- print "device \'$device->{devpath}\' expecting node/link \'$device->{exp_name}\'\n";
return if (!defined($device->{exp_name}));
- if ((-e "$udev_dev/$device->{exp_name}") ||
- (-l "$udev_dev/$device->{exp_name}")) {
- print "add $device->{devpath}: ok\n";
+ my @st = lstat("$udev_dev/$device->{exp_name}");
+ if (-l _) {
+ my $cwd = getcwd();
+ my $dir = "$udev_dev/$device->{exp_name}";
+ $dir =~ s!/[^/]*$!!;
+ my $tgt = readlink("$udev_dev/$device->{exp_name}");
+ $tgt = abs_path("$dir/$tgt");
+ $tgt =~ s!^$cwd/!!;
+
+ if ($tgt ne $devnode) {
+ print "symlink $device->{exp_name}: error, found -> $tgt\n";
+ $error++;
+ system("tree", "$udev_dev");
+ } else {
+ print "symlink $device->{exp_name}: ok\n";
+ }
} else {
- print "add $device->{devpath}: error";
+ print "symlink $device->{exp_name}: error";
if ($device->{exp_add_error}) {
print " as expected\n";
} else {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
From 0e0b90ffcf0731865846bfa2754a809cc2b8c53e Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 24 Apr 2018 17:57:47 +0200
Subject: [PATCH] test/udev-test.pl: fix wrong test descriptions
udev hasn't supported renaming device nodes for some time.
(cherry picked from commit 46bc71b2b73f8a1e27dc5e142730e9877dd05e3e)
Related: #1642728
---
test/udev-test.pl | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index f5edecefd0..d5d0e130e3 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -212,7 +212,7 @@ KERNEL=="ttyACM[0-9]*", SYMLINK+="modem/%n"
EOF
},
{
- desc => "replace kernel name",
+ desc => "don't replace kernel name",
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
@@ -223,7 +223,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem"
EOF
},
{
- desc => "Handle comment lines in config file (and replace kernel name)",
+ desc => "Handle comment lines in config file (and don't replace kernel name)",
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
@@ -236,7 +236,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem"
EOF
},
{
- desc => "Handle comment lines in config file with whitespace (and replace kernel name)",
+ desc => "Handle comment lines in config file with whitespace (and don't replace kernel name)",
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
@@ -249,7 +249,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem"
EOF
},
{
- desc => "Handle whitespace only lines (and replace kernel name)",
+ desc => "Handle whitespace only lines (and don't replace kernel name)",
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
@@ -267,7 +267,7 @@ KERNEL=="ttyACM0", SYMLINK+="whitespace"
EOF
},
{
- desc => "Handle empty lines in config file (and replace kernel name)",
+ desc => "Handle empty lines in config file (and don't replace kernel name)",
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
@@ -280,7 +280,7 @@ KERNEL=="ttyACM0", SYMLINK+="modem"
EOF
},
{
- desc => "Handle backslashed multi lines in config file (and replace kernel name)",
+ desc => "Handle backslashed multi lines in config file (and don't replace kernel name)",
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
@@ -303,8 +303,9 @@ EOF
KERNEL=="ttyACM0", PROGRAM=="/bin/echo -e \\101", RESULT=="A", SYMLINK+="aaa"
EOF
},
+ # 20
{
- desc => "Handle stupid backslashed multi lines in config file (and replace kernel name)",
+ desc => "Handle stupid backslashed multi lines in config file (and don't replace kernel name)",
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",

View File

@ -0,0 +1,35 @@
From 2d0b828715e67f7accda6f73481deb74febebcb6 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 24 Apr 2018 18:08:18 +0200
Subject: [PATCH] test/udev-test.pl: last_rule is unsupported
the "last_rule" option hasn't been supported for some time.
Therefore this test fails if a "not_exp_links" attribute is added,
as it should be. Mark it appropriately.
(cherry picked from commit 17cce031531a5d3f38a27374c99d1bdba5959dbd)
Related: #1642728
---
test/udev-test.pl | 3 +++
1 file changed, 3 insertions(+)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index d5d0e130e3..a9c2dd95f1 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1373,11 +1373,14 @@ SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="start-%r-end"
EOF
},
{
+ # This is not supported any more
desc => "last_rule option",
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
exp_links => ["last"],
+ not_exp_links => ["very-last"],
+ exp_nodev_error => "yes",
}],
rules => <<EOF
SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="last", OPTIONS="last_rule"

View File

@ -0,0 +1,74 @@
From 134a415c2d690e57d0f1add23e900e60bcef4627 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 24 Apr 2018 18:09:50 +0200
Subject: [PATCH] test/udev-test.pl: Make some tests a little harder
Add some rules that make it a bit harder to pass, mainly the
non-existence checks.
(cherry picked from commit 06d4d4e24e7d0b51120b165e540d278842e8b1a3)
Related: #1642728
---
test/udev-test.pl | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index a9c2dd95f1..7465b5859e 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1358,7 +1358,7 @@ EOF
exp_links => ["sda-part-1"],
}],
rules => <<EOF
-SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="%P-part-1"
+SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="%P-part-%n"
EOF
},
{
@@ -1486,6 +1486,7 @@ SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="true"
SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}="absolutely-\$env{ASSIGN}"
SUBSYSTEMS=="scsi", KERNEL=="sda1", SYMLINK+="before"
SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="yes", SYMLINK+="no"
+SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="true", SYMLINK+="bad"
SUBSYSTEMS=="scsi", KERNEL=="sda1", ENV{ASSIGN}=="absolutely-true", SYMLINK+="true"
EOF
},
@@ -1497,6 +1498,11 @@ EOF
exp_links => ["part"],
not_exp_links => ["disk"],
},
+ {
+ devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
+ exp_links => ["disk"],
+ not_exp_links => ["part"],
+ },
],
rules => <<EOF
SUBSYSTEM=="block", KERNEL=="*[0-9]", ENV{PARTITION}="true", ENV{MAINDEVICE}="false"
@@ -1588,7 +1594,7 @@ EOF
}],
rules => <<EOF
KERNEL=="sda", GROUP:="tty"
-KERNEL=="sda", GROUP="not-ok", MODE="0640", SYMLINK+="ok"
+KERNEL=="sda", GROUP="root", MODE="0640", SYMLINK+="ok"
EOF
},
{
@@ -1602,7 +1608,7 @@ EOF
rules => <<EOF
KERNEL=="sda", GROUP:="tty"
SUBSYSTEM=="block", MODE:="640"
-KERNEL=="sda", GROUP="not-ok", MODE="0666", SYMLINK+="ok"
+KERNEL=="sda", GROUP="root", MODE="0666", SYMLINK+="ok"
EOF
},
{
@@ -1983,7 +1989,6 @@ EOF
{
devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda",
exp_perms => "1:1:0400",
- exp_rem_error => "yes",
}],
rules => <<EOF
KERNEL=="sda", MODE="666"

View File

@ -0,0 +1,28 @@
From c63273de445789ffeea961448fda51d21c924f8c Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 24 Apr 2018 18:16:59 +0200
Subject: [PATCH] test/udev-test.pl: remove bogus rules from magic subsys test
These rules have survived from an ancient version of the code
and save no purpose any more.
(cherry picked from commit 86634df43b715f3f77c7de73a3ef6566e5cdf571)
Related: #1642728
---
test/udev-test.pl | 2 --
1 file changed, 2 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 7465b5859e..6928439d14 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -2017,8 +2017,6 @@ EOF
exp_perms => "0:0:0600",
}],
rules => <<EOF
-KERNEL=="sda", PROGRAM="/bin/true create-envp"
-KERNEL=="sda", ENV{TESTENV}="change-envp"
KERNEL=="sda", SYMLINK+="%k-%s{[dmi/id]product_name}-end"
EOF
},

View File

@ -0,0 +1,57 @@
From 7821ecc0b35c422bc8ed26e0e44c841d067f88d7 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 24 Apr 2018 18:27:25 +0200
Subject: [PATCH] test/udev-test.pl: merge "space and var with space" tests
As we can check multiple links in a single test now, these 3
tests can be merged into one.
(cherry picked from commit 2084fe0d3290c525ecb9faa07d07c3abc2488e59)
Related: #1642728
---
test/udev-test.pl | 31 +++----------------------------
1 file changed, 3 insertions(+), 28 deletions(-)
diff --git a/test/udev-test.pl b/test/udev-test.pl
index 6928439d14..880a73b10b 100755
--- a/test/udev-test.pl
+++ b/test/udev-test.pl
@@ -1103,34 +1103,9 @@ EOF
devices => [
{
devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_links => ["first"],
- not_exp_links => [" "],
- }],
- rules => <<EOF
-ENV{WITH_WS}=" one two three "
-SYMLINK=" first name-\$env{WITH_WS}-end another_symlink a b c "
-EOF
- },
- {
- desc => "symlink with space and var with space, part 2",
- devices => [
- {
- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_links => ["name-one_two_three-end"],
- not_exp_links => [" "],
- }],
- rules => <<EOF
-ENV{WITH_WS}=" one two three "
-SYMLINK=" first name-\$env{WITH_WS}-end another_symlink a b c "
-EOF
- },
- {
- desc => "symlink with space and var with space, part 3",
- devices => [
- {
- devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0",
- exp_links => ["another_symlink"],
- not_exp_links => [" "],
+ exp_links => ["first", "name-one_two_three-end",
+ "another_symlink", "a", "b", "c"],
+ not_exp_links => [" "],
}],
rules => <<EOF
ENV{WITH_WS}=" one two three "

Some files were not shown because too many files have changed in this diff Show More