#!/bin/sh
#
# 	$Source: /home/bs/src/bootcd/backupcvs/bootcd/bootcd/bootcdbackupwizard.src,v $
#       $Id: bootcdbackupwizard.src,v 1.79 2011-04-11 07:21:48 bs Exp $
#    author: Carsten Dinkelmann <din@foobar-cpa.de>
#            Bernd Schumacher <bernd.schumacher@hpe.com>
#     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-backup/examples/bootcdbackupwizard
VERSION=5_04 # 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_1.0.43_all.deb"

# 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/bootcd/bootcdmk2diskconf"

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

# If SCRIPT is not empty, Warnings would be ignored
SCRIPT=""

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

SCRIPTNAME=$(basename $0)

IAHEAD="$SCRIPTNAME $VERSION"

MNTBOOTCDDISC="mnt/bootcd.disc"

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

#     version: $Version: $
#   copyright: Bernd Schumacher <bernd.schumacher@hpe.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
#

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

msg()
{
  echo "--- MESSAGE ---" | tee -a $ERRLOG >&2
  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 dbg prints DEBUG Messages to stderr.
# It is very fast because no if-check has to be done
# functions dbgoff, dbgon, and dbgtoggle switches dbg on or off
dbgoff()
{
   eval "dbg() { :; }"
}

if [ "$(type dbg 2>&1| grep "not found")" ]; then
  dbgoff
fi

dbgon()
{
   eval "dbg() { echo \"DEBUG \$@\" >&2; }"
}

# with option -v dbtoggle is more verbose
dbgtoggle()
{
  if [ "$(dbg 2>&1)" ]; then
    dbgoff
    [ "$1" = "-v" ] && echo "debug off" >&2
  else
    dbgon
    [ "$1" = "-v" ] && echo "debug on" >&2
  fi
}

# 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 
        # Next line handles EOF from a pipe as "(e)xit"
        [ $? -ne 0 ] && A="e"
        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
        echo "switching to interactive mode to point ($ntrctv_ANSW) which failed"
        echo
	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
#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"
#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 
        # Next line handles EOF from a pipe as "(e)xit"
        [ $? -ne 0 ] && A="e"
        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"
          ntrctv_i=$(($ntrctv_i+1))
        done
        
        # dbg exists, print flag (do not use declare to be posix konform)
        [ "$(type dbg 2>&1| grep "not found")" ] || 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
        # Next line handles EOF from a pipe as "q"
        [ $? -ne 0 ] && ntrctv_A="q"
        [ "$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\> ===" "n=$((n+1))" f1; }
#n=""; IANAME="main" interactive -e -h "=== \$IANAME \(opts=-e\) n=\<\$n\> ===" "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  "$quest"
    read a
    # Next line handles EOF from a pipe as "default"
    [ $? -ne 0 ] && 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
}

