static int automount_coldplug(Unit *u) { Automount *a = AUTOMOUNT(u); int r; assert(a); assert(a->state == AUTOMOUNT_DEAD); if (a->deserialized_state != a->state) { r = open_dev_autofs(u->manager); if (r < 0) return r; if (a->deserialized_state == AUTOMOUNT_WAITING || a->deserialized_state == AUTOMOUNT_RUNNING) { assert(a->pipe_fd >= 0); r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u); if (r < 0) 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); } return 0; }
static int automount_coldplug(Unit *u) { Automount *a = AUTOMOUNT(u); int r; assert(a); assert(a->state == AUTOMOUNT_DEAD); if (a->deserialized_state != a->state) { r = open_dev_autofs(u->manager); if (r < 0) return r; if (a->deserialized_state == AUTOMOUNT_WAITING || a->deserialized_state == AUTOMOUNT_RUNNING) { assert(a->pipe_fd >= 0); r = sd_event_add_io(u->manager->event, a->pipe_fd, EPOLLIN, automount_dispatch_io, u, &a->pipe_event_source); if (r < 0) return r; } automount_set_state(a, a->deserialized_state); } return 0; }
static void automount_enter_dead(Automount *a, AutomountResult f) { assert(a); if (a->result == AUTOMOUNT_SUCCESS) a->result = f; automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); }
static void automount_enter_running(Automount *a) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; Unit *trigger; struct stat st; int r; assert(a); /* If the user masked our unit in the meantime, fail */ if (UNIT(a)->load_state != UNIT_LOADED) { log_unit_error(UNIT(a), "Suppressing automount event since unit is no longer loaded."); goto fail; } /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_stop_pending(UNIT(a))) { log_unit_debug(UNIT(a), "Suppressing automount request since unit stop is scheduled."); automount_send_ready(a, a->tokens, -EHOSTDOWN); automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); return; } mkdir_p_label(a->where, a->directory_mode); /* 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; } /* The mount unit may have been explicitly started before we got the * autofs request. Ack it to unblock anything waiting on the mount point. */ if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) { log_unit_info(UNIT(a), "Automount point already active?"); automount_send_ready(a, a->tokens, 0); return; } 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; } automount_set_state(a, AUTOMOUNT_RUNNING); return; fail: automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }
static void automount_reset_failed(Unit *u) { Automount *a = AUTOMOUNT(u); assert(a); if (a->state == AUTOMOUNT_FAILED) automount_set_state(a, AUTOMOUNT_DEAD); a->result = AUTOMOUNT_SUCCESS; }
static void automount_enter_runnning(Automount *a) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; struct stat st; int r; assert(a); /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_stop_pending(UNIT(a))) { log_debug_unit(UNIT(a)->id, "Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id); automount_send_ready(a, -EHOSTDOWN); return; } mkdir_p_label(a->where, a->directory_mode); /* Before we do anything, let's see if somebody is playing games with us? */ if (lstat(a->where, &st) < 0) { log_warning_unit(UNIT(a)->id, "%s failed to stat automount point: %m", UNIT(a)->id); goto fail; } if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) log_info_unit(UNIT(a)->id, "%s's automount point already active?", UNIT(a)->id); else { r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL); if (r < 0) { log_warning_unit(UNIT(a)->id, "%s failed to queue mount startup job: %s", UNIT(a)->id, bus_error_message(&error, r)); goto fail; } } automount_set_state(a, AUTOMOUNT_RUNNING); return; fail: automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }
static void automount_enter_waiting(Automount *a) { _cleanup_close_ int ioctl_fd = -1; int p[2] = { -1, -1 }; char name[sizeof("systemd-")-1 + DECIMAL_STR_MAX(pid_t) + 1]; char options[sizeof("fd=,pgrp=,minproto=5,maxproto=5,direct")-1 + DECIMAL_STR_MAX(int) + DECIMAL_STR_MAX(gid_t) + 1]; bool mounted = false; int r, dev_autofs_fd; struct stat st; assert(a); assert(a->pipe_fd < 0); assert(a->where); set_clear(a->tokens); r = unit_fail_if_symlink(UNIT(a), a->where); if (r < 0) goto fail; (void) mkdir_p_label(a->where, 0555); unit_warn_if_dir_nonempty(UNIT(a), a->where); dev_autofs_fd = open_dev_autofs(UNIT(a)->manager); if (dev_autofs_fd < 0) { r = dev_autofs_fd; goto fail; } if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) { r = -errno; goto fail; } xsprintf(options, "fd=%i,pgrp="PID_FMT",minproto=5,maxproto=5,direct", p[1], getpgrp()); xsprintf(name, "systemd-"PID_FMT, getpid()); if (mount(name, a->where, "autofs", 0, options) < 0) { r = -errno; goto fail; } mounted = true; p[1] = safe_close(p[1]); if (stat(a->where, &st) < 0) { r = -errno; goto fail; } ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev); if (ioctl_fd < 0) { r = ioctl_fd; goto fail; } r = autofs_protocol(dev_autofs_fd, ioctl_fd); if (r < 0) goto fail; r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, a->timeout_idle_usec); if (r < 0) goto fail; /* Autofs fun fact: * * Unless we close the ioctl fd here, for some weird reason * the direct mount will not receive events from the * kernel. */ r = sd_event_add_io(UNIT(a)->manager->event, &a->pipe_event_source, p[0], EPOLLIN, automount_dispatch_io, a); if (r < 0) goto fail; (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); a->pipe_fd = p[0]; a->dev_id = st.st_dev; automount_set_state(a, AUTOMOUNT_WAITING); return; fail: log_unit_error_errno(UNIT(a), r, "Failed to initialize automounter: %m"); safe_close_pair(p); if (mounted) { r = repeat_unmount(a->where, MNT_DETACH); if (r < 0) log_error_errno(r, "Failed to unmount, ignoring: %m"); } automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }
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); 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); } 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) { int p[2] = { -1, -1 }; char name[32], options[128]; bool mounted = false; int r, ioctl_fd = -1, dev_autofs_fd; struct stat st; assert(a); assert(a->pipe_fd < 0); assert(a->where); if (a->tokens) set_clear(a->tokens); dev_autofs_fd = open_dev_autofs(UNIT(a)->manager); if (dev_autofs_fd < 0) { r = dev_autofs_fd; goto fail; } /* We knowingly ignore the results of this call */ mkdir_p_label(a->where, 0555); warn_if_dir_nonempty(a->meta.id, a->where); if (pipe2(p, O_NONBLOCK|O_CLOEXEC) < 0) { r = -errno; goto fail; } snprintf(options, sizeof(options), "fd=%i,pgrp=%u,minproto=5,maxproto=5,direct", p[1], (unsigned) getpgrp()); char_array_0(options); snprintf(name, sizeof(name), "systemd-%u", (unsigned) getpid()); char_array_0(name); if (mount(name, a->where, "autofs", 0, options) < 0) { r = -errno; goto fail; } mounted = true; close_nointr_nofail(p[1]); p[1] = -1; if (stat(a->where, &st) < 0) { r = -errno; goto fail; } ioctl_fd = open_ioctl_fd(dev_autofs_fd, a->where, st.st_dev); if (ioctl_fd < 0) { r = ioctl_fd; goto fail; } r = autofs_protocol(dev_autofs_fd, ioctl_fd); if (r < 0) goto fail; r = autofs_set_timeout(dev_autofs_fd, ioctl_fd, 300); if (r < 0) goto fail; /* Autofs fun fact: * * Unless we close the ioctl fd here, for some weird reason * the direct mount will not receive events from the * kernel. */ close_nointr_nofail(ioctl_fd); ioctl_fd = -1; r = sd_event_add_io(UNIT(a)->manager->event, p[0], EPOLLIN, automount_dispatch_io, a, &a->pipe_event_source); if (r < 0) goto fail; a->pipe_fd = p[0]; a->dev_id = st.st_dev; automount_set_state(a, AUTOMOUNT_WAITING); return; fail: assert_se(close_pipe(p) == 0); if (ioctl_fd >= 0) close_nointr_nofail(ioctl_fd); if (mounted) repeat_unmount(a->where); log_error_unit(UNIT(a)->id, "Failed to initialize automounter: %s", strerror(-r)); automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); }