###############################################################################
#
# This perl module contains the variables and functions needed for installing
# rpms in a platform/security patch.  The functions in this file are either
# called locally from another function in the file or called externally from
# the rpm_install script (executed when a patch with rpms is activated).
#
###############################################################################
package update_rpm;

use fileop qw(Copy);
use Exporter;
@ISA = qw(Exporter);

@EXPORT_OK = qw(&get_ramdisk_info &install_rpm &rpm_to_install_islatest
                &sync_ramdisk &record_rpm_files &extract_rpm_file_to_ramdisk
                &perform_service_requests &add_service_request 
                $PLATFORM_UPDATE_NAME $KERNEL_UPDATE_NAME);
@EXPORT = ();
%EXPORT_TAGS = ();

#################################################################
#
# NAME: update_funcs
#
# WHAT: helper functions used when installing rpm files.
#
# SPECIAL CONSIDERATIONS: None
#
################################################################

use File::Basename;

use lib '/opt/ecs/lib/perl';
use POSIX ":sys_wait_h";
use update qw($UPDATEDIR $UPDATEFILESDAT $OPTWSDIR $SCREEN
              $COMMAND $SUCCESS $UPDLOG $FAILURE log_msg);

# return codes
my $RPM_OUTOFDATE = 2;

# flags for type of rpm install
my $DO_INSTALLTEST = 1;
my $DO_INSTALL = 2;
my $DO_RESTORE = 3;
my $DO_RESTORETEST = 4;
# number of times to wait for
# child process to finish installing rpm
# in between 2 second wait increments
# (for a total of 90 * 2 = 180 seconds)
my $WAIT_TIME = 90;

# array holding service requests
my @service_requests = ();
# service request file
my $REQUEST_FILE = "/root/plat_updts_request_services";
# time to hold off in minutes until executing request script
my $MIN = 1;
# patch to ramdisk files
my $RAMDISK_FILES_DIR = "/opt/ecs/ramdisk";
# name of file to put failed rpm installs
my $FAILEDINSTALLFILE = "failed_rpm_installs.dat";
# messages to be displayed when an rpm install fails
my $RESTORE_RPM_SUCCESS_MSG = "Installation of new rpms has failed.\n" .
			      "All rpms that were installed before " .
			      "the patch was activated have been " .
			      "successfully restored.\n";
my $RESTORE_RPM_FAIL_MSG = "Installation of new rpms has failed.\n" .
			   "Attempt to return to originally " .
			   "installed rpms was unsuccessful.  " .
			   "Please contact a service representative " .
			   "immediately.\n";
# name part of security patches
# NOTE: If you change the value of this variable you must
# make the same changes to the variable locally defined
# in update_unpack.sh.
$PLATFORM_UPDATE_NAME = "PLAT";
$KERNEL_UPDATE_NAME = "KERNEL";

my @RAMDISK_LIST;          # global array of files in ramdisk (files.dat)
my @RPM_FILES_LIST;        # global array of files an rpm will try to install
my @CONFLICT_FILES;        # global array of files rpm installs that conflict
                           # with acpconf rpm.  Files in this array
                           # will need to be copied from /opt/ws/
my @OLD_RPMS;              # global array of rpms successfully installed

my $FCN = basename("$0");  # name of running script
my $UPDATE;                # global variable for update name
my $RAMDISK_OPT = 0;       # global variable for return value of ramdisk check
my $RAMDISK_ENABLED = 0;   # global variable for ramdisk enabled

##########################################################
#
# Name: add_service_request
#
# Description: Add passed in text containing a service
# request to array that will later be added to a temp
# file that will be executed.
#
# Parameters:  Name     Description
#              ----     ----------
#              $request request string to execute later
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub add_service_request()
{
	($request) = @_;
	push @service_requests, $request;
} # end sub add_service_request()

##########################################################
#
# Name: get_ramdisk_info
#
# Description: Generate a list of files copied to ramdisk
# by looking at the /opt/ecs/ramdisk/files.dat file.
#
# Parameters:  Name     Description
#              ----     ----------
#              $UPDATE  update we are trying to apply
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub get_ramdisk_info()
{
	# set global variable of update
	($UPDATE) = @_;

	# initialize sucessfully installed rpm list
	@OLD_RPMS = ();

	# if a failed rpm installs file exists, remove it.
	# we don't want rpms showing up in this file that
	# may actually be installed on a later activate
	if( -e "$UPDATEDIR/$UPDATE/$FAILEDINSTALLFILE" )
	{
		`rm -f $UPDATEDIR/$UPDATE/$FAILEDINSTALLFILE`;
	}

	# perform ramdisk enabled check
	$RAMDISK_OPT = system("/opt/ecs/sbin/ramdisk -s");
	@RAMDISK_LIST = ();

	# ramdisk returns value of 256 if enabled
	if( $RAMDISK_OPT == 256 )
	{
		$RAMDISK_ENABLED = 1;

		# get list of ramdisk files stored on machine
		open( RAMDISK_TMPLIST, "< $RAMDISK_FILES_DIR/files.dat" ) ||
			do
			{
				&log_msg($SCREEN|$UPDLOG|$COMMAND,
				         "Could not open $RAMDISK_FILES_DIR/" .
				         "files.dat to read ramdisk files\n",
				         $FCN);
				return $FAILURE;
			};

		while( <RAMDISK_TMPLIST> )
		{
			next if /^\s*#/;        # skip comments
			next if /^$/;           # skip blanks

			chomp;
			push @RAMDISK_LIST, $_;
		}

		close( RAMDISK_TMPLIST );
	}

	return ($SUCCESS, $RAMDISK_ENABLED);
} # get_ramdisk_info() sub

