1
0
mirror of https://github.com/systemd/systemd synced 2024-07-09 04:26:06 +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:
Luca Boccassi 2022-10-12 23:46:28 +01:00 committed by Luca Boccassi
parent 115d6abf87
commit 81a183800f
3 changed files with 106 additions and 10 deletions

View File

@ -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>

View File

@ -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
View 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