chiark / gitweb /
3961d5d965425fc6d9233efd58a9b5c8e17bac68
[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         n = unit_name_build_escape("cryptsetup", name, ".service");
80         if (!n) {
81                 r = -ENOMEM;
82                 log_error("Failed to allocate unit name.");
83                 goto fail;
84         }
85
86         p = join(arg_dest, "/", n, NULL);
87         if (!p) {
88                 r = -ENOMEM;
89                 log_error("Failed to allocate unit file name.");
90                 goto fail;
91         }
92
93         u = fstab_node_to_udev_node(device);
94         if (!u) {
95                 r = -ENOMEM;
96                 log_error("Failed to allocate device node.");
97                 goto fail;
98         }
99
100         d = unit_name_from_path(u, ".device");
101         if (!d) {
102                 r = -ENOMEM;
103                 log_error("Failed to allocate device name.");
104                 goto fail;
105         }
106
107         f = fopen(p, "wxe");
108         if (!f) {
109                 r = -errno;
110                 log_error("Failed to create unit file: %m");
111                 goto fail;
112         }
113
114         fprintf(f,
115                 "# Automatically generated by systemd-cryptsetup-generator\n\n"
116                 "[Unit]\n"
117                 "Description=Cryptography Setup for %%I\n"
118                 "SourcePath=/etc/crypttab\n"
119                 "Conflicts=umount.target\n"
120                 "DefaultDependencies=no\n"
121                 "BindTo=%s dev-mapper-%%i.device\n"
122                 "After=systemd-readahead-collect.service systemd-readahead-replay.service %s\n"
123                 "Before=umount.target\n",
124                 d, d);
125
126         if (!nofail)
127                 fprintf(f,
128                         "Before=cryptsetup.target\n");
129
130         if (password && (streq(password, "/dev/urandom") ||
131                          streq(password, "/dev/random") ||
132                          streq(password, "/dev/hw_random")))
133                 fputs("After=systemd-random-seed-load.service\n", f);
134         else
135                 fputs("Before=local-fs.target\n", f);
136
137         fprintf(f,
138                 "\n[Service]\n"
139                 "Type=oneshot\n"
140                 "RemainAfterExit=yes\n"
141                 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
142                 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
143                 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
144                 name, u, strempty(password), strempty(options),
145                 name);
146
147         if (has_option(options, "tmp"))
148                 fprintf(f,
149                         "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
150                         name);
151
152         if (has_option(options, "swap"))
153                 fprintf(f,
154                         "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
155                         name);
156
157         fflush(f);
158
159         if (ferror(f)) {
160                 r = -errno;
161                 log_error("Failed to write file: %m");
162                 goto fail;
163         }
164
165         if (asprintf(&from, "../%s", n) < 0) {
166                 r = -ENOMEM;
167                 goto fail;
168         }
169
170         if (!noauto) {
171
172                 to = join(arg_dest, "/", d, ".wants/", n, NULL);
173                 if (!to) {
174                         r = -ENOMEM;
175                         goto fail;
176                 }
177
178                 mkdir_parents_label(to, 0755);
179                 if (symlink(from, to) < 0) {
180                         log_error("Failed to create symlink '%s' to '%s': %m", from, to);
181                         r = -errno;
182                         goto fail;
183                 }
184
185                 free(to);
186
187                 if (!nofail)
188                         to = join(arg_dest, "/cryptsetup.target.requires/", n, NULL);
189                 else
190                         to = join(arg_dest, "/cryptsetup.target.wants/", n, NULL);
191                 if (!to) {
192                         r = -ENOMEM;
193                         goto fail;
194                 }
195
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);
199                         r = -errno;
200                         goto fail;
201                 }
202
203                 free(to);
204                 to = NULL;
205         }
206
207         e = unit_name_escape(name);
208         to = join(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
209         if (!to) {
210                 r = -ENOMEM;
211                 goto fail;
212         }
213
214         mkdir_parents_label(to, 0755);
215         if (symlink(from, to) < 0) {
216                 log_error("Failed to create symlink '%s' to '%s': %m", from, to);
217                 r = -errno;
218                 goto fail;
219         }
220
221         r = 0;
222
223 fail:
224         free(p);
225         free(n);
226         free(d);
227         free(e);
228
229         free(from);
230         free(to);
231
232         if (f)
233                 fclose(f);
234
235         return r;
236 }
237
238 int main(int argc, char *argv[]) {
239         FILE *f;
240         int r = EXIT_SUCCESS;
241         unsigned n = 0;
242
243         if (argc > 1 && argc != 4) {
244                 log_error("This program takes three or no arguments.");
245                 return EXIT_FAILURE;
246         }
247
248         if (argc > 1)
249                 arg_dest = argv[1];
250
251         log_set_target(LOG_TARGET_SAFE);
252         log_parse_environment();
253         log_open();
254
255         umask(0022);
256
257         f = fopen("/etc/crypttab", "re");
258         if (!f) {
259
260                 if (errno == ENOENT)
261                         r = EXIT_SUCCESS;
262                 else {
263                         r = EXIT_FAILURE;
264                         log_error("Failed to open /etc/crypttab: %m");
265                 }
266
267                 goto finish;
268         }
269
270         for (;;) {
271                 char line[LINE_MAX], *l;
272                 char *name = NULL, *device = NULL, *password = NULL, *options = NULL;
273                 int k;
274
275                 if (!fgets(line, sizeof(line), f))
276                         break;
277
278                 n++;
279
280                 l = strstrip(line);
281                 if (*l == '#' || *l == 0)
282                         continue;
283
284                 k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options);
285                 if (k < 2 || k > 4) {
286                         log_error("Failed to parse /etc/crypttab:%u, ignoring.", n);
287                         r = EXIT_FAILURE;
288                         goto next;
289                 }
290
291                 if (create_disk(name, device, password, options) < 0)
292                         r = EXIT_FAILURE;
293
294         next:
295                 free(name);
296                 free(device);
297                 free(password);
298                 free(options);
299         }
300
301 finish:
302         return r;
303 }