automount.c revision 3dbadf9ef96e76f1bc472660ba5435dc0fa27a66
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering This file is part of systemd.
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering Copyright 2010 Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering systemd is free software; you can redistribute it and/or modify it
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering under the terms of the GNU Lesser General Public License as published by
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering (at your option) any later version.
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering systemd is distributed in the hope that it will be useful, but
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering Lesser General Public License for more details.
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering You should have received a copy of the GNU Lesser General Public License
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poetteringstatic const UnitActiveState state_translation_table[_AUTOMOUNT_STATE_MAX] = {
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poetteringstatic inline void expire_data_free(struct expire_data *data) {
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart PoetteringDEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poetteringstatic int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata);
938d2699d2e818bd996614e89ea3d668200ad2a8Zbigniew Jędrzejewski-Szmek assert(u->load_state == UNIT_STUB);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poetteringstatic void repeat_unmount(const char *path) {
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering /* If there are multiple mounts on a mount point, this
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering * removes them all */
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering log_error_errno(errno, "Failed to unmount: %m");
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poetteringstatic int automount_send_ready(Automount *a, Set *tokens, int status);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poetteringstatic void unmount_autofs(Automount *a) {
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering automount_send_ready(a, a->tokens, -EHOSTDOWN);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering automount_send_ready(a, a->expire_tokens, -EHOSTDOWN);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering a->pipe_event_source = sd_event_source_unref(a->pipe_event_source);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering /* If we reload/reexecute things we keep the mount point
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering (UNIT(a)->manager->exit_code != MANAGER_RELOAD &&
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering UNIT(a)->manager->exit_code != MANAGER_REEXECUTE))
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering a->expire_event_source = sd_event_source_unref(a->expire_event_source);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poetteringstatic int automount_add_mount_links(Automount *a) {
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering return unit_require_mounts_for(UNIT(a), parent);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poetteringstatic int automount_add_default_dependencies(Automount *a) {
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering if (UNIT(a)->manager->running_as != MANAGER_SYSTEM)
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poetteringstatic int automount_verify(Automount *a) {
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering log_unit_error(UNIT(a), "Cannot have an automount unit for the root directory. Refusing.");
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering r = unit_name_from_path(a->where, ".automount", &e);
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering return log_unit_error(UNIT(a), "Failed to generate unit name from path: %m");
938d2699d2e818bd996614e89ea3d668200ad2a8Zbigniew Jędrzejewski-Szmek if (!unit_has_name(UNIT(a), e)) {
938d2699d2e818bd996614e89ea3d668200ad2a8Zbigniew Jędrzejewski-Szmek log_unit_error(UNIT(a), "Where= setting doesn't match unit name. Refusing.");
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering /* Load a .automount file */
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering r = unit_load_fragment_and_dropin_optional(u);
3cadce7d33e263ec7a6a83c00c11144930258b22Thomas Bächler r = unit_load_related_unit(u, ".mount", &x);
c7fdf44d08e1217d40dc092fb90a65978a0f541fLennart Poettering r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
4cd2b2cf8ca585d15ebc859701b346658262b5bbDenis Tikhomirovstatic void automount_set_state(Automount *a, AutomountState state) {
4cd2b2cf8ca585d15ebc859701b346658262b5bbDenis Tikhomirov log_unit_debug(UNIT(a), "Changed %s -> %s", automount_state_to_string(old_state), automount_state_to_string(state));
0c9d8f1d4b5018199cb5a9b57580dc1480a7f915Jani Nikula unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true);
7b909d7407965c03caaba30daae7aee113627a83Josh Triplett if (a->deserialized_state == AUTOMOUNT_WAITING ||
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u);
3cadce7d33e263ec7a6a83c00c11144930258b22Thomas Bächler (void) sd_event_source_set_description(a->pipe_event_source, "automount-io");
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering automount_set_state(a, a->deserialized_state);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poetteringstatic void automount_dump(Unit *u, FILE *f, const char *prefix) {
c33b329709ebe2755181980a050d02ec7c81ed87Michal Schmidt "%sAutomount State: %s\n"
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek "%sResult: %s\n"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering "%sWhere: %s\n"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering "%sDirectoryMode: %04o\n"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering "%sTimeoutIdleUSec: %s\n",
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering prefix, automount_state_to_string(a->state),
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek prefix, automount_result_to_string(a->result),
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, a->timeout_idle_usec, USEC_PER_SEC));
938d2699d2e818bd996614e89ea3d668200ad2a8Zbigniew Jędrzejewski-Szmekstatic void automount_enter_dead(Automount *a, AutomountResult f) {
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY);
56f64d95763a799ba4475daf44d8e9f72a1bd474Michal Schmidt return log_error_errno(errno, "Failed to open /dev/autofs: %m");
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering if (ioctl(m->dev_autofs_fd, AUTOFS_DEV_IOCTL_VERSION, ¶m) < 0) {
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek m->dev_autofs_fd = safe_close(m->dev_autofs_fd);
3cadce7d33e263ec7a6a83c00c11144930258b22Thomas Bächler log_debug("Autofs kernel version %i.%i", param.ver_major, param.ver_minor);
3cadce7d33e263ec7a6a83c00c11144930258b22Thomas Bächlerstatic int open_ioctl_fd(int dev_autofs_fd, const char *where, dev_t devid) {
be3f52f4ed02a9256b1577719677b32a17b525acLennart Poettering l = sizeof(struct autofs_dev_ioctl) + strlen(where) + 1;
be3f52f4ed02a9256b1577719677b32a17b525acLennart Poettering if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_OPENMOUNT, param) < 0)
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poetteringstatic int autofs_protocol(int dev_autofs_fd, int ioctl_fd) {
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOVER, ¶m) < 0)
934ae16baf543af03f3f521277d14524ca772d17Lennart Poettering if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_PROTOSUBVER, ¶m) < 0)
0f4ba83c397e807939a4eb0b2cbd04ad4ab548ccLennart Poettering log_debug("Autofs protocol version %i.%i", major, minor);
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmekstatic int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, usec_t usec) {
bca81be7755d15e7369d764bfa94a7ca6c595c76Topi Miettinen /* Convert to seconds, rounding up. */
7b909d7407965c03caaba30daae7aee113627a83Josh Triplett param.timeout.timeout = (usec + USEC_PER_SEC - 1) / USEC_PER_SEC;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) < 0)
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poetteringstatic int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, int status) {
da927ba997d68401563b927f92e6e40e021a8e5cMichal Schmidt if (ioctl(dev_autofs_fd, status ? AUTOFS_DEV_IOCTL_FAIL : AUTOFS_DEV_IOCTL_READY, ¶m) < 0)
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmekstatic int automount_send_ready(Automount *a, Set *tokens, int status) {
assert(a);
if (ioctl_fd < 0)
return ioctl_fd;
if (status)
status);
assert(a);
switch (state) {
case MOUNT_MOUNTED:
case MOUNT_REMOUNTING:
r = automount_start_expire(a);
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:
switch (state) {
case MOUNT_DEAD:
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:
bool mounted = false;
int r, dev_autofs_fd;
assert(a);
goto fail;
if (dev_autofs_fd < 0) {
r = dev_autofs_fd;
goto fail;
r = -errno;
goto fail;
r = -errno;
goto fail;
mounted = true;
r = -errno;
goto fail;
if (ioctl_fd < 0) {
r = ioctl_fd;
goto fail;
goto fail;
goto fail;
r = sd_event_add_io(UNIT(a)->manager->event, &a->pipe_event_source, p[0], EPOLLIN, automount_dispatch_io, a);
goto fail;
a->pipe_fd = p[0];
fail:
safe_close_pair(p);
if (mounted)
static void *expire_thread(void *p) {
return NULL;
assert(a);
if (!data)
return log_oom();
return automount_start_expire(a);
assert(a);
if (a->timeout_idle_usec == 0)
if (a->expire_event_source) {
r = sd_event_add_time(
&a->expire_event_source,
assert(a);
goto fail;
goto fail;
fail:
assert(a);
return -EEXIST;
return -ENOENT;
assert(a);
Iterator i;
assert(a);
assert(f);
if (a->pipe_fd >= 0) {
int copy;
if (copy < 0)
return copy;
assert(a);
if (state < 0)
else if (f != AUTOMOUNT_SUCCESS)
a->result = f;
a->dev_id = (unsigned) d;
unsigned token;
log_oom();
unsigned token;
log_oom();
int fd;
assert(u);
assert(u);
assert(u);
if (!UNIT_TRIGGER(u))
assert(a);
goto fail;
goto fail;
log_unit_info(UNIT(a), "Got automount request for %s, triggered by %"PRIu32" (%s)", a->where, packet.v5_packet.pid, strna(p));
goto fail;
goto fail;
goto fail;
goto fail;
goto fail;
r = manager_add_job(UNIT(a)->manager, JOB_STOP, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL);
goto fail;
fail:
assert(m);
assert(a);
static bool automount_supported(void) {
if (supported < 0)
return supported;
.sections =
.no_alias = true,
.no_instances = true,
.finished_start_job = {
.finished_stop_job = {