#!/bin/sh
# ctdb event script for Samba

. $CTDB_BASE/functions

detect_init_style

case $CTDB_INIT_STYLE in
	suse)
		CTDB_SERVICE_SMB=${CTDB_SERVICE_SMB:-smb}
		CTDB_SERVICE_NMB=${CTDB_SERVICE_NMB:-nmb}
		CTDB_SERVICE_WINBIND=${CTDB_SERVICE_WINBIND:-winbind}
		;;
	debian)
		CTDB_SERVICE_SMB=${CTDB_SERVICE_SMB:-samba}
		CTDB_SERVICE_NMB=${CTDB_SERVICE_NMB:-""}
		CTDB_SERVICE_WINBIND=${CTDB_SERVICE_WINBIND:-winbind}
		;;
	*)
		# should not happen, but for now use redhat style as default:
		CTDB_SERVICE_SMB=${CTDB_SERVICE_SMB:-smb}
		CTDB_SERVICE_NMB=${CTDB_SERVICE_NMB:-""}
		CTDB_SERVICE_WINBIND=${CTDB_SERVICE_WINBIND:-winbind}
		;;
esac

service_name="samba"

loadconfig

ctdb_setup_service_state_dir

service_start ()
{
	# If set then we force-start the relevant service.
	_service_name="$1"

	# make sure samba is not already started
	if [ "$_service_name" = "samba" ] || \
	    is_ctdb_managed_service "samba" ; then
		service "$CTDB_SERVICE_SMB" stop > /dev/null 2>&1
		if [ -n "$CTDB_SERVICE_NMB" ] ; then
		    service "$CTDB_SERVICE_NMB" stop > /dev/null 2>&1
		fi
		killall -0 -q smbd && {
		    sleep 1
		    # make absolutely sure samba is dead
		    killall -q -9 smbd
		}

		killall -0 -q nmbd && {
		    sleep 1
		    # make absolutely sure samba is dead
		    killall -q -9 nmbd
		}
	fi

	# make sure winbind is not already started
	if [ "$_service_name" = "winbind" ] || \
	    check_ctdb_manages_winbind ; then
		service "$CTDB_SERVICE_WINBIND" stop > /dev/null 2>&1
		killall -0 -q winbindd && {
		    sleep 1
          	    # make absolutely sure winbindd is dead
		    killall -q -9 winbindd
		}

	fi

	# start the winbind service
	if [ "$_service_name" = "winbind" ] || \
	    check_ctdb_manages_winbind ; then
		service "$CTDB_SERVICE_WINBIND" start || {
		    echo failed to start winbind
		    exit 1
		}
	fi

	# start Samba service. Start it reniced, as under very heavy load 
	# the number of smbd processes will mean that it leaves few cycles for
	# anything else
	if [ "$_service_name" = "samba" ] || \
	    is_ctdb_managed_service "samba" ; then
		net serverid wipe

		if [ -n "$CTDB_SERVICE_NMB" ] ; then
		    nice_service "$CTDB_SERVICE_NMB" start || {
			echo failed to start nmbd
			exit 1
		    }
		fi
		nice_service "$CTDB_SERVICE_SMB" start || {
			echo failed to start samba
			exit 1
		}
	fi
}

service_stop ()
{
	# If set then we force-stop the relevant service.
	_service_name="$1"

	# shutdown Samba when ctdb goes down
	if [ "$_service_name" = "samba" ] || \
	    is_ctdb_managed_service "samba" ; then
		service "$CTDB_SERVICE_SMB" stop
		if [ -n "$CTDB_SERVICE_NMB" ] ; then
		    service "$CTDB_SERVICE_NMB" stop
		fi
	fi

	# stop the winbind service
	if [ "$_service_name" = "winbind" ] || \
	    check_ctdb_manages_winbind ; then
		service "$CTDB_SERVICE_WINBIND" stop
	fi
}

service_reconfigure ()
{
    # Samba automatically reloads config - no restart needed.
    :
}

# set default samba cleanup period - in minutes
[ -z "$SAMBA_CLEANUP_PERIOD" ] && {
    SAMBA_CLEANUP_PERIOD=10
}

# we keep a cached copy of smb.conf here
smbconf_cache="$service_state_dir/smb.conf.cache"


#############################################
# update the smb.conf cache in the foreground
testparm_foreground_update() {
    testparm -s 2> /dev/null | egrep -v 'registry.shares.=|include.=' > "$smbconf_cache"
}

