#!/bin/bash
#
# $Id: rc.autofs.in,v 1.15 2004/05/10 13:57:14 raven Exp $
#
# rc file for automount using a Sun-style "master map".
# We first look for a local /etc/auto.master, then a YP
# map with that name
#
# On most distributions, this file should be called:
# /etc/rc.d/init.d/autofs or /etc/init.d/autofs
#

# For Redhat-ish systems
#
# chkconfig: 345 28 72
# processname: /usr/sbin/automount
# config: /etc/auto.master
# description: Automounts filesystems on demand

# This is used in the Debian distribution to determine the proper
# location for the S- and K-links to this init file.
# The following value is extracted by debstd to figure out how to
# generate the postinst script. Edit the field to change the way the
# script is registered through update-rc.d (see the manpage for
# update-rc.d!)
#
FLAGS="defaults 21"

#
# Location of the automount daemon and the init directory
#
DAEMON=/usr/sbin/automount
prog=`basename $DAEMON`
initdir=/etc/rc.d/init.d

#
# Determine which kind of configuration we're using
#
system=redhat

if [ $system = redhat ]; then
    . $initdir/functions
fi

test -e $DAEMON || exit 0

if [ $system = debian ]; then
    thisscript="$0"
    if [ ! -f "$thisscript" ]; then
        echo "$0: Cannot find myself" 1>&2
        exit 1
    fi
fi

PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH

#
# Define custom options in /etc/sysconfig/autofs
#
LOCALOPTIONS=""
DAEMONOPTIONS=""
UNDERSCORETODOT=1
DISABLE_DIRECT=1
ONE_AUTO_MASTER=0
DAEMON_EXIT_WAIT=34
GHOSTDIRS=""
LDAPAUTOMASTER=""
BASEDN=""
OLD_LDAP_LOOKUP=1

. /etc/sysconfig/autofs

#
# Init script options: forcestart and forcerestart
#
# A forced startup (or restart) will unlink umount all mounts
# under the autofs managed mount point so, in this case, we
# require that the init script be manually invoked by the admin
# rather than use a configuration option.
#
FORCE_START=0

#
# Check for all maps that are to be loaded
#
function getschemes()
{
    if ! grep -q ^automount: /etc/nsswitch.conf; then
	echo "files"
    else
	grep ^automount: /etc/nsswitch.conf | sed -e 's/^.*://' -e 's/\[.*\]/ /g'
    fi
}
function catnismap()
{
    if [ -z "$1" ] ; then
        map="auto_master"
    else
        map="$1"
    fi
    /usr/bin/ypcat -k "$map" 2> /dev/null | sed -e '/^#/d' -e '/^$/d'
}
function getfilemounts()
{
    if [ -f /etc/auto.master ] ; then
        cat /etc/auto.master | sed -e '/^#/d' -e '/^$/d' | (
        while read auto_master_in
        do
            if [ "`echo $auto_master_in | grep '^+'`" = "" ]; then
                echo $auto_master_in
            else
		mapname=`echo $auto_master_in | sed -e 's/\+//g'`
		expanded_mapname=`/usr/lib64/autofs/nsswitch $mapname 2>/dev/null`
		expanded_mapname=`echo $expanded_mapname | sed -e 's/:/ /g'`
		: $DAEMON -D /bogus $expanded_mapname
		$DAEMON -D /bogus $expanded_mapname
            fi
        done
        )
    fi
}
function getnismounts()
{
    YPMAP=`catnismap auto.master`
    if [ -z "$YPMAP" ]; then
       catnismap
    else
       catnismap auto.master
    fi
}
function getldapmounts()
{
    if [ -x /usr/lib64/autofs/autofs-ldap-auto-master ]; then
        # Put quotes around the BASEDN so that autofs-ldap-auto-master
        # treats it as a single argument with embedded spaces.
	if [ -n "$BASEDN" ]; then
		basednopt="-b"
	else
		basednopt=""
	fi
        /usr/lib64/autofs/autofs-ldap-auto-master $LDAPAUTOMASTER \
	    $basednopt "$BASEDN" 2> /dev/null
    fi
}
function getrawmounts()
{
    for scheme in `getschemes` ; do
        case "$scheme" in
            files)
                if [ -z "$filescheme" ] ; then
                    FILEMOUNTS=`getfilemounts`
		    if [ -n "$FILEMOUNTS" -o -f /etc/auto.master ] && [ "$ONE_AUTO_MASTER" -eq 1 ];
		    then
			getfilemounts
			return
		    fi
		    getfilemounts
                    filescheme=1
                    export filescheme
                fi
                ;;
            nis)
                if [ -z "$nisscheme" ] ; then
                    NISMOUNTS=`getnismounts`
		    if [ -n "$NISMOUNTS" ] && [ "$ONE_AUTO_MASTER" -eq 1 ];
		    then
			getnismounts
			return
		    fi
		    getnismounts
                    nisscheme=1
                    export nisscheme
                fi
                ;;
            ldap*)
                if [ -z "$ldapscheme" ] ; then
                    LDAPMOUNTS=`getldapmounts`
		    if [ -n "$LDAPMOUNTS" ] && [ "$ONE_AUTO_MASTER" -eq 1 ];
		    then
			getldapmounts
			return
		    fi
		    getldapmounts
                    ldapscheme=1
                    export ldapscheme
                fi
                ;;
	    \#*)
		return
        esac
    done
}


