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