#!/bin/sh
#
# $Id: start,v 1.134 2001/06/06 21:02:33 per Exp $

### If --silent-start is given as the first argument,
### nothing will be printed to stdout by the script.

if [ "x$1" = "x--silent-start" ]
then
    SILENT_START="y"
    shift
fi

pre="`echo \"                   \" | sed -e 's/\(.........\)\(.*\)/\1/g'` :"

dp() {
  if [ "x$SILENT_START" = "xy" ]
  then :; else
    echo "$pre" "$@" 2>&1
  fi
}


change_owner( ) {
 uid="$1";  shift
 gid="$1";  shift

 for a in "$@" ; do 
    ok=`ls -lgd $a 2>/dev/null | grep $uid | grep $gid | wc -l`
    ok=`echo $ok`
    if [ x$ok = x1 ] ; then 
      dp "$a already has the correct ownership"
    else
      dp "Changing ownership of $a to $uid:$gid"
      if chown -R "$uid" "$a" 2>/dev/null ; then
        if  chgrp -R "$gid" "$a" 2>/dev/null ; then
	  :
        else
          dp "Group change failed"
        fi
       else
        dp "Ownership change failed"
       fi
     fi
 done
}


check_owner() {
   uid=`$pike -e 'write("%d",getuid())'`
   gargs=''

   if [ x"$uid" = x0 ] ; then
     # Shall we change uid/gid?
     num=`grep --help 2>&1|grep gnu|wc -l`
     num=`echo $num`
     if [ x"$num" = x1 ] ; then
       gargs='-a'
     fi
     ugid=`grep $gargs "='User'>" $DIR/Global_Variables 2>/dev/null\
           | sed -e 's/.*<str>//' -e 's,</str>.*,,'`
     if [ ! x$ugid = x ] ; then
        dp "Doing uid change to $ugid"
        oifs="$IFS";IFS=':';set $ugid;IFS="$oifs"
        change_owner $1 $2 $DIR $VARDIR $LOGDIR
        ok=`ls -lgd . 2>/dev/null | grep $2 |wc -l`
        ok=`echo $ok`
        if [ "x$ok" = x1 ] ; then
          dp ". is already readable by $2"
        else
  	  chgrp -R "$2" .
          chmod -R g+w  .
        fi
     fi
   fi
}


# Breaks on linux when using symlinks.
dn="`dirname $0`"
case "$dn" in 
 ""|".")
  ;;
 *)
  dp "Changing current directory to '$dn' (now `pwd`)"
  cd $dn
  dp "Got new directory as `pwd`"
  ;;
esac

# Can be set with '--config-dir=DIR'
DIR=../configurations/
LOGDIR=../logs/
FILES="default"
program=base_server/roxenloader.pike
extra_args=""
VARDIR=../var/
LOCALDIR=../local/

# Make LOCALDIR an absolute path
if test -d $LOCALDIR/.; then
  LOCALDIR=`cd $LOCALDIR; pwd`
else :; fi

if test -d $VARDIR/.; then :; else
  dp "Creating directory $VARDIR"
  mkdir $VARDIR || exit 1
fi

roxen_version() {
   VERSION="`sed <etc/include/version.h -e '/__roxen_version__/s/[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\)[^0-9]*/\1/p' -n | head -1`"
   BUILD="`sed <etc/include/version.h -e '/__roxen_build__/s/[^0-9]*\([0-9][0-9]*\)[^0-9]*/\1/p' -n | head -1`"
   echo $VERSION.$BUILD
}

fullpath() {
  (
    IFS=:
    for path in $PATH ; do
      if [ -x "$path/$1" ] ; then
	full="$path/$1"
	break
      fi
    done
    test "x$full" != "x" && echo $full
  )
}

pcdir="$VARDIR/`roxen_version`/precompiled/`uname -m | tr ' ' '_' `.`uname -r`"
old_roxen_defines="$pcdir/old_roxen_defines"
./mkdir -p $pcdir
chmod 1777 $pcdir

# Default verbosity level.
verbose=1

# Do not default to using a relative path.
roxendir="`pwd`"

