/*<ATGCOPYRIGHT>
 * Copyright (C) 1997 Art Technology Group, Inc.
 * All Rights Reserved.  No use, copying or distribution of this
 * work may be made except in accordance with a valid license
 * agreement from Art Technology Group.  This notice must be
 * included on all copies, modifications and derivatives of this
 * work.
 *
 * Art Technology Group (ATG) MAKES NO REPRESENTATIONS OR WARRANTIES 
 * ABOUT THE SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. ATG SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, 
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * "Dynamo" is a trademark of Art Technology Group, Inc.
 </ATGCOPYRIGHT>*/

/* 
   CMLDRP.c - routines for CMLDRP subsystem
   ("Connection-Module Load-Distribution Request Protocol", or something)
 */

#ifndef WIN32
/* NT */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif

/* #define DEBUG */

#include <stdio.h>              /* for sprintf */
#include <stdlib.h>

#include <string.h>

#include "DRPClient.h"
#include "CMLDRP.h"

#ifdef _DRP_MTHREAD
#include "threadabstr.h"
#endif

#define kSilentCMLDRPLogInterval    150 /* seconds */



/* static prototypes */
static int generateCMLDRPIdentifier(CMLDRP_Block * pBlock,
                                    unsigned short iHTTPPort);
static int dispatchResponseMetaData(CMLDRP_Block * pBlock,
                                    int iBytesReceived);
static int dispatchResponseData(CMLDRP_Block * pBlock, int iBytesReceived);
static int cmldrpUpdate(CMLDRP_Block * pBlock);
static int selectNextCMLDRPServer(CMLDRP_Block * pBlock);
static int internalizeCMLDRPServerInfo(CMLDRP_Block * pBlock,
                                       int iNumServers,
                                       const CMLDRP_ServerInfo * iServers);
static int openUDPSocket(CMLDRP_Block * pBlock, const char *iBindAddress);

/* NOTE: Currently not used so not declared. - Frank Kim, 12/28/99 (lmd 3/24/00) */
#if 0
static int compareDRPServerRecords_ipPort(const void *iRec1,
                                          const void *iRec2);
#endif

#ifdef _DRP_MTHREAD
static int connectToNextCMLDRPServer(CMLDRP_Block * pBlock);
static void closeCMLDRPConnection(CMLDRP_Block * pBlock);
static int startCMLDRPThread(CMLDRP_Block * pBlock);
static kThread_returnType cmldrpThreadFunction(kThread_argType iArg);

static Thread_t gCMLDRPThread = kThread_InvalidThread;
RWLock gLoadDataLock;

#define kDefaultMaxHandlers       100   /* a maximum of 100 handler threads  */
#define kDefaultHandlersTimeout   30000 /* wait at most 30 seconds to acquire mutex */
#define kDefaultBackoffInterval   5
/* wait at most 5 seconds once limit has exceeded before testing next time */
RWLock gHandlerCountLock;
int gEnforceMaxHandlers = 0;    /* By default do not enforce the max handlers */
int gMaxHandlers = kDefaultMaxHandlers;
int gHandlerCount = 0;
int gExceededLimit = 0;
time_t gBackoffInterval = kDefaultBackoffInterval;      /* in seconds */
time_t gLastTimeThreadLimitReported = 0;
time_t gLastTimeExceededLimitReached = 0;
#endif

/* global variables */

CMLDRP_Block gCMLDRPBlock;

drpServerRecord gDrpServers[kMaxDRPRecords];
drpServerRecord *gDrpServerPtrs[kMaxDRPRecords];
int gNumDrpServers;

DebugInfo gDebugInfo = { 0,     /* numRequestThreads */
    0,                          /* numActiveRequests */
    0,                          /* numWaitingOnReadLock */
    0,                          /* numWaitingOnPool */
    0,                          /* numWaitingToConnect */
    0,                          /* numSendingRequest */
    0
};                              /* numWaitingForResponse */

unsigned short gCMLDRP_Shutdown;
unsigned int gCMLDRP_InfoVersion;
unsigned short gCMLDRP_UpdateNow;
unsigned short gCMLDRP_ManagerRotationEnabled = 1;

#ifdef _DRP_MTHREAD
unsigned short gCMLDRP_UseTcp = 0;
int gCMLDRP_TcpTimeout = kCMLDRP_DefaultTcpTimeout;
unsigned short gCMLDRP_ThreadIsRunning = 0;
#endif

#ifdef _CPOOL
extern int gCP_Enabled;
#endif

/**
 * cmldrpInitialize
 * Initializes the CMLDRP subsystem and initiates the first attempt to
 *   obtain DRP server information from a CMLDRP server (in the multiprocess
 *   case) or starts the CMLDRP thread (in the multithreaded case).
 * iNumServers: number of CMLDRP server info blocks in iServers
 * iServers: array of CMLDRP_ServerInfo objects
 * iBindAddress: address to which CMLDRP subsystem should bind its socket
 *               (NULL: bind to any appropriate local address)
 * iHTTPPort: main service port of the host HTTP server (for purposes of
 *            identifying this CMLDRP client to the CMLDRP server).
 * iLogFunction: http-server-API-specific logging routine
 * iLogShellData: opaque shellData object to be passed to iLogFunction
 * return: 0 == success; !0 == failure
 */