##########################################################
#
# Name: get_rpm_location
#
# Description: Get the location of the passed in rpm from
#              the listing of the rpm in update_files.dat.
#
# Parameters:  Name             Description
#              ----             ----------
#              $rpm_to_install  rpm to install
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub get_rpm_location()
{
	my $rpm_to_intall;

	($rpm_to_install) = @_;

	# open the update_files.dat file
	open(UPDATEFILESDAT, "$UPDATEDIR/$UPDATE/$UPDATEFILESDAT") ||
		do
		{
			&log_msg($UPDLOG|$SCREEN|$COMMAND, "Cannot " .
			         "open necessary file: $UPDATEDIR/$UPDATE/" .
			         "$UPDATEFILESDAT. Update aborting....\n",
			         $FCN);
			return "";
		};

	# go through each file in the udpate_files.dat file
	while(<UPDATEFILESDAT>)
	{
		undef $file;
		undef $dir;

		next if /^\s*#/;    # skip comments
		next if /^$/;       # skip blanks

		# read file and directory fields from line
		($file, $dir) = split(' ',$_);
		chomp($dir);

		# check if we found rpm we're installing
		if ( $file =~ /$rpm_to_install/ )
		{
			# found rpm, close file and return rpm location
			close UPDATEFILESDAT;
			return $dir;
		}
	}

	close UPDATEFILESDAT;

	return "";
} # end get_rpm_location sub

##########################################################
#
# Name: log_rpmfailed_install
#
# Description: Log message explaining that attempt
#              to install rpm failed to file meant
#              to store this information.
#
# Parameters:  Name	        Description
#              ----             ----------
#              $rpm_to_install  rpm to install
#
# SPECIAL CONSIDERATIONS: $FCN has to be set before
#                         calling this routine.
#
##########################################################
sub log_rpmfailed_install()
{
	# full name of rpm to install
	my $rpm_to_install;

	($rpm_to_install) = @_;

	&log_msg($UPDLOG|$SCREEN|$COMMAND, "Attempt to install rpm " .
	         "$rpm_to_install failed, $FCN did NOT install rpm file " .
	         "$rpm_to_install, this file will be listed " .
	         "in $UPDATEDIR/$UPDATE/$FAILEDINSTALLFILE\n", $FCN);

	my $dt = `date '+%a %b %d %T'`;
	chomp $dt;

	# store failed rpm in failed install file
	open( FAILEDRPMINSTALLS, ">> $UPDATEDIR/$UPDATE/$FAILEDINSTALLFILE" );

	print FAILEDRPMINSTALLS "$dt $FCN: Could not install rpm $rpm_to_install\n";

	close( FAILEDRPMINSTALLS );
} # end log_rpmfailed_install() routine

