/* $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. Since it is possible * for the channel to exist when no IIU exists (because the user * created the channel), and because an IIU can disconnect and be * destroyed at any time, then it is necessary to hold a mutex while * the IIU pointer is in use. This mutex can not be the IIU's mutex * because the IIU's lock must not be held while waiting for a * message to be sent (otherwise a push pull deadlock can occur). */ #include "iocinf.h" #include "nciu_IL.h" #include "netReadCopyIO_IL.h" #include "netReadNotifyIO_IL.h" #include "netWriteNotifyIO_IL.h" #include "netSubscription_IL.h" #include "cac_IL.h" tsFreeList < class nciu, 1024 > nciu::freeList; nciu::nciu ( cac &cacIn, cacChannel &chan, const char *pNameIn ) : cacChannelIO ( chan ), cacPrivate ( cacIn ) { static const caar defaultAccessRights = { false, false }; size_t strcnt; strcnt = strlen ( pNameIn ) + 1; // second constraint is imposed by size field in protocol header if ( strcnt > MAX_UDP_SEND - sizeof ( caHdr ) || strcnt > 0xffff ) { throwWithLocation ( caErrorCode ( ECA_STRTOBIG ) ); } this->pNameStr = new char [ strcnt ]; if ( ! this->pNameStr ) { this->f_fullyConstructed = false; return; } strcpy ( this->pNameStr, pNameIn ); this->piiu = 0u; this->typeCode = USHRT_MAX; /* invalid initial type */ this->count = 0; /* invalid initial count */ this->sid = UINT_MAX; /* invalid initial server id */ this->ar = defaultAccessRights; this->nameLength = strcnt; this->previousConn = 0; this->f_connected = false; this->f_fullyConstructed = true; this->retry = 0u; this->retrySeqNo = 0u; this->ptrLockCount = 0u; this->ptrUnlockWaitCount = 0u; this->cacCtx.registerChannel ( *this ); this->cacCtx.installDisconnectedChannel ( *this ); chan.attachIO ( *this ); } void nciu::destroy () { delete this; } nciu::~nciu () { if ( ! this->fullyConstructed () ) { return; } // this must go in the derived class's destructor because // this calls virtual functions in the cacChannelIO base this->ioReleaseNotify (); this->destroyAllIO (); this->lockPIIU (); if ( this->f_connected && this->piiu ) { this->piiu->clearChannelRequest ( this->getId (), this->sid ); } this->detachChanFromIIU (); this->unlockPIIU (); this->cacCtx.unregisterChannel ( *this ); delete [] this->pNameStr; } int nciu::read ( unsigned type, unsigned long countIn, cacNotify ¬ify ) { int status; unsigned id; // // fail out if their arguments are invalid // if ( INVALID_DB_REQ (type) ) { return ECA_BADTYPE; } if ( ! this->ar.read_access ) { return ECA_NORDACCESS; } if ( countIn > UINT_MAX ) { return ECA_BADCOUNT; } if ( countIn == 0 ) { countIn = this->count; } bool success = netReadNotifyIO::factory ( *this, notify, id ); if ( ! success ) { return ECA_ALLOCMEM; } this->lockPIIU (); if ( this->piiu ) { status = this->piiu->readNotifyRequest ( id, this->sid, type, countIn ); } else { status = ECA_DISCONNCHID; } this->unlockPIIU (); if ( status != ECA_NORMAL ) { this->cacCtx.ioDestroy ( id ); } return status; } int nciu::read ( unsigned type, unsigned long countIn, void *pValue ) { unsigned id; bool success; int status; /* * fail out if channel isnt connected or arguments are * otherwise invalid */ if ( ! this->f_connected ) { return ECA_DISCONNCHID; } if ( INVALID_DB_REQ ( type ) ) { return ECA_BADTYPE; } if ( ! this->ar.read_access ) { return ECA_NORDACCESS; } if ( countIn > this->count || countIn > 0xffff ) { return ECA_BADCOUNT; } if ( countIn == 0 ) { countIn = this->count; } success = netReadCopyIO::factory ( *this, type, countIn, pValue, this->readSequence (), id ); if ( ! success ) { return ECA_ALLOCMEM; } this->lockPIIU (); if ( this->piiu ) { status = this->piiu->readCopyRequest ( id, this->sid, type, countIn ); } else { status = ECA_DISCONNCHID; } this->unlockPIIU (); if ( status != ECA_NORMAL ) { this->cacCtx.ioDestroy ( id ); } return status; } /* * check_a_dbr_string() */ LOCAL int check_a_dbr_string ( const char *pStr, const unsigned count ) { unsigned i; for ( i = 0; i < count; i++ ) { unsigned int strsize = 0; while ( 1 ) { if (strsize >= MAX_STRING_SIZE ) { return ECA_STRTOBIG; } if ( pStr[strsize] == '\0' ) { break; } strsize++; } pStr += MAX_STRING_SIZE; } return ECA_NORMAL; } #ifdef JUNKYARD /* * nciu::issuePut () */ int nciu::issuePut ( ca_uint16_t cmd, unsigned idIn, chtype type, unsigned long countIn, const void *pvalue ) { int status; caHdr hdr; unsigned postcnt; # ifdef CONVERSION_REQUIRED void *pCvrtBuf; # endif /*CONVERSION_REQUIRED*/ /* * fail out if the conn is down or the arguments are otherwise invalid */ if ( ! this->f_connected ) { return ECA_DISCONNCHID; } if ( INVALID_DB_REQ (type) ) { return ECA_BADTYPE; } /* * compound types not allowed */ if ( dbr_value_offset[type] ) { return ECA_BADTYPE; } if ( ! this->ar.write_access ) { return ECA_NOWTACCESS; } if ( countIn > this->count || countIn > 0xffff || countIn == 0 ) { return ECA_BADCOUNT; } if (type==DBR_STRING) { status = check_a_dbr_string ( (char *) pvalue, countIn ); if (status != ECA_NORMAL) { return status; } } postcnt = dbr_size_n ( type, countIn ); if ( postcnt > 0xffff ) { return ECA_TOLARGE; } if ( type == DBR_STRING && countIn == 1 ) { char *pstr = (char *) pvalue; postcnt = strlen (pstr) +1; } else { postcnt = dbr_size_n ( type, countIn ); } # ifdef CONVERSION_REQUIRED { unsigned i; void *pdest; unsigned size_of_one; size_of_one = dbr_size[type]; #error can we eliminate this? pCvrtBuf = pdest = this->cacCtx.mallocPutConvert ( postcnt ); if ( ! pdest ) { this->unlockPIIU (); return ECA_ALLOCMEM; } /* * No compound types here because these types are read only * and therefore only appropriate for gets or monitors * * I changed from a for to a while loop here to avoid bounds * checker pointer out of range error, and unused pointer * update when it is a single element. */ i=0; while ( TRUE ) { switch ( type ) { case DBR_LONG: * (dbr_long_t *) pdest = htonl ( ( * reinterpret_cast < const dbr_long_t * > (pvalue) ) ); break; case DBR_CHAR: * (dbr_char_t *) pdest = * (dbr_char_t *) pvalue; break; case DBR_ENUM: case DBR_SHORT: case DBR_PUT_ACKT: case DBR_PUT_ACKS: # if DBR_INT != DBR_SHORT # error DBR_INT != DBR_SHORT ? # endif /*DBR_INT != DBR_SHORT*/ * (dbr_short_t *) pdest = htons ( ( * reinterpret_cast < const dbr_short_t * > (pvalue) ) ); break; case DBR_FLOAT: dbr_htonf ( (dbr_float_t *) pvalue, (dbr_float_t *) pdest ); break; case DBR_DOUBLE: dbr_htond ( (dbr_double_t *) pvalue, (dbr_double_t *) pdest ); break; case DBR_STRING: /* * string size checked above */ strcpy ( (char *) pdest, (char *) pvalue ); break; default: return ECA_BADTYPE; } if ( ++i >= countIn ) { break; } pdest = ( (char *) pdest ) + size_of_one; pvalue = ( (char *) pvalue ) + size_of_one; } pvalue = pCvrtBuf; } # endif /*CONVERSION_REQUIRED*/ hdr.m_cmmd = htons ( cmd ); hdr.m_dataType = htons ( static_cast ( type ) ); hdr.m_count = htons ( static_cast ( countIn ) ); hdr.m_cid = this->sid; hdr.m_available = idIn; this->lockPIIU (); if ( this->piiu ) { status = this->piiu->pushStreamMsg ( &hdr, pvalue, postcnt ); } else { status = ECA_DISCONNCHID; } this->unlockPIIU (); # ifdef CONVERSION_REQUIRED this->cacCtx.freePutConvert ( pCvrtBuf ); # endif /*CONVERSION_REQUIRED*/ return status; } #endif int nciu::write ( unsigned type, unsigned long countIn, const void *pValue ) { int status; if ( ! this->ar.write_access ) { return ECA_NOWTACCESS; } if ( countIn > this->count || countIn == 0 ) { return ECA_BADCOUNT; } if ( type == DBR_STRING ) { status = check_a_dbr_string ( (char *) pValue, countIn ); if ( status != ECA_NORMAL ) { return status; } } this->lockPIIU (); if ( this->piiu ) { status = this->piiu->writeRequest ( this->sid, type, countIn, pValue ); } else { status = ECA_DISCONNCHID; } this->lockPIIU (); return status; } int nciu::write ( unsigned type, unsigned long countIn, const void *pValue, cacNotify ¬ify ) { ca_uint32_t newId; int status; if ( ! this->ar.write_access ) { return ECA_NOWTACCESS; } if ( countIn > this->count || countIn == 0 ) { return ECA_BADCOUNT; } if ( type == DBR_STRING ) { status = check_a_dbr_string ( (char *) pValue, countIn ); if ( status != ECA_NORMAL ) { return status; } } // dont use monix pointer because monix could be deleted // when the channel disconnects or when the IO completes bool success = netWriteNotifyIO::factory ( *this, notify, newId ); if ( ! success ) { return ECA_ALLOCMEM; } this->lockPIIU (); if ( this->piiu ) { status = this->piiu->writeNotifyRequest ( newId, this->sid, type, countIn, pValue ); } else { status = ECA_DISCONNCHID; } this->unlockPIIU (); if ( status != ECA_NORMAL ) { /* * we need to be careful about touching the monix * pointer after the lock has been released */ this->cacCtx.ioDestroy ( newId ); } return status; } void nciu::connect ( tcpiiu &iiu, unsigned nativeType, unsigned long nativeCount, unsigned sidIn ) { if ( this->f_connected ) { ca_printf ( "CAC: Ignored conn resp to conn chan CID=%u SID=%u?\n", this->getId (), sidIn ); return; } this->lock (); this->typeCode = nativeType; this->count = nativeCount; this->sid = sidIn; this->f_connected = true; this->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 ( ! iiu.ca_v41_ok () ) { this->ar.read_access = true; this->ar.write_access = true; } this->unlock (); // resubscribe for monitors from this channel this->subscribeAllIO (); this->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 ( ! iiu.ca_v41_ok () ) { this->accessRightsNotify ( this->ar ); } } void nciu::disconnect () { char hostNameBuf[64]; this->hostName ( hostNameBuf, sizeof (hostNameBuf) ); this->lock (); if ( ! this->f_connected ) { this->unlock (); return; } this->retry = 0u; this->typeCode = USHRT_MAX; this->count = 0u; this->sid = UINT_MAX; this->ar.read_access = false; this->ar.write_access = false; this->f_connected = false; this->unlock (); /* * look for events that have an event cancel in progress */ disconnectAllIO ( hostNameBuf ); this->disconnectNotify (); this->accessRightsNotify ( this->ar ); this->cacCtx.installDisconnectedChannel ( *this ); } /* * nciu::searchMsg () */ bool nciu::searchMsg ( unsigned short retrySeqNumber, unsigned &retryNoForThisChannel ) { caHdr msg; bool status; 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 (); this->lockPIIU (); if ( this->piiu ) { status = this->piiu->pushDatagramMsg ( msg, this->pNameStr, this->nameLength ); } else { status = false; } this->unlockPIIU (); if ( status ) { // // increment the number of times we have tried // to find this channel // this->lock (); if ( this->retry < MAXCONNTRIES ) { this->retry++; } this->retrySeqNo = retrySeqNumber; retryNoForThisChannel = this->retry; this->unlock (); } return status; } int nciu::subscriptionMsg ( unsigned subscriptionId, unsigned typeIn, unsigned long countIn, unsigned short maskIn) { int status; /* * clip to the native count and set to the native count if they * specify zero */ if ( countIn > this->count ){ countIn = this->count; } if ( this->f_connected ) { this->lockPIIU (); if ( this->piiu ) { status = this->piiu->subscriptionRequest ( subscriptionId, this->sid, typeIn, countIn, maskIn ); } else { status = ECA_NORMAL; } this->unlockPIIU (); } else { status = ECA_NORMAL; } return status; } void nciu::attachChanToIIU ( netiiu &iiu ) { this->lockPIIU (); if ( this->piiu ) { this->piiu->mutex.lock (); this->piiu->chidList.remove ( *this ); if ( this->piiu->chidList.count () == 0u ) { this->piiu->lastChannelDetachNotify (); } this->piiu->mutex.unlock (); } iiu.mutex.lock (); // add to the front of the list so that // search requests for new channels will be sent first iiu.chidList.push ( *this ); this->piiu = &iiu; iiu.mutex.unlock (); this->unlockPIIU (); } void nciu::detachChanFromIIU () { this->lockPIIU (); if ( this->piiu ) { this->piiu->mutex.lock (); this->piiu->chidList.remove ( *this ); if ( this->piiu->chidList.count () == 0u ) { this->piiu->lastChannelDetachNotify (); } this->piiu->mutex.unlock (); this->piiu = 0u; } this->unlockPIIU (); } void nciu::incrementOutstandingIO () { this->cacCtx.incrementOutstandingIO (); } void nciu::decrementOutstandingIO () { this->cacCtx.decrementOutstandingIO (); } void nciu::decrementOutstandingIO ( unsigned seqNumber ) { this->cacCtx.decrementOutstandingIO ( seqNumber ); } void nciu::lockOutstandingIO () const { this->cacCtx.lockOutstandingIO (); } void nciu::unlockOutstandingIO () const { this->cacCtx.unlockOutstandingIO (); } unsigned nciu::readSequence () const { return this->cacCtx.readSequence (); } void nciu::hostName ( char *pBuf, unsigned bufLength ) const { this->lockPIIU (); if ( this->piiu ) { this->piiu->hostName ( pBuf, bufLength ); } else { strncpy (pBuf, "", bufLength); pBuf[bufLength-1] = '\0'; } this->unlockPIIU (); } // deprecated - please do not use, this is _not_ thread safe const char * nciu::pHostName () const { this->lockPIIU (); const char *pName = this->piiu->pHostName (); this->unlockPIIU (); return pName; // ouch ! } bool nciu::ca_v42_ok () const { bool status; this->lockPIIU (); if ( this->piiu ) { status = this->piiu->ca_v42_ok (); } else { status = false; } this->unlockPIIU (); return status; } short nciu::nativeType () const { short type; this->lock (); if ( this->f_connected ) { type = static_cast ( this->typeCode ); } else { type = TYPENOTCONN; } this->unlock (); return type; } unsigned long nciu::nativeElementCount () const { unsigned long countOut; this->lock (); if ( this->f_connected ) { countOut = this->count; } else { countOut = 0ul; } this->unlock (); return countOut; } channel_state nciu::state () const { channel_state stateOut; this->lock (); if ( this->f_connected ) { stateOut = cs_conn; } else if ( this->previousConn ) { stateOut = cs_prev_conn; } else { stateOut = cs_never_conn; } this->unlock (); return stateOut; } caar nciu::accessRights () const { return this->ar; } const char *nciu::pName () const { return this->pNameStr; } unsigned nciu::searchAttempts () const { return this->retry; } int nciu::subscribe ( unsigned type, unsigned long countIn, unsigned mask, cacNotify ¬ify ) { unsigned id; bool success = netSubscription::factory ( *this, type, countIn, static_cast (mask), notify, id ); if ( success ) { return ECA_NORMAL; } else { return ECA_ALLOCMEM; } }