#!/bin/bash

set -e

usage ()
{
    cat <<EOF
$0 [OPTIONS] -- IMAGE [GROUP [NODE]]

Options:
  --verbose true/false
	Whether to run in verbose mode

  IMAGE
	Docker tcwg-host image

  GROUP
	User group to configure access to; or "all"

  NODE
	Jenkins node ID to connect as; or "host"
EOF
    exit 1
}

group="all"
node="host"
jenkins_server="https://ci.linaro.org"
verbose=false
additional_options=""

while [ $# -gt 0 ]; do
    case $1 in
	--verbose) verbose="$2"; shift ;;
	--additional_options) additional_options="$2"; shift ;;
	--) shift; break ;;
	*) echo "ERROR: Wrong option: $1"; usage ;;
    esac
    shift
done

image="$1"
group="${2-$group}"
node="${3-$node}"
jenkins_server="${4-$jenkins_server}"

if $verbose; then
    set -x
fi

if [ x"$image" = x"" ]; then
  echo "ERROR: image name not provided"
  usage
fi

if [ x"$(id -u)" = x"0" ] || groups 2>/dev/null | grep -q docker; then
    # Run docker straight up if $USER is root or in "docker" group.
    DOCKER="docker"
elif groups tcwg-buildslave 2>/dev/null | grep -q docker; then
    # If tcwg-buildslave user is present, use it to start the container
    # to have [sudo] log record of container startups.
    DOCKER="sudo -u tcwg-buildslave docker"
else
    # Fallback to sudo otherwise.
    DOCKER="sudo docker"
fi

mounts=""
# Bind-mount user HOMEs.
mounts="$mounts -v /home:/home"
# Bind-mount root's home so that run.sh can grant $root_users access
# to bare machine.
mounts="$mounts -v /root:/root"
# Bind-mount docker socket and binary to give access to docker.
mounts="$mounts -v /var/run/docker.sock:/var/run/docker.sock"
mounts="$mounts -v /usr/bin/docker:/usr/bin/docker"
# Bind-mount ssh host keys.
for key in /etc/ssh/ssh_host_*_key{,.pub}; do
    mounts="$mounts -v $key:$key:ro"
done

# Use at most half of all available RAM.
memlimit=$(free -m | awk '/^Mem/ { print $2 }')
memlimit=$(($memlimit / 2))m

case "$(uname -r):$(uname -m):$($DOCKER --version | cut -d" " -f3)" in
    *:armv7l:18*|\
    4.4.38-tegra:aarch64:18*)
	# Somewhere between docker-ce 19.03.5 and 19.03.6 docker bridge network
	# got broken on, at least, armhf with 3.10 kernel (aka TK1s).
	# At the same time to run ubuntu:focal we need docker-ce 19.03.9-ish
	# due to seccomp not supporting some of the syscalls.
	# We have two options:
	# 1. Use old docker and workaround ubuntu:focal's seccomp problem by
	# disabling it via --privileged option.
	# 2. Use new docker and workaround broken bridge network by using
	# --network host.
	# In the case of tcwg-tk1-* and tcwg-tx1-* boards, which are used
	# for jenkins CI, we need the bridge network, so we choose (1).
	workaround="--privileged"
	;;
    *:aarch64:[0-9].*|\
	*:aarch64:1[0-9].*|\
	*:aarch64:2[0123].*)
	# On aarch64 servers (which we generally use both in aarch64
	# and aarch32 modes), we require a recent-enough docker.
	# Docker from Ubuntu 20.04 had problems with some syscalls in
	# aarch32 mode, leading to wrong regression reports when
	# running glibc tests in aarch32 mode. (version 24.0.5 from
	# Ubuntu 22.04.3 is OK)
	msg="Docker too old: $($DOCKER --version). Upgrade to 24.0.5+"
	# If we are running on the bare machine, make this an error, a
	# warning otherwise.
	if [ -f /.dockerenv ]; then
	    echo "WARNING: $msg"
	else
	    echo "ERROR: $msg"
	    exit 1
	fi
	;;
esac

