1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-10-27 01:55:32 +03:00

Merge pull request #8104 from keszybz/tmpfiles-allow-overrides

tmpfiles allow overrides
This commit is contained in:
Lennart Poettering 2018-02-07 12:12:52 +01:00 committed by GitHub
commit a166e13771
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 158 additions and 52 deletions

View File

@ -84,12 +84,16 @@
</para>
<para>If invoked with no arguments, it applies all directives from all configuration
files. 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
files. 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.</para>
are searched for a matching file and the file found that has the highest priority is
executed.</para>
</refsect1>
<refsect1>
@ -176,6 +180,22 @@
consulted.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--replace=<replaceable>PATH</replaceable></option></term>
<listitem><para>When this option is given, one ore more positional arguments
must be specified. All configuration files found in the directories listed in
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
will be read, and the configuration given on the command line will be
handled instead of and with the same priority as the configuration file
<replaceable>PATH</replaceable>.</para>
<para>This option is intended to be used when package installation scripts
are running and files belonging to that package are not yet available on
disk, so their contents must be given on the command line, but the admin
configuration might already exist and should be given higher priority.
</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>

View File

@ -154,7 +154,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
return 0;
}
int conf_files_insert(char ***strv, const char *root, const char *dirs, const char *path) {
int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path) {
/* Insert a path into strv, at the place honouring the usual sorting rules:
* - we first compare by the basename
* - and then we compare by dirname, allowing just one file with the given
@ -174,22 +174,22 @@ int conf_files_insert(char ***strv, const char *root, const char *dirs, const ch
c = base_cmp(*strv + i, &path);
if (c == 0) {
const char *dir;
char **dir;
/* Oh, we found our spot and it already contains something. */
NULSTR_FOREACH(dir, dirs) {
STRV_FOREACH(dir, dirs) {
char *p1, *p2;
p1 = path_startswith((*strv)[i], root);
if (p1)
/* Skip "/" in dir, because p1 is without "/" too */
p1 = path_startswith(p1, dir + 1);
/* Skip "/" in *dir, because p1 is without "/" too */
p1 = path_startswith(p1, *dir + 1);
if (p1)
/* Existing entry with higher priority
* or same priority, no need to do anything. */
return 0;
p2 = path_startswith(path, dir);
p2 = path_startswith(path, *dir);
if (p2) {
/* Our new entry has higher priority */
t = path_join(root, path, NULL);
@ -218,6 +218,18 @@ int conf_files_insert(char ***strv, const char *root, const char *dirs, const ch
return r;
}
int conf_files_insert_nulstr(char ***strv, const char *root, const char *dirs, const char *path) {
_cleanup_strv_free_ char **d = NULL;
assert(strv);
d = strv_split_nulstr(dirs);
if (!d)
return -ENOMEM;
return conf_files_insert(strv, root, d, path);
}
int conf_files_list_strv(char ***strv, const char *suffix, const char *root, unsigned flags, const char* const* dirs) {
_cleanup_strv_free_ char **copy = NULL;
@ -246,14 +258,14 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, unsigned
return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
}
int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, unsigned flags, const char *d) {
_cleanup_strv_free_ char **dirs = NULL;
int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, unsigned flags, const char *dirs) {
_cleanup_strv_free_ char **d = NULL;
assert(strv);
dirs = strv_split_nulstr(d);
if (!dirs)
d = strv_split_nulstr(dirs);
if (!d)
return -ENOMEM;
return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
return conf_files_list_strv_internal(strv, suffix, root, flags, d);
}

View File

@ -28,4 +28,5 @@ enum {
int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir, ...);
int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsigned flags, const char* const* dirs);
int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs);
int conf_files_insert(char ***strv, const char *root, const char *dirs, const char *path);
int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path);
int conf_files_insert_nulstr(char ***strv, const char *root, const char *dirs, const char *path);

View File

