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

systemd / systemd / 13688105616

05 Mar 2025 11:59PM UTC coverage: 71.788% (-0.07%) from 71.855%
13688105616

push

github

yuwata
mkosi: update debian commit reference

* dfdab6b205 Install new files
* e00bee5b4a Install new files

294781 of 410628 relevant lines covered (71.79%)

715950.09 hits per line

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

37.99
/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(
590✔
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;
590✔
38
        FILE *f;
590✔
39
        int r;
590✔
40

41
        assert(dir);
590✔
42
        assert(ret_file);
590✔
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) {
590✔
48
                r = fopen_temporary(dir, &f, &p);
527✔
49
                if (r < 0)
527✔
50
                        return log_error_errno(r, "Failed to create temporary unit file in '%s': %m", dir);
×
51

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

54
                *ret_temp_path = TAKE_PTR(p);
527✔
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,
590✔
74
                "# Automatically generated by %s\n\n",
75
                program_invocation_short_name);
76

77
        *ret_file = f;
590✔
78

79
        if (ret_final_path)
590✔
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 (!is_device_path(what)) {
14✔
290
                log_warning("Checking was requested for \"%s\", but it is not a device.", what);
×
291
                return 0;
×
292
        }
293

294
        if (!isempty(fstype) && !streq(fstype, "auto")) {
24✔
295
                r = fsck_exists_for_fstype(fstype);
×
296
                if (r < 0)
×
297
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
×
298
                else if (r == 0) {
×
299
                        /* treat missing check as essentially OK */
300
                        log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
×
301
                        return 0;
×
302
                }
303
        } else {
304
                r = fsck_exists();
14✔
305
                if (r < 0)
14✔
306
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if the fsck command may be used, proceeding: %m", what);
×
307
                else if (r == 0) {
14✔
308
                        /* treat missing fsck as essentially OK */
309
                        log_debug("Checking was requested for %s, but the fsck command does not exist.", what);
×
310
                        return 0;
×
311
                }
312
        }
313

314
        if (path_equal(where, "/")) {
14✔
315
                const char *lnk;
×
316

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

322
                lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
×
323

324
                (void) mkdir_parents(lnk, 0755);
×
325
                if (symlink(SYSTEM_DATA_UNIT_DIR "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
×
326
                        return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
×
327

328
        } else {
329
                _cleanup_free_ char *_fsck = NULL;
14✔
330
                const char *fsck, *dep;
14✔
331

332
                if (in_initrd() && path_equal(where, "/sysroot")) {
24✔
333
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_ROOT_SERVICE, dir, what, SPECIAL_INITRD_ROOT_DEVICE_TARGET);
6✔
334
                        if (r < 0)
6✔
335
                                return r;
336

337
                        fsck = SPECIAL_FSCK_ROOT_SERVICE;
338
                        dep = "Requires";
339

340
                } else if (in_initrd() && path_equal(where, "/sysusr/usr")) {
12✔
341
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_USR_SERVICE, dir, what, NULL);
1✔
342
                        if (r < 0)
1✔
343
                                return r;
344

345
                        fsck = SPECIAL_FSCK_USR_SERVICE;
346
                        dep = "Requires";
347
                } else {
348
                        /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
349
                         * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
350
                         * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
351
                         * we'd have to unmount /usr too.  */
352

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

355
                        r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
7✔
356
                        if (r < 0)
7✔
357
                                return log_error_errno(r, "Failed to create fsck service name: %m");
×
358

359
                        fsck = _fsck;
7✔
360
                }
361

362
                fprintf(f,
14✔
363
                        "%1$s=%2$s\n"
364
                        "After=%2$s\n",
365
                        dep, fsck);
366
        }
367

368
        return 0;
369
}
370

371
int generator_write_device_timeout(
46✔
372
                const char *dir,
373
                const char *what,
374
                const char *opts,
375
                char **filtered) {
376

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

381
        _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
46✔
382
        usec_t u;
46✔
383
        int r;
46✔
384

385
        assert(dir);
46✔
386
        assert(what);
46✔
387

388
        r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
46✔
389
                                       "x-systemd.device-timeout\0",
390
                                 NULL, &timeout, NULL, filtered);
391
        if (r < 0) {
46✔
392
                log_warning_errno(r, "Failed to parse fstab options, ignoring: %m");
×
393
                return 0;
×
394
        }
