#=============================================================================
#  
#  shared_functions - shared functions for ../bin/semaphore
#  semaphore - a Korn shell implementation of a counting semaphore
#  Copyright (C) 2004 Intel Corporation
#  
#  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 of the License, 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; if not, see http://www.gnu.org/copyleft/gpl.html or
#  write to:
#  
#  The Free Software Foundation, Inc.,
#  59 Temple Place
#  Suite 330, Boston, MA 02111-1307
#  USA
#  
#  Author:
#  
#  John Spurgeon
#  5200 NE Elam Young Parkway
#  Hillsboro, OR 97124
#  
#  john.p.spurgeon@intel.com
#  
#=============================================================================

abort()
{
	local FUNC_NAME=abort
	${TRACE:-trace $FUNC_NAME $@}

	${INFO:-info "PID $$ received an interrupt signal."}
	exit 1
}

is_integer()
{
	# Returns 0 if $1 is an integer; otherwise, returns 1.

	local FUNC_NAME=is_integer
	${TRACE:-trace $FUNC_NAME $@}

	integer n

	if [[ -n $1 ]]
	then
		if n=$1 2>&-
		then
			return 0
		fi
	fi
	return 1
}

is_non_negative_integer()
{
	# Returns 0 if $1 is a non-negative integer; otherwise, returns 1.

	local FUNC_NAME=is_non_negative_integer
	${TRACE:-trace $FUNC_NAME $@}

	if is_integer $1
	then
		if (( $1 >= 0 ))
		then
			return 0
		fi
	fi
	return 1
}

is_permissions_mode()
{
	# This function is not implemented yet.
	return 0
}

is_ownership_string()
{
	# This function is not implemented yet.
	return 0
}

get_busy_resources()
{
	local FUNC_NAME=get_busy_resources
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1
	local resources_dir=$HOME_DIR/$semaphore/$RESOURCES_DIRNAME

	ls -1 $resources_dir/* 2>&- | while read line
	do
		echo ${line##*/}
	done
}

