chiark / gitweb /
relicense to LGPLv2.1 (with exceptions)
[elogind.git] / src / cryptsetup / cryptsetup-generator.c
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
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.
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
16   Lesser General Public License for more details.
17
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/>.
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"
29 #include "mkdir.h"
30
31 const char *arg_dest = "/tmp";
32
33 static bool has_option(const char *haystack, const char *needle) {
34         const char *f = haystack;
35         size_t l;
36
37         assert(needle);
38
39         if (!haystack)
40                 return false;
41
42         l = strlen(needle);
43
44         while ((f = strstr(f, needle))) {
45
46                 if (f > haystack && f[-1] != ',') {
47                         f++;
48                         continue;
49                 }
50
51                 if (f[l] != 0 && f[l] != ',') {
52                         f++;
53                         continue;
54                 }
55
56                 return true;
57         }
58
59         return false;
60 }
61
62 static int create_disk(
63                 const char *name,
64                 const char *device,
65                 const char *password,
66                 const char *options) {
67
68         char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *from = NULL, *to = NULL, *e = NULL;
69         int r;
70         FILE *f = NULL;
71         bool noauto, nofail;
72
73         assert(name);
74         assert(device);
75
76         noauto = has_option(options, "noauto");
77         nofail = has_option(options, "nofail");
78
79         if (!(n = unit_name_build_escape("cryptsetup", name, ".service"))) {
80                 r = -ENOMEM;
81                 log_error("Failed to allocate unit name.");
82                 goto fail;
83         }
84
85         if (asprintf(&p, "%s/%s", arg_dest, n) < 0) {
86                 r = -ENOMEM;
87                 log_error("Failed to allocate unit file name.");
88                 goto fail;
89         }
90
91         if (!(u = fstab_node_to_udev_node(device))) {
92                 r = -ENOMEM;
93                 log_error("Failed to allocate device node.");
94                 goto fail;
95         }
96
97         if (!(d = unit_name_from_path(u, ".device"))) {
98                 r = -ENOMEM;
99                 log_error("Failed to allocate device name.");
100                 goto fail;
101         }
102
103         if (!(f = fopen(p, "wxe"))) {
104                 r = -errno;
105                 log_error("Failed to create unit file: %m");
106                 goto fail;
107         }
108
109         fprintf(f,
110                 "[Unit]\n"
111                 "Description=Cryptography Setup for %%I\n"
112                 "Conflicts=umount.target\n"
113                 "DefaultDependencies=no\n"
114                 "BindTo=%s dev-mapper-%%i.device\n"
115                 "After=systemd-readahead-collect.service systemd-readahead-replay.service %s\n"
116                 "Before=umount.target\n",
117                 d, d);
118
119         if (!nofail)
120                 fprintf(f,
121                         "Before=cryptsetup.target\n");
122
123         if (password && (streq(password, "/dev/urandom") ||
124                          streq(password, "/dev/random") ||
125                          streq(password, "/dev/hw_random")))
126                 fprintf(f,
127                         "After=systemd-random-seed-load.service\n");
128         else
129                 fprintf(f,
130                         "Before=local-fs.target\n");
131
132         fprintf(f,
133                 "\n[Service]\n"
134                 "Type=oneshot\n"
135                 "RemainAfterExit=yes\n"
136                 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
137                 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
138                 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
139                 name, u, strempty(password), strempty(options),
140                 name);
141
142         if (has_option(options, "tmp"))
143                 fprintf(f,
144                         "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
145                         name);
146
147         if (has_option(options, "swap"))
148                 fprintf(f,
149                         "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
150                         name);
151
152         fflush(f);
153
154         if (ferror(f)) {
155                 r = -errno;
156                 log_error("Failed to write file: %m");
157                 goto fail;
158         }
159
160         if (asprintf(&from, "../%s", n) < 0) {
161                 r = -ENOMEM;
162                 goto fail;
163         }
164
165         if (!noauto) {
166
167                 if (asprintf(&to, "%s/%s.wants/%s", arg_dest, d, n) < 0) {
168                         r = -ENOMEM;
169                         goto fail;
170                 }
171
172                 mkdir_parents(to, 0755);
173
174                 if (symlink(from, to) < 0) {
175                         log_error("Failed to create symlink '%s' to '%s': %m", from, to);
176                         r = -errno;
177                         goto fail;
178                 }
179
180                 free(to);
181                 to = NULL;
182
183                 if (!nofail)
184                         asprintf(&to, "%s/cryptsetup.target.requires/%s", arg_dest, n);
185                 else
186                         asprintf(&to, "%s/cryptsetup.target.wants/%s", arg_dest, n);
187
188                 if (!to) {
189                         r = -ENOMEM;
190                         goto fail;
191                 }
192
193                 mkdir_parents(to, 0755);
194
195                 if (symlink(from, to) < 0) {
196                         log_error("Failed to create symlink '%s' to '%s': %m", from, to);
197                         r = -errno;
198                         goto fail;
199                 }
200         }
201
202         free(to);
203         to = NULL;
204
205         e = unit_name_escape(name);
206         if (asprintf(&to, "%s/dev-mapper-%s.device.requires/%s", arg_dest, e, n) < 0) {
207                 r = -ENOMEM;
208                 goto fail;
209         }
210
211         mkdir_parents(to, 0755);
212
213         if (symlink(from, to) < 0) {
214                 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
215                 r = -errno;
216                 goto fail;
217         }
218
219         r = 0;
220
221 fail:
222         free(p);
223         free(n);
224         free(d);
225         free(e);
226
227         free(from);
228         free(to);
229
230         if (f)
231                 fclose(f);
232
233         return r;
234 }
235
236 int main(int argc, char *argv[]) {
237         FILE *f;
238         int r = EXIT_SUCCESS;
239         unsigned n = 0;
240
241         if (argc > 2) {
242                 log_error("This program takes one or no arguments.");
243                 return EXIT_FAILURE;
244         }
245
246         if (argc > 1)
247                 arg_dest = argv[1];
248
249         log_set_target(LOG_TARGET_AUTO);
250         log_parse_environment();
251         log_open();
252
253         umask(0022);
254
255         if (!(f = fopen("/etc/crypttab", "re"))) {
256
257                 if (errno == ENOENT)
258                         r = EXIT_SUCCESS;
259                 else {
260                         r = EXIT_FAILURE;
261                         log_error("Failed to open /etc/crypttab: %m");
262                 }
263
264                 goto finish;
265         }
266
267         for (;;) {
268                 char line[LINE_MAX], *l;
269                 char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
270                 int k;
271
272                 if (!(fgets(line, sizeof(line), f)))
273                         break;
274
275                 n++;
276
277                 l = strstrip(line);
278                 if (*l == '#' || *l == 0)
279                         continue;
280
281                 if ((k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options)) < 2 || k > 4) {
282                         log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
283                         r = EXIT_FAILURE;
284                         goto next;
285                 }
286
287                 if (create_disk(name, device, password, options) < 0)
288                         r = EXIT_FAILURE;
289
290         next:
291                 free(name);
292                 free(device);
293                 free(password);
294                 free(options);
295         }
296
297 finish:
298         return r;
299 }