396 lines
9.7 KiB
Bash
Executable File
396 lines
9.7 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# chkconfig: 2345 98 2
|
|
# description: shellbox service for spawning programs
|
|
|
|
HOME=/root
|
|
. /etc/profile
|
|
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
|
|
|
|
if [ -f /usr/local/bin/getIocBootEnv ]
|
|
then
|
|
. /usr/local/bin/getIocBootEnv
|
|
#echo ""
|
|
#echo "shellbox: IOC=$IOC"
|
|
#echo "shellbox: FACILITY=$FACILITY"
|
|
#echo "shellbox: ROOTFSSERVERIP=$ROOTFSSERVERIP"
|
|
#echo "shellbox: BOOTPC=$BOOTPC"
|
|
#echo "shellbox: CMDLINE_HOST=$CMDLINE_HOST"
|
|
#echo "shellbox: CMDLINE_ETH=$CMDLINE_ETH"
|
|
#echo "shellbox: BOOTIF_BCAST=$BOOTIF_BCAST"
|
|
#echo "shellbox: BOOTIF_IP=$BOOTIF_IP"
|
|
#echo ""
|
|
fi
|
|
if [ "$IOC" == "" ]
|
|
then
|
|
HOSTNAME=$(hostname -s)
|
|
else
|
|
HOSTNAME=$IOC
|
|
fi
|
|
export HOSTNAME
|
|
|
|
fail () {
|
|
echo $@ >&2
|
|
exit 1
|
|
}
|
|
|
|
options="-k ^X --killsig 15 -x ^D -i ^C --allow -c"
|
|
prog=shellbox
|
|
conf=/etc/shellbox.conf
|
|
logdir=/var/log/$prog
|
|
shells=/var/run/$prog
|
|
|
|
# color only if printing to terminal
|
|
declare -A COLOR
|
|
if [ -t 1 ]
|
|
then
|
|
COLOR[RUNNING]=
|
|
COLOR[STOPPED]=$'\033[46;30m'
|
|
COLOR[DISABLED]=$'\033[47;38;5;8m'
|
|
COLOR[OBSOLETE]=$'\033[48;5;8;37m'
|
|
COLOR[MODIFIED]=$'\033[43;30m'
|
|
COLOR[DEAD]=$'\033[41;37m'
|
|
COLOR[NORMAL]=$'\033[0;39m'
|
|
COLOR[HEADER]=$'\033[0;1;4m'
|
|
fi
|
|
|
|
|
|
echo_failure () {
|
|
echo " [failed]"
|
|
}
|
|
|
|
checkpid () {
|
|
[ -d /proc/$* ]
|
|
}
|
|
|
|
launch () {
|
|
[ -f /ioc/${HOSTNAME}/DoNotStartEpics ] && fail "Not starting EPICS because /ioc/${HOSTNAME}/DoNotStartEpics exists"
|
|
if [ "$1" = "-reload" ]
|
|
then
|
|
reload=YES
|
|
shift
|
|
fi
|
|
|
|
[ -d /var/run/procServ ] || mkdir -pm 755 /var/run/procServ
|
|
[ -z "$logdir" -o -d "$logdir" ] || mkdir -pm 755 "$logdir"
|
|
|
|
temp=$(mktemp -p $(dirname $shells)) || fail "can't create temporary file"
|
|
# delete entries for dead shell that we are going to restart
|
|
if [ -f $shells ]
|
|
then
|
|
while read -r PID PORT LINE
|
|
do
|
|
if ! checkpid $PID
|
|
then
|
|
[ -z "$*" ] && continue
|
|
for p in "$@"; do [ "$p" = "$PORT" ] && continue 2; done
|
|
fi
|
|
# keep all other shell entries (dead or running)
|
|
echo "$PID $PORT $LINE" >> $temp
|
|
_PID[$PORT]=$PID
|
|
_LINE[$PORT]="$LINE"
|
|
done < $shells
|
|
fi
|
|
|
|
# add entries for shells we start
|
|
while read -r PORT USER DIR COMMAND
|
|
do
|
|
# ignore empty lines and comments
|
|
[ "${PORT%#*}" ] && STATUS=STOPPED || STATUS=DISABLED
|
|
PORT="${PORT#\#}"
|
|
test "$PORT" -ge 0 2>/dev/null || continue
|
|
|
|
# skip if line does not match given port number
|
|
if [ "$*" ]
|
|
then
|
|
for p in "$@" end; do [ "$p" = "$PORT" ] && break; done
|
|
[ $p = end ] && continue
|
|
fi
|
|
|
|
if [ $STATUS = DISABLED ]
|
|
then
|
|
echo "Shell $PORT has been disabled in the configuration" >&2
|
|
continue
|
|
fi
|
|
|
|
DIR=${DIR//_IOCNAME_/$HOSTNAME}
|
|
ARGS=${ARGS//_IOCNAME_/$HOSTNAME}
|
|
|
|
# check if this shell had already been started
|
|
if [ "${_PID[$PORT]}" ]
|
|
then
|
|
# warn if trying to start already running shell
|
|
if [ -z "$reload" ]
|
|
then
|
|
if [ "$USER $DIR $COMMAND" != "${_LINE[$PORT]}" ]
|
|
then
|
|
echo "Running but modified: ${_LINE[$PORT]}" >&2
|
|
echo "New configuration : $USER $DIR $COMMAND" >&2
|
|
echo "You may want to restart $PORT" >&2
|
|
mod=1
|
|
else
|
|
echo "Already running: $PORT ${_LINE[$PORT]}" >&2
|
|
fi
|
|
fi
|
|
# skip already running shells
|
|
continue
|
|
fi
|
|
|
|
if [ -n "$logdir" ]
|
|
then
|
|
LOG=$logdir/$PORT
|
|
rm -f $LOG
|
|
else
|
|
LOG=/dev/null
|
|
fi
|
|
|
|
# start shell
|
|
echo "Starting: $PORT $USER $DIR $COMMAND"
|
|
if [ "$UID" -eq 0 ]
|
|
then
|
|
# sudo may be installed in different locations
|
|
# and may delete important environment variables
|
|
SUDO=$(which sudo) && SUDO+=" -H -u $USER SHELLBOX=$HOSTNAME:$PORT EPICS_HOST_ARCH=$EPICS_HOST_ARCH PATH=$PATH"
|
|
fi
|
|
|
|
pidfile=/var/run/procServ/$PORT.pid
|
|
rm -f $pidfile
|
|
logpipe=/tmp/procServ-$PORT.log
|
|
mknod -m 666 $logpipe p 2>/dev/null
|
|
cat $logpipe | logger -p local0.info -t shellbox[$PORT] 2>/dev/null &
|
|
$exe -p $pidfile $options $DIR -L $logpipe $PORT $SUDO $COMMAND >> $LOG 2>&1 < /dev/null
|
|
# check if starting worked or failed
|
|
usleep 100000
|
|
if [ -e $pidfile ]
|
|
then
|
|
PID=$(<$pidfile)
|
|
_PID[$PORT]=$PID
|
|
echo "$PID $PORT $USER $DIR $COMMAND" >> $temp
|
|
else
|
|
_PID[$PORT]=fail
|
|
echo_failure
|
|
echo
|
|
cat $LOG
|
|
fi
|
|
done < $conf
|
|
mv $temp $shells
|
|
chmod 0444 $shells
|
|
for p in "$@"
|
|
do
|
|
if [ -z ${_PID[$p]]} ]
|
|
then
|
|
echo "No configuration for $p found" >&2
|
|
fi
|
|
done
|
|
|
|
[ "$mod" ] && fail "To apply all modifcations use reload"
|
|
}
|
|
|
|
start () {
|
|
[ -r $conf ] || fail "$conf not readable"
|
|
exe=$(which procServ) || fail "procServ not found"
|
|
[ -x $exe ] || fail "$exe is not executable"
|
|
mkdir -pm 755 /var/lock/subsys
|
|
touch /var/lock/subsys/$prog
|
|
launch $*
|
|
}
|
|
|
|
stopshell() {
|
|
PID=$1
|
|
PORT=$2
|
|
shift
|
|
echo -n "Stopping: $*"
|
|
kill $PID 2> /dev/null || echo_failure
|
|
echo
|
|
if [ $logdir ]
|
|
then
|
|
echo -e "\n**** stopped ****" >> $logdir/$PORT
|
|
fi
|
|
}
|
|
|
|
stop () {
|
|
# anything to stop?
|
|
if [ ! -f $shells ]
|
|
then
|
|
echo "$prog: No shells started."
|
|
exit 0
|
|
fi
|
|
if [ -z "$1" ]
|
|
then
|
|
# kill all shellboxes
|
|
while read -r PID PORT ARGS
|
|
do
|
|
stopshell $PID $PORT $ARGS
|
|
done < $shells
|
|
rm -f $shells
|
|
rm -f /var/lock/subsys/$prog
|
|
else
|
|
# kill only selected shellboxes
|
|
temp=$(mktemp -p $(dirname $shells)) || fail "can't create temporary file"
|
|
while read -r PID PORT ARGS
|
|
do
|
|
echo "$*" | grep -qE "(^|[[:space:]])$PORT([[:space:]]|$)" && \
|
|
stopshell $PID $PORT $ARGS || \
|
|
echo "$PID $PORT $ARGS" >> $temp
|
|
done < $shells
|
|
mv $temp $shells
|
|
chmod 0444 $shells
|
|
fi
|
|
}
|
|
|
|
reload () {
|
|
echo "Reloading $conf... "
|
|
[ -f $conf ] || fail "not readable"
|
|
# anything to stop?
|
|
if [ -f $shells ]
|
|
then
|
|
# first kill all shells that are modified or not configured any more
|
|
temp=$(mktemp -p $(dirname $shells)) || fail "can't create temporary file"
|
|
while read -r PID LINE
|
|
do
|
|
while read -r PORT USER DIR COMMAND ARGS
|
|
do
|
|
DIR=${DIR//_IOCNAME_/$HOSTNAME}
|
|
ARGS=${ARGS//_IOCNAME_/$HOSTNAME}
|
|
|
|
if [ "$PORT $USER $DIR $COMMAND $ARGS" = "$LINE" ]
|
|
then
|
|
echo "Keeping: $LINE"
|
|
echo "$PID $LINE" >> $temp
|
|
continue 2
|
|
fi
|
|
done < $conf
|
|
stopshell $PID $PORT $LINE
|
|
done < $shells
|
|
mv $temp $shells
|
|
chmod 0444 $shells
|
|
fi
|
|
# now start all new or modified shells
|
|
sleep 2
|
|
launch -reload
|
|
}
|
|
|
|
status () {
|
|
while [ "$1" ]
|
|
do
|
|
case "$1" in
|
|
-log | --log) log=1; shift ;;
|
|
--json5) json5=1; shift ;;
|
|
--) shift; break;;
|
|
-* ) echo "unknown option $1 ignored" >&2; shift ;;
|
|
*) break
|
|
esac
|
|
done
|
|
|
|
# First read all configured shells
|
|
if [ -f $conf ]
|
|
then
|
|
while read -r PORT USER DIR CMD
|
|
do
|
|
# Assume stopped until proven otherwise
|
|
[ "${PORT%#*}" ] && STATUS=STOPPED || STATUS=DISABLED
|
|
# check for empty lines, comments, rubbish
|
|
PORT="${PORT#\#}"
|
|
test "$PORT" -ge 0 2>/dev/null || continue
|
|
_STATUS[$PORT]=$STATUS
|
|
_USER[$PORT]=$USER
|
|
_DIR[$PORT]=${DIR//_IOCNAME_/$HOSTNAME}
|
|
_CMD[$PORT]=${CMD//_IOCNAME_/$HOSTNAME}
|
|
_CFG[$PORT]="${_USER[$PORT]} ${_DIR[$PORT]} ${_CMD[$PORT]}"
|
|
done < $conf
|
|
fi
|
|
|
|
# Now check all started shells
|
|
if [ -f $shells ]
|
|
then
|
|
while read -r PID PORT USER DIR CMD
|
|
do
|
|
CFG="$USER $DIR $CMD"
|
|
_PID[$PORT]=$PID
|
|
_USER[$PORT]=$USER
|
|
_DIR[$PORT]=$DIR
|
|
_CMD[$PORT]=$CMD
|
|
if checkpid $PID
|
|
then
|
|
if [ "${_STATUS[$PORT]}" = DISABLED ]
|
|
then
|
|
_STATUS[$PORT]=OBSOLETE
|
|
elif [ -z "${_CFG[$PORT]}" ]
|
|
then
|
|
_STATUS[$PORT]=OBSOLETE
|
|
elif [ "${_CFG[$PORT]}" != "$CFG" ]
|
|
then
|
|
_STATUS[$PORT]=MODIFIED
|
|
else
|
|
_STATUS[$PORT]=RUNNING
|
|
fi
|
|
else
|
|
_STATUS[$PORT]=DEAD
|
|
_PID[$PORT]=
|
|
fi
|
|
done < $shells
|
|
fi
|
|
|
|
|
|
if [ "$json5" ]
|
|
then
|
|
echo '{ shells: ['
|
|
for PORT in ${!_STATUS[*]}
|
|
do
|
|
echo ' {'
|
|
echo ' port: '$PORT','
|
|
[ "${_PID[$PORT]}" ] && echo ' pid: '${_PID[$PORT]}','
|
|
echo ' status: "'${_STATUS[$PORT]}'",'
|
|
echo ' user: "'${_USER[$PORT]}'",'
|
|
dir=${_DIR[$PORT]//\\/\\\\}
|
|
echo ' dir: "'${dir//\"/\\\"}'",'
|
|
cmd=${_CMD[$PORT]//\\/\\\\}
|
|
echo ' cmd: "'${cmd//\"/\\\"}'"'
|
|
echo ' },'
|
|
done
|
|
echo ']}'
|
|
exit
|
|
fi
|
|
|
|
USERLEN=0
|
|
for v in "${_USER[@]}"
|
|
do
|
|
[ ${#v} -gt $USERLEN ] && USERLEN=${#v}
|
|
done
|
|
DIRLEN=0
|
|
for v in "${_DIR[@]}"
|
|
do
|
|
[ ${#v} -gt $DIRLEN ] && DIRLEN=${#v}
|
|
done
|
|
CMDLEN=0
|
|
for v in "${_CMD[@]}"
|
|
do
|
|
[ ${#v} -gt $CMDLEN ] && CMDLEN=${#v}
|
|
done
|
|
|
|
printf "${COLOR[HEADER]}#%-5s %-8s %-5s %-*s %-*s %-*s ${COLOR[NORMAL]}\n" pid status port $USERLEN user $DIRLEN dir $CMDLEN command
|
|
|
|
for PORT in ${!_STATUS[*]}
|
|
do
|
|
# check if we have to report all shells
|
|
[ "$*" ] && echo "$*" | grep -qvE "(^|[[:space:]])$PORT([[:space:]]|$)" && continue
|
|
printf "${COLOR[${_STATUS[$PORT]}]}%-6s %-8s %5d %-*s %-*s %-*s ${COLOR[NORMAL]}\n" "${_PID[$PORT]}" ${_STATUS[$PORT]} $PORT $USERLEN ${_USER[$PORT]} $DIRLEN ${_DIR[$PORT]} $CMDLEN "${_CMD[$PORT]}"
|
|
if [ "$logdir" -a "$log" ]
|
|
then
|
|
grep '\*\*\*\*' $logdir/$PORT 2>/dev/null
|
|
fi
|
|
done
|
|
}
|
|
|
|
CMD=$1
|
|
shift
|
|
case "$CMD" in
|
|
(start) start $*;;
|
|
(stop) stop $*;;
|
|
(restart) stop $*; sleep 2; start $*;; # kill all shells, then start again
|
|
(reread|reload) reload $*;; # reload shellbox.conf without killing too much
|
|
(status) status $*;;
|
|
(*) echo "Usage: $0 {start [ports]|stop [ports]|restart [ports]|reload|status [-log] [ports]}" ;;
|
|
esac
|
|
|