get_link_target()
{
	# Echos the target filename that a symbolic link references. Returns 0
	# if $1 is a symbolic link; otherwise, returns 1.

	local FUNC_NAME=get_link_target
	${TRACE:-trace $FUNC_NAME $@}

	local link=$1
	local target

	if [[ -L $link ]]
	then
		line=$(ls -l $link 2>&-)
		target=${line##*'-> '}
		if [[ -n $target ]]
		then
			echo $target
			return 0
		else
			${INFO:-info "race condition: $link disappeared or changed!"}
		fi
	fi
	${ERROR:-error "$link is not a symbolic link"}
	return 1
}

assert_semaphore()
{
	# This function and the associated calls to it were added to account
	# for the possibility of the semaphore being destroyed while processes
	# are blocked waiting for a resource. In that scenario, processes could
	# get stuck in an infinate loop if we don't abort as soon as we detect
	# that the semaphore no longer exists. Returns 0 if the semaphore exists;
	# otherwise, exits the program.

	local FUNC_NAME=assert_semaphore
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1

	if [[ -d $HOME_DIR/$semaphore/ ]] \
	&& [[ -f $HOME_DIR/$semaphore/$RESOURCES_FILENAME ]] \
	&& [[ -d $HOME_DIR/$semaphore/$QUEUE_DIRNAME ]] \
	&& [[ -d $HOME_DIR/$semaphore/$RESOURCES_DIRNAME ]]
	then
		return 0
	else
		${ERROR:-error "Semaphore $semaphore does not exist!"}
		exit 1
	fi
}

is_initialized()
{
	# Returns 0 if a semaphore exists and is initialized; otherwise, returns
	# 1. The semaphore $semaphore exists and is initialized if the symbolic
	# link $HOME_DIR/$semaphore/$RESOURCES_FILENAME exists and points to a
	# "filename" that is a non-negative integer.

	local FUNC_NAME=is_initialized
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1

	integer num_resources
	local resources_file=$HOME_DIR/$semaphore/$RESOURCES_FILENAME

	if [[ -f $resources_file ]]
	then
		if is_non_negative_integer $(cat -s $resources_file)
		then
			${INFO:-info "Semaphore $semaphore is initialized."}
			return 0
		fi
	fi
	${INFO:-info "Semaphore $semaphore is not initialized."}
	return 1
}

create_directories()
{
	# Creates the directories required to implement a semaphore. Uses the
	# global constants ROOT_DIR, HOME_DIR, SEMAPHORE_DIR
	# and QUEUE_DIR. Retuns 0 if all required directories have
	# been set up; otherwise, returns 1.

	local FUNC_NAME=create_directories
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1
	local semaphore_dir=$HOME_DIR/$semaphore
	local queue_dir=$semaphore_dir/$QUEUE_DIRNAME
	local resources_dir=$semaphore_dir/$RESOURCES_DIRNAME
	local perm_string

	for dir in $ROOT_DIR $HOME_DIR $semaphore_dir $queue_dir $resources_dir
	do
		if [[ -d $dir ]]
		then
			${INFO:-info "Directory $dir exists."}
		else
			${INFO:-info "Creating directory $dir ..."}
			if mkdir $dir 2>&-
			then
				if [[ $dir = $ROOT_DIR ]]
				then
					perm_string=1777
				else
					perm_string=$PERM_STRING
				fi
				chmod $perm_string $dir
				chown $OWNER_STRING $dir
			else
				if [[ ! -d $dir ]]
				then
					${ERROR:-error "Failed to create directory: $dir"}
					return 1
				fi
			fi
		fi
		if [[ ! -w $dir ]]
		then
			${ERROR:-error "Directory $dir is not writable!"}
			exit 1
		fi
	done
}

get_semaphore_value()
{
	local FUNC_NAME=get_sem_value
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1
	local resources_file=$HOME_DIR/$semaphore/$RESOURCES_FILENAME
	local resources_dir=$HOME_DIR/$semaphore/$RESOURCES_DIRNAME

	assert_semaphore $semaphore

	integer num_resources=$(cat -s $resources_file)
	integer num_taken_resources=$(ls -1 $resources_dir | wc -l)
	echo $(( $num_resources - $num_taken_resources ))
}

get_num_resources()
{
    # Echos the number of resources that a semaphore has. (See also
    # Init_semaphore).  Return the contents of the specified semaphore's 
    # resource file, and return 0 if the semaphore has been initialized;

	local FUNC_NAME=get_num_resources
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1
	local resources_file=$HOME_DIR/$semaphore/$RESOURCES_FILENAME

	assert_semaphore $semaphore

	integer num_resources=$(cat -s $resources_file)
	echo ${num_resources:-0}
}

set_num_resources()
{
	# Sets the number of resources for the semaphore. Returns 0 if the
	# number of resources was set; otherwise, returns non-zero.

	local FUNC_NAME=set_num_resources
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1
	integer num_resources=$2
	local resources_file=$HOME_DIR/$semaphore/$RESOURCES_FILENAME

	if is_non_negative_integer $num_resources
	then
		echo $num_resources > $resources_file
		return $?
	else
		return 1
	fi
}

get_resource_holder()
{
	# Echos the process id (pid) of the process that currently has a
	# particular resource.

	local FUNC_NAME=get_resource_holder
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1
	integer resource_num=$2
	local pid
	local resource=$HOME_DIR/$semaphore/$RESOURCES_DIRNAME/$resource_num

	assert_semaphore $semaphore

	if [[ -L $resource ]]
	then
		pid=$(get_link_target $resource)
		if [[ -n $pid ]]
		then
			if is_non_negative_integer $pid
			then
				echo $pid
				return 0
			fi
		fi
	elif [[ -f $resource ]]
	then
		rm -f $resource # regular files shouldn't exist
	fi
	return 1
}

time_since_last_wipe()
{
	local FUNC_NAME=wipe_semaphore
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1
	local last_wipe_file=$HOME_DIR/$semaphore/$LAST_WIPE_FILENAME

	if [[ -f  $last_wipe_file ]]
	then
		last_wipe=$(cat $last_wipe_file)
		current=$(date '+%y%m%d%H%M%S')
		if is_integer $last_wipe
		then
			echo $(($current-$last_wipe))
		else
			echo 0
		fi
	else
		wipe_semaphore $semaphore
		echo 0
	fi
}

wipe_semaphore()
{
	# Frees semaphore resources that are being held by processes that are
	# no longer running.

	local FUNC_NAME=wipe_semaphore
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1
	local resource_num
	local pid

	date '+%y%m%d%H%M%S' > $HOME_DIR/$semaphore/$LAST_WIPE_FILENAME

	for resource_num in $(get_busy_resources $semaphore)
	do
		pid=$(get_resource_holder $semaphore $resource_num)
		if [[ -n $pid ]]
		then
			if ! ps -p $pid >&- 2>&-
			then
				${INFO:-info "PID $pid is no longer running."}
				return_resource $semaphore $resource_num $pid
			fi
		fi
	done
}

get_semaphores()
{
	local FUNC_NAME=get_semaphores
	${TRACE:-trace $FUNC_NAME $@}

	for dir in $(ls -d $HOME_DIR/* 2>&-)
	do
		echo $(basename $dir)
	done
}

return_resource()
{
	# Frees a resource by deleting the symbolic link that represents the
	# fact that the resource is currently in use. See also grab_resource.

	local FUNC_NAME=return_resource
	${TRACE:-trace $FUNC_NAME $@}

	local semaphore=$1
	integer resource_num=$2
	integer pid=$3

	if [[ $pid = "$(get_resource_holder $semaphore $resource_num)" ]]
	then
		${INFO:-info "Returning resource $resource_num"}
		rm -f $HOME_DIR/$semaphore/$RESOURCES_DIRNAME/$resource_num
		return $?
	else
		return 1
	fi
}
