#!/bin/ksh

# $Id: lupatch.sh 209 2008-12-08 06:06:22Z elkner $
# (C) 2008 by Jens Elkner jel+lu@cs.uni-magdeburg.de
# Licence: CDDL - see http://opensource.org/licenses/cddl1.php

# path to pca
PCA="/local/misc/sbin/pca"
# additional options to use for all pca operations
PCA_OPTS="-a"

export PATH="/usr/bin:/usr/sbin"

usage() {
	printf 'Usage: %s [-h] [-t tmpdir] [-R root_path] [-d] [-r] [-i] [zone ...]
  zone         .. the name of the zone, whoms patches should be installed. If 
                  no zone is given, all available zones will be used instead.
  -t tmpdir    .. directory, where zone patch lists should be stored. 
                  Default: /var/tmp
  -R root_path .. Define the full path name of a directory to use as the
                  root_path (same as in pca(1M) and lumount(1M)). Default: /mnt
  -h           .. print this help and exit
  -d           .. download all patches for the given zones. Also creates the
                  patchlists for the given zone in tmpdir as patchList.$zone
  -r           .. list all patch READMEs for the patch list of the given zones,
                  created by -d .
  -i           .. install patches from the patch list of the given zones,
                  created by -d .
  -D           .. Dry. Just show, what this script would finally do.
' `basename $0`
}

D=""
R=""
I=""
T="/var/tmp"
RPATH="/mnt"
DRY=""

while getopts "ht:driR:D" option ; do
	case "$option" in
		"h") usage; exit 1;;
		"t") T="${OPTARG}"
			if [ ! -d "$T" ]; then
				echo "directory $T does not exist."
				exit 2
			fi
			;;
		"R") RPATH="${OPTARG}"
			if [ ! -d "$RPATH" ]; then
				echo "root directory $RPATH does not exist."
				exit 2
			fi
			;;
		"d") D="true";;
		"r") R="true";;
		"i") I="true";;
		"D") DRY="echo";;
	esac
done
shift $((OPTIND-1))

if [ -z "$D" -a -z "$R" -a -z "$I" ]; then
	usage
	exit 3
fi
integer OSR
OSR=`uname -r | cut -f2 -d.`
if [ -z "$OSR" -o $OSR -lt 10 ]; then
	print -u2 "This script runs on Solaris 5.10 or higher, only."
	exit 4
fi
if [ "$RPATH" = "/" ]; then
	RZPATH="/root"
else
	RZPATH="/lu/a"
fi
if [ -z "$1" ]; then
	ZONES=`zoneadm -R "$RPATH" list -pi | cut -f2 -d: | xargs`
	ZPATHS=`zoneadm -R "$RPATH" list -pi | cut -f2,4 -d: | \
	nawk -F: -v SP=$RZPATH '{
		if ( $1=="global" ) { print $2 " " } else { print $2 SP " " }
	}'`
else
	ZONES=""
	ZPATHS=""
	while [ -n "$1" ]; do
		ZP=`zoneadm -R "$RPATH" -z "$1" list -p 2>/dev/null | cut -f4 -d:`
		if [ -z "$ZP" ]; then
			echo "Zone \"$1\" does not exist - ignored"
		elif [ "$1" = "global" ]; then
			ZONES="global $ZONES"
			ZPATHS="$ZP $ZPATHS"
		else
			ZONES="$ZONES $1"
			ZPATHS="$ZPATHS ${ZP}${RZPATH}"
		fi
		shift
	done
fi

