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