#!/bin/sh
#
# ident "$Id$ SMI"
# 
# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# Start/stop script for the Sun Ray Administration Web Server
#

# shell/scripting environment
#
umask 022
set -u      # enforce initialization of variables
debug=off   # setting debug=on will cause additional log messages

# core global variables
# other variables are initialized in setEnvironment function
#
PRODUCT_GROUP="Sun Ray"
PRODUCT_NAME="${PRODUCT_GROUP} Web Administration"
PRODUCT_BUNDLE_NAME="${PRODUCT_GROUP} Server Software"

BASE_DIR="`/etc/opt/SUNWut/basedir/lib/utprodinfo -r SUNWuto`"
PRODUCT_INSTALL_DIR="${BASE_DIR}/SUNWut"
PRODUCT_CONFIG_FILE="/etc/opt/SUNWut/utadmin.conf"
PRODUCT_LOG_DIR="/var/opt/SUNWut/log/"

# logging utilities
# 
UT_AGELOG="${PRODUCT_INSTALL_DIR}/lib/utagelog"
MAX_ARCHIVED_LOGS=5

# exit codes returned by this script
#
EXIT_SUCCESS=0
EXIT_FAILURE=1

# parseable status messages
#
SERVER_RUNNING="running=yes"
SERVER_NOT_RUNNING="running=no"

# supported sub-commands
#
START_CMD="start"
STOP_CMD="stop"
RESTART_CMD="restart"
STATUS_CMD="status"
ENABLE_CMD="enable"
DISABLE_CMD="disable"
HELP_FLAG_SHORT="-h"
HELP_FLAG_LONG="--help"
PARSEABLE_FLAG_SHORT="-p"
PARSEABLE_FLAG_LONG="--parseable"

# setup command environment for target OS.
#
OS=`uname -s`
if [ ! -n "${OS}" ]; then
    echo "Unable to determine operating system." 1>&2
    exit $EXIT_FAILURE
fi

LAUNCH_COMMAND="su"
SU_SHELL=""
LOG_FILE=""
if [ "${OS}" = "SunOS" ]; then
    PATH=/bin:/sbin:/usr/sbin
    ECHO=echo
    if [ -x /usr/ucb/echo ]; then
        ECHO=/usr/ucb/echo
    fi
    PING=ping
    WHICH="/bin/ksh whence"
 
elif [ "${OS}" = "Linux" ]; then
    PATH=/bin:/sbin:/usr/bin:/usr/sbin
    ECHO="/bin/echo -e"
    PING="ping -c 1"
    WHICH="which --skip-alias"

    # The "su" command runs the launcher identity's shell to run any command
    # specified via the -c option.  However this yields access permissions
    # problems because /root is read-only by root and thus cannot execute
    # the ".rc" for that shell.  So we pick another shell that is NOT the
    # default shell for root, and we pick a non-restricted shell listed
    # in /etc/shells.
    #
    rootShell=`grep "^root" /etc/passwd | awk -F: '{print $7}'`
    if [ ! -n "$rootShell" ]; then
        rootShell=/bin/sh
    fi
    for shell in `cat /etc/shells`
    do
        if [ $shell != $rootShell ]; then
            SU_SHELL="-s $shell"
            break
        fi
    done

else
    echo "Not supported on detected OS ${OS}." 1>&2
    exit $EXIT_FAILURE
fi

################################################################################
#
# setEnvironment
#
# Intialize the environment variables based on the properties in $CONFIG_FILE.
# We fallback to default values, if properties are not explicitly defined.
#
################################################################################

