#!/usr/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2004,2007 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
#

# @(#)06   1.14.1.1   src/csm/install/osfirstboot.perl, setup, csm_rfish, rfishs001b 2/28/07 05:01:09

#-------------------------------------------------------------------------------

=head1	osfirstboot

	Description: This script is executed after the first reboot of a
	node after the operating system has been installed. An entry for
	this script was added to the /etc/inittab file by the osprereboot
	script.  It will remove itself from
	the /etc/inittab file. 

	Exit codes:
		0 - success
		1 - error
=cut

#-------------------------------------------------------------------------------

use strict;

# BEGIN is executed first, regardless of the loading order of
# other program modules, such as NodeUtils.pm.

BEGIN
{

	use File::Basename;    # The path of the command
	($::Bin) = dirname($0);    # Assumes msgmaps are in same dir as application
	     # This can be meaningful for using csm.core code like
	     # runcmd, or using messaging and logging from /csminstall

	# setup messaging globals
	$::MSGCAT     = 'csmInstall.cat';
	$::MSGMAPPATH = $::Bin;
	$::MSGSET     = 'csminstall';
	#NodeUtils should use the msgmap files in the current directory
	$NodeUtils::MSGMAPPATH = $::MSGMAPPATH;

}

use lib "$::Bin";
use NodeUtils;
use CSMDefs;
use ServerUtils;

#-------------------------------------------------------------------------------

=head3	initialize	

	init the global variables

        Notes:

=cut

#-------------------------------------------------------------------------------

sub initialize
{

	$::command_line;

	my @command_line = ();
	@command_line   = @ARGV;
	$::command_line = $0 . " " . join(" ", @command_line);

	# setup the global defs
	ServerUtils->get_OSDefs($::PLTFRM);

	$::csm_server   = "";    # the name of the management server
	$::akb_nodename = "";    # the name of this node as known by
	                         # the management server

	$::CFGINFOFILE     = "/opt/csm/install/configinfo";
	$::MSCSMINSTDIR    = "/csminstall/csm";
	$::LOCALCSMINSTDIR = "$::CSMCLIENTMNTDIR/csm";      #"/var/log/csm/mnt/csm";

	# Don't hard code LOCALPOSTINSTALLDIR, get FIRSTBOOT_SCRIPT_DIR 
	# from config_info
	#$::LOCALPOSTINSTALLDIR = "$::CSMCLIENTMNTDIR/csm/scripts/installpostreboot";
	$::inittab             = "/etc/inittab";
	$::tmpinittab          = "/etc/inittab.tmp";
	$::mounted             = 0;
	$::GLOBAL_EXIT         = 0;
	$::CSMINSTALL_ATFTP    = "$::LOCALCSMINSTDIR/atftp";

	@::postscriptlist = ();    #  list of user-provided scripts to run

	$::arch = `uname -m`;
	chomp($::arch);

	# make OSDEP INST directories for the MS and the nodes:
	my $msPrefixDir   = "/csminstall";
	my $nodePrefixDir = $::CSMCLIENTMNTDIR;
	my $suffixDir     = "";

	if    ($::PLTFRM eq "Linux") { $suffixDir = "Linux"; }
	elsif ($::PLTFRM eq "AIX")   { $suffixDir = "AIX/csm"; }

	$::CSMOSDEPINSTDIR      = "$msPrefixDir/$suffixDir";
	$::LOCALCSMOSDEPINSTDIR = "$nodePrefixDir/$suffixDir";

	$::OSDEPmounted = 0;
	$::tmpetchosts  = "/tmp/etchosts";
	$::etchosts     = "/etc/hosts";
	$::ETC_HOSTS_MS;    # the "ip longhost shorthost" of the management server

}

#-------------------------------------------------------------------------------

=head3	update_inittab 

	remove an entry for csmfirstboot to the /etc/inittab file    

	Description:  This function updates the /etc/inittab file.  It
	removes an entry for osfirstboot in /etc/inittab so that it wil
	not run on the next reboot

	Arguments:    None.

	Returns 0 - success
		1 - error

        Notes:

