#!/bin/sh -X

# From "http://fedoraproject.org/wiki/FCNewInit/Initscripts":
#
# Status Exit Codes
#
#  0 program is running or service is OK
#  1 program is dead and /var/run pid file exists
#  2 program is dead and /var/lock lock file exists
#  3 program is not running
#  4 program or service status is unknown
#  5-99 reserved for future LSB use
#  100-149 reserved for distribution use
#  150-199 reserved for application use
#  200-254 reserved
#
# Non-Status Exit Codes
#
#  0 action was successful
#  1 generic or unspecified error (current practice)
#  2 invalid or excess argument(s)
#  3 unimplemented feature (for example, "reload")
#  4 user had insufficient privilege
#  5 program is not installed
#  6 program is not configured
#  7 program is not running
#  8-99    reserved for future LSB use
#  100-149 reserved for distribution use
#  150-199 reserved for application use
#  200-254 reserved
#

# default PKI configuration
. /usr/share/pki/etc/pki.conf

# system-wide PKI configuration
if [ -f /etc/pki/pki.conf ]
then
    . /etc/pki/pki.conf
fi

default_error=0

if [ -f /etc/debian_version ]; then
    debian=true
else
    debian=false
fi

case $command in
    start)
        # 1 generic or unspecified error (current practice)
        default_error=1
        ;;
    stop|restart)
        if $debian; then
            default_error=1
        else
            default_error=2
        fi
        ;;
    status)
        # 4 program or service status is unknown
        default_error=4
        ;;
    *)
        # 2 invalid argument(s)
        default_error=2
        ;;
esac

# Enable nullglob, if set then shell pattern globs which do not match any
# file returns the empty string rather than the unmodified glob pattern.
shopt -s nullglob

OS=`uname -s`
ARCHITECTURE=`arch`

# Check to insure that this script's original invocation directory
# has not been deleted!
CWD=`/bin/pwd > /dev/null 2>&1`
if [ $? -ne 0 ] ; then
    echo "Cannot invoke '$PROG_NAME' from non-existent directory!"
    exit ${default_error}
fi

# Check to insure that this script's associated PKI
# subsystem currently resides on this system.
PKI_CA_PATH="/usr/share/pki/ca"
PKI_KRA_PATH="/usr/share/pki/kra"
PKI_OCSP_PATH="/usr/share/pki/ocsp"
PKI_TKS_PATH="/usr/share/pki/tks"
PKI_TPS_PATH="/usr/share/pki/tps"
if [ ! -d ${PKI_CA_PATH} ]   &&
   [ ! -d ${PKI_KRA_PATH} ]  &&
   [ ! -d ${PKI_OCSP_PATH} ] &&
   [ ! -d ${PKI_TKS_PATH} ]  &&
   [ ! -d ${PKI_TPS_PATH} ] ; then
    echo "This machine is missing all PKI '${PKI_TYPE}' subsystems!"
    if [ "${command}" != "status" ]; then
        # 5 program is not installed
        exit 5
    else
        exit ${default_error}
    fi
fi

PKI_REGISTRY_ENTRIES=""
PKI_SUBSYSTEMS=""
TOTAL_PKI_REGISTRY_ENTRIES=0
TOTAL_UNCONFIGURED_PKI_ENTRIES=0

