#!/bin/bash

### BEGIN INIT INFO
# Provides:          quobyte-nfs
# Required-Start:    $network $remote_fs rpcbind
# Required-Stop:     $network $remote_fs
# Should-Start:      portmap quobyte-registry quobyte-metadata quobyte-data
# Should-Stop:       $null
# Default-Start:     3 5
# Default-Stop:      0 1 2 6
# Short-Description: Quobyte NFS Proxy
# Description:       Quobyte NFS Proxy
### END INIT INFO

QUOBYTE_SERVICES=(nfs)

# Source function library.
if [ -e /lib/lsb/init-functions ]; then
    . /lib/lsb/init-functions
elif [ -e /etc/init.d/functions ]; then
    . /etc/init.d/functions
fi

if [ -z $QUOBYTE_BASEDIR ]; then
  BASE_DIR="/"
else
  BASE_DIR=${QUOBYTE_BASEDIR}/
fi

if [ -r ${QUOBYTE_BASEDIR}/etc/default/quobyte ]; then
  . ${QUOBYTE_BASEDIR}/etc/default/quobyte
fi

DEPRECATED_RUN_DIR=var/run/
RUN_DIR=var/run/quobyte/
LOG_DIR=var/log/quobyte/
BIN_DIR=usr/bin/
CONFIG_DIR=etc/quobyte/
LIB_DIR=opt/quobyte/lib/
LOCAL_BIN_DIR=opt/quobyte/bin/
GWT_LIB_DIR=${LIB_DIR}gwt/
LIMIT_RTPRIO=4
LIMIT_NICE=40
LIMIT_OPEN_FILES=500000
LIMIT_OPEN_FILES_NFS=16384
LIMIT_MAX_PROCESSES=16384
FORCE_KILL_AFTER_TIMEOUT_S=90

# For SELinux we need to use 'runuser' not 'su'
if [ -x "/sbin/runuser" ]; then
    SU="/sbin/runuser"
else
    SU="/bin/su"
fi

pre_check() {
    if [ $DAEMON_USER ]; then
        exists=`getent passwd ${DAEMON_USER} | wc -l`
        if [ $exists -eq 0 ]; then
            echo "User ${DAEMON_USER} does not exist. Create it first."
            exit 1
        fi
    fi
    if [ "$1" == "s3" -a "$S3_DAEMON_USER" != "" ]; then
        exists=`getent passwd ${S3_DAEMON_USER} | wc -l`
        if [ $exists -eq 0 ]; then
            echo "User ${S3_DAEMON_USER} does not exist. Create it first."
            exit 1
        fi
    fi
}

create_directories() {
    local PID_DIR="${BASE_DIR}${RUN_DIR}"
    if [ ! -e $PID_DIR ]; then
      echo "Creating run directory ${PID_DIR}"
      mkdir -p $PID_DIR
    fi
    local LOG_DIR="${BASE_DIR}${LOG_DIR}"
    if [ ! -e $LOG_DIR ]; then
      echo "Creating log directory ${LOG_DIR}"
      mkdir -p $LOG_DIR
    fi
}

set_display_name() {
    case "$1" in
        "registry")
            DISPLAY_NAME="Registry Service (registry)"
            ;;
        "metadata")
            DISPLAY_NAME="Metadata Service (metadata)"
            ;;
        "data")
            DISPLAY_NAME="Data Service (data)"
            ;;
        "api")
            DISPLAY_NAME="API Service (api)"
            ;;
        "webconsole")
            DISPLAY_NAME="Web Console (webconsole)"
            ;;
        "s3")
            DISPLAY_NAME="S3 Proxy (s3)"
            ;;
        "nfs")
            DISPLAY_NAME="NFS Proxy (nfs)"
            ;;
    esac
}

