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

systemd / systemd / 14183987197

31 Mar 2025 08:35PM UTC coverage: 71.916% (+0.02%) from 71.892%
14183987197

push

github

web-flow
validatefs: several follow-ups (#36910)

11 of 11 new or added lines in 2 files covered. (100.0%)

4131 existing lines in 69 files now uncovered.

296681 of 412540 relevant lines covered (71.92%)

654446.78 hits per line

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

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

3
#include <errno.h>
4
#include <unistd.h>
5

6
#include "alloc-util.h"
7
#include "cgroup-util.h"
8
#include "dropin.h"
9
#include "escape.h"
10
#include "fd-util.h"
11
#include "fileio.h"
12
#include "fstab-util.h"
13
#include "generator.h"
14
#include "initrd-util.h"
15
#include "log.h"
16
#include "macro.h"
17
#include "mkdir-label.h"
18
#include "mountpoint-util.h"
19
#include "parse-util.h"
20
#include "path-util.h"
21
#include "process-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
int generator_open_unit_file_full(
382✔
30
                const char *dir,
31
                const char *source,
32
                const char *fn,
33
                FILE **ret_file,
34
                char **ret_final_path,
35
                char **ret_temp_path) {
36

37
        _cleanup_free_ char *p = NULL;
382✔
38
        FILE *f;
382✔
39
        int r;
382✔
40

41
        assert(dir);
382✔
42
        assert(ret_file);
382✔
43

44
        /* If <ret_temp_path> is specified, it creates a temporary unit file and also returns its
45
         * temporary path. */
46

47
        if (ret_temp_path) {
382✔
48
                r = fopen_temporary(dir, &f, &p);
319✔
49
                if (r < 0)
319✔
50
                        return log_error_errno(r, "Failed to create temporary unit file in '%s': %m", dir);
×
51

52
                (void) fchmod(fileno(f), 0644);
319✔
53

54
                *ret_temp_path = TAKE_PTR(p);
319✔
55
        } else {
56
                assert(fn);
63✔
57

58
                p = path_join(dir, fn);
63✔
59
                if (!p)
63✔
60
                        return log_oom();
×
61

62
                r = fopen_unlocked(p, "wxe", &f);
63✔
63
                if (r < 0) {
63✔
64
                        if (source && r == -EEXIST)
×
65
                                return log_error_errno(r,
×
66
                                                       "Failed to create unit file '%s', as it already exists. Duplicate entry in '%s'?",
67
                                                       p, source);
68

69
                        return log_error_errno(r, "Failed to create unit file '%s': %m", p);
×
70
                }
71
        }
72

73
        fprintf(f,
382✔
74
                "# Automatically generated by %s\n\n",
75
                program_invocation_short_name);
76

77
        *ret_file = f;
382✔
78

79
        if (ret_final_path)
382✔
80
                *ret_final_path = TAKE_PTR(p);
×
81

82
        return 0;
83
}
84

85
int generator_add_symlink_full(
180✔
86
                const char *dir,
87
                const char *dst,
88
                const char *dep_type,
89
                const char *src,
90
                const char *instance) {
91

92
        _cleanup_free_ char *dn = NULL, *fn = NULL, *instantiated = NULL, *to = NULL, *from = NULL;
180✔
93
        int r;
180✔
94

95
        assert(dir);
180✔
96
        assert(dst);
180✔
97
        assert(src);
180✔
98

99
        /* If 'dep_type' is specified adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise).
100
         *
101
         * If 'dep_type' is NULL, it will create a symlink to <src> (i.e. create an alias.
102
         *
103
         * If <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
104

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

109
        r = path_extract_filename(src, &fn);
180✔
110
        if (r < 0)
180✔
111
                return log_error_errno(r, "Failed to extract file name from '%s': %m", src);
×
112
        if (r == O_DIRECTORY)
180✔
113
                return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src);
×
114

115
        if (instance) {
180✔
116
                r = unit_name_replace_instance(fn, instance, &instantiated);
1✔
117
                if (r < 0)
1✔
118
                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance);
×
119
        }
120

121
        if (dep_type) { /* Create a .wants/ style dep */
180✔
122
                from = path_join(dn ?: "..", fn);
267✔
123
                if (!from)
180✔
124
                        return log_oom();
×
125

126
                to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn);
180✔
127
        } else { /* or create an alias */
128
                from = dn ? path_join(dn, fn) : strdup(fn);
×
129
                if (!from)
×
130
                        return log_oom();
×
131

132
                to = strjoin(dir, "/", dst);
×
133
        }
