diff options
author | Lennart Poettering <lennart@poettering.net> | 2013-02-11 23:48:36 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2013-02-11 23:54:30 +0100 |
commit | fabe5c0e5fce730aa66e10a9c4f9fdd443d7aeda (patch) | |
tree | 11a1e012f820f3e4f30e5d60fd75f81dddaa1310 | |
parent | 853b8397acdebdd44777810e560403bae3756859 (diff) |
binfmt,tmpfiles,modules-load,sysctl: rework the various early-boot services that work on .d/ directories
This unifies much of the logic behind them:
- All four will now ofllow the rule that the earlier file and earlier
assignment in the .d/ directories wins. Before, sysctl was the only
outlier, where the later setting always won.
- All four now support getopt() and --help on the command line.
- All four can now handle specification of configuration file names on
the command line to apply. The tools will automatically find them, and
apply them. Previously only tmpfiles could do that. This is useful for
%post scripts in RPMs and suchlike.
- This fixes various error path issues in conf_files_list()
-rw-r--r-- | man/sysctl.d.xml | 2 | ||||
-rw-r--r-- | src/binfmt/binfmt.c | 130 | ||||
-rw-r--r-- | src/modules-load/modules-load.c | 181 | ||||
-rw-r--r-- | src/shared/conf-files.c | 112 | ||||
-rw-r--r-- | src/shared/conf-files.h | 1 | ||||
-rw-r--r-- | src/shared/hashmap.c | 27 | ||||
-rw-r--r-- | src/shared/hashmap.h | 2 | ||||
-rw-r--r-- | src/shared/path-util.c | 10 | ||||
-rw-r--r-- | src/shared/path-util.h | 1 | ||||
-rw-r--r-- | src/shared/strv.c | 16 | ||||
-rw-r--r-- | src/shared/strv.h | 1 | ||||
-rw-r--r-- | src/shared/util.c | 79 | ||||
-rw-r--r-- | src/shared/util.h | 3 | ||||
-rw-r--r-- | src/sysctl/sysctl.c | 153 | ||||
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 88 |
15 files changed, 516 insertions, 290 deletions
diff --git a/man/sysctl.d.xml b/man/sysctl.d.xml index 69aac8cab7..0ef5545163 100644 --- a/man/sysctl.d.xml +++ b/man/sysctl.d.xml @@ -92,7 +92,7 @@ alphabetical order, regardless in which of the directories they reside, to guarantee that a specific configuration file takes precedence over another file - with an alphabetically earlier name, if both files + with an alphabetically later name, if both files contain the same variable setting.</para> <para>If the administrator wants to disable a diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c index 296607d6a2..f8c97b5ca5 100644 --- a/src/binfmt/binfmt.c +++ b/src/binfmt/binfmt.c @@ -26,6 +26,7 @@ #include <stdio.h> #include <limits.h> #include <stdarg.h> +#include <getopt.h> #include "log.h" #include "hashmap.h" @@ -33,28 +34,34 @@ #include "util.h" #include "conf-files.h" +static const char conf_file_dirs[] = + "/etc/binfmt.d\0" + "/run/binfmt.d\0" + "/usr/local/lib/binfmt.d\0" + "/usr/lib/binfmt.d\0" +#ifdef HAVE_SPLIT_USR + "/lib/binfmt.d\0" +#endif + ; + static int delete_rule(const char *rule) { - char *x, *fn = NULL, *e; - int r; + _cleanup_free_ char *x = NULL, *fn = NULL; + char *e; assert(rule[0]); - if (!(x = strdup(rule))) + x = strdup(rule); + if (!x) return log_oom(); e = strchrnul(x+1, x[0]); *e = 0; - asprintf(&fn, "/proc/sys/fs/binfmt_misc/%s", x+1); - free(x); - + fn = strappend("/proc/sys/fs/binfmt_misc/", x+1); if (!fn) return log_oom(); - r = write_one_line_file(fn, "-1"); - free(fn); - - return r; + return write_one_line_file(fn, "-1"); } static int apply_rule(const char *rule) { @@ -62,7 +69,8 @@ static int apply_rule(const char *rule) { delete_rule(rule); - if ((r = write_one_line_file("/proc/sys/fs/binfmt_misc/register", rule)) < 0) { + r = write_one_line_file("/proc/sys/fs/binfmt_misc/register", rule); + if (r < 0) { log_error("Failed to add binary format: %s", strerror(-r)); return r; } @@ -71,21 +79,22 @@ static int apply_rule(const char *rule) { } static int apply_file(const char *path, bool ignore_enoent) { - FILE *f; - int r = 0; + _cleanup_fclose_ FILE *f = NULL; + int r; assert(path); - if (!(f = fopen(path, "re"))) { - if (ignore_enoent && errno == ENOENT) + r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) return 0; - log_error("Failed to open file '%s', ignoring: %m", path); - return -errno; + log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r)); + return r; } log_debug("apply: %s\n", path); - while (!feof(f)) { + for (;;) { char l[LINE_MAX], *p; int k; @@ -94,30 +103,71 @@ static int apply_file(const char *path, bool ignore_enoent) { break; log_error("Failed to read file '%s', ignoring: %m", path); - r = -errno; - goto finish; + return -errno; } p = strstrip(l); - if (!*p) continue; - if (strchr(COMMENTS, *p)) continue; - if ((k = apply_rule(p)) < 0 && r == 0) + k = apply_rule(p); + if (k < 0 && r == 0) r = k; } -finish: - fclose(f); - return r; } +static int help(void) { + + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Registers binary formats.\n\n" + " -h --help Show this help\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + int main(int argc, char *argv[]) { - int r = 0; + int r, k; + + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); @@ -125,28 +175,21 @@ int main(int argc, char *argv[]) { umask(0022); - if (argc > 1) { - int i; + r = 0; - for (i = 1; i < argc; i++) { - int k; + if (argc > optind) { + int i; + for (i = optind; i < argc; i++) { k = apply_file(argv[i], false); if (k < 0 && r == 0) r = k; } } else { - char **files, **f; + _cleanup_strv_free_ char **files = NULL; + char **f; - r = conf_files_list(&files, ".conf", NULL, - "/etc/binfmt.d", - "/run/binfmt.d", - "/usr/local/lib/binfmt.d", - "/usr/lib/binfmt.d", -#ifdef HAVE_SPLIT_USR - "/lib/binfmt.d", -#endif - NULL); + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); if (r < 0) { log_error("Failed to enumerate binfmt.d files: %s", strerror(-r)); goto finish; @@ -156,15 +199,12 @@ int main(int argc, char *argv[]) { write_one_line_file("/proc/sys/fs/binfmt_misc/status", "-1"); STRV_FOREACH(f, files) { - int k; - k = apply_file(*f, true); if (k < 0 && r == 0) r = k; } - - strv_free(files); } + finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c index 936aaed8e4..88b1261494 100644 --- a/src/modules-load/modules-load.c +++ b/src/modules-load/modules-load.c @@ -26,6 +26,7 @@ #include <sys/stat.h> #include <limits.h> #include <dirent.h> +#include <getopt.h> #include <libkmod.h> #include "log.h" @@ -36,6 +37,16 @@ static char **arg_proc_cmdline_modules = NULL; +static const char conf_file_dirs[] = + "/etc/modules-load.d\0" + "/run/modules-load.d\0" + "/usr/local/lib/modules-load.d\0" + "/usr/lib/modules-load.d\0" +#ifdef HAVE_SPLIT_USR + "/lib/modules-load.d\0" +#endif + ; + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" static void systemd_kmod_log(void *data, int priority, const char *file, int line, @@ -46,14 +57,14 @@ static void systemd_kmod_log(void *data, int priority, const char *file, int lin #pragma GCC diagnostic pop static int add_modules(const char *p) { - char **t, **k; + char **t; + _cleanup_strv_free_ char **k = NULL; k = strv_split(p, ","); if (!k) return log_oom(); t = strv_merge(arg_proc_cmdline_modules, k); - strv_free(k); if (!t) return log_oom(); @@ -162,15 +173,98 @@ static int load_module(struct kmod_ctx *ctx, const char *m) { return r; } +static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) { + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(ctx); + assert(path); + + r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) + return 0; + + log_error("Failed to open %s, ignoring: %s", path, strerror(-r)); + return r; + } + + log_debug("apply: %s\n", path); + for (;;) { + char line[LINE_MAX], *l; + int k; + + if (!fgets(line, sizeof(line), f)) { + if (feof(f)) + break; + + log_error("Failed to read file '%s', ignoring: %m", path); + return -errno; + } + + l = strstrip(line); + if (!*l) + continue; + if (strchr(COMMENTS, *l)) + continue; + + k = load_module(ctx, l); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static int help(void) { + + printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n" + "Loads statically configured kernel modules.\n\n" + " -h --help Show this help\n", + program_invocation_short_name); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case '?': + return -EINVAL; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + int main(int argc, char *argv[]) { - int r = EXIT_FAILURE, k; - char **files = NULL, **fn, **i; + int r, k; struct kmod_ctx *ctx; - if (argc > 1) { - log_error("This program takes no argument."); - return EXIT_FAILURE; - } + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); @@ -190,70 +284,43 @@ int main(int argc, char *argv[]) { kmod_load_resources(ctx); kmod_set_log_fn(ctx, systemd_kmod_log, NULL); - r = EXIT_SUCCESS; + r = 0; - STRV_FOREACH(i, arg_proc_cmdline_modules) { - k = load_module(ctx, *i); - if (k < 0) - r = EXIT_FAILURE; - } - - k = conf_files_list(&files, ".conf", NULL, - "/etc/modules-load.d", - "/run/modules-load.d", - "/usr/local/lib/modules-load.d", - "/usr/lib/modules-load.d", -#ifdef HAVE_SPLIT_USR - "/lib/modules-load.d", -#endif - NULL); - if (k < 0) { - log_error("Failed to enumerate modules-load.d files: %s", strerror(-k)); - r = EXIT_FAILURE; - goto finish; - } - - STRV_FOREACH(fn, files) { - FILE *f; + if (argc > optind) { + int i; - f = fopen(*fn, "re"); - if (!f) { - if (errno == ENOENT) - continue; - - log_error("Failed to open %s: %m", *fn); - r = EXIT_FAILURE; - continue; + for (i = optind; i < argc; i++) { + k = apply_file(ctx, argv[i], false); + if (k < 0 && r == 0) + r = k; } - log_debug("apply: %s\n", *fn); - for (;;) { - char line[LINE_MAX], *l; - - if (!fgets(line, sizeof(line), f)) - break; + } else { + _cleanup_free_ char **files = NULL; + char **fn, **i; - l = strstrip(line); - if (*l == '#' || *l == 0) - continue; - - k = load_module(ctx, l); + STRV_FOREACH(i, arg_proc_cmdline_modules) { + k = load_module(ctx, *i); if (k < 0) r = EXIT_FAILURE; } - if (ferror(f)) { - log_error("Failed to read from file: %m"); - r = EXIT_FAILURE; + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (r < 0) { + log_error("Failed to enumerate modules-load.d files: %s", strerror(-r)); + goto finish; } - fclose(f); + STRV_FOREACH(fn, files) { + k = apply_file(ctx, *fn, true); + if (k < 0 && r == 0) + r = k; + } } finish: - strv_free(files); kmod_unref(ctx); strv_free(arg_proc_cmdline_modules); - return r; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c index 5bbd2388d3..296e605761 100644 --- a/src/shared/conf-files.c +++ b/src/shared/conf-files.c @@ -38,7 +38,7 @@ #include "conf-files.h" static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) { - _cleanup_closedir_ DIR *dir; + _cleanup_closedir_ DIR *dir = NULL; _cleanup_free_ char *dirpath = NULL; if (asprintf(&dirpath, "%s%s", root ? root : "", path) < 0) @@ -55,11 +55,11 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char struct dirent *de; union dirent_storage buf; char *p; - int err; + int r; - err = readdir_r(dir, &buf.de, &de); - if (err != 0) - return err; + r = readdir_r(dir, &buf.de, &de); + if (r != 0) + return -r; if (!de) break; @@ -67,11 +67,19 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char if (!dirent_is_file_with_suffix(de, suffix)) continue; - if (asprintf(&p, "%s/%s", dirpath, de->d_name) < 0) + p = strjoin(dirpath, "/", de->d_name, NULL); + if (!p) return -ENOMEM; - if (hashmap_put(h, path_get_file_name(p), p) <= 0) { - log_debug("Skip overridden file: %s.", p); + r = hashmap_put(h, path_get_file_name(p), p); + if (r == -EEXIST) { + log_debug("Skipping overridden file: %s.", p); + free(p); + } else if (r < 0) { + free(p); + return r; + } else if (r == 0) { + log_debug("Duplicate file %s", p); free(p); } } @@ -87,64 +95,84 @@ static int base_cmp(const void *a, const void *b) { return strcmp(path_get_file_name(s1), path_get_file_name(s2)); } -int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char **dirs) { - Hashmap *fh = NULL; - char **files = NULL; - const char **p; +static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) { + Hashmap *fh; + char **files, **p; int r; - assert(dirs); + assert(strv); + assert(suffix); + + /* This alters the dirs string array */ + if (!path_strv_canonicalize_uniq(dirs)) + return -ENOMEM; fh = hashmap_new(string_hash_func, string_compare_func); - if (!fh) { - r = -ENOMEM; - goto finish; - } + if (!fh) + return -ENOMEM; STRV_FOREACH(p, dirs) { r = files_add(fh, root, *p, suffix); - if (r < 0) - log_warning("Failed to search for files in %s: %s", - *p, strerror(-r)); + if (r == -ENOMEM) { + hashmap_free_free(fh); + return r; + } else if (r < 0) + log_debug("Failed to search for files in %s: %s", + *p, strerror(-r)); } files = hashmap_get_strv(fh); if (files == NULL) { - log_error("Failed to compose list of files."); - r = -ENOMEM; - goto finish; + hashmap_free_free(fh); + return -ENOMEM; } + qsort(files, hashmap_size(fh), sizeof(char *), base_cmp); - r = 0; + *strv = files; -finish: hashmap_free(fh); - *strv = files; - return r; + return 0; +} + +int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char **dirs) { + _cleanup_strv_free_ char **copy = NULL; + + assert(strv); + assert(suffix); + + copy = strv_copy((char**) dirs); + if (!copy) + return -ENOMEM; + + return conf_files_list_strv_internal(strv, suffix, root, copy); } int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) { - char **dirs = NULL; + _cleanup_strv_free_ char **dirs = NULL; va_list ap; - int r; + + assert(strv); + assert(suffix); va_start(ap, dir); dirs = strv_new_ap(dir, ap); va_end(ap); - if (!dirs) { - r = -ENOMEM; - goto finish; - } - if (!path_strv_canonicalize(dirs)) { - r = -ENOMEM; - goto finish; - } - strv_uniq(dirs); + if (!dirs) + return -ENOMEM; + + return conf_files_list_strv_internal(strv, suffix, root, dirs); +} - r = conf_files_list_strv(strv, suffix, root, (const char **)dirs); +int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) { + _cleanup_strv_free_ char **dirs = NULL; + + assert(strv); + assert(suffix); + + dirs = strv_split_nulstr(d); + if (!dirs) + return -ENOMEM; -finish: - strv_free(dirs); - return r; + return conf_files_list_strv_internal(strv, suffix, root, dirs); } diff --git a/src/shared/conf-files.h b/src/shared/conf-files.h index 4d7941f4cb..28588e6f03 100644 --- a/src/shared/conf-files.h +++ b/src/shared/conf-files.h @@ -27,5 +27,6 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...); int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char **dirs); +int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *dirs); #endif diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c index a2c728d642..9f7db34397 100644 --- a/src/shared/hashmap.c +++ b/src/shared/hashmap.c @@ -309,6 +309,17 @@ void hashmap_free_free(Hashmap *h) { hashmap_free(h); } +void hashmap_free_free_free(Hashmap *h) { + + /* Free the hashmap and all data and key objects in it */ + + if (!h) + return; + + hashmap_clear_free_free(h); + hashmap_free(h); +} + void hashmap_clear(Hashmap *h) { if (!h) return; @@ -327,6 +338,22 @@ void hashmap_clear_free(Hashmap *h) { free(p); } +void hashmap_clear_free_free(Hashmap *h) { + if (!h) + return; + + while (h->iterate_list_head) { + void *a, *b; + + a = h->iterate_list_head->value; + b = (void*) h->iterate_list_head->key; + remove_entry(h, h->iterate_list_head); + free(a); + free(b); + } +} + + static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) { struct hashmap_entry *e; assert(h); diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h index 6fd71cf519..26bd03096d 100644 --- a/src/shared/hashmap.h +++ b/src/shared/hashmap.h @@ -50,6 +50,7 @@ int uint64_compare_func(const void *a, const void *b); Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func); void hashmap_free(Hashmap *h); void hashmap_free_free(Hashmap *h); +void hashmap_free_free_free(Hashmap *h); Hashmap *hashmap_copy(Hashmap *h); int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func); @@ -77,6 +78,7 @@ void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i); void hashmap_clear(Hashmap *h); void hashmap_clear_free(Hashmap *h); +void hashmap_clear_free_free(Hashmap *h); void *hashmap_steal_first(Hashmap *h); void *hashmap_steal_first_key(Hashmap *h); diff --git a/src/shared/path-util.c b/src/shared/path-util.c index 52ce65de6d..0b50ea646a 100644 --- a/src/shared/path-util.c +++ b/src/shared/path-util.c @@ -215,6 +215,16 @@ char **path_strv_canonicalize(char **l) { return l; } +char **path_strv_canonicalize_uniq(char **l) { + if (strv_isempty(l)) + return l; + + if (!path_strv_canonicalize(l)) + return NULL; + + return strv_uniq(l); +} + char *path_kill_slashes(char *path) { char *f, *t; bool slash = false; diff --git a/src/shared/path-util.h b/src/shared/path-util.h index e37ab9350b..ff523943dc 100644 --- a/src/shared/path-util.h +++ b/src/shared/path-util.h @@ -37,6 +37,7 @@ bool path_equal(const char *a, const char *b); char **path_strv_make_absolute_cwd(char **l); char **path_strv_canonicalize(char **l); +char **path_strv_canonicalize_uniq(char **l); int path_is_mount_point(const char *path, bool allow_symlink); int path_is_read_only_fs(const char *path); diff --git a/src/shared/strv.c b/src/shared/strv.c index ee0b71ece0..ec25755289 100644 --- a/src/shared/strv.c +++ b/src/shared/strv.c @@ -504,6 +504,22 @@ char **strv_parse_nulstr(const char *s, size_t l) { return v; } +char **strv_split_nulstr(const char *s) { + const char *i; + char **r = NULL; + + NULSTR_FOREACH(i, s) + if (strv_extend(&r, i) < 0) { + strv_free(r); + return NULL; + } + + if (!r) + return strv_new(NULL, NULL); + + return r; +} + bool strv_overlap(char **a, char **b) { char **i, **j; diff --git a/src/shared/strv.h b/src/shared/strv.h index d28625bd2f..b3802a7a3f 100644 --- a/src/shared/strv.h +++ b/src/shared/strv.h @@ -62,6 +62,7 @@ char **strv_split_quoted(const char *s) _malloc_; char *strv_join(char **l, const char *separator) _malloc_; char **strv_parse_nulstr(const char *s, size_t l); +char **strv_split_nulstr(const char *s); bool strv_overlap(char **a, char **b); diff --git a/src/shared/util.c b/src/shared/util.c index 29cb9f1e8d..24f9e7ee58 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -5909,3 +5909,82 @@ int on_ac_power(void) { return found_online || !found_offline; } + +static int search_and_fopen_internal(const char *path, const char *mode, char **search, FILE **_f) { + char **i; + + assert(path); + assert(mode); + assert(_f); + + if (!path_strv_canonicalize_uniq(search)) + return -ENOMEM; + + STRV_FOREACH(i, search) { + _cleanup_free_ char *p = NULL; + FILE *f; + + p = strjoin(*i, "/", path, NULL); + if (!p) + return -ENOMEM; + + f = fopen(p, mode); + if (f) { + *_f = f; + return 0; + } + + if (errno != ENOENT) + return -errno; + } + + return -ENOENT; +} + +int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f) { + _cleanup_strv_free_ char **copy = NULL; + + assert(path); + assert(mode); + assert(_f); + + if (path_is_absolute(path)) { + FILE *f; + + f = fopen(path, mode); + if (f) { + *_f = f; + return 0; + } + + return -errno; + } + + copy = strv_copy((char**) search); + if (!copy) + return -ENOMEM; + + return search_and_fopen_internal(path, mode, copy, _f); +} + +int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f) { + _cleanup_strv_free_ char **s = NULL; + + if (path_is_absolute(path)) { + FILE *f; + + f = fopen(path, mode); + if (f) { + *_f = f; + return 0; + } + + return -errno; + } + + s = strv_split_nulstr(search); + if (!s) + return -ENOMEM; + + return search_and_fopen_internal(path, mode, s, _f); +} diff --git a/src/shared/util.h b/src/shared/util.h index d926b01919..cd13457528 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -568,6 +568,9 @@ char *strip_tab_ansi(char **p, size_t *l); int on_ac_power(void); +int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f); +int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f); + #define FOREACH_LINE(f, line, on_error) \ for (char line[LINE_MAX]; !feof(f); ) \ if (!fgets(line, sizeof(line), f)) { \ diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index f5ef89b385..f59a858323 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -35,28 +35,42 @@ #include "path-util.h" #include "conf-files.h" -#define PROC_SYS_PREFIX "/proc/sys/" +static char **arg_prefixes = NULL; -static char **arg_prefixes; -static Hashmap *sysctl_options; +static const char conf_file_dirs[] = + "/etc/sysctl.d\0" + "/run/sysctl.d\0" + "/usr/local/lib/sysctl.d\0" + "/usr/lib/sysctl.d\0" +#ifdef HAVE_SPLIT_USR + "/lib/sysctl.d\0" +#endif + ; + +static char *normalize_sysctl(char *s) { + char *n; + + for (n = s; *n; n++) + if (*n == '.') + *n = '/'; + + return s; +} static int apply_sysctl(const char *property, const char *value) { - char *p, *n; + _cleanup_free_ char *p = NULL; + char *n; int r = 0, k; log_debug("Setting '%s' to '%s'", property, value); - p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property)); + p = new(char, sizeof("/proc/sys/") + strlen(property)); if (!p) return log_oom(); - n = stpcpy(p, PROC_SYS_PREFIX); + n = stpcpy(p, "/proc/sys/"); strcpy(n, property); - for (; *n; n++) - if (*n == '.') - *n = '/'; - if (!strv_isempty(arg_prefixes)) { char **i; bool good = false; @@ -69,14 +83,12 @@ static int apply_sysctl(const char *property, const char *value) { if (!good) { log_debug("Skipping %s", p); - free(p); return 0; } } k = write_one_line_file(p, value); if (k < 0) { - log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to write '%s' to '%s': %s", value, p, strerror(-k)); @@ -84,16 +96,16 @@ static int apply_sysctl(const char *property, const char *value) { r = k; } - free(p); - return r; } -static int apply_all(void) { +static int apply_all(Hashmap *sysctl_options) { int r = 0; char *property, *value; Iterator i; + assert(sysctl_options); + HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) { int k; @@ -104,36 +116,35 @@ static int apply_all(void) { return r; } -static int parse_file(const char *path, bool ignore_enoent) { - FILE *f; - int r = 0; +static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) { + _cleanup_fclose_ FILE *f = NULL; + int r; assert(path); - f = fopen(path, "re"); - if (!f) { - if (ignore_enoent && errno == ENOENT) + r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && errno == -ENOENT) return 0; - log_error("Failed to open file '%s', ignoring: %m", path); - return -errno; + log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r)); + return r; } log_debug("parse: %s\n", path); while (!feof(f)) { - char l[LINE_MAX], *p, *value, *new_value, *property; + char l[LINE_MAX], *p, *value, *new_value, *property, *existing; + int k; if (!fgets(l, sizeof(l), f)) { if (feof(f)) break; log_error("Failed to read file '%s', ignoring: %m", path); - r = -errno; - goto finish; + return -errno; } p = strstrip(l); - if (!*p) continue; @@ -152,40 +163,36 @@ static int parse_file(const char *path, bool ignore_enoent) { *value = 0; value++; - property = strdup(strstrip(p)); - if (!property) { - r = log_oom(); - goto finish; + p = normalize_sysctl(strstrip(p)); + value = strstrip(value); + + existing = hashmap_get(sysctl_options, p); + if (existing) { + if (!streq(value, existing)) + log_warning("Two ore more conflicting assignments of %s, ignoring.", property); + + continue; } - new_value = strdup(strstrip(value)); + property = strdup(p); + if (!property) + return log_oom(); + + new_value = strdup(value); if (!new_value) { free(property); - r = log_oom(); - goto finish; + return log_oom(); } - r = hashmap_put(sysctl_options, property, new_value); - if (r < 0) { - if (r == -EEXIST) { - /* ignore this "error" to avoid returning it - * for the function when this is the last key - * in the file being parsed. */ - r = 0; - log_debug("Skipping previously assigned sysctl variable %s", property); - } else - log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-r)); - + k = hashmap_put(sysctl_options, property, new_value); + if (k < 0) { + log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-r)); free(property); free(new_value); - if (r != 0) - goto finish; + return k; } } -finish: - fclose(f); - return r; } @@ -257,8 +264,7 @@ static int parse_argv(int argc, char *argv[]) { int main(int argc, char *argv[]) { int r = 0, k; - char *property, *value; - Iterator it; + Hashmap *sysctl_options; r = parse_argv(argc, argv); if (r <= 0) @@ -282,54 +288,35 @@ int main(int argc, char *argv[]) { int i; for (i = optind; i < argc; i++) { - k = parse_file(argv[i], false); - if (k < 0) + k = parse_file(sysctl_options, argv[i], false); + if (k < 0 && r == 0) r = k; } } else { - char **files, **f; + _cleanup_strv_free_ char **files = NULL; + char **f; - r = conf_files_list(&files, ".conf", NULL, - "/etc/sysctl.d", - "/run/sysctl.d", - "/usr/local/lib/sysctl.d", - "/usr/lib/sysctl.d", -#ifdef HAVE_SPLIT_USR - "/lib/sysctl.d", -#endif - NULL); + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); if (r < 0) { log_error("Failed to enumerate sysctl.d files: %s", strerror(-r)); goto finish; } - /* We parse the files in decreasing order of precedence. - * parse_file() will skip keys that were already assigned. */ + r = parse_file(sysctl_options, "/etc/sysctl.conf", true); - r = parse_file("/etc/sysctl.conf", true); - - f = files + strv_length(files) - 1; - STRV_FOREACH_BACKWARDS(f, files) { - k = parse_file(*f, true); - if (k < 0) + STRV_FOREACH(f, files) { + k = parse_file(sysctl_options, *f, true); + if (k < 0 && r == 0) r = k; } - - strv_free(files); } - k = apply_all(); - if (k < 0) + k = apply_all(sysctl_options); + if (k < 0 && r == 0) r = k; finish: - HASHMAP_FOREACH_KEY(value, property, sysctl_options, it) { - hashmap_remove(sysctl_options, property); - free(property); - free(value); - } - hashmap_free(sysctl_options); - + hashmap_free_free_free(sysctl_options); strv_free(arg_prefixes); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 96adbff42e..6b3f70e071 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -106,16 +106,15 @@ static bool arg_remove = false; static const char *arg_prefix = NULL; -static const char * const conf_file_dirs[] = { - "/etc/tmpfiles.d", - "/run/tmpfiles.d", - "/usr/local/lib/tmpfiles.d", - "/usr/lib/tmpfiles.d", +static const char conf_file_dirs[] = + "/etc/tmpfiles.d\0" + "/run/tmpfiles.d\0" + "/usr/local/lib/tmpfiles.d\0" + "/usr/lib/tmpfiles.d\0" #ifdef HAVE_SPLIT_USR - "/lib/tmpfiles.d", + "/lib/tmpfiles.d\0" #endif - NULL -}; + ; #define MAX_DEPTH 256 @@ -1289,20 +1288,19 @@ static int parse_argv(int argc, char *argv[]) { static int read_config_file(const char *fn, bool ignore_enoent) { FILE *f; unsigned v = 0; - int r = 0; + int r; Iterator iterator; Item *i; assert(fn); - f = fopen(fn, "re"); - if (!f) { - - if (ignore_enoent && errno == ENOENT) + r = search_and_fopen_nulstr(fn, "re", conf_file_dirs, &f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) return 0; - log_error("Failed to open %s: %m", fn); - return -errno; + log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r)); + return r; } log_debug("apply: %s\n", fn); @@ -1363,32 +1361,8 @@ static int read_config_file(const char *fn, bool ignore_enoent) { return r; } -static char *resolve_fragment(const char *fragment, const char **search_paths) { - const char **p; - char *resolved_path; - - if (is_path(fragment)) - return strdup(fragment); - - STRV_FOREACH(p, search_paths) { - resolved_path = strjoin(*p, "/", fragment, NULL); - if (resolved_path == NULL) { - log_oom(); - return NULL; - } - - if (access(resolved_path, F_OK) == 0) - return resolved_path; - - free(resolved_path); - } - - errno = ENOENT; - return NULL; -} - int main(int argc, char *argv[]) { - int r; + int r, k; Item *i; Iterator iterator; @@ -1408,46 +1382,36 @@ int main(int argc, char *argv[]) { globs = hashmap_new(string_hash_func, string_compare_func); if (!items || !globs) { - log_oom(); - r = EXIT_FAILURE; + r = log_oom(); goto finish; } - r = EXIT_SUCCESS; + r = 0; if (optind < argc) { int j; for (j = optind; j < argc; j++) { - char *fragment; - - fragment = resolve_fragment(argv[j], (const char **)conf_file_dirs); - if (!fragment) { - log_error("Failed to find a %s file: %m", argv[j]); - r = EXIT_FAILURE; - goto finish; - } - if (read_config_file(fragment, false) < 0) - r = EXIT_FAILURE; - free(fragment); + k = read_config_file(argv[j], false); + if (k < 0 && r == 0) + r = k; } } else { - char **files, **f; + _cleanup_strv_free_ char **files = NULL; + char **f; - r = conf_files_list_strv(&files, ".conf", NULL, (const char **)conf_file_dirs); + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); if (r < 0) { log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r)); - r = EXIT_FAILURE; goto finish; } STRV_FOREACH(f, files) { - if (read_config_file(*f, true) < 0) - r = EXIT_FAILURE; + k = read_config_file(*f, true); + if (k < 0 && r == 0) + r = k; } - - strv_free(files); } HASHMAP_FOREACH(i, globs, iterator) @@ -1470,5 +1434,5 @@ finish: label_finish(); - return r; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } |