#!/bin/ksh
# 
# start/stop script template for an SMF service
#
#
# The code to start, stop, refresh, and check the application is in the functions
#
#   start_application
#   stop_application
#   check_application
#   refresh_application
#
# These functions and the variables at the start of the script should be the only parts of the script that must be changed.
#
#
# Return codes 
#   100 - usage message printed and exit
#
#   251 - script aborted by QUIT signal
#   252 - script aborted by CTRL-C
#   253 - script aborted by TERM signal
#   254 - script aborted by an unknown signal
#
#
#    SMF_EXIT_OK=0
#      success
#
#    SMF_EXIT_ERR_FATAL=95
#      failed, administrative action required
#
#    SMF_EXIT_ERR_CONFIG=96
#       unrecoverable configuration error (e.g. a config file is missing)
#
#    SMF_EXIT_MON_DEGRADE=97
#       service is in degraded mode
#
#    SMF_EXIT_MON_OFFLINE=98
#       service is non-responsive and offline
#
#    SMF_EXIT_ERR_NOSMF=99
#       method called outside of SMF
#
#    SMF_EXIT_ERR_PERM=100
#       permission for the method missing
# 
# Environment variables defined in /lib/svc/share/smf_include.sh:
#
#  SMF_FMRI        
#    The service fault management resource identifier (FMRI) of the instance for which the
#    method is invoked.
#
#  SMF_METHOD      
#    The full method name of the method that is invoked
#
#  SMF_RESTARTER   
#    The service FMRI of the restarter that invokes the method
#
#
#  Method Tokens
#     A set of tokens are parsed  and  expanded  with  appropriate
#     values   from   the   method  invocation  by  the  restarter
#     svc.startd.  Other  restarters  might  not  support   method
#     tokens.   The   delegated   restarter   for  inet  services,
#     inetd(1M), does not support the following method expansions.
#
#     %%              %
#
#     %r              Name of the restarter, such as svc.startd
#
#     %m              Name of the method, such as start or stop
#
#     %s              Name of the service
#
#     %i              Name of the instance
#
#     %f              FMRI of the instance
#
#     %{prop[:,]}     Value(s) of a property. The prop might be  a
#                     property  FMRI,  a property group name and a
#                     property name separated by a /,  or  a  pro-
#                     perty   name  in  the  application  property
#                     group. These values can be followed by  a  ,
#                     (comma)  or  :   (colon).  If  present,  the
#                     separators are  used  to  separate  multiple
#                     e.g. %{start/user} 
#
#    Note that these tokens are only replaced with values if they are used 
#    in the command to call a method
#    e.g.
#   (bash):root@ferrari:/var/develop/script_templates # svcprop mysql | grep "start/exec"
#   start/exec astring /etc/my_smf_scripts/mysql.server\ start\ \"%r\ +\ \ %m\ +\ %s\ +\ %i\ +\ %f\ %{start/user}\ +\ %{start/group}\"\
#
#
# History: 
#   20.09.2006 /bs initial release
#
#
# Author
#   Bernd.Schemmer@gmx.de
#


### variables to change

# executable to start the application (if used in the function start_application)
#
  APPLICATION_START_COMMAND=""

# executable to stop the application (if used in the function stop_application)
#
  APPLICATION_STOP_COMMAND="kill -TERM \${PROCESS_PID}"

# executable to stop the application (if used in the function stop_application)
#
  APPLICATION_KILL_COMMAND="kill -KILL \${PROCESS_PID}"
  
  
# PID file of the application (if used in the function check_application)
#
  PID_FILE=""

# process name of the application (if used in the function check_application)
#
  PROCESS_NAME=""
  
# application start timeout in seconds (use "" if none are required)
#
  __APPLICATION_START_TIMEOUT=5

# application stop timeout in seconds (use "" if none are required)
#
  __APPLICATION_STOP_TIMEOUT=5

# application kill timeout in seconds (use "" if none are required)
#
  __APPLICATION_KILL_TIMEOUT=5

# contract kill timeout in seconds (use "" if none are required)
#
  __APPLICATION_CONTRACT_TIMEOUT=60
  
  
# constants for true and false
  __TRUE=0
  __FALSE=1


# name of this script without the path
#
  __THISSCRIPT=` basename $0` 
  
#  change the name of the logfile to your need; use __LOGFILE=""
#  to suppress using a logfile at all
#
#  __LOGFILE="/var/tmp/${__THISSCRIPT}.log"
  __LOGFILE=""

