diff options
Diffstat (limited to 'src/core/automount.c')
-rw-r--r-- | src/core/automount.c | 338 |
1 files changed, 198 insertions, 140 deletions
diff --git a/src/core/automount.c b/src/core/automount.c index d847dc1629..7d7a0a6e46 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -1,5 +1,3 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - /*** This file is part of systemd. @@ -20,29 +18,37 @@ ***/ #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,34 +90,14 @@ 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) return; - automount_send_ready(a, a->tokens, -EHOSTDOWN); - automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); - a->pipe_event_source = sd_event_source_unref(a->pipe_event_source); a->pipe_fd = safe_close(a->pipe_fd); @@ -116,8 +105,14 @@ 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)) { + automount_send_ready(a, a->tokens, -EHOSTDOWN); + automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); + + r = repeat_unmount(a->where, MNT_DETACH); + if (r < 0) + log_error_errno(r, "Failed to unmount: %m"); + } } static void automount_done(Unit *u) { @@ -127,26 +122,22 @@ static void automount_done(Unit *u) { unmount_autofs(a); - free(a->where); - a->where = NULL; + 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); } @@ -156,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); @@ -227,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); @@ -244,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); @@ -276,6 +271,11 @@ static int automount_coldplug(Unit *u) { return r; (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); + if (a->deserialized_state == AUTOMOUNT_RUNNING) { + r = automount_start_expire(a); + if (r < 0) + log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); + } } automount_set_state(a, a->deserialized_state); @@ -306,7 +306,7 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) { static void automount_enter_dead(Automount *a, AutomountResult f) { assert(a); - if (f != AUTOMOUNT_SUCCESS) + if (a->result == AUTOMOUNT_SUCCESS) a->result = f; automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); @@ -417,7 +417,7 @@ static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, in init_autofs_dev_ioctl(¶m); param.ioctlfd = ioctl_fd; - if (status) { + if (status != 0) { param.fail.token = token; param.fail.status = status; } else @@ -444,7 +444,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."); @@ -471,51 +471,68 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) { return r; } -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); - switch (state) { - case MOUNT_MOUNTED: - case MOUNT_REMOUNTING: - automount_send_ready(a, a->tokens, 0); - 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); - break; - default: - break; + /* 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; } - 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; + /* 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); + + r = automount_start_expire(a); + if (r < 0) + log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); + + automount_set_state(a, AUTOMOUNT_RUNNING); } - return 0; + if (IN_SET(MOUNT(other)->state, + MOUNT_MOUNTING, MOUNT_MOUNTING_DONE, + MOUNT_MOUNTED, MOUNT_REMOUNTING, + 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->expire_tokens, -ENODEV); + } + + if (MOUNT(other)->state == MOUNT_DEAD) + (void) automount_send_ready(a, a->expire_tokens, 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) { @@ -603,12 +620,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); } @@ -633,8 +654,6 @@ static void *expire_thread(void *p) { return NULL; } -static int automount_start_expire(Automount *a); - static int automount_dispatch_expire(sd_event_source *source, usec_t usec, void *userdata) { Automount *a = AUTOMOUNT(userdata); _cleanup_(expire_data_freep) struct expire_data *data = NULL; @@ -672,7 +691,10 @@ static int automount_start_expire(Automount *a) { assert(a); - timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/10, USEC_PER_SEC); + if (a->timeout_idle_usec == 0) + return 0; + + timeout = now(CLOCK_MONOTONIC) + MAX(a->timeout_idle_usec/3, USEC_PER_SEC); if (a->expire_event_source) { r = sd_event_source_set_time(a->expire_event_source, timeout); @@ -695,8 +717,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; @@ -722,18 +753,21 @@ 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; } } - r = automount_start_expire(a); - if (r < 0) - log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); - automount_set_state(a, AUTOMOUNT_RUNNING); return; @@ -743,6 +777,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); @@ -752,8 +788,21 @@ 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; + } + + r = unit_acquire_invocation_id(u); + if (r < 0) + return r; a->result = AUTOMOUNT_SUCCESS; automount_enter_waiting(a); @@ -772,8 +821,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); @@ -788,15 +838,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; } @@ -901,9 +945,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); @@ -950,7 +996,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) { @@ -963,7 +1009,26 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m"); goto fail; } - r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL); + + /* Before we do anything, let's see if somebody is playing games with us? */ + if (lstat(a->where, &st) < 0) { + log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m"); + goto fail; + } + + if (!S_ISDIR(st.st_mode) || st.st_dev == a->dev_id) { + log_unit_info(UNIT(a), "Automount point already unmounted?"); + automount_send_ready(a, a->expire_tokens, 0); + break; + } + + 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; @@ -1008,18 +1073,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); @@ -1032,9 +1090,6 @@ const UnitVTable automount_vtable = { "Automount\0" "Install\0", - .no_alias = true, - .no_instances = true, - .init = automount_init, .load = automount_load, .done = automount_done, @@ -1054,10 +1109,14 @@ const UnitVTable automount_vtable = { .check_gc = automount_check_gc, + .trigger_notify = automount_trigger_notify, + .reset_failed = automount_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Automount", .bus_vtable = bus_automount_vtable, + .bus_set_property = bus_automount_set_property, + + .can_transient = true, .shutdown = automount_shutdown, .supported = automount_supported, @@ -1066,7 +1125,6 @@ const UnitVTable automount_vtable = { .finished_start_job = { [JOB_DONE] = "Set up automount %s.", [JOB_FAILED] = "Failed to set up automount %s.", - [JOB_DEPENDENCY] = "Dependency failed for %s.", }, .finished_stop_job = { [JOB_DONE] = "Unset automount %s.", |