#!/sbin/sh
#
# whatap-infra daemon control script.
# Written for SunOS by whatap

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

whatapinfra_start() {
  # Sanity checks.
  if [ ! -r $CONF ]; then # no config file, exit:
    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

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

whatapinfra_test_conf() {
  echo "Checking configuration for correct syntax and"
  echo "then trying to open files referenced in configuration..."
  $BIN -t -c $CONF
}

whatapinfra_term() {
  echo "Shutdown whatap-infra quickly..."
  kill -TERM `cat $PID`
}

# 이 버전부터 whatap_infrad 가 control-stop 을 이해한다.
# 그 아래 버전에 control-stop 을 주면 에러가 아니라 무한 대기가 된다.
#
# 이 파일은 Solaris 10 의 /sbin/sh(전통적 Bourne sh)에서도 돌아야 한다.
# Bourne sh 는 $( ) 명령치환을 모른다 - 파일을 읽는 단계에서 문법 오류가
# 나서 start/stop 을 포함한 모든 명령이 실패한다. 그래서 backtick 만 쓰고,
# head -n 대신 sed 를 쓴다. (Solaris 11 의 /sbin/sh 는 ksh93 이라 양쪽 다 된다.)
MIN_CONTROL_VERSION=1.5.0

# X.Y.Z 형태의 버전 문자열을 정수로 바꾼다. 각 구성요소를 3자리 폭으로
# 묶는다 - 2자리였을 때 1.4.100 처럼 세 자리 컴포넌트가 앞자리로 넘쳐
# (overflow) 1.5.0 보다 큰 것으로 비교되는 결함이 있었다("판단이 안 서면
# 옛 길로" 가야 하는데 거꾸로 새 길을 타 버린다). 숫자가 아닌 값은 0으로
# 막아 printf 가 stderr 에 에러를 흘리지 않게 한다.
version_num() {
    major=`echo "$1" | cut -d. -f1`
    minor=`echo "$1" | cut -d. -f2`
    patch=`echo "$1" | cut -d. -f3`
    case "$major" in ''|*[!0-9]*) major=0 ;; esac
    case "$minor" in ''|*[!0-9]*) minor=0 ;; esac
    case "$patch" in ''|*[!0-9]*) patch=0 ;; esac
    printf "%d%03d%03d" "$major" "$minor" "$patch"
}

# "$BIN version" 을 타임아웃(5초)을 걸고 실행해 그 출력을 내보낸다.
# 실패 양상이 행이라 타임아웃이 없으면 탐침 자체가 멈춘다. 워처(watcher)의
# 표준출력/에러는 /dev/null 로 돌린다 - 안 그러면 명령치환의 파이프 쓰기측을
# 워처가 쥐고 있어서, 탐침이 곧바로 끝나는 신버전 빠른 경로에서도 워처가
# 잠들어 있는 5초 내내 명령치환이 EOF 를 못 보고 기다린다(실측: 매
# stop/restart 마다 5초가 그냥 더 붙는다). wait 가 끝나는 즉시 워처를 죽여 -
# probe pid 가 재사용된 뒤 엉뚱한 프로세스를 5초 뒤에 kill -9 하는 사고도
# 막고, 워처가 불필요하게 남아있지 않게 한다.
probe_agent_version() {
    "$BIN" version 2>/dev/null &
    probe=$!
    ( sleep 5; kill -9 $probe 2>/dev/null ) >/dev/null 2>&1 &
    watcher=$!
    wait $probe
    rc=$?
    kill -9 $watcher 2>/dev/null
    wait $watcher 2>/dev/null
    return $rc
}

agent_supports_control() {
    [ -x "$BIN" ] || return 1
    # 탐침의 종료 코드를 출력 마지막 줄에 붙여 받는다. 명령치환 대입의 종료
    # 코드에 기대지 않는다 - 구형 Bourne sh 마다 동작이 달라 믿을 수 없다.
    # Solaris 10 Bourne sh 는 자식이 이미 끝난 뒤 다른 백그라운드(워처)를 띄우고
    # wait 하면 그 종료 코드를 잃고 0 을 준다. 실제 바이너리는 python 기동에
    # 수백 ms 가 걸려 워처 기동 시점에 아직 살아 있으므로 코드가 보존되고,
    # 잃더라도 아래에서 숫자 버전 >= MIN_CONTROL_VERSION 이어야만 새 길로 간다.
    # 2>/dev/null: Bourne sh 가 kill 된 워처마다 찍는 "<pid> Killed" 를 막는다.
    out=`probe_agent_version 2>/dev/null; echo "probe_rc=$?"`
    case "$out" in
        *"probe_rc=0") ;;
        *) return 1 ;;
    esac
    # 표시 줄을 떼고 첫 줄만 쓴다 - 버전 뒤에 부가 출력이 붙어도 오탐하지
    # 않는다. 버전 출력에 줄바꿈이 없어 표시와 한 줄로 붙으면 그 줄이 통째로
    # 빠져 빈 값이 되고, 아래에서 옛 길로 간다.
    v=`echo "$out" | sed '$d' | sed -n 1p`
    [ -n "$v" ] || return 1
    cur=`version_num "$v"`
    min=`version_num "$MIN_CONTROL_VERSION"`
    [ "$cur" -ge "$min" ]
}