# Enable WSL-Interop inside containers.  This allows us to build toolchains
# inside docker containers inside WSL2 environments, and still have ability
# to run generated win32 executables.
# If this doesn't work, make WSL2 version is 2.0.14 or later; WSL 2.0.9
# has a bug preventing interop outside of the "main init" process tree.
case "$(uname -r)" in
    *"-WSL2") mounts="$mounts -v /init:/init:ro -v /run/WSL:/run/WSL" ;;
esac

# Since we're starting a host container, take the opportunity to ensure that GDB
# can attach to test programs when running the testsuite, and that core files
# are being correctly stored in the filesystem.
(
    # Use "sudo sysctl" because there's no guarantee that this script is
    # running as root. And there's no guarantee that "sudo sysctl" will
    # actually work but, at least, it will get the attention of
    # the developer.
    set +e
    ptrace_scope=$(sudo sysctl -n kernel.yama.ptrace_scope)
    res=$?
    ptrace_scope_config=""
    # If sysctl above failed (e.g., because this is a non-privileged
    # jenkins container), then do not attempt to change system configuration.
    if [ $res = 0 ] && [ "$ptrace_scope" != 0 ]; then
	set -e
	sudo sysctl -we kernel.yama.ptrace_scope=0
	ptrace_scope_config="kernel.yama.ptrace_scope = 0"
    fi

    set +e
    core_pattern=$(sudo sysctl -n kernel.core_pattern)
    res=$?
    core_pattern_config=""
    # If sysctl above failed (e.g., because this is a non-privileged
    # jenkins container), then do not attempt to change system configuration.
    if [ $res = 0 ] && echo "$core_pattern" | grep -q -v -e '^core'; then
	set -e
	sudo sysctl -we kernel.core_pattern=core.%e.%p.%h.%t
	core_pattern_config="
# %e: executable filename
# %p: PID
# %h: hostname
# %t: UNIX time of dump
kernel.core_pattern = core.%e.%p.%h.%t"
    fi

    # If there's an Apport service running, then it will restore its own
    # kernel.core_pattern on reboot, irrespective of the sysctl configuration
    # file. Therefore, we need to disable it.
    if systemctl --quiet is-active apport.service; then
	systemctl stop apport.service
	systemctl disable apport.service
    fi

    # setup apparmor kernel config : restrict_unprivileged_userns
    set +e
    apparmor_userns=$(sudo sysctl -n kernel.apparmor_restrict_unprivileged_userns)
    res=$?
    apparmor_userns_config=""
    if [ $res = 0 ] && [ "$apparmor_userns" != 0 ]; then
	set -e
	sudo sysctl -we kernel.apparmor_restrict_unprivileged_userns=0
	apparmor_userns_config="kernel.apparmor_restrict_unprivileged_userns = 0"
    fi

    # setup apparmor kernel config : restrict_unprivileged_unconfined
    set +e
    apparmor_unconfined=$(sudo sysctl -n kernel.apparmor_restrict_unprivileged_unconfined)
    res=$?
    apparmor_unconfined_config=""
    if [ $res = 0 ] && [ "$apparmor_unconfined" != 0 ]; then
	set -e
	sudo sysctl -we kernel.apparmor_restrict_unprivileged_unconfined=0
	apparmor_unconfined_config="kernel.apparmor_restrict_unprivileged_unconfined = 0"
    fi

    # If any sysctl change was made, persist it in a configuration file.
    if [ -n "$ptrace_scope_config" ] || [ -n "$core_pattern_config" ] || \
       [ -n "$apparmor_userns_config" ] || [ -n "$apparmor_unconfined_config" ]; then
	sudo tee /etc/sysctl.d/90-tcwg.conf > /dev/null <<EOF
$ptrace_scope_config
$core_pattern_config
$apparmor_userns_config
$apparmor_unconfined_config
EOF
	sudo chmod 644 /etc/sysctl.d/90-tcwg.conf
    fi
)

$DOCKER run -dt --name=$node --network host --restart=unless-stopped $mounts \
	--memory=$memlimit --pids-limit=5000 $workaround $additional_options \
	$image "$group" "$node" "$jenkins_server"
