#! /bin/sh
#
# Copyright (c) 2000-2002 SuSE GmbH Nuernberg, Germany. All rights reserved.
#
# Author: Marius Tomaschewski <mt@suse.de>, 2000-2002
#
# $Id: rc.script.in,v 1.2.2.2 2004/03/30 12:04:16 mt Exp $
#
# etc/init.d/ftp-proxy  --  LSB compliant(?) run level script
#
# see also ftp-proxy(8) and ftp-proxy.conf(5) manual pages.
#
### BEGIN INIT INFO
# Provides:             ftp-proxy
# Required-Start:       $network $remote_fs network-remotefs $syslog $named $time
# Required-Stop:        $network $remote_fs network-remotefs $syslog $named $time
# Default-Start:        3 5
# Default-Stop:         0 1 2 6
# Short-Description:    Starts SuSE proxy-suite ftp-proxy
# Description:          Starts SuSE proxy-suite ftp-proxy daemon.
### END INIT INFO
#
# Return values acc. to LSB for all commands but status:
# 0 - success
# 1 - generic or unspecified error
# 2 - invalid or excess argument(s)
# 3 - unimplemented feature (e.g. "reload")
# 4 - insufficient privilege
# 5 - program is not installed
# 6 - program is not configured
# 7 - program is not running
#
# Note that starting an already running service, stopping
# or restarting a not-running service as well as the restart
# with force-reload (in case signalling is not supported) are
# considered a success.

#
# default values...
#
FTP_PROXY_CAPS="no"
FTP_PROXY_NAME="ftp-proxy"
FTP_PROXY_BIN="/usr/sbin/${FTP_PROXY_NAME}"
FTP_PROXY_CFG="/etc/proxy-suite/${FTP_PROXY_NAME}.conf"

#
# common utils...
#
ldd="/usr/bin/ldd"
awk="/bin/awk"
grep="/bin/grep"
logger="/bin/logger"
compartment="/usr/sbin/compartment"
LOGGER="${logger} -i -t ${FTP_PROXY_NAME}-boot"
LOG_INF="${LOGGER} -p info"
LOG_ERR="${LOGGER} -p err"

# LSB compliant helper shell functions sourced from /etc/rc.status:
#      rc_check         check and set local and overall rc status
#      rc_status        check and set local and overall rc status
#      rc_status -v     ditto but be verbose in local rc status
#      rc_status -v -r  ditto and clear the local rc status
#      rc_failed        set local and overall rc status to failed
#      rc_failed <num>  set local and overall rc status to <num><num>
#      rc_reset         clear local rc status (overall remains)
#      rc_exit          exit appropriate to overall rc status
#
# I assume, the /etc/rc.status script is SuSE specific...
# -> port this script or copy rc.status from SuSE system...
#
. /etc/rc.status

# SuSE >= 8.0 uses sysconfig/NAME instead of rc.config
[ -f /etc/sysconfig/clock    ]          && . /etc/sysconfig/clock
# perhaps you may need to overide some of above variables...
[ -f /etc/sysconfig/${FTP_PROXY_NAME} ] && . /etc/sysconfig/${FTP_PROXY_NAME}

#
# check if binary and config installed...
#
if [ ! -x "${FTP_PROXY_BIN}" ] ; then
  echo "$0: unable to find ftp-proxy binary: $FTP_PROXY_BIN" 1>&2
  exit 5
fi
if [ ! -f "${FTP_PROXY_CFG}" ] ; then
  echo "$0: unable to find ftp-proxy config: $FTP_PROXY_CFG" 1>&2
  exit 6
fi

#
# check if we should use compartment
#
if [ "$FTP_PROXY_CAPS" = yes ] ; then
  [ -x "$compartment" ] || {
    echo "$0: unable to find compartment tool: $compartment" 1>&2
    exit 5
  }
else
  compartment=""
fi