##########################################################
#
# Name: install_rpm
#
# Description: Perform installation of a passed in rpm
#              file.  Before actually installing rpm check
#              to see if there is a newer version of the
#              same rpm already installed on the system.
#              If there is not a newer version on the
#              system, create a list of which files the
#              rpm will install reside in ramdisk.  This
#              is found using the record_rpm_files()
#              subroutine.  After getting files in ramdisk,
#              execute rpm install string to install rpm
#              to /diskroot if ramdisk is enabled and to
#              / if not.  After installing the rpm, if
#              ramdisk is enabled we go back through the
#              list of files in the rpm that are copied
#              to ramdisk and manually extract them to a
#              ramdisk location instead of /diskroot.
#
# Parameters:  Name             Description
#              ----             ----------
#              $rpm_to_install  rpm to install
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub install_rpm()
{
	# full name of rpm to install
	my $rpm_to_install;
	# rpm currently installed on system
	my $current_rpm;

	($rpm_to_install) = @_;

	# location of rpm files
	my $rpm_location;
	# flag for being okay to install rpm
	my $not_okay_to_install = 0;
	# return code variable
	my $rec_result = 0;
	# return value of call to Copy
	my $copy_result = 0;
	# variable holding value for using diskroot
	my $diskroot = "";
	# array for error string return from Copy
	my @copy_err_str = ();

	# retrieve location of rpm file
	$rpm_location = &get_rpm_location( $rpm_to_install );

	# location wasn't found, use update directory
	if( !$rpm_location )
	{
		$rpm_location = "$UPDATEDIR/$UPDATE/";
	}
	# if location doesn't contain ending "/", add one
	elsif( $rpm_location !~ /\/$/ )
	{
		$rpm_location .= "/";
	}

	# test if rpm to install would install correctly:
	# see if it is newer than existing rpm on system
	# and that other issues don't come up on install
	# what if no rpm is currently installed?
	($not_okay_to_install,
	 $current_rpm) = &rpm_to_install_islatest( $rpm_location,
						   $rpm_to_install );

	# not okay to install the rpm, log error and exit
	if( $not_okay_to_install == $FAILURE )
	{
		# log that the rpm failed to install
		&log_rpmfailed_install( $rpm_to_install );

		# try to restore rpms installed
		# by patch to original configuration
		$rec_result = &restore_system_rpm_state( );

		# Output log_msg here about if we were able to restore
		# to previously installed rpms or not.
		if( $rec_result == $SUCCESS )
		{
			&log_msg($UPDLOG|$COMMAND|$SCREEN,
			         "$RESTORE_RPM_SUCCESS_MSG", $FCN);
		}
		else
		{
			&log_msg($UPDLOG|$COMMAND|$SCREEN,
			         "$RESTORE_RPM_FAIL_MSG", $FCN);
		}

		return $FAILURE;
	}
	# not okay to install due to rpm on system being
	# same or newer than rpm we're trying to install
	elsif( $not_okay_to_install == $RPM_OUTOFDATE )
	{
		# rpm didn't install because it is older or the same as the rpm
		# currently on the system, log this, but don't return as a failure
		&log_msg($UPDLOG|$SCREEN|$COMMAND, "Did NOT install " .
		         "rpm $rpm_to_install, a newer or current " .
		         "version is already on the system.\n", $FCN);

		return $SUCCESS;
	}

	# okay to attempt install of rpm, continue

	# record files that this rpm changed,
	# this is used later to sync ramdisk when installing rpms
	if( $RAMDISK_ENABLED )
	{
		$rec_result = &record_rpm_files( $rpm_to_install,
						 $rpm_location );

		$diskroot = "/diskroot";
	}

	# If $rec_result is nonzero then recording of rpm
	# files failed, so we will not install rpm.
	if( $rec_result )
	{
		# log that the rpm failed to install
		&log_rpmfailed_install( $rpm_to_install );

		# try to restore rpms installed
		# by patch to original configuration
		$rec_result = &restore_system_rpm_state( );

		# Output log_msg here about if we were able to restore
		# to previously installed rpms or not.
		if( $rec_result == $SUCCESS )
		{
			&log_msg($UPDLOG|$COMMAND|$SCREEN,
			         "$RESTORE_RPM_SUCCESS_MSG", $FCN);
		}
		else
		{
			&log_msg($UPDLOG|$COMMAND|$SCREEN,
			         "$RESTORE_RPM_FAIL_MSG", $FCN);
		}

		return $FAILURE;
	}

	# store previously installed rpm for failure case,
	# put rpm in list so we will restore an rpm that may
	# only be partially installed in the following install code.
	if( $current_rpm )
	{
		push @OLD_RPMS, $current_rpm;
	}

	# attempt only once to install rpm
	($not_okay_to_install, $rpminstall_message) = &perform_rpm_install(
							$rpm_to_install,
							$rpm_location,
							$DO_INSTALL);

	# rpm installed okay, perform any necessary actions
	if( !$not_okay_to_install )
	{
		# This subroutine looks at a compiled list of 
		# files that reside in ramdisk and are changed
		# when installing rpms.
		# This subroutine will then go
		# through this list of files and determine if
		# if the file needs to be copied over to ramdisk
		# or the hard disk and will do so to keep the two
		# consistent with each other.
		# Patch creator DOES NOT edit this subroutine.
		if( $RAMDISK_ENABLED )
		{
			&log_msg($UPDLOG|$SCREEN,
			         "Syncing ramdisk for $rpm_to_install\n",
			         $FCN);

			$rec_result = &sync_ramdisk( $rpm_to_install,
			                             $rpm_location );
		}

		# if we synced ramdisk files without any
		# problems, we can continue
		if( $rec_result == 0 )
		{
			# do automated version of copying any
			# files that conflict with acpconf rpm
			# from /opt/ws/ to their default location
			foreach my $conflict_file (@CONFLICT_FILES)
			{
				my $conflict_file_base = basename( 
					$conflict_file );

				&log_msg($UPDLOG|$SCREEN, "Copying " .
				         "'$OPTWSDIR/$conflict_file_base' to " .
				         "'$diskroot$conflict_file'\n", $FCN);

				# copy from /opt/ws dir to diskroot
				# dir, $conflict_file has leading "/".
				# use special Copy (which uses fopCopy)
				# routine for all files
				# Copy returns 1 on success
				$copy_result = &Copy(
					"$OPTWSDIR/$conflict_file_base",
					"$diskroot$conflict_file",
					0, @copy_err_str );

				# log any error
				if( $copy_result == 0 )
				{
					&log_msg($UPDLOG|$SCREEN,
						 "Copy of '$OPTWSDIR/" .
						 "$conflict_file_base' to " .
						 "'$diskroot$conflict_file' " .
						 "failed\n", $FCN);
				}
			}

			return $SUCCESS;
		}
		else
		{
			# log ramdisk sync failure, if ramdisk sync failed
			# treat as rpm install failed because not all files
			# rpm tried to install have been properly copied
			&log_msg($UPDLOG|$SCREEN|$COMMAND,
				 "Ramdisk sync failed " .
				 "for $rpm_to_install!\n", $FCN);
		}
	}

	# if we fell through this far it means something
	# went wrong with installing the rpm, try to revert back
	# to previously installed versions of rpms

	# if we fell through this means rpm failed to install, log
	&log_rpmfailed_install( $rpm_to_install );

	# try to restore rpms installed
	# by patch to original configuration
	$rec_result = &restore_system_rpm_state( );

	# Output log_msg here about if we were able to restore
	# to previously installed rpms or not.
	if( $rec_result == $SUCCESS )
	{
		&log_msg($UPDLOG|$COMMAND|$SCREEN,
			 "$RESTORE_RPM_SUCCESS_MSG", $FCN);
	}
	else
	{
		&log_msg($UPDLOG|$COMMAND|$SCREEN,
			 "$RESTORE_RPM_FAIL_MSG", $FCN);
	}

	return $FAILURE;
} # install_rpm routine

