]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/automount.c
Merge pull request #3170 from poettering/v230-preparation-fixes
[thirdparty/systemd.git] / src / core / automount.c
index 5bb61b95d3132fb809eac4325a754b976b1748da..f06d837e300c48262d9df7af6d21a922f77195df 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /***
   This file is part of systemd.
 
 ***/
 
 #include <errno.h>
-#include <limits.h>
-#include <sys/mount.h>
-#include <unistd.h>
 #include <fcntl.h>
+#include <limits.h>
+#include <linux/auto_dev-ioctl.h>
+#include <linux/auto_fs4.h>
 #include <sys/epoll.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
-#include <linux/auto_fs4.h>
-#include <linux/auto_dev-ioctl.h>
+#include <unistd.h>
 
-#include "unit.h"
+#include "alloc-util.h"
+#include "async.h"
 #include "automount.h"
-#include "mount.h"
-#include "unit-name.h"
-#include "special.h"
+#include "bus-error.h"
+#include "bus-util.h"
+#include "dbus-automount.h"
+#include "fd-util.h"
+#include "formats-util.h"
+#include "io-util.h"
 #include "label.h"
 #include "mkdir.h"
+#include "mount-util.h"
+#include "mount.h"
+#include "parse-util.h"
 #include "path-util.h"
-#include "dbus-automount.h"
-#include "bus-util.h"
-#include "bus-error.h"
-#include "formats-util.h"
 #include "process-util.h"
-#include "async.h"
+#include "special.h"
+#include "stdio-util.h"
+#include "string-table.h"
+#include "string-util.h"
+#include "unit-name.h"
+#include "unit.h"
 
 static const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
         [AUTOMOUNT_DEAD] = UNIT_INACTIVE,
@@ -69,6 +75,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free);
 
 static int open_dev_autofs(Manager *m);
 static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata);
