#!/bin/bash
#
# osx_automount
# Custom script to start/stop/restart automount processes
#
# This script is distributed under the MIT LICENSE
#
# Copyright (c) 2004-2006 by Brian R. Chapados <chapados@sciencegeeks.org> 
#
# Permission is hereby granted, free of charge, to any person obtaining a copy 
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights 
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
# copies of the Software, and to permit persons to whom the Software is furnished 
# to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
###########################################################################
# Notes about this script 
#
# Handles:
#  * converting Sun/Linux NIS format automount maps to apple format
#  * creating directories necessary for automount processes
#  * start automount processes
#  * stop automount processes
#  * gracefully (as possible) restart automount processes
#  * tries to handle reloaded updated automount maps
#
# How to use this script:
# 
# version history:
# 27-May-2004 v0.1d initial version created
# 20-Jul-2004 v0.2d cleanup initial version, removed the use of nullmount.kext
#                   -replaced nullmounts with symlinks
#                   -gave a copy to TSRI Research Computing via Bill Young
# 11-Oct-2004 v0.3d changed location of automount mount points to simplify processing
#                   -removed dependencies on script to create symbolic links
#                   -as of 10.3.5, automount correctly handles mounting shares
#                    on the local machine
# 28-Oct-2004 v0.4d re-structured script to conform to a "typical" apple start-up script
#                   -include use of rc.common
#                   -add start/stop/restart functions
#                   -separate function to handle regenerating maps
#                   -can now be used in a cron job to auto-update maps 
#                   -added ConsoleMessage/echo statements for verbose output
#                   -removed support for static mounts
# 11-Feb-2005 v0.5d added support for -host option to automount exports via host
#                   -by default this script uses /nfs as the map root for the nfs hosts
#                   -*** seems unstable, generates time-outs, commenting out for now -host functionality ***
# 18-May-2007 v0.5.1 swapped the order of columns in AUTOMAP_TABLE_FILE to correspond to
#                    the output of ypcat -k auto.master
##

# you should be calling this file from:
# /System/Library/StartupItems/NFS/NFS

# declare global variables
MAPDIR="/etc/automount"
HOST_MOUNT_DIR="/nfs"
MNTDIR="/auto"
APPLE_STATIC_MOUNTS="static.mounts" # deprecated. no longer supported
AFMAP_ROOT="afmap"
AUTOMAP_TABLE_FILE="auto_master"
EXCLUDE_FILE="exclude"
AUTOMAP_TO_APPLEMAP="/etc/automount/automap2applemap"
AUTOMOUNT_PID_FILE="/var/run/automount.pid"
VERBOSE=0  # set to 1 for addition ConsoleMessage statements
DEBUG=0    # not used right now, but could be used to control debugging statements

# commands used in script
AUTOMOUNT_CMD="/usr/sbin/automount"
GREP_CMD="/usr/bin/grep"
DF_CMD="/bin/df"
MKDIR_CMD="/bin/mkdir"
CHMOD_CMD="/bin/chmod"
AWK_CMD="/usr/bin/awk"
KILLALL_CMD="/usr/bin/killall"
RM_CMD="/bin/rm"
OUTPUT_CMD="ConsoleMessage"
PS_CMD="/bin/ps"
CMP_CMD="/usr/bin/cmp"
OUTPUT_CMD="echo"

# source rc.common, which is used in all startup scripts
. /etc/rc.common


RunService ()
{
    ####
    # Custom RunService method that overwrites the one in /etc/rc.common
    #   This version has an 'update' feature --> function UpdateService
    #   that updates any maps that are out-of-date and restarts the
    #   associated automount processes
    ####
    case $1 in
      start  ) StartService   ;;
      stop   ) StopService    ;;
      restart) RestartService ;;
      update ) UpdateService  ;;
      *      ) echo "$0: unknown argument: $1";;
    esac
}



is_running ()
{
    # check to see if automount processes are running
    # also check to see if AUTOMOUNT_PID_FILE exists

    RUNNING_P=0 # default to not running

    ${PS_CMD} -acux | ${GREP_CMD} "automount" > /dev/null 2>&1
    if [ "$?" -eq "0" ]; then
        RUNNING_P=1
    fi

    if [ -f ${AUTOMOUNT_PID_FILE} ]; then
        RUNNING_P=1
    fi

    return ${RUNNING_P}
}

