#
#  Copyright Red Hat Inc., 2002
#  Copyright Mission Critical Linux, 2000
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2, or (at your option) any
#  later version.
#
#  This program is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; see the file COPYING.  If not, write to the
#  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
#  MA 02139, USA.
#

# $Revision: 1.11 $
#
# Author: Tim Burke <tburke at redhat.com>

#
# Shell library for Samba functions.
#
typeset SAMBA_CONFIG_DIR=/etc/samba
typeset SMBD_COMMAND=/usr/sbin/smbd
typeset NMBD_COMMAND=/usr/sbin/nmbd
typeset KILLALL_COMMAND=/usr/bin/killall
typeset SAMBA_PID_DIR=/var/run/samba
typeset SAMBA_LOCK_DIR=/var/cache/samba

#
# Called to stop, start or status check a Samba services.
#
startStopSamba()
{
	# cloned from startFilesystem
	if [ $# -ne 2 ]; then
	  logAndPrint $LOG_ERR "Usage: startStopSamba command svcID"
	fi

	typeset command=$1
	typeset svcID=$2

	typeset svc_name=$(getSvcName $DB $svcID) > /dev/null

	# This routine is called on the start of ALL services.
	# First verify if this service has any Samba shares, if not
	# there's nothing to do so simply return success.
	is_samba_service $svcID 
	if [ $? -ne $YES ]; then
	    return $SUCCESS
	fi

	# Call share_service to do the real work.
	share_service $command $svcID
	if [ $? -ne $SUCCESS ]; then
	    return $FAIL
	fi

	return $SUCCESS
}

#
# Samba action serviceID
#
samba()
{

	if [ $# -ne 2 ]; then
	  logAndPrint $LOG_ERR "Usage: samba [start, stop, status] serviceID"
	  return $FAIL
	fi

	typeset action=$1
	typeset svcID=$2

	case "$SVC_ACTION" in
	'start')
	  startStopSamba "start" $svcID
	  return $?
	  ;;

	'stop')
	  startStopSamba "stop" $svcID
	  return $?
	  ;;

	'status')
	  startStopSamba "status" $svcID
	  return $?
	  ;;
	esac
}

#
# See if this service has Samba enabled
#
is_samba_service()
{
	typeset svcID=$1
	typeset tokenlist

	tokenlist=$(getTokenList $DB \
	        "$SVC_SERVICES_LIST_STR$SEP$SVC_SERVICE_STR$svcID$SEP$SVC_DEVICE_STR[0-9]+" \
		"$SVC_DEVICE_SHARE_NAME")
	[ -n "$tokenlist" ] && return $YES
	return $NO
}

#
# See if this service has Samba enabled
#
share_service()
{
	typeset command=$1
	typeset svcID=$2
	typeset svc_name=$(getSvcName $DB $svcID) > /dev/null
	typeset token tokenlist

	#
	# Loop through all of the devices and see if any of them
	# have a non-None specification which enables Samba.
	#
	tokenlist=$(getSvcDeviceTokenList $DB $svcID)
	for token in $tokenlist; do
		#
		# We loop on devices looking for devices that have non-None samba
		#
		typeset share_name
		share_name=$(getSvcShareName $DB $token) > /dev/null
		[ $? -eq $SUCCESS ] || continue

		if [ "$share_name" = "None" ]; then
			logAndPrint $LOG_DEBUG \
				"share_service: service=$svc_name, is None"
	      		return $SUCCESS
		fi

		if [ "$share_name" = "none" ]; then
			logAndPrint $LOG_DEBUG \
				"share_service: service=$svc_name, is none"
			return $SUCCESS
		fi

	        share_start_stop $command $svcID "$share_name"
	        if [ $? -ne $SUCCESS ]; then
			# XXX - proceed to other shares?
			return $FAIL
		fi
	done

	return $SUCCESS
}


