#!/bin/bash
#
# System installer.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License along with this program.  If not, see
# <http://www.gnu.org/licenses/>.

# Load.
source $APP_BASE/lib/hydra/functions || exit 1
hydra_config_load

# Create a logical volume.
function hydra_provision_lvcreate {
  local volume="$1"
  local size="$2"
  local space

  if [ -z "$volume" ] || [ "$size" == "0" ]; then
    return
  fi

  if ! lvdisplay $vg/$volume &> /dev/null; then
    echo "Creating logical volume $volume..."
    if [ "$size" == "-1" ]; then
      space="-l 100%FREE"
    else
      space="-L $size"
    fi

    # See http://forums.funtoo.org/viewtopic.php?id=1206
    #     https://bbs.archlinux.org/viewtopic.php?id=124615
    if [ "$disable_zeroing" == "y" ]; then
      hydra_safe_run lvcreate -Z n $space -n $volume $vg
    else
      hydra_safe_run lvcreate $space -n $volume $vg
    fi
  fi
}

# Create a physical volume.
function hydra_provision_create_volume {
  local volume="$1"

  if [ -z "$volume" ] || [ ! -b "/dev/mapper/$vg-$volume" ]; then
    return
  fi

  if [ "$encrypt" == "y" ]; then
    echo "Creating encrypted $volume device..."
    hydra_safe_run cryptsetup -h sha256 -c aes-cbc-essiv:sha256 -s 256 luksFormat /dev/mapper/$vg-$volume
    hydra_safe_run cryptsetup luksOpen /dev/mapper/$vg-$volume $volume
    hydra_safe_run mkfs.ext4 /dev/mapper/$volume

    if [ "$volume" == "root" ]; then
      install_device="/dev/mapper/root"
    fi
  else
    echo "Creating $volume device..."
    mkfs.ext4 /dev/mapper/$vg-$volume

    if [ "$volume" == "root" ]; then
      install_device="/dev/mapper/$vg-root"
    fi
  fi
}

# Make sure there is provision config.
function hydra_provision_config {
  hydra_user_config interactive y "Interactive mode? (y/n)"
  hydra_user_config device /dev/sdb "Destination device"
  hydra_user_config device_label msdos "Device disk label"
  hydra_user_config root_size 20G "Size of root partition"
  hydra_user_config swap_size 2000 "Swap size (in MB, 0 to not create it)"
  hydra_user_config home_size 20G "Size of home partition (0 to not create it, -1 for all free space)"
  hydra_user_config var_size 20G "Size of var partition (0 to not create it, -1 for all free space)"
  hydra_user_config encrypt y "Encrypt volumes? (y/n)"
  hydra_user_config garbage y "Pre-fill volumes with garbage? (y/n)"
  hydra_user_config disable_zeroing n "Disable zeroing of lvm volumes? (y/n)"
  hydra_user_config hostname $HOSTNAME "Hostname"
  hydra_user_config domain example.com "Domain"
  hydra_user_config arch amd64 "System arch"
  hydra_user_config version wheezy "Distro version"
  hydra_user_config vg vg "Temporary install vg"
  hydra_user_config grub y "Setup GRUB? (y/n)"
  hydra_user_config mirror http://http.debian.net/debian/ "Debian mirror"
}

# Setup.
if [ ! -z "$1" ]; then
  if [ -f "$1" ]; then
    echo "Using configuration from file $1..."
    source "$1"
  else
    echo "File not found: $1"
    exit 1
  fi
fi

# Get config parameters.
hydra_provision_config

# Check for requirements.
for req in debootstrap cryptsetup grub-pc lvm2 parted mdadm; do
  hydra_install_package $req
done

# Warning.
if [ "$interactive" == "y" ]; then
  echo "WARNING: about to partition $device!"
  echo "Press ENTER to continue, Ctrl-C to abort."
  read answer
fi

# Disk partitioning.
if [ "$swap" != "0" ]; then
  boot_end=$(($swap_size + 200))
  hydra_safe_run parted -s -- $device mklabel $device_label
  hydra_safe_run parted -s -- $device unit MB mkpart primary linux-swap 2          $swap_size
  hydra_safe_run parted -s -- $device unit MB mkpart primary ext2       $swap_size $boot_end
  hydra_safe_run parted -s -- $device unit MB mkpart primary ext2       $boot_end  -1
  hydra_safe_run parted -s -- $device set 2 boot on
  hydra_safe_run parted -s -- $device set 3 lvm  on

  # Change devices to absolute path names.
  swap_device="$device"1
  boot_device="$device"2
  syst_device="$device"3