# ---------------------------------------
#
# save the parameter
#
  __ACTION="$1"
  [ $# -ne 0 ] && shift
  __ACTION_PARAMETER="$*"

# ---------------------------------------
#
# predefined variables
#

# return code of the script
  __MAIN_RC=0

# variable to check for script aborts (for example because of a syntax error)
  __EXIT_VIA_DIE=${__FALSE}

#
# set __VERBOSE to __TRUE for debugging (use LogInfo for debugging messages)
#
  __VERBOSE=${__FALSE}
#  __VERBOSE=${__TRUE}

  __HOSTNAME=` uname -n `

  __OS=` uname -s `

# __OS_VERSION - Operating system version (e.g 5.9 or 5.10)
#
  __OS_VERSION=` uname -r `

  __RUNLEVEL=`who -r | tr -s " " | cut -f8 -d " " `

#
# set __USER_BREAK_ALLOWED to ${__FALSE} to suppress user aborts with CTRL-C
#
  __USER_BREAK_ALLOWED=${__TRUE}

# 
# return codes for the signal handler if the script is NOT called from svc.startd
#
  __QUIT_SIGNAL_RECEIVED_RC=251
  __BREAK_SIGNAL_RECEIVED_RC=252
  __TERM_SIGNAL_RECEIVED_RC=253
  __UNKNOWN_SIGNAL_RECEIVED_RC=254


# define the SMF return codes so that we can use them even if SMF is not installed
# or the variable names change in future releases
# If /lib/svc/share/smf_include.sh exists these variables will be overwritten by that script
#
  SMF_EXIT_OK=0
  SMF_EXIT_ERR_FATAL=95
  SMF_EXIT_ERR_CONFIG=96
  SMF_EXIT_MON_DEGRADE=97
  SMF_EXIT_MON_OFFLINE=98
  SMF_EXIT_ERR_NOSMF=99
  SMF_EXIT_ERR_PERM=100

  
# ---------------------------------------
#
# define variables here
#
# Note: Do not use leading underscores (__) for variable names -
#      these names are reserved for internal variables
#
 
# ---------------------------------------
#
# sub routines
#

# ---------------------------------------
#
# Usage: automatically called in the sub routine die
#
cleanup() {

  LogInfo "Doing house keeping ..."

## add code for house keeping here

 
}

# ---------------------------------------
# 
# Usage: LogMsg [message]
#
LogMsg() {
  LogMsg_THISMSG="${__THISSCRIPT} - $*"
  echo "${LogMsg_THISMSG}"
  [ "${__LOGFILE}"x != ""x ] && echo "${LogMsg_THISMSG}" >>"${__LOGFILE}"
}

# ---------------------------------------
# 
# Usage: LogInfo [message]
#
# Note: LogInfo only prints messages if __VERBOSE is true
#
LogInfo() {
  [ ${__VERBOSE} -eq ${__TRUE} ] && LogMsg "INFO: $*"
}

# ---------------------------------------
#
# Usage: LogWarning [message]
#
LogWarning() {
  LogMsg "WARNING: $*!" 
}


# ---------------------------------------
#
# Usage: LogError [message]
#
# Note: Error messages are written to STDERR
#
LogError() {
  LogMsg "ERROR: $*!" >&2
}


# ---------------------------------------
#
# Usage: die [returncode] [message]
# 
die() {  
  die_THISRC=$1
  [ $# -ne 0 ] && shift
  die_THISMSG="$*"
  __EXIT_VIA_DIE=${__TRUE}

  cleanup

  if [ $# -ne 0 ] ; then
    if [ "${die_THISRC}"x = "0"x ] ; then
      LogMsg "${die_THISMSG}"
    else
      LogError "${die_THISMSG}"
    fi
  fi

  exit ${die_THISRC}
}


# ---------------------------------------
# GENERAL_SIGNAL_HANDLER
#
# general trap handler 
#
# usage: called automatically (parameter $1 is the signal number)
#
# returns: -
#
GENERAL_SIGNAL_HANDLER() {

  case $1 in 

    1 )
        LogInfo "HUP signal received."
        ;;

    2 )
        if [ ${__USER_BREAK_ALLOWED} -eq ${__TRUE} ] ; then
          die ${__BREAK_SIGNAL_RECEIVED_RC} "Script aborted by the user via signal BREAK (CTRL-C)" 
        else
          LogInfo "Break signal (CTRL-C) received and ignored (Break is disabled)."
        fi
        ;;

    3 )
        die ${__QUIT_SIGNAL_RECEIVED_RC} "QUIT signal received" 
        ;;

   15 )
        die ${__TERM_SIGNAL_RECEIVED_RC} "Script aborted by the external signal TERM" 
        ;;

   "exit" | 0 )
        if [ "${__EXIT_VIA_DIE}"x != "${__TRUE}"x ] ; then
          LogWarning "You should use the function \"die\" to end the program"
        fi    

        ;;
       
    * ) die ${__UNKNOWN_SIGNAL_RECEIVED_RC=} "Unknown signal catched: $1 "
        ;;

  esac
}


