#!/bin/sh
#
# 	$Source: /cvsroot/bootcd/bootcd/bootcdbackupwizard.src,v $
#       $Id: bootcdbackupwizard.src,v 1.53 2007-11-16 16:36:34 bs Exp $
#    author: Carsten Dinkelmann <din@foobar-cpa.de>
#     start: 20.09.2007
#     topic: wizard for bootcddebootstrap and bootcdbackup
#
#  Download: the newest version of this wizard can be downloaded with "wget
#            ftp://bootcd.alioth.debian.org/pub/bootcd/bootcdbackupwizard"
#


# Version of debian package bootcd with this wizard included in 
# /usr/share/doc/bootcd/examples/bootcdbackupwizard
VERSION=3_08ubuntu1 # sources are tagged in cvs with rev=debian_version_${VERSION}

# fixe language problem
export LANG=C
export LC_ALL=C

# used basis directory under the workdir
IMGDIR=bootcd-img

# download from there

BOOTSTRAPPATH="ftp://ftp.de.debian.org/debian/pool/main/d/debootstrap/debootstrap_0.3.3.2etch1_all.deb"

CVSBOOTCD="http://alioth.debian.org/plugins/scmcvs/cvsweb.php/~checkout~/bootcd"
BOOTCDDEBOOTSTRAPPATH="$CVSBOOTCD/bootcddebootstrap?only_with_tag=debian_version_${VERSION};content-type=text%2Fplain;cvsroot=bootcd"
NEWESTBOOTCDBACKUPWIZARDPATH="$CVSBOOTCD/bootcdbackupwizard?rev=HEAD;content-type=text%2Fplain;cvsroot=bootcd"
BOOTCDMK2DISKCONFPATH="$CVSBOOTCD/bootcdmk2diskconf?only_with_tag=debian_version_${VERSION};content-type=text%2Fplain;cvsroot=bootcd"
NEWESTBOOTCDMK2DISKCONFPATH="$CVSBOOTCD/bootcdmk2diskconf?rev=HEAD;content-type=text%2Fplain;cvsroot=bootcd"

NEWESTBOOTCDDEBOOTSTRAPPATH="http://alioth.debian.org/plugins/scmcvs/cvsweb.php/~checkout~/bootcd/bootcddebootstrap?rev=HEAD;content-type=text%2Fplain;cvsroot=bootcd"

# needed tools
TOOLS="wget tar ./bootcddebootstrap"

# exclude from backing up (ending slash includes the dirextory itself!)
EXCLUDE_DIRS="/proc/ /sys/ /dev/"

# subdir to $workdir where the backup tar resists later
BACKUPDIR="backup"

# PATH to bootcdmk2diskconf, under $workdir
BOOTCDMK2DISKCONF="/usr/share/doc/bootcd/examples/bootcdmk2diskconf"

ERRLOG="/var/log/bootcdbackupwizard.log"

######################### nothing to configure behind this line ###############

SCRIPTNAME=$(basename $0)

IAHEAD="$SCRIPTNAME $VERSION"

### BOOTCD_LIB Placeholder ##############
# bootcd-run.lib
# vim: set filetype=sh :

#     version: $Version: $
#   copyright: Bernd Schumacher <bernd.schumacher@hp.com> (2007)
#     license: GNU General Public License, version 2
#     comment: use ANSW to change the next point from external
# description: To understand what to do with this library you can download
#              the test programs with wget
#
# The newest Version can be found here:
#   http://alioth.debian.org/plugins/scmcvs/cvsweb.php/~checkout~/bootcd/bootcd-run.lib?rev=HEAD;content-type=text%2Fplain;cvsroot=bootcd
#
# A really small test program:
#   http://alioth.debian.org/plugins/scmcvs/cvsweb.php/~checkout~/bootcd/bootcd-run.lib.tst1?rev=HEAD;content-type=text%2Fplain;cvsroot=bootcd
#
# A large test program:
#   http://alioth.debian.org/plugins/scmcvs/cvsweb.php/~checkout~/bootcd/bootcd-run.lib.tst2?rev=HEAD;content-type=text%2Fplain;cvsroot=bootcd

RUN_INTERACT="no"
LANG=C
LC_ALL=C
IGNORE=""
STDOUT=""

