chiark / gitweb /
unit: use weaker dependencies between mount and device units in --user mode
[elogind.git] / src / core / mount.c
index fc160b6b1db510fa20dbba249933c596fe9bcf39..40037e7866e01d16893e9908c73f2c9252976ffc 100644 (file)
@@ -21,7 +21,6 @@
 
 #include <errno.h>
 #include <stdio.h>
-#include <mntent.h>
 #include <sys/epoll.h>
 #include <signal.h>
 #include <libmount.h>
@@ -30,8 +29,6 @@
 #include "manager.h"
 #include "unit.h"
 #include "mount.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
 #include "log.h"
 #include "sd-messages.h"
 #include "strv.h"
 #include "unit-name.h"
 #include "dbus-mount.h"
 #include "special.h"
-#include "bus-errors.h"
 #include "exit-status.h"
-#include "def.h"
+#include "fstab-util.h"
+
+#define RETRY_UMOUNT_MAX 32
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter);
 
 static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = {
         [MOUNT_DEAD] = UNIT_INACTIVE,
@@ -65,7 +66,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user
 static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
 
 static bool mount_needs_network(const char *options, const char *fstype) {
-        if (mount_test_option(options, "_netdev"))
+        if (fstab_test_option(options, "_netdev\0"))
                 return true;
 
         if (fstype && fstype_is_network(fstype))
@@ -74,37 +75,31 @@ static bool mount_needs_network(const char *options, const char *fstype) {
         return false;
 }
 
-static bool mount_is_network(MountParameters *p) {
+static bool mount_is_network(const MountParameters *p) {
         assert(p);
 
         return mount_needs_network(p->options, p->fstype);
 }
 
-static bool mount_is_bind(MountParameters *p) {
+static bool mount_is_bind(const MountParameters *p) {
         assert(p);
 
-        if (mount_test_option(p->options, "bind"))
-                return true;
-
-        if (p->fstype && streq(p->fstype, "bind"))
+        if (fstab_test_option(p->options, "bind\0" "rbind\0"))
                 return true;
 
-        if (mount_test_option(p->options, "rbind"))
-                return true;
-
-        if (p->fstype && streq(p->fstype, "rbind"))
+        if (p->fstype && STR_IN_SET(p->fstype, "bind", "rbind"))
                 return true;
 
         return false;
 }
 
-static bool mount_is_auto(MountParameters *p) {
+static bool mount_is_auto(const MountParameters *p) {
         assert(p);
 
-        return !mount_test_option(p->options, "noauto");
+        return !fstab_test_option(p->options, "noauto\0");
 }
 
-static bool needs_quota(MountParameters *p) {
+static bool needs_quota(const MountParameters *p) {
         assert(p);
 
         if (mount_is_network(p))
@@ -113,11 +108,8 @@ static bool needs_quota(MountParameters *p) {
         if (mount_is_bind(p))
                 return false;
 
-        return mount_test_option(p->options, "usrquota") ||
-                mount_test_option(p->options, "grpquota") ||
-                mount_test_option(p->options, "quota") ||
-                mount_test_option(p->options, "usrjquota") ||
-                mount_test_option(p->options, "grpjquota");
+        return fstab_test_option(p->options,
+                                 "usrquota\0" "grpquota\0" "quota\0" "usrjquota\0" "grpjquota\0");
 }
 
 static void mount_init(Unit *u) {
@@ -303,7 +295,7 @@ static int mount_add_device_links(Mount *m) {
 
         assert(m);
 
-        p = get_mount_parameters_fragment(m);
+        p = get_mount_parameters(m);
         if (!p)
                 return 0;
 
@@ -364,7 +356,7 @@ static bool should_umount(Mount *m) {
                 return false;
 
         p = get_mount_parameters(m);
-        if (p && mount_test_option(p->options, "x-initrd.mount") &&
+        if (p && fstab_test_option(p->options, "x-initrd.mount\0") &&
             !in_initrd())
                 return false;
 
@@ -381,13 +373,20 @@ static int mount_add_default_dependencies(Mount *m) {
         if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM)
                 return 0;
 
-        p = get_mount_parameters(m);
-
-        if (!p)
+        /* We do not add any default dependencies to / and /usr, since
+         * they are guaranteed to stay mounted the whole time, since
+         * our system is on it. Also, don't bother with anything
+         * mounted below virtual file systems, it's also going to be
+         * virtual, and hence not worth the effort. */
+        if (path_equal(m->where, "/") ||
+            path_equal(m->where, "/usr") ||
+            path_startswith(m->where, "/proc") ||
+            path_startswith(m->where, "/sys") ||
+            path_startswith(m->where, "/dev"))
                 return 0;
 
-        if (path_equal(m->where, "/") ||
-            path_equal(m->where, "/usr"))
+        p = get_mount_parameters(m);
+        if (!p)
                 return 0;
 
         if (mount_is_network(p)) {
@@ -864,20 +863,26 @@ static void mount_enter_unmounting(Mount *m) {
 
         assert(m);
 
+        /* Start counting our attempts */
+        if (!IN_SET(m->state,
+                    MOUNT_UNMOUNTING,
+                    MOUNT_UNMOUNTING_SIGTERM,
+                    MOUNT_UNMOUNTING_SIGKILL))
+                m->n_retry_umount = 0;
+
         m->control_command_id = MOUNT_EXEC_UNMOUNT;
         m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
 
-        if ((r = exec_command_set(
-                             m->control_command,
-                             "/bin/umount",
-                             "-n",
-                             m->where,
-                             NULL)) < 0)
+        r = exec_command_set(m->control_command, "/bin/umount", m->where, NULL);
+        if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+                r = exec_command_append(m->control_command, "-n", NULL);
+        if (r < 0)
                 goto fail;
 
         mount_unwatch_control_pid(m);
 
-        if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
+        r = mount_spawn(m, m->control_command, &m->control_pid);
+        if (r < 0)
                 goto fail;
 
         mount_set_state(m, MOUNT_UNMOUNTING);
@@ -913,17 +918,25 @@ static void mount_enter_mounting(Mount *m) {
         if (r < 0)
                 goto fail;
 
-        if (m->from_fragment)
-                r = exec_command_set(
-                                m->control_command,
-                                "/bin/mount",
-                                m->sloppy_options ? "-ns" : "-n",
-                                m->parameters_fragment.what,
-                                m->where,
-                                "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto",
-                                m->parameters_fragment.options ? "-o" : NULL, m->parameters_fragment.options,
-                                NULL);
-        else
+        if (m->from_fragment) {
+                _cleanup_free_ char *opts = NULL;
+
+                r = fstab_filter_options(m->parameters_fragment.options,
+                                         "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts);
+                if (r < 0)
+                        goto fail;
+
+                r = exec_command_set(m->control_command, "/bin/mount",
+                                     m->parameters_fragment.what, m->where, NULL);
+                if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+                        r = exec_command_append(m->control_command, "-n", NULL);
+                if (r >= 0 && m->sloppy_options)
+                        r = exec_command_append(m->control_command, "-s", NULL);
+                if (r >= 0 && m->parameters_fragment.fstype)
+                        r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
+                if (r >= 0 && !isempty(opts))
+                        r = exec_command_append(m->control_command, "-o", opts, NULL);
+        } else
                 r = -ENOENT;
 
         if (r < 0)
@@ -958,19 +971,19 @@ static void mount_enter_remounting(Mount *m) {
                 const char *o;
 
                 if (m->parameters_fragment.options)
-                        o = strappenda("remount,", m->parameters_fragment.options);
+                        o = strjoina("remount,", m->parameters_fragment.options);
                 else
                         o = "remount";
 
-                r = exec_command_set(
-                                m->control_command,
-                                "/bin/mount",
-                                m->sloppy_options ? "-ns" : "-n",
-                                m->parameters_fragment.what,
-                                m->where,
-                                "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto",
-                                "-o", o,
-                                NULL);
+                r = exec_command_set(m->control_command, "/bin/mount",
+                                     m->parameters_fragment.what, m->where,
+                                     "-o", o, NULL);
+                if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+                        r = exec_command_append(m->control_command, "-n", NULL);
+                if (r >= 0 && m->sloppy_options)
+                        r = exec_command_append(m->control_command, "-s", NULL);
+                if (r >= 0 && m->parameters_fragment.fstype)
+                        r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
         } else
                 r = -ENOENT;
 
@@ -1019,7 +1032,7 @@ static int mount_start(Unit *u) {
         m->reload_result = MOUNT_SUCCESS;
 
         mount_enter_mounting(m);
-        return 0;
+        return 1;
 }
 
 static int mount_stop(Unit *u) {
@@ -1043,7 +1056,7 @@ static int mount_stop(Unit *u) {
                m->state == MOUNT_REMOUNTING_SIGKILL);
 
         mount_enter_unmounting(m);
-        return 0;
+        return 1;
 }
 
 static int mount_reload(Unit *u) {
@@ -1236,9 +1249,31 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         case MOUNT_UNMOUNTING_SIGKILL:
         case MOUNT_UNMOUNTING_SIGTERM:
 
-                if (f == MOUNT_SUCCESS)
-                        mount_enter_dead(m, f);
-                else if (m->from_proc_self_mountinfo)
+                if (f == MOUNT_SUCCESS) {
+
+                        if (m->from_proc_self_mountinfo) {
+
+                                /* Still a mount point? If so, let's
+                                 * try again. Most likely there were
+                                 * multiple mount points stacked on
+                                 * top of each other. Note that due to
+                                 * the io event priority logic we can
+                                 * be sure the new mountinfo is loaded
+                                 * before we process the SIGCHLD for
+                                 * the mount command. */
+
+                                if (m->n_retry_umount < RETRY_UMOUNT_MAX) {
+                                        log_unit_debug(u->id, "%s: mount still present, trying again.", u->id);
+                                        m->n_retry_umount++;
+                                        mount_enter_unmounting(m);
+                                } else {
+                                        log_unit_debug(u->id, "%s: mount still present after %u attempts to unmount, giving up.", u->id, m->n_retry_umount);
+                                        mount_enter_mounted(m, f);
+                                }
+                        } else
+                                mount_enter_dead(m, f);
+
+                } else if (m->from_proc_self_mountinfo)
                         mount_enter_mounted(m, f);
                 else
                         mount_enter_dead(m, f);
@@ -1412,7 +1447,6 @@ static int mount_add_one(
                         goto fail;
                 }
 
-
                 if (m->running_as == SYSTEMD_SYSTEM) {
                         const char* target;
 
@@ -1514,17 +1548,9 @@ fail:
         return r;
 }
 
-static inline void mnt_free_table_p(struct libmnt_table **tb) {
-        mnt_free_table(*tb);
-}
-
-static inline void mnt_free_iter_p(struct libmnt_iter **itr) {
-        mnt_free_iter(*itr);
-}
-
 static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
-        _cleanup_(mnt_free_table_p) struct libmnt_table *tb = NULL;
-        _cleanup_(mnt_free_iter_p) struct libmnt_iter *itr = NULL;
+        _cleanup_(mnt_free_tablep) struct libmnt_table *tb = NULL;
+        _cleanup_(mnt_free_iterp) struct libmnt_iter *itr = NULL;
         struct libmnt_fs *fs;
         int r = 0;
 
@@ -1621,14 +1647,18 @@ static int mount_enumerate(Manager *m) {
 
         if (m->utab_inotify_fd < 0) {
                 m->utab_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
-                if (m->utab_inotify_fd < 0)
-                        goto fail_with_errno;
+                if (m->utab_inotify_fd < 0) {
+                        r = -errno;
+                        goto fail;
+                }
 
                 (void) mkdir_p_label("/run/mount", 0755);
 
                 r = inotify_add_watch(m->utab_inotify_fd, "/run/mount", IN_MOVED_TO);
-                if (r < 0)
-                        goto fail_with_errno;
+                if (r < 0) {
+                        r = -errno;
+                        goto fail;
+                }
 
                 r = sd_event_add_io(m->event, &m->mount_utab_event_source, m->utab_inotify_fd, EPOLLIN, mount_dispatch_io, m);
                 if (r < 0)
@@ -1645,8 +1675,6 @@ static int mount_enumerate(Manager *m) {
 
         return 0;
 
-fail_with_errno:
-        r = -errno;
 fail:
         mount_shutdown(m);
         return r;
@@ -1666,23 +1694,37 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
          * for mount options. */
 
         if (fd == m->utab_inotify_fd) {
-                char inotify_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
-                struct inotify_event *event;
-                char *p;
-                int rescan = 0;
-
-                while ((r = read(fd, inotify_buffer, sizeof(inotify_buffer))) > 0)
-                        for (p = inotify_buffer; p < inotify_buffer + r; ) {
-                                event = (struct inotify_event *) p;
-                                /* only care about changes to utab, but we have
-                                 * to monitor the directory to reliably get
-                                 * notifications about when utab is replaced
-                                 * using rename(2) */
-                                if ((event->mask & IN_Q_OVERFLOW) || streq(event->name, "utab"))
-                                        rescan = 1;
-                                p += sizeof(struct inotify_event) + event->len;
+                bool rescan = false;
+
+                /* FIXME: We *really* need to replace this with
+                 * libmount's own API for this, we should not hardcode
+                 * internal behaviour of libmount here. */
+
+                for (;;) {
+                        union inotify_event_buffer buffer;
+                        struct inotify_event *e;
+                        ssize_t l;
+
+                        l = read(fd, &buffer, sizeof(buffer));
+                        if (l < 0) {
+                                if (errno == EAGAIN || errno == EINTR)
+                                        break;
+
+                                log_error_errno(errno, "Failed to read utab inotify: %m");
+                                break;
                         }
 
+                        FOREACH_INOTIFY_EVENT(e, buffer, l) {
+                                /* Only care about changes to utab,
+                                 * but we have to monitor the
+                                 * directory to reliably get
+                                 * notifications about when utab is
+                                 * replaced using rename(2) */
+                                if ((e->mask & IN_Q_OVERFLOW) || streq(e->name, "utab"))
+                                        rescan = true;
+                        }
+                }
+
                 if (!rescan)
                         return 0;
         }