//Sample code file: var/ndk/webBuildengine/tmp/viewable_samples/7dc83345-68a3-4f0f-9806-411aabc6b121/hmo.c

//Warning: This code has been marked up for HTML

//*++======================================================================

// Program Name:

//

//

// File Name:        hmo.c

//

// Version:

//

// Author:

//

// Abstract:

//

// Notes:

//

// Revision History:

//

// Copyright (C) 2001  Novell, Inc.  All Rights Reserved.

//

// No part of this file may be duplicated, revised, translated, localized,

// or modified in any manner or compiled, linked or uploaded or downloaded

// to or from any computer system without the prior written consent of

// Novell, Inc.

//

//=====================================================================--*/


/*  SAMPLE LOAD SCRIPT
add secondary ipaddress 1.2.3.5
hmoldap /KEY=ldap_resource_name /LOOK=100 /IS=300 /HOST=localhost /PORT=389 /DN= /PSW=cluster


SAMPLE UNLOAD SCRIPT
del secondary ipaddress 1.2.3.5

*/

class='cKeyword'>#include <procdefs.h>
class='cKeyword'>#include <stdio.h>
class='cKeyword'>#include <stdlib.h>
class='cKeyword'>#include <string.h>
class='cKeyword'>#include <nwthread.h>
class='cKeyword'>#include <signal.h>
class='cKeyword'>#include <ncssdk.h>
class='cKeyword'>#include <mpkapis.h>
class='cKeyword'>#include <clstrlib.h>
class='cKeyword'>#include "hmo.h"

UINT32            ShutDownFlag, DeadFlag;
UINT8             savedCommandLine[512];
int               ThdGrpID;

typedef struct{
   UINT32             (*curLooksAlive)( int ParamCount, UINT8 *ParamList[]);
   UINT32             (*curIsAlive)( int ParamCount, UINT8 *ParamList[]);
   UINT32            IsWait, LooksWait;
   UINT8               WhichResource[ 64];
   UINT8               params[ MAX_PARAMS][ 80];      // data from parse

   UINT8               keywords[ MAX_PARAMS][ 20];      // What to parse

   UINT32             NumParams;
   int               thisHmo;
   NCS_RESOURCE      aRes, *pRes;
   UINT32            GroupEpoch;      // which epoch are we at

   UINT32            NodeBitmap;      // who's in the cluster

   UINT8               loadBuff[1200];
   UINT8               unloadBuff[1200];
   UINT32            ThisNodeNum;
   NCS_HANDLE         sdkHandle;
   UINT32            RecoveryFlag;
   UINT32            FailFlag;
}HMO_CONTEXT;

HMO_CONTEXT anHmo, *pHmo;

UINT8 *parseStrs[ MAX_PARAMS +5] = { MSG("KEY", 1), MSG("LOOK", 2), MSG("IS", 3), MSG("RECOVERY", 4), MSG("FAILED", 5), 
   NULL };

UINT8 *recoverStrs[ 5]={MSG("MIGRATE", 6), MSG("OFFLINE", 7), MSG("RESTART", 8), MSG("ABEND", 9), NULL};
typedef enum {
   RECOVER_UNKNOWN, RECOVER_MIGRATE, RECOVER_OFFLINE, RECOVER_RESTART,
   RECOVER_ABEND
}RECOVER_TYPES;
   

// ParseLine looks for the following:

// /key=<phrase> phrase to look for in the load script to find this resource

// /looks=#  number of seconds between looks-alive calls

// /is=#  number of seconds between is-alive calls. Should be longer than looks

// All other key words are to be registered from the HmoRegister call for the

// application specific parameters.


void ParseCommandHmo( HMO_CONTEXT *pHmo, UINT8 *ptrCommandLine);
UINT32 MoveThis( HMO_CONTEXT *pHmo);
void HmoGaurdian( THREAD thr, void *arg);

/******************************************************************************\
*   HmoStart
\******************************************************************************/
class='cKeyword'>class='cKeyword'>#ifdef  MsgTagNLM      
BYTE                         **NLMMessageTable;
class='cKeyword'>#endif

struct ScreenStruct *CScreen = NULL;

//struct LoadDefinitionStructure *   NLMHandle = NULL;


   LONG
