Coverage Report

Created: 2026-10-28 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/shared/generator.c
Line
Count
Source
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 "dropin.h"
10
#include "escape.h"
11
#include "fd-util.h"
12
#include "fileio.h"
13
#include "fstab-util.h"
14
#include "generator.h"
15
#include "initrd-util.h"
16
#include "log.h"
17
#include "mkdir.h"
18
#include "mountpoint-util.h"
19
#include "parse-util.h"
20
#include "path-util.h"
21
#include "special.h"
22
#include "specifier.h"
23
#include "string-util.h"
24
#include "strv.h"
25
#include "time-util.h"
26
#include "tmpfile-util.h"
27
#include "unit-name.h"
28
#include "verbs.h"
29
30
784
static int symlink_unless_exists(const char *target, const char *linkpath) {
31
784
        (void) mkdir_parents(linkpath, 0755);
32
33
784
        if (symlink(target, linkpath) < 0 && errno != EEXIST)
34
784
                return log_error_errno(errno, "Failed to create symlink %s: %m", linkpath);
35
784
        return 0;
36
784
}
37
38
int generator_open_unit_file_full(
39
                const char *dir,
40
                const char *source,
41
                const char *filename,
42
                FILE **ret_file,
43
                char **ret_final_path,
44
784
                char **ret_temp_path) {
45
46
784
        _cleanup_free_ char *p = NULL;
47
784
        FILE *f;
48
784
        int r;
49
50
784
        assert(dir);
51
784
        assert(ret_file);
52
53
        /* If <ret_temp_path> is specified, it creates a temporary unit file and also returns its
54
         * temporary path. */
55
56
784
        if (ret_temp_path) {
57
0
                r = fopen_temporary(dir, &f, &p);
58
0
                if (r < 0)
59
0
                        return log_error_errno(r, "Failed to create temporary unit file in '%s': %m", dir);
60
61
0
                (void) fchmod(fileno(f), 0644);
62
63
0
                *ret_temp_path = TAKE_PTR(p);
64
784
        } else {
65
784
                assert(filename);
66
67
784
                p = path_join(dir, filename);
68
784
                if (!p)
69
0
                        return log_oom();
70
71
784
                r = fopen_unlocked(p, "wxe", &f);
72
784
                if (r < 0) {
73
0
                        if (source && r == -EEXIST)
74
0
                                return log_error_errno(r,
75
0
                                                       "Failed to create unit file '%s', as it already exists. Duplicate entry in '%s'?",
76
0
                                                       p, source);
77
78
0
                        return log_error_errno(r, "Failed to create unit file '%s': %m", p);
79
0
                }
80
784
        }
81
82
784
        fprintf(f,
83
784
                "# Automatically generated by %s\n\n",
84
784
                program_invocation_short_name);
85
86
784
        *ret_file = f;
87
88
784
        if (ret_final_path)
89
0
                *ret_final_path = TAKE_PTR(p);
90
91
784
        return 0;
92
784
}
93
94
int generator_add_symlink_full(
95
                const char *dir,
96
                const char *dst,
97
                const char *dep_type,
98
                const char *src,
99
784
                const char *instance) {
100
101
784
        _cleanup_free_ char *dn = NULL, *fn = NULL, *instantiated = NULL, *to = NULL, *from = NULL;
102
784
        int r;
103
104
784
        assert(dir);
105
784
        assert(dst);
106
784
        assert(src);
107
108
        /* If 'dep_type' is specified adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise).
109
         *
110
         * If 'dep_type' is NULL, it will create a symlink to <src> (i.e. create an alias.
111
         *
112
         * If <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
113
114
784
        r = path_split_prefix_filename(src, &dn, &fn);
115
784
        if (r < 0)
116
784
                return log_error_errno(r, "Failed to split '%s' into directory prefix and filename: %m", src);
117
784
        if (r == O_DIRECTORY)
118
784
                return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src);
119
120
784
        if (instance) {
121
0
                r = unit_name_replace_instance(fn, instance, &instantiated);
122
0
                if (r < 0)
123
0
                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance);
124
0
        }
125
126
784
        if (dep_type) { /* Create a .wants/ style dep */
127
784
                from = path_join(dn ?: "..", fn);
128
784
                if (!from)
129
0
                        return log_oom();
130
131
784
                to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn);
132
784
        } else { /* or create an alias */
133
0
                from = dn ? path_join(dn, fn) : strdup(fn);
134
0
                if (!from)
135
0
                        return log_oom();
136
137
0
                to = strjoin(dir, "/", dst);
138
0
        }
139
784
        if (!to)
140
0
                return log_oom();
141
142
784
        return symlink_unless_exists(from, to);