# Locate Pike binary.
pike=`fullpath pike`
if [ -x bin/pike ] ; then pike=$roxendir/bin/pike; fi
if [ -x bin/roxen ] ; then  pike=$roxendir/bin/roxen; fi
if [ -x $LOCALDIR/bin/pike ] ; then pike=$LOCALDIR/bin/pike; fi
if [ -x $LOCALDIR/bin/roxen ] ; then pike=$LOCALDIR/bin/roxen; fi
if [ "x$PIKE" = "x" ]; then :; else
  if [ -x "$PIKE" ]; then pike="$PIKE";
  else
    pikepath=`fullpath "$PIKE"`
    if [ "x$pikepath" != "x" ]; then pike="$pikepath";
    else dp "$PIKE is not executable - ignored."; fi
  fi
fi


if [ x"$pike" = "x" ] ; then
  dp "No pike binary found. Aborting."
  exit 1
fi

if [ ! -f "$pike" ] ; then
  dp "No pike binary found. Aborting."
  exit 1
fi

# If environment file doesn't exist, try to create it.
if
  test ! -f $LOCALDIR/environment && test -f bin/buildenv.pike
then
  $pike bin/buildenv.pike
  touch $LOCALDIR/environment
fi

# Set up environment
if test -f $LOCALDIR/environment; then
  . $LOCALDIR/environment
fi

