Bring in the new automounter, similar to what's provided in most other

UNIX systems, eg. MacOS X and Solaris.  It uses Sun-compatible map format,
has proper kernel support, and LDAP integration.

There are still a few outstanding problems; they will be fixed shortly.

Reviewed by:	allanjude@, emaste@, kib@, wblock@ (earlier versions)
Phabric:	D523
MFC after:	2 weeks
Relnotes:	yes
Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Edward Tomasz Napierala 2014-08-17 09:44:42 +00:00
parent f0f8856f68
commit 3914ddf8a7
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=270096
45 changed files with 5808 additions and 5 deletions

View file

@ -11,7 +11,8 @@ SUBDIR= sendmail
SUBDIR+=tests
.endif
BIN1= crontab \
BIN1= auto_master \
crontab \
devd.conf \
devfs.conf \
ddb.conf \
@ -225,6 +226,7 @@ distribution:
echo "./etc/spwd.db type=file mode=0600 uname=root gname=wheel"; \
) | ${METALOG.add}
.endif
${_+_}cd ${.CURDIR}/autofs; ${MAKE} install
.if ${MK_BLUETOOTH} != "no"
${_+_}cd ${.CURDIR}/bluetooth; ${MAKE} install
.endif

5
etc/auto_master Normal file
View file

@ -0,0 +1,5 @@
# $FreeBSD$
#
# Automounter master map, see auto_master(5) for details.
#
/net -hosts -nosuid

9
etc/autofs/Makefile Normal file
View file

@ -0,0 +1,9 @@
# $FreeBSD$
FILES= include_ldap special_hosts special_null
NO_OBJ=
FILESDIR= /etc/autofs
FILESMODE= 755
.include <bsd.prog.mk>

38
etc/autofs/include_ldap Normal file
View file

@ -0,0 +1,38 @@
#!/bin/sh
#
# $FreeBSD$
#
# Modify this to suit your needs. The "$1" is the map name, eg. "auto_master".
# To debug, simply run this script with map name as the only parameter. It's
# supposed to output map contents ("key location" pairs) to standard output.
SEARCHBASE="ou=$1,dc=example,dc=com"
ENTRY_ATTRIBUTE="cn"
VALUE_ATTRIBUTE="automountInformation"
/usr/local/bin/ldapsearch -LLL -x -o ldif-wrap=no -b "$SEARCHBASE" "$ENTRY_ATTRIBUTE" "$VALUE_ATTRIBUTE" | awk '
$1 == "'$ENTRY_ATTRIBUTE':" {
key = $2
}
$1 == "'$VALUE_ATTRIBUTE':" && key {
printf "%s%s", key, OFS
key = ""
for (i=2; i<NF; i++) {
printf "%s%s", $(i), OFS
}
printf "%s%s", $NF, ORS
}
# Double colon after attribute name means the value is in Base64.
$1 == "'$VALUE_ATTRIBUTE'::" && key {
printf "%s%s", key, OFS
key = ""
for (i=2; i<NF; i++) {
printf "%s%s", $(i), OFS
}
printf "%s", $NF | "b64decode -rp"
close("b64decode -rp")
printf "%s", ORS
}
'

17
etc/autofs/special_hosts Normal file
View file

