mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-07 00:50:50 +00:00
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:
parent
f0f8856f68
commit
3914ddf8a7
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=270096
|
@ -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
5
etc/auto_master
Normal file
|
@ -0,0 +1,5 @@
|
|||
# $FreeBSD$
|
||||
#
|
||||
# Automounter master map, see auto_master(5) for details.
|
||||
#
|
||||
/net -hosts -nosuid
|
9
etc/autofs/Makefile
Normal file
9
etc/autofs/Makefile
Normal 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
38
etc/autofs/include_ldap
Normal 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
17
etc/autofs/special_hosts
Normal 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
4
etc/autofs/special_null
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/true
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
|
@ -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).
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
etc
|
||||
X11
|
||||
..
|
||||
autofs
|
||||
..
|
||||
bluetooth
|
||||
..
|
||||
casper
|
||||
|
|
|
@ -20,6 +20,9 @@ FILES= DAEMON \
|
|||
atm3 \
|
||||
auditd \
|
||||
auditdistd \
|
||||
automount \
|
||||
automountd \
|
||||
autounmountd \
|
||||
bgfsck \
|
||||
${_bluetooth} \
|
||||
bootparams \
|
||||
|
|
31
etc/rc.d/automount
Normal file
31
etc/rc.d/automount
Normal 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
19
etc/rc.d/automountd
Normal 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
18
etc/rc.d/autounmountd
Normal 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"
|
|
@ -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 *);
|
||||
|
|
|
@ -114,6 +114,7 @@ static struct opt {
|
|||
{ MNT_ACLS, "acls" },
|
||||
{ MNT_NFS4ACLS, "nfsv4acls" },
|
||||
{ MNT_GJOURNAL, "gjournal" },
|
||||
{ MNT_AUTOMOUNTED, "automounted" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -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
99
share/man/man5/autofs.5
Normal 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.
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
637
sys/fs/autofs/autofs.c
Normal 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
141
sys/fs/autofs/autofs.h
Normal 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 */
|
89
sys/fs/autofs/autofs_ioctl.h
Normal file
89
sys/fs/autofs/autofs_ioctl.h
Normal 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 */
|
209
sys/fs/autofs/autofs_vfsops.c
Normal file
209
sys/fs/autofs/autofs_vfsops.c
Normal 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(&->am_lock, "autofslk");
|
||||
amp->am_last_fileno = 1;
|
||||
|
||||
vfs_getnewfsid(mp);
|
||||
|
||||
AUTOFS_LOCK(amp);
|
||||
error = autofs_node_new(NULL, amp, ".", -1, &->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(&->am_root->an_children)) {
|
||||
anp = TAILQ_FIRST(&->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(&->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);
|
646
sys/fs/autofs/autofs_vnops.c
Normal file
646
sys/fs/autofs/autofs_vnops.c
Normal 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(&->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);
|
||||
}
|
|
@ -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
51
sys/libkern/strndup.c
Normal 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);
|
||||
}
|
|
@ -48,6 +48,7 @@ SUBDIR= \
|
|||
ata \
|
||||
ath \
|
||||
ath_pci \
|
||||
autofs \
|
||||
${_auxio} \
|
||||
${_bce} \
|
||||
bfe \
|
||||
|
|
11
sys/modules/autofs/Makefile
Normal file
11
sys/modules/autofs/Makefile
Normal 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>
|
|
@ -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 *);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
SUBDIR= adduser \
|
||||
arp \
|
||||
autofs \
|
||||
binmiscctl \
|
||||
bootparamd \
|
||||
bsdconfig \
|
||||
|
|
33
usr.sbin/autofs/Makefile
Normal file
33
usr.sbin/autofs/Makefile
Normal 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>
|
272
usr.sbin/autofs/auto_master.5
Normal file
272
usr.sbin/autofs/auto_master.5
Normal 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
107
usr.sbin/autofs/automount.8
Normal 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
345
usr.sbin/autofs/automount.c
Normal 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);
|
||||
}
|
103
usr.sbin/autofs/automountd.8
Normal file
103
usr.sbin/autofs/automountd.8
Normal 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.
|
498
usr.sbin/autofs/automountd.c
Normal file
498
usr.sbin/autofs/automountd.c
Normal 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);
|
||||
}
|
||||
|
88
usr.sbin/autofs/autounmountd.8
Normal file
88
usr.sbin/autofs/autounmountd.8
Normal 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.
|
351
usr.sbin/autofs/autounmountd.c
Normal file
351
usr.sbin/autofs/autounmountd.c
Normal 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
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
112
usr.sbin/autofs/common.h
Normal 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
270
usr.sbin/autofs/defined.c
Normal 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
196
usr.sbin/autofs/log.c
Normal 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
191
usr.sbin/autofs/popen.c
Normal 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
57
usr.sbin/autofs/token.l
Normal 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; }
|
||||
%%
|
Loading…
Reference in a new issue