int
cmldrpInitialize(int iNumServers, CMLDRP_ServerInfo * iServers,
                 const char *iBindAddress, unsigned short iHTTPPort,
                 LogMessage_fn iLogFunction, void *iLogShellData)
{
    int returnValue = 0;

    memset(&gCMLDRPBlock, 0, sizeof(gCMLDRPBlock));

    gCMLDRPBlock.logFunction = iLogFunction;
    gCMLDRPBlock.logShellData = iLogShellData;

    gCMLDRPBlock.nextServerToContact = NULL;
    gCMLDRPBlock.lastTimeSilenceReported = 0;
    gCMLDRPBlock.sock = kInvalidSocket;

#ifdef _DRP_MTHREAD
    gCMLDRPBlock.inputStream = NULL;
    gCMLDRPBlock.outputStream = NULL;
#endif

    gCMLDRPBlock.timeout.tv_sec = kCMLDRP_DefaultTimeoutSec;
    gCMLDRPBlock.timeout.tv_usec = kCMLDRP_DefaultTimeoutUsec;

    gCMLDRPBlock.nextUpdateTime = time(NULL);

#ifdef _DRP_MTHREAD
    if (0 == gCMLDRP_UseTcp) {
        returnValue = openUDPSocket(&gCMLDRPBlock, iBindAddress);
        if (returnValue)
            goto CMLDRP_INIT_EXIT;
    }
#else
    returnValue = openUDPSocket(&gCMLDRPBlock, iBindAddress);
    if (returnValue)
        goto CMLDRP_INIT_EXIT;
#endif


    gCMLDRPBlock.httpPort = iHTTPPort;

    returnValue = generateCMLDRPIdentifier(&gCMLDRPBlock, iHTTPPort);
    if (returnValue)
        goto CMLDRP_INIT_EXIT;

    returnValue = internalizeCMLDRPServerInfo(&gCMLDRPBlock, iNumServers,
                                              iServers);
    if (returnValue)
        goto CMLDRP_INIT_EXIT;

    gCMLDRPBlock.nextServerToContactIdx = 0;

    gNumDrpServers = 0;

    gCMLDRPBlock.cmldrpVersion = kCMLDRP_RequestVersion;

    gCMLDRP_Shutdown = 0;
    gCMLDRP_UpdateNow = 0;
    gCMLDRP_InfoVersion = 0;

#ifdef _DRP_MTHREAD
    if (initializeRWLock(&gLoadDataLock)) {
        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Error,
                                 kLogMessage_CannotCreateRWLock, NULL,
                                 NULL);
        returnValue = -1;
        goto CMLDRP_INIT_EXIT;
    }
    if (initializeRWLock(&gHandlerCountLock)) {
        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Error,
                                 kLogMessage_CannotCreateRWLock, NULL,
                                 NULL);
        returnValue = -1;
        goto CMLDRP_INIT_EXIT;
    }
    returnValue = startCMLDRPThread(&gCMLDRPBlock);
#else
    cmldrpUpdate(&gCMLDRPBlock);        /* discard return value */
#endif

  CMLDRP_INIT_EXIT:

    return returnValue;
}

void cmldrpSetDebug(int flag)
{
    gCMLDRPBlock.debugEnabled = flag;
}

void cmldrpSetManagerRotationEnabled(int flag)
{
    gCMLDRP_ManagerRotationEnabled = flag;
}

#ifdef _DRP_MTHREAD
/**
 * startCMLDRPThread
 * starts up the CMLDRP client thread that will periodically refresh
 *   DRP server info for the entire DRP Client in the current process.
 * return: 0 == success; !0 == failure
 */
static int startCMLDRPThread(CMLDRP_Block * pBlock)
{
    int returnValue = 0;

    gCMLDRPThread =
        thread_startNewThread(kThread_defaultStackSize,
                              cmldrpThreadFunction,
                              (kThread_argType) pBlock);
    if (gCMLDRPThread == kThread_InvalidThread) {
        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Error,
                                 kLogMessage_CannotStartCMLDRPThread, NULL,
                                 NULL);
        returnValue = -1;
    }

    return returnValue;
}


/**
 * cmldrpThreadFunction
 * the routine run by the CMDLRP client thread.
 * iArg: pointer to a cmldrpBlock object (already initialized).
 */
static kThread_returnType cmldrpThreadFunction(kThread_argType iArg)
{
    CMLDRP_Block *cmldrpBlock = (CMLDRP_Block *) iArg;
    int result;
    short sleepy = 0;


#ifdef _DRP_MTHREAD
    gCMLDRP_ThreadIsRunning = 1;
#endif
    while (!gCMLDRP_Shutdown) {
        result = cmldrpUpdate(cmldrpBlock);
        if (result == 0)
            gCMLDRP_UpdateNow = 0;      /* clear force-update flag */
        else {
            /* update() failed; set next polltime to minimum polltime
               and update next manager to contact */
            cmldrpBlock->nextUpdateTime =
                time(NULL) + kCMLDRP_MinimumPollInterval;
            selectNextCMLDRPServer(cmldrpBlock);
        }

        sleepy = 1;
        while (sleepy) {
            thread_sleep(kCMLDRP_Sleeptime);
            if ((time(NULL) >= cmldrpBlock->nextUpdateTime)
                || gCMLDRP_UpdateNow || gCMLDRP_Shutdown)
                sleepy = 0;     /* i.e. terminate while (sleepy) loop, 
                                   return to top of while (! shutdown) loop */
        }
    }

#ifdef _DRP_MTHREAD
    gCMLDRP_ThreadIsRunning = 0;
#endif

    thread_return(0);
}
#else                           /* i.e. ndef _DRP_MTHREAD */

/**
 * cmldrpUpdate_MP    (_MP: multi-process)
 * If appropriate, forces CMLDRP subsystem to refresh DRP server information
 *   from the CMLDRP server.  for use in multi-process CMs.
 * return: 0 == success; !0 == failure
 */
int cmldrpUpdate_MP(CMLDRP_Block * pBlock)
{
    int returnValue = 0;
    int i = 0;

    /* Loop until we find an CMLDRP Server that is available. */
    while (((time(NULL) >= pBlock->nextUpdateTime) || gCMLDRP_UpdateNow)
           && (i < pBlock->numCMLDRPServers)) {
        returnValue = cmldrpUpdate(pBlock);
        if (returnValue == 0) {
            /* Success. */
            gCMLDRP_UpdateNow = 0;
            return 0;
        } else {
            /* Current CMLDRP Server is available.  Select next one. */
            pBlock->nextUpdateTime =
                time(NULL) + kCMLDRP_MinimumPollInterval;
            selectNextCMLDRPServer(pBlock);
            i++;
        }
    }

    return returnValue;
}
#endif


/**
 * internalizeCMLDRPServerInfo
 * Stores contact information for CMLDRP servers in a CMLDRP_Block object
 *   (for use during the initialization phase).
 * return: 0 == success; !0 == failure
 */
