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

systemd / systemd / 17750153392

15 Sep 2025 06:38PM UTC coverage: 72.241% (-0.03%) from 72.266%
17750153392

push

github

bluca
Revert "TEST-55-OOMD: Verify that ExecStopPost= runs on oom-kill"

The test consistently fails on CentOS 9:

TEST-55-OOMD.sh[678]: + test -f /run/testbloat-exec-stop-post
[FAILED] Failed to start TEST-55-OOMD.service.

https://github.com/systemd/systemd/actions/runs/17689186773/job/50293446228?pr=38911

This reverts commit 5bf7438ff.

302589 of 418863 relevant lines covered (72.24%)

1047733.04 hits per line

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

36.75
/src/shared/generator.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdlib.h>
4
#include <sys/stat.h>
5
#include <unistd.h>
6

7
#include "alloc-util.h"
8
#include "argv-util.h"
9
#include "cgroup-util.h"
10
#include "dropin.h"
11
#include "escape.h"
12
#include "fd-util.h"
13
#include "fileio.h"
14
#include "fstab-util.h"
15
#include "generator.h"
16
#include "initrd-util.h"
17
#include "log.h"
18
#include "mkdir-label.h"
19
#include "mountpoint-util.h"
20
#include "parse-util.h"
21
#include "path-util.h"
22
#include "special.h"
23
#include "specifier.h"
24
#include "string-util.h"
25
#include "time-util.h"
26
#include "tmpfile-util.h"
27
#include "unit-name.h"
28

29
static int symlink_unless_exists(const char *to, const char *from) {
182✔
30
        (void) mkdir_parents(from, 0755);
182✔
31

32
        if (symlink(to, from) < 0 && errno != EEXIST)
182✔
33
                return log_error_errno(errno, "Failed to create symlink %s: %m", from);
×
34
        return 0;
35
}
36

37
int generator_open_unit_file_full(
358✔
38
                const char *dir,
39
                const char *source,
40
                const char *fn,
41
                FILE **ret_file,
42
                char **ret_final_path,
43
                char **ret_temp_path) {
44

45
        _cleanup_free_ char *p = NULL;
358✔
46
        FILE *f;
358✔
47
        int r;
358✔
48

49
        assert(dir);
358✔
50
        assert(ret_file);
358✔
51

52
        /* If <ret_temp_path> is specified, it creates a temporary unit file and also returns its
53
         * temporary path. */
54

55
        if (ret_temp_path) {
358✔
56
                r = fopen_temporary(dir, &f, &p);
295✔
57
                if (r < 0)
295✔
58
                        return log_error_errno(r, "Failed to create temporary unit file in '%s': %m", dir);
×
59

60
                (void) fchmod(fileno(f), 0644);
295✔
61

62
                *ret_temp_path = TAKE_PTR(p);
295✔
63
        } else {
64
                assert(fn);
63✔
65

66
                p = path_join(dir, fn);
63✔
67
                if (!p)
63✔
68
                        return log_oom();
×
69

70
                r = fopen_unlocked(p, "wxe", &f);
63✔
71
                if (r < 0) {
63✔
72
                        if (source && r == -EEXIST)
×
73
                                return log_error_errno(r,
×
74
                                                       "Failed to create unit file '%s', as it already exists. Duplicate entry in '%s'?",
75
                                                       p, source);
76

77
                        return log_error_errno(r, "Failed to create unit file '%s': %m", p);
×
78
                }
79
        }
80

81
        fprintf(f,
358✔
82
                "# Automatically generated by %s\n\n",
83
                program_invocation_short_name);
84

85
        *ret_file = f;
358✔
86

87
        if (ret_final_path)
358✔
88
                *ret_final_path = TAKE_PTR(p);
×
89

90
        return 0;
91
}
92

93
int generator_add_symlink_full(
182✔
94
                const char *dir,
95
                const char *dst,
96
                const char *dep_type,
97
                const char *src,
98
                const char *instance) {
99

100
        _cleanup_free_ char *dn = NULL, *fn = NULL, *instantiated = NULL, *to = NULL, *from = NULL;
182✔
101
        int r;
182✔
102

103
        assert(dir);
182✔
104
        assert(dst);
182✔
105
        assert(src);
182✔
106

107
        /* If 'dep_type' is specified adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise).
108
         *
109
         * If 'dep_type' is NULL, it will create a symlink to <src> (i.e. create an alias.
110
         *
111
         * If <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
112

113
        r = path_extract_directory(src, &dn);
182✔
114
        if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → just a file name was passed */
182✔
115
                return log_error_errno(r, "Failed to extract directory name from '%s': %m", src);
×
116

117
        r = path_extract_filename(src, &fn);
182✔
118
        if (r < 0)
182✔
119
                return log_error_errno(r, "Failed to extract file name from '%s': %m", src);
×
120
        if (r == O_DIRECTORY)
182✔
121
                return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src);
×
122

123
        if (instance) {
182✔
124
                r = unit_name_replace_instance(fn, instance, &instantiated);
2✔
125
                if (r < 0)
2✔
126
                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance);
