#!/bin/sh

HOST_USER=$USER
HOST_UID=$(getent passwd | grep "^$USER:" | cut -d":" -f3)
CURRENT_DIRECTORY=$(basename `pwd`)
DEBIAN_RELEASE=jessie
INVALID_OPTIONS=false
NO_GIT_BRANCH=false
GIT_BRANCH=
CONTAINER_NAME=
LIST_ALL_CONTAINERS=false
LXC_VERSION=$(lxc-ls --version)
DUMMY_MODE=false
LANG=C
LC_CTYPE=$LANG
BIND_LOCALHOST_PORT=3000
ARCH=
HOME_DIRECTORY=HOLODEV

# Detect OS
if [ -f /etc/lsb-release ]; then
  . /etc/lsb-release
  OS=$DISTRIB_ID
elif [ -f /etc/debian_version ]; then
  OS=Debian
elif [ -f /etc/arch-release ]; then
  OS=Arch
elif [ -f /etc/SuSE-release ]; then
  OS=Suse
else
  OS=$(uname -s)
fi

is_under_git_control() {
  git rev-parse --is-inside-work-tree > /dev/null 2>&1
}

if is_under_git_control; then
  GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
fi

previous=
for i in "$@"; do
  case $i in
    attach|create|destroy|info|list|restart|run|start|setup|stop)
      COMMAND=$i
      shift
      ;;
    --bind)
      BIND_LOCALHOST_PORT=$2
      shift
      ;;
    --no-branch)
      NO_GIT_BRANCH=true
      shift
      ;;
    --branch)
      GIT_BRANCH=$2
      shift 2
      ;;
    --release)
      DEBIAN_RELEASE=$2
      shift 2
      ;;
    --version)
      shift
      echo 0.9
      exit 0
      ;;
    --dummy)
      DUMMY_MODE=true
      shift
      ;;
    --all)
      LIST_ALL_CONTAINERS=true
      shift
      ;;
    --arch)
      ARCH=$2
      shift 2
      ;;
    *)
      if [ -n $previous ] && [ "$previous" = '--arch' ]; then
        # we don't need validation for --arch param
        true
      elif [ -n $previous ] && [ "$previous" = '--release' ]; then
        # we don't need validation for --release param
        true
      elif [ -n $previous ] && [ "$previous" = '--branch' ]; then
        # we don't need validation for --branch param
        true
      elif [ -n $previous ] && [ "$previous" = '--bind' ]; then
        # TODO validate if --bind param is only-number
        true
      elif [ -z "$COMMAND" ] || [ "$COMMAND" != 'run' ]; then
        INVALID_OPTIONS=true
      fi
      ;;
  esac
  previous=$i
done

if [ -z $COMMAND ] || $INVALID_OPTIONS; then
  echo "E: missing command or invalid options" >&2
  echo "" >&2
  echo "Usage: `basename $0` <command> [options]" >&2
  echo "" >&2
  echo "* <command> = {attach|create|destroy|info|list|restart|run|start|setup|stop}" >&2
  echo "* [options] = --no-branch" >&2
  echo "              --branch <branch-name>" >&2
  echo "              --dummy" >&2
  echo "              --release <debian-codename>" >&2
  echo "              --version" >&2
  echo "              --all" >&2
  exit 2
fi

if $NO_GIT_BRANCH || [ -z $GIT_BRANCH ]; then
  CONTAINER_NAME=$CURRENT_DIRECTORY
else
  CONTAINER_NAME=$CURRENT_DIRECTORY-$GIT_BRANCH
fi

compare_version() {
  VER1=$(echo $1 | sed 's/\.//g')
  VER2=$(echo $2 | sed 's/\.//g')
  [ $VER1 -ge $VER2 ]
}

run() {
  CYAN=$(tput setaf 6)
  ENDCOLOR=$(tput sgr0)
  if $DUMMY_MODE; then
    echo "${CYAN}[HOLODEV] sudo $@${ENDCOLOR}"
  else
    sudo "$@"
  fi
}

