]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup-generator.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.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 }