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 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL;
84 _cleanup_fclose_ FILE *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 int add_mount(
216 const char *source) {
218 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
219 *automount_name = NULL, *automount_unit = NULL;
220 _cleanup_fclose_ FILE *f = NULL;
229 if (streq(type, "autofs"))
232 if (!is_path(where)) {
233 log_warning("Mount point %s is not a valid path, ignoring.", where);
237 if (mount_point_is_api(where) ||
238 mount_point_ignore(where))
241 name = unit_name_from_path(where, ".mount");
245 unit = strjoin(arg_dest, "/", name, NULL);
249 f = fopen(unit, "wxe");
252 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
254 log_error("Failed to create unit file %s: %m", unit);
259 "# Automatically generated by systemd-fstab-generator\n\n"
262 "DefaultDependencies=no\n",
265 if (!path_equal(where, "/")) {
284 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
285 "Before=" SPECIAL_UMOUNT_TARGET "\n");
288 if (post && !noauto && !nofail && !automount)
305 if (!isempty(opts) &&
306 !streq(opts, "defaults"))
313 log_error("Failed to write unit file %s: %m", unit);
319 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
323 mkdir_parents_label(lnk, 0755);
324 if (symlink(unit, lnk) < 0) {
325 log_error("Failed to create symlink %s: %m", lnk);
331 !path_equal(where, "/")) {
333 r = device_name(what, &device);
339 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
343 mkdir_parents_label(lnk, 0755);
344 if (symlink(unit, lnk) < 0) {
345 log_error("Failed to create symlink %s: %m", lnk);
352 if (automount && !path_equal(where, "/")) {
353 automount_name = unit_name_from_path(where, ".automount");
357 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
362 f = fopen(automount_unit, "wxe");
364 log_error("Failed to create unit file %s: %m", automount_unit);
369 "# Automatically generated by systemd-fstab-generator\n\n"
372 "DefaultDependencies=no\n"
373 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
374 "Before=" SPECIAL_UMOUNT_TARGET "\n",
389 log_error("Failed to write unit file %s: %m", automount_unit);
394 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
398 mkdir_parents_label(lnk, 0755);
399 if (symlink(automount_unit, lnk) < 0) {
400 log_error("Failed to create symlink %s: %m", lnk);
408 static int parse_fstab(const char *prefix, bool initrd) {
409 _cleanup_free_ char *fstab_path = NULL;
414 fstab_path = strjoin(strempty(prefix), "/etc/fstab", NULL);
418 f = setmntent(fstab_path, "r");
423 log_error("Failed to open %s/etc/fstab: %m", strempty(prefix));
427 while ((me = getmntent(f))) {
428 _cleanup_free_ char *where = NULL, *what = NULL;
431 if (initrd && !mount_in_initrd(me))
434 what = fstab_node_to_udev_node(me->mnt_fsname);
435 where = strjoin(strempty(prefix), me->mnt_dir, NULL);
436 if (!what || !where) {
442 path_kill_slashes(where);
444 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
446 if (streq(me->mnt_type, "swap"))
447 k = add_swap(what, me);
449 bool noauto, nofail, automount, isbind;
450 const char *pre, *pre2, *post, *online;
452 noauto = !!hasmntopt(me, "noauto");
453 nofail = !!hasmntopt(me, "nofail");
455 hasmntopt(me, "comment=systemd.automount") ||
456 hasmntopt(me, "x-systemd.automount");
457 isbind = mount_is_bind(me);
460 pre = pre2 = online = NULL;
461 post = SPECIAL_INITRD_FS_TARGET;
462 } else if (mount_in_initrd(me)) {
463 pre = pre2 = online = NULL;
464 post = SPECIAL_INITRD_ROOT_FS_TARGET;
465 } else if (mount_is_network(me)) {
466 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
467 pre2 = SPECIAL_NETWORK_TARGET;
468 online = SPECIAL_NETWORK_ONLINE_TARGET;
469 post = SPECIAL_REMOTE_FS_TARGET;
471 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
472 pre2 = online = NULL;
473 post = SPECIAL_LOCAL_FS_TARGET;
476 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
477 me->mnt_passno, noauto, nofail, automount,
478 isbind, pre, pre2, online, post, fstab_path);
490 static int parse_new_root_from_proc_cmdline(void) {
491 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
496 r = read_one_line_file("/proc/cmdline", &line);
498 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
503 type = strdup("auto");
507 /* root= and roofstype= may occur more than once, the last instance should take precedence.
508 * In the case of multiple rootflags= the arguments should be concatenated */
509 FOREACH_WORD_QUOTED(w, l, line, state) {
510 _cleanup_free_ char *word;
512 word = strndup(w, l);
516 else if (startswith(word, "root=")) {
518 what = fstab_node_to_udev_node(word+5);
522 } else if (startswith(word, "rootfstype=")) {
524 type = strdup(word + 11);
528 } else if (startswith(word, "rootflags=")) {
531 o = strjoin(opts, ",", word + 10, NULL);
538 } else if (streq(word, "ro") || streq(word, "rw")) {
541 o = strjoin(opts, ",", word, NULL);
551 log_debug("Could not find a root= entry on the kernel commandline.");
555 if (what[0] != '/') {
556 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
560 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
561 r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
562 false, NULL, NULL, NULL, SPECIAL_INITRD_ROOT_FS_TARGET, "/proc/cmdline");
564 return (r < 0) ? r : 0;
567 static int parse_proc_cmdline(void) {
568 _cleanup_free_ char *line = NULL;
573 if (detect_container(NULL) > 0)
576 r = read_one_line_file("/proc/cmdline", &line);
578 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
582 FOREACH_WORD_QUOTED(w, l, line, state) {
583 _cleanup_free_ char *word = NULL;
585 word = strndup(w, l);
589 if (startswith(word, "fstab=")) {
590 r = parse_boolean(word + 6);
592 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
596 } else if (startswith(word, "rd.fstab=")) {
599 r = parse_boolean(word + 6);
601 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
606 } else if (startswith(word, "fstab.") ||
607 (in_initrd() && startswith(word, "rd.fstab."))) {
609 log_warning("Unknown kernel switch %s. Ignoring.", word);
616 int main(int argc, char *argv[]) {
619 if (argc > 1 && argc != 4) {
620 log_error("This program takes three or no arguments.");
627 log_set_target(LOG_TARGET_SAFE);
628 log_parse_environment();
633 if (parse_proc_cmdline() < 0)
637 r = parse_new_root_from_proc_cmdline();
640 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
642 k = parse_fstab(NULL, false);
645 l = parse_fstab("/sysroot", true);
647 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;