automount.c revision b26fa1a2fbcfee7d03b0c8fd15ec3aa64ae70b9f
/***
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 <fcntl.h>
#include <limits.h>
#include <linux/auto_dev-ioctl.h>
#include <linux/auto_fs4.h>
#include <unistd.h>
#include "alloc-util.h"
#include "async.h"
#include "automount.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 "process-util.h"
#include "special.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
#include "unit-name.h"
#include "unit.h"
};
struct expire_data {
int dev_autofs_fd;
int ioctl_fd;
};
if (!data)
return;
}
static int open_dev_autofs(Manager *m);
static void automount_init(Unit *u) {
assert(u);
a->pipe_fd = -1;
a->directory_mode = 0755;
UNIT(a)->ignore_on_isolate = true;
}
static void unmount_autofs(Automount *a) {
int r;
assert(a);
if (a->pipe_fd < 0)
return;
* around */
if (a->where &&
if (r < 0)
log_error_errno(r, "Failed to unmount: %m");
}
}
static void automount_done(Unit *u) {
assert(a);
unmount_autofs(a);
}
static int automount_add_mount_links(Automount *a) {
assert(a);
if (!parent)
return -ENOMEM;
}
static int automount_add_default_dependencies(Automount *a) {
int r;
assert(a);
if (!UNIT(a)->default_dependencies)
return 0;
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) {
_cleanup_free_ char *e = NULL;
int r;
assert(a);
return 0;
return -EINVAL;
}
if (r < 0)
if (!unit_has_name(UNIT(a), e)) {
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 (r < 0)
return r;
}
path_kill_slashes(a->where);
r = unit_load_related_unit(u, ".mount", &x);
if (r < 0)
return r;
if (r < 0)
return r;
r = automount_add_mount_links(a);
if (r < 0)
return r;
if (r < 0)
return r;
}
return automount_verify(a);
}
assert(a);
if (state != AUTOMOUNT_WAITING &&
unmount_autofs(a);
log_unit_debug(UNIT(a), "Changed %s -> %s", automount_state_to_string(old_state), automount_state_to_string(state));
}
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) {
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;
}
}
return 0;
}
char time_string[FORMAT_TIMESPAN_MAX];
assert(a);
fprintf(f,
"%sAutomount State: %s\n"
"%sResult: %s\n"
"%sWhere: %s\n"
"%sDirectoryMode: %04o\n"
"%sTimeoutIdleUSec: %s\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;
}
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);
/* Convert to seconds, rounding up. */
return -errno;
return 0;
}
struct autofs_dev_ioctl param;
assert(dev_autofs_fd >= 0);
if (status) {
} else
return -errno;
return 0;
}
unsigned token;
int r;
assert(a);
if (set_isempty(tokens))
return 0;
if (ioctl_fd < 0)
return ioctl_fd;
if (status)
else
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;
}
return r;
}
static int automount_start_expire(Automount *a);
int r;
assert(a);
switch (state) {
case MOUNT_MOUNTED:
case MOUNT_REMOUNTING:
automount_send_ready(a, a->tokens, 0);
r = automount_start_expire(a);
if (r < 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:
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:
break;
default:
break;
}
return 0;
}
static void automount_enter_waiting(Automount *a) {
int p[2] = { -1, -1 };
bool mounted = false;
int r, dev_autofs_fd;
assert(a);
if (r < 0)
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;
}
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. */
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;
a->pipe_fd = p[0];
return;
fail:
safe_close_pair(p);
if (mounted) {
if (r < 0)
log_error_errno(r, "Failed to unmount, ignoring: %m");
}
}
static void *expire_thread(void *p) {
struct autofs_dev_ioctl param;
int r;
do {
} while (r >= 0);
return NULL;
}
int r;
assert(a);
if (!data)
return log_oom();
if (data->dev_autofs_fd < 0)
if (r < 0)
return automount_start_expire(a);
}
static int automount_start_expire(Automount *a) {
int r;
assert(a);
if (a->timeout_idle_usec == 0)
return 0;
if (a->expire_event_source) {
if (r < 0)
return r;
}
r = sd_event_add_time(
&a->expire_event_source,
CLOCK_MONOTONIC, timeout, 0,
if (r < 0)
return r;
return 0;
}
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_stop_pending(UNIT(a))) {
return;
}
/* Before we do anything, let's see if somebody is playing games with us? */
goto fail;
}
else {
if (r < 0) {
goto fail;
}
}
return;
fail:
}
static int automount_start(Unit *u) {
assert(a);
if (path_is_mount_point(a->where, 0) > 0) {
return -EEXIST;
}
return -ENOENT;
a->result = AUTOMOUNT_SUCCESS;
return 1;
}
static int automount_stop(Unit *u) {
assert(a);
return 1;
}
Iterator i;
void *p;
int r;
assert(a);
assert(f);
SET_FOREACH(p, a->tokens, i)
SET_FOREACH(p, a->expire_tokens, i)
if (r < 0)
return r;
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 (r < 0) {
log_oom();
return 0;
}
if (r < 0)
log_unit_error_errno(u, r, "Failed to add token to set: %m");
}
unsigned token;
else {
if (r < 0) {
log_oom();
return 0;
}
if (r < 0)
log_unit_error_errno(u, r, "Failed to add expire token to set: %m");
}
int fd;
else {
safe_close(a->pipe_fd);
}
} 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(u);
if (!UNIT_TRIGGER(u))
return false;
}
union autofs_v5_packet_union packet;
int r;
assert(a);
goto fail;
}
if (r < 0) {
goto fail;
}
_cleanup_free_ char *p = NULL;
log_unit_info(UNIT(a), "Got automount request for %s, triggered by %"PRIu32" (%s)", a->where, packet.v5_packet.pid, strna(p));
} else
if (r < 0) {
goto fail;
}
if (r < 0) {
goto fail;
}
break;
if (r < 0) {
goto fail;
}
if (r < 0) {
goto fail;
}
/* Before we do anything, let's see if somebody is playing games with us? */
goto fail;
}
automount_send_ready(a, a->expire_tokens, 0);
break;
}
if (r < 0) {
goto fail;
}
break;
default:
break;
}
return 0;
fail:
return 0;
}
static void automount_shutdown(Manager *m) {
assert(m);
}
static void automount_reset_failed(Unit *u) {
assert(a);
if (a->state == AUTOMOUNT_FAILED)
a->result = AUTOMOUNT_SUCCESS;
}
static bool automount_supported(void) {
static int supported = -1;
if (supported < 0)
return supported;
}
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,
.finished_start_job = {
[JOB_DONE] = "Set up automount %s.",
[JOB_FAILED] = "Failed to set up automount %s.",
},
.finished_stop_job = {
[JOB_DONE] = "Unset automount %s.",
[JOB_FAILED] = "Failed to unset automount %s.",
},
},
};