134
        if (!to)
180✔
135
                return log_oom();
×
136

137
        (void) mkdir_parents_label(to, 0755);
180✔
138

139
        if (symlink(from, to) < 0 && errno != EEXIST)
180✔
140
                return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
×
141

142
        return 0;
143
}
144

145
static int generator_add_ordering(
×
146
                const char *dir,
147
                const char *src,
148
                const char *order,
149
                const char *dst,
150
                const char *instance) {
151

152
        _cleanup_free_ char *instantiated = NULL, *p = NULL, *fn = NULL;
×
153
        _cleanup_fclose_ FILE *f = NULL;
×
154
        const char *to;
×
155
        int r;
×
156

157
        assert(dir);
×
158
        assert(src);
×
159
        assert(order);
×
160
        assert(dst);
×
161

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

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

170
                to = instantiated;
×
171
        } else
172
                to = dst;
173

174
        fn = strjoin(src, ".d/50-order-", to, ".conf");
×
175
        if (!fn)
×
176
                return log_oom();
×
177

178
        p = path_join(dir, fn);
×
179
        if (!p)
×
180
                return log_oom();
×
181

182
        (void) mkdir_parents_label(p, 0755);
×
183

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

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

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

200
        return 0;
201
}
202

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

209
        _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
7✔
210
        _cleanup_fclose_ FILE *f = NULL;
7✔
211
        int r;
7✔
212

213
        assert(unit);
7✔
214
        assert(dir);
7✔
215
        assert(what);
7✔
216

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

222
        escaped = specifier_escape(what);
7✔
223
        if (!escaped)
7✔
224
                return log_oom();
×
225

226
        escaped2 = cescape(escaped);
7✔
227
        if (!escaped2)
7✔
228
                return log_oom();
×
229

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

234
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
7✔
235
        if (r < 0)
7✔
236
                return r;
237

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

261
        r = fflush_and_check(f);
7✔
262
        if (r < 0)
7✔
263
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
264

265
        return 0;
266
}
267

