/* $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" #include "ioCounter_IL.h" tsFreeList < class nciu, 1024 > nciu::freeList; nciu::nciu ( cac &cacIn, cacChannel &chanIn, const char *pNameIn ) : cacChannelIO ( chanIn ), cacPrivateListOfIO ( 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->f_previousConn = false; this->f_connected = false; this->f_fullyConstructed = true; this->f_claimSent = false; this->retry = 0u; this->retrySeqNo = 0u; this->ptrLockCount = 0u; this->ptrUnlockWaitCount = 0u; this->cacCtx.registerChannel ( *this ); this->cacCtx.installDisconnectedChannel ( *this ); chanIn.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 idCopy; // // fail out if their arguments are 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 > UINT_MAX ) { return ECA_BADCOUNT; } if ( countIn == 0 ) { countIn = this->count; } bool success = netReadNotifyIO::factory ( *this, notify, idCopy ); if ( ! success ) { return ECA_ALLOCMEM; } this->lockPIIU (); if ( this->piiu ) { status = this->piiu->readNotifyRequest ( idCopy, 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 idCopy; 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 (), idCopy ); if ( ! success ) { return ECA_ALLOCMEM; } this->lockPIIU (); if ( this->piiu ) { status = this->piiu->readCopyRequest ( idCopy, 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; // check this first so thet get a decent diagnostic if ( ! this->f_connected ) { return ECA_DISCONNCHID; } 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; // check this first so thet get a decent diagnostic if ( ! this->f_connected ) { return ECA_DISCONNCHID; } 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 ( unsigned nativeType, unsigned long nativeCount, unsigned sidIn ) { 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; } this->lockPIIU (); bool v41Ok = this->piiu->ca_v41_ok (); this->unlockPIIU (); this->lock (); 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->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 ( ! v41Ok ) { this->accessRightsNotify ( this->ar ); } } void nciu::disconnect () { char hostNameBuf[64]; this->hostName ( hostNameBuf, sizeof (hostNameBuf) ); bool wasConnected; this->lock (); 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_claimSent = false; if ( this->f_connected ) { wasConnected = true; } else { wasConnected = false; } this->f_connected = false; this->unlock (); if ( wasConnected ) { /* * 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->channelList.remove ( *this ); if ( this->piiu->channelList.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 and so that // channels lacking a claim message prior to connecting // are located iiu.channelList.push ( *this ); this->piiu = &iiu; iiu.mutex.unlock (); this->unlockPIIU (); } void nciu::detachChanFromIIU () { this->lockPIIU (); if ( this->piiu ) { this->piiu->mutex.lock (); this->piiu->channelList.remove ( *this ); if ( this->piiu->channelList.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.readSequenceOfOutstandingIO (); } 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->f_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 idCopy; bool success = netSubscription::factory ( *this, type, countIn, static_cast (mask), notify, idCopy ); if ( success ) { return ECA_NORMAL; } else { return ECA_ALLOCMEM; } } void nciu::show ( unsigned level ) const { 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 ) { printf ( ", native type %s, native element count %u", dbf_type_to_text ( this->typeCode ), this->count ); printf ( ", %sread access, %swrite access", this->ar.read_access ? "" : "no ", this->ar.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, ptr lock count=%u, ptr unlock wait count=%u\n", this->piiu, this->ptrLockCount, this->ptrUnlockWaitCount ); 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 ); } } bool nciu::setClaimMsgCache ( claimMsgCache &cache ) { cache.clientId = this->id; cache.serverId = this->sid; if ( cache.v44 ) { unsigned len = strlen ( this->pNameStr ) + 1u; if ( cache.bufLen < len ) { unsigned newBufLen = 2 * len; char *pNewStr = new char [ newBufLen ]; if ( pNewStr ) { delete [] cache.pStr; cache.pStr = pNewStr; cache.bufLen = newBufLen; } else { return false; } } strcpy ( cache.pStr, this->pNameStr ); cache.currentStrLen = len; } else { cache.currentStrLen = 0u; } this->f_claimSent = true; return true; }