]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-generator.c
cgroup: do not allow manipulating the cgroup path of units within the systemd:/system...
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-generator.c
CommitLineData
e23a0ce8
LP
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
5430f7f2
LP
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
e23a0ce8
LP
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
5430f7f2 16 Lesser General Public License for more details.
e23a0ce8 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
e23a0ce8
LP
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"
49e942b2 29#include "mkdir.h"
66a78c2b
LP
30#include "virt.h"
31#include "strv.h"
a860325e 32#include "fileio.h"
e23a0ce8 33
66a78c2b
LP
34static const char *arg_dest = "/tmp";
35static bool arg_enabled = true;
36static bool arg_read_crypttab = true;
e23a0ce8
LP
37
38static bool has_option(const char *haystack, const char *needle) {
39 const char *f = haystack;
40 size_t l;
41
f653f683
LP
42 assert(needle);
43
44 if (!haystack)
45 return false;
46
e23a0ce8
LP
47 l = strlen(needle);
48
49 while ((f = strstr(f, needle))) {
50
51 if (f > haystack && f[-1] != ',') {
52 f++;
53 continue;
54 }
55
aae5220d 56 if (f[l] != 0 && f[l] != ',') {
e23a0ce8
LP
57 f++;
58 continue;
59 }
60
61 return true;
62 }
63
64 return false;
65}
66
67static int create_disk(
68 const char *name,
69 const char *device,
70 const char *password,
71 const char *options) {
72
7fd1b19b
HH
73 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 _cleanup_fclose_ FILE *f = NULL;
155da457 75 bool noauto, nofail;
e23a0ce8
LP
76
77 assert(name);
78 assert(device);
79
155da457
LP
80 noauto = has_option(options, "noauto");
81 nofail = has_option(options, "nofail");
82
35eb6b12 83 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
24a988e9
HH
84 if (!n)
85 return log_oom();
e23a0ce8 86
b7def684 87 p = strjoin(arg_dest, "/", n, NULL);
24a988e9
HH
88 if (!p)
89 return log_oom();
e23a0ce8 90
f7f21d33 91 u = fstab_node_to_udev_node(device);
24a988e9
HH
92 if (!u)
93 return log_oom();
e23a0ce8 94
f7f21d33 95 d = unit_name_from_path(u, ".device");
24a988e9
HH
96 if (!d)
97 return log_oom();
e23a0ce8 98
f7f21d33
LP
99 f = fopen(p, "wxe");
100 if (!f) {
1cda32b8 101 log_error("Failed to create unit file %s: %m", p);
24a988e9 102 return -errno;
e23a0ce8
LP
103 }
104
9ece938a 105 fputs(
6b1dc2bd 106 "# Automatically generated by systemd-cryptsetup-generator\n\n"
e23a0ce8 107 "[Unit]\n"
9ece938a 108 "Description=Cryptography Setup for %I\n"
1c732700 109 "Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
1b64d026 110 "SourcePath=/etc/crypttab\n"
155da457 111 "Conflicts=umount.target\n"
e23a0ce8 112 "DefaultDependencies=no\n"
9ece938a
TW
113 "BindsTo=dev-mapper-%i.device\n"
114 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n",
115 f);
e23a0ce8 116
155da457
LP
117 if (!nofail)
118 fprintf(f,
119 "Before=cryptsetup.target\n");
120
ceca9501
TW
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 }
e23a0ce8 132
9ece938a
TW
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
e23a0ce8
LP
144 fprintf(f,
145 "\n[Service]\n"
146 "Type=oneshot\n"
147 "RemainAfterExit=yes\n"
90724929 148 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
260ab287 149 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
7f4e0805 150 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
e23a0ce8
LP
151 name, u, strempty(password), strempty(options),
152 name);
153
f653f683 154 if (has_option(options, "tmp"))
e23a0ce8 155 fprintf(f,
50109038 156 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
1d3399e6 157 name);
e23a0ce8 158
f653f683 159 if (has_option(options, "swap"))
e23a0ce8 160 fprintf(f,
50109038 161 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
1d3399e6 162 name);
e23a0ce8
LP
163
164 fflush(f);
165
166 if (ferror(f)) {
1cda32b8 167 log_error("Failed to write file %s: %m", p);
24a988e9 168 return -errno;
e23a0ce8
LP
169 }
170
24a988e9
HH
171 if (asprintf(&from, "../%s", n) < 0)
172 return log_oom();
74715b82 173
155da457 174 if (!noauto) {
e23a0ce8 175
b7def684 176 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
24a988e9
HH
177 if (!to)
178 return log_oom();
e23a0ce8 179
d2e54fae 180 mkdir_parents_label(to, 0755);
e23a0ce8 181 if (symlink(from, to) < 0) {
c79bb9e4 182 log_error("Failed to create symlink %s: %m", to);
24a988e9 183 return -errno;
e23a0ce8 184 }
2f8cd170
LP
185
186 free(to);
155da457 187 if (!nofail)
b7def684 188 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
155da457 189 else
b7def684 190 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
24a988e9
HH
191 if (!to)
192 return log_oom();
2f8cd170 193
d2e54fae 194 mkdir_parents_label(to, 0755);
155da457 195 if (symlink(from, to) < 0) {
c79bb9e4 196 log_error("Failed to create symlink %s: %m", to);
24a988e9 197 return -errno;
2f8cd170 198 }
f7f21d33 199 }
74715b82
LP
200
201 e = unit_name_escape(name);
608d41f3
LP
202 if (!e)
203 return log_oom();
204
205 free(to);
b7def684 206 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
24a988e9
HH
207 if (!to)
208 return log_oom();
74715b82 209
d2e54fae 210 mkdir_parents_label(to, 0755);
74715b82 211 if (symlink(from, to) < 0) {
c79bb9e4 212 log_error("Failed to create symlink %s: %m", to);
24a988e9 213 return -errno;
74715b82
LP
214 }
215
68395007
HH
216 if (!noauto && !nofail) {
217 int r;
218 free(p);
219 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
220 if (!p)
221 return log_oom();
222
223 mkdir_parents_label(p, 0755);
224
225 r = write_string_file(p,
226 "# Automatically generated by systemd-cryptsetup-generator\n\n"
227 "[Unit]\n"
228 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
229 if (r)
230 return r;
231 }
232
24a988e9 233 return 0;
e23a0ce8
LP
234}
235
951657bd 236static int parse_proc_cmdline(char ***arg_proc_cmdline_disks, char **arg_proc_cmdline_keyfile) {
7fd1b19b 237 _cleanup_free_ char *line = NULL;
24a988e9 238 char *w = NULL, *state = NULL;
66a78c2b
LP
239 int r;
240 size_t l;
241
242 if (detect_container(NULL) > 0)
243 return 0;
244
245 r = read_one_line_file("/proc/cmdline", &line);
246 if (r < 0) {
247 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
248 return 0;
249 }
250
251 FOREACH_WORD_QUOTED(w, l, line, state) {
7fd1b19b 252 _cleanup_free_ char *word = NULL;
66a78c2b
LP
253
254 word = strndup(w, l);
24a988e9
HH
255 if (!word)
256 return log_oom();
66a78c2b
LP
257
258 if (startswith(word, "luks=")) {
259 r = parse_boolean(word + 5);
260 if (r < 0)
261 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
262 else
263 arg_enabled = r;
264
265 } else if (startswith(word, "rd.luks=")) {
266
267 if (in_initrd()) {
268 r = parse_boolean(word + 8);
269 if (r < 0)
270 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
271 else
272 arg_enabled = r;
273 }
274
275 } else if (startswith(word, "luks.crypttab=")) {
276 r = parse_boolean(word + 14);
277 if (r < 0)
278 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
279 else
280 arg_read_crypttab = r;
281
282 } else if (startswith(word, "rd.luks.crypttab=")) {
283
284 if (in_initrd()) {
285 r = parse_boolean(word + 17);
286 if (r < 0)
287 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
288 else
289 arg_read_crypttab = r;
290 }
291
292 } else if (startswith(word, "luks.uuid=")) {
e32530cb 293 if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
24a988e9
HH
294 return log_oom();
295
66a78c2b
LP
296 } else if (startswith(word, "rd.luks.uuid=")) {
297
298 if (in_initrd()) {
e32530cb 299 if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
24a988e9 300 return log_oom();
66a78c2b
LP
301 }
302
951657bd
HH
303 } else if (startswith(word, "luks.key=")) {
304 *arg_proc_cmdline_keyfile = strdup(word + 9);
5a8e2178 305 if (!*arg_proc_cmdline_keyfile)
951657bd
HH
306 return log_oom();
307
308 } else if (startswith(word, "rd.luks.key=")) {
309
310 if (in_initrd()) {
311 if (*arg_proc_cmdline_keyfile)
312 free(*arg_proc_cmdline_keyfile);
313 *arg_proc_cmdline_keyfile = strdup(word + 12);
5a8e2178 314 if (!*arg_proc_cmdline_keyfile)
951657bd
HH
315 return log_oom();
316 }
317
66a78c2b
LP
318 } else if (startswith(word, "luks.") ||
319 (in_initrd() && startswith(word, "rd.luks."))) {
320
321 log_warning("Unknown kernel switch %s. Ignoring.", word);
322 }
66a78c2b
LP
323 }
324
24a988e9 325 strv_uniq(*arg_proc_cmdline_disks);
66a78c2b 326
24a988e9 327 return 0;
66a78c2b
LP
328}
329
e23a0ce8 330int main(int argc, char *argv[]) {
7fd1b19b 331 _cleanup_fclose_ FILE *f = NULL;
e23a0ce8 332 unsigned n = 0;
24a988e9 333 int r = EXIT_SUCCESS;
66a78c2b 334 char **i;
7fd1b19b
HH
335 _cleanup_strv_free_ char **arg_proc_cmdline_disks_done = NULL;
336 _cleanup_strv_free_ char **arg_proc_cmdline_disks = NULL;
337 _cleanup_free_ char *arg_proc_cmdline_keyfile = NULL;
e23a0ce8 338
07719a21
LP
339 if (argc > 1 && argc != 4) {
340 log_error("This program takes three or no arguments.");
e23a0ce8
LP
341 return EXIT_FAILURE;
342 }
343
2a796654
LP
344 if (argc > 1)
345 arg_dest = argv[1];
e23a0ce8 346
a6903061 347 log_set_target(LOG_TARGET_SAFE);
e23a0ce8
LP
348 log_parse_environment();
349 log_open();
350
4c12626c
LP
351 umask(0022);
352
951657bd 353 if (parse_proc_cmdline(&arg_proc_cmdline_disks, &arg_proc_cmdline_keyfile) < 0)
66a78c2b
LP
354 return EXIT_FAILURE;
355
24a988e9
HH
356 if (!arg_enabled)
357 return EXIT_SUCCESS;
66a78c2b 358
e2cb60fa
HH
359 if (arg_read_crypttab) {
360 f = fopen("/etc/crypttab", "re");
66a78c2b 361
e2cb60fa
HH
362 if (!f) {
363 if (errno == ENOENT)
364 r = EXIT_SUCCESS;
365 else {
366 r = EXIT_FAILURE;
367 log_error("Failed to open /etc/crypttab: %m");
368 }
24a988e9 369 } else for (;;) {
e2cb60fa 370 char line[LINE_MAX], *l;
7fd1b19b 371 _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
e2cb60fa 372 int k;
66a78c2b 373
e2cb60fa
HH
374 if (!fgets(line, sizeof(line), f))
375 break;
66a78c2b 376
e2cb60fa 377 n++;
66a78c2b 378
e2cb60fa
HH
379 l = strstrip(line);
380 if (*l == '#' || *l == 0)
381 continue;
e23a0ce8 382
e2cb60fa
HH
383 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
384 if (k < 2 || k > 4) {
385 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
386 r = EXIT_FAILURE;
24a988e9 387 continue;
e2cb60fa 388 }
e23a0ce8 389
e2cb60fa
HH
390 if (arg_proc_cmdline_disks) {
391 /*
392 If luks UUIDs are specified on the kernel command line, use them as a filter
393 for /etc/crypttab and only generate units for those.
394 */
395 STRV_FOREACH(i, arg_proc_cmdline_disks) {
7fd1b19b 396 _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
e2cb60fa
HH
397 const char *p = *i;
398
399 if (startswith(p, "luks-"))
400 p += 5;
401
402 proc_name = strappend("luks-", p);
403 proc_device = strappend("UUID=", p);
404
24a988e9
HH
405 if (!proc_name || !proc_device)
406 return log_oom();
407
e2cb60fa 408 if (streq(proc_device, device) || streq(proc_name, name)) {
e2cb60fa
HH
409 if (create_disk(name, device, password, options) < 0)
410 r = EXIT_FAILURE;
411
e32530cb 412 if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
24a988e9 413 return log_oom();
e2cb60fa 414 }
e2cb60fa
HH
415 }
416 } else {
417 if (create_disk(name, device, password, options) < 0)
418 r = EXIT_FAILURE;
419 }
e2cb60fa 420 }
e23a0ce8
LP
421 }
422
e2cb60fa
HH
423 STRV_FOREACH(i, arg_proc_cmdline_disks) {
424 /*
425 Generate units for those UUIDs, which were specified
426 on the kernel command line and not yet written.
427 */
e23a0ce8 428
7fd1b19b 429 _cleanup_free_ char *name = NULL, *device = NULL;
e2cb60fa 430 const char *p = *i;
e23a0ce8 431
e2cb60fa
HH
432 if (startswith(p, "luks-"))
433 p += 5;
e23a0ce8 434
e2cb60fa 435 if (strv_contains(arg_proc_cmdline_disks_done, p))
e23a0ce8
LP
436 continue;
437
e2cb60fa
HH
438 name = strappend("luks-", p);
439 device = strappend("UUID=", p);
440
24a988e9
HH
441 if (!name || !device)
442 return log_oom();
e23a0ce8 443
951657bd 444 if (create_disk(name, device, arg_proc_cmdline_keyfile, "timeout=0") < 0)
e23a0ce8 445 r = EXIT_FAILURE;
e23a0ce8
LP
446 }
447
e23a0ce8
LP
448 return r;
449}