#############################################
# update the smb.conf cache in the background
testparm_background_update() {
    # if the cache doesn't exist, then update in the foreground
    [ -f $smbconf_cache ] || {
	testparm_foreground_update
    }
    # otherwise do a background update
    (
	tmpfile="${smbconf_cache}.$$"
	testparm -s > $tmpfile 2> /dev/null &
	# remember the pid of the teamparm process
	pid="$!"
	# give it 10 seconds to run
	timeleft=10
	while [ $timeleft -gt 0 ]; do
	    timeleft=$(($timeleft - 1))
	    # see if the process still exists
	    kill -0 $pid > /dev/null 2>&1 || {
		# it doesn't exist, grab its exit status
		wait $pid
		[ $? = 0 ] || {
		    echo "50.samba: smb.conf background update exited with status $?"
		    rm -f "${tmpfile}"
		    exit 1
		}		
		# put the new smb.conf contents in the cache (atomic rename)
		# make sure we remove references to the registry while doing 
		# this to ensure that running testparm on the cache does
		# not use the registry
		egrep -v 'registry.shares.=|include.=' < "$tmpfile" > "${tmpfile}.2"
		rm -f "$tmpfile"
		mv -f "${tmpfile}.2" "$smbconf_cache" || {
		    echo "50.samba: failed to update background cache"
		    rm -f "${tmpfile}.2"
		    exit 1
		}
		exit 0
	    }
	    # keep waiting for testparm to finish
	    sleep 1
	done
	# it took more than 10 seconds - kill it off
	rm -f "${tmpfile}"
	kill -9 "$pid" > /dev/null 2>&1
	echo "50.samba: timed out updating smbconf cache in background"
	exit 1
    ) &
}

##################################################
# show the testparm output using a cached smb.conf 
# to avoid registry access
testparm_cat() {
    [ -f $smbconf_cache ] || {
	testparm_foreground_update
    }
    testparm -s "$smbconf_cache" "$@" 2>/dev/null
}

# function to see if ctdb manages winbind - this overrides with extra
# logic if $CTDB_MANAGES_WINBIND is not set or null.
check_ctdb_manages_winbind() {
    if is_ctdb_managed_service "winbind" ; then
	return 0
    elif [ -n "$CTDB_MANAGES_WINBIND" ] ; then
	# If this variable is set we want to respect it.  We return
	# false here because we know it is not set to "yes" - if it
	# were then the 1st "if" above would have succeeded.
	return 1
    else
	_secmode=`testparm_cat --parameter-name=security`
	case "$_secmode" in
	    ADS|DOMAIN)
		return 0
		;;
	    *)
		return 1
		;;
	esac
    fi
}

list_samba_shares ()
{
    testparm_cat |
    sed -n -e 's@^[[:space:]]*path[[:space:]]*=[[:space:]]@@p' |
    sed -e 's/"//g'
}


###########################
# periodic cleanup function
periodic_cleanup() {
    # running smbstatus scrubs any dead entries from the connections
    # and sessionid database
    # echo "Running periodic cleanup of samba databases"
    smbstatus -np > /dev/null 2>&1 &
}

###########################

ctdb_start_stop_service
ctdb_start_stop_service "winbind"

is_ctdb_managed_service || is_ctdb_managed_service "winbind" || exit 0

###########################

case "$1" in 
     startup)
	ctdb_service_start
	;;
	
     shutdown)
	ctdb_service_stop
	;;

     monitor)
	# Create a dummy file to track when we need to do periodic cleanup
	# of samba databases
	periodic_cleanup_file="$service_state_dir/periodic_cleanup"
	[ -f "$periodic_cleanup_file" ] || {
		touch "$periodic_cleanup_file"
	}
	[ `find "$periodic_cleanup_file" -mmin +$SAMBA_CLEANUP_PERIOD | wc -l` -eq 1 ] && {
		# Cleanup the databases
	    	periodic_cleanup
		touch "$periodic_cleanup_file"
	}

	is_ctdb_managed_service "samba" && {
		[ "$CTDB_SAMBA_SKIP_SHARE_CHECK" = "yes" ] || {
			testparm_background_update

			testparm_cat | egrep '^WARNING|^ERROR|^Unknown' && {
			    testparm_foreground_update
			    testparm_cat | egrep '^WARNING|^ERROR|^Unknown' && {
				echo "ERROR: testparm shows smb.conf is not clean"
				exit 1
			    }
			}
			
			list_samba_shares |
			ctdb_check_directories_probe || {
			    testparm_foreground_update
			    list_samba_shares |
			    ctdb_check_directories
			} || exit $?
		}

		smb_ports="$CTDB_SAMBA_CHECK_PORTS"
		[ -z "$smb_ports" ] && {
			smb_ports=`testparm_cat --parameter-name="smb ports"`
		}
		ctdb_check_tcp_ports $smb_ports || exit $?
	}

	# check winbind is OK
	check_ctdb_manages_winbind && {
		ctdb_check_command "winbind" "wbinfo -p"
	}
	;;

     takeip|releaseip)
	iface=$2
	ip=$3
	maskbits=$4

	smbcontrol winbindd ip-dropped $ip >/dev/null 2>/dev/null
	;;
    *)
	ctdb_standard_event_handler "$@"
	;;
esac

exit 0
