/*******************************************************************************
API: alarmClockFinalize

Description : Stops the timer driving the clock and delete the timer

Arguments In: 
    none
Arguments Out:
    none
Return Value:
    none
*******************************************************************************/
void alarmClockFinalize ( void )
{
    ClRcT                rc = CL_OK;

    /* Stop the clock
     */
    alarmClockStop();

    rc =  clTimerDelete(timerHandle);
    if (rc != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR,
                    "alarmClockFinalize(pid=%d): Timer delete failed: 0x%x\n", 
                    getpid(), rc);
    }

    rc = clTimerFinalize();
    if (rc != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_CRITICAL,
                    "alarmClockFinalize(pid=%d): Timer finalize failed:0x%x\n", 
                    getpid(), rc);
        return;
    }

}
/*******************************************************************************
API: alarmClockStop

Description : Stops the clock (i.e the timer that drives the clock)

Arguments In: 
    None

Arguments Out:
    none
Return Value:
    none
*******************************************************************************/
void alarmClockStop( void )
{
    ClRcT rc = CL_OK;
    if (alarmClock.clockRunning == CL_TRUE)
    {
        rc = clTimerStop(timerHandle);
        if (rc != CL_OK)
        {
            alarmClockLogWrite(CL_LOG_SEV_ERROR,
                    "alarmClockStop(pid=%d): Timer stop failed: 0x%x\n", 
                    getpid(), rc);
        }
        else
        {
            alarmClock.clockRunning = CL_FALSE;

            alarmClockLogWrite(CL_LOG_DEBUG,
                "alarmClockStop(pid=%d): Clock Stop successful\n", 
                getpid());
        }
    }
    else
    {
        alarmClockLogWrite(CL_LOG_DEBUG,
            "alarmClockStop(pid=%d): Clock already stopped\n", 
            getpid());
    }
}
/*
 * Create the section if it doesn't exist.
 * Called on becoming active
 */
