Merge pull request #10538 from poettering/tmpfiles-reorder
tmpfiles: remove children before their parents plus other fixlets
This commit is contained in:
commit
606b0b64a7
|
@ -176,14 +176,12 @@
|
||||||
<xi:include href="standard-options.xml" xpointer="version" />
|
<xi:include href="standard-options.xml" xpointer="version" />
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
<para>It is possible to combine <option>--create</option>,
|
<para>It is possible to combine <option>--create</option>, <option>--clean</option>, and <option>--remove</option>
|
||||||
<option>--clean</option>, and <option>--remove</option> in one
|
in one invocation (in which case removal and clean-up are executed before creation of new files). For example,
|
||||||
invocation. For example, during boot the following command line is
|
during boot the following command line is executed to ensure that all temporary and volatile directories are
|
||||||
executed to ensure that all temporary and volatile directories are
|
|
||||||
removed and created according to the configuration file:</para>
|
removed and created according to the configuration file:</para>
|
||||||
|
|
||||||
<programlisting>systemd-tmpfiles --remove --create</programlisting>
|
<programlisting>systemd-tmpfiles --remove --create</programlisting>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
|
|
@ -46,12 +46,9 @@
|
||||||
directories which usually reside in directories such as
|
directories which usually reside in directories such as
|
||||||
<filename>/run</filename> or <filename>/tmp</filename>.</para>
|
<filename>/run</filename> or <filename>/tmp</filename>.</para>
|
||||||
|
|
||||||
<para>Volatile and temporary files and directories are those
|
<para>Volatile and temporary files and directories are those located in <filename>/run</filename>,
|
||||||
located in <filename>/run</filename> (and its alias
|
<filename>/tmp</filename>, <filename>/var/tmp</filename>, the API file systems such as <filename>/sys</filename> or
|
||||||
<filename>/var/run</filename>), <filename>/tmp</filename>,
|
<filename>/proc</filename>, as well as some other directories below <filename>/var</filename>.</para>
|
||||||
<filename>/var/tmp</filename>, the API file systems such as
|
|
||||||
<filename>/sys</filename> or <filename>/proc</filename>, as well
|
|
||||||
as some other directories below <filename>/var</filename>.</para>
|
|
||||||
|
|
||||||
<para>System daemons frequently require private runtime
|
<para>System daemons frequently require private runtime
|
||||||
directories below <filename>/run</filename> to place communication
|
directories below <filename>/run</filename> to place communication
|
||||||
|
@ -70,28 +67,20 @@
|
||||||
The second variant should be used when it is desirable to make it
|
The second variant should be used when it is desirable to make it
|
||||||
easy to override just this part of configuration.</para>
|
easy to override just this part of configuration.</para>
|
||||||
|
|
||||||
<para>Files in <filename>/etc/tmpfiles.d</filename> override files
|
<para>Files in <filename>/etc/tmpfiles.d</filename> override files with the same name in
|
||||||
with the same name in <filename>/usr/lib/tmpfiles.d</filename> and
|
<filename>/usr/lib/tmpfiles.d</filename> and <filename>/run/tmpfiles.d</filename>. Files in
|
||||||
<filename>/run/tmpfiles.d</filename>. Files in
|
<filename>/run/tmpfiles.d</filename> override files with the same name in
|
||||||
<filename>/run/tmpfiles.d</filename> override files with the same
|
<filename>/usr/lib/tmpfiles.d</filename>. Packages should install their configuration files in
|
||||||
name in <filename>/usr/lib/tmpfiles.d</filename>. Packages should
|
<filename>/usr/lib/tmpfiles.d</filename>. Files in <filename>/etc/tmpfiles.d</filename> are reserved for the local
|
||||||
install their configuration files in
|
administrator, who may use this logic to override the configuration files installed by vendor packages. All
|
||||||
<filename>/usr/lib/tmpfiles.d</filename>. Files in
|
configuration files are sorted by their filename in lexicographic order, regardless of which of the directories
|
||||||
<filename>/etc/tmpfiles.d</filename> are reserved for the local
|
they reside in. If multiple files specify the same path, the entry in the file with the lexicographically earliest
|
||||||
administrator, who may use this logic to override the
|
name will be applied. All other conflicting entries will be logged as errors. When two lines are prefix path and
|
||||||
configuration files installed by vendor packages. All
|
suffix path of each other, then the prefix line is always created first, the suffix later (and if removal applies
|
||||||
configuration files are sorted by their filename in lexicographic
|
to the line, the order is reversed: the suffix is removed first, the prefix later). Lines that take globs are
|
||||||
order, regardless of which of the directories they reside in. If
|
applied after those accepting no globs. If multiple operations shall be applied on the same file, (such as ACL,
|
||||||
multiple files specify the same path, the entry in the file with
|
xattr, file attribute adjustments), these are always done in the same fixed order. Otherwise, the files/directories
|
||||||
the lexicographically earliest name will be applied. All other
|
are processed in the order they are listed.</para>
|
||||||
conflicting entries will be logged as errors. When two lines are
|
|
||||||
prefix and suffix of each other, then the prefix is always
|
|
||||||
processed first, the suffix later. Lines that take globs are
|
|
||||||
applied after those accepting no globs. If multiple operations
|
|
||||||
shall be applied on the same file, (such as ACL, xattr, file
|
|
||||||
attribute adjustments), these are always done in the same fixed
|
|
||||||
order. Otherwise, the files/directories are processed in the order
|
|
||||||
they are listed.</para>
|
|
||||||
|
|
||||||
<para>If the administrator wants to disable a configuration file
|
<para>If the administrator wants to disable a configuration file
|
||||||
supplied by the vendor, the recommended way is to place a symlink
|
supplied by the vendor, the recommended way is to place a symlink
|
||||||
|
|
|
@ -1178,6 +1178,18 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
|
||||||
if (subvol_fd < 0)
|
if (subvol_fd < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
|
/* Let's check if this is actually a subvolume. Note that this is mostly redundant, as BTRFS_IOC_SNAP_DESTROY
|
||||||
|
* would fail anyway if it is not. However, it's a good thing to check this ahead of time so that we can return
|
||||||
|
* ENOTTY unconditionally in this case. This is different from the ioctl() which will return EPERM/EACCES if we
|
||||||
|
* don't have the privileges to remove subvolumes, regardless if the specified directory is actually a
|
||||||
|
* subvolume or not. In order to make it easy for callers to cover the "this is not a btrfs subvolume" case
|
||||||
|
* let's prefer ENOTTY over EPERM/EACCES though. */
|
||||||
|
r = btrfs_is_subvol_fd(subvol_fd);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0) /* Not a btrfs subvolume */
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
if (subvol_id == 0) {
|
if (subvol_id == 0) {
|
||||||
r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
|
r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -65,6 +65,12 @@
|
||||||
* properly owned directories beneath /tmp, /var/tmp, /run, which are
|
* properly owned directories beneath /tmp, /var/tmp, /run, which are
|
||||||
* volatile and hence need to be recreated on bootup. */
|
* volatile and hence need to be recreated on bootup. */
|
||||||
|
|
||||||
|
typedef enum OperationMask {
|
||||||
|
OPERATION_CREATE = 1 << 0,
|
||||||
|
OPERATION_REMOVE = 1 << 1,
|
||||||
|
OPERATION_CLEAN = 1 << 2,
|
||||||
|
} OperationMask;
|
||||||
|
|
||||||
typedef enum ItemType {
|
typedef enum ItemType {
|
||||||
/* These ones take file names */
|
/* These ones take file names */
|
||||||
CREATE_FILE = 'f',
|
CREATE_FILE = 'f',
|
||||||
|
@ -130,17 +136,20 @@ typedef struct Item {
|
||||||
|
|
||||||
bool allow_failure:1;
|
bool allow_failure:1;
|
||||||
|
|
||||||
bool done:1;
|
OperationMask done;
|
||||||
} Item;
|
} Item;
|
||||||
|
|
||||||
typedef struct ItemArray {
|
typedef struct ItemArray {
|
||||||
Item *items;
|
Item *items;
|
||||||
size_t count;
|
size_t n_items;
|
||||||
size_t size;
|
size_t allocated;
|
||||||
|
|
||||||
|
struct ItemArray *parent;
|
||||||
|
Set *children;
|
||||||
} ItemArray;
|
} ItemArray;
|
||||||
|
|
||||||
typedef enum DirectoryType {
|
typedef enum DirectoryType {
|
||||||
DIRECTORY_RUNTIME = 0,
|
DIRECTORY_RUNTIME,
|
||||||
DIRECTORY_STATE,
|
DIRECTORY_STATE,
|
||||||
DIRECTORY_CACHE,
|
DIRECTORY_CACHE,
|
||||||
DIRECTORY_LOGS,
|
DIRECTORY_LOGS,
|
||||||
|
@ -149,9 +158,7 @@ typedef enum DirectoryType {
|
||||||
|
|
||||||
static bool arg_cat_config = false;
|
static bool arg_cat_config = false;
|
||||||
static bool arg_user = false;
|
static bool arg_user = false;
|
||||||
static bool arg_create = false;
|
static OperationMask arg_operation = 0;
|
||||||
static bool arg_clean = false;
|
|
||||||
static bool arg_remove = false;
|
|
||||||
static bool arg_boot = false;
|
static bool arg_boot = false;
|
||||||
static PagerFlags arg_pager_flags = 0;
|
static PagerFlags arg_pager_flags = 0;
|
||||||
|
|
||||||
|
@ -352,9 +359,9 @@ static struct Item* find_glob(OrderedHashmap *h, const char *match) {
|
||||||
Iterator i;
|
Iterator i;
|
||||||
|
|
||||||
ORDERED_HASHMAP_FOREACH(j, h, i) {
|
ORDERED_HASHMAP_FOREACH(j, h, i) {
|
||||||
unsigned n;
|
size_t n;
|
||||||
|
|
||||||
for (n = 0; n < j->count; n++) {
|
for (n = 0; n < j->n_items; n++) {
|
||||||
Item *item = j->items + n;
|
Item *item = j->items + n;
|
||||||
|
|
||||||
if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
|
if (fnmatch(item->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
|
||||||
|
@ -2131,12 +2138,10 @@ static int remove_item_instance(Item *i, const char *instance) {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TRUNCATE_DIRECTORY:
|
|
||||||
case RECURSIVE_REMOVE_PATH:
|
case RECURSIVE_REMOVE_PATH:
|
||||||
/* FIXME: we probably should use dir_cleanup() here
|
/* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
|
||||||
* instead of rm_rf() so that 'x' is honoured. */
|
|
||||||
log_debug("rm -rf \"%s\"", instance);
|
log_debug("rm -rf \"%s\"", instance);
|
||||||
r = rm_rf(instance, (i->type == RECURSIVE_REMOVE_PATH ? REMOVE_ROOT|REMOVE_SUBVOLUME : 0) | REMOVE_PHYSICAL);
|
r = rm_rf(instance, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
|
||||||
if (r < 0 && r != -ENOENT)
|
if (r < 0 && r != -ENOENT)
|
||||||
return log_error_errno(r, "rm_rf(%s): %m", instance);
|
return log_error_errno(r, "rm_rf(%s): %m", instance);
|
||||||
|
|
||||||
|
@ -2150,14 +2155,24 @@ static int remove_item_instance(Item *i, const char *instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int remove_item(Item *i) {
|
static int remove_item(Item *i) {
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
|
|
||||||
log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
|
log_debug("Running remove action for entry %c %s", (char) i->type, i->path);
|
||||||
|
|
||||||
switch (i->type) {
|
switch (i->type) {
|
||||||
|
|
||||||
case REMOVE_PATH:
|
|
||||||
case TRUNCATE_DIRECTORY:
|
case TRUNCATE_DIRECTORY:
|
||||||
|
/* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
|
||||||
|
log_debug("rm -rf \"%s\"", i->path);
|
||||||
|
r = rm_rf(i->path, REMOVE_PHYSICAL);
|
||||||
|
if (r < 0 && r != -ENOENT)
|
||||||
|
return log_error_errno(r, "rm_rf(%s): %m", i->path);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case REMOVE_PATH:
|
||||||
case RECURSIVE_REMOVE_PATH:
|
case RECURSIVE_REMOVE_PATH:
|
||||||
return glob_item(i, remove_item_instance);
|
return glob_item(i, remove_item_instance);
|
||||||
|
|
||||||
|
@ -2239,60 +2254,66 @@ static int clean_item(Item *i) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int process_item_array(ItemArray *array);
|
static int process_item(Item *i, OperationMask operation) {
|
||||||
|
OperationMask todo;
|
||||||
static int process_item(Item *i) {
|
int r, q, p;
|
||||||
int r, q, p, t = 0;
|
|
||||||
_cleanup_free_ char *prefix = NULL;
|
|
||||||
|
|
||||||
assert(i);
|
assert(i);
|
||||||
|
|
||||||
if (i->done)
|
todo = operation & ~i->done;
|
||||||
|
if (todo == 0) /* Everything already done? */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
i->done = true;
|
i->done |= operation;
|
||||||
|
|
||||||
prefix = malloc(strlen(i->path) + 1);
|
r = chase_symlinks(i->path, NULL, CHASE_NO_AUTOFS, NULL);
|
||||||
if (!prefix)
|
if (r == -EREMOTE) {
|
||||||
return log_oom();
|
log_debug_errno(r, "Item '%s' is behind autofs, skipping.", i->path);
|
||||||
|
return 0;
|
||||||
|
} else if (r < 0)
|
||||||
|
log_debug_errno(r, "Failed to determine whether '%s' is behind autofs, ignoring: %m", i->path);
|
||||||
|
|
||||||
PATH_FOREACH_PREFIX(prefix, i->path) {
|
r = FLAGS_SET(operation, OPERATION_CREATE) ? create_item(i) : 0;
|
||||||
ItemArray *j;
|
|
||||||
|
|
||||||
j = ordered_hashmap_get(items, prefix);
|
|
||||||
if (j) {
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s = process_item_array(j);
|
|
||||||
if (s < 0 && t == 0)
|
|
||||||
t = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chase_symlinks(i->path, NULL, CHASE_NO_AUTOFS, NULL) == -EREMOTE)
|
|
||||||
return t;
|
|
||||||
|
|
||||||
r = arg_create ? create_item(i) : 0;
|
|
||||||
q = arg_remove ? remove_item(i) : 0;
|
|
||||||
p = arg_clean ? clean_item(i) : 0;
|
|
||||||
/* Failure can only be tolerated for create */
|
/* Failure can only be tolerated for create */
|
||||||
if (i->allow_failure)
|
if (i->allow_failure)
|
||||||
r = 0;
|
r = 0;
|
||||||
|
|
||||||
return t < 0 ? t :
|
q = FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(i) : 0;
|
||||||
r < 0 ? r :
|
p = FLAGS_SET(operation, OPERATION_CLEAN) ? clean_item(i) : 0;
|
||||||
|
|
||||||
|
return r < 0 ? r :
|
||||||
q < 0 ? q :
|
q < 0 ? q :
|
||||||
p;
|
p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int process_item_array(ItemArray *array) {
|
static int process_item_array(ItemArray *array, OperationMask operation) {
|
||||||
unsigned n;
|
int r = 0;
|
||||||
int r = 0, k;
|
size_t n;
|
||||||
|
|
||||||
assert(array);
|
assert(array);
|
||||||
|
|
||||||
for (n = 0; n < array->count; n++) {
|
/* Create any parent first. */
|
||||||
k = process_item(array->items + n);
|
if (FLAGS_SET(operation, OPERATION_CREATE) && array->parent)
|
||||||
|
r = process_item_array(array->parent, operation & OPERATION_CREATE);
|
||||||
|
|
||||||
|
/* Clean up all children first */
|
||||||
|
if ((operation & (OPERATION_REMOVE|OPERATION_CLEAN)) && !set_isempty(array->children)) {
|
||||||
|
Iterator i;
|
||||||
|
ItemArray *c;
|
||||||
|
|
||||||
|
SET_FOREACH(c, array->children, i) {
|
||||||
|
int k;
|
||||||
|
|
||||||
|
k = process_item_array(c, operation & (OPERATION_REMOVE|OPERATION_CLEAN));
|
||||||
|
if (k < 0 && r == 0)
|
||||||
|
r = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < array->n_items; n++) {
|
||||||
|
int k;
|
||||||
|
|
||||||
|
k = process_item(array->items + n, operation);
|
||||||
if (k < 0 && r == 0)
|
if (k < 0 && r == 0)
|
||||||
r = k;
|
r = k;
|
||||||
}
|
}
|
||||||
|
@ -2313,13 +2334,15 @@ static void item_free_contents(Item *i) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void item_array_free(ItemArray *a) {
|
static void item_array_free(ItemArray *a) {
|
||||||
unsigned n;
|
size_t n;
|
||||||
|
|
||||||
if (!a)
|
if (!a)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (n = 0; n < a->count; n++)
|
for (n = 0; n < a->n_items; n++)
|
||||||
item_free_contents(a->items + n);
|
item_free_contents(a->items + n);
|
||||||
|
|
||||||
|
set_free(a->children);
|
||||||
free(a->items);
|
free(a->items);
|
||||||
free(a);
|
free(a);
|
||||||
}
|
}
|
||||||
|
@ -2468,8 +2491,7 @@ static int patch_var_run(const char *fname, unsigned line, char **path) {
|
||||||
|
|
||||||
log_notice("[%s:%u] Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", fname, line, *path, n);
|
log_notice("[%s:%u] Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", fname, line, *path, n);
|
||||||
|
|
||||||
free(*path);
|
free_and_replace(*path, n);
|
||||||
*path = n;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2706,8 +2728,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
|
||||||
if (!p)
|
if (!p)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
free(i.path);
|
free_and_replace(i.path, p);
|
||||||
i.path = p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isempty(user) && !streq(user, "-")) {
|
if (!isempty(user) && !streq(user, "-")) {
|
||||||
|
@ -2776,9 +2797,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
|
||||||
|
|
||||||
existing = ordered_hashmap_get(h, i.path);
|
existing = ordered_hashmap_get(h, i.path);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
unsigned n;
|
size_t n;
|
||||||
|
|
||||||
for (n = 0; n < existing->count; n++) {
|
for (n = 0; n < existing->n_items; n++) {
|
||||||
if (!item_compatible(existing->items + n, &i)) {
|
if (!item_compatible(existing->items + n, &i)) {
|
||||||
log_notice("[%s:%u] Duplicate line for path \"%s\", ignoring.",
|
log_notice("[%s:%u] Duplicate line for path \"%s\", ignoring.",
|
||||||
fname, line, i.path);
|
fname, line, i.path);
|
||||||
|
@ -2791,19 +2812,21 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
r = ordered_hashmap_put(h, i.path, existing);
|
r = ordered_hashmap_put(h, i.path, existing);
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
|
free(existing);
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GREEDY_REALLOC(existing->items, existing->size, existing->count + 1))
|
if (!GREEDY_REALLOC(existing->items, existing->allocated, existing->n_items + 1))
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
memcpy(existing->items + existing->count++, &i, sizeof(i));
|
existing->items[existing->n_items++] = i;
|
||||||
|
i = (struct Item) {};
|
||||||
|
|
||||||
/* Sort item array, to enforce stable ordering of application */
|
/* Sort item array, to enforce stable ordering of application */
|
||||||
typesafe_qsort(existing->items, existing->count, item_compare);
|
typesafe_qsort(existing->items, existing->n_items, item_compare);
|
||||||
|
|
||||||
zero(i);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2907,15 +2930,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_CREATE:
|
case ARG_CREATE:
|
||||||
arg_create = true;
|
arg_operation |= OPERATION_CREATE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_CLEAN:
|
case ARG_CLEAN:
|
||||||
arg_clean = true;
|
arg_operation |= OPERATION_CLEAN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_REMOVE:
|
case ARG_REMOVE:
|
||||||
arg_remove = true;
|
arg_operation |= OPERATION_REMOVE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_BOOT:
|
case ARG_BOOT:
|
||||||
|
@ -2959,7 +2982,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
assert_not_reached("Unhandled option");
|
assert_not_reached("Unhandled option");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!arg_clean && !arg_create && !arg_remove && !arg_cat_config) {
|
if (arg_operation == 0 && !arg_cat_config) {
|
||||||
log_error("You need to specify at least one of --clean, --create or --remove.");
|
log_error("You need to specify at least one of --clean, --create or --remove.");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -3109,12 +3132,49 @@ static int read_config_files(char **config_dirs, char **args, bool *invalid_conf
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int link_parent(ItemArray *a) {
|
||||||
|
const char *path;
|
||||||
|
char *prefix;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(a);
|
||||||
|
|
||||||
|
/* Finds the closestq "parent" item array for the specified item array. Then registers the specified item array
|
||||||
|
* as child of it, and fills the parent in, linking them both ways. This allows us to later create parents
|
||||||
|
* before their children, and clean up/remove children before their parents. */
|
||||||
|
|
||||||
|
if (a->n_items <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
path = a->items[0].path;
|
||||||
|
prefix = alloca(strlen(path) + 1);
|
||||||
|
PATH_FOREACH_PREFIX(prefix, path) {
|
||||||
|
ItemArray *j;
|
||||||
|
|
||||||
|
j = ordered_hashmap_get(items, prefix);
|
||||||
|
if (j) {
|
||||||
|
r = set_ensure_allocated(&j->children, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = set_put(j->children, a);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
a->parent = j;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int r, k, r_process = 0;
|
|
||||||
ItemArray *a;
|
|
||||||
Iterator iterator;
|
|
||||||
_cleanup_strv_free_ char **config_dirs = NULL;
|
_cleanup_strv_free_ char **config_dirs = NULL;
|
||||||
|
int r, k, r_process = 0, phase;
|
||||||
bool invalid_config = false;
|
bool invalid_config = false;
|
||||||
|
Iterator iterator;
|
||||||
|
ItemArray *a;
|
||||||
|
|
||||||
r = parse_argv(argc, argv);
|
r = parse_argv(argc, argv);
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
|
@ -3178,20 +3238,46 @@ int main(int argc, char *argv[]) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
/* The non-globbing ones usually create things, hence we apply
|
/* Let's now link up all child/parent relationships */
|
||||||
* them first */
|
|
||||||
ORDERED_HASHMAP_FOREACH(a, items, iterator) {
|
ORDERED_HASHMAP_FOREACH(a, items, iterator) {
|
||||||
k = process_item_array(a);
|
r = link_parent(a);
|
||||||
if (k < 0 && r_process == 0)
|
if (r < 0)
|
||||||
r_process = k;
|
goto finish;
|
||||||
|
}
|
||||||
|
ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
|
||||||
|
r = link_parent(a);
|
||||||
|
if (r < 0)
|
||||||
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The globbing ones usually alter things, hence we apply them
|
/* If multiple operations are requested, let's first run the remove/clean operations, and only then the create
|
||||||
* second. */
|
* operations. i.e. that we first clean out the platform we then build on. */
|
||||||
ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
|
for (phase = 0; phase < 2; phase++) {
|
||||||
k = process_item_array(a);
|
OperationMask op;
|
||||||
if (k < 0 && r_process == 0)
|
|
||||||
r_process = k;
|
if (phase == 0)
|
||||||
|
op = arg_operation & (OPERATION_REMOVE|OPERATION_CLEAN);
|
||||||
|
else if (phase == 1)
|
||||||
|
op = arg_operation & OPERATION_CREATE;
|
||||||
|
else
|
||||||
|
assert_not_reached("unexpected phase");
|
||||||
|
|
||||||
|
if (op == 0) /* Nothing requested in this phase */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* The non-globbing ones usually create things, hence we apply them first */
|
||||||
|
ORDERED_HASHMAP_FOREACH(a, items, iterator) {
|
||||||
|
k = process_item_array(a, op);
|
||||||
|
if (k < 0 && r_process == 0)
|
||||||
|
r_process = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The globbing ones usually alter things, hence we apply them second. */
|
||||||
|
ORDERED_HASHMAP_FOREACH(a, globs, iterator) {
|
||||||
|
k = process_item_array(a, op);
|
||||||
|
if (k < 0 && r_process == 0)
|
||||||
|
r_process = k;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
|
|
38
test/TEST-22-TMPFILES/test-06.sh
Executable file
38
test/TEST-22-TMPFILES/test-06.sh
Executable file
|
@ -0,0 +1,38 @@
|
||||||
|
#! /bin/bash
|
||||||
|
#
|
||||||
|
# Inspired by https://github.com/systemd/systemd/issues/9508
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
test_snippet() {
|
||||||
|
systemd-tmpfiles "$@" - <<EOF
|
||||||
|
d /var/tmp/foobar-test-06
|
||||||
|
d /var/tmp/foobar-test-06/important
|
||||||
|
R /var/tmp/foobar-test-06
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
test_snippet --create --remove
|
||||||
|
test -d /var/tmp/foobar-test-06
|
||||||
|
test -d /var/tmp/foobar-test-06/important
|
||||||
|
|
||||||
|
test_snippet --remove
|
||||||
|
! test -f /var/tmp/foobar-test-06
|
||||||
|
! test -f /var/tmp/foobar-test-06/important
|
||||||
|
|
||||||
|
test_snippet --create
|
||||||
|
test -d /var/tmp/foobar-test-06
|
||||||
|
test -d /var/tmp/foobar-test-06/important
|
||||||
|
|
||||||
|
touch /var/tmp/foobar-test-06/something-else
|
||||||
|
|
||||||
|
test_snippet --create
|
||||||
|
test -d /var/tmp/foobar-test-06
|
||||||
|
test -d /var/tmp/foobar-test-06/important
|
||||||
|
test -f /var/tmp/foobar-test-06/something-else
|
||||||
|
|
||||||
|
test_snippet --create --remove
|
||||||
|
test -d /var/tmp/foobar-test-06
|
||||||
|
test -d /var/tmp/foobar-test-06/important
|
||||||
|
! test -f /var/tmp/foobar-test-06/something-else
|
Loading…
Reference in a new issue