/* * $Id$ * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 * * Copyright, 1986, The Regents of the University of California. * * Author: Jeff Hill */ #include "iocinf.h" #include "inetAddrID_IL.h" #include "bhe_IL.h" tsFreeList < class tcpiiu, 16 > tcpiiu::freeList; #ifdef DEBUG # define debugPrintf(argsInParen) printf argsInParen #else # define debugPrintf(argsInParen) #endif #ifdef CONVERSION_REQUIRED extern CACVRTFUNC *cac_dbr_cvrt[]; #endif /*CONVERSION_REQUIRED*/ typedef void (*pProtoStubTCP) (tcpiiu *piiu); const static char nullBuff[32] = { 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0 }; /* * constructTCPIIU () * (lock must be applied by caller) */ tcpiiu * constructTCPIIU (cac *pcac, const struct sockaddr_in *pina, unsigned minorVersion) { bhe *pBHE; tcpiiu *piiu; /* * look for an existing virtual circuit */ pBHE = pcac->lookupBeaconInetAddr ( *pina ); if ( ! pBHE ) { pBHE = pcac->createBeaconHashEntry ( *pina, osiTime () ); if ( ! pBHE ) { return NULL; } } piiu = pBHE->getIIU (); if ( piiu ) { if ( piiu->state == iiu_connecting || piiu->state == iiu_connected ) { return piiu; } else { return NULL; } } piiu = new tcpiiu (pcac, *pina, minorVersion, *pBHE); if (!piiu) { return NULL; } if ( piiu->fullyConstructed () ) { return piiu; } else { delete piiu; return NULL; } } /* * cac_connect_tcp () */ LOCAL void cac_connect_tcp (tcpiiu *piiu) { int status; /* * attempt to connect to a CA server */ while (1) { int errnoCpy; status = connect ( piiu->sock, &piiu->dest.sa, sizeof (piiu->dest.sa) ); if (status == 0) { break; } errnoCpy = SOCKERRNO; if (errnoCpy==SOCK_EISCONN) { /* * called connect after we are already connected * (this appears to be how they provide * connect completion notification) */ break; } else if ( errnoCpy==SOCK_EINPROGRESS || errnoCpy==SOCK_EWOULDBLOCK /* for WINSOCK */ ) { /* * The socket is non-blocking and a * connection attempt has been initiated, * but not completed. */ return; } else if (errnoCpy==SOCK_EALREADY) { return; } #ifdef _WIN32 /* * including this with vxWorks appears to * cause trouble */ else if ( errnoCpy == SOCK_EINVAL ) { /* a SOCK_EALREADY alias used by early WINSOCK */ return; } #endif else if ( errnoCpy == SOCK_EINTR ) { /* * restart the system call if interrupted */ continue; } else { ca_printf ("Unable to connect because %d=\"%s\"\n", errnoCpy, SOCKERRSTR (errnoCpy) ); piiu->shutdown (); return; } } /* * put the iiu into the connected state */ piiu->state = iiu_connected; piiu->rescheduleRecvTimer (); // reset connection activity watchdog return; } /* * retryPendingClaims() * * This assumes that all channels with claims pending are at the * front of the list (and that the channel is moved to the end of * the list when a claim message has been sent for it) * * We send claim messages here until the outgoing message buffer * will not accept any more messages */ LOCAL void retryPendingClaims (tcpiiu *piiu) { bool success; LOCK (piiu->pcas); tsDLIterBD chan ( piiu->chidList.first () ); while ( chan != chan.eol () ) { if (!chan->claimPending) { piiu->claimRequestsPending = false; piiu->flush (); break; } // this moves the channel to the end of the list success = chan->claimMsg (piiu); if ( ! success ) { piiu->flush (); break; } chan = piiu->chidList.first (); } UNLOCK (piiu->pcas); } /* * cacSendThreadTCP () */ extern "C" void cacSendThreadTCP (void *pParam) { tcpiiu *piiu = (tcpiiu *) pParam; while ( true ) { unsigned sendCnt; char *pOutBuf; int status; pOutBuf = static_cast ( cacRingBufferReadReserveNoBlock (&piiu->send, &sendCnt) ); while (!pOutBuf) { piiu->tcpSendWatchdog::cancel (); pOutBuf = (char *) cacRingBufferReadReserve (&piiu->send, &sendCnt); if ( piiu->state != iiu_connected ) { semBinaryGive ( piiu->sendThreadExitSignal ); return; } } assert ( sendCnt <= INT_MAX ); status = send ( piiu->sock, pOutBuf, (int) sendCnt, 0 ); if ( status > 0 ) { cacRingBufferReadCommit ( &piiu->send, (unsigned long) status ); cacRingBufferReadFlush ( &piiu->send ); if ( piiu->claimRequestsPending ) { retryPendingClaims ( piiu ); } if ( piiu->echoRequestPending ) { piiu->echoRequestMsg (); } } else { int localError = SOCKERRNO; cacRingBufferReadCommit (&piiu->send, 0); if ( status == 0 ) { semBinaryGive (piiu->sendThreadExitSignal); piiu->shutdown (); return; } if ( localError == SOCK_SHUTDOWN ) { break; } if ( localError != SOCK_EWOULDBLOCK && localError != SOCK_EINTR ) { if ( localError != SOCK_EPIPE && localError != SOCK_ECONNRESET && localError != SOCK_ETIMEDOUT) { ca_printf ("CAC: unexpected TCP send error: %s\n", SOCKERRSTR (localError) ); } semBinaryGive ( piiu->sendThreadExitSignal ); piiu->shutdown (); return; } } } semBinaryGive ( piiu->sendThreadExitSignal ); } /* * tcpiiu::recvMsg () */ void tcpiiu::recvMsg () { char *pProto; unsigned writeSpace; unsigned totalBytes; int status; if ( this->state != iiu_connected ) { return; } pProto = (char *) cacRingBufferWriteReserve (&this->recv, &writeSpace); assert ( writeSpace <= INT_MAX ); status = ::recv ( this->sock, pProto, (int) writeSpace, 0); if ( status <= 0 ) { int localErrno = SOCKERRNO; cacRingBufferWriteCommit (&this->recv, 0); if ( status == 0 ) { this->shutdown (); return; } if ( localErrno == SOCK_SHUTDOWN ) { return; } if ( localErrno == SOCK_EWOULDBLOCK || localErrno == SOCK_EINTR ) { return; } ca_printf ( "Disconnecting from CA server because: %s\n", SOCKERRSTR (localErrno) ); this->shutdown (); return; } assert ( ( (unsigned) status ) <= writeSpace ); totalBytes = (unsigned) status; cacRingBufferWriteCommit (&this->recv, totalBytes); // cacRingBufferWriteFlush (&this->recv); this->rescheduleRecvTimer (); // reschedule connection activity watchdog return; } /* * cacRecvThreadTCP () */ extern "C" void cacRecvThreadTCP (void *pParam) { tcpiiu *piiu = (tcpiiu *) pParam; cac_connect_tcp (piiu); if ( piiu->state == iiu_connected ) { unsigned priorityOfSelf = threadGetPrioritySelf (); unsigned priorityOfSend; threadBoolStatus tbs; threadId tid; tbs = threadHighestPriorityLevelBelow (priorityOfSelf, &priorityOfSend); if ( tbs != tbsSuccess ) { priorityOfSend = priorityOfSelf; } tid = threadCreate ("CAC TCP Send", priorityOfSend, threadGetStackSize (threadStackMedium), cacSendThreadTCP, piiu); if (tid) { while (1) { piiu->recvMsg (); if ( piiu->state != iiu_connected ) { break; } piiu->pcas->signalRecvActivityIIU (*piiu); } } else { piiu->shutdown (); } } semBinaryGive (piiu->recvThreadExitSignal); } // // tcpiiu::tcpiiu () // tcpiiu::tcpiiu (cac *pcac, const struct sockaddr_in &ina, unsigned minorVersion, class bhe &bheIn) : tcpRecvWatchdog (pcac->ca_connectTMO, pcac->timerQueue, CA_V43 (CA_PROTOCOL_VERSION, minorVersion) ), tcpSendWatchdog (pcac->ca_connectTMO, pcac->timerQueue), netiiu (pcac), bhe (bheIn) { SOCKET newSocket; int status; int flag; newSocket = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( newSocket == INVALID_SOCKET ) { ca_printf ("CAC: unable to create virtual circuit because \"%s\"\n", SOCKERRSTR (SOCKERRNO)); this->fc = false; return; } // // apparently this is nolonger necessary with modern IP kernels // #if 0 flag = TRUE; status = setsockopt ( newSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag) ); if (status < 0) { ca_printf ("CAC: problems setting socket option TCP_NODELAY = \"%s\"\n", SOCKERRSTR (SOCKERRNO)); } #endif flag = TRUE; status = setsockopt ( newSocket , SOL_SOCKET, SO_KEEPALIVE, (char *)&flag, sizeof (flag) ); if (status < 0) { ca_printf ("CAC: problems setting socket option SO_KEEPALIVE = \"%s\"\n", SOCKERRSTR (SOCKERRNO)); } #if 0 { int i; /* * some concern that vxWorks will run out of mBuf's * if this change is made joh 11-10-98 */ i = MAX_MSG_SIZE; status = setsockopt ( newSocket, SOL_SOCKET, SO_SNDBUF, (char *)&i, sizeof (i) ); if (status < 0) { ca_printf ("CAC: problems setting socket option SO_SNDBUF = \"%s\"\n", SOCKERRSTR (SOCKERRNO)); } i = MAX_MSG_SIZE; status = setsockopt (newSocket, SOL_SOCKET, SO_RCVBUF, (char *)&i, sizeof (i) ); if (status < 0) { ca_printf ("CAC: problems setting socket option SO_RCVBUF = \"%s\"\n", SOCKERRSTR (SOCKERRNO)); } } #endif this->sock = newSocket; this->minor_version_number = minorVersion; this->dest.ia = ina; this->contiguous_msg_count = 0u; this->client_busy = false; this->claimRequestsPending = false; this->echoRequestPending = false; this->sendPending = false; this->pushPending = false; this->beaconAnomaly = false; this->curDataMax = 0ul; this->curMsgBytes = 0ul; this->curDataBytes = 0ul; memset ( (void *) &this->curMsg, '\0', sizeof (this->curMsg) ); this->pCurData = 0; status = cacRingBufferConstruct (&this->recv); if (status) { ca_printf ("CA: unable to create recv ring buffer\n"); socket_close ( newSocket ); this->fc = false; return; } status = cacRingBufferConstruct (&this->send); if (status) { ca_printf ("CA: unable to create send ring buffer\n"); cacRingBufferDestroy (&this->recv); socket_close ( newSocket ); this->fc = false; return; } /* * Save the Host name for efficient access in the * future. */ ipAddrToA ( &this->dest.ia, this->host_name_str, sizeof (this->host_name_str) ); /* * TCP starts out in the connecting state and later transitions * to the connected state */ this->state = iiu_connecting; this->sendThreadExitSignal = semBinaryCreate (semEmpty); if ( ! this->sendThreadExitSignal ) { ca_printf ("CA: unable to create CA client send thread exit semaphore\n"); cacRingBufferDestroy (&this->recv); cacRingBufferDestroy (&this->send); socket_close ( newSocket ); this->fc = false; return; } this->recvThreadExitSignal = semBinaryCreate (semEmpty); if ( ! this->recvThreadExitSignal ) { ca_printf ("CA: unable to create CA client send thread exit semaphore\n"); semBinaryDestroy (this->sendThreadExitSignal); cacRingBufferDestroy (&this->recv); cacRingBufferDestroy (&this->send); socket_close ( newSocket ); this->fc = false; return; } { unsigned priorityOfSelf = threadGetPrioritySelf (); unsigned priorityOfRecv; threadId tid; threadBoolStatus tbs; tbs = threadHighestPriorityLevelBelow (priorityOfSelf, &priorityOfRecv); if ( tbs != tbsSuccess ) { priorityOfRecv = priorityOfSelf; } tid = threadCreate ("CAC TCP Recv", priorityOfRecv, threadGetStackSize (threadStackMedium), cacRecvThreadTCP, this); if (tid==0) { ca_printf ("CA: unable to create CA client receive thread\n"); semBinaryDestroy (this->recvThreadExitSignal); semBinaryDestroy (this->sendThreadExitSignal); cacRingBufferDestroy (&this->recv); cacRingBufferDestroy (&this->send); socket_close ( newSocket ); this->fc = false; return; } } bhe.bindToIIU (this); pcac->installIIU (*this); this->fc = true; } /* * tcpiiu::shutdown () */ void tcpiiu::shutdown () { LOCK ( this->pcas ); if ( this->state != iiu_disconnected ) { this->state = iiu_disconnected; ::shutdown ( this->sock, SD_BOTH ); cacRingBufferShutDown ( &this->send ); cacRingBufferShutDown ( &this->recv ); this->pcas->signalRecvActivityIIU ( *this ); } UNLOCK ( this->pcas ); } // // tcpiiu::~tcpiiu () // tcpiiu::~tcpiiu () { unsigned chanDisconnectCount; if ( ! this->fc ) { return; } this->fc = false; this->shutdown (); LOCK (this->pcas); chanDisconnectCount = ellCount (&this->chidList); if ( chanDisconnectCount ) { genLocalExcep (this->pcas, ECA_DISCONN, this->host_name_str); } tsDLIterBD iter ( this->chidList.first () ); while ( iter != iter.eol () ) { tsDLIterBD next = iter.itemAfter (); iter->disconnect (); iter = next; } UNLOCK (this->pcas); // wait for send and recv threads to exit semBinaryMustTake ( this->sendThreadExitSignal ); semBinaryMustTake ( this->recvThreadExitSignal ); semBinaryDestroy (this->sendThreadExitSignal); semBinaryDestroy (this->recvThreadExitSignal); this->pcas->removeIIU ( *this ); this->pcas->removeBeaconInetAddr ( this->dest.ia ); if ( this->pcas->ca_fd_register_func ) { (*this->pcas->ca_fd_register_func) ((void *)this->pcas->ca_fd_register_arg, this->sock, FALSE); } socket_close (this->sock); cacRingBufferDestroy (&this->recv); cacRingBufferDestroy (&this->send); /* * free message body cache */ if (this->pCurData) { free (this->pCurData); } } void tcpiiu::suicide () { delete this; } bool tcpiiu::compareIfTCP ( nciu &chan, const sockaddr_in &addr ) const { if ( this->dest.ia.sin_addr.s_addr != addr.sin_addr.s_addr || this->dest.ia.sin_port != addr.sin_port ) { char rej[64]; ipAddrToA ( &addr, rej, sizeof (rej) ); LOCK ( this->pcas ); sprintf ( this->pcas->ca_sprintf_buf, "Channel: %s Accepted: %s Rejected: %s ", chan.pName (), this->host_name_str, rej ); genLocalExcep (this->pcas, ECA_DBLCHNL, this->pcas->ca_sprintf_buf); UNLOCK ( this->pcas ); } return true; } void tcpiiu::flush () { if ( cacRingBufferWriteFlush ( &this->send ) ) { this->rescheduleSendTimer (); } } void tcpiiu::show ( unsigned /* level */ ) const { } /* * tcpiiu::noopRequestMsg () */ void tcpiiu::noopRequestMsg () { caHdr hdr; int status; hdr.m_cmmd = htons (CA_PROTO_NOOP); hdr.m_dataType = htons (0); hdr.m_count = htons (0); hdr.m_cid = htons (0); hdr.m_available = htons (0); hdr.m_postsize = 0; status = this->pushStreamMsg (&hdr, NULL, true); if ( status == ECA_NORMAL ) { this->flush (); } } /* * tcpiiu::echoRequestMsg () */ void tcpiiu::echoRequestMsg () { caHdr hdr; hdr.m_cmmd = htons (CA_PROTO_ECHO); hdr.m_dataType = htons (0); hdr.m_count = htons (0); hdr.m_cid = htons (0); hdr.m_available = htons (0); hdr.m_postsize = 0u; /* * If we are out of buffer space then postpone this * operation until later. This avoids any possibility * of a push pull deadlock (since this can be sent when * parsing the UDP input buffer). */ if ( this->pushStreamMsg (&hdr, NULL, false) == ECA_NORMAL ) { this->flush (); this->echoRequestPending = false; } else { this->echoRequestPending = true; } } /* * tcpiiu::busyRequestMsg () */ int tcpiiu::busyRequestMsg () { caHdr hdr; int status; hdr = cacnullmsg; hdr.m_cmmd = htons ( CA_PROTO_EVENTS_OFF ); status = this->pushStreamMsg ( &hdr, NULL, true ); if ( status == ECA_NORMAL ) { this->flush (); } return status; } /* * tcpiiu::readyRequestMsg () */ int tcpiiu::readyRequestMsg () { caHdr hdr; int status; hdr = cacnullmsg; hdr.m_cmmd = htons (CA_PROTO_EVENTS_ON); status = this->pushStreamMsg (&hdr, NULL, true); if ( status == ECA_NORMAL ) { this->flush (); } return status; } /* * tcpiiu::hostNameSetMsg () */ void tcpiiu::hostNameSetMsg () { unsigned size; caHdr hdr; char *pName; if ( ! CA_V41(CA_PROTOCOL_VERSION, this->minor_version_number) ) { return; } /* * allocate space in the outgoing buffer */ pName = this->pcas->ca_pHostName, size = strlen (pName) + 1; hdr = cacnullmsg; hdr.m_cmmd = htons (CA_PROTO_HOST_NAME); hdr.m_postsize = size; this->pushStreamMsg (&hdr, pName, true); return; } /* * tcpiiu::userNameSetMsg () */ void tcpiiu::userNameSetMsg () { unsigned size; caHdr hdr; char *pName; if ( ! CA_V41(CA_PROTOCOL_VERSION, this->minor_version_number) ) { return; } /* * allocate space in the outgoing buffer */ pName = this->pcas->ca_pUserName, size = strlen (pName) + 1; hdr = cacnullmsg; hdr.m_cmmd = htons ( CA_PROTO_CLIENT_NAME ); hdr.m_postsize = size; this->pushStreamMsg ( &hdr, pName, true ); return; } /* * tcp_noop_action () */ LOCAL void tcp_noop_action (tcpiiu * /* piiu */) { return; } /* * echo_resp_action () */ LOCAL void echo_resp_action (tcpiiu *piiu) { piiu->echoResponseNotify (); piiu->beaconAnomaly = false; return; } /* * write_notify_resp_action () */ LOCAL void write_notify_resp_action (tcpiiu *piiu) { baseNMIU *monix; LOCK (piiu->pcas); monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); if (monix) { int status = ntohl (piiu->curMsg.m_cid); if ( status == ECA_NORMAL ) { monix->completionNotify (); } else { monix->exceptionNotify ( status, "write notify request rejected" ); } monix->destroy (); } UNLOCK (piiu->pcas); } /* * read_notify_resp_action () */ LOCAL void read_notify_resp_action ( tcpiiu *piiu ) { baseNMIU *monix; LOCK (piiu->pcas); monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); if (monix) { int v41; int status; /* * convert the data buffer from net * format to host format */ # ifdef CONVERSION_REQUIRED if (piiu->curMsg.m_dataTypecurMsg.m_dataType])( piiu->pCurData, piiu->pCurData, FALSE, piiu->curMsg.m_count); } else { piiu->curMsg.m_cid = htonl(ECA_BADTYPE); } # endif /* * the channel id field is abused for * read notify status starting * with CA V4.1 */ v41 = CA_V41 (CA_PROTOCOL_VERSION, piiu->minor_version_number); if (v41) { status = ntohl (piiu->curMsg.m_cid); } else{ status = ECA_NORMAL; } if ( status == ECA_NORMAL ) { monix->completionNotify (piiu->curMsg.m_dataType, piiu->curMsg.m_count, piiu->pCurData); } else { monix->exceptionNotify (status, "read failed", piiu->curMsg.m_dataType, piiu->curMsg.m_count); } monix->destroy (); } UNLOCK (piiu->pcas); return; } /* * event_resp_action () */ LOCAL void event_resp_action (tcpiiu *piiu) { baseNMIU *monix; /* * run the user's event handler */ LOCK (piiu->pcas); monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); if ( monix ) { int v41; int status; /* * m_postsize = 0 used to be a confirmation, but is * now a noop because the above hash lookup will * not find a matching IO block */ if ( ! piiu->curMsg.m_postsize ) { monix->destroy (); UNLOCK (piiu->pcas); return; } /* * convert the data buffer from net * format to host format */ # ifdef CONVERSION_REQUIRED if (piiu->curMsg.m_dataTypecurMsg.m_dataType])( piiu->pCurData, piiu->pCurData, FALSE, piiu->curMsg.m_count); } else { piiu->curMsg.m_cid = htonl(ECA_BADTYPE); } # endif /* * the channel id field is abused for * read notify status starting * with CA V4.1 */ v41 = CA_V41 (CA_PROTOCOL_VERSION, piiu->minor_version_number); if (v41) { status = ntohl (piiu->curMsg.m_cid); } else { status = ECA_NORMAL; } if ( status == ECA_NORMAL ) { monix->completionNotify ( piiu->curMsg.m_dataType, piiu->curMsg.m_count, piiu->pCurData ); } else { monix->exceptionNotify ( status, "subscription update failed", piiu->curMsg.m_dataType, piiu->curMsg.m_count ); } } UNLOCK (piiu->pcas); return; } /* * read_resp_action () */ LOCAL void read_resp_action (tcpiiu *piiu) { baseNMIU *pIOBlock; LOCK (piiu->pcas); /* * verify the event id */ pIOBlock = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); if ( pIOBlock ) { /* * convert the data buffer from net * format to host format */ pIOBlock->completionNotify (piiu->curMsg.m_dataType, piiu->curMsg.m_count, piiu->pCurData); pIOBlock->destroy (); } UNLOCK (piiu->pcas); return; } /* * clear_channel_resp_action () */ LOCAL void clear_channel_resp_action (tcpiiu * /* piiu */) { /* presently a noop */ return; } /* * exception_resp_action () */ LOCAL void exception_resp_action (tcpiiu *piiu) { baseNMIU *monix; char context[255]; caHdr *req = (caHdr *) piiu->pCurData; if ( piiu->curMsg.m_postsize > sizeof (caHdr) ){ sprintf (context, "detected by: %s for: %s", piiu->host_name_str, (char *)(req+1)); } else{ sprintf (context, "detected by: %s", piiu->host_name_str); } LOCK (piiu->pcas); switch ( ntohs (req->m_cmmd) ) { case CA_PROTO_READ_NOTIFY: monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); if (monix) { monix->exceptionNotify ( ntohl (piiu->curMsg.m_available), context, ntohs (req->m_dataType), ntohs (req->m_count) ); monix->destroy (); } else { piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), context, __FILE__, __LINE__); } break; case CA_PROTO_READ: monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); if (monix) { monix->exceptionNotify ( ntohl (piiu->curMsg.m_available), context, ntohs (req->m_dataType), ntohs (req->m_count) ); monix->destroy (); } else { piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), context, __FILE__, __LINE__); } break; case CA_PROTO_WRITE_NOTIFY: monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); if (monix) { monix->exceptionNotify (ntohl (piiu->curMsg.m_available), context); monix->destroy (); } else { piiu->pcas->exceptionNotify (ntohl ( piiu->curMsg.m_available), context, __FILE__, __LINE__); } break; case CA_PROTO_WRITE: piiu->pcas->exceptionNotify (ntohl ( piiu->curMsg.m_available), context, ntohs (req->m_dataType), ntohs (req->m_count), __FILE__, __LINE__); break; case CA_PROTO_EVENT_ADD: monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); if (monix) { monix->exceptionNotify (ntohl (piiu->curMsg.m_available), context); monix->destroy (); } else { piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), context, __FILE__, __LINE__); } break; case CA_PROTO_EVENT_CANCEL: monix = piiu->pcas->lookupIO ( piiu->curMsg.m_available ); if (monix) { monix->exceptionNotify (ntohl (piiu->curMsg.m_available), context); monix->destroy (); } else { piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), context, __FILE__, __LINE__); } break; default: piiu->pcas->exceptionNotify (ntohl (piiu->curMsg.m_available), context, __FILE__, __LINE__); break; } UNLOCK (piiu->pcas); return; } /* * access_rights_resp_action () */ LOCAL void access_rights_resp_action (tcpiiu *piiu) { int ar; nciu *chan; LOCK (piiu->pcas); chan = piiu->pcas->lookupChan (piiu->curMsg.m_cid); if ( ! chan ) { /* * end up here if they delete the channel * prior to connecting */ UNLOCK (piiu->pcas); return; } ar = ntohl (piiu->curMsg.m_available); chan->ar.read_access = (ar&CA_PROTO_ACCESS_RIGHT_READ)?1:0; chan->ar.write_access = (ar&CA_PROTO_ACCESS_RIGHT_WRITE)?1:0; chan->accessRightsNotify (chan->ar); UNLOCK (piiu->pcas); return; } /* * claim_ciu_resp_action () */ LOCAL void claim_ciu_resp_action (tcpiiu *piiu) { unsigned sid; nciu *chan; LOCK (piiu->pcas); chan = piiu->pcas->lookupChan (piiu->curMsg.m_cid); if ( ! chan ) { UNLOCK (piiu->pcas); return; } if ( CA_V44 (CA_PROTOCOL_VERSION, piiu->minor_version_number) ) { sid = piiu->curMsg.m_available; } else { sid = chan->sid; } chan->connect (*piiu, piiu->curMsg.m_dataType, piiu->curMsg.m_count, sid); UNLOCK (piiu->pcas); return; } /* * verifyAndDisconnectChan () */ LOCAL void verifyAndDisconnectChan (tcpiiu *piiu) { nciu *chan; LOCK (piiu->pcas); chan = piiu->pcas->lookupChan (piiu->curMsg.m_cid); if (!chan) { /* * end up here if they delete the channel * prior to this response */ UNLOCK (piiu->pcas); return; } /* * need to move the channel back to the cast niiu * (so we will be able to reconnect) * * this marks the niiu for disconnect if the channel * count goes to zero */ chan->disconnect (); UNLOCK (piiu->pcas); return; } /* * bad_tcp_resp_action () */ LOCAL void bad_tcp_resp_action (tcpiiu *piiu) { ca_printf ("CAC: Bad response code in TCP message from %s was %u\n", piiu->host_name_str, piiu->curMsg.m_cmmd); } /* * TCP protocol jump table */ LOCAL const pProtoStubTCP tcpJumpTableCAC[] = { tcp_noop_action, event_resp_action, bad_tcp_resp_action, read_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, exception_resp_action, clear_channel_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, read_notify_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, claim_ciu_resp_action, write_notify_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, access_rights_resp_action, echo_resp_action, bad_tcp_resp_action, bad_tcp_resp_action, verifyAndDisconnectChan, verifyAndDisconnectChan }; /* * post_tcp_msg () * * LOCK should be applied when calling this routine * */ int tcpiiu::post_msg (char *pInBuf, unsigned long blockSize) { unsigned long size; while ( blockSize ) { /* * fetch a complete message header */ if ( this->curMsgBytes < sizeof (this->curMsg) ) { char *pHdr; size = sizeof (this->curMsg) - this->curMsgBytes; size = min (size, blockSize); pHdr = (char *) &this->curMsg; memcpy ( pHdr + this->curMsgBytes, pInBuf, size); this->curMsgBytes += size; if ( this->curMsgBytes < sizeof (this->curMsg) ) { #if 0 printf ("waiting for %d msg hdr bytes\n", sizeof(this->curMsg) - this->curMsgBytes); #endif return ECA_NORMAL; } pInBuf += size; blockSize -= size; /* * fix endian of bytes */ this->curMsg.m_postsize = ntohs (this->curMsg.m_postsize); this->curMsg.m_cmmd = ntohs (this->curMsg.m_cmmd); this->curMsg.m_dataType = ntohs (this->curMsg.m_dataType); this->curMsg.m_count = ntohs (this->curMsg.m_count); #if 0 ca_printf ( "%s Cmd=%3d Type=%3d Count=%4d Size=%4d", this->host_name_str, this->curMsg.m_cmmd, this->curMsg.m_dataType, this->curMsg.m_count, this->curMsg.m_postsize); ca_printf ( " Avail=%8x Cid=%6d\n", this->curMsg.m_available, this->curMsg.m_cid); #endif } /* * dont allow huge msg body until * the server supports it */ if ( this->curMsg.m_postsize > (unsigned) MAX_TCP ) { this->curMsgBytes = 0; this->curDataBytes = 0; return ECA_TOLARGE; } /* * make sure we have a large enough message body cache */ if ( this->curMsg.m_postsize > this->curDataMax ) { void *pData; size_t cacheSize; /* * scalar DBR_STRING is sometimes clipped to the * actual string size so make sure this cache is * as large as one DBR_STRING so they will * not page fault if they read MAX_STRING_SIZE * bytes (instead of the actual string size). */ cacheSize = max ( this->curMsg.m_postsize, MAX_STRING_SIZE ); pData = (void *) calloc (1u, cacheSize); if (!pData) { return ECA_ALLOCMEM; } if (this->pCurData) { free (this->pCurData); } this->pCurData = pData; this->curDataMax = this->curMsg.m_postsize; } /* * Fetch a complete message body * (allows for arrays larger than than the * ring buffer size) */ if (this->curMsg.m_postsize > this->curDataBytes ) { char *pBdy; size = this->curMsg.m_postsize - this->curDataBytes; size = min (size, blockSize); pBdy = (char *) this->pCurData; memcpy ( pBdy + this->curDataBytes, pInBuf, size); this->curDataBytes += size; if (this->curDataBytes < this->curMsg.m_postsize) { #if 0 printf ("waiting for %d msg bdy bytes\n", this->curMsg.m_postsize - this->curDataBytes); #endif return ECA_NORMAL; } pInBuf += size; blockSize -= size; } /* * execute the response message */ pProtoStubTCP pStub; if ( this->curMsg.m_cmmd >= NELEMENTS (tcpJumpTableCAC) ) { pStub = bad_tcp_resp_action; } else { pStub = tcpJumpTableCAC [this->curMsg.m_cmmd]; } (*pStub) (this); this->curMsgBytes = 0; this->curDataBytes = 0; } return ECA_NORMAL; } void tcpiiu::hostName ( char *pBuf, unsigned bufLength ) const { if ( bufLength ) { strncpy ( pBuf, this->host_name_str, bufLength ); pBuf[bufLength - 1u] = '\0'; } } bool tcpiiu::ca_v42_ok () const { return CA_V42 (CA_PROTOCOL_VERSION, this->minor_version_number); } bool tcpiiu::ca_v41_ok () const { return CA_V41 (CA_PROTOCOL_VERSION, this->minor_version_number); } /* * tcpiiu::pushStreamMsg () */ int tcpiiu::pushStreamMsg (const caHdr *pmsg, const void *pext, bool BlockingOk) { caHdr msg; ca_uint16_t actualextsize; ca_uint16_t extsize; unsigned msgsize; unsigned bytesSent; if ( pext == NULL ) { extsize = actualextsize = 0; } else { if ( pmsg->m_postsize > 0xffff-7 ) { return ECA_TOLARGE; } actualextsize = pmsg->m_postsize; extsize = CA_MESSAGE_ALIGN (actualextsize); } msg = *pmsg; msg.m_postsize = htons (extsize); msgsize = extsize + sizeof (msg); if ( ! cacRingBufferWriteLockNoBlock ( &this->send, msgsize ) ) { if ( BlockingOk ) { this->rescheduleSendTimer (); cacRingBufferWriteLock ( &this->send ); } else { return ECA_OPWILLBLOCK; } } /* * push the header onto the ring */ bytesSent = cacRingBufferWrite ( &this->send, &msg, sizeof (msg) ); if ( bytesSent != sizeof (msg) ) { cacRingBufferWriteUnlock ( &this->send ); return ECA_DISCONNCHID; } /* * push message body onto the ring * * (optionally encode in network format as we send) */ if ( extsize > 0u ) { bytesSent = cacRingBufferWrite ( &this->send, pext, actualextsize ); if ( bytesSent != actualextsize ) { cacRingBufferWriteUnlock ( &this->send ); return ECA_DISCONNCHID; } /* * force pad bytes at the end of the message to nill * if present (this avoids messages from purify) */ { unsigned long n; n = extsize-actualextsize; if (n) { assert ( n <= sizeof (nullBuff) ); bytesSent = cacRingBufferWrite ( &this->send, nullBuff, n ); if ( bytesSent != n ) { cacRingBufferWriteUnlock ( &this->send ); return ECA_DISCONNCHID; } } } } cacRingBufferWriteUnlock ( &this->send ); return ECA_NORMAL; } int tcpiiu::pushDatagramMsg (const caHdr * /* pMsg */, const void * /* pExt */, ca_uint16_t /* extsize */) { return ECA_INTERNAL; } /* * add to the beginning of the list until we * have sent the claim message (after which we * move it to the end of the list) */ void tcpiiu::addToChanList (nciu *chan) { LOCK (this->pcas); chan->claimPending = TRUE; this->chidList.push (*chan); chan->piiu = this; UNLOCK (this->pcas); } void tcpiiu::removeFromChanList (nciu *chan) { LOCK (this->pcas); this->chidList.remove (*chan); chan->piiu = NULL; UNLOCK (this->pcas); if ( this->chidList.count () == 0 ) { this->shutdown (); } } void tcpiiu::disconnect (nciu *chan) { LOCK (this->pcas); this->removeFromChanList (chan); /* * try to reconnect */ assert (this->pcas->pudpiiu); this->pcas->pudpiiu->addToChanList (chan); this->pcas->pudpiiu->searchTmr.reset (0.0); UNLOCK (this->pcas); }