# function: prepare_chroot <workdir> [-i]
# globals: IAHEAD
prepare_chroot()
{
  local workdir="$1"
  local interact=""; [ $# -ge 2 ] && interact="$2"
  local TODO="" mm=0
  if [ "$(env | grep "^IAHEAD=")" ]; then 
    local IAHEAD="$IAHEAD / prepare_chroot"
  else
    local IAHEAD="prepare_chroot"
  fi

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"mount --bind /dev $workdir/dev\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"mount -t proc none $workdir/proc\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"mount -t sysfs none $workdir/sys\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"mount -t devpts none $workdir/dev/pts\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"chroot $workdir sh -c \\\"[ -f /etc/mtab ] && mv /etc/mtab /etc/mtab.bootcd || rm -f /etc/mtab.bootcd\\\"\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"chroot $workdir sh -c \\\"grep -v -e \\\\\\\"^sunrpc\>\\\\\\\" -e \\\\\\\"^rootfs\>\\\\\\\" /proc/mounts >/etc/mtab\\\"\""

  # we need selinux mounted to install openssh_server without
  # error "chage: Permission denied."
  if [ "$(cat /proc/mounts | grep selinux)" ]; then
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    TODO="$TODO \"mount -t selinuxfs none $workdir/selinux\""
  fi

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

# unprepare_chroot <workdir> [-i]
# globals IAHEAD
unprepare_chroot()
{
  local workdir="$1"
  local interact=""; [ $# -ge 2 ] && interact="$2"
  local TODO="" mm=0
  if [ "$(env | grep "^IAHEAD=")" ]; then 
    local IAHEAD="$IAHEAD / unprepare_chroot"
  else
    local IAHEAD="unprepare_chroot"
  fi

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"chroot $workdir sh -c \\\"[ -f /etc/mtab.bootcd ] && mv /etc/mtab.bootcd /etc/mtab || rm -f /etc/mtab\\\"\""

  if [ "$(cat /proc/mounts | grep selinux)" ]; then
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    TODO="$TODO \"umount $workdir/selinux\""
  fi

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"umount $workdir/dev/pts\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"umount $workdir/sys\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"umount $workdir/proc\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"umount $workdir/dev\""

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

### BOOTCDBACKUP_LIB Placeholder ##############
# bootcdbackup.lib
# vim: set filetype=sh :

run_cpiostar_kern()
{
  local use_type=$1 # star|cpio
  echo >&2 "backup to $backupdir/data with $use_type"

  mkdir $backupdir/data

  # exclude all files and directories 
  (cd $dir; find . -print0 | 
    sed -e "s|^|\x00|;$ext;s|^\x00||" | 
    (
    if [ "$use_type" = "star" ]; then
      sed "s|\x00|\n|g" | star -xattr -H=exustar -c list=- -f -
    else
      cpio --null --quiet --format=crc -o
    fi
    ) |
    gzip -c |
    split -b2000m -d - $backupdir/data/$use_type)
}

# bootcdbackup.lib
# vim: set filetype=sh :

# function: run_cpiostar -- backup the partition and split into files < 2GB 
#                      (mkisofs restriction!)
# get: $n -- -d <dir> the "root" directory (optional)
#      $n -- -u <cpio|star> 
#      $last -- backupdir (write backup to this directory)
# need: RCPIO_EXCLUDE_DIRS -- exclude this files / directories
run_cpiostar()
{
  local ext=""
  local use_type="auto" # cpio or star
  local dir=/
  local selinux=""
  local star=""
  local interact=""

  while [ $# -gt 1 ]; do
    if [ "$1" = "-d" ]; then
      dir=$2
      shift 2
    elif [ "$1" = "-u" ]; then
      use_type=$2
      shift 2
    elif [ "$1" = "-i" ]; then
      interact="$1"
      shift
    else
      err "run_cpiostar: unknown argument: <$1>"
    fi
  done

  local backupdir="$1"
  shift
  [ "$backupdir" ] || err "run_cpiostar argument missing"

  if [ -f $dir/etc/selinux/config ]; then
    if [ "$(cat "$dir/etc/selinux/config" | \
      grep -e "^SELINUX=enforcing\>" \
      -e "^SELINUX=permissive\>")" ]
    then
      selinux="1"
    fi
  fi

  if [ -f /bin/star ]; then
    star="/bin/star"
  elif [ -f /usr/bin/star ]; then
    star="/usr/bin/star"
  fi

  local TODO=""  mm=0
  local IAHEAD="$IAHEAD / run_cpiostar"

  if [ "$use_type" = "star" ]; then
    if [ ! "$star" ]; then
      err "star is forced, but not installed"
    fi
  elif [ "$use_type" = "cpio" ]; then
    if [ "$selinux" ]; then
      warn "selinux is active; cpio is forced; using cpio; loosing infos"
    fi
  elif [ "$use_type" = "auto" ]; then
    use_type="cpio"
    if [ "$selinux" ]; then
      if [ "$star" ]; then
        use_type="star"
      else
        warn "selinux is active; star not installed; using cpio; loosing infos"
      fi
    fi
  else
    err "use_type=<$use_type> not known"
  fi

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

  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/.*"
  in_stdout $mm "^cpio.*truncating inode number"

  in_stdout $mm "^star: .* unsupported file type 'socket'. Not dumped."
  in_stdout $mm "^star: .* blocks + .* bytes (total of .* bytes =.*)"
  in_stdout $mm "^star: Missing links .*, Name too long .*, File too big .*, Not dumped .*"
  in_stdout $mm "^star: Processed all possible files, despite earlier errors."
  in_stdout $mm "^star: The following problems occurred during archive processing:"
  in_stdout $mm "^star: Cannot: stat 0, open 0, read/write 0. Size changed 0."
  TODO="$TODO \"run_cpiostar_kern $use_type\""

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

#. /usr/share/bootcd/bootcd-run.lib
#RCPIO_EXCLUDE_DIRS=""
#IAHEAD="main"
#f=/tmp/run_cpiostar; rm -rf $f; mkdir -p $f/t; touch $f/t/"a a"
#run_cpiostar -i -d $f/t $f
#[ -f $f/cpio00 ] && (cat $f/cpio* |gzip -d -c | cpio -it )
#[ -f $f/star00 ] && (cat $f/star* |gzip -d -c | star -xattr -t -f - )
#run_cpiostar -d $f/t -u cpio $f
#[ -f $f/cpio00 ] && (cat $f/cpio* |gzip -d -c | cpio -it )
#[ -f $f/star00 ] && (cat $f/star* |gzip -d -c | star -xattr -t -f - )
#run_cpiostar -d $f/t -u star $f
#[ -f $f/cpio00 ] && (cat $f/cpio* |gzip -d -c | cpio -it )
#[ -f $f/star00 ] && (cat $f/star* |gzip -d -c | star -xattr -t -f - )
### BOOTCDBACKUP_LIB Placeholder ##############

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

  echo  "              $SCRIPTNAME Version $VERSION"
  echo  ""
  echo  " $SCRIPTNAME is the wizard for bootcddebootstrap and bootcdbackup. "
  echo  " bootcddebootstrap build a debian bootcd from scratch and bootcdbackup guess " 
  echo  " the parameters of the running system and the system itself to the" 
  echo  " created image. After that, you can backup your running system to a CD/DVD."
  echo ""
  echo  " syntax: $SCRIPTNAME [-i] [-m <mirror>] [-e <directories>] -d <directory>"
  echo  ""
  echo  "                 -i   -- do all things interactive"
  echo  "          -m <mirror> -- debian mirror for debootstrap (e.g. http://ftp.de.debian.org/debian)"
  echo  "     -e <directories> -- space seperated directories exclude from backup"
  echo  "       -c <directory> -- look for bootcd2disk.conf and bootcdwrite.conf in"
  echo  "                         this directory and use it if found."
  echo  "       -d <directory> -- create image in this directory (Space needed!)"
  echo  "                   -b -- backup only (the debian chroot already has to exist)"
  echo  ""
  echo  "                   -B -- do not delete files needed to backup again (using -b)"                 
  echo  ""
  echo  "               --star -- use star for backup"
  echo  ""
  echo  "               --cpio -- use cpio for star"
  echo  ""
  echo  "         --externdata -- do not put backupfiles on bootcd"
  echo  ""
  echo  " example: $SCRIPTNAME -e \"/home/data /home/newbie/tmp\" -d /var/spool/bootcd"
  echo  ""
 
  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 "^[[: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:]]*$"
  in_stdout $mm "^==> SIZE "
}


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


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

  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\""

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

  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 \""

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

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

trapfunc()
{
  # Ignore Traps
  trap "" 0 2

  # We must tell the user to unmount if necessarry
  echo >&2 "bootcdbackupwizard trapfunc: checking if filesystems are unmounted in $workdir/$IMGDIR/$MNTBOOTCDDISC"
  if [ -e $workdir/$IMGDIR/$MNTBOOTCDDISC ]; then
    if [ "$(/bin/ls $workdir/$IMGDIR/$MNTBOOTCDDISC/)" ]; then
      BOOTCDMP="$workdir/$IMGDIR/$MNTBOOTCDDISC"
      $workdir/$IMGDIR/usr/share/bootcd/bootcd2disk <-i> -onlyunmount -c /$BACKUPDIR -s -o -r $workdir/$IMGDIR
      if [ "$(/bin/ls $workdir/$IMGDIR/$MNTBOOTCDDISC/)" ]; then
        err "Could not unmount $workdir/$IMGDIR/$MNTBOOTCDDISC.
Be sure not to accidently remove $workdir/$IMGDIR before manually unmounting."
      fi
      rmdir -f $workdir/$IMGDIR/$MNTBOOTCDDISC
    fi
  fi

  exit 1
}

# function: do_backup
# Input:
#   -i     -- interactive (optional)
#   --cpio
#   --star
#   --externdata
do_backup()
{
  local interact=""
  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"

  local externdata=""
  local use_type=""
  while [ $# -gt 0 ]; do
    case "$1" in
     "--star")
       use_type="$1"
       ;;
     "--cpio")
       use_type="$1"
       ;;
     "--externdata")
       externdata="$1"
       ;;
     "-i")
       interact="$1"
       ;;
      *) 
        echo "do_backup internal error unknown option $1" >&2
        exit 1
        ;;
    esac
    shift
  done

  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 
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    TODO="$TODO \"cp $bootcd2diskconf $workdir/$IMGDIR/$BACKUPDIR/ # copy config given on commandline\""
  else
    # check chroot and file exist
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    TODO="$TODO \"chroot $workdir/$IMGDIR sh -c \\\"chmod 755 $BOOTCDMK2DISKCONF\\\"\""
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_stdout $mm "^  No volume groups found$"
    TODO="$TODO \"sh -c \\\"ERRLOG=$ERRLOG $workdir/$IMGDIR/$BOOTCDMK2DISKCONF -b >$workdir/$IMGDIR/$BACKUPDIR/bootcd2disk.conf\\\" # create config from actual system\""
  fi


  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_norun $mm
  BOOTCDMP="$workdir/$IMGDIR/$MNTBOOTCDDISC"
  TODO="$TODO \"$workdir/$IMGDIR/usr/share/bootcd/bootcd2disk <-i> -onlymount -c /$BACKUPDIR -s -o -r $workdir/$IMGDIR\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_norun $mm
  TODO="$TODO \"chroot $workdir/$IMGDIR /usr/share/bootcd/bootcdbackup <-i> -e \\\"$workdir $extdirs $EXCLUDE_DIRS\\\" -cpiostar $use_type \\\"$IAHEAD\\\" /$MNTBOOTCDDISC /$BACKUPDIR\""

  if [ "$externdata" ]; then
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_norun $mm
    TODO="$TODO \"mkdir $workdir/backup\""

    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_norun $mm
    TODO="$TODO \"mv $workdir/$IMGDIR/backup/data  $workdir/backup\""

    # make empty mountpoint /backup/data for sshfs or nfs mounts
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_norun $mm
    TODO="$TODO \"mkdir $workdir/$IMGDIR/backup/data\""

    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_norun $mm
    TODO="$TODO \"cp $workdir/$IMGDIR/backup/bootcd2disk.conf $workdir/backup\""
  else
    # Make sure backup data, which is already compressed, will not be compressed again by mkzftree
    TODO="$TODO \"sed -i \\\"/added by bootcdbackupwizard/d\\\" $workdir/$IMGDIR/etc/bootcd/bootcdwrite.conf\""
    TODO="$TODO \"echo \\\"NOTCOMPRESSED=\\\\\\\"\\\\\\\$NOTCOMPRESSED /backup/data\\\\\\\" # added by bootcdbackupwizard\\\" >> $workdir/$IMGDIR/etc/bootcd/bootcdwrite.conf\""
  fi

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_norun $mm
  BOOTCDMP="$workdir/$IMGDIR/$MNTBOOTCDDISC"
  TODO="$TODO \"$workdir/$IMGDIR/usr/share/bootcd/bootcd2disk -onlyunmount -c /$BACKUPDIR -s -o -r $workdir/$IMGDIR\""

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

  # check if filesystems are really umounted
  if [ "$(/bin/ls $workdir/$IMGDIR/$MNTBOOTCDDISC/)" ]; then
    echo >&2 ""
    echo  >&2 "ATTENTION! Found files in \"$workdir/$IMGDIR/$MNTBOOTCDDISC\"!"
    echo >&2 "Seems that umount doesn't work, please try it manualy!"
    ask_user "Continue [y|N] " "n"
    [ $? -eq 0 ] && err "User break after umount of \"$workdir/$IMGDIR/$MNTBOOTCDDISC\" failed!"
  fi
}


# 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", "sr0" or "sr1".
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"

  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/bootcdbackup.log; do
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    TODO="$TODO \"mv $workdir/$IMGDIR/$i $workdir\""
  done

  if [ ! "$backuponly" ]; then
    for i in var/log/bootstrap.log; do
      mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
      TODO="$TODO \"mv $workdir/$IMGDIR/$i $workdir\""
    done
  fi

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

# function: check_chroot_timezone
# get: $1    -- chroot directory
check_chroot_timezone()
{
  local chrootdir=$1
  if [ "$(date "+%z")" != "$(chroot $chrootdir date "+%z")" ]; then
    echo "timezone in chroot is different !!! (possible fix: cp /etc/localtime)"
    return 1
  fi
  return 0
}

# function: fixchrootfuture
# get: $1  -- chroot directory
fixchrootfuture()
{
  local chrootdir=$1
  chroot $chrootdir sh -c "touch /tmp/timefile ;find / -xdev -newer /tmp/timefile -print0 | xargs -0r touch; rm /tmp/timefile"
}

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


TODO=""
workdir=""
interact=""
extdirs=""
ownconfdir=""
bootcd2diskconf=""
bootcdwriteconf=""
debootstrapmirror=""
backuponly=""
backuponlynexttime=""
script=""
use_type=""
externdata=""
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"
     ;;
   "-b")
     backuponly="$1"
     ;;
   "-B")
     backuponlynexttime="$1"
     ;;
   "--star")
     use_type="$1"
     ;;
   "--cpio")
     use_type="$1"
     ;;
   "--externdata")
     externdata="$1"
     ;;
   "-s")
     script="$1"
     ;;
    --help|-h) 
      usage
      ;;
    *) 
      usage "parameter \"$1\" unknown!"
      ;;
  esac
  shift