×
127
        }
128

129
        if (dep_type) { /* Create a .wants/ style dep */
182✔
130
                from = path_join(dn ?: "..", fn);
269✔
131
                if (!from)
182✔
132
                        return log_oom();
×
133

134
                to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn);
182✔
135
        } else { /* or create an alias */
136
                from = dn ? path_join(dn, fn) : strdup(fn);
×
137
                if (!from)
×
138
                        return log_oom();
×
139

140
                to = strjoin(dir, "/", dst);
×
141
        }
142
        if (!to)
182✔
143
                return log_oom();
×
144

145
        return symlink_unless_exists(from, to);
182✔
146
}
147

148
static int generator_add_ordering(
×
149
                const char *dir,
150
                const char *src,
151
                const char *order,
152
                const char *dst,
153
                const char *instance) {
154

155
        _cleanup_free_ char *instantiated = NULL, *p = NULL, *fn = NULL;
×
156
        _cleanup_fclose_ FILE *f = NULL;
×
157
        const char *to;
×
158
        int r;
×
159

160
        assert(dir);
×
161
        assert(src);
×
162
        assert(order);
×
163
        assert(dst);
×
164

165
        /* Adds in an explicit ordering dependency of type <order> from <src> to <dst>. If <instance> is
166
         * specified, it is inserted into <dst>. */
167

168
        if (instance) {
×
169
                r = unit_name_replace_instance(dst, instance, &instantiated);
×
170
                if (r < 0)
×
171
                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", dst, instance);
×
172

173
                to = instantiated;
×
174
        } else
175
                to = dst;
176

177
        fn = strjoin(src, ".d/50-order-", to, ".conf");
×
178
        if (!fn)
×
179
                return log_oom();
×
180

181
        p = path_join(dir, fn);
×
182
        if (!p)
×
183
                return log_oom();
×
184

185
        (void) mkdir_parents_label(p, 0755);
×
186

187
        r = fopen_unlocked(p, "wxe", &f);
×
188
        if (r < 0)
×
189
                return log_error_errno(r, "Failed to create '%s': %m", p);
×
190

191
        fprintf(f,
×
192
                "# Automatically generated by %s\n\n"
193
                "[Unit]\n"
194
                "%s=%s\n",
195
                program_invocation_short_name,
196
                order,
197
                to);
198

199
        r = fflush_and_check(f);
×
200
        if (r < 0)
×
201
                return log_error_errno(r, "Failed to write drop-in '%s': %m", p);
×
202

203
        return 0;
204
}
205

206
static int write_fsck_sysroot_service(
7✔
207
                const char *unit, /* Either SPECIAL_FSCK_ROOT_SERVICE or SPECIAL_FSCK_USR_SERVICE */
208
                const char *dir,
209
                const char *what,
210
                const char *extra_after) {
211

212
        _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
7✔
213
        _cleanup_fclose_ FILE *f = NULL;
7✔
214
        int r;
7✔
215

216
        assert(unit);
7✔
217
        assert(dir);
7✔
218
        assert(what);
7✔
219

220
        /* Writes out special versions of systemd-fsck-root.service and systemd-fsck-usr.service for use in
221
         * the initrd. The regular statically shipped versions of these unit files use / and /usr for as
222
         * paths, which doesn't match what we need for the initrd (where the dirs are /sysroot/ +
223
         * /sysusr/usr/), hence we overwrite those versions here. */
224

225
        escaped = specifier_escape(what);
7✔
226
        if (!escaped)
7✔
227
                return log_oom();
×
228

229
        escaped2 = cescape(escaped);
7✔
230
        if (!escaped2)
7✔
231
                return log_oom();
×
232

233
        r = unit_name_from_path(what, ".device", &device);
7✔
234
        if (r < 0)
7✔
235
                return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
×
236

237
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
7✔
238
        if (r < 0)
7✔
239
                return r;
240

241
        fprintf(f,
14✔
242
                "[Unit]\n"
243
                "Description=File System Check on %1$s\n"
244
                "Documentation=man:%2$s(8)\n"
245
                "\n"
246
                "DefaultDependencies=no\n"
247
                "BindsTo=%3$s\n"
248
                "Conflicts=shutdown.target\n"
249
                "After=%4$s%5$slocal-fs-pre.target %3$s\n"
250
                "Before=shutdown.target\n"
251
                "\n"
252
                "[Service]\n"
253
                "Type=oneshot\n"
254
                "RemainAfterExit=yes\n"
255
                "ExecStart=" SYSTEMD_FSCK_PATH " %6$s\n"
256
                "TimeoutSec=infinity\n"
257
                "ImportCredential=fsck.*\n",
258
                escaped,
259
                unit,
260
                device,
261
                strempty(extra_after),
262
                isempty(extra_after) ? "" : " ",
7✔
263
                escaped2);
264

265
        r = fflush_and_check(f);
7✔
266
        if (r < 0)
7✔
267
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
268

269
        return 0;
270
}
271

