#!/bin/sh
#
# 	$Source: /cvsroot/bootcd/bootcd/bootcddebootstrap.src,v $
#       $Id: bootcddebootstrap.src,v 1.28 2007-12-14 06:58:07 bs Exp $
#    author: Carsten Dinkelmann <din@foobar-cpa.de>
#     start: 12.09.2007
#     topic: create a small debian cd and configured it for bootcd
#

# Version of debian package bootcd with this tools is included
# /usr/share/doc/bootcd-backup/examples/bootcddebootstrap
VERSION=3_10+nmu1 # sources are tagged in cvs with rev=debian_version_${VERSION}

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

# use for temporary files
TMPDIR=/tmp/.debootstrap

# used as basis directory
WORKDIR=/var/spool/bootcd-img

# only for help, the download path
DOWNLOADPATH="http://packages.debian.org/stable/debootstrap/all/download"

# mirrors for debootstrap
BOOTSTRAPMIRRORS="http://ftp2.de.debian.org/debian/ http://ftp.se.debian.org/debian/ http://ftp.debian.org"

# build deboostrap for this distribution
DISTRI="etch"

# links for debootstrap
DEBOOTLINKS="/usr/lib/debootstrap"

# needed tools
TOOLS="ar tar chroot"

# kernel
KERNEL="linux-image-2.6-486"

# bootcd packages
BOOTCDPACKAGES_APT="bootcd/unstable bootcd-mkinitramfs/unstable bootcd-backup/unstable bootcd-i386/unstable"
BOOTCDPACKAGES_LOCAL="bootcd bootcd-mkinitramfs bootcd-backup bootcd-i386"

# needed packages
PACKAGES="lvm2 grub mkisofs fdutils file dosfstools realpath syslinux"

# usefull packages
UPACKAGES="less psmisc net-tools ssh vim netbase xbase-clients star console-common console-data console-tools"

# logfile
ERRLOG="/var/log/bootcddebootstrap.log"

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

SCRIPTNAME=$(basename $0)

IAHEAD="$SCRIPTNAME"

### BOOTCD-RUN_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 "--- (e)xit (r)edo (i)gnore (s)witch interactive --- " | tee -a $ERRLOG
        else 
          echo "--- (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 ""
	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 "--- (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"
          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 "? [$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\> ===" "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
    [ "$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  >&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-RUN_LIB Placeholder ##############

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

  echo ""
  echo  " $SCRIPTNAME creates a debian bootcd image in a chroot-enviroment."
  echo  " After that, you can backup your running system to a CD/DVD."
  echo  ""
  echo  " syntax: $SCRIPTNAME [-i] [-m <mirror>] [-d <directory>] <debootstrap path|directory>"
  echo  ""
  echo  "                 -i   -- do all things interactive"
  echo  "       -d <directory> -- create image in this directory (Space needed!)"
  echo  "                         default: \"$WORKDIR\""
  echo  "          -m <mirror> -- debian mirror for debootstrap (e.g. http://ftp.de.debian.org/debian)"
  echo  "       -nu            -- no usefull packages (don't install network, lvm ... in bootcdimage)"
  echo  "   <debootstrap path> -- path to debootstrap debian package, use bootcd packages from"
  echo  "                         \"apt\" source server"
  echo  "   <debootstrap directory> -- directory with debootstrap and bootcd debian packages"
  echo  ""                         
  echo  " download debootstrap: \"$DOWNLOADPATH\" "
  echo  ""
  echo  " example: $SCRIPTNAME /tmp/debootstrap_0.3.3.2etch1_all.deb"
  echo  ""
 
  exit 0
}


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


# function: extract_debootstrab -- extract debootstrap
# get: $1 -- tmpdir
#      $2 -- path to debfile
extract_debootstrap()
{
  local tmpdir=$1
  local debfile=$2

  echo >&2 "extract debootstrap from debian package"

  (cd $tmpdir; ar -x $debfile data.tar.gz)

  tar -xz -C$tmpdir -f /$tmpdir/data.tar.gz

  # we need one link that debootstrap is working
  for i in $DEBOOTLINKS; do 
     [ ! -e "${i}" ] && ln -s ${tmpdir}${i} ${i}
  done
  
  echo
}


# function: config_apt -- configure apt (pinning) to get unstable/testing 
#                         packages for bootcd
# get: $1 -- workdir
config_apt()
{
  local workdir=$1

  echo >&2 "write sources.list"
  cat >$workdir/etc/apt/sources.list <<END
# stable (etch)
deb http://ftp.debian.org/debian $DISTRI main

# unstable
deb http://ftp.debian.org/debian unstable main
END

  echo >&2 "write apt_preferences"
  cat >$workdir/etc/apt/preferences <<END
Package: *
Pin: release a=$DISTRI
Pin-Priority: 700

Package: *
Pin: release a=stable
Pin-Priority: 700

Package: *
Pin: release a=testing
Pin-Priority: 650

Package: *
Pin: release a=unstable
Pin-Priority: 600
END
}

# function: in_stdout_aptget -- generic in_stdout for apt
# get: $1 -- number
# ret: stdout string in STDOUT<number>
in_stdout_aptget()
{
  local mm=$1
  in_stdout $mm "^Reading package lists..."
  in_stdout $mm "^Building dependency tree..."
  in_stdout $mm "^The following extra packages.*"
  in_stdout $mm "^Suggested packages:"
  in_stdout $mm "^Recommended packages:"
  in_stdout $mm "^The following NEW packages will be installed:"
  in_stdout $mm "^[[:digit:]]* upgraded, .*"
  in_stdout $mm "^Need to get .*"
  in_stdout $mm "^After unpacking .*"
  in_stdout $mm "^Get:"
  in_stdout $mm "^debconf: .*"
  in_stdout $mm "^Fetched .*"
  in_stdout $mm "^Unpacking .*"
  in_stdout $mm "^Selecting .*"
  in_stdout $mm "^(Reading database..."  
  in_stdout $mm "^[[:digit:]]* files and directories .*"  
  in_stdout $mm "^[dD]one.$"  
  in_stdout $mm "^Setting up .*"
  in_stdout $mm "^$"
}


# function: config_kernel -- configure kernel install
# get: $1 -- workdir
config_kernel()
{
  local file=$1//etc/kernel-img.conf

  echo >&2 "write kernel-img.conf"

  for i in do_symlinks do_initrd; do
     [ ! "$(grep "[[:space:]]*${i}[[:space:]]*=" $file 2>/dev/null)" ] && echo "$i = Yes" >>$file
     sed -i -e "s|\([[:space:]]*${i}[[:space:]]*=[[:space:]]*\).*|\1Yes|" $file
  done

}

# get: $1 -- workdir
config_motd()
{
  local file=$1/etc/motd

  echo >&2 "--- configuring motd ---"
  echo "to change keyboard, use \"loadkeys /usr/share/keymaps/.....kmap.gz\"" >>$file
}


# function: get_debootstrapmirror
# globals: mirror (from cmdline), BOOTSTRAPMIRRORS
# set:      debootstrapmirror
get_debootstrapmirror()
{
  local i

  echo >&2 "check mirrors"
  # if mirror set by user, use it else try all with ping
  for i in $mirror $BOOTSTRAPMIRRORS; do
     echo  >&2 " check mirror $i ... "
     if [ $(wget --timeout=1 -t 1 -O /dev/null $i >/dev/null 2>&1; echo $?) -eq 0 ]; then
       echo >&2 "ok"
       debootstrapmirror=$i;
       break;
     else
       echo >&2 "doesn't work!"
     fi
  done

  [ ! "$debootstrapmirror" ] && err "No working mirror found!"
}


# function: debconf_set_console_selections
# description: prevent questions while installing: console-tools console-data console-common
# get: $1 -- workdir
debconf_set_console_selections() 
{
  # The config data was created after manual config with
  # debconf-get-selections | grep -e "^console*" |grep -v "select[[:space:]]*$"
  cat <<END | chroot $1 debconf-set-selections
console-data    console-data/keymap/qwerty/layout       select  US american
console-data    console-data/keymap/qwerty/us_american/standard/keymap  select  Standard
console-common  console-data/keymap/family      select  qwerty
console-data    console-data/keymap/family      select  qwerty
console-common  console-data/keymap/powerpcadb  boolean
console-data    console-data/keymap/powerpcadb  boolean
console-data    console-data/keymap/qwerty/us_american/variant  select  Standard
console-common  console-data/keymap/policy      select  Select keymap from arch list
console-data    console-data/keymap/policy      select  Select keymap from arch list
console-common  console-data/bootmap-md5sum     string  4bdb3d92fccb234f48640757dab09905
console-data    console-data/bootmap-md5sum     string  4bdb3d92fccb234f48640757dab09905
console-common  console-data/keymap/ignored     note
console-data    console-data/keymap/ignored     note
END
  
}