=cut

#-------------------------------------------------------------------------------

sub update_inittab
{

	my ($cmd, $rc, $line, $entry);

	#
	# remove entry for osfirstboot in /etc/inittab
	#
	if ($::PLTFRM eq "AIX")
	{

		# see if it is there
		$entry = "csm";
		$cmd   = "$::LSITAB $entry >/dev/null 2>&1";
		$rc    = system("$cmd");
		if ($rc == 0)
		{    #  entry does exist

			$cmd = "$::RMITAB $entry";
			$rc  = system("$cmd");
			if ($rc >> 8)
			{

				#  Could not remove osfirstboot from /etc/inittab
				MessageUtils->message('E', 'EMsgNO_REMOVE', "csm", $::inittab);
				return $::NOK;
			}
		}
	}
	elsif ($::PLTFRM eq "Linux")
	{

		#--Begin add for ppc64
		#    if ($::arch eq "ppc64") {
		#    	return $::OK;
		#    }
		#--End
		unless (open(INITTAB, "<$::inittab"))
		{
			MessageUtils->message('E', 'EMsgCANT_READ_FILE', $::inittab);
			return $::NOK;
		}
		unless (open(TMPINITTAB, ">$::tmpinittab"))
		{
			MessageUtils->message('E', 'EMsgCANT_WRITEtoFILE', $::tmpinittab);
			return $::NOK;
		}
		while ($line = <INITTAB>)
		{
			if (!grep(/csm/, $line))
			{
				unless (print TMPINITTAB "$line")
				{
					MessageUtils->message('E', 'EMsgCANT_WRITEtoFILE',
										  $::tmpinittab);
					return $::NOK;
				}
			}
		}
		my $cmd = "$::MV -f $::tmpinittab $::inittab";
		MessageUtils->message('V', 'IMsgCMD', $cmd);
		my $rc = system($cmd) >> 8;
		if ($rc)
		{
			MessageUtils->message('E', 'EMsgCANT_RUN', $cmd, $rc);
		}

		close(INITTAB);
		close(TMPINITTAB);

		#  do we need this ??
		$cmd = "$::CHMOD 644 $::inittab";
		$rc  = system("$cmd");
		if ($rc >> 8)
		{
			MessageUtils->message('E', 'EMsgCMD_FAILED_RC', $::CHMOD, $rc);

			# try to continue
		}
	}
	else
	{

		# error could not rm osfirstboot from /etc/inittab.
		MessageUtils->message('E', 'EMsgNO_REMOVE', "csm", $::inittab);
		return $::NOK;
	}
	return $::OK;
}

#-------------------------------------------------------------------------------

=head3	get_configinfo

	figure out the name of the management server, the hostname of this node
	as known by the management server, the CSM Version, etc. 

	return 0 or 1

        Notes:

=cut

#-------------------------------------------------------------------------------

