#! /bin/bash
#
# wrappercheck.sh background command args ... -- command args ...
#
# Wrapper script which runs one command in the background and the
# other in the foreground. Once that second command completes, the
# first one is send a SIGINT+SIGTERM, then a SIGKILL until it terminates.
#
# Overall return code of this script is the return code of the
# foreground command or, if that is 0, the background command.

set -e
set -x
PS4='wrappercheck-$$ line ${LINENO}: '

PIDS=

trap "[ \"$PIDS\" ] && kill -TERM $PIDS" TERM
trap "[ \"$PIDS\" ] && kill -INT $PIDS" INT

DAEMON_LOG=
WAIT_FOR_DAEMON_OUTPUT=
WAIT_FOR_DBUS_DAEMON=

declare -a BACKGROUND
declare -a ENV
# parse parameters
while [ $# -gt 1 ] && [ "$1" != "--" ] ; do
    case "$1" in
        --daemon-log)
            shift
            DAEMON_LOG="$1"
            ;;
        --wait-for-dbus-daemon)
            shift
            WAIT_FOR_DBUS_DAEMON="$1"
            ;;
        --wait-for-daemon-output)
            shift
            WAIT_FOR_DAEMON_OUTPUT="$1"
            ;;
        --daemon-sleep)
            shift
            DAEMON_SLEEP="$1"
            ;;
        *=*)
            ENV[${#ENV[*]}]="$1"
            ;;
        *)
            break
            ;;
    esac
    shift
done
# gather command and its parameters
while [ $# -gt 1 ] && [ "$1" != "--" ] ; do
    BACKGROUND[${#BACKGROUND[*]}]="$1"
    shift
done
shift

if [ "$DAEMON_LOG" ] && [ "$WAIT_FOR_DAEMON_OUTPUT" ]; then
    daemonmatches=$(grep -e "$WAIT_FOR_DAEMON_OUTPUT" "$DAEMON_LOG" | wc -l)
fi

( set +x; echo >&2 "*** starting ${BACKGROUND[0]} as background daemon, output to ${DAEMON_LOG:-stderr}" )
# We need to create a process group so that we can kill all processes started by the sub-shell.
# ${BACKGROUND[*]} is used instead of ${BACKGROUND[@]} because although the later should have
# avoided expansion of words (good!) somehow the quoting got messed up in practice (bad!).
( set -x; exec >>${DAEMON_LOG:-&2} 2>&1; exec env "${ENV[@]}" setsid /bin/bash -c "set -x -o pipefail; ${BACKGROUND[*]} 2>&1 | $(dirname $0)/logger.py" ) &

BACKGROUND_PID=$!
PIDS+="$BACKGROUND_PID"

if [ "$DAEMON_LOG" ] && [ "$WAIT_FOR_DAEMON_OUTPUT" ]; then
    ( set +x; echo >&2 "*** waiting for daemon to write '$WAIT_FOR_DAEMON_OUTPUT' into $DAEMON_LOG"
        while kill -0 $BACKGROUND_PID 2>/dev/null &&
              [ $daemonmatches -eq $(grep -e "$WAIT_FOR_DAEMON_OUTPUT" "$DAEMON_LOG" | wc -l) ]; do
            sleep 1
        done
    )
fi

if [ "$WAIT_FOR_DBUS_DAEMON" ]; then
    ( set +x; echo >&2 "*** waiting for daemon to connect to D-Bus as '$WAIT_FOR_DBUS_DAEMON'"
      while kill -0 $BACKGROUND_PID 2>/dev/null &&
            ! (dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep -q "$WAIT_FOR_DBUS_DAEMON"); do
          sleep 1
      done
    )
fi

if kill -0 $BACKGROUND_PID 2>/dev/null; then
    set +e
    if [ "$DAEMON_SLEEP" ]; then
        ( set +x; echo >&2 "*** 'sleep $DAEMON_SLEEP' for daemon to settle down" )
        sleep $DAEMON_SLEEP
    fi
    (set -x; "$@")
    RET=$?
    set -e
else
    echo >&2 "*** ${BACKGROUND[0]} terminated prematurely"
    RET=1
fi

( set +x; echo >&2 "*** killing and waiting for ${BACKGROUND[0]}" )
if kill -INT -$BACKGROUND_PID >&2 && kill -TERM -$BACKGROUND_PID >&2 || kill -INT $BACKGROUND_PID >&2 && kill -TERM $BACKGROUND_PID >&2; then
    perl -e "sleep(60); kill(9, -$BACKGROUND_PID);" &
    KILL_PID=$!
else
    ps x --forest >&2
    KILL_PID=
fi
set +e
wait $BACKGROUND_PID
SUBRET=$?
case $SUBRET in 0|130|137|139|143) SUBRET=0;; # 130 and 143 indicate that it was killed, probably by us, which is okay
esac
SUBRET=0 # TODO: don't ignore daemon results
if [ "$KILL_PID" ]; then
    msg=$(LC_ALL=C kill -KILL $KILL_PID 2>&1)
    if echo "$msg" | grep -q 'No such process'; then
        # Consider this a success.
        SUBRET=0
    else
        echo "$msg"
    fi
    wait $KILL_PID
fi
set -e
if [ $RET = 0 ]; then
    RET=$SUBRET
fi

exit $RET
