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