# Make sure $CLASSPATH contains the servlet stuff
CLASSPATH=java/classes${CLASSPATH:+:}$CLASSPATH
for jar in java/classes/*.jar; do
  CLASSPATH="$jar:$CLASSPATH"
done
export CLASSPATH


####### BEGIN ARGUMENT PARSING


DEFINES="$DEFINES -DRAM_CACHE"

# Enable threads (if available) on Solaris.
# Most other OS's have thread bugs that cause them or Roxen to crash.
if uname | grep 'SunOS' >/dev/null 2>&1; then
  if uname -r | grep '5\.[5-9]' >/dev/null 2>&1; then
#    if [ $verbose -gt 0 ] ; then
#      dp "Solaris 2.5 or later detected. Using threads by default."
#    fi
    DEFINES="$DEFINES -DENABLE_THREADS"
  fi
fi

gdb=no

remove_old_dot_o_files () {
  dp "Removing old .o files ($1)"
  find $pcdir -name '*.o' | xargs rm -f
}

## Parse all arguments.
## GNU-style, long options only, except for -D, simply passed on.
ARGS=""

parse_args() {
 while [ ! c"$1" = "c" ] ; do
  case $1 in
  -D*)
   DEFINES="$DEFINES $1"
  ;;
  -l*)
   ARGS="$ARGS $1"
  ;;
  --truss)
     pike="truss $pike"
   ;;
  --truss-c)
     pike="truss -c $pike"
   ;;
  --log-dir=*)
   LOGDIR=`echo $1 | sed -e 's/--log-dir=//'`
  ;;
  --config-dir=*)
   DIR=`echo $1 | sed -e 's/--config-dir=//'`
   FILES=`echo $1 | sed -e's/--config-dir=//' -e's/\.//g' -e's./..g' -e 's.-..g'`
  ;;
  --pid-file=*)
   extra_args="$extra_args $1"
  ;;
  '--with-security'|'--enable-security')
    DEFINES="$DEFINES -DSECURITY"
  ;;

  '--debug'|'--with-debug'|'--enable-debug')
    debug=1
  ;;
  '--without-debug')
    debug=-1
  ;;
  '--fd-debug'|'--with-fd-debug'|'--enable-fd-debug')
    DEFINES="-DFD_DEBUG $DEFINES"
  ;;
  '--strip-backslash'|'--without-backslash'|'--strip-back-slash')
    DEFINES="-DSTRIP_BSLASH $DEFINES"
  ;;
  '--without-ram-cache'|'--disable-ram-cache')
    DEFINES="`echo $DEFINES | sed -e 's/-DRAM_CACHE//g'`"
  ;;
  '--without-ram-cache-stat'|'--disable-ram-cache-stat')
    DEFINES="`-DRAM_CACHE_ASUME_STATIC_CONTENT`"
  ;;
  '--dump-debug'|'--with-dump-debug'|'--enable-dump-debug')
    DEFINES="-DDUMP_DEBUG $DEFINES"
  ;;
  '--threads'|'--with-threads'|'--enable-threads')
    DEFINES="-DENABLE_THREADS $DEFINES"
  ;;
  '--no-threads'|'--without-threads'|'--disable-threads')
    DEFINES="`echo $DEFINES | sed -e 's/-DENABLE_THREADS//g'`"
  ;;
  '--no-keep-alive'|'--without-keep-alive'|'--disable-keep-alive')
    DEFINES="`echo $DEFINES | sed -e 's/-DKEEP_ALIVE//g'`"
  ;;
  '--with-profile'|'--profile')
    DEFINES="-DPROFILE $DEFINES"
  ;;
  '--with-file-profile'|'--file-profile')
    DEFINES="-DPROFILE -DFILE_PROFILE $DEFINES"
  ;;
  '--keep-alive'|'--with-keep-alive'|'--enable-keep-alive')
    DEFINES="-DKEEP_ALIVE $DEFINES"
  ;;
  '--quiet'|'-q')
    verbose=0
  ;;
  '--verbose'|'-v')
    verbose=2
    debug=1
  ;;
  '--remove-dumped')
     remove_dumped=1;
  ;;
  '--once')
    once=1
  ;;
# Misspelling --once might give undesirable results, so let's accept
# some "creative" spellings...  :-)
  '--onve'|'--onec'|'--onev')
    once=1
  ;;
  '--gdb')
    gdb=gdb
    once=1
  ;;
  '--program')
    program="$2"
    once=1
    shift
  ;;
  '--cd')
    cd_to="$2"
    # Use the absolute path...
    roxendir="`pwd`"
    once=1
    shift
  ;;
  --debug-without=*|-r*|-d*|-t*|-l*|-w*)
    # Argument passed along to Pike.
    ARGS="$ARGS $1"
  ;;
  -D*|-M*|-I*|-P*)
    # Argument passed along to Pike.
    DEFINES="$DEFINES $1"
  ;;
  '--version')
    if [ -f base_server/roxen.pike ]; then
      echo "Roxen WebServer `roxen_version`"
      exit 0
    else
      echo 'base_server/roxen.pike not found!'
      exit 1
    fi
  ;;
  '--help'|'-?')
  sed -e "s/\\.B/`tput 'bold' 2>/dev/null`/g" -e "s/B\\./`tput 'sgr0' 2>/dev/null`/g" << EOF
.BThis command will start the Roxen WebServerB..

The environment variable .BROXEN_ARGSB. can be used to specify
the default arguments.

   .BArguments:B.

      .B--versionB.:                  Output version information.

      .B--help -?B.:                  This information.

      .B--remove-dumpedB.:            Remove all dumped code, thus forcing
                                  a recompile.

      .B--verbose -vB.:               Enable more verbose messages.

      .B--quiet -qB.:                 Disable most of the messages.

      .B--log-dir=DIRB.:              Set the log directory. Defaults to .B../logsB..

      .B--config-dir=DIRB.:           Use an alternate configuration directory.
				  Defaults to .B../configurationB..

      .B--silent-startB.:             Inhibits output to stdout. If used,
                                  this argument must be the first one.

      .B--strip-backslashB.:          Replace all \\ characters in URIs with /


      .B--without-ram-cacheB.:        Do not use an in-RAM cache to speed
				  things up. Saves RAM at the cost of speed.

      .B--without-ram-cache-statB.:   Disable the stat that is usualy done
                                  for files in the ram cache to ensure that
				  they are not  changed before they are sent.
                                  Improves performance at the cost of constant
                                  aggrevation if the site is edited. Useful for
				  truly static sites.

      .B--with-threadsB.:             If threads are available, use them.

      .B--without-threadsB.:          Even if threads are enabled by default,
                                  disable them.

      .B--with-profileB.:             Store runtime profiling information on
				  a directory basis. This information is
 				  not saved on permanent storage, it is only
				  available until the next server restart
				  This will enable a new 'action' in the
				  administration interface

      .B--with-file-profileB.:        Like .B--with-profileB., but save information
                                  for each and every file.

      .B--onceB.:                     Run the server only once, in the foreground.
			   	  This is very useful when debugging.

      .B--gdbB.:                      Run the server in gdb. Implies .B--onceB..

      .B--programB.:                  Start a different program with the roxen
				  Pike. As an example,
                                  .B./start --program bin/install.pikeB. will
				  start the installation program normally
                                  started with .B./installB.

      .B--with-debugB.:               Enable debug

      .B--without-debugB.:            Disable all debug

      .B--with-fd-debugB.:            Enable FD debug.

      .B--with-dump-debugB.:          Enable dump debug.

      .B--trussB.:                    (Solaris only). Run the server under
				  truss, shows .BallB. system calls. This is
				  extremely noisy, and is not intented for
				  anything but debug.

      .B--truss-cB.:                  (Solaris only). Run the server under
				  truss -c, shows times for all system calls
                                  on exit. This is not intented for anything
                                  but debug. Slows the server down.

      .B--pid-file=<file>B.:          Store the roxen and startscript pids in this
				  file. Defaults to .B/tmp/roxen_$UIDB.

  .BArguments passed to pike:B.

       .B-DDEFINEB.:                  Define the symbol .BDEFINEB..

       .B-d<level>B.:                 Set the runtime Pike debug to level.
				  This only works if Pike is compiled
				  with debug.

       .B-rtB.:                       Enable runtime typechecking.
				  Things will run more slowly, but it is very
                                  useful while developing code.

				  Enabled when starting roxen with --debug

       .B-rTB.:                       Enable strict types.
				  Same as adding #pragma strict-types
				  to all files.

				  This enables more strict
				  type-checking, things that are
				  normally permitted (such as calling
				  a mixed value, or assigning a typed
				  object variable with an untyped
				  object) will generate warnings.

				  Useful for module and roxen core
				  developers, but not so useful for
				  the occasional pike-script-writer.

				  Enabled when starting roxen with --debug

       .B-s<size>B.:                  Set the stack size.

       .B-M<path>B.:                  Add the path to the Pike module path.

       .B-I<path>B.:                  Add the path to the Pike include path.

       .B-P<path>B.:                  Add the path to the Pike program path.

       .B-dtB.:                       Turn off tail recursion optimization.

       .B-tB.:                        Turn on Pike level tracing.

       .B-t<level>B.:                 Turn on more Pike tracing. This only
				  works if Pike is compiled with debug.

       .B-wB.:                        Turn on Pike warnings.

  .BEnvironment variables:B.

     .BLANGB.:                        Used to determine the default locale
                                  in the administration interface and logs.
     .BROXEN_CONFIGDIRB.:             Same as .B--config-dir=... B.
     .BROXEN_PID_FILEB.:              Same as .B--pid-file=... B.
     .BROXEN_LANGB.:                  The default language for all language
		                  related tags. Defaults to 'en' for english.

EOF
    tput 'rmso' 2>/dev/null
    exit 0
   ;;
  *)
    pass="$pass $1"
   ;;
  esac
  shift
 done
}

if [ ! "X$ROXEN_ARGS" = "X" ]; then
  parse_args $ROXEN_ARGS
fi

parse_args $@

# The work has already been done above, but the debug printout is better
# to have _after_ parse_args (consider --help and --version)

if [ ! "X$ROXEN_ARGS" = "X" ]; then
  if [ $verbose -gt 0 ]; then
    dp "Used $ROXEN_ARGS from ROXEN_ARGS."
  else :; fi
fi


#if uname | grep 'SunOS' >/dev/null 2>&1; then
#  if uname -r | grep '5\.[5-9]' >/dev/null 2>&1; then
#    if [ $verbose -gt 0 ] ; then
#      dp "Solaris 2.5 or later was detected. Threads enabled by default."
#    fi
#  fi
#fi


if [ ! "X$pass" = "X" ] ; then set -- $pass ;fi


####### END ARGUMENT PARSING


####### BEGIN PIKE OPTIONS

# Roxen WebServer will create files as the initial user,
# which it expects to be able to read as the run-time user.
umask 022

if [ "x$PIKE_NO_DEFAULT_PATHS" = "x" ]; then
  # Pike default Master-program
  if [ "x$PIKE_MASTER" = "x" ]; then
    if [ -f lib/master.pike ]; then
      DEFINES="$DEFINES -m$roxendir/lib/master.pike"
    elif [ -f lib/pike/master.pike ]; then
      DEFINES="$DEFINES -m$roxendir/lib/pike/master.pike"
    fi
  else
    # This is useful when using several different Pikes.
    # Specify include and module paths with
    # PIKE_INCLUDE_PATH and PIKE_MODULE_PATH
    # they are handled automatically by the master,
    # so no need to do it here.
    DEFINES="$DEFINES -m$PIKE_MASTER"
  fi
fi

# Extra module-path
if [ -d etc/modules ]; then
  DEFINES="$DEFINES -M$roxendir/etc/modules"
fi

# Extra include-path
if [ -d etc/include ]; then
  DEFINES="$DEFINES -I$roxendir/etc/include"
fi

if [ -d $LOCALDIR/include ]; then
  DEFINES="$DEFINES -I$LOCALDIR/include"
fi

# Extra include-path (2)
if [ -d base_server ]; then
  DEFINES="$DEFINES -I$roxendir/base_server"
fi

if [ -d $LOCALDIR/base_server ]; then
  DEFINES="$DEFINES -I$LOCALDIR/base_server -P$LOCALDIR/base_server"
fi

# Extra program-path
DEFINES="$DEFINES -P`pwd`"

# Support for adding local pike-modules
if [ -d $LOCALDIR/etc/. ]; then
  # Extra module-path
  if [ -d $LOCALDIR/etc/modules/. ]; then
    DEFINES="$DEFINES -M$LOCALDIR/etc/modules"
  fi

  # Extra include-path
  if [ -d $LOCALDIR/etc/include ]; then
    DEFINES="$DEFINES -I$LOCALDIR/etc/include"
  fi

  # Extra program-path
  DEFINES="$DEFINES -P$LOCALDIR/etc"
fi

# Extra kludge for HPUX
# HPUX doesn't like group 60001(nobody)
if uname | grep 'HP-UX' >/dev/null 2>&1; then
  if [ $verbose -gt 0 ]; then
    dp "WARNING: Applying kludge for HPUX. (see base_server/privs.pike)"
  else :; fi
  DEFINES="$DEFINES -DHPUX_KLUDGE"
fi


case "x$debug" in
  "x")
    DEBUG="-DMODULE_DEBUG "
    ARGS="$ARGS -w"
    ;;
  "x-1")
    DEBUG=""
    ;;
  "x1")
    DEBUG="-DDEBUG -DMODULE_DEBUG"
    ARGS="$ARGS -w"
    ;;
esac

DEFINES="$DEBUG $DEFINES"

####### END PIKE OPTIONS


#
# Some useful functions
#

rotate () {
  b=5;
  for a in 4 3 2 1 ; do mv -f $1.$a $1.$b 2> /dev/null;  b=$a; done
}

signal_exit() {
  dp "Start script terminated."
  if [ "x$ROXEN_PID" != "x" ]; then
    kill $ROXEN_PID 2>/dev/null
    while kill -0 $ROXEN_PID 2>/dev/null; do
      sleep 1
    done
  fi
  dp "Roxen WebServer shutdown."
  exit 0
}

start_roxen() {
  check_owner
  if [ x$remove_dumped = x1 ] ; then
    remove_old_dot_o_files "user request"
  fi
  if [ "x$DIR" != "x../configurations/" ] ; then
    args="$DEFINES $ARGS $program --config-dir=$DIR $pass"
  else
    args="$DEFINES $ARGS $program $pass"
  fi
  if [ x$cd_to != x ] ; then
    cd $cd_to
  fi
  if [ "x$gdb" = "xno" ]; then
    if [ $verbose -gt 0 ]; then
      if [ $verbose -gt 1 -o -z "$once" ] ; then
        dp "Executing $pike $args $@"|sed -e "s!`pwd`!.!g"
      else
        dp "Using the '$pike' pike binary"|sed -e "s!`pwd`!.!g"
      fi
    fi

    trap signal_exit 2 15
    trap "" 1
    if [ "x$once" = "x" ]; then
      $pike $args "$@" 2>>$LOGDIR/debug/$FILES.1 1>&2 &
      ROXEN_PID=$!
      dp "Roxen WebServer server pid $ROXEN_PID."
      wait $! 2>/dev/null 1>&2
      exitcode="$?"
      ROXEN_PID=""
    else
      $pike $args "$@" 2>&1
    fi
  else
    echo >.gdbinit handle SIGPIPE nostop noprint pass
    echo >>.gdbinit handle SIGUSR1 nostop noprint pass
    echo >>.gdbinit handle SIGUSR2 nostop noprint pass
    echo >>.gdbinit handle SIGLWP nostop noprint pass
    firstline=`head -1 "$pike" 2>/dev/null`
    if expr "x$firstline" : 'x#! */.*' >/dev/null; then
      # Looks like a script. gdb will not grok it, so we try passing
      # --gdb to it instead (which works in the special case of the
      # bin/pike script that is built by the top level Makefile in the
      # Pike source tree).
      dp "Executing $pike --gdb $args $@"
      $pike --gdb $args "$@"
    else
      dp "Executing gdb $pike $args $@"
      echo >>.gdbinit run $args $@
      gdb $pike
    fi
    rm .gdbinit
  fi
}