272
int generator_write_fsck_deps(
14✔
273
                FILE *f,
274
                const char *dir,
275
                const char *what,
276
                const char *where,
277
                const char *fstype,
278
                const char *options) {
279

280
        int r;
14✔
281

282
        assert(f);
14✔
283
        assert(dir);
14✔
284
        assert(what);
14✔
285
        assert(where);
14✔
286

287
        /* Let's do an early exit if we are invoked for the root and /usr/ trees in the initrd, to avoid
288
         * generating confusing log messages. */
289
        if (in_initrd() && PATH_IN_SET(where, "/", "/usr")) {
14✔
290
                log_debug("Skipping fsck for %s in initrd.", where);
×
291
                return 0;
×
292
        }
293

294
        if (fstype) {
14✔
295
                if (!fstype_is_blockdev_backed(fstype)) {
10✔
296
                        log_debug("Skipping file system check for non-block based file system '%s'.", what);
×
297
                        return 0;
×
298
                }
299

300
                if (fstype_is_ro(fstype)) {
10✔
301
                        log_debug("Skipping file system check for read-only file system '%s'.", what);
×
302
                        return 0;
×
303
                }
304
        }
305

306
        if (fstab_test_option(options, "bind\0rbind\0")) {
14✔
307
                log_debug("Skipping file system check for bind mount of '%s'.", what);
×
308
                return 0;
×
309
        }
310

311
        if (!is_device_path(what)) {
14✔
312
                log_debug("Checking was requested for \"%s\", but it is not a device.", what);
×
313
                return 0;
×
314
        }
315

316
        if (!isempty(fstype) && !streq(fstype, "auto")) {
24✔
317
                r = fsck_exists_for_fstype(fstype);
×
318
                if (r < 0)
×
319
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
×
320
                else if (r == 0) {
×
321
                        /* treat missing check as essentially OK */
322
                        log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
×
323
                        return 0;
×
324
                }
325
        } else {
326
                r = fsck_exists();
14✔
327
                if (r < 0)
14✔
328
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if the fsck command may be used, proceeding: %m", what);
×
329
                else if (r == 0) {
14✔
330
                        /* treat missing fsck as essentially OK */
331
                        log_debug("Checking was requested for %s, but the fsck command does not exist.", what);
×
332
                        return 0;
×
333
                }
334
        }
335

336
        if (path_equal(where, "/")) {
14✔
337
                /* We support running the fsck instance for the root fs while it is already mounted, for
338
                 * compatibility with non-initrd boots. It's ugly, but it is how it is. Since – unlike for
339
                 * regular file systems – this means the ordering is reversed (i.e. mount *before* fsck) we
340
                 * have a separate fsck unit for this, independent of systemd-fsck@.service. */
341

342
                const char *lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
×
343

344
                r = symlink_unless_exists(SYSTEM_DATA_UNIT_DIR "/" SPECIAL_FSCK_ROOT_SERVICE, lnk);
×
345
                if (r < 0)
×
346
                        return r;
×
347
        } else {
348
                _cleanup_free_ char *_fsck = NULL;
14✔
349
                const char *fsck, *dep;
14✔
350

351
                if (in_initrd() && path_equal(where, "/sysroot")) {
24✔
352
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_ROOT_SERVICE, dir, what, SPECIAL_INITRD_ROOT_DEVICE_TARGET);
6✔
353
                        if (r < 0)
6✔
354
                                return r;
355

356
                        fsck = SPECIAL_FSCK_ROOT_SERVICE;
357
                        dep = "Requires";
358

359
                } else if (in_initrd() && path_equal(where, "/sysusr/usr")) {
12✔
360
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_USR_SERVICE, dir, what, NULL);
1✔
361
                        if (r < 0)
1✔
362
                                return r;
363

364
                        fsck = SPECIAL_FSCK_USR_SERVICE;
365
                        dep = "Requires";
366
                } else {
367
                        /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
368
                         * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
369
                         * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
370
                         * we'd have to unmount /usr too.  */
371

372
                        dep = path_equal(where, "/usr") ? "Wants" : "Requires";
7✔
373

374
                        r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
7✔
375
                        if (r < 0)
7✔
376
                                return log_error_errno(r, "Failed to create fsck service name: %m");
×
377

378
                        fsck = _fsck;
7✔
379
                }
380

381
                fprintf(f,
14✔
382
                        "%1$s=%2$s\n"
383
                        "After=%2$s\n",
384
                        dep, fsck);
385
        }
386

387
        return 0;
388
}
389

390
int generator_write_device_timeout(
46✔
391
                const char *dir,
392
                const char *what,
393
                const char *opts,
394
                char **filtered) {
395

396
        /* Configure how long we wait for a device that backs a mount point or a
397
         * swap partition to show up. This is useful to support endless device timeouts
398
         * for devices that show up only after user input, like crypto devices. */
399

400
        _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
46✔
401
        usec_t u;
46✔
402
        int r;
46✔
403

404
        assert(dir);
46✔
405
        assert(what);
46✔
406

407
        r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
46✔
408
                                       "x-systemd.device-timeout\0",
409
                                 NULL, &timeout, NULL, filtered);
