#! /usr/bin/perl # IBM_PROLOG_BEGIN_TAG # This is an automatically generated prolog. # # # # Licensed Materials - Property of IBM # # (C) COPYRIGHT International Business Machines Corp. 2000,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 = "@(#)04 1.45 src/rsct/pgs/cmds/cluster/cthagsp.perl, gsctrl, rsct_rady, rady2035a 11/12/15 16:44:49" #ARGS: -s ${SUBSYS} -p ${CLUSTER_ID_OR_NAME} -d ${DFLT_LOG} # put the include path unshift(@INC, '/opt/rsct/bin'); unshift(@INC, '/opt/rsct/bin'); # require "/opt/rsct/bin/hagsSetup.pkg"; use POSIX; # for uname() ($dir,$progname) = $0 =~ /(.*\/)?(.*)/; # get basename of $0 &TSprint( TRACE_FYI, "$0 $#ARGV $[ input arguments: @ARGV\n" ); sub Usage { warn "Usage: $progname -s -p [-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) { $CLUSTER_ID_OR_NAME = shift @ARGV; } elsif ("-d" eq $arg) { $DefLogName = shift @ARGV; } else { &Usage; } } # ($SYS_SYSNAME, $SYS_NODENAME, $SYS_RELEASE, $SYS_VERSION, $SYS_MACHINE) = POSIX::uname(); $RSCT_ROOT = "/opt/rsct"; $RSCT_BIN = "$RSCT_ROOT/bin"; $Perl = $ENV{'PGSD_PERL'}; if( "" eq $Perl) { $Perl="/usr/bin/perl"; } $daemon = $ENV{'PGSD_DAEMON'}; &TSprint(TRACE_FYI, "subsys=$SUBSYS, ClusterName=$CLUSTER_ID_OR_NAME, ". "DefLogName=$DefLogName, Perl=$Perl\n" ); if (!defined $ENV{'PGSD_TID_DIR'}) { $TidDir = "/var/ct/$CLUSTER_ID_OR_NAME/lck/$SUBSYS/$SUBSYS.tid"; $ENV{'PGSD_TID_DIR'} = $TidDir; } 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" ); # # Get the node number $NodeNumber = $ENV{'PGSD_NODE_NUMBER'}; &TSprint( TRACE_FYI, "PGSD_NODE_NUMBER=$NodeNumber\n" ); # Get HATS/HAGS info ----------------------------------- my @lines = (); my %tsInfo = (); my %gsInfo = (); $HATSINFOCMD="/opt/rsct/bin/ct_hats_info"; if(open(INFOCMDFILE, "${HATSINFOCMD} |")) { #refresh @lines = ; %tsInfo = &trans_list_to_hash(@lines); close INFOCMDFILE; } $HAGSINFOCMD="/opt/rsct/bin/ct_hags_info"; if(open(INFOCMDFILE, "${HAGSINFOCMD} -l |")) { #norefresh @lines = ; %gsInfo = &trans_list_to_hash(@lines); close INFOCMDFILE; } my $GSRealm = $gsInfo{"REALM"}; # Find the domain envrionmane my $GSDomEnv = $gsInfo{"ENVIRONMENT"}; if(!defined($GSDomEnv)) { $GSDomEnv = "cfgmgr"; } if($GSDomEnv eq "gpfs") { $ENV{"PGS_ENVIRONMENT"} = "GPFS"; } elsif($GSDomEnv eq "scaffold") { $ENV{"PGS_ENVIRONMENT"} = "SCAFFOLD"; } elsif($GSDomEnv eq "cfgmgr" || $GSDomEnv eq "configrm") { $ENV{"PGS_ENVIRONMENT"} = "CFGMGR"; } else { # assume it is under "ConfigRM" $ENV{"PGS_ENVIRONMENT"} = "CFGMGR"; $GSDomEnv = "cfgmgr"; } # # HAGS or RPD_RSCT level if( $GSDomEnv eq "cfgmgr" || $GSDomEnv eq "configrm" ) { # RPD active version will be obtained inside the daemon (including in CAA). # Therefore, here ct_clusterinfo -v is not needed #.local($rsct_ver) = `/opt/rsct/bin/ct_clusterinfo -v`; #.$rc = $?; #.if($rc == 0) { #. chomp($rsct_ver); #. $ENV{"RPD_ACTIVE_RSCT_VERSION"} = $rsct_ver; #. &TSprint(TRACE_FYI, "RPD_ACTIVE_RSCT_VERSION=$rsct_ver\n"); #.} else { #. local($msg) = " cannot obtain RPD_ACTIVE_VERSION. RC=$rc."; #. &TSprint( TRACE_FYI, "$msg\n"); #.} $ENV{"PGSD_MIXEDLEVEL"} = "1"; } else { # need RELEASE_LEVEL # Note: The level must be to the proper daemon level here # to ensure the proper behaviour of daemons $ENV{'PGSD_CODELEVEL'} = &GSCurrentReleaseLevel(); &TSprint( TRACE_FYI, "PGSD_CODELEVEL=$ENV{'PGSD_CODELEVEL'}\n" ); $ENV{'PGSD_MIXEDLEVEL'} = "yes"; # Yup, we've got mixed levels. } if (!defined( $ENV{'PGSD_ALLOW_MIXING'} ) ) { if($GSDomEnv eq "gpfs" || $GSDomEnv eq "scaffold") { $ENV{'PGSD_ALLOW_MIXING'} = "yes"; } else { # ConfigRM..Donot Allow mixed level $ENV{'PGSD_ALLOW_MIXING'} = "no"; } } # &TSprint( TRACE_FYI, "PGSD_MIXEDLEVEL=$ENV{'PGSD_MIXEDLEVEL'}\n" ); &TSprint( TRACE_FYI, "PGSD_ALLOW_MIXING=$ENV{'PGSD_ALLOW_MIXING'}\n" ); # # To disable "domain control delegation(eg merge)" (for the testing) #..$ENV{"PGSD_DONT_DELEGATE_DOMAIN_CONTROL"} = "1"; # Set the fixed priority value of the daemon. If not set, it won't setpri # Logsize control $GSLogLength = $ENV{'PGSD_LOGSIZE'}; if( !defined($GSLogLength) ) { $GSLogLength = $gsInfo{"LOGFILELEN"}; } # GSLogLength is in no of lines if( !defined($GSLogLength) || ($GSLogLength < 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 as 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; $ENV{'CT_TR_FILE_SIZE'} = $GSLogSize; # Log Dir Size in KB $GSLogDirSize=$ENV{'PGSD_REAP_SIZE'}; if(!defined($GSLogDirSize)) { $GSLogDirSize = $gsInfo{"LOGDIRSIZE"}; } if( !defined($GSLogDirSize) || ($GSLogDirSize < 3000) ) { $GSLogDirSize = 5000; # in MB } $ENV{'PGSD_LOGDIRSIZE'} = $GSLogDirSize; # Fixed priority? my $TSpriority = $tsInfo{"FIXED_PRI"}; my $GSpriority = 0; # Defect 146172: save the TS priority as default my $GSDefaultPriority = 0; $GSDefaultPriority = `/opt/rsct/bin/cthats -V PRIO`; $ENV{'GSDEFAULT_PRIORITY'} = $GSDefaultPriority; if( defined($TSpriority) ) { # $TSpriority == 0 means "no fixed priority" (PGSD_FIXED_PRIORITY env # variable is not set) if($TSpriority != 0) { if($TSpriority < 0) { $TSpriority = `/opt/rsct/bin/cthats -V PRIO`; if($? != 0) { $TSpriority = -1; } } if($TSpriority >= 0) { my $osname=`/bin/uname`; if( $osname =~ "AIX" ) { $GSpriority = $TSpriority + 1; } else { $GSpriority = $TSpriority - 1; } } $ENV{'PGSD_FIXED_PRIORITY'} = $GSpriority; } } &TSprint( TRACE_FYI, "PGSD_FIXED_PRIORITY=$GSpriority\n" ); &TSprint( TRACE_FYI, "CT_TR_FILE_SIZE=$ENV{'CT_TR_FILE_SIZE'}\n" ); &TSprint( TRACE_FYI, "PGSD_LOGDIRSIZE=$ENV{'PGSD_LOGDIRSIZE'}\n" ); # # # 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.$CLUSTER_ID_OR_NAME"; $ENV{'PGSD_MARKFILE'} = $MarkFile; &TSprint( TRACE_FYI, "PGSD_MARKFILE=$ENV{'PGSD_MARKFILE'}\n" ); # #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"); # # $IncarnationNumber = &GetInstance( $TidDir, "${SUBSYS}d" ); $ENV{'PGSD_INCARNATION_NUMBER'} = $IncarnationNumber; &TSprint( TRACE_FYI, "PGSD_INCARNATION_NUMBER=$ENV{'PGSD_INCARNATION_NUMBER'}\n" ); # # # # set PGSD_LOGFILE local($LogDir) = $ENV{'PGSD_LOGDIR'}; if(!defined($LogDir)) { $LogDir = "/var/ct/$CLUSTER_ID_OR_NAME/log/$SUBSYS"; $ENV{'PGSD_LOGDIR'} = $LogDir; } $TRACEFILEPREFIX="trace"; 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'}) { $RunDir = "/var/ct/$CLUSTER_ID_OR_NAME/run/$SUBSYS"; $ENV{'PGSD_RUN_DIR'} = $RunDir; } # 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" ); # 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. # 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 ); if ("" ne $DefLogName) { # We want to save the messages in the default log. # so, we rename it to the log name, and daemon appends to the log! local($newDefLogName) = "$DefLogName.${NodeNumber}_$IncarnationNumber"; &TSprint(TRACE_FYI, "rename $DefLogName to $newDefLogName\n"); rename($DefLogName, $newDefLogName) || warn "rename of $DefLogName to $newDefLogName failed!: $!\n"; eval `chmod 0644 $newDefLogName`; } # 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 = $GSLogDirSize * 1024; # in Bytes if( "" eq $DefLogName ) { $DefLogName="/var/ct/$CLUSTER_ID_OR_NAME/log/$SUBSYS/$SUBSYS.default"; #$DefLogName="/var/ct/$CLUSTER_ID_OR_NAME/log/$SUBSYS/$TRACEFILEPREFIX.default"; } $DefLogNameParameter = "-d $DefLogName"; # $cmd = "$Perl -S hagsreap $DefLogNameParameter $SUBSYS $CLUSTER_ID_OR_NAME ". " $Command $sizeLimit $NodeNumber " . " $ENV{'PGSD_LOGDIR'} $ENV{'PGSD_RUN_DIR'}"; # select STDOUT; $|=1; # disable buffering &TSprint( TRACE_FYI, "cmd = $cmd.\n" ); if (0 == ($kidPid = fork)) { exec $cmd; die "Couldn't exec hagsreap: $!\n"; } select STDOUT; $|=0; # enable buffering # # Remove the client socket directory # (/var/ct//soc/ctgrpsvcs/clients.) on CLUSTER # $client_dir = "/var/ct/$CLUSTER_ID_OR_NAME/soc/$SUBSYS/$SUBSYS.clients"; 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 # Set the envrionment variables for HA stacks # if (!defined( $ENV{'PGSD_PRM_SOCK'} ) ) { $ENV{'PGSD_PRM_SOCK'} = "$SUBSYS"; } &TSprint( TRACE_FYI, "PGSD_PRM_SOCK=$ENV{'PGSD_PRM_SOCK'}\n"); if (!defined( $ENV{'PGSD_SUPP_SOCK'} ) ) { $ENV{'PGSD_SUPP_SOCK'} = "/var/ct/$CLUSTER_ID_OR_NAME/soc/${SUBSYS}/${SUBSYS}dsocket"; } &TSprint( TRACE_FYI, "PGSD_SUPP_SOCK=$ENV{'PGSD_SUPP_SOCK'}\n"); # remove pgsd socket (so that client can get errno=ENO rather ECONNREFUSED) # 145396 - (esp. Interix), socket file can be deleted cleanly via 'unlink()' (in hagsd) #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'} ) ) { $ENV{'HB_SERVER_SOCKET'} = "/var/ct/$CLUSTER_ID_OR_NAME/soc/cthats/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. $ENV{'PGSD_NEW_FUNCTION'} = "unsafe"; &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; # Now that the GS daemon has an auxiliary thread, make sure that the # system scope threads are used $ENV{"AIXTHREAD_SCOPE"} = "S"; # Load the DMS if needed load_dms_CAA_env(); wait_till_dms_loaded(); # # run the daemon $cmd = "$daemon $SUBSYS @ARGV"; &TSprint( TRACE_FYI, "cmd = $cmd\n" ); exec $cmd; die "exec $cmd failed!: $!\n"; #----------------------------------------------------------------- # # GS Current Release level sub GSCurrentReleaseLevel { $CURRENT_MIN_RELEASE_LEVEL = 10; return $CURRENT_MIN_RELEASE_LEVEL; } # # 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.45 -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`; } # # # Input: list of a pair of and # eg: K1 V1 # K2 V2 # Output: hash table with pairs of {K}={V} sub trans_list_to_hash { my @lines = @_; my %outtbl = (); foreach $aline (@lines) { chomp($aline); my @kv=split(" ",$aline); $outtbl{$kv[0]} = join(" ",@kv[1..$#kv]); } return %outtbl; } # # load_dms(): Check if the Dead Man Switch module is loaded. Load it if it is # not loaded or the loaded DMS version is too old. # # Currently, DMS is only supported in AIX. Also note that the DMS should only # get loaded in the CAA environment, since HATS is responsible for loading # it in the non-CAA environment. # sub load_dms { my ($query_output); my ($load_dms_output); if ("$SYS_SYSNAME" eq "AIX") { &TSprint( TRACE_FYI, "---- Performing DMS kernel extension check ----\n"); &TSprint( TRACE_FYI, " (errors that follow are not critical)\n"); $query_output = `$RSCT_BIN/haDMS/haDMS_query`; if ($? != 0) { &TSprint( TRACE_FYI, "DMS kernel extension not loaded or has an older version.\n"); &TSprint( TRACE_FYI, "---- Initial check completed ----\n"); &TSprint( TRACE_FYI, "Loading DMS kernel extension.\n"); $load_dms_output = `$RSCT_BIN/haDMS/haDMS_load $RSCT_BIN/haDMS/haDMS_kex 2>&1`; if ($? != 0) { &TSprint( TRACE_ERROR, "$load_dms_output"); &TSprint( TRACE_FYI, "Startup will continue in case DMS module gets loaded by other means.\n"); # In principle, failing to load the DMS module is a fatal # error, since hagsd may be unable to load to missing # symbols. But the script will be allowed to continue, in # case the DMS module gets loaded by other means. } } else { &TSprint( TRACE_FYI, "$query_output"); &TSprint( TRACE_FYI, "No need to load DMS kernel extension: already loaded.\n"); &TSprint( TRACE_FYI, "---- Initial check completed ----\n"); } } } # # load_dms_CAA_env(): load the DMS in the CAA environment (since HATS will # not do it for us) # sub load_dms_CAA_env() { my ($realm); $realm = `$RSCT_BIN/ct_clusterinfo -r`; chomp($realm); if($realm eq "CAA") { load_dms(); } else { &TSprint( TRACE_FYI, "Not CAA environment: not loading DMS\n"); } } # # 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 ("$SYS_SYSNAME" 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 }