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