diff --git a/man/standard-options.xml b/man/standard-options.xml index 40edb4b4e1..4ffe124905 100644 --- a/man/standard-options.xml +++ b/man/standard-options.xml @@ -50,4 +50,13 @@ footer with hints. + + + + + + Copy the contents of config files to standard output. + Before each file, the filename is printed as a comment. + + diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index 12d54640a9..55751ec107 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -78,6 +78,12 @@ OPTIONS dump + + systemd-analyze + OPTIONS + cat-config + NAME|PATH + systemd-analyze OPTIONS @@ -184,6 +190,34 @@ state. Its format is subject to change without notice and should not be parsed by applications. + systemd-analyze cat-config is similar + to systemctl cat, but operates on config files. + It will copy the contents of a config file and any drop-ins to standard + output, using the usual systemd set of directories and rules for + precedence. Each argument must be either an absolute path including + the prefix (such as /etc/systemd/logind.conf or + /usr/lib/systemd/logind.conf), or a name + relative to the prefix (such as systemd/logind.conf). + + + + Showing logind configuration + $ systemd-analyze cat-config systemd/logind.conf +# /etc/systemd/logind.conf +# This file is part of systemd. +... +[Login] +NAutoVTs=8 +... + +# /usr/lib/systemd/logind.conf.d/20-test.conf +... some override from another package + +# /etc/systemd/logind.conf.d/50-override.conf +... some adminstrator override + + + systemd-analyze unit-paths outputs a list of all directories from which unit files, .d overrides, and .wants, .requires symlinks may be @@ -341,6 +375,13 @@ generators enabled will generally result in some warnings. + + + + With cat-files, show config files underneath + the specified root path PATH. + + diff --git a/man/systemd-binfmt.service.xml b/man/systemd-binfmt.service.xml index f02e8f0cb0..37ac163f07 100644 --- a/man/systemd-binfmt.service.xml +++ b/man/systemd-binfmt.service.xml @@ -8,7 +8,8 @@ Copyright 2012 Lennart Poettering --> - + systemd-binfmt.service @@ -52,6 +53,14 @@ for information about the configuration of this service. + Options + + + + + + + See Also diff --git a/man/systemd-sysctl.service.xml b/man/systemd-sysctl.service.xml index b4fdc708ad..5df310cff5 100644 --- a/man/systemd-sysctl.service.xml +++ b/man/systemd-sysctl.service.xml @@ -79,6 +79,7 @@ + diff --git a/man/systemd-sysusers.xml b/man/systemd-sysusers.xml index 0f4a47bc7e..ea119fe375 100644 --- a/man/systemd-sysusers.xml +++ b/man/systemd-sysusers.xml @@ -124,6 +124,7 @@ line instead of a file name. + diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index c40f8a920a..f8a289eba3 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -183,6 +183,7 @@ + diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 30139dd5f5..c1b5aca7e2 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -19,12 +19,15 @@ #include "bus-unit-util.h" #include "bus-util.h" #include "calendarspec.h" +#include "def.h" +#include "conf-files.h" #include "glob-util.h" #include "hashmap.h" #include "locale-util.h" #include "log.h" #include "pager.h" #include "parse-util.h" +#include "path-util.h" #if HAVE_SECCOMP #include "seccomp-util.h" #endif @@ -70,6 +73,7 @@ static const char *arg_host = NULL; static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; static bool arg_man = true; static bool arg_generators = false; +static const char *arg_root = NULL; struct boot_times { usec_t firmware_time; @@ -1308,6 +1312,45 @@ static int dump(int argc, char *argv[], void *userdata) { return 0; } +static int cat_config(int argc, char *argv[], void *userdata) { + char **arg; + int r; + + (void) pager_open(arg_no_pager, false); + + STRV_FOREACH(arg, argv + 1) { + const char *t = NULL; + + if (arg != argv + 1) + printf("%s%*s%s\n\n", + ansi_underline(), + columns(), "", + ansi_normal()); + + if (path_is_absolute(*arg)) { + const char *dir; + + NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) { + t = path_startswith(*arg, dir); + if (t) + break; + } + + if (!t) { + log_error("Path %s does not start with any known prefix.", *arg); + return -EINVAL; + } + } else + t = *arg; + + r = conf_files_cat(arg_root, t); + if (r < 0) + return r; + } + + return 0; +} + static int set_log_level(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1639,6 +1682,7 @@ static int help(int argc, char *argv[], void *userdata) { " log-level [LEVEL] Get/set logging threshold for manager\n" " log-target [TARGET] Get/set logging target for manager\n" " dump Output state serialization of service manager\n" + " cat-config Show configuration file and drop-ins\n" " unit-paths List load directories for units\n" " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n" " verify FILE... Check unit files for correctness\n" @@ -1658,6 +1702,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERSION = 0x100, ARG_ORDER, ARG_REQUIRE, + ARG_ROOT, ARG_SYSTEM, ARG_USER, ARG_GLOBAL, @@ -1674,6 +1719,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "order", no_argument, NULL, ARG_ORDER }, { "require", no_argument, NULL, ARG_REQUIRE }, + { "root", required_argument, NULL, ARG_ROOT }, { "system", no_argument, NULL, ARG_SYSTEM }, { "user", no_argument, NULL, ARG_USER }, { "global", no_argument, NULL, ARG_GLOBAL }, @@ -1702,6 +1748,10 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); + case ARG_ROOT: + arg_root = optarg; + break; + case ARG_SYSTEM: arg_scope = UNIT_FILE_SYSTEM; break; @@ -1795,6 +1845,11 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_root && !streq_ptr(argv[optind], "cat-config")) { + log_error("Option --root is only supported for cat-config right now."); + return -EINVAL; + } + return 1; /* work to do */ } @@ -1815,6 +1870,7 @@ int main(int argc, char *argv[]) { { "set-log-target", 2, 2, 0, set_log_target }, { "get-log-target", VERB_ANY, 1, 0, get_log_target }, { "dump", VERB_ANY, 1, 0, dump }, + { "cat-config", 2, VERB_ANY, 0, cat_config }, { "unit-paths", 1, 1, 0, dump_unit_paths }, { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters }, { "verify", 2, VERB_ANY, 0, do_verify }, diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index b5cad5a6e3..1112dbf2f8 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -13,6 +13,7 @@ #include #include "conf-files.h" +#include "def.h" #include "dirent-util.h" #include "fd-util.h" #include "hashmap.h" @@ -23,6 +24,7 @@ #include "stat-util.h" #include "string-util.h" #include "strv.h" +#include "terminal-util.h" #include "util.h" static int files_add(Hashmap *h, const char *suffix, const char *root, unsigned flags, const char *path) { @@ -256,3 +258,71 @@ int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, u return conf_files_list_strv_internal(strv, suffix, root, flags, d); } + +int conf_files_list_with_replacement( + const char *root, + char **config_dirs, + const char *replacement, + char ***files, + char **replace_file) { + + _cleanup_strv_free_ char **f = NULL; + _cleanup_free_ char *p = NULL; + int r; + + assert(config_dirs); + assert(files); + assert(replace_file || !replacement); + + r = conf_files_list_strv(&f, ".conf", root, 0, (const char* const*) config_dirs); + if (r < 0) + return log_error_errno(r, "Failed to enumerate config files: %m"); + + if (replacement) { + r = conf_files_insert(&f, root, config_dirs, replacement); + if (r < 0) + return log_error_errno(r, "Failed to extend config file list: %m"); + + p = path_join(root, replacement, NULL); + if (!p) + return log_oom(); + } + + *files = TAKE_PTR(f); + if (replace_file) + *replace_file = TAKE_PTR(p); + return 0; +} + +int conf_files_cat(const char *root, const char *name) { + _cleanup_strv_free_ char **dirs = NULL, **files = NULL; + _cleanup_free_ char *path = NULL; + const char *dir; + char **t; + int r; + + NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) { + assert(endswith(dir, "/")); + r = strv_extendf(&dirs, "%s%s.d", dir, name); + if (r < 0) + return log_error("Failed to build directory list: %m"); + } + + r = conf_files_list_strv(&files, ".conf", root, 0, (const char* const*) dirs); + if (r < 0) + return log_error_errno(r, "Failed to query file list: %m"); + + path = path_join(root, "/etc", name); + if (!path) + return log_oom(); + + if (DEBUG_LOGGING) { + log_debug("Looking for configuration in:"); + log_debug(" %s", path); + STRV_FOREACH(t, dirs) + log_debug(" %s/*.conf", *t); + } + + /* show */ + return cat_files(path, files, CAT_FLAGS_MAIN_FILE_OPTIONAL); +} diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index b902c3a3b3..2dd1b272ca 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -17,3 +17,10 @@ int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsi 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, char **dirs, const char *path); int conf_files_insert_nulstr(char ***strv, const char *root, const char *dirs, const char *path); +int conf_files_list_with_replacement( + const char *root, + char **config_dirs, + const char *replacement, + char ***files, + char **replace_file); +int conf_files_cat(const char *root, const char *name); diff --git a/src/basic/def.h b/src/basic/def.h index 0ffd81fe39..559721754a 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -54,8 +54,10 @@ #define NOTIFY_BUFFER_MAX PIPE_BUF #if HAVE_SPLIT_USR -# define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0" +# define _CONF_PATHS_SPLIT_USR_NULSTR(n) "/lib/" n "\0" +# define _CONF_PATHS_SPLIT_USR(n) , "/lib/" n #else +# define _CONF_PATHS_SPLIT_USR_NULSTR(n) # define _CONF_PATHS_SPLIT_USR(n) #endif @@ -68,6 +70,14 @@ "/run/" n "\0" \ "/usr/local/lib/" n "\0" \ "/usr/lib/" n "\0" \ - _CONF_PATHS_SPLIT_USR(n) + _CONF_PATHS_SPLIT_USR_NULSTR(n) + +#define CONF_PATHS_STRV(n) \ + STRV_MAKE( \ + "/etc/" n, \ + "/run/" n, \ + "/usr/local/lib/" n, \ + "/usr/lib/" n \ + _CONF_PATHS_SPLIT_USR(n)) #define LONG_LINE_MAX (1U*1024U*1024U) diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 2bf0f4f86d..7f18d3d35c 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -28,6 +28,8 @@ #include #include "alloc-util.h" +#include "copy.h" +#include "def.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" @@ -1362,3 +1364,57 @@ int terminal_urlify_path(const char *path, const char *text, char **ret) { return terminal_urlify(url, text, ret); } + +static int cat_file(const char *filename, bool newline) { + _cleanup_fclose_ FILE *f = NULL; + int r; + + f = fopen(filename, "re"); + if (!f) + return -errno; + + printf("%s%s# %s%s\n", + newline ? "\n" : "", + ansi_highlight_blue(), + filename, + ansi_normal()); + fflush(stdout); + + for (;;) { + _cleanup_free_ char *line = NULL; + + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return log_error_errno(r, "Failed to read \"%s\": %m", filename); + if (r == 0) + break; + + puts(line); + } + + return 0; +} + +int cat_files(const char *file, char **dropins, CatFlags flags) { + char **path; + int r; + + if (file) { + r = cat_file(file, false); + if (r == -ENOENT && (flags & CAT_FLAGS_MAIN_FILE_OPTIONAL)) + printf("%s# config file %s not found%s\n", + ansi_highlight_magenta(), + file, + ansi_normal()); + else if (r < 0) + return log_warning_errno(r, "Failed to cat %s: %m", file); + } + + STRV_FOREACH(path, dropins) { + r = cat_file(*path, file || path != dropins); + if (r < 0) + return log_warning_errno(r, "Failed to cat %s: %m", *path); + } + + return 0; +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index ad6ee338ed..ea7eb2d63b 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -133,6 +133,7 @@ DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED); DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN); DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW); DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE); +DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA); DEFINE_ANSI_FUNC(normal, NORMAL); DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL); @@ -159,3 +160,9 @@ int vt_reset_keyboard(int fd); int terminal_urlify(const char *url, const char *text, char **ret); int terminal_urlify_path(const char *path, const char *text, char **ret); + +typedef enum CatFlags { + CAT_FLAGS_MAIN_FILE_OPTIONAL = 1 << 1, +} CatFlags; + +int cat_files(const char *file, char **dropins, CatFlags flags); diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c index 03c4937dac..0eb9e46014 100644 --- a/src/binfmt/binfmt.c +++ b/src/binfmt/binfmt.c @@ -21,9 +21,10 @@ #include "log.h" #include "string-util.h" #include "strv.h" +#include "terminal-util.h" #include "util.h" -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("binfmt.d"); +static bool arg_cat_config = false; static int delete_rule(const char *rule) { _cleanup_free_ char *x = NULL, *fn = NULL; @@ -63,7 +64,7 @@ static int apply_file(const char *path, bool ignore_enoent) { assert(path); - r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); + r = search_and_fopen(path, "re", NULL, (const char**) CONF_PATHS_STRV("binfmt.d"), &f); if (r < 0) { if (ignore_enoent && r == -ENOENT) return 0; @@ -102,6 +103,7 @@ static void help(void) { "Registers binary formats.\n\n" " -h --help Show this help\n" " --version Show package version\n" + " --cat-config Show configuration files\n" , program_invocation_short_name); } @@ -109,11 +111,13 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_CAT_CONFIG, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "cat-config", no_argument, NULL, ARG_CAT_CONFIG }, {} }; @@ -133,6 +137,10 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); + case ARG_CAT_CONFIG: + arg_cat_config = true; + break; + case '?': return -EINVAL; @@ -140,6 +148,11 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } + if (arg_cat_config && argc > optind) { + log_error("Positional arguments are not allowed with --cat-config"); + return -EINVAL; + } + return 1; } @@ -170,12 +183,17 @@ int main(int argc, char *argv[]) { _cleanup_strv_free_ char **files = NULL; char **f; - r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs); + r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("binfmt.d")); if (r < 0) { log_error_errno(r, "Failed to enumerate binfmt.d files: %m"); goto finish; } + if (arg_cat_config) { + r = cat_files(NULL, files, 0); + goto finish; + } + /* Flush out all rules */ write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", 0); diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index 75a3680db0..5489cb96b7 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -23,11 +23,11 @@ #include "string-util.h" #include "strv.h" #include "sysctl-util.h" +#include "terminal-util.h" #include "util.h" static char **arg_prefixes = NULL; - -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysctl.d"); +static bool arg_cat_config = false; static int apply_all(OrderedHashmap *sysctl_options) { char *property, *value; @@ -83,7 +83,7 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign assert(path); - r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f); + r = search_and_fopen(path, "re", NULL, (const char**) CONF_PATHS_STRV("sysctl.d"), &f); if (r < 0) { if (ignore_enoent && r == -ENOENT) return 0; @@ -168,6 +168,7 @@ static void help(void) { "Applies kernel sysctl settings.\n\n" " -h --help Show this help\n" " --version Show package version\n" + " --cat-config Show configuration files\n" " --prefix=PATH Only apply rules with the specified prefix\n" , program_invocation_short_name); } @@ -176,13 +177,15 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, - ARG_PREFIX + ARG_CAT_CONFIG, + ARG_PREFIX, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "prefix", required_argument, NULL, ARG_PREFIX }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "cat-config", no_argument, NULL, ARG_CAT_CONFIG }, + { "prefix", required_argument, NULL, ARG_PREFIX }, {} }; @@ -202,6 +205,10 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); + case ARG_CAT_CONFIG: + arg_cat_config = true; + break; + case ARG_PREFIX: { char *p; @@ -231,6 +238,11 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } + if (arg_cat_config && argc > optind) { + log_error("Positional arguments are not allowed with --cat-config"); + return -EINVAL; + } + return 1; } @@ -268,12 +280,17 @@ int main(int argc, char *argv[]) { _cleanup_strv_free_ char **files = NULL; char **f; - r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs); + r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("sysctl.d")); if (r < 0) { log_error_errno(r, "Failed to enumerate sysctl.d files: %m"); goto finish; } + if (arg_cat_config) { + r = cat_files(NULL, files, 0); + goto finish; + } + STRV_FOREACH(f, files) { k = parse_file(sysctl_options, *f, true); if (k < 0 && r == 0) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index a6646aa7f6..a8ca47b3ad 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -5314,23 +5314,6 @@ static int show(int argc, char *argv[], void *userdata) { return ret; } -static int cat_file(const char *filename, bool newline) { - _cleanup_close_ int fd; - - fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return -errno; - - printf("%s%s# %s%s\n", - newline ? "\n" : "", - ansi_highlight_blue(), - filename, - ansi_normal()); - fflush(stdout); - - return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, 0); -} - static int cat(int argc, char *argv[], void *userdata) { _cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_strv_free_ char **names = NULL; @@ -5361,7 +5344,6 @@ static int cat(int argc, char *argv[], void *userdata) { STRV_FOREACH(name, names) { _cleanup_free_ char *fragment_path = NULL; _cleanup_strv_free_ char **dropin_paths = NULL; - char **path; r = unit_find_paths(bus, *name, &lp, &fragment_path, &dropin_paths); if (r < 0) @@ -5388,17 +5370,9 @@ static int cat(int argc, char *argv[], void *userdata) { arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", ansi_normal()); - if (fragment_path) { - r = cat_file(fragment_path, false); - if (r < 0) - return log_warning_errno(r, "Failed to cat %s: %m", fragment_path); - } - - STRV_FOREACH(path, dropin_paths) { - r = cat_file(*path, path == dropin_paths); - if (r < 0) - return log_warning_errno(r, "Failed to cat %s: %m", *path); - } + r = cat_files(fragment_path, dropin_paths, 0); + if (r < 0) + return r; } return 0; diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 7c5fafcf8a..92e5851519 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -23,6 +23,7 @@ #include "specifier.h" #include "string-util.h" #include "strv.h" +#include "terminal-util.h" #include "uid-range.h" #include "user-util.h" #include "utf8.h" @@ -61,11 +62,10 @@ typedef struct Item { } Item; static char *arg_root = NULL; +static bool arg_cat_config = false; static const char *arg_replace = NULL; static bool arg_inline = false; -static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysusers.d"); - static OrderedHashmap *users = NULL, *groups = NULL; static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL; static OrderedHashmap *members = NULL; @@ -1688,7 +1688,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) { if (streq(fn, "-")) f = stdin; else { - r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &rf); + r = search_and_fopen(fn, "re", arg_root, (const char**) CONF_PATHS_STRV("sysusers.d"), &rf); if (r < 0) { if (ignore_enoent && r == -ENOENT) return 0; @@ -1744,11 +1744,24 @@ static void free_database(Hashmap *by_name, Hashmap *by_id) { hashmap_free(by_id); } +static int cat_config(void) { + _cleanup_strv_free_ char **files = NULL; + _cleanup_free_ char *replace_file = NULL; + int r; + + r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, NULL); + if (r < 0) + return r; + + return cat_files(NULL, files, 0); +} + static void help(void) { printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" "Creates system user accounts.\n\n" " -h --help Show this help\n" " --version Show package version\n" + " --cat-config Show configuration files\n" " --root=PATH Operate on an alternate filesystem root\n" " --replace=PATH Treat arguments as replacement for PATH\n" " --inline Treat arguments as configuration lines\n" @@ -1759,17 +1772,19 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_CAT_CONFIG, ARG_ROOT, ARG_REPLACE, ARG_INLINE, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "root", required_argument, NULL, ARG_ROOT }, - { "replace", required_argument, NULL, ARG_REPLACE }, - { "inline", no_argument, NULL, ARG_INLINE }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "cat-config", no_argument, NULL, ARG_CAT_CONFIG }, + { "root", required_argument, NULL, ARG_ROOT }, + { "replace", required_argument, NULL, ARG_REPLACE }, + { "inline", no_argument, NULL, ARG_INLINE }, {} }; @@ -1789,6 +1804,10 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); + case ARG_CAT_CONFIG: + arg_cat_config = true; + break; + case ARG_ROOT: r = parse_path_argument_and_warn(optarg, true, &arg_root); if (r < 0) @@ -1816,6 +1835,11 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } + if (arg_replace && arg_cat_config) { + log_error("Option --replace= is not supported with --cat-config"); + return -EINVAL; + } + if (arg_replace && optind >= argc) { log_error("When --replace= is given, some configuration items must be specified"); return -EINVAL; @@ -1844,25 +1868,15 @@ static int parse_arguments(char **args) { return 0; } -static int read_config_files(const char* dirs, char **args) { +static int read_config_files(char **args) { _cleanup_strv_free_ char **files = NULL; _cleanup_free_ char *p = NULL; char **f; int r; - r = conf_files_list_nulstr(&files, ".conf", arg_root, 0, dirs); + r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, &p); if (r < 0) - return log_error_errno(r, "Failed to enumerate sysusers.d files: %m"); - - if (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"); - - p = path_join(arg_root, arg_replace, NULL); - if (!p) - return log_oom(); - } + return r; STRV_FOREACH(f, files) if (p && path_equal(*f, p)) { @@ -1882,7 +1896,6 @@ static int read_config_files(const char* dirs, char **args) { } int main(int argc, char *argv[]) { - _cleanup_close_ int lock = -1; Iterator iterator; int r; @@ -1897,6 +1910,11 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + if (arg_cat_config) { + r = cat_config(); + goto finish; + } + umask(0022); r = mac_selinux_init(); @@ -1912,7 +1930,7 @@ int main(int argc, char *argv[]) { * read configuration and execute it. */ if (arg_replace || optind >= argc) - r = read_config_files(conf_file_dirs, argv + optind); + r = read_config_files(argv + optind); else r = parse_arguments(argv + optind); if (r < 0) diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index c83dfa9dcd..61a3ea5e56 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -14,6 +14,7 @@ #include "fileio.h" #include "log.h" #include "macro.h" +#include "strv.h" #include "terminal-util.h" #include "util.h" @@ -76,6 +77,14 @@ static void test_terminal_urlify(void) { printf("Or click on %s to have a look at it!\n", formatted); } +static void test_cat_files(void) { + assert_se(cat_files("/no/such/file", NULL, 0) == -ENOENT); + assert_se(cat_files("/no/such/file", NULL, CAT_FLAGS_MAIN_FILE_OPTIONAL) == 0); + + if (access("/etc/fstab", R_OK) >= 0) + assert_se(cat_files("/etc/fstab", STRV_MAKE("/etc/fstab", "/etc/fstab"), 0) == 0); +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -83,6 +92,7 @@ int main(int argc, char *argv[]) { test_default_term_for_tty(); test_read_one_char(); test_terminal_urlify(); + test_cat_files(); return 0; } diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index bc66299142..b4a9b87728 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -60,6 +60,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "terminal-util.h" #include "umask-util.h" #include "user-util.h" #include "util.h" @@ -149,6 +150,7 @@ typedef enum DirectoryType { _DIRECTORY_TYPE_MAX, } DirectoryType; +static bool arg_cat_config = false; static bool arg_user = false; static bool arg_create = false; static bool arg_clean = false; @@ -2441,12 +2443,24 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool return 0; } +static int cat_config(char **config_dirs, char **args) { + _cleanup_strv_free_ char **files = NULL; + int r; + + r = conf_files_list_with_replacement(arg_root, config_dirs, arg_replace, &files, NULL); + if (r < 0) + return r; + + return cat_files(NULL, files, 0); +} + static void help(void) { printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" "Creates, deletes and cleans up volatile and temporary files and directories.\n\n" " -h --help Show this help\n" " --user Execute user configuration\n" " --version Show package version\n" + " --cat-config Show configuration files\n" " --create Create marked files/directories\n" " --clean Clean up marked directories\n" " --remove Remove marked files/directories\n" @@ -2462,6 +2476,7 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_CAT_CONFIG, ARG_USER, ARG_CREATE, ARG_CLEAN, @@ -2477,6 +2492,7 @@ static int parse_argv(int argc, char *argv[]) { { "help", no_argument, NULL, 'h' }, { "user", no_argument, NULL, ARG_USER }, { "version", no_argument, NULL, ARG_VERSION }, + { "cat-config", no_argument, NULL, ARG_CAT_CONFIG }, { "create", no_argument, NULL, ARG_CREATE }, { "clean", no_argument, NULL, ARG_CLEAN }, { "remove", no_argument, NULL, ARG_REMOVE }, @@ -2504,6 +2520,10 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); + case ARG_CAT_CONFIG: + arg_cat_config = true; + break; + case ARG_USER: arg_user = true; break; @@ -2557,11 +2577,16 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (!arg_clean && !arg_create && !arg_remove) { + if (!arg_clean && !arg_create && !arg_remove && !arg_cat_config) { log_error("You need to specify at least one of --clean, --create or --remove."); return -EINVAL; } + if (arg_replace && arg_cat_config) { + log_error("Option --replace= is not supported with --cat-config"); + return -EINVAL; + } + if (arg_replace && optind >= argc) { log_error("When --replace= is given, some configuration items must be specified"); return -EINVAL; @@ -2677,19 +2702,9 @@ static int read_config_files(char **config_dirs, char **args, bool *invalid_conf char **f; int r; - r = conf_files_list_strv(&files, ".conf", arg_root, 0, (const char* const*) config_dirs); + r = conf_files_list_with_replacement(arg_root, config_dirs, arg_replace, &files, &p); 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(); - } + return r; STRV_FOREACH(f, files) if (p && path_equal(*f, p)) { @@ -2721,20 +2736,6 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - umask(0022); - - mac_selinux_init(); - - items = ordered_hashmap_new(&string_hash_ops); - globs = ordered_hashmap_new(&string_hash_ops); - - if (!items || !globs) { - r = log_oom(); - goto finish; - } - - r = 0; - if (arg_user) { r = user_config_paths(&config_dirs); if (r < 0) { @@ -2757,6 +2758,23 @@ int main(int argc, char *argv[]) { log_debug("Looking for configuration files in (higher priority first):\n\t%s", t); } + if (arg_cat_config) { + r = cat_config(config_dirs, argv + optind); + goto finish; + } + + umask(0022); + + mac_selinux_init(); + + items = ordered_hashmap_new(&string_hash_ops); + globs = ordered_hashmap_new(&string_hash_ops); + + if (!items || !globs) { + r = log_oom(); + goto finish; + } + /* 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