Merge pull request #30380 from keszybz/tmpfiles-dry-run

Make tmpfiles/sysusers nicer with local files and implement tmpfiles --dry-run
This commit is contained in:
Lennart Poettering 2024-02-13 09:45:50 +01:00 committed by GitHub
commit c9cdbaed17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 887 additions and 464 deletions

3
TODO
View file

@ -2457,18 +2457,17 @@ Features:
* support crash reporting operation modes (https://live.gnome.org/GnomeOS/Design/Whiteboards/ProblemReporting)
* tmpfiles:
- apply "x" on "D" too (see patch from William Douglas)
- allow time-based cleanup in r and R too
- instead of ignoring unknown fields, reject them.
- creating new directories/subvolumes/fifos/device nodes
should not follow symlinks. None of the other adjustment or creation
calls follow symlinks.
- add --test mode
- teach tmpfiles.d q/Q logic something sensible in the context of XFS/ext4
project quota
- teach tmpfiles.d m/M to move / atomic move + symlink old -> new
- add new line type for setting btrfs subvolume attributes (i.e. rw/ro)
- tmpfiles: add new line type for setting fcaps
- add -n as shortcut for --dry-run in tmpfiles & sysusers & possibly other places
* udev-link-config:
- Make sure ID_PATH is always exported and complete for

View file

@ -352,10 +352,10 @@
<para>When this option is used the original file descriptors <command>systemd-run</command> receives are passed
to the service processes as-is. If the service runs with different privileges than
<command>systemd-run</command>, this means the service might not be able to re-open the passed file
<command>systemd-run</command>, this means the service might not be able to reopen the passed file
descriptors, due to normal file descriptor access restrictions. If the invoked process is a shell script that
uses the <command>echo "hello" >/dev/stderr</command> construct for writing messages to stderr, this might
cause problems, as this only works if stderr can be re-opened. To mitigate this use the construct <command>echo
cause problems, as this only works if stderr can be reopened. To mitigate this use the construct <command>echo
"hello" >&amp;2</command> instead, which is mostly equivalent and avoids this pitfall.</para>
<xi:include href="version-info.xml" xpointer="v235"/></listitem>

View file

@ -40,17 +40,17 @@
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
<para>If invoked with no arguments, it applies all directives from all files found in the directories
<para>If invoked with no arguments, directives from the configuration files found in the directories
specified by
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>. When
invoked with positional arguments, if option <option>--replace=<replaceable>PATH</replaceable></option>
is specified, arguments specified on the command line are used instead of the configuration file
<replaceable>PATH</replaceable>. Otherwise, just the configuration specified by the command line
arguments is executed. The string <literal>-</literal> may be specified instead of a filename to instruct
<command>systemd-sysusers</command> to read the configuration from standard input. If the argument is a
relative path, all configuration directories are searched for a matching file and the file found that has
the highest priority is executed. If the argument is an absolute path, that file is used directly without
searching of the configuration directories.</para>
arguments is executed. If the string <literal>-</literal> is specified instead of a filename, the
configuration is read from standard input. If the argument is a file name (without any slashes), all
configuration directories are searched for a matching file and the file found that has the highest
priority is executed. If the argument is a path, that file is used directly without searching the
configuration directories for any other matching file.</para>
</refsect1>
<refsect1>

View file

@ -23,8 +23,7 @@
<refname>systemd-tmpfiles-setup-dev.service</refname>
<refname>systemd-tmpfiles-clean.service</refname>
<refname>systemd-tmpfiles-clean.timer</refname>
<refpurpose>Creates, deletes and cleans up volatile
and temporary files and directories</refpurpose>
<refpurpose>Create, delete, and clean up files and directories</refpurpose>
</refnamediv>
<refsynopsisdiv>
@ -59,17 +58,20 @@
<para><command>systemd-tmpfiles</command> creates, deletes, and cleans up volatile and temporary files
and directories, using the configuration file format and location specified in
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>. It must
be invoked with one or more options <option>--create</option>, <option>--remove</option>, and
be invoked with one or more commands <option>--create</option>, <option>--remove</option>, and
<option>--clean</option>, to select the respective subset of operations.</para>
<para>By default, directives from all configuration files are applied. When invoked with
<option>--replace=<replaceable>PATH</replaceable></option>, arguments specified on the command line are
used instead of the configuration file <replaceable>PATH</replaceable>. Otherwise, if one or more
absolute filenames are passed on the command line, only the directives in these files are applied. If
<literal>-</literal> is specified instead of a filename, directives are read from standard input. If only
the basename of a configuration file is specified, all configuration directories as specified in
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> are
searched for a matching file and the file found that has the highest priority is executed.</para>
<para>If invoked with no arguments, directives from the configuration files found in the directories
specified by
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>. When
invoked with positional arguments, if option <option>--replace=<replaceable>PATH</replaceable></option>
is specified, arguments specified on the command line are used instead of the configuration file
<replaceable>PATH</replaceable>. Otherwise, just the configuration specified by the command line
arguments is executed. If the string <literal>-</literal> is specified instead of a filename, the
configuration is read from standard input. If the argument is a file name (without any slashes), all
configuration directories are searched for a matching file and the file found that has the highest
priority is executed. If the argument is a path, that file is used directly without searching the
configuration directories for any other matching file.</para>
<para>System services (<filename>systemd-tmpfiles-setup.service</filename>,
<filename>systemd-tmpfiles-setup-dev-early.service</filename>,
@ -96,14 +98,14 @@
</refsect1>
<refsect1>
<title>Options</title>
<title>Commands and options</title>
<para>The following options are understood:</para>
<para>The following commands are understood:</para>
<variablelist>
<varlistentry>
<term><option>--create</option></term>
<listitem><para>If this option is passed, all files and
<listitem><para>If this command is passed, all files and
directories marked with
<varname>f</varname>,
<varname>F</varname>,
@ -129,14 +131,14 @@
<varlistentry>
<term><option>--clean</option></term>
<listitem><para>If this option is passed, all files and
<listitem><para>If this command is passed, all files and
directories with an age parameter configured will be cleaned
up.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--remove</option></term>
<listitem><para>If this option is passed, the contents of
<listitem><para>If this command is passed, the contents of
directories marked with <varname>D</varname> or
<varname>R</varname>, and files or directories themselves
marked with <varname>r</varname> or <varname>R</varname> are
@ -179,6 +181,14 @@
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--dry-run</option></term>
<listitem><para>Process the configuration and print what operations would be performed, but don't
actually change anything in the file system.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--prefix=<replaceable>path</replaceable></option></term>
<listitem><para>Only apply rules with paths that start with

View file

@ -3017,7 +3017,7 @@ SystemCallErrorNumber=EPERM</programlisting>
the kernel log buffer, the unit will implicitly gain a dependency of type <varname>After=</varname>
on <filename>systemd-journald.socket</filename> (also see the "Implicit Dependencies" section
above). Also note that in this case stdout (or stderr, see below) will be an
<constant>AF_UNIX</constant> stream socket, and not a pipe or FIFO that can be re-opened. This means
<constant>AF_UNIX</constant> stream socket, and not a pipe or FIFO that can be reopened. This means
when executing shell scripts the construct <command>echo "hello" &gt; /dev/stderr</command> for
writing text to stderr will not work. To mitigate this use the construct <command>echo "hello"
>&amp;2</command> instead, which is mostly equivalent and avoids this pitfall.</para>

View file

@ -353,9 +353,7 @@ L /tmp/foobar - - - - /dev/null</programlisting>
<term><varname>x</varname></term>
<listitem><para>Ignore a path during cleaning. Use this type
to exclude paths from clean-up as controlled with the Age
parameter. Note that lines of this type do not influence the
effect of <varname>r</varname> or <varname>R</varname>
lines. Lines of this type accept shell-style globs in place
parameter. Lines of this type accept shell-style globs in place
of normal path names. </para></listitem>
</varlistentry>
@ -365,9 +363,7 @@ L /tmp/foobar - - - - /dev/null</programlisting>
to exclude paths from clean-up as controlled with the Age
parameter. Unlike <varname>x</varname>, this parameter will
not exclude the content if path is a directory, but only
directory itself. Note that lines of this type do not
influence the effect of <varname>r</varname> or
<varname>R</varname> lines. Lines of this type accept
directory itself. Lines of this type accept
shell-style globs in place of normal path names.
</para>

View file

@ -9,7 +9,10 @@
#include "conf-files.h"
#include "constants.h"
#include "dirent-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "glyph-util.h"
#include "hashmap.h"
#include "log.h"
#include "macro.h"
@ -372,3 +375,97 @@ int conf_files_list_dropins(
return conf_files_list_strv(ret, ".conf", root, 0, (const char* const*) dropin_dirs);
}
/**
* Open and read a config file.
*
* The <fn> argument may be:
* - '-', meaning stdin.
* - a file name without a path. In this case <config_dirs> are searched.
* - a path, either relative or absolute. In this case <fn> is opened directly.
*
* This method is only suitable for configuration files which have a flat layout without dropins.
*/
int conf_file_read(
const char *root,
const char **config_dirs,
const char *fn,
parse_line_t parse_line,
void *userdata,
bool ignore_enoent,
bool *invalid_config) {
_cleanup_fclose_ FILE *_f = NULL;
_cleanup_free_ char *_fn = NULL;
unsigned v = 0;
FILE *f;
int r = 0;
assert(fn);
if (streq(fn, "-")) {
f = stdin;
fn = "<stdin>";
log_debug("Reading config from stdin%s", special_glyph(SPECIAL_GLYPH_ELLIPSIS));
} else if (is_path(fn)) {
r = path_make_absolute_cwd(fn, &_fn);
if (r < 0)
return log_error_errno(r, "Failed to make path absolute: %m");
fn = _fn;
f = _f = fopen(fn, "re");
if (!_f)
r = -errno;
else
log_debug("Reading config file \"%s\"%s", fn, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
} else {
r = search_and_fopen(fn, "re", root, config_dirs, &_f, &_fn);
if (r >= 0) {
f = _f;
fn = _fn;
log_debug("Reading config file \"%s\"%s", fn, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
}
}
if (r == -ENOENT && ignore_enoent) {
log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
return 0; /* No error, but nothing happened. */
}
if (r < 0)
return log_error_errno(r, "Failed to read '%s': %m", fn);
r = 1; /* We entered the part where we may modify state. */
for (;;) {
_cleanup_free_ char *line = NULL;
bool invalid_line = false;
int k;
k = read_stripped_line(f, LONG_LINE_MAX, &line);
if (k < 0)
return log_error_errno(k, "Failed to read '%s': %m", fn);
if (k == 0)
break;
v++;
if (IN_SET(line[0], 0, '#'))
continue;
k = parse_line(fn, v, line, invalid_config ? &invalid_line : NULL, userdata);
if (k < 0 && invalid_line)
/* Allow reporting with a special code if the caller requested this. */
*invalid_config = true;
else
/* The first error, if any, becomes our return value. */
RET_GATHER(r, k);
}
if (ferror(f))
RET_GATHER(r, log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read from file %s.", fn));
return r;
}

View file

@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include "macro.h"
enum {
@ -29,3 +31,19 @@ int conf_files_list_dropins(
const char *dropin_dirname,
const char *root,
const char * const *dirs);
typedef int parse_line_t(
const char *fname,
unsigned line,
const char *buffer,
bool *invalid_config,
void *userdata);
int conf_file_read(
const char *root,
const char **config_dirs,
const char *fn,
parse_line_t parse_line,
void *userdata,
bool ignore_enoent,
bool *invalid_config);

View file

@ -303,7 +303,7 @@ EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf) {
* Some broken firmwares cannot handle large file reads and will instead return
* an error. As a workaround, read such files in small chunks.
* Note that we cannot just try reading the whole file first on such firmware as
* that will permanently break the handle even if it is re-opened.
* that will permanently break the handle even if it is reopened.
*
* https://github.com/systemd/systemd/issues/25911 */

View file

@ -502,7 +502,7 @@ static void test_sequence_numbers_one(void) {
test_close(one);
/* If the machine-id is not initialized, the header file verification
* (which happens when re-opening a journal file) will fail. */
* (which happens when reopening a journal file) will fail. */
if (sd_id128_get_machine(NULL) >= 0) {
/* restart server */
seqnum = 0;

View file

@ -270,7 +270,7 @@ int inhibitor_load(Inhibitor *i) {
if (i->fifo_path) {
_cleanup_close_ int fd = -EBADF;
/* Let's re-open the FIFO on both sides, and close the writing side right away */
/* Let's reopen the FIFO on both sides, and close the writing side right away */
fd = inhibitor_create_fifo(i);
if (fd < 0)
return log_error_errno(fd, "Failed to reopen FIFO: %m");

View file

@ -760,7 +760,7 @@ int pty_forward_new(
else {
/* If we shall be invoked in interactive mode, let's switch on non-blocking mode, so that we
* never end up staving one direction while we block on the other. However, let's be careful
* here and not turn on O_NONBLOCK for stdin/stdout directly, but of re-opened copies of
* here and not turn on O_NONBLOCK for stdin/stdout directly, but of reopened copies of
* them. This has two advantages: when we are killed abruptly the stdin/stdout fds won't be
* left in O_NONBLOCK state for the next process using them. In addition, if some process
* running in the background wants to continue writing to our stdout it can do so without

View file

@ -1680,11 +1680,13 @@ static int item_equivalent(Item *a, Item *b) {
}
static int parse_line(
Context *c,
const char *fname,
unsigned line,
const char *buffer) {
const char *buffer,
bool *invalid_config,
void *context) {
Context *c = ASSERT_PTR(context);
_cleanup_free_ char *action = NULL,
*name = NULL, *resolved_name = NULL,
*id = NULL, *resolved_id = NULL,
@ -1697,10 +1699,10 @@ static int parse_line(
int r;
const char *p;
assert(c);
assert(fname);
assert(line >= 1);
assert(buffer);
assert(!invalid_config); /* We don't support invalid_config yet. */
/* Parse columns */
p = buffer;
@ -1969,57 +1971,14 @@ static int parse_line(
}
static int read_config_file(Context *c, const char *fn, bool ignore_enoent) {
_cleanup_fclose_ FILE *rf = NULL;
_cleanup_free_ char *pp = NULL;
FILE *f = NULL;
unsigned v = 0;
int r = 0;
assert(c);
assert(fn);
if (streq(fn, "-"))
f = stdin;
else {
r = search_and_fopen(fn, "re", arg_root, (const char**) CONF_PATHS_STRV("sysusers.d"), &rf, &pp);
if (r < 0) {
if (ignore_enoent && r == -ENOENT)
return 0;
return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn);
}
f = rf;
fn = pp;
}
for (;;) {
_cleanup_free_ char *line = NULL;
int k;
k = read_stripped_line(f, LONG_LINE_MAX, &line);
if (k < 0)
return log_error_errno(k, "Failed to read '%s': %m", fn);
if (k == 0)
break;
v++;
if (IN_SET(line[0], 0, '#'))
continue;
k = parse_line(c, fn, v, line);
if (k < 0 && r == 0)
r = k;
}
if (ferror(f)) {
log_error_errno(errno, "Failed to read from file %s: %m", fn);
if (r == 0)
r = -EIO;
}
return r;
return conf_file_read(
arg_root,
(const char**) CONF_PATHS_STRV("sysusers.d"),
ASSERT_PTR(fn),
parse_line,
ASSERT_PTR(c),
ignore_enoent,
/* invalid_config= */ NULL);
}
static int cat_config(void) {
@ -2140,10 +2099,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_REPLACE:
if (!path_is_absolute(optarg) ||
!endswith(optarg, ".conf"))
if (!path_is_absolute(optarg))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"The argument to --replace= must an absolute path to a config file");
"The argument to --replace= must be an absolute path.");
if (!endswith(optarg, ".conf"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"The argument to --replace= must have the extension '.conf'.");
arg_replace = optarg;
break;
@ -2191,7 +2152,7 @@ static int parse_arguments(Context *c, char **args) {
STRV_FOREACH(arg, args) {
if (arg_inline)
/* Use (argument):n, where n==1 for the first positional arg */
r = parse_line(c, "(argument)", pos, *arg);
r = parse_line("(argument)", pos, *arg, /* invalid_config= */ NULL, c);
else
r = read_config_file(c, *arg, /* ignore_enoent= */ false);
if (r < 0)

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,14 @@ mkdir /tmp/{C,d,D,e}
mkdir /tmp/d/2
chmod 777 /tmp/d/2
systemd-tmpfiles --dry-run --create - <<EOF
d /tmp/d/1 0755 daemon daemon - -
d /tmp/d/2 0755 daemon daemon - -
EOF
test ! -d /tmp/d/1
test -d /tmp/d/2
systemd-tmpfiles --create - <<EOF
d /tmp/d/1 0755 daemon daemon - -
d /tmp/d/2 0755 daemon daemon - -
@ -104,6 +112,14 @@ chmod 755 /tmp/C/{1,2,3}-origin/f1
mkdir /tmp/C/{2,3}
touch /tmp/C/3/f1
systemd-tmpfiles --dry-run --create - <<EOF
C /tmp/C/1 0755 daemon daemon - /tmp/C/1-origin
C /tmp/C/2 0755 daemon daemon - /tmp/C/2-origin
EOF
test ! -d /tmp/C/1
test -d /tmp/C/2
systemd-tmpfiles --create - <<EOF
C /tmp/C/1 0755 daemon daemon - /tmp/C/1-origin
C /tmp/C/2 0755 daemon daemon - /tmp/C/2-origin

View file

@ -12,6 +12,14 @@ touch /tmp/file-owned-by-root
#
# 'f'
#
systemd-tmpfiles --dry-run --create - <<EOF
f /tmp/f/1 0644 - - - -
f /tmp/f/2 0644 - - - This string should be written
EOF
test ! -e /tmp/f/1
test ! -e /tmp/f/2
systemd-tmpfiles --create - <<EOF
f /tmp/f/1 0644 - - - -
f /tmp/f/2 0644 - - - This string should be written
@ -189,6 +197,11 @@ touch /tmp/w/overwritten
touch /tmp/w/appended
### nop if the target does not exist.
systemd-tmpfiles --dry-run --create - <<EOF
w /tmp/w/unexistent 0644 - - - new content
EOF
test ! -e /tmp/w/unexistent
systemd-tmpfiles --create - <<EOF
w /tmp/w/unexistent 0644 - - - new content
EOF
@ -200,6 +213,12 @@ w /tmp/w/unexistent 0644 - - - -
EOF
### write into an empty file.
systemd-tmpfiles --dry-run --create - <<EOF
w /tmp/w/overwritten 0644 - - - old content
EOF
test -f /tmp/w/overwritten
test -z "$(< /tmp/w/overwritten)"
systemd-tmpfiles --create - <<EOF
w /tmp/w/overwritten 0644 - - - old content
EOF
@ -207,6 +226,12 @@ test -f /tmp/w/overwritten
test "$(< /tmp/w/overwritten)" = "old content"
### old content is overwritten
systemd-tmpfiles --dry-run --create - <<EOF
w /tmp/w/overwritten 0644 - - - new content
EOF
test -f /tmp/w/overwritten
test "$(< /tmp/w/overwritten)" = "old content"
systemd-tmpfiles --create - <<EOF
w /tmp/w/overwritten 0644 - - - new content
EOF
@ -223,6 +248,10 @@ test -f /tmp/w/appended
test "$(< /tmp/w/appended)" = "$(echo -ne '12\n3')"
### writing into an 'exotic' file should be allowed.
systemd-tmpfiles --dry-run --create - <<EOF
w /dev/null - - - - new content
EOF
systemd-tmpfiles --create - <<EOF
w /dev/null - - - - new content
EOF

View file

@ -9,6 +9,12 @@ rm -fr /tmp/p
mkdir /tmp/p
touch /tmp/p/f1
systemd-tmpfiles --dry-run --create - <<EOF
p /tmp/p/fifo1 0666 - - - -
EOF
test ! -p /tmp/p/fifo1
systemd-tmpfiles --create - <<EOF
p /tmp/p/fifo1 0666 - - - -
EOF

View file

@ -12,6 +12,15 @@ mkdir /tmp/{z,Z}
mkdir /tmp/z/d{1,2}
touch /tmp/z/f1 /tmp/z/d1/f11 /tmp/z/d2/f21
systemd-tmpfiles --dry-run --create - <<EOF
z /tmp/z/f1 0755 daemon daemon - -
z /tmp/z/d1 0755 daemon daemon - -
EOF
test "$(stat -c %U /tmp/z/f1)" = "$USER"
test "$(stat -c %U /tmp/z/d1)" = "$USER"
test "$(stat -c %U /tmp/z/d1/f11)" = "$USER"
systemd-tmpfiles --create - <<EOF
z /tmp/z/f1 0755 daemon daemon - -
z /tmp/z/d1 0755 daemon daemon - -

View file

@ -6,7 +6,14 @@ set -eux
set -o pipefail
test_snippet() {
systemd-tmpfiles "$@" - <<EOF
# First call with --dry-run to test the code paths
systemd-tmpfiles --dry-run "$@" - <<EOF
d /var/tmp/foobar-test-06
d /var/tmp/foobar-test-06/important
R /var/tmp/foobar-test-06
EOF
systemd-tmpfiles "$@" - <<EOF
d /var/tmp/foobar-test-06
d /var/tmp/foobar-test-06/important
R /var/tmp/foobar-test-06

View file

@ -12,6 +12,19 @@ mkdir /tmp/x
mkdir -p /tmp/x/{1,2}
touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
systemd-tmpfiles --clean --dry-run - <<EOF
d /tmp/x - - - 0
x /tmp/x/1
EOF
find /tmp/x | sort
test -f /tmp/x/1/x1
test -f /tmp/x/1/x2
test -f /tmp/x/2/y1
test -f /tmp/x/2/y2
test -f /tmp/x/z1
test -f /tmp/x/z2
systemd-tmpfiles --clean - <<EOF
d /tmp/x - - - 0
x /tmp/x/1
@ -34,6 +47,19 @@ test ! -f /tmp/x/z2
mkdir -p /tmp/x/{1,2}
touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
systemd-tmpfiles --dry-run --clean - <<EOF
d /tmp/x - - - 0
X /tmp/x/1
EOF
find /tmp/x | sort
test -f /tmp/x/1/x1
test -f /tmp/x/1/x2
test -f /tmp/x/2/y1
test -f /tmp/x/2/y2
test -f /tmp/x/z1
test -f /tmp/x/z2
systemd-tmpfiles --clean - <<EOF
d /tmp/x - - - 0
X /tmp/x/1
@ -56,6 +82,20 @@ test ! -f /tmp/x/z2
mkdir -p /tmp/x/{1,2}
touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
systemd-tmpfiles --dry-run --clean - <<EOF
d /tmp/x - - - 0
x /tmp/x/[1345]
x /tmp/x/z*
EOF
find /tmp/x | sort
test -f /tmp/x/1/x1
test -f /tmp/x/1/x2
test -f /tmp/x/2/y1
test -f /tmp/x/2/y2
test -f /tmp/x/z1
test -f /tmp/x/z2
systemd-tmpfiles --clean - <<EOF
d /tmp/x - - - 0
x /tmp/x/[1345]
@ -79,6 +119,20 @@ test -f /tmp/x/z2
mkdir -p /tmp/x/{1,2}
touch /tmp/x/1/{x1,x2} /tmp/x/2/{y1,y2} /tmp/x/{z1,z2}
systemd-tmpfiles --dry-run --clean - <<EOF
d /tmp/x - - - 0
X /tmp/x/[1345]
X /tmp/x/?[12]
EOF
find /tmp/x | sort
test -f /tmp/x/1/x1
test -f /tmp/x/1/x2
test -f /tmp/x/2/y1
test -f /tmp/x/2/y2
test -f /tmp/x/z1
test -f /tmp/x/z2
systemd-tmpfiles --clean - <<EOF
d /tmp/x - - - 0
X /tmp/x/[1345]
@ -102,6 +156,19 @@ test -f /tmp/x/z2
mkdir -p /tmp/x/{1,2}/a
touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
systemd-tmpfiles --dry-run --clean - <<EOF
# x/X is not supposed to influence r
x /tmp/x/1/a
X /tmp/x/2/a
r /tmp/x/1
r /tmp/x/2
EOF
test -f /tmp/x/1/a/x1
test -f /tmp/x/1/a/x2
test -f /tmp/x/2/a/y1
test -f /tmp/x/2/a/y2
systemd-tmpfiles --clean - <<EOF
# x/X is not supposed to influence r
x /tmp/x/1/a
@ -125,17 +192,58 @@ test -f /tmp/x/2/a/y2
mkdir -p /tmp/x/{1,2}/a
touch /tmp/x/1/a/{x1,x2} /tmp/x/2/a/{y1,y2}
systemd-tmpfiles --dry-run --remove - <<EOF
# Check that X is honoured below R
X /tmp/x/1/a
X /tmp/x/2/a
R /tmp/x/1
EOF
test -f /tmp/x/1/a/x1
test -f /tmp/x/1/a/x2
test -f /tmp/x/2/a/y1
test -f /tmp/x/2/a/y2
systemd-tmpfiles --remove - <<EOF
# X is not supposed to influence R
# Check that X is honoured below R
X /tmp/x/1/a
X /tmp/x/2/a
R /tmp/x/1
EOF
find /tmp/x | sort
test ! -d /tmp/x/1
test ! -d /tmp/x/1/a
test ! -f /tmp/x/1/a/x1
test ! -f /tmp/x/1/a/x2
test -d /tmp/x/1
test -d /tmp/x/1/a
test -f /tmp/x/1/a/x1
test -f /tmp/x/1/a/x2
test -f /tmp/x/2/a/y1
test -f /tmp/x/2/a/y2
#
# 'r/R/D' and non-directories
#
touch /tmp/x/{11,22,33}
systemd-tmpfiles --dry-run --remove - <<EOF
# Check that X is honoured below R
r /tmp/x/11
R /tmp/x/22
D /tmp/x/33
EOF
test -f /tmp/x/11
test -f /tmp/x/22
test -f /tmp/x/33
systemd-tmpfiles --remove - <<EOF
# Check that X is honoured below R
r /tmp/x/11
R /tmp/x/22
D /tmp/x/33
EOF
find /tmp/x | sort
test ! -f /tmp/x/11
test ! -f /tmp/x/22
test -f /tmp/x/33

View file

@ -52,6 +52,16 @@ sleep 1
touch /tmp/ageby/d{3,4}/f{2..4}
# Check for cleanup of "f1" in each of "/tmp/d{1..4}".
systemd-tmpfiles --dry-run --clean - <<-EOF
d /tmp/ageby/d1 - - - a:1m -
e /tmp/ageby/d2 - - - m:3m -
D /tmp/ageby/d3 - - - c:2s -
EOF
for d in d{1..3}; do
test -f "/tmp/ageby/${d}/f1"
done
systemd-tmpfiles --clean - <<-EOF
d /tmp/ageby/d1 - - - a:1m -
e /tmp/ageby/d2 - - - m:3m -

View file

@ -12,6 +12,12 @@ mkdir /tmp/L
home='/somewhere'
dst='/tmp/L/1'
src="$home"
HOME="$home" \
systemd-tmpfiles --dry-run --create - <<EOF
L $dst - - - - %h
EOF
test ! -h "$dst"
HOME="$home" \
systemd-tmpfiles --create - <<EOF
L $dst - - - - %h
@ -25,6 +31,12 @@ home='/%U'
src="/usr/share/factory$home"
mkdir -p "$root$src"
dst="$root$home"
HOME="$home" \
systemd-tmpfiles --create --dry-run --root="$root" - <<EOF
L %h - - - -
EOF
test ! -h "$dst"
HOME="$home" \
systemd-tmpfiles --create --root="$root" - <<EOF
L %h - - - -

View file

@ -12,6 +12,11 @@ rm -f /tmp/acl_exec
touch /tmp/acl_exec
# No ACL set yet
systemd-tmpfiles --dry-run --create - <<EOF
a /tmp/acl_exec - - - - u:root:rwX
EOF
assert_not_in 'user:root:rw-' "$(getfacl -Ec /tmp/acl_exec)"
systemd-tmpfiles --create - <<EOF
a /tmp/acl_exec - - - - u:root:rwX
EOF

View file

@ -8,26 +8,27 @@ set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
systemd-tmpfiles --create - <<EOF
c='
d /tmp/somedir
f /tmp/somedir/somefile - - - - baz
EOF
'
systemd-tmpfiles --create - <<<"$c"
test -f /tmp/somedir/somefile
grep -q baz /tmp/somedir/somefile
systemd-tmpfiles --purge - <<EOF
d /tmp/somedir
f /tmp/somedir/somefile - - - - baz
EOF
systemd-tmpfiles --purge --dry-run - <<<"$c"
test -f /tmp/somedir/somefile
grep -q baz /tmp/somedir/somefile
systemd-tmpfiles --purge - <<<"$c"
test ! -f /tmp/somedir/somefile
test ! -d /tmp/somedir/
systemd-tmpfiles --create --purge - <<EOF
d /tmp/somedir
f /tmp/somedir/somefile - - - - baz
EOF
systemd-tmpfiles --create --purge --dry-run - <<<"$c"
test ! -f /tmp/somedir/somefile
test ! -d /tmp/somedir/
systemd-tmpfiles --create --purge - <<<"$c"
test -f /tmp/somedir/somefile
grep -q baz /tmp/somedir/somefile

29
test/units/testsuite-22.19.sh Executable file
View file

@ -0,0 +1,29 @@
#!/bin/bash
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# Tests for character and block device creation
#
set -eux
set -o pipefail
rm -rf /tmp/dev
mkdir /tmp/dev
# We are running tests in /tmp, which would normally be mounted nodev,
# so we only try with --dry-run.
systemd-tmpfiles --dry-run --create - <<EOF
c /tmp/dev/char - - - - 11:12
b /tmp/dev/block - - - - 11:14
EOF
test ! -e /tmp/dev/char
test ! -e /tmp/dev/block
systemd-tmpfiles --dry-run --create - <<EOF
c+ /tmp/dev/char - - - - 11:12
b+ /tmp/dev/block - - - - 11:14
EOF
test ! -e /tmp/dev/char
test ! -e /tmp/dev/block

View file

@ -169,9 +169,9 @@ systemd-nspawn.xml /refsect1[title="Options"]/refsect2[title="Execution Options"
systemd-nspawn.xml /refsect1[title="Options"]/refsect2[title="System Identity Options"]/variablelist/varlistentry[term="--uuid="]
systemd-nspawn.xml /refsect1[title="Options"]/refsect2[title="Networking Options"]/variablelist/varlistentry[term="--private-network"]
systemd-nspawn.xml /refsect1[title="Options"]/refsect2[title="Image Options"]/variablelist/varlistentry[term="--read-only"]
systemd-tmpfiles.xml /refsect1[title="Options"]/variablelist/varlistentry[term="--create"]
systemd-tmpfiles.xml /refsect1[title="Options"]/variablelist/varlistentry[term="--clean"]
systemd-tmpfiles.xml /refsect1[title="Options"]/variablelist/varlistentry[term="--remove"]
systemd-tmpfiles.xml /refsect1[title="Commands and options"]/variablelist/varlistentry[term="--create"]
systemd-tmpfiles.xml /refsect1[title="Commands and options"]/variablelist/varlistentry[term="--clean"]
systemd-tmpfiles.xml /refsect1[title="Commands and options"]/variablelist/varlistentry[term="--remove"]
systemd-tmpfiles.xml /refsect1[title="Options"]/variablelist/varlistentry[term="--prefix=path"]
systemd.automount.xml /refsect1[title="Options"]/variablelist/varlistentry[term="Where="]
systemd.automount.xml /refsect1[title="Options"]/variablelist/varlistentry[term="DirectoryMode="]