143
784
}
144
145
static int generator_add_ordering(
146
                const char *dir,
147
                const char *src,
148
                const char *order,
149
                const char *dst,
150
0
                const char *instance) {
151
152
0
        _cleanup_free_ char *instantiated = NULL, *p = NULL, *fn = NULL;
153
0
        _cleanup_fclose_ FILE *f = NULL;
154
0
        const char *to;
155
0
        int r;
156
157
0
        assert(dir);
158
0
        assert(src);
159
0
        assert(order);
160
0
        assert(dst);
161
162
        /* Adds an explicit ordering dependency of type <order> from <src> to <dst>. If <instance> is
163
         * specified, it is inserted into <dst>. */
164
165
0
        if (instance) {
166
0
                r = unit_name_replace_instance(dst, instance, &instantiated);
167
0
                if (r < 0)
168
0
                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", dst, instance);
169
170
0
                to = instantiated;
171
0
        } else
172
0
                to = dst;
173
174
0
        fn = strjoin(src, ".d/50-order-", to, ".conf");
175
0
        if (!fn)
176
0
                return log_oom();
177
178
0
        p = path_join(dir, fn);
179
0
        if (!p)
180
0
                return log_oom();
181
182
0
        (void) mkdir_parents_label(p, 0755);
183
184
0
        r = fopen_unlocked(p, "wxe", &f);
185
0
        if (r < 0)
186
0
                return log_error_errno(r, "Failed to create '%s': %m", p);
187
188
0
        fprintf(f,
189
0
                "# Automatically generated by %s\n\n"
190
0
                "[Unit]\n"
191
0
                "%s=%s\n",
192
0
                program_invocation_short_name,
193
0
                order,
194
0
                to);
195
196
0
        r = fflush_and_check(f);
197
0
        if (r < 0)
198
0
                return log_error_errno(r, "Failed to write drop-in '%s': %m", p);
199
200
0
        return 0;
201
0
}
202
203
static int write_fsck_sysroot_service(
204
                const char *unit, /* Either SPECIAL_FSCK_ROOT_SERVICE or SPECIAL_FSCK_USR_SERVICE */
205
                const char *dir,
206
                const char *what,
207
0
                const char *extra_after) {
208
209
0
        _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
210
0
        _cleanup_fclose_ FILE *f = NULL;
211
0
        int r;
212
213
0
        assert(unit);
214
0
        assert(dir);
215
0
        assert(what);
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
0
        escaped = specifier_escape(what);
223
0
        if (!escaped)
224
0
                return log_oom();
225
226
0
        escaped2 = cescape(escaped);
227
0
        if (!escaped2)
228
0
                return log_oom();
229
230
0
        r = unit_name_from_path(what, ".device", &device);
231
0
        if (r < 0)
232
0
                return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
233
234
0
        r = generator_open_unit_file(dir, /* source= */ NULL, unit, &f);
235
0
        if (r < 0)
236
0
                return r;
237
238
0
        fprintf(f,
239
0
                "[Unit]\n"
240
0
                "Description=File System Check on %1$s\n"
241
0
                "Documentation=man:%2$s(8)\n"
242
0
                "\n"
243
0
                "DefaultDependencies=no\n"
244
0
                "BindsTo=%3$s\n"
245
0
                "Conflicts=shutdown.target\n"
246
0
                "After=%4$s%5$slocal-fs-pre.target %3$s\n"
247
0
                "Before=shutdown.target\n"
248
0
                "\n"
249
0
                "[Service]\n"
250
0
                "Type=oneshot\n"
251
0
                "RemainAfterExit=yes\n"
252
0
                "ExecStart=" SYSTEMD_FSCK_PATH " %6$s\n"
253
0
                "TimeoutSec=infinity\n"
254
0
                "ImportCredential=fsck.*\n",
255
0
                escaped,
256
0
                unit,
257
0
                device,
258
0
                strempty(extra_after),
259
0
                isempty(extra_after) ? "" : " ",
260
0
                escaped2);
261
262
0
        r = fflush_and_check(f);
263
0
        if (r < 0)
264
0
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
265
266
0
        return 0;
267
0
}
268
269
int generator_write_fsck_deps(
270
                FILE *f,
271
                const char *dir,
272
                const char *what,
273
                const char *where,
274
                const char *fstype,
275
0
                const char *options) {
276
277
0
        int r;
278
279
0
        assert(f);
280
0
        assert(dir);
281
0
        assert(what);
282
0
        assert(where);
283
284
        /* Let's do an early exit if we are invoked for the root and /usr/ trees in the initrd, to avoid
285
         * generating confusing log messages. */
286
0
        if (in_initrd() && PATH_IN_SET(where, "/", "/usr")) {
287
0
                log_debug("Skipping fsck for %s in initrd.", where);
288
0
                return 0;
289
0
        }
290
291
0
        if (fstype) {
292
0
                if (!fstype_is_blockdev_backed(fstype)) {
293
0
                        log_debug("Skipping file system check for non-block based file system '%s'.", what);
294
0
                        return 0;
295
0
                }
296
297
0
                if (fstype_is_ro(fstype)) {
298
0
                        log_debug("Skipping file system check for read-only file system '%s'.", what);
299
0
                        return 0;
300
0
                }
301
0
        }
302
303
0
        if (fstab_test_option(options, "bind\0rbind\0")) {
304
0
                log_debug("Skipping file system check for bind mount of '%s'.", what);
305
0
                return 0;
306
0
        }
307
308
0
        if (!is_device_path(what)) {
309
0
                log_debug("Checking was requested for \"%s\", but it is not a device.", what);
310
0
                return 0;
311
0
        }
312
313
0
        if (!isempty(fstype) && !streq(fstype, "auto")) {
314
0
                r = fsck_exists_for_fstype(fstype);
315
0
                if (r < 0)
316
0
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
317
0
                else if (r == 0) {
318
                        /* treat missing check as essentially OK */
319
0
                        log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
320
0
                        return 0;
321
0
                }
322
0
        } else {
323
0
                r = fsck_exists();
324
0
                if (r < 0)
325
0
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if the fsck command may be used, proceeding: %m", what);
326
0
                else if (r == 0) {
327
                        /* treat missing fsck as essentially OK */
328
0
                        log_debug("Checking was requested for %s, but the fsck command does not exist.", what);
329
0
                        return 0;
330
0
                }
331
0
        }
332
333
0
        if (path_equal(where, "/")) {
334
                /* We support running the fsck instance for the root fs while it is already mounted, for
335
                 * compatibility with non-initrd boots. It's ugly, but it is how it is. Since – unlike for
336
                 * regular file systems – this means the ordering is reversed (i.e. mount *before* fsck) we
337
                 * have a separate fsck unit for this, independent of systemd-fsck@.service. */
338
339
0
                const char *lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
340
341
0
                r = symlink_unless_exists(SYSTEM_DATA_UNIT_DIR "/" SPECIAL_FSCK_ROOT_SERVICE, lnk);
342
0
                if (r < 0)
343
0
                        return r;
344
0
        } else {
345
0
                _cleanup_free_ char *_fsck = NULL;
346
0
                const char *fsck, *dep;
347
348
0
                if (in_initrd() && path_equal(where, "/sysroot")) {
349
0
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_ROOT_SERVICE, dir, what, SPECIAL_INITRD_ROOT_DEVICE_TARGET);
350
0
                        if (r < 0)
351
0
                                return r;
352
353
0
                        fsck = SPECIAL_FSCK_ROOT_SERVICE;
354
0
                        dep = "Requires";
355
356
0
                } else if (in_initrd() && path_equal(where, "/sysusr/usr")) {
357
0
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_USR_SERVICE, dir, what, NULL);
358
0
                        if (r < 0)
359
0
                                return r;
360
361
0
                        fsck = SPECIAL_FSCK_USR_SERVICE;
362
0
                        dep = "Requires";
363
0
                } else {
364
                        /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
365
                         * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
366
                         * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
367
                         * we'd have to unmount /usr too.  */
368
369
0
                        dep = path_equal(where, "/usr") ? "Wants" : "Requires";
370
371
0
                        r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
372
0
                        if (r < 0)
373
0
                                return log_error_errno(r, "Failed to create fsck service name: %m");
374
375
0
                        fsck = _fsck;
376
0
                }
