chiark / gitweb /
793796d36381a8909123502bb584647fcba35eb1
[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 #define PROC_SYS_PREFIX "/proc/sys/"
39
40 static char **arg_prefixes;
41 static Hashmap *sysctl_options;
42
43 static int apply_sysctl(const char *property, const char *value) {
44         char *p, *n;
45         int r = 0, k;
46
47         log_debug("Setting '%s' to '%s'", property, value);
48
49         p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property));
50         if (!p)
51                 return log_oom();
52
53         n = stpcpy(p, PROC_SYS_PREFIX);
54         strcpy(n, property);
55
56         for (; *n; n++)
57                 if (*n == '.')
58                         *n = '/';
59
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                 }
75         }
76
77         k = write_one_line_file(p, value);
78         if (k < 0) {
79
80                 log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
81                          "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
82
83                 if (k != -ENOENT && r == 0)
84                         r = k;
85         }
86
87         free(p);
88
89         return r;
90 }
91
92 static 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
107 static int parse_file(const char *path, bool ignore_enoent) {
108         FILE *f;
109         int r = 0;
110
111         assert(path);
112
113         f = fopen(path, "re");
114         if (!f) {
115                 if (ignore_enoent && errno == ENOENT)
116                         return 0;
117
118                 log_error("Failed to open file '%s', ignoring: %m", path);
119                 return -errno;
120         }
121
122         log_debug("parse: %s\n", path);
123         while (!feof(f)) {
124                 char l[LINE_MAX], *p, *value, *new_value, *property;
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);
131                         r = -errno;
132                         goto finish;
133                 }
134
135                 p = strstrip(l);
136
137                 if (!*p)
138                         continue;
139
140                 if (strchr(COMMENTS, *p))
141                         continue;
142
143                 value = strchr(p, '=');
144                 if (!value) {
145                         log_error("Line is not an assignment in file '%s': %s", path, value);
146
147                         if (r == 0)
148                                 r = -EINVAL;
149                         continue;
150                 }
151
152                 *value = 0;
153                 value++;
154
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) {
170                         if (r == -EEXIST)
171                                 log_debug("Skipping previously assigned sysctl variable %s", property);
172                         else
173                                 log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-r));
174
175                         free(property);
176                         free(new_value);
177                         if (r != -EEXIST)
178                                 goto finish;
179                 }
180         }
181
182 finish:
183         fclose(f);
184
185         return r;
186 }
187
188 static int help(void) {
189
190         printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
191                "Applies kernel sysctl settings.\n\n"
192                "  -h --help             Show this help\n"
193                "     --prefix=PATH      Only apply rules that apply to paths with the specified prefix\n",
194                program_invocation_short_name);
195
196         return 0;
197 }
198
199 static int parse_argv(int argc, char *argv[]) {
200
201         enum {
202                 ARG_PREFIX
203         };
204
205         static const struct option options[] = {
206                 { "help",      no_argument,       NULL, 'h'           },
207                 { "prefix",    required_argument, NULL, ARG_PREFIX    },
208                 { NULL,        0,                 NULL, 0             }
209         };
210
211         int c;
212
213         assert(argc >= 0);
214         assert(argv);
215
216         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
217
218                 switch (c) {
219
220                 case 'h':
221                         help();
222                         return 0;
223
224                 case ARG_PREFIX: {
225                         char *p;
226                         char **l;
227
228                         for (p = optarg; *p; p++)
229                                 if (*p == '.')
230                                         *p = '/';
231
232                         l = strv_append(arg_prefixes, optarg);
233                         if (!l)
234                                 return log_oom();
235
236                         strv_free(arg_prefixes);
237                         arg_prefixes = l;
238
239                         break;
240                 }
241
242                 case '?':
243                         return -EINVAL;
244
245                 default:
246                         log_error("Unknown option code %c", c);
247                         return -EINVAL;
248                 }
249         }
250
251         return 1;
252 }
253
254 int main(int argc, char *argv[]) {
255         int r = 0;
256         char *property, *value;
257         Iterator it;
258
259         r = parse_argv(argc, argv);
260         if (r <= 0)
261                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
262
263         log_set_target(LOG_TARGET_AUTO);
264         log_parse_environment();
265         log_open();
266
267         umask(0022);
268
269         sysctl_options = hashmap_new(string_hash_func, string_compare_func);
270         if (!sysctl_options) {
271                 r = log_oom();
272                 goto finish;
273         }
274
275         if (argc > optind) {
276                 int i;
277
278                 for (i = optind; i < argc; i++) {
279                         int k;
280
281                         k = parse_file(argv[i], false);
282                         if (k < 0 && r == 0)
283                                 r = k;
284                 }
285         } else {
286                 char **files, **f;
287                 int k;
288
289                 r = conf_files_list(&files, ".conf",
290                                     "/etc/sysctl.d",
291                                     "/run/sysctl.d",
292                                     "/usr/local/lib/sysctl.d",
293                                     "/usr/lib/sysctl.d",
294 #ifdef HAVE_SPLIT_USR
295                                     "/lib/sysctl.d",
296 #endif
297                                     NULL);
298                 if (r < 0) {
299                         log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
300                         goto finish;
301                 }
302
303                 /* We parse the files in decreasing order of precedence.
304                  * parse_file() will skip keys that were already assigned. */
305
306                 r = parse_file("/etc/sysctl.conf", true);
307
308                 f = files + strv_length(files) - 1;
309                 STRV_FOREACH_BACKWARDS(f, files) {
310                         k = parse_file(*f, true);
311                         if (k < 0 && r == 0)
312                                 r = k;
313                 }
314
315                 strv_free(files);
316         }
317
318         r = apply_all();
319 finish:
320         HASHMAP_FOREACH_KEY(value, property, sysctl_options, it) {
321                 hashmap_remove(sysctl_options, property);
322                 free(property);
323                 free(value);
324         }
325         hashmap_free(sysctl_options);
326
327         strv_free(arg_prefixes);
328
329         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
330 }