]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/generator.c
core/scope: drop effectively unused unit_watch_pidref() calls (#38186)
[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"
dee29aeb 9#include "cgroup-util.h"
4f5dd394
LP
10#include "dropin.h"
11#include "escape.h"
3ffd4af2 12#include "fd-util.h"
4f5dd394
LP
13#include "fileio.h"
14#include "fstab-util.h"
3ffd4af2 15#include "generator.h"
baa6a42d 16#include "initrd-util.h"
a8fbdf54 17#include "log.h"
35cd0ba5 18#include "mkdir-label.h"
fc5c6ecc 19#include "mountpoint-util.h"
70ae9dc4 20#include "parse-util.h"
4f5dd394
LP
21#include "path-util.h"
22#include "special.h"
98bad05e 23#include "specifier.h"
07630cea 24#include "string-util.h"
a8fbdf54 25#include "time-util.h"
8a84e0d7 26#include "tmpfile-util.h"
e48fdd84 27#include "unit-name.h"
e48fdd84 28
8a84e0d7 29int generator_open_unit_file_full(
e8ede6f5 30 const char *dir,
fb883e75 31 const char *source,
e8ede6f5 32 const char *fn,
8a84e0d7 33 FILE **ret_file,
7ceb76b6 34 char **ret_final_path,
8a84e0d7 35 char **ret_temp_path) {
fb883e75 36
e8ede6f5 37 _cleanup_free_ char *p = NULL;
fb883e75 38 FILE *f;
41f6e627 39 int r;
fb883e75 40
e8ede6f5 41 assert(dir);
8a84e0d7 42 assert(ret_file);
e8ede6f5 43
8a84e0d7
AAF
44 /* If <ret_temp_path> is specified, it creates a temporary unit file and also returns its
45 * temporary path. */
fb883e75 46
8a84e0d7
AAF
47 if (ret_temp_path) {
48 r = fopen_temporary(dir, &f, &p);
49 if (r < 0)
50 return log_error_errno(r, "Failed to create temporary unit file in '%s': %m", dir);
e8ede6f5 51
8a84e0d7
AAF
52 (void) fchmod(fileno(f), 0644);
53
54 *ret_temp_path = TAKE_PTR(p);
55 } else {
56 assert(fn);
57
58 p = path_join(dir, fn);
59 if (!p)
60 return log_oom();
61
62 r = fopen_unlocked(p, "wxe", &f);
63 if (r < 0) {
64 if (source && r == -EEXIST)
65 return log_error_errno(r,
66 "Failed to create unit file '%s', as it already exists. Duplicate entry in '%s'?",
67 p, source);
68
69 return log_error_errno(r, "Failed to create unit file '%s': %m", p);
70 }
fb883e75
ZJS
71 }
72
fb883e75
ZJS
73 fprintf(f,
74 "# Automatically generated by %s\n\n",
75 program_invocation_short_name);
76
8a84e0d7 77 *ret_file = f;
7ceb76b6
LP
78
79 if (ret_final_path)
80 *ret_final_path = TAKE_PTR(p);
81
fb883e75
ZJS
82 return 0;
83}
84
0ba07f90
LP
85int generator_add_symlink_full(
86 const char *dir,
87 const char *dst,
88 const char *dep_type,
89 const char *src,
90 const char *instance) {
91
92 _cleanup_free_ char *dn = NULL, *fn = NULL, *instantiated = NULL, *to = NULL, *from = NULL;
03469b77
LP
93 int r;
94
0ba07f90
LP
95 assert(dir);
96 assert(dst);
0ba07f90
LP
97 assert(src);
98
3392079e
LP
99 /* If 'dep_type' is specified adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise).
100 *
101 * If 'dep_type' is NULL, it will create a symlink to <src> (i.e. create an alias.
102 *
103 * If <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
0ba07f90
LP
104
105 r = path_extract_directory(src, &dn);
106 if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → just a file name was passed */
107 return log_error_errno(r, "Failed to extract directory name from '%s': %m", src);
b559616f 108
0ba07f90 109 r = path_extract_filename(src, &fn);
03469b77 110 if (r < 0)
0ba07f90
LP
111 return log_error_errno(r, "Failed to extract file name from '%s': %m", src);
112 if (r == O_DIRECTORY)
113 return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src);
114
115 if (instance) {
116 r = unit_name_replace_instance(fn, instance, &instantiated);
117 if (r < 0)
118 return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance);
119 }
b559616f 120
3392079e
LP
121 if (dep_type) { /* Create a .wants/ style dep */
122 from = path_join(dn ?: "..", fn);
123 if (!from)
124 return log_oom();
0ba07f90 125
3392079e
LP
126 to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn);
127 } else { /* or create an alias */
128 from = dn ? path_join(dn, fn) : strdup(fn);
129 if (!from)
130 return log_oom();
131
132 to = strjoin(dir, "/", dst);
133 }
0ba07f90
LP
134 if (!to)
135 return log_oom();
b559616f 136
35cd0ba5 137 (void) mkdir_parents_label(to, 0755);
0ba07f90
LP
138
139 if (symlink(from, to) < 0 && errno != EEXIST)
140 return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
b559616f
ZJS
141
142 return 0;
143}
144
50072ccf
LP
145static int generator_add_ordering(
146 const char *dir,
147 const char *src,
148 const char *order,
149 const char *dst,
150 const char *instance) {
151
152 _cleanup_free_ char *instantiated = NULL, *p = NULL, *fn = NULL;
153 _cleanup_fclose_ FILE *f = NULL;
154 const char *to;
155 int r;
156
157 assert(dir);
158 assert(src);
159 assert(order);
160 assert(dst);
161
162 /* Adds in an explicit ordering dependency of type <order> from <src> to <dst>. If <instance> is
163 * specified, it is inserted into <dst>. */
164
165 if (instance) {
166 r = unit_name_replace_instance(dst, instance, &instantiated);
167 if (r < 0)
168 return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", dst, instance);
169
170 to = instantiated;
171 } else
172 to = dst;
173
174 fn = strjoin(src, ".d/50-order-", to, ".conf");
175 if (!fn)
176 return log_oom();
177
178 p = path_join(dir, fn);
179 if (!p)
180 return log_oom();
181
182 (void) mkdir_parents_label(p, 0755);
183
184 r = fopen_unlocked(p, "wxe", &f);
185 if (r < 0)
186 return log_error_errno(r, "Failed to create '%s': %m", p);
187
188 fprintf(f,
189 "# Automatically generated by %s\n\n"
190 "[Unit]\n"
191 "%s=%s\n",
192 program_invocation_short_name,
193 order,
194 to);
195
196 r = fflush_and_check(f);
197 if (r < 0)
198 return log_error_errno(r, "Failed to write drop-in '%s': %m", p);
199
200 return 0;
201}
202
2b66f48e
LP
203static int write_fsck_sysroot_service(
204 const char *unit, /* Either SPECIAL_FSCK_ROOT_SERVICE or SPECIAL_FSCK_USR_SERVICE */
205 const char *dir,
206 const char *what,
207 const char *extra_after) {
208
98bad05e 209 _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
4dda4e63
ZJS
210 _cleanup_fclose_ FILE *f = NULL;
211 int r;
212
121ce4a6
MY
213 assert(unit);
214 assert(dir);
215 assert(what);
216
c3600c38 217 /* Writes out special versions of systemd-fsck-root.service and systemd-fsck-usr.service for use in
2b66f48e 218 * the initrd. The regular statically shipped versions of these unit files use / and /usr for as
7852e301
LP
219 * paths, which doesn't match what we need for the initrd (where the dirs are /sysroot/ +
220 * /sysusr/usr/), hence we overwrite those versions here. */
2b66f48e 221
98bad05e 222 escaped = specifier_escape(what);
fa05e972
AB
223 if (!escaped)
224 return log_oom();
225
98bad05e
LP
226 escaped2 = cescape(escaped);
227 if (!escaped2)
228 return log_oom();
229
4dda4e63
ZJS
230 r = unit_name_from_path(what, ".device", &device);
231 if (r < 0)
232 return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
233
121ce4a6
MY
234 r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
235 if (r < 0)
236 return r;
4dda4e63
ZJS
237
238 fprintf(f,
4dda4e63 239 "[Unit]\n"
121ce4a6
MY
240 "Description=File System Check on %1$s\n"
241 "Documentation=man:%2$s(8)\n"
ce37fb0d 242 "\n"
4dda4e63 243 "DefaultDependencies=no\n"
121ce4a6 244 "BindsTo=%3$s\n"
ff9bf8d0 245 "Conflicts=shutdown.target\n"
121ce4a6 246 "After=%4$s%5$slocal-fs-pre.target %3$s\n"
4dda4e63
ZJS
247 "Before=shutdown.target\n"
248 "\n"
249 "[Service]\n"
250 "Type=oneshot\n"
251 "RemainAfterExit=yes\n"
121ce4a6 252 "ExecStart=" SYSTEMD_FSCK_PATH " %6$s\n"
a9b837aa 253 "TimeoutSec=infinity\n",
98bad05e 254 escaped,
2b66f48e 255 unit,
fa05e972 256 device,
2b66f48e
LP
257 strempty(extra_after),
258 isempty(extra_after) ? "" : " ",
98bad05e 259 escaped2);
4dda4e63 260
2929b4a6
LP
261 r = fflush_and_check(f);
262 if (r < 0)
121ce4a6 263 return log_error_errno(r, "Failed to write unit %s: %m", unit);
4dda4e63
ZJS
264
265 return 0;
266}
267
e48fdd84
LP
268int generator_write_fsck_deps(
269 FILE *f,
2e852276 270 const char *dir,
e48fdd84
LP
271 const char *what,
272 const char *where,
bed3ff8e
LP
273 const char *fstype,
274 const char *options) {
e48fdd84 275
7410616c
LP
276 int r;
277
e48fdd84 278 assert(f);
2e852276 279 assert(dir);
6db615c1
LP
280 assert(what);
281 assert(where);
e48fdd84 282
122860f1 283 /* Let's do an early exit if we are invoked for the root and /usr/ trees in the initrd, to avoid
7852e301 284 * generating confusing log messages. */
122860f1
LP
285 if (in_initrd() && PATH_IN_SET(where, "/", "/usr")) {
286 log_debug("Skipping fsck for %s in initrd.", where);
287 return 0;
288 }
289
f21090d2
LP
290 if (fstype) {
291 if (!fstype_is_blockdev_backed(fstype)) {
292 log_debug("Skipping file system check for non-block based file system '%s'.", what);
293 return 0;
294 }
295
296 if (fstype_is_ro(fstype)) {
297 log_debug("Skipping file system check for read-only file system '%s'.", what);
298 return 0;
299 }
300 }
301
bed3ff8e
LP
302 if (fstab_test_option(options, "bind\0rbind\0")) {
303 log_debug("Skipping file system check for bind mount of '%s'.", what);
304 return 0;
305 }
306
e48fdd84 307 if (!is_device_path(what)) {
e04c7a92 308 log_debug("Checking was requested for \"%s\", but it is not a device.", what);
e48fdd84
LP
309 return 0;
310 }
311
6db615c1 312 if (!isempty(fstype) && !streq(fstype, "auto")) {
13556724 313 r = fsck_exists_for_fstype(fstype);
85eca92e
LP
314 if (r < 0)
315 log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
316 else if (r == 0) {
e48fdd84 317 /* treat missing check as essentially OK */
85eca92e 318 log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
571d0134 319 return 0;
85eca92e 320 }
13556724
JK
321 } else {
322 r = fsck_exists();
323 if (r < 0)
324 log_warning_errno(r, "Checking was requested for %s, but couldn't detect if the fsck command may be used, proceeding: %m", what);
325 else if (r == 0) {
326 /* treat missing fsck as essentially OK */
327 log_debug("Checking was requested for %s, but the fsck command does not exist.", what);
328 return 0;
329 }
e48fdd84
LP
330 }
331
2e852276 332 if (path_equal(where, "/")) {
85eca92e 333 const char *lnk;
e48fdd84 334
599aee40
LP
335 /* We support running the fsck instance for the root fs while it is already mounted, for
336 * compatibility with non-initrd boots. It's ugly, but it is how it is. Since – unlike for
337 * regular file systems – this means the ordering is reversed (i.e. mount *before* fsck) we
338 * have a separate fsck unit for this, independent of systemd-fsck@.service. */
339
cbdc9cfe 340 lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
e48fdd84 341
37dc34f7 342 (void) mkdir_parents(lnk, 0755);
835cf75a 343 if (symlink(SYSTEM_DATA_UNIT_DIR "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
4a62c710 344 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
e48fdd84
LP
345
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}