#!/bin/sh
set -e

DISTRO=ubuntu
SUITE=xenial
ARCH=amd64
DISKSIZE=12287
LXC=0
VBOX=0
DOCKER=0
DOCKER_IMAGE_HASH=""

usage() {
  echo "Usage: ${0##*/} [OPTION]..."
  echo "Make a base client."
  echo
  cat << EOF
  --help                    display this help and exit
  --distro D                build distro D (e.g. debian) instead of ubuntu
  --suite U                 build suite U instead of xenial
  --arch A                  build architecture A (e.g. i386) instead of amd64
  --disksize S              disk/image size S in MB (default 12287)
  --lxc                     use lxc instead of kvm
  --vbox                    use VirtualBox instead of kvm
  --docker                  use docker instead of kvm
  --docker-image-hash D     digest of the docker image to build from

  The MIRROR_HOST environment variable can be used to change the
  apt-cacher host.  It should be something that both the host and the
  target VM can reach.  It may be set to 127.0.0.1, in which case it will be
  changed to 10.0.2.2 on the guest (or GITIAN_HOST_IP if it is defined)
  10.0.2.2 is the host IP as visible from the guest under qemu networking.

  The DEBOOTSTRAP_DIR (but also GITIAN_SUDO_USE_DEBOOTSTRAP_DIR, see below!)
  environment variable can be set to select a directory
  that will contain data like in "/usr/share/debootstrap/". This allows user to
  make a copy of this files to some local dir and modify them locally:
  e.g. set env variable "DEBOOTSTRAP_DIR=./mydeboot/", then copy or link
  system's version of files there, and modify them there
  (e.g. copy your debootstrap-script file "xenial" to "./mydeboot/scripts/").

  Set env GITIAN_SUDO_USE_DEBOOTSTRAP_DIR="yes" to allow sudo for debootstrap
  to use flags like --preserve-env that are required for DEBOOTSTRAP_DIR to work.
  It must be equal string "yes".
  This is done as separate variable to make it clear that we modify sudo
  behaviour here regarding security (though anyway env is cleared with
  whitelist so should be perfectly safe).

  The --docker-image-hash option can be used to specify the hash of a particular
  base image to use. These hashes can be found under the "RepoDigests" field of
  "docker image inspect <image>". They will be reported in the form "sha256:<hash>";
  only need the <hash> part is needed
EOF
}

if [ $# != 0 ] ; then
  while true ; do
    case "$1" in
      --help|-h)
        usage
        exit 0
        ;;
      --distro|-d)
        DISTRO="$2"
        shift 2
        ;;
      --suite|-s)
        SUITE="$2"
        shift 2
        ;;
      --arch|-a)
        ARCH="$2"
        shift 2
        ;;
      --disksize)
        DISKSIZE="$2"
        shift 2
        ;;
      --lxc)
        LXC=1
        shift 1
        ;;
      --vbox)
        VBOX=1
        shift 1
        ;;
      --docker)
        DOCKER=1
        shift 1
        ;;
      --docker-image-digest)
        DOCKER_IMAGE_HASH="$2"
        shift 2
        ;;
      --*)
        echo "unrecognized option $1"
        exit 1
        ;;
      *)
        break
        ;;
    esac
  done
fi

if [ $DOCKER = "1" ]; then
  MIRROR_DEFAULT=172.17.0.1
else
  MIRROR_DEFAULT=127.0.0.1
fi
MIRROR_BASE=http://${MIRROR_HOST:-$MIRROR_DEFAULT}:3142

if [ $DISTRO = "ubuntu" ]; then
  MIRROR=$MIRROR_BASE/archive.ubuntu.com/ubuntu
  SECURITY_MIRROR=$MIRROR_BASE/security.ubuntu.com/ubuntu
  components=main,universe
elif [ $DISTRO = "debian" ]; then
  MIRROR=$MIRROR_BASE/ftp.debian.org/debian
  SECURITY_MIRROR=$MIRROR_BASE/security.debian.org/
  components=main,contrib
fi

mkdir -p var

if [ ! -e var/id_rsa ]; then
  ssh-keygen -t rsa -f var/id_rsa -N ""
fi

OUT=base-$SUITE-$ARCH
FLAVOUR=virtual

