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