]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
fstab-generator: allow x-systemd.device-timeout for swap units
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-generator.c
CommitLineData
e23a0ce8
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
e23a0ce8
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
e23a0ce8 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
e23a0ce8
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <string.h>
23#include <errno.h>
24#include <unistd.h>
25
26#include "log.h"
27#include "util.h"
28#include "unit-name.h"
49e942b2 29#include "mkdir.h"
66a78c2b 30#include "strv.h"
a860325e 31#include "fileio.h"
bde29068 32#include "path-util.h"
e23a0ce8 33
66a78c2b
LP
34static const char *arg_dest = "/tmp";
35static bool arg_enabled = true;
36static bool arg_read_crypttab = true;
744198e9
LP
37static char **arg_disks = NULL;
38static char **arg_options = NULL;
39static char *arg_keyfile = NULL;
141a79f4 40
e23a0ce8
LP
41static bool has_option(const char *haystack, const char *needle) {
42 const char *f = haystack;
43 size_t l;
44
f653f683
LP
45 assert(needle);
46
47 if (!haystack)
48 return false;
49
e23a0ce8
LP
50 l = strlen(needle);
51
52 while ((f = strstr(f, needle))) {
53
54 if (f > haystack && f[-1] != ',') {
55 f++;
56 continue;
57 }
58
aae5220d 59 if (f[l] != 0 && f[l] != ',') {
e23a0ce8
LP
60 f++;
61 continue;
62 }
63
64 return true;
65 }
66
67 return false;
68}
69
70static int create_disk(
71 const char *name,
72 const char *device,
73 const char *password,
74 const char *options) {
75
744198e9 76 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *to = NULL, *e = NULL;
7fd1b19b 77 _cleanup_fclose_ FILE *f = NULL;
8c11d3c1 78 bool noauto, nofail, tmp, swap;
744198e9
LP
79 char *from;
80 int r;
e23a0ce8
LP
81
82 assert(name);
83 assert(device);
84
155da457
LP
85 noauto = has_option(options, "noauto");
86 nofail = has_option(options, "nofail");
8c11d3c1
TG
87 tmp = has_option(options, "tmp");
88 swap = has_option(options, "swap");
89
90 if (tmp && swap) {
91 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
92 return -EINVAL;
93 }
155da457 94
744198e9
LP
95 e = unit_name_escape(name);
96 if (!e)
97 return log_oom();
98
99 n = unit_name_build("systemd-cryptsetup", e, ".service");
24a988e9
HH
100 if (!n)
101 return log_oom();
e23a0ce8 102
b7def684 103 p = strjoin(arg_dest, "/", n, NULL);
24a988e9
HH
104 if (!p)
105 return log_oom();
e23a0ce8 106
f7f21d33 107 u = fstab_node_to_udev_node(device);
24a988e9
HH
108 if (!u)
109 return log_oom();
e23a0ce8 110
f7f21d33 111 d = unit_name_from_path(u, ".device");
24a988e9
HH
112 if (!d)
113 return log_oom();
e23a0ce8 114
f7f21d33
LP
115 f = fopen(p, "wxe");
116 if (!f) {
1cda32b8 117 log_error("Failed to create unit file %s: %m", p);
24a988e9 118 return -errno;
e23a0ce8
LP
119 }
120
9ece938a 121 fputs(
6b1dc2bd 122 "# Automatically generated by systemd-cryptsetup-generator\n\n"
e23a0ce8 123 "[Unit]\n"
9ece938a 124 "Description=Cryptography Setup for %I\n"
c3834f9b 125 "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n"
1b64d026 126 "SourcePath=/etc/crypttab\n"
e23a0ce8 127 "DefaultDependencies=no\n"
744198e9 128 "Conflicts=umount.target\n"
9ece938a 129 "BindsTo=dev-mapper-%i.device\n"
4469ff4a 130 "IgnoreOnIsolate=true\n"
9542239e 131 "After=systemd-readahead-collect.service systemd-readahead-replay.service cryptsetup-pre.target\n",
9ece938a 132 f);
e23a0ce8 133
155da457
LP
134 if (!nofail)
135 fprintf(f,
136 "Before=cryptsetup.target\n");
137
ceca9501 138 if (password) {
744198e9 139 if (STR_IN_SET(password, "/dev/urandom", "/dev/random", "/dev/hw_random"))
a0f70805 140 fputs("After=systemd-random-seed.service\n", f);
66a5dbdf 141 else if (!streq(password, "-") && !streq(password, "none")) {
744198e9
LP
142 _cleanup_free_ char *uu;
143
144 uu = fstab_node_to_udev_node(password);
145 if (!uu)
66a5dbdf
DR
146 return log_oom();
147
bde29068 148 if (!path_equal(uu, "/dev/null")) {
744198e9 149
bde29068
LP
150 if (is_device_path(uu)) {
151 _cleanup_free_ char *dd;
66a5dbdf 152
bde29068
LP
153 dd = unit_name_from_path(uu, ".device");
154 if (!dd)
155 return log_oom();
156
157 fprintf(f, "After=%1$s\nRequires=%1$s\n", dd);
158 } else
159 fprintf(f, "RequiresMountsFor=%s\n", password);
160 }
66a5dbdf 161 }
ceca9501 162 }
e23a0ce8 163
9ece938a
TW
164 if (is_device_path(u))
165 fprintf(f,
166 "BindsTo=%s\n"
167 "After=%s\n"
168 "Before=umount.target\n",
169 d, d);
170 else
171 fprintf(f,
172 "RequiresMountsFor=%s\n",
173 u);
174
e23a0ce8
LP
175 fprintf(f,
176 "\n[Service]\n"
177 "Type=oneshot\n"
178 "RemainAfterExit=yes\n"
90724929 179 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
260ab287 180 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
7f4e0805 181 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
e23a0ce8
LP
182 name, u, strempty(password), strempty(options),
183 name);
184
8c11d3c1 185 if (tmp)
e23a0ce8 186 fprintf(f,
50109038 187 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
1d3399e6 188 name);
e23a0ce8 189
8c11d3c1 190 if (swap)
e23a0ce8 191 fprintf(f,
50109038 192 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
1d3399e6 193 name);
e23a0ce8
LP
194
195 fflush(f);
e23a0ce8 196 if (ferror(f)) {
1cda32b8 197 log_error("Failed to write file %s: %m", p);
24a988e9 198 return -errno;
e23a0ce8
LP
199 }
200
744198e9 201 from = strappenda("../", n);
74715b82 202
155da457 203 if (!noauto) {
e23a0ce8 204
b7def684 205 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
24a988e9
HH
206 if (!to)
207 return log_oom();
e23a0ce8 208
d2e54fae 209 mkdir_parents_label(to, 0755);
e23a0ce8 210 if (symlink(from, to) < 0) {
c79bb9e4 211 log_error("Failed to create symlink %s: %m", to);
24a988e9 212 return -errno;
e23a0ce8 213 }
2f8cd170
LP
214
215 free(to);
155da457 216 if (!nofail)
b7def684 217 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
155da457 218 else
b7def684 219 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
24a988e9
HH
220 if (!to)
221 return log_oom();
2f8cd170 222
d2e54fae 223 mkdir_parents_label(to, 0755);
155da457 224 if (symlink(from, to) < 0) {
c79bb9e4 225 log_error("Failed to create symlink %s: %m", to);
24a988e9 226 return -errno;
2f8cd170 227 }
f7f21d33 228 }
74715b82 229
608d41f3 230 free(to);
b7def684 231 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
24a988e9
HH
232 if (!to)
233 return log_oom();
74715b82 234
d2e54fae 235 mkdir_parents_label(to, 0755);
74715b82 236 if (symlink(from, to) < 0) {
c79bb9e4 237 log_error("Failed to create symlink %s: %m", to);
24a988e9 238 return -errno;
74715b82
LP
239 }
240
68395007 241 if (!noauto && !nofail) {
744198e9 242
68395007
HH
243 free(p);
244 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
245 if (!p)
246 return log_oom();
247
248 mkdir_parents_label(p, 0755);
68395007
HH
249 r = write_string_file(p,
250 "# Automatically generated by systemd-cryptsetup-generator\n\n"
251 "[Unit]\n"
252 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
744198e9
LP
253 if (r < 0) {
254 log_error("Failed to write device drop-in: %s", strerror(-r));
68395007 255 return r;
744198e9 256 }
68395007
HH
257 }
258
24a988e9 259 return 0;
e23a0ce8
LP
260}
261
059cb385 262static int parse_proc_cmdline_item(const char *key, const char *value) {
74df0fca 263 int r;
66a78c2b 264
059cb385
LP
265 if (STR_IN_SET(key, "luks", "rd.luks") && value) {
266
267 r = parse_boolean(value);
141a79f4 268 if (r < 0)
059cb385 269 log_warning("Failed to parse luks switch %s. Ignoring.", value);
141a79f4
ZJS
270 else
271 arg_enabled = r;
66a78c2b 272
059cb385 273 } else if (STR_IN_SET(key, "luks.crypttab", "rd.luks.crypttab") && value) {
66a78c2b 274
059cb385 275 r = parse_boolean(value);
141a79f4 276 if (r < 0)
059cb385 277 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value);
141a79f4
ZJS
278 else
279 arg_read_crypttab = r;
66a78c2b 280
059cb385 281 } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) {
66a78c2b 282
059cb385 283 if (strv_extend(&arg_disks, value) < 0)
141a79f4 284 return log_oom();
66a78c2b 285
059cb385 286 } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) {
66a78c2b 287
059cb385 288 if (strv_extend(&arg_options, value) < 0)
141a79f4 289 return log_oom();
66a78c2b 290
059cb385 291 } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) {
66a78c2b 292
141a79f4 293 free(arg_keyfile);
66aaf85e 294 arg_keyfile = strdup(value);
141a79f4
ZJS
295 if (!arg_keyfile)
296 return log_oom();
7ab064a6 297
85013844 298 }
66a78c2b 299
24a988e9 300 return 0;
66a78c2b
LP
301}
302
e23a0ce8 303int main(int argc, char *argv[]) {
141a79f4 304 _cleanup_strv_free_ char **disks_done = NULL;
7fd1b19b 305 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 306 unsigned n = 0;
141a79f4 307 int r = EXIT_FAILURE, r2 = EXIT_FAILURE;
66a78c2b 308 char **i;
e23a0ce8 309
07719a21
LP
310 if (argc > 1 && argc != 4) {
311 log_error("This program takes three or no arguments.");
e23a0ce8
LP
312 return EXIT_FAILURE;
313 }
314
2a796654
LP
315 if (argc > 1)
316 arg_dest = argv[1];
e23a0ce8 317
a6903061 318 log_set_target(LOG_TARGET_SAFE);
e23a0ce8
LP
319 log_parse_environment();
320 log_open();
321
4c12626c
LP
322 umask(0022);
323
059cb385 324 if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
141a79f4 325 goto cleanup;
66a78c2b 326
141a79f4
ZJS
327 if (!arg_enabled) {
328 r = r2 = EXIT_SUCCESS;
329 goto cleanup;
330 }
331
332 strv_uniq(arg_disks);
66a78c2b 333
e2cb60fa 334 if (arg_read_crypttab) {
8973790e 335 struct stat st;
66a78c2b 336
8973790e 337 f = fopen("/etc/crypttab", "re");
e2cb60fa
HH
338 if (!f) {
339 if (errno == ENOENT)
340 r = EXIT_SUCCESS;
141a79f4 341 else
e2cb60fa 342 log_error("Failed to open /etc/crypttab: %m");
8973790e
LP
343
344 goto next;
345 }
346
347 if (fstat(fileno(f), &st) < 0) {
348 log_error("Failed to stat /etc/crypttab: %m");
8973790e
LP
349 goto next;
350 }
351
2b68185a
LP
352 /* If we readd support for specifying passphrases
353 * directly in crypttabe we should upgrade the warning
354 * below, though possibly only if a passphrase is
355 * specified directly. */
8973790e 356 if (st.st_mode & 0005)
2b68185a 357 log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
8973790e
LP
358
359 for (;;) {
e2cb60fa 360 char line[LINE_MAX], *l;
7fd1b19b 361 _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
e2cb60fa 362 int k;
66a78c2b 363
e2cb60fa
HH
364 if (!fgets(line, sizeof(line), f))
365 break;
66a78c2b 366
e2cb60fa 367 n++;
66a78c2b 368
e2cb60fa
HH
369 l = strstrip(line);
370 if (*l == '#' || *l == 0)
371 continue;
e23a0ce8 372
e2cb60fa
HH
373 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
374 if (k < 2 || k > 4) {
375 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
24a988e9 376 continue;
e2cb60fa 377 }
e23a0ce8 378
141a79f4
ZJS
379 /*
380 If options are specified on the kernel commandline, let them override
381 the ones from crypttab.
382 */
383 STRV_FOREACH(i, arg_options) {
384 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
385 const char *p = *i;
386
387 k = sscanf(p, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
388 if (k == 2 && streq(proc_uuid, device + 5)) {
389 free(options);
390 options = strdup(p);
391 if (!proc_options) {
392 log_oom();
393 goto cleanup;
7ab064a6
TG
394 }
395 }
396 }
397
141a79f4 398 if (arg_disks) {
e2cb60fa
HH
399 /*
400 If luks UUIDs are specified on the kernel command line, use them as a filter
401 for /etc/crypttab and only generate units for those.
402 */
141a79f4 403 STRV_FOREACH(i, arg_disks) {
7fd1b19b 404 _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
e2cb60fa
HH
405 const char *p = *i;
406
407 if (startswith(p, "luks-"))
408 p += 5;
409
410 proc_name = strappend("luks-", p);
411 proc_device = strappend("UUID=", p);
412
141a79f4
ZJS
413 if (!proc_name || !proc_device) {
414 log_oom();
415 goto cleanup;
416 }
24a988e9 417
e2cb60fa 418 if (streq(proc_device, device) || streq(proc_name, name)) {
e2cb60fa 419 if (create_disk(name, device, password, options) < 0)
141a79f4 420 goto cleanup;
e2cb60fa 421
141a79f4
ZJS
422 if (strv_extend(&disks_done, p) < 0) {
423 log_oom();
424 goto cleanup;
425 }
e2cb60fa 426 }
e2cb60fa 427 }
141a79f4
ZJS
428 } else if (create_disk(name, device, password, options) < 0)
429 goto cleanup;
430
e2cb60fa 431 }
e23a0ce8
LP
432 }
433
141a79f4
ZJS
434 r = EXIT_SUCCESS;
435
8973790e 436next:
141a79f4 437 STRV_FOREACH(i, arg_disks) {
e2cb60fa
HH
438 /*
439 Generate units for those UUIDs, which were specified
440 on the kernel command line and not yet written.
441 */
e23a0ce8 442
7ab064a6 443 _cleanup_free_ char *name = NULL, *device = NULL, *options = NULL;
e2cb60fa 444 const char *p = *i;
e23a0ce8 445
e2cb60fa
HH
446 if (startswith(p, "luks-"))
447 p += 5;
e23a0ce8 448
141a79f4 449 if (strv_contains(disks_done, p))
e23a0ce8
LP
450 continue;
451
e2cb60fa
HH
452 name = strappend("luks-", p);
453 device = strappend("UUID=", p);
454
141a79f4
ZJS
455 if (!name || !device) {
456 log_oom();
457 goto cleanup;
458 }
e23a0ce8 459
141a79f4 460 if (arg_options) {
7ab064a6
TG
461 /*
462 If options are specified on the kernel commandline, use them.
463 */
464 char **j;
465
141a79f4 466 STRV_FOREACH(j, arg_options) {
7ab064a6
TG
467 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
468 const char *s = *j;
469 int k;
470
471 k = sscanf(s, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
472 if (k == 2) {
473 if (streq(proc_uuid, device + 5)) {
744198e9
LP
474 free(options);
475 options = proc_options;
476 proc_options = NULL;
7ab064a6
TG
477 }
478 } else if (!options) {
479 /*
480 Fall back to options without a specified UUID
481 */
482 options = strdup(s);
141a79f4
ZJS
483 if (!options) {
484 log_oom();
485 goto cleanup;
486 };
7ab064a6
TG
487 }
488 }
489 }
490
491 if (!options) {
492 options = strdup("timeout=0");
141a79f4
ZJS
493 if (!options) {
494 log_oom();
495 goto cleanup;
496 }
7ab064a6
TG
497 }
498
141a79f4
ZJS
499 if (create_disk(name, device, arg_keyfile, options) < 0)
500 goto cleanup;
e23a0ce8
LP
501 }
502
141a79f4
ZJS
503 r2 = EXIT_SUCCESS;
504
505cleanup:
506 strv_free(arg_disks);
507 strv_free(arg_options);
508 free(arg_keyfile);
509
510 return r != EXIT_SUCCESS ? r : r2;
e23a0ce8 511}