#
# ftp-proxy.conf parsing function...
#
# remember: this script works case-sensitive,
#           the proxy-suite itself not...
#
read_config ()
{
  get_value ()
  {
    key=${1}
    shift
    val=${@}
    echo "$key=\"$val\""
  }

  if [ -n "$FTP_PROXY_CFG" ] && [ -f "$FTP_PROXY_CFG" ] ; then
    while read line ; do
      case "$line" in
        \#*|"")
        ;;
        AllowTransProxy*)
          eval $(get_value $line)
          # may be: yes, on, true, >0
          case "${AllowTransProxy}" in
           [Yy]*|[Oo]*|[Tt]*|[1-9]*)
             AllowTransProxy="yes" ;;
           *)
             AllowTransProxy="no" ;;
          esac
        ;;
        LogDestination*)
          eval $(get_value $line)
          if [ "${LogDestination}" != ${LogDestination#|*} ] ; then
            echo "$0: unable to handle a pipe as LogDestination" 1>&2
            return 1
          fi
        ;;
        User*)                eval $(get_value $line) ;;
        Group*)               eval $(get_value $line) ;;
        PidFile*)             eval $(get_value $line) ;;
        TCPWrapper*)          eval $(get_value $line) ;;
        LDAPServer*)          eval $(get_value $line) ;;
        ServerRoot*)          eval $(get_value $line) ;;
        ServerType*)          eval $(get_value $line) ;;
        DenyMessage*)         eval $(get_value $line) ;;
        MaxClientsMessage*)   eval $(get_value $line) ;;
        WelcomeMessage*)      eval $(get_value $line) ;;
        *) ;;
      esac
    done < "$FTP_PROXY_CFG"
    return 0
  else
    return 1
  fi
}

