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