#!/bin/sh

export PATH=/sbin:/bin:/usr/sbin:/usr/bin

#If $INIT_D is unset (e.g. if script is being run by sysvinit), determine
#$INIT_D by the location of the functions script. If everyone would agree
#on where scripts should go, this would be just INIT_D=${INIT_D:-path}
#but there are 3 popular directories: /sbin/init.d, /etc/init.d and /etc/rc.d
#In order to make this script compatible with all 3 choices we have to work
#some shell magic.
if [ ! -n "$INIT_D" ]; then
  INIT_D=`PATH=/sbin/init.d:/etc/init.d:/etc/rc.d type -p functions`
  INIT_D=${INIT_D%/functions} #remove "/functions to get directory"
  
  if [ ! -n "$INIT_D" ]; then
    echo 1>&2 Cannot determine INIT_D!
    exit 1
  fi
  export INIT_D
fi

if [ "$USERSPACE_INIT" = "1" ]  #if testing boot scripts in user space
then                            #simulating some binaries might be desired
  if [ -f $INIT_D/sim-bins ]; then
    source $INIT_D/sim-bins
  fi
  if [ ! -n "$INIT_ROOT" ]; then 
    echo 1>&2 INIT_ROOT not set!
    exit 1
  fi
  
  #catch common error of forgetting to add need symlink in user space
  #testing environment
  if [ ! -x $INIT_ROOT/sbin/need ]; then 
    echo 1>&2 "Cannot find $INIT_ROOT/sbin/need"
    exit 1
  fi  
  
  #prefer binaries in the current directory, in case someone forgot to
  #export PATH=$INIT_ROOT/sbin:$PATH
  export PATH=.:$PATH 
fi

if [ -f $INIT_D/services ]; then
  source $INIT_D/services
fi

#how many seconds between SIGTERM and SIGKILL
KILLDELAY=3

COL=$(stty size)
COL=${COL##* }
COL=${COL:-80}

WCOL=$(($COL - 30))
COL=$(($COL - 7))
CURS_UP="echo -en \\033[A"
SET_COL="echo -en \\033[${COL}G"
SET_WCOL="echo -en \\033[${WCOL}G"
NORMAL="echo -en \\033[0;39m"
SUCCESS="echo -en \\033[1;32m"
FAILURE="echo -en \\033[1;31m"
WARNING="echo -en \\033[1;33m"
SKIPPED="echo -en \\033[1;34m"

evaluate_retval()
{
        saved_status=$?
        if [ $saved_status = 0 ]
        then
                print_status success
        else
                print_status failure
        fi
        return $saved_status
}

up_evaluate_retval()
{
        saved_status=$?
        $CURS_UP
        if [ $saved_status = 0 ]
        then
                print_status success
        else
                print_status failure
        fi
        return $saved_status
}

print_status()
{
        if [ $# = 0 ]
        then
                echo "Usage: print_status success|failure|skipped|warning"
                return 1
        fi

        case "$1" in
                success)
                        $SET_COL
                        echo -n "[  "
                        $SUCCESS
                        echo -n "OK"
                        $NORMAL
                        echo "  ]"
                        ;;
                failure)
                        $SET_COL
                        echo -n "["
                        $FAILURE
                        echo -n "FAILED"
                        $NORMAL
                        echo "]"
                        ;;
                warning)
                        $SET_COL
                        echo -n "[ "
                        $WARNING
                        echo -n "ATTN"
                        $NORMAL
                        echo " ]"
                        ;;        
                skipped)
                        $SET_COL
                        echo -n "[ "
                        $SKIPPED
                        echo -n "SKIP"
                        $NORMAL
                        echo " ]"
                        ;;         
        esac
}