##########################################################
#
# Name: extract_rpm_file_to_ramdisk
#
# Description: This subroutine extracts a single file
#              from an rpm and extracts that file to a
#              ramdisk location.  This extraction of the
#              file is required because when we install
#              an rpm we install it to /diskroot and
#              placing a file in the /diskroot directory
#              will cause prune to perform a filesync
#              with ramdisk (non-diskroot directory).
#              Filesync works by copying the file in
#              ramdisk to /diskroot, so the change we
#              just made to the /diskroot file would be
#              lost.  A separate subroutine for extracting
#              an rpm file to ramdisk has been created
#              so it may be used by the patch creator for
#              files that are not in
#              /opt/ecs/ramdsisk/files.dat.
#
# NOTE: This subroutine is only executed when ramdisk
#       is enabled.
#
#
# Parameters:  Name             Description
#              ----             ----------
#              $rpm_to_install  rpm to install
#              $rpm_file        file in rpm to extract
#              $rpm_location    rpm location
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub extract_rpm_file_to_ramdisk()
{
	my ($rpm_to_install) = $_[0];
	my ($rpm_file) = $_[1];
	my ($rpm_location) = $_[2];
	my $extract_failed;

	if( !$rpm_location )
	{
		$rpm_location = "$UPDATEDIR/$UPDATE/";
	}

	chdir "/" ||
		do
		{
			&log_msg($UPDLOG|$SCREEN|$COMMAND, "Attempt to" .
				 "extract rpm file to ramdisk failed because " .
				 "cannot change to directory '/'\n", $FCN);
			return $FAILURE;
		};

	&log_msg($UPDLOG|$SCREEN, "Extracting file $rpm_file from " .
		 "$rpm_to_install to ramdisk\n", $FCN);

	# extract single file from rpm and place in ramdisk location
	$extract_failed = system ("rpm2cpio $rpm_location$rpm_to_install | " .
				  "cpio -iu $rpm_file");

	if( $extract_failed )
	{
		&log_msg($UPDLOG|$SCREEN|$COMMAND, "Attempt to extract file " .
			 "$rpm_file from $rpm_to_install to ramdisk failed\n",
			 $FCN);
	}

	return $extract_failed;
} # end extract_rpm_file_to_ramdisk routine

