]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/sysctl/sysctl.c
sysctl: tweak debug message
[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 <stdlib.h>
23 #include <stdbool.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <limits.h>
28 #include <getopt.h>
29
30 #include "log.h"
31 #include "strv.h"
32 #include "util.h"
33 #include "hashmap.h"
34 #include "path-util.h"
35 #include "conf-files.h"
36 #include "fileio.h"
37 #include "build.h"
38 #include "sysctl-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 int r = 0;
46 char *property, *value;
47 Iterator i;
48
49 assert(sysctl_options);
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(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
57 "Failed to write '%s' to '%s': %s", value, property, strerror(-k));
58
59 if (r == 0)
60 r = k;
61 }
62 }
63 return r;
64 }
65
66 static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
67 _cleanup_fclose_ FILE *f = NULL;
68 int r;
69
70 assert(path);
71
72 r = search_and_fopen_nulstr(path, "re", NULL, conf_file_dirs, &f);
73 if (r < 0) {
74 if (ignore_enoent && r == -ENOENT)
75 return 0;
76
77 return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
78 }
79
80 log_debug("Parsing %s", path);
81 while (!feof(f)) {
82 char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
83 void *v;
84 int k;
85
86 if (!fgets(l, sizeof(l), f)) {
87 if (feof(f))
88 break;
89
90 log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path);
91 return -errno;
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 puts(PACKAGE_STRING);
200 puts(SYSTEMD_FEATURES);
201 return 0;
202
203 case ARG_PREFIX: {
204 char *p;
205
206 /* We used to require people to specify absolute paths
207 * in /proc/sys in the past. This is kinda useless, but
208 * we need to keep compatibility. We now support any
209 * sysctl name available. */
210 sysctl_normalize(optarg);
211 if (startswith(optarg, "/proc/sys"))
212 p = strdup(optarg);
213 else
214 p = strappend("/proc/sys/", optarg);
215
216 if (!p)
217 return log_oom();
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 }