1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "conf-files.h"
35 #include "path-util.h"
36 #include "string-util.h"
38 #include "sysctl-util.h"
41 static char **arg_prefixes
= NULL
;
43 static const char conf_file_dirs
[] = CONF_PATHS_NULSTR("sysctl.d");
45 static int apply_all(OrderedHashmap
*sysctl_options
) {
46 char *property
, *value
;
50 ORDERED_HASHMAP_FOREACH_KEY(value
, property
, sysctl_options
, i
) {
53 k
= sysctl_write(property
, value
);
55 /* If the sysctl is not available in the kernel or we are running with reduced privileges and
56 * cannot write it, then log about the issue at LOG_NOTICE level, and proceed without
57 * failing. (EROFS is treated as a permission problem here, since that's how container managers
58 * usually protected their sysctls.) In all other cases log an error and make the tool fail. */
60 if (IN_SET(k
, -EPERM
, -EACCES
, -EROFS
, -ENOENT
))
61 log_notice_errno(k
, "Couldn't write '%s' to '%s', ignoring: %m", value
, property
);
63 log_error_errno(k
, "Couldn't write '%s' to '%s': %m", value
, property
);
73 static bool test_prefix(const char *p
) {
76 if (strv_isempty(arg_prefixes
))
79 STRV_FOREACH(i
, arg_prefixes
) {
82 t
= path_startswith(*i
, "/proc/sys/");
85 if (path_startswith(p
, t
))
92 static int parse_file(OrderedHashmap
*sysctl_options
, const char *path
, bool ignore_enoent
) {
93 _cleanup_fclose_
FILE *f
= NULL
;
99 r
= search_and_fopen_nulstr(path
, "re", NULL
, conf_file_dirs
, &f
);
101 if (ignore_enoent
&& r
== -ENOENT
)
104 return log_error_errno(r
, "Failed to open file '%s', ignoring: %m", path
);
107 log_debug("Parsing %s", path
);
109 char l
[LINE_MAX
], *p
, *value
, *new_value
, *property
, *existing
;
113 if (!fgets(l
, sizeof(l
), f
)) {
117 return log_error_errno(errno
, "Failed to read file '%s', ignoring: %m", path
);
126 if (strchr(COMMENTS
"\n", *p
))
129 value
= strchr(p
, '=');
131 log_error("Line is not an assignment at '%s:%u': %s", path
, c
, value
);
141 p
= sysctl_normalize(strstrip(p
));
142 value
= strstrip(value
);
147 existing
= ordered_hashmap_get2(sysctl_options
, p
, &v
);
149 if (streq(value
, existing
))
152 log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p
, path
, c
);
153 free(ordered_hashmap_remove(sysctl_options
, p
));
157 property
= strdup(p
);
161 new_value
= strdup(value
);
167 k
= ordered_hashmap_put(sysctl_options
, property
, new_value
);
169 log_error_errno(k
, "Failed to add sysctl variable %s to hashmap: %m", property
);
179 static void help(void) {
180 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
181 "Applies kernel sysctl settings.\n\n"
182 " -h --help Show this help\n"
183 " --version Show package version\n"
184 " --prefix=PATH Only apply rules with the specified prefix\n"
185 , program_invocation_short_name
);
188 static int parse_argv(int argc
, char *argv
[]) {
195 static const struct option options
[] = {
196 { "help", no_argument
, NULL
, 'h' },
197 { "version", no_argument
, NULL
, ARG_VERSION
},
198 { "prefix", required_argument
, NULL
, ARG_PREFIX
},
207 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
221 /* We used to require people to specify absolute paths
222 * in /proc/sys in the past. This is kinda useless, but
223 * we need to keep compatibility. We now support any
224 * sysctl name available. */
225 sysctl_normalize(optarg
);
227 if (path_startswith(optarg
, "/proc/sys"))
230 p
= strappend("/proc/sys/", optarg
);
234 if (strv_consume(&arg_prefixes
, p
) < 0)
244 assert_not_reached("Unhandled option");
250 int main(int argc
, char *argv
[]) {
251 OrderedHashmap
*sysctl_options
= NULL
;
254 r
= parse_argv(argc
, argv
);
258 log_set_target(LOG_TARGET_AUTO
);
259 log_parse_environment();
264 sysctl_options
= ordered_hashmap_new(&string_hash_ops
);
265 if (!sysctl_options
) {
275 for (i
= optind
; i
< argc
; i
++) {
276 k
= parse_file(sysctl_options
, argv
[i
], false);
281 _cleanup_strv_free_
char **files
= NULL
;
284 r
= conf_files_list_nulstr(&files
, ".conf", NULL
, 0, conf_file_dirs
);
286 log_error_errno(r
, "Failed to enumerate sysctl.d files: %m");
290 STRV_FOREACH(f
, files
) {
291 k
= parse_file(sysctl_options
, *f
, true);
297 k
= apply_all(sysctl_options
);
302 ordered_hashmap_free_free_free(sysctl_options
);
303 strv_free(arg_prefixes
);
305 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;