268
int generator_write_fsck_deps(
14✔
269
                FILE *f,
270
                const char *dir,
271
                const char *what,
272
                const char *where,
273
                const char *fstype) {
274

275
        int r;
14✔
276

277
        assert(f);
14✔
278
        assert(dir);
14✔
279
        assert(what);
14✔
280
        assert(where);
14✔
281

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

289
        if (fstype) {
14✔
290
                if (!fstype_is_blockdev_backed(fstype)) {
10✔
291
                        log_debug("Skipping file system check for non-block based file system '%s'.", what);
×
292
                        return 0;
×
293
                }
294

295
                if (fstype_is_ro(fstype)) {
10✔
296
                        log_debug("Skipping file system check for read-only file system '%s'.", what);
×
297
                        return 0;
×
298
                }
299
        }
300

301
        if (!is_device_path(what)) {
14✔
302
                log_warning("Checking was requested for \"%s\", but it is not a device.", what);
×
303
                return 0;
×
304
        }
305

306
        if (!isempty(fstype) && !streq(fstype, "auto")) {
24✔
307
                r = fsck_exists_for_fstype(fstype);
×
308
                if (r < 0)
×
309
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
×
310
                else if (r == 0) {
×
311
                        /* treat missing check as essentially OK */
312
                        log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
×
313
                        return 0;
×
314
                }
315
        } else {
316
                r = fsck_exists();
14✔
317
                if (r < 0)
14✔
318
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if the fsck command may be used, proceeding: %m", what);
×
319
                else if (r == 0) {
14✔
320
                        /* treat missing fsck as essentially OK */
321
                        log_debug("Checking was requested for %s, but the fsck command does not exist.", what);
×
322
                        return 0;
×
323
                }
324
        }
325

326
        if (path_equal(where, "/")) {
14✔
327
                const char *lnk;
×
328

329
                /* We support running the fsck instance for the root fs while it is already mounted, for
330
                 * compatibility with non-initrd boots. It's ugly, but it is how it is. Since – unlike for
331
                 * regular file systems – this means the ordering is reversed (i.e. mount *before* fsck) we
332
                 * have a separate fsck unit for this, independent of systemd-fsck@.service. */
333

334
                lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
×
335

336
                (void) mkdir_parents(lnk, 0755);
×
337
                if (symlink(SYSTEM_DATA_UNIT_DIR "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
×
338
                        return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
×
339

340
        } else {
341
                _cleanup_free_ char *_fsck = NULL;
14✔
342
                const char *fsck, *dep;
14✔
343

344
                if (in_initrd() && path_equal(where, "/sysroot")) {
24✔
345
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_ROOT_SERVICE, dir, what, SPECIAL_INITRD_ROOT_DEVICE_TARGET);
6✔
346
                        if (r < 0)
6✔
347
                                return r;
348

349
                        fsck = SPECIAL_FSCK_ROOT_SERVICE;
350
                        dep = "Requires";
351

352
                } else if (in_initrd() && path_equal(where, "/sysusr/usr")) {
12✔
353
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_USR_SERVICE, dir, what, NULL);
1✔
354
                        if (r < 0)
1✔
355
                                return r;
356

357
                        fsck = SPECIAL_FSCK_USR_SERVICE;
358
                        dep = "Requires";
359
                } else {
360
                        /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
361
                         * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
362
                         * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
363
                         * we'd have to unmount /usr too.  */
364

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

367
                        r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
7✔
368
                        if (r < 0)
7✔
369
                                return log_error_errno(r, "Failed to create fsck service name: %m");
×
370

371
                        fsck = _fsck;
7✔
372
                }
373

374
                fprintf(f,
14✔
375
                        "%1$s=%2$s\n"
376
                        "After=%2$s\n",
377
                        dep, fsck);
378
        }
379

380
        return 0;
381
}
382

383
int generator_write_device_timeout(
46✔
384
                const char *dir,
385
                const char *what,
386
                const char *opts,
387
                char **filtered) {
388

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

393
        _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
46✔
394
        usec_t u;
46✔
395
        int r;
46✔
396

397
        assert(dir);
46✔
398
        assert(what);
46✔
399

400
        r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
46✔
401
                                       "x-systemd.device-timeout\0",
402
                                 NULL, &timeout, NULL, filtered);
403
        if (r < 0) {
46✔
404
                log_warning_errno(r, "Failed to parse fstab options, ignoring: %m");
×
405
                return 0;
×
406
        }
407
        if (r == 0)
46✔
408
                return 0;
409

410
        r = parse_sec_fix_0(timeout, &u);
1✔
411
        if (r < 0) {
1✔
412
                log_warning("Failed to parse timeout for device '%s', ignoring: %s", what, timeout);
×
413
                return 0;
×
414
        }
415

416
        node = fstab_node_to_udev_node(what);
1✔
417
        if (!node)
1✔
418
                return log_oom();
×
419
        if (!is_device_path(node)) {
1✔
420
                log_warning("'%s' is not a device path, ignoring x-systemd.device-timeout= option.", node);
1✔
421
                return 0;
1✔
422
        }
423

424
        r = unit_name_from_path(node, ".device", &unit);
×
425
        if (r < 0)
×
426
                return log_error_errno(r, "Failed to make unit name from device path '%s': %m", node);
×
427

428
        r = write_drop_in_format(dir, unit, 50, "device-timeout",
×
429
                                 "# Automatically generated by %s\n"
430
                                 "# from supplied options \"%s\"\n\n"
431
                                 "[Unit]\n"
432
                                 "JobRunningTimeoutSec=%s",
433
                                 program_invocation_short_name,
434
                                 opts,
435
                                 timeout);
436
        if (r < 0)
×
437
                return r;
×
438

439
        return 1;
440
}
441

