]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/generator.c
veritysetup-generator: add support for verity root options
[thirdparty/systemd.git] / src / shared / generator.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
e48fdd84 2
a8fbdf54 3#include <errno.h>
e48fdd84
LP
4#include <unistd.h>
5
b5efdb8a 6#include "alloc-util.h"
dee29aeb 7#include "cgroup-util.h"
4f5dd394
LP
8#include "dropin.h"
9#include "escape.h"
3ffd4af2 10#include "fd-util.h"
4f5dd394
LP
11#include "fileio.h"
12#include "fstab-util.h"
3ffd4af2 13#include "generator.h"
a8fbdf54
TA
14#include "log.h"
15#include "macro.h"
e48fdd84 16#include "mkdir.h"
4f5dd394
LP
17#include "path-util.h"
18#include "special.h"
98bad05e 19#include "specifier.h"
07630cea 20#include "string-util.h"
a8fbdf54 21#include "time-util.h"
e48fdd84 22#include "unit-name.h"
4f5dd394 23#include "util.h"
e48fdd84 24
fb883e75
ZJS
25int generator_open_unit_file(
26 const char *dest,
27 const char *source,
28 const char *name,
29 FILE **file) {
30
31 const char *unit;
32 FILE *f;
41f6e627 33 int r;
fb883e75 34
270384b2 35 unit = prefix_roota(dest, name);
fb883e75 36
41f6e627
ZJS
37 r = fopen_unlocked(unit, "wxe", &f);
38 if (r < 0) {
39 if (source && r == -EEXIST)
40 return log_error_errno(r,
fb883e75
ZJS
41 "Failed to create unit file %s, as it already exists. Duplicate entry in %s?",
42 unit, source);
43 else
41f6e627 44 return log_error_errno(r,
fb883e75
ZJS
45 "Failed to create unit file %s: %m",
46 unit);
47 }
48
fb883e75
ZJS
49 fprintf(f,
50 "# Automatically generated by %s\n\n",
51 program_invocation_short_name);
52
53 *file = f;
54 return 0;
55}
56
5c176eb4
ZJS
57int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src) {
58 /* Adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute)
59 * or ../<src> (otherwise). */
b559616f
ZJS
60
61 const char *from, *to;
62
5c176eb4
ZJS
63 from = path_is_absolute(src) ? src : strjoina("../", src);
64 to = strjoina(dir, "/", dst, ".", dep_type, "/", basename(src));
b559616f
ZJS
65
66 mkdir_parents_label(to, 0755);
67 if (symlink(from, to) < 0)
7f0cc637
ZJS
68 if (errno != EEXIST)
69 return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
b559616f
ZJS
70
71 return 0;
72}
73
4dda4e63 74static int write_fsck_sysroot_service(const char *dir, const char *what) {
98bad05e 75 _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
4dda4e63 76 _cleanup_fclose_ FILE *f = NULL;
85eca92e 77 const char *unit;
4dda4e63
ZJS
78 int r;
79
98bad05e 80 escaped = specifier_escape(what);
fa05e972
AB
81 if (!escaped)
82 return log_oom();
83
98bad05e
LP
84 escaped2 = cescape(escaped);
85 if (!escaped2)
86 return log_oom();
87
5ff8da10 88 unit = strjoina(dir, "/"SPECIAL_FSCK_ROOT_SERVICE);
4dda4e63
ZJS
89 log_debug("Creating %s", unit);
90
91 r = unit_name_from_path(what, ".device", &device);
92 if (r < 0)
93 return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
94
95 f = fopen(unit, "wxe");
96 if (!f)
97 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
98
99 fprintf(f,
100 "# Automatically generated by %1$s\n\n"
101 "[Unit]\n"
4dda4e63 102 "Description=File System Check on %2$s\n"
da495a03 103 "Documentation=man:systemd-fsck-root.service(8)\n"
4dda4e63
ZJS
104 "DefaultDependencies=no\n"
105 "BindsTo=%3$s\n"
ff9bf8d0 106 "Conflicts=shutdown.target\n"
0900593e 107 "After=initrd-root-device.target local-fs-pre.target %3$s\n"
4dda4e63
ZJS
108 "Before=shutdown.target\n"
109 "\n"
110 "[Service]\n"
111 "Type=oneshot\n"
112 "RemainAfterExit=yes\n"
fa05e972 113 "ExecStart=" SYSTEMD_FSCK_PATH " %4$s\n"
4dda4e63
ZJS
114 "TimeoutSec=0\n",
115 program_invocation_short_name,
98bad05e 116 escaped,
fa05e972 117 device,
98bad05e 118 escaped2);
4dda4e63 119
2929b4a6
LP
120 r = fflush_and_check(f);
121 if (r < 0)
122 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
4dda4e63
ZJS
123
124 return 0;
125}
126
e48fdd84
LP
127int generator_write_fsck_deps(
128 FILE *f,
2e852276 129 const char *dir,
e48fdd84
LP
130 const char *what,
131 const char *where,
6db615c1 132 const char *fstype) {
e48fdd84 133
7410616c
LP
134 int r;
135
e48fdd84 136 assert(f);
2e852276 137 assert(dir);
6db615c1
LP
138 assert(what);
139 assert(where);
e48fdd84
LP
140
141 if (!is_device_path(what)) {
142 log_warning("Checking was requested for \"%s\", but it is not a device.", what);
143 return 0;
144 }
145
6db615c1 146 if (!isempty(fstype) && !streq(fstype, "auto")) {
eb66db55 147 r = fsck_exists(fstype);
85eca92e
LP
148 if (r < 0)
149 log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
150 else if (r == 0) {
e48fdd84 151 /* treat missing check as essentially OK */
85eca92e 152 log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
571d0134 153 return 0;
85eca92e 154 }
e48fdd84
LP
155 }
156
2e852276 157 if (path_equal(where, "/")) {
85eca92e 158 const char *lnk;
e48fdd84 159
cbdc9cfe 160 lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
e48fdd84 161
37dc34f7 162 (void) mkdir_parents(lnk, 0755);
cbdc9cfe 163 if (symlink(SYSTEM_DATA_UNIT_PATH "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
4a62c710 164 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
e48fdd84
LP
165
166 } else {
4dda4e63 167 _cleanup_free_ char *_fsck = NULL;
afacf3fc 168 const char *fsck, *dep;
4dda4e63
ZJS
169
170 if (in_initrd() && path_equal(where, "/sysroot")) {
171 r = write_fsck_sysroot_service(dir, what);
172 if (r < 0)
173 return r;
174
5ff8da10 175 fsck = SPECIAL_FSCK_ROOT_SERVICE;
afacf3fc 176 dep = "Requires";
4dda4e63 177 } else {
afacf3fc
LP
178 /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
179 * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
180 * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
181 * we'd have to unmount /usr too. */
182
183 dep = !in_initrd() && path_equal(where, "/usr") ? "Wants" : "Requires";
184
4dda4e63
ZJS
185 r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
186 if (r < 0)
187 return log_error_errno(r, "Failed to create fsck service name: %m");
188
189 fsck = _fsck;
190 }
e48fdd84
LP
191
192 fprintf(f,
afacf3fc
LP
193 "%1$s=%2$s\n"
194 "After=%2$s\n",
195 dep, fsck);
e48fdd84
LP
196 }
197
198 return 0;
199}
29686440 200
2e852276
ZJS
201int generator_write_timeouts(
202 const char *dir,
203 const char *what,
204 const char *where,
205 const char *opts,
206 char **filtered) {
29686440 207
c2f4bcfc
ZJS
208 /* Configure how long we wait for a device that backs a mount point or a
209 * swap partition to show up. This is useful to support endless device timeouts
210 * for devices that show up only after user input, like crypto devices. */
29686440 211
d15d0333 212 _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
29686440
ZJS
213 usec_t u;
214 int r;
29686440 215
0004f698
ZJS
216 r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
217 "x-systemd.device-timeout\0",
d15d0333
ZJS
218 NULL, &timeout, filtered);
219 if (r <= 0)
220 return r;
b3208b66 221
0004f698 222 r = parse_sec_fix_0(timeout, &u);
29686440 223 if (r < 0) {
7410616c 224 log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
29686440
ZJS
225 return 0;
226 }
227
228 node = fstab_node_to_udev_node(what);
229 if (!node)
230 return log_oom();
c67bd1f7
N
231 if (!is_device_path(node)) {
232 log_warning("x-systemd.device-timeout ignored for %s", what);
233 return 0;
234 }
29686440 235
7410616c
LP
236 r = unit_name_from_path(node, ".device", &unit);
237 if (r < 0)
238 return log_error_errno(r, "Failed to make unit name from path: %m");
29686440 239
8eea8687 240 return write_drop_in_format(dir, unit, 50, "device-timeout",
7cecc563
ZJS
241 "# Automatically generated by %s\n"
242 "# from supplied options \"%s\"\n\n"
acd53eaa
LP
243 "[Unit]\n"
244 "JobRunningTimeoutSec=%s",
245 program_invocation_short_name,
7cecc563 246 opts,
acd53eaa 247 timeout);
29686440 248}
7163e1ca 249
4195077a
MK
250int generator_write_device_deps(
251 const char *dir,
252 const char *what,
253 const char *where,
254 const char *opts) {
255
256 /* fstab records that specify _netdev option should apply the network
257 * ordering on the actual device depending on network connection. If we
258 * are not mounting real device (NFS, CIFS), we rely on _netdev effect
259 * on the mount unit itself. */
260
261 _cleanup_free_ char *node = NULL, *unit = NULL;
262 int r;
263
ad8f1b0f
FB
264 if (fstab_is_extrinsic(where, opts))
265 return 0;
266
4195077a
MK
267 if (!fstab_test_option(opts, "_netdev\0"))
268 return 0;
269
270 node = fstab_node_to_udev_node(what);
271 if (!node)
272 return log_oom();
273
274 /* Nothing to apply dependencies to. */
275 if (!is_device_path(node))
276 return 0;
277
278 r = unit_name_from_path(node, ".device", &unit);
279 if (r < 0)
da495a03
ZJS
280 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
281 node);
4195077a
MK
282
283 /* See mount_add_default_dependencies for explanation why we create such
284 * dependencies. */
285 return write_drop_in_format(dir, unit, 50, "netdev-dependencies",
286 "# Automatically generated by %s\n\n"
287 "[Unit]\n"
288 "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n"
289 "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n",
290 program_invocation_short_name);
291}
292
7163e1ca
DD
293int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
294 _cleanup_free_ char *unit = NULL;
295 int r;
296
297 r = unit_name_from_path(what, ".device", &unit);
298 if (r < 0)
da495a03
ZJS
299 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
300 what);
7163e1ca
DD
301
302 return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
303 "# Automatically generated by %s\n\n"
acd53eaa
LP
304 "[Unit]\n"
305 "Requires=%s\n"
306 "After=%s",
307 program_invocation_short_name,
308 unit,
309 unit);
7163e1ca 310}
da495a03
ZJS
311
312int generator_hook_up_mkswap(
313 const char *dir,
314 const char *what) {
315
316 _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
317 _cleanup_fclose_ FILE *f = NULL;
318 const char *unit_file;
319 int r;
320
321 node = fstab_node_to_udev_node(what);
322 if (!node)
323 return log_oom();
324
325 /* Nothing to work on. */
baaa35ad
ZJS
326 if (!is_device_path(node))
327 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
328 "Cannot format something that is not a device node: %s",
329 node);
da495a03
ZJS
330
331 r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
332 if (r < 0)
333 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
334 node);
335
270384b2 336 unit_file = prefix_roota(dir, unit);
da495a03
ZJS
337 log_debug("Creating %s", unit_file);
338
339 escaped = cescape(node);
340 if (!escaped)
341 return log_oom();
342
343 r = unit_name_from_path(what, ".swap", &where_unit);
344 if (r < 0)
345 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
346 what);
347
348 f = fopen(unit_file, "wxe");
349 if (!f)
350 return log_error_errno(errno, "Failed to create unit file %s: %m",
351 unit_file);
352
353 fprintf(f,
354 "# Automatically generated by %s\n\n"
355 "[Unit]\n"
356 "Description=Make Swap on %%f\n"
357 "Documentation=man:systemd-mkswap@.service(8)\n"
358 "DefaultDependencies=no\n"
359 "BindsTo=%%i.device\n"
ff9bf8d0 360 "Conflicts=shutdown.target\n"
da495a03 361 "After=%%i.device\n"
ff9bf8d0 362 "Before=shutdown.target %s\n"
da495a03
ZJS
363 "\n"
364 "[Service]\n"
365 "Type=oneshot\n"
366 "RemainAfterExit=yes\n"
367 "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
368 "TimeoutSec=0\n",
369 program_invocation_short_name,
370 where_unit,
371 escaped);
372
373 r = fflush_and_check(f);
374 if (r < 0)
375 return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
376
377 return generator_add_symlink(dir, where_unit, "requires", unit);
378}
379
380int generator_hook_up_mkfs(
381 const char *dir,
382 const char *what,
383 const char *where,
384 const char *type) {
385
386 _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
387 _cleanup_fclose_ FILE *f = NULL;
388 const char *unit_file;
389 int r;
390
391 node = fstab_node_to_udev_node(what);
392 if (!node)
393 return log_oom();
394
395 /* Nothing to work on. */
baaa35ad
ZJS
396 if (!is_device_path(node))
397 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
398 "Cannot format something that is not a device node: %s",
399 node);
da495a03 400
baaa35ad
ZJS
401 if (!type || streq(type, "auto"))
402 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
403 "Cannot format partition %s, filesystem type is not specified",
404 node);
da495a03 405
804f8e17 406 r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit);
da495a03
ZJS
407 if (r < 0)
408 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
409 node);
410
270384b2 411 unit_file = prefix_roota(dir, unit);
da495a03
ZJS
412 log_debug("Creating %s", unit_file);
413
414 escaped = cescape(node);
415 if (!escaped)
416 return log_oom();
417
418 r = unit_name_from_path(where, ".mount", &where_unit);
419 if (r < 0)
420 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
421 where);
422
423 f = fopen(unit_file, "wxe");
424 if (!f)
425 return log_error_errno(errno, "Failed to create unit file %s: %m",
426 unit_file);
427
428 fprintf(f,
429 "# Automatically generated by %s\n\n"
430 "[Unit]\n"
431 "Description=Make File System on %%f\n"
804f8e17 432 "Documentation=man:systemd-makefs@.service(8)\n"
da495a03
ZJS
433 "DefaultDependencies=no\n"
434 "BindsTo=%%i.device\n"
ff9bf8d0 435 "Conflicts=shutdown.target\n"
da495a03
ZJS
436 "After=%%i.device\n"
437 /* fsck might or might not be used, so let's be safe and order
438 * ourselves before both systemd-fsck@.service and the mount unit. */
ff9bf8d0 439 "Before=shutdown.target systemd-fsck@%%i.service %s\n"
da495a03
ZJS
440 "\n"
441 "[Service]\n"
442 "Type=oneshot\n"
443 "RemainAfterExit=yes\n"
444 "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
445 "TimeoutSec=0\n",
446 program_invocation_short_name,
447 where_unit,
448 type,
449 escaped);
450 // XXX: what about local-fs-pre.target?
451
452 r = fflush_and_check(f);
453 if (r < 0)
454 return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
455
456 return generator_add_symlink(dir, where_unit, "requires", unit);
457}
7f2806d5
ZJS
458
459int generator_hook_up_growfs(
460 const char *dir,
461 const char *where,
462 const char *target) {
463
464 _cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL;
465 _cleanup_fclose_ FILE *f = NULL;
466 const char *unit_file;
467 int r;
468
469 escaped = cescape(where);
470 if (!escaped)
471 return log_oom();
472
473 r = unit_name_from_path_instance("systemd-growfs", where, ".service", &unit);
474 if (r < 0)
475 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
476 where);
477
478 r = unit_name_from_path(where, ".mount", &where_unit);
479 if (r < 0)
480 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
481 where);
482
270384b2 483 unit_file = prefix_roota(dir, unit);
7f2806d5
ZJS
484 log_debug("Creating %s", unit_file);
485
486 f = fopen(unit_file, "wxe");
487 if (!f)
488 return log_error_errno(errno, "Failed to create unit file %s: %m",
489 unit_file);
490
491 fprintf(f,
492 "# Automatically generated by %s\n\n"
493 "[Unit]\n"
494 "Description=Grow File System on %%f\n"
495 "Documentation=man:systemd-growfs@.service(8)\n"
496 "DefaultDependencies=no\n"
497 "BindsTo=%%i.mount\n"
ff9bf8d0 498 "Conflicts=shutdown.target\n"
7f2806d5 499 "After=%%i.mount\n"
18e6e863
LP
500 "Before=shutdown.target %s\n",
501 program_invocation_short_name,
502 target);
503
504 if (empty_or_root(where)) /* Make sure the root fs is actually writable before we resize it */
505 fprintf(f,
506 "After=systemd-remount-fs.service\n");
507
508 fprintf(f,
7f2806d5
ZJS
509 "\n"
510 "[Service]\n"
511 "Type=oneshot\n"
512 "RemainAfterExit=yes\n"
513 "ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
514 "TimeoutSec=0\n",
7f2806d5
ZJS
515 escaped);
516
517 return generator_add_symlink(dir, where_unit, "wants", unit);
518}
afe44c8f 519
9b69569d
ZJS
520int generator_enable_remount_fs_service(const char *dir) {
521 /* Pull in systemd-remount-fs.service */
522 return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants",
523 SYSTEM_DATA_UNIT_PATH "/" SPECIAL_REMOUNT_FS_SERVICE);
524}
525
a7e88558
LP
526int generator_write_blockdev_dependency(
527 FILE *f,
528 const char *what) {
529
530 _cleanup_free_ char *escaped = NULL;
531 int r;
532
533 assert(f);
534 assert(what);
535
536 if (!path_startswith(what, "/dev/"))
537 return 0;
538
539 r = unit_name_path_escape(what, &escaped);
540 if (r < 0)
541 return log_error_errno(r, "Failed to escape device node path %s: %m", what);
542
543 fprintf(f,
544 "After=blockdev@%s.target\n",
545 escaped);
546
547 return 0;
548}
549
550int generator_write_cryptsetup_unit_section(
551 FILE *f,
552 const char *source) {
553
554 assert(f);
555
556 fprintf(f,
557 "[Unit]\n"
558 "Description=Cryptography Setup for %%I\n"
559 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n");
560
561 if (source)
562 fprintf(f, "SourcePath=%s\n", source);
563
564 fprintf(f,
565 "DefaultDependencies=no\n"
566 "IgnoreOnIsolate=true\n"
239952e8 567 "After=cryptsetup-pre.target systemd-udevd-kernel.socket\n"
a7e88558
LP
568 "Before=blockdev@dev-mapper-%%i.target\n"
569 "Wants=blockdev@dev-mapper-%%i.target\n");
570
571 return 0;
572}
573
574int generator_write_cryptsetup_service_section(
575 FILE *f,
576 const char *name,
577 const char *what,
578 const char *password,
579 const char *options) {
580
581 _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *password_escaped = NULL, *options_escaped = NULL;
582
583 assert(f);
584 assert(name);
585 assert(what);
586
587 name_escaped = specifier_escape(name);
588 if (!name_escaped)
589 return log_oom();
590
591 what_escaped = specifier_escape(what);
592 if (!what_escaped)
593 return log_oom();
594
595 if (password) {
596 password_escaped = specifier_escape(password);
597 if (!password_escaped)
598 return log_oom();
599 }
600
601 if (options) {
602 options_escaped = specifier_escape(options);
603 if (!options_escaped)
604 return log_oom();
605 }
606
607 fprintf(f,
608 "\n"
609 "[Service]\n"
610 "Type=oneshot\n"
611 "RemainAfterExit=yes\n"
612 "TimeoutSec=0\n" /* The binary handles timeouts on its own */
613 "KeyringMode=shared\n" /* Make sure we can share cached keys among instances */
614 "OOMScoreAdjust=500\n" /* Unlocking can allocate a lot of memory if Argon2 is used */
615 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
616 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
617 name_escaped, what_escaped, strempty(password_escaped), strempty(options_escaped),
618 name_escaped);
619
620 return 0;
621}
622
afe44c8f 623void log_setup_generator(void) {
dee29aeb
LP
624 /* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
625 if (cg_pid_get_owner_uid(0, NULL) == -ENXIO /* not running in a per-user slice */)
626 log_set_prohibit_ipc(true);
627
628 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); /* This effectively means: journal for per-user generators, kmsg otherwise */
629 log_parse_environment();
630 (void) log_open();
afe44c8f 631}