- LogCore: - Removed global DebugLevel var, now passed as parameter - Implemented binary output values - FunctionCore, SelectCore and SelectableCore: - internal DebugLevel param implemented - new internal DisplayOutput param (Normal/Hex/Bin) - BufferCore: - Bug fix: allow for empty search string/marker
1266 lines
37 KiB
C++
1266 lines
37 KiB
C++
/*
|
|
* CSelectableCore.cpp
|
|
*
|
|
* Created on: 20 May 2016
|
|
* Author: wentzelc
|
|
*/
|
|
|
|
// redA Libraries
|
|
#include "SelectableCore.h"
|
|
#include "TimingCore.h"
|
|
#include "LogCore.h"
|
|
|
|
// Standard C/C++ Libraries
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
#include <arpa/inet.h>
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
CSelectableCore::CSelectableCore( const char * Name, CSelect * Selector, EDebugLevel pDebugLevel, int pOutputDisplay ) :
|
|
CFunctionCore( Name, pDebugLevel, pOutputDisplay )
|
|
{
|
|
// Handles
|
|
FirstHandle = NULL;
|
|
|
|
// Select
|
|
Select = Selector;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CSelectableCore::~CSelectableCore()
|
|
{
|
|
THandle * NextHandle = NULL;
|
|
|
|
// Destroy File Handles
|
|
while (FirstHandle)
|
|
{
|
|
// Close handle if open
|
|
if ((FirstHandle->State == csOpen) || (FirstHandle->State == csWaitingtoOpen)) {
|
|
Close( FirstHandle, true );
|
|
}
|
|
|
|
NextHandle = FirstHandle->Next;
|
|
DestroyHandle( FirstHandle );
|
|
FirstHandle = NextHandle;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::CreateHandle( const char * HandleName, bool CreateLocalIO, bool AutoManage )
|
|
{
|
|
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*)malloc( sizeof(THandle) );
|
|
memset( *Handle, 0, 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
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Created", Name, HandleName );
|
|
}
|
|
|
|
// Create Matching IO point
|
|
if (CreateLocalIO) {
|
|
(*Handle)->LocalIO = AddLocalIO( HandleName );
|
|
}
|
|
|
|
// Set other params
|
|
(*Handle)->Auto = AutoManage;
|
|
|
|
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
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Removed", 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->FileName)
|
|
free( Handle->FileName );
|
|
if (Handle->Address)
|
|
free( Handle->Address );
|
|
|
|
// 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::SetPortHandle( THandle * Handle, const char * FileName )
|
|
{
|
|
// Validate
|
|
if (!Handle || (Handle->Type != ctNone) || !FileName) {
|
|
return false;
|
|
}
|
|
|
|
// Set Type
|
|
Handle->Type = ctPort;
|
|
|
|
// Clear File Name
|
|
if (Handle->FileName) {
|
|
free( Handle->FileName );
|
|
}
|
|
|
|
// Set name
|
|
Handle->FileName = (char*)malloc( strlen(FileName)+1 );
|
|
strcpy( Handle->FileName, FileName );
|
|
|
|
// Log event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Set as Port [%s]", Name, Handle->Name, FileName );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::SetSocketHandle( THandle * Handle, EConnectType Type, const char * Address, const int PortNo, bool KeepAlive )
|
|
{
|
|
// Validate
|
|
if (!Handle || (Handle->Type != ctNone) || !Address || (Type == ctNone) || (Type == ctPort)) {
|
|
return false;
|
|
}
|
|
|
|
// Set Type
|
|
Handle->Type = Type;
|
|
Handle->KeepAlive = KeepAlive;
|
|
|
|
// Clear Address
|
|
if (Handle->Address) {
|
|
free( Handle->Address );
|
|
}
|
|
|
|
// Set Address & Port
|
|
Handle->Address = (char*)malloc( strlen(Address)+1 );
|
|
strcpy( Handle->Address, Address );
|
|
Handle->PortNo = PortNo;
|
|
|
|
// Log event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Set as %s [%s:%d]", Name, Handle->Name, ConnectTypeName[Type], Address, PortNo );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::ClearHandle( THandle * Handle )
|
|
{
|
|
// Validate
|
|
if (!Handle) {
|
|
return false;
|
|
}
|
|
|
|
// Reset Type related parameters
|
|
if (Handle->FileName) {
|
|
free( Handle->FileName );
|
|
Handle->FileName = NULL;
|
|
}
|
|
if (Handle->Address) {
|
|
free( Handle->Address );
|
|
Handle->Address = NULL;
|
|
}
|
|
Handle->PortNo = 0;
|
|
|
|
// Reset Parameters
|
|
Handle->Type = ctNone;
|
|
|
|
// Log event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Set as None", Name, Handle->Name );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::SetBuffers( THandle * Handle, int InBufSize, int OutBufSize, 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 = new CBuffer( InBufSize );
|
|
}
|
|
|
|
// Output Buffer
|
|
if (Handle->OutBuffer) {
|
|
delete Handle->OutBuffer;
|
|
Handle->OutBuffer = NULL;
|
|
}
|
|
if (OutBufSize) {
|
|
Handle->OutBuffer = new CBuffer( OutBufSize );
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::OpenPort( THandle * Handle )
|
|
{
|
|
// Validate
|
|
if (!Handle || (Handle->Type == ctNone)) {
|
|
return -1;
|
|
} else if (Handle->State == csOpen) {
|
|
return Handle->FD;
|
|
}
|
|
|
|
// Check if port exits
|
|
if (access( Handle->FileName, F_OK ) != 0)
|
|
{
|
|
// Log event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port not found [%s]", Name, Handle->Name, Handle->FileName );
|
|
return -1;
|
|
}
|
|
|
|
// Open Port
|
|
Handle->FD = open( Handle->FileName, O_RDWR );
|
|
if (Handle->FD == -1)
|
|
{
|
|
// Log event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Could not open Port [%s]", Name, Handle->Name, Handle->FileName );
|
|
return -1;
|
|
}
|
|
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port opened [%s]", Name, Handle->Name, Handle->FileName );
|
|
|
|
// Add to Select Lists
|
|
if (Select) {
|
|
Select->Add( Handle->FD, true, false, this );
|
|
}
|
|
|
|
// Set state
|
|
Handle->State = csOpen;
|
|
return Handle->FD;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::OpenServerSocket( THandle * Handle )
|
|
{
|
|
socklen_t addr_len;
|
|
struct sockaddr_in address;
|
|
|
|
// Socket options
|
|
struct linger ServerLinger_opt;
|
|
ServerLinger_opt.l_onoff = 1;
|
|
ServerLinger_opt.l_linger = 5;
|
|
|
|
int Reuse_opt = 1;
|
|
int KeepAlive_opt = 1;
|
|
int TCPidle_opt = 60;
|
|
int TCPint_opt = 15;
|
|
int TCPcnt_opt = 3;
|
|
|
|
// Validate Handle
|
|
if (Handle->Type != ctServer) {
|
|
return false;
|
|
}
|
|
|
|
// Create address
|
|
address.sin_family = AF_INET;
|
|
address.sin_addr.s_addr = inet_addr(Handle->Address);
|
|
address.sin_port = htons(Handle->PortNo);
|
|
addr_len = sizeof(address);
|
|
|
|
// Create socket
|
|
if ((Handle->FD = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Failed to create Server socket [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) );
|
|
|
|
// Set state
|
|
Handle->State = csFailed;
|
|
return -1;
|
|
};
|
|
|
|
// Configure connection
|
|
if ((setsockopt( Handle->FD, SOL_SOCKET, SO_LINGER, &ServerLinger_opt, sizeof(ServerLinger_opt)) == -1) ||
|
|
(setsockopt( Handle->FD, SOL_SOCKET, SO_REUSEADDR, &Reuse_opt, sizeof(Reuse_opt)) == -1))
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Could not set socket options [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) );
|
|
|
|
// Set state
|
|
Handle->State = csFailed;
|
|
return -1;
|
|
}
|
|
|
|
// Configure TCP keep alive settings
|
|
if (Handle->KeepAlive &&
|
|
((setsockopt( Handle->FD, SOL_SOCKET, SO_KEEPALIVE, &KeepAlive_opt, sizeof(KeepAlive_opt)) == -1) ||
|
|
(setsockopt( Handle->FD, SOL_TCP, TCP_KEEPIDLE, &TCPidle_opt, sizeof(TCPidle_opt)) == -1) ||
|
|
(setsockopt( Handle->FD, SOL_TCP, TCP_KEEPCNT, &TCPcnt_opt, sizeof(TCPcnt_opt)) == -1) ||
|
|
(setsockopt( Handle->FD, SOL_TCP, TCP_KEEPINTVL, &TCPint_opt, sizeof(TCPint_opt)) == -1) ))
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Could not set KeepAlive options [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) );
|
|
|
|
// Set state
|
|
Handle->State = csFailed;
|
|
return -1;
|
|
}
|
|
|
|
// Set non-blocking flag
|
|
int flags = fcntl( Handle->FD, F_GETFL, 0 );
|
|
fcntl( Handle->FD, F_SETFL, flags | O_NONBLOCK );
|
|
|
|
// Bind socket
|
|
if (bind( Handle->FD, (struct sockaddr *)&address, addr_len ) < 0)
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Failed to bind Server socket [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) );
|
|
|
|
// Set state
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
Handle->State = csFailed;
|
|
return -1;
|
|
};
|
|
|
|
// Create que for 5 connections
|
|
if (listen( Handle->FD, 5 ) < 0)
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Failed to listen on Server socket [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) );
|
|
|
|
// Set state
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
Handle->State = csFailed;
|
|
return -1;
|
|
};
|
|
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server binded and listening [%s:%d]", Name, Handle->Name, Handle->Address, Handle->PortNo );
|
|
|
|
// Add to Select Lists
|
|
if (Select) {
|
|
Select->Add( Handle->FD, true, false, this );
|
|
}
|
|
|
|
// Set state
|
|
Handle->State = csOpen;
|
|
return Handle->FD;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::OpenRemoteClientSocket( THandle * Handle )
|
|
{
|
|
THandle ** RemoteClient;
|
|
int ClientFD;
|
|
char ClientAddress[100];
|
|
char ClientName[100];
|
|
|
|
socklen_t addr_len;
|
|
struct sockaddr_in address;
|
|
|
|
// Validate
|
|
if (!Handle) {
|
|
return -1;
|
|
}
|
|
|
|
// Check Handle type
|
|
if (Handle->Type == ctServer)
|
|
{
|
|
// Accept connection on current socket
|
|
addr_len = sizeof( address );
|
|
if ((ClientFD = accept( Handle->FD, (struct sockaddr *)&address, &addr_len)) == -1)
|
|
{
|
|
// Log Event
|
|
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server failed to accept blocking connection (%s)", Name, Handle->Name, strerror(errno) );
|
|
else
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server failed to accept connection (%s)", Name, Handle->Name, strerror(errno) );
|
|
return -1;
|
|
}
|
|
|
|
// Set non-blocking flag
|
|
int flags = fcntl( ClientFD, F_GETFL, 0 );
|
|
fcntl( ClientFD, F_SETFL, flags | O_NONBLOCK );
|
|
|
|
// Return client address
|
|
strcpy( ClientAddress, inet_ntoa(address.sin_addr) );
|
|
|
|
// Get end of client list
|
|
RemoteClient = &FirstHandle;
|
|
while (*RemoteClient) {
|
|
RemoteClient = &((*RemoteClient)->Next);
|
|
}
|
|
|
|
// Create Remote Client Handle
|
|
sprintf( ClientName, "%s-%d", Handle->Name, ClientFD );
|
|
*RemoteClient = CreateHandle( ClientName, false, false );
|
|
SetSocketHandle( *RemoteClient, ctRemoteClient, ClientAddress, 0, Handle->KeepAlive );
|
|
|
|
// Copy Parent Buffer setup
|
|
SetBuffers( *RemoteClient, ((Handle->InBuffer)? Handle->InBuffer->Size() : 0),
|
|
((Handle->OutBuffer)? Handle->OutBuffer->Size() : 0),
|
|
Handle->InTimeout, Handle->InMarker, Handle->InMarkerLen );
|
|
|
|
// Set Key parameters
|
|
(*RemoteClient)->FD = ClientFD;
|
|
(*RemoteClient)->Parent = Handle;
|
|
(*RemoteClient)->State = csWaitingtoOpen;
|
|
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server accepted Remote Client connection [%s]", Name, Handle->Name, ClientAddress );
|
|
|
|
// Add to Select Lists
|
|
if (Select) {
|
|
Select->Add( (*RemoteClient)->FD, true, true, this );
|
|
}
|
|
|
|
return (*RemoteClient)->FD;
|
|
}
|
|
else if (Handle->Type == ctRemoteClient)
|
|
{
|
|
// Check state
|
|
if (Handle->State == csOpen)
|
|
{
|
|
// Already open
|
|
return Handle->FD;
|
|
}
|
|
else if (Handle->State == csWaitingtoOpen)
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Remote Client connection open [%s]", Name, Handle->Name, Handle->Address );
|
|
|
|
// Update state
|
|
Handle->State = csOpen;
|
|
return Handle->FD;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::OpenClientSocket( THandle * Handle )
|
|
{
|
|
socklen_t addr_len;
|
|
struct sockaddr_in address;
|
|
|
|
// Socket options
|
|
int KeepAlive_opt = 1;
|
|
int TCPidle_opt = 5;
|
|
int TCPcnt_opt = 3;
|
|
int TCPint_opt = 2;
|
|
|
|
// Check state
|
|
if (Handle->State == csOpen) {
|
|
// Already open
|
|
return Handle->FD;
|
|
}
|
|
|
|
if (Handle->State != csWaitingtoOpen)
|
|
{
|
|
// Create File descriptor
|
|
if ((Handle->FD = socket( AF_INET, SOCK_STREAM, 0 )) < 0)
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Failed to create Client socket [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) );
|
|
|
|
// Set Status
|
|
Handle->State = csFailed;
|
|
return -1;
|
|
};
|
|
|
|
// Set Non blocking open
|
|
int flags = fcntl( Handle->FD, F_GETFL, 0 );
|
|
fcntl( Handle->FD, F_SETFL, O_NONBLOCK|flags );
|
|
|
|
// Configure TCP keep alive settings
|
|
if (Handle->KeepAlive &&
|
|
((setsockopt( Handle->FD, SOL_SOCKET, SO_KEEPALIVE, &KeepAlive_opt, sizeof(KeepAlive_opt)) == -1) ||
|
|
(setsockopt( Handle->FD, SOL_TCP, TCP_KEEPIDLE, &TCPidle_opt, sizeof(TCPidle_opt)) == -1) ||
|
|
(setsockopt( Handle->FD, SOL_TCP, TCP_KEEPCNT, &TCPcnt_opt, sizeof(TCPcnt_opt)) == -1) ||
|
|
(setsockopt( Handle->FD, SOL_TCP, TCP_KEEPINTVL, &TCPint_opt, sizeof(TCPint_opt)) == -1) ))
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle %s - Could not set KeepAlive options [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) );
|
|
|
|
// Set State
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
Handle->State = csFailed;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Declare address
|
|
address.sin_family = AF_INET;
|
|
address.sin_addr.s_addr = inet_addr( Handle->Address );
|
|
address.sin_port = htons( Handle->PortNo );
|
|
addr_len = sizeof(address);
|
|
|
|
if (!connect( Handle->FD, (struct sockaddr *)&address, addr_len ))
|
|
{
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle %s - Client connected [%s:%d]", Name, Handle->Name, Handle->Address, Handle->PortNo );
|
|
|
|
// Add to Select Lists
|
|
if (Select) {
|
|
Select->Add( Handle->FD, true, true, this );
|
|
}
|
|
|
|
// Set status
|
|
Handle->State = csOpen;
|
|
return Handle->FD;
|
|
}
|
|
else if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINPROGRESS) || (errno == EALREADY))
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Client waiting to connect [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) );
|
|
|
|
// Add to Select Lists
|
|
if (Select) {
|
|
Select->Add( Handle->FD, true, true, this );
|
|
}
|
|
|
|
// Set status
|
|
Handle->State = csWaitingtoOpen;
|
|
return Handle->FD;
|
|
}
|
|
else
|
|
{
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Client could not connect [%s:%d] (%s)", Name, Handle->Name, Handle->Address, Handle->PortNo, strerror(errno) );
|
|
|
|
// Remove from Select List
|
|
if (Select) {
|
|
Select->Remove( Handle->FD, true, true );
|
|
}
|
|
|
|
// Close socket
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
Handle->State = csFailed;
|
|
return -1;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::Open( THandle * Handle )
|
|
{
|
|
int FD = -1;
|
|
|
|
// Validate
|
|
if (!Handle) {
|
|
return -1;
|
|
}
|
|
|
|
// Open correctly
|
|
switch (Handle->Type) {
|
|
case ctPort :
|
|
FD = OpenPort( Handle );
|
|
break;
|
|
case ctServer :
|
|
FD = OpenServerSocket( Handle );
|
|
break;
|
|
case ctClient :
|
|
FD = OpenClientSocket( Handle );
|
|
break;
|
|
case ctRemoteClient :
|
|
FD = OpenRemoteClientSocket( Handle );
|
|
break;
|
|
default:
|
|
FD = -1;
|
|
}
|
|
return FD;
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Delete socket
|
|
bool CSelectableCore::Close( THandle * Handle, bool CloseChildren )
|
|
{
|
|
bool Fail;
|
|
THandle * ChildHandle = NULL;
|
|
THandle * NextHandle = NULL;
|
|
|
|
// Validate
|
|
if (!Handle || (Handle->FD == -1))
|
|
return false;
|
|
|
|
// Close Children
|
|
if (CloseChildren && (Handle->Type == ctServer)) {
|
|
ChildHandle = FirstHandle;
|
|
while (ChildHandle) {
|
|
if (ChildHandle->Parent == Handle) {
|
|
NextHandle = ChildHandle->Next;
|
|
|
|
// Close
|
|
Close( ChildHandle );
|
|
|
|
// Next Handle
|
|
ChildHandle = NextHandle;
|
|
} else {
|
|
// Next Handle
|
|
ChildHandle = ChildHandle->Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Close Handle
|
|
Fail = (close( Handle->FD ))? true : false;
|
|
Handle->State = ((Fail)? csFailed : csClosed);
|
|
|
|
// Show action
|
|
switch (Handle->Type)
|
|
{
|
|
case ctPort:
|
|
// Log Event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port %s [%s]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->FileName );
|
|
break;
|
|
|
|
case ctServer:
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Server %s [%s:%d]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Address, Handle->PortNo );
|
|
break;
|
|
|
|
case ctRemoteClient:
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Remote Client connection %s [%s]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Address );
|
|
break;
|
|
|
|
case ctClient:
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Client connection %s [%s:%d]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Address, Handle->PortNo );
|
|
break;
|
|
|
|
case ctNone:
|
|
default:
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - %s, invalid Handle type", Name, Handle->Name, ((Fail)? "failed" : "closed") );
|
|
break;
|
|
};
|
|
|
|
// Remove from Select List
|
|
if (!Fail && Select) {
|
|
Select->Remove( Handle->FD, true, true );
|
|
}
|
|
|
|
// Reset FD
|
|
Handle->FD = ((Fail)? Handle->FD : -1);
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Device Interface
|
|
bool CSelectableCore::Read( THandle * Handle )
|
|
{
|
|
int ClientFD = -1;
|
|
int BytesRead = 0;
|
|
int BytesWaiting = -1;
|
|
|
|
// Validate
|
|
if (!Handle) {
|
|
return false;
|
|
}
|
|
|
|
// Log Read Event
|
|
LogMessage( DebugLevel, dlHigh, "%s: Handle '%s' - Read Event", Name, Handle->Name );
|
|
|
|
// Check for closing/opening event on Socket
|
|
if (Handle->Type == ctServer)
|
|
{
|
|
// Incoming client request
|
|
ClientFD = OpenRemoteClientSocket( Handle );
|
|
if (ClientFD != -1) {
|
|
// Add to Select Lists
|
|
if (Select) {
|
|
Select->Add( ClientFD, true, true, this );
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else if ((Handle->Type == ctRemoteClient) || (Handle->Type == ctClient))
|
|
{
|
|
// Check if socket ready (non-block open in progress)
|
|
if (Handle->State == csWaitingtoOpen)
|
|
{
|
|
if (Handle->Type == ctRemoteClient) {
|
|
OpenRemoteClientSocket( Handle );
|
|
} else if (Handle->Type == ctClient) {
|
|
OpenClientSocket( Handle );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Check if anything to read
|
|
ioctl( Handle->FD, FIONREAD, &BytesWaiting );
|
|
|
|
// EOF from server
|
|
if (!BytesWaiting)
|
|
{
|
|
// Close Handle
|
|
Close( Handle );
|
|
|
|
// Destroy Remote Client
|
|
if (Handle->Type == ctRemoteClient) {
|
|
RemoveHandle( Handle );
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Validate
|
|
if (Handle->State != csOpen) {
|
|
return false;
|
|
}
|
|
|
|
// Read File directly into buffer
|
|
if (!Handle->InBuffer || !(BytesRead = Handle->InBuffer->ReadFromFD( Handle->FD ))) {
|
|
return false;
|
|
}
|
|
|
|
// Process Buffer
|
|
ProcessBuffer( Handle, false );
|
|
|
|
// Reset timer
|
|
SetStartTime( &(Handle->InStart) );
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::Write( THandle * Handle )
|
|
{
|
|
int Len = 0;
|
|
char * Data = NULL;
|
|
int BytesWritten = 0;
|
|
|
|
// Validate
|
|
if (!Handle) {
|
|
return false;
|
|
}
|
|
|
|
// Log Ready for Write Event
|
|
LogMessage( DebugLevel, dlHigh, "%s: Handle '%s' - Write Event", Name, Handle->Name );
|
|
|
|
if (Handle->State == csWaitingtoOpen)
|
|
{
|
|
// Complete socket open process
|
|
if (Handle->Type == ctRemoteClient) {
|
|
OpenRemoteClientSocket( Handle );
|
|
} else if (Handle->Type == ctClient) {
|
|
OpenClientSocket( Handle );
|
|
}
|
|
|
|
// Remove from set for select write
|
|
if (Select) {
|
|
Select->Remove( Handle->FD, false, true );
|
|
}
|
|
return true;
|
|
}
|
|
else if (Handle->State == csOpen)
|
|
{
|
|
if (Handle->OutBuffer)
|
|
{
|
|
// Write to FD directly from buffer
|
|
if ((BytesWritten = Handle->OutBuffer->WriteToFD( Handle->FD )))
|
|
{
|
|
if (DebugLevel >= dlHigh) {
|
|
// Show event
|
|
Len = Handle->OutBuffer->Peek( &Data );
|
|
ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - OUT:", Name, Handle->Name );
|
|
}
|
|
|
|
// Update Buffer
|
|
Handle->OutBuffer->Clear( BytesWritten );
|
|
}
|
|
|
|
// Check if Buffer emtpy
|
|
if (!Handle->OutBuffer->Len()) {
|
|
// Add to Select Write list
|
|
if (Select) {
|
|
Select->Remove( Handle->FD, false, true );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No Output buffer, so remove from Write list
|
|
if (Select) {
|
|
Select->Remove( Handle->FD, false, true );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::ProcessBuffer( THandle * Handle, bool Force )
|
|
{
|
|
int Pos = 0;
|
|
int Len = 0;
|
|
char * Data = NULL;
|
|
TLocalIO * LocalIO = NULL;
|
|
TLinkedIO * Output = NULL;
|
|
|
|
// Check if buffered data
|
|
if (!Handle || !Handle->InBuffer || !Handle->InBuffer->Len()) {
|
|
return false;
|
|
}
|
|
|
|
// Check if forced processed
|
|
if (Force)
|
|
{
|
|
// Show Packet
|
|
Len = Handle->InBuffer->Peek( &Data );
|
|
ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - IN-F:", Name, Handle->Name );
|
|
|
|
// Write buffer to Outputs
|
|
if (Handle->Type == ctRemoteClient) {
|
|
LocalIO = Handle->Parent->LocalIO;
|
|
} else {
|
|
LocalIO = Handle->LocalIO;
|
|
}
|
|
if (LocalIO) {
|
|
Output = LocalIO->FirstOutput;
|
|
while (Output) {
|
|
Output->Function->Input( Output->IOName, Data, Len );
|
|
Output = Output->Next;
|
|
}
|
|
}
|
|
|
|
// 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+1 );
|
|
ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - IN-M:", Name, Handle->Name );
|
|
|
|
// Write buffer to Outputs
|
|
if (Handle->Type == ctRemoteClient) {
|
|
LocalIO = Handle->Parent->LocalIO;
|
|
} else {
|
|
LocalIO = Handle->LocalIO;
|
|
}
|
|
if (LocalIO) {
|
|
Output = LocalIO->FirstOutput;
|
|
while (Output) {
|
|
Output->Function->Input( Output->IOName, Data, Len );
|
|
Output = Output->Next;
|
|
}
|
|
}
|
|
|
|
// 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 = 0;
|
|
|
|
// Check if buffer created
|
|
if ((FD == -1) || !Data) {
|
|
return 0;
|
|
}
|
|
|
|
// Read Data into buffer
|
|
DataRemain = (MaxLen == -1)? strlen(Data) : MaxLen;
|
|
while (DataRemain)
|
|
{
|
|
// Read from file descriptor
|
|
BytesRead = read( FD, Data, DataRemain );
|
|
if (BytesRead <= 0)
|
|
break;
|
|
|
|
// Update Data Pointers
|
|
TotalRead += BytesRead;
|
|
DataRemain -= BytesRead;
|
|
}
|
|
|
|
return TotalRead;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::WriteToFD( int FD, const char * Data, int Len )
|
|
{
|
|
int BytesWritten = 0;
|
|
int TotalWritten = 0;
|
|
int DataRemain = 0;
|
|
|
|
// Check if buffer created
|
|
if ((FD == -1) || !Data) {
|
|
return 0;
|
|
}
|
|
|
|
// Read Data into buffer
|
|
DataRemain = (Len == -1)? strlen(Data) : Len;
|
|
while (DataRemain)
|
|
{
|
|
// Read from file descriptor
|
|
BytesWritten = write( FD, Data, DataRemain );
|
|
//write( FD, "\n", 1 );
|
|
if (BytesWritten <= 0)
|
|
break;
|
|
|
|
// Update Data Pointers
|
|
TotalWritten += BytesWritten;
|
|
DataRemain -= BytesWritten;
|
|
}
|
|
|
|
return TotalWritten;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::Input( const char * IOName, const char * Data, int Len )
|
|
{
|
|
THandle * Handle = NULL;
|
|
THandle * ChildHandle = NULL;
|
|
int BytesWritten = 0;
|
|
|
|
// Validate
|
|
if (!IOName || !Data) {
|
|
return 0;
|
|
}
|
|
else if (Len == -1) {
|
|
Len = strlen( Data );
|
|
}
|
|
|
|
// Get File handle
|
|
if (!(Handle = GetHandle( IOName )))
|
|
{
|
|
// Log event
|
|
LogMessage( DebugLevel, dlHigh, "%s: Local IO '%s' - Input rejected, Input not found", Name, IOName );
|
|
return 0;
|
|
}
|
|
|
|
// Log event
|
|
ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: LocalIO '%s' - IN:", Name, IOName );
|
|
|
|
if (Handle->Type == ctServer)
|
|
{
|
|
// Cannot write to server socket, so Update Remote Client connections individually
|
|
ChildHandle = FirstHandle;
|
|
while (ChildHandle)
|
|
{
|
|
// Check if child
|
|
if (ChildHandle->Parent == Handle)
|
|
{
|
|
// Decide where to put data
|
|
if (ChildHandle->OutBuffer)
|
|
{
|
|
// Write to buffer
|
|
BytesWritten = ChildHandle->OutBuffer->Push( Data, Len );
|
|
|
|
// Add to select write list
|
|
if (BytesWritten) {
|
|
Select->Add( ChildHandle->FD, false, true, this );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Show event
|
|
ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - OUT:", Name, ChildHandle->Name );
|
|
|
|
// Write directly to handle
|
|
BytesWritten = WriteToFD( ChildHandle->FD, Data, Len );
|
|
}
|
|
}
|
|
// Next
|
|
ChildHandle = ChildHandle->Next;
|
|
}
|
|
|
|
// Cannot verify individually, so assume all bytes was written
|
|
BytesWritten = Len;
|
|
}
|
|
else
|
|
{
|
|
// Decide where to put data
|
|
if (Handle->OutBuffer)
|
|
{
|
|
// Write to buffer
|
|
BytesWritten = Handle->OutBuffer->Push( Data, Len );
|
|
|
|
// Add to select write list
|
|
if (BytesWritten) {
|
|
Select->Add( Handle->FD, false, true, this );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Show event
|
|
ShowOutput( DebugLevel, dlHigh, OutputDisplay, Data, Len, "%s: Handle '%s' - OUT:", Name, Handle->Name );
|
|
|
|
// Write directly to handle
|
|
BytesWritten = WriteToFD( Handle->FD, Data, Len );
|
|
}
|
|
}
|
|
|
|
return BytesWritten;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::Process()
|
|
{
|
|
THandle * Handle = NULL;
|
|
|
|
// Check all handles
|
|
Handle = FirstHandle;
|
|
while (Handle)
|
|
{
|
|
// Auto manage handles
|
|
if (Handle->Auto && ((Handle->State == csNone) || (Handle->State == csFailed) || (Handle->State == csClosed)))
|
|
{
|
|
// Reopen handle
|
|
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
|
|
ProcessBuffer( Handle, true );
|
|
|
|
// Reset timer
|
|
SetInterval( &(Handle->InStart), 0 );
|
|
}
|
|
}
|
|
Handle = Handle->Next;
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Set serial port configuration parameters
|
|
bool CSelectableCore::SerialConfig( THandle * Handle, int Baud, short DataBits, short StopBits, short Parity, short FlowCtrl, int Wait )
|
|
{
|
|
struct termios newtio;
|
|
int flags = 0;
|
|
speed_t _baud = 0;
|
|
int mcs = 0;
|
|
|
|
// Get Handle
|
|
if (!Handle || (Handle->Type != ctPort)) {
|
|
return false;
|
|
}
|
|
|
|
// Flush Data from port
|
|
tcflush( Handle->FD, TCIFLUSH );
|
|
|
|
// Set Open flags
|
|
flags = fcntl( Handle->FD, F_GETFL, 0 );
|
|
fcntl( Handle->FD, F_SETFL, flags & ~O_NDELAY );
|
|
|
|
// Read options
|
|
if (tcgetattr( Handle->FD, &newtio ) != 0)
|
|
return false;
|
|
|
|
// Process the baud rate
|
|
switch (Baud)
|
|
{
|
|
case 921600: _baud = B921600; break;
|
|
case 576000: _baud = B576000; break;
|
|
case 460800: _baud = B460800; break;
|
|
case 230400: _baud = B230400; break;
|
|
//case 128000: _baud = B128000; break;
|
|
case 115200: _baud = B115200; break;
|
|
//case 76800: _baud = B76800; break;
|
|
case 57600: _baud = B57600; break;
|
|
case 38400: _baud = B38400; break;
|
|
//case 28800: _baud = B28800; break;
|
|
case 19200: _baud = B19200; break;
|
|
//case 14400: _baud = B14400; break;
|
|
case 9600: _baud = B9600; break;
|
|
case 4800: _baud = B4800; break;
|
|
case 2400: _baud = B2400; break;
|
|
case 1800: _baud = B1800; break;
|
|
case 1200: _baud = B1200; break;
|
|
case 600: _baud = B600; break;
|
|
case 300: _baud = B300; break;
|
|
case 200: _baud = B200; break;
|
|
case 150: _baud = B150; break;
|
|
case 134: _baud = B134; break;
|
|
case 110: _baud = B110; break;
|
|
case 75: _baud = B75; break;
|
|
case 50: _baud = B50; break;
|
|
|
|
default: _baud = B9600; break;
|
|
}
|
|
|
|
// Set Baud rate
|
|
cfsetospeed( &newtio, (speed_t)_baud );
|
|
cfsetispeed( &newtio, (speed_t)_baud );
|
|
|
|
// Generate Mark/Space parity
|
|
if ((DataBits == 7) && ((Parity == MARK_PARITY) || (Parity == SPACE_PARITY)))
|
|
DataBits = 8;
|
|
|
|
// Process the data bits
|
|
newtio.c_cflag &= ~CSIZE;
|
|
switch (DataBits)
|
|
{
|
|
case 5: newtio.c_cflag |= CS5; break;
|
|
case 6: newtio.c_cflag |= CS6; break;
|
|
case 7: newtio.c_cflag |= CS7; break;
|
|
case 8: newtio.c_cflag |= CS8; break;
|
|
|
|
default: newtio.c_cflag |= CS8; break;
|
|
}
|
|
|
|
// Set other flags
|
|
newtio.c_cflag |= CLOCAL | CREAD;
|
|
|
|
// Process Parity
|
|
newtio.c_cflag &= ~(PARENB | PARODD);
|
|
if (Parity == EVEN_PARITY)
|
|
newtio.c_cflag |= PARENB;
|
|
else if (Parity == ODD_PARITY)
|
|
newtio.c_cflag |= (PARENB | PARODD);
|
|
|
|
// Flow Control (for now)
|
|
newtio.c_cflag &= ~CRTSCTS;
|
|
|
|
// Process Stop Bits
|
|
if (StopBits == 2)
|
|
newtio.c_cflag |= CSTOPB;
|
|
else
|
|
newtio.c_cflag &= ~CSTOPB;
|
|
|
|
newtio.c_iflag = IGNBRK;
|
|
|
|
// Software Flow Control
|
|
if (FlowCtrl == SW_FLOWCTRL)
|
|
newtio.c_iflag |= IXON | IXOFF;
|
|
else
|
|
newtio.c_iflag &= ~(IXON | IXOFF | IXANY);
|
|
|
|
// Set RAW input & output
|
|
newtio.c_lflag=0;
|
|
newtio.c_oflag=0;
|
|
|
|
// Set wait parameters
|
|
newtio.c_cc[VTIME] = Wait; // Blocking: Allow at least a tenth of a second to wait for data
|
|
newtio.c_cc[VMIN] = 0; // Non-blocking: Don't set min bytes to receive
|
|
|
|
// Set Options (first time)
|
|
//tcflush( Handle->FD, TCIFLUSH);
|
|
if (tcsetattr( Handle->FD, TCSANOW, &newtio)!=0)
|
|
return false;
|
|
|
|
// Set Terminal options
|
|
ioctl( Handle->FD, TIOCMGET, &mcs);
|
|
mcs |= TIOCM_RTS;
|
|
ioctl( Handle->FD, TIOCMSET, &mcs);
|
|
|
|
// Get Options (again)
|
|
if (tcgetattr( Handle->FD, &newtio) != 0)
|
|
return false;
|
|
|
|
// Process Hardware Flow Control
|
|
if (FlowCtrl == HW_FLOWCTRL)
|
|
newtio.c_cflag |= CRTSCTS;
|
|
else
|
|
newtio.c_cflag &= ~CRTSCTS;
|
|
|
|
// Set Options (second time)
|
|
if (tcsetattr( Handle->FD, TCSANOW, &newtio ) != 0)
|
|
{
|
|
// Log event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port not configured", Name, Handle->Name );
|
|
return false;
|
|
}
|
|
|
|
// Port configured
|
|
// Log event
|
|
LogMessage( DebugLevel, dlMedium, "%s: Handle '%s' - Port configured", Name, Handle->Name );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|