]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
cryptsetup: RequiresMountsFor if source is a file
[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
24a988e9
HH
73 char _cleanup_free_ *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 FILE _cleanup_fclose_ *f = NULL;
155da457 75 bool noauto, nofail;
e23a0ce8
LP
76
77 assert(name);
78 assert(device);
79
155da457
LP
80 noauto = has_option(options, "noauto");
81 nofail = has_option(options, "nofail");
82
35eb6b12 83 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
24a988e9
HH
84 if (!n)
85 return log_oom();
e23a0ce8 86
b7def684 87 p = strjoin(arg_dest, "/", n, NULL);
24a988e9
HH
88 if (!p)
89 return log_oom();
e23a0ce8 90
f7f21d33 91 u = fstab_node_to_udev_node(device);
24a988e9
HH
92 if (!u)
93 return log_oom();
e23a0ce8 94
f7f21d33 95 d = unit_name_from_path(u, ".device");
24a988e9
HH
96 if (!d)
97 return log_oom();
e23a0ce8 98
f7f21d33
LP
99 f = fopen(p, "wxe");
100 if (!f) {
1cda32b8 101 log_error("Failed to create unit file %s: %m", p);
24a988e9 102 return -errno;
e23a0ce8
LP
103 }
104
9ece938a 105 fputs(
6b1dc2bd 106 "# Automatically generated by systemd-cryptsetup-generator\n\n"
e23a0ce8 107 "[Unit]\n"
9ece938a 108 "Description=Cryptography Setup for %I\n"
1c732700 109 "Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
1b64d026 110 "SourcePath=/etc/crypttab\n"
155da457 111 "Conflicts=umount.target\n"
e23a0ce8 112 "DefaultDependencies=no\n"
9ece938a
TW
113 "BindsTo=dev-mapper-%i.device\n"
114 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n",
115 f);
e23a0ce8 116
155da457
LP
117 if (!nofail)
118 fprintf(f,
119 "Before=cryptsetup.target\n");
120
e23a0ce8
LP
121 if (password && (streq(password, "/dev/urandom") ||
122 streq(password, "/dev/random") ||
123 streq(password, "/dev/hw_random")))
1b64d026 124 fputs("After=systemd-random-seed-load.service\n", f);
87e75fdd 125 else
1b64d026 126 fputs("Before=local-fs.target\n", f);
e23a0ce8 127
9ece938a
TW
128 if (is_device_path(u))
129 fprintf(f,
130 "BindsTo=%s\n"
131 "After=%s\n"
132 "Before=umount.target\n",
133 d, d);
134 else
135 fprintf(f,
136 "RequiresMountsFor=%s\n",
137 u);
138
e23a0ce8
LP
139 fprintf(f,
140 "\n[Service]\n"
141 "Type=oneshot\n"
142 "RemainAfterExit=yes\n"
90724929 143 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
260ab287 144 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
7f4e0805 145 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
e23a0ce8
LP
146 name, u, strempty(password), strempty(options),
147 name);
148
f653f683 149 if (has_option(options, "tmp"))
e23a0ce8 150 fprintf(f,
50109038 151 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
1d3399e6 152 name);
e23a0ce8 153
f653f683 154 if (has_option(options, "swap"))
e23a0ce8 155 fprintf(f,
50109038 156 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
1d3399e6 157 name);
e23a0ce8
LP
158
159 fflush(f);
160
161 if (ferror(f)) {
1cda32b8 162 log_error("Failed to write file %s: %m", p);
24a988e9 163 return -errno;
e23a0ce8
LP
164 }
165
24a988e9
HH
166 if (asprintf(&from, "../%s", n) < 0)
167 return log_oom();
74715b82 168
155da457 169 if (!noauto) {
e23a0ce8 170
b7def684 171 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
24a988e9
HH
172 if (!to)
173 return log_oom();
e23a0ce8 174
d2e54fae 175 mkdir_parents_label(to, 0755);
e23a0ce8
LP
176 if (symlink(from, to) < 0) {
177 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
24a988e9 178 return -errno;
e23a0ce8 179 }
2f8cd170
LP
180
181 free(to);
155da457 182 if (!nofail)
b7def684 183 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
155da457 184 else
b7def684 185 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
24a988e9
HH
186 if (!to)
187 return log_oom();
2f8cd170 188
d2e54fae 189 mkdir_parents_label(to, 0755);
155da457
LP
190 if (symlink(from, to) < 0) {
191 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
24a988e9 192 return -errno;
2f8cd170 193 }
f7f21d33 194 }
74715b82
LP
195
196 e = unit_name_escape(name);
608d41f3
LP
197 if (!e)
198 return log_oom();
199
200 free(to);
b7def684 201 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
24a988e9
HH
202 if (!to)
203 return log_oom();
74715b82 204
d2e54fae 205 mkdir_parents_label(to, 0755);
74715b82
LP
206 if (symlink(from, to) < 0) {
207 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
24a988e9 208 return -errno;
74715b82
LP
209 }
210
24a988e9 211 return 0;
e23a0ce8
LP
212}
213
24a988e9
HH
214static int parse_proc_cmdline(char ***arg_proc_cmdline_disks) {
215 char _cleanup_free_ *line = NULL;
216 char *w = NULL, *state = NULL;
66a78c2b
LP
217 int r;
218 size_t l;
219
220 if (detect_container(NULL) > 0)
221 return 0;
222
223 r = read_one_line_file("/proc/cmdline", &line);
224 if (r < 0) {
225 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
226 return 0;
227 }
228
229 FOREACH_WORD_QUOTED(w, l, line, state) {
24a988e9 230 char _cleanup_free_ *word = NULL;
66a78c2b
LP
231
232 word = strndup(w, l);
24a988e9
HH
233 if (!word)
234 return log_oom();
66a78c2b
LP
235
236 if (startswith(word, "luks=")) {
237 r = parse_boolean(word + 5);
238 if (r < 0)
239 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
240 else
241 arg_enabled = r;
242
243 } else if (startswith(word, "rd.luks=")) {
244
245 if (in_initrd()) {
246 r = parse_boolean(word + 8);
247 if (r < 0)
248 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
249 else
250 arg_enabled = r;
251 }
252
253 } else if (startswith(word, "luks.crypttab=")) {
254 r = parse_boolean(word + 14);
255 if (r < 0)
256 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
257 else
258 arg_read_crypttab = r;
259
260 } else if (startswith(word, "rd.luks.crypttab=")) {
261
262 if (in_initrd()) {
263 r = parse_boolean(word + 17);
264 if (r < 0)
265 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
266 else
267 arg_read_crypttab = r;
268 }
269
270 } else if (startswith(word, "luks.uuid=")) {
e32530cb 271 if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
24a988e9
HH
272 return log_oom();
273
66a78c2b
LP
274 } else if (startswith(word, "rd.luks.uuid=")) {
275
276 if (in_initrd()) {
e32530cb 277 if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
24a988e9 278 return log_oom();
66a78c2b
LP
279 }
280
281 } else if (startswith(word, "luks.") ||
282 (in_initrd() && startswith(word, "rd.luks."))) {
283
284 log_warning("Unknown kernel switch %s. Ignoring.", word);
285 }
66a78c2b
LP
286 }
287
24a988e9 288 strv_uniq(*arg_proc_cmdline_disks);
66a78c2b 289
24a988e9 290 return 0;
66a78c2b
LP
291}
292
e23a0ce8 293int main(int argc, char *argv[]) {
24a988e9 294 FILE _cleanup_fclose_ *f = NULL;
e23a0ce8 295 unsigned n = 0;
24a988e9 296 int r = EXIT_SUCCESS;
66a78c2b 297 char **i;
24a988e9
HH
298 char _cleanup_strv_free_ **arg_proc_cmdline_disks_done = NULL;
299 char _cleanup_strv_free_ **arg_proc_cmdline_disks = NULL;
e23a0ce8 300
07719a21
LP
301 if (argc > 1 && argc != 4) {
302 log_error("This program takes three or no arguments.");
e23a0ce8
LP
303 return EXIT_FAILURE;
304 }
305
2a796654
LP
306 if (argc > 1)
307 arg_dest = argv[1];
e23a0ce8 308
a6903061 309 log_set_target(LOG_TARGET_SAFE);
e23a0ce8
LP
310 log_parse_environment();
311 log_open();
312
4c12626c
LP
313 umask(0022);
314
24a988e9 315 if (parse_proc_cmdline(&arg_proc_cmdline_disks) < 0)
66a78c2b
LP
316 return EXIT_FAILURE;
317
24a988e9
HH
318 if (!arg_enabled)
319 return EXIT_SUCCESS;
66a78c2b 320
e2cb60fa
HH
321 if (arg_read_crypttab) {
322 f = fopen("/etc/crypttab", "re");
66a78c2b 323
e2cb60fa
HH
324 if (!f) {
325 if (errno == ENOENT)
326 r = EXIT_SUCCESS;
327 else {
328 r = EXIT_FAILURE;
329 log_error("Failed to open /etc/crypttab: %m");
330 }
24a988e9 331 } else for (;;) {
e2cb60fa 332 char line[LINE_MAX], *l;
24a988e9 333 char _cleanup_free_ *name = NULL, *device = NULL, *password = NULL, *options = NULL;
e2cb60fa 334 int k;
66a78c2b 335
e2cb60fa
HH
336 if (!fgets(line, sizeof(line), f))
337 break;
66a78c2b 338
e2cb60fa 339 n++;
66a78c2b 340
e2cb60fa
HH
341 l = strstrip(line);
342 if (*l == '#' || *l == 0)
343 continue;
e23a0ce8 344
e2cb60fa
HH
345 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
346 if (k < 2 || k > 4) {
347 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
348 r = EXIT_FAILURE;
24a988e9 349 continue;
e2cb60fa 350 }
e23a0ce8 351
e2cb60fa
HH
352 if (arg_proc_cmdline_disks) {
353 /*
354 If luks UUIDs are specified on the kernel command line, use them as a filter
355 for /etc/crypttab and only generate units for those.
356 */
357 STRV_FOREACH(i, arg_proc_cmdline_disks) {
24a988e9 358 char _cleanup_free_ *proc_device = NULL, *proc_name = NULL;
e2cb60fa
HH
359 const char *p = *i;
360
361 if (startswith(p, "luks-"))
362 p += 5;
363
364 proc_name = strappend("luks-", p);
365 proc_device = strappend("UUID=", p);
366
24a988e9
HH
367 if (!proc_name || !proc_device)
368 return log_oom();
369
e2cb60fa 370 if (streq(proc_device, device) || streq(proc_name, name)) {
e2cb60fa
HH
371 if (create_disk(name, device, password, options) < 0)
372 r = EXIT_FAILURE;
373
e32530cb 374 if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
24a988e9 375 return log_oom();
e2cb60fa 376 }
e2cb60fa
HH
377 }
378 } else {
379 if (create_disk(name, device, password, options) < 0)
380 r = EXIT_FAILURE;
381 }
e2cb60fa 382 }
e23a0ce8
LP
383 }
384
e2cb60fa
HH
385 STRV_FOREACH(i, arg_proc_cmdline_disks) {
386 /*
387 Generate units for those UUIDs, which were specified
388 on the kernel command line and not yet written.
389 */
e23a0ce8 390
24a988e9 391 char _cleanup_free_ *name = NULL, *device = NULL;
e2cb60fa 392 const char *p = *i;
e23a0ce8 393
e2cb60fa
HH
394 if (startswith(p, "luks-"))
395 p += 5;
e23a0ce8 396
e2cb60fa 397 if (strv_contains(arg_proc_cmdline_disks_done, p))
e23a0ce8
LP
398 continue;
399
e2cb60fa
HH
400 name = strappend("luks-", p);
401 device = strappend("UUID=", p);
402
24a988e9
HH
403 if (!name || !device)
404 return log_oom();
e23a0ce8 405
e2cb60fa 406 if (create_disk(name, device, NULL, "timeout=0") < 0)
e23a0ce8 407 r = EXIT_FAILURE;
e23a0ce8
LP
408 }
409
e23a0ce8
LP
410 return r;
411}