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