if [ $ARCH = "amd64" -a $SUITE = "hardy" ]; then
  FLAVOUR=server
fi

if [ $DISTRO = "debian" -a $ARCH = "amd64" ]; then
  FLAVOUR=amd64
elif [ $DISTRO = "debian" -a $ARCH = "i386" -a \($SUITE = "squeeze" -o $SUITE = "lenny" -o $SUITE = "etch" -o $SUITE = "sarge" -o $SUITE = "woody" -o $SUITE = "potato" -o $SUITE = "slink" -o $SUITE = "hamm" -o $SUITE = "bo" -o $SUITE = "rex" -o $SUITE = "buzz"\) ]; then
  FLAVOUR=686
elif [ $DISTRO = "debian" ]; then
  FLAVOUR=686-pae
fi


LOCALE_PKG=language-pack-en
if [ $DISTRO = "debian" ]; then
  LOCALE_PKG=locales
fi

addpkg=pciutils,build-essential,git,subversion,$LOCALE_PKG,wget,lsb-release,sudo,gnupg

if [ $DISTRO = "ubuntu" ]; then
  # Need comma at end to work around an issue with apt for Debian <= Wheezy regarding empty strings
  #
  # If we left the comma down below when adding KERNEL_PKG to addpkg, the fact that KERNEL_PKG is undefined
  # if DISTRO is debian would result in two commas in a row (,,), which is interpreted by apt-get as the
  # package with the name empty string (""). This triggers a bug with apt versions < 1.0.3. So by adding the
  # comma to the end of KERNEL_PKG, we are including that comma if the distro is ubuntu (and therefore we do
  # have a kernel package that needs to be installed). If KERNEL_PKG is not set (i.e. we have Debian as the
  # distro), then we don't add that extra comma and therefore, we don't end up with two commas in a row.
  #
  # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=744940
  # http://anonscm.debian.org/cgit/apt/apt.git/commit/?h=1.0.3&id=d99854cac4065bc7b337815fb2116269d58dab73
  KERNEL_PKG=linux-image-generic,
fi

GRUB_PKG=grub
if [ $DISTRO = "ubuntu" ]; then
  GRUB_PKG=grub-pc
fi

if [ $LXC = "1" ]; then
  addpkg=$addpkg,lxc
else
  # Lack of comma after KERNEL_PKG is not a typo
  addpkg=$addpkg,${KERNEL_PKG}${GRUB_PKG},openssh-server
fi

# Remove cron to work around vmbuilder issue when umounting /dev on target
removepkg=cron

if [ $DOCKER = "1" ]; then

  addpkg=`echo $addpkg | tr ',' ' '`

  mkdir -p docker
  cd docker

  if [ -n "$DOCKER_IMAGE_HASH" ]; then
    base_image="$DISTRO@sha256:$DOCKER_IMAGE_HASH"
    OUT=base-$DOCKER_IMAGE_HASH-$ARCH
  else
    base_image="$DISTRO:$SUITE"
  fi

  if [ $(uname) != "Darwin" ]; then
    CACHELINE=$(cat << EOF0
RUN echo 'Acquire::http { Proxy "$MIRROR_BASE"; };' > /etc/apt/apt.conf.d/50cacher
EOF0
)
  else
    # MacOS, no local debian cache
    CACHELINE=""
  fi
  # Generate the dockerfile
  cat << EOF > $OUT.Dockerfile
FROM $base_image

ENV DEBIAN_FRONTEND=noninteractive
$CACHELINE
RUN apt-get update && apt-get --no-install-recommends -y install $addpkg

RUN useradd -ms /bin/bash -U $DISTRO
USER $DISTRO:$DISTRO
WORKDIR /home/$DISTRO

CMD ["sleep", "infinity"]
EOF

  docker build --pull -f $OUT.Dockerfile -t $OUT .

  exit 0
fi

