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