ZDIRS=""
UNAMETAIL=`uname -a | cut -f4- -d\ `
getZoneInfo() {
	# $1 zonename , $2 zonepath incl. trailing /lu/a for none-global zones
	[ ! -f "$2"/etc/release ] && return
	typeset ZN="$1"
	typeset BN="zoneinfo.${ZN}.$$"
	typeset ZIDIR="${T}/$BN"
	typeset ZPATH="$2"
	if [ ! -d $ZIDIR ]; then
		mkdir -p $ZIDIR 2>/dev/null
		if [ $? -ne 0 ]; then
			print -u2 "Unable to create $ZIDIR - exiting."
			exit 5
		fi
	fi
	if [ ! -f "${ZIDIR}/uname.out" ]; then
		typeset LI=`head -1 ${ZPATH}/etc/release | awk '{ print $1 ":" $2 }'`
		[ "${LI%:*}" != "Solaris" ] && return
		if [ "${LI}" = "Solaris:Express" ]; then
			typeset -i ZOR=11
		else
			typeset -i ZOR=${LI#*:} >/dev/null
		fi
		[ $? -ne 0 ] && return
		echo "SunOS ${ZN} 5.${ZOR} ${UNAMETAIL}" >${ZIDIR}/uname.out
	fi
	if [ ! -f "${ZIDIR}/showrev.out" ]; then
		showrev -p -R "${ZPATH}" >${ZIDIR}/showrev.out
	fi
	if [ ! -f "${ZIDIR}/pkginfo.out" ]; then
		pkginfo -x -R "${ZPATH}" >${ZIDIR}/pkginfo.out
	fi
	echo "$ZIDIR"
}

# no ksh93 -> no assoc arrays
set -A ZN $ZONES
set -A ZP $ZPATHS
integer N
integer COUNT
COUNT=${#ZN[@]}
if [ "$D" = "true" ]; then
	N=0
	while [ $N -lt $COUNT ]; do
		[ $N -eq 1 ] && PCA_OPTS="-y ${PCA_OPTS}"
		ZNAME=${ZN[$N]}
		ZPATH=${ZP[$N]}
		echo "\n####### $ZNAME ######\n"
		ZIPATH=`getZoneInfo "$ZNAME" "$ZPATH"`
		if [ -z "$ZIPATH" ]; then
			echo "Skipping unsupported zone $ZNAME"
		else
			ZDIRS="${ZDIRS}	\\ \n	${ZIPATH}"
			$DRY $PCA -d $PCA_OPTS -R "$ZPATH" -f "${ZIPATH}/"
			PL="${T}/patchList.${ZNAME}"
			echo "# $ZNAME" >$PL
			$PCA -l $PCA_OPTS -R "$ZPATH" -f "${ZIPATH}/" \
				--format "%p-%c %i %r%s%b %a %y" >>$PL
			X=`wc -l ${PL} | awk '{ print $1 }'`
			[ "$X" = "6" -o "$X" = "1" ] && rm $PL
		fi
		N=$((N+1))
	done
	echo "\n###### Available patch lists ######\n"
	LIST=`ls -1 ${T}/patchList.* 2>/dev/null`
	if [ -z "$LIST" ]; then
		Z=`echo $ZONES | tr ' ' ','`
		echo "No new patches - $Z seem to be clean."
	else
		echo "$LIST"
	fi
	echo
fi
if [ "$R" = "true" ]; then
	N=0
	PFL=""
	while [ $N -lt $COUNT ]; do
		ZNAME=${ZN[$N]}
		if [ -r "${T}/patchList.${ZNAME}" ]; then
			PFL="${PFL} ${T}/patchList.${ZNAME}"
		fi
		N=$((N+1))
	done
	if [ -z "$PFL" ]; then
		Z=`echo $ZONES | tr ' ' ','`
		echo "No patch lists found for $Z"
	else
		PATCHES=`cat $PFL | /usr/xpg4/bin/egrep '^[0-9]{6}-[0-9]{2} ' | \
			cut -f1 -d\  | sort | uniq | tr '\n' ' '`
		$DRY $PCA -r $PCA_OPTS $PATCHES
	fi
fi
if [ "$I" = "true" ]; then
	N=0
	while [ $N -lt $COUNT ]; do
		[ $N -eq 1 ] && PCA_OPTS="-y ${PCA_OPTS}"
		ZNAME=${ZN[$N]}
		ZPATH=${ZP[$N]}
		PL="${T}/patchList.${ZNAME}"
		[ $N -eq 1 ] && PCA_OPTS="-y ${PCA_OPTS}"
		if [ -r "$PL" ]; then
			PATCHES=`cat $PL | /usr/xpg4/bin/egrep '^[0-9]{6}-[0-9]{2} ' | \
				cut -f1 -d\  `
			if [ -n "$PATCHES" ]; then
				ZIPATH=`getZoneInfo "$ZNAME" "$ZPATH"`
				if [ -z "$ZIPATH" ]; then
					echo "Skipping unsupported zone $ZNAME"
				else
					ZDIRS="${ZDIRS}	\\ \n	${ZIPATH}"
					# regenerate showrev.out - might have change via global zone
					mv -f ${ZIPATH}/showrev.out ${ZIPATH}/showrev.out.old
					ZIPATH=`getZoneInfo "$ZNAME" "$ZPATH"`
					NPATCHES=`$PCA -l -H $PCA_OPTS -R "$ZPATH" -f "${ZIPATH}/" \
						--format "%p-%c"`
					NPATCHES=`echo "${PATCHES}
${NPATCHES}" | sort | uniq -d`
					if [ -n "$NPATCHES" ]; then
						# recreate PL before
						$DRY $PCA -i $PCA_OPTS -R "$ZPATH" -f "${ZIPATH}/" $NPATCHES
					fi
					echo "$ZNAME zone finished."
				fi
			else
				echo "$ZNAME zone is clean."
			fi
		else
			echo "Skipping $ZNAME - no patchlist found."
		fi
		N=$((N+1))
	done
fi
if [ -n "$ZDIRS" ]; then
	echo "\nPlease cleanup the following directories using:\n"
	print "rm -rf $ZDIRS"
fi