##########################################################
#
# Name: perform_rpm_install
#
# Description: File local routine that given an rpm and a
# flag to perform just a test or actually install,
# install/test the rpm by forking off a child process
# and performing the install/test.  By having an child
# process do the work we can catch if the rpm command
# hangs, and also still get the return code and output
# printed by the child process.
#
# Parameters:  Name            Description
#              ----            ----------
#              rpm_to_install  rpm to install/test
#              rpm_location    location of rpm to install
#              perform_test    flag set if we will just
#                              do a test or actually
#                              install the rpm
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub perform_rpm_install()
{
	# get values for passed args of:
	# -location of rpm
	# -rpm name to install
	# -flag for performing test
	my ($rpm_to_install) = $_[0];
	my ($rpm_location) = $_[1];
	my ($do_test) = $_[2];
	my $rpminstall_message = "";

	# return code variable
	my $rec_result = 0;
	# string holding diskroot value for root
	my $diskroot = "";
	# string holding value for rpm already installed
	my $already_installed = "already installed";
	# string holding value of error
	my $error = "error";
	# string holding value of conflicts
	my $conflicts = "conflicts";
	# string holding value of conflicts with acp
	my $conflicts_acpconf = "conflicts with file from package acpconf";

	# set not_okay_to_install to SUCCESS until found otherwise
	my $not_okay_to_install = $SUCCESS;

	# Create a unique file name for rpm -U output.
	#
	if ($rpm_to_install =~
		/^([\w\-\+\.]+)-([\w\.]+)-([\w\.]+)\.(\w)+\.rpm/m)
	{
		$rpm_maj = $1;
	}
	my $date = `date +%m%d%H%M%S`;
	chomp($date);
	my $file_name = "/var/tmp/rpm-${rpm_maj}.${date}";

	# record files that this rpm changed,
	# this is used later to sync ramdisk when installing rpms
	if( $RAMDISK_ENABLED )
	{
		$diskroot = "--root /diskroot";
	}

	# Do install test on a forked process so we can catch if it hangs.
	my $child_pid = open RPMINSTALL, "-|";
	defined($child_pid) ||
		# child process could not be opened to install rpm
		do
		{
			if( $do_test eq $DO_INSTALLTEST )
			{
				$rpminstall_message = "Could not test rpm " .
						      "install\n";
				&log_msg($UPDLOG|$SCREEN|$COMMAND, "Not " .
					 "installing rpm file " .
					 "$rpm_to_install: $?\n", $FCN);
			}
			elsif( $do_test eq $DO_RESTORETEST )
			{
				$rpminstall_message = "Could not test rpm " .
						      "install for restore\n";
				&log_msg($UPDLOG|$SCREEN|$COMMAND, "Not " .
					 "restoring rpm file " .
					 "$rpm_to_install: $?\n", $FCN);
			}
			elsif( $do_test eq $DO_INSTALL )
			{
				# log that the rpm failed to install
				&log_rpmfailed_install( $rpm_to_install );

				# try to restore rpms installed
				# by patch to original configuration
				$rec_result = &restore_system_rpm_state( );

				# Output log_msg here about if we were able to
				# restore to previously installed rpms or not.
				if( $rec_result == $SUCCESS )
				{
					&log_msg($UPDLOG|$COMMAND|$SCREEN,
						 "$RESTORE_RPM_SUCCESS_MSG", $FCN);
				}
				else
				{
					&log_msg($UPDLOG|$COMMAND|$SCREEN,
						 "$RESTORE_RPM_FAIL_MSG", $FCN);
				}
			}
			elsif( $do_test eq $DO_RESTORE )
			{
				$rpminstall_message = "Could not restore rpm " .
						      "$rpm_to_install\n";
				&log_msg($UPDLOG|$SCREEN|$COMMAND, "Not " .
					 "installing rpm file " .
					 "$rpm_to_install: " .
					 "$rpminstall_message\n", $FCN);
			}

			return $FAILURE;
		};

	# This is the parent if a non-zero child_id was returned.
	# Wait for child to exit or time out.
	if( $child_pid )
	{
		my $waiting_cnt = 0;
		# wait for child to finish INSTALL/TEST
		my $ret_id = waitpid($child_pid, WNOHANG);

		# wait so long for child to finish
		# (up to 2 * $WAIT_TIME seconds)
		while( $ret_id == 0 && $waiting_cnt < $WAIT_TIME )
		{
			sleep( 2 );
			# waitpid will return if rpm installed correctly
			$ret_id = waitpid($child_pid, WNOHANG);
			$waiting_cnt++;
		}

		# still waiting on child to finish and install has timed out,
		# ret_id of zero means child is still running,
		if( $ret_id == 0 )
		{
			# kill child process and set not_okay_to_install
			my $kill_ret = system( "kill -9 $child_pid" );

			&log_msg($UPDLOG|$SCREEN|$COMMAND,
				 "Timed out attempting to install " .
				 "rpm ${rpm_to_install}, look in " .
				 "${file_name} for addition info.\n",
				 $FCN);

			# If installing or restoring an RPM fails,
			# attempt to clean up.
			#
			if ( $do_test eq $DO_INSTALL ||
			     $do_test eq $DO_RESTORE )
			{
				# Strip off the ".*.rpm" at the end
				# of the rpm_to_install to get the
				# RPM name for erasing, if installed.
				#
				if ($rpm_to_install =~
				    /([\w\-\+\.]+)\.\w+\.rpm/m)
				{
					$rpm_name = $1;
				}

				# Clear the RPM transaction lock and
				# the RPM database, then initialize
				# the RPM database.  This is being
				# done to ensure a sane RPM database.
				#
				`rm -f /var/lock/rpm/transaction`;
				`rm /usr/lib/rpm/db/__db*`;
				`rpm --initdb`;

				# Query for the RPM major name,
				# if the RPM name is installed,
				# then erasing the RPM and init
				# the database again or the rpm
				# entry in the database is left
				# corrupted.
				#
				my $rpm_query = `rpm -qa $rpm_maj`;
				chomp $rpm_query;
				if ($rpm_query =~ /$rpm_name/)
				{
					&log_msg($UPDLOG|$SCREEN|$COMMAND,
						"Erasing RPM " .
						"${rpm_name}.\n",
						$FCN); 
					`rpm -e $rpm_name`;
					`rpm --initdb`;
				}
			}
			$not_okay_to_install = $FAILURE;
		}
		# installed okay, check output for any warnings/errors
		else
		{
			# the tests need to keep track of error/warning messages
			if( $do_test eq $DO_INSTALLTEST ||
			    $do_test eq $DO_RESTORETEST )
			{
				while( <RPMINSTALL> )
				{
					chomp;
					# also look for different error strings:
					#      error:     rpm database issue
					#      conflicts: file in to install rpm
					#                 conflicts with a file
					#                 from another package

					# only do "already installed" errors for
					# DO_TEST because we want
					# to force install for restores.
					if( $do_test eq $DO_INSTALLTEST &&
					    /$already_installed/ )
					{
						$not_okay_to_install = $RPM_OUTOFDATE;
						$rpminstall_message = "$_";
					}
					elsif( /$error/ )
					{
						$not_okay_to_install = $FAILURE;
						$rpminstall_message = "$_";
					}
					# else if we see a conflicts message
					# from install attempt and we haven't
					# already set not_okay_to_install to 1
					# then handle conflicts message
					elsif( /$conflicts/ && !$not_okay_to_install )
					{
						# if a file conflicts with
						# acpconf then we know to later
						# copy back the file from
						# /opt/ws/.  It is still okay
						# to install rpm though because
						# doing a force on actual
						# install will do a
						# --replacefiles.
						if( /$conflicts_acpconf/ )
						{
							(my $conflict_file) =
							    (/file\s*(.*)\s+from install/);
							push @CONFLICT_FILES,
							     $conflict_file;
						}
			
						$rpminstall_message = "$_";
					}
				} # end while loop going through child output
			} # end condition for doing a test install
			# install and restore install record return value
			elsif( $do_test eq $DO_INSTALL ||
			       $do_test eq $DO_RESTORE )
			{
				# get if rpm installed okay from return code.
				# a return code of zero means rpm installed okay.
				$not_okay_to_install = ($?) ? $FAILURE : $SUCCESS;
			}
		} # end else block of ret_id == 0 condition
	} # end parent process "if( child_id )" condition
	# child: try to install rpm
	else
	{
		if( $do_test eq $DO_INSTALLTEST ||
		    $do_test eq $DO_RESTORETEST )
		{
			# exec rpm test command within child process space
			$not_okay_to_install = exec( "rpm -Uv --test --nodeps " .
						     "$rpm_location$rpm_to_install 2>&1" );
		}
		else
		{
			# exec rpm install command within child process space
			$not_okay_to_install = exec( "rpm $diskroot " .
				"--force --nodeps -Uvv " .
				"${rpm_location}${rpm_to_install}" .
				" > ${file_name} 2>&1" );
		}
	}

	close( RPMINSTALL );

	# If successful and the debug file exist, remove it.
	#
	if (($not_okay_to_install == $SUCCESS) && (-e ${file_name}))
	{
		`rm -f ${file_name}`;
	}

	return($not_okay_to_install, $rpminstall_message);
} # end perform_rpm_install() sub

