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