442
int generator_write_unit_timeout(
24✔
443
                FILE *f,
444
                const char *where,
445
                const char *opts,
446
                const char *filter,
447
                const char *unit_setting) {
448

449
        _cleanup_free_ char *timeout = NULL;
24✔
450
        usec_t u;
24✔
451
        int r;
24✔
452

453
        assert(f);
24✔
454
        assert(where);
24✔
455
        assert(filter);
24✔
456
        assert(unit_setting);
24✔
457

458
        r = fstab_filter_options(opts, filter, NULL, &timeout, NULL, NULL);
24✔
459
        if (r < 0)
24✔
460
                return log_error_errno(r, "Failed to parse options for '%s': %m", where);
×
461
        if (r == 0)
24✔
462
                return 0;
463

464
        r = parse_sec_fix_0(timeout, &u);
×
465
        if (r < 0) {
×
466
                log_warning_errno(r, "Failed to parse timeout '%s' for '%s', ignoring: %m", timeout, where);
×
467
                return 0;
×
468
        }
469

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

472
        return 0;
473
}
474

475
int generator_write_network_device_deps(
24✔
476
                const char *dir,
477
                const char *what,
478
                const char *where,
479
                const char *opts) {
480

481
        /* fstab records that specify _netdev option should apply the network
482
         * ordering on the actual device depending on network connection. If we
483
         * are not mounting real device (NFS, CIFS), we rely on _netdev effect
484
         * on the mount unit itself. */
485

486
        _cleanup_free_ char *node = NULL, *unit = NULL;
24✔
487
        int r;
24✔
488

489
        assert(dir);
24✔
490
        assert(what);
24✔
491
        assert(where);
24✔
492

493
        if (fstab_is_extrinsic(where, opts))
24✔
494
                return 0;
495

496
        if (!fstab_test_option(opts, "_netdev\0"))
22✔
497
                return 0;
498

499
        node = fstab_node_to_udev_node(what);
×
500
        if (!node)
×
501
                return log_oom();
×
502

503
        /* Nothing to apply dependencies to. */
504
        if (!is_device_path(node))
×
505
                return 0;
506

507
        r = unit_name_from_path(node, ".device", &unit);
×
508
        if (r < 0)
×
509
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", node);
×
510

511
        /* See mount_add_default_dependencies for explanation why we create such
512
         * dependencies. */
513
        return write_drop_in_format(dir, unit, 50, "netdev-dependencies",
×
514
                                    "# Automatically generated by %s\n\n"
515
                                    "[Unit]\n"
516
                                    "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n"
517
                                    "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n",
518
                                    program_invocation_short_name);
519
}
520

521
int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
6✔
522
        _cleanup_free_ char *unit = NULL;
6✔
523
        int r;
6✔
524

525
        r = unit_name_from_path(what, ".device", &unit);
6✔
526
        if (r < 0)
6✔
527
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
528
                                       what);
529

530
        return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
6✔
531
                                    "# Automatically generated by %s\n\n"
532
                                    "[Unit]\n"
533
                                    "Requires=%s\n"
534
                                    "After=%s",
535
                                    program_invocation_short_name,
536
                                    unit,
537
                                    unit);
538
}
539

540
int generator_hook_up_mkswap(
×
541
                const char *dir,
542
                const char *what) {
543

544
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
×
545
        _cleanup_fclose_ FILE *f = NULL;
×
546
        int r;
×
547

548
        assert(dir);
×
549
        assert(what);
×
550

551
        node = fstab_node_to_udev_node(what);
×
552
        if (!node)
×
553
                return log_oom();
×
554

555
        /* Nothing to work on. */
556
        if (!is_device_path(node))
×
557
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
558
                                       "Cannot format something that is not a device node: %s",
559
                                       node);
560

561
        r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
×
562
        if (r < 0)
×
563
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
×
564
                                       node);
565

566
        escaped = cescape(node);
×
567
        if (!escaped)
×
568
                return log_oom();
×
569

570
        r = unit_name_from_path(what, ".swap", &where_unit);
×
571
        if (r < 0)
×
572
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
573
                                       what);
574

575
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
×
576
        if (r < 0)
×
577
                return r;
578

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

599
        r = fflush_and_check(f);
×
600
        if (r < 0)
×
601
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
602

603
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
604
}
605

