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