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 k = write_string_file(p, value);
83 log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
84 "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
86 if (k != -ENOENT && r == 0)
93 static int apply_all(Hashmap *sysctl_options) {
95 char *property, *value;
98 assert(sysctl_options);
100 HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
103 k = apply_sysctl(property, value);
110 static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
111 _cleanup_fclose_ FILE *f = NULL;
116 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
118 if (ignore_enoent && r == -ENOENT)
121 return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
124 log_debug("parse: %s", path);
126 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
130 if (!fgets(l, sizeof(l), f)) {
134 log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
142 if (strchr(COMMENTS "\n", *p))
145 value = strchr(p, '=');
147 log_error("Line is not an assignment in file '%s': %s", path, value);
157 p = normalize_sysctl(strstrip(p));
158 value = strstrip(value);
160 if (!strv_isempty(arg_prefixes)) {
162 STRV_FOREACH(i, arg_prefixes) {
163 t = path_startswith(*i, "/proc/sys/");
166 if (path_startswith(p, t))
174 existing = hashmap_get2(sysctl_options, p, &v);
176 if (streq(value, existing))
179 log_info("Overwriting earlier assignment of %s in file '%s'.", p, path);
180 free(hashmap_remove(sysctl_options, p));
184 property = strdup(p);
188 new_value = strdup(value);
194 k = hashmap_put(sysctl_options, property, new_value);
196 log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property);
206 static void help(void) {
207 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
208 "Applies kernel sysctl settings.\n\n"
209 " -h --help Show this help\n"
210 " --version Show package version\n"
211 " --prefix=PATH Only apply rules with the specified prefix\n"
212 , program_invocation_short_name);
215 static int parse_argv(int argc, char *argv[]) {
222 static const struct option options[] = {
223 { "help", no_argument, NULL, 'h' },
224 { "version", no_argument, NULL, ARG_VERSION },
225 { "prefix", required_argument, NULL, ARG_PREFIX },
234 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
243 puts(PACKAGE_STRING);
244 puts(SYSTEMD_FEATURES);
250 /* We used to require people to specify absolute paths
251 * in /proc/sys in the past. This is kinda useless, but
252 * we need to keep compatibility. We now support any
253 * sysctl name available. */
254 normalize_sysctl(optarg);
255 if (startswith(optarg, "/proc/sys"))
258 p = strappend("/proc/sys/", optarg);
262 if (strv_consume(&arg_prefixes, p) < 0)
272 assert_not_reached("Unhandled option");
278 int main(int argc, char *argv[]) {
280 Hashmap *sysctl_options;
282 r = parse_argv(argc, argv);
284 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
286 log_set_target(LOG_TARGET_AUTO);
287 log_parse_environment();
292 sysctl_options = hashmap_new(&string_hash_ops);
293 if (!sysctl_options) {
303 for (i = optind; i < argc; i++) {
304 k = parse_file(sysctl_options, argv[i], false);
309 _cleanup_strv_free_ char **files = NULL;
312 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
314 log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
318 STRV_FOREACH(f, files) {
319 k = parse_file(sysctl_options, *f, true);
325 k = apply_all(sysctl_options);
330 hashmap_free_free_free(sysctl_options);
331 strv_free(arg_prefixes);
333 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;