be0c12
From 516fc73142e803a7a6cbd126c338e1c3c73d6843 Mon Sep 17 00:00:00 2001
be0c12
From: Jan Macku <jamacku@redhat.com>
be0c12
Date: Fri, 21 Jan 2022 12:10:45 +0100
be0c12
Subject: [PATCH] sysctl: if options are prefixed with "-" ignore write errors
be0c12
be0c12
(cherry picked from commit dec02d6e1993d420a0a94c7fec294605df55e88e)
be0c12
be0c12
Resolves: #2037807
be0c12
---
be0c12
 src/sysctl/sysctl.c | 115 ++++++++++++++++++++++++++++++--------------
be0c12
 1 file changed, 80 insertions(+), 35 deletions(-)
be0c12
be0c12
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
be0c12
index 0151f7dabe..7b0528877c 100644
be0c12
--- a/src/sysctl/sysctl.c
be0c12
+++ b/src/sysctl/sysctl.c
be0c12
@@ -26,25 +26,71 @@ static char **arg_prefixes = NULL;
be0c12
 static bool arg_cat_config = false;
be0c12
 static bool arg_no_pager = false;
be0c12
 
be0c12
+typedef struct Option {
be0c12
+        char *key;
be0c12
+        char *value;
be0c12
+        bool ignore_failure;
be0c12
+} Option;
be0c12
+
be0c12
+static Option *option_free(Option *o) {
be0c12
+        if (!o)
be0c12
+                return NULL;
be0c12
+
be0c12
+        free(o->key);
be0c12
+        free(o->value);
be0c12
+
be0c12
+        return mfree(o);
be0c12
+}
be0c12
+
be0c12
+DEFINE_TRIVIAL_CLEANUP_FUNC(Option*, option_free);
be0c12
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(option_hash_ops, char, string_hash_func, string_compare_func, Option, option_free);
be0c12
+
be0c12
+static Option *option_new(
be0c12
+                const char *key,
be0c12
+                const char *value,
be0c12
+                bool ignore_failure) {
be0c12
+
be0c12
+        _cleanup_(option_freep) Option *o = NULL;
be0c12
+
be0c12
+        assert(key);
be0c12
+        assert(value);
be0c12
+
be0c12
+        o = new(Option, 1);
be0c12
+        if (!o)
be0c12
+                return NULL;
be0c12
+
be0c12
+        *o = (Option) {
be0c12
+                .key = strdup(key),
be0c12
+                .value = strdup(value),
be0c12
+                .ignore_failure = ignore_failure,
be0c12
+        };
be0c12
+
be0c12
+        if (!o->key || !o->value)
be0c12
+                return NULL;
be0c12
+
be0c12
+        return TAKE_PTR(o);
be0c12
+}
be0c12
+
be0c12
 static int apply_all(OrderedHashmap *sysctl_options) {
be0c12
-        char *property, *value;
be0c12
+        Option *option;
be0c12
         Iterator i;
be0c12
         int r = 0;
be0c12
 
be0c12
-        ORDERED_HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
be0c12
+        ORDERED_HASHMAP_FOREACH(option, sysctl_options, i) {
be0c12
                 int k;
be0c12
 
be0c12
-                k = sysctl_write(property, value);
be0c12
+                k = sysctl_write(option->key, option->value);
be0c12
                 if (k < 0) {
be0c12
-                        /* If the sysctl is not available in the kernel or we are running with reduced privileges and
be0c12
-                         * cannot write it, then log about the issue at LOG_NOTICE level, and proceed without
be0c12
-                         * failing. (EROFS is treated as a permission problem here, since that's how container managers
be0c12
-                         * usually protected their sysctls.) In all other cases log an error and make the tool fail. */
be0c12
-
be0c12
-                        if (IN_SET(k, -EPERM, -EACCES, -EROFS, -ENOENT))
be0c12
-                                log_notice_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", value, property);
be0c12
+                        /* If the sysctl is not available in the kernel or we are running with reduced
be0c12
+                         * privileges and cannot write it, then log about the issue at LOG_NOTICE level, and
be0c12
+                         * proceed without failing. (EROFS is treated as a permission problem here, since
be0c12
+                         * that's how container managers usually protected their sysctls.) In all other cases
be0c12
+                         * log an error and make the tool fail. */
be0c12
+
be0c12
+                        if (IN_SET(k, -EPERM, -EACCES, -EROFS, -ENOENT) || option->ignore_failure)
be0c12
+                                log_notice_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", option->value, option->key);
be0c12
                         else {
be0c12
-                                log_error_errno(k, "Couldn't write '%s' to '%s': %m", value, property);
be0c12
+                                log_error_errno(k, "Couldn't write '%s' to '%s': %m", option->value, option->key);
be0c12
                                 if (r == 0)
be0c12
                                         r = k;
be0c12
                         }
be0c12
@@ -90,9 +136,11 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
be0c12
 
be0c12
         log_debug("Parsing %s", path);
be0c12
         for (;;) {
be0c12
-                char *p, *value, *new_value, *property, *existing;
be0c12
+                _cleanup_(option_freep) Option *new_option = NULL;
be0c12
                 _cleanup_free_ char *l = NULL;
be0c12
-                void *v;
be0c12
+                bool ignore_failure;
be0c12
+                Option *existing;
be0c12
+                char *p, *value;
be0c12
                 int k;
be0c12
 
be0c12
                 k = read_line(f, LONG_LINE_MAX, &l);
be0c12
@@ -122,39 +170,37 @@ static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ign
be0c12
                 *value = 0;
be0c12
                 value++;
be0c12
 
be0c12
-                p = sysctl_normalize(strstrip(p));
be0c12
+                p = strstrip(p);
be0c12
+                ignore_failure = p[0] == '-';
be0c12
+                if (ignore_failure)
be0c12
+                        p++;
be0c12
+
be0c12
+                p = sysctl_normalize(p);
be0c12
                 value = strstrip(value);
be0c12
 
be0c12
                 if (!test_prefix(p))
be0c12
                         continue;
be0c12
 
be0c12
-                existing = ordered_hashmap_get2(sysctl_options, p, &v);
be0c12
+                existing = ordered_hashmap_get(sysctl_options, p);
be0c12
                 if (existing) {
be0c12
-                        if (streq(value, existing))
be0c12
+                        if (streq(value, existing->value)) {
be0c12
+                                existing->ignore_failure = existing->ignore_failure || ignore_failure;
be0c12
                                 continue;
be0c12
+                        }
be0c12
 
be0c12
                         log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, path, c);
be0c12
-                        free(ordered_hashmap_remove(sysctl_options, p));
be0c12
-                        free(v);
be0c12
+                        option_free(ordered_hashmap_remove(sysctl_options, p));
be0c12
                 }
be0c12
 
be0c12
-                property = strdup(p);
be0c12
-                if (!property)
be0c12
+                new_option = option_new(p, value, ignore_failure);
be0c12
+                if (!new_option)
be0c12
                         return log_oom();
be0c12
 
be0c12
-                new_value = strdup(value);
be0c12
-                if (!new_value) {
be0c12
-                        free(property);
be0c12
-                        return log_oom();
be0c12
-                }
be0c12
+                k = ordered_hashmap_put(sysctl_options, new_option->key, new_option);
be0c12
+                if (k < 0)
be0c12
+                        return log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", p);
be0c12
 
be0c12
-                k = ordered_hashmap_put(sysctl_options, property, new_value);
be0c12
-                if (k < 0) {
be0c12
-                        log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property);
be0c12
-                        free(property);
be0c12
-                        free(new_value);
be0c12
-                        return k;
be0c12
-                }
be0c12
+                TAKE_PTR(new_option);
be0c12
         }
be0c12
 
be0c12
         return r;
be0c12
@@ -251,7 +297,7 @@ static int parse_argv(int argc, char *argv[]) {
be0c12
 }
be0c12
 
be0c12
 int main(int argc, char *argv[]) {
be0c12
-        OrderedHashmap *sysctl_options = NULL;
be0c12
+        _cleanup_(ordered_hashmap_freep) OrderedHashmap *sysctl_options = NULL;
be0c12
         int r = 0, k;
be0c12
 
be0c12
         r = parse_argv(argc, argv);
be0c12
@@ -264,7 +310,7 @@ int main(int argc, char *argv[]) {
be0c12
 
be0c12
         umask(0022);
be0c12
 
be0c12
-        sysctl_options = ordered_hashmap_new(&path_hash_ops);
be0c12
+        sysctl_options = ordered_hashmap_new(&option_hash_ops);
be0c12
         if (!sysctl_options) {
be0c12
                 r = log_oom();
be0c12
                 goto finish;
be0c12
@@ -311,7 +357,6 @@ int main(int argc, char *argv[]) {
be0c12
 finish:
be0c12
         pager_close();
be0c12
 
be0c12
-        ordered_hashmap_free_free_free(sysctl_options);
be0c12
         strv_free(arg_prefixes);
be0c12
 
be0c12
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;