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