#!/bin/sh
#-----------------------------------------------------
# modsvc
# manage system services. 
#
# Copyright (c) 2004 by Alias, Inc., and 
# Alias, a division of Silicon Graphics 
# Canada Ltd.  All rights reserved.
#
# This script provides a way to add and remove system
# services and configure them to execute through the 
# internet service daemons xinetd and inetd
#-----------------------------------------------------

netdgrep="inetd"
scriptdir="/etc/init.d"
inetdconf="/etc/inetd.conf"
xinetdconf="/etc/xinetd.d"
SERVICEFILE="/etc/services"
xinetddump=USR1
xinetdreconf=HUP
inetdreconf=HUP
xinetdumpfile=/var/run/xinetd.dump
platform=`uname | awk '{print tolower(\$1)}'`
if [ 'linux' == "$platform" ] ; then
	if [ -e /etc/redhat-release ] ; then 
		release=`awk '{print \$5}' /etc/redhat-release`
			[ '7.2' == "$release" ] && {
				xinetdreconf=USR2
				xinetddump=HUP
			}
	fi
fi
prog=`basename $0`
PS='ps -ef'

fatalError() 
{
	msg=$1
	[ $nointer -eq 1 ] || echo "Fatal Error: $msg" >&2 
	exit 1
}

readstring()
{
	rsdef=$2
	rsmsg="$1 [$2]" 
	while true ; do
		echo -n "$rsmsg "
		answer=""
		read answer
		if [ "$answer" ] ; then
			return 
		else 
			answer=$2
			return
		fi
	done
}

findpath() {
	answer=$1
	fpmsg=$2
	done=0
	[ -e $answer ] || {
		while true ; do
			echo "$answer does not appear to exist"
			readstring "$fpmsg" "$answer"
			case $answer in
				q|Q|quit|QUIT|Quit) 
					answer='q'
					return 
					;;
				*)
					return
					;;
			esac
		done
	}
}

modifyInetdSvc() {
	[ $verbose -eq 0 ] || echo "updating inetd..."
	if [ $nointer -eq 1 ] ; then
		[ -f $inetdconf ] || fatalError "$inetdconf does not exist"
	else 
		while [ ! -f $inetdconf -a ".$answer" != '.q' ] ; do
			findpath $inetdconf "Where is the inetd configuration file ?"
			inetdconf=$answer
		done
	fi
	inetdtmp=/tmp/inetdconf$$.tmp
	[ ".$inetdconf" != '.q' ] || return
	svcline=`grep "^$service[ \t]" $inetdconf`
	orglen=`wc -l $inetdconf | awk '{print$1}'`
	newlen=$orglen
	case $action in
	'update')
		msg="inetd is configured to run $serviceh"
		if [ ".$svcline" == "." ] ; then
			cp $inetdconf $inetdtmp
			echo "$service $stype $sproto $swait $suser $spath $sargs" >> $inetdtmp
			[ $? == 0 ] || { 
				echo "!!! cannot append service entry to $inetdconf."
				echo "!!! please make sure $inetdconf is writeable."
				return
			}
			newlen=`expr $orglen + 1`
		else
			awk "/^$service[ \t]/ { print \"$service $stype $sproto $swait $suser $spath $sargs\"; next} {print}" \
			$inetdconf > $inetdtmp 
			newlen=$orglen
			
		fi
		;;
	'remove')
		msg="inetd is configured without $serviceh"
		if [ ".$svcline" != "." ] ; then
			awk "/^$service[ \t]/ {next} {print}"  $inetdconf > $inetdtmp ;
			newlen=`expr $orglen - 1`
		else
			newlen=$orglen
		fi			
		;;
	*)
		echo "invalid action in modifyInetdSvc: $action"
	esac
	if [ -f $inetdtmp ] ; then
		len=`wc -l $inetdtmp | awk '{print $1}'`
	else
		len=$newlen
	fi

	if [ $len -eq $newlen ] ; then
		[ -f $inetdtmp ]  &&  { 
			mv $inetdtmp $inetdconf
			pid=`$PS | grep inetd | grep root | grep -v grep | awk '{print $2}'`
			kill -$inetdreconf $pid 2> /dev/null
			if [ $? != 0 ]  ; then
				echo "WARNING: could not send SIG$inetdreconf to inetd. you should do this manually."
			fi
		}
		[ $verbose -eq 1 ] && echo "$msg"
	else 
		echo "Error updating $inetdconf. Please add the following line manually:"
		echo "$service $stype $sproto $swait $suser $spath $sargs"
	fi
}

