]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/sysctl/sysctl.c
treewide: a few more log_*_errno + return simplifications
[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"
86fc77c4 33#include "hashmap.h"
9eb977db 34#include "path-util.h"
2c21044f 35#include "conf-files.h"
a5c32cff 36#include "fileio.h"
eb9da376 37#include "build.h"
8e1bd70d 38
fabe5c0e 39static char **arg_prefixes = NULL;
8e1bd70d 40
7f0a55d4 41static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysctl");
fabe5c0e 42
2e573fcf 43static char* normalize_sysctl(char *s) {
fabe5c0e
LP
44 char *n;
45
2e573fcf
ZJS
46 n = strpbrk(s, "/.");
47 /* If the first separator is a slash, the path is
48 * assumed to be normalized and slashes remain slashes
49 * and dots remains dots. */
50 if (!n || *n == '/')
51 return s;
52
53 /* Otherwise, dots become slashes and slashes become
54 * dots. Fun. */
55 while (n) {
fabe5c0e
LP
56 if (*n == '.')
57 *n = '/';
2e573fcf
ZJS
58 else
59 *n = '.';
60
61 n = strpbrk(n + 1, "/.");
62 }
fabe5c0e
LP
63
64 return s;
65}
7a2a0b90 66
c1b664d0 67static int apply_sysctl(const char *property, const char *value) {
fabe5c0e
LP
68 _cleanup_free_ char *p = NULL;
69 char *n;
c1b664d0 70 int r = 0, k;
8e1bd70d
LP
71
72 log_debug("Setting '%s' to '%s'", property, value);
73
f8294e41 74 p = new(char, strlen("/proc/sys/") + strlen(property) + 1);
0d0f0c50
SL
75 if (!p)
76 return log_oom();
8e1bd70d 77
fabe5c0e 78 n = stpcpy(p, "/proc/sys/");
8e1bd70d
LP
79 strcpy(n, property);
80
f68c5a70
LP
81 if (!strv_isempty(arg_prefixes)) {
82 char **i;
83 bool good = false;
84
85 STRV_FOREACH(i, arg_prefixes)
86 if (path_startswith(p, *i)) {
87 good = true;
88 break;
89 }
90
91 if (!good) {
92 log_debug("Skipping %s", p);
f68c5a70
LP
93 return 0;
94 }
7a2a0b90
LP
95 }
96
574d5f2d 97 k = write_string_file(p, value);
7a2a0b90 98 if (k < 0) {
c1b664d0
LP
99 log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
100 "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
24a35973 101
c1b664d0
LP
102 if (k != -ENOENT && r == 0)
103 r = k;
8e1bd70d
LP
104 }
105
c1b664d0 106 return r;
8e1bd70d
LP
107}
108
fabe5c0e 109static int apply_all(Hashmap *sysctl_options) {
86fc77c4
MS
110 int r = 0;
111 char *property, *value;
112 Iterator i;
113
fabe5c0e
LP
114 assert(sysctl_options);
115
86fc77c4
MS
116 HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
117 int k;
118
119 k = apply_sysctl(property, value);
120 if (k < 0 && r == 0)
121 r = k;
122 }
123 return r;
124}
125
fabe5c0e
LP
126static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
127 _cleanup_fclose_ FILE *f = NULL;
128 int r;
8e1bd70d
LP
129
130 assert(path);
131
4cf7ea55 132 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
fabe5c0e 133 if (r < 0) {
6f6fad96 134 if (ignore_enoent && r == -ENOENT)
c1b664d0
LP
135 return 0;
136
8d3d7072 137 return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
8e1bd70d
LP
138 }
139
9f6445e3 140 log_debug("parse: %s", path);
8e1bd70d 141 while (!feof(f)) {
fabe5c0e 142 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
04bf3c1a 143 void *v;
fabe5c0e 144 int k;
8e1bd70d
LP
145
146 if (!fgets(l, sizeof(l), f)) {
147 if (feof(f))
148 break;
149
150 log_error("Failed to read file '%s', ignoring: %m", path);
fabe5c0e 151 return -errno;
8e1bd70d
LP
152 }
153
154 p = strstrip(l);
8e1bd70d
LP
155 if (!*p)
156 continue;
157
d3b6d0c2 158 if (strchr(COMMENTS "\n", *p))
8e1bd70d
LP
159 continue;
160
86fc77c4
MS
161 value = strchr(p, '=');
162 if (!value) {
8e1bd70d 163 log_error("Line is not an assignment in file '%s': %s", path, value);
c1b664d0
LP
164
165 if (r == 0)
166 r = -EINVAL;
8e1bd70d
LP
167 continue;
168 }
169
170 *value = 0;
171 value++;
172
fabe5c0e
LP
173 p = normalize_sysctl(strstrip(p));
174 value = strstrip(value);
175
04bf3c1a 176 existing = hashmap_get2(sysctl_options, p, &v);
fabe5c0e 177 if (existing) {
04bf3c1a
KS
178 if (streq(value, existing))
179 continue;
fabe5c0e 180
04bf3c1a
KS
181 log_info("Overwriting earlier assignment of %s in file '%s'.", p, path);
182 free(hashmap_remove(sysctl_options, p));
183 free(v);
86fc77c4
MS
184 }
185
fabe5c0e
LP
186 property = strdup(p);
187 if (!property)
188 return log_oom();
189
190 new_value = strdup(value);
86fc77c4
MS
191 if (!new_value) {
192 free(property);
fabe5c0e 193 return log_oom();
86fc77c4
MS
194 }
195
fabe5c0e
LP
196 k = hashmap_put(sysctl_options, property, new_value);
197 if (k < 0) {
da927ba9 198 log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property);
86fc77c4
MS
199 free(property);
200 free(new_value);
fabe5c0e 201 return k;
86fc77c4 202 }
8e1bd70d
LP
203 }
204
c1b664d0 205 return r;
8e1bd70d
LP
206}
207
601185b4 208static void help(void) {
7a2a0b90
LP
209 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
210 "Applies kernel sysctl settings.\n\n"
211 " -h --help Show this help\n"
eb9da376 212 " --version Show package version\n"
0e1f5792 213 " --prefix=PATH Only apply rules with the specified prefix\n"
601185b4 214 , program_invocation_short_name);
7a2a0b90
LP
215}
216
217static int parse_argv(int argc, char *argv[]) {
218
219 enum {
eb9da376 220 ARG_VERSION = 0x100,
7a2a0b90
LP
221 ARG_PREFIX
222 };
223
224 static const struct option options[] = {
225 { "help", no_argument, NULL, 'h' },
eb9da376 226 { "version", no_argument, NULL, ARG_VERSION },
7a2a0b90 227 { "prefix", required_argument, NULL, ARG_PREFIX },
eb9da376 228 {}
7a2a0b90
LP
229 };
230
231 int c;
232
233 assert(argc >= 0);
234 assert(argv);
235
601185b4 236 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
7a2a0b90
LP
237
238 switch (c) {
239
240 case 'h':
601185b4
ZJS
241 help();
242 return 0;
eb9da376
LP
243
244 case ARG_VERSION:
245 puts(PACKAGE_STRING);
246 puts(SYSTEMD_FEATURES);
7a2a0b90
LP
247 return 0;
248
249 case ARG_PREFIX: {
250 char *p;
251
0e1f5792
DH
252 /* We used to require people to specify absolute paths
253 * in /proc/sys in the past. This is kinda useless, but
254 * we need to keep compatibility. We now support any
255 * sysctl name available. */
256 normalize_sysctl(optarg);
257 if (startswith(optarg, "/proc/sys"))
258 p = strdup(optarg);
259 else
260 p = strappend("/proc/sys/", optarg);
261
262 if (!p)
263 return log_oom();
264 if (strv_consume(&arg_prefixes, p) < 0)
0d0f0c50 265 return log_oom();
f68c5a70 266
7a2a0b90
LP
267 break;
268 }
269
270 case '?':
271 return -EINVAL;
272
273 default:
eb9da376 274 assert_not_reached("Unhandled option");
7a2a0b90 275 }
7a2a0b90
LP
276
277 return 1;
278}
279
8e1bd70d 280int main(int argc, char *argv[]) {
0187f62b 281 int r = 0, k;
fabe5c0e 282 Hashmap *sysctl_options;
8e1bd70d 283
7a2a0b90
LP
284 r = parse_argv(argc, argv);
285 if (r <= 0)
286 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
287
8e1bd70d
LP
288 log_set_target(LOG_TARGET_AUTO);
289 log_parse_environment();
290 log_open();
291
4c12626c
LP
292 umask(0022);
293
d5099efc 294 sysctl_options = hashmap_new(&string_hash_ops);
86fc77c4
MS
295 if (!sysctl_options) {
296 r = log_oom();
297 goto finish;
298 }
299
0187f62b
LP
300 r = 0;
301
de19ece7
LP
302 if (argc > optind) {
303 int i;
304
305 for (i = optind; i < argc; i++) {
fabe5c0e
LP
306 k = parse_file(sysctl_options, argv[i], false);
307 if (k < 0 && r == 0)
de19ece7
LP
308 r = k;
309 }
310 } else {
fabe5c0e
LP
311 _cleanup_strv_free_ char **files = NULL;
312 char **f;
c1b664d0 313
fabe5c0e 314 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
44143309 315 if (r < 0) {
da927ba9 316 log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
44143309
KS
317 goto finish;
318 }
db1413d7 319
fabe5c0e
LP
320 STRV_FOREACH(f, files) {
321 k = parse_file(sysctl_options, *f, true);
322 if (k < 0 && r == 0)
db1413d7
KS
323 r = k;
324 }
8e1bd70d 325 }
86fc77c4 326
fabe5c0e
LP
327 k = apply_all(sysctl_options);
328 if (k < 0 && r == 0)
0187f62b
LP
329 r = k;
330
44143309 331finish:
fabe5c0e 332 hashmap_free_free_free(sysctl_options);
f68c5a70
LP
333 strv_free(arg_prefixes);
334
c1b664d0 335 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
8e1bd70d 336}