#!/bin/ksh
##### if you don't have ksh, use bash #####
#
# script to assist creating links in /etc/init.d
# for usage, run with no arguments.
#
# note: <num> generally means a 2-digit number ([0-9][0-9])
#
# ari edelkind (03/07/2000)
# last modified 03/08/2000)

# $BASE_DIR/rc?.d are the startup directories
# $INIT_DIR is the directory which contains all actual files
BASE_DIR="/etc"
INIT_DIR="/etc/init.d"

# default link type: if $SYMBOLIC is 0, a hard link is used.
DEF_SYMBOLIC=0

# default force setting: if $FORCE is 1, 'rm -f' will be executed
# before linkage.
DEF_FORCE=0

# default added runlevels for -k
DEF_KRL=S01

# if $TESTING is 1, commands will be echoed instead of executed.
TESTING=0

K_RUNLEVELS=
S_RUNLEVELS=
K_PRIO=
S_PRIO=

SYMBOLIC=$DEF_SYMBOLIC
FORCE=$DEF_FORCE
KRL=$DEF_KRL

usage() {

	if [ "$DEF_SYMBOLIC" -eq 1 ]; then
		SLDEF="(default)"
		HLDEF=""
	else
		SLDEV=""
		HLDEF="(default)"
	fi

	if [ "$DEF_FORCE" -eq 1 ]; then
		YFDEF="(default)"
		NFDEF=""
	else
		YFDEF=""
		NFDEF="(default)"
	fi

	if [ ! -z "$*" ]; then
		echo ""
		echo "$*"
	fi

	echo ""
	echo "links init files from a file in $INIT_DIR"
	echo ""
	echo "usage: $0 [opts] <[-k<num>] [-s<num>] | [-p<num>]> <-r<levels>> <file>"
	echo ""
	echo "	-k<num>	Add K<num><file> to runlevels + $DEF_KRL"
	echo "	-s<num>	Add S<num><file> to runlevels"
	echo "	-p<num>	Use priority <num>, and auto-create S* and K*"
	echo "	-r<num>	Runlevels to use, one level per digit (i.e. -r12)"
	echo "	-S	Use symbolic link  $SLDEF"
	echo "	-H	Use hard link      $HLDEF"
	echo "	-f	Remove link first        $YFDEF"
	echo "	-F	Don't remove link first  $NFDEF"
	echo "	-B<dir>	Use <dir> for base dir ($BASE_DIR)"
	echo "	-I<dir>	Use <dir> for init dir ($INIT_DIR)"
	echo "	-KRL<r>	Use <r> for added K runlevels ($DEF_KRL)"
	echo "	-test	testing only (nothing will be committed)"
	echo ""
	echo "example:"
	echo "	$0 -k34 -s68 -r12 xinetd"
	echo ""
	exit 1
}

docmd() {
	if [ "$TESTING" -eq 1 ]; then
		echo "CMD: $*"
	else
		$*
	fi
}

eh() {
	echo "FATAL: $*"
	exit 1
}

wh() {
	echo "WARNING: $*"
}

for arg in $*; do
	case "$arg" in
	-k[0-9][0-9])
		[ ! -z "$K_PRIO" ] && usage "$arg: -k ($K_PRIO) already given"
		K_PRIO=`echo $arg |sed 's/^-k//'`
		[ $K_PRIO -gt 00 ] || eh "$arg: priority must be greater than 0"
		;;

	-s[0-9][0-9])
		[ ! -z "$S_PRIO" ] && usage "$arg: -s ($S_PRIO) already given"
		S_PRIO=`echo $arg |sed 's/^-s//'`
		[ $S_PRIO -gt 00 ] || eh "$arg: priority must be greater than 0"
		;;

	-p[0-9][0-9])
		[ -z "$X_PRIO" ] || usage "$arg: -p ($X_PRIO) already given"
		[ -z "$S_PRIO" ] || usage "$arg: incompatible with -s"
		[ -z "$K_PRIO" ] || usage "$arg: incompatible with -k"
		X_PRIO=`echo $arg |sed 's/^-p//'`
		[ $X_PRIO -gt 00 ] || eh "$arg: priority must be greater than 0"
		S_PRIO=$X_PRIO
		K_PRIO=`expr 100 - $X_PRIO`
		;;

	-r*)
		[ ! -z "$RUNLEVELS" ] && \
			usage "$arg: -r ($RUNLEVELS) already given"

		RUNLEVELS=`echo $arg |sed 's/^-r//'`
		;;
	-S)
		SYMBOLIC=1
		;;
	-H)
		SYMBOLIC=0
		;;
	-f)
		FORCE=1
		;;
	-F)
		FORCE=0
		;;
	-B*)
		BASE_DIR=`echo $arg |sed 's/^-B//'`
		;;
	-I*)
		INIT_DIR=`echo $arg |sed 's/^-I//'`
		;;
	-KRL*)
		KRL=`echo $arg |sed 's/^-KRL//'`
		;;
	-test)
		TESTING=1
		;;
	*)
		[ ! -z "$FNAME" ] && \
			usage "$arg: filename ($FNAME) already given"

		FNAME=$arg
		;;
	esac
