From 516fc73142e803a7a6cbd126c338e1c3c73d6843 Mon Sep 17 00:00:00 2001 From: Jan Macku Date: Fri, 21 Jan 2022 12:10:45 +0100 Subject: [PATCH] sysctl: if options are prefixed with "-" ignore write errors (cherry picked from commit dec02d6e1993d420a0a94c7fec294605df55e88e) Resolves: #2037807 --- src/sysctl/sysctl.c | 115 ++++++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 35 deletions(-) diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index 0151f7dabe..7b0528877c 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -26,25 +26,71 @@ static char **arg_prefixes = NULL; static bool arg_cat_config = false; static bool arg_no_pager = false; +typedef struct Option { + char *key; + char *value; + bool ignore_failure; +} Option; + +static Option *option_free(Option *o) { + if (!o) + return NULL; + + free(o->key); + free(o->value); + + return mfree(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 Option *option_new( + const char *key, + const char *value, + bool ignore_failure) { + + _cleanup_(option_freep) Option *o = NULL; + + assert(key); + assert(value); + + o = new(Option, 1); + if (!o) + return NULL; + + *o = (Option) { + .key = strdup(key), + .value = strdup(value), + .ignore_failure = ignore_failure, + }; + + if (!o->key || !o->value) + return NULL; + + return TAKE_PTR(o); +} + static int apply_all(OrderedHashmap *sysctl_options) { - char *property, *value; + Option *option; Iterator i; int r = 0; - ORDERED_HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) { + ORDERED_HASHMAP_FOREACH(option, sysctl_options, i) { int k; - k = sysctl_write(property, value); + 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 at LOG_NOTICE level, 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 (IN_SET(k, -EPERM, -EACCES, -EROFS, -ENOENT)) - log_notice_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", value, property); + /* 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 at LOG_NOTICE level, 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 (IN_SET(k, -EPERM, -EACCES, -EROFS, -ENOENT) || option->ignore_failure) + log_notice_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", value, property); + log_error_errno(k, "Couldn't write '%s' to '%s': %m", option->value, option->key); if (r == 0) r = k; } @@ -90,9 +136,11 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign log_debug("Parsing %s", path); for (;;) { - char *p, *value, *new_value, *property, *existing; + _cleanup_(option_freep) Option *new_option = NULL; _cleanup_free_ char *l = NULL; - void *v; + bool ignore_failure; + Option *existing; + char *p, *value; int k; k = read_line(f, LONG_LINE_MAX, &l); @@ -122,39 +170,37 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign *value = 0; value++; - p = sysctl_normalize(strstrip(p)); + p = strstrip(p); + ignore_failure = p[0] == '-'; + if (ignore_failure) + p++; + + p = sysctl_normalize(p); value = strstrip(value); if (!test_prefix(p)) continue; - existing = ordered_hashmap_get2(sysctl_options, p, &v); + existing = ordered_hashmap_get(sysctl_options, p); if (existing) { - if (streq(value, existing)) + if (streq(value, existing->value)) { + existing->ignore_failure = existing->ignore_failure || ignore_failure; continue; + } log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, path, c); - free(ordered_hashmap_remove(sysctl_options, p)); - free(v); + option_free(ordered_hashmap_remove(sysctl_options, p)); } - property = strdup(p); - if (!property) + new_option = option_new(p, value, ignore_failure); + if (!new_option) return log_oom(); - new_value = strdup(value); - if (!new_value) { - free(property); - return log_oom(); - } + k = ordered_hashmap_put(sysctl_options, new_option->key, new_option); + if (k < 0) + return log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", p); - k = ordered_hashmap_put(sysctl_options, property, new_value); - if (k < 0) { - log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property); - free(property); - free(new_value); - return k; - } + TAKE_PTR(new_option); } return r; @@ -251,7 +297,7 @@ static int parse_argv(int argc, char *argv[]) { } int main(int argc, char *argv[]) { - OrderedHashmap *sysctl_options = NULL; + _cleanup_(ordered_hashmap_freep) OrderedHashmap *sysctl_options = NULL; int r = 0, k; r = parse_argv(argc, argv); @@ -264,7 +310,7 @@ int main(int argc, char *argv[]) { umask(0022); - sysctl_options = ordered_hashmap_new(&path_hash_ops); + sysctl_options = ordered_hashmap_new(&option_hash_ops); if (!sysctl_options) { r = log_oom(); goto finish; @@ -311,7 +357,6 @@ int main(int argc, char *argv[]) { finish: pager_close(); - ordered_hashmap_free_free_free(sysctl_options); strv_free(arg_prefixes); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;