mk_chroot ()
{
  dir_mode="-m0755"
  state_mode="0775"

  owner=${User:-root}
  group=${Group:-root}

  if [ -n "${ServerRoot}" ] ; then
    if [ ! -d "${ServerRoot}" ] ; then
      [ -e "${ServerRoot}" ] && rm -f "${ServerRoot}"
      $LOG_INF "creating missing ServerRoot directory '$ServerRoot'"
      mkdir -p $dir_mode "${ServerRoot}" || return 1
    fi
  else
    $LOG_ERR "chroot directory not configured"
    return 1
  fi

  cpifnewer ()
  {
    for src in $1; do
       dst=$2/$(basename $src)
       if [ -e $src -a ! -f $src -a ! -L $src ]; then
         $LOG_INF "copying missing $dst"
         cp -fpR $src $dst || return 1
       elif [ $src -nt $dst -o $src -ot $dst ]; then
         [ -e "$dst" ] && rm -f "$dst"
         $LOG_INF "updating $dst"
         cp -fp $src $dst || return 1
       fi
    done
    return 0
  }

  #
  # list of directories that should be writeable
  #
  STATE_DIR=""

  DIRS="dev etc lib usr/sbin usr/lib"
  # add config directory
  DIRS="$DIRS "$(dirname "$FTP_PROXY_CFG")
  # add message direcrories
  [ -n "$DenyMessage"       ] && DIRS="$DIRS "$(dirname "$DenyMessage")
  [ -n "$WelcomeMessage"    ] && DIRS="$DIRS "$(dirname "$WelcomeMessage")
  [ -n "$MaxClientsMessage" ] && DIRS="$DIRS "$(dirname "$MaxClientsMessage")
  # add Pid-Directory
  if [ -n "$PidFile" ] ; then
    dir=$(dirname "$PidFile")
    DIRS="$DIRS $dir"
    STATE_DIR="$STATE_DIR $dir"
  else
    DIRS="$DIRS var/run"
    STATE_DIR="$STATE_DIR var/run"
  fi
  # add Log-Directory if a log file is used...
  if [ -n "$LogDestination" ] && \
     [ "${LogDestination}" != ${LogDestination#/*} ] ;
  then
    dir=$(dirname "$LogDestination")
    DIRS="$DIRS $dir"
    STATE_DIR="$STATE_DIR $dir"
  fi

  # ok, create the dirs
  [ -n "$DIRS" ] && for dir in $DIRS ; do
    [ -d "${ServerRoot}/${dir}" ] && continue
    [ -e "${ServerRoot}/${dir}" ] && rm -f "${ServerRoot}/${dir}"
    $LOG_INF "creating missing directory ${ServerRoot}/${dir}"
    mkdir -p $dir_mode "${ServerRoot}/${dir}" || return 1
  done
  # make sure, state-dirs are writeable after cap's chroot
  [ -n "$STATE_DIR" ] && for dir in $STATE_DIR ; do
    chmod $state_mode     "${ServerRoot}/${dir}"
    chown "$owner:$group" "${ServerRoot}/${dir}"
  done

  # create a null device
  if [ ! -c "${ServerRoot}/dev/null" ] ; then
    [ -e "${ServerRoot}/dev/null" ] && \
      rm -f "${ServerRoot}/dev/null"
    $LOG_INF "creating missing device '${ServerRoot}/dev/null"
    cpifnewer /dev/null "${ServerRoot}/dev" || return 1
    chmod 0666 "${ServerRoot}/dev/null" || return 1
  fi

  # BSD: create ipnat/pf natlook device if needed
  if [ -n "$AllowTransProxy" ] && [ "$AllowTransProxy" = yes ] ; then
    if [ -c /dev/ipnat ] && [ ! -c "${ServerRoot}/dev/ipnat" ] ; then
      [ -e "${ServerRoot}/dev/ipnat" ] && \
        rm -f "${ServerRoot}/dev/ipnat"
      $LOG_INF "creating missing device '${ServerRoot}/dev/ipnat"
      cpifnewer /dev/ipnat "${ServerRoot}/dev"        || return 1
      chmod 0440            "${ServerRoot}/dev/ipnat" || return 1
      chown "$owner:$group" "${ServerRoot}/dev/ipnat" || return 1
    fi
    if [ -c /dev/pf ] && [ ! -c "${ServerRoot}/dev/pf" ] ; then
      [ -e "${ServerRoot}/dev/pf" ] && \
        rm -f "${ServerRoot}/dev/pf"
      $LOG_INF "creating missing device '${ServerRoot}/dev/pf"
      cpifnewer /dev/pf     "${ServerRoot}/dev"    || return 1
      chmod 0440            "${ServerRoot}/dev/pf" || return 1
      chown "$owner:$group" "${ServerRoot}/dev/pf" || return 1
    fi
  fi

  # copy binaries
  BINS="$FTP_PROXY_BIN"
  [ -n "$BINS" ] && for bin in $BINS ; do
    if [ -n "$bin" ] && [ -x "$bin" ] ; then
      bdir=$(dirname $bin)
      if [ ! -d "${ServerRoot}/${bdir}" ] ; then
        [ -e "${ServerRoot}/${bdir}" ] && rm -f "${ServerRoot}/${bdir}"
        $LOG_INF "creating missing directory ${ServerRoot}/${bdir}"
        mkdir -p $dir_mode "${ServerRoot}/${bdir}" || return 1
      fi
      cpifnewer "$bin" "${ServerRoot}/${bdir}" || return 1

      #
      # copy all libs ldd says they are needed for the binary
      #
      LIB=$($ldd "$bin" 2> /dev/null | $grep -v "not "| $awk '{print$ 3}')
      for lib in $LIB ; do
        ldir=$(dirname $lib)
        if [ ! -d "${ServerRoot}/${ldir}" ] ; then
          [ -e "${ServerRoot}/${ldir}" ] && rm -f "${ServerRoot}/${ldir}"
          $LOG_INF "creating missing directory ${ServerRoot}/${ldir}"
          mkdir -p $dir_mode "${ServerRoot}/${ldir}" || return 1
        fi
        cpifnewer "$lib" "${ServerRoot}/${ldir}" || return 1
      done
    else
      $LOG_ERR "no such file or directory '$bin'"
      return 1
    fi
  done

  # handle /etc/localtime link
  if [ -L /etc/localtime ]; then
    ZONEDIR="/usr/share/zoneinfo"
    ZONEDST="$ZONEDIR"

    [ -n "$TIMEZONE" ] || TIMEZONE=GMT
    if [ $(dirname "$TIMEZONE") != "." ] ; then
      ZONEDST="${ZONEDST}/"$(dirname "$TIMEZONE")
    fi
    [ -d "${ServerRoot}/${ZONEDST}" ] || \
      mkdir -p $dir_mode "${ServerRoot}/${ZONEDST}"

    cpifnewer "${ZONEDIR}/${TIMEZONE}" "$ServerRoot/${ZONEDST}"
    ln -sf "../${ZONEDIR}/${TIMEZONE}" "${ServerRoot}/etc/localtime"
  else
    cpifnewer /etc/localtime "${ServerRoot}/etc"
  fi

  # copy proxy config
  if [ -f "$FTP_PROXY_CFG" ] ; then
    if [ -e "${ServerRoot}/${FTP_PROXY_CFG}" ] ; then
      rm -f "${ServerRoot}/${FTP_PROXY_CFG}"
    fi
    if [ -n "$compartment" ] ; then
      #
      # remove chroot-stuff compartment is used
      #
      while read line ; do
        case "$line" in
          \#*|"")         ;;
          Group*)         ;;
          User*)          ;;
          ServerRoot*)    ;;
          *) echo $line   ;;
        esac
      done < "$FTP_PROXY_CFG" > "${ServerRoot}/${FTP_PROXY_CFG}"
    else
      cp -f "$FTP_PROXY_CFG" "${ServerRoot}/${FTP_PROXY_CFG}"
    fi
  fi

  if [ ! -f "${ServerRoot}/etc/ld.so.conf" ] ; then
    touch "${ServerRoot}/etc/ld.so.conf"
  fi

  CONF="/etc/host.conf /etc/resolv.conf /etc/hosts"
  CONF="$CONF /etc/services /etc/protocols"
  CONF="$CONF /etc/passwd /etc/group"
  [ -f /etc/nsswitch.conf ] && CONF="$CONF /etc/nsswitch.conf"

  if [ -n "$TCPWrapper" ] && [ "$TCPWrapper" = yes ] ; then
    CONF="$CONF /etc/hosts.allow /etc/hosts.deny"
  fi
  [ -n "$DenyMessage"       ] && CONF="$CONF $DenyMessage"
  [ -n "$WelcomeMessage"    ] && CONF="$CONF $WelcomeMessage"
  [ -n "$MaxClientsMessage" ] && CONF="$CONF $MaxClientsMessage"

  # copy/update (config) files
  [ -n "$CONF" ] && for cfg in $CONF ; do
    if [ -n "$cfg" ] && [ -f "$cfg" ] ; then
      cdir=$(dirname $cfg)
      if [ ! -d "${ServerRoot}/${cdir}" ] ; then
        [ -e "${ServerRoot}/${cdir}" ] && rm -f "${ServerRoot}/${cdir}"
        $LOG_INF "creating missing directory ${ServerRoot}/${cdir}"
        mkdir -p $dir_mode "${ServerRoot}/${cdir}" || return 1
      fi
      cpifnewer "$cfg" "${ServerRoot}/${cdir}" || return 1
    else
      $LOG_ERR "no such file or directory '$cfg'"
      return 1
    fi
  done

  # copy/update nss libraries
  if [ -f /etc/nsswitch.conf ] ; then
    LIBS="/lib/libnss* /lib/libnsl* /lib/libresolv* /lib/libdb*"
  fi
  [ -n "$LIBS" ] && for lib in $LIBS ; do
    if [ -n "$lib" ] && [ -x "$lib" ] ; then
      ldir=$(dirname $lib)
      if [ ! -d "${ServerRoot}/${ldir}" ] ; then
        [ -e "${ServerRoot}/${ldir}" ] && rm -f "${ServerRoot}/${ldir}"
        $LOG_INF "creating missing directory ${ServerRoot}/${ldir}"
        mkdir -p $dir_mode "${ServerRoot}/${ldir}" || return 1
      fi
      cpifnewer "$lib" "${ServerRoot}/${ldir}" || return 1
    else
      $LOG_ERR "no such file or directory '$lib'"
      return 1
    fi
  done

  #
  # rebuild /etc/ld.so.cache
  #
  ldconfig -X -r "${ServerRoot}" 2>&1 | $LOG_INF

  return 0
}