377
378
0
                fprintf(f,
379
0
                        "%1$s=%2$s\n"
380
0
                        "After=%2$s\n",
381
0
                        dep, fsck);
382
0
        }
383
384
0
        return 0;
385
0
}
386
387
int generator_write_device_timeout(
388
                const char *dir,
389
                const char *what,
390
                const char *opts,
391
0
                char **filtered) {
392
393
        /* Configure how long we wait for a device that backs a mount point or a
394
         * swap partition to show up. This is useful to support endless device timeouts
395
         * for devices that show up only after user input, like crypto devices. */
396
397
0
        _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
398
0
        usec_t u;
399
0
        int r;
400
401
0
        assert(dir);
402
0
        assert(what);
403
404
0
        r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
405
0
                                       "x-systemd.device-timeout\0",
406
0
                                 NULL, &timeout, NULL, filtered);
407
0
        if (r < 0) {
408
0
                log_warning_errno(r, "Failed to parse fstab options, ignoring: %m");
409
0
                return 0;
410
0
        }
411
0
        if (r == 0)
412
0
                return 0;
413
414
0
        r = parse_sec_fix_0(timeout, &u);
415
0
        if (r < 0) {
416
0
                log_warning("Failed to parse timeout for device '%s', ignoring: %s", what, timeout);
417
0
                return 0;
418
0
        }
419
420
0
        node = fstab_node_to_udev_node(what);
421
0
        if (!node)
422
0
                return log_oom();
423
0
        if (!is_device_path(node)) {
424
0
                log_warning("'%s' is not a device path, ignoring x-systemd.device-timeout= option.", node);
425
0
                return 0;
426
0
        }
427
428
0
        r = unit_name_from_path(node, ".device", &unit);
429
0
        if (r < 0)
430
0
                return log_error_errno(r, "Failed to make unit name from device path '%s': %m", node);
431
432
0
        r = write_drop_in_format(dir, unit, 50, "device-timeout",
433
0
                                 "# Automatically generated by %s\n"
434
0
                                 "# from supplied options \"%s\"\n\n"
435
0
                                 "[Unit]\n"
436
0
                                 "JobRunningTimeoutSec=%s",
437
0
                                 program_invocation_short_name,
438
0
                                 opts,
439
0
                                 timeout);
440
0
        if (r < 0)
441
0
                return r;
442
443
0
        return 1;