395
        if (r == 0)
46✔
396
                return 0;
397

398
        r = parse_sec_fix_0(timeout, &u);
1✔
399
        if (r < 0) {
1✔
400
                log_warning("Failed to parse timeout for device '%s', ignoring: %s", what, timeout);
×
401
                return 0;
×
402
        }
403

404
        node = fstab_node_to_udev_node(what);
1✔
405
        if (!node)
1✔
406
                return log_oom();
×
407
        if (!is_device_path(node)) {
1✔
408
                log_warning("'%s' is not a device path, ignoring x-systemd.device-timeout= option.", node);
1✔
409
                return 0;
1✔
410
        }
411

412
        r = unit_name_from_path(node, ".device", &unit);
×
413
        if (r < 0)
×
414
                return log_error_errno(r, "Failed to make unit name from device path '%s': %m", node);
×
415

416
        r = write_drop_in_format(dir, unit, 50, "device-timeout",
×
417
                                 "# Automatically generated by %s\n"
418
                                 "# from supplied options \"%s\"\n\n"
419
                                 "[Unit]\n"
420
                                 "JobRunningTimeoutSec=%s",
421
                                 program_invocation_short_name,
422
                                 opts,
423
                                 timeout);
424
        if (r < 0)
×
425
                return r;
×
426

427
        return 1;
428
}
429

430
int generator_write_unit_timeout(
24✔
431
                FILE *f,
432
                const char *where,
433
                const char *opts,
434
                const char *filter,
435
                const char *unit_setting) {
436

437
        _cleanup_free_ char *timeout = NULL;
24✔
438
        usec_t u;
24✔
439
        int r;
24✔
440

441
        assert(f);
24✔
442
        assert(where);
24✔
443
        assert(filter);
24✔
444
        assert(unit_setting);
24✔
445

446
        r = fstab_filter_options(opts, filter, NULL, &timeout, NULL, NULL);
24✔
447
        if (r < 0)
24✔
448
                return log_error_errno(r, "Failed to parse options for '%s': %m", where);
×
449
        if (r == 0)
24✔
450
                return 0;
451

452
        r = parse_sec_fix_0(timeout, &u);
×
453
        if (r < 0) {
×
454
                log_warning_errno(r, "Failed to parse timeout '%s' for '%s', ignoring: %m", timeout, where);
×
455
                return 0;
×
456
        }
457

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

460
        return 0;
461
}
462

463
int generator_write_network_device_deps(
24✔
464
                const char *dir,
465
                const char *what,
466
                const char *where,
467
                const char *opts) {
468

469
        /* fstab records that specify _netdev option should apply the network
470
         * ordering on the actual device depending on network connection. If we
471
         * are not mounting real device (NFS, CIFS), we rely on _netdev effect
472
         * on the mount unit itself. */
473

474
        _cleanup_free_ char *node = NULL, *unit = NULL;
24✔
475
        int r;
24✔
476

477
        assert(dir);
24✔
478
        assert(what);
24✔
479
        assert(where);
24✔
480

481
        if (fstab_is_extrinsic(where, opts))
24✔
482
                return 0;
483

484
        if (!fstab_test_option(opts, "_netdev\0"))
22✔
485
                return 0;
486

487
        node = fstab_node_to_udev_node(what);
×
488
        if (!node)
×
489
                return log_oom();
×
490

491
        /* Nothing to apply dependencies to. */
492
        if (!is_device_path(node))
×
493
                return 0;
494

495
        r = unit_name_from_path(node, ".device", &unit);
×
496
        if (r < 0)
×
497
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", node);
×
498

499
        /* See mount_add_default_dependencies for explanation why we create such
500
         * dependencies. */
501
        return write_drop_in_format(dir, unit, 50, "netdev-dependencies",
×
502
                                    "# Automatically generated by %s\n\n"
503
                                    "[Unit]\n"
504
                                    "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n"
505
                                    "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n",
506
                                    program_invocation_short_name);
507
}
508

509
int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
6✔
510
        _cleanup_free_ char *unit = NULL;
6✔
511
        int r;
6✔
512

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

518
        return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
6✔
519
                                    "# Automatically generated by %s\n\n"
520
                                    "[Unit]\n"
521
                                    "Requires=%s\n"