#
# mk_chroot call-helper
#
do_chroot ()
{
  # chroot is useless if daemon runs with UID 0...
  if [ -z "$User" ] ; then
    echo "$0: set the User variable in $FTP_PROXY_CFG" 1>&2
    return 1
  fi

  [ -x "$grep" -a -x "$logger" -a -x "$ldd" -a -x "$awk" ] || {
    echo "$0: unable to find all needed utilities" 1>&2
    return 1
  }

  # prepare/update chroot dir
  mk_chroot || return 1

  # if syslog is used, a log socket should
  # be avaliable as ${ServerRoot}/dev/log
  if [ -n "${LogDestination}" ] && \
     [ "${LogDestination}" = "${LogDestination#/*}" ] && \
     [ ! -S "${ServerRoot}/dev/log" ] ;
  then
    echo "$0: add ${ServerRoot}/dev/log socket to syslog config" 2>&1
    return 1
  fi
  return 0
}

#
# initialize relevant config variables
#
User=""
Group=""
PidFile=""
TCPWrapper=""
LDAPServer=""
ServerRoot=""
ServerType=""
LogDestination=""
DenyMessage=""
MaxClientsMessage=""
WelcomeMessage=""

#
# read proxy configuration
#
read_config || exit 1

# reset status of this service
rc_reset

