]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
log.h: new log_oom() -> int -ENOMEM, use it
[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 = strjoin(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 "BindsTo=%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 = strjoin(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 = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
195 else
196 to = strjoin(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 = strjoin(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 r = log_oom();
307 goto finish;
308 }
309 strv_free(arg_proc_cmdline_disks);
310 arg_proc_cmdline_disks = t;
311
312 } else if (startswith(word, "rd.luks.uuid=")) {
313
314 if (in_initrd()) {
315 char **t;
316
317 t = strv_append(arg_proc_cmdline_disks, word + 13);
318 if (!t) {
319 r = log_oom();
320 goto finish;
321 }
322 strv_free(arg_proc_cmdline_disks);
323 arg_proc_cmdline_disks = t;
324 }
325
326 } else if (startswith(word, "luks.") ||
327 (in_initrd() && startswith(word, "rd.luks."))) {
328
329 log_warning("Unknown kernel switch %s. Ignoring.", word);
330 }
331
332 free(word);
333 }
334
335 r = 0;
336
337 finish:
338 free(line);
339 return r;
340 }
341
342 int main(int argc, char *argv[]) {
343 FILE *f = NULL;
344 int r = EXIT_SUCCESS;
345 unsigned n = 0;
346 char **i;
347
348 if (argc > 1 && argc != 4) {
349 log_error("This program takes three or no arguments.");
350 return EXIT_FAILURE;
351 }
352
353 if (argc > 1)
354 arg_dest = argv[1];
355
356 log_set_target(LOG_TARGET_SAFE);
357 log_parse_environment();
358 log_open();
359
360 umask(0022);
361
362 if (parse_proc_cmdline() < 0)
363 return EXIT_FAILURE;
364
365 if (!arg_enabled) {
366 r = EXIT_SUCCESS;
367 goto finish;
368 }
369
370 STRV_FOREACH(i, arg_proc_cmdline_disks) {
371 char *name, *device;
372 const char *p = *i;
373
374 if (startswith(p, "luks-"))
375 p += 5;
376
377 name = strappend("luks-", *i);
378 device = strappend("UUID=", *i);
379
380 if (!name || !device) {
381 log_oom();
382 r = EXIT_FAILURE;
383 free(name);
384 free(device);
385 goto finish;
386 }
387
388 if (create_disk(name, device, NULL, NULL) < 0)
389 r = EXIT_FAILURE;
390
391 free(name);
392 free(device);
393 }
394
395 if (!arg_read_crypttab)
396 return r;
397
398 f = fopen("/etc/crypttab", "re");
399 if (!f) {
400
401 if (errno == ENOENT)
402 r = EXIT_SUCCESS;
403 else {
404 r = EXIT_FAILURE;
405 log_error("Failed to open /etc/crypttab: %m");
406 }
407
408 goto finish;
409 }
410
411 for (;;) {
412 char line[LINE_MAX], *l;
413 char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
414 int k;
415
416 if (!fgets(line, sizeof(line), f))
417 break;
418
419 n++;
420
421 l = strstrip(line);
422 if (*l == '#' || *l == 0)
423 continue;
424
425 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
426 if (k < 2 || k > 4) {
427 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
428 r = EXIT_FAILURE;
429 goto next;
430 }
431
432 if (create_disk(name, device, password, options) < 0)
433 r = EXIT_FAILURE;
434
435 next:
436 free(name);
437 free(device);
438 free(password);
439 free(options);
440 }
441
442 finish:
443 if (f)
444 fclose(f);
445
446 strv_free(arg_proc_cmdline_disks);
447
448 return r;
449 }