606
int generator_hook_up_mkfs(
×
607
                const char *dir,
608
                const char *what,
609
                const char *where,
610
                const char *type) {
611

612
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
×
613
        _cleanup_fclose_ FILE *f = NULL;
×
614
        const char *fsck_unit;
×
615
        int r;
×
616

617
        assert(dir);
×
618
        assert(what);
×
619
        assert(where);
×
620

621
        node = fstab_node_to_udev_node(what);
×
622
        if (!node)
×
623
                return log_oom();
×
624

625
        /* Nothing to work on. */
626
        if (!is_device_path(node))
×
627
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
628
                                       "Cannot format something that is not a device node: %s",
629
                                       node);
630

631
        if (!type || streq(type, "auto"))
×
632
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
633
                                       "Cannot format partition %s, filesystem type is not specified",
634
                                       node);
635

636
        r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit);
×
637
        if (r < 0)
×
638
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
×
639
                                       node);
640

641
        if (in_initrd() && path_equal(where, "/sysroot"))
×
642
                fsck_unit = SPECIAL_FSCK_ROOT_SERVICE;
643
        else if (in_initrd() && path_equal(where, "/sysusr/usr"))
×
644
                fsck_unit = SPECIAL_FSCK_USR_SERVICE;
645
        else
646
                fsck_unit = "systemd-fsck@%i.service";
647

648
        escaped = cescape(node);
×
649
        if (!escaped)
×
650
                return log_oom();
×
651

652
        r = unit_name_from_path(where, ".mount", &where_unit);
×
653
        if (r < 0)
×
654
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
655
                                       where);
656

657
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
×
658
        if (r < 0)
×
659
                return r;
660

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

686
        r = fflush_and_check(f);
×
687
        if (r < 0)
×
688
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
689

690
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
691
}
692

693
int generator_hook_up_growfs(
×
694
                const char *dir,
695
                const char *where,
696
                const char *target) {
697

698
        const char *growfs_unit, *growfs_unit_path;
×
699
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
700
        int r;
×
701

702
        assert(dir);
×
703
        assert(where);
×
704

705
        r = unit_name_from_path(where, ".mount", &where_unit);
×
706
        if (r < 0)
×
707
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
708

709
        if (empty_or_root(where)) {
×
710
                growfs_unit = SPECIAL_GROWFS_ROOT_SERVICE;
711
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_ROOT_SERVICE;
712
        } else {
713
                growfs_unit = SPECIAL_GROWFS_SERVICE;
×
714
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_SERVICE;
×
715

716
                r = unit_name_path_escape(where, &instance);
×
717
                if (r < 0)
×
718
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
719
        }
720

721
        if (target) {
×
722
                r = generator_add_ordering(dir, target, "After", growfs_unit, instance);
×
723
                if (r < 0)
×
724
                        return r;
725
        }
726

727
        return generator_add_symlink_full(dir, where_unit, "wants", growfs_unit_path, instance);
×
728
}
729

730
int generator_hook_up_pcrfs(
×
731
                const char *dir,
732
                const char *where,
733
                const char *target) {
734

735
        const char *pcrfs_unit, *pcrfs_unit_path;
×
736
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
737
        int r;
×
738

739
        assert(dir);
×
740
        assert(where);
×
741

742
        r = unit_name_from_path(where, ".mount", &where_unit);
×
743
        if (r < 0)
×
744
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
745

746
        if (empty_or_root(where)) {
×
747
                pcrfs_unit = SPECIAL_PCRFS_ROOT_SERVICE;
748
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_ROOT_SERVICE;
749
        } else {
750
                pcrfs_unit = SPECIAL_PCRFS_SERVICE;
×
751
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_SERVICE;
×
752

753
                r = unit_name_path_escape(where, &instance);
×
754
                if (r < 0)
×
755
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
756
        }
757

758
        if (target) {
×
759
                r = generator_add_ordering(dir, target, "After", pcrfs_unit, instance);
×
760
                if (r < 0)
×
761
                        return r;
762
        }
763

764
        return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
×
765
}
766

