]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/generator.c
Merge pull request #10787 from yuwata/libudev-cleanups
[thirdparty/systemd.git] / src / shared / generator.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e48fdd84 2
a8fbdf54 3#include <errno.h>
fb883e75 4#include <stdio_ext.h>
e48fdd84
LP
5#include <unistd.h>
6
b5efdb8a 7#include "alloc-util.h"
4f5dd394
LP
8#include "dropin.h"
9#include "escape.h"
3ffd4af2 10#include "fd-util.h"
4f5dd394
LP
11#include "fileio.h"
12#include "fstab-util.h"
3ffd4af2 13#include "generator.h"
a8fbdf54
TA
14#include "log.h"
15#include "macro.h"
e48fdd84 16#include "mkdir.h"
4f5dd394
LP
17#include "path-util.h"
18#include "special.h"
98bad05e 19#include "specifier.h"
07630cea 20#include "string-util.h"
a8fbdf54 21#include "time-util.h"
e48fdd84 22#include "unit-name.h"
4f5dd394 23#include "util.h"
e48fdd84 24
fb883e75
ZJS
25int generator_open_unit_file(
26 const char *dest,
27 const char *source,
28 const char *name,
29 FILE **file) {
30
31 const char *unit;
32 FILE *f;
33
34 unit = strjoina(dest, "/", name);
35
36 f = fopen(unit, "wxe");
37 if (!f) {
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?",
41 unit, source);
42 else
43 return log_error_errno(errno,
44 "Failed to create unit file %s: %m",
45 unit);
46 }
47
48 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
49
50 fprintf(f,
51 "# Automatically generated by %s\n\n",
52 program_invocation_short_name);
53
54 *file = f;
55 return 0;
56}
57
b559616f
ZJS
58int generator_add_symlink(const char *root, const char *dst, const char *dep_type, const char *src) {
59 /* Adds a symlink from <dst>.<dep_type>.d/ to ../<src> */
60
61 const char *from, *to;
62
63 from = strjoina("../", src);
64 to = strjoina(root, "/", dst, ".", dep_type, "/", src);
65
66 mkdir_parents_label(to, 0755);
67 if (symlink(from, to) < 0)
7f0cc637
ZJS
68 if (errno != EEXIST)
69 return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
b559616f
ZJS
70
71 return 0;
72}
73
4dda4e63 74static int write_fsck_sysroot_service(const char *dir, const char *what) {
98bad05e 75 _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
4dda4e63 76 _cleanup_fclose_ FILE *f = NULL;
85eca92e 77 const char *unit;
4dda4e63
ZJS
78 int r;
79
98bad05e 80 escaped = specifier_escape(what);
fa05e972
AB
81 if (!escaped)
82 return log_oom();
83
98bad05e
LP
84 escaped2 = cescape(escaped);
85 if (!escaped2)
86 return log_oom();
87
4dda4e63
ZJS
88 unit = strjoina(dir, "/systemd-fsck-root.service");
89 log_debug("Creating %s", unit);
90
91 r = unit_name_from_path(what, ".device", &device);
92 if (r < 0)
93 return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
94
95 f = fopen(unit, "wxe");
96 if (!f)
97 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
98
99 fprintf(f,
100 "# Automatically generated by %1$s\n\n"
101 "[Unit]\n"
4dda4e63 102 "Description=File System Check on %2$s\n"
da495a03 103 "Documentation=man:systemd-fsck-root.service(8)\n"
4dda4e63
ZJS
104 "DefaultDependencies=no\n"
105 "BindsTo=%3$s\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
2e852276 159 lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service");
e48fdd84
LP
160
161 mkdir_parents(lnk, 0755);
4a62c710
MS
162 if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0)
163 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
e48fdd84
LP
164
165 } else {
4dda4e63
ZJS
166 _cleanup_free_ char *_fsck = NULL;
167 const char *fsck;
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 = "systemd-fsck-root.service";
175 } else {
176 r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
177 if (r < 0)
178 return log_error_errno(r, "Failed to create fsck service name: %m");
179
180 fsck = _fsck;
181 }
e48fdd84
LP
182
183 fprintf(f,
f32b43bd 184 "Requires=%1$s\n"
2e852276 185 "After=%1$s\n",
e48fdd84
LP
186 fsck);
187 }
188
189 return 0;
190}
29686440 191
2e852276
ZJS
192int generator_write_timeouts(
193 const char *dir,
194 const char *what,
195 const char *where,
196 const char *opts,
197 char **filtered) {
29686440
ZJS
198
199 /* Allow configuration how long we wait for a device that
200 * backs a mount point to show up. This is useful to support
201 * endless device timeouts for devices that show up only after
202 * user input, like crypto devices. */
203
d15d0333 204 _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
29686440
ZJS
205 usec_t u;
206 int r;
29686440 207
0004f698
ZJS
208 r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
209 "x-systemd.device-timeout\0",
d15d0333
ZJS
210 NULL, &timeout, filtered);
211 if (r <= 0)
212 return r;
b3208b66 213
0004f698 214 r = parse_sec_fix_0(timeout, &u);
29686440 215 if (r < 0) {
7410616c 216 log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
29686440
ZJS
217 return 0;
218 }
219
220 node = fstab_node_to_udev_node(what);
221 if (!node)
222 return log_oom();
c67bd1f7
N
223 if (!is_device_path(node)) {
224 log_warning("x-systemd.device-timeout ignored for %s", what);
225 return 0;
226 }
29686440 227
7410616c
LP
228 r = unit_name_from_path(node, ".device", &unit);
229 if (r < 0)
230 return log_error_errno(r, "Failed to make unit name from path: %m");
29686440 231
8eea8687
ZJS
232 return write_drop_in_format(dir, unit, 50, "device-timeout",
233 "# Automatically generated by %s\n\n"
acd53eaa
LP
234 "[Unit]\n"
235 "JobRunningTimeoutSec=%s",
236 program_invocation_short_name,
237 timeout);
29686440 238}
7163e1ca 239
4195077a
MK
240int generator_write_device_deps(
241 const char *dir,
242 const char *what,
243 const char *where,
244 const char *opts) {
245
246 /* fstab records that specify _netdev option should apply the network
247 * ordering on the actual device depending on network connection. If we
248 * are not mounting real device (NFS, CIFS), we rely on _netdev effect
249 * on the mount unit itself. */
250
251 _cleanup_free_ char *node = NULL, *unit = NULL;
252 int r;
253
254 if (!fstab_test_option(opts, "_netdev\0"))
255 return 0;
256
257 node = fstab_node_to_udev_node(what);
258 if (!node)
259 return log_oom();
260
261 /* Nothing to apply dependencies to. */
262 if (!is_device_path(node))
263 return 0;
264
265 r = unit_name_from_path(node, ".device", &unit);
266 if (r < 0)
da495a03
ZJS
267 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
268 node);
4195077a
MK
269
270 /* See mount_add_default_dependencies for explanation why we create such
271 * dependencies. */
272 return write_drop_in_format(dir, unit, 50, "netdev-dependencies",
273 "# Automatically generated by %s\n\n"
274 "[Unit]\n"
275 "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n"
276 "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n",
277 program_invocation_short_name);
278}
279
7163e1ca
DD
280int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
281 _cleanup_free_ char *unit = NULL;
282 int r;
283
284 r = unit_name_from_path(what, ".device", &unit);
285 if (r < 0)
da495a03
ZJS
286 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
287 what);
7163e1ca
DD
288
289 return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
290 "# Automatically generated by %s\n\n"
acd53eaa
LP
291 "[Unit]\n"
292 "Requires=%s\n"
293 "After=%s",
294 program_invocation_short_name,
295 unit,
296 unit);
7163e1ca 297}
da495a03
ZJS
298
299int generator_hook_up_mkswap(
300 const char *dir,
301 const char *what) {
302
303 _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
304 _cleanup_fclose_ FILE *f = NULL;
305 const char *unit_file;
306 int r;
307
308 node = fstab_node_to_udev_node(what);
309 if (!node)
310 return log_oom();
311
312 /* Nothing to work on. */
313 if (!is_device_path(node)) {
314 log_error("Cannot format something that is not a device node: %s", node);
315 return -EINVAL;
316 }
317
318 r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
319 if (r < 0)
320 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
321 node);
322
323 unit_file = strjoina(dir, "/", unit);
324 log_debug("Creating %s", unit_file);
325
326 escaped = cescape(node);
327 if (!escaped)
328 return log_oom();
329
330 r = unit_name_from_path(what, ".swap", &where_unit);
331 if (r < 0)
332 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
333 what);
334
335 f = fopen(unit_file, "wxe");
336 if (!f)
337 return log_error_errno(errno, "Failed to create unit file %s: %m",
338 unit_file);
339
340 fprintf(f,
341 "# Automatically generated by %s\n\n"
342 "[Unit]\n"
343 "Description=Make Swap on %%f\n"
344 "Documentation=man:systemd-mkswap@.service(8)\n"
345 "DefaultDependencies=no\n"
346 "BindsTo=%%i.device\n"
347 "After=%%i.device\n"
348 "Before=%s\n"
349 "Before=shutdown.target\n"
350 "\n"
351 "[Service]\n"
352 "Type=oneshot\n"
353 "RemainAfterExit=yes\n"
354 "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
355 "TimeoutSec=0\n",
356 program_invocation_short_name,
357 where_unit,
358 escaped);
359
360 r = fflush_and_check(f);
361 if (r < 0)
362 return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
363
364 return generator_add_symlink(dir, where_unit, "requires", unit);
365}
366
367int generator_hook_up_mkfs(
368 const char *dir,
369 const char *what,
370 const char *where,
371 const char *type) {
372
373 _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
374 _cleanup_fclose_ FILE *f = NULL;
375 const char *unit_file;
376 int r;
377
378 node = fstab_node_to_udev_node(what);
379 if (!node)
380 return log_oom();
381
382 /* Nothing to work on. */
383 if (!is_device_path(node)) {
384 log_error("Cannot format something that is not a device node: %s", node);
385 return -EINVAL;
386 }
387
388 if (!type || streq(type, "auto")) {
389 log_error("Cannot format partition %s, filesystem type is not specified", node);
390 return -EINVAL;
391 }
392
393 r = unit_name_from_path_instance("systemd-mkfs", node, ".service", &unit);
394 if (r < 0)
395 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
396 node);
397
398 unit_file = strjoina(dir, "/", unit);
399 log_debug("Creating %s", unit_file);
400
401 escaped = cescape(node);
402 if (!escaped)
403 return log_oom();
404
405 r = unit_name_from_path(where, ".mount", &where_unit);
406 if (r < 0)
407 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
408 where);
409
410 f = fopen(unit_file, "wxe");
411 if (!f)
412 return log_error_errno(errno, "Failed to create unit file %s: %m",
413 unit_file);
414
415 fprintf(f,
416 "# Automatically generated by %s\n\n"
417 "[Unit]\n"
418 "Description=Make File System on %%f\n"
419 "Documentation=man:systemd-mkfs@.service(8)\n"
420 "DefaultDependencies=no\n"
421 "BindsTo=%%i.device\n"
422 "After=%%i.device\n"
423 /* fsck might or might not be used, so let's be safe and order
424 * ourselves before both systemd-fsck@.service and the mount unit. */
425 "Before=systemd-fsck@%%i.service\n"
426 "Before=%s\n"
427 "Before=shutdown.target\n"
428 "\n"
429 "[Service]\n"
430 "Type=oneshot\n"
431 "RemainAfterExit=yes\n"
432 "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
433 "TimeoutSec=0\n",
434 program_invocation_short_name,
435 where_unit,
436 type,
437 escaped);
438 // XXX: what about local-fs-pre.target?
439
440 r = fflush_and_check(f);
441 if (r < 0)
442 return log_error_errno(r, "Failed to write unit file %s: %m", unit_file);
443
444 return generator_add_symlink(dir, where_unit, "requires", unit);
445}
7f2806d5
ZJS
446
447int generator_hook_up_growfs(
448 const char *dir,
449 const char *where,
450 const char *target) {
451
452 _cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL;
453 _cleanup_fclose_ FILE *f = NULL;
454 const char *unit_file;
455 int r;
456
457 escaped = cescape(where);
458 if (!escaped)
459 return log_oom();
460
461 r = unit_name_from_path_instance("systemd-growfs", where, ".service", &unit);
462 if (r < 0)
463 return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
464 where);
465
466 r = unit_name_from_path(where, ".mount", &where_unit);
467 if (r < 0)
468 return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
469 where);
470
471 unit_file = strjoina(dir, "/", unit);
472 log_debug("Creating %s", unit_file);
473
474 f = fopen(unit_file, "wxe");
475 if (!f)
476 return log_error_errno(errno, "Failed to create unit file %s: %m",
477 unit_file);
478
479 fprintf(f,
480 "# Automatically generated by %s\n\n"
481 "[Unit]\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 "After=%%i.mount\n"
487 "Before=shutdown.target\n"
488 "Before=%s\n"
489 "\n"
490 "[Service]\n"
491 "Type=oneshot\n"
492 "RemainAfterExit=yes\n"
493 "ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
494 "TimeoutSec=0\n",
495 program_invocation_short_name,
496 target,
497 escaped);
498
499 return generator_add_symlink(dir, where_unit, "wants", unit);
500}