@ -90,6 +90,7 @@ fi \
%journal_catalog_update() %{nil}
# Deprecated. Use %tmpfiles_create_package instead
%tmpfiles_create() \
systemd-tmpfiles --create %{?*} >/dev/null 2>&1 || : \
%{nil}
@ -103,24 +104,40 @@ systemd-sysusers %{?*} >/dev/null 2>&1 || : \
echo %{?*} | systemd-sysusers - >/dev/null 2>&1 || : \
%{nil}
# This should be used by package installation scripts which
# require users or groups to be present before the files installed
# by the package are present on disk (for example because some files
# are owned by those users or groups).
# This should be used by package installation scripts which require users or
# groups to be present before the files installed by the package are present on
# disk (for example because some files are owned by those users or groups).
#
# Example:
# Source1: %{name}.conf
# Source1: %{name}-sysusers.conf
# ...
# %install
# install -Dt %{buildroot}%{sysusersdir} %SOURCE1
# install -D %SOURCE1 %{buildroot}%{_sysusersdir}/%{name}.conf
# %pre
# %sysusers_create_package %{name} %SOURCE1
# %files
# %{sysusersdir}/%{name}.conf
# %{_sysusersdir}/%{name}.conf
%sysusers_create_package() \
echo "%(cat %2)" | systemd-sysusers --replace=%_sysusersdir/%1.conf - >/dev/null 2>&1 || : \
%{nil}
# This may be used by package installation scripts to create files according to
# their tmpfiles configuration from a package installation script, even before
# the files of that package are installed on disk.
#
# Example:
# Source1: %{name}-tmpfiles.conf
# ...
# %install
# install -D %SOURCE1 %{buildroot}%{_tmpfilesdir}/%{name}.conf
# %pre
# %tmpfiles_create_package %{name} %SOURCE1
# %files
# %{_tmpfilesdir}/%{name}.conf
%tmpfiles_create_package() \
echo "%(cat %2)" | systemd-tmpfiles --replace=%_tmpfilesdir/%1.conf --create - >/dev/null 2>&1 || : \
%{nil}
%sysctl_apply() \
@rootlibexecdir@/systemd-sysctl %{?*} >/dev/null 2>&1 || : \
%{nil}

View File