setEnvironment() {

    CATALINA_BASE="${PRODUCT_INSTALL_DIR}/webadmin"
    CONFIG_FILE="${CATALINA_BASE}/conf/webadmin.conf"
    LOG_FILE="`getConfigParameter log.file ${CATALINA_BASE}/logs/utwebadmin.log`"

    # Tomcat (Catalina) process settings
    #
    PROCESS_PIDFILE="${CATALINA_BASE}/tmp/utwebadmin.pid"
    PROCESS_USERNAME="`getConfigParameter process.username utwww`"
    PROCESS_GROUPNAME="`getConfigParameter process.groupname utadmin`"
    
    CATALINA_HOME="`getConfigParameter catalina.home`"
    CATALINA_OPTS="`getConfigParameter catalina.options`"
    CATALINA_WORK="${CATALINA_BASE}/work/com_sun_ut_web_admin"
    CATALINA_KEYSTORE="${CATALINA_BASE}/conf/keystore"
    CATALINA_POLICY="${CATALINA_BASE}/conf/policy.conf"

    SECURE_PORT="`getConfigParameter https.port`"
    UNSECURE_PORT="`getConfigParameter http.port 1660`"
    SHUTDOWN_PORT="`getConfigParameter shutdown.port 50505`"
    REMOTE_ACCESS="`getConfigParameter remote.access 127.0.0.1`"
    SESSION_TIMEOUT="`getConfigParameter session.timeout 30`"
    LOGGING_LEVEL="`getConfigParameter log.level error`"

    SERVER_TEMPLATE_FILE=${CATALINA_BASE}/conf/templates/server_template.xml
    SERVER_FILE=${CATALINA_BASE}/conf/server.xml
    WEB_TEMPLATE_FILE=${CATALINA_BASE}/conf/templates/web_template.xml
    WEB_FILE=${CATALINA_BASE}/conf/web.xml

    # Java VM settings
    #
    JAVA_HOME="`getConfigParameter jre.home /etc/opt/SUNWut/jre`"
    JAVA="${JAVA_HOME}/bin/java"
    JAVA_LIBRARY_PATH="${PRODUCT_INSTALL_DIR}/lib"
    JAVA_OPTS="`getConfigParameter jre.options`"
    JAVA_KEYTOOL="${JAVA_HOME}/bin/keytool"
    
    JAVA_CLASSPATH="${CATALINA_HOME}/bin/bootstrap.jar"
    if [ -f "${JAVA_HOME}/lib/tools.jar" ] ; then
        JAVA_CLASSPATH=${JAVA_CLASSPATH}:"${JAVA_HOME}/lib/tools.jar"
    fi
    if [ -f "${JAVA_HOME}/jre/lib/jsse.jar" ] ; then
        JAVA_CLASSPATH=${JAVA_CLASSPATH}:"${JAVA_HOME}/jre/lib/jsse.jar"
    fi
    if [ "${OS}" = "Linux" ]; then
        LD_LIBRARY_PATH="${JAVA_LIBRARY_PATH}"
    else
        LD_LIBRARY_PATH=""
    fi

} # setEnvironment


################################################################################
#
# usage
#
# Print out help/usage paragraph describing the supported sub-commands.
#
# Parameters:
# $1 = exitCode - (Optionally) the exit code that should be returned by this 
#                 function.
# Returns:
#    The function will exit with EXIT_SUCESS unless a different exit code has
#    been specified.
# 
################################################################################

