mirror of
https://github.com/systemd/systemd
synced 2024-10-07 00:30:59 +00:00
tmpfiles: add --purge switch
Any file/directory created by a tmpfiles.d will be deleted. Useful for purge/factory reset patterns.
This commit is contained in:
parent
115d6abf87
commit
81a183800f
|
@ -145,6 +145,14 @@
|
|||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--purge</option></term>
|
||||
<listitem><para>If this option is passed, all files and directories created by a
|
||||
<filename>tmpfiles.d/</filename> entry will be deleted.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--user</option></term>
|
||||
<listitem><para>Execute "user" configuration, i.e. <filename>tmpfiles.d</filename>
|
||||
|
|
|
@ -82,6 +82,7 @@ typedef enum OperationMask {
|
|||
OPERATION_CREATE = 1 << 0,
|
||||
OPERATION_REMOVE = 1 << 1,
|
||||
OPERATION_CLEAN = 1 << 2,
|
||||
OPERATION_PURGE = 1 << 3,
|
||||
} OperationMask;
|
||||
|
||||
typedef enum ItemType {
|
||||
|
@ -378,6 +379,24 @@ static int user_config_paths(char*** ret) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool needs_purge(ItemType t) {
|
||||
return IN_SET(t,
|
||||
COPY_FILES,
|
||||
TRUNCATE_FILE,
|
||||
CREATE_FILE,
|
||||
WRITE_FILE,
|
||||
EMPTY_DIRECTORY,
|
||||
CREATE_SUBVOLUME,
|
||||
CREATE_SUBVOLUME_INHERIT_QUOTA,
|
||||
CREATE_SUBVOLUME_NEW_QUOTA,
|
||||
CREATE_CHAR_DEVICE,
|
||||
CREATE_BLOCK_DEVICE,
|
||||
CREATE_SYMLINK,
|
||||
CREATE_FIFO,
|
||||
CREATE_DIRECTORY,
|
||||
TRUNCATE_DIRECTORY);
|
||||
}
|
||||
|
||||
static bool needs_glob(ItemType t) {
|
||||
return IN_SET(t,
|
||||
WRITE_FILE,
|
||||
|
@ -2839,6 +2858,33 @@ static int create_item(Context *c, Item *i) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int purge_item_instance(Context *c, Item *i, const char *instance, CreationMode creation) {
|
||||
int r;
|
||||
|
||||
/* FIXME: we probably should use dir_cleanup() here instead of rm_rf() so that 'x' is honoured. */
|
||||
log_debug("rm -rf \"%s\"", instance);
|
||||
r = rm_rf(instance, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "rm_rf(%s): %m", instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int purge_item(Context *c, Item *i) {
|
||||
|
||||
assert(i);
|
||||
|
||||
if (!needs_purge(i->type))
|
||||
return 0;
|
||||
|
||||
log_debug("Running purge owned action for entry %c %s", (char) i->type, i->path);
|
||||
|
||||
if (needs_glob(i->type))
|
||||
return glob_item(c, i, purge_item_instance);
|
||||
|
||||
return purge_item_instance(c, i, i->path, CREATION_EXISTING);
|
||||
}
|
||||
|
||||
static int remove_item_instance(
|
||||
Context *c,
|
||||
Item *i,
|
||||
|
@ -3031,7 +3077,7 @@ static int process_item(
|
|||
OperationMask todo;
|
||||
_cleanup_free_ char *_path = NULL;
|
||||
const char *path;
|
||||
int r, q, p;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(i);
|
||||
|
@ -3067,12 +3113,11 @@ static int process_item(
|
|||
if (i->allow_failure)
|
||||
r = 0;
|
||||
|
||||
q = FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(c, i) : 0;
|
||||
p = FLAGS_SET(operation, OPERATION_CLEAN) ? clean_item(c, i) : 0;
|
||||
RET_GATHER(r, FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(c, i) : 0);
|
||||
RET_GATHER(r, FLAGS_SET(operation, OPERATION_CLEAN) ? clean_item(c, i) : 0);
|
||||
RET_GATHER(r, FLAGS_SET(operation, OPERATION_PURGE) ? purge_item(c, i) : 0);
|
||||
|
||||
return r < 0 ? r :
|
||||
q < 0 ? q :
|
||||
p;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int process_item_array(
|
||||
|
@ -3091,13 +3136,13 @@ static int process_item_array(
|
|||
r = process_item_array(c, array->parent, operation & OPERATION_CREATE);
|
||||
|
||||
/* Clean up all children first */
|
||||
if ((operation & (OPERATION_REMOVE|OPERATION_CLEAN)) && !set_isempty(array->children)) {
|
||||
if ((operation & (OPERATION_REMOVE|OPERATION_CLEAN|OPERATION_PURGE)) && !set_isempty(array->children)) {
|
||||
ItemArray *cc;
|
||||
|
||||
SET_FOREACH(cc, array->children) {
|
||||
int k;
|
||||
|
||||
k = process_item_array(c, cc, operation & (OPERATION_REMOVE|OPERATION_CLEAN));
|
||||
k = process_item_array(c, cc, operation & (OPERATION_REMOVE|OPERATION_CLEAN|OPERATION_PURGE));
|
||||
if (k < 0 && r == 0)
|
||||
r = k;
|
||||
}
|
||||
|
@ -3982,6 +4027,7 @@ static int help(void) {
|
|||
" --remove Remove marked files/directories\n"
|
||||
" --boot Execute actions only safe at boot\n"
|
||||
" --graceful Quietly ignore unknown users or groups\n"
|
||||
" --purge Delete all files owned by the configuration files\n"
|
||||
" --prefix=PATH Only apply rules with the specified prefix\n"
|
||||
" --exclude-prefix=PATH Ignore rules with the specified prefix\n"
|
||||
" -E Ignore rules prefixed with /dev, /proc, /run, /sys\n"
|
||||
|
@ -4009,6 +4055,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_CREATE,
|
||||
ARG_CLEAN,
|
||||
ARG_REMOVE,
|
||||
ARG_PURGE,
|
||||
ARG_BOOT,
|
||||
ARG_GRACEFUL,
|
||||
ARG_PREFIX,
|
||||
|
@ -4029,6 +4076,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "create", no_argument, NULL, ARG_CREATE },
|
||||
{ "clean", no_argument, NULL, ARG_CLEAN },
|
||||
{ "remove", no_argument, NULL, ARG_REMOVE },
|
||||
{ "purge", no_argument, NULL, ARG_PURGE },
|
||||
{ "boot", no_argument, NULL, ARG_BOOT },
|
||||
{ "graceful", no_argument, NULL, ARG_GRACEFUL },
|
||||
{ "prefix", required_argument, NULL, ARG_PREFIX },
|
||||
|
@ -4084,6 +4132,10 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_boot = true;
|
||||
break;
|
||||
|
||||
case ARG_PURGE:
|
||||
arg_operation |= OPERATION_PURGE;
|
||||
break;
|
||||
|
||||
case ARG_GRACEFUL:
|
||||
arg_graceful = true;
|
||||
break;
|
||||
|
@ -4151,7 +4203,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
|
||||
if (arg_operation == 0 && arg_cat_flags == CAT_CONFIG_OFF)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"You need to specify at least one of --clean, --create, or --remove.");
|
||||
"You need to specify at least one of --clean, --create, --remove, or --purge.");
|
||||
|
||||
if (arg_replace && arg_cat_flags != CAT_CONFIG_OFF)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
|
@ -4402,6 +4454,7 @@ static int run(int argc, char *argv[]) {
|
|||
bool invalid_config = false;
|
||||
ItemArray *a;
|
||||
enum {
|
||||
PHASE_PURGE,
|
||||
PHASE_REMOVE_AND_CLEAN,
|
||||
PHASE_CREATE,
|
||||
_PHASE_MAX
|
||||
|
@ -4537,7 +4590,9 @@ static int run(int argc, char *argv[]) {
|
|||
for (phase = 0; phase < _PHASE_MAX; phase++) {
|
||||
OperationMask op;
|
||||
|
||||
if (phase == PHASE_REMOVE_AND_CLEAN)
|
||||
if (phase == PHASE_PURGE)
|
||||
op = arg_operation & OPERATION_PURGE;
|
||||
else if (phase == PHASE_REMOVE_AND_CLEAN)
|
||||
op = arg_operation & (OPERATION_REMOVE|OPERATION_CLEAN);
|
||||
else if (phase == PHASE_CREATE)
|
||||
op = arg_operation & OPERATION_CREATE;
|
||||
|
|
33
test/units/testsuite-22.18.sh
Executable file
33
test/units/testsuite-22.18.sh
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# Tests for the --purge switch
|
||||
#
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
export SYSTEMD_LOG_LEVEL=debug
|
||||
|
||||
systemd-tmpfiles --create - <<EOF
|
||||
d /tmp/somedir
|
||||
f /tmp/somedir/somefile - - - - baz
|
||||
EOF
|
||||
|
||||
test -f /tmp/somedir/somefile
|
||||
grep -q baz /tmp/somedir/somefile
|
||||
|
||||
systemd-tmpfiles --purge - <<EOF
|
||||
d /tmp/somedir
|
||||
f /tmp/somedir/somefile - - - - baz
|
||||
EOF
|
||||
|
||||
test ! -f /tmp/somedir/somefile
|
||||
test ! -d /tmp/somedir/
|
||||
|
||||
systemd-tmpfiles --create --purge - <<EOF
|
||||
d /tmp/somedir
|
||||
f /tmp/somedir/somefile - - - - baz
|
||||
EOF
|
||||
|
||||
test -f /tmp/somedir/somefile
|
||||
grep -q baz /tmp/somedir/somefile
|
Loading…
Reference in a new issue