msg()
{
  echo "--- MESSAGE ---" | tee -a $ERRLOG
  echo "$@" | tee -a $ERRLOG >&2
}

set +u
if [ ! "$ERRLOG" ]; then
  ERRLOG=/var/log/bootcd.errlog
  touch "$ERRLOG" >/dev/null 2>&1
  [ $? -ne 0 ] && ERRLOG=/tmp/bootcd.errlog
  msg "ERRLOG was unset. It is set to $ERRLOG now."
fi
set -u

# function run: run given command and output error
# get: $IGNORE   -- regexp for error messages, generate no output, no question
#                   (set from function ignore)
#      $STDOUT   -- regexp for error messages, only output, no question
#                   (set from function stdout)
#      $ERRORLOG -- write to this file
#
#      $RUN_INTERACT -- "yes" meens run from interactiv
# comment: by default, all errors (stdout, error) generates output and questions 
#
# Attention: do not try to set global variables in a command called by run !!!
#
run()
{
  local ERR C T A
  #echo >&2 "IGNORE $IGNORE"
  #echo >&2 "STDOUT $STDOUT"
  while :
  do

    > $ERRLOG.tmp
    (
      eval "$@" 2>&1
      ERR=$?
      if [ "$ERR" != "0" ]; then
        echo "exit=$ERR"
      fi
    ) |
    if [ "$IGNORE" != "" ]; then
      C="grep -v $IGNORE"
      tee $ERRLOG.tmp | tee -a $ERRLOG | eval "$C"
    else
      tee -a $ERRLOG.tmp | tee -a $ERRLOG
    fi

    T=`cat $ERRLOG.tmp`
    if [ "$IGNORE" != "" ]; then
      C="grep -v $IGNORE"
      T=`echo "$T" | eval "$C"`
    fi
    if [ "$STDOUT" != "" ]; then
      C="grep -v $STDOUT"
      T=`echo "$T" | eval "$C"`
    fi
  
    if [ "$T" != "" ]; then
      A=""
      while [ "$A" != "e" -a "$A" != "r" -a "$A" != "i" -a "$A" != "s" ]
      do
        echo "--- OUTPUT from <$@> ---" | tee -a $ERRLOG
        echo "$T" | tee -a $ERRLOG
        if [ "$RUN_INTERACT" = "yes" ]; then 
          echo -n "--- (e)xit (r)edo (i)gnore (s)witch interactive --- " | tee -a $ERRLOG
        else 
          echo -n "--- (e)xit (r)edo (i)gnore --- " | tee -a $ERRLOG
        fi
        read A 
        echo "$A" >> $ERRLOG
      done
      if [ "$A" = "e" ]; then
        exit 1
      elif [ "$A" = "i" ]; then
        break
      # switch to interactive mode
      elif [ "$A" = "s" -a "$RUN_INTERACT" = "yes" ]; then
        echo -e "\nswitching to interactive mode to point ($ntrctv_ANSW) which failed\n"
        ntrctv_ANSW=$((ntrctv_ANSW-1))
        ntrctv_y=""
        break
      fi
    else
      break
    fi
  
  done
  IGNORE=""
  STDOUT=""
}

# function: build stdout for interactive
# get: $1 -- number
#      $2 -- stdout string
in_stdout()
{
  local nr=$1
  shift 

  eval STDOUT${nr}=\"\$STDOUT${nr} -e \\\"\$@\\\"\"
}

# function: build ignore for interactive
stdout()
{
  STDOUT="$STDOUT -e \"$@\""
}

# function: build ignore for interactive
# get: $1 -- number
#      $2 -- ignore string
in_ignore()
{
  local nr=$1
  shift 

  eval IGNORE${nr}=\"\$IGNORE${nr} -e \\\"\$@\\\"\"
}

## example
#mm=0
#let mm=$mm+1; eval IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
#in_ignore $mm "error bla"
#in_ignore $mm "^bluber sumsel .*"
#in_stdout $mm "no error but but a useful comment"
#let mm=$mm+1; eval IGNORE${mm}=\"\"
#in_norun $mm
#echo $IGNORE1 -- $IGNORE2
#exit