sub get_configinfo
{

	my ($attr, $value);
	my $match_svr         = 0;
	my $match_node        = 0;
	my $match_csmver      = 0;
	my $match_csmosname   = 0;
	my $match_rshell      = 0;
	my $match_setuprshell = 0;
	my $match_powermethod = 0;

	# get info from the config_info file
	if (-f $::CFGINFOFILE)
	{
		unless (open(CFGINFO, "<$::CFGINFOFILE"))
		{

			# Could not open $::CFGINFOFILE.
			MessageUtils->message('E', 'EMsgCANT_READ_FILE', $::CFGINFOFILE);
			return $::NOK;
		}

		while (<CFGINFO>)
		{
			($attr, $value) = split('=');

			if ($attr eq "ManagementServerIP")
			{
				$match_svr++;
				$::csm_server = $value;
				chomp $::csm_server;
			}
			if ($attr eq "Hostname")
			{
				$match_node++;
				$::akb_nodename = $value;
				chomp $::akb_nodename;
			}
			if ($attr eq "InstallCSMVersion")
			{
				$match_csmver++;
				$::csm_version = $value;
				chomp $::csm_version;
			}
			if ($attr eq "InstallOSName")
			{
				$match_csmosname++;
				$::csm_osname = $value;
				chomp $::csm_osname;
			}
			if ($attr eq "RemoteShell")
			{
				$match_rshell++;
				$::csm_rshell = $value;
				chomp $::csm_rshell;
			}
			if ($attr eq "SetupRemoteShell")
			{
				$match_setuprshell++;
				$::csm_setuprshell = $value;
				chomp $::csm_setuprshell;
			}
			if ($attr eq "InstallServer")
			{
				$::InstallServer = $value;
				chomp $::InstallServer;
			}
			if ($attr eq "InstallServerAKBNode")
			{
				$::InstallServerAKBNode = $value;
				chomp $::InstallServerAKBNode;
			}
			if ($attr eq "InstallServerIsGroup")
			{
				my $isgroup = $value;
				chomp $value;
				if ($value eq "yes") { $::ISGrp = 1; }
			}
			if ($attr eq "NFSServer")
			{
				$::NFSServer = $value;
				chomp $::NFSServer;
			}
			if ($attr eq "InstallMsgServer")
			{
				$::MSGServer = $value;
				chomp $::MSGServer;
			}
			
			if ($attr eq "InstallMethod")
			{
				$::InstallMethod = $value;
				chomp $::InstallMethod;
			}

			if ($attr eq "PowerMethod")
			{
				$match_powermethod++;
				$::csm_powermethod = $value;
				chomp $::csm_powermethod;
			}

			if ($attr eq "FIRSTBOOT_SCRIPT_DIR")
			{
				$::FIRSTBOOT_SCRIPT_DIR = $value;
				chomp $::FIRSTBOOT_SCRIPT_DIR;
			}

			if ($attr eq "OSUPGRADE_FIRSTBOOT_SCRIPT_DIR")
			{
				$::OSUPGRADE_FIRSTBOOT_SCRIPT_DIR = $value;
				chomp $::OSUPGRADE_FIRSTBOOT_SCRIPT_DIR;
			}

			if ($attr eq "POSTINSTSCRIPTS")
			{
				#@::postscriptlist = split(" ", $value);
				@::postinstscripts = split(" ", $value);
			}
			if ($attr eq "POSTOSUPGRADESCRIPTS")
			{
				@::postosupgradescripts = split(" ", $value);
			}
			if ($attr eq "Mode")
			{
				$::Mode = $value;
				chomp $::Mode;
			}
			if ($attr eq "InActiveMSAttr")
			{
				$::InActiveMSAttr = $value;
				chomp $::InActiveMSAttr;
			}
			if ($attr eq "ETCHOSTSMS")
			{
				$::ETC_HOSTS_MS = $value;
				chomp $::ETC_HOSTS_MS;
			}
		}
		if (!$match_svr || !$match_node)
		{

			# Could not find attribute values in the config_info file.
			MessageUtils->message('E', 'EMsgNO_Attr_Values');
			return $::NOK;
		}
		close(CFGINFO);
	}
	else
	{

		# Could not find $::CFGINFOFILE file.
		MessageUtils->message('E', 'EMsgNO_FIND', $::CFGINFOFILE);
		return $::NOK;
	}

	return $::OK;
}
#-------------------------------------------------------------------------------

=head3	mount_csminstall

	mount the /csminstall/csm directory to $::CSMCLIENTMNTDIR

	returns 0 - good  or 1 - bad 

        Notes:

=cut

#-------------------------------------------------------------------------------