#
# This function will build a list of automount commands to execute in
# order to activate all the mount points. It is used to figure out
# the difference of automount points in case of a reload
#
function getmounts()
{
	local LC_ALL=C
	export LC_ALL
	knownmaps=" "
	ghostdirs="$(echo "$GHOSTDIRS" | sed 's/ /\|/g')"

	getrawmounts | (
	while read dir map options
	do
	    # These checks screen out duplicates and skip over directories
	    # where the map is '-'.
	    # We can't do empty or direct host maps, so don't bother trying.

	    # Strip trailing slashes from the dir line if it exists to aid
	    # in checking for duplicate maps
	    dir=`echo "$dir" | sed -e "s/\/*$//"`

	    if [ ! -z "$map" -a "$map" = "-hosts" ] ; then
		continue
	    fi
	    if [ $DISABLE_DIRECT -eq 1 \
			-a x`echo $dir | grep -E "^/-"` != 'x' ]
	    then
		continue
	    fi

	    # Do not include a map if it is a duplicate, maps on top of
	    # another map or another map maps on top of it, unless it's
	    # a direct map.
	    if [ x`echo $dir | grep -E "^/-"` == 'x' ]; then
		for knownmap in $knownmaps
		do
		    if [ "`echo $dir/ | grep ^$knownmap`" != "" \
			    -o "`echo $knownmap | grep ^$dir/`" != "" \]
		    then
			continue 2
		    fi
		done
	    fi

	    if [ "$map" == "-null" ]; then
		knownmaps=" $dir/ $knownmaps"
		continue
	    fi

	    if [ ! -z "$dir" -a ! -z "$map" \
			-a x`echo "$map" | cut -c1` != 'x-' ]
	    then
		# If the options include a -t or --timeout, a -g or --ghost,
		# a -v or --verbose or a -d or --debug paramter, then pull
		# those particular options out.
		: echo DAEMONOPTIONS OPTIONS $DAEMONOPTIONS $options
		startupoptions=
		if echo "$DAEMONOPTIONS $options" | grep -qE -- '-(t|-timeout)[[:space:]=]*[0-9]+' ;
		then
		    startupoptions="--timeout=$(echo $DAEMONOPTIONS $options |\
			sed 's/.*-\(t\|-timeout\)[ \t=]*\([0-9]\+\).*$/\2/g')"
		fi

		# Check for the ghost option
		if echo "$DAEMONOPTIONS $options" | grep -qE -- '\B-(g\b|-ghost\b)' ;
		then
		    startupoptions="$startupoptions --ghost"
		fi
		# Dont even deal with conflicts between --ghost and [no]browse
		# Its just insane to configure things like that.
		if echo "$options" | grep -q 'browse' ;
		then
		    if echo "$options" | grep -qE -- '^[	 ]*-browse' ||
		       echo "$options" | grep -q -- ',browse' ;
		    then
			startupoptions="$startupoptions --ghost"
		    fi
		fi
		# check list of directories to ghost
		if echo "$dir" | grep -qE -- "^($ghostdirs)\$" ;
		then
		    echo "$startupoptions" | grep -qE -- "--ghost"
		    if [ $? -eq 1 ]; then
			startupoptions="$startupoptions --ghost"
		    fi
		fi
		# Check for verbose
		if echo "$DAEMONOPTIONS $options" | \
					grep -qE -- '\B-(v\b|-verbose\b)' ;
		then
		    startupoptions="$startupoptions --verbose"
		fi

		# Check for debug
		if echo "$DAEMONOPTIONS $options" | \
					grep -qE -- '\B-(d\b|-debug\b)' ;
		then
		    startupoptions="$startupoptions --debug"
		fi

		# Check for lstat on expire
		if echo "$DAEMONOPTIONS $options" | \
					grep -qE -- '\B-(L\b|-use-lstat-on-expire\b)' ;
		then
		    startupoptions="$startupoptions --use-lstat-on-expire"
		fi

		if echo "$DAEMONOPTIONS $options" | \
				grep -qE -- '--random-multimount-selection' ;
		then
		    startupoptions="$startupoptions --random-multimount-selection"
		fi

		if [ $FORCE_START == "1" ]
		then
		    startupoptions="$startupoptions --force"
		fi

		if [ $OLD_LDAP_LOOKUP == "1" ]; then
		    startupoptions="$startupoptions --use-old-ldap-lookup"
		fi

		# --negative-timeout=N
		if echo "$DAEMONOPTIONS $options" | grep -qE -- '--negative-timeout[[:space:]=]*[0-9]+' ;
		then
		    startupoptions="$startupoptions --negative-timeout=$(echo $DAEMONOPTIONS $options |\
			sed 's/.*--negative-timeout[ \t=]*\([0-9]\+\).*$/\1/g')"
		fi

		# Other option flags are intended for maps.
		mapoptions="$(echo "$DAEMONOPTIONS $options" |\
		      sed   's/--negative-timeout[ \t=]*\([0-9][0-9]*\)//g' |
		      sed   's/-\(t[^0-9]*\|-timeout\)[ \t=]*\([0-9][0-9]*\)//g' |
		      sed   's/-\(g\b\|-ghost\b\)//g' |
		      sed   's/-\(v\b\|-verbose\b\)//g' |
		      sed   's/-\(d\b\|-debug\b\)//g' |
		      sed   's/-\(L\b\|-use-lstat-on-expire\b\)//g' |
		      sed   's/--\(no\)\?browse\b//g' |
		      sed   's/,\b\(no\)\?browse\b//g' |
		      sed   's/-\(no\)\?browse,/-/g' |
		      sed   's/[	 ]\?-\(no\)\?browse//g' |
		      sed   's/--force//g' |
		      sed   's/--random-multimount-selection//g' )"

		# Break up the maptype and map, if the map type is specified
		maptype=`echo $map | cut -f1 -d:`
		# Handle degenerate map specifiers
		if [ "$maptype" = "$map" ] ; then
		    if [ "$map" = "hesiod" -o "$map" = "userhome" ] ; then
			maptype=$map
			map=
		    elif [ "$map" = "multi" ] ; then
			maptype=$map
			map=
		    else
			if [ "$UNDERSCORETODOT" = "1" ]; then
			    nsswitch_opts="-u"
			else
			    nsswitch_opts=
			fi
			nsswitch_output=`/usr/lib64/autofs/nsswitch $nsswitch_opts $map 2>/dev/null`
			if [ $? -eq 0 ]; then
			    maptype=`echo $nsswitch_output | cut -d: -f 1`
			    map=`echo $nsswitch_output | cut -d: -f 2`
			else
			    continue;
			fi
		    fi
		fi
		map=`echo $map | cut -f2- -d:`

		: echo STARTUPOPTIONS $startupoptions
		: echo DIR $dir
		: echo MAPTYPE $maptype
		: echo MAP $map
		: echo MAPOPTIONS $mapoptions
		: echo LOCALOPTIONS $LOCALOPTIONS

		# Multimap entries are of the form:
		#     maptype mapname <other stuff>
		# and each entry is separated by a '--' delimeter.
		# We need to parse that format out so that we can apply
		# the underscore to dot translation on each map name.
		NEWOPTIONS=""
		multi_mapname=""
		multi_maptype=""
		for m in $mapoptions
		do
		    if [ -z "$multi_mapname" ]; then
			if [ -z "$multi_maptype" ]; then
			    multi_maptype=$m
			    NEWOPTIONS="$NEWOPTIONS $m"
			else
			    if [ "$UNDERSCORETODOT" == "1" ]; then
				multi_mapname=`echo $m | sed -e 's/_/\./g'`
				NEWOPTIONS="$NEWOPTIONS $multi_mapname"
			    else
				multi_mapname=$m
				NEWOPTIONS="$NEWOPTIONS $m"
			    fi
			fi
			continue
		    fi

		    if [ x"$m" = x"--" ]; then
			NEWOPTIONS="$NEWOPTIONS $LOCALOPTIONS --"
			multi_mapname=
			multi_maptype=
		    else
			NEWOPTIONS="$NEWOPTIONS $m"
		    fi
		done
		mapoptions=$NEWOPTIONS

		echo "$DAEMON $startupoptions $dir $maptype $map $mapoptions $LOCALOPTIONS" | sed -e 's/	/ /g' -e 's/  */ /g'

		: echo ------------------------
	    knownmaps=" $dir/ $knownmaps"
	    fi
	done
    )
}

