#!/bin/sh

#Copyright (c) 2001 Matthias S. Benkmann
#licensed under the GNU General Public License (GPL) Version 2

runlevel_prefix="runlevel."
runlevel_suffix=""

HELP_TEXT='
telinit invocations:

telinit help		displays this help text

telinit <num>
  or           
telinit '"${runlevel_prefix}<num>${runlevel_suffix}"\
'	will go up or down to runlevel <num>

telinit single          will bring the system to maintenance mode 

telinit <service> <params> 
			will call <service> with arguments <params>,
			e.g. telinit logging status
			You should prefer this method over calling boot
			scripts directly because telinit makes sure the
			environment is set up similar to that created by init
			when it starts boot scripts.
			
			NOTE: If you call 
			        telinit <service> start
			      or 
			        telinit <service> stop
			      you will bypass the dependency management system,
			      i.e. the script will be started or stopped 
			      without init knowing about it. 
			      Read initctl(8) to understand all the 
			      implications of this.
'



#locate and source functions script
source `PATH=$INIT_D/:${0%/*}/:${0%/*/*}/:${0%/*/*/*}/ type -p functions`

#block SIGHUP in case the script is called over a modem line that disconnects
#  (or someone closes his xterm after calling the script, or ...)
#block SIGINT to prevent nervous users from Ctrl-C'ing us
#block SIGQUIT to prevent users from Ctrl-\'ing us. 
#block SIGTSTP to prevent users from Ctrl-Z'ing us.
#We use ":" rather than "" because the latter would be inherited by processes
#  such as fsck that we start (SIG_IGN is inherited) while the former is not.
#  This means that with ":" the scripts called by rc can be interrupted. It's
#  just the rc script that you can't interrupt. Usually a user would only want
#  to interrupt a certain script but if he presses the keys at the wrong time,
#  he might hit rc by accident.
trap ":" SIGHUP SIGINT SIGQUIT SIGTSTP


remove_trailing_slash()
{
  i=${1%/}              #cut off trailing slash
  i=${i:-/}             #if string is now empty put it back
  echo $i
}

needdir()
{
  case "z$1" in
    z) return 0 ;;
    -*) initctl -n "$@" ;; #if switch is specified, use normal need
  esac

  dir=$1
  dir=${1##[^/]*}          #dir="" if $1 is not an absolute path, else dir=$1
  dir=${dir:-$INIT_D/$1}   #if dir="" (i.e. $1 not abs.) then dir=$INIT_D/$1
  dir=`remove_trailing_slash $dir`
  #dir is a non-empty absolute path without a trailing slash now.

  #test if $dir is a directory, if not try using normal need on input
  test -d $dir || { initctl -n "$@"; return $? ; }

  servicelist=`echo $dir/*`     #list of all services in $dir
  if [ "$servicelist" = $dir'/*' ]; then return 0; fi  #if list is empty then return
  
  errcode=0
  for service in $servicelist
  do
    case $service in 
      *~) ;;   #do not run backup files
      
      #strip pathname (see comment at top) and +
      #return immediately if error because + services are essential
      */+*) initctl -n ${service##*/+} || return 1 ;;

      #strip pathname (see comment at top)
      *) initctl -n ${service##*/} || errcode=2 ;; 
    esac  
  done
  
  return $errcode
}

#unlike get_inittab_entry in telinit, this function returns all words for an
#entry with more than one word after the "="
get_inittab_entry2()
{
  expr "`echo ; cat $INIT_ROOT/etc/inittab ; echo`" : $'.*\n[ \r\t]*'${1}$'[ \r\t]*=*[ \r\t]*''\('$'[^\n]*''\).*' 
}