else
  hydra_safe_run parted -s -- $device mklabel $device_label
  hydra_safe_run parted -s -- $device unit MB mkpart primary ext2 2    200
  hydra_safe_run parted -s -- $device unit MB mkpart primary ext2 200 -1
  hydra_safe_run parted -s -- $device set 1 boot on
  hydra_safe_run parted -s -- $device set 2 lvm  on

  # Change devices to absolute path names.
  boot_device="$device"1
  syst_device="$device"2
fi

# Create volumes.
echo "Creating the needed disk volumes..."

if ! pvdisplay $syst_device &> /dev/null; then
  echo "Creating physical volume..."
  hydra_safe_run pvcreate $syst_device
fi

if ! vgdisplay $vg &> /dev/null; then
  echo "Creating volume group..."
  hydra_safe_run vgcreate $vg $syst_device
fi

# Create root partition
hydra_safe_run vgchange -a y $vg
hydra_provision_lvcreate root $root_size

# Create home partition
if [ "$home_size" != "0" ]; then
  hydra_provision_lvcreate home $home_size
fi

# Create var partition
if [ "$var_size" != "0" ]; then
  hydra_provision_lvcreate var  $var_size
fi

# Garbage.
if [ "$garbage" == "y" ]; then
  echo "Filling volumes with garbage..."

  dd if=/dev/urandom of=/dev/mapper/$vg-root

  if [ -b "/dev/mapper/$vg-home" ]; then
    dd if=/dev/urandom of=/dev/mapper/$vg-home
  fi

  if [ -b "/dev/mapper/$vg-var" ]; then
    dd if=/dev/urandom of=/dev/mapper/$vg-var
  fi

  if [ "$swap" != "0" ]; then
    dd if=/dev/urandom of=$swap_device
  fi
fi

# Setup mountpoint and make sure it's not mounted due to a failed install.
mkdir -p /tmp/debootstrap
umount /tmp/debootstrap &> /dev/null
for folder in proc dev home var boot sys; do
  umount /tmp/debootstrap/$folder &> /dev/null
done

# Create root fs
hydra_provision_create_volume root

# Create home fs
if [ "$home_size" != "0" ]; then
  hydra_provision_create_volume home
fi

# Create var fs
if [ "$var_size" != "0" ]; then
  hydra_provision_create_volume var
fi

# Mount root volume
hydra_safe_run mount $install_device /tmp/debootstrap/

# Mount additional volumes
if [ "$home_size" != "0" ]; then
  mkdir /tmp/debootstrap/home

  if [ "$encrypt" == "y" ]; then
    hydra_safe_run mount /dev/mapper/home /tmp/debootstrap/home
  else
    hydra_safe_run mount /dev/mapper/$vg-home /tmp/debootstrap/home
  fi
fi

if [ "$var_size" != "0" ]; then
  mkdir /tmp/debootstrap/var

  if [ "$encrypt" == "y" ]; then
    hydra_safe_run mount /dev/mapper/var /tmp/debootstrap/var
  else
    hydra_safe_run mount /dev/mapper/$vg-var /tmp/debootstrap/var
  fi
fi

# Initial system install.
echo "Installing base system..."
hydra_safe_run debootstrap --arch=$arch $version /tmp/debootstrap/ $mirror

# Initial configuration.
echo "Applying initial configuration..."
mount none -t proc /tmp/debootstrap/proc
mount none -t sysfs /tmp/debootstrap/sys
mount -o bind /dev/ /tmp/debootstrap/dev
echo LANG=C > /tmp/debootstrap/etc/default/locale

# Resolver configuration.
echo "domain $domain" > /tmp/debootstrap/etc/resolv.conf
echo "search $hostname.$domain" >> /tmp/debootstrap/etc/resolv.conf
grep nameserver /etc/resolv.conf >> /tmp/debootstrap/etc/resolv.conf

# Hostname configuration.
echo $hostname.$domain > /tmp/debootstrap/etc/hostname
echo "127.0.0.1 localhost" >> /tmp/debootstrap/etc/hosts
echo "127.0.0.1 $hostname $hostname.$domain" >> /tmp/debootstrap/etc/hosts

# Invert hostname contents to avoid http://projects.puppetlabs.com/issues/2533
tac /tmp/debootstrap/etc/hosts > /tmp/debootstrap/etc/hosts.new
mv /tmp/debootstrap/etc/hosts.new /tmp/debootstrap/etc/hosts

# Initial upgrade.
echo "Applying initial upgrades..."
chroot /tmp/debootstrap/ apt-get update
chroot /tmp/debootstrap/ apt-get upgrade -y
chroot /tmp/debootstrap/ apt-get install locales cryptsetup lvm2 initramfs-tools -y

# Crypttab.
echo "Configuring crypttab..."
echo "# <target name> <source device>   <key file>  <options>" > /tmp/debootstrap/etc/crypttab

