mirror of
https://github.com/systemd/systemd
synced 2024-07-24 11:44:41 +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>
|
</para></listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><option>--user</option></term>
|
<term><option>--user</option></term>
|
||||||
<listitem><para>Execute "user" configuration, i.e. <filename>tmpfiles.d</filename>
|
<listitem><para>Execute "user" configuration, i.e. <filename>tmpfiles.d</filename>
|
||||||
|
|
|
@ -82,6 +82,7 @@ typedef enum OperationMask {
|
||||||
OPERATION_CREATE = 1 << 0,
|
OPERATION_CREATE = 1 << 0,
|
||||||
OPERATION_REMOVE = 1 << 1,
|
OPERATION_REMOVE = 1 << 1,
|
||||||
OPERATION_CLEAN = 1 << 2,
|
OPERATION_CLEAN = 1 << 2,
|
||||||
|
OPERATION_PURGE = 1 << 3,
|
||||||
} OperationMask;
|
} OperationMask;
|
||||||
|
|
||||||
typedef enum ItemType {
|
typedef enum ItemType {
|
||||||
|
@ -378,6 +379,24 @@ static int user_config_paths(char*** ret) {
|
||||||
return 0;
|
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) {
|
static bool needs_glob(ItemType t) {
|
||||||
return IN_SET(t,
|
return IN_SET(t,
|
||||||
WRITE_FILE,
|
WRITE_FILE,
|
||||||
|
@ -2839,6 +2858,33 @@ static int create_item(Context *c, Item *i) {
|
||||||
return 0;
|
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(
|
static int remove_item_instance(
|
||||||
Context *c,
|
Context *c,
|
||||||
Item *i,
|
Item *i,
|
||||||
|
@ -3031,7 +3077,7 @@ static int process_item(
|
||||||
OperationMask todo;
|
OperationMask todo;
|
||||||
_cleanup_free_ char *_path = NULL;
|
_cleanup_free_ char *_path = NULL;
|
||||||
const char *path;
|
const char *path;
|
||||||
int r, q, p;
|
int r;
|
||||||
|
|
||||||
assert(c);
|
assert(c);
|
||||||
assert(i);
|
assert(i);
|
||||||
|
@ -3067,12 +3113,11 @@ static int process_item(
|
||||||
if (i->allow_failure)
|
if (i->allow_failure)
|
||||||
r = 0;
|
r = 0;
|
||||||
|
|
||||||
q = FLAGS_SET(operation, OPERATION_REMOVE) ? remove_item(c, i) : 0;
|
RET_GATHER(r, 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_CLEAN) ? clean_item(c, i) : 0);
|
||||||
|
RET_GATHER(r, FLAGS_SET(operation, OPERATION_PURGE) ? purge_item(c, i) : 0);
|
||||||
|
|
||||||
return r < 0 ? r :
|
return r;
|
||||||
q < 0 ? q :
|
|
||||||
p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int process_item_array(
|
static int process_item_array(
|
||||||
|
@ -3091,13 +3136,13 @@ static int process_item_array(
|
||||||
r = process_item_array(c, array->parent, operation & OPERATION_CREATE);
|
r = process_item_array(c, array->parent, operation & OPERATION_CREATE);
|
||||||
|
|
||||||
/* Clean up all children first */
|
/* 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;
|
ItemArray *cc;
|
||||||
|
|
||||||
SET_FOREACH(cc, array->children) {
|
SET_FOREACH(cc, array->children) {
|
||||||
int k;
|
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)
|
if (k < 0 && r == 0)
|
||||||
r = k;
|
r = k;
|
||||||
}
|
}
|
||||||
|
@ -3982,6 +4027,7 @@ static int help(void) {
|
||||||
" --remove Remove marked files/directories\n"
|
" --remove Remove marked files/directories\n"
|
||||||
" --boot Execute actions only safe at boot\n"
|
" --boot Execute actions only safe at boot\n"
|
||||||
" --graceful Quietly ignore unknown users or groups\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"
|
" --prefix=PATH Only apply rules with the specified prefix\n"
|
||||||
" --exclude-prefix=PATH Ignore 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"
|
" -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_CREATE,
|
||||||
ARG_CLEAN,
|
ARG_CLEAN,
|
||||||
ARG_REMOVE,
|
ARG_REMOVE,
|
||||||
|
ARG_PURGE,
|
||||||
ARG_BOOT,
|
ARG_BOOT,
|
||||||
ARG_GRACEFUL,
|
ARG_GRACEFUL,
|
||||||
ARG_PREFIX,
|
ARG_PREFIX,
|
||||||
|
@ -4029,6 +4076,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "create", no_argument, NULL, ARG_CREATE },
|
{ "create", no_argument, NULL, ARG_CREATE },
|
||||||
{ "clean", no_argument, NULL, ARG_CLEAN },
|
{ "clean", no_argument, NULL, ARG_CLEAN },
|
||||||
{ "remove", no_argument, NULL, ARG_REMOVE },
|
{ "remove", no_argument, NULL, ARG_REMOVE },
|
||||||
|
{ "purge", no_argument, NULL, ARG_PURGE },
|
||||||
{ "boot", no_argument, NULL, ARG_BOOT },
|
{ "boot", no_argument, NULL, ARG_BOOT },
|
||||||
{ "graceful", no_argument, NULL, ARG_GRACEFUL },
|
{ "graceful", no_argument, NULL, ARG_GRACEFUL },
|
||||||
{ "prefix", required_argument, NULL, ARG_PREFIX },
|
{ "prefix", required_argument, NULL, ARG_PREFIX },
|
||||||
|
@ -4084,6 +4132,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
arg_boot = true;
|
arg_boot = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_PURGE:
|
||||||
|
arg_operation |= OPERATION_PURGE;
|
||||||
|
break;
|
||||||
|
|
||||||
case ARG_GRACEFUL:
|
case ARG_GRACEFUL:
|
||||||
arg_graceful = true;
|
arg_graceful = true;
|
||||||
break;
|
break;
|
||||||
|
@ -4151,7 +4203,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
|
|
||||||
if (arg_operation == 0 && arg_cat_flags == CAT_CONFIG_OFF)
|
if (arg_operation == 0 && arg_cat_flags == CAT_CONFIG_OFF)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
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)
|
if (arg_replace && arg_cat_flags != CAT_CONFIG_OFF)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
@ -4402,6 +4454,7 @@ static int run(int argc, char *argv[]) {
|
||||||
bool invalid_config = false;
|
bool invalid_config = false;
|
||||||
ItemArray *a;
|
ItemArray *a;
|
||||||
enum {
|
enum {
|
||||||
|
PHASE_PURGE,
|
||||||
PHASE_REMOVE_AND_CLEAN,
|
PHASE_REMOVE_AND_CLEAN,
|
||||||
PHASE_CREATE,
|
PHASE_CREATE,
|
||||||
_PHASE_MAX
|
_PHASE_MAX
|
||||||
|
@ -4537,7 +4590,9 @@ static int run(int argc, char *argv[]) {
|
||||||
for (phase = 0; phase < _PHASE_MAX; phase++) {
|
for (phase = 0; phase < _PHASE_MAX; phase++) {
|
||||||
OperationMask op;
|
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);
|
op = arg_operation & (OPERATION_REMOVE|OPERATION_CLEAN);
|
||||||
else if (phase == PHASE_CREATE)
|
else if (phase == PHASE_CREATE)
|
||||||
op = arg_operation & OPERATION_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