#
# Status lister.
#
function status()
{
	echo -e $"Configured Mount Points:\n------------------------"
	getmounts
	echo ""
	echo -e $"Active Mount Points:\n--------------------"
	ps axwww|grep "[0-9]:[0-9][0-9] $DAEMON " | (
		while read pid tt stat time command; do echo $command; done
	)
}

# return true if at least one pid is alive
function alive()
{
    if [ -z "$*" ]; then
	return 1
    fi
    for i in $*; do
	if kill -0 $i 2> /dev/null; then
	    return 0
	fi
    done

    return 1
}

#
# Find pid of deepest nested submount
#
function get_deepest_submount()
{
	ps axw|grep $DAEMON|\
	   grep submount|\
	   sed -e 's/--timeout[ =][0-9]*//g'|\
	   awk '{print $7,$1}'|\
	   sort -r|\
	   awk 'BEGIN {ORS=" "} {print $2}'|\
	   awk '{print $1}'
}

#
# Signal each nested submount
#
function signal_submounts()
{
	tries=0
	pid=`get_deepest_submount`
	while [ -n "$pid" -a $tries -lt 8 ]; do
		oldpid=$pid
		kill -USR2 $pid 2> /dev/null
		usleep 300000
		pid=`get_deepest_submount`
		if [ "$oldpid" = "$pid" ]; then
			tries=`expr $tries + 1`
		else
			tries=0
		fi
	done
	echo $pid
}


