• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 24917789359

24 Apr 2026 04:59PM UTC coverage: 71.97% (-0.2%) from 72.201%
24917789359

push

github

bluca
units: order networkd resolve hook After=network-pre.target

Without this, the socket is available well before systemd-networkd.service
is able to start, because of its own After=network-pre.target ordering.
Then, if resolved handles queries before network-pre.target, it will
hang waiting for networkd to reply to hook queries.

This is currently happening in the wild with cloud-init.

322629 of 448281 relevant lines covered (71.97%)

1175521.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.12
/src/sysctl/sysctl.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdio.h>
4
#include <sys/stat.h>
5

6
#include "alloc-util.h"
7
#include "build.h"
8
#include "conf-files.h"
9
#include "constants.h"
10
#include "creds-util.h"
11
#include "errno-util.h"
12
#include "format-table.h"
13
#include "glob-util.h"
14
#include "hashmap.h"
15
#include "log.h"
16
#include "main-func.h"
17
#include "options.h"
18
#include "pager.h"
19
#include "path-util.h"
20
#include "pretty-print.h"
21
#include "string-util.h"
22
#include "strv.h"
23
#include "sysctl-util.h"
24

25
static char **arg_prefixes = NULL;
26
static CatFlags arg_cat_flags = CAT_CONFIG_OFF;
27
static bool arg_strict = false;
28
static bool arg_inline = false;
29
static PagerFlags arg_pager_flags = 0;
30

31
STATIC_DESTRUCTOR_REGISTER(arg_prefixes, strv_freep);
1,133✔
32

33
typedef struct SysctlOption {
34
        char *key;
35
        char *value;
36
        bool ignore_failure;
37
} SysctlOption;
38

39
static SysctlOption* sysctl_option_free(SysctlOption *o) {
6,231✔
40
        if (!o)
6,231✔
41
                return NULL;
42

43
        free(o->key);
6,231✔
44
        free(o->value);
6,231✔
45

46
        return mfree(o);
6,231✔
47
}
48

49
DEFINE_TRIVIAL_CLEANUP_FUNC(SysctlOption*, sysctl_option_free);
12,462✔
50
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
6,231✔
51
                sysctl_option_hash_ops,
52
                char, string_hash_func, string_compare_func,
53
                SysctlOption, sysctl_option_free);
54

55
static bool test_prefix(const char *p) {
26,844✔
56
        if (strv_isempty(arg_prefixes))
26,844✔
57
                return true;
58

59
        return path_startswith_strv(p, arg_prefixes);
23,976✔
60
}
61

62
static SysctlOption* sysctl_option_new(
6,231✔
63
                const char *key,
64
                const char *value,
65
                bool ignore_failure) {
66

67
        _cleanup_(sysctl_option_freep) SysctlOption *o = NULL;
6,231✔
68

69
        assert(key);
6,231✔
70

71
        o = new(SysctlOption, 1);
6,231✔
72
        if (!o)
6,231✔
73
                return NULL;
74

75
        *o = (SysctlOption) {
12,462✔
76
                .key = strdup(key),
6,231✔
77
                .value = value ? strdup(value) : NULL,
6,231✔
78
                .ignore_failure = ignore_failure,
79
        };
80

81
        if (!o->key)
6,231✔
82
                return NULL;
83
        if (value && !o->value)
6,231✔
84
                return NULL;
85

86
        return TAKE_PTR(o);
6,231✔
87
}
88

