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