#
# Signal each automount
#
function signal_automounts()
{
	local STARTTIME=`date +"%s"`

	if [ -z "`signal_submounts`" ]; then
	    while `expr \`date +"%s"\` - $STARTTIME \\< $DAEMON_EXIT_WAIT >&/dev/null`; do
		pids="`/sbin/pidof $DAEMON`"
		[ -z "$pids" ] && break;

		for one_pid in $pids ; do
			kill -USR2 $one_pid 2> /dev/null
		done
		usleep 200000
	    done
	fi

	if [ -n "`/sbin/pidof $DAEMON`" ] ; then
	    return 1
	fi

	return 0
}

function umount_loopback()
{
    loops=`LC_ALL=C awk '!/^#/ && $1 ~ /^\/dev\/loop/ && $2 != "/" {print $2}' /proc/mounts`
    automounts=`LC_ALL=C awk '!/^#/ && $1 ~ /^automount/ {print $2}' /proc/mounts`

    for l in $loops
    do
	# If a loop device is mounted into a subdir of the automount point,
	# umount it here.
	for a in $automounts
	do
	    match=`echo $l | grep -E "^$a[\$|/]"`
	    if [ -n "$match" ]; then
		echo ""
		echo -n $"Unmounting loopback filesystem $match:  "
		loopdev=`LC_ALL=C grep $l /proc/mounts | awk '{print $1}'`
		umount -d $match
		if [ $? -eq 0 ]; then
		    echo "done"
		else
		    echo "failed ($?)"
		fi
	    fi
	done
    done

    # If the loop device file exists under the automount point, umount
    # it here.
    loopmnts=`LC_ALL=C awk '!/^#/ && $1 ~ /^\/dev\/loop/ && $2 != "/" {print $1}' /proc/mounts`
    for l in $loopmnts
    do
	loopmnt=`losetup $l | awk -F\( '{print $2}' | awk -F\) '{print $1}'`
	for a in $automounts
	do
	    match=`echo $loopmnt | grep -E "^$a[\$|/]"`
	    if [ -n "$match" ]; then
		echo ""
		echo -n $"Unmounting loopback filesystem $match:  "
		umount -d $match
		if [ $? -eq 0 ]; then
		    echo "done"
		else
		    echo "failed ($?)"
		fi
	    fi
	done

    done
}

