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