]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
e48fdd84 | 2 | |
a8fbdf54 | 3 | #include <errno.h> |
e48fdd84 LP |
4 | #include <unistd.h> |
5 | ||
b5efdb8a | 6 | #include "alloc-util.h" |
dee29aeb | 7 | #include "cgroup-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" |
baa6a42d | 14 | #include "initrd-util.h" |
a8fbdf54 TA |
15 | #include "log.h" |
16 | #include "macro.h" | |
35cd0ba5 | 17 | #include "mkdir-label.h" |
fc5c6ecc | 18 | #include "mountpoint-util.h" |
4f5dd394 | 19 | #include "path-util.h" |
911017f0 | 20 | #include "process-util.h" |
4f5dd394 | 21 | #include "special.h" |
98bad05e | 22 | #include "specifier.h" |
07630cea | 23 | #include "string-util.h" |
a8fbdf54 | 24 | #include "time-util.h" |
8a84e0d7 | 25 | #include "tmpfile-util.h" |
e48fdd84 | 26 | #include "unit-name.h" |
e48fdd84 | 27 | |
8a84e0d7 | 28 | int generator_open_unit_file_full( |
e8ede6f5 | 29 | const char *dir, |
fb883e75 | 30 | const char *source, |
e8ede6f5 | 31 | const char *fn, |
8a84e0d7 | 32 | FILE **ret_file, |
7ceb76b6 | 33 | char **ret_final_path, |
8a84e0d7 | 34 | char **ret_temp_path) { |
fb883e75 | 35 | |
e8ede6f5 | 36 | _cleanup_free_ char *p = NULL; |
fb883e75 | 37 | FILE *f; |
41f6e627 | 38 | int r; |
fb883e75 | 39 | |
e8ede6f5 | 40 | assert(dir); |
8a84e0d7 | 41 | assert(ret_file); |
e8ede6f5 | 42 | |
8a84e0d7 AAF |
43 | /* If <ret_temp_path> is specified, it creates a temporary unit file and also returns its |
44 | * temporary path. */ | |
fb883e75 | 45 | |
8a84e0d7 AAF |
46 | if (ret_temp_path) { |
47 | r = fopen_temporary(dir, &f, &p); | |
48 | if (r < 0) | |
49 | return log_error_errno(r, "Failed to create temporary unit file in '%s': %m", dir); | |
e8ede6f5 | 50 | |
8a84e0d7 AAF |
51 | (void) fchmod(fileno(f), 0644); |
52 | ||
53 | *ret_temp_path = TAKE_PTR(p); | |
54 | } else { | |
55 | assert(fn); | |
56 | ||
57 | p = path_join(dir, fn); | |
58 | if (!p) | |
59 | return log_oom(); | |
60 | ||
61 | r = fopen_unlocked(p, "wxe", &f); | |
62 | if (r < 0) { | |
63 | if (source && r == -EEXIST) | |
64 | return log_error_errno(r, | |
65 | "Failed to create unit file '%s', as it already exists. Duplicate entry in '%s'?", | |
66 | p, source); | |
67 | ||
68 | return log_error_errno(r, "Failed to create unit file '%s': %m", p); | |
69 | } | |
fb883e75 ZJS |
70 | } |
71 | ||
fb883e75 ZJS |
72 | fprintf(f, |
73 | "# Automatically generated by %s\n\n", | |
74 | program_invocation_short_name); | |
75 | ||
8a84e0d7 | 76 | *ret_file = f; |
7ceb76b6 LP |
77 | |
78 | if (ret_final_path) | |
79 | *ret_final_path = TAKE_PTR(p); | |
80 | ||
fb883e75 ZJS |
81 | return 0; |
82 | } | |
83 | ||
0ba07f90 LP |
84 | int generator_add_symlink_full( |
85 | const char *dir, | |
86 | const char *dst, | |
87 | const char *dep_type, | |
88 | const char *src, | |
89 | const char *instance) { | |
90 | ||
91 | _cleanup_free_ char *dn = NULL, *fn = NULL, *instantiated = NULL, *to = NULL, *from = NULL; | |
03469b77 LP |
92 | int r; |
93 | ||
0ba07f90 LP |
94 | assert(dir); |
95 | assert(dst); | |
0ba07f90 LP |
96 | assert(src); |
97 | ||
3392079e LP |
98 | /* If 'dep_type' is specified adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise). |
99 | * | |
100 | * If 'dep_type' is NULL, it will create a symlink to <src> (i.e. create an alias. | |
101 | * | |
102 | * If <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */ | |
0ba07f90 LP |
103 | |
104 | r = path_extract_directory(src, &dn); | |
105 | if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → just a file name was passed */ | |
106 | return log_error_errno(r, "Failed to extract directory name from '%s': %m", src); | |
b559616f | 107 | |
0ba07f90 | 108 | r = path_extract_filename(src, &fn); |
03469b77 | 109 | if (r < 0) |
0ba07f90 LP |
110 | return log_error_errno(r, "Failed to extract file name from '%s': %m", src); |
111 | if (r == O_DIRECTORY) | |
112 | return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src); | |
113 | ||
114 | if (instance) { | |
115 | r = unit_name_replace_instance(fn, instance, &instantiated); | |
116 | if (r < 0) | |
117 | return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance); | |
118 | } | |
b559616f | 119 | |
3392079e LP |
120 | if (dep_type) { /* Create a .wants/ style dep */ |
121 | from = path_join(dn ?: "..", fn); | |
122 | if (!from) | |
123 | return log_oom(); | |
0ba07f90 | 124 | |
3392079e LP |
125 | to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn); |
126 | } else { /* or create an alias */ | |
127 | from = dn ? path_join(dn, fn) : strdup(fn); | |
128 | if (!from) | |
129 | return log_oom(); | |
130 | ||
131 | to = strjoin(dir, "/", dst); | |
132 | } | |
0ba07f90 LP |
133 | if (!to) |
134 | return log_oom(); | |
b559616f | 135 | |
35cd0ba5 | 136 | (void) mkdir_parents_label(to, 0755); |
0ba07f90 LP |
137 | |
138 | if (symlink(from, to) < 0 && errno != EEXIST) | |
139 | return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to); | |
b559616f ZJS |
140 | |
141 | return 0; | |
142 | } | |
143 | ||
50072ccf LP |
144 | static int generator_add_ordering( |
145 | const char *dir, | |
146 | const char *src, | |
147 | const char *order, | |
148 | const char *dst, | |
149 | const char *instance) { | |
150 | ||
151 | _cleanup_free_ char *instantiated = NULL, *p = NULL, *fn = NULL; | |
152 | _cleanup_fclose_ FILE *f = NULL; | |
153 | const char *to; | |
154 | int r; | |
155 | ||
156 | assert(dir); | |
157 | assert(src); | |
158 | assert(order); | |
159 | assert(dst); | |
160 | ||
161 | /* Adds in an explicit ordering dependency of type <order> from <src> to <dst>. If <instance> is | |
162 | * specified, it is inserted into <dst>. */ | |
163 | ||
164 | if (instance) { | |
165 | r = unit_name_replace_instance(dst, instance, &instantiated); | |
166 | if (r < 0) | |
167 | return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", dst, instance); | |
168 | ||
169 | to = instantiated; | |
170 | } else | |
171 | to = dst; | |
172 | ||
173 | fn = strjoin(src, ".d/50-order-", to, ".conf"); | |
174 | if (!fn) | |
175 | return log_oom(); | |
176 | ||
177 | p = path_join(dir, fn); | |
178 | if (!p) | |
179 | return log_oom(); | |
180 | ||
181 | (void) mkdir_parents_label(p, 0755); | |
182 | ||
183 | r = fopen_unlocked(p, "wxe", &f); | |
184 | if (r < 0) | |
185 | return log_error_errno(r, "Failed to create '%s': %m", p); | |
186 | ||
187 | fprintf(f, | |
188 | "# Automatically generated by %s\n\n" | |
189 | "[Unit]\n" | |
190 | "%s=%s\n", | |
191 | program_invocation_short_name, | |
192 | order, | |
193 | to); | |
194 | ||
195 | r = fflush_and_check(f); | |
196 | if (r < 0) | |
197 | return log_error_errno(r, "Failed to write drop-in '%s': %m", p); | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
2b66f48e LP |
202 | static int write_fsck_sysroot_service( |
203 | const char *unit, /* Either SPECIAL_FSCK_ROOT_SERVICE or SPECIAL_FSCK_USR_SERVICE */ | |
204 | const char *dir, | |
205 | const char *what, | |
206 | const char *extra_after) { | |
207 | ||
98bad05e | 208 | _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL; |
4dda4e63 ZJS |
209 | _cleanup_fclose_ FILE *f = NULL; |
210 | int r; | |
211 | ||
121ce4a6 MY |
212 | assert(unit); |
213 | assert(dir); | |
214 | assert(what); | |
215 | ||
c3600c38 | 216 | /* Writes out special versions of systemd-fsck-root.service and systemd-fsck-usr.service for use in |
2b66f48e LP |
217 | * the initrd. The regular statically shipped versions of these unit files use / and /usr for as |
218 | * paths, which doesn't match what we need for the initrd (where the dirs are /sysroot + | |
219 | * /sysusr/usr), hence we overwrite those versions here. */ | |
220 | ||
98bad05e | 221 | escaped = specifier_escape(what); |
fa05e972 AB |
222 | if (!escaped) |
223 | return log_oom(); | |
224 | ||
98bad05e LP |
225 | escaped2 = cescape(escaped); |
226 | if (!escaped2) | |
227 | return log_oom(); | |
228 | ||
4dda4e63 ZJS |
229 | r = unit_name_from_path(what, ".device", &device); |
230 | if (r < 0) | |
231 | return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what); | |
232 | ||
121ce4a6 MY |
233 | r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f); |
234 | if (r < 0) | |
235 | return r; | |
4dda4e63 ZJS |
236 | |
237 | fprintf(f, | |
4dda4e63 | 238 | "[Unit]\n" |
121ce4a6 MY |
239 | "Description=File System Check on %1$s\n" |
240 | "Documentation=man:%2$s(8)\n" | |
ce37fb0d | 241 | "\n" |
4dda4e63 | 242 | "DefaultDependencies=no\n" |
121ce4a6 | 243 | "BindsTo=%3$s\n" |
ff9bf8d0 | 244 | "Conflicts=shutdown.target\n" |
121ce4a6 | 245 | "After=%4$s%5$slocal-fs-pre.target %3$s\n" |
4dda4e63 ZJS |
246 | "Before=shutdown.target\n" |
247 | "\n" | |
248 | "[Service]\n" | |
249 | "Type=oneshot\n" | |
250 | "RemainAfterExit=yes\n" | |
121ce4a6 | 251 | "ExecStart=" SYSTEMD_FSCK_PATH " %6$s\n" |
a9b837aa | 252 | "TimeoutSec=infinity\n", |
98bad05e | 253 | escaped, |
2b66f48e | 254 | unit, |
fa05e972 | 255 | device, |
2b66f48e LP |
256 | strempty(extra_after), |
257 | isempty(extra_after) ? "" : " ", | |
98bad05e | 258 | escaped2); |
4dda4e63 | 259 | |
2929b4a6 LP |
260 | r = fflush_and_check(f); |
261 | if (r < 0) | |
121ce4a6 | 262 | return log_error_errno(r, "Failed to write unit %s: %m", unit); |
4dda4e63 ZJS |
263 | |
264 | return 0; | |
265 | } | |
266 | ||
e48fdd84 LP |
267 | int generator_write_fsck_deps( |
268 | FILE *f, | |
2e852276 | 269 | const char *dir, |
e48fdd84 LP |
270 | const char *what, |
271 | const char *where, | |
6db615c1 | 272 | const char *fstype) { |
e48fdd84 | 273 | |
7410616c LP |
274 | int r; |
275 | ||
e48fdd84 | 276 | assert(f); |
2e852276 | 277 | assert(dir); |
6db615c1 LP |
278 | assert(what); |
279 | assert(where); | |
e48fdd84 | 280 | |
122860f1 LP |
281 | /* Let's do an early exit if we are invoked for the root and /usr/ trees in the initrd, to avoid |
282 | * generating confusing log messages */ | |
283 | if (in_initrd() && PATH_IN_SET(where, "/", "/usr")) { | |
284 | log_debug("Skipping fsck for %s in initrd.", where); | |
285 | return 0; | |
286 | } | |
287 | ||
e48fdd84 LP |
288 | if (!is_device_path(what)) { |
289 | log_warning("Checking was requested for \"%s\", but it is not a device.", what); | |
290 | return 0; | |
291 | } | |
292 | ||
6db615c1 | 293 | if (!isempty(fstype) && !streq(fstype, "auto")) { |
13556724 | 294 | r = fsck_exists_for_fstype(fstype); |
85eca92e LP |
295 | if (r < 0) |
296 | log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype); | |
297 | else if (r == 0) { | |
e48fdd84 | 298 | /* treat missing check as essentially OK */ |
85eca92e | 299 | log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype); |
571d0134 | 300 | return 0; |
85eca92e | 301 | } |
13556724 JK |
302 | } else { |
303 | r = fsck_exists(); | |
304 | if (r < 0) | |
305 | log_warning_errno(r, "Checking was requested for %s, but couldn't detect if the fsck command may be used, proceeding: %m", what); | |
306 | else if (r == 0) { | |
307 | /* treat missing fsck as essentially OK */ | |
308 | log_debug("Checking was requested for %s, but the fsck command does not exist.", what); | |
309 | return 0; | |
310 | } | |
e48fdd84 LP |
311 | } |
312 | ||
2e852276 | 313 | if (path_equal(where, "/")) { |
85eca92e | 314 | const char *lnk; |
e48fdd84 | 315 | |
599aee40 LP |
316 | /* We support running the fsck instance for the root fs while it is already mounted, for |
317 | * compatibility with non-initrd boots. It's ugly, but it is how it is. Since – unlike for | |
318 | * regular file systems – this means the ordering is reversed (i.e. mount *before* fsck) we | |
319 | * have a separate fsck unit for this, independent of systemd-fsck@.service. */ | |
320 | ||
cbdc9cfe | 321 | lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE); |
e48fdd84 | 322 | |
37dc34f7 | 323 | (void) mkdir_parents(lnk, 0755); |
835cf75a | 324 | if (symlink(SYSTEM_DATA_UNIT_DIR "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0) |
4a62c710 | 325 | return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); |
e48fdd84 LP |
326 | |
327 | } else { | |
4dda4e63 | 328 | _cleanup_free_ char *_fsck = NULL; |
afacf3fc | 329 | const char *fsck, *dep; |
4dda4e63 ZJS |
330 | |
331 | if (in_initrd() && path_equal(where, "/sysroot")) { | |
2b66f48e | 332 | r = write_fsck_sysroot_service(SPECIAL_FSCK_ROOT_SERVICE, dir, what, SPECIAL_INITRD_ROOT_DEVICE_TARGET); |
4dda4e63 ZJS |
333 | if (r < 0) |
334 | return r; | |
335 | ||
5ff8da10 | 336 | fsck = SPECIAL_FSCK_ROOT_SERVICE; |
afacf3fc | 337 | dep = "Requires"; |
2b66f48e LP |
338 | |
339 | } else if (in_initrd() && path_equal(where, "/sysusr/usr")) { | |
340 | r = write_fsck_sysroot_service(SPECIAL_FSCK_USR_SERVICE, dir, what, NULL); | |
341 | if (r < 0) | |
342 | return r; | |
343 | ||
344 | fsck = SPECIAL_FSCK_USR_SERVICE; | |
345 | dep = "Requires"; | |
4dda4e63 | 346 | } else { |
afacf3fc LP |
347 | /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires= |
348 | * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a | |
349 | * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then | |
350 | * we'd have to unmount /usr too. */ | |
351 | ||
122860f1 | 352 | dep = path_equal(where, "/usr") ? "Wants" : "Requires"; |
afacf3fc | 353 | |
4dda4e63 ZJS |
354 | r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck); |
355 | if (r < 0) | |
356 | return log_error_errno(r, "Failed to create fsck service name: %m"); | |
357 | ||
358 | fsck = _fsck; | |
359 | } | |
e48fdd84 LP |
360 | |
361 | fprintf(f, | |
afacf3fc LP |
362 | "%1$s=%2$s\n" |
363 | "After=%2$s\n", | |
364 | dep, fsck); | |
e48fdd84 LP |
365 | } |
366 | ||
367 | return 0; | |
368 | } | |
29686440 | 369 | |
2e852276 ZJS |
370 | int generator_write_timeouts( |
371 | const char *dir, | |
372 | const char *what, | |
373 | const char *where, | |
374 | const char *opts, | |
375 | char **filtered) { | |
29686440 | 376 | |
c2f4bcfc ZJS |
377 | /* Configure how long we wait for a device that backs a mount point or a |
378 | * swap partition to show up. This is useful to support endless device timeouts | |
379 | * for devices that show up only after user input, like crypto devices. */ | |
29686440 | 380 | |
d15d0333 | 381 | _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL; |
29686440 ZJS |
382 | usec_t u; |
383 | int r; | |
29686440 | 384 | |
0004f698 ZJS |
385 | r = fstab_filter_options(opts, "comment=systemd.device-timeout\0" |
386 | "x-systemd.device-timeout\0", | |
ff0c31bc | 387 | NULL, &timeout, NULL, filtered); |
924f6503 ZJS |
388 | if (r < 0) { |
389 | log_warning_errno(r, "Failed to parse fstab options, ignoring: %m"); | |
390 | return 0; | |
391 | } | |
392 | if (r == 0) | |
393 | return 0; | |
b3208b66 | 394 | |
0004f698 | 395 | r = parse_sec_fix_0(timeout, &u); |
29686440 | 396 | if (r < 0) { |
7410616c | 397 | log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout); |
29686440 ZJS |
398 | return 0; |
399 | } | |
400 | ||
401 | node = fstab_node_to_udev_node(what); | |
402 | if (!node) | |
403 | return log_oom(); | |
c67bd1f7 N |
404 | if (!is_device_path(node)) { |
405 | log_warning("x-systemd.device-timeout ignored for %s", what); | |
406 | return 0; | |
407 | } | |
29686440 | 408 | |
7410616c LP |
409 | r = unit_name_from_path(node, ".device", &unit); |
410 | if (r < 0) | |
411 | return log_error_errno(r, "Failed to make unit name from path: %m"); | |
29686440 | 412 | |
8eea8687 | 413 | return write_drop_in_format(dir, unit, 50, "device-timeout", |
7cecc563 ZJS |
414 | "# Automatically generated by %s\n" |
415 | "# from supplied options \"%s\"\n\n" | |
acd53eaa LP |
416 | "[Unit]\n" |
417 | "JobRunningTimeoutSec=%s", | |
418 | program_invocation_short_name, | |
7cecc563 | 419 | opts, |
acd53eaa | 420 | timeout); |
29686440 | 421 | } |
7163e1ca | 422 | |
4195077a MK |
423 | int generator_write_device_deps( |
424 | const char *dir, | |
425 | const char *what, | |
426 | const char *where, | |
427 | const char *opts) { | |
428 | ||
429 | /* fstab records that specify _netdev option should apply the network | |
430 | * ordering on the actual device depending on network connection. If we | |
431 | * are not mounting real device (NFS, CIFS), we rely on _netdev effect | |
432 | * on the mount unit itself. */ | |
433 | ||
434 | _cleanup_free_ char *node = NULL, *unit = NULL; | |
435 | int r; | |
436 | ||
ad8f1b0f FB |
437 | if (fstab_is_extrinsic(where, opts)) |
438 | return 0; | |
439 | ||
4195077a MK |
440 | if (!fstab_test_option(opts, "_netdev\0")) |
441 | return 0; | |
442 | ||
443 | node = fstab_node_to_udev_node(what); | |
444 | if (!node) | |
445 | return log_oom(); | |
446 | ||
447 | /* Nothing to apply dependencies to. */ | |
448 | if (!is_device_path(node)) | |
449 | return 0; | |
450 | ||
451 | r = unit_name_from_path(node, ".device", &unit); | |
452 | if (r < 0) | |
da495a03 ZJS |
453 | return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", |
454 | node); | |
4195077a MK |
455 | |
456 | /* See mount_add_default_dependencies for explanation why we create such | |
457 | * dependencies. */ | |
458 | return write_drop_in_format(dir, unit, 50, "netdev-dependencies", | |
459 | "# Automatically generated by %s\n\n" | |
460 | "[Unit]\n" | |
461 | "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n" | |
462 | "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n", | |
463 | program_invocation_short_name); | |
464 | } | |
465 | ||
7163e1ca DD |
466 | int generator_write_initrd_root_device_deps(const char *dir, const char *what) { |
467 | _cleanup_free_ char *unit = NULL; | |
468 | int r; | |
469 | ||
470 | r = unit_name_from_path(what, ".device", &unit); | |
471 | if (r < 0) | |
da495a03 ZJS |
472 | return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", |
473 | what); | |
7163e1ca DD |
474 | |
475 | return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device", | |
476 | "# Automatically generated by %s\n\n" | |
acd53eaa LP |
477 | "[Unit]\n" |
478 | "Requires=%s\n" | |
479 | "After=%s", | |
480 | program_invocation_short_name, | |
481 | unit, | |
482 | unit); | |
7163e1ca | 483 | } |
da495a03 ZJS |
484 | |
485 | int generator_hook_up_mkswap( | |
486 | const char *dir, | |
487 | const char *what) { | |
488 | ||
489 | _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; | |
490 | _cleanup_fclose_ FILE *f = NULL; | |
da495a03 ZJS |
491 | int r; |
492 | ||
121ce4a6 MY |
493 | assert(dir); |
494 | assert(what); | |
495 | ||
da495a03 ZJS |
496 | node = fstab_node_to_udev_node(what); |
497 | if (!node) | |
498 | return log_oom(); | |
499 | ||
500 | /* Nothing to work on. */ | |
baaa35ad ZJS |
501 | if (!is_device_path(node)) |
502 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
503 | "Cannot format something that is not a device node: %s", | |
504 | node); | |
da495a03 ZJS |
505 | |
506 | r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit); | |
507 | if (r < 0) | |
508 | return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", | |
509 | node); | |
510 | ||
da495a03 ZJS |
511 | escaped = cescape(node); |
512 | if (!escaped) | |
513 | return log_oom(); | |
514 | ||
515 | r = unit_name_from_path(what, ".swap", &where_unit); | |
516 | if (r < 0) | |
517 | return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", | |
518 | what); | |
519 | ||
121ce4a6 MY |
520 | r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f); |
521 | if (r < 0) | |
522 | return r; | |
da495a03 ZJS |
523 | |
524 | fprintf(f, | |
da495a03 ZJS |
525 | "[Unit]\n" |
526 | "Description=Make Swap on %%f\n" | |
527 | "Documentation=man:systemd-mkswap@.service(8)\n" | |
ce37fb0d | 528 | "\n" |
da495a03 ZJS |
529 | "DefaultDependencies=no\n" |
530 | "BindsTo=%%i.device\n" | |
531 | "After=%%i.device\n" | |
ce37fb0d ZJS |
532 | "Before=%s\n" |
533 | "Conflicts=shutdown.target\n" | |
534 | "Before=shutdown.target\n" | |
da495a03 ZJS |
535 | "\n" |
536 | "[Service]\n" | |
537 | "Type=oneshot\n" | |
538 | "RemainAfterExit=yes\n" | |
539 | "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n" | |
a9b837aa | 540 | "TimeoutSec=infinity\n", |
da495a03 ZJS |
541 | where_unit, |
542 | escaped); | |
543 | ||
544 | r = fflush_and_check(f); | |
545 | if (r < 0) | |
121ce4a6 | 546 | return log_error_errno(r, "Failed to write unit %s: %m", unit); |
da495a03 ZJS |
547 | |
548 | return generator_add_symlink(dir, where_unit, "requires", unit); | |
549 | } | |
550 | ||
551 | int generator_hook_up_mkfs( | |
552 | const char *dir, | |
553 | const char *what, | |
554 | const char *where, | |
555 | const char *type) { | |
556 | ||
121ce4a6 | 557 | _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL; |
da495a03 | 558 | _cleanup_fclose_ FILE *f = NULL; |
121ce4a6 | 559 | const char *fsck_unit; |
da495a03 ZJS |
560 | int r; |
561 | ||
121ce4a6 MY |
562 | assert(dir); |
563 | assert(what); | |
564 | assert(where); | |
565 | ||
da495a03 ZJS |
566 | node = fstab_node_to_udev_node(what); |
567 | if (!node) | |
568 | return log_oom(); | |
569 | ||
570 | /* Nothing to work on. */ | |
baaa35ad ZJS |
571 | if (!is_device_path(node)) |
572 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
573 | "Cannot format something that is not a device node: %s", | |
574 | node); | |
da495a03 | 575 | |
baaa35ad ZJS |
576 | if (!type || streq(type, "auto")) |
577 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
578 | "Cannot format partition %s, filesystem type is not specified", | |
579 | node); | |
da495a03 | 580 | |
804f8e17 | 581 | r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit); |
da495a03 ZJS |
582 | if (r < 0) |
583 | return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", | |
584 | node); | |
585 | ||
0941ccae WF |
586 | if (in_initrd() && path_equal(where, "/sysroot")) |
587 | fsck_unit = SPECIAL_FSCK_ROOT_SERVICE; | |
588 | else if (in_initrd() && path_equal(where, "/sysusr/usr")) | |
589 | fsck_unit = SPECIAL_FSCK_USR_SERVICE; | |
590 | else | |
591 | fsck_unit = "systemd-fsck@%i.service"; | |
592 | ||
da495a03 ZJS |
593 | escaped = cescape(node); |
594 | if (!escaped) | |
595 | return log_oom(); | |
596 | ||
597 | r = unit_name_from_path(where, ".mount", &where_unit); | |
598 | if (r < 0) | |
599 | return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", | |
600 | where); | |
601 | ||
121ce4a6 MY |
602 | r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f); |
603 | if (r < 0) | |
604 | return r; | |
da495a03 ZJS |
605 | |
606 | fprintf(f, | |
da495a03 ZJS |
607 | "[Unit]\n" |
608 | "Description=Make File System on %%f\n" | |
804f8e17 | 609 | "Documentation=man:systemd-makefs@.service(8)\n" |
ce37fb0d | 610 | "\n" |
da495a03 ZJS |
611 | "DefaultDependencies=no\n" |
612 | "BindsTo=%%i.device\n" | |
613 | "After=%%i.device\n" | |
614 | /* fsck might or might not be used, so let's be safe and order | |
615 | * ourselves before both systemd-fsck@.service and the mount unit. */ | |
ce37fb0d ZJS |
616 | "Before=%s %s\n" |
617 | "Conflicts=shutdown.target\n" | |
618 | "Before=shutdown.target\n" | |
da495a03 ZJS |
619 | "\n" |
620 | "[Service]\n" | |
621 | "Type=oneshot\n" | |
622 | "RemainAfterExit=yes\n" | |
623 | "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n" | |
a9b837aa | 624 | "TimeoutSec=infinity\n", |
0941ccae | 625 | fsck_unit, |
da495a03 ZJS |
626 | where_unit, |
627 | type, | |
628 | escaped); | |
629 | // XXX: what about local-fs-pre.target? | |
630 | ||
631 | r = fflush_and_check(f); | |
632 | if (r < 0) | |
121ce4a6 | 633 | return log_error_errno(r, "Failed to write unit %s: %m", unit); |
da495a03 ZJS |
634 | |
635 | return generator_add_symlink(dir, where_unit, "requires", unit); | |
636 | } | |
7f2806d5 ZJS |
637 | |
638 | int generator_hook_up_growfs( | |
639 | const char *dir, | |
640 | const char *where, | |
641 | const char *target) { | |
642 | ||
50072ccf LP |
643 | const char *growfs_unit, *growfs_unit_path; |
644 | _cleanup_free_ char *where_unit = NULL, *instance = NULL; | |
7f2806d5 ZJS |
645 | int r; |
646 | ||
400c1e8f LP |
647 | assert(dir); |
648 | assert(where); | |
649 | ||
7f2806d5 ZJS |
650 | r = unit_name_from_path(where, ".mount", &where_unit); |
651 | if (r < 0) | |
50072ccf | 652 | return log_error_errno(r, "Failed to make unit name from path '%s': %m", where); |
7f2806d5 | 653 | |
50072ccf LP |
654 | if (empty_or_root(where)) { |
655 | growfs_unit = SPECIAL_GROWFS_ROOT_SERVICE; | |
656 | growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_ROOT_SERVICE; | |
657 | } else { | |
658 | growfs_unit = SPECIAL_GROWFS_SERVICE; | |
659 | growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_SERVICE; | |
18e6e863 | 660 | |
50072ccf LP |
661 | r = unit_name_path_escape(where, &instance); |
662 | if (r < 0) | |
663 | return log_error_errno(r, "Failed to escape path '%s': %m", where); | |
664 | } | |
18e6e863 | 665 | |
50072ccf LP |
666 | if (target) { |
667 | r = generator_add_ordering(dir, target, "After", growfs_unit, instance); | |
668 | if (r < 0) | |
669 | return r; | |
670 | } | |
7f2806d5 | 671 | |
50072ccf | 672 | return generator_add_symlink_full(dir, where_unit, "wants", growfs_unit_path, instance); |
7f2806d5 | 673 | } |
afe44c8f | 674 | |
04959faa LP |
675 | int generator_hook_up_pcrfs( |
676 | const char *dir, | |
677 | const char *where, | |
678 | const char *target) { | |
679 | ||
680 | const char *pcrfs_unit, *pcrfs_unit_path; | |
681 | _cleanup_free_ char *where_unit = NULL, *instance = NULL; | |
682 | int r; | |
683 | ||
684 | assert(dir); | |
685 | assert(where); | |
686 | ||
687 | r = unit_name_from_path(where, ".mount", &where_unit); | |
688 | if (r < 0) | |
689 | return log_error_errno(r, "Failed to make unit name from path '%s': %m", where); | |
690 | ||
691 | if (empty_or_root(where)) { | |
692 | pcrfs_unit = SPECIAL_PCRFS_ROOT_SERVICE; | |
693 | pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_ROOT_SERVICE; | |
694 | } else { | |
695 | pcrfs_unit = SPECIAL_PCRFS_SERVICE; | |
696 | pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_SERVICE; | |
697 | ||
698 | r = unit_name_path_escape(where, &instance); | |
699 | if (r < 0) | |
700 | return log_error_errno(r, "Failed to escape path '%s': %m", where); | |
701 | } | |
702 | ||
703 | if (target) { | |
704 | r = generator_add_ordering(dir, target, "After", pcrfs_unit, instance); | |
705 | if (r < 0) | |
706 | return r; | |
707 | } | |
708 | ||
709 | return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance); | |
710 | } | |
711 | ||
fc5c6ecc TB |
712 | int generator_hook_up_quotacheck( |
713 | const char *dir, | |
714 | const char *what, | |
715 | const char *where, | |
716 | const char *target, | |
717 | const char *fstype) { | |
718 | ||
719 | _cleanup_free_ char *where_unit = NULL, *instance = NULL; | |
720 | int r; | |
721 | ||
722 | assert(dir); | |
723 | assert(where); | |
724 | ||
725 | if (isempty(fstype) || streq(fstype, "auto")) | |
726 | return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Couldn't determine filesystem type for %s, quota cannot be activated", what); | |
727 | if (!fstype_needs_quota(fstype)) | |
728 | return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Quota was requested for %s, but not supported, ignoring: %s", what, fstype); | |
729 | ||
730 | /* quotacheck unit for system root */ | |
731 | if (path_equal(where, "/")) | |
732 | return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_ROOT_SERVICE); | |
733 | ||
734 | r = unit_name_path_escape(where, &instance); | |
735 | if (r < 0) | |
736 | return log_error_errno(r, "Failed to escape path '%s': %m", where); | |
737 | ||
738 | if (target) { | |
739 | r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTACHECK_SERVICE, instance); | |
740 | if (r < 0) | |
741 | return r; | |
742 | } | |
743 | ||
744 | r = unit_name_from_path(where, ".mount", &where_unit); | |
745 | if (r < 0) | |
746 | return log_error_errno(r, "Failed to make unit name from path '%s': %m", where); | |
747 | ||
748 | return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_SERVICE, instance); | |
749 | } | |
750 | ||
751 | int generator_hook_up_quotaon( | |
752 | const char *dir, | |
753 | const char *where, | |
754 | const char *target) { | |
755 | ||
756 | _cleanup_free_ char *where_unit = NULL, *instance = NULL; | |
757 | int r; | |
758 | ||
759 | assert(dir); | |
760 | assert(where); | |
761 | ||
762 | /* quotaon unit for system root is not instantiated */ | |
763 | if (path_equal(where, "/")) | |
764 | return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_ROOT_SERVICE); | |
765 | ||
766 | r = unit_name_path_escape(where, &instance); | |
767 | if (r < 0) | |
768 | return log_error_errno(r, "Failed to escape path '%s': %m", where); | |
769 | ||
770 | if (target) { | |
771 | r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTAON_SERVICE, instance); | |
772 | if (r < 0) | |
773 | return r; | |
774 | } | |
775 | ||
776 | r = unit_name_from_path(where, ".mount", &where_unit); | |
777 | if (r < 0) | |
778 | return log_error_errno(r, "Failed to make unit name from path '%s': %m", where); | |
779 | ||
780 | return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_SERVICE, instance); | |
781 | } | |
782 | ||
9b69569d ZJS |
783 | int generator_enable_remount_fs_service(const char *dir) { |
784 | /* Pull in systemd-remount-fs.service */ | |
785 | return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants", | |
835cf75a | 786 | SYSTEM_DATA_UNIT_DIR "/" SPECIAL_REMOUNT_FS_SERVICE); |
9b69569d ZJS |
787 | } |
788 | ||
a7e88558 LP |
789 | int generator_write_blockdev_dependency( |
790 | FILE *f, | |
791 | const char *what) { | |
792 | ||
793 | _cleanup_free_ char *escaped = NULL; | |
794 | int r; | |
795 | ||
796 | assert(f); | |
797 | assert(what); | |
798 | ||
799 | if (!path_startswith(what, "/dev/")) | |
800 | return 0; | |
801 | ||
802 | r = unit_name_path_escape(what, &escaped); | |
803 | if (r < 0) | |
804 | return log_error_errno(r, "Failed to escape device node path %s: %m", what); | |
805 | ||
806 | fprintf(f, | |
807 | "After=blockdev@%s.target\n", | |
808 | escaped); | |
809 | ||
810 | return 0; | |
811 | } | |
812 | ||
813 | int generator_write_cryptsetup_unit_section( | |
814 | FILE *f, | |
815 | const char *source) { | |
816 | ||
817 | assert(f); | |
818 | ||
819 | fprintf(f, | |
820 | "[Unit]\n" | |
821 | "Description=Cryptography Setup for %%I\n" | |
822 | "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"); | |
823 | ||
824 | if (source) | |
825 | fprintf(f, "SourcePath=%s\n", source); | |
826 | ||
827 | fprintf(f, | |
ce37fb0d | 828 | "\n" |
a7e88558 | 829 | "DefaultDependencies=no\n" |
2e64cb71 | 830 | "After=cryptsetup-pre.target systemd-udevd-kernel.socket systemd-tpm2-setup-early.service\n" |
a7e88558 | 831 | "Before=blockdev@dev-mapper-%%i.target\n" |
ce37fb0d ZJS |
832 | "Wants=blockdev@dev-mapper-%%i.target\n" |
833 | "IgnoreOnIsolate=true\n"); | |
a7e88558 LP |
834 | |
835 | return 0; | |
836 | } | |
837 | ||
838 | int generator_write_cryptsetup_service_section( | |
839 | FILE *f, | |
840 | const char *name, | |
841 | const char *what, | |
b7de9651 | 842 | const char *key_file, |
a7e88558 LP |
843 | const char *options) { |
844 | ||
b7de9651 | 845 | _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *key_file_escaped = NULL, *options_escaped = NULL; |
a7e88558 LP |
846 | |
847 | assert(f); | |
848 | assert(name); | |
849 | assert(what); | |
850 | ||
851 | name_escaped = specifier_escape(name); | |
852 | if (!name_escaped) | |
853 | return log_oom(); | |
854 | ||
855 | what_escaped = specifier_escape(what); | |
856 | if (!what_escaped) | |
857 | return log_oom(); | |
858 | ||
b7de9651 JJ |
859 | if (key_file) { |
860 | key_file_escaped = specifier_escape(key_file); | |
861 | if (!key_file_escaped) | |
a7e88558 LP |
862 | return log_oom(); |
863 | } | |
864 | ||
865 | if (options) { | |
866 | options_escaped = specifier_escape(options); | |
867 | if (!options_escaped) | |
868 | return log_oom(); | |
869 | } | |
870 | ||
871 | fprintf(f, | |
872 | "\n" | |
873 | "[Service]\n" | |
874 | "Type=oneshot\n" | |
875 | "RemainAfterExit=yes\n" | |
a9b837aa | 876 | "TimeoutSec=infinity\n" /* The binary handles timeouts on its own */ |
a7e88558 LP |
877 | "KeyringMode=shared\n" /* Make sure we can share cached keys among instances */ |
878 | "OOMScoreAdjust=500\n" /* Unlocking can allocate a lot of memory if Argon2 is used */ | |
b952663c | 879 | "ImportCredential=cryptsetup.*\n" |
a7e88558 LP |
880 | "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" |
881 | "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", | |
b7de9651 | 882 | name_escaped, what_escaped, strempty(key_file_escaped), strempty(options_escaped), |
a7e88558 LP |
883 | name_escaped); |
884 | ||
885 | return 0; | |
886 | } | |
887 | ||
08b04ec7 GP |
888 | int generator_write_veritysetup_unit_section( |
889 | FILE *f, | |
890 | const char *source) { | |
891 | ||
892 | assert(f); | |
893 | ||
894 | fprintf(f, | |
895 | "[Unit]\n" | |
896 | "Description=Integrity Protection Setup for %%I\n" | |
897 | "Documentation=man:veritytab(5) man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n"); | |
898 | ||
899 | if (source) | |
900 | fprintf(f, "SourcePath=%s\n", source); | |
901 | ||
902 | fprintf(f, | |
903 | "DefaultDependencies=no\n" | |
904 | "IgnoreOnIsolate=true\n" | |
685e0dd1 | 905 | "After=veritysetup-pre.target systemd-udevd-kernel.socket\n" |
08b04ec7 GP |
906 | "Before=blockdev@dev-mapper-%%i.target\n" |
907 | "Wants=blockdev@dev-mapper-%%i.target\n"); | |
908 | ||
909 | return 0; | |
910 | } | |
911 | ||
912 | int generator_write_veritysetup_service_section( | |
913 | FILE *f, | |
914 | const char *name, | |
915 | const char *data_what, | |
916 | const char *hash_what, | |
917 | const char *roothash, | |
918 | const char *options) { | |
919 | ||
47237e0e | 920 | _cleanup_free_ char *name_escaped = NULL, *data_what_escaped = NULL, *hash_what_escaped = NULL, |
08b04ec7 GP |
921 | *roothash_escaped = NULL, *options_escaped = NULL; |
922 | ||
923 | assert(f); | |
924 | assert(name); | |
925 | assert(data_what); | |
926 | assert(hash_what); | |
927 | ||
928 | name_escaped = specifier_escape(name); | |
929 | if (!name_escaped) | |
930 | return log_oom(); | |
931 | ||
932 | data_what_escaped = specifier_escape(data_what); | |
933 | if (!data_what_escaped) | |
934 | return log_oom(); | |
935 | ||
936 | hash_what_escaped = specifier_escape(hash_what); | |
937 | if (!hash_what_escaped) | |
938 | return log_oom(); | |
939 | ||
940 | roothash_escaped = specifier_escape(roothash); | |
941 | if (!roothash_escaped) | |
942 | return log_oom(); | |
943 | ||
944 | if (options) { | |
945 | options_escaped = specifier_escape(options); | |
946 | if (!options_escaped) | |
947 | return log_oom(); | |
948 | } | |
949 | ||
950 | fprintf(f, | |
951 | "\n" | |
952 | "[Service]\n" | |
953 | "Type=oneshot\n" | |
954 | "RemainAfterExit=yes\n" | |
955 | "ExecStart=" SYSTEMD_VERITYSETUP_PATH " attach '%s' '%s' '%s' '%s' '%s'\n" | |
956 | "ExecStop=" SYSTEMD_VERITYSETUP_PATH " detach '%s'\n", | |
957 | name_escaped, data_what_escaped, hash_what_escaped, roothash_escaped, strempty(options_escaped), | |
958 | name_escaped); | |
959 | ||
960 | return 0; | |
961 | } | |
962 | ||
afe44c8f | 963 | void log_setup_generator(void) { |
911017f0 ZJS |
964 | if (invoked_by_systemd()) { |
965 | /* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */ | |
966 | if (cg_pid_get_owner_uid(0, NULL) == -ENXIO /* not running in a per-user slice */) | |
967 | log_set_prohibit_ipc(true); | |
968 | ||
969 | /* This effectively means: journal for per-user generators, kmsg otherwise */ | |
970 | log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); | |
971 | } | |
dee29aeb | 972 | |
aa976d87 | 973 | log_setup(); |
afe44c8f | 974 | } |