444
0
}
445
446
int generator_write_unit_timeout(
447
                FILE *f,
448
                const char *where,
449
                const char *opts,
450
                const char *filter,
451
0
                const char *unit_setting) {
452
453
0
        _cleanup_free_ char *timeout = NULL;
454
0
        usec_t u;
455
0
        int r;
456
457
0
        assert(f);
458
0
        assert(where);
459
0
        assert(filter);
460
0
        assert(unit_setting);
461
462
0
        r = fstab_filter_options(opts, filter, NULL, &timeout, NULL, NULL);
463
0
        if (r < 0)
464
0
                return log_error_errno(r, "Failed to parse options for '%s': %m", where);
465
0
        if (r == 0)
466
0
                return 0;
467
468
0
        r = parse_sec_fix_0(timeout, &u);
469
0
        if (r < 0) {
470
0
                log_warning_errno(r, "Failed to parse timeout '%s' for '%s', ignoring: %m", timeout, where);
471
0
                return 0;
472
0
        }
473
474
0
        fprintf(f, "%s=%s\n", unit_setting, FORMAT_TIMESPAN(u, 0));
475
476
0
        return 0;
477
0
}
478
479
int generator_write_network_device_deps(
480
                const char *dir,
481
                const char *what,
482
                const char *where,
483
0
                const char *opts) {
484
485
        /* fstab records that specify _netdev option should apply the network
486
         * ordering on the actual device depending on network connection. If we
487
         * are not mounting real device (NFS, CIFS), we rely on _netdev effect
488
         * on the mount unit itself. */
489
490
0
        _cleanup_free_ char *node = NULL, *unit = NULL;
491
0
        int r;
492
493
0
        assert(dir);
494
0
        assert(what);
495
496
0
        if (where && fstab_is_extrinsic(where, opts))
497
0
                return 0;
498
499
0
        if (!fstab_test_option(opts, "_netdev\0"))
500
0
                return 0;
501
502
0
        node = fstab_node_to_udev_node(what);
503
0
        if (!node)
504
0
                return log_oom();
505
506
        /* Nothing to apply dependencies to. */
507
0
        if (!is_device_path(node))
508
0
                return 0;
509
510
0
        r = unit_name_from_path(node, ".device", &unit);
511
0
        if (r < 0)
512
0
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", node);
513
514
        /* See mount_add_default_dependencies for explanation why we create such
515
         * dependencies. */
516
0
        return write_drop_in_format(dir, unit, 50, "netdev-dependencies",
517
0
                                    "# Automatically generated by %s\n\n"
518
0
                                    "[Unit]\n"
519
0
                                    "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n"
520
0
                                    "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n",
521
0
                                    program_invocation_short_name);
522
0
}
523
524
0
int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
525
0
        _cleanup_free_ char *unit = NULL;
526
0
        int r;
527
528
0
        r = unit_name_from_path(what, ".device", &unit);
529
0
        if (r < 0)
530
0
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
531
0
                                       what);
532
533
0
        return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
534
0
                                    "# Automatically generated by %s\n\n"
535
0
                                    "[Unit]\n"
536
0
                                    "Requires=%s\n"
537
0
                                    "After=%s",
538
0
                                    program_invocation_short_name,
539
0
                                    unit,
540
0
                                    unit);
541
0
}
542
543
int generator_hook_up_mkswap(
544
                const char *dir,
545
0
                const char *what) {
546
547
0
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
548
0
        _cleanup_fclose_ FILE *f = NULL;
549
0
        int r;
550
551
0
        assert(dir);
552
0
        assert(what);
553
554
0
        node = fstab_node_to_udev_node(what);
555
0
        if (!node)
556
0
                return log_oom();
557
558
        /* Nothing to work on. */
559
0
        if (!is_device_path(node))
560
0
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
561
0
                                       "Cannot format something that is not a device node: %s",
562
0
                                       node);
563
564
0
        r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
565
0
        if (r < 0)
566
0
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
567
0
                                       node);
568
569
0
        escaped = cescape(node);
570
0
        if (!escaped)
571
0
                return log_oom();
572
573
0
        r = unit_name_from_path(what, ".swap", &where_unit);
574
0
        if (r < 0)
575
0
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
576
0
                                       what);
577
578
0
        r = generator_open_unit_file(dir, /* source= */ NULL, unit, &f);
579
0
        if (r < 0)
580
0
                return r;
581
582
0
        fprintf(f,
583
0
                "[Unit]\n"
584
0
                "Description=Make Swap on %%f\n"
585
0
                "Documentation=man:systemd-mkswap@.service(8)\n"
586
0
                "\n"
587
0
                "DefaultDependencies=no\n"
588
0
                "BindsTo=%%i.device\n"
589
0
                "After=%%i.device\n"
590
0
                "Before=%s\n"
591
0
                "Conflicts=shutdown.target\n"
592
0
                "Before=shutdown.target\n"
593
0
                "\n"
594
0
                "[Service]\n"
595
0
                "Type=oneshot\n"
596
0
                "RemainAfterExit=yes\n"
597
0
                "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
598
0
                "TimeoutSec=infinity\n",
599
0
                where_unit,
600
0
                escaped);
601
602
0
        r = fflush_and_check(f);
603
0
        if (r < 0)
604
0
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
605
606
0
        return generator_add_symlink(dir, where_unit, "requires", unit);