767
int generator_hook_up_validatefs(
×
768
                const char *dir,
769
                const char *where,
770
                const char *target) {
771

UNCOV
772
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
UNCOV
773
        const char *validatefs_unit, *validatefs_unit_path;
×
774
        int r;
×
775

UNCOV
776
        assert(dir);
×
777
        assert(where);
×
778

779
        /* never hook this in for the actual root fs, because it's too late then, we already are running from
780
         * the root fs, it makes no sense to validate it anymore */
781
        if (empty_or_root(where))
×
782
                return 0;
783

UNCOV
784
        r = unit_name_from_path(where, ".mount", &where_unit);
×
UNCOV
785
        if (r < 0)
×
786
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
787

UNCOV
788
        validatefs_unit = SPECIAL_VALIDATEFS_SERVICE;
×
789
        validatefs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_VALIDATEFS_SERVICE;
×
790

791
        r = unit_name_path_escape(where, &instance);
×
UNCOV
792
        if (r < 0)
×
793
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
794

795
        if (target) {
×
UNCOV
796
                r = generator_add_ordering(dir, target, "After", validatefs_unit, instance);
×
UNCOV
797
                if (r < 0)
×
798
                        return r;
799
        }
800

801
        return generator_add_symlink_full(dir, where_unit, "wants", validatefs_unit_path, instance);
×
802
}
803

UNCOV
804
int generator_hook_up_quotacheck(
×
805
                const char *dir,
806
                const char *what,
807
                const char *where,
808
                const char *target,
809
                const char *fstype) {
810

811
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
812
        int r;
×
813

814
        assert(dir);
×
815
        assert(where);
×
816

UNCOV
817
        if (isempty(fstype) || streq(fstype, "auto"))
×
818
                return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Couldn't determine filesystem type for %s, quota cannot be activated", what);
×
819
        if (!fstype_needs_quota(fstype))
×
UNCOV
820
                return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Quota was requested for %s, but not supported, ignoring: %s", what, fstype);
×
821

822
        /* quotacheck unit for system root */
823
        if (path_equal(where, "/"))
×
UNCOV
824
                return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_ROOT_SERVICE);
×
825

826
        r = unit_name_path_escape(where, &instance);
×
827
        if (r < 0)
×
UNCOV
828
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
829

UNCOV
830
        if (target) {
×
831
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTACHECK_SERVICE, instance);
×
832
                if (r < 0)
×
833
                        return r;
834
        }
835

UNCOV
836
        r = unit_name_from_path(where, ".mount", &where_unit);
×
UNCOV
837
        if (r < 0)
×
UNCOV
838
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
839

UNCOV
840
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_SERVICE, instance);
×
841
}
842

UNCOV
843
int generator_hook_up_quotaon(
×
844
                const char *dir,
845
                const char *where,
846
                const char *target) {
847

UNCOV
848
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
UNCOV
849
        int r;
×
850

UNCOV
851
        assert(dir);
×
UNCOV
852
        assert(where);
×
853

854
        /* quotaon unit for system root is not instantiated */
UNCOV
855
        if (path_equal(where, "/"))
×
856
                return generator_add_symlink(dir,  SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_ROOT_SERVICE);
×
857

UNCOV
858
        r = unit_name_path_escape(where, &instance);
×
UNCOV
859
        if (r < 0)
×
UNCOV
860
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
861

UNCOV
862
        if (target) {
×
UNCOV
863
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTAON_SERVICE, instance);
×
UNCOV
864
                if (r < 0)
×
865
                        return r;
866
        }
867

UNCOV
868
        r = unit_name_from_path(where, ".mount", &where_unit);
×
UNCOV
869
        if (r < 0)
×
UNCOV
870
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
871

UNCOV
872
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_SERVICE, instance);
×
873
}
874

875
int generator_enable_remount_fs_service(const char *dir) {
2✔
876
        /* Pull in systemd-remount-fs.service */
877
        return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants",
2✔
878
                                     SYSTEM_DATA_UNIT_DIR "/" SPECIAL_REMOUNT_FS_SERVICE);
879
}
880

