diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2020-01-16 19:38:21 +0100 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2020-02-04 00:01:50 +0100 |
commit | e0f424790d3dbde136a29a7fa4c2777c2e3fd695 (patch) | |
tree | 39773eb654ef830812b5c58ccd66227cec60c4f5 /src | |
parent | man: add syntax quickhelp to sysctl.d(5) (diff) | |
download | systemd-e0f424790d3dbde136a29a7fa4c2777c2e3fd695.tar.xz systemd-e0f424790d3dbde136a29a7fa4c2777c2e3fd695.zip |
sysctl: add glob syntax to sysctl.d files
This is intended for net.*.conf.*.foo files. Setting just "default" is not very
useful because any interfaces present before systemd-sysctl is invoked are not
affected. Setting "all" is too harsh, because the kernel takes the stronger of
the device-specific setting and the "all" value, so effectively having a weaker
setting for specific interfaces is not possible. Let's add a way in which can
set "default" first and then all the others without "all".
Diffstat (limited to '')
-rw-r--r-- | src/sysctl/sysctl.c | 168 |
1 files changed, 117 insertions, 51 deletions
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index afef0a222b..bbcf0c4323 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -14,6 +14,7 @@ #include "errno-util.h" #include "fd-util.h" #include "fileio.h" +#include "glob-util.h" #include "hashmap.h" #include "log.h" #include "main-func.h" @@ -49,6 +50,26 @@ static Option *option_free(Option *o) { DEFINE_TRIVIAL_CLEANUP_FUNC(Option*, option_free); DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(option_hash_ops, char, string_hash_func, string_compare_func, Option, option_free); +static bool test_prefix(const char *p) { + char **i; + + if (strv_isempty(arg_prefixes)) + return true; + + STRV_FOREACH(i, arg_prefixes) { + const char *t; + + t = path_startswith(*i, "/proc/sys/"); + if (!t) + t = *i; + + if (path_startswith(p, t)) + return true; + } + + return false; +} + static Option *option_new( const char *key, const char *value, @@ -57,7 +78,6 @@ static Option *option_new( _cleanup_(option_freep) Option *o = NULL; assert(key); - assert(value); o = new(Option, 1); if (!o) @@ -65,16 +85,38 @@ static Option *option_new( *o = (Option) { .key = strdup(key), - .value = strdup(value), + .value = value ? strdup(value) : NULL, .ignore_failure = ignore_failure, }; - if (!o->key || !o->value) + if (!o->key) + return NULL; + if (value && !o->value) return NULL; return TAKE_PTR(o); } +static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_failure) { + int r; + + r = sysctl_write(key, value); + if (r < 0) { + /* If the sysctl is not available in the kernel or we are running with reduced privileges and + * cannot write it, then log about the issue, and proceed without failing. (EROFS is treated + * as a permission problem here, since that's how container managers usually protected their + * sysctls.) In all other cases log an error and make the tool fail. */ + if (ignore_failure || r == -EROFS || ERRNO_IS_PRIVILEGE(r)) + log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key); + else if (r == -ENOENT) + log_info_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key); + else + return log_error_errno(r, "Couldn't write '%s' to '%s': %m", value, key); + } + + return 0; +} + static int apply_all(OrderedHashmap *sysctl_options) { Option *option; Iterator i; @@ -83,46 +125,60 @@ static int apply_all(OrderedHashmap *sysctl_options) { ORDERED_HASHMAP_FOREACH(option, sysctl_options, i) { int k; - k = sysctl_write(option->key, option->value); - if (k < 0) { - /* If the sysctl is not available in the kernel or we are running with reduced - * privileges and cannot write it, then log about the issue, and proceed without - * failing. (EROFS is treated as a permission problem here, since that's how - * container managers usually protected their sysctls.) In all other cases log an - * error and make the tool fail. */ - - if (option->ignore_failure || k == -EROFS || ERRNO_IS_PRIVILEGE(k)) - log_debug_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", option->value, option->key); - else if (k == -ENOENT) - log_info_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", option->value, option->key); - else { - log_error_errno(k, "Couldn't write '%s' to '%s': %m", option->value, option->key); - if (r == 0) - r = k; - } - } - } + /* Ignore "negative match" options, they are there only to exclude stuff from globs. */ + if (!option->value) + continue; - return r; -} + if (string_is_glob(option->key)) { + _cleanup_strv_free_ char **paths = NULL; + _cleanup_free_ char *pattern = NULL; + char **s; -static bool test_prefix(const char *p) { - char **i; + pattern = path_join("/proc/sys", option->key); + if (!pattern) + return log_oom(); - if (strv_isempty(arg_prefixes)) - return true; + k = glob_extend(&paths, pattern); + if (k < 0) { + if (option->ignore_failure || ERRNO_IS_PRIVILEGE(r)) + log_debug_errno(k, "Failed to resolve glob '%s', ignoring: %m", + option->key); + else { + log_error_errno(k, "Couldn't resolve glob '%s': %m", + option->key); + if (r == 0) + r = k; + } - STRV_FOREACH(i, arg_prefixes) { - const char *t; + } else if (strv_isempty(paths)) + log_debug("No match for glob: %s", option->key); - t = path_startswith(*i, "/proc/sys/"); - if (!t) - t = *i; - if (path_startswith(p, t)) - return true; + STRV_FOREACH(s, paths) { + const char *key; + + assert_se(key = path_startswith(*s, "/proc/sys")); + + if (!test_prefix(key)) + continue; + + if (ordered_hashmap_contains(sysctl_options, key)) { + log_info("Not setting %s (explicit setting exists).", key); + continue; + } + + k = sysctl_write_or_warn(key, option->value, option->ignore_failure); + if (r == 0) + r = k; + } + + } else { + k = sysctl_write_or_warn(option->key, option->value, option->ignore_failure); + if (r == 0) + r = k; + } } - return false; + return r; } static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent) { @@ -144,7 +200,7 @@ static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ig for (;;) { _cleanup_(option_freep) Option *new_option = NULL; _cleanup_free_ char *l = NULL; - bool ignore_failure; + bool ignore_failure = false; Option *existing; char *p, *value; int k; @@ -165,25 +221,35 @@ static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ig continue; value = strchr(p, '='); - if (!value) { - log_syntax(NULL, LOG_WARNING, path, c, 0, "Line is not an assignment, ignoring: %s", p); - if (r == 0) - r = -EINVAL; - continue; - } + if (value) { + if (p[0] == '-') { + ignore_failure = true; + p++; + } - *value = 0; - value++; + *value = 0; + value++; + value = strstrip(value); - p = strstrip(p); - ignore_failure = p[0] == '-'; - if (ignore_failure) - p++; + } else { + if (p[0] == '-') + /* We have a "negative match" option. Let's continue with value==NULL. */ + p++; + else { + log_syntax(NULL, LOG_WARNING, path, c, 0, + "Line is not an assignment, ignoring: %s", p); + if (r == 0) + r = -EINVAL; + continue; + } + } + p = strstrip(p); p = sysctl_normalize(p); - value = strstrip(value); - if (!test_prefix(p)) + /* We can't filter out globs at this point, we'll need to do that later. */ + if (!string_is_glob(p) && + !test_prefix(p)) continue; if (ordered_hashmap_ensure_allocated(sysctl_options, &option_hash_ops) < 0) |