From 385294f7031227f7363e3c47f268fb71833a55d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 10 Mar 2026 22:56:00 +0100 Subject: [PATCH 1/4] sysusers: disallow --cat-config with --inline It doesn't work and it doesn't make much sense. --- src/sysusers/sysusers.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 4e604f9a75..d1570eda56 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -2184,6 +2184,10 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --replace= is not supported with --cat-config/--tldr."); + if (arg_inline && arg_cat_flags != CAT_CONFIG_OFF) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Option --inline is not supported with --cat-config/--tldr."); + if (arg_replace && optind >= argc) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "When --replace= is given, some configuration items must be specified."); From 548816e05829df7547319e7f9eb7dbab039246a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 10 Mar 2026 22:53:03 +0100 Subject: [PATCH 2/4] tmpfiles: add --inline This option is exactly like the one in sysusers. (In fact the implementation is copied too.) It is occasionally useful to be able to specify and execute some tmpfiles config not through config files but directly on the command line. This also makes it very easy to test config with: SYSTEMD_LOG_LEVEL=debug systemd-tmpfiles --dry-run --inline ... --- man/systemd-tmpfiles.xml | 8 ++++++++ src/tmpfiles/tmpfiles.c | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index 815dcd88d6..c48c0653b0 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -210,6 +210,14 @@ + + + Treat each positional argument as a separate configuration + line instead of a file name. + + + + Only apply rules with paths that start with diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 0d62b847b6..7885a668fd 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -199,6 +199,7 @@ typedef enum { static CatFlags arg_cat_flags = CAT_CONFIG_OFF; static bool arg_dry_run = false; +static bool arg_inline = false; static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; static OperationMask arg_operation = 0; static bool arg_boot = false; @@ -3589,6 +3590,7 @@ static int parse_line( assert(fname); assert(line >= 1); assert(buffer); + assert(invalid_config); const Specifier specifier_table[] = { { 'h', specifier_user_home, NULL }, @@ -4157,6 +4159,7 @@ static int help(void) { " --image-policy=POLICY Specify disk image dissection policy\n" " --replace=PATH Treat arguments as replacement for PATH\n" " --dry-run Just print what would be done\n" + " --inline Treat arguments as configuration lines\n" " --no-pager Do not pipe output into a pager\n" "\nSee the %5$s for details.\n", program_invocation_short_name, @@ -4187,6 +4190,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_IMAGE_POLICY, ARG_REPLACE, ARG_DRY_RUN, + ARG_INLINE, ARG_NO_PAGER, }; @@ -4209,6 +4213,7 @@ static int parse_argv(int argc, char *argv[]) { { "image-policy", required_argument, NULL, ARG_IMAGE_POLICY }, { "replace", required_argument, NULL, ARG_REPLACE }, { "dry-run", no_argument, NULL, ARG_DRY_RUN }, + { "inline", no_argument, NULL, ARG_INLINE }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, {} }; @@ -4316,6 +4321,10 @@ static int parse_argv(int argc, char *argv[]) { arg_dry_run = true; break; + case ARG_INLINE: + arg_inline = true; + break; + case ARG_NO_PAGER: arg_pager_flags |= PAGER_DISABLE; break; @@ -4339,6 +4348,10 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --replace= is not supported with --cat-config/--tldr."); + if (arg_inline && arg_cat_flags != CAT_CONFIG_OFF) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Option --inline is not supported with --cat-config/--tldr."); + if (arg_replace && optind >= argc) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "When --replace= is given, some configuration items must be specified."); @@ -4415,14 +4428,29 @@ static int parse_arguments( char **config_dirs, char **args, bool *invalid_config) { + + unsigned pos = 1; int r; assert(c); STRV_FOREACH(arg, args) { - r = read_config_file(c, config_dirs, *arg, false, invalid_config); - if (r < 0) - return r; + if (arg_inline) { + bool invalid_arg = false; + + /* Use (argument):n, where n==1 for the first positional arg */ + r = parse_line("(argument)", pos, *arg, &invalid_arg, c); + if (invalid_arg) + *invalid_config = true; + else if (r < 0) + return r; + } else { + r = read_config_file(c, config_dirs, *arg, /* ignore_enoent= */ false, invalid_config); + if (r < 0) + return r; + } + + pos++; } return 0; From ee97cef1ead7fb97a33bf76bc963c0c4b43260db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 10 Mar 2026 22:59:37 +0100 Subject: [PATCH 3/4] units: stop using rm in system-update-cleanup.service If we are running on a system without coreutils, rm might not be available. Let's use our own binary that should be available. --- 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 a54e74567e..e9ff88c73c 100644 --- a/units/system-update-cleanup.service +++ b/units/system-update-cleanup.service @@ -34,4 +34,4 @@ ConditionPathIsSymbolicLink=|/etc/system-update [Service] Type=oneshot -ExecStart=rm -fv /system-update /etc/system-update +ExecStart=systemd-tmpfiles --inline --remove 'r /system-update' 'r /etc/system-update' From 151f803c3e802c6230a4b99936a7dd0a397d1b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 10 Mar 2026 23:20:15 +0100 Subject: [PATCH 4/4] TEST-22-TMPFILES: add simple test for tmpfiles --inline Also add tests for handling of invalid_config. --- test/units/TEST-22-TMPFILES.01.sh | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/test/units/TEST-22-TMPFILES.01.sh b/test/units/TEST-22-TMPFILES.01.sh index 4159796dae..4062c838dc 100755 --- a/test/units/TEST-22-TMPFILES.01.sh +++ b/test/units/TEST-22-TMPFILES.01.sh @@ -9,5 +9,24 @@ set -o pipefail rm -fr /tmp/test echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create - - test ! -e /tmp/test + +touch /tmp/test +echo "r /tmp/test - - - -" | systemd-tmpfiles --remove - +test ! -e /tmp/test + +touch /tmp/test +systemd-tmpfiles --remove --inline 'p /tmp/fifo' 'r /tmp/test' +test ! -e /tmp/fifo +test ! -e /tmp/test + +# Test invalid config +systemd-tmpfiles --inline --remove 'garbage' || ret=$? +test "$ret" -eq 65 # EX_DATAERR + +echo 'garbage' >/tmp/config.conf +systemd-tmpfiles --remove /tmp/config.conf || ret=$? +test "$ret" -eq 65 # EX_DATAERR + +systemd-tmpfiles --remove /tmp/config-missing.conf || ret=$? +test "$ret" -eq 1