if [ "$encrypt" == "y" ]; then
  cat > /tmp/debootstrap/etc/crypttab <<-EOF
root            /dev/mapper/vg-root      none            luks,cipher=aes-cbc-essiv:sha256  
EOF
fi

if [ "$home_size" != "0" ] && [ "$encrypt" == "y" ]; then
  cat >> /tmp/debootstrap/etc/crypttab <<-EOF
home            /dev/mapper/vg-home      none            luks,cipher=aes-cbc-essiv:sha256
EOF
fi

if [ "$var_size" != "0" ] && [ "$encrypt" == "y" ]; then
  cat >> /tmp/debootstrap/etc/crypttab <<-EOF
var             /dev/mapper/vg-var       none            luks,cipher=aes-cbc-essiv:sha256
EOF
fi

if [ "$swap" != "0" ]; then
  cat >> /tmp/debootstrap/etc/crypttab <<-EOF
cswap           $swap_device                  /dev/random     swap,cipher=aes-cbc-essiv:sha256
EOF
fi

# Fstab.
echo "Configuring fstab..."
echo "" > /tmp/debootstrap/etc/fstab
if [ "$swap" != "0" ]; then
  cat >> /tmp/debootstrap/etc/fstab <<-EOF
/dev/mapper/cswap     none           swap  sw                                 0 0
EOF
fi

if [ "$encrypt" == "y" ]; then
  cat >> /tmp/debootstrap/etc/fstab <<-EOF
/dev/mapper/root      /              ext4  defaults,errors=remount-ro         0 1
EOF
else
  cat >> /tmp/debootstrap/etc/fstab <<-EOF
/dev/mapper/vg-root   /              ext4  defaults,errors=remount-ro         0 1
EOF
fi

if [ "$home_size" != "0" ]; then
  if [ "$encrypt" == "y" ]; then
  cat >> /tmp/debootstrap/etc/fstab <<-EOF
/dev/mapper/home      /home          ext4  defaults,errors=remount-ro         0 2
EOF
  else
  cat >> /tmp/debootstrap/etc/fstab <<-EOF
/dev/mapper/vg-home   /home          ext4  defaults,errors=remount-ro         0 2
EOF
  fi
fi

if [ "$var_size" != "0" ]; then
  if [ "$encrypt" == "y" ]; then
  cat >> /tmp/debootstrap/etc/fstab <<-EOF
/dev/mapper/var       /var           ext4  defaults,errors=remount-ro         0 2
EOF
  else
  cat >> /tmp/debootstrap/etc/fstab <<-EOF
/dev/mapper/vg-var    /var           ext4  defaults,errors=remount-ro         0 2
EOF
  fi
fi

# Grub.
if [ "$grub" == "y" ]; then
  echo "Boot device setup..."
  hydra_safe_run mkfs.ext4 $boot_device
  hydra_safe_run mount $boot_device /tmp/debootstrap/boot
  echo "$boot_device /boot ext4 defaults,errors=remount-ro 0 2" >> /tmp/debootstrap/etc/fstab

  echo "Setting up GRUB..."
  hydra_safe_run chroot /tmp/debootstrap/ apt-get install grub-pc -y
fi

# Kernel.
echo "Installing kernel..."
cat > /tmp/debootstrap/etc/initramfs-tools/modules <<-EOF
dm-mod
dm-crypt
aes
twofish
sha256
EOF

cat > /tmp/debootstrap/etc/kernel-img.conf <<-EOF
do_initrd = Yes
EOF

if [ "$arch" == "i386" ]; then
  kernel_arch="686"
else
  kernel_arch="$arch"
fi

if [ "$version" == "squeeze" ]; then
  hydra_safe_run chroot /tmp/debootstrap apt-get install linux-image-2.6-vserver-$kernel_arch -y
else
  hydra_safe_run chroot /tmp/debootstrap apt-get install linux-image-$kernel_arch -y
fi

# Initramfs.
echo "Creating initramfs..."
hydra_safe_run chroot /tmp/debootstrap update-initramfs -v -u

# Utils.
echo "Installing basic utilities..."
chroot /tmp/debootstrap apt-get install screen cron lsb-release openssl -y

# Ssh.
echo "Installing OpenSSH daemon..."
chroot /tmp/debootstrap apt-get install openssh-server -y

echo "OpenSSH fingerprints:"
chroot /tmp/debootstrap ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key.pub
chroot /tmp/debootstrap ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub

# Accounts.
echo "Installing sudo..."
chroot /tmp/debootstrap apt-get install sudo -y

echo "Choose a root password."
chroot /tmp/debootstrap passwd root

cat <<-EOF

Now proceeed with final steps:

  - Create an user account with sudo privileges.
  - Network setup.

See http://padrao.sarava.org/install for more information.
EOF