From 4ec1dc6cd7c3d3d7c84d060e525a86ebb712dd19 Mon Sep 17 00:00:00 2001 From: Charl Wentzel Date: Sun, 10 Dec 2017 21:39:55 +0200 Subject: [PATCH] Important Update: - Add Resolve Delay, allow time delay before resolving address Allow completion of other actions before blocking all to getaddr() - Remove Handle->KeepAlive, assume always true --- SelectableCore.cpp | 113 +++++++++++++++++++++++++++------------------ SelectableCore.h | 25 +++++----- 2 files changed, 82 insertions(+), 56 deletions(-) diff --git a/SelectableCore.cpp b/SelectableCore.cpp index a4ad0e0..2d8106a 100644 --- a/SelectableCore.cpp +++ b/SelectableCore.cpp @@ -77,6 +77,7 @@ bool CSelectableCore::LoadConfigData() char * FlowCtrlText; short Parity; short FlowCtrl; + long Delay; // Call Previous load config CFunctionCore::LoadConfigData(); @@ -96,11 +97,13 @@ bool CSelectableCore::LoadConfigData() Type = (char*)DataTree->GetStr( TempMember, "Type", "TCPclient", true ); if (!strcasecmp( Type, "Serial" )) { - Address = (char*)DataTree->GetStr( TempMember, "Port/Address", NULL ); // Get default value if ((Name = (char*)DataTree->GetStr( TempMember, "Port/Name", NULL ))) { sprintf( Path, "Address/%s/Address", Name ); Address = (char*)DataTree->GetStr( NULL, Path, Address, true ); // Get address list value } + else { + Address = (char*)DataTree->GetStr( TempMember, "Port/Address", NULL, true ); // Get default value + } SetSerialHandle( Handle, Address ); // Update configuration if specified @@ -133,36 +136,44 @@ bool CSelectableCore::LoadConfigData() } else if (!strcasecmp( Type, "LinePrinter" )) { - Address = (char*)DataTree->GetStr( TempMember, "Port/Address", NULL ); // Get default value if ((Name = (char*)DataTree->GetStr( TempMember, "Port/Name", NULL ))) { sprintf( Path, "Address/%s/Address", Name ); Address = (char*)DataTree->GetStr( NULL, Path, Address, true ); // Get address list value } + else { + Address = (char*)DataTree->GetStr( TempMember, "Port/Address", NULL, true ); // Get default value + } SetLinePrinterHandle( Handle, Address ); } else if (!strcasecmp( Type, "TCPserver" )) { - Address = (char*)DataTree->GetStr( TempMember, "Socket/Address", NULL ); // Get default Address value - Port = (char*)DataTree->GetStr( TempMember, "Socket/Port", "0" ); // Get default Port value if ((Name = (char*)DataTree->GetStr( TempMember, "Socket/Name", NULL ))) { sprintf( Path, "Address/%s/Address", Name ); Address = (char*)DataTree->GetStr( NULL, Path, Address, true ); // Get AddressList Address value sprintf( Path, "Address/%s/Port", Name ); Port = (char*)DataTree->GetStr( NULL, Path, Port, true ); // Get AddressList Port value } - SetSocketHandle( Handle, ctServer, Address, strlcase(Port), true ); // Assign values + else { + Address = (char*)DataTree->GetStr( TempMember, "Socket/Address", NULL, true ); // Get default Address value + Port = (char*)DataTree->GetStr( TempMember, "Socket/Port", "0", true ); // Get default Port value + } + Delay = DataTree->GetInt( TempMember, "Socket/ResolveDelay", 0, true ); + SetSocketHandle( Handle, ctServer, Address, strlcase(Port), Delay ); } else if (!strcasecmp( Type, "TCPclient" )) { - Address = (char*)DataTree->GetStr( TempMember, "Socket/Address", NULL ); // Get default Address value - Port = (char*)DataTree->GetStr( TempMember, "Socket/Port", "0" ); // Get default Port value if ((Name = (char*)DataTree->GetStr( TempMember, "Socket/Name", NULL ))) { sprintf( Path, "Address/%s/Address", Name ); Address = (char*)DataTree->GetStr( NULL, Path, Address, true ); // Get AddressList Address value sprintf( Path, "Address/%s/Port", Name ); Port = (char*)DataTree->GetStr( NULL, Path, Port, true ); // Get AddressList Port value } - SetSocketHandle( Handle, ctClient, Address, strlcase(Port), true ); // Assign values + else { + Address = (char*)DataTree->GetStr( TempMember, "Socket/Address", NULL, true ); // Get default Address value + Port = (char*)DataTree->GetStr( TempMember, "Socket/Port", "0", true ); // Get default Port value + } + Delay = DataTree->GetInt( TempMember, "Socket/ResolveDelay", 0, true ); + SetSocketHandle( Handle, ctClient, Address, strlcase(Port), Delay ); } else if (!strcasecmp( Type, "ForkPipe" )) { Address = (char*)DataTree->GetStr( TempMember, "Fork/ExecPath", NULL, true ); // Get default value @@ -383,7 +394,7 @@ bool CSelectableCore::SetForkPipeHandle( THandle * Handle, const char * ExecPath } //--------------------------------------------------------------------------- -bool CSelectableCore::SetSocketHandle( THandle * Handle, EConnectType Type, const char * HostName, const char * PortName, bool KeepAlive ) +bool CSelectableCore::SetSocketHandle( THandle * Handle, EConnectType Type, const char * HostName, const char * PortName, long ResolveDelay ) { // Validate if (!Handle || ((Handle->Type != ctNone) && (Handle->Type != ctServer) && (Handle->Type != ctClient) && (Handle->Type != ctRemoteClient)) || @@ -392,8 +403,8 @@ bool CSelectableCore::SetSocketHandle( THandle * Handle, EConnectType Type, con } // Set Type - Handle->Type = Type; - Handle->KeepAlive = KeepAlive; + Handle->Type = Type; + Handle->ResolveDelay = ResolveDelay; // Clear HostName & Port Name if (Handle->HostName) @@ -717,7 +728,7 @@ int CSelectableCore::OpenForkPipe( THandle * Handle ) //--------------------------------------------------------------------------- -bool CSelectableCore::ResolveAddress( THandle * Handle ) +bool CSelectableCore::ResolveAddress( THandle * Handle, bool DelayResolve ) { struct addrinfo hints; int result; @@ -762,11 +773,21 @@ bool CSelectableCore::ResolveAddress( THandle * Handle ) if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Resolving Host name [%s:%s]...", Name, Handle->Name, Handle->HostName, Handle->PortName ); + // Should address be resolved later during process() + if (DelayResolve) + { + if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Delay resolving of Host Name [%s:%s]", + Name, Handle->Name, Handle->HostName, Handle->PortName ); + ChangeState( Handle, csOpenRequest ); + return false; + } + // Resolve Host & Port Names if ((result = getaddrinfo( Handle->HostName, Handle->PortName, &hints, &(Handle->AddressList))) != 0) { if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Failed to resolve Host Name [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, gai_strerror(result) ); + ChangeState( Handle, csFailed ); return false; } @@ -780,6 +801,7 @@ bool CSelectableCore::ResolveAddress( THandle * Handle ) freeaddrinfo( Handle->AddressList ); Handle->AddressList = NULL; Handle->AddressInfo = NULL; + ChangeState( Handle, csFailed ); return false; } @@ -792,7 +814,7 @@ bool CSelectableCore::ResolveAddress( THandle * Handle ) } //--------------------------------------------------------------------------- -int CSelectableCore::OpenServerSocket( THandle * Handle ) +int CSelectableCore::OpenServerSocket( THandle * Handle, bool DelayResolve ) { // Socket options struct linger ServerLinger_opt; @@ -811,12 +833,8 @@ int CSelectableCore::OpenServerSocket( THandle * Handle ) } // Resolve Host & Port Names - if (!ResolveAddress( Handle )) - { - // Set Status - ChangeState( Handle, csFailed ); + if (!ResolveAddress( Handle, DelayResolve )) return -1; - } // Create socket if ((Handle->FD = socket( Handle->AddressInfo->ai_family, Handle->AddressInfo->ai_socktype, Handle->AddressInfo->ai_protocol )) < 0) @@ -842,11 +860,10 @@ int CSelectableCore::OpenServerSocket( THandle * Handle ) } // 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) )) + 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: Handle '%s' - Could not set KeepAlive options [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) ); @@ -953,7 +970,7 @@ int CSelectableCore::OpenRemoteClientSocket( THandle * Handle ) // Create Remote Client Handle sprintf( ClientName, "%s-%d", Handle->Name, ClientFD ); *RemoteClient = CreateHandle( ClientName, false ); - if (!SetSocketHandle( *RemoteClient, ctRemoteClient, ClientAddress, ClientPort, Handle->KeepAlive )) { + if (!SetSocketHandle( *RemoteClient, ctRemoteClient, ClientAddress, ClientPort, 0 )) { if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - TCP Server failed to configure Remote TCP Client connection (%s)", Name, Handle->Name, strerror(errno) ); return -1; } @@ -1003,7 +1020,7 @@ int CSelectableCore::OpenRemoteClientSocket( THandle * Handle ) } //--------------------------------------------------------------------------- -int CSelectableCore::OpenClientSocket( THandle * Handle ) +int CSelectableCore::OpenClientSocket( THandle * Handle, bool DelayResolve ) { // Socket options int KeepAlive_opt = 1; @@ -1020,12 +1037,8 @@ int CSelectableCore::OpenClientSocket( THandle * Handle ) if (Handle->State != csWaitingtoOpen) { // Resolve IP Address - if (!ResolveAddress( Handle )) - { - // Set Status - ChangeState( Handle, csFailed ); + if (!ResolveAddress( Handle, DelayResolve )) return -1; - } // Create File descriptor if ((Handle->FD = socket( Handle->AddressInfo->ai_family, Handle->AddressInfo->ai_socktype, Handle->AddressInfo->ai_protocol )) < 0) @@ -1043,11 +1056,10 @@ int CSelectableCore::OpenClientSocket( THandle * Handle ) 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) )) + 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: Handle '%s' - Could not set KeepAlive options [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) ); @@ -1110,7 +1122,7 @@ int CSelectableCore::OpenClientSocket( THandle * Handle ) } //--------------------------------------------------------------------------- -int CSelectableCore::Open( THandle * Handle ) +int CSelectableCore::Open( THandle * Handle, bool DelayResolve ) { int FD = -1; @@ -1131,10 +1143,10 @@ int CSelectableCore::Open( THandle * Handle ) FD = OpenForkPipe( Handle ); break; case ctServer : - FD = OpenServerSocket( Handle ); + FD = OpenServerSocket( Handle, DelayResolve ); break; case ctClient : - FD = OpenClientSocket( Handle ); + FD = OpenClientSocket( Handle, DelayResolve ); break; case ctRemoteClient : FD = OpenRemoteClientSocket( Handle ); @@ -1278,7 +1290,7 @@ bool CSelectableCore::Read( THandle * Handle ) if (Handle->Type == ctRemoteClient) { OpenRemoteClientSocket( Handle ); } else if (Handle->Type == ctClient) { - OpenClientSocket( Handle ); + OpenClientSocket( Handle, true ); } // Reset Timer (for auto-close) SetStartTime( &(Handle->LastAction) ); @@ -1378,7 +1390,7 @@ bool CSelectableCore::Write( THandle * Handle ) else if (Timeout( Handle->LastAction, Handle->ReopenDelay )) { // Attempt to re-open port - Open( Handle ); + Open( Handle, true ); } } @@ -1391,7 +1403,7 @@ bool CSelectableCore::Write( THandle * Handle ) if (Handle->Type == ctRemoteClient) { OpenRemoteClientSocket( Handle ); } else if (Handle->Type == ctClient) { - OpenClientSocket( Handle ); + OpenClientSocket( Handle, true ); } // Reset Timer (for auto-close) SetStartTime( &(Handle->LastAction) ); @@ -1623,10 +1635,14 @@ int CSelectableCore::OutputHandle( THandle * Handle, const char * Data, int Len else if (Timeout( Handle->LastAction, Handle->ReopenDelay )) { // Complete opening process - Open( Handle ); + Open( Handle, true ); // Check if Handle is open - if (Handle->State == csWaitingtoOpen) { + if (Handle->State == csOpenRequest) { + if (Log) Log->Message( LogLevel, dlHigh, "%s: Handle '%s' - Input rejected, Request to resolve (auto-managed) Handle", Name, Handle->Name ); + return 0; + } + else if (Handle->State == csWaitingtoOpen) { if (Log) Log->Message( LogLevel, dlHigh, "%s: Handle '%s' - Input rejected, Waiting to open (auto-managed) Handle", Name, Handle->Name ); return 0; } @@ -1727,11 +1743,18 @@ bool CSelectableCore::Process() while (Handle) { // Auto manage handles - if ((Handle->State != csOpen) && Handle->AutoManage && Handle->Persistent) + if ((Handle->State == csOpenRequest)) + { + // Resolve then open socket + if (Timeout( Handle->LastAction, Handle->ResolveDelay )) { + Open( Handle, false ); + } + } + else if (((Handle->State != csOpen) && Handle->AutoManage && Handle->Persistent) ) { // Try to re-open port after delay if (Timeout( Handle->LastAction, Handle->ReopenDelay )) { - Open( Handle ); + Open( Handle, false ); } } diff --git a/SelectableCore.h b/SelectableCore.h index 43bf3ee..3b87adf 100644 --- a/SelectableCore.h +++ b/SelectableCore.h @@ -20,8 +20,8 @@ typedef enum { ctNone = 0, ctSerial = 1, ctLinePrinter = 2, ctServer = 3, ctRemoteClient = 4, ctClient = 5, ctForkPipe = 6 } EConnectType; const char ConnectTypeName[][15] = { "None", "Port", "ForkPipe", "Server", "RemoteClient", "Client" }; -typedef enum { csNone = 0, csWaitingtoOpen = 1, csOpen = 2, csDataWaiting = 3, csClosed = 4, csFailed = 5 } EConnectState; -const char ConnectStateName[][15] = { "None", "WaitingToOpen", "Open", "DataWaiting", "Closed", "Failed" }; +typedef enum { csNone = 0, csOpenRequest = 1, csWaitingtoOpen = 2, csOpen = 3, csDataWaiting = 4, csClosed = 5, csFailed = 6 } EConnectState; +const char ConnectStateName[][15] = { "None", "OpenRequest", "WaitingToOpen", "Open", "DataWaiting", "Closed", "Failed" }; //--------------------------------------------------------------------------- @@ -79,8 +79,8 @@ struct SHandle { bool AutoManage; bool Persistent; timeval LastAction; - long ReopenDelay; // millisecs - long CloseTimeout; // millisecs + long ReopenDelay; // millisecs before trying to re-open socket + long CloseTimeout; // millisecs of no traffic before closing socket // Callback functions FHandleCallback StateCallback[ 6 ]; @@ -88,15 +88,18 @@ struct SHandle { // Type specific parameters char * Path; // Port (file)name or Exec path + // Fork config pid_t ChildPID; // Forked child PID + // Socket config char * HostName; // Host name or IP adddress char * PortName; // Socket port no or protocol, e.g. "80" or "HTTP" struct addrinfo * AddressList; // List of resolved IP Addresses for host name struct addrinfo * AddressInfo; // Current selected IP Address bool AddressFailed; // Indicate failure to connect to address - bool KeepAlive; // Socket keep alive + long ResolveDelay; // Delay before resolving hostname via DNS + // Serial Port config bool SerialConfig; int InBaudrate; int OutBaudrate; @@ -209,10 +212,10 @@ protected: int OpenForkPipe( THandle * Handle ); // Socket Operations - bool ResolveAddress( THandle * Handle ); - int OpenServerSocket( THandle * Handle ); + bool ResolveAddress( THandle * Handle, bool DelayResolve ); + int OpenServerSocket( THandle * Handle, bool DelayResolve ); int OpenRemoteClientSocket( THandle * Handle ); - int OpenClientSocket( THandle * Handle ); + int OpenClientSocket( THandle * Handle, bool DelayResolve ); // Mutual Operations int ReadFromFD( int FD, char * Data, int MaxLen ); @@ -266,17 +269,17 @@ public: bool SetSerialHandleConfig( THandle * Handle, int Baudrate, short DataBits, short Parity, short StopBits, short FlowCtrl, int DataWait ); bool SetLinePrinterHandle( THandle * Handle, const char * FileName ); bool SetForkPipeHandle( THandle * Handle, const char * ExecPath ); - bool SetSocketHandle( THandle * Handle, EConnectType Type, const char * HostName, const char * PortName, bool KeepAlive ); + bool SetSocketHandle( THandle * Handle, EConnectType Type, const char * HostName, const char * PortName, long ResolveDelay ); bool ClearHandle( THandle * Handle ); // FD Operations - virtual int Open( THandle * Handle ); + virtual int Open( THandle * Handle, bool DelayResolve = false ); virtual bool Close( THandle * Handle, bool QuickReopen ); virtual bool Read( THandle * Handle ); virtual bool Write( THandle * Handle ); // FD operations - inline virtual int Open( const char * HandleName ) { return (Open( GetHandle( HandleName ))); }; + inline virtual int Open( const char * HandleName, bool DelayResolve = false ) { return (Open( GetHandle( HandleName ), DelayResolve)); }; inline virtual bool Close( const char * HandleName, bool QuickReopen ) { return (Close( GetHandle( HandleName ), QuickReopen )); }; inline virtual bool Close( int FD, bool QuickReopen ) { return (Close( GetHandle( FD ), QuickReopen )); };