410
        if (r < 0) {
46✔
411
                log_warning_errno(r, "Failed to parse fstab options, ignoring: %m");
×
412
                return 0;
×
413
        }
414
        if (r == 0)
46✔
415
                return 0;
416

417
        r = parse_sec_fix_0(timeout, &u);
1✔
418
        if (r < 0) {
1✔
419
                log_warning("Failed to parse timeout for device '%s', ignoring: %s", what, timeout);
×
420
                return 0;
×
421
        }
422

423
        node = fstab_node_to_udev_node(what);
1✔
424
        if (!node)
1✔
425
                return log_oom();
×
426
        if (!is_device_path(node)) {
1✔
427
                log_warning("'%s' is not a device path, ignoring x-systemd.device-timeout= option.", node);
1✔
428
                return 0;
1✔
429
        }
430

431
        r = unit_name_from_path(node, ".device", &unit);
×
432
        if (r < 0)
×
433
                return log_error_errno(r, "Failed to make unit name from device path '%s': %m", node);
×
434

435
        r = write_drop_in_format(dir, unit, 50, "device-timeout",
×
436
                                 "# Automatically generated by %s\n"
437
                                 "# from supplied options \"%s\"\n\n"
438
                                 "[Unit]\n"
439
                                 "JobRunningTimeoutSec=%s",
440
                                 program_invocation_short_name,
441
                                 opts,
442
                                 timeout);
443
        if (r < 0)
×
444
                return r;
×
445

446
        return 1;
447
}
448

449
int generator_write_unit_timeout(
24✔
450
                FILE *f,
451
                const char *where,
452
                const char *opts,
453
                const char *filter,
454
                const char *unit_setting) {
455

456
        _cleanup_free_ char *timeout = NULL;
24✔
457
        usec_t u;
24✔
458
        int r;
24✔
459

460
        assert(f);
24✔
461
        assert(where);
24✔
462
        assert(filter);
24✔
463
        assert(unit_setting);
24✔
464

465
        r = fstab_filter_options(opts, filter, NULL, &timeout, NULL, NULL);
24✔
466
        if (r < 0)
24✔
467
                return log_error_errno(r, "Failed to parse options for '%s': %m", where);
×
468
        if (r == 0)
24✔
469
                return 0;
470

471
        r = parse_sec_fix_0(timeout, &u);
×
472
        if (r < 0) {
×
473
                log_warning_errno(r, "Failed to parse timeout '%s' for '%s', ignoring: %m", timeout, where);
×
474
                return 0;
×
475
        }
476

477
        fprintf(f, "%s=%s\n", unit_setting, FORMAT_TIMESPAN(u, 0));
×
478

479
        return 0;
480
}
481

482
int generator_write_network_device_deps(
24✔
483
                const char *dir,
484
                const char *what,
485
                const char *where,
486
                const char *opts) {
487

488
        /* fstab records that specify _netdev option should apply the network
489
         * ordering on the actual device depending on network connection. If we
490
         * are not mounting real device (NFS, CIFS), we rely on _netdev effect
491
         * on the mount unit itself. */
492

493
        _cleanup_free_ char *node = NULL, *unit = NULL;
24✔
494
        int r;
24✔
495

496
        assert(dir);
24✔
497
        assert(what);
24✔
498
        assert(where);
24✔
499

500
        if (fstab_is_extrinsic(where, opts))
24✔
501
                return 0;
502

503
        if (!fstab_test_option(opts, "_netdev\0"))
22✔
504
                return 0;
505

506
        node = fstab_node_to_udev_node(what);
×
507
        if (!node)
×
508
                return log_oom();
×
509

510
        /* Nothing to apply dependencies to. */
511
        if (!is_device_path(node))
×
512
                return 0;
513

514
        r = unit_name_from_path(node, ".device", &unit);
×
515
        if (r < 0)
×
516
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", node);
×
517

518
        /* See mount_add_default_dependencies for explanation why we create such
519
         * dependencies. */
520
        return write_drop_in_format(dir, unit, 50, "netdev-dependencies",
×
521
                                    "# Automatically generated by %s\n\n"
522
                                    "[Unit]\n"
523
                                    "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n"
524
                                    "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n",
525
                                    program_invocation_short_name);
526
}
527

528
int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
6✔
529
        _cleanup_free_ char *unit = NULL;
6✔
530
        int r;
6✔
531

532
        r = unit_name_from_path(what, ".device", &unit);
6✔
533
        if (r < 0)
6✔
534
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
535
                                       what);
536

537
        return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
6✔
538
                                    "# Automatically generated by %s\n\n"
539
                                    "[Unit]\n"
540
                                    "Requires=%s\n"
541
                                    "After=%s",
542
                                    program_invocation_short_name,
543
                                    unit,
544
                                    unit);
545
}
546

