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