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