881
int generator_write_blockdev_dependency(FILE *f, const char *what) {
24✔
882
        _cleanup_free_ char *escaped = NULL;
24✔
883
        int r;
24✔
884

885
        assert(f);
24✔
886
        assert(what);
24✔
887

888
        if (!path_startswith(what, "/dev/"))
24✔
889
                return 0;
890

891
        r = unit_name_path_escape(what, &escaped);
16✔
892
        if (r < 0)
16✔
UNCOV
893
                return log_error_errno(r, "Failed to escape device node path %s: %m", what);
×
894

895
        fprintf(f,
16✔
896
                "After=blockdev@%s.target\n",
897
                escaped);
898

899
        return 0;
900
}
901

902
int generator_write_cryptsetup_unit_section(FILE *f, const char *source) {
22✔
903
        assert(f);
22✔
904

905
        fprintf(f,
22✔
906
                "[Unit]\n"
907
                "Description=Cryptography Setup for %%I\n"
908
                "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n");
909

910
        if (source)
22✔
911
                fprintf(f, "SourcePath=%s\n", source);
22✔
912

913
        fprintf(f,
22✔
914
                "\n"
915
                "DefaultDependencies=no\n"
916
                "After=cryptsetup-pre.target systemd-udevd-kernel.socket systemd-tpm2-setup-early.service\n"
917
                "Before=blockdev@dev-mapper-%%i.target\n"
918
                "Wants=blockdev@dev-mapper-%%i.target\n"
919
                "IgnoreOnIsolate=true\n");
920

921
        return 0;
22✔
922
}
923

924
int generator_write_cryptsetup_service_section(
22✔
925
                FILE *f,
926
                const char *name,
927
                const char *what,
928
                const char *key_file,
929
                const char *options) {
930

931
        _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *key_file_escaped = NULL, *options_escaped = NULL;
22✔
932

933
        assert(f);
22✔
934
        assert(name);
22✔
935
        assert(what);
22✔
936

937
        name_escaped = specifier_escape(name);
22✔
938
        if (!name_escaped)
22✔
UNCOV
939
                return log_oom();
×
940

941
        what_escaped = specifier_escape(what);
22✔
942
        if (!what_escaped)
22✔
UNCOV
943
                return log_oom();
×
944

945
        if (key_file) {
22✔
946
                key_file_escaped = specifier_escape(key_file);
22✔
947
                if (!key_file_escaped)
22✔
948
                        return log_oom();
×
949
        }
950

951
        if (options) {
22✔
952
                options_escaped = specifier_escape(options);
22✔
953
                if (!options_escaped)
22✔
UNCOV
954
                        return log_oom();
×
955
        }
956

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

971
        return 0;
972
}
973

974
int generator_write_veritysetup_unit_section(FILE *f, const char *source) {
×
975
        assert(f);
×
976

UNCOV
977
        fprintf(f,
×
978
                "[Unit]\n"
979
                "Description=Integrity Protection Setup for %%I\n"
980
                "Documentation=man:veritytab(5) man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n");
981

982
        if (source)
×
983
                fprintf(f, "SourcePath=%s\n", source);
×
984

UNCOV
985
        fprintf(f,
×
986
                "DefaultDependencies=no\n"
987
                "IgnoreOnIsolate=true\n"
988
                "After=veritysetup-pre.target systemd-udevd-kernel.socket\n"
989
                "Before=blockdev@dev-mapper-%%i.target\n"
990
                "Wants=blockdev@dev-mapper-%%i.target\n");
991

992
        return 0;
×
993
}
994