modifyXinetdSvc() {
	[ $verbose -eq 0 ] || echo "updating xinetd..."
	if [ $nointer -eq 1 ] ; then
		[ -d $xinetdconf ] || fatalError "$xinetdconf does not exist"
	else
		while [[ ! -d $xinetdconf && $answer != 'q' ]] ; do
			findpath $xinetdconf "Where is the xinetd configuration directory ?"
			xinetdconf=$answer
		done
	fi
	xinetdfile=$xinetdconf/$service
	pid=`$PS | grep xinetd | grep root | grep -v grep | awk '{print $2}'`
	success=1 
	case $action in 
	'update')
		[ ! -f $xinetdfile ] || rm $xinetdfile
		[ $? == 0 ] || { 
			echo "can't delete $xinetdfile"
			return 
		}	
		echo "# description: $serviceh" >> $xinetdfile
		echo "service $service" >> $xinetdfile
		echo "{" >> $xinetdfile
		echo "    flags       = REUSE" >> $xinetdfile
		echo "    socket_type = $stype" >> $xinetdfile
		echo "    user        = $suser" >> $xinetdfile
		if [ ".$swait" == ".nowait" ] ; then
			echo "    wait        = no" >> $xinetdfile
		else
			echo "    wait        = yes" >> $xinetdfile
		fi
		echo "    server          = $spath" >> $xinetdfile
		echo "    log_on_failure += USERID" >> $xinetdfile
		echo "}" >> $xinetdfile
		rm $xinetdumpfile >/dev/null 2>&1
		kill -$xinetdreconf $pid 2> /dev/null
		sleep 4
		kill -$xinetddump $pid 2> /dev/null
		# give xinetd time to dump state
		sleep 5
		if [ -f $xinetdumpfile ] ; then
			fdump=`grep "Service = $service" $xinetdumpfile | awk '{print $3}'` 
			if [ "${fdump}" != "$service" ] ; then
				# xinetd did not pick up the change
				success=0;
			else
				[ $verbose == 1 ] && echo "xinetd is configured to run $serviceh"
			fi
		else
			success=0
		fi
		;;
		
	'remove')
		rm $xinetdfile >/dev/null 2>&1
		kill -$xinetdreconf $pid 2> /dev/null
		kill -$xinetddump $pid 2> /dev/null
		# give xinetd time to dump state
		sleep 5
		fdump=`grep "Service = $service" $xinetdumpfile | awk '{print $3}'` 
		if [ "$fdump" == "$service" ] ; then
			success=0;
		else
			[ $verbose == 1 ] && echo "xinetd is configured without $serviceh"
		fi
		;;
	*)
		echo "invalid action in modifyXinetdSvc: $action"
	esac
	[ $success -eq 0 ] && {
		[ $verbose -eq 1 ] && {
			echo " "
			echo "Could not confirm that xinetd has picked up the configuration"
			echo "change. Please note that on some systems, xinetd does not"
			echo "reconfigure on SIG$xinetdreconf. Also, if mental ray network services"
			echo "are currently running xinetd may not register the change."
			echo " "
			echo "If you encounter problems using mental ray network rendering with"
			echo "this workstation you may need to kill any running mental ray services"
			echo "and restart xinetd or have it re-read its configuration files."
			echo " "
			echo "Please refer to the xinetd man page for information on how to do this."
			return
		}
	}
}

#	are we using inetd or xinetd?
modifyNetd() {
	[ $verbose -ne 0 ] && echo "looking for services daemon..."
	netd=`ps -e | grep $netdgrep | grep -v grep | awk -F' ' '{ print ($4) }'`
	if [ ".$netd" == "." ] ; then
		# [x]inetd does not seem to be running. See if there is a bootscript
		[ $verbose -eq 0 ] || echo "I can't see a running inetd or xinetd. ";
		if [ $nointer -eq 1 ] ; then
			[ -d $scriptdir ] || fatalError "$scriptdir does not exist"
		else
			findpath $scriptdir "Where does this system store its bootscripts? : " 
		fi
		scriptdir=$answer
		inetd=`ls $scriptdir | grep '^inetd'`
		xinetd=`ls $scriptdir | grep '^xinetd'`
		if [ ".$inetd" != "." ] ; then
			if [ ".$xinetd" != "." ] ; then
				[ $verbose -eq 0 ] || echo "There are bootscripts for both xinetd and inetd in $scriptdir"
				while [ ".$xinetd" != ".$netd" -a  ".$inetd" != ".$netd" -a ".$netd" != '.none' ] ; do
					readstring "Which net daemon should I configure? (inetd, xinetd or none) " "none"
					netd=$answer
				done
				if [ ".$answer" = ".none" -o ".$answer" == "q" ] ; then
					echo " "
					echo "!!1 $serviceh cannot be configured at this time."
					echo "!!! Please configure either inetd or xinetd run $prog again."
					exit 1
				else
					netd=$answer
				fi
			else
				netd=$inetd
			fi
		else
			# nothing to work with
			echo " "
			echo "!!! $serviceh cannot be configured at this time."
			echo "!!! Please configure either inetd or xinetd and run $prog again."
			exit 1
		fi
	else
		[ $verbose -eq 0 ] || echo "You appear to be running $netd"
	fi

	case $netd in
	'inetd')
		modifyInetdSvc $action $service "$serviceh"
		;;
	'xinetd')
		modifyXinetdSvc $action $service "$serviceh"
		;;
	*)
		;;
	esac
}

