Example: Application exit program

This code example contains an application cluster resource group exit program.

You can find this code example in the QUSRTOOL library.

Note: By using the code examples, you agree to the terms of the Code license and disclaimer information.
/***************************************************************************/
/*                                                                         */
/* Library:  QUSRTOOL                                                      */
/* File:     QATTSYSC                                                      */
/* Member:   TCSTAPPEXT                                                    */
/* Type:     ILE C                                                         */
/*                                                                         */
/* Description:                                                            */
/* This is an example application CRG exit program which gets called for   */
/* various cluster events or cluster APIs.  The bulk of the logic must     */
/* still be added because that logic is really dependent upon the unique   */
/* things that need to be done for a particular application.               */
/*                                                                         */
/* The intent of this example to to provide a shell which contains the     */
/* basics for building a CRG exit program.  Comments throughout the example*/
/* highlight the kinds of issues that need to be addressed by the real     */
/* exit program implementation.                                            */
/*                                                                         */
/* Every action code that applies to an application CRG is handled in this */
/* example.                                                                */
/*                                                                         */
/* The tcstdtaara.h include is also shipped in the QUSRTOOL library.  See  */
/* the TCSTDTAARA member in the QATTSYSC file.                             */
/*                                                                         */
/* Change log:                                                             */
/* Flag Reason   Ver    Date   User Id   Description                       */
/* ____ ________ ______ ______ ___________________________________________ */
/* ...  D98332   v5r1m0 000509 ROCH     Initial creation.                  */
/* $A1  P9950070 v5r2m0 010710 ROCH     Dataarea fixes                     */
/* $A2  D99055   v5r2m0 010913 ROCH     Added CancelFailover action code   */
/* $A3  D98854   v5r2m0 010913 ROCH     Added VerificationPhase action code*/
/* $A4  P9A10488 v5r3m0 020524 ROCH     Added example code to wait for data*/
/*                                      CRGs on switchover action code     */
/*                                                                         */
/***************************************************************************/


/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Header files                                                            */
/*                                                                         */
/*-------------------------------------------------------------------------*/
#include              /* Useful when debugging                   */
#include             /* offsetof macro                           */
#include             /* system function                          */
#include             /* String functions                         */
#include             /* Exception handling constants/structures  */
#include               /* Various cluster constants               */
#include           /* Structure of CRG information                */
#include "qusrtool/qattsysc/tcstdtaara" /* QCSTHAAPPI/QCSTHAAPPO data areas*/
#include           /* API to Retrieve contents of a data area     */
#include              /* API error code type definition           */
#include              /* mitime builtin                           */
#include           /* waittime builtin                            */ 


/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Constants                                                               */
/*                                                                         */
/*-------------------------------------------------------------------------*/
#define UnknownRole -999
#define DependCrgDataArea "QCSTHAAPPO"
#define ApplCrgDataArea "QCSTHAAPPI"
#define Nulls 0x00000000000000000000


/*-------------------------------------------------------------------------*/
/*                                                                         */
/* The following constants are used in the checkDependCrgDataArea()        */
/* function.  The first defines how long to sleep before checking the data */
/* area.  The second defines that maximum time to wait for the data area   */
/* to become ready before failing to start the application when the Start  */
/* CRG function is being run.  The third defines the maximum wait time for */
/* the Initiate Switchover or failover functions.                          */
/*                                                                         */
/*-------------------------------------------------------------------------*/
#define WaitSecondsIncrement 30
#define MaxStartCrgWaitSeconds 0
#define MaxWaitSeconds 900 

/*-------------------------------------------------------------------------*/
/*                                                                         */
/* As this exit program is updated to handle new action codes, change the  */
/* define below to the value of the highest numbered action code that is   */
/* handled.                                                                */
/*                                                                         */
/*-------------------------------------------------------------------------*/
#define MaxAc 21

/*-------------------------------------------------------------------------*/
/*                                                                         */
/* If the exit program data in the CRG has a particular structure to it,   */
/* include the header file for that structure definition and change the    */
/* define below to use that structure name rather than char.               */
/*                                                                         */
/*-------------------------------------------------------------------------*/
#define EpData char

/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Change the following define to the library the application resides in   */
/* and thus where the QCSTHAAPPO and QCSTHAAPPI data areas will be found.  */
/*                                                                         */
/*-------------------------------------------------------------------------*/
#define ApplLib "QGPL" 


