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

systemd / systemd / 21846209963

09 Feb 2026 03:52PM UTC coverage: 72.697% (-0.02%) from 72.716%
21846209963

push

github

daandemeyer
meson: guard symlinks in sysconfdir behind install_sysconfidr

Symlinks to files inside sysconfdir are now only installed if
ìnstall_sysconfdir=true (which is the default).

If sshconfdir,sshdconfdir,shellprofiledir are not inside sysconfdir and
install_sysconfidr=false, these symlinks are still installed to the
configured directory.

311951 of 429113 relevant lines covered (72.7%)

1156102.48 hits per line

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

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

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

7
#include "alloc-util.h"
8
#include "argv-util.h"
9
#include "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-label.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 "time-util.h"
25
#include "tmpfile-util.h"
26
#include "unit-name.h"
27

28
static int symlink_unless_exists(const char *target, const char *linkpath) {
184✔
29
        (void) mkdir_parents(linkpath, 0755);
184✔
30

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

36
int generator_open_unit_file_full(
362✔
37
                const char *dir,
38
                const char *source,
39
                const char *filename,
40
                FILE **ret_file,
41
                char **ret_final_path,
42
                char **ret_temp_path) {
43

44
        _cleanup_free_ char *p = NULL;
362✔
45
        FILE *f;
362✔
46
        int r;
362✔
47

48
        assert(dir);
362✔
49
        assert(ret_file);
362✔
50

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

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

59
                (void) fchmod(fileno(f), 0644);
299✔
60

61
                *ret_temp_path = TAKE_PTR(p);
299✔
62
        } else {
63
                assert(filename);
63✔
64

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

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

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

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

84
        *ret_file = f;
362✔
85

86
        if (ret_final_path)
362✔
87
                *ret_final_path = TAKE_PTR(p);
×
88

89
        return 0;
90
}
91

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

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

102
        assert(dir);
184✔
103
        assert(dst);
184✔
104
        assert(src);
184✔
105

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

112
        r = path_split_prefix_filename(src, &dn, &fn);
184✔
113
        if (r < 0)
184✔
114
                return log_error_errno(r, "Failed to split '%s' into directory prefix and filename: %m", src);
×
115
        if (r == O_DIRECTORY)
184✔
116
                return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src);
×
117

118
        if (instance) {
184✔
119
                r = unit_name_replace_instance(fn, instance, &instantiated);
2✔
120
                if (r < 0)
2✔
121
                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance);
×
122
        }
123

124
        if (dep_type) { /* Create a .wants/ style dep */
184✔
125
                from = path_join(dn ?: "..", fn);
269✔
126
                if (!from)
182✔
127
                        return log_oom();
×
128

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

135
                to = strjoin(dir, "/", dst);
2✔
136
        }
137
        if (!to)
184✔
138
                return log_oom();
×
139

140
        return symlink_unless_exists(from, to);
184✔
141
}
142

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

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

155
        assert(dir);
×
156
        assert(src);
×
157
        assert(order);
×
158
        assert(dst);
×
159

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

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

168
                to = instantiated;
×
169
        } else
170
                to = dst;
171

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

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

180
        (void) mkdir_parents_label(p, 0755);
×
181

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

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

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

198
        return 0;
199
}
200

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

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

211
        assert(unit);
7✔
212
        assert(dir);
7✔
213
        assert(what);
7✔
214

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

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

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

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

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

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

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

264
        return 0;
265
}
266

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

275
        int r;
14✔
276

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

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

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

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

301
        if (fstab_test_option(options, "bind\0rbind\0")) {
14✔
302
                log_debug("Skipping file system check for bind mount of '%s'.", what);
×
303
                return 0;
×
304
        }
305

306
        if (!is_device_path(what)) {
14✔
307
                log_debug("Checking was requested for \"%s\", but it is not a device.", what);
×
308
                return 0;
×
309
        }
310

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

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

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

339
                r = symlink_unless_exists(SYSTEM_DATA_UNIT_DIR "/" SPECIAL_FSCK_ROOT_SERVICE, lnk);
