]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysctl/sysctl.c
Merge pull request #1880 from fsateler/sysctl-doc
[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_PATHS_NULSTR("sysctl.d");
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 return log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
92 }
93
94 p = strstrip(l);
95 if (!*p)
96 continue;
97
98 if (strchr(COMMENTS "\n", *p))
99 continue;
100
101 value = strchr(p, '=');
102 if (!value) {
103 log_error("Line is not an assignment in file '%s': %s", path, value);
104
105 if (r == 0)
106 r = -EINVAL;
107 continue;
108 }
109
110 *value = 0;
111 value++;
112
113 p = sysctl_normalize(strstrip(p));
114 value = strstrip(value);
115
116 if (!strv_isempty(arg_prefixes)) {
117 char **i, *t;
118 STRV_FOREACH(i, arg_prefixes) {
119 t = path_startswith(*i, "/proc/sys/");
120 if (t == NULL)
121 t = *i;
122 if (path_startswith(p, t))
123 goto found;
124 }
125 /* not found */
126 continue;
127 }
128
129 found:
130 existing = hashmap_get2(sysctl_options, p, &v);
131 if (existing) {
132 if (streq(value, existing))
133 continue;
134
135 log_debug("Overwriting earlier assignment of %s in file '%s'.", p, path);
136 free(hashmap_remove(sysctl_options, p));
137 free(v);
138 }
139
140 property = strdup(p);
141 if (!property)
142 return log_oom();
143
144 new_value = strdup(value);
145 if (!new_value) {
146 free(property);
147 return log_oom();
148 }
149
150 k = hashmap_put(sysctl_options, property, new_value);
151 if (k < 0) {
152 log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property);
153 free(property);
154 free(new_value);
155 return k;
156 }
157 }
158
159 return r;
160 }
161
162 static void help(void) {
163 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
164 "Applies kernel sysctl settings.\n\n"
165 " -h --help Show this help\n"
166 " --version Show package version\n"
167 " --prefix=PATH Only apply rules with the specified prefix\n"
168 , program_invocation_short_name);
169 }
170
171 static int parse_argv(int argc, char *argv[]) {
172
173 enum {
174 ARG_VERSION = 0x100,
175 ARG_PREFIX
176 };
177
178 static const struct option options[] = {
179 { "help", no_argument, NULL, 'h' },
180 { "version", no_argument, NULL, ARG_VERSION },
181 { "prefix", required_argument, NULL, ARG_PREFIX },
182 {}
183 };
184
185 int c;
186
187 assert(argc >= 0);
188 assert(argv);
189
190 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
191
192 switch (c) {
193
194 case 'h':
195 help();
196 return 0;
197
198 case ARG_VERSION:
199 return version();
200
201 case ARG_PREFIX: {
202 char *p;
203
204 /* We used to require people to specify absolute paths
205 * in /proc/sys in the past. This is kinda useless, but
206 * we need to keep compatibility. We now support any
207 * sysctl name available. */
208 sysctl_normalize(optarg);
209
210 if (startswith(optarg, "/proc/sys"))
211 p = strdup(optarg);
212 else
213 p = strappend("/proc/sys/", optarg);
214 if (!p)
215 return log_oom();
216
217 if (strv_consume(&arg_prefixes, p) < 0)
218 return log_oom();
219
220 break;
221 }
222
223 case '?':
224 return -EINVAL;
225
226 default:
227 assert_not_reached("Unhandled option");
228 }
229
230 return 1;
231 }
232
233 int main(int argc, char *argv[]) {
234 int r = 0, k;
235 Hashmap *sysctl_options;
236
237 r = parse_argv(argc, argv);
238 if (r <= 0)
239 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
240
241 log_set_target(LOG_TARGET_AUTO);
242 log_parse_environment();
243 log_open();
244
245 umask(0022);
246
247 sysctl_options = hashmap_new(&string_hash_ops);
248 if (!sysctl_options) {
249 r = log_oom();
250 goto finish;
251 }
252
253 r = 0;
254
255 if (argc > optind) {
256 int i;
257
258 for (i = optind; i < argc; i++) {
259 k = parse_file(sysctl_options, argv[i], false);
260 if (k < 0 && r == 0)
261 r = k;
262 }
263 } else {
264 _cleanup_strv_free_ char **files = NULL;
265 char **f;
266
267 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
268 if (r < 0) {
269 log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
270 goto finish;
271 }
272
273 STRV_FOREACH(f, files) {
274 k = parse_file(sysctl_options, *f, true);
275 if (k < 0 && r == 0)
276 r = k;
277 }
278 }
279
280 k = apply_all(sysctl_options);
281 if (k < 0 && r == 0)
282 r = k;
283
284 finish:
285 hashmap_free_free_free(sysctl_options);
286 strv_free(arg_prefixes);
287
288 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
289 }