#!/bin/zsh -feu

zmodload zsh/regex

if getopt -T; then # should test (( $? = -4 ))
    echo "incompatible  getopt(1) installed. Abort"
    exit -1
fi

trap 'print ${PROGRAM-$0} ERROR: $LINENO:  $ZSH_EVAL_CONTEXT $0 >&2' ZERR

# wrapper for 3 commands:
# systemd-nspawn
# machinectl
# systemd-run

# Enter a container with 32-bit environment.

# 4 scenarios to invoke this:
# --boot  .... start a systemd as init
# --start .... bind & mount & invoke SHELL as init.

# cmd                 .... run the cmd inside a _running_
# --interactive .. will bind-mount standard user's dirs.

# --start cmd  run inside a _started_


#               full      |   user shell  | command
# start       --boot      |   --start     |
#   ...a non-running
# join-shell    cmd       |               |
#
# join          empty cmd                 |
#  ...interactively?


# defaults:
CONTAINER_DIR=
readonly MACHINES_DIR=/var/lib/machines

distro=sid
fallback_arch=x32
# i386

VERBOSE=n
as_root=n
boot=n
start=n
interactive=n
workdir=""
# todo:  --root

arguments=()

typeset -a bind_mounts=(
)

# full machine:
readonly special_id=$(id -u michal)
# my Source repositories to be able to build.

# fixme: maybe does not exist:
typeset -r new_machine_mounts=(/run/tmp/ /home/)
# sorry this break dbus on the host!  /run/user/$special_id
# you can only bind  /run/user/$special_id/pulse maybe.

# the `standalone' scenario
readonly user_bind_mounts=(
    $HOME
    /dev/
)


usage()
{
    setopt POSIX_ARGZERO
    cat <<EOF
usage: ${0##*/} [-d distro [-a arch] | -C dir ]    [+-n] [ -c dir] [-b dir] [--] ARGS...
       ${0##*/} [-l | -L]   list
       ${0##*/} [-k] .. (todo)


# which one
-a arch
-d  --distro ... name of the $MACHINES_DIR subdirectory
-a  --arch     i386, x32
-C --container  dir

# commands:
--start
-B --boot   ...
-n --private-network ... setup private network (no networking at start?)


# Generic commands:
-l --list\tlist the running machines
-L --list-image\tlist available machines?

# Enter:
-b --bind  .. add directory as --bind mount.
-r  ... start as root
-c  ... chdir at start

# Misc
-h  --usage --help ..
EOF
}

# what's the binding between short & long? only in the `case' below!
cmd_options=$(getopt --name $0 --shell bash \
                     --options a:b:BcC:d:hilLmn:rsv \
                     --longoptions \
                     arch:,bind:,boot,chdir:,container:,distro:,help,list,list-image,name,private-network,root,start,usage,verbose \
                     -- "$@" ) || { usage; exit 1 }
# name: n:
eval set -- "$cmd_options"

while (( $# > 0 )) do
      case $1 in
          -a | --arch)
              shift
              arch=$1
              ;;
          -b | --bind )
              shift
              bind_mounts+=("$1")
              ;;
          -B | --boot)
              boot=y
              arguments+=(--boot)
              # /dev/ ->no
              bind_mounts+=($new_machine_mounts)
              ;;
          -C | --container )
              shift
              CONTAINER_DIR="$1"
              ;;
          -c | --chdir )
              shift
              workdir="$1"
              ;;
          -d | --distro)
              shift
              distro=$1

              if [[ -z ${arch-} ]]; then
                  arch=$(arch)
              fi
              ;;

          -h | --usage | --help)
              usage
              exit 0
              ;;
          -l | --list)
              machinectl list
              exit 0
              ;;
          -L | --list-images)
              machinectl --all list-images
              exit 0
              ;;
          -n | --name)
               shift
               machine_name="$1"
               ;;

          -p| --private-network)
              arguments+=(--private-network)
              ;;
          -r | --root )
              # as root!
              as_root=y
              ;;
          -s | --start)
              start=y
              ;;
          -v | --verbose)
              VERBOSE=y
              ;;
          -- )
              shift
              break
              ;;
          *)
              echo "unknown option $1">&2
              usage >&2
              exit 2
      esac
      shift
done

if [ $VERBOSE = y ]; then set -x;fi

