#!/bin/bash
# 17654 - Spring 04
# Homework 2 - repman
# Team 5
######################

FILENAME_LOG="log_repman"
FILENAME_LOG_TMP="log_repman.tmp"
FILENAME_ERR_TMP="error.tmp"

## prepare all the log and temporary files
touch $FILENAME_LOG
touch $FILENAME_ERR_TMP
touch $FILENAME_LOG_TMP


########## MODE 2 ##################
# If no commandline option is given
# then operates in mode 2, list all 
# started processes from log file
####################################
if [ $# -lt 1 ]; then  ##no argument is given

  exec 4<$FILENAME_LOG  ##load the log file
  while read line <&4   ##and process it line by line
  do
    if [ ${#line} -gt 0 ]; then  ##ignore any blank line
      ##parse out label/machine/pid
      label=$(echo $line | awk '{print $1}')
      machine=$(echo $line | awk '{print $2}')
      PID=$(echo $line | awk '{print $3}')
 
      if [ $machine == "<local>" ]; then  ##local process
        if [ -e "/proc/$PID" ]; then  ##check if the process is still running
          echo "$label $machine"
          log=$(printf "%s\t%s\t%s\n" $label $machine $PID)
          echo $log 1>>${FILENAME_LOG_TMP}  ##this process will be kept in the log file
        fi
      else ## remote program:
        ##check if the process is still running on the remote machine
        tmp=$(rsh $machine "ls /proc/$PID" 2>$FILENAME_ERR_TMP)
        ##if the file doesn't exist, the error file will take the error messages.
        size=$(wc $FILENAME_ERR_TMP | awk '{print $1}')  ##get the word count of the error message
        if [ $size -eq 0 ]; then  ##error message is empty, remote process is still running
          echo "$label $machine"
          log=$(printf "%s\t%s\t%s\n" $label $machine $PID)
          echo $log 1>>${FILENAME_LOG_TMP}  ##this process will be kept in the log file
        fi
      fi
    fi
  done  ##end of processing of the log file
  exec 4<&-  ##reset the pipeline

  ##update the log file with only the running processes
  tmp=$(mv $FILENAME_LOG_TMP $FILENAME_LOG);

  exit 0
fi


#####################################
# Start to parse commandline options
# and store them in variables
#####################################
while getopts ":p:l:m:k:" OPTION
do
  case ${OPTION} in
    p) PROGRAM=${OPTARG};;
    l) LABEL=${OPTARG};;
    m) MACHINE=${OPTARG};;
    k) KILL=${OPTARG};;
    *) echo "Unknown option."
       echo "Usage: repman -p <program and args> -l <label> [-m <machine>]"
       exit -1;;
  esac
done

#echo "PROGRAM=$PROGRAM"
#echo "LABEL=$LABEL"
#echo "MACHINE=$MACHINE"
#echo "KILL=$KILL"


########## MODE 3 ########################
# If -k is provided in the command line,
# operate in mode 3, killed the specified
# process.
##########################################
if [ ! -z "$KILL" ]; then
  ##-k option is given, all other arguments will be ignored.
  ##find the last line from the log file with the given label to kill
  line=$(grep $KILL $FILENAME_LOG -n | tail -n1)  ##-n to include a line number in front of each line
  PID=$(echo $line | awk '{print $3}')  ##parse out PID and machine name of the process
  machine=$(echo $line | awk '{print $2}')

  if [ ${#PID} -eq 0 ]; then  ##can't find the process with the given label
    echo "Can't recognize the label \"$KILL\"."
    exit -1
  fi

  echo "Killing \"$KILL\" (PID=$PID)..."
  if [ $machine == "<local>" ]; then
    kill -9 $PID  #kill the local process
  else
    rsh $machine "kill -9 $PID"  ##kill the process on the remote machine
  fi
  echo "done."

  ##remove the label from the log file
  ##parse out the line number of the line with the given label
  lineNum=$(echo $line | awk '{c=split($0, s, ":"); print s[1]}')
  newlog=$(sed "${lineNum}d" $FILENAME_LOG)  ##remove that line from the content of the log file
  echo "$newlog" 1>$FILENAME_LOG  ##update the log file
  exit 0;
fi

########## MODE 1 #############################
# If -p and -l is provided in the commandline,
# operate in mode 3, start the specified
# process.
###############################################
if [ -z "$PROGRAM" ]; then
  echo "Program (-p) can't be null."
  exit -1
fi

if [ -z "$LABEL" ]; then
  echo "Label (-l) can't be null."
  exit -1
fi

if [ -z "$MACHINE" ]; then
  ## local program
  MACHINE="<local>"  ##all the local processes will have the machine name as "<local>"
  $PROGRAM &  #start the process in background
  ## get the process ID of from the beginning of the result pidof command,
  ##+which will be the latest pid of the program in case there're multiple copies of it running.
  PID=$(pidof $PROGRAM | awk '{print $1}')
else
  ## remote program
  echo "Launching \"$PROGRAM\" on remote machine \"$MACHINE\"..."
  rsh $MACHINE $PROGRAM &  ##start the process in background on the remote machine
  PID=$(rsh $MACHINE "pidof $PROGRAM" | awk '{print $1}')  ##get the pid on the remote machine.
fi

if [ -z "$PID" ]; then
  echo "PID is null. The program has already terminated."
else
  echo "Program has been started."
  echo "Logging to $FILENAME_LOG:"
  #log the label, machine name and the pid of the process
  log=$(printf "%s\t%s\t%s\n" $LABEL $MACHINE $PID)
  echo "$log"
  echo $log 1>>$FILENAME_LOG  ##append to the log file
fi

## clean up the temporary files
tmp=$(rm -f $FILENAME_LOG_TMP $FILENAME_ERR_TMP)