SaAisErrorT alarmClockCkptActivate(SaCkptCheckpointHandleT ckptHdl, ClUint32T section_num)
{
    SaAisErrorT rc = SA_AIS_OK;
    SaCkptSectionIdT *id;

    if(section_num > NUM_SECTION_ID_MAPS)
        return SA_AIS_ERR_INVALID_PARAM;

    id = &sectionIdMap[section_num-1].sectionId;

    rc = clCkptSectionCheck(ckptHdl, (ClCkptSectionIdT*)id);
    if(rc != CL_OK)
    {
        if(CL_GET_ERROR_CODE(rc) == CL_ERR_NOT_EXIST)
        {
            rc = alarmClockCkptSectionCreate(ckptHdl, 1);
            if(rc == SA_AIS_OK)
            {
                alarmClockLogWrite(CL_LOG_SEV_INFO, 
                                   "alarmClockActivate: Section [%.*s] created successfully", 
                                   id->idLen, (const char *)id->id);
            }
        }
        else rc = SA_AIS_ERR_UNAVAILABLE; /* internal error */
    }
    else rc = SA_AIS_OK;

    if(rc != SA_AIS_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_INFO, "alarmClockActivate: Section operation on section [%d] "
                           "failed with [%#x]", section_num, rc);
    }
    
    return rc;
}
/*******************************************************************************
API: alarmClockRaiseAlarm

Description : Does the prescribed behavior on reaching an alarm state; i.e.
              raise an alarm or kill the process

Arguments In: 
    None

Arguments Out:
    none
Return Value:
    none
*******************************************************************************/
void alarmClockRaiseAlarm ( void )
{
    if (alarmClock.alarm.reaction == ALARM_REACTION_KILL)
    {
         alarmClockLogWrite(CL_LOG_SEV_NOTICE,
                 "alarmClockRaiseAlarm(pid=%d): killing the clock\n", getpid());

        /* kill the process running the clock
        * to force a failover
        */
        exit(alarmClock.clockId);
    }
    else if (alarmClock.alarm.reaction == ALARM_REACTION_TRAP)
    {
         alarmClockLogWrite(CL_LOG_SEV_NOTICE,
                 "alarmClockRaiseAlarm(pid=%d): raising an alarm not supported yet\n", getpid());
    }
    else
    {
         alarmClockLogWrite(CL_LOG_SEV_NOTICE,
                 "alarmClockRaiseAlarm(pid=%d): reaction value not set; not raising alarm\n",
                 getpid());

    }
}
/*******************************************************************************
Feature  API: alarmClockCkptRead

Description : 

This API will not attempt to validate arguments, the thinking being that 
this is a volitional act by the invoker of this API to ensure that invalid 
parameters can be handled gracefully by the subsystem

Arguments In: 
    1. ClCkptHdlT : ckpt handle
    2. section number
    3. data to read
    4. size of data to read 

Return Value:
    ClInt32Teger 0 if success, non zero if failure
*******************************************************************************/
ClRcT
alarmClockCkptRead (
    ClCkptHdlT     ckpt_hdl,
    ClInt32T       section_num,
    void*          data,
    ClInt32T       data_size )
{
    ClRcT       ret_code = CL_OK;
    ClUint32T   error_index;
    ClCharT     section_id_name[ CL_MAX_NAME_LENGTH ];
    ClCkptIOVectorElementT  io_vector;

    /*
     * Check if its in sync through hot standby update before reading
     */
    if((g_hot_standby & __HOT_STANDBY_ACTIVE))
        return CL_ERR_NO_OP;

    sprintf(section_id_name, "s%05d", section_num);
    io_vector.sectionId.id = (ClUint8T*)section_id_name;
    io_vector.sectionId.idLen = strlen(section_id_name);

    /* assign other fields of the IoVector
     */
    io_vector.dataBuffer  = data;
    io_vector.dataSize    = data_size;
    io_vector.dataOffset  = 0;
    io_vector.readSize    = 0;

    ret_code = clCkptCheckpointRead(ckpt_hdl, &io_vector, 1, &error_index );
    if (ret_code != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, 
                           "alarmClockCkptRead(pid=%d): (error_index=%d) "
                           "Failed to read section [%s]: 0x%x\n", 
                           getpid(), error_index, 
                           section_id_name, ret_code); 
        return ret_code;
    }
    else
    {
        if ( io_vector.readSize != data_size )
        {
            alarmClockLogWrite(CL_LOG_SEV_ERROR, 
                    "alarmClockCkptRead(pid=%d): read %d bytes; expected %d bytes\n", 
                   getpid(), (ClInt32T)io_vector.readSize, data_size);
        }
        else
        {
            alarmClockLogWrite(CL_LOG_SEV_DEBUG, 
                "alarmClockCkptRead(pid=%d): read %d bytes from %s\n", 
                getpid(), data_size, section_id_name); 
        }
    }

    return ret_code;
}
/*******************************************************************************
Feature  API: alarmClockCkptCallback

Description : 

Other API in this file are either completely generic or mostly so, the hot standby
feature cannot be generic as  the state needs to be conveyed to the application
and made "hot" so to speak. Here we are going to have to know internals of the 
alarm clock structure

Arguments In: 
    1. ClCkptHdlT : ckpt handle
    2. SaNameT *    ckpt_name being updated
    3. ClCkptIOVectorElementT   *io_vector containing updated checkpoint data (all sections)
    4. ClInt32T     number of sections within checkpoint
    5. ClPtrT       cookie; can be used to keep globals within the task

Return Value:
    ClInt32Teger 0 if success, non zero if failure
*******************************************************************************/
static ClRcT 
alarmClockCkptCallback( ClCkptHdlT              ckpt_hdl,
                        SaNameT                 *ckpt_name,
                        ClCkptIOVectorElementT  *io_vector,
                        ClUint32T               num_sections,
                        ClPtrT                  cookie )
{
    ClRcT       ret_code = CL_OK;
    ClInt32T    count;
    ClInt32T    pid = getpid();
    ClInt32T    data_size = sizeof(acClockT);
    ClUint32T section_index = 1; /*our section name apparently*/
    ClCkptSectionIdT target_section_id;

    target_section_id.id = (ClUint8T*)&section_index;
    target_section_id.idLen = sizeof(section_index);

    alarmClockLogWrite(CL_LOG_SEV_INFO, 
            "alarmClockCkptCallback(pid=%d): ckpt[%.*s] update received\n", 
            pid, ckpt_name->length, ckpt_name->value);
    for (count = 0; count < num_sections; count++)
    {
        /*
         * check if the received update is for the section of interest to us,
         * if it is update the section, 
         */
        if ((target_section_id.idLen == io_vector[count].sectionId.idLen) && 
            (memcmp(target_section_id.id, (ClCharT *)io_vector[count].sectionId.id, 
                    target_section_id.idLen) == 0))
        {
            if (io_vector[count].dataSize != data_size)
            {
                alarmClockLogWrite(CL_LOG_SEV_ERROR, 
                       "alarmClockCkptCallback(pid=%d): received %d bytes; expected %d bytes\n", 
                        pid, (ClInt32T)io_vector[count].readSize, (ClInt32T)sizeof(acClockT));
            }
            else
            {

                alarmClockLogWrite(CL_LOG_SEV_INFO, 
                        "alarmClockCkptCallback(pid=%d): received %d bytes from section %d\n", 
                        pid, (ClInt32T)io_vector[count].readSize, section_index); 

                alarmClockCopyHotStandby((acClockT*)io_vector[count].dataBuffer);
            }

            break;
        }
    }
    /*
     * ckpt in sync through hot standby update
     */
    if(!(g_hot_standby & HOT_STANDBY_ACTIVE))
        g_hot_standby |= HOT_STANDBY_ACTIVE;
    return ret_code;
}
/*******************************************************************************
Feature  API: alarmClockCkptCallback

Description : 

Other API in this file are either completely generic or mostly so, the hot standby
feature cannot be generic as  the state needs to be conveyed to the application
and made "hot" so to speak. Here we are going to have to know internals of the 
alarm clock structure

Arguments In: 
    1. ClCkptHdlT : ckpt handle
    2. SaNameT *    ckpt_name being updated
    3. ClCkptIOVectorElementT   *io_vector containing updated checkpoint data (all sections)
    4. ClInt32T     number of sections within checkpoint
    5. ClPtrT       cookie; can be used to keep globals within the task

Return Value:
    ClInt32Teger 0 if success, non zero if failure
*******************************************************************************/
static ClRcT 
alarmClockCkptCallback( ClCkptHdlT              ckpt_hdl,
                        SaNameT                 *ckpt_name,
                        ClCkptIOVectorElementT  *io_vector,
                        ClUint32T               num_sections,
                        ClPtrT                  cookie )
{
    ClRcT       ret_code = CL_OK;
    ClInt32T    count;
    ClInt32T    pid = getpid();
    ClInt32T    data_size = sizeof(acClockT);
    ClInt32T    section_id_len;
    ClCharT     section_id_name[ CL_MAX_NAME_LENGTH ];

    sprintf(section_id_name, "s00001");
    section_id_len = strlen(section_id_name);

    alarmClockLogWrite(CL_LOG_SEV_INFO, 
            "alarmClockCkptCallback(pid=%d): ckpt[%.*s] update received\n", 
            pid, ckpt_name->length, ckpt_name->value);
    for (count = 0; count < num_sections; count++)
    {
        /*
         * check if the received update is for the section of interest to us,
         * if it is update the section, 
         */
        if ((section_id_len == io_vector[count].sectionId.idLen) && 
            (strncmp(section_id_name, (ClCharT *)io_vector[count].sectionId.id, section_id_len) == 0))
        {
            if (io_vector[count].dataSize != data_size)
            {
                alarmClockLogWrite(CL_LOG_SEV_ERROR, 
                       "alarmClockCkptCallback(pid=%d): received %d bytes; expected %d bytes\n", 
                        pid, (ClInt32T)io_vector[count].readSize, (ClInt32T)sizeof(acClockT));
            }
            else
            {

                alarmClockLogWrite(CL_LOG_SEV_INFO, 
                        "alarmClockCkptCallback(pid=%d): received %d bytes from %s\n", 
                        pid, (ClInt32T)io_vector[count].readSize, section_id_name); 

                alarmClockCopyHotStandby((acClockT*)io_vector[count].dataBuffer);
            }

            break;
        }
    }
    /*
     * ckpt in sync through hot standby update
     */
    if(!(g_hot_standby & __HOT_STANDBY_ACTIVE))
        g_hot_standby |= __HOT_STANDBY_ACTIVE;
    return ret_code;
}
/*******************************************************************************
Feature  API: alarmClockCkptRead

Description : 

This API will not attempt to validate arguments, the thinking being that 
this is a volitional act by the invoker of this API to ensure that invalid 
parameters can be handled gracefully by the subsystem

Arguments In: 
    1. ClCkptHdlT : ckpt handle
    2. section number
    3. data to read
    4. size of data to read 

*******************************************************************************/
SaAisErrorT
alarmClockCkptRead (
    SaCkptCheckpointHandleT    ckpt_hdl,
    SaUint32T                  section_num,
    void*                      data,
    SaUint32T                  data_size )
{
    SaAisErrorT             ret_code = SA_AIS_OK;
    SaUint32T               error_index;
    SaCkptIOVectorElementT  io_vector;

   /*
     * If we are in sync via the hot-standby, no need to read
     */
    if((g_hot_standby & HOT_STANDBY_ACTIVE))
        return SA_AIS_ERR_NO_OP;
    
    
    io_vector.sectionId.id = (SaUint8T*)&section_num;
    io_vector.sectionId.idLen = sizeof(section_num);

    /* assign other fields of the IoVector
     */
    io_vector.dataBuffer  = data;
    io_vector.dataSize    = data_size;
    io_vector.dataOffset  = 0;
    io_vector.readSize    = 0;

    ret_code = saCkptCheckpointRead(ckpt_hdl, &io_vector, 1, &error_index );
    if (ret_code != SA_AIS_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptRead: (error_index=%d) Failed to read: 0x%x\n", error_index, ret_code); 
        goto finalizeRet;
    }
    else
    {
        if ( io_vector.readSize != data_size )
        {
            alarmClockLogWrite(CL_LOG_SEV_ERROR, 
                    "alarmClockCkptRead(pid=%d): read %d bytes; expected %d bytes\n", 
                   getpid(), (ClInt32T)io_vector.readSize, data_size);
        }
        else
        {
            alarmClockLogWrite(CL_LOG_SEV_DEBUG, 
                "alarmClockCkptRead(pid=%d): read %d bytes from section:%d\n", 
                getpid(), data_size, section_num); 
        }
    }
    
finalizeRet:
    return ret_code;
}
/*******************************************************************************
API: alarmClockInitialze

Description : Set the alarm clock to all zeroes, and the boolean variable to 
              indicate non-running clock

Arguments In: 
    none
Arguments Out:
    none
Return Value:
    none
*******************************************************************************/
ClRcT alarmClockInitialize ( void )
{
    ClRcT                rc = CL_OK;
    ClTimerTimeOutT      timeOut;

    alarmClock.clockTime.hour   = 0;
    alarmClock.clockTime.minute = 0;
    alarmClock.clockTime.second = 0;

    alarmClock.alarm.time.hour   = 0;
    alarmClock.alarm.time.minute = 0;
    alarmClock.alarm.time.second = 0;
    alarmClock.alarm.reaction    = ALARM_REACTION_NONE;

    alarmClock.clockId          = 0;;

    alarmClock.clockRunning = CL_FALSE;
    alarmClock.alarmSet     = CL_FALSE;

    /* one second in the clock is 50 millisecond of real time
     * as an enhancement this can be made provisionable as well
     */
    timeOut.tsSec = 1;
    timeOut.tsMilliSec = 0;

/*
    rc = clTimerInitialize(NULL);
    if (rc != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_CRITICAL,
                    "alarmClockInitialize(pid=%d): Timer initialize failed:0x%x\n", 
                    getpid(), rc);
        return rc;
    }
*/

    rc = clTimerCreate(timeOut, CL_TIMER_REPETITIVE, CL_TIMER_SEPARATE_CONTEXT,
                       (ClTimerCallBackT)&alarmClockAdvance, NULL, &timerHandle);
    if (rc != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_CRITICAL,
                    "alarmClockInitialize(pid=%d): Timer start failed: 0x%x\n", 
                    getpid(), rc);
    }

    alarmClockLogWrite(CL_LOG_DEBUG,
            "alarmClockInitialize(pid=%d): Clock Initialize successful\n", 
            getpid());

    return rc;
}
/*******************************************************************************
Feature  API: alarmClockCkptWrite

Description : 

This API will not attempt to validate arguments, the thinking being that 
this is a volitional act by the invoker of this API to ensure that invalid 
parameters can be handled gracefully by the subsystem

Arguments In: 
    1. ClCkptHdlT : ckpt handle
    2. section number
    3. data to write
    4. size of data to write 

*******************************************************************************/
SaAisErrorT  
alarmClockCkptWrite (
    SaCkptCheckpointHandleT ckpt_hdl,
    SaUint32T               section_num,
    void*                   data,
    SaUint32T               data_size )
{
    SaAisErrorT          ret_code = SA_AIS_OK; 
    SaCkptSectionIdT     *section_id = NULL;
    SaCkptSectionIdT     *ctrl_section_id = NULL;
    SaCkptIOVectorElementT *ioVecIter = ioVecs;
    ClUint32T errVecIndex = 0;

    if(section_num > NUM_SECTION_ID_MAPS || 
       (data_size != sizeof(acClockT)))
        return SA_AIS_ERR_INVALID_PARAM;

    data_size = sizeof(SessionT);
    section_id = &sectionIdMap[section_num-1].sectionId;
    ctrl_section_id = &sectionIdMap[section_num-1].ctrlSectionId;
    memset(ioVecs, 0, sizeof(*ioVecs) * MAX_NUM_IOVECS);
    SessionCtrlT *sessionCtrl = sessionAddCkpt((acClockT*)data, &ioVecIter, ctrl_section_id);
    CL_ASSERT(sessionCtrl != NULL);
    memcpy(&ioVecIter->sectionId, section_id, sizeof(ioVecIter->sectionId));
    ioVecIter->dataOffset = sessionCtrl->offset * data_size;
    ioVecIter->dataBuffer = &sessionCtrl->session;
    ioVecIter->readSize = ioVecIter->dataSize = sizeof(sessionCtrl->session);
    ++ioVecIter;
    ret_code = saCkptCheckpointWrite(ckpt_hdl, ioVecs, (ClUint32T)(ioVecIter - ioVecs),
                                     &errVecIndex);

    if (ret_code != SA_AIS_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptWrite(hdl=%llx): Failed to write: 0x%x "
                           "for session [%d] at offset [%d]. Error iovec index [%d]\n", 
                           ckpt_hdl, ret_code, sessionCtrl->session.id,
                           sessionCtrl->offset, errVecIndex); 
    }
    else
    {
        alarmClockLogWrite(CL_LOG_SEV_DEBUG, "alarmClockCkptWrite: wrote %d bytes to section: %.*s, "
                           "iovecs [%d] for session [%d] at offset [%d]", 
                           data_size, section_id->idLen, (char*)section_id->id,
                           (ClUint32T)(ioVecIter - ioVecs), 
                           sessionCtrl->session.id, sessionCtrl->offset); 
    }

    return ret_code;
}
SaAisErrorT alarmClockCkptSessionDelete(SaCkptCheckpointHandleT ckpt_hdl, 
                                        ClUint32T section_num,
                                        ClUint32T id)
{
    SaCkptIOVectorElementT *ioVecIter = ioVecs;
    SessionCtrlT ctrl;
    SaCkptSectionIdT *ctrl_section_id;
    SaCkptSectionIdT *section_id;
    SaUint32T errVecIndex = 0;
    SaAisErrorT rc = SA_AIS_OK;
    if(section_num > NUM_SECTION_ID_MAPS)
        return SA_AIS_ERR_INVALID_PARAM;
    ctrl_section_id = &sectionIdMap[section_num-1].ctrlSectionId;
    section_id = &sectionIdMap[section_num-1].sectionId;
    memset(ioVecs, 0, sizeof(*ioVecs) * MAX_NUM_IOVECS);
    rc = sessionDelCkpt(id, &ctrl, &ioVecIter, ctrl_section_id);
    if(rc != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptSessionDelete: "
                           "Session id [%d] delete failed with [%#x]", 
                           id, rc);
        rc = SA_AIS_ERR_NOT_EXIST;
        goto out;
    }
    /*
     * Write a delete entry by marking the session as deleted
     */
    memcpy(&ioVecIter->sectionId, section_id, sizeof(ioVecIter->sectionId));
    ioVecIter->dataOffset = ctrl.offset * sizeof(SessionT);
    ioVecIter->dataBuffer = &ctrl.session;
    ioVecIter->readSize = ioVecIter->dataSize = sizeof(ctrl.session);
    ++ioVecIter;
    rc = saCkptCheckpointWrite(ckpt_hdl, ioVecs, (ClUint32T)(ioVecIter - ioVecs), 
                               &errVecIndex);
    if(rc != SA_AIS_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptSessionDelete: "
                           "Session delete for id [%d] at offset [%d] failed with ckpt error [%#x]",
                           id, ctrl.offset, rc);
        goto out;
    }
    alarmClockLogWrite(CL_LOG_SEV_DEBUG, "alarmClockCkptSessionDelete: "
                       "Session delete success for id [%d] at offset [%d]",
                       id, ctrl.offset);

    out:
    return rc;
}
/*******************************************************************************
Feature  API: alarmClockCkptRead

Description : 

This API will not attempt to validate arguments, the thinking being that 
this is a volitional act by the invoker of this API to ensure that invalid 
parameters can be handled gracefully by the subsystem

Arguments In: 
    1. ClCkptHdlT : ckpt handle
    2. section number
    3. data to read
    4. size of data to read 

*******************************************************************************/
static void bitmapWalkCallback(BitmapT *bitmap, ClUint32T bit, void *arg)
{
    SaCkptIOVectorElementT *ioVec = arg;
    alarmClockLogWrite(CL_LOG_SEV_DEBUG, "alarmClockCkptRead(%d): Bit set at offset [%u]",
                       getpid(), bit);
    if(!ioVec) return;
    SessionT *session = (SessionT*)ioVec->dataBuffer + bit;
    ClUint32T readSize = (ClUint32T)ioVec->readSize;
    if( (bit * sizeof(SessionT)) >= readSize)
    {
        alarmClockLogWrite(CL_LOG_SEV_WARNING, "alarmClockCkptRead(%d): Bit [%d] "
                           "exceeds session size [%d]", getpid(), bit, readSize);
        return;
    }
    sessionUpdate(session, bit);
}
/*******************************************************************************
API: alarmClockHotStandbyCopy

Description : Copy the clock data structure; except for clock running status
              which on the standby should be "stopped"

Arguments In: 
    acClockT

Arguments Out:
    none
Return Value:
    ClRcT
*******************************************************************************/
void alarmClockCopyHotStandby ( acClockT *backup )
{
    if(sessionBufferMode())
    {
        memcpy(&alarmClock, backup, sizeof(acClockT));
        return;
    }

    if (alarmClock.clockRunning == CL_TRUE)
    {
        alarmClockLogWrite(CL_LOG_SEV_CRITICAL, 
                "alarmClockHotStandbyCopy(pid=%d): clock running; will be stopped\n",
                getpid());
        /* stop the clock if it is running
         */
        alarmClockStop();
    }
  
    /* copy the clock struct
     */
    memcpy( &alarmClock, backup, sizeof(acClockT));

    /* set value of clockRunning to false as it has been stopped
     */
    alarmClock.clockRunning = CL_FALSE;
}
/*******************************************************************************
Feature  API: alarmClockCkptWrite

Description : 

This API will not attempt to validate arguments, the thinking being that 
this is a volitional act by the invoker of this API to ensure that invalid 
parameters can be handled gracefully by the subsystem

Arguments In: 
    1. ClCkptHdlT : ckpt handle
    2. section number
    3. data to write
    4. size of data to write 

Return Value:
    ClInt32Teger 0 if success, non zero if failure
*******************************************************************************/
ClRcT 
alarmClockCkptWrite (
    ClCkptHdlT     ckpt_hdl,
    ClInt32T       section_num,
    void*          data,
    ClInt32T       data_size )
{
    ClRcT                ret_code = CL_OK;
    ClInt32T             pid = getpid();
    ClCharT              section_id_name[ CL_MAX_NAME_LENGTH ];
    ClCkptSectionIdT     section_id;

    sprintf(section_id_name, "s%05d", section_num);
    section_id.id = (ClUint8T*)section_id_name;
    section_id.idLen = strlen(section_id_name);

    ret_code = clCkptSectionOverwrite(ckpt_hdl,
                                      &section_id,
                                      data, data_size);
    if (ret_code != CL_OK)
    {
        if(CL_GET_ERROR_CODE(ret_code) == 0xa || 
           CL_GET_ERROR_CODE(ret_code) == CL_ERR_NOT_EXIST)
        {
            ClCkptSectionCreationAttributesT     section_cr_attr;
            section_cr_attr.sectionId = &section_id;
            section_cr_attr.expirationTime = (ClTimeT)CL_TIME_END;
            ret_code = clCkptSectionCreate(ckpt_hdl, &section_cr_attr,
                                           data, data_size);
        }
    }

    if(ret_code != CL_OK)
    {
            alarmClockLogWrite(CL_LOG_SEV_ERROR, 
                               "alarmClockCkptWrite(pid=%d): Failed to write: 0x%x\n", 
                               pid, ret_code); 
    }
    else
    {
        alarmClockLogWrite(CL_LOG_SEV_DEBUG, 
                           "alarmClockCkptWrite(pid=%d): wrote %d bytes to %s\n", 
                           pid, data_size, section_id_name); 
    }

    return ret_code;
}
/*******************************************************************************
Feature  API: alarmClockCkptWrite

Description : 

This API will not attempt to validate arguments, the thinking being that 
this is a volitional act by the invoker of this API to ensure that invalid 
parameters can be handled gracefully by the subsystem

Arguments In: 
    1. ClCkptHdlT : ckpt handle
    2. section number
    3. data to write
    4. size of data to write 

*******************************************************************************/
SaAisErrorT  
alarmClockCkptWrite (
    SaCkptCheckpointHandleT ckpt_hdl,
    SaUint32T               section_num,
    void*                   data,
    SaUint32T               data_size )
{
    SaAisErrorT          ret_code = SA_AIS_OK; 
    SaCkptSectionIdT     section_id;


    section_id.id = (SaUint8T*)&section_num;
    section_id.idLen = sizeof(section_num);
    
    ret_code = saCkptSectionOverwrite(ckpt_hdl, &section_id, data, data_size);
    if (ret_code != SA_AIS_OK)
    {
        /* If the overwrite failed because the section did not exist then try to create the section */
        if ((ret_code == 0x1000a) || (ret_code == SA_AIS_ERR_NOT_EXIST))
        {
            SaCkptSectionCreationAttributesT    section_cr_attr;
            
            /* Expiration time for section can also be varied
             * once parameters are read from a configuration file
             */
            section_cr_attr.expirationTime = SA_TIME_END;
            section_cr_attr.sectionId = &section_id;
            ret_code = saCkptSectionCreate(ckpt_hdl,&section_cr_attr,data, data_size);            
        }
    }
    
    if (ret_code != SA_AIS_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptWrite(hdl=%llx): Failed to write: 0x%x\n", 
                           ckpt_hdl, ret_code); 
    }
    else
    {
        alarmClockLogWrite(CL_LOG_SEV_DEBUG, "alarmClockCkptWrite: wrote %d bytes to section: %d\n", data_size, section_num); 
    }

    return ret_code;
}
SaAisErrorT alarmClockCkptActivate(SaCkptCheckpointHandleT ckptHdl, ClUint32T section_num)
{
    SaAisErrorT rc = SA_AIS_OK;
    SaCkptSectionIdT id;

    id.idLen = sizeof(section_num);
    id.id = (ClUint8T*)&section_num;

    rc = saCkptActiveReplicaSet(ckptHdl);
    if(rc != SA_AIS_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockReplicaSet: returned [%#x]", rc);
        return rc;
    }
    
    rc = clCkptSectionCheck(ckptHdl, (ClCkptSectionIdT*)&id);
    if(rc != CL_OK)
    {
        if(CL_GET_ERROR_CODE(rc) == CL_ERR_NOT_EXIST)
        {
            SaCkptSectionCreationAttributesT attr;
            attr.sectionId = &id;
            attr.expirationTime = (ClTimeT)CL_TIME_END;
            rc = saCkptSectionCreate(ckptHdl, &attr, NULL, 0);
            if(rc == SA_AIS_OK)
            {
                alarmClockLogWrite(CL_LOG_SEV_INFO, 
                                   "alarmClockActivate: Section [%d] created successfully", 
                                   section_num);
            }
        }
        else rc = SA_AIS_ERR_UNAVAILABLE; /* internal error */
    }
    else rc = SA_AIS_OK;

    if(rc != SA_AIS_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_INFO, "alarmClockActivate: Section operation on section [%d] "
                           "failed with [%#x]", section_num, rc);
    }
    
    return rc;
}
ClRcT alarmClockCkptActivate(ClCkptHdlT ckptHdl, ClUint32T numSections)
{
    ClRcT rc = CL_OK;
    ClCkptSectionIdT id;
    ClCharT section_name[CL_MAX_NAME_LENGTH];

    snprintf(section_name, sizeof(section_name), "s%05d", numSections);
    id.idLen = strlen(section_name);
    id.id = (ClUint8T*)section_name;

    rc = clCkptActiveReplicaSet(ckptHdl);
    if(rc != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockReplicaSet: returned [%#x]", rc);
        return rc;
    }
    
    rc = clCkptSectionCheck(ckptHdl, &id);
    if(CL_GET_ERROR_CODE(rc) == CL_ERR_NOT_EXIST)
    {
        ClCkptSectionCreationAttributesT attr;
        attr.sectionId = &id;
        attr.expirationTime = (ClTimeT)CL_TIME_END;
        rc = clCkptSectionCreate(ckptHdl, &attr, NULL, 0);
        if(rc == CL_OK)
        {
            alarmClockLogWrite(CL_LOG_SEV_INFO, "alarmClockActivate: Section [%s] created successfully",
                               section_name);
        }
    }
    
    if(rc != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_INFO, "alarmClockActivate: Section operation on [%s] "
                           "failed with [%#x]", section_name, rc);
    }
    
    return rc;
}
/*
 * Register checkpoint for immediate consumption, all components that
 * register using this API for a checkpoint will be notified through
 * the callback function provided when a checkpoint update occurs
 * All components except the one that actually updates the checkpoint.
 */
