#!/usr/bin/env bash
# * ##########################################################################
# *
# *              //===//   //=====   //===//   //       //   //===//
# *             //    //  //        //    //  //       //   //    //
# *            //===//   //=====   //===//   //       //   //===<<
# *           //   \\         //  //        //       //   //    //
# *          //     \\  =====//  //        //=====  //   //===//   Version III
# *
# *             ###################################################
# *           ======  D E M O N S T R A T I O N   S Y S T E M  ======
# *             ###################################################
# *
# * ############# An Efficient RSerPool Prototype Implementation #############
# *
# * Copyright (C) 2002-2026 by Thomas Dreibholz
# *
# * This program 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 of the License, or
# * (at your option) any later version.
# *
# * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
# *
# * Contact: dreibh.iem.uni-due.de

if [ $# -lt 3 ] ; then
   echo >&2 "Usage: ComponentControl [Scenario Configuration File] {Configuration File Options} [Identifier] [start|stop|kill|stopall|killall|restart|status|log|monitor] {Arguments ...}"
   exit 1
fi


# ====== Get configuration ==================================================
CC_CONFIGFILE="$1"
shift

if [ ! -e "${CC_CONFIGFILE}" ] ; then
   echo >&2 "ERROR: Configuration file ${CC_CONFIGFILE} does not exist!"
   exit 1
fi

CC_CONFIGMISCARGS=""
# The config file has to store miscellaneous arguments into CC_CONFIGMISCARGS.
# ComponentControl only expects its own arguments after calling the
# configuration file!
# shellcheck disable=SC1090
. "${CC_CONFIGFILE}"

CC_IDENTIFIER=$1
CC_COMMAND=$2
shift ; shift
CC_MISCARGS=$*


# ====== Check directories and program ======================================
if [ "${CC_PROGRAM_DIRECTORY}" == "" ] ; then
   echo >&2 "ERROR: Variable \${CC_PROGRAM_DIRECTORY} has to be set to the program directory!"
   exit 1
fi
if [ ! -e "${CC_PROGRAM_DIRECTORY}" ] ; then
   echo >&2 "ERROR: Program directory ${CC_PROGRAM_DIRECTORY} does not exist!"
   exit 1
fi
if [ "${CC_PID_DIRECTORY}" == "" ] ; then
   echo >&2 "ERROR: Variable \${CC_PID_DIRECTORY} has to be set to the PID directory!"
   exit 1
fi
if [ ! -e "${CC_PID_DIRECTORY}" ] ; then
   echo >&2 "ERROR: PID directory ${CC_PID_DIRECTORY} does not exist!"
   exit 1
fi
if [ "${CC_OUTPUT_DIRECTORY}" == "" ] ; then
   echo >&2 "ERROR: Variable \${CC_OUTPUT_DIRECTORY} has to be set to the output directory!"
   exit 1
fi
if [ ! -e "${CC_OUTPUT_DIRECTORY}" ] ; then
   echo >&2 "ERROR: Output directory ${CC_OUTPUT_DIRECTORY} does not exist!"
   exit 1
fi
if [ "${CC_PROGRAM}" == "" ] ; then
   echo >&2 "ERROR: Variable \${CC_PROGRAM} has to be set to the program name!"
   exit 1
fi
if [ ! -x "${CC_PROGRAM_DIRECTORY}/${CC_PROGRAM}" ] ; then
   echo >&2 "ERROR: Program ${CC_PROGRAM_DIRECTORY}/${CC_PROGRAM} does not exist or is not executable!"
   exit 1
fi


# ====== Check directories and program ======================================
PROGFILE="${CC_PROGRAM_DIRECTORY}/${CC_PROGRAM}"
OUTFILE="${CC_PID_DIRECTORY}/${CC_PROGRAM}-${CC_IDENTIFIER}.out"
PIDFILE="${CC_PID_DIRECTORY}/${CC_PROGRAM}-${CC_IDENTIFIER}.pid"
LOGFILE="${CC_PID_DIRECTORY}/${CC_PROGRAM}-${CC_IDENTIFIER}.log"

CC_ARGS="${CC_ARGS} ${CC_MISCARGS}"
if [ "${CC_LOGARGS}" != "" ] ; then
   CC_ARGS="${CC_ARGS} ${CC_LOGARGS}${LOGFILE}"
fi



# ====== Stop a program using its PID file ==================================
# Arguments:
#  $1 = Program name
#  $2 = PID file
#  $3 = SIGNAL
stop_program_using_pid_file() {
   if [ -e "$2" ] ; then
      pid=$(cat "$2")
      # shellcheck disable=SC2009
      if ps -p "${pid}" | grep "$1" >/dev/null 2>&1 ; then
         echo "Sending SIG$3 to PID ${pid}"
         ${CC_PRESTART} kill -"$3" "${pid}"
      else
         echo "${pid} is already gone, removing PID file only"
         rm -f "$2"
      fi
   fi
}



# ====== Execute command ====================================================
if [ "${CC_COMMAND}" == "start" ] ; then
   if [ -e "${PIDFILE}" ] ; then
      PID=$(cat "${PIDFILE}")
      # shellcheck disable=SC2009
      if ps -p "${PID}" | grep "${CC_PROGRAM}" >/dev/null 2>&1 ; then
         echo >&2 "ERROR: Programm is already running, PID is ${PID}."
         exit 1
      fi
      rm -f "${PIDFILE}"
   fi
   echo "Starting:"
   echo "   Prestart    = ${CC_PRESTART}"
   echo "   Program     = ${PROGFILE}"
   echo "   Arguments   = ${CC_ARGS}"
   echo "   Output File = ${OUTFILE}"
   echo "   PID File    = ${PIDFILE}"
   echo "   Log File    = ${LOGFILE}"
   # shellcheck disable=SC2086
   ${CC_PRESTART} "${PROGFILE}" ${CC_ARGS} >"${OUTFILE}" 2>&1 &
   PID=$!
   echo ${PID} > "${PIDFILE}"
   echo "Started, PID is ${PID}."

   sleep 1

elif [ "${CC_COMMAND}" == "stop" ] ; then
   echo "Stopping ${CC_PROGRAM}, identifier ${CC_IDENTIFIER} ..."
   stop_program_using_pid_file "${CC_PROGRAM}" "${PIDFILE}" INT

elif [ "${CC_COMMAND}" == "kill" ] ; then
   echo "Killing ${CC_PROGRAM}, identifier ${CC_IDENTIFIER} ..."
   stop_program_using_pid_file "${CC_PROGRAM}" "${PIDFILE}" KILL

elif [ "${CC_COMMAND}" == "stopall" ] ; then
   find "${CC_PID_DIRECTORY}" -name "${CC_PROGRAM}-*.pid" | (
      while read -r pidfile ; do
         stop_program_using_pid_file "${CC_PROGRAM}" "${pidfile}" INT
      done
   )

elif [ "${CC_COMMAND}" == "killall" ] ; then
   pgrep "^${CC_PROGRAM}$" | while read -r pid ; do
      echo "Sending SIGKILL to PID ${pid}"
      kill -KILL "${pid}"
   done
   rm -f "${CC_PROGRAM_DIRECTORY}/${CC_PROGRAM}-*.pid"

elif [ "${CC_COMMAND}" == "restart" ] ; then
   # IMPORTANT: ComponentControl expects arguments shifted-away in the
   #            configuration file in CC_CONFIGMISCARGS. They are needed here.
   $0 "${CC_CONFIGFILE}" "$CC_CONFIGMISCARGS" "${CC_IDENTIFIER}" stop "${CC_MISCARGS}"
   sleep 2
   $0 "${CC_CONFIGFILE}" "$CC_CONFIGMISCARGS" "${CC_IDENTIFIER}" start "${CC_MISCARGS}"

elif [ "${CC_COMMAND}" == "status" ] ; then
   echo -n "Status of ${CC_PROGRAM}, identifier ${CC_IDENTIFIER} is: "
   if [ ! -e "${PIDFILE}" ] ; then
      echo "not running."
   else
      PID=$(cat "${PIDFILE}")
      # shellcheck disable=SC2009
      if ps -p "${PID}" | grep "${CC_PROGRAM}" >/dev/null 2>&1 ; then
         echo "running (PID is ${PID})."
      else
         echo "terminated - removing PID file."
         rm -f "${PIDFILE}"
      fi
   fi

elif [ "${CC_COMMAND}" == "log" ] ; then
   konsole "${CC_MISCARGS}" -e less "${LOGFILE}" >/dev/null 2>&1 &

elif [ "${CC_COMMAND}" == "monitor" ] ; then
   konsole "${CC_MISCARGS}" -e tail -f "${LOGFILE}" >/dev/null 2>&1 &

else
   echo >&2 "ERROR: Invalid command ${CC_COMMAND}!"
   exit 1
fi
