]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
treewide: auto-convert the simple cases to log_*_errno()
[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) {
a6fb0dc1
HG
249 _cleanup_free_ char *dmname;
250 dmname = strjoin("dev-mapper-", e, ".device", NULL);
251 if (!dmname)
252 return log_oom();
253
254 r = write_drop_in(arg_dest, dmname, 90, "device-timeout",
8eea8687
ZJS
255 "# Automatically generated by systemd-cryptsetup-generator \n\n"
256 "[Unit]\nJobTimeoutSec=0");
744198e9 257 if (r < 0) {
0a1beeb6 258 log_error_errno(-r, "Failed to write device drop-in: %m");
68395007 259 return r;
744198e9 260 }
68395007
HH
261 }
262
24a988e9 263 return 0;
e23a0ce8
LP
264}
265
059cb385 266static int parse_proc_cmdline_item(const char *key, const char *value) {
74df0fca 267 int r;
66a78c2b 268
059cb385
LP
269 if (STR_IN_SET(key, "luks", "rd.luks") && value) {
270
271 r = parse_boolean(value);
141a79f4 272 if (r < 0)
059cb385 273 log_warning("Failed to parse luks switch %s. Ignoring.", value);
141a79f4
ZJS
274 else
275 arg_enabled = r;
66a78c2b 276
059cb385 277 } else if (STR_IN_SET(key, "luks.crypttab", "rd.luks.crypttab") && value) {
66a78c2b 278
059cb385 279 r = parse_boolean(value);
141a79f4 280 if (r < 0)
059cb385 281 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value);
141a79f4
ZJS
282 else
283 arg_read_crypttab = r;
66a78c2b 284
059cb385 285 } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) {
66a78c2b 286
059cb385 287 if (strv_extend(&arg_disks, value) < 0)
141a79f4 288 return log_oom();
66a78c2b 289
059cb385 290 } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) {
66a78c2b 291
059cb385 292 if (strv_extend(&arg_options, value) < 0)
141a79f4 293 return log_oom();
66a78c2b 294
059cb385 295 } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) {
66a78c2b 296
141a79f4 297 free(arg_keyfile);
66aaf85e 298 arg_keyfile = strdup(value);
141a79f4
ZJS
299 if (!arg_keyfile)
300 return log_oom();
7ab064a6 301
85013844 302 }
66a78c2b 303
24a988e9 304 return 0;
66a78c2b
LP
305}
306
e23a0ce8 307int main(int argc, char *argv[]) {
141a79f4 308 _cleanup_strv_free_ char **disks_done = NULL;
7fd1b19b 309 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 310 unsigned n = 0;
b5884878 311 int r = EXIT_FAILURE, r2 = EXIT_FAILURE, z;
66a78c2b 312 char **i;
e23a0ce8 313
07719a21
LP
314 if (argc > 1 && argc != 4) {
315 log_error("This program takes three or no arguments.");
e23a0ce8
LP
316 return EXIT_FAILURE;
317 }
318
2a796654
LP
319 if (argc > 1)
320 arg_dest = argv[1];
e23a0ce8 321
a6903061 322 log_set_target(LOG_TARGET_SAFE);
e23a0ce8
LP
323 log_parse_environment();
324 log_open();
325
4c12626c
LP
326 umask(0022);
327
b5884878
LP
328 z = parse_proc_cmdline(parse_proc_cmdline_item);
329 if (z < 0)
0a1beeb6 330 log_warning_errno(-z, "Failed to parse kernel command line, ignoring: %m");
66a78c2b 331
141a79f4
ZJS
332 if (!arg_enabled) {
333 r = r2 = EXIT_SUCCESS;
334 goto cleanup;
335 }
336
337 strv_uniq(arg_disks);
66a78c2b 338
e2cb60fa 339 if (arg_read_crypttab) {
8973790e 340 struct stat st;
66a78c2b 341
8973790e 342 f = fopen("/etc/crypttab", "re");
e2cb60fa
HH
343 if (!f) {
344 if (errno == ENOENT)
345 r = EXIT_SUCCESS;
141a79f4 346 else
e2cb60fa 347 log_error("Failed to open /etc/crypttab: %m");
8973790e
LP
348
349 goto next;
350 }
351
352 if (fstat(fileno(f), &st) < 0) {
353 log_error("Failed to stat /etc/crypttab: %m");
8973790e
LP
354 goto next;
355 }
356
2b68185a
LP
357 /* If we readd support for specifying passphrases
358 * directly in crypttabe we should upgrade the warning
359 * below, though possibly only if a passphrase is
360 * specified directly. */
8973790e 361 if (st.st_mode & 0005)
2b68185a 362 log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
8973790e
LP
363
364 for (;;) {
e2cb60fa 365 char line[LINE_MAX], *l;
7fd1b19b 366 _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
e2cb60fa 367 int k;
66a78c2b 368
e2cb60fa
HH
369 if (!fgets(line, sizeof(line), f))
370 break;
66a78c2b 371
e2cb60fa 372 n++;
66a78c2b 373
e2cb60fa
HH
374 l = strstrip(line);
375 if (*l == '#' || *l == 0)
376 continue;
e23a0ce8 377
e2cb60fa
HH
378 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
379 if (k < 2 || k > 4) {
380 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
24a988e9 381 continue;
e2cb60fa 382 }
e23a0ce8 383
141a79f4 384 /*
3f85ef0f 385 If options are specified on the kernel command line, let them override
141a79f4
ZJS
386 the ones from crypttab.
387 */
388 STRV_FOREACH(i, arg_options) {
389 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
390 const char *p = *i;
391
392 k = sscanf(p, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
393 if (k == 2 && streq(proc_uuid, device + 5)) {
394 free(options);
395 options = strdup(p);
0e2f1401 396 if (!options) {
141a79f4
ZJS
397 log_oom();
398 goto cleanup;
7ab064a6
TG
399 }
400 }
401 }
402
141a79f4 403 if (arg_disks) {
e2cb60fa
HH
404 /*
405 If luks UUIDs are specified on the kernel command line, use them as a filter
406 for /etc/crypttab and only generate units for those.
407 */
141a79f4 408 STRV_FOREACH(i, arg_disks) {
7fd1b19b 409 _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
e2cb60fa
HH
410 const char *p = *i;
411
412 if (startswith(p, "luks-"))
413 p += 5;
414
415 proc_name = strappend("luks-", p);
416 proc_device = strappend("UUID=", p);
417
141a79f4
ZJS
418 if (!proc_name || !proc_device) {
419 log_oom();
420 goto cleanup;
421 }
24a988e9 422
e2cb60fa 423 if (streq(proc_device, device) || streq(proc_name, name)) {
e2cb60fa 424 if (create_disk(name, device, password, options) < 0)
141a79f4 425 goto cleanup;
e2cb60fa 426
141a79f4
ZJS
427 if (strv_extend(&disks_done, p) < 0) {
428 log_oom();
429 goto cleanup;
430 }
e2cb60fa 431 }
e2cb60fa 432 }
141a79f4
ZJS
433 } else if (create_disk(name, device, password, options) < 0)
434 goto cleanup;
435
e2cb60fa 436 }
e23a0ce8
LP
437 }
438
141a79f4
ZJS
439 r = EXIT_SUCCESS;
440
8973790e 441next:
141a79f4 442 STRV_FOREACH(i, arg_disks) {
e2cb60fa
HH
443 /*
444 Generate units for those UUIDs, which were specified
445 on the kernel command line and not yet written.
446 */
e23a0ce8 447
7ab064a6 448 _cleanup_free_ char *name = NULL, *device = NULL, *options = NULL;
e2cb60fa 449 const char *p = *i;
e23a0ce8 450
e2cb60fa
HH
451 if (startswith(p, "luks-"))
452 p += 5;
e23a0ce8 453
141a79f4 454 if (strv_contains(disks_done, p))
e23a0ce8
LP
455 continue;
456
e2cb60fa
HH
457 name = strappend("luks-", p);
458 device = strappend("UUID=", p);
459
141a79f4
ZJS
460 if (!name || !device) {
461 log_oom();
462 goto cleanup;
463 }
e23a0ce8 464
141a79f4 465 if (arg_options) {
7ab064a6 466 /*
3f85ef0f 467 If options are specified on the kernel command line, use them.
7ab064a6
TG
468 */
469 char **j;
470
141a79f4 471 STRV_FOREACH(j, arg_options) {
7ab064a6
TG
472 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
473 const char *s = *j;
474 int k;
475
476 k = sscanf(s, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
477 if (k == 2) {
478 if (streq(proc_uuid, device + 5)) {
744198e9
LP
479 free(options);
480 options = proc_options;
481 proc_options = NULL;
7ab064a6
TG
482 }
483 } else if (!options) {
484 /*
485 Fall back to options without a specified UUID
486 */
487 options = strdup(s);
141a79f4
ZJS
488 if (!options) {
489 log_oom();
490 goto cleanup;
491 };
7ab064a6
TG
492 }
493 }
494 }
495
496 if (!options) {
497 options = strdup("timeout=0");
141a79f4
ZJS
498 if (!options) {
499 log_oom();
500 goto cleanup;
501 }
7ab064a6
TG
502 }
503
141a79f4
ZJS
504 if (create_disk(name, device, arg_keyfile, options) < 0)
505 goto cleanup;
e23a0ce8
LP
506 }
507
141a79f4
ZJS
508 r2 = EXIT_SUCCESS;
509
510cleanup:
511 strv_free(arg_disks);
512 strv_free(arg_options);
513 free(arg_keyfile);
514
515 return r != EXIT_SUCCESS ? r : r2;
e23a0ce8 516}