usage() {

    if [ $# -eq 0 ]; then
        exitCode=$EXIT_SUCCESS
    else
        exitCode="$1"
    fi
    progName=`basename "$0"` 
    cat - << EOF

    Usage: ${progName} SUBCOMMAND
     or: ${progName} OPTIONS

    Manages the Web server hosting the ${progName}.

    The accepted values for SUBCOMMAND are:

    ${START_CMD}   Starts the server
    ${STOP_CMD}    Stops the server
    ${RESTART_CMD} Stop followed by start
    ${STATUS_CMD}  Display status of server
    ${ENABLE_CMD}  Enable the server to start at system boot
    ${DISABLE_CMD} Disable the server from starting at system boot

    The accepted values for OPTIONS are:

    $HELP_FLAG_SHORT, $HELP_FLAG_LONG
           Display this help list.
           
EOF
    exit $exitCode

} # usage


################################################################################
#
# isRoot
#
# Check the user ID to see if it matches root's (0).
#
# Returns:
#     Exit with status $EXIT_FAILURE if NOT root.
#
################################################################################

isRoot() {

    USERID=`id | sed -e 's/uid=\([0-9]*\).*/\1/'`

    if [ $USERID -ne 0 ]; then
        $ECHO "You must be the system's root user to manage the ${PRODUCT_NAME} server." 1>&2
        exit $EXIT_FAILURE
    fi

} # isRoot


################################################################################
#
# isProductConfigured
#
# Checks the availability of the product configuration. This is a pre-
# requirement for starting the admin GUI.
#
# Returns:
#     Exit with status $EXIT_FAILURE if product has not been configured yet.
# 
################################################################################

isProductConfigured() {
    
    # check if utadmin.conf exists
    #
    if [ ! -f "${PRODUCT_CONFIG_FILE}" ]; then
        logError "${PRODUCT_BUNDLE_NAME} has not been configured yet. Run 'utconfig' first."
        exit $EXIT_FAILURE
    fi

    # check if CATALINA_HOME has been specified in webadmin.conf
    #
    if [ ! -n "${CATALINA_HOME}" ]; then
        logError "${PRODUCT_NAME} has not been configured yet. Run 'utconfig -w' first."
        exit $EXIT_FAILURE
    fi

    # check Java runtime environment
    #
    if [ ! -f "${JAVA_HOME}/bin/java" ]; then
        logError "No Java Runtime Environment (JRE) found at ${JAVA_HOME}."
        exit $EXIT_FAILURE
    fi

    # check Tomcat runtime environment
    if [ ! -f "${CATALINA_HOME}/bin/bootstrap.jar" ]; then
        logError "No Tomcat runtime environment (bootstrap.jar) found at ${CATALINA_HOME}/bin."
        exit $EXIT_FAILURE
    fi

} # isProductConfigured


################################################################################
#
# getConfigParamter
#
# Extract the value of a given property/parameter specified in the central 
# configuration file ($CONFIG_FILE). Properties must be specified in the 
# format name=value.
#
# Parameters:
# $1 = propertyName - The name of the property
# $2 = defaultValue - (Optionally) the value that should be used as fallback in 
#                     case the property is not specified in $CONFIG_FILE
# Returns:
#    The actual value of the property as string or the default value in case the
#    property could not be found.
# 
################################################################################

getConfigParameter() {
    propertyName="$1"
    if [ $# -eq 2 ]; then
        defaultValue="$2"
    else
        defaultValue=""
    fi 

    if [ -f "${CONFIG_FILE}" ]; then
        propertyValue="`grep '^'$propertyName'=' "${CONFIG_FILE}" | cut -f2-11 -d'='`"
    fi
    if [ ! -n "$propertyValue" ]; then
        propertyValue="$defaultValue"
        logDebug "Property ${propertyName} not specified, using default '${propertyName}=${propertyValue}'"
    else 
        logDebug "Retrieved property '${propertyName}=${propertyValue}'"
    fi
    $ECHO "$propertyValue"

} # getConfigParameter


################################################################################
#
# setConfigParameter
#
# Change the value of an existing property in the central configuration file
# (specified via $CONFIG_FILE).
#
# Parameters:
# $1 = propertyName  - The name of the property
# $2 = propertyValue - The desired new value
#
################################################################################

setConfigParameter() {
    propertyName="$1"
    propertyValue="$2"
    tmpConfigFile=/tmp/webadmin.conf$$

    if [ -f "${CONFIG_FILE}" ]; then
        sed 's|^'"${propertyName}"'=.*|'"${propertyName}"'='"${propertyValue}"'|'\
            < "${CONFIG_FILE}" > $tmpConfigFile
        mv $tmpConfigFile "${CONFIG_FILE}"
        chmod 644 "${CONFIG_FILE}"
        logDebug "Changed property '${propertyName}=${propertyValue}'"
    fi         

} # setConfigParameter


################################################################################
#
# logMessage, logInfo, logDebug, logError, printInfo
#
# Log a message to the standard log file (utwebadmin.log). The log file is 
# automatically created on demand.
# 
# Parameters:
# $1 = message - The message to be logged
#
################################################################################

logMessage() {
    if [ -n "${LOG_FILE}" ]; then
        if [ ! -f "${LOG_FILE}" ]; then
            touch "${LOG_FILE}"
            chmod 640 "${LOG_FILE}"
        fi
        $ECHO "$1" >> "${LOG_FILE}"
    fi

} # logMessage

logDebug() {
    if [ $debug = "on" ]; then
        logMessage "DEBUG: $1"
    fi    
    
} # logDebug

logInfo() {
    logMessage "INFO: $1"

} # logInfo

logError() {
    $ECHO "$1" 1>&2
    logMessage "ERROR: $1"

} # logError

printInfo() {
    $ECHO "$1"

} # printInfo


################################################################################
#
# getCompleteHostName
#
# Return the unique id that represents the server machine one which the
# webserver will be running. This will be used to assign uniqueness to
# the certificate being generated. The procudure will be as follows:
#
#   - try hostname, if the hostname contains a "." it is most probably the 
#     FQDN, if we get a reply from ping, use it.
#   - if DNS configured use nslookup;  if we get a reply from ping, use it.
#   - try `hostname`.`domainname`; if we get a reply from ping,
#     use it.
#   - when all else fails, use the host name from /etc/hosts
#
################################################################################

getCompleteHostName() {

    # check if host name includes domainname
    logDebug "Try to retrieve hostname..."
    hostname=`hostname`
    common_name=`$ECHO $hostname | cut -d"." -f1`
    if [ "$common_name" = "$hostname" ]; then
        ${PING} $hostname > /dev/null 2>&1
        if [ $? -eq 0 ]; then
            # the assignment below is needed for Linux
            common_name=$hostname
            logDebug "Determined hostname via hostname command."
            $ECHO $hostname            
        fi

    # check if DNS configured
    elif [ -s /etc/resolv.conf ]; then
        common_name=`env LANG=C LC_ALL=C nslookup $hostname`
        if [ $? -eq 0 ]; then
            logDebug "Determined hostname via nslookup."
            $ECHO $common_name | sed -e 's/  / /g' -e 's/^.*Name: //' | cut -d" " -f1
        else
            # concatenate hostname and domainname and check if it is valid
            domain=`domainname`
            ${PING} "$hostname.$domain" > /dev/null 2>&1
            if [ $? -eq 0 ]; then
                logDebug "Determined hostname via domainname command."
                $ECHO $hostname.$domain
            else
                # use the IP address from /etc/hosts
                common_name=`awk '$2 == "'"$hostname"'" {print $1}' /etc/hosts`
                logDebug "Determined hostname from /etc/hosts."
                $ECHO $common_name
            fi
        fi
    else
        # concatenate hostname and domainname and check if it is valid
        domain=`domainname`
        ${PING} "$hostname.$domain" > /dev/null 2>&1
        if [ $? -eq 0 ]; then
            logDebug "Determined hostname via domainname command."
            $ECHO $hostname.$domain
        else
            # use the IP address from /etc/hosts
            common_name=`awk '$2 == "'"$hostname"'" {print $1}' /etc/hosts`
            logDebug "Determined hostname from /etc/hosts."
            $ECHO $common_name
        fi
    fi

} # getCompleteHostName


################################################################################
#
# generateKeystore
#
# Generate the keystore and install it in ${CATALINA_BASE}/conf/keystore if it
# has not been generated yet. The keystore is required for establishing
# secure connections via HTTPS.
#
################################################################################

generateKeystore() {
    if [ ! -f "${CATALINA_KEYSTORE}" ]; then
        if [ -n "${SECURE_PORT}" ] && [ ! "${SECURE_PORT}" -eq 0 ]; then
            common_name=`getCompleteHostName`
            logInfo "Generating self-signed certificate for $common_name."
            if [ -x "${JAVA_KEYTOOL}" ]; then
                ${JAVA_KEYTOOL} \
                    -genkey -alias tomcat -keyalg RSA \
                    -keypass changeit -storepass changeit \
                    -keystore "${CATALINA_KEYSTORE}" \
                    -dname "CN=$common_name, OU=Sun Ray Products and Tools, O=Sun Microsystems, L=Menlo Park, ST=Ca, C=US" \
                    -validity 2000
                if [ -f "${CATALINA_KEYSTORE}" ]; then    
                    chmod 644 "${CATALINA_KEYSTORE}"
                else
                    logError "Certificate generation failed."
                fi
            else
                logError "Can not generate self-signed certificate - access to keytool ${JAVA_KEYTOOL} failed."
            fi
        fi
    fi

} # generateKeystore


################################################################################
#
# createServerConfiguration
#
# Merge the server.xml and web.xml templates with the configuration
# settings to create the actual configuration files used by Tomcat.
#
################################################################################

createServerConfiguration() {

    # check if the shutdown port is already in use - if yes, we try to find
    # a new available port in the dynamical port region (starting at 49152)
    #
    netstat -na | grep LISTEN | grep -w "$SHUTDOWN_PORT" >> /dev/null
    STATUS=$?
    if [ $STATUS -eq 0 ]; then
        logDebug "Shutdown port $SHUTDOWN_PORT is already in use."
        SHUTDOWN_PORT=49152
        while [ $STATUS -eq 0 ]
        do
            logDebug "Checking now port ${SHUTDOWN_PORT}..."
            netstat -na | grep LISTEN | grep -w "$SHUTDOWN_PORT" >> /dev/null
            STATUS=$?
            SHUTDOWN_PORT=`expr ${SHUTDOWN_PORT} + 1`
        done
        setConfigParameter "shutdown.port" "$SHUTDOWN_PORT"
        logDebug "Adapted shutdown port to ${SHUTDOWN_PORT}."
    fi
    
    # check if any adaptations have been done to the configuration
    #
    if [ ! -f "${SERVER_FILE}" ] || [ ! -f "${WEB_FILE}" ]; then
        configChanges="yes"
    else
        configChanges=`find "${CONFIG_FILE}" -prune -newer "${SERVER_FILE}"`
    fi
    if [ -n "$configChanges" ]; then
        tmpServerFile=/tmp/pllserver.$$
        tmpWebFile=/tmp/pllweb.$$
        logDebug "Adapting server.xml and web.xml files according to configuration."

        if [ ! -n "${SECURE_PORT}" ] || [ "${SECURE_PORT}" -eq 0 ] || [ ! -f "${CATALINA_KEYSTORE}" ]; then
            logDebug "Configuring unsecure HTTP connector."

            # server.xml file - remove HTTPS connector
            #
            sed -e '/PllHttpsPort/,/Connector/ d'\
                -e '/PllRedirectPort/ d'\
                < "${SERVER_TEMPLATE_FILE}" > "$tmpServerFile"
            
            # web.xml file - remove security contraint causing redirection
            # 
            sed -e '/security-constraint/,/security-constraint/ d'\
                < "${WEB_TEMPLATE_FILE}" > "$tmpWebFile"

        else
            logDebug "Configuring secure HTTPS connector."
            cp -f "${SERVER_TEMPLATE_FILE}" "$tmpServerFile"
            cp -f "${WEB_TEMPLATE_FILE}" "$tmpWebFile"
        fi
        
        # server.xml file - fill in port values and access filter
        #
        sed -e 's/PllHttpPort/'"${UNSECURE_PORT}"'/'\
            -e 's/PllHttpsPort/'"${SECURE_PORT}"'/'\
            -e 's/PllRedirectPort/'"${SECURE_PORT}"'/'\
            -e 's/PllShutdownPort/'"${SHUTDOWN_PORT}"'/'\
            -e 's/PllRemoteAdmin/'"${REMOTE_ACCESS}"'/'\
            < "$tmpServerFile" > "${SERVER_FILE}"

        # web.xml file - fill in HTTP port for Java Help access, session 
        # timeout value and log level
        #
        sed -e 's/PllHttpPort/'"${UNSECURE_PORT}"'/'\
            -e 's/PllSessionTimeout/'"${SESSION_TIMEOUT}"'/'\
            -e 's/PllLoggingLevel/'"${LOGGING_LEVEL}"'/'\
            < "$tmpWebFile" > "${WEB_FILE}"

        rm -f "$tmpServerFile"
        rm -f "$tmpWebFile"
    fi

} # createServerConfiguration


################################################################################
#
# waitForProcessExit
#
# Wait for a specified max time for the process exit. This allows the process
# to gracefully shutdown.
#
# $1 = PID of process to wait for
# $2 = timeout (seconds)
#
################################################################################

waitForProcessExit() {
    processID=$1
    timeout=$2

    t=0
    while [ $t -lt $timeout ] ; do
        sleep 1
        t=`(expr $t + 1)`
        if [ -n "`/bin/ps -deaf | awk '{print $2}' | grep $processID`" ]; then
            logDebug "Waiting ${t} second(s) for process (pid ${processID}) exit ..."
            continue
        else
            logDebug "Server process (pid ${processID}) exited."
            break
        fi
    done

} # waitForProcessExit


################################################################################
#
# checkAccessPermissions
#
# Ensure that the various files (log files, configuration files, etc.) have the
# correct permissions. This is our safety belt, if the permissions gets 
# corrupted for whatever reasons. 
#
################################################################################

checkAccessPermissions() {

    logDebug "Checking access permissions."
    
    # Work and Log directories must be writeable by the user identity 
    # of the server process. Root writes to log directory too, but the
    # ownership and permissions are irrelevant.
    #
    chown ${PROCESS_USERNAME}:${PROCESS_GROUPNAME} $LOG_FILE
    if [ -d "$CATALINA_WORK" ]; then
        chown -R ${PROCESS_USERNAME}:${PROCESS_GROUPNAME} $CATALINA_WORK
    fi

    # Product config file must have the correct group ownership for 
    # accessing utguiauthd - otherwise authentication will always fail
    #
    if [ -f "$PRODUCT_CONFIG_FILE" ]; then
        chgrp ${PROCESS_GROUPNAME} $PRODUCT_CONFIG_FILE
    fi

    # Ensure that log files are accessible
    # 
    chgrp ${PROCESS_GROUPNAME} ${PRODUCT_LOG_DIR}/messages* > /dev/null
    chmod g+r ${PRODUCT_LOG_DIR}/messages* > /dev/null
    chgrp ${PROCESS_GROUPNAME} ${PRODUCT_LOG_DIR}/*_log* > /dev/null
    chmod g+r ${PRODUCT_LOG_DIR}/*_log* > /dev/null

} # checkAccessPermissions


################################################################################
#
# serverStart
#
# Handler for the "start" subcommand.
#
################################################################################

serverStart () {

    # if we have a PID, then we should be already running.  But just
    # in case we were killed directly, we should check that the PID
    # is valid.
    #
    if [ -f ${PROCESS_PIDFILE} ]; then
        PID=`cat ${PROCESS_PIDFILE}`
        if [ -n "`/bin/ps -deaf | awk '{print $2}' | grep $PID`" ]; then
            printInfo "${PRODUCT_NAME} (pid ${PID}) already running"
            exit $EXIT_SUCCESS
        fi
        rm -f ${PROCESS_PIDFILE}
    fi
    
    # server is not running - thus check if the product is correctly
    # configured and all information for startup is available
    isProductConfigured
    
    # start a new log file
    ${UT_AGELOG} "${LOG_FILE}" ${MAX_ARCHIVED_LOGS}

    printInfo "Starting ${PRODUCT_NAME}..."
    generateKeystore
    checkAccessPermissions
    createServerConfiguration

    # Note: java.security.policy must be specified as shown
    # (using '==') so that we use ONLY the specified policy 
    # file (policy.conf) and ignore all others.
    #
    SECURITY_ARGS="-Djava.security.manager \
        -Djava.security.policy==${CATALINA_POLICY}"

    # Add any special library search paths for the JVM
    #
    LINK_ARGS=""
    if [ "${JAVA_LIBRARY_PATH}" != "" ]; then
        LINK_ARGS="-Djava.library.path=${JAVA_LIBRARY_PATH}"
    fi

    # Create a temporary startup script.  Note that always set JVM
    # parameters such that the ones we do not want to be overridden
    # (such as classpath) must appear after the user-settable option.
    #
    # Note also that there are 2 possible failure cases when trying to
    # launch the script under a different identity:
    #   1) failure to assume the specified identity,
    #   2) failure to launch the JVM
    # We have no fullproof way of checking for 1) without at least trying
    # it.  So the script dumps its process owner to a temp file, so we
    # can check for it's existence later and compare it's value against
    # whom we think we launched as.
    
    TMP_SH=/tmp/uttomcat.$$
    TMP_PIDFILE=/tmp/utpid.$$
    TMP_IDFILE=/tmp/utid.$$

    logDebug "Creating temporary startup script ${TMP_SH} ..."
    $ECHO "#!/bin/sh" > ${TMP_SH}
    $ECHO "" >> ${TMP_SH}
    $ECHO "id > $TMP_IDFILE" >> $TMP_SH

    if [ -n "${LD_LIBRARY_PATH}" ]; then
        $ECHO "LD_LIBRARY_PATH=${LD_LIBRARY_PATH} " >> ${TMP_SH}
        $ECHO "export LD_LIBRARY_PATH " >> ${TMP_SH}
    fi
    
    $ECHO "" >> ${TMP_SH}
    $ECHO "if [ ! -r \"${CATALINA_HOME}/bin/bootstrap.jar\" ]; then" >> ${TMP_SH}
    $ECHO "    echo \"ERROR: Can not access Tomcat runtime environment (bootstrap.jar) at ${CATALINA_HOME}/bin.\" \\" >> ${TMP_SH}
    $ECHO "    >> ${LOG_FILE}" >> ${TMP_SH}
    $ECHO "    echo \"ERROR: This may be due to one of the following:\" >> ${LOG_FILE}" >> ${TMP_SH}
    $ECHO "    echo \"ERROR:  - No permissions to read the ${CATALINA_HOME}/bin directory as user ${PROCESS_USERNAME}.\" \\" >> ${TMP_SH}
    $ECHO "    >> ${LOG_FILE}" >> ${TMP_SH}
    $ECHO "    exit ${EXIT_FAILURE}" >> ${TMP_SH}
    $ECHO "fi" >> ${TMP_SH}

    $ECHO "" >> ${TMP_SH}
    $ECHO "${JAVA} ${JAVA_OPTS} \\" >> ${TMP_SH}
    $ECHO "    ${SECURITY_ARGS} \\" >> ${TMP_SH}
    $ECHO "    ${LINK_ARGS} \\" >> ${TMP_SH}
    $ECHO "    ${CATALINA_OPTS} \\" >> ${TMP_SH}
    $ECHO "    -classpath ${JAVA_CLASSPATH} \\" >> ${TMP_SH}
    $ECHO "    -Dcatalina.home=${CATALINA_HOME} \\" >> ${TMP_SH}
    $ECHO "    -Dcatalina.base=${CATALINA_BASE} \\" >> ${TMP_SH}
    $ECHO "    -Djava.awt.headless=true \\" >> ${TMP_SH}
    $ECHO "    org.apache.catalina.startup.Bootstrap start \\" >> ${TMP_SH}
    $ECHO "    >> ${LOG_FILE} 2>&1 &" >> ${TMP_SH}
    $ECHO "SERVER_PID=\$!" >> ${TMP_SH}
    $ECHO "echo \${SERVER_PID} > ${TMP_PIDFILE}" >> ${TMP_SH}

    # Startup script must be executable and owned by user identity we 
    # want to run as.
    chmod 755 ${TMP_SH}
    chown  ${PROCESS_USERNAME} ${TMP_SH} >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        logError "Startup failed: user identity \"${PROCESS_USERNAME}\" does not exist."
        rm -f ${TMP_SH} ${TMP_PIDFILE} > /dev/null
        exit $EXIT_FAILURE
    fi

    # Must change the current working directory to the application base.
    cd "${CATALINA_BASE}"

    # WARNING!  The code below here that manages the launch is not for
    # the faint-of-heart!  There are numerous system conditions taken
    # into account here as behavior is different on each Unix platform
    # under each of those conditions. This logic has been taken over from
    # the Java webconsole.
    #

    # Change to our process user identity to start the daemon process.
    if [ "${LAUNCH_COMMAND}" = "su" ]; then

    # Some systems have the identity's home directory the same as root's,
    # and this is typically the case for "noaccess" on Solaris.
    # But those systems may also have restricted access to root's home
    # directory.  So let's try a simple test to see if we can at least
    # assume that user's identity.
    #
    su ${SU_SHELL} ${PROCESS_USERNAME} -c "pwd >/dev/null 2>&1" >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        logError "Startup failed: unable to become the user identity \"${PROCESS_USERNAME}\"."
        logError "This may be due to one of the following:"
        logError "  - No permissions to read the ${PROCESS_USERNAME} home directory."
        logError "  - No permissions to read the root (/) home directory."
        logError "  - Invalid login shell for the user ${PROCESS_USERNAME}."
        rm -f ${TMP_SH} >/dev/null
        exit $EXIT_FAILURE
    fi

    # Startup script running under specified identity will attempt to
    # create temporary files in a given directory.  The directory 
    # containing those files must be writeable by that user identity.
    # So we assume the identity and verify if we have write permissions,
    # exitting fatally if we don't.
    #
    touchFile=`dirname $TMP_SH`/tiptoe.$$
    su ${SU_SHELL} ${PROCESS_USERNAME} -c "$ECHO \"\" > $touchFile 2>&1" >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        logError "Startup failed: the user identity \"${PROCESS_USERNAME}\""
        logError "does not have permissions to write to the directory `dirname $TMP_SH`."
        rm -f $touchFile $TMP_SH > /dev/null
        exit $EXIT_FAILURE
    fi
    rm -f $touchFile

    logDebug "Executing startup script ${TMP_SH} with user identity ${PROCESS_USERNAME}."
    su ${SU_SHELL} ${PROCESS_USERNAME} -c "nohup ${TMP_SH} >/dev/null 2>&1"

    else
        logError "Startup failed: invalid launch command - ${LAUNCH_COMMAND}"
        rm -f ${TMP_SH} >/dev/null
        exit $EXIT_FAILURE
    fi

    # If the startup script at least ran, extract the identity it ran under.
    launchedID=""
    if [ -f $TMP_IDFILE ]; then
        launchedID=`cat $TMP_IDFILE | awk '{print $1}' | \
            sed -e 's/uid=\([0-9]*\)//' -e 's/(//' -e 's/)//'`
    fi
    rm -f $TMP_IDFILE

    # Fatal if unable to verify the identity we thought we launched as.
    if [ ! -n "$launchedID" -o "$launchedID" != ${PROCESS_USERNAME} ]; then
        logError "Startup failed: cannot assume user identity \"${PROCESS_USERNAME}\"."
        logError "Check to make sure \"${PROCESS_USERNAME}\" has a valid login shell."
        rm -f ${TMP_SH} ${TMP_PIDFILE} > /dev/null
        exit $EXIT_FAILURE
    fi

    # If we get here, we were at least successful in assuming the
    # the specified identity to run the server, and we at least tried
    # to launch the JVM.

    # A non-existent or zero-length PID file indicates some error occurred 
    # in launching the JVM.
    if [ ! -f ${TMP_PIDFILE} -o ! -s ${TMP_PIDFILE} ]; then
        logError "Startup failed. See ${LOG_FILE} for detailed error information."
        rm -f ${TMP_SH} ${TMP_PIDFILE} > /dev/null
        exit $EXIT_FAILURE
    fi

    # Check to make sure server has started.  We should see the PID from
    # a `ps' output.
    #
    PID=`cat ${TMP_PIDFILE}`
    if [ ! -n "`/bin/ps -deaf | awk '{print $2}' | grep $PID`" ]; then
        logError "Startup failed. See ${LOG_FILE} for detailed error information."
        rm -f ${TMP_SH} ${TMP_PIDFILE} > /dev/null
        exit $EXIT_FAILURE
    fi

    # When we get here, the server is ready.
    cp ${TMP_PIDFILE} ${PROCESS_PIDFILE}
    rm -f ${TMP_PIDFILE}
    rm ${TMP_SH}

} # serverStart


################################################################################
#
# serverStop
#
# Handler for the "stop" subcommand.
#
################################################################################

serverStop () {

    # If we don't have a PID, then we should not be running.
    # If client's don't want to see msg, they can run "status" subcommand.
    #
    if [ ! -f ${PROCESS_PIDFILE} ]; then
        printInfo "${PRODUCT_NAME} not running - ${PROCESS_PIDFILE} does not exist"
        return $EXIT_FAILURE
    fi

    printInfo "Shutting down ${PRODUCT_NAME}..."
    
    # We first try to stop the process via the Catalina stop command
    #
    if [ -n "${CATALINA_HOME}" ]; then
        logDebug "Triggered Catalina stop command for server process."
        ${JAVA} ${JAVA_OPTS} -classpath ${JAVA_CLASSPATH} \
        ${CATALINA_OPTS} \
        -Dcatalina.base=${CATALINA_BASE} \
        -Dcatalina.home=${CATALINA_HOME} \
        org.apache.catalina.startup.Bootstrap stop >/dev/null 2>&1
    fi

    # The above should gracefully shutdown the server.  However it's possible
    # certain errors may prevent a gracefull shutdown.  So as a catch-all,
    # we check to see if the process is still alive - if it is, kill it.
    #
    PID=`cat ${PROCESS_PIDFILE}`
    waitForProcessExit $PID 10
    if [ -n "`/bin/ps -deaf | awk '{print $2}' | grep $PID`" ]; then
        logDebug "Killed server process (pid $PID)."
        kill -9 $PID >/dev/null 2>&1
    fi
    rm -f ${PROCESS_PIDFILE}

} # serverStop


################################################################################
#
# serverStatus
#
# Handler for the "status" subcommand.
#
################################################################################

serverStatus () {
    # Check if the status command is used within a scripted environment
    # and the status should be returned via parseable output.
    #
    parseableMode=0
    if [ $# -eq 1 ]; then
        if [ $1 = ${PARSEABLE_FLAG_SHORT} ]; then
            parseableMode=1
        fi    
    fi

    # If we don't have a PID, then we should not be running.
    #
    if [ ! -f ${PROCESS_PIDFILE} ]; then
        # Server not running
        #
        if [ $parseableMode -eq 0 ]; then
            printInfo "${PRODUCT_NAME} is not running."
        else
            $ECHO "${SERVER_NOT_RUNNING}"
        fi

    else
        # Server running.
        # If we have a PID, then we should be running.  But just
        # in case we were killed directly, we should check that the PID
        # is valid.
        #
        PID=`cat ${PROCESS_PIDFILE}`
        if [ -n "`/bin/ps -deaf | awk '{print $2}' | grep $PID`" ]; then
            if [ $parseableMode -eq 0 ]; then
                printInfo "${PRODUCT_NAME} is running (pid ${PID})."
            else 
                $ECHO "${SERVER_RUNNING}"
            fi        
            
        else 
            if [ $parseableMode -eq 0 ]; then
                printInfo "${PRODUCT_NAME} is not running."
            else
                $ECHO "${SERVER_NOT_RUNNING}"
            fi
            rm -f ${PROCESS_PIDFILE}
        fi
    fi

} # serverStatus


################################################################################
#
# Main function area for register
#
# Parameters work as follows:
#
# $1 = subcommand | -h
# $2 = optional flag
#
################################################################################

main() {
    subcommand=$1
    case "$subcommand" in

    "$HELP_FLAG_SHORT" | "$HELP_FLAG_LONG" )
    usage
    ;;

    "$START_CMD")
    isRoot
    setEnvironment
    serverStart
    if [ $? -eq $EXIT_SUCCESS ] ; then
        printInfo "See ${LOG_FILE} for server logging information."
    fi
    ;;

    "$STOP_CMD")
    isRoot
    setEnvironment
    serverStop
    if [ $? -eq $EXIT_SUCCESS ] ; then
        printInfo "See ${LOG_FILE} for server logging information."
    fi
    ;;

    "$STATUS_CMD")
    args=""
    if [ $# -eq 2 ]; then
        args=`$ECHO "$2" | sed -e "s/${PARSEABLE_FLAG_LONG}/${PARSEABLE_FLAG_SHORT}/"`
    fi
    isRoot
    setEnvironment
    serverStatus $args
    ;;
    
    "$ENABLE_CMD")
    isRoot
    setEnvironment
    setConfigParameter auto.start true
    printInfo "${PRODUCT_NAME} enabled to start at system boot."
    ;;

    "$DISABLE_CMD")
    isRoot
    setEnvironment
    setConfigParameter auto.start false
    printInfo "${PRODUCT_NAME} disabled from starting at system boot."
    ;;

    "$RESTART_CMD")
    isRoot
    setEnvironment
    if [ "`serverStatus ${PARSEABLE_FLAG_SHORT}`" = "${SERVER_RUNNING}" ]; then
        serverStop
    fi    
    serverStart
    printInfo "See ${LOG_FILE} for server logging information."
    ;;

    *)
    usage $EXIT_FAILURE 1>&2
    ;;

    esac

    exit $EXIT_SUCCESS

} ## main


main $*
##  DO NOT ADD ANY CODE BELOW HERE!!

