From 95bb229337a0f41a7e3113036ec70952c6889ee3 Mon Sep 17 00:00:00 2001
From: Matthew Harmsen <mharmsen@redhat.com>
Date: Mon, 27 Aug 2012 20:43:22 -0700
Subject: [PATCH] Verify symbolic links

* TRAC Ticket #301 - Need to modify init scripts to verify needed
  symlinks in an instance
---
 base/setup/scripts/functions |  415 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 408 insertions(+), 7 deletions(-)

diff --git a/base/setup/scripts/functions b/base/setup/scripts/functions
index 516bf32..12d0a7f 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
@@ -154,7 +154,7 @@ usage_systemd()
     echo -n "|try-restart"
     echo -n "|reload"
     echo -n "|status} "
-    echo -n "subsytem-type "
+    echo -n "subsystem-type "
     echo -n "[instance-name]"
     echo
     echo
@@ -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,400 @@ 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)
+
+    # '${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
+}
+
 start_instance()
 {
     rv=0
@@ -746,11 +1140,18 @@ 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 
+            # 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 +1161,7 @@ start_instance()
                      $PKI_INSTANCE_INITSCRIPT start
                 rv=$?
             else
-                $PKI_INSTANCE_INITSCRIPT start 
+                $PKI_INSTANCE_INITSCRIPT start
                 rv=$?
             fi
             ;;
@@ -1065,9 +1466,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