522
                                    "After=%s",
523
                                    program_invocation_short_name,
524
                                    unit,
525
                                    unit);
526
}
527

528
int generator_hook_up_mkswap(
×
529
                const char *dir,
530
                const char *what) {
531

532
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
×
533
        _cleanup_fclose_ FILE *f = NULL;
×
534
        int r;
×
535

536
        assert(dir);
×
537
        assert(what);
×
538

539
        node = fstab_node_to_udev_node(what);
×
540
        if (!node)
×
541
                return log_oom();
×
542

543
        /* Nothing to work on. */
544
        if (!is_device_path(node))
×
545
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
546
                                       "Cannot format something that is not a device node: %s",
547
                                       node);
548

549
        r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
×
550
        if (r < 0)
×
551
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
×
552
                                       node);
553

554
        escaped = cescape(node);
×
555
        if (!escaped)
×
556
                return log_oom();
×
557

558
        r = unit_name_from_path(what, ".swap", &where_unit);
×
559
        if (r < 0)
×
560
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
561
                                       what);
562

563
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
×
564
        if (r < 0)
×
565
                return r;
566

567
        fprintf(f,
×
568
                "[Unit]\n"
569
                "Description=Make Swap on %%f\n"
570
                "Documentation=man:systemd-mkswap@.service(8)\n"
571
                "\n"
572
                "DefaultDependencies=no\n"
573
                "BindsTo=%%i.device\n"
574
                "After=%%i.device\n"
575
                "Before=%s\n"
576
                "Conflicts=shutdown.target\n"
577
                "Before=shutdown.target\n"
578
                "\n"
579
                "[Service]\n"
580
                "Type=oneshot\n"
581
                "RemainAfterExit=yes\n"
582
                "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
583
                "TimeoutSec=infinity\n",
584
                where_unit,
585
                escaped);
586

587
        r = fflush_and_check(f);
×
588
        if (r < 0)
×
589
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
590

591
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
592
}
593

594
int generator_hook_up_mkfs(
×
595
                const char *dir,
596
                const char *what,
597
                const char *where,
598
                const char *type) {
599

600
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
×
601
        _cleanup_fclose_ FILE *f = NULL;
×
602
        const char *fsck_unit;
×
603
        int r;
×
604

605
        assert(dir);
×
606
        assert(what);
×
607
        assert(where);
×
608

609
        node = fstab_node_to_udev_node(what);
×
610
        if (!node)
×
611
                return log_oom();
×
612

613
        /* Nothing to work on. */
614
        if (!is_device_path(node))
×
615
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
616
                                       "Cannot format something that is not a device node: %s",
617
                                       node);
618

619
        if (!type || streq(type, "auto"))
×
620
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
621
                                       "Cannot format partition %s, filesystem type is not specified",
622
                                       node);
623

624
        r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit);
×
625
        if (r < 0)
×
626
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
×
627
                                       node);
628

629
        if (in_initrd() && path_equal(where, "/sysroot"))
×
630
                fsck_unit = SPECIAL_FSCK_ROOT_SERVICE;
631
        else if (in_initrd() && path_equal(where, "/sysusr/usr"))
×
632
                fsck_unit = SPECIAL_FSCK_USR_SERVICE;
633
        else
634
                fsck_unit = "systemd-fsck@%i.service";
635

636
        escaped = cescape(node);
×
637
        if (!escaped)
×
638
                return log_oom();
×
639

640
        r = unit_name_from_path(where, ".mount", &where_unit);
×
641
        if (r < 0)
×
642
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
643
                                       where);
644

645
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
×
646
        if (r < 0)
×
647
                return r;
648

649
        fprintf(f,
×
650
                "[Unit]\n"
651
                "Description=Make File System on %%f\n"
652
                "Documentation=man:systemd-makefs@.service(8)\n"
653
                "\n"
654
                "DefaultDependencies=no\n"
655
                "BindsTo=%%i.device\n"
656
                "After=%%i.device\n"
657
                /* fsck might or might not be used, so let's be safe and order
658
                 * ourselves before both systemd-fsck@.service and the mount unit. */
659
                "Before=%s %s\n"
660
                "Conflicts=shutdown.target\n"
661
                "Before=shutdown.target\n"
662
                "\n"
663
                "[Service]\n"
664
                "Type=oneshot\n"
665
                "RemainAfterExit=yes\n"
666
                "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
667
                "TimeoutSec=infinity\n",
668
                fsck_unit,
669
                where_unit,
670
                type,
671
                escaped);