89
static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_failure, bool ignore_enoent) {
5,871✔
90
        int r;
5,871✔
91

92
        r = sysctl_write(key, value);
5,871✔
93
        if (r < 0) {
5,871✔
94
                /* Proceed without failing if ignore_failure is true.
95
                 * If the sysctl is not available in the kernel or we are running with reduced privileges and
96
                 * cannot write it, then log about the issue, and proceed without failing. Unless strict mode
97
                 * (arg_strict = true) is enabled, in which case we should fail. (EROFS is treated as a
98
                 * permission problem here, since that's how container managers usually protected their
99
                 * sysctls.)
100
                 * In all other cases log an error and make the tool fail. */
101
                if (ignore_failure || (!arg_strict && ERRNO_IS_NEG_FS_WRITE_REFUSED(r)))
1,105✔
102
                        log_debug_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
446✔
103
                else if (ignore_enoent && r == -ENOENT)
659✔
104
                        log_warning_errno(r, "Couldn't write '%s' to '%s', ignoring: %m", value, key);
656✔
105
                else
106
                        return log_error_errno(r, "Couldn't write '%s' to '%s': %m", value, key);
3✔
107
        }
108

109
        return 0;
110
}
111

112
static int apply_glob_option_with_prefix(OrderedHashmap *sysctl_options, SysctlOption *option, const char *prefix) {
12,354✔
113
        _cleanup_strv_free_ char **paths = NULL;
×
114
        _cleanup_free_ char *pattern = NULL;
12,354✔
115
        int r;
12,354✔
116

117
        assert(sysctl_options);
12,354✔
118
        assert(option);
12,354✔
119

120
        if (prefix) {
12,354✔
121
                _cleanup_free_ char *key = NULL;
11,997✔
122

123
                r = path_glob_can_match(option->key, prefix, &key);
11,997✔
124
                if (r < 0)
11,997✔
125
                        return log_error_errno(r, "Failed to check if the glob '%s' matches prefix '%s': %m",
×
126
                                               option->key, prefix);
127
                if (r == 0) {
11,997✔
128
                        log_debug("The glob '%s' does not match prefix '%s'.", option->key, prefix);
8,994✔
129
                        return 0;
8,994✔
130
                }
131

132
                log_debug("The glob '%s' is prefixed with '%s': '%s'", option->key, prefix, key);
3,003✔
133

134
                if (!string_is_glob(key)) {
3,003✔
135
                        /* The prefixed pattern is not glob anymore. Let's skip to call glob(). */
136
                        if (ordered_hashmap_contains(sysctl_options, key)) {
3,003✔
137
                                log_debug("Not setting %s (explicit setting exists).", key);
×
138
                                return 0;
×
139
                        }
140

141
                        return sysctl_write_or_warn(key, option->value,
3,003✔
142
                                                    /* ignore_failure= */ option->ignore_failure,
3,003✔
143
                                                    /* ignore_enoent= */ true);
144
                }
145

146
                pattern = path_join("/proc/sys", key);
×
147
        } else
148
                pattern = path_join("/proc/sys", option->key);
357✔
149
        if (!pattern)
357✔
150
                return log_oom();
×
151

152
        r = glob_extend(&paths, pattern, GLOB_NOCHECK);
357✔
153
        if (r < 0) {
357✔
154
                if (r == -ENOENT) {
×
155
                        log_debug("No match for glob: %s", option->key);
×
156
                        return 0;
×
157
                }
158
                if (option->ignore_failure || ERRNO_IS_PRIVILEGE(r)) {
×
159
                        log_debug_errno(r, "Failed to resolve glob '%s', ignoring: %m", option->key);
×
160
                        return 0;
×
161
                }
162

163
                return log_error_errno(r, "Couldn't resolve glob '%s': %m", option->key);
×
164
        }
165

166
        STRV_FOREACH(s, paths) {
1,428✔
167
                const char *key = ASSERT_SE_PTR(path_startswith(*s, "/proc/sys"));
1,071✔
168

169
                if (ordered_hashmap_contains(sysctl_options, key)) {
1,071✔
170
                        log_debug("Not setting %s (explicit setting exists).", key);
714✔
171
                        continue;
714✔
172
                }
173

174
                RET_GATHER(r,
357✔
175
                           sysctl_write_or_warn(key, option->value,
176
                                                /* ignore_failure= */ option->ignore_failure,
177
                                                /* ignore_enoent= */ !arg_strict));
178
        }
179

180
        return r;
181
}
182

