]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/generator.c
shutdown: log on container exit
[thirdparty/systemd.git] / src / shared / generator.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <unistd.h>
5
6 #include "alloc-util.h"
7 #include "cgroup-util.h"
8 #include "dropin.h"
9 #include "escape.h"
10 #include "fd-util.h"
11 #include "fileio.h"
12 #include "fstab-util.h"
13 #include "generator.h"
14 #include "log.h"
15 #include "macro.h"
16 #include "mkdir.h"
17 #include "path-util.h"
18 #include "special.h"
19 #include "specifier.h"
20 #include "string-util.h"
21 #include "time-util.h"
22 #include "unit-name.h"
23 #include "util.h"
24
25 int 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;
33 int r;
34
35 unit = prefix_roota(dest, name);
36
37 r = fopen_unlocked(unit, "wxe", &f);
38 if (r < 0) {
39 if (source && r == -EEXIST)
40 return log_error_errno(r,
41 "Failed to create unit file %s, as it already exists. Duplicate entry in %s?",
42 unit, source);
43 else
44 return log_error_errno(r,
45 "Failed to create unit file %s: %m",
46 unit);
47 }
48
49 fprintf(f,
50 "# Automatically generated by %s\n\n",
51 program_invocation_short_name);
52
53 *file = f;
54 return 0;
55 }
56
57 int 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). */
60
61 const char *from, *to;
62
63 from = path_is_absolute(src) ? src : strjoina("../", src);
64 to = strjoina(dir, "/", dst, ".", dep_type, "/", basename(src));
65
66 mkdir_parents_label(to, 0755);
67 if (symlink(from, to) < 0)
68 if (errno != EEXIST)
69 return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
70
71 return 0;
72 }
73
74 static int write_fsck_sysroot_service(const char *dir, const char *what) {
75 _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
76 _cleanup_fclose_ FILE *f = NULL;
77 const char *unit;
78 int r;
79
80 escaped = specifier_escape(what);
81 if (!escaped)
82 return log_oom();
83
84 escaped2 = cescape(escaped);
85 if (!escaped2)
86 return log_oom();
87
88 unit = strjoina(dir, "/"SPECIAL_FSCK_ROOT_SERVICE);
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"
102 "Description=File System Check on %2$s\n"
103 "Documentation=man:systemd-fsck-root.service(8)\n"
104 "DefaultDependencies=no\n"
105 "BindsTo=%3$s\n"
106 "Conflicts=shutdown.target\n"
107 "After=initrd-root-device.target local-fs-pre.target %3$s\n"
108 "Before=shutdown.target\n"
109 "\n"
110 "[Service]\n"
111 "Type=oneshot\n"
112 "RemainAfterExit=yes\n"
113 "ExecStart=" SYSTEMD_FSCK_PATH " %4$s\n"
114 "TimeoutSec=0\n",
115 program_invocation_short_name,
116 escaped,
117 device,
118 escaped2);
119
120 r = fflush_and_check(f);
121 if (r < 0)
122 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
123
124 return 0;
125 }
126
127 int generator_write_fsck_deps(
128 FILE *f,
129 const char *dir,
130 const char *what,
131 const char *where,
132 const char *fstype) {
133
134 int r;
135
136 assert(f);
137 assert(dir);
138 assert(what);
139 assert(where);
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
146 if (!isempty(fstype) && !streq(fstype, "auto")) {
147 r = fsck_exists(fstype);
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) {
151 /* treat missing check as essentially OK */
152 log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
153 return 0;
154 }
155 }
156
157 if (path_equal(where, "/")) {
158 const char *lnk;
159
160 lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
161
162 (void) mkdir_parents(lnk, 0755);
163 if (symlink(SYSTEM_DATA_UNIT_PATH "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
164 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
165
166 } else {
167 _cleanup_free_ char *_fsck = NULL;
168 const char *fsck, *dep;
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
175 fsck = SPECIAL_FSCK_ROOT_SERVICE;
176 dep = "Requires";
177 } else {
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
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 }
191
192 fprintf(f,
193 "%1$s=%2$s\n"
194 "After=%2$s\n",
195 dep, fsck);
196 }
197
198 return 0;
199 }
200
201 int generator_write_timeouts(
202 const char *dir,
203 const char *what,
204 const char *where,
205 const char *opts,
206 char **filtered) {
207
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. */
211
212 _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
213 usec_t u;
214 int r;
215
216 r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
217 "x-systemd.device-timeout\0",
218 NULL, &timeout, filtered);
219 if (r <= 0)
220 return r;
221
222 r = parse_sec_fix_0(timeout, &u);
223 if (r < 0) {
224 log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
225 return 0;
226 }
227
228 node = fstab_node_to_udev_node(what);
229 if (!node)
230 return log_oom();
231 if (!is_device_path(node)) {
232 log_warning("x-systemd.device-timeout ignored for %s", what);
233 return 0;
234 }
235
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");
239
240 return write_drop_in_format(dir, unit, 50, "device-timeout",
241 "# Automatically generated by %s\n"
242 "# from supplied options \"%s\"\n\n"
243 "[Unit]\n"
244 "JobRunningTimeoutSec=%s",
245 program_invocation_short_name,
246 opts,
247 timeout);
248 }
249
250 int 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
264 if (fstab_is_extrinsic(where, opts))
265 return 0;
266
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)
280 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
281 node);
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
293 int 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)
299 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
300 what);
301
302 return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
303 "# Automatically generated by %s\n\n"
304 "[Unit]\n"
305 "Requires=%s\n"
306 "After=%s",
307 program_invocation_short_name,
308 unit,
309 unit);
310 }
311
312 int 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. */
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);
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
336 unit_file = prefix_roota(dir, unit);
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"
360 "Conflicts=shutdown.target\n"
361 "After=%%i.device\n"
362 "Before=shutdown.target %s\n"
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
380 int 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. */
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);
400
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);
405
406 r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit);
407 if (r < 0)
408 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
409 node);
410
411 unit_file = prefix_roota(dir, unit);
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"
432 "Documentation=man:systemd-makefs@.service(8)\n"
433 "DefaultDependencies=no\n"
434 "BindsTo=%%i.device\n"
435 "Conflicts=shutdown.target\n"
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. */
439 "Before=shutdown.target systemd-fsck@%%i.service %s\n"
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 }
458
459 int 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
483 unit_file = prefix_roota(dir, unit);
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"
498 "Conflicts=shutdown.target\n"
499 "After=%%i.mount\n"
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,
509 "\n"
510 "[Service]\n"
511 "Type=oneshot\n"
512 "RemainAfterExit=yes\n"
513 "ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
514 "TimeoutSec=0\n",
515 escaped);
516
517 return generator_add_symlink(dir, where_unit, "wants", unit);
518 }
519
520 int 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
526 int 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
550 int 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"
567 "After=cryptsetup-pre.target systemd-udevd-kernel.socket\n"
568 "Before=blockdev@dev-mapper-%%i.target\n"
569 "Wants=blockdev@dev-mapper-%%i.target\n");
570
571 return 0;
572 }
573
574 int 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
623 int generator_write_veritysetup_unit_section(
624 FILE *f,
625 const char *source) {
626
627 assert(f);
628
629 fprintf(f,
630 "[Unit]\n"
631 "Description=Integrity Protection Setup for %%I\n"
632 "Documentation=man:veritytab(5) man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n");
633
634 if (source)
635 fprintf(f, "SourcePath=%s\n", source);
636
637 fprintf(f,
638 "DefaultDependencies=no\n"
639 "IgnoreOnIsolate=true\n"
640 "After=cryptsetup-pre.target systemd-udevd-kernel.socket\n"
641 "Before=blockdev@dev-mapper-%%i.target\n"
642 "Wants=blockdev@dev-mapper-%%i.target\n");
643
644 return 0;
645 }
646
647 int generator_write_veritysetup_service_section(
648 FILE *f,
649 const char *name,
650 const char *data_what,
651 const char *hash_what,
652 const char *roothash,
653 const char *options) {
654
655 _cleanup_free_ char *name_escaped = NULL, *data_what_escaped = NULL, *hash_what_escaped = NULL,
656 *roothash_escaped = NULL, *options_escaped = NULL;
657
658 assert(f);
659 assert(name);
660 assert(data_what);
661 assert(hash_what);
662
663 name_escaped = specifier_escape(name);
664 if (!name_escaped)
665 return log_oom();
666
667 data_what_escaped = specifier_escape(data_what);
668 if (!data_what_escaped)
669 return log_oom();
670
671 hash_what_escaped = specifier_escape(hash_what);
672 if (!hash_what_escaped)
673 return log_oom();
674
675 roothash_escaped = specifier_escape(roothash);
676 if (!roothash_escaped)
677 return log_oom();
678
679 if (options) {
680 options_escaped = specifier_escape(options);
681 if (!options_escaped)
682 return log_oom();
683 }
684
685 fprintf(f,
686 "\n"
687 "[Service]\n"
688 "Type=oneshot\n"
689 "RemainAfterExit=yes\n"
690 "ExecStart=" SYSTEMD_VERITYSETUP_PATH " attach '%s' '%s' '%s' '%s' '%s'\n"
691 "ExecStop=" SYSTEMD_VERITYSETUP_PATH " detach '%s'\n",
692 name_escaped, data_what_escaped, hash_what_escaped, roothash_escaped, strempty(options_escaped),
693 name_escaped);
694
695 return 0;
696 }
697
698 void log_setup_generator(void) {
699 /* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
700 if (cg_pid_get_owner_uid(0, NULL) == -ENXIO /* not running in a per-user slice */)
701 log_set_prohibit_ipc(true);
702
703 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); /* This effectively means: journal for per-user generators, kmsg otherwise */
704 log_parse_environment();
705 (void) log_open();
706 }