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