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