static int
internalizeCMLDRPServerInfo(CMLDRP_Block * pBlock, int iNumServers,
                            const CMLDRP_ServerInfo * iServers)
{
    int returnValue = 0;
    int s;

    for (s = 0, pBlock->numCMLDRPServers = 0; s < iNumServers; s++) {
        pBlock->cmldrpServers[pBlock->numCMLDRPServers].sin_family =
            AF_INET;
        pBlock->cmldrpServers[pBlock->numCMLDRPServers].sin_port =
            htons(iServers[s].port);
        pBlock->cmldrpServers[pBlock->numCMLDRPServers].sin_addr.s_addr =
            hostToInetAddr(iServers[s].host);

        if (pBlock->cmldrpServers[pBlock->numCMLDRPServers].sin_addr.
            s_addr != INADDR_NONE)
            pBlock->numCMLDRPServers++;
        else {
            pBlock->logFunction(pBlock->logShellData, kLog_Error,
                                kLogMessage_CantResolveCMLDRPHost,
                                iServers[s].host, NULL);
        }
    }

    pBlock->nextServerToContact =
        &(pBlock->cmldrpServers[pBlock->nextServerToContactIdx]);

    if (pBlock->numCMLDRPServers < 1) {
        pBlock->logFunction(pBlock->logShellData, kLog_Error,
                            kLogMessage_NoGoodCMLDRPServers, NULL, NULL);
        returnValue = -1;
    }

    return returnValue;
}


/**
 * generateCMLDRPIdentifier
 * Construct an identification string that will uniquely identify this
 *   CMLDRP client entity
 * CMLDRP identifier has the form:
 *   <IP>:<port>:<thread/process id>:<opaque-id>
 *   where <IP> and <port> reflect the IP and port describing the host HTTP
 *     server; <thread/process id> is the id of the execution context
 *     of the CMLDRP client entity; <opaque-id> serves to uniquely identify
 *     CMLDRP entities across time.
 */
static int
generateCMLDRPIdentifier(CMLDRP_Block * pBlock, unsigned short iHTTPPort)
{
    int returnValue = 0;
    char hostname[MAXHOSTNAMELEN];
    struct hostent *hostinfo;
    struct in_addr inAddr;

    if (gethostname(hostname, MAXHOSTNAMELEN)) {
        returnValue = -1;
        goto GENERATE_CMLDRP_ID_EXIT;
    }

    hostinfo = gethostbyname(hostname);
    if (hostinfo) {
        memcpy(&inAddr, (void *) hostinfo->h_addr, hostinfo->h_length);
        pBlock->identifierLength = (char)
#if defined(SOLARIS2) && !defined(_LP64) && !defined(_I32LPx)
            sprintf(pBlock->identifier, "%s:%u:%ld:%ld",
#else
            sprintf(pBlock->identifier, "%s:%u:%d:%ld",
#endif
                    inet_ntoa(inAddr), iHTTPPort, getpid(), time(NULL));
    } else {
        returnValue = -1;
        goto GENERATE_CMLDRP_ID_EXIT;
    }

  GENERATE_CMLDRP_ID_EXIT:

    return returnValue;
}

/**
 * outputLength
 * The number of bytes waiting to be sent in the io buffer
 */
static int outputLength(CMLDRP_Block * pBlock)
{
    return (sizeof(pBlock->cmldrpVersion) +
            sizeof(pBlock->sequenceNumber) + 1 + pBlock->identifierLength);
}


/**
 * sendCmldrpRequest_Udp
 * Send a CMLDRP request via UDP
 * return: 0 == success; !0 == failure
 */
int sendCmldrpRequestUdp(CMLDRP_Block * pBlock)
{

    /* send request buffer */
    if (sendto(pBlock->sock, pBlock->ioBuffer,
               outputLength(pBlock),
               0,
               (struct sockaddr *) pBlock->nextServerToContact,
               sizeof(*(pBlock->nextServerToContact)))
        == SOCKET_ERROR) {
        return -1;
    }
    return 0;
}

#ifdef _DRP_MTHREAD
/**
 * sendCmldrpRequest_Tcp
 * Send a CMLDRP request via TCP
 * return: 0 == success; !0 == failure
 */
int sendCmldrpRequestTcp(CMLDRP_Block * pBlock)
{
    IOStatus status;
    int returnValue = 0;

    status = writeToOutputStream(pBlock->outputStream,
                                 pBlock->ioBuffer, outputLength(pBlock));
    if (status == IOStatusOk)
        status = flushOutputStream(pBlock->outputStream);

    if (IOStatusOk != status) {
        char buf[180];
        const char *ioStatusString;
        struct in_addr *inAddr;
        struct sockaddr_in *addr;

        addr = pBlock->nextServerToContact;
        inAddr = (struct in_addr *) &addr->sin_addr.s_addr;

        ioStatusString = getIOStatusString(status);

        sprintf(buf,
                "CMLDRP(TCP) Error writing to Load Manager=%s:%d, io status=%s",
                inet_ntoa(*inAddr), ntohs(addr->sin_port), ioStatusString);

        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Error,
                                 kLogMessage_OneString, buf, NULL);

        returnValue = -1;
    }
    return returnValue;
}
#endif

/**
 * checkResponseType
 * Check that the response type for this was set
 * return: 0 == success; !0 == failure
 */
static int checkResponseType(CMLDRP_Block * pBlock)
{
    unsigned short responseType;
    struct sockaddr_in *contactServer = pBlock->nextServerToContact;

    /* make sure the response has the response indicator set */
    memcpy(&responseType, &pBlock->ioBuffer[kCMLDRP_ResponseTypeOffset],
           sizeof(responseType));
    if (ntohs(responseType) != kCMLDRP_ResponseType) {
        char portNumber[8];
        struct in_addr *inAddr;

        sprintf(portNumber, "%d", ntohs(contactServer->sin_port));
        inAddr = (struct in_addr *) &contactServer->sin_addr.s_addr;
        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Warn,
                                 kLogMessage_BadCMLDRPResponseType,
                                 inet_ntoa(*inAddr), portNumber);
        return -1;
    }
    return 0;
}


/**
 * receiveCmldrpResponseUdp
 * Receive a CMLDRP response via UDP
 * return: 0 == success; !0 == failure
 */
