1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
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.
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.
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/>.
30 #include "unit-name.h"
31 #include "path-util.h"
32 #include "mount-setup.h"
38 static const char *arg_dest = "/tmp";
39 static bool arg_enabled = true;
41 static int device_name(const char *path, char **unit) {
46 if (!is_device_path(path))
49 p = unit_name_from_path(path, ".device");
57 static int mount_find_pri(struct mntent *me, int *ret) {
64 pri = hasmntopt(me, "pri");
71 r = strtoul(pri, &end, 10);
75 if (end == pri || (*end != ',' && *end != 0))
82 static int add_swap(const char *what, struct mntent *me) {
83 char _cleanup_free_ *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
84 FILE _cleanup_fclose_ *f = NULL;
91 r = mount_find_pri(me, &pri);
93 log_error("Failed to parse priority");
97 noauto = !!hasmntopt(me, "noauto");
98 nofail = !!hasmntopt(me, "nofail");
100 name = unit_name_from_path(what, ".swap");
104 unit = strjoin(arg_dest, "/", name, NULL);
108 f = fopen(unit, "wxe");
111 log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
113 log_error("Failed to create unit file %s: %m", unit);
117 fputs("# Automatically generated by systemd-fstab-generator\n\n"
119 "SourcePath=/etc/fstab\n"
120 "DefaultDependencies=no\n"
121 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
122 "Before=" SPECIAL_UMOUNT_TARGET "\n", f);
124 if (!noauto && !nofail)
125 fputs("Before=" SPECIAL_SWAP_TARGET "\n", f);
140 log_error("Failed to write unit file %s: %m", unit);
145 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
149 mkdir_parents_label(lnk, 0755);
150 if (symlink(unit, lnk) < 0) {
151 log_error("Failed to create symlink %s: %m", lnk);
155 r = device_name(what, &device);
161 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
165 mkdir_parents_label(lnk, 0755);
166 if (symlink(unit, lnk) < 0) {
167 log_error("Failed to create symlink %s: %m", lnk);
176 static bool mount_is_bind(struct mntent *me) {
180 hasmntopt(me, "bind") ||
181 streq(me->mnt_type, "bind") ||
182 hasmntopt(me, "rbind") ||
183 streq(me->mnt_type, "rbind");
186 static bool mount_is_network(struct mntent *me) {
190 hasmntopt(me, "_netdev") ||
191 fstype_is_network(me->mnt_type);
194 static bool mount_in_initrd(struct mntent *me) {
198 hasmntopt(me, "x-initrd.mount") ||
199 streq(me->mnt_dir, "/usr");
202 static bool mount_is_rootfs(struct mntent *me) {
205 return hasmntopt(me, "x-initrd.rootfs");
208 static int add_mount(
221 const char *source) {
223 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
224 *automount_name = NULL, *automount_unit = NULL;
225 FILE _cleanup_fclose_ *f = NULL;
234 if (streq(type, "autofs"))
237 if (!is_path(where)) {
238 log_warning("Mount point %s is not a valid path, ignoring.", where);
242 if (mount_point_is_api(where) ||
243 mount_point_ignore(where))
246 name = unit_name_from_path(where, ".mount");
250 unit = strjoin(arg_dest, "/", name, NULL);
254 f = fopen(unit, "wxe");
257 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
259 log_error("Failed to create unit file %s: %m", unit);
264 "# Automatically generated by systemd-fstab-generator\n\n"
267 "DefaultDependencies=no\n",
270 if (!path_equal(where, "/")) {
282 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
283 "Before=" SPECIAL_UMOUNT_TARGET "\n");
286 if (post && !noauto && !nofail && !automount)
303 if (!isempty(opts) &&
304 !streq(opts, "defaults"))
311 log_error("Failed to write unit file %s: %m", unit);
317 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
321 mkdir_parents_label(lnk, 0755);
322 if (symlink(unit, lnk) < 0) {
323 log_error("Failed to create symlink %s: %m", lnk);
329 !path_equal(where, "/")) {
331 r = device_name(what, &device);
337 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
341 mkdir_parents_label(lnk, 0755);
342 if (symlink(unit, lnk) < 0) {
343 log_error("Failed to create symlink %s: %m", lnk);
350 if (automount && !path_equal(where, "/")) {
351 automount_name = unit_name_from_path(where, ".automount");
355 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
360 f = fopen(automount_unit, "wxe");
362 log_error("Failed to create unit file %s: %m", automount_unit);
367 "# Automatically generated by systemd-fstab-generator\n\n"
370 "DefaultDependencies=no\n"
371 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
372 "Before=" SPECIAL_UMOUNT_TARGET "\n",
387 log_error("Failed to write unit file %s: %m", automount_unit);
392 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
396 mkdir_parents_label(lnk, 0755);
397 if (symlink(automount_unit, lnk) < 0) {
398 log_error("Failed to create symlink %s: %m", lnk);
406 static int parse_fstab(const char *prefix, bool initrd) {
407 _cleanup_free_ char *fstab_path = NULL;
412 fstab_path = strjoin(strempty(prefix), "/etc/fstab", NULL);
416 f = setmntent(fstab_path, "r");
421 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
425 while ((me = getmntent(f))) {
426 char _cleanup_free_ *where = NULL, *what = NULL;
429 if (initrd && !mount_in_initrd(me))
432 what = fstab_node_to_udev_node(me->mnt_fsname);
433 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
434 if (!what || !where) {
440 path_kill_slashes(where);
442 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
444 if (streq(me->mnt_type, "swap"))
445 k = add_swap(what, me);
447 bool noauto, nofail, automount, isbind;
448 const char *pre, *post, *setup;
450 noauto = !!hasmntopt(me, "noauto");
451 nofail = !!hasmntopt(me, "nofail");
453 hasmntopt(me, "comment=systemd.automount") ||
454 hasmntopt(me, "x-systemd.automount");
455 isbind = mount_is_bind(me);
458 post = SPECIAL_INITRD_FS_TARGET;
461 } else if (mount_is_rootfs(me)) {
462 post = SPECIAL_INITRD_ROOT_FS_TARGET;
465 } else if (mount_is_network(me)) {
466 post = SPECIAL_REMOTE_FS_TARGET;
467 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
468 setup = SPECIAL_REMOTE_FS_SETUP_TARGET;
470 post = SPECIAL_LOCAL_FS_TARGET;
471 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
475 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
476 me->mnt_passno, noauto, nofail, automount,
477 isbind, pre, post, setup, fstab_path);
489 static int parse_new_root_from_proc_cmdline(void) {
490 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
495 r = read_one_line_file("/proc/cmdline", &line);
497 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
502 type = strdup("auto");
506 /* root= and roofstype= may occur more than once, the last instance should take precedence.
507 * In the case of multiple rootflags= the arguments should be concatenated */
508 FOREACH_WORD_QUOTED(w, l, line, state) {
509 _cleanup_free_ char *word;
511 word = strndup(w, l);
515 else if (startswith(word, "root=")) {
517 what = fstab_node_to_udev_node(word+5);
521 } else if (startswith(word, "rootfstype=")) {
523 type = strdup(word + 11);
527 } else if (startswith(word, "rootflags=")) {
530 o = strjoin(opts, ",", word + 10, NULL);
537 } else if (streq(word, "ro") || streq(word, "rw")) {
540 o = strjoin(opts, ",", word, NULL);
550 log_debug("Could not find a root= entry on the kernel commandline.");
554 if (what[0] != '/') {
555 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
559 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
560 r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
561 false, NULL, SPECIAL_INITRD_ROOT_FS_TARGET, NULL, "/proc/cmdline");
563 return (r < 0) ? r : 0;
566 static int parse_proc_cmdline(void) {
567 char _cleanup_free_ *line = NULL;
572 if (detect_container(NULL) > 0)
575 r = read_one_line_file("/proc/cmdline", &line);
577 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
581 FOREACH_WORD_QUOTED(w, l, line, state) {
582 char _cleanup_free_ *word = NULL;
584 word = strndup(w, l);
588 if (startswith(word, "fstab=")) {
589 r = parse_boolean(word + 6);
591 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
595 } else if (startswith(word, "rd.fstab=")) {
598 r = parse_boolean(word + 6);
600 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
605 } else if (startswith(word, "fstab.") ||
606 (in_initrd() && startswith(word, "rd.fstab."))) {
608 log_warning("Unknown kernel switch %s. Ignoring.", word);
615 int main(int argc, char *argv[]) {
618 if (argc > 1 && argc != 4) {
619 log_error("This program takes three or no arguments.");
626 log_set_target(LOG_TARGET_SAFE);
627 log_parse_environment();
632 if (parse_proc_cmdline() < 0)
636 r = parse_new_root_from_proc_cmdline();
639 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
641 k = parse_fstab(NULL, false);
644 l = parse_fstab("/sysroot", true);
646 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;