@ -0,0 +1,17 @@
#!/bin/sh
#
# $FreeBSD$
#
if [ $# -eq 0 ]; then
out=`getent hosts`
[ $? -eq 0 ] || exit 1
echo "$out" | awk '{ print $2 }' | sort -u
exit 0
fi
out=`showmount -e "$1"`
[ $? -eq 0 ] || exit 1
echo "$out" | awk -v host="$1" \
'NR > 1 { printf "%s\t%s:%s ", $1, host, $1 } END { printf "\n" }'

4
etc/autofs/special_null Normal file
View file

@ -0,0 +1,4 @@
#!/usr/bin/true
#
# $FreeBSD$
#

View file

@ -306,6 +306,7 @@ amd_enable="NO" # Run amd service with $amd_flags (or NO).
amd_program="/usr/sbin/amd" # path to amd, if you want a different one.
amd_flags="-a /.amd_mnt -l syslog /host /etc/amd.map /net /etc/amd.map"
amd_map_program="NO" # Can be set to "ypcat -k amd.master"
autofs_enable="NO" # Run automountd(8)
nfs_client_enable="NO" # This host is an NFS client (or NO).
nfs_access_cache="60" # Client cache timeout in seconds
nfs_server_enable="NO" # This host is an NFS server (or NO).

View file

@ -24,6 +24,8 @@
etc
X11
..
autofs
..
bluetooth
..
casper

View file

@ -20,6 +20,9 @@ FILES= DAEMON \
atm3 \
auditd \
auditdistd \
automount \
automountd \
autounmountd \
bgfsck \
${_bluetooth} \
bootparams \

31
etc/rc.d/automount Normal file
View file

@ -0,0 +1,31 @@
#!/bin/sh
#
# $FreeBSD$
#
# PROVIDE: automount
# REQUIRE: nfsclient
# KEYWORD: nojail shutdown
. /etc/rc.subr
name="automount"
rcvar="autofs_enable"
start_cmd="automount_start"
stop_cmd="automount_stop"
required_modules="autofs"
automount_start()
{
/usr/sbin/automount
}
automount_stop()
{
/sbin/umount -At autofs
}
load_rc_config $name
run_rc_command "$1"

19
etc/rc.d/automountd Normal file
View file

@ -0,0 +1,19 @@
#!/bin/sh
#
# $FreeBSD$
#
# PROVIDE: automountd
# REQUIRE: automount
# KEYWORD: nojail
. /etc/rc.subr
name="automountd"
rcvar="autofs_enable"
pidfile="/var/run/${name}.pid"
command="/usr/sbin/${name}"
required_modules="autofs"
load_rc_config $name
run_rc_command "$1"

18
etc/rc.d/autounmountd Normal file
View file

@ -0,0 +1,18 @@
#!/bin/sh
#
# $FreeBSD$
#
# PROVIDE: autounmountd
# REQUIRE: nfsclient
# KEYWORD: nojail
. /etc/rc.subr
name="autounmountd"
rcvar="autofs_enable"
pidfile="/var/run/${name}.pid"
command="/usr/sbin/${name}"
load_rc_config $name
run_rc_command "$1"

View file

@ -33,7 +33,7 @@
struct mntopt {
const char *m_option; /* option name */
int m_inverse; /* if a negative option, e.g. "atime" */
int m_flag; /* bit to set, e.g. MNT_RDONLY */
long long m_flag; /* bit to set, e.g. MNT_RDONLY */
int m_altloc; /* 1 => set bit in altflags */
};
@ -55,6 +55,7 @@ struct mntopt {
#define MOPT_MULTILABEL { "multilabel", 0, MNT_MULTILABEL, 0 }
#define MOPT_ACLS { "acls", 0, MNT_ACLS, 0 }
#define MOPT_NFS4ACLS { "nfsv4acls", 0, MNT_NFS4ACLS, 0 }
#define MOPT_AUTOMOUNTED { "automounted",0, MNT_AUTOMOUNTED, 0 }
/* Control flags. */
#define MOPT_FORCE { "force", 0, MNT_FORCE, 0 }
@ -89,7 +90,8 @@ struct mntopt {
MOPT_NOCLUSTERW, \
MOPT_MULTILABEL, \
MOPT_ACLS, \
MOPT_NFS4ACLS
MOPT_NFS4ACLS, \
MOPT_AUTOMOUNTED
void getmntopts(const char *, const struct mntopt *, int *, int *);
void rmslashes(char *, char *);

View file

@ -114,6 +114,7 @@ static struct opt {
{ MNT_ACLS, "acls" },
{ MNT_NFS4ACLS, "nfsv4acls" },
{ MNT_GJOURNAL, "gjournal" },
{ MNT_AUTOMOUNTED, "automounted" },
{ 0, NULL }
};

View file

@ -7,6 +7,7 @@
MAN= acct.5 \
ar.5 \
a.out.5 \
autofs.5 \
bluetooth.device.conf.5 \
bluetooth.hosts.5 \
bluetooth.protocols.5 \

99
share/man/man5/autofs.5 Normal file
View file

@ -0,0 +1,99 @@
.\" Copyright (c) 2014 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This software was developed by Edward Tomasz Napierala under sponsorship
.\" from the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd July 14, 2014
.Dt AUTOFS 5
.Os
.Sh NAME
.Nm autofs
.Nd "automounter filesystem"
.Sh SYNOPSIS
To compile this driver into the kernel,
place the following line in the
kernel configuration file:
.Bd -ragged -offset indent
.Cd "options AUTOFS"
.Ed
.Pp
Alternatively, to load the driver as a
module at boot time, place the following line in
.Xr loader.conf 5 :
.Bd -literal -offset indent
autofs_load="YES"
.Ed
.Sh DESCRIPTION
The
.Nm
driver is the kernel component of the automounter infrastructure.
Its job is to pass mount requests to the
.Xr automountd 8
daemon, and pause the processes trying to access the automounted filesystem
until the mount is completed.
It is mounted by the
.Xr automount 8 .
.Sh OPTIONS
These options are available when
mounting
.Nm
file systems:
.Bl -tag -width indent
.It Cm master_options
Mount options for all filesystems specified in the map entry.
.It Cm master_prefix
Filesystem mountpoint prefix.
.El
.Sh EXAMPLES
To unmount all mounted
.Nm
filesystems:
.Pp
.Dl "umount -At autofs"
.Pp
To mount
.Nm
filesystems specified in
.Xr auto_master 5 :
.Pp
.Dl "automount"
.Sh SEE ALSO
.Xr auto_master 5 ,
.Xr automount 8 ,
.Xr automountd 8 ,
.Xr autounmountd 8
.Sh HISTORY
The
.Nm
driver first appeared in
.Fx 10.2 .
.Sh AUTHORS
The
.Nm
was developed by
.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org
under sponsorship from the FreeBSD Foundation.

View file

@ -1012,6 +1012,7 @@ options FFS #Fast filesystem
options NFSCLIENT #Network File System client
# The rest are optional:
options AUTOFS #Automounter filesystem
options CD9660 #ISO 9660 filesystem
options FDESCFS #File descriptor filesystem
options FUSE #FUSE support module

View file

@ -2627,6 +2627,9 @@ dev/xen/timer/timer.c optional xen | xenhvm
dev/xen/pvcpu/pvcpu.c optional xen | xenhvm
dev/xl/if_xl.c optional xl pci
dev/xl/xlphy.c optional xl pci
fs/autofs/autofs.c optional autofs
fs/autofs/autofs_vfsops.c optional autofs
fs/autofs/autofs_vnops.c optional autofs
fs/deadfs/dead_vnops.c standard
fs/devfs/devfs_devs.c standard
fs/devfs/devfs_dir.c standard
@ -3164,6 +3167,7 @@ libkern/strcmp.c standard
libkern/strcpy.c standard
libkern/strcspn.c standard
libkern/strdup.c standard
libkern/strndup.c standard
libkern/strlcat.c standard
libkern/strlcpy.c standard
libkern/strlen.c standard

View file

@ -221,6 +221,7 @@ INCLUDE_CONFIG_FILE opt_config.h
# time, since the corresponding lkms cannot work if there are any static
# dependencies. Unusability is enforced by hiding the defines for the
# options in a never-included header.
AUTOFS opt_dontuse.h
CD9660 opt_dontuse.h
EXT2FS opt_dontuse.h
FDESCFS opt_dontuse.h

637
sys/fs/autofs/autofs.c Normal file
View file

@ -0,0 +1,637 @@
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/*-
* Copyright (c) 1989, 1991, 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/dirent.h>
#include <sys/ioccom.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/refcount.h>
#include <sys/sx.h>
#include <sys/sysctl.h>
#include <sys/syscallsubr.h>
#include <sys/vnode.h>
#include <machine/atomic.h>
#include <vm/uma.h>
#include "autofs.h"
#include "autofs_ioctl.h"
MALLOC_DEFINE(M_AUTOFS, "autofs", "Automounter filesystem");
uma_zone_t autofs_request_zone;
uma_zone_t autofs_node_zone;
static int autofs_open(struct cdev *dev, int flags, int fmt,
struct thread *td);
static int autofs_close(struct cdev *dev, int flag, int fmt,
struct thread *td);
static int autofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg,
int mode, struct thread *td);
static struct cdevsw autofs_cdevsw = {
.d_version = D_VERSION,
.d_open = autofs_open,
.d_close = autofs_close,
.d_ioctl = autofs_ioctl,
.d_name = "autofs",
};
/*
* List of signals that can interrupt an autofs trigger. Might be a good
* idea to keep it synchronised with list in sys/fs/nfs/nfs_commonkrpc.c.
*/
int autofs_sig_set[] = {
SIGINT,
SIGTERM,
SIGHUP,
SIGKILL,
SIGQUIT
};
struct autofs_softc *sc;
SYSCTL_NODE(_vfs, OID_AUTO, autofs, CTLFLAG_RD, 0, "Automounter filesystem");
int autofs_debug = 1;
TUNABLE_INT("vfs.autofs.debug", &autofs_debug);
SYSCTL_INT(_vfs_autofs, OID_AUTO, debug, CTLFLAG_RWTUN,
&autofs_debug, 1, "Enable debug messages");
int autofs_mount_on_stat = 0;
TUNABLE_INT("vfs.autofs.mount_on_stat", &autofs_mount_on_stat);
SYSCTL_INT(_vfs_autofs, OID_AUTO, mount_on_stat, CTLFLAG_RWTUN,
&autofs_mount_on_stat, 0, "Trigger mount on stat(2) on mountpoint");
int autofs_timeout = 30;
TUNABLE_INT("vfs.autofs.timeout", &autofs_timeout);
SYSCTL_INT(_vfs_autofs, OID_AUTO, timeout, CTLFLAG_RWTUN,
&autofs_timeout, 30, "Number of seconds to wait for automountd(8)");
int autofs_cache = 600;
TUNABLE_INT("vfs.autofs.cache", &autofs_cache);
SYSCTL_INT(_vfs_autofs, OID_AUTO, cache, CTLFLAG_RWTUN,
&autofs_cache, 600, "Number of seconds to wait before reinvoking "
"automountd(8) for any given file or directory");
int autofs_retry_attempts = 3;
TUNABLE_INT("vfs.autofs.retry_attempts", &autofs_retry_attempts);
SYSCTL_INT(_vfs_autofs, OID_AUTO, retry_attempts, CTLFLAG_RWTUN,
&autofs_retry_attempts, 3, "Number of attempts before failing mount");
int autofs_retry_delay = 1;
TUNABLE_INT("vfs.autofs.retry_delay", &autofs_retry_delay);
SYSCTL_INT(_vfs_autofs, OID_AUTO, retry_delay, CTLFLAG_RWTUN,
&autofs_retry_delay, 1, "Number of seconds before retrying");
int autofs_interruptible = 1;
TUNABLE_INT("vfs.autofs.interruptible", &autofs_interruptible);
SYSCTL_INT(_vfs_autofs, OID_AUTO, interruptible, CTLFLAG_RWTUN,
&autofs_interruptible, 1, "Allow requests to be interrupted by signal");
int
autofs_init(struct vfsconf *vfsp)
{
int error;
sc = malloc(sizeof(*sc), M_AUTOFS, M_WAITOK | M_ZERO);
autofs_request_zone = uma_zcreate("autofs_request",
sizeof(struct autofs_request), NULL, NULL, NULL, NULL,
UMA_ALIGN_PTR, 0);
autofs_node_zone = uma_zcreate("autofs_node",
sizeof(struct autofs_node), NULL, NULL, NULL, NULL,
UMA_ALIGN_PTR, 0);
TAILQ_INIT(&sc->sc_requests);
cv_init(&sc->sc_cv, "autofscv");
sx_init(&sc->sc_lock, "autofslk");
error = make_dev_p(MAKEDEV_CHECKNAME, &sc->sc_cdev, &autofs_cdevsw,
NULL, UID_ROOT, GID_WHEEL, 0600, "autofs");
if (error != 0) {
AUTOFS_WARN("failed to create device node, error %d", error);
free(sc, M_AUTOFS);
return (error);
}
sc->sc_cdev->si_drv1 = sc;
return (0);
}
int
autofs_uninit(struct vfsconf *vfsp)
{
sx_xlock(&sc->sc_lock);
if (sc->sc_dev_opened) {
sx_xunlock(&sc->sc_lock);
return (EBUSY);
}
if (sc->sc_cdev != NULL)
destroy_dev(sc->sc_cdev);
uma_zdestroy(autofs_request_zone);
uma_zdestroy(autofs_node_zone);
sx_xunlock(&sc->sc_lock);
/*
* XXX: Race with open?
*/
free(sc, M_AUTOFS);
return (0);
}
bool
autofs_ignore_thread(const struct thread *td)
{
struct proc *p;
p = td->td_proc;
if (sc->sc_dev_opened == false)
return (false);
PROC_LOCK(p);
if (p->p_session->s_sid == sc->sc_dev_sid) {
PROC_UNLOCK(p);
return (true);
}
PROC_UNLOCK(p);
return (false);
}
static char *
autofs_path(struct autofs_node *anp)
{
struct autofs_mount *amp;
char *path, *tmp;
amp = anp->an_mount;
path = strdup("", M_AUTOFS);
for (; anp->an_parent != NULL; anp = anp->an_parent) {
tmp = malloc(strlen(anp->an_name) + strlen(path) + 2,
M_AUTOFS, M_WAITOK);
strcpy(tmp, anp->an_name);
strcat(tmp, "/");
strcat(tmp, path);
free(path, M_AUTOFS);
path = tmp;
}
tmp = malloc(strlen(amp->am_mountpoint) + strlen(path) + 2,
M_AUTOFS, M_WAITOK);
strcpy(tmp, amp->am_mountpoint);
strcat(tmp, "/");
strcat(tmp, path);
free(path, M_AUTOFS);
path = tmp;
return (path);
}
static void
autofs_callout(void *context)
{
struct autofs_request *ar;
struct autofs_softc *sc;
ar = context;
sc = ar->ar_mount->am_softc;
sx_xlock(&sc->sc_lock);
AUTOFS_WARN("request %d for %s timed out after %d seconds",
ar->ar_id, ar->ar_path, autofs_timeout);
/*
* XXX: EIO perhaps?
*/
ar->ar_error = ETIMEDOUT;
ar->ar_done = true;
ar->ar_in_progress = false;
cv_broadcast(&sc->sc_cv);
sx_xunlock(&sc->sc_lock);
}
bool
autofs_cached(struct autofs_node *anp, const char *component, int componentlen)
{
int error;
struct autofs_mount *amp;
amp = anp->an_mount;
AUTOFS_ASSERT_UNLOCKED(amp);
/*
* For top-level nodes we need to request automountd(8)
* assistance even if the node is marked as cached,
* but the requested subdirectory does not exist. This
* is necessary for wildcard indirect map keys to work.
*/
if (anp->an_parent == NULL && componentlen != 0) {
AUTOFS_LOCK(amp);
error = autofs_node_find(anp, component, componentlen, NULL);
AUTOFS_UNLOCK(amp);
if (error != 0)
return (false);
}
return (anp->an_cached);
}
static void
autofs_cache_callout(void *context)
{
struct autofs_node *anp;
anp = context;
anp->an_cached = false;
}
/*
* The set/restore sigmask functions are used to (temporarily) overwrite
* the thread td_sigmask during triggering.
*/
static void
autofs_set_sigmask(sigset_t *oldset)
{
sigset_t newset;
int i;
SIGFILLSET(newset);
/* Remove the autofs set of signals from newset */
PROC_LOCK(curproc);
mtx_lock(&curproc->p_sigacts->ps_mtx);
for (i = 0 ; i < sizeof(autofs_sig_set)/sizeof(int) ; i++) {
/*
* But make sure we leave the ones already masked
* by the process, i.e. remove the signal from the
* temporary signalmask only if it wasn't already
* in p_sigmask.
*/
if (!SIGISMEMBER(curthread->td_sigmask, autofs_sig_set[i]) &&
!SIGISMEMBER(curproc->p_sigacts->ps_sigignore,
autofs_sig_set[i])) {
SIGDELSET(newset, autofs_sig_set[i]);
}
}
mtx_unlock(&curproc->p_sigacts->ps_mtx);
kern_sigprocmask(curthread, SIG_SETMASK, &newset, oldset,
SIGPROCMASK_PROC_LOCKED);
PROC_UNLOCK(curproc);
}
static void
autofs_restore_sigmask(sigset_t *set)
{
kern_sigprocmask(curthread, SIG_SETMASK, set, NULL, 0);
}
static int
autofs_trigger_one(struct autofs_node *anp,
const char *component, int componentlen)
{
sigset_t oldset;
struct autofs_mount *amp;
struct autofs_softc *sc;
struct autofs_node *firstanp;
struct autofs_request *ar;
char *key, *path;
int error = 0, request_error, last;
amp = VFSTOAUTOFS(anp->an_vnode->v_mount);
sc = amp->am_softc;
sx_assert(&sc->sc_lock, SA_XLOCKED);
if (anp->an_parent == NULL) {
key = strndup(component, componentlen, M_AUTOFS);
} else {
for (firstanp = anp; firstanp->an_parent->an_parent != NULL;
firstanp = firstanp->an_parent)
continue;
key = strdup(firstanp->an_name, M_AUTOFS);
}
path = autofs_path(anp);
TAILQ_FOREACH(ar, &sc->sc_requests, ar_next) {
if (strcmp(ar->ar_path, path) != 0)
continue;
if (strcmp(ar->ar_key, key) != 0)
continue;
KASSERT(strcmp(ar->ar_from, amp->am_from) == 0,
("from changed; %s != %s", ar->ar_from, amp->am_from));
KASSERT(strcmp(ar->ar_prefix, amp->am_prefix) == 0,
("prefix changed; %s != %s",
ar->ar_prefix, amp->am_prefix));
KASSERT(strcmp(ar->ar_options, amp->am_options) == 0,
("options changed; %s != %s",
ar->ar_options, amp->am_options));
break;
}
if (ar != NULL) {
refcount_acquire(&ar->ar_refcount);
} else {
ar = uma_zalloc(autofs_request_zone, M_WAITOK | M_ZERO);
ar->ar_mount = amp;
ar->ar_id = atomic_fetchadd_int(&sc->sc_last_request_id, 1);
strlcpy(ar->ar_from, amp->am_from, sizeof(ar->ar_from));
strlcpy(ar->ar_path, path, sizeof(ar->ar_path));
strlcpy(ar->ar_prefix, amp->am_prefix, sizeof(ar->ar_prefix));
strlcpy(ar->ar_key, key, sizeof(ar->ar_key));
strlcpy(ar->ar_options,
amp->am_options, sizeof(ar->ar_options));
callout_init(&ar->ar_callout, 1);
callout_reset(&ar->ar_callout,
autofs_timeout * hz, autofs_callout, ar);
refcount_init(&ar->ar_refcount, 1);
TAILQ_INSERT_TAIL(&sc->sc_requests, ar, ar_next);
}
cv_broadcast(&sc->sc_cv);
while (ar->ar_done == false) {
if (autofs_interruptible != 0) {
autofs_set_sigmask(&oldset);
error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock);
autofs_restore_sigmask(&oldset);
if (error != 0) {
/*
* XXX: For some reson this returns -1
* instead of EINTR, wtf?!
*/
error = EINTR;
AUTOFS_WARN("cv_wait_sig for %s failed "
"with error %d", ar->ar_path, error);
break;
}
} else {
cv_wait(&sc->sc_cv, &sc->sc_lock);
}
}
request_error = ar->ar_error;
if (request_error != 0) {
AUTOFS_WARN("request for %s completed with error %d",
ar->ar_path, request_error);
}
last = refcount_release(&ar->ar_refcount);
if (last) {
TAILQ_REMOVE(&sc->sc_requests, ar, ar_next);
/*
* XXX: Is it safe?
*/
sx_xunlock(&sc->sc_lock);
callout_drain(&ar->ar_callout);
sx_xlock(&sc->sc_lock);
uma_zfree(autofs_request_zone, ar);
}
/*
* Note that we do not do negative caching on purpose. This
* way the user can retry access at any time, e.g. after fixing
* the failure reason, without waiting for cache timer to expire.
*/
if (error == 0 && request_error == 0 && autofs_cache > 0) {
anp->an_cached = true;
callout_reset(&anp->an_callout, autofs_cache * hz,
autofs_cache_callout, anp);
}
free(key, M_AUTOFS);
free(path, M_AUTOFS);
if (error != 0)
return (error);
return (request_error);
}
/*
* Send request to automountd(8) and wait for completion.
*/
int
autofs_trigger(struct autofs_node *anp,
const char *component, int componentlen)
{
int error;
for (;;) {
error = autofs_trigger_one(anp, component, componentlen);
if (error == 0) {
anp->an_retries = 0;
return (0);
}
if (error == EINTR) {
AUTOFS_DEBUG("trigger interrupted by signal, "
"not retrying");
anp->an_retries = 0;
return (error);
}
anp->an_retries++;
if (anp->an_retries >= autofs_retry_attempts) {
AUTOFS_DEBUG("trigger failed %d times; returning "
"error %d", anp->an_retries, error);
anp->an_retries = 0;
return (error);
}
AUTOFS_DEBUG("trigger failed with error %d; will retry in "
"%d seconds, %d attempts left", error, autofs_retry_delay,
autofs_retry_attempts - anp->an_retries);
sx_xunlock(&sc->sc_lock);
pause("autofs_retry", autofs_retry_delay * hz);
sx_xlock(&sc->sc_lock);
}
}
static int
autofs_ioctl_request(struct autofs_softc *sc, struct autofs_daemon_request *adr)
{
struct autofs_request *ar;
int error;
sx_xlock(&sc->sc_lock);
for (;;) {
TAILQ_FOREACH(ar, &sc->sc_requests, ar_next) {
if (ar->ar_done)
continue;
if (ar->ar_in_progress)
continue;
break;
}
if (ar != NULL)
break;
error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock);
if (error != 0) {
/*
* XXX: For some reson this returns -1 instead
* of EINTR, wtf?!
*/
error = EINTR;
sx_xunlock(&sc->sc_lock);
AUTOFS_DEBUG("failed with error %d", error);
return (error);
}
}
ar->ar_in_progress = true;
sx_xunlock(&sc->sc_lock);
adr->adr_id = ar->ar_id;
strlcpy(adr->adr_from, ar->ar_from, sizeof(adr->adr_from));
strlcpy(adr->adr_path, ar->ar_path, sizeof(adr->adr_path));
strlcpy(adr->adr_prefix, ar->ar_prefix, sizeof(adr->adr_prefix));
strlcpy(adr->adr_key, ar->ar_key, sizeof(adr->adr_key));
strlcpy(adr->adr_options, ar->ar_options, sizeof(adr->adr_options));
PROC_LOCK(curproc);
sc->sc_dev_sid = curproc->p_session->s_sid;
PROC_UNLOCK(curproc);
return (0);
}
static int
autofs_ioctl_done(struct autofs_softc *sc, struct autofs_daemon_done *add)
{
struct autofs_request *ar;
sx_xlock(&sc->sc_lock);
TAILQ_FOREACH(ar, &sc->sc_requests, ar_next) {
if (ar->ar_id == add->add_id)
break;
}
if (ar == NULL) {
sx_xunlock(&sc->sc_lock);
AUTOFS_DEBUG("id %d not found", add->add_id);
return (ESRCH);
}
ar->ar_error = add->add_error;
ar->ar_done = true;
ar->ar_in_progress = false;
cv_broadcast(&sc->sc_cv);
sx_xunlock(&sc->sc_lock);
return (0);
}
static int
autofs_open(struct cdev *dev, int flags, int fmt, struct thread *td)
{
sx_xlock(&sc->sc_lock);
if (sc->sc_dev_opened) {
sx_xunlock(&sc->sc_lock);
return (EBUSY);
}
sc->sc_dev_opened = true;
sx_xunlock(&sc->sc_lock);
return (0);
}
static int
autofs_close(struct cdev *dev, int flag, int fmt, struct thread *td)
{
sx_xlock(&sc->sc_lock);
KASSERT(sc->sc_dev_opened, ("not opened?"));
sc->sc_dev_opened = false;
sx_xunlock(&sc->sc_lock);
return (0);
}
static int
autofs_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode,
struct thread *td)
{
KASSERT(sc->sc_dev_opened, ("not opened?"));
switch (cmd) {
case AUTOFSREQUEST:
return (autofs_ioctl_request(sc,
(struct autofs_daemon_request *)arg));
case AUTOFSDONE:
return (autofs_ioctl_done(sc,
(struct autofs_daemon_done *)arg));
default:
AUTOFS_DEBUG("invalid cmd %lx", cmd);
return (EINVAL);
}
}