607
0
}
608
609
int generator_hook_up_mkfs(
610
                const char *dir,
611
                const char *what,
612
                const char *where,
613
0
                const char *type) {
614
615
0
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
616
0
        _cleanup_fclose_ FILE *f = NULL;
617
0
        const char *fsck_unit;
618
0
        int r;
619
620
0
        assert(dir);
621
0
        assert(what);
622
0
        assert(where);
623
624
0
        node = fstab_node_to_udev_node(what);
625
0
        if (!node)
626
0
                return log_oom();
627
628
        /* Nothing to work on. */
629
0
        if (!is_device_path(node))
630
0
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
631
0
                                       "Cannot format something that is not a device node: %s",
632
0
                                       node);
633
634
0
        if (!type || streq(type, "auto"))
635
0
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
636
0
                                       "Cannot format partition %s, filesystem type is not specified",
637
0
                                       node);
638
639
0
        r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit);
640
0
        if (r < 0)
641
0
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
642
0
                                       node);
643
644
0
        if (in_initrd() && path_equal(where, "/sysroot"))
645
0
                fsck_unit = SPECIAL_FSCK_ROOT_SERVICE;
646
0
        else if (in_initrd() && path_equal(where, "/sysusr/usr"))
647
0
                fsck_unit = SPECIAL_FSCK_USR_SERVICE;
648
0
        else
649
0
                fsck_unit = "systemd-fsck@%i.service";
650
651
0
        escaped = cescape(node);
652
0
        if (!escaped)
653
0
                return log_oom();
654
655
0
        r = unit_name_from_path(where, ".mount", &where_unit);
656
0
        if (r < 0)
657
0
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
658
0
                                       where);
659
660
0
        r = generator_open_unit_file(dir, /* source= */ NULL, unit, &f);
661
0
        if (r < 0)
662
0
                return r;
663
664
0
        fprintf(f,
665
0
                "[Unit]\n"
666
0
                "Description=Make File System on %%f\n"
667
0
                "Documentation=man:systemd-makefs@.service(8)\n"
668
0
                "\n"
669
0
                "DefaultDependencies=no\n"
670
0
                "BindsTo=%%i.device\n"
671
0
                "After=%%i.device\n"
672
                /* fsck might or might not be used, so let's be safe and order
673
                 * ourselves before both systemd-fsck@.service and the mount unit. */
674
0
                "Before=%s %s\n"
675
0
                "Conflicts=shutdown.target\n"
676
0
                "Before=shutdown.target\n"
677
0
                "\n"
678
0
                "[Service]\n"
679
0
                "Type=oneshot\n"
680
0
                "RemainAfterExit=yes\n"
681
0
                "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
682
0
                "TimeoutSec=infinity\n",
683
0
                fsck_unit,
684
0
                where_unit,
685
0
                type,
686
0
                escaped);
687
        // XXX: what about local-fs-pre.target?
688
689
0
        r = fflush_and_check(f);
690
0
        if (r < 0)
691
0
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
692
693
0
        return generator_add_symlink(dir, where_unit, "requires", unit);
694
0
}
695
696
int generator_hook_up_growfs(
697
                const char *dir,
698
                const char *where,
699
0
                const char *target) {
700
701
0
        const char *growfs_unit, *growfs_unit_path;
702
0
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
703
0
        int r;
704
705
0
        assert(dir);
706
0
        assert(where);
707
708
0
        r = unit_name_from_path(where, ".mount", &where_unit);
709
0
        if (r < 0)
710
0
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
711
712
0
        if (empty_or_root(where)) {
713
0
                growfs_unit = SPECIAL_GROWFS_ROOT_SERVICE;
714
0
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_ROOT_SERVICE;
715
0
        } else {
716
0
                growfs_unit = SPECIAL_GROWFS_SERVICE;
717
0
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_SERVICE;
718
719
0
                r = unit_name_path_escape(where, &instance);
720
0
                if (r < 0)
721
0
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
722
0
        }
723
724
0
        if (target) {
725
0
                r = generator_add_ordering(dir, target, "After", growfs_unit, instance);
726
0
                if (r < 0)
727
0
                        return r;
728
0
        }
729
730
0
        return generator_add_symlink_full(dir, where_unit, "wants", growfs_unit_path, instance);
731
0
}
732
733
int generator_hook_up_pcrfs(
734
                const char *dir,
735
                const char *where,
736
0
                const char *target) {
737
738
0
        const char *pcrfs_unit, *pcrfs_unit_path;
739
0
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
740
0
        int r;
741
742
0
        assert(dir);
743
0
        assert(where);
744
745
0
        r = unit_name_from_path(where, ".mount", &where_unit);
746
0
        if (r < 0)
747
0
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
748
749
0
        if (empty_or_root(where)) {
750
0
                pcrfs_unit = SPECIAL_PCRFS_ROOT_SERVICE;
751
0
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_ROOT_SERVICE;
752
0
        } else {
753
0
                pcrfs_unit = SPECIAL_PCRFS_SERVICE;
754
0
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_SERVICE;
755
756
0
                r = unit_name_path_escape(where, &instance);
757
0
                if (r < 0)
758
0
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
759
0
        }
760
761
0
        if (target) {
762
0
                r = generator_add_ordering(dir, target, "After", pcrfs_unit, instance);
763
0
                if (r < 0)
764
0
                        return r;
765
0
        }
766
767
0
        return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
768
0
}
769
770
int generator_hook_up_validatefs(
771
                const char *dir,
772
                const char *where,
773
0
                const char *target) {
774
775
0
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
776
0
        const char *validatefs_unit, *validatefs_unit_path;
777
0
        int r;
778
779
0
        assert(dir);
780
0
        assert(where);
781
782
        /* never hook this in for the actual root fs, because it's too late then, we already are running from
783
         * the root fs, it makes no sense to validate it anymore */
784
0
        if (empty_or_root(where))
785
0
                return 0;
786
787
0
        r = unit_name_from_path(where, ".mount", &where_unit);
788
0
        if (r < 0)
789
0
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
790
791
0
        validatefs_unit = SPECIAL_VALIDATEFS_SERVICE;
792
0
        validatefs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_VALIDATEFS_SERVICE;
793
794
0
        r = unit_name_path_escape(where, &instance);
795
0
        if (r < 0)
796
0
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
797
798
0
        if (target) {
799
0
                r = generator_add_ordering(dir, target, "After", validatefs_unit, instance);
800
0
                if (r < 0)
801
0
                        return r;
802
0
        }
803
804
0
        return generator_add_symlink_full(dir, where_unit, "wants", validatefs_unit_path, instance);
805
0
}
806
807
int generator_hook_up_quotacheck(
808
                const char *dir,
809
                const char *what,
810
                const char *where,
811
                const char *target,
812
0
                const char *fstype) {
813
814
0
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
815
0
        int r;
816
817
0
        assert(dir);
818
0
        assert(where);
819
820
0
        if (isempty(fstype) || streq(fstype, "auto"))
821
0
                return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Couldn't determine filesystem type for %s, quota cannot be activated", what);
822
0
        if (fstype_has_internal_quota(fstype)) {
823
0
                log_debug("%s handles quotas internally, skipping quotacheck/quotaon setup for %s", fstype, what);
824
0
                return 0;
825
0
        }
826
0
        if (!fstype_needs_quota(fstype))
827
0
                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
0
        if (path_equal(where, "/")) {
831
0
                r = generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_ROOT_SERVICE);
832
0
                return r < 0 ? r : 1;
833
0
        }
