#include "conf-files.h"
#include "def.h"
+#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"
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,
_cleanup_(option_freep) Option *o = NULL;
assert(key);
- assert(value);
o = new(Option, 1);
if (!o)
*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;
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 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. */
+ /* Ignore "negative match" options, they are there only to exclude stuff from globs. */
+ if (!option->value)
+ continue;
- 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", option->value, option->key);
- if (r == 0)
- r = k;
- }
- }
- }
+ if (string_is_glob(option->key)) {
+ _cleanup_strv_free_ char **paths = NULL;
+ _cleanup_free_ char *pattern = NULL;
+ char **s;
- return r;
-}
+ pattern = path_join("/proc/sys", option->key);
+ if (!pattern)
+ return log_oom();
-static bool test_prefix(const char *p) {
- char **i;
+ k = glob_extend(&paths, pattern, GLOB_NOCHECK);
+ if (k < 0) {
+ if (option->ignore_failure || ERRNO_IS_PRIVILEGE(k))
+ 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;
+ }
- if (strv_isempty(arg_prefixes))
- return true;
+ } else if (strv_isempty(paths))
+ log_debug("No match for glob: %s", option->key);
- STRV_FOREACH(i, arg_prefixes) {
- const char *t;
+ STRV_FOREACH(s, paths) {
+ const char *key;
- t = path_startswith(*i, "/proc/sys/");
- if (!t)
- t = *i;
- if (path_startswith(p, t))
- return true;
+ 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) {
+static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent) {
_cleanup_fclose_ FILE *f = NULL;
unsigned c = 0;
int r;
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;
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;
- existing = ordered_hashmap_get(sysctl_options, p);
+ if (ordered_hashmap_ensure_allocated(sysctl_options, &option_hash_ops) < 0)
+ return log_oom();
+
+ existing = ordered_hashmap_get(*sysctl_options, p);
if (existing) {
- if (streq(value, existing->value)) {
+ if (streq_ptr(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);
- option_free(ordered_hashmap_remove(sysctl_options, p));
+ option_free(ordered_hashmap_remove(*sysctl_options, p));
}
new_option = option_new(p, value, ignore_failure);
if (!new_option)
return log_oom();
- k = ordered_hashmap_put(sysctl_options, new_option->key, new_option);
+ 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);
umask(0022);
- sysctl_options = ordered_hashmap_new(&option_hash_ops);
- if (!sysctl_options)
- return log_oom();
-
if (argc > optind) {
int i;
r = 0;
for (i = optind; i < argc; i++) {
- k = parse_file(sysctl_options, argv[i], false);
+ k = parse_file(&sysctl_options, argv[i], false);
if (k < 0 && r == 0)
r = k;
}
}
STRV_FOREACH(f, files) {
- k = parse_file(sysctl_options, *f, true);
+ k = parse_file(&sysctl_options, *f, true);
if (k < 0 && r == 0)
r = k;
}