141
sys/fs/autofs/autofs.h Normal file
View file

@ -0,0 +1,141 @@
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef AUTOFS_H
#define AUTOFS_H
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#define VFSTOAUTOFS(mp) ((struct autofs_mount *)((mp)->mnt_data))
MALLOC_DECLARE(M_AUTOFS);
extern uma_zone_t autofs_request_zone;
extern uma_zone_t autofs_node_zone;
extern int autofs_debug;
extern int autofs_mount_on_stat;
#define AUTOFS_DEBUG(X, ...) \
if (autofs_debug > 1) { \
printf("%s: " X "\n", __func__, ## __VA_ARGS__);\
} while (0)
#define AUTOFS_WARN(X, ...) \
if (autofs_debug > 0) { \
printf("WARNING: %s: " X "\n", \
__func__, ## __VA_ARGS__); \
} while (0)
#define AUTOFS_LOCK(X) sx_xlock(&X->am_lock)
#define AUTOFS_UNLOCK(X) sx_xunlock(&X->am_lock)
#define AUTOFS_ASSERT_LOCKED(X) sx_assert(&X->am_lock, SA_XLOCKED)
#define AUTOFS_ASSERT_UNLOCKED(X) sx_assert(&X->am_lock, SA_UNLOCKED)
struct autofs_node {
TAILQ_ENTRY(autofs_node) an_next;
char *an_name;
int an_fileno;
struct autofs_node *an_parent;
TAILQ_HEAD(, autofs_node) an_children;
struct autofs_mount *an_mount;
struct vnode *an_vnode;
struct sx an_vnode_lock;
bool an_cached;
struct callout an_callout;
int an_retries;
struct timespec an_ctime;
};
struct autofs_mount {
TAILQ_ENTRY(autofs_mount) am_next;
struct autofs_softc *am_softc;
struct autofs_node *am_root;
struct mount *am_mp;
struct sx am_lock;
char am_from[MAXPATHLEN];
char am_mountpoint[MAXPATHLEN];
char am_options[MAXPATHLEN];
char am_prefix[MAXPATHLEN];
int am_last_fileno;
};
struct autofs_request {
TAILQ_ENTRY(autofs_request) ar_next;
struct autofs_mount *ar_mount;
int ar_id;
bool ar_done;
int ar_error;
bool ar_in_progress;
char ar_from[MAXPATHLEN];
char ar_path[MAXPATHLEN];
char ar_prefix[MAXPATHLEN];
char ar_key[MAXPATHLEN];
char ar_options[MAXPATHLEN];
struct callout ar_callout;
volatile u_int ar_refcount;
};
struct autofs_softc {
device_t sc_dev;
struct cdev *sc_cdev;
struct cv sc_cv;
struct sx sc_lock;
TAILQ_HEAD(, autofs_request) sc_requests;
bool sc_dev_opened;
pid_t sc_dev_sid;
int sc_last_request_id;
};
/*
* Limits and constants
*/
#define AUTOFS_NAMELEN 24
#define AUTOFS_FSNAMELEN 16 /* equal to MFSNAMELEN */
#define AUTOFS_DELEN (8 + AUTOFS_NAMELEN)
int autofs_init(struct vfsconf *vfsp);
int autofs_uninit(struct vfsconf *vfsp);
int autofs_trigger(struct autofs_node *anp, const char *component,
int componentlen);
bool autofs_cached(struct autofs_node *anp, const char *component,
int componentlen);
bool autofs_ignore_thread(const struct thread *td);
int autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp,
const char *name, int namelen, struct autofs_node **anpp);
int autofs_node_find(struct autofs_node *parent,
const char *name, int namelen, struct autofs_node **anpp);
void autofs_node_delete(struct autofs_node *anp);
int autofs_node_vn(struct autofs_node *anp, struct mount *mp,
struct vnode **vpp);
#endif /* !AUTOFS_H */

View file

@ -0,0 +1,89 @@
/*-
* Copyright (c) 2013 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef AUTOFS_IOCTL_H
#define AUTOFS_IOCTL_H
#define AUTOFS_PATH "/dev/autofs"
struct autofs_daemon_request {
/*
* Request identifier.
*/
int adr_id;
/*
* The "from" field, containing map name. For example,
* when accessing '/net/192.168.1.3/tank/vm/', that would
* be '-hosts'.
*/
char adr_from[MAXPATHLEN];
/*
* Full path to the node being looked up; for requests that result
* in actual mount it is the full mount path.
*/
char adr_path[MAXPATHLEN];
/*
* Prefix, which is basically the mountpoint from auto_master(5).
* In example above that would be "/net"; for direct maps it is "/".
*/
char adr_prefix[MAXPATHLEN];
/*
* Map key, also used as command argument for dynamic maps; in example
* above that would be '192.168.1.3'.
*/
char adr_key[MAXPATHLEN];
/*
* Mount options from auto_master(5).
*/
char adr_options[MAXPATHLEN];
};
struct autofs_daemon_done {
/*
* Identifier, copied from adr_id.
*/
int add_id;
/*
* Error number, possibly returned to userland.
*/
int add_error;
};
#define AUTOFSREQUEST _IOR('I', 0x01, struct autofs_daemon_request)
#define AUTOFSDONE _IOW('I', 0x02, struct autofs_daemon_done)
#endif /* !AUTOFS_IOCTL_H */

View file

@ -0,0 +1,209 @@
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/condvar.h>
#include <sys/ioccom.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/mount.h>
#include <sys/sx.h>
#include <sys/vnode.h>
#include "autofs.h"
static const char *autofs_opts[] = {
"from", "master_options", "master_prefix", NULL
};
extern struct autofs_softc *sc;
static int
autofs_mount(struct mount *mp)
{
struct autofs_mount *amp;
char *from, *fspath, *options, *prefix;
int error;
if (vfs_filteropt(mp->mnt_optnew, autofs_opts))
return (EINVAL);
if (mp->mnt_flag & MNT_UPDATE)
return (0);
if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL))
return (EINVAL);
if (vfs_getopt(mp->mnt_optnew, "fspath", (void **)&fspath, NULL))
return (EINVAL);
if (vfs_getopt(mp->mnt_optnew, "master_options", (void **)&options, NULL))
return (EINVAL);
if (vfs_getopt(mp->mnt_optnew, "master_prefix", (void **)&prefix, NULL))
return (EINVAL);
amp = malloc(sizeof(*amp), M_AUTOFS, M_WAITOK | M_ZERO);
mp->mnt_data = amp;
amp->am_mp = mp;
amp->am_softc = sc;
strlcpy(amp->am_from, from, sizeof(amp->am_from));
strlcpy(amp->am_mountpoint, fspath, sizeof(amp->am_mountpoint));
strlcpy(amp->am_options, options, sizeof(amp->am_options));
strlcpy(amp->am_prefix, prefix, sizeof(amp->am_prefix));
sx_init(&amp->am_lock, "autofslk");
amp->am_last_fileno = 1;
vfs_getnewfsid(mp);
AUTOFS_LOCK(amp);
error = autofs_node_new(NULL, amp, ".", -1, &amp->am_root);
if (error != 0) {
AUTOFS_UNLOCK(amp);
free(amp, M_AUTOFS);
return (error);
}
AUTOFS_UNLOCK(amp);
vfs_mountedfrom(mp, from);
return (0);
}
static int
autofs_unmount(struct mount *mp, int mntflags)
{
struct autofs_mount *amp;
struct autofs_node *anp;
struct autofs_request *ar;
int error, flags;
bool found;
amp = VFSTOAUTOFS(mp);
flags = 0;
if (mntflags & MNT_FORCE)
flags |= FORCECLOSE;
error = vflush(mp, 0, flags, curthread);
if (error != 0) {
AUTOFS_WARN("vflush failed with error %d", error);
return (error);
}
/*
* All vnodes are gone, and new one will not appear - so,
* no new triggerings. We can iterate over outstanding
* autofs_requests and terminate them.
*/
for (;;) {
found = false;
sx_xlock(&sc->sc_lock);
TAILQ_FOREACH(ar, &sc->sc_requests, ar_next) {
if (ar->ar_mount != amp)
continue;
ar->ar_error = ENXIO;
ar->ar_done = true;
ar->ar_in_progress = false;
found = true;
}
sx_xunlock(&sc->sc_lock);
if (found == false)
break;
cv_broadcast(&sc->sc_cv);
pause("autofs_umount", 1);
}
AUTOFS_LOCK(amp);
/*
* Not terribly efficient, but at least not recursive.
*/
while (!TAILQ_EMPTY(&amp->am_root->an_children)) {
anp = TAILQ_FIRST(&amp->am_root->an_children);
while (!TAILQ_EMPTY(&anp->an_children))
anp = TAILQ_FIRST(&anp->an_children);
autofs_node_delete(anp);
}
autofs_node_delete(amp->am_root);
mp->mnt_data = NULL;
AUTOFS_UNLOCK(amp);
sx_destroy(&amp->am_lock);
free(amp, M_AUTOFS);
return (0);
}
static int
autofs_root(struct mount *mp, int flags, struct vnode **vpp)
{
struct autofs_mount *amp;
int error;
amp = VFSTOAUTOFS(mp);
error = autofs_node_vn(amp->am_root, mp, vpp);
return (error);
}
static int
autofs_statfs(struct mount *mp, struct statfs *sbp)
{
sbp->f_bsize = 512;
sbp->f_iosize = 0;
sbp->f_blocks = 0;
sbp->f_bfree = 0;
sbp->f_bavail = 0;
sbp->f_files = 0;
sbp->f_ffree = 0;
return (0);
}
static struct vfsops autofs_vfsops = {
.vfs_fhtovp = NULL, /* XXX */
.vfs_mount = autofs_mount,
.vfs_unmount = autofs_unmount,
.vfs_root = autofs_root,
.vfs_statfs = autofs_statfs,
.vfs_init = autofs_init,
.vfs_uninit = autofs_uninit,
};
VFS_SET(autofs_vfsops, autofs, VFCF_SYNTHETIC | VFCF_NETWORK);
MODULE_VERSION(autofs, 1);

View file