183
static int apply_glob_option(OrderedHashmap *sysctl_options, SysctlOption *option) {
3,363✔
184
        int r = 0;
3,363✔
185

186
        if (strv_isempty(arg_prefixes))
3,363✔
187
                return apply_glob_option_with_prefix(sysctl_options, option, NULL);
357✔
188

189
        STRV_FOREACH(i, arg_prefixes)
15,003✔
190
                RET_GATHER(r, apply_glob_option_with_prefix(sysctl_options, option, *i));
11,997✔
191
        return r;
192
}
193

194
static int apply_all(OrderedHashmap *sysctl_options) {
1,133✔
195
        SysctlOption *option;
1,133✔
196
        int r = 0;
1,133✔
197

198
        ORDERED_HASHMAP_FOREACH(option, sysctl_options) {
7,364✔
199
                int k;
6,231✔
200

201
                /* Ignore "negative match" options, they are there only to exclude stuff from globs. */
202
                if (!option->value)
6,231✔
203
                        continue;
357✔
204

205
                if (string_is_glob(option->key))
5,874✔
206
                        k = apply_glob_option(sysctl_options, option);
3,363✔
207
                else
208
                        k = sysctl_write_or_warn(option->key, option->value,
2,511✔
209
                                                 /* ignore_failure= */ option->ignore_failure,
2,511✔
210
                                                 /* ignore_enoent= */ !arg_strict);
2,511✔
211
                RET_GATHER(r, k);
5,874✔
212
        }
213

214
        return r;
1,133✔
215
}
216

217
static int parse_line(const char *fname, unsigned line, const char *buffer, bool *invalid_config, void *userdata) {
30,207✔
218
        OrderedHashmap **sysctl_options = ASSERT_PTR(userdata);
30,207✔
219
        _cleanup_free_ char *k = NULL, *v = NULL;
30,207✔
220
        bool ignore_failure = false;
30,207✔
221
        int r;
30,207✔
222

223
        const char *eq = strchr(buffer, '=');
30,207✔
224
        if (eq) {
30,207✔
225
                if (buffer[0] == '-') {
26,853✔
226
                        ignore_failure = true;
2,242✔
227
                        buffer++;
2,242✔
228
                }
229

230
                k = strndup(buffer, eq - buffer);
26,853✔
231
                if (!k)
26,853✔
232
                        return log_oom();
×
233

234
                v = strdup(eq + 1);
26,853✔
235
                if (!v)
26,853✔
236
                        return log_oom();
×
237

238
        } else {
239
                if (buffer[0] == '-')
3,354✔
240
                        /* We have a "negative match" option. Let's continue with value==NULL. */
241
                        buffer++;
3,354✔
242
                else
243
                        return log_syntax(NULL, LOG_WARNING, fname, line, SYNTHETIC_ERRNO(EINVAL),
×
244
                                          "Line is not an assignment, ignoring: %s", buffer);
245

246
                k = strdup(buffer);
3,354✔
247
                if (!k)
3,354✔
248
                        return log_oom();
×
249
        }
250

251
        const char *key = sysctl_normalize(strstrip(k)), *value = strstrip(v);
30,207✔
252

253
        /* We can't filter out globs at this point, we'll need to do that later. */
254
        if (!string_is_glob(key) && !test_prefix(key))
30,207✔
255
                return 0;
256

257
        SysctlOption *existing = ordered_hashmap_get(*sysctl_options, key);
6,231✔
258
        if (existing) {
6,231✔
259
                if (streq_ptr(value, existing->value)) {
×
260
                        existing->ignore_failure = existing->ignore_failure || ignore_failure;
×
261
                        return 0;
×
262
                }
263

264
                log_syntax(NULL, LOG_DEBUG, fname, line, 0,
×
265
                           "Overwriting earlier assignment of '%s'.", key);
266
                sysctl_option_free(ordered_hashmap_remove(*sysctl_options, key));
×
267
        }
268

269
        _cleanup_(sysctl_option_freep) SysctlOption *option = sysctl_option_new(key, value, ignore_failure);
12,462✔
270
        if (!option)
6,231✔
271
                return log_oom();
×
272

273
        r = ordered_hashmap_ensure_put(sysctl_options, &sysctl_option_hash_ops, option->key, option);
6,231✔
274
        if (r < 0)
6,231✔
275
                return log_error_errno(r, "Failed to add sysctl variable '%s' to hashmap: %m", key);
×
276

277
        TAKE_PTR(option);
6,231✔
278
        return 0;
6,231✔
279
}
280