# 
# Initiates the work of starting or stopping an individual share.
# Also called for status checks.
#
share_start_stop()
{
	typeset command=$1
	typeset svcID=$2
	typeset shareName=$3
	typeset ret=$SUCCESS

	typeset svc_name=$(getSvcName $DB $svcID) > /dev/null
	typeset configFile="$SAMBA_CONFIG_DIR/smb.conf.$shareName"


	if ! [ -f "$configFile" ]; then
		if [ "$command" != "stop" ]; then
			# Can't start without a config file.
			logAndPrint $LOG_ERR \
"share_start_stop: Samba configuration file $configFile does not exist."
			return $FAIL
		else 
			# Go ahead and kill the daemons anyway, if they're
			# present.  This can be done regardless of the
			# existence of the config files.
			logAndPrint $LOG_WARNING \
"share_start_stop: Samba configuration file $configFile does not exist."
		fi
	fi
	#
	# Specify daemon options
	# -D = spawn off as separate daemon
	# -s = the following arg specifies the config file
	#
	typeset smbd_options=" -D -s "
	typeset nmbd_options=" -D -s "

	#
	# On clusters with multiple samba shares, we need to ensure (as much
	# as possible) that each service is advertised as a separate netbios
	# name.
	#
	# Generally, the admin sets this in smb.conf.sharename - but since
	# it is not required, we need another option.  Consequently, we use
	# the first part of the hostname associated with the first IP address
	# in a service.  Failing that, we use the IP address itself...
	#
	if [ -f "$configFile" ]; then
		grep -qe "^\([[:space:]]\+n\|n\)etbios[[:space:]]\+name[[:space:]]*=[[:space:]]*[[:alnum:]]\+" "$configFile"
		if [ $? -ne 0 ]; then

			typeset first_ip tl t
			tl=$(getSvcIPaddressTokenList $DB $svcID)
			for t in $tl; do
				first_ip=$(getSvcIPaddress $DB $t)
				[ -z "$first_ip" ] || break
			done

			typeset netbios_name=`$FINDHOST -i $first_ip | cut -f1 -d.`
			if [ -z "$netbios_name" ]; then
				netbios_name=$first_ip
			fi

			logAndPrint $LOG_DEBUG "Using $netbios_name as NetBIOS name for share $shareName (service $svc_name)"
			nmbd_options=" -n $netbios_name $nmbd_options"
		fi
	fi
	
	typeset smbd_command="$SMBD_COMMAND$smbd_options$configFile"
	typeset nmbd_command="$NMBD_COMMAND$nmbd_options$configFile" 
        case "$command" in
            start) 
		mkdir -p "$SAMBA_PID_DIR/$shareName"
		mkdir -p "$SAMBA_LOCK_DIR/$shareName"
		# Kick off the per-service smbd
		${smbd_command}
		ret_val=$?
		if [ $ret_val -ne 0 ]; then
	  	    logAndPrint $LOG_ERR "Samba service failed: $smbd_command"
	  	    return $FAIL
		else 
	  	    logAndPrint $LOG_DEBUG "Samba servicesucceeded: $smbd_command"
		fi
		# Kick off the per-service nmbd
		${nmbd_command}
		ret_val=$?
		if [ $ret_val -ne 0 ]; then
	  	    logAndPrint $LOG_ERR "Samba service failed: $nmbd_command"
	  	    return $FAIL
		else 
	  	    logAndPrint $LOG_DEBUG "Samba service succeeded: $nmbd_command"
		fi
		;;
            stop)     
		kill_daemon_by_arg "nmbd" $command "$configFile"
		kill_daemon_by_arg "smbd" $command "$configFile"
		if [ "$SAMBA_PID_DIR/$shareName" != "/" ]; then
			pushd "$SAMBA_PID_DIR" &> /dev/null
			rm -rf "$shareName"
			popd &> /dev/null
		fi
		if [ "$SAMBA_LOCK_DIR/$sharename" != "/" ]; then
			pushd "$SAMBA_LOCK_DIR" &> /dev/null
			rm -rf "$shareName"
			popd &> /dev/null
		fi
		;;
            status)     
		kill_daemon_by_arg "nmbd" $command "$configFile"
		if [ $? -ne $SUCCESS ]; then
	            logAndPrint $LOG_ERR \
		    "share_start_stop: nmbd for service $svc_name died!"
		    return $FAIL
		fi
		kill_daemon_by_arg "smbd" $command "$configFile"
		if [ $? -ne $SUCCESS ]; then
	            logAndPrint $LOG_ERR \
		    "share_start_stop: nmbd for service $svc_name died!"
		    return $FAIL
		fi
	esac
	return $ret
}