@ -0,0 +1,646 @@
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/condvar.h>
#include <sys/dirent.h>
#include <sys/fcntl.h>
#include <sys/lock.h>
#include <sys/mount.h>
#include <sys/mutex.h>
#include <sys/namei.h>
#include <sys/signalvar.h>
#include <sys/systm.h>
#include <sys/vnode.h>
#include <machine/atomic.h>
#include <vm/uma.h>
#include "autofs.h"
static int autofs_trigger_vn(struct vnode *vp, const char *path,
int pathlen, struct vnode **newvp);
static int
autofs_access(struct vop_access_args *ap)
{
/*
* Nothing to do here; the only kind of access control
* needed is in autofs_mkdir().
*/
return (0);
}
static int
autofs_getattr(struct vop_getattr_args *ap)
{
struct vnode *vp, *newvp;
struct autofs_node *anp;
struct mount *mp;
struct vattr *vap;
int error;
vp = ap->a_vp;
anp = vp->v_data;
mp = vp->v_mount;
vap = ap->a_vap;
KASSERT(ap->a_vp->v_type == VDIR, ("!VDIR"));
/*
* The reason we must do this is that some tree-walking software,
* namely fts(3), assumes that stat(".") results will not change
* between chdir("subdir") and chdir(".."), and fails with ENOENT
* otherwise.
*/
if (autofs_mount_on_stat && autofs_cached(anp, NULL, 0) == false &&
autofs_ignore_thread(curthread) == false) {
error = autofs_trigger_vn(vp, "", 0, &newvp);
if (error != 0)
return (error);
if (newvp != NULL) {
error = VOP_GETATTR(newvp, ap->a_vap,
ap->a_cred);
vput(newvp);
return (error);
}
}
vap->va_type = VDIR;
vap->va_mode = 0755;
vap->va_nlink = 3; /* XXX */
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_rdev = NODEV;
vap->va_fsid = mp->mnt_stat.f_fsid.val[0];
vap->va_fileid = anp->an_fileno;
vap->va_size = 512; /* XXX */
vap->va_blocksize = 512;
vap->va_mtime = anp->an_ctime;
vap->va_atime = anp->an_ctime;
vap->va_ctime = anp->an_ctime;
vap->va_birthtime = anp->an_ctime;
vap->va_gen = 0;
vap->va_flags = 0;
vap->va_rdev = 0;
vap->va_bytes = 512; /* XXX */
vap->va_filerev = 0;
vap->va_spare = 0;
return (0);
}
/*
* Unlock the vnode, request automountd(8) action, and then lock it back.
* If anything got mounted on top of the vnode, return the new filesystem's
* root vnode in 'newvp', locked.
*/
static int
autofs_trigger_vn(struct vnode *vp, const char *path, int pathlen,
struct vnode **newvp)
{
struct autofs_node *anp;
struct autofs_mount *amp;
struct autofs_softc *sc;
int error, lock_flags;
anp = vp->v_data;
amp = VFSTOAUTOFS(vp->v_mount);
sc = amp->am_softc;
/*
* Release the vnode lock, so that other operations, in partcular
* mounting a filesystem on top of it, can proceed. Increase use
* count, to prevent the vnode from being deallocated and to prevent
* filesystem from being unmounted.
*/
lock_flags = VOP_ISLOCKED(vp);
vref(vp);
VOP_UNLOCK(vp, 0);
sx_xlock(&sc->sc_lock);
/*
* XXX: Workaround for mounting the same thing multiple times; revisit.
*/
if (vp->v_mountedhere != NULL) {
error = 0;
goto mounted;
}
error = autofs_trigger(anp, path, pathlen);
mounted:
sx_xunlock(&sc->sc_lock);
vn_lock(vp, lock_flags | LK_RETRY);
vunref(vp);
if ((vp->v_iflag & VI_DOOMED) != 0) {
AUTOFS_DEBUG("VI_DOOMED");
return (ENOENT);
}
if (error != 0)
return (error);
if (vp->v_mountedhere == NULL) {
*newvp = NULL;
return (0);
} else {
/*
* If the operation that succeeded was mount, then mark
* the node as non-cached. Otherwise, if someone unmounts
* the filesystem before the cache times out, we will fail
* to trigger.
*/
anp->an_cached = false;
}
error = VFS_ROOT(vp->v_mountedhere, lock_flags, newvp);
if (error != 0) {
AUTOFS_WARN("VFS_ROOT() failed with error %d", error);
return (error);
}
return (0);
}
static int
autofs_lookup(struct vop_lookup_args *ap)
{
struct vnode *dvp, *newvp, **vpp;
struct mount *mp;
struct autofs_mount *amp;
struct autofs_node *anp, *child;
struct componentname *cnp;
int error, lock_flags;
dvp = ap->a_dvp;
vpp = ap->a_vpp;
mp = dvp->v_mount;
amp = VFSTOAUTOFS(mp);
anp = dvp->v_data;
cnp = ap->a_cnp;
if (cnp->cn_flags & ISDOTDOT) {
KASSERT(anp->an_parent != NULL, ("NULL parent"));
/*
* Note that in this case, dvp is the child vnode, and we are
* looking up the parent vnode - exactly reverse from normal
* operation. To preserve lock order, we unlock the child
* (dvp), obtain the lock on parent (*vpp) in autofs_node_vn(),
* then relock the child. We use vhold()/vdrop() to prevent
* dvp from being freed in the meantime.
*/
lock_flags = VOP_ISLOCKED(dvp);
vhold(dvp);
VOP_UNLOCK(dvp, 0);
error = autofs_node_vn(anp->an_parent, mp, vpp);
if (error != 0) {
AUTOFS_WARN("autofs_node_vn() failed with error %d",
error);
}
vn_lock(dvp, lock_flags | LK_RETRY);
vdrop(dvp);
return (error);
}
if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
vref(dvp);
*vpp = dvp;
return (0);
}
if (autofs_cached(anp, cnp->cn_nameptr, cnp->cn_namelen) == false &&
autofs_ignore_thread(cnp->cn_thread) == false) {
error = autofs_trigger_vn(dvp,
cnp->cn_nameptr, cnp->cn_namelen, &newvp);
if (error != 0)
return (error);
if (newvp != NULL) {
error = VOP_LOOKUP(newvp, ap->a_vpp, ap->a_cnp);
/*
* Instead of figuring out whether our vnode should
* be locked or not given the error and cnp flags,
* just "copy" the lock status from vnode returned
* by mounted filesystem's VOP_LOOKUP(). Get rid
* of that new vnode afterwards.
*/
lock_flags = VOP_ISLOCKED(newvp);
if (lock_flags == 0) {
VOP_UNLOCK(dvp, 0);
vrele(newvp);
} else {
vput(newvp);
}
return (error);
}
}
if (cnp->cn_nameiop == RENAME)
return (EOPNOTSUPP);
AUTOFS_LOCK(amp);
error = autofs_node_find(anp, cnp->cn_nameptr, cnp->cn_namelen, &child);
if (error != 0) {
if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE) {
AUTOFS_UNLOCK(amp);
return (EJUSTRETURN);
}
AUTOFS_UNLOCK(amp);
return (ENOENT);
}
/*
* XXX: Dropping the node here is ok, because we never remove nodes.
*/
AUTOFS_UNLOCK(amp);
error = autofs_node_vn(child, mp, vpp);
if (error != 0) {
if ((cnp->cn_flags & ISLASTCN) && cnp->cn_nameiop == CREATE)
return (EJUSTRETURN);
return (error);
}
return (0);
}
static int
autofs_mkdir(struct vop_mkdir_args *ap)
{
struct vnode *vp;
struct autofs_node *anp;
struct autofs_mount *amp;
struct autofs_node *child;
int error;
vp = ap->a_dvp;
anp = vp->v_data;
amp = VFSTOAUTOFS(vp->v_mount);
/*
* Do not allow mkdir() if the calling thread is not
* automountd(8) descendant.
*/
if (autofs_ignore_thread(curthread) == false)
return (EPERM);
AUTOFS_LOCK(amp);
error = autofs_node_new(anp, amp, ap->a_cnp->cn_nameptr,
ap->a_cnp->cn_namelen, &child);
if (error != 0) {
AUTOFS_UNLOCK(amp);
return (error);
}
AUTOFS_UNLOCK(amp);
error = autofs_node_vn(child, vp->v_mount, ap->a_vpp);
return (error);
}
static int
autofs_readdir_one(struct uio *uio, const char *name, int fileno)
{
struct dirent dirent;
int error, i;
memset(&dirent, 0, sizeof(dirent));
dirent.d_type = DT_DIR;
dirent.d_reclen = AUTOFS_DELEN;
dirent.d_fileno = fileno;
/* PFS_DELEN was picked to fit PFS_NAMLEN */
for (i = 0; i < AUTOFS_NAMELEN - 1 && name[i] != '\0'; ++i)
dirent.d_name[i] = name[i];
dirent.d_name[i] = 0;
dirent.d_namlen = i;
error = uiomove(&dirent, AUTOFS_DELEN, uio);
return (error);
}
static int
autofs_readdir(struct vop_readdir_args *ap)
{
struct vnode *vp, *newvp;
struct autofs_mount *amp;
struct autofs_node *anp, *child;
struct uio *uio;
off_t offset;
int error, i, resid;
vp = ap->a_vp;
amp = VFSTOAUTOFS(vp->v_mount);
anp = vp->v_data;
uio = ap->a_uio;
KASSERT(vp->v_type == VDIR, ("!VDIR"));
if (autofs_cached(anp, NULL, 0) == false &&
autofs_ignore_thread(curthread) == false) {
error = autofs_trigger_vn(vp, "", 0, &newvp);
if (error != 0)
return (error);
if (newvp != NULL) {
error = VOP_READDIR(newvp, ap->a_uio, ap->a_cred,
ap->a_eofflag, ap->a_ncookies, ap->a_cookies);
vput(newvp);
return (error);
}
}
/* only allow reading entire entries */
offset = uio->uio_offset;
resid = uio->uio_resid;
if (offset < 0 || offset % AUTOFS_DELEN != 0 ||
(resid && resid < AUTOFS_DELEN))
return (EINVAL);
if (resid == 0)
return (0);
if (ap->a_eofflag != NULL)
*ap->a_eofflag = TRUE;
if (offset == 0 && resid >= AUTOFS_DELEN) {
error = autofs_readdir_one(uio, ".", anp->an_fileno);
if (error != 0)
return (error);
offset += AUTOFS_DELEN;
resid -= AUTOFS_DELEN;
}
if (offset == AUTOFS_DELEN && resid >= AUTOFS_DELEN) {
if (anp->an_parent == NULL) {
/*
* XXX: Right?
*/
error = autofs_readdir_one(uio, "..", anp->an_fileno);
} else {
error = autofs_readdir_one(uio, "..",
anp->an_parent->an_fileno);
}
if (error != 0)
return (error);
offset += AUTOFS_DELEN;
resid -= AUTOFS_DELEN;
}
i = 2; /* Account for "." and "..". */
AUTOFS_LOCK(amp);
TAILQ_FOREACH(child, &anp->an_children, an_next) {
if (resid < AUTOFS_DELEN) {
if (ap->a_eofflag != NULL)
*ap->a_eofflag = 0;
break;
}
/*
* Skip entries returned by previous call to getdents().
*/
i++;
if (i * AUTOFS_DELEN <= offset)
continue;
error = autofs_readdir_one(uio, child->an_name,
child->an_fileno);
if (error != 0) {
AUTOFS_UNLOCK(amp);
return (error);
}
offset += AUTOFS_DELEN;
resid -= AUTOFS_DELEN;
}
AUTOFS_UNLOCK(amp);
return (0);
}
static int
autofs_reclaim(struct vop_reclaim_args *ap)
{
struct vnode *vp = ap->a_vp;
struct autofs_node *anp = vp->v_data;
vp = ap->a_vp;
anp = vp->v_data;
/*
* We do not free autofs_node here; instead we are
* destroying them in autofs_node_delete().
*/
sx_xlock(&anp->an_vnode_lock);
anp->an_vnode = NULL;
vp->v_data = NULL;
sx_xunlock(&anp->an_vnode_lock);
return (0);
}
struct vop_vector autofs_vnodeops = {
.vop_default = &default_vnodeops,
.vop_access = autofs_access,
.vop_lookup = autofs_lookup,
.vop_create = VOP_EOPNOTSUPP,
.vop_getattr = autofs_getattr,
.vop_link = VOP_EOPNOTSUPP,
.vop_mkdir = autofs_mkdir,
.vop_mknod = VOP_EOPNOTSUPP,
.vop_read = VOP_EOPNOTSUPP,
.vop_readdir = autofs_readdir,
.vop_remove = VOP_EOPNOTSUPP,
.vop_rename = VOP_EOPNOTSUPP,
.vop_rmdir = VOP_EOPNOTSUPP,
.vop_setattr = VOP_EOPNOTSUPP,
.vop_symlink = VOP_EOPNOTSUPP,
.vop_write = VOP_EOPNOTSUPP,
.vop_reclaim = autofs_reclaim,
};
int
autofs_node_new(struct autofs_node *parent, struct autofs_mount *amp,
const char *name, int namelen, struct autofs_node **anpp)
{
struct autofs_node *anp;
if (parent != NULL)
AUTOFS_ASSERT_LOCKED(parent->an_mount);
anp = uma_zalloc(autofs_node_zone, M_WAITOK | M_ZERO);
if (namelen >= 0)
anp->an_name = strndup(name, namelen, M_AUTOFS);
else
anp->an_name = strdup(name, M_AUTOFS);
anp->an_fileno = atomic_fetchadd_int(&amp->am_last_fileno, 1);
callout_init(&anp->an_callout, 1);
/*
* The reason for SX_NOWITNESS here is that witness(4)
* cannot tell vnodes apart, so the following perfectly
* valid lock order...
*
* vnode lock A -> autofsvlk B -> vnode lock B
*
* ... gets reported as a LOR.
*/
sx_init_flags(&anp->an_vnode_lock, "autofsvlk", SX_NOWITNESS);
getnanotime(&anp->an_ctime);
anp->an_parent = parent;
anp->an_mount = amp;
if (parent != NULL)
TAILQ_INSERT_TAIL(&parent->an_children, anp, an_next);
TAILQ_INIT(&anp->an_children);
*anpp = anp;
return (0);
}
int
autofs_node_find(struct autofs_node *parent, const char *name,
int namelen, struct autofs_node **anpp)
{
struct autofs_node *anp;
AUTOFS_ASSERT_LOCKED(parent->an_mount);
TAILQ_FOREACH(anp, &parent->an_children, an_next) {
if (namelen >= 0) {
if (strncmp(anp->an_name, name, namelen) != 0)
continue;
} else {
if (strcmp(anp->an_name, name) != 0)
continue;
}
if (anpp != NULL)
*anpp = anp;
return (0);
}
return (ENOENT);
}
void
autofs_node_delete(struct autofs_node *anp)
{
struct autofs_node *parent;
AUTOFS_ASSERT_LOCKED(anp->an_mount);
KASSERT(TAILQ_EMPTY(&anp->an_children), ("have children"));
callout_drain(&anp->an_callout);
parent = anp->an_parent;
if (parent != NULL)
TAILQ_REMOVE(&parent->an_children, anp, an_next);
sx_destroy(&anp->an_vnode_lock);
free(anp->an_name, M_AUTOFS);
uma_zfree(autofs_node_zone, anp);
}
int
autofs_node_vn(struct autofs_node *anp, struct mount *mp, struct vnode **vpp)
{
struct vnode *vp;
int error;
AUTOFS_ASSERT_UNLOCKED(anp->an_mount);
sx_xlock(&anp->an_vnode_lock);
vp = anp->an_vnode;
if (vp != NULL) {
error = vget(vp, LK_EXCLUSIVE | LK_RETRY, curthread);
if (error != 0) {
AUTOFS_WARN("vget failed with error %d", error);
sx_xunlock(&anp->an_vnode_lock);
return (error);
}
if (vp->v_iflag & VI_DOOMED) {
/*
* We got forcibly unmounted.
*/
AUTOFS_DEBUG("doomed vnode");
sx_xunlock(&anp->an_vnode_lock);
vput(vp);
return (ENOENT);
}
*vpp = vp;
sx_xunlock(&anp->an_vnode_lock);
return (0);
}
error = getnewvnode("autofs", mp, &autofs_vnodeops, &vp);
if (error != 0) {
sx_xunlock(&anp->an_vnode_lock);
return (error);
}
error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
if (error != 0) {
sx_xunlock(&anp->an_vnode_lock);
vdrop(vp);
return (error);
}
vp->v_type = VDIR;
if (anp->an_parent == NULL)
vp->v_vflag |= VV_ROOT;
vp->v_data = anp;
error = insmntque(vp, mp);
if (error != 0) {
AUTOFS_WARN("insmntque() failed with error %d", error);
sx_xunlock(&anp->an_vnode_lock);
return (error);
}
KASSERT(anp->an_vnode == NULL, ("lost race"));
anp->an_vnode = vp;
sx_xunlock(&anp->an_vnode_lock);
*vpp = vp;
return (0);
}

View file

