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