# 정지의 결과를 삼키지 않는다.
#
# 예전에는 `rm -f $PID` 가 이 함수의 마지막 명령이라 함수의 - 그리고
# `whatap-infra stop` 의 - 종료 코드가 항상 rm 의 것(=0)이었다. ER710007/8
# 로 끝난 정지도 호출자에게는 성공으로 보였고, restart 는 아직 살아 있는
# 에이전트 위에 2세대를 띄웠다. 진실은 상태 파일(control_status.json)에만
# 남는데 그것을 읽는 출하 호출자는 하나도 없다.
#
# 실패했을 때 pid 파일을 남기는 것도 같은 목적이다. whatapinfra_start() 가
# `[ -s $PID ]` 로 "이미 실행 중" 을 판정하므로, 실패한 정지 뒤에 이어지는
# start/restart 가 2세대를 띄우지 못하게 막아 준다.
whatapinfra_stop() {
    echo "Shutdown whatap-infra gracefully..."
    if agent_supports_control; then
        "$BIN" control-stop
        rc=$?
    else
        # 레거시 경로. 판단이 안 서면 옛 길로 간다.
        #
        # 정지시킬 대상이 아예 없으면(pid 파일이 없거나 그 pid 가 이미
        # 죽었으면) 예전 동작 그대로 성공으로 본다 - 그렇게 하지 않으면
        # `cat` 과 `kill` 의 실패가 그대로 새어 나와 "안 떠 있는데 stop"
        # 이 실패로 뒤집힌다. 대상이 살아 있는데 신호를 보내지 못한 경우만
        # 실패다.
        legacy_pid=`cat $PID 2>/dev/null`
        if [ -n "$legacy_pid" ] && kill -0 "$legacy_pid" 2>/dev/null; then
            kill -TERM "$legacy_pid" 2>/dev/null
            rc=$?
        else
            rc=0
        fi
    fi
    if [ $rc -eq 0 ]; then
        rm -f $PID
    else
        echo "whatap-infra stop failed (rc=$rc); keeping $PID"
    fi
    return $rc
}

whatapinfra_reload() {
  echo "Reloading whatap-infra configuration..."
  kill -HUP `cat $PID`
}

whatapinfra_upgrade() {
  echo "Upgrading to the new whatap-infra binary."
  echo "Make sure the whatap-infra binary has been replaced with new one"
  echo "or whatap-infra modules were added/removed."
  kill -USR2 `cat $PID`
  sleep 3
  kill -QUIT `cat $PID.oldbin`
}

whatapinfra_rotate() {
  echo "Rotating whatap-infra logs..."
  kill -USR1 `cat $PID`
}

whatapinfra_restart() {
  whatapinfra_stop
  rc=$?
  # 정지가 실패했으면 기동하지 않는다. 살아 있는 에이전트 위에 2세대를
  # 띄우는 것이 이 브랜치가 막으려는 바로 그 사고다.
  if [ $rc -ne 0 ]; then
    echo "whatap-infra restart aborted: stop failed (rc=$rc)"
    return $rc
  fi
  sleep 3
  whatapinfra_start
}

case "$1" in
  check)
    whatapinfra_test_conf
    ;;
  start)
    whatapinfra_start
    ;;
  term)
    whatapinfra_term
    ;;
  stop)
    whatapinfra_stop
    ;;
  reload)
    whatapinfra_reload
    ;;
  restart)
    whatapinfra_restart
    ;;
  upgrade)
    whatapinfra_upgrade
    ;;
  rotate)
    whatapinfra_rotate
    ;;
  *)
  echo "usage: `basename $0` {check|start|term|stop|reload|restart|upgrade|rotate}"
esac