834
835
0
        r = unit_name_path_escape(where, &instance);
836
0
        if (r < 0)
837
0
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
838
839
0
        if (target) {
840
0
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTACHECK_SERVICE, instance);
841
0
                if (r < 0)
842
0
                        return r;
843
0
        }
844
845
0
        r = unit_name_from_path(where, ".mount", &where_unit);
846
0
        if (r < 0)
847
0
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
848
849
0
        r = generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_SERVICE, instance);
850
0
        return r < 0 ? r : 1;
851
0
}
852
853
int generator_hook_up_quotaon(
854
                const char *dir,
855
                const char *where,
856
0
                const char *target) {
857
858
0
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
859
0
        int r;
860
861
0
        assert(dir);
862
0
        assert(where);
863
864
        /* quotaon unit for system root is not instantiated */
865
0
        if (path_equal(where, "/"))
866
0
                return generator_add_symlink(dir,  SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_ROOT_SERVICE);
867
868
0
        r = unit_name_path_escape(where, &instance);
869
0
        if (r < 0)
870
0
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
871
872
0
        if (target) {
873
0
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTAON_SERVICE, instance);
874
0
                if (r < 0)
875
0
                        return r;
876
0
        }
877
878
0
        r = unit_name_from_path(where, ".mount", &where_unit);
879
0
        if (r < 0)
880
0
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
881
882
0
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_SERVICE, instance);
883
0
}
884
885
0
int generator_enable_remount_fs_service(const char *dir) {
886
        /* Pull in systemd-remount-fs.service */
887
0
        return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants",
888
0
                                     SYSTEM_DATA_UNIT_DIR "/" SPECIAL_REMOUNT_FS_SERVICE);
