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