summaryrefslogtreecommitdiffstats
path: root/src/sysctl
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2020-01-16 19:38:21 +0100
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2020-02-04 00:01:50 +0100
commite0f424790d3dbde136a29a7fa4c2777c2e3fd695 (patch)
tree39773eb654ef830812b5c58ccd66227cec60c4f5 /src/sysctl
parentman: add syntax quickhelp to sysctl.d(5) (diff)
downloadsystemd-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 'src/sysctl')
-rw-r--r--src/sysctl/sysctl.c168
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)