1 /* SPDX-License-Identifier: LGPL-2.1+ */
7 #include "alloc-util.h"
12 #include "fstab-util.h"
13 #include "generator.h"
17 #include "path-util.h"
19 #include "specifier.h"
20 #include "string-util.h"
21 #include "time-util.h"
22 #include "unit-name.h"
25 int generator_open_unit_file(
34 unit
= strjoina(dest
, "/", name
);
36 f
= fopen(unit
, "wxe");
38 if (source
&& errno
== EEXIST
)
39 return log_error_errno(errno
,
40 "Failed to create unit file %s, as it already exists. Duplicate entry in %s?",
43 return log_error_errno(errno
,
44 "Failed to create unit file %s: %m",
48 (void) __fsetlocking(f
, FSETLOCKING_BYCALLER
);
51 "# Automatically generated by %s\n\n",
52 program_invocation_short_name
);
58 int generator_add_symlink(const char *dir
, const char *dst
, const char *dep_type
, const char *src
) {
59 /* Adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute)
60 * or ../<src> (otherwise). */
62 const char *from
, *to
;
64 from
= path_is_absolute(src
) ? src
: strjoina("../", src
);
65 to
= strjoina(dir
, "/", dst
, ".", dep_type
, "/", basename(src
));
67 mkdir_parents_label(to
, 0755);
68 if (symlink(from
, to
) < 0)
70 return log_error_errno(errno
, "Failed to create symlink \"%s\": %m", to
);
75 static int write_fsck_sysroot_service(const char *dir
, const char *what
) {
76 _cleanup_free_
char *device
= NULL
, *escaped
= NULL
, *escaped2
= NULL
;
77 _cleanup_fclose_
FILE *f
= NULL
;
81 escaped
= specifier_escape(what
);
85 escaped2
= cescape(escaped
);
89 unit
= strjoina(dir
, "/"SPECIAL_FSCK_ROOT_SERVICE
);
90 log_debug("Creating %s", unit
);
92 r
= unit_name_from_path(what
, ".device", &device
);
94 return log_error_errno(r
, "Failed to convert device \"%s\" to unit name: %m", what
);
96 f
= fopen(unit
, "wxe");
98 return log_error_errno(errno
, "Failed to create unit file %s: %m", unit
);
101 "# Automatically generated by %1$s\n\n"
103 "Description=File System Check on %2$s\n"
104 "Documentation=man:systemd-fsck-root.service(8)\n"
105 "DefaultDependencies=no\n"
107 "Conflicts=shutdown.target\n"
108 "After=initrd-root-device.target local-fs-pre.target %3$s\n"
109 "Before=shutdown.target\n"
113 "RemainAfterExit=yes\n"
114 "ExecStart=" SYSTEMD_FSCK_PATH
" %4$s\n"
116 program_invocation_short_name
,
121 r
= fflush_and_check(f
);
123 return log_error_errno(r
, "Failed to write unit file %s: %m", unit
);
128 int generator_write_fsck_deps(
133 const char *fstype
) {
142 if (!is_device_path(what
)) {
143 log_warning("Checking was requested for \"%s\", but it is not a device.", what
);
147 if (!isempty(fstype
) && !streq(fstype
, "auto")) {
148 r
= fsck_exists(fstype
);
150 log_warning_errno(r
, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what
, fstype
);
152 /* treat missing check as essentially OK */
153 log_debug("Checking was requested for %s, but fsck.%s does not exist.", what
, fstype
);
158 if (path_equal(where
, "/")) {
161 lnk
= strjoina(dir
, "/" SPECIAL_LOCAL_FS_TARGET
".wants/"SPECIAL_FSCK_ROOT_SERVICE
);
163 mkdir_parents(lnk
, 0755);
164 if (symlink(SYSTEM_DATA_UNIT_PATH
"/"SPECIAL_FSCK_ROOT_SERVICE
, lnk
) < 0)
165 return log_error_errno(errno
, "Failed to create symlink %s: %m", lnk
);
168 _cleanup_free_
char *_fsck
= NULL
;
171 if (in_initrd() && path_equal(where
, "/sysroot")) {
172 r
= write_fsck_sysroot_service(dir
, what
);
176 fsck
= SPECIAL_FSCK_ROOT_SERVICE
;
178 r
= unit_name_from_path_instance("systemd-fsck", what
, ".service", &_fsck
);
180 return log_error_errno(r
, "Failed to create fsck service name: %m");
194 int generator_write_timeouts(
201 /* Configure how long we wait for a device that backs a mount point or a
202 * swap partition to show up. This is useful to support endless device timeouts
203 * for devices that show up only after user input, like crypto devices. */
205 _cleanup_free_
char *node
= NULL
, *unit
= NULL
, *timeout
= NULL
;
209 r
= fstab_filter_options(opts
, "comment=systemd.device-timeout\0"
210 "x-systemd.device-timeout\0",
211 NULL
, &timeout
, filtered
);
215 r
= parse_sec_fix_0(timeout
, &u
);
217 log_warning("Failed to parse timeout for %s, ignoring: %s", where
, timeout
);
221 node
= fstab_node_to_udev_node(what
);
224 if (!is_device_path(node
)) {
225 log_warning("x-systemd.device-timeout ignored for %s", what
);
229 r
= unit_name_from_path(node
, ".device", &unit
);
231 return log_error_errno(r
, "Failed to make unit name from path: %m");
233 return write_drop_in_format(dir
, unit
, 50, "device-timeout",
234 "# Automatically generated by %s\n\n"
236 "JobRunningTimeoutSec=%s",
237 program_invocation_short_name
,
241 int generator_write_device_deps(
247 /* fstab records that specify _netdev option should apply the network
248 * ordering on the actual device depending on network connection. If we
249 * are not mounting real device (NFS, CIFS), we rely on _netdev effect
250 * on the mount unit itself. */
252 _cleanup_free_
char *node
= NULL
, *unit
= NULL
;
255 if (!fstab_test_option(opts
, "_netdev\0"))
258 node
= fstab_node_to_udev_node(what
);
262 /* Nothing to apply dependencies to. */
263 if (!is_device_path(node
))
266 r
= unit_name_from_path(node
, ".device", &unit
);
268 return log_error_errno(r
, "Failed to make unit name from path \"%s\": %m",
271 /* See mount_add_default_dependencies for explanation why we create such
273 return write_drop_in_format(dir
, unit
, 50, "netdev-dependencies",
274 "# Automatically generated by %s\n\n"
276 "After=" SPECIAL_NETWORK_ONLINE_TARGET
" " SPECIAL_NETWORK_TARGET
"\n"
277 "Wants=" SPECIAL_NETWORK_ONLINE_TARGET
"\n",
278 program_invocation_short_name
);
281 int generator_write_initrd_root_device_deps(const char *dir
, const char *what
) {
282 _cleanup_free_
char *unit
= NULL
;
285 r
= unit_name_from_path(what
, ".device", &unit
);
287 return log_error_errno(r
, "Failed to make unit name from path \"%s\": %m",
290 return write_drop_in_format(dir
, SPECIAL_INITRD_ROOT_DEVICE_TARGET
, 50, "root-device",
291 "# Automatically generated by %s\n\n"
295 program_invocation_short_name
,
300 int generator_hook_up_mkswap(
304 _cleanup_free_
char *node
= NULL
, *unit
= NULL
, *escaped
= NULL
, *where_unit
= NULL
;
305 _cleanup_fclose_
FILE *f
= NULL
;
306 const char *unit_file
;
309 node
= fstab_node_to_udev_node(what
);
313 /* Nothing to work on. */
314 if (!is_device_path(node
))
315 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
316 "Cannot format something that is not a device node: %s",
319 r
= unit_name_from_path_instance("systemd-mkswap", node
, ".service", &unit
);
321 return log_error_errno(r
, "Failed to make unit instance name from path \"%s\": %m",
324 unit_file
= strjoina(dir
, "/", unit
);
325 log_debug("Creating %s", unit_file
);
327 escaped
= cescape(node
);
331 r
= unit_name_from_path(what
, ".swap", &where_unit
);
333 return log_error_errno(r
, "Failed to make unit name from path \"%s\": %m",
336 f
= fopen(unit_file
, "wxe");
338 return log_error_errno(errno
, "Failed to create unit file %s: %m",
342 "# Automatically generated by %s\n\n"
344 "Description=Make Swap on %%f\n"
345 "Documentation=man:systemd-mkswap@.service(8)\n"
346 "DefaultDependencies=no\n"
347 "BindsTo=%%i.device\n"
348 "Conflicts=shutdown.target\n"
350 "Before=shutdown.target %s\n"
354 "RemainAfterExit=yes\n"
355 "ExecStart="SYSTEMD_MAKEFS_PATH
" swap %s\n"
357 program_invocation_short_name
,
361 r
= fflush_and_check(f
);
363 return log_error_errno(r
, "Failed to write unit file %s: %m", unit_file
);
365 return generator_add_symlink(dir
, where_unit
, "requires", unit
);
368 int generator_hook_up_mkfs(
374 _cleanup_free_
char *node
= NULL
, *unit
= NULL
, *escaped
= NULL
, *where_unit
= NULL
;
375 _cleanup_fclose_
FILE *f
= NULL
;
376 const char *unit_file
;
379 node
= fstab_node_to_udev_node(what
);
383 /* Nothing to work on. */
384 if (!is_device_path(node
))
385 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
386 "Cannot format something that is not a device node: %s",
389 if (!type
|| streq(type
, "auto"))
390 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
391 "Cannot format partition %s, filesystem type is not specified",
394 r
= unit_name_from_path_instance("systemd-makefs", node
, ".service", &unit
);
396 return log_error_errno(r
, "Failed to make unit instance name from path \"%s\": %m",
399 unit_file
= strjoina(dir
, "/", unit
);
400 log_debug("Creating %s", unit_file
);
402 escaped
= cescape(node
);
406 r
= unit_name_from_path(where
, ".mount", &where_unit
);
408 return log_error_errno(r
, "Failed to make unit name from path \"%s\": %m",
411 f
= fopen(unit_file
, "wxe");
413 return log_error_errno(errno
, "Failed to create unit file %s: %m",
417 "# Automatically generated by %s\n\n"
419 "Description=Make File System on %%f\n"
420 "Documentation=man:systemd-makefs@.service(8)\n"
421 "DefaultDependencies=no\n"
422 "BindsTo=%%i.device\n"
423 "Conflicts=shutdown.target\n"
425 /* fsck might or might not be used, so let's be safe and order
426 * ourselves before both systemd-fsck@.service and the mount unit. */
427 "Before=shutdown.target systemd-fsck@%%i.service %s\n"
431 "RemainAfterExit=yes\n"
432 "ExecStart="SYSTEMD_MAKEFS_PATH
" %s %s\n"
434 program_invocation_short_name
,
438 // XXX: what about local-fs-pre.target?
440 r
= fflush_and_check(f
);
442 return log_error_errno(r
, "Failed to write unit file %s: %m", unit_file
);
444 return generator_add_symlink(dir
, where_unit
, "requires", unit
);
447 int generator_hook_up_growfs(
450 const char *target
) {
452 _cleanup_free_
char *unit
= NULL
, *escaped
= NULL
, *where_unit
= NULL
;
453 _cleanup_fclose_
FILE *f
= NULL
;
454 const char *unit_file
;
457 escaped
= cescape(where
);
461 r
= unit_name_from_path_instance("systemd-growfs", where
, ".service", &unit
);
463 return log_error_errno(r
, "Failed to make unit instance name from path \"%s\": %m",
466 r
= unit_name_from_path(where
, ".mount", &where_unit
);
468 return log_error_errno(r
, "Failed to make unit name from path \"%s\": %m",
471 unit_file
= strjoina(dir
, "/", unit
);
472 log_debug("Creating %s", unit_file
);
474 f
= fopen(unit_file
, "wxe");
476 return log_error_errno(errno
, "Failed to create unit file %s: %m",
480 "# Automatically generated by %s\n\n"
482 "Description=Grow File System on %%f\n"
483 "Documentation=man:systemd-growfs@.service(8)\n"
484 "DefaultDependencies=no\n"
485 "BindsTo=%%i.mount\n"
486 "Conflicts=shutdown.target\n"
488 "Before=shutdown.target %s\n"
492 "RemainAfterExit=yes\n"
493 "ExecStart="SYSTEMD_GROWFS_PATH
" %s\n"
495 program_invocation_short_name
,
499 return generator_add_symlink(dir
, where_unit
, "wants", unit
);
502 int generator_enable_remount_fs_service(const char *dir
) {
503 /* Pull in systemd-remount-fs.service */
504 return generator_add_symlink(dir
, SPECIAL_LOCAL_FS_TARGET
, "wants",
505 SYSTEM_DATA_UNIT_PATH
"/" SPECIAL_REMOUNT_FS_SERVICE
);
508 void log_setup_generator(void) {
509 log_set_prohibit_ipc(true);