#!/bin/sh
#----------------------------------------------------------------------------
# CHARON guest startup script for Linux
#----------------------------------------------------------------------------
# Copyright (C) 2013-2022 STROMASYS.
# All rights reserved.
#
# chkconfig: 345 99 00
# description: CHARON guest startup and clean stop management
#
VERSION=2.16
LOCKFILE=/var/lock/subsys/charon_gstart

# Source function library.
. /etc/rc.d/init.d/functions

for P in `ls /etc/profile.d/charon*`;do . $P;done
. /opt/charon/utils/charon_common

if test `id -u` -ne 0
then
  echo "[ERROR] Must be root !"
  exit 1
fi

WTIME=20
MAXLICCHK=4
BOOTLIST=${CHARONDIR}/utils/charon_gstart.boot
SCRIPT=`basename $0 | sed "s=S[0-9][0-9]==g"`
LOGFILE="${CHARONDIR}/log/${SCRIPT}.log"
WALLOFF="/root/.charonwalloff"

GCFG=$2
if test -n "${GCFG}"
then
  if test ! -e "${GCFG}"
  then
    if test -z "`echo ${GCFG} | grep '/'`" -a -z "`echo ${GCFG} | grep '\.cfg$'`"
    then
      GCFG="/opt/charon/cfg/${GCFG}.cfg"
    fi
  fi
fi