sub mount_csminstall
{
	my ($src_dir, $dest_dir) = @_;
	my ($cmd, $rc, $server, $dir);

	# if there is an install server value and a directory to use
	#      then set it as the src_dir
	if ($::InstallServer)
	{
		($server, $dir) = split ':', $::InstallServer;
		if ($::InstallServerAKBNode && !$::ISGrp)
		{
			$server = $::InstallServerAKBNode;
		}
	}
	elsif ($::NFSServer)
	{
		($server, $dir) = split ':', $::NFSServer;
	}    #only look at install server if not on AIX

	if ($server && $server ne $::csm_server)
	{
		if ($dir)
		{
			$src_dir =~ s/\/csminstall/$dir/;
		}
		else
		{
			$src_dir =~ s/\/csminstall/$::SERVER_DIRECTORY/;
		}
	}
	else
	{
		$server = $::csm_server;
	}

	if (!-d $dest_dir)
	{

		# create a local directory
		$cmd = "$::MKDIR -p $dest_dir";
		$rc  = system("$cmd");
		if ($rc >> 8)
		{

			# Could not create $::CSMINSTDIR directory.
			MessageUtils->message('E', 'EMsgNO_CreateDir', $dest_dir);
			return $::NOK;
		}
	}

	#  Mount the filesystem
	$cmd = "$::MOUNT -o ro $server:$src_dir $dest_dir";

	#The NIC may not be so stable after the node just boot up.
	#So, here, try 3 times, and sleep 30 in between each time;
	my $times      = 3;
	my $sleep_time = 30;
	my $mounted    = 0;
	my $failed     = 0;

	while (($times > 0) && ($mounted == 0))
	{
		$times--;
		if ($failed)
		{
			sleep $sleep_time;
		}
		$rc = system("$cmd");
		if ($rc >> 8)
		{
			$failed = 1;
		}
		else
		{
			$mounted = 1;    #
		}
	}
	if (!$mounted)
	{

		#  Could not mount $srcdir.
		MessageUtils->message('E', 'EMsgNO_MOUNT', $server . ":" . $src_dir);
		return $::NOK;
	}
	return $::OK;
}
#-------------------------------------------------------------------------------

=head3	unmount_csminstall

	unmount the /csminstall/csm directory                #

	return 0 or 1

        Notes:

=cut

#-------------------------------------------------------------------------------

sub unmount_csminstall
{

	my ($unmnt_dir, undef) = @_;
	my ($rc,        $cmd);

	$cmd = "$::UNMOUNT $unmnt_dir";
	$rc  = system("$cmd");
	if ($rc >> 8)
	{

		# Internal call to unmount was not successful.
		MessageUtils->message('E', 'EMsgNO_INTERNAL_CALL', $::UMOUNT);
		return $::NOK;
	}
	return $::OK;
}
#-------------------------------------------------------------------------------

=head3	setagetty

	add -h option into /etc/inittab agetty entry for hardware flow control        #

	return 
		0 success
		1 fail
        Notes:

=cut

#-------------------------------------------------------------------------------