if [ $VBOX = "1" ]; then
  NAME="$SUITE-$ARCH"
  if ! vagrant status | grep "$NAME" | grep "not created" > /dev/null; then
    echo "Vagrant machine "$NAME" already exists, please remove it first (vagrant destroy "$NAME")"
    exit 1
  fi

  DISTRO_USER_CREATE=0
  if [ $DISTRO = "debian" ]; then
      # we use a vagrant provider
      DISTRO_USER_CREATE=1
  fi

  vagrant up "$NAME"
  if [ $DISTRO_USER_CREATE = "1" ]; then
    vagrant ssh "$NAME" -c "sudo useradd -m -s /bin/bash $DISTRO"
  fi

  vagrant ssh "$NAME" -c "sudo mkdir -p /root/.ssh && sudo chmod 700 /root/.ssh"
  vagrant ssh "$NAME" -c "sudo sh -c 'cat >> /root/.ssh/authorized_keys'" < var/id_rsa.pub
  vagrant ssh "$NAME" -c "sudo -u $DISTRO mkdir -p /home/$DISTRO/.ssh && sudo -u $DISTRO chmod 700 /home/$DISTRO/.ssh"
  vagrant ssh "$NAME" -c "sudo sh -c 'cat >> /home/$DISTRO/.ssh/authorized_keys'" < var/id_rsa.pub

  VBoxManage snapshot "Gitian-$NAME" take "Gitian-Clean"
  vagrant suspend "$NAME"

  exit 0
fi

if [ $LXC = "1" ]; then
  if [ -e $OUT ]; then
    echo $OUT already exists, please remove it first
    exit 1
  fi
  sudo rm -rf $OUT-bootstrap
  # Need universe for lxc in lucid

  unset preserve_env
  if [ "$GITIAN_SUDO_USE_DEBOOTSTRAP_DIR" = "yes" ]; then
    echo "sudo will preserve (some) env flags"
    preserve_env=yes # if you would want to set false then unset this variable
  fi
  env -i LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 DEBOOTSTRAP_DIR="$DEBOOTSTRAP_DIR" sudo ${preserve_env+--preserve-env} debootstrap --arch=$ARCH --include=$addpkg --exclude=$removepkg --components=$components $SUITE $OUT-bootstrap $MIRROR
  # Fix lxc issue
  if [ -f $OUT-bootstrap/usr/lib/lxc/lxc-init ]
  then
    sudo cp $OUT-bootstrap/usr/lib/lxc/lxc-init $OUT-bootstrap/usr/sbin/init.lxc
  else
    if [ $ARCH = "amd64" ]
    then
      if [ -f $OUT-bootstrap/usr/lib/x86_64-linux-gnu/lxc/lxc-init ]
      then
        sudo cp $OUT-bootstrap/usr/lib/x86_64-linux-gnu/lxc/lxc-init $OUT-bootstrap/usr/sbin/init.lxc
      fi
    else
      if [ -f $OUT-bootstrap/usr/lib/i386-linux-gnu/lxc/lxc-init ]
      then
        sudo cp $OUT-bootstrap/usr/lib/i386-linux-gnu/lxc/lxc-init $OUT-bootstrap/usr/sbin/init.lxc
      fi
    fi
  fi
  dd if=/dev/zero of=$OUT-lxc bs=1M count=1 seek=$DISKSIZE
  /sbin/mkfs.ext4 -F $OUT-lxc
  t=`mktemp -d gitian.XXXXXXXX`
  sudo mount $OUT-lxc $t
  sudo cp -a $OUT-bootstrap/* $t
  sudo umount $t
  rmdir $t

  sudo rm -rf $OUT-bootstrap
  mv $OUT-lxc $OUT
  # bootstrap-fixup is done in libexec/make-clean-vm
else
  if [ -e $OUT.qcow2 ]; then
    echo $OUT.qcow2 already exists, please remove it first
    exit 1
  fi
  libexec/config-bootstrap-fixup

  rm -rf $OUT
  env -i LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 sudo vmbuilder kvm $DISTRO --rootsize $DISKSIZE --arch=$ARCH --suite=$SUITE --addpkg=$addpkg --removepkg=$removepkg --ssh-key=var/id_rsa.pub --ssh-user-key=var/id_rsa.pub --mirror=$MIRROR --security-mirror=$SECURITY_MIRROR --dest=$OUT --flavour=$FLAVOUR --firstboot=`pwd`/target-bin/bootstrap-fixup
  mv $OUT/*.qcow2 $OUT.qcow2
  rm -rf $OUT
  # bootstrap-fixup is done on first boot
fi

