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