HmoStart(
    struct LoadDefinitionStructure *loadHandle,
    struct ScreenStruct *initErrorScreen,
    STR *commandLine)
{
   UINT retVal;

class='cKeyword'>class='cKeyword'>#ifdef MsgTagNLM
   LONG            messageCount;
   LONG            languageId;
   BYTE            *helpFile;

   /*
    * Load the message file.
    */
   ReturnMessageInformation((LONG)loadHandle, &NLMMessageTable,
                     &messageCount, &languageId, &helpFile);
class='cKeyword'>#endif /* MsgTagNLM */

   CScreen = initErrorScreen;

   /* save the command line */
   CStrCpy( savedCommandLine, commandLine);

   /* init the flags */
   ShutDownFlag = FALSE;
   DeadFlag = 0;

   /* start the hmo */
   retVal = StartHmoApp();

   /* return to the loader */
   return retVal;
}

/******************************************************************************\
*   HmoExit
\******************************************************************************/

   LONG
HmoExit(void)
{
   OutputToScreen( CScreen, MSG("HmoExit called\r\n", 10));   
   
   ShutDownFlag = 1;
   if( pHmo != NULL && pHmo->thisHmo != 0){
      while( DeadFlag == 0){
         kYieldThread();
      }
   }

   return 0;
}


void HmoDeregister( HMO_HANDLE hmoHandle)
{
   HMO_CONTEXT *pHmo;

   OutputToScreen( CScreen, MSG("Deregister called %x\r\n", 11), hmoHandle);   

   pHmo = ( HMO_CONTEXT *)hmoHandle;
   if( pHmo != NULL){
      if( pHmo->sdkHandle != NULL){
         NCS_Deregister( pHmo->sdkHandle);
         pHmo->sdkHandle = NULL;
      }
   }

}


//*****************************************

// Callback for node events.


void myEpochCback( IN NCS_HANDLE Handle, IN UINT32 GroupEpoch, 
   IN UINT32 Members ){

}


//*****************************************

// Callback for resource events.


UINT32 myCrmCback(NCS_MAPI crmMapi, NCS_RESOURCE *pRes,  void * cxt)
{
   OutputToScreen( CScreen, MSG("myCrmCback called\r\n", 12));   
   return 0;
}

//*************************************************************************

// OUT: newHandle gets a pointer to a structure of HMO_CONTEXT.

// IN: function calls for looks and is alive. The param?'s are for strings

// to parse for input parameters to the looks and isalive functions


UINT32 HmoRegister( HMO_HANDLE *newHandle, UINT32 (*looksAlive)( int ParamCount, 
   class='cKeyword'>char *ParamList[]), 
   UINT32 (*isAlive)( int ParamCount, class='cKeyword'>char *ParamList[]),
   int ParamCount, class='cKeyword'>char *ParamList[])
{

   UINT32 retVal;
   UINT32 pCount;
   int i, j, done;
   UINT8 tBuff[ 64], *pList[MAX_PARAMS], *aPtr;

   retVal = HMO_UNKNOWN;
   
   if( looksAlive == NULL || isAlive == NULL || newHandle == NULL){
      return HMO_BAD_PARAMETER;
   }
   memset( pList, 0, sizeof( pList));
   for( i=0; i < ParamCount; i++){
      pList[i] = ParamList[i];
   }
   pCount = ParamCount;
   pHmo = &anHmo;
   memset( pHmo, 0, sizeof( HMO_CONTEXT));
   *newHandle = ( HMO_HANDLE) pHmo;
   pHmo->curLooksAlive = looksAlive;
   pHmo->curIsAlive = isAlive;
   
// DEFAULTS

   
   pHmo->RecoveryFlag = RECOVER_MIGRATE;
   pHmo->FailFlag = RECOVER_OFFLINE;
   
   for( i =0; i < MAX_PARAMS; i++){
      if( pList[i] != NULL){
         aPtr = pList[i];
         if( *aPtr != 0){
            j = strlen( aPtr);
            if( j >=20 ){
               memcpy( pHmo->keywords[ pHmo->NumParams], aPtr, 19);
            }else{
               strcpy( pHmo->keywords[ pHmo->NumParams], aPtr);
            }
            pHmo->NumParams++;
         }
      }
   }

   ParseCommandHmo( pHmo, savedCommandLine);
   
   OutputToScreen( CScreen, MSG("params is=%d look=%d which=%s p1=%s p2=%s p3=%s p4=%s\r\n", 13),
      pHmo->IsWait, pHmo->LooksWait, pHmo->WhichResource, pHmo->params[0],    
      pHmo->params[1], pHmo->params[2], pHmo->params[3]);
   
   if( pHmo->WhichResource[0] != 0){
      sprintf( tBuff, MSG("Thd%s", 14), pHmo->WhichResource);

      pHmo->thisHmo = kStartThread( tBuff, HmoGaurdian, 0, 16000, pHmo);

// In order to ensure a large enough stack, all the NCS calls are done on the

// thread. This start thread waits for the resource to be found. If it doesn't

// happen with 20 seconds, abort the HMO.


      i = 0;
      done = FALSE;
      while( i < 40 && !done){
         if( pHmo->aRes.name[ 0] != 0){
            done = TRUE;
            continue;
         }else{
            delay( 500);
            i++;
         }
      }
      if( !done){
         OutputToScreen( CScreen, MSG("Cannot find resource %s.\n", 15), pHmo->WhichResource);
         retVal = HMO_NORESOURCE;
      }else{
         retVal = HMO_OK;
      }
   }else{
      retVal = HMO_NORESOURCE;
   }
   return retVal;   
   
}

