#! /bin/sh


###############################################################################
#
# Create a jail for mldonkey-server (by using makejail)
#
#==========================================#
#
#     Copyright (C) 2005 by Mick Kappenburg                                  
#                                                                            
#     This program is free software; you can redistribute it and/or modify   
#     it under the terms of the GNU General Public License as published by   
#     the Free Software Foundation; either version 2 of the License, or      
#     (at your option) any later version.                                    
#                                                                            
#     This program is distributed in the hope that it will be useful,        
#     but WITHOUT ANY WARRANTY; without even the implied warranty of         
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
#     GNU General Public License for more details.                           
#                                                                            
#     You should have received a copy of the GNU General Public License      
#     along with this program; if not, write to the                          
#     Free Software Foundation, Inc.,                                        
#     59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.              
#
###############################################################################


#
# v0.3: 09-05-2005:
#         - change from bash to sh shell
#         - added copyright
#         - changed "$VAR" tests to "x$VAR"  
# v0.2: 05-05-2005: 
#         - added command line options
#         - added more tests
#         - rewrite code using static tmp filenames
#         - rewrite code which used indirec return code ($?)
# v0.1: 24-04-2005: script created: Mick Kappenburg
####



###############################################################################
#### VARS
setDefaults ()
{
  LOG=/dev/null
  DEFAULT_SETTINGS=/etc/default/mldonkey-server
  ##SETTINGS WHICH SHOULD BE IN $DEFAULT_SETTINGS
  CHROOT_DIR=/var/jail/mldonkey
  MLDONKEY_DIR=/var/lib/mldonkey
  MLDONKEY_USER=mldonkey
  MLDONKEY_GROUP=mldonkey
 ##not needed to create jail but could be added to make the install complete and 
 ##more difficult 
 # MLDONKEY_UMASK=0002
 # MAX_ALIVE=24
 # LAUNCH_AT_STARTUP=true
 # MLDONKEY_NICENESS=
 ##--
  ##Settings for makejail
  CLEANFIRST=0
}
########



###############################################################################
#### FUNCTIONS

#==========================================#
## Usage
usage ()
{
  local progname=$0
  progname=`basename $progname`
  setDefaults   #(re)set the defaults for correct (??) output

  cat <<EOF

  Usage:  
    $progname [options]
    Options are:
      -f <filename>  file containing default settings. Default $DEFAULT_SETTINGS
      -u <user>      run mldonkey-server as user <user>. Default $MLDONKEY_USER
      -g <group>     run mldonkey-server as group <group>. Default $MLDONKEY_GROUP
      -r <path>      root directory for jail. Default $CHROOT_DIR
      -d <path>      directory for downloads and mldonkey 'run time' files are stored. Default $MLDONKEY_DIR
      -l <filename>  write log to <filename>. Default $LOG
      -c             clean jail first. MOST BUT NOT ALL FILES ARE DETETED.
                     (TODO explain this some more for now, read the source)
      -y             assume yes to all questions
      -h             shows this message end exits
EOF
}

#==========================================#
## Get options
getOptions ()
{
  local argC=$#
  local argNr=0

  increment () { eval $1=$(($1+1)); }

  while getopts "f:u:g:r:d:l:cyh" arg; do
    increment argNr
    case $arg in
      f)  DEFAULT_SETTINGS=$OPTARG; increment argNr;;
      u)  MLDONKEY_USER=$OPTARG   ; increment argNr;;
      g)  MLDONKEY_GROUP=$OPTARG  ; increment argNr;;
      r)  CHROOT_DIR=$OPTARG      ; increment argNr;;
      d)  MLDONKEY_DIR=$OPTARG    ; increment argNr;;
      l)  LOG=$OPTARG             ; increment argNr;;
      c)  CLEANFIRST=1      ;;
      y)  ASUME_YES=1       ;;
      h)  usage; myExit     ;;  #myExit has return value 1, but the help message probably succeeded
      *)  usage; myExit     ;;
    esac
  done

  if [ "x$argC" != "x$argNr" ]; then 
    eval "faultArg=\$$OPTIND";
    echo
    echo "  Oeps, first non valid argument is argument number $OPTIND ($faultArg).";
    usage;
    myExit 
  fi
}



