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