@ -649,6 +649,10 @@ vfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions)
fsflags |= MNT_SYNCHRONOUS;
else if (strcmp(opt->name, "union") == 0)
fsflags |= MNT_UNION;
else if (strcmp(opt->name, "automounted") == 0) {
fsflags |= MNT_AUTOMOUNTED;
vfs_freeopt(optlist, opt);
}
}
/*

51
sys/libkern/strndup.c Normal file
View file

@ -0,0 +1,51 @@
/*-
* Copyright (c) 2003 Networks Associates Technology, Inc.
* All rights reserved.
*
* This software was developed for the FreeBSD Project by Network
* Associates Laboratories, the Security Research Division of Network
* Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
* ("CBOSS"), as part of the DARPA CHATS research program.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
#include <sys/malloc.h>
char *
strndup(const char *string, size_t maxlen, struct malloc_type *type)
{
size_t len;
char *copy;
len = strnlen(string, maxlen) + 1;
copy = malloc(len, type, M_WAITOK);
bcopy(string, copy, len);
copy[len - 1] = '\0';
return (copy);
}

View file

@ -48,6 +48,7 @@ SUBDIR= \
ata \
ath \
ath_pci \
autofs \
${_auxio} \
${_bce} \
bfe \

View file

@ -0,0 +1,11 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../fs/autofs
KMOD= autofs
SRCS= vnode_if.h \
autofs.c \
autofs_vnops.c \
autofs_vfsops.c
.include <bsd.kmod.mk>

View file

@ -117,6 +117,7 @@ int strcmp(const char *, const char *);
char *strcpy(char * __restrict, const char * __restrict);
size_t strcspn(const char * __restrict, const char * __restrict) __pure;
char *strdup(const char *__restrict, struct malloc_type *);
char *strndup(const char *__restrict, size_t, struct malloc_type *);
size_t strlcat(char *, const char *, size_t);
size_t strlcpy(char *, const char *, size_t);
size_t strlen(const char *);

View file

@ -260,6 +260,7 @@ void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *);
#define MNT_NOCLUSTERR 0x0000000040000000ULL /* disable cluster read */
#define MNT_NOCLUSTERW 0x0000000080000000ULL /* disable cluster write */
#define MNT_SUJ 0x0000000100000000ULL /* using journaled soft updates */
#define MNT_AUTOMOUNTED 0x0000000200000000ULL /* mounted by automountd(8) */
/*
* NFS export related mount flags.
@ -296,7 +297,7 @@ void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *);
MNT_NOCLUSTERW | MNT_SUIDDIR | MNT_SOFTDEP | \
MNT_IGNORE | MNT_EXPUBLIC | MNT_NOSYMFOLLOW | \
MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS | \
MNT_NFS4ACLS)
MNT_NFS4ACLS | MNT_AUTOMOUNTED)
/* Mask of flags that can be updated. */
#define MNT_UPDATEMASK (MNT_NOSUID | MNT_NOEXEC | \
@ -304,7 +305,8 @@ void __mnt_vnode_markerfree_active(struct vnode **mvp, struct mount *);
MNT_NOATIME | \
MNT_NOSYMFOLLOW | MNT_IGNORE | \
MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR | \
MNT_ACLS | MNT_USER | MNT_NFS4ACLS)
MNT_ACLS | MNT_USER | MNT_NFS4ACLS | \
MNT_AUTOMOUNTED)
/*
* External filesystem command modifier flags.

View file

@ -5,6 +5,7 @@
SUBDIR= adduser \
arp \
autofs \
binmiscctl \
bootparamd \
bsdconfig \

33
usr.sbin/autofs/Makefile Normal file
View file

@ -0,0 +1,33 @@
# $FreeBSD$
PROG= automountd
SRCS= automount.c
SRCS+= automountd.c
SRCS+= autounmountd.c
SRCS+= common.c
SRCS+= defined.c
SRCS+= getmntopts.c
SRCS+= log.c
SRCS+= popen.c
SRCS+= token.l
CFLAGS+=-I${.CURDIR}
CFLAGS+=-I${.CURDIR}/../../sys/fs/autofs
MAN= automount.8 automountd.8 autounmountd.8 auto_master.5
DPADD= ${LIBUTIL}
LDADD= -lutil
# Needed for getmntopts.c
MOUNT= ${.CURDIR}/../../sbin/mount
CFLAGS+=-I${MOUNT}
WARNS= 6
LINKS= ${BINDIR}/automountd ${BINDIR}/automount
LINKS+= ${BINDIR}/automountd ${BINDIR}/autounmountd
.PATH: ${MOUNT}
.include <bsd.prog.mk>

View file

@ -0,0 +1,272 @@
.\" Copyright (c) 2014 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This software was developed by Edward Tomasz Napierala under sponsorship
.\" from the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd July 31, 2014
.Dt AUTO_MASTER 5
.Os
.Sh NAME
.Nm auto_master
.Nd auto_master and map file format
.Sh DESCRIPTION
The
.Nm
configuration file is used by the
.Xr automount 8
command.
Map files are read by the
.Xr automountd 8
daemon.
.Sh AUTO_MASTER SYNTAX
The
.Nm
file consists of lines with two or three entries separated by whitespace
and terminated by newline character:
.Bd -literal -offset indent
.Pa mountpoint Pa map_name Op Ar -options
.Ed
.Pp
.Pa mountpoint
is either a fully specified path, or
.Li /- .
When
.Pa mountpoint
is a full path,
.Pa map_name
must reference an indirect map.
Otherwise,
.Pa map_name
must reference a direct map.
See
.Sx "MAP SYNTAX" below.
.Pp
.Pa map_name
specifies map to use.
If
.Pa map_name
begins with
.Li - ,
it specifies a special map.
See
.Sx "MAP SYNTAX"
below.
If
.Pa map_name
is not a fully specified path
.Pq it does not start with Li / ,
.Xr automountd 8
will search for that name in
.Li /etc .
Otherwise it will use the path as given.
If the file indicated by
.Pa map_name
is executable,
.Xr automountd 8
will assume it is an executable map.
See
.Sx "MAP SYNTAX"
below.
Otherwise, the file is opened and the contents parsed.
.Pp
.Pa -options
is an optional field that starts with
.Li -
and can contain generic filesystem mount options.
.Pp
The following example specifies that the /etc/auto_example indirect map
will be mounted on /example.
.Bd -literal -offset indent
/example auto_example
.Ed
.Sh MAP SYNTAX
Map files consist of lines with a number of entries separated by whitespace
and terminated by newline character:
.Bd -literal -offset indent
.Pa key Oo Ar -options Oc Oo Ar mountpoint Oo -options Oc Oc Ar location Op ...
.Ed
.Pp
In most cases, it can be simplified to:
.Bd -literal -offset indent
.Pa key Oo Ar -options Oc Ar location
.Ed
.Pp
.Pa key
is the path component used by
.Xr automountd 8
to find the right map entry to use.
It is also used to form the final mountpoint.
.Pp
The
.Ar options
field, if present, must begin with
.Li - .
When mounting the filesystem, options supplied to
.Nm
and options specified in the map entry are concatenated together.
The special option
.Li fstype
is used to specify filesystem type.
It is not passed to the mount program as an option.
Instead, it is passed as argument to
.Cm "mount -t".
.Pp
The optional
.Pa mountpoint
field is used to specify multiple mount points
for a single key.
.Pp
The
.Ar location
field specifies the filesystem to be mounted.
To pass location that begins with
.Li / ,
prefix it with colon.
For example,
.Li :/dev/cd0 .
.Pp
This example, when used with the
.Nm
example above, specifies that the NFS share
.Li 192.168.1.1:/share/example/x
will be mounted on
.Pa /example/x/
when any process attempts to access that mountpoint, with
.Li intr
and
.Li nfsv4
mount options:
.Bd -literal -offset indent
.Li x -intr,nfsv4 192.168.1.1:/share/example/x
.Ed
.Pp
Automatically mount the CD drive on access:
.Bd -literal -offset indent
.Li cd -intr,fstype=cd9660 :/dev/cd0
.Ed
.Sh SPECIAL MAPS
Special maps have names beginning with
.Li - .
Supported special maps are:
.Pp
.Bl -tag -width "-hosts" -compact
.It Li -hosts
This map queries the remote NFS server and maps exported volumes.
It is traditionally mounted on
.Pa /net .
It enables access to files on a remote NFS server by accessing
.Pa /net/nfs-server-ip/share-name/
directory, without the need for any further configuration.
.It Li -null
This map prevents the
.Xr automountd 8
from mounting anything on the mountpoint.
.El
.Sh EXECUTABLE MAPS
If the map file specified in
.Nm
has execute bit set, the
.Xr automountd 8
will execute it and parse the standard output instead of parsing
the file contents.
.Sh INDIRECT VERSUS DIRECT MAPS
Indirect maps are referred to in
.Nm
by entries with a fully qualified path as a mount point, and must contain only
relative paths as keys.
Direct maps are referred to in
.Nm
by entries with
.Li /-
as the mountpoint, and must contain only fully qualified paths as keys.
For indirect maps, the final mount point is determined by concatenating the
.Nm
mountpoint with the map entry key and optional map entry mountpoint.
For direct maps, the final mount point is determined by concatenating
the map entry key with the optional map entry mountpoint.
.Pp
The example above could be rewritten using direct map, by placing this in
.Nm :
.Bd -literal -offset indent
.Li /- auto_example
.Ed
.Pp
and this in
.Li /etc/auto_example
map file:
.Bd -literal -offset indent
.Li /example/x -intr,nfsv4 192.168.1.1:/share/example/x
.Li /example/cd -intr,fstype=cd9660 :/dev/cd0
.Ed
.Sh DIRECTORY SERVICES
Both
.Nm
and maps may contain entries consisting of a plus sign and map name:
.Bd -literal -offset indent
.Li +auto_master
.Ed
.Pp
Those entries cause
.Xr automountd 8
daemon to retrieve the named map from directory services (like LDAP)
and include it where the entry was.
.Pp
If the file containing the map referenced in
.Nm
is not found, the map will be retrieved from directory services instead.
.Pp
To retrieve entries from directory services,
.Xr automountd 8
daemon runs
.Pa /etc/autofs/include ,
which is usually a shell script, with map name as the only command line
parameter.
The script should output entries formatted according to
.Nm
or automounter map syntax to standard output.
An example script to use LDAP is included in
.Pa /etc/autofs/include_ldap .
It can be symlinked to
.Pa /etc/autofs/include .
.Sh FILES
.Bl -tag -width ".Pa /etc/auto_master" -compact
.It Pa /etc/auto_master
The default location of the
.Pa auto_master
file.
.El
.Sh SEE ALSO
.Xr autofs 5 ,
.Xr automount 8 ,
.Xr automountd 8 ,
.Xr autounmountd 8
.Sh AUTHORS
The
.Nm
configuration file functionality was developed by
.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org
under sponsorship from the FreeBSD Foundation.

107
usr.sbin/autofs/automount.8 Normal file
View file

@ -0,0 +1,107 @@
.\" Copyright (c) 2014 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This software was developed by Edward Tomasz Napierala under sponsorship
.\" from the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd April 20, 2014
.Dt AUTOMOUNT 8
.Os
.Sh NAME
.Nm automount
.Nd update autofs mounts
.Sh SYNOPSIS
.Nm
.Op Fl D Ar name=value
.Op Fl L
.Op Fl f
.Op Fl o Ar options
.Op Fl v
.Op Fl u
.Sh DESCRIPTION
When called without options, the
.Nm
command parses the
.Xr auto_master 5
configuration file and any direct maps that it references, and mounts
or unmounts
.Xr autofs 4
filesystems to match.
These options are available:
.Bl -tag -width ".Fl v"
.It Fl D
Define a variable.
It is only useful with
.Fl L .
.It Fl L
Do not mount or unmount anything.
Instead parse
.Xr auto_master 5
and any direct maps, then print them to standard output.
When specified more than once, all the maps, including indirect ones,
will be parsed and shown.
This is useful when debugging configuration problems.
.It Fl f
Force unmount, to be used with
.Fl u .
.It Fl o
Specify mount options to be used along with the ones specified in the maps.
It is only useful with
.Fl L .
.It Fl u
Try to unmount filesystems mounted by
.Xr automountd 8 .
.Xr autofs 5
mounts are not unmounted.
To unmount all
.Xr autofs
mounts, use
.Cm "umount -At autofs".
.It Fl v
Increase verbosity.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
Unmount all filesystems mounted by
.Xr automountd 8 :
.Dl Nm Fl u
.Sh SEE ALSO
.Xr auto_master 5 ,
.Xr autofs 5 ,
.Xr automountd 8 ,
.Xr autounmountd 8
.Sh HISTORY
The
.Nm
command appeared in
.Fx 10.1 .
.Sh AUTHORS
The
.Nm
was developed by
.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org
under sponsorship from the FreeBSD Foundation.

345
usr.sbin/autofs/automount.c Normal file
View file

@ -0,0 +1,345 @@
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/linker.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <netdb.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libutil.h>
#include "common.h"
#include "mntopts.h"
static int
unmount_by_statfs(const struct statfs *sb, bool force)
{
char *fsid_str;
int error, ret, flags;
ret = asprintf(&fsid_str, "FSID:%d:%d",
sb->f_fsid.val[0], sb->f_fsid.val[1]);
if (ret < 0)
log_err(1, "asprintf");
log_debugx("unmounting %s using %s", sb->f_mntonname, fsid_str);
flags = MNT_BYFSID;
if (force)
flags |= MNT_FORCE;
error = unmount(fsid_str, flags);
free(fsid_str);
if (error != 0)
log_warn("cannot unmount %s", sb->f_mntonname);
return (error);
}
static const struct statfs *
find_statfs(const struct statfs *mntbuf, int nitems, const char *mountpoint)
{
int i;
for (i = 0; i < nitems; i++) {
if (strcmp(mntbuf[i].f_mntonname, mountpoint) == 0)
return (mntbuf + i);
}
return (NULL);
}
static void
mount_autofs(const char *from, const char *fspath, const char *options,
const char *prefix)
{
struct iovec *iov = NULL;
char errmsg[255];
int error, iovlen = 0;
create_directory(fspath);
log_debugx("mounting %s on %s, prefix \"%s\", options \"%s\"",
from, fspath, prefix, options);
memset(errmsg, 0, sizeof(errmsg));
build_iovec(&iov, &iovlen, "fstype",
__DECONST(void *, "autofs"), (size_t)-1);
build_iovec(&iov, &iovlen, "fspath",
__DECONST(void *, fspath), (size_t)-1);
build_iovec(&iov, &iovlen, "from",
__DECONST(void *, from), (size_t)-1);
build_iovec(&iov, &iovlen, "errmsg",
errmsg, sizeof(errmsg));
/*
* Append the options and mountpoint defined in auto_master(5);
* this way automountd(8) does not need to parse it.
*/
build_iovec(&iov, &iovlen, "master_options",
__DECONST(void *, options), (size_t)-1);
build_iovec(&iov, &iovlen, "master_prefix",
__DECONST(void *, prefix), (size_t)-1);
error = nmount(iov, iovlen, 0);
if (error != 0) {
if (*errmsg != '\0') {
log_err(1, "cannot mount %s on %s: %s",
from, fspath, errmsg);
} else {
log_err(1, "cannot mount %s on %s", from, fspath);
}
}
}
static void
mount_if_not_already(const struct node *n, const char *map,
const struct statfs *mntbuf, int nitems)
{
const struct statfs *sb;
char *mountpoint;
char *from;
int ret;
ret = asprintf(&from, "map %s", map);
if (ret < 0)
log_err(1, "asprintf");
mountpoint = node_path(n);
sb = find_statfs(mntbuf, nitems, mountpoint);
if (sb != NULL) {
if (strcmp(sb->f_fstypename, "autofs") != 0) {
log_debugx("unknown filesystem mounted "
"on %s; mounting", mountpoint);
/*
* XXX: Compare options and 'from',
* and update the mount if necessary.
*/
} else {
log_debugx("autofs already mounted "
"on %s", mountpoint);
free(from);
free(mountpoint);
return;
}
} else {
log_debugx("nothing mounted on %s; mounting",
mountpoint);
}
mount_autofs(from, mountpoint, n->n_options, n->n_key);
free(from);
free(mountpoint);
}
static void
mount_unmount(struct node *root)
{
struct statfs *mntbuf;
struct node *n, *n2, *n3;
int i, nitems;
nitems = getmntinfo(&mntbuf, MNT_WAIT);
if (nitems <= 0)
log_err(1, "getmntinfo");
log_debugx("unmounting stale autofs mounts");
for (i = 0; i < nitems; i++) {
if (strcmp(mntbuf[i].f_fstypename, "autofs") != 0) {
log_debugx("skipping %s, filesystem type is not autofs",
mntbuf[i].f_mntonname);
continue;
}
n = node_find(root, mntbuf[i].f_mntonname);
if (n != NULL) {
log_debugx("leaving autofs mounted on %s",
mntbuf[i].f_mntonname);
continue;
}
log_debugx("autofs mounted on %s not found "
"in new configuration; unmounting", mntbuf[i].f_mntonname);
unmount_by_statfs(&(mntbuf[i]), false);
}
log_debugx("mounting new autofs mounts");
TAILQ_FOREACH(n, &root->n_children, n_next) {
if (!node_is_direct_map(n)) {
mount_if_not_already(n, n->n_map, mntbuf, nitems);
continue;
}
TAILQ_FOREACH(n2, &n->n_children, n_next) {
TAILQ_FOREACH(n3, &n2->n_children, n_next) {
mount_if_not_already(n3, n->n_map,
mntbuf, nitems);
}
}
}
}
static void
unmount_automounted(bool force)
{
struct statfs *mntbuf;
int i, nitems;
nitems = getmntinfo(&mntbuf, MNT_WAIT);
if (nitems <= 0)
log_err(1, "getmntinfo");
log_debugx("unmounting automounted filesystems");
for (i = 0; i < nitems; i++) {
if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) {
log_debugx("skipping %s, filesystem type is autofs",
mntbuf[i].f_mntonname);
continue;
}
if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) {
log_debugx("skipping %s, not automounted",
mntbuf[i].f_mntonname);
continue;
}
unmount_by_statfs(&(mntbuf[i]), force);
}
}
static void
usage_automount(void)
{
fprintf(stderr, "usage: automount [-D name=value][-o opts][-Lfuv]\n");
exit(1);
}
int
main_automount(int argc, char **argv)
{
struct node *root;
int ch, debug = 0, show_maps = 0;
char *options = NULL;
bool do_unmount = false, force_unmount = false;
/*
* Note that in automount(8), the only purpose of variable
* handling is to aid in debugging maps (automount -L).
*/
defined_init();
while ((ch = getopt(argc, argv, "D:Lfo:uv")) != -1) {
switch (ch) {
case 'D':
defined_parse_and_add(optarg);
break;
case 'L':
show_maps++;
break;
case 'f':
force_unmount = true;
break;
case 'o':
if (options == NULL) {
options = checked_strdup(optarg);
} else {
options =
separated_concat(options, optarg, ',');
}
break;
case 'u':
do_unmount = true;
break;
case 'v':
debug++;
break;
case '?':
default:
usage_automount();
}
}
argc -= optind;
if (argc != 0)
usage_automount();
if (force_unmount && !do_unmount)
usage_automount();
log_init(debug);
if (do_unmount) {
unmount_automounted(force_unmount);
return (0);
}
root = node_new_root();
parse_master(root, AUTO_MASTER_PATH);
if (show_maps) {
if (options != NULL) {
root->n_options = separated_concat(options,
root->n_options, ',');
}
if (show_maps > 1) {
node_expand_indirect_maps(root);
node_expand_ampersand(root, NULL);
}
node_expand_defined(root);
node_print(root);
return (0);
}
mount_unmount(root);
return (0);
}