is_container_stopped() {
  CONTAINER_STATUS=$(run lxc-info -s -n $CONTAINER_NAME)
  echo $CONTAINER_STATUS | grep STOPPED > /dev/null
}

is_apparmor_enabled() {
  if compare_version $LXC_VERSION '1.1.0' && [ -f /sys/module/apparmor/parameters/enabled ]; then
    APPARMOR_STATUS=$(cat /sys/module/apparmor/parameters/enabled)
    echo $APPARMOR_STATUS | grep -e '^Y$' > /dev/null
  else
    false
  fi
}

start_if_stopped() {
  if is_container_stopped; then
    do_start
  fi
}

info() {
  GRAY=$(tput bold; tput setaf 8)
  ENDCOLOR=$(tput sgr0)
  if ! $DUMMY_MODE; then
    echo "${GRAY}[HOLODEV] $@${ENDCOLOR}"
  fi
}

highlight() {
  YELLOW=$(tput bold; tput setaf 3)
  ENDCOLOR=$(tput sgr0)
  echo "${YELLOW}[HOLODEV] $@${ENDCOLOR}"
}

error() {
  RED=$(tput bold; tput setaf 1)
  ENDCOLOR=$(tput sgr0)
  echo "${RED}[HOLODEV] $@${ENDCOLOR}"
}

create_default_configuration_file() {
  info "creating default configuration file"
  run sh -c "sed -i 's/lxc.network./#lxc.network./' /var/lib/lxc/$CONTAINER_NAME/config"
  run sh -c "echo >> /var/lib/lxc/$CONTAINER_NAME/config"
  run sh -c "echo \# holodev configurations >> /var/lib/lxc/$CONTAINER_NAME/config"
  run sh -c "echo lxc.network.type = veth >> /var/lib/lxc/$CONTAINER_NAME/config"
  run sh -c "echo lxc.network.link = virbr0 >> /var/lib/lxc/$CONTAINER_NAME/config"
  run sh -c "echo lxc.mount = /var/lib/lxc/$CONTAINER_NAME/fstab >> /var/lib/lxc/$CONTAINER_NAME/config"
  if is_apparmor_enabled; then
    run sh -c "echo lxc.aa_allow_incomplete = 1 >> /var/lib/lxc/$CONTAINER_NAME/config"
  fi
  if [ -f /etc/apparmor.d/disabled/usr.bin.lxc-start ]; then
    run sh -c "echo lxc.aa_profile = unconfined >> /var/lib/lxc/$CONTAINER_NAME/config"
  fi
}

lxc_attach() {
  if [ $OS = 'Arch' ]; then
    run lxc-attach --clear-env -n $CONTAINER_NAME -- "$@"
  else
    run lxc-attach -n $CONTAINER_NAME -- "$@"
  fi
}

create_user_into_container() {
  info "creating my user into the container"
  if $DUMMY_MODE || ! sudo grep $HOST_USER /var/lib/lxc/$CONTAINER_NAME/rootfs/etc/passwd > /dev/null; then
    lxc_attach adduser --system --shell /bin/bash --home /$HOME_DIRECTORY --uid $HOST_UID --disabled-password --quiet $HOST_USER
    lxc_attach chown $HOST_USER:nogroup /$HOME_DIRECTORY
  fi
}

add_user_to_sudo() {
  info "adding the user created in the container to sudo"
  if ! sudo test -e /var/lib/lxc/$CONTAINER_NAME/rootfs/etc/sudoers.d/sudo-group-nopasswd; then
    lxc_attach apt-get update
    lxc_attach apt-get -y install debian-archive-keyring sudo
    run sh -c "echo '%sudo ALL=(ALL) NOPASSWD:ALL' > /var/lib/lxc/$CONTAINER_NAME/rootfs/etc/sudoers.d/sudo-group-nopasswd"
    lxc_attach adduser $HOST_USER sudo
  fi
}