281
static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent) {
5,719✔
282
        return conf_file_read(
11,438✔
283
                        /* root= */ NULL,
284
                        (const char**) CONF_PATHS_STRV("sysctl.d"),
5,719✔
285
                        path,
286
                        parse_line,
287
                        sysctl_options,
288
                        ignore_enoent,
289
                        /* invalid_config= */ NULL);
290
}
291

292
static int read_credential_lines(OrderedHashmap **sysctl_options) {
1,118✔
293
        _cleanup_free_ char *j = NULL;
1,118✔
294
        const char *d;
1,118✔
295
        int r;
1,118✔
296

297
        r = get_credentials_dir(&d);
1,118✔
298
        if (r == -ENXIO)
1,118✔
299
                return 0;
300
        if (r < 0)
119✔
301
                return log_error_errno(r, "Failed to get credentials directory: %m");
×
302

303
        j = path_join(d, "sysctl.extra");
119✔
304
        if (!j)
119✔
305
                return log_oom();
×
306

307
        return parse_file(sysctl_options, j, /* ignore_enoent= */ true);
119✔
308
}
309

310
static int cat_config(char **files) {
×
311
        pager_open(arg_pager_flags);
×
312

313
        return cat_files(NULL, files, arg_cat_flags);
×
314
}
315

316
static int help(void) {
×
317
        _cleanup_free_ char *link = NULL;
×
318
        _cleanup_(table_unrefp) Table *commands = NULL, *options = NULL;
×
319
        int r;
×
320

321
        r = terminal_urlify_man("systemd-sysctl.service", "8", &link);
×
322
        if (r < 0)
×
323
                return log_oom();
×
324

325
        r = option_parser_get_help_table(&commands);
×
326
        if (r < 0)
×
327
                return r;
328

329
        r = option_parser_get_help_table_group("Options", &options);
×
330
        if (r < 0)
×
331
                return r;
332

333
        (void) table_sync_column_widths(0, commands, options);
×
334

335
        printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n"
×
336
               "\n%sApplies kernel sysctl settings.%s\n"
337
               "\n%sCommands:%s\n",
338
               program_invocation_short_name,
339
               ansi_highlight(),
340
               ansi_normal(),
341
               ansi_underline(),
342
               ansi_normal());
343

344
        r = table_print_or_warn(commands);
×
345
        if (r < 0)
×
346
                return r;
347

348
        printf("\n%sOptions:%s\n", ansi_underline(), ansi_normal());
×
349

350
        r = table_print_or_warn(options);
×
351
        if (r < 0)
×
352
                return r;
353

354
        printf("\nSee the %s for details.\n", link);
×
355
        return 0;
356
}
357

