#!/bin/sh
## @file
# VirtualBox Validation Kit - TestBoxScript service init script.
#

#
# Copyright (C) 2006-2020 Oracle Corporation
#
# This file is part of VirtualBox Open Source Edition (OSE), as
# available from http://www.virtualbox.org. This file is free software;
# you can redistribute it and/or modify it under the terms of the GNU
# General Public License (GPL) as published by the Free Software
# Foundation, in version 2 as it comes in the "COPYING" file of the
# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
#
# The contents of this file may alternatively be used under the terms
# of the Common Development and Distribution License Version 1.0
# (CDDL) only, as it comes in the "COPYING.CDDL" file of the
# VirtualBox OSE distribution, in which case the provisions of the
# CDDL are applicable instead of those of the GPL.
#
# You may elect to license modified versions of this file under the
# terms and conditions of either the GPL or the CDDL or both.
#

# chkconfig: 35 35 65
# description: TestBoxScript service
#
### BEGIN INIT INFO
# Provides:       testboxscript-service
# Required-Start: $network
# Required-Stop:
# Default-Start:  2 3 4 5
# Default-Stop:   0 1 6
# Description:    TestBoxScript service
### END INIT INFO


PATH=$PATH:/bin:/sbin:/usr/sbin

#
# Load config and set up defaults.
#
service_name="testboxscript"

[ -r /etc/default/${service_name} ] && . /etc/default/${service_name}
[ -r /etc/conf.d/${service_name}  ] && . /etc/conf.d/${service_name}

if [ -z "${TESTBOXSCRIPT_DIR}" ]; then
    TESTBOXSCRIPT_DIR="/opt/testboxscript"
fi
if [ -z "${TESTBOXSCRIPT_USER}" ]; then
    TESTBOXSCRIPT_USER="vbox"
fi
binary="${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript.py"
binary_real="${TESTBOXSCRIPT_DIR}/testboxscript/testboxscript_real.py"



#
# Detect and abstract distro
#
[ -f /etc/debian_release -a -f /lib/lsb/init-functions ] || NOLSB=yes

system=unknown
if [ -f /etc/redhat-release ]; then
    system=redhat
    PIDFILE="/var/run/${service_name}-service.pid"
elif [ -f /etc/SuSE-release ]; then
    system=suse
    PIDFILE="/var/lock/subsys/${service_name}-service"
elif [ -f /etc/debian_version ]; then
    system=debian
    PIDFILE="/var/run/${service_name}-service"
elif [ -f /etc/gentoo-release ]; then
    system=gentoo
    PIDFILE="/var/run/${service_name}-service"
elif [ -f /etc/arch-release ]; then
     system=arch
     PIDFILE="/var/run/${service_name}-service"
elif [ -f /etc/slackware-version ]; then
    system=slackware
    PIDFILE="/var/run/${service_name}-service"
elif [ -f /etc/lfs-release ]; then
    system=lfs
    PIDFILE="/var/run/${service_name}-service.pid"
else
    system=other
    if [ -d /var/run -a -w /var/run ]; then
        PIDFILE="/var/run/${service_name}-service"
    fi
fi


#
# Generic implementation.
#

## Query daemon status.
# $1 = daemon-user; $2 = binary name
# returns 0 if running, 1 if started but no longer running, 3 if not started.
# When 0 is return the pid variable contains a list of relevant pids.
my_query_status() {
    a_USER="$1";
    a_BINARY="$2";
    pid="";
    if [ -f "${PIDFILE}" -a -s "${PIDFILE}" ]; then
        MY_LINE="";
        read MY_LINE < "${PIDFILE}";
        for MY_PID in `echo $MY_LINE | sed -e 's/[^0123456789 ]/ /g'`;
        do
            if [ "`stat -c '%U' /proc/$MY_PID 2> /dev/null `" = "$a_USER" ]; then
                pid="${pid} ${MY_PID}";
            fi
        done
        if [ -n "${pid}" ]; then
            RETVAL=0;
        else
            RETVAL=1;
        fi
    else
        RETVAL=3
    fi
    return $RETVAL;
}

