]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysctl/sysctl.c
util-lib: move CONF_DIRS_NULSTR definition to def.h
[thirdparty/systemd.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 <errno.h>
23 #include <getopt.h>
24 #include <limits.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "conf-files.h"
31 #include "def.h"
32 #include "fd-util.h"
33 #include "fileio.h"
34 #include "hashmap.h"
35 #include "log.h"
36 #include "path-util.h"
37 #include "string-util.h"
38 #include "strv.h"
39 #include "sysctl-util.h"
40 #include "util.h"
41
42 static char **arg_prefixes = NULL;
43
44 static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysctl");
45
46 static int apply_all(Hashmap *sysctl_options) {
47 char *property, *value;
48 Iterator i;
49 int r = 0;
50
51 HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
52 int k;
53
54 k = sysctl_write(property, value);
55 if (k < 0) {
56 log_full_errno(k == -ENOENT ? LOG_INFO : LOG_WARNING, k,
57 "Couldn't write '%s' to '%s', ignoring: %m", value, property);
58
59 if (r == 0 && k != -ENOENT)
60 r = k;
61 }
62 }
63
64 return r;
65 }
66
67 static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
68 _cleanup_fclose_ FILE *f = NULL;
69 int r;
70
71 assert(path);
72
73 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
74 if (r < 0) {
75 if (ignore_enoent && r == -ENOENT)
76 return 0;
77
78 return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
79 }
80
81 log_debug("Parsing %s", path);
82 while (!feof(f)) {
83 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
84 void *v;
85 int k;
86
87 if (!fgets(l, sizeof(l), f)) {
88 if (feof(f))
89 break;
90
91 log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
92 return -errno;
93 }
94
95 p = strstrip(l);
96 if (!*p)
97 continue;
98
99 if (strchr(COMMENTS "\n", *p))
100 continue;
101
102 value = strchr(p, '=');
103 if (!value) {
104 log_error("Line is not an assignment in file '%s': %s", path, value);
105
106 if (r == 0)
107 r = -EINVAL;
108 continue;
109 }
110
111 *value = 0;
112 value++;
113
114 p = sysctl_normalize(strstrip(p));
115 value = strstrip(value);
116
117 if (!strv_isempty(arg_prefixes)) {
118 char **i, *t;
119 STRV_FOREACH(i, arg_prefixes) {
120 t = path_startswith(*i, "/proc/sys/");
121 if (t == NULL)
122 t = *i;
123 if (path_startswith(p, t))
124 goto found;
125 }
126 /* not found */
127 continue;
128 }
129
130 found:
131 existing = hashmap_get2(sysctl_options, p, &v);
132 if (existing) {
133 if (streq(value, existing))
134 continue;
135
136 log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path);
137 free(hashmap_remove(sysctl_options, p));
138 free(v);
139 }
140
141 property = strdup(p);
142 if (!property)
143 return log_oom();
144
145 new_value = strdup(value);
146 if (!new_value) {
147 free(property);
148 return log_oom();
149 }
150
151 k = hashmap_put(sysctl_options, property, new_value);
152 if (k < 0) {
153 log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property);
154 free(property);
155 free(new_value);
156 return k;
157 }
158 }
159
160 return r;
161 }
162
163 static void help(void) {
164 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
165 "Applies kernel sysctl settings.\n\n"
166 " -h --help Show this help\n"
167 " --version Show package version\n"
168 " --prefix=PATH Only apply rules with the specified prefix\n"
169 , program_invocation_short_name);
170 }
171
172 static int parse_argv(int argc, char *argv[]) {
173
174 enum {
175 ARG_VERSION = 0x100,
176 ARG_PREFIX
177 };
178
179 static const struct option options[] = {
180 { "help", no_argument, NULL, 'h' },
181 { "version", no_argument, NULL, ARG_VERSION },
182 { "prefix", required_argument, NULL, ARG_PREFIX },
183 {}
184 };
185
186 int c;
187
188 assert(argc >= 0);
189 assert(argv);
190
191 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
192
193 switch (c) {
194
195 case 'h':
196 help();
197 return 0;
198
199 case ARG_VERSION:
200 return version();
201
202 case ARG_PREFIX: {
203 char *p;
204
205 /* We used to require people to specify absolute paths
206 * in /proc/sys in the past. This is kinda useless, but
207 * we need to keep compatibility. We now support any
208 * sysctl name available. */
209 sysctl_normalize(optarg);
210
211 if (startswith(optarg, "/proc/sys"))
212 p = strdup(optarg);
213 else
214 p = strappend("/proc/sys/", optarg);
215 if (!p)
216 return log_oom();
217
218 if (strv_consume(&arg_prefixes, p) < 0)
219 return log_oom();
220
221 break;
222 }
223
224 case '?':
225 return -EINVAL;
226
227 default:
228 assert_not_reached("Unhandled option");
229 }
230
231 return 1;
232 }
233
234 int main(int argc, char *argv[]) {
235 int r = 0, k;
236 Hashmap *sysctl_options;
237
238 r = parse_argv(argc, argv);
239 if (r <= 0)
240 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
241
242 log_set_target(LOG_TARGET_AUTO);
243 log_parse_environment();
244 log_open();
245
246 umask(0022);
247
248 sysctl_options = hashmap_new(&string_hash_ops);
249 if (!sysctl_options) {
250 r = log_oom();
251 goto finish;
252 }
253
254 r = 0;
255
256 if (argc > optind) {
257 int i;
258
259 for (i = optind; i < argc; i++) {
260 k = parse_file(sysctl_options, argv[i], false);
261 if (k < 0 && r == 0)
262 r = k;
263 }
264 } else {
265 _cleanup_strv_free_ char **files = NULL;
266 char **f;
267
268 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
269 if (r < 0) {
270 log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
271 goto finish;
272 }
273
274 STRV_FOREACH(f, files) {
275 k = parse_file(sysctl_options, *f, true);
276 if (k < 0 && r == 0)
277 r = k;
278 }
279 }
280
281 k = apply_all(sysctl_options);
282 if (k < 0 && r == 0)
283 r = k;
284
285 finish:
286 hashmap_free_free_free(sysctl_options);
287 strv_free(arg_prefixes);
288
289 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
290 }