#==========================================#
##exit function with message
myExit ()
{
  echo -e "   $@"
  exit 1
}


#==========================================#
## myTrap
myTrap ()
{
  trap - $TRAPPED;               #reset traps
  runAtExit;                     #run commands added with: runAtexit add comand args
  #now exit with a nice message
  myExit "Oeps, trap triggered.\n Don't know the state I left your system in, sorry.\n Could have made a mess in $CHROOT_DIR, even proc can be mounted there!"
}

#==========================================#
## Functions run at exit
runAtExit ()
{
  case "$1" in
    "add")
      shift;
      functionsRunAtExit[${#functionsRunAtExit[*]}]=$@
      return
      ;;
    "run"|"")
      runCount=${#functionsRunAtExit[*]};
      for i in `seq 0 $(($runCount - 1))`; do
        ${functionsRunAtExit[$i]}
      done
      ;;
    *)
      myExit "Error: Wrong arg given to runAtExit. This is a bug!!"
      ;;
  esac
}

#==========================================#
## Extend PRESERVE variable, takes a path to a dir or file as argument
addToPreserve ()
{
  if [ "x$PRESERVE" = "x" ] ; then
    PRESERVE=\"$@\"
  else
    PRESERVE=$PRESERVE,\"$@\"
  fi
}

#==========================================#
##umount and remove proc if proc already in CHROOT_DIR
##(???Bug in makejail? If cleanJailFirst=1 and proc in preserve list, makejail
## results in an OSerror (no 17).  (Not reported yet) ???)
manageProc ()
{
  if [ -e $CHROOT_DIR/proc ] ; then
    umount  $CHROOT_DIR/proc 2> /dev/null 
    rmdir $CHROOT_DIR/proc
  fi
}

#==========================================#
##create configuration file for makejail
##(the config file is a python script!)
create_configfile_makejail ()
{
  cat >$PYTHON_SCRIPT <<EOB
##
# Tmp makejail configuration file for mldonkey
# (auto created)
##

##
cleanJailFirst=$CLEANFIRST
sleepAfterStartCommand=$SLEEP_AFTER_START_COMMAND
useDepends=$USE_DEPENDS
keepStraceOutputs=0
##

chroot="$CHROOT_DIR"
packages=["mldonkey-server"]

preserve=["/proc",$PRESERVE]

#forceCopy=["/etc/group","$MLDONKEY_DIR/*"]
forceCopy=["$MLDONKEY_DIR/*"]
#defaults for doNotCopy (at the moment of writing... doNotCopy is not defined yet => += doesn't work..)
doNotCopy=["/etc/hosts","/usr/share/doc","/usr/share/info","/usr/share/man","/etc/fstab","/etc/mtab"]
doNotCopy+=$EXTRA_DONOTCOPY

$JAIL_GROUPS
$JAIL_lUSERS


testCommandsInsideJail=["$MLDONKEY_START"]
processNames=["mldonkey_server","mlnet","logger"]
#testCommandsOutsideJail=["telnet $TELNET_PORT"]

EOB
}
####




###############################################################################
##### MAIN

#==========================================#
#### Vars not settable by commandline, mainly for makejail
#TELNET_PORT=4000
MLDONKEY_START="/etc/init.d/mldonkey-server start"
SLEEP_AFTER_START_COMMAND=3
USE_DEPENDS=0
#files which we don't want in the jail. Makejail's doc tells us we can use shell patterns
#like * and ?. The patterns won't work. The source matches the start of the /path/file 
#with the list => use /lib/libcrypt in stead of /lib/libcrypt* (???bug in makejail doc??? not 
#reported yet)
EXTRA_DONOTCOPY='["/etc/hosts","/etc/nsswitch.conf","/lib/libcrypt","/lib/resolv","/usr/bin/perl"]'
PRESERVE=""
addToPreserve "/proc"
#addToPreserve $MLDONKEY_DIR ##added later, after init settings
JAIL_USERS=""
JAIL_GROUPS=""
##


#==========================================#
## Set, get, overwrite, hack, loos etc.  the default settings
setDefaults            #set the defaults defined internal
. $DEFAULT_SETTINGS    #get external defined vars
getOptions $@          #get the command line options