#getpids progname|progpath
#prints the pids with which the program is running. Only pids of processes
#belonging to the caller will be returned. The pid of the caller or that of its
#parent is never returned
getpids()
{
        basename=${1##*/}
        
        #we also want to catch scripts of the specified name,
        #this is default behaviour for psmisc's killall but has to be
        #activated with -x for the pidof from sysvinit/simpleinit-msb
        #We want to be compatible with both, so we first try with -x and
        #fall back to doing it without if that fails
        #The "exec sh -c 'echo $$PPID ; exec " is necessary because $$
        #does not return the pid of the subshell running getpids. 
        pidlist=`exec sh -c 'echo $PPID ; exec pidof -x '$basename 2>/dev/null`
        if [ $? != 0 ]; then 
          pidlist=`exec sh -c 'echo $PPID ; exec pidof '$basename`
        fi  
        
        gpPID=${pidlist%%[^0-9]*}  #PID if subshell running getpids
        
        for pid in $pidlist
        do
          #ignore ourselves and our parent (which might have the same name
          #as our target program)
          if [ $pid = $$ -o $pid = $PPID -o $pid = $gpPID ]; then continue; fi
          
          #only print pids of processes owned by caller
          if [ -O /proc/$pid ]; then echo $pid ; fi
        done
}

#loads a program unless it is already loaded. For the test whether it is 
#loaded only processes belonging to the caller are considered (i.e. if some
#other user runs a program of the same name it will not confuse this function).
#The calling script and its parent will also be ignored, even if they have the
#same name as the program to load, so a script called syslogd can use loadproc
#to start syslogd
loadproc()
{
        if [ $# == 0 ]
        then
                echo 1>&2 "Usage: loadproc program [args]"
                return 1
        fi

        pidlist=`getpids $1`

        if [ ! -n "$pidlist" ]; 
        then 
          #if not found, then start program
          "$@"
          up_evaluate_retval
        else
          $CURS_UP
          $SET_WCOL
          echo -n "...already running!"
          print_status skipped
          return 0
        fi
}

#sends a signal to all processes of a given name that belong to the caller,
#with the exception of the caller and its parent (should they have the same
#name)
#If a signal is passed as second parameter, only that signal is sent
#If the 2nd parameter is omitted, the process is shot down using SIGTERM
#followed $KILLDELAY seconds later by SIGKILL
killproc()
{
        if [ $# = 0 ]
        then
                echo 1>&2 "Usage: killproc program [signal]"
                echo 1>&2 "If signal is not given, shoot down with TERM+KILL"
                return 1
        fi

        if [ ! -n "$2" ]; then
          signal=TERM
          fallback=KILL
        else  
          signal=${2##-}  #tolerate signal with leading "-"
          signal=${signal##SIG} #tolerate signal with leading "SIG"
          fallback=""
        fi  

        pidlist=`getpids $1`
        
        if [ -n "$pidlist" ]
        then
              failure=0        
           
              for pid in $pidlist
              do  
                kill -$signal $pid 2>/dev/null
                
                #wait a little - if the process terminates in this time frame
                #we don't have to wait several seconds
                for((i=0; $i<1000; i=$i+1)) ; do : ; done
                
                #test if process still exists and wait if it does
                for((i=0; $i<$KILLDELAY; i=$i+1)) ; do
                  kill -0 $pid 2>/dev/null || break
                  sleep 1
                done  
                
                if [ -n "$fallback" ]; then kill -$fallback $pid 2>/dev/null; fi
 
                #make sure it's really gone, set failure if it isn't 
                kill -0 $pid 2>/dev/null && failure=1
              done  
               
              #if killing was successful make sure pidfile is removed
              basename=${1##*/} 
              if [ $failure = 0 ]; then rm -f /var/run/$basename.pid ; fi
              
              (exit $failure)
              up_evaluate_retval

        else  #if pidlist is empty, i.e. process did not exist in the 1st place
                $CURS_UP
                $SET_WCOL
                echo -n "...not running!"
                print_status skipped
                return 0
        fi
}

#
#The reloadproc functions sends a signal to all programs of the specified 
#name that belong to the caller (except for the caller itself and its parent
#should they have the same name), telling them to
#reload their configuration files.
#If no signal is specified, SIGHUP will be assumed

reloadproc()
{
        if [ $# = 0 ]
        then
                echo 1>&2 "Usage: reloadproc program [signal]"
                echo 1>&2 "If signal is not given, the function uses SIGHUP"
                return 1
        fi

        if [ ! -n "$2" ]; then
          signal=HUP
        else  
          signal=${2##-}  #tolerate signal with leading "-"
          signal=${signal##SIG} #tolerate signal with leading "SIG"
        fi  

        pidlist=`getpids $1`
        
        if [ -n "$pidlist" ]
        then
              failure=0        
           
              for pid in $pidlist
              do  
                kill -$signal $pid || failure=1
              done  
               
              (exit $failure)
              up_evaluate_retval

        else  #if pidlist is empty, i.e. process did not exist in the 1st place
                $CURS_UP
                $SET_WCOL
                echo -n "...not running!"
                print_status skipped
                return 0
        fi
}

#
# The statusproc function will try to find out if a process is running
# or not
#

statusproc()
{
        if [ $# = 0 ]
        then
                echo 1>&2 "Usage: statusproc program"
                return 1
        fi

        basename=${1##*/}
        pidlist=`getpids $basename`
        
        if [ -n "$pidlist" ]
        then
            echo "$basename running with PID(s) $pidlist"
        else
            if [ -s /var/run/$basename.pid ]
            then
              echo "$basename is not running but /var/run/$basename.pid exists"
              return 1
            else
                echo "$basename is not running"
            fi
        fi
        return 0
}

exit_if_any_error()
{
  if [ $? -ne 0 ]; then exit 1; fi
  return 0
}

exit_if_fatal_error()
{
  status=$?
  if [ $status -ne 2 -a $status -ne 0 ]; then exit 1; fi
  return $status
}

need()
{
  if [ $# == 0 ]; then
    echo 1>&2 'need called without argument! Maybe you used "need $var" and $var is unset'
    return 2;
  fi
  
  if [ -x $INIT_ROOT/sbin/need ]; then  #if the system supports dependency
    $INIT_ROOT/sbin/need "$@"           #management (modern inits), use it
    return $?                           
  else                                  #if not (sysvinit), assume that the 
    return 0                            #admin set up symlink numbers correctly
  fi    
}