547
int generator_hook_up_mkswap(
×
548
                const char *dir,
549
                const char *what) {
550

551
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
×
552
        _cleanup_fclose_ FILE *f = NULL;
×
553
        int r;
×
554

555
        assert(dir);
×
556
        assert(what);
×
557

558
        node = fstab_node_to_udev_node(what);
×
559
        if (!node)
×
560
                return log_oom();
×
561

562
        /* Nothing to work on. */
563
        if (!is_device_path(node))
×
564
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
565
                                       "Cannot format something that is not a device node: %s",
566
                                       node);
567

568
        r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
×
569
        if (r < 0)
×
570
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
×
571
                                       node);
572

573
        escaped = cescape(node);
×
574
        if (!escaped)
×
575
                return log_oom();
×
576

577
        r = unit_name_from_path(what, ".swap", &where_unit);
×
578
        if (r < 0)
×
579
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
580
                                       what);
581

582
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
×
583
        if (r < 0)
×
584
                return r;
585

586
        fprintf(f,
×
587
                "[Unit]\n"
588
                "Description=Make Swap on %%f\n"
589
                "Documentation=man:systemd-mkswap@.service(8)\n"
590
                "\n"
591
                "DefaultDependencies=no\n"
592
                "BindsTo=%%i.device\n"
593
                "After=%%i.device\n"
594
                "Before=%s\n"
595
                "Conflicts=shutdown.target\n"
596
                "Before=shutdown.target\n"
597
                "\n"
598
                "[Service]\n"
599
                "Type=oneshot\n"
600
                "RemainAfterExit=yes\n"
601
                "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
602
                "TimeoutSec=infinity\n",
603
                where_unit,
604
                escaped);
605

606
        r = fflush_and_check(f);
×
607
        if (r < 0)
×
608
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
609

610
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
611
}
612

613
int generator_hook_up_mkfs(
×
614
                const char *dir,
615
                const char *what,
616
                const char *where,
617
                const char *type) {
618

619
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
×
620
        _cleanup_fclose_ FILE *f = NULL;
×
621
        const char *fsck_unit;
×
622
        int r;
×
623

624
        assert(dir);
×
625
        assert(what);
×
626
        assert(where);
×
627

628
        node = fstab_node_to_udev_node(what);
×
629
        if (!node)
×
630
                return log_oom();
×
631

632
        /* Nothing to work on. */
633
        if (!is_device_path(node))
×
634
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
635
                                       "Cannot format something that is not a device node: %s",
636
                                       node);
637

638
        if (!type || streq(type, "auto"))
×
639
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
640
                                       "Cannot format partition %s, filesystem type is not specified",
641
                                       node);
642

643
        r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit);
×
644
        if (r < 0)
×
645
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
×
646
                                       node);
647

648
        if (in_initrd() && path_equal(where, "/sysroot"))
×
649
                fsck_unit = SPECIAL_FSCK_ROOT_SERVICE;
650
        else if (in_initrd() && path_equal(where, "/sysusr/usr"))
×
651
                fsck_unit = SPECIAL_FSCK_USR_SERVICE;
652
        else
653
                fsck_unit = "systemd-fsck@%i.service";
654

655
        escaped = cescape(node);
×
656
        if (!escaped)
×
657
                return log_oom();
×
658

659
        r = unit_name_from_path(where, ".mount", &where_unit);
×
660
        if (r < 0)
×
661
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
662
                                       where);
663

664
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
×
665
        if (r < 0)
×
666
                return r;
667

668
        fprintf(f,
×
669
                "[Unit]\n"
670
                "Description=Make File System on %%f\n"
671
                "Documentation=man:systemd-makefs@.service(8)\n"
672
                "\n"
673
                "DefaultDependencies=no\n"
674
                "BindsTo=%%i.device\n"
675
                "After=%%i.device\n"
676
                /* fsck might or might not be used, so let's be safe and order
677
                 * ourselves before both systemd-fsck@.service and the mount unit. */
678
                "Before=%s %s\n"
679
                "Conflicts=shutdown.target\n"
680
                "Before=shutdown.target\n"
681
                "\n"
682
                "[Service]\n"
683
                "Type=oneshot\n"
684
                "RemainAfterExit=yes\n"
685
                "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
686
                "TimeoutSec=infinity\n",
687
                fsck_unit,
688
                where_unit,
689
                type,
690
                escaped);
691
        // XXX: what about local-fs-pre.target?
692

693
        r = fflush_and_check(f);
×
694
        if (r < 0)
×
695
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
696

697
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
698
}
699

