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