chiark / gitweb /
man: add reference to crypttab(5) from cryptsetup units
[elogind.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) man:crypttab(5)\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 }