672
        // XXX: what about local-fs-pre.target?
673

674
        r = fflush_and_check(f);
×
675
        if (r < 0)
×
676
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
677

678
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
679
}
680

681
int generator_hook_up_growfs(
×
682
                const char *dir,
683
                const char *where,
684
                const char *target) {
685

686
        const char *growfs_unit, *growfs_unit_path;
×
687
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
688
        int r;
×
689

690
        assert(dir);
×
691
        assert(where);
×
692

693
        r = unit_name_from_path(where, ".mount", &where_unit);
×
694
        if (r < 0)
×
695
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
696

697
        if (empty_or_root(where)) {
×
698
                growfs_unit = SPECIAL_GROWFS_ROOT_SERVICE;
699
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_ROOT_SERVICE;
700
        } else {
701
                growfs_unit = SPECIAL_GROWFS_SERVICE;
×
702
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_SERVICE;
×
703

704
                r = unit_name_path_escape(where, &instance);
×
705
                if (r < 0)
×
706
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
707
        }
708

709
        if (target) {
×
710
                r = generator_add_ordering(dir, target, "After", growfs_unit, instance);
×
711
                if (r < 0)
×
712
                        return r;
713
        }
714

715
        return generator_add_symlink_full(dir, where_unit, "wants", growfs_unit_path, instance);
×
716
}
717

718
int generator_hook_up_pcrfs(
×
719
                const char *dir,
720
                const char *where,
721
                const char *target) {
722

723
        const char *pcrfs_unit, *pcrfs_unit_path;
×
724
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
725
        int r;
×
726

727
        assert(dir);
×
728
        assert(where);
×
729

730
        r = unit_name_from_path(where, ".mount", &where_unit);
×
731
        if (r < 0)
×
732
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
733

734
        if (empty_or_root(where)) {
×
735
                pcrfs_unit = SPECIAL_PCRFS_ROOT_SERVICE;
736
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_ROOT_SERVICE;
737
        } else {
738
                pcrfs_unit = SPECIAL_PCRFS_SERVICE;
×
739
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_SERVICE;
×
740

741
                r = unit_name_path_escape(where, &instance);
×
742
                if (r < 0)
×
743
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
744
        }
745

746
        if (target) {
×
747
                r = generator_add_ordering(dir, target, "After", pcrfs_unit, instance);
×
748
                if (r < 0)
×
749
                        return r;
750
        }
751

752
        return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
×
753
}
754

755
int generator_hook_up_quotacheck(
×
756
                const char *dir,
757
                const char *what,
758
                const char *where,
759
                const char *target,
760
                const char *fstype) {
761

762
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
763
        int r;
×
764

765
        assert(dir);
×
766
        assert(where);
×
767

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

773
        /* quotacheck unit for system root */
774
        if (path_equal(where, "/"))
×
775
                return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_ROOT_SERVICE);
×
776

777
        r = unit_name_path_escape(where, &instance);
×
778
        if (r < 0)
×
779
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
780

781
        if (target) {
×
782
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTACHECK_SERVICE, instance);
×
783
                if (r < 0)
×
784
                        return r;
785
        }
786

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

791
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_SERVICE, instance);
×
792
}
793

794
int generator_hook_up_quotaon(
×
795
                const char *dir,
796
                const char *where,
797
                const char *target) {
798

799
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
800
        int r;
×
801

802
        assert(dir);
×
803
        assert(where);
×
804

805
        /* quotaon unit for system root is not instantiated */
806
        if (path_equal(where, "/"))
×
807
                return generator_add_symlink(dir,  SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_ROOT_SERVICE);
×
808

809
        r = unit_name_path_escape(where, &instance);
×
810
        if (r < 0)
×
811
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
812

813
        if (target) {
×
814
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTAON_SERVICE, instance);
×
815
                if (r < 0)
×
816
                        return r;
817
        }
818

819
        r = unit_name_from_path(where, ".mount", &where_unit);
×
820
        if (r < 0)
×
821
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
822

823
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_SERVICE, instance);
×
824
}
825