##########################################################
#
# Name: perform_service_requests
#
# Description: Take list of added service requests and
# write them to a file for later execution by at.
#
# Parameters:  Name     Description
#              ----     ----------
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub perform_service_requests()
{
	open( RQ_FILE, "> $REQUEST_FILE" );

	print RQ_FILE "#!/bin/bash\n";

	# add all requests from list
	foreach my $rq (@service_requests)
	{
		print RQ_FILE "$rq\n";
	}

	print RQ_FILE "/bin/rm $REQUEST_FILE";

	close( RQ_FILE );

	# set executable permissions bit on request file
	`chmod +x $REQUEST_FILE`;

	# set at to execute request script in $min minutes
	`at -f $REQUEST_FILE now + $MIN minute`;
	# setup of execute request worked okay with a return value of zero
	if( $? == 0 )
	{
		&log_msg( $UPDLOG|$SCREEN,
			  "Service requests will be performed in $MIN " .
			  "minute(s).\n", $FCN );
	}
	else
	{
		&log_msg( $UPDLOG|$SCREEN|$COMMAND,
			  "Service requests could not be performed!\n" .
			  $FCN );
	}
} # end sub perform_service requests

##########################################################
#
# Name: record_rpm_files
#
# Description: This subroutine will do a query of an rpm
#              to list all files the rpm will install
#              or change.  The files returned from this
#              query will be stored in a global array.
#
# Parameters:  Name             Description
#              ----             ----------
#              $rpm_to_install  rpm to install
#              $rpm_location    rpm location (optional)
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub record_rpm_files()
{
	my ($rpm_to_install, $rpm_location) = @_;
	# clear out global list for each rpm
	@RPM_FILES_LIST = ();

	# location not passed in, use update directory
	if( !$rpm_location )
	{
		$rpm_location = "$UPDATEDIR/$UPDATE/";
	}

	&log_msg($UPDLOG|$SCREEN, "Recording rpm files for " .
		 "'$rpm_to_install'\n", $FCN);

	# get list of files changed in rpm
	open( RPM_FILES, "rpm -qpl $rpm_location$rpm_to_install |" ) ||
		do
		{
			&log_msg($UPDLOG|$SCREEN|$COMMAND,
				 "Cannot open rpm get files query of " .
				 "rpm file $rpm_to_install.  rpm will not " .
				 "be installed....\n", $FCN);
			return $FAILURE;
		};
    
	# store rpm files in a list to be used in sync_ramdisk()
	while(<RPM_FILES>)
	{
		chomp;
		push @RPM_FILES_LIST, $_;
	}

	close( RPM_FILES );

	return $SUCCESS;
} # record_rpm_files routine