verify_default_services() {
  # Services listed in DEFAULT_SERVICES for default start by 'quobyte start'.
  QUOBYTE_SERVICES_DEFAULT=()
  for service in $DEFAULT_SERVICES; do
    local service_found=0
    local service_lc=$(echo $service | tr '[:upper:]' '[:lower:]')
    local i
    for (( i = 0 ; i < ${#QUOBYTE_SERVICES[@]} ; i++ )); do
      if [[ $service_lc == ${QUOBYTE_SERVICES[i]} ]]; then
        service_found=1
        break
      fi
    done
    if [ $service_found -eq 1 ]; then
      QUOBYTE_SERVICES_DEFAULT+=($service)
    else
      echo "WARNING: Unknown service specified in DEFAULT_SERVICES was ignored: ${service}"
    fi
  done
}

pid_file_exists() {
  local servicename=$1
  local pid_file="${BASE_DIR}${RUN_DIR}${servicename}.run"
  # TODO(tilman): Here and below: remove old_pid_file when no more
  #               installations older than 2.0 are in the wild
  local old_pid_file="${BASE_DIR}${DEPRECATED_RUN_DIR}${servicename}.run"

  if [ -f "$pid_file" ] || [ -f "$old_pid_file" ]; then
      return 0
  fi
  return 1
}

delete_pidfile() {
  local servicename=$1
  local pid_file="${BASE_DIR}${RUN_DIR}${servicename}.run"
  local old_pid_file="${BASE_DIR}${DEPRECATED_RUN_DIR}${servicename}.run"

  if [ -f "$pid_file" ]; then
      rm -f "$pid_file"
  fi
  if [ -f "$old_pid_file" ]; then
      rm -f "$old_pid_file"
  fi
}

get_pid() {
  local servicename=$1
  local pid_file="${BASE_DIR}${RUN_DIR}${servicename}.run"
  local old_pid_file="${BASE_DIR}${DEPRECATED_RUN_DIR}${servicename}.run"

  pid=-1
  if [ -f $pid_file ]; then
      pid=`cat $pid_file | head -n1`
  fi
  if [ -f $old_pid_file ]; then
      pid=`cat $old_pid_file | head -n1`
  fi
}

warn_default_services_not_configured() {
  cat <<EOF
If you want to run 'quobyte $1' without specifiying a service as second
parameter, you have to specify all to be started services in the variable
DEFAULT_SERVICES in the file /etc/default/quobyte.

EOF
  echo -en "Available services:\n  "
  local i
  for (( i = 0 ; i < ${#QUOBYTE_SERVICES[@]} ; i++ )); do
    echo -n "${QUOBYTE_SERVICES[i]} "
  done
  echo -en "\n"
  echo "No services were started. Exiting with return code 1."
  exit 1
}

start() {
    if [ -z $1 ]; then
      if [ ${#QUOBYTE_SERVICES_DEFAULT[@]} -eq 0 ]; then
        warn_default_services_not_configured "start"
      else
        local i
        for (( i = 0 ; i < ${#QUOBYTE_SERVICES_DEFAULT[@]} ; i++ )); do
          start_service ${QUOBYTE_SERVICES_DEFAULT[i]}
        done
      fi
    else
      check_service_name $1
      start_service $1
    fi
}

stop() {
    if [ -z $1 ]; then
      local SERVICES=( "${QUOBYTE_SERVICES[@]}" )
      if [ ${#QUOBYTE_SERVICES_DEFAULT[@]} -gt 0 ]; then
        SERVICES=( "${QUOBYTE_SERVICES_DEFAULT[@]}" )
      fi
      local i
      for (( i = 0 ; i < ${#SERVICES[@]} ; i++ )); do
        stop_service ${SERVICES[i]}
      done
    else
      check_service_name $1
      stop_service $1
    fi
}

status() {
    if [ -z $1 ]; then
      local SERVICES=( "${QUOBYTE_SERVICES[@]}" )
      if [ ${#QUOBYTE_SERVICES_DEFAULT[@]} -gt 0 ]; then
        SERVICES=( "${QUOBYTE_SERVICES_DEFAULT[@]}" )
      fi
      local i
      for (( i = 0 ; i < ${#SERVICES[@]} ; i++ )); do
        status_service ${SERVICES[i]}
      done
    else
      check_service_name $1
      status_service $1
    fi
}

reload() {
    if [ -z $1 ]; then
      local SERVICES=( "${QUOBYTE_SERVICES[@]}" )
      if [ ${#QUOBYTE_SERVICES_DEFAULT[@]} -gt 0 ]; then
        SERVICES=( "${QUOBYTE_SERVICES_DEFAULT[@]}" )
      fi
      local i
      for (( i = 0 ; i < ${#SERVICES[@]} ; i++ )); do
        reload_service ${SERVICES[i]}
      done
    else
      check_service_name $1
      reload_service $1
    fi
}


start_service() {
    local servicename=$1
    local pid_file="${BASE_DIR}${RUN_DIR}${servicename}.run"
    local log_file="${BASE_DIR}${LOG_DIR}${servicename}.log"

    set_display_name "$servicename"
    pre_check
    create_directories

    if pid_file_exists "$servicename"; then
        get_pid "$servicename"
        if [ -e /proc/$pid ];then
            echo "Quobyte ${DISPLAY_NAME} already running"
            return 0
        else
            echo "Previous Quobyte ${DISPLAY_NAME} was not shutdown correctly (PID $pid)."
        fi
    fi

    if [ ! -f $log_file ]; then
        touch $log_file
    fi
    if [ -n "$DAEMON_USER" ]; then
      /bin/chown $DAEMON_USER $log_file
    fi

    echo >> $log_file
    date >> $log_file
    echo -e "Starting Quobyte ${DISPLAY_NAME}...\n\n" >> $log_file
    echo -n "Starting Quobyte ${DISPLAY_NAME}... "

    if [ "$servicename" = "nfs" ]; then
      if [ $UID -ne 0 ]; then
        echo "failed"
        echo "ERROR: Quobyte ${DISPLAY_NAME} has to be run as 'root'."
        return 1
      else
        ulimit -n $LIMIT_OPEN_FILES_NFS
        ${BASE_DIR}${LOCAL_BIN_DIR}ganesha.nfsd -f ${BASE_DIR}${CONFIG_DIR}nfs.cfg -p $pid_file
        local proc_pid=`cat $pid_file | head -n1`
      fi
    else
      # Maximize the virtual memory limit to make sure that Java can set the MaxHeapSize (-Xmx) correctly.
      ulimit_setup="ulimit -e $LIMIT_NICE ; ulimit -v unlimited ; ulimit -u $LIMIT_MAX_PROCESSES"
      ulimit_setup="$ulimit_setup; ulimit -r $LIMIT_RTPRIO; ulimit -n $LIMIT_OPEN_FILES"
      eval $ulimit_setup

      # Redirect stdout to /dev/null because the service logs everything to its log file.
      # Keep stderr to debug general startup problems.
      if [ -n "$DAEMON_USER" -a "$servicename" != "s3" ]; then
        $SU -s /bin/bash $DAEMON_USER -c "$ulimit_setup ; ${BASE_DIR}${BIN_DIR}quobyte-${servicename}" >> $log_file 2>&1 &
      elif [ -n "$S3_DAEMON_USER" -a "$servicename" == "s3" ]; then
        $SU -s /bin/bash $S3_DAEMON_USER -c "$ulimit_setup ; ${BASE_DIR}${BIN_DIR}quobyte-s3" >> $log_file 2>&1 &
      else
        ${BASE_DIR}${BIN_DIR}quobyte-${servicename} >> $log_file 2>&1 &
      fi
      local proc_pid=$!
      echo $proc_pid > $pid_file
    fi

    sleep 1
    if [ -e /proc/$proc_pid ]; then
        echo "success"
    else
        echo "failed"
        return 1
    fi

    # Check that the max no of open files was correctly set or warn otherwise.
    if [ "$servicename" = "data" ]; then
      if [ -n "$DAEMON_USER" ]; then
        pid=$(ps --no-headers -o pid --ppid=$proc_pid | tr -d ' ')
      else
        pid=$proc_pid
      fi
      actual_nofile=$(grep "Max open files" /proc/$pid/limits 2>/dev/null | awk '{ print $4 }')
      if [[ $actual_nofile -lt $LIMIT_OPEN_FILES ]] && [[ -e /proc/$pid/limits ]]; then
        echo "WARNING: Failed to increase the maximum number of open files to $LIMIT_OPEN_FILES. Current limit is: $actual_nofile"
        echo "         Make sure that there are no conflicting entries in /etc/security/limits.conf"
      fi
    fi

    return 0
}

stop_service() {
    local servicename=$1

    set_display_name "$servicename"

    result=0
    if pid_file_exists "$servicename"; then
        get_pid "$servicename"
        if [ -n "$pid" -a -d "/proc/$pid" ]; then
            quobyte_process_found=0
            local i
            for i in $pid $(ps --no-headers -o pid --ppid=$pid | tr -d ' '); do
                grep -q quobyte "/proc/$i/environ" 2> /dev/null || \
                    grep -q quobyte "/proc/$i/cmdline" 2> /dev/null
                if [ $? -eq 0 ]; then
                    quobyte_process_found=1
                    break
                fi
            done
            if [ $quobyte_process_found -ne 1 ]; then
                echo "Stopping Quobyte ${DISPLAY_NAME} not running"
                delete_pidfile "$servicename"
                return 0
            fi

            echo -n "Stopping Quobyte ${DISPLAY_NAME}... "
            if [ "$servicename" = "nfs" -o "$servicename" = "s3" -o -z "$DAEMON_USER" ]; then
                kill $pid
                kill_result=$?
                if [ $kill_result -ne 0 ]; then
                    result=$kill_result
                else
                    wait_time=0
                    result=0
                    while kill -0 $pid 2>/dev/null; do
                        sleep 1
                        wait_time=$(($wait_time + 1))
                        if [ $wait_time -eq $FORCE_KILL_AFTER_TIMEOUT_S ]; then
                            kill -9 $pid 2>/dev/null
                            break
                        fi
                    done
                fi
            else
                local pgid="$(ps -o pgid --no-header --ppid=$pid)"
                pkill -g $pgid
                kill_result=$?
                if [ $kill_result -ne 0 ]; then
                    result=$kill_result
                else
                    wait_time=0
                    result=0
                    while pkill -0 -g $pgid 2>/dev/null; do
                        sleep 1
                        wait_time=$(($wait_time + 1))
                        if [ $wait_time -eq $FORCE_KILL_AFTER_TIMEOUT_S ]; then
                            pkill -9 -g $pgid 2>/dev/null
                            break
                        fi
                    done
                fi
            fi
            if [ $result -eq 0 ]; then
                delete_pidfile "$servicename"
                echo "success"
            else
                echo "failed"
            fi
        else
            echo "Stopping Quobyte ${DISPLAY_NAME} not running"
        fi
    fi
    return $result
}

status_service() {
    local servicename=$1

    set_display_name "$servicename"

    if pid_file_exists "$servicename"; then
        get_pid "$servicename"
        if [ ! -e /proc/$pid ]; then
            echo "Quobyte ${DISPLAY_NAME} has crashed (PID $pid)."
            return 1
        else
            echo "Quobyte ${DISPLAY_NAME} is running."
            return 0
        fi
    else
        echo "Quobyte ${DISPLAY_NAME} not running"
        return 3
    fi
}

reload_service() {
    local servicename=$1

    set_display_name "$servicename"

    if [ "$servicename" != "nfs" ]; then
        echo "Quobyte ${DISPLAY_NAME} currently does not support 'reload'. Use 'restart'."
        return 0
    fi

    if pid_file_exists "$servicename"; then
        get_pid "$servicename"
        pid=$?
        if [ ! -e /proc/$pid ]; then
            echo "Quobyte ${DISPLAY_NAME} has crashed. Could not reload."
            return 1
        else
            kill -HUP $pid
            echo "Quobyte ${DISPLAY_NAME} configuration was reloaded."
            return 0
        fi
    else
        echo "Quobyte ${DISPLAY_NAME} not running"
        return 3
    fi
}

extract_service_from_script_name() {
  # readlink is required to resolve a path like '/etc/rc3.d/S50quobyte-registry'
  script_basename="$(basename "$(readlink -f "$0")")"
  if [ "$script_basename" = "quobyte" ]; then
    return
  fi
  local i
  for ((i=0; i < ${#QUOBYTE_SERVICES[@]}; ++i)); do
    if [ "$script_basename" = "quobyte-${QUOBYTE_SERVICES[$i]}" ]; then
      echo "${QUOBYTE_SERVICES[$i]}"
      return
    fi
  done
}

check_service_name() {
  local i
  for ((i=0; i < ${#QUOBYTE_SERVICES[@]}; ++i)); do
    if [ "$1" = "${QUOBYTE_SERVICES[$i]}" ]; then return 0; fi
  done
  echo "ERROR: unknown Quobyte service: $1"
  exit 1
}

if [ -n "$2" ]; then
  service_name="$2"
else
  service_name=$(extract_service_from_script_name "$0")
fi
verify_default_services

# See how we were called.
case "$1" in
    start)
        start "$service_name"
        result=$?
        ;;
    stop)
        stop "$service_name"
        result=$?
        ;;
    status)
        status "$service_name"
        result=$?
        ;;
    reload)
        reload "$service_name"
        result=$?
        ;;
    restart)
        if [ -z "$service_name" -a ${#QUOBYTE_SERVICES_DEFAULT[@]} -eq 0 ]; then
          warn_default_services_not_configured "restart"
        fi
        stop "$service_name"
        if [ "$service_name" = "nfs" ]; then
          # NFS takes very long to shutdown. We have to wait for it.
          sleep 6
        else
          sleep 1
        fi
        start "$service_name"
        result=$?
        ;;
    try-restart)
        ## Stop the service and if this succeeds (i.e. the
        ## service was running before), start it again.
        $0 status "$service_name" >/dev/null
        if [ $? -eq 0 ]; then
          $0 restart "$service_name"
          result=$?
        else
          result=0
        fi
        ;;
    *)
        if [ -z "$service_name" ]; then
          services_string=$(printf ",%s" "${QUOBYTE_SERVICES[@]}")
          # Cut off the starting ','.
          services_string=" [${services_string:1}]"
        fi
        echo -e "Usage: $0 {start|stop|restart|reload|status|try-restart}${services_string}\n"
        result=1
        ;;
esac

exit $result
