#!/bin/sh

# whatap_infra service
#


export WHATAP_HOME=/usr/whatap/infra/conf
PATH=/sbin:/usr/sbin:/usr/bin:/usr/lib/netsvc:/usr/lib/netsvc/yp
export PATH

BIN=/usr/whatap/infra/whatap_infrad
CONF=/usr/whatap/infra/conf/whatap.conf
PID=/usr/whatap/infra/conf/whatap.pid


whatapinfra_start() {
    export WHATAP_HOME=/usr/whatap/infra/conf
    PATH=/sbin:/usr/sbin:/usr/bin:/usr/lib/netsvc:/usr/lib/netsvc/yp
    export PATH

    BIN=/usr/whatap/infra/whatap_infrad
    CONF=/usr/whatap/infra/conf/whatap.conf
    PID=/usr/whatap/infra/conf/whatap.pid
    if [ -f "$PID" ]; then
        OLD_PID=$(cat "$PID" 2>/dev/null | tr -d ' \t\r\n')

        # SERVER-1901: compare the recorded pid exactly against the pids of THIS
        # install. The previous check grepped the whole ps line for the pid as a
        # substring, so an unrelated column (ppid, a time field, a longer pid)
        # could satisfy it; and unlike stop()/term() it skipped the tr -d above,
        # so a stray CR in the pid file made the match fail. Both error
        # directions matter now that a sweep follows: a false "running" blocks a
        # legitimate start, and a false "not running" drops a healthy master
        # into the sweep below and restarts it for no reason.
        if is_valid_pid "$OLD_PID" \
            && whatap_infrad_pids | grep -x "$OLD_PID" >/dev/null 2>&1; then
            echo "whatap-infra already running (PID $OLD_PID)."
            exit 1
        else
            echo "Stale PID file found (PID $OLD_PID). Removing $PID"
            rm -f "$PID"
        fi
    fi


    if [ ! -r $CONF ]; then
        echo "$CONF does not appear to exist. Abort."
        exit 1
    fi

    if [ -s $PID ]; then
        echo "whatap-infra appears to already be running?\nTo override, you could delete pid file like following.\n\nrm -f $PID \n"
        exit 1
    fi

    # SERVER-1901: reaching here means no tracked master is running, yet workers
    # orphaned by an earlier crash / kill -9 / stop-without-cleanup may still be
    # alive - stop() removes the pid file, so the checks above cannot see them.
    # Starting on top of those leftovers is exactly how the orphan count grew one
    # process per restart cycle on HP-UX. Sweep before spawning a new master.
    # (Defined further down; sh resolves functions at call time.)
    kill_whatap_infrad_processes

    echo "Starting whatap-infra daemon..."
    if [ -x $BIN ]; then
        $BIN
    fi
}


# SERVER-1901: reject anything that is not a positive decimal pid before it
# reaches kill. An empty / corrupt pid file (e.g. "-1" or garbage) would
# otherwise turn "kill" into a broadcast (kill -1 signals every process the
# user owns) or signal a recycled, unrelated pid.
is_valid_pid() {
    case "$1" in
        ''|*[!0-9]*) return 1 ;;
        *) [ "$1" -gt 0 ] ;;
    esac
}