358
static int parse_argv(int argc, char *argv[], char ***remaining_args) {
1,133✔
359
        assert(argc >= 0);
1,133✔
360
        assert(argv);
1,133✔
361
        assert(remaining_args);
1,133✔
362

363
        OptionParser state = { argc, argv };
1,133✔
364
        const char *arg;
1,133✔
365

366
        FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
6,276✔
367
                switch (c) {
4,010✔
368

369
                OPTION_COMMON_HELP:
×
370
                        return help();
×
371

372
                OPTION_COMMON_VERSION:
×
373
                        return version();
×
374

375
                OPTION_COMMON_CAT_CONFIG:
×
376
                        arg_cat_flags = CAT_CONFIG_ON;
×
377
                        break;
×
378

379
                OPTION_COMMON_TLDR:
×
380
                        arg_cat_flags = CAT_TLDR;
×
381
                        break;
×
382

383
                OPTION_GROUP("Options"): {}
3,999✔
384

385
                OPTION_LONG("prefix", "PATH",
3,999✔
386
                            "Only apply rules with the specified prefix"): {
387
                        _cleanup_free_ char *normalized = strdup(arg);
3,999✔
388
                        if (!normalized)
3,999✔
389
                                return log_oom();
×
390
                        sysctl_normalize(normalized);
3,999✔
391

392
                        /* We used to require people to specify absolute paths
393
                         * in /proc/sys in the past. This is kinda useless, but
394
                         * we need to keep compatibility. We now support any
395
                         * sysctl name available. */
396
                        const char *s = path_startswith(normalized, "/proc/sys");
3,999✔
397

398
                        if (strv_extend(&arg_prefixes, s ?: normalized) < 0)
7,998✔
399
                                return log_oom();
×
400

401
                        break;
3,999✔
402
                }
403

404
                OPTION_COMMON_NO_PAGER:
×
405
                        arg_pager_flags |= PAGER_DISABLE;
×
406
                        break;
×
407

408
                OPTION_LONG("strict", NULL,
6✔
409
                            "Fail on any kind of failures"):
410
                        arg_strict = true;
6✔
411
                        break;
6✔
412

413
                OPTION_LONG("inline", NULL,
5✔
414
                            "Treat arguments as configuration lines"):
415
                        arg_inline = true;
5✔
416
                        break;
5✔
417
                }
418

419
        *remaining_args = option_parser_get_args(&state);
1,133✔
420

421
        if (arg_cat_flags != CAT_CONFIG_OFF && !strv_isempty(*remaining_args))
1,133✔
422
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
423
                                       "Positional arguments are not allowed with --cat-config/--tldr.");
424

425
        return 1;
426
}
427

428
static int run(int argc, char *argv[]) {
1,133✔
429
        _cleanup_ordered_hashmap_free_ OrderedHashmap *sysctl_options = NULL;
1,133✔
430
        int r;
1,133✔
431

432
        char **args = NULL;
1,133✔
433
        r = parse_argv(argc, argv, &args);
1,133✔
434
        if (r <= 0)
1,133✔
435
                return r;
436

437
        log_setup();
1,133✔
438

439
        umask(0022);
1,133✔
440

441
        if (!strv_isempty(args)) {
1,133✔
442
                unsigned pos = 0;
443

444
                STRV_FOREACH(arg, args) {
32✔
445
                        if (arg_inline)
17✔
446
                                /* Use (argument):n, where n==1 for the first positional arg */
447
                                RET_GATHER(r, parse_line("(argument)", ++pos, *arg, /* invalid_config= */ NULL, &sysctl_options));
7✔
448
                        else
449
                                RET_GATHER(r, parse_file(&sysctl_options, *arg, false));
10✔
450
                }
451
        } else {
452
                _cleanup_strv_free_ char **files = NULL;
×
453

454
                r = conf_files_list_strv(&files, ".conf", /* root= */ NULL, CONF_FILES_WARN,
2,236✔
455
                                         (const char**) CONF_PATHS_STRV("sysctl.d"));
1,118✔
456
                if (r < 0)
1,118✔
457
                        return log_error_errno(r, "Failed to enumerate sysctl.d files: %m");
×
458

459
                if (arg_cat_flags != CAT_CONFIG_OFF)
1,118✔
460
                        return cat_config(files);
×
461

462
                STRV_FOREACH(f, files)
6,708✔
463
                        RET_GATHER(r, parse_file(&sysctl_options, *f, true));
5,590✔
464

465
                RET_GATHER(r, read_credential_lines(&sysctl_options));
1,118✔
466
        }
467

468
        return RET_GATHER(r, apply_all(sysctl_options));
1,133✔
469
}
470

471
DEFINE_MAIN_FUNCTION(run);
1,133✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc