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