windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/upssvc/apcsmart/cdevice.cxx

937 lines
27 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*
*
* NOTES:
*
* REVISIONS:
* ane09DEC92: fixed thread rundown problems
* ane16DEC92: added semaphore to protect multi-threaded access to
* current transaction group on protocol
* jod29Dec92: Made SubmitList return an INT -> changed Get and Set
* ane30Dec92: optimized polling
* jod13Jan93: Added eventList to InterpretMessage() call and moved all
* HandleEvent calls to the Poll loop
* ane25Jan93: Added call to error logger when initialization of port
* fails
* ane03Feb93: Added checks to avoid crashes during rundown of polling
* thread, also updated destructor
* ane09Feb93: Use ExitWait when telling the poll thread to go away
* pcy16Feb93: Explicitly check for COMM LOST on returning msgs
* pcy18Feb93: Fixed typo Err_RETRYING_COMM to ErrRETRYING_COM
* pcy21Apr93: OS2 FE merge
* pcy14May93: Added Set(PTransactionGroup)
* cad09Jul93: using exitwait, new semaphores
* cad15Jul93: sleeping during no-comm to allow cpu to detect reconnect better
* pcy16Jul93: Added NT semaphores
* jod24Aug93: Added Unregister
* cad08Sep93: Added handling of events during gets
* pcy10Sep93: Set theCurrentTransaction to the top of the list on Unregister
* cad14Sep93: Support for pausing polling
* cad07Oct93: Plugging Memory Leaks
* cad12Oct93: Not sending garbage down for gets
* ajr18Oct93: Made changes to work with Process manager for SINGLETHREADED
* cad17Nov93: more lost comm fixups
* mwh19Nov93: changed EventID to INT
* ajr28Dec93: Added types.h on unix platforms to resolve typing problems.
* cad11Jan94: Changes for new process manager
* cad13Jan94: fixed up/simplified interval time measurement
* cad14Jan94: fixing polling time for single-threaded
* mwh14Jan94: fix singlethreaded client skipping every other poll
* cad18Jan94: cleaned up single-threaded poll
* pcy28Jan94: Create poll events on stack
* ajr16Feb94: Added default id to SubmitList.
* ajr16Feb94: Added writeUpsOffFile() protected member function.
* ram21Mar94: Included header files for Novell FE to work
* ajr23Mar94: Made sure to close upsoff.cmd
* pcy08Apr94: Trim size, use static iterators, dead code removal
* pcy13Apr94: Use automatic variables decrease dynamic mem allocation
* mwh05May94: #include file madness , part 2
* mwh09May94: lose the auto. aggregate init. e.g CHAR foo[2] = "";
* ajr20May94: SCO stuff
* mwh23May94: port for NCR
* ajr24May94: Uware includes....
* ajr31May94: Saw to it that upsoff.cmd is erased after execution...
* ajr31May94: Added stat.h for IRIX too....
* mwh01Jun94: port for INTERACTIVE
* cad01Jul94: fix for unix sleep problem and unregister crash
* ajr29Aug94: reworked writeUpsOffFile to take Message *
* ajr02May95: Need to stop carrying time in milliseconds
* dml21Jun95: modify for general utility to get default pwrchute directory; req'd for Windows
* dml24Aug95: removed conditional code for OS2 ver 1.3
* ajr07Nov95: SINIX Port
* poc28Sep96: Fixed SIR 4392.
* jps17Oct96: Moved and added Access() and Release() calls, attempting to eliminate
* application errors on exit
* srt21Oct96: Replaced a timerManager wait with a timed semaphore wait
* jps23Oct96: Added Access()/Release() to ~CommDevice(); commented out test for
* TURN_ON_SMART_MODE in Set() so it always calls Access()/Release()
* srt24Oct96: Added an abort semaphore
* inf14Apr97: Loaded error string from resource file.
* tjg09Jul97: Changed NT code to call getPwrchuteDirectory instead of getenv
* awm21Oct97: Added initialization for performance monitor shared memory blocks
* awm14Jan98: Removed initialization for performance monitor shared memory blocks
* tjg26Jan98: Added Stop method
* tjg02Mar98: Removed Stop method (dead code)
*
* v-stebe 29Jul2000 Fixed PREfix errors (bugs #46363-46365, #46367, #46368)
* v-stebe 11Sep2000 Fixed additional PREfix errors
*/
#include "cdefine.h"
#include "stream.h"
extern "C" {
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <time.h>
}
#include "_defs.h"
#include "apcobj.h"
#include "list.h"
#include "dispatch.h"
#include "event.h"
#include "message.h"
#include "proto.h"
#include "trans.h"
#include "cdevice.h"
#include "err.h"
#include "timerman.h"
#include "thread.h"
#include "contrlr.h"
#include "errlogr.h"
#include "utils.h"
#include "protlist.h"
#include "codes.h"
#include "apcsemnt.h"
#include "mutexnt.h"
#define RETRY_FAILED 1
#define RETRY_SUCCESS 0
#define ASKUPS_FAILED 1
#define ASKUPS_OK 0
/*--------------------------------------------------------------------
*
* Function...: CommDevice
*
* Description: Constructor.
*
*-------------------------------------------------------------------*/
CommDevice::CommDevice(Controller* control)
: theController(control),
thePollList((ProtectedList *)NULL),
theCurrentTransaction((PTransactionGroup)NULL),
pollStartTime(0),
theSleepingFlag(0),
thePollIsDone(0),
theState(NORMAL_STATE),
thePort((PStream)NULL),
theProtocol((Protocol*)NULL),
thePollIterator((PListIterator)NULL),
thePollThread((PThread)NULL),
thePollInterval(4L),
theLostCommFlag(0)
{
theEventList = new ProtectedList();
theEventIterator = (PListIterator) &(theEventList->InitIterator());
theAskLock = new ApcMutexLock();
theAbortSem = new ApcSemaphore();
}
/*--------------------------------------------------------------------
*
* Function...: CommDevice
*
* Description: Destructor.
*
*-------------------------------------------------------------------*/
CommDevice::~CommDevice()
{
Access();
theController->UnregisterEvent(EXIT_THREAD_NOW, this);
theAbortSem->Post();
if (theEventList)
{
theEventList->FlushAll();
delete theEventList;
theEventList = (PProtectedList)NULL;
if (theEventIterator) {
delete theEventIterator;
theEventIterator = (PListIterator)NULL;
}
}
if (thePollList)
{
thePollList->FlushAll();
delete thePollList;
thePollList = (ProtectedList *)NULL;
}
if (thePollIterator)
{
delete thePollIterator;
thePollIterator = (PListIterator)NULL;
}
if (thePort)
{
delete thePort;
thePort = (PStream)NULL;
}
if (theProtocol)
{
delete theProtocol;
theProtocol = (PProtocol)NULL;
}
if (thePollThread)
{
// Use ExitWait to make sure that the poll thread is exited before
// deleting the poll list, etc.
//
// NT workaround
//
thePollThread->TerminateThreadNow();
delete thePollThread;
thePollThread = (PThread)NULL;
}
Release();
delete theAskLock;
// theAskLock is a mutex lock. Everyone should have one. Dont us Semaphore
// or NullMutexLock.
theAskLock = (PMutexLock) NULL;
delete theAbortSem;
theAbortSem = NULL;
}
/*--------------------------------------------------------------------
*
* Function...: Initialize
*
* Description: .
*
*-------------------------------------------------------------------*/
INT CommDevice::Initialize()
{
INT err = ErrNO_ERROR;
if (thePort)
{
err = thePort->Initialize();
}
theObjectStatus = err;
// Can't call this in the constructor since theController vtbl isn't set
// up at that point...It'll crash
theController->RegisterEvent(EXIT_THREAD_NOW, this);
return err;
}
INT CommDevice::Update(PEvent anEvent)
{
switch (anEvent->GetCode())
{
case EXIT_THREAD_NOW:
if(thePollThread)
{
thePollThread->ExitWait();
delete thePollThread;
thePollThread = (PThread)NULL;
}
break;
default:
break;
}
return ErrNO_ERROR;
}
/*--------------------------------------------------------------------
*
* Function...: Get
*
* Description:
*
*-------------------------------------------------------------------*/
INT CommDevice::Get(INT id, CHAR* value)
{
INT err = ErrNO_ERROR;
if (theLostCommFlag) {
err = ErrCOMMUNICATION_LOST;
}
else {
// Build the get transaction
PTransactionGroup transaction_group = new TransactionGroup(GET);
PTransactionItem transaction_item = new TransactionItem(GET, id, "");
if ((transaction_group != NULL) && (transaction_item != NULL)) {
transaction_group->AddTransactionItem(transaction_item);
// Let the Protocol build the message. The messages to send will be
// placed in msglist. There may be more than one message.
if (theProtocol)
err = theProtocol->BuildTransactionGroupMessages(transaction_group);
else
err = ErrRETRYING_COMM;
if (!err && theProtocol)
{
Access();
theProtocol->SetCurrentTransactionGroup(transaction_group);
err = SubmitList(transaction_group->GetProtocolMessageList());
Release();
if (!(err))
{
err = transaction_group->GetErrorCode();
}
if (err == ErrNO_ERROR || err == ErrCONTINUE)
{
PList tiList = transaction_group->GetTransactionItemList();
PTransactionItem tmpti = new TransactionItem(GET,id);
PTransactionItem transitem = (PTransactionItem)tiList->Find(tmpti);
strcpy(value, transitem->GetValue());
delete tmpti;
tmpti = NULL;
}
}
delete transaction_group;
transaction_group = NULL;
}
else {
err = ErrMEMORY;
// Cleanup allocations so that we don't leak
delete transaction_item;
transaction_item = NULL;
delete transaction_group;
transaction_group = NULL;
}
}
return err;
}
/*--------------------------------------------------------------------
*
* Function...: Get with a Transaction Group
*
* Description:
*
*-------------------------------------------------------------------*/
INT CommDevice::Get(PTransactionGroup transaction_group)
{
INT err = ErrNO_ERROR;
if(theLostCommFlag) {
err = ErrCOMMUNICATION_LOST;
}
else {
// Let the Protocol build the message. The messages to send will be
// placed in msglist. There may be more than one message.
if (theProtocol)
err = theProtocol->BuildTransactionGroupMessages(transaction_group);
else
err = ErrRETRYING_COMM;
if (!err && theProtocol)
{
Access();
theProtocol->SetCurrentTransactionGroup(transaction_group);
INT getErr = SubmitList(transaction_group->GetProtocolMessageList());
Release();
if (!(getErr))
{
getErr = transaction_group->GetErrorCode();
}
return getErr;
}
}
return err;
}
/*--------------------------------------------------------------------
*
* Function...: Set
*
* Description: .
*
*-------------------------------------------------------------------*/
INT CommDevice::Set(INT id, CHAR* value)
{
INT err = ErrNO_ERROR;
if ((id != TURN_ON_SMART_MODE) && theLostCommFlag) {
err = ErrCOMMUNICATION_LOST;
}
else {
// Build the set transaction
//
PTransactionGroup transaction_group = new TransactionGroup(SET);
PTransactionItem transaction_item = new TransactionItem(SET, id, value);
if ((transaction_group != NULL) && (transaction_item != NULL)) {
transaction_group->AddTransactionItem(transaction_item);
// Let the Protocol build the message. The messages to send will be
// placed in msglist. There may be more than one message.
if (theProtocol)
err = theProtocol->BuildTransactionGroupMessages(transaction_group);
else
err = ErrRETRYING_COMM;
if (!err && theProtocol)
{
// if(id != TURN_ON_SMART_MODE) {
Access();
// }
theProtocol->SetCurrentTransactionGroup(transaction_group);
err = SubmitList(transaction_group->GetProtocolMessageList(),id);
// if(id != TURN_ON_SMART_MODE) {
Release();
// }
if (!err) {
err = transaction_group->GetErrorCode();
}
}
if (!err) {
err = transaction_item->GetErrorCode();
}
delete transaction_group;
transaction_group = NULL;
}
else {
err = ErrMEMORY;
// Cleanup allocations so that we don't leak
delete transaction_item;
transaction_item = NULL;
delete transaction_group;
transaction_group = NULL;
}
}
return err;
}
INT CommDevice::Set(PTransactionGroup transaction_group)
{
INT err = ErrNO_ERROR;
if(theLostCommFlag) {
err = ErrCOMMUNICATION_LOST;
}
else {
// Let the Protocol build the message. The messages to send will be
// placed in msglist. There may be more than one message.
if (theProtocol)
err = theProtocol->BuildTransactionGroupMessages(transaction_group);
else
err = ErrRETRYING_COMM;
if (!err && theProtocol)
{
Access();
theProtocol->SetCurrentTransactionGroup(transaction_group);
INT setErr = SubmitList(transaction_group->GetProtocolMessageList());
Release();
if (!(setErr))
{
setErr = transaction_group->GetErrorCode();
}
return setErr;
}
}
return err;
}
/*--------------------------------------------------------------------
*
* Function...: SubmitList
*
* Description: .
*
*-------------------------------------------------------------------*/
INT CommDevice::SubmitList(List* msglist, INT id) // ***, ObjList* eventlist)
{
ListIterator iterator(*msglist);
PMessage msg = (PMessage)&(iterator.Current());
PMessage answer = (Message *)NULL;
INT submitErr = 0;
// #if !C_OS & C_WIN311
// in windows we need to get to the ASKUP during the initialize retries
if (theState==COMM_STOPPED) {
submitErr = theState; // exit out of this routine - ex NO_COMM
}
// #endif
while (msg && (submitErr == 0)) {
// Send the message
AskUps(msg);
//check the message for an error
if (msg->getErrcode()) {
submitErr = msg->getErrcode();
}
else {
List* newmsglist = new List();
if (newmsglist != NULL) {
if (theProtocol) {
theProtocol->InterpretMessage(msg,theEventList, newmsglist);
}
else {
submitErr = ErrRETRYING_COMM;
}
if (msg->getErrcode()) {
submitErr = msg->getErrcode();
}
else if (submitErr == 0) {
// If the Protocol has decided additional messages are
// required to be sent as the result of the response,
// send these before continuing.
//
if (newmsglist->GetItemsInContainer()) {
submitErr = SubmitList(newmsglist);
}
msg = (PMessage)iterator.Next();
}
newmsglist->FlushAll();
delete newmsglist;
newmsglist = NULL;
}
else {
submitErr = ErrMEMORY;
}
}
}
return submitErr;
}
/*--------------------------------------------------------------------
*
* Function...: RegisterEvent
*
* Description:
*
*-------------------------------------------------------------------*/
INT CommDevice::RegisterEvent(INT id, UpdateObj* object)
{
INT err = ErrNO_ERROR;
UpdateObj::RegisterEvent(id, object);
// Add the event to the poll list
PTransactionGroup transaction_group = new TransactionGroup(GET);
PTransactionItem transaction_item = new TransactionItem(GET, id, "");
if ((transaction_group != NULL) && (transaction_item != NULL)) {
transaction_group->AddTransactionItem(transaction_item);
if (theProtocol)
err = theProtocol->BuildPollTransactionGroupMessages(transaction_group);
else
err = ErrRETRYING_COMM;
if (!err)
{
int listAllocated = FALSE;
if (thePollList == (PList) NULL)
thePollList = new ProtectedList;
thePollList->Append(transaction_group);
if (theCurrentTransaction == (PTransactionGroup)NULL)
theCurrentTransaction = (PTransactionGroup) thePollList->GetHead();
if (thePollIterator == (PListIterator)NULL)
thePollIterator = new ListIterator (*thePollList);
}
else
{
delete transaction_group;
transaction_group = NULL;
}
}
else {
err = ErrMEMORY;
// Cleanup allocations so that we don't leak
delete transaction_item;
transaction_item = NULL;
delete transaction_group;
transaction_group = NULL;
}
// Ignore errors that come back from BuildPollTransactionGroupMessages
// since the worst that will happen is the parameter won't be polled for
if (err == ErrRETRYING_COMM)
{
return err;
}
else
{
return ErrNO_ERROR;
}
}
/*--------------------------------------------------------------------
*
* Function...: UnregisterEvent
*
* Description: .
*
*-------------------------------------------------------------------*/
INT CommDevice::UnregisterEvent(INT id, UpdateObj* object)
{
UpdateObj::UnregisterEvent(id, object);
if ( !(theDispatcher->GetRegisteredCount(id)) && // If Empty List
thePollIterator && thePollList ) // just in case
{
PTransactionGroup tmp = (PTransactionGroup)NULL;
thePollIterator->Reset();
PTransactionGroup group = (PTransactionGroup) &(thePollIterator->Current());
Access();
while (group != (PTransactionGroup)NULL)
{
tmp = (PTransactionGroup)thePollIterator->Next();
if (group->GetFirstTransactionItem()->GetCode() == id)
{
thePollList->Detach((RObj)*group);
delete group;
group = NULL;
//
// We have to do this because if we remove the
// transaction we're currently pointing to we
// die
//
thePollIterator->Reset();
theCurrentTransaction = (PTransactionGroup) thePollList->GetHead();
break;
}
group = tmp;
}
Release();
}
return ErrNO_ERROR;
}
/*--------------------------------------------------------------------
*
* Function...: Equal
*
* Description: .
*
*-------------------------------------------------------------------*/
INT CommDevice::Equal(RObj item) const
{
RCommDevice comp = (RCommDevice)item;
if ( theController == comp.GetController() )
{
if ( theProtocol == comp.GetProtocol() )
{
if ( thePort == comp.GetPort() )
{
return TRUE;
}
}
}
return FALSE;
}
/*--------------------------------------------------------------------
*
* Function...: HandleEvents
*
* Description: .
*
*-------------------------------------------------------------------*/
INT CommDevice::HandleEvents()
{
INT err = ErrNO_ERROR;
PEvent event = (PEvent)NULL;
if (theEventIterator)
{
theEventIterator->Reset();
event = (PEvent) &(theEventIterator->Current());
}
else
{
err = ErrRETRYING_COMM;
}
while (event && (err == ErrNO_ERROR))
{
PEvent tmp = (PEvent)NULL;
if (theEventList && theEventIterator &&
(theEventList->GetItemsInContainer() > 0))
{
// Remove the event before dispatching it to prevent
// unwanted loops
tmp = (PEvent) theEventIterator->Next();
theEventList->Detach((RObj)*event);
UpdateObj::Update(event);
delete event;
event = tmp;
}
else
{
err = ErrRETRYING_COMM;
}
}
return err;
}
/*--------------------------------------------------------------------
*
* Function...: Poll
*
* Description: .
*
*-------------------------------------------------------------------*/
INT CommDevice::Poll()
{
time_t ElapsedTime = time((time_t*)NULL);
List *newmsglist = (List*)NULL;
INT err = ErrNO_ERROR;
if ( (theSleepingFlag == FALSE) && (theCurrentTransaction) && (err == ErrNO_ERROR))
{
if(pollStartTime ==0)
{
pollStartTime = time((time_t*)NULL);
}
Access();
PTransactionGroup transactionGroup = theCurrentTransaction;
PMessage msg = (PMessage) transactionGroup->GetProtocolMessageList()->GetHead();
if ((theState == COMM_STOPPED) || (theState == PAUSE_POLLING))
{
// sleep and let the CPU get a break, maybe things will be ;
// better when we wake up.;
theSleepingFlag = TRUE;
}
else
{
// If there are no comm problems;
AskUps(msg); // Should this be there ???;
theAbortSem->TimedWait(500);
if (msg->getErrcode() == ErrCOMMUNICATION_LOST)
{
theCurrentTransaction = (PTransactionGroup)NULL;
}
else
{
List* msglist = (List*)NULL;
if (msg->getResponse())
{
theProtocol->SetCurrentTransactionGroup(theCurrentTransaction);
theProtocol->InterpretMessage(msg, theEventList, msglist);
if (msglist)
{
SubmitList(msglist);
}
PList a_list = transactionGroup->GetTransactionItemList();
ListIterator itemIter(*a_list);
for (INT i = 0; i < a_list->GetItemsInContainer(); i++)
{
RTransactionItem transItem = (RTransactionItem)itemIter.Current();
// Check transItem for NULL. We have to check the address because
// the previous call returns a reference instead of a pointer.
// Fixes bug #227550
if ((&transItem!=NULL) && (!(transItem.GetErrorCode())))
{
Event event(transItem.GetCode(), transItem.GetValue());
UpdateObj::Update(&event);
}
itemIter++;
}
}
// Clean out old errors and responses;
msg->setErrcode(ErrNO_ERROR);
PTransactionGroup tmp = (PTransactionGroup)thePollIterator->Next();
if (tmp == (PTransactionGroup)NULL)
{
thePollIterator->Reset();
theCurrentTransaction = (PTransactionGroup)thePollList->GetHead();
theSleepingFlag = TRUE;
thePollIsDone = TRUE;
}
else
{
theCurrentTransaction = tmp;
}
}
}
msg->ReleaseResponse();
Release();
if ((err == ErrNO_ERROR) && theEventList && theEventList->GetItemsInContainer())
{
err = HandleEvents();
theAbortSem->TimedWait(250);
}
}
else if (theCurrentTransaction == NULL)
{
// puts("No transactions to process");
}
// Give up CPU
_theTimerManager->Wait(0L);
theAbortSem->TimedWait(250);
if (theSleepingFlag == TRUE)
{
ULONG elapsed = (ULONG)(time((time_t*)NULL) - pollStartTime);
if (elapsed > (ULONG)thePollInterval)
{
theSleepingFlag = FALSE;
}
else
{
theAbortSem->TimedWait(1000 * ((thePollInterval - elapsed)+ 1));
}
// for single-threaded apps, this is used outside here,
// and set externally
pollStartTime = 0;
}
return err;
}
//-------------------------------------------------------------------
VOID CommDevice::StartPollThread ()
{
// Create a thread which will poll the server at a regular interval
// for updated information on events in the poll list
PollLoop *pollLoop = new PollLoop (this);
thePollThread = new Thread (pollLoop);
if (thePollThread != NULL) {
thePollThread->Start();
}
}
VOID CommDevice::OkToPoll ()
{
if (!thePollThread) {
StartPollThread();
}
//
// We must give up the CPU here to allow the poll thread to start running.
// Otherwise we release the poll thread before we call wait, leaving the
// wait to hang
//
_theTimerManager->Wait(500L);
if (thePollThread) {
thePollThread->Release();
}
}
VOID CommDevice::Access()
{
theAskLock->Request();
}
VOID CommDevice::Release()
{
theAskLock->Release();
}
/*****************************************************************************
*
* PollLoop::ThreadMain is run within a seperate thread. It's purpose is to
* periodically look at the events on the poll list and ask the server for
* updated information on each event.
*
****************************************************************************/
PollLoop::PollLoop (PCommDevice aDevice)
: theDevice (aDevice)
{
SetThreadName("APC UPS Polling");
}
PollLoop::~PollLoop ()
{
}
VOID PollLoop::ThreadMain () {
// Wait for the thread to be released to start polling
Wait();
while (ExitNow() == FALSE) {
if(theDevice->HasLostComm()) {
TimedWait(5000);
}
else {
theDevice->Poll();
}
}
DoneExiting();
}