#
# Now do the stuff
#
PIKEVERSION="`$pike --version 2>&1|head -1`"
LS="`ls -lL $pike 2>/dev/null` `ls -lL etc/modules/ etc/modules/Variable.pmod etc/modules/RXML.pmod`"

VERSION_DATA="$PIKEVERSION   $DEFINES   $LS"

if [ "$program" = "base_server/roxenloader.pike" ] ; then
  if [ "`cat $old_roxen_defines 2>/dev/null`" != "$VERSION_DATA" ] ; then
    remove_old_dot_o_files "defines or pike version changed"
  fi
  echo "$VERSION_DATA" > $old_roxen_defines
fi

if [ -z "$once" ] ; then
  if [ $verbose -gt 0 ]; then
    dp "Starting the Roxen WebServer."
  else :; fi
  ./mkdir -p $LOGDIR/debug/

  if [ $verbose -gt 0 ]; then
    dp "Using configuration from $DIR, storing the debug log in $LOGDIR/debug/$FILES.1."
    dp "You can use the administration interface in the server to get debug info."
  else :; fi

  if (
    (
      # Do not use the prefix since it contains the pid of the script
      # we were forked from and not the one of this fork, which is
      # confusing. Can't rebuild $pre from $$ here, since some sh's
      # seems to cache it, causing it to be wrong here.
      pre=""

      exec 3>&-

      while : ; do
	if test -d "$LOGDIR/debug/."; then :; else
	  # Avoid infinite loop if the debug directory is deleted.
	  # Thanks to Emils Klotins <emils@dot.lv> for reporting it.
	  if ./mkdir -p "$LOGDIR/debug/"; then :; else
	    dp "Failed to create log directory $LOGDIR/debug/."
	    exit 1
	  fi
	fi

	dp "Server start at `date`"
	dp "Debug log in $LOGDIR/debug/$FILES.1"
	rotate $LOGDIR/debug/$FILES
	start_roxen $extra_args

	if [ "$exitcode" -eq "0" ] ; then
	  # Clean shutdown.
	  dp "Roxen WebServer shutdown."
	  exit 0
	fi
	if [ "$exitcode" -lt "0" ] ; then
	  # Signal death.
	  dp "Roxen WebServer died of signal $exitcode. Restarting..."
	else
	  dp "Roxen WebServer down. Restarting."
	fi
      done
    ) &
    dp "Forked start script, pid $!." >&3
  ) 3>&1 </dev/null >$LOGDIR/debug/start_$FILES.output 2>&1; then
    :
  else
    dp 'Failed to spawn subshell. -- Permission problem?'
    exit 1
  fi

  # Try to get rid of some fd's.
  # Some /bin/sh's have problems detaching otherwise.

  exec >/dev/null
  exec </dev/null
else
  start_roxen $extra_args
fi
