From 6d4f1c39cf4b96d7055511bd2565a8ecf51f1405 Mon Sep 17 00:00:00 2001
From: Matthew Harmsen <mharmsen@redhat.com>
Date: Mon, 27 Aug 2012 20:48:34 -0700
Subject: [PATCH] Verify symbolic links and update CS.cfg for Dogtag 10

* TRAC Ticket #301 - Need to modify init scripts to verify needed
  symlinks in an instance
* TRAC Ticket #303 - Dogtag 10: CS.cfg parameters for Dogtag 9 instance
  running under Dogtag 10 packages . . .
---
 base/deploy/scripts/operations          |  468 ++++++++++++++++++++++++++++++
 base/deploy/src/scriptlets/pkiparser.py |    2 +-
 base/setup/scripts/functions            |  470 ++++++++++++++++++++++++++++++-
 3 files changed, 933 insertions(+), 7 deletions(-)

diff --git a/base/deploy/scripts/operations b/base/deploy/scripts/operations
index 4f89c1a..5d8b779 100644
--- a/base/deploy/scripts/operations
+++ b/base/deploy/scripts/operations
@@ -767,6 +767,467 @@ display_instance_status()
     return $rv
 }
 
+make_symlink()
+{
+    symlink="${1}"
+    target="${2}"
+    user="${3}"
+    group="${4}"
+
+    rv=0
+
+    # 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
+
+    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}"
+        if [ -e ${symlink} ]; then
+            if [ -h ${symlink} ]; then
+                target=${symlinks[${key}]}
+                # 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
+                        # Attempt to remove the dangling symlink and
+                        # issue an ERROR that the expected target to which
+                        # the symbolic link is expected to point is NOT
+                        # fully resolvable.
+                        rm ${symlink}
+                        rv=$?
+                        if [ $rv -ne 0 ]; then
+                            echo "ERROR:  Failed to remove dangling"
+                            echo "        '${symlink}' ->"\
+                                 "'${target}'!"
+                        else
+                            echo "WARNING:  Removed dangling '${symlink}' ->"\
+                                 "'${target}'!"
+                        fi
+                        echo "ERROR:  Checking '${symlink}' -> '${target}'"
+                        echo "        where '${target}' is NOT fully"
+                        echo "        resolvable!"
+                        rv=1
+                    else
+                        current_target=`readlink ${symlink}`
+                        # Check to see if the current_target
+                        # to which the symlink points, exists.
+                        if [ -e ${current_target} ]; then
+                            # Verify that the current target to which the
+                            # symlink points is the expected target!
+                            if [ ${current_target} == ${target} ]; then
+                                # ALWAYS run 'chown' on an existing '${symlink}'
+                                #
+                                #     NOTE:  Ignore 'chown' errors.
+                                #
+                                chown -h ${user}:${group} ${symlink}
+                                # echo "SUCCESS:  '${symlink}' -> '${target}'"
+                            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.
+                                #
+                                # NOTE:  For this particular case, although
+                                #        it is known that the 'current_target'
+                                #        exists, we do NOT care whether or not
+                                #        this 'current_target' is fully
+                                #        resolvable (i. e. - the current
+                                #        symlink may still be a dangling
+                                #        symlink) since we will be attempting
+                                #        to change the symlink to point to the
+                                #        expected 'target' which we already
+                                #        know to be fully resolvable.
+                                #
+                                echo "WARNING:  Attempting to change"
+                                echo "          '${symlink}' to point to"
+                                echo "          '${target}' INSTEAD of"
+                                echo "          '${current_target}'!"
+                                rm ${symlink}
+                                rv=$?
+                                if [ $rv -ne 0 ]; then
+                                    echo "ERROR:  Failed to remove"
+                                    echo "        '${symlink}' ->"\
+                                         "'${current_target}'!"
+                                    rv=1
+                                else
+                                    echo "SUCCESS:  Removed '${symlink}' ->"\
+                                         "'${current_target}'!"
+                                    make_symlink ${symlink} ${target}\
+                                                 ${user} ${group}
+                                    rv=$?
+                                fi
+                            fi
+                        else
+                            # Attempt to remove this dangling symbolic link and
+                            # issue a WARNING that a new symbolic
+                            # link is being created to point to the expected
+                            # target rather than the current missing target
+                            # to which it points.
+                            echo "WARNING:  Attempting to change dangling"
+                            echo "          '${symlink}' to point to"
+                            echo "          '${target}' INSTEAD of missing"
+                            echo "          '${current_target}'!"
+                            # Attempt to create the symlink and chown it.
+                            rm ${symlink}
+                            rv=$?
+                            if [ $rv -ne 0 ]; then
+                                echo "ERROR:  Failed to remove dangling"
+                                echo "        '${symlink}' ->"\
+                                     "'${current_target}'!"
+                                rv=1
+                            else
+                                echo "SUCCESS:  Removed dangling"\
+                                     "'${symlink}' -> '${current_target}'!"
+                                make_symlink ${symlink} ${target}\
+                                             ${user} ${group}
+                                rv=$?
+                            fi
+                        fi
+                    fi
+                else
+                    # Attempt to remove this dangling symbolic link and
+                    # issue an ERROR that the target to which the
+                    # symbolic link is expected to point does NOT exist.
+                    rm ${symlink}
+                    rv=$?
+                    if [ $rv -ne 0 ]; then
+                        echo "ERROR:  Failed to remove dangling"
+                        echo "        '${symlink}' ->"\
+                             "'${target}'!"
+                    else
+                        echo "WARNING:  Removed dangling '${symlink}' ->"\
+                             "'${target}'!"
+                    fi
+                    echo "ERROR:  Checking '${symlink}' -> '${target}'"
+                    echo "        where '${target}' does NOT exist!"
+                    rv=1
+                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
+            # This symbolic link does not exist; attempt to create it.
+            echo "WARNING:  '${symlink}' does NOT currently exist!"
+            echo "          Attempting to create '${symlink}' . . ."
+            target=${symlinks[${key}]}
+            if [ -e ${target} ]; then
+                # Check that the target is fully resolvable!
+                if [ ! `readlink -qe ${target}` ]; then
+                    # Issue an ERROR that the target to which the
+                    # symbolic link points is NOT fully resolvable!
+                    echo "ERROR:  '${symlink}' will NOT be created because"
+                    echo "        the '${target}' to which it must point"
+                    echo "        is NOT fully resolvable!"
+                    rv=1
+                else
+                    # Attempt to create the symlink and chown it.
+                    make_symlink ${symlink} ${target} ${user} ${group}
+                    rv=$?
+                fi
+            else
+                # Issue an ERROR that NEITHER the symbolic link NOR the
+                # target to which the symbolic link points exist.
+                echo "ERROR:  Neither '${symlink}' nor '${target}' exist!"
+                rv=1
+            fi
+        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 root_symlinks
+    declare -A ca_symlinks
+    declare -A kra_symlinks
+    declare -A ocsp_symlinks
+    declare -A tks_symlinks
+    declare -A common_jar_symlinks
+    declare -A ca_jar_symlinks
+    declare -A kra_jar_symlinks
+    declare -A ocsp_jar_symlinks
+    declare -A tks_jar_symlinks
+    declare -A systemd_symlinks
+
+    # Dogtag 10 Conditional Variables
+    if [ ${ARCHITECTURE} == "x86_64" ]; then
+        jni_dir="/usr/lib64/java"
+    else
+        jni_dir="/usr/lib/java"
+    fi
+
+    # Dogtag 10 Symbolic Link Target Variables
+    java_dir="/usr/share/java"
+    pki_systemd_service="pki-${PKI_WEB_SERVER_TYPE}d@.service"
+    resteasy_java_dir="/usr/share/java/resteasy"
+    systemd_dir="/lib/systemd/system"
+
+    # Dogtag 10 Symbolic Link Variables
+    pki_common_jar_dir="${PKI_INSTANCE_PATH}/common/lib"
+    pki_registry_dir="/etc/sysconfig/pki/${PKI_WEB_SERVER_TYPE}/${PKI_INSTANCE_ID}"
+    pki_systemd_dir="/etc/systemd/system/pki-tomcatd.target.wants"
+    pki_systemd_link="pki-${PKI_WEB_SERVER_TYPE}d@${PKI_INSTANCE_ID}.service"
+    # FUTURE: "pki_<pki_subsystem>_webapps_jar_dir" directories
+    pki_ca_jar_dir="${pki_common_jar_dir}"
+    pki_kra_jar_dir="${pki_common_jar_dir}"
+    pki_ocsp_jar_dir="${pki_common_jar_dir}"
+    pki_tks_jar_dir="${pki_common_jar_dir}"
+
+    # '${PKI_INSTANCE_PATH}' symlinks
+    base_symlinks=(
+        [alias]=/etc/pki/${PKI_INSTANCE_ID}/alias
+        [bin]=/usr/share/tomcat/bin
+        [conf]=/etc/pki/${PKI_INSTANCE_ID}
+        [lib]=/usr/share/tomcat/lib
+        [logs]=/var/log/pki/${PKI_INSTANCE_ID})
+
+    # '${PKI_INSTANCE_PATH}' symlinks (root:root ownership)
+    root_symlinks[${PKI_INSTANCE_ID}]=/usr/sbin/tomcat-sysd
+
+    # '${PKI_INSTANCE_PATH}/ca' symlinks
+    ca_symlinks=(
+        [alias]=${PKI_INSTANCE_PATH}/alias
+        [conf]=/etc/pki/${PKI_INSTANCE_ID}/ca
+        [logs]=/var/log/pki/${PKI_INSTANCE_ID}/ca
+        [registry]=${pki_registry_dir}
+        [webapps]=${PKI_INSTANCE_PATH}/webapps)
+
+    # '${pki_ca_jar_dir}' symlinks
+    ca_jar_symlinks[pki-ca.jar]=/usr/share/java/pki/pki-ca.jar
+
+    # '${PKI_INSTANCE_PATH}/kra' symlinks
+    kra_symlinks=(
+        [alias]=${PKI_INSTANCE_PATH}/alias
+        [conf]=/etc/pki/${PKI_INSTANCE_ID}/kra
+        [logs]=/var/log/pki/${PKI_INSTANCE_ID}/kra
+        [registry]=${pki_registry_dir}
+        [webapps]=${PKI_INSTANCE_PATH}/webapps)
+
+    # '${pki_kra_jar_dir}' symlinks
+    kra_jar_symlinks[pki-kra.jar]=/usr/share/java/pki/pki-kra.jar
+
+    # '${PKI_INSTANCE_PATH}/ocsp' symlinks
+    ocsp_symlinks=(
+        [alias]=${PKI_INSTANCE_PATH}/alias
+        [conf]=/etc/pki/${PKI_INSTANCE_ID}/ocsp
+        [logs]=/var/log/pki/${PKI_INSTANCE_ID}/ocsp
+        [registry]=${pki_registry_dir}
+        [webapps]=${PKI_INSTANCE_PATH}/webapps)
+
+    # '${pki_ocsp_jar_dir}' symlinks
+    ocsp_jar_symlinks[pki-ocsp.jar]=/usr/share/java/pki/pki-ocsp.jar
+
+    # '${PKI_INSTANCE_PATH}/tks' symlinks
+    tks_symlinks=(
+        [alias]=${PKI_INSTANCE_PATH}/alias
+        [conf]=/etc/pki/${PKI_INSTANCE_ID}/tks
+        [logs]=/var/log/pki/${PKI_INSTANCE_ID}/tks
+        [registry]=${pki_registry_dir}
+        [webapps]=${PKI_INSTANCE_PATH}/webapps)
+
+    # '${pki_tks_jar_dir}' symlinks
+    tks_jar_symlinks[pki-tks.jar]=/usr/share/java/pki/pki-tks.jar
+
+    # '${pki_common_jar_dir}' symlinks
+    common_jar_symlinks=(
+        [apache-commons-codec.jar]=${java_dir}/commons-codec.jar
+        [apache-commons-collections.jar]=${java_dir}/apache-commons-collections.jar
+        [apache-commons-lang.jar]=${java_dir}/apache-commons-lang.jar
+        [apache-commons-logging.jar]=${java_dir}/apache-commons-logging.jar
+        [httpclient.jar]=${java_dir}/httpcomponents/httpclient.jar
+        [httpcore.jar]=${java_dir}/httpcomponents/httpcore.jar
+        [javassist.jar]=${java_dir}/javassist.jar
+        [jaxrs-api.jar]=${resteasy_java_dir}/jaxrs-api.jar
+        [jettison.jar]=${java_dir}/jettison.jar
+        [jss4.jar]=${jni_dir}/jss4.jar
+        [ldapjdk.jar]=${java_dir}/ldapjdk.jar
+        [pki-certsrv.jar]=/usr/share/java/pki/pki-certsrv.jar
+        [pki-cms.jar]=/usr/share/java/pki/pki-cms.jar
+        [pki-cmsbundle.jar]=/usr/share/java/pki/pki-cmsbundle.jar
+        [pki-cmscore.jar]=/usr/share/java/pki/pki-cmscore.jar
+        [pki-cmsutil.jar]=/usr/share/java/pki/pki-cmsutil.jar
+        [pki-nsutil.jar]=/usr/share/java/pki/pki-nsutil.jar
+        [resteasy-atom-provider.jar]=${resteasy_java_dir}/resteasy-atom-provider.jar
+        [resteasy-jaxb-provider.jar]=${resteasy_java_dir}/resteasy-jaxb-provider.jar
+        [resteasy-jaxrs.jar]=${resteasy_java_dir}/resteasy-jaxrs.jar
+        [resteasy-jettison-provider.jar]=${resteasy_java_dir}/resteasy-jettison-provider.jar
+        [scannotation.jar]=${java_dir}/scannotation.jar
+        [symkey.jar]=${jni_dir}/symkey.jar
+        [tomcatjss.jar]=${java_dir}/tomcat7jss.jar
+        [velocity.jar]=${java_dir}/velocity.jar
+        [xerces-j2.jar]=${java_dir}/xerces-j2.jar
+        [xml-commons-apis.jar]=${java_dir}/xml-commons-apis.jar
+        [xml-commons-resolver.jar]=${java_dir}/xml-commons-resolver.jar)
+
+    # '${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
+
+        # Detect and correct 'root_symlinks'
+        root_symlinks_string=$(declare -p root_symlinks)
+        eval "declare -A symlinks=${root_symlinks_string#*=}"
+        check_symlinks ${PKI_INSTANCE_PATH} "root" "root"
+        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
+            # Detect and correct 'ca_jar_symlinks'
+            ca_jar_symlinks_string=$(declare -p ca_jar_symlinks)
+            eval "declare -A symlinks=${ca_jar_symlinks_string#*=}"
+            check_symlinks ${pki_ca_jar_dir} ${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
+            # Detect and correct 'kra_jar_symlinks'
+            kra_jar_symlinks_string=$(declare -p kra_jar_symlinks)
+            eval "declare -A symlinks=${kra_jar_symlinks_string#*=}"
+            check_symlinks ${pki_kra_jar_dir} ${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
+            # Detect and correct 'ocsp_jar_symlinks'
+            ocsp_jar_symlinks_string=$(declare -p ocsp_jar_symlinks)
+            eval "declare -A symlinks=${ocsp_jar_symlinks_string#*=}"
+            check_symlinks ${pki_ocsp_jar_dir} ${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
+            # Detect and correct 'tks_jar_symlinks'
+            tks_jar_symlinks_string=$(declare -p tks_jar_symlinks)
+            eval "declare -A symlinks=${tks_jar_symlinks_string#*=}"
+            check_symlinks ${pki_tks_jar_dir} ${PKI_USER} ${PKI_GROUP}
+            rv=$?
+            if [ $rv -ne 0 ]; then
+                return $rv
+            fi
+        fi
+
+        # Detect and correct 'common_jar_symlinks'
+        common_jar_symlinks_string=$(declare -p common_jar_symlinks)
+        eval "declare -A symlinks=${common_jar_symlinks_string#*=}"
+        check_symlinks ${pki_common_jar_dir} ${PKI_USER} ${PKI_GROUP}
+        rv=$?
+        if [ $rv -ne 0 ]; then
+            return $rv
+        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
+}
+
 start_instance()
 {
     rv=0
@@ -775,6 +1236,13 @@ start_instance()
         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
+
     # Invoke the initscript for this instance
     case $PKI_WEB_SERVER_TYPE in
         tomcat)
diff --git a/base/deploy/src/scriptlets/pkiparser.py b/base/deploy/src/scriptlets/pkiparser.py
index 1fe74e8..4b61284 100644
--- a/base/deploy/src/scriptlets/pkiparser.py
+++ b/base/deploy/src/scriptlets/pkiparser.py
@@ -69,7 +69,7 @@ def process_command_line_arguments(argv):
         mandatory.add_argument('-i',
                                dest='pki_deployed_instance_name',
                                action='store',
-                               nargs=1, metavar='<instance>',
+                               nargs=1, required=True, metavar='<instance>',
                                help='FORMAT:  ${pki_instance_name}'
                                     '[.${pki_admin_domain_name}]')
     # Establish 'Optional' command-line options
diff --git a/base/setup/scripts/functions b/base/setup/scripts/functions
index a4318ef..9abf2f0 100644
--- a/base/setup/scripts/functions
+++ b/base/setup/scripts/functions
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # From "http://fedoraproject.org/wiki/FCNewInit/Initscripts":
-# 
+#
 # Status Exit Codes
 #
 #  0 program is running or service is OK
@@ -203,7 +203,7 @@ if [ $SYSTEMD ]; then
             exit 4
         fi
     fi
-else  
+else
     if [ $# -lt 1 ] ; then
         # 3 unimplemented feature (for example, "reload")
         #     [insufficient arguments]
@@ -738,6 +738,450 @@ display_instance_status()
     return $rv
 }
 
+make_symlink()
+{
+    symlink="${1}"
+    target="${2}"
+    user="${3}"
+    group="${4}"
+
+    rv=0
+
+    # 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
+
+    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}"
+        if [ -e ${symlink} ]; then
+            if [ -h ${symlink} ]; then
+                target=${symlinks[${key}]}
+                # 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
+                        # Attempt to remove the dangling symlink and
+                        # issue an ERROR that the expected target to which
+                        # the symbolic link is expected to point is NOT
+                        # fully resolvable.
+                        rm ${symlink}
+                        rv=$?
+                        if [ $rv -ne 0 ]; then
+                            echo "ERROR:  Failed to remove dangling"
+                            echo "        '${symlink}' ->"\
+                                 "'${target}'!"
+                        else
+                            echo "WARNING:  Removed dangling '${symlink}' ->"\
+                                 "'${target}'!"
+                        fi
+                        echo "ERROR:  Checking '${symlink}' -> '${target}'"
+                        echo "        where '${target}' is NOT fully"
+                        echo "        resolvable!"
+                        rv=1
+                    else
+                        current_target=`readlink ${symlink}`
+                        # Check to see if the current_target
+                        # to which the symlink points, exists.
+                        if [ -e ${current_target} ]; then
+                            # Verify that the current target to which the
+                            # symlink points is the expected target!
+                            if [ ${current_target} == ${target} ]; then
+                                # ALWAYS run 'chown' on an existing '${symlink}'
+                                #
+                                #     NOTE:  Ignore 'chown' errors.
+                                #
+                                chown -h ${user}:${group} ${symlink}
+                                # echo "SUCCESS:  '${symlink}' -> '${target}'"
+                            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.
+                                #
+                                # NOTE:  For this particular case, although
+                                #        it is known that the 'current_target'
+                                #        exists, we do NOT care whether or not
+                                #        this 'current_target' is fully
+                                #        resolvable (i. e. - the current
+                                #        symlink may still be a dangling
+                                #        symlink) since we will be attempting
+                                #        to change the symlink to point to the
+                                #        expected 'target' which we already
+                                #        know to be fully resolvable.
+                                #
+                                echo "WARNING:  Attempting to change"
+                                echo "          '${symlink}' to point to"
+                                echo "          '${target}' INSTEAD of"
+                                echo "          '${current_target}'!"
+                                rm ${symlink}
+                                rv=$?
+                                if [ $rv -ne 0 ]; then
+                                    echo "ERROR:  Failed to remove"
+                                    echo "        '${symlink}' ->"\
+                                         "'${current_target}'!"
+                                    rv=1
+                                else
+                                    echo "SUCCESS:  Removed '${symlink}' ->"\
+                                         "'${current_target}'!"
+                                    make_symlink ${symlink} ${target}\
+                                                 ${user} ${group}
+                                    rv=$?
+                                fi
+                            fi
+                        else
+                            # Attempt to remove this dangling symbolic link and
+                            # issue a WARNING that a new symbolic
+                            # link is being created to point to the expected
+                            # target rather than the current missing target
+                            # to which it points.
+                            echo "WARNING:  Attempting to change dangling"
+                            echo "          '${symlink}' to point to"
+                            echo "          '${target}' INSTEAD of missing"
+                            echo "          '${current_target}'!"
+                            # Attempt to create the symlink and chown it.
+                            rm ${symlink}
+                            rv=$?
+                            if [ $rv -ne 0 ]; then
+                                echo "ERROR:  Failed to remove dangling"
+                                echo "        '${symlink}' ->"\
+                                     "'${current_target}'!"
+                                rv=1
+                            else
+                                echo "SUCCESS:  Removed dangling"\
+                                     "'${symlink}' -> '${current_target}'!"
+                                make_symlink ${symlink} ${target}\
+                                             ${user} ${group}
+                                rv=$?
+                            fi
+                        fi
+                    fi
+                else
+                    # Attempt to remove this dangling symbolic link and
+                    # issue an ERROR that the target to which the
+                    # symbolic link is expected to point does NOT exist.
+                    rm ${symlink}
+                    rv=$?
+                    if [ $rv -ne 0 ]; then
+                        echo "ERROR:  Failed to remove dangling"
+                        echo "        '${symlink}' ->"\
+                             "'${target}'!"
+                    else
+                        echo "WARNING:  Removed dangling '${symlink}' ->"\
+                             "'${target}'!"
+                    fi
+                    echo "ERROR:  Checking '${symlink}' -> '${target}'"
+                    echo "        where '${target}' does NOT exist!"
+                    rv=1
+                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
+            # This symbolic link does not exist; attempt to create it.
+            echo "WARNING:  '${symlink}' does NOT currently exist!"
+            echo "          Attempting to create '${symlink}' . . ."
+            target=${symlinks[${key}]}
+            if [ -e ${target} ]; then
+                # Check that the target is fully resolvable!
+                if [ ! `readlink -qe ${target}` ]; then
+                    # Issue an ERROR that the target to which the
+                    # symbolic link points is NOT fully resolvable!
+                    echo "ERROR:  '${symlink}' will NOT be created because"
+                    echo "        the '${target}' to which it must point"
+                    echo "        is NOT fully resolvable!"
+                    rv=1
+                else
+                    # Attempt to create the symlink and chown it.
+                    make_symlink ${symlink} ${target} ${user} ${group}
+                    rv=$?
+                fi
+            else
+                # Issue an ERROR that NEITHER the symbolic link NOR the
+                # target to which the symbolic link points exist.
+                echo "ERROR:  Neither '${symlink}' nor '${target}' exist!"
+                rv=1
+            fi
+        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 -A apache_symlinks
+    declare -A perl_symlinks
+    declare -A base_symlinks
+    declare -A root_symlinks
+    declare -A common_jar_symlinks
+    declare -A webapps_jar_symlinks
+    declare -A systemd_symlinks
+
+    # Dogtag 9 Conditional Variables
+    if [ ${ARCHITECTURE} == "x86_64" ]; then
+        jni_dir="/usr/lib64/java"
+    else
+        jni_dir="/usr/lib/java"
+    fi
+    if   [ ${PKI_SUBSYSTEM_TYPE} == "ca" ]; then
+        pki_systemd_link="pki-cad@${PKI_INSTANCE_ID}.service"
+        pki_systemd_service="pki-cad@.service"
+    elif [ ${PKI_SUBSYSTEM_TYPE} == "kra" ]; then
+        pki_systemd_link="pki-krad@${PKI_INSTANCE_ID}.service"
+        pki_systemd_service="pki-krad@.service"
+    elif [ ${PKI_SUBSYSTEM_TYPE} == "ocsp" ]; then
+        pki_systemd_link="pki-ocspd@${PKI_INSTANCE_ID}.service"
+        pki_systemd_service="pki-ocspd@.service"
+    elif [ ${PKI_SUBSYSTEM_TYPE} == "ra" ]; then
+        pki_systemd_link="pki-rad@${PKI_INSTANCE_ID}.service"
+        pki_systemd_service="pki-rad@.service"
+    elif [ ${PKI_SUBSYSTEM_TYPE} == "tks" ]; then
+        pki_systemd_link="pki-tksd@${PKI_INSTANCE_ID}.service"
+        pki_systemd_service="pki-tksd@.service"
+    elif [ ${PKI_SUBSYSTEM_TYPE} == "tps" ]; then
+        pki_systemd_link="pki-tpsd@${PKI_INSTANCE_ID}.service"
+        pki_systemd_service="pki-tpsd@.service"
+    fi
+
+    # Dogtag 9 Symbolic Link Target Variables
+    systemd_dir="/lib/systemd/system"
+
+    # Dogtag 9 Symbolic Link Variables
+    pki_common_jar_dir="${PKI_INSTANCE_PATH}/common/lib"
+    # pki_registry_dir="/etc/sysconfig/pki/${PKI_SUBSYSTEM_TYPE}/${PKI_INSTANCE_ID}"
+    pki_systemd_dir="/etc/systemd/system/pki-cad.target.wants"
+    pki_webapps_jar_dir="${PKI_INSTANCE_PATH}/webapps/${PKI_SUBSYSTEM_TYPE}/WEB-INF/lib"
+
+    # '${PKI_INSTANCE_PATH}' symlinks
+    apache_symlinks=(
+        [conf]=/etc/${PKI_INSTANCE_ID}
+        [logs]=/var/log/${PKI_INSTANCE_ID}
+        [run]=/var/run/pki/${PKI_SUBSYSTEM_TYPE})
+
+    base_symlinks=(
+        [conf]=/etc/${PKI_INSTANCE_ID}
+        [logs]=/var/log/${PKI_INSTANCE_ID})
+
+    # '${PKI_INSTANCE_PATH}' symlinks (root:root ownership)
+    root_symlinks[${PKI_INSTANCE_ID}]=/usr/sbin/tomcat6-sysd
+
+    # '${PKI_INSTANCE_PATH}/lib' symlinks
+    perl_symlinks[perl]=/usr/share/pki/${PKI_SUBSYSTEM_TYPE}/lib/perl
+
+    # '${pki_common_jar_dir}' symlinks
+    common_jar_symlinks=(
+        [apache-commons-logging.jar]=/usr/share/java/apache-commons-logging.jar
+        [jss4.jar]=${jni_dir}/jss4.jar
+        [tomcatjss.jar]=/usr/share/java/tomcatjss.jar
+        # Dogtag 9 -> Dogtag 10
+        [apache-commons-codec.jar]=/usr/share/java/commons-codec.jar)
+
+    # '${pki_webapps_jar_dir}' symlinks
+    webapps_jar_symlinks=(
+        [apache-commons-collections.jar]=/usr/share/java/apache-commons-collections.jar
+        [apache-commons-lang.jar]=/usr/share/java/apache-commons-lang.jar
+        [ldapjdk.jar]=/usr/share/java/ldapjdk.jar
+        # [osutil.jar]=${jni_dir}/osutil.jar
+        [${PKI_INSTANCE_ID}.jar]=/usr/share/java/pki/${PKI_INSTANCE_ID}.jar
+        [pki-certsrv.jar]=/usr/share/java/pki/pki-certsrv.jar
+        [pki-cms.jar]=/usr/share/java/pki/pki-cms.jar
+        [pki-cmsbundle.jar]=/usr/share/java/pki/pki-cmsbundle.jar
+        [pki-cmscore.jar]=/usr/share/java/pki/pki-cmscore.jar
+        [pki-cmsutil.jar]=/usr/share/java/pki/pki-cmsutil.jar
+        [pki-nsutil.jar]=/usr/share/java/pki/pki-nsutil.jar
+        [symkey.jar]=${jni_dir}/symkey.jar
+        [velocity.jar]=/usr/share/java/velocity.jar
+        [xerces-j2.jar]=/usr/share/java/xerces-j2.jar
+        [xml-commons-apis.jar]=/usr/share/java/xml-commons-apis.jar
+        [xml-commons-resolver.jar]=/usr/share/java/xml-commons-resolver.jar)
+
+    # '${pki_systemd_dir}' symlinks
+    systemd_symlinks[${pki_systemd_link}]=${systemd_dir}/${pki_systemd_service}
+
+    # Detect and correct PKI subsystem 'instance' 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_SUBSYSTEM_TYPE}" == "ra"   ]  ||
+         [ "${PKI_SUBSYSTEM_TYPE}" == "tps"  ]
+    then
+        # Detect and correct 'apache_symlinks'
+        apache_symlinks_string=$(declare -p apache_symlinks)
+        eval "declare -A symlinks=${apache_symlinks_string#*=}"
+        check_symlinks ${PKI_INSTANCE_PATH} ${PKI_USER} ${PKI_GROUP}
+        rv=$?
+        if [ $rv -ne 0 ]; then
+            return $rv
+        fi
+
+        # Detect and correct 'perl_symlinks'
+        perl_symlinks_string=$(declare -p perl_symlinks)
+        eval "declare -A symlinks=${perl_symlinks_string#*=}"
+        check_symlinks ${PKI_INSTANCE_PATH}/lib ${PKI_USER} ${PKI_GROUP}
+        rv=$?
+        if [ $rv -ne 0 ]; then
+            return $rv
+        fi
+
+        if [ "${PKI_SUBSYSTEM_TYPE}" == "tps"  ]; then
+            # ALWAYS recreate this "convenience" link and 'chown' it
+            # NOTE:  Ignore 'chown' errors.
+            cd ${PKI_INSTANCE_PATH}/docroot ;
+            ln -s tokendb tus ;
+            rv=$?
+            if [ $rv -eq 0 ]; then
+                chown -h ${PKI_USER}:${PKI_GROUP} tus
+                # echo "SUCCESS:  Created 'tus' -> 'tokendb'"
+            else
+                echo "ERROR:  Failed to create 'tus' -> 'tokendb' convenience"
+                echo "        symbolic link for '${PKI_INSTANCE_ID}'!"
+                return 1
+            fi
+        fi
+    elif [ "${PKI_SUBSYSTEM_TYPE}" == "ca"   ]  ||
+         [ "${PKI_SUBSYSTEM_TYPE}" == "kra"  ]  ||
+         [ "${PKI_SUBSYSTEM_TYPE}" == "ocsp" ]  ||
+         [ "${PKI_SUBSYSTEM_TYPE}" == "tks"  ]
+    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
+
+        # Detect and correct 'root_symlinks'
+        root_symlinks_string=$(declare -p root_symlinks)
+        eval "declare -A symlinks=${root_symlinks_string#*=}"
+        check_symlinks ${PKI_INSTANCE_PATH} "root" "root"
+        rv=$?
+        if [ $rv -ne 0 ]; then
+            return $rv
+        fi
+
+        # Detect and correct 'common_jar_symlinks'
+        common_jar_symlinks_string=$(declare -p common_jar_symlinks)
+        eval "declare -A symlinks=${common_jar_symlinks_string#*=}"
+        check_symlinks ${pki_common_jar_dir} ${PKI_USER} ${PKI_GROUP}
+        rv=$?
+        if [ $rv -ne 0 ]; then
+            return $rv
+        fi
+
+        # Detect and correct 'webapps_jar_symlinks'
+        webapps_jar_symlinks_string=$(declare -p webapps_jar_symlinks)
+        eval "declare -A symlinks=${webapps_jar_symlinks_string#*=}"
+        check_symlinks ${pki_webapps_jar_dir} ${PKI_USER} ${PKI_GROUP}
+        rv=$?
+        if [ $rv -ne 0 ]; then
+            return $rv
+        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
+}
+
+# NOTE:  This code will NOT be executed if the file called
+#        '${PKI_INSTANCE_PATH}/conf/DOGTAG_10_UPDATE_MARKER' exists!
+update_cs_cfg_for_dogtag_10()
+{
+    # declare a simple array (to maintain specified parameter order)
+    # and specify Dogtag 10 'CS.cfg' specific parameters (CA specific)
+    declare -a dogtag_10_cs_cfg_parameters=(
+        processor.caDoRevoke.authorityId=ca
+        processor.caDoRevoke.authzMgr=BasicAclAuthz
+        processor.caDoRevoke.authzResourceName=certServer.ee.certificates
+        processor.caDoRevoke.getClientCert=false
+        processor.caDoRevoke-agent.authMgr=certUserDBAuthMgr
+        processor.caDoRevoke-agent.authorityId=ca
+        processor.caDoRevoke-agent.authzMgr=BasicAclAuthz
+        processor.caDoRevoke-agent.authzResourceName=certServer.ca.certificates
+        processor.caDoRevoke-agent.getClientCert=true
+        processor.caDoUnrevoke.authMgr=certUserDBAuthMgr
+        processor.caDoUnrevoke.authorityId=ca
+        processor.caDoUnrevoke.authzMgr=BasicAclAuthz
+        processor.caDoUnrevoke.authzResourceName=certServer.ca.certificate
+        processor.caDoUnrevoke.getClientCert=true
+        processor.caProfileProcess.authMgr=certUserDBAuthMgr
+        processor.caProfileProcess.authorityId=ca
+        processor.caProfileProcess.authzMgr=BasicAclAuthz
+        processor.caProfileProcess.authzResourceName=certServer.ca.request.profile
+        processor.caProfileProcess.getClientCert=true
+        processor.caProfileSubmit.authorityId=ca
+        processor.caProfileSubmit.authzMgr=BasicAclAuthz
+        processor.caProfileSubmit.authzResourceName=certServer.ee.profile
+        processor.caProfileSubmit.getClientCert=false)
+
+    # Append ANY missing Dogtag 10 CFG parameter to the end of the 'CS.cfg'
+    for key in "${!dogtag_10_cs_cfg_parameters[@]}"
+    do
+        line="${dogtag_10_cs_cfg_parameters[${key}]}"
+        grep -q ${line} ${pki_instance_configuration_file}
+        rv=$?
+        if [ ${rv} -ne 0 ] ; then
+            echo "INFO:  Appending '${line}' to"\
+                 "'${pki_instance_configuration_file}'"
+            echo ${line} >> ${pki_instance_configuration_file}
+        fi
+    done
+
+    # Create a MARKER to indicate that this update has been completed
+    touch ${PKI_INSTANCE_PATH}/conf/DOGTAG_10_UPDATE_MARKER
+}
+
 start_instance()
 {
     rv=0
@@ -746,11 +1190,25 @@ start_instance()
         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
+
     # Invoke the initscript for this instance
     case $PKI_SUBSYSTEM_TYPE in
         ca|kra|ocsp|tks)
 
-            # We must export the service name so that the systemd version 
+            # If required, update 'CS.cfg' from Dogtag 9 -> Dogtag 10
+            if   [ ${PKI_SUBSYSTEM_TYPE} == "ca" ]  &&
+                 [ ! -e ${PKI_INSTANCE_PATH}/conf/DOGTAG_10_UPDATE_MARKER ]
+            then
+                 update_cs_cfg_for_dogtag_10
+            fi
+
+            # We must export the service name so that the systemd version
             # of the tomcat6 init script knows which instance specific
             # configuration file to source.
             export SERVICE_NAME=$PKI_INSTANCE_ID
@@ -760,7 +1218,7 @@ start_instance()
                      $PKI_INSTANCE_INITSCRIPT start
                 rv=$?
             else
-                $PKI_INSTANCE_INITSCRIPT start 
+                $PKI_INSTANCE_INITSCRIPT start
                 rv=$?
             fi
             ;;
@@ -1065,9 +1523,9 @@ registry_status()
 
         case $PKI_SUBSYSTEM_TYPE in
             ca|kra|ocsp|tks)
-                if [ $SYSTEMD ]; then 
+                if [ $SYSTEMD ]; then
                     display_instance_status_systemd
-                else 
+                else
                     display_instance_status
                 fi
                 rv=$?
-- 
1.7.10.4