case "$1" in
  start-dir)
    if [ $# -lt 2 ]; then exit 1; fi
    
    needdir $2 || exit_if_fatal_error

    rl=${2##*$runlevel_prefix}
    rl=${rl%$runlevel_suffix}
    echo -n Runlevel $rl reached!
    print_status success
    ;;

  start)
    if [ $# -lt 2 ]; then exit 1; fi
    
    parameters="$@"
    
    for (( i=0; $i<2 ; i=$i+1 )); do
      while [ $# -ge 2 ]; do
         #target could be a script (e.g. "single") rather than a runlevel num
         if [ -e "$INIT_D/$2" ];
        		then target=$2
       			else target=$runlevel_prefix$2$runlevel_suffix
         fi
        
         if [ -e "$INIT_D/$target"  ]; then break 2; fi
         
         #if $target not found, try the next parameter
         shift 1;
      done
      
      #we didn't find a valid runlevel in the parameter list, so we try
      #the default setting from inittab
      bootprog_entry="`get_inittab_entry2 bootprog`"
      eval set -- $bootprog_entry
    done     
        
    if [ ! -e "$INIT_D/$target"  ]; then
       echo -n 1>&2 "No valid runlevel in parameter list: "
       set -- $parameters
       shift 1  #remove start
       echo 1>&2 "$@"
       echo -n 1>&2 "No valid runlevel in bootprog parameter list: "
       eval set -- $bootprog_entry
       shift 1  #remove rc
       echo 1>&2 "$@"
       exit 1
    fi
    
    need "$target" || exit_if_fatal_error
    ;;

  stop-dir)
    if [ $# -lt 2 ]; then exit 1; fi
    
    rl=${2##*$runlevel_prefix}
    rl=${rl%$runlevel_suffix}
    echo -n Runlevel $rl going down!
    print_status success
    ;;

  stop)
    ;;

  telinit)
      ########################################################
      #            telinit help
      ########################################################
      if [ $# == 1 -o "$2" == "help" -o "$2" == "--help" ]; then
        echo "$HELP_TEXT"
        exit 0
      fi

      ########################################################
      #            telinit <service> <params>      
      ########################################################
      if [ $# -gt 2 ]; then
        script=`cd $INIT_D ; PATH=$INIT_PATH type -ap $2`
    
        #set scriptpath="" if script is not abs. path
        scriptpath=${script##[^/]*}  
        
        #set scriptpath=$INIT_D/$script if script not abs. path
        scriptpath=${scriptpath:-$INIT_D/$script} 
        
        if [ ! -x "$scriptpath" -o ! -f "$scriptpath" ]; then
          echo 1>&2 "Cannot execute $scriptpath"
          exit 1
        fi
        shift 2
        exec $scriptpath "$@"
        exit $?
      fi

      ########################################################
      #            telinit <runlevel_name>|<num>      
      ########################################################

      #target could be a script (e.g. "single") rather than a runlevel num
        if [ -e "$INIT_D/$2" ];
        		then target=$2
       			else target=$runlevel_prefix$2$runlevel_suffix
        fi
        
        if [ ! -e "$INIT_D/$target"  ]; then
          echo 1>&2 Illegal runlevel: "$2"
          exit 1
        fi

        make_dir_list()
        {
          if [ ! -d "$INIT_D/$1" ]; then return; fi
          echo $1
          for f in $INIT_D/$1/* ; do
            f=${f##*/+}
            make_dir_list ${f##*/}
          done
        }

      #list of all directories needed by the target
        dirlist=$target" "`make_dir_list $target`

        cur_services=`initctl -d 2>/dev/null`
        cur_services=`expr "$cur_services" : '.*AVAILABLE SERVICES:\(.*\)STARTING SERVICES:.*'`
        
      #find the highest service in the stack of available services that is
      #one of the needed directories; everything above this service is not
      #part of the target runlevel
        rollback_target=""
        for s1 in $cur_services EnD_ofLiSt__; do 
          for s2 in $dirlist; do
            if [ $s1 == $s2 ]; then rollback_target=$s1; break 2; fi
          done
        done

        need -r "$rollback_target" || exit_if_fatal_error
        need "$target" || exit_if_fatal_error
    ;;
  *)
    exit 1
    ;;
esac
    
exit 0
