]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
fstab-generator: Generate explicit dependencies on systemd-fsck@.service instead...
[thirdparty/systemd.git] / src / fstab-generator / fstab-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 2012 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 <stdio.h>
23 #include <mntent.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "log.h"
29 #include "util.h"
30 #include "unit-name.h"
31 #include "path-util.h"
32 #include "mount-setup.h"
33 #include "special.h"
34 #include "mkdir.h"
35 #include "virt.h"
36 #include "fileio.h"
37
38 static const char *arg_dest = "/tmp";
39 static bool arg_enabled = true;
40
41 static int mount_find_pri(struct mntent *me, int *ret) {
42 char *end, *pri;
43 unsigned long r;
44
45 assert(me);
46 assert(ret);
47
48 pri = hasmntopt(me, "pri");
49 if (!pri)
50 return 0;
51
52 pri += 4;
53
54 errno = 0;
55 r = strtoul(pri, &end, 10);
56 if (errno > 0)
57 return -errno;
58
59 if (end == pri || (*end != ',' && *end != 0))
60 return -EINVAL;
61
62 *ret = (int) r;
63 return 1;
64 }
65
66 static int add_swap(const char *what, struct mntent *me) {
67 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
68 _cleanup_fclose_ FILE *f = NULL;
69 bool noauto;
70 int r, pri = -1;
71
72 assert(what);
73 assert(me);
74
75 r = mount_find_pri(me, &pri);
76 if (r < 0) {
77 log_error("Failed to parse priority");
78 return pri;
79 }
80
81 noauto = !!hasmntopt(me, "noauto");
82
83 name = unit_name_from_path(what, ".swap");
84 if (!name)
85 return log_oom();
86
87 unit = strjoin(arg_dest, "/", name, NULL);
88 if (!unit)
89 return log_oom();
90
91 f = fopen(unit, "wxe");
92 if (!f) {
93 if (errno == EEXIST)
94 log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
95 else
96 log_error("Failed to create unit file %s: %m", unit);
97 return -errno;
98 }
99
100 fprintf(f,
101 "# Automatically generated by systemd-fstab-generator\n\n"
102 "[Unit]\n"
103 "SourcePath=/etc/fstab\n\n"
104 "[Swap]\n"
105 "What=%s\n",
106 what);
107
108 if (pri >= 0)
109 fprintf(f,
110 "Priority=%i\n",
111 pri);
112
113 fflush(f);
114 if (ferror(f)) {
115 log_error("Failed to write unit file %s: %m", unit);
116 return -errno;
117 }
118
119 if (!noauto) {
120 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
121 if (!lnk)
122 return log_oom();
123
124 mkdir_parents_label(lnk, 0755);
125 if (symlink(unit, lnk) < 0) {
126 log_error("Failed to create symlink %s: %m", lnk);
127 return -errno;
128 }
129 }
130
131 return 0;
132 }
133
134 static bool mount_is_network(struct mntent *me) {
135 assert(me);
136
137 return
138 hasmntopt(me, "_netdev") ||
139 fstype_is_network(me->mnt_type);
140 }
141
142 static bool mount_in_initrd(struct mntent *me) {
143 assert(me);
144
145 return
146 hasmntopt(me, "x-initrd.mount") ||
147 streq(me->mnt_dir, "/usr");
148 }
149
150 static int add_mount(
151 const char *what,
152 const char *where,
153 const char *type,
154 const char *opts,
155 int passno,
156 bool noauto,
157 bool nofail,
158 bool automount,
159 const char *post,
160 const char *source) {
161 _cleanup_free_ char
162 *name = NULL, *unit = NULL, *lnk = NULL,
163 *automount_name = NULL, *automount_unit = NULL;
164 _cleanup_fclose_ FILE *f = NULL;
165
166 assert(what);
167 assert(where);
168 assert(type);
169 assert(opts);
170 assert(source);
171
172 if (streq(type, "autofs"))
173 return 0;
174
175 if (!is_path(where)) {
176 log_warning("Mount point %s is not a valid path, ignoring.", where);
177 return 0;
178 }
179
180 if (mount_point_is_api(where) ||
181 mount_point_ignore(where))
182 return 0;
183
184 name = unit_name_from_path(where, ".mount");
185 if (!name)
186 return log_oom();
187
188 unit = strjoin(arg_dest, "/", name, NULL);
189 if (!unit)
190 return log_oom();
191
192 f = fopen(unit, "wxe");
193 if (!f) {
194 if (errno == EEXIST)
195 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
196 else
197 log_error("Failed to create unit file %s: %m", unit);
198 return -errno;
199 }
200
201 fprintf(f,
202 "# Automatically generated by systemd-fstab-generator\n\n"
203 "[Unit]\n"
204 "SourcePath=%s\n",
205 source);
206
207 if (post && !noauto && !nofail && !automount)
208 fprintf(f,
209 "Before=%s\n",
210 post);
211
212 if (passno > 0) {
213 _cleanup_free_ char *fsck = NULL;
214
215 fsck = unit_name_from_path_instance("systemd-fsck", what, ".service");
216 if (!fsck)
217 return log_oom();
218
219 fprintf(f,
220 "Requires=%s\n"
221 "After=%s\n",
222 fsck,
223 fsck);
224 }
225
226
227 fprintf(f,
228 "\n"
229 "[Mount]\n"
230 "What=%s\n"
231 "Where=%s\n"
232 "Type=%s\n",
233 what,
234 where,
235 type);
236
237 if (!isempty(opts) &&
238 !streq(opts, "defaults"))
239 fprintf(f,
240 "Options=%s\n",
241 opts);
242
243 fflush(f);
244 if (ferror(f)) {
245 log_error("Failed to write unit file %s: %m", unit);
246 return -errno;
247 }
248
249 if (!noauto) {
250 if (post) {
251 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
252 if (!lnk)
253 return log_oom();
254
255 mkdir_parents_label(lnk, 0755);
256 if (symlink(unit, lnk) < 0) {
257 log_error("Failed to create symlink %s: %m", lnk);
258 return -errno;
259 }
260 }
261 }
262
263 if (automount && !path_equal(where, "/")) {
264 automount_name = unit_name_from_path(where, ".automount");
265 if (!automount_name)
266 return log_oom();
267
268 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
269 if (!automount_unit)
270 return log_oom();
271
272 fclose(f);
273 f = fopen(automount_unit, "wxe");
274 if (!f) {
275 log_error("Failed to create unit file %s: %m", automount_unit);
276 return -errno;
277 }
278
279 fprintf(f,
280 "# Automatically generated by systemd-fstab-generator\n\n"
281 "[Unit]\n"
282 "SourcePath=%s\n",
283 source);
284
285 if (post)
286 fprintf(f,
287 "Before= %s\n",
288 post);
289
290 fprintf(f,
291 "[Automount]\n"
292 "Where=%s\n",
293 where);
294
295 fflush(f);
296 if (ferror(f)) {
297 log_error("Failed to write unit file %s: %m", automount_unit);
298 return -errno;
299 }
300
301 free(lnk);
302 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
303 if (!lnk)
304 return log_oom();
305
306 mkdir_parents_label(lnk, 0755);
307 if (symlink(automount_unit, lnk) < 0) {
308 log_error("Failed to create symlink %s: %m", lnk);
309 return -errno;
310 }
311 }
312
313 return 0;
314 }
315
316 static int parse_fstab(const char *prefix, bool initrd) {
317 char *fstab_path;
318 _cleanup_endmntent_ FILE *f;
319 int r = 0;
320 struct mntent *me;
321
322 fstab_path = strappenda(strempty(prefix), "/etc/fstab");
323 f = setmntent(fstab_path, "r");
324 if (!f) {
325 if (errno == ENOENT)
326 return 0;
327
328 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
329 return -errno;
330 }
331
332 while ((me = getmntent(f))) {
333 _cleanup_free_ char *where = NULL, *what = NULL;
334 int k;
335
336 if (initrd && !mount_in_initrd(me))
337 continue;
338
339 what = fstab_node_to_udev_node(me->mnt_fsname);
340 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
341 if (!what || !where)
342 return log_oom();
343
344 if (is_path(where))
345 path_kill_slashes(where);
346
347 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
348
349 if (streq(me->mnt_type, "swap"))
350 k = add_swap(what, me);
351 else {
352 bool noauto, nofail, automount;
353 const char *post;
354
355 noauto = !!hasmntopt(me, "noauto");
356 nofail = !!hasmntopt(me, "nofail");
357 automount =
358 hasmntopt(me, "comment=systemd.automount") ||
359 hasmntopt(me, "x-systemd.automount");
360
361 if (initrd) {
362 post = SPECIAL_INITRD_FS_TARGET;
363 } else if (mount_in_initrd(me)) {
364 post = SPECIAL_INITRD_ROOT_FS_TARGET;
365 } else if (mount_is_network(me)) {
366 post = SPECIAL_REMOTE_FS_TARGET;
367 } else {
368 post = SPECIAL_LOCAL_FS_TARGET;
369 }
370
371 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
372 me->mnt_passno, noauto, nofail, automount,
373 post, fstab_path);
374 }
375
376 if (k < 0)
377 r = k;
378 }
379
380 return r;
381 }
382
383 static int parse_new_root_from_proc_cmdline(void) {
384 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
385 char *w, *state;
386 int r;
387 size_t l;
388 bool noauto, nofail;
389
390 r = read_one_line_file("/proc/cmdline", &line);
391 if (r < 0) {
392 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
393 return 0;
394 }
395
396 opts = strdup("ro");
397 type = strdup("auto");
398 if (!opts || !type)
399 return log_oom();
400
401 /* root= and roofstype= may occur more than once, the last instance should take precedence.
402 * In the case of multiple rootflags= the arguments should be concatenated */
403 FOREACH_WORD_QUOTED(w, l, line, state) {
404 _cleanup_free_ char *word;
405
406 word = strndup(w, l);
407 if (!word)
408 return log_oom();
409
410 else if (startswith(word, "root=")) {
411 free(what);
412 what = fstab_node_to_udev_node(word+5);
413 if (!what)
414 return log_oom();
415
416 } else if (startswith(word, "rootfstype=")) {
417 free(type);
418 type = strdup(word + 11);
419 if (!type)
420 return log_oom();
421
422 } else if (startswith(word, "rootflags=")) {
423 char *o;
424
425 o = strjoin(opts, ",", word + 10, NULL);
426 if (!o)
427 return log_oom();
428
429 free(opts);
430 opts = o;
431
432 } else if (streq(word, "ro") || streq(word, "rw")) {
433 char *o;
434
435 o = strjoin(opts, ",", word, NULL);
436 if (!o)
437 return log_oom();
438
439 free(opts);
440 opts = o;
441 }
442 }
443
444 noauto = !!strstr(opts, "noauto");
445 nofail = !!strstr(opts, "nofail");
446
447 if (!what) {
448 log_debug("Could not find a root= entry on the kernel commandline.");
449 return 0;
450 }
451
452 if (what[0] != '/') {
453 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
454 return 0;
455 }
456
457 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
458 r = add_mount(what, "/sysroot", type, opts, 1, noauto, nofail, false,
459 SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
460
461 return (r < 0) ? r : 0;
462 }
463
464 static int parse_proc_cmdline(void) {
465 _cleanup_free_ char *line = NULL;
466 char *w, *state;
467 int r;
468 size_t l;
469
470 if (detect_container(NULL) > 0)
471 return 0;
472
473 r = read_one_line_file("/proc/cmdline", &line);
474 if (r < 0) {
475 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
476 return 0;
477 }
478
479 FOREACH_WORD_QUOTED(w, l, line, state) {
480 _cleanup_free_ char *word = NULL;
481
482 word = strndup(w, l);
483 if (!word)
484 return log_oom();
485
486 if (startswith(word, "fstab=")) {
487 r = parse_boolean(word + 6);
488 if (r < 0)
489 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
490 else
491 arg_enabled = r;
492
493 } else if (startswith(word, "rd.fstab=")) {
494
495 if (in_initrd()) {
496 r = parse_boolean(word + 9);
497 if (r < 0)
498 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 9);
499 else
500 arg_enabled = r;
501 }
502
503 } else if (startswith(word, "fstab.") ||
504 (in_initrd() && startswith(word, "rd.fstab."))) {
505
506 log_warning("Unknown kernel switch %s. Ignoring.", word);
507 }
508 }
509
510 return 0;
511 }
512
513 int main(int argc, char *argv[]) {
514 int r = 0, k, l = 0;
515
516 if (argc > 1 && argc != 4) {
517 log_error("This program takes three or no arguments.");
518 return EXIT_FAILURE;
519 }
520
521 if (argc > 1)
522 arg_dest = argv[1];
523
524 log_set_target(LOG_TARGET_SAFE);
525 log_parse_environment();
526 log_open();
527
528 umask(0022);
529
530 if (parse_proc_cmdline() < 0)
531 return EXIT_FAILURE;
532
533 if (in_initrd())
534 r = parse_new_root_from_proc_cmdline();
535
536 if (!arg_enabled)
537 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
538
539 k = parse_fstab(NULL, false);
540
541 if (in_initrd())
542 l = parse_fstab("/sysroot", true);
543
544 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
545 }