×
340
                if (r < 0)
×
341
                        return r;
×
342
        } else {
343
                _cleanup_free_ char *_fsck = NULL;
14✔
344
                const char *fsck, *dep;
14✔
345

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

351
                        fsck = SPECIAL_FSCK_ROOT_SERVICE;
352
                        dep = "Requires";
353

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

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

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

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

373
                        fsck = _fsck;
7✔
374
                }
375

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

382
        return 0;
383
}
384

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

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

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

399
        assert(dir);
46✔
400
        assert(what);
46✔
401

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

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

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

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

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

441
        return 1;
442
}
443

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

451
        _cleanup_free_ char *timeout = NULL;
24✔
452
        usec_t u;
24✔
453
        int r;
24✔
454

455
        assert(f);
24✔
456
        assert(where);
24✔
457
        assert(filter);
24✔
458
        assert(unit_setting);
24✔
459

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

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

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

474
        return 0;
475
}
476

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

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

488
        _cleanup_free_ char *node = NULL, *unit = NULL;
24✔
489
        int r;
24✔
490

491
        assert(dir);
24✔
492
        assert(what);
24✔
493
        assert(where);
24✔
494

495
        if (fstab_is_extrinsic(where, opts))
24✔
496
                return 0;
497

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

501
        node = fstab_node_to_udev_node(what);
×
502
        if (!node)
×
503
                return log_oom();
×
504

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

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

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

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

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

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

542
int generator_hook_up_mkswap(
×
543
                const char *dir,
544
                const char *what) {
545

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

550
        assert(dir);
×
551
        assert(what);
×
552

553
        node = fstab_node_to_udev_node(what);
×
554
        if (!node)
×
555
                return log_oom();
×
556

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

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

568
        escaped = cescape(node);
×
569
        if (!escaped)
×
570
                return log_oom();
×
571

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

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

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

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

605
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
606
}
607

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

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

619
        assert(dir);
×
620
        assert(what);
×
621
        assert(where);
×
622

623
        node = fstab_node_to_udev_node(what);
×
624
        if (!node)
×
625
                return log_oom();
×
626

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

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

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

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

650
        escaped = cescape(node);
×
651
        if (!escaped)
×
652
                return log_oom();
×
653

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

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

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

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

692
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
693
}
694

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

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

704
        assert(dir);
×
705
        assert(where);
×
706

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

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

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

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

729
        return generator_add_symlink_full(dir, where_unit, "wants", growfs_unit_path, instance);
×
730
}
731

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

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

741
        assert(dir);
×
742
        assert(where);
×
743

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

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

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

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

766
        return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
×
767
}
768

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

774
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
775
        const char *validatefs_unit, *validatefs_unit_path;
×
776
        int r;
×
777

778
        assert(dir);
×
779
        assert(where);
×
780

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

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

790
        validatefs_unit = SPECIAL_VALIDATEFS_SERVICE;
×
791
        validatefs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_VALIDATEFS_SERVICE;
×
792

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

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

803
        return generator_add_symlink_full(dir, where_unit, "wants", validatefs_unit_path, instance);
×
804
}
805

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

813
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
814
        int r;
×
815

816
        assert(dir);
×
817
        assert(where);
×
818

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

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

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

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

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

842
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_SERVICE, instance);
×
843
}
844

845
int generator_hook_up_quotaon(
×
846
                const char *dir,
847
                const char *where,
848
                const char *target) {
849

850
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
851
        int r;
×
852

853
        assert(dir);
×
854
        assert(where);
×
855

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

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

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

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

874
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_SERVICE, instance);
×
875
}
876

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

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

887
        assert(f);
24✔
888
        assert(what);
24✔
889

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

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

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

901
        return 0;
902
}
903

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

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

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

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

923
        return 0;
22✔
924
}
925

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

933
        _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *key_file_escaped = NULL, *options_escaped = NULL;
22✔
934

935
        assert(f);
22✔
936
        assert(name);
22✔
937
        assert(what);
22✔
938