done

[ ! "$workdir" ] && usage "Mandatory parameter \"workdir\" not found!"
[ -d "$workdir" ] || usage "workdir <$workdir> does not exist!"

workdir=$(realpath $workdir)

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

trap trapfunc 0 2

if [ ! "$backuponly" ]; then
  TODO="$TODO \"prepare_debian_chroot <-i> # using $(basename $BOOTSTRAPPATH)\""
  tt=""
  [ "$debootstrapmirror" ] && tt="-m $debootstrapmirror"

  # We try to use the same grub (by installing grub or grub-legacy debian 
  # package) on debian bootcd, as used by system we want to backup.
  grubopt=""
  [ -x /sbin/grub ] && [ "$(/sbin/grub --version | grep "\<0.97\>")" ] && grubopt="--grub-legacy"

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

TODO="$TODO \"prepare_chroot $workdir/$IMGDIR <-i>\""
TODO="$TODO \"check_chroot_timezone $workdir/$IMGDIR\""

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\""
fi

TODO="$TODO \"do_backup <-i> $use_type $externdata # including filesystem layout\""


TODO="$TODO \"fixchrootfuture $workdir/$IMGDIR\""

# Allow login without passwd for bootcd root (for ssh we would also need 
# "PermitEmptyPasswords yes" in /etc/sshd_config)
TODO="$TODO \"sed -i \\\"s/^root:x:/root::/\\\" $workdir/$IMGDIR/etc/passwd\""

TODO="$TODO \"run_bootcdwrite <-i> # to create the iso image\""
TODO="$TODO \"unprepare_chroot $workdir/$IMGDIR <-i>\""

if [ ! "$backuponly" ]; then
  if [ ! "$backuponlynexttime" ]; then
    TODO="$TODO \"rm -rf $workdir/$IMGDIR # cleanup\""
  fi
  TODO="$TODO \"rm -f $workdir/bootcddebootstrap $workdir/$(basename $BOOTSTRAPPATH) # cleanup\""
fi

TODO="$TODO \"mv $ERRLOG $workdir\""
if [ ! "$backuponly" ]; then
  TODO="$TODO \"mv /var/log/bootcddebootstrap.log $workdir\""
fi

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

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