# function: install_packages
# get: $1 -- workdir
#      $2 -- useful packages switch (upack | no_upack)
#      $3 -- interactive (optional)
install_packages()
{
  local workdir=$1
  local kernel=$KERNEL
  local packages=$PACKAGES
  [ "$DEBOOTSTRAPDIR" ] && local bootcdpackages=$BOOTCDPACKAGES_LOCAL || local bootcdpackages=$BOOTCDPACKAGES_APT
  [ "$2" = "upack" ] && local usepkg=$UPACKAGES || local usepkg=""
  local interact=""; [ $# -ge 3 ] && interact="$3"
  local TODO=""
  local mm=0
  local IAHEAD="$IAHEAD / install_packages"

  # only pin sources.list if we install bootcd with apt-get
  if [ ! "$DEBOOTSTRAPDIR" ]; then
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_stdout $mm ".*"
    TODO="$TODO \"config_apt $workdir\""
  fi

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout $mm "^Get:.*"
  in_stdout $mm "^Hit .*"
  in_stdout $mm "^Ign .*"
  in_stdout $mm "^Fetched .*"
  in_stdout $mm "^Reading package lists....*"
  TODO="$TODO \"chroot $workdir apt-get update\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout $mm "write kernel-img.conf"
  TODO="$TODO \"config_kernel $workdir\""

  # Don't know why, but next step seems to be needed.
  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  TODO="$TODO \"chroot $workdir sh -c \\\"mkdir -p /tmp/.debootstrap\\\"\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout_aptget $mm
  in_stdout $mm "^/sys/class/net/ is not available, persistent interface names not saved."
  in_stdout $mm "^  busybox initramfs-tools klibc-utils libklibc libvolume-id0$"
  in_stdout $mm "^  linux-image-2.6.18-5-486 module-init-tools udev$"
  in_stdout $mm "^  linux-image-2.6-486 linux-image-2.6.18-5-486 module-init-tools udev$"
  in_stdout $mm "^  linux-doc-2.6.18 grub lilo$"
  in_stdout $mm "^ Hmm. The package shipped with a symbolic link /lib/modules/2.6.18-5-486/source"
  in_stdout $mm "^ However, I can not read the target: No such file or directory"
  in_stdout $mm "^ Therefore, I am deleting /lib/modules/2.6.18-5-486/source"
  in_stdout $mm "^A chroot environment has been detected, udev not started."
  in_stdout $mm "^Running depmod."
  in_stdout $mm "^Finding valid ramdisk creators."
  in_stdout $mm "^Using mkinitramfs-kpkg to build the ramdisk."
  in_stdout $mm "^linux-image.*is already the newest version."
  TODO="$TODO \"chroot $workdir sh -c \\\"apt-get -y -f install $kernel\\\"\""

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout_aptget $mm
  in_stdout $mm "^  genisoimage libmagic1 lvm-common$"
  in_stdout $mm "^  wodim cdrkit-doc grub-doc mdadm dmsetup"
  in_stdout $mm "^  mtools logrotate"
  in_stdout $mm "^  dosfstools fdutils file genisoimage grub libmagic1 lvm-common lvm2 mkisofs"
  in_stdout $mm "^  realpath"
  in_stdout $mm "^Backing up any"
  TODO="$TODO \"chroot $workdir sh -c \\\"apt-get -y -f install $packages\\\"\""
  # use packages from configured directory
  if [ "$DEBOOTSTRAPDIR" ]; then
    local inst=""
    for i in $bootcdpackages; do
       mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
       local file=$(ls $DEBOOTSTRAPDIR/${i}_*.deb |head -1)
       TODO="$TODO \"cp $file $workdir/tmp/\""
       [ "$inst" ] && inst="$inst /tmp/$(basename $file)" || inst="/tmp/$(basename $file)"
    done  
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_stdout $mm "^(Reading database ... .*"
    in_stdout $mm "^Selecting .*" 
    in_stdout $mm "^Unpacking .*"
    in_stdout $mm "^Setting up .*"
    in_stdout $mm "^update-initramfs: Generating.*"
    in_stdout $mm "^[[:digit:]]* files and directories .*"
    TODO="$TODO \"chroot $workdir sh -c \\\"dpkg -i $inst \\\"\""
  else 
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_stdout_aptget $mm
    in_stdout $mm ".*"
    TODO="$TODO \"chroot $workdir sh -c \\\"apt-get -y -f install $bootcdpackages\\\"\""
  fi

  if [ "$(echo "$usepkg" | grep console-data)" ]; then
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    TODO="$TODO \"debconf_set_console_selections $workdir\""
  fi

  if [ "$usepkg" ]; then
    mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
    in_stdout_aptget $mm
    # The following extra packages will be installed:
    in_stdout $mm "^  adduser defoma fontconfig-config ifupdown iputils-ping libconsole libdrm2$"
    in_stdout $mm "^  libedit2 libexpat1 libfontconfig1 libfreetype6 libfs6 libgl1-mesa-glx$"
    in_stdout $mm "^  libgpmg1 libice6 libkrb53 libnewt0.52 libpng12-0 libpopt0 libsm6 libssl0.9.8$"
    in_stdout $mm "^  libwrap0 libx11-6 libx11-data libxau6 libxaw7 libxcursor1 libxdmcp6 libxext6$"
    in_stdout $mm "^  libwrap0 libx11-6 libx11-data libxau6 libxaw7 libxcursor1 libxdmcp6 libxext6$"
    in_stdout $mm "^  libxfixes3 libxft2 libxi6 libxkbfile1 libxmu6 libxmuu1 libxpm4 libxrandr2$"
    in_stdout $mm "^  libxrender1 libxss1 libxt6 libxtrap6 libxtst6 libxv1 libxxf86dga1$"
    in_stdout $mm "^  libxxf86vm1 openbsd-inetd openssh-client openssh-server tcpd ttf-dejavu ucf$"
    in_stdout $mm "^  update-inetd vim-common vim-runtime whiptail x11-common$"
    # Suggested packages:
    in_stdout $mm "^  unicode-data kbd-compat defoma-doc psfontmgr x-ttcidfont-conf dfontmgr$"
    in_stdout $mm "^  iproute dhcp3-client dhcp-client ppp libfreetype6-dev gpm krb5-doc krb5-user$"
    in_stdout $mm "^  ssh-askpass rssh molly-guard ctags vim-doc vim-scripts mesa-utils$"
    # Recommended packages:
    in_stdout $mm "^  libft-perl libfribidi0 debconf-utils$"
    # The following NEW packages will be installed:
    in_stdout $mm "^  adduser console-common console-data console-tools defoma fontconfig-config$"
    in_stdout $mm "^  ifupdown iputils-ping less libconsole libdrm2 libedit2 libexpat1$"
    in_stdout $mm "^  libfontconfig1 libfreetype6 libfs6 libgl1-mesa-glx libgpmg1 libice6 libkrb53$"
    in_stdout $mm "^  libnewt0.52 libpng12-0 libpopt0 libsm6 libssl0.9.8 libwrap0 libx11-6$"
    in_stdout $mm "^  libx11-data libxau6 libxaw7 libxcursor1 libxdmcp6 libxext6 libxfixes3$"
    in_stdout $mm "^  libxft2 libxi6 libxkbfile1 libxmu6 libxmuu1 libxpm4 libxrandr2 libxrender1$"
    in_stdout $mm "^  libxss1 libxt6 libxtrap6 libxtst6 libxv1 libxxf86dga1 libxxf86vm1 net-tools$"
    in_stdout $mm "^  netbase openbsd-inetd openssh-client openssh-server psmisc ssh star tcpd$"
    in_stdout $mm "^  ttf-dejavu ucf update-inetd vim vim-common vim-runtime whiptail x11-common$"
    in_stdout $mm "^  xbase-clients$"
    # special from packages
    in_stdout $mm "^ifupdown.postinst: .*"
    in_stdout $mm "^Moving old data out of the way"
    in_stdout $mm "^Stopping internet superserver: .*"
    in_stdout $mm "Not starting internet superserver:"
    in_stdout $mm "^Creating SSH2 .* key; this may take some time ...$"
    in_stdout $mm "^Moving old data out of the way"
    in_stdout $mm "^Processing /usr/share/vim/addons/doc$"
    in_stdout $mm "^\.$"
    in_stdout $mm "^Restarting OpenBSD Secure Shell server:.*"
    in_stdout $mm "^Looking for keymap to install:$"
    in_stdout $mm "^NONE$"
    in_stdout $mm "^Setting console screen modes and fonts.$"
    in_stdout $mm "^Looking for keymap to install:$"

    TODO="$TODO \"chroot $workdir sh -c \\\"apt-get -y -f install $usepkg\\\"\""
  fi

  mm=$(($mm+1)); eval local IGNORE${mm}=\"\"; eval local STDOUT${mm}=\"\"
  in_stdout $mm "^--- configuring motd ---$"
  TODO="$TODO \"config_motd $workdir\""

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

# function: do_clean -- clean all things
# get: $1 -- tmpdir
do_clean()
{
  local tmpdir=$1

  rm -rf $tmpdir

  # remove only links!! link that debootstrap is working
  for i in $DEBOOTLINKS; do 
     [ -L $i ] && rm -f $i
  done
}


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


TODO=""
workdir=$WORKDIR
upack="upack"
interact=""
DEBOOTSTRAP=""
DEBOOTSTRAPDIR="" 
debootstrapmirror=""
mirror=""
mm=0
while [ $# -gt 0 ]; do
  case "$1" in
    "-d")
      shift
      workdir="$1"
      ;;
    "-nu")
      upack="no_upack"
      ;;
   "-h")
     shift
     IAHEAD="$1 / $IAHEAD"
     ;;
   "-m")
     shift
     mirror="$1"
     ;;
   "-i")
     interact="$1"
     ;;
    --help|-h) 
      usage
      ;;
    *) 
      if [ ! "$DEBOOTSTRAP" -a ! "$DEBOOTSTRAPDIR" ]; then
        [ -d "$1" ] && DEBOOTSTRAPDIR=$1 || DEBOOTSTRAP=$1
      else
        usage "parameter \"$1\" unknown!"
      fi
      ;;
  esac
  shift