/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Prototypes for internal functions.                                      */
/*                                                                         */
/*-------------------------------------------------------------------------*/
static int getMyRole(Qcst_EXTP0100_t *, int, int);
#pragma argopt(getMyRole)
static int doAction(int, int, int, Qcst_EXTP0100_t *, EpData *);
#pragma argopt(doAction)
static int createCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int startCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int restartCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int endCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int verifyPhase(int, int, Qcst_EXTP0100_t *, EpData *);
static int deleteCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int memberIsJoining(int, int, Qcst_EXTP0100_t *, EpData *);
static int memberIsLeaving(int, int, Qcst_EXTP0100_t *, EpData *);
static int switchPrimary(int, int, Qcst_EXTP0100_t *, EpData *);
static int addNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int rmvNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int chgCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int deleteCrgWithCmd(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoPriorAction(int, int, Qcst_EXTP0100_t *, EpData *);
static int endNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int chgNodeStatus(int, int, Qcst_EXTP0100_t *, EpData *);
static int cancelFailover(int, int, Qcst_EXTP0100_t *, EpData *);
static int newActionCode(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoCreateCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoStartCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoEndCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoMemberIsJoining(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoMemberIsLeaving(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoSwitchPrimary(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoAddNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoRmvNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoChgCrg(int, int, Qcst_EXTP0100_t *, EpData *);  
static int undoCancelFailover(int, int, Qcst_EXTP0100_t *, EpData *);  
static void bldDataAreaName(char *, char *, char *);
#pragma argopt(bldDataAreaName)
static int checkDependCrgDataArea(unsigned int);
#pragma argopt(checkDependCrgDataArea)
static void setApplCrgDataArea(char);
#pragma argopt(setApplCrgDataArea)
static void cancelHandler(_CNL_Hndlr_Parms_T *);
static void unexpectedExceptionHandler(_INTRPT_Hndlr_Parms_T *);
static void endApplication(unsigned int, int, int, Qcst_EXTP0100_t *, EpData *);
#pragma argopt(endApplication)

/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Some debug routines                                                     */
/*                                                                         */
/*-------------------------------------------------------------------------*/
static void printParms(int, int, int, Qcst_EXTP0100_t *, EpData *);
static void printActionCode(unsigned int);
static void printCrgStatus(int);
static void printRcvyDomain(char *,
                            unsigned int,
                            Qcst_Rcvy_Domain_Array1_t *);
static void printStr(char *, char *, unsigned int);


/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Type definitions                                                        */
/*                                                                         */
/*-------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*/
/*                                                                         */
/* This structure defines data that will be passed to the exception and    */
/* cancel handlers.  Extend it with information unique to your application.*/
/*                                                                         */
/*-------------------------------------------------------------------------*/
typedef struct {
  int *retCode;             /* Pointer to return code                      */
  EpData *epData;           /* Exit program data from the CRG              */
  Qcst_EXTP0100_t *crgData; /* CRG data                                    */
  unsigned int actionCode;  /* The action code                             */
  int role;                 /* This node's recovery domain role            */
  int priorRole;            /* This node's prior recovery domainrole       */
} volatile HandlerDataT;


/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Function pointer array for handling action codes.  When the exit program*/
/* is updated to handle new action codes, add the new function names to    */
/* this function pointer array.                                            */
/*                                                                         */
/*-------------------------------------------------------------------------*/
static int (*fcn[MaxAc+1]) (int role,
                            int priorRole,
                            Qcst_EXTP0100_t *crgData,
                            EpData *epData) = {
  newActionCode,   /* 0 - currently reserved */
  createCrg,       /* 1  */
  startCrg,        /* 2  */
  restartCrg,      /* 3  */
  endCrg,          /* 4  */ 
  verifyPhase,     /* 5 - currently reserved */
  newActionCode,   /* 6 - currently reserved */
  deleteCrg,       /* 7  */
  memberIsJoining, /* 8  */
  memberIsLeaving, /* 9  */
  switchPrimary,   /* 10 */
  addNode,         /* 11 */
  rmvNode,         /* 12 */
  chgCrg,          /* 13 */
  deleteCrgWithCmd,/* 14 */
  undoPriorAction, /* 15 */ 
  endNode,         /* 16 */ 
  newActionCode,   /* 17 - applies only to a device CRG */  
  newActionCode,   /* 18 - applies only to a device CRG */
  newActionCode,   /* 19 - applies only to a device CRG */
  chgNodeStatus,   /* 20 */
  cancelFailover   /* 21 */ 
};


/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Function pointer array for handling prior action codes when called with */
/* the Undo action code.  When the exit program is updated to handle       */
/* Undo for new action codes, add the new function names to this function  */
/* pointer array.                                                          */
/*                                                                         */
/*-------------------------------------------------------------------------*/
static int (*undoFcn[MaxAc+1]) (int role,
                                int priorRole,
                                Qcst_EXTP0100_t *crgData,
                                EpData *epData) = {
  newActionCode,       /* 0 - currently reserved */
  undoCreateCrg,       /* 1  */
  undoStartCrg,        /* 2  */
  newActionCode,       /* 3  */
  undoEndCrg,          /* 4  */ 
  newActionCode,       /* 5 - no undo for this action code */
  newActionCode,       /* 6 - currently reserved */
  newActionCode,       /* 7  */
  undoMemberIsJoining, /* 8  */
  undoMemberIsLeaving, /* 9  */
  undoSwitchPrimary,   /* 10 */
  undoAddNode,         /* 11 */
  undoRmvNode,         /* 12 */
  undoChgCrg,          /* 13 */
  newActionCode,       /* 14 */
  newActionCode,       /* 15 */
  newActionCode,       /* 16 */
  newActionCode,       /* 17 - applies only to a device CRG */  
  newActionCode,       /* 18 - applies only to a device CRG */
  newActionCode,       /* 19 - applies only to a device CRG */
  newActionCode,       /* 20 */
  undoCancelFailover   /* 21 */
};



/***************************************************************************/
/*                                                                         */
/* This is the entry point for the exit program.                           */
/*                                                                         */
/***************************************************************************/
void main(int argc, char *argv[]) {

  HandlerDataT hdlData;

 
/*----------------------------------------------------------------------- */
  /*                                                                      */
  /* Take each of the arguments passed in the argv array and castit to    */
  /* the correct data type.                                               */
  /*                                                                      */
 
/*----------------------------------------------------------------------- */
  int *retCode    = (int *)argv[1];
  unsigned int *actionCode = (unsigned int *)argv[2];
  EpData *epData           = (EpData *)argv[3];
  Qcst_EXTP0100_t *crgData = (Qcst_EXTP0100_t *)argv[4];
  char *formatName         = (char *)argv[5];

 
/*-----------------------------------------------------------------------*/
  /*                                                                      */
  /* Ensure the format of the data being passed is correct.  */
  /* If not, a change has been made and this exit program needs tobe      */
  /* updated to accomodate the change.  Add appropriate errorlogging for  */
  /* your application design.                                             */
  /*                                                                      */
 
/*-----------------------------------------------------------------------*/
  if (0 != memcmp(formatName, "EXTP0100", 8))
    abort();

 
/*-----------------------------------------------------------------------*/
  /*                                                                      */
  /* Set up the data that will be passed to the exception andcancel       */
  /* handlers.                                                            */
  /*                                                                      */
 
/*-----------------------------------------------------------------------*/
  hdlData.retCode    = retCode;
  hdlData.epData     = epData;
  hdlData.crgData    = crgData;
  hdlData.actionCode = *actionCode;
  hdlData.role       = UnknownRole;
  hdlData.priorRole  = UnknownRole;
  _VBDY();  /* force changed variables to home storage location           */

 
/*-----------------------------------------------------------------------*/
  /*                                                                      */
  /* Enable an exception handler for any and all exceptions.              */
  /*                                                                      */
 
/*-----------------------------------------------------------------------*/
#pragma exception_handler(unexpectedExceptionHandler, hdlData, \
                          _C1_ALL, _C2_ALL, _CTLA_INVOKE )

 
/*-----------------------------------------------------------------------*/
  /*                                                                      */
  /* Enable a cancel handler to recover if this job is canceled.         */
  /*                                                                      */
 
/*-----------------------------------------------------------------------*/
#pragma cancel_handler(cancelHandler, hdlData)

 
/*-----------------------------------------------------------------------*/
  /*                                                                      */
  /* Extract the role and prior role of the node this exit program is      */
  /* running on.  If the cluster API or event changes the recovery domain  */
  /* (node role or membership status), the new recovery domain's offset is */
  /* passed in Offset_Rcvy_Domain_Array and the offset of the recovery     */
  /* domain as it looked prior to the API or cluster event is passed in    */
  /* Offset_Prior_Rcvy_Domain_Array.  If the recovery domain isn't changed,*/
  /* only Offset_Rcvy_Domain_Array can be used to address the recovery     */
  /* domain.                                                               */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  hdlData.role = getMyRole(crgData,
                           crgData->Offset_Rcvy_Domain_Array,
                           crgData->Number_Nodes_Rcvy_Domain);
  if (crgData->Offset_Prior_Rcvy_Domain_Array)
    hdlData.priorRole = 
                 getMyRole(crgData,
                          
crgData->Offset_Prior_Rcvy_Domain_Array,
                          
crgData->Number_Nodes_Prior_Rcvy_Domain);
  else
    hdlData.priorRole = hdlData.role;
  _VBDY();  /* force changed variables to home storage location           */  

 
/*-----------------------------------------------------------------------*/
  /*                                                                      */
  /* Enable the following to print out debug information.                 */
  /*                                                                      */
 
/*-----------------------------------------------------------------------*/
  /*
  printParms(*actionCode, hdlData.role, hdlData.priorRole, crgData,
epData);
  */

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Do the correct thing based upon the action code.  The return code     */
  /* is set to the function result of doAction().                          */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  *retCode = doAction(*actionCode,
                      hdlData.role,
                      hdlData.priorRole,
                      crgData,
                      epData);

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* The exit program job will end when control returns to the operating   */
  /* system at this point.                                                 */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  return;

#pragma disable_handler  /* unexpectedExceptionHandler                     */
#pragma disable_handler  /* cancelHandler                                  */
}  /* end main()                                                           */


/***************************************************************************/
/*                                                                         */
/* Get the role of this particular node from one of the views of the       */
/* recovery domain.                                                        */
/*                                                                         */
/* APIs and cluster events which pass the updated and prior recovery domain*/
/* to the exit program are:                                                */
/* QcstAddNodeToRcvyDomain                                                 */
/* QcstChangeClusterNodeEntry                                              */
/* QcstChangeClusterResourceGroup                                          */
/* QcstEndClusterNode (ending node does not get the prior domain)          */
/* QcstInitiateSwitchOver                                                  */
/* QcstRemoveClusterNodeEntry (removed node does not get the prior domain) */
/* QcstRemoveNodeFromRcvyDomain                                            */
/* QcstStartClusterResourceGroup (only if inactive backup nodes are        */
/*                                reordered)                               */
/* a failure causing failover                                              */
/* a node rejoining the cluster                                            */
/* cluster partitions merging                                              */
/*                                                                         */
/* All other APIs pass only the updated recovery domain.                   */
/*                                                                         */
/***************************************************************************/
static int getMyRole(Qcst_EXTP0100_t *crgData, int offset, int
count) {

  Qcst_Rcvy_Domain_Array1_t *nodeData;
  unsigned int iter = 0;

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Under some circumstances, the operating system may not be able to     */
  /* determine the ID of this node and passes *NONE.  An example of such a */
  /* circumstance is when cluster resource services is not active on a     */
  /* node and the DLTCRG CL command is used.                               */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  if (0 == memcmp(crgData->This_Nodes_ID, QcstNone,
sizeof(Qcst_Node_Id_t)))
    return UnknownRole;

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Compute a pointer to the first element of the recovery domain array.  */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  nodeData = (Qcst_Rcvy_Domain_Array1_t *)((char *)crgData +
offset);

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Find my node in the recovery domain array.  I will not be in the      */
  /* prior recovery domain if I am being added by the Add Node to Recovery */
  /* Domain API.                                                           */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  while (  0 != memcmp(crgData->This_Nodes_ID,
                       nodeData->Node_ID,
                       sizeof(Qcst_Node_Id_t))
         &&
           iter < count
        ) {
    nodeData++;
    iter++;
  }

  if (iter < count)
    return nodeData->Node_Role;
  else
    return UnknownRole;
}  /* end getMyRole()                                                      */


/***************************************************************************/
/*                                                                         */
/* Call the correct function based upon the cluster action code.  The      */
/* doAction() function was split out from main() in order to clarify the   */
/* example.  See the function prologues for each called function for       */
/* information about a particular cluster action.                          */
/*                                                                         */
/* Each action code is split out into a separate function only to help     */
/* clarify this example.  For a particular exit program, some action codes */
/* may perform the same function in which case multiple action codes could */
/* be handled by the same function.                                        */
/*                                                                         */
/***************************************************************************/
static int doAction(int actionCode,
                    int role,
                    int priorRole,
                    Qcst_EXTP0100_t *crgData,
                    EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* For action codes this exit program knows about, call a function to    */   
  /* do the work for that action code.                                     */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/

  if (actionCode <= MaxAc )
    return (*fcn[actionCode]) (role, priorRole, crgData, epData);
  else 
   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* IBM has defined a new action code in a new operating system release */
    /* and this exit program has not yet been updated to handle it.  Take a*/
    /* default action for now.                                             */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    return newActionCode(role, priorRole, crgData, epData);
}  /* end doAction()                                                       */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcInitialize                                       */
/*                                                                         */
/* The QcstCreateClusterResourceGroup API was called.  A new cluster       */
/* resource group object is being created.                                 */
/*                                                                         */
/* Things to consider:                                                     */
/*   - Check that the application program and all associated objects are on*/
/*     the primary and backup nodes.  If the objects are not there,        */
/*     consider sending error/warning messages or return a failure return  */
/*     code.                                                               */
/*   - Check that required data or device CRGs are on all nodes in the     */
/*     recovery domain.                                                    */
/*   - Perform any necessary setup that is required to run the             */
/*     the application on the primary or backup nodes.                     */
/*   - If this CRG is enabled to use the QcstDistributeInformation API,    */
/*     the user queue needed by that API could be created at this time.    */
/*                                                                         */
/***************************************************************************/
static int createCrg(int role,
                     int doesNotApply,
                     Qcst_EXTP0100_t *crgData,
                     EpData *epData) {

  return QcstSuccessful;
}  /* end createCrg()                                                      */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcStart                                            */
/*                                                                         */
/* The QcstStartClusterResourceGroup API was called.  A cluster resource   */
/* group is being started.                                                 */
/* The QcstInitiateSwitchOver API was called and this is the second action */
/* code being passed to the exit program.                                  */
/* The fail over event occurred and this is the second action code being   */
/* passed to the exit program.                                             */
/*                                                                         */
/* A maximum wait time is used when checking to see if all dependent CRGs  */
/* are active.  This is a short time if the CRG is being started because of*/
/* the QcstStartClusterResourceGroup API.  It is a longer time if it is    */
/* because of a failover or switchover.  When failover or switchover are   */
/* being done, it make take a while for data or device CRGs to become      */
/* ready so the wait time is long.  If the Start CRG API is being used, the*/
/* dependent CRGs should already be started or some error occurred, the    */
/* CRGs were started out of order, etc. and there is no need for a long    */
/* wait.                                                                   */
/*                                                                         */
/* Things to consider:                                                     */
/*   - If this node's role is primary, the application should be started.  */
/*     This exit program should either call the application so that it runs*/
/*     in this same job or it should monitor any job started by this       */
/*     exit program so the exit program knows when the application job     */
/*     ends.  By far, the simplest approach is run the application in this */
/*     job by calling it.                                                  */
/*     Cluster Resource Services is not expecting this exit program to     */
/*     return until the application finishes running.                      */
/*   - If necessary, start any associated subsystems, server jobs, etc.    */
/*   - Ensure that required data CRGs have a status of active on all nodes */
/*     in the recovery domain.                                             */
/*                                                                         */
/***************************************************************************/
static int startCrg(int role,
                    int doesNotApply,
                    Qcst_EXTP0100_t *crgData,
                    EpData *epData) {

  unsigned int maxWaitTime;

  /* Start the application if this node is the primary                     */
  if (role == QcstPrimaryNodeRole) {
   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Determine if all CRGs that this application CRG is dependent upon   */
    /* are ready.  If the check fails, return from the Start action code.  */
    /* Cluster Resource Services will change the state of the CRG to       */
    /* Inactive.                                                           */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    if (crgData->Cluster_Resource_Group_Status ==
QcstCrgStartCrgPending)
      maxWaitTime = MaxStartCrgWaitSeconds;
    else
      maxWaitTime = MaxWaitSeconds;
    if (QcstSuccessful != checkDependCrgDataArea(maxWaitTime))
      return QcstSuccessful;


   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Just before starting the application, update the data area to       */
    /* indicate the application is running.                                */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    setApplCrgDataArea(Appl_Running);

   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Add logic to call application here.  It is expected that control    */
    /* will not return until something causes the application to end: a    */
    /* normal return from the exit program, the job is canceled, or an    */
    /* unhandled exception occurs.  See the cancelHandler() function for   */
    /* some common ways this job could be canceled.                       */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/



   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* After the application has ended normally, update the data area to   */
    /* indicate the application is no longer running.                      */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    setApplCrgDataArea(Appl_Ended);
  }
  else
   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* On backup or replicate nodes, mark the status of the application in */
    /* the data area as not running.                                       */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    setApplCrgDataArea(Appl_Ended); 
  

  return QcstSuccessful;
}  /* end startCrg()                                               
       */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcRestart                                          */
/*                                                                         */
/* The previous call of the exit program failed and set the return   */
/* code to QcstFailWithRestart or it failed due to an exception and the    */
/* exception was allowed to percolate up the call stack.  In either  */
/* case, the maximum number of times for restarting the exit program has   */
/* not been reached yet.                                                   */
/*                                                                         */
/* This action code is passed only to application CRG exit programs which  */
/* had been called with the Start action code.                             */
/*                                                                         */
/***************************************************************************/
static int restartCrg(int role,
                      int doesNotApply,
                      Qcst_EXTP0100_t *crgData,
                      EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Perform any unique logic that may be necessary when restarting the    */
  /* application after a failure and then call the startCrg() function to  */
  /* do the start functions.                                               */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/


  return startCrg(role, doesNotApply, crgData, epData);
}  /* end restartCrg()                                                     */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcEnd                                              */
/*                                                                         */
/* The end action code is used for one of the following reasons:           */
/*   - The QcstEndClusterResourceGroup API was called.                     */
/*   - The cluster has become partitioned and this node is in the secondary*/
/*     partition.  The End action code is used regardless of whether the   */
/*     CRG was active or inactive.  Action code dependent data of          */
/*     QcstPartitionFailure will also be passed.                           */
/*   - The application ended.   Action code dependent data of              */
/*     QcstResourceEnd will also be passed.  All nodes in the recovery     */
/*     domain will see the same action code (including the primary).       */
/*   - The CRG job has been canceled.  The exit program on this node will */
/*     be called with the End action code.  QcstMemberFailure will be      */
/*     passed as action code dependent data.                               */
/*                                                                         */
/*                                                                         */
/*                                                                         */
/* Things to consider:                                                     */
/*   - If the CRG is active, the job running the application is canceled  */
/*     and the IP takeover address is ended AFTER the exit program is      */
/*     called.                                                             */
/*   - If subsystems or server jobs were started as a result of the        */
/*     QcstCrgAcStart action code, end them here or consolidate all logic  */
/*     to end the application in the cancelHandler() since it will be      */
/*     invoked for all Cluster Resource Services APIs which must end the   */
/*     application on the current primary.                                 */  
/*                                                                         */
/***************************************************************************/
static int endCrg(int role,
                  int priorRole,
                  Qcst_EXTP0100_t *crgData,
                  EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* End the application if it is running on this node.                    */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  endApplication(QcstCrgAcRemoveNode, role, priorRole, crgData,
epData);

  return QcstSuccessful;
}  /* end endCrg()                                                         */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcVerificationPhase                                */
/*                                                                         */
/* The verification phase action code is used to allow the exit program to */
/* do some verification before proceeding with the requested function      */
/* identified by the action code depended data. If the exit program        */
/* determines that the requested function cannot proceed it should return  */
/* QcstFailWithOutRestart.                                                 */
/*                                                                         */
/*                                                                         */
/* NOTE: The exit program will NOT be called with Undo action code.        */
/*                                                                         */
/***************************************************************************/
static int verifyPhase(int role,
                       int doesNotApply,
                       Qcst_EXTP0100_t *crgData,
                       EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Do verification                                                       */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  if (crgData->Action_Code_Dependent_Data == QcstDltCrg) {
        /* do verification  */
        /* if ( fail ) */
          /* return QcstFailWithOutRestart */
  }

  return QcstSuccessful;
}  /* end verifyPhase()                                                    */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcDelete                                           */
/*                                                                         */
/* The QcstDeleteClusterResourceGroup or QcstDeleteCluster API was called. */
/* A cluster resource group is being deleted while Cluster Resource        */
/* Services is active.                                                     */
/* If the QcstDeleteCluster API was used, action code dependent data of    */
/* QcstDltCluster is passed.                                               */
/* If the QcstDeleteCluster API was used and the CRG is active, the exit   */
/* program job which is still active for the Start action code is canceled*/
/* after the Delete action code is processed.                              */
/*                                                                         */
/* Things to consider:                                                     */
/*   - Delete application programs and objects from nodes where they are   */
/*     no longer needed such as backup nodes.  Care needs to be exercised  */
/*     when deleting application objects just because a CRG is being       */
/*     deleted since a particular scenario may want to leave the           */
/*     application objects on all nodes.                                   */
/*                                                                         */
/***************************************************************************/
static int deleteCrg(int role,
                     int doesNotApply,
                     Qcst_EXTP0100_t *crgData,
                     EpData *epData) {

  return QcstSuccessful;
}  /* end deleteCrg()                                              
       */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcReJoin                                           */
/*                                                                         */
/* One of three things is occurring-                                       */
/*  1. The problem which caused the cluster to become partitioned has been */
/*     corrected and the 2 partitions are merging back together to become  */
/*     a single cluster.  Action code dependent data of QcstMerge will be  */
/*     passed.                                                             */
/*  2. A node which either previously failed or which was ended has had    */
/*     cluster resource services started again and the node is joining the */
/*     cluster.  Action code dependent data of QcstJoin will be passed.    */
/*  3. The CRG job on a particular node which may have been canceled or   */
/*     ended has been restarted.  Action code dependent data of QcstJoin   */
/*     will be passed.                                                     */
/*                                                                         */
/* Things to consider:                                                     */
/*   - If the application replicates application state information to other*/
/*     nodes when the application is running, this state information will  */
/*     need to be resynchronized with the joining nodes if the CRG is      */
/*     active.                                                             */
/*   - Check for missing application objects on the joining nodes.         */
/*   - Ensure the required data CRGs are on the joining nodes.             */
/*   - If the application CRG is active, ensure the required data CRGs are */
/*     active.                                                             */
/*                                                                         */
/***************************************************************************/
static int memberIsJoining(int role,
                           int priorRole,
                           Qcst_EXTP0100_t *crgData,
                           EpData *epData) {

 
/*---------------------------------------------------------------------*/
  /*                                                                     */
  /* Ensure the data area status on this node starts out indicating      */
  /* the application is not running if this node is not the primary.     */
  /*                                                                     */
 
/*---------------------------------------------------------------------*/
  if (role != QcstPrimaryNodeRole) {
    setApplCrgDataArea(Appl_Ended);
  }

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* If a single node is rejoining the cluster, you may do a certain set of*/
  /* actions.  Whereas if the nodes in a cluster which became partitioned  */
  /* are merging back together, you may have a different set of actions.   */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  if (crgData->Action_Code_Dependent_Data == QcstJoin) {
    /* Do actions for a node joining.                                      */
  }
  else {
    /* Do actions for partitions merging.                                  */
  }

  return QcstSuccessful;
}  /* end memberIsJoining()                                                */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcFailover                                         */
/*                                                                         */
/* Cluster resource services on a particular node(s) has failed or ended   */
/* for this cluster resource group.  The Failover action code is passed    */
/* regardless of whether the CRG is active or inactive.  Failover can      */
/* happen for a number of reasons:                                         */
/*                                                                         */
/*   - an operator canceled the CRG job on a node.  Action code dependent */
/*     data of QcstMemberFailure will be passed.                           */
/*   - cluster resource services was ended on the node (for example, the   */
/*     QSYSWRK subsystem was ended with CRS still active).  Action code    */
/*     dependent data of QcstNodeFailure will be passed.                   */
/*   - the application for an application CRG has failed on the primary    */
/*     node and could not be restarted there.  The CRG is Active.          */
/*     Action code dependent data of QcstApplFailure will be passed.       */
/*   - the node failed (such as a power failure).  Action code dependent   */
/*     data of QcstNodeFailure will be passed.                             */
/*   - The cluster has become partitioned due to some communication failure*/
/*     such as a communication line or LAN failure.  The Failover action   */
/*     code is passed to recovery domain nodes in the majority partition.  */
/*     Nodes in the minority partition see the End action code.  Action    */
/*     code dependent data of QcstPartitionFailure will be passed.         */
/*   - A node in the CRG's recovery domain is being ended with the         */
/*     QcstEndClusterNode API.  The node being ended will see the End Node */
/*     action code.  All other nodes in the recovery domain will see the   */
/*     Failover action code.  Action code dependent data of QcstEndNode    */
/*     will be passed for the Failover action code.                        */
/*   - An active recovery domain node for an active CRG is being removed   */
/*     from the cluster with the QcstRemoveClusterNodeEntry API.   Action  */
/*     code dependent data of QcstRemoveNode will be passed.  If an        */
/*     inactive node is removed for an active CRG, or if the CRG is        */
/*     inactive, an action code of Remove Node is passed.                  */
/*                                                                         */
/* The exit program is called regardless of whether or not the CRG is      */
/* active.  The exit program may have nothing to do if the CRG is not      */
/* active.                                                                 */
/*                                                                         */
/* If the CRG is active and the leaving member was the primary node,       */
/* perform the functions necessary for failover to a new primary.          */
/*                                                                         */
/* The Action_Code_Dependent_Data field can be used to determine if:       */
/*  - the failure was due to a problem that caused the cluster to become   */
/*     partitioned (all CRGs which had the partitioned nodes in the        */
/*     recovery domain are affected)                                       */
/*  - a node failed or had cluster resource services ended on the node (all*/
/*    CRGs which had the failed/ended node in the recovery  domain are     */
/*    affected)                                                            */
/*  - only a single CRG was affected (for example a single CRG job was     */
/*    canceled on a node or a single application failed)                  */
/*                                                                         */
/*                                                                         */
/* Things to consider:                                                     */
/*   - Prepare the new primary node so the application can be started.     */
/*   - The application should NOT be started at this time.  The exit       */
/*     program will be called again with the QcstCrgAcStart action code if */
/*     the CRG was active when the failure occurred.                       */
/*   - If the application CRG is active, ensure the required data CRGs are */
/*     active.                                                             */ 
/*                                                                         */
/***************************************************************************/
static int memberIsLeaving(int role,
                           int priorRole,
                           Qcst_EXTP0100_t *crgData,
                           EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* If the CRG is active, perform failover.  Otherwise, nothing to do.    */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  if (crgData->Original_Cluster_Res_Grp_Stat == QcstCrgActive) {

   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* The CRG is active.  Determine if my role has changed and I am now   */
    /* the new primary.                                                    */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/

    if (priorRole != role && role == QcstPrimaryNodeRole) {

     
/*-------------------------------------------------------------------*/
      /*                                                                   */
      /* I was not the primary but am now.  Do failover actions but don't  */
      /* start the application at this time because this exit program will */
      /* be called again with the Start action code.                       */
      /*                                                                   */
     
/*-------------------------------------------------------------------*/

     
/*-------------------------------------------------------------------*/
      /*                                                                   */
      /* Ensure the data area status on this node starts out indicating    */
      /* the application is not running.                                   */
      /*                                                                   */
     
/*-------------------------------------------------------------------*/
      setApplCrgDataArea(Appl_Ended);

     
/*-------------------------------------------------------------------*/
      /*                                                                   */
      /* If the application has no actions to do on the Start action code  */
      /* and will become active as soon as the takeover IP address is      */
      /* activated, then this code should be uncommented.  This code will  */
      /* determine if all CRGs that this application CRG is dependent upon */
      /* are ready.  If this check fails, return failure from the action   */
      /* code.                                                             */
      /*                                                                   */
     
/*-------------------------------------------------------------------*/
/*      if (QcstSuccessful != checkDependCrgDataArea(MaxWaitSeconds))  */
/*         return QcstFailWithOutRestart;                              */

    }
  } 

  return QcstSuccessful;
}  /* end memberIsLeaving()                                                */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcSwitchover                                       */
/*                                                                         */
/* The QcstInitiateSwitchOver API was called.  The first backup node in    */
/* the cluster resource group's recovery domain is taking over as the      */
/* primary node and the current primary node is being made the last backup.*/
/*                                                                         */
/* Things to consider:                                                     */
/*   - Prepare the new primary node so the application can be started.     */
/*   - The application should NOT be started at this time.  The exit       */
/*     program will be called again with the QcstCrgAcStart action code.   */
/*   - The job running the application is canceled and the IP takeover    */
/*     address is ended prior to the exit program being called on the      */
/*     current primary.                                                    */
/*   - Ensure required data or device CRGs have switched over and are      */
/*     active.                                                             */
/*                                                                         */
/***************************************************************************/
static int switchPrimary(int role,
                         int priorRole,
                         Qcst_EXTP0100_t *crgData,
                         EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* See if I am the old primary.                                          */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  if (priorRole == QcstPrimaryNodeRole) {
   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Do what ever needs to be done to cleanup the old primary before the */
    /* switch.  Remember that that job which was running the exit program  */
    /* which started the application was canceled already.                */
    /*                                                                     */
    /* One example may be to clean up any processes holding locks on the   */
    /* database.  This may have been done by the application cancel        */
    /* handler if one was invoked.                                         */
   
/*---------------------------------------------------------------------*/
  }

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* I'm not the old primary.  See if I'm the new primary.                 */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  else if (role == QcstPrimaryNodeRole) {
   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Do what ever needs to be done on the new primary before the         */
    /* application is started with the QcstCrgAcStart action code.         */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/

   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Ensure the data area status on this nodes starts out indicating     */
    /* the application is not running.                                     */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    setApplCrgDataArea(Appl_Ended);

   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* If the application has no actions to do on the Start action code    */
    /* and will become active as soon as the takeover IP address is        */
    /* activated, then this code should be uncommented.  This code will    */
    /* determine if all CRGs that this application CRG is dependent upon   */
    /* are ready.  If this check fails, return failure from the action     */
    /* code.                                                               */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
/*      if (QcstSuccessful != checkDependCrgDataArea(MaxWaitSeconds))      */
/*         return QcstFailWithOutRestart;                                  */

  }
  else {
   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* This node is one of the other backup nodes or it is a replicate     */
    /* node.  If there is anything those nodes must do, do it here.  If */
    /* not, remove this else block.                                        */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/

   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Ensure the data area status on this nodes starts out indicating     */
    /* the application is not running.                                     */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    setApplCrgDataArea(Appl_Ended);
  }

  return QcstSuccessful;
}  /* end switchPrimary()                                                  */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcAddNode                                          */
/*                                                                         */
/* The QcstAddNodeToRcvyDomain API was called.  A new node is being added  */
/* to the recovery domain of a cluster resource group.                     */
/*                                                                         */
/* Things to consider:                                                     */
/*   - A new node is being added to the recovery domain.  See the          */
/*     considerations in the createCrg() function.                         */
/*   - If this CRG is enabled to use the QcstDistributeInformation API,    */
/*     the user queue needed by that API could be created at this time.    */
/*                                                                         */
/***************************************************************************/
static int addNode(int role,
                   int priorRole,
                   Qcst_EXTP0100_t *crgData,
                   EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  
  /*                                                                       */
  /* Determine if I am the node being added.                               */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
 
  if (0 == memcmp(&crgData->This_Nodes_ID, 
                  &crgData->Changing_Node_ID, 
                  sizeof(Qcst_Node_Id_t)))
  {
   
/*---------------------------------------------------------------------*/
  
    /*                                                                     */
    /* Set the status of the data area on this new node.                   */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    setApplCrgDataArea(Appl_Ended);

   
/*---------------------------------------------------------------------*/
  
    /*                                                                     */
    /* Create the queue needed by the Distribute Information API.          */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
 
    if (0 == memcmp(&crgData->DI_Queue_Name,
                    Nulls,
                    sizeof(crgData->DI_Queue_Name)))
    {
    }
  }

  return QcstSuccessful;
}  /* end addNode()                                                
       */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcRemoveNode                                       */
/*                                                                         */
/* The QcstRemoveNodeFromRcvyDomain or the QcstRemoveClusterNodeEntry      */
/* API was called.  A node is being removed from the recovery domain of    */
/* a cluster resource group or it is being removed entirely from the       */
/* cluster.                                                                */
/*                                                                         */
/* This action code is seen by:                                            */
/*  For the QcstRemoveClusterNodeEntry API:                                */
/*    - If the removed node is active and the CRG is Inactive, all nodes in*/
/*      the recovery domain including the node being removed see this      */
/*      action code.  The nodes NOT being removed see action code dependent*/
/*      data of QcstNodeFailure.                                           */
/*    - If the removed node is active and the CRG is Active, the node being*/
/*      removed sees the Remove Node action code.  All other nodes in the  */
/*      recovery domain see an action code of Failover and action code     */
/*      dependent data of QcstNodeFailure.                                 */
/*    - If the node being removed is not active in the cluster, all nodes  */
/*      in the recovery domain will see this action code.                  */    
/*  For the QcstRemoveNodeFromRcvyDomain API:                              */
/*    - All nodes see the Remove Node action code regardless of whether or */
/*      not the CRG is Active.  Action code dependent data of              */
/*      QcstRmvRcvyDmnNode will also be passed.                            */
/*                                                                         */
/* Things to consider:                                                     */
/*   - You may want to cleanup the removed node by deleting objects no     */
/*     longer needed there.                                                */
/*   - The job running the application is canceled and the IP takeover    */
/*     address is ended after the exit program is called if this is the    */
/*     primary node and the CRG is active.                                 */
/*   - If subsystems or server jobs were started as a result of the        */
/*     QcstCrgAcStart action code, end them here or consolidate all logic  */
/*     to end the application in the cancelHandler() since it will be      */
/*     invoked for all Cluster Resource Services APIs which must end the   */
/*     application on the current primary.                                 */
/*                                                                         */
/***************************************************************************/
static int rmvNode(int role,
                   int priorRole,
                   Qcst_EXTP0100_t *crgData,
                   EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  
  /*                                                                       */
  /* Determine if I am the node being removed.                             */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
 
  if (0 == memcmp(&crgData->This_Nodes_ID, 
                  &crgData->Changing_Node_ID, 
                  sizeof(Qcst_Node_Id_t)))
  {
     
/*-------------------------------------------------------------------*/
      /*                                                                   */
      /* End the application if it is running on this node.                */
      /*                                                                   */
     
/*-------------------------------------------------------------------*/
      endApplication(QcstCrgAcRemoveNode, role, priorRole, crgData,
epData);

  }
  return QcstSuccessful;
}  /* end rmvNode                                                          */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcChange                                           */
/*                                                                         */
/* The QcstChangeClusterResourceGroup API was called.  Some attribute      */
/* or information stored in the cluster resource group object is being     */
/* changed.  Note that not all changes to the CRG object cause the exit    */
/* program to be called.  As of V5R1M0, only these changes will cause the  */   
/* exit program to be called-                                              */
/*   - the current recovery domain is being changed                        */
/*   - the preferred recovery domain is being changed                      */
/*                                                                         */
/* If any of the above changes are being made but additionally the exit    */
/* program is being changed to *NONE, the exit program is not called.      */
/*                                                                         */
/* Things to consider:                                                     */
/*   - None unless changing the recovery domain affects information or     */
/*     processes for this cluster resource group.  Note that the primary   */
/*     node cannot be changed with the QcstChangeClusterResourceGroup API  */
/*     if the CRG is active.                                               */
/*                                                                         */
/***************************************************************************/
static int chgCrg(int role,
                  int priorRole,
                  Qcst_EXTP0100_t *crgData,
                  EpData *epData) {

  return QcstSuccessful;
}  /* end chgCrg()                                                         */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcDeleteCommand                                    */
/*                                                                         */
/* The Delete Cluster Resource Group (DLTCRG) CL command has been called   */
/* to delete a cluster resource group object, the QcstDeleteCluster API    */
/* has been called, or the QcstRemoveClusterNodeEntry API has been called. */
/* In each case, cluster resource services is not active on the cluster    */
/* node where the command or API was called.  Thus, this function is not   */
/* distributed cluster wide but occurs only on the node where the CL       */
/* command or API was called.                                              */
/*                                                                         */
/* If the QcstDeleteCluster API was used, action code dependent data of    */
/* QcstDltCluster is passed.                                               */
/*                                                                         */
/* See the considerations in the deleteCrg() function                      */
/*                                                                         */
/***************************************************************************/
static int deleteCrgWithCmd(int role,
                            int doesNotApply,
                            Qcst_EXTP0100_t *crgData,
                            EpData *epData) {

  return QcstSuccessful;
}  /* end deleteCrgWithCmd()                                               */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgEndNode                                            */
/*                                                                         */
/* The QcstEndClusterNode API was called or a CRG job was canceled.       */
/*                                                                         */
/* The QcstCrgEndNode action code is passed to the exit program only on the*/
/* node being ended or where the CRG job was canceled.  On the node where */
/* a Cluster Resource Services job is canceled, action code dependent data*/
/* of QcstMemberFailure will be passed.                                    */
/* When Cluster Resource Services ends on this node or the CRG job ends, it*/
/* will cause all other nodes in the cluster to go through failover        */
/* processing.  The action code passed to all other nodes will be          */
/* QcstCrgAcFailover.   Those nodes will see action code dependent data of */
/* QcstMemberFailure if a CRG job is canceled or QcstNodeFailure if the   */
/* node is ended.                                                          */
/*                                                                         */
/* Things to consider:                                                     */
/*   - The job running the application is canceled and the IP takeover    */
/*     address is ended after the exit program is called if this is the    */
/*     primary node and the CRG is active.                                 */
/*   - If subsystems or server jobs were started as a result of the        */
/*     QcstCrgAcStart action code, end them here.                          */
/*                                                                         */
/***************************************************************************/
static int endNode(int role,
                   int priorRole,
                   Qcst_EXTP0100_t *crgData,
                   EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* End the application if it is running on this node.                    */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  endApplication(QcstCrgEndNode, role, priorRole, crgData, epData);

  return QcstSuccessful;
}  /* end endNode()                                                        */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcChgNodeStatus                                    */
/*                                                                         */
/* The QcstChangeClusterNodeEntry API was called.  The status of a node    */
/* is being changed to failed.  This API is used to inform cluster resource*/
/* services that the node did not partition but really failed.             */
/*                                                                         */
/* Things to consider:                                                     */
/*   - The exit program was called previously with an action code of       */
/*     QcstCrgAcEnd if the CRG was active or an action code of             */
/*     QcstCrgAcFailover if the CRG was inactive because cluster resource  */
/*     services thought the cluster had become partitioned.  The user is   */
/*     now telling cluster resource services that the node really failed   */
/*     instead of partitioned.  The exit program has something to do only  */
/*     if it performed some action previously that needs to be changed now */
/*     that node failure can be confirmed.                                 */ 
/*                                                                         */
/***************************************************************************/
static int chgNodeStatus(int role,
                         int priorRole,
                         Qcst_EXTP0100_t *crgData,
                         EpData *epData) {

  return QcstSuccessful;
}  /* end chgNodeStatus()                                                  */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcCancelFailover                                   */
/*                                                                         */
/* Cluster resource services on the primary node has failed or ended       */
/* for this cluster resource group.  A message was sent to the failover    */
/* message queue specified for the CRG, and the result of that message     */
/* was to cancel the failover.  This will change the status of the CRG to  */
/* inactive and leave the primary node as primary.                         */
/*                                                                         */
/* Things to consider:                                                     */
/*   - The primary node is no longer participating in cluster activities.  */
/*     The problem which caused the primary node to fail should be fixed   */
/*     so that the CRG may be started again.                               */
/*                                                                         */
/***************************************************************************/
static int cancelFailover(int role,
                          int priorRole,
                          Qcst_EXTP0100_t *crgData,
                          EpData *epData) {

  return QcstSuccessful;
}  /* end cancelFailover()                                                 */


/***************************************************************************/
/*                                                                         */
/* Action code = exit program does not know it yet                         */
/*                                                                         */
/* A new action code has been passed to this exit program.  This can occur */
/* after a new i5/OS release has been installed and some new cluster API  */
/* was called or some new cluster event occurred.  The logic in this exit  */
/* program has not yet been updated to understand the new action code.     */
/*                                                                         */
/* Two different strategies could be used for the new action code.  The    */
/* correct strategy is dependent upon the kinds of things this particular  */
/* exit program does for the application.                                  */
/*                                                                         */
/* One strategy is to not do anything and return a successful return code. */
/* This allows the new cluster API or event to run to completion.  It      */
/* allows the function to be performed even though this exit program       */
/* did not understand the new action code.  The risk, though, is that the  */
/* exit program should have done something and it did not.  At a minimum,  */
/* you may want to log some kind of error message about what happened so   */
/* that programming can investigate and get the exit program updated.      */
/*                                                                         */
/* The opposite strategy is to return an error return code such as         */
/* QcstFailWithRestart.  Of course doing this means that the new cluster   */
/* API or event cannot be used until the exit program is updated for the   */
/* new action code.  Again, logging some kind of error message for         */
/* programming to investigate would be worthwhile.                         */
/*                                                                         */
/* Only the designer of the exit program can really decide which is the    */
/* better course of action.                                                */
/*                                                                         */
/***************************************************************************/
static int newActionCode(int role,
                         int doesNotApply,
                         Qcst_EXTP0100_t *crgData,
                         EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Add logic to log an error somewhere - operator message queue, job     */
  /* log, application specific error log, etc. so that the exit program    */
  /* gets updated to properly handle the new action code.                  */
  /*                                                                       */
  /* Note that if this is left coded as it is, this is the "don't do       */
  /* anything" strategy described in the prologue above.                   */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/

  return QcstSuccessful;
}  /* end newActionCode()                                                  */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Note: The exit program is never called with an undo action code for     */
/* any of these prior action codes:                                        */
/*   QcstCrgAcChgNodeStatus                                                */
/*   QcstCrgAcDelete                                                       */
/*   QcstCrgAcDeleteCommand                                                */
/*   QcstCrgEndNode                                                        */
/*   QstCrgAcRemoveNode (If the node being removed is active in the        */
/*                       cluster and the API is Remove Cluster Node.       */
/*                       The Remove Node From Recovery Domain will call    */
/*                       with Undo and the Remove Cluster Node API will    */
/*                       call with Undo if the node being removed is       */
/*                       inactive.                                         */
/*   QcstCrgAcRestart                                                      */
/*   QcstCrgAcUndo                                                         */
/*                                                                         */
/* APIs that call an exit program do things in 3 steps.                    */
/*   1. Logic which must be done prior to calling the exit program.        */
/*   2. Call the exit program.                                             */
/*   3. Logic which must be done after calling the exit program.           */
/*                                                                         */
/* Any errors that occur during steps 2 or 3 result in the exit program    */
/* being called again with the undo action code.  This gives the exit      */
/* program an opportunity to back out any work performed when it was first */
/* called by the API.  The API will also be backing out any work it        */
/* performed trying to return the state of the cluster and cluster objects */
/* to what it was before the API was called.                               */
/*                                                                         */
/* It is suggested that the following return codes be returned for the     */
/* specified action code as that return code will result in the most       */
/* appropriate action being taken.                                         */
/*                                                                         */
/*   QcstCrgAcInitialize: QcstSuccessful; The CRG is not created.          */
/*   QcstCrgAcStart:      QcstSuccessful; The CRG is not started.          */
/*   QcstCrgAcEnd:        QcstFailWithOutRestart; The CRG is set to Indoubt*/
/*                                        The cause of the failure needs to*/
/*                                        investigated.                    */
/*   QcstCrgAcReJoin:     QcstFailWithOutRestart; The CRG is set to Indoubt*/
/*                                        The cause of the failure needs to*/
/*                                        investigated.                    */
/*   QcstCrgAcFailover:   QcstFailWithOutRestart; The CRG is set to Indoubt*/
/*                                        The cause of the failure needs to*/
/*                                        investigated.                    */
/*   QcstCrgAcSwitchover: QcstFailWithOutRestart; The CRG is set to Indoubt*/
/*                                        The cause of the failure needs to*/
/*                                        investigated.                    */
/*   QcstCrgAcAddNode:    QcstSuccessful; The node is not added.           */
/*   QcstCrgAcRemoveNode: QcstFailWithOutRestart; The CRG is set to Indoubt*/
/*                                        The cause of the failure needs to*/
/*                                        investigated.                    */
/*   QcstCrgAcChange:     QcstSuccessful; The recovery domain is not       */
/*                                        changed.                         */
/*                                                                         */
/***************************************************************************/
static int undoPriorAction(int role,
                           int priorRole,
                           Qcst_EXTP0100_t *crgData,
                           EpData *epData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* The prior action code defines what the exit program was doing when    */
  /* it failed, was canceled, or returned a non successful return code.   */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  if (crgData->Prior_Action_Code <= MaxAc )
    return (*undoFcn[crgData-<Prior_Action_Code])
                                         (role, priorRole, crgData,
epData);
  else 
   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* IBM has defined a new action code in a new operating system release */
    /* and this exit program has not yet been updated to handle it.  Take a*/
    /* default action for now.                                             */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    return newActionCode(role, priorRole, crgData, epData);
}  /* end undoPriorAction()                                                */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcInitialize                                 */
/*                                                                         */
/* Things to consider:                                                     */
/*   The CRG will not be created.  Objects that might have been created    */
/*   on nodes in the recovery domain should be deleted since a subsequent  */
/*   create could fail if those objects already exist.                     */
/*                                                                         */
/***************************************************************************/
static int undoCreateCrg(int role,
                         int doesNotApply,
                         Qcst_EXTP0100_t *crgData,
                         EpData *epData) {

  return QcstSuccessful;
}  /* end undoCreateCrg()                                                 */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcStart                                      */
/*                                                                         */
/* Things to consider:                                                     */
/*   Cluster Resource Services failed when it was finishing the Start CRG  */
/*   API after it had already called the exit program with the Start       */
/*   Action code.                                                          */
/*                                                                         */
/*   On the primary node, the exit program job which is running the        */
/*   application will be canceled.  The exit program will then be called  */
/*   with the Undo action code.                                            */
/*                                                                         */
/*   All other nodes in the recovery domain will be called with the Undo   */
/*   action code.                                                          */
/*                                                                         */
/***************************************************************************/
static int undoStartCrg(int role,
                        int doesNotApply,
                        Qcst_EXTP0100_t *crgData,
                        EpData *epData) {

  return QcstSuccessful;
}  /* end undoStartCrg()                                                   */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcEnd                                        */
/*                                                                         */
/* Things to consider:                                                     */
/*   The CRG will not be ended.  If the exit program did anything to bring */
/*   down the application it can either restart the application or it can  */
/*   decide to not restart the application.  If the application is not     */
/*   restarted, the return code should be set to QcstFailWithOutRestart so */
/*   the status of the CRG is set to Indoubt.                              */
/*                                                                         */
/***************************************************************************/
static int undoEndCrg(int role,
                      int doesNotApply,
                      Qcst_EXTP0100_t *crgData,
                      EpData *epData) {

  return QcstFailWithOutRestart;
}  /* end undoEndCrg()                                                     */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcReJoin                                     */
/*                                                                         */
/* Things to consider:                                                     */
/*   An error occurred which won't allow the member to join this CRG       */
/*   group.  Anything done for the Join action code needs to be looked at  */
/*   to see if something must be undone if this member is not an active    */
/*   member of the CRG group.                                              */
/*                                                                         */
/***************************************************************************/
static int undoMemberIsJoining(int role,
                               int doesNotApply,
                               Qcst_EXTP0100_t *crgData,
                               EpData *epData) {

  return QcstFailWithOutRestart;
}  /* end undoMemberIsJoining()                                            */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcFailover                                   */
/*                                                                         */
/* Things to consider:                                                     */
/*   This does not mean that the node failure or failing member is being   */
/*   undone.  That failure is irreversible.  What it does mean is that the */
/*   exit program returned an error from the Failover action code or       */
/*   Cluster Resource Services ran into a problem after it called the exit */
/*   program.  If the CRG was active when Failover was attempted, it is    */
/*   not at this point.  End the resilient resource and expect a human to  */
/*   look into the failure.  After the failure is corrected, the CRG will  */
/*   must be started with the Start CRG API.                            */
/*                                                                         */
/*                                                                         */
/***************************************************************************/
static int undoMemberIsLeaving(int role,
                               int doesNotApply,
                               Qcst_EXTP0100_t *crgData,
                               EpData *epData) {

  return QcstFailWithOutRestart;
}  /* end undoMemberIsLeaving()                                            */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcSwitchover                                 */
/*                                                                         */
/* Things to consider:                                                     */
/*   Some error occurred after the point of access was moved from the      */
/*   original primary and before it could be brought up on the new primary.*/
/*   The IP address was ended on the original primary before moving the    */
/*   point of access but is started on the original primary again.  Cluster*/
/*   Resource Services will now attempt to move the point of access back   */
/*   to the original primary.  The application exit program and IP takeover*/
/*   address will be started on the original primary.                      */
/*                                                                         */
/*                                                                         */
/***************************************************************************/
static int undoSwitchPrimary(int role,
                             int doesNotApply,
                             Qcst_EXTP0100_t *crgData,
                             EpData *epData) {

  return QcstFailWithOutRestart;
}  /* end undoSwitchPrimary()                                              */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcAddNode                                    */
/*                                                                         */
/* Things to consider:                                                     */
/*   If objects were created on the new node, they should be removed so    */
/*   that a subsequent Add Node to aRecovery Domain does not fail if it    */
/*   attempts to create objects again.                                     */
/*                                                                         */
/*                                                                         */
/***************************************************************************/
static int undoAddNode(int role,
                       int doesNotApply,
                       Qcst_EXTP0100_t *crgData,
                       EpData *epData) {

  return QcstSuccessful;
}  /* end undoAddNode()                                                    */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcRemoveNode                                 */
/*                                                                         */
/* Things to consider:                                                     */
/*   The node is still in the recovery domain.  If objects were removed    */
/*   from the node, they should be added back.                             */
/*                                                                         */
/***************************************************************************/
static int undoRmvNode(int role,
                       int doesNotApply,
                       Qcst_EXTP0100_t *crgData,
                       EpData *epData) {

  return QcstFailWithOutRestart;
}  /* end undoRmvNode()                                                    */


/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcChange                                     */
/*                                                                         */
/* Things to consider:                                                     */
/*   Changes to the CRG will be backed out so that the CRG and its         */
/*   recovery domain look just like it did prior to the attempted change.  */
/*   Any changes the exit program made should also be backed out.          */
/*                                                                         */
/***************************************************************************/
static int undoChgCrg(int role,
                      int doesNotApply,
                      Qcst_EXTP0100_t *crgData,
                      EpData *epData) {

  return QcstSuccessful;
}  /* end undoChgCrg()                                                     */

   
/***************************************************************************/
/*                                                                         */
/* Action code = QcstCrgAcUndo                                             */
/*                                                                         */
/* Prior action code = QcstCrgAcCancelFailover                             */
/*                                                                         */
/* Things to consider:                                                     */
/*   This does not mean that the node failure or failing member is being   */
/*   undone.  That failure is irreversible.  What it does mean is that     */
/*   Cluster Resource Services ran into a problem after it called the exit */
/*   program.  The CRG will be InDoubt regardless of what is returned from */
/*   this exit program call.  Someone will need to manually look into the  */
/*   the failure.  After the failure is corrected, the CRG will must be */
/*   started with the Start CRG API.                                       */
/*                                                                         */
/*                                                                         */
/***************************************************************************/
static int undoCancelFailover(int role,
                              int doesNotApply,
                              Qcst_EXTP0100_t *crgData,
                              EpData *epData) {

  return QcstSuccessful;
}  /* end undoCancelFailover()                                             */


/***************************************************************************/
/*                                                                         */
/* A simple routine to take a null terminated object name and a null       */
/* terminated library name and build a 20 character non-null terminated    */
/* qualified name.                                                         */
/*                                                                         */
/***************************************************************************/
static void bldDataAreaName(char *objName, char* libName, char *qualName) {

  memset(qualName, 0x40, 20);
  memcpy(qualName, objName, strlen(objName));
  qualName += 10;
  memcpy(qualName, libName, strlen(libName));
  return;
}  /* end bldDataAreaName                                                  */


/***************************************************************************/
/*                                                                         */
/* The data area is checked to see if all the CRGs that this application   */
/* is dependent upon are ready.  If they are not ready, a wait for a       */
/* certain amount of time is performed and the data area is checked again. */
/* This check, wait loop continues until all dependent CRGs become ready or*/
/* until the maximum wait time has been reached.                           */
/* The length of the wait can be changed to some other value if a          */
/* particular situation would be better with shorter or longer wait times. */
/*                                                                         */
/*                                                                         */
/***************************************************************************/
static int checkDependCrgDataArea(unsigned int maxWaitTime) {

  Qus_EC_t errCode = { sizeof(Qus_EC_t), 0 };
  char dataAreaName[20];
  struct {
    Qwc_Rdtaa_Data_Returned_t stuff;
    char ready;
  } data;

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* This is an accumulation of the time waited for the dependent CRGs to  */
  /* become ready.                                                         */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  unsigned int timeWaited = 0;

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Build definition of the amount of time to wait.                       */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  _MI_Time   timeToWait;
  int hours     = 0;
  int minutes   = 0;
  int seconds   = WaitSecondsIncrement;
  int hundreths = 0;
  short int options = _WAIT_NORMAL;
  mitime( &timeToWait, hours, minutes, seconds, hundreths );

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Build the qualified name of the data area.                            */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  bldDataAreaName(DependCrgDataArea, ApplLib, dataAreaName);

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Get the data from the data area that indicates whether or not the     */
  /* CRGs are all ready.  This data area is updated by the High            */
  /* Availability Business Partners when it is ok for the application to   */
  /* proceed.                                                              */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  QWCRDTAA(&data,
           sizeof(data),
           dataAreaName,
           offsetof(Qcst_HAAPPO_t,Data_Status)+1,  /* API wants a 1 origin */
           sizeof(data.ready),
           &errCode);

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* If the dependent CRGs are not ready, wait for a bit and check again.  */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  while (data.ready != Data_Available) {

   
/*---------------------------------------------------------------------    */
    /*                                                                     */
    /* If the dependent CRGs are not ready after the maximum wait time,    */
    /* return an error.  Consider logging some message to describe why the */
    /* application did not start so that  the problem can be looked into.  */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    if (timeWaited >= maxWaitTime)
      return QcstFailWithOutRestart;

   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Wait to allow the data CRGs to become ready.                        */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    waittime(&timeToWait, options);
    timeWaited += WaitSecondsIncrement;

   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Get information from the data area again to see if the data CRGs are*/
    /* ready.                                                              */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    QWCRDTAA(&data,
             sizeof(data),
             dataAreaName,
             offsetof(Qcst_HAAPPO_t,Data_Status)+1,  /* API wants a 1 origin */
             sizeof(data.ready),
             &errCode);
  }

  return QcstSuccessful;
}  /* end checkDependCrgDataArea                                             */


/***************************************************************************/
/*                                                                         */
/* The application CRG data area is updated to indicate that the           */
/* application is running or to indicate it is not running.  This data area*/
/* information is used by the High Availability Business Partners to       */
/* coordinate the switchover activities between CRGs that have dependencies*/
/* on each other.                                                          */
/*                                                                         */
/***************************************************************************/
static void setApplCrgDataArea(char status) {

  char cmd[54];
  char cmdEnd[3] = {0x00, ')', 0x00};

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Set up the CL command string with the data area library name, the data*/
  /* area name, and the character to put into the data area.  Then run the */
  /* CL command.                                                           */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  memcpy(cmd, "CHGDTAARA DTAARA(", strlen("CHGDTAARA DTAARA(")+1);
  strcat(cmd, ApplLib);
  strcat(cmd, "/");
  strcat(cmd, ApplCrgDataArea);
  strcat(cmd, " (425 1)) VALUE(");                                 /* @A1C */
  cmdEnd[0] = status;
  strcat(cmd, cmdEnd);

  system(cmd);

  return;
}  /* end setApplCrgDataArea                                               */


/***************************************************************************/
/*                                                                         */
/* This function is called any time the exit program receives an exception */
/* not specifically monitored for by some other exception handler.  Add    */
/* appropriate logic to perform cleanup functions that may be required.    */
/* A failure return code is then set and control returns to the operating  */
/* system.  The job this exit program is running in will then end.         */
/*                                                                         */
/* When this function gets called, myData->role may still contain the      */
/* UnknownRole value if an exception occurred before this node's role      */
/* value was set.  To be completely correct, the role should be tested     */
/* for UnknownRole before making any decisions based upon the value of     */
/* role.                                                                   */
/*                                                                         */
/***************************************************************************/
static void unexpectedExceptionHandler(_INTRPT_Hndlr_Parms_T
*exData) {

 
/*-----------------------------------------------------------------------  */
  /*                                                                       */
  /* Get a pointer to the structure containing data that is passed to the  */
  /* exception handler.                                                    */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  HandlerDataT *myData = (HandlerDataT *)exData->Com_Area;

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Perform as much cleanup function as necessary.  Some global state     */
  /* information may must be kept so the exception handler knows what   */
  /* steps were completed before the failure occurred and thus knows what  */
  /* cleanup steps must be performed.  This state information could be     */
  /* kept in the HandlerDataT structure or it could be kept in some other  */
  /* location that this function can address.                              */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* If this is the primary node and the application was started, end it.  */
  /* The application is ended because the exit program will be called again*/
  /* with the Restart action code and want the restartCrg() function to */
  /* always work the same way.  In addition, ending the application may    */
  /* clear up the condition that caused the exception.    */
  /* If possible, warn users and have them stop using the application so   */
  /* things are done in an orderly manner.                                 */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  endApplication(myData->actionCode,
                 myData->role,
                 myData->priorRole,
                 myData->crgData,
                 myData->epData);

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Set the exit program return code.                                     */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  *myData->retCode = QcstFailWithRestart;

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Let the exception percolate up the call stack.                  */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  return;
}  /* end unexpectedExceptionHandler                                       */


/***************************************************************************/
/*                                                                         */
/* This function is called any time the job this exit program is running in*/
/* is canceled.  The job could be canceled due to any of the following   */
/* (the list is not intended to be all inclusive)-                         */
/*   - an API cancels an active application CRG.  The End CRG, Initiate    */
/*     Switchover, End Cluster Node, Remove Cluster Node or Delete Cluster */
/*     API cancels the job which was submitted when the exit program was   */
/*     called with a Start action code.                                    */
/*   - operator cancels the job from some operating system display such as */
/*     Work with Active Jobs                                               */
/*   - the subsystem this job is running in is ended                       */
/*   - all subsystems are ended                                            */
/*   - the system is powered down                                          */
/*   - an operating system machine check occurred                          */
/*                                                                         */
/* When this function gets called, myData->role may still contain the      */
/* UnknownRole value if cancelling occurred before this node's role        */
/* value was set.  To be completely correct, the role should be tested     */
/* for UnknownRole before making any decisions based upon the value of     */
/* role.                                                                   */
/*                                                                         */
/***************************************************************************/
static void cancelHandler(_CNL_Hndlr_Parms_T *cnlData) {

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Get a pointer to the structure containing data that was passed to the */
  /* cancel handler.                                                       */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  HandlerDataT *myData = (HandlerDataT *)cnlData->Com_Area;

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Perform as much cleanup function as necessary.  Some global state     */
  /* information may must be kept so the cancel handler knows what      */
  /* steps were completed before the job was canceled and thus knows if   */
  /* the function had really completed successfully or was only partially  */
  /* complete and thus needs some cleanup to be done.  This state          */
  /* information could be kept in the HandlerDataT structure or it could   */
  /* be kept in some other location that this function can address.        */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/

 
/*-----------------------------------------------------------------------*/

  /*                                                                       */
  /* This job is being canceled.  If I was running the application as a   */
  /* result of the Start or Restart action codes, end the application now. */
  /* This job is being canceled because a Switch Over or some other       */
  /* Cluster Resource Services API was used which affects the primary node */
  /* or someone did a cancel job with a CL command, from a system display, */
  /* etc.                                                                  */

 
/*-----------------------------------------------------------------------*/
  endApplication(myData->actionCode,
                 myData->role,
                 myData->priorRole,
                 myData->crgData,
                 myData->epData);

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Set the exit program return code.                                     */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  *myData->retCode = QcstSuccessful;

 
/*-----------------------------------------------------------------------*/
  /*                                                                       */
  /* Return to the operating system for final ending of the job.           */
  /*                                                                       */
 
/*-----------------------------------------------------------------------*/
  return;
}  /* end cancelHandler                                                    */


/***************************************************************************/
/*                                                                         */
/* A common routine used to end the application by various action code     */
/* functions, the exception handler, and the cancel handler.               */
/*                                                                         */
/***************************************************************************/
static void endApplication(unsigned int actionCode,
                           int role,
                           int priorRole,
                           Qcst_EXTP0100_t *crgData,
                           EpData *epData) {

  if (  role == QcstPrimaryNodeRole
      &&
        crgData->Original_Cluster_Res_Grp_Stat == QcstCrgActive)
{
   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* Add logic to end the application here.  You may need to add logic   */
    /* to determine if the application is still running because this       */
    /* function could be called once for an action code and again from     */
    /* the cancel handler (End CRG is an example).                         */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/



   
/*---------------------------------------------------------------------*/
    /*                                                                     */
    /* After the application has ended, update the data area to indicate   */
    /* the application is no longer running.                               */
    /*                                                                     */
   
/*---------------------------------------------------------------------*/
    setApplCrgDataArea(Appl_Ended);
  }

  return;
}  /* end endApplication                                                   */


/***************************************************************************/
/*                                                                         */
/* Print out the data passed to this program.                              */
/*                                                                         */
/***************************************************************************/
static void printParms(int actionCode,
                       int role,
                       int priorRole, 
                       Qcst_EXTP0100_t *crgData,
                       EpData *epData) {

  unsigned int i;
  char *str;

  /* Print the action code.                                                */
  printf("%s", "Action_Code = ");
  printActionCode(actionCode);

  /* Print the action code dependent data.                                 */
  printf("%s", "    Action_Code_Dependent_Data = ");
  switch (crgData->Action_Code_Dependent_Data) {
    case QcstNoDependentData:  str = "QcstNoDependentData";
                               break;
    case QcstMerge:            str = "QcstMerge";
                               break;
    case QcstJoin:             str = "QcstJoin";
                               break;
    case QcstPartitionFailure: str = "QcstPartitionFailure";
                               break;
    case QcstNodeFailure:      str = "QcstNodeFailure";
                               break;
    case QcstMemberFailure:    str = "QcstMemberFailure";
                               break;
    case QcstEndNode:          str = "QcstEndNode";
                               break;
    case QcstRemoveNode:       str = "QcstRemoveNode";
                               break;
    case QcstApplFailure:      str = "QcstApplFailure";
                               break;
    case QcstResourceEnd:      str = "QcstResourceEnd";
                               break;
    case QcstDltCluster:       str = "QcstDltCluster";
                               break;
    case QcstRmvRcvyDmnNode:   str = "QcstRmvRcvyDmnNode";
                               break;
    case QcstDltCrg:           str = "QcstDltCrg";
                               break;
    default: str = "unknown action code dependent data";
  }
  printf("%s \n", str);


  /* Print the prior action code.                                          */
  printf("%s", "  Prior_Action_Code = ");
  if (crgData->Prior_Action_Code)
    printActionCode(crgData->Prior_Action_Code);
  printf("\n");

  /* Print the cluster name.                                               */
  printStr("  Cluster_Name = ",
           crgData->Cluster_Name, sizeof(Qcst_Cluster_Name_t));

  /* Print the CRG name.                                                   */
  printStr("  Cluster_Resource_Group_Name = ",
           crgData->Cluster_Resource_Group_Name,
sizeof(Qcst_Crg_Name_t));

  /* Print the CRG type.                                                   */
  printf("%s \n", "  Cluster_Resource_Group_Type =
QcstCrgApplResiliency");

  /* Print the CRG status.                                                 */
  printf("%s", "  Cluster_Resource_Group_Status = ");
  printCrgStatus(crgData->Cluster_Resource_Group_Status);

  /* Print the CRG original status.                                        */
  printf("%s", "  Original_Cluster_Res_Grp_Stat = ");
  printCrgStatus(crgData->Original_Cluster_Res_Grp_Stat);

  /* Print the Distribute Information queue name.                          */
  printStr("  DI_Queue_Name = ",
           crgData->DI_Queue_Name,
sizeof(crgData->DI_Queue_Name));
  printStr("  DI_Queue_Library_Name = ",
           crgData->DI_Queue_Library_Name,
           sizeof(crgData->DI_Queue_Library_Name));

  /* Print the CRG attributes.                                             */
  printf("%s", "  Cluster_Resource_Group_Attr = ");
  if (crgData->Cluster_Resource_Group_Attr &
QcstTcpConfigByUsr)
    printf("%s", "User Configures IP Takeover Address");
  printf("\n");

  /* Print the ID of this node.                                            */
  printStr("  This_Nodes_ID = ",
           crgData->This_Nodes_ID, sizeof(Qcst_Node_Id_t));

  /* Print the role of this node.                                          */
  printf("%s %d \n", "  this node's role = ", role);

  /* Print the prior role of this node.                                    */
  printf("%s %d \n", "  this node's prior role = ", priorRole);

  /* Print which recovery domain this role comes from.                     */
  printf("%s", "  Node_Role_Type = ");
  if (crgData->Node_Role_Type == QcstCurrentRcvyDmn)
    printf("%s \n", "QcstCurrentRcvyDmn");
  else
    printf("%s \n", "QcstPreferredRcvyDmn");

  /* Print the ID of the changing node (if any).                           */
  printStr("  Changing_Node_ID = ",
           crgData->Changing_Node_ID, sizeof(Qcst_Node_Id_t));

  /* Print the role of the changing node (if any).                         */
  printf("%s", "  Changing_Node_Role = ");
  if (crgData->Changing_Node_Role == -3)
    printf("%s \n", "*LIST");
  else if (crgData->Changing_Node_Role == -2)
   printf("%s \n", "does not apply");
  else  
    printf("%d \n", crgData->Changing_Node_Role);

  /* Print the takeover IP address.                                        */
  printStr("  Takeover_IP_Address = ",
           crgData->Takeover_IP_Address,
sizeof(Qcst_TakeOver_IP_Address_t));

  /* Print the job name.                                                   */
  printStr("  Job_Name = ", crgData->Job_Name, 10);

  /* Print the CRG changes.                                                */
  printf("%s \n", "  Cluster_Resource_Group_Changes = ");
  if (crgData->Cluster_Resource_Group_Changes &
QcstRcvyDomainChange)
    printf("      %s \n", "Recovery domain changed");
  if (crgData->Cluster_Resource_Group_Changes &
QcstTakeOverIpAddrChange)
    printf("      %s \n", "Takeover IP address changed");

  /* Print the failover wait time.                                         */
  printf("%s", "Failover_Wait_Time = ");
  if (crgData->Failover_Wait_Time == QcstFailoverWaitForever)
     printf("%d %s \n", crgData->Failover_Wait_Time, "Wait
forever");
  else if (crgData->Failover_Wait_Time == QcstFailoverNoWait)
     printf("%d %s \n", crgData->Failover_Wait_Time, "No wait");
  else
     printf("%d %s \n", crgData->Failover_Wait_Time, "minutes");

  /* Print the failover default action.                                    */
  printf("%s", "Failover_Default_Action = "); 
  if (crgData->Failover_Default_Action == QcstFailoverProceed)
     printf("%d %s \n", crgData->Failover_Default_Action,
"Proceed");
  else
     printf("%d %s \n", crgData->Failover_Default_Action,
"Cancel");

  /* Print the failover message queue name.                                */
  printStr("  Failover_Msg_Queue = ",
           crgData->Failover_Msg_Queue,
sizeof(crgData->Failover_Msg_Queue));
  printStr("  Failover_Msg_Queue_Lib = ",
           crgData->Failover_Msg_Queue_Lib,
           sizeof(crgData->Failover_Msg_Queue_Lib));

  /* Print the cluster version.                                            */
  printf("%s %d \n",
         "  Cluster_Version = ", crgData->Cluster_Version);

  /* Print the cluster version mod level                                   */
  printf("%s %d \n",
         "  Cluster_Version_Mod_Level = ", 
         crgData->Cluster_Version_Mod_Level);

  /* Print the requesting user profile.                                    */
  printStr("  Req_User_Profile = ",
           crgData->Req_User_Profile,
sizeof(crgData->Req_User_Profile));

  /* Print the length of the data in the structure.                        */
  printf("%s %d \n",
         "  Length_Info_Returned = ",
crgData->Length_Info_Returned);

  /* Print the offset to the recovery domain array.                        */
  printf("%s %d \n",
         "  Offset_Rcvy_Domain_Array = ",
crgData->Offset_Rcvy_Domain_Array);

  /* Print the number of nodes in the recovery domain array.               */
  printf("%s %d \n",
         "  Number_Nodes_Rcvy_Domain = ",
crgData->Number_Nodes_Rcvy_Domain);

  /* Print the current/new recovery domain.                                */
  printRcvyDomain("   The recovery domain:",
                  crgData->Number_Nodes_Rcvy_Domain,
                  (Qcst_Rcvy_Domain_Array1_t *)
                  ((char *)crgData +
crgData->Offset_Rcvy_Domain_Array));

  /* Print the offset to the prior recovery domain array.                  */
  printf("%s %d \n",
         "  Offset_Prior_Rcvy_Domain_Array = ",
         crgData->Offset_Prior_Rcvy_Domain_Array);

  /* Print the number of nodes in the prior recovery domain array.         */
  printf("%s %d \n",
         "  Number_Nodes_Prior_Rcvy_Domain = ",
         crgData->Number_Nodes_Prior_Rcvy_Domain);

  /* Print the prior recovery domain if one was passed.                    */
  if (crgData->Offset_Prior_Rcvy_Domain_Array) {
    printRcvyDomain("   The prior recovery domain:",
                    crgData->Number_Nodes_Prior_Rcvy_Domain,
                    (Qcst_Rcvy_Domain_Array1_t *)
                ((char *)crgData +
crgData->Offset_Prior_Rcvy_Domain_Array));
  }

  return;
}  /* end printParms                                                       */


/***************************************************************************/
/*                                                                         */
/* Print a string for the action code.                                     */
/*                                                                         */
/***************************************************************************/
static void printActionCode(unsigned int ac) {

  char *code;
  switch (ac) {
    case QcstCrgAcInitialize: code = "QcstCrgAcInitialize"; 
                              break;
    case QcstCrgAcStart:      code = "QcstCrgAcStart";
                              break;
    case QcstCrgAcRestart:    code = "QcstCrgAcRestart";
                              break;
    case QcstCrgAcEnd:        code = "QcstCrgAcEnd";
                              break;
    case QcstCrgAcDelete:     code = "QcstCrgAcDelete";
                              break;
    case QcstCrgAcReJoin:     code = "QcstCrgAcReJoin";
                              break;
    case QcstCrgAcFailover:   code = "QcstCrgAcFailover";
                              break;
    case QcstCrgAcSwitchover: code = "QcstCrgAcSwitchover";
                              break;
    case QcstCrgAcAddNode:    code = "QcstCrgAcAddNode";
                              break;
    case QcstCrgAcRemoveNode: code = "QcstCrgAcRemoveNode";
                              break;
    case QcstCrgAcChange:     code = "QcstCrgAcChange";
                              break;
    case QcstCrgAcDeleteCommand: code = "QcstCrgAcDeleteCommand";
                              break;
    case QcstCrgAcUndo:       code = "QcstCrgAcUndo";
                              break;
    case QcstCrgEndNode:      code = "QcstCrgEndNode";
                              break;
    case QcstCrgAcAddDevEnt:  code = "QcstCrgAcAddDevEnt";
                              break;
    case QcstCrgAcRmvDevEnt:  code = "QcstCrgAcRmvDevEnt";
                              break;
    case QcstCrgAcChgDevEnt:  code = "QcstCrgAcChgDevEnt";
                              break;
    case QcstCrgAcChgNodeStatus: code = "QcstCrgAcChgNodeStatus";
                              break;
    case QcstCrgAcCancelFailover: code = "QcstCrgAcCancelFailover";
                              break;
    case QcstCrgAcVerificationPhase: code =
"QcstCrgAcVerificationPhase";
                              break;
    default:                  code = "unknown action code";
                              break;
  }
  printf("%s", code);

  return;
}  /* end printActionCode                                                  */


/***************************************************************************/
/*                                                                         */
/* Print the CRG status.                                                   */
/*                                                                         */
/***************************************************************************/
static void printCrgStatus(int status) {

  char * str;
  switch (status) {
    case QcstCrgActive:               str = "QcstCrgActive";
                                      break;
    case QcstCrgInactive:             str= "QcstCrgInactive";
                                      break;
    case QcstCrgIndoubt:              str = "QcstCrgIndoubt";
                                      break;
    case QcstCrgRestored:             str = "QcstCrgRestored";
                                      break;
    case QcstCrgAddnodePending:       str =
"QcstCrgAddnodePending";
                                      break;
    case QcstCrgDeletePending:        str = "QcstCrgDeletePending";
                                      break;
    case QcstCrgChangePending:        str = "QcstCrgChangePending";
                                      break;
    case QcstCrgEndCrgPending:        str = "QcstCrgEndCrgPending";
                                      break;
    case QcstCrgInitializePending:    str =
"QcstCrgInitializePending";
                                      break;
    case QcstCrgRemovenodePending:    str =
"QcstCrgRemovenodePending";
                                      break;
    case QcstCrgStartCrgPending:      str =
"QcstCrgStartCrgPending";
                                      break;
    case QcstCrgSwitchOverPending:    str =
"QcstCrgSwitchOverPending";
                                      break;
    case QcstCrgDeleteCmdPending:     str =
"QcstCrgDeleteCmdPending";
                                      break;
    case QcstCrgAddDevEntPending:     str =
"QcstCrgAddDevEntPending";
                                      break;
    case QcstCrgRmvDevEntPending:     str =
"QcstCrgRmvDevEntPending";
                                      break;
    case QcstCrgChgDevEntPending:     str =
"QcstCrgChgDevEntPending";
                                      break;
    case QcstCrgChgNodeStatusPending: str =
"QcstCrgChgNodeStatusPending";
                                      break;
    default: str = "unknown CRG status";
  }
  printf("%s \n", str);

  return;
}  /* end printCrgStatus                                                   */


/***************************************************************************/
/*                                                                         */
/* Print the recovery domain.                                              */
/*                                                                         */
/***************************************************************************/
static void printRcvyDomain(char *str,
                            unsigned int count,
                            Qcst_Rcvy_Domain_Array1_t *rd) {

  unsigned int i;
  printf("\n %s \n", str);
  for (i=1; i<=count; i++) {
    printStr("    Node_ID = ", rd->Node_ID,
sizeof(Qcst_Node_Id_t));
    printf("%s %d \n", "      Node_Role = ", rd->Node_Role);
    printf("%s", "      Membership_Status = ");
    switch (rd->Membership_Status) {
      case 0: str = "Active";
              break;
      case 1: str = "Inactive";
              break;
      case 2: str = "Partition";
              break;
      default: str = "unknown node status";
    }
    printf("%s \n", str);
    rd++;
  }
  return;
}  /* end printRcvyDomain                                                  */

/***************************************************************************/
/*                                                                         */
/* Concatenate a null terminated string and a non null terminated string   */
/* and print it.                                                           */
/*                                                                         */
/***************************************************************************/
static void printStr(char *s1, char *s2, unsigned int len) {

  char buffer[132];
  memset(buffer, 0x00, sizeof(buffer));
  memcpy(buffer, s1, strlen(s1));
  strncat(buffer, s2, len);
  printf("%s \n", buffer);
  return;
}  /* end printStr                                                         */