int receiveCmldrpResponseUdp(CMLDRP_Block * pBlock)
{
    int returnValue = 0;
    fd_set readSet;
    int nfds;
    unsigned short requestSequence = pBlock->sequenceNumber;
    unsigned short responseSequence = !requestSequence;
    /*unsigned short requestType = kCMLDRP_RequestType; */
    struct sockaddr_in *contactServer = pBlock->nextServerToContact;

    do {
        /* receive into response buffer */
        FD_ZERO(&readSet);
        FD_SET(pBlock->sock, &readSet);
        nfds = (int) pBlock->sock + 1;
        if (select(nfds, &readSet, NULL, NULL, &pBlock->timeout) > 0) {
            /* ready to read */
            size_t serverAddrSize = sizeof(*(pBlock->nextServerToContact));
            int bytesReceived =
                recvfrom(pBlock->sock, pBlock->ioBuffer,
                         kCMLDRP_IOBufferSize, 0,
                         (struct sockaddr *) pBlock->nextServerToContact,
                         &serverAddrSize);
            if ((bytesReceived == SOCKET_ERROR) || (bytesReceived <= 0)) {
                returnValue = -1;
                goto CMLDRP_RESPONSE_EXIT;
            }

            returnValue = checkResponseType(pBlock);

            if (returnValue)
                goto CMLDRP_RESPONSE_EXIT;

            /* check sequence number - if it's a match, use the response data,
               otherwise discard and see if another packet arrived from the
               CMLDRP server */
            memcpy(&responseSequence,
                   &pBlock->ioBuffer[kCMLDRP_ResponseSequenceOffset],
                   sizeof(responseSequence));
            responseSequence = ntohs(responseSequence);
            if (responseSequence == requestSequence) {
                /* dispatchRepsonseMetaData: will internalize CMLDRP flags, next
                   CMLDRP server to contact, etc; returns !0 if OK to dispatch
                   remaining response data (i.e. DRP server contact info) */
                if (dispatchResponseMetaData(pBlock, bytesReceived)) {
                    returnValue =
                        dispatchResponseData(pBlock, bytesReceived);
                    if (returnValue == 0)
                        /* increment info version so service threads can see there's new
                           load data available */
                        gCMLDRP_InfoVersion++;
                }
            }
        } else {
            /* not ready to read */
            char portNumber[8];
            struct in_addr *inAddr;

            if (time(NULL) >
                pBlock->lastTimeSilenceReported +
                kSilentCMLDRPLogInterval) {
                /* we've been trying to contact this CMLDRP server for more than
                   k..Interval seconds (or this is the first failure) */
                sprintf(portNumber, "%d", ntohs(contactServer->sin_port));
                inAddr =
                    (struct in_addr *) &contactServer->sin_addr.s_addr;
                if (pBlock->lastTimeSilenceReported > 0)
                    gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData,
                                             kLog_Warn,
                                             kLogMessage_NoCMLDRPResponsePeriod,
                                             inet_ntoa(*inAddr),
                                             portNumber);
                else
                    gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData,
                                             kLog_Warn,
                                             kLogMessage_NoCMLDRPResponseFirst,
                                             inet_ntoa(*inAddr),
                                             portNumber);
                pBlock->lastTimeSilenceReported = time(NULL);
            }
            returnValue = -1;
            goto CMLDRP_RESPONSE_EXIT;
        }
    } while (responseSequence != requestSequence);

  CMLDRP_RESPONSE_EXIT:

    return returnValue;
}

#ifdef _DRP_MTHREAD
/**
 * receiveCmldrpResponseTcp
 * Send a CMLDRP response via TCP
 * return: 0 == success; !0 == failure
 */
int receiveCmldrpResponseTcp(CMLDRP_Block * pBlock)
{
    int returnValue = 0;
    IOStatus status;
    int read;
    int bytesReceived = 0;

    status = readFromInputStream(pBlock->inputStream,
                                 pBlock->ioBuffer,
                                 kCMLDRP_BaseResponseSize, &read);

    if (IOStatusOk == status) {
        unsigned short numRecords;
        int recordsLength;

        bytesReceived = kCMLDRP_BaseResponseSize;

        /* Calculate the number of Dynamo records expected */
        memcpy(&numRecords,
               &pBlock->ioBuffer[kCMLDRP_ResponseNumRecsOffset],
               sizeof(numRecords));
        numRecords = ntohs(numRecords);
        recordsLength = numRecords * kCMLDRP_LoadRecordSize;

        /* Read the Dynamo records */
        status = readFromInputStream(pBlock->inputStream,
                                     &pBlock->
                                     ioBuffer
                                     [kCMLDRP_ResponseRecordOffset],
                                     recordsLength, &read);
        if (IOStatusOk == status)
            bytesReceived += recordsLength;
    }

    if (IOStatusOk != status) {
        char buf[180];
        const char *ioStatusString = getIOStatusString(status);
        struct in_addr *inAddr;
        struct sockaddr_in *addr;

        addr = pBlock->nextServerToContact;
        inAddr = (struct in_addr *) &addr->sin_addr.s_addr;

        sprintf(buf,
                "CMLDRP(TCP) Error reading from Load Manager=%s:%d, io status=%s",
                inet_ntoa(*inAddr), ntohs(addr->sin_port), ioStatusString);

        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Error,
                                 kLogMessage_OneString, buf, NULL);

        returnValue = -1;
    }

    if (0 == returnValue)
        returnValue = checkResponseType(pBlock);

    if (0 == returnValue) {

#ifdef DEBUG
        {
            char buf[20];
            int i;

            for (i = 0; i < bytesReceived; i++) {
                sprintf(buf, "byte[%d] == %u", i, pBlock->ioBuffer[i]);
                gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData,
                                         kLog_Warn, kLogMessage_OneString,
                                         buf, NULL);
            }
        }
#endif

        if (dispatchResponseMetaData(pBlock, bytesReceived)) {
            returnValue = dispatchResponseData(pBlock, bytesReceived);
            if (returnValue == 0)
                /* increment info version so service threads can see there's new
                   load data available */
                gCMLDRP_InfoVersion++;
        }
    }

    return returnValue;;
}
#endif

/**
 * cmldrpUpdate
 * Attempt to update locally held DRP server load information by 
 *   contacting a CMLDRP server
 * return: 0 == success; !0 == failure
 */
