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