826
int generator_enable_remount_fs_service(const char *dir) {
2✔
827
        /* Pull in systemd-remount-fs.service */
828
        return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants",
2✔
829
                                     SYSTEM_DATA_UNIT_DIR "/" SPECIAL_REMOUNT_FS_SERVICE);
830
}
831

832
int generator_write_blockdev_dependency(FILE *f, const char *what) {
24✔
833
        _cleanup_free_ char *escaped = NULL;
24✔
834
        int r;
24✔
835

836
        assert(f);
24✔
837
        assert(what);
24✔
838

839
        if (!path_startswith(what, "/dev/"))
24✔
840
                return 0;
841

842
        r = unit_name_path_escape(what, &escaped);
16✔
843
        if (r < 0)
16✔
844
                return log_error_errno(r, "Failed to escape device node path %s: %m", what);
×
845

846
        fprintf(f,
16✔
847
                "After=blockdev@%s.target\n",
848
                escaped);
849

850
        return 0;
851
}
852

853
int generator_write_cryptsetup_unit_section(FILE *f, const char *source) {
22✔
854
        assert(f);
22✔
855

856
        fprintf(f,
22✔
857
                "[Unit]\n"
858
                "Description=Cryptography Setup for %%I\n"
859
                "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n");
860

861
        if (source)
22✔
862
                fprintf(f, "SourcePath=%s\n", source);
22✔
863

864
        fprintf(f,
22✔
865
                "\n"
866
                "DefaultDependencies=no\n"
867
                "After=cryptsetup-pre.target systemd-udevd-kernel.socket systemd-tpm2-setup-early.service\n"
868
                "Before=blockdev@dev-mapper-%%i.target\n"
869
                "Wants=blockdev@dev-mapper-%%i.target\n"
870
                "IgnoreOnIsolate=true\n");
871

872
        return 0;
22✔
873
}
874

875
int generator_write_cryptsetup_service_section(
22✔
876
                FILE *f,
877
                const char *name,
878
                const char *what,
879
                const char *key_file,
880
                const char *options) {
881

882
        _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *key_file_escaped = NULL, *options_escaped = NULL;
22✔
883

884
        assert(f);
22✔
885
        assert(name);
22✔
886
        assert(what);
22✔
887

888
        name_escaped = specifier_escape(name);
22✔
889
        if (!name_escaped)
22✔
890
                return log_oom();
×
891

892
        what_escaped = specifier_escape(what);
22✔
893
        if (!what_escaped)
22✔
894
                return log_oom();
×
895

896
        if (key_file) {
22✔
897
                key_file_escaped = specifier_escape(key_file);
22✔
898
                if (!key_file_escaped)
22✔
899
                        return log_oom();
×
900
        }
901

902
        if (options) {
22✔
903
                options_escaped = specifier_escape(options);
22✔
904
                if (!options_escaped)
22✔
905
                        return log_oom();
×
906
        }
907

908
        fprintf(f,
22✔
909
                "\n"
910
                "[Service]\n"
911
                "Type=oneshot\n"
912
                "RemainAfterExit=yes\n"
913
                "TimeoutSec=infinity\n"   /* The binary handles timeouts on its own */
914
                "KeyringMode=shared\n"    /* Make sure we can share cached keys among instances */
915
                "OOMScoreAdjust=500\n"    /* Unlocking can allocate a lot of memory if Argon2 is used */
916
                "ImportCredential=cryptsetup.*\n"
917
                "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
918
                "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
919
                name_escaped, what_escaped, strempty(key_file_escaped), strempty(options_escaped),
920
                name_escaped);
921

922
        return 0;
923
}
924

925
int generator_write_veritysetup_unit_section(FILE *f, const char *source) {
×
926
        assert(f);
×
927

928
        fprintf(f,
×
929
                "[Unit]\n"
930
                "Description=Integrity Protection Setup for %%I\n"
931
                "Documentation=man:veritytab(5) man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n");
932

933
        if (source)
×
934
                fprintf(f, "SourcePath=%s\n", source);
×
935

936
        fprintf(f,
×
937
                "DefaultDependencies=no\n"
938
                "IgnoreOnIsolate=true\n"
939
                "After=veritysetup-pre.target systemd-udevd-kernel.socket\n"
940
                "Before=blockdev@dev-mapper-%%i.target\n"
941
                "Wants=blockdev@dev-mapper-%%i.target\n");
942

943
        return 0;
×
944
}
945

