Branch data 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 : 208 : 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 [ + - + + : 1040 : from = path_is_absolute(src) ? src : strjoina("../", src);
+ - - + -
+ + + +
- ]
63 [ + + + - : 3120 : to = strjoina(dir, "/", dst, ".", dep_type, "/", basename(src));
- + - + +
+ + - ]
64 : :
65 : 208 : mkdir_parents_label(to, 0755);
66 [ + + ]: 208 : if (symlink(from, to) < 0)
67 [ - + ]: 100 : if (errno != EEXIST)
68 [ # # ]: 0 : return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
69 : :
70 : 208 : 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 : 76 : void log_setup_generator(void) {
515 : 76 : log_set_prohibit_ipc(true);
516 : 76 : log_setup_service();
517 : 76 : }
|