##########################################################
#
# Name: restore_system_rpm_state
#
# Description: Platform/security patch may install multiple
#              rpm files.  If one of these rpm installs were
#              to fail, the whole patch activation should fail.
#              Therefore when installing rpms we need to keep
#              track of which of the patch rpms has been
#              installed.  This is so if an rpm install fails
#              we can go back and restore all the rpms to
#              what was installed before the patch was activated.
#
# Parameters:  Name             Description
#              ----             ----------
#
# Returns:     Success or failure of restore
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub restore_system_rpm_state()
{
	# flag for being okay to install rpm
	my $not_okay_to_install = 0;
	my $diskroot = "";
	# location of previously installed rpm should be /var/disk/rpms/
	my $rpm_location = "/var/disk/rpms/";
	# string holding value of error
	my $error = "error";
	# return code variable
	my $rec_result = 0;
	# return value of Copy
	my $copy_result = 0;
	# string holding rpm install message
	my $rpminstall_message;

	foreach my $old_rpm (@OLD_RPMS)
	{
		# record files that this rpm changed,
		# this is used later to sync ramdisk when installing rpms
		if( $RAMDISK_ENABLED )
		{
			$rec_result = &record_rpm_files( $old_rpm,
							 $rpm_location );

			$diskroot = "/diskroot";
		}

		# If $rec_result is nonzero then recording of rpm
		# files failed, so we will not install rpm.
		if( $rec_result )
		{
			# At this point we failed to install NEW rpms and
			# now we are failing to restore back to previously
			# installed rpms.  The system needs manual help,
			# we must give up and bail.
			return $FAILURE;
		}

		# clear global conflict files array, need
		# to start from scratch for rpms to restore
		@CONFLICT_FILES = ();

		($not_okay_to_install, $rpminstall_message) = &perform_rpm_install(
								$old_rpm,
								$rpm_location,
								$DO_RESTORETEST);

		if( $rpminstall_message )
		{
			&log_msg($UPDLOG|$SCREEN|$COMMAND, "Restoring to " .
				 "rpm file $old_rpm returns message:" .
				 "\n$rpminstall_message\n", $FCN);
		}

		# rpm install test failed
		if( $not_okay_to_install == $FAILURE )
		{
			# At this point we failed to install NEW rpms and
			# now we are failing to restore back to previously
			# installed rpms.  The system needs manual help,
			# we must give up and bail.
			return $FAILURE;
		}

		# rpm install test was okay, perform actual install
		($not_okay_to_install, $rpminstall_message) = &perform_rpm_install(
								$old_rpm,
								$rpm_location,
								$DO_RESTORE);

		# rpm failed to install
		if( $not_okay_to_install == $FAILURE )
		{
			# At this point we failed to install NEW rpms and
			# now we are failing to restore back to previously
			# installed rpms.  The system needs manual help,
			# we must give up and bail.
			return $FAILURE;
		}

		# rpm installed okay, continue on

		# This subroutine looks at a compiled list of 
		# files that reside in ramdisk and are changed
		# when installing rpms.
		# This subroutine will then go
		# through this list of files and determine if
		# if the file needs to be copied over to ramdisk
		# or the hard disk and will do so to keep the two
		# consistent with each other.
		# Patch creator DOES NOT edit this subroutine.
		if( $RAMDISK_ENABLED )
		{
			&log_msg($UPDLOG|$SCREEN|$COMMAND,
				 "Syncing ramdisk for $old_rpm\n", $FCN);

			$rec_result = &sync_ramdisk( $old_rpm,
						     $rpm_location );
		}

		# if we synced ramdisk files without any
		# problems, we can continue
		if( $rec_result == 0 )
		{
			# do automated version of copying any
			# files that conflict with acpconf rpm
			# from /opt/ws/ to their default location
			foreach my $conflict_file (@CONFLICT_FILES)
			{
				my $conflict_file_base = basename( 
					$conflict_file );

				&log_msg($UPDLOG|$SCREEN|$COMMAND, "Copying " .
					 "'$OPTWSDIR/$conflict_file_base' to " .
					 "'$diskroot$conflict_file'\n", $FCN);

				# copy from /opt/ws dir to diskroot
				# dir, $conflict_file has leading "/".
				# use special Copy (which uses fopCopy)
				# routine for all files
				# Copy returns 1 on success
				$copy_result = &Copy(
					"$OPTWSDIR/$conflict_file_base",
					"$diskroot$conflict_file",
					0, @copy_err_str );

				# log any error
				if( $copy_result == 0 )
				{
					&log_msg($UPDLOG|$SCREEN|$COMMAND,
						 "Copy of '$OPTWSDIR/" .
						 "$conflict_file_base' to " .
						 "'$diskroot$conflict_file' " .
						 "failed\n", $FCN);
				}
			}
		}
		else
		{
			# log ramdisk sync failure, if ramdisk sync failed
			# treat as rpm install failed because not all files
			# rpm tried to install have been properly copied
			&log_msg($UPDLOG|$SCREEN|$COMMAND,
				 "Ramdisk sync failed for '$old_rpm'!\n", $FCN);

			# At this point we failed to install NEW rpms and
			# now we are failing to restore back to previously
			# installed rpms.  The system needs manual help,
			# we must give up and bail.
			return $FAILURE;
		}
	}

	# we have restored previously installed rpms successfully,
	# returning SUCCESS here so install_rpm() knows we restored rpms
	return $SUCCESS;
} # end restore_system_rpm_state routine

