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 "IgnoreOnIsolate=true\n"
122 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n",
127 "Before=cryptsetup.target\n");
130 if (streq(password, "/dev/urandom") ||
131 streq(password, "/dev/random") ||
132 streq(password, "/dev/hw_random"))
133 fputs("After=systemd-random-seed.service\n", f);
134 else if (!streq(password, "-") &&
135 !streq(password, "none"))
137 "RequiresMountsFor=%s\n",
141 if (is_device_path(u))
145 "Before=umount.target\n",
149 "RequiresMountsFor=%s\n",
155 "RemainAfterExit=yes\n"
156 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
157 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
158 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
159 name, u, strempty(password), strempty(options),
164 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
169 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
175 log_error("Failed to write file %s: %m", p);
179 if (asprintf(&from, "../%s", n) < 0)
184 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
188 mkdir_parents_label(to, 0755);
189 if (symlink(from, to) < 0) {
190 log_error("Failed to create symlink %s: %m", to);
196 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
198 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
202 mkdir_parents_label(to, 0755);
203 if (symlink(from, to) < 0) {
204 log_error("Failed to create symlink %s: %m", to);
209 e = unit_name_escape(name);
214 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
218 mkdir_parents_label(to, 0755);
219 if (symlink(from, to) < 0) {
220 log_error("Failed to create symlink %s: %m", to);
224 if (!noauto && !nofail) {
227 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
231 mkdir_parents_label(p, 0755);
233 r = write_string_file(p,
234 "# Automatically generated by systemd-cryptsetup-generator\n\n"
236 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
244 static int parse_proc_cmdline(char ***arg_proc_cmdline_disks, char ***arg_proc_cmdline_options, char **arg_proc_cmdline_keyfile) {
245 _cleanup_free_ char *line = NULL;
246 char *w = NULL, *state = NULL;
250 if (detect_container(NULL) > 0)
253 r = read_one_line_file("/proc/cmdline", &line);
255 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
259 FOREACH_WORD_QUOTED(w, l, line, state) {
260 _cleanup_free_ char *word = NULL;
262 word = strndup(w, l);
266 if (startswith(word, "luks=")) {
267 r = parse_boolean(word + 5);
269 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
273 } else if (startswith(word, "rd.luks=")) {
276 r = parse_boolean(word + 8);
278 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
283 } else if (startswith(word, "luks.crypttab=")) {
284 r = parse_boolean(word + 14);
286 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
288 arg_read_crypttab = r;
290 } else if (startswith(word, "rd.luks.crypttab=")) {
293 r = parse_boolean(word + 17);
295 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
297 arg_read_crypttab = r;
300 } else if (startswith(word, "luks.uuid=")) {
301 if (strv_extend(arg_proc_cmdline_disks, word + 10) < 0)
304 } else if (startswith(word, "rd.luks.uuid=")) {
307 if (strv_extend(arg_proc_cmdline_disks, word + 13) < 0)
311 } else if (startswith(word, "luks.options=")) {
312 if (strv_extend(arg_proc_cmdline_options, word + 13) < 0)
315 } else if (startswith(word, "rd.luks.options=")) {
318 if (strv_extend(arg_proc_cmdline_options, word + 16) < 0)
322 } else if (startswith(word, "luks.key=")) {
323 if (*arg_proc_cmdline_keyfile)
324 free(*arg_proc_cmdline_keyfile);
325 *arg_proc_cmdline_keyfile = strdup(word + 9);
326 if (!*arg_proc_cmdline_keyfile)
329 } else if (startswith(word, "rd.luks.key=")) {
332 if (*arg_proc_cmdline_keyfile)
333 free(*arg_proc_cmdline_keyfile);
334 *arg_proc_cmdline_keyfile = strdup(word + 12);
335 if (!*arg_proc_cmdline_keyfile)
339 } else if (startswith(word, "luks.") ||
340 (in_initrd() && startswith(word, "rd.luks."))) {
342 log_warning("Unknown kernel switch %s. Ignoring.", word);
346 strv_uniq(*arg_proc_cmdline_disks);
351 int main(int argc, char *argv[]) {
352 _cleanup_strv_free_ char **arg_proc_cmdline_disks_done = NULL;
353 _cleanup_strv_free_ char **arg_proc_cmdline_disks = NULL;
354 _cleanup_strv_free_ char **arg_proc_cmdline_options = NULL;
355 _cleanup_free_ char *arg_proc_cmdline_keyfile = NULL;
356 _cleanup_fclose_ FILE *f = NULL;
358 int r = EXIT_SUCCESS;
361 if (argc > 1 && argc != 4) {
362 log_error("This program takes three or no arguments.");
369 log_set_target(LOG_TARGET_SAFE);
370 log_parse_environment();
375 if (parse_proc_cmdline(&arg_proc_cmdline_disks, &arg_proc_cmdline_options, &arg_proc_cmdline_keyfile) < 0)
381 if (arg_read_crypttab) {
384 f = fopen("/etc/crypttab", "re");
390 log_error("Failed to open /etc/crypttab: %m");
396 if (fstat(fileno(f), &st) < 0) {
397 log_error("Failed to stat /etc/crypttab: %m");
402 /* If we readd support for specifying passphrases
403 * directly in crypttabe we should upgrade the warning
404 * below, though possibly only if a passphrase is
405 * specified directly. */
406 if (st.st_mode & 0005)
407 log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
410 char line[LINE_MAX], *l;
411 _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
414 if (!fgets(line, sizeof(line), f))
420 if (*l == '#' || *l == 0)
423 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
424 if (k < 2 || k > 4) {
425 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
430 if (arg_proc_cmdline_options) {
432 If options are specified on the kernel commandline, let them override
433 the ones from crypttab.
435 STRV_FOREACH(i, arg_proc_cmdline_options) {
436 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
439 k = sscanf(p, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
440 if (k == 2 && streq(proc_uuid, device + 5)) {
450 if (arg_proc_cmdline_disks) {
452 If luks UUIDs are specified on the kernel command line, use them as a filter
453 for /etc/crypttab and only generate units for those.
455 STRV_FOREACH(i, arg_proc_cmdline_disks) {
456 _cleanup_free_ char *proc_device = NULL, *proc_name = NULL;
459 if (startswith(p, "luks-"))
462 proc_name = strappend("luks-", p);
463 proc_device = strappend("UUID=", p);
465 if (!proc_name || !proc_device)
468 if (streq(proc_device, device) || streq(proc_name, name)) {
469 if (create_disk(name, device, password, options) < 0)
472 if (strv_extend(&arg_proc_cmdline_disks_done, p) < 0)
477 if (create_disk(name, device, password, options) < 0)
484 STRV_FOREACH(i, arg_proc_cmdline_disks) {
486 Generate units for those UUIDs, which were specified
487 on the kernel command line and not yet written.
490 _cleanup_free_ char *name = NULL, *device = NULL, *options = NULL;
493 if (startswith(p, "luks-"))
496 if (strv_contains(arg_proc_cmdline_disks_done, p))
499 name = strappend("luks-", p);
500 device = strappend("UUID=", p);
502 if (!name || !device)
505 if (arg_proc_cmdline_options) {
507 If options are specified on the kernel commandline, use them.
511 STRV_FOREACH(j, arg_proc_cmdline_options) {
512 _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL;
516 k = sscanf(s, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options);
518 if (streq(proc_uuid, device + 5)) {
521 options = strdup(proc_options);
525 } else if (!options) {
527 Fall back to options without a specified UUID
537 options = strdup("timeout=0");
542 if (create_disk(name, device, arg_proc_cmdline_keyfile, options) < 0)