StartService ()
{
    # Check to make sure automount is not running
    # before trying to start it.

    # exit now if automount is running
    is_running; RUNNING_P="$?"
    if [ "${RUNNING_P}" -eq "1" ]; then
        ${OUTPUT_CMD} "automount is currently running.  stop current processes before starting."
        exit 1
    fi
   
    # otherwise start automount processes:
    # The normal Apple NFS automount startup processes are:
    ####
    #  automount -m /Network -nsl
    #  automount -m /automount/Servers -fstab -mnt /private/Network/Servers \
    #            -m /automount/static -static -mnt /private/automount
    #  automount -m /automount/Servers -fstab -mnt /private/Network/Servers
    #  ln -s /automount/Servers /Network/Servers 
    ####
    # we leave out: '-m /automount/static -static -mnt /private/automount'
    # which disables automounting shares defined in NetInfo
    # it also prevents loading shares from mounts.byname if it exists
    ####

    # start with the Apple Network Shares
    ${AUTOMOUNT_CMD} -m /Network -nsl 

    # now mount network servers
    ${AUTOMOUNT_CMD} -m /automount/Servers -fstab -mnt /private/Network/Servers

    # start automount process using -host option
    # this doesn't rely on any maps, it just looks for hosts that are exporting nfs shares
    # unstable right now, commenting out
    #mkdir_if_not_exist ${HOST_MOUNT_DIR} 755
    #${AUTOMOUNT_CMD} -m ${HOST_MOUNT_DIR} -host

    # localize /Network -- I'm not sure what this does... BRC
    ln -s . /Network/.localized

    # now start the nfs automount processes
    load_staticmounts
    load_automount_maps

    return 0
}

StopService ()
{
    # check to make sure automount is running before trying to kill it
    is_running; RUNNING_P="$?"

    if [ "${RUNNING_P}" -eq "1" ]; then
        ${OUTPUT_CMD} "Stopping automount processes..."
        # kill automount processes
        ${KILLALL_CMD} automount
        
        # delete automount.pid file
        if [ -f ${AUTOMOUNT_PID_FILE} ]; then
            ${RM_CMD} -f ${AUTOMOUNT_PID_FILE}
        fi
    fi
          
    return 0
}

RestartService ()
{
    # regenerate maps restart all automount processes
    # by sending the HUP signal (SIGHUP)
    # This will cause new automount maps to be re-loaded
    ${OUTPUT_CMD} "Generating new automount maps..."
    regenerate_maps # generate new automount maps
    ${OUTPUT_CMD} "Restarting automount processes..."
    ${KILLALL_CMD} -SIGHUP automount  # restart automount processes
    
    return 0
}

is_map_current()
{
    ####
    # Args: $1 --> map node to check 
    #       $2 --> automap name to use (i.e. auto.asd, auto.home)
    #
    # Returns: 0 = No 
    #          1 = Yes
    ####
    ${AUTOMAP_TO_APPLEMAP} -m ${2} | ${CMP_CMD} -s - ${AFMAP_ROOT}.${1} > /dev/null 2>&1
    return $?
}