700
int generator_hook_up_growfs(
×
701
                const char *dir,
702
                const char *where,
703
                const char *target) {
704

705
        const char *growfs_unit, *growfs_unit_path;
×
706
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
707
        int r;
×
708

709
        assert(dir);
×
710
        assert(where);
×
711

712
        r = unit_name_from_path(where, ".mount", &where_unit);
×
713
        if (r < 0)
×
714
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
715

716
        if (empty_or_root(where)) {
×
717
                growfs_unit = SPECIAL_GROWFS_ROOT_SERVICE;
718
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_ROOT_SERVICE;
719
        } else {
720
                growfs_unit = SPECIAL_GROWFS_SERVICE;
×
721
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_SERVICE;
×
722

723
                r = unit_name_path_escape(where, &instance);
×
724
                if (r < 0)
×
725
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
726
        }
727

728
        if (target) {
×
729
                r = generator_add_ordering(dir, target, "After", growfs_unit, instance);
×
730
                if (r < 0)
×
731
                        return r;
732
        }
733

734
        return generator_add_symlink_full(dir, where_unit, "wants", growfs_unit_path, instance);
×
735
}
736

737
int generator_hook_up_pcrfs(
×
738
                const char *dir,
739
                const char *where,
740
                const char *target) {
741

742
        const char *pcrfs_unit, *pcrfs_unit_path;
×
743
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
744
        int r;
×
745

746
        assert(dir);
×
747
        assert(where);
×
748

749
        r = unit_name_from_path(where, ".mount", &where_unit);
×
750
        if (r < 0)
×
751
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
752

753
        if (empty_or_root(where)) {
×
754
                pcrfs_unit = SPECIAL_PCRFS_ROOT_SERVICE;
755
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_ROOT_SERVICE;
756
        } else {
757
                pcrfs_unit = SPECIAL_PCRFS_SERVICE;
×
758
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_SERVICE;
×
759

760
                r = unit_name_path_escape(where, &instance);
×
761
                if (r < 0)
×
762
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
763
        }
764

765
        if (target) {
×
766
                r = generator_add_ordering(dir, target, "After", pcrfs_unit, instance);
×
767
                if (r < 0)
×
768
                        return r;
769
        }
770

771
        return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
×
772
}
773

774
int generator_hook_up_validatefs(
×
775
                const char *dir,
776
                const char *where,
777
                const char *target) {
778

779
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
780
        const char *validatefs_unit, *validatefs_unit_path;
×
781
        int r;
×
782

783
        assert(dir);
×
784
        assert(where);
×
785

786
        /* never hook this in for the actual root fs, because it's too late then, we already are running from
787
         * the root fs, it makes no sense to validate it anymore */
788
        if (empty_or_root(where))
×
789
                return 0;
790

791
        r = unit_name_from_path(where, ".mount", &where_unit);
×
792
        if (r < 0)
×
793
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
794

795
        validatefs_unit = SPECIAL_VALIDATEFS_SERVICE;
×
796
        validatefs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_VALIDATEFS_SERVICE;
×
797

798
        r = unit_name_path_escape(where, &instance);
×
799
        if (r < 0)
×
800
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
801

802
        if (target) {
×
803
                r = generator_add_ordering(dir, target, "After", validatefs_unit, instance);
×
804
                if (r < 0)
×
805
                        return r;
806
        }
807

808
        return generator_add_symlink_full(dir, where_unit, "wants", validatefs_unit_path, instance);
×
809
}
810

811
int generator_hook_up_quotacheck(
×
812
                const char *dir,
813
                const char *what,
814
                const char *where,
815
                const char *target,
816
                const char *fstype) {
817

818
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
819
        int r;
×
820

821
        assert(dir);
×
822
        assert(where);
×
823

824
        if (isempty(fstype) || streq(fstype, "auto"))
×
825
                return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Couldn't determine filesystem type for %s, quota cannot be activated", what);
×
826
        if (!fstype_needs_quota(fstype))
×
827
                return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Quota was requested for %s, but not supported, ignoring: %s", what, fstype);
×
828

829
        /* quotacheck unit for system root */
830
        if (path_equal(where, "/"))
×
831
                return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_ROOT_SERVICE);
×
832

833
        r = unit_name_path_escape(where, &instance);
×
834
        if (r < 0)
×
835
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
836

837
        if (target) {
×
838
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTACHECK_SERVICE, instance);
×
839
                if (r < 0)
×
840
                        return r;
841
        }
842

843
        r = unit_name_from_path(where, ".mount", &where_unit);
×
844
        if (r < 0)
×
845
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
846

847
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_SERVICE, instance);
×
848
}
849

850
int generator_hook_up_quotaon(
×
851
                const char *dir,
852
                const char *where,
853
                const char *target) {
854

855
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
856
        int r;
×
857

858
        assert(dir);
×
859
        assert(where);
×
860

861
        /* quotaon unit for system root is not instantiated */
862
        if (path_equal(where, "/"))
×
863
                return generator_add_symlink(dir,  SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_ROOT_SERVICE);
×
864

865
        r = unit_name_path_escape(where, &instance);
×
866
        if (r < 0)
×
867
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
868

869
        if (target) {
×
870
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTAON_SERVICE, instance);
×
871
                if (r < 0)
×
872
                        return r;
873
        }
874

875
        r = unit_name_from_path(where, ".mount", &where_unit);
×
876
        if (r < 0)
×
877
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
878

879
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_SERVICE, instance);
×
880
}
881

