]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysctl/sysctl.c
honor SELinux labels, when creating and writing config files
[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
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_one_line_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 && errno == -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\n", path);
136 while (!feof(f)) {
137 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
138 int k;
139
140 if (!fgets(l, sizeof(l), f)) {
141 if (feof(f))
142 break;
143
144 log_error("Failed to read file '%s', ignoring: %m", path);
145 return -errno;
146 }
147
148 p = strstrip(l);
149 if (!*p)
150 continue;
151
152 if (strchr(COMMENTS, *p))
153 continue;
154
155 value = strchr(p, '=');
156 if (!value) {
157 log_error("Line is not an assignment in file '%s': %s", path, value);
158
159 if (r == 0)
160 r = -EINVAL;
161 continue;
162 }
163
164 *value = 0;
165 value++;
166
167 p = normalize_sysctl(strstrip(p));
168 value = strstrip(value);
169
170 existing = hashmap_get(sysctl_options, p);
171 if (existing) {
172 if (!streq(value, existing))
173 log_warning("Duplicate assignment of %s in file '%s', ignoring.",
174 p, path);
175
176 continue;
177 }
178
179 property = strdup(p);
180 if (!property)
181 return log_oom();
182
183 new_value = strdup(value);
184 if (!new_value) {
185 free(property);
186 return log_oom();
187 }
188
189 k = hashmap_put(sysctl_options, property, new_value);
190 if (k < 0) {
191 log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-r));
192 free(property);
193 free(new_value);
194 return k;
195 }
196 }
197
198 return r;
199 }
200
201 static int help(void) {
202
203 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
204 "Applies kernel sysctl settings.\n\n"
205 " -h --help Show this help\n"
206 " --prefix=PATH Only apply rules that apply to paths with the specified prefix\n",
207 program_invocation_short_name);
208
209 return 0;
210 }
211
212 static int parse_argv(int argc, char *argv[]) {
213
214 enum {
215 ARG_PREFIX
216 };
217
218 static const struct option options[] = {
219 { "help", no_argument, NULL, 'h' },
220 { "prefix", required_argument, NULL, ARG_PREFIX },
221 { NULL, 0, NULL, 0 }
222 };
223
224 int c;
225
226 assert(argc >= 0);
227 assert(argv);
228
229 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
230
231 switch (c) {
232
233 case 'h':
234 help();
235 return 0;
236
237 case ARG_PREFIX: {
238 char *p;
239 char **l;
240
241 for (p = optarg; *p; p++)
242 if (*p == '.')
243 *p = '/';
244
245 l = strv_append(arg_prefixes, optarg);
246 if (!l)
247 return log_oom();
248
249 strv_free(arg_prefixes);
250 arg_prefixes = l;
251
252 break;
253 }
254
255 case '?':
256 return -EINVAL;
257
258 default:
259 log_error("Unknown option code %c", c);
260 return -EINVAL;
261 }
262 }
263
264 return 1;
265 }
266
267 int main(int argc, char *argv[]) {
268 int r = 0, k;
269 Hashmap *sysctl_options;
270
271 r = parse_argv(argc, argv);
272 if (r <= 0)
273 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
274
275 log_set_target(LOG_TARGET_AUTO);
276 log_parse_environment();
277 log_open();
278
279 umask(0022);
280
281 sysctl_options = hashmap_new(string_hash_func, string_compare_func);
282 if (!sysctl_options) {
283 r = log_oom();
284 goto finish;
285 }
286
287 r = 0;
288
289 if (argc > optind) {
290 int i;
291
292 for (i = optind; i < argc; i++) {
293 k = parse_file(sysctl_options, argv[i], false);
294 if (k < 0 && r == 0)
295 r = k;
296 }
297 } else {
298 _cleanup_strv_free_ char **files = NULL;
299 char **f;
300
301 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
302 if (r < 0) {
303 log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
304 goto finish;
305 }
306
307 r = parse_file(sysctl_options, "/etc/sysctl.conf", true);
308
309 STRV_FOREACH(f, files) {
310 k = parse_file(sysctl_options, *f, true);
311 if (k < 0 && r == 0)
312 r = k;
313 }
314 }
315
316 k = apply_all(sysctl_options);
317 if (k < 0 && r == 0)
318 r = k;
319
320 finish:
321 hashmap_free_free_free(sysctl_options);
322 strv_free(arg_prefixes);
323
324 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
325 }