#
# Killing off the samba daemons was miserable to implement, merely
# because killall doesn't distinguish by program commandline.
# Consequently I had to implement these routines to selectively pick 'em off.

#
# Kills of either the {smbd|nmbd} which is running and was started with
# the specified argument.  Can't use `killall` to do this because it
# doesn't allow you to distinguish which process to kill based on any
# of the program arguments.
#
# This routine is also called on "status" checks.  In this case it doesn't
# actually kill anything.
#
# Parameters:
#       daemonName - daemon name, can be either smbd or nmbd
#	command - [stop|start|status]
#       arg     - argument passed to daemon.  In this case its not the
#                 full set of program args, rather its really just the
#                 samba config file.
#
# Returns: 0 - success (or the daemon isn't currently running)
#          1 - failure
#
kill_daemon_by_arg()
{
    typeset daemonName=$1
    typeset command=$2
    typeset arg=$3
    # Create a unique temporary file to stash off intermediate results
    typeset tmpfile_str=/tmp/sambapids.XXXXXX
    typeset tmpfile
    tmpfile=$(mktemp $tmpfile_str); ret_val=$?

    if [ -z "$tmpfile" ]; then
	logAndPrint $LOG_ERR "kill_daemon_by_arg: Can't create tmp file"
	return $FAIL
    fi

    # Mumble, need to strip off the /etc/samba portion, otherwise the
    # grep pattern matching will fail.
    typeset confFile="$(basename $arg)"

    # First generate a list of candidate pids.
    pidof $daemonName > $tmpfile
    if [ $? -ne 0 ]; then
	logAndPrint $LOG_DEBUG "kill_daemon_by_arg: no pids for $daemonName"
        rm -f $tmpfile
	case "$action" in
	'stop')
	  return $SUCCESS
	  ;;
	'status')
	  return $FAIL
	  ;;
	esac
	return $SUCCESS
    fi

    # If you don't find any matching daemons for a "stop" operation, thats
    # considered success; whereas for "status" inquiries its a failure.
    case "$action" in
    'stop')
      typeset ret=$SUCCESS
      ;;
    'status')
      typeset ret=$FAIL
      ;;
    esac
    #
    # At this point tmpfile contains a set of pids for the corresponding
    # {smbd|nmbd}.  Now look though this candidate set of pids and compare
    # the program arguments (samba config file name).  This distinguishes
    # which ones should be killed off.
    #
    typeset daemonPid=""
    for daemonPid in $(cat $tmpfile)
    do
	typeset commandLine=$(cat /proc/$daemonPid/cmdline)
    	typeset confBase="$(basename $commandLine)"
	if [ "$confBase" = "$confFile" ]; then
	    case "$action" in
	    'status')
    	      rm -f $tmpfile
	      return $SUCCESS
	      ;;
	    esac
	    kill_daemon_pid $daemonPid
	    if [ $? -ne $SUCCESS ]; then
		ret=$FAIL
	        logAndPrint $LOG_ERR \
		"kill_daemon_by_arg: kill_daemon_pid $daemonPid failed"
	    else
	        logAndPrint $LOG_DEBUG \
		 "kill_daemon_by_arg: kill_daemon_pid $daemonPid success"
	    fi
	fi
    done
    rm -f $tmpfile
    return $ret
}
#
# Kill off the specified PID
#
kill_daemon_pid() 
{
    typeset pid=$1
    typeset retval=$SUCCESS

	
    kill -TERM $pid
    if [ $? -eq 0 ]; then
	logAndPrint $LOG_DEBUG "Samba: successfully killed $pid"
    else
        logAndPrint $LOG_ERR "Samba: failed to kill $pid"
	retval=$FAIL
    fi
    return $retval
}