if [[ -t 1 && $# = 0 ]]; then
    interactive=y
fi


if [[ -z ${arch-} ]]; then
    arch=$fallback_arch
fi

# hostname is derived from this
if [[ ! -v machine_name ]]; then
    # _ . / is wrong!
    # echo
    machine_name=$distro-$arch
    machine_name=${machine_name//./-}
    machine_name=${machine_name/_/-}
    # is this wrong?  _ is forbidden char!
    # machine_name=$distro-${arch/_/-}
fi

if [[ -z $CONTAINER_DIR ]]; then
    CONTAINER_DIR=$MACHINES_DIR/$distro-$arch
fi


rewrite_array()
{
    # array=$1
    local prefix=$2
    local suffix=$3

    local i
    for ((i=1; i<= ${#bind_mounts}; i++))
        {
            if [[ -d $bind_mounts[$i] ]]
               then
                   bind_mounts[$i]="$prefix$bind_mounts[$i]$suffix"
            else
                # delete it:
                bind_mounts[$i]=""
            fi
        }
    # echo $bind_mounts
}

# --setenv --register=no
if [[ $VERBOSE = yy ]]; then set -x;fi

# sudo systemd-nspawn --bind=/dev/  --directory /var/lib/machines/sid-x32 '--bind=/home/michal' --capability=all  --boot --private-users=no


readonly FAKE_USER_DIR=/run/user-ro/

if [[ $start = y || $boot = y ]]; then

    if [[ $interactive = y && $boot = n ]]; then
       # this would be used if we --start:
       bind_mounts+=($user_bind_mounts)
    fi

    rewrite_array bind_mounts "--bind=" ""

    # this is needed for ... start only?
    arguments+=(--machine="$machine_name")
    arguments+=(--bind-ro /run/user:$FAKE_USER_DIR)
    arguments+=(--private-users=no)

    if [[ $boot = n && $as_root = n ]]
    then
        arguments+=(--user $USER)
    fi

    if [[ $workdir != "" ]];
    then
        arguments+=(--chdir "$workdir")
    fi


    command=()
    # for interactive use?

    if [[ $start = y ]]; then
        # This is needed if I run Zsh, and want the Job-control (C-z does not work without it)
        # this was useful when: systemd-nspawn ...i.e. 1-access only.
        command=(/usr/bin/setsid -c -w)

        if [[ $interactive = y ]]; then
            command+=(/bin/zsh -l)
        else
            command+=($@)
        fi
    fi

    if [[ $VERBOSE = y ]]; then
       set -x
    fi
    exec sudo systemd-nspawn \
         --directory $CONTAINER_DIR \
         --capability=all \
         $bind_mounts \
         $arguments $command
else

    # mmc: let's try:
    args=($arguments)

    if [[ $as_root = n ]]; then
         args+=( --uid=$(id --user) )
    fi

    if [[ -n ${GITSHA-} ]]; then
        args+=(--setenv "GITSHA=$GITSHA")
    fi

    args+=(--setenv "MAKEFLAGS=$MAKEFLAGS"
           --setenv PULSE_SERVER=$FAKE_USER_DIR/1000/pulse/native
           --setenv DISPLAY=$DISPLAY)

    # `join' an existing:
    if [[ $interactive = y ]]; then
        # --setenv "inside_container"
        if [[ $VERBOSE = y ]]; then
            set -x
        fi
        # for ubuntu 16.4 we need /bin/zsh appended:
        additional_args=()
        if [[ $machine_name = xena || $machine_name -regex-match ubuntu16 ]]; then
            additional_args+=(/bin/zsh)
        fi
        exec sudo machinectl $args shell $machine_name $additional_args
    else

        if [[ -n "$workdir" ]]; then
            # bug:
            args+=(-- /bin/bash -c)
            # I need the $@ to be inside the d-quotes!

            # This is ugly: once it is a string, and below it is array!
            cmd="cd $workdir; $@"
        else
            cmd=($@)
        fi

        if [[ $VERBOSE = y ]]; then
            set -x
        fi
        # for ubuntu16.4:
        if [[ $machine_name = xena ]]; then
            exec sudo machinectl shell $machine_name  $cmd
        else
        exec sudo systemd-run --pty --machine=$machine_name \
             $args $cmd
        fi
    fi
fi