copy_dot_files_into_container() {
  info "copying dot files (gnupg, ssh, gitconfig, etc) into the container"
  if [ -d ~/.gnupg ]; then
    run sh -c "cp -r ~$HOST_USER/.gnupg /var/lib/lxc/$CONTAINER_NAME/rootfs/$HOME_DIRECTORY/"
    run sh -c "chown -R $HOST_USER:nogroup /var/lib/lxc/$CONTAINER_NAME/rootfs/$HOME_DIRECTORY/.gnupg"
  fi
  if [ -f ~/.gitconfig ]; then
    run sh -c "cp ~$HOST_USER/.gitconfig /var/lib/lxc/$CONTAINER_NAME/rootfs/$HOME_DIRECTORY/"
    run sh -c "chown -R $HOST_USER:nogroup /var/lib/lxc/$CONTAINER_NAME/rootfs/$HOME_DIRECTORY/.gitconfig"
  fi
}

ensure_UTF8_under_container() {
  info "enabling UTF-8 under container"
  lxc_attach locale-gen "$LANG.UTF-8"
  lxc_attach update-locale LANG=$LANG.UTF-8 LC_ALL=$LANG.UTF-8
}

setup_libvirt_Debian() {
  NET_AUTOSTART=$(sudo virsh net-info default | grep Autostart: | awk '{print $2}')
  NET_ACTIVE=$(sudo virsh net-info default | grep Active: | awk '{print $2}')
  if [ -n $NET_AUTOSTART ] && [ "$NET_AUTOSTART" != 'yes' ]; then
    sudo virsh net-autostart default
  fi
  if [ -n $NET_ACTIVE ] && [ "$NET_ACTIVE" != 'yes' ]; then
    sudo virsh net-start default
  fi
  sudo systemctl enable libvirt-guests
}

setup_libvirt_Ubuntu() {
  setup_libvirt_Debian
}

setup_libvirt_Suse() {
  setup_libvirt_Debian
}

setup_libvirt_Arch() {
  while systemctl list-units  | grep libvirt-guests.service | grep -q "deactivating"; do
    sleep 2s
  done
  while [ ! -S /var/run/libvirt/libvirt-sock ]; do
    sleep 2s
  done
  setup_libvirt_Debian
}

create_xinetd_config_file() {
  local FILENAME=/tmp/holodev-$CONTAINER_NAME.xinetd
  CONTAINER_IP=$(do_info | grep 'IP:' | awk '{print $2}')
  cat > $FILENAME <<EOF
    service holodev-$CONTAINER_NAME
    {
       socket_type = stream
       wait        = no
       user        = root
       type        = UNLISTED
       bind        = 127.0.0.1
       redirect    = $CONTAINER_IP $BIND_LOCALHOST_PORT
       port        = $BIND_LOCALHOST_PORT
    }
EOF
  echo $FILENAME
}

xinetd_bind() {
  info "binding localhost:$BIND_LOCALHOST_PORT"
  if is_container_stopped; then
    error "container $CONTAINER_NAME isn't running!"
  else
    sudo xinetd -f $(create_xinetd_config_file) -pidfile /run/$CONTAINER_NAME.xinetd.pid
  fi
}

xinetd_unbind() {
  info "un-binding localhost:$BIND_LOCALHOST_PORT"
  if [ -e /run/$CONTAINER_NAME.xinetd.pid ]; then
    sudo kill `cat /run/$CONTAINER_NAME.xinetd.pid`
  else
    error "xinetd isn't running for this container"
  fi
}

do_restart() {
  do_stop
  do_start
}

do_attach() {
  highlight "opening console to '$CONTAINER_NAME'"
  start_if_stopped
  lxc_attach su - $HOST_USER
}

do_setup() {
  which sudo > /dev/null; if [ $? -ne 0 ]; then
    echo
    error "sudo not found!" 1>&2
    error "Please install sudo first and add yourself to the sudo group"
    exit 4
  fi

  sudo which brctl > /dev/null; if [ $? -ne 0 ]; then
    echo
    error "brctl not found!" 1>&2
    error "Please install bridge-utils first and run 'setup' again"
    exit 4
  fi

  virbr0=$(sudo brctl show | grep virbr0 2>&1)
  if [ -z "$virbr0" ]; then
    info "configuring 'virbr0' virtual network device..."
    setup_libvirt_$OS
  fi
}