+static int automount_start_expire(Automount *a);
+static void automount_stop_expire(Automount *a);
+static int automount_send_ready(Automount *a, Set *tokens, int status);
 
 static void automount_init(Unit *u) {
         Automount *a = AUTOMOUNT(u);
@@ -81,26 +90,9 @@ static void automount_init(Unit *u) {
         UNIT(a)->ignore_on_isolate = true;
 }
 
-static void repeat_unmount(const char *path) {
-        assert(path);
-
-        for (;;) {
-                /* If there are multiple mounts on a mount point, this
-                 * removes them all */
-
-                if (umount2(path, MNT_DETACH) >= 0)
-                        continue;
-
-                if (errno != EINVAL)
-                        log_error_errno(errno, "Failed to unmount: %m");
-
-                break;
-        }
-}
-
-static int automount_send_ready(Automount *a, Set *tokens, int status);
-
 static void unmount_autofs(Automount *a) {
+        int r;
+
         assert(a);
 
         if (a->pipe_fd < 0)
@@ -116,8 +108,11 @@ static void unmount_autofs(Automount *a) {
          * around */
         if (a->where &&
             (UNIT(a)->manager->exit_code != MANAGER_RELOAD &&
-             UNIT(a)->manager->exit_code != MANAGER_REEXECUTE))
-                repeat_unmount(a->where);
+             UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) {
+                r = repeat_unmount(a->where, MNT_DETACH);
+                if (r < 0)
+                        log_error_errno(r, "Failed to unmount: %m");
+        }
 }
 
 static void automount_done(Unit *u) {
@@ -129,23 +124,20 @@ static void automount_done(Unit *u) {
 
         a->where = mfree(a->where);
 
-        set_free(a->tokens);
-        a->tokens = NULL;
-        set_free(a->expire_tokens);
-        a->expire_tokens = NULL;
+        a->tokens = set_free(a->tokens);
+        a->expire_tokens = set_free(a->expire_tokens);
 
         a->expire_event_source = sd_event_source_unref(a->expire_event_source);
 }
 
 static int automount_add_mount_links(Automount *a) {
         _cleanup_free_ char *parent = NULL;
-        int r;
 
         assert(a);
 
-        r = path_get_parent(a->where, &parent);
-        if (r < 0)
-                return r;
+        parent = dirname_malloc(a->where);
+        if (!parent)
+                return -ENOMEM;
 
         return unit_require_mounts_for(UNIT(a), parent);
 }
@@ -155,7 +147,10 @@ static int automount_add_default_dependencies(Automount *a) {
 
         assert(a);
 
-        if (UNIT(a)->manager->running_as != MANAGER_SYSTEM)
+        if (!UNIT(a)->default_dependencies)
+                return 0;
+
+        if (!MANAGER_IS_SYSTEM(UNIT(a)->manager))
                 return 0;
 
         r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
@@ -226,11 +221,9 @@ static int automount_load(Unit *u) {
                 if (r < 0)
                         return r;
 
-                if (UNIT(a)->default_dependencies) {
-                        r = automount_add_default_dependencies(a);
-                        if (r < 0)
-                                return r;
-                }
+                r = automount_add_default_dependencies(a);
+                if (r < 0)
+                        return r;
         }
 
         return automount_verify(a);
@@ -243,6 +236,9 @@ static void automount_set_state(Automount *a, AutomountState state) {
         old_state = a->state;
         a->state = state;
 
+        if (state != AUTOMOUNT_RUNNING)
+                automount_stop_expire(a);
+
         if (state != AUTOMOUNT_WAITING &&
             state != AUTOMOUNT_RUNNING)
                 unmount_autofs(a);
@@ -416,7 +412,7 @@ static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, in
         init_autofs_dev_ioctl(&param);
         param.ioctlfd = ioctl_fd;
 
-        if (status) {
+        if (status != 0) {
                 param.fail.token = token;
                 param.fail.status = status;
         } else
@@ -443,7 +439,7 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) {
         if (ioctl_fd < 0)
                 return ioctl_fd;
 
-        if (status)
+        if (status != 0)
                 log_unit_debug_errno(UNIT(a), status, "Sending failure: %m");
         else
                 log_unit_debug(UNIT(a), "Sending success.");
@@ -470,59 +466,54 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) {
         return r;
 }
 
-static int automount_start_expire(Automount *a);
-
-int automount_update_mount(Automount *a, MountState old_state, MountState state) {
+static void automount_trigger_notify(Unit *u, Unit *other) {
+        Automount *a = AUTOMOUNT(u);
         int r;
 
         assert(a);
+        assert(other);
+
+        /* Filter out invocations with bogus state */
+        if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT)
+                return;
+
+        /* Don't propagate state changes from the mount if we are already down */
+        if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING))
+                return;
+
+        /* Propagate start limit hit state */
+        if (other->start_limit_hit) {
+                automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT);
+                return;
+        }
+
+        /* Don't propagate anything if there's still a job queued */
+        if (other->job)
+                return;
+
+        /* The mount is successfully established */
+        if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTED, MOUNT_REMOUNTING)) {
+                (void) automount_send_ready(a, a->tokens, 0);
 
-        switch (state) {
-        case MOUNT_MOUNTED:
-        case MOUNT_REMOUNTING:
-                automount_send_ready(a, a->tokens, 0);
                 r = automount_start_expire(a);
                 if (r < 0)
                         log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
-                break;
-         case MOUNT_DEAD:
-         case MOUNT_UNMOUNTING:
-         case MOUNT_MOUNTING_SIGTERM:
-         case MOUNT_MOUNTING_SIGKILL:
-         case MOUNT_REMOUNTING_SIGTERM:
-         case MOUNT_REMOUNTING_SIGKILL:
-         case MOUNT_UNMOUNTING_SIGTERM:
-         case MOUNT_UNMOUNTING_SIGKILL:
-         case MOUNT_FAILED:
-                if (old_state != state)
-                        automount_send_ready(a, a->tokens, -ENODEV);
-                (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
-                break;
-        default:
-                break;
-        }
 
-        switch (state) {
-        case MOUNT_DEAD:
-                automount_send_ready(a, a->expire_tokens, 0);
-                break;
-         case MOUNT_MOUNTING:
-         case MOUNT_MOUNTING_DONE:
-         case MOUNT_MOUNTING_SIGTERM:
-         case MOUNT_MOUNTING_SIGKILL:
-         case MOUNT_REMOUNTING_SIGTERM:
-         case MOUNT_REMOUNTING_SIGKILL:
-         case MOUNT_UNMOUNTING_SIGTERM:
-         case MOUNT_UNMOUNTING_SIGKILL:
-         case MOUNT_FAILED:
-                if (old_state != state)
-                        automount_send_ready(a, a->expire_tokens, -ENODEV);
-                break;
-        default:
-                break;
+                automount_set_state(a, AUTOMOUNT_RUNNING);
         }
 
-        return 0;
+        /* The mount is in some unhappy state now, let's unfreeze any waiting clients */
+        if (IN_SET(MOUNT(other)->state,
+                   MOUNT_DEAD, MOUNT_UNMOUNTING,
+                   MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL,
+                   MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL,
+                   MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL,
+                   MOUNT_FAILED)) {
+
+                (void) automount_send_ready(a, a->tokens, -ENODEV);
+
+                automount_set_state(a, AUTOMOUNT_WAITING);
+        }
 }
 
 static void automount_enter_waiting(Automount *a) {
@@ -610,12 +601,16 @@ static void automount_enter_waiting(Automount *a) {
         return;
 
 fail:
+        log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m");
+
         safe_close_pair(p);
 
-        if (mounted)
-                repeat_unmount(a->where);
+        if (mounted) {
+                r = repeat_unmount(a->where, MNT_DETACH);
+                if (r < 0)
+                        log_error_errno(r, "Failed to unmount, ignoring: %m");
+        }
 
-        log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m");
         automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES);
 }
 
@@ -703,8 +698,17 @@ static int automount_start_expire(Automount *a) {
         return 0;
 }
 
+static void automount_stop_expire(Automount *a) {
+        assert(a);
+
+        if (!a->expire_event_source)
+                return;
+
+        (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
+}
+
 static void automount_enter_runnning(Automount *a) {
-        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         struct stat st;
         int r;
 
@@ -730,8 +734,15 @@ static void automount_enter_runnning(Automount *a) {
         if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id)
                 log_unit_info(UNIT(a), "Automount point already active?");
         else {
-                r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)),
-                                    JOB_REPLACE, true, &error, NULL);
+                Unit *trigger;
+
+                trigger = UNIT_TRIGGER(UNIT(a));
+                if (!trigger) {
+                        log_unit_error(UNIT(a), "Unit to trigger vanished.");
+                        goto fail;
+                }
+
+                r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL);
                 if (r < 0) {
                         log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r));
                         goto fail;