889
0
}
890
891
0
int generator_write_blockdev_dependency(FILE *f, const char *what) {
892
0
        _cleanup_free_ char *escaped = NULL;
893
0
        int r;
894
895
0
        assert(f);
896
0
        assert(what);
897
898
0
        if (!path_startswith(what, "/dev/"))
899
0
                return 0;
900
901
0
        r = unit_name_path_escape(what, &escaped);
902
0
        if (r < 0)
903
0
                return log_error_errno(r, "Failed to escape device node path %s: %m", what);
904
905
0
        fprintf(f,
906
0
                "After=blockdev@%s.target\n",
907
0
                escaped);
908
909
0
        return 0;
910
0
}
911
912
0
int generator_write_cryptsetup_unit_section(FILE *f, const char *source) {
913
0
        assert(f);
914
915
0
        fprintf(f,
916
0
                "[Unit]\n"
917
0
                "Description=Cryptography Setup for %%I\n"
918
0
                "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n");
919
920
0
        if (source)
921
0
                fprintf(f, "SourcePath=%s\n", source);
922
923
0
        fprintf(f,
924
0
                "\n"
925
0
                "DefaultDependencies=no\n"
926
                /* The ordering against systemd-udevd.service is mostly about shutdown, not startup: on stop
927
                 * the device is detached via a device-mapper ioctl that carries a udev cookie, and we block in
928
                 * dm_udev_wait() until udev releases it (see 95-dm-notify.rules). Ordering After= it means we
929
                 * are stopped first, while udevd is still around to service that cookie; otherwise during
930
                 * soft-reboot/shutdown udevd may exit first and the detach hangs until the job timeout. */
931
0
                "After=cryptsetup-pre.target systemd-udevd-kernel.socket systemd-udevd.service systemd-tpm2-setup-early.service\n"
932
0
                "Before=blockdev@dev-mapper-%%i.target\n"
933
0
                "Wants=blockdev@dev-mapper-%%i.target\n"
934
0
                "IgnoreOnIsolate=true\n");
935
936
0
        return 0;
937
0
}
938
939
int generator_write_cryptsetup_service_section(
940
                FILE *f,
941
                const char *name,
942
                const char *what,
943
                const char *key_file,
944
0
                const char *options) {
945
946
0
        _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *key_file_escaped = NULL, *options_escaped = NULL;
947
948
0
        assert(f);
949
0
        assert(name);
950
0
        assert(what);
951
952
0
        name_escaped = specifier_escape(name);
953
0
        if (!name_escaped)
954
0
                return log_oom();
955
956
0
        what_escaped = specifier_escape(what);
957
0
        if (!what_escaped)
958
0
                return log_oom();
959
960
0
        if (key_file) {
961
0
                key_file_escaped = specifier_escape(key_file);
962
0
                if (!key_file_escaped)
963
0
                        return log_oom();
964
0
        }
965
966
0
        if (options) {
967
0
                options_escaped = specifier_escape(options);
968
0
                if (!options_escaped)
969
0
                        return log_oom();
970
0
        }
971
972
0
        fprintf(f,
973
0
                "\n"
974
0
                "[Service]\n"
975
0
                "Type=oneshot\n"
976
0
                "RemainAfterExit=yes\n"
977
0
                "TimeoutSec=infinity\n"   /* The binary handles timeouts on its own */
978
0
                "KeyringMode=shared\n"    /* Make sure we can share cached keys among instances */
979
0
                "OOMScoreAdjust=500\n"    /* Unlocking can allocate a lot of memory if Argon2 is used */
980
0
                "ImportCredential=cryptsetup.*\n"
981
0
                "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
982
0
                "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
983
0
                name_escaped, what_escaped, strempty(key_file_escaped), strempty(options_escaped),
984
0
                name_escaped);
985
986
0
        return 0;
987
0
}
988
989
0
int generator_write_veritysetup_unit_section(FILE *f, const char *source) {
990
0
        assert(f);
991
992
0
        fprintf(f,
993
0
                "[Unit]\n"
994
0
                "Description=Integrity Protection Setup for %%I\n"
995
0
                "Documentation=man:veritytab(5) man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n");
996
997
0
        if (source)
998
0
                fprintf(f, "SourcePath=%s\n", source);
999
1000
0
        fprintf(f,
1001
0
                "DefaultDependencies=no\n"
1002
0
                "IgnoreOnIsolate=true\n"
1003
                /* The systemd-udevd.service ordering is mostly about shutdown, not startup: on stop the dm
1004
                 * device is detached via an ioctl carrying a udev cookie that blocks in dm_udev_wait() until
1005
                 * udev releases it (95-dm-notify.rules). Stopping before udevd keeps it around to service the
1006
                 * cookie; otherwise during soft-reboot/shutdown udevd may exit first and the detach hangs. */
1007
0
                "After=veritysetup-pre.target systemd-udevd-kernel.socket systemd-udevd.service\n"
1008
0
                "Before=blockdev@dev-mapper-%%i.target\n"
1009
0
                "Wants=blockdev@dev-mapper-%%i.target\n");
1010
1011
0
        return 0;
1012
0
}
1013
1014
int generator_write_veritysetup_service_section(
1015
                FILE *f,
1016
                const char *name,
1017
                const char *data_what,
1018
                const char *hash_what,
1019
                const char *roothash,
1020
0
                const char *options) {
1021
1022
0
        _cleanup_free_ char *name_escaped = NULL, *data_what_escaped = NULL, *hash_what_escaped = NULL,
1023
0
                            *roothash_escaped = NULL, *options_escaped = NULL;
1024
1025
0
        assert(f);
1026
0
        assert(name);
1027
0
        assert(data_what);
1028
0
        assert(hash_what);
1029
1030
0
        name_escaped = specifier_escape(name);
1031
0
        if (!name_escaped)
1032
0
                return log_oom();
1033
1034
0
        data_what_escaped = specifier_escape(data_what);
1035
0
        if (!data_what_escaped)
1036
0
                return log_oom();
1037
1038
0
        hash_what_escaped = specifier_escape(hash_what);
1039
0
        if (!hash_what_escaped)
1040
0
                return log_oom();
1041
1042
0
        if (roothash) {
1043
0
                roothash_escaped = specifier_escape(roothash);
1044
0
                if (!roothash_escaped)
1045
0
                        return log_oom();
1046
0
        }
1047
1048
0
        if (options) {
1049
0
                options_escaped = specifier_escape(options);
1050
0
                if (!options_escaped)
1051
0
                        return log_oom();
1052
0
        }
1053
1054
0
        fprintf(f,
1055
0
                "\n"
1056
0
                "[Service]\n"
1057
0
                "Type=oneshot\n"
1058
0
                "RemainAfterExit=yes\n"
1059
0
                "ExecStart=" SYSTEMD_VERITYSETUP_PATH " attach '%s' '%s' '%s' '%s' '%s'\n"
1060
0
                "ExecStop=" SYSTEMD_VERITYSETUP_PATH " detach '%s'\n",
1061
0
                name_escaped, data_what_escaped, hash_what_escaped, empty_to_dash(roothash_escaped), strempty(options_escaped),
1062
0
                name_escaped);
1063
1064
0
        return 0;
1065
0
}
1066
1067
0
void log_setup_generator(void) {
1068
0
        if (invoked_by_systemd()) {
1069
                /* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
1070
0
                if (streq_ptr(getenv("SYSTEMD_SCOPE"), "system"))
1071
0
                        log_set_prohibit_ipc(true);
1072
1073
                /* This effectively means: journal for per-user service manager generators, kmsg for per-system service manager generators */
1074
0
                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
1075
0
        } else
1076
0
                log_set_target(LOG_TARGET_AUTO);
1077
1078
0
        log_parse_environment();
1079
0
        log_open();
1080
0
}
1081
1082
0
bool generator_soft_rebooted(void) {
1083
0
        static int cached = -1;
1084
0
        int r;
1085
1086
0
        if (cached >= 0)
1087
0
                return cached;
1088
1089
0
        const char *e = secure_getenv("SYSTEMD_SOFT_REBOOTS_COUNT");
1090
0
        if (!e)
1091
0
                return (cached = false);
1092
1093
0
        unsigned u;
1094
1095
0
        r = safe_atou(e, &u);
1096
0
        if (r < 0) {
1097
0
                log_debug_errno(r, "Failed to parse $SYSTEMD_SOFT_REBOOTS_COUNT, assuming the system hasn't soft-rebooted: %m");
1098
0
                return (cached = false);
1099
0
        }
1100
1101
0
        return (cached = (u > 0));
1102
0
}
1103
1104
int _generator_main(
1105
                const Verb verbs[],
1106
                const Verb verbs_end[],
1107
                const Option options[],
1108
                const Option options_end[],
1109
                int argc, char *argv[],
1110
0
                generator_main_func_t impl) {
1111
0
        int r;
1112
1113
0
        assert(argc >= 0);
1114
0
        assert(argv);
1115
0
        assert(impl);
1116
1117
0
        char **args = NULL;
1118
0
        r = _common_parse_argv(verbs, verbs_end, options, options_end, argc, argv,
1119
0
                               OPTION_PARSER_NORMAL,
1120
0
                               /* min_args= */ 0, /* max_args= */ SIZE_MAX, &args);
1121
0
        if (r <= 0)
1122
0
                return r;
1123
1124
        /* The count is checked here rather than through min_args/max_args so that every wrong count
1125
         * is reported with the same message. */
1126
0
        size_t n = strv_length(args);
1127
0
        if (!IN_SET(n, 1, 3))
1128
0
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1129
0
                                       "This program takes one or three arguments.");
