]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
man: document systemd-cryptsetup
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-generator.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
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"
29 #include "mkdir.h"
30 #include "virt.h"
31 #include "strv.h"
32
33 static const char *arg_dest = "/tmp";
34 static bool arg_enabled = true;
35 static bool arg_read_crypttab = true;
36 static char **arg_proc_cmdline_disks = NULL;
37
38 static bool has_option(const char *haystack, const char *needle) {
39 const char *f = haystack;
40 size_t l;
41
42 assert(needle);
43
44 if (!haystack)
45 return false;
46
47 l = strlen(needle);
48
49 while ((f = strstr(f, needle))) {
50
51 if (f > haystack && f[-1] != ',') {
52 f++;
53 continue;
54 }
55
56 if (f[l] != 0 && f[l] != ',') {
57 f++;
58 continue;
59 }
60
61 return true;
62 }
63
64 return false;
65 }
66
67 static int create_disk(
68 const char *name,
69 const char *device,
70 const char *password,
71 const char *options) {
72
73 char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 int r;
75 FILE *f = NULL;
76 bool noauto, nofail;
77
78 assert(name);
79 assert(device);
80
81 noauto = has_option(options, "noauto");
82 nofail = has_option(options, "nofail");
83
84 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
85 if (!n) {
86 r = -ENOMEM;
87 log_error("Failed to allocate unit name.");
88 goto fail;
89 }
90
91 p = join(arg_dest, "/", n, NULL);
92 if (!p) {
93 r = -ENOMEM;
94 log_error("Failed to allocate unit file name.");
95 goto fail;
96 }
97
98 u = fstab_node_to_udev_node(device);
99 if (!u) {
100 r = -ENOMEM;
101 log_error("Failed to allocate device node.");
102 goto fail;
103 }
104
105 d = unit_name_from_path(u, ".device");
106 if (!d) {
107 r = -ENOMEM;
108 log_error("Failed to allocate device name.");
109 goto fail;
110 }
111
112 f = fopen(p, "wxe");
113 if (!f) {
114 r = -errno;
115 log_error("Failed to create unit file: %m");
116 goto fail;
117 }
118
119 fprintf(f,
120 "# Automatically generated by systemd-cryptsetup-generator\n\n"
121 "[Unit]\n"
122 "Description=Cryptography Setup for %%I\n"
123 "Documentation=man:systemd-cryptsetup@.service(8)\n"
124 "SourcePath=/etc/crypttab\n"
125 "Conflicts=umount.target\n"
126 "DefaultDependencies=no\n"
127 "BindTo=%s dev-mapper-%%i.device\n"
128 "After=systemd-readahead-collect.service systemd-readahead-replay.service %s\n"
129 "Before=umount.target\n",
130 d, d);
131
132 if (!nofail)
133 fprintf(f,
134 "Before=cryptsetup.target\n");
135
136 if (password && (streq(password, "/dev/urandom") ||
137 streq(password, "/dev/random") ||
138 streq(password, "/dev/hw_random")))
139 fputs("After=systemd-random-seed-load.service\n", f);
140 else
141 fputs("Before=local-fs.target\n", f);
142
143 fprintf(f,
144 "\n[Service]\n"
145 "Type=oneshot\n"
146 "RemainAfterExit=yes\n"
147 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
148 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
149 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
150 name, u, strempty(password), strempty(options),
151 name);
152
153 if (has_option(options, "tmp"))
154 fprintf(f,
155 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
156 name);
157
158 if (has_option(options, "swap"))
159 fprintf(f,
160 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
161 name);
162
163 fflush(f);
164
165 if (ferror(f)) {
166 r = -errno;
167 log_error("Failed to write file: %m");
168 goto fail;
169 }
170
171 if (asprintf(&from, "../%s", n) < 0) {
172 r = -ENOMEM;
173 goto fail;
174 }
175
176 if (!noauto) {
177
178 to = join(arg_dest, "/", d, ".wants/", n, NULL);
179 if (!to) {
180 r = -ENOMEM;
181 goto fail;
182 }
183
184 mkdir_parents_label(to, 0755);
185 if (symlink(from, to) < 0) {
186 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
187 r = -errno;
188 goto fail;
189 }
190
191 free(to);
192
193 if (!nofail)
194 to = join(arg_dest, "/cryptsetup.target.requires/", n, NULL);
195 else
196 to = join(arg_dest, "/cryptsetup.target.wants/", n, NULL);
197 if (!to) {
198 r = -ENOMEM;
199 goto fail;
200 }
201
202 mkdir_parents_label(to, 0755);
203 if (symlink(from, to) < 0) {
204 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
205 r = -errno;
206 goto fail;
207 }
208
209 free(to);
210 to = NULL;
211 }
212
213 e = unit_name_escape(name);
214 to = join(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
215 if (!to) {
216 r = -ENOMEM;
217 goto fail;
218 }
219
220 mkdir_parents_label(to, 0755);
221 if (symlink(from, to) < 0) {
222 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
223 r = -errno;
224 goto fail;
225 }
226
227 r = 0;
228
229 fail:
230 free(p);
231 free(n);
232 free(d);
233 free(e);
234
235 free(from);
236 free(to);
237
238 if (f)
239 fclose(f);
240
241 return r;
242 }
243
244 static int parse_proc_cmdline(void) {
245 char *line, *w, *state;
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) {
259 char *word;
260
261 word = strndup(w, l);
262 if (!word) {
263 r = -ENOMEM;
264 goto finish;
265 }
266
267 if (startswith(word, "luks=")) {
268 r = parse_boolean(word + 5);
269 if (r < 0)
270 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
271 else
272 arg_enabled = r;
273
274 } else if (startswith(word, "rd.luks=")) {
275
276 if (in_initrd()) {
277 r = parse_boolean(word + 8);
278 if (r < 0)
279 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
280 else
281 arg_enabled = r;
282 }
283
284 } else if (startswith(word, "luks.crypttab=")) {
285 r = parse_boolean(word + 14);
286 if (r < 0)
287 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
288 else
289 arg_read_crypttab = r;
290
291 } else if (startswith(word, "rd.luks.crypttab=")) {
292
293 if (in_initrd()) {
294 r = parse_boolean(word + 17);
295 if (r < 0)
296 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
297 else
298 arg_read_crypttab = r;
299 }
300
301 } else if (startswith(word, "luks.uuid=")) {
302 char **t;
303
304 t = strv_append(arg_proc_cmdline_disks, word + 10);
305 if (!t) {
306 log_error("Out of memory");
307 r = -ENOMEM;
308 goto finish;
309 }
310 strv_free(arg_proc_cmdline_disks);
311 arg_proc_cmdline_disks = t;
312
313 } else if (startswith(word, "rd.luks.uuid=")) {
314
315 if (in_initrd()) {
316 char **t;
317
318 t = strv_append(arg_proc_cmdline_disks, word + 13);
319 if (!t) {
320 log_error("Out of memory");
321 r = -ENOMEM;
322 goto finish;
323 }
324 strv_free(arg_proc_cmdline_disks);
325 arg_proc_cmdline_disks = t;
326 }
327
328 } else if (startswith(word, "luks.") ||
329 (in_initrd() && startswith(word, "rd.luks."))) {
330
331 log_warning("Unknown kernel switch %s. Ignoring.", word);
332 }
333
334 free(word);
335 }
336
337 r = 0;
338
339 finish:
340 free(line);
341 return r;
342 }
343
344 int main(int argc, char *argv[]) {
345 FILE *f = NULL;
346 int r = EXIT_SUCCESS;
347 unsigned n = 0;
348 char **i;
349
350 if (argc > 1 && argc != 4) {
351 log_error("This program takes three or no arguments.");
352 return EXIT_FAILURE;
353 }
354
355 if (argc > 1)
356 arg_dest = argv[1];
357
358 log_set_target(LOG_TARGET_SAFE);
359 log_parse_environment();
360 log_open();
361
362 umask(0022);
363
364 if (parse_proc_cmdline() < 0)
365 return EXIT_FAILURE;
366
367 if (!arg_enabled) {
368 r = EXIT_SUCCESS;
369 goto finish;
370 }
371
372 STRV_FOREACH(i, arg_proc_cmdline_disks) {
373 char *name, *device;
374 const char *p = *i;
375
376 if (startswith(p, "luks-"))
377 p += 5;
378
379 name = strappend("luks-", *i);
380 device = strappend("UUID=", *i);
381
382 if (!name || !device) {
383 log_error("Out of memory");
384 r = EXIT_FAILURE;
385 free(name);
386 free(device);
387 goto finish;
388 }
389
390 if (create_disk(name, device, NULL, NULL) < 0)
391 r = EXIT_FAILURE;
392
393 free(name);
394 free(device);
395 }
396
397 if (!arg_read_crypttab)
398 return r;
399
400 f = fopen("/etc/crypttab", "re");
401 if (!f) {
402
403 if (errno == ENOENT)
404 r = EXIT_SUCCESS;
405 else {
406 r = EXIT_FAILURE;
407 log_error("Failed to open /etc/crypttab: %m");
408 }
409
410 goto finish;
411 }
412
413 for (;;) {
414 char line[LINE_MAX], *l;
415 char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
416 int k;
417
418 if (!fgets(line, sizeof(line), f))
419 break;
420
421 n++;
422
423 l = strstrip(line);
424 if (*l == '#' || *l == 0)
425 continue;
426
427 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
428 if (k < 2 || k > 4) {
429 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
430 r = EXIT_FAILURE;
431 goto next;
432 }
433
434 if (create_disk(name, device, password, options) < 0)
435 r = EXIT_FAILURE;
436
437 next:
438 free(name);
439 free(device);
440 free(password);
441 free(options);
442 }
443
444 finish:
445 if (f)
446 fclose(f);
447
448 strv_free(arg_proc_cmdline_disks);
449
450 return r;
451 }