#!/bin/bash
#
# This file is part of TALER
# Copyright (C) 2023, 2024 Taler Systems SA
#
# TALER is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3, or
# (at your option) any later version.
#
# TALER is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with TALER; see the file COPYING.  If not, see
# <http://www.gnu.org/licenses/>
#
# Author: Christian Grothoff
#
# Script to test for network connectivity, and if
# successful launch some "main" application. If
# the network goes down, stop the "main" application
# and show an error message.
#

set -eu

EXIT_STATUS=0
CHILD_PID=-1
ERROR_PID=-1

# Exit, with error message (hard failure)
function exit_fail() {
    echo " FAILURE:" "$@" >&2
    EXIT_STATUS=1
    exit "$EXIT_STATUS"
}

CONF="$HOME/.config/taler.conf"

# Parse command-line options
while getopts ':c:f:h' OPTION; do
    case "$OPTION" in
        c)
            CONF="$OPTARG"
            ;;
        f)
            FAIL_PROG="$OPTARG"
            ;;
        h)
            echo 'Supported options:'
            # shellcheck disable=SC2016
            echo '  -c $CONF     -- set configuration'
            # shellcheck disable=SC2016
            echo '  -f $CMD      -- command to run to display failures'
            ;;
        ?)
            exit_fail "Unrecognized command line option"
            ;;
    esac
done
shift $((OPTIND - 1))

WORKER="$@"

dig -h > /dev/null || exit_fail "'dig' is required"

if [[ ${FAIL_PROG:-} = "" ]]
then
    FAIL_PROG=$(taler-config -c "$CONF" -s taler-mdb -o "FAIL_HELPER")
fi

# Cleanup to run whenever we exit
function cleanup()
{
    echo "network-check terminating!" >&2

    if [ "$ERROR_PID" != "-1" ]
    then
        kill -TERM "$ERROR_PID"
        wait "$ERROR_PID"
        ERROR_PID="-1"
    fi
    if [ "$CHILD_PID" != "-1" ]
    then
        kill -TERM "$CHILD_PID"
        wait "$CHILD_PID"
        CHILD_PID="-1"
    fi
    for n in $(jobs -p)
    do
        kill "$n" 2> /dev/null || true
    done
    wait
    exit "$EXIT_STATUS"
}

# Function called with the short-hand of a detected
# Failure. Stop our child (if any) and show it.
function show_failure()
{
    echo "Showing failure $1"
    if [ "$ERROR_PID" != "-1" ]
    then
        kill -TERM "$ERROR_PID"
        wait
        ERROR_PID="-1"
    fi
    if [ "$CHILD_PID" != "-1" ]
    then
        kill -TERM "$CHILD_PID"
        wait
        CHILD_PID="-1"
    fi
    ${FAIL_PROG} "$1" >&2 &
    ERROR_PID=$!
}


# Install cleanup handler (except for kill -9)
trap cleanup EXIT

# shellcheck disable=SC2120
function check_network()
{
    DEF_BACKEND=$(taler-config -c "$CONF" -s "taler-mdb" -o "BACKEND_BASE_URL")
    BACKEND_HOSTNAME=$(echo "$DEF_BACKEND" | awk -F/ '{print $3}')
    BACKEND_AUTH=$(taler-config -c "$CONF" -s "taler-mdb" -o "BACKEND_AUTHORIZATION")

    DNS_HOST=$(grep nameserver /etc/resolv.conf | grep -v '^#' | head -n1 | awk '{print $2}')
    if ! ping -c1 "$DNS_HOST" &> /dev/null
    then
        show_failure no-ip
        return
    fi
    if ! dig "$BACKEND_HOSTNAME" &> /dev/null
    then
        show_failure backend-dns-resolution-failure
        return
    fi
    if ! ping -c1 "$BACKEND_HOSTNAME" &> /dev/null
    then
        show_failure backend-unreachable
        return
    fi

    if echo "$DEF_BACKEND" | grep "https://" > /dev/null
    then
        if ! wget --no-check-certificate "${DEF_BACKEND}config" -O /dev/null &> /dev/null
        then
            show_failure backend-no-webserver
            return
        fi
        if ! wget "${DEF_BACKEND}config" -O /dev/null &> /dev/null
        then
            show_failure backend-x509-cert-bad
            return
        fi
    else
        if ! wget "${DEF_BACKEND}config" -O /dev/null &> /dev/null
        then
            show_failure backend-no-webserver
            return
        fi
    fi

    HAVE_PRODUCT=0
    for PS in $(taler-config -c "$CONF" -S | grep "product-" | head -n1)
    do
        URL=$(taler-config -c "$CONF" -s "$PS" -o "INSTANCE" 2> /dev/null || echo "$DEF_BACKEND")
        AUTH=$(taler-config -c "$CONF" -s "$PS" -o "BACKEND_AUTHORIZATION" 2> /dev/null || echo "$BACKEND_AUTH")

        if ! wget --header "Authorization: $AUTH" "${URL}private/orders" -O /dev/null &> /dev/null
        then
            echo "Failed to access backend for product '$PS'" >&2
        else
            HAVE_PRODUCT=1
        fi
    done
    if [ "$HAVE_PRODUCT" = 0 ]
    then
        show_failure backend-auth-failure
        return
    fi

    echo "Network OK"
    if [ "$ERROR_PID" != "-1" ]
    then
        kill -TERM "$ERROR_PID"
        wait
        ERROR_PID="-1"
    fi
    
    if [ "$CHILD_PID" = "-1" ]
    then
        echo "Starting child using $WORKER"
        # shellcheck disable=SC2068
        $WORKER &
        CHILD_PID=$!
    fi
}


# Check network status
while true
do
    echo "Checking network status"
    check_network
    sleep 30
    if ! ps "$ERROR_PID" &> /dev/null
    then
        wait "${ERROR_PID}"
        ERROR_PID="-1"
    fi
    if ! ps "$CHILD_PID" &> /dev/null
    then
        wait "${CHILD_PID}"
        CHILD_PID="-1"
    fi
done