View file

@ -0,0 +1,103 @@
.\" Copyright (c) 2014 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This software was developed by Edward Tomasz Napierala under sponsorship
.\" from the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd April 20, 2014
.Dt AUTOMOUNTD 8
.Os
.Sh NAME
.Nm automountd
.Nd daemon handling autofs mount requests
.Sh SYNOPSIS
.Nm
.Op Fl D Ar name=value
.Op Fl i
.Op Fl m Ar maxproc
.Op Fl o Ar options
.Op Fl d
.Op Fl v
.Sh DESCRIPTION
The
.Nm
daemon is responsible for handling
.Xr autofs 5
mount requests, parsing maps,
and mounting filesystems they specify.
On startup,
.Nm
forks into background and waits for kernel requests.
When a request is received,
.Nm
forks a child process.
The child process parses the appropriate map and mounts filesystems accordingly.
Then it signals the kernel to release blocked processes that were waiting
for the mount.
.Bl -tag -width ".Fl v"
.It Fl D
Define a variable.
.It Fl i
For indirect mounts, only create subdirectories if there are no wildcard
entries.
Without
.Fl i ,
.Nm
creates all the subdirectories it can.
Users may not realize that the wildcard map entry makes it possible to access
directories that have not yet been created.
.It Fl m Ar maxproc
Limit the number of forked
.Nm
processes, and thus the number of mount requests being handled in parallel.
The default is 30.
.It Fl d
Debug mode: increase verbosity and do not daemonize.
.It Fl o Ar options
Specify mount options.
Options specified here ill be overridden by options entered in maps or
.Xr auto_master 5 .
.It Fl v
Increase verbosity.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr auto_master 5 ,
.Xr autofs 5 ,
.Xr automount 8 ,
.Xr autounmountd 8
.Sh HISTORY
The
.Nm
daemon appeared in
.Fx 10.1 .
.Sh AUTHORS
The
.Nm
was developed by
.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org
under sponsorship from the FreeBSD Foundation.

View file

@ -0,0 +1,498 @@
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/linker.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <netdb.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libutil.h>
#include "autofs_ioctl.h"
#include "common.h"
#define AUTOMOUNTD_PIDFILE "/var/run/automountd.pid"
static int nchildren = 0;
static int autofs_fd;
static int request_id;
static void
done(int request_error)
{
struct autofs_daemon_done add;
int error;
memset(&add, 0, sizeof(add));
add.add_id = request_id;
add.add_error = request_error;
log_debugx("completing request %d with error %d",
request_id, request_error);
error = ioctl(autofs_fd, AUTOFSDONE, &add);
if (error != 0) {
/*
* Do this instead of log_err() to avoid calling
* done() again with error, from atexit handler.
*/
log_warn("AUTOFSDONE");
}
quick_exit(1);
}
/*
* Remove "fstype=whatever" from optionsp and return the "whatever" part.
*/
static char *
pick_option(const char *option, char **optionsp)
{
char *tofree, *pair, *newoptions;
char *picked = NULL;
bool first = true;
tofree = *optionsp;
newoptions = calloc(strlen(*optionsp) + 1, 1);
if (newoptions == NULL)
log_err(1, "calloc");
while ((pair = strsep(optionsp, ",")) != NULL) {
/*
* XXX: strncasecmp(3) perhaps?
*/
if (strncmp(pair, option, strlen(option)) == 0) {
picked = checked_strdup(pair + strlen(option));
} else {
if (first == false)
strcat(newoptions, ",");
else
first = false;
strcat(newoptions, pair);
}
}
free(tofree);
*optionsp = newoptions;
return (picked);
}
static void
create_subtree(const struct node *node, bool incomplete)
{
const struct node *child;
char *path;
bool wildcard_found = false;
/*
* Skip wildcard nodes.
*/
if (strcmp(node->n_key, "*") == 0)
return;
path = node_path(node);
log_debugx("creating subtree at %s", path);
create_directory(path);
if (incomplete) {
TAILQ_FOREACH(child, &node->n_children, n_next) {
if (strcmp(child->n_key, "*") == 0) {
wildcard_found = true;
break;
}
}
if (wildcard_found) {
log_debugx("node %s contains wildcard entry; "
"not creating its subdirectories due to -d flag",
path);
free(path);
return;
}
}
free(path);
TAILQ_FOREACH(child, &node->n_children, n_next)
create_subtree(child, incomplete);
}
static void
exit_callback(void)
{
done(EIO);
}
static void
handle_request(const struct autofs_daemon_request *adr, char *cmdline_options,
bool incomplete_hierarchy)
{
const char *map;
struct node *root, *parent, *node;
FILE *f;
char *options, *fstype, *retrycnt, *tmp;
int error;
log_debugx("got request %d: from %s, path %s, prefix \"%s\", "
"key \"%s\", options \"%s\"", adr->adr_id, adr->adr_from,
adr->adr_path, adr->adr_prefix, adr->adr_key, adr->adr_options);
/*
* Try to notify the kernel about any problems.
*/
request_id = adr->adr_id;
atexit(exit_callback);
if (strncmp(adr->adr_from, "map ", 4) != 0) {
log_errx(1, "invalid mountfrom \"%s\"; failing request",
adr->adr_from);
}
map = adr->adr_from + 4; /* 4 for strlen("map "); */
root = node_new_root();
if (adr->adr_prefix[0] == '\0' || strcmp(adr->adr_prefix, "/") == 0) {
parent = root;
} else {
parent = node_new_map(root, checked_strdup(adr->adr_prefix),
checked_strdup(adr->adr_options), checked_strdup(map),
checked_strdup("[kernel request]"), lineno);
}
parse_map(parent, map, adr->adr_key[0] != '\0' ? adr->adr_key : NULL);
if (adr->adr_key[0] != '\0')
node_expand_wildcard(root, adr->adr_key);
node = node_find(root, adr->adr_path);
if (node == NULL) {
log_errx(1, "map %s does not contain key for \"%s\"; "
"failing mount", map, adr->adr_path);
}
if (node->n_location == NULL) {
log_debugx("found node defined at %s:%d; not a mountpoint",
node->n_config_file, node->n_config_line);
/*
* Not a mountpoint; create directories in the autofs mount
* and complete the request.
*/
create_subtree(node, incomplete_hierarchy);
if (incomplete_hierarchy && adr->adr_key[0] != '\0') {
/*
* We still need to create the single subdirectory
* user is trying to access.
*/
tmp = separated_concat(adr->adr_path,
adr->adr_key, '/');
node = node_find(root, tmp);
if (node != NULL)
create_subtree(node, false);
}
done(0);
log_debugx("nothing to mount; exiting");
/*
* Exit without calling exit_callback().
*/
quick_exit(0);
}
log_debugx("found node defined at %s:%d; it is a mountpoint",
node->n_config_file, node->n_config_line);
node_expand_ampersand(node,
adr->adr_key[0] != '\0' ? adr->adr_key : NULL);
error = node_expand_defined(node);
if (error != 0) {
log_errx(1, "variable expansion failed for %s; "
"failing mount", adr->adr_path);
}
options = node_options(node);
/*
* Prepend options passed via automountd(8) command line.
*/
if (cmdline_options != NULL)
options = separated_concat(cmdline_options, options, ',');
/*
* Append "automounted".
*/
options = separated_concat(options, "automounted", ',');
/*
* Figure out fstype.
*/
fstype = pick_option("fstype=", &options);
if (fstype == NULL) {
log_debugx("fstype not specified in options; "
"defaulting to \"nfs\"");
fstype = checked_strdup("nfs");
}
if (strcmp(fstype, "nfs") == 0) {
/*
* The mount_nfs(8) command defaults to retry undefinitely.
* We do not want that behaviour, because it leaves mount_nfs(8)
* instances and automountd(8) children hanging forever.
* Disable retries unless the option was passed explicitly.
*/
retrycnt = pick_option("retrycnt=", &options);
if (retrycnt == NULL) {
log_debugx("retrycnt not specified in options; "
"defaulting to 1");
options = separated_concat(options,
separated_concat("retrycnt", "1", '='), ',');
} else {
options = separated_concat(options,
separated_concat("retrycnt", retrycnt, '='), ',');
}
}
f = auto_popen("mount", "-t", fstype, "-o", options,
node->n_location, adr->adr_path, NULL);
assert(f != NULL);
error = auto_pclose(f);
if (error != 0)
log_errx(1, "mount failed");
done(0);
log_debugx("mount done; exiting");
/*
* Exit without calling exit_callback().
*/
quick_exit(0);
}
static int
wait_for_children(bool block)
{
pid_t pid;
int status;
int num = 0;
for (;;) {
/*
* If "block" is true, wait for at least one process.
*/
if (block && num == 0)
pid = wait4(-1, &status, 0, NULL);
else
pid = wait4(-1, &status, WNOHANG, NULL);
if (pid <= 0)
break;
if (WIFSIGNALED(status)) {
log_warnx("child process %d terminated with signal %d",
pid, WTERMSIG(status));
} else if (WEXITSTATUS(status) != 0) {
log_warnx("child process %d terminated with exit status %d",
pid, WEXITSTATUS(status));
} else {
log_debugx("child process %d terminated gracefully", pid);
}
num++;
}
return (num);
}
static void
usage_automountd(void)
{
fprintf(stderr, "usage: automountd [-D name=value][-m maxproc]"
"[-o opts][-Tidv]\n");
exit(1);
}
int
main_automountd(int argc, char **argv)
{
struct pidfh *pidfh;
pid_t pid, otherpid;
const char *pidfile_path = AUTOMOUNTD_PIDFILE;
char *options = NULL;
struct autofs_daemon_request request;
int ch, debug = 0, error, maxproc = 30, retval, saved_errno;
bool dont_daemonize = false, incomplete_hierarchy = false;
defined_init();
while ((ch = getopt(argc, argv, "D:Tdim:o:v")) != -1) {
switch (ch) {
case 'D':
defined_parse_and_add(optarg);
break;
case 'T':
/*
* For compatibility with other implementations,
* such as OS X.
*/
debug++;
break;
case 'd':
dont_daemonize = true;
debug++;
break;
case 'i':
incomplete_hierarchy = true;
break;
case 'm':
maxproc = atoi(optarg);
break;
case 'o':
if (options == NULL) {
options = checked_strdup(optarg);
} else {
options =
separated_concat(options, optarg, ',');
}
break;
case 'v':
debug++;
break;
case '?':
default:
usage_automountd();
}
}
argc -= optind;
if (argc != 0)
usage_automountd();
log_init(debug);
pidfh = pidfile_open(pidfile_path, 0600, &otherpid);
if (pidfh == NULL) {
if (errno == EEXIST) {
log_errx(1, "daemon already running, pid: %jd.",
(intmax_t)otherpid);
}
log_err(1, "cannot open or create pidfile \"%s\"",
pidfile_path);
}
autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC);
if (autofs_fd < 0 && errno == ENOENT) {
saved_errno = errno;
retval = kldload("autofs");
if (retval != -1)
autofs_fd = open(AUTOFS_PATH, O_RDWR | O_CLOEXEC);
else
errno = saved_errno;
}
if (autofs_fd < 0)
log_err(1, "failed to open %s", AUTOFS_PATH);
if (dont_daemonize == false) {
if (daemon(0, 0) == -1) {
log_warn("cannot daemonize");
pidfile_remove(pidfh);
exit(1);
}
} else {
lesser_daemon();
}
pidfile_write(pidfh);
for (;;) {
log_debugx("waiting for request from the kernel");
memset(&request, 0, sizeof(request));
error = ioctl(autofs_fd, AUTOFSREQUEST, &request);
if (error != 0) {
if (errno == EINTR) {
nchildren -= wait_for_children(false);
assert(nchildren >= 0);
continue;
}
log_err(1, "AUTOFSREQUEST");
}
if (dont_daemonize) {
log_debugx("not forking due to -d flag; "
"will exit after servicing a single request");
} else {
nchildren -= wait_for_children(false);
assert(nchildren >= 0);
while (maxproc > 0 && nchildren >= maxproc) {
log_debugx("maxproc limit of %d child processes hit; "
"waiting for child process to exit", maxproc);
nchildren -= wait_for_children(true);
assert(nchildren >= 0);
}
log_debugx("got request; forking child process #%d",
nchildren);
nchildren++;
pid = fork();
if (pid < 0)
log_err(1, "fork");
if (pid > 0)
continue;
}
pidfile_close(pidfh);
handle_request(&request, options, incomplete_hierarchy);
}
pidfile_close(pidfh);
return (0);
}