# Redhat start/stop function.
#
function redhat()
{

#
# See how we were called.
#
case "$1" in
  start)
        # Make sure the autofs filesystem type is available.
        (grep -q autofs /proc/filesystems || /sbin/modprobe -k autofs4 || /sbin/modprobe -k autofs) 2> /dev/null
        echo -n $"Starting $prog: "
        TMP=`mktemp /tmp/autofs.XXXXXX` || { echo $"could not make temp file" >& 2; exit 1; }

	getmounts | tee $TMP | (
	    WARN=0
	    SUCCESS=0
	    while read cmd rest
	    do
		echo "$cmd $rest" | sh
		if [ $? -ne 0 ]; then
		    echo "failed to load map: $cmd $rest"
		    WARN=1
		else
		    SUCCESS=1
		fi
	    done;
	    [ $SUCCESS = 0 ] && exit 2
	    [ $WARN = 1 ] && exit 1
	    exit 0
	)
	RETVAL=$?
	if [ -s $TMP ]; then
	    if [ $RETVAL = 1 ]; then
		warning "$prog startup"
	    elif [ $RETVAL = 2 ]; then
		failure "$prog startup"
	    else
		success "$prog startup"
	    fi
	    [ $RETVAL != 2 ] && touch /var/lock/subsys/autofs
	else
	    echo -n $"No Mountpoints Defined"
	    success "$prog startup"
	    touch /var/lock/subsys/autofs
	fi
	rm -f $TMP
	echo
	;;
  stop)
	echo -n $"Stopping $prog:"
	if [ -z "`pidofproc $prog`" -a -z "`getmounts`" ]; then
		RETVAL=0
	else
		umount_loopback
		signal_automounts
		RETVAL=$?
	fi
	umount -a -f -t autofs
	rm -f /var/lock/subsys/autofs
	if [ -n "`/sbin/pidof $DAEMON`" ] ; then
	    failure "$prog shutdown"
	else
	    success "$prog shutdown"
	fi
	echo
	;;
  restart)
	redhat stop
	redhat start
	;;
  reload)
	if [ ! -f /var/lock/subsys/autofs ]; then
		echo $"$prog not running"
		RETVAL=1
		return
	fi
	echo $"Checking for changes to /etc/auto.master ...."
        TMP1=`mktemp /tmp/autofs1.XXXXXX` || { echo $"could not make temp file" >& 2; exit 1; }
        TMP2=`mktemp /tmp/autofs2.XXXXXX` || { echo $"could not make temp file" >& 2; exit 1; }
	getmounts | sed 's/"//g' >$TMP1
	ps axwww|grep "[0-9]:[0-9][0-9] $DAEMON " | (
	    while read pid tt stat time command; do
		echo "$command" >>$TMP2
		if ! grep -q "^$command" $TMP1; then
		    if ! echo "$command" | grep -q -e --submount; then
			kill -USR2 $pid 2> /dev/null
			echo $"Stop $command"
		    fi
		else
			kill -HUP $pid 2> /dev/null
			echo $"Reload map $command"
		fi
	    done
	)
	cat $TMP1 | ( while read x; do
		if ! grep -q "^$x" $TMP2; then
			echo $"Start $x"
			$x | sh -
		fi
        done )
	rm -f $TMP1 $TMP2
	;;
  status)
	status
	;;
  condrestart)
	[ -f /var/lock/subsys/autofs ] && redhat restart
        RETVAL=0
	;;
  forcestart)
	FORCE_START="1"
        redhat start
	;;
  forcerestart)
	FORCE_START="1"
        redhat restart
	;;
  *)
	echo $"Usage: $0 {start|forcestart|stop|restart|forcerestart|reload|condrestart|status}"
	RETVAL=0
esac
}

#
# Debian start/stop functions.
#
function debian()
{
#
# See how we were called.
#
case "$1" in
    start)
	echo -n 'Starting automounter:'
	getmounts | while read cmd rest
	do
		mnt=`echo $rest | sed 's/^.* \(\/[^ ]*\) [A-Za-z].*$/\1/'`
		echo -n " $mnt"
		pidfile=/var/run/autofs`echo $mnt | sed 's/\//./'`.pid
		start-stop-daemon --start --pidfile $pidfile --quiet \
			--exec $DAEMON $DAEMONOPTIONS -- $rest
	done
	echo "."
	;;
    stop)
	echo 'Stopping automounter.'
	umount_loopback
	start-stop-daemon --stop --quiet --signal USR2 --exec $DAEMON
	;;
    reload|restart)
	echo "Reloading automounter: checking for changes ... "
	TMP=/var/run/autofs.tmp
	getmounts >$TMP
	for i in /var/run/autofs.*.pid
	do
		pid=`head -n 1 $i 2>/dev/null`
		[ "$pid" = "" ] && continue
		command=`tail +2 $i`
		if ! grep -q "^$command" $TMP
		then
			echo "Stopping automounter: $command"
			kill -USR2 $pid 2> /dev/null
		else
			echo "Reloading automounter map: $command"
			kill -HUP $pid 2> /dev/null
		fi
	done
	rm -f $TMP
	$thisscript start
	;;
    status)
	status
	;;
    *)
	echo "Usage: $initdir/autofs {start|stop|restart|reload|status}" >&2
	exit 1
	;;
esac
}

RETVAL=0
if [ $system = debian ]; then
	debian "$@"
elif [ $system = redhat ]; then
	redhat "$@"
fi

exit $RETVAL
