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