#include "strv.h"
#include "util.h"
#include "strv.h"
+#include "hashmap.h"
+#include "path-util.h"
#include "conf-files.h"
-
-#define PROC_SYS_PREFIX "/proc/sys/"
+#include "fileio.h"
static char **arg_prefixes = NULL;
+static const char conf_file_dirs[] =
+ "/etc/sysctl.d\0"
+ "/run/sysctl.d\0"
+ "/usr/local/lib/sysctl.d\0"
+ "/usr/lib/sysctl.d\0"
+#ifdef HAVE_SPLIT_USR
+ "/lib/sysctl.d\0"
+#endif
+ ;
+
+static char *normalize_sysctl(char *s) {
+ char *n;
+
+ for (n = s; *n; n++)
+ if (*n == '.')
+ *n = '/';
+
+ return s;
+}
+
static int apply_sysctl(const char *property, const char *value) {
- char *p, *n;
+ _cleanup_free_ char *p = NULL;
+ char *n;
int r = 0, k;
log_debug("Setting '%s' to '%s'", property, value);
- p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property));
- if (!p) {
- log_error("Out of memory");
- return -ENOMEM;
- }
+ p = new(char, sizeof("/proc/sys/") + strlen(property));
+ if (!p)
+ return log_oom();
- n = stpcpy(p, PROC_SYS_PREFIX);
+ n = stpcpy(p, "/proc/sys/");
strcpy(n, property);
- for (; *n; n++)
- if (*n == '.')
- *n = '/';
-
if (!strv_isempty(arg_prefixes)) {
char **i;
bool good = false;
if (!good) {
log_debug("Skipping %s", p);
- free(p);
return 0;
}
}
- k = write_one_line_file(p, value);
+ k = write_string_file(p, value);
if (k < 0) {
-
log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
"Failed to write '%s' to '%s': %s", value, p, strerror(-k));
r = k;
}
- free(p);
-
return r;
}
-static int apply_file(const char *path, bool ignore_enoent) {
- FILE *f;
+static int apply_all(Hashmap *sysctl_options) {
int r = 0;
+ char *property, *value;
+ Iterator i;
+
+ assert(sysctl_options);
+
+ HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
+ int k;
+
+ k = apply_sysctl(property, value);
+ if (k < 0 && r == 0)
+ r = k;
+ }
+ return r;
+}
+
+static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
assert(path);
- if (!(f = fopen(path, "re"))) {
- if (ignore_enoent && errno == ENOENT)
+ r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f);
+ if (r < 0) {
+ if (ignore_enoent && r == -ENOENT)
return 0;
- log_error("Failed to open file '%s', ignoring: %m", path);
- return -errno;
+ log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r));
+ return r;
}
- log_debug("apply: %s\n", path);
+ log_debug("parse: %s\n", path);
while (!feof(f)) {
- char l[LINE_MAX], *p, *value;
+ char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
+ void *v;
int k;
if (!fgets(l, sizeof(l), f)) {
break;
log_error("Failed to read file '%s', ignoring: %m", path);
- r = -errno;
- goto finish;
+ return -errno;
}
p = strstrip(l);
-
if (!*p)
continue;
- if (strchr(COMMENTS, *p))
+ if (strchr(COMMENTS "\n", *p))
continue;
- if (!(value = strchr(p, '='))) {
+ value = strchr(p, '=');
+ if (!value) {
log_error("Line is not an assignment in file '%s': %s", path, value);
if (r == 0)
*value = 0;
value++;
- if ((k = apply_sysctl(strstrip(p), strstrip(value))) < 0 && r == 0)
- r = k;
- }
+ p = normalize_sysctl(strstrip(p));
+ value = strstrip(value);
-finish:
- fclose(f);
+ existing = hashmap_get2(sysctl_options, p, &v);
+ if (existing) {
+ if (streq(value, existing))
+ continue;
+
+ log_info("Overwriting earlier assignment of %s in file '%s'.", p, path);
+ free(hashmap_remove(sysctl_options, p));
+ free(v);
+ }
+
+ property = strdup(p);
+ if (!property)
+ return log_oom();
+
+ new_value = strdup(value);
+ if (!new_value) {
+ free(property);
+ return log_oom();
+ }
+
+ k = hashmap_put(sysctl_options, property, new_value);
+ if (k < 0) {
+ log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-k));
+ free(property);
+ free(new_value);
+ return k;
+ }
+ }
return r;
}
*p = '/';
l = strv_append(arg_prefixes, optarg);
- if (!l) {
- log_error("Out of memory");
- return -ENOMEM;
- }
+ if (!l)
+ return log_oom();
strv_free(arg_prefixes);
arg_prefixes = l;
}
int main(int argc, char *argv[]) {
- int r = 0;
+ int r = 0, k;
+ Hashmap *sysctl_options;
r = parse_argv(argc, argv);
if (r <= 0)
umask(0022);
+ sysctl_options = hashmap_new(string_hash_func, string_compare_func);
+ if (!sysctl_options) {
+ r = log_oom();
+ goto finish;
+ }
+
+ r = 0;
+
if (argc > optind) {
int i;
for (i = optind; i < argc; i++) {
- int k;
-
- k = apply_file(argv[i], false);
+ k = parse_file(sysctl_options, argv[i], false);
if (k < 0 && r == 0)
r = k;
}
} else {
- char **files, **f;
- int k;
+ _cleanup_strv_free_ char **files = NULL;
+ char **f;
- r = conf_files_list(&files, ".conf",
- "/etc/sysctl.d",
- "/run/sysctl.d",
- "/usr/local/lib/sysctl.d",
- "/usr/lib/sysctl.d",
-#ifdef HAVE_SPLIT_USR
- "/lib/sysctl.d",
-#endif
- NULL);
+ r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
if (r < 0) {
log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
goto finish;
}
STRV_FOREACH(f, files) {
- k = apply_file(*f, true);
+ k = parse_file(sysctl_options, *f, true);
if (k < 0 && r == 0)
r = k;
}
+ }
- k = apply_file("/etc/sysctl.conf", true);
- if (k < 0 && r == 0)
- r = k;
+ k = apply_all(sysctl_options);
+ if (k < 0 && r == 0)
+ r = k;
- strv_free(files);
- }
finish:
+ hashmap_free_free_free(sysctl_options);
strv_free(arg_prefixes);
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;