1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
34 #include "path-util.h"
35 #include "conf-files.h"
39 static char **arg_prefixes = NULL;
41 static const char conf_file_dirs[] =
44 "/usr/local/lib/sysctl.d\0"
51 static char* normalize_sysctl(char *s) {
55 /* If the first separator is a slash, the path is
56 * assumed to be normalized and slashes remain slashes
57 * and dots remains dots. */
61 /* Otherwise, dots become slashes and slashes become
69 n = strpbrk(n + 1, "/.");
75 static int apply_sysctl(const char *property, const char *value) {
76 _cleanup_free_ char *p = NULL;
80 log_debug("Setting '%s' to '%s'", property, value);
82 p = new(char, strlen("/proc/sys/") + strlen(property) + 1);
86 n = stpcpy(p, "/proc/sys/");
89 if (!strv_isempty(arg_prefixes)) {
93 STRV_FOREACH(i, arg_prefixes)
94 if (path_startswith(p, *i)) {
100 log_debug("Skipping %s", p);
105 k = write_string_file(p, value);
107 log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
108 "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
110 if (k != -ENOENT && r == 0)
117 static int apply_all(Hashmap *sysctl_options) {
119 char *property, *value;
122 assert(sysctl_options);
124 HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
127 k = apply_sysctl(property, value);
134 static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
135 _cleanup_fclose_ FILE *f = NULL;
140 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
142 if (ignore_enoent && r == -ENOENT)
145 log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r));
149 log_debug("parse: %s", path);
151 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
155 if (!fgets(l, sizeof(l), f)) {
159 log_error("Failed to read file '%s', ignoring: %m", path);
167 if (strchr(COMMENTS "\n", *p))
170 value = strchr(p, '=');
172 log_error("Line is not an assignment in file '%s': %s", path, value);
182 p = normalize_sysctl(strstrip(p));
183 value = strstrip(value);
185 existing = hashmap_get2(sysctl_options, p, &v);
187 if (streq(value, existing))
190 log_info("Overwriting earlier assignment of %s in file '%s'.", p, path);
191 free(hashmap_remove(sysctl_options, p));
195 property = strdup(p);
199 new_value = strdup(value);
205 k = hashmap_put(sysctl_options, property, new_value);
207 log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-k));
217 static void help(void) {
218 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
219 "Applies kernel sysctl settings.\n\n"
220 " -h --help Show this help\n"
221 " --version Show package version\n"
222 " --prefix=PATH Only apply rules with the specified prefix\n"
223 , program_invocation_short_name);
226 static int parse_argv(int argc, char *argv[]) {
233 static const struct option options[] = {
234 { "help", no_argument, NULL, 'h' },
235 { "version", no_argument, NULL, ARG_VERSION },
236 { "prefix", required_argument, NULL, ARG_PREFIX },
245 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
254 puts(PACKAGE_STRING);
255 puts(SYSTEMD_FEATURES);
261 /* We used to require people to specify absolute paths
262 * in /proc/sys in the past. This is kinda useless, but
263 * we need to keep compatibility. We now support any
264 * sysctl name available. */
265 normalize_sysctl(optarg);
266 if (startswith(optarg, "/proc/sys"))
269 p = strappend("/proc/sys/", optarg);
273 if (strv_consume(&arg_prefixes, p) < 0)
283 assert_not_reached("Unhandled option");
289 int main(int argc, char *argv[]) {
291 Hashmap *sysctl_options;
293 r = parse_argv(argc, argv);
295 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
297 log_set_target(LOG_TARGET_AUTO);
298 log_parse_environment();
303 sysctl_options = hashmap_new(&string_hash_ops);
304 if (!sysctl_options) {
314 for (i = optind; i < argc; i++) {
315 k = parse_file(sysctl_options, argv[i], false);
320 _cleanup_strv_free_ char **files = NULL;
323 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
325 log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
329 STRV_FOREACH(f, files) {
330 k = parse_file(sysctl_options, *f, true);
336 k = apply_all(sysctl_options);
341 hashmap_free_free_free(sysctl_options);
342 strv_free(arg_prefixes);
344 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;