1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
28 #include "unit-name.h"
34 static const char *arg_dest = "/tmp";
35 static bool arg_enabled = true;
36 static bool arg_read_crypttab = true;
38 static bool has_option(const char *haystack, const char *needle) {
39 const char *f = haystack;
49 while ((f = strstr(f, needle))) {
51 if (f > haystack && f[-1] != ',') {
56 if (f[l] != 0 && f[l] != ',') {
67 static int create_disk(
71 const char *options) {
73 _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 _cleanup_fclose_ FILE *f = NULL;
75 bool noauto, nofail, tmp, swap;
80 noauto = has_option(options, "noauto");
81 nofail = has_option(options, "nofail");
82 tmp = has_option(options, "tmp");
83 swap = has_option(options, "swap");
86 log_error("Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.", name);
90 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
94 p = strjoin(arg_dest, "/", n, NULL);
98 u = fstab_node_to_udev_node(device);
102 d = unit_name_from_path(u, ".device");
108 log_error("Failed to create unit file %s: %m", p);
113 "# Automatically generated by systemd-cryptsetup-generator\n\n"
115 "Description=Cryptography Setup for %I\n"
116 "Documentation=man:systemd-cryptsetup@.service(8) man:crypttab(5)\n"
117 "SourcePath=/etc/crypttab\n"
118 "Conflicts=umount.target\n"
119 "DefaultDependencies=no\n"
120 "BindsTo=dev-mapper-%i.device\n"
121 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n",
126 "Before=cryptsetup.target\n");
129 if (streq(password, "/dev/urandom") ||
130 streq(password, "/dev/random") ||
131 streq(password, "/dev/hw_random"))
132 fputs("After=systemd-random-seed-load.service\n", f);
133 else if (!streq(password, "-") &&
134 !streq(password, "none"))
136 "RequiresMountsFor=%s\n",
140 if (is_device_path(u))
144 "Before=umount.target\n",
148 "RequiresMountsFor=%s\n",
154 "RemainAfterExit=yes\n"
155 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
156 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
157 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
158 name, u, strempty(password), strempty(options),
163 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
168 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
174 log_error("Failed to write file %s: %m", p);
178 if (asprintf(&from, "../%s", n) < 0)
183 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
187 mkdir_parents_label(to, 0755);
188 if (symlink(from, to) < 0) {
189 log_error("Failed to create symlink %s: %m", to);
195 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
197 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
201 mkdir_parents_label(to, 0755);
202 if (symlink(from, to) < 0) {
203 log_error("Failed to create symlink %s: %m", to);
208 e = unit_name_escape(name);
213 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
217 mkdir_parents_label(to, 0755);
218 if (symlink(from, to) < 0) {
219 log_error("Failed to create symlink %s: %m", to);
223 if (!noauto && !nofail) {
226 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
230 mkdir_parents_label(p, 0755);
232 r = write_string_file(p,
233 "# Automatically generated by systemd-cryptsetup-generator\n\n"
235 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
243 static int parse_proc_cmdline(char ***arg_proc_cmdline_disks, char ***arg_proc_cmdline_options, char **arg_proc_cmdline_keyfile) {
244 _cleanup_free_ char *line = NULL;
245 char *w = NULL, *state = NULL;
249 if (detect_container(NULL) > 0)
252 r = read_one_line_file("/proc/cmdline", &line);
254 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
258 FOREACH_WORD_QUOTED(w, l, line, state) {
259 _cleanup_free_ char *word = NULL;
261 word = strndup(w, l);
265 if (startswith(word, "luks=")) {
266 r = parse_boolean(word + 5);
268 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
272 } else if (startswith(word, "rd.luks=")) {
275 r = parse_boolean(word + 8);
277 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
282 } else if (startswith(word, "luks.crypttab=")) {
283 r = parse_boolean(word + 14);
285 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
287 arg_read_crypttab = r;
289 } else if (startswith(word, "rd.luks.crypttab=")) {
292 r = parse_boolean(word + 17);
294 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
296 arg_read_crypttab = r;
299 } else if (startswith(word, "luks.uuid=")) {
300 if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
303 } else if (startswith(word, "rd.luks.uuid=")) {
306 if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
310 } else if (startswith(word, "luks.options=")) {
311 if (strv_extend(arg_proc_cmdline_options, word + 13) < 0)
314 } else if (startswith(word, "rd.luks.options=")) {
317 if (strv_extend(arg_proc_cmdline_options, word + 16) < 0)
321 } else if (startswith(word, "luks.key=")) {
322 if (*arg_proc_cmdline_keyfile)
323 free(*arg_proc_cmdline_keyfile);
324 *arg_proc_cmdline_keyfile = strdup(word + 9);
325 if (!*arg_proc_cmdline_keyfile)
328 } else if (startswith(word, "rd.luks.key=")) {
331 if (*arg_proc_cmdline_keyfile)
332 free(*arg_proc_cmdline_keyfile);
333 *arg_proc_cmdline_keyfile = strdup(word + 12);
334 if (!*arg_proc_cmdline_keyfile)
338 } else if (startswith(word, "luks.") ||
339 (in_initrd() && startswith(word, "rd.luks."))) {
341 log_warning("Unknown kernel switch %s. Ignoring.", word);
345 strv_uniq(*arg_proc_cmdline_disks);
350 int main(int argc, char *argv[]) {
351 _cleanup_strv_free_ char **arg_proc_cmdline_disks_done = NULL;
352 _cleanup_strv_free_ char **arg_proc_cmdline_disks = NULL;
353 _cleanup_strv_free_ char **arg_proc_cmdline_options = NULL;
354 _cleanup_free_ char *arg_proc_cmdline_keyfile = NULL;
355 _cleanup_fclose_ FILE *f = NULL;
357 int r = EXIT_SUCCESS;
360 if (argc > 1 && argc != 4) {
361 log_error("This program takes three or no arguments.");
368 log_set_target(LOG_TARGET_SAFE);
369 log_parse_environment();
374 if (parse_proc_cmdline(&arg_proc_cmdline_disks, &arg_proc_cmdline_options, &arg_proc_cmdline_keyfile) < 0)
380 if (arg_read_crypttab) {
383 f = fopen("/etc/crypttab", "re");
389 log_error("Failed to open /etc/crypttab: %m");
395 if (fstat(fileno(f), &st) < 0) {
396 log_error("Failed to stat /etc/crypttab: %m");
401 /* If we readd support for specifying passphrases
402 * directly in crypttabe we should upgrade the warning
403 * below, though possibly only if a passphrase is
404 * specified directly. */
405 if (st.st_mode & 0005)
406 log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
409 char line[LINE_MAX], *l;
410 _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
413 if (!fgets(line, sizeof(line), f))
419 if (*l == '#' || *l == 0)
422 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
423 if (k < 2 || k > 4) {
424 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
429 if (arg_proc_cmdline_options) {
431 If options are specified on the kernel commandline, let them override
432 the ones from crypttab.
434 STRV_FOREACH(i, arg_proc_cmdline_options) {
435 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
438 k = sscanf(p, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
439 if (k == 2 && streq(proc_uuid, device + 5)) {
449 if (arg_proc_cmdline_disks) {
451 If luks UUIDs are specified on the kernel command line, use them as a filter
452 for /etc/crypttab and only generate units for those.
454 STRV_FOREACH(i, arg_proc_cmdline_disks) {
455 _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
458 if (startswith(p, "luks-"))
461 proc_name = strappend("luks-", p);
462 proc_device = strappend("UUID=", p);
464 if (!proc_name || !proc_device)
467 if (streq(proc_device, device) || streq(proc_name, name)) {
468 if (create_disk(name, device, password, options) < 0)
471 if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
476 if (create_disk(name, device, password, options) < 0)
483 STRV_FOREACH(i, arg_proc_cmdline_disks) {
485 Generate units for those UUIDs, which were specified
486 on the kernel command line and not yet written.
489 _cleanup_free_ char *name = NULL, *device = NULL, *options = NULL;
492 if (startswith(p, "luks-"))
495 if (strv_contains(arg_proc_cmdline_disks_done, p))
498 name = strappend("luks-", p);
499 device = strappend("UUID=", p);
501 if (!name || !device)
504 if (arg_proc_cmdline_options) {
506 If options are specified on the kernel commandline, use them.
510 STRV_FOREACH(j, arg_proc_cmdline_options) {
511 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
515 k = sscanf(s, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
517 if (streq(proc_uuid, device + 5)) {
520 options = strdup(proc_options);
524 } else if (!options) {
526 Fall back to options without a specified UUID
536 options = strdup("timeout=0");
541 if (create_disk(name, device, arg_proc_cmdline_keyfile, options) < 0)