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