# ---------------------------------------
# sleep n seconds
#
wait_for_application() {
   [ "$1"x != ""x ] && sleep $1
}
   
# ---------------------------------------
# check if the application is running with the check commands for the application
#
# This routine must set the following global variables
#
#   PROCESS_PID (PID of the application process)
#   PROCESS_IS_RUNNING ( ${__TRUE} or ${__FALSE} )
#
# return code: 
#   ${__TRUE} - the application is running
#   ${__FALSE} - the application is not running
#
check_application() {

# init the global result variables
  PROCESS_PID=""
  PROCESS_RUNNING=${__FALSE}
  
  if [ "${PROCESS_NAME}"x != ""x ] ; then
# 
# code to get the PID of a process given the process name
#
    PROCESS_PID=` ps -ef | grep ${PROCESS_NAME} | grep -v grep | awk '{print $2}' `
#    PROCESS_PID=` pgrep ${PROCESS_NAME} `
    [ "${PROCESS_PID}"x != ""x ] && PROCESS_RUNNING=${__TRUE} || PROCESS_RUNNING=${__FALSE}

  elif [ "${PID_FILE}"x != ""x ] ; then
# 
# code to use a PID file
#
    [ -f ${PID_FILE} ]  && PROCESS_PID=` cat ${PID_FILE} ` 
    [ "${PROCESS_PID}"x != ""x ] && PROCESS_PID=`/usr/bin/ps -p ${PROCESS_PID} -o pid=`
    [ "${PROCESS_PID}"x != ""x ] && PROCESS_RUNNING=${__TRUE} || PROCESS_RUNNING=${__FALSE} 
  else
    die ${SMF_EXIT_ERR_FATAL} "The check function for the application is not implemented properly"
  fi
  
  return ${PROCESS_RUNNING}
}


# ---------------------------------------
# code to start the application 
#
start_application() {
  typeset THISRC=${SMF_EXIT_ERR_FATAL}

  LogMsg "Starting ${__APPLICATION_NAME} ..." 
  ${APPLICATION_START_COMMAND} & 

# wait if necessary ...
  wait_for_application ${__APPLICATION_START_TIMEOUT}

  check_application

  [ ${PROCESS_RUNNING} = ${__TRUE} ] && THISRC=${SMF_EXIT_OK}
  
  return ${THISRC}
}


# ---------------------------------------
# code to stop the application 
#
stop_application() {
  typeset THISRC=${SMF_EXIT_ERR_FATAL}

  LogMsg "Stopping ${__APPLICATION_NAME} (PID is ${PROCESS_PID}) ..." 

# stop the application with the recommended command
#
  eval "${APPLICATION_STOP_COMMAND}"

# wait if necessary ...
  wait_for_application ${__APPLICATION_STOP_TIMEOUT}
  
# check that the application was really stopped
#
  check_application

  if [ ${PROCESS_RUNNING} = ${__TRUE} ] ; then
# the application is still running - kill it

    eval "${APPLICATION_KILL_COMMAND}"

    wait_for_application ${__APPLICATION_KILL_TIMEOUT}

    check_application
  fi


  if [ ${PROCESS_RUNNING} != ${__TRUE} ] ; then
# if the application is still running kill all processes in the contract

    CONTRACT=$( svcprop "restarter/contract" "${SMF_FRMI}"  )
    if [ "${CONTRACT}"x != ""x ] ; then
      smf_kill_contract "${CONTRACT}" KILL ${__TRUE} ${__APPLICATION_CONTRACT_TIMEOUT}
    fi

    check_application
  fi
  
  if [ ${PROCESS_RUNNING} != ${__TRUE} ] ; then
    THISRC=${SMF_EXIT_OK}
  else
    LogError "Could not stop the application with the recommended method"
  fi

  return ${THISRC}
}