int cmldrpUpdate(CMLDRP_Block * pBlock)
{
    int returnValue = 0;
    unsigned short requestSequence = pBlock->sequenceNumber;
    unsigned short requestType = kCMLDRP_RequestType;

#ifdef _DRP_MTHREAD
    if (gCMLDRP_UseTcp) {
        if (connectToNextCMLDRPServer(pBlock)) {
            returnValue = -1;
            goto CMLDRP_UPDATE_EXIT;
        }
    }
#endif

    /* set up request buffer */
    *((unsigned short *) &pBlock->ioBuffer[kCMLDRP_RequestVersionOffset]) =
        htons(pBlock->cmldrpVersion);
    *((unsigned short *) &pBlock->ioBuffer[kCMLDRP_RequestTypeOffset]) =
        htons(requestType);
    *((unsigned short *) &pBlock->
      ioBuffer[kCMLDRP_RequestSequenceOffset]) = htons(requestSequence);
    *((unsigned short *) &pBlock->
      ioBuffer[kCMLDRP_RequestIDLengthOffset]) =
htons(pBlock->identifierLength);
    strncpy(&(pBlock->ioBuffer[kCMLDRP_RequestIDOffset]),
            pBlock->identifier, pBlock->identifierLength);

#ifdef _DRP_MTHREAD
    if ((gCMLDRP_UseTcp == 0) ?
        sendCmldrpRequestUdp(pBlock) : sendCmldrpRequestTcp(pBlock))
#else
    if (sendCmldrpRequestUdp(pBlock))
#endif
    {
        returnValue = -1;
        goto CMLDRP_UPDATE_EXIT;
    }
#ifdef _DRP_MTHREAD
    if (0 == gCMLDRP_UseTcp)
        returnValue = receiveCmldrpResponseUdp(pBlock);
    else
        returnValue = receiveCmldrpResponseTcp(pBlock);
#else
    returnValue = receiveCmldrpResponseUdp(pBlock);
#endif

  CMLDRP_UPDATE_EXIT:

#ifdef _DRP_MTHREAD
    if (gCMLDRP_UseTcp)
        closeCMLDRPConnection(pBlock);
#endif

    pBlock->sequenceNumber++;

    return returnValue;
}


/**
 * dispatchResponseMetaData
 * parse general information from the CMLDRP server's response (i.e. only
 *   information not pertaining to DRP servers).
 * return: 0 == do NOT dispatch remaining response data; 
 *         >0 == do dispatch remaining response data;
 *         <0 == error
 */
static int
dispatchResponseMetaData(CMLDRP_Block * pBlock, int iBytesReceived)
{
    int returnValue = 0;
    unsigned short cmldrpFlags;
    unsigned long nextManagerHost;
    unsigned short nextManagerPort;
    unsigned short numRecords;

    if (iBytesReceived < kCMLDRP_BaseResponseSize) {
        returnValue = -1;

#ifdef DEBUG
        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Info,
                                 kLogMessage_OneString,
                                 "dispatchResponseMetaData received to view bytes",
                                 NULL);
#endif
        goto DISPATCH_META_DATA_EXIT;
    }

    /* extract cmldrp flags from response */
    memcpy(&cmldrpFlags, &pBlock->ioBuffer[kCMLDRP_ResponseFlagsOffset],
           sizeof(cmldrpFlags));
    cmldrpFlags = ntohs(cmldrpFlags);

    /* extract next-manager-to-contact info from response */
    memcpy(&nextManagerHost,
           &pBlock->ioBuffer[kCMLDRP_ResponseNextHostOffset],
           sizeof(nextManagerHost));
    memcpy(&nextManagerPort,
           &pBlock->ioBuffer[kCMLDRP_ResponseNextPortOffset],
           sizeof(nextManagerPort));
    nextManagerPort = ntohs(nextManagerPort);

    /* extract number of DRP records and check against bytes received */
    memcpy(&numRecords, &pBlock->ioBuffer[kCMLDRP_ResponseNumRecsOffset],
           sizeof(numRecords));
    numRecords = ntohs(numRecords);

    if ((numRecords * kCMLDRP_LoadRecordSize + kCMLDRP_BaseResponseSize !=
         iBytesReceived) || (numRecords > kMaxDRPRecords)) {
        returnValue = -1;

#ifdef DEBUG
        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Info,
                                 kLogMessage_OneString,
                                 "dispatchResponseMetaData bad load record size",
                                 NULL);
#endif
        goto DISPATCH_META_DATA_EXIT;
    }

    /* set some local fields w/ response data */
    pBlock->cmldrpFlags = cmldrpFlags;

    /*
     * This is an option put in for kodak, but it might be useful generally.
     * If this option is set to false, we ignore the load manager that the
     * current LM wants us to poll and just keep on using our own heuristics.  
     * This feature is there so that if we have more than one load manager, we 
     * can revert back to using the original primary after it comes back up.
     * 
     * Kodak, though, has a proxy server so the IP address that the LM thinks
     * it is, is not the right one.
     */
    if (gCMLDRP_ManagerRotationEnabled) {
        /* set next CMLDRP server to contact */
        pBlock->tempCMLDRPServer.sin_family = AF_INET;
        pBlock->tempCMLDRPServer.sin_port = ntohs(nextManagerPort);
        pBlock->tempCMLDRPServer.sin_addr.s_addr = nextManagerHost;
        if (memcmp(pBlock->nextServerToContact, &pBlock->tempCMLDRPServer,
                   sizeof(pBlock->tempCMLDRPServer))) {
            /* only reset lastTimeSilenceReported if nextServerToContact changes */
            pBlock->lastTimeSilenceReported = 0;
        }
        pBlock->nextServerToContact = &pBlock->tempCMLDRPServer;
    }

    /* if CMLDRP server indicates it is finished w/ initialization, return 
       >0, to force internalization of DRP server response data */
    if (cmldrpFlags & kCMLDRPFlags_Initialized)
        returnValue = 1;
    else {
        /* CMLDRP server still initializing; set next polltime to minimum
           polltime */
        pBlock->nextUpdateTime = time(NULL) + kCMLDRP_MinimumPollInterval;

#ifdef DEBUG
        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Info,
                                 kLogMessage_OneString,
                                 "setting nextUpdateTime - not initialized",
                                 NULL);
#endif
    }

  DISPATCH_META_DATA_EXIT:

    return returnValue;
}



/**
 * dispatchResponseData
 * Copy data from a CMLDRP server's response into local structures
 * return: 0 == success; !0 == failure
 */
