From ebaf52a1a074f2146bf6123e1c9cc59583992c2e Mon Sep 17 00:00:00 2001 From: Charl Wentzel Date: Fri, 29 Mar 2019 15:44:13 +0200 Subject: [PATCH] Important Update: - Split SelectableCore.cpp into two classes: SelectableBare & SelectableCore --- CMakeLists.txt | 3 +- SelectCore.cpp | 2 +- SelectableBare.cpp | 939 +++++++++++++++++++++++++++++++++++++++++++++ SelectableCore.cpp | 478 +---------------------- SelectableCore.h | 107 ++++-- 5 files changed, 1010 insertions(+), 519 deletions(-) create mode 100644 SelectableBare.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c39d4d..9c28820 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,4 +2,5 @@ PROJECT(lib_redAcore) ADD_LIBRARY(redAcore DateTimeCore.cpp LogCore.cpp SignalCore.cpp DataTreeCore.cpp JSONparseCore.cpp CharBufferCore.cpp LiteProtocolCore.cpp ItemBufferCore.cpp EventBufferCore.cpp - ApplicationCore.cpp FunctionCore.cpp FileCore.cpp SelectCore.cpp SelectableCore.cpp WatchdogCore.cpp DeviceCore.cpp ) + ApplicationCore.cpp FunctionCore.cpp FileCore.cpp SelectCore.cpp SelectableBare.cpp SelectableCore.cpp + WatchdogCore.cpp DeviceCore.cpp ) diff --git a/SelectCore.cpp b/SelectCore.cpp index 67061f4..eeeec5e 100644 --- a/SelectCore.cpp +++ b/SelectCore.cpp @@ -84,7 +84,7 @@ void CSelect::Clear() //--------------------------------------------------------------------------- // Add Select File Descriptor -void CSelect::Add( int FD, bool Read, bool Write, THandle * Handle, CSelectableCore * Function ) +void CSelect::Add( int FD, bool Read, bool Write, THandle * Handle, CSelectableBare * Function ) { TSelectHandle ** SelectHandle = NULL; diff --git a/SelectableBare.cpp b/SelectableBare.cpp new file mode 100644 index 0000000..86dc00f --- /dev/null +++ b/SelectableBare.cpp @@ -0,0 +1,939 @@ +/* + * SelectableBare.cpp + * + * Created on: Feb 2019 + * Author: wentzelc + */ + +// Standard C/C++ Libraries +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +// 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; + + // Handles + FirstHandle = NULL; +} +//--------------------------------------------------------------------------- + +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; + } +} +//--------------------------------------------------------------------------- + +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 = (THandle*)calloc( 1, sizeof(THandle) ); + + // Set name + if (HandleName) { + (*Handle)->Name = (char*)malloc( strlen(HandleName)+1 ); + strcpy( (*Handle)->Name, HandleName ); + } + + // Set File Descriptor + (*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 ); + } + + 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 ); + if (Handle->HostName) + free( Handle->HostName ); + if (Handle->PortName) + free( Handle->PortName ); + if (Handle->AddressInfo) + freeaddrinfo( Handle->AddressList ); + + // Destroy Buffers + if (Handle->InBuffer) + delete Handle->InBuffer; + if (Handle->OutBuffer) + delete Handle->OutBuffer; + + // Clear Input Markers + if (Handle->InMarker) + free( Handle->InMarker ); + + // Destroy Pointer + free( Handle ); + return true; +} +//--------------------------------------------------------------------------- + +bool CSelectableBare::ClearHandle( THandle * Handle ) +{ + // Validate + if (!Handle) { + return false; + } + + // Reset Type related parameters + if (Handle->Path) { + free( Handle->Path ); + Handle->Path = NULL; + } + if (Handle->HostName) { + free( Handle->HostName ); + Handle->HostName = NULL; + } + if (Handle->PortName) { + free( Handle->PortName ); + Handle->PortName = NULL; + } + if (Handle->AddressList) { + freeaddrinfo( Handle->AddressList ); + Handle->AddressList = NULL; + Handle->AddressInfo = NULL; + } + + // Reset Parameters + Handle->Type = ctNone; + + // Log event + if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Set as None", + ProcessName, Name, Handle->Name ); + 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->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, Data, Len ); + } else { + Output( Handle->Channel, Data, Len ); + } + + // Clear processed bytes from buffer + Handle->InBuffer->Clear( Len ); + } + else + { +if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - IN:", + ProcessName, Name, Handle->Name ); + + // 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, Data, Len ); + } else { + Output( Handle->Channel, Data, Len ); + } + + // Clear processed bytes from buffer + Handle->InBuffer->Clear( Len ); + } + } + return true; +} +//--------------------------------------------------------------------------- + +int CSelectableBare::Open( THandle * Handle, bool DelayResolve ) +{ + 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 state + ChangeState( Handle, csOpen ); + + // Set timer (for re-open or auto-close) + SetStartTime( &Handle->LastAction ); + 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; + ChangeState( Handle, ((Fail)? csFailed : csClosed) ); + + // 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") ); + + // 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); + 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, true ); + } + } + + // 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 * 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' - Input rejected, Channel not found", + ProcessName, Name, ChannelName ); + return 0; + } + else if (!Channel->InputEnabled) { + // Channel disabled + if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Input rejected, Channel input disabled", + ProcessName, Name, ChannelName ); + return 0; + } + + // Log event + if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Channel '%s' - IN:", + ProcessName, Name, 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' - Input rejected, No Handles found", + ProcessName, Name, 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, true ); + + // Check if Handle is open + if (Handle->State == csOpenRequest) { + if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Request to resolve (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 == csOpenRequest)) + { + // Resolve then open socket + if (Timeout( Handle->LastAction, Handle->ResolveDelay )) { + Open( Handle, false ); + } + } + else if (((Handle->State != csOpen) && Handle->AutoManage && Handle->Persistent) ) + { + // Try to re-open port after delay + if (Timeout( Handle->LastAction, Handle->ReopenDelay )) { + Open( Handle, false ); + } + } + + // 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->Type != ctTCPserver) && (Handle->Type != ctUNIXserver) && Handle->AutoManage && !Handle->Persistent) + { + // Close port after timeout + if (Timeout( Handle->LastAction, Handle->CloseTimeout )) { + Close( Handle, true ); + } + } + + Handle = Handle->Next; + } + return true; +} +//--------------------------------------------------------------------------- + +bool CSelectableBare::BuildArgs( const char * ExecPath, int &Count, char * Args[] ) +{ + bool ParamStarted = false; + bool OpenQuotes = false; + char * MatchPos = NULL; + char * StartPos = NULL; + int Len; + + // Validate + if (!ExecPath || !*ExecPath) { + return false; + } + + // Split params + MatchPos = (char*)ExecPath; + for (;;) + { + // Look for whitespace + if (!ParamStarted) + { + // Quotes starts quoted parameter + if (*MatchPos == '"') { + ParamStarted = true; + OpenQuotes = true; + StartPos = MatchPos+1; // Skip starting quote + } + // Non-whitespace starts normal parameter + else if ((*MatchPos != ' ') && (*MatchPos != 0)) { + ParamStarted = true; + StartPos = MatchPos; + } + } + else if (OpenQuotes) + { + // Another quote ends parameter + if (*MatchPos == '"') { + Len = MatchPos-StartPos-1; // Skip end quote + Args[Count] = (char*)malloc( Len+1 ); + strncpy( Args[Count], StartPos, Len ); + Args[Count][Len] = 0; + Count++; + ParamStarted = false; + OpenQuotes = false; + } + } + else + { + // Whitespace ends parameter + if ((*MatchPos == ' ') || (*MatchPos == 0)) { + Len = MatchPos-StartPos; + Args[Count] = (char*)malloc( Len+1 ); + strncpy( Args[Count], StartPos, Len ); + Args[Count][Len] = 0; + Count++; + ParamStarted = false; + } + } + // Next char, unless NULL + if (*MatchPos) + MatchPos++; + else + break; + } + + // Check all parameters closed + if (ParamStarted) { + Count = 0; + Args[0] = NULL; + return false; + } + + // Set last Param to NULL + Args[Count] = NULL; + + return true; +} +//--------------------------------------------------------------------------- + diff --git a/SelectableCore.cpp b/SelectableCore.cpp index 425f42a..1962dcc 100644 --- a/SelectableCore.cpp +++ b/SelectableCore.cpp @@ -40,7 +40,7 @@ CFunctionCore * NewSelectableCore( const char * Name ) { } //--------------------------------------------------------------------------- -CSelectableCore::CSelectableCore( const char * pName, const char * pType ) : CFunctionCore( pName, pType ) +CSelectableCore::CSelectableCore( const char * pName, const char * pType ) : CSelectableBare( pName, pType ) { // Quick access Selector = Application->Selector; @@ -243,107 +243,6 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig ) } //--------------------------------------------------------------------------- -THandle * CSelectableCore::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 = (THandle*)calloc( 1, sizeof(THandle) ); - - // Set name - if (HandleName) { - (*Handle)->Name = (char*)malloc( strlen(HandleName)+1 ); - strcpy( (*Handle)->Name, HandleName ); - } - - // Set File Descriptor - (*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 ); - } - - return *Handle; -} -//--------------------------------------------------------------------------- - -bool CSelectableCore::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 CSelectableCore::DestroyHandle( THandle * Handle ) -{ - // Validate Handle - if (!Handle) - return false; - - // Clear parameters - if (Handle->Name) - free( Handle->Name ); - if (Handle->Path) - free( Handle->Path ); - if (Handle->HostName) - free( Handle->HostName ); - if (Handle->PortName) - free( Handle->PortName ); - if (Handle->AddressInfo) - freeaddrinfo( Handle->AddressList ); - - // Destroy Buffers - if (Handle->InBuffer) - delete Handle->InBuffer; - if (Handle->OutBuffer) - delete Handle->OutBuffer; - - // Clear Input Markers - if (Handle->InMarker) - free( Handle->InMarker ); - - // Destroy Pointer - free( Handle ); - return true; -} -//--------------------------------------------------------------------------- - bool CSelectableCore::SetSerialHandle( THandle * Handle, const char * FileName ) { // Validate @@ -510,122 +409,6 @@ bool CSelectableCore::SetSocketHandle( THandle * Handle, EConnectType Type, con } //--------------------------------------------------------------------------- -bool CSelectableCore::ClearHandle( THandle * Handle ) -{ - // Validate - if (!Handle) { - return false; - } - - // Reset Type related parameters - if (Handle->Path) { - free( Handle->Path ); - Handle->Path = NULL; - } - if (Handle->HostName) { - free( Handle->HostName ); - Handle->HostName = NULL; - } - if (Handle->PortName) { - free( Handle->PortName ); - Handle->PortName = NULL; - } - if (Handle->AddressList) { - freeaddrinfo( Handle->AddressList ); - Handle->AddressList = NULL; - Handle->AddressInfo = NULL; - } - - // Reset Parameters - Handle->Type = ctNone; - - // Log event - if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Set as None", - ProcessName, Name, Handle->Name ); - return true; -} -//--------------------------------------------------------------------------- - -bool CSelectableCore::SetCallback( THandle * Handle, EConnectState pState, FHandleCallback pCallback ) -{ - // Validate - if (!Handle) { - return false; - } - - // Set callback - Handle->StateCallback[ (int)pState ] = pCallback; - return true; -} -//--------------------------------------------------------------------------- - -bool CSelectableCore::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 CSelectableCore::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 CSelectableCore::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; -} -//--------------------------------------------------------------------------- - THandle * CSelectableCore::OpenSerialPort( THandle * Handle ) { // Validate @@ -2153,130 +1936,6 @@ bool CSelectableCore::Write( THandle * Handle ) } //--------------------------------------------------------------------------- -bool CSelectableCore::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->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, Data, Len ); - } else { - Output( Handle->Channel, 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, Data, Len ); - } else { - Output( Handle->Channel, Data, Len ); - } - - // Clear processed bytes from buffer - Handle->InBuffer->Clear( Len ); - } - } - return true; -} -//--------------------------------------------------------------------------- - -int CSelectableCore::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 CSelectableCore::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 CSelectableCore::ReadFromUDP( THandle * Handle, char * RemoteAddr, char * RemotePort, char * Data, int MaxLen ) { int BytesRead = 0; @@ -2324,62 +1983,6 @@ int CSelectableCore::WriteToUDP( THandle * Handle, const char * Data, int Len, b } //--------------------------------------------------------------------------- -int CSelectableCore::Input( const char * ChannelName, 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' - Input rejected, Channel not found", - ProcessName, Name, ChannelName ); - return 0; - } - else if (!Channel->InputEnabled) { - // Channel disabled - if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Channel '%s' - Input rejected, Channel input disabled", - ProcessName, Name, ChannelName ); - return 0; - } - - // Log event - if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Channel '%s' - IN:", - ProcessName, Name, 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' - Input rejected, No Handles not found", - ProcessName, Name, ChannelName ); - return 0; - } - - return BytesWritten; -} -//--------------------------------------------------------------------------- - int CSelectableCore::OutputHandle( THandle * Handle, const char * Data, int Len ) { THandle * ChildHandle = NULL; @@ -2868,82 +2471,3 @@ bool CSelectableCore::ReadSerialConfig( THandle * Handle ) return true; } //--------------------------------------------------------------------------- - -bool CSelectableCore::BuildArgs( const char * ExecPath, int &Count, char * Args[] ) -{ - bool ParamStarted = false; - bool OpenQuotes = false; - char * MatchPos = NULL; - char * StartPos = NULL; - int Len; - - // Validate - if (!ExecPath || !*ExecPath) { - return false; - } - - // Split params - MatchPos = (char*)ExecPath; - for (;;) - { - // Look for whitespace - if (!ParamStarted) - { - // Quotes starts quoted parameter - if (*MatchPos == '"') { - ParamStarted = true; - OpenQuotes = true; - StartPos = MatchPos+1; // Skip starting quote - } - // Non-whitespace starts normal parameter - else if ((*MatchPos != ' ') && (*MatchPos != 0)) { - ParamStarted = true; - StartPos = MatchPos; - } - } - else if (OpenQuotes) - { - // Another quote ends parameter - if (*MatchPos == '"') { - Len = MatchPos-StartPos-1; // Skip end quote - Args[Count] = (char*)malloc( Len+1 ); - strncpy( Args[Count], StartPos, Len ); - Args[Count][Len] = 0; - Count++; - ParamStarted = false; - OpenQuotes = false; - } - } - else - { - // Whitespace ends parameter - if ((*MatchPos == ' ') || (*MatchPos == 0)) { - Len = MatchPos-StartPos; - Args[Count] = (char*)malloc( Len+1 ); - strncpy( Args[Count], StartPos, Len ); - Args[Count][Len] = 0; - Count++; - ParamStarted = false; - } - } - // Next char, unless NULL - if (*MatchPos) - MatchPos++; - else - break; - } - - // Check all parameters closed - if (ParamStarted) { - Count = 0; - Args[0] = NULL; - return false; - } - - // Set last Param to NULL - Args[Count] = NULL; - - return true; -} -//--------------------------------------------------------------------------- - diff --git a/SelectableCore.h b/SelectableCore.h index 499226e..6179aa3 100644 --- a/SelectableCore.h +++ b/SelectableCore.h @@ -45,10 +45,11 @@ typedef struct SSelectHandle TSelectHandle; typedef struct SHandle THandle; class CSelect; +class CSelectableBare; class CSelectableCore; // Callback function for handle events -typedef void (*FHandleCallback)( CSelectableCore * Function, THandle * Handle, EConnectState State ); +typedef void (*FHandleCallback)( CSelectableBare * Function, THandle * Handle, EConnectState State ); //--------------------------------------------------------------------------- @@ -61,7 +62,7 @@ struct SSelectHandle { // Event Object THandle * Handle; - CSelectableCore * Function; + CSelectableBare * Function; // List TSelectHandle * Next; @@ -164,7 +165,7 @@ public: // Manage FDs void Clear(); - void Add( int FD, bool Read, bool Write, THandle * Handle, CSelectableCore * Function = NULL); + void Add( int FD, bool Read, bool Write, THandle * Handle, CSelectableBare * Function = NULL); void Remove( int FD, bool Read, bool Write ); // Testing FDs @@ -180,7 +181,7 @@ CFunctionCore * NewSelectableCore( const char * Name ); //--------------------------------------------------------------------------- -class CSelectableCore : public CFunctionCore +class CSelectableBare : public CFunctionCore { protected: // FDs @@ -208,35 +209,10 @@ protected: return true; } - // Port Operations - THandle * OpenSerialPort( THandle * Handle ); - bool WriteSerialConfig( THandle * Handle ); - bool ReadSerialConfig( THandle * Handle ); - - THandle * OpenLinePrinterPort( THandle * Handle ); - - // File Socket Operations - THandle * OpenForkPipe( THandle * Handle ); - THandle * OpenUNIXserverSocket( THandle * Handle ); - THandle * OpenUNIXclientSocket( THandle * Handle ); - THandle * OpenUNIXremoteSocket( THandle * Handle ); - - // Socket Operations - bool ResolveAddress( THandle * Handle, bool DelayResolve ); - THandle * OpenUDPserverSocket( THandle * Handle, bool DelayResolve ); - THandle * OpenUDPremoteSocket( THandle * Handle, char * RemoteAddr, char * RemotePort ); - THandle * OpenUDPclientSocket( THandle * Handle, bool DelayResolve ); - THandle * OpenTCPserverSocket( THandle * Handle, bool DelayResolve ); - THandle * OpenTCPremoteSocket( THandle * Handle ); - THandle * OpenTCPclientSocket( THandle * Handle, bool DelayResolve ); - // Mutual Operations int ReadFromFD( int FD, char * Data, int MaxLen ); int WriteToFD( int FD, const char * Data, int Len, bool Force ); - int ReadFromUDP( THandle * Handle, char * RemoteAddr, char * RemotePort, char * Data, int MaxLen ); - int WriteToUDP( THandle * Handle, const char * Data, int Len, bool Force ); - // Buffer operations virtual bool ProcessInputBuffer( THandle * Handle, bool Force ); @@ -252,11 +228,11 @@ protected: public: // Life Cycle - CSelectableCore( const char * Name, const char * Type = TYPE_SELECTABLE ); - virtual ~CSelectableCore(); + CSelectableBare( const char * Name, const char * Type = TYPE_SELECTABLE ); + virtual ~CSelectableBare(); // Configuration - virtual bool Init( CDataMember * FunctionConfig ); + virtual bool Init( CDataMember * FunctionConfig ) = 0; // Finding Handles inline THandle * GetHandle( const char * HandleName ) @@ -285,17 +261,10 @@ public: bool SetOutBuffer( THandle * Handle, int OutBufSize ); // Specific port parameters - bool SetSerialHandle( THandle * Handle, const char * FileName ); - bool SetSerialHandleConfig( THandle * Handle, int Baudrate, short DataBits, short Parity, short StopBits, short FlowCtrl, int DataWait ); - bool SetLinePrinterHandle( THandle * Handle, const char * FileName ); - bool SetForkPipeHandle( THandle * Handle, const char * ExecPath ); - bool SetUnixHandle( THandle * Handle, EConnectType Type, const char * FileName, short Queue ); - bool SetSocketHandle( THandle * Handle, EConnectType Type, const char * HostName, const char * PortName, short Queue, long ResolveDelay ); - bool ClearHandle( THandle * Handle ); // FD Operations - virtual int Open( THandle * Handle, bool DelayResolve = false ); + virtual int Open( THandle * Handle, bool DelayResolve = false ) = 0; virtual bool Close( THandle * Handle, bool QuickReopen ); virtual bool Read( THandle * Handle ); virtual bool Write( THandle * Handle ); @@ -330,4 +299,62 @@ public: //--------------------------------------------------------------------------- +class CSelectableCore : public CSelectableBare +{ +protected: + // Port Operations + THandle * OpenSerialPort( THandle * Handle ); + bool WriteSerialConfig( THandle * Handle ); + bool ReadSerialConfig( THandle * Handle ); + + THandle * OpenLinePrinterPort( THandle * Handle ); + + // File Socket Operations + THandle * OpenForkPipe( THandle * Handle ); + THandle * OpenUNIXserverSocket( THandle * Handle ); + THandle * OpenUNIXclientSocket( THandle * Handle ); + THandle * OpenUNIXremoteSocket( THandle * Handle ); + + // Socket Operations + bool ResolveAddress( THandle * Handle, bool DelayResolve ); + THandle * OpenUDPserverSocket( THandle * Handle, bool DelayResolve ); + THandle * OpenUDPremoteSocket( THandle * Handle, char * RemoteAddr, char * RemotePort ); + THandle * OpenUDPclientSocket( THandle * Handle, bool DelayResolve ); + THandle * OpenTCPserverSocket( THandle * Handle, bool DelayResolve ); + THandle * OpenTCPremoteSocket( THandle * Handle ); + THandle * OpenTCPclientSocket( THandle * Handle, bool DelayResolve ); + + // Mutual Operations + int ReadFromUDP( THandle * Handle, char * RemoteAddr, char * RemotePort, char * Data, int MaxLen ); + int WriteToUDP( THandle * Handle, const char * Data, int Len, bool Force ); + +public: + // Life Cycle + CSelectableCore( const char * Name, const char * Type = TYPE_SELECTABLE ); + virtual ~CSelectableCore(); + + // Configuration + virtual bool Init( CDataMember * FunctionConfig ); + + // Specific port parameters + bool SetSerialHandle( THandle * Handle, const char * FileName ); + bool SetSerialHandleConfig( THandle * Handle, int Baudrate, short DataBits, short Parity, short StopBits, short FlowCtrl, int DataWait ); + bool SetLinePrinterHandle( THandle * Handle, const char * FileName ); + bool SetForkPipeHandle( THandle * Handle, const char * ExecPath ); + bool SetUnixHandle( THandle * Handle, EConnectType Type, const char * FileName, short Queue ); + bool SetSocketHandle( THandle * Handle, EConnectType Type, const char * HostName, const char * PortName, short Queue, long ResolveDelay ); + + // FD Operations + virtual int Open( THandle * Handle, bool DelayResolve = false ); + virtual bool Close( THandle * Handle, bool QuickReopen ); + virtual bool Read( THandle * Handle ); + virtual bool Write( THandle * Handle ); + + // Function Interface + int OutputHandle( THandle * Handle, const char * Data, int Len ); + virtual bool Process(); +}; + +//--------------------------------------------------------------------------- + #endif /* REDACORE_SELECTABLECORE_H_ */