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 char _cleanup_free_ *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
74 FILE _cleanup_fclose_ *f = NULL;
80 noauto = has_option(options, "noauto");
81 nofail = has_option(options, "nofail");
83 n = unit_name_from_path_instance("systemd-cryptsetup", name, ".service");
87 p = strjoin(arg_dest, "/", n, NULL);
91 u = fstab_node_to_udev_node(device);
95 d = unit_name_from_path(u, ".device");
101 log_error("Failed to create unit file %s: %m", p);
106 "# Automatically generated by systemd-cryptsetup-generator\n\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",
120 "Before=cryptsetup.target\n");
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);
127 fputs("Before=local-fs.target\n", f);
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),
139 if (has_option(options, "tmp"))
141 "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
144 if (has_option(options, "swap"))
146 "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
152 log_error("Failed to write file %s: %m", p);
156 if (asprintf(&from, "../%s", n) < 0)
161 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
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);
175 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
177 to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL);
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);
191 e = unit_name_escape(name);
192 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
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);
205 static int parse_proc_cmdline(char ***arg_proc_cmdline_disks) {
206 char _cleanup_free_ *line = NULL;
207 char *w = NULL, *state = NULL;
211 if (detect_container(NULL) > 0)
214 r = read_one_line_file("/proc/cmdline", &line);
216 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
220 FOREACH_WORD_QUOTED(w, l, line, state) {
221 char _cleanup_free_ *word = NULL;
223 word = strndup(w, l);
227 if (startswith(word, "luks=")) {
228 r = parse_boolean(word + 5);
230 log_warning("Failed to parse luks switch %s. Ignoring.", word + 5);
234 } else if (startswith(word, "rd.luks=")) {
237 r = parse_boolean(word + 8);
239 log_warning("Failed to parse luks switch %s. Ignoring.", word + 8);
244 } else if (startswith(word, "luks.crypttab=")) {
245 r = parse_boolean(word + 14);
247 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 14);
249 arg_read_crypttab = r;
251 } else if (startswith(word, "rd.luks.crypttab=")) {
254 r = parse_boolean(word + 17);
256 log_warning("Failed to parse luks crypttab switch %s. Ignoring.", word + 17);
258 arg_read_crypttab = r;
261 } else if (startswith(word, "luks.uuid=")) {
264 t = strv_append(*arg_proc_cmdline_disks, word + 10);
268 strv_free(*arg_proc_cmdline_disks);
269 *arg_proc_cmdline_disks = t;
271 } else if (startswith(word, "rd.luks.uuid=")) {
276 t = strv_append(*arg_proc_cmdline_disks, word + 13);
280 strv_free(*arg_proc_cmdline_disks);
281 *arg_proc_cmdline_disks = t;
284 } else if (startswith(word, "luks.") ||
285 (in_initrd() && startswith(word, "rd.luks."))) {
287 log_warning("Unknown kernel switch %s. Ignoring.", word);
291 strv_uniq(*arg_proc_cmdline_disks);
296 int main(int argc, char *argv[]) {
297 FILE _cleanup_fclose_ *f = NULL;
299 int r = EXIT_SUCCESS;
301 char _cleanup_strv_free_ **arg_proc_cmdline_disks_done = NULL;
302 char _cleanup_strv_free_ **arg_proc_cmdline_disks = NULL;
304 if (argc > 1 && argc != 4) {
305 log_error("This program takes three or no arguments.");
312 log_set_target(LOG_TARGET_SAFE);
313 log_parse_environment();
318 if (parse_proc_cmdline(&arg_proc_cmdline_disks) < 0)
324 if (arg_read_crypttab) {
325 f = fopen("/etc/crypttab", "re");
332 log_error("Failed to open /etc/crypttab: %m");
335 char line[LINE_MAX], *l;
336 char _cleanup_free_ *name = NULL, *device = NULL, *password = NULL, *options = NULL;
339 if (!fgets(line, sizeof(line), f))
345 if (*l == '#' || *l == 0)
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);
355 if (arg_proc_cmdline_disks) {
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.
360 STRV_FOREACH(i, arg_proc_cmdline_disks) {
361 char _cleanup_free_ *proc_device = NULL, *proc_name = NULL;
364 if (startswith(p, "luks-"))
367 proc_name = strappend("luks-", p);
368 proc_device = strappend("UUID=", p);
370 if (!proc_name || !proc_device)
373 if (streq(proc_device, device) || streq(proc_name, name)) {
376 if (create_disk(name, device, password, options) < 0)
379 t = strv_append(arg_proc_cmdline_disks_done, p);
383 strv_free(arg_proc_cmdline_disks_done);
384 arg_proc_cmdline_disks_done = t;
388 if (create_disk(name, device, password, options) < 0)
394 STRV_FOREACH(i, arg_proc_cmdline_disks) {
396 Generate units for those UUIDs, which were specified
397 on the kernel command line and not yet written.
400 char _cleanup_free_ *name = NULL, *device = NULL;
403 if (startswith(p, "luks-"))
406 if (strv_contains(arg_proc_cmdline_disks_done, p))
409 name = strappend("luks-", p);
410 device = strappend("UUID=", p);
412 if (!name || !device)
415 if (create_disk(name, device, NULL, "timeout=0") < 0)