#!/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