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