# Gather ALL registered instances of this PKI web server type
for INSTANCE in ${PKI_REGISTRY}/*; do
    if [ -d "$INSTANCE" ] ; then
        for REGISTRY in ${INSTANCE}/*; do
            if [ -f "$REGISTRY" ] ; then
                PKI_REGISTRY_ENTRIES="${PKI_REGISTRY_ENTRIES} $REGISTRY"
                TOTAL_PKI_REGISTRY_ENTRIES=`expr ${TOTAL_PKI_REGISTRY_ENTRIES} + 1`
            fi
        done
    fi
done

# Execute the specified registered instance of this PKI web server type
if [ -n "${pki_instance_id}" ]; then
    for INSTANCE in ${PKI_REGISTRY_ENTRIES}; do
        if [ "`basename ${INSTANCE}`" == "${pki_instance_id}" ]; then
            PKI_REGISTRY_ENTRIES="${INSTANCE}"
            TOTAL_PKI_REGISTRY_ENTRIES=1
            break
        fi
    done
fi

usage()
{
    echo -n "Usage: /usr/bin/pkidaemon "
    echo -n "{start"
    if $debian; then
        echo -n "|stop|restart"
    fi
    echo -n "|status} "
    echo -n "[instance-name]"
    echo
    echo
}

list_instances()
{
    echo
    for INSTANCE in /etc/sysconfig/pki/tomcat/*; do
        if [ -d "${INSTANCE}" ] ; then
            instance_name=`basename ${INSTANCE}`
            echo "    $instance_name"
        fi
    done
    echo
}

get_subsystems()
{
    # Re-initialize PKI_SUBSYSTEMS for each instance
    PKI_SUBSYSTEMS=""
    case ${PKI_WEB_SERVER_TYPE} in
        tomcat)
            for SUBSYSTEM in ca kra ocsp tks tps; do
                if [ -d ${PKI_INSTANCE_PATH}/conf/${SUBSYSTEM} ]; then
                    if [ '${PKI_SUBSYSTEMS}' == "" ] ; then
                        PKI_SUBSYSTEMS="${SUBSYSTEM}"
                    else
                        PKI_SUBSYSTEMS="${PKI_SUBSYSTEMS} ${SUBSYSTEM}"
                    fi
                fi
            done
            ;;
        *)
            echo "Unknown web server type ($PKI_WEB_SERVER_TYPE)"
            exit ${default_error}
            ;;
    esac
}

# Check arguments
if [ $# -lt 1 ] ; then
    #     [insufficient arguments]
    echo "$PROG_NAME:  Insufficient arguments!"
    echo
    usage
    echo "where valid instance names include:"
    list_instances
    exit 3
elif [ ${default_error} -eq 2 ] ; then
    # 2 invalid argument
    echo "$PROG_NAME:  Invalid arguments!"
    echo
    usage
    echo "where valid instance names include:"
    list_instances
    exit 2
elif [ $# -gt 2 ] ; then
    echo "$PROG_NAME:  Excess arguments!"
    echo
    usage
    echo "where valid instance names include:"
    list_instances
    if [ "${command}" != "status" ]; then
        # 2 excess arguments
        exit 2
    else
        # 4 program or service status is unknown
        exit 4
    fi
fi

# If an "instance" was supplied, check that it is a "valid" instance
if [ -n "${pki_instance_id}" ]; then
    valid=0
    for PKI_REGISTRY_ENTRY in $PKI_REGISTRY_ENTRIES; do
        instance_name=`basename $PKI_REGISTRY_ENTRY`
        if [ "${pki_instance_id}" == "${instance_name}" ]; then
            valid=1
            break
        fi
    done
    if [ $valid -eq 0 ]; then
        echo -n "${pki_instance_id} is an invalid '${PKI_TYPE}' instance"
        echo

        if [ "${command}" != "status" ]; then
            # 5 program is not installed
            exit 5
        else
            # 4 program or service status is unknown
            exit 4
        fi
    fi
fi

check_pki_configuration_status()
{
    rv=0
    case ${PKI_WEB_SERVER_TYPE} in
        tomcat)
            for SUBSYSTEM in ca kra ocsp tks tps; do
                if [ -d ${PKI_INSTANCE_PATH}/conf/${SUBSYSTEM} ]; then
                    rv=`grep -c ^preop ${PKI_INSTANCE_PATH}/conf/${SUBSYSTEM}/CS.cfg`
                    rv=`expr ${rv} + 0`
                fi
            done
            ;;
        *)
            echo "Unknown web server type ($PKI_WEB_SERVER_TYPE)"
            exit ${default_error}
            ;;
    esac
    if [ $rv -ne 0 ] ; then
        echo "    '${PKI_INSTANCE_NAME}' must still be CONFIGURED!"
        echo "    (see /var/log/${PKI_INSTANCE_NAME}-install.log)"
        if [ "${command}" != "status" ]; then
            # 6 program is not configured
            rv=6
        else
            # 4 program or service status is unknown
            rv=4
        fi
        TOTAL_UNCONFIGURED_PKI_ENTRIES=`expr ${TOTAL_UNCONFIGURED_PKI_ENTRIES} + 1`
    elif [ -f ${RESTART_SERVER} ] ; then
        echo -n "    Although '${PKI_INSTANCE_NAME}' has been CONFIGURED, "
        echo -n "it must still be RESTARTED!"
        echo
        if [ "${command}" != "status" ]; then
            # 1 generic or unspecified error (current practice)
            rv=1
        else
            # 4 program or service status is unknown
            rv=4
        fi
    fi

    return $rv
}

get_pki_status_definitions()
{
    case $PKI_WEB_SERVER_TYPE in
        tomcat)
            PKI_SERVER_XML_CONF=${PKI_INSTANCE_PATH}/conf/server.xml
            get_pki_status_definitions_tomcat
            return $?
            ;;
        *)
            echo "Unknown web server type ($PKI_WEB_SERVER_TYPE)"
            exit ${default_error}
            ;;
    esac
}

get_pki_status_definitions_tomcat()
{
    # establish well-known strings
    begin_pki_status_comment="<!-- DO NOT REMOVE - Begin PKI Status Definitions -->"
    begin_ca_status_comment="<!-- CA Status Definitions -->"
    begin_kra_status_comment="<!-- KRA Status Definitions -->"
    begin_ocsp_status_comment="<!-- OCSP Status Definitions -->"
    begin_tks_status_comment="<!-- TKS Status Definitions -->"
    begin_tps_status_comment="<!-- TPS Status Definitions -->"
    end_pki_status_comment="<!-- Begin DO NOT REMOVE - End PKI Status Definitions -->"
    total_ports=0
    unsecure_port_statement="Unsecure Port"
    secure_agent_port_statement="Secure Agent Port"
    secure_ee_port_statement="Secure EE Port"
    secure_ee_client_auth_port_statement="EE Client Auth Port"
    secure_admin_port_statement="Secure Admin Port"
    pki_console_port_statement="PKI Console Port"
    unsecure_url_statement="Unsecure URL"
    secure_url_statement="Secure URL"
    secure_agent_url_statement="Secure Agent URL"
    secure_ee_url_statement="Secure EE URL"
    secure_ee_client_auth_url_statement="EE Client Auth URL"
    secure_admin_url_statement="Secure Admin URL"
    pki_console_command_statement="PKI Console Command"
    tomcat_port_statement="Tomcat Port"
    unsecure_phone_home_statement="Unsecure PHONE HOME"
    secure_phone_home_statement="Secure PHONE HOME"

    # initialize looping variables
    pki_status_comment_found=0
    display_pki_ca_status_banner=0
    display_pki_kra_status_banner=0
    display_pki_ocsp_status_banner=0
    display_pki_tks_status_banner=0
    display_pki_tps_status_banner=0
    process_pki_ca_status=0
    process_pki_kra_status=0
    process_pki_ocsp_status=0
    process_pki_tks_status=0
    process_pki_tps_status=0

    # first check to see that an instance-specific "server.xml" file exists
    if [ ! -f ${PKI_SERVER_XML_CONF} ] ; then
        echo "File '${PKI_SERVER_XML_CONF}' does not exist!"
        exit ${default_error}
    fi

    # identify all PKI subsystems present within this PKI instance
    if [ -e ${PKI_INSTANCE_PATH}/ca ]; then
        display_pki_ca_status_banner=1
    fi
    if [ -e ${PKI_INSTANCE_PATH}/kra ]; then
        display_pki_kra_status_banner=1
    fi
    if [ -e ${PKI_INSTANCE_PATH}/ocsp ]; then
        display_pki_ocsp_status_banner=1
    fi
    if [ -e ${PKI_INSTANCE_PATH}/tks ]; then
        display_pki_tks_status_banner=1
    fi
    if [ -e ${PKI_INSTANCE_PATH}/tps ]; then
        display_pki_tps_status_banner=1
    fi

    # read this instance-specific "server.xml" file line-by-line
    # to obtain the current PKI Status Definitions
    exec < ${PKI_SERVER_XML_CONF}
    while read line; do
        # first look for the well-known end PKI Status comment
        # (to turn off processing)
        if [ "$line" == "$end_pki_status_comment" ] ; then
            # always turn off processing TPS status at this point
            process_pki_tps_status=0
            pki_status_comment_found=0
            break;
        fi

        # then look for the well-known begin PKI Status comment
        # (to turn on processing)
        if [ "$line" == "$begin_pki_status_comment" ] ; then
            pki_status_comment_found=1
        fi

        # once the well-known begin PKI Status comment has been found,
        # begin processing to obtain all of the PKI Status Definitions
        if [ $pki_status_comment_found -eq 1 ] ; then
            head=`echo "$line" | sed -e 's/^\([^=]*\)[ \t]*= .*$/\1/' -e 's/[ \t]*$//'`
            if   [ "$line" == "$begin_ca_status_comment" ] ; then
                 if [ $display_pki_ca_status_banner -eq 1 ] ; then
                     # print CA Status Definition banner
                     echo
                     echo "    [CA Status Definitions]"
                     # turn on processing CA status at this point
                     process_pki_ca_status=1
                 fi
            elif [ "$line" == "$begin_kra_status_comment" ] ; then
                 # always turn off processing CA status at this point
                 process_pki_ca_status=0
                 if [ $display_pki_kra_status_banner -eq 1 ] ; then
                     # print KRA Status Definition banner
                     echo
                     echo "    [KRA Status Definitions]"
                     # turn on processing KRA status at this point
                     process_pki_kra_status=1
                 fi
            elif [ "$line" == "$begin_ocsp_status_comment" ] ; then
                 # always turn off processing KRA status at this point
                 process_pki_kra_status=0
                 if [ $display_pki_ocsp_status_banner -eq 1 ] ; then
                     # print OCSP Status Definition banner
                     echo
                     echo "    [OCSP Status Definitions]"
                     # turn on processing OCSP status at this point
                     process_pki_ocsp_status=1
                 fi
            elif [ "$line" == "$begin_tks_status_comment" ] ; then
                 # always turn off processing OCSP status at this point
                 process_pki_ocsp_status=0
                 if [ $display_pki_tks_status_banner -eq 1 ] ; then
                     # print TKS Status Definition banner
                     echo
                     echo "    [TKS Status Definitions]"
                     # turn on processing TKS status at this point
                     process_pki_tks_status=1
                 fi
            elif [ "$line" == "$begin_tps_status_comment" ] ; then
                 # always turn off processing TKS status at this point
                 process_pki_tks_status=0
                 if [ $display_pki_tps_status_banner -eq 1 ] ; then
                     # print TPS Status Definition banner
                     echo
                     echo "    [TPS Status Definitions]"
                     # turn on processing TPS status at this point
                     process_pki_tps_status=1
                 fi
            elif [ $process_pki_ca_status -eq 1 ]    ||
                 [ $process_pki_kra_status -eq 1 ]   ||
                 [ $process_pki_ocsp_status -eq 1 ]  ||
                 [ $process_pki_tks_status -eq 1 ]   ||
                 [ $process_pki_tps_status -eq 1 ] ; then
                 # look for a PKI Status Definition and print it
                 if [ "$head" == "$unsecure_port_statement"     ]          ||
                    [ "$head" == "$secure_agent_port_statement" ]          ||
                    [ "$head" == "$secure_ee_port_statement"    ]          ||
                    [ "$head" == "$secure_admin_port_statement" ]          ||
                    [ "$head" == "$secure_ee_client_auth_port_statement" ] ||
                    [ "$head" == "$pki_console_port_statement"  ]          ||
                    [ "$head" == "$unsecure_url_statement"      ]          ||
                    [ "$head" == "$secure_url_statement"        ]          ||
                    [ "$head" == "$secure_agent_url_statement"  ]          ||
                    [ "$head" == "$secure_ee_url_statement"     ]          ||
                    [ "$head" == "$secure_admin_url_statement"  ]          ||
                    [ "$head" == "$secure_ee_client_auth_url_statement" ]  ||
                    [ "$head" == "$pki_console_command_statement"  ]       ||
                    [ "$head" == "$unsecure_phone_home_statement"  ]       ||
                    [ "$head" == "$secure_phone_home_statement"    ]       ||
                    [ "$head" == "$tomcat_port_statement"       ] ; then
                     echo "    $line"
                     total_ports=`expr ${total_ports} + 1`
                 fi
            fi
        fi
    done

    return 0;
}

get_pki_configuration_definitions()
{
    # Obtain the PKI Subsystem Type
    line=`grep -e '^[ \t]*cs.type[ \t]*=' ${PKI_SUBSYSTEM_CONFIGURATION_FILE}`
    pki_subsystem=`echo "${line}" | sed -e 's/^[^=]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
    if [ "${line}" != "" ] ; then
        if  [ "${pki_subsystem}" != "CA"   ]  &&
            [ "${pki_subsystem}" != "KRA"  ]  &&
            [ "${pki_subsystem}" != "OCSP" ]  &&
            [ "${pki_subsystem}" != "TKS"  ]  &&
            [ "${pki_subsystem}" != "TPS"  ]
        then
            return ${default_error}
        fi
    else
        return ${default_error}
    fi

    # If "${pki_subsystem}" is a CA, KRA, OCSP, or TKS,
    # check to see if "${pki_subsystem}" is a "Clone"
    pki_clone=""
    if  [ "${pki_subsystem}" == "CA"   ]  ||
        [ "${pki_subsystem}" == "KRA"  ]  ||
        [ "${pki_subsystem}" == "OCSP" ]  ||
        [ "${pki_subsystem}" == "TKS"  ]
    then
        line=`grep -e '^[ \t]*subsystem.select[ \t]*=' ${PKI_SUBSYSTEM_CONFIGURATION_FILE}`
        if [ "${line}" != "" ] ; then
            pki_clone=`echo "${line}" | sed -e 's/^[^=]*[ \t]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
            if [ "${pki_clone}" != "Clone" ] ; then
                # Reset "${pki_clone}" to be empty
                pki_clone=""
            fi
        else
            return ${default_error}
        fi
    fi

    # If "${pki_subsystem}" is a CA, and is NOT a "Clone", check to
    # see "${pki_subsystem}" is a "Root" or a "Subordinate" CA
    pki_hierarchy=""
    if    [ "${pki_subsystem}" == "CA" ]  &&
        [ "${pki_clone}" != "Clone"  ]
    then
        line=`grep -e '^[ \t]*hierarchy.select[ \t]*=' ${PKI_SUBSYSTEM_CONFIGURATION_FILE}`
        if [ "${line}" != "" ] ; then
            pki_hierarchy=`echo "${line}" | sed -e 's/^[^=]*[ \t]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
        else
            return ${default_error}
        fi
    fi

    # If ${pki_subsystem} is a CA, check to
    # see if it is also a Security Domain
    pki_security_domain=""
    if    [ "${pki_subsystem}" == "CA" ] ; then
        line=`grep -e '^[ \t]*securitydomain.select[ \t]*=' ${PKI_SUBSYSTEM_CONFIGURATION_FILE}`
        if [ "${line}" != "" ] ; then
            pki_security_domain=`echo "${line}" | sed -e 's/^[^=]*[ \t]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
            if [ "${pki_security_domain}" == "new" ] ; then
                # Set a fixed value for "${pki_security_domain}"
                pki_security_domain="(Security Domain)"
            else
                # Reset "${pki_security_domain}" to be empty
                pki_security_domain=""
            fi
        else
            return ${default_error}
        fi
    fi

    # If ${pki_subsystem} is a KRA or OCSP, check to see if
    # it is either a Stand-alone KRA or a Stand-alone OCSP
    #
    #     NOTE:  Ignore errors when the '<pki>.standalone' parameter
    #            is not present as this is most likely a legacy instance!
    #
    pki_standalone=""
    if  [ "${pki_subsystem}" == "KRA"  ] ; then
        line=`grep -e '^[ \t]*kra.standalone[ \t]*=' ${PKI_SUBSYSTEM_CONFIGURATION_FILE}`
        if [ "${line}" != "" ] ; then
            pki_standalone=`echo "${line}" | sed -e 's/^[^=]*[ \t]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
            if [ "${pki_standalone}" == "true" ] ; then
                # Set a fixed value for "${pki_standalone}"
                pki_standalone="(Stand-alone)"
            else
                # Reset "${pki_standalone}" to be empty
                pki_standalone=""
            fi
        fi
    elif [ "${pki_subsystem}" == "OCSP" ] ; then
        line=`grep -e '^[ \t]*ocsp.standalone[ \t]*=' ${PKI_SUBSYSTEM_CONFIGURATION_FILE}`
        if [ "${line}" != "" ] ; then
            pki_standalone=`echo "${line}" | sed -e 's/^[^=]*[ \t]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
            if [ "${pki_standalone}" == "true" ] ; then
                # Set a fixed value for "${pki_standalone}"
                pki_standalone="(Stand-alone)"
            else
                # Reset "${pki_standalone}" to be empty
                pki_standalone=""
            fi
        fi
    fi

    # Always obtain this PKI instance's "registered"
    # security domain information
    pki_security_domain_name=""
    pki_security_domain_hostname=""
    pki_security_domain_https_admin_port=""

    line=`grep -e '^[ \t]*securitydomain.name[ \t]*=' ${PKI_SUBSYSTEM_CONFIGURATION_FILE}`
    if [ "${line}" != "" ] ; then
        pki_security_domain_name=`echo "${line}" | sed -e 's/^[^=]*[ \t]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
    else
        return ${default_error}
    fi

    line=`grep -e '^[ \t]*securitydomain.host[ \t]*=' ${PKI_SUBSYSTEM_CONFIGURATION_FILE}`
    if [ "${line}" != "" ] ; then
        pki_security_domain_hostname=`echo "${line}" | sed -e 's/^[^=]*[ \t]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
    else
        return ${default_error}
    fi

    line=`grep -e '^[ \t]*securitydomain.httpsadminport[ \t]*=' ${PKI_SUBSYSTEM_CONFIGURATION_FILE}`
    if [ "${line}" != "" ] ; then
        pki_security_domain_https_admin_port=`echo "${line}" | sed -e 's/^[^=]*[ \t]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
    else
        return ${default_error}
    fi

    # Compose the "PKI Instance Name" Status Line
    pki_instance_name="PKI Instance Name:   ${PKI_INSTANCE_NAME}"

    # Compose the "PKI Subsystem Type" Status Line
    header="PKI Subsystem Type: "
    if   [ "${pki_clone}" != "" ] ; then
        if [ "${pki_security_domain}" != "" ]; then
            # Possible Values:
            #
            #     "CA Clone (Security Domain)"
            #
            data="${pki_subsystem} ${pki_clone} ${pki_security_domain}"
        else
            # Possible Values:
            #
            #     "CA Clone"
            #     "KRA Clone"
            #     "OCSP Clone"
            #     "TKS Clone"
            #
            data="${pki_subsystem} ${pki_clone}"
        fi
    elif [ "${pki_hierarchy}" != "" ] ; then
        if [ "${pki_security_domain}" != "" ]; then
            # Possible Values:
            #
            #     "Root CA (Security Domain)"
            #     "Subordinate CA (Security Domain)"
            #
            data="${pki_hierarchy} ${pki_subsystem} ${pki_security_domain}"
        else
            # Possible Values:
            #
            #     "Root CA"
            #     "Subordinate CA"
            #
            data="${pki_hierarchy} ${pki_subsystem}"
        fi
    elif [ "${pki_standalone}" != "" ] ; then
        # Possible Values:
        #
        #     "KRA (Stand-alone)"
        #     "OCSP (Stand-alone)"
        #
        data="${pki_subsystem} ${pki_standalone}"
    else
        # Possible Values:
        #
        #     "KRA"
        #     "OCSP"
        #     "TKS"
        #     "TPS"
        #
        data="${pki_subsystem}"
    fi
    pki_subsystem_type="${header} ${data}"

    # Compose the "Registered PKI Security Domain Information" Status Line
    header="Name: "
    registered_pki_security_domain_name="${header} ${pki_security_domain_name}"

    header="URL:  "
    if  [ "${pki_security_domain_hostname}" != ""         ] &&
        [ "${pki_security_domain_https_admin_port}" != "" ]
    then
        data="https://${pki_security_domain_hostname}:${pki_security_domain_https_admin_port}"
    else
        return ${default_error}
    fi
    registered_pki_security_domain_url="${header} ${data}"

    # Print the "PKI Subsystem Type" Status Line
    echo
    echo "    [${pki_subsystem} Configuration Definitions]"
    echo "    ${pki_instance_name}"

    # Print the "PKI Subsystem Type" Status Line
    echo
    echo "    ${pki_subsystem_type}"

    # Print the "Registered PKI Security Domain Information" Status Line
    echo
    echo "    Registered PKI Security Domain Information:"
    echo "    =========================================================================="
    echo "    ${registered_pki_security_domain_name}"
    echo "    ${registered_pki_security_domain_url}"
    echo "    =========================================================================="

    return 0
}

display_configuration_information()
{
    result=0
    check_pki_configuration_status
    rv=$?
    if [ $rv -eq 0 ] ; then
        get_pki_status_definitions
        rv=$?
        if [ $rv -ne 0 ] ; then
            result=$rv
            echo
            echo "${PKI_INSTANCE_NAME} Status Definitions not found"
        else
            get_subsystems
            for SUBSYSTEM in ${PKI_SUBSYSTEMS}; do
                PKI_SUBSYSTEM_CONFIGURATION_FILE="${PKI_INSTANCE_PATH}/conf/${SUBSYSTEM}/CS.cfg"
                get_pki_configuration_definitions
                rv=$?
                if [ $rv -ne 0 ] ; then
                    result=$rv
                    echo
                    echo "${PKI_INSTANCE_NAME} Configuration Definitions not found for ${SUBSYSTEM}"
                fi
            done
        fi
    fi
    return $result
}

display_instance_status_systemd()
{
    # source the instance specific variables
    . /etc/sysconfig/${PKI_INSTANCE_NAME}

    echo "Instance ${PKI_INSTANCE_NAME} is configured to use nuxwdog: ${USE_NUXWDOG}"
    echo -n "Status for ${PKI_INSTANCE_NAME}: "

    # find the systemd target to query status
    PKI_SYSTEMD_TARGET="pki-${pki_instance_type}d"
    if [ "$USE_NUXWDOG" = true ] ; then
        PKI_SYSTEMD_TARGET="${PKI_SYSTEMD_TARGET}-nuxwdog"
    fi

    systemctl status "$PKI_SYSTEMD_TARGET@$PKI_INSTANCE_NAME.service" > /dev/null 2>&1
    rv=$?

    if [ $rv -eq 0 ] ; then
        echo "$PKI_INSTANCE_NAME is running .."
        display_configuration_information
    else
        echo "$PKI_INSTANCE_NAME is stopped"
    fi

    return $rv
}

display_instance_status_debian()
{
    set_debian_tomcat_parameters
    start-stop-daemon --status --pidfile "$CATALINA_PID" \
            --user $TOMCAT7_USER --exec "$JAVA_HOME/bin/java" \
            >/dev/null 2>&1
    rv=$?

    if [ $rv -eq 0 ] ; then
        display_configuration_information
    else
        echo "Instance $PKI_INSTANCE_NAME is not running."
    fi

    return $rv
}

make_symlink()
{
    symlink="${1}"
    target="${2}"
    user="${3}"
    group="${4}"

    rv=0

    echo "INFO:  Attempting to create '${symlink}' -> '${target}' . . ."
    # Check to make certain that the expected target exists.
    #
    #     NOTE:  The symbolic link does NOT exist at this point.
    #
    if [ -e ${target} ]; then
        # Check that the expected target is fully resolvable!
        if [ ! `readlink -qe ${target}` ]; then
            # Issue an ERROR that the target to which the
            # symbolic link is expected to point is NOT fully resolvable!
            echo "ERROR:  Failed making '${symlink}' -> '${target}'"\
                 "since target '${target}' is NOT fully resolvable!"
            rv=1
        else
            # Attempt to remove any existing symbolic link.
            # This is likely pointing to a non-existent target
            if [ `readlink ${symlink}` ]; then
                rm ${symlink}
            fi

            # Attempt to create a symbolic link and 'chown' it.
            ln -s ${target} ${symlink}
            rv=$?
            if [ $rv -eq 0 ]; then
                # NOTE:  Ignore 'chown' errors.
                chown -h ${user}:${group} ${symlink}
                echo "SUCCESS:  Created '${symlink}' -> '${target}'"
            else
                echo "ERROR:  Failed to create '${symlink}' -> '${target}'!"
                rv=1
            fi
        fi
    else
        # Issue an ERROR that the target to which the
        # symbolic link is expected to point does NOT exist.
        echo "ERROR:  Failed making '${symlink}' -> '${target}'"\
             "since target '${target}' does NOT exist!"
        rv=1
    fi

    return $rv
}

check_symlinks()
{
    # declare -p symlinks
    path="${1}"
    user="${2}"
    group="${3}"

    rv=0

    # process key/value pairs (symlink/target) in the associative array
    for key in "${!symlinks[@]}"
    do
        symlink="${path}/${key}"
        target=${symlinks[${key}]}
        if [ -e ${symlink} ]; then
            if [ -h ${symlink} ]; then
                current_target=`readlink ${symlink}`
                # Verify that the current target to which the
                # symlink points is the expected target
                if [ ${current_target} == ${target} ]; then
                    # Check to make certain that the expected target exists.
                    if [ -e ${target} ]; then
                        # Check that the expected target is fully resolvable!
                        if [ ! `readlink -qe ${target}` ]; then
                            # Issue an ERROR that the target to which the
                            # symbolic link is expected to point is NOT
                            # fully resolvable!
                            echo "WARNING:  Symbolic link '${symlink}'"\
                                 "exists, but is a dangling symlink!"\
                            echo "ERROR:  Unable to create"\
                                 "'${symlink}' -> '${target}'"\
                                 "since target '${target}' is NOT fully"\
                                 "resolvable!"
                            rv=1
                        else
                            # ALWAYS run 'chown' on an existing '${symlink}'
                            # that points to a fully resolvable '${target}'
                            #
                            #     NOTE:  Ignore 'chown' errors.
                            #
                            chown -h ${user}:${group} ${symlink}
                            # echo "SUCCESS:  '${symlink}' -> '${target}'"
                        fi
                    else
                        # Issue an ERROR that the target to which the
                        # symbolic link is expected to point does NOT exist.
                        echo "WARNING:  Symbolic link '${symlink}'"\
                             "exists, but is a dangling symlink!"\
                        echo "ERROR:  Unable to create"\
                             "'${symlink}' -> '${target}'"\
                             "since target '${target}' does NOT exist!"
                        rv=1
                    fi
                else
                    # Attempt to remove this symbolic link and
                    # issue a WARNING that a new symbolic link is
                    # being created to point to the expected target
                    # rather than the current target to which it
                    # points.
                    echo "WARNING:  Attempting to change symbolic link"\
                         "'${symlink}' to point to target '${target}'"\
                         "INSTEAD of current target '${current_target}'!"
                    rm ${symlink}
                    rv=$?
                    if [ $rv -ne 0 ]; then
                        echo "ERROR:  Failed to remove"\
                             "'${symlink}' -> '${current_target}'!"
                        rv=1
                    else
                        echo "INFO:  Removed"\
                             "'${symlink}' -> '${current_target}'!"
                        # Attempt to create the symbolic link and chown it.
                        make_symlink ${symlink} ${target} ${user} ${group}
                        rv=$?
                    fi
                fi
            elif [ -f ${symlink} ]; then
                # Issue a WARNING that the administrator may have replaced
                # the symbolic link with a file for debugging purposes.
                echo "WARNING:  '${symlink}' exists but is NOT a symbolic link!"
            else
                # Issue an ERROR that the symbolic link has been replaced
                # by something unusable (such as a directory).
                echo "ERROR:  '${symlink}' exists but is NOT a symbolic link!"
                rv=1
            fi
        else
            # Issue a WARNING that this symbolic link does not exist.
            echo "WARNING:  Symbolic link '${symlink}' does NOT exist!"
            # Attempt to create the symbolic link and chown it.
            make_symlink ${symlink} ${target} ${user} ${group}
            rv=$?
        fi
    done

    return $rv
}

# Detect and correct any missing or incorrect symlinks.
#
#     Use the following command to locate PKI 'instance' symlinks:
#
#        find ${PKI_INSTANCE_PATH} -type l | sort | xargs file
#
verify_symlinks()
{
    # declare associative arrays
    declare -A base_symlinks
    declare -A ca_symlinks
    declare -A kra_symlinks
    declare -A ocsp_symlinks
    declare -A tks_symlinks
    declare -A tps_symlinks
    declare -A ca_jar_symlinks
    declare -A kra_jar_symlinks
    declare -A ocsp_jar_symlinks
    declare -A tks_jar_symlinks
    declare -A tps_jar_symlinks
    declare -A systemd_symlinks

    # Dogtag 10 Conditional Variables
    if $debian ; then
        jni_jar_dir="/usr/share/java"
        tomcat_dir="/usr/share/tomcat7"
    else
        jni_jar_dir="$JNI_JAR_DIR"
        tomcat_dir="/usr/share/tomcat"
    fi

    # Dogtag 10 Symbolic Link Variables
    java_dir="/usr/share/java"
    pki_common_jar_dir="${PKI_INSTANCE_PATH}/common/lib"
    pki_registry_dir="/etc/sysconfig/pki/${PKI_WEB_SERVER_TYPE}/${PKI_INSTANCE_NAME}"

    # Dogtag 10 Systemd variables
    systemd_dir="/lib/systemd/system"
    if [ "${STARTED_BY_SYSTEMD}" == "1" ]; then
        pki_systemd_service="pki-${PKI_WEB_SERVER_TYPE}d-nuxwdog@.service"
        pki_systemd_dir="/etc/systemd/system/pki-tomcatd-nuxwdog.target.wants"
        pki_systemd_link="pki-${PKI_WEB_SERVER_TYPE}d-nuxwdog@${PKI_INSTANCE_NAME}.service"
    else
        pki_systemd_service="pki-${PKI_WEB_SERVER_TYPE}d@.service"
        pki_systemd_dir="/etc/systemd/system/pki-tomcatd.target.wants"
        pki_systemd_link="pki-${PKI_WEB_SERVER_TYPE}d@${PKI_INSTANCE_NAME}.service"
    fi

    # '${PKI_INSTANCE_PATH}' symlinks
    base_symlinks=(
        [alias]=/etc/pki/${PKI_INSTANCE_NAME}/alias
        [bin]=${tomcat_dir}/bin
        [conf]=/etc/pki/${PKI_INSTANCE_NAME}
        [logs]=/var/log/pki/${PKI_INSTANCE_NAME})

    # '${PKI_INSTANCE_PATH}/ca' symlinks
    ca_symlinks=(
        [alias]=${PKI_INSTANCE_PATH}/alias
        [conf]=/etc/pki/${PKI_INSTANCE_NAME}/ca
        [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/ca
        [registry]=${pki_registry_dir})

    # '${PKI_INSTANCE_PATH}/kra' symlinks
    kra_symlinks=(
        [alias]=${PKI_INSTANCE_PATH}/alias
        [conf]=/etc/pki/${PKI_INSTANCE_NAME}/kra
        [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/kra
        [registry]=${pki_registry_dir})

    # '${PKI_INSTANCE_PATH}/ocsp' symlinks
    ocsp_symlinks=(
        [alias]=${PKI_INSTANCE_PATH}/alias
        [conf]=/etc/pki/${PKI_INSTANCE_NAME}/ocsp
        [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/ocsp
        [registry]=${pki_registry_dir})

    # '${PKI_INSTANCE_PATH}/tks' symlinks
    tks_symlinks=(
        [alias]=${PKI_INSTANCE_PATH}/alias
        [conf]=/etc/pki/${PKI_INSTANCE_NAME}/tks
        [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/tks
        [registry]=${pki_registry_dir})

    # '${PKI_INSTANCE_PATH}/tps' symlinks
    tps_symlinks=(
        [alias]=${PKI_INSTANCE_PATH}/alias
        [conf]=/etc/pki/${PKI_INSTANCE_NAME}/tps
        [logs]=/var/log/pki/${PKI_INSTANCE_NAME}/tps
        [registry]=${pki_registry_dir})

    # '${pki_systemd_dir}' symlinks
    systemd_symlinks[${pki_systemd_link}]=${systemd_dir}/${pki_systemd_service}

    # Detect and correct 'Tomcat' symbolic links
    #
    #     (1) convert the specified associative array into a string
    #     (2) create a new global 'symlinks' associative array from this
    #         specified string which will be used by the "check_symlinks()"
    #         subroutine
    #     (3) call "check_symlinks()" with the appropriate arguments to
    #         detect and correct this specified associative array;
    #         "check_symlinks()" returns 0 on success and 1 on failure
    #
    if [ ${PKI_WEB_SERVER_TYPE} == 'tomcat' ]; then
        # Detect and correct 'base_symlinks'
        base_symlinks_string=$(declare -p base_symlinks)
        eval "declare -A symlinks=${base_symlinks_string#*=}"
        check_symlinks ${PKI_INSTANCE_PATH} ${PKI_USER} ${PKI_GROUP}
        rv=$?
        if [ $rv -ne 0 ]; then
            return $rv
        fi

        if [ -e ${PKI_INSTANCE_PATH}/ca ]; then
            # Detect and correct 'ca_symlinks'
            ca_symlinks_string=$(declare -p ca_symlinks)
            eval "declare -A symlinks=${ca_symlinks_string#*=}"
            check_symlinks ${PKI_INSTANCE_PATH}/ca ${PKI_USER} ${PKI_GROUP}
            rv=$?
            if [ $rv -ne 0 ]; then
                return $rv
            fi
        fi

        if [ -e ${PKI_INSTANCE_PATH}/kra ]; then
            # Detect and correct 'kra_symlinks'
            kra_symlinks_string=$(declare -p kra_symlinks)
            eval "declare -A symlinks=${kra_symlinks_string#*=}"
            check_symlinks ${PKI_INSTANCE_PATH}/kra ${PKI_USER} ${PKI_GROUP}
            rv=$?
            if [ $rv -ne 0 ]; then
                return $rv
            fi
        fi

        if [ -e ${PKI_INSTANCE_PATH}/ocsp ]; then
            # Detect and correct 'ocsp_symlinks'
            ocsp_symlinks_string=$(declare -p ocsp_symlinks)
            eval "declare -A symlinks=${ocsp_symlinks_string#*=}"
            check_symlinks ${PKI_INSTANCE_PATH}/ocsp ${PKI_USER} ${PKI_GROUP}
            rv=$?
            if [ $rv -ne 0 ]; then
                return $rv
            fi
        fi

        if [ -e ${PKI_INSTANCE_PATH}/tks ]; then
            # Detect and correct 'tks_symlinks'
            tks_symlinks_string=$(declare -p tks_symlinks)
            eval "declare -A symlinks=${tks_symlinks_string#*=}"
            check_symlinks ${PKI_INSTANCE_PATH}/tks ${PKI_USER} ${PKI_GROUP}
            rv=$?
            if [ $rv -ne 0 ]; then
                return $rv
            fi
        fi

        if [ -e ${PKI_INSTANCE_PATH}/tps ]; then
            # Detect and correct 'tps_symlinks'
            tps_symlinks_string=$(declare -p tps_symlinks)
            eval "declare -A symlinks=${tps_symlinks_string#*=}"
            check_symlinks ${PKI_INSTANCE_PATH}/tps ${PKI_USER} ${PKI_GROUP}
            rv=$?
            if [ $rv -ne 0 ]; then
                return $rv
            fi
        fi

        # Detect and correct 'systemd_symlinks'
        systemd_symlinks_string=$(declare -p systemd_symlinks)
        eval "declare -A symlinks=${systemd_symlinks_string#*=}"
        check_symlinks ${pki_systemd_dir} ${PKI_USER} ${PKI_GROUP}
        rv=$?
        if [ $rv -ne 0 ]; then
            return $rv
        fi
    fi

    return 0
}

backup_instance_configuration_files()
{
    declare -a pki_subsystems=('ca'
                               'kra'
                               'ocsp'
                               'tks'
                               'tps')

    # Utilize an identical timestamp on archives for each PKI subsystem
    # residing within the same instance to mark a common archival time
    timestamp=`date +%Y%m%d%H%M%S`

    # Automatically enable timestamped archives
    #
    #     NOTE:  To disable this feature for a particular PKI subsystem
    #            within an instance, edit that PKI subsystem's 'CS.cfg' file
    #            within the instance:
    #
    #            If the 'archive.configuration_file' parameter exists,
    #            change it to 'archive.configuration_file=false'.
    #
    #            However, if the 'archive.configuration_file' parameter does
    #            not exist, simply add 'archive.configuration_file=false'
    #            to the 'CS.cfg'.
    #
    #            In either case, it is unnecessary to restart the instance,
    #            as each instance's 'CS.cfg' file is always processed every
    #            time an instance is restarted.
    #
    backup_errors=0
    for pki in "${pki_subsystems[@]}"
    do
        config_dir=${PKI_INSTANCE_PATH}/conf/${pki}

        # Check to see if this PKI subsystem exists within this instance
        if [ ! -d ${config_dir} ] ; then
            continue
        fi

        # Compute uppercase representation of this PKI subsystem
        PKI=${pki^^}

        # Backup parameters
        pki_instance_configuration_file=${config_dir}/CS.cfg
        backup_file=${config_dir}/CS.cfg.bak
        saved_backup_file=${config_dir}/CS.cfg.bak.saved

        # Check for an empty 'CS.cfg'
        #
        #     NOTE:  'CS.cfg' is always a regular file
        #
        if [ ! -s ${pki_instance_configuration_file} ] ; then
            # Issue a warning that the 'CS.cfg' is empty
            echo "WARNING:  The '${pki_instance_configuration_file}' is empty!"
            echo "          ${PKI} backups will be discontinued until this"
            echo "          issue has been resolved!"
            ((backup_errors++))
            continue
        fi

        # Make certain that a previous attempt to backup 'CS.cfg' has not failed
        # (i. e. - 'CS.cfg.bak.saved' exists)
        #
        #     NOTE:  'CS.cfg.bak.saved' is always a regular file
        #
        if [ -f ${saved_backup_file} ] ; then
            # 'CS.cfg.bak.saved' is a regular file or a symlink
            echo "WARNING:  Since the file '${saved_backup_file}' exists, a"
            echo "          previous backup attempt has failed!  ${PKI} backups"
            echo "          will be discontinued until this issue has been resolved!"
            ((backup_errors++))
            continue
        fi

        # If present, compare 'CS.cfg' to 'CS.cfg.bak' to see if it is necessary
        # to backup 'CS.cfg'.  'CS.cfg.bak' may be a regular file, a
        # symlink, or a dangling symlink
        #
        #     NOTE:  'CS.cfg.bak' may be a regular file, a symlink, or a
        #            dangling symlink
        #
        if [ -f ${backup_file} ] ; then
            # 'CS.cfg.bak' is a regular file or a symlink
            cmp --silent ${pki_instance_configuration_file} ${backup_file}
            rv=$?
            if [ $rv -eq 0 ] ; then
                # 'CS.cfg' is identical to 'CS.cfg.bak';
                # no need to archive or backup 'CS.cfg'
                continue
            fi

            # Since it is known that the previous 'CS.cfg.bak' file exists, and
            # and it is either a symlink or a regular file, save the previous
            # 'CS.cfg.bak' to 'CS.cfg.bak.saved'
            #
            # NOTE:  If switching between simply creating backups to generating
            #        timestamped archives, the previous 'CS.cfg.bak' that
            #        existed as a regular file will NOT be archived!
            #
            if [ -h ${backup_file} ] ; then
                # 'CS.cfg.bak' is a symlink
                # (i. e. - copy the timestamped archive to a regular file)
                cp ${backup_file} ${saved_backup_file}

                # remove the 'CS.cfg.bak' symlink
                rm ${backup_file}
            else
                # 'CS.cfg.bak' is a regular file
                # (i. e. - simply rename the regular file)
                mv ${backup_file} ${saved_backup_file}
            fi
        elif [ -h ${backup_file} ] ; then
            # 'CS.cfg.bak' is a dangling symlink
            echo "WARNING:  The file '${backup_file}' is a dangling symlink"
            echo "          which suggests that the previous backup file has"
            echo "          been removed!  ${PKI} backups will be discontinued"
            echo "          until this issue has been resolved!"
            ((backup_errors++))
            continue
        fi

        # Check 'CS.cfg' for 'archive.configuration_file' parameter
        # to see if timestamped archives should be disabled
        archive_configuration_file="true"
        line=`grep -e '^[ \t]*archive.configuration_file[ \t]*=' ${pki_instance_configuration_file}`
        if [ "${line}" != "" ] ; then
            archive_configuration_file=`echo "${line}" | sed -e 's/^[^=]*[ \t]*=[ \t]*\(.*\)/\1/' -e 's/[ \t]*$//'`
        fi

        # Backup 'CS.cfg'
        if [ "${archive_configuration_file}" != "true" ] ; then
            # Always backup 'CS.cfg' to 'CS.cfg.bak'
            cp -b ${pki_instance_configuration_file} ${backup_file}
        else
            # Archive parameters
            archive_dir=${config_dir}/archives
            archived_file=${archive_dir}/CS.cfg.bak.${timestamp}

            # If not present, create an archives directory for this 'CS.cfg'
            if [ ! -d ${archive_dir} ] ; then
                mkdir -p ${archive_dir}
            fi

            # Archive 'CS.cfg' to 'CS.cfg.bak.${timestamp}'
            cp -a ${pki_instance_configuration_file} ${archived_file}
            if [ ! -s ${archived_file} ] ; then
                # Issue a warning that the archived backup failed
                echo "WARNING:  Failed to archive '${pki_instance_configuration_file}' to '${archived_file}'!"
                ((backup_errors++))
                continue
            fi

            # Always create 'CS.cfg.bak' by linking to this archived file
            ln -s ${archived_file} ${backup_file}

            # Report that 'CS.cfg' has been successfully archived
            echo "SUCCESS:  Successfully archived '${archived_file}'"
        fi

        # Check that a non-empty 'CS.cfg.bak' symlink or regular file exists
        if [ ! -s ${backup_file} ] ; then
            # Issue a warning that the backup failed
            echo "WARNING:  Failed to backup '${pki_instance_configuration_file}' to '${backup_file}'!"
            ((backup_errors++))
            continue
        else
            # Report that 'CS.cfg' has been successfully backed up
            echo "SUCCESS:  Successfully backed up '${backup_file}'"
        fi

        # Since 'CS.cfg' was backed up successfully, remove 'CS.cfg.bak.saved'
        if [ -f ${saved_backup_file} ] ; then
            rm ${saved_backup_file}
        fi
    done

    if [ ${backup_errors} -ne 0 ]; then
        return 1
    fi

    return 0
}

start_instance()
{
    rv=0

    if [ -f ${RESTART_SERVER} ] ; then
        rm -f ${RESTART_SERVER}
    fi

    # Verify symbolic links (detecting and correcting them if possible)
    verify_symlinks
    rv=$?
    if [ $rv -ne 0 ] ; then
        return $rv
    fi

    # Copy pki.policy template
    /bin/cp /usr/share/pki/server/conf/pki.policy /var/lib/pki/$PKI_INSTANCE_NAME/conf

    # Add permissions for all JAR files in /var/lib/pki/$PKI_INSTANCE_NAME/common/lib
    for path in /var/lib/pki/$PKI_INSTANCE_NAME/common/lib/*; do

        cat >> /var/lib/pki/$PKI_INSTANCE_NAME/conf/pki.policy << EOF

grant codeBase "file:$(realpath $path)" {
    permission java.security.AllPermission;
};
EOF
    done

    # Generate catalina.policy dynamically.
    cat /usr/share/pki/server/conf/catalina.policy \
        /usr/share/tomcat/conf/catalina.policy \
        /var/lib/pki/$PKI_INSTANCE_NAME/conf/pki.policy \
        /var/lib/pki/$PKI_INSTANCE_NAME/conf/custom.policy > \
        /var/lib/pki/$PKI_INSTANCE_NAME/conf/catalina.policy

    pki-server banner-validate -i "$PKI_INSTANCE_NAME"
    rv=$?
    if [ $rv -ne 0 ]; then
        return $rv
    fi

    if [ "${PKI_SERVER_AUTO_ENABLE_SUBSYSTEMS}" = "true" ] ; then
        # enable all subsystems
        pki-server subsystem-enable -i "$PKI_INSTANCE_NAME" --all
    fi

    # We no longer start tomcat instances here.
    # instead we rely on the tomcat unit scripts

    # From the PKI point of view a returned error code of 6 implies
    # that the program is not "configured". An error code of 1 implies
    # that the program was "configured" but must still be restarted.
    #
    # If the return code is 6 return this value unchanged to the
    # calling routine so that the total number of configuration errors
    # may be counted. Other return codes are ignored.
    #
    check_pki_configuration_status
    rv=$?
    if [ $rv -eq 6 ]; then
        # 6 program is not configured
        return 6
    else
        # 0 success

        # Always create a backup of each PKI subsystem's 'CS.cfg' file
        # within an instance.
        #
        # For every backup failure detected within a PKI subsystem within
        # an instance, a warning message will be issued, and an error code
        # of 1 will be returned.
        #
        # Note that until they have been resolved, every previous backup
        # failures of any PKI subsystem within an instance will also issue
        # a warning message and return an error code of 1.  Backups of that
        # particular instance's PKI subsystem will be suspended until this
        # error has been addressed.
        #
        # By default, unless they have been explicitly disabled,
        # a timestamped archive of each PKI subsystem's 'CS.cfg' file
        # within an instance will also be created. Note that a single
        # timestamp will be utlized across each PKI subsystem within
        # an instance for each invocation of this function.
        #
        # When enabled, any timestamped archive failures also issue a
        # warning message and return an error code of 1.
        #
        backup_instance_configuration_files
        return $?
    fi
}

# function used in debian to find the correct jdk
# this is used to set OPENJDKS
# taken from /etc/init.d/tomcat7
find_openjdks()
{
    for jvmdir in /usr/lib/jvm/java-7-openjdk-*
    do
        if [ -d "${jvmdir}" -a "${jvmdir}" != "/usr/lib/jvm/java-7-openjdk-common" ]
        then
            OPENJDKS=$jvmdir
        fi
    done
    for jvmdir in /usr/lib/jvm/java-6-openjdk-*
    do
        if [ -d "${jvmdir}" -a "${jvmdir}" != "/usr/lib/jvm/java-6-openjdk-common" ]
        then
            OPENJDKS="${OPENJDKS} ${jvmdir}"
        fi
    done
}

# function used by debian to set JAVA_HOME
# taken from /etc/init.d/tomcat7
# TODO: get JAVA_HOME for Debian from pki.conf
set_java_home()
{
    find_openjdks
    # The first existing directory is used for JAVA_HOME (if JAVA_HOME is not
    # defined in $DEFAULT)
    JDK_DIRS="/usr/lib/jvm/default-java ${OPENJDKS} /usr/lib/jvm/java-6-openjdk /usr/lib/jvm/java-6-sun /usr/lib/jvm/java-7-oracle"

    # Look for the right JVM to use
    for jdir in $JDK_DIRS; do
        if [ -r "$jdir/bin/java" -a -z "${JAVA_HOME}" ]; then
            JAVA_HOME="$jdir"
        fi
    done

    export JAVA_HOME
}

# used to start debian instances
# taken from /etc/init.d/tomcat7
catalina_sh() {
    # Escape any double quotes in the value of JAVA_OPTS
    JAVA_OPTS="$(echo $JAVA_OPTS | sed 's/\"/\\\"/g')"

    AUTHBIND_COMMAND=""
    if [ "$AUTHBIND" = "yes" -a "$1" = "start" ]; then
            JAVA_OPTS="$JAVA_OPTS -Djava.net.preferIPv4Stack=true"
            AUTHBIND_COMMAND="/usr/bin/authbind --deep /bin/sh -c "
    fi

    # Define the command to run Tomcat's catalina.sh as a daemon
    # set -a tells sh to export assigned variables to spawned shells.
    TOMCAT_SH="set -a; JAVA_HOME=\"$JAVA_HOME\"; . \"$DEFAULT\"; \
            CATALINA_HOME=\"$CATALINA_HOME\"; \
            CATALINA_BASE=\"$CATALINA_BASE\"; \
            JAVA_OPTS=\"$JAVA_OPTS\"; \
            CATALINA_PID=\"$CATALINA_PID\"; \
            CATALINA_TMPDIR=\"$CATALINA_TMPDIR\"; \
            LANG=\"$LANG\"; JSSE_HOME=\"$JSSE_HOME\"; \
            cd \"$CATALINA_BASE\"; \
            \"$CATALINA_SH\" $@"

    if [ "$AUTHBIND" = "yes" -a "$1" = "start" ]; then
            TOMCAT_SH="'$TOMCAT_SH'"
    fi

    # Run the catalina.sh script as a daemon
    touch "$CATALINA_PID" "$CATALINA_BASE"/logs/catalina.out
    chown $TOMCAT7_USER "$CATALINA_PID" "$CATALINA_BASE"/logs/catalina.out
    start-stop-daemon --start -b -u "$TOMCAT7_USER" -g "$TOMCAT7_GROUP" \
            -c "$TOMCAT7_USER" -d "$CATALINA_TMPDIR" -p "$CATALINA_PID" \
            -x /bin/sh -- -c "$AUTHBIND_COMMAND $TOMCAT_SH"
    status="$?"
    set +a
    return $status
}

set_debian_tomcat_parameters()
{
    set_java_home
    CATALINA_HOME=/usr/share/tomcat7
    CATALINA_BASE=/var/lib/pki/${PKI_INSTANCE_NAME}
    DESC="Tomcat 7 instance for ${PKI_INSTANCE_NAME}"

    if [ -z "$JAVA_OPTS" ]; then
        JAVA_OPTS="-Djava.awt.headless=true -Xmx128M"
    fi

    # overwrite with settings from the registry file
    if [ -f /etc/sysconfig/${PKI_INSTANCE_NAME} ]; then
        . /etc/sysconfig/${PKI_INSTANCE_NAME}
    fi

    JVM_TMP=/tmp/tomcat7-${PKI_INSTANCE_NAME}-tmp
    if [ -z "$CATALINA_TMPDIR" ]; then
        CATALINA_TMPDIR="$JVM_TMP"
    fi

    # Set the JSP compiler if set in the tomcat7.default file
    if [ -n "$JSP_COMPILER" ]; then
        JAVA_OPTS="$JAVA_OPTS -Dbuild.compiler=\"$JSP_COMPILER\""
    fi

    # Generate catalina.policy dynamically.
    # for now, do not generate policy here, because we need to figure out
    # where to get the tomcat policy.

    TOMCAT7_SECURITY=no
    SECURITY=""
    if [ "$TOMCAT7_SECURITY" = "True" ]; then
        SECURITY="-security"
    fi

    # Define other required variables
    CATALINA_PID="/var/run/${PKI_INSTANCE_NAME}.pid"
    CATALINA_SH="$CATALINA_HOME/bin/catalina.sh"

    # Look for Java Secure Sockets Extension (JSSE) JARs
    if [ -z "${JSSE_HOME}" -a -r "${JAVA_HOME}/jre/lib/jsse.jar" ]; then
        JSSE_HOME="${JAVA_HOME}/jre/"
    fi

    TOMCAT7_USER=$TOMCAT_USER
    TOMCAT7_GROUP=$TOMCAT_USER
}

start_deb_instance()
{
    rv=0

    if [ -f ${RESTART_SERVER} ] ; then
        rm -f ${RESTART_SERVER}
    fi

    # Verify symbolic links (detecting and correcting them if possible)
    verify_symlinks
    rv=$?
    if [ $rv -ne 0 ] ; then
        return $rv
    fi

    set_debian_tomcat_parameters

    if start-stop-daemon --test --start --pidfile "$CATALINA_PID" \
           --user $TOMCAT7_USER --exec "$JAVA_HOME/bin/java" \
           >/dev/null; then

        # Remove / recreate JVM_TMP directory
        rm -rf "$JVM_TMP"
        mkdir -p "$JVM_TMP" || {
            log_failure_msg "could not create JVM temporary directory"
            exit 1
        }
        chown $TOMCAT7_USER "$JVM_TMP"

        catalina_sh start $SECURITY
        sleep 5
        if start-stop-daemon --test --start --pidfile "$CATALINA_PID" \
               --user $TOMCAT7_USER --exec "$JAVA_HOME/bin/java" \
               >/dev/null; then
            if [ -f "$CATALINA_PID" ]; then
                rm -f "$CATALINA_PID"
            fi
            log_end_msg 1
        else
            log_end_msg 0
        fi
    else
        log_progress_msg "(already running)"
        log_end_msg 0
    fi

    check_pki_configuration_status
    rv=$?
    if [ $rv -eq 6 ]; then
        # 6 program is not configured
        return 6
    else
        # 0 success
        return 0
    fi
}

# used to stop debian instances
# taken from /etc/init.d/tomcat7
stop_instance()
{
    set_debian_tomcat_parameters
    log_daemon_msg "Stopping $DESC" "${PKI_INSTANCE_NAME}"

    if [ -f "$CATALINA_PID" ]; then
        start-stop-daemon --stop --pidfile "$CATALINA_PID" \
                --user "$TOMCAT7_USER" \
                --retry=TERM/20/KILL/5 >/dev/null
        if [ $? -eq 1 ]; then
            log_progress_msg "$DESC is not running but pid file exists, cleaning up"
        elif [ $? -eq 3 ]; then
            PID="`cat $CATALINA_PID`"
            log_failure_msg "Failed to stop $PKI_INSTANCE_NAME (pid $PID)"
            exit 1
        fi
        rm -f "$CATALINA_PID"
        rm -rf "$JVM_TMP"
    else
        log_progress_msg "(not running)"
    fi
    log_end_msg 0
}

stop()
{
    error_rv=0
    rv=0
    errors=0

    if [ ${TOTAL_PKI_REGISTRY_ENTRIES} -eq 0 ]; then
        echo
        echo "ERROR:  No '${PKI_TYPE}' instances installed!"
        # 5 program is not installed
        return 5
    fi

    if [ ${TOTAL_PKI_REGISTRY_ENTRIES} -gt 1 ] ; then
        echo "BEGIN SHUTTING DOWN '${PKI_TYPE}' INSTANCE(S):"
    fi

    # Shutdown every PKI instance of this type that is running
    for PKI_REGISTRY_ENTRY in ${PKI_REGISTRY_ENTRIES}; do
        # Source values associated with this particular PKI instance
        [ -f ${PKI_REGISTRY_ENTRY} ] &&
        . ${PKI_REGISTRY_ENTRY}

        [ ${TOTAL_PKI_REGISTRY_ENTRIES} -gt 1 ] && echo

        stop_instance
        rv=$?
        if [ $rv != 0 ] ; then
            errors=`expr $errors + 1`
            error_rv=$rv
        fi
    done

    # ONLY print a "WARNING" message if multiple
    # instances are being examined
    if [ ${TOTAL_PKI_REGISTRY_ENTRIES} -gt 1 ] ; then
        if [ ${errors} -eq 1 ]; then
            # Since only ONE error exists, return that "bad" error code.
            rv=${error_rv}
        elif [ ${errors} -gt 1 ]; then
            # Since MORE than ONE error exists, return an OVERALL status
            # of "1 generic or unspecified error (current practice)"
            rv=1
        fi

        if [ ${errors} -ge 1 ]; then
            echo
            echo -n "WARNING:  "
            echo -n "${errors} of ${TOTAL_PKI_REGISTRY_ENTRIES} "
            echo -n "'${PKI_TYPE}' instances were "
            echo -n "unsuccessfully stopped!"
            echo
        fi

        echo
        echo "FINISHED SHUTTING DOWN '${PKI_TYPE}' INSTANCE(S)."
    fi

    return $rv
}

restart()
{
    stop
    sleep 2
    start

    return $?
}


start()
{
    error_rv=0
    rv=0
    config_errors=0
    errors=0

    if [ ${TOTAL_PKI_REGISTRY_ENTRIES} -eq 0 ]; then
        echo
        echo "ERROR:  No '${PKI_TYPE}' instances installed!"
        # 5 program is not installed
        return 5
    fi

    if [ ${TOTAL_PKI_REGISTRY_ENTRIES} -gt 1 ]; then
        echo "BEGIN STARTING '${PKI_TYPE}' INSTANCES:"
    fi

    # Start every PKI instance of this type that isn't already running
    for PKI_REGISTRY_ENTRY in ${PKI_REGISTRY_ENTRIES}; do
        # Source values associated with this particular PKI instance
        [ -f ${PKI_REGISTRY_ENTRY} ] &&
        . ${PKI_REGISTRY_ENTRY}

        [ ${TOTAL_PKI_REGISTRY_ENTRIES} -gt 1 ] && echo

        if ! $debian; then
            start_instance
            rv=$?
        else
            start_deb_instance
            rv=$?
        fi

        if [ $rv = 6 ] ; then
            # Since at least ONE configuration error exists, then there
            # is at least ONE unconfigured instance from the PKI point
            # of view.
            #
            # However, it must still be considered that the
            # instance is "running" from the point of view of other
            # OS programs such as 'chkconfig'.
            #
            # Therefore, ignore non-zero return codes resulting
            # from configuration errors.
            #

            config_errors=`expr $config_errors + 1`
            rv=0
        elif [ $rv != 0 ] ; then
            errors=`expr $errors + 1`
            error_rv=$rv
        fi
    done
    # ONLY print a "WARNING" message if multiple
    # instances are being examined
    if [ ${TOTAL_PKI_REGISTRY_ENTRIES} -gt 1 ] ; then
        # NOTE:  "bad" return code(s) OVERRIDE configuration errors!
        if [ ${errors} -eq 1 ]; then
            # Since only ONE error exists, return that "bad" error code.
            rv=${error_rv}
        elif [ ${errors} -gt 1 ]; then
            # Since MORE than ONE error exists, return an OVERALL status
            # of "1 generic or unspecified error (current practice)"
            rv=1
        fi

        if [ ${errors} -ge 1 ]; then
            echo
            echo -n "WARNING:  "
            echo -n "${errors} of ${TOTAL_PKI_REGISTRY_ENTRIES} "
            echo -n "'${PKI_TYPE}' instances failed to start!"
            echo
        fi

        if [ ${TOTAL_UNCONFIGURED_PKI_ENTRIES} -ge 1 ]; then
            echo
            echo -n "WARNING:  "
            echo -n "${TOTAL_UNCONFIGURED_PKI_ENTRIES} "
            echo -n "of ${TOTAL_PKI_REGISTRY_ENTRIES} "
            echo -n "'${PKI_TYPE}' instances MUST be configured!"
            echo
        fi

        echo
        echo "FINISHED STARTING '${PKI_TYPE}' INSTANCE(S)."
    fi

    return $rv
}

registry_status()
{
    error_rv=0
    rv=0
    errors=0

    if [ ${TOTAL_PKI_REGISTRY_ENTRIES} -eq 0 ]; then
        echo
        echo "ERROR:  No '${PKI_TYPE}' instances installed!"
        # 4 program or service status is unknown
        return 4
    fi

    if [ ${TOTAL_PKI_REGISTRY_ENTRIES} -gt 1 ] ; then
        echo "REPORT STATUS OF '${PKI_TYPE}' INSTANCE(S):"
    fi

    # Obtain status of every PKI instance of this type
    for PKI_REGISTRY_ENTRY in ${PKI_REGISTRY_ENTRIES}; do
        # Source values associated with this particular PKI instance
        [ -f ${PKI_REGISTRY_ENTRY} ] &&
        . ${PKI_REGISTRY_ENTRY}

        [ ${TOTAL_PKI_REGISTRY_ENTRIES} -gt 1 ] && echo

        case $PKI_WEB_SERVER_TYPE in
            tomcat)
                if $debian; then
                    display_instance_status_debian
                    rv=$?
                else
                    display_instance_status_systemd
                    rv=$?
                fi
                ;;
        esac
        if [ $rv -ne 0 ] ; then
            errors=`expr $errors + 1`
            error_rv=$rv
        fi
    done

    # ONLY print a "WARNING" message if multiple
    # instances are being examined
    if [ ${TOTAL_PKI_REGISTRY_ENTRIES} -gt 1 ] ; then
        if [ ${errors} -eq 1 ]; then
            # Since only ONE error exists, return that "bad" error code.
            rv=${error_rv}
        elif [ ${errors} -gt 1 ]; then
            # Since MORE than ONE error exists, return an OVERALL status
            # of "4 - program or service status is unknown"
            rv=4
        fi

        if [ ${errors} -ge 1 ]; then
            echo
            echo -n "WARNING:  "
            echo -n "${errors} of ${TOTAL_PKI_REGISTRY_ENTRIES} "
            echo -n "'${PKI_TYPE}' instances reported status failures!"
            echo
        fi

        if [ ${TOTAL_UNCONFIGURED_PKI_ENTRIES} -ge 1 ]; then
            echo
            echo -n "WARNING:  "
            echo -n "${TOTAL_UNCONFIGURED_PKI_ENTRIES} "
            echo -n "of ${TOTAL_PKI_REGISTRY_ENTRIES} "
            echo -n "'${PKI_TYPE}' instances MUST be configured!"
            echo
        fi

        echo
        echo "FINISHED REPORTING STATUS OF '${PKI_TYPE}' INSTANCE(S)."
    fi

    return $rv
}

