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