do_create() {
  highlight "creating '$CONTAINER_NAME' with '$DEBIAN_RELEASE'"

  # create container, deboostrap debian
  if [ ! -d /var/lib/lxc/$CONTAINER_NAME ]; then
    if [ ! -z $ARCH ]; then
      run LC_ALL=$LANG lxc-create -n $CONTAINER_NAME -t debian -- -r $DEBIAN_RELEASE -a $ARCH
    else
      run LC_ALL=$LANG lxc-create -n $CONTAINER_NAME -t debian -- -r $DEBIAN_RELEASE
    fi
  fi

  # mount current curectory (suppose is the source-code of project i'm working on) into container
  run sh -c "echo `pwd` /var/lib/lxc/$CONTAINER_NAME/rootfs/$HOME_DIRECTORY/$CURRENT_DIRECTORY none bind 0 0 > /var/lib/lxc/$CONTAINER_NAME/fstab"

  # create directory into container to mount project sources I'm working on
  if ! sudo test -d /var/lib/lxc/$CONTAINER_NAME/rootfs/$HOME_DIRECTORY/$CURRENT_DIRECTORY; then
    run sh -c "mkdir -p /var/lib/lxc/$CONTAINER_NAME/rootfs/$HOME_DIRECTORY/$CURRENT_DIRECTORY"
    run sh -c "echo 'cd $CURRENT_DIRECTORY' > /var/lib/lxc/$CONTAINER_NAME/rootfs/$HOME_DIRECTORY/.bash_profile"
    run sh -c "chown -R $HOST_USER:nogroup /var/lib/lxc/$CONTAINER_NAME/rootfs/$HOME_DIRECTORY/.bash_profile"
  fi

  create_default_configuration_file
  start_if_stopped
  create_user_into_container
  add_user_to_sudo
  copy_dot_files_into_container
  ensure_UTF8_under_container
}

do_info() {
  highlight "getting info about '$CONTAINER_NAME'"
  run lxc-info -n $CONTAINER_NAME
}

do_run() {
  COMMAND="$@"
  highlight "running '$COMMAND' under '$CONTAINER_NAME'"
  start_if_stopped
  lxc_attach su - $HOST_USER -c "$COMMAND"
}

up_network() {
  # be sure network is up and running
  if lxc_attach which systemctl > /dev/null; then
    RETRY=5
    info "checking if systemd is active"
    while [ $RETRY -gt 0 ]; do
      if lxc_attach systemctl 2>&1 | grep -q -e 'Unknown error -1'; then
        RETRY=$(echo "$RETRY - 1" | bc)
        sleep 1s
      else
        RETRY=0
        SYSTEMD_IS_ACTIVE=true
      fi
    done
    if [ $SYSTEMD_IS_ACTIVE ]; then
      info "waiting the system be operational"
      IS_SYSTEM_RUNNING=$(lxc_attach systemctl is-system-running)
      while ! echo $IS_SYSTEM_RUNNING | grep -q -e 'degraded' -e 'running'; do
        sleep 1s
        IS_SYSTEM_RUNNING=$(lxc_attach systemctl is-system-running)
      done
      lxc_attach systemctl enable systemd-networkd.service
      lxc_attach systemctl start systemd-networkd.service
    fi
  fi
  lxc_attach service networking start
}

do_start() {
  highlight "starting '$CONTAINER_NAME'"
  run lxc-start -n $CONTAINER_NAME -d
  run lxc-wait -n $CONTAINER_NAME -s RUNNING
  up_network
  xinetd_bind
}

do_stop() {
  highlight "stopping '$CONTAINER_NAME'"
  xinetd_unbind
  run lxc-stop -n $CONTAINER_NAME
}

do_destroy() {
  highlight "destroing '$CONTAINER_NAME'"
  xinetd_unbind
  run lxc-destroy -n $CONTAINER_NAME -f
}

do_list() {
  highlight "listing containers"
  if $LIST_ALL_CONTAINERS; then
    run lxc-ls -1
  else
    run lxc-ls -1 $CURRENT_DIRECTORY
  fi
}

do_$COMMAND "$@"
exit 0
