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"
38 #include "sysctl-util.h"
40 static char **arg_prefixes = NULL;
42 static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysctl");
44 static int apply_all(Hashmap *sysctl_options) {
46 char *property, *value;
49 assert(sysctl_options);
51 HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
54 k = sysctl_write(property, value);
56 log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
57 "Failed to write '%s' to '%s': %s", value, property, strerror(-k));
66 static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
67 _cleanup_fclose_ FILE *f = NULL;
72 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
74 if (ignore_enoent && r == -ENOENT)
77 return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
80 log_debug("Parsing %s", path);
82 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
86 if (!fgets(l, sizeof(l), f)) {
90 log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
98 if (strchr(COMMENTS "\n", *p))
101 value = strchr(p, '=');
103 log_error("Line is not an assignment in file '%s': %s", path, value);
113 p = sysctl_normalize(strstrip(p));
114 value = strstrip(value);
116 if (!strv_isempty(arg_prefixes)) {
118 STRV_FOREACH(i, arg_prefixes) {
119 t = path_startswith(*i, "/proc/sys/");
122 if (path_startswith(p, t))
130 existing = hashmap_get2(sysctl_options, p, &v);
132 if (streq(value, existing))
135 log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path);
136 free(hashmap_remove(sysctl_options, p));
140 property = strdup(p);
144 new_value = strdup(value);
150 k = hashmap_put(sysctl_options, property, new_value);
152 log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property);
162 static void help(void) {
163 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
164 "Applies kernel sysctl settings.\n\n"
165 " -h --help Show this help\n"
166 " --version Show package version\n"
167 " --prefix=PATH Only apply rules with the specified prefix\n"
168 , program_invocation_short_name);
171 static int parse_argv(int argc, char *argv[]) {
178 static const struct option options[] = {
179 { "help", no_argument, NULL, 'h' },
180 { "version", no_argument, NULL, ARG_VERSION },
181 { "prefix", required_argument, NULL, ARG_PREFIX },
190 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
199 puts(PACKAGE_STRING);
200 puts(SYSTEMD_FEATURES);
206 /* We used to require people to specify absolute paths
207 * in /proc/sys in the past. This is kinda useless, but
208 * we need to keep compatibility. We now support any
209 * sysctl name available. */
210 sysctl_normalize(optarg);
211 if (startswith(optarg, "/proc/sys"))
214 p = strappend("/proc/sys/", optarg);
218 if (strv_consume(&arg_prefixes, p) < 0)
228 assert_not_reached("Unhandled option");
234 int main(int argc, char *argv[]) {
236 Hashmap *sysctl_options;
238 r = parse_argv(argc, argv);
240 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
242 log_set_target(LOG_TARGET_AUTO);
243 log_parse_environment();
248 sysctl_options = hashmap_new(&string_hash_ops);
249 if (!sysctl_options) {
259 for (i = optind; i < argc; i++) {
260 k = parse_file(sysctl_options, argv[i], false);
265 _cleanup_strv_free_ char **files = NULL;
268 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
270 log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
274 STRV_FOREACH(f, files) {
275 k = parse_file(sysctl_options, *f, true);
281 k = apply_all(sysctl_options);
286 hashmap_free_free_free(sysctl_options);
287 strv_free(arg_prefixes);
289 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;