static int dispatchResponseData(CMLDRP_Block * pBlock, int iBytesReceived)
{
    int returnValue = 0;
    unsigned short cmldrpVersion;
    unsigned short pollInterval;
    unsigned short numRecords;
    int l;
    int recordOffset;
    int r;

    if (iBytesReceived < kCMLDRP_BaseResponseSize) {
        returnValue = -1;
        goto DISPATCH_RESP_DATA_EXIT;
    }

    memcpy(&cmldrpVersion,
           &pBlock->ioBuffer[kCMLDRP_ResponseVersionOffset],
           sizeof(cmldrpVersion));
    cmldrpVersion = ntohs(cmldrpVersion);

    memcpy(&pollInterval,
           &pBlock->ioBuffer[kCMLDRP_ResponseIntervalOffset],
           sizeof(pollInterval));
    pollInterval = ntohs(pollInterval);

    memcpy(&numRecords, &pBlock->ioBuffer[kCMLDRP_ResponseNumRecsOffset],
           sizeof(numRecords));
    numRecords = ntohs(numRecords);

    pBlock->pollInterval = pollInterval;
    pBlock->nextUpdateTime = time(NULL) + pollInterval;

#ifdef _DRP_MTHREAD
    {
        int result;
        do {
            result =
                getWriteLock(&gLoadDataLock, kRWL_DefaultWriteTimeout);
        } while (result != 0 && !gCMLDRP_Shutdown);
        if (gCMLDRP_Shutdown)
            return 0;
    }
#endif

    /* at this point we've decided the data packet is legit */
    /* mark all known servers inactive; after we've absorbed all the
       per-drp-server data (below), all servers marked as active will have
       data from the CMLDRP server, and any servers not mentioned by 
       CMLDRP server will be marked inactive */
    for (r = 0; r < gNumDrpServers; r++) {
        gDrpServers[r].flags &= ~kDrpServerFlags_Active;
    }

    for (l = 0, recordOffset = kCMLDRP_ResponseRecordOffset;
         l < numRecords; l++, recordOffset += kCMLDRP_LoadRecordSize) {
        drpServerRecord record;
        drpServerRecord *recordPtr;
        drpServerRecord **match;

        memset(&record, 0, sizeof(record));

        memcpy(&record.inetaddr,
               &pBlock->ioBuffer[recordOffset + kCMLDRP_RecordHostOffset],
               sizeof(record.inetaddr));
        memcpy(&record.port,
               &pBlock->ioBuffer[recordOffset + kCMLDRP_RecordPortOffset],
               sizeof(record.port));
        record.port = ntohs(record.port);
        memcpy(&record.probability,
               &pBlock->ioBuffer[recordOffset + kCMLDRP_RecordProbOffset],
               sizeof(record.probability));
        record.probability = ntohs(record.probability);
        memcpy(&record.flags,
               &pBlock->ioBuffer[recordOffset + kCMLDRP_RecordFlagsOffset],
               sizeof(record.flags));
        record.flags = ntohs(record.flags);
        /*
         * Mark this server as active.  We don't have 
         */
        record.flags |= kDrpServerFlags_Active;

        /* look up record in list of record ptrs */
        recordPtr = &record;
        match = bsearch(&recordPtr, gDrpServerPtrs, gNumDrpServers,
                        sizeof(recordPtr), compareDRPServerRecPtrs_ipPort);
        if (match) {
            (*match)->probability = record.probability;
            (*match)->flags = record.flags;
        } else {
            drpServerRecord *newServer = &(gDrpServers[gNumDrpServers]);
            newServer->probability = record.probability;
            newServer->flags = record.flags;
            newServer->inetaddr = record.inetaddr;
            newServer->port = record.port;
            newServer->sock = kInvalidSocket;

            gDrpServerPtrs[gNumDrpServers] = newServer;

            gNumDrpServers++;

            /* must now sort new record into the list of pointers */
            qsort(gDrpServerPtrs, gNumDrpServers, sizeof(recordPtr),
                  compareDRPServerRecPtrs_ipPort);

#ifdef _CPOOL
            /* initialize the connection pool for this server */
            if (gCP_Enabled) {
                newServer->pool = malloc(sizeof(connectionPool));
                initializeCP(newServer->pool);
            }
#endif
        }
    }

#ifdef _DRP_MTHREAD
    releaseWriteLock(&gLoadDataLock);
#endif

    if (gCMLDRPBlock.debugEnabled) {
        char buf[1024];
        int i;

        if (pBlock->nextServerToContact != NULL) {
            struct in_addr *inAddr =
                (struct in_addr *) &pBlock->nextServerToContact->sin_addr.
                s_addr;
            sprintf(buf,
                    "updating %d servers - next lm=%s:%d%s - num request threads=%d",
                    gNumDrpServers, inet_ntoa(*inAddr),
                    ntohs(pBlock->nextServerToContact->sin_port),
                    (gCMLDRP_ManagerRotationEnabled ==
                     0) ? " (load manager rotation disabled)" : "",
                    gDebugInfo.numRequestThreads);
        } else
            sprintf(buf,
                    "updating %d servers - next lm=null - num request threads=%d",
                    gNumDrpServers, gDebugInfo.numRequestThreads);
        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Info,
                                 kLogMessage_OneString, buf, NULL);

        sprintf(buf,
                "num active requests=%d - num waiting on lock=%d - num waiting on pool=%d - num waiting to connect=%d - num sending request=%d, num waiting for response=%d",
                gDebugInfo.numActiveRequests,
                gDebugInfo.numWaitingOnReadLock,
                gDebugInfo.numWaitingOnPool,
                gDebugInfo.numWaitingToConnect,
                gDebugInfo.numSendingRequest,
                gDebugInfo.numWaitingForResponse);
        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Info,
                                 kLogMessage_OneString, buf, NULL);

        for (i = 0; i < gNumDrpServers; i++) {
            sprintf(buf, "server %ld.%ld.%ld.%ld port=%d prob=%d flags=%d",
                    gDrpServerPtrs[i]->inetaddr & 0xff,
                    (gDrpServerPtrs[i]->inetaddr >> 8) & 0xff,
                    (gDrpServerPtrs[i]->inetaddr >> 16) & 0xff,
                    (gDrpServerPtrs[i]->inetaddr >> 24) & 0xff,
                    gDrpServerPtrs[i]->port,
                    gDrpServerPtrs[i]->probability,
                    gDrpServerPtrs[i]->flags);
            gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Info,
                                     kLogMessage_OneString, buf, NULL);
        }
    }

  DISPATCH_RESP_DATA_EXIT:

    return returnValue;
}


/**
 * compareDRPServerRecords_ipPort
 * (for use with qsort, bsearch)
 * compares two drpServerRecord objects first by IP address, then by port.
 * return: <0 : iRec1 <  iRec2
 *          0 : iRec1 == iRec2
 *         >0 : iRec1 >  iRec2
 */