sub setagetty
{
	my $ret;
	$ret = $::NOK;
	if ($::PLTFRM eq "Linux")
	{
		my $line;
		unless (open(INITTAB, "<$::inittab"))
		{
			MessageUtils->message('E', 'EMsgCANT_READ_FILE', $::inittab);
			return $::NOK;
		}
		unless (open(TMPINITTAB, ">$::tmpinittab"))
		{
			MessageUtils->message('E', 'EMsgCANT_WRITEtoFILE', $::tmpinittab);
			return $::NOK;
		}
		while ($line = <INITTAB>)
		{
				# if agetty command don't contain -h option ,add it
				if (grep(/^[^#].*agetty/, $line))
				{
						if ($line !~ /-h/)
						{
								$line =~ s/agetty/agetty\ -h/;
								$ret = $::OK;
						}
				}

				unless (print TMPINITTAB "$line")
				{
						MessageUtils->message('E', 'EMsgCANT_WRITEtoFILE',
										$::tmpinittab);
						return $::NOK;
				}
		}
		my $cmd = "$::MV -f $::tmpinittab $::inittab";
		MessageUtils->message('V', 'IMsgCMD', $cmd);
		my $rc = system($cmd) >> 8;
		if ($rc)
		{
			MessageUtils->message('E', 'EMsgCANT_RUN', $cmd, $rc);
		}

		close(INITTAB);
		close(TMPINITTAB);

		#  do we need this ??
		$cmd = "$::CHMOD 644 $::inittab";
		$rc  = system("$cmd");
		if ($rc >> 8)
		{
			MessageUtils->message('E', 'EMsgCMD_FAILED_RC', $::CHMOD, $rc);

			# try to continue
		}
	}
	return $ret;
}

#-------------------------------------------------------------------------------

=head3	Main	

        Notes:

=cut

#-------------------------------------------------------------------------------

{    # main

	my ($cmd, $rc);

	# initialize the global data
	&initialize;

	#
	# start logging
	#
	MessageUtils->append_logging($::INSTALL_LOG);

	#
	# get the management server name and hostname for this node from
	#    the configinfo file
	#
	if (&get_configinfo != 0)
	{

		# Could not get node attribute values from the configinfo file.
		MessageUtils->message('E', 'EMsgNO_Attr_Values');
		$::GLOBAL_EXIT = 1;
		exit;
	}
    #
	# For hardware flow control used by SOL machines, 
	# "-h" option should be added into "agetty" command.
	# For all nodes using autoyast installmethod,
	# have this issue.  
    if ($::InstallMethod eq 'autoyast'
		&& $::arch !~ /ppc/)
	{
		# If agetty entry don't contain -h option,add it
		if (!&setagetty())
		{
			# Send signal to restart init process
			NodeUtils->runcmd("telinit q",-1);
			# Kill old agetty process to make new agetty start
			NodeUtils->runcmd("killall -q agetty", -1);

		};
		
	}

    # Release port 657 to make RMC take up it once makenode is called
    # and remove /etc/rc.d/rc3.d/S13csmgrabport script in case it is run every boot.
    if (  ($::PLTFRM eq "Linux")
        && ( -f "/etc/redhat-release")
        && !($::mode =~ /DCInstall/ || $::mode =~ /MinManaged/)
        && ( -f "/etc/rc.d/rc3.d/S13csmgrabport")
    ) 
    {
        my $port = 657;
        my $output = NodeUtils->runcmd("lsof -i:$port",-1);
        chomp($output);
        MessageUtils->message('V', 'IMsgShow_Output', "Searching csmgrabport:\n$output") if ($output);
        # Kill previous process that grab port 657.
        NodeUtils->runcmd("kill `cat /var/lock/csm/csmgrabport.pid`", -1);
        $output = NodeUtils->runcmd("lsof -i:$port",-1);
        chomp($output);
        if ($output)
        {
            MessageUtils->message('V', 'IMsgShow_Output', "CSM grab socket can not be killed:\n$output");   
        }
        else
        {
            MessageUtils->message('V', 'IMsgShow_Output', "Port $port has released successfully");
        }
        # Remvoe our csmgrabport script from runlevel3 scripts.
        NodeUtils->runcmd("$::RM -f /etc/rc.d/rc3.d/S13csmgrabport",-1);

    }

	#
	# mount /csminstall/csm
	#      the subroutine takes care of the InstallServer case
	#
	if (&mount_csminstall($::MSCSMINSTDIR, $::LOCALCSMINSTDIR) != 0)
	{

		#  Could not mount $::CSMINSTDIR.
		$::GLOBAL_EXIT = 1;
		exit;
	}
	else
	{
		$::mounted++;
	}
	if ($::PLTFRM eq "Linux")
	{

		# Since CSM does not install AIX packages, only do this if the node is
		# running Linux

		if (&mount_csminstall($::CSMOSDEPINSTDIR, $::LOCALCSMOSDEPINSTDIR) != 0)
		{

			#  Could not mount $::CSMOSDEPINSTDIR.
			$::GLOBAL_EXIT = 1;
			exit;
		}
		else
		{
			$::OSDEPmounted++;
		}
	}

	##		Run customer provided scripts

	#	Set some environment variables for the cust scripts to use

	#   The name of this node as it is known by the CSM
	#   management server

	$ENV{'CSMNODENAME'} = "$::akb_nodename";

	#	The mount point for CSM on the node is equivalent
	#	to /csminstall on the management server

	$ENV{'CSMMOUNTPOINT'} = "/var/opt/csm/mnt";

	#	The data directory where additional user provided scripts
	#	and data files will be located.

	$ENV{'SCRIPTDATAPATH'} = "/var/opt/csm/mnt/csm/scripts/data";

	##	Run each user script
	#   The script list and script directory are different for different 
	#   install methods
	my @scriptlist;
	my $scriptdir;
	if ($::InstallMethod eq "you" || $::InstallMethod eq "kickstart-upgrade")
	{
		@scriptlist = @::postosupgradescripts;
		$scriptdir  = $::OSUPGRADE_FIRSTBOOT_SCRIPT_DIR
	}
	else
	{
		@scriptlist = @::postinstscripts;
		$scriptdir  = $::FIRSTBOOT_SCRIPT_DIR
	}

	foreach my $name (@scriptlist)
	{

		# cd to the directory of the users cust script in case they
		#   use a relative path to get to the data directory.
		my $cmd = "cd $scriptdir; ./$name";
		my $output = NodeUtils->runcmd($cmd, 0);
		if ($::RUNCMD_RC)
		{
			MessageUtils->message('E', 'EMsgCANT_RUN', $cmd, $::RUNCMD_RC);
		}
		else
		{
			MessageUtils->message('I', 'IMsgShow_Output', $output);
		}
	}
	#
	# remove osfirstboot from /etc/initab
	#
	if (&update_inittab != 0)
	{

		#  Could not update  /etc/inittab
		MessageUtils->message('E', 'EMsgNO_UPDATE', $::inittab);
		$::GLOBAL_EXIT = 1;
		exit;
	}

	#
	# finish up and exit
	#
	END
	{

		if ($::PLTFRM eq "Linux")
		{
			my $write_status = "$::LOCALCSMINSTDIR/nodestatus.client";
			my $cmd;
			if($::Mode eq "DCInstall")
			{
				$cmd =
	  				"$write_status -S $::MSGServer -n $::akb_nodename -M \"Installed\"";
				$rc = system("$cmd");
				if ($rc >> 8)
				{

					#  Could not execute the write_status command.
					MessageUtils->message('E', 'EMsgNO_RunCmd', $cmd);
					$::GLOBAL_EXIT = 1;
					exit;
				}
			}
			$cmd = "$write_status -S $::MSGServer -n $::akb_nodename  -E";
			my $rc  = system("$cmd");
			if ($rc >> 8)
			{

				#  Could not execute the write_status command.
				MessageUtils->message('E', 'EMsgNO_RunCmd', $cmd);
				$::GLOBAL_EXIT = 1;
				exit;
			}
		}
		#
		# unmount $::LOCALCSMINSTDIR  - if we mounted it
		#
		if ($::mounted)
		{

			# unmount /csminstall/csm
			if (&unmount_csminstall($::LOCALCSMINSTDIR) != 0)
			{

				#  Unmount of $::CSMINSTDIR failed.
				MessageUtils->message('E', 'EMsgNO_UNMOUNT', $::LOCALCSMINSTDIR);
				$::GLOBAL_EXIT = 1;
			}
		}

		if ($::OSDEPmounted)
		{

			# unmount
			if (&unmount_csminstall($::LOCALCSMOSDEPINSTDIR) != 0)
			{

				#  Unmount of $::CSMINSTDIR failed.
				MessageUtils->message('E', 'EMsgNO_UNMOUNT',
								   $::LOCALCSMOSDEPINSTDIR);
				$::GLOBAL_EXIT = 1;
			}
		}
		MessageUtils->stop_logging();

		#Determine exit code
		if ($::GLOBAL_EXIT > $?)
		{
			$? = $::GLOBAL_EXIT;
		}

		#Always exit 0 so the install doesn't completely fail
		$? = 0;

	}

}
