]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/sysctl/sysctl.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / sysctl / sysctl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
8e1bd70d
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
8e1bd70d
LP
10 (at your option) any later version.
11
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
5430f7f2 15 Lesser General Public License for more details.
8e1bd70d 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
8e1bd70d
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
8e1bd70d 21#include <errno.h>
7a2a0b90 22#include <getopt.h>
3f6fd1ba
LP
23#include <limits.h>
24#include <stdbool.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
8e1bd70d 28
2c21044f 29#include "conf-files.h"
a0f29c76 30#include "def.h"
3ffd4af2 31#include "fd-util.h"
a5c32cff 32#include "fileio.h"
3f6fd1ba
LP
33#include "hashmap.h"
34#include "log.h"
35#include "path-util.h"
07630cea 36#include "string-util.h"
3f6fd1ba 37#include "strv.h"
88a60da0 38#include "sysctl-util.h"
3f6fd1ba 39#include "util.h"
8e1bd70d 40
fabe5c0e 41static char **arg_prefixes = NULL;
8e1bd70d 42
75eb6154 43static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysctl.d");
fabe5c0e 44
886cf982 45static int apply_all(OrderedHashmap *sysctl_options) {
86fc77c4
MS
46 char *property, *value;
47 Iterator i;
e50b33be 48 int r = 0;
fabe5c0e 49
886cf982 50 ORDERED_HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
86fc77c4
MS
51 int k;
52
88a60da0
KS
53 k = sysctl_write(property, value);
54 if (k < 0) {
39540de8
LP
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. */
59
60 if (IN_SET(k, -EPERM, -EACCES, -EROFS, -ENOENT))
61 log_notice_errno(k, "Couldn't write '%s' to '%s', ignoring: %m", value, property);
62 else {
63 log_error_errno(k, "Couldn't write '%s' to '%s': %m", value, property);
64 if (r == 0)
65 r = k;
66 }
88a60da0 67 }
86fc77c4 68 }
e50b33be 69
86fc77c4
MS
70 return r;
71}
72
9c37b41c
LP
73static bool test_prefix(const char *p) {
74 char **i;
75
76 if (strv_isempty(arg_prefixes))
77 return true;
78
79 STRV_FOREACH(i, arg_prefixes) {
80 const char *t;
81
82 t = path_startswith(*i, "/proc/sys/");
83 if (!t)
84 t = *i;
85 if (path_startswith(p, t))
86 return true;
87 }
88
89 return false;
90}
91
886cf982 92static int parse_file(OrderedHashmap *sysctl_options, const char *path, bool ignore_enoent) {
fabe5c0e 93 _cleanup_fclose_ FILE *f = NULL;
98bf5011 94 unsigned c = 0;
fabe5c0e 95 int r;
8e1bd70d
LP
96
97 assert(path);
98
4cf7ea55 99 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
fabe5c0e 100 if (r < 0) {
6f6fad96 101 if (ignore_enoent && r == -ENOENT)
c1b664d0
LP
102 return 0;
103
8d3d7072 104 return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
8e1bd70d
LP
105 }
106
924bc14f 107 log_debug("Parsing %s", path);
4f14f2bb 108 for (;;) {
fabe5c0e 109 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
04bf3c1a 110 void *v;
fabe5c0e 111 int k;
8e1bd70d
LP
112
113 if (!fgets(l, sizeof(l), f)) {
114 if (feof(f))
115 break;
116
e1427b13 117 return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
8e1bd70d
LP
118 }
119
98bf5011
LP
120 c++;
121
8e1bd70d 122 p = strstrip(l);
8e1bd70d
LP
123 if (!*p)
124 continue;
125
d3b6d0c2 126 if (strchr(COMMENTS "\n", *p))
8e1bd70d
LP
127 continue;
128
86fc77c4
MS
129 value = strchr(p, '=');
130 if (!value) {
98bf5011 131 log_error("Line is not an assignment at '%s:%u': %s", path, c, value);
c1b664d0
LP
132
133 if (r == 0)
134 r = -EINVAL;
8e1bd70d
LP
135 continue;
136 }
137
138 *value = 0;
139 value++;
140
88a60da0 141 p = sysctl_normalize(strstrip(p));
fabe5c0e
LP
142 value = strstrip(value);
143
9c37b41c 144 if (!test_prefix(p))
b99802f7 145 continue;
b99802f7 146
886cf982 147 existing = ordered_hashmap_get2(sysctl_options, p, &v);
fabe5c0e 148 if (existing) {
04bf3c1a
KS
149 if (streq(value, existing))
150 continue;
fabe5c0e 151
98bf5011 152 log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, path, c);
886cf982 153 free(ordered_hashmap_remove(sysctl_options, p));
04bf3c1a 154 free(v);
86fc77c4
MS
155 }
156
fabe5c0e
LP
157 property = strdup(p);
158 if (!property)
159 return log_oom();
160
161 new_value = strdup(value);
86fc77c4
MS
162 if (!new_value) {
163 free(property);
fabe5c0e 164 return log_oom();
86fc77c4
MS
165 }
166
886cf982 167 k = ordered_hashmap_put(sysctl_options, property, new_value);
fabe5c0e 168 if (k < 0) {
da927ba9 169 log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property);
86fc77c4
MS
170 free(property);
171 free(new_value);
fabe5c0e 172 return k;
86fc77c4 173 }
8e1bd70d
LP
174 }
175
c1b664d0 176 return r;
8e1bd70d
LP
177}
178
601185b4 179static void help(void) {
7a2a0b90
LP
180 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
181 "Applies kernel sysctl settings.\n\n"
182 " -h --help Show this help\n"
eb9da376 183 " --version Show package version\n"
0e1f5792 184 " --prefix=PATH Only apply rules with the specified prefix\n"
601185b4 185 , program_invocation_short_name);
7a2a0b90
LP
186}
187
188static int parse_argv(int argc, char *argv[]) {
189
190 enum {
eb9da376 191 ARG_VERSION = 0x100,
7a2a0b90
LP
192 ARG_PREFIX
193 };
194
195 static const struct option options[] = {
196 { "help", no_argument, NULL, 'h' },
eb9da376 197 { "version", no_argument, NULL, ARG_VERSION },
7a2a0b90 198 { "prefix", required_argument, NULL, ARG_PREFIX },
eb9da376 199 {}
7a2a0b90
LP
200 };
201
202 int c;
203
204 assert(argc >= 0);
205 assert(argv);
206
601185b4 207 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
7a2a0b90
LP
208
209 switch (c) {
210
211 case 'h':
601185b4
ZJS
212 help();
213 return 0;
eb9da376
LP
214
215 case ARG_VERSION:
3f6fd1ba 216 return version();
7a2a0b90
LP
217
218 case ARG_PREFIX: {
219 char *p;
220
0e1f5792
DH
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. */
88a60da0 225 sysctl_normalize(optarg);
e50b33be 226
27458ed6 227 if (path_startswith(optarg, "/proc/sys"))
0e1f5792
DH
228 p = strdup(optarg);
229 else
230 p = strappend("/proc/sys/", optarg);
0e1f5792
DH
231 if (!p)
232 return log_oom();
e50b33be 233
0e1f5792 234 if (strv_consume(&arg_prefixes, p) < 0)
0d0f0c50 235 return log_oom();
f68c5a70 236
7a2a0b90
LP
237 break;
238 }
239
240 case '?':
241 return -EINVAL;
242
243 default:
eb9da376 244 assert_not_reached("Unhandled option");
7a2a0b90 245 }
7a2a0b90
LP
246
247 return 1;
248}
249
8e1bd70d 250int main(int argc, char *argv[]) {
e5105081 251 OrderedHashmap *sysctl_options = NULL;
0187f62b 252 int r = 0, k;
8e1bd70d 253
7a2a0b90
LP
254 r = parse_argv(argc, argv);
255 if (r <= 0)
e5105081 256 goto finish;
7a2a0b90 257
8e1bd70d
LP
258 log_set_target(LOG_TARGET_AUTO);
259 log_parse_environment();
260 log_open();
261
4c12626c
LP
262 umask(0022);
263
886cf982 264 sysctl_options = ordered_hashmap_new(&string_hash_ops);
86fc77c4
MS
265 if (!sysctl_options) {
266 r = log_oom();
267 goto finish;
268 }
269
0187f62b
LP
270 r = 0;
271
de19ece7
LP
272 if (argc > optind) {
273 int i;
274
275 for (i = optind; i < argc; i++) {
fabe5c0e
LP
276 k = parse_file(sysctl_options, argv[i], false);
277 if (k < 0 && r == 0)
de19ece7
LP
278 r = k;
279 }
280 } else {
fabe5c0e
LP
281 _cleanup_strv_free_ char **files = NULL;
282 char **f;
c1b664d0 283
b5084605 284 r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
44143309 285 if (r < 0) {
da927ba9 286 log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
44143309
KS
287 goto finish;
288 }
db1413d7 289
fabe5c0e
LP
290 STRV_FOREACH(f, files) {
291 k = parse_file(sysctl_options, *f, true);
292 if (k < 0 && r == 0)
db1413d7
KS
293 r = k;
294 }
8e1bd70d 295 }
86fc77c4 296
fabe5c0e
LP
297 k = apply_all(sysctl_options);
298 if (k < 0 && r == 0)
0187f62b
LP
299 r = k;
300
44143309 301finish:
886cf982 302 ordered_hashmap_free_free_free(sysctl_options);
f68c5a70
LP
303 strv_free(arg_prefixes);
304
c1b664d0 305 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
8e1bd70d 306}