/* $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 * * Notes: * 1) This class has a pointer to the IIU. This pointer always points at * a valid IIU. If the client context is deleted then the channel points at a * static file scope IIU. IIU's that disconnect go into an inactive state * and are stored on a list for later reuse. When the channel calls a * member function of the IIU, the IIU verifies that the channel's IIU * pointer is still pointing at itself only after it has acquired the IIU * lock. */ #include #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "iocinf.h" #include "netIO.h" #include "cac.h" #define epicsExportSharedSymbols #include "udpiiu.h" #include "cadef.h" // for ECA_STRTOBIG and TYPENOTCONN etc #include "db_access.h" // for INVALID_DB_REQ #undef epicsExportSharedSymbols tsFreeList < class nciu, 1024 > nciu::freeList; epicsMutex nciu::freeListMutex; nciu::nciu ( cac &cacIn, netiiu &iiuIn, cacChannelNotify &chanIn, const char *pNameIn ) : cacChannel ( chanIn ), cacCtx ( cacIn ), piiu ( &iiuIn ), sid ( UINT_MAX ), count ( 0 ), retry ( 0u ), retrySeqNo ( 0u ), nameLength ( strlen ( pNameIn ) + 1 ), typeCode ( USHRT_MAX ), f_connected ( false ), f_previousConn ( false ), f_claimSent ( false ), f_firstConnectDecrementsOutstandingIO ( false ), f_connectTimeOutSeen ( false ) { // second constraint is imposed by size field in protocol header if ( this->nameLength > MAX_UDP_SEND - sizeof ( caHdr ) || this->nameLength > 0xffff ) { throwWithLocation ( caErrorCode ( ECA_STRTOBIG ) ); } this->pNameStr = new char [ this->nameLength ]; if ( ! this->pNameStr ) { throw std::bad_alloc (); } strcpy ( this->pNameStr, pNameIn ); } nciu::~nciu () { // care is taken so that a lock is not applied during this phase this->cacCtx.destroyAllIO ( *this ); this->cacCtx.uninstallChannel ( *this ); if ( ! this->f_connectTimeOutSeen && ! this->f_previousConn ) { if ( this->f_firstConnectDecrementsOutstandingIO ) { this->cacCtx.decrementOutstandingIO (); } } delete [] this->pNameStr; } void nciu::connect ( unsigned nativeType, unsigned nativeCount, unsigned sidIn ) { bool v41Ok; if ( ! this->f_claimSent ) { this->cacCtx.printf ( "CAC: Ignored conn resp to chan lacking virtual circuit CID=%u SID=%u?\n", this->getId (), sidIn ); return; } if ( this->f_connected ) { this->cacCtx.printf ( "CAC: Ignored conn resp to conn chan CID=%u SID=%u?\n", this->getId (), sidIn ); return; } if ( ! dbf_type_is_valid ( nativeType ) ) { this->cacCtx.printf ( "CAC: Ignored conn resp with bad native data type CID=%u SID=%u?\n", this->getId (), sidIn ); return; } if ( ! this->f_connectTimeOutSeen && ! this->f_previousConn ) { if ( this->f_firstConnectDecrementsOutstandingIO ) { this->cacCtx.decrementOutstandingIO (); } } if ( this->piiu ) { v41Ok = this->piiu->ca_v41_ok (); } else { v41Ok = false; } this->typeCode = static_cast < unsigned short > ( nativeType ); this->count = nativeCount; this->sid = sidIn; this->f_connected = true; this->f_previousConn = true; /* * if less than v4.1 then the server will never * send access rights and we know that there * will always be access */ if ( ! v41Ok ) { this->accessRightState.setReadPermit(); this->accessRightState.setWritePermit(); } // resubscribe for monitors from this channel this->cacCtx.connectAllIO ( *this ); static bool once = 0; if ( ! once ) { printf ( "there is a problem here where we are calling through a defunct vf table\n" ); once = 1; } this->notify().connectNotify (); /* * if less than v4.1 then the server will never * send access rights and we know that there * will always be access and also need to call * their call back here */ if ( ! v41Ok ) { this->notify().accessRightsNotify ( this->accessRightState ); } } void nciu::disconnect ( netiiu &newiiu ) { bool wasConnected; this->piiu->disconnectAllIO ( *this ); this->piiu = &newiiu; this->retry = 0u; this->typeCode = USHRT_MAX; this->count = 0u; this->sid = UINT_MAX; this->accessRightState.clrReadPermit(); this->accessRightState.clrWritePermit(); this->f_claimSent = false; if ( this->f_connected ) { wasConnected = true; } else { wasConnected = false; } this->f_connected = false; if ( wasConnected ) { /* * look for events that have an event cancel in progress */ this->notify().disconnectNotify (); this->notify().accessRightsNotify ( this->accessRightState ); } this->resetRetryCount (); } /* * nciu::searchMsg () */ bool nciu::searchMsg ( unsigned short retrySeqNumber, unsigned &retryNoForThisChannel ) { caHdr msg; bool success; msg.m_cmmd = htons ( CA_PROTO_SEARCH ); msg.m_available = this->getId (); msg.m_dataType = htons ( DONTREPLY ); msg.m_count = htons ( CA_MINOR_PROTOCOL_REVISION ); msg.m_cid = this->getId (); success = this->piiu->pushDatagramMsg ( msg, this->pNameStr, this->nameLength ); if ( success ) { // // increment the number of times we have tried // to find this channel // if ( this->retry < UINT_MAX ) { this->retry++; } this->retrySeqNo = retrySeqNumber; retryNoForThisChannel = this->retry; } return success; } const char *nciu::pName () const { return this->pNameStr; } unsigned nciu::nameLen () const { return this->nameLength; } void nciu::createChannelRequest () { this->piiu->createChannelRequest ( *this ); this->f_claimSent = true; } cacChannel::ioStatus nciu::read ( unsigned type, arrayElementCount countIn, cacReadNotify ¬ify, ioid *pId ) { // // fail out if their arguments are invalid // if ( ! this->f_connected ) { throw notConnected (); } if ( INVALID_DB_REQ (type) ) { throw badType (); } if ( ! this->accessRightState.readPermit() ) { throw noReadAccess (); } if ( countIn > UINT_MAX ) { throw outOfBounds (); } if ( countIn == 0 ) { countIn = this->count; } ioid tmpId = this->cacCtx.readNotifyRequest ( *this, type, countIn, notify ); if ( pId ) { *pId = tmpId; } return cacChannel::iosAsynch; } void nciu::stringVerify ( const char *pStr, const unsigned count ) { for ( unsigned i = 0; i < count; i++ ) { unsigned int strsize = 0; while ( pStr[strsize++] != '\0' ) { if ( strsize >= MAX_STRING_SIZE ) { throw badString(); } } pStr += MAX_STRING_SIZE; } } void nciu::write ( unsigned type, arrayElementCount countIn, const void *pValue ) { if ( ! this->accessRightState.writePermit() ) { throw noWriteAccess(); } if ( countIn > this->count || countIn == 0 ) { throw outOfBounds(); } if ( type == DBR_STRING ) { nciu::stringVerify ( (char *) pValue, countIn ); } this->cacCtx.writeRequest ( *this, type, countIn, pValue ); } cacChannel::ioStatus nciu::write ( unsigned type, arrayElementCount countIn, const void *pValue, cacWriteNotify ¬ify, ioid *pId ) { if ( ! this->accessRightState.writePermit() ) { throw noWriteAccess(); } if ( countIn > this->count || countIn == 0 ) { throw outOfBounds(); } if ( type == DBR_STRING ) { nciu::stringVerify ( (char *) pValue, countIn ); } ioid tmpId = this->cacCtx.writeNotifyRequest ( *this, type, countIn, pValue, notify ); if ( pId ) { *pId = tmpId; } return cacChannel::iosAsynch; } void nciu::subscribe ( unsigned type, arrayElementCount nElem, unsigned mask, cacStateNotify ¬ify, ioid *pId ) { if ( INVALID_DB_REQ(type) ) { throw badType(); } if ( mask > 0xffff || mask == 0u ) { throw badEventSelection(); } ioid tmpId = this->cacCtx.subscriptionRequest ( *this, type, nElem, mask, notify ); if ( pId ) { *pId = tmpId; } } void nciu::ioCancel ( const ioid &idIn ) { this->cacCtx.ioCancel ( *this, idIn ); } void nciu::ioShow ( const ioid &idIn, unsigned level ) const { this->cacCtx.ioShow ( idIn, level ); } void nciu::initiateConnect () { this->notifyStateChangeFirstConnectInCountOfOutstandingIO (); this->cacCtx.installNetworkChannel ( *this, this->piiu ); } void nciu::hostName ( char *pBuf, unsigned bufLength ) const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); this->piiu->hostName ( pBuf, bufLength ); } // deprecated - please do not use, this is _not_ thread safe const char * nciu::pHostName () const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); return this->piiu->pHostName (); // ouch ! } bool nciu::ca_v42_ok () const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); return this->piiu->ca_v42_ok (); } short nciu::nativeType () const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); short type; if ( this->f_connected ) { if ( this->typeCode < SHRT_MAX ) { type = static_cast ( this->typeCode ); } else { type = TYPENOTCONN; } } else { type = TYPENOTCONN; } return type; } arrayElementCount nciu::nativeElementCount () const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); arrayElementCount countOut; if ( this->f_connected ) { countOut = this->count; } else { countOut = 0ul; } return countOut; } caAccessRights nciu::accessRights () const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); caAccessRights tmp = this->accessRightState; return tmp; } unsigned nciu::searchAttempts () const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); return this->retry; } double nciu::beaconPeriod () const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); return this->piiu->beaconPeriod (); } void nciu::notifyStateChangeFirstConnectInCountOfOutstandingIO () { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); // test is performed via a callback so that locking is correct if ( ! this->f_connectTimeOutSeen && ! this->f_previousConn ) { if ( this->notify ().includeFirstConnectInCountOfOutstandingIO () ) { if ( ! this->f_firstConnectDecrementsOutstandingIO ) { this->cacCtx.incrementOutstandingIO (); this->f_firstConnectDecrementsOutstandingIO = true; } } else { if ( this->f_firstConnectDecrementsOutstandingIO ) { this->cacCtx.decrementOutstandingIO (); this->f_firstConnectDecrementsOutstandingIO = false; } } } } void nciu::show ( unsigned level ) const { epicsAutoMutex locker ( this->cacCtx.mutexRef() ); if ( this->f_connected ) { char hostNameTmp [256]; this->hostName ( hostNameTmp, sizeof ( hostNameTmp ) ); ::printf ( "Channel \"%s\", connected to server %s", this->pNameStr, hostNameTmp ); if ( level > 1u ) { int tmpTypeCode = static_cast < int > ( this->typeCode ); ::printf ( ", native type %s, native element count %u", dbf_type_to_text ( tmpTypeCode ), this->count ); ::printf ( ", %sread access, %swrite access", this->accessRightState.readPermit() ? "" : "no ", this->accessRightState.writePermit() ? "" : "no "); } ::printf ( "\n" ); } else if ( this->f_previousConn ) { ::printf ( "Channel \"%s\" (previously connected to a server)\n", this->pNameStr ); } else { ::printf ( "Channel \"%s\" (unable to locate server)\n", this->pNameStr ); } if ( level > 2u ) { ::printf ( "\tnetwork IO pointer = %p\n", static_cast ( this->piiu ) ); ::printf ( "\tserver identifier %u\n", this->sid ); ::printf ( "\tsearch retry number=%u, search retry sequence number=%u\n", this->retry, this->retrySeqNo ); ::printf ( "\tname length=%u\n", this->nameLength ); } } int nciu::printf ( const char *pFormat, ... ) { va_list theArgs; int status; va_start ( theArgs, pFormat ); status = this->cacCtx.vPrintf ( pFormat, theArgs ); va_end ( theArgs ); return status; }