View file

@ -0,0 +1,88 @@
.\" Copyright (c) 2014 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This software was developed by Edward Tomasz Napierala under sponsorship
.\" from the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd April 20, 2014
.Dt AUTOUNMOUNTD 8
.Os
.Sh NAME
.Nm autounmountd
.Nd daemon unmounting automounted filesystems
.Sh SYNOPSIS
.Nm
.Op Fl d
.Op Fl r time
.Op Fl t time
.Op Fl v
.Sh DESCRIPTION
The
.Nm
daemon is responsible for unmounting filesystems mounted by
.Xr automountd 8 .
On startup,
.Nm
retrieves a list of filesystems that have the
.Li automounted
mount option set.
The list is updated every time a filesystem is mounted or unmounted.
After a specified time passes,
.Nm
attempts to unmount a filesystem, retrying after some time if necessary.
.Pp
These options are available:
.Bl -tag -width ".Fl v"
.It Fl d
Debug mode: increase verbosity and do not daemonize.
.It Fl r
Number of seconds to wait before trying to unmount an expired filesystem
after a previous attempt failed, possibly due to filesystem being busy.
The default value is 600, or ten minutes.
.It Fl t
Number of seconds to wait before trying to unmount a filesystem.
The default value is 600, or ten minutes.
.It Fl v
Increase verbosity.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr auto_master 5 ,
.Xr autofs 5 ,
.Xr automount 8 ,
.Xr automountd 8
.Sh HISTORY
The
.Nm
daemon appeared in
.Fx 10.1 .
.Sh AUTHORS
The
.Nm
was developed by
.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org
under sponsorship from the FreeBSD Foundation.

View file

@ -0,0 +1,351 @@
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/event.h>
#include <sys/mount.h>
#include <sys/time.h>
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libutil.h>
#include "common.h"
#define AUTOUNMOUNTD_PIDFILE "/var/run/autounmountd.pid"
struct automounted_fs {
TAILQ_ENTRY(automounted_fs) af_next;
time_t af_mount_time;
bool af_mark;
fsid_t af_fsid;
char af_mountpoint[MNAMELEN];
};
static TAILQ_HEAD(, automounted_fs) automounted;
static struct automounted_fs *
automounted_find(fsid_t fsid)
{
struct automounted_fs *af;
TAILQ_FOREACH(af, &automounted, af_next) {
if (af->af_fsid.val[0] == fsid.val[0] &&
af->af_fsid.val[1] == fsid.val[1])
return (af);
}
return (NULL);
}
static struct automounted_fs *
automounted_add(fsid_t fsid, const char *mountpoint)
{
struct automounted_fs *af;
af = calloc(sizeof(*af), 1);
if (af == NULL)
log_err(1, "calloc");
af->af_mount_time = time(NULL);
af->af_fsid = fsid;
strlcpy(af->af_mountpoint, mountpoint, sizeof(af->af_mountpoint));
TAILQ_INSERT_TAIL(&automounted, af, af_next);
return (af);
}
static void
automounted_remove(struct automounted_fs *af)
{
TAILQ_REMOVE(&automounted, af, af_next);
free(af);
}
static void
refresh_automounted(void)
{
struct automounted_fs *af, *tmpaf;
struct statfs *mntbuf;
int i, nitems;
nitems = getmntinfo(&mntbuf, MNT_WAIT);
if (nitems <= 0)
log_err(1, "getmntinfo");
log_debugx("refreshing list of automounted filesystems");
TAILQ_FOREACH(af, &automounted, af_next)
af->af_mark = false;
for (i = 0; i < nitems; i++) {
if (strcmp(mntbuf[i].f_fstypename, "autofs") == 0) {
log_debugx("skipping %s, filesystem type is autofs",
mntbuf[i].f_mntonname);
continue;
}
if ((mntbuf[i].f_flags & MNT_AUTOMOUNTED) == 0) {
log_debugx("skipping %s, not automounted",
mntbuf[i].f_mntonname);
continue;
}
af = automounted_find(mntbuf[i].f_fsid);
if (af == NULL) {
log_debugx("new automounted filesystem found on %s "
"(FSID:%d:%d)", mntbuf[i].f_mntonname,
mntbuf[i].f_fsid.val[0], mntbuf[i].f_fsid.val[1]);
af = automounted_add(mntbuf[i].f_fsid,
mntbuf[i].f_mntonname);
} else {
log_debugx("already known automounted filesystem "
"found on %s (FSID:%d:%d)", mntbuf[i].f_mntonname,
mntbuf[i].f_fsid.val[0], mntbuf[i].f_fsid.val[1]);
}
af->af_mark = true;
}
TAILQ_FOREACH_SAFE(af, &automounted, af_next, tmpaf) {
if (af->af_mark)
continue;
log_debugx("lost filesystem mounted on %s (FSID:%d:%d)",
af->af_mountpoint, af->af_fsid.val[0], af->af_fsid.val[1]);
automounted_remove(af);
}
}
static int
unmount_by_fsid(const fsid_t fsid, const char *mountpoint)
{
char *fsid_str;
int error, ret;
ret = asprintf(&fsid_str, "FSID:%d:%d", fsid.val[0], fsid.val[1]);
if (ret < 0)
log_err(1, "asprintf");
error = unmount(fsid_str, MNT_BYFSID);
if (error != 0) {
if (errno == EBUSY) {
log_debugx("cannot unmount %s (%s): %s",
mountpoint, fsid_str, strerror(errno));
} else {
log_warn("cannot unmount %s (%s)",
mountpoint, fsid_str);
}
}
free(fsid_str);
return (error);
}
static double
expire_automounted(double expiration_time)
{
struct automounted_fs *af, *tmpaf;
time_t now;
double mounted_for, mounted_max = 0;
int error;
bool unmounted = false;
now = time(NULL);
log_debugx("expiring automounted filesystems");
TAILQ_FOREACH_SAFE(af, &automounted, af_next, tmpaf) {
mounted_for = difftime(now, af->af_mount_time);
if (mounted_for < expiration_time) {
log_debugx("skipping %s (FSID:%d:%d), mounted "
"for %.0f seconds", af->af_mountpoint,
af->af_fsid.val[0], af->af_fsid.val[1],
mounted_for);
if (mounted_for > mounted_max)
mounted_max = mounted_for;
continue;
}
log_debugx("filesystem mounted on %s (FSID:%d:%d), "
"was mounted for %.0f seconds; unmounting",
af->af_mountpoint, af->af_fsid.val[0], af->af_fsid.val[1],
mounted_for);
error = unmount_by_fsid(af->af_fsid, af->af_mountpoint);
if (error != 0) {
if (mounted_for > mounted_max)
mounted_max = mounted_for;
} else {
unmounted = true;
}
}
if (unmounted) {
/*
* Successful unmount of a filesystem could unbusy its parent
* filesystem that can now be unmounted.
*/
log_debugx("filesystem got unmounted; go around");
return (expire_automounted(expiration_time));
}
return (mounted_max);
}
static void
usage_autounmountd(void)
{
fprintf(stderr, "usage: autounmountd [-r time][-t time][-dv]\n");
exit(1);
}
static void
do_wait(int kq, double sleep_time)
{
struct timespec timeout;
struct kevent unused;
int error;
assert(sleep_time > 0);
timeout.tv_sec = sleep_time;
timeout.tv_nsec = 0;
log_debugx("waiting for filesystem event for %.0f seconds", sleep_time);
error = kevent(kq, NULL, 0, &unused, 1, &timeout);
if (error < 0)
log_err(1, "kevent");
if (error == 0)
log_debugx("timeout reached");
else
log_debugx("got filesystem event");
}
int
main_autounmountd(int argc, char **argv)
{
struct kevent event;
struct pidfh *pidfh;
pid_t otherpid;
const char *pidfile_path = AUTOUNMOUNTD_PIDFILE;
int ch, debug = 0, error, kq;
double expiration_time = 600, retry_time = 600, mounted_max, sleep_time;
bool dont_daemonize = false;
while ((ch = getopt(argc, argv, "dr:t:v")) != -1) {
switch (ch) {
case 'd':
dont_daemonize = true;
debug++;
break;
case 'r':
retry_time = atoi(optarg);
break;
case 't':
expiration_time = atoi(optarg);
break;
case 'v':
debug++;
break;
case '?':
default:
usage_autounmountd();
}
}
argc -= optind;
if (argc != 0)
usage_autounmountd();
if (retry_time <= 0)
log_errx(1, "retry time must be greater than zero");
if (expiration_time <= 0)
log_errx(1, "expiration time must be greater than zero");
log_init(debug);
pidfh = pidfile_open(pidfile_path, 0600, &otherpid);
if (pidfh == NULL) {
if (errno == EEXIST) {
log_errx(1, "daemon already running, pid: %jd.",
(intmax_t)otherpid);
}
log_err(1, "cannot open or create pidfile \"%s\"",
pidfile_path);
}
if (dont_daemonize == false) {
if (daemon(0, 0) == -1) {
log_warn("cannot daemonize");
pidfile_remove(pidfh);
exit(1);
}
}
pidfile_write(pidfh);
TAILQ_INIT(&automounted);
kq = kqueue();
if (kq < 0)
log_err(1, "kqueue");
EV_SET(&event, 0, EVFILT_FS, EV_ADD | EV_CLEAR, 0, 0, NULL);
error = kevent(kq, &event, 1, NULL, 0, NULL);
if (error < 0)
log_err(1, "kevent");
for (;;) {
refresh_automounted();
mounted_max = expire_automounted(expiration_time);
if (mounted_max < expiration_time) {
sleep_time = difftime(expiration_time, mounted_max);
log_debugx("some filesystems expire in %.0f seconds",
sleep_time);
} else {
sleep_time = retry_time;
log_debugx("some expired filesystems remain mounted, "
"will retry in %.0f seconds", sleep_time);
}
do_wait(kq, sleep_time);
}
return (0);
}

1129
usr.sbin/autofs/common.c Normal file

File diff suppressed because it is too large Load diff

112
usr.sbin/autofs/common.h Normal file
View file

