]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/sysctl/sysctl.c
sysctl: apply configuration at once
[thirdparty/systemd.git] / src / sysctl / sysctl.c
CommitLineData
8e1bd70d
LP
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
5430f7f2
LP
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
8e1bd70d
LP
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
5430f7f2 16 Lesser General Public License for more details.
8e1bd70d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8e1bd70d
LP
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>
8e1bd70d
LP
26#include <stdio.h>
27#include <limits.h>
7a2a0b90 28#include <getopt.h>
8e1bd70d
LP
29
30#include "log.h"
db1413d7 31#include "strv.h"
8e1bd70d 32#include "util.h"
f68c5a70 33#include "strv.h"
86fc77c4 34#include "hashmap.h"
9eb977db 35#include "path-util.h"
2c21044f 36#include "conf-files.h"
8e1bd70d
LP
37
38#define PROC_SYS_PREFIX "/proc/sys/"
39
86fc77c4
MS
40static char **arg_prefixes;
41static Hashmap *sysctl_options;
7a2a0b90 42
c1b664d0 43static int apply_sysctl(const char *property, const char *value) {
8e1bd70d 44 char *p, *n;
c1b664d0 45 int r = 0, k;
8e1bd70d
LP
46
47 log_debug("Setting '%s' to '%s'", property, value);
48
7a2a0b90 49 p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property));
0d0f0c50
SL
50 if (!p)
51 return log_oom();
8e1bd70d
LP
52
53 n = stpcpy(p, PROC_SYS_PREFIX);
54 strcpy(n, property);
55
56 for (; *n; n++)
57 if (*n == '.')
58 *n = '/';
59
f68c5a70
LP
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 }
7a2a0b90
LP
75 }
76
77 k = write_one_line_file(p, value);
78 if (k < 0) {
5707631e 79
c1b664d0
LP
80 log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
81 "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
24a35973 82
c1b664d0
LP
83 if (k != -ENOENT && r == 0)
84 r = k;
8e1bd70d
LP
85 }
86
87 free(p);
c1b664d0
LP
88
89 return r;
8e1bd70d
LP
90}
91
86fc77c4
MS
92static 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
107static int parse_file(const char *path, bool ignore_enoent) {
8e1bd70d 108 FILE *f;
c1b664d0 109 int r = 0;
8e1bd70d
LP
110
111 assert(path);
112
86fc77c4
MS
113 f = fopen(path, "re");
114 if (!f) {
c1b664d0
LP
115 if (ignore_enoent && errno == ENOENT)
116 return 0;
117
8e1bd70d 118 log_error("Failed to open file '%s', ignoring: %m", path);
c1b664d0 119 return -errno;
8e1bd70d
LP
120 }
121
86fc77c4 122 log_debug("parse: %s\n", path);
8e1bd70d 123 while (!feof(f)) {
86fc77c4 124 char l[LINE_MAX], *p, *value, *new_value, *property;
8e1bd70d
LP
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);
c1b664d0 131 r = -errno;
8e1bd70d
LP
132 goto finish;
133 }
134
135 p = strstrip(l);
136
137 if (!*p)
138 continue;
139
140 if (strchr(COMMENTS, *p))
141 continue;
142
86fc77c4
MS
143 value = strchr(p, '=');
144 if (!value) {
8e1bd70d 145 log_error("Line is not an assignment in file '%s': %s", path, value);
c1b664d0
LP
146
147 if (r == 0)
148 r = -EINVAL;
8e1bd70d
LP
149 continue;
150 }
151
152 *value = 0;
153 value++;
154
86fc77c4
MS
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 }
8e1bd70d
LP
180 }
181
182finish:
183 fclose(f);
c1b664d0
LP
184
185 return r;
8e1bd70d
LP
186}
187
7a2a0b90
LP
188static 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
199static 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;
f68c5a70 226 char **l;
7a2a0b90
LP
227
228 for (p = optarg; *p; p++)
229 if (*p == '.')
230 *p = '/';
231
f68c5a70 232 l = strv_append(arg_prefixes, optarg);
0d0f0c50
SL
233 if (!l)
234 return log_oom();
f68c5a70
LP
235
236 strv_free(arg_prefixes);
237 arg_prefixes = l;
7a2a0b90
LP
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
8e1bd70d 254int main(int argc, char *argv[]) {
c1b664d0 255 int r = 0;
86fc77c4
MS
256 char *property, *value;
257 Iterator it;
8e1bd70d 258
7a2a0b90
LP
259 r = parse_argv(argc, argv);
260 if (r <= 0)
261 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
262
8e1bd70d
LP
263 log_set_target(LOG_TARGET_AUTO);
264 log_parse_environment();
265 log_open();
266
4c12626c
LP
267 umask(0022);
268
86fc77c4
MS
269 sysctl_options = hashmap_new(string_hash_func, string_compare_func);
270 if (!sysctl_options) {
271 r = log_oom();
272 goto finish;
273 }
274
de19ece7
LP
275 if (argc > optind) {
276 int i;
277
278 for (i = optind; i < argc; i++) {
279 int k;
280
86fc77c4 281 k = parse_file(argv[i], false);
de19ece7
LP
282 if (k < 0 && r == 0)
283 r = k;
284 }
285 } else {
db1413d7 286 char **files, **f;
de19ece7 287 int k;
c1b664d0 288
44143309 289 r = conf_files_list(&files, ".conf",
44143309 290 "/etc/sysctl.d",
fc1a2e06 291 "/run/sysctl.d",
40013ebc 292 "/usr/local/lib/sysctl.d",
44143309 293 "/usr/lib/sysctl.d",
de19ece7 294#ifdef HAVE_SPLIT_USR
223a3558 295 "/lib/sysctl.d",
de19ece7 296#endif
44143309
KS
297 NULL);
298 if (r < 0) {
299 log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
300 goto finish;
301 }
db1413d7 302
86fc77c4
MS
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);
db1413d7
KS
311 if (k < 0 && r == 0)
312 r = k;
313 }
314
315 strv_free(files);
8e1bd70d 316 }
86fc77c4
MS
317
318 r = apply_all();
44143309 319finish:
86fc77c4
MS
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
f68c5a70
LP
327 strv_free(arg_prefixes);
328
c1b664d0 329 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
8e1bd70d 330}