addToPreserve $MLDONKEY_DIR #Now we know the real $MLDONKEY_DIR add it to the preserve list


#==========================================#
## Some checks:
##exit if CHROOT_DIR is in a stupid place
if [ "x$CHROOT_DIR" = "x" ] || [ "x$CHROOT_DIR" = "x/" ] ; then
  myExit "Root dir for jail is / or empty. This makes no sense => bailing out"
fi
##check if CHROOT_DIR exist already
if [ -e $CHROOT_DIR ] && [ "x$ASUME_YES" != "x1" ] ; then
  echo "The jail root directory already exist. ($CHROOT_DIR)"
  echo "Some files will be deleted / overwritten. Do you want to continue? (yes/No):"
  read ANSWER;
  while [ "x$ANSWER" != "xyes" ] && [ "x$ANSWER" != "xno" ] && [ "x$ANSWER" != "x" ] ; do
    echo "Please say 'yes' or 'no' (the complete word, or just hit enter for no)"
    read ANSWER
  done 
  if [ "x$ANSWER" != "xyes" ] ; then
    myExit
  fi
fi

#### External Commands used:
##not checked:
# mount, umount, mkdir, rmdir, rm, cat, cut, grep, mktemp
##checked:
#  makejail, ypcat       #telnet
####
if [ ! -x /usr/sbin/makejail ] ; then
  myExit "makejail not found in /usr/sbin. Sorry, can't do much without it" 
fi
if [ -x  /usr/bin/ypcat ] ; then
  HAS_YPCAT=TRUE
fi


#### 
# test if critical files exist
if [ "$DEFAULT_SETTINGS" ] && [ ! -e $DEFAULT_SETTINGS  ] ; then
  myExit "Sorry, can't read file $DEFAULT_SETTINGS (file containing default settings).\n Use -h to see how to change the location."
fi


#==========================================#
## SET TRAPS
TRAPPED="ERR EXIT"
trap myTrap $TRAPPED


#==========================================#
##INIT GLOBAL VARS
####Gobal vars
## Array holding all functions needed to run on exit
declare -a functionsRunAtExit
## Location script for makejail 
PYTHON_SCRIPT=`mktemp -t makejail_mldonkey-server.py.XXXXXX`
runAtExit add rm -f $PYTHON_SCRIPT

#==========================================#
mkdir -p $CHROOT_DIR


#==========================================#
## Gather/create some info
#makejail doesn't extract nis entries to passwd/group file (it
#installs the nis tools)
#Further, we want to make sure there are no members added to the group
#and the user shell must be /bin/false (and the home dir is set to $MLDONKEY_DIR, 
#just for fun :)

if [ "x$HAS_YPCAT" = "xTRUE" ] ; then
  MLD_GROUP=`{ ypcat group 2>/dev/null | grep $MLDONKEY_GROUP | cut --fields=1-3 -d: ;  }`
  MLD_PASSWD=`{ ypcat passwd 2>/dev/null | grep $MLDONKEY_USER | cut --fields=1-5 -d: ;  }`
else
  MLD_GROUP=""
  MLD_PASSWD=""
fi
if [ -n "$MLD_GROUP"  ] ; then
  mkdir -p $CHROOT_DIR/etc
  echo -e "root:x :0:\n$MLD_GROUP:" >  $CHROOT_DIR/etc/group
  addToPreserve "/etc/group"
else
  JAIL_GROUPS="groups=[\"root\",\"$MLDONKEY_GROUP\"]"   #add groups to makejail configuration file
fi

if [ -n "$MLD_PASSWD" ] ; then
  mkdir -p $CHROOT_DIR/etc
  echo -e "root:x:0:0:root:/root:/bin/false\n$MLD_PASSWD:$MLDONKEY_DIR:/bin/false"  > $CHROOT_DIR/etc/passwd
  addToPreserve "/etc/passwd"
else
  JAIL_USERS="users=[\"root\",\"$MLDONKEY_USER\"]"   #add users to makejail configuration file
fi



#==========================================#
create_configfile_makejail
manageProc  #see function description
makejail $PYTHON_SCRIPT > $LOG

#clean tmp files (and do other stuff?)
runAtExit;
##reset traps
trap - $TRAPPED;      
######
######