done

[ ! "$DEBOOTSTRAP" -a ! "$DEBOOTSTRAPDIR" ] && usage "Mandatory parameter \"debootstrap path|directory\" not found!"

# if DEBOOTSTRAPDIR is set, the user has set DIR 
#    (DEBOOTSTRAP is automatically computed!)
if [ "$DEBOOTSTRAPDIR" ]; then
  DEBOOTSTRAPDIR="$(realpath $DEBOOTSTRAPDIR)"
  # check for debootstrap and bootcd packages 
  DEBOOTSTRAP="$(ls $DEBOOTSTRAPDIR/debootstrap_*.deb |head -1)"
  echo "use debootstrap package: $DEBOOTSTRAP"
  # search local bootcd packages
  for i in $BOOTCDPACKAGES_LOCAL; do
     [ ! -f "$(ls $DEBOOTSTRAPDIR/${i}_*.deb |head -1)" ] && err "debian package of $i not found in \"$DEBOOTSTRAPDIR\"!"
  done
fi

DEBOOTSTRAP="$(realpath $DEBOOTSTRAP)"
[ ! -f "$DEBOOTSTRAP" ] && err "debootstrap file \"$DEBOOTSTRAP\" not found!"


mm=$(($mm+1)); eval IGNORE${mm}=\"\"; eval STDOUT${mm}=\"\"
in_stdout $mm "^[[:space:]]*check for \"[^[:space:]]*\" ...ok$"
TODO="$TODO \"check_tools $TOOLS\""