882
int generator_enable_remount_fs_service(const char *dir) {
2✔
883
        /* Pull in systemd-remount-fs.service */
884
        return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants",
2✔
885
                                     SYSTEM_DATA_UNIT_DIR "/" SPECIAL_REMOUNT_FS_SERVICE);
886
}
887

888
int generator_write_blockdev_dependency(FILE *f, const char *what) {
24✔
889
        _cleanup_free_ char *escaped = NULL;
24✔
890
        int r;
24✔
891

892
        assert(f);
24✔
893
        assert(what);
24✔
894

895
        if (!path_startswith(what, "/dev/"))
24✔
896
                return 0;
897

898
        r = unit_name_path_escape(what, &escaped);
16✔
899
        if (r < 0)
16✔
900
                return log_error_errno(r, "Failed to escape device node path %s: %m", what);
×
901

902
        fprintf(f,
16✔
903
                "After=blockdev@%s.target\n",
904
                escaped);
905

906
        return 0;
907
}
908

909
int generator_write_cryptsetup_unit_section(FILE *f, const char *source) {
22✔
910
        assert(f);
22✔
911

912
        fprintf(f,
22✔
913
                "[Unit]\n"
914
                "Description=Cryptography Setup for %%I\n"
915
                "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n");
916

917
        if (source)
22✔
918
                fprintf(f, "SourcePath=%s\n", source);
22✔
919

920
        fprintf(f,
22✔
921
                "\n"
922
                "DefaultDependencies=no\n"
923
                "After=cryptsetup-pre.target systemd-udevd-kernel.socket systemd-tpm2-setup-early.service\n"
924
                "Before=blockdev@dev-mapper-%%i.target\n"
925
                "Wants=blockdev@dev-mapper-%%i.target\n"
926
                "IgnoreOnIsolate=true\n");
927

928
        return 0;
22✔
929
}
930

931
int generator_write_cryptsetup_service_section(
22✔
932
                FILE *f,
933
                const char *name,
934
                const char *what,
935
                const char *key_file,
936
                const char *options) {
937

938
        _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *key_file_escaped = NULL, *options_escaped = NULL;
22✔
939

940
        assert(f);
22✔
941
        assert(name);
22✔
942
        assert(what);
22✔
943

944
        name_escaped = specifier_escape(name);
22✔
945
        if (!name_escaped)
22✔
946
                return log_oom();
×
947

948
        what_escaped = specifier_escape(what);
22✔
949
        if (!what_escaped)
22✔
950
                return log_oom();
×
951

952
        if (key_file) {
22✔
953
                key_file_escaped = specifier_escape(key_file);
22✔
954
                if (!key_file_escaped)
22✔
955
                        return log_oom();
×
956
        }
957

958
        if (options) {
22✔
959
                options_escaped = specifier_escape(options);
22✔
960
                if (!options_escaped)
22✔
961
                        return log_oom();
×
962
        }
963

964
        fprintf(f,
22✔
965
                "\n"
966
                "[Service]\n"
967
                "Type=oneshot\n"
968
                "RemainAfterExit=yes\n"
969
                "TimeoutSec=infinity\n"   /* The binary handles timeouts on its own */
970
                "KeyringMode=shared\n"    /* Make sure we can share cached keys among instances */
971
                "OOMScoreAdjust=500\n"    /* Unlocking can allocate a lot of memory if Argon2 is used */
972
                "ImportCredential=cryptsetup.*\n"
973
                "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
974
                "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
975
                name_escaped, what_escaped, strempty(key_file_escaped), strempty(options_escaped),
976
                name_escaped);
977

978
        return 0;
979
}
980

981
int generator_write_veritysetup_unit_section(FILE *f, const char *source) {
×
982
        assert(f);
×
983

984
        fprintf(f,
×
985
                "[Unit]\n"
986
                "Description=Integrity Protection Setup for %%I\n"
987
                "Documentation=man:veritytab(5) man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n");
988

989
        if (source)
×
990
                fprintf(f, "SourcePath=%s\n", source);
×
991

992
        fprintf(f,
×
993
                "DefaultDependencies=no\n"
994
                "IgnoreOnIsolate=true\n"
995
                "After=veritysetup-pre.target systemd-udevd-kernel.socket\n"
996
                "Before=blockdev@dev-mapper-%%i.target\n"
997
                "Wants=blockdev@dev-mapper-%%i.target\n");
998

999
        return 0;
×
1000
}
1001