1130
1131
0
        return impl(args[0], args[n == 3 ? 1 : 0], args[n == 3 ? 2 : 0]);
1132
0
}
1133
1134
0
GptAutoRoot parse_gpt_auto_root(const char *switch_name, const char *value) {
1135
0
        assert(switch_name);
1136
0
        assert(value);
1137
1138
        /* Parses the 'gpt-auto'/'gpt-auto-root'/'dissect'/'dissect-force' parameters to root=
1139
         *
1140
         * note that we are not using a regular string table here, because the mode names don't fully match
1141
         * the parameter names. And root= being something else is not an error. */
1142
1143
0
        if (streq(value, "gpt-auto")) {
1144
0
                log_debug("Enabling partition auto-detection (respecting factory reset mode), %s is explicitly set to 'gpt-auto'.", switch_name);
1145
0
                return GPT_AUTO_ROOT_ON;
1146
0
        }
1147
1148
0
        if (streq(value, "gpt-auto-force")) {
1149
0
                log_debug("Enabling partition auto-detection (ignoring factory reset mode), %s is explicitly set to 'gpt-auto-force'.", switch_name);
1150
0
                return GPT_AUTO_ROOT_FORCE;
1151
0
        }
1152
1153
0
        if (streq(value, "dissect")) {
1154
0
                log_debug("Enabling partition auto-detection via full image dissection (respecting factory reset mode), %s is explicitly set to 'dissect'.", switch_name);
1155
0
                return GPT_AUTO_ROOT_DISSECT;
1156
0
        }
1157
1158
0
        if (streq(value, "dissect-force")) {
1159
0
                log_debug("Enabling partition auto-detection via full image dissection (ignoring factory reset mode), %s is explicitly set to 'dissect-force'.", switch_name);
1160
0
                return GPT_AUTO_ROOT_DISSECT_FORCE;
1161
0
        }
1162
1163
0
        if (streq(value, "off"))
1164
0
                log_debug("Disabling partition auto-detection, %s handling is explicitly turned off.", switch_name);
1165
0
        else
1166
0
                log_debug("Disabling partition auto-detection, %s is neither unset, nor set to 'gpt-auto', 'gpt-auto-force', 'dissect' or 'dissect-force'.", switch_name);
1167
1168
0
        return GPT_AUTO_ROOT_OFF;
1169
0
}