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