SaAisErrorT alarmClockCkptHotStandbyRegister(SaCkptCheckpointHandleT ckpt_hdl)
{
    ClRcT ret_code = CL_OK;
    /* When the standby becomes Active it checks this flag to see whether
     * it needs to perform a checkpoint read or not. If TRUE then the
     * assumption is that the checkpoint updates on standby have happened
     * via the callback
     */
    g_hot_standby = HOT_STANDBY_IDLE;
    ret_code = clCkptImmediateConsumptionRegister(ckpt_hdl, alarmClockCkptCallback, NULL);
    if (ret_code != CL_OK)
    {
        alarmClockLogWrite( CL_LOG_SEV_ERROR, 
                            "alarmClockCkptCreate(pid=%d): Failed to register ckpt callback rc[0x%x]\n",
                            getpid(), ret_code);

        alarmClockLogWrite(CL_LOG_SEV_WARNING, 
                           "alarmClockCkptCreate(pid=%d): Falling back to warm standby mode\n", getpid());

        return SA_AIS_ERR_BAD_OPERATION;
    }
    alarmClockLogWrite(CL_LOG_SEV_DEBUG, "alarmClockCkptCreate: Successfully registered for callbacks");
    return SA_AIS_OK;
}
/*******************************************************************************
Feature API: alarmClockCkptInitialize

*******************************************************************************/
SaAisErrorT
alarmClockCkptInitialize (void)
{
    SaAisErrorT  ret_code = SA_AIS_OK;

    if (ckpt_svc_hdl == 0)
    {
        ret_code = saCkptInitialize(&ckpt_svc_hdl, NULL, &ckpt_version);    
        if (ret_code != SA_AIS_OK)
        {
            alarmClockLogWrite(CL_LOG_SEV_ERROR,
                    "alarmClockCkptInitialize(pid=%d): Failed %x\n", 
                    getpid(), ret_code);
        }
    }    
    return ret_code;
}
/*******************************************************************************
Feature API: alarmClockCkptInitialize

*******************************************************************************/
ClRcT
alarmClockCkptInitialize (void)
{
    ClRcT ret_code = CL_OK;

    if (ckpt_svc_hdl == 0)
    {
        ret_code = clCkptInitialize(&ckpt_svc_hdl, NULL, &ckpt_version);    
        if (ret_code != CL_OK)
        {
            alarmClockLogWrite(CL_LOG_SEV_ERROR,
                    "alarmClockCkptInitialize(pid=%d): Failed %x\n", 
                    getpid(), ret_code);
        }
    }    
    return ret_code;
}
/*******************************************************************************
Feature API: alarmClockCkptFinalize

*******************************************************************************/
ClRcT
alarmClockCkptFinalize (void)
{
    ClRcT ret_code = CL_OK;

    if (ckpt_svc_hdl != 0)
    {
        ret_code = clCkptFinalize(ckpt_svc_hdl);    
        if (ret_code != CL_OK)
        {
            alarmClockLogWrite(CL_LOG_SEV_ERROR,
                    "alarmClockCkptFinalize(pid=%d): Failed %x\n", 
                    getpid(), ret_code);
        }
        else
        {
            ckpt_svc_hdl = 0;
        }
    }    
    return ret_code;
}
/*******************************************************************************
Feature API: alarmClockCkptFinalize

*******************************************************************************/
SaAisErrorT
alarmClockCkptFinalize (void)
{
    SaAisErrorT  ret_code = SA_AIS_OK;

    if (ckpt_svc_hdl != 0)
    {
        ret_code = saCkptFinalize(ckpt_svc_hdl);    
        if (ret_code != SA_AIS_OK)
        {
            alarmClockLogWrite(CL_LOG_SEV_ERROR,
                    "alarmClockCkptFinalize(pid=%d): Failed %x\n", 
                    getpid(), ret_code);
        }
        else
        {
            ckpt_svc_hdl = 0;
        }
    }    
    return ret_code;
}
/*******************************************************************************
Feature API: alarmClockCkptInitialize

*******************************************************************************/
SaAisErrorT
alarmClockCkptInitialize (void)
{
    SaAisErrorT  ret_code = SA_AIS_OK;

    if (ckpt_svc_hdl == 0)
    {
        ret_code = saCkptInitialize(&ckpt_svc_hdl, NULL, &ckpt_version);    
        if (ret_code != SA_AIS_OK)
        {
            alarmClockLogWrite(CL_LOG_SEV_ERROR,
                    "alarmClockCkptInitialize(pid=%d): Failed %x\n", 
                    getpid(), ret_code);
        }
    }    
    sessionInit();
    ClRcT rc = clOsalMutexInit(&alarmClockCkptMutex);
    CL_ASSERT(rc == CL_OK);
    ioVecs = clHeapCalloc(MAX_NUM_IOVECS, sizeof(*ioVecs));
    CL_ASSERT(ioVecs != NULL);
    return ret_code;
}
/*******************************************************************************
Feature  API: alarmClockCkptCreate

Description : Create a checkpoint given a name and the number of sections. In 
addition the nature of the checkpoint needs to be specified, synchronous versus 
asynchronous. If asynchronous collocated versus non collocated.

Arguments In: 
    1. Checkpoint Name
    2. Number of Sections
    3. Size of section

*******************************************************************************/
SaAisErrorT
alarmClockCkptCreate (
                      const SaStringT           ckpt_name,
                      SaUint32T                 num_sections,
                      SaSizeT                   section_size,
                      SaCkptCheckpointHandleT   *ckpt_hdl )
{

    SaAisErrorT                         ret_code = SA_AIS_OK;
    SaNameT                             ckpt_name_t;
    SaCkptCheckpointCreationAttributesT ckpt_cr_attr;
    SaCkptCheckpointOpenFlagsT          ckpt_open_flags;
    SaTimeT                             timeout;

    /* Initiailze name struct for ckpt
     */
    ckpt_name_t.length = strlen(ckpt_name);
    strcpy( ( SaStringT )ckpt_name_t.value,ckpt_name);
    
    /* Initialize check point creation flags
     */
    ckpt_cr_attr.creationFlags = CL_CKPT_DISTRIBUTED | CL_CKPT_PEER_TO_PEER_REPLICA;

    /* Maximum checkpoint size = size of all checkpoints combined
     */
    ckpt_cr_attr.checkpointSize = 0;

    /* Retention time: forever
     */
    ckpt_cr_attr.retentionDuration = (SaTimeT)-1;
    if ( num_sections == 1 ) 
    {   
        /* use a named section instead of the default section */
        ckpt_cr_attr.maxSections = num_sections + 1;
    }
    else
    {
        ckpt_cr_attr.maxSections = num_sections;
    }

    ckpt_cr_attr.maxSectionSize = 0;
    ckpt_cr_attr.maxSectionIdSize = 256;

    /*  forever
     */
    timeout = (SaTimeT)-1;

    /* Initialize the checkpoint open flags
     */

    ckpt_open_flags = (SA_CKPT_CHECKPOINT_READ  |
                       SA_CKPT_CHECKPOINT_WRITE |
                       SA_CKPT_CHECKPOINT_CREATE);
    /*  forever
     */
    timeout = (SaTimeT)-1;

    ret_code = saCkptCheckpointOpen(ckpt_svc_hdl, 
                                    &ckpt_name_t, 
                                    &ckpt_cr_attr,
                                    ckpt_open_flags,
                                    timeout,
                                    ckpt_hdl); 

    if ((ret_code != SA_AIS_OK)||(*ckpt_hdl == 0))
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptCreate: Failed to create ckpt:0x%x\n", ret_code);
        return ret_code;        
    }

    alarmClockLogWrite(CL_LOG_SEV_DEBUG, "alarmClockCkptCreate: Successful creation ckpt [%llx]\n", 
                       *ckpt_hdl);    
    return SA_AIS_OK;    
}
ClRcT alarmClockAdvance ( void )
{
    ClBoolT  raiseAlarm = CL_FALSE;
    ClInt32T pid = getpid();

    /* advance the clock by a second
     */
    if (++alarmClock.clockTime.second >= 60)
    {
        alarmClock.clockTime.second = 0;
        if (++alarmClock.clockTime.minute >= 60)
        {
            alarmClock.clockTime.minute = 0;
            if (++alarmClock.clockTime.hour >= 24)
            {
                alarmClock.clockTime.hour = 0;
            }
        }
    }


    /* Turn the alarm off if it is due to go off; or else the standby 
     * will also set the alarm off; 
     */
    if ((alarmClock.alarmSet == CL_TRUE) &&
        (alarmClock.alarm.time.hour == alarmClock.clockTime.hour) &&
        (alarmClock.alarm.time.minute == alarmClock.clockTime.minute))
    {
        alarmClock.alarmSet = CL_FALSE;
        raiseAlarm = CL_TRUE;
    }
    

    /*
     * Checkpoint every 2 seconds
     */
    if (! (alarmClock.clockTime.second & 1) )
    {
        alarmClockCkptLock();
        alarmClockCkptWrite(alarmClockCkptHdl, 1, &alarmClock, sizeof(acClockT));
        alarmClockCkptUnlock();
    }

    /* Log 
     */
    alarmClockLogWrite(CL_LOG_SEV_INFO,
                       "alarm clock (pid=%d): time is %dh:%dm:%ds\n", 
                       pid, 
                       alarmClock.clockTime.hour,
                       alarmClock.clockTime.minute,
                       alarmClock.clockTime.second);

    if (raiseAlarm == CL_TRUE)
    {
        /* raising an alarm can have two consequences
         * calling the API to raise the alarm
         * or killing the process that has triggered
         * the alarm
         */
        alarmClockRaiseAlarm();
    }

    return CL_OK;
}
/*******************************************************************************
Feature  API: alarmClockCkptCreate

Description : Create a checkpoint given a name and the number of sections. In 
addition the nature of the checkpoint needs to be specified, synchronous versus 
asynchronous. If asynchronous collocated versus non collocated.

Arguments In: 
    1. Checkpoint Name
    2. Number of Sections
    3. Size of section

*******************************************************************************/
SaAisErrorT
alarmClockCkptCreate (
    const SaStringT           ckpt_name,
    SaUint32T                 num_sections,
    SaSizeT                   section_size,
    SaCkptCheckpointHandleT   *ckpt_hdl )
{

    SaAisErrorT                         ret_code = SA_AIS_OK;
    SaNameT                             ckpt_name_t;
    SaCkptCheckpointCreationAttributesT ckpt_cr_attr;
    SaCkptCheckpointOpenFlagsT          ckpt_open_flags;
    SaTimeT                             timeout;

    /* Initiailze name struct for ckpt
     */
    ckpt_name_t.length = strlen(ckpt_name);
    strcpy( ( SaStringT )ckpt_name_t.value,ckpt_name);
    
    /* Initialize check point creation flags
     */
    ckpt_cr_attr.creationFlags = SA_CKPT_CHECKPOINT_COLLOCATED | CL_CKPT_DISTRIBUTED;

    /* Maximum checkpoint size = size of all checkpoints combined
     */
    ckpt_cr_attr.checkpointSize = num_sections * section_size;

    /* Retention time: forever
     */
    ckpt_cr_attr.retentionDuration = (SaTimeT)-1;
    if ( num_sections == 1 ) 
    {   
        /* use a named section instead of the default section */
        ckpt_cr_attr.maxSections = num_sections + 1;
    }
    else
    {
        ckpt_cr_attr.maxSections = num_sections;
    }

    ckpt_cr_attr.maxSectionSize = section_size;
    ckpt_cr_attr.maxSectionIdSize = sizeof(SaUint32T);

     /*  forever
     */
    timeout = (SaTimeT)-1;

    /* Initialize the checkpoint open flags
     */

    ckpt_open_flags = (SA_CKPT_CHECKPOINT_READ  |
                       SA_CKPT_CHECKPOINT_WRITE |
                       SA_CKPT_CHECKPOINT_CREATE);
     /*  forever
     */
    timeout = (SaTimeT)-1;

    ret_code = saCkptCheckpointOpen(ckpt_svc_hdl, 
                                    &ckpt_name_t, 
                                    &ckpt_cr_attr,
                                    ckpt_open_flags,
                                    timeout,
                                    ckpt_hdl); 

    if ((ret_code != SA_AIS_OK)||(*ckpt_hdl == 0))
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptCreate: Failed to create ckpt:0x%x\n", ret_code);
        return ret_code;        
    }

    if (1) // *ckpt_hot_standby == CL_TRUE)
    {
        /*
         * Register checkpoint for immediate consumption, all components that
         * register using this API for a checkpoint will be notified through
         * the callback function provided when a checkpoint update occurs
         * All components except the one that actually updates the checkpoint.
         */
        
        /* When the standby becomes Active it checks this flag to see whether
         * it needs to perform a checkpoint read or not. If TRUE then the
         * assumption is that the checkpoint updates on standby have happened
         * via the callback
         */
        g_hot_standby = HOT_STANDBY_IDLE;
        ret_code = clCkptImmediateConsumptionRegister(*ckpt_hdl, alarmClockCkptCallback, NULL);
        if (ret_code != CL_OK)
        {
            alarmClockLogWrite( CL_LOG_SEV_ERROR, 
                    "alarmClockCkptCreate(pid=%d): Failed to register ckpt callback rc[0x%x]\n",
                    getpid(), ret_code);

            alarmClockLogWrite(CL_LOG_SEV_WARNING, 
                    "alarmClockCkptCreate(pid=%d): Falling back to warm standby mode\n", getpid());

            //*ckpt_hot_standby = CL_FALSE;
            return ret_code;
        }
        alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptCreate: Successfully registered for callbacks");    
    }

    alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptCreate: Successful creation ckpt [%llx]\n", 
                       *ckpt_hdl);    
    return SA_AIS_OK;    
}
/*******************************************************************************
Feature  API: alarmClockCkptCallback

Description : 

Other API in this file are either completely generic or mostly so, the hot standby
feature cannot be generic as  the state needs to be conveyed to the application
and made "hot" so to speak. Here we are going to have to know internals of the 
alarm clock structure

Arguments In: 
    1. ClCkptHdlT : ckpt handle
    2. ClNameT *    ckpt_name being updated
    3. ClCkptIOVectorElementT   *io_vector containing updated checkpoint data (all sections)
    4. ClInt32T     number of sections within checkpoint
    5. ClPtrT       cookie; can be used to keep globals within the task

Return Value:
    ClInt32Teger 0 if success, non zero if failure
*******************************************************************************/
static ClRcT 
alarmClockCkptCallback( ClCkptHdlT              ckpt_hdl,
                        ClNameT                 *ckpt_name,
                        ClCkptIOVectorElementT  *io_vector,
                        ClUint32T               num_sections,
                        ClPtrT                  cookie )
{
    ClRcT       ret_code = CL_OK;
    ClInt32T    count;
    ClInt32T    pid = getpid();
    ClInt32T    data_size = sizeof(SessionT);
    ClCkptSectionIdT *target_section_id;

    target_section_id = (ClCkptSectionIdT*)&sectionIdMap[0].sectionId;

    alarmClockLogWrite(CL_LOG_SEV_INFO, 
            "alarmClockCkptCallback(pid=%d): ckpt[%.*s] update received\n", 
            pid, ckpt_name->length, ckpt_name->value);
    
    /*
     * Normal case, would be zero-contention on the lock and futex with no contention 
     * would incur no futex_wait or syscall overhead.
     */
    alarmClockCkptLock();

    for (count = 0; count < num_sections; count++)
    {
        /*
         * check if the received update is for the section of interest to us,
         * if it is update the section, 
         */
        if ((target_section_id->idLen == io_vector[count].sectionId.idLen) && 
            (memcmp(target_section_id->id, (ClCharT *)io_vector[count].sectionId.id, 
                    target_section_id->idLen) == 0))
        {
            if (io_vector[count].dataSize != data_size)
            {
                alarmClockLogWrite(CL_LOG_SEV_ERROR, 
                       "alarmClockCkptCallback(pid=%d): received %d bytes; expected %d bytes\n", 
                        pid, (ClInt32T)io_vector[count].readSize, (ClInt32T)sizeof(acClockT));
            }
            else
            {

                alarmClockLogWrite(CL_LOG_SEV_INFO, 
                                   "alarmClockCkptCallback(pid=%d): received %d bytes from section %.*s "
                                   "for offset [%d]", 
                                   pid, (ClInt32T)io_vector[count].readSize, 
                                   target_section_id->idLen, (const char *)target_section_id->id,
                                   (ClUint32T)io_vector[count].dataOffset);
                
                SessionT *session = (SessionT*)io_vector[count].dataBuffer;
                ClUint32T offset = (ClUint32T)io_vector[count].dataOffset/data_size;
                sessionUpdate(session, offset);
                alarmClockCopyHotStandby(&session->clock);
            }

            break;
        }
    }
    /*
     * ckpt in sync through hot standby update
     */
    if(!(g_hot_standby & HOT_STANDBY_ACTIVE))
        g_hot_standby |= HOT_STANDBY_ACTIVE;

    alarmClockCkptUnlock();

    return ret_code;
}
SaAisErrorT
alarmClockCkptRead (
                    SaCkptCheckpointHandleT    ckpt_hdl,
                    SaUint32T                  section_num,
                    void*                      data,
                    SaUint32T                  data_size )
{
    SaAisErrorT             ret_code = SA_AIS_OK;
    SaUint32T               error_index = 0;
    SaCkptIOVectorElementT *ioVecIter = ioVecs;
    ClUint32T bitmapChunks = 0;
    ClUint32T chunkSize = 0;
    ClUint32T ctrlHeaderSize = sessionBitmapHeaderSize(&chunkSize);

    if(g_hot_standby & HOT_STANDBY_ACTIVE)
    {
        /*
         * Start session buffering if hotstandby mode is active
         */
        sessionBufferStart();
        return SA_AIS_ERR_EXIST;
    }

    if(section_num > NUM_SECTION_ID_MAPS)
        return SA_AIS_ERR_INVALID_PARAM;

    /*
     * If section doesn't exist, then skip the read
     */
    if(!sectionCreated)
    {
        if(__alarmClockCkptSectionCheck(ckpt_hdl, section_num) != CL_OK)
            return SA_AIS_ERR_NOT_EXIST;
        sectionCreated = CL_TRUE;
    }
    
    memset(ioVecs, 0, sizeof(*ioVecs) * MAX_NUM_IOVECS);

    /*
     * We don't need to read in the ctrl section since we would
     * be syncing our session bitmaps while reading the session data
     */
    memcpy(&ioVecIter->sectionId, &sectionIdMap[section_num-1].ctrlSectionId,
           sizeof(ioVecIter->sectionId));
    ++ioVecIter;
    memcpy(&ioVecIter->sectionId, &sectionIdMap[section_num-1].sectionId,
           sizeof(ioVecIter->sectionId));
    ++ioVecIter;
    ret_code = saCkptCheckpointRead(ckpt_hdl, ioVecs, 2, &error_index);
    if(ret_code == SA_AIS_OK)
    {
        if(!ioVecs[0].readSize)
        {
            alarmClockLogWrite(CL_LOG_SEV_NOTICE, "alarmClockCkptRead(pid=%d): "
                               "Session empty", getpid());
            goto out_free;
        }

        if(ioVecs[0].readSize < ctrlHeaderSize)
        {
            alarmClockLogWrite(CL_LOG_SEV_WARNING, "alarmClockCkptRead(pid=%d): "
                               "read [%d] bytes, expected [%d] bytes",
                               getpid(), (ClUint32T)ioVecs[0].readSize, ctrlHeaderSize);
            ret_code = SA_AIS_ERR_BAD_OPERATION;
            goto out_free;
        }

        bitmapChunks = *(ClUint32T*)ioVecs[0].dataBuffer;
        alarmClockLogWrite(CL_LOG_SEV_DEBUG, "alarmClockCkptRead(pid=%d): "
                           "Bitmap chunks [%d], Bitmap size [%d]\n",
                           getpid(), bitmapChunks, 
                           (ClUint32T)(ioVecs[0].readSize - sizeof(bitmapChunks)));
        BitmapT bitmap = {0};
        bitmap.map = (ClUint8T*)ioVecs[0].dataBuffer + sizeof(bitmapChunks);
        bitmap.words = BYTES_TO_WORDS(bitmapChunks * chunkSize);
        /*
         * We can also walk the session itself (#else case) without using the bitmap segment
         * but could be slightly costly in the worst case with session holes in between
         * for deleted or empty sessions.   
         */
#if 1
        __find_next_bit_walk(&bitmap, &ioVecs[1], bitmapWalkCallback);
        alarmClockLogWrite(CL_LOG_SEV_DEBUG, "alarmClockCkptRead(pid=%d): "
                           "Found [%d] active sessions", getpid(), bitmap.bits);
#else
        __find_next_bit_walk(&bitmap, NULL, bitmapWalkCallback);
        /*
         * Read in session data
         */
        ClUint32T numSessions = 0;
        ClUint8T *sessionBuffer = ioVecs[1].dataBuffer;
        SessionT *sessionIter = (SessionT*)sessionBuffer;
        ClUint32T sessionDataSize = ioVecs[1].readSize;
        numSessions = sessionDataSize/sizeof(SessionT);
        alarmClockLogWrite(CL_LOG_SEV_DEBUG, "alarmClockCkptRead(pid=%d): "
                           "Found [%d] sessions with ckpt size of [%d] bytes", 
                           getpid(), numSessions, sessionDataSize);
        for(ClUint32T i = 0; i < numSessions; ++i, ++sessionIter)
        {
            /*
             * Real world, has to unmarshall or copy-in to avoid unaligned access
             */
            sessionUpdate(sessionIter, i);
        }
#endif
    }

    out_free:
    if(ioVecs[0].dataBuffer)
    {
        clHeapFree(ioVecs[0].dataBuffer);
        ioVecs[0].dataBuffer = NULL;
    }
    if(ioVecs[1].dataBuffer)
    {
        clHeapFree(ioVecs[1].dataBuffer);
        ioVecs[1].dataBuffer = NULL;
    }

    return ret_code;
}
void clCompAppAMFCSISet(SaInvocationT       invocation,
                        const SaNameT       *compName,
                        SaAmfHAStateT       haState,
                        SaAmfCSIDescriptorT csiDescriptor)
{
    /*
     * Print information about the CSI Set
     */

    clprintf (CL_LOG_SEV_INFO, "Component [%.*s] : PID [%d]. CSI Set Received\n", 
              compName->length, compName->value, mypid);

    clCompAppAMFPrintCSI(csiDescriptor, haState);

    SaAisErrorT         rc = SA_AIS_OK;
 
    /* create a checkpoint based on the csi descriptor name to ensure uniqueness
       Both processes will attempt to open/create the checkpoint.  The first one
       will create it.
     */
     if (alarmClockCkptHdl == 0)
     {
         strcpy(alarmClockCkptName, (char *)csiDescriptor.csiName.value);
         rc = alarmClockCkptCreate(alarmClockCkptName, 1, sizeof(acClockT), &alarmClockCkptHdl);
         if (rc != SA_AIS_OK)
         {
             alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptCreate failed code [%d]", rc);
             assert(0);
             alarmClockCkptHdl = 0;
         }
     }

   /*
     * Take appropriate action based on state
     */
    
    switch ( haState )
    {
        case SA_AMF_HA_ACTIVE:
        {
            /*
             * AMF has requested application to take the active HA state 
             * for the CSI.
             */
            rc = alarmClockCkptActivate(alarmClockCkptHdl, 1);
            if (rc != SA_AIS_OK)
            {
                alarmClockLogWrite(CL_LOG_SEV_ERROR, "alarmClockCkptCreate(pid=%d): Failed to activate ckpt :0x%x\n", getpid(), rc);
            }            

            if (rc == SA_AIS_OK)  /* try to read the checkpoint regardless of prior state if I become active prevHaState == SA_AMF_HA_STANDBY) */
            {       /* this way the ckpt will be read on process restart */
                
                acClockT    alarmClock = {{0}};

                 alarmClockLogWrite(CL_LOG_SEV_INFO, "Reading checkpoint to recover the time");
                 
                 rc = alarmClockCkptRead ( alarmClockCkptHdl, 1, &alarmClock, sizeof(acClockT));
                 if (rc == SA_AIS_OK)
                 {
                    alarmClockCopyAndStart( &alarmClock);
                 }
                 else
                 {
                     alarmClockStart();
                 }
             }
            else
            {
                alarmClockStart();
            }


            prevHaState = haState;

            saAmfResponse(amfHandle, invocation, SA_AIS_OK);
            break;
        }

        case SA_AMF_HA_STANDBY:
        {
            /*
             * AMF has requested application to take the standby HA state 
             * for this CSI.
             */
            alarmClockLogWrite(CL_LOG_SEV_INFO, "I am standby");
            prevHaState = haState;
   
            saAmfResponse(amfHandle, invocation, SA_AIS_OK);
            break;
        }

        case SA_AMF_HA_QUIESCED:
        {
            /*
             * AMF has requested application to quiesce the CSI currently
             * assigned the active or quiescing HA state. The application 
             * must stop work associated with the CSI immediately.
             */

            alarmClockStop();
            prevHaState = haState;

            saAmfResponse(amfHandle, invocation, SA_AIS_OK);
            break;
        }

        case SA_AMF_HA_QUIESCING:
        {
            /*
             * AMF has requested application to quiesce the CSI currently
             * assigned the active HA state. The application must stop work
             * associated with the CSI gracefully and not accept any new
             * workloads while the work is being terminated.
             */

            saAmfCSIQuiescingComplete(amfHandle, invocation, SA_AIS_OK);
            break;
        }

        default:
        {
            assert(0);
            break;
        }
    }

    return;
}
/*******************************************************************************
Feature  API: alarmClockCkptCreate

Description : Create a checkpoint given a name and the number of sections. In 
addition the nature of the checkpoint needs to be specified, synchronous versus 
asynchronous. If asynchronous collocated versus non collocated.

Arguments In: 
    1. Checkpoint Name
    2. Number of Sections
    3. Size of section
    4. ckpt_hot_standby: If set to true then hot standby turned on

Arguments Out:
    1. : returns ckpt_handle
    2. ckpt_hot_standby: If set to true then hot standby was successful

Return Value:
    ClInt32Teger 0 if success, non zero if failure
*******************************************************************************/
ClRcT
alarmClockCkptCreate (
                      const ClCharT   *ckpt_name,
                      ClInt32T        num_sections,
                      ClInt32T        section_size,
                      ClCkptHdlT      *ckpt_hdl,
                      ClBoolT         *ckpt_hot_standby )
{

    ClRcT                               ret_code = CL_OK;
    SaNameT                             ckpt_name_t;
    ClCkptCheckpointCreationAttributesT ckpt_cr_attr;
    ClCkptOpenFlagsT                    ckpt_open_flags;
    ClTimeT                             timeout;
    ClCharT                              section_id_name[ CL_MAX_NAME_LENGTH ];

    /* First ensure that the Initialize function is called
     */
    ret_code = alarmClockCkptInitialize();
    if (ret_code != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR,
                           "alarmClockCkptCreate(pid=%d): failed initialze service\n",
                           getpid());
        return ret_code;
    }

    /* Initiailze name struct for ckpt
     */
    ckpt_name_t.length = strlen(ckpt_name);
    strcpy(ckpt_name_t.value, ckpt_name);

    /* Get the max size for a  name of sectionId
     */
    sprintf(section_id_name, "s%05d", num_sections);     

    if (*ckpt_hot_standby == CL_TRUE)
    {
        /* Initialize check point creation flags (includes hot standby flag)
         */
        ckpt_cr_attr.creationFlags = CL_CKPT_CHECKPOINT_COLLOCATED | CL_CKPT_DISTRIBUTED;
    }
    else
    {
        /* Initialize check point creation flags 
         */
        ckpt_cr_attr.creationFlags = CL_CKPT_CHECKPOINT_COLLOCATED; 
    }

    /* Maximum checkpoint size = size of all checkpoints combined
     */
    ckpt_cr_attr.checkpointSize = num_sections * section_size;

    /* Retention time: forever
     */
    ckpt_cr_attr.retentionDuration = (ClTimeT)-1;
    if ( num_sections == 1 ) 
    {   
        /* use a named section instead of the default section */
        ckpt_cr_attr.maxSections = num_sections + 1;
    }
    else
    {
        ckpt_cr_attr.maxSections = num_sections;
    }

    ckpt_cr_attr.maxSectionSize = section_size;
    ckpt_cr_attr.maxSectionIdSize = (ClSizeT)(strlen(section_id_name)+1);

    /*  forever
     */
    timeout = (ClTimeT)-1;

    /* Initialize the checkpoint open flags
     */
    ckpt_open_flags = (CL_CKPT_CHECKPOINT_READ  |
                       CL_CKPT_CHECKPOINT_WRITE |
                       CL_CKPT_CHECKPOINT_CREATE);

    /*  forever
     */
    timeout = (ClTimeT)-1;

    ret_code = clCkptCheckpointOpen(ckpt_svc_hdl, 
                                    &ckpt_name_t, 
                                    &ckpt_cr_attr,
                                    ckpt_open_flags,
                                    timeout,
                                    (ClCkptHdlT *)ckpt_hdl);

    if (ret_code != CL_OK)
    {
        alarmClockLogWrite(CL_LOG_SEV_ERROR,
                           "alarmClockCkptCreate(pid=%d): Failed to create ckpt:0x%x\n",
                           getpid(), ret_code);
        return ret_code;
    }

    if (*ckpt_hot_standby == CL_TRUE)
    {
        /*
         * Register checkpoint for immediate consumption, all components that
         * register using this API for a checkpoint will be notified through
         * the callback function provided when a checkpoint update occurs
         * All components except the one that actually updates the checkpoint.
         */
        
        /* When the standby becomes Active it checks this flag to see whether
         * it needs to perform a checkpoint read or not. If TRUE then the
         * assumption is that the checkpoint updates on standby have happened
         * via the callback
         */
        g_hot_standby = __HOT_STANDBY_IDLE;
        ret_code = clCkptImmediateConsumptionRegister(*ckpt_hdl, alarmClockCkptCallback, NULL);
        if (ret_code != CL_OK)
        {
            alarmClockLogWrite( CL_LOG_SEV_ERROR, 
                                "alarmClockCkptCreate(pid=%d): Failed to register ckpt callback rc[0x%x]\n",
                                getpid(), ret_code);

            alarmClockLogWrite(CL_LOG_SEV_WARNING, 
                               "alarmClockCkptCreate(pid=%d): Falling back to warm standby mode\n", getpid());

            *ckpt_hot_standby = CL_FALSE;
        }
    }

    alarmClockLogWrite(CL_LOG_SEV_INFO, "alarmClockCkptCreate: Ckpt [%s] created successfully",
                       ckpt_name);
    return ret_code;    
}