/* * SelectableCore.cpp * * Created on: 20 May 2016 * Author: wentzelc */ // Standard C/C++ Libraries #include #include #include #include #include #include #include #include #include #include #include // redA Libraries #include "ApplicationCore.h" #include "SelectableCore.h" //--------------------------------------------------------------------------- // Global Vars //extern char * ProcessName; extern CApplication * Application; //--------------------------------------------------------------------------- CSelectableCore::CSelectableCore( const char * pName, const char * pType ) : CFunctionCore( pName, pType ) { // Quick access Selector = Application->Selector; // Handles FirstHandle = NULL; } //--------------------------------------------------------------------------- CSelectableCore::~CSelectableCore() { THandle * NextHandle = NULL; // Destroy File Handles while (FirstHandle) { // Close handle if open if ((FirstHandle->State == csOpen) || (FirstHandle->State == csWaitingtoOpen)) { Close( FirstHandle, false ); } NextHandle = FirstHandle->Next; DestroyHandle( FirstHandle ); FirstHandle = NextHandle; } } //--------------------------------------------------------------------------- bool CSelectableCore::LoadConfigData() { TDataMember * TempMember; TDataMember * SerialConfig; THandle * Handle; char * Type; char * Name; char Path[100]; char * Address; char * Port; char * ParityText; char * FlowCtrlText; short Parity; short FlowCtrl; long Delay; // Call Previous load config CFunctionCore::LoadConfigData(); // Load Handles TempMember = DataTree->GetFirstChild( DataTree->GetMember( ConfigMember, "Handles", false ) ); while (TempMember) { // Check if name is valid if (!TempMember->Name || !*TempMember->Name) continue; // Create Handle and channel link Handle = CreateHandle( TempMember->Name, false ); Handle->Channel = GetChannel( DataTree->GetStr( TempMember, "Channel" ) ); Type = (char*)DataTree->GetStr( TempMember, "Type", "TCPclient", true ); if (!strcasecmp( Type, "Serial" )) { 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 if ((SerialConfig = DataTree->GetMember( TempMember, "Port/SerialConfig", false ))) { ParityText = (char*)DataTree->GetStr( SerialConfig, "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; FlowCtrlText = (char*)DataTree->GetStr( SerialConfig, "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; Handle->SerialConfig = true; SetSerialHandleConfig( Handle, DataTree->GetInt( SerialConfig, "BaudRate", 19200, true ), DataTree->GetInt( SerialConfig, "DataBits", 8, true ), Parity, DataTree->GetInt( SerialConfig, "StopBits", 1, true ), FlowCtrl, DataTree->GetInt( SerialConfig, "DataWait", 0, true )); } } else if (!strcasecmp( Type, "LinePrinter" )) { if ((Name = (char*)DataTree->GetStr( TempMember, "Port/Name", NULL ))) { sprintf( Path, "Address/%s/Address", Name ); Address = (char*)DataTree->GetStr( NULL, Path, NULL, 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" )) { if ((Name = (char*)DataTree->GetStr( TempMember, "Socket/Name", NULL ))) { sprintf( Path, "Address/%s/Address", Name ); Address = (char*)DataTree->GetStr( NULL, Path, NULL, true ); // Get AddressList Address value sprintf( Path, "Address/%s/Port", Name ); Port = (char*)DataTree->GetStr( NULL, Path, "0", true ); // Get AddressList Port value } 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" )) { if ((Name = (char*)DataTree->GetStr( TempMember, "Socket/Name", NULL ))) { sprintf( Path, "Address/%s/Address", Name ); Address = (char*)DataTree->GetStr( NULL, Path, NULL, true ); // Get AddressList Address value sprintf( Path, "Address/%s/Port", Name ); Port = (char*)DataTree->GetStr( NULL, Path, "0", true ); // Get AddressList Port value } 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 SetForkPipeHandle( Handle, Address ); } // Set Auto Mange SetAutoManage( Handle, DataTree->GetBool( TempMember, "AutoManage/Enabled", true, true ), DataTree->GetBool( TempMember, "AutoManage/Persistent", false, true ), DataTree->GetInt( TempMember, "AutoManage/ReopenDelay", 2000, true ), DataTree->GetInt( TempMember, "AutoManage/CloseTimeout", 2000, true )); // Input buffer SetInBuffer( Handle, DataTree->GetInt( TempMember, "InputBuffer/Size", 0 ), DataTree->GetInt( TempMember, "InputBuffer/Timeout", 250 ), DataTree->GetStr( TempMember, "InputBuffer/Marker", "" ), DataTree->GetInt( TempMember, "InputBuffer/MarkerLen", 0 ) ); SetOutBuffer( Handle, DataTree->GetInt( TempMember, "OutputBuffer/Size", 0 ) ); // Next TempMember = DataTree->GetNextChild( TempMember ); } return true; } //--------------------------------------------------------------------------- THandle * CSelectableCore::CreateHandle( const char * HandleName, bool CreateChannel ) { THandle ** Handle = NULL; // Find Handle by Name or get end of list Handle = &FirstHandle; while ( *Handle && strcmp( HandleName, (*Handle)->Name )) Handle = &((*Handle)->Next); // Create if necessary if (!*Handle) { // Create File handle at end of list *Handle = (THandle*)calloc( 1, sizeof(THandle) ); // Set name if (HandleName) { (*Handle)->Name = (char*)malloc( strlen(HandleName)+1 ); strcpy( (*Handle)->Name, HandleName ); } // Set File Descriptor (*Handle)->FD = -1; // Log event if (Log) Log->Message( LogLevel, dlMedium, "%s: 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( LogLevel, 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->HostName) free( Handle->HostName ); if (Handle->PortName) free( Handle->PortName ); if (Handle->AddressInfo) freeaddrinfo( Handle->AddressList ); // Destroy Buffers if (Handle->InBuffer) delete Handle->InBuffer; if (Handle->OutBuffer) delete Handle->OutBuffer; // Clear Input Markers if (Handle->InMarker) free( Handle->InMarker ); // Destroy Pointer free( Handle ); return true; } //--------------------------------------------------------------------------- bool CSelectableCore::SetSerialHandle( THandle * Handle, const char * FileName ) { // Validate if (!Handle || ((Handle->Type != ctNone) && (Handle->Type != ctSerial)) || !FileName) { return false; } // Set Type Handle->Type = ctSerial; // 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( LogLevel, dlMedium, "%s: Handle '%s' - Set as Port [%s]", 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; // 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( LogLevel, 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( LogLevel, dlMedium, "%s: Handle '%s' - Set as ForkPipe [%s]", Name, Handle->Name, ExecPath ); return true; } //--------------------------------------------------------------------------- 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)) || !((Type == ctServer) || (Type == ctClient) || (Type == ctRemoteClient)) || !HostName || !PortName ) { return false; } // Set Type Handle->Type = Type; Handle->ResolveDelay = ResolveDelay; // 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 = (char*)malloc( strlen(HostName)+1 ); strcpy( Handle->HostName, HostName ); Handle->PortName = (char*)malloc( strlen(PortName)+1 ); strcpy( Handle->PortName, PortName ); Handle->AddressList = NULL; Handle->AddressInfo = NULL; // Log event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Set as %s [%s:%s]", Name, Handle->Name, ConnectTypeName[Type], HostName, PortName ); 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: 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, bool Persistent, int ReopenDelay, int CloseTimeout ) { // Validate if (!Handle) { return false; } // Set params Handle->AutoManage = AutoManage; Handle->Persistent = Persistent; Handle->ReopenDelay = ReopenDelay; Handle->CloseTimeout = CloseTimeout; return true; } //--------------------------------------------------------------------------- bool CSelectableCore::SetInBuffer( THandle * Handle, int InBufSize, int InTimeout, const char * InMarker, int InMarkerLen ) { // Validate if (!Handle) { return false; } // Input Buffer if (Handle->InBuffer) { delete Handle->InBuffer; Handle->InBuffer = NULL; } if (InBufSize) { Handle->InBuffer = (CRollingBuffer*) new CRollingBuffer( InBufSize ); } // Set Input Timeout Handle->InTimeout = InTimeout; // Set Input Markers if (InMarkerLen && InMarker) { Handle->InMarkerLen = InMarkerLen; Handle->InMarker = (char *)malloc( InMarkerLen+1 ); memcpy( Handle->InMarker, InMarker, InMarkerLen ); Handle->InMarker[InMarkerLen] = 0; } return true; } //--------------------------------------------------------------------------- bool CSelectableCore::SetOutBuffer( THandle * Handle, int OutBufSize ) { // Validate if (!Handle) { return false; } // Output Buffer if (Handle->OutBuffer) { delete Handle->OutBuffer; Handle->OutBuffer = NULL; } if (OutBufSize) { Handle->OutBuffer = (CRollingBuffer*) new CRollingBuffer( OutBufSize ); } return true; } //--------------------------------------------------------------------------- int CSelectableCore::OpenSerialPort( 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( LogLevel, dlMedium, "%s: Handle '%s' - Serial 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( LogLevel, dlMedium, "%s: Handle '%s' - Could not open Serial Port [%s]", Name, Handle->Name, Handle->Path ); return -1; } // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Serial Port opened [%s]", Name, Handle->Name, Handle->Path ); // Update port configuration if (Handle->SerialConfig) WriteSerialConfig( Handle ); else ReadSerialConfig( Handle ); if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Serial Port config, IB:%d, OB:%d, D:%d, P:%d, S:%d, F:%d", 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, this ); } // Set state ChangeState( Handle, csOpen ); return Handle->FD; } //--------------------------------------------------------------------------- int CSelectableCore::OpenLinePrinterPort( 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( LogLevel, dlMedium, "%s: Handle '%s' - Printer 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( LogLevel, dlMedium, "%s: Handle '%s' - Could not open Printer Port [%s]", Name, Handle->Name, Handle->Path ); return -1; } // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Printer Port opened [%s]", Name, Handle->Name, Handle->Path ); // Add to Select Lists if (Selector) { Selector->Add( Handle->FD, true, false, this ); } // Set state ChangeState( Handle, csOpen ); return Handle->FD; } //--------------------------------------------------------------------------- int CSelectableCore::OpenForkPipe( THandle * Handle ) { int pipefd[2]; int newfd; 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( LogLevel, dlMedium, "%s: Handle '%s' - Invalid path for Exec", Name, Handle->Name ); return -1; } // Create Pipe if ((pipe( pipefd ) == -1) || (pipefd[0] == -1) || (pipefd[1] == -1)) { // Log event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Could not open Pipe (%s)", Name, Handle->Name, strerror(errno) ); 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( LogLevel, dlMedium, "%s: Handle '%s' - Could not Fork process, %s", Name, Handle->Name, strerror(errno) ); 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( LogLevel, dlMedium, "%s: Handle '%s' - Process forked successfully", 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: Handle '%s' - Stdin redirect failed on forked process", 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: Handle '%s' - Exec on forked process failed", Name, Handle->Name ); exit( EXIT_FAILURE ); } // Add to Select Lists if (Selector) { Selector->Add( Handle->FD, false, true, this ); } // Set state ChangeState( Handle, csOpen ); return Handle->FD; } //--------------------------------------------------------------------------- bool CSelectableCore::ResolveAddress( THandle * Handle, bool DelayResolve ) { struct addrinfo hints; int result; // 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: Handle '%s' - Use next resolved Address [%s:%s]->[%s:%u]", 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 memset( &hints, 0, sizeof hints ); hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6 hints.ai_socktype = SOCK_STREAM; 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; } // Select first address, skip "0.0.0.0" Handle->AddressInfo = Handle->AddressList; 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: Handle '%s' - Failed to resolve Host Name [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, gai_strerror(result) ); freeaddrinfo( Handle->AddressList ); Handle->AddressList = NULL; Handle->AddressInfo = NULL; ChangeState( Handle, csFailed ); return false; } // Return address if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Host name resolved [%s:%s]->[%s:%u]", 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; } //--------------------------------------------------------------------------- int CSelectableCore::OpenServerSocket( THandle * Handle, bool DelayResolve ) { // 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; } // Resolve Host & Port Names 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) { // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Failed to create TCP Server socket [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, 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( LogLevel, dlMedium, "%s: Handle '%s' - Could not set socket options [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) ); // Set state ChangeState( Handle, csFailed ); return -1; } // 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: Handle '%s' - Could not set KeepAlive options [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, 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, Handle->AddressInfo->ai_addr, Handle->AddressInfo->ai_addrlen ) < 0) { // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Failed to bind TCP Server socket [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) ); // Set state close( Handle->FD ); Handle->FD = -1; ChangeState( Handle, csFailed ); Handle->AddressFailed = true; return -1; }; // Create que for 5 connections if (listen( Handle->FD, 5 ) < 0) { // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Failed to listen on TCP Server socket [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) ); // Set state close( Handle->FD ); Handle->FD = -1; ChangeState( Handle, csFailed ); Handle->AddressFailed = true; return -1; }; // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - TCP Server binded and listening [%s:%s]", Name, Handle->Name, Handle->HostName, Handle->PortName ); // Add to Select Lists if (Selector) { Selector->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[50]; char ClientPort[20]; 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( LogLevel, dlMedium, "%s: Handle '%s' - TCP Server failed to accept blocking connection (%s)", Name, Handle->Name, strerror(errno) ); } else { if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - TCP 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) ); 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, 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; } // 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: Handle '%s' - TCP Server accepted Remote TCP Client connection [%s]", Name, Handle->Name, ClientAddress ); // Add to Select Lists if (Selector) { Selector->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( LogLevel, dlMedium, "%s: Handle '%s' - Remote TCP Client connection open [%s]", Name, Handle->Name, Handle->HostName ); // Update state ChangeState( Handle, csOpen ); return Handle->FD; } } return -1; } //--------------------------------------------------------------------------- int CSelectableCore::OpenClientSocket( THandle * Handle, bool DelayResolve ) { // 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) { // Resolve IP Address 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) { // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Failed to create TCP Client socket [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, 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 ((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) ); // Set State close( Handle->FD ); Handle->FD = -1; ChangeState( Handle, csFailed ); return -1; } } // Try to connect to address if (!connect( Handle->FD, Handle->AddressInfo->ai_addr, Handle->AddressInfo->ai_addrlen )) { if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - TCP Client connected [%s:%s]", Name, Handle->Name, Handle->HostName, Handle->PortName ); // Add to Select Lists if (Selector) { Selector->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( LogLevel, dlMedium, "%s: Handle '%s' - TCP Client waiting to connect [%s:%s] (%s)", Name, Handle->Name, Handle->HostName, Handle->PortName, strerror(errno) ); // Add to Select Lists if (Selector) { Selector->Add( Handle->FD, true, true, this ); } // Set status ChangeState( Handle, csWaitingtoOpen ); return Handle->FD; } else { // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - TCP Client could not connect [%s:%s] (%s)", 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 ); ChangeState( Handle, csFailed ); Handle->AddressFailed = true; // Reset Handle Handle->FD = -1; return -1; } } //--------------------------------------------------------------------------- int CSelectableCore::Open( THandle * Handle, bool DelayResolve ) { int FD = -1; // Validate if (!Handle) { return -1; } // Open correctly switch (Handle->Type) { case ctSerial : FD = OpenSerialPort( Handle ); break; case ctLinePrinter : FD = OpenLinePrinterPort( Handle ); break; case ctForkPipe : FD = OpenForkPipe( Handle ); break; case ctServer : FD = OpenServerSocket( Handle, DelayResolve ); break; case ctClient : FD = OpenClientSocket( Handle, DelayResolve ); break; case ctRemoteClient : FD = OpenRemoteClientSocket( Handle ); break; default: FD = -1; } // Set timer (for re-open or auto-close) SetStartTime( &Handle->LastAction ); return FD; }; //--------------------------------------------------------------------------- // 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 == ctServer) { 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; } } } // Close Handle Fail = (close( Handle->FD ))? true : false; ChangeState( Handle, ((Fail)? csFailed : csClosed) ); // Start timer (for re-open) if (QuickReopen) ClearStartTime( &(Handle->LastAction) ); else SetStartTime( &(Handle->LastAction) ); // Show action switch (Handle->Type) { case ctSerial: // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Serial Port %s [%s]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Path ); break; case ctLinePrinter: // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Printer Port %s [%s]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->Path ); break; case ctForkPipe: // Log Event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Forked Pipe %s", Name, Handle->Name, ((Fail)? "failed" : "closed")); break; case ctServer: if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - TCP Server %s [%s:%s]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->HostName, Handle->PortName ); break; case ctRemoteClient: if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Remote TCP Client connection %s [%s]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->HostName ); break; case ctClient: if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - TCP Client connection %s [%s:%s]", Name, Handle->Name, ((Fail)? "failed" : "closed"), Handle->HostName, Handle->PortName ); break; case ctNone: default: if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - %s, invalid Handle type", Name, Handle->Name, ((Fail)? "failed" : "closed") ); break; }; // Remove from Select List if (!Fail && Selector) { Selector->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( LogLevel, 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) { return false; } // Reset Timer SetStartTime( &(Handle->LastAction) ); // Add to Select Lists if (Selector) { Selector->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, true ); } // 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: Handle '%s' - Data waiting error (%s)", Name, Handle->Name, strerror(errno) ); // Close Handle Close( Handle, false ); // Destroy Client if (Handle->Type == ctRemoteClient) { RemoveHandle( Handle ); } return false; } } 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: Handle '%s' - Data waiting error (%s)", 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: Handle '%s' - Data waiting error (%s)", 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 && (BytesRead = Handle->InBuffer->ReadFromFD( Handle->FD, BytesWaiting ))) { // Process Buffer 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, true ); } } // Log Ready for Write Event if (Log) Log->Message( LogLevel, 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, true ); } // 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 to FD directly from output buffer if ((BytesWritten = Handle->OutBuffer->WriteToFD( Handle->FD ))) { if (LogLevel >= dlHigh) { // Show event Len = Handle->OutBuffer->Peek( &Data ); if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s: Handle '%s' - OUT:", Name, Handle->Name ); } // Update Buffer Handle->OutBuffer->Clear( BytesWritten ); // Reset Timer SetStartTime( &(Handle->LastAction) ); } // Check if Buffer emtpy if (!Handle->OutBuffer->Len()) { // Add to Select Write list if (Selector) { Selector->Remove( Handle->FD, false, true ); } } } else { // No Output buffer to write from, so remove from Write list if (Selector) { Selector->Remove( Handle->FD, false, true ); } } return true; } return false; } //--------------------------------------------------------------------------- bool CSelectableCore::ProcessInputBuffer( THandle * Handle, bool Force ) { int Pos = 0; int Len = 0; char * Data = NULL; // Check if buffered data if (!Handle || !Handle->InBuffer || !Handle->InBuffer->Len()) { return false; } // Check if forced processed if (Force || !Handle->InMarkerLen) { // Show Packet Len = Handle->InBuffer->Peek( &Data ); if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s: Handle '%s' - IN-T:", 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+Handle->InMarkerLen ); if (Log) Log->Output( LogLevel, dlHigh, LogOutput, 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 ) { TChannel * Channel = NULL; THandle * Handle = NULL; int HandleCount = 0; int TempWritten = 0; int BytesWritten = 0; // Validate if (!ChannelName || !Data) { return 0; } // Get Channel if (!(Channel = GetChannel( ChannelName ))) { // Channel not found if (Log) Log->Message( LogLevel, dlHigh, "%s: Channel '%s' - Input rejected, Channel not found", Name, ChannelName ); return 0; } else if (!Channel->InputEnabled) { // Channel disabled if (Log) Log->Message( LogLevel, dlHigh, "%s: Channel '%s' - Input rejected, Channel input disabled", Name, ChannelName ); return 0; } // Log event if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s: Channel '%s' - IN:", Name, ChannelName ); // Find Linked handle Handle = FirstHandle; while( Handle ) { if (Handle->Channel && !strcasecmp( ChannelName, Handle->Channel->Name )) { // Input to Handle TempWritten = OutputHandle( Handle, Data, Len ); BytesWritten = (TempWritten > BytesWritten)? TempWritten : BytesWritten; HandleCount++; } Handle = Handle->Next; } if (!HandleCount) { // Handle not found if (Log) Log->Message( LogLevel, dlHigh, "%s: Channel '%s' - Input rejected, No Handles not found", Name, ChannelName ); return 0; } return BytesWritten; } //--------------------------------------------------------------------------- int CSelectableCore::OutputHandle( THandle * Handle, const char * Data, int Len ) { THandle * ChildHandle = NULL; int BytesWritten = 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: Handle '%s' - Input rejected, Handle not Open (not auto-managed)", Name, Handle->Name ); return 0; } else if (Timeout( Handle->LastAction, Handle->ReopenDelay )) { // Complete opening process Open( Handle, true ); // Check if Handle is open if (Handle->State == csOpenRequest) { if (Log) Log->Message( LogLevel, dlHigh, "%s: 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; } else if (Handle->State != csOpen) { if (Log) Log->Message( LogLevel, dlHigh, "%s: Handle '%s' - Input rejected, (auto-managed) Handle failed to Open", Name, Handle->Name ); return 0; } } else { if (Log) Log->Message( LogLevel, dlHigh, "%s: Handle '%s' - Input rejected, Retry (auto-managed) Handle re-open in %d ms", Name, Handle->Name, TimeLeft( Handle->LastAction, Handle->ReopenDelay) ); return 0; } } // Check packet length if (Len == -1) { Len = strlen( Data ); } 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( true, Data, Len ); // Add to select write list if (BytesWritten && Selector) { Selector->Add( ChildHandle->FD, false, true, this ); } } else { // Show event if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s: Handle '%s' - OUT:", Name, ChildHandle->Name ); // Write directly to handle BytesWritten = WriteToFD( ChildHandle->FD, Data, Len, true ); // Reset Timer SetStartTime( &(Handle->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, this ); } } else { // Show event if (Log) Log->Output( LogLevel, dlHigh, LogOutput, Data, Len, "%s: Handle '%s' - OUT:", Name, Handle->Name ); // Write directly to handle if ((BytesWritten = WriteToFD( Handle->FD, Data, Len, true ))) { // Reset Timer SetStartTime( &(Handle->LastAction) ); } } } return BytesWritten; } //--------------------------------------------------------------------------- bool CSelectableCore::Process() { THandle * Handle = NULL; // Check all handles Handle = FirstHandle; while (Handle) { // Auto manage handles if ((Handle->State == csOpenRequest)) { // Resolve then open socket if (Timeout( Handle->LastAction, Handle->ResolveDelay )) { Open( Handle, false ); } } else if (((Handle->State != csOpen) && Handle->AutoManage && Handle->Persistent) ) { // Try to re-open port after delay if (Timeout( Handle->LastAction, Handle->ReopenDelay )) { Open( Handle, false ); } } // Check Input buffers if (Handle->InBuffer && (Handle->InBuffer->Len() > 0)) { // Check duration since last PortIn if (Timeout( Handle->InStart, Handle->InTimeout )) { // Process Input ProcessInputBuffer( Handle, true ); // Reset timer ClearStartTime( &(Handle->InStart) ); } } // Check for auto close (but not on servers) if ((Handle->State == csOpen) && (Handle->Type != ctServer) && Handle->AutoManage && !Handle->Persistent) { // Close port after timeout if (Timeout( Handle->LastAction, Handle->CloseTimeout )) { Close( Handle, true ); } } Handle = Handle->Next; } return true; } //--------------------------------------------------------------------------- // 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: Handle '%s' - Port not configured", Name, Handle->Name ); return false; } // Port configured // Log event if (Log) Log->Message( LogLevel, dlMedium, "%s: Handle '%s' - Port configured", 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; } //---------------------------------------------------------------------------