automount.c revision 20ad4cfd8e5592f634f20468798cbc1055ab9faf
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/auto_fs4.h>
#include <linux/auto_dev-ioctl.h>
#include "unit.h"
#include "automount.h"
#include "mount.h"
#include "load-fragment.h"
#include "load-dropin.h"
#include "unit-name.h"
#include "dbus-automount.h"
#include "bus-errors.h"
#include "special.h"
#include "label.h"
#include "mkdir.h"
#include "path-util.h"
#include "dbus-common.h"
};
static int open_dev_autofs(Manager *m);
static void automount_init(Unit *u) {
assert(u);
a->directory_mode = 0755;
UNIT(a)->ignore_on_isolate = true;
}
static void repeat_unmout(const char *path) {
for (;;) {
/* If there are multiple mounts on a mount point, this
* removes them all */
continue;
log_error("Failed to unmount: %m");
break;
}
}
static void unmount_autofs(Automount *a) {
assert(a);
if (a->pipe_fd < 0)
return;
a->pipe_fd = -1;
* around */
if (a->where &&
repeat_unmout(a->where);
}
static void automount_done(Unit *u) {
assert(a);
unmount_autofs(a);
unit_ref_unset(&a->mount);
}
int r;
assert(a);
assert(m);
return 0;
return 0;
return 0;
if (r < 0)
return r;
return 0;
}
static int automount_add_mount_links(Automount *a) {
int r;
assert(a);
if (r < 0)
return r;
}
return 0;
}
static int automount_add_default_dependencies(Automount *a) {
int r;
assert(a);
return 0;
r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
if (r < 0)
return r;
return 0;
}
static int automount_verify(Automount *a) {
bool b;
char *e;
assert(a);
return 0;
log_error("Cannot have an automount unit for the root directory. Refusing.");
return -EINVAL;
}
if (!e)
return -ENOMEM;
b = unit_has_name(UNIT(a), e);
free(e);
if (!b) {
return -EINVAL;
}
return 0;
}
static int automount_load(Unit *u) {
int r;
assert(u);
/* Load a .automount file */
if (r < 0)
return r;
if (u->load_state == UNIT_LOADED) {
Unit *x;
if (!a->where) {
if (!a->where)
return -ENOMEM;
}
path_kill_slashes(a->where);
r = automount_add_mount_links(a);
if (r < 0)
return r;
r = unit_load_related_unit(u, ".mount", &x);
if (r < 0)
return r;
unit_ref_set(&a->mount, x);
if (r < 0)
return r;
if (UNIT(a)->default_dependencies) {
if (r < 0)
return r;
}
}
return automount_verify(a);
}
assert(a);
if (state != AUTOMOUNT_WAITING &&
unmount_autofs(a);
log_debug("%s changed %s -> %s",
}
static int automount_coldplug(Unit *u) {
int r;
assert(a);
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) {
if (r < 0)
return r;
}
}
return 0;
}
assert(a);
fprintf(f,
"%sAutomount State: %s\n"
"%sResult: %s\n"
"%sWhere: %s\n"
"%sDirectoryMode: %04o\n",
prefix, a->directory_mode);
}
assert(a);
if (f != AUTOMOUNT_SUCCESS)
a->result = f;
}
static int open_dev_autofs(Manager *m) {
struct autofs_dev_ioctl param;
assert(m);
if (m->dev_autofs_fd >= 0)
return m->dev_autofs_fd;
if (m->dev_autofs_fd < 0) {
return -errno;
}
m->dev_autofs_fd = -1;
return -errno;
}
return m->dev_autofs_fd;
}
struct autofs_dev_ioctl *param;
size_t l;
assert(dev_autofs_fd >= 0);
return -errno;
return -EIO;
}
struct autofs_dev_ioctl param;
assert(dev_autofs_fd >= 0);
return -errno;
return -errno;
return 0;
}
struct autofs_dev_ioctl param;
assert(dev_autofs_fd >= 0);
return -errno;
return 0;
}
struct autofs_dev_ioctl param;
assert(dev_autofs_fd >= 0);
if (status) {
} else
return -errno;
return 0;
}
int ioctl_fd, r;
unsigned token;
assert(a);
if (set_isempty(a->tokens))
return 0;
if (ioctl_fd < 0) {
r = ioctl_fd;
goto fail;
}
if (status)
else
log_debug("Sending success.");
r = 0;
/* Autofs thankfully does not hand out 0 as a token */
int k;
/* Autofs fun fact II:
*
* if you pass a positive status code here, the kernel will
* freeze! Yay! */
status);
if (k < 0)
r = k;
}
fail:
if (ioctl_fd >= 0)
return r;
}
static void automount_enter_waiting(Automount *a) {
int p[2] = { -1, -1 };
bool mounted = false;
assert(a);
if (a->tokens)
if (dev_autofs_fd < 0) {
r = dev_autofs_fd;
goto fail;
}
/* We knowingly ignore the results of this call */
r = -errno;
goto fail;
}
snprintf(options, sizeof(options), "fd=%i,pgrp=%u,minproto=5,maxproto=5,direct", p[1], (unsigned) getpgrp());
r = -errno;
goto fail;
}
mounted = true;
close_nointr_nofail(p[1]);
p[1] = -1;
r = -errno;
goto fail;
}
if (ioctl_fd < 0) {
r = ioctl_fd;
goto fail;
}
if (r < 0)
goto fail;
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. */
ioctl_fd = -1;
if (r < 0)
goto fail;
a->pipe_fd = p[0];
return;
fail:
assert_se(close_pipe(p) == 0);
if (ioctl_fd >= 0)
if (mounted)
repeat_unmout(a->where);
}
static void automount_enter_runnning(Automount *a) {
int r;
assert(a);
/* We don't take mount requests anymore if we are supposed to
* shut down anyway */
if (unit_pending_inactive(UNIT(a))) {
return;
}
/* Before we do anything, let's see if somebody is playing games with us? */
goto fail;
}
else if ((r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_DEREF(a->mount), JOB_REPLACE, true, &error, NULL)) < 0) {
goto fail;
}
return;
fail:
}
static int automount_start(Unit *u) {
assert(a);
if (path_is_mount_point(a->where, false)) {
return -EEXIST;
}
return -ENOENT;
a->result = AUTOMOUNT_SUCCESS;
return 0;
}
static int automount_stop(Unit *u) {
assert(a);
return 0;
}
void *p;
Iterator i;
assert(a);
assert(f);
SET_FOREACH(p, a->tokens, i)
if (a->pipe_fd >= 0) {
int copy;
if (copy < 0)
return copy;
}
return 0;
}
int r;
assert(a);
if (state < 0)
else
a->deserialized_state = state;
if (f < 0)
else if (f != AUTOMOUNT_SUCCESS)
a->result = f;
unsigned d;
else
a->dev_id = (unsigned) d;
unsigned token;
else {
if (!a->tokens)
return -ENOMEM;
if (r < 0)
return r;
}
int fd;
else {
if (a->pipe_fd >= 0)
}
} else
return 0;
}
assert(u);
}
static const char *automount_sub_state_to_string(Unit *u) {
assert(u);
}
static bool automount_check_gc(Unit *u) {
assert(a);
if (!UNIT_DEREF(a->mount))
return false;
}
union autofs_v5_packet_union packet;
ssize_t l;
int r;
assert(a);
log_error("Got invalid poll event on pipe.");
goto fail;
}
if (l != sizeof(packet)) {
goto fail;
}
_cleanup_free_ char *p = NULL;
log_debug("Got direct mount request on %s, triggered by %lu (%s)",
} else
if (r < 0) {
log_error("Failed to allocate token set.");
goto fail;
}
if (r < 0) {
goto fail;
}
break;
default:
break;
}
return;
fail:
}
static void automount_shutdown(Manager *m) {
assert(m);
if (m->dev_autofs_fd >= 0)
}
static void automount_reset_failed(Unit *u) {
assert(a);
if (a->state == AUTOMOUNT_FAILED)
a->result = AUTOMOUNT_SUCCESS;
}
static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_WAITING] = "waiting",
[AUTOMOUNT_RUNNING] = "running",
[AUTOMOUNT_FAILED] = "failed"
};
static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
[AUTOMOUNT_SUCCESS] = "success",
[AUTOMOUNT_FAILURE_RESOURCES] = "resources"
};
const UnitVTable automount_vtable = {
.object_size = sizeof(Automount),
.sections =
"Unit\0"
"Automount\0"
"Install\0",
.no_alias = true,
.no_instances = true,
.init = automount_init,
.load = automount_load,
.done = automount_done,
.dump = automount_dump,
.start = automount_start,
.stop = automount_stop,
.bus_interface = "org.freedesktop.systemd1.Automount",
.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.",
[JOB_FAILED] = "Failed to unset automount %s.",
},
},
};