]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
cryptsetup-generator: don't create tmp+swap units
[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
LP
30#include "virt.h"
31#include "strv.h"
a860325e 32#include "fileio.h"
e23a0ce8 33
66a78c2b
LP
34static const char *arg_dest = "/tmp";
35static bool arg_enabled = true;
36static bool arg_read_crypttab = true;
e23a0ce8
LP
37
38static bool has_option(const char *haystack, const char *needle) {
39 const char *f = haystack;
40 size_t l;
41
f653f683
LP
42 assert(needle);
43
44 if (!haystack)
45 return false;
46
e23a0ce8
LP
47 l = strlen(needle);
48
49 while ((f = strstr(f, needle))) {
50
51 if (f > haystack && f[-1] != ',') {
52 f++;
53 continue;
54 }
55
aae5220d 56 if (f[l] != 0 && f[l] != ',') {
e23a0ce8
LP
57 f++;
58 continue;
59 }
60
61 return true;
62 }
63
64 return false;
65}
66
67static int create_disk(
68 const char *name,
69 const char *device,
70 const char *password,
71 const char *options) {
72
7fd1b19b
HH
73 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 _cleanup_fclose_ FILE *f = NULL;
8c11d3c1 75 bool noauto, nofail, tmp, swap;
e23a0ce8
LP
76
77 assert(name);
78 assert(device);
79
155da457
LP
80 noauto = has_option(options, "noauto");
81 nofail = has_option(options, "nofail");
8c11d3c1
TG
82 tmp = has_option(options, "tmp");
83 swap = has_option(options, "swap");
84
85 if (tmp && swap) {
86 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
87 return -EINVAL;
88 }
155da457 89
35eb6b12 90 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
24a988e9
HH
91 if (!n)
92 return log_oom();
e23a0ce8 93
b7def684 94 p = strjoin(arg_dest, "/", n, NULL);
24a988e9
HH
95 if (!p)
96 return log_oom();
e23a0ce8 97
f7f21d33 98 u = fstab_node_to_udev_node(device);
24a988e9
HH
99 if (!u)
100 return log_oom();
e23a0ce8 101
f7f21d33 102 d = unit_name_from_path(u, ".device");
24a988e9
HH
103 if (!d)
104 return log_oom();
e23a0ce8 105
f7f21d33
LP
106 f = fopen(p, "wxe");
107 if (!f) {
1cda32b8 108 log_error("Failed to create unit file %s: %m", p);
24a988e9 109 return -errno;
e23a0ce8
LP
110 }
111
9ece938a 112 fputs(
6b1dc2bd 113 "# Automatically generated by systemd-cryptsetup-generator\n\n"
e23a0ce8 114 "[Unit]\n"
9ece938a 115 "Description=Cryptography Setup for %I\n"
1c732700 116 "Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
1b64d026 117 "SourcePath=/etc/crypttab\n"
155da457 118 "Conflicts=umount.target\n"
e23a0ce8 119 "DefaultDependencies=no\n"
9ece938a
TW
120 "BindsTo=dev-mapper-%i.device\n"
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"))
132 fputs("After=systemd-random-seed-load.service\n", f);
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
951657bd 243static int parse_proc_cmdline(char ***arg_proc_cmdline_disks, char **arg_proc_cmdline_keyfile) {
7fd1b19b 244 _cleanup_free_ char *line = NULL;
24a988e9 245 char *w = NULL, *state = NULL;
66a78c2b
LP
246 int r;
247 size_t l;
248
249 if (detect_container(NULL) > 0)
250 return 0;
251
252 r = read_one_line_file("/proc/cmdline", &line);
253 if (r < 0) {
254 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
255 return 0;
256 }
257
258 FOREACH_WORD_QUOTED(w, l, line, state) {
7fd1b19b 259 _cleanup_free_ char *word = NULL;
66a78c2b
LP
260
261 word = strndup(w, l);
24a988e9
HH
262 if (!word)
263 return log_oom();
66a78c2b
LP
264
265 if (startswith(word, "luks=")) {
266 r = parse_boolean(word + 5);
267 if (r < 0)
268 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
269 else
270 arg_enabled = r;
271
272 } else if (startswith(word, "rd.luks=")) {
273
274 if (in_initrd()) {
275 r = parse_boolean(word + 8);
276 if (r < 0)
277 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
278 else
279 arg_enabled = r;
280 }
281
282 } else if (startswith(word, "luks.crypttab=")) {
283 r = parse_boolean(word + 14);
284 if (r < 0)
285 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
286 else
287 arg_read_crypttab = r;
288
289 } else if (startswith(word, "rd.luks.crypttab=")) {
290
291 if (in_initrd()) {
292 r = parse_boolean(word + 17);
293 if (r < 0)
294 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
295 else
296 arg_read_crypttab = r;
297 }
298
299 } else if (startswith(word, "luks.uuid=")) {
e32530cb 300 if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
24a988e9
HH
301 return log_oom();
302
66a78c2b
LP
303 } else if (startswith(word, "rd.luks.uuid=")) {
304
305 if (in_initrd()) {
e32530cb 306 if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
24a988e9 307 return log_oom();
66a78c2b
LP
308 }
309
951657bd
HH
310 } else if (startswith(word, "luks.key=")) {
311 *arg_proc_cmdline_keyfile = strdup(word + 9);
5a8e2178 312 if (!*arg_proc_cmdline_keyfile)
951657bd
HH
313 return log_oom();
314
315 } else if (startswith(word, "rd.luks.key=")) {
316
317 if (in_initrd()) {
318 if (*arg_proc_cmdline_keyfile)
319 free(*arg_proc_cmdline_keyfile);
320 *arg_proc_cmdline_keyfile = strdup(word + 12);
5a8e2178 321 if (!*arg_proc_cmdline_keyfile)
951657bd
HH
322 return log_oom();
323 }
324
66a78c2b
LP
325 } else if (startswith(word, "luks.") ||
326 (in_initrd() && startswith(word, "rd.luks."))) {
327
328 log_warning("Unknown kernel switch %s. Ignoring.", word);
329 }
66a78c2b
LP
330 }
331
24a988e9 332 strv_uniq(*arg_proc_cmdline_disks);
66a78c2b 333
24a988e9 334 return 0;
66a78c2b
LP
335}
336
e23a0ce8 337int main(int argc, char *argv[]) {
8973790e
LP
338 _cleanup_strv_free_ char **arg_proc_cmdline_disks_done = NULL;
339 _cleanup_strv_free_ char **arg_proc_cmdline_disks = NULL;
340 _cleanup_free_ char *arg_proc_cmdline_keyfile = NULL;
7fd1b19b 341 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 342 unsigned n = 0;
24a988e9 343 int r = EXIT_SUCCESS;
66a78c2b 344 char **i;
e23a0ce8 345
07719a21
LP
346 if (argc > 1 && argc != 4) {
347 log_error("This program takes three or no arguments.");
e23a0ce8
LP
348 return EXIT_FAILURE;
349 }
350
2a796654
LP
351 if (argc > 1)
352 arg_dest = argv[1];
e23a0ce8 353
a6903061 354 log_set_target(LOG_TARGET_SAFE);
e23a0ce8
LP
355 log_parse_environment();
356 log_open();
357
4c12626c
LP
358 umask(0022);
359
951657bd 360 if (parse_proc_cmdline(&arg_proc_cmdline_disks, &arg_proc_cmdline_keyfile) < 0)
66a78c2b
LP
361 return EXIT_FAILURE;
362
24a988e9
HH
363 if (!arg_enabled)
364 return EXIT_SUCCESS;
66a78c2b 365
e2cb60fa 366 if (arg_read_crypttab) {
8973790e 367 struct stat st;
66a78c2b 368
8973790e 369 f = fopen("/etc/crypttab", "re");
e2cb60fa
HH
370 if (!f) {
371 if (errno == ENOENT)
372 r = EXIT_SUCCESS;
373 else {
374 r = EXIT_FAILURE;
375 log_error("Failed to open /etc/crypttab: %m");
376 }
8973790e
LP
377
378 goto next;
379 }
380
381 if (fstat(fileno(f), &st) < 0) {
382 log_error("Failed to stat /etc/crypttab: %m");
383 r = EXIT_FAILURE;
384 goto next;
385 }
386
2b68185a
LP
387 /* If we readd support for specifying passphrases
388 * directly in crypttabe we should upgrade the warning
389 * below, though possibly only if a passphrase is
390 * specified directly. */
8973790e 391 if (st.st_mode & 0005)
2b68185a 392 log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
8973790e
LP
393
394 for (;;) {
e2cb60fa 395 char line[LINE_MAX], *l;
7fd1b19b 396 _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
e2cb60fa 397 int k;
66a78c2b 398
e2cb60fa
HH
399 if (!fgets(line, sizeof(line), f))
400 break;
66a78c2b 401
e2cb60fa 402 n++;
66a78c2b 403
e2cb60fa
HH
404 l = strstrip(line);
405 if (*l == '#' || *l == 0)
406 continue;
e23a0ce8 407
e2cb60fa
HH
408 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
409 if (k < 2 || k > 4) {
410 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
411 r = EXIT_FAILURE;
24a988e9 412 continue;
e2cb60fa 413 }
e23a0ce8 414
e2cb60fa
HH
415 if (arg_proc_cmdline_disks) {
416 /*
417 If luks UUIDs are specified on the kernel command line, use them as a filter
418 for /etc/crypttab and only generate units for those.
419 */
420 STRV_FOREACH(i, arg_proc_cmdline_disks) {
7fd1b19b 421 _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
e2cb60fa
HH
422 const char *p = *i;
423
424 if (startswith(p, "luks-"))
425 p += 5;
426
427 proc_name = strappend("luks-", p);
428 proc_device = strappend("UUID=", p);
429
24a988e9
HH
430 if (!proc_name || !proc_device)
431 return log_oom();
432
e2cb60fa 433 if (streq(proc_device, device) || streq(proc_name, name)) {
e2cb60fa
HH
434 if (create_disk(name, device, password, options) < 0)
435 r = EXIT_FAILURE;
436
e32530cb 437 if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
24a988e9 438 return log_oom();
e2cb60fa 439 }
e2cb60fa
HH
440 }
441 } else {
442 if (create_disk(name, device, password, options) < 0)
443 r = EXIT_FAILURE;
444 }
e2cb60fa 445 }
e23a0ce8
LP
446 }
447
8973790e 448next:
e2cb60fa
HH
449 STRV_FOREACH(i, arg_proc_cmdline_disks) {
450 /*
451 Generate units for those UUIDs, which were specified
452 on the kernel command line and not yet written.
453 */
e23a0ce8 454
7fd1b19b 455 _cleanup_free_ char *name = NULL, *device = NULL;
e2cb60fa 456 const char *p = *i;
e23a0ce8 457
e2cb60fa
HH
458 if (startswith(p, "luks-"))
459 p += 5;
e23a0ce8 460
e2cb60fa 461 if (strv_contains(arg_proc_cmdline_disks_done, p))
e23a0ce8
LP
462 continue;
463
e2cb60fa
HH
464 name = strappend("luks-", p);
465 device = strappend("UUID=", p);
466
24a988e9
HH
467 if (!name || !device)
468 return log_oom();
e23a0ce8 469
951657bd 470 if (create_disk(name, device, arg_proc_cmdline_keyfile, "timeout=0") < 0)
e23a0ce8 471 r = EXIT_FAILURE;
e23a0ce8
LP
472 }
473
e23a0ce8
LP
474 return r;
475}