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