chiark / gitweb /
Verify validity of session name when received from outside
[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 #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_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\n", 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                "     --prefix=PATH      Only apply rules that apply to paths with the specified prefix\n",
209                program_invocation_short_name);
210
211         return 0;
212 }
213
214 static int parse_argv(int argc, char *argv[]) {
215
216         enum {
217                 ARG_PREFIX
218         };
219
220         static const struct option options[] = {
221                 { "help",      no_argument,       NULL, 'h'           },
222                 { "prefix",    required_argument, NULL, ARG_PREFIX    },
223                 { NULL,        0,                 NULL, 0             }
224         };
225
226         int c;
227
228         assert(argc >= 0);
229         assert(argv);
230
231         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
232
233                 switch (c) {
234
235                 case 'h':
236                         help();
237                         return 0;
238
239                 case ARG_PREFIX: {
240                         char *p;
241                         char **l;
242
243                         for (p = optarg; *p; p++)
244                                 if (*p == '.')
245                                         *p = '/';
246
247                         l = strv_append(arg_prefixes, optarg);
248                         if (!l)
249                                 return log_oom();
250
251                         strv_free(arg_prefixes);
252                         arg_prefixes = l;
253
254                         break;
255                 }
256
257                 case '?':
258                         return -EINVAL;
259
260                 default:
261                         log_error("Unknown option code %c", c);
262                         return -EINVAL;
263                 }
264         }
265
266         return 1;
267 }
268
269 int main(int argc, char *argv[]) {
270         int r = 0, k;
271         Hashmap *sysctl_options;
272
273         r = parse_argv(argc, argv);
274         if (r <= 0)
275                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
276
277         log_set_target(LOG_TARGET_AUTO);
278         log_parse_environment();
279         log_open();
280
281         umask(0022);
282
283         sysctl_options = hashmap_new(string_hash_func, string_compare_func);
284         if (!sysctl_options) {
285                 r = log_oom();
286                 goto finish;
287         }
288
289         r = 0;
290
291         if (argc > optind) {
292                 int i;
293
294                 for (i = optind; i < argc; i++) {
295                         k = parse_file(sysctl_options, argv[i], false);
296                         if (k < 0 && r == 0)
297                                 r = k;
298                 }
299         } else {
300                 _cleanup_strv_free_ char **files = NULL;
301                 char **f;
302
303                 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
304                 if (r < 0) {
305                         log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
306                         goto finish;
307                 }
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 }