# function: run interactiv -r without run for this menu point
# get: $1 -- number
# comment: please update norunstring also in interactive!
in_norun()
{
  local norunstring="interactive_without_run"
  local nr=$1

  eval IGNORE${nr}=\"$norunstring\"
  eval STDOUT${nr}=\"$norunstring\"
}


ignore()
{
  IGNORE="$IGNORE -e \"$@\""
}

warn()
{
  if [ $# -gt 0 ]; then
    A=""
    while [ "$A" != "e" -a "$A" != "i" ]
    do
      echo "--- WARNING ---" | tee -a $ERRLOG
      echo "$@" | tee -a $ERRLOG
      echo -n "--- (e)xit (i)gnore --- " | tee -a $ERRLOG
      if [ "$SCRIPT" ]; then
        A="i"
	echo "$A (SCRIPT)"
        echo "$A (SCRIPT)" >> $ERRLOG
        echo
      else
        read A 
        echo "$A" >> $ERRLOG
      fi
    done
    if [ "$A" = "e" ]; then
      exit 1
    fi
  fi
}

err()
{
  (
  echo "--- FATAL ERROR ---" 
  echo "$@"
  ) |
  if [ "$(set | grep ^ERRLOG=)" ]; then
    tee -a $ERRLOG >&2
  else
    cat >&2
  fi
  exit 1
}

# create_host_keys <DIR>
#   if ssh_host_key, ssh_host_rsa or ... exists in <DIR>
#   then recreate it.
create_host_keys() 
{
  # Now only ssh versions which support Option -t are supported

  F=ssh_host_key
  if [ -f $1/$F ]; then  
    echo "Creating OpenSSH_2 $F" >>$ERRLOG
    run "rm -f $1/$F $1/$F.pub"
    run "ssh-keygen -b 1024 -f $1/$F -N '' -t rsa1 >>$ERRLOG 2>&1"
  fi

  F=ssh_host_rsa_key
  if [ -f $1/$F ]; then  
    echo "Creating OpenSSH_2 $F" >>$ERRLOG
    run "rm -f $1/$F $1/$F.pub"
    run "ssh-keygen -b 1024 -f $1/$F -N '' -t rsa >>$ERRLOG 2>&1"
  fi

  F=ssh_host_dsa_key
  if [ -f $1/$F ]; then  
    echo "Creating OpenSSH_2 $F" >>$ERRLOG
    run "rm -f $1/$F $1/$F.pub"
    run "ssh-keygen -b 1024 -f $1/$F -N '' -t dsa >>$ERRLOG 2>&1"
  fi
}

#      syntax: interactive [-y] [-e] [-h <head>] <functioncall with params> ...
# description: interactive evaluates the listed functioncalls. If the first 
#              argument is not -y an interactive menu will be given. This menu
#              can get a header <head> with -h. An extra evaluation of <head> 
#              and <functioncall with params> can be forced with option -e.
#              The <functioncall with params> can contain the special 
#              character sequences "<-i>" and "<h>". "<-i>" will be translated
#              either to "-i" or to "". This can be toggled with the key "i".
#              
#              -y        --   run without interaction
#              -e        --   evaluate each function call one times more
#              -h <head> --   print <head> as menu head
#              -r        --   run command with "run"
#
# helper functions for "run" (-r given to interactive):
#            in_stdout <number> <string> -- ignore stdout+stderr
#            in_ignore <number> <string> -- ignore stdout+stderr, don't print
#            in_norun <number>           -- run menupoint without run!
#
#
interactive()
{
  local ntrctv_ANSW ntrctv_A="" ntrctv_i ntrctv_y="" ntrctv_t="-i " ntrctv_head="" ntrctv_e="" ntrctv_run="" ntrctv_c
  local ntrctv_cmd
  local norunstring="interactive_without_run"

  # get all options
  while [ $# -ge 1 ]; do
    if [ "$1" = "-y" ]; then
      ntrctv_y="$1"
      ntrctv_t=""
      shift
    elif [ "$1" = "-e" ]; then
      ntrctv_e="$1"
      shift
    elif [ "$1" = "-h" -a $# -gt 2 ]; then
      ntrctv_head="$2"
      shift 2
    elif [ "$1" = "-r" ]; then
      ntrctv_run="yes"
      shift 1
    else
      break
    fi
  done
  
  # read the function calls
  if [ $# -gt 0 ]; then
    ntrctv_ANSW="1"
    while :; do
      if [ ! "$ntrctv_y" ]; then
        # print head 
        if [ "$ntrctv_head" ]; then 
	  ntrctv_c="$ntrctv_head"
          # evaluate the head
          [ "$ntrctv_e" ] && ntrctv_c="$(eval echo "$ntrctv_c")"
          echo "$ntrctv_c"
        fi
        ntrctv_i=1
        # build menu
        while [ $ntrctv_i -le $# ]; do
          ntrctv_c=$(echo "$ntrctv_i $(eval echo "\${$ntrctv_i}")" | sed "s/<-i>[[:space:]]*/$ntrctv_t/g")
          [ "$ntrctv_e" ] && ntrctv_c="$(eval echo "$ntrctv_c")"
          echo "$ntrctv_c"
          let ntrctv_i=ntrctv_i+1
        done
        
        # dbg exists, print flag
        [ "$(declare -f dbg)" ] &&  echo "d toggle dbg() function on and off"
        # i flag can be toggled 
        [ "$(echo "$*"|grep "<-i>")" ] && echo "i toggle -i flag"
        # menu point "continue"
        [ "$ntrctv_ANSW" != "q" ] && [ $ntrctv_ANSW -lt $# ] && echo "c continue without questions"
        echo "q quit"
        echo -n "? [$ntrctv_ANSW]"
        read ntrctv_A
        [ "$ntrctv_A" -a "$ntrctv_A" != "c" -a "$ntrctv_A" != "i" -a "$ntrctv_A" != "d" ] && ntrctv_ANSW="$ntrctv_A"
      fi
      if [ "$ntrctv_ANSW" = "q" ]; then
        return
      elif [ "$ntrctv_A" = "d" ]; then
        dbgtoggle -v
      elif [ "$ntrctv_A" = "i" ]; then
        [ "$ntrctv_t" ] && ntrctv_t="" || ntrctv_t="-i "
      elif [ "$(echo "$ntrctv_ANSW" | sed 's/[0-9]//g')" ]; then
        ntrctv_ANSW="?"
      elif [ "$ntrctv_ANSW" -ge 1 -a "$ntrctv_ANSW" -le $# ]; then
        [ "$ntrctv_A" = "c" ] && ntrctv_y="-y"
        # build the command and set -i, if needed
        ntrctv_cmd="$(echo "$(eval echo "\${${ntrctv_ANSW}}")" | sed -e "s/<-i>/$ntrctv_t/g")"
        # eval function call again
        [ "$ntrctv_e" ] && ntrctv_cmd="$(eval echo "$ntrctv_cmd")"

        # call function
        if [ "$ntrctv_run" ]; then
          local IGNORE="" STDOUT=""
	  [ "$(set | grep "^IGNORE${ntrctv_ANSW}=")" ] && eval IGNORE=\"\$IGNORE${ntrctv_ANSW}\"
	  [ "$(set | grep "^STDOUT${ntrctv_ANSW}=")" ] && eval STDOUT=\"\$STDOUT${ntrctv_ANSW}\"
          # run is on but run this menu point without run!
          if [ "$IGNORE" == "$STDOUT" -a "$STDOUT" == "$norunstring" ]; then
            eval "$ntrctv_cmd"
          else
            [ "$ntrctv_y" ] && local RUN_INTERACT="yes" || local RUN_INTERACT="no"
            run "$ntrctv_cmd"
          fi
        else
          eval "$ntrctv_cmd"
        fi
        ntrctv_ANSW=$(($ntrctv_ANSW+1))
        [ $ntrctv_ANSW -gt $# ] && ntrctv_ANSW=q
      fi
    done
  fi
}
## examples ... comment out and try it
#
#a() { A="called: a"; [ $# -ge 1 ] && A="$A <$1>"; [ $# -ge 2 ] && A="$A <$2>"; [ $# -ge 3 ] && A="$A <$3>"; [ $# -ge 4 ] && A="$A <$4>"; echo "$A" ;}
#interactive a "a -x # with arg and comment" "a -x \"one two\" # 2 args, one with spaces"
#exit 0
#
#interactive -y a "a -x # with arg and comment" "a -x \"one two\" # 2 args, one with spaces"
#exit 0
#
#interactive "a A" "a B" "a C" "a D" "a E" "a F" "a G" "a H" "a I" "a J" "a K"
#exit 0
#
#f="a \"a -x # with arg and comment\" \"a -x \\\"one two\\\" # 2 args, one with spaces\""
#eval "interactive $f"
#exit 0
#
#interactive # should return without doing anything
#exit 0
#
#interactive "a <-i> A" "a B" "a <-i> C"
#exit 0
#
#f1() { IANAME="$IANAME/f1" interactive -e -h "=== \$IANAME \(opts=-e\) n=\<\$n\> ===" "let n=n+1" f1; }
#n=""; IANAME="main" interactive -e -h "=== \$IANAME \(opts=-e\) n=\<\$n\> ===" "let n=n+1" f1
#exit 0
#
#interactive -r "sh -c \"exit 0\"" "echo \"error auf stdout\"" "echo \"error auf stderr\" >&2" "sh -c \"exit 1\""
#exit 0
#
#interactive -y -r "sh -c \"exit 0\"" "echo \"error auf stdout\"" "echo \"error auf stderr\" >&2" "sh -c \"exit 1\""
#exit 0
#


# function: ask_user -- ask user
# get: question = string
#      default  = y|n
# ret: 1 == yes | 0 == no
ask_user()
{
  local quest=$1
  [ "$2" == "y" -o "$2" == "n" ] && local default=$2 || local default=""

  while [ 1 ]; do
    echo -ne "$quest"
    read a
    [ "$a" == "" ] && a=$default
    if [ "$a" == "Y" -o "$a" == "y" -o "$a" == "J" -o "$a" == "j" ]; then
      # echo "yes"
      return 1;
    fi
    if [ "$a" == "N" -o "$a" == "n" ]; then
      # echo "no"
      return 0;
    fi
    echo >&2 "Error: \"$a\" is not a allowed answer!"
  done
}


# function: do_dir -- create directory
# get: $1 -- directory
#      $2 -- permissions (optional)
do_dir()
{
  local dir=$1
  local perm=""; [ $# -ge 2 ] && perm=$2

  if [ -d "$dir" ]; then
    ask_user "Directory \"$dir\" exist's, remove it [Y|n] " "y"
    [ $? == 0 ] && err "Can't work without directory \"$dir\"."
    rm -rf $dir
  fi
  mkdir -p $dir
  [ "$perm" ] && chmod $perm $dir
  echo ""
}


# function: check_tools -- check if tools exist
#           use ./ to search in local directory
# get: $1..$n -- tools
check_tools()
{
  while [ $# -gt 0 ]; do 
    echo -n >&2 " check for \"$1\" ..."
    if [ "$(echo $1 |cut -c1-2)" == "./" ]; then
      [ ! -f "$1" ] && err "Can't find file \"$1\"!"
    else
      [ ! "$(which $1)" ] && err "Can't find tool \"$1\" in PATH!"
    fi
    echo >&2 "ok"
    shift
  done
}

### BOOTCD_LIB Placeholder ##############

# function: usage
# get: $1 -- error string
usage()
{
  [ "$1" ] && echo -e >&2 "\n$1\n"

  echo -e "\n              $SCRIPTNAME Version $VERSION"
  echo -e "\n $SCRIPTNAME is the wizard for bootcddebootstrap and bootcdbackup. "
  echo -e " bootcddebootstrap build a debian bootcd from scratch and bootcdbackup guess " 
  echo -e " the parameters of the running system and the system itself to the" 
  echo -e " created image. After that, you can backup your running system to a CD/DVD.\n"
  echo -e " syntax: $SCRIPTNAME [-i] [-m <mirror>] [-e <directories>] -d <directory>"
  echo -e ""
  echo -e "                 -i   -- do all things interactive"
  echo -e "          -m <mirror> -- debian mirror for debootstrap (e.g. http://ftp.de.debian.org/debian)"
  echo -e "     -e <directories> -- space seperated directories exclude from backup"
  echo -e "       -c <directory> -- look for bootcd2disk.conf and bootcdwrite.conf in"
  echo -e "                         this directory and use it if found."
  echo -e "       -d <directory> -- create image in this directory (Space needed!)"
  echo -e ""
  echo -e " example: $SCRIPTNAME -e \"/home/data /home/newbie/tmp\" -d /var/spool/bootcd"
  echo -e ""
 
  exit 0
}

# function: realpath -- gives the full path of file or dir
realpath()
{
  [ $# -eq 1 ] && echo "$(cd $(dirname $1); /bin/pwd)/$(basename $1)"
}


# function: in_stdout_wget -- in_stdout for wget
# get: $1 -- number
# ret: stdout string in STDOUT<number>
in_stdout_wget()
{
  local mm=$1

  in_stdout $mm "^--[[:digit:]:]*--.*"
  in_stdout $mm "^[[:space:]]*=> .*"
  in_stdout $mm "^Resolving .*"
  in_stdout $mm "^Connecting .*"
  in_stdout $mm "^Logging .*"
  in_stdout $mm "^==> SYST .*"
  in_stdout $mm "^==> TYPE .*"
  in_stdout $mm "^==> PASV .*"
  in_stdout $mm "^Length: .*"
  in_stdout $mm "^[[:space:]]*OK.*"
  in_stdout $mm "^Length .*"
  in_stdout $mm "^$"
  in_stdout $mm "^[[:digit:]:]* .*"  
  in_stdout $mm "^HTTP request sent, .* 200 OK[[:space:]]*$"
}


### functions below split tasks in main functions 
### - to see what is going on
### - this functions use global variables


# function: create_debian_chroot 
# get: $1  -- interactive (optional)
# globals: debootstrapmirror
create_debian_chroot()
{
  local interact=""; [ $# -ge 1 ] && interact="$1"
  local TODO="" tt="" mm=0
  local IAHEAD="$IAHEAD / create_debian_chroot"

  let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout $mm "^[[:space:]]*check for \"[^[:space:]]*\" ...ok$"
  TODO="$TODO \"check_tools $TOOLS # Check if tools are installed\""

  let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\" 
  in_stdout $mm "Directory.*"
  TODO="$TODO \"do_dir $workdir # Create work directories \""

  let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\" 
  in_stdout_wget $mm
  TODO="$TODO \"wget -P $workdir \\\"$BOOTSTRAPPATH\\\" # Get files to create mini debian system \""

  # let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\" 
  # in_stdout_wget $mm
  # TODO="$TODO \"wget -O $workdir/../bootcddebootstrap \\\"$bootcddebootstrappath\\\" # Get script to create mini debian system\""

  # link debian files to work directory
  for i in $(ls *deb); do
     let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
     TODO="$TODO \"ln -s $(pwd)/$i $workdir/\""
  done

  let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_norun $mm # run the next without run 
  [ "$debootstrapmirror" ] && tt="-m $debootstrapmirror"

  TODO="$TODO \"./bootcddebootstrap <-i> -h \\\"$IAHEAD\\\" $tt -d $workdir/$IMGDIR $workdir\""

  [ "$interact" ] && interact="" || interact="-y"
  eval "interactive -h \"=== \$IAHEAD ===\" $interact -r $TODO"
}


# function: do_backup
# get: $1   -- interactive (optional)
do_backup()
{
  local interact=""; [ $# -ge 1 ] && interact="$1"
  local TODO=""
  local mm=0

  local BOOTSTRAP_VERSION="$(chroot $workdir/$IMGDIR sh -c "dpkg-query -W -f='\${Version}\n' bootcd")"
  local WIZARD_VERSION="$(echo "$VERSION" | sed "s/_/./")"

  local IAHEAD="$IAHEAD / do_backup"

  let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout $mm "Directory.*"
  TODO="$TODO \"do_dir $workdir/$IMGDIR/$BACKUPDIR # Create work directories\""

  if [ "$bootcd2diskconf" ]; then 
    let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    TODO="$TODO \"cp $bootcd2diskconf $workdir/$IMGDIR/$BACKUPDIR/ # copy config given on commandline\""
  else
    if [ "$newestother" ]; then
      let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
      in_stdout_wget $mm
      TODO="$TODO \"wget -O $workdir/$IMGDIR/$BOOTCDMK2DISKCONF \\\"$NEWESTBOOTCDMK2DISKCONFPATH\\\" # Get newest bootcdmk2diskconf\""
    elif [ "$(chroot $workdir/$IMGDIR sh -c "dpkg --compare-versions \"$WIZARD_VERSION\" -gt \"$BOOTSTRAP_VERSION\"; echo \$?")" = "0" ]; then
      let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
      in_stdout_wget $mm
      TODO="$TODO \"wget -O $workdir/$IMGDIR/$BOOTCDMK2DISKCONF \\\"$BOOTCDMK2DISKCONFPATH\\\" # upgrade bootcdmk2diskconf from $BOOTSTRAP_VERSION to $WIZARD_VERSION\""
    fi

    # check chroot and file exist
    let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    TODO="$TODO \"chroot $workdir/$IMGDIR sh -c \\\"chmod 755 $BOOTCDMK2DISKCONF\\\"\""
    let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_stdout $mm "^  No volume groups found$"
    TODO="$TODO \"sh -c \\\"$workdir/$IMGDIR/$BOOTCDMK2DISKCONF -b >$workdir/$IMGDIR/$BACKUPDIR/bootcd2disk.conf\\\" # create config from actual system\""
  fi

  let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout $mm "^backup to .*"
  in_ignore $mm "^find: WARNING: Hard link count is wrong for ./proc: .*"
  in_stdout $mm "Permission denied$"
  in_stdout $mm "^find: \./proc/.*"
  TODO="$TODO \"run_cpio # copy files to backup\""

  [ "$interact" ] && interact="" || interact="-y"
  eval "interactive -h \"=== \$IAHEAD ===\" $interact -r $TODO"
}


# function: do_cpio -- backup the partition and split into files < 2GB 
#                      (mkisofs restriction!)
run_cpio()
{
  local ext=""

  for i in $workdir $extdirs $EXCLUDE_DIRS ; do
     [ "$ext" ] && ext="$ext; s|\x00.${i}[^\x00]*||g" || ext="s|\x00${i}[^\x00]*||g"
  done

  echo >&2 "backup to /$workdir/$IMGDIR/$BACKUPDIR/backup"

  # exclude all files and directories 
  (cd /; find . -print0 | 
    sed -e "s|^|\x00|;$ext;s|^\x00||" | 
    cpio --null --quiet -o | 
    gzip -c |
    split -b2000m -d - /$workdir/$IMGDIR/$BACKUPDIR/backup)
}

# function: add_info -- add informations to first page
# get: $1    -- bootcd2disk.conf - file
add_info()
{
  local ctrlL=""
  local white="0f"
  local grey="07"
  local green="0a"
  local yellow="0e"
  local bootcdconf=$1
  local file=/etc/bootcd/bootcdbackupwizard.txt

  echo -n "${green}" >>$workdir/$IMGDIR/$file
  cat >>$workdir/$IMGDIR/$file <<-'END'
  _                 _          _
 | |__   ___   ___ | |_ ___ __| |
 | '_ \ / _ \ / _ \| __/ __/ _` |_____
 | |_) | (_) | (_) | || (_| (_| |_____|
 |_.__/ \___/ \___/ \__\___\__,_|
     _                _                        _                  _
    | |__   __ _  ___| | ___   _ _ ____      _(_)______ _ _ __ __| |
    | '_ \ / _` |/ __| |/ / | | | '_ \ \ /\ / / |_  / _` | '__/ _` |
    | |_) | (_| | (__|   <| |_| | |_) \ V  V /| |/ / (_| | | | (_| |
    |_.__/ \__,_|\___|_|\_\\__,_| .__/ \_/\_/ |_/___\__,_|_|  \__,_|
                                |_|
END

cat >>$workdir/$IMGDIR/$file <<END

 ${grey}Backup of host ${white}$(hostname) ${grey}from ${white}$(date "+%d. %B %Y, %X")
 ${white}restore with: ${yellow}bootcd2disk -c /backup

 ${grey}To select a cdrom specify "hda", "hdb", "hdc", "hdd", "scd0" or "scd1".
END

  sed -ie "s|^\(DISPLAY=\"\)[^\"]*\(\"\)|\1${file}\2|" $bootcdconf
}


# function: run_bootcdwrite
# get: $1     -- interactive (optional)
run_bootcdwrite()
{
  local interact=""; [ $# -ge 1 ] && interact="$1"
  local TODO=""
  local mm=0
  local i

  local IAHEAD="$IAHEAD / run_bootcdwrite"

  let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout $mm ".*"
  TODO="$TODO \"chroot $workdir/$IMGDIR bootcdwrite -s # create cd iso image\""

  for i in var/spool/bootcd/cdimage.iso var/log/bootcdwrite.log var/log/bootstrap.log; do
    let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    TODO="$TODO \"mv $workdir/$IMGDIR/$i $workdir\""
  done

  let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout $mm ".*"
  TODO="$TODO \"rm -rf $workdir/$IMGDIR # cleanup\""
  let mm=$mm+1; eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout $mm ".*"
  TODO="$TODO \"rm -f $workdir/bootcddebootstrap $workdir/$(basename $BOOTSTRAPPATH) # cleanup\""

  [ "$interact" ] && interact="" || interact="-y"
  eval "interactive -h \"=== \$IAHEAD ===\" $interact -r $TODO"
}

#################### the real things #########################################


TODO=""
workdir=""
interact=""
extdirs=""
ownconfdir=""
bootcd2diskconf=""
bootcdwriteconf=""
newestthis=""
newestother=""
debootstrapmirror=""
while [ $# -gt 0 ]; do
  case "$1" in
    "-d")
      shift
      workdir="$1"
      ;;
   "-i")
     interact="$1"
     ;;
   "-e")
     shift
     extdirs="$1"
     ;;
   "-c")
     shift
     ownconfdir="$1"
     ;;
   "-m")
     shift
     debootstrapmirror="$1"
     ;;
   "-N")
     # undocumented Option only for developement
     newestthis="$1"
     ;;
   "-n")
     # undocumented Option only for developement
     newestother="$1"
     ;;
    --help|-h) 
      usage
      ;;
    *) 
      usage "parameter \"$1\" unknown!"
      ;;
  esac
  shift
done

if [ "$newestthis" ]; then
  echo "Trying to load newer Backup"
  [ -f $0.old ] || cp $0 $0.old
  wget -O $0 "$NEWESTBOOTCDBACKUPWIZARDPATH"
  exit 0
fi

[ ! "$workdir" ] && usage "Mandatory parameter \"workdir\" not found!"

workdir=$(realpath $workdir)

bootcddebootstrappath="$BOOTCDDEBOOTSTRAPPATH"
if [ "$newestother" ]; then
  bootcddebootstrappath="$NEWESTBOOTCDDEBOOTSTRAPPATH"
fi


ext=""
for i in $workdir $extdirs $EXCLUDE_DIRS; do
   [ "$ext" ] && ext="$ext --exclude $i" || ext="--exclude $i"
done

if [ "$ownconfdir" ]; then
  ownconfdir=$(realpath $ownconfdir)
  [ -e "$ownconfdir/bootcd2disk.conf" ] && bootcd2diskconf="$ownconfdir/bootcd2disk.conf"
  [ -e "$ownconfdir/bootcdwrite.conf" ] && bootcdwriteconf="$ownconfdir/bootcdwrite.conf"
fi

TODO="$TODO \"create_debian_chroot <-i> # using $(basename $BOOTSTRAPPATH)\""
TODO="$TODO \"do_backup <-i> # including filesystem layout\""
if [ "$bootcdwriteconf" ]; then 
  TODO="$TODO \"cp $bootcdwriteconf $workdir/$IMGDIR/etc/bootcd/ # use bootcd config from commandline\""
else
  TODO="$TODO \"add_info $workdir/$IMGDIR/etc/bootcd/bootcdwrite.conf\""
  TODO="$TODO \"# change $workdir/$IMGDIR/etc/bootcd/bootcdwrite.conf if needed\""
fi
TODO="$TODO \"run_bootcdwrite <-i> # to create the iso image\""
TODO="$TODO \"mv $ERRLOG $workdir\""

[ "$interact" ] && interact="" || interact="-y"
eval "interactive -h \"=== \$IAHEAD\" $interact $TODO"

echo "The result cdimage.iso plus logfiles are in $workdir"