1002
int generator_write_veritysetup_service_section(
×
1003
                FILE *f,
1004
                const char *name,
1005
                const char *data_what,
1006
                const char *hash_what,
1007
                const char *roothash,
1008
                const char *options) {
1009

1010
        _cleanup_free_ char *name_escaped = NULL, *data_what_escaped = NULL, *hash_what_escaped = NULL,
×
1011
                            *roothash_escaped = NULL, *options_escaped = NULL;
×
1012

1013
        assert(f);
×
1014
        assert(name);
×
1015
        assert(data_what);
×
1016
        assert(hash_what);
×
1017

1018
        name_escaped = specifier_escape(name);
×
1019
        if (!name_escaped)
×
1020
                return log_oom();
×
1021

1022
        data_what_escaped = specifier_escape(data_what);
×
1023
        if (!data_what_escaped)
×
1024
                return log_oom();
×
1025

1026
        hash_what_escaped = specifier_escape(hash_what);
×
1027
        if (!hash_what_escaped)
×
1028
                return log_oom();
×
1029

1030
        if (roothash) {
×
1031
                roothash_escaped = specifier_escape(roothash);
×
1032
                if (!roothash_escaped)
×
1033
                        return log_oom();
×
1034
        }
1035

1036
        if (options) {
×
1037
                options_escaped = specifier_escape(options);
×
1038
                if (!options_escaped)
×
1039
                        return log_oom();
×
1040
        }
1041

1042
        fprintf(f,
×
1043
                "\n"
1044
                "[Service]\n"
1045
                "Type=oneshot\n"
1046
                "RemainAfterExit=yes\n"
1047
                "ExecStart=" SYSTEMD_VERITYSETUP_PATH " attach '%s' '%s' '%s' '%s' '%s'\n"
1048
                "ExecStop=" SYSTEMD_VERITYSETUP_PATH " detach '%s'\n",
1049
                name_escaped, data_what_escaped, hash_what_escaped, empty_to_dash(roothash_escaped), strempty(options_escaped),
1050
                name_escaped);
1051

1052
        return 0;
1053
}
1054

1055
void log_setup_generator(void) {
264✔
1056
        if (invoked_by_systemd()) {
264✔
1057
                /* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
1058
                if (streq_ptr(getenv("SYSTEMD_SCOPE"), "system"))
193✔
1059
                        log_set_prohibit_ipc(true);
×
1060

1061
                /* This effectively means: journal for per-user service manager generators, kmsg for per-system service manager generators */
1062
                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
193✔
1063
        } else
1064
                log_set_target(LOG_TARGET_AUTO);
71✔
1065

1066
        log_parse_environment();
264✔
1067
        log_open();
264✔
1068
}
264✔
1069

1070
bool generator_soft_rebooted(void) {
×
1071
        static int cached = -1;
×
1072
        int r;
×
1073

1074
        if (cached >= 0)
×
1075
                return cached;
×
1076

1077
        const char *e = secure_getenv("SYSTEMD_SOFT_REBOOTS_COUNT");
×
1078
        if (!e)
×
1079
                return (cached = false);
×
1080

1081
        unsigned u;
×
1082

1083
        r = safe_atou(e, &u);
×
1084
        if (r < 0) {
×
1085
                log_debug_errno(r, "Failed to parse $SYSTEMD_SOFT_REBOOTS_COUNT, assuming the system hasn't soft-rebooted: %m");
×
1086
                return (cached = false);
×
1087
        }
1088

1089
        return (cached = (u > 0));
×
1090
}
1091

1092
GptAutoRoot parse_gpt_auto_root(const char *switch_name, const char *value) {
17✔
1093
        assert(switch_name);
17✔
1094
        assert(value);
17✔
1095

1096
        /* Parses the 'gpt-auto'/'gpt-auto-root'/'dissect'/'dissect-force' parameters to root=
1097
         *
1098
         * note that we are not using a regular string table here, because the mode names don't fully match
1099
         * the parameter names. And root= being something else is not an error. */
1100

1101
        if (streq(value, "gpt-auto")) {
17✔
1102
                log_debug("Enabling partition auto-detection (respecting factory reset mode), %s is explicitly set to 'gpt-auto'.", switch_name);
×
1103
                return GPT_AUTO_ROOT_ON;
×
1104
        }
1105

1106
        if (streq(value, "gpt-auto-force")) {
17✔
1107
                log_debug("Enabling partition auto-detection (ignoring factory reset mode), %s is explicitly set to 'gpt-auto-force'.", switch_name);
×
1108
                return GPT_AUTO_ROOT_FORCE;
×
1109
        }
1110

1111
        if (streq(value, "dissect")) {
17✔
1112
                log_debug("Enabling partition auto-detection via full image dissection (respecting factory reset mode), %s is explicitly set to 'dissect'.", switch_name);
×
1113
                return GPT_AUTO_ROOT_DISSECT;
×
1114
        }
1115

1116
        if (streq(value, "dissect-force")) {
17✔
1117
                log_debug("Enabling partition auto-detection via full image dissection (ignoring factory reset mode), %s is explicitly set to 'dissect-force'.", switch_name);
×
1118
                return GPT_AUTO_ROOT_DISSECT_FORCE;
×
1119
        }
1120

1121
        if (streq(value, "off"))
17✔
1122
                log_debug("Disabling partition auto-detection, %s handling is explicitly turned off.", switch_name);
×
1123
        else
1124
                log_debug("Disabling partition auto-detection, %s is neither unset, nor set to 'gpt-auto', 'gpt-auto-force', 'dissect' or 'dissect-force'.", switch_name);
17✔
1125

1126
        return GPT_AUTO_ROOT_OFF;
1127
}
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