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