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[] = CONF_DIRS_NULSTR("sysctl");
43 static char* normalize_sysctl(char *s) {
47 /* If the first separator is a slash, the path is
48 * assumed to be normalized and slashes remain slashes
49 * and dots remains dots. */
53 /* Otherwise, dots become slashes and slashes become
61 n = strpbrk(n + 1, "/.");
67 static int apply_sysctl(const char *property, const char *value) {
68 _cleanup_free_ char *p = NULL;
72 log_debug("Setting '%s' to '%s'", property, value);
74 p = new(char, strlen("/proc/sys/") + strlen(property) + 1);
78 n = stpcpy(p, "/proc/sys/");
81 if (!strv_isempty(arg_prefixes)) {
85 STRV_FOREACH(i, arg_prefixes)
86 if (path_startswith(p, *i)) {
92 log_debug("Skipping %s", p);
97 k = write_string_file(p, value);
99 log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
100 "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
102 if (k != -ENOENT && r == 0)
109 static int apply_all(Hashmap *sysctl_options) {
111 char *property, *value;
114 assert(sysctl_options);
116 HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
119 k = apply_sysctl(property, value);
126 static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
127 _cleanup_fclose_ FILE *f = NULL;
132 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
134 if (ignore_enoent && r == -ENOENT)
137 return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
140 log_debug("parse: %s", path);
142 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
146 if (!fgets(l, sizeof(l), f)) {
150 log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
158 if (strchr(COMMENTS "\n", *p))
161 value = strchr(p, '=');
163 log_error("Line is not an assignment in file '%s': %s", path, value);
173 p = normalize_sysctl(strstrip(p));
174 value = strstrip(value);
176 existing = hashmap_get2(sysctl_options, p, &v);
178 if (streq(value, existing))
181 log_info("Overwriting earlier assignment of %s in file '%s'.", p, path);
182 free(hashmap_remove(sysctl_options, p));
186 property = strdup(p);
190 new_value = strdup(value);
196 k = hashmap_put(sysctl_options, property, new_value);
198 log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property);
208 static void help(void) {
209 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
210 "Applies kernel sysctl settings.\n\n"
211 " -h --help Show this help\n"
212 " --version Show package version\n"
213 " --prefix=PATH Only apply rules with the specified prefix\n"
214 , program_invocation_short_name);
217 static int parse_argv(int argc, char *argv[]) {
224 static const struct option options[] = {
225 { "help", no_argument, NULL, 'h' },
226 { "version", no_argument, NULL, ARG_VERSION },
227 { "prefix", required_argument, NULL, ARG_PREFIX },
236 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
245 puts(PACKAGE_STRING);
246 puts(SYSTEMD_FEATURES);
252 /* We used to require people to specify absolute paths
253 * in /proc/sys in the past. This is kinda useless, but
254 * we need to keep compatibility. We now support any
255 * sysctl name available. */
256 normalize_sysctl(optarg);
257 if (startswith(optarg, "/proc/sys"))
260 p = strappend("/proc/sys/", optarg);
264 if (strv_consume(&arg_prefixes, p) < 0)
274 assert_not_reached("Unhandled option");
280 int main(int argc, char *argv[]) {
282 Hashmap *sysctl_options;
284 r = parse_argv(argc, argv);
286 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
288 log_set_target(LOG_TARGET_AUTO);
289 log_parse_environment();
294 sysctl_options = hashmap_new(&string_hash_ops);
295 if (!sysctl_options) {
305 for (i = optind; i < argc; i++) {
306 k = parse_file(sysctl_options, argv[i], false);
311 _cleanup_strv_free_ char **files = NULL;
314 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
316 log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
320 STRV_FOREACH(f, files) {
321 k = parse_file(sysctl_options, *f, true);
327 k = apply_all(sysctl_options);
332 hashmap_free_free_free(sysctl_options);
333 strv_free(arg_prefixes);
335 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;