946
int generator_write_veritysetup_service_section(
×
947
                FILE *f,
948
                const char *name,
949
                const char *data_what,
950
                const char *hash_what,
951
                const char *roothash,
952
                const char *options) {
953

954
        _cleanup_free_ char *name_escaped = NULL, *data_what_escaped = NULL, *hash_what_escaped = NULL,
×
955
                            *roothash_escaped = NULL, *options_escaped = NULL;
×
956

957
        assert(f);
×
958
        assert(name);
×
959
        assert(data_what);
×
960
        assert(hash_what);
×
961

962
        name_escaped = specifier_escape(name);
×
963
        if (!name_escaped)
×
964
                return log_oom();
×
965

966
        data_what_escaped = specifier_escape(data_what);
×
967
        if (!data_what_escaped)
×
968
                return log_oom();
×
969

970
        hash_what_escaped = specifier_escape(hash_what);
×
971
        if (!hash_what_escaped)
×
972
                return log_oom();
×
973

974
        roothash_escaped = specifier_escape(roothash);
×
975
        if (!roothash_escaped)
×
976
                return log_oom();
×
977

978
        if (options) {
×
979
                options_escaped = specifier_escape(options);
×
980
                if (!options_escaped)
×
981
                        return log_oom();
×
982
        }
983

984
        fprintf(f,
×
985
                "\n"
986
                "[Service]\n"
987
                "Type=oneshot\n"
988
                "RemainAfterExit=yes\n"
989
                "ExecStart=" SYSTEMD_VERITYSETUP_PATH " attach '%s' '%s' '%s' '%s' '%s'\n"
990
                "ExecStop=" SYSTEMD_VERITYSETUP_PATH " detach '%s'\n",
991
                name_escaped, data_what_escaped, hash_what_escaped, roothash_escaped, strempty(options_escaped),
992
                name_escaped);
993

994
        return 0;
995
}
996

997
void log_setup_generator(void) {
207✔
998
        if (invoked_by_systemd()) {
207✔
999
                /* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
1000
                if (cg_pid_get_owner_uid(0, NULL) == -ENXIO /* not running in a per-user slice */)
140✔
1001
                        log_set_prohibit_ipc(true);
1✔
1002

1003
                /* This effectively means: journal for per-user generators, kmsg otherwise */
1004
                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
140✔
1005
        } else
1006
                log_set_target(LOG_TARGET_AUTO);
67✔
1007

1008
        log_parse_environment();
207✔
1009
        log_open();
207✔
1010
}
207✔
1011

1012
bool generator_soft_rebooted(void) {
×
1013
        static int cached = -1;
×
1014
        int r;
×
1015

1016
        if (cached >= 0)
×
1017
                return cached;
×
1018

1019
        const char *e = secure_getenv("SYSTEMD_SOFT_REBOOTS_COUNT");
×
1020
        if (!e)
×
1021
                return (cached = false);
×
1022

1023
        unsigned u;
×
1024

1025
        r = safe_atou(e, &u);
×
1026
        if (r < 0) {
×
1027
                log_debug_errno(r, "Failed to parse $SYSTEMD_SOFT_REBOOTS_COUNT, assuming the system hasn't soft-rebooted: %m");
×
1028
                return (cached = false);
×
1029
        }
1030

1031
        return (cached = (u > 0));
×
1032
}
1033

1034
GptAutoRoot parse_gpt_auto_root(const char *value) {
17✔
1035
        assert(value);
17✔
1036

1037
        /* Parses the 'gpt-auto'/'gpt-auto-root' parameters to root= */
1038

1039
        if (streq(value, "gpt-auto")) {
17✔
1040
                log_debug("Enabling root partition auto-detection (respecting factory reset mode), root= is explicitly set to 'gpt-auto'.");
×
1041
                return GPT_AUTO_ROOT_ON;
×
1042
        }
1043

1044
        if (streq(value, "gpt-auto-force")) {
17✔
1045
                log_debug("Enabling root partition auto-detection (ignoring factory reset mode), root= is explicitly set to 'gpt-auto-force'.");
×
1046
                return GPT_AUTO_ROOT_FORCE;
×
1047
        }
1048

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