@ -1855,7 +1855,7 @@ static int read_config_files(const char* dirs, char **args) {
return log_error_errno(r, "Failed to enumerate sysusers.d files: %m");
if (arg_replace) {
r = conf_files_insert(&files, arg_root, dirs, arg_replace);
r = conf_files_insert_nulstr(&files, arg_root, dirs, arg_replace);
if (r < 0)
return log_error_errno(r, "Failed to extend sysusers.d file list: %m");

View File

@ -171,6 +171,7 @@ static bool arg_boot = false;
static char **arg_include_prefixes = NULL;
static char **arg_exclude_prefixes = NULL;
static char *arg_root = NULL;
static char *arg_replace = NULL;
#define MAX_DEPTH 256
@ -2364,6 +2365,7 @@ static void help(void) {
" --prefix=PATH Only apply rules with the specified prefix\n"
" --exclude-prefix=PATH Ignore rules with the specified prefix\n"
" --root=PATH Operate on an alternate filesystem root\n"
" --replace=PATH Treat arguments as replacement for PATH\n"
, program_invocation_short_name);
}
@ -2379,6 +2381,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PREFIX,
ARG_EXCLUDE_PREFIX,
ARG_ROOT,
ARG_REPLACE,
};
static const struct option options[] = {
@ -2392,6 +2395,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "prefix", required_argument, NULL, ARG_PREFIX },
{ "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
{ "root", required_argument, NULL, ARG_ROOT },
{ "replace", required_argument, NULL, ARG_REPLACE },
{}
};
@ -2447,6 +2451,16 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
case ARG_REPLACE:
if (!path_is_absolute(optarg) ||
!endswith(optarg, ".conf")) {
log_error("The argument to --replace= must an absolute path to a config file");
return -EINVAL;
}
arg_replace = optarg;
break;
case '?':
return -EINVAL;
@ -2459,10 +2473,15 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
}
if (arg_replace && optind >= argc) {
log_error("When --replace= is given, some configuration items must be specified");
return -EINVAL;
}
return 1;
}
static int read_config_file(const char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
_cleanup_fclose_ FILE *_f = NULL;
FILE *f;
char line[LINE_MAX];
@ -2474,11 +2493,11 @@ static int read_config_file(const char **config_dirs, const char *fn, bool ignor
assert(fn);
if (streq(fn, "-")) {
log_debug("Reading config from stdin.");
log_debug("Reading config from stdin");
fn = "<stdin>";
f = stdin;
} else {
r = search_and_fopen(fn, "re", arg_root, config_dirs, &_f);
r = search_and_fopen(fn, "re", arg_root, (const char**) config_dirs, &_f);
if (r < 0) {
if (ignore_enoent && r == -ENOENT) {
log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
@ -2487,7 +2506,7 @@ static int read_config_file(const char **config_dirs, const char *fn, bool ignor
return log_error_errno(r, "Failed to open '%s': %m", fn);
}
log_debug("Reading config file \"%s\".", fn);
log_debug("Reading config file \"%s\"", fn);
f = _f;
}
@ -2550,13 +2569,60 @@ static int read_config_file(const char **config_dirs, const char *fn, bool ignor
return r;
}
static int parse_arguments(char **config_dirs, char **args, bool *invalid_config) {
char **arg;
int r;
STRV_FOREACH(arg, args) {
r = read_config_file(config_dirs, *arg, false, invalid_config);
if (r < 0)
return r;
}
return 0;
}
static int read_config_files(char **config_dirs, char **args, bool *invalid_config) {
_cleanup_strv_free_ char **files = NULL;
_cleanup_free_ char *p = NULL;
char **f;
int r;
r = conf_files_list_strv(&files, ".conf", arg_root, 0, (const char* const*) config_dirs);
if (r < 0)
return log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
if (arg_replace) {
r = conf_files_insert(&files, arg_root, config_dirs, arg_replace);
if (r < 0)
return log_error_errno(r, "Failed to extend tmpfiles.d file list: %m");
p = path_join(arg_root, arg_replace, NULL);
if (!p)
return log_oom();
}
STRV_FOREACH(f, files)
if (p && path_equal(*f, p)) {
log_debug("Parsing arguments at position \"%s\"", *f);
r = parse_arguments(config_dirs, args, invalid_config);
if (r < 0)
return r;
} else
/* Just warn, ignore result otherwise.
* read_config_file() has some debug output, so no need to print anything. */
(void) read_config_file(config_dirs, *f, true, invalid_config);
return 0;
}
int main(int argc, char *argv[]) {
int r, k;
ItemArray *a;
Iterator iterator;
_cleanup_strv_free_ char **config_dirs = NULL;
bool invalid_config = false;
char **f;
r = parse_argv(argc, argv);
if (r <= 0)
@ -2602,30 +2668,20 @@ int main(int argc, char *argv[]) {
log_debug("Looking for configuration files in (higher priority first:\n\t%s", t);
}
if (optind < argc) {
int j;
/* If command line arguments are specified along with --replace, read all
* configuration files and insert the positional arguments at the specified
* place. Otherwise, if command line arguments are specified, execute just
* them, and finally, without --replace= or any positional arguments, just
* read configuration and execute it.
*/
if (arg_replace || optind >= argc)
r = read_config_files(config_dirs, argv + optind, &invalid_config);
else
r = parse_arguments(config_dirs, argv + optind, &invalid_config);
if (r < 0)
goto finish;
for (j = optind; j < argc; j++) {
k = read_config_file((const char**) config_dirs, argv[j], false, &invalid_config);
if (k < 0 && r == 0)
r = k;
}
} else {
_cleanup_strv_free_ char **files = NULL;
r = conf_files_list_strv(&files, ".conf", arg_root, 0, (const char* const*) config_dirs);
if (r < 0) {
log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
goto finish;
}
STRV_FOREACH(f, files) {
k = read_config_file((const char**) config_dirs, *f, true, &invalid_config);
if (k < 0 && r == 0)
r = k;
}
}
/* The non-globbing ones usually create things, hence we apply
* them first */