@ -0,0 +1,112 @@
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef AUTOMOUNTD_H
#define AUTOMOUNTD_H
#include <sys/queue.h>
#include <stdbool.h>
#define AUTO_MASTER_PATH "/etc/auto_master"
#define AUTO_MAP_PREFIX "/etc"
#define AUTO_SPECIAL_PREFIX "/etc/autofs"
#define AUTO_INCLUDE_PATH AUTO_SPECIAL_PREFIX "/include"
struct node {
TAILQ_ENTRY(node) n_next;
TAILQ_HEAD(nodehead, node) n_children;
struct node *n_parent;
char *n_key;
char *n_options;
char *n_location;
char *n_map;
const char *n_config_file;
int n_config_line;
};
struct defined_value {
TAILQ_ENTRY(defined_value) d_next;
char *d_name;
char *d_value;
};
void log_init(int level);
void log_set_peer_name(const char *name);
void log_set_peer_addr(const char *addr);
void log_err(int, const char *, ...)
__dead2 __printf0like(2, 3);
void log_errx(int, const char *, ...)
__dead2 __printf0like(2, 3);
void log_warn(const char *, ...) __printf0like(1, 2);
void log_warnx(const char *, ...) __printflike(1, 2);
void log_debugx(const char *, ...) __printf0like(1, 2);
char *checked_strdup(const char *);
char *separated_concat(const char *s1, const char *s2, char separator);
void create_directory(const char *path);
struct node *node_new_root(void);
struct node *node_new(struct node *parent, char *key, char *options,
char *location, const char *config_file, int config_line);
struct node *node_new_map(struct node *parent, char *key, char *options,
char *map, const char *config_file, int config_line);
struct node *node_find(struct node *root, const char *mountpoint);
bool node_is_direct_map(const struct node *n);
char *node_path(const struct node *n);
char *node_options(const struct node *n);
void node_expand_ampersand(struct node *root, const char *key);
void node_expand_wildcard(struct node *root, const char *key);
int node_expand_defined(struct node *root);
void node_expand_indirect_maps(struct node *n);
void node_print(const struct node *n);
void parse_master(struct node *root, const char *path);
void parse_map(struct node *parent, const char *map, const char *args);
char *defined_expand(const char *string);
void defined_init(void);
void defined_parse_and_add(char *def);
void lesser_daemon(void);
int main_automount(int argc, char **argv);
int main_automountd(int argc, char **argv);
int main_autounmountd(int argc, char **argv);
FILE *auto_popen(const char *argv0, ...);
int auto_pclose(FILE *iop);
/*
* lex(1) stuff.
*/
extern int lineno;
#define STR 1
#define NEWLINE 2
#endif /* !AUTOMOUNTD_H */

270
usr.sbin/autofs/defined.c Normal file
View file

@ -0,0 +1,270 @@
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
/*
* All the "defined" stuff is for handling variables,
* such as ${OSNAME}, in maps.
*/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/linker.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <netdb.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libutil.h>
#include "common.h"
static TAILQ_HEAD(, defined_value) defined_values;
static const char *
defined_find(const char *name)
{
struct defined_value *d;
TAILQ_FOREACH(d, &defined_values, d_next) {
if (strcmp(d->d_name, name) == 0)
return (d->d_value);
}
return (NULL);
}
char *
defined_expand(const char *string)
{
const char *value;
char c, *expanded, *name;
int i, ret, before_len = 0, name_off = 0, name_len = 0, after_off = 0;
bool backslashed = false, bracketed = false;
expanded = checked_strdup(string);
for (i = 0; string[i] != '\0'; i++) {
c = string[i];
if (c == '\\' && backslashed == false) {
backslashed = true;
continue;
}
if (backslashed) {
backslashed = false;
continue;
}
backslashed = false;
if (c != '$')
continue;
/*
* The 'before_len' variable contains the number
* of characters before the '$'.
*/
before_len = i;
assert(i + 1 < (int)strlen(string));
if (string[i + 1] == '{')
bracketed = true;
if (string[i + 1] == '\0') {
log_warnx("truncated variable");
return (NULL);
}
/*
* Skip '$'.
*/
i++;
if (bracketed) {
if (string[i + 1] == '\0') {
log_warnx("truncated variable");
return (NULL);
}
/*
* Skip '{'.
*/
i++;
}
/*
* The 'name_off' variable contains the number
* of characters before the variable name,
* including the "$" or "${".
*/
name_off = i;
for (; string[i] != '\0'; i++) {
c = string[i];
/*
* XXX: Decide on the set of characters that can be
* used in a variable name.
*/
if (isalnum(c) || c == '_')
continue;
/*
* End of variable name.
*/
if (bracketed) {
if (c != '}')
continue;
/*
* The 'after_off' variable contains the number
* of characters before the rest of the string,
* i.e. after the variable name.
*/
after_off = i + 1;
assert(i > 1);
assert(i - 1 > name_off);
name_len = i - name_off;
break;
}
after_off = i;
assert(i > 1);
assert(i > name_off);
name_len = i - name_off;
break;
}
name = strndup(string + name_off, name_len);
if (name == NULL)
log_err(1, "strndup");
value = defined_find(name);
if (value == NULL) {
log_warnx("undefined variable ${%s}", name);
return (NULL);
}
/*
* Concatenate it back.
*/
ret = asprintf(&expanded, "%.*s%s%s",
before_len, string, value, string + after_off);
if (ret < 0)
log_err(1, "asprintf");
//log_debugx("\"%s\" expanded to \"%s\"", string, expanded);
free(name);
/*
* Figure out where to start searching for next variable.
*/
string = expanded;
i = before_len + strlen(value);
backslashed = bracketed = false;
before_len = name_off = name_len = after_off = 0;
assert(i <= (int)strlen(string));
}
if (before_len != 0 || name_off != 0 || name_len != 0 || after_off != 0) {
log_warnx("truncated variable");
return (NULL);
}
return (expanded);
}
static void
defined_add(const char *name, const char *value)
{
struct defined_value *d;
const char *found;
found = defined_find(name);
if (found != NULL)
log_errx(1, "variable %s already defined", name);
log_debugx("defining variable %s=%s", name, value);
d = calloc(sizeof(*d), 1);
if (d == NULL)
log_err(1, "calloc");
d->d_name = checked_strdup(name);
d->d_value = checked_strdup(value);
TAILQ_INSERT_TAIL(&defined_values, d, d_next);
}
void
defined_parse_and_add(char *def)
{
char *name, *value;
value = def;
name = strsep(&value, "=");
if (value == NULL || value[0] == '\0')
log_errx(1, "missing variable value");
if (name == NULL || name[0] == '\0')
log_errx(1, "missing variable name");
defined_add(name, value);
}
void
defined_init(void)
{
struct utsname name;
int error;
TAILQ_INIT(&defined_values);
error = uname(&name);
if (error != 0)
log_err(1, "uname");
defined_add("ARCH", name.machine);
defined_add("CPU", name.machine);
defined_add("HOST", name.nodename);
defined_add("OSNAME", name.sysname);
defined_add("OSREL", name.release);
defined_add("OSVERS", name.version);
}

196
usr.sbin/autofs/log.c Normal file
View file

@ -0,0 +1,196 @@
/*-
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <vis.h>
#include "common.h"
static int log_level = 0;
static char *peer_name = NULL;
static char *peer_addr = NULL;
#define MSGBUF_LEN 1024
void
log_init(int level)
{
log_level = level;
openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON);
}
void
log_set_peer_name(const char *name)
{
/*
* XXX: Turn it into assertion?
*/
if (peer_name != NULL)
log_errx(1, "%s called twice", __func__);
if (peer_addr == NULL)
log_errx(1, "%s called before log_set_peer_addr", __func__);
peer_name = checked_strdup(name);
}
void
log_set_peer_addr(const char *addr)
{
/*
* XXX: Turn it into assertion?
*/
if (peer_addr != NULL)
log_errx(1, "%s called twice", __func__);
peer_addr = checked_strdup(addr);
}
static void
log_common(int priority, int log_errno, const char *fmt, va_list ap)
{
static char msgbuf[MSGBUF_LEN];
static char msgbuf_strvised[MSGBUF_LEN * 4 + 1];
int ret;
ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
if (ret < 0) {
fprintf(stderr, "%s: snprintf failed", getprogname());
syslog(LOG_CRIT, "snprintf failed");
exit(1);
}
ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL);
if (ret < 0) {
fprintf(stderr, "%s: strnvis failed", getprogname());
syslog(LOG_CRIT, "strnvis failed");
exit(1);
}
if (log_errno == -1) {
if (peer_name != NULL) {
fprintf(stderr, "%s: %s (%s): %s\n", getprogname(),
peer_addr, peer_name, msgbuf_strvised);
syslog(priority, "%s (%s): %s",
peer_addr, peer_name, msgbuf_strvised);
} else if (peer_addr != NULL) {
fprintf(stderr, "%s: %s: %s\n", getprogname(),
peer_addr, msgbuf_strvised);
syslog(priority, "%s: %s",
peer_addr, msgbuf_strvised);
} else {
fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised);
syslog(priority, "%s", msgbuf_strvised);
}
} else {
if (peer_name != NULL) {
fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(),
peer_addr, peer_name, msgbuf_strvised, strerror(errno));
syslog(priority, "%s (%s): %s: %s",
peer_addr, peer_name, msgbuf_strvised, strerror(errno));
} else if (peer_addr != NULL) {
fprintf(stderr, "%s: %s: %s: %s\n", getprogname(),
peer_addr, msgbuf_strvised, strerror(errno));
syslog(priority, "%s: %s: %s",
peer_addr, msgbuf_strvised, strerror(errno));
} else {
fprintf(stderr, "%s: %s: %s\n", getprogname(),
msgbuf_strvised, strerror(errno));
syslog(priority, "%s: %s",
msgbuf_strvised, strerror(errno));
}
}
}
void
log_err(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_common(LOG_CRIT, errno, fmt, ap);
va_end(ap);
exit(eval);
}
void
log_errx(int eval, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_common(LOG_CRIT, -1, fmt, ap);
va_end(ap);
exit(eval);
}
void
log_warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_common(LOG_WARNING, errno, fmt, ap);
va_end(ap);
}
void
log_warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_common(LOG_WARNING, -1, fmt, ap);
va_end(ap);
}
void
log_debugx(const char *fmt, ...)
{
va_list ap;
if (log_level == 0)
return;
va_start(ap, fmt);
log_common(LOG_DEBUG, -1, fmt, ap);
va_end(ap);
}

191
usr.sbin/autofs/popen.c Normal file
View file

@ -0,0 +1,191 @@
/*
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This code is derived from software written by Ken Arnold and
* published in UNIX Review, Vol. 6, No. 8.
*
* Portions of this software were developed by Edward Tomasz Napierala
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <paths.h>
#include "common.h"
extern char **environ;
struct pid {
SLIST_ENTRY(pid) next;
FILE *outfp;
pid_t pid;
char *command;
};
static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
#define ARGV_LEN 42
/*
* Replacement for popen(3), without stdin (which we do not use), but with
* stderr, proper logging, and improved command line arguments passing.
* Error handling is built in - if it returns, then it succeeded.
*/
FILE *
auto_popen(const char *argv0, ...)
{
va_list ap;
struct pid *cur, *p;
pid_t pid;
int error, i, nullfd, outfds[2];
char *arg, *argv[ARGV_LEN], *command;
nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
if (nullfd < 0)
log_err(1, "cannot open %s", _PATH_DEVNULL);
error = pipe(outfds);
if (error != 0)
log_err(1, "pipe");
cur = malloc(sizeof(struct pid));
if (cur == NULL)
log_err(1, "malloc");
argv[0] = checked_strdup(argv0);
command = argv[0];
va_start(ap, argv0);
for (i = 1;; i++) {
if (i >= ARGV_LEN)
log_errx(1, "too many arguments to auto_popen");
arg = va_arg(ap, char *);
argv[i] = arg;
if (arg == NULL)
break;
command = separated_concat(command, arg, ' ');
}
va_end(ap);
cur->command = checked_strdup(command);
switch (pid = fork()) {
case -1: /* Error. */
log_err(1, "fork");
/* NOTREACHED */
case 0: /* Child. */
dup2(nullfd, STDIN_FILENO);
dup2(outfds[1], STDOUT_FILENO);
close(nullfd);
close(outfds[0]);
close(outfds[1]);
SLIST_FOREACH(p, &pidlist, next)
close(fileno(p->outfp));
execvp(argv[0], argv);
log_err(1, "failed to execute %s", argv[0]);
/* NOTREACHED */
}
log_debugx("executing \"%s\" as pid %d", command, pid);
/* Parent; assume fdopen cannot fail. */
cur->outfp = fdopen(outfds[0], "r");
close(nullfd);
close(outfds[1]);
/* Link into list of file descriptors. */
cur->pid = pid;
SLIST_INSERT_HEAD(&pidlist, cur, next);
return (cur->outfp);
}
int
auto_pclose(FILE *iop)
{
struct pid *cur, *last = NULL;
int status;
pid_t pid;
/*
* Find the appropriate file pointer and remove it from the list.
*/
SLIST_FOREACH(cur, &pidlist, next) {
if (cur->outfp == iop)
break;
last = cur;
}
if (cur == NULL) {
return (-1);
}
if (last == NULL)
SLIST_REMOVE_HEAD(&pidlist, next);
else
SLIST_REMOVE_AFTER(last, next);
fclose(cur->outfp);
do {
pid = wait4(cur->pid, &status, 0, NULL);
} while (pid == -1 && errno == EINTR);
if (WIFSIGNALED(status)) {
log_warnx("\"%s\", pid %d, terminated with signal %d",
cur->command, pid, WTERMSIG(status));
return (status);
}
if (WEXITSTATUS(status) != 0) {
log_warnx("\"%s\", pid %d, terminated with exit status %d",
cur->command, pid, WEXITSTATUS(status));
return (status);
}
log_debugx("\"%s\", pid %d, terminated gracefully", cur->command, pid);
free(cur->command);
free(cur);
return (pid == -1 ? -1 : status);
}

57
usr.sbin/autofs/token.l Normal file
View file

@ -0,0 +1,57 @@
%{
/*-
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Edward Tomasz Napierala under sponsorship
* from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "common.h"
int lineno;
#define YY_DECL int yylex(void)
extern int yylex(void);
%}
%option noinput
%option nounput
%option noyywrap
%%
[a-zA-Z0-9\.\+-_/\:\[\]$&{}]+ { return STR; }
#.*\n { lineno++; return NEWLINE; };
\\\n { lineno++; };
\n { lineno++; return NEWLINE; }
[ \t]+ /* ignore whitespace */;
. { return STR; }
%%