]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/sysctl/sysctl.c
Introduce CONF_DIRS_NULSTR helper to define standard conf dirs
[thirdparty/systemd.git] / src / sysctl / sysctl.c
CommitLineData
8e1bd70d
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
8e1bd70d
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
8e1bd70d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8e1bd70d
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <stdlib.h>
23#include <stdbool.h>
24#include <errno.h>
25#include <string.h>
8e1bd70d
LP
26#include <stdio.h>
27#include <limits.h>
7a2a0b90 28#include <getopt.h>
8e1bd70d
LP
29
30#include "log.h"
db1413d7 31#include "strv.h"
8e1bd70d 32#include "util.h"
86fc77c4 33#include "hashmap.h"
9eb977db 34#include "path-util.h"
2c21044f 35#include "conf-files.h"
a5c32cff 36#include "fileio.h"
eb9da376 37#include "build.h"
8e1bd70d 38
fabe5c0e 39static char **arg_prefixes = NULL;
8e1bd70d 40
7f0a55d4 41static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysctl");
fabe5c0e 42
2e573fcf 43static char* normalize_sysctl(char *s) {
fabe5c0e
LP
44 char *n;
45
2e573fcf
ZJS
46 n = strpbrk(s, "/.");
47 /* If the first separator is a slash, the path is
48 * assumed to be normalized and slashes remain slashes
49 * and dots remains dots. */
50 if (!n || *n == '/')
51 return s;
52
53 /* Otherwise, dots become slashes and slashes become
54 * dots. Fun. */
55 while (n) {
fabe5c0e
LP
56 if (*n == '.')
57 *n = '/';
2e573fcf
ZJS
58 else
59 *n = '.';
60
61 n = strpbrk(n + 1, "/.");
62 }
fabe5c0e
LP
63
64 return s;
65}
7a2a0b90 66
c1b664d0 67static int apply_sysctl(const char *property, const char *value) {
fabe5c0e
LP
68 _cleanup_free_ char *p = NULL;
69 char *n;
c1b664d0 70 int r = 0, k;
8e1bd70d
LP
71
72 log_debug("Setting '%s' to '%s'", property, value);
73
f8294e41 74 p = new(char, strlen("/proc/sys/") + strlen(property) + 1);
0d0f0c50
SL
75 if (!p)
76 return log_oom();
8e1bd70d 77
fabe5c0e 78 n = stpcpy(p, "/proc/sys/");
8e1bd70d
LP
79 strcpy(n, property);
80
f68c5a70
LP
81 if (!strv_isempty(arg_prefixes)) {
82 char **i;
83 bool good = false;
84
85 STRV_FOREACH(i, arg_prefixes)
86 if (path_startswith(p, *i)) {
87 good = true;
88 break;
89 }
90
91 if (!good) {
92 log_debug("Skipping %s", p);
f68c5a70
LP
93 return 0;
94 }
7a2a0b90
LP
95 }
96
574d5f2d 97 k = write_string_file(p, value);
7a2a0b90 98 if (k < 0) {
c1b664d0
LP
99 log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
100 "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
24a35973 101
c1b664d0
LP
102 if (k != -ENOENT && r == 0)
103 r = k;
8e1bd70d
LP
104 }
105
c1b664d0 106 return r;
8e1bd70d
LP
107}
108
fabe5c0e 109static int apply_all(Hashmap *sysctl_options) {
86fc77c4
MS
110 int r = 0;
111 char *property, *value;
112 Iterator i;
113
fabe5c0e
LP
114 assert(sysctl_options);
115
86fc77c4
MS
116 HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
117 int k;
118
119 k = apply_sysctl(property, value);
120 if (k < 0 && r == 0)
121 r = k;
122 }
123 return r;
124}
125
fabe5c0e
LP
126static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
127 _cleanup_fclose_ FILE *f = NULL;
128 int r;
8e1bd70d
LP
129
130 assert(path);
131
4cf7ea55 132 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
fabe5c0e 133 if (r < 0) {
6f6fad96 134 if (ignore_enoent && r == -ENOENT)
c1b664d0
LP
135 return 0;
136
fabe5c0e
LP
137 log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r));
138 return r;
8e1bd70d
LP
139 }
140
9f6445e3 141 log_debug("parse: %s", path);
8e1bd70d 142 while (!feof(f)) {
fabe5c0e 143 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
04bf3c1a 144 void *v;
fabe5c0e 145 int k;
8e1bd70d
LP
146
147 if (!fgets(l, sizeof(l), f)) {
148 if (feof(f))
149 break;
150
151 log_error("Failed to read file '%s', ignoring: %m", path);
fabe5c0e 152 return -errno;
8e1bd70d
LP
153 }
154
155 p = strstrip(l);
8e1bd70d
LP
156 if (!*p)
157 continue;
158
d3b6d0c2 159 if (strchr(COMMENTS "\n", *p))
8e1bd70d
LP
160 continue;
161
86fc77c4
MS
162 value = strchr(p, '=');
163 if (!value) {
8e1bd70d 164 log_error("Line is not an assignment in file '%s': %s", path, value);
c1b664d0
LP
165
166 if (r == 0)
167 r = -EINVAL;
8e1bd70d
LP
168 continue;
169 }
170
171 *value = 0;
172 value++;
173
fabe5c0e
LP
174 p = normalize_sysctl(strstrip(p));
175 value = strstrip(value);
176
04bf3c1a 177 existing = hashmap_get2(sysctl_options, p, &v);
fabe5c0e 178 if (existing) {
04bf3c1a
KS
179 if (streq(value, existing))
180 continue;
fabe5c0e 181
04bf3c1a
KS
182 log_info("Overwriting earlier assignment of %s in file '%s'.", p, path);
183 free(hashmap_remove(sysctl_options, p));
184 free(v);
86fc77c4
MS
185 }
186
fabe5c0e
LP
187 property = strdup(p);
188 if (!property)
189 return log_oom();
190
191 new_value = strdup(value);
86fc77c4
MS
192 if (!new_value) {
193 free(property);
fabe5c0e 194 return log_oom();
86fc77c4
MS
195 }
196
fabe5c0e
LP
197 k = hashmap_put(sysctl_options, property, new_value);
198 if (k < 0) {
04bf3c1a 199 log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-k));
86fc77c4
MS
200 free(property);
201 free(new_value);
fabe5c0e 202 return k;
86fc77c4 203 }
8e1bd70d
LP
204 }
205
c1b664d0 206 return r;
8e1bd70d
LP
207}
208
601185b4 209static void help(void) {
7a2a0b90
LP
210 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
211 "Applies kernel sysctl settings.\n\n"
212 " -h --help Show this help\n"
eb9da376 213 " --version Show package version\n"
0e1f5792 214 " --prefix=PATH Only apply rules with the specified prefix\n"
601185b4 215 , program_invocation_short_name);
7a2a0b90
LP
216}
217
218static int parse_argv(int argc, char *argv[]) {
219
220 enum {
eb9da376 221 ARG_VERSION = 0x100,
7a2a0b90
LP
222 ARG_PREFIX
223 };
224
225 static const struct option options[] = {
226 { "help", no_argument, NULL, 'h' },
eb9da376 227 { "version", no_argument, NULL, ARG_VERSION },
7a2a0b90 228 { "prefix", required_argument, NULL, ARG_PREFIX },
eb9da376 229 {}
7a2a0b90
LP
230 };
231
232 int c;
233
234 assert(argc >= 0);
235 assert(argv);
236
601185b4 237 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
7a2a0b90
LP
238
239 switch (c) {
240
241 case 'h':
601185b4
ZJS
242 help();
243 return 0;
eb9da376
LP
244
245 case ARG_VERSION:
246 puts(PACKAGE_STRING);
247 puts(SYSTEMD_FEATURES);
7a2a0b90
LP
248 return 0;
249
250 case ARG_PREFIX: {
251 char *p;
252
0e1f5792
DH
253 /* We used to require people to specify absolute paths
254 * in /proc/sys in the past. This is kinda useless, but
255 * we need to keep compatibility. We now support any
256 * sysctl name available. */
257 normalize_sysctl(optarg);
258 if (startswith(optarg, "/proc/sys"))
259 p = strdup(optarg);
260 else
261 p = strappend("/proc/sys/", optarg);
262
263 if (!p)
264 return log_oom();
265 if (strv_consume(&arg_prefixes, p) < 0)
0d0f0c50 266 return log_oom();
f68c5a70 267
7a2a0b90
LP
268 break;
269 }
270
271 case '?':
272 return -EINVAL;
273
274 default:
eb9da376 275 assert_not_reached("Unhandled option");
7a2a0b90 276 }
7a2a0b90
LP
277
278 return 1;
279}
280
8e1bd70d 281int main(int argc, char *argv[]) {
0187f62b 282 int r = 0, k;
fabe5c0e 283 Hashmap *sysctl_options;
8e1bd70d 284
7a2a0b90
LP
285 r = parse_argv(argc, argv);
286 if (r <= 0)
287 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
288
8e1bd70d
LP
289 log_set_target(LOG_TARGET_AUTO);
290 log_parse_environment();
291 log_open();
292
4c12626c
LP
293 umask(0022);
294
d5099efc 295 sysctl_options = hashmap_new(&string_hash_ops);
86fc77c4
MS
296 if (!sysctl_options) {
297 r = log_oom();
298 goto finish;
299 }
300
0187f62b
LP
301 r = 0;
302
de19ece7
LP
303 if (argc > optind) {
304 int i;
305
306 for (i = optind; i < argc; i++) {
fabe5c0e
LP
307 k = parse_file(sysctl_options, argv[i], false);
308 if (k < 0 && r == 0)
de19ece7
LP
309 r = k;
310 }
311 } else {
fabe5c0e
LP
312 _cleanup_strv_free_ char **files = NULL;
313 char **f;
c1b664d0 314
fabe5c0e 315 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
44143309
KS
316 if (r < 0) {
317 log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
318 goto finish;
319 }
db1413d7 320
fabe5c0e
LP
321 STRV_FOREACH(f, files) {
322 k = parse_file(sysctl_options, *f, true);
323 if (k < 0 && r == 0)
db1413d7
KS
324 r = k;
325 }
8e1bd70d 326 }
86fc77c4 327
fabe5c0e
LP
328 k = apply_all(sysctl_options);
329 if (k < 0 && r == 0)
0187f62b
LP
330 r = k;
331
44143309 332finish:
fabe5c0e 333 hashmap_free_free_free(sysctl_options);
f68c5a70
LP
334 strv_free(arg_prefixes);
335
c1b664d0 336 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
8e1bd70d 337}