Line data Source code
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 0 : 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 0 : unit = prefix_roota(dest, name);
35 :
36 0 : r = fopen_unlocked(unit, "wxe", &f);
37 0 : if (r < 0) {
38 0 : if (source && r == -EEXIST)
39 0 : 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 0 : return log_error_errno(r,
44 : "Failed to create unit file %s: %m",
45 : unit);
46 : }
47 :
48 0 : fprintf(f,
49 : "# Automatically generated by %s\n\n",
50 : program_invocation_short_name);
51 :
52 0 : *file = f;
53 0 : return 0;
54 : }
55 :
56 52 : 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 260 : from = path_is_absolute(src) ? src : strjoina("../", src);
63 780 : to = strjoina(dir, "/", dst, ".", dep_type, "/", basename(src));
64 :
65 52 : mkdir_parents_label(to, 0755);
66 52 : if (symlink(from, to) < 0)
67 25 : if (errno != EEXIST)
68 0 : return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
69 :
70 52 : return 0;
71 : }
72 :
73 0 : static int write_fsck_sysroot_service(const char *dir, const char *what) {
74 0 : _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
75 0 : _cleanup_fclose_ FILE *f = NULL;
76 : const char *unit;
77 : int r;
78 :
79 0 : escaped = specifier_escape(what);
80 0 : if (!escaped)
81 0 : return log_oom();
82 :
83 0 : escaped2 = cescape(escaped);
84 0 : if (!escaped2)
85 0 : return log_oom();
86 :
87 0 : unit = strjoina(dir, "/"SPECIAL_FSCK_ROOT_SERVICE);
88 0 : log_debug("Creating %s", unit);
89 :
90 0 : r = unit_name_from_path(what, ".device", &device);
91 0 : if (r < 0)
92 0 : return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
93 :
94 0 : f = fopen(unit, "wxe");
95 0 : if (!f)
96 0 : return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
97 :
98 0 : 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 0 : r = fflush_and_check(f);
120 0 : if (r < 0)
121 0 : return log_error_errno(r, "Failed to write unit file %s: %m", unit);
122 :
123 0 : return 0;
124 : }
125 :
126 0 : 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 0 : assert(f);
136 0 : assert(dir);
137 0 : assert(what);
138 0 : assert(where);
139 :
140 0 : if (!is_device_path(what)) {
141 0 : log_warning("Checking was requested for \"%s\", but it is not a device.", what);
142 0 : return 0;
143 : }
144 :
145 0 : if (!isempty(fstype) && !streq(fstype, "auto")) {
146 0 : r = fsck_exists(fstype);
147 0 : if (r < 0)
148 0 : log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
149 0 : else if (r == 0) {
150 : /* treat missing check as essentially OK */
151 0 : log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
152 0 : return 0;
153 : }
154 : }
155 :
156 0 : if (path_equal(where, "/")) {
157 : const char *lnk;
158 :
159 0 : lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
160 :
161 0 : (void) mkdir_parents(lnk, 0755);
162 0 : if (symlink(SYSTEM_DATA_UNIT_PATH "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
163 0 : return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
164 :
165 : } else {
166 0 : _cleanup_free_ char *_fsck = NULL;
167 : const char *fsck, *dep;
168 :
169 0 : if (in_initrd() && path_equal(where, "/sysroot")) {
170 0 : r = write_fsck_sysroot_service(dir, what);
171 0 : if (r < 0)
172 0 : return r;
173 :
174 0 : fsck = SPECIAL_FSCK_ROOT_SERVICE;
175 0 : 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 0 : dep = !in_initrd() && path_equal(where, "/usr") ? "Wants" : "Requires";
183 :
184 0 : r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
185 0 : if (r < 0)
186 0 : return log_error_errno(r, "Failed to create fsck service name: %m");
187 :
188 0 : fsck = _fsck;
189 : }
190 :
191 0 : fprintf(f,
192 : "%1$s=%2$s\n"
193 : "After=%2$s\n",
194 : dep, fsck);
195 : }
196 :
197 0 : return 0;
198 : }
199 :
200 0 : 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 0 : _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
212 : usec_t u;
213 : int r;
214 :
215 0 : r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
216 : "x-systemd.device-timeout\0",
217 : NULL, &timeout, filtered);
218 0 : if (r <= 0)
219 0 : return r;
220 :
221 0 : r = parse_sec_fix_0(timeout, &u);
222 0 : if (r < 0) {
223 0 : log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
224 0 : return 0;
225 : }
226 :
227 0 : node = fstab_node_to_udev_node(what);
228 0 : if (!node)
229 0 : return log_oom();
230 0 : if (!is_device_path(node)) {
231 0 : log_warning("x-systemd.device-timeout ignored for %s", what);
232 0 : return 0;
233 : }
234 :
235 0 : r = unit_name_from_path(node, ".device", &unit);
236 0 : if (r < 0)
237 0 : return log_error_errno(r, "Failed to make unit name from path: %m");
238 :
239 0 : return write_drop_in_format(dir, unit, 50, "device-timeout",
240 : "# Automatically generated by %s\n\n"
241 : "[Unit]\n"
242 : "JobRunningTimeoutSec=%s",
243 : program_invocation_short_name,
244 : timeout);
245 : }
246 :
247 0 : int generator_write_device_deps(
248 : const char *dir,
249 : const char *what,
250 : const char *where,
251 : const char *opts) {
252 :
253 : /* fstab records that specify _netdev option should apply the network
254 : * ordering on the actual device depending on network connection. If we
255 : * are not mounting real device (NFS, CIFS), we rely on _netdev effect
256 : * on the mount unit itself. */
257 :
258 0 : _cleanup_free_ char *node = NULL, *unit = NULL;
259 : int r;
260 :
261 0 : if (!fstab_test_option(opts, "_netdev\0"))
262 0 : return 0;
263 :
264 0 : node = fstab_node_to_udev_node(what);
265 0 : if (!node)
266 0 : return log_oom();
267 :
268 : /* Nothing to apply dependencies to. */
269 0 : if (!is_device_path(node))
270 0 : return 0;
271 :
272 0 : r = unit_name_from_path(node, ".device", &unit);
273 0 : if (r < 0)
274 0 : return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
275 : node);
276 :
277 : /* See mount_add_default_dependencies for explanation why we create such
278 : * dependencies. */
279 0 : return write_drop_in_format(dir, unit, 50, "netdev-dependencies",
280 : "# Automatically generated by %s\n\n"
281 : "[Unit]\n"
282 : "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n"
283 : "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n",
284 : program_invocation_short_name);
285 : }
286 :
287 0 : int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
288 0 : _cleanup_free_ char *unit = NULL;
289 : int r;
290 :
291 0 : r = unit_name_from_path(what, ".device", &unit);
292 0 : if (r < 0)
293 0 : return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
294 : what);
295 :
296 0 : return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
297 : "# Automatically generated by %s\n\n"
298 : "[Unit]\n"
299 : "Requires=%s\n"
300 : "After=%s",
301 : program_invocation_short_name,
302 : unit,
303 : unit);
304 : }
305 :
306 0 : int generator_hook_up_mkswap(
307 : const char *dir,
308 : const char *what) {
309 :
310 0 : _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
311 0 : _cleanup_fclose_ FILE *f = NULL;
312 : const char *unit_file;
313 : int r;
314 :
315 0 : node = fstab_node_to_udev_node(what);
316 0 : if (!node)
317 0 : return log_oom();
318 :
319 : /* Nothing to work on. */
320 0 : if (!is_device_path(node))
321 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
322 : "Cannot format something that is not a device node: %s",
323 : node);
324 :
325 0 : r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
326 0 : if (r < 0)
327 0 : return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
328 : node);
329 :
330 0 : unit_file = prefix_roota(dir, unit);
331 0 : log_debug("Creating %s", unit_file);
332 :
333 0 : escaped = cescape(node);
334 0 : if (!escaped)
335 0 : return log_oom();
336 :
337 0 : r = unit_name_from_path(what, ".swap", &where_unit);
338 0 : if (r < 0)
339 0 : return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
340 : what);
341 :
342 0 : f = fopen(unit_file, "wxe");
343 0 : if (!f)
344 0 : return log_error_errno(errno, "Failed to create unit file %s: %m",
345 : unit_file);
346 :
347 0 : fprintf(f,
348 : "# Automatically generated by %s\n\n"
349 : "[Unit]\n"
350 : "Description=Make Swap on %%f\n"
351 : "Documentation=man:systemd-mkswap@.service(8)\n"
352 : "DefaultDependencies=no\n"
353 : "BindsTo=%%i.device\n"
354 : "Conflicts=shutdown.target\n"
355 : "After=%%i.device\n"
356 : "Before=shutdown.target %s\n"
357 : "\n"
358 : "[Service]\n"
359 : "Type=oneshot\n"
360 : "RemainAfterExit=yes\n"
361 : "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
362 : "TimeoutSec=0\n",
363 : program_invocation_short_name,
364 : where_unit,
365 : escaped);
366 :
367 0 : r = fflush_and_check(f);
368 0 : if (r < 0)
369 0 : return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
370 :
371 0 : return generator_add_symlink(dir, where_unit, "requires", unit);
372 : }
373 :
374 0 : int generator_hook_up_mkfs(
375 : const char *dir,
376 : const char *what,
377 : const char *where,
378 : const char *type) {
379 :
380 0 : _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
381 0 : _cleanup_fclose_ FILE *f = NULL;
382 : const char *unit_file;
383 : int r;
384 :
385 0 : node = fstab_node_to_udev_node(what);
386 0 : if (!node)
387 0 : return log_oom();
388 :
389 : /* Nothing to work on. */
390 0 : if (!is_device_path(node))
391 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
392 : "Cannot format something that is not a device node: %s",
393 : node);
394 :
395 0 : if (!type || streq(type, "auto"))
396 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
397 : "Cannot format partition %s, filesystem type is not specified",
398 : node);
399 :
400 0 : r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit);
401 0 : if (r < 0)
402 0 : return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
403 : node);
404 :
405 0 : unit_file = prefix_roota(dir, unit);
406 0 : log_debug("Creating %s", unit_file);
407 :
408 0 : escaped = cescape(node);
409 0 : if (!escaped)
410 0 : return log_oom();
411 :
412 0 : r = unit_name_from_path(where, ".mount", &where_unit);
413 0 : if (r < 0)
414 0 : return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
415 : where);
416 :
417 0 : f = fopen(unit_file, "wxe");
418 0 : if (!f)
419 0 : return log_error_errno(errno, "Failed to create unit file %s: %m",
420 : unit_file);
421 :
422 0 : fprintf(f,
423 : "# Automatically generated by %s\n\n"
424 : "[Unit]\n"
425 : "Description=Make File System on %%f\n"
426 : "Documentation=man:systemd-makefs@.service(8)\n"
427 : "DefaultDependencies=no\n"
428 : "BindsTo=%%i.device\n"
429 : "Conflicts=shutdown.target\n"
430 : "After=%%i.device\n"
431 : /* fsck might or might not be used, so let's be safe and order
432 : * ourselves before both systemd-fsck@.service and the mount unit. */
433 : "Before=shutdown.target systemd-fsck@%%i.service %s\n"
434 : "\n"
435 : "[Service]\n"
436 : "Type=oneshot\n"
437 : "RemainAfterExit=yes\n"
438 : "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
439 : "TimeoutSec=0\n",
440 : program_invocation_short_name,
441 : where_unit,
442 : type,
443 : escaped);
444 : // XXX: what about local-fs-pre.target?
445 :
446 0 : r = fflush_and_check(f);
447 0 : if (r < 0)
448 0 : return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
449 :
450 0 : return generator_add_symlink(dir, where_unit, "requires", unit);
451 : }
452 :
453 0 : int generator_hook_up_growfs(
454 : const char *dir,
455 : const char *where,
456 : const char *target) {
457 :
458 0 : _cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL;
459 0 : _cleanup_fclose_ FILE *f = NULL;
460 : const char *unit_file;
461 : int r;
462 :
463 0 : escaped = cescape(where);
464 0 : if (!escaped)
465 0 : return log_oom();
466 :
467 0 : r = unit_name_from_path_instance("systemd-growfs", where, ".service", &unit);
468 0 : if (r < 0)
469 0 : return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
470 : where);
471 :
472 0 : r = unit_name_from_path(where, ".mount", &where_unit);
473 0 : if (r < 0)
474 0 : return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
475 : where);
476 :
477 0 : unit_file = prefix_roota(dir, unit);
478 0 : log_debug("Creating %s", unit_file);
479 :
480 0 : f = fopen(unit_file, "wxe");
481 0 : if (!f)
482 0 : return log_error_errno(errno, "Failed to create unit file %s: %m",
483 : unit_file);
484 :
485 0 : fprintf(f,
486 : "# Automatically generated by %s\n\n"
487 : "[Unit]\n"
488 : "Description=Grow File System on %%f\n"
489 : "Documentation=man:systemd-growfs@.service(8)\n"
490 : "DefaultDependencies=no\n"
491 : "BindsTo=%%i.mount\n"
492 : "Conflicts=shutdown.target\n"
493 : "After=%%i.mount\n"
494 : "Before=shutdown.target %s\n"
495 : "\n"
496 : "[Service]\n"
497 : "Type=oneshot\n"
498 : "RemainAfterExit=yes\n"
499 : "ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
500 : "TimeoutSec=0\n",
501 : program_invocation_short_name,
502 : target,
503 : escaped);
504 :
505 0 : return generator_add_symlink(dir, where_unit, "wants", unit);
506 : }
507 :
508 0 : int generator_enable_remount_fs_service(const char *dir) {
509 : /* Pull in systemd-remount-fs.service */
510 0 : return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants",
511 : SYSTEM_DATA_UNIT_PATH "/" SPECIAL_REMOUNT_FS_SERVICE);
512 : }
513 :
514 19 : void log_setup_generator(void) {
515 19 : log_set_prohibit_ipc(true);
516 19 : log_setup_service();
517 19 : }
|