UpdateService ()
{
    ####
    # Check to make sure that all maps are to date
    # If not, then update the out-of-date map and restart that process
    # Leave up-to-date maps/processes alone
    ####

    SERVICE_UPDATED_P=0  # flag to indicate whether anything was actually updated

    verbose_ConsoleMessage "Updating automount maps..."

    for automap_info in `${AWK_CMD} '{printf "%s#%s\n",$1,$2}' ${MAPDIR}/${AUTOMAP_TABLE_FILE}`; do
        automap=`echo "${automap_info}" | awk -F# '{print $2}'`
        map_node=`echo "${automap_info}" | awk -F# '{print $1}'`
        MAP_NODE=${map_node#/}

        # check to see if this map is up-to-date
        # map_current_p = 0 if current, 1 if not current 
        is_map_current ${MAP_NODE} ${automap}
        map_current_p="$?"

        if [ "${map_current_p}" -eq "1" ]; then
            # map is not current, generate new map
            AF_MAP="${AFMAP_ROOT}.${MAP_NODE}"
            ${AUTOMAP_TO_APPLEMAP} -m ${automap} -e ${MAPDIR}/${EXCLUDE_FILE} ${MAP_NODE} > ${MAPDIR}/${AF_MAP}
            ConsoleMessage "Updated automount map for ${MAP_NODE}."

            # restart the automount process that handles this map
            restart_automount ${MAP_NODE}

            SERVICE_UPDATED_P=1
        fi
    done

    if [ "${SERVICE_UPDATED_P}" -eq "1" ]; then
        ${OUTPUT_CMD} "Automount maps updated."
    else
        ${OUTPUT_CMD} "Automount maps are up-to-date."
        verbose_ConsoleMessage "Automount maps are up-to-date."
    fi

    return 0
}

 
restart_automount ()
{
    ####
    # Args: $1 -> current MAP_NODE (i.e. home, asd)
    #
    # Restart the automount process that handles the current map node 
    #
    # Returns 0 --> successful restart
    #        >0 --> error
    ####
    
    # get id of the automount process controlling the map
    automount_pid=`${PS_CMD} -x | ${GREP_CMD} "${AUTOMOUNT_CMD}" | grep "${AFMAP_ROOT}.${1}" | awk '{print $1}'`
    
    if [ "${automount_pid}" -gt "0" ]; then
        # restart the process (kill -SIGHUP)
        kill -HUP ${automount_pid}
        ${OUTPUT_CMD} "Restarted automount process for $1 [${automount_pid}]."
        ConsoleMessage "Restarted automount process for $1 [${automount_pid}]."
        return $?
    else
        # no process
        ${OUTPUT_CMD} "No automount process for $1 is currently running."
        return 1
    fi
}
    
load_staticmounts()
# deprecated.  This function is never called.  This script no longer handles static mount points.
{
    # mount static maps
    # if they are not already mounted
    ${DF_CMD} | ${GREP_CMD} "/automount/static" > /dev/null 2>&1
    if [ "$?" -ne "0" ]; then
        ${AUTOMOUNT_CMD} -m /automount/static ${MAPDIR}/${APPLE_STATIC_MOUNTS} -static -mnt /private/var/automount/static
    fi
}

verbose_ConsoleMessage ()
{
    ####
    # Args: $1 --> message to print
    #
    # writes a ConsoleMessage if global VERBOSE variable is set to "1" (ON)
    # otherwise exits silently
    ####
    if [ "${VERBOSE}" -eq "1" ]; then
        ConsoleMessage "${1}"
    fi

    return 0
}

mkdir_if_not_exist()
{
    # takes target_dir = $1, target_mode = $2  as input
    # makes a directory if it doesn't already exist
    # sets mode to target_mode
    target_dir=$1
    target_mode=$2

    if [ ! -d ${target_dir} ]; then
        ${MKDIR_CMD} -p ${target_dir} 
        ${CHMOD_CMD} ${target_mode} ${target_dir}
    fi
}

regenerate_maps()
{
    # regenerate automount maps using the lasted NIS maps
    for automap_info in `${AWK_CMD} '{printf "%s#%s\n",$1,$2}' ${MAPDIR}/${AUTOMAP_TABLE_FILE}`; do
        automap=`echo "${automap_info}" | awk -F# '{print $2}'`
        map_node=`echo "${automap_info}" | awk -F# '{print $1}'`
        MAP_NODE=${map_node#/}

        # convert autofs map to apple automount map
        AF_MAP="${AFMAP_ROOT}.${MAP_NODE}"
        ${AUTOMAP_TO_APPLEMAP} -m ${automap} -e ${MAPDIR}/${EXCLUDE_FILE} ${MAP_NODE} > ${MAPDIR}/${AF_MAP}
    done
}

load_automount_maps()
{ 
# process entries in the automount master file
for automap_info in `${AWK_CMD} '{printf "%s#%s\n",$1,$2}' ${MAPDIR}/${AUTOMAP_TABLE_FILE}`
do
    automap=`echo "${automap_info}" | awk -F# '{print $2}'`
    map_node=`echo "${automap_info}" | awk -F# '{print $1}'`
    MAP_NODE=${map_node#/}

    # convert autofs map to apple automount map
    AF_MAP="${AFMAP_ROOT}.${MAP_NODE}"
    ${AUTOMAP_TO_APPLEMAP} -m ${automap} -e ${MAPDIR}/${EXCLUDE_FILE} ${MAP_NODE} > ${MAPDIR}/${AF_MAP}

    mkdir_if_not_exist /${MAP_NODE} 755
    mkdir_if_not_exist ${MNTDIR} 755
    mkdir_if_not_exist ${MNTDIR}/${MAP_NODE} 755

    # start an automount process for this mount point
    ConsoleMessage "automounting /${MAP_NODE}"
    ${AUTOMOUNT_CMD} -m /${MAP_NODE} ${MAPDIR}/${AF_MAP} -mnt ${MNTDIR}/${MAP_NODE} &

done
}

RunService "$1"
