/* $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 "iocinf.h" #include "nciu_IL.h" #include "netReadNotifyIO_IL.h" #include "netWriteNotifyIO_IL.h" #include "netSubscription_IL.h" #include "cac_IL.h" tsFreeList < class nciu, 1024 > nciu::freeList; epicsMutex nciu::freeListMutex; static const caar defaultAccessRights = { false, false }; nciu::nciu ( cac &cacIn, netiiu &iiuIn, cacChannelNotify &chanIn, const char *pNameIn ) : cacChannelIO ( chanIn ), cacCtx ( cacIn ), accessRightState ( defaultAccessRights ), count ( 0 ), piiu ( &iiuIn ), sid ( UINT_MAX ), retry ( 0u ), retrySeqNo ( 0u ), nameLength ( strlen ( pNameIn ) + 1 ), typeCode ( USHRT_MAX ), f_connected ( false ), f_fullyConstructed ( true ), 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 ) { this->f_fullyConstructed = false; return; } strcpy ( this->pNameStr, pNameIn ); } nciu::~nciu () { if ( ! this->fullyConstructed () ) { return; } // 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 long nativeCount, unsigned sidIn ) { bool v41Ok; if ( ! this->f_claimSent ) { ca_printf ( "CAC: Ignored conn resp to chan lacking virtual circuit CID=%u SID=%u?\n", this->getId (), sidIn ); return; } if ( this->f_connected ) { ca_printf ( "CAC: Ignored conn resp to conn chan 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 = 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.read_access = true; this->accessRightState.write_access = true; } // resubscribe for monitors from this channel this->cacCtx.connectAllIO ( *this ); this->notify().connectNotify ( *this ); /* * 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, 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.read_access = false; this->accessRightState.write_access = false; 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 ); this->notify ().accessRightsNotify ( *this, 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_VERSION ); 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 < MAXCONNTRIES ) { 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; } int nciu::createChannelRequest () { int status = this->piiu->createChannelRequest ( *this ); if ( status == ECA_NORMAL ) { this->f_claimSent = true; } return status; } int nciu::read ( unsigned type, unsigned long countIn, cacNotify ¬ify ) { // // fail out if their arguments are invalid // if ( ! this->f_connected ) { return ECA_DISCONNCHID; } if ( INVALID_DB_REQ (type) ) { return ECA_BADTYPE; } if ( ! this->accessRightState.read_access ) { return ECA_NORDACCESS; } if ( countIn > UINT_MAX ) { return ECA_BADCOUNT; } if ( countIn == 0 ) { countIn = this->count; } return this->cacCtx.readNotifyRequest ( *this, notify, type, countIn ); } /* * check_a_dbr_string() */ static int check_a_dbr_string ( 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 ) { return ECA_STRTOBIG; } } pStr += MAX_STRING_SIZE; } return ECA_NORMAL; } int nciu::write ( unsigned type, unsigned long countIn, const void *pValue ) { // check this first so thet get a decent diagnostic if ( ! this->f_connected ) { return ECA_DISCONNCHID; } if ( ! this->accessRightState.write_access ) { return ECA_NOWTACCESS; } if ( countIn > this->count || countIn == 0 ) { return ECA_BADCOUNT; } if ( type == DBR_STRING ) { int status = check_a_dbr_string ( (char *) pValue, countIn ); if ( status != ECA_NORMAL ) { return status; } } return this->cacCtx.writeRequest ( *this, type, countIn, pValue ); } int nciu::write ( unsigned type, unsigned long countIn, const void *pValue, cacNotify ¬ify ) { // check this first so thet get a decent diagnostic if ( ! this->f_connected ) { return ECA_DISCONNCHID; } if ( ! this->accessRightState.write_access ) { return ECA_NOWTACCESS; } if ( countIn > this->count || countIn == 0 ) { return ECA_BADCOUNT; } if ( type == DBR_STRING ) { int status = check_a_dbr_string ( (char *) pValue, countIn ); if ( status != ECA_NORMAL ) { return status; } } return this->cacCtx.writeNotifyRequest ( *this, notify, type, countIn, pValue ); } int nciu::subscribe ( unsigned type, unsigned long nElem, unsigned mask, cacNotify ¬ify, cacNotifyIO *&pNotifyIO ) { if ( INVALID_DB_REQ(type) ) { return ECA_BADTYPE; } if ( mask > 0xffff || mask == 0u ) { return ECA_BADMASK; } cacNotifyIO * pIO = this->cacCtx.subscriptionRequest ( *this, type, nElem, mask, notify ); if ( pIO ) { pNotifyIO = pIO; return ECA_NORMAL;; } else { return ECA_ALLOCMEM; } } void nciu::initiateConnect () { this->notifyStateChangeFirstConnectInCountOfOutstandingIO (); this->cacCtx.installNetworkChannel ( *this, this->piiu ); } void nciu::hostName ( char *pBuf, unsigned bufLength ) const { epicsAutoMutex locker ( this->cacCtx.mutex() ); this->piiu->hostName ( pBuf, bufLength ); } // deprecated - please do not use, this is _not_ thread safe const char * nciu::pHostName () const { epicsAutoMutex locker ( this->cacCtx.mutex() ); return this->piiu->pHostName (); // ouch ! } bool nciu::ca_v42_ok () const { epicsAutoMutex locker ( this->cacCtx.mutex() ); return this->piiu->ca_v42_ok (); } short nciu::nativeType () const { epicsAutoMutex locker ( this->cacCtx.mutex() ); short type; if ( this->f_connected ) { if ( this->typeCode < SHRT_MAX ) { type = static_cast ( this->typeCode ); } else { type = TYPENOTCONN; } } else { type = TYPENOTCONN; } return type; } unsigned long nciu::nativeElementCount () const { epicsAutoMutex locker ( this->cacCtx.mutex() ); unsigned long countOut; if ( this->f_connected ) { countOut = this->count; } else { countOut = 0ul; } return countOut; } channel_state nciu::state () const { epicsAutoMutex locker ( this->cacCtx.mutex() ); channel_state stateOut; if ( this->f_connected ) { stateOut = cs_conn; } else if ( this->f_previousConn ) { stateOut = cs_prev_conn; } else { stateOut = cs_never_conn; } return stateOut; } caar nciu::accessRights () const { epicsAutoMutex locker ( this->cacCtx.mutex() ); caar tmp = this->accessRightState; return tmp; } unsigned nciu::searchAttempts () const { epicsAutoMutex locker ( this->cacCtx.mutex() ); return this->retry; } double nciu::beaconPeriod () const { epicsAutoMutex locker ( this->cacCtx.mutex() ); return this->piiu->beaconPeriod (); } void nciu::notifyStateChangeFirstConnectInCountOfOutstandingIO () { epicsAutoMutex locker ( this->cacCtx.mutex() ); // 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.mutex() ); 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.read_access ? "" : "no ", this->accessRightState.write_access ? "" : "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 ); printf ( "\tfully cunstructed boolean=%u\n", this->f_fullyConstructed ); } }