#!/bin/sh
# Reverts configuration if config-reverter flag is not deleted within timeout.
# Version: v2
#
# Usage:
# 1. Before configuring device, enable setup revert on timeout, execute command: 
#    touch /var/local/telem/config-reverter.flag
# 2. After configuring and regaining access, cancel setup revert, execute command:
#    rm /var/local/telem/config-reverter.flag
#    Or connect any GWS and wait for automatic cancel.
#
# shellcheck disable=SC3057

VAR_LOCAL_TELEM_DIR="/var/local/telem"
USR_LOCAL_ETC_TELEM_DIR="/usr/local/etc/telem"
FLAG_NAME='config-reverter'
FLAG_FILE="${VAR_LOCAL_TELEM_DIR}/${FLAG_NAME}.flag"
STAT_FILE="/root/${FLAG_NAME}.stat"

WARN_FILE="${VAR_LOCAL_TELEM_DIR}/errors/config-reverted"
SHA512_FILE="${USR_LOCAL_ETC_TELEM_DIR}/setup.sha512"

# Defaults
REVERT_TIMEOUT=300  # default 5 minutes in seconds
REVERT_REBOOTS=5    # default device reseting/rebooting count of 5 times

log  () { logger -p 'user.info' -t 'config-reverter' "$@"; }
logw () { logger -p 'user.warn' -t 'config-reverter' "$@"; }

cleanupWarn() {
    # Remove warning if another setup is used.
    cmp -s "${WARN_FILE}" "${SHA512_FILE}" || rm -f "${WARN_FILE}" 2>/dev/null
}

cleanup() {
    # Remove reverter specific files.
    rm -f "${STAT_FILE}" "${FLAG_FILE}" 2>/dev/null
}

gwsOK() {
    # Check if there are GWS processes that has runtime at least 29 seconds.
    ps -Awwo etimes,pid,stat,cmd \
    | awk '
        BEGIN{r=1}
        !/ echo Test[a-zA-Z0-9]{16}/{next}  # Avoid not GWS
        $1~/^\s*0*[012]?.$/{next}           # Avoid less than 29 seconds
        $3~/X|Z/{next}                      # Avoid dead and zombies
        {r=0;exit r}
        END{exit r}
    '
}

GWSOKCANCELLED=false
gwsOKCancel() {
    if gwsOK; then
        log "GWS reconnection lasted 30+ seconds, cancel revert."
        rm -f "${FLAG_FILE}" 2>/dev/null
        GWSOKCANCELLED=true
    fi
}

reverter() {
    cleanupWarn
    # Reversion enabled if flag is set
    if [ -r "${FLAG_FILE}" ]; then
        # Flag may contain longer timeout value
        read -r REVERT_TIMEOUT 2>/dev/null < "${FLAG_FILE}"
        # Set to 5 minutes if invalid or less than 5 minutes
        test "${REVERT_TIMEOUT}" -ge "300" 2>/dev/null || REVERT_TIMEOUT=300
    else
        # Reversion not needed, do cleanup
        cleanup
        # No need to run the rest of the script without flag file
        exit 0
    fi

    CURRENT_SETUP="/usr/local/etc/telem/setup.tar.xz"
    DO_REVERT=false
    REBOOTS=0
    WAITED=0

    if [ ! -f "${CURRENT_SETUP}.0" ]; then
        log "There is no backup setup, cancel revert."
        cleanup
        exit
    fi

    # Load saved state
    if [ -r "${STAT_FILE}" ]; then
        # shellcheck source=/dev/null
        . "${STAT_FILE}"
    fi

    # Script should be started once per boot
    echo "REBOOTS=$((REBOOTS+=1))" >> "${STAT_FILE}"

    # Reversion condition checks
    if [ "${WAITED}" -ge "${REVERT_TIMEOUT}" ]; then
        logw "Reversion flag still present after ${REVERT_TIMEOUT} seconds."
        DO_REVERT=true
    elif [ "${REBOOTS}" -ge "${REVERT_REBOOTS}" ]; then
        logw "Reversion flag still present after ${REVERT_REBOOTS} reboots."
        DO_REVERT=true
    fi

    # Check if we need to revert
    if ${DO_REVERT}; then
        # Remove stats and flag files, if it fails, then filesystem might be read-only
        if ! rm -f "${STAT_FILE}" "${FLAG_FILE}" 2>/dev/null; then
            log "Reversion failed, can't modify files."
            exit
        fi

        sha1="$(sha256sum "${CURRENT_SETUP}")"
        sha2="$(sha256sum "${CURRENT_SETUP}.0")"
        logw "Reverting setup from '${sha1::8}' to '${sha2::8}'"

        sha512sum "${CURRENT_SETUP}.0" | awk '{print $1}' > "${WARN_FILE}"
        chown martem "${WARN_FILE}"

        # Revert setup
        /etc/init.d/S11telem-config old 0
        # Alternative could be: "gwconf -a 0"
        # But this is not compatible with older FW

        exit
    fi

    (
        # This part will sleep in background and revert setup in clean state after reboot.
        # Clean state: where no other process will be interupting/altering reversion

        if [ -f "${FLAG_FILE}" ]; then
            log "Start, reboots ${REBOOTS} of ${REVERT_REBOOTS}, waited ${WAITED}s of ${REVERT_TIMEOUT}s"
        fi

        WAITINT=5
        while [ -f "${FLAG_FILE}" ] && [ "${WAITED}" -lt "${REVERT_TIMEOUT}" ]; do
            sleep "${WAITINT}"
            echo "WAITED=$((WAITED+=WAITINT))" >> "${STAT_FILE}" 2>/dev/null || exit
            gwsOKCancel
        done

        if [ -f "${FLAG_FILE}" ]; then
            log "Reversion flag still present after ${REVERT_TIMEOUT} seconds."
            echo "DO_REVERT=true" >> "${STAT_FILE}" || exit
            log "Reverting configuration after reboot"
            reboot
            # Reboot can take long time and flag can be still deleted by user!
        else
            # Flag was removed, do cleanup
            $GWSOKCANCELLED || log "Reversion flag removed, cancel revert."
            cleanup
        fi
    ) &
}

start() {
    reverter
}

stop() {
    pkill -f config-reverter
}

cancel() {
    if [ -f "${FLAG_FILE}" ]; then
        echo "rm ${FLAG_FILE}"
        cleanup
    fi
    [ -f "${FLAG_FILE}" ] && echo "FAIL" || echo "OK"
}

setFlag() {
    echo "touch ${FLAG_FILE}"
    touch "${FLAG_FILE}" \
    && chown martem "${FLAG_FILE}"
    [ -f "${FLAG_FILE}" ] && echo "OK" || echo "FAIL"
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    cancel)
        cancel
        ;;
    enable|touch)
        setFlag
        ;;
    *)
        echo "Usage: $0 {start|stop}"
        exit 1
esac
