- FunctionCore/SelectableCore: - Replace Channel->State with InState - Add Channel-OutState (ready for output) updated by ChannelStateEvent() - TimingCore: - Add GetUpCounter() for calculating uptime string (in seconds)
850 lines
26 KiB
C++
850 lines
26 KiB
C++
/*
|
|
* SelectableBare.cpp
|
|
*
|
|
* Created on: Feb 2019
|
|
* Author: wentzelc
|
|
*/
|
|
|
|
// Standard C/C++ Libraries
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
|
|
// redA Libraries
|
|
#include "ApplicationCore.h"
|
|
#include "SelectableCore.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Global Vars
|
|
extern char * ProcessName;
|
|
extern CApplication * Application;
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
CSelectableBare::CSelectableBare( const char * pName, const char * pType ) : CFunctionCore( pName, pType )
|
|
{
|
|
// Quick access
|
|
Selector = Application->Selector;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CSelectableBare::~CSelectableBare()
|
|
{
|
|
THandle * NextHandle = NULL;
|
|
|
|
// Destroy File Handles
|
|
while (FirstHandle)
|
|
{
|
|
// Close handle if open
|
|
if ((FirstHandle->State == csOpen) || (FirstHandle->State == csWaitingtoOpen)) {
|
|
Close( FirstHandle, false );
|
|
}
|
|
|
|
NextHandle = FirstHandle->Next;
|
|
DestroyHandle( FirstHandle );
|
|
FirstHandle = NextHandle;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDataMember * CSelectableBare::GetHandleAddress( THandle * Handle, const char * HandleRef )
|
|
{
|
|
CDataMember * AddressDef = NULL;
|
|
char NamePath[100];
|
|
char * Address;
|
|
|
|
// Handle renamed?
|
|
sprintf( NamePath, "Application/Addresses/Rename/%s", HandleRef );
|
|
if (!(Address = (char*)Application->Config->GetChStr( NamePath, NULL, false ))) {
|
|
Address = (char*)HandleRef;
|
|
}
|
|
|
|
// Get address def
|
|
if ((AddressDef = Application->AddressList->GetChild( Address ))) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Use address '%s' ('%s')",
|
|
ProcessName, Name, Handle->Name, Address, HandleRef );
|
|
}
|
|
return AddressDef;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableBare::CreateHandle( const char * HandleName, bool CreateChannel )
|
|
{
|
|
THandle ** Handle = NULL;
|
|
|
|
// Find Handle by Name or get end of list
|
|
Handle = &FirstHandle;
|
|
while ( *Handle && strcmp( HandleName, (*Handle)->Name ))
|
|
Handle = &((*Handle)->Next);
|
|
|
|
// Create if necessary
|
|
if (!*Handle)
|
|
{
|
|
// Create File handle at end of list
|
|
*Handle = new THandle;
|
|
|
|
// Set name
|
|
if (HandleName) {
|
|
(*Handle)->Name = (char*)malloc( strlen(HandleName)+1 );
|
|
strcpy( (*Handle)->Name, HandleName );
|
|
}
|
|
|
|
// Set File Descriptor
|
|
(*Handle)->Function = this;
|
|
(*Handle)->FD = -1;
|
|
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Created",
|
|
ProcessName, Name, HandleName );
|
|
}
|
|
|
|
// Create Matching Channel
|
|
if (CreateChannel) {
|
|
(*Handle)->Channel = AddChannel( HandleName, CH_off );
|
|
}
|
|
|
|
return *Handle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::RemoveHandle( THandle * Handle )
|
|
{
|
|
THandle ** HandlePtr = NULL;
|
|
|
|
// Validate
|
|
if (!Handle || (Handle->State == csOpen) || (Handle->State == csWaitingtoOpen)) {
|
|
return false;
|
|
}
|
|
|
|
// Find in List
|
|
HandlePtr = &FirstHandle;
|
|
while (*HandlePtr && (*HandlePtr != Handle)) {
|
|
HandlePtr = &((*HandlePtr)->Next);
|
|
}
|
|
// Remove from list if found
|
|
if (*HandlePtr) {
|
|
*HandlePtr = (*HandlePtr)->Next;
|
|
}
|
|
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Removed",
|
|
ProcessName, Name, Handle->Name );
|
|
|
|
// Destroy Child handle
|
|
DestroyHandle( Handle );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::DestroyHandle( THandle * Handle )
|
|
{
|
|
// Validate Handle
|
|
if (!Handle)
|
|
return false;
|
|
|
|
// Clear parameters
|
|
if (Handle->Name)
|
|
free( Handle->Name );
|
|
if (Handle->Path)
|
|
free( Handle->Path );
|
|
|
|
// Destroy Pointer
|
|
delete Handle;
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::HandleState( THandle * Handle, EConnectState State )
|
|
{
|
|
EChannelState ChannelState = CH_off;
|
|
|
|
// Validate
|
|
if (!Handle || (Handle->State == State))
|
|
return false;
|
|
|
|
// Set Call back
|
|
if (Handle->StateCallback[ (int)State ])
|
|
(Handle->StateCallback[ (int)State ])( this, Handle, State );
|
|
|
|
// Change state
|
|
Handle->State = State;
|
|
|
|
// Update Channel
|
|
if (Handle->Channel) {
|
|
if ((Handle->State == csPreparing) || (Handle->State == csPrepared) || (Handle->State == csWaitingtoOpen))
|
|
ChannelState = CH_wait;
|
|
else if ((Handle->State == csOpen) || (Handle->State == csDataWaiting))
|
|
ChannelState = CH_ready;
|
|
else
|
|
ChannelState = CH_off;
|
|
if (Handle->Channel->InState != ChannelState)
|
|
SetChannelState( Handle->Channel, ChannelState );
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::SetCallback( THandle * Handle, EConnectState pState, FHandleCallback pCallback )
|
|
{
|
|
// Validate
|
|
if (!Handle) {
|
|
return false;
|
|
}
|
|
|
|
// Set callback
|
|
Handle->StateCallback[ (int)pState ] = pCallback;
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::SetAutoManage( THandle * Handle, bool AutoManage, bool Persistent, int ReopenDelay, int CloseTimeout )
|
|
{
|
|
// Validate
|
|
if (!Handle) {
|
|
return false;
|
|
}
|
|
|
|
// Set params
|
|
Handle->AutoManage = AutoManage;
|
|
Handle->Persistent = Persistent;
|
|
Handle->ReopenDelay = ReopenDelay;
|
|
Handle->CloseTimeout = CloseTimeout;
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::SetInBuffer( THandle * Handle, int InBufSize, int InTimeout, const char * InMarker, int InMarkerLen )
|
|
{
|
|
// Validate
|
|
if (!Handle) {
|
|
return false;
|
|
}
|
|
|
|
// Input Buffer
|
|
if (Handle->InBuffer) {
|
|
delete Handle->InBuffer;
|
|
Handle->InBuffer = NULL;
|
|
}
|
|
if (InBufSize) {
|
|
Handle->InBuffer = (CRollingBuffer*) new CRollingBuffer( InBufSize );
|
|
}
|
|
|
|
// Set Input Timeout
|
|
Handle->InTimeout = InTimeout;
|
|
|
|
// Set Input Markers
|
|
if (InMarkerLen && InMarker) {
|
|
Handle->InMarkerLen = InMarkerLen;
|
|
Handle->InMarker = (char *)malloc( InMarkerLen+1 );
|
|
memcpy( Handle->InMarker, InMarker, InMarkerLen );
|
|
Handle->InMarker[InMarkerLen] = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::SetOutBuffer( THandle * Handle, int OutBufSize )
|
|
{
|
|
// Validate
|
|
if (!Handle) {
|
|
return false;
|
|
}
|
|
|
|
// Output Buffer
|
|
if (Handle->OutBuffer) {
|
|
delete Handle->OutBuffer;
|
|
Handle->OutBuffer = NULL;
|
|
}
|
|
if (OutBufSize) {
|
|
Handle->OutBuffer = (CRollingBuffer*) new CRollingBuffer( OutBufSize );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::ProcessInputBuffer( THandle * Handle, bool Force )
|
|
{
|
|
int Pos = 0;
|
|
int Len = 0;
|
|
char * Data = NULL;
|
|
|
|
// Check if buffered data
|
|
if (!Handle || !Handle->InBuffer || !Handle->InBuffer->Len()) {
|
|
return false;
|
|
}
|
|
|
|
// Check if forced processed
|
|
if (Force || (!Handle->InTimeout && !Handle->InMarkerLen))
|
|
{
|
|
// Show Packet
|
|
Len = Handle->InBuffer->Peek( &Data );
|
|
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - IN-T:",
|
|
ProcessName, Name, Handle->Name );
|
|
|
|
// Write buffer to Outputs
|
|
if ((Handle->Type == ctTCPremote) || (Handle->Type == ctUNIXremote)) {
|
|
Output( Handle->Parent->Channel, NULL, true, Data, Len );
|
|
} else {
|
|
Output( Handle->Channel, NULL, true, Data, Len );
|
|
}
|
|
|
|
// Clear processed bytes from buffer
|
|
Handle->InBuffer->Clear( Len );
|
|
}
|
|
else
|
|
{
|
|
// Search for end of packet marker
|
|
while (Handle->InBuffer->FindStr( Handle->InMarker, Handle->InMarkerLen, Pos ))
|
|
{
|
|
// Show Packet
|
|
Len = Handle->InBuffer->Peek( &Data, 0, Pos+Handle->InMarkerLen );
|
|
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - IN-M:",
|
|
ProcessName, Name, Handle->Name );
|
|
|
|
// Write buffer to Outputs
|
|
if ((Handle->Type == ctTCPremote) || (Handle->Type == ctUNIXremote)) {
|
|
Output( Handle->Parent->Channel, NULL, true, Data, Len );
|
|
} else {
|
|
Output( Handle->Channel, NULL, true, Data, Len );
|
|
}
|
|
|
|
// Clear processed bytes from buffer
|
|
Handle->InBuffer->Clear( Len );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableBare::Open( THandle * Handle )
|
|
{
|
|
THandle * NewHandle = NULL;
|
|
|
|
|
|
// Validate
|
|
if (!Handle || (Handle->Type == ctNone)) {
|
|
return -1;
|
|
} else if (Handle->State == csOpen) {
|
|
return Handle->FD;
|
|
}
|
|
|
|
// Check if port exits
|
|
if (access( Handle->Path, F_OK ) != 0)
|
|
{
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Path not found [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
return -1;
|
|
}
|
|
|
|
// Open Port
|
|
Handle->FD = open( Handle->Path, O_RDWR );
|
|
if (Handle->FD == -1)
|
|
{
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Could not open Path [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
return -1;
|
|
}
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Path opened [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
|
|
// Add to Select Lists
|
|
if (Selector) {
|
|
Selector->Add( Handle->FD, true, false, Handle, this );
|
|
}
|
|
|
|
// Set timer (for re-open or auto-close)
|
|
SetStartTime( &Handle->LastAction );
|
|
|
|
// Set state
|
|
HandleState( Handle, csOpen );
|
|
return (NewHandle)? NewHandle->FD : -1;
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Delete socket
|
|
bool CSelectableBare::Close( THandle * Handle, bool QuickReopen )
|
|
{
|
|
bool Fail;
|
|
|
|
// Validate
|
|
if (!Handle || (Handle->FD == -1))
|
|
return false;
|
|
|
|
// Close Handle
|
|
Fail = (close( Handle->FD ))? true : false;
|
|
|
|
// Remove from Select List
|
|
if (!Fail && Selector) {
|
|
if (Handle->Type != ctUDPremote) {
|
|
Selector->Remove( Handle->FD, true, true );
|
|
}
|
|
}
|
|
|
|
// Reset FD
|
|
Handle->FD = ((Fail)? Handle->FD : -1);
|
|
|
|
// Start timer (for re-open)
|
|
if (QuickReopen)
|
|
ClearStartTime( &(Handle->LastAction) );
|
|
else
|
|
SetStartTime( &(Handle->LastAction) );
|
|
|
|
// Show action
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - %s",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed") );
|
|
|
|
// Set State
|
|
HandleState( Handle, ((Fail)? csFailed : csClosed) );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Device Interface
|
|
bool CSelectableBare::Read( THandle * Handle )
|
|
{
|
|
int BytesRead = 0;
|
|
int BytesWaiting = -1;
|
|
|
|
// Validate
|
|
if (!Handle || (Handle->State == csNone) || (Handle->State == csFailed) || (Handle->State == csClosed)) {
|
|
return false;
|
|
}
|
|
|
|
// Log Read Event
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Read Event",
|
|
ProcessName, Name, Handle->Name );
|
|
|
|
// Check if socket ready (non-block open in progress)
|
|
if (Handle->State == csWaitingtoOpen)
|
|
{
|
|
Open( Handle );
|
|
|
|
// Reset Timer (for auto-close)
|
|
SetStartTime( &(Handle->LastAction) );
|
|
return true;
|
|
}
|
|
|
|
// Check if anything to read
|
|
ioctl( Handle->FD, FIONREAD, &BytesWaiting );
|
|
|
|
// Error on port
|
|
if (BytesWaiting < 0)
|
|
{
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Data waiting error (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
|
|
// Close Handle
|
|
Close( Handle, false );
|
|
return false;
|
|
}
|
|
|
|
// Validate
|
|
if (Handle->State != csOpen) {
|
|
return false;
|
|
}
|
|
|
|
// Read File directly into buffer
|
|
if (Handle->InBuffer)
|
|
{
|
|
errno = 0;
|
|
BytesRead = Handle->InBuffer->ReadFromFD( Handle->FD, BytesWaiting );
|
|
|
|
// Report failure
|
|
if (errno) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Error reading data [%d/%d] (%s)",
|
|
ProcessName, Name, Handle->Name, -BytesRead, BytesWaiting, strerror(errno) );
|
|
}
|
|
else if (BytesRead < BytesWaiting) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Incomplete data read [%d/%d]",
|
|
ProcessName, Name, Handle->Name, BytesRead, BytesWaiting );
|
|
}
|
|
|
|
if (BytesRead != 0) {
|
|
// Process Buffer
|
|
ProcessInputBuffer( Handle, false );
|
|
}
|
|
}
|
|
|
|
// Reset timer
|
|
SetStartTime( &(Handle->InStart) );
|
|
|
|
return (bool)BytesRead;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::Write( THandle * Handle )
|
|
{
|
|
int Len = 0;
|
|
char * Data = NULL;
|
|
int BytesWritten = 0;
|
|
|
|
// Validate
|
|
if (!Handle)
|
|
return false;
|
|
|
|
// Is Handle open?
|
|
if ((Handle->State == csNone) || (Handle->State == csFailed) || (Handle->State == csClosed))
|
|
{
|
|
// May it be opened?
|
|
if (!Handle->AutoManage)
|
|
{
|
|
// Must be opened manually
|
|
return false;
|
|
}
|
|
else if (Timeout( Handle->LastAction, Handle->ReopenDelay ))
|
|
{
|
|
// Attempt to re-open port
|
|
Open( Handle );
|
|
}
|
|
}
|
|
|
|
// Log Ready for Write Event
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Write Event",
|
|
ProcessName, Name, Handle->Name );
|
|
|
|
if (Handle->State == csWaitingtoOpen)
|
|
{
|
|
// Complete socket open process
|
|
Open( Handle );
|
|
|
|
// Reset Timer (for auto-close)
|
|
SetStartTime( &(Handle->LastAction) );
|
|
|
|
// Remove from set for select write
|
|
if (Selector) {
|
|
Selector->Remove( Handle->FD, false, true );
|
|
}
|
|
return true;
|
|
}
|
|
else if (Handle->State == csOpen)
|
|
{
|
|
if (Handle->OutBuffer)
|
|
{
|
|
// Write directly to handle / socket
|
|
errno = 0;
|
|
BytesWritten = Handle->OutBuffer->WriteToFD( Handle->FD );
|
|
|
|
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - OUT:",
|
|
ProcessName, Name, Handle->Name );
|
|
|
|
// Report failure
|
|
if (errno) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Error sending data [%d/%d] (%s)",
|
|
ProcessName, Name, Handle->Name, -BytesWritten, Len, strerror(errno) );
|
|
}
|
|
else if (BytesWritten < Len) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Incomplete data sent [%d/%d]",
|
|
ProcessName, Name, Handle->Name, BytesWritten, Len );
|
|
}
|
|
|
|
if (BytesWritten != 0)
|
|
{
|
|
// Update Buffer
|
|
Handle->OutBuffer->Clear( (BytesWritten > 0)? BytesWritten : -BytesWritten ); // negative value reported if error occurred
|
|
|
|
// Check if Buffer empty
|
|
if (!Handle->OutBuffer->Len()) {
|
|
// Remove from Select Write list
|
|
if (Selector) {
|
|
Selector->Remove( Handle->FD, false, true );
|
|
}
|
|
}
|
|
|
|
// Reset timeout
|
|
SetStartTime( &(Handle->LastAction) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No Output buffer to write from, so remove from Write list
|
|
if (Selector) {
|
|
Selector->Remove( Handle->FD, false, true );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableBare::ReadFromFD( int FD, char * Data, int MaxLen )
|
|
{
|
|
int BytesRead = 0;
|
|
int TotalRead = 0;
|
|
int DataRemain = MaxLen;
|
|
bool Error = false;
|
|
|
|
// Check if buffer created
|
|
if ((FD == -1) || (MaxLen < 1)) {
|
|
return 0;
|
|
}
|
|
|
|
// Read Data into buffer
|
|
while (DataRemain)
|
|
{
|
|
// Read from file descriptor
|
|
BytesRead = read( FD, &Data[TotalRead], DataRemain );
|
|
if ((BytesRead < 0)) {
|
|
Error = true;
|
|
errno = (!BytesRead)? 0 : errno; // No error if no bytes written
|
|
break;
|
|
}
|
|
|
|
// Update Data Pointers
|
|
TotalRead += BytesRead;
|
|
DataRemain -= BytesRead;
|
|
|
|
if (DataRemain) {
|
|
usleep( 500 );
|
|
}
|
|
}
|
|
return (Error)? -TotalRead : TotalRead; // Report negative total on error
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableBare::WriteToFD( int FD, const char * Data, int Len, bool Force )
|
|
{
|
|
int BytesWritten = 0;
|
|
int TotalWritten = 0;
|
|
int DataRemain = (Len != -1)? Len : (Data)? strlen(Data) : 0;
|
|
bool Error = false;
|
|
|
|
// Check if buffer created
|
|
if ((FD == -1) || !DataRemain) {
|
|
return 0;
|
|
}
|
|
|
|
// Read Data into buffer
|
|
while (DataRemain)
|
|
{
|
|
// Read from file descriptor
|
|
BytesWritten = write( FD, &Data[TotalWritten], DataRemain );
|
|
if ((BytesWritten <= 0) && (!Force || (errno != EAGAIN))) {
|
|
Error = true;
|
|
errno = (!BytesWritten)? 0 : errno; // No error if no bytes written
|
|
break;
|
|
}
|
|
|
|
// Update Data Pointers
|
|
TotalWritten += BytesWritten;
|
|
DataRemain -= BytesWritten;
|
|
|
|
if (DataRemain) {
|
|
usleep( 500 );
|
|
}
|
|
}
|
|
return (Error)? -TotalWritten : TotalWritten; // Report negative total on error
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableBare::Input( const char * ChannelName, const char * SourceRef, const char * Data, int Len )
|
|
{
|
|
TChannel * Channel = NULL;
|
|
THandle * Handle = NULL;
|
|
int HandleCount = 0;
|
|
int TempWritten = 0;
|
|
int BytesWritten = 0;
|
|
|
|
// Validate
|
|
if (!ChannelName || !Data) {
|
|
return 0;
|
|
}
|
|
|
|
// Get Channel
|
|
if (!(Channel = GetChannel( ChannelName ))) {
|
|
// Channel not found
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s'->'%s' - Input rejected, Channel not found",
|
|
ProcessName, Name, ((SourceRef && *SourceRef)? SourceRef : "(Any)"), ChannelName );
|
|
return 0;
|
|
}
|
|
else if (Channel->InState != CH_ready) {
|
|
// Channel disabled
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s'->'%s' - Input rejected, Channel not Ready",
|
|
ProcessName, Name, ((SourceRef && *SourceRef)? SourceRef : "(Any)"), ChannelName );
|
|
return 0;
|
|
}
|
|
|
|
// Log event
|
|
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Channel '%s'->'%s' - IN:",
|
|
ProcessName, Name, ((SourceRef && *SourceRef)? SourceRef : "(Any)"), ChannelName );
|
|
|
|
// Find Linked handle
|
|
Handle = FirstHandle;
|
|
while( Handle )
|
|
{
|
|
if (Handle->Channel && !strcasecmp( ChannelName, Handle->Channel->Name ))
|
|
{
|
|
// Input to Handle
|
|
TempWritten = OutputHandle( Handle, Data, Len );
|
|
BytesWritten = (TempWritten > BytesWritten)? TempWritten : BytesWritten;
|
|
HandleCount++;
|
|
}
|
|
Handle = Handle->Next;
|
|
}
|
|
|
|
if (!HandleCount) {
|
|
// Handle not found
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s'->'%s' - Input rejected, No Handles found",
|
|
ProcessName, Name, ((SourceRef && *SourceRef)? SourceRef : "(Any)"), ChannelName );
|
|
return 0;
|
|
}
|
|
|
|
return BytesWritten;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableBare::OutputHandle( THandle * Handle, const char * Data, int Len )
|
|
{
|
|
int BytesWritten = 0;
|
|
int DataLen = (Len != -1)? Len : (Data)? strlen(Data) : 0;
|
|
|
|
if ((Handle->State != csOpen))
|
|
{
|
|
// Check if auto-managed handle
|
|
if (!Handle->AutoManage)
|
|
{
|
|
// Handle is not open or auto-managed
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Handle not Open (not auto-managed)",
|
|
ProcessName, Name, Handle->Name );
|
|
return 0;
|
|
}
|
|
else if (Timeout( Handle->LastAction, Handle->ReopenDelay ))
|
|
{
|
|
// Complete opening process
|
|
Open( Handle );
|
|
|
|
// Check if Handle is open
|
|
if ((Handle->State == csPreparing) || (Handle->State == csPrepared)) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Preparing (auto-managed) Handle",
|
|
ProcessName, Name, Handle->Name );
|
|
return 0;
|
|
}
|
|
else if (Handle->State == csWaitingtoOpen) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Waiting to open (auto-managed) Handle",
|
|
ProcessName, Name, Handle->Name );
|
|
return 0;
|
|
}
|
|
else if (Handle->State != csOpen) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, (auto-managed) Handle failed to Open",
|
|
ProcessName, Name, Handle->Name );
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Retry (auto-managed) Handle re-open in %d ms",
|
|
ProcessName, Name, Handle->Name, TimeLeft( Handle->LastAction, Handle->ReopenDelay) );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Check packet length
|
|
if (Len == -1) {
|
|
Len = strlen( Data );
|
|
}
|
|
|
|
// Decide where to put data
|
|
if (Handle->OutBuffer)
|
|
{
|
|
// Write to buffer
|
|
BytesWritten = Handle->OutBuffer->Push( true, Data, Len );
|
|
|
|
// Add to select write list
|
|
if (BytesWritten && Selector) {
|
|
Selector->Add( Handle->FD, false, true, Handle, this );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Write directly to handle / socket
|
|
errno = 0;
|
|
BytesWritten = WriteToFD( Handle->FD, Data, Len, true );
|
|
|
|
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - OUT:",
|
|
ProcessName, Name, Handle->Name );
|
|
|
|
// Report failure
|
|
if (errno) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Error sending data [%d/%d] (%s)",
|
|
ProcessName, Name, Handle->Name, -BytesWritten, DataLen, strerror(errno) );
|
|
}
|
|
else if (BytesWritten < DataLen) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Incomplete data sent [%d/%d]",
|
|
ProcessName, Name, Handle->Name, BytesWritten, DataLen );
|
|
}
|
|
|
|
if (BytesWritten != 0) {
|
|
// Reset timeout
|
|
SetStartTime( &(Handle->LastAction) );
|
|
}
|
|
}
|
|
|
|
return BytesWritten;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableBare::Process()
|
|
{
|
|
THandle * Handle = NULL;
|
|
|
|
// Check all handles
|
|
Handle = FirstHandle;
|
|
while (Handle)
|
|
{
|
|
// Auto manage handles
|
|
if (Handle->State == csPrepared) {
|
|
// Proceed to open
|
|
Open( Handle );
|
|
}
|
|
else if ((Handle->State != csOpen) && Handle->AutoManage && Handle->Persistent) {
|
|
// Try to re-open port after delay
|
|
if (Timeout( Handle->LastAction, Handle->ReopenDelay )) {
|
|
Open( Handle );
|
|
}
|
|
}
|
|
|
|
// Check Input buffers
|
|
if (Handle->InBuffer && (Handle->InBuffer->Len() > 0)) {
|
|
// Check duration since last PortIn
|
|
if (Timeout( Handle->InStart, Handle->InTimeout )) {
|
|
// Process Input
|
|
ProcessInputBuffer( Handle, true );
|
|
|
|
// Reset timer
|
|
ClearStartTime( &(Handle->InStart) );
|
|
}
|
|
}
|
|
|
|
// Check for auto close (but not on servers)
|
|
if ((Handle->State == csOpen) && Handle->AutoManage && !Handle->Persistent) {
|
|
// Close port after timeout
|
|
if (Timeout( Handle->LastAction, Handle->CloseTimeout )) {
|
|
Close( Handle, true );
|
|
}
|
|
}
|
|
Handle = Handle->Next;
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|