done

S_RUNLEVELS=$RUNLEVELS
K_RUNLEVELS=$S_RUNLEVELS$KRL

# separate runlevels
S_RUNLEVELS="`echo $S_RUNLEVELS |sed 's/./& /g'`"
K_RUNLEVELS="`echo $K_RUNLEVELS |sed 's/./& /g'`"

# input sanity checks
[ -z "$S_RUNLEVELS" ] && usage
[ -z "$K_PRIO" -a -z "$S_PRIO" ] && usage
[ -z "$FNAME" ] && usage

IFNAME="$INIT_DIR/$FNAME"
[ ! -f "$IFNAME" ] && eh "$IFNAME does not exist."
[ ! -x "$IFNAME" ] && wh "$IFNAME is not executable."

[ "$SYMBOLIC" -eq 1 ] && LNOPTS="-s"

# i could consolidate the following two blocks into one function with lots
# of 'eval's and shell escapes, but the resulting code, while slightly smaller,
# would be evidently slower and harder to read.
if [ ! -z "$S_PRIO" ]; then
	S_RLDONE=
	for level in $S_RUNLEVELS; do
		# don't do it more than once
		[ ! -z "`echo $S_RLDONE |grep $level`" ] && continue
		S_RLDONE="${S_RLDONE}$level"

		SKIPDIR=0

		DIR="$BASE_DIR/rc$level.d"

		if [ ! -d "$DIR" ]; then
			wh "no such directory: $DIR        (skipping)"
			SKIPDIR=1
		elif [ ! -w "$DIR" ]; then
			wh "can't write to directory: $DIR (skipping)"
			SKIPDIR=1
		fi

		# don't retry known problem dirs
		if [ "$SKIPDIR" -eq 1 ]; then
			S_RUNLEVELS=`echo $S_RUNLEVELS |sed s/$level//g`
			K_RUNLEVELS=`echo $K_RUNLEVELS |sed s/$level//g`
			continue
		fi

		docmd cd "$DIR"
		if [ $? -ne 0 ]; then
			wh "could not cd to $DIR"
			continue
		fi

		LINK="S${S_PRIO}$FNAME"
		[ "$FORCE" -eq 1 ] && docmd rm -f "$LINK"
		echo "linking $DIR/$LINK -> $IFNAME"
		#ln $LNOPTS "$IFNAME" "$LINK" || wh "could not link $DIR/$LINK -> $IFNAME"
		docmd ln $LNOPTS "$IFNAME" "$LINK" || continue
	done
fi

if [ ! -z "$K_PRIO" ]; then
	K_RLDONE=
	for level in $K_RUNLEVELS; do
		# don't do it more than oince
		[ ! -z "`echo $K_RLDONE |grep $level`" ] && continue
		K_RLDONE="${K_RLDONE}$level"

		SKIPDIR=0

		DIR="$BASE_DIR/rc$level.d"

		if [ ! -d "$DIR" ]; then
			wh "no such directory: $DIR        (skipping)"
			SKIPDIR=1
		elif [ ! -w "$DIR" ]; then
			wh "can't write to directory: $DIR (skipping)"
			SKIPDIR=1
		fi

		# don't retry known problem dirs
		if [ "$SKIPDIR" -eq 1 ]; then
			S_RUNLEVELS=`echo $S_RUNLEVELS |sed s/$level//g`
			K_RUNLEVELS=`echo $K_RUNLEVELS |sed s/$level//g`
			continue
		fi

		docmd cd "$DIR"
		if [ $? -ne 0 ]; then
			wh "could not cd to $DIR"
			continue
		fi

		LINK="K${K_PRIO}$FNAME"
		[ "$FORCE" -eq 1 ] && docmd rm -f "$LINK"
		echo "linking $DIR/$LINK -> $IFNAME"
		#ln $LNOPTS "$IFNAME" "$LINK" || wh "could not link $DIR/$LINK -> $IFNAME"
		docmd ln $LNOPTS "$IFNAME" "$LINK" || continue
	done
fi