939
        name_escaped = specifier_escape(name);
22✔
940
        if (!name_escaped)
22✔
941
                return log_oom();
×
942

943
        what_escaped = specifier_escape(what);
22✔
944
        if (!what_escaped)
22✔
945
                return log_oom();
×
946

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

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

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

973
        return 0;
974
}
975

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

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

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

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

994
        return 0;
×
995
}
996

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

1005
        _cleanup_free_ char *name_escaped = NULL, *data_what_escaped = NULL, *hash_what_escaped = NULL,
×
1006
                            *roothash_escaped = NULL, *options_escaped = NULL;
×
1007

1008
        assert(f);
×
1009
        assert(name);
×
1010
        assert(data_what);
×
1011
        assert(hash_what);
×
1012

1013
        name_escaped = specifier_escape(name);
×
1014
        if (!name_escaped)
×
1015
                return log_oom();
×
1016

1017
        data_what_escaped = specifier_escape(data_what);
×
1018
        if (!data_what_escaped)
×
1019
                return log_oom();
×
1020

1021
        hash_what_escaped = specifier_escape(hash_what);
×
1022
        if (!hash_what_escaped)
×
1023
                return log_oom();
×
1024

1025
        if (roothash) {
×
1026
                roothash_escaped = specifier_escape(roothash);
×
1027
                if (!roothash_escaped)
×
1028
                        return log_oom();
×
1029
        }
1030

1031
        if (options) {
×
1032
                options_escaped = specifier_escape(options);
×
1033
                if (!options_escaped)
×
1034
                        return log_oom();
×
1035
        }
1036

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

1047
        return 0;
1048
}
1049

1050
void log_setup_generator(void) {
294✔
1051
        if (invoked_by_systemd()) {
294✔
1052
                /* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
1053
                if (streq_ptr(getenv("SYSTEMD_SCOPE"), "system"))
223✔
1054
                        log_set_prohibit_ipc(true);
×
1055

1056
                /* This effectively means: journal for per-user service manager generators, kmsg for per-system service manager generators */
1057
                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
223✔
1058
        } else
1059
                log_set_target(LOG_TARGET_AUTO);
71✔
1060

1061
        log_parse_environment();
294✔
1062
        log_open();
294✔
1063
}
294✔
1064

1065
bool generator_soft_rebooted(void) {
×
1066
        static int cached = -1;
×
1067
        int r;
×
1068

1069
        if (cached >= 0)
×
1070
                return cached;
×
1071

1072
        const char *e = secure_getenv("SYSTEMD_SOFT_REBOOTS_COUNT");
×
1073
        if (!e)
×
1074
                return (cached = false);
×
1075

1076
        unsigned u;
×
1077

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

1084
        return (cached = (u > 0));
×
1085
}
1086

1087
GptAutoRoot parse_gpt_auto_root(const char *switch_name, const char *value) {
17✔
1088
        assert(switch_name);
17✔
1089
        assert(value);
17✔
1090

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

1096
        if (streq(value, "gpt-auto")) {
17✔
1097
                log_debug("Enabling partition auto-detection (respecting factory reset mode), %s is explicitly set to 'gpt-auto'.", switch_name);
×
1098
                return GPT_AUTO_ROOT_ON;
×
1099
        }
1100

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

1106
        if (streq(value, "dissect")) {
17✔
1107
                log_debug("Enabling partition auto-detection via full image dissection (respecting factory reset mode), %s is explicitly set to 'dissect'.", switch_name);
×
1108
                return GPT_AUTO_ROOT_DISSECT;
×
1109
        }
1110

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

1116
        if (streq(value, "off"))
17✔
1117
                log_debug("Disabling partition auto-detection, %s handling is explicitly turned off.", switch_name);
×
1118
        else
1119
                log_debug("Disabling partition auto-detection, %s is neither unset, nor set to 'gpt-auto', 'gpt-auto-force', 'dissect' or 'dissect-force'.", switch_name);
17✔
1120

1121
        return GPT_AUTO_ROOT_OFF;
1122
}
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