# SERVER-1901: the pid file only records the master process. The master forks a
# "foreground" worker child whose pid is never tracked, and the service scripts
# used to stop the master with SIGQUIT - a signal the agent does not register a
# handler for - so it died via the default action WITHOUT running its child-kill
# logic, and dumped core on the way out. Worker children orphaned this way (or
# by a kill -9 on the master) linger and accumulate across restart/patch/
# reinstall, which is the zombie/orphan buildup observed on HP-UX. We now send
# SIGTERM, which the agent does handle.
#
# List the pids of every whatap_infrad process belonging to THIS install. We
# intentionally do NOT scope by parent pid: orphans from earlier cycles have
# already been reparented to init (ppid=1), so ppid matching would miss exactly
# the leaked workers we must reap.
#
# The match is an exact whitespace-delimited token equal to the absolute $BIN
# path - not a bare "whatap_infrad" name and not a substring:
#   - the install root is NOT fixed: install.sh rewrites the default prefix in
#     this very file to $INSTALL_ROOT at install time (which is also why this
#     comment must not spell the default prefix out - the sed would rewrite the
#     comment too and invert its meaning). A bare-name match therefore also hits
#     agents of a different install root on the same host, and any unrelated
#     process that merely mentions the binary (an editor, a tail, a wrapper);
#   - a substring match on the full path still hits siblings that share it as a
#     prefix - the patch script leaves "whatap_infrad.bak-<timestamp>" files
#     right beside the binary, so this is not hypothetical.
# Both were reproduced on HP-UX. The master runs as "<python> $BIN" and its
# worker child as "<python> $BIN foreground" (the child inherits argv[0] from
# the master), so an exact token match covers every process we own and nothing
# we do not. awk's own argv holds "bin=<path>", never a bare "<path>" token, so
# the matcher cannot select itself.
whatap_infrad_pids() {
    ps -ef 2>/dev/null | awk -v bin="$BIN" '
        { for (i = 1; i <= NF; i++) if ($i == bin) { print $2; break } }'
}

# Poll for the processes to drain rather than sleeping a fixed amount. The
# agent's SIGTERM handler kills its child and then sleeps 5s before exiting, so
# a shorter wait would SIGKILL the master mid-shutdown and lose its termination
# log; older/slower boxes need more headroom still. Polling keeps the common
# case near 1s while allowing up to 10s before we escalate to SIGKILL.
wait_whatap_infrad_exit() {
    waited=0
    while [ "$waited" -lt 10 ]; do
        if [ -z "$(whatap_infrad_pids)" ]; then
            return 0
        fi
        sleep 1
        waited=$((waited + 1))
    done
    return 1
}

# Terminate every whatap_infrad process of this install. Signal masters first so
# their own handler can kill their live child, then force-kill any survivors.
# This also guarantees the master itself is gone before we drop the pid file, so
# a following start cannot race into a second instance.
kill_whatap_infrad_processes() {
    for p in $(whatap_infrad_pids); do
        is_valid_pid "$p" || continue
        echo "Stopping whatap-infra process (PID $p)"
        kill -TERM "$p" 2>/dev/null
    done

    if wait_whatap_infrad_exit; then
        return 0
    fi

    for p in $(whatap_infrad_pids); do
        is_valid_pid "$p" || continue
        echo "whatap-infra process (PID $p) still alive, sending SIGKILL"
        kill -9 "$p" 2>/dev/null
    done
}


# SERVER-1901: stop/term no longer signal the pid from the pid file directly.
# That kill was unverified - unlike start(), it never checked that the recorded
# pid still belongs to this install, so a stale pid file whose number had been
# recycled meant signalling an unrelated process. It was also redundant:
# kill_whatap_infrad_processes() already signals every process matching $BIN,
# which includes the master, and doing both delivered a second SIGTERM that
# could re-enter the agent's handler while it was still shutting down.
whatapinfra_term() {
    echo "Shutdown whatap-infra quickly..."
    kill_whatap_infrad_processes
    rm -f $PID
}


whatapinfra_stop() {
    echo "Shutdown whatap-infra gracefully..."
    kill_whatap_infrad_processes
    rm -f $PID
}


whatapinfra_restart() {
    whatapinfra_stop
    sleep 3
    whatapinfra_start
}


case "$1" in
    start)
        whatapinfra_start
        ;;
    stop)
        whatapinfra_stop
        ;;
    # SERVER-1901: whatapinfra_term existed but had no case branch, so it was
    # unreachable. Wire it up rather than leave dead code behind.
    term)
        whatapinfra_term
        ;;
    restart)
        whatapinfra_restart
        ;;
    *)
        echo "usage: `basename $0` {start|stop|term|restart}"
esac

