void BlockingTCPAcceptor::destroy() { SOCKET sock; { Lock guard(_mutex); if(_destroyed) return; _destroyed = true; sock = _serverSocketChannel; _serverSocketChannel = INVALID_SOCKET; } if(sock!=INVALID_SOCKET) { char ipAddrStr[48]; ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr)); LOG(logLevelDebug, "Stopped accepting connections at %s.", ipAddrStr); switch(epicsSocketSystemCallInterruptMechanismQuery()) { case esscimqi_socketBothShutdownRequired: shutdown(sock, SHUT_RDWR); epicsSocketDestroy(sock); _thread.exitWait(); break; case esscimqi_socketSigAlarmRequired: LOG(logLevelError, "SigAlarm close not implemented for this target\n"); case esscimqi_socketCloseRequired: epicsSocketDestroy(sock); _thread.exitWait(); break; } } }
void AbstractResponseHandler::handleResponse(osiSockAddr* responseFrom, Transport::shared_pointer const & /*transport*/, int8 version, int8 command, size_t payloadSize, ByteBuffer* payloadBuffer) { if(_debugLevel >= 3) { // TODO make a constant of sth (0 - off, 1 - debug, 2 - more/trace, 3 - messages) char ipAddrStr[48]; ipAddrToDottedIP(&responseFrom->ia, ipAddrStr, sizeof(ipAddrStr)); ostringstream prologue; prologue<<"Message [0x"<<hex<<(int)command<<", v0x"<<hex; prologue<<(int)version<<"] received from "<<ipAddrStr; hexDump(prologue.str(), _description, (const int8*)payloadBuffer->getArray(), payloadBuffer->getPosition(), static_cast<int>(payloadSize)); } }
/* * removeDuplicateAddresses () */ extern "C" void epicsShareAPI removeDuplicateAddresses ( ELLLIST *pDestList, ELLLIST *pSrcList, int silent ) { ELLNODE *pRawNode; while ( (pRawNode = ellGet ( pSrcList ) ) ) { STATIC_ASSERT ( offsetof (osiSockAddrNode, node) == 0 ); osiSockAddrNode *pNode = reinterpret_cast <osiSockAddrNode *> ( pRawNode ); osiSockAddrNode *pTmpNode; if ( pNode->addr.sa.sa_family == AF_INET ) { pTmpNode = (osiSockAddrNode *) ellFirst (pDestList); // X aCC 749 while ( pTmpNode ) { if (pTmpNode->addr.sa.sa_family == AF_INET) { if ( pNode->addr.ia.sin_addr.s_addr == pTmpNode->addr.ia.sin_addr.s_addr && pNode->addr.ia.sin_port == pTmpNode->addr.ia.sin_port ) { if ( ! silent ) { char buf[64]; ipAddrToDottedIP ( &pNode->addr.ia, buf, sizeof (buf) ); fprintf ( stderr, "Warning: Duplicate EPICS CA Address list entry \"%s\" discarded\n", buf ); } free (pNode); pNode = NULL; break; } } pTmpNode = (osiSockAddrNode *) ellNext (&pTmpNode->node); // X aCC 749 } if (pNode) { ellAdd (pDestList, &pNode->node); } } else { ellAdd (pDestList, &pNode->node); } } }
SOCKET BlockingTCPConnector::tryConnect(osiSockAddr& address, int tries) { char strBuffer[64]; ipAddrToDottedIP(&address.ia, strBuffer, sizeof(strBuffer)); for(int tryCount = 0; tryCount<tries; tryCount++) { LOG(logLevelDebug, "Opening socket to PVA server %s, attempt %d.", strBuffer, tryCount+1); SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (socket == INVALID_SOCKET) { epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); std::ostringstream temp; temp<<"Socket create error: "<<strBuffer; THROW_EXCEPTION2(std::runtime_error, temp.str()); } else { // TODO: use non-blocking connect() to have controllable timeout if(::connect(socket, &address.sa, sizeof(sockaddr))==0) { return socket; } else { epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); char saddr[32]; sockAddrToDottedIP(&address.sa, saddr, sizeof(saddr)); epicsSocketDestroy (socket); std::ostringstream temp; temp<<"error connecting to "<<saddr<<" : "<<strBuffer; throw std::runtime_error(temp.str()); } } } return INVALID_SOCKET; }
/* * cas_send_bs_msg() * * (channel access server send message) */ void cas_send_bs_msg ( struct client *pclient, int lock_needed ) { int status; if ( CASDEBUG > 2 && pclient->send.stk ) { errlogPrintf ( "CAS: Sending a message of %d bytes\n", pclient->send.stk ); } if ( pclient->disconnect ) { if ( CASDEBUG > 2 ) { errlogPrintf ( "CAS: msg Discard for sock %d addr %x\n", pclient->sock, (unsigned) pclient->addr.sin_addr.s_addr ); } pclient->send.stk = 0u; return; } if ( lock_needed ) { SEND_LOCK ( pclient ); } while ( pclient->send.stk && ! pclient->disconnect ) { status = send ( pclient->sock, pclient->send.buf, pclient->send.stk, 0 ); if ( status >= 0 ) { unsigned transferSize = (unsigned) status; if ( transferSize >= pclient->send.stk ) { pclient->send.stk = 0; epicsTimeGetCurrent ( &pclient->time_at_last_send ); break; } else { unsigned bytesLeft = pclient->send.stk - transferSize; memmove ( pclient->send.buf, &pclient->send.buf[transferSize], bytesLeft ); pclient->send.stk = bytesLeft; } } else { int causeWasSocketHangup = 0; int anerrno = SOCKERRNO; char buf[64]; if ( pclient->disconnect ) { pclient->send.stk = 0u; break; } if ( anerrno == SOCK_EINTR ) { continue; } if ( anerrno == SOCK_ENOBUFS ) { errlogPrintf ( "CAS: Out of network buffers, retrying send in 15 seconds\n" ); epicsThreadSleep ( 15.0 ); continue; } ipAddrToDottedIP ( &pclient->addr, buf, sizeof(buf) ); if ( anerrno == SOCK_ECONNABORTED || anerrno == SOCK_ECONNRESET || anerrno == SOCK_EPIPE || anerrno == SOCK_ETIMEDOUT ) { causeWasSocketHangup = 1; } else { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: TCP send to %s failed - %s\n", buf, sockErrBuf); } pclient->disconnect = TRUE; pclient->send.stk = 0u; /* * wakeup the receive thread */ if ( ! causeWasSocketHangup ) { enum epicsSocketSystemCallInterruptMechanismQueryInfo info = epicsSocketSystemCallInterruptMechanismQuery (); switch ( info ) { case esscimqi_socketCloseRequired: if ( pclient->sock != INVALID_SOCKET ) { epicsSocketDestroy ( pclient->sock ); pclient->sock = INVALID_SOCKET; } break; case esscimqi_socketBothShutdownRequired: { int status = shutdown ( pclient->sock, SHUT_RDWR ); if ( status ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ("CAS: Socket shutdown error - %s\n", sockErrBuf ); } } break; case esscimqi_socketSigAlarmRequired: epicsSignalRaiseSigAlarm ( pclient->tid ); break; default: break; }; break; } } } if ( lock_needed ) { SEND_UNLOCK(pclient); } DLOG ( 3, ( "------------------------------\n\n" ) ); return; }
/* * cas_send_dg_msg() * * (channel access server send udp message) */ void cas_send_dg_msg ( struct client * pclient ) { int status; int sizeDG; char * pDG; caHdr * pMsg; if ( CASDEBUG > 2 && pclient->send.stk ) { errlogPrintf ( "CAS: Sending a udp message of %d bytes\n", pclient->send.stk ); } SEND_LOCK ( pclient ); if ( pclient->send.stk <= sizeof (caHdr) ) { SEND_UNLOCK(pclient); return; } pDG = pclient->send.buf; pMsg = ( caHdr * ) pDG; sizeDG = pclient->send.stk; assert ( ntohs ( pMsg->m_cmmd ) == CA_PROTO_VERSION ); if ( CA_V411 ( pclient->minor_version_number ) ) { pMsg->m_cid = htonl ( pclient->seqNoOfReq ); pMsg->m_dataType = htons ( sequenceNoIsValid ); } else { pDG += sizeof (caHdr); sizeDG -= sizeof (caHdr); } status = sendto ( pclient->sock, pDG, sizeDG, 0, (struct sockaddr *)&pclient->addr, sizeof(pclient->addr) ); if ( status >= 0 ) { if ( status >= sizeDG ) { epicsTimeGetCurrent ( &pclient->time_at_last_send ); } else { errlogPrintf ( "CAS: System failed to send entire udp frame?\n" ); } } else { char sockErrBuf[64]; char buf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); ipAddrToDottedIP ( &pclient->addr, buf, sizeof(buf) ); errlogPrintf( "CAS: UDP send to %s failed - %s\n", buf, sockErrBuf); } pclient->send.stk = 0u; /* * add placeholder for the first version message should it be needed */ rsrv_version_reply ( pclient ); SEND_UNLOCK(pclient); DLOG ( 3, ( "------------------------------\n\n" ) ); return; }
/* * create_tcp_client () */ struct client *create_tcp_client ( SOCKET sock ) { int status; struct client *client; int intTrue = TRUE; osiSocklen_t addrSize; unsigned priorityOfEvents; /* socket passed in is destroyed here if unsuccessful */ client = create_client ( sock, IPPROTO_TCP ); if ( ! client ) { return NULL; } /* * see TCP(4P) this seems to make unsolicited single events much * faster. I take care of queue up as load increases. */ status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, (char *) &intTrue, sizeof (intTrue) ); if (status < 0) { errlogPrintf ( "CAS: TCP_NODELAY option set failed\n" ); destroy_client ( client ); return NULL; } /* * turn on KEEPALIVE so if the client crashes * this task will find out and exit */ status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &intTrue, sizeof (intTrue) ); if ( status < 0 ) { errlogPrintf ( "CAS: SO_KEEPALIVE option set failed\n" ); destroy_client ( client ); return NULL; } /* * some concern that vxWorks will run out of mBuf's * if this change is made * * joh 11-10-98 */ #if 0 /* * set TCP buffer sizes to be synergistic * with CA internal buffering */ i = MAX_MSG_SIZE; status = setsockopt ( sock, SOL_SOCKET, SO_SNDBUF, (char *) &i, sizeof (i) ); if (status < 0) { errlogPrintf ( "CAS: SO_SNDBUF set failed\n" ); destroy_client ( client ); return NULL; } i = MAX_MSG_SIZE; status = setsockopt ( sock, SOL_SOCKET, SO_RCVBUF, (char *) &i, sizeof (i) ); if (status < 0) { errlogPrintf ( "CAS: SO_RCVBUF set failed\n" ); destroy_client ( client ); return NULL; } #endif addrSize = sizeof ( client->addr ); status = getpeername ( sock, (struct sockaddr *)&client->addr, &addrSize ); if ( status < 0 ) { epicsPrintf ("CAS: peer address fetch failed\n"); destroy_tcp_client (client); return NULL; } client->evuser = (struct event_user *) db_init_events (); if ( ! client->evuser ) { errlogPrintf ("CAS: unable to init the event facility\n"); destroy_tcp_client (client); return NULL; } status = db_add_extra_labor_event ( client->evuser, rsrv_extra_labor, client ); if (status != DB_EVENT_OK) { errlogPrintf("CAS: unable to setup the event facility\n"); destroy_tcp_client (client); return NULL; } { epicsThreadBooleanStatus tbs; tbs = epicsThreadHighestPriorityLevelBelow ( epicsThreadPriorityCAServerLow, &priorityOfEvents ); if ( tbs != epicsThreadBooleanStatusSuccess ) { priorityOfEvents = epicsThreadPriorityCAServerLow; } } status = db_start_events ( client->evuser, "CAS-event", NULL, NULL, priorityOfEvents ); if ( status != DB_EVENT_OK ) { errlogPrintf ( "CAS: unable to start the event facility\n" ); destroy_tcp_client ( client ); return NULL; } /* * add first version message should it be needed */ rsrv_version_reply ( client ); if ( CASDEBUG > 0 ) { char buf[64]; ipAddrToDottedIP ( &client->addr, buf, sizeof(buf) ); errlogPrintf ( "CAS: conn req from %s\n", buf ); } return client; }
/* * log_one_client () */ static void log_one_client (struct client *client, unsigned level) { char *pproto; double send_delay; double recv_delay; char *state[] = {"up", "down"}; epicsTimeStamp current; char clientHostName[256]; ipAddrToDottedIP (&client->addr, clientHostName, sizeof(clientHostName)); if(client->proto == IPPROTO_UDP){ pproto = "UDP"; } else if(client->proto == IPPROTO_TCP){ pproto = "TCP"; } else{ pproto = "UKN"; } epicsTimeGetCurrent(¤t); send_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_send); recv_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_recv); printf ( "%s %s(%s): User=\"%s\", V%u.%u, %d Channels, Priority=%u\n", pproto, clientHostName, client->pHostName ? client->pHostName : "", client->pUserName ? client->pUserName : "", CA_MAJOR_PROTOCOL_REVISION, client->minor_version_number, ellCount(&client->chanList) + ellCount(&client->chanPendingUpdateARList), client->priority ); if ( level >= 1 ) { printf ("\tTask Id=%p, Socket FD=%d\n", (void *) client->tid, client->sock); printf( "\tSecs since last send %6.2f, Secs since last receive %6.2f\n", send_delay, recv_delay); printf( "\tUnprocessed request bytes=%u, Undelivered response bytes=%u\n", client->recv.cnt - client->recv.stk, client->send.stk ); printf( "\tState=%s%s%s\n", state[client->disconnect?1:0], client->send.type == mbtLargeTCP ? " jumbo-send-buf" : "", client->recv.type == mbtLargeTCP ? " jumbo-recv-buf" : ""); } if ( level >= 2u ) { unsigned bytes_reserved = 0; bytes_reserved += sizeof(struct client); bytes_reserved += countChanListBytes ( client, & client->chanList ); bytes_reserved += countChanListBytes ( client, & client->chanPendingUpdateARList ); printf( "\t%d bytes allocated\n", bytes_reserved); showChanList ( client, & client->chanList ); showChanList ( client, & client->chanPendingUpdateARList ); printf("\n"); } if ( level >= 3u ) { printf( "\tSend Lock\n"); epicsMutexShow(client->lock,1); printf( "\tPut Notify Lock\n"); epicsMutexShow (client->putNotifyLock,1); printf( "\tAddress Queue Lock\n"); epicsMutexShow (client->chanListLock,1); printf( "\tEvent Queue Lock\n"); epicsMutexShow (client->eventqLock,1); printf( "\tBlock Semaphore\n"); epicsEventShow (client->blockSem,1); } }
/* * Read from the TCP port */ static asynStatus readIt(void *drvPvt, asynUser *pasynUser, char *data, size_t maxchars,size_t *nbytesTransfered,int *gotEom) { ttyController_t *tty = (ttyController_t *)drvPvt; int thisRead; int readPollmsec; int reason = 0; epicsTimeStamp startTime; epicsTimeStamp endTime; asynStatus status = asynSuccess; assert(tty); asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s read.\n", tty->IPDeviceName); if (tty->fd == INVALID_SOCKET) { if (tty->flags & FLAG_CONNECT_PER_TRANSACTION) { if ((status = connectIt(drvPvt, pasynUser)) != asynSuccess) return status; } else { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, "%s disconnected:", tty->IPDeviceName); return asynError; } } if (maxchars <= 0) { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, "%s maxchars %d. Why <=0?",tty->IPDeviceName,(int)maxchars); return asynError; } readPollmsec = (int) (pasynUser->timeout * 1000.0); if (readPollmsec == 0) readPollmsec = 1; if (readPollmsec < 0) readPollmsec = -1; #ifdef USE_SOCKTIMEOUT { struct timeval tv; tv.tv_sec = readPollmsec / 1000; tv.tv_usec = (readPollmsec % 1000) * 1000; if (setsockopt(tty->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv) < 0) { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, "Can't set %s socket receive timeout: %s", tty->IPDeviceName, strerror(SOCKERRNO)); status = asynError; } } #endif if (gotEom) *gotEom = 0; #ifdef USE_POLL { struct pollfd pollfd; pollfd.fd = tty->fd; pollfd.events = POLLIN; epicsTimeGetCurrent(&startTime); while (poll(&pollfd, 1, readPollmsec) < 0) { if (errno != EINTR) { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, "Poll() failed: %s", strerror(errno)); return asynError; } epicsTimeGetCurrent(&endTime); if (epicsTimeDiffInSeconds(&endTime, &startTime)*1000. > readPollmsec) break; } } #endif if (tty->socketType == SOCK_DGRAM) { /* We use recvfrom() for SOCK_DRAM so we can print the source address with ASYN_TRACEIO_DRIVER */ osiSockAddr oa; unsigned int addrlen = sizeof(oa.ia); thisRead = recvfrom(tty->fd, data, (int)maxchars, 0, &oa.sa, &addrlen); if (thisRead > 0) { if (pasynTrace->getTraceMask(pasynUser) & ASYN_TRACEIO_DRIVER) { char inetBuff[32]; ipAddrToDottedIP(&oa.ia, inetBuff, sizeof(inetBuff)); asynPrintIO(pasynUser, ASYN_TRACEIO_DRIVER, data, thisRead, "%s (from %s) read %d\n", tty->IPDeviceName, inetBuff, thisRead); } tty->nRead += (unsigned long)thisRead; } } else { thisRead = recv(tty->fd, data, (int)maxchars, 0); if (thisRead > 0) { asynPrintIO(pasynUser, ASYN_TRACEIO_DRIVER, data, thisRead, "%s read %d\n", tty->IPDeviceName, thisRead); tty->nRead += (unsigned long)thisRead; } } if (thisRead < 0) { int should_close = (tty->userFlags & USERFLAG_CLOSE_ON_READ_TIMEOUT) || ((SOCKERRNO != SOCK_EWOULDBLOCK) && (SOCKERRNO != SOCK_EINTR)); if (should_close) { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, "%s read error: %s", tty->IPDeviceName, strerror(SOCKERRNO)); closeConnection(pasynUser,tty,"Read error"); status = asynError; } else { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, "%s timeout: %s", tty->IPDeviceName, strerror(SOCKERRNO)); status = asynTimeout; } } /* If recv() returns 0 on a SOCK_STREAM (TCP) socket, the connection has closed */ if ((thisRead == 0) && (tty->socketType == SOCK_STREAM)) { epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, "%s connection closed", tty->IPDeviceName); closeConnection(pasynUser,tty,"Read from broken connection"); reason |= ASYN_EOM_END; } if (thisRead < 0) thisRead = 0; *nbytesTransfered = thisRead; /* If there is room add a null byte */ if (thisRead < (int) maxchars) data[thisRead] = 0; else reason |= ASYN_EOM_CNT; if (gotEom) *gotEom = reason; return status; }
int BlockingTCPAcceptor::initialize() { char ipAddrStr[48]; ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr)); int tryCount = 0; while(tryCount<2) { char strBuffer[64]; LOG(logLevelDebug, "Creating acceptor to %s.", ipAddrStr); _serverSocketChannel = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(_serverSocketChannel==INVALID_SOCKET) { epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); ostringstream temp; temp<<"Socket create error: "<<strBuffer; LOG(logLevelError, "%s", temp.str().c_str()); THROW_BASE_EXCEPTION(temp.str().c_str()); } else { //epicsSocketEnableAddressReuseDuringTimeWaitState(_serverSocketChannel); // try to bind int retval = ::bind(_serverSocketChannel, &_bindAddress.sa, sizeof(sockaddr)); if(retval<0) { epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); LOG(logLevelDebug, "Socket bind error: %s.", strBuffer); if(_bindAddress.ia.sin_port!=0) { // failed to bind to specified bind address, // try to get port dynamically, but only once LOG( logLevelDebug, "Configured TCP port %d is unavailable, trying to assign it dynamically.", ntohs(_bindAddress.ia.sin_port)); _bindAddress.ia.sin_port = htons(0); } else { epicsSocketDestroy(_serverSocketChannel); break; // exit while loop } } else { // if(retval<0) // bind succeeded // update bind address, if dynamically port selection was used if(ntohs(_bindAddress.ia.sin_port)==0) { osiSocklen_t sockLen = sizeof(sockaddr); // read the actual socket info retval = ::getsockname(_serverSocketChannel, &_bindAddress.sa, &sockLen); if(retval<0) { // error obtaining port number epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); LOG(logLevelDebug, "getsockname error: %s", strBuffer); } else { LOG( logLevelInfo, "Using dynamically assigned TCP port %d.", ntohs(_bindAddress.ia.sin_port)); } } retval = ::listen(_serverSocketChannel, 4); if(retval<0) { epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); ostringstream temp; temp<<"Socket listen error: "<<strBuffer; LOG(logLevelError, "%s", temp.str().c_str()); THROW_BASE_EXCEPTION(temp.str().c_str()); } _thread.start(); // all OK, return return ntohs(_bindAddress.ia.sin_port); } // successful bind } // successfully obtained socket tryCount++; } // while ostringstream temp; temp<<"Failed to create acceptor to "<<ipAddrStr; THROW_BASE_EXCEPTION(temp.str().c_str()); }
void BlockingTCPAcceptor::run() { // rise level if port is assigned dynamically char ipAddrStr[48]; ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr)); LOG(logLevelDebug, "Accepting connections at %s.", ipAddrStr); bool socketOpen = true; char strBuffer[64]; while(socketOpen) { { Lock guard(_mutex); if (_destroyed) break; } osiSockAddr address; osiSocklen_t len = sizeof(sockaddr); SOCKET newClient = epicsSocketAccept(_serverSocketChannel, &address.sa, &len); if(newClient!=INVALID_SOCKET) { // accept succeeded ipAddrToDottedIP(&address.ia, ipAddrStr, sizeof(ipAddrStr)); LOG(logLevelDebug, "Accepted connection from PVA client: %s.", ipAddrStr); // enable TCP_NODELAY (disable Nagle's algorithm) int optval = 1; // true int retval = ::setsockopt(newClient, IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(int)); if(retval<0) { epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); LOG(logLevelDebug, "Error setting TCP_NODELAY: %s.", strBuffer); } // enable TCP_KEEPALIVE retval = ::setsockopt(newClient, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(int)); if(retval<0) { epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); LOG(logLevelDebug, "Error setting SO_KEEPALIVE: %s.", strBuffer); } // do NOT tune socket buffer sizes, this will disable auto-tunning // get TCP send buffer size osiSocklen_t intLen = sizeof(int); int _socketSendBufferSize; retval = getsockopt(newClient, SOL_SOCKET, SO_SNDBUF, (char *)&_socketSendBufferSize, &intLen); if(retval<0) { epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); LOG(logLevelDebug, "Error getting SO_SNDBUF: %s.", strBuffer); } /** * Create transport, it registers itself to the registry. */ detail::BlockingServerTCPTransportCodec::shared_pointer transport = detail::BlockingServerTCPTransportCodec::create( _context, newClient, _responseHandler, _socketSendBufferSize, _receiveBufferSize); // validate connection if(!validateConnection(transport, ipAddrStr)) { // TODO // wait for negative response to be sent back and // hold off the client for retrying at very high rate epicsThreadSleep(1.0); transport->close(); LOG( logLevelDebug, "Connection to PVA client %s failed to be validated, closing it.", ipAddrStr); continue; } LOG(logLevelDebug, "Serving to PVA client: %s.", ipAddrStr); }// accept succeeded else socketOpen = false; } // while }
/* * logClientCreate() */ logClientId epicsShareAPI logClientCreate ( struct in_addr server_addr, unsigned short server_port) { epicsTimeStamp begin, current; logClient *pClient; double diff; pClient = calloc (1, sizeof (*pClient)); if (pClient==NULL) { return NULL; } pClient->addr.sin_family = AF_INET; pClient->addr.sin_addr = server_addr; pClient->addr.sin_port = htons(server_port); ipAddrToDottedIP (&pClient->addr, pClient->name, sizeof(pClient->name)); pClient->mutex = epicsMutexCreate (); if ( ! pClient->mutex ) { free ( pClient ); return NULL; } pClient->sock = INVALID_SOCKET; pClient->connected = 0u; pClient->connFailStatus = 0; pClient->shutdown = 0; pClient->shutdownConfirm = 0; epicsAtExit (logClientDestroy, (void*) pClient); pClient->stateChangeNotify = epicsEventCreate (epicsEventEmpty); if ( ! pClient->stateChangeNotify ) { epicsMutexDestroy ( pClient->mutex ); free ( pClient ); return NULL; } pClient->restartThreadId = epicsThreadCreate ( "logRestart", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackSmall), logClientRestart, pClient ); if ( pClient->restartThreadId == NULL ) { epicsMutexDestroy ( pClient->mutex ); epicsEventDestroy ( pClient->stateChangeNotify ); free (pClient); fprintf(stderr, "log client: unable to start log client connection watch dog thread\n"); return NULL; } /* * attempt to synchronize with circuit connect */ epicsTimeGetCurrent ( & begin ); epicsMutexMustLock ( pClient->mutex ); do { epicsMutexUnlock ( pClient->mutex ); epicsEventWaitWithTimeout ( pClient->stateChangeNotify, LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT / 10.0 ); epicsTimeGetCurrent ( & current ); diff = epicsTimeDiffInSeconds ( & current, & begin ); epicsMutexMustLock ( pClient->mutex ); } while ( ! pClient->connected && diff < LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT ); epicsMutexUnlock ( pClient->mutex ); if ( ! pClient->connected ) { fprintf (stderr, "log client create: timed out synchronizing with circuit connect to \"%s\" after %.1f seconds\n", pClient->name, LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT ); } return (void *) pClient; }
Transport::shared_pointer BlockingTCPConnector::connect(std::tr1::shared_ptr<ClientChannelImpl> const & client, ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& address, int8 transportRevision, int16 priority) { SOCKET socket = INVALID_SOCKET; char ipAddrStr[64]; ipAddrToDottedIP(&address.ia, ipAddrStr, sizeof(ipAddrStr)); Context::shared_pointer context = _context.lock(); TransportRegistry::Reservation rsvp(context->getTransportRegistry(), address, priority); // we are now blocking any connect() to this destination (address and prio) // concurrent connect() to other destination is allowed. // This prevents us from opening duplicate connections. Transport::shared_pointer transport = context->getTransportRegistry()->get(address, priority); if(transport.get()) { LOG(logLevelDebug, "Reusing existing connection to PVA server: %s.", ipAddrStr); if (transport->acquire(client)) return transport; } try { LOG(logLevelDebug, "Connecting to PVA server: %s.", ipAddrStr); socket = tryConnect(address, 3); LOG(logLevelDebug, "Socket connected to PVA server: %s.", ipAddrStr); // enable TCP_NODELAY (disable Nagle's algorithm) int optval = 1; // true int retval = ::setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(int)); if(retval<0) { char errStr[64]; epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); LOG(logLevelWarn, "Error setting TCP_NODELAY: %s.", errStr); } // enable TCP_KEEPALIVE retval = ::setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(int)); if(retval<0) { char errStr[64]; epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); LOG(logLevelWarn, "Error setting SO_KEEPALIVE: %s.", errStr); } // TODO tune buffer sizes?! Win32 defaults are 8k, which is OK // create transport // TODO introduce factory // get TCP send buffer size osiSocklen_t intLen = sizeof(int); int _socketSendBufferSize; retval = getsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&_socketSendBufferSize, &intLen); if(retval<0) { char strBuffer[64]; epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); LOG(logLevelDebug, "Error getting SO_SNDBUF: %s.", strBuffer); } // create() also adds to context connection pool _context->getTransportRegistry() transport = detail::BlockingClientTCPTransportCodec::create( context, socket, responseHandler, _receiveBufferSize, _socketSendBufferSize, client, transportRevision, _heartbeatInterval, priority); // verify if(!transport->verify(5000)) { LOG( logLevelDebug, "Connection to PVA server %s failed to be validated, closing it.", ipAddrStr); std::ostringstream temp; temp<<"Failed to verify TCP connection to '"<<ipAddrStr<<"'."; THROW_BASE_EXCEPTION(temp.str().c_str()); } LOG(logLevelDebug, "Connected to PVA server: %s.", ipAddrStr); return transport; } catch(std::exception&) { if(transport.get()) transport->close(); else if(socket!=INVALID_SOCKET) epicsSocketDestroy(socket); throw; } }
/* * RSRV_ONLINE_NOTIFY_TASK */ void rsrv_online_notify_task(void *pParm) { unsigned priorityOfSelf = epicsThreadGetPrioritySelf (); osiSockAddrNode *pNode; double delay; double maxdelay; long longStatus; double maxPeriod; caHdr msg; int status; SOCKET sock; int intTrue = TRUE; unsigned short port; ca_uint32_t beaconCounter = 0; char * pStr; int autoBeaconAddr; ELLLIST autoAddrList; char buf[16]; unsigned priorityOfUDP; epicsThreadBooleanStatus tbs; epicsThreadId tid; taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL); if ( envGetConfigParamPtr ( & EPICS_CAS_BEACON_PERIOD ) ) { longStatus = envGetDoubleConfigParam ( & EPICS_CAS_BEACON_PERIOD, & maxPeriod ); } else { longStatus = envGetDoubleConfigParam ( & EPICS_CA_BEACON_PERIOD, & maxPeriod ); } if (longStatus || maxPeriod<=0.0) { maxPeriod = 15.0; epicsPrintf ("EPICS \"%s\" float fetch failed\n", EPICS_CAS_BEACON_PERIOD.name); epicsPrintf ("Setting \"%s\" = %f\n", EPICS_CAS_BEACON_PERIOD.name, maxPeriod); } delay = 0.02; /* initial beacon period in sec */ maxdelay = maxPeriod; /* * Open the socket. * Use ARPA Internet address format and datagram socket. * Format described in <sys/socket.h>. */ if ( (sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { errlogPrintf ("CAS: online socket creation error\n"); epicsThreadSuspendSelf (); } status = setsockopt (sock, SOL_SOCKET, SO_BROADCAST, (char *)&intTrue, sizeof(intTrue)); if (status<0) { errlogPrintf ("CAS: online socket set up error\n"); epicsThreadSuspendSelf (); } { /* * this connect is to supress a warning message on Linux * when we shutdown the read side of the socket. If it * fails (and it will on old ip kernels) we just ignore * the failure. */ osiSockAddr sockAddr; sockAddr.ia.sin_family = AF_UNSPEC; sockAddr.ia.sin_port = htons ( 0 ); sockAddr.ia.sin_addr.s_addr = htonl (0); connect ( sock, & sockAddr.sa, sizeof ( sockAddr.sa ) ); shutdown ( sock, SHUT_RD ); } memset((char *)&msg, 0, sizeof msg); msg.m_cmmd = htons (CA_PROTO_RSRV_IS_UP); msg.m_count = htons (ca_server_port); msg.m_dataType = htons (CA_MINOR_PROTOCOL_REVISION); ellInit ( & beaconAddrList ); ellInit ( & autoAddrList ); pStr = envGetConfigParam(&EPICS_CAS_AUTO_BEACON_ADDR_LIST, sizeof(buf), buf); if ( ! pStr ) { pStr = envGetConfigParam(&EPICS_CA_AUTO_ADDR_LIST, sizeof(buf), buf); } if (pStr) { if (strstr(pStr,"no")||strstr(pStr,"NO")) { autoBeaconAddr = FALSE; } else if (strstr(pStr,"yes")||strstr(pStr,"YES")) { autoBeaconAddr = TRUE; } else { fprintf(stderr, "CAS: EPICS_CA(S)_AUTO_ADDR_LIST = \"%s\"? Assuming \"YES\"\n", pStr); autoBeaconAddr = TRUE; } } else { autoBeaconAddr = TRUE; } /* * load user and auto configured * broadcast address list */ if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) { port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT, (unsigned short) CA_REPEATER_PORT ); } else { port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT, (unsigned short) CA_REPEATER_PORT ); } /* * discover beacon addresses associated with this interface */ if ( autoBeaconAddr ) { osiSockAddr addr; ELLLIST tmpList; ellInit ( &tmpList ); addr.ia.sin_family = AF_UNSPEC; osiSockDiscoverBroadcastAddresses (&tmpList, sock, &addr); forcePort ( &tmpList, port ); removeDuplicateAddresses ( &autoAddrList, &tmpList, 1 ); } /* * by default use EPICS_CA_ADDR_LIST for the * beacon address list */ { const ENV_PARAM *pParam; if (envGetConfigParamPtr(&EPICS_CAS_INTF_ADDR_LIST) || envGetConfigParamPtr(&EPICS_CAS_BEACON_ADDR_LIST)) { pParam = &EPICS_CAS_BEACON_ADDR_LIST; } else { pParam = &EPICS_CA_ADDR_LIST; } /* * add in the configured addresses */ addAddrToChannelAccessAddressList ( &autoAddrList, pParam, port, pParam == &EPICS_CA_ADDR_LIST ); } removeDuplicateAddresses ( &beaconAddrList, &autoAddrList, 0 ); if ( ellCount ( &beaconAddrList ) == 0 ) { errlogPrintf ("The CA server's beacon address list was empty after initialization?\n"); } # ifdef DEBUG printChannelAccessAddressList (&beaconAddrList); # endif tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfSelf, &priorityOfUDP ); if ( tbs != epicsThreadBooleanStatusSuccess ) { priorityOfUDP = priorityOfSelf; } casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty); casudp_ctl = ctlPause; tid = epicsThreadCreate ( "CAS-UDP", priorityOfUDP, epicsThreadGetStackSize (epicsThreadStackMedium), cast_server, 0 ); if ( tid == 0 ) { epicsPrintf ( "CAS: unable to start UDP daemon thread\n" ); } epicsEventMustWait(casudp_startStopEvent); epicsEventSignal(beacon_startStopEvent); while (TRUE) { pNode = (osiSockAddrNode *) ellFirst (&beaconAddrList); while (pNode) { char buf[64]; status = connect (sock, &pNode->addr.sa, sizeof(pNode->addr.sa)); if (status<0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf)); errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n", __FILE__, buf, sockErrBuf); } else { struct sockaddr_in if_addr; osiSocklen_t size = sizeof (if_addr); status = getsockname (sock, (struct sockaddr *) &if_addr, &size); if (status<0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n", __FILE__, sockErrBuf); } else if (if_addr.sin_family==AF_INET) { msg.m_available = if_addr.sin_addr.s_addr; msg.m_cid = htonl ( beaconCounter ); status = send (sock, (char *)&msg, sizeof(msg), 0); if (status < 0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf)); errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n", __FILE__, buf, sockErrBuf); } else { assert (status == sizeof(msg)); } } } pNode = (osiSockAddrNode *) pNode->node.next; } epicsThreadSleep(delay); if (delay<maxdelay) { delay *= 2.0; if (delay>maxdelay) { delay = maxdelay; } } beaconCounter++; /* expected to overflow */ while (beacon_ctl == ctlPause) { epicsThreadSleep(0.1); delay = 0.02; /* Restart beacon timing if paused */ } } }
/* * CAST_SERVER * * service UDP messages * */ void cast_server(void *pParm) { struct sockaddr_in sin; int status; int count=0; struct sockaddr_in new_recv_addr; osiSocklen_t recv_addr_size; unsigned short port; osiSockIoctl_t nchars; if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) { port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); } else { port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); } recv_addr_size = sizeof(new_recv_addr); if( IOC_cast_sock!=0 && IOC_cast_sock!=INVALID_SOCKET ) { epicsSocketDestroy ( IOC_cast_sock ); } /* * Open the socket. * Use ARPA Internet address format and datagram socket. */ if ( ( IOC_cast_sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0) ) == INVALID_SOCKET ) { epicsPrintf ("CAS: cast socket creation error\n"); epicsThreadSuspendSelf (); } /* * some concern that vxWorks will run out of mBuf's * if this change is made * * joh 11-10-98 */ #if 0 { /* * * this allows for faster connects by queuing * additional incomming UDP search frames * * this allocates a 32k buffer * (uses a power of two) */ int size = 1u<<15u; status = setsockopt (IOC_cast_sock, SOL_SOCKET, SO_RCVBUF, (char *)&size, sizeof(size)); if (status<0) { epicsPrintf ("CAS: unable to set cast socket size\n"); } } #endif epicsSocketEnableAddressUseForDatagramFanout ( IOC_cast_sock ); /* Zero the sock_addr structure */ memset((char *)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = htons(port); /* get server's Internet address */ if( bind(IOC_cast_sock, (struct sockaddr *)&sin, sizeof (sin)) < 0){ char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsPrintf ("CAS: UDP server port bind error was \"%s\"\n", sockErrBuf ); epicsSocketDestroy ( IOC_cast_sock ); epicsThreadSuspendSelf (); } /* * setup new client structure but reuse old structure if * possible * */ while ( TRUE ) { prsrv_cast_client = create_client ( IOC_cast_sock, IPPROTO_UDP ); if ( prsrv_cast_client ) { break; } epicsThreadSleep(300.0); } casAttachThreadToClient ( prsrv_cast_client ); /* * add placeholder for the first version message should it be needed */ rsrv_version_reply ( prsrv_cast_client ); epicsEventSignal(casudp_startStopEvent); while (TRUE) { status = recvfrom ( IOC_cast_sock, prsrv_cast_client->recv.buf, prsrv_cast_client->recv.maxstk, 0, (struct sockaddr *)&new_recv_addr, &recv_addr_size); if (status < 0) { if (SOCKERRNO != SOCK_EINTR) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsPrintf ("CAS: UDP recv error (errno=%s)\n", sockErrBuf); epicsThreadSleep(1.0); } } else if (casudp_ctl == ctlRun) { prsrv_cast_client->recv.cnt = (unsigned) status; prsrv_cast_client->recv.stk = 0ul; epicsTimeGetCurrent(&prsrv_cast_client->time_at_last_recv); prsrv_cast_client->minor_version_number = 0; prsrv_cast_client->seqNoOfReq = 0; /* * If we are talking to a new client flush to the old one * in case we are holding UDP messages waiting to * see if the next message is for this same client. */ if (prsrv_cast_client->send.stk>sizeof(caHdr)) { status = memcmp( (void *)&prsrv_cast_client->addr, (void *)&new_recv_addr, recv_addr_size); if(status){ /* * if the address is different */ cas_send_dg_msg(prsrv_cast_client); prsrv_cast_client->addr = new_recv_addr; } } else { prsrv_cast_client->addr = new_recv_addr; } if (CASDEBUG>1) { char buf[40]; ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf)); errlogPrintf ("CAS: cast server msg of %d bytes from addr %s\n", prsrv_cast_client->recv.cnt, buf); } if (CASDEBUG>2) count = ellCount (&prsrv_cast_client->chanList); status = camessage ( prsrv_cast_client ); if(status == RSRV_OK){ if(prsrv_cast_client->recv.cnt != prsrv_cast_client->recv.stk){ char buf[40]; ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf)); epicsPrintf ("CAS: partial (damaged?) UDP msg of %d bytes from %s ?\n", prsrv_cast_client->recv.cnt-prsrv_cast_client->recv.stk, buf); } } else { char buf[40]; ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf)); epicsPrintf ("CAS: invalid (damaged?) UDP request from %s ?\n", buf); } if (CASDEBUG>2) { if ( ellCount (&prsrv_cast_client->chanList) ) { errlogPrintf ("CAS: Fnd %d name matches (%d tot)\n", ellCount(&prsrv_cast_client->chanList)-count, ellCount(&prsrv_cast_client->chanList)); } } } /* * allow messages to batch up if more are comming */ nchars = 0; /* supress purify warning */ status = socket_ioctl(IOC_cast_sock, FIONREAD, &nchars); if (status<0) { errlogPrintf ("CA cast server: Unable to fetch N characters pending\n"); cas_send_dg_msg (prsrv_cast_client); clean_addrq (); } else if (nchars == 0) { cas_send_dg_msg (prsrv_cast_client); clean_addrq (); } } }