//void HmoGaurdian( void *arg){

void HmoGaurdian( THREAD thr, void *arg){

   HMO_CONTEXT *pHmo;
   UINT32 retVal, done, looksCount, isCount;
   UINT8  *ppList[ MAX_PARAMS];
   UINT32 LookFailure;
   int i;

   pHmo = (HMO_CONTEXT *)arg;
   retVal = NCS_Register( myCrmCback, pHmo->WhichResource, myEpochCback, 
         NULL, NULL, &pHmo->sdkHandle);

   if( retVal == NCS_SUCCESS)
   {
      retVal = NCS_NodeNumber( pHmo->sdkHandle,  &pHmo->ThisNodeNum);
      pHmo->pRes = &pHmo->aRes;
      retVal = NCS_FindResource( pHmo->sdkHandle, pHmo->WhichResource, 1,
            pHmo->pRes);
      OutputToScreen( CScreen, MSG("Resource is %s Node=%d\r\n", 16), 
         pHmo->pRes->name, pHmo->ThisNodeNum);

      if( retVal != NCS_RESOURCE_FOUND){
         NCS_Deregister( pHmo->sdkHandle);
         pHmo->sdkHandle = 0;
         retVal = HMO_NORESOURCE;
      }else{
         delay( 1);
         pHmo->pRes->loadScript = pHmo->loadBuff;
         pHmo->pRes->unloadScript = pHmo->unloadBuff;
         retVal = NCS_ReadResource( pHmo->sdkHandle, pHmo->pRes, sizeof( pHmo->loadBuff));
         if( retVal != 0){
            NCS_Deregister( pHmo->sdkHandle);
            pHmo->sdkHandle = 0;
            retVal = HMO_NORESOURCE;
         }      
      }
      retVal = 0;
   }else{
      retVal = HMO_UNKNOWN;
   }
   if( retVal != 0){
      StopHmoApp( );
      DeadFlag = 1;
      return;
   }
   done = FALSE;
   isCount = 0;
   looksCount = 0;
   LookFailure = 0;
   memset( ppList, 0, sizeof( ppList));
   
   for( i = 0; i< pHmo->NumParams; i++){
      ppList[i] = pHmo->params[i];
   }
   
   while( !done){
      if( ShutDownFlag){
         done = TRUE;
      }
      
      if( isCount >= pHmo->IsWait){
         isCount = 0;
         looksCount = 0;
         LookFailure = 0;
         retVal = pHmo->curIsAlive( pHmo->NumParams, ppList);       
         if( retVal){
            retVal = MoveThis( pHmo);
            done = TRUE;
            continue;
         }      
      }
      if( looksCount >= pHmo->LooksWait){
//         debugFail++;

//         if( debugFail > 10){

//            retVal = MoveThis( pHmo);

//            done = TRUE;

//            continue;

//         }            

         looksCount = 0;
         retVal = pHmo->curLooksAlive( pHmo->NumParams, ppList); 
         if( retVal){
            LookFailure++;
            if( LookFailure > 1){
               isCount = 0;
               looksCount = 0;
               LookFailure = 0;
               retVal = pHmo->curIsAlive( pHmo->NumParams, ppList); 
               if( retVal){
                  retVal = MoveThis( pHmo);
                  done = TRUE;
                  continue;
               }
            }
         }      
      }
      isCount++;
      looksCount++;
      delay( 1000);
   }
   StopHmoApp();
   
// In order to be consistent with an offline of a working LDAP and a failed

// terminate, this thread can't go away until the exit is given

   while( !ShutDownFlag){
      delay(100);
   }
   DeadFlag = 1;
}