/* NOTE: Currently not used so not defined. - Frank Kim, 12/28/99 (lmd 3/24/00)*/
#if 0
int compareDRPServerRecords_ipPort(const void *iRec1, const void *iRec2)
{
    drpServerRecord *rec1 = (drpServerRecord *) iRec1;
    drpServerRecord *rec2 = (drpServerRecord *) iRec2;

    if (rec1->inetaddr < rec2->inetaddr)
        return -1;
    if (rec1->inetaddr > rec2->inetaddr)
        return 1;

    if (rec1->port < rec2->port)
        return -1;
    if (rec1->port > rec2->port)
        return 1;

    return 0;
}
#endif

/**
 * compareDRPServerRecPtrs_ipPort
 * (for use with qsort, bsearch)
 * compares two _pointers to_ drpServerRecord objects first by IP address, 
 *   then by port.
 * return: <0 : *iRec1 <  *iRec2
 *          0 : *iRec1 == *iRec2
 *         >0 : *iRec1 >  *iRec2
 */
int compareDRPServerRecPtrs_ipPort(const void *iPtr1, const void *iPtr2)
{
    drpServerRecord **ptr1 = (drpServerRecord **) iPtr1;
    drpServerRecord **ptr2 = (drpServerRecord **) iPtr2;

    if ((*ptr1)->inetaddr < (*ptr2)->inetaddr)
        return -1;
    if ((*ptr1)->inetaddr > (*ptr2)->inetaddr)
        return 1;

    if ((*ptr1)->port < (*ptr2)->port)
        return -1;
    if ((*ptr1)->port > (*ptr2)->port)
        return 1;

    return 0;
}


/**
 * openUDPSocket
 * Establish a UDP datagram socket for use in obtaining load information
 *   from CMLDRP server(s).
 * pBlock: CMLDRP information block
 * iBindAddress: if present, bind the datagram socket to the address 
 *               indicated; if NULL, bind to any appropriate network interface
 * return: 0 == success; !0 == failure
 */
static int openUDPSocket(CMLDRP_Block * pBlock, const char *iBindAddress)
{
    int returnValue = 0;
    struct sockaddr_in clientAddr;      /* host of current process */

    if ((pBlock->sock = socket(AF_INET, SOCK_DGRAM, 0)) == kInvalidSocket) {
        returnValue = -1;
        goto OPEN_UDP_EXIT;
    }

    memset(&clientAddr, 0, sizeof(clientAddr));
    clientAddr.sin_family = AF_INET;
    clientAddr.sin_port = htons(0);
    if (iBindAddress) {
        /* Parse out the potential "name:port" value */
        char tmpAddress[256];
        char *port;
        strcpy(tmpAddress, iBindAddress);
        port = strchr(tmpAddress, ':');
        if (port != NULL) {
            *port = '\0';
            port++;
            clientAddr.sin_port = (short) atoi(port);
            clientAddr.sin_port = htons(clientAddr.sin_port);
        }
        /* bind to specific address (for multi-homed hosts) */
        clientAddr.sin_addr.s_addr = hostToInetAddr(tmpAddress);

        {
            char buf[256];
            sprintf(buf, "Binding to specified local address: %s:%d",
                    tmpAddress, (int) ntohs(clientAddr.sin_port));
            pBlock->logFunction(pBlock->logShellData, kLog_Info,
                                kLogMessage_OneString, buf, NULL);
        }
    } else {
        /* bind to any local address (for single-homed hosts, or if user d/n
           care which network interface is used) */
        clientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    }

    if (bind(pBlock->sock, (struct sockaddr *) &clientAddr,
             sizeof(clientAddr))) {
        returnValue = -1;
        goto OPEN_UDP_EXIT;
    }

  OPEN_UDP_EXIT:

    return returnValue;
}

#ifdef _DRP_MTHREAD
/**
 * connectToNextCMLDRPServer
 * Connect to a CMLDRP server using TCP
 * pBlock: CMLDRP information block
 * return: 0 == success; !0 == failure
 */
int connectToNextCMLDRPServer(CMLDRP_Block * pBlock)
{
    struct sockaddr_in *addr = pBlock->nextServerToContact;
    IOStatus status;
    int returnValue = 0;

#ifdef WIN32
    status = createSocketIOStreamsFromAddressWithTimeout((*addr),
                                                         gCMLDRP_TcpTimeout,
                                                         &(pBlock->
                                                           inputStream),
                                                         &(pBlock->
                                                           outputStream));
#else
    status = createSocketIOStreamsFromAddressWithBlocking((*addr),
                                                          &(pBlock->
                                                            inputStream),
                                                          &(pBlock->
                                                            outputStream));
#endif

    if (IOStatusOk != status ||
        pBlock->inputStream == NULL || pBlock->outputStream == NULL) {

        if (time(NULL) >
            pBlock->lastTimeSilenceReported + kSilentCMLDRPLogInterval) {
            char buf[180];
            const char *ioStatusString = getIOStatusString(status);
            struct in_addr *inAddr;

            inAddr = (struct in_addr *) &addr->sin_addr.s_addr;

            sprintf(buf,
                    "CMLDRP(TCP) Failure to connect via TCP to Load Manager=%s:%d, io status=%s",
                    inet_ntoa(*inAddr),
                    ntohs(addr->sin_port), ioStatusString);

            gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData, kLog_Error,
                                     kLogMessage_OneString, buf, NULL);
            pBlock->lastTimeSilenceReported = time(NULL);
        }
        returnValue = -1;
    }

    return returnValue;
}

/**
 * closeCMLDRPConnection
 * Close a TCP connection to a CMLDRP server
 * pBlock: CMLDRP information block
 */
void closeCMLDRPConnection(CMLDRP_Block * pBlock)
{

    if (pBlock->inputStream)
        destroyInputStream(pBlock->inputStream);

    if (pBlock->outputStream)
        destroyOutputStream(pBlock->outputStream);
    return;
}
#endif

/**
 * selectNextCMLDRPServer
 * Determines which known CMLDRP server should be next to contact; to be
 *   invoked in case of lack of response from the current CMLDRP server.
 * return: 0 == success; !0 == failure
 */
int selectNextCMLDRPServer(CMLDRP_Block * pBlock)
{
    int returnValue = 0;

#ifdef DEBUG
    char dbgBuf[64];
    sprintf(dbgBuf, "num of CMLDRPServers: %d", pBlock->numCMLDRPServers);
    pBlock->logFunction(pBlock->logShellData, kLog_Info,
                        kLogMessage_OneString, dbgBuf, NULL);
    sprintf(dbgBuf, "next Server to Contact index: %d",
            pBlock->nextServerToContactIdx);
    pBlock->logFunction(pBlock->logShellData, kLog_Info,
                        kLogMessage_OneString, dbgBuf, NULL);
#endif

    pBlock->nextServerToContactIdx =
        (pBlock->nextServerToContactIdx + 1) % pBlock->numCMLDRPServers;
    if (pBlock->numCMLDRPServers > 1)
        pBlock->lastTimeSilenceReported = 0;

    pBlock->nextServerToContact =
        &(pBlock->cmldrpServers[pBlock->nextServerToContactIdx]);

    return returnValue;
}