case "$1" in
chroot)
        # prepare chroot...
        do_chroot || exit 1

;;
start)
        # check if daemon configured
        if [ "${ServerType}" != standalone ] ; then
          echo "$0: ServerType is not standalone in $FTP_PROXY_CFG" 1>&2
          exit 1
        fi

        # prepare/update chroot dir if used
        if [ -n "${ServerRoot}" ] ; then
          do_chroot || exit 1
        fi

        if test -n "$compartment" ; then

          echo -n "Starting $FTP_PROXY_NAME (in ${ServerRoot})"
          [ -n $Group ] && chroot_group="--group $Group"
          PATH="/bin:/sbin:/usr/bin:/usr/sbin" \
          $compartment --fork --cap CAP_NET_BIND_SERVICE \
                       --chroot "$ServerRoot" $chroot_group \
                       "${FTP_PROXY_BIN}" -f "${FTP_PROXY_CFG}"
          rc_status -v
        else
          if test -n "$ServerRoot" ; then
            echo -n "Starting $FTP_PROXY_NAME (in ${ServerRoot})"
          else
            echo -n "Starting $FTP_PROXY_NAME"
          fi
          startproc "$FTP_PROXY_BIN" -f "$FTP_PROXY_CFG"
          rc_status -v
        fi

;;
stop)
        ## Stop the service
        echo -n "Shutting down $FTP_PROXY_NAME"
        [ -n "$compartment" -a -n "${ServerRoot}" ] || ServerRoot=""
        killproc -TERM "${ServerRoot}/${FTP_PROXY_BIN}"
        rc_status -v

;;
try-restart)
        ## Stop the service and if this succeeds (i.e.
        ## the service was running before), start it again.
        ## Note: try-restart is not (yet) part of LSB (as of 0.7.5)
        $0 status >/dev/null &&  $0 restart
        rc_status

;;
restart)
        ## Stop the service and regardless of whether it was
        ## running or not, start it again.
        $0 stop
        $0 start
        rc_status

;;
force-reload)
        ## Signal the daemon to reload its config.
        ## If it does not support it, restart.
        echo -n "Reload the $FTP_PROXY_NAME configuration"
        [ -n "$compartment" -a -n "${ServerRoot}" ] || ServerRoot=""
        killproc -HUP  "${ServerRoot}/${FTP_PROXY_BIN}"
        rc_status -v

;;
reload)
        ## Like force-reload, but if daemon does not
        ## support signalling, do nothing (!)
        echo -n "Reload the $FTP_PROXY_NAME configuration"
        [ -n "$compartment" -a -n "${ServerRoot}" ] || ServerRoot=""
        killproc -HUP  "${ServerRoot}/${FTP_PROXY_BIN}"
        rc_status -v

;;
status)
        echo -n "Checking for $FTP_PROXY_NAME"
        ## Check status with checkproc(8), if process is running
        ## checkproc will return with exit status 0.
        [ -n "$compartment" -a -n "${ServerRoot}" ] || ServerRoot=""
        checkproc "${ServerRoot}/${FTP_PROXY_BIN}"
        rc_status -v

;;
probe)
        ## Optional: Probe for the necessity of a reload,
        ## give out the argument which is required for a reload.
        [ -n "$compartment" -a -n "${ServerRoot}" ] || ServerRoot=""
        test "${ServerRoot}/${FTP_PROXY_CFG}" -nt \
             "${ServerRoot}/${PidFile}" && echo reload

;;
*)
        echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe|chroot}"
        exit 1
        ;;
esac

# Inform the caller not only verbosely and set an exit status.
rc_exit