void ExpandNodeList( UINT32 Bitmap, UINT8 Expanded[32]){
   UINT32 i, j, onee;
   
   onee = 1;
   j = 0;
   memset( Expanded, -1, 32);
   for( i = 0; i < 32; i++){
      if( onee & Bitmap){
         Expanded[ j++] = i;
      }
      onee *= 2;
   }
}


UINT32 MoveThis( HMO_CONTEXT *pHmo){
   UINT32 retVal, tempUI, tempUI2;
   UINT8  nodeList[32];
   int i, j, k, m;
   
// Get the node list of who's active in the cluster   


   OutputToScreen( CScreen, MSG("Move %s recover=%d fail=%d \r\n", 17), pHmo->pRes->name, pHmo->RecoveryFlag,
      pHmo->FailFlag);

   if( pHmo->RecoveryFlag == RECOVER_OFFLINE ){
      pHmo->pRes->fromTo = -1;
      retVal = NCS_ResourceControl( pHmo->sdkHandle, NCS_MAPI_OFFLINE, pHmo->pRes,
                  NULL);
      if( retVal != 0){
         goto DO_FAILED_OPTION;
      }
      return retVal;
   }


   if( pHmo->RecoveryFlag == RECOVER_MIGRATE){
      retVal = NCS_Query_Epoch( pHmo->sdkHandle, &pHmo->GroupEpoch, &pHmo->NodeBitmap);
      tempUI = pHmo->NodeBitmap;
      tempUI2 = 1<< pHmo->ThisNodeNum;
      tempUI &= ~tempUI2;
      ExpandNodeList( tempUI, nodeList);
      if( tempUI != 0){         // there are other nodes active in the cluster

// do a round robin of the nodes to migrate to      

         j = -1;
         for( i = pHmo->ThisNodeNum+1; i < 32; i++){
            for( k = 0; k < 32; k++){
               if( nodeList[k] == i ){
                  for( m = 0; m < 32; m++){
                     if( pHmo->pRes->fail_order[m] == i){
                        j = i;
                        goto FOUND_NEXT;
                     }
                  }
               }
            }
         }
// start back at the beginning

         for( i = 0; i < pHmo->ThisNodeNum; i++){
            for( k = 0; k < 32; k++){
               if( nodeList[k] == i ){
                  for( m = 0; m < 32; m++){
                     if( pHmo->pRes->fail_order[m] == i){
                        j = i;
                        goto FOUND_NEXT;
                     }
                  }
               }
            }
         }
// No other node is suitable. Do the failed option

         goto DO_FAILED_OPTION;

      }
// No other nodes. Do the failed option


      goto DO_FAILED_OPTION;

FOUND_NEXT:;      // j has the node number

      pHmo->pRes->fromTo = j;
      OutputToScreen( CScreen, MSG("Migrate to %d\r\n", 18), j);
      retVal = NCS_ResourceControl( pHmo->sdkHandle, NCS_MAPI_MIGRATE, pHmo->pRes,
         NULL);

      // What to do if the migrate fails?

      OutputToScreen( CScreen, MSG("ret=%d ", 19), retVal);
      if( retVal){
         goto DO_FAILED_OPTION;
      }
      return( retVal);
   }
   if( pHmo->RecoveryFlag == RECOVER_ABEND){
      NWCLSTR_Abend( MSG("NWCS: HMO Abend on recover.\n", 20));
   }
   if( pHmo->RecoveryFlag == RECOVER_RESTART){
      RestartServer( NULL);
      return 0;
   }

DO_FAILED_OPTION:;
   OutputToScreen( CScreen, MSG("Doing failed option\r\n", 21));
   if( pHmo->FailFlag == RECOVER_OFFLINE || pHmo->FailFlag == 0 || 
            pHmo->FailFlag == RECOVER_MIGRATE){
      pHmo->pRes->fromTo = -1;
      retVal = NCS_ResourceControl( pHmo->sdkHandle, NCS_MAPI_OFFLINE, pHmo->pRes,
                  NULL);
      return retVal;
   }
   if( pHmo->FailFlag == RECOVER_ABEND){
      NWCLSTR_Abend( MSG("NWCS: HMO Abend on failure to recover.\n", 22));
   }
   if( pHmo->FailFlag == RECOVER_RESTART){
      RestartServer( NULL);
      return 0;
   }

   return retVal;
}

