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 bool remote_fs_target, bool initrd_fs_target, 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;
210 const char *post, *pre;
218 if (streq(type, "autofs"))
221 if (!is_path(where)) {
222 log_warning("Mount point %s is not a valid path, ignoring.", where);
226 if (mount_point_is_api(where) ||
227 mount_point_ignore(where))
230 if (remote_fs_target) {
231 post = SPECIAL_REMOTE_FS_TARGET;
232 pre = SPECIAL_REMOTE_FS_PRE_TARGET;
233 } else if (initrd_fs_target) {
234 post = SPECIAL_INITRD_FS_TARGET;
235 pre = SPECIAL_INITRD_FS_PRE_TARGET;
237 post = SPECIAL_LOCAL_FS_TARGET;
238 pre = SPECIAL_LOCAL_FS_PRE_TARGET;
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, "/"))
269 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
270 "Before=" SPECIAL_UMOUNT_TARGET "\n",
275 if (!noauto && !nofail && !automount)
292 if (!isempty(opts) &&
293 !streq(opts, "defaults"))
300 log_error("Failed to write unit file %s: %m", unit);
305 lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL);
309 mkdir_parents_label(lnk, 0755);
310 if (symlink(unit, lnk) < 0) {
311 log_error("Failed to create symlink %s: %m", lnk);
316 !path_equal(where, "/")) {
318 r = device_name(what, &device);
324 lnk = strjoin(arg_dest, "/", device, ".wants/", name, NULL);
328 mkdir_parents_label(lnk, 0755);
329 if (symlink(unit, lnk) < 0) {
330 log_error("Failed to create symlink %s: %m", lnk);
337 if (automount && !path_equal(where, "/")) {
338 automount_name = unit_name_from_path(where, ".automount");
342 automount_unit = strjoin(arg_dest, "/", automount_name, NULL);
347 f = fopen(automount_unit, "wxe");
349 log_error("Failed to create unit file %s: %m", automount_unit);
354 "# Automatically generated by systemd-fstab-generator\n\n"
357 "DefaultDependencies=no\n"
358 "Conflicts=" SPECIAL_UMOUNT_TARGET "\n"
359 "Before=" SPECIAL_UMOUNT_TARGET " %s\n"
369 log_error("Failed to write unit file %s: %m", automount_unit);
374 lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL);
378 mkdir_parents_label(lnk, 0755);
379 if (symlink(automount_unit, lnk) < 0) {
380 log_error("Failed to create symlink %s: %m", lnk);
388 static int parse_fstab(const char *prefix, bool initrd) {
390 _cleanup_free_ char *fstab_path = NULL;
395 fstab_path = strjoin(prefix, "/etc/fstab", NULL);
396 f = setmntent(fstab_path, "r");
401 log_error("Failed to open %s/etc/fstab: %m", prefix);
405 while ((me = getmntent(f))) {
406 char _cleanup_free_ *where = NULL, *what = NULL;
409 if (initrd && !mount_in_initrd(me))
412 what = fstab_node_to_udev_node(me->mnt_fsname);
414 where = strjoin(prefix, me->mnt_dir, NULL);
415 if (!what || !where) {
421 path_kill_slashes(where);
424 char _cleanup_free_ *mu = NULL, *name = NULL;
425 /* Skip generation, if unit already exists */
426 name = unit_name_from_path(where, ".mount");
429 mu = strjoin(arg_dest, "/", name, NULL);
433 k = access(mu, R_OK);
438 log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type);
440 if (streq(me->mnt_type, "swap"))
441 k = add_swap(what, me);
443 bool noauto, nofail, automount, isbind, isnetwork;
445 noauto = !!hasmntopt(me, "noauto");
446 nofail = !!hasmntopt(me, "nofail");
448 hasmntopt(me, "comment=systemd.automount") ||
449 hasmntopt(me, "x-systemd.automount");
450 isbind = mount_is_bind(me);
451 isnetwork = mount_is_network(me);
453 k = add_mount(what, where, me->mnt_type, me->mnt_opts,
454 me->mnt_passno, noauto, nofail, automount,
455 isbind, isnetwork, initrd, fstab_path);
467 static int parse_new_root_from_proc_cmdline(void) {
469 _cleanup_free_ char *what = NULL, *type = NULL, *opts = NULL, *line = NULL, *mu = NULL;
473 /* Skip generation, if sysroot.mount already exists */
474 mu = strjoin(arg_dest, "/", "sysroot.mount", NULL);
478 r = access(mu, R_OK);
482 r = read_one_line_file("/proc/cmdline", &line);
484 log_error("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
489 type = strdup("auto");
493 /* root= and roofstype= may occur more than once, the last instance should take precedence.
494 * In the case of multiple rootflags= the arguments should be concatenated */
495 FOREACH_WORD_QUOTED(w, l, line, state) {
496 char *word, *tmp_word;
498 word = strndup(w, l);
502 else if (startswith(word, "root=")) {
504 what = fstab_node_to_udev_node(word+5);
508 } else if (startswith(word, "rootfstype=")) {
510 type = strdup(word + 11);
514 } else if (startswith(word, "rootflags=")) {
516 opts = strjoin(opts, ",", word + 10, NULL);
521 } else if (streq(word, "ro") || streq(word, "rw")) {
523 opts = strjoin(opts, ",", word, NULL);
534 log_error("Could not find a root= entry on the kernel commandline.");
538 if (what[0] != '/') {
539 log_debug("Skipping entry what=%s where=/sysroot type=%s", what, type);
543 log_debug("Found entry what=%s where=/sysroot type=%s", what, type);
544 r = add_mount(what, "/sysroot", type, opts, 0, false, false, false,
545 false, false, true, "/proc/cmdline");
547 return (r < 0) ? r : 0;
550 static int parse_proc_cmdline(void) {
551 char _cleanup_free_ *line = NULL;
556 if (detect_container(NULL) > 0)
559 r = read_one_line_file("/proc/cmdline", &line);
561 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
565 FOREACH_WORD_QUOTED(w, l, line, state) {
566 char _cleanup_free_ *word = NULL;
568 word = strndup(w, l);
572 if (startswith(word, "fstab=")) {
573 r = parse_boolean(word + 6);
575 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
579 } else if (startswith(word, "rd.fstab=")) {
582 r = parse_boolean(word + 6);
584 log_warning("Failed to parse fstab switch %s. Ignoring.", word + 6);
589 } else if (startswith(word, "fstab.") ||
590 (in_initrd() && startswith(word, "rd.fstab."))) {
592 log_warning("Unknown kernel switch %s. Ignoring.", word);
599 int main(int argc, char *argv[]) {
600 int r = 0, k = 0, l = 0;
602 if (argc > 1 && argc != 4) {
603 log_error("This program takes three or no arguments.");
610 log_set_target(LOG_TARGET_SAFE);
611 log_parse_environment();
616 if (parse_proc_cmdline() < 0)
620 r = parse_fstab("", false);
624 k = parse_fstab("/sysroot", true);
625 l = parse_new_root_from_proc_cmdline();
628 return (r < 0) || (k < 0) || (l < 0) ? EXIT_FAILURE : EXIT_SUCCESS;