#! /bin/sh
#
# ident data/SCCS/s.install_cluster_zone 1.3 02/02/06:14:24:52 SMI
#
# Script to install a patch cluster supplement for Solaris 10 or later
#
########################################################################

set -e

#
# Force the path to first look in certain directories
#
PATH=/sbin:/usr/sbin:/bin:$PATH
export PATH


#
# Supplement tag information
# (Tag needs to be convertible to a valid filename too...)
SUPPLEMENT_NAME="Solaris 10 Recommended Patch Cluster"

VALID_OS="5.10"
PATCH_ORDER="patch_order"
OS_REL=`uname -r`

# Broadcast what we are doing
#
echo
echo "Patch cluster install script for ${SUPPLEMENT_NAME}"
echo

logname=`echo ${SUPPLEMENT_NAME} | sed 's/ /_/g'`_log
LOGFILE=/var/sadm/install_data/${logname}

#
# Command line and environment option processing
#
# (Default is *not* to save base objects)
#
Usage()
{
	echo "usage: `basename $0` [-q] [-t] [-nosave] [target] [patchdir]"
	echo " [-q]       - quiet option, suppresses interactive warning"
	echo " [-t]       - Maintains the patchadd return codes from the"
        echo "              Solaris release  prior to Solaris 10"
	echo " [-nosave]  - do not save original objects"
        echo " [target]   - destination system to apply or query patches, can be one of:" 
        echo "              [-R client_root_path]  - apply/query patch on bootable client root"
	echo " [patchdir] - optional pathname to directory of patches"

}


#
# Validate the system and zones to ensure processing should continue
#
validate_system()
{

        touch $LOGFILE || {
                echo "ERROR:  $LOGFILE not writeable.\n"
                return 1
        }

        if [ "$OS_REL" != "$VALID_OS" ]; then
                echo "ERROR:  OS is not valid for this cluster.  Exiting."
                return 1
        fi

        ZONENAME="/sbin/zonename"
        if [ -x $ZONENAME ]; then
                if [ `$ZONENAME` != "global" ]; then
                        echo "ERROR:  install_cluster can only be applied to global zone.  Exiting."
                        return 1
                fi
        else
                echo "ERROR:  $ZONENAME cannot be found."
                echo "        install_cluster for $SUPPLEMENT_NAME not applied.  Exiting."
                return 1
        fi

}


cleanup()
{
        #
        # A little cleanup.
        #
        if [ "$finished" = "NO" ]; then
                failed_list="${failed_list} ${patch}"
        fi

        #
        # List the patches that were successfully installed (if any)
        #
        if [ " ${success_list}" != " " ]
        then
                echo
                echo "The following patches were able to be installed:"
		echo ${success_list} | /usr/bin/tr ' ' '\012' | sed 's/^/        /g'
        fi

        #
        # List the patches that failed installation (if any)
        #
        if [ " ${failed_list}" != " " ]
        then
                echo
                echo "ERROR:  The following patches were not able to be installed:"
		echo ${failed_list} | /usr/bin/tr ' ' '\012' | sed 's/^/        /g'
        fi

        echo
        echo "For more installation messages refer to the installation logfile:"
        echo "  ${LOGFILE}"
        echo
        echo "Use '/usr/bin/showrev -p' to verify installed patch-ids."
        echo "Refer to individual patch README files for more patch detail."
        echo "Rebooting the system is usually necessary after installation."
}

 
uid=`id | sed 's/uid=\([0-9]*\)(.*/\1/'`
if [ "$uid" != "0" ] ; then
	echo "You must be root to execute this script."
	exit 1
fi

###################################################################
# New functionality: user prompt driven by install_cluster_question
###################################################################

# Check to see if the install_cluster_question file is present.
# If it is, cat it to stdout - it includes the user prompt for
# a passcode stored in the CLUSTER_README file. If it is present
# but empty or unreadable (shouldn't happen, but it's possible),
# err on the side of caution and provide a default prompt.

answer_required="no"

if [ -f install_cluster_question ]
then
   answer_required="yes"
   if [ -r install_cluster_question -a -s install_cluster_question ]
   then
      cat install_cluster_question
   else
      echo "[empty/unreadable install_cluster_question file: default used]"
      echo "  "
      echo "Special handling may be required for some of the patches in"
      echo "this cluster.  Please refer to the CAUTION section in the"
      echo "cluster README for further instructions on how to proceed."
      echo "  "
      echo "This section also contains a passcode that will need to be"
      echo "provided below to allow the install_cluster script to continue"
      echo "running."
      echo "  "
      echo "Please enter passcode:"
   fi
fi

# If the question file is present, look up the passcode in the
# CLUSTER_README file. Try to anticipate various ways in which
# PASSCODE: may appear in the README file.
# Look for PASSCODE:, remove any tab characters, look for PASSCODE:
# optionally preceded by blanks, only use the first matching line,
# strip away PASSCODE: and any blanks before or after it.

if [ "$answer_required" = "yes" ]
then
   answer_string=`grep PASSCODE: CLUSTER_README | 
      tr -d '\011' |
      egrep -e "^ *PASSCODE:" |
      head -1 |
      sed -e "s/^ *PASSCODE: *//"`

   # If the answer string cannot be found, improvise. Provide a
   # diagnostic message, set the answer to a known value, and
   # provide it to the user so that cluster installation may
   # proceed.

   if [ -z "$answer_string" ]
   then
      echo "  "
      echo "[ERROR: 'PASSCODE:' missing/malformed in CLUSTER_README]"
      echo "  "
      echo "The passcode value expected in the CLUSTER_README file"
      echo "cannot be determined. Please type 'proceed' (no quotes)"
      echo "to proceed with cluster installation:"
      answer_string="proceed"
   fi

   # Prompt user for the answer string. If it's wrong, exit.

   read user_response

   if [ ! "$user_response" = "$answer_string" ]
   then
      echo "Passcode is incorrect; exiting."
      exit 1
   fi