modifyService()
{
	[ $verbose -eq 1 ] && echo "updating ${service} services entry..."
	findsvc=`grep "^${service}[ \t]" ${SERVICEFILE}` 
	svctmp=/tmp/service$$.tmp
	[ -f ${svctmp} ] && rm ${svctmp}
	success=1
	orglen=`wc -l ${SERVICEFILE} | awk '{print$1}'`
	newlen=$orglen
	case $action in
	'update')
		
		if [ ".$findsvc" == "." ] ; then
			cp ${SERVICEFILE} $svctmp
			echo "${service}    ${port}    # $serviceh" >> $svctmp
			newlen=`expr $orglen + 1`
			msg="add ${service}  ${port}  # $serviceh"
		else 
			awk "/^${service}[ \t]/ { print \"${service}    ${port}    # $serviceh\"; next} {print}" \
				${SERVICEFILE} > $svctmp
			msg="update ${service}  $port  # $serviceh"
		fi
		;;
	'remove')
		msg="remove ${service} entry"
		if [ ".$findsvc" != "." ] ; then
			grep -v "^${service}[ \t]" ${SERVICEFILE} > $svctmp
			newlen=`expr $orglen - 1`
		fi
		;;
	esac
	if [ -f ${svctmp} ] ; then
		len=`wc -l ${svctmp} | awk '{print $1}'`
		if [ $newlen -eq $len ] ; then
			cp ${svctmp} ${SERVICEFILE}
			rm ${svctmp}
		else
			echo "!!! Error updating ${SERVICEFILE}."
			echo "!!! failed to ${msg}"
			exit 1
		fi
	fi
}	


# set defaults and parse the command line
verbose=0
nointer=0
suser=nobody
while getopts iup:s:r:e:h:d:vn opt; do
	case $opt in
	\?)	exit 1
		;;
	i) 	action='update'
		;;
	u)	action='remove'
		;;
	h)	serviceh=$OPTARG
		;;
	r)  root=$OPTARG
		;;
	p)	port=$OPTARG
		;;
	v)	verbose=1
		;;
	n)	nointer=1
		;;
	d)	suser=$OPTARG
		;;
	e)  executable=$OPTARG
		;;
	esac
done
shift `expr $OPTIND - 1`


usage()
{
	cat <<!EOT!

Usage: $prog [-vn] (-i|-u) [-h <name>] [-r <root>] [-d <user>]
           [-e <executable>] [-p <port>] <service> [<args>]
where:
    -i              install service entries
    -u              uninstall service entries
    -v              verbose messages
    -n              non-interactive 
    -h <name>       human-readable service name
    -r <root>       root executable directory
    -d <user>       username under which the service should run
    -p <port>       port to register in /etc/services. 
    -e <executable> executable file that implements the service
    <service>       service identifier
    <args>          service arguments

!EOT!
	exit 1
}

if [ ! $# ] ; then
	echo "!!! Not enough parameters!"
	usage
fi

service="$1"
sargs="$2"

# validate parameters
[ ".$action" == "." ] && {
	echo "!!! No action specified. One of -i or -u must be set"
	usage
}

MYUID=`id -u`
if test "$MYUID" != "0" ; then
	echo "$prog must be run with root priveleges";
	exit 1;
fi

[ ".$serviceh" == "." ] && serviceh=$service
[ ".$suser" == "." ] && suser=nobody


[ ".$port" != "." ] && modifyService
[ ".$executable" != "." ] &&  {
	stype=stream
	sproto=tcp
	swait=nowait
	sargs=$executable $args
	spath=$root/$executable
	modifyNetd 
}
exit 0