/**
 * getDrpServerRecord
 * Given a list of DRP server records and an address-port combination,
 *   finds the server record (if any) in the list with that addr-port.
 * return: NULL if no matching record found
 */
drpServerRecord **getDrpServerRecord(const drpServerRecord ** iServerPtrs,
                                     int iNumServers, unsigned long iAddr,
                                     unsigned short iPort)
{
    drpServerRecord record;
    drpServerRecord *recordPtr = &record;

    memset(&record, 0, sizeof(record));
    record.inetaddr = iAddr;
    record.port = iPort;

    return bsearch(&recordPtr, iServerPtrs, iNumServers, sizeof(recordPtr),
                   compareDRPServerRecPtrs_ipPort);
}


/**
 * parseDynamoManagers
 * Given list of Dynamo Managers (i.e. CMDLRP servers), builds and returns
 *   a list of CMLDRP_ServerInfo objects containing contact information for
 *   the CMLDRP servers specified.
 * iDynamoManagers: expected format is:
 *                  {host}:{port},{host}:{port},...
 * oNumServers: on exit, contains the number of server records in the 
 *              list returned.
 *
 * NB: modifies iDynamoManagers in place
 * NB: allocates data
 */
CMLDRP_ServerInfo *parseDynamoManagers(char *iDynamoManagers,
                                       int *oNumServers)
{
    int numCommas;
    char *tempPtr;
    char *nextPtr;
    char *portPtr;
    CMLDRP_ServerInfo *serverData = NULL;

    /* first: get max number of servers in list by counting commas */
    for (tempPtr = iDynamoManagers, numCommas = 0; tempPtr;) {
        tempPtr = strchr(tempPtr, kChar_Comma);
        if (tempPtr) {
            tempPtr++;
            numCommas++;
        }
    }

    serverData = (CMLDRP_ServerInfo *)
        malloc(sizeof(CMLDRP_ServerInfo) * (numCommas + 1));

    /* second: populate the server records */

    tempPtr = iDynamoManagers;
    *oNumServers = 0;

    while (tempPtr && (*tempPtr != kChar_Null)) {
        nextPtr = strchr(tempPtr, kChar_Comma);
        if (nextPtr)
            *nextPtr++ = kChar_Null;    /* terminate this host[:port] entry */

        portPtr = strchr(tempPtr, kChar_Colon);
        if (portPtr)
            *portPtr++ = kChar_Null;    /* terminate this host entry */

        serverData[*oNumServers].host = (char *)
            malloc(sizeof(char) * (strlen(tempPtr) + 1));
        strcpy(serverData[*oNumServers].host, tempPtr);
        serverData[*oNumServers].port =
            (portPtr ? atoi(portPtr) : kCMLDRP_DefaultCMLDRPPort);

        (*oNumServers)++;

        tempPtr = nextPtr;
    }

    return serverData;
}


/**
 * cmldrpShutdown()
 * Cause the CMLDRP thread, if active, to stop
 *
 * On ISAPI, the inetinfo process would die during shutdown sometimes
 * without the loop and the IsRunning flag.  This seems to fix this problem.
 */
void cmldrpShutdown(void)
{
    gCMLDRP_Shutdown = 1;

#ifdef _DRP_MTHREAD
    do {
        thread_sleep(kCMLDRP_Sleeptime * 2);
    } while (gCMLDRP_ThreadIsRunning);
#endif
}

#ifdef _DRP_MTHREAD
void setEnforceMaxHandlers(int flag)
{
    gEnforceMaxHandlers = flag;
}

void setMaxHandlers(int count)
{
    gMaxHandlers = count;
}

void setBackoffInterval(time_t interval)
{
    gBackoffInterval = interval;
}

/**
 * Returns 0 if this thread can be a handler, otherwize
 * it can return non-zero 
 */
int acquireHandler()
{
    int returnValue = -1;

    if (!gEnforceMaxHandlers) {
        return 0;
    } else {
        if (0 == getWriteLock(&gHandlerCountLock, kDefaultHandlersTimeout)) {

            if (gExceededLimit) {
                time_t now = time(NULL);

                /* Log an error at intervals */
                if (now >
                    gLastTimeThreadLimitReported +
                    kSilentCMLDRPLogInterval) {
                    char threadLimitReached[80];

                    sprintf(threadLimitReached,
                            "Dymamo handler limit reached = %d, backoff interval = %d seconds",
                            gMaxHandlers, gBackoffInterval);
                    gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData,
                                             kLog_Error,
                                             kLogMessage_OneString,
                                             threadLimitReached, NULL);
                    gLastTimeThreadLimitReported = now;
                }

                /* At intervals check to re-test the exceeded limit */
                /* Log an error at intervals */
                if (now >
                    (gLastTimeExceededLimitReached + gBackoffInterval)) {
                    if (gLastTimeExceededLimitReached > 0) {
                        char limitOver[80];
                        sprintf(limitOver,
                                "Dymamo backoff interval over. limit = %d, backoff interval = %d seconds",
                                gMaxHandlers, gBackoffInterval);
                        gCMLDRPBlock.logFunction(gCMLDRPBlock.logShellData,
                                                 kLog_Info,
                                                 kLogMessage_OneString,
                                                 limitOver, NULL);
                        gExceededLimit = 0;
                    }
                }
            }

            if (!gExceededLimit) {
                if (gHandlerCount < gMaxHandlers) {
                    gHandlerCount++;
                    returnValue = 0;
                } else {
                    gLastTimeExceededLimitReached = time(NULL);
                    gExceededLimit = 1;
                }
            }

            releaseWriteLock(&gHandlerCountLock);
        }
    }
    return returnValue;
}

/**
 * Release a handler.
 */
void releaseHandler()
{
    if (gEnforceMaxHandlers != 0) {
        if (0 == getWriteLock(&gHandlerCountLock, kDefaultHandlersTimeout)) {
            gHandlerCount--;
            releaseWriteLock(&gHandlerCountLock);
        }
    }
}

#endif