fi  # end of passcode retrieval/response/check block

###################################################################
# end of install_cluster_question / passcode processing
###################################################################

finished="NO"
saveold=''
useOldOSErr=''
quiet="NO"
target=""
while [ "$1" != "" ];
do
	case $1 in
	-nosave)	saveold='-d';;
	--nosave)	saveold='-d';;
	-n)	        saveold='-d';;
	-q)		quiet='YES';;
	-t)		useOldOSErr='-t';;
	-R)		shift
                        if [ $# -gt 0 ] && [ ! -d $1 ]; then
                            echo "$1 client root path not found?"
                            exit 1
                        else
                            target="-R $1" 
                        fi;;
	-*)		Usage; exit 2;;
	*)
		if [ "$finished" = "YES" ]; then
			Usage; exit 2
		fi
		#
		# Where do we find the list of patches to install
		# (we need an absolute pathname...)
		#
		# This is done to maintain the cwd to PATCHDIR to
		# suppress auto unmounts...
		if [ ! -d $1 ]; then
                  echo "$1 optional patch directory not found?"
                  exit 1
                else
		  cd $1
		  finished="YES"
                fi
		;;
	esac
	shift
done
PATCHDIR=`pwd`

if [ "$quiet" != "YES" ]; then
	cat << EOF

*WARNING* SYSTEMS WITH LIMITED DISK SPACE SHOULD *NOT* INSTALL PATCHES:
With or without using the save option, the patch installation process
will still require some amount of disk space for installation and
administrative tasks in the /, /usr, /var, or /opt partitions where
patches are typically installed.  The exact amount of space will
depend on the machine's architecture, software packages already 
installed, and the difference in the patched objects size.  To be
safe, it is not recommended that a patch cluster be installed on a
system with less than 4 MBytes of available space in each of these
partitions.  Running out of disk space during installation may result
in only partially loaded patches.  Check and be sure adequate disk space
is available before continuing.

EOF
	while .
	do
	  echo "Are you ready to continue with install? [y/n]: \c"
	  read reply
	  case $reply
	  in
	    y)	break;;
	    n)	exit;;
	    *)	echo invalid response, enter only 'y' for yes or 'n' for no;;	
	  esac
	done
fi

#
# The patch reference directory must exist
#
if [ ! -d /var/sadm/patch ]; then
        mkdir /var/sadm/patch
        chown bin /var/sadm/patch
        chgrp bin /var/sadm/patch
        chmod 555 /var/sadm/patch
fi

#
# If using save feature:
# Approximate and set cluster save size value.  The maximum save space
# needed should be about no more than half of the actual patch cluster size
# since the patch installation procedure will use compression to save files, and
# patches with multiple architecture packages will only have appropriate
# packages installed on the system.
#
if [ "${saveold}" != -d ]; then
	echo "Determining if sufficient save space exists..."
	BASESIZE=`du -sk $PATCHDIR | cut -f1`
	SUPPLEMENT_SIZE=`expr $BASESIZE / 2`
	bytes_avail=`df -b /var/sadm/patch | tail -1`
	bytes_avail=`echo $bytes_avail | sed 's/.*\ //'`
	if [ ${SUPPLEMENT_SIZE} -gt ${bytes_avail} ]; then
		echo "Insufficient space in /var/sadm/patch to save old files."
		echo "Space required in kilobytes:  ${SUPPLEMENT_SIZE}"
		echo "Space available in kilobytes:  ${bytes_avail}"
		exit 1
	else
		echo "Sufficient save space exists, continuing..."
	fi
else
	echo "The nosave option was used.  Objects will not be saved."
fi


#
# First validate system.  
#
validate_system || exit 1

#
# Install all the patches in patch_order file in PATCHDIR
#
echo "Installing patches located in $PATCH_ORDER file in ${PATCHDIR}"
echo "" >> ${LOGFILE}
echo "" >> ${LOGFILE}
echo "*** Install ${SUPPLEMENT_NAME} begins `date` ***" >> ${LOGFILE}
echo "*** PATCHDIR = ${PATCHDIR} ***" >> ${LOGFILE}
echo "" >> ${LOGFILE}

set +e

finished="YES"
success_list=""
failed_list=
result=0
trap 'echo; echo Interrupted ... Please wait; cleanup; exit 1' 1 2 3 15

# Check for existence of patch_order file
if [ -f $PATCH_ORDER ]; then
    echo "Using $PATCHDIR/$PATCH_ORDER file for patch installation sequence"

    for patch in `cat $PATCH_ORDER` ; do
        if [ -d $patch ]; then
            echo "Installing ${patch}..." | tee -a ${LOGFILE}
            finished="NO"
            ( /usr/sbin/patchadd ${saveold} ${useOldOSErr} \
             ${target} ${patch} ) >> ${LOGFILE} 2>&1
            result=$?
            if [ ${result} -ne 0 ]; then
                echo "  Installation of ${patch} failed. Return code ${result}."
                failed_list="${failed_list} ${patch}"
            else
                echo "  Installation of ${patch} succeeded.  Return code ${result}."
                success_list="${success_list} ${patch}"
            fi
            finished="YES"
        fi
    done
else
    echo ""
    echo "ERROR:  $PATCH_ORDER file does not exist.  Unable to install cluster."
fi

cleanup

if [ " ${failed_list}" = " " ]; then
        exit 0
else
        exit 1
fi
