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