- Rename function ApplicationCore::GetProcessName -> GetFileName - Correct misspelled variable "Persistent" in CSelectableCore::Init - Clear compiler warning: Persistent not initialised
2562 lines
95 KiB
C++
2562 lines
95 KiB
C++
/*
|
|
* SelectableCore.cpp
|
|
*
|
|
* Created on: 20 May 2016
|
|
* 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"
|
|
#include "UtilCore.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Global Vars
|
|
extern char * ProcessName;
|
|
extern CApplication * Application;
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Function Constructor
|
|
CFunctionCore * NewSelectableCore( const char * Name ) {
|
|
return (CFunctionCore*) new CSelectableCore( Name );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Resolve action handler
|
|
void ResolveHandler( int Signal, siginfo_t * SignalInfo, void * Context )
|
|
{
|
|
TResolveReq * ResolveReq;
|
|
THandle * Handle;
|
|
|
|
// Validate signal
|
|
if ((SignalInfo->si_code != SI_ASYNCNL) ||
|
|
(SignalInfo->si_signo != SIGRTMIN))
|
|
return;
|
|
|
|
// Get Handle & Request
|
|
ResolveReq = (TResolveReq*)(SignalInfo->si_value.sival_ptr);
|
|
Handle = ResolveReq->Handle;
|
|
|
|
((CSelectableCore*)Handle->Function)->HandleResolve( Handle );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CSelectableCore::CSelectableCore( const char * pName, const char * pType ) : CSelectableBare( pName, pType )
|
|
{
|
|
// Configure resolve signal handler
|
|
ResolveAct.sa_sigaction = &ResolveHandler;
|
|
sigemptyset( &ResolveAct.sa_mask );
|
|
ResolveAct.sa_flags = SA_SIGINFO;
|
|
|
|
sigaction( SIGRTMIN, &ResolveAct, NULL );
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
CSelectableCore::~CSelectableCore()
|
|
{
|
|
THandle * NextHandle = NULL;
|
|
|
|
// Destroy File Handles
|
|
while (FirstHandle) {
|
|
// Cancel pending resolve request
|
|
CancelResolve( FirstHandle );
|
|
|
|
// Close handle if open
|
|
if ((FirstHandle->State == csOpen) || (FirstHandle->State == csWaitingtoOpen))
|
|
Close( FirstHandle, false );
|
|
|
|
// Destroy handle
|
|
NextHandle = FirstHandle->Next;
|
|
DestroyHandle( FirstHandle );
|
|
FirstHandle = NextHandle;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
|
{
|
|
CDataMember * HandleConfig;
|
|
CDataMember * AddressDef;
|
|
CDataMember * SerialConfig;
|
|
THandle * Handle;
|
|
char * Type;
|
|
char * PortName;
|
|
char * Address;
|
|
char * Port;
|
|
char * ParityText;
|
|
char * FlowCtrlText;
|
|
short Parity;
|
|
short FlowCtrl;
|
|
short Queue;
|
|
bool Persistent = false;
|
|
|
|
// Call Previous load config
|
|
if (!CFunctionCore::Init( FunctionConfig ))
|
|
return false;
|
|
|
|
// Load Handles
|
|
HandleConfig = FunctionConfig->GetChFirstChild( "Handles", true );
|
|
while (HandleConfig) {
|
|
// Create Handle and channel link
|
|
Handle = CreateHandle( HandleConfig->GetName(), false );
|
|
Handle->Channel = GetChannel( HandleConfig->GetChStr( "Channel" ) );
|
|
|
|
// Load handle specifics
|
|
Type = (char*)HandleConfig->GetChStr( "Type", "TCPclient", true );
|
|
if (!strcasecmp( Type, "Serial" )) {
|
|
// Get port address
|
|
if ((PortName = (char*)HandleConfig->GetChStr( "Port/Name", NULL )) && (AddressDef = Application->AddressList->GetChild( PortName )))
|
|
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
|
else
|
|
Address = (char*)HandleConfig->GetChStr( "Port/Address", NULL, true ); // Get default value
|
|
SetSerialHandle( Handle, Address );
|
|
|
|
// Update configuration if specified
|
|
if ((SerialConfig = HandleConfig->GetChild( "Port/SerialConfig", false ))) {
|
|
|
|
ParityText = (char*)SerialConfig->GetChStr( "Parity", "none", true );
|
|
if (!strcasecmp( ParityText, "none" ))
|
|
Parity = NO_PARITY;
|
|
else if (!strcasecmp( ParityText, "odd" ))
|
|
Parity = ODD_PARITY;
|
|
else if (!strcasecmp( ParityText, "even" ))
|
|
Parity = EVEN_PARITY;
|
|
else if (!strcasecmp( ParityText, "mark" ))
|
|
Parity = MARK_PARITY;
|
|
else
|
|
Parity = NO_PARITY;
|
|
|
|
FlowCtrlText = (char*)SerialConfig->GetChStr( "FlowCtrl", "none", true );
|
|
if (!strcasecmp( FlowCtrlText, "none" ))
|
|
FlowCtrl = NO_FLOWCTRL;
|
|
else if (!strcasecmp( FlowCtrlText, "hardware" ))
|
|
FlowCtrl = HW_FLOWCTRL;
|
|
else if (!strcasecmp( FlowCtrlText, "software" ))
|
|
FlowCtrl = SW_FLOWCTRL;
|
|
else if (!strcasecmp( FlowCtrlText, "rs485" ))
|
|
FlowCtrl = RS485_FLOWCTRL;
|
|
else
|
|
FlowCtrl = NO_FLOWCTRL;
|
|
|
|
Handle->SerialConfig = true;
|
|
SetSerialHandleConfig( Handle, SerialConfig->GetChInt( "BaudRate", 19200, true ),
|
|
SerialConfig->GetChInt( "DataBits", 8, true ),
|
|
Parity, SerialConfig->GetChInt( "StopBits", 1, true ),
|
|
FlowCtrl, SerialConfig->GetChInt( "DataWait", 0, true ));
|
|
Persistent = true;
|
|
}
|
|
}
|
|
else if (!strcasecmp( Type, "LinePrinter" )) {
|
|
// Get port address
|
|
if ((PortName = (char*)HandleConfig->GetChStr( "Port/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName )))
|
|
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
|
else
|
|
Address = (char*)HandleConfig->GetChStr( "Port/Address", NULL, true ); // Get default value
|
|
|
|
SetLinePrinterHandle( Handle, Address );
|
|
Persistent = true;
|
|
}
|
|
else if (!strcasecmp( Type, "UNIXserver" )) {
|
|
// Get UNIX socket handle
|
|
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName )))
|
|
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
|
else
|
|
Address = (char*)HandleConfig->GetChStr( "Socket/Address", NULL, true ); // Get default Address value
|
|
|
|
Queue = HandleConfig->GetChInt( "Socket/Queue", 2, true );
|
|
SetUnixHandle( Handle, ctUNIXserver, Address, Queue );
|
|
Persistent = true;
|
|
}
|
|
else if (!strcasecmp( Type, "UNIXclient" )) {
|
|
// Get UNIX socket handle
|
|
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName )))
|
|
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
|
else
|
|
Address = (char*)HandleConfig->GetChStr( "Socket/Address", NULL, true ); // Get default Address value
|
|
|
|
SetUnixHandle( Handle, ctUNIXclient, Address, 0 );
|
|
Persistent = false;
|
|
}
|
|
else if (!strcasecmp( Type, "UDPserver" )) {
|
|
// Get UDP address & port
|
|
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
|
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
|
Port = (char*)AddressDef->GetChStr( "Port", "0", true ); // Get AddressList Port value
|
|
}
|
|
else {
|
|
Address = (char*)HandleConfig->GetChStr( "Socket/Address", NULL, true ); // Get default Address value
|
|
Port = (char*)HandleConfig->GetChStr( "Socket/Port", "0", true ); // Get default Port value
|
|
}
|
|
SetSocketHandle( Handle, ctUDPserver, Address, strlcase(Port), 0 );
|
|
Persistent = true;
|
|
}
|
|
else if (!strcasecmp( Type, "UDPclient" )) {
|
|
// Get UDP address & port
|
|
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
|
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
|
Port = (char*)AddressDef->GetChStr( "Port", "0", true ); // Get AddressList Port value
|
|
}
|
|
else {
|
|
Address = (char*)HandleConfig->GetChStr( "Socket/Address", NULL, true ); // Get default Address value
|
|
Port = (char*)HandleConfig->GetChStr( "Socket/Port", "0", true ); // Get default Port value
|
|
}
|
|
SetSocketHandle( Handle, ctUDPclient, Address, strlcase(Port), 0 );
|
|
Persistent = false;
|
|
}
|
|
else if (!strcasecmp( Type, "TCPserver" )) {
|
|
// Get TCP address & port
|
|
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
|
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
|
Port = (char*)AddressDef->GetChStr( "Port", "0", true ); // Get AddressList Port value
|
|
}
|
|
else {
|
|
Address = (char*)HandleConfig->GetChStr( "Socket/Address", NULL, true ); // Get default Address value
|
|
Port = (char*)HandleConfig->GetChStr( "Socket/Port", "0", true ); // Get default Port value
|
|
}
|
|
Queue = HandleConfig->GetChInt( "Socket/Queue", 2, true );
|
|
SetSocketHandle( Handle, ctTCPserver, Address, strlcase(Port), Queue );
|
|
Persistent = true;
|
|
}
|
|
else if (!strcasecmp( Type, "TCPclient" )) {
|
|
// Get TCP address & port
|
|
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
|
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
|
Port = (char*)AddressDef->GetChStr( "Port", "0", true ); // Get AddressList Port value
|
|
}
|
|
else {
|
|
Address = (char*)HandleConfig->GetChStr( "Socket/Address", NULL, true ); // Get default Address value
|
|
Port = (char*)HandleConfig->GetChStr( "Socket/Port", "0", true ); // Get default Port value
|
|
}
|
|
SetSocketHandle( Handle, ctTCPclient, Address, strlcase(Port), 0 );
|
|
Persistent = false;
|
|
}
|
|
else if (!strcasecmp( Type, "ForkPipe" )) {
|
|
// Get fork pipe handle
|
|
Address = (char*)HandleConfig->GetChStr( "Fork/ExecPath", NULL, true ); // Get default value
|
|
SetForkPipeHandle( Handle, Address );
|
|
Persistent = true;
|
|
}
|
|
|
|
// Set Auto Manage
|
|
SetAutoManage( Handle, HandleConfig->GetChBool( "AutoManage/Enabled", true, true ),
|
|
HandleConfig->GetChBool( "AutoManage/Persistent", Persistent, true ),
|
|
HandleConfig->GetChInt( "AutoManage/ReopenDelay", 2000, true ),
|
|
HandleConfig->GetChInt( "AutoManage/CloseTimeout", 2000, true ));
|
|
|
|
// Input buffer
|
|
SetInBuffer( Handle, HandleConfig->GetChInt( "InputBuffer/Size", 0 ),
|
|
HandleConfig->GetChInt( "InputBuffer/Timeout", 250 ),
|
|
HandleConfig->GetChStr( "InputBuffer/Marker", "" ),
|
|
HandleConfig->GetChInt( "InputBuffer/MarkerLen", 0 ) );
|
|
SetOutBuffer( Handle, HandleConfig->GetChInt( "OutputBuffer/Size", 0 ) );
|
|
|
|
// Next
|
|
HandleConfig = HandleConfig->GetNextPeer();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::DestroyHandle( THandle * Handle )
|
|
{
|
|
// Validate Handle
|
|
if (!Handle)
|
|
return false;
|
|
|
|
// Cancel pending resolve request
|
|
CancelResolve( Handle );
|
|
|
|
// 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->AddressList)
|
|
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
|
|
delete Handle;
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
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::SetSerialHandle( THandle * Handle, const char * FileName )
|
|
{
|
|
// Validate
|
|
if (!Handle || ((Handle->Type != ctNone) && (Handle->Type != ctSerial)) || !FileName)
|
|
return false;
|
|
|
|
// Set Type
|
|
Handle->Type = ctSerial;
|
|
|
|
// Set path
|
|
if (Handle->Path) free( Handle->Path );
|
|
Handle->Path = strdup( FileName );
|
|
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Set as Port [%s]",
|
|
ProcessName, Name, Handle->Name, FileName );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::SetSerialHandleConfig( THandle * Handle, int Baudrate, short DataBits, short Parity, short StopBits, short FlowCtrl, int DataWait )
|
|
{
|
|
// Validate
|
|
if (!Handle || (Handle->Type != ctSerial))
|
|
return false;
|
|
|
|
Handle->InBaudrate = Baudrate;
|
|
Handle->OutBaudrate = Baudrate;
|
|
Handle->DataBits = DataBits;
|
|
Handle->Parity = Parity;
|
|
Handle->StopBits = StopBits;
|
|
Handle->FlowCtrl = FlowCtrl;
|
|
Handle->DataWait = DataWait;
|
|
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::SetLinePrinterHandle( THandle * Handle, const char * FileName )
|
|
{
|
|
// Validate
|
|
if (!Handle || ((Handle->Type != ctNone) && (Handle->Type != ctLinePrinter)) || !FileName)
|
|
return false;
|
|
|
|
// Set Type
|
|
Handle->Type = ctLinePrinter;
|
|
|
|
// Set Path
|
|
if (Handle->Path) free( Handle->Path );
|
|
Handle->Path = strdup( FileName );
|
|
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Set as Port [%s]",
|
|
ProcessName, Name, Handle->Name, FileName );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::SetForkPipeHandle( THandle * Handle, const char * ExecPath )
|
|
{
|
|
// Validate
|
|
if (!Handle || ((Handle->Type != ctNone) && (Handle->Type != ctForkPipe)) || !ExecPath)
|
|
return false;
|
|
|
|
// Set Type
|
|
Handle->Type = ctForkPipe;
|
|
|
|
// Set path
|
|
if (Handle->Path) free( Handle->Path );
|
|
Handle->Path = strdup( ExecPath );
|
|
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Set as ForkPipe [%s]",
|
|
ProcessName, Name, Handle->Name, ExecPath );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::SetUnixHandle( THandle * Handle, EConnectType Type, const char * FileName, short Queue )
|
|
{
|
|
// Validate
|
|
if (!Handle || !FileName ||
|
|
!((Type == ctUNIXserver) || (Type == ctUNIXclient) || (Type == ctUNIXremote)) ||
|
|
!((Handle->Type == ctNone) || (Handle->Type == Type)) )
|
|
return false;
|
|
|
|
// Set Type
|
|
Handle->Type = Type;
|
|
|
|
// Clear File Name
|
|
if (Handle->Path)
|
|
free( Handle->Path );
|
|
|
|
// Set name
|
|
Handle->Path = strdup( FileName );
|
|
Handle->Queue = Queue;
|
|
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Set as %s [%s]",
|
|
ProcessName, Name, Handle->Name, ConnectTypeName[Type], FileName );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::SetSocketHandle( THandle * Handle, EConnectType Type, const char * HostName, const char * PortName, short Queue )
|
|
{
|
|
// Validate
|
|
if (!Handle || !HostName || !PortName ||
|
|
!((Type == ctUDPserver) || (Type == ctUDPclient) || (Type == ctUDPremote) || (Type == ctTCPserver) || (Type == ctTCPclient) || (Type == ctTCPremote)) ||
|
|
!((Handle->Type == ctNone) || (Handle->Type == Type)) )
|
|
return false;
|
|
|
|
// Set Type
|
|
Handle->Type = Type;
|
|
|
|
// Clear HostName & Port Name
|
|
if (Handle->HostName)
|
|
free( Handle->HostName );
|
|
if (Handle->PortName)
|
|
free( Handle->PortName );
|
|
if (Handle->AddressList)
|
|
freeaddrinfo( Handle->AddressList );
|
|
|
|
// Set HostName & Port
|
|
Handle->HostName = strdup( HostName );
|
|
Handle->PortName = strdup( PortName );
|
|
|
|
Handle->AddressList = NULL;
|
|
Handle->AddressInfo = NULL;
|
|
|
|
Handle->Queue = Queue;
|
|
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Set as %s [%s:%s]",
|
|
ProcessName, Name, Handle->Name, ConnectTypeName[Type], HostName, PortName );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenSerialPort( THandle * Handle )
|
|
{
|
|
// Validate
|
|
if (!Handle || (Handle->Type == ctNone))
|
|
return NULL;
|
|
else if (Handle->State == csOpen)
|
|
return Handle;
|
|
|
|
// Check if port exits
|
|
if (access( Handle->Path, F_OK ) != 0) {
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Serial Port not found [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
return NULL;
|
|
}
|
|
|
|
// 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 Serial Port [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
return NULL;
|
|
}
|
|
|
|
// Set Send Enable (via RTS) for RS485
|
|
if ((Handle->Type == ctSerial) && (Handle->FlowCtrl == RS485_FLOWCTRL)) {
|
|
int sercmd = TIOCM_RTS;
|
|
ioctl( Handle->FD, TIOCMBIS, &sercmd );
|
|
}
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Serial Port opened [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
|
|
// Update port configuration
|
|
if (Handle->SerialConfig)
|
|
WriteSerialConfig( Handle );
|
|
else
|
|
ReadSerialConfig( Handle );
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Serial Port config, IB:%d, OB:%d, D:%d, P:%d, S:%d, F:%d",
|
|
ProcessName, Name, Handle->Name, Handle->InBaudrate, Handle->OutBaudrate,
|
|
Handle->DataBits, Handle->Parity, Handle->StopBits, Handle->FlowCtrl );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, false, Handle, this );
|
|
|
|
// Set state
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenLinePrinterPort( THandle * Handle )
|
|
{
|
|
// Validate
|
|
if (!Handle || (Handle->Type == ctNone))
|
|
return NULL;
|
|
else if (Handle->State == csOpen)
|
|
return Handle;
|
|
|
|
// Check if port exits
|
|
if (access( Handle->Path, F_OK ) != 0) {
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Printer Port not found [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
return NULL;
|
|
}
|
|
|
|
// 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 Printer Port [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
return NULL;
|
|
}
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Printer Port opened [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, false, Handle, this );
|
|
|
|
// Set state
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenForkPipe( THandle * Handle )
|
|
{
|
|
int pipefd[2];
|
|
int newfd;
|
|
char * Args[50];
|
|
int Count = 0;
|
|
|
|
// Validate
|
|
if (!Handle || (Handle->Type == ctNone))
|
|
return NULL;
|
|
else if (Handle->State == csOpen)
|
|
return Handle;
|
|
|
|
// Validate Exec path
|
|
if (!Handle->Path || !*(Handle->Path)) {
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - No path specified for Exec",
|
|
ProcessName, Name, Handle->Name );
|
|
return NULL;
|
|
}
|
|
|
|
// Create Pipe
|
|
if ((pipe( pipefd ) == -1) || (pipefd[0] == -1) || (pipefd[1] == -1)) {
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Could not open Pipe (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
return NULL;
|
|
}
|
|
|
|
// Fork process
|
|
Handle->ChildPID = fork();
|
|
if (Handle->ChildPID < 0) {
|
|
// Fork has failed
|
|
// Close both ends of the pipe
|
|
close( pipefd[0] );
|
|
close( pipefd[1] );
|
|
Handle->FD = -1;
|
|
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Could not Fork process, %s",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
return NULL;
|
|
}
|
|
else if (Handle->ChildPID > 0) {
|
|
// Fork success - this is parent
|
|
// Close Read-end of pipe, but keep the Write-end
|
|
close( pipefd[0] );
|
|
Handle->FD = pipefd[1];
|
|
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Process forked successfully",
|
|
ProcessName, Name, Handle->Name );
|
|
}
|
|
else {
|
|
// Fork success - this is child
|
|
// Close Write-end of pipe
|
|
close( pipefd[1] );
|
|
|
|
// Replace stdin with Read-end of pipe
|
|
close( 0 );
|
|
newfd = dup2( pipefd[0], 0 );
|
|
close( pipefd[0] );
|
|
if (newfd != 0) {
|
|
// Replace failed, exit immediately
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Stdin redirect failed on forked process",
|
|
ProcessName, Name, Handle->Name );
|
|
exit( EXIT_FAILURE );
|
|
}
|
|
|
|
// Fork : Replace Processing image
|
|
BuildArgs( Handle->Path, Count, Args );
|
|
// printf( "Execute params [%d]:\n", Count );
|
|
// int i = 0;
|
|
// while (Args[i]) {
|
|
// printf( "%d: %s\n", i, Args[i] );
|
|
// i++;
|
|
// }
|
|
execvp( Args[0], Args );
|
|
|
|
// Replace failed, exit immediately
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Exec on forked process failed",
|
|
ProcessName, Name, Handle->Name );
|
|
exit( EXIT_FAILURE );
|
|
}
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, false, true, Handle, this );
|
|
|
|
// Set state
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenUNIXserverSocket( THandle * Handle )
|
|
{
|
|
socklen_t addr_len;
|
|
struct sockaddr_un address;
|
|
|
|
// Validate
|
|
if (Handle->Type != ctUNIXserver)
|
|
return NULL;
|
|
else if (Handle->State == csOpen)
|
|
return Handle;
|
|
|
|
// Remove old socket
|
|
if (unlink( Handle->Path ) && (errno != ENOENT)) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to remove old UNIX Server socket [%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->Path, strerror(errno) );
|
|
|
|
// Set state
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
}
|
|
|
|
// Create socket
|
|
if ((Handle->FD = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to create new UNIX Server socket [%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->Path, strerror(errno) );
|
|
|
|
// Set state
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// Set non-blocking flag
|
|
int flags = fcntl( Handle->FD, F_GETFL, 0 );
|
|
fcntl( Handle->FD, F_SETFL, flags | O_NONBLOCK );
|
|
|
|
// Name Socket
|
|
memset( &address, 0, sizeof(struct sockaddr_un) );
|
|
address.sun_family = AF_UNIX;
|
|
strcpy( address.sun_path, Handle->Path );
|
|
addr_len = sizeof(address);
|
|
|
|
// Bind socket
|
|
if (bind( Handle->FD, (struct sockaddr*)&address, addr_len ) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to bind UNIX Server socket [%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->Path, strerror(errno) );
|
|
|
|
// Set state
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// Create que for 5 connections
|
|
if (listen( Handle->FD, Handle->Queue ) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to listen on UNIX Server socket [%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->Path, strerror(errno) );
|
|
|
|
// Set state
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Server binded and listening [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, false, Handle, this );
|
|
|
|
// Set state
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenUNIXclientSocket( THandle * Handle )
|
|
{
|
|
socklen_t addr_len;
|
|
struct sockaddr_un address;
|
|
|
|
// Check state
|
|
if (Handle->State == csOpen)
|
|
return Handle; // Already open
|
|
|
|
if (Handle->State != csWaitingtoOpen) {
|
|
// Create socket
|
|
if ((Handle->FD = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to create new UNIX Client socket [%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->Path, strerror(errno) );
|
|
|
|
// Set state
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// Set non-blocking flag
|
|
int flags = fcntl( Handle->FD, F_GETFL, 0 );
|
|
fcntl( Handle->FD, F_SETFL, flags | O_NONBLOCK );
|
|
|
|
// Name Socket
|
|
memset( &address, 0, sizeof(struct sockaddr_un) );
|
|
address.sun_family = AF_UNIX;
|
|
strcpy( address.sun_path, Handle->Path );
|
|
addr_len = sizeof(address);
|
|
|
|
// Try to connect to address
|
|
if (!connect( Handle->FD, (struct sockaddr*)&address, addr_len )) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Client waiting to connect [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, true, Handle, this );
|
|
|
|
// Set status
|
|
HandleState( Handle, csWaitingtoOpen );
|
|
return Handle;
|
|
}
|
|
else {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Client could not connect [%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->Path, strerror(errno) );
|
|
|
|
// Remove from Select List
|
|
if (Selector)
|
|
Selector->Remove( Handle->FD, true, true );
|
|
|
|
// Close socket
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
Handle->AddressFailed = true;
|
|
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Client connected [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, true, Handle, this );
|
|
|
|
// Set status
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenUNIXremoteSocket( THandle * Handle )
|
|
{
|
|
THandle ** RemoteClient;
|
|
int ClientFD;
|
|
char ClientName[100];
|
|
|
|
socklen_t addr_len;
|
|
struct sockaddr_un address;
|
|
|
|
// Validate
|
|
if (!Handle)
|
|
return NULL;
|
|
|
|
// Check Handle type
|
|
if (Handle->Type == ctUNIXserver) {
|
|
// 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)) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Server failed to accept blocking connection (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
}
|
|
else {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Server failed to accept connection (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Set non-blocking flag
|
|
int flags = fcntl( ClientFD, F_GETFL, 0 );
|
|
fcntl( ClientFD, F_SETFL, flags | O_NONBLOCK );
|
|
|
|
// 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 );
|
|
if (!SetUnixHandle( *RemoteClient, ctUNIXremote, Handle->Path, 0 )) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Server failed to configure Remote Client connection (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
return NULL;
|
|
}
|
|
|
|
// Copy Parent Buffer setup
|
|
SetInBuffer( *RemoteClient, ((Handle->InBuffer)? Handle->InBuffer->Size() : 0),
|
|
Handle->InTimeout, Handle->InMarker, Handle->InMarkerLen );
|
|
SetOutBuffer( *RemoteClient, ((Handle->OutBuffer)? Handle->OutBuffer->Size() : 0) );
|
|
|
|
// Set Key parameters
|
|
(*RemoteClient)->FD = ClientFD;
|
|
(*RemoteClient)->Parent = Handle;
|
|
(*RemoteClient)->State = csWaitingtoOpen;
|
|
|
|
// Reset Timer
|
|
SetStartTime( &((*RemoteClient)->LastAction) );
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Server accepted Remote Client connection [%s]",
|
|
ProcessName, Name, Handle->Name, (*RemoteClient)->Path );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( (*RemoteClient)->FD, true, true, *RemoteClient, this );
|
|
|
|
return *RemoteClient;
|
|
}
|
|
else if (Handle->Type == ctUNIXremote) {
|
|
// Check state
|
|
if (Handle->State == csOpen) {
|
|
// Already open
|
|
return Handle;
|
|
}
|
|
else if (Handle->State == csWaitingtoOpen) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Remote UNIX Client connected [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->Path );
|
|
|
|
// Update state
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::ResolveAddress( THandle * Handle )
|
|
{
|
|
struct addrinfo * Hints;
|
|
TResolveReq * ResolveReq;
|
|
sigevent ResolveEvt;
|
|
int Result;
|
|
|
|
// Ignore if busy resolving
|
|
if (Handle->State == csPreparing)
|
|
return false;
|
|
|
|
// Check if resolved address available
|
|
if (Handle->AddressInfo) {
|
|
// Return if address still valid
|
|
if (!Handle->AddressFailed)
|
|
return true;
|
|
|
|
// Get next address
|
|
Handle->AddressInfo = Handle->AddressInfo->ai_next;
|
|
Handle->AddressFailed = false;
|
|
}
|
|
|
|
// Dismiss invalid "0.0.0.0" address
|
|
if (Handle->AddressInfo && !strcmp( inet_ntoa(((struct sockaddr_in *)Handle->AddressInfo->ai_addr)->sin_addr), "0.0.0.0" ))
|
|
Handle->AddressInfo = Handle->AddressInfo->ai_next;
|
|
|
|
// Check valid address
|
|
if (Handle->AddressInfo) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Use next resolved Address [%s:%s]->[%s:%u]",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName,
|
|
inet_ntoa(((struct sockaddr_in *)Handle->AddressInfo->ai_addr)->sin_addr),
|
|
ntohs(((struct sockaddr_in *)Handle->AddressInfo->ai_addr)->sin_port) );
|
|
return true;
|
|
}
|
|
|
|
// Clear existing address list
|
|
if (Handle->AddressList) {
|
|
freeaddrinfo( Handle->AddressList );
|
|
Handle->AddressList = NULL;
|
|
Handle->AddressInfo = NULL;
|
|
}
|
|
|
|
// Set address specification
|
|
Hints = (struct addrinfo*)calloc( 1, sizeof(struct addrinfo) );
|
|
if ((Handle->Type == ctTCPserver) || (Handle->Type == ctTCPclient)) {
|
|
Hints->ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
|
|
Hints->ai_socktype = SOCK_STREAM;
|
|
}
|
|
else {
|
|
Hints->ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
|
|
Hints->ai_socktype = SOCK_DGRAM;
|
|
}
|
|
|
|
// Create request data
|
|
ResolveReq = new TResolveReq;
|
|
Handle->ResolveReq = ResolveReq;
|
|
|
|
ResolveReq->Handle = Handle;
|
|
ResolveReq->Request = (gaicb*)calloc( 1, sizeof(gaicb) );
|
|
|
|
// DNS request / reply structure
|
|
ResolveReq->Request->ar_name = Handle->HostName;
|
|
ResolveReq->Request->ar_service = Handle->PortName;
|
|
ResolveReq->Request->ar_request = Hints;
|
|
ResolveReq->Request->ar_result = NULL;
|
|
|
|
// Configure signal event
|
|
ResolveEvt.sigev_notify = SIGEV_SIGNAL;
|
|
ResolveEvt.sigev_signo = SIGRTMIN;
|
|
ResolveEvt.sigev_value.sival_ptr = ResolveReq;
|
|
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Resolving Host name [%s:%s]...",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName );
|
|
|
|
// Resolve Host & Port Names
|
|
HandleState( Handle, csPreparing );
|
|
|
|
if ((Result = getaddrinfo_a( GAI_NOWAIT, &(Handle->ResolveReq->Request), 1, &ResolveEvt )) != 0) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Error resolving Host Name [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, gai_strerror(Result) );
|
|
DestroyResolveReq( Handle, true );
|
|
|
|
HandleState( Handle, csFailed );
|
|
SetStartTime( &Handle->LastAction ); // Allow delay before retrying resolve
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::HandleResolve( THandle * Handle )
|
|
{
|
|
bool Result;
|
|
|
|
// Validate result
|
|
if ((Result = gai_error( Handle->ResolveReq->Request )) != 0) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Error resolving Host Name [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, gai_strerror(Result) );
|
|
DestroyResolveReq( Handle, true );
|
|
|
|
HandleState( Handle, csFailed );
|
|
SetStartTime( &Handle->LastAction ); // Allow delay before retrying resolve
|
|
return false;
|
|
}
|
|
|
|
// Read result
|
|
Handle->AddressList = Handle->ResolveReq->Request->ar_result;
|
|
Handle->AddressInfo = Handle->AddressList;
|
|
|
|
// Select first address, skip "0.0.0.0"
|
|
if (!strcmp( inet_ntoa(((struct sockaddr_in *)Handle->AddressInfo->ai_addr)->sin_addr), "0.0.0.0" ))
|
|
Handle->AddressInfo = Handle->AddressInfo->ai_next;
|
|
|
|
if (!Handle->AddressInfo) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to resolve Host Name [%s:%s] (No Result)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName );
|
|
freeaddrinfo( Handle->AddressList );
|
|
Handle->AddressList = NULL;
|
|
Handle->AddressInfo = NULL;
|
|
|
|
HandleState( Handle, csFailed );
|
|
SetStartTime( &Handle->LastAction ); // Allow delay before retrying resolve
|
|
return false;
|
|
}
|
|
|
|
// Return address
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Host name resolved [%s:%s]->[%s:%u]",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName,
|
|
inet_ntoa(((struct sockaddr_in *)Handle->AddressInfo->ai_addr)->sin_addr),
|
|
ntohs(((struct sockaddr_in *)Handle->AddressInfo->ai_addr)->sin_port) );
|
|
|
|
// Destroy request
|
|
DestroyResolveReq( Handle, false );
|
|
HandleState( Handle, csPrepared );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::CancelResolve( THandle * Handle )
|
|
{
|
|
int Result;
|
|
int WaitCnt = 0;
|
|
|
|
// Check if request pending
|
|
if (!Handle->ResolveReq)
|
|
return true;
|
|
|
|
// Cancel request
|
|
Result = gai_cancel( FirstHandle->ResolveReq->Request );
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Cancelling Host Name resolve [%s:%s] (%s)",
|
|
ProcessName, Name, FirstHandle->Name, FirstHandle->HostName, FirstHandle->PortName, gai_strerror(Result) );
|
|
|
|
if (Result == EAI_CANCELED) {
|
|
// Clear request
|
|
DestroyResolveReq( Handle, true );
|
|
}
|
|
else if (Result == EAI_NOTCANCELED) {
|
|
// Allow request to return
|
|
while (FirstHandle->ResolveReq && (WaitCnt < 1000)) {
|
|
WaitCnt++;
|
|
usleep(1000);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::DestroyResolveReq( THandle * Handle, bool DestroyResult )
|
|
{
|
|
// Validate
|
|
if (!Handle || !Handle->ResolveReq)
|
|
return false;
|
|
|
|
// Destroy resolv request
|
|
if (Handle->ResolveReq->Request) {
|
|
if (DestroyResult && Handle->ResolveReq->Request->ar_result)
|
|
freeaddrinfo( Handle->ResolveReq->Request->ar_result );
|
|
if (Handle->ResolveReq->Request->ar_request)
|
|
free( (void*)Handle->ResolveReq->Request->ar_request );
|
|
if (Handle->ResolveReq->Request)
|
|
free( Handle->ResolveReq->Request );
|
|
}
|
|
|
|
// Reset request
|
|
if (Handle->ResolveReq)
|
|
delete Handle->ResolveReq;
|
|
Handle->ResolveReq = NULL;
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenUDPserverSocket( THandle * Handle )
|
|
{
|
|
// Validate Handle
|
|
if (Handle->Type != ctUDPserver)
|
|
return NULL;
|
|
|
|
// Resolve Host & Port Names
|
|
if (!ResolveAddress( Handle ))
|
|
return NULL;
|
|
|
|
// Create socket
|
|
if ((Handle->FD = socket( Handle->AddressInfo->ai_family, Handle->AddressInfo->ai_socktype, Handle->AddressInfo->ai_protocol )) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to create UDP socket [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Set state
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// 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, Handle->AddressInfo->ai_addr, Handle->AddressInfo->ai_addrlen ) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to bind UDP socket [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Close handle
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
Handle->AddressFailed = true;
|
|
|
|
// Change state
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UDP socket binded and listening [%s:%s]",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, false, Handle, this );
|
|
|
|
// Set state
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenUDPremoteSocket( THandle * Handle, char * ClientAddress, char * ClientPort )
|
|
{
|
|
THandle ** RemoteClient;
|
|
int ClientCount;
|
|
char ClientName[100];
|
|
|
|
// Validate
|
|
if (!Handle || (Handle->Type != ctUDPserver))
|
|
return NULL;
|
|
|
|
// Check if Remote client already exists
|
|
ClientCount = 1;
|
|
RemoteClient = &FirstHandle;
|
|
while (*RemoteClient && (strcmp((*RemoteClient)->HostName, ClientAddress) || strcmp((*RemoteClient)->PortName, ClientPort))) {
|
|
RemoteClient = &((*RemoteClient)->Next);
|
|
ClientCount++;
|
|
}
|
|
if (*RemoteClient)
|
|
return *RemoteClient;
|
|
|
|
// Create Remote Client Handle
|
|
sprintf( ClientName, "%s-%d", Handle->Name, ClientCount );
|
|
*RemoteClient = CreateHandle( ClientName, false );
|
|
if (!SetSocketHandle( *RemoteClient, ctUDPremote, ClientAddress, ClientPort, 0 )) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UDP Server failed to configure Remote Client connection (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
return NULL;
|
|
}
|
|
|
|
// Copy Parent Buffer setup
|
|
SetInBuffer( *RemoteClient, ((Handle->InBuffer)? Handle->InBuffer->Size() : 0),
|
|
Handle->InTimeout, Handle->InMarker, Handle->InMarkerLen );
|
|
SetOutBuffer( *RemoteClient, ((Handle->OutBuffer)? Handle->OutBuffer->Size() : 0) );
|
|
|
|
// Set Key parameters
|
|
(*RemoteClient)->FD = Handle->FD;
|
|
(*RemoteClient)->Parent = Handle;
|
|
(*RemoteClient)->State = csOpen;
|
|
|
|
// Reset Timer
|
|
SetStartTime( &((*RemoteClient)->LastAction) );
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UDP Server accepted Remote Client connection [%s:%s]",
|
|
ProcessName, Name, Handle->Name, ClientAddress, ClientPort );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( (*RemoteClient)->FD, true, true, *RemoteClient, this );
|
|
|
|
return *RemoteClient;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenUDPclientSocket( THandle * Handle )
|
|
{
|
|
// Check state
|
|
if (Handle->State == csOpen)
|
|
return Handle; // Already open
|
|
|
|
// Resolve IP Address
|
|
if (!ResolveAddress( Handle ))
|
|
return NULL;
|
|
|
|
// Create File descriptor
|
|
if ((Handle->FD = socket( Handle->AddressInfo->ai_family, Handle->AddressInfo->ai_socktype, Handle->AddressInfo->ai_protocol )) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to create UDP Client socket [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Set Status
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// Set Non blocking open
|
|
int flags = fcntl( Handle->FD, F_GETFL, 0 );
|
|
fcntl( Handle->FD, F_SETFL, O_NONBLOCK|flags );
|
|
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UDP Client ready [%s:%s]",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, true, Handle, this );
|
|
|
|
// Set status
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenTCPserverSocket( THandle * Handle )
|
|
{
|
|
// 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 != ctTCPserver)
|
|
return NULL;
|
|
|
|
// Resolve Host & Port Names
|
|
if (!ResolveAddress( Handle ))
|
|
return NULL;
|
|
|
|
// Create socket
|
|
if ((Handle->FD = socket( Handle->AddressInfo->ai_family, Handle->AddressInfo->ai_socktype, Handle->AddressInfo->ai_protocol )) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to create TCP Server socket [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Set state
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// 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
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Could not set socket options [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Set state
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
}
|
|
|
|
// Configure TCP keep alive settings
|
|
if ((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
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Could not set KeepAlive options [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Set state
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
}
|
|
|
|
// 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, Handle->AddressInfo->ai_addr, Handle->AddressInfo->ai_addrlen ) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to bind TCP Server socket [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Close handle
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
Handle->AddressFailed = true;
|
|
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// Create que for 5 connections
|
|
if (listen( Handle->FD, Handle->Queue ) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to listen on TCP Server socket [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Close handle
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
Handle->AddressFailed = true;
|
|
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Server binded and listening [%s:%s]",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, false, Handle, this );
|
|
|
|
// Set state
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenTCPremoteSocket( THandle * Handle )
|
|
{
|
|
THandle ** RemoteClient;
|
|
int ClientFD;
|
|
char ClientAddress[50];
|
|
char ClientPort[20];
|
|
char ClientName[100];
|
|
|
|
socklen_t addr_len;
|
|
struct sockaddr_in address;
|
|
|
|
// Validate
|
|
if (!Handle)
|
|
return NULL;
|
|
|
|
// Check Handle type
|
|
if (Handle->Type == ctTCPserver) {
|
|
// 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)) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Server failed to accept blocking connection (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
}
|
|
else {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Server failed to accept connection (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// 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) );
|
|
sprintf( ClientPort, "%d", ntohs(address.sin_port) );
|
|
|
|
// 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 );
|
|
if (!SetSocketHandle( *RemoteClient, ctTCPremote, ClientAddress, ClientPort, 0 )) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Server failed to configure Remote Client connection (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
return NULL;
|
|
}
|
|
|
|
// Copy Parent Buffer setup
|
|
SetInBuffer( *RemoteClient, ((Handle->InBuffer)? Handle->InBuffer->Size() : 0),
|
|
Handle->InTimeout, Handle->InMarker, Handle->InMarkerLen );
|
|
SetOutBuffer( *RemoteClient, ((Handle->OutBuffer)? Handle->OutBuffer->Size() : 0) );
|
|
|
|
// Set Key parameters
|
|
(*RemoteClient)->FD = ClientFD;
|
|
(*RemoteClient)->Parent = Handle;
|
|
(*RemoteClient)->State = csWaitingtoOpen;
|
|
|
|
// Reset Timer
|
|
SetStartTime( &((*RemoteClient)->LastAction) );
|
|
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Server accepted Remote Client connection [%s]",
|
|
ProcessName, Name, Handle->Name, ClientAddress );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( (*RemoteClient)->FD, true, true, *RemoteClient, this );
|
|
|
|
return *RemoteClient;
|
|
}
|
|
else if (Handle->Type == ctTCPremote) {
|
|
// Check state
|
|
if (Handle->State == csOpen) {
|
|
// Already open
|
|
return Handle;
|
|
}
|
|
else if (Handle->State == csWaitingtoOpen) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Remote TCP Client connected [%s]",
|
|
ProcessName, Name, Handle->Name, Handle->HostName );
|
|
|
|
// Update state
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
THandle * CSelectableCore::OpenTCPclientSocket( THandle * Handle )
|
|
{
|
|
// Socket options
|
|
int KeepAlive_opt = 1; // Enable/disable keep alive
|
|
int TCPidle_opt = 5; // Idle time on socket before sending first keep alive signal
|
|
int TCPint_opt = 2; // Interval between keep alive signals
|
|
int TCPcnt_opt = 3; // No of missed keep alive response before connection fail
|
|
int TCPsyn_opt = 3; // Max SYN (connect retries) before open fails
|
|
|
|
// Check state
|
|
if (Handle->State == csOpen)
|
|
return Handle; // Already open
|
|
|
|
if (Handle->State != csWaitingtoOpen) {
|
|
// Resolve IP Address
|
|
if (!ResolveAddress( Handle ))
|
|
return NULL;
|
|
|
|
// Create File descriptor
|
|
if ((Handle->FD = socket( Handle->AddressInfo->ai_family, Handle->AddressInfo->ai_socktype, Handle->AddressInfo->ai_protocol )) < 0) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to create TCP Client socket [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Set Status
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
};
|
|
|
|
// 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 ((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) ||
|
|
(setsockopt( Handle->FD, SOL_TCP, TCP_SYNCNT, &TCPsyn_opt, sizeof(TCPsyn_opt)) == -1)) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Could not set KeepAlive options [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Close handle
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// Try to connect to address
|
|
if (!connect( Handle->FD, Handle->AddressInfo->ai_addr, Handle->AddressInfo->ai_addrlen )) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Client connected [%s:%s]",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, true, Handle, this );
|
|
|
|
// Set status
|
|
HandleState( Handle, csOpen );
|
|
return Handle;
|
|
}
|
|
else if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINPROGRESS) || (errno == EALREADY)) {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Client waiting to connect [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( Handle->FD, true, true, Handle, this );
|
|
|
|
// Set status
|
|
HandleState( Handle, csWaitingtoOpen );
|
|
return Handle;
|
|
}
|
|
else {
|
|
// Log Event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Client could not connect [%s:%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) );
|
|
|
|
// Remove from Select List
|
|
if (Selector)
|
|
Selector->Remove( Handle->FD, true, true );
|
|
|
|
// Close socket
|
|
close( Handle->FD );
|
|
Handle->FD = -1;
|
|
Handle->AddressFailed = true;
|
|
|
|
HandleState( Handle, csFailed );
|
|
return NULL;
|
|
}
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::Open( THandle * Handle )
|
|
{
|
|
THandle * NewHandle = NULL;
|
|
|
|
// Validate
|
|
if (!Handle)
|
|
return -1;
|
|
|
|
// Open correctly
|
|
switch (Handle->Type) {
|
|
case ctSerial :
|
|
NewHandle = OpenSerialPort( Handle );
|
|
break;
|
|
case ctLinePrinter :
|
|
NewHandle = OpenLinePrinterPort( Handle );
|
|
break;
|
|
case ctForkPipe :
|
|
NewHandle = OpenForkPipe( Handle );
|
|
break;
|
|
case ctUNIXserver :
|
|
NewHandle = OpenUNIXserverSocket( Handle );
|
|
break;
|
|
case ctUNIXclient :
|
|
NewHandle = OpenUNIXclientSocket( Handle );
|
|
break;
|
|
case ctUDPserver :
|
|
NewHandle = OpenUDPserverSocket( Handle );
|
|
break;
|
|
case ctUDPclient :
|
|
NewHandle = OpenUDPclientSocket( Handle );
|
|
break;
|
|
case ctTCPserver :
|
|
NewHandle = OpenTCPserverSocket( Handle );
|
|
break;
|
|
case ctTCPclient :
|
|
NewHandle = OpenTCPclientSocket( Handle );
|
|
break;
|
|
default:
|
|
NewHandle = NULL;
|
|
}
|
|
|
|
// Set timer (for re-open or auto-close)
|
|
SetStartTime( &Handle->LastAction );
|
|
return (NewHandle)? NewHandle->FD : -1;
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Delete socket
|
|
bool CSelectableCore::Close( THandle * Handle, bool QuickReopen )
|
|
{
|
|
bool Fail;
|
|
THandle * ChildHandle = NULL;
|
|
THandle * NextHandle = NULL;
|
|
|
|
// Validate
|
|
if (!Handle || (Handle->FD == -1))
|
|
return false;
|
|
|
|
// Close Children
|
|
if ((Handle->Type == ctTCPserver) || (Handle->Type == ctUDPserver) || (Handle->Type == ctUNIXserver)) {
|
|
ChildHandle = FirstHandle;
|
|
while (ChildHandle) {
|
|
if (ChildHandle->Parent == Handle) {
|
|
// Close and remove handle
|
|
NextHandle = ChildHandle->Next;
|
|
Close( ChildHandle, false );
|
|
ChildHandle = NextHandle;
|
|
}
|
|
else {
|
|
// Skip Handle
|
|
ChildHandle = ChildHandle->Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set Send Enable (via RTS) for RS485
|
|
if ((Handle->Type == ctSerial) && (Handle->FlowCtrl == RS485_FLOWCTRL)) {
|
|
int sercmd = TIOCM_RTS;
|
|
ioctl( Handle->FD, TIOCMBIC, &sercmd );
|
|
}
|
|
|
|
// Close Handle
|
|
if (Handle->Type == ctUDPremote)
|
|
Fail = false;
|
|
else
|
|
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
|
|
switch (Handle->Type) {
|
|
case ctSerial:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Serial Port %s [%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Path );
|
|
break;
|
|
|
|
case ctLinePrinter:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Printer Port %s [%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Path );
|
|
break;
|
|
|
|
case ctForkPipe:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Forked Pipe %s",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"));
|
|
break;
|
|
|
|
case ctUNIXserver:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Server %s [%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Path );
|
|
|
|
// Remove UNIX socket file
|
|
if (unlink( Handle->Path ) && (errno != ENOENT)) {
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Failed to remove old UNIX Server socket [%s] (%s)",
|
|
ProcessName, Name, Handle->Name, Handle->Path, strerror(errno) );
|
|
}
|
|
break;
|
|
|
|
case ctUNIXclient:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Client %s [%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Path );
|
|
break;
|
|
|
|
case ctUNIXremote:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UNIX Remote Client %s [%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Path );
|
|
break;
|
|
|
|
case ctUDPserver:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UDP Server %s [%s:%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->HostName, Handle->PortName );
|
|
break;
|
|
|
|
case ctUDPclient:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UDP Client %s [%s:%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->HostName, Handle->PortName );
|
|
break;
|
|
|
|
case ctUDPremote:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - UDP Remote Client %s [%s:%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->HostName, Handle->PortName );
|
|
break;
|
|
|
|
case ctTCPserver:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Server %s [%s:%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->HostName, Handle->PortName );
|
|
break;
|
|
|
|
case ctTCPremote:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Remote Client %s [%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->HostName );
|
|
break;
|
|
|
|
case ctTCPclient:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - TCP Client %s [%s:%s]",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->HostName, Handle->PortName );
|
|
break;
|
|
|
|
case ctNone:
|
|
default:
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - %s, invalid Handle type",
|
|
ProcessName, Name, Handle->Name, ((Fail)? "failed" : "closed") );
|
|
break;
|
|
};
|
|
|
|
// Change State
|
|
HandleState( Handle, ((Fail)? csFailed : csClosed) );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Device Interface
|
|
bool CSelectableCore::Read( THandle * Handle )
|
|
{
|
|
THandle * ClientHandle = NULL;
|
|
|
|
int BytesRead = 0;
|
|
int BytesWaiting = -1;
|
|
|
|
char * UDPbuffer = NULL;
|
|
char UDPaddress[50] = "";
|
|
char UDPport[20] = "";
|
|
|
|
// 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 for closing/opening event on Socket
|
|
if ((Handle->Type == ctTCPserver) || (Handle->Type == ctUNIXserver)) {
|
|
// Incoming client request
|
|
if (Handle->Type == ctTCPserver)
|
|
ClientHandle = OpenTCPremoteSocket( Handle );
|
|
else if (Handle->Type == ctUNIXserver)
|
|
ClientHandle = OpenUNIXremoteSocket( Handle );
|
|
|
|
if (ClientHandle == NULL)
|
|
return false;
|
|
|
|
// Reset Timer
|
|
SetStartTime( &(Handle->LastAction) );
|
|
|
|
// Add to Select Lists
|
|
if (Selector)
|
|
Selector->Add( ClientHandle->FD, true, true, ClientHandle, this );
|
|
return true;
|
|
}
|
|
else if ((Handle->Type == ctTCPremote) || (Handle->Type == ctTCPclient) ||
|
|
(Handle->Type == ctUNIXremote) || (Handle->Type == ctUNIXclient)) {
|
|
// Check if socket ready (non-block open in progress)
|
|
if (Handle->State == csWaitingtoOpen) {
|
|
if (Handle->Type == ctTCPremote)
|
|
OpenTCPremoteSocket( Handle );
|
|
else if (Handle->Type == ctTCPclient)
|
|
OpenTCPclientSocket( Handle );
|
|
else if (Handle->Type == ctUNIXremote)
|
|
OpenUNIXremoteSocket( Handle );
|
|
else if (Handle->Type == ctUNIXclient)
|
|
OpenUNIXclientSocket( Handle );
|
|
|
|
// Reset Timer (for auto-close)
|
|
SetStartTime( &(Handle->LastAction) );
|
|
|
|
return true;
|
|
}
|
|
|
|
// Check if anything to read
|
|
ioctl( Handle->FD, FIONREAD, &BytesWaiting );
|
|
|
|
// Error or EOF from server
|
|
if (BytesWaiting < 1) {
|
|
// Log if there is an error
|
|
if (Log && (BytesWaiting < 1))
|
|
Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Data waiting error (%s)",
|
|
ProcessName, Name, Handle->Name, strerror(errno) );
|
|
|
|
// Close Handle
|
|
Close( Handle, false );
|
|
|
|
// Destroy Client
|
|
if ((Handle->Type == ctTCPremote) || (Handle->Type == ctUNIXremote))
|
|
RemoveHandle( Handle );
|
|
return false;
|
|
}
|
|
}
|
|
else if ((Handle->Type == ctUDPserver) || (Handle->Type == ctUDPclient) || (Handle->Type == ctUDPremote)) {
|
|
// Check if anything to read
|
|
ioctl( Handle->FD, FIONREAD, &BytesWaiting );
|
|
|
|
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;
|
|
}
|
|
|
|
// Read incoming message and address
|
|
errno = 0;
|
|
UDPbuffer = (char*)malloc( BytesWaiting+1 );
|
|
BytesRead = ReadFromUDP( Handle, (char*)UDPaddress, (char*)UDPport, UDPbuffer, BytesWaiting );
|
|
|
|
if (!errno && (Handle->Type == ctUDPserver)) {
|
|
// Create/Find Incoming client
|
|
ClientHandle = OpenUDPremoteSocket( Handle, UDPaddress, UDPport );
|
|
if (ClientHandle == NULL)
|
|
return false;
|
|
}
|
|
|
|
// Reset Timer
|
|
SetStartTime( &(Handle->LastAction) );
|
|
}
|
|
else if (Handle->Type == ctSerial) {
|
|
// 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;
|
|
}
|
|
}
|
|
else if (Handle->Type == ctLinePrinter) {
|
|
// // Check if anything to read
|
|
// ioctl( Handle->FD, FIONREAD, &BytesWaiting ); <-- Not valid ioctl for lp port
|
|
//
|
|
// // 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) {
|
|
if ((Handle->Type == ctUDPserver) || (Handle->Type == ctUDPclient) || (Handle->Type == ctUDPremote)) {
|
|
if (BytesRead)
|
|
Handle->InBuffer->Push( true, UDPbuffer, abs(BytesRead) );
|
|
if (UDPbuffer) free( UDPbuffer );
|
|
}
|
|
else {
|
|
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 );
|
|
}
|
|
|
|
// Process Buffer
|
|
if (BytesRead != 0)
|
|
ProcessInputBuffer( Handle, false );
|
|
}
|
|
|
|
// Reset timer
|
|
SetStartTime( &(Handle->InStart) );
|
|
|
|
return (bool)BytesRead;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool CSelectableCore::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
|
|
if (Handle->Type == ctTCPremote)
|
|
OpenTCPremoteSocket( Handle );
|
|
else if (Handle->Type == ctTCPclient)
|
|
OpenTCPclientSocket( Handle );
|
|
else if (Handle->Type == ctUNIXremote)
|
|
OpenUNIXremoteSocket( Handle );
|
|
else if (Handle->Type == ctUNIXclient)
|
|
OpenUNIXclientSocket( 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;
|
|
if ((Handle->Type == ctUDPclient) || (Handle->Type == ctUDPremote)) {
|
|
Len = Handle->OutBuffer->Peek( &Data );
|
|
BytesWritten = WriteToUDP( Handle, Data, Len, true );
|
|
}
|
|
else if ((Handle->Type == ctSerial) && (Handle->FlowCtrl == RS485_FLOWCTRL)) {
|
|
// Set Send Enable (via RTS pin) when sending data
|
|
int sercmd = TIOCM_RTS;
|
|
ioctl( Handle->FD, TIOCMBIC, &sercmd );
|
|
usleep( 1000 );
|
|
|
|
BytesWritten = Handle->OutBuffer->WriteToFD( Handle->FD );
|
|
|
|
tcdrain( Handle->FD );
|
|
usleep( 1000 );
|
|
ioctl( Handle->FD, TIOCMBIS, &sercmd );
|
|
}
|
|
else {
|
|
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 CSelectableCore::ReadFromUDP( THandle * Handle, char * RemoteAddr, char * RemotePort, char * Data, int MaxLen )
|
|
{
|
|
int BytesRead = 0;
|
|
|
|
struct sockaddr_in Addr;
|
|
socklen_t AddrLen = sizeof(Addr);
|
|
|
|
// Check if buffer created
|
|
if (!Handle || (Handle->FD == -1) || !RemoteAddr || !RemotePort || (MaxLen < 1))
|
|
return 0;
|
|
|
|
// Get datagram
|
|
Addr.sin_family = AF_UNSPEC; // use AF_INET6 to force IPv6
|
|
Addr.sin_port = ((struct sockaddr_in *)Handle->AddressInfo->ai_addr)->sin_port;
|
|
Addr.sin_addr.s_addr = ((struct sockaddr_in *)Handle->AddressInfo->ai_addr)->sin_addr.s_addr;
|
|
|
|
BytesRead = recvfrom( Handle->FD, Data, MaxLen, 0, (struct sockaddr *)&Addr, &AddrLen );
|
|
|
|
if (BytesRead >= 0) {
|
|
// Decode address
|
|
strcpy( RemoteAddr, inet_ntoa(Addr.sin_addr) );
|
|
sprintf( RemotePort, "%d", ntohs(Addr.sin_port) );
|
|
}
|
|
|
|
return (BytesRead < 0)? 0 : BytesRead; // Report zero on error
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::WriteToUDP( THandle * Handle, const char * Data, int Len, bool Force )
|
|
{
|
|
int BytesWritten = 0;
|
|
int DataLen = (Len != -1)? Len : (Data)? strlen(Data) : 0;
|
|
|
|
// Check if buffer created
|
|
if (!Handle || (Handle->FD == -1) || !DataLen)
|
|
return 0;
|
|
|
|
// Set Options
|
|
BytesWritten = sendto( Handle->FD, Data, DataLen, MSG_NOSIGNAL /*| ((!Force)? MSG_DONTWAIT : 0)*/,
|
|
Handle->AddressInfo->ai_addr, Handle->AddressInfo->ai_addrlen );
|
|
|
|
return (BytesWritten < 0)? 0 : BytesWritten; // Report zero on error
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
int CSelectableCore::OutputHandle( THandle * Handle, const char * Data, int Len )
|
|
{
|
|
THandle * ChildHandle = NULL;
|
|
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) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Input rejected, Resolving (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 );
|
|
|
|
if ((Handle->Type == ctTCPserver) || (Handle->Type == ctUNIXserver)) {
|
|
// 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( true, Data, Len );
|
|
|
|
// Add to select write list
|
|
if (BytesWritten && Selector)
|
|
Selector->Add( ChildHandle->FD, false, true, ChildHandle, this );
|
|
}
|
|
else {
|
|
// Write directly to handle / socket
|
|
errno = 0;
|
|
if ((Handle->Type == ctUDPclient)|| (Handle->Type == ctUDPremote))
|
|
BytesWritten = WriteToUDP( ChildHandle, Data, Len, true );
|
|
else
|
|
BytesWritten = WriteToFD( ChildHandle->FD, Data, Len, true );
|
|
|
|
if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s/%s: Handle '%s' - OUT:",
|
|
ProcessName, Name, ChildHandle->Name );
|
|
|
|
// Report failure
|
|
if (errno) {
|
|
if (Log) Log->Message( LogLevel, dlHigh, "%s/%s: Handle '%s' - Error sending data [%d/%d] (%s)",
|
|
ProcessName, Name, ChildHandle->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, ChildHandle->Name, BytesWritten, DataLen );
|
|
}
|
|
|
|
// Reset timeout
|
|
if (BytesWritten != 0)
|
|
SetStartTime( &(ChildHandle->LastAction) );
|
|
}
|
|
}
|
|
// 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( 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;
|
|
if ((Handle->Type == ctUDPclient)|| (Handle->Type == ctUDPremote))
|
|
BytesWritten = WriteToUDP( Handle, Data, Len, true );
|
|
else
|
|
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 );
|
|
}
|
|
|
|
// Reset timeout
|
|
if (BytesWritten != 0)
|
|
SetStartTime( &(Handle->LastAction) );
|
|
}
|
|
}
|
|
|
|
return BytesWritten;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Set serial port configuration parameters
|
|
bool CSelectableCore::WriteSerialConfig( THandle * Handle )
|
|
{
|
|
struct termios newtio;
|
|
int flags = 0;
|
|
speed_t inbaud = 0;
|
|
speed_t outbaud = 0;
|
|
int mcs = 0;
|
|
|
|
// Get Handle
|
|
if (!Handle || (Handle->Type != ctSerial))
|
|
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 (Handle->InBaudrate) {
|
|
case 921600: inbaud = B921600; break;
|
|
case 576000: inbaud = B576000; break;
|
|
case 460800: inbaud = B460800; break;
|
|
case 230400: inbaud = B230400; break;
|
|
//case 128000: _inbaud = B128000; break;
|
|
case 115200: inbaud = B115200; break;
|
|
//case 76800: _inbaud = B76800; break;
|
|
case 57600: inbaud = B57600; break;
|
|
case 38400: inbaud = B38400; break;
|
|
//case 28800: _inbaud = B28800; break;
|
|
case 19200: inbaud = B19200; break;
|
|
//case 14400: _inbaud = B14400; break;
|
|
case 9600: inbaud = B9600; break;
|
|
case 4800: inbaud = B4800; break;
|
|
case 2400: inbaud = B2400; break;
|
|
case 1800: inbaud = B1800; break;
|
|
case 1200: inbaud = B1200; break;
|
|
case 600: inbaud = B600; break;
|
|
case 300: inbaud = B300; break;
|
|
case 200: inbaud = B200; break;
|
|
case 150: inbaud = B150; break;
|
|
case 134: inbaud = B134; break;
|
|
case 110: inbaud = B110; break;
|
|
case 75: inbaud = B75; break;
|
|
case 50: inbaud = B50; break;
|
|
|
|
default: inbaud = B9600; break;
|
|
}
|
|
switch (Handle->OutBaudrate) {
|
|
case 921600: outbaud = B921600; break;
|
|
case 576000: outbaud = B576000; break;
|
|
case 460800: outbaud = B460800; break;
|
|
case 230400: outbaud = B230400; break;
|
|
//case 128000: _outbaud = B128000; break;
|
|
case 115200: outbaud = B115200; break;
|
|
//case 76800: _outbaud = B76800; break;
|
|
case 57600: outbaud = B57600; break;
|
|
case 38400: outbaud = B38400; break;
|
|
//case 28800: _outbaud = B28800; break;
|
|
case 19200: outbaud = B19200; break;
|
|
//case 14400: _outbaud = B14400; break;
|
|
case 9600: outbaud = B9600; break;
|
|
case 4800: outbaud = B4800; break;
|
|
case 2400: outbaud = B2400; break;
|
|
case 1800: outbaud = B1800; break;
|
|
case 1200: outbaud = B1200; break;
|
|
case 600: outbaud = B600; break;
|
|
case 300: outbaud = B300; break;
|
|
case 200: outbaud = B200; break;
|
|
case 150: outbaud = B150; break;
|
|
case 134: outbaud = B134; break;
|
|
case 110: outbaud = B110; break;
|
|
case 75: outbaud = B75; break;
|
|
case 50: outbaud = B50; break;
|
|
|
|
default: outbaud = B9600; break;
|
|
}
|
|
|
|
// Set Baud rate
|
|
cfsetispeed( &newtio, (speed_t)inbaud );
|
|
cfsetospeed( &newtio, (speed_t)outbaud );
|
|
|
|
// Generate Mark/Space parity
|
|
if ((Handle->DataBits == 7) && ((Handle->Parity == MARK_PARITY) || (Handle->Parity == SPACE_PARITY)))
|
|
Handle->DataBits = 8;
|
|
|
|
// Process the data bits
|
|
newtio.c_cflag &= ~CSIZE;
|
|
switch (Handle->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 (Handle->Parity == EVEN_PARITY)
|
|
newtio.c_cflag |= PARENB;
|
|
else if (Handle->Parity == ODD_PARITY)
|
|
newtio.c_cflag |= (PARENB | PARODD);
|
|
|
|
// Flow Control (for now)
|
|
newtio.c_cflag &= ~CRTSCTS;
|
|
|
|
// Process Stop Bits
|
|
if (Handle->StopBits == 2)
|
|
newtio.c_cflag |= CSTOPB;
|
|
else
|
|
newtio.c_cflag &= ~CSTOPB;
|
|
|
|
newtio.c_iflag = IGNBRK;
|
|
|
|
// Software Flow Control
|
|
if (Handle->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] = Handle->DataWait; // 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 (Handle->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
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Port not configured",
|
|
ProcessName, Name, Handle->Name );
|
|
return false;
|
|
}
|
|
|
|
// Port configured
|
|
// Log event
|
|
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Port configured",
|
|
ProcessName, Name, Handle->Name );
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Get serial port configuration parameters
|
|
bool CSelectableCore::ReadSerialConfig( THandle * Handle )
|
|
{
|
|
int inspeed;
|
|
int outspeed;
|
|
unsigned char bits;
|
|
struct termios getTermios;
|
|
|
|
// Validate handle
|
|
if (!Handle || (Handle->Type != ctSerial))
|
|
return false;
|
|
|
|
// Get port setup
|
|
tcgetattr( Handle->FD, &getTermios );
|
|
inspeed = cfgetispeed( &getTermios );
|
|
outspeed = cfgetospeed( &getTermios );
|
|
|
|
// Get Baud Rate
|
|
switch (inspeed) {
|
|
case B0 : Handle->InBaudrate = 0; break;
|
|
case B50 : Handle->InBaudrate = 50; break;
|
|
case B75 : Handle->InBaudrate = 75; break;
|
|
case B110 : Handle->InBaudrate = 110; break;
|
|
case B134 : Handle->InBaudrate = 134; break;
|
|
case B150 : Handle->InBaudrate = 150; break;
|
|
case B200 : Handle->InBaudrate = 200; break;
|
|
case B300 : Handle->InBaudrate = 300; break;
|
|
case B600 : Handle->InBaudrate = 600; break;
|
|
case B1200 : Handle->InBaudrate = 1200; break;
|
|
case B1800: Handle->InBaudrate = 1800; break;
|
|
case B2400: Handle->InBaudrate = 2400; break;
|
|
case B4800: Handle->InBaudrate = 4800; break;
|
|
case B9600: Handle->InBaudrate = 9600; break;
|
|
case B19200: Handle->InBaudrate = 19200; break;
|
|
case B38400: Handle->InBaudrate = 38400; break;
|
|
case B57600: Handle->InBaudrate = 57600; break;
|
|
case B115200: Handle->InBaudrate = 115200; break;
|
|
case B230400: Handle->InBaudrate = 230400; break;
|
|
default: Handle->InBaudrate = 0; break;
|
|
}
|
|
switch (outspeed) {
|
|
case B0 : Handle->OutBaudrate = 0; break;
|
|
case B50 : Handle->OutBaudrate = 50; break;
|
|
case B75 : Handle->OutBaudrate = 75; break;
|
|
case B110 : Handle->OutBaudrate = 110; break;
|
|
case B134 : Handle->OutBaudrate = 134; break;
|
|
case B150 : Handle->OutBaudrate = 150; break;
|
|
case B200 : Handle->OutBaudrate = 200; break;
|
|
case B300 : Handle->OutBaudrate = 300; break;
|
|
case B600 : Handle->OutBaudrate = 600; break;
|
|
case B1200 : Handle->OutBaudrate = 1200; break;
|
|
case B1800: Handle->OutBaudrate = 1800; break;
|
|
case B2400: Handle->OutBaudrate = 2400; break;
|
|
case B4800: Handle->OutBaudrate = 4800; break;
|
|
case B9600: Handle->OutBaudrate = 9600; break;
|
|
case B19200: Handle->OutBaudrate = 19200; break;
|
|
case B38400: Handle->OutBaudrate = 38400; break;
|
|
case B57600: Handle->OutBaudrate = 57600; break;
|
|
case B115200: Handle->OutBaudrate = 115200; break;
|
|
case B230400: Handle->OutBaudrate = 230400; break;
|
|
default: Handle->OutBaudrate = 0 ; break;
|
|
}
|
|
|
|
// Get Data Bits
|
|
bits = getTermios.c_cflag & CSIZE;
|
|
switch (bits) {
|
|
case CS5: Handle->DataBits = 5; break;
|
|
case CS6: Handle->DataBits = 6; break;
|
|
case CS7: Handle->DataBits = 7; break;
|
|
case CS8: Handle->DataBits = 8; break;
|
|
default: Handle->DataBits = 255; break;
|
|
}
|
|
|
|
// Get Stop Bits
|
|
if (getTermios.c_cflag & CSTOPB)
|
|
Handle->StopBits = 2;
|
|
else
|
|
Handle->StopBits = 1;
|
|
|
|
// Get Parity
|
|
if (getTermios.c_iflag & (PARODD | PARENB))
|
|
Handle->Parity = ODD_PARITY;
|
|
else if (getTermios.c_iflag & PARENB)
|
|
Handle->Parity = EVEN_PARITY;
|
|
else
|
|
Handle->Parity = NO_PARITY;
|
|
|
|
// Get Flow Control
|
|
if (getTermios.c_cflag & CRTSCTS)
|
|
Handle->FlowCtrl = true;
|
|
else
|
|
Handle->FlowCtrl = false;
|
|
|
|
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;
|
|
}
|
|
//---------------------------------------------------------------------------
|