diff --git a/shellbox b/shellbox index 2fa41a4..b6e4dc9 100755 --- a/shellbox +++ b/shellbox @@ -5,26 +5,21 @@ HOME=/root . /etc/profile +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin -export PATH=$PATH:/usr/local/bin - -# AD84: export HOSTNAME=$(hostname -s) - - -# AD84: added 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 "" + #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 @@ -33,28 +28,39 @@ else HOSTNAME=$IOC fi export HOSTNAME -# AD84: end added +fail () { + echo $@ >&2 + exit 1 +} -exe=/usr/local/bin/procServ options="-k ^X --killsig 15 -x ^D -i ^C --allow -c" prog=shellbox -params= conf=/etc/shellbox.conf logdir=/var/log/$prog shells=/var/run/$prog -fail () { - echo $@ - exit 1 -} +# 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/$PID ] + [ -d /proc/$* ] } launch () { @@ -64,87 +70,129 @@ launch () { 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" - while read PORT USER DIR COMMAND ARGS - do - # check for empty lines and comments - [[ $PORT == "" || $PORT == \#* ]] && continue - - # AD84: added - DIR=${DIR//_IOCNAME_/$HOSTNAME} - ARGS=${ARGS//_IOCNAME_/$HOSTNAME} - # AD84: end added - - # check if already started shell is still alive - if LINE=$(grep "$PORT $USER $DIR $COMMAND $ARGS" $shells 2> /dev/null) - then - PID=${LINE%% *} - if checkpid $PID + # 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 - if [ -z "$reload" ] && [ -z "$*" ] || echo "$*" | grep -qE "(^|[[:space:]])$PORT([[:space:]]|$)" - then - echo "Already running: $PORT $USER $DIR $COMMAND $ARGS" - fi - echo "$LINE" >> $temp - continue + [ -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 - # check if we have to start all shells or only this PORT - [ "$*" ] && echo "$*" | grep -qvE "(^|[[:space:]])$PORT([[:space:]]|$)" && continue + 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 - [ -d $logdir ] || mkdir -m 777 $logdir LOG=$logdir/$PORT rm -f $LOG else LOG=/dev/null fi - # start shellbox as other user - echo -n Starting: $PORT $USER $DIR $COMMAND $ARGS - - if /usr/bin/sudo -E true 2> /dev/null + # start shell + echo Starting: $PORT $USER $DIR $COMMAND + if [ "$UID" -eq 0 ] then - SUDO="/usr/bin/sudo -H -u $USER SHELLBOX=$HOSTNAME:$PORT EPICS_HOST_ARCH=$EPICS_HOST_ARCH PATH=$PATH" - elif /usr/bin/sudo true 2> /dev/null - then - SUDO="/usr/bin/sudo -H -u $USER" + # 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 - SUDO="/usr/bin/sudo -H -u $USER SHELLBOX=$HOSTNAME:$PORT EPICS_HOST_ARCH=$EPICS_HOST_ARCH PATH=$PATH" - - #export SHELLBOX=$HOSTNAME:$PORT - - pidfile=/var/run/procServ-$PORT.pid + pidfile=/var/run/procServ/$PORT.pid rm -f $pidfile - L=/tmp/$SHELLBOX.log - mknod -m 666 $L p 2>/dev/null - logger -p local0.info -t shellbox["$PORT"] -f $L & - $exe -p $pidfile $options $DIR $params -L $L $PORT $SUDO $(which $COMMAND) $ARGS >> $LOG 2>&1 < /dev/null + 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) - echo "$PID $PORT $USER $DIR $COMMAND $ARGS" >> $temp - echo + _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 0644 $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" - launch $* + mkdir -pm 755 /var/lock/subsys touch /var/lock/subsys/$prog + launch $* } stopshell() { @@ -162,7 +210,7 @@ stopshell() { stop () { # anything to stop? - if [ ! -r $shells ] + if [ ! -f $shells ] then echo "$prog: No shells started." exit 0 @@ -170,7 +218,7 @@ stop () { if [ -z "$1" ] then # kill all shellboxes - while read PID PORT ARGS + while read -r PID PORT ARGS do stopshell $PID $PORT $ARGS done < $shells @@ -179,31 +227,32 @@ stop () { else # kill only selected shellboxes temp=$(mktemp -p $(dirname $shells)) || fail "can't create temporary file" - while read PID PORT ARGS + while read -r PID PORT ARGS do - echo "$*" | grep -qE "(^|[[:space:]])$PORT([[:space:]]|$)" && stopshell $PID $PORT $ARGS || echo "$PID $PORT $ARGS" >> $temp + echo "$*" | grep -qE "(^|[[:space:]])$PORT([[:space:]]|$)" && \ + stopshell $PID $PORT $ARGS || \ + echo "$PID $PORT $ARGS" >> $temp done < $shells mv $temp $shells - chmod 0644 $shells + chmod 0444 $shells fi } reload () { - echo "Reloading $conf: " - [ -r $conf ] || fail "not readable" + echo "Reloading $conf... " + [ -f $conf ] || fail "not readable" # anything to stop? - if [ -r $shells ] + if [ -f $shells ] then - #first kill all shells that are not configured any more + # 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 PID LINE + while read -r PID LINE do - while read PORT USER DIR COMMAND ARGS + while read -r PORT USER DIR COMMAND ARGS do - # AD84: added DIR=${DIR//_IOCNAME_/$HOSTNAME} ARGS=${ARGS//_IOCNAME_/$HOSTNAME} - # AD84: end added + if [ "$PORT $USER $DIR $COMMAND $ARGS" = "$LINE" ] then echo "Keeping: $LINE" @@ -214,62 +263,123 @@ reload () { stopshell $PID $PORT $LINE done < $shells mv $temp $shells - chmod 0644 $shells + chmod 0444 $shells fi - #now start all new shells + # now start all new or modified shells sleep 2 launch -reload } status () { - [ -r $conf ] || fail "$conf not readable" - if [ "$1" = "-log" ] - then - log=YES - shift - fi - echo -e "pid\tport\tuser\tdir\t\t\tcommand" - while read PORT USER DIR COMMAND ARGS + while [ "$1" ] do - # check for empty lines and comments - [[ $PORT == "" || $PORT == \#* ]] && continue + case "$1" in + -log | --log) log=1; shift ;; + --json5) json5=1; shift ;; + --) shift; break;; + -* ) echo "unknown option $1 ignored" >&2; shift ;; + *) break + esac + done - # AD84: added - DIR=${DIR//_IOCNAME_/$HOSTNAME} - ARGS=${ARGS//_IOCNAME_/$HOSTNAME} - # AD84: end added + # 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 - # check if we have to report all shells - [ "$*" ] && echo "$*" | grep -qvE "(^|[[:space:]])$PORT([[:space:]]|$)" && continue - - if [ "$logdir" -a "$log" ] - then - echo "-------------------------------------------------------------------" - fi - - if LINE=$(grep "$PORT $USER $DIR $COMMAND $ARGS" $shells 2> /dev/null) - then - PID=${LINE%% *} + # 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 - echo -n $PID + 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 - $SETCOLOR_FAILURE - echo -n DEAD - $SETCOLOR_NORMAL + _STATUS[$PORT]=DEAD + _PID[$PORT]= fi - else - $SETCOLOR_FAILURE - echo -n STOPPED - $SETCOLOR_NORMAL - fi - echo -e "\t$PORT\t$USER\t$DIR\t$COMMAND $ARGS" - + 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 + grep '\*\*\*\*' $logdir/$PORT 2>/dev/null fi - done < $conf + done } CMD=$1