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_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.key=")) {
311 *arg_proc_cmdline_keyfile
= strdup(word
+ 9);
312 if (!*arg_proc_cmdline_keyfile
)
315 } else if (startswith(word
, "rd.luks.key=")) {
318 if (*arg_proc_cmdline_keyfile
)
319 free(*arg_proc_cmdline_keyfile
);
320 *arg_proc_cmdline_keyfile
= strdup(word
+ 12);
321 if (!*arg_proc_cmdline_keyfile
)
325 } else if (startswith(word
, "luks.") ||
326 (in_initrd() && startswith(word
, "rd.luks."))) {
328 log_warning("Unknown kernel switch %s. Ignoring.", word
);
332 strv_uniq(*arg_proc_cmdline_disks
);
337 int main(int argc
, char *argv
[]) {
338 _cleanup_strv_free_
char **arg_proc_cmdline_disks_done
= NULL
;
339 _cleanup_strv_free_
char **arg_proc_cmdline_disks
= NULL
;
340 _cleanup_free_
char *arg_proc_cmdline_keyfile
= NULL
;
341 _cleanup_fclose_
FILE *f
= NULL
;
343 int r
= EXIT_SUCCESS
;
346 if (argc
> 1 && argc
!= 4) {
347 log_error("This program takes three or no arguments.");
354 log_set_target(LOG_TARGET_SAFE
);
355 log_parse_environment();
360 if (parse_proc_cmdline(&arg_proc_cmdline_disks
, &arg_proc_cmdline_keyfile
) < 0)
366 if (arg_read_crypttab
) {
369 f
= fopen("/etc/crypttab", "re");
375 log_error("Failed to open /etc/crypttab: %m");
381 if (fstat(fileno(f
), &st
) < 0) {
382 log_error("Failed to stat /etc/crypttab: %m");
387 /* If we readd support for specifying passphrases
388 * directly in crypttabe we should upgrade the warning
389 * below, though possibly only if a passphrase is
390 * specified directly. */
391 if (st
.st_mode
& 0005)
392 log_debug("/etc/crypttab is world-readable. This is usually not a good idea.");
395 char line
[LINE_MAX
], *l
;
396 _cleanup_free_
char *name
= NULL
, *device
= NULL
, *password
= NULL
, *options
= NULL
;
399 if (!fgets(line
, sizeof(line
), f
))
405 if (*l
== '#' || *l
== 0)
408 k
= sscanf(l
, "%ms %ms %ms %ms", &name
, &device
, &password
, &options
);
409 if (k
< 2 || k
> 4) {
410 log_error("Failed to parse /etc/crypttab:%u, ignoring.", n
);
415 if (arg_proc_cmdline_disks
) {
417 If luks UUIDs are specified on the kernel command line, use them as a filter
418 for /etc/crypttab and only generate units for those.
420 STRV_FOREACH(i
, arg_proc_cmdline_disks
) {
421 _cleanup_free_
char *proc_device
= NULL
, *proc_name
= NULL
;
424 if (startswith(p
, "luks-"))
427 proc_name
= strappend("luks-", p
);
428 proc_device
= strappend("UUID=", p
);
430 if (!proc_name
|| !proc_device
)
433 if (streq(proc_device
, device
) || streq(proc_name
, name
)) {
434 if (create_disk(name
, device
, password
, options
) < 0)
437 if (strv_extend(&arg_proc_cmdline_disks_done
, p
) < 0)
442 if (create_disk(name
, device
, password
, options
) < 0)
449 STRV_FOREACH(i
, arg_proc_cmdline_disks
) {
451 Generate units for those UUIDs, which were specified
452 on the kernel command line and not yet written.
455 _cleanup_free_
char *name
= NULL
, *device
= NULL
;
458 if (startswith(p
, "luks-"))
461 if (strv_contains(arg_proc_cmdline_disks_done
, p
))
464 name
= strappend("luks-", p
);
465 device
= strappend("UUID=", p
);
467 if (!name
|| !device
)
470 if (create_disk(name
, device
, arg_proc_cmdline_keyfile
, "timeout=0") < 0)