##########################################################
#
# Name: rpm_to_install_islatest
#
# Description: Given an rpm, determine if it should be
#              installed on the system by comparing it to
#              the rpm currently installed on the system.
#              The compare is done by doing a test install
#              of the rpm.  If the rpm to install is a
#              later version then the rpm already
#              installed, the new rpm will be installed.
#
# Parameters:  Name             Description
#              ----             ----------
#              $rpm_location    location of rpm to install
#              $rpm_to_install  rpm to install
#
# SPECIAL CONSIDERATIONS: None
#
##########################################################
sub rpm_to_install_islatest()
{
	# full name of rpm to install
	my $rpm_to_install;
	# path to rpm location
	my $rpm_location;
	# path to previously installed rpm
	my $prev_installed_rpm_location  = "/var/disk/rpms/";
	# rpm major name (used to search for currently installed rpm)
	my $rpm_major;
	# holds current rpm installed on system
	my $rpm_query;
	# flag for being okay to install rpm
	my $not_okay_to_install;
	# string holding rpm install message
	my $rpminstall_message;

	# start with okay to install until found otherwise
	$not_okay_to_install = $SUCCESS;
	($rpm_location, $rpm_to_install) = @_;

	# rpm file major, or package name, is taken from anything that
	# comes before the second to last dash.
	if ($rpm_to_install =~
		/^([\w\-\+\.]+)-([\w\.]+)-([\w\.]+)\.(\w)+\.rpm/m)
	{
		# Set the RPM package name to the first backtick
		# (thing in the parenthesis above).
		$rpm_major = $1;
	}
	else
	{
		&log_msg($UPDLOG|$SCREEN|$COMMAND, "Cannot determine " .
			 "the package name for $rpm_to_install. " .
			 "Update aborting....\n", $FCN);
		return $FAILURE;
	}

	# Verify that there are not more than one version of this RPM
	# installed.
	#
	my $rpm_num =`rpm -qa $rpm_major | wc -l`;
	chomp $rpm_num;

	if ($rpm_num > 1)
	{
		&log_msg($UPDLOG|$SCREEN|$COMMAND, "More than 1 rpm " .
			 "version installed (${rpm_num}) for " .
			 "$rpm_major. Update aborting....\n", $FCN);
		return $FAILURE;
	}

	# execute rpm query command to get currently installed version
	$rpm_query = `rpm -qa $rpm_major`;
	if ($? != 0)
	{
		&log_msg($UPDLOG|$SCREEN|$COMMAND, "Cannot query " .
			 "rpm $rpm_major. Update aborting....\n", $FCN);
		return $FAILURE;
	}
	chomp $rpm_query;

	if( $rpm_query )
	{
		# get full rpm name
		$rpm_query = basename(
			`ls $prev_installed_rpm_location$rpm_query*` );
		chomp $rpm_query;
	}
	
	# clear conflict files array
	@CONFLICT_FILES = ();

	($not_okay_to_install, $rpminstall_message) = &perform_rpm_install(
							$rpm_to_install,
							$rpm_location,
							$DO_INSTALLTEST);

	# log installation activity
	if( !$not_okay_to_install )
	{
		if( $rpm_query )
		{
			&log_msg($UPDLOG|$SCREEN|$COMMAND, "Installing rpm " .
				 "file $rpm_to_install, this will install " .
				 "over existing rpm $rpm_query\n", $FCN);
		}
		else
		{
			&log_msg($UPDLOG|$SCREEN|$COMMAND, "Installing " .
				 "rpm file $rpm_to_install, system does " .
				 "not have rpm installed\n", $FCN);
		}

		if( $rpminstall_message )
		{
			&log_msg($UPDLOG|$SCREEN|$COMMAND, "Installing " .
				 "rpm file $rpm_to_install returns message:" .
				 "\n$rpminstall_message\n", $FCN);
		}
	}
	else
	{
		&log_msg($UPDLOG|$SCREEN|$COMMAND, "Not installing " .
			 "rpm file $rpm_to_install: $rpminstall_message\n",
			 $FCN);
	}

	return ($not_okay_to_install, $rpm_query);
} # rpm_to_install_islatest() routine

##########################################################
#
# Name: sync_ramdisk
#
# Description: By looking at a known list of files in
#              /opt/ecs/ramdisk/files.dat as well as files
#              being installed for an rpm, this subroutine
#              will check if a file being installed by
#              an rpm is listed in files.dat.  If so,
#              we need to manually extract the file and
#              place it in the ramdisk directory so when
#              prune performs a filesync the changes to
#              the file will not be lost.
#
# Parameters:  Name             Description
#              ----             ----------
#              $rpm_to_install  rpm to install
#              $rpm_location    location of rpm to install
#
# SPECIAL CONSIDERATIONS: This subroutine is only executed
#                         when ramdisk is enabled.
#
#
##########################################################
sub sync_ramdisk()
{
	my ($rpm_to_install, $rpm_location) = @_;
	my $extract_failed = 0;

	&log_msg($SCREEN|$UPDLOG, "Now syncing ramdisk files for " .
		 "$rpm_to_install:\n", $FCN);

	# go through each file included in rpm
	foreach my $rpm_file (@RPM_FILES_LIST)
	{
		# if file in rpm is also known to be in ramdisk,
		# extract single file to ramdisk.  Use the \Q and \E
		# to ensure that any special characters in the file name
		# are not interpreted in the regex!
		if( grep(/^\Q$rpm_file\E$/, @RAMDISK_LIST) )
		{
			$extract_failed =
				&extract_rpm_file_to_ramdisk( $rpm_to_install,
							$rpm_file,
							$rpm_location );

			# if extracting rpm file from rpm failed,
			# treat this as an error in installing rpm
			if( $extract_failed )
			{
				return $FAILURE;
			}
		}
	}

	return $SUCCESS;
} # end sync_ramdisk routine

# perl modules require a "1" return code
1
