]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysctl/sysctl.c
283eefe1a1d233e49786034668d64c80cbe507f6
[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, strlen("/proc/sys/") + strlen(property) + 1);
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", NULL, 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
249 for (p = optarg; *p; p++)
250 if (*p == '.')
251 *p = '/';
252
253 if (strv_extend(&arg_prefixes, optarg) < 0)
254 return log_oom();
255
256 break;
257 }
258
259 case '?':
260 return -EINVAL;
261
262 default:
263 assert_not_reached("Unhandled option");
264 }
265 }
266
267 return 1;
268 }
269
270 int main(int argc, char *argv[]) {
271 int r = 0, k;
272 Hashmap *sysctl_options;
273
274 r = parse_argv(argc, argv);
275 if (r <= 0)
276 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
277
278 log_set_target(LOG_TARGET_AUTO);
279 log_parse_environment();
280 log_open();
281
282 umask(0022);
283
284 sysctl_options = hashmap_new(string_hash_func, string_compare_func);
285 if (!sysctl_options) {
286 r = log_oom();
287 goto finish;
288 }
289
290 r = 0;
291
292 if (argc > optind) {
293 int i;
294
295 for (i = optind; i < argc; i++) {
296 k = parse_file(sysctl_options, argv[i], false);
297 if (k < 0 && r == 0)
298 r = k;
299 }
300 } else {
301 _cleanup_strv_free_ char **files = NULL;
302 char **f;
303
304 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
305 if (r < 0) {
306 log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
307 goto finish;
308 }
309
310 STRV_FOREACH(f, files) {
311 k = parse_file(sysctl_options, *f, true);
312 if (k < 0 && r == 0)
313 r = k;
314 }
315 }
316
317 k = apply_all(sysctl_options);
318 if (k < 0 && r == 0)
319 r = k;
320
321 finish:
322 hashmap_free_free_free(sysctl_options);
323 strv_free(arg_prefixes);
324
325 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
326 }