mm=$(($mm+1)); eval IGNORE${mm}=\"\"; eval STDOUT${mm}=\"\"
in_stdout $mm "Directory.*"
TODO="$TODO \"do_dir $workdir 700\""

mm=$(($mm+1)); eval IGNORE${mm}=\"\"; eval STDOUT${mm}=\"\"
in_stdout $mm "Directory.*"
TODO="$TODO \"do_dir $TMPDIR 700\""

mm=$(($mm+1)); eval IGNORE${mm}=\"\"; eval STDOUT${mm}=\"\"
in_norun $mm
TODO="$TODO \"get_debootstrapmirror\""

mm=$(($mm+1)); eval IGNORE${mm}=\"\"; eval STDOUT${mm}=\"\"
in_stdout $mm "^extract debootstrap from debian package$"
TODO="$TODO \"extract_debootstrap $TMPDIR $DEBOOTSTRAP\""

mm=$(($mm+1)); eval IGNORE${mm}=\"\"; eval STDOUT${mm}=\"\"
in_stdout $mm "^I: .*"
TODO="$TODO \"$TMPDIR/usr/sbin/debootstrap --arch i386 --variant=buildd $DISTRI $workdir \\\$debootstrapmirror\""

mm=$(($mm+1)); eval IGNORE${mm}=\"\"; eval STDOUT${mm}=\"\"
in_norun $mm # do not start functions that itself use interactive with run
TODO="$TODO \"install_packages $workdir $upack <-i>\"" 

mm=$(($mm+1)); eval IGNORE${mm}=\"\"; eval STDOUT${mm}=\"\"
TODO="$TODO \"do_clean $TMPDIR\""

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