@@ -747,6 +758,8 @@ fail:
 
 static int automount_start(Unit *u) {
         Automount *a = AUTOMOUNT(u);
+        Unit *trigger;
+        int r;
 
         assert(a);
         assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
@@ -756,8 +769,17 @@ static int automount_start(Unit *u) {
                 return -EEXIST;
         }
 
-        if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
+        trigger = UNIT_TRIGGER(u);
+        if (!trigger || trigger->load_state != UNIT_LOADED) {
+                log_unit_error(u, "Refusing to start, unit to trigger not loaded.");
                 return -ENOENT;
+        }
+
+        r = unit_start_limit_test(u);
+        if (r < 0) {
+                automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
+                return r;
+        }
 
         a->result = AUTOMOUNT_SUCCESS;
         automount_enter_waiting(a);
@@ -776,8 +798,9 @@ static int automount_stop(Unit *u) {
 
 static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
         Automount *a = AUTOMOUNT(u);
-        void *p;
         Iterator i;
+        void *p;
+        int r;
 
         assert(a);
         assert(f);
@@ -792,15 +815,9 @@ static int automount_serialize(Unit *u, FILE *f, FDSet *fds) {
         SET_FOREACH(p, a->expire_tokens, i)
                 unit_serialize_item_format(u, f, "expire-token", "%u", PTR_TO_UINT(p));
 
-        if (a->pipe_fd >= 0) {
-                int copy;
-
-                copy = fdset_put_dup(fds, a->pipe_fd);
-                if (copy < 0)
-                        return copy;
-
-                unit_serialize_item_format(u, f, "pipe-fd", "%i", copy);
-        }
+        r = unit_serialize_item_fd(u, f, fds, "pipe-fd", a->pipe_fd);
+        if (r < 0)
+                return r;
 
         return 0;
 }
@@ -905,10 +922,11 @@ static bool automount_check_gc(Unit *u) {
 }
 
 static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata) {
-        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         union autofs_v5_packet_union packet;
         Automount *a = AUTOMOUNT(userdata);
         struct stat st;
+        Unit *trigger;
         int r;
 
         assert(a);
@@ -955,7 +973,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
         case autofs_ptype_expire_direct:
                 log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where);
 
-                (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
+                automount_stop_expire(a);
 
                 r = set_ensure_allocated(&a->expire_tokens, NULL);
                 if (r < 0) {
@@ -981,7 +999,13 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
                         break;
                 }
 
-                r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL);
+                trigger = UNIT_TRIGGER(UNIT(a));
+                if (!trigger) {
+                        log_unit_error(UNIT(a), "Unit to trigger vanished.");
+                        goto fail;
+                }
+
+                r = manager_add_job(UNIT(a)->manager, JOB_STOP, trigger, JOB_REPLACE, &error, NULL);
                 if (r < 0) {
                         log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r));
                         goto fail;
@@ -1026,18 +1050,11 @@ static bool automount_supported(void) {
         return supported;
 }
 
-static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
-        [AUTOMOUNT_DEAD] = "dead",
-        [AUTOMOUNT_WAITING] = "waiting",
-        [AUTOMOUNT_RUNNING] = "running",
-        [AUTOMOUNT_FAILED] = "failed"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
-
 static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
         [AUTOMOUNT_SUCCESS] = "success",
-        [AUTOMOUNT_FAILURE_RESOURCES] = "resources"
+        [AUTOMOUNT_FAILURE_RESOURCES] = "resources",
+        [AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
+        [AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult);
@@ -1050,9 +1067,6 @@ const UnitVTable automount_vtable = {
                 "Automount\0"
                 "Install\0",
 
-        .no_alias = true,
-        .no_instances = true,
-
         .init = automount_init,
         .load = automount_load,
         .done = automount_done,
@@ -1072,6 +1086,8 @@ const UnitVTable automount_vtable = {
 
         .check_gc = automount_check_gc,
 
+        .trigger_notify = automount_trigger_notify,
+
         .reset_failed = automount_reset_failed,
 
         .bus_vtable = bus_automount_vtable,