//*****************************************************************


void ParseCommandHmo( HMO_CONTEXT *pHmo, UINT8 *ptrCommandLine){
   int len, wordLen, done;
   UINT8 *aPtr, *bPtr, copyOverByte;
   int i, ii, j, newCount, j2, ii2, i2 ;


   newCount = 5 + pHmo->NumParams;
   for( i= 0; i < pHmo->NumParams; i++){
      parseStrs[ i+5] = pHmo->keywords[ i];
   }
   len = strlen( ptrCommandLine);
   aPtr = ptrCommandLine;

   OutputToScreen( CScreen, MSG("in Parse len=%d s=%s\r\n", 23), len, aPtr);   

   while( len > 0 && *aPtr == ' '){
      aPtr++;
      len--;
   }      
   bPtr = aPtr;
   wordLen = 0;
   while( len > 0 ){
      while( len > 0 && *aPtr != '/'){
         aPtr++;
         len--;
      }
      if( len >0){
         wordLen = 0;
         aPtr++;
         len--;
         bPtr = aPtr;
         done = FALSE;
         while( !done){
            if( len <= 0 || *bPtr == 0 || *bPtr == ' ' || *bPtr == '='){
               copyOverByte = *bPtr;
               *bPtr = 0;
               done = TRUE;
            }else{
               len--;
               bPtr++;      
               wordLen++;
            }
         }
         if( wordLen > 0){
            i = 0;
            while( i < newCount && parseStrs[ i] != NULL){
               ii = strlen( parseStrs[ i]);

               j = memicmp( aPtr, parseStrs[ i], ii);
               
               if( j == 0 && ii == wordLen ){
                  *bPtr = copyOverByte;
                  if( (i == 0 || i >4) && i < (MAX_PARAMS +5)){
                     if( len > 0){
                        bPtr++;
                        aPtr = bPtr;
                        len--;
                        wordLen = 0;
                        while( len >0 && *aPtr != ' '){
                           aPtr++;
                           len--;
                           wordLen++;
                        }
                        if( wordLen > 0){
                           *aPtr = 0;
                           if( i == 0 ){
                              if( wordLen >= 63){
                                 memcpy( pHmo->WhichResource, bPtr, 63);
                              }else{
                                 strcpy( pHmo->WhichResource, bPtr);
                              }
                           }else{
                              if( wordLen >= 79){
                                 memcpy( pHmo->params[ i-5], bPtr, 79);
                              }else{
                                 strcpy( pHmo->params[ i-5], bPtr);
                              }
                           }
                        }
                        bPtr = aPtr;
                     }
                     goto NEXT_PHRASE;
                  }else{
                     if( i >= 1 && i <= 4){
                                             // looks alive time

                                             // is alive time

                        if( len > 0){
                           bPtr++;
                           aPtr = bPtr;
                           len--;
                           wordLen = 0;
                           while( len >0 && *aPtr != ' '){
                              aPtr++;
                              len--;
                              wordLen++;
                           }
                           if( wordLen > 0){
                              *aPtr = 0;
                              switch( i){
                              class='cKeyword'>case 1:
                                 pHmo->LooksWait = atoi( bPtr);
                                 class='cKeyword'>break;
                              class='cKeyword'>case 2:
                                 pHmo->IsWait = atoi( bPtr);
                                 class='cKeyword'>break;
                              class='cKeyword'>case 3:
                              class='cKeyword'>case 4:
                                 i2 = 0;
                                 while( i2 < 4 && recoverStrs[ i2] != NULL){
                                    ii2 = strlen( recoverStrs[ i2]);

                                    j2 = memicmp( bPtr, recoverStrs[ i2], ii2);
               
                                    if( j2 == 0 && ii2 == wordLen ){
                                       i2++;
                                       if( i == 3){
                                          pHmo->RecoveryFlag = i2;
                                       }else{
                                          pHmo->FailFlag = i2;
                                       }
                                       class='cKeyword'>break;
                                    }
                                    i2++;
                                 }
                                 class='cKeyword'>break;
                              default:
                                 class='cKeyword'>break;
                              }
                           }
                           bPtr = aPtr;
                        }
                     }
                     goto NEXT_PHRASE;
                  }
               }
               i++;
            }
         }
      }
NEXT_PHRASE:;
      wordLen = 0;
   }
}