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 int add_mount(const char *what, const char *where, const char *type, const char *opts,
203 int passno, bool noauto, bool nofail, bool automount, bool isbind,
204 const char *pre, const char *post, const char *source) {
206 *name = NULL, *unit = NULL, *lnk = NULL, *device = NULL,
207 *automount_name = NULL, *automount_unit = NULL;
208 FILE _cleanup_fclose_ *f = NULL;
217 if (streq(type, "autofs"))
220 if (!is_path(where)) {
221 log_warning("Mount point %s is not a valid path, ignoring.", where);
225 if (mount_point_is_api(where) ||
226 mount_point_ignore(where))
229 name = unit_name_from_path(where, ".mount");
233 unit = strjoin(arg_dest, "/", name, NULL);
237 f = fopen(unit, "wxe");
240 log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit);
242 log_error("Failed to create unit file %s: %m", unit);
247 "# Automatically generated by systemd-fstab-generator\n\n"
250 "DefaultDependencies=no\n",
253 if (!path_equal(where, "/")) {
261 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
262 "Before=" SPECIAL_UMOUNT_TARGET "\n");
265 if (post && !noauto && !nofail && !automount)
282 if (!isempty(opts) &&
283 !streq(opts, "defaults"))
290 log_error("Failed to write unit file %s: %m", unit);
296 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
300 mkdir_parents_label(lnk, 0755);
301 if (symlink(unit, lnk) < 0) {
302 log_error("Failed to create symlink %s: %m", lnk);
308 !path_equal(where, "/")) {
310 r = device_name(what, &device);
316 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
320 mkdir_parents_label(lnk, 0755);
321 if (symlink(unit, lnk) < 0) {
322 log_error("Failed to create symlink %s: %m", lnk);
329 if (automount && !path_equal(where, "/")) {
330 automount_name = unit_name_from_path(where, ".automount");
334 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
339 f = fopen(automount_unit, "wxe");
341 log_error("Failed to create unit file %s: %m", automount_unit);
346 "# Automatically generated by systemd-fstab-generator\n\n"
349 "DefaultDependencies=no\n"
350 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
351 "Before=" SPECIAL_UMOUNT_TARGET "\n",
366 log_error("Failed to write unit file %s: %m", automount_unit);
371 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
375 mkdir_parents_label(lnk, 0755);
376 if (symlink(automount_unit, lnk) < 0) {
377 log_error("Failed to create symlink %s: %m", lnk);
385 static int parse_fstab(const char *prefix, bool initrd) {
387 _cleanup_free_ char *fstab_path = NULL;
392 fstab_path = strjoin(prefix, "/etc/fstab", NULL);
393 f = setmntent(fstab_path, "r");
398 log_error("Failed to open %s/etc/fstab: %m", prefix);
402 while ((me = getmntent(f))) {
403 char _cleanup_free_ *where = NULL, *what = NULL;
406 if (initrd && !mount_in_initrd(me))
409 what = fstab_node_to_udev_node(me->mnt_fsname);
410 where = strjoin(prefix, me->mnt_dir, NULL);
411 if (!what || !where) {
417 path_kill_slashes(where);
419 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
421 if (streq(me->mnt_type, "swap"))
422 k = add_swap(what, me);
424 bool noauto, nofail, automount, isbind;
425 const char *pre, *post;
427 noauto = !!hasmntopt(me, "noauto");
428 nofail = !!hasmntopt(me, "nofail");
430 hasmntopt(me, "comment=systemd.automount") ||
431 hasmntopt(me, "x-systemd.automount");
432 isbind = mount_is_bind(me);
435 post = SPECIAL_INITRD_FS_TARGET;
437 } else if (mount_is_network(me)) {
438 post = SPECIAL_REMOTE_FS_TARGET;
439 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
441 post = SPECIAL_LOCAL_FS_TARGET;
442 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
445 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
446 me->mnt_passno, noauto, nofail, automount,
447 isbind, pre, post, fstab_path);
459 static int parse_new_root_from_proc_cmdline(void) {
461 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL;
465 r = read_one_line_file("/proc/cmdline", &line);
467 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
472 type = strdup("auto");
476 /* root= and roofstype= may occur more than once, the last instance should take precedence.
477 * In the case of multiple rootflags= the arguments should be concatenated */
478 FOREACH_WORD_QUOTED(w, l, line, state) {
479 char *word, *tmp_word;
481 word = strndup(w, l);
485 else if (startswith(word, "root=")) {
487 what = fstab_node_to_udev_node(word+5);
491 } else if (startswith(word, "rootfstype=")) {
493 type = strdup(word + 11);
497 } else if (startswith(word, "rootflags=")) {
499 opts = strjoin(opts, ",", word + 10, NULL);
504 } else if (streq(word, "ro") || streq(word, "rw")) {
506 opts = strjoin(opts, ",", word, NULL);
517 log_error("Could not find a root= entry on the kernel commandline.");
521 if (what[0] != '/') {
522 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
526 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
527 r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
528 false, NULL, SPECIAL_ROOT_FS_TARGET, "/proc/cmdline");
530 return (r < 0) ? r : 0;
533 static int parse_proc_cmdline(void) {
534 char _cleanup_free_ *line = NULL;
539 if (detect_container(NULL) > 0)
542 r = read_one_line_file("/proc/cmdline", &line);
544 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
548 FOREACH_WORD_QUOTED(w, l, line, state) {
549 char _cleanup_free_ *word = NULL;
551 word = strndup(w, l);
555 if (startswith(word, "fstab=")) {
556 r = parse_boolean(word + 6);
558 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
562 } else if (startswith(word, "rd.fstab=")) {
565 r = parse_boolean(word + 6);
567 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
572 } else if (startswith(word, "fstab.") ||
573 (in_initrd() && startswith(word, "rd.fstab."))) {
575 log_warning("Unknown kernel switch %s. Ignoring.", word);
582 int main(int argc, char *argv[]) {
585 if (argc > 1 && argc != 4) {
586 log_error("This program takes three or no arguments.");
593 log_set_target(LOG_TARGET_SAFE);
594 log_parse_environment();
599 if (parse_proc_cmdline() < 0)
603 r = parse_new_root_from_proc_cmdline();
606 return (r < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
608 k = parse_fstab("", false);
611 l = parse_fstab("/sysroot", true);
613 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;