# ---------------------------------------
# code to refresh the application using the start commands for the application
#
refresh_application() {
  typeset  THISRC=${SMF_EXIT_ERR_FATAL}

  LogMsg "Refreshing ${__APPLICATION_NAME} ..." 

# sample code 
  kill -HUP ${PROCESS_PID}

  THISRC=${SMF_EXIT_OK}
  
  return ${THISRC}
}


  
# -------------------------------------------------------------------------------------------------
#
# main code
#

# install trap handler
  trap "GENERAL_SIGNAL_HANDLER  1"  1
  trap "GENERAL_SIGNAL_HANDLER  2"  2
  trap "GENERAL_SIGNAL_HANDLER  3"  3
  trap "GENERAL_SIGNAL_HANDLER 15" 15
  trap "GENERAL_SIGNAL_HANDLER exit" 0

# -------------------------------------------------------------------------------------------------

# check the logfile
  if [ "${__LOGFILE}"x != ""x ] ; then
    touch "${__LOGFILE}"
    [ $? -ne 0 ] && __LOGFILE=""
  fi

# -------------------------------------------------------------------------------------------------


# ---------------------------------------
# check if we are called by SMF or manually
#
# results:
#   __SMF_PRESENT = 0 -> script is called from within SMF
#   __SMF_PRESENT = 1 -> script is NOT called from within SMF
#

  __SMF_INCLUDE_SCRIPT="/lib/svc/share/smf_include.sh"
  __SMF_PRESENT=1
  
  [ -f "${__SMF_INCLUDE_SCRIPT}" ] && . "${__SMF_INCLUDE_SCRIPT}"
  if [ "${SMF_FMRI}"x != ""x ] ; then

# the script is called from the svc.startd
    __SMF_PRESENT=0

# sample code to read properties from the repository
#

# SMF specific start/stop prepare code, e.g. read the properties from the service definition
    [ "`svcprop -p general/verbose ${SMF_FMRI} 2>/dev/null`"x = "true"x ] && __VERBOSE=${__TRUE}

#      LogMsg "Reading the values from the service definition ..."

#      svcprop -q -p mysql/datadir "${SMF_FMRI}"
#      if [ $? -eq 0 ] ; then
#        MYSQLD_DATA_DIR=` svcprop -p mysql/datadir "${SMF_FMRI}"` 
#        LogMsg "MYSQL data dir is \"${MYSQLD_DATA_DIR}\""
#      fi

#
# redefine the return codes for the signal handler
#
    __QUIT_SIGNAL_RECEIVED_RC=${SMF_EXIT_ERR_FATAL}
    __BREAK_SIGNAL_RECEIVED_RC=${SMF_EXIT_ERR_FATAL}
    __TERM_SIGNAL_RECEIVED_RC=${SMF_EXIT_ERR_FATAL}
    __UNKNOWN_SIGNAL_RECEIVED_RC=${SMF_EXIT_ERR_FATAL}

  else
    die ${SMF_EXIT_ERR_NOSMF} "This script can only be called by the SMF daemon"
  fi

# -------------------------------------------------------------------------------------------------
# for debugging only

  LogInfo "Action is \"${__ACTION}\""
  LogInfo "Actionparameter are \"${__ACTION_PARAMETER}\""
 
# -------------------------------------------------------------------------------------------------

# prepare the action
#
  LogInfo "SMF_FMRI is \"${SMF_FMRI}\""
  LogInfo "SMF_METHOD is \"${SMF_METHOD}\""
  LogInfo "SMF_RESTARTER is \"${SMF_RESTARTER}\""    

  check_application
    
# execute the action
  
  case ${__ACTION} in

    start | enable ) # start the application
      if [ "${PROCESS_RUNNING}" = "${__FALSE}" ] ; then
        start_application
        __MAIN_RC=$?
      else
        __MAIN_RC=${SMF_EXIT_OK}
      fi
      ;;

    stop | disable ) # stop the application
      if [ "${PROCESS_RUNNING}" = "${__TRUE}" ] ; then 
        stop_application
        __MAIN_RC=$?
      else
        __MAIN_RC=${SMF_EXIT_OK}
      fi
      ;;

    refresh ) # refresh the application
      if [ "${PROCESS_RUNNING}" = "${__FALSE}" ] ; then
        refresh_application
        __MAIN_RC=$?
      else
        __MAIN_RC=${SMF_EXIT_MON_OFFLINE}
      fi
      ;;
    
    * ) # unknown action code
      die ${SMF_EXIT_ERR_FATAL} "Invalid action code: ${ACTION}; usage:  \"${__THISSCRIPT} [start|stop|refresh]\" "

      ;;  
  
  esac
    
die ${__MAIN_RC}
  
   
# ---------------------------------------
