#! /usr/bin/perl # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # # # Licensed Materials - Property of IBM # # (C) COPYRIGHT International Business Machines Corp. 1997,2019 # 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 #*====================================================================== #* #* Module Name: hagsp #* #* Description: #* Script to start the Group Services Daemon as an SRC subsystem. #* #* This file is formatted with tabstops of 4. #*====================================================================== # sccsid = "@(#)54 1.79 src/rsct/pgs/cmds/aix/hagsp.pl, gsctrl, rsct_rady, rady2035a 11/16/15 15:05:20" unshift(@INC, '/opt/rsct/bin'); unshift(@INC, '/opt/rsct/bin'); # require "/opt/rsct/bin/hagsSetup.pkg"; $OSname = `/bin/uname -s`; chomp($OSname); if ("Linux" eq $OSname) { $ENV{'PATH'} = $ENV{'PATH'} . ":/bin"; } ($dir,$progname) = $0 =~ /(.*\/)?(.*)/; # get basename of $0 &TSprint( TRACE_FYI, "$0 $#ARGV $[ $subsys input arguments: @ARGV\n" ); sub Usage { warn "Usage: $progname -s -p [-i ] [-d ]\n"; exit -1; } # Too many arguments? if (8 <= $#ARGV) { &Usage; } # Avoid getopts, to remove dependencies on Perl libraries... while (@ARGV) { $arg = shift @ARGV; if ("-s" eq $arg) { $subsys = shift @ARGV; } elsif ("-p" eq $arg) { $DomainName = shift @ARGV; } elsif ("-i" eq $arg) { $ClusterID = shift @ARGV; } elsif ("-d" eq $arg) { $DefaultLogName = shift @ARGV; } else { &Usage; } } $RSCT_ROOT = "/opt/rsct"; $RSCT_BIN = "$RSCT_ROOT/bin"; # # Note: PGSD_RELEASE_LEVEL must be to the proper daemon level here. # PGSD_RELEASE_LEVEL ensures the proper behaviour of daemons, esp. in the # mixed environment. # Current Release Level $CURRENT_RELEASE_LEVEL = 21; # current level # $PGSD_RELEASE_LEVEL = &GSCurrentReleaseLevel(); &TSprint( TRACE_FYI, "PGSD_RELEASE_LEVEL=$PGSD_RELEASE_LEVEL\n"); $Perl = $ENV{'PGSD_PERL'}; $domainType = $ENV{'HA_DOMAIN_TYPE'}; $daemon = $ENV{'PGSD_DAEMON'}; if (!defined $ENV{'PGSD_DAEMON'}) { $daemon = "${subsys}d"; } # defect 145926, for HACMP case the file name will have grpsvcs_ prefix $TRACEFILEPREFIX="trace"; if($domainType eq "HAES") { $TRACEFILEPREFIX="${subsys}_trace"; } elsif($domainType eq "PSSP") { # considering PSSP & partition name $TRACEFILEPREFIX="${subsys}_${DomainName}_trace"; } if($domainType eq "CLUSTER") { &TSprint( TRACE_FYI, "domainType=$domainType, subsys=$subsys, ". "DomainName=$DomainName, ". "ClusterID=$ClusterID, ". "DefaultLogName=$DefaultLogName, Perl=$Perl\n" ); } else { &TSprint( TRACE_FYI, "domainType=$domainType, subsys=$subsys, ". "DomainName=$DomainName, ". "DefaultLogName=$DefaultLogName, Perl=$Perl\n" ); } if (!defined $ENV{'PGSD_TID_DIR'}) { if( $domainType eq "CLUSTER" ) { $TidDir = "/var/ct/$ClusterID/lck/$subsys/$subsys.tid.$DomainName"; } else { $TidDir = "/var/ha/lck/$subsys.tid.$DomainName"; } $ENV{'PGSD_TID_DIR'} = $TidDir; } else { $TidDir = $ENV{'PGSD_TID_DIR'}; } if(! -d $TidDir) { eval `mkdir -p $TidDir`; if ($? != 0) { local($msg) = " cannot create directory $TidDir."; &TSprint( TRACE_FYI, "$msg\n"); printf STDERR "$msg\n"; &fcstart_error(__LINE__,$msg); exit 3; } eval `chmod 0750 $TidDir`; } &TSprint( TRACE_FYI, "PGSD_TID_DIR=$ENV{'PGSD_TID_DIR'}\n" ); &TSprint( TRACE_FYI, "PATH=$ENV{'PATH'}\n" ); # Set the env variable HA_DOMAIN_NAME to our partition name so that # heartbeat will see it. For now, also set HA_SYSPAR_NAME. $ENV{'HA_SYSPAR_NAME'} = $DomainName; $ENV{'HA_DOMAIN_NAME'} = $DomainName; $ENV{'SP_NAME'} = $DomainName; if($domainType eq "CLUSTER") { # Set CT_DOMAIN, CT_CLUSTER_ID, and CT_CLUSTER_NAME $ENV{'CT_DOMAIN'} = 1; } # # Get the node number $NodeNumber = $ENV{'PGSD_NODE_NUMBER'}; &TSprint( TRACE_FYI, "NodeNumber=$NodeNumber\n" ); &TSprint( TRACE_FYI, "PGSD_NODE_NUMBER=$ENV{'PGSD_NODE_NUMBER'}\n" ); # Declare the variables for Cluster local(%ct_gs_node_level); # node level(RSCT) for each node local($ct_gs_global_logLength); local($ct_gs_global_logDirSize); local(%ct_gs_local_level); # per node local(%ct_gs_local_logLength); # per node local(%ct_gs_local_logDirSize); # per node local($ct_gs_pri_fixed, $ct_gs_pri_value); # # In PSSP/Cluster, Update the release level as early as possible if($domainType eq "CLUSTER") { # # Update the release level and build the registry information &Update_CodeLevel_And_Build_Cluster_Table($NodeNumber, $PGSD_RELEASE_LEVEL); } elsif($domainType eq "PSSP") { &Query_And_Update_My_CodeLevel($NodeNumber, $PGSD_RELEASE_LEVEL); } # # Handling for Markfile is now changed. # In principal, every node will have a markfile and it will be updated # whenever the state of daemon is changed. The daemon will also update # the content of the markfile at the time of death. # The daemon will read the file, compute the time how much the daemon # has been down, and call hb_simulate_death with the time length # if it was LNN or NS. Otherwise it call the function to stop SD. # Note: if the markfile does not exist, the time length will be zero. # # A new environment will be passed so that the daemon knows whether # SD is enabled(PSSP,CLUSTER) or disabled(under HAES) # PGSD_ALLOW_SIMULATE_DEATH # If this environment is not defined, it is assumed as enabled. # # After the change of SD, PGSD_STEP_ON_HATS will be obsolete # although it will still be passed for awhile. # # $MarkFile = "$TidDir/${subsys}.status.$DomainName"; $ENV{'PGSD_MARKFILE'} = $MarkFile; &TSprint( TRACE_FYI, "PGSD_MARKFILE=$ENV{'PGSD_MARKFILE'}\n" ); # if ($domainType eq "HAES") { #simulate death will be disabled $ENV{'PGSD_ALLOW_SIMULATE_DEATH'} = "no"; $ENV{'PGSD_STEP_ON_HATS'} = "no"; } else { #simulate death will be enabled $ENV{'PGSD_ALLOW_SIMULATE_DEATH'} = "yes"; if ( ! -e $MarkFile ) { &TSprint( TRACE_FYI, "No Markfile $MarkFile found.\n" ); $ENV{'PGSD_STEP_ON_HATS'} = "no"; } else { &TSprint( TRACE_FYI, "Markfile $MarkFile found.\n"); $ENV{'PGSD_STEP_ON_HATS'} = "yes"; } } &TSprint( TRACE_FYI, "SimulateDeath is set to $ENV{'PGSD_ALLOW_SIMULATE_DEATH'}\n"); &TSprint( TRACE_FYI, "(Obsolete) Step On Hats value = $ENV{'PGSD_STEP_ON_HATS'}\n"); $NodeNumber = $ENV{'PGSD_NODE_NUMBER'}; &TSprint( TRACE_FYI, "NodeNumber=$NodeNumber\n" ); &TSprint( TRACE_FYI, "PGSD_NODE_NUMBER=$ENV{'PGSD_NODE_NUMBER'}\n" ); # # $IncarnationNumber = &GetInstance( $TidDir, "${subsys}d" ); $ENV{'PGSD_INCARNATION_NUMBER'} = $IncarnationNumber; &TSprint( TRACE_FYI, "PGSD_INCARNATION_NUMBER=$ENV{'PGSD_INCARNATION_NUMBER'}\n" ); # if($domainType eq "CLUSTER") { $daemonName = $subsys; } else { $daemonName = (0 == $NodeNumber) ? "$subsys.$DomainName" : $subsys; } # # # set PGSD_LOGFILE local($LogDir) = $ENV{'PGSD_LOGDIR'}; if(!defined($LogDir)) { if($domainType eq "CLUSTER") { $LogDir = "/var/ct/$ClusterID/log/$subsys"; } else { $LogDir = "/var/ha/log"; } $ENV{'PGSD_LOGDIR'} = $LogDir; } if (!defined( $ENV{'PGSD_LOGFILE'} ) ) { $ENV{'PGSD_LOGFILE'} = "$LogDir/${TRACEFILEPREFIX}"; } if(! -d $LogDir) { eval `mkdir -p $LogDir`; if ($? != 0) { local($msg) = " cannot create directory $LogDir."; &TSprint( TRACE_FYI, "$msg\n"); printf STDERR "$msg\n"; &fcstart_error(__LINE__,$msg); exit 3; } eval `chmod 0750 $LogDir`; } &TSprint( TRACE_FYI, "PGSD_LOGFILE=$ENV{'PGSD_LOGFILE'}, ", "PGSD_LOGDIR=$ENV{'PGSD_LOGDIR'}\n" ); # #Set run dir if (!defined $ENV{'PGSD_RUN_DIR'}) { if($domainType eq "CLUSTER") { $RunDir = "/var/ct/$ClusterID/run/$subsys"; } else { $RunDir = "/var/ha/run/$subsys.$DomainName"; } $ENV{'PGSD_RUN_DIR'} = $RunDir; } else { $RunDir = $ENV{'PGSD_RUN_DIR'}; } # defect 169686: avoid the invocation of /usr/bin/pwd by sh/ksh $ENV{'PWD'} = $ENV{'PGSD_RUN_DIR'}; if(! -d $RunDir) { eval `mkdir -p $RunDir`; if ($? != 0) { local($msg) = " cannot create directory $RunDir."; &TSprint( TRACE_FYI, "$msg\n"); printf STDERR "$msg\n"; &fcstart_error(__LINE__,$msg); exit 3; } eval `chmod 0750 $RunDir`; } &TSprint( TRACE_FYI, "PGSD_RUN_DIR=$ENV{'PGSD_RUN_DIR'}\n" ); # hagsreap is a perl script which will trim the amount of space that # hags logs and cores occupies. We fire it off, and then just let it # go, and check back on it later. # hagsreap will remove hags logs and core files so that they consume # no more than sizeLimit bytes. $Command=$ENV{'PGSD_REAP_CMD'}; $sizeLimit=$ENV{'PGSD_REAP_SIZE'}; # determine PGSD_REAP_SIZE in CLUSTERS if(($domainType eq "CLUSTER") && !defined($sizeLimit)) { # CLUSTER and No PGSD_REAP_SIZE defined $sizeLimit = $ct_gs_local_logDirSize{$NodeNumber}; if(!defined($sizeLimit) || ($sizeLimit == 0)) { $sizeLimit = $ct_gs_global_logDirSize; } if(!defined($sizeLimit) || ($sizeLimit == 0)) { $sizeLimit = 5; # in MB } $sizeLimit = $sizeLimit * 1024 * 1024; # in Bytes } elsif(!defined($sizeLimit)) { $sizeLimit=(5 * 1024 * 1024); # default size is 5M } # defect 145926: if don't declare here then outside the following # if clause the newDefaultLogname would be empty then in hagsreap # it will get an empty default log name $DefaultLogNameParameter = ("" ne $DefaultLogName) ? "-d $DefaultLogName" : ""; $cmd = "$Perl -S hagsreap $DefaultLogNameParameter $subsys $DomainName ". " $Command $sizeLimit $NodeNumber " . " $ENV{'PGSD_LOGDIR'} $ENV{'PGSD_RUN_DIR'}"; select STDOUT; $|=1; # to flush output &TSprint( TRACE_FYI, "cmd = $cmd.\n" ); if (0 == ($kidPid = fork)) { exec $cmd; die "Couldn't exec hagsreap: $!\n"; } select STDOUT; $|=0; # enable buffering again # # Remove the client socket directory # (/var/ha/soc/.clients[.]) on HAES and PSSP # (/var/ct//soc/ctgrpsvcs/clients.) on CLUSTER # if($domainType eq "CLUSTER") { $client_dir = "/var/ct/$ClusterID/soc/$subsys/$subsys.clients"; } else { $client_dir = "/var/ha/soc/$subsys.clients"; } if( defined($DomainName) ) { $client_dir = "$client_dir.$DomainName"; } # select STDOUT; $|=1; # disable buffering # 1. Fork another process that will remove the client socket files if( 0 == ($client_rm_pid = fork) ) { # create the client dir eval `mkdir -p $client_dir`; eval `chmod 1777 $client_dir`; # child process. delete the socket files and directory eval `rm -rf $client_dir/*`; exit(0); } # select STDOUT; $|=0; # enable buffering again # # # note that MoveToRunDir should probably be called BEFORE hagsreap, # so that the core file is found and renamed before we calculate how # much disk space we are using, otherwise it's not counted. # It will be counted the next time we restart, however. # If we move it, we lose some parallelism, as we'd have to determine # our incarnation number first. &MoveToRunDir( $RunDir, $NodeNumber, $IncarnationNumber ); local($newDefaultLogName); if ("" ne $DefaultLogName) { # We want to save the messages in the default log. # so, we rename it to the log name, and daemon appends to the log! $newDefaultLogName = "$DefaultLogName.${NodeNumber}_$IncarnationNumber"; &TSprint(TRACE_FYI, "rename $DefaultLogName to $newDefaultLogName\n"); rename($DefaultLogName, $newDefaultLogName) || warn "rename of $DefaultLogName to $newDefaultLogName failed!: $!\n"; eval `chmod 0644 $newDefaultLogName`; } # Set the fixed priority value of the daemon. If not set, it won't setpri # to this value. If PGSD_TSCONFIG set, use it. If not, if PSSP, use SDR, # if HAES, use odm, if CLUSTER, use Registry. if (defined $ENV{'PGSD_TSCONFIG'}) { &TSprint( TRACE_FYI, "PGSD_TSCONFIG=$ENV{'PGSD_TSCONFIG'}\n" ); ($fixFlag, $TSpriority, $GSLogLength) = split(':',$ENV{'PGSD_TSCONFIG'}); $GSpriority = (38 < $TSpriority) ? 1 + $TSpriority : 39; } elsif ($domainType eq "PSSP") { $cmd = "SDRGetObjects -x TS_Config Run_FixPri FixPri_Value Frequency Sensitivity Log_Length |"; &TSprint( TRACE_FYI, "$cmd\n" ); if (open(TSCONFIG, $cmd)) { ($fixFlag, $TSpriority, $TSfrequency, $TSsensitivity, $TSLogLength) = split(' ', ); close TSCONFIG; } else { warn "Can't get TS_Config from SDR: $cmd: $!\n"; warn "Setting some default values.\n"; # need some default values $fixFlag = 0; $TSpriority = 39; $TSLogLength = 5100; } $GSLogLength = $TSLogLength; $GSpriority = (38 < $TSpriority) ? 1 + $TSpriority : 39; } elsif ($domainType eq "HAES") { # save the ODMDIR $saved_odmdir=$ENV{'ODMDIR'}; # set the ODMDIR in preperation for the odmget $ENV{'ODMDIR'} = "/etc/es/objrepos"; $odmcmd = "odmget HACMPtopsvcs | awk '/runFixedPri/ {runFixedPri=\$3} /fixedPriLevel/ {fixedPriLevel=\$3} /gsLogLength/ {gsLogLen=\$3} END { printf(\"%d:%d:%d\", runFixedPri, fixedPriLevel, gsLogLen)}'|"; &TSprint( TRACE_FYI, "odmcmd = $odmcmd.\n" ); if (open(TSCONFIG, $odmcmd)) { ($fixFlag, $TSpriority, $GSLogLength) = split(':',); close TSCONFIG; if( !defined($GSLogLength) || int($GSLogLength) < 5000 ) { # force the length at a minimum of 5000 $GSLogLength = 5000; } } else { $fixFlag = 0; $TSpriority = 38; $GSLogLength = 5200; } # restore the ODMDIR to original $ENV{'ODMDIR'} = $saved_odmdir; $GSpriority = (38 < $TSpriority) ? 1 + $TSpriority : 39; } elsif ($domainType eq "CLUSTER") { # Cluster. Read SR /IBM/GroupServices/GS_GlobalConfig and GS_LocalConfig $fixFlag = $ct_gs_pri_fixed; $GSpriority = $ct_gs_pri_value; $GSLogLength = $ct_gs_global_logLength; } else { # Unknown environment, and no PGSD_TSCONFIG. Use some defaults. $fixFlag = 0; $TSpriority = 38; $GSLogLength = 6000; $GSpriority = (38 < $TSpriority) ? 1 + $TSpriority : 39; } if (defined($fixFlag) && 0 != $fixFlag) { if(!defined($GSpriority)) { $GSpriority = 39; } $ENV{'PGSD_FIXED_PRIORITY'} = $GSpriority; } # This no of lines if(!defined($GSLogLength) || ($GSLogLength < 5000) ) { # make the logLength at least 5000 $GSLogLength = 5000; } # now convert "GSLogLength" to "GSLogSize" in Byte if "CT_TR_FILE_SIZE" is not defined $GSLogSize = $ENV{'CT_TR_FILE_SIZE'}; if (!defined($ENV{'CT_TR_FILE_SIZE'})) { $GSLogSize = $GSLogLength * 200 * 4; # make initial size to 4MB } else { $GSLogSize = $ENV{'CT_TR_FILE_SIZE'}; # use whatever it is in the environment variable } $KbSzUnit = 256 * 1024; # 256Kb $GSLogSize = (($GSLogSize / $KbSzUnit) + 1 ) * $KbSzUnit; &TSprint( TRACE_FYI, "TS_Config data: Run_FixPri=$fixFlag, ", "GSFixPri_Value=$GSpriority, ", "LogLength=$GSLogLength.\n" ); &TSprint( TRACE_FYI, "PGSD_FIXED_PRIORITY=$ENV{'PGSD_FIXED_PRIORITY'}\n" ); $ENV{'CT_TR_FILE_SIZE'} = $GSLogSize; &TSprint( TRACE_FYI, "CT_TR_FILE_SIZE=$ENV{'CT_TR_FILE_SIZE'}\n" ); if (!defined( $ENV{'PGSD_PRM_SOCK'} ) ) { if ($domainType eq "HAES") { if (!defined($ENV{"PGSD_HAESLEVEL"})) { # Assume HACMP/ES 4.3 $ENV{'PGSD_PRM_SOCK'} = "$subsys"; } else { $haesLevel = $ENV{"PGSD_HAESLEVEL"}; if ($haesLevel eq "42") { &TSprint( TRACE_FYI, "Assuming HAES level 42.\n"); $ENV{'PGSD_PRM_SOCK'} = "$subsys.$DomainName"; } else { $ENV{'PGSD_PRM_SOCK'} = "$subsys"; } } } elsif($domainType eq "CLUSTER") { $ENV{'PGSD_PRM_SOCK'} = "$subsys"; } else { $ENV{'PGSD_PRM_SOCK'} = "$subsys.$DomainName"; } } &TSprint( TRACE_FYI, "PGSD_PRM_SOCK=$ENV{'PGSD_PRM_SOCK'}\n"); if (!defined( $ENV{'PGSD_SUPP_SOCK'} ) ) { if($domainType eq "CLUSTER") { $ENV{'PGSD_SUPP_SOCK'} = "/var/ct/$ClusterID/soc/${subsys}/${subsys}dsocket.$DomainName"; } else { $ENV{'PGSD_SUPP_SOCK'} = "/var/ha/soc/${subsys}dsocket.$DomainName"; } } &TSprint( TRACE_FYI, "PGSD_SUPP_SOCK=$ENV{'PGSD_SUPP_SOCK'}\n"); # remove pgsd socket (so that client can get errno=ENO rather ECONNREFUSED) if( -e $ENV{'PGSD_SUPP_SOCK'} ) { eval `rm -f $ENV{'PGSD_SUPP_SOCK'}`; } # If in HAES/CLUSTER, worry about setting our hats socket pointer. if (!defined( $ENV{'HB_SERVER_SOCKET'} ) ) { if ($domainType eq "HAES") { $ENV{'HB_SERVER_SOCKET'} = "/var/ha/soc/topsvcs/server_socket"; } elsif($domainType eq "CLUSTER") { $ENV{'HB_SERVER_SOCKET'} = "/var/ct/$ClusterID/soc/cttopsvcs/server_socket"; } } &TSprint( TRACE_FYI, "HB_SERVER_SOCKET=$ENV{HB_SERVER_SOCKET}\n"); # Allowing new function differs between HAES and PSSP, since HAES has # no SDR to give us the lowest common denominator. if ($domainType eq "HAES") { $ENV{'PGSD_NEW_FUNCTION'} = "unsafe"; } else { if (!defined( $ENV{'PGSD_NEW_FUNCTION'} ) ) { $ENV{'PGSD_NEW_FUNCTION'} = "safe"; } } &TSprint( TRACE_FYI, "PGSD_NEW_FUNCTION=$ENV{'PGSD_NEW_FUNCTION'}\n" ); $| = 1; #flush and unbuffer stdout to get next messages asap. # Allow hagsreap to finish, if it hasn't already. if (0 != $kidPid) { local($startedWaiting) = time; waitpid( $kidPid, 0 ); if ( 0 < ($waited = time - $startedWaiting) ) { &TSprint( TRACE_FYI, "We waited $waited seconds for hagsreap.\n" ); } } # # Wait the child process that is removing the client socket files # waitpid($client_rm_pid, 0); # #if ( ! -d $client_dir ) { # # create the client directory # mkdir($client_dir, 0777) || die "Can't make $client_dir: $!"; #} # make sure the filemode (rwxrwxrwt) of the client socket directory eval `chmod 1777 $client_dir`; &TSprint( TRACE_FYI, "Client socket directory = $client_dir\n" ); # The environment PGSD_AUTH_BY_DAEMON to enable the new auth. method $ENV{"PGSD_AUTH_BY_DAEMON"} = 1; # If in the HA domain, then no SDR. Hard code for now! Otherwise, # use the SDR to find the LOWEST code level for this partition. # Also, determine if the domain is, or is not, a single level. In # the HAES domain this is important to determine if we allow back-level # daemons in. if ($domainType eq "HAES") { # See 73167. We'll determine the gs_level based on HACMP level # and choose the reasonable minimum gs_level. # In this release, the appropripate gs_level is 5. # See 73402, In addition, the max. upgradable level is also 5. #$ENV{'PGSD_CODELEVEL'} = $SUPPORTED_RELEASE_LEVEL_FOR_HAES; #$ENV{'PGSD_MAX_CODELEVEL'} = $MAX_RELEASE_LEVEL_FOR_HAES; # Check for mixed (migrating) domain. $haesver = `clmixver`; $ENV{'PGSD_HAES_CODELEVEL'} = $haesver; $rcMix = $?; chomp $ENV{'PGSD_HAES_CODELEVEL'}; if ((0 == $rcMix) && ($haesver >= 11) ) { # next HACMP version is 11 &TSprint( TRACE_FYI, "clmixver says the domain is one level.\n"); $ENV{'PGSD_MIXEDLEVEL'} = "no"; $allowMixing = "no"; } else { &TSprint( TRACE_FYI, "clmixver says the domain is mixed levels,", " returns an error: $rcMix\n"); $ENV{'PGSD_MIXEDLEVEL'} = "yes"; # assume most flexible $allowMixing = "yes"; } if (!defined( $ENV{'PGSD_ALLOW_MIXING'} ) ) { # Override if not set. $ENV{'PGSD_ALLOW_MIXING'} = $allowMixing; } $ENV{'HA_GS_DOMAIN_MASTER_NAME'} = "clstrmgr"; } elsif($domainType eq "CLUSTER") { # Determine the working code level from REGISTRY # Determine gs_release_level local($ext_gs_level) = $ENV{'PGSD_CODELEVEL'}; local($desired_level); $desired_level = &Determine_CodeLevel_For_Cluster($NodeNumber,$ext_gslevel); if($desired_level < $PGSD_RELEASE_LEVEL) { # There are other old-nodes out there. &TSprint( TRACE_FYI, "There are some (old) level nodes with lvl=$desired_level.\n"); sleep(6); # WAIT for the other's joining $desired_level = &Determine_CodeLevel_For_Cluster( $NodeNumber,$ext_gslevel ); } # ?? remove DCE creds if it was obtained # &remove_dce_creds_for_sdr_if_exist(); # # Not needed: Conversion: "int" level -> "str" level # $desired_level = &GSLevel_To_RSCT( $desired_level ); $ENV{'PGSD_CODELEVEL'} = $desired_level; # for setting PGSD_MIXEDLEVEL # Not needed: local($ourLevel) = &RSCT_To_GSLevel(&GSCurrentReleaseLevel()); local($ourLevel) = &GSCurrentReleaseLevel(); if ($ourLevel eq $desired_level) { $ENV{'PGSD_MIXEDLEVEL'} = "no"; # All, are at our level. } else { $ENV{'PGSD_MIXEDLEVEL'} = "yes"; # Yup, we've got mixed levels. } if (!defined( $ENV{'PGSD_ALLOW_MIXING'} ) ) { $ENV{'PGSD_ALLOW_MIXING'} = "yes"; } } else { # # Determine the working code level from SDR and PSSP levels # # Determine gs_release_level local($ext_gs_level) = $ENV{'PGSD_CODELEVEL'}; local($desired_level) = &Determine_CodeLevel_For_PSSP( $NodeNumber,$ext_gslevel); if($desired_level < $PGSD_RELEASE_LEVEL) { # There are other old-nodes out there. &TSprint( TRACE_FYI, "There are some (old) level nodes lvl=$desired_level.\n"); sleep(6); # WAIT for the other's joining $desired_level = &Determine_CodeLevel_For_PSSP( $NodeNumber,$ext_gslevel ); } # remove DCE creds if it was obtained &remove_dce_creds_for_sdr_if_exist(); # # Not needed: Conversion: "int" level -> "str" level # $desired_level = &GSLevel_To_RSCT( $desired_level ); $ENV{'PGSD_CODELEVEL'} = $desired_level; # for setting PGSD_MIXEDLEVEL # Not needed: local($ourLevel) = &RSCT_To_GSLevel(&GSCurrentReleaseLevel()); local($ourLevel) = &GSCurrentReleaseLevel(); if ($ourLevel eq $desired_level) { $ENV{'PGSD_MIXEDLEVEL'} = "no"; # All, are at our level. } else { $ENV{'PGSD_MIXEDLEVEL'} = "yes"; # Yup, we've got mixed levels. } if (!defined( $ENV{'PGSD_ALLOW_MIXING'} ) ) { $ENV{'PGSD_ALLOW_MIXING'} = "yes"; } } # # remove the dce/creds if exist &remove_dce_creds_for_sdr_if_exist(); # # wait until the DMS is loaded (needed now by GS) wait_till_dms_loaded(); &TSprint( TRACE_FYI, "PGSD_CODELEVEL=$ENV{'PGSD_CODELEVEL'}\n" ); if(defined $ENV{'PGSD_MAX_CODELEVEL'}) { &TSprint( TRACE_FYI, "PGSD_MAX_CODELEVEL=$ENV{'PGSD_MAX_CODELEVEL'}\n"); } &TSprint( TRACE_FYI, "PGSD_MIXEDLEVEL=$ENV{'PGSD_MIXEDLEVEL'}\n" ); &TSprint( TRACE_FYI, "PGSD_ALLOW_MIXING=$ENV{'PGSD_ALLOW_MIXING'}\n" ); # # run the daemon $cmd = "$daemon $daemonName @ARGV"; &TSprint( TRACE_FYI, "cmd = $cmd\n" ); exec $cmd; die "exec $cmd failed!: $!\n"; #----------------------------------------------------------------- # # Determination Steps: # Precondition: # SDR (GS_Config) for my node was already updated. # Brief procedure: # 1. If desired level is known, return and use it. # 2. Build a hash (spversions{}) for "splst_versions -t" that includes # all nodes except CWS(node 0) in the current partition. # 3. Read GS_Config and build two hash tables; # gsreleases{} for the nodes in spversions{}, # and gonenodes{} for unknown nodes(i.e., unknown # 4. On CWS, delete GS_Config entries for gonenodes{} # 5. Walk through spversions(): # Add the node to gsreleases{} if it is not in gsreleases{}. # NOTE: table gsreleases{} will have information for all nodes. # 6. Choose the lowest release_level from the gsreleases{}. # Note: This function will exit if it hits any error situations. # prototype: Determine_CodeLevel_For_PSSP(NodeNumber, desired_level) sub Determine_CodeLevel_For_PSSP { local($NodeNumber, $desired_level) = @_; # 1. If desired_level is known, return it with the level # if( defined($desired_level) ) { # If $desired level is not "int", convert it to number if(!($desired_level =~ /^[0-9]/)) { $desired_level = &RSCT_To_GSLevel($desired_level); } return $desired_level; } # Local variables local(%gsreleases); # for gs_config local(%gonenodes); # unknown(deleted) nodes local($a_line, $nodenum, $pssp_level, $gs_level, $sdr_level); local(@pair); # 2. Build a hash table (spversions) from 'splst_versions -t' # note: splst_verisons -t does NOT give you the version of the CWS # note: splst_verisons -n 0 DOES give you the version of the CWS if( !defined(%spversions) || !defined($Already_SPLST_Found) ) { $Already_SPLST_Found = 1; %spversions = &Build_SP_Versions(); } # 3. Read GS_Config and build tables(%gsreleases, %gonenodes) local(@rel_pairs)=`SDRGetObjects -x GS_Config node_number gs_release_level`; if( $? != 0 ) { local($msg) = "Unable to retrieve SDR GS_Config. $!"; &TSprint( TRACE_FYI, "$msg\n"); #&print_message("EMsg_Cannot_get_dce_creds", "hagsd"); printf STDERR "$msg\n"; &fcstart_error(__LINE__,$msg); exit 1; } foreach $a_line (@rel_pairs) { chop($a_line); # trim new-line on each line... ($nodenum, $gs_level) = split(' ',$a_line); if( $nodenum == 0 || defined($spversions{$nodenum}) ) { # node in the partition $gsreleases{$nodenum} = $gs_level; } else { # unknown or deleted node $gonenodes{$nodenum} = $gs_level; } } # 4. On CWS, delete GS_Config entries in %gonenodes if( $NodeNumber == 0 && !defined($Already_GSCFG_Removed) ) { $Already_GSCFG_Removed = 1; # To prevent second removals while( @pair = each %gonenodes ) { $nodenum = $pair[0]; &get_dce_creds_for_sdr_if_first(); # obtain DCE creds `SDRDeleteObjects GS_Config node_number==$nodenum`; } } # 5. Walk through spversions and complete gsreleases while( @pair = each %spversions ) { ($nodenum, $pssp_level) = @pair; if( !defined($gsreleases{$nodenum}) ) { # node is in "splst" but not in "gs_config". Add it $gsreleases{$nodenum} = &RSCT_To_GSLevel($pssp_level); } } # 6. Choose the lowest levels from %gsreleases $desired_level = &GSCurrentReleaseLevel(); while( @pair = each %gsreleases ) { $gs_level = $pair[1]; if($gs_level < $desired_level) { $desired_level = $gs_level; } } # "int" level return $desired_level; } # This will update SDR with my release level # Query_And_Update_My_CodeLevel(NodeNumber, release_level) # return 1 if it is upgraded sub Query_And_Update_My_CodeLevel { local($NodeNumber, $pgsd_release_level) = @_; # Get the SDR local($my_level)=`SDRGetObjects -q -x GS_Config gs_release_level node_number==$NodeNumber`; if( $? == 0 && defined($my_level) ) { chop($my_level); # chop newline if($my_level == $pgsd_release_level) { # SDR already has my release level. Nothing to do return 0; } # Time to change the SDR entry &get_dce_creds_for_sdr_if_first(); # obtain DCE creds local($new_attr_arg)="gs_release_level=$pgsd_release_level"; `SDRChangeAttrValues GS_Config node_number==$NodeNumber $new_attr_arg`; } else { # Time to add an SDR entry &get_dce_creds_for_sdr_if_first(); # obtain DCE creds local($new_attr_arg)="gs_release_level=$pgsd_release_level"; `SDRCreateObjects GS_Config node_number=$NodeNumber $new_attr_arg`; } return 1; } # #----------------------------------------------------------------- # # Build a hash table (spversions) from 'splst_versions -t' # note: splst_verisons -t does NOT give you the version of the CWS # note: splst_verisons -n 0 DOES give you the version of the CWS # returns %spversions{} sub Build_SP_Versions { local(%spversions); local($a_line, $nodenum, $pssp_level); local(@Versions) = `splst_versions -t`; foreach $a_line (@Versions) { chop($a_line); # trim new-line on each line... ($nodenum, $pssp_level) = split(' ',$a_line); $spversions{$nodenum} = $pssp_level; } return %spversions; } # # GS Release Levels: # 1 for PSSP-2.1 and PSSP-2.2 # 2 for PSSP-2.3 and PSSP-2.4(original) # 3 for PSSP-2.4 update # 4 for PSSP-3.1 and RSCT-1.0 # 5 for PSSP-3.2 PSSP-3.2 or RSCT 1.2 # 6-7 skip # 8 for PSSP-3.4 and RSCT-1.2.1, RSCT-2.1x # 10 for PSSP-3.5 # GS Current Release level sub GSCurrentReleaseLevel { return $CURRENT_RELEASE_LEVEL; } # # Conversion: RSCT level to gs_release_level # sub RSCT_To_GSLevel { local($pssplvl) = $_[0]; if( $pssplvl =~ "PSSP-2.1" ) { return 1; } # LocustGrove if( $pssplvl =~ "PSSP-2.2" ) { return 1; } # LocustGrove if( $pssplvl =~ "PSSP-2.3" ) { return 2; } # Springwood if( $pssplvl =~ "PSSP-2.4" ) { return 2; } # Opus(Original) # 3 for Opus2 if( $pssplvl =~ "PSSP-3.1" ) { return 4; } # Troutbeck if( $pssplvl =~ "PSSP-3.1.1" ) { return 4; } # Troutbeck if( $pssplvl =~ "RSCT-1.0" ) { return 4; } # troutbeck if( $pssplvl =~ "RSCT-1.1" ) { return 4; } # troutbeck if( $pssplvl =~ "RSCT-1.2.1" ) { return 8; } # after mohonk if( $pssplvl =~ "RSCT-1.2.2" ) { return 8; } # after mohonk if( $pssplvl =~ "PSSP-3.2" ) { return 5; } # Mohonk if( $pssplvl =~ "RSCT-1.2" ) { return 5; } # Mohonk if( $pssplvl =~ "PSSP-3.4" ) { return 8; } # After Mohonk if( $pssplvl =~ "PSSP-3.3" ) { return 8; } # After Mohonk if( $pssplvl =~ "RSCT-2.1" ) { return 8; } # After Mohonk if( $pssplvl =~ "RSCT-2.2" ) { return 8; } # After Mohonk return &GSCurrentReleaseLevel(); # unknown, treated as current level } # # Conversion: PSSP level to gs_release_level sub PSSP_To_GSLevel { return RSCT_To_GSLevel(@_); } # # Conversion: GSLevel to PSSP # sub GSLevel_To_PSSP { local($gslvl) = int($_[0]); if( $gslvl == 1 ) { return "PSSP-2.2"; } if( $gslvl == 2 ) { return "PSSP-2.3"; } if( $gslvl == 3 ) { return "PSSP-2.4"; } if( $gslvl == 4 ) { return "PSSP-3.1"; } if( $gslvl == 5 ) { return "PSSP-3.2"; } if( $gslvl == 8 ) { return "PSSP-3.4"; } return "PSSP-3.4"; # unknown } # # Conversion: GSLevel to RSCT # sub GSLevel_To_RSCT { local($gslvl) = int($_[0]); if( $gslvl == 1 ) { return "PSSP-2.2"; } if( $gslvl == 2 ) { return "PSSP-2.3"; } if( $gslvl == 3 ) { return "PSSP-2.4"; } if( $gslvl == 4 ) { return "RSCT-1.0"; } if( $gslvl == 5 ) { return "RSCT-1.2"; } if( $gslvl == 8 ) { return "RSCT-2.2"; } return "RSCT-2.2"; # unknown } # #----------------------------------------------------------------- # # Determination for CLUSTER Steps: # Precondition: # %ct_... already exist # Brief procedure: # NOTE: Until CLUSTER provides the list of nodes, the following # method will be used. # 1. If desired level is known, return and use it. # 2. Choose the minimum level from the %ct_gs_local_level # Note: This function will exit if it hits any error situations. # prototype: Determine_CodeLevel_For_Cluster(NodeNumber, desired_level) sub Determine_CodeLevel_For_Cluster { local($NodeNumber, $desired_level) = @_; # 1. If desired_level is known, return it with the level # if( defined($desired_level) ) { # If $desired level is not "int", convert it to number if(!($desired_level =~ /^[0-9]/)) { $desired_level = &RSCT_To_GSLevel($desired_level); } return $desired_level; } # 2. Choose the lowest levels from %ct_gs_local_level $desired_level = &GSCurrentReleaseLevel(); while( @pair = each %ct_gs_local_level ) { $gs_level = $pair[1]; if($gs_level < $desired_level) { $desired_level = $gs_level; } } # "int" level return $desired_level; } #----------------------------------------------------------------- # Update the release level and build the registry information # Input: NodeNumber, MyReleaseLevel # Output: sub Update_CodeLevel_And_Build_Cluster_Table { local($NodeNumber) = $_[0]; local($MyCodeLevel) = $_[1]; # # use: ctgrpsvcs_sr_process -c -u= -l # local($cmd) = "/opt/rsct/bin/ctgrpsvcs_sr_process -c " . "-u $NodeNumber=$PGSD_RELEASE_LEVEL -l |"; if(!open(GS_CT_FILE,$cmd)) { local($msg) = "Unable to run ctgrpsvcs_sr_process. $!"; &TSprint( TRACE_FYI, "$msg\n"); printf STDERR "$msg\n"; &fcstart_error(__LINE__,$msg); exit 1; } # read the file and parse local($line); local($tokens); # tokenized line while( $line = ) { @tokens = split(" ", $line); next if !defined(@tokens); # discard empty line next if scalar(@tokens) < 1; # discard empty line next if ($tokens[0] =~ /^#/); # discard a comment line #print "OUTPUT: ", join(" ", @tokens), "\n"; if($tokens[0] =~ /CLUSTER/) { # cluster info } elsif($tokens[0] =~ /NODE/ ) { # node info: Node Version ... $ct_gs_node_level{$tokens[1]} = $tokens[2]; } elsif($tokens[0] =~ /GS_PRIORITY/) { # FixPri, PriVal $ct_gs_pri_fixed = $tokens[1]; $ct_gs_pri_value = $tokens[2]; } elsif($tokens[0] =~ /GLOBAL_CFG/) { # LogLength, LogDirSize $ct_gs_global_logLength = $tokens[1]; $ct_gs_global_logDirSize = $tokens[1]; } elsif($tokens[0] =~ /LOCAL_CFG/) { local($node) = $tokens[1]; $ct_gs_local_level{$node} = $tokens[2]; $ct_gs_local_logLength{$node} = $tokens[3]; $ct_gs_local_logDirSize{$node} = $tokens[4]; } else { # unknown line. Ignore } } close(GS_CT_FILE); } # # ######################################################################### # # # Function: security_mode # # Description: Use lsauthpts to determine authentication method used # # in current partition. # # # ######################################################################### sub get_security_mode { # The "lsauthpts -c" command will return a list of colon delimited # methods. Defect 49774, issue number 1. local($authpts)=`/usr/lpp/ssp/bin/lsauthpts -c`; if($? != 0) { return ""; # lsauthpts failed } else { chomp($authpts); # truncate '\n' return $authpts; } } # # # Get dce credentials - dsrvtgt checks for dce before obtaining creds. # Use ssp/spbgroot pricipal for write access to SDR. sub get_dce_creds_for_sdr_if_first { if(defined($Already_DCE_Creds_Obtained) && ($Already_DCE_Creds_Obtained != 0) ) { # Already cred obtained return; } if( ! -e "/usr/lpp/ssp/bin/dsrvtgt" ) { # 56941 - File not exist. Assume no security &TSprint(TRACE_FYI, "/usr/lpp/ssp/bin/dsrvtgt not exist. Assume no security\n"); return; } local($authpts) = get_security_mode(); if( $authpts eq "dce" ) { $old_krb5=$ENV{'KRB5CCNAME'}; local($tmpname)=`/usr/lpp/ssp/bin/dsrvtgt ssp/spbgroot`; local($rc) = $?; chomp($tmpname); # truncate '\n' if( $rc != 0 || $tmpname eq "" ) { local($msg)="Unable to get DCE credentials for SDR update. $!"; &TSprint( TRACE_FYI, "$msg\n"); &print_message("EMsg_Cannot_get_dce_creds", "hagsd"); &fcstart_error(__LINE__,"$msg"); exit 1; } else { $Already_DCE_Creds_Obtained = 1; $ENV{'KRB5CCNAME'}=$tmpname; &TSprint(TRACE_FYI, "KRB5CCNAME=$ENV{'KRB5CCNAME'}\n"); } } } # Remove any DCE credentials file we may have obtained. # Return KRB5CCNAME to original value. sub remove_dce_creds_for_sdr_if_exist { if(!defined($Already_DCE_Creds_Obtained) || ($Already_DCE_Creds_Obtained == 0) ) { # Creds not obtained return; } $Already_DCE_Creds_Obtained = 0; `/bin/kdestroy >/dev/null 2>&1`; $ENV{'KRB5CCNAME'}=$old_krb5; } # # print_message(list) sub print_message { local(@args) = @_; local($MSGCMD)="/opt/rsct/bin/hadspmsg hagsctrl ha_gs.cat"; local($MSGOUT)=`$MSGCMD @args`; printf STDERR $MSGOUT; } # # start_error(lineno, msg) # sub fcstart_error { local($lineno) = $_[0]; local($tmsg) = $_[1]; if(!defined($lineno) || $lineno eq "") { $lineno = "0"; } local($cmd) = "/opt/rsct/bin/fclogerr -x ALPHA -y 100 ". "-d \"$tmsg\" -p $lineno -s $0 -v 1.76 -l RSCT ". "-r $subsys -t ERRID_GS_STARTERR_ER -e FFDC_ERROR ". "-i /opt/rsct/include/ha_gs.err.S.h ". "-b \"hagsp: $tmsg\""; `$cmd`; } # # wait_till_dms_loaded(): in an AIX environment, wait until the DMS kernel # module is loaded. This is needed because, as long as the DMS is not # loaded, the GS daemon cannot start. And worse, failing to load because of # missing symbols causes perl to quit execution even if there are statements # following 'exec'. # sub wait_till_dms_loaded { my ($query_output); my ($cnt); my ($rc); # if not AIX then just return if ("$OSname" eq "AIX") { $query_output = `$RSCT_BIN/haDMS/haDMS_query`; if ($? != 0) { &TSprint( TRACE_FYI, "DMS kernel extension not loaded or has an older version.\n"); for_cnt: for($cnt = 0; $cnt < 10; $cnt++) { sleep(10); $query_output = `$RSCT_BIN/haDMS/haDMS_query`; $rc = $?; if($rc == 0) { # success: quit waiting last for_cnt; } &TSprint( TRACE_FYI, "DMS kernel extension still not loaded.\n"); } # for } # if haDMS_query else { &TSprint( TRACE_FYI, "DMS kernel extension is loaded.\n"); } } # AIX }