case $1 in
  start)
    TIM=`date +"%Y%m%d;%H%M%S"`
    echo "${TIM};STARTING;${GCFG}" >>${LOGFILE}
    GUEST_CFGBAS=`basename ${GCFG}|sed "s=\.cfg==g"`
    SVC_IS_ACTIVE=`systemctl is-active charon_monusb.service 2>/dev/null`
    echo "[INFO ] aksusbd monitor service status is: ${SVC_IS_ACTIVE}"
    SVC_IS_ACTIVE=`systemctl is-active charon_logmon_${GUEST_CFGBAS}.service 2>/dev/null`
    echo "[INFO ] Log monitor service status is: ${SVC_IS_ACTIVE}"
    get_preferences
    if test "${SVC_IS_ACTIVE}" = "active" -a "${GLOBALLOGMON}" = "enabled"
    then
      get_logfile ${GCFG}
      if test -n "${LOGF}"
      then
        TAILCMD=`grep ^TAILCMD= ${CHARONDIR}/utils/charon_logchk 2>/dev/null | cut -f2 -d'=' | tr -d '"'`
        typeset -i LOOP=0
        while test "${TAILCMD}" != ""
        do
          #--- Must ensure the "tail" is running, not only the service status
          #--- This is to avoid missing first lines of the log
          PRC=`ps -ef | grep "${TAILCMD} ${LOGF}" | grep -v grep`
          if test -z "${PRC}"
          then
            if test ${LOOP} -gt 12
            then
              TIM=`date +"%Y%m%d;%H%M%S"`
              echo "${TIM};STARTING;${GCFG};WARN;Stopped waiting for log monitor to be started." >>${LOGFILE}
              echo "[WARN ] Stopped waiting for log monitor to be started."
              break
            else
              LOOP=LOOP+1
              TIM=`date +"%Y%m%d;%H%M%S"`
              echo "${TIM};STARTING;${GCFG};INFO;Waiting for log monitor to be started (${LOOP}/12)" >>${LOGFILE}
              echo "[INFO ] Waiting for log monitor to be started (${LOOP}/12)"
              sleep 5
            fi
          else
            TIM=`date +"%Y%m%d;%H%M%S"`
            echo "${TIM};STARTING;${GCFG};INFO;Log monitor is active" >>${LOGFILE}
            echo "[INFO ] Log monitor is active."
            break
          fi
        done
      fi
    fi

    TIM=`date +"%Y%m%d;%H%M%S"`
    echo "${TIM};STARTING;${GCFG};INFO;Checking network settings" >>${LOGFILE}
    echo "[INFO ] Checking network settings..."
    check_network_settings ${GCFG} alert

    TIM=`date +"%Y%m%d;%H%M%S"`
    echo "${TIM};STARTING;${GCFG};INFO;Checking vdisk files if any" >>${LOGFILE}
    if test -n "`grep \.vdisk ${GCFG} | grep -v ^#`"
    then
      echo "[INFO ] Checking vdisk files if any..."
      check_vdisk_files ${GCFG} alert
    fi

    get_logfile ${GCFG}
    TIM=`date +"%Y%m%d;%H%M%S"`
    if test -s ${LOGF}
    then
      if test -L ${LOGF}
      then
        echo "${TIM};STARTING;${GCFG};INFO;Log file ${LOGF} is a symbolic link- rotating log file" >>${LOGFILE}
      else
        echo "${TIM};STARTING;${GCFG};INFO;Renaming log file ${LOGF} to ${LOGF}.${LOGFRENEXT}" >>${LOGFILE}
        mv -f ${LOGF} ${LOGF}.${LOGFRENEXT}
        if test ${ROTA} = 1
        then
          echo "${TIM};STARTING;${GCFG};INFO;Log file ${LOGF} is regular whereas it is set to be a rotating log file" >>${LOGFILE}
          SVC_IS_ACTIVE=`systemctl is-active charon_logmon_${GUEST_CFGBAS}.service 2>/dev/null`
          if test "${SVC_IS_ACTIVE}" = "active"
          then
            echo "[INFO ] Restarting log monitoring service for ${LOGF} (mode changed)"
            echo "${TIM};STARTING;${GCFG};INFO;Restarting log monitoring service for ${LOGF}" >>${LOGFILE}
            systemctl restart charon_logmon_${GUEST_CFGBAS}.service
            while test 1
            do
              echo "[INFO ] Waiting for log monitoring activation"
              sleep 5
              CHKACT=`systemctl status charon_logmon_${GUEST_CFGBAS}.service 2>/dev/null | grep "${TAILCMD}"`
              test -n "${CHKACT}" && break
            done
            SVC_IS_ACTIVE=`systemctl is-active charon_logmon_${GUEST_CFGBAS}.service 2>/dev/null`
            echo "[INFO ] Log monitor service status is: ${SVC_IS_ACTIVE}"
          fi
        fi
      fi
    fi

    GEXE=`grep ";${GCFG}$" ${BOOTLIST}`
    test -z "${GEXE}" && GEXE=`grep ";${GCFG};" ${BOOTLIST}`
    GEXE=`echo ${GEXE} | cut -f1 -d';'`
    if test -z "${GEXE}"
    then
      TIM=`date +"%Y%m%d;%H%M%S"`
      echo "${TIM};STARTING;${GCFG};ERROR;Executable not defined for ${GCFG} configuration file" >>${LOGFILE}
      echo "[ERROR] Executable not defined for ${GCFG} configuration file."
      exit 1
    else
      if test -x "${GEXE}"
      then
        TIM=`date +"%Y%m%d;%H%M%S"`
        echo "${TIM};STARTING;${GCFG};INFO;Starting emulator: ${GEXE} -d ${GCFG}" >>${LOGFILE}
        echo "[INFO ] Starting emulator: ${GEXE} -d ${GCFG}"

        if test -x ${CHARONDIR}/utils/charon_gstart.prestart
        then
          grep -q "${GCFG})" ${CHARONDIR}/utils/charon_gstart.prestart 2>/dev/null
          if test $? = 0
          then
            echo "${TIM}:${RLEVEL}:START_COMMAND:${GCFG}" >>${LOGFILE}
            echo "[INFO ] Executing pre-start script."
            . ${CHARONDIR}/utils/charon_gstart.prestart ${GCFG} ${GEXE}
            RET=$?
            if test ${RET} = 0
            then
              echo "[INFO ] Success."
            else
              echo "[ERROR] Failed. Return code: ${RET}"
            fi
          fi
        fi

        ${GEXE} -d ${GCFG}

        if test "`/sbin/sysctl -n vm.overcommit_memory`" = "0"
        then
          if test "${OOMPRVPIDKILL}" = "enabled"
          then
            echo "${TIM};OOMKILL;${GCFG};INFO;OOM process killing prevention is enabled" >>${LOGFILE}
            echo "[INFO ] OOM process killing prevention is enabled"
            VMPID=`ps -ef | grep "${GEXE} -d" | grep -w ${GCFG} | awk '{print $2}'`
            if test -n "${VMPID}"
            then
              echo -1000 > /proc/${VMPID}/oom_score_adj
              echo "${TIM};OOMKILL;${GCFG};INFO;Process ${VMPID} is now protected" >>${LOGFILE}
              echo "[INFO ] Process ${VMPID} is now protected"
            fi
          else
            echo "${TIM};OOMKILL;${GCFG};INFO;OOM process killing prevention is disabled" >>${LOGFILE}
            echo "[INFO ] OOM process killing prevention is disabled"
          fi
        else
          echo "${TIM};OOMKILL;${GCFG};INFO;OOM is globally disabled" >>${LOGFILE}
        fi
      else
        TIM=`date +"%Y%m%d;%H%M%S"`
        echo "${TIM};STARTING;${GCFG};ERROR;Executable '${GEXE}' does not exist or is not executable" >>${LOGFILE}
        echo "[ERROR] Executable '${GEXE}' does not exist or is not executable."
        exit 2
      fi
    fi
    ;;
  check)
    #---Pb: cannot use $SERVICE_RESULT, $EXIT_CODE, and $EXIT_STATUS on RHEL/CentOS 7. Requires systemd 232 mini (Linux 8)
    #---    so this workaround is used: script restart it self with "at now" and checks the status of the service
    if test -z "$3"
    then
      echo $0 $1 $2 "X" | at now
    else
      sleep 5
      get_preferences
      GEXE=`grep ";${GCFG}$" ${BOOTLIST}`
      test -z "${GEXE}" && GEXE=`grep ";${GCFG};" ${BOOTLIST}`
      GEXE=`echo ${GEXE} | cut -f1 -d';'`
      GUEST_CFGBAS=`basename ${GCFG}|sed "s=\.cfg==g"`

      SVC_IS_ACTIVE=`systemctl is-active charon_${GUEST_CFGBAS}.service 2>/dev/null`
      if test "${SVC_IS_ACTIVE}" != "active" -a "${SVC_IS_ACTIVE}" != "inactive"
      then
        #--- Add entry in charon_gstart.log file
        TIM=`date +"%Y%m%d;%H%M%S"`
        echo "${TIM};FAILURE;${GEXE} -f ${GCFG};${SVC_IS_ACTIVE}" >>${LOGFILE}

        #--- Add entry in the virtual machine log file
        get_logfile ${GCFG}
        GSIG=`systemctl show -p Result charon_${GUEST_CFGBAS}.service | cut -f2 -d'='`
        if test "${GSIG}" != "success"
        then
          GMST=`systemctl show -p ExecMainStatus charon_${GUEST_CFGBAS}.service | cut -f2 -d'='`
          if test ${GMST:-0} = 0
          then
            GMST=`systemctl show -p ExecStart charon_${GUEST_CFGBAS}.service | cut -f8 -d';' | tr -d '{ }' | cut -f2 -d'='`
          else
            GMST=`kill -l ${GMST} 2>/dev/null`
          fi
          echo "`date +%Y%m%d:%H%M%S`:FATAL:3:70000001:LinuxToolkit( 1):  Service state is ${SVC_IS_ACTIVE}, detected ${GSIG}/${GMST}" >>${LOGF}
        fi

        #--- Send alert
        GRES="The service state is ${SVC_IS_ACTIVE}."
        GRESTART=`systemctl show -p Restart charon_${GUEST_CFGBAS}.service | cut -f2 -d'='`
        if test "${GRESTART}" = "on-failure"
        then
          GRESTARTUSEC=`systemctl show -p RestartUSec charon_${GUEST_CFGBAS}.service | cut -f2 -d'='`
          GRESTARTLBUR=`systemctl show -p StartLimitBurst charon_${GUEST_CFGBAS}.service | cut -f2 -d'='`
          GRESTARTLINT=`systemctl show -p StartLimitInterval charon_${GUEST_CFGBAS}.service | cut -f2 -d'='`
          GRESTARTLINT=`echo ${GRESTARTLINT}/1000000 | bc`
          GRES="The service state is ${SVC_IS_ACTIVE}. It is set to restart automatically on failure after ${GRESTARTUSEC}, max ${GRESTARTLBUR} times in ${GRESTARTLINT} seconds. Please check its status."
        fi

        CFGTMP=`basename ${GCFG}`
        CFGTMP=`echo ${CFGTMP}|sed "s=\.cfg==g"`
        if test "${GLOBALLOGMON}" = "enabled"
        then
          test -x ${CHARONDIR}/utils/charon_check.alertcmd && \
            echo ${CHARONDIR}/utils/charon_check.alertcmd ABORTED ${CFGTMP} ${GEXE} ${GCFG} \"${GRES}\" | \
                  at now >/dev/null 2>&1
        fi
      fi
    fi
    ;;
  restart)
    echo -n $"Restarting charon_gstart: FUNCTION NOT AVAILABLE"
    warning
    echo
    ;;
  stop)
    RLEVEL=`who -b | awk '{print $2}'`
    TIM=`date +"%Y%m%d:%H%M%S"`
    echo "${TIM}:${RLEVEL}:STOP:${GCFG:-ALL}" >>${LOGFILE}
    if test -e ${BOOTLIST}
    then
      SELCFG=""
      if test -n "${GCFG}"
      then
        SELCFG="${GCFG}"
        if test ! -s ${GCFG}
        then
          echo "[ERROR] Configuration file ${GCFG} is empty or does not exist."
        fi
      fi
      echo
      for LINE in `grep -v ^# ${BOOTLIST} | grep ";${SELCFG}"`
      do
        CHARON_EXE=`echo ${LINE} | cut -f1 -d';'`
        CHARON_CFG=`echo ${LINE} | cut -f2 -d';'`

        get_logfile ${CHARON_CFG}

        CHK=`ps -ef | grep "${CHARON_EXE} -d ${CHARON_CFG}" | grep -v grep`
        echo "[INFO ] Stopping..."
        TIM=`date +"%Y%m%d:%H%M%S"`
        if test -n "${CHK}"
        then
          #--- this part is for service being stopped by systemctl; not via console
          echo "`date +%Y%m%d:%H%M%S`:INFO :0:SERVICE STOP REQUEST" >>${LOGF}

          PRC=`echo ${CHK} | awk '{print $2}'`
          if test -x ${CHARONDIR}/utils/charon_gstart.stop
          then
            echo "${TIM}:${RLEVEL}:STOP_COMMAND:${CHARON_CFG}" >>${LOGFILE}
            echo "[INFO ] Executing stop script. Emulator pid is ${PRC}."
            ${CHARONDIR}/utils/charon_gstart.stop ${CHARON_CFG} ${CHARON_EXE}
            RET=$?
            TIM=`date +"%Y%m%d:%H%M%S"`
            echo "${TIM}:${RLEVEL}:STOP_COMMAND:returned ${RET}" >>${LOGFILE}
            if test ${RET} = 0
            then
              echo "[INFO ] Success."
            else
              echo "[ERROR] Stop script returned error code ${RET}"
            fi
          else
            TIM=`date +"%Y%m%d:%H%M%S"`
            echo "${TIM};${RLEVEL}:STOP_COMMAND;${CHARON_CFG};NO STOP SCRIPT KILL ${PRC}" >>${LOGFILE}
            echo "[WARN ] No stop script defined, sending KILL signal to emulator, pid ${PRC}"
            kill_emulator ${PRC}
            RET=$?
            TIM=`date +"%Y%m%d:%H%M%S"`
            echo "${TIM};${RLEVEL}:STOP_COMMAND;${CHARON_CFG};RESULT:${RET}" >>${LOGFILE}
          fi
          for LOOP in 1 2 3 4 5
          do 
            TIM=`date +"%Y%m%d:%H%M%S"`
            ps -P ${PRC} >/dev/null
            if test $? = 0
            then
              echo "[INFO ] Waiting for process id ${PRC} termination"
              echo "${TIM}:${RLEVEL}:STOP_COMMAND:RETRY ${LOOP}" >>${LOGFILE}
              sleep 5
            else
              echo "${TIM}:${RLEVEL}:STOP_COMMAND:SUCCESS" >>${LOGFILE}
              echo "[INFO ] Process id ${PRC} has terminated"
              sleep 7 # sleep to allow log monitor to report guest stop
              break
            fi
          done
          TIM=`date +"%Y%m%d:%H%M%S"`
          ps -P ${PRC} >/dev/null
          if test $? = 0
          then
            echo "${TIM}:${RLEVEL}:STOP_COMMAND:FAILURE:process still running" >>${LOGFILE}
            echo "[ERROR] Stop script failed. Process id ${PRC} is still running!"
            exit 3
          fi
        else
          #--- this part is for service check
          echo "${TIM}:${RLEVEL}:STOP_COMMAND:${CHARON_CFG}:NOT RUNNING" >>${LOGFILE}
        fi
      done
    else
      echo "[ERROR] Boot list not found"
    fi

    #rm -f ${LOCKFILE} 2>/dev/null

    ;;
  *)
    TIM=`date +"%Y%m%d:%H%M%S"`
    echo "${TIM}:INVALID_PARAMETER:$1" >>${LOGFILE}
    echo -n $"charon_gstart: parameter '$1' is not allowed."
    failure
    ;;
esac
exit