UNCOV
995
int generator_write_veritysetup_service_section(
×
996
                FILE *f,
997
                const char *name,
998
                const char *data_what,
999
                const char *hash_what,
1000
                const char *roothash,
1001
                const char *options) {
1002

UNCOV
1003
        _cleanup_free_ char *name_escaped = NULL, *data_what_escaped = NULL, *hash_what_escaped = NULL,
×
UNCOV
1004
                            *roothash_escaped = NULL, *options_escaped = NULL;
×
1005

UNCOV
1006
        assert(f);
×
UNCOV
1007
        assert(name);
×
UNCOV
1008
        assert(data_what);
×
UNCOV
1009
        assert(hash_what);
×
1010

UNCOV
1011
        name_escaped = specifier_escape(name);
×
UNCOV
1012
        if (!name_escaped)
×
UNCOV
1013
                return log_oom();
×
1014

UNCOV
1015
        data_what_escaped = specifier_escape(data_what);
×
UNCOV
1016
        if (!data_what_escaped)
×
UNCOV
1017
                return log_oom();
×
1018

UNCOV
1019
        hash_what_escaped = specifier_escape(hash_what);
×
UNCOV
1020
        if (!hash_what_escaped)
×
UNCOV
1021
                return log_oom();
×
1022

UNCOV
1023
        roothash_escaped = specifier_escape(roothash);
×
1024
        if (!roothash_escaped)
×
1025
                return log_oom();
×
1026

UNCOV
1027
        if (options) {
×
1028
                options_escaped = specifier_escape(options);
×
1029
                if (!options_escaped)
×
UNCOV
1030
                        return log_oom();
×
1031
        }
1032

1033
        fprintf(f,
×
1034
                "\n"
1035
                "[Service]\n"
1036
                "Type=oneshot\n"
1037
                "RemainAfterExit=yes\n"
1038
                "ExecStart=" SYSTEMD_VERITYSETUP_PATH " attach '%s' '%s' '%s' '%s' '%s'\n"
1039
                "ExecStop=" SYSTEMD_VERITYSETUP_PATH " detach '%s'\n",
1040
                name_escaped, data_what_escaped, hash_what_escaped, roothash_escaped, strempty(options_escaped),
1041
                name_escaped);
1042

1043
        return 0;
1044
}
1045

1046
void log_setup_generator(void) {
207✔
1047
        if (invoked_by_systemd()) {
207✔
1048
                /* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
1049
                if (cg_pid_get_owner_uid(0, NULL) == -ENXIO /* not running in a per-user slice */)
140✔
1050
                        log_set_prohibit_ipc(true);
1✔
1051

1052
                /* This effectively means: journal for per-user generators, kmsg otherwise */
1053
                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
140✔
1054
        } else
1055
                log_set_target(LOG_TARGET_AUTO);
67✔
1056

1057
        log_parse_environment();
207✔
1058
        log_open();
207✔
1059
}
207✔
1060

UNCOV
1061
bool generator_soft_rebooted(void) {
×
UNCOV
1062
        static int cached = -1;
×
UNCOV
1063
        int r;
×
1064

UNCOV
1065
        if (cached >= 0)
×
UNCOV
1066
                return cached;
×
1067

UNCOV
1068
        const char *e = secure_getenv("SYSTEMD_SOFT_REBOOTS_COUNT");
×
UNCOV
1069
        if (!e)
×
UNCOV
1070
                return (cached = false);
×
1071

UNCOV
1072
        unsigned u;
×
1073

UNCOV
1074
        r = safe_atou(e, &u);
×
UNCOV
1075
        if (r < 0) {
×
UNCOV
1076
                log_debug_errno(r, "Failed to parse $SYSTEMD_SOFT_REBOOTS_COUNT, assuming the system hasn't soft-rebooted: %m");
×
UNCOV
1077
                return (cached = false);
×
1078
        }
1079

UNCOV
1080
        return (cached = (u > 0));
×
1081
}
1082

1083
GptAutoRoot parse_gpt_auto_root(const char *value) {
17✔
1084
        assert(value);
17✔
1085

1086
        /* Parses the 'gpt-auto'/'gpt-auto-root' parameters to root= */
1087

1088
        if (streq(value, "gpt-auto")) {
17✔
UNCOV
1089
                log_debug("Enabling root partition auto-detection (respecting factory reset mode), root= is explicitly set to 'gpt-auto'.");
×
UNCOV
1090
                return GPT_AUTO_ROOT_ON;
×
1091
        }
1092

1093
        if (streq(value, "gpt-auto-force")) {
17✔
UNCOV
1094
                log_debug("Enabling root partition auto-detection (ignoring factory reset mode), root= is explicitly set to 'gpt-auto-force'.");
×
UNCOV
1095
                return GPT_AUTO_ROOT_FORCE;
×
1096
        }
1097

1098
        log_debug("Disabling root partition auto-detection, root= is neither unset, nor set to 'gpt-auto' or 'gpt-auto-force'.");
17✔
1099
        return GPT_AUTO_ROOT_OFF;
1100
}
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