## Starts detached daeamon in screen or tmux.
# $1 = daemon-user; $2+ = daemon and its arguments
my_start_daemon() {
    a_USER="$1"
    shift
    if touch "${PIDFILE}" && chown "${a_USER}" -- "${PIDFILE}"; then
        ARGS=""
        while [ $# -gt 0 ];
        do
            ARGS="$ARGS '$1'";
            shift
        done
        ARGS="$ARGS --pidfile '$PIDFILE'";
        if type screen > /dev/null; then
            su - "${a_USER}" -c "screen -S ${service_name} -d -m ${ARGS}";
        elif type tmux > /dev/null; then
            su - "${a_USER}" -c "tmux new-session -AdD -s ${service_name} ${ARGS}";
        else
            echo "Need screen or tmux, please install!"
            exit 1
        fi
        RETVAL=$?;
        if [ $RETVAL -eq 0 ]; then
            sleep 0.6;
            if [ ! -s "$PIDFILE" ]; then sleep 1; fi
            if [ ! -s "$PIDFILE" ]; then sleep 2; fi
            if [ ! -s "$PIDFILE" ]; then sleep 3; fi
            if [ -s "$PIDFILE" ]; then
                RETVAL=0;
            else
                RETVAL=1;
            fi
        else
            fail_msg "su failed with exit code $RETVAL";
        fi
    else
        fail_msg "Failed to create pid file and change it's ownership to ${a_USER}."
        RETVAL=1;
    fi
    return $RETVAL;
}

## Stops the daemon.
# $1 = daemon-user; $2 = binary name
my_stop_daemon() {
    a_USER="$1";
    a_BINARY="$2";
    my_query_status "$a_USER" "$a_BINARY"
    RETVAL=$?
    if [ $RETVAL -eq 0 -a -n "$pid" ]; then
        kill $pid;
    fi
    sleep 0.6
    if my_query_status "$a_USER" "$a_BINARY"; then sleep 1; fi
    if my_query_status "$a_USER" "$a_BINARY"; then sleep 2; fi
    if my_query_status "$a_USER" "$a_BINARY"; then sleep 3; fi
    if ! my_query_status "$a_USER" "$a_BINARY"; then
        rm -f -- "${PIDFILE}"
        return 0;
    fi
    return 1;
}

if [ -z "$NOLSB" ]; then
    . /lib/lsb/init-functions
    fail_msg() {
        echo ""
        log_failure_msg "$1"
    }
    succ_msg() {
        log_success_msg " done."
    }
    begin_msg() {
        log_daemon_msg "$@"
    }
else
    fail_msg() {
        echo " ...fail!"
        echo "$@"
    }
    succ_msg() {
        echo " ...done."
    }
    begin_msg() {
        echo -n "$1"
    }
fi

#
# System specific overrides.
#

if [ "$system" = "redhat" ]; then
    . /etc/init.d/functions
    if [ -n "$NOLSB" ]; then
        fail_msg() {
            echo_failure
            echo
        }
        succ_msg() {
            echo_success
            echo
        }
        begin_msg() {
            echo -n "$1"
        }
    fi
fi

if [ "$system" = "suse" ]; then
    . /etc/rc.status
    if [ -n "$NOLSB" ]; then
        fail_msg() {
            rc_failed 1
            rc_status -v
        }
        succ_msg() {
            rc_reset
            rc_status -v
        }
        begin_msg() {
            echo -n "$1"
        }
    fi
fi

if [ "$system" = "debian" ]; then
    # Share my_start_daemon and my_stop_daemon with gentoo
    if [ -n "$NOLSB" ]; then
        fail_msg() {
            echo " ...fail!"
        }
        succ_msg() {
            echo " ...done."
        }
        begin_msg() {
            echo -n "$1"
       }
    fi
fi

if [ "$system" = "gentoo" ]; then
    if [ -f /sbin/functions.sh ]; then
        . /sbin/functions.sh
    elif [ -f /etc/init.d/functions.sh ]; then
        . /etc/init.d/functions.sh
    fi
    # Share my_start_daemon and my_stop_daemon with debian.
    if [ -n "$NOLSB" ]; then
        if [ "`which $0`" = "/sbin/rc" ]; then
            shift
        fi
    fi
fi

if [ "$system" = "debian" -o  "$system" = "gentoo" ]; then
    #my_start_daemon() {
    #    usr="$1"
    #    shift
    #    bin="$1"
    #    shift
    #    echo usr=$usr
    #    start-stop-daemon --start --background  --pidfile "${PIDFILE}" --make-pidfile --chuid "${usr}" --user "${usr}" \
    #        --exec $bin -- $@
    #}
    my_stop_daemon() {
        a_USER="$1"
        a_BINARY="$2"
        start-stop-daemon --stop --user "${a_USER}" --pidfile "${PIDFILE}"
        RETVAL=$?
        rm -f "${PIDFILE}"
        return $RETVAL
    }
fi

if [ "$system" = "arch" ]; then
    USECOLOR=yes
    . /etc/rc.d/functions
    if [ -n "$NOLSB" ]; then
        fail_msg() {
            stat_fail
        }
        succ_msg() {
            stat_done
        }
        begin_msg() {
            stat_busy "$1"
        }
    fi
fi

if [ "$system" = "lfs" ]; then
    . /etc/rc.d/init.d/functions
    if [ -n "$NOLSB" ]; then
        fail_msg() {
            echo_failure
        }
        succ_msg() {
            echo_ok
        }
        begin_msg() {
            echo $1
        }
    fi
fi

#
# Implement the actions.
#
check_single_user() {
    if [ -n "$2" ]; then
        fail_msg "TESTBOXSCRIPT_USER must not contain multiple users!"
        exit 1
    fi
}

#
# Open ports at the firewall:
#   6000..6100 / TCP for VRDP
#   5000..5032 / TCP for netperf
#   5000..5032 / UDP for netperf
#
set_iptables() {
    if [ -x /sbin/iptables ]; then
        I="/sbin/iptables -j ACCEPT -A INPUT -m state --state NEW"
        if ! /sbin/iptables -L INPUT | grep -q "testsuite vrdp"; then
            $I -m tcp -p tcp --dport 6000:6100 -m comment --comment "testsuite vrdp"
        fi
        if ! /sbin/iptables -L INPUT | grep -q "testsuite perftcp"; then
            $I -m tcp -p tcp --dport 5000:5032 -m comment --comment "testsuite perftcp"
        fi
        if ! /sbin/iptables -L INPUT | grep -q "testsuite perfudp"; then
            $I -m udp -p udp --dport 5000:5032 -m comment --comment "testsuite perfudp"
        fi
    fi
}


start() {
    if [ ! -f "${PIDFILE}" ]; then
        begin_msg "Starting TestBoxScript";

        #
        # Verify config and installation.
        #
        if [ ! -d "$TESTBOXSCRIPT_DIR"  -o  ! -r "$binary"  -o  ! -r "$binary_real" ]; then
            fail_msg "Cannot find TestBoxScript installation under '$TESTBOXSCRIPT_DIR'!"
            exit 0;
        fi
        ## @todo check ownership (for upgrade purposes)
        check_single_user $TESTBOXSCRIPT_USER

        #
        # Open some ports in the firewall
        # Allows to access VMs remotely by VRDP, netperf
        #
        set_iptables

        #
        # Set execute bits to make installation (unzip) easier.
        #
        chmod a+x  > /dev/null 2>&1  \
            "${binary}" \
            "${binary_real}" \
            "${TESTBOXSCRIPT_DIR}/linux/amd64/TestBoxHelper" \
            "${TESTBOXSCRIPT_DIR}/linux/x86/TestBoxHelper"

        #
        # Start the daemon as the specified user.
        #
        PARAMS=""
        if [ "${TESTBOXSCRIPT_HWVIRT}"        = "yes" ]; then PARAMS="${PARAMS} --hwvirt"; fi
        if [ "${TESTBOXSCRIPT_HWVIRT}"        = "no"  ]; then PARAMS="${PARAMS} --no-hwvirt"; fi
        if [ "${TESTBOXSCRIPT_NESTED_PAGING}" = "yes" ]; then PARAMS="${PARAMS} --nested-paging"; fi
        if [ "${TESTBOXSCRIPT_NESTED_PAGING}" = "no"  ]; then PARAMS="${PARAMS} --no-nested-paging"; fi
        if [ "${TESTBOXSCRIPT_IOMMU}"         = "yes" ]; then PARAMS="${PARAMS} --io-mmu"; fi
        if [ "${TESTBOXSCRIPT_IOMMU}"         = "no"  ]; then PARAMS="${PARAMS} --no-io-mmu"; fi
        if [ -n "${TESTBOXSCRIPT_SYSTEM_UUID}"     ]; then PARAMS="${PARAMS} --system-uuid '${TESTBOXSCRIPT_SYSTEM_UUID}'"; fi
        if [ -n "${TESTBOXSCRIPT_TEST_MANAGER}"    ]; then PARAMS="${PARAMS} --test-manager '${TESTBOXSCRIPT_TEST_MANAGER}'"; fi
        if [ -n "${TESTBOXSCRIPT_SCRATCH_ROOT}"    ]; then PARAMS="${PARAMS} --scratch-root '${TESTBOXSCRIPT_SCRATCH_ROOT}'"; fi

        if [ -n "${TESTBOXSCRIPT_BUILDS_PATH}"     ]; then PARAMS="${PARAMS} --builds-path '${TESTBOXSCRIPT_BUILDS_PATH}'"; fi
        if [ -n "${TESTBOXSCRIPT_BUILDS_TYPE}"     ]; then PARAMS="${PARAMS} --builds-server-type   '${TESTBOXSCRIPT_BUILDS_TYPE}'"; fi
        if [ -n "${TESTBOXSCRIPT_BUILDS_NAME}"     ]; then PARAMS="${PARAMS} --builds-server-name   '${TESTBOXSCRIPT_BUILDS_NAME}'"; fi
        if [ -n "${TESTBOXSCRIPT_BUILDS_SHARE}"    ]; then PARAMS="${PARAMS} --builds-server-share  '${TESTBOXSCRIPT_BUILDS_SHARE}'"; fi
        if [ -n "${TESTBOXSCRIPT_BUILDS_USER}"     ]; then PARAMS="${PARAMS} --builds-server-user   '${TESTBOXSCRIPT_BUILDS_USER}'"; fi
        if [ -n "${TESTBOXSCRIPT_BUILDS_PASSWD}"   ]; then PARAMS="${PARAMS} --builds-server-passwd '${TESTBOXSCRIPT_BUILDS_PASSWD}'"; fi
        if [ -n "${TESTBOXSCRIPT_BUILDS_MOUNTOPT}" ]; then PARAMS="${PARAMS} --builds-server-mountopt '${TESTBOXSCRIPT_BUILDS_MOUNTOPT}'"; fi
        if [ -n "${TESTBOXSCRIPT_TESTRSRC_PATH}"   ]; then PARAMS="${PARAMS} --testrsrc-path '${TESTBOXSCRIPT_TESTRSRC_PATH}'"; fi
        if [ -n "${TESTBOXSCRIPT_TESTRSRC_TYPE}"   ]; then PARAMS="${PARAMS} --testrsrc-server-type   '${TESTBOXSCRIPT_TESTRSRC_TYPE}'"; fi
        if [ -n "${TESTBOXSCRIPT_TESTRSRC_NAME}"   ]; then PARAMS="${PARAMS} --testrsrc-server-name   '${TESTBOXSCRIPT_TESTRSRC_NAME}'"; fi
        if [ -n "${TESTBOXSCRIPT_TESTRSRC_SHARE}"  ]; then PARAMS="${PARAMS} --testrsrc-server-share  '${TESTBOXSCRIPT_TESTRSRC_SHARE}'"; fi
        if [ -n "${TESTBOXSCRIPT_TESTRSRC_USER}"   ]; then PARAMS="${PARAMS} --testrsrc-server-user   '${TESTBOXSCRIPT_TESTRSRC_USER}'"; fi
        if [ -n "${TESTBOXSCRIPT_TESTRSRC_PASSWD}" ]; then PARAMS="${PARAMS} --testrsrc-server-passwd '${TESTBOXSCRIPT_TESTRSRC_PASSWD}'"; fi
        if [ -n "${TESTBOXSCRIPT_TESTRSRC_MOUNTOPT}" ]; then PARAMS="${PARAMS} --testrsrc-server-mountopt '${TESTBOXSCRIPT_TESTRSRC_MOUNTOPT}'"; fi

        if [ -n "${TESTBOXSCRIPT_PYTHON}" ]; then
            my_start_daemon "${TESTBOXSCRIPT_USER}" "${TESTBOXSCRIPT_PYTHON}" "${binary}" ${PARAMS}
        else
            my_start_daemon "${TESTBOXSCRIPT_USER}" "${binary}" ${PARAMS}
        fi
        RETVAL=$?

        if [ $RETVAL -eq 0 ]; then
            succ_msg
        else
            fail_msg
        fi
    else
        succ_msg "Already running."
        RETVAL=0
    fi
    return $RETVAL
}

stop() {
    if [ -f "${PIDFILE}" ]; then
        begin_msg "Stopping TestBoxScript";
        my_stop_daemon "${TESTBOXSCRIPT_USER}" "${binary}"
        RETVAL=$?
        if [ $RETVAL -eq 0 ]; then
            succ_msg
        else
            fail_msg
        fi
    else
        RETVAL=0
    fi
    return $RETVAL
}

restart() {
    stop && sleep 1 && start
}

status() {
    echo -n "Checking for TestBoxScript"
    my_query_status "${TESTBOXSCRIPT_USER}" "${binary}"
    RETVAL=$?
    if [ ${RETVAL} -eq 0 ]; then
        echo " ...running"
    elif [ ${RETVAL} -eq 3 ]; then
        echo " ...stopped"
    elif [ ${RETVAL} -eq 1 ]; then
        echo " ...started but not running"
    else
        echo " ...unknown status '${RETVAL}'"
    fi
}


#
# main().
#
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    force-reload)
        restart
        ;;
    status)
        status
        ;;
    setup)
        ;;
    cleanup)
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
esac

exit $RETVAL

