diff --git a/src/ca/Makefile b/src/ca/Makefile index 4126fc007..8e6b9f089 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -8,7 +8,10 @@ CMPLR = STRICT # # includes to install from this subproject # +INC += cadef.h +INC += caerr.h INC += caProto.h +INC += db_access.h #LIBSRCS += caOsDependent.c LIBSRCS += access.cpp diff --git a/src/ca/Makefile.Host b/src/ca/Makefile.Host index 0edad6c12..b234fd83f 100644 --- a/src/ca/Makefile.Host +++ b/src/ca/Makefile.Host @@ -14,13 +14,13 @@ INC += caProto.h # # on generic system -ca_SRCS_DEFAULT = if_depen.c +#ca_SRCS_DEFAULT = if_depen.c # on WIN32 only -ca_SRCS_WIN32 = windows_depen.c +#ca_SRCS_WIN32 = windows_depen.c # on vxWorks only -ca_SRCS_vxWorks = vxWorks_depen.c +#ca_SRCS_vxWorks = vxWorks_depen.c # on all systems ca_SRCS += caOsDependent.c diff --git a/src/ca/access.c b/src/ca/access.c deleted file mode 100644 index 372263730..000000000 --- a/src/ca/access.c +++ /dev/null @@ -1,3508 +0,0 @@ -/* $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: Jeffrey O. Hill - * - */ - - -static char *sccsId = "@(#) $Id$"; - -/* - * allocate error message string array - * here so I can use sizeof - */ -#define CA_ERROR_GLBLSOURCE - -/* - * allocate db_access message strings here - */ -#define DB_TEXT_GLBLSOURCE - -/* - * allocate header version strings here - */ -#define CAC_VERSION_GLOBAL - -#include "tsStamp.h" -#include "iocinf.h" -#include "sigPipeIgnore.h" - -#include "freeList.h" - -#ifdef iocCore -#include "dbEvent.h" -#endif - -/****************************************************************/ -/* Macros for syncing message insertion into send buffer */ -/****************************************************************/ -#define EXTMSGPTR(PIIU)\ - ((caHdr *) &(PIIU)->send.buf[(PIIU)->send.wtix]) - -/****************************************************************/ -/* Macros for error checking */ -/****************************************************************/ - -/* - * valid type check filters out disconnected channel - */ - -#define CHIXCHK(CHIX) \ -{ \ - if( (CHIX)->state != cs_conn || INVALID_DB_REQ((CHIX)->privType) ){ \ - return ECA_DISCONNCHID; \ - } \ -} - -/* type to detect them using deallocated channel in use block */ -#define TYPENOTINUSE (-2) - -/* allow them to add monitors to a disconnected channel */ -#define LOOSECHIXCHK(CHIX) \ -{ \ - if( (CHIX)->privType==TYPENOTINUSE ){ \ - return ECA_BADCHID; \ - } \ -} - -static caHdr nullmsg; - -/* - * local functions - */ - -LOCAL int cac_alloc_msg_no_flush( -struct ioc_in_use *piiu, -unsigned extsize, -caHdr **ppMsg -); -LOCAL int issue_get_callback( -evid monix, -ca_uint16_t cmmd -); -#ifdef iocCore -LOCAL void ca_event_handler( -void *usrArg, -struct dbAddr *paddr, -int hold, -db_field_log *pfl -); -LOCAL void ca_put_notify_action(PUTNOTIFY *ppn); -#endif -LOCAL void ca_pend_io_cleanup(); - -LOCAL int issue_ca_array_put -( -ca_uint16_t cmd, -unsigned id, -chtype type, -unsigned long count, -chid chix, -const void *pvalue -); -LOCAL void ca_default_exception_handler (struct exception_handler_args args); - -LOCAL int cac_push_msg( -struct ioc_in_use *piiu, -caHdr *pmsg, -const void *pext -); - -LOCAL int cac_push_msg_no_block( -struct ioc_in_use *piiu, -caHdr *pmsg, -const void *pext -); - -LOCAL void cac_add_msg (IIU *piiu); - -#ifdef CONVERSION_REQUIRED -LOCAL void *malloc_put_convert(unsigned long size); -LOCAL void free_put_convert(void *pBuf); -#endif - -LOCAL miu caIOBlockCreate(CA_STATIC *ca_static); - -LOCAL int check_a_dbr_string(const char *pStr, const unsigned count); - -/* - * - * cac_push_msg() - * - */ -LOCAL int cac_push_msg( -struct ioc_in_use *piiu, -caHdr *pmsg, -const void *pext -) -{ - CA_STATIC *ca_static = piiu->pcas; - int contig = piiu->sock_proto != IPPROTO_TCP; - caHdr msg; - unsigned bytesAvailable; - unsigned actualextsize; - unsigned extsize; - unsigned msgsize; - unsigned bytesSent; - - msg = *pmsg; - actualextsize = pmsg->m_postsize; - extsize = CA_MESSAGE_ALIGN(pmsg->m_postsize); - msg.m_postsize = htons((ca_uint16_t)extsize); - msgsize = extsize+sizeof(msg); - - - LOCK; - - /* - * Force contiguous header and body - * - * o Forces hdr and bdy into the same frame if UDP - * - * o Does not allow interleaved messages - * caused by removing recv backlog during - * send under UNIX. - * - * o Does not allow for messages larger than the - * ring buffer size. - */ - if (msgsize>piiu->send.max_msg) { - return ECA_TOLARGE; - } - - bytesAvailable = cacRingBufferWriteSize(&piiu->send, contig); - - if (bytesAvailablestate==iiu_connected) { - (*piiu->sendBytes)(piiu); - } - - /* - * if connection drops request - * cant be completed - */ - if (piiu->state!=iiu_connected) { - UNLOCK; - return ECA_DISCONNCHID; - } - - bytesAvailable = - cacRingBufferWriteSize(&piiu->send, contig); - - while(TRUE){ - struct timeval itimeout; - - /* - * record the time if we end up blocking so that - * we can time out - */ - if (bytesAvailable>=msgsize){ - piiu->sendPending = FALSE; - break; - } - else { - if (!piiu->sendPending) { - piiu->timeAtSendBlock = - ca_static->currentTime; - piiu->sendPending = TRUE; - } - } - - UNLOCK; - - LD_CA_TIME (cac_fetch_poll_period(ca_static), &itimeout); - cac_mux_io (ca_static,&itimeout, FALSE); - - LOCK; - - /* - * if connection drops request - * cant be completed - */ - if (piiu->state!=iiu_connected) { - UNLOCK; - return ECA_DISCONNCHID; - } - - bytesAvailable = cacRingBufferWriteSize( - &piiu->send, contig); - } - } - - - /* - * push the header onto the ring - */ - bytesSent = cacRingBufferWrite( - &piiu->send, - &msg, - sizeof(msg)); - assert(bytesSent == sizeof(msg)); - - /* - * push message body onto the ring - * - * (optionally encode in network format as we send) - */ - if (extsize>0u) { - bytesSent = cacRingBufferWrite( - &piiu->send, - pext, - actualextsize); - assert(bytesSent == actualextsize); - /* - * force pad bytes at the end of the message to nill - * if present (this avoids messages from purify) - */ - { - /* - * static variables are initialized to zero - */ - static char nullBuff[32]; - unsigned n; - - n = extsize-actualextsize; - if (n) { - assert(n<=sizeof(nullBuff)); - bytesSent = cacRingBufferWrite( - &piiu->send, - nullBuff, - n); - assert(bytesSent == n); - } - } - } - - UNLOCK; - - return ECA_NORMAL; -} - -/* - * - * cac_push_msg_no_block() - * - */ -LOCAL int cac_push_msg_no_block( -struct ioc_in_use *piiu, -caHdr *pmsg, -const void *pext) -{ - CA_STATIC *ca_static = piiu->pcas; - int contig = piiu->sock_proto != IPPROTO_TCP; - caHdr msg; - unsigned bytesAvailable; - unsigned actualextsize; - unsigned extsize; - unsigned msgsize; - unsigned bytesSent; - - msg = *pmsg; - actualextsize = pmsg->m_postsize; - extsize = CA_MESSAGE_ALIGN(pmsg->m_postsize); - msg.m_postsize = htons((ca_uint16_t)extsize); - msgsize = extsize+sizeof(msg); - - - LOCK; - - /* - * Force contiguous header and body - * - * o Forces hdr and bdy into the same frame if UDP - * - * o Does not allow interleaved messages - * caused by removing recv backlog during - * send under UNIX. - * - * o Does not allow for messages larger than the - * ring buffer size. - */ - if(msgsize>piiu->send.max_msg){ - return ECA_TOLARGE; - } - - bytesAvailable = cacRingBufferWriteSize(&piiu->send, contig); - - if (bytesAvailablesend, - &msg, - sizeof(msg)); - assert(bytesSent == sizeof(msg)); - - /* - * push message body onto the ring - * - * (optionally encode in netrwork format as we send) - */ - if (extsize>0u) { - bytesSent = cacRingBufferWrite( - &piiu->send, - pext, - actualextsize); - assert(bytesSent == actualextsize); - /* - * force pad bytes at the end of the message to nill - * if present (this avoids messages from purify) - */ - { - /* - * static variables are initialized to zero - */ - static char nullBuff[32]; - unsigned n; - - n = extsize-actualextsize; - if (n) { - assert(n<=sizeof(nullBuff)); - bytesSent = cacRingBufferWrite( - &piiu->send, - nullBuff, - n); - assert(bytesSent == n); - } - } - } - - UNLOCK; - - return ECA_NORMAL; -} - -/* - * - * cac_alloc_msg_no_flush() - * - * return a pointer to reserved message buffer space or - * nill if the message will not fit - * - * dont flush output if the message does not fit - * - * LOCK should be on - * - */ -LOCAL int cac_alloc_msg_no_flush( -struct ioc_in_use *piiu, -unsigned extsize, -caHdr **ppMsg -) -{ - unsigned msgsize; - unsigned long bytesAvailable; - caHdr *pmsg; - - /* - * This only works with UDP (because TCP must be allowed - * to wrap around from the end to the beg of the buffer) - */ - assert (piiu->sock_proto == IPPROTO_UDP); - - msgsize = sizeof(caHdr)+extsize; - - /* - * fail if max message size exceeded - */ - if(msgsize>=piiu->send.max_msg){ - return ECA_TOLARGE; - } - - bytesAvailable = cacRingBufferWriteSize (&piiu->send, TRUE); - if (bytesAvailablesend.buf[piiu->send.wtix]; - pmsg->m_postsize = extsize; - *ppMsg = pmsg; - - return ECA_NORMAL; -} - -/* - * cac_add_msg () - */ -LOCAL void cac_add_msg (IIU *piiu) -{ - unsigned long size; - caHdr *mp = EXTMSGPTR(piiu); - - /* - * Performs worst case message alignment - */ - mp->m_postsize = (unsigned short) - CA_MESSAGE_ALIGN(mp->m_postsize); - size = mp->m_postsize; - mp->m_postsize = htons(mp->m_postsize); - CAC_RING_BUFFER_WRITE_ADVANCE( - &piiu->send, - sizeof(caHdr) + size); -} - -/* - * ca_os_independent_init () - */ -int ca_os_independent_init (CA_STATIC *ca_static) -{ - long status; - - installSigPipeIgnore(); - - ca_static->ca_exception_func = ca_default_exception_handler; - ca_static->ca_exception_arg = NULL; - - caSetDefaultPrintfHandler(ca_static); - - /* record a default user name */ - ca_static->ca_pUserName = localUserName(); - if(!ca_static->ca_pUserName){ - return ECA_ALLOCMEM; - } - - /* record a default user name */ - ca_static->ca_pHostName = localHostName(); - if(!ca_static->ca_pHostName){ - free(ca_static->ca_pUserName); - return ECA_ALLOCMEM; - } - - cac_gettimeval (&ca_static->currentTime); - ca_static->programBeginTime = ca_static->currentTime; - - /* init sync group facility */ - ca_sg_init(ca_static); - - /* - * init broadcasted search counters - * (current time must be initialized before calling this) - */ - ca_static->ca_search_tries_congest_thresh = UINT_MAX; - ca_static->ca_search_responses = 0u; - ca_static->ca_search_tries = 0u; - ca_static->ca_search_retry_seq_no = 0u; - ca_static->ca_seq_no_at_list_begin = 0u; - ca_static->ca_frames_per_try = INITIALTRIESPERFRAME; - ca_static->ca_conn_next_retry = ca_static->currentTime; - cacSetRetryInterval (ca_static,0u); - - ellInit(&ca_static->ca_iiuList); - ellInit(&ca_static->ca_ioeventlist); - ellInit(&ca_static->ca_pend_read_list); - ellInit(&ca_static->ca_pend_write_list); - ellInit(&ca_static->putCvrtBuf); - ellInit(&ca_static->fdInfoFreeList); - ellInit(&ca_static->fdInfoList); - - freeListInitPvt(&ca_static->ca_ioBlockFreeListPVT, - sizeof(struct pending_event), 256); - - ca_static->ca_pSlowBucket = - bucketCreate(CLIENT_HASH_TBL_SIZE); - assert(ca_static->ca_pSlowBucket); - - ca_static->ca_pFastBucket = - bucketCreate(CLIENT_HASH_TBL_SIZE); - assert(ca_static->ca_pFastBucket); - - status = envGetDoubleConfigParam ( - &EPICS_CA_CONN_TMO, - &ca_static->ca_connectTMO); - if (status) { - ca_static->ca_connectTMO = - CA_CONN_VERIFY_PERIOD; - ca_printf ( - "EPICS \"%s\" float fetch failed\n", - EPICS_CA_CONN_TMO.name); - ca_printf ( - "Setting \"%s\" = %f\n", - EPICS_CA_CONN_TMO.name, - ca_static->ca_connectTMO); - } - - ca_static->ca_repeater_port = - caFetchPortConfig(&EPICS_CA_REPEATER_PORT, CA_REPEATER_PORT); - - ca_static->ca_server_port = - caFetchPortConfig(&EPICS_CA_SERVER_PORT, CA_SERVER_PORT); - - if (repeater_installed(ca_static)==FALSE) { - ca_spawn_repeater(); - } - - ca_static->ca_flush_pending = FALSE; - ca_static->ca_min_retry = UINT_MAX; - ca_static->ca_number_iiu_in_fc = 0u; - - return ECA_NORMAL; -} - -/* - * create_udp_fd - */ -void cac_create_udp_fd(CA_STATIC *ca_static) -{ - int status; - - if(ca_static->ca_piiuCast){ - return; - } - - status = create_net_chan( - ca_static, - &ca_static->ca_piiuCast, - NULL, - IPPROTO_UDP); - if (~status & CA_M_SUCCESS) { - ca_static->ca_piiuCast = NULL; - return; - } - -#ifdef iocCore - { - int pri; - threadId tid; - char name[64]; - cac_recv_taskArgs *pcac_recv_taskArgs; - - pri = threadGetPriority(threadGetIdSelf()); - strcpy(name,"RD"); - strncat( - name, - threadGetName(threadGetIdSelf()), - sizeof(name)-strlen(name)-1); - - /*the new thread MUST free cac_recv_taskArgs */ - pcac_recv_taskArgs = calloc(1,sizeof(cac_recv_taskArgs)); - pcac_recv_taskArgs->pcas = ca_static; - pcac_recv_taskArgs->tcas = threadGetIdSelf(); - tid = threadCreate(name, - pri-1, - threadGetStackSize(threadStackMedium), - (THREADFUNC)cac_recv_task, - (void *)pcac_recv_taskArgs); - if (tid==0) { - genLocalExcep (ECA_INTERNAL,NULL); - } - ca_static->recv_tid = tid; - } -#endif -} - -/* - * CA_MODIFY_HOST_NAME() - * - * Modify or override the default - * client host name. - * - * This entry point was changed to a NOOP - */ -int epicsShareAPI ca_modify_host_name(const char *pHostName) -{ - return ECA_NORMAL; -} - -/* - * CA_MODIFY_USER_NAME() - * - * Modify or override the default - * client user name. - * - * This entry point was changed to a NOOP - */ -int epicsShareAPI ca_modify_user_name(const char *pClientName) -{ - return ECA_NORMAL; -} - - -/* - * - * CA_TASK_EXIT_TID() / CA_PROCESS_EXIT() - * releases all resources alloc to a channel access client - * - * On multi thread os it is assumed that all threads are - * before calling this routine. - * - */ -void ca_process_exit(CA_STATIC *ca_static) -{ - chid chix; - chid chixNext; - IIU *piiu; - int status; - - assert (ca_static); - - LOCK; - - /* - * after activity eliminated - * close all sockets before clearing chid blocks and remote - * event blocks - */ - piiu = (struct ioc_in_use *) - ca_static->ca_iiuList.node.next; - while (piiu) { - if(ca_static->ca_fd_register_func){ - (*ca_static->ca_fd_register_func)( - (void *)ca_static->ca_fd_register_arg, - piiu->sock_chan, - FALSE); - } - if (socket_close(piiu->sock_chan) < 0){ - genLocalExcep ( ECA_INTERNAL, - "Problems closing a socket during ca shutdown?"); - } - piiu = (struct ioc_in_use *) piiu->node.next; - } - - /* - * remove remote chid blocks and event blocks - */ - piiu = (struct ioc_in_use *) - ca_static->ca_iiuList.node.next; - while(piiu){ - chix = (chid) ellFirst(&piiu->chidlist); - while (chix) { - chixNext = (chid) ellNext (&chix->node); - clearChannelResources (ca_static,chix->cid); - chix = chixNext; - } - - /* - * free message body cache - */ - if(piiu->pCurData){ - free(piiu->pCurData); - piiu->pCurData = NULL; - piiu->curDataMax = 0; - } - - /* - * free address list - */ - ellFree(&piiu->destAddr); - - piiu = (struct ioc_in_use *) piiu->node.next; - } - - /* remove any pending read blocks */ - caIOBlockListFree (ca_static, &ca_static->ca_pend_read_list, - NULL, FALSE, ECA_INTERNAL); - - /* remove any pending write blocks */ - caIOBlockListFree (ca_static, &ca_static->ca_pend_write_list, - NULL, FALSE, ECA_INTERNAL); - - /* remove any pending io event blocks */ - ellFree(&ca_static->ca_ioeventlist); - - /* remove put convert block free list */ - ellFree(&ca_static->putCvrtBuf); - - /* reclaim sync group resources */ - ca_sg_shutdown(ca_static); - - /* remove remote waiting ev blocks */ - freeListCleanup(ca_static->ca_ioBlockFreeListPVT); - - /* free select context lists */ - ellFree(&ca_static->fdInfoFreeList); - ellFree(&ca_static->fdInfoList); - - /* - * remove IOCs in use - */ - ellFree(&ca_static->ca_iiuList); - - /* - * free user name string - */ - if(ca_static->ca_pUserName){ - free(ca_static->ca_pUserName); - } - - /* - * free host name string - */ - if(ca_static->ca_pHostName){ - free(ca_static->ca_pHostName); - } - - /* - * free hash tables - */ - status = bucketFree (ca_static->ca_pSlowBucket); - assert (status == S_bucket_success); - status = bucketFree (ca_static->ca_pFastBucket); - assert (status == S_bucket_success); - - /* - * free beacon hash table - */ - freeBeaconHash(ca_static); - - UNLOCK; -} - -/* - * - * CA_BUILD_AND_CONNECT - * - * backwards compatible entry point to ca_search_and_connect() - */ -int epicsShareAPI ca_build_and_connect -( -const char *name_str, -chtype get_type, -unsigned long get_count, -chid * chixptr, -void *pvalue, -void (*conn_func) (struct connection_handler_args), -void *puser -) -{ - if(get_type != TYPENOTCONN && pvalue!=0 && get_count!=0){ - return ECA_ANACHRONISM; - } - - return ca_search_and_connect(name_str,chixptr,conn_func,puser); -} - -/* - * ca_search_and_connect() - */ -int epicsShareAPI ca_search_and_connect -( - const char *name_str, - chid *chixptr, - void (*conn_func) (struct connection_handler_args), - const void *puser -) -{ - long status; - ciu chix; - int strcnt; - CA_OSD_GET_CA_STATIC - - /* - * make sure that chix is NULL on fail - */ - *chixptr = NULL; - - INITCHK; - - /* Put some reasonable limit on user supplied string size */ - strcnt = strlen(name_str) + 1; - if (strcnt > MAX_UDP-sizeof(caHdr)) - return ECA_STRTOBIG; - if (strcnt == 1) - return ECA_EMPTYSTR; - - /* - * only for IOCs - */ -#ifdef iocCore - { - struct dbAddr tmp_paddr; - - /* Find out quickly if channel is on this node */ - status = db_name_to_addr(name_str, &tmp_paddr); - if (status == OK) { - int size; - - /* - * allocate CHIU (channel in use) block - * - * also allocate enough for the channel name & paddr - * block - */ - size = CA_MESSAGE_ALIGN(sizeof(*chix) + strcnt) + - sizeof(struct dbAddr); - *chixptr = chix = (ciu) calloc(1,size); - if (!chix){ - return ECA_ALLOCMEM; - } - chix->id.paddr = (struct dbAddr *) - (CA_MESSAGE_ALIGN(sizeof(*chix)+strcnt) + (char *)chix); - *chix->id.paddr = tmp_paddr; - chix->puser = puser; - chix->pConnFunc = conn_func; - chix->privType = chix->id.paddr->dbr_field_type; - chix->privCount = chix->id.paddr->no_elements; - chix->piiu = NULL; /* none */ - chix->state = cs_conn; - chix->ar.read_access = TRUE; - chix->ar.write_access = TRUE; - chix->retry = 0; - ellInit(&chix->eventq); - strncpy((char *)(chix + 1), name_str, strcnt); - - LOCK; - ellAdd(&local_chidlist, &chix->node); - - if (chix->pConnFunc) { - struct connection_handler_args args; - - args.chid = chix; - args.op = CA_OP_CONN_UP; - (*chix->pConnFunc) (args); - } - if (chix->pAccessRightsFunc) { - struct access_rights_handler_args args; - - args.chid = chix; - args.ar = chix->ar; - (*chix->pAccessRightsFunc) (args); - } - UNLOCK; - return ECA_NORMAL; - } - } -#endif - - if (!ca_static->ca_piiuCast) { - cac_create_udp_fd(ca_static); - if(!ca_static->ca_piiuCast){ - return ECA_NOCAST; - } - } - - /* allocate CHIU (channel in use) block */ - /* also allocate enough for the channel name */ - *chixptr = chix = (ciu) calloc(1, sizeof(*chix) + strcnt); - if (!chix){ - return ECA_ALLOCMEM; - } - - LOCK; - do { - chix->cid = CLIENT_SLOW_ID_ALLOC; - status = bucketAddItemUnsignedId (pSlowBucket, - &chix->cid, chix); - } while (status == S_bucket_idInUse); - - if (status != S_bucket_success) { - *chixptr = (chid) NULL; - free((char *) chix); - UNLOCK; - if (status == S_bucket_noMemory) { - return ECA_ALLOCMEM; - } - return ECA_INTERNAL; - } - - chix->puser = (void *) puser; - chix->pConnFunc = conn_func; - chix->privType = TYPENOTCONN; /* invalid initial type */ - chix->privCount = 0; /* invalid initial count */ - chix->id.sid = ~0U; /* invalid initial server id */ - chix->ar.read_access = FALSE; - chix->ar.write_access = FALSE; - - chix->state = cs_never_conn; - ellInit(&chix->eventq); - - /* Save this channels name for retry if required */ - strncpy((char *)(chix + 1), name_str, strcnt); - - addToChanList(chix, piiuCast); - - /* - * reset broadcasted search counters - */ - ca_static->ca_conn_next_retry = ca_static->currentTime; - cacSetRetryInterval (ca_static,0u); - - /* - * Connection Management takes care - * of sending the the search requests - */ - if (!chix->pConnFunc) { - SETPENDRECV; - } - UNLOCK; - - return ECA_NORMAL; -} - -/* - * SEARCH_MSG() - */ -int search_msg( -ciu chix, -int reply_type -) -{ - int status; - int size; - caHdr *mptr; - IIU *piiu; - CA_STATIC *ca_static; - - piiu = chix->piiu; - ca_static = piiu->pcas; - - if (piiu!=piiuCast) { - return ECA_INTERNAL; - } - - size = strlen((char *)(chix+1))+1; - - LOCK; - status = cac_alloc_msg_no_flush (piiu, size, &mptr); - if (status != ECA_NORMAL) { - UNLOCK; - return status; - } - - mptr->m_cmmd = htons (CA_PROTO_SEARCH); - mptr->m_available = chix->cid; - mptr->m_dataType = reply_type; - mptr->m_count = htons (CA_MINOR_VERSION); - mptr->m_cid = chix->cid; - - /* - * channel name string - forces a NULL at the end because - * strcnt is always >= strlen + 1 - */ - mptr++; - strncpy((char *)mptr, (char *)(chix + 1), size); - - cac_add_msg(piiu); - - /* - * increment the number of times we have tried this - * channel - */ - if (chix->retryretry++; - } - if (ca_static->ca_search_triesca_search_tries++; - } - - /* - * move the channel to the end of the list so - * that all channels get a equal chance - */ - ellDelete(&piiu->chidlist, &chix->node); - ellAdd(&piiu->chidlist, &chix->node); - - UNLOCK; - - return ECA_NORMAL; -} - -/* - * CA_ARRAY_GET() - * - * - */ -int epicsShareAPI ca_array_get -( -chtype type, -unsigned long count, -chid chix, -void *pvalue -) -{ - int status; - miu monix; - CA_STATIC *ca_static; - - CHIXCHK(chix); - - if (INVALID_DB_REQ(type)) - return ECA_BADTYPE; - - if(!chix->ar.read_access){ - return ECA_NORDACCESS; - } - - if (count > chix->privCount) { - return ECA_BADCOUNT; - } - -#ifdef iocCore - { - int status; - - if(!chix->piiu) { - status = db_get_field( - chix->id.paddr, - type, - pvalue, - count, - NULL); - if (status == OK) - return ECA_NORMAL; - else - return ECA_GETFAIL; - } - } -#endif - - /* - * lock around io block create and list add - * so that we are not deleted without - * reclaiming the resource - */ - ca_static = ((IIU *)chix->piiu)->pcas; - LOCK; - monix = caIOBlockCreate(ca_static); - if (monix) { - - monix->chan = chix; - monix->type = type; - monix->count = count; - monix->usr_arg = pvalue; - - ellAdd(&pend_read_list, &monix->node); - } - - if (monix) { - status = issue_get_callback(monix, CA_PROTO_READ); - if (status == ECA_NORMAL) { - SETPENDRECV; - } - else { - if (ca_state(chix)==cs_conn) { - ellDelete (&pend_read_list, &monix->node); - caIOBlockFree (ca_static,monix); - } - } - } - else { - status = ECA_ALLOCMEM; - } - UNLOCK; - return status; -} - -/* - * CA_ARRAY_GET_CALLBACK() - */ -int epicsShareAPI ca_array_get_callback -( -chtype type, -unsigned long count, -chid chix, -void (*pfunc) (struct event_handler_args), -const void *arg -) -{ - int status; - miu monix; - CA_STATIC *ca_static; - - CHIXCHK(chix); - - if (INVALID_DB_REQ(type)) - return ECA_BADTYPE; - - if (count > chix->privCount) - return ECA_BADCOUNT; - - if (pfunc==NULL) { - return ECA_BADFUNCPTR; - } - - if(!chix->ar.read_access){ - return ECA_NORDACCESS; - } - -#ifdef iocCore - if (!chix->piiu) { - struct pending_event ev; - - ev.usr_func = pfunc; - ev.usr_arg = arg; - ev.chan = chix; - ev.type = type; - ev.count = count; - ca_event_handler(&ev, chix->id.paddr, 0, NULL); - return ECA_NORMAL; - } -#endif - - /* - * lock around io block create and list add - * so that we are not deleted without - * reclaiming the resource - */ - ca_static = ((IIU *)chix->piiu)->pcas; - LOCK; - monix = caIOBlockCreate(ca_static); - if (monix) { - - monix->chan = chix; - monix->usr_func = pfunc; - monix->usr_arg = (void *) arg; - monix->type = type; - monix->count = count; - - ellAdd(&pend_read_list, &monix->node); - } - - if (monix) { - status = issue_get_callback(monix, CA_PROTO_READ_NOTIFY); - if (status != ECA_NORMAL) { - if (ca_state(chix)==cs_conn) { - ellDelete (&pend_read_list, &monix->node); - caIOBlockFree (ca_static,monix); - } - } - } - else { - status = ECA_ALLOCMEM; - } - UNLOCK; - - return status; -} - -/* - * caIOBlockCreate(ca_static) - */ -LOCAL miu caIOBlockCreate(CA_STATIC *ca_static) -{ - int status; - miu pIOBlock; - - LOCK; - - pIOBlock = (miu) freeListCalloc(ca_static->ca_ioBlockFreeListPVT); - if (pIOBlock) { - do { - pIOBlock->id = CLIENT_FAST_ID_ALLOC; - status = bucketAddItemUnsignedId( - pFastBucket, - &pIOBlock->id, - pIOBlock); - } while (status == S_bucket_idInUse); - - if(status != S_bucket_success){ - freeListFree(ca_static->ca_ioBlockFreeListPVT, - pIOBlock); - pIOBlock = NULL; - } - } - - UNLOCK; - - return pIOBlock; -} - -/* - * caIOBlockFree() - */ -void caIOBlockFree(CA_STATIC *ca_static,miu pIOBlock) -{ - int status; - - LOCK; - status = bucketRemoveItemUnsignedId( - ca_static->ca_pFastBucket, - &pIOBlock->id); - assert (status == S_bucket_success); - pIOBlock->id = ~0U; /* this id always invalid */ - freeListFree(ca_static->ca_ioBlockFreeListPVT, pIOBlock); - UNLOCK; -} - -/* - * Free all io blocks on the list attached to the specified channel - */ -void caIOBlockListFree( -CA_STATIC *ca_static, -ELLLIST *pList, -chid chan, -int cbRequired, -int status) -{ - miu monix; - miu next; - struct event_handler_args args; - - for (monix = (miu) ellFirst (pList); - monix; - monix = next) { - - next = (miu) ellNext (&monix->node); - - if (chan == NULL || monix->chan == chan) { - - ellDelete (pList, &monix->node); - - args.usr = (void *) monix->usr_arg; - args.chid = monix->chan; - args.type = monix->type; - args.count = monix->count; - args.status = status; - args.dbr = NULL; - - caIOBlockFree (ca_static, monix); - - if (cbRequired && monix->usr_func) { - (*monix->usr_func) (args); - } - } - } -} - -/* - * - * ISSUE_GET_CALLBACK() - * (lock must be on) - */ -LOCAL int issue_get_callback( -evid monix, -ca_uint16_t cmmd) -{ - int status; - chid chix = monix->chan; - ca_uint16_t count; - caHdr hdr; - struct ioc_in_use *piiu; - - piiu = chix->piiu; - - /* - * dont send the message if the conn is down - * (it will be sent once connected) - */ - if (chix->state != cs_conn) { - return ECA_DISCONNCHID; - } - - /* - * set to the native count if they specify zero - */ - if (monix->count == 0 || monix->count > chix->privCount){ - count = chix->privCount; - } - else{ - count = (ca_uint16_t) monix->count; - } - - /* msg header only on db read notify req */ - hdr.m_cmmd = htons (cmmd); - hdr.m_dataType = htons ((ca_uint16_t)monix->type); - hdr.m_count = htons (count); - hdr.m_available = monix->id; - hdr.m_postsize = 0; - hdr.m_cid = chix->id.sid; - - status = cac_push_msg (piiu, &hdr, 0); - - return status; -} - -/* - * CA_ARRAY_PUT_CALLBACK() - * - */ -int epicsShareAPI ca_array_put_callback -( -chtype type, -unsigned long count, -chid chix, -const void *pvalue, -void (*pfunc)(struct event_handler_args), -const void *usrarg -) -{ - IIU *piiu; - int status; - miu monix; - CA_OSD_GET_CA_STATIC - - /* - * valid channel id test - */ - CHIXCHK(chix); - - /* - * compound types not allowed - */ - if(dbr_value_offset[type]){ - return ECA_BADTYPE; - } - - if(!chix->ar.write_access){ - return ECA_NOWTACCESS; - } - - if (pfunc==NULL) { - return ECA_BADFUNCPTR; - } - - /* - * check for valid count - */ - if(count > chix->privCount || count == 0) - return ECA_BADCOUNT; - - piiu = chix->piiu; - - if (piiu) { - if(!CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number)){ - return ECA_NOSUPPORT; - } - } - - if (type==DBR_STRING) { - int status; - status = check_a_dbr_string(pvalue, count); - if (status != ECA_NORMAL) { - return status; - } - } - -#ifdef iocCore - if (!piiu) { - /* cast removes const */ - ciu pChan = (ciu) chix; - CACLIENTPUTNOTIFY *ppn; - unsigned size; - - size = dbr_size_n(type,count); - LOCK; - - ppn = pChan->ppn; - if(ppn){ - /* - * wait while it is busy - */ - if(ppn->busy){ - UNLOCK; - status = semBinaryTakeTimeout( - ca_static->ca_blockSem, - 60.0); - if(status != semTakeOK){ - return ECA_PUTCBINPROG; - } - LOCK; - } - - /* - * if not busy then free the current - * block if it is to small - */ - if(ppn->valueSizeppn = NULL; - } - } - - if(!ppn){ - ppn = (CACLIENTPUTNOTIFY *) - calloc(1, sizeof(*ppn)+size); - if(!ppn){ - UNLOCK; - return ECA_ALLOCMEM; - } - pChan->ppn = ppn; - ppn->pcas = ca_static; - ppn->dbPutNotify.userCallback = - ca_put_notify_action; - ppn->dbPutNotify.usrPvt = pChan; - ppn->dbPutNotify.paddr = chix->id.paddr; - ppn->dbPutNotify.pbuffer = (ppn+1); - } - ppn->busy = TRUE; - ppn->caUserCallback = pfunc; - ppn->caUserArg = (void *) usrarg; - ppn->dbPutNotify.nRequest = count; - memcpy(ppn->dbPutNotify.pbuffer, (char *)pvalue, size); - status = dbPutNotifyMapType(&ppn->dbPutNotify, type); - if(status){ - UNLOCK; - ppn->busy = FALSE; - return ECA_PUTFAIL; - } - status = dbPutNotify(&ppn->dbPutNotify); - UNLOCK; - if(status && status != S_db_Pending){ - if(status==S_db_Blocked){ - return ECA_PUTCBINPROG; - } - ppn->busy = FALSE; - return ECA_PUTFAIL; - } - return ECA_NORMAL; - } -#endif /*iocCore*/ - - /* - * lock around io block create and list add - * so that we are not deleted without - * reclaiming the resource - */ - LOCK; - monix = caIOBlockCreate(ca_static); - if (!monix) { - UNLOCK; - return ECA_ALLOCMEM; - } - ellAdd(&pend_write_list, &monix->node); - UNLOCK; - - monix->chan = chix; - monix->usr_func = pfunc; - monix->usr_arg = (void *) usrarg; - monix->type = type; - monix->count = count; - - status = issue_ca_array_put( - CA_PROTO_WRITE_NOTIFY, - monix->id, - type, - count, - chix, - pvalue); - if(status != ECA_NORMAL){ - LOCK; - if (ca_state(chix)==cs_conn) { - ellDelete (&pend_write_list, &monix->node); - caIOBlockFree(ca_static, monix); - } - UNLOCK; - return status; - } - - - return status; -} - -/* - * CA_PUT_NOTIFY_ACTION - */ -#ifdef iocCore -LOCAL void ca_put_notify_action(PUTNOTIFY *ppn) -{ - CACLIENTPUTNOTIFY *pcapn; - struct ioc_in_use *piiu; - struct CA_STATIC *pcas; - chid chix; - - /* - * we choose to suspend the task if there - * is an internal failure - */ - chix = (chid) ppn->usrPvt; - if(!chix){ - threadSuspend(threadGetIdSelf()); - } - if(!chix->ppn){ - threadSuspend(threadGetIdSelf()); - } - - piiu = chix->piiu; - pcapn = chix->ppn; - pcas = pcapn->pcas; - - /* - * independent lock used here in order to - * avoid any possibility of blocking - * the database (or indirectly blocking - * one client on another client). - */ - semMutexMustTake(pcas->ca_putNotifyLock); - ellAdd(&pcas->ca_putNotifyQue, &pcapn->node); - semMutexGive(pcas->ca_putNotifyLock); - - /* - * offload the labor for this to the - * event task so that we never block - * the db or another client. - */ - db_post_extra_labor(pcas->ca_evuser); - -} -#endif /*iocCore*/ - -/* - * CA_ARRAY_PUT() - * - * - */ -int epicsShareAPI ca_array_put ( - chtype type, - unsigned long count, - chid chix, - const void *pvalue -) -{ - /* - * valid channel id test - */ - CHIXCHK(chix); - - /* - * compound types not allowed - */ - if(dbr_value_offset[type]){ - return ECA_BADTYPE; - } - - if(!chix->ar.write_access){ - return ECA_NOWTACCESS; - } - - /* - * check for valid count - */ - if(count > chix->privCount || count == 0) - return ECA_BADCOUNT; - - if (type==DBR_STRING) { - int status; - status = check_a_dbr_string(pvalue, count); - if (status != ECA_NORMAL) { - return status; - } - } - -#ifdef iocCore - /* - * If channel is on this client's host then - * call the database directly - */ - if(!chix->piiu){ - int status; - - status = db_put_field( chix->id.paddr, - type, - pvalue, - count); - if(status==OK) - return ECA_NORMAL; - else - return ECA_PUTFAIL; - } -#endif /*iocCore*/ - - return issue_ca_array_put(CA_PROTO_WRITE, ~0U, type, count, chix, pvalue); -} - -/* - * 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; - - strsize = strlen(pStr) + 1; - - if (strsize>MAX_STRING_SIZE) { - return ECA_STRTOBIG; - } - - pStr += MAX_STRING_SIZE; - } - - return ECA_NORMAL; -} - -/* - * issue_ca_array_put() - */ -LOCAL int issue_ca_array_put -( -ca_uint16_t cmd, -unsigned id, -chtype type, -unsigned long count, -chid chix, -const void *pvalue -) -{ - int status; - struct ioc_in_use *piiu; - caHdr hdr; - int postcnt; - unsigned size_of_one; -# ifdef CONVERSION_REQUIRED - unsigned i; - void *pCvrtBuf; - void *pdest; -# endif /*CONVERSION_REQUIRED*/ - - piiu = chix->piiu; - size_of_one = dbr_size[type]; - postcnt = dbr_size_n(type,count); - - if (type == DBR_STRING && count == 1) { - char *pstr = (char *)pvalue; - - postcnt = strlen(pstr)+1; - } - -# ifdef CONVERSION_REQUIRED - pCvrtBuf = pdest = malloc_put_convert(postcnt); - if(!pdest){ - 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 (*(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 - case DBR_INT: -# endif /*DBR_INT != DBR_SHORT*/ - *(dbr_short_t *)pdest = htons (*(dbr_short_t *)pvalue); - break; - - case DBR_FLOAT: - dbr_htonf ((dbr_float_t *)pvalue, pdest); - break; - - case DBR_DOUBLE: - dbr_htond ((dbr_double_t *)pvalue, pdest); - break; - - case DBR_STRING: - /* - * string size checked above - */ - strcpy (pdest, pvalue); - break; - - default: - UNLOCK; - return ECA_BADTYPE; - } - - if (++i>=count) { - 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(((ca_uint16_t)type)); - hdr.m_count = htons(((ca_uint16_t)count)); - hdr.m_cid = chix->id.sid; - hdr.m_available = id; - hdr.m_postsize = (ca_uint16_t) postcnt; - - status = cac_push_msg (piiu, &hdr, pvalue); - -# ifdef CONVERSION_REQUIRED - free_put_convert (pCvrtBuf); -# endif /*CONVERSION_REQUIRED*/ - - return status; -} - -/* - * malloc_put_convert() - */ -#ifdef CONVERSION_REQUIRED -LOCAL void *malloc_put_convert(unsigned long size) -{ - struct putCvrtBuf *pBuf; - CA_OSD_GET_CA_STATIC - - LOCK; - pBuf = (struct putCvrtBuf *) ellFirst(&ca_static->putCvrtBuf); - while(pBuf){ - if(pBuf->size >= size){ - break; - } - pBuf = (struct putCvrtBuf *) ellNext(&pBuf->node); - } - - if(pBuf){ - ellDelete(&ca_static->putCvrtBuf, &pBuf->node); - } - UNLOCK; - - if(!pBuf){ - pBuf = (struct putCvrtBuf *) malloc(sizeof(*pBuf)+size); - if(!pBuf){ - return NULL; - } - pBuf->size = size; - pBuf->pBuf = (void *) (pBuf+1); - } - - return pBuf->pBuf; -} -#endif /* CONVERSION_REQUIRED */ - -/* - * free_put_convert() - */ -#ifdef CONVERSION_REQUIRED -LOCAL void free_put_convert(void *pBuf) -{ - struct putCvrtBuf *pBufHdr; - CA_OSD_GET_CA_STATIC - - pBufHdr = (struct putCvrtBuf *)pBuf; - pBufHdr -= 1; - assert(pBufHdr->pBuf == (void *)(pBufHdr+1)); - - LOCK; - ellAdd(&ca_static->putCvrtBuf, &pBufHdr->node); - UNLOCK; - - return; -} -#endif /* CONVERSION_REQUIRED */ - -/* - * Specify an event subroutine to be run for connection events - * - */ -int epicsShareAPI ca_change_connection_event -( -chid chix, -void (*pfunc)(struct connection_handler_args) -) -{ - ciu pChan = (ciu) chix; /* remove const */ - CA_OSD_GET_CA_STATIC - - INITCHK; - LOOSECHIXCHK(pChan); - - if(pChan->pConnFunc == pfunc) - return ECA_NORMAL; - - LOCK; - if (pChan->state==cs_never_conn) { - if(!pChan->pConnFunc){ - CLRPENDRECV; - } - if(!pfunc){ - SETPENDRECV; - } - } - pChan->pConnFunc = pfunc; - UNLOCK; - - return ECA_NORMAL; -} - -/* - * ca_replace_access_rights_event - */ -int epicsShareAPI ca_replace_access_rights_event( -chid chan, -void (*pfunc)(struct access_rights_handler_args)) -{ - ciu pChan = (ciu) chan; /* remove const */ - struct access_rights_handler_args args; - CA_OSD_GET_CA_STATIC - - INITCHK; - LOOSECHIXCHK(pChan); - - pChan->pAccessRightsFunc = pfunc; - - /* - * make certain that it runs at least once - */ - if(pChan->state == cs_conn && pChan->pAccessRightsFunc){ - args.chid = chan; - args.ar = chan->ar; - (*pChan->pAccessRightsFunc)(args); - } - - return ECA_NORMAL; -} - -/* - * Specify an event subroutine to be run for asynch exceptions - * - */ -int epicsShareAPI ca_add_exception_event -( - void (*pfunc)(struct exception_handler_args), - const void *arg -) -{ - CA_OSD_GET_CA_STATIC - - INITCHK; - LOCK; - if (pfunc) { - ca_static->ca_exception_func = pfunc; - ca_static->ca_exception_arg = arg; - } - else { - ca_static->ca_exception_func = ca_default_exception_handler; - ca_static->ca_exception_arg = NULL; - } - UNLOCK; - - return ECA_NORMAL; -} - -/* - * CA_ADD_MASKED_ARRAY_EVENT - * - * - */ -int epicsShareAPI ca_add_masked_array_event -( -chtype type, -unsigned long count, -chid chix, -void (*ast)(struct event_handler_args), -const void *astarg, -ca_real p_delta, -ca_real n_delta, -ca_real timeout, -evid *monixptr, -long mask -) -{ - ciu pChan = (ciu) chix; /* remove const */ - miu monix; - int status; - CA_OSD_GET_CA_STATIC - - INITCHK; - LOOSECHIXCHK(pChan); - - if(INVALID_DB_REQ(type)) - return ECA_BADTYPE; - - /* - * Check for huge waveform - * - * (the count is not checked here against the native count - * when connected because this introduces a race condition - * for the client tool - the requested count is clipped to - * the actual count when the monitor request is sent so - * verifying that the requested count is valid here isnt - * required) - */ - if(dbr_size_n(type,count)>MAX_MSG_SIZE-sizeof(caHdr)){ - return ECA_TOLARGE; - } - - if (ast==NULL) { - return ECA_BADFUNCPTR; - } - - if(!mask) - return ECA_BADMASK; - - /* - * lock around io block create and list add - * so that we are not deleted without - * reclaiming the resource - */ - LOCK; - - if (!pChan->piiu) { -# ifdef iocCore - monix = freeListMalloc (ca_static->ca_dbMonixFreeList); -# else - return ECA_INTERNAL; -# endif - } - else { - monix = caIOBlockCreate(ca_static); - } - - if(!monix){ - UNLOCK; - return ECA_ALLOCMEM; - } - - /* they dont have to supply one if they dont want to */ - if(monixptr){ - *monixptr = monix; - } - - monix->chan = chix; - monix->usr_func = ast; - monix->usr_arg = (void *) astarg; - monix->type = type; - monix->count = count; - monix->p_delta = p_delta; - monix->n_delta = n_delta; - monix->timeout = timeout; - monix->mask = (unsigned short) mask; - -# ifdef iocCore - if(!pChan->piiu){ - status = db_add_event( - evuser, - pChan->id.paddr, - ca_event_handler, - monix, - mask, - (struct event_block *)(monix+1)); - if(status == ERROR){ - UNLOCK; - freeListFree (ca_static->ca_dbMonixFreeList, monix); - return ECA_DBLCLFAIL; - } - - /* - * Place in the channel list - * - do it after db_add_event so there - * is no chance that it will be deleted - * at exit before it is completely created - */ - ellAdd(&pChan->eventq, &monix->node); - - /* - * force event to be called at least once - * return warning msg if they have made the queue to full - * to force the first (untriggered) event. - */ - if(db_post_single_event((struct event_block *)(monix+1))==ERROR){ - UNLOCK; - return ECA_OVEVFAIL; - } - - UNLOCK; - return ECA_NORMAL; - } -# endif - - /* It can be added to the list any place if it is remote */ - /* Place in the channel list */ - ellAdd(&pChan->eventq, &monix->node); - - UNLOCK; - - if(pChan->state == cs_conn){ - status = ca_request_event(monix); - if (status != ECA_NORMAL) { - LOCK; - if (ca_state(pChan)==cs_conn) { - ellDelete (&pChan->eventq, &monix->node); - caIOBlockFree(ca_static, monix); - } - UNLOCK - } - return status; - } - else{ - return ECA_NORMAL; - } -} - -/* - * CA_REQUEST_EVENT() - */ -int ca_request_event(evid monix) -{ - int status; - chid chix = monix->chan; - ca_uint16_t count; - struct monops msg; - struct ioc_in_use *piiu; - ca_float32_t p_delta; - ca_float32_t n_delta; - ca_float32_t tmo; - - piiu = chix->piiu; - - /* - * dont send the message if the conn is down - * (it will be sent once connected) - */ - if(chix->state != cs_conn){ - return ECA_DISCONNCHID; - } - - /* - * clip to the native count and set to the native count if they - * specify zero - */ - if (monix->count > chix->privCount || monix->count == 0){ - count = chix->privCount; - } - else{ - count = (ca_uint16_t) monix->count; - } - - /* msg header */ - msg.m_header.m_cmmd = htons(CA_PROTO_EVENT_ADD); - msg.m_header.m_available = monix->id; - msg.m_header.m_dataType = htons((ca_uint16_t)monix->type); - msg.m_header.m_count = htons(count); - msg.m_header.m_cid = chix->id.sid; - msg.m_header.m_postsize = sizeof(msg.m_info); - - /* msg body */ - p_delta = (ca_float32_t) monix->p_delta; - n_delta = (ca_float32_t) monix->n_delta; - tmo = (ca_float32_t) monix->timeout; - dbr_htonf(&p_delta, &msg.m_info.m_hval); - dbr_htonf(&n_delta, &msg.m_info.m_lval); - dbr_htonf(&tmo, &msg.m_info.m_toval); - msg.m_info.m_mask = htons(monix->mask); - msg.m_info.m_pad = 0; /* allow future use */ - - status = cac_push_msg(piiu, &msg.m_header, &msg.m_info); - - return status; -} - -/* - * - * CA_EVENT_HANDLER() - * (only for local (for now iocCore) clients) - * - */ -#ifdef iocCore -LOCAL void ca_event_handler( -void *usrArg, -struct dbAddr *paddr, -int hold, -db_field_log *pfl -) -{ - miu monix = (miu) usrArg; - int status; - int count; - union db_access_val valbuf; - void *pval; - unsigned size; - struct tmp_buff{ - ELLNODE node; - unsigned size; - }; - struct tmp_buff *pbuf = NULL; - CA_OSD_GET_CA_STATIC - - /* - * clip to the native count - * and set to the native count if they specify zero - */ - if(monix->count > monix->chan->privCount || monix->count == 0){ - count = monix->chan->privCount; - } - else{ - count = monix->count; - } - - if(monix->type == paddr->dbr_field_type){ - pval = paddr->pfield; - status = OK; - } - else{ - size = dbr_size_n(monix->type,count); - - if( size <= sizeof(valbuf) ){ - pval = (void *) &valbuf; - } - else{ - - /* - * find a preallocated block which fits - * (stored with largest block first) - */ - LOCK; - pbuf = (struct tmp_buff *) - lcl_buff_list.node.next; - if(pbuf && pbuf->size >= size){ - ellDelete( - &lcl_buff_list, - &pbuf->node); - }else - pbuf = NULL; - UNLOCK; - - /* - * test again so malloc is not inside LOCKED - * section - */ - if(!pbuf){ - pbuf = (struct tmp_buff *) - malloc(sizeof(*pbuf)+size); - if(!pbuf){ - ca_printf("%s: No Mem, Event Discarded\n", - __FILE__); - return; - } - pbuf->size = size; - } - pval = (void *) (pbuf+1); - } - } - - - status = db_get_field( paddr, - monix->type, - pval, - count, - pfl); - - /* - * Call user's callback - */ - LOCK; - if (monix->usr_func) { - struct event_handler_args args; - - args.usr = (void *) monix->usr_arg; - args.chid = monix->chan; - args.type = monix->type; - args.count = count; - args.dbr = pval; - - if(status == OK){ - args.status = ECA_NORMAL; - } - else{ - args.status = ECA_GETFAIL; - } - - (*monix->usr_func)(args); - } - - /* - * - * insert the buffer back into the que in size order if - * one was used. - */ - if(pbuf){ - struct tmp_buff *ptbuf; - - for( ptbuf = (struct tmp_buff *) lcl_buff_list.node.next; - ptbuf; - ptbuf = (struct tmp_buff *) pbuf->node.next){ - - if(ptbuf->size <= pbuf->size){ - break; - } - } - if(ptbuf) - ptbuf = (struct tmp_buff *) ptbuf->node.previous; - - ellInsert( - &lcl_buff_list, - &ptbuf->node, - &pbuf->node); - } - UNLOCK; - - return; -} -#endif - -/* - * - * CA_CLEAR_EVENT(MONIX) - * - * Cancel an outstanding event for a channel. - * - * NOTE: returns before operation completes in the server - * (and the client). - * This is a good reason not to allow them to make the monix - * block as part of a larger structure. - * Nevertheless the caller is gauranteed that his specified - * event is disabled and therefore will not run (from this source) - * after leaving this routine. - * - */ -int epicsShareAPI ca_clear_event (evid monix) -{ - ciu chix = (ciu) monix->chan; /* cast removes const */ - miu pMon = (miu) monix; /* cast removes const */ - int status; - caHdr hdr; - evid lkup; - CA_OSD_GET_CA_STATIC - - /* - * is it a valid channel ? - */ - LOOSECHIXCHK(chix); - - /* - * is it a valid monitor id - */ - if (chix->piiu) { - LOCK; - lkup = (evid) bucketLookupItemUnsignedId( - pFastBucket, - &pMon->id); - UNLOCK; - if (lkup != pMon) { - return ECA_BADMONID; - } - } - - /* disable any further events from this event block */ - pMon->usr_func = NULL; - -#ifdef iocCore - if (!chix->piiu) { - register int status; - - /* - * dont allow two threads to delete the same moniitor at once - */ - LOCK; - ellDelete(&chix->eventq, &pMon->node); - status = db_cancel_event((struct event_block *)(pMon + 1)); - UNLOCK; - freeListFree (ca_static->ca_dbMonixFreeList, pMon); - - return ECA_NORMAL; - } -#endif /*iocCore*/ - - /* - * dont send the message if the conn is down (just delete from the - * queue and return) - * - * check for conn down while locked to avoid a race - */ - if(chix->state == cs_conn){ - struct ioc_in_use *piiu; - - piiu = chix->piiu; - - /* msg header */ - hdr.m_cmmd = htons(CA_PROTO_EVENT_CANCEL); - hdr.m_available = pMon->id; - hdr.m_dataType = htons(chix->privType); - hdr.m_count = htons(chix->privCount); - hdr.m_cid = chix->id.sid; - hdr.m_postsize = 0; - - status = cac_push_msg(piiu, &hdr, NULL); - - /* - * NOTE: I free the monitor block only - * after confirmation from IOC - */ - } - else{ - LOCK; - ellDelete(&chix->eventq, &pMon->node); - caIOBlockFree(ca_static, pMon); - UNLOCK; - status = ECA_NORMAL; - } - - return status; -} - -/* - * - * CA_CLEAR_CHANNEL(CHID) - * - * clear the resources allocated for a channel by search - * - * NOTE: returns before operation completes in the server - * (and the client). - * This is a good reason not to allow them to make the monix - * block part of a larger structure. - * Nevertheless the caller is gauranteed that his specified - * event is disabled and therefore will not run - * (from this source) after leaving this routine. - * - */ -int epicsShareAPI ca_clear_channel (chid pChan) -{ - miu monix; - int status; - struct ioc_in_use *piiu = pChan->piiu; - caHdr hdr; - caCh *pold_ch; - enum channel_state old_chan_state; - CA_OSD_GET_CA_STATIC - - if (pChan->piiu) { - pChan = bucketLookupItemUnsignedId - (ca_static->ca_pSlowBucket, &pChan->cid); - if (pChan == NULL) { - return ECA_BADCHID; - } - } - else { - LOOSECHIXCHK(pChan); - } - - /* disable their further use of deallocated channel */ - pChan->privType = TYPENOTINUSE; - old_chan_state = pChan->state; - pChan->state = cs_closed; - pChan->pAccessRightsFunc = NULL; - pold_ch = pChan->pConnFunc; - pChan->pConnFunc = NULL; - - /* the while is only so I can break to the lock exit */ - LOCK; - /* disable any further events from this channel */ - for (monix = (miu) pChan->eventq.node.next; - monix; - monix = (miu) monix->node.next) - monix->usr_func = NULL; - /* disable any further get callbacks from this channel */ - for (monix = (miu) pend_read_list.node.next; - monix; - monix = (miu) monix->node.next) - if (monix->chan == pChan) - monix->usr_func = NULL; - /* disable any further put callbacks from this channel */ - for (monix = (miu) pend_write_list.node.next; - monix; - monix = (miu) monix->node.next) - if (monix->chan == pChan) - monix->usr_func = NULL; - -#ifdef iocCore - if (!pChan->piiu) { - CACLIENTPUTNOTIFY *ppn; - int status; - - /* - * clear out the events for this channel - */ - while ( (monix = (miu) ellGet(&pChan->eventq)) ) { - status = db_cancel_event((struct event_block *)(monix + 1)); - assert (status == OK); - freeListFree (ca_static->ca_dbMonixFreeList, monix); - } - - /* - * cancel any outstanding put notifies - */ - if(pChan->ppn){ - ppn = pChan->ppn; - if(ppn->busy){ - dbNotifyCancel(&ppn->dbPutNotify); - } - free(ppn); - } - - /* - * clear out this channel - */ - ellDelete(&local_chidlist, &pChan->node); - free((char *) pChan); - - UNLOCK; - return ECA_NORMAL; - } -#endif - - /* - * if this channel does not have a connection handler - * and it has not connected for the first time then clear the - * outstanding IO count - */ - if (old_chan_state == cs_never_conn && !pold_ch) { - CLRPENDRECV; - } - - /* - * dont send the message if not conn - * (just delete from the queue and return) - * - * check for conn state while locked to avoid a race - */ - if (old_chan_state != cs_conn) { - UNLOCK; - clearChannelResources (ca_static,pChan->cid); - return ECA_NORMAL; - } - - /* - * clear events and all other resources for this chid on the - * IOC - */ - - /* msg header */ - hdr.m_cmmd = htons(CA_PROTO_CLEAR_CHANNEL); - hdr.m_available = pChan->cid; - hdr.m_cid = pChan->id.sid; - hdr.m_dataType = htons(0); - hdr.m_count = htons(0); - hdr.m_postsize = 0; - - status = cac_push_msg(piiu, &hdr, NULL); - - /* - * NOTE: I free the chid and monitor blocks only after - * confirmation from IOC - */ - - UNLOCK; - - return status; -} - -/* - * clearChannelResources() - */ -void clearChannelResources(CA_STATIC *ca_static,unsigned id) -{ - struct ioc_in_use *piiu; - ciu chix; - int status; - - LOCK; - - chix = bucketLookupItemUnsignedId (ca_static->ca_pSlowBucket, &id); - if (chix==NULL) { - UNLOCK; - genLocalExcep (ECA_BADCHID,"clearChannelResources()"); - return; - } - - piiu = chix->piiu; - - /* - * remove any orphaned get callbacks for this channel - */ - caIOBlockListFree (ca_static, &ca_static->ca_pend_read_list, chix, - FALSE, ECA_INTERNAL); - - /* - * remove any orphaned put callbacks for this channel - */ - caIOBlockListFree (ca_static, &ca_static->ca_pend_write_list, chix, - FALSE, ECA_INTERNAL); - - /* - * remove any monitors still attached to this channel - */ - caIOBlockListFree (ca_static, &chix->eventq, NULL, FALSE, ECA_INTERNAL); - - status = bucketRemoveItemUnsignedId ( - ca_static->ca_pSlowBucket, &chix->cid); - assert (status == S_bucket_success); - removeFromChanList (chix); - free (chix); - - UNLOCK; -} - - -/************************************************************************/ -/* This routine pends waiting for channel events and calls the */ -/* timeout is specified as 0 infinite timeout is assumed. */ -/* if the argument early is specified TRUE then CA_NORMAL is */ -/* returned early (prior to timeout experation) when outstanding */ -/* IO completes. */ -/* Output buffers are flushed by this routine */ -/************************************************************************/ -int epicsShareAPI ca_pend (ca_real timeout, int early) -{ - struct timeval beg_time; - ca_real delay; - struct timeval tmo; - CA_OSD_GET_CA_STATIC - - INITCHK; - - if (EVENTLOCKTEST) { - return ECA_EVDISALLOW; - } - - cac_gettimeval (&ca_static->currentTime); - - /* - * Flush the send buffers - * (guarantees that we wait for all send buffer to be - * flushed even if this requires blocking) - */ - ca_static->ca_flush_pending = TRUE; - - if(pndrecvcnt==0u && early){ - /* - * force the flush - */ - CLR_CA_TIME (&tmo); - cac_mux_io(ca_static,&tmo, TRUE); - return ECA_NORMAL; - } - - if(timeout<0.0){ - /* - * force the flush - */ - CLR_CA_TIME (&tmo); - cac_mux_io(ca_static,&tmo, TRUE); - return ECA_TIMEOUT; - } - - beg_time = ca_static->currentTime; - delay = 0.0; - while(TRUE){ - ca_real remaining; - - if (pndrecvcnt==0 && early) { - /* - * force the flush - */ - CLR_CA_TIME (&tmo); - cac_mux_io(ca_static,&tmo, TRUE); - return ECA_NORMAL; - } - if(timeout == 0.0){ - remaining = cac_fetch_poll_period(ca_static); - } - else{ - remaining = timeout-delay; - /* - * Allow for CA background labor - */ - remaining = min(cac_fetch_poll_period(ca_static), remaining); - } - - - /* - * If we are not waiting for any significant delay - * then force the delay to zero so that we avoid - * scheduling delays (which can be substantial - * on some os) - */ -# ifdef CLOCKS_PER_SEC -# define CAC_SIGNIF_INTERVAL (1.0/CLOCKS_PER_SEC) -# else - /* - * we guess (because gcc does not provide - * CLOCKS_PER_SEC under sunos4) - */ -# define CAC_SIGNIF_INTERVAL (1.0/1000000) -# endif - if (remaining <= CAC_SIGNIF_INTERVAL) { - if(early){ - ca_pend_io_cleanup(); - ca_static->ca_flush_pending = TRUE; - } - /* - * be certain that we processed - * recv backlog at least once - */ - /* - * force the flush - */ - CLR_CA_TIME (&tmo); - /* - * unfortunately this causes additional messages - * to be read and so it is possible in rare circumstances - * for ECA_TIMEOUT to be returned when the IO completed - * during the pend io timeout clean up phase. - */ - cac_block_for_io_completion (ca_static,&tmo); - return ECA_TIMEOUT; - } - tmo.tv_sec = (long) remaining; - tmo.tv_usec = (long) ((remaining-tmo.tv_sec)*USEC_PER_SEC); - cac_block_for_io_completion (ca_static,&tmo); - - if (timeout != 0.0) { - delay = cac_time_diff (&ca_static->currentTime, - &beg_time); - } - } -} - -/* - * cac_fetch_poll_period() - */ -double cac_fetch_poll_period(CA_STATIC *ca_static) -{ - if (!piiuCast) { - return SELECT_POLL_NO_SEARCH; - } - else if (ellCount(&piiuCast->chidlist)==0) { - return SELECT_POLL_NO_SEARCH; - } - else { - return SELECT_POLL_SEARCH; - } -} - - -void cac_gettimeval(struct timeval *pt) -{ - TS_STAMP ts; - int status; - - tsStampGetCurrent(&ts); - pt->tv_sec = ts.secPastEpoch; - pt->tv_usec = ts.nsec/1000; -} - - -/* - * cac_time_diff() - */ -ca_real cac_time_diff (ca_time *pTVA, ca_time *pTVB) -{ - ca_real delay; - ca_real udelay; - - /* - * works with unsigned tv_sec in struct timeval - * under HPUX - */ - if (pTVA->tv_sec>pTVB->tv_sec) { - delay = pTVA->tv_sec - pTVB->tv_sec; - } - /* - * only assume an overflow has occurred when the - * delay 3/4 of the total range - */ - else if ( pTVB->tv_sec-pTVA->tv_sec < 3*(ULONG_MAX/4) ) { - /* - * return a negative delay - */ - delay = pTVB->tv_sec - pTVA->tv_sec; - delay = -delay; - } - else { - delay = 1 + ((unsigned long)pTVA->tv_sec); - delay += (ULONG_MAX - (unsigned long)pTVB->tv_sec); - } - - if(pTVA->tv_usec>pTVB->tv_usec){ - udelay = pTVA->tv_usec - pTVB->tv_usec; - } - else{ - delay -= 1.0; - udelay = (USEC_PER_SEC - pTVB->tv_usec) + pTVA->tv_usec + 1.0; - } - delay += udelay / USEC_PER_SEC; - - return delay; -} - -/* - * cac_time_sum() - */ -ca_time cac_time_sum (ca_time *pTVA, ca_time *pTVB) -{ - long usum; - ca_time sum; - - sum.tv_sec = pTVA->tv_sec + pTVB->tv_sec; - usum = pTVA->tv_usec + pTVB->tv_usec; - if(usum>=USEC_PER_SEC){ - sum.tv_sec++; - sum.tv_usec = usum-USEC_PER_SEC; - } - else{ - sum.tv_usec = usum; - } - - return sum; -} - -/* - * noopConnHandler() - * This is installed into channels which dont have - * a connection handler when ca_pend_io() times - * out so that we will not decrement the pending - * recv count in the future. - */ -LOCAL void noopConnHandler(struct connection_handler_args args) -{ -} - -/* - * - * set pending IO count back to zero and - * send a sync to each IOC and back. dont - * count reads until we recv the sync - * - */ -LOCAL void ca_pend_io_cleanup() -{ - struct ioc_in_use *piiu; - ciu pchan; - CA_OSD_GET_CA_STATIC - - LOCK; - pchan = (ciu) ellFirst (&piiuCast->chidlist); - while (pchan) { - if (!pchan->pConnFunc) { - pchan->pConnFunc = noopConnHandler; - } - pchan = (ciu) ellNext (&pchan->node); - } - for( piiu = (IIU *) iiuList.node.next; - piiu; - piiu = (IIU *) piiu->node.next){ - - caHdr hdr; - - if (piiu->state!=iiu_connected || piiu==piiuCast) { - continue; - } - - piiu->cur_read_seq++; - - hdr = nullmsg; - hdr.m_cmmd = htons(CA_PROTO_READ_SYNC); - cac_push_msg(piiu, &hdr, NULL); - } - UNLOCK; - pndrecvcnt = 0u; -} - -/* - * CA_FLUSH_IO() - * reprocess connection state and - * flush the send buffer - */ -int epicsShareAPI ca_flush_io() -{ - struct timeval timeout; - CA_OSD_GET_CA_STATIC - - INITCHK; - - /* - * Wait for all send buffers to be flushed - * while performing socket io and processing recv backlog - */ - ca_static->ca_flush_pending = TRUE; - CLR_CA_TIME (&timeout); - cac_mux_io (ca_static,&timeout, TRUE); - - return ECA_NORMAL; -} - -/* - * CA_TEST_IO () - * - */ -int epicsShareAPI ca_test_io() -{ - CA_OSD_GET_CA_STATIC - if(pndrecvcnt==0u){ - return ECA_IODONE; - } - else{ - return ECA_IOINPROGRESS; - } -} - -/* - * genLocalExcepWFL () - * (generate local exception with file and line number) - */ -void genLocalExcepWFL (long stat, const char *ctx, -const char *pFile, unsigned lineNo) -{ - struct exception_handler_args args; - CA_OSD_GET_CA_STATIC - - args.usr = (void *) ca_static->ca_exception_arg; - args.chid = NULL; - args.type = -1; - args.count = 0u; - args.addr = NULL; - args.stat = stat; - args.op = CA_OP_OTHER; - args.ctx = ctx; - args.pFile = pFile; - args.lineNo = lineNo; - - /* - * dont lock if there is no CA context - */ - if (ca_static==NULL) { - ca_default_exception_handler (args); - } - /* - * NOOP if they disable exceptions - */ - else if (ca_static->ca_exception_func!=NULL) { - LOCK; - (*ca_static->ca_exception_func) (args); - UNLOCK; - } -} - -/* - * CA_SIGNAL() - */ -void epicsShareAPI ca_signal(long ca_status, const char *message) -{ - ca_signal_with_file_and_lineno(ca_status, message, NULL, 0); -} - -/* - * ca_message (long ca_status) - * - * - if it is an unknown error code then it possible - * that the error string generated below - * will be overwritten before (or while) the caller - * of this routine is calling this routine - * (if they call this routine again). - */ -READONLY char * epicsShareAPI ca_message (long ca_status) -{ - unsigned msgNo = CA_EXTRACT_MSG_NO(ca_status); - - if( msgNo < NELEMENTS(ca_message_text) ){ - return ca_message_text[msgNo]; - } - else { - return "new CA message number known only by server - see caerr.h"; - } -} - -/* - * ca_signal_with_file_and_lineno() - */ -void epicsShareAPI ca_signal_with_file_and_lineno( -long ca_status, -const char *message, -const char *pfilenm, -int lineno) -{ - ca_signal_formated (ca_status, pfilenm, lineno, message); -} - -/* - * ca_signal_formated() - */ -void epicsShareAPI ca_signal_formated (long ca_status, const char *pfilenm, - int lineno, const char *pFormat, ...) -{ - va_list theArgs; - static const char *severity[] = - { - "Warning", - "Success", - "Error", - "Info", - "Fatal", - "Fatal", - "Fatal", - "Fatal" - }; - - va_start (theArgs, pFormat); - - ca_printf ( - "CA.Client.Diagnostic..............................................\n"); - - ca_printf ( - " %s: \"%s\"\n", - severity[CA_EXTRACT_SEVERITY(ca_status)], - ca_message (ca_status)); - - if (pFormat) { - ca_printf (" Context: \""); - ca_vPrintf (pFormat, theArgs); - ca_printf ("\"\n"); - } - - if (pfilenm) { - ca_printf( - " Source File: %s Line Number: %d\n", - pfilenm, - lineno); - } - - /* - * Terminate execution if unsuccessful - */ - if( !(ca_status & CA_M_SUCCESS) && - CA_EXTRACT_SEVERITY(ca_status) != CA_K_WARNING ){ - abort(); - } - - ca_printf( - "..................................................................\n"); - - va_end (theArgs); -} - - -/* - * CA_BUSY_MSG() - * - */ -int ca_busy_message(struct ioc_in_use *piiu) -{ - caHdr hdr; - int status; - CA_OSD_GET_CA_STATIC - - if(!piiu){ - return ECA_INTERNAL; - } - - /* - * dont broadcast - */ - if(piiu == piiuCast){ - return ECA_INTERNAL; - } - - hdr = nullmsg; - hdr.m_cmmd = htons(CA_PROTO_EVENTS_OFF); - - status = cac_push_msg_no_block(piiu, &hdr, NULL); - if (status == ECA_NORMAL) { - piiu->pushPending = TRUE; - } - return status; -} - -/* - * CA_READY_MSG() - * - */ -int ca_ready_message(struct ioc_in_use *piiu) -{ - caHdr hdr; - int status; - CA_STATIC *ca_static; - - if(!piiu){ - return ECA_INTERNAL; - } - ca_static = piiu->pcas; - - /* - * dont broadcast - */ - if(piiu == piiuCast){ - return ECA_INTERNAL; - } - - hdr = nullmsg; - hdr.m_cmmd = htons(CA_PROTO_EVENTS_ON); - - status = cac_push_msg_no_block(piiu, &hdr, NULL); - if (status == ECA_NORMAL) { - piiu->pushPending = TRUE; - } - return status; -} - -/* - * - * echo_request (lock must be on) - * - */ -int echo_request( - struct ioc_in_use *piiu, - ca_time *pCurrentTime) -{ - caHdr hdr; - int status; - - 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). - */ - status = cac_push_msg_no_block(piiu, &hdr, NULL); - if (status == ECA_NORMAL) { - piiu->echoPending = TRUE; - piiu->pushPending = TRUE; - piiu->timeAtEchoRequest = *pCurrentTime; - } - - return status; -} - -/* - * - * NOOP_MSG (lock must be on) - * - */ -void noop_msg(struct ioc_in_use *piiu) -{ - caHdr hdr; - int status; - CA_STATIC *ca_static; - - ca_static = piiu->pcas; - 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 = cac_push_msg_no_block(piiu, &hdr, NULL); - if (status == ECA_NORMAL) { - piiu->pushPending = TRUE; - } -} - -/* - * ISSUE_IOC_HOST_NAME - * (lock must be on) - * - */ -void issue_client_host_name( struct ioc_in_use *piiu) -{ - unsigned size; - caHdr hdr; - char *pName; - CA_STATIC *ca_static; - - if(!piiu){ - return; - } - ca_static = piiu->pcas; - - /* - * dont broadcast client identification protocol - */ - if(piiu == piiuCast){ - return; - } - - /* - * prior to version 4.1 client identification - * protocol causes a disconnect - so - * dont send it in this case - */ - if(!CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number)){ - return; - } - - /* - * allocate space in the outgoing buffer - */ - pName = ca_static->ca_pHostName, - size = strlen(pName)+1; - hdr = nullmsg; - hdr.m_cmmd = htons(CA_PROTO_HOST_NAME); - hdr.m_postsize = size; - - cac_push_msg(piiu, &hdr, pName); - - return; -} - -/* - * ISSUE_IDENTIFY_CLIENT (lock must be on) - * - */ -void issue_identify_client(struct ioc_in_use *piiu) -{ - unsigned size; - caHdr hdr; - char *pName; - CA_STATIC *ca_static; - - if(!piiu){ - return; - } - ca_static = piiu->pcas; - - /* - * dont broadcast client identification protocol - */ - if(piiu == piiuCast){ - return; - } - - /* - * prior to version 4.1 client identification - * protocol causes a disconnect - so - * dont send it in this case - */ - if(!CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number)){ - return; - } - - /* - * allocate space in the outgoing buffer - */ - pName = ca_static->ca_pUserName, - size = strlen(pName)+1; - hdr = nullmsg; - hdr.m_cmmd = htons(CA_PROTO_CLIENT_NAME); - hdr.m_postsize = size; - - cac_push_msg(piiu, &hdr, pName); - - return; -} - -/* - * ISSUE_CLAIM_CHANNEL - */ -int issue_claim_channel (chid pchan) -{ - IIU *piiu = (IIU *) pchan->piiu; - caHdr hdr; - unsigned size; - const char *pStr; - int status; - CA_STATIC *ca_static; - - - if (!piiu) { - ca_printf("CAC: nill piiu claim attempted?\n"); - return ECA_INTERNAL; - } - ca_static = piiu->pcas; - - LOCK; - /* - * dont broadcast - */ - if (piiu == piiuCast) { - ca_printf("CAC: UDP claim attempted?\n"); - UNLOCK; - return ECA_INTERNAL; - } - - if (!pchan->claimPending) { - ca_printf("CAC: duplicate claim attempted (while disconnected)?\n"); - UNLOCK; - return ECA_INTERNAL; - } - - if (pchan->state==cs_conn) { - ca_printf("CAC: duplicate claim attempted (while connected)?\n"); - UNLOCK; - return ECA_INTERNAL; - } - - hdr = nullmsg; - hdr.m_cmmd = htons(CA_PROTO_CLAIM_CIU); - - if (CA_V44(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { - hdr.m_cid = pchan->cid; - pStr = ca_name(pchan); - size = strlen(ca_name(pchan))+1u; - } - else { - hdr.m_cid = pchan->id.sid; - pStr = NULL; - size = 0u; - } - - hdr.m_postsize = size; - - /* - * The available field is used (abused) - * here to communicate the minor version number - * starting with CA 4.1. - */ - hdr.m_available = htonl(CA_MINOR_VERSION); - - /* - * If we are out of buffer space then postpone this - * operation until later. This avoids any possibility - * of a push pull deadlock (since this is sent when - * parsing the UDP input buffer). - */ - status = cac_push_msg_no_block(piiu, &hdr, pStr); - - if (status == ECA_NORMAL) { - - /* - * move to the end of the list once the claim has been sent - */ - pchan->claimPending = FALSE; - ellDelete (&piiu->chidlist, &pchan->node); - ellAdd (&piiu->chidlist, &pchan->node); - - if (!CA_V42(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { - cac_reconnect_channel(ca_static, pchan->cid, TYPENOTCONN, 0); - } - } - else { - piiu->claimsPending = TRUE; - } - UNLOCK; - - return status; -} - -/* - * - * Default Exception Handler - * - * - */ -LOCAL void ca_default_exception_handler (struct exception_handler_args args) -{ - if (args.chid && args.op != CA_OP_OTHER) { - ca_signal_formated ( - args.stat, - args.pFile, - args.lineNo, - "%s - with request chan=%s op=%ld data type=%s count=%ld", - args.ctx, - ca_name (args.chid), - args.op, - dbr_type_to_text(args.type), - args.count); - } - else { - ca_signal_formated ( - args.stat, - args.pFile, - args.lineNo, - args.ctx); - } -} - -/* - * CA_ADD_FD_REGISTRATION - * - * call their function with their argument whenever - * a new fd is added or removed - * (for a manager of the select system call under UNIX) - * - */ -int epicsShareAPI ca_add_fd_registration(CAFDHANDLER *func, const void *arg) -{ - CA_OSD_GET_CA_STATIC - INITCHK; - - fd_register_func = func; - fd_register_arg = arg; - - return ECA_NORMAL; -} - -/* - * CA_DEFUNCT - * - * what is called by vacated entries in the VMS share image jump table - * - */ -int ca_defunct() -{ - SEVCHK(ECA_DEFUNCT, NULL); - return ECA_DEFUNCT; -} - -/* - * CA_HOST_NAME_FUNCTION() - * - * returns a pointer to the channel's host name - * - * currently implemented as a function - * (may be implemented as a MACRO in the future) - */ -READONLY char * epicsShareAPI ca_host_name_function(chid chix) -{ - IIU *piiu; - - piiu = chix->piiu; - - if(!piiu){ - return ""; - } - return piiu->host_name_str; -} - -/* - * ca_v42_ok(chid chan) - */ -int epicsShareAPI ca_v42_ok(chid chan) -{ - int v42; - IIU *piiu; - - piiu = chan->piiu; - - v42 = CA_V42( - CA_PROTOCOL_VERSION, - piiu->minor_version_number); - - return v42; -} - -/* - * ca_version() - * function that returns the CA version string - */ -READONLY char * epicsShareAPI ca_version() -{ - return CA_VERSION_STRING; -} - -/* - * ca_replace_printf_handler () - */ -int epicsShareAPI ca_replace_printf_handler ( -int (*ca_printf_func)(const char *pformat, va_list args) -) -{ - CA_OSD_GET_CA_STATIC - INITCHK; - - if (ca_printf_func) { - ca_static->ca_printf_func = ca_printf_func; - } - else { - /* - * os dependent - */ - caSetDefaultPrintfHandler(ca_static); - } - - return ECA_NORMAL; -} - -/* - * ca_printf() - */ -int epicsShareAPI ca_printf (const char *pformat, ...) -{ - va_list theArgs; - int status; - - va_start (theArgs, pformat); - - status = ca_vPrintf (pformat, theArgs); - - va_end (theArgs); - - return status; -} - -/* - * ca_vPrintf() - */ -int epicsShareAPI ca_vPrintf (const char *pformat, va_list args) -{ - int (*ca_printf_func)(const char *pformat, va_list args); - CA_OSD_GET_CA_STATIC - - if (ca_static) { - if (ca_static->ca_printf_func) { - ca_printf_func = ca_static->ca_printf_func; - } - else { - ca_printf_func = epicsVprintf; - } - } - else { - ca_printf_func = epicsVprintf; - } - - return (*ca_printf_func) (pformat, args); -} - - -/* - * ca_get_field_type() - */ -short epicsShareAPI ca_get_field_type (chid chan) -{ - if (chan->state==cs_conn) { - return chan->privType; - } - else { - return TYPENOTCONN; - } -} - -/* - * ca_get_element_count() - */ -unsigned short epicsShareAPI ca_get_element_count (chid chan) -{ - if (chan->state==cs_conn) { - return chan->privCount; - } - else { - return 0; - } -} - -/* - * ca_get_ioc_connection_count() - * - * returns the number of IOC's that CA is connected to - * (for testing purposes only) - */ -unsigned epicsShareAPI ca_get_ioc_connection_count () -{ - unsigned count; - CA_OSD_GET_CA_STATIC - - INITCHK; - - LOCK; - - count = ellCount (&ca_static->ca_iiuList); - - /* - * if there is a UDP IIU then subtract it out - */ - if (ca_static->ca_piiuCast!=NULL) { - count--; - } - - UNLOCK; - - return count; -} diff --git a/src/ca/access.cpp b/src/ca/access.cpp new file mode 100644 index 000000000..ebd74b928 --- /dev/null +++ b/src/ca/access.cpp @@ -0,0 +1,3343 @@ + +/* + * $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: Jeffrey O. Hill + * + */ + +#include "osiSigPipeIgnore.h" +#include "freeList.h" +#include "osiProcess.h" + +/* + * allocate error message string array + * here so I can use sizeof + */ +#define CA_ERROR_GLBLSOURCE + +/* + * allocate db_access message strings here + */ +#define DB_TEXT_GLBLSOURCE + +/* + * allocate header version strings here + */ +#define CAC_VERSION_GLOBAL + +#include "iocinf.h" + +static caHdr nullmsg; +static threadPrivateId caClientContextId; +threadPrivateId cacRecursionLock; + +#define TYPENOTINUSE (-2) + +/* + * fetchClientContext (); + */ +int fetchClientContext (cac **ppcac) +{ + int status; + + if ( caClientContextId != NULL ) { + *ppcac = (cac *) threadPrivateGet (caClientContextId); + if (*ppcac) { + return ECA_NORMAL; + } + } + + status = ca_task_initialize (); + if (status == ECA_NORMAL) { + *ppcac = (cac *) threadPrivateGet (caClientContextId); + if (!*ppcac) { + status = ECA_INTERNAL; + } + } + + return status; +} + +#if 0 +/* + * cacSetSendPending () + */ +LOCAL void cacSetSendPending (tcpiiu *piiu) +{ + if (!piiu->sendPending) { + piiu->timeAtSendBlock = piiu->niiu.iiu.pcas->currentTime; + piiu->sendPending = TRUE; + } +} +#endif + +/* + * cac_push_tcp_msg() + */ +LOCAL int cac_push_tcp_msg (tcpiiu *piiu, const caHdr *pmsg, const void *pext) +{ + 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); + + /* + * push the header onto the ring + */ + bytesSent = cacRingBufferWrite ( &piiu->send, &msg, sizeof (msg) ); + if ( bytesSent != sizeof (msg) ) { + return ECA_DISCONNCHID; + } + + /* + * push message body onto the ring + * + * (optionally encode in network format as we send) + */ + if (extsize>0u) { + bytesSent = cacRingBufferWrite ( &piiu->send, pext, actualextsize ); + if ( bytesSent != actualextsize ) { + return ECA_DISCONNCHID; + } + /* + * force pad bytes at the end of the message to nill + * if present (this avoids messages from purify) + */ + { + /* static variables are initialized to zero */ + const static char nullBuff[32]; + unsigned long n; + + n = extsize-actualextsize; + if (n) { + assert (n<=sizeof(nullBuff)); + bytesSent = cacRingBufferWrite ( &piiu->send, nullBuff, n ); + if ( bytesSent != n ) { + return ECA_DISCONNCHID; + } + } + } + } + + return ECA_NORMAL; +} + +/* + * cac_push_tcp_msg_no_block () + */ +LOCAL bool cac_push_tcp_msg_no_block (tcpiiu *piiu, const caHdr *pmsg, const void *pext) +{ + unsigned size; + int status; + + size = sizeof (*pmsg); + if ( pext != NULL ) { + size += CA_MESSAGE_ALIGN (pmsg->m_postsize); + } + + if ( cacRingBufferWriteLockNoBlock (&piiu->send, size) ) { + status = cac_push_tcp_msg (piiu, pmsg, pext); + cacRingBufferWriteUnlock (&piiu->send); + if (status == ECA_NORMAL) { + return true; + } + else { + return false; + } + } + else { + return false; + } +} + +/* + * cac_push_udp_msg() + */ +LOCAL int cac_push_udp_msg (udpiiu *piiu, const caHdr *pMsg, const void *pExt, ca_uint16_t extsize) +{ + unsigned long msgsize; + ca_uint16_t allignedExtSize; + caHdr *pbufmsg; + + allignedExtSize = CA_MESSAGE_ALIGN (extsize); + msgsize = sizeof (caHdr) + allignedExtSize; + + + /* fail out if max message size exceeded */ + if ( msgsize >= sizeof (piiu->xmitBuf)-7 ) { + return ECA_TOLARGE; + } + + semMutexMustTake (piiu->xmitBufLock); + if ( msgsize + piiu->nBytesInXmitBuf > sizeof (piiu->xmitBuf) ) { + semMutexGive (piiu->xmitBufLock); + return ECA_TOLARGE; + } + + pbufmsg = (caHdr *) &piiu->xmitBuf[piiu->nBytesInXmitBuf]; + *pbufmsg = *pMsg; + memcpy (pbufmsg+1, pExt, extsize); + if ( extsize != allignedExtSize ) { + char *pDest = (char *) (pbufmsg+1); + memset (pDest + extsize, '\0', allignedExtSize - extsize); + } + pbufmsg->m_postsize = htons (allignedExtSize); + piiu->nBytesInXmitBuf += msgsize; + semMutexGive (piiu->xmitBufLock); + + return ECA_NORMAL; +} + +/* + * Default Exception Handler + */ +LOCAL void ca_default_exception_handler (struct exception_handler_args args) +{ + if (args.chid && args.op != CA_OP_OTHER) { + ca_signal_formated ( + args.stat, + args.pFile, + args.lineNo, + "%s - with request chan=%s op=%ld data type=%s count=%ld", + args.ctx, + ca_name (args.chid), + args.op, + dbr_type_to_text(args.type), + args.count); + } + else { + ca_signal_formated ( + args.stat, + args.pFile, + args.lineNo, + args.ctx); + } +} + +/* + * default local pv interface entry points that always fail + */ +LOCAL pvId pvNameToIdNoop (const char *pname) +{ + return 0; +} +LOCAL int pvPutFieldNoop (pvId id, int src_type, + const void *psrc, int no_elements) +{ + return -1; +} +LOCAL int pvGetFieldNoop (pvId id, int dest_type, + void *pdest, int no_elements, void *pfl) +{ + return -1; +} +LOCAL long pvPutNotifyInitiateNoop (pvId id, + unsigned type, unsigned long count, const void *pValue, + void (*callback)(void *), void *usrPvt, putNotifyId * pID) +{ + return -1; +} +LOCAL void pvPutNotifyDestroyNoop (putNotifyId idIn) +{ +} + +LOCAL const char * pvNameNoop (pvId id) +{ + return ""; +} +LOCAL unsigned long pvNoElementsNoop (pvId id) +{ + return 0u; +} +short pvTypeNoop (pvId) +{ + return -1; +} +LOCAL dbEventCtx pvEventQueueInitNoop (void) +{ + return NULL; +} +LOCAL int pvEventQueueStartNoop (dbEventCtx ctx, const char *taskname, + void (*init_func)(void *), void *init_func_arg, int priority_offset) +{ + return -1; +} +LOCAL void pvEventQueueCloseNoop (dbEventCtx ctx) +{ +} +LOCAL dbEventSubscription pvEventQueueAddEventNoop (dbEventCtx ctx, pvId id, + void (*user_sub)(void *usrArg, pvId id, int hold, struct db_field_log *pfl), + void *user_arg, unsigned select) +{ + return NULL; +} +LOCAL int pvEventQueuePostSingleEventNoop (dbEventSubscription es) +{ + return -1; +} +LOCAL void pvEventQueueCancelEventNoop (dbEventSubscription dbes) +{ +} +LOCAL int pvEventQueueAddExtraLaborEventNoop (dbEventCtx ctx, + void (*func)(void *), void *arg) +{ + return -1; +} +LOCAL int pvEventQueuePostExtraLaborNoop (dbEventCtx ctx) +{ + return -1; +} + +LOCAL const pvAdapter pvAdapterNOOP = +{ + pvNameToIdNoop, + pvPutFieldNoop, + pvGetFieldNoop, + pvPutNotifyInitiateNoop, + pvPutNotifyDestroyNoop, + pvNameNoop, + pvNoElementsNoop, + pvTypeNoop, + pvEventQueueInitNoop, + pvEventQueueStartNoop, + pvEventQueueCloseNoop, + pvEventQueuePostSingleEventNoop, + pvEventQueueAddEventNoop, + pvEventQueueCancelEventNoop, + pvEventQueueAddExtraLaborEventNoop, + pvEventQueuePostExtraLaborNoop +}; + +/* + * event_import() + */ +LOCAL void event_import (void *pParam) +{ + threadPrivateSet (caClientContextId, pParam); +} + +/* + * constructLocalIIU + */ +LOCAL int constructLocalIIU (cac *pcac, const pvAdapter *pva, lclIIU *piiu) +{ + long status; + + freeListInitPvt (&piiu->localSubscrFreeListPVT, sizeof (lmiu), 256); + + piiu->putNotifyLock = semMutexCreate (); + if (!piiu->putNotifyLock) { + return ECA_ALLOCMEM; + } + + ellInit (&piiu->buffList); + ellInit (&piiu->chidList); + piiu->iiu.pcas = pcac; + piiu->pva = pva; + + piiu->evctx = (*piiu->pva->p_pvEventQueueInit) (); + if (piiu->evctx) { + + /* higher priority */ + status = (*piiu->pva->p_pvEventQueueStart) + (piiu->evctx, "CAC Event", event_import, pcac, +1); + if (status) { + (*piiu->pva->p_pvEventQueueClose) (piiu->evctx); + semMutexDestroy (piiu->putNotifyLock); + return ECA_ALLOCMEM; + } + } + return ECA_NORMAL; +} + +/* + * convert a generic ciu pointer to a network ciu + */ +LOCAL nciu *ciuToNCIU (baseCIU *pIn) +{ + char *pc = (char *) pIn; + + assert (pIn->piiu != &pIn->piiu->pcas->localIIU.iiu); + pc -= offsetof (nciu, ciu); + return (nciu *) pc; +} + +/* + * convert a generic baseCIU pointer to a local ciu + */ +LOCAL lciu *ciuToLCIU (baseCIU *pIn) +{ + char *pc = (char *) pIn; + + assert (pIn->piiu == &pIn->piiu->pcas->localIIU.iiu); + pc -= offsetof (lciu, ciu); + return (lciu *) pc; +} + +/* + * convert a generic miu pointer to a network miu + */ +LOCAL nmiu *miuToNMIU (baseMIU *pIn) +{ + char *pc = (char *) pIn; + + assert (pIn->pChan->piiu != &pIn->pChan->piiu->pcas->localIIU.iiu); + pc -= offsetof (nmiu, miu); + return (nmiu *) pc; +} + +/* + * convert a generic miu pointer to a local miu + */ +LOCAL lmiu *miuToLMIU (baseMIU *pIn) +{ + char *pc = (char *) pIn; + + assert (pIn->pChan->piiu == &pIn->pChan->piiu->pcas->localIIU.iiu); + pc -= offsetof (lmiu, miu); + return (lmiu *) pc; +} + +/* + * localMonitorResourceDestroy () + */ +LOCAL void localMonitorResourceDestroy (lmiu *pSubscription) +{ + lciu *pLocalChan = ciuToLCIU (pSubscription->miu.pChan); + + /* + * lock must be off when canceling the event so that + * the event queue can be flushed + */ + (*pSubscription->miu.pChan->piiu->pcas->localIIU.pva->p_pvEventQueueCancelEvent) (pSubscription->es); + + LOCK (pLocalChan->ciu.piiu->pcas); + ellDelete (&pLocalChan->eventq, &pSubscription->node); + freeListFree (pSubscription->miu.pChan->piiu->pcas->localIIU.localSubscrFreeListPVT, pSubscription); + UNLOCK (pLocalChan->ciu.piiu->pcas); +} + +/* + * caPutNotifydestroy () + */ +LOCAL void caPutNotifydestroy (lclIIU *piiu, caPutNotify *ppn) +{ + if (ppn) { + (*piiu->pva->p_pvPutNotifyDestroy) (ppn->dbPutNotify); + free (ppn); + } +} + +/* + * localChannelDestroy () + */ +LOCAL void localChannelDestroy (lclIIU *piiu, lciu *pChan) +{ + lmiu *pSubscription; + + LOCK (piiu->iiu.pcas); + + pChan->ciu.pAccessRightsFunc = NULL; + pChan->ciu.pConnFunc = NULL; + + while ( (pSubscription = (lmiu *) ellFirst (&pChan->eventq)) ) { + + /* + * temp release lock so that the event task + * can flush the event + */ + UNLOCK (piiu->iiu.pcas); + localMonitorResourceDestroy (pSubscription); + LOCK (piiu->iiu.pcas); + } + + caPutNotifydestroy (piiu, pChan->ppn); + + ellDelete (&piiu->chidList, &pChan->node); + + UNLOCK (piiu->iiu.pcas); + + pChan->ciu.puser = NULL; + pChan->ciu.piiu = NULL; + + free (pChan); +} + +/* + * liiu_destroy () + */ +LOCAL void liiu_destroy (lclIIU *piiu) +{ + lciu *pChan; + + freeListCleanup (piiu->localSubscrFreeListPVT); + + /* + * destroy all channels + */ + pChan = (lciu *) ellFirst (&piiu->chidList); + while (pChan) { + lciu *pChanNext = (lciu *) ellNext (&pChan->node); + localChannelDestroy (piiu, pChan); + pChan = pChanNext; + } + + /* + * All local events must be canceled prior to closing the + * local event facility + */ + (*piiu->pva->p_pvEventQueueClose) (piiu->evctx); + + ellFree (&piiu->buffList); + + semMutexDestroy (piiu->putNotifyLock); +} + +/* + * ca_task_initialize () + */ +int epicsShareAPI ca_task_initialize (void) +{ + cac *pcac; + + if (caClientContextId==NULL) { + caClientContextId = threadPrivateCreate (); + if (!caClientContextId) { + return ECA_ALLOCMEM; + } + } + + pcac = (cac *) threadPrivateGet (caClientContextId); + if (pcac) { + return ECA_NORMAL; + } + + pcac = new cac; + if (!pcac) { + return ECA_ALLOCMEM; + } + return ECA_NORMAL; +} + +// +// cac::cac () +// +cac::cac () +{ + long status; + int caStatus; + + if (cacRecursionLock==NULL) { + cacRecursionLock = threadPrivateCreate (); + if (!cacRecursionLock) { + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + } + + if (!bsdSockAttach()) { + throwWithLocation ( caErrorCode (ECA_INTERNAL) ); + } + + ellInit (&this->ca_taskVarList); + ellInit (&this->putCvrtBuf); + ellInit (&this->ca_iiuList); + ellInit (&this->fdInfoFreeList); + ellInit (&this->fdInfoList); + this->ca_printf_func = errlogVprintf; + this->pudpiiu = NULL; + this->ca_exception_func = NULL; + this->ca_exception_arg = NULL; + this->ca_fd_register_func = NULL; + this->ca_fd_register_arg = NULL; + this->ca_pEndOfBCastList = NULL; + this->ca_pndrecvcnt = 0; + this->ca_nextSlowBucketId = 0; + this->ca_nextFastBucketId = 0; + this->ca_flush_pending = FALSE; + this->ca_number_iiu_in_fc = 0u; + this->ca_manage_conn_active = FALSE; + memset (this->ca_beaconHash, '\0', sizeof (this->ca_beaconHash) ); + ca_sg_init (this); + + this->ca_client_lock = semMutexCreate(); + if (!this->ca_client_lock) { + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + this->ca_io_done_sem = semBinaryCreate(semEmpty); + if (!this->ca_io_done_sem) { + semMutexDestroy (this->ca_client_lock); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + this->ca_blockSem = semBinaryCreate(semEmpty); + if (!this->ca_blockSem) { + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + + installSigPipeIgnore(); + + { + char tmp[256]; + size_t len; + osiGetUserNameReturn gunRet; + + gunRet = osiGetUserName ( tmp, sizeof (tmp) ); + if (gunRet!=osiGetUserNameSuccess) { + tmp[0] = '\0'; + } + len = strlen (tmp) + 1; + this->ca_pUserName = (char *) malloc ( len ); + if ( ! this->ca_pUserName ) { + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + strncpy (this->ca_pUserName, tmp, len); + } + + /* record the host name */ + this->ca_pHostName = localHostName(); + if (!this->ca_pHostName) { + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + free (this->ca_pUserName); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + + status = tsStampGetCurrent (&this->currentTime); + if (status!=0) { + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + free (this->ca_pUserName); + free (this->ca_pHostName); + throwWithLocation ( caErrorCode (ECA_INTERNAL) ); + } + this->programBeginTime = this->currentTime; + this->programBeginTime = this->currentTime; + + /* + * construct the local IIU with a default + * NOOP database adapter) + */ + caStatus = constructLocalIIU (this, &pvAdapterNOOP, &this->localIIU); + if (caStatus!=ECA_NORMAL) { + ca_sg_shutdown (this); + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + free (this->ca_pUserName); + free (this->ca_pHostName); + throwWithLocation ( caErrorCode (caStatus) ); + } + + freeListInitPvt (&this->ca_ioBlockFreeListPVT, sizeof (nmiu), 256); + + this->ca_pSlowBucket = bucketCreate (CLIENT_HASH_TBL_SIZE); + if (this->ca_pSlowBucket==NULL) { + ca_sg_shutdown (this); + liiu_destroy (&this->localIIU); + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + free (this->ca_pUserName); + free (this->ca_pHostName); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + + this->ca_pFastBucket = bucketCreate (CLIENT_HASH_TBL_SIZE); + if (this->ca_pFastBucket==NULL) { + ca_sg_shutdown (this); + liiu_destroy (&this->localIIU);\ + bucketFree (this->ca_pSlowBucket); + semMutexDestroy (this->ca_client_lock); + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + free (this->ca_pUserName); + free (this->ca_pHostName); + throwWithLocation ( caErrorCode (ECA_ALLOCMEM) ); + } + + status = envGetDoubleConfigParam (&EPICS_CA_CONN_TMO, &this->ca_connectTMO); + if (status) { + this->ca_connectTMO = CA_CONN_VERIFY_PERIOD; + ca_printf (this, + "EPICS \"%s\" float fetch failed\n", + EPICS_CA_CONN_TMO.name); + ca_printf (this, + "Setting \"%s\" = %f\n", + EPICS_CA_CONN_TMO.name, + this->ca_connectTMO); + } + + this->ca_server_port = + caFetchPortConfig (this, &EPICS_CA_SERVER_PORT, CA_SERVER_PORT); + + threadPrivateSet (caClientContextId, (void *) this); +} + +/* + * CA_MODIFY_HOST_NAME() + * + * Modify or override the default + * client host name. + * + * This entry point was changed to a NOOP + */ +int epicsShareAPI ca_modify_host_name(const char *pHostName) +{ + return ECA_NORMAL; +} + +/* + * ca_modify_user_name() + * + * Modify or override the default + * client user name. + * + * This entry point was changed to a NOOP + */ +int epicsShareAPI ca_modify_user_name (const char *pClientName) +{ + return ECA_NORMAL; +} + + +/* + * ca_task_exit() + * + * releases all resources alloc to a channel access client + */ +epicsShareFunc int epicsShareAPI ca_task_exit (void) +{ + cac *pcac; + + if ( caClientContextId != NULL ) { + pcac = (cac *) threadPrivateGet (caClientContextId); + if (pcac) { + delete pcac; + } + } + + return ECA_NORMAL; +} + +/* + * cac_destroy () + * + * releases all resources alloc to a channel access client + */ +cac::~cac () +{ + tcpiiu *piiu; + int status; + + threadPrivateSet (caClientContextId, NULL); + + // + // shutdown all tcp connections and wait for threads to exit + // + while ( 1 ) { + semBinaryId id; + + LOCK (this); + piiu = (tcpiiu *) ellFirst (&this->ca_iiuList); + if (piiu) { + semBinaryId id = piiu->recvThreadExitSignal; + initiateShutdownTCPIIU (piiu); + } + UNLOCK (this); + + if (!piiu) { + break; + } + else { + semBinaryTake (id); + } + } + + // + // shutdown udp and wait for threads to exit + // + if (this->pudpiiu) { + cacShutdownUDP (*this->pudpiiu); + delete this->pudpiiu; + } + + LOCK (this); + + liiu_destroy (&this->localIIU); + + /* remove put convert block free list */ + ellFree (&this->putCvrtBuf); + + /* reclaim sync group resources */ + ca_sg_shutdown (this); + + /* remove remote waiting ev blocks */ + freeListCleanup (this->ca_ioBlockFreeListPVT); + + /* free select context lists */ + ellFree (&this->fdInfoFreeList); + ellFree (&this->fdInfoList); + + /* + * remove IOCs in use + */ + ellFree (&this->ca_iiuList); + + /* + * free user name string + */ + if (this->ca_pUserName) { + free (this->ca_pUserName); + } + + /* + * free host name string + */ + if (this->ca_pHostName) { + free (this->ca_pHostName); + } + + /* + * free hash tables + */ + status = bucketFree (this->ca_pSlowBucket); + assert (status == S_bucket_success); + status = bucketFree (this->ca_pFastBucket); + assert (status == S_bucket_success); + + /* + * free beacon hash table + */ + freeBeaconHash (this); + + semBinaryDestroy (this->ca_io_done_sem); + semBinaryDestroy (this->ca_blockSem); + + semMutexDestroy (this->ca_client_lock); + + bsdSockRelease (); +} + +/* + * + * CA_BUILD_AND_CONNECT + * + * backwards compatible entry point to ca_search_and_connect() + */ +int epicsShareAPI ca_build_and_connect (const char *name_str, chtype get_type, + unsigned long get_count, chid * chan, void *pvalue, + void (*conn_func) (struct connection_handler_args), void *puser) +{ + if (get_type != TYPENOTCONN && pvalue!=0 && get_count!=0) { + return ECA_ANACHRONISM; + } + + return ca_search_and_connect (name_str, chan, conn_func, puser); +} + +/* + * constructCoreChannel () + */ +LOCAL void constructCoreChannel (baseCIU *pciu, + void (*conn_func) (struct connection_handler_args), void *puser) +{ + pciu->piiu = NULL; + pciu->puser = puser; + pciu->pConnFunc = conn_func; + pciu->pAccessRightsFunc = NULL; +} + +/* + * constructLocalChannel () + */ +LOCAL int constructLocalChannel (cac *pcac, pvId idIn, + void (*conn_func) (struct connection_handler_args), void *puser, + chid *pid) +{ + lciu *pchan; + struct connection_handler_args args; + + /* + * allocate channel data structue and also allocate enough + * space for the channel name + */ + pchan = (lciu *) calloc (1, sizeof(*pchan)); + if (!pchan){ + return ECA_ALLOCMEM; + } + + LOCK (pcac); + + constructCoreChannel (&pchan->ciu, conn_func, puser); + ellInit (&pchan->eventq); + pchan->id = idIn; + pchan->ppn = NULL; + pchan->ciu.piiu = &pcac->localIIU.iiu; + ellAdd (&pcac->localIIU.chidList, &pchan->node); + + args.chid = (chid) &pchan->ciu; + args.op = CA_OP_CONN_UP; + (*conn_func) (args); + + UNLOCK (pcac); + + *pid = (chid *) &pchan->ciu; + + return ECA_NORMAL; +} + +/* + * constructNetChannel () + */ +LOCAL int constructNetChannel (cac *pcac, + void (*conn_func) (struct connection_handler_args), + void *puser, const char *pName, unsigned nameLength, chid *pid) +{ + nciu *pchan; + char *pNameDest; + long status; + + if (nameLength>USHRT_MAX) { + return ECA_STRTOBIG; + } + + if (!pcac->pudpiiu) { + pcac->pudpiiu = new udpiiu (pcac); + if (!pcac->pudpiiu) { + return ECA_NOCAST; + } + } + + /* + * allocate channel data structue and also allocate enough + * space for the channel name + */ + pchan = (nciu *) calloc (1, sizeof(*pchan) + nameLength); + if (!pchan){ + return ECA_ALLOCMEM; + } + + LOCK (pcac); + + do { + pchan->cid = CLIENT_SLOW_ID_ALLOC (pcac); + status = bucketAddItemUnsignedId (pcac->ca_pSlowBucket, &pchan->cid, pchan); + } while (status == S_bucket_idInUse); + if (status != S_bucket_success) { + UNLOCK (pcac); + free (pchan); + if (status == S_bucket_noMemory) { + return ECA_ALLOCMEM; + } + return ECA_INTERNAL; + } + + constructCoreChannel (&pchan->ciu, conn_func, puser); + + pNameDest = (char *)(pchan + 1); + strncpy (pNameDest, pName, nameLength); + pNameDest[nameLength-1] = '\0'; + + pchan->type = USHRT_MAX; /* invalid initial type */ + pchan->count = 0; /* invalid initial count */ + pchan->sid = UINT_MAX; /* invalid initial server id */ + pchan->ar.read_access = FALSE; + pchan->ar.write_access = FALSE; + pchan->nameLength = nameLength; + pchan->previousConn = 0; + pchan->connected = 0; + addToChanList (pchan, &pcac->pudpiiu->niiu); + ellInit (&pchan->eventq); + + /* + * reset broadcasted search counters + */ + pcac->pudpiiu->searchTmr.reset (CA_RECAST_DELAY); + + /* + * Connection Management takes care + * of sending the the search requests + */ + if (!pchan->ciu.pConnFunc) { + pcac->ca_pndrecvcnt++; + } + + UNLOCK (pcac); + + *pid = (chid *) &pchan->ciu; + + return ECA_NORMAL; +} + +/* + * ca_search_and_connect() + */ +int epicsShareAPI ca_search_and_connect (const char *name_str, chid *chanptr, + void (*conn_func) (struct connection_handler_args), void *puser) +{ + int caStatus; + cac *pcac; + pvId tmpId; + unsigned strcnt; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + /* + * rational limit on user supplied string size + */ + if (name_str==NULL) { + return ECA_EMPTYSTR; + } + strcnt = strlen (name_str) + 1; + if (strcnt > MAX_UDP-sizeof(caHdr)) { + return ECA_STRTOBIG; + } + if (strcnt <= 1) { + return ECA_EMPTYSTR; + } + + /* + * check to see if the channel is hosted within this address space + */ + tmpId = (*pcac->localIIU.pva->p_pvNameToId) (name_str); + if (tmpId) { + return constructLocalChannel (pcac, tmpId, conn_func, puser, chanptr); + } + else { + return constructNetChannel (pcac, conn_func, puser, name_str, strcnt, chanptr); + } +} + +/* + * cac_search_msg () + */ +int cac_search_msg (nciu *chan) +{ + udpiiu *piiu = chan->ciu.piiu->pcas->pudpiiu; + int status; + caHdr msg; + + if ( chan->ciu.piiu != &piiu->niiu.iiu ) { + return ECA_INTERNAL; + } + + if (chan->nameLength > 0xffff) { + return ECA_STRTOBIG; + } + + msg.m_cmmd = htons (CA_PROTO_SEARCH); + msg.m_available = chan->cid; + msg.m_dataType = htons (DONTREPLY); + msg.m_count = htons (CA_MINOR_VERSION); + msg.m_cid = chan->cid; + + status = cac_push_udp_msg (piiu, &msg, chan+1, chan->nameLength); + if (status != ECA_NORMAL) { + return status; + } + + /* + * increment the number of times we have tried to find this channel + */ + if (chan->retryretry++; + } + + /* + * move the channel to the end of the list so + * that all channels get a equal chance + */ + LOCK (chan->ciu.piiu->pcas); + ellDelete (&chan->ciu.piiu->pcas->pudpiiu->niiu.chidList, &chan->node); + ellAdd (&chan->ciu.piiu->pcas->pudpiiu->niiu.chidList, &chan->node); + UNLOCK (chan->ciu.piiu->pcas); + + return ECA_NORMAL; +} + +/* + * caIOBlockCreate () + */ +LOCAL nmiu *caIOBlockCreate (nciu *pChan, unsigned cmdIn, chtype type, + unsigned long count, void (*pFunc) (struct event_handler_args), void *pParam) +{ + int status; + nmiu *pIOBlock; + + LOCK (pChan->ciu.piiu->pcas); + + pIOBlock = (nmiu *) freeListCalloc (pChan->ciu.piiu->pcas->ca_ioBlockFreeListPVT); + if (pIOBlock) { + do { + pIOBlock->id = CLIENT_FAST_ID_ALLOC (pChan->ciu.piiu->pcas); + status = bucketAddItemUnsignedId (pChan->ciu.piiu->pcas->ca_pFastBucket, &pIOBlock->id, pIOBlock); + } while (status == S_bucket_idInUse); + + if(status != S_bucket_success){ + freeListFree (pChan->ciu.piiu->pcas->ca_ioBlockFreeListPVT, pIOBlock); + pIOBlock = NULL; + } + } + + pIOBlock->cmd = cmdIn; + pIOBlock->miu.pChan = &pChan->ciu; + pIOBlock->miu.type = type; + pIOBlock->miu.count = count; + pIOBlock->miu.usr_func = pFunc; + pIOBlock->miu.usr_arg = pParam; + + ellAdd (&pChan->eventq, &pIOBlock->node); + + UNLOCK (pChan->ciu.piiu->pcas); + + return pIOBlock; +} + +/* + * + * CA_EVENT_HANDLER() + * (only for clients attached to local PVs) + * + */ +LOCAL void ca_event_handler (void *usrArg, + pvId idIn, int hold, struct db_field_log *pfl) +{ + lmiu *monix = (lmiu *) usrArg; + lciu *pChan = ciuToLCIU (monix->miu.pChan); + lclIIU *piiu = iiuToLIIU (pChan->ciu.piiu); + union db_access_val valbuf; + unsigned long count; + unsigned long nativeElementCount; + void *pval; + size_t size; + int status; + struct tmp_buff { + ELLNODE node; + size_t size; + }; + struct tmp_buff *pbuf = NULL; + + nativeElementCount = (*piiu->pva->p_pvNoElements) (pChan->id); + + /* + * clip to the native count + * and set to the native count if they specify zero + */ + if (monix->miu.count > nativeElementCount || monix->miu.count == 0){ + count = nativeElementCount; + } + else { + count = monix->miu.count; + } + + size = dbr_size_n (monix->miu.type, count); + + if ( size <= sizeof(valbuf) ) { + pval = (void *) &valbuf; + } + else { + /* + * find a preallocated block which fits + * (stored with largest block first) + */ + LOCK (piiu->iiu.pcas); + pbuf = (struct tmp_buff *) ellFirst (&piiu->buffList); + if (pbuf && pbuf->size >= size) { + ellDelete (&piiu->buffList, &pbuf->node); + } + else { + pbuf = NULL; + } + UNLOCK (piiu->iiu.pcas); + + /* + * test again so malloc is not inside LOCKED + * section + */ + if (!pbuf) { + pbuf = (struct tmp_buff *) malloc(sizeof(*pbuf)+size); + if (!pbuf) { + ca_printf (piiu->iiu.pcas, + "%s: No Mem, Event Discarded\n", + __FILE__); + return; + } + pbuf->size = size; + } + pval = (void *) (pbuf+1); + } + status = (*piiu->pva->p_pvGetField) (idIn, monix->miu.type, + pval, count, pfl); + + /* + * Call user's callback + */ + LOCK (piiu->iiu.pcas); + if (monix->miu.usr_func) { + struct event_handler_args args; + + args.usr = (void *) monix->miu.usr_arg; + args.chid = monix->miu.pChan; + args.type = monix->miu.type; + args.count = count; + args.dbr = pval; + + if (status) { + args.status = ECA_GETFAIL; + } + else{ + args.status = ECA_NORMAL; + } + + (*monix->miu.usr_func)(args); + } + + /* + * insert the buffer back into the que in size order if + * one was used. + */ + if(pbuf){ + struct tmp_buff *ptbuf; + + for (ptbuf = (struct tmp_buff *) ellFirst (&piiu->buffList); + ptbuf; ptbuf = (struct tmp_buff *) ellNext (&pbuf->node) ){ + + if(ptbuf->size <= pbuf->size){ + break; + } + } + if (ptbuf) { + ptbuf = (struct tmp_buff *) ptbuf->node.previous; + } + + ellInsert (&piiu->buffList, &ptbuf->node, &pbuf->node); + } + UNLOCK (piiu->iiu.pcas); + + return; +} + +/* + * issue_get () + */ +LOCAL int issue_get (ca_uint16_t cmd, nciu *chan, chtype type, + unsigned long count, void (*pfunc) (struct event_handler_args), void *pParam) +{ + int status; + caHdr hdr; + ca_uint16_t type_u16; + ca_uint16_t count_u16; + tcpiiu *piiu; + + /* + * fail out if channel isnt connected or arguments are + * otherwise invalid + */ + if (!chan->connected) { + return ECA_DISCONNCHID; + } + if (INVALID_DB_REQ(type)) { + return ECA_BADTYPE; + } + if (!chan->ar.read_access) { + return ECA_NORDACCESS; + } + if (count > chan->count || count>0xffff) { + return ECA_BADCOUNT; + } + if (count == 0) { + if (cmd==CA_PROTO_READ_NOTIFY) { + count = chan->count; + } + else { + return ECA_BADCOUNT; + } + } + + /* + * only after range checking type and count cast + * them down to a smaller size + */ + type_u16 = (ca_uint16_t) type; + count_u16 = (ca_uint16_t) count; + + LOCK (chan->ciu.piiu->pcas); + { + nmiu *monix = caIOBlockCreate (chan, cmd, type, count, pfunc, pParam); + if (!monix) { + UNLOCK (chan->ciu.piiu->pcas); + return ECA_ALLOCMEM; + } + + hdr.m_cmmd = htons (cmd); + hdr.m_dataType = htons (type_u16); + hdr.m_count = htons (count_u16); + hdr.m_available = monix->id; + hdr.m_postsize = 0; + hdr.m_cid = chan->sid; + } + UNLOCK (chan->ciu.piiu->pcas); + + piiu = iiuToTCPIIU (chan->ciu.piiu); + status = cac_push_tcp_msg (piiu, &hdr, NULL); + if (status!=ECA_NORMAL && status!=ECA_DISCONNCHID) { + /* + * we need to be careful about touching the monix + * pointer after the lock has been released + */ + caIOBlockFree (chan->ciu.piiu->pcas, hdr.m_available); + } + + return status; +} + +/* + * ca_array_get () + */ +int epicsShareAPI ca_array_get (chtype type, unsigned long count, chid chanIn, void *pvalue) +{ + baseCIU *pChan = (baseCIU *) chanIn; + int status; + + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + lciu *pLocalChan = ciuToLCIU (pChan); + + status = (*pChan->piiu->pcas->localIIU.pva->p_pvGetField) ( + pLocalChan->id, type, pvalue, count, NULL); + if (status) { + return ECA_GETFAIL; + } + else { + return ECA_NORMAL; + } + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + status = issue_get (CA_PROTO_READ, pNetChan, type, count, NULL, pvalue); + if (status==ECA_NORMAL) { + pChan->piiu->pcas->ca_pndrecvcnt++; + } + return status; + } +} + +/* + * ca_array_get_callback () + */ +int epicsShareAPI ca_array_get_callback (chtype type, unsigned long count, chid chanIn, + void (*pfunc) (struct event_handler_args), void *arg) +{ + baseCIU *pChan = (baseCIU *) chanIn; + + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + lciu *pLocalChan = ciuToLCIU (pChan); + lmiu ev; + + ev.miu.usr_func = pfunc; + ev.miu.usr_arg = arg; + ev.miu.pChan = pChan; + ev.miu.type = type; + ev.miu.count = count; + ca_event_handler (&ev, pLocalChan->id, 0, NULL); + return ECA_NORMAL; + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + return issue_get (CA_PROTO_READ_NOTIFY, pNetChan, type, count, pfunc, arg); + } +} + +/* + * caIOBlockFree () + */ +void caIOBlockFree (cac *pcac, unsigned id) +{ + nmiu *pIOBlock; + nciu *pNetChan; + + LOCK (pcac); + pIOBlock = (nmiu *) bucketLookupAndRemoveItemUnsignedId ( + pcac->ca_pFastBucket, &id); + if (!pIOBlock) { + ca_printf (pcac, "CAC: Delete of invalid IO block identifier = %u ignored?\n", id); + UNLOCK (pcac); + return; + } + pIOBlock->id = ~0U; /* this id always invalid */ + pNetChan = ciuToNCIU (pIOBlock->miu.pChan); + ellDelete (&pNetChan->eventq, &pIOBlock->node); + freeListFree (pcac->ca_ioBlockFreeListPVT, pIOBlock); + UNLOCK (pcac); +} + +/* + * 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; + + strsize = strlen(pStr) + 1; + + if (strsize>MAX_STRING_SIZE) { + return ECA_STRTOBIG; + } + + pStr += MAX_STRING_SIZE; + } + + return ECA_NORMAL; +} + +/* + * malloc_put_convert() + */ +#ifdef CONVERSION_REQUIRED +LOCAL void *malloc_put_convert (cac *pcac, unsigned long size) +{ + struct putCvrtBuf *pBuf; + + LOCK (pcac); + while ( (pBuf = (struct putCvrtBuf *) ellGet(&pcac->putCvrtBuf)) ) { + if(pBuf->size >= size){ + break; + } + else { + free (pBuf); + } + } + UNLOCK (pcac); + + if (!pBuf) { + pBuf = (struct putCvrtBuf *) malloc (sizeof(*pBuf)+size); + if (!pBuf) { + return NULL; + } + pBuf->size = size; + pBuf->pBuf = (void *) (pBuf+1); + } + + return pBuf->pBuf; +} +#endif /* CONVERSION_REQUIRED */ + +/* + * free_put_convert() + */ +#ifdef CONVERSION_REQUIRED +LOCAL void free_put_convert(cac *pcac, void *pBuf) +{ + struct putCvrtBuf *pBufHdr; + + pBufHdr = (struct putCvrtBuf *)pBuf; + pBufHdr -= 1; + assert (pBufHdr->pBuf == (void *)(pBufHdr+1)); + + LOCK (pcac); + ellAdd (&pcac->putCvrtBuf, &pBufHdr->node); + UNLOCK (pcac); + + return; +} +#endif /* CONVERSION_REQUIRED */ + +/* + * issue_put() + */ +LOCAL int issue_put (ca_uint16_t cmd, unsigned id, nciu *chan, chtype type, + unsigned long count, const void *pvalue) +{ + int status; + caHdr hdr; + unsigned postcnt; + ca_uint16_t type_u16; + ca_uint16_t count_u16; + void *pCvrtBuf; + tcpiiu *piiu; + + /* + * fail out if the conn is down or the arguments are otherwise invalid + */ + if (!chan->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 (!chan->ar.write_access) { + return ECA_NOWTACCESS; + } + if ( count > chan->count || count > 0xffff || count == 0 ) { + return ECA_BADCOUNT; + } + if (type==DBR_STRING) { + status = check_a_dbr_string ( (char *) pvalue, count ); + if (status != ECA_NORMAL) { + return status; + } + } + postcnt = dbr_size_n (type,count); + if (postcnt>0xffff) { + return ECA_TOLARGE; + } + + /* + * only after range checking type and count cast + * them down to a smaller size + */ + type_u16 = (ca_uint16_t) type; + count_u16 = (ca_uint16_t) count; + + if (type == DBR_STRING && count == 1) { + char *pstr = (char *)pvalue; + + postcnt = strlen(pstr)+1; + } + +# ifdef CONVERSION_REQUIRED + { + unsigned i; + void *pdest; + unsigned size_of_one; + + size_of_one = dbr_size[type]; + + pCvrtBuf = pdest = malloc_put_convert (chan->ciu.piiu->pcas, postcnt); + if (!pdest) { + 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 (*(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 (*(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>=count) { + 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 (type_u16); + hdr.m_count = htons (count_u16); + hdr.m_cid = chan->sid; + hdr.m_available = id; + hdr.m_postsize = (ca_uint16_t) postcnt; + + piiu = iiuToTCPIIU (chan->ciu.piiu); + status = cac_push_tcp_msg (piiu, &hdr, pvalue); + +# ifdef CONVERSION_REQUIRED + free_put_convert (chan->ciu.piiu->pcas, pCvrtBuf); +# endif /*CONVERSION_REQUIRED*/ + + return status; +} + +/* + * ca_put_notify_action + */ +LOCAL void ca_put_notify_action (void *pPrivate) +{ + lciu *pChan = (lciu *) pPrivate; + lclIIU *pliiu = iiuToLIIU (pChan->ciu.piiu); + + /* + * independent lock used here in order to + * avoid any possibility of blocking + * the database (or indirectly blocking + * one client on another client). + */ + semMutexMustTake (pliiu->putNotifyLock); + ellAdd (&pliiu->putNotifyQue, &pChan->ppn->node); + semMutexGive (pliiu->putNotifyLock); + + /* + * offload the labor for this to the + * event task so that we never block + * the db or another client. + */ + (*pliiu->pva->p_pvEventQueuePostExtraLabor) (pliiu->evctx); +} + +/* + * localPutNotifyInitiate () + */ +int localPutNotifyInitiate (lciu *pChan, chtype type, unsigned long count, + const void *pValue, void (*pCallback)(struct event_handler_args), void *usrArg) +{ + lclIIU *pliiu = iiuToLIIU (pChan->ciu.piiu); + unsigned size; + long dbStatus; + + size = dbr_size_n (type, count); + + LOCK (pChan->ciu.piiu->pcas); + + if (pChan->ppn) { + /* + * wait while it is busy + */ + while (pChan->ppn->dbPutNotify) { + semTakeStatus semStatus; + UNLOCK (pChan->ciu.piiu->pcas); + semStatus = semBinaryTakeTimeout ( + pChan->ciu.piiu->pcas->ca_blockSem, 60.0); + if (semStatus != semTakeOK) { + return ECA_PUTCBINPROG; + } + LOCK (pChan->ciu.piiu->pcas); + } + + /* + * once its not busy then free the current + * block if it is too small + */ + if ( pChan->ppn->valueSize < size ) { + free ( pChan->ppn ); + pChan->ppn = NULL; + } + } + + if ( !pChan->ppn ) { + + pChan->ppn = (caPutNotify *) calloc (1, sizeof(*pChan->ppn)+size); + if ( !pChan->ppn ) { + UNLOCK (pChan->ciu.piiu->pcas); + return ECA_ALLOCMEM; + } + } + pChan->ppn->pValue = pChan->ppn + 1; + memcpy (pChan->ppn->pValue, pValue, size); + pChan->ppn->caUserCallback = pCallback; + pChan->ppn->caUserArg = usrArg; + + dbStatus = (*pliiu->pva->p_pvPutNotifyInitiate) + (pChan->id, type, count, pChan->ppn->pValue, ca_put_notify_action, pChan, + &pChan->ppn->dbPutNotify); + UNLOCK (pChan->ciu.piiu->pcas); + if (dbStatus!=0 && dbStatus!=S_db_Pending) { + errMessage (dbStatus, "CAC: unable to initiate put callback\n"); + if (dbStatus==S_db_Blocked) { + return ECA_PUTCBINPROG; + } + return ECA_PUTFAIL; + } + return ECA_NORMAL; +} + +/* + * ca_array_put_callback () + */ +int epicsShareAPI ca_array_put_callback (chtype type, unsigned long count, + chid pChanIn, const void *pvalue, void (*pfunc)(struct event_handler_args), + void *usrarg) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + int status; + unsigned id; + + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + lciu *pLocalChan = ciuToLCIU (pChan); + + return localPutNotifyInitiate (pLocalChan, type, count, pvalue, pfunc, usrarg); + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + tcpiiu *piiu; + nmiu *monix; + + if ( !pNetChan->connected ) { + return ECA_DISCONNCHID; + } + + piiu = iiuToTCPIIU (pChan->piiu); + + if (!CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { + return ECA_NOSUPPORT; + } + + /* + * lock around io block create and list add + * so that we are not deleted without + * reclaiming the resource + */ + LOCK (pChan->piiu->pcas); + + monix = caIOBlockCreate (pNetChan, CA_PROTO_WRITE_NOTIFY, + type, count, pfunc, usrarg); + if (!monix) { + UNLOCK (pChan->piiu->pcas); + return ECA_ALLOCMEM; + } + + id = monix->id; + + UNLOCK (pChan->piiu->pcas); + + status = issue_put (CA_PROTO_WRITE_NOTIFY, id, pNetChan, type, count, pvalue); + if (status!=ECA_NORMAL && status!=ECA_DISCONNCHID) { + /* + * we need to be careful about touching the monix + * pointer after the lock has been released + */ + caIOBlockFree (pChan->piiu->pcas, id); + } + return status; + } +} + +/* + * ca_array_put () + */ +int epicsShareAPI ca_array_put (chtype type, unsigned long count, chid pChanIn, const void *pvalue) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + /* + * If channel is on this client's host then + * call the database directly + */ + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + lciu *pLocalChan = ciuToLCIU (pChan); + int status; + + status = (*pChan->piiu->pcas->localIIU.pva->p_pvPutField) (pLocalChan->id, + type, pvalue, count); + if (status) { + return ECA_PUTFAIL; + } + else { + return ECA_NORMAL; + } + } + else { + return issue_put (CA_PROTO_WRITE, ~0U, ciuToNCIU (pChan), type, count, pvalue); + } +} + +/* + * Specify an event subroutine to be run for connection events + */ +int epicsShareAPI ca_change_connection_event (chid pChanIn, void (*pfunc)(struct connection_handler_args)) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + if (!pChan->piiu) { + return ECA_BADCHID; + } + + if (pChan->pConnFunc == pfunc) { + return ECA_NORMAL; + } + + LOCK (pChan->piiu->pcas); + if (pChan->piiu != &pChan->piiu->pcas->localIIU.iiu) { + nciu *pNetChan = ciuToNCIU (pChan); + + if (!pNetChan->previousConn) { + if (!pChan->pConnFunc) { + if (--pChan->piiu->pcas->ca_pndrecvcnt==0u) { + semBinaryGive (pChan->piiu->pcas->ca_io_done_sem); + } + } + if (!pfunc) { + pChan->piiu->pcas->ca_pndrecvcnt++; + } + } + } + pChan->pConnFunc = pfunc; + UNLOCK (pChan->piiu->pcas); + + return ECA_NORMAL; +} + +/* + * ca_replace_access_rights_event + */ +int epicsShareAPI ca_replace_access_rights_event (chid pChanIn, void (*pfunc)(struct access_rights_handler_args)) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + struct access_rights_handler_args args; + caar ar; + int connected; + + if (!pChan->piiu) { + return ECA_BADCHID; + } + + LOCK (pChan->piiu->pcas); + pChan->pAccessRightsFunc = pfunc; + + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + connected = TRUE; + ar.read_access = TRUE; + ar.write_access = TRUE; + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + if (pNetChan->connected) { + connected = TRUE; + } + else { + connected = FALSE; + } + ar = pNetChan->ar; + } + + /* + * make certain that it runs at least once + */ + if ( connected && pChan->pAccessRightsFunc ) { + args.chid = (chid) pChan; + args.ar = ar; + (*pChan->pAccessRightsFunc)(args); + } + + UNLOCK (pChan->piiu->pcas); + + return ECA_NORMAL; +} + +/* + * Specify an event subroutine to be run for asynch exceptions + */ +int epicsShareAPI ca_add_exception_event + (void (*pfunc)(struct exception_handler_args), void *arg) +{ + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + LOCK (pcac); + if (pfunc) { + pcac->ca_exception_func = pfunc; + pcac->ca_exception_arg = arg; + } + else { + pcac->ca_exception_func = ca_default_exception_handler; + pcac->ca_exception_arg = NULL; + } + UNLOCK (pcac); + + return ECA_NORMAL; +} + +/* + * ca_add_masked_array_event + */ +int epicsShareAPI ca_add_masked_array_event (chtype type, unsigned long count, chid pChanIn, + void (*pCallBack)(struct event_handler_args), void *pCallBackArg, ca_real p_delta, + ca_real n_delta, ca_real timeout, evid *monixptr, long mask) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + baseMIU *pMon; + int status; + + if (!pChan->piiu) { + return ECA_BADCHID; + } + + if ( INVALID_DB_REQ(type) ) { + return ECA_BADTYPE; + } + + /* + * Check for huge waveform + * + * (the count is not checked here against the native count + * when connected because this introduces a race condition + * for the client tool - the requested count is clipped to + * the actual count when the monitor request is sent so + * verifying that the requested count is valid here isnt + * required) + */ + if (dbr_size_n(type,count)>MAX_MSG_SIZE-sizeof(caHdr)) { + return ECA_TOLARGE; + } + + if (pCallBack==NULL) { + return ECA_BADFUNCPTR; + } + + if (mask&USHRT_MAX==0) { + return ECA_BADMASK; + } + + /* + * lock around io block create and list add + * so that we are not deleted while + * creating the resource + */ + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + lciu *pLocalChan = ciuToLCIU (pChan); + lmiu *pLclMon; + + LOCK (pChan->piiu->pcas); + + pLclMon = (lmiu *) freeListMalloc (pChan->piiu->pcas->localIIU.localSubscrFreeListPVT); + if (!pLclMon) { + UNLOCK (pChan->piiu->pcas); + return ECA_ALLOCMEM; + } + + pLclMon->miu.type = type; + pLclMon->miu.usr_func = pCallBack; + pLclMon->miu.usr_arg = pCallBackArg; + pLclMon->miu.count = count; + pLclMon->miu.pChan = pChan; + + pLclMon->es = (*pChan->piiu->pcas->localIIU.pva->p_pvEventQueueAddEvent) + (pChan->piiu->pcas->localIIU.evctx, + pLocalChan->id, ca_event_handler, pLclMon, mask); + + if (!pLclMon->es) { + freeListFree (pChan->piiu->pcas->localIIU.localSubscrFreeListPVT, pLclMon); + UNLOCK (pChan->piiu->pcas); + return ECA_ALLOCMEM; + } + + ellAdd (&pLocalChan->eventq, &pLclMon->node); + + UNLOCK (pChan->piiu->pcas); + + (*pChan->piiu->pcas->localIIU.pva->p_pvEventQueuePostSingleEvent) (pLclMon->es); + + pMon = &pLclMon->miu; + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + nmiu *pNetMon; + unsigned id; + + LOCK (pChan->piiu->pcas); + + pNetMon = caIOBlockCreate (pNetChan, CA_PROTO_EVENT_ADD, + type, count, pCallBack, pCallBackArg); + if (!pNetMon) { + UNLOCK (pChan->piiu->pcas); + return ECA_ALLOCMEM; + } + + pNetMon->p_delta = p_delta; + pNetMon->n_delta = n_delta; + pNetMon->timeout = timeout; + pNetMon->mask = (unsigned short) mask; + + id = pNetMon->id; + UNLOCK (pChan->piiu->pcas); + if (pNetChan->connected) { + status = ca_request_event (pNetChan, pNetMon); + if (status != ECA_NORMAL) { + if ( !pNetChan->connected ) { + caIOBlockFree (pNetChan->ciu.piiu->pcas, id); + } + return status; + } + } + pMon = &pNetMon->miu; + } + + if (monixptr) { + *monixptr = pMon; + } + + return ECA_NORMAL; +} + +/* + * CA_REQUEST_EVENT() + */ +int ca_request_event (nciu *pNetChan, nmiu *pNetMon) +{ + int status; + unsigned long count; + struct monops msg; + ca_float32_t p_delta; + ca_float32_t n_delta; + ca_float32_t tmo; + + /* + * dont send the message if the conn is down + * (it will be sent once connected) + */ + if (!pNetChan->connected) { + return ECA_DISCONNCHID; + } + + /* + * clip to the native count and set to the native count if they + * specify zero + */ + if (pNetMon->miu.count > pNetChan->count || pNetMon->miu.count == 0){ + count = pNetChan->count; + } + else { + count = pNetMon->miu.count; + } + + /* + * dont allow overflow when converting to ca_uint16_t + */ + if (count>0xffff) { + count = 0xffff; + } + + /* msg header */ + msg.m_header.m_cmmd = htons (CA_PROTO_EVENT_ADD); + msg.m_header.m_available = pNetMon->id; + msg.m_header.m_dataType = htons ( ((ca_uint16_t)pNetMon->miu.type) ); + msg.m_header.m_count = htons ( ((ca_uint16_t)count) ); + msg.m_header.m_cid = pNetChan->sid; + msg.m_header.m_postsize = sizeof (msg.m_info); + + /* msg body */ + p_delta = (ca_float32_t) pNetMon->p_delta; + n_delta = (ca_float32_t) pNetMon->n_delta; + tmo = (ca_float32_t) pNetMon->timeout; + dbr_htonf (&p_delta, &msg.m_info.m_hval); + dbr_htonf (&n_delta, &msg.m_info.m_lval); + dbr_htonf (&tmo, &msg.m_info.m_toval); + msg.m_info.m_mask = htons (pNetMon->mask); + msg.m_info.m_pad = 0; /* allow future use */ + + status = cac_push_tcp_msg (iiuToTCPIIU(pNetChan->ciu.piiu), &msg.m_header, &msg.m_info); + + return status; +} + + +/* + * + * ca_clear_event () + * + * Cancel an outstanding event for a channel. + * + * NOTE: returns before operation completes in the server + * (and the client). + * This is a good reason not to allow them to make the monix + * block as part of a larger structure. + * Nevertheless the caller is gauranteed that his specified + * event is disabled and therefore will not run (from this source) + * after leaving this routine. + * + */ +int epicsShareAPI ca_clear_event (evid evidIn) +{ + baseMIU *pMon = (baseMIU *) evidIn; + int status; + + /* disable any further events from this event block */ + pMon->usr_func = NULL; + + /* + * is it a local event subscription ? + */ + if (pMon->pChan->piiu == &pMon->pChan->piiu->pcas->localIIU.iiu) { + localMonitorResourceDestroy (miuToLMIU (pMon)); + status = ECA_NORMAL; + } + else { + nmiu *pNetMIU = miuToNMIU (pMon); + nciu *pNetCIU = ciuToNCIU (pMon->pChan); + + if (pNetCIU->connected) { + caHdr hdr; + ca_uint16_t type, count; + + type = (ca_uint16_t) pNetCIU->type; + if (pNetCIU->count>0xffff) { + count = 0xffff; + } + else { + count = (ca_uint16_t) pNetCIU->count; + } + + hdr.m_cmmd = htons (CA_PROTO_EVENT_CANCEL); + hdr.m_available = pNetMIU->id; + hdr.m_dataType = htons (type); + hdr.m_count = htons (count); + hdr.m_cid = pNetCIU->sid; + hdr.m_postsize = 0; + + status = cac_push_tcp_msg (iiuToTCPIIU(pMon->pChan->piiu), &hdr, NULL); + } + else { + status = ECA_NORMAL; + } + + caIOBlockFree (pMon->pChan->piiu->pcas, pNetMIU->id); + } + + return status; +} + +/* + * + * ca_clear_channel () + * + * clear the resources allocated for a channel by search + * + * NOTE: returns before operation completes in the server + * (and the client). + * This is a good reason not to allow them to make the monix + * block part of a larger structure. + * Nevertheless the caller is gauranteed that his specified + * event is disabled and therefore will not run + * (from this source) after leaving this routine. + * + */ +int epicsShareAPI ca_clear_channel (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + int status; + + /* + * is it a local channel ? + */ + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + localChannelDestroy (iiuToLIIU(pChan->piiu), ciuToLCIU(pChan)); + status = ECA_NORMAL; + } + else { + nciu *pNetCIU = ciuToNCIU (pChan); + + if (pNetCIU->connected) { + caHdr hdr; + + hdr.m_cmmd = htons (CA_PROTO_CLEAR_CHANNEL); + hdr.m_available = pNetCIU->cid; + hdr.m_cid = pNetCIU->sid; + hdr.m_dataType = htons (0); + hdr.m_count = htons (0); + hdr.m_postsize = 0; + + status = cac_push_tcp_msg (iiuToTCPIIU(pChan->piiu), &hdr, NULL); + } + else { + status = ECA_NORMAL; + } + + netChannelDestroy (pChan->piiu->pcas, pNetCIU->cid); + } + return status; +} + +/* + * netChannelDestroy () + */ +void netChannelDestroy (cac *pcac, unsigned id) +{ + nciu *chan; + nmiu *monix; + nmiu *next; + + LOCK (pcac); + + chan = (nciu *) bucketLookupAndRemoveItemUnsignedId (pcac->ca_pSlowBucket, &id); + if (chan==NULL) { + UNLOCK (pcac); + genLocalExcep (pcac, ECA_BADCHID, "netChannelDestroy()"); + return; + } + + /* + * if this channel does not have a connection handler + * and it has not connected for the first time then clear the + * outstanding IO count + */ + if (!chan->previousConn && !chan->ciu.pConnFunc) { + if (--pcac->ca_pndrecvcnt==0u) { + semBinaryGive (pcac->ca_io_done_sem); + } + } + + /* + * remove any IO blocks still attached to this channel + */ + for (monix = (nmiu *) ellFirst (&chan->eventq); + monix; monix = next) { + + next = (nmiu *) ellNext (&monix->node); + + caIOBlockFree (pcac, monix->id); + } + + removeFromChanList (chan); + + /* + * attempt to catch use of this channel after it + * is returned to pool + */ + chan->ciu.piiu = NULL; + + free (chan); + + UNLOCK (pcac); +} + +#if 0 +/* + * cacSetPushPending () + */ +LOCAL void cacSetPushPending (tcpiiu *piiu) +{ + unsigned long sendCnt; + + cacRingBufferWriteFlush (&piiu->send); + + sendCnt = cacRingBufferReadSize (&piiu->send, TRUE); + if (sendCnt) { + piiu->pushPending = TRUE; + cacSetSendPending (piiu); + } +} +#endif + +/* + * cacFlushAllIIU () + */ +void cacFlushAllIIU (cac *pcac) +{ + tcpiiu *piiu; + + /* + * set the push pending flag on all virtual circuits + */ + LOCK (pcac); + for ( piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); + piiu; piiu = (tcpiiu *) ellNext (&piiu->node) ) { + cacRingBufferWriteFlush (&piiu->send); + } + UNLOCK (pcac); +} + +/* + * noopConnHandler() + * This is installed into channels which dont have + * a connection handler when ca_pend_io() times + * out so that we will not decrement the pending + * recv count in the future. + */ +LOCAL void noopConnHandler(struct connection_handler_args args) +{ +} + +/* + * + * set pending IO count back to zero and + * send a sync to each IOC and back. dont + * count reads until we recv the sync + * + */ +LOCAL void ca_pend_io_cleanup (cac *pcac) +{ + tcpiiu *piiu; + nciu *pchan; + + LOCK (pcac); + + pchan = (nciu *) ellFirst (&pcac->pudpiiu->niiu.chidList); + while (pchan) { + if (!pchan->ciu.pConnFunc) { + pchan->ciu.pConnFunc = noopConnHandler; + } + pchan = (nciu *) ellNext (&pchan->node); + } + + for (piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); + piiu; piiu = (tcpiiu *) ellNext (&piiu->node.next) ){ + + caHdr hdr; + + if ( piiu->state != iiu_connected ) { + continue; + } + + piiu->cur_read_seq++; + + hdr = nullmsg; + hdr.m_cmmd = htons (CA_PROTO_READ_SYNC); + cac_push_tcp_msg (piiu, &hdr, NULL); + } + UNLOCK (pcac); + pcac->ca_pndrecvcnt = 0u; +} + +/* + * ca_pend_private () + */ +static int ca_pend_private (cac *pcac, ca_real timeout, int early) +{ + TS_STAMP beg_time; + TS_STAMP cur_time; + double delay; + int caStatus; + + caStatus = tsStampGetCurrent (&cur_time); + if (caStatus != 0) { + return ECA_INTERNAL; + } + + LOCK (pcac); + pcac->currentTime = cur_time; + UNLOCK (pcac); + + cacFlushAllIIU (pcac); + + if (pcac->ca_pndrecvcnt==0u && early) { + return ECA_NORMAL; + } + + if (timeout<0.0) { + if (early) ca_pend_io_cleanup (pcac); + return ECA_TIMEOUT; + } + + beg_time = cur_time; + delay = 0.0; + while (TRUE) { + ca_real remaining; + + if(timeout == 0.0){ + remaining = 60.0; + } + else{ + remaining = timeout-delay; + remaining = min (60.0, remaining); + + /* + * If we are not waiting for any significant delay + * then force the delay to zero so that we avoid + * scheduling delays (which can be substantial + * on some os) + */ + if (remaining <= CAC_SIGNIFICANT_SELECT_DELAY) { + if (early) ca_pend_io_cleanup (pcac); + return ECA_TIMEOUT; + } + } + + /* recv occurs in another thread */ + semBinaryTakeTimeout (pcac->ca_io_done_sem, remaining); + + if (pcac->ca_pndrecvcnt==0 && early) { + return ECA_NORMAL; + } + + /* force time update */ + caStatus = tsStampGetCurrent (&cur_time); + if (caStatus != 0) { + if (early) ca_pend_io_cleanup (pcac); + return ECA_INTERNAL; + } + + LOCK (pcac); + pcac->currentTime = cur_time; + UNLOCK (pcac); + + if (timeout != 0.0) { + delay = tsStampDiffInSeconds (&cur_time, &beg_time); + } + } +} + +/* + * ca_pend () + */ +int epicsShareAPI ca_pend (ca_real timeout, int early) +{ + cac *pcac; + int status; + void *p; + + status = fetchClientContext (&pcac); + if ( status != ECA_NORMAL ) { + return status; + } + + /* + * dont allow recursion + */ + p = threadPrivateGet (cacRecursionLock); + if (p) { + return ECA_EVDISALLOW; + } + + threadPrivateSet (cacRecursionLock, &cacRecursionLock); + + status = ca_pend_private (pcac, timeout, early); + + threadPrivateSet (cacRecursionLock, NULL); + + return status; +} + +#if 0 +/* + * cac_fetch_poll_period() + */ +double cac_fetch_poll_period (cac *pcac) +{ + if (!pcac->pudpiiu) { + return SELECT_POLL_NO_SEARCH; + } + else if ( ellCount (&pcac->pudpiiu->niiu.chidList) == 0 ) { + return SELECT_POLL_NO_SEARCH; + } + else { + return SELECT_POLL_SEARCH; + } +} +#endif /* #if 0 */ + +/* + * CA_FLUSH_IO() + * reprocess connection state and + * flush the send buffer + * + * Wait for all send buffers to be flushed + * while performing socket io and processing recv backlog + */ +int epicsShareAPI ca_flush_io () +{ + int caStatus; + cac *pcac; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + cacFlushAllIIU (pcac); + + return ECA_NORMAL; +} + +/* + * CA_TEST_IO () + */ +int epicsShareAPI ca_test_io () +{ + int caStatus; + cac *pcac; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + if (pcac->ca_pndrecvcnt==0u) { + return ECA_IODONE; + } + else{ + return ECA_IOINPROGRESS; + } +} + +/* + * genLocalExcepWFL () + * (generate local exception with file and line number) + */ +void genLocalExcepWFL (cac *pcac, long stat, const char *ctx, const char *pFile, unsigned lineNo) +{ + struct exception_handler_args args; + + args.chid = NULL; + args.type = -1; + args.count = 0u; + args.addr = NULL; + args.stat = stat; + args.op = CA_OP_OTHER; + args.ctx = ctx; + args.pFile = pFile; + args.lineNo = lineNo; + + /* + * dont lock if there is no CA context + */ + if (pcac==NULL) { + args.usr = NULL; + ca_default_exception_handler (args); + } + /* + * NOOP if they disable exceptions + */ + else if (pcac->ca_exception_func!=NULL) { + args.usr = pcac->ca_exception_arg; + + LOCK (pcac); + (*pcac->ca_exception_func) (args); + UNLOCK (pcac); + } +} + +/* + * CA_SIGNAL() + */ +void epicsShareAPI ca_signal (long ca_status, const char *message) +{ + ca_signal_with_file_and_lineno (ca_status, message, NULL, 0); +} + +/* + * ca_message (long ca_status) + * + * - if it is an unknown error code then it possible + * that the error string generated below + * will be overwritten before (or while) the caller + * of this routine is calling this routine + * (if they call this routine again). + */ +READONLY char * epicsShareAPI ca_message (long ca_status) +{ + unsigned msgNo = CA_EXTRACT_MSG_NO (ca_status); + + if( msgNo < NELEMENTS (ca_message_text) ){ + return ca_message_text[msgNo]; + } + else { + return "new CA message number known only by server - see caerr.h"; + } +} + +/* + * ca_signal_with_file_and_lineno() + */ +void epicsShareAPI ca_signal_with_file_and_lineno (long ca_status, + const char *message, const char *pfilenm, int lineno) +{ + ca_signal_formated (ca_status, pfilenm, lineno, message); +} + +/* + * ca_signal_formated() + */ +void epicsShareAPI ca_signal_formated (long ca_status, const char *pfilenm, + int lineno, const char *pFormat, ...) +{ + cac *pcac; + va_list theArgs; + static const char *severity[] = + { + "Warning", + "Success", + "Error", + "Info", + "Fatal", + "Fatal", + "Fatal", + "Fatal" + }; + + if (caClientContextId) { + pcac = (cac *) threadPrivateGet (caClientContextId); + } + else { + pcac = NULL; + } + + va_start (theArgs, pFormat); + + ca_printf (pcac, + "CA.Client.Diagnostic..............................................\n"); + + ca_printf (pcac, + " %s: \"%s\"\n", + severity[CA_EXTRACT_SEVERITY(ca_status)], + ca_message (ca_status)); + + if (pFormat) { + ca_printf (pcac, " Context: \""); + ca_vPrintf (pcac, pFormat, theArgs); + ca_printf (pcac, "\"\n"); + } + + if (pfilenm) { + ca_printf (pcac, + " Source File: %s Line Number: %d\n", + pfilenm, + lineno); + } + + /* + * Terminate execution if unsuccessful + */ + if( !(ca_status & CA_M_SUCCESS) && + CA_EXTRACT_SEVERITY(ca_status) != CA_K_WARNING ){ + abort(); + } + + ca_printf (pcac, + "..................................................................\n"); + + va_end (theArgs); +} + + +/* + * ca_busy_message () + */ +int ca_busy_message (tcpiiu *piiu) +{ + caHdr hdr; + int status; + + hdr = nullmsg; + hdr.m_cmmd = htons (CA_PROTO_EVENTS_OFF); + hdr.m_postsize = 0; + + status = cac_push_tcp_msg (piiu, &hdr, NULL); + if (status == ECA_NORMAL) { + cacRingBufferWriteFlush (&piiu->send); + } + return status; +} + +/* + * ca_ready_message () + */ +int ca_ready_message (tcpiiu *piiu) +{ + caHdr hdr; + int status; + + hdr = nullmsg; + hdr.m_cmmd = htons (CA_PROTO_EVENTS_ON); + hdr.m_postsize = 0; + + status = cac_push_tcp_msg (piiu, &hdr, NULL); + if (status == ECA_NORMAL) { + cacRingBufferWriteFlush (&piiu->send); + } + return status; +} + +/* + * echo_request () + */ +void echo_request (tcpiiu *piiu) +{ + 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 (cac_push_tcp_msg_no_block (piiu, &hdr, NULL)) { + piiu->echoPending = TRUE; + LOCK (piiu->niiu.iiu.pcas); + piiu->timeAtEchoRequest = piiu->niiu.iiu.pcas->currentTime; + UNLOCK (piiu->niiu.iiu.pcas) + cacRingBufferWriteFlush (&piiu->send); + } +} + +/* + * noop_msg () + */ +void noop_msg (tcpiiu *piiu) +{ + caHdr hdr; + bool 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 = cac_push_tcp_msg_no_block (piiu, &hdr, NULL); + if (status) { + cacRingBufferWriteFlush (&piiu->send); + } +} + +/* + * issue_client_host_name () + */ +void issue_client_host_name (tcpiiu *piiu) +{ + unsigned size; + caHdr hdr; + char *pName; + + if (!CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { + return; + } + + /* + * allocate space in the outgoing buffer + */ + pName = piiu->niiu.iiu.pcas->ca_pHostName, + size = strlen (pName) + 1; + hdr = nullmsg; + hdr.m_cmmd = htons(CA_PROTO_HOST_NAME); + hdr.m_postsize = size; + + cac_push_tcp_msg (piiu, &hdr, pName); + + return; +} + +/* + * issue_identify_client () + */ +void issue_identify_client (tcpiiu *piiu) +{ + unsigned size; + caHdr hdr; + char *pName; + + if (!CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { + return; + } + + /* + * allocate space in the outgoing buffer + */ + pName = piiu->niiu.iiu.pcas->ca_pUserName, + size = strlen (pName) + 1; + hdr = nullmsg; + hdr.m_cmmd = htons (CA_PROTO_CLIENT_NAME); + hdr.m_postsize = size; + + cac_push_tcp_msg (piiu, &hdr, pName); + + return; +} + +/* + * issue_claim_channel () + */ +bool issue_claim_channel (nciu *pchan) +{ + tcpiiu *pNetIIU; + caHdr hdr; + unsigned size; + const char *pStr; + bool success; + + LOCK (pchan->ciu.piiu->pcas); + + if (pchan->ciu.piiu == &pchan->ciu.piiu->pcas->pudpiiu->niiu.iiu) { + ca_printf (pchan->ciu.piiu->pcas, + "CAC: UDP claim attempted?\n"); + UNLOCK (pchan->ciu.piiu->pcas); + return false; + } + + pNetIIU = iiuToTCPIIU (pchan->ciu.piiu); + + if (!pchan->claimPending) { + ca_printf (pchan->ciu.piiu->pcas, + "CAC: duplicate claim attempted (while disconnected)?\n"); + UNLOCK (pchan->ciu.piiu->pcas); + return false; + } + + if (pchan->connected) { + ca_printf (pchan->ciu.piiu->pcas, + "CAC: duplicate claim attempted (while connected)?\n"); + UNLOCK (pchan->ciu.piiu->pcas); + return false; + } + + hdr = nullmsg; + hdr.m_cmmd = htons (CA_PROTO_CLAIM_CIU); + + if (CA_V44(CA_PROTOCOL_VERSION, pNetIIU->minor_version_number)) { + hdr.m_cid = pchan->cid; + pStr = ca_name (&pchan->ciu); + size = pchan->nameLength; + } + else { + hdr.m_cid = pchan->sid; + pStr = NULL; + size = 0u; + } + + hdr.m_postsize = size; + + /* + * The available field is used (abused) + * here to communicate the minor version number + * starting with CA 4.1. + */ + hdr.m_available = htonl (CA_MINOR_VERSION); + + /* + * If we are out of buffer space then postpone this + * operation until later. This avoids any possibility + * of a push pull deadlock (since this is sent when + * parsing the UDP input buffer). + */ + success = cac_push_tcp_msg_no_block (pNetIIU, &hdr, pStr); + if ( success ) { + + /* + * move to the end of the list once the claim has been sent + */ + pchan->claimPending = FALSE; + ellDelete (&pNetIIU->niiu.chidList, &pchan->node); + ellAdd (&pNetIIU->niiu.chidList, &pchan->node); + + if (!CA_V42(CA_PROTOCOL_VERSION, pNetIIU->minor_version_number)) { + cac_reconnect_channel (pNetIIU, pchan->cid, USHRT_MAX, 0); + } + } + else { + pNetIIU->claimsPending = TRUE; + } + UNLOCK (pchan->ciu.piiu->pcas); + + return success; +} + +/* + * CA_ADD_FD_REGISTRATION + * + * call their function with their argument whenever + * a new fd is added or removed + * (for a manager of the select system call under UNIX) + * + */ +int epicsShareAPI ca_add_fd_registration(CAFDHANDLER *func, void *arg) +{ + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + LOCK (pcac); + pcac->ca_fd_register_func = func; + pcac->ca_fd_register_arg = arg; + UNLOCK (pcac); + + return ECA_NORMAL; +} + +/* + * CA_DEFUNCT + * + * what is called by vacated entries in the VMS share image jump table + */ +int ca_defunct() +{ + SEVCHK (ECA_DEFUNCT, NULL); + return ECA_DEFUNCT; +} + +/* + * CA_HOST_NAME() + * + * returns a pointer to the channel's host name + * + * currently implemented as a function + * (may be implemented as a MACRO in the future) + */ +const char * epicsShareAPI ca_host_name (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + /* + * is it a local channel ? + */ + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + return pChan->piiu->pcas->ca_pHostName; + } + else { + nciu *pNetChan = (nciu *) ciuToNCIU (pChan); + + if (pNetChan->connected) { + tcpiiu *piiu = iiuToTCPIIU (pChan->piiu); + return piiu->host_name_str; + } + else { + return ""; + } + } + +} + +/* + * ca_v42_ok(chid chan) + */ +int epicsShareAPI ca_v42_ok (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + /* + * is it a local channel ? + */ + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + return TRUE; + } + else { + nciu *pNetChan = (nciu *) ciuToNCIU (pChan); + + if (pNetChan->connected) { + tcpiiu *piiu = iiuToTCPIIU (pChan->piiu); + return CA_V42 (CA_PROTOCOL_VERSION, + piiu->minor_version_number); + } + else { + return FALSE; + } + } +} + +/* + * ca_version() + * function that returns the CA version string + */ +const char * epicsShareAPI ca_version() +{ + return CA_VERSION_STRING; +} + +/* + * ca_replace_printf_handler () + */ +int epicsShareAPI ca_replace_printf_handler ( +int (*ca_printf_func)(const char *pformat, va_list args) +) +{ + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + LOCK (pcac); + if (ca_printf_func) { + pcac->ca_printf_func = ca_printf_func; + } + else { + pcac->ca_printf_func = epicsVprintf; + } + UNLOCK (pcac); + + return ECA_NORMAL; +} + +/* + * ca_printf() + */ +int ca_printf (cac *pcac, const char *pformat, ...) +{ + va_list theArgs; + int status; + + va_start (theArgs, pformat); + + status = ca_vPrintf (pcac, pformat, theArgs); + + va_end (theArgs); + + return status; +} + +/* + * ca_vPrintf() + */ +int ca_vPrintf (cac *pcac, const char *pformat, va_list args) +{ + int (*ca_printf_func)(const char *pformat, va_list args); + + if (pcac) { + if (pcac->ca_printf_func) { + ca_printf_func = pcac->ca_printf_func; + } + else { + ca_printf_func = errlogVprintf; + } + } + else { + ca_printf_func = errlogVprintf; + } + + return (*ca_printf_func) (pformat, args); +} + + +/* + * ca_field_type() + */ +short epicsShareAPI ca_field_type (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + /* + * is it a local channel ? + */ + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + lciu *pLocalChan = ciuToLCIU (pChan); + return (*pChan->piiu->pcas->localIIU.pva->p_pvType) (pLocalChan->id); + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + if (pNetChan->connected) { + return (short) pNetChan->type; + } + else { + return TYPENOTCONN; + } + } +} + +/* + * ca_element_count () + */ +unsigned long epicsShareAPI ca_element_count (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + /* + * is it a local channel ? + */ + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + lciu *pLocalChan = ciuToLCIU (pChan); + return (*pChan->piiu->pcas->localIIU.pva->p_pvNoElements) (pLocalChan->id); + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + if (pNetChan->connected) { + return pNetChan->count; + } + else { + return 0; + } + } +} + +/* + * ca_state () + */ +epicsShareFunc enum channel_state epicsShareAPI ca_state (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + if ( pChan->piiu == &pChan->piiu->pcas->localIIU.iiu ) { + return cs_conn; + } + else if ( pChan->piiu == NULL ) { + return cs_closed; + } + else { + nciu *pNChan = ciuToNCIU (pChan); + if (pNChan->connected) { + return cs_conn; + } + else if (pNChan->previousConn) { + return cs_prev_conn; + } + else { + return cs_never_conn; + } + } +} + +/* + * ca_set_puser () + */ +epicsShareFunc void epicsShareAPI ca_set_puser (chid pChanIn, void *puser) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + pChan->puser = puser; +} + +/* + * ca_read_access () + */ +epicsShareFunc unsigned epicsShareAPI ca_read_access (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + return TRUE; + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + return pNetChan->ar.read_access; + } +} + +/* + * ca_write_access () + */ +epicsShareFunc unsigned epicsShareAPI ca_write_access (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + return TRUE; + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + return pNetChan->ar.write_access; + } +} + +/* + * ca_name () + */ +epicsShareFunc const char * epicsShareAPI ca_name (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + /* + * is it a local channel ? + */ + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + lciu *pLocalChan = ciuToLCIU (pChan); + lclIIU *pLocalIIU = iiuToLIIU (pChan->piiu); + return (*pLocalIIU->pva->p_pvName) (pLocalChan->id); + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + return (const char *) (pNetChan+1); + } +} + +epicsShareFunc unsigned epicsShareAPI ca_search_attempts (chid pChanIn) +{ + baseCIU *pChan = (baseCIU *) pChanIn; + + /* + * is it a local channel ? + */ + if (pChan->piiu == &pChan->piiu->pcas->localIIU.iiu) { + return 0; + } + else { + nciu *pNetChan = ciuToNCIU (pChan); + return pNetChan->retry; + } +} + +/* + * ca_get_ioc_connection_count() + * + * returns the number of IOC's that CA is connected to + * (for testing purposes only) + */ +unsigned epicsShareAPI ca_get_ioc_connection_count () +{ + unsigned count; + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + LOCK (pcac); + + count = ellCount (&pcac->ca_iiuList); + + UNLOCK (pcac); + + return count; +} + +LOCAL void niiuShow (netIIU *piiu, unsigned level) +{ + nciu *pChan; + + pChan = (nciu *) ellFirst (&piiu->chidList); + while ( pChan ) { + printf( "%s native type=%d ", + ca_name (&pChan->ciu), ca_field_type (&pChan->ciu)); + printf( "N elements=%lu server=%s state=", + ca_element_count (&pChan->ciu), ca_host_name(&pChan->ciu)); + switch ( ca_state (&pChan->ciu) ) { + case cs_never_conn: + printf ("never connected to an IOC"); + break; + case cs_prev_conn: + printf ("disconnected from IOC"); + break; + case cs_conn: + printf ("connected to an IOC"); + break; + case cs_closed: + printf ("invalid channel"); + break; + default: + break; + } + printf("\n"); + pChan = (nciu *) ellNext (&pChan->node); + } +} + +epicsShareFunc int epicsShareAPI ca_channel_status (threadId tid) +{ + tcpiiu *piiu; + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + LOCK (pcac); + if (pcac->pudpiiu) { + niiuShow (&pcac->pudpiiu->niiu, 10); + } + piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); + while (piiu) { + niiuShow (&piiu->niiu, 10); + piiu = (tcpiiu *) ellNext (&piiu->node); + } + UNLOCK (pcac); + return ECA_NORMAL; +} + +/* + * ca_current_context () + * + * used when an auxillary thread needs to join a CA client context started + * by another thread + */ +epicsShareFunc int epicsShareAPI ca_current_context (caClientCtx *pCurrentContext) +{ + if (caClientContextId) { + void *pCtx = threadPrivateGet (caClientContextId); + if (pCtx) { + *pCurrentContext = pCtx; + return ECA_NORMAL; + } + else { + return ECA_NOCACTX; + } + } + else { + return ECA_NOCACTX; + } +} + +/* + * ca_attach_context () + * + * used when an auxillary thread needs to join a CA client context started + * by another thread + */ +epicsShareFunc int epicsShareAPI ca_attach_context (caClientCtx context) +{ + threadPrivateSet (caClientContextId, context); + return ECA_NORMAL; +} diff --git a/src/ca/acctst.c b/src/ca/acctst.c index 3c112d44a..1020b8a89 100644 --- a/src/ca/acctst.c +++ b/src/ca/acctst.c @@ -8,29 +8,29 @@ static char *sccsId = "@(#) $Id$"; /* * ANSI */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include /* * EPICS */ -#include "epicsAssert.h" -#include "tsStamp.h" +#include "epicsAssert.h" +#include "tsStamp.h" /* * CA */ -#include "cadef.h" +#include "cadef.h" -#define EVENT_ROUTINE null_event -#define CONN_ROUTINE conn +#define EVENT_ROUTINE null_event +#define CONN_ROUTINE conn -#define NUM 1 +#define NUM 1 -int conn_cb_count; +int conn_cb_count; #ifndef min #define min(A,B) ((A)>(B)?(B):(A)) @@ -45,32 +45,34 @@ int conn_cb_count; #endif #ifndef NELEMENTS -#define NELEMENTS(A) (sizeof(A)/sizeof(A[0])) +#define NELEMENTS(A) ( sizeof (A) / sizeof (A[0]) ) #endif -int doacctst(char *pname); -void test_sync_groups(chid chix); -void multiple_sg_requests(chid chix, CA_SYNC_GID gid); -void null_event(struct event_handler_args args); -void write_event(struct event_handler_args args); -void conn(struct connection_handler_args args); -void get_cb(struct event_handler_args args); -void accessSecurity_cb(struct access_rights_handler_args args); -void pend_event_delay_test(dbr_double_t request); +int doacctst(char *pname); +void test_sync_groups(chid chix); +void multiple_sg_requests(chid chix, CA_SYNC_GID gid); +void null_event(struct event_handler_args args); +void write_event(struct event_handler_args args); +void conn(struct connection_handler_args args); +void get_cb(struct event_handler_args args); +void accessSecurity_cb(struct access_rights_handler_args args); +void pend_event_delay_test(dbr_double_t request); +void performMonitorUpdateTest (chid chan); +void performDeleteTest (chid chan); void doubleTest( -chid chan, -dbr_double_t beginValue, -dbr_double_t increment, -dbr_double_t epsilon, -unsigned iterations); +chid chan, +dbr_double_t beginValue, +dbr_double_t increment, +dbr_double_t epsilon, +unsigned iterations); void floatTest( -chid chan, -dbr_float_t beginValue, -dbr_float_t increment, -dbr_float_t epsilon, -unsigned iterations); +chid chan, +dbr_float_t beginValue, +dbr_float_t increment, +dbr_float_t epsilon, +unsigned iterations); void performGrEnumTest (chid chan); @@ -78,912 +80,834 @@ void performCtrlDoubleTest (chid chan); int acctst(char *pname) { - chid chix1; - chid chix2; - chid chix3; - chid chix4; - struct dbr_gr_float *pgrfloat = NULL; - dbr_float_t *pfloat = NULL; - dbr_double_t *pdouble = NULL; - long status; - long i, j; - evid monix; - char pstring[NUM][MAX_STRING_SIZE]; - unsigned monCount=0u; + chid chix1; + chid chix2; + chid chix3; + chid chix4; + struct dbr_gr_float *pgrfloat = NULL; + dbr_float_t *pfloat = NULL; + dbr_double_t *pdouble = NULL; + long status; + long i, j; + evid monix; + char pstring[NUM][MAX_STRING_SIZE]; + unsigned monCount=0u; - SEVCHK(ca_task_initialize(), "Unable to initialize"); + SEVCHK(ca_task_initialize(), "Unable to initialize"); - conn_cb_count = 0; + conn_cb_count = 0; - printf("begin\n"); + printf("begin\n"); - printf("CA Client V%s\n", ca_version()); + printf("CA Client V%s\n", ca_version()); - /* - * CA pend event delay accuracy test - * (CA asssumes that search requests can be sent - * at least every 25 mS on all supported os) - */ - pend_event_delay_test(1.0); - pend_event_delay_test(0.1); - pend_event_delay_test(0.25); + /* + * CA pend event delay accuracy test + * (CA asssumes that search requests can be sent + * at least every 25 mS on all supported os) + */ + pend_event_delay_test(1.0); + pend_event_delay_test(0.1); + pend_event_delay_test(0.25); - /* - * verify that we dont print a disconnect message when - * we delete the last channel - * (this fails if we see a disconnect message) - */ - status = ca_search( pname, &chix3); - SEVCHK(status, NULL); - status = ca_pend_io(1000.0); - SEVCHK(status, NULL); - status = ca_clear_channel(chix3); - SEVCHK(status, NULL); + /* + * verify that we dont print a disconnect message when + * we delete the last channel + * (this fails if we see a disconnect message) + */ + status = ca_search( pname, &chix3); + SEVCHK(status, NULL); + status = ca_pend_io(1000.0); + SEVCHK(status, NULL); + status = ca_clear_channel(chix3); + SEVCHK(status, NULL); - /* - * verify lots of disconnects - * verify channel connected state variables - */ - printf("Connect/disconnect test"); - fflush(stdout); - for (i = 0; i < 10; i++) { + /* + * verify lots of disconnects + * verify channel connected state variables + */ + printf("Connect/disconnect test"); + fflush(stdout); + for (i = 0; i < 10; i++) { - status = ca_search( - pname, - &chix3); - SEVCHK(status, NULL); + status = ca_search( + pname, + &chix3); + SEVCHK(status, NULL); - status = ca_search( - pname, - &chix4); - SEVCHK(status, NULL); + status = ca_search( + pname, + &chix4); + SEVCHK(status, NULL); - status = ca_search( - pname, - &chix2); - SEVCHK(status, NULL); + status = ca_search( + pname, + &chix2); + SEVCHK(status, NULL); - status = ca_search( - pname, - &chix1); - SEVCHK(status, NULL); + status = ca_search( + pname, + &chix1); + SEVCHK(status, NULL); - if (ca_test_io() == ECA_IOINPROGRESS) { - assert(INVALID_DB_REQ(ca_field_type(chix1)) == TRUE); - assert(INVALID_DB_REQ(ca_field_type(chix2)) == TRUE); - assert(INVALID_DB_REQ(ca_field_type(chix3)) == TRUE); - assert(INVALID_DB_REQ(ca_field_type(chix4)) == TRUE); + if (ca_test_io() == ECA_IOINPROGRESS) { + assert(INVALID_DB_REQ(ca_field_type(chix1)) == TRUE); + assert(INVALID_DB_REQ(ca_field_type(chix2)) == TRUE); + assert(INVALID_DB_REQ(ca_field_type(chix3)) == TRUE); + assert(INVALID_DB_REQ(ca_field_type(chix4)) == TRUE); - assert(ca_state(chix1) == cs_never_conn); - assert(ca_state(chix2) == cs_never_conn); - assert(ca_state(chix3) == cs_never_conn); - assert(ca_state(chix4) == cs_never_conn); - } + assert(ca_state(chix1) == cs_never_conn); + assert(ca_state(chix2) == cs_never_conn); + assert(ca_state(chix3) == cs_never_conn); + assert(ca_state(chix4) == cs_never_conn); + } - status = ca_pend_io(1000.0); - SEVCHK(status, NULL); + status = ca_pend_io(1000.0); + SEVCHK(status, NULL); - printf("."); - fflush(stdout); + printf("."); + fflush(stdout); - assert(ca_test_io() == ECA_IODONE); + assert(ca_test_io() == ECA_IODONE); - assert(ca_state(chix1) == cs_conn); - assert(ca_state(chix2) == cs_conn); - assert(ca_state(chix3) == cs_conn); - assert(ca_state(chix4) == cs_conn); + assert(ca_state(chix1) == cs_conn); + assert(ca_state(chix2) == cs_conn); + assert(ca_state(chix3) == cs_conn); + assert(ca_state(chix4) == cs_conn); - SEVCHK(ca_clear_channel(chix4), NULL); - SEVCHK(ca_clear_channel(chix3), NULL); - SEVCHK(ca_clear_channel(chix2), NULL); - SEVCHK(ca_clear_channel(chix1), NULL); + SEVCHK(ca_clear_channel(chix4), NULL); + SEVCHK(ca_clear_channel(chix3), NULL); + SEVCHK(ca_clear_channel(chix2), NULL); + SEVCHK(ca_clear_channel(chix1), NULL); - /* - * verify that connections to IOC's that are - * not in use are dropped - */ - j=0; - do { - ca_pend_event (0.1); - assert (++j<100); - } - while (ca_get_ioc_connection_count()>0u); + /* + * verify that connections to IOC's that are + * not in use are dropped + */ + j=0; + do { + ca_pend_event (0.1); + assert (++j<100); + } + while (ca_get_ioc_connection_count()>0u); - } - printf("\n"); + } + printf("\n"); - /* - * look for problems with ca_search(), ca_clear_channel(), - * ca_change_connection_event(), and ca_pend_io(() combo - */ - status = ca_search(pname,& chix3); - SEVCHK(status, NULL); + /* + * look for problems with ca_search(), ca_clear_channel(), + * ca_change_connection_event(), and ca_pend_io(() combo + */ + status = ca_search(pname,& chix3); + SEVCHK(status, NULL); - status = ca_replace_access_rights_event(chix3, accessSecurity_cb); - SEVCHK(status, NULL); + status = ca_replace_access_rights_event(chix3, accessSecurity_cb); + SEVCHK(status, NULL); - /* - * verify clear before connect - */ - status = ca_search(pname, &chix4); - SEVCHK(status, NULL); + /* + * verify clear before connect + */ + status = ca_search(pname, &chix4); + SEVCHK(status, NULL); - status = ca_clear_channel(chix4); - SEVCHK(status, NULL); + status = ca_clear_channel(chix4); + SEVCHK(status, NULL); - status = ca_search(pname, &chix4); - SEVCHK(status, NULL); + status = ca_search(pname, &chix4); + SEVCHK(status, NULL); - status = ca_replace_access_rights_event(chix4, accessSecurity_cb); - SEVCHK(status, NULL); + status = ca_replace_access_rights_event(chix4, accessSecurity_cb); + SEVCHK(status, NULL); - status = ca_search(pname, &chix2); - SEVCHK(status, NULL); + status = ca_search(pname, &chix2); + SEVCHK(status, NULL); - status = ca_replace_access_rights_event(chix2, accessSecurity_cb); - SEVCHK(status, NULL); + status = ca_replace_access_rights_event(chix2, accessSecurity_cb); + SEVCHK(status, NULL); - status = ca_search(pname, &chix1); - SEVCHK(status, NULL); + status = ca_search(pname, &chix1); + SEVCHK(status, NULL); - status = ca_replace_access_rights_event(chix1, accessSecurity_cb); - SEVCHK(status, NULL); + status = ca_replace_access_rights_event(chix1, accessSecurity_cb); + SEVCHK(status, NULL); - status = ca_change_connection_event(chix1,conn); - SEVCHK(status, NULL); + status = ca_change_connection_event(chix1,conn); + SEVCHK(status, NULL); - status = ca_change_connection_event(chix1,NULL); - SEVCHK(status, NULL); + status = ca_change_connection_event(chix1,NULL); + SEVCHK(status, NULL); - status = ca_change_connection_event(chix1,conn); - SEVCHK(status, NULL); + status = ca_change_connection_event(chix1,conn); + SEVCHK(status, NULL); - status = ca_change_connection_event(chix1,NULL); - SEVCHK(status, NULL); + status = ca_change_connection_event(chix1,NULL); + SEVCHK(status, NULL); - status = ca_pend_io(1000.0); - SEVCHK(status, NULL); + status = ca_pend_io(1000.0); + SEVCHK(status, NULL); - assert (ca_state(chix1) == cs_conn); - assert (ca_state(chix2) == cs_conn); - assert (ca_state(chix3) == cs_conn); - assert (ca_state(chix4) == cs_conn); + assert (ca_state(chix1) == cs_conn); + assert (ca_state(chix2) == cs_conn); + assert (ca_state(chix3) == cs_conn); + assert (ca_state(chix4) == cs_conn); - assert (INVALID_DB_REQ(ca_field_type(chix1)) == FALSE); - assert (INVALID_DB_REQ(ca_field_type(chix2)) == FALSE); - assert (INVALID_DB_REQ(ca_field_type(chix3)) == FALSE); - assert (INVALID_DB_REQ(ca_field_type(chix4)) == FALSE); + assert (INVALID_DB_REQ(ca_field_type(chix1)) == FALSE); + assert (INVALID_DB_REQ(ca_field_type(chix2)) == FALSE); + assert (INVALID_DB_REQ(ca_field_type(chix3)) == FALSE); + assert (INVALID_DB_REQ(ca_field_type(chix4)) == FALSE); - printf("%s Read Access=%d Write Access=%d\n", - ca_name(chix1), ca_read_access(chix1), ca_write_access(chix1)); + printf("%s Read Access=%d Write Access=%d\n", + ca_name(chix1), ca_read_access(chix1), ca_write_access(chix1)); - /* - * clear chans before starting another test - */ - status = ca_clear_channel(chix1); - SEVCHK(status, NULL); - status = ca_clear_channel(chix2); - SEVCHK(status, NULL); - status = ca_clear_channel(chix3); - SEVCHK(status, NULL); - status = ca_clear_channel(chix4); - SEVCHK(status, NULL); + /* + * clear chans before starting another test + */ + status = ca_clear_channel(chix1); + SEVCHK(status, NULL); + status = ca_clear_channel(chix2); + SEVCHK(status, NULL); + status = ca_clear_channel(chix3); + SEVCHK(status, NULL); + status = ca_clear_channel(chix4); + SEVCHK(status, NULL); - /* - * verify ca_pend_io() does not see old search requests - * (that did not specify a connection handler) - */ - status = ca_search_and_connect(pname, &chix1, NULL, NULL); - SEVCHK(status, NULL); - /* - * channel will connect synchronously if on the - * local host - */ - if (ca_state(chix1)==cs_never_conn) { - status = ca_pend_io(1e-16); - if (status==ECA_TIMEOUT) { + /* + * verify ca_pend_io() does not see old search requests + * (that did not specify a connection handler) + */ + status = ca_search_and_connect(pname, &chix1, NULL, NULL); + SEVCHK(status, NULL); + /* + * channel will connect synchronously if on the + * local host + */ + if (ca_state(chix1)==cs_never_conn) { + status = ca_pend_io(1e-16); + if (status==ECA_TIMEOUT) { - printf ("waiting on pend io verify connect..."); - fflush (stdout); - while (ca_state(chix1)!=cs_conn) { - ca_pend_event(0.1); - } - printf ("done\n"); + printf ("waiting on pend io verify connect..."); + fflush (stdout); + while (ca_state(chix1)!=cs_conn) { + ca_pend_event(0.1); + } + printf ("done\n"); - /* - * we end up here if the channel isnt on the same host - */ - status = ca_search_and_connect (pname, &chix2, NULL, NULL); - SEVCHK (status, NULL); - status = ca_pend_io(1e-16); - if (status!=ECA_TIMEOUT) { - assert(ca_state(chix2)==cs_conn); - } - status = ca_clear_channel (chix2); - SEVCHK (status, NULL); - } - else { - assert (ca_state(chix1)==cs_conn); - } - } - status = ca_clear_channel(chix1); - SEVCHK (status, NULL); + /* + * we end up here if the channel isnt on the same host + */ + status = ca_search_and_connect (pname, &chix2, NULL, NULL); + SEVCHK (status, NULL); + status = ca_pend_io(1e-16); + if (status!=ECA_TIMEOUT) { + assert(ca_state(chix2)==cs_conn); + } + status = ca_clear_channel (chix2); + SEVCHK (status, NULL); + } + else { + assert (ca_state(chix1)==cs_conn); + } + } + status = ca_clear_channel(chix1); + SEVCHK (status, NULL); - /* - * verify connection handlers are working - */ - status = ca_search_and_connect(pname, &chix1, conn, NULL); - SEVCHK(status, NULL); - status = ca_search_and_connect(pname, &chix2, conn, NULL); - SEVCHK(status, NULL); - status = ca_search_and_connect(pname, &chix3, conn, NULL); - SEVCHK(status, NULL); - status = ca_search_and_connect(pname, &chix4, conn, NULL); - SEVCHK(status, NULL); + /* + * verify connection handlers are working + */ + status = ca_search_and_connect(pname, &chix1, conn, NULL); + SEVCHK(status, NULL); + status = ca_search_and_connect(pname, &chix2, conn, NULL); + SEVCHK(status, NULL); + status = ca_search_and_connect(pname, &chix3, conn, NULL); + SEVCHK(status, NULL); + status = ca_search_and_connect(pname, &chix4, conn, NULL); + SEVCHK(status, NULL); - printf("waiting on conn handler call back connect..."); - fflush(stdout); - while (conn_cb_count != 4) { - ca_pend_event(0.1); - } - printf("done\n"); + printf("waiting on conn handler call back connect..."); + fflush(stdout); + while (conn_cb_count != 4) { + ca_pend_event(0.1); + } + printf("done\n"); - performGrEnumTest (chix1); + performGrEnumTest (chix1); - performCtrlDoubleTest (chix1); + performCtrlDoubleTest (chix1); - /* - * ca_pend_io() must block - */ - if(ca_read_access(chix4)){ - dbr_float_t req; - dbr_float_t resp; + /* + * ca_pend_io() must block + */ + if(ca_read_access(chix4)){ + dbr_float_t req; + dbr_float_t resp; - printf ("get TMO test ..."); - fflush(stdout); - req = 56.57f; - resp = -99.99f; - SEVCHK(ca_put(DBR_FLOAT, chix4, &req),NULL); - SEVCHK(ca_get(DBR_FLOAT, chix4, &resp),NULL); - status = ca_pend_io(1.0e-12); - if (status==ECA_NORMAL) { - if (resp != req) { - printf ( - "get block test failed - val written %f\n", req); - printf ( - "get block test failed - val read %f\n", resp); - assert(0); - } - } - else if (resp != -99.99f) { - printf ( - "CA didnt block for get to return?\n"); - } - - req = 33.44f; - resp = -99.99f; - SEVCHK (ca_put(DBR_FLOAT, chix4, &req),NULL); - SEVCHK (ca_get(DBR_FLOAT, chix4, &resp),NULL); - SEVCHK (ca_pend_io(2000.0),NULL); - if (resp != req) { - printf ( - "get block test failed - val written %f\n", req); - printf ( - "get block test failed - val read %f\n", resp); - assert(0); - } - printf ("done\n"); - } + printf ("get TMO test ..."); + fflush(stdout); + req = 56.57f; + resp = -99.99f; + SEVCHK(ca_put(DBR_FLOAT, chix4, &req),NULL); + SEVCHK(ca_get(DBR_FLOAT, chix4, &resp),NULL); + status = ca_pend_io(1.0e-12); + if (status==ECA_NORMAL) { + if (resp != req) { + printf ( + "get block test failed - val written %f\n", req); + printf ( + "get block test failed - val read %f\n", resp); + assert(0); + } + } + else if (resp != -99.99f) { + printf ( + "CA didnt block for get to return?\n"); + } + + req = 33.44f; + resp = -99.99f; + SEVCHK (ca_put(DBR_FLOAT, chix4, &req),NULL); + SEVCHK (ca_get(DBR_FLOAT, chix4, &resp),NULL); + SEVCHK (ca_pend_io(2000.0),NULL); + if (resp != req) { + printf ( + "get block test failed - val written %f\n", req); + printf ( + "get block test failed - val read %f\n", resp); + assert(0); + } + printf ("done\n"); + } - /* - * Verify that we can do IO with the new types for ALH - */ + /* + * Verify that we can do IO with the new types for ALH + */ #if 0 - if(ca_read_access(chix4)&&ca_write_access(chix4)){ - { - dbr_put_ackt_t acktIn=1u; - dbr_put_acks_t acksIn=1u; - struct dbr_stsack_string stsackOut; + if(ca_read_access(chix4)&&ca_write_access(chix4)){ + { + dbr_put_ackt_t acktIn=1u; + dbr_put_acks_t acksIn=1u; + struct dbr_stsack_string stsackOut; - SEVCHK (ca_put(DBR_PUT_ACKT, chix4, &acktIn),NULL); - SEVCHK (ca_put(DBR_PUT_ACKS, chix4, &acksIn),NULL); - SEVCHK (ca_get(DBR_STSACK_STRING, chix4, &stsackOut),NULL); - SEVCHK (ca_pend_io(2000.0),NULL); - } + SEVCHK (ca_put(DBR_PUT_ACKT, chix4, &acktIn),NULL); + SEVCHK (ca_put(DBR_PUT_ACKS, chix4, &acksIn),NULL); + SEVCHK (ca_get(DBR_STSACK_STRING, chix4, &stsackOut),NULL); + SEVCHK (ca_pend_io(2000.0),NULL); + } #endif - /* - * Verify that we can write and then read back - * the same analog value (DBR_FLOAT) - */ - if( (ca_field_type(chix1)==DBR_DOUBLE || - ca_field_type(chix1)==DBR_FLOAT) && - ca_read_access(chix1) && - ca_write_access(chix1)){ + /* + * Verify that we can write and then read back + * the same analog value (DBR_FLOAT) + */ + if( (ca_field_type(chix1)==DBR_DOUBLE || + ca_field_type(chix1)==DBR_FLOAT) && + ca_read_access(chix1) && + ca_write_access(chix1)){ - dbr_float_t incr; - dbr_float_t epsil; - dbr_float_t base; - unsigned long iter; + dbr_float_t incr; + dbr_float_t epsil; + dbr_float_t base; + unsigned long iter; - printf ("dbr_float_t test "); - fflush (stdout); - epsil = FLT_EPSILON*4.0F; - base = FLT_MIN; - for (i=FLT_MIN_EXP; iFLT_MAX/10.0) { - iter = (unsigned long) (FLT_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - floatTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - base = FLT_MAX; - for (i=FLT_MIN_EXP; iFLT_MAX/10.0) { - iter = (unsigned long) (FLT_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - floatTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - base = - FLT_MAX; - for (i=FLT_MIN_EXP; iFLT_MAX/10.0) { - iter = (unsigned long) (FLT_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - floatTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - printf ("done\n"); - } + printf ("dbr_float_t test "); + fflush (stdout); + epsil = FLT_EPSILON*4.0F; + base = FLT_MIN; + for (i=FLT_MIN_EXP; iFLT_MAX/10.0) { + iter = (unsigned long) (FLT_MAX/fabs(incr)); + } + else { + iter = 10ul; + } + floatTest(chix1, base, incr, epsil, iter); + printf ("."); + fflush (stdout); + } + base = FLT_MAX; + for (i=FLT_MIN_EXP; iFLT_MAX/10.0) { + iter = (unsigned long) (FLT_MAX/fabs(incr)); + } + else { + iter = 10ul; + } + floatTest(chix1, base, incr, epsil, iter); + printf ("."); + fflush (stdout); + } + base = - FLT_MAX; + for (i=FLT_MIN_EXP; iFLT_MAX/10.0) { + iter = (unsigned long) (FLT_MAX/fabs(incr)); + } + else { + iter = 10ul; + } + floatTest(chix1, base, incr, epsil, iter); + printf ("."); + fflush (stdout); + } + printf ("done\n"); + } - /* - * Verify that we can write and then read back - * the same analog value (DBR_DOUBLE) - */ - if( ca_field_type(chix1)==DBR_DOUBLE && - ca_read_access(chix1) && - ca_write_access(chix1)){ + /* + * Verify that we can write and then read back + * the same analog value (DBR_DOUBLE) + */ + if( ca_field_type(chix1)==DBR_DOUBLE && + ca_read_access(chix1) && + ca_write_access(chix1)){ - dbr_double_t incr; - dbr_double_t epsil; - dbr_double_t base; - unsigned long iter; + dbr_double_t incr; + dbr_double_t epsil; + dbr_double_t base; + unsigned long iter; - printf ("dbr_double_t test "); - fflush(stdout); - epsil = DBL_EPSILON*4; - base = DBL_MIN; - for (i=DBL_MIN_EXP; iDBL_MAX/10.0) { - iter = (unsigned long) (DBL_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - doubleTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - base = DBL_MAX; - for (i=DBL_MIN_EXP; iDBL_MAX/10.0) { - iter = (unsigned long) (DBL_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - doubleTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - base = - DBL_MAX; - for (i=DBL_MIN_EXP; iDBL_MAX/10.0) { - iter = (unsigned long) (DBL_MAX/fabs(incr)); - } - else { - iter = 10ul; - } - doubleTest(chix1, base, incr, epsil, iter); - printf ("."); - fflush (stdout); - } - printf ("done\n"); - } + printf ("dbr_double_t test "); + fflush(stdout); + epsil = DBL_EPSILON*4; + base = DBL_MIN; + for (i=DBL_MIN_EXP; iDBL_MAX/10.0) { + iter = (unsigned long) (DBL_MAX/fabs(incr)); + } + else { + iter = 10ul; + } + doubleTest(chix1, base, incr, epsil, iter); + printf ("."); + fflush (stdout); + } + base = DBL_MAX; + for (i=DBL_MIN_EXP; iDBL_MAX/10.0) { + iter = (unsigned long) (DBL_MAX/fabs(incr)); + } + else { + iter = 10ul; + } + doubleTest(chix1, base, incr, epsil, iter); + printf ("."); + fflush (stdout); + } + base = - DBL_MAX; + for (i=DBL_MIN_EXP; iDBL_MAX/10.0) { + iter = (unsigned long) (DBL_MAX/fabs(incr)); + } + else { + iter = 10ul; + } + doubleTest(chix1, base, incr, epsil, iter); + printf ("."); + fflush (stdout); + } + printf ("done\n"); + } - /* - * Verify that we can write and then read back - * the same integer value (DBR_LONG) - */ - if (ca_read_access(chix1) && ca_write_access(chix1)) { + /* + * Verify that we can write and then read back + * the same integer value (DBR_LONG) + */ + if (ca_read_access(chix1) && ca_write_access(chix1)) { - dbr_long_t iter, rdbk, incr; - struct dbr_ctrl_long cl; + dbr_long_t iter, rdbk, incr; + struct dbr_ctrl_long cl; - status = ca_get (DBR_CTRL_LONG, chix1, &cl); - SEVCHK (status, "graphic long fetch failed\n"); - status = ca_pend_io (10.0); - SEVCHK (status, "graphic long pend failed\n"); + status = ca_get (DBR_CTRL_LONG, chix1, &cl); + SEVCHK (status, "graphic long fetch failed\n"); + status = ca_pend_io (10.0); + SEVCHK (status, "graphic long pend failed\n"); - incr = (cl.upper_ctrl_limit - cl.lower_ctrl_limit); - if (incr>=1) { - incr /= 1000; - if (incr==0) { - incr = 1; - } - printf ("dbr_long_t test "); - fflush (stdout); - for (iter=cl.lower_ctrl_limit; - iter<=cl.upper_ctrl_limit; iter+=incr) { + incr = (cl.upper_ctrl_limit - cl.lower_ctrl_limit); + if (incr>=1) { + incr /= 1000; + if (incr==0) { + incr = 1; + } + printf ("dbr_long_t test "); + fflush (stdout); + for (iter=cl.lower_ctrl_limit; + iter<=cl.upper_ctrl_limit; iter+=incr) { - status = ca_put (DBR_LONG, chix1, &iter); - status = ca_get (DBR_LONG, chix1, &rdbk); - status = ca_pend_io (10.0); - SEVCHK (status, "get pend failed\n"); - assert (iter == rdbk); - printf ("."); - fflush (stdout); - } - printf ("done\n"); - } - } + status = ca_put (DBR_LONG, chix1, &iter); + status = ca_get (DBR_LONG, chix1, &rdbk); + status = ca_pend_io (10.0); + SEVCHK (status, "get pend failed\n"); + assert (iter == rdbk); + printf ("."); + fflush (stdout); + } + printf ("done\n"); + } + } - /* - * verify we dont jam up on many uninterrupted - * solicitations - */ - if(ca_read_access(chix4)){ - dbr_float_t temp; + /* + * verify we dont jam up on many uninterrupted + * solicitations + */ + if(ca_read_access(chix4)){ + dbr_float_t temp; - printf("Performing multiple get test..."); - fflush(stdout); - for(i=0; i<10000; i++){ - SEVCHK(ca_get(DBR_FLOAT, chix4, &temp),NULL); - } - SEVCHK(ca_pend_io(2000.0), NULL); - printf("done.\n"); - } - else{ - printf("Skipped multiple get test - no read access\n"); - } + printf("Performing multiple get test..."); + fflush(stdout); + for(i=0; i<10000; i++){ + SEVCHK(ca_get(DBR_FLOAT, chix4, &temp),NULL); + } + SEVCHK(ca_pend_io(2000.0), NULL); + printf("done.\n"); + } + else{ + printf("Skipped multiple get test - no read access\n"); + } - /* - * verify we dont jam up on many uninterrupted requests - */ - if(ca_write_access(chix4)){ - printf("Performing multiple put test..."); - fflush(stdout); - for(i=0; i<10000; i++){ - dbr_double_t fval = 3.3; - status = ca_put(DBR_DOUBLE, chix4, &fval); - SEVCHK(status, NULL); - } - SEVCHK(ca_pend_io(2000.0), NULL); - printf("done.\n"); - } - else{ - printf("Skipped multiple put test - no write access\n"); - } + /* + * verify we dont jam up on many uninterrupted requests + */ + if(ca_write_access(chix4)){ + printf("Performing multiple put test..."); + fflush(stdout); + for(i=0; i<10000; i++){ + dbr_double_t fval = 3.3; + status = ca_put(DBR_DOUBLE, chix4, &fval); + SEVCHK(status, NULL); + } + SEVCHK(ca_pend_io(2000.0), NULL); + printf("done.\n"); + } + else{ + printf("Skipped multiple put test - no write access\n"); + } - /* - * verify we dont jam up on many uninterrupted - * solicitations - */ - if(ca_read_access(chix1)){ - unsigned count=0u; - printf("Performing multiple get callback test..."); - fflush(stdout); - for(i=0; i<10000; i++){ - status = ca_array_get_callback( - DBR_FLOAT, - 1, - chix1, - null_event, - &count); - - SEVCHK(status, NULL); - } - SEVCHK(ca_flush_io(), NULL); - while (count<10000u) { - ca_pend_event(1.0); - printf("waiting..."); - fflush(stdout); - } - printf("done.\n"); - } - else{ - printf("Skipped multiple get cb test - no read access\n"); - } + /* + * verify we dont jam up on many uninterrupted + * solicitations + */ + if(ca_read_access(chix1)){ + unsigned count=0u; + printf("Performing multiple get callback test..."); + fflush(stdout); + for(i=0; i<10000; i++){ + status = ca_array_get_callback( + DBR_FLOAT, 1, chix1, null_event, &count); + + SEVCHK(status, NULL); + } + SEVCHK(ca_flush_io(), NULL); + while (count<10000u) { + ca_pend_event(1.0); + printf("waiting..."); + fflush(stdout); + } + printf("done.\n"); + } + else{ + printf("Skipped multiple get cb test - no read access\n"); + } - /* - * verify we dont jam up on many uninterrupted - * put callback solicitations - */ - if(ca_write_access(chix1) && ca_v42_ok(chix1)){ - unsigned count=0u; - printf("Performing multiple put callback test..."); - fflush(stdout); - for(i=0; i<10000; i++){ - dbr_float_t fval = 3.3F; - status = ca_array_put_callback( - DBR_FLOAT, - 1, - chix1, - &fval, - null_event, - &count); - SEVCHK(status, NULL); - } - SEVCHK(ca_flush_io(), NULL); - while (count<10000u) { - ca_pend_event(1.0); - printf("waiting..."); - fflush(stdout); - } + /* + * verify we dont jam up on many uninterrupted + * put callback solicitations + */ + if(ca_write_access(chix1) && ca_v42_ok(chix1)){ + unsigned count=0u; + printf ("Performing multiple put callback test..."); + fflush (stdout); + for(i=0; i<10000; i++){ + dbr_float_t fval = 3.3F; + status = ca_array_put_callback ( + DBR_FLOAT, 1, chix1, &fval, + null_event, &count); + SEVCHK (status, NULL); + } + SEVCHK(ca_flush_io(), NULL); + while (count<10000u) { + ca_pend_event(1.0); + printf("waiting..."); + fflush(stdout); + } - printf("done.\n"); - } - else{ - printf("Skipped multiple put cb test - no write access\n"); - } + printf("done.\n"); + } + else{ + printf("Skipped multiple put cb test - no write access\n"); + } - /* - * verify that we detect that a large string has been written - */ - if(ca_write_access(chix1)){ - dbr_string_t stimStr; - dbr_string_t respStr; - memset(stimStr, 'a', sizeof(stimStr)); - status = ca_array_put(DBR_STRING, 1u, chix1, stimStr); - assert(status==ECA_STRTOBIG); - sprintf(stimStr, "%u", 8u); - status = ca_array_put(DBR_STRING, 1u, chix1, stimStr); - assert(status==ECA_NORMAL); - status = ca_array_get(DBR_STRING, 1u, chix1, respStr); - assert(status==ECA_NORMAL); - status = ca_pend_io(0.0); - assert(status==ECA_NORMAL); - printf( + /* + * verify that we detect that a large string has been written + */ + if(ca_write_access(chix1)){ + dbr_string_t stimStr; + dbr_string_t respStr; + memset(stimStr, 'a', sizeof(stimStr)); + status = ca_array_put(DBR_STRING, 1u, chix1, stimStr); + assert(status==ECA_STRTOBIG); + sprintf(stimStr, "%u", 8u); + status = ca_array_put(DBR_STRING, 1u, chix1, stimStr); + assert(status==ECA_NORMAL); + status = ca_array_get(DBR_STRING, 1u, chix1, respStr); + assert(status==ECA_NORMAL); + status = ca_pend_io(0.0); + assert(status==ECA_NORMAL); + printf( "Test fails if stim \"%s\" isnt roughly equiv to resp \"%s\"\n", - stimStr, respStr); - } - else{ - printf("Skipped bad string test - no write access\n"); - } + stimStr, respStr); + } + else{ + printf("Skipped bad string test - no write access\n"); + } - if(ca_v42_ok(chix1)){ - test_sync_groups(chix1); - } + if(ca_v42_ok(chix1)){ + test_sync_groups(chix1); + } - /* - * verify we can add many monitors at once - */ - printf("Performing multiple monitor test..."); - fflush(stdout); - { - unsigned count=0u; - evid mid[1000]; - dbr_float_t temp, getResp; + performMonitorUpdateTest (chix4); + performDeleteTest (chix2); - for(i=0; i1u && ca_read_access(chix1)) { + dbr_float_t *pRF, *pWF, *pEF, *pT1, *pT2; - /* - * delete the event - */ - status = ca_clear_event(mid[i]); - if(status != ECA_NORMAL){ - printf( - "Clear of event %ld %x failed because \"%s\"\n", - i, mid[i]->id, ca_message(status)); - } - SEVCHK(status,NULL); + printf("Performing %u element array test...", + ca_element_count(chix1)); + fflush(stdout); - } + pRF = (dbr_float_t *) calloc(ca_element_count(chix1), + sizeof(*pRF)); + assert(pRF!=NULL); - /* - * force all of the clear event requests to - * complete - */ - SEVCHK(ca_get(DBR_FLOAT,chix4,&temp),NULL); - SEVCHK(ca_pend_io(1000.0),NULL); - } - printf("done.\n"); - - if (VALID_DB_REQ(ca_field_type(chix4))) { - status = ca_add_event( - DBR_FLOAT, - chix4, - EVENT_ROUTINE, - &monCount, - &monix); - SEVCHK(status, NULL); - SEVCHK(ca_clear_event(monix), NULL); - status = ca_add_event( - DBR_FLOAT, - chix4, - EVENT_ROUTINE, - &monCount, - &monix); - SEVCHK(status, NULL); - } - if (VALID_DB_REQ(ca_field_type(chix4))) { - status = ca_add_event( - DBR_FLOAT, - chix4, - EVENT_ROUTINE, - &monCount, - &monix); - SEVCHK(status, NULL); - SEVCHK(ca_clear_event(monix), NULL); - } - if (VALID_DB_REQ(ca_field_type(chix3))) { - status = ca_add_event( - DBR_FLOAT, - chix3, - EVENT_ROUTINE, - &monCount, - &monix); - SEVCHK(status, NULL); - status = ca_add_event( - DBR_FLOAT, - chix3, - write_event, - &monCount, - &monix); - SEVCHK(status, NULL); - } + pWF = (dbr_float_t *)calloc(ca_element_count(chix1), + sizeof(*pWF)); + assert(pWF!=NULL); - pfloat = (dbr_float_t *) calloc(sizeof(*pfloat),NUM); - assert (pfloat); - pdouble = (dbr_double_t *) calloc(sizeof(*pdouble),NUM); - assert (pdouble); - pgrfloat = (struct dbr_gr_float *) calloc(sizeof(*pgrfloat),NUM); - assert (pgrfloat); + /* + * write some random numbers into the array + */ + if (ca_write_access(chix1)) { + pT1 = pWF; + pEF = &pWF[ca_element_count(chix1)]; + while(pT11u && ca_read_access(chix1)) { - dbr_float_t *pRF, *pWF, *pEF, *pT1, *pT2; + /* + * verify read response matches values written + */ + if (ca_read_access(chix1) && ca_write_access(chix1)) { + pEF = &pRF[ca_element_count(chix1)]; + pT1 = pRF; + pT2 = pWF; + while (pT1\n", argv[0]); - } - return 0; + if(argc == 2){ + acctst(argv[1]); + } + else{ + printf("usage: %s \n", argv[0]); + } + return 0; } #endif /*iocCore */ @@ -992,124 +916,122 @@ int main(int argc, char **argv) */ void pend_event_delay_test(dbr_double_t request) { - int status; - TS_STAMP end_time; - TS_STAMP start_time; - dbr_double_t delay; - dbr_double_t accuracy; + int status; + TS_STAMP end_time; + TS_STAMP start_time; + dbr_double_t delay; + dbr_double_t accuracy; - tsStampGetCurrent(&start_time); - status = ca_pend_event(request); - if (status != ECA_TIMEOUT) { - SEVCHK(status, NULL); - } - tsStampGetCurrent(&end_time); - delay = tsStampDiffInSeconds(&end_time,&start_time); - accuracy = 100.0*(delay-request)/request; - printf("CA pend event delay = %f sec results in error = %f %%\n", - request, accuracy); - assert (fabs(accuracy) < 10.0); + tsStampGetCurrent(&start_time); + status = ca_pend_event(request); + if (status != ECA_TIMEOUT) { + SEVCHK(status, NULL); + } + tsStampGetCurrent(&end_time); + delay = tsStampDiffInSeconds(&end_time,&start_time); + accuracy = 100.0*(delay-request)/request; + printf("CA pend event delay = %f sec results in error = %f %%\n", + request, accuracy); + assert (fabs(accuracy) < 10.0); } /* * floatTest () */ void floatTest( -chid chan, -dbr_float_t beginValue, -dbr_float_t increment, -dbr_float_t epsilon, -unsigned iterations) +chid chan, +dbr_float_t beginValue, +dbr_float_t increment, +dbr_float_t epsilon, +unsigned iterations) { - unsigned i; - dbr_float_t fval; - dbr_float_t fretval; - int status; + unsigned i; + dbr_float_t fval; + dbr_float_t fretval; + int status; - fval = beginValue; - for (i=0; i epsilon) { - printf ("float test failed val written %f\n", fval); - printf ("float test failed val read %f\n", fretval); - assert(0); - } + fval = beginValue; + for (i=0; i epsilon) { + printf ("float test failed val written %f\n", fval); + printf ("float test failed val read %f\n", fretval); + assert(0); + } - fval += increment; - } + fval += increment; + } } /* * doubleTest () */ void doubleTest( -chid chan, -dbr_double_t beginValue, -dbr_double_t increment, -dbr_double_t epsilon, -unsigned iterations) +chid chan, +dbr_double_t beginValue, +dbr_double_t increment, +dbr_double_t epsilon, +unsigned iterations) { - unsigned i; - dbr_double_t fval; - dbr_double_t fretval; - int status; + unsigned i; + dbr_double_t fval; + dbr_double_t fretval; + int status; - fval = beginValue; - for (i=0; i epsilon) { - printf ("float test failed val written %f\n", fval); - printf ("float test failed val read %f\n", fretval); - assert(0); - } + fval = beginValue; + for (i=0; i epsilon) { + printf ("float test failed val written %f\n", fval); + printf ("float test failed val read %f\n", fretval); + assert(0); + } - fval += increment; - } + fval += increment; + } } /* * null_event () */ -void null_event(struct event_handler_args args) +void null_event (struct event_handler_args args) { - unsigned *pInc = (unsigned *) args.usr; - int status; + unsigned *pInc = (unsigned *) args.usr; + int status; - /* - * no pend event in event call back test - */ - status = ca_pend_event(1e-6); - assert (status==ECA_EVDISALLOW); - - if (pInc) { - (*pInc)++; - if (*pInc%1000u == 0u) { + /* + * no pend event in event call back test + */ #if 0 - printf("1000 occurred\n"); + status = ca_pend_event (1e-6); + assert (status==ECA_EVDISALLOW); #endif - } - } + + if (pInc) { + (*pInc)++; + } + #if 0 - if (ca_state(args.chid)==cs_conn) { - status = ca_put(DBR_FLOAT, args.chid, &fval); - SEVCHK(status, "put failed in null_event()"); - } - else { - printf("null_event() called for disconnected %s\n", - ca_name(args.chid)); - } + if (ca_state(args.chid)==cs_conn) { + status = ca_put (DBR_FLOAT, args.chid, &fval); + SEVCHK(status, "put failed in null_event()"); + } + else { + printf("null_event() called for disconnected %s\n", + ca_name(args.chid)); + } #endif } @@ -1118,149 +1040,146 @@ void null_event(struct event_handler_args args) */ void write_event(struct event_handler_args args) { - int status; - dbr_float_t *pFloat = (dbr_float_t *) args.dbr; - dbr_float_t a; + int status; + dbr_float_t *pFloat = (dbr_float_t *) args.dbr; + dbr_float_t a; - if (!args.dbr) { - return; - } + if (!args.dbr) { + return; + } - a = *pFloat; - a += 10.1F; + a = *pFloat; + a += 10.1F; - status = ca_array_put( - DBR_FLOAT, - 1, - args.chid, - &a); - SEVCHK(status,NULL); - SEVCHK(ca_flush_io(), NULL); + status = ca_array_put( + DBR_FLOAT, + 1, + args.chid, + &a); + SEVCHK(status,NULL); + SEVCHK(ca_flush_io(), NULL); } void conn(struct connection_handler_args args) { #if 0 - if (args.op == CA_OP_CONN_UP) - printf("Channel On Line [%s]\n", ca_name(args.chid)); - else if (args.op == CA_OP_CONN_DOWN) - printf("Channel Off Line [%s]\n", ca_name(args.chid)); - else - printf("Ukn conn ev\n"); + if (args.op == CA_OP_CONN_UP) + printf("Channel On Line [%s]\n", ca_name(args.chid)); + else if (args.op == CA_OP_CONN_DOWN) + printf("Channel Off Line [%s]\n", ca_name(args.chid)); + else + printf("Ukn conn ev\n"); #endif - ca_get_callback(DBR_GR_FLOAT, args.chid, get_cb, NULL); + ca_get_callback(DBR_GR_FLOAT, args.chid, get_cb, NULL); } void get_cb (struct event_handler_args args) { - if(!(args.status & CA_M_SUCCESS)){ - printf("Get cb failed because \"%s\"\n", - ca_message(args.status)); - } - conn_cb_count++; + if(!(args.status & CA_M_SUCCESS)){ + printf("Get cb failed because \"%s\"\n", + ca_message(args.status)); + } + conn_cb_count++; } - /* * test_sync_groups() */ void test_sync_groups(chid chix) { - int status; - CA_SYNC_GID gid1=0; - CA_SYNC_GID gid2=0; + int status; + CA_SYNC_GID gid1=0; + CA_SYNC_GID gid2=0; - printf("Performing sync group test..."); - fflush(stdout); + printf ("Performing sync group test..."); + fflush (stdout); - status = ca_sg_create(&gid1); - SEVCHK(status, NULL); + status = ca_sg_create (&gid1); + SEVCHK (status, NULL); - multiple_sg_requests(chix, gid1); - status = ca_sg_reset(gid1); - SEVCHK(status, NULL); + multiple_sg_requests (chix, gid1); + status = ca_sg_reset (gid1); + SEVCHK (status, NULL); - status = ca_sg_create(&gid2); - SEVCHK(status, NULL); + status = ca_sg_create (&gid2); + SEVCHK (status, NULL); - multiple_sg_requests(chix, gid2); - multiple_sg_requests(chix, gid1); - status = ca_sg_test(gid2); - SEVCHK(status, "SYNC GRP2"); - status = ca_sg_test(gid1); - SEVCHK(status, "SYNC GRP1"); - status = ca_sg_block(gid1, 500.0); - SEVCHK(status, "SYNC GRP1"); - status = ca_sg_block(gid2, 500.0); - SEVCHK(status, "SYNC GRP2"); + multiple_sg_requests (chix, gid2); + multiple_sg_requests (chix, gid1); + status = ca_sg_test (gid2); + SEVCHK (status, "SYNC GRP2"); + status = ca_sg_test (gid1); + SEVCHK (status, "SYNC GRP1"); + status = ca_sg_block (gid1, 500.0); + SEVCHK (status, "SYNC GRP1"); + status = ca_sg_block (gid2, 500.0); + SEVCHK (status, "SYNC GRP2"); - status = ca_sg_delete(gid2); - SEVCHK(status, NULL); - status = ca_sg_create(&gid2); - SEVCHK(status, NULL); + status = ca_sg_delete (gid2); + SEVCHK (status, NULL); + status = ca_sg_create (&gid2); + SEVCHK (status, NULL); - multiple_sg_requests(chix, gid1); - multiple_sg_requests(chix, gid2); - status = ca_sg_block(gid1, 15.0); - SEVCHK(status, "SYNC GRP1"); - status = ca_sg_block(gid2, 15.0); - SEVCHK(status, "SYNC GRP2"); - status = ca_sg_delete(gid1); - SEVCHK(status, NULL); - status = ca_sg_delete(gid2); - SEVCHK(status, NULL); + multiple_sg_requests (chix, gid1); + multiple_sg_requests (chix, gid2); + status = ca_sg_block (gid1, 15.0); + SEVCHK (status, "SYNC GRP1"); + status = ca_sg_block (gid2, 15.0); + SEVCHK (status, "SYNC GRP2"); + status = ca_sg_delete (gid1); + SEVCHK (status, NULL); + status = ca_sg_delete (gid2); + SEVCHK (status, NULL); - printf("done\n"); + printf ("done\n"); } - - /* * multiple_sg_requests() */ void multiple_sg_requests(chid chix, CA_SYNC_GID gid) { - int status; - unsigned i; - static dbr_float_t fvalput = 3.3F; - static dbr_float_t fvalget; + int status; + unsigned i; + static dbr_float_t fvalput = 3.3F; + static dbr_float_t fvalget; - for(i=0; i<1000; i++){ - if(ca_write_access(chix)){ - status = ca_sg_array_put( - gid, - DBR_FLOAT, - 1, - chix, - &fvalput); - SEVCHK(status, NULL); - } + for(i=0; i<1000; i++){ + if(ca_write_access(chix)){ + status = ca_sg_array_put( + gid, + DBR_FLOAT, + 1, + chix, + &fvalput); + SEVCHK(status, NULL); + } - if(ca_read_access(chix)){ - status = ca_sg_array_get( - gid, - DBR_FLOAT, - 1, - chix, - &fvalget); - SEVCHK(status, NULL); - } - } + if(ca_read_access(chix)){ + status = ca_sg_array_get( + gid, + DBR_FLOAT, + 1, + chix, + &fvalget); + SEVCHK(status, NULL); + } + } } /* * accessSecurity_cb() */ -void accessSecurity_cb(struct access_rights_handler_args args) +void accessSecurity_cb(struct access_rights_handler_args args) { -# ifdef DEBUG - printf( "%s on %s has %s/%s access\n", - ca_name(args.chid), - ca_host_name(args.chid), - ca_read_access(args.chid)?"read":"noread", - ca_write_access(args.chid)?"write":"nowrite"); -# endif +# ifdef DEBUG + printf( "%s on %s has %s/%s access\n", + ca_name(args.chid), + ca_host_name(args.chid), + ca_read_access(args.chid)?"read":"noread", + ca_write_access(args.chid)?"write":"nowrite"); +# endif } /* @@ -1268,25 +1187,25 @@ void accessSecurity_cb(struct access_rights_handler_args args) */ void performGrEnumTest (chid chan) { - struct dbr_gr_enum ge; - unsigned count; - int status; - unsigned i; + struct dbr_gr_enum ge; + unsigned count; + int status; + unsigned i; - status = ca_get (DBR_GR_ENUM, chan, &ge); - SEVCHK (status, "DBR_GR_ENUM ca_get()"); + status = ca_get (DBR_GR_ENUM, chan, &ge); + SEVCHK (status, "DBR_GR_ENUM ca_get()"); - status = ca_pend_io (2.0); - assert (status == ECA_NORMAL); + status = ca_pend_io (2.0); + assert (status == ECA_NORMAL); - if (ge.no_str>0) { - count = (unsigned) ge.no_str; - printf ("Enum state str = "); - for (i=0; i0) { + count = (unsigned) ge.no_str; + printf ("Enum state str = "); + for (i=0; ilastValue = * (dbr_float_t *) args.dbr; + pET->count++; +} + +/* + * performMonitorUpdateTest + * + * 1) verify we can add many monitors at once + * 2) verify that under heavy load the last monitor + * returned is the last modification sent + */ +void performMonitorUpdateTest (chid chan) +{ + unsigned count=0u; + eventTest test[1000]; + dbr_float_t temp, getResp; + unsigned i, j; + unsigned flowCtrlCount; + unsigned tries; + + if (!ca_read_access(chan)) { + return; + } + + printf ("Performing event subscription update test..."); + fflush (stdout); + + for(i=0; ifdInfoFreeList); - - if (!pfdi) { - pfdi = (caFDInfo *) calloc (1, sizeof(*pfdi)); - if (!pfdi) { - ca_printf("CAC: no mem for select ctx?\n"); - UNLOCK; - return -1; - } - } - ellAdd (&ca_static->fdInfoList, &pfdi->node); - - FD_ZERO (&pfdi->readMask); - FD_ZERO (&pfdi->writeMask); - - maxfd = 0; - ioPending = FALSE; - for( piiu = (IIU *) iiuList.node.next; - piiu; piiu = (IIU *) piiu->node.next) { - - if (piiu->state==iiu_disconnected) { - continue; - } - - if (!FD_IN_FDSET(piiu->sock_chan)) - { - ca_printf( - "%s.%d: file number %d > FD_SETSIZE=%d ignored\n", - __FILE__, __LINE__, piiu->sock_chan, FD_SETSIZE); - continue; - } - - /* - * Dont bother receiving if we have insufficient - * space for the maximum UDP message, or space - * for one TCP byte. - */ - if (flags&CA_DO_RECVS) { - freespace = cacRingBufferWriteSize (&piiu->recv, TRUE); - if (freespace>=piiu->minfreespace) { - maxfd = max (maxfd,piiu->sock_chan); - FD_SET (piiu->sock_chan, &pfdi->readMask); - piiu->recvPending = TRUE; - ioPending = TRUE; - } - else { - piiu->recvPending = FALSE; - } - } - else { - piiu->recvPending = FALSE; - } - - if (piiu->state==iiu_connecting) { - FD_SET (piiu->sock_chan, &pfdi->writeMask); - ioPending = TRUE; - } - else if (flags&CA_DO_SENDS || piiu->pushPending) { - if (cacRingBufferReadSize(&piiu->send, FALSE)>0) { - maxfd = max (maxfd,piiu->sock_chan); - FD_SET (piiu->sock_chan, &pfdi->writeMask); - ioPending = TRUE; - } - } - } - UNLOCK; - - /* - * win32 requires this (others will - * run faster with this installed) - */ - if (!ioPending) { - /* - * recover from subtle differences between - * windows sockets and UNIX sockets implementation - * of select() - */ - if ( ptimeout->tv_sec!=0 || ptimeout->tv_usec!=0 ) { - osiSleep (ptimeout->tv_sec, ptimeout->tv_usec); - } - status = 0; - } - else { - status = select (maxfd+1, &pfdi->readMask, &pfdi->writeMask, - NULL, &autoTimeOut); - - if (status<0) { - int errnoCpy = SOCKERRNO; - - if (errnoCpy!=SOCK_EINTR) { - ca_printf ( - "CAC: unexpected select fail: %s\n", - SOCKERRSTR(errnoCpy)); - } - } - } - - /* - * get a new time stamp if we have been waiting - * for any significant length of time - */ - if (ptimeout->tv_sec || ptimeout->tv_usec) { - cac_gettimeval (&ca_static->currentTime); - } - - LOCK; - - /* - * must run through the IIU list even if no IO is pending - * if any of the IOCs are in flow control (so that an exit - * flow control msg can be sent to each of them that are) - */ - if (status>0 || (ca_static->ca_number_iiu_in_fc>0u && status>=0)) { - status = 0; - for (piiu = (IIU *) iiuList.node.next; - piiu; piiu = (IIU *) piiu->node.next) { - - if (piiu->state==iiu_disconnected) { - continue; - } - - if (FD_ISSET(piiu->sock_chan,&pfdi->readMask)) { - unsigned long bytesReceived; - - bytesReceived = (*piiu->recvBytes)(piiu); - if (bytesReceived>0) { - status++; - /* - * if we are not blocking and there is a - * message present then start to suspect that - * we are getting behind - */ - if (piiu->sock_proto==IPPROTO_TCP) { - if (ptimeout->tv_sec==0 && ptimeout->tv_usec==0) { - flow_control_on(piiu); - } - } - } - } - else if (piiu->recvPending) { - /* - * if we are looking for incoming messages - * and there are none then we are certain that - * we are not getting behind - */ - if (piiu->sock_proto==IPPROTO_TCP) { - flow_control_off(piiu); - } - } - - if (FD_ISSET(piiu->sock_chan,&pfdi->writeMask)) { - unsigned long bytesSent; - - bytesSent = (*piiu->sendBytes)(piiu); - if (bytesSent>0) { - status++; - } - } - } - } - - ellDelete (&ca_static->fdInfoList, &pfdi->node); - ellAdd (&ca_static->fdInfoFreeList, &pfdi->node); - UNLOCK; - - return status; -} - diff --git a/src/ca/caProto.h b/src/ca/caProto.h index 0a7f09016..79fcdaabf 100644 --- a/src/ca/caProto.h +++ b/src/ca/caProto.h @@ -35,37 +35,37 @@ * CA protocol number * TCP/UDP port number (bumped each major protocol change) */ -#define CA_PROTOCOL_VERSION 4u -#define CA_MINOR_VERSION 8u -#define CA_VERSION_STRING "4.8" -#define CA_UKN_MINOR_VERSION 0u /* unknown minor version */ +#define CA_PROTOCOL_VERSION 4u +#define CA_MINOR_VERSION 8u +#define CA_VERSION_STRING "4.8" +#define CA_UKN_MINOR_VERSION 0u /* unknown minor version */ #if CA_PROTOCOL_VERSION == 4u -#define CA_V41(MAJOR,MINOR) ((MINOR)>=1u) -#define CA_V42(MAJOR,MINOR) ((MINOR)>=2u) -#define CA_V43(MAJOR,MINOR) ((MINOR)>=3u) -#define CA_V44(MAJOR,MINOR) ((MINOR)>=4u) -#define CA_V45(MAJOR,MINOR) ((MINOR)>=5u) -#define CA_V46(MAJOR,MINOR) ((MINOR)>=6u) -#define CA_V47(MAJOR,MINOR) ((MINOR)>=7u) -#define CA_V48(MAJOR,MINOR) ((MINOR)>=8u) +#define CA_V41(MAJOR,MINOR) ((MINOR)>=1u) +#define CA_V42(MAJOR,MINOR) ((MINOR)>=2u) +#define CA_V43(MAJOR,MINOR) ((MINOR)>=3u) +#define CA_V44(MAJOR,MINOR) ((MINOR)>=4u) +#define CA_V45(MAJOR,MINOR) ((MINOR)>=5u) +#define CA_V46(MAJOR,MINOR) ((MINOR)>=6u) +#define CA_V47(MAJOR,MINOR) ((MINOR)>=7u) +#define CA_V48(MAJOR,MINOR) ((MINOR)>=8u) #elif CA_PROTOCOL_VERSION > 4u -#define CA_V41(MAJOR,MINOR) ( 1u ) -#define CA_V42(MAJOR,MINOR) ( 1u ) -#define CA_V43(MAJOR,MINOR) ( 1u ) -#define CA_V44(MAJOR,MINOR) ( 1u ) -#define CA_V45(MAJOR,MINOR) ( 1u ) -#define CA_V46(MAJOR,MINOR) ( 1u ) -#define CA_V47(MAJOR,MINOR) ( 1u ) -#define CA_V48(MAJOR,MINOR) ( 1u ) +#define CA_V41(MAJOR,MINOR) ( 1u ) +#define CA_V42(MAJOR,MINOR) ( 1u ) +#define CA_V43(MAJOR,MINOR) ( 1u ) +#define CA_V44(MAJOR,MINOR) ( 1u ) +#define CA_V45(MAJOR,MINOR) ( 1u ) +#define CA_V46(MAJOR,MINOR) ( 1u ) +#define CA_V47(MAJOR,MINOR) ( 1u ) +#define CA_V48(MAJOR,MINOR) ( 1u ) #else -#define CA_V41(MAJOR,MINOR) ( 0u ) -#define CA_V42(MAJOR,MINOR) ( 0u ) -#define CA_V43(MAJOR,MINOR) ( 0u ) -#define CA_V44(MAJOR,MINOR) ( 0u ) -#define CA_V45(MAJOR,MINOR) ( 0u ) -#define CA_V46(MAJOR,MINOR) ( 0u ) -#define CA_V47(MAJOR,MINOR) ( 0u ) -#define CA_V48(MAJOR,MINOR) ( 0u ) +#define CA_V41(MAJOR,MINOR) ( 0u ) +#define CA_V42(MAJOR,MINOR) ( 0u ) +#define CA_V43(MAJOR,MINOR) ( 0u ) +#define CA_V44(MAJOR,MINOR) ( 0u ) +#define CA_V45(MAJOR,MINOR) ( 0u ) +#define CA_V46(MAJOR,MINOR) ( 0u ) +#define CA_V47(MAJOR,MINOR) ( 0u ) +#define CA_V48(MAJOR,MINOR) ( 0u ) #endif /* @@ -74,14 +74,14 @@ * environment variables "EPICS_CA_REPEATER_PORT" and * "EPICS_CA_SERVER_PORT" */ -#define CA_PORT_BASE IPPORT_USERRESERVED + 56U -#define CA_SERVER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u) -#define CA_REPEATER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u+1u) +#define CA_PORT_BASE IPPORT_USERRESERVED + 56U +#define CA_SERVER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u) +#define CA_REPEATER_PORT (CA_PORT_BASE+CA_PROTOCOL_VERSION*2u+1u) /* 1492(min of ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP) joh 8-6-97 */ -#define ETHERNET_MAX_UDP (1482u-20u-8u) -#define MAX_UDP 1024u /* original MAX_UDP */ -#define MAX_TCP (MAX_UDP*16u) /* so waveforms fit */ -#define MAX_MSG_SIZE (MAX_TCP) /* the larger of tcp and udp max */ +#define ETHERNET_MAX_UDP (1482u-20u-8u) +#define MAX_UDP 1024u /* original MAX_UDP */ +#define MAX_TCP (MAX_UDP*16u) /* so waveforms fit */ +#define MAX_MSG_SIZE (MAX_TCP) /* the larger of tcp and udp max */ /* * architecture independent types @@ -89,39 +89,39 @@ * (so far this works on all archs we have ported to) */ typedef unsigned short ca_uint16_t; -typedef unsigned int ca_uint32_t; +typedef unsigned int ca_uint32_t; typedef float ca_float32_t; -typedef ca_uint32_t caResId; +typedef ca_uint32_t caResId; - /* values for m_cmmd */ -#define CA_PROTO_NOOP 0u /* do nothing, but verify TCP */ -#define CA_PROTO_EVENT_ADD 1u /* add an event */ -#define CA_PROTO_EVENT_CANCEL 2u /* cancel an event */ -#define CA_PROTO_READ 3u /* read and return a channel value*/ -#define CA_PROTO_WRITE 4u /* write a channel value */ -#define CA_PROTO_SNAPSHOT 5u /* snapshot of the system */ -#define CA_PROTO_SEARCH 6u /* IOC channel search */ -#define CA_PROTO_BUILD 7u /* build - obsolete */ -#define CA_PROTO_EVENTS_OFF 8u /* flow control */ -#define CA_PROTO_EVENTS_ON 9u /* flow control */ -#define CA_PROTO_READ_SYNC 10u /* purge old reads */ -#define CA_PROTO_ERROR 11u /* an operation failed */ -#define CA_PROTO_CLEAR_CHANNEL 12u /* free chan resources */ -#define CA_PROTO_RSRV_IS_UP 13u /* CA server has joined the net */ -#define CA_PROTO_NOT_FOUND 14u /* channel not found */ -#define CA_PROTO_READ_NOTIFY 15u /* add a one shot event */ -#define CA_PROTO_READ_BUILD 16u /* read and build - obsolete */ -#define REPEATER_CONFIRM 17u /* registration confirmation */ -#define CA_PROTO_CLAIM_CIU 18u /* client claims resource in server */ -#define CA_PROTO_WRITE_NOTIFY 19u /* notify after write chan value */ -#define CA_PROTO_CLIENT_NAME 20u /* CA V4.1 identify client */ -#define CA_PROTO_HOST_NAME 21u /* CA V4.1 identify client */ -#define CA_PROTO_ACCESS_RIGHTS 22u /* CA V4.2 asynch access rights chg */ -#define CA_PROTO_ECHO 23u /* CA V4.3 connection verify */ -#define REPEATER_REGISTER 24u /* registr for repeater fan out */ -#define CA_PROTO_SIGNAL 25u /* knock the server out of select */ -#define CA_PROTO_CLAIM_CIU_FAILED 26u /* unable to create chan resource in server */ -#define CA_PROTO_SERVER_DISCONN 27u /* server deletes PV (or channel) */ + /* values for m_cmmd */ +#define CA_PROTO_NOOP 0u /* do nothing, but verify TCP */ +#define CA_PROTO_EVENT_ADD 1u /* add an event */ +#define CA_PROTO_EVENT_CANCEL 2u /* cancel an event */ +#define CA_PROTO_READ 3u /* read and return a channel value*/ +#define CA_PROTO_WRITE 4u /* write a channel value */ +#define CA_PROTO_SNAPSHOT 5u /* snapshot of the system */ +#define CA_PROTO_SEARCH 6u /* IOC channel search */ +#define CA_PROTO_BUILD 7u /* build - obsolete */ +#define CA_PROTO_EVENTS_OFF 8u /* flow control */ +#define CA_PROTO_EVENTS_ON 9u /* flow control */ +#define CA_PROTO_READ_SYNC 10u /* purge old reads */ +#define CA_PROTO_ERROR 11u /* an operation failed */ +#define CA_PROTO_CLEAR_CHANNEL 12u /* free chan resources */ +#define CA_PROTO_RSRV_IS_UP 13u /* CA server has joined the net */ +#define CA_PROTO_NOT_FOUND 14u /* channel not found */ +#define CA_PROTO_READ_NOTIFY 15u /* add a one shot event */ +#define CA_PROTO_READ_BUILD 16u /* read and build - obsolete */ +#define REPEATER_CONFIRM 17u /* registration confirmation */ +#define CA_PROTO_CLAIM_CIU 18u /* client claims resource in server */ +#define CA_PROTO_WRITE_NOTIFY 19u /* notify after write chan value */ +#define CA_PROTO_CLIENT_NAME 20u /* CA V4.1 identify client */ +#define CA_PROTO_HOST_NAME 21u /* CA V4.1 identify client */ +#define CA_PROTO_ACCESS_RIGHTS 22u /* CA V4.2 asynch access rights chg */ +#define CA_PROTO_ECHO 23u /* CA V4.3 connection verify */ +#define REPEATER_REGISTER 24u /* registr for repeater fan out */ +#define CA_PROTO_SIGNAL 25u /* knock the server out of select */ +#define CA_PROTO_CLAIM_CIU_FAILED 26u /* unable to create chan resource in server */ +#define CA_PROTO_SERVER_DISCONN 27u /* server deletes PV (or channel) */ #define CA_PROTO_LAST_CMMD CA_PROTO_CLAIM_CIU_FAILED @@ -129,28 +129,28 @@ typedef ca_uint32_t caResId; * for use with search and not_found (if search fails and * its not a broadcast tell the client to look elesewhere) */ -#define DOREPLY 10u -#define DONTREPLY 5u +#define DOREPLY 10u +#define DONTREPLY 5u /* size of object in bytes rounded up to nearest oct word */ -#define OCT_ROUND(A) ((((unsigned long)(A))+7u)>>3u) -#define OCT_SIZEOF(A) (OCT_ROUND(sizeof(A))) +#define OCT_ROUND(A) (((A)+7)/8) +#define OCT_SIZEOF(A) (OCT_ROUND(sizeof(A))) /* size of object in bytes rounded up to nearest long word */ -#define QUAD_ROUND(A) (((unsigned long)(A))+3u)>>2u) -#define QUAD_SIZEOF(A) (QUAD_ROUND(sizeof(A))) +#define QUAD_ROUND(A) ((A)+3)/4) +#define QUAD_SIZEOF(A) (QUAD_ROUND(sizeof(A))) /* size of object in bytes rounded up to nearest short word */ -#define BI_ROUND(A) ((((unsigned long)(A))+1u)>>1u) -#define BI_SIZEOF(A) (BI_ROUND(sizeof(A))) +#define BI_ROUND(A) (((A)+1)/2) +#define BI_SIZEOF(A) (BI_ROUND(sizeof(A))) /* * For communicating access rights to the clients * * (placed in m_available hdr field of CA_PROTO_ACCESS_RIGHTS cmmd */ -#define CA_PROTO_ACCESS_RIGHT_READ (1u<<0u) -#define CA_PROTO_ACCESS_RIGHT_WRITE (1u<<1u) +#define CA_PROTO_ACCESS_RIGHT_READ (1u<<0u) +#define CA_PROTO_ACCESS_RIGHT_WRITE (1u<<1u) /* * All structures passed in the protocol must have individual @@ -166,30 +166,30 @@ typedef ca_uint32_t caResId; * the common part of each message sent/recv by the * CA server. */ -typedef struct ca_hdr { - ca_uint16_t m_cmmd; /* operation to be performed */ - ca_uint16_t m_postsize; /* size of message extension */ - ca_uint16_t m_dataType; /* operation data type */ - ca_uint16_t m_count; /* operation data count */ - ca_uint32_t m_cid; /* channel identifier */ - ca_uint32_t m_available; /* undefined message location for use - * by client processes */ +typedef struct ca_hdr { + ca_uint16_t m_cmmd; /* operation to be performed */ + ca_uint16_t m_postsize; /* size of message extension */ + ca_uint16_t m_dataType; /* operation data type */ + ca_uint16_t m_count; /* operation data count */ + ca_uint32_t m_cid; /* channel identifier */ + ca_uint32_t m_available; /* undefined message location for use + * by client processes */ }caHdr; /* * for monitor (event) message extension */ struct mon_info{ - ca_float32_t m_lval; /* low delta */ - ca_float32_t m_hval; /* high delta */ - ca_float32_t m_toval; /* period btween samples */ - ca_uint16_t m_mask; /* event select mask */ - ca_uint16_t m_pad; /* extend to 32 bits */ + ca_float32_t m_lval; /* low delta */ + ca_float32_t m_hval; /* high delta */ + ca_float32_t m_toval; /* period btween samples */ + ca_uint16_t m_mask; /* event select mask */ + ca_uint16_t m_pad; /* extend to 32 bits */ }; -struct monops { /* monitor req opi to ioc */ - caHdr m_header; - struct mon_info m_info; +struct monops { /* monitor req opi to ioc */ + caHdr m_header; + struct mon_info m_info; }; /* diff --git a/src/ca/caPutNotify.cpp b/src/ca/caPutNotify.cpp new file mode 100644 index 000000000..e6d320af6 --- /dev/null +++ b/src/ca/caPutNotify.cpp @@ -0,0 +1,75 @@ + + + + +/* + * caExtraEventLabor () + */ +LOCAL void caExtraEventLabor (void *pArg) +{ + cac *pcac = (cac *) pArg; + dbcaPutNotify *ppnb; + struct event_handler_args args; + + while (TRUE) { + semTakeStatus semStatus; + + /* + * independent lock used here in order to + * avoid any possibility of blocking + * the database (or indirectly blocking + * one client on another client). + */ + semStatus = semMutexTakeTimeout (pcac->localIIU.putNotifyLock, 60.0 /* sec */); + if (semStatus!=semTakeOK) { + ca_printf (pcac, "cac: put notify deadlock condition detected\n"); + (*pcac->localIIU.pdbAdapter->p_db_post_extra_labor) (pcac->localIIU.evctx); + break; + } + ppnb = (dbcaPutNotify *) ellGet (&pcac->localIIU.putNotifyQue); + semMutexGive (pcac->localIIU.putNotifyLock); + + /* + * break to loop exit + */ + if (!ppnb) { + break; + } + + /* + * setup arguments and call user's function + */ + args.usr = ppnb->caUserArg; + args.chid = ppnb->dbPutNotify.usrPvt; +#error type must be an external type + args.type = ppnb->dbPutNotify.dbrType; + args.count = ppnb->dbPutNotify.nRequest; + args.dbr = NULL; + if (ppnb->dbPutNotify.status) { + if (ppnb->dbPutNotify.status == S_db_Blocked) { + args.status = ECA_PUTCBINPROG; + } + else { + args.status = ECA_PUTFAIL; + } + } + else { + args.status = ECA_NORMAL; + } + + LOCK (pcac); + (*ppnb->caUserCallback) (args); + UNLOCK (pcac); + + ppnb->busy = FALSE; + } + + /* + * wakeup the TCP thread if it is waiting for a cb to complete + */ + semBinaryGive (pcac->ca_blockSem); +} + + + + diff --git a/src/ca/caRepeater.c b/src/ca/caRepeater.c deleted file mode 100644 index 82c20d70d..000000000 --- a/src/ca/caRepeater.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * caRepeater.c - * share/src/ca/caRepeater.c - * - * CA broadcast repeater executable - * - * Author: Jeff Hill - * Date: 3-27-90 - * - * PURPOSE: - * Broadcasts fan out over the LAN, but old IP kernels do not allow - * two processes on the same machine to get the same broadcast - * (and modern IP kernels do not allow two processes on the same machine - * to receive the same unicast). - * - * This code fans out UDP messages sent to the CA repeater port - * to all CA client processes that have subscribed. - * - * NOTES: - * - * see repeater.c - * - */ - -#include "iocinf.h" - -int main() -{ - ca_repeater (); - assert (0); - return (0); -} - diff --git a/src/ca/caRepeater.cpp b/src/ca/caRepeater.cpp new file mode 100644 index 000000000..82364a12f --- /dev/null +++ b/src/ca/caRepeater.cpp @@ -0,0 +1,35 @@ +/* + * $Id$ + * + * CA UDP repeater standalone executable + * + * Author: Jeff Hill + * Date: 3-27-90 + * + * PURPOSE: + * Broadcasts fan out over the LAN, but old IP kernels do not allow + * two processes on the same machine to get the same broadcast + * (and modern IP kernels do not allow two processes on the same machine + * to receive the same unicast). + * + * This code fans out UDP messages sent to the CA repeater port + * to all CA client processes that have subscribed. + * + * NOTES: + * + * see repeater.c + * + */ + +#include "epicsAssert.h" +#include "shareLib.h" + +epicsShareFunc void epicsShareAPI ca_repeater (void); + +int main() +{ + ca_repeater (); + assert (0); + return (0); +} + diff --git a/src/ca/catime.c b/src/ca/catime.c index f9dbdcc3a..ac50a2894 100644 --- a/src/ca/catime.c +++ b/src/ca/catime.c @@ -25,9 +25,7 @@ #define LOCAL static #endif -#ifndef OK -#define OK 0 -#endif +#define CATIME_OK 0 #ifndef NULL #define NULL 0 @@ -199,7 +197,7 @@ int catime (char *channelName, enum appendNumberFlag appNF) SEVCHK (ca_task_exit (), "Unable to free resources at exit"); - return OK; + return CATIME_OK; } /* @@ -216,7 +214,7 @@ LOCAL void printSearchStat(unsigned iterations) double stdDev; for (pi=itemList; pi<&itemList[iterations]; pi++) { - double retry = pi->chix->retry; + double retry = ca_search_attempts (pi->chix); X += retry; XX += retry*retry; if (retry>max) { diff --git a/src/ca/conn.c b/src/ca/conn.c deleted file mode 100644 index af8b33cd4..000000000 --- a/src/ca/conn.c +++ /dev/null @@ -1,1132 +0,0 @@ -/* $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. */ -/* */ -/* */ -/* History */ -/* ------- */ -/* .00 06xx89 joh Init Release */ -/* .01 060591 joh delinting */ -/* .02 031892 joh initial broadcast retry delay is now a #define */ -/* .03 031992 joh reset the iiu delay if the current time */ -/* is specified */ -/* .04 043092 joh check to see if the conn is up when setting */ -/* for CA_CUURRENT_TIME to be safe */ -/* .05 072792 joh better messages */ -/* .06 111892 joh tuned up cast retries */ -/* .07 010493 joh print retry count when `' */ -/* .08 010493 joh removed `' message */ -/* .09 090293 joh removed flush from manage_conn */ -/* (now handled by the send needed flag) */ -/* .10 102093 joh improved broadcast scheduling for */ -/* reconnects */ -/* .11 042994 joh removed client side heart beat */ -/* .12 110194 joh improved search scheduling */ -/* (dont send all chans in a block) */ -/* */ -/* - * $Log$ - * Revision 1.45 1999/09/02 21:44:48 jhill - * improved the way that socket error numbers are converted to strings, - * changed () to (void) in func proto, and fixed missing parameter to - * checkConnWatchdogs() bug resulting from this - * - * Revision 1.44 1999/07/16 16:38:59 jhill - * added congestion thresh parm to search alg - * - * Revision 1.43.4.1 1999/07/15 20:33:46 jhill - * added congestion thresh to search sched alg - * - * Revision 1.43 1998/11/17 02:40:56 jhill - * fixed infinite loop - * - * Revision 1.42 1998/10/07 14:30:41 jba - * Modified log message. - * - * Revision 1.41 1998/09/24 21:22:51 jhill - * detect reconnect faster when IOC reboots quickly - * - * Revision 1.40 1997/08/04 23:30:53 jhill - * detect IOC reboot faster that EPICS_CA_CONN_TMO - * - * Revision 1.39 1997/06/13 09:14:15 jhill - * connect/search proto changes - * - * Revision 1.38 1997/04/10 19:26:09 jhill - * asynch connect, faster connect, ... - * - * Revision 1.37 1996/11/02 00:50:46 jhill - * many pc port, const in API, and other changes - * - * Revision 1.36 1996/09/16 16:35:22 jhill - * local exceptions => exception handler - * - * Revision 1.35 1996/06/19 17:59:04 jhill - * many 3.13 beta changes - * - * Revision 1.34 1995/08/22 00:19:21 jhill - * use current time var to init time stamp in a beacon hash entry - * */ -/*_begin */ -/************************************************************************/ -/* */ -/* Title: IOC connection automation */ -/* File: atcs:[ca]conn.c */ -/* Environment: VMS, UNIX, vxWorks */ -/* Equipment: VAX, SUN, VME */ -/* */ -/* */ -/* */ -/************************************************************************/ -/*_end */ - -static char *sccsId = "@(#) $Id$"; - -#include "iocinf.h" -#include "bsdSocketResource.h" - -#ifdef DEBUG -#define LOGRETRYINTERVAL logRetryInterval(__FILE__, __LINE__); -LOCAL void logRetryInterval(char *pFN, unsigned lineno); -#else -#define LOGRETRYINTERVAL -#endif - -LOCAL void retrySearchRequest(CA_STATIC *ca_static); -LOCAL unsigned bhtHashIP(CA_STATIC *ca_static, const struct sockaddr_in *pina); -LOCAL int updateBeaconPeriod (CA_STATIC *ca_static, bhe *pBHE); - -/* - * checkConnWatchdogs() - */ -void checkConnWatchdogs(CA_STATIC *ca_static, unsigned closeAllowed) -{ - IIU *piiu; - ca_real delay; - - LOCK; - - piiu = (IIU *) ellFirst (&iiuList); - while (piiu) { - IIU *pnextiiu = (IIU *) ellNext (&piiu->node); - - if (piiu!=piiuCast) { - /* - * mark connection for shutdown if outgoing messages - * are not accepted by TCP/IP for several seconds - */ - if (piiu->sendPending) { - delay = cac_time_diff ( - &ca_static->currentTime, - &piiu->timeAtSendBlock); - if (delay>ca_static->ca_connectTMO) { - TAG_CONN_DOWN (piiu); - } - } - - /* - * mark connection for shutdown if an echo response - * times out - */ - if (piiu->echoPending) { - delay = cac_time_diff ( - &ca_static->currentTime, - &piiu->timeAtEchoRequest); - if (delay > CA_ECHO_TIMEOUT) { - TAG_CONN_DOWN (piiu); - } - } - } - - if (closeAllowed && piiu->state==iiu_disconnected) { - cac_close_ioc (piiu); - } - - piiu = pnextiiu; - } - - UNLOCK; -} - - -/* - * - * MANAGE_CONN - * - * manages - * o retry of disconnected channels - * o connection heart beats - * - * - */ -void manage_conn(CA_STATIC *ca_static) -{ - IIU *piiu; - ca_real delay; - - /* - * prevent recursion - */ - if(ca_static->ca_manage_conn_active){ - return; - } - - ca_static->ca_manage_conn_active = TRUE; - - /* - * issue connection heartbeat - * (if we dont see a beacon) - */ - LOCK; - for(piiu = (IIU *) iiuList.node.next; - piiu; piiu = (IIU *) piiu->node.next){ - - if (piiu==piiuCast || piiu->state!=iiu_connected) { - continue; - } - - /* - * remain backwards compatible with old servers - * ( this isnt an echo request ) - */ - if(!CA_V43(CA_PROTOCOL_VERSION, piiu->minor_version_number)){ - int stmo; - int rtmo; - - delay = cac_time_diff ( - &ca_static->currentTime, - &piiu->timeAtEchoRequest); - stmo = delay > CA_RETRY_PERIOD; - delay = cac_time_diff ( - &ca_static->currentTime, - &piiu->timeAtLastRecv); - rtmo = delay > CA_RETRY_PERIOD; - if(stmo && rtmo && !piiu->sendPending){ - piiu->timeAtEchoRequest = ca_static->currentTime; - noop_msg(piiu); - } - continue; - } - - if (!piiu->echoPending) { - delay = cac_time_diff ( - &ca_static->currentTime, - &piiu->timeAtLastRecv); - if (delay>ca_static->ca_connectTMO) { - echo_request (piiu, &ca_static->currentTime); - } - } - } - UNLOCK; - - /* - * try to attach to the repeater if we havent yet - */ - if (!ca_static->ca_repeater_contacted) { - delay = cac_time_diff ( - &ca_static->currentTime, - &ca_static->ca_last_repeater_try); - if (delay > REPEATER_TRY_PERIOD) { - ca_static->ca_last_repeater_try = ca_static->currentTime; - notify_ca_repeater(ca_static); - } - } - - /* - * Stop here if there are not any disconnected channels - */ - if (!piiuCast) { - ca_static->ca_manage_conn_active = FALSE; - return; - } - if (piiuCast->chidlist.count == 0) { - ca_static->ca_manage_conn_active = FALSE; - return; - } - - delay = cac_time_diff ( - &ca_static->ca_conn_next_retry, - &ca_static->currentTime); - - /* - * the retry sequence number if we have tried a reasonable - * number of times and if the retry delay has expired - * - * (search_retry increments once all channels have received this - * number of tries) - */ - if (delay <= 0.0 && ca_static->ca_search_retry < MAXCONNTRIES) { - retrySearchRequest (ca_static); - } - - ca_static->ca_manage_conn_active = FALSE; -} - -/* - * retrySearchRequest () - */ -LOCAL void retrySearchRequest (CA_STATIC *ca_static) -{ - ciu chan; - ciu firstChan; - int status; - unsigned nSent=0u; - - if (!piiuCast) { - return; - } - - /* - * check to see if there is nothing to do here - */ - if (ellCount(&piiuCast->chidlist)==0) { - return; - } - - LOCK; - - /* - * increment the retry sequence number - */ - ca_static->ca_search_retry_seq_no++; /* allowed to roll over */ - - /* - * dynamically adjust the number of UDP frames per - * try depending how many search requests are not - * replied to - * - * This determines how many search request can be - * sent together (at the same instant in time). - * - * The variable ca_static->ca_frames_per_try - * determines the number of UDP frames to be sent - * each time that retrySearchRequest() is called. - * If this value is too high we will waste some - * network bandwidth. If it is too low we will - * use very little of the incoming UDP message - * buffer associated with the server's port and - * will therefore take longer to connect. We - * initialize ca_static->ca_frames_per_try - * to a prime number so that it is less likely that the - * same channel is in the last UDP frame - * sent every time that this is called (and - * potentially discarded by a CA server with - * a small UDP input queue). - */ - /* - * increase frames per try only if we see better than - * a 93.75% success rate for one pass through the list - */ - if (ca_static->ca_search_responses > - (ca_static->ca_search_tries-(ca_static->ca_search_tries/16u)) ) { - /* - * increase UDP frames per try if we have a good score - */ - if (ca_static->ca_frames_per_try < MAXTRIESPERFRAME) { - /* - * it was observed that doubling the number of frames here infuenced - * an mbuf starvation lock up bug to occur in vxWorks 5.3.1 under - * heavy load testing of CA connection activity (the SENS IP kernel - * was not installed) - * - * therefore a congestion avoidance threshold similar to TCP is - * now used - */ - if (ca_static->ca_frames_per_tryca_search_tries_congest_thresh) { - ca_static->ca_frames_per_try += ca_static->ca_frames_per_try; - } - else { - ca_static->ca_frames_per_try += (ca_static->ca_frames_per_try/8) + 1; - } -#if 0 - printf ("Increasing frame count to %u t=%u r=%u\n", - ca_static->ca_frames_per_try, ca_static->ca_search_tries, - ca_static->ca_search_responses); -#endif - } - } - /* - * if we detect congestion because we have less than a 87.5% success - * rate then gradually reduce the frames per try - */ - else if (ca_static->ca_search_responses < - (ca_static->ca_search_tries-(ca_static->ca_search_tries/8u)) ) { - if (ca_static->ca_frames_per_try>1) { - ca_static->ca_frames_per_try--; - } - ca_static->ca_search_tries_congest_thresh = - ca_static->ca_frames_per_try/2 + 1; -#if 0 - printf ("Congestion detected - set frames per try to %u t=%u r=%u\n", - ca_static->ca_frames_per_try, ca_static->ca_search_tries, - ca_static->ca_search_responses); -#endif - } - - /* - * a successful search_msg() sends channel to - * the end of the list - */ - firstChan = chan = (ciu) ellFirst (&piiuCast->chidlist); - while (chan) { - - ca_static->ca_min_retry = - min(ca_static->ca_min_retry, chan->retry); - - /* - * clear counter when we reach the end of the list - * - * if we are making some progress then - * dont increase the delay between search - * requests - */ - if (ca_static->ca_pEndOfBCastList == chan) { - if (ca_static->ca_search_responses==0u) { -#if 0 - printf ("increasing search try interval\n"); -#endif - cacSetRetryInterval(ca_static, ca_static->ca_min_retry+1u); - } - - ca_static->ca_min_retry = UINT_MAX; - - /* - * increment the retry sequence number - * (this prevents the time of the next search - * try from being set to the current time if - * we are handling a response from an old - * search message) - */ - ca_static->ca_search_retry_seq_no++; /* allowed to roll over */ - - /* - * so that old search tries will not update the counters - */ - ca_static->ca_seq_no_at_list_begin = ca_static->ca_search_retry_seq_no; - - /* - * reset the search try/response counters at the end of the list - * (sequence number) so that we dont overflow, but dont subtract - * out tries that dont have a matching response yet in case they - * are delayed - */ - if (ca_static->ca_search_tries>ca_static->ca_search_responses) { - ca_static->ca_search_tries -= ca_static->ca_search_responses; - } - else { - ca_static->ca_search_tries = 0; - } - ca_static->ca_search_responses = 0; - -#if 0 - printf ("saw end of list\n"); -#endif - } - - /* - * this moves the channel to the end of the - * list (if successful) - */ - status = search_msg (chan, DONTREPLY); - if (status != ECA_NORMAL) { - nSent++; - - if (nSent>=ca_static->ca_frames_per_try) { - break; - } - - /* - * flush out the search request buffer - */ - (*piiuCast->sendBytes)(piiuCast); - - /* - * try again - */ - status = search_msg (chan, DONTREPLY); - if (status != ECA_NORMAL) { - break; - } - } - chan->retrySeqNo = ca_static->ca_search_retry_seq_no; - chan = (ciu) ellFirst (&piiuCast->chidlist); - - /* - * dont send any of the channels twice within one try - */ - if (chan==firstChan) { - /* - * add one to nSent because there may be - * one more partial frame to be sent - */ - nSent++; - - /* - * cap ca_static->ca_frames_per_try to - * the number of frames required for all of - * the unresolved channels - */ - if (ca_static->ca_frames_per_try>nSent) { - ca_static->ca_frames_per_try = nSent; - } - - break; - } - } - - UNLOCK; - - ca_static->ca_conn_next_retry = - cac_time_sum ( - &ca_static->currentTime, - &ca_static->ca_conn_retry_delay); - LOGRETRYINTERVAL - -#ifdef DEBUG - printf("sent %u at cur sec=%u cur usec=%u delay sec=%u delay usec = %u\n", - nSent, ca_static->currentTime.tv_sec, - ca_static->currentTime.tv_usec, - ca_static->ca_conn_retry_delay.tv_sec, - ca_static->ca_conn_retry_delay.tv_usec); -#endif - -} - -/* - * cacSetRetryInterval() - * (sets the interval between search tries) - */ -void cacSetRetryInterval(CA_STATIC *ca_static, unsigned retryNo) -{ - unsigned idelay; - ca_real delay; - - /* - * set the retry number - */ - ca_static->ca_search_retry = min(retryNo, MAXCONNTRIES+1u); - - /* - * set the retry interval - */ - idelay = 1u << min(ca_static->ca_search_retry, - CHAR_BIT*sizeof(idelay)-1u); - delay = idelay * CA_RECAST_DELAY; /* sec */ - /* - * place upper limit on the retry delay - */ - delay = min (CA_RECAST_PERIOD, delay); - ca_static->ca_conn_retry_delay.tv_sec = (long) delay; - ca_static->ca_conn_retry_delay.tv_usec = - (long) ((delay-ca_static->ca_conn_retry_delay.tv_sec) - * USEC_PER_SEC); -#if 0 - printf ("new search period is %u sec %u usec\n", - ca_static->ca_conn_retry_delay.tv_sec, - ca_static->ca_conn_retry_delay.tv_usec); -#endif -} - - -/* - * logRetryInterval() - */ -#ifdef DEBUG -LOCAL void logRetryInterval(char *pFN, unsigned lineno) -{ - ca_time currentTime; - ca_real delay; - CA_OSD_GET_CA_STATIC - - assert(ca_static->ca_conn_next_retry.tv_usecca_conn_next_retry, - ¤tTime); - ca_printf("%s.%d next retry in %f sec\n", - pFN, - lineno, - delay); - delay = ca_static->ca_conn_retry_delay.tv_sec + - ((double)ca_static->ca_conn_retry_delay.tv_usec)/ - USEC_PER_SEC; - ca_printf("%s.%d retry interval = %f sec - disconn count = %d\n", - pFN, - lineno, - delay, - ellCount(&piiuCast->chidlist)); -} -#endif - - -/* - * MARK_SERVER_AVAILABLE - */ -void mark_server_available ( - CA_STATIC *ca_static, - const struct sockaddr_in *pnet_addr) -{ - ciu chan; - bhe *pBHE; - unsigned port; - int netChange; - - if(!piiuCast){ - return; - } - - LOCK; - /* - * look for it in the hash table - */ - pBHE = lookupBeaconInetAddr(ca_static, pnet_addr); - if (pBHE) { - - netChange = updateBeaconPeriod (ca_static, pBHE); - - /* - * update state of health for active virtual circuits - * (only if we are not suspicious about past beacon changes - * until the next echo reply) - */ - if (pBHE->piiu) { - if (!pBHE->piiu->beaconAnomaly) { - pBHE->piiu->timeAtLastRecv = ca_static->currentTime; - } - } - } - else { - /* - * This is the first beacon seen from this server. - * Wait until 2nd beacon is seen before deciding - * if it is a new server (or just the first - * time that we have seen a server's beacon - * shortly after the program started up) - */ - netChange = FALSE; - createBeaconHashEntry (ca_static,pnet_addr, TRUE); - } - - if(!netChange){ - UNLOCK; - return; - } - - /* - * This part is needed when many machines - * have channels in a disconnected state that - * dont exist anywhere on the network. This insures - * that we dont have many CA clients synchronously - * flooding the network with broadcasts (and swamping - * out requests for valid channels). - * - * I fetch the local port number and use the low order bits - * as a pseudo random delay to prevent every one - * from replying at once. - */ - { - struct sockaddr_in saddr; - int saddr_length = sizeof(saddr); - int status; - - status = getsockname( - piiuCast->sock_chan, - (struct sockaddr *)&saddr, - &saddr_length); - assert (status>=0); - port = ntohs(saddr.sin_port); - } - - { - ca_real diff; - ca_real delay; - long idelay; - ca_time ca_delay; - ca_time next; - - delay = (port&CA_RECAST_PORT_MASK); - delay /= MSEC_PER_SEC; - delay += CA_RECAST_DELAY; - idelay = (long) delay; - ca_delay.tv_sec = idelay; - ca_delay.tv_usec = (long) ((delay-idelay) * USEC_PER_SEC); - next = cac_time_sum(&ca_static->currentTime, &ca_delay); - - diff = cac_time_diff( - &ca_static->ca_conn_next_retry, - &next); - if(diff>0.0){ - ca_static->ca_conn_next_retry = next; - LOGRETRYINTERVAL - } - } - - /* - * set retry count of all disconnected channels - * to zero - */ - cacSetRetryInterval(ca_static, 0u); - chan = (ciu) ellFirst(&piiuCast->chidlist); - while (chan) { - chan->retry = 0u; - chan = (ciu) ellNext (&chan->node); - } - - UNLOCK; - -# if DEBUG - { - char buf[64]; - ipAddrToA (pnet_addr, buf, sizeof(buf)); - printf ("new server available: %s\n", buf); - } -# endif - -} - -/* - * update beacon period - * - * updates beacon period, and looks for beacon anomalies - */ -LOCAL int updateBeaconPeriod (CA_STATIC *ca_static, bhe *pBHE) -{ - ca_real currentPeriod; - int netChange = FALSE; - - if (pBHE->timeStamp.tv_sec==0 && pBHE->timeStamp.tv_usec==0) { - /* - * this is the 1st beacon seen - the beacon time stamp - * was not initialized during BHE create because - * a TCP/IP connection created the beacon. - * (nothing to do but set the beacon time stamp and return) - */ - pBHE->timeStamp = ca_static->currentTime; - - /* - * be careful about using beacons to reset the connection - * time out watchdog until we have received a ping response - * from the IOC (this makes the software detect reconnects - * faster when the server is rebooted twice in rapid - * succession before a 1st or 2nd beacon has been received) - */ - if (pBHE->piiu) { - pBHE->piiu->beaconAnomaly = TRUE; - } - - return netChange; - } - - /* - * compute the beacon period (if we have seen at least two beacons) - */ - currentPeriod = cac_time_diff ( - &ca_static->currentTime, &pBHE->timeStamp); - - if (pBHE->averagePeriod<0.0) { - ca_real totalRunningTime; - - /* - * this is the 2nd beacon seen. We cant tell about - * the change in period at this point so we just - * initialize the average period and return. - */ - pBHE->averagePeriod = currentPeriod; - - /* - * be careful about using beacons to reset the connection - * time out watchdog until we have received a ping response - * from the IOC (this makes the software detect reconnects - * faster when the server is rebooted twice in rapid - * succession before a 2nd beacon has been received) - */ - if (pBHE->piiu) { - pBHE->piiu->beaconAnomaly = TRUE; - } - - /* - * ignore beacons seen for the first time shortly after - * init, but do not ignore beacons arriving with a short - * period because the IOC was rebooted soon after the - * client starts up. - */ - totalRunningTime = cac_time_diff ( - &pBHE->timeStamp, &ca_static->programBeginTime); - if (currentPeriod<=totalRunningTime) { - netChange = TRUE; -# ifdef DEBUG - { - char name[64]; - - ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); - ca_printf( - "new beacon from %s with period=%f running time to first beacon=%f\n", - name, currentPeriod, totalRunningTime); - } -# endif - } - } - else { - - /* - * Is this an IOC seen because of a restored - * network segment? - * - * It may be possible to get false triggers here - * if the client is busy, but this does not cause - * problems because the echo response will tell us - * that the server is available - */ - if (currentPeriod >= pBHE->averagePeriod*1.25) { - - if (pBHE->piiu) { - /* - * trigger on any missing beacon - * if connected to this server - */ - pBHE->piiu->beaconAnomaly = TRUE; - } - - if (currentPeriod >= pBHE->averagePeriod*3.25) { - /* - * trigger on any 3 contiguous missing beacons - * if not connected to this server - */ - netChange = TRUE; - } - } - - -# ifdef DEBUG - if (netChange) { - char name[64]; - - ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); - ca_printf( - "net resume seen %s cur=%f avg=%f\n", - name, currentPeriod, pBHE->averagePeriod); - } -# endif - - /* - * Is this an IOC seen because of an IOC reboot - * (beacon come at a higher rate just after the - * IOC reboots). Lower tolarance here because we - * dont have to worry about lost beacons. - * - * It may be possible to get false triggers here - * if the client is busy, but this does not cause - * problems because the echo response will tell us - * that the server is available - */ - if (currentPeriod <= pBHE->averagePeriod * 0.80) { -# ifdef DEBUG - { - char name[64]; - - ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); - ca_printf( - "reboot seen %s cur=%f avg=%f\n", - name, currentPeriod, pBHE->averagePeriod); - } -# endif - netChange = TRUE; - if (pBHE->piiu) { - pBHE->piiu->beaconAnomaly = TRUE; - } - } - - /* - * update a running average period - */ - pBHE->averagePeriod = currentPeriod*0.125 + pBHE->averagePeriod*0.875; - } - - /* - * update beacon time stamp - */ - pBHE->timeStamp = ca_static->currentTime; - - return netChange; -} - - -/* - * createBeaconHashEntry() - * - * LOCK must be applied - */ -bhe *createBeaconHashEntry( -CA_STATIC *ca_static, -const struct sockaddr_in *pina, -unsigned sawBeacon) -{ - bhe *pBHE; - unsigned index; - - pBHE = lookupBeaconInetAddr(ca_static, pina); - if(pBHE){ - return pBHE; - } - - index = bhtHashIP(ca_static,pina); - - pBHE = (bhe *)calloc(1,sizeof(*pBHE)); - if(!pBHE){ - return NULL; - } - -#ifdef DEBUG - { - char name[64]; - - ipAddrToA (pina, name, sizeof(name)); - ca_printf("created beacon entry for %s\n", name); - } -#endif - - /* - * store the inet address - */ - pBHE->inetAddr = *pina; - - /* - * set average to -1.0 so that when the next beacon - * occurs we can distinguish between: - * o new server - * o existing server's beacon we are seeing - * for the first time shortly after program - * start up - */ - pBHE->averagePeriod = -1.0; - - /* - * if creating this in response to a search reply - * and not in response to a beacon then sawBeacon - * is false and we set the beacon time stamp to - * zero (so we can correctly compute the period - * between the 1st and 2nd beacons) - */ - if (sawBeacon) { - pBHE->timeStamp = ca_static->currentTime; - } - else { - pBHE->timeStamp.tv_sec = 0; - pBHE->timeStamp.tv_usec = 0; - } - - /* - * install in the hash table - */ - pBHE->pNext = ca_static->ca_beaconHash[index]; - ca_static->ca_beaconHash[index] = pBHE; - - return pBHE; -} - - -/* - * lookupBeaconInetAddr() - * - * LOCK must be applied - */ -bhe *lookupBeaconInetAddr ( -CA_STATIC *ca_static, -const struct sockaddr_in *pina) -{ - bhe *pBHE; - unsigned index; - - index = bhtHashIP(ca_static,pina); - - pBHE = ca_static->ca_beaconHash[index]; - while (pBHE) { - if ( pBHE->inetAddr.sin_addr.s_addr == pina->sin_addr.s_addr && - pBHE->inetAddr.sin_port == pina->sin_port) { - break; - } - pBHE = pBHE->pNext; - } - return pBHE; -} - - - -/* - * removeBeaconInetAddr() - * - * LOCK must be applied - */ -void removeBeaconInetAddr ( -CA_STATIC *ca_static, -const struct sockaddr_in *pina) -{ - bhe *pBHE; - bhe **ppBHE; - unsigned index; - - index = bhtHashIP(ca_static,pina); - - ppBHE = &ca_static->ca_beaconHash[index]; - pBHE = *ppBHE; - while (pBHE) { - if ( pBHE->inetAddr.sin_addr.s_addr == pina->sin_addr.s_addr && - pBHE->inetAddr.sin_port == pina->sin_port) { - *ppBHE = pBHE->pNext; - free (pBHE); - return; - } - ppBHE = &pBHE->pNext; - pBHE = *ppBHE; - } - assert (0); -} - -/* - * bhtHashIP() - */ -LOCAL unsigned bhtHashIP(CA_STATIC *ca_static, const struct sockaddr_in *pina) -{ - unsigned index; - -#if BHT_INET_ADDR_MASK != 0xff -# error BHT_INET_ADDR_MASK changed - recode this routine ! -#endif - - index = pina->sin_addr.s_addr; - index ^= pina->sin_port; - index = (index>>16u) ^ index; - index = (index>>8u) ^ index; - index &= BHT_INET_ADDR_MASK; - assert(indexca_beaconHash)); - return index; -} - - - -/* - * freeBeaconHash() - * - * LOCK must be applied - */ -void freeBeaconHash(struct CA_STATIC *ca_temp) -{ - bhe *pBHE; - bhe **ppBHE; - int len; - - len = NELEMENTS(ca_temp->ca_beaconHash); - for( ppBHE = ca_temp->ca_beaconHash; - ppBHE < &ca_temp->ca_beaconHash[len]; - ppBHE++){ - - pBHE = *ppBHE; - while(pBHE){ - bhe *pOld; - - pOld = pBHE; - pBHE = pBHE->pNext; - free(pOld); - } - } -} - -/* - * 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 - */ -void retryPendingClaims(IIU *piiu) -{ - chid chan; - int status; - CA_STATIC *ca_static = piiu->pcas; - - LOCK; - while ( (chan= (ciu) ellFirst (&piiu->chidlist)) ) { - if (!chan->claimPending) { - piiu->claimsPending = FALSE; - break; - } - status = issue_claim_channel(chan); - if (status!=ECA_NORMAL) { - break; - } - } - UNLOCK; -} - -/* - * Add chan to IIU and guarantee that - * one chan on the B cast IIU list is pointed to by - * ca_pEndOfBCastList - */ -void addToChanList(ciu chan, IIU *piiu) -{ - CA_STATIC *ca_static = piiu->pcas; - if (piiu==piiuCast) { - /* - * add to the beginning of the list so that search requests for - * this channel will be sent first (since the retry count is zero) - */ - if (ellCount(&piiu->chidlist)==0) { - ca_static->ca_pEndOfBCastList = chan; - } - /* - * add to the front of the list so that - * search requests for new channels will be sent first - */ - chan->retry = 0u; - ellInsert(&piiu->chidlist, NULL, &chan->node); - } - else { - /* - * 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) - */ - chan->claimPending = TRUE; - ellInsert(&piiu->chidlist, NULL, &chan->node); - } - chan->piiu = piiu; -} - -/* - * Remove chan from B-cast IIU and guarantee that - * one chan on the list is pointed to by - * ca_pEndOfBCastList - */ -void removeFromChanList (ciu chan) -{ - IIU *piiu = (IIU *) chan->piiu; - CA_STATIC *ca_static = piiu->pcas; - - if (piiu==piiuCast) { - if (ca_static->ca_pEndOfBCastList == chan) { - if (ellPrevious(&chan->node)) { - ca_static->ca_pEndOfBCastList = (ciu) - ellPrevious (&chan->node); - } - else { - ca_static->ca_pEndOfBCastList = (ciu) - ellLast (&piiu->chidlist); - } - } - } - else if (ellCount (&piiu->chidlist)==1) { - TAG_CONN_DOWN(piiu); - } - ellDelete (&piiu->chidlist, &chan->node); - chan->piiu = NULL; -} - diff --git a/src/ca/conn.cpp b/src/ca/conn.cpp new file mode 100644 index 000000000..4c4f5834f --- /dev/null +++ b/src/ca/conn.cpp @@ -0,0 +1,677 @@ +/* $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" + +#ifdef DEBUG +#define LOGRETRYINTERVAL logRetryInterval(__FILE__, __LINE__); +LOCAL void logRetryInterval (pcac, char *pFN, unsigned lineno); +#else +#define LOGRETRYINTERVAL +#endif + +/* + * checkConnWatchdogs() + */ +void checkConnWatchdogs (cac *pcac) +{ + tcpiiu *piiu; + ca_real delay; + + LOCK (pcac); + + piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); + while (piiu) { + tcpiiu *pnextiiu = (tcpiiu *) ellNext (&piiu->node); + + /* + * mark connection for shutdown if outgoing messages + * are not accepted by TCP/IP for EPICS_CA_CONN_TMO seconds + */ + if (piiu->sendPending) { + delay = tsStampDiffInSeconds (&pcac->currentTime, + &piiu->timeAtSendBlock); + if (delay>pcac->ca_connectTMO) { + initiateShutdownTCPIIU (piiu); + } + } + /* + * otherwise if we dont already have a send timer pending + * mark the connection for shutdown if an echo response + * times out + */ + else if (piiu->echoPending) { + + delay = tsStampDiffInSeconds (&pcac->currentTime, + &piiu->timeAtEchoRequest); + if (delay > CA_ECHO_TIMEOUT) { + initiateShutdownTCPIIU (piiu); + } + } + + piiu = pnextiiu; + } + + UNLOCK (pcac); +} + +/* + * + * MANAGE_CONN + * + * manages + * o retry of disconnected channels + * o connection heart beats + * + * + */ +void manage_conn (cac *pcac) +{ + tcpiiu *piiu; + ca_real delay; + + /* + * prevent recursion + */ + if(pcac->ca_manage_conn_active){ + return; + } + + pcac->ca_manage_conn_active = TRUE; + + /* + * issue connection heartbeat + * (if we dont see a beacon) + */ + LOCK (pcac); + for (piiu = (tcpiiu *) ellFirst (&pcac->ca_iiuList); + piiu; piiu = (tcpiiu *) ellNext (&piiu->node) ) { + + if (piiu->state!=iiu_connected) { + continue; + } + + /* + * remain backwards compatible with old servers + * ( this isnt an echo request ) + */ + if(!CA_V43(CA_PROTOCOL_VERSION, piiu->minor_version_number)){ + int stmo; + int rtmo; + + delay = tsStampDiffInSeconds (&pcac->currentTime, + &piiu->timeAtEchoRequest); + stmo = delay > CA_RETRY_PERIOD; + delay = tsStampDiffInSeconds (&pcac->currentTime, + &piiu->timeAtLastRecv); + rtmo = delay > CA_RETRY_PERIOD; + if(stmo && rtmo && !piiu->sendPending){ + piiu->timeAtEchoRequest = pcac->currentTime; + noop_msg (piiu); + } + continue; + } + + if (!piiu->echoPending) { + /* + * check delay since last beacon + */ + delay = tsStampDiffInSeconds (&pcac->currentTime, + &piiu->pBHE->timeStamp); + if (delay>pcac->ca_connectTMO) { + /* + * check delay since last virtual circuit receive + */ + delay = tsStampDiffInSeconds (&pcac->currentTime, + &piiu->timeAtLastRecv); + if (delay>pcac->ca_connectTMO) { + /* + * no activity - so ping through the virtual circuit + */ + echo_request (piiu); + } + } + } + } + UNLOCK (pcac); + + /* + * Stop here if there are not any disconnected channels + */ + if (!pcac->pudpiiu) { + pcac->ca_manage_conn_active = FALSE; + return; + } + if (pcac->pudpiiu->niiu.chidList.count == 0) { + pcac->ca_manage_conn_active = FALSE; + return; + } + + pcac->ca_manage_conn_active = FALSE; +} + +/* + * update beacon period + * + * updates beacon period, and looks for beacon anomalies + */ +LOCAL int updateBeaconPeriod (cac *pcac, bhe *pBHE) +{ + ca_real currentPeriod; + int netChange = FALSE; + + if (pBHE->timeStamp.secPastEpoch==0 && pBHE->timeStamp.nsec==0) { + /* + * this is the 1st beacon seen - the beacon time stamp + * was not initialized during BHE create because + * a TCP/IP connection created the beacon. + * (nothing to do but set the beacon time stamp and return) + */ + pBHE->timeStamp = pcac->currentTime; + + /* + * be careful about using beacons to reset the connection + * time out watchdog until we have received a ping response + * from the IOC (this makes the software detect reconnects + * faster when the server is rebooted twice in rapid + * succession before a 1st or 2nd beacon has been received) + */ + if (pBHE->piiu) { + pBHE->piiu->beaconAnomaly = TRUE; + } + + return netChange; + } + + /* + * compute the beacon period (if we have seen at least two beacons) + */ + currentPeriod = tsStampDiffInSeconds (&pcac->currentTime, + &pBHE->timeStamp); + if (pBHE->averagePeriod<0.0) { + ca_real totalRunningTime; + + /* + * this is the 2nd beacon seen. We cant tell about + * the change in period at this point so we just + * initialize the average period and return. + */ + pBHE->averagePeriod = currentPeriod; + + /* + * be careful about using beacons to reset the connection + * time out watchdog until we have received a ping response + * from the IOC (this makes the software detect reconnects + * faster when the server is rebooted twice in rapid + * succession before a 2nd beacon has been received) + */ + if (pBHE->piiu) { + pBHE->piiu->beaconAnomaly = TRUE; + } + + /* + * ignore beacons seen for the first time shortly after + * init, but do not ignore beacons arriving with a short + * period because the IOC was rebooted soon after the + * client starts up. + */ + totalRunningTime = tsStampDiffInSeconds (&pBHE->timeStamp, + &pcac->programBeginTime); + if (currentPeriod<=totalRunningTime) { + netChange = TRUE; +# ifdef DEBUG + { + char name[64]; + + ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); + ca_printf (pcac, + "new beacon from %s with period=%f running time to first beacon=%f\n", + name, currentPeriod, totalRunningTime); + } +# endif + } + } + else { + + /* + * Is this an IOC seen because of a restored + * network segment? + * + * It may be possible to get false triggers here + * if the client is busy, but this does not cause + * problems because the echo response will tell us + * that the server is available + */ + if (currentPeriod >= pBHE->averagePeriod*1.25) { + + if (pBHE->piiu) { + /* + * trigger on any missing beacon + * if connected to this server + */ + pBHE->piiu->beaconAnomaly = TRUE; + } + + if (currentPeriod >= pBHE->averagePeriod*3.25) { + /* + * trigger on any 3 contiguous missing beacons + * if not connected to this server + */ + netChange = TRUE; + } + } + + +# ifdef DEBUG + if (netChange) { + char name[64]; + + ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); + ca_printf (pcac, + "net resume seen %s cur=%f avg=%f\n", + name, currentPeriod, pBHE->averagePeriod); + } +# endif + + /* + * Is this an IOC seen because of an IOC reboot + * (beacon come at a higher rate just after the + * IOC reboots). Lower tolarance here because we + * dont have to worry about lost beacons. + * + * It may be possible to get false triggers here + * if the client is busy, but this does not cause + * problems because the echo response will tell us + * that the server is available + */ + if (currentPeriod <= pBHE->averagePeriod * 0.80) { +# ifdef DEBUG + { + char name[64]; + + ipAddrToA (&pBHE->inetAddr, name, sizeof(name)); + ca_printf (pcac, + "reboot seen %s cur=%f avg=%f\n", + name, currentPeriod, pBHE->averagePeriod); + } +# endif + netChange = TRUE; + if (pBHE->piiu) { + pBHE->piiu->beaconAnomaly = TRUE; + } + } + + /* + * update a running average period + */ + pBHE->averagePeriod = currentPeriod*0.125 + pBHE->averagePeriod*0.875; + } + + /* + * update beacon time stamp + */ + pBHE->timeStamp = pcac->currentTime; + + return netChange; +} + +/* + * MARK_SERVER_AVAILABLE + */ +void mark_server_available (cac *pcac, const struct sockaddr_in *pnet_addr) +{ + nciu *chan; + bhe *pBHE; + unsigned port; + int netChange; + + if (!pcac->pudpiiu) { + return; + } + + LOCK (pcac); + /* + * look for it in the hash table + */ + pBHE = lookupBeaconInetAddr (pcac, pnet_addr); + if (pBHE) { + + netChange = updateBeaconPeriod (pcac, pBHE); + + /* + * update state of health for active virtual circuits + * (only if we are not suspicious about past beacon changes + * until the next echo reply) + */ + if (pBHE->piiu) { + if (!pBHE->piiu->beaconAnomaly) { + pBHE->piiu->timeAtLastRecv = pcac->currentTime; + } + } + } + else { + /* + * This is the first beacon seen from this server. + * Wait until 2nd beacon is seen before deciding + * if it is a new server (or just the first + * time that we have seen a server's beacon + * shortly after the program started up) + */ + netChange = FALSE; + createBeaconHashEntry (pcac, pnet_addr, TRUE); + } + + if (!netChange) { + UNLOCK (pcac); + return; + } + + /* + * This part is needed when many machines + * have channels in a disconnected state that + * dont exist anywhere on the network. This insures + * that we dont have many CA clients synchronously + * flooding the network with broadcasts (and swamping + * out requests for valid channels). + * + * I fetch the local port number and use the low order bits + * as a pseudo random delay to prevent every one + * from replying at once. + */ + { + struct sockaddr_in saddr; + int saddr_length = sizeof(saddr); + int status; + + status = getsockname ( + pcac->pudpiiu->sock, + (struct sockaddr *)&saddr, + &saddr_length); + assert (status>=0); + port = ntohs(saddr.sin_port); + } + + { + ca_real delay; + + delay = (port&CA_RECAST_PORT_MASK); + delay /= MSEC_PER_SEC; + delay += CA_RECAST_DELAY; + + pcac->pudpiiu->searchTmr.reset (delay); + } + + /* + * set retry count of all disconnected channels + * to zero + */ + chan = (nciu *) ellFirst (&pcac->pudpiiu->niiu.chidList); + while (chan) { + chan->retry = 0u; + chan = (nciu *) ellNext (&chan->node); + } + + UNLOCK (pcac); + +# if DEBUG + { + char buf[64]; + ipAddrToA (pnet_addr, buf, sizeof(buf)); + printf ("new server available: %s\n", buf); + } +# endif + +} + +/* + * bhtHashIP () + */ +LOCAL unsigned bhtHashIP (cac *pcac, const struct sockaddr_in *pina) +{ + unsigned index; + +#if BHT_INET_ADDR_MASK != 0xff +# error BHT_INET_ADDR_MASK changed - recode this routine ! +#endif + + index = pina->sin_addr.s_addr; + index ^= pina->sin_port; + index = (index>>16u) ^ index; + index = (index>>8u) ^ index; + index &= BHT_INET_ADDR_MASK; + assert(indexca_beaconHash)); + return index; +} + +/* + * createBeaconHashEntry() + * + * LOCK must be applied + */ +bhe *createBeaconHashEntry( +cac *pcac, +const struct sockaddr_in *pina, +unsigned sawBeacon) +{ + bhe *pBHE; + unsigned index; + + pBHE = lookupBeaconInetAddr(pcac, pina); + if(pBHE){ + return pBHE; + } + + index = bhtHashIP (pcac,pina); + + pBHE = (bhe *)calloc (1,sizeof(*pBHE)); + if(!pBHE){ + return NULL; + } + +#ifdef DEBUG + { + char name[64]; + + ipAddrToA (pina, name, sizeof(name)); + ca_printf (pcac, "created beacon entry for %s\n", name); + } +#endif + + /* + * store the inet address + */ + pBHE->inetAddr = *pina; + + /* + * set average to -1.0 so that when the next beacon + * occurs we can distinguish between: + * o new server + * o existing server's beacon we are seeing + * for the first time shortly after program + * start up + */ + pBHE->averagePeriod = -1.0; + + /* + * if creating this in response to a search reply + * and not in response to a beacon then sawBeacon + * is false and we set the beacon time stamp to + * zero (so we can correctly compute the period + * between the 1st and 2nd beacons) + */ + if (sawBeacon) { + LOCK (pcac) + pBHE->timeStamp = pcac->currentTime; + UNLOCK (pcac) + } + else { + pBHE->timeStamp.secPastEpoch = 0; + pBHE->timeStamp.nsec = 0; + } + + /* + * install in the hash table + */ + pBHE->pNext = pcac->ca_beaconHash[index]; + pcac->ca_beaconHash[index] = pBHE; + + return pBHE; +} + +/* + * lookupBeaconInetAddr() + * + * LOCK must be applied + */ +bhe *lookupBeaconInetAddr ( +cac *pcac, +const struct sockaddr_in *pina) +{ + bhe *pBHE; + unsigned index; + + index = bhtHashIP (pcac,pina); + + pBHE = pcac->ca_beaconHash[index]; + while (pBHE) { + if ( pBHE->inetAddr.sin_addr.s_addr == pina->sin_addr.s_addr && + pBHE->inetAddr.sin_port == pina->sin_port) { + break; + } + pBHE = pBHE->pNext; + } + return pBHE; +} + +/* + * removeBeaconInetAddr() + * + * LOCK must be applied + */ +void removeBeaconInetAddr ( +cac *pcac, +const struct sockaddr_in *pina) +{ + bhe *pBHE; + bhe **ppBHE; + unsigned index; + + index = bhtHashIP (pcac,pina); + + ppBHE = &pcac->ca_beaconHash[index]; + pBHE = *ppBHE; + while (pBHE) { + if ( pBHE->inetAddr.sin_addr.s_addr == pina->sin_addr.s_addr && + pBHE->inetAddr.sin_port == pina->sin_port) { + *ppBHE = pBHE->pNext; + free (pBHE); + return; + } + ppBHE = &pBHE->pNext; + pBHE = *ppBHE; + } + assert (0); +} + +/* + * freeBeaconHash() + * + * LOCK must be applied + */ +void freeBeaconHash(struct cac *ca_temp) +{ + bhe *pBHE; + bhe **ppBHE; + int len; + + len = NELEMENTS(ca_temp->ca_beaconHash); + for( ppBHE = ca_temp->ca_beaconHash; + ppBHE < &ca_temp->ca_beaconHash[len]; + ppBHE++){ + + pBHE = *ppBHE; + while(pBHE){ + bhe *pOld; + + pOld = pBHE; + pBHE = pBHE->pNext; + free(pOld); + } + } +} + +/* + * Add chan to iiu and guarantee that + * one chan on the B cast iiu list is pointed to by + * ca_pEndOfBCastList + */ +void addToChanList (nciu *chan, netIIU *piiu) +{ + LOCK (piiu->iiu.pcas); + if ( piiu == &piiu->iiu.pcas->pudpiiu->niiu ) { + /* + * add to the beginning of the list so that search requests for + * this channel will be sent first (since the retry count is zero) + */ + if (ellCount(&piiu->chidList)==0) { + piiu->iiu.pcas->ca_pEndOfBCastList = chan; + } + /* + * add to the front of the list so that + * search requests for new channels will be sent first + */ + chan->retry = 0u; + ellInsert (&piiu->chidList, NULL, &chan->node); + } + else { + /* + * 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) + */ + chan->claimPending = TRUE; + ellInsert (&piiu->chidList, NULL, &chan->node); + } + chan->ciu.piiu = &piiu->iiu; + UNLOCK (piiu->iiu.pcas); +} + +/* + * Remove chan from B-cast IIU and guarantee that + * one chan on the list is pointed to by + * ca_pEndOfBCastList + */ +void removeFromChanList (nciu *chan) +{ + if ( chan->ciu.piiu == &chan->ciu.piiu->pcas->pudpiiu->niiu.iiu ) { + if (chan->ciu.piiu->pcas->ca_pEndOfBCastList == chan) { + if (ellPrevious(&chan->node)) { + chan->ciu.piiu->pcas->ca_pEndOfBCastList = (nciu *) + ellPrevious (&chan->node); + } + else { + chan->ciu.piiu->pcas->ca_pEndOfBCastList = (nciu *) + ellLast (&chan->ciu.piiu->pcas->pudpiiu->niiu.chidList); + } + } + ellDelete (&chan->ciu.piiu->pcas->pudpiiu->niiu.chidList, &chan->node); + } + else { + tcpiiu *piiu = iiuToTCPIIU (chan->ciu.piiu); + if ( ellCount (&piiu->niiu.chidList) == 1 ) { + initiateShutdownTCPIIU (piiu); + } + ellDelete (&piiu->niiu.chidList, &chan->node); + } + chan->ciu.piiu = NULL; +} + diff --git a/src/ca/convert.c b/src/ca/convert.cpp similarity index 92% rename from src/ca/convert.c rename to src/ca/convert.cpp index 68eaa651d..30f556e7a 100644 --- a/src/ca/convert.c +++ b/src/ca/convert.cpp @@ -28,8 +28,6 @@ * */ -static char *sccsId = "@(#) $Id$"; - #include #include "iocinf.h" @@ -168,8 +166,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - char *pSrc = s; - char *pDest = d; + char *pSrc = (char *) s; + char *pDest = (char *) d; /* convert "in place" -> nothing to do */ if (s == d) @@ -196,8 +194,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - dbr_short_t *pSrc = s; - dbr_short_t *pDest = d; + dbr_short_t *pSrc = (dbr_short_t *) s; + dbr_short_t *pDest = (dbr_short_t *) d; unsigned long i; for(i=0; i nothing to do */ if (s == d) @@ -251,8 +249,8 @@ unsigned long num /* number of values */ ) { unsigned long i; - dbr_long_t *pSrc = s; - dbr_long_t *pDest = d; + dbr_long_t *pSrc = (dbr_long_t *) s; + dbr_long_t *pDest = (dbr_long_t *) d; for(i=0; istatus = dbr_ntohs(pSrc->status); @@ -423,8 +421,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_sts_int *pSrc = s; - struct dbr_sts_int *pDest = d; + struct dbr_sts_int *pSrc = (struct dbr_sts_int *) s; + struct dbr_sts_int *pDest = (struct dbr_sts_int *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); @@ -458,8 +456,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_sts_float *pSrc = s; - struct dbr_sts_float *pDest = d; + struct dbr_sts_float *pSrc = (struct dbr_sts_float *) s; + struct dbr_sts_float *pDest = (struct dbr_sts_float *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -484,8 +482,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_sts_double *pSrc = s; - struct dbr_sts_double *pDest = d; + struct dbr_sts_double *pSrc = (struct dbr_sts_double *) s; + struct dbr_sts_double *pDest = (struct dbr_sts_double *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -513,8 +511,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_sts_enum *pSrc = s; - struct dbr_sts_enum *pDest = d; + struct dbr_sts_enum *pSrc = (struct dbr_sts_enum *) s; + struct dbr_sts_enum *pDest = (struct dbr_sts_enum *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -541,8 +539,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_gr_int *pSrc = s; - struct dbr_gr_int *pDest = d; + struct dbr_gr_int *pSrc = (struct dbr_gr_int *) s; + struct dbr_gr_int *pDest = (struct dbr_gr_int *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -578,8 +576,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_gr_char *pSrc = s; - struct dbr_gr_char *pDest = d; + struct dbr_gr_char *pSrc = (struct dbr_gr_char *) s; + struct dbr_gr_char *pDest = (struct dbr_gr_char *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -619,8 +617,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_gr_long *pSrc = s; - struct dbr_gr_long *pDest = d; + struct dbr_gr_long *pSrc = (struct dbr_gr_long *) s; + struct dbr_gr_long *pDest = (struct dbr_gr_long *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -657,8 +655,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_gr_enum *pSrc = s; - struct dbr_gr_enum *pDest = d; + struct dbr_gr_enum *pSrc = (struct dbr_gr_enum *) s; + struct dbr_gr_enum *pDest = (struct dbr_gr_enum *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -690,8 +688,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_gr_double *pSrc = s; - struct dbr_gr_double *pDest = d; + struct dbr_gr_double *pSrc = (struct dbr_gr_double *) s; + struct dbr_gr_double *pDest = (struct dbr_gr_double *) d; /* these are same for vax to ieee or ieee to vax */ pDest->status = dbr_ntohs(pSrc->status); @@ -752,8 +750,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_gr_float *pSrc = s; - struct dbr_gr_float *pDest = d; + struct dbr_gr_float *pSrc = (struct dbr_gr_float *) s; + struct dbr_gr_float *pDest = (struct dbr_gr_float *) d; /* these are same for vax to ieee or ieee to vax */ pDest->status = dbr_ntohs(pSrc->status); @@ -815,8 +813,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_ctrl_int *pSrc = s; - struct dbr_ctrl_int *pDest = d; + struct dbr_ctrl_int *pSrc = (struct dbr_ctrl_int *) s; + struct dbr_ctrl_int *pDest = (struct dbr_ctrl_int *) d; /* vax to ieee or ieee to vax -- same code */ pDest->status = dbr_ntohs(pSrc->status); @@ -855,8 +853,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_ctrl_long *pSrc = s; - struct dbr_ctrl_long *pDest = d; + struct dbr_ctrl_long *pSrc = (struct dbr_ctrl_long*) s; + struct dbr_ctrl_long *pDest = (struct dbr_ctrl_long *) d; /* vax to ieee or ieee to vax -- same code */ pDest->status = dbr_ntohs(pSrc->status); @@ -895,8 +893,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_ctrl_char *pSrc = s; - struct dbr_ctrl_char *pDest = d; + struct dbr_ctrl_char *pSrc = (struct dbr_ctrl_char *) s; + struct dbr_ctrl_char *pDest = (struct dbr_ctrl_char *) d; /* vax to ieee or ieee to vax -- same code */ pDest->status = dbr_ntohs(pSrc->status); @@ -933,8 +931,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_ctrl_double *pSrc = s; - struct dbr_ctrl_double *pDest = d; + struct dbr_ctrl_double *pSrc = (struct dbr_ctrl_double *) s; + struct dbr_ctrl_double *pDest = (struct dbr_ctrl_double *) d; /* these are the same for ieee to vax or vax to ieee */ pDest->status = dbr_ntohs(pSrc->status); @@ -997,8 +995,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_ctrl_float *pSrc = s; - struct dbr_ctrl_float *pDest = d; + struct dbr_ctrl_float *pSrc = (struct dbr_ctrl_float *) s; + struct dbr_ctrl_float *pDest = (struct dbr_ctrl_float *) d; /* these are the same for ieee to vaax or vax to ieee */ pDest->status = dbr_ntohs(pSrc->status); @@ -1060,8 +1058,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_ctrl_enum *pSrc = s; - struct dbr_ctrl_enum *pDest = d; + struct dbr_ctrl_enum *pSrc = (struct dbr_ctrl_enum *) s; + struct dbr_ctrl_enum *pDest = (struct dbr_ctrl_enum *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -1096,8 +1094,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_sts_char *pSrc = s; - struct dbr_sts_char *pDest = d; + struct dbr_sts_char *pSrc = (struct dbr_sts_char *) s; + struct dbr_sts_char *pDest = (struct dbr_sts_char *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); @@ -1127,8 +1125,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_sts_long *pSrc = s; - struct dbr_sts_long *pDest = d; + struct dbr_sts_long *pSrc = (struct dbr_sts_long *) s; + struct dbr_sts_long *pDest = (struct dbr_sts_long *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); @@ -1159,8 +1157,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_time_string *pSrc = s; - struct dbr_time_string *pDest = d; + struct dbr_time_string *pSrc = (struct dbr_time_string *) s; + struct dbr_time_string *pDest = (struct dbr_time_string *) d; /* convert ieee to vax format or vax to ieee */ pDest->status = dbr_ntohs(pSrc->status); @@ -1191,8 +1189,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_time_short *pSrc = s; - struct dbr_time_short *pDest = d; + struct dbr_time_short *pSrc = (struct dbr_time_short *) s; + struct dbr_time_short *pDest = (struct dbr_time_short *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); @@ -1225,8 +1223,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_time_float *pSrc = s; - struct dbr_time_float *pDest = d; + struct dbr_time_float *pSrc = (struct dbr_time_float *) s; + struct dbr_time_float *pDest = (struct dbr_time_float *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -1253,8 +1251,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_time_double *pSrc = s; - struct dbr_time_double *pDest = d; + struct dbr_time_double *pSrc = (struct dbr_time_double *) s; + struct dbr_time_double *pDest = (struct dbr_time_double *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -1282,8 +1280,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_time_enum *pSrc = s; - struct dbr_time_enum *pDest = d; + struct dbr_time_enum *pSrc = (struct dbr_time_enum *) s; + struct dbr_time_enum *pDest = (struct dbr_time_enum *) d; pDest->status = dbr_ntohs(pSrc->status); pDest->severity = dbr_ntohs(pSrc->severity); @@ -1312,8 +1310,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_time_char *pSrc = s; - struct dbr_time_char *pDest = d; + struct dbr_time_char *pSrc = (struct dbr_time_char *) s; + struct dbr_time_char *pDest = (struct dbr_time_char *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); @@ -1344,8 +1342,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - struct dbr_time_long *pSrc = s; - struct dbr_time_long *pDest = d; + struct dbr_time_long *pSrc = (struct dbr_time_long *) s; + struct dbr_time_long *pDest = (struct dbr_time_long *) d; /* convert vax to ieee or ieee to vax format -- same code*/ pDest->status = dbr_ntohs(pSrc->status); @@ -1375,8 +1373,8 @@ int encode, /* cvrt HOST to NET if T */ unsigned long num /* number of values */ ) { - dbr_put_ackt_t *pSrc = s; - dbr_put_ackt_t *pDest = d; + dbr_put_ackt_t *pSrc = (dbr_put_ackt_t *) s; + dbr_put_ackt_t *pDest = (dbr_put_ackt_t *) d; unsigned long i; for(i=0; istatus = dbr_ntohs(pSrc->status); diff --git a/src/ca/evtime.c b/src/ca/evtime.c index 3b6e3d5eb..c85b668b2 100644 --- a/src/ca/evtime.c +++ b/src/ca/evtime.c @@ -40,7 +40,7 @@ int evtime(char *pname) status = ca_pend_io(10.0); if(status != ECA_NORMAL){ printf("%s not found\n", pname); - return OK; + return 0; } status = ca_add_event( diff --git a/src/ca/flow_control.c b/src/ca/flow_control.c deleted file mode 100644 index 7190de556..000000000 --- a/src/ca/flow_control.c +++ /dev/null @@ -1,106 +0,0 @@ -/************************************************************************/ -/* */ -/* 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. */ -/* */ -/* */ -/* History */ -/* ------- */ -/* 06xx89 joh First Release */ -/* 060591 joh delinting */ -/* */ -/*_begin */ -/************************************************************************/ -/* */ -/* Title: IOC network flow control module */ -/* File: atcs:[ca]flow_control.c */ -/* Environment: VMS. UNIX, VRTX */ -/* Equipment: VAX, SUN, VME */ -/* */ -/* */ -/* Purpose */ -/* ------- */ -/* */ -/* ioc flow control module */ -/* */ -/* */ -/* Special comments */ -/* ------- -------- */ -/* */ -/************************************************************************/ -/*_end */ - -static char *sccsId = "@(#) $Id$"; - -#include "iocinf.h" - - -/* - * FLOW CONTROL - * - * Keep track of how many times messages have - * come with out a break in between and - * suppress monitors if we are behind - * (an update is sent when we catch up) - */ - -void flow_control_on(struct ioc_in_use *piiu) -{ - int status; - CA_STATIC *ca_static; - - ca_static = piiu->pcas; - LOCK; - - /* - * I prefer to avoid going into flow control - * as this impacts the performance of batched fetches - */ - if (piiu->contiguous_msg_count >= MAX_CONTIGUOUS_MSG_COUNT) { - if (!piiu->client_busy) { - status = ca_busy_message(piiu); - if (status==ECA_NORMAL) { - assert(ca_static->ca_number_iiu_in_fcca_number_iiu_in_fc++; - piiu->client_busy = TRUE; -# if defined(DEBUG) - printf("fc on\n"); -# endif - } - } - } - else { - piiu->contiguous_msg_count++; - } - - UNLOCK; - return; -} - -void flow_control_off(struct ioc_in_use *piiu) -{ - int status; - CA_STATIC *ca_static; - - ca_static = piiu->pcas; - LOCK; - - piiu->contiguous_msg_count = 0; - if (piiu->client_busy) { - status = ca_ready_message(piiu); - if (status==ECA_NORMAL) { - assert(ca_static->ca_number_iiu_in_fc>0u); - ca_static->ca_number_iiu_in_fc--; - piiu->client_busy = FALSE; -# if defined(DEBUG) - printf("fc off\n"); -# endif - } - } - - UNLOCK; - return; -} diff --git a/src/ca/flow_control.cpp b/src/ca/flow_control.cpp new file mode 100644 index 000000000..09552b5f2 --- /dev/null +++ b/src/ca/flow_control.cpp @@ -0,0 +1,76 @@ +/* $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" + +/* + * FLOW CONTROL + * + * Keep track of how many times messages have + * come with out a break in between and + * suppress monitors if we are behind + * (an update is sent when we catch up) + */ + +void flow_control_on (tcpiiu *piiu) +{ + int status; + + LOCK (piiu->niiu.iiu.pcas); + + /* + * I prefer to avoid going into flow control + * as this impacts the performance of batched fetches + */ + if (piiu->contiguous_msg_count >= MAX_CONTIGUOUS_MSG_COUNT) { + if (!piiu->client_busy) { + status = ca_busy_message(piiu); + if (status==ECA_NORMAL) { + assert(piiu->niiu.iiu.pcas->ca_number_iiu_in_fcniiu.iiu.pcas->ca_number_iiu_in_fc++; + piiu->client_busy = TRUE; +# if defined(DEBUG) + printf("fc on\n"); +# endif + } + } + } + else { + piiu->contiguous_msg_count++; + } + + UNLOCK (piiu->niiu.iiu.pcas); + return; +} + +void flow_control_off (tcpiiu *piiu) +{ + int status; + + LOCK (piiu->niiu.iiu.pcas); + + piiu->contiguous_msg_count = 0; + if (piiu->client_busy) { + status = ca_ready_message(piiu); + if (status==ECA_NORMAL) { + assert (piiu->niiu.iiu.pcas->ca_number_iiu_in_fc>0u); + piiu->niiu.iiu.pcas->ca_number_iiu_in_fc--; + piiu->client_busy = FALSE; +# if defined(DEBUG) + printf("fc off\n"); +# endif + } + } + + UNLOCK (piiu->niiu.iiu.pcas); + return; +} diff --git a/src/ca/if_depen.c b/src/ca/if_depen.c deleted file mode 100644 index e0c92526a..000000000 --- a/src/ca/if_depen.c +++ /dev/null @@ -1,326 +0,0 @@ -/* if_depen.c */ -/* share/src/ca/$Id$ */ - -/* - * Author: Jeff Hill - * Date: 04-05-94 - * Experimental Physics and Industrial Control System (EPICS) - * - * Copyright 1991, the Regents of the University of California, - * and the University of Chicago Board of Governors. - * - * This software was produced under U.S. Government contracts: - * (W-7405-ENG-36) at the Los Alamos National Laboratory, - * and (W-31-109-ENG-38) at Argonne National Laboratory. - * - * Initial development by: - * The Controls and Automation Group (AT-8) - * Ground Test Accelerator - * Accelerator Technology Division - * Los Alamos National Laboratory - * - * Co-developed with - * The Controls and Computing Group - * Accelerator Systems Division - * Advanced Photon Source - * Argonne National Laboratory - * - * Modification Log: - * ----------------- - * 8/87 Jeff Hill Init Release - * 072792 Jeff Hill better messages - * 09-DEC-1992 Gerhard Grygiel (GeG) support VMS/UCX - * 050593 Jeff Hill now checks all N interfaces - * (and not N-1 interfaces) - */ - - -static char *sccsId = "@(#) $Id$"; - -#include "iocinf.h" - -#ifdef DEBUG -#define ifDepenDebugPrintf(argsInParen) printf argsInParen -#else -#define ifDepenDebugPrintf(argsInParen) -#endif - -/* - * Dont use ca_static based lock macros here because this is - * also called by the server. All locks required are applied at - * a higher level. - */ - -/* - * local_addr() - * - * A valid non-loopback local address is required in the - * beacon message in certain situations where - * there are beacon repeaters and there are addresses - * in the EPICS_CA_ADDRESS_LIST for which we dont have - * a strictly correct local server address on a multi-interface - * system. In this situation we use the first valid non-loopback local - * address found in the beacon message. - */ -int local_addr(int s, struct sockaddr_in *plcladdr) -{ - int status; - struct ifconf ifconf; - struct ifreq ifreq[25]; - struct ifreq *pifreq; - static struct sockaddr_in addr; - static char init = FALSE; - struct sockaddr_in *tmpaddr; - - if (init){ - *plcladdr = addr; - return OK; - } - - /* - * get the addr of the first interface found - * (report inconsistent interfaces however) - */ - ifconf.ifc_len = sizeof ifreq; - ifconf.ifc_req = ifreq; - status = socket_ioctl(s, SIOCGIFCONF, &ifconf); - if (status < 0 || ifconf.ifc_len == 0) { - ca_printf( - "CAC: SIOCGIFCONF ioctl failed because \"%s\"\n", - SOCKERRSTR(SOCKERRNO)); - ifconf.ifc_len = 0; - } - - ifDepenDebugPrintf ( ("local_addr: %ld net intf(s) found\n", - (unsigned long) (ifconf.ifc_len/sizeof(*pifreq))) ); - - for ( pifreq = ifconf.ifc_req; - ((size_t)ifconf.ifc_len) >= sizeof(*pifreq); - pifreq++, ifconf.ifc_len -= sizeof(*pifreq)) { - unsigned flags; - - status = socket_ioctl(s, SIOCGIFFLAGS, pifreq); - if (status == ERROR){ - ca_printf("local_addr: net intf flags fetch for %s failed\n", pifreq->ifr_name); - continue; - } - - /* - * flags are stored in a union now - */ - flags = (unsigned) pifreq->ifr_flags; - - if (!(flags & IFF_UP)) { - ifDepenDebugPrintf ( ("local_addr: net intf %s was down\n", pifreq->ifr_name) ); - continue; - } - - /* - * dont use the loop back interface - */ - if (flags & IFF_LOOPBACK) { - ifDepenDebugPrintf ( ("local_addr: ignoring loopback interface: %s\n", pifreq->ifr_name) ); - continue; - } - - status = socket_ioctl(s, SIOCGIFADDR, pifreq); - if (status == ERROR){ - ifDepenDebugPrintf ( ("local_addr: could not obtain addr for %s\n", pifreq->ifr_name) ); - continue; - } - - if (pifreq->ifr_addr.sa_family != AF_INET){ - ifDepenDebugPrintf ( ("local_addr: interface %s was not AF_INET\n", pifreq->ifr_name) ); - continue; - } - - ifDepenDebugPrintf ( ("local_addr: net intf %s found\n", pifreq->ifr_name) ); - - tmpaddr = (struct sockaddr_in *) &pifreq->ifr_addr; - - init = TRUE; - addr = *tmpaddr; - break; - } - - if(!init){ - return ERROR; - } - - *plcladdr = addr; - return OK; -} - -/* - * caDiscoverInterfaces() - * - * This routine is provided with the address of an ELLLIST, a socket - * a destination port number, and a match address. When the - * routine returns there will be one additional inet address - * (a caAddrNode) in the list for each inet interface found that - * is up and isnt a loop back interface (match addr is INADDR_ANY) - * or it matches the specified interface (match addr isnt INADDR_ANY). - * If the interface supports broadcast then I add its broadcast - * address to the list. If the interface is a point to - * point link then I add the destination address of the point to - * point link to the list. In either case I set the port number - * in the address node to the port supplied in the argument - * list. - * - * LOCK should be applied here for (pList) - * (this is also called from the server) - */ -void epicsShareAPI caDiscoverInterfaces - (ELLLIST *pList, int socket, unsigned short port, struct in_addr matchAddr) -{ - struct sockaddr_in *pInetAddr; - caAddrNode *pNode; - int status; - struct ifconf ifconf; - struct ifreq *pIfreqList; - struct ifreq *pifreq; - unsigned long nelem; - - /* - * use pool so that we avoid using to much stack space - * under vxWorks - * - * nelem is set to the maximum interfaces - * on one machine here - */ - nelem = 100; - pIfreqList = (struct ifreq *)calloc(nelem, sizeof(*pifreq)); - if(!pIfreqList){ - return; - } - - ifconf.ifc_len = nelem*sizeof(*pifreq); - ifconf.ifc_req = pIfreqList; - status = socket_ioctl(socket, SIOCGIFCONF, &ifconf); - if (status < 0 || ifconf.ifc_len == 0) { - free(pIfreqList); - return; - } - - nelem = ifconf.ifc_len/sizeof(struct ifreq); - ifDepenDebugPrintf ( ("caDiscoverInterfaces: %ld net intf(s) found\n", nelem) ); - - for (pifreq = pIfreqList; pifreq<(pIfreqList+nelem); pifreq++) { - unsigned flags; - - status = socket_ioctl(socket, SIOCGIFFLAGS, pifreq); - if (status) { - ca_printf ("caDiscoverInterfaces: net intf flags fetch for %s failed\n", pifreq->ifr_name); - continue; - } - - /* - * flags are stored in a union now - */ - flags = (unsigned) pifreq->ifr_flags; - - /* - * dont bother with interfaces that have been disabled - */ - if (!(flags & IFF_UP)) { - ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s was down\n", pifreq->ifr_name) ); - continue; - } - - /* - * dont use the loop back interface - */ - if (flags & IFF_LOOPBACK) { - ifDepenDebugPrintf ( ("caDiscoverInterfaces: ignoring loopback interface: %s\n", pifreq->ifr_name) ); - continue; - } - - /* - * Fetch the local address for this interface - */ - status = socket_ioctl(socket, SIOCGIFADDR, pifreq); - if (status){ - ifDepenDebugPrintf ( ("caDiscoverInterfaces: could not obtain addr for %s\n", pifreq->ifr_name) ); - continue; - } - - /* - * If its not an internet inteface - * then dont use it. - */ - if (pifreq->ifr_addr.sa_family != AF_INET) { - ifDepenDebugPrintf ( ("caDiscoverInterfaces: interface %s was not AF_INET\n", pifreq->ifr_name) ); - continue; - } - - /* - * save the interface's IP address - */ - pInetAddr = (struct sockaddr_in *)&pifreq->ifr_addr; - - /* - * if it isnt a wildcarded interface then look for - * an exact match - */ - if (matchAddr.s_addr != htonl(INADDR_ANY)) { - if (pInetAddr->sin_addr.s_addr != matchAddr.s_addr) { - ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s didnt match\n", pifreq->ifr_name) ); - continue; - } - } - - /* - * If this is an interface that supports - * broadcast fetch the broadcast address. - * - * Otherwise if this is a point to point - * interface then use the destination address. - * - * Otherwise CA will not query through the - * interface. - */ - if (flags & IFF_BROADCAST) { - status = socket_ioctl( - socket, - SIOCGIFBRDADDR, - pifreq); - if (status) { - ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s: bcast addr fetch fail\n", pifreq->ifr_name) ); - continue; - } - } - else if(flags & IFF_POINTOPOINT){ - status = socket_ioctl( - socket, - SIOCGIFDSTADDR, - pifreq); - if (status){ - ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s: pt to pt addr fetch fail\n", pifreq->ifr_name) ); - continue; - } - } - else{ - ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s: not pt to pt or bcast\n", pifreq->ifr_name) ); - continue; - } - - ifDepenDebugPrintf ( ("caDiscoverInterfaces: net intf %s found\n", pifreq->ifr_name) ); - - pNode = (caAddrNode *) calloc(1,sizeof(*pNode)); - if(!pNode){ - ca_printf ("caDiscoverInterfaces: malloc failed for net intf %s: \n", pifreq->ifr_name); - continue; - } - - pNode->destAddr.in = *pInetAddr; - pNode->destAddr.in.sin_port = htons(port); - - /* - * LOCK applied externally - */ - ellAdd(pList, &pNode->node); - } - - free(pIfreqList); -} - diff --git a/src/ca/iocinf.c b/src/ca/iocinf.c deleted file mode 100644 index faa3458a2..000000000 --- a/src/ca/iocinf.c +++ /dev/null @@ -1,1988 +0,0 @@ -/* * $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. - */ - -static char *sccsId = "@# $Id$"; - -/* Allocate storage for global variables in this module */ -#define CA_GLBLSOURCE -#include "iocinf.h" -#include "net_convert.h" -#include "bsdSocketResource.h" - - -LOCAL void cac_set_iiu_non_blocking (struct ioc_in_use *piiu); -LOCAL unsigned long tcp_recv_msg( struct ioc_in_use *piiu); - -LOCAL unsigned long cac_connect_iiu(struct ioc_in_use *piiu); - - -LOCAL unsigned long cac_tcp_send_msg_piiu( struct ioc_in_use *piiu); -LOCAL unsigned long cac_udp_send_msg_piiu( struct ioc_in_use *piiu); -LOCAL unsigned long udp_recv_msg( struct ioc_in_use *piiu); -LOCAL void ca_process_tcp(struct ioc_in_use *piiu); -LOCAL void ca_process_udp(struct ioc_in_use *piiu); -LOCAL void cacRingBufferInit(struct ca_buffer *pBuf, unsigned long size); -LOCAL char *getToken(const char **ppString, char *pBuf, unsigned bufSize); - - -/* - * ALLOC_IOC() - * - * allocate and initialize an IOC info block for unallocated IOC - * - */ -int alloc_ioc( -CA_STATIC *ca_static, -const struct sockaddr_in *pina, -struct ioc_in_use **ppiiu -) -{ - int status; - bhe *pBHE; - - /* - * look for an existing connection - */ - LOCK; - pBHE = lookupBeaconInetAddr(ca_static,pina); - if(!pBHE){ - pBHE = createBeaconHashEntry(ca_static, pina, FALSE); - if(!pBHE){ - UNLOCK; - return ECA_ALLOCMEM; - } - } - - if(pBHE->piiu){ - if (pBHE->piiu->state!=iiu_disconnected) { - *ppiiu = pBHE->piiu; - status = ECA_NORMAL; - } - else { - status = ECA_DISCONN; - } - } - else{ - status = create_net_chan( - ca_static, - ppiiu, - pina, - IPPROTO_TCP); - if(status == ECA_NORMAL){ - pBHE->piiu = *ppiiu; - } - } - - UNLOCK; - - return status; -} - -/* - * CREATE_NET_CHAN() - * - */ -int create_net_chan( -CA_STATIC *ca_static, -struct ioc_in_use **ppiiu, -const struct sockaddr_in *pina, /* only used by TCP connections */ -int net_proto -) -{ - struct ioc_in_use *piiu; - int status; - SOCKET sock; - int true = TRUE; - caAddrNode *pNode; - - LOCK; - - piiu = (IIU *) calloc(1, sizeof(*piiu)); - if(!piiu){ - UNLOCK; - return ECA_ALLOCMEM; - } - - piiu->pcas = ca_static; - ellInit(&piiu->chidlist); - ellInit(&piiu->destAddr); - - piiu->sock_proto = net_proto; - - /* - * set the minor version ukn until the server - * updates the client - */ - piiu->minor_version_number = CA_UKN_MINOR_VERSION; - - /* - * initially there are no claim messages pending - */ - piiu->claimsPending = FALSE; - piiu->recvPending = FALSE; - piiu->pushPending = FALSE; - piiu->beaconAnomaly = FALSE; - - switch(piiu->sock_proto) - { - case IPPROTO_TCP: - - pNode = (caAddrNode *)calloc(1,sizeof(*pNode)); - if(!pNode){ - free(piiu); - UNLOCK; - return ECA_ALLOCMEM; - } - memset((char *)&pNode->destAddr,0,sizeof(pNode->destAddr)); - - pNode->destAddr.in = *pina; - ellAdd(&piiu->destAddr, &pNode->node); - piiu->recvBytes = tcp_recv_msg; - piiu->sendBytes = cac_connect_iiu; - piiu->procInput = ca_process_tcp; - piiu->minfreespace = 1; - - /* allocate a socket */ - sock = socket( AF_INET, /* domain */ - SOCK_STREAM, /* type */ - 0); /* deflt proto */ - if(sock == INVALID_SOCKET){ - free(pNode); - free(piiu); - UNLOCK; - return ECA_SOCK; - } - - piiu->sock_chan = sock; - - /* - * see TCP(4P) this seems to make unsollicited single events - * much faster. I take care of queue up as load increases. - */ - status = setsockopt( - sock, - IPPROTO_TCP, - TCP_NODELAY, - (char *)&true, - sizeof(true)); - if(status < 0){ - free(pNode); - free(piiu); - status = socket_close(sock); - if(status<0){ - SEVCHK(ECA_INTERNAL,NULL); - } - UNLOCK; - return ECA_SOCK; - } - - /* - * This should cause the connection to be checked - * periodically and an error to be returned if it is lost? - * - * In practice the conn is checked very infrequently. - */ - - status = setsockopt( - sock, - SOL_SOCKET, - SO_KEEPALIVE, - (char *)&true, - sizeof true); - if(status < 0){ - free(pNode); - free(piiu); - status = socket_close(sock); - if(status<0){ - SEVCHK(ECA_INTERNAL,NULL); - } - UNLOCK; - return ECA_SOCK; - } - -#ifdef JUNKYARD - { - struct linger linger; - int linger_size = sizeof linger; - status = getsockopt( - sock, - SOL_SOCKET, - SO_LINGER, - &linger, - &linger_size); - assert(status >= 0); - ca_printf( "CAC: linger was on:%d linger:%d\n", - linger.l_onoff, - linger.l_linger); - } -#endif - - /* - * some concern that vxWorks will run out of mBuf's - * if this change is made - * - * joh 11-10-98 - */ -#if 0 - { - int i; - - /* set TCP buffer sizes */ - i = MAX_MSG_SIZE; - status = setsockopt( - sock, - SOL_SOCKET, - SO_SNDBUF, - (char *)&i, - sizeof(i)); - if(status < 0){ - free(pNode); - free(piiu); - socket_close(sock); - UNLOCK; - return ECA_SOCK; - } - i = MAX_MSG_SIZE; - status = setsockopt( - sock, - SOL_SOCKET, - SO_RCVBUF, - (char *)&i, - sizeof(i)); - if(status < 0){ - free(pNode); - free(piiu); - socket_close(sock); - UNLOCK; - return ECA_SOCK; - } - } -#endif - - cacRingBufferInit(&piiu->recv, sizeof(piiu->recv.buf)); - cacRingBufferInit(&piiu->send, sizeof(piiu->send.buf)); - - cac_gettimeval (&piiu->timeAtLastRecv); - - /* - * Save the Host name for efficient access in the - * future. - */ - ipAddrToA (&pNode->destAddr.in, piiu->host_name_str, - sizeof(piiu->host_name_str)); - - /* - * TCP starts out in the connecting state and later transitions - * to the connected state - */ - piiu->state = iiu_connecting; - - cac_set_iiu_non_blocking (piiu); - - /* - * initiate connect sequence - */ - cac_connect_iiu (piiu); - - break; - - case IPPROTO_UDP: - - piiu->recvBytes = udp_recv_msg; - piiu->sendBytes = cac_udp_send_msg_piiu; - piiu->procInput = ca_process_udp; - piiu->minfreespace = ETHERNET_MAX_UDP+2*sizeof(struct udpmsglog); - - /* allocate a socket */ - sock = socket( AF_INET, /* domain */ - SOCK_DGRAM, /* type */ - 0); /* deflt proto */ - if(sock == INVALID_SOCKET){ - free (piiu); - UNLOCK; - return ECA_SOCK; - } - - piiu->sock_chan = sock; - - /* - * The following only needed on BSD 4.3 machines - */ - status = setsockopt( - sock, - SOL_SOCKET, - SO_BROADCAST, - (char *)&true, - sizeof(true)); - if(status<0){ - free(piiu); - ca_printf("CAC: sso (err=\"%s\")\n", - SOCKERRSTR(SOCKERRNO)); - status = socket_close(sock); - if(status < 0){ - SEVCHK(ECA_INTERNAL,NULL); - } - UNLOCK; - return ECA_CONN; - } - - /* - * some concern that vxWorks will run out of mBuf's - * if this change is made - * - * joh 11-10-98 - */ -#if 0 - /* - * bump up the UDP recv buffer - */ - { - /* - * - * this allows for faster connects by queuing - * additional incomming UDP search response frames - * - * this allocates a 32k buffer - * (uses a power of two) - */ - int size = 1u<<15u; - status = setsockopt( - sock, - SOL_SOCKET, - SO_RCVBUF, - (char *)&size, - sizeof(size)); - if (status<0) { - ca_printf("CAC: setsockopt SO_RCVBUF (err=%s)\n", - SOCKERRSTR(SOCKERRNO)); - } - } -#endif - -#if 0 - memset((char *)&saddr,0,sizeof(saddr)); - saddr.sin_family = AF_INET; - /* - * let slib pick lcl addr - */ - saddr.sin_addr.s_addr = htonl(INADDR_ANY); - saddr.sin_port = htons(0U); - - status = bind( sock, - (struct sockaddr *) &saddr, - sizeof(saddr)); - if(status<0){ - ca_printf("CAC: bind (err=%s)\n",SOCKERRSTR(SOCKERRNO)); - genLocalExcep ( ECA_INTERNAL,"bind failed"); - } -#endif - - /* - * load user and auto configured - * broadcast address list - */ - caSetupBCastAddrList( - &piiu->destAddr, - sock, - ca_static->ca_server_port); - - - cacRingBufferInit(&piiu->recv, sizeof(piiu->recv.buf)); - cacRingBufferInit(&piiu->send, min(MAX_UDP, - sizeof(piiu->send.buf))); - - /* - * UDP isnt connection oriented so we tag the piiu - * as up immediately - */ - piiu->state = iiu_connected; - - strncpy( - piiu->host_name_str, - "<>", - sizeof(piiu->host_name_str)-1); - - cac_set_iiu_non_blocking (piiu); - - break; - - default: - free(piiu); - genLocalExcep (ECA_INTERNAL,"create_net_chan: ukn protocol"); - /* - * turn off gcc warnings - */ - UNLOCK; - return ECA_INTERNAL; - } - - - if (fd_register_func) { - (*fd_register_func)((void *)fd_register_arg, sock, TRUE); - } - - /* - * add to the list of active IOCs - */ - ellAdd(&iiuList, &piiu->node); - - *ppiiu = piiu; - - UNLOCK; - - return ECA_NORMAL; -} - -/* - * cac_set_iiu_non_blocking() - */ -LOCAL void cac_set_iiu_non_blocking (struct ioc_in_use *piiu) -{ - osiSockIoctl_t value = TRUE; - int status; - - /* - * Set non blocking IO - * to prevent dead locks - */ - status = socket_ioctl( - piiu->sock_chan, - FIONBIO, - &value); - if(status<0){ - ca_printf( - "CAC: failed to set non-blocking because \"%s\"\n", - SOCKERRSTR(SOCKERRNO)); - } -} - -/* - * cac_connect_iiu() - */ -LOCAL unsigned long cac_connect_iiu ( -struct ioc_in_use *piiu) -{ - CA_STATIC *ca_static = piiu->pcas; - caAddrNode *pNode; - int status; - - if (piiu->state==iiu_connected) { - ca_printf("CAC: redundant connect() attempt?\n"); - return 0ul; - } - - if (piiu->state==iiu_disconnected) { - ca_printf("CAC: connecting when disconnected?\n"); - return 0ul; - } - - LOCK; - - assert (ellCount(&piiu->destAddr)==1u); - pNode = (caAddrNode *) ellFirst(&piiu->destAddr); - - /* - * attempt to connect to a CA server - */ - while (1) { - int errnoCpy; - - status = connect( - piiu->sock_chan, - &pNode->destAddr.sa, - sizeof(pNode->destAddr.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. - */ - UNLOCK; - return 0ul; - } - else if (errnoCpy==SOCK_EALREADY) { - UNLOCK; - return 0ul; - } -#ifdef _WIN32 - /* - * including this with vxWorks appears to - * cause trouble - */ - else if (errnoCpy==SOCK_EINVAL) { /* a SOCK_EALREADY alias used by early WINSOCK */ - UNLOCK; - return 0ul; - } -#endif - else if(errnoCpy==SOCK_EINTR) { - /* - * restart the system call if interrupted - */ - continue; - } - else { - TAG_CONN_DOWN(piiu); - UNLOCK; - ca_printf( - "CAC: Unable to connect port %d on \"%s\" because %d=\"%s\"\n", - ntohs(pNode->destAddr.in.sin_port), - piiu->host_name_str, errnoCpy, - SOCKERRSTR(errnoCpy)); - return 0ul; - } - } - - /* - * put the iiu into the connected state - */ - piiu->state = iiu_connected; - - piiu->sendBytes = cac_tcp_send_msg_piiu; - - cac_gettimeval (&piiu->timeAtLastRecv); - - /* - * When we are done connecting and there are - * IOC_CLAIM_CHANNEL requests outstanding - * then add them to the outgoing message buffer - */ - if (piiu->claimsPending) { - retryPendingClaims (piiu); - } - UNLOCK; - - return 1ul; -} - -/* - * caSetupBCastAddrList() - */ -void caSetupBCastAddrList ( ELLLIST *pList, SOCKET sock, unsigned short port) -{ - char *pstr; - char yesno[32u]; - int yes; - - /* - * dont load the list twice - */ - assert (ellCount(pList)==0); - - /* - * Check to see if the user has disabled - * initializing the search b-cast list - * from the interfaces found. - */ - yes = TRUE; - pstr = envGetConfigParam ( - &EPICS_CA_AUTO_ADDR_LIST, - sizeof(yesno), - yesno); - if (pstr) { - if (strstr(pstr,"no")||strstr(pstr,"NO")) { - yes = FALSE; - } - } - - /* - * LOCK is for piiu->destAddr list - * (lock outside because this is used by the server also) - */ - if (yes) { - struct in_addr addr; - addr.s_addr = htonl(INADDR_ANY); - caDiscoverInterfaces( - pList, - sock, - port, - addr); - } - - caAddConfiguredAddr( - pList, - &EPICS_CA_ADDR_LIST, - sock, - port); - - if (ellCount(pList)==0) { - genLocalExcep ( ECA_NOSEARCHADDR, NULL); - } -} - -/* - * NOTIFY_CA_REPEATER() - * - * tell the cast repeater that another client needs fan out - * - * NOTES: - * 1) local communication only (no LAN traffic) - * - */ -void notify_ca_repeater(CA_STATIC *ca_static) -{ - caHdr msg; - struct sockaddr_in saddr; - int status; - static int once = FALSE; - int len; - - if (ca_static->ca_repeater_contacted) { - return; - } - - if (!piiuCast) { - return; - } - - if (piiuCast->state!=iiu_connected) { - return; - } - - if (ca_static->ca_repeater_tries>N_REPEATER_TRIES_PRIOR_TO_MSG){ - if (!once) { - ca_printf( - "Unable to contact CA repeater after %d tries\n", - N_REPEATER_TRIES_PRIOR_TO_MSG); - ca_printf( - "Silence this message by starting a CA repeater daemon\n"); - once = TRUE; - } - } - - /* - * In 3.13 beta 11 and before the CA repeater calls local_addr() - * to determine a local address and does not allow registration - * messages originating from other addresses. In these - * releases local_addr() returned the address of the first enabled - * interface found, and this address may or may not have been the loop - * back address. Starting with 3.13 beta 12 local_addr() was - * changed to always return the address of the first enabled - * non-loopback interface because a valid non-loopback local - * address is required in the beacon messages. Therefore, to - * guarantee compatibility with past versions of the repeater - * we alternate between the address returned by local_addr() - * and the loopback address here. - * - * CA repeaters in R3.13 beta 12 and higher allow - * either the loopback address or the address returned - * by local address (the first non-loopback address found) - */ - if (ca_static->ca_repeater_tries&1) { - status = local_addr (piiuCast->sock_chan, &saddr); - if (status<0) { - /* - * use the loop back address to communicate with the CA repeater - * if this os does not have interface query capabilities - * - * this will only work with 3.13 beta 12 CA repeaters or later - */ - saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - } - } - else { - saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - } - - LOCK; /*MULTINET TCP/IP routines are not reentrant*/ - - memset((char *)&msg, 0, sizeof(msg)); - msg.m_cmmd = htons(REPEATER_REGISTER); - msg.m_available = saddr.sin_addr.s_addr; - saddr.sin_port = htons (ca_static->ca_repeater_port); - - /* - * Intentionally sending a zero length message here - * until most CA repeater daemons have been restarted - * (and only then will accept the above protocol) - * (repeaters began accepting this protocol - * starting with EPICS 3.12) - * - * SOLARIS will not accept a zero length message - * and we are just porting there for 3.12 so - * we will use the new protocol for 3.12 - * - * recent versions of UCX will not accept a zero - * length message and we will assume that folks - * using newer versions of UCX have rebooted (and - * therefore restarted the CA repeater - and therefore - * moved it to an EPICS release that accepts this protocol) - */ -# if defined(SOLARIS) || defined(UCX) - len = sizeof(msg); -# else /* SOLARIS */ - len = 0; -# endif /* SOLARIS */ - - status = sendto (piiuCast->sock_chan, (char *)&msg, len, - 0, (struct sockaddr *)&saddr, sizeof(saddr)); - if (status < 0) { - int errnoCpy = SOCKERRNO; - if( errnoCpy != SOCK_EINTR && - errnoCpy != SOCK_EWOULDBLOCK && - /* - * This is returned from Linux when - * the repeater isnt running - */ - errnoCpy != SOCK_ECONNREFUSED - ) { - ca_printf( - "CAC: error sending to repeater was \"%s\"\n", - SOCKERRSTR(errnoCpy)); - } - } - ca_static->ca_repeater_tries++; - - UNLOCK; -} - - -/* - * CAC_UDP_SEND_MSG_PIIU() - */ -LOCAL unsigned long cac_udp_send_msg_piiu( struct ioc_in_use *piiu) -{ - CA_STATIC *ca_static = piiu->pcas; - caAddrNode *pNode; - unsigned long sendCnt; - unsigned long totalBytes = 0ul; - int status; - - /* - * check for shutdown in progress - */ - if(piiu->state!=iiu_connected){ - return 0ul; - } - - LOCK; - - sendCnt = cacRingBufferReadSize(&piiu->send, TRUE); - - assert(sendCnt<=piiu->send.max_msg); - - /* - * return if nothing to send - */ - if(sendCnt == 0){ - UNLOCK; - return 0ul; - } - - pNode = (caAddrNode *) piiu->destAddr.node.next; - while (pNode) { - unsigned long actualSendCnt; - - status = sendto( - piiu->sock_chan, - &piiu->send.buf[piiu->send.rdix], - (int) sendCnt, - 0, - &pNode->destAddr.sa, - sizeof(pNode->destAddr.sa)); - if(status>=0){ - actualSendCnt = (unsigned long) status; - assert (actualSendCnt == sendCnt); - totalBytes += actualSendCnt; - } - else { - int localErrno; - - localErrno = SOCKERRNO; - - if( localErrno != SOCK_EWOULDBLOCK && - localErrno != SOCK_EINTR){ - char buf[64]; - - ipAddrToA (&pNode->destAddr.in, buf, sizeof(buf)); - - ca_printf( - "CAC: error = \"%s\" sending UDP msg to %s\n", - SOCKERRSTR(localErrno), buf); - } - } - pNode = (caAddrNode *) pNode->node.next; - } - - /* - * forces UDP send buffer to be a - * queue instead of a ring buffer - * (solves ring boundary problems) - */ - cacRingBufferInit( - &piiu->send, - min(MAX_UDP, sizeof(piiu->send.buf))); - piiu->pushPending = FALSE; - UNLOCK; - return totalBytes; -} - -/* - * CAC_TCP_SEND_MSG_PIIU() - */ -LOCAL unsigned long cac_tcp_send_msg_piiu( struct ioc_in_use *piiu) -{ - CA_STATIC *ca_static = piiu->pcas; - unsigned long sendCnt; - unsigned long totalBytes = 0ul; - int status; - int localError; - - /* - * check for shutdown in progress - */ - if(piiu->state!=iiu_connected){ - return 0ul; - } - - LOCK; - - /* - * Check at least twice to see if there is anything - * in the ring buffer (in case the block of messages - * isnt continuous). Always return if the send was - * less bytes than requested. - */ - while (TRUE) { - sendCnt = cacRingBufferReadSize(&piiu->send, TRUE); - assert(sendCnt<=piiu->send.max_msg); - - /* - * return if nothing to send - */ - if(sendCnt == 0){ - piiu->sendPending = FALSE; - piiu->pushPending = FALSE; - UNLOCK; - - /* - * If we cleared out some send backlog and there are - * IOC_CLAIM_CHANNEL requests outstanding - * then add them to the outgoing message buffer - */ - if (piiu->claimsPending) { - retryPendingClaims(piiu); - } - return totalBytes; - } - - assert (sendCnt<=INT_MAX); - - status = send( - piiu->sock_chan, - &piiu->send.buf[piiu->send.rdix], - (int) sendCnt, - 0); - if (status<=0) { - break; - } - - CAC_RING_BUFFER_READ_ADVANCE(&piiu->send, status); - totalBytes += (unsigned long) status; - } - - if (status==0) { - TAG_CONN_DOWN(piiu); - UNLOCK; - return totalBytes; - } - - localError = SOCKERRNO; - - if( localError == SOCK_EWOULDBLOCK || - localError == SOCK_EINTR){ - UNLOCK; - return totalBytes; - } - - if( localError != SOCK_EPIPE && - localError != SOCK_ECONNRESET && - localError != SOCK_ETIMEDOUT){ - ca_printf( - "CAC: unexpected TCP send error: %s\n", - SOCKERRSTR(localError)); - } - - TAG_CONN_DOWN(piiu); - UNLOCK; - return totalBytes; -} - -/* - * ca_process_input_queue() - */ -void ca_process_input_queue(CA_STATIC *ca_static) -{ - struct ioc_in_use *piiu; - - LOCK; - - /* - * dont allow recursion - */ - if(post_msg_active){ - UNLOCK; - return; - } - - for(piiu=(IIU *)iiuList.node.next; - piiu; piiu=(IIU *)piiu->node.next){ - - if(piiu->state!=iiu_connected){ - continue; - } - - (*piiu->procInput)( piiu); - } - - UNLOCK; -} - -/* - * cac_block_for_io_completion() - */ -void cac_block_for_io_completion(CA_STATIC *ca_static,struct timeval *pTV) -{ - struct timeval itimeout; - double waitTime; - -#ifndef iocCore - cac_mux_io(ca_static,pTV, TRUE); -#else - /*flush outputs */ - /*recv occurs in another thread*/ - itimeout.tv_usec = 0; - itimeout.tv_sec = 0; - cac_mux_io (ca_static, &itimeout, TRUE); - waitTime = pTV->tv_sec + pTV->tv_usec/1000.0; - if(waitTime>POLLDELAY) waitTime = POLLDELAY; - semBinaryTakeTimeout(ca_static->ca_io_done_sem,waitTime); - /* - *force a time update because we are not - *going to get one with a nill timeout in - * ca_mux_io() - */ - cac_gettimeval (&ca_static->currentTime); -#endif -} - - -/* - * TCP_RECV_MSG() - * - */ -LOCAL unsigned long tcp_recv_msg(struct ioc_in_use *piiu) -{ - CA_STATIC *ca_static = piiu->pcas; - unsigned long writeSpace; - unsigned long totalBytes = 0; - int status; - - if(piiu->state!=iiu_connected){ - return totalBytes; - } - - LOCK; - - /* - * Check at least twice to see if there is ana space left - * in the ring buffer (in case the messages block - * isnt continuous). Always return if the send was - * less bytes than requested. - */ - while (TRUE) { - - writeSpace = cacRingBufferWriteSize(&piiu->recv, TRUE); - if(writeSpace == 0){ - break; - } - - assert (writeSpace<=INT_MAX); - - status = recv( piiu->sock_chan, - &piiu->recv.buf[piiu->recv.wtix], - (int) writeSpace, - 0); - if(status == 0){ - TAG_CONN_DOWN(piiu); - break; - } - else if(status <0){ - int localErrno = SOCKERRNO; - /* try again on status of -1 and no luck this time */ - if(localErrno == SOCK_EWOULDBLOCK || localErrno == SOCK_EINTR){ - break; - } - - if( localErrno != SOCK_EPIPE && - localErrno != SOCK_ECONNRESET && - localErrno != SOCK_ETIMEDOUT){ - ca_printf( - "CAC: unexpected TCP recv error: %s\n", - SOCKERRSTR(localErrno)); - } - TAG_CONN_DOWN(piiu); - break; - } - - assert (((unsigned long)status)<=writeSpace); - - CAC_RING_BUFFER_WRITE_ADVANCE(&piiu->recv, status); - - totalBytes += (unsigned long) status; - - /* - * Record the time whenever we receive a message - * from this IOC - */ - piiu->timeAtLastRecv = ca_static->currentTime; - } - - UNLOCK; - return totalBytes; -} - -/* - * ca_process_tcp() - * - */ -LOCAL void ca_process_tcp( struct ioc_in_use *piiu) -{ - CA_STATIC *ca_static = piiu->pcas; - caAddrNode *pNode; - int status; - long bytesToProcess; - unsigned countDown; - - LOCK; - - /* - * dont allow recursion - */ - if(post_msg_active){ - UNLOCK; - return; - } - - post_msg_active = TRUE; - - pNode = (caAddrNode *) piiu->destAddr.node.next; - - /* - * Dont loop forever here if the server floods a slow - * client with monitors and there is a flush call in - * an event routine. This code was specifically added - * to allow a client to get out of an infinite loop - * occurring when the client does a put/flush in a - * monitor call back to the same PV that is being - * monitored - * - * it should be sufficent to read two sections from the - * ring buffer in case the populated space is split - */ - countDown = 3; - while (--countDown) { - bytesToProcess = cacRingBufferReadSize(&piiu->recv, TRUE); - if(bytesToProcess == 0){ - break; - } - - /* post message to the user */ - status = post_msg( - piiu, - &pNode->destAddr.in, - &piiu->recv.buf[piiu->recv.rdix], - bytesToProcess); - if(status != OK){ - TAG_CONN_DOWN(piiu); - post_msg_active = FALSE; - UNLOCK; - return; - } - CAC_RING_BUFFER_READ_ADVANCE( - &piiu->recv, - bytesToProcess); - } - - post_msg_active = FALSE; - UNLOCK; - - return; -} - -/* - * UDP_RECV_MSG() - * - */ -LOCAL unsigned long udp_recv_msg( struct ioc_in_use *piiu) -{ - CA_STATIC *ca_static = piiu->pcas; - int status; - int reply_size; - struct udpmsglog *pmsglog; - unsigned long bytesAvailable; - unsigned long totalBytes = 0ul; - - if(piiu->state!=iiu_connected){ - return totalBytes; - } - - LOCK; - - bytesAvailable = cacRingBufferWriteSize(&piiu->recv, TRUE); - assert(bytesAvailable >= ETHERNET_MAX_UDP+2*sizeof(*pmsglog)); - pmsglog = (struct udpmsglog *) &piiu->recv.buf[piiu->recv.wtix]; - - reply_size = sizeof(pmsglog->addr); - status = recvfrom( - piiu->sock_chan, - (char *)(pmsglog+1), - bytesAvailable-2*sizeof(*pmsglog), /* was MAX_UDP before 8-5-97 */ - 0, - (struct sockaddr *)&pmsglog->addr, - &reply_size); - if(status < 0){ - int errnoCpy = SOCKERRNO; - /* - * op would block which is ok to ignore till ready - * later - */ - if(errnoCpy == SOCK_EWOULDBLOCK || errnoCpy == SOCK_EINTR){ - UNLOCK; - return totalBytes; - } -# ifdef linux - /* - * Avoid spurious ECONNREFUSED bug - * in linux - */ - if (errnoCpy==SOCK_ECONNREFUSED) { - UNLOCK; - return totalBytes; - } -# endif - ca_printf ("Unexpected UDP recv error %s\n", SOCKERRSTR(errnoCpy)); - } - else if(status > 0){ - - /* - * log the msg size - * and advance the ring index - */ - pmsglog->nbytes = status; - pmsglog->valid = TRUE; - totalBytes = sizeof(*pmsglog) + (unsigned long) status; - CAC_RING_BUFFER_WRITE_ADVANCE(&piiu->recv, totalBytes); - /* - * if there isnt enough room at the end advance - * to the beginning of the ring - */ - bytesAvailable = cacRingBufferWriteSize(&piiu->recv, TRUE); - if( bytesAvailable < ETHERNET_MAX_UDP+2*sizeof(*pmsglog) ){ - assert(bytesAvailable>=sizeof(*pmsglog)); - pmsglog = (struct udpmsglog *) - &piiu->recv.buf[piiu->recv.wtix]; - pmsglog->valid = FALSE; - pmsglog->nbytes = bytesAvailable - sizeof(*pmsglog); - CAC_RING_BUFFER_WRITE_ADVANCE( - &piiu->recv, bytesAvailable); - } -# ifdef DEBUG - ca_printf("%s: udp reply of %d bytes\n", __FILE__, status); -# endif - } - - UNLOCK; - - return totalBytes; -} - -/* - * CA_PROCESS_UDP() - * - */ -LOCAL void ca_process_udp( struct ioc_in_use *piiu) -{ - CA_STATIC *ca_static = piiu->pcas; - int status; - struct udpmsglog *pmsglog; - char *pBuf; - unsigned long bytesAvailable; - - LOCK; - - /* - * dont allow recursion - */ - if(post_msg_active){ - UNLOCK; - return; - } - - post_msg_active = TRUE; - - while(TRUE){ - - bytesAvailable = cacRingBufferReadSize(&piiu->recv, TRUE); - if(bytesAvailable == 0){ - break; - } - - assert(bytesAvailable>=sizeof(*pmsglog)); - - pBuf = &piiu->recv.buf[piiu->recv.rdix]; - while(pBuf<&piiu->recv.buf[piiu->recv.rdix]+bytesAvailable){ - pmsglog = (struct udpmsglog *) pBuf; - - /* post message to the user */ - if(pmsglog->valid){ - - status = post_msg( - piiu, - &pmsglog->addr, - (char *)(pmsglog+1), - pmsglog->nbytes); - if(status != OK || piiu->curMsgBytes){ - char buf[64]; - - ipAddrToA (&pmsglog->addr, buf, sizeof(buf)); - - ca_printf("%s: bad UDP msg from %s\n", __FILE__, buf); - - /* - * resync the ring buffer - * (discard existing messages) - */ - cacRingBufferInit( - &piiu->recv, - sizeof(piiu->recv.buf)); - piiu->curMsgBytes = 0; - piiu->curDataBytes = 0; - post_msg_active = FALSE; - UNLOCK; - return; - } - } - pBuf += sizeof(*pmsglog)+pmsglog->nbytes; - } - CAC_RING_BUFFER_READ_ADVANCE( - &piiu->recv, - bytesAvailable); - } - - post_msg_active = FALSE; - UNLOCK; - - return; -} - -/* - * cac_close_ioc () - * (free resources associated with a client connection) - * - */ -void cac_close_ioc (IIU *piiu) -{ - CA_STATIC *ca_static; - caAddrNode *pNode; - ciu chix; - int status; - unsigned chanDisconnectCount; - - /* - * dont close twice - */ - assert (piiu->sock_chan!=INVALID_SOCKET); - ca_static = piiu->pcas; - - LOCK; - - ellDelete (&iiuList, &piiu->node); - - /* - * attempt to clear out messages in recv queue - */ - (*piiu->procInput) (piiu); - - - if (piiu == piiuCast) { - piiuCast = NULL; - chanDisconnectCount = 0u; - } - else { - ciu pNext; - - chanDisconnectCount = ellCount(&piiu->chidlist); - - /* - * remove IOC from the beacon hash table - */ - pNode = (caAddrNode *) piiu->destAddr.node.next; - assert (pNode); - removeBeaconInetAddr (ca_static, &pNode->destAddr.in); - - chix = (ciu) ellFirst(&piiu->chidlist); - while (chix) { - pNext = (ciu) ellNext(&chix->node); - cacDisconnectChannel (chix); - chix = pNext; - } - } - - if (fd_register_func) { - (*fd_register_func) ((void *)fd_register_arg, piiu->sock_chan, FALSE); - } - - status = socket_close (piiu->sock_chan); - assert (status == 0); - - /* - * free message body cache - */ - if (piiu->pCurData) { - free (piiu->pCurData); - piiu->pCurData = NULL; - piiu->curDataMax = 0; - } - - piiu->sock_chan = INVALID_SOCKET; - - ellFree (&piiu->destAddr); - - if (chanDisconnectCount) { - genLocalExcep ( ECA_DISCONN, piiu->host_name_str); - } - - free (piiu); - - UNLOCK; -} - -/* - * cacDisconnectChannel() - */ -void cacDisconnectChannel(ciu chix) -{ - IIU *piiu = (IIU *)chix->piiu; - CA_STATIC *ca_static = piiu->pcas; - LOCK; - - /* - * if a client initiated channel delete is pending then we will - * never get a delete confirm message from this server, and will therefore - * need to take care of freeing the remaing channel resources here - */ - if (chix->state == cs_closed) { - clearChannelResources (ca_static, chix->cid); - UNLOCK; - return; - } - - chix->privType = TYPENOTCONN; - chix->privCount = 0u; - chix->id.sid = ~0u; - chix->ar.read_access = FALSE; - chix->ar.write_access = FALSE; - - /* - * call their connection handler as required - */ - if (chix->state==cs_conn) { - miu monix, next; - - chix->state = cs_prev_conn; - - /* - * clear outstanding get call backs - */ - caIOBlockListFree (ca_static, &pend_read_list, chix, - TRUE, ECA_DISCONN); - - /* - * clear outstanding put call backs - */ - caIOBlockListFree (ca_static, &pend_write_list, chix, - TRUE, ECA_DISCONN); - - /* - * look for events that have an event cancel in progress - */ - for (monix = (miu) ellFirst (&chix->eventq); - monix; monix = next) { - - next = (miu) ellNext (&monix->node); - - /* - * if there is an event cancel in progress - * delete the event - we will never receive - * an event cancel confirm from this server - */ - if (monix->usr_func == NULL) { - ellDelete (&chix->eventq, &monix->node); - caIOBlockFree (ca_static, monix); - } - } - - if (chix->pConnFunc) { - struct connection_handler_args args; - - args.chid = chix; - args.op = CA_OP_CONN_DOWN; - (*chix->pConnFunc) (args); - } - if (chix->pAccessRightsFunc) { - struct access_rights_handler_args args; - - args.chid = chix; - args.ar = chix->ar; - (*chix->pAccessRightsFunc) (args); - } - } - removeFromChanList(chix); - /* - * try to reconnect - */ - assert (piiuCast); - addToChanList(chix, piiuCast); - cacSetRetryInterval(ca_static, 0u); - UNLOCK; -} - -/* - * REPEATER_INSTALLED() - * - * Test for the repeater already installed - * - * NOTE: potential race condition here can result - * in two copies of the repeater being spawned - * however the repeater detects this, prints a message, - * and lets the other task start the repeater. - * - * QUESTION: is there a better way to test for a port in use? - * ANSWER: none that I can find. - * - * Problems with checking for the repeater installed - * by attempting to bind a socket to its address - * and port. - * - * 1) Closed socket may not release the bound port - * before the repeater wakes up and tries to grab it. - * Attempting to bind the open socket to another port - * also does not work. - * - * 072392 - problem solved by using SO_REUSEADDR - */ -int repeater_installed(CA_STATIC *ca_static) -{ - int status; - SOCKET sock; - struct sockaddr_in bd; - int true = 1; - int installed = FALSE; - - LOCK; - - /* allocate a socket */ - sock = socket( AF_INET, /* domain */ - SOCK_DGRAM, /* type */ - 0); /* deflt proto */ - if(sock == INVALID_SOCKET) { - UNLOCK; - return installed; - } - - memset((char *)&bd,0,sizeof bd); - bd.sin_family = AF_INET; - bd.sin_addr.s_addr = htonl(INADDR_ANY); - bd.sin_port = htons(ca_static->ca_repeater_port); - status = bind( sock, - (struct sockaddr *) &bd, - sizeof bd); - if(status<0){ - if(SOCKERRNO == SOCK_EADDRINUSE){ - installed = TRUE; - } - } - - /* - * turn on reuse only after the test so that - * this works on kernels that support multicast - */ - status = setsockopt( sock, - SOL_SOCKET, - SO_REUSEADDR, - (char *)&true, - sizeof true); - if(status<0){ - ca_printf( "CAC: set socket option reuseaddr failed\n"); - } - - status = socket_close(sock); - if(status<0){ - SEVCHK(ECA_INTERNAL,NULL); - } - - UNLOCK; - - return installed; -} - -/* - * cacRingBufferRead() - * - * returns the number of bytes read which may be less than - * the number requested. - */ -unsigned long cacRingBufferRead( -struct ca_buffer *pRing, -void *pBuf, -unsigned long nBytes) -{ - unsigned long potentialBytes; - unsigned long actualBytes; - char *pCharBuf; - - actualBytes = 0u; - pCharBuf = pBuf; - while(TRUE){ - potentialBytes = cacRingBufferReadSize(pRing, TRUE); - if(potentialBytes == 0u){ - return actualBytes; - } - potentialBytes = min(potentialBytes, nBytes-actualBytes); - memcpy(pCharBuf, &pRing->buf[pRing->rdix], potentialBytes); - CAC_RING_BUFFER_READ_ADVANCE(pRing, potentialBytes); - pCharBuf += potentialBytes; - actualBytes += potentialBytes; - if(nBytes <= actualBytes){ - return actualBytes; - } - } -} - -/* - * cacRingBufferWrite() - * - * returns the number of bytes written which may be less than - * the number requested. - */ -unsigned long cacRingBufferWrite( -struct ca_buffer *pRing, -const void *pBuf, -unsigned long nBytes) -{ - unsigned long potentialBytes; - unsigned long actualBytes; - const char *pCharBuf; - - actualBytes = 0u; - pCharBuf = pBuf; - while(TRUE){ - potentialBytes = cacRingBufferWriteSize(pRing, TRUE); - if(potentialBytes == 0u){ - return actualBytes; - } - potentialBytes = min(potentialBytes, nBytes-actualBytes); - memcpy(pRing->buf+pRing->wtix, pCharBuf, potentialBytes); - CAC_RING_BUFFER_WRITE_ADVANCE(pRing, potentialBytes); - pCharBuf += potentialBytes; - actualBytes += potentialBytes; - if(nBytes <= actualBytes){ - return actualBytes; - } - } -} - -/* - * cacRingBufferInit() - * - */ -LOCAL void cacRingBufferInit(struct ca_buffer *pBuf, unsigned long size) -{ - assert(size<=sizeof(pBuf->buf)); - pBuf->max_msg = size; - pBuf->rdix = 0u; - pBuf->wtix = 0u; - pBuf->readLast = TRUE; -} - -/* - * cacRingBufferReadSize() - * - * returns N bytes available - * (not nec contiguous) - */ -unsigned long cacRingBufferReadSize(struct ca_buffer *pBuf, int contiguous) -{ - unsigned long count; - - if(pBuf->wtix < pBuf->rdix){ - count = pBuf->max_msg - pBuf->rdix; - if(!contiguous){ - count += pBuf->wtix; - } - } - else if(pBuf->wtix > pBuf->rdix){ - count = pBuf->wtix - pBuf->rdix; - } - else if(pBuf->readLast){ - count = 0u; - } - else{ - if(contiguous){ - count = pBuf->max_msg - pBuf->rdix; - } - else{ - count = pBuf->max_msg; - } - } - -#if 0 - printf("%d bytes available for reading \n", count); -#endif - - return count; -} - -/* - * cacRingBufferWriteSize() - * - * returns N bytes available - * (not nec contiguous) - */ -unsigned long cacRingBufferWriteSize(struct ca_buffer *pBuf, int contiguous) -{ - unsigned long count; - - if(pBuf->wtix < pBuf->rdix){ - count = pBuf->rdix - pBuf->wtix; - } - else if(pBuf->wtix > pBuf->rdix){ - count = pBuf->max_msg - pBuf->wtix; - if(!contiguous){ - count += pBuf->rdix; - } - } - else if(pBuf->readLast){ - if(contiguous){ - count = pBuf->max_msg - pBuf->wtix; - } - else{ - count = pBuf->max_msg; - } - } - else{ - count = 0u; - } - - return count; -} - -/* - * localHostName() - * - * o Indicates failure by setting ptr to nill - * - * o Calls non posix gethostbyname() so that we get DNS style names - * (gethostbyname() should be available with most BSD sock libs) - * - * vxWorks user will need to configure a DNS format name for the - * host name if they wish to be cnsistent with UNIX and VMS hosts. - * - * this needs to attempt to determine if the process is a remote - * login - hard to do under UNIX - */ -char *localHostName() -{ - int size; - int status; - char pName[MAXHOSTNAMELEN]; - char *pTmp; - - status = gethostname(pName, sizeof(pName)); - if(status){ - return NULL; - } - - size = strlen(pName)+1; - pTmp = malloc(size); - if(!pTmp){ - return pTmp; - } - - strncpy(pTmp, pName, size-1); - pTmp[size-1] = '\0'; - - return pTmp; -} - -/* - * caAddConfiguredAddr() - */ -void epicsShareAPI caAddConfiguredAddr(ELLLIST *pList, const ENV_PARAM *pEnv, - SOCKET socket, unsigned short port) -{ - caAddrNode *pNode; - const char *pStr; - const char *pToken; - caAddr addr; - char buf[32u]; /* large enough to hold an IP address */ - int status; - - pStr = envGetConfigParamPtr(pEnv); - if(!pStr){ - return; - } - - while( (pToken = getToken(&pStr, buf, sizeof(buf))) ){ - status = aToIPAddr(pToken, port, &addr.in); - if (status<0) { - ca_printf("%s: Parsing '%s'\n", __FILE__, pEnv->name); - ca_printf("\tBad internet address or host name: '%s'\n", pToken); - continue; - } - pNode = (caAddrNode *) calloc (1, sizeof(*pNode)); - if (pNode) { - pNode->destAddr = addr; - ellAdd (pList, &pNode->node); - } - } - - return; -} - -/* - * getToken() - */ -LOCAL char *getToken(const char **ppString, char *pBuf, unsigned bufSIze) -{ - const char *pToken; - unsigned i; - - pToken = *ppString; - while(isspace(*pToken)&&*pToken){ - pToken++; - } - - for (i=0u; idestAddr.sa.sa_family != AF_INET){ - printf(""); - continue; - } - ipAddrToA (&pNode->destAddr.in, buf, sizeof(buf)); - printf( "%s\n", buf); - - pNode = (caAddrNode *) ellNext(&pNode->node); - } -} - -/* - * caFetchPortConfig() - */ -unsigned short epicsShareAPI caFetchPortConfig - (const ENV_PARAM *pEnv, unsigned short defaultPort) -{ - long longStatus; - long epicsParam; - int port; - - longStatus = envGetLongConfigParam(pEnv, &epicsParam); - if (longStatus!=0) { - epicsParam = (long) defaultPort; - ca_printf ("EPICS \"%s\" integer fetch failed\n", pEnv->name); - ca_printf ("setting \"%s\" = %ld\n", pEnv->name, epicsParam); - } - - /* - * This must be a server port that will fit in an unsigned - * short - */ - if (epicsParam<=IPPORT_USERRESERVED || epicsParam>USHRT_MAX) { - ca_printf ("EPICS \"%s\" out of range\n", pEnv->name); - /* - * Quit if the port is wrong due CA coding error - */ - assert (epicsParam != (long) defaultPort); - epicsParam = (long) defaultPort; - ca_printf ("Setting \"%s\" = %ld\n", pEnv->name, epicsParam); - } - - /* - * ok to clip to unsigned short here because we checked the range - */ - port = (unsigned short) epicsParam; - - return port; -} - -/* - * CAC_MUX_IO() - */ -void cac_mux_io( - CA_STATIC *ca_static, - struct timeval *ptimeout, - unsigned iocCloseAllowed) -{ - int count; - struct timeval timeout; - unsigned countDown; - - /* - * first check for pending recv's with a zero time out so that - * 1) flow control works correctly (and) - * 2) we queue up sends resulting from recvs properly - * (this results in improved max throughput) - * - * ... but dont allow this to go on forever if a fast - * server is flooding a slow client with monitors ... - */ - countDown = 512u; - while (--countDown) { - CLR_CA_TIME (&timeout); - /* - * NOTE cac_select_io() will set the - * send flag for a particular iiu irregradless - * of what is requested here if piiu->pushPending - * is set - */ - count = cac_select_io(ca_static, &timeout, CA_DO_RECVS); - if (count<=0) { - break; - } - ca_process_input_queue(ca_static); - } - - /* - * manage search timers and detect disconnects - */ - manage_conn(ca_static); - - /* - * next check for pending writes's with the specified time out - * - * ... but dont allow this to go on forever if a fast - * server is flooding a slow client with monitors ... - */ - countDown = 512u; - timeout = *ptimeout; - while (TRUE) { - count = cac_select_io(ca_static, &timeout, CA_DO_RECVS|CA_DO_SENDS); - countDown--; - if (count<=0 || countDown==0u) { - /* - * if its a flush then loop until all - * of the send buffers are empty - */ - if (ca_static->ca_flush_pending) { - /* - * complete flush is postponed if we are - * inside an event routine - */ - if (EVENTLOCKTEST) { - break; - } - else { - if (caSendMsgPending(ca_static)) { - countDown = 512u; - LD_CA_TIME (cac_fetch_poll_period(ca_static), &timeout); - } - else { - ca_static->ca_flush_pending = FALSE; - break; - } - } - } - else { - break; - } - } - else { - CLR_CA_TIME (&timeout); - } - ca_process_input_queue(ca_static); - - } - - checkConnWatchdogs(ca_static, iocCloseAllowed); -} - -/* - * caSendMsgPending() - */ -int caSendMsgPending(CA_STATIC *ca_static) -{ - int pending = FALSE; - unsigned long bytesPending; - struct ioc_in_use *piiu; - - LOCK; - for (piiu = (IIU *) ellFirst(&iiuList); - piiu; piiu = (IIU *) ellNext(&piiu->node)){ - - if(piiu == piiuCast){ - continue; - } - - if (piiu->state == iiu_connected) { - bytesPending = cacRingBufferReadSize(&piiu->send, FALSE); - if(bytesPending > 0u){ - pending = TRUE; - } - } - } - UNLOCK; - - return pending; -} - diff --git a/src/ca/iocinf.cpp b/src/ca/iocinf.cpp new file mode 100644 index 000000000..5de924456 --- /dev/null +++ b/src/ca/iocinf.cpp @@ -0,0 +1,1570 @@ +/* * $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 + */ + +/* Allocate storage for global variables in this module */ +#define CA_GLBLSOURCE +#include "iocinf.h" +#include "net_convert.h" +#include "locationException.h" +#include "osiProcess.h" + +/* + * 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; + nciu *chan; + + LOCK (piiu->niiu.iiu.pcas); + while ( (chan = (nciu *) ellFirst (&piiu->niiu.chidList)) ) { + if (!chan->claimPending) { + piiu->claimsPending = FALSE; + break; + } + success = issue_claim_channel (chan); + if (!success) { + break; + } + } + UNLOCK (piiu->niiu.iiu.pcas); +} + +/* + * 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 { + initiateShutdownTCPIIU (piiu); + ca_printf (piiu->niiu.iiu.pcas, + "CAC: Unable to connect to \"%s\" because %d=\"%s\"\n", + piiu->host_name_str, errnoCpy, SOCKERRSTR(errnoCpy)); + return; + } + } + + /* + * put the iiu into the connected state + */ + piiu->state = iiu_connected; + + status = tsStampGetCurrent (&piiu->timeAtLastRecv); + assert (status==0); + + return; +} + +/* + * constructNIIU () + */ +void constructNIIU (cac *pcac, netIIU *piiu) +{ + piiu->iiu.pcas = pcac; + ellInit (&piiu->chidList); + + piiu->curDataMax = 0u; + piiu->curMsgBytes = 0u; + piiu->curDataBytes = 0u; + memset ( (void *) &piiu->curMsg, '\0', sizeof (piiu->curMsg) ); + piiu->pCurData = 0; +} + +/* + * destroyNIIU () + */ +LOCAL void destroyNIIU (netIIU *piiu) +{ + nciu *pChan, *pNext; + + pChan = (nciu *) ellFirst (&piiu->chidList); + while (pChan) { + pNext = (nciu *) ellNext (&pChan->node); + /* channel is destroyed here if it is disconnected */ + cacDisconnectChannel (pChan); + pChan = pNext; + } + + /* + * free message body cache + */ + if (piiu->pCurData) { + free (piiu->pCurData); + } +} + +/* + * cac_tcp_recv_msg () + */ +LOCAL void cac_tcp_recv_msg (tcpiiu *piiu) +{ + char *pProto; + unsigned writeSpace; + unsigned totalBytes; + int status; + + if ( piiu->state != iiu_connected ) { + return; + } + + pProto = (char *) cacRingBufferWriteReserve (&piiu->recv, &writeSpace); + + assert (writeSpace<=INT_MAX); + status = recv ( piiu->sock, pProto, (int) writeSpace, 0); + if ( status <= 0 ) { + int localErrno = SOCKERRNO; + + cacRingBufferWriteCommit (&piiu->recv, 0); + + if (status == 0) { + initiateShutdownTCPIIU (piiu); + return; + } + + if (localErrno == SOCK_SHUTDOWN) { + return; + } + + if ( localErrno == SOCK_EWOULDBLOCK || localErrno == SOCK_EINTR ) { + return; + } + + if ( localErrno != SOCK_EPIPE && + localErrno != SOCK_ECONNRESET && + localErrno != SOCK_ETIMEDOUT){ + ca_printf (piiu->niiu.iiu.pcas, + "CAC: unexpected TCP recv error: %s\n", SOCKERRSTR(localErrno)); + } + initiateShutdownTCPIIU (piiu); + return; + } + + assert ( ( (unsigned) status ) <= writeSpace ); + totalBytes = (unsigned) status; + + cacRingBufferWriteCommit (&piiu->recv, totalBytes); + cacRingBufferWriteFlush (&piiu->recv); + + status = tsStampGetCurrent (&piiu->timeAtLastRecv); + assert (status==0); + + return; +} + +/* + * cacSendThreadTCP () + */ +LOCAL void cacSendThreadTCP (void *pParam) +{ + tcpiiu *piiu = (tcpiiu *) pParam; + + while (1) { + unsigned sendCnt; + char *pOutBuf; + + pOutBuf = (char *) cacRingBufferReadReserve (&piiu->send, &sendCnt); + if (pOutBuf) { + int status; + + 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->claimsPending) { + retryPendingClaims (piiu); + } + } + else { + int localError = SOCKERRNO; + + cacRingBufferReadCommit (&piiu->send, 0); + + if (status==0) { + initiateShutdownTCPIIU (piiu); + break; + } + + if (localError == SOCK_SHUTDOWN) { + break; + } + + if ( localError != SOCK_EWOULDBLOCK && localError != SOCK_EINTR ) { + if ( localError != SOCK_EPIPE && localError != SOCK_ECONNRESET && + localError != SOCK_ETIMEDOUT) { + ca_printf ( piiu->niiu.iiu.pcas, "CAC: unexpected TCP send error: %s\n", + SOCKERRSTR (localError) ); + } + + initiateShutdownTCPIIU (piiu); + break; + } + } + } + else if ( piiu->state != iiu_connected ) { + break; + } + } + + semBinaryGive (piiu->sendThreadExitSignal); +} + +/* + * cacRecvThreadTCP () + */ +LOCAL void cacRecvThreadTCP (void *pParam) +{ + tcpiiu *piiu = (tcpiiu *) pParam; + unsigned chanDisconnectCount; + + cac_connect_tcp (piiu); + if ( piiu->state == iiu_connected ) { + threadId tid; + + tid = threadCreate ("CAC TCP Send", threadPriorityChannelAccessClient, + threadGetStackSize (threadStackMedium), cacSendThreadTCP, piiu); + if (tid) { + while (1) { + cac_tcp_recv_msg (piiu); + if ( piiu->state != iiu_connected ) { + break; + } + piiu->niiu.iiu.pcas->procThread.installLabor (*piiu); + } + } + } + + semBinaryMustTake (piiu->sendThreadExitSignal); + + piiu->niiu.iiu.pcas->procThread.removeLabor (*piiu); + + LOCK (piiu->niiu.iiu.pcas); + + chanDisconnectCount = ellCount (&piiu->niiu.chidList); + if (chanDisconnectCount) { + genLocalExcep (piiu->niiu.iiu.pcas, ECA_DISCONN, piiu->host_name_str); + } + + removeBeaconInetAddr (piiu->niiu.iiu.pcas, &piiu->dest.ia); + + ellDelete (&piiu->niiu.iiu.pcas->ca_iiuList, &piiu->node); + + destroyNIIU (&piiu->niiu); + + if (piiu->niiu.iiu.pcas->ca_fd_register_func) { + (*piiu->niiu.iiu.pcas->ca_fd_register_func) + ((void *)piiu->niiu.iiu.pcas->ca_fd_register_arg, piiu->sock, FALSE); + } + cacRingBufferDestroy (&piiu->recv); + cacRingBufferDestroy (&piiu->send); + socket_close (piiu->sock); + semBinaryDestroy (piiu->sendThreadExitSignal); + + semBinaryGive (piiu->recvThreadExitSignal); + + UNLOCK (piiu->niiu.iiu.pcas); + + free (piiu); +} + +/* + * constructTCPIIU () + * (lock must be applied by caller) + */ +tcpiiu * constructTCPIIU (cac *pcac, const struct sockaddr_in *pina, + unsigned minorVersion) +{ + SOCKET sock; + tcpiiu *piiu; + int status; + int flag; + threadId tid; + bhe *pBHE; + + /* + * look for an existing virtual circuit + */ + pBHE = lookupBeaconInetAddr (pcac, pina); + if (!pBHE) { + pBHE = createBeaconHashEntry (pcac, pina, FALSE); + if (!pBHE){ + UNLOCK (pcac); + return NULL; + } + } + + if (pBHE->piiu) { + if ( pBHE->piiu->state == iiu_connecting || + pBHE->piiu->state == iiu_connected ) { + return pBHE->piiu; + } + else { + return NULL; + } + } + + sock = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if (sock == INVALID_SOCKET) { + ca_printf (pcac, "CAC: unable to create virtual circuit because \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + return NULL; + } + + flag = TRUE; + status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&flag, sizeof(flag) ); + if (status < 0) { + ca_printf (pcac, "CAC: problems setting socket option TCP_NODELAY = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + + flag = TRUE; + status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, + (char *)&flag, sizeof (flag) ); + if (status < 0) { + ca_printf (pcac, "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 ( piiu->sock, SOL_SOCKET, SO_SNDBUF, + (char *)&i, sizeof (i) ); + if (status < 0) { + ca_printf (pcac, "CAC: problems setting socket option SO_SNDBUF = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + i = MAX_MSG_SIZE; + status = setsockopt (piiu->sock, SOL_SOCKET, SO_RCVBUF, + (char *)&i, sizeof (i) ); + if (status < 0) { + ca_printf (pcac, "CAC: problems setting socket option SO_RCVBUF = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + } +#endif + + piiu = (tcpiiu *) calloc (1, sizeof (*piiu) ); + if (!piiu) { + socket_close (sock); + return NULL; + } + + constructNIIU (pcac, &piiu->niiu); + piiu->sock = sock; + piiu->minor_version_number = minorVersion; + piiu->claimsPending = FALSE; + piiu->recvPending = FALSE; + piiu->pushPending = FALSE; + piiu->beaconAnomaly = FALSE; + piiu->dest.ia = *pina; + + status = cacRingBufferConstruct (&piiu->recv); + if (status) { + ca_printf (pcac, "CA: unable to create recv ring buffer\n"); + socket_close (sock); + destroyNIIU (&piiu->niiu); + free (piiu); + } + + status = cacRingBufferConstruct (&piiu->send); + if (status) { + ca_printf (pcac, "CA: unable to create send ring buffer\n"); + cacRingBufferDestroy (&piiu->recv); + socket_close (sock); + destroyNIIU (&piiu->niiu); + free (piiu); + } + + status = tsStampGetCurrent (&piiu->timeAtLastRecv); + assert (status==0); + + /* + * Save the Host name for efficient access in the + * future. + */ + ipAddrToA (&piiu->dest.ia, piiu->host_name_str, sizeof (piiu->host_name_str) ); + + /* + * TCP starts out in the connecting state and later transitions + * to the connected state + */ + piiu->state = iiu_connecting; + + piiu->sendThreadExitSignal = semBinaryCreate (semEmpty); + if ( !piiu->sendThreadExitSignal ) { + ca_printf (pcac, "CA: unable to create CA client send thread exit semaphore\n"); + cacRingBufferDestroy (&piiu->recv); + cacRingBufferDestroy (&piiu->send); + destroyNIIU (&piiu->niiu); + socket_close (sock); + free (piiu); + return NULL; + } + + piiu->recvThreadExitSignal = semBinaryCreate (semEmpty); + if ( !piiu->sendThreadExitSignal ) { + ca_printf (pcac, "CA: unable to create CA client send thread exit semaphore\n"); + semBinaryDestroy (piiu->sendThreadExitSignal); + cacRingBufferDestroy (&piiu->recv); + cacRingBufferDestroy (&piiu->send); + destroyNIIU (&piiu->niiu); + socket_close (sock); + free (piiu); + return NULL; + } + + tid = threadCreate ( + "CAC TCP Recv", + threadPriorityChannelAccessClient, + threadGetStackSize (threadStackMedium), + cacRecvThreadTCP, + piiu); + if (tid==0) { + ca_printf (pcac, "CA: unable to create CA client receive thread\n"); + semBinaryDestroy (piiu->recvThreadExitSignal); + semBinaryDestroy (piiu->sendThreadExitSignal); + cacRingBufferDestroy (&piiu->recv); + cacRingBufferDestroy (&piiu->send); + destroyNIIU (&piiu->niiu); + socket_close (sock); + free (piiu); + return NULL; + } + pBHE->piiu = piiu; + + ellAdd (&pcac->ca_iiuList, &piiu->node); + + if (pcac->ca_fd_register_func) { + (*pcac->ca_fd_register_func) ((void *)pcac->ca_fd_register_arg, piiu->sock, TRUE); + } + + return piiu; +} + +/* + * initiateShutdownTCPIIU () + */ +void initiateShutdownTCPIIU (tcpiiu *piiu) +{ + LOCK (piiu->niiu.iiu.pcas); + if (piiu->state != iiu_disconnected) { + piiu->state = iiu_disconnected; + shutdown (piiu->sock, SD_BOTH); + cacRingBufferShutDown (&piiu->send); + cacRingBufferShutDown (&piiu->recv); + } + UNLOCK (piiu->niiu.iiu.pcas); +} + +/* + * cac_udp_recv_msg () + */ +LOCAL int cac_udp_recv_msg (udpiiu *piiu) +{ + osiSockAddr src; + int src_size = sizeof (src); + int status; + + status = recvfrom (piiu->sock, piiu->recvBuf, sizeof (piiu->recvBuf), 0, + &src.sa, &src_size); + if (status < 0) { + int errnoCpy = SOCKERRNO; + + if (errnoCpy == SOCK_SHUTDOWN) { + return -1; + } + if (errnoCpy == SOCK_EWOULDBLOCK || errnoCpy == SOCK_EINTR) { + return 0; + } +# ifdef linux + /* + * Avoid spurious ECONNREFUSED bug + * in linux + */ + if (errnoCpy==SOCK_ECONNREFUSED) { + return 0; + } +# endif + ca_printf (piiu->niiu.iiu.pcas, + "Unexpected UDP recv error %s\n", SOCKERRSTR(errnoCpy)); + } + else if (status > 0) { + + status = post_msg (&piiu->niiu, &src.ia, + piiu->recvBuf, (unsigned long) status); + if ( status!=ECA_NORMAL || piiu->niiu.curMsgBytes ) { + char buf[64]; + + ipAddrToA (&src.ia, buf, sizeof(buf)); + + ca_printf (piiu->niiu.iiu.pcas, + "%s: bad UDP msg from %s\n", __FILE__, buf); + + /* + * resync the ring buffer + * (discard existing messages) + */ + piiu->niiu.curMsgBytes = 0; + piiu->niiu.curDataBytes = 0; + return 0; + } + } + + return 0; +} + +/* + * cacRecvThreadUDP () + */ +LOCAL void cacRecvThreadUDP (void *pParam) +{ + udpiiu *piiu = (udpiiu *) pParam; + int status; + + do { + status = cac_udp_recv_msg (piiu); + } while ( status == 0 ); + + semBinaryGive (piiu->recvThreadExitSignal); +} + +/* + * NOTIFY_CA_REPEATER() + * + * tell the cast repeater that another client needs fan out + * + * NOTES: + * 1) local communication only (no LAN traffic) + * + */ +void notify_ca_repeater (udpiiu *piiu) +{ + caHdr msg; + osiSockAddr saddr; + int status; + static int once = FALSE; + int len; + + if (piiu->repeaterContacted) { + return; + } + + if (piiu->repeaterTries > N_REPEATER_TRIES_PRIOR_TO_MSG ) { + if (!once) { + ca_printf (piiu->niiu.iiu.pcas, + "Unable to contact CA repeater after %d tries\n", + N_REPEATER_TRIES_PRIOR_TO_MSG); + ca_printf (piiu->niiu.iiu.pcas, + "Silence this message by starting a CA repeater daemon\n"); + once = TRUE; + } + } + + /* + * In 3.13 beta 11 and before the CA repeater calls local_addr() + * to determine a local address and does not allow registration + * messages originating from other addresses. In these + * releases local_addr() returned the address of the first enabled + * interface found, and this address may or may not have been the loop + * back address. Starting with 3.13 beta 12 local_addr() was + * changed to always return the address of the first enabled + * non-loopback interface because a valid non-loopback local + * address is required in the beacon messages. Therefore, to + * guarantee compatibility with past versions of the repeater + * we alternate between the address returned by local_addr() + * and the loopback address here. + * + * CA repeaters in R3.13 beta 12 and higher allow + * either the loopback address or the address returned + * by local address (the first non-loopback address found) + */ + if (piiu->repeaterTries&1) { + saddr = osiLocalAddr (piiu->sock); + if (saddr.sa.sa_family != AF_INET) { + /* + * use the loop back address to communicate with the CA repeater + * if this os does not have interface query capabilities + * + * this will only work with 3.13 beta 12 CA repeaters or later + */ + saddr.ia.sin_family = AF_INET; + saddr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + saddr.ia.sin_port = htons (piiu->repeaterPort); + } + } + else { + saddr.ia.sin_family = AF_INET; + saddr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + saddr.ia.sin_port = htons (piiu->repeaterPort); + } + + memset ((char *)&msg, 0, sizeof(msg)); + msg.m_cmmd = htons (REPEATER_REGISTER); + msg.m_available = saddr.ia.sin_addr.s_addr; + + /* + * Intentionally sending a zero length message here + * until most CA repeater daemons have been restarted + * (and only then will accept the above protocol) + * (repeaters began accepting this protocol + * starting with EPICS 3.12) + * + * SOLARIS will not accept a zero length message + * and we are just porting there for 3.12 so + * we will use the new protocol for 3.12 + * + * recent versions of UCX will not accept a zero + * length message and we will assume that folks + * using newer versions of UCX have rebooted (and + * therefore restarted the CA repeater - and therefore + * moved it to an EPICS release that accepts this protocol) + */ +# if defined (DOES_NOT_ACCEPT_ZERO_LENGTH_UDP) + len = sizeof (msg); +# else + len = 0; +# endif + + status = sendto (piiu->sock, (char *)&msg, len, + 0, (struct sockaddr *)&saddr, sizeof(saddr)); + if (status < 0) { + int errnoCpy = SOCKERRNO; + if( errnoCpy != SOCK_EINTR && + errnoCpy != SOCK_EWOULDBLOCK && + /* + * This is returned from Linux when + * the repeater isnt running + */ + errnoCpy != SOCK_ECONNREFUSED + ) { + ca_printf (piiu->niiu.iiu.pcas, + "CAC: error sending to repeater was \"%s\"\n", + SOCKERRSTR(errnoCpy)); + } + } + piiu->repeaterTries++; + piiu->contactRepeater = 0u; +} + +/* + * cacSendThreadUDP () + */ +LOCAL void cacSendThreadUDP (void *pParam) +{ + udpiiu *piiu = (udpiiu *) pParam; + + while (1) { + int status; + + if (piiu->contactRepeater) { + notify_ca_repeater (piiu); + } + + semMutexMustTake (piiu->xmitBufLock); + + if (piiu->nBytesInXmitBuf > 0) { + osiSockAddrNode *pNode; + + pNode = (osiSockAddrNode *) ellFirst (&piiu->dest); + while (pNode) { + + assert ( piiu->nBytesInXmitBuf <= INT_MAX ); + status = sendto (piiu->sock, piiu->xmitBuf, + (int) piiu->nBytesInXmitBuf, 0, + &pNode->addr.sa, sizeof(pNode->addr.sa)); + if (status <= 0) { + int localErrno = SOCKERRNO; + + if (status==0) { + break; + } + + if (localErrno == SOCK_SHUTDOWN) { + break; + } + else if ( localErrno == SOCK_EINTR ) { + status = 1; + } + else { + char buf[64]; + + ipAddrToA (&pNode->addr.ia, buf, sizeof (buf)); + + ca_printf (piiu->niiu.iiu.pcas, + "CAC: error = \"%s\" sending UDP msg to %s\n", + SOCKERRSTR(localErrno), buf); + + break; + } + } + pNode = (osiSockAddrNode *) ellNext (&pNode->node); + } + + piiu->nBytesInXmitBuf = 0u; + + if (status<=0) { + break; + } + } + + semMutexGive (piiu->xmitBufLock); + + semBinaryMustTake (piiu->xmitSignal); + } + + semBinaryGive (piiu->sendThreadExitSignal); +} + +/* + * cacShutdownUDP () + */ +void cacShutdownUDP (udpiiu &iiu) +{ + shutdown (iiu.sock, SD_BOTH); + semBinaryGive (iiu.xmitSignal); +} + +/* + * udpiiu::~udpiiu () + */ +udpiiu::~udpiiu () +{ + semBinaryMustTake (this->recvThreadExitSignal); + semBinaryMustTake (this->sendThreadExitSignal); + + semBinaryDestroy (this->xmitSignal); + semMutexDestroy (this->xmitBufLock); + semBinaryDestroy (this->recvThreadExitSignal); + semBinaryDestroy (this->sendThreadExitSignal); + ellFree (&this->dest); + + if (this->niiu.iiu.pcas->ca_fd_register_func) { + (*this->niiu.iiu.pcas->ca_fd_register_func) + (this->niiu.iiu.pcas->ca_fd_register_arg, this->sock, FALSE); + } + destroyNIIU (&this->niiu); + socket_close (this->sock); +} + +/* + * getToken() + */ +LOCAL char *getToken(const char **ppString, char *pBuf, unsigned bufSIze) +{ + const char *pToken; + unsigned i; + + pToken = *ppString; + while(isspace(*pToken)&&*pToken){ + pToken++; + } + + for (i=0u; iname); + ca_printf (pcac, "\tBad internet address or host name: '%s'\n", pToken); + continue; + } + + pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); + if (pNewNode==NULL) { + ca_printf (pcac, "caAddConfiguredAddr(): no memory available for configuration\n"); + return; + } + + pNewNode->addr.ia = addr; + + /* + * LOCK applied externally + */ + ellAdd (pList, &pNewNode->node); + } + + return; +} + +/* + * caSetupBCastAddrList() + */ +void caSetupBCastAddrList (cac *pcac, ELLLIST *pList, + SOCKET sock, unsigned short port) +{ + osiSockAddrNode *pNode; + ELLLIST tmpList; + char *pstr; + char yesno[32u]; + int yes; + + /* + * dont load the list twice + */ + assert ( ellCount(pList) == 0 ); + + ellInit ( &tmpList ); + + /* + * Check to see if the user has disabled + * initializing the search b-cast list + * from the interfaces found. + */ + yes = TRUE; + pstr = envGetConfigParam (&EPICS_CA_AUTO_ADDR_LIST, + sizeof(yesno), yesno); + if (pstr) { + if ( strstr ( pstr, "no" ) || strstr ( pstr, "NO" ) ) { + yes = FALSE; + } + } + + /* + * LOCK is for piiu->destAddr list + * (lock outside because this is used by the server also) + */ + if (yes) { + osiSockAddr addr; + addr.ia.sin_family = AF_UNSPEC; + osiSockDiscoverBroadcastAddresses ( &tmpList, sock, &addr ); + } + + caAddConfiguredAddr (pcac, &tmpList, &EPICS_CA_ADDR_LIST, sock, port ); + + /* + * eliminate duplicates and set the port + */ + while ( (pNode = (osiSockAddrNode *) ellGet ( &tmpList ) ) ) { + osiSockAddrNode *pTmpNode; + + if ( pNode->addr.sa.sa_family == AF_INET ) { + /* + * set the correct destination port + */ + pNode->addr.ia.sin_port = htons (port); + + pTmpNode = (osiSockAddrNode *) ellFirst (pList); + while ( pTmpNode ) { + if (pTmpNode->addr.sa.sa_family == AF_INET) { + if (pNode->addr.ia.sin_addr.s_addr == pTmpNode->addr.ia.sin_addr.s_addr && + pNode->addr.ia.sin_port == pTmpNode->addr.ia.sin_port) { + char buf[64]; + ipAddrToA (&pNode->addr.ia, buf, sizeof(buf)); + printf ("Warning: Duplicate EPICS CA Address list entry \"%s\" discarded\n", buf); + free (pNode); + pNode = NULL; + break; + } + } + pTmpNode = (osiSockAddrNode *) ellNext (&pNode->node); + } + if (pNode) { + ellAdd (pList, &pNode->node); + } + } + else { + ellAdd (pList, &pNode->node); + } + } + + /* + * print warning message if there is an empty search query + * address list + */ + if ( ellCount ( pList ) == 0 ) { + genLocalExcep ( NULL, ECA_NOSEARCHADDR, NULL ); + return; + } +} + +/* + * repeater_installed () + * + * Test for the repeater already installed + * + * NOTE: potential race condition here can result + * in two copies of the repeater being spawned + * however the repeater detects this, prints a message, + * and lets the other task start the repeater. + * + * QUESTION: is there a better way to test for a port in use? + * ANSWER: none that I can find. + * + * Problems with checking for the repeater installed + * by attempting to bind a socket to its address + * and port. + * + * 1) Closed socket may not release the bound port + * before the repeater wakes up and tries to grab it. + * Attempting to bind the open socket to another port + * also does not work. + * + * 072392 - problem solved by using SO_REUSEADDR + */ +int repeater_installed (udpiiu *piiu) +{ + int installed = FALSE; + int status; + SOCKET sock; + struct sockaddr_in bd; + int flag; + + sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == INVALID_SOCKET) { + return installed; + } + + memset ( (char *) &bd, 0, sizeof (bd) ); + bd.sin_family = AF_INET; + bd.sin_addr.s_addr = htonl (INADDR_ANY); + bd.sin_port = htons (piiu->repeaterPort); + status = bind (sock, (struct sockaddr *) &bd, sizeof(bd) ); + if (status<0) { + if (SOCKERRNO == SOCK_EADDRINUSE) { + installed = TRUE; + } + } + + /* + * turn on reuse only after the test so that + * this works on kernels that support multicast + */ + flag = TRUE; + status = setsockopt ( sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&flag, sizeof (flag) ); + if (status<0) { + ca_printf (piiu->niiu.iiu.pcas, "CAC: set socket option reuseaddr set failed\n"); + } + + socket_close (sock); + + return installed; +} + +// +// udpiiu::udpiiu () +// +udpiiu::udpiiu (cac *pcac) : + searchTmr (*this, pcac->timerQueue), repeaterSubscribeTmr (*this, pcac->timerQueue) +{ + static const unsigned short PORT_ANY = 0u; + osiSockAddr addr; + int boolValue = TRUE; + SOCKET sock; + int status; + threadId tid; + + this->repeaterPort = + caFetchPortConfig (pcac, &EPICS_CA_REPEATER_PORT, CA_REPEATER_PORT); + + sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == INVALID_SOCKET) { + ca_printf (pcac, "CAC: unable to create datagram socket because = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + throwWithLocation ( noSocket () ); + } + + status = setsockopt ( sock, SOL_SOCKET, SO_BROADCAST, + (char *) &boolValue, sizeof (boolValue) ); + if (status<0) { + ca_printf (pcac, "CAC: unable to enable IP broadcasting because = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + +#if 0 + { + /* + * some concern that vxWorks will run out of mBuf's + * if this change is made joh 11-10-98 + * + * bump up the UDP recv buffer + */ + int size = 1u<<15u; + status = setsockopt ( sock, SOL_SOCKET, SO_RCVBUF, + (char *)&size, sizeof (size) ); + if (status<0) { + ca_printf ("CAC: unable to set socket option SO_RCVBUF because \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + } + } +#endif + + /* + * force a bind to an unconstrained address because we may end + * up receiving first + */ + memset ( (char *)&addr, 0 , sizeof (addr) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl (INADDR_ANY); + addr.ia.sin_port = htons (PORT_ANY); + status = bind (sock, &addr.sa, sizeof (addr) ); + if (status<0) { + socket_close (sock); + ca_printf (pcac, "CAC: unable to bind to an unconstrained address because = \"%s\"\n", + SOCKERRSTR (SOCKERRNO)); + throwWithLocation ( noSocket () ); + } + + constructNIIU (pcac, &this->niiu); + + this->sock = sock; + this->nBytesInXmitBuf = 0u; + this->contactRepeater = 0u; + this->repeaterContacted = 0u; + this->repeaterTries = 0u; + + this->xmitBufLock = semMutexCreate (); + if (!this->xmitBufLock) { + socket_close (this->sock); + destroyNIIU (&this->niiu); + throwWithLocation ( noMemory () ); + } + + this->recvThreadExitSignal = semBinaryCreate (semEmpty); + if (!this->recvThreadExitSignal) { + semMutexDestroy (this->xmitBufLock); + socket_close (this->sock); + destroyNIIU (&this->niiu); + throwWithLocation ( noMemory () ); + } + + this->sendThreadExitSignal = semBinaryCreate (semEmpty); + if (!this->sendThreadExitSignal) { + semBinaryDestroy (this->recvThreadExitSignal); + semMutexDestroy (this->xmitBufLock); + destroyNIIU (&this->niiu); + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + + this->xmitSignal = semBinaryCreate (semEmpty); + if (!this->sendThreadExitSignal) { + ca_printf (pcac, "CA: unable to create xmit signal\n"); + semBinaryDestroy (this->recvThreadExitSignal); + semBinaryDestroy (this->sendThreadExitSignal); + semMutexDestroy (this->xmitBufLock); + destroyNIIU (&this->niiu); + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + + /* + * load user and auto configured + * broadcast address list + */ + ellInit (&this->dest); + caSetupBCastAddrList (pcac, &this->dest, this->sock, pcac->ca_server_port); + + tid = threadCreate ("CAC UDP Recv", threadPriorityChannelAccessClient, + threadGetStackSize (threadStackMedium), cacRecvThreadUDP, this); + if (tid==0) { + ca_printf (pcac, "CA: unable to create UDP receive thread\n"); + shutdown (this->sock, SD_BOTH); + semBinaryDestroy (this->xmitSignal); + semBinaryDestroy (this->recvThreadExitSignal); + semBinaryDestroy (this->sendThreadExitSignal); + semMutexDestroy (this->xmitBufLock); + destroyNIIU (&this->niiu); + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + + tid = threadCreate ( "CAC UDP Send", threadPriorityChannelAccessClient, + threadGetStackSize (threadStackMedium), cacSendThreadUDP, this); + if (tid==0) { + ca_printf (pcac, "CA: unable to create UDP transmitt thread\n"); + shutdown (this->sock, SD_BOTH); + semMutexMustTake (this->recvThreadExitSignal); + semBinaryDestroy (this->xmitSignal); + semBinaryDestroy (this->recvThreadExitSignal); + semBinaryDestroy (this->sendThreadExitSignal); + semMutexDestroy (this->xmitBufLock); + destroyNIIU (&this->niiu); + socket_close (this->sock); + throwWithLocation ( noMemory () ); + } + + if (pcac->ca_fd_register_func) { + (*pcac->ca_fd_register_func) (pcac->ca_fd_register_arg, this->sock, TRUE); + } + + if ( ! repeater_installed (this) ) { + osiSpawnDetachedProcessReturn osptr; + + /* + * This is not called if the repeater is known to be + * already running. (in the event of a race condition + * the 2nd repeater exits when unable to attach to the + * repeater's port) + */ + osptr = osiSpawnDetachedProcess ("CA Repeater", "caRepeater"); + if (osptr==osiSpawnDetachedProcessNoSupport) { + threadId tid; + + tid = threadCreate ( + "CA repeater", + threadPriorityChannelAccessClient, + threadGetStackSize (threadStackMedium), + caRepeaterThread, + 0); + if (tid==0) { + ca_printf (pcac, "CA: unable to create CA repeater daemon thread\n"); + } + } + else if (osptr==osiSpawnDetachedProcessFail) { + ca_printf (pcac, "CA: unable to start CA repeater daemon detached process\n"); + } + } + + this->repeaterSubscribeTmr.reschedule (); +} + + +/* + * cacDisconnectChannel() + */ +void cacDisconnectChannel (nciu *chan) +{ + cac *pcac = chan->ciu.piiu->pcas; + + LOCK (pcac); + + if (chan->ciu.piiu == &pcac->pudpiiu->niiu.iiu) { + netChannelDestroy (pcac, chan->cid); + UNLOCK (pcac); + return; + } + + chan->type = USHRT_MAX; + chan->count = 0u; + chan->sid = UINT_MAX; + chan->ar.read_access = FALSE; + chan->ar.write_access = FALSE; + + /* + * call their connection handler as required + */ + if ( chan->connected ) { + nmiu *monix, *next; + + /* + * look for events that have an event cancel in progress + */ + for (monix = (nmiu *) ellFirst (&chan->eventq); + monix; monix = next) { + + next = (nmiu *) ellNext (&monix->node); + + if (monix->cmd==CA_PROTO_EVENT_ADD) { + /* + * if there is an event cancel in progress + * delete the event - we will never receive + * an event cancel confirm from this server + */ + if (monix->miu.usr_func == NULL) { + caIOBlockFree (chan->ciu.piiu->pcas, monix->id); + } + } + else { + struct event_handler_args args; + + args.usr = monix->miu.usr_arg; + args.chid = monix->miu.pChan; + args.type = monix->miu.type; + args.count = monix->miu.count; + args.status = ECA_DISCONN; + args.dbr = NULL; + + if (monix->miu.usr_func) { + (*monix->miu.usr_func) (args); + } + caIOBlockFree (chan->ciu.piiu->pcas, monix->id); + } + } + + if (chan->ciu.pConnFunc) { + struct connection_handler_args args; + + args.chid = (chid) &chan->ciu; + args.op = CA_OP_CONN_DOWN; + (*chan->ciu.pConnFunc) (args); + } + if (chan->ciu.pAccessRightsFunc) { + struct access_rights_handler_args args; + + args.chid = (chid) &chan->ciu; + args.ar = chan->ar; + (*chan->ciu.pAccessRightsFunc) (args); + } + } + removeFromChanList (chan); + /* + * try to reconnect + */ + assert (pcac->pudpiiu); + addToChanList (chan, &pcac->pudpiiu->niiu); + pcac->pudpiiu->searchTmr.reset (0.0); + UNLOCK (pcac); +} + +/* + * localHostName() + * + * o Indicates failure by setting ptr to nill + * + * o Calls non posix gethostbyname() so that we get DNS style names + * (gethostbyname() should be available with most BSD sock libs) + * + * vxWorks user will need to configure a DNS format name for the + * host name if they wish to be cnsistent with UNIX and VMS hosts. + * + * this needs to attempt to determine if the process is a remote + * login - hard to do under UNIX + */ +char *localHostName () +{ + int size; + int status; + char pName[MAXHOSTNAMELEN]; + char *pTmp; + + status = gethostname ( pName, sizeof (pName) ); + if(status){ + return NULL; + } + + size = strlen (pName)+1; + pTmp = (char *) malloc (size); + if (!pTmp) { + return pTmp; + } + + strncpy ( pTmp, pName, size-1 ); + pTmp[size-1] = '\0'; + + return pTmp; +} + +/* + * caPrintAddrList() + */ +void caPrintAddrList (ELLLIST *pList) +{ + osiSockAddrNode *pNode; + + printf ("Channel Access Address List\n"); + pNode = (osiSockAddrNode *) ellFirst (pList); + while (pNode) { + char buf[64]; + ipAddrToA (&pNode->addr.ia, buf, sizeof(buf)); + printf ("%s\n", buf); + pNode = (osiSockAddrNode *) ellNext (&pNode->node); + } +} + +/* + * caFetchPortConfig() + */ +unsigned short caFetchPortConfig + (cac *pcac, const ENV_PARAM *pEnv, unsigned short defaultPort) +{ + long longStatus; + long epicsParam; + int port; + + longStatus = envGetLongConfigParam(pEnv, &epicsParam); + if (longStatus!=0) { + epicsParam = (long) defaultPort; + ca_printf (pcac, "EPICS \"%s\" integer fetch failed\n", pEnv->name); + ca_printf (pcac, "setting \"%s\" = %ld\n", pEnv->name, epicsParam); + } + + /* + * This must be a server port that will fit in an unsigned + * short + */ + if (epicsParam<=IPPORT_USERRESERVED || epicsParam>USHRT_MAX) { + ca_printf (pcac, "EPICS \"%s\" out of range\n", pEnv->name); + /* + * Quit if the port is wrong due CA coding error + */ + assert (epicsParam != (long) defaultPort); + epicsParam = (long) defaultPort; + ca_printf (pcac, "Setting \"%s\" = %ld\n", pEnv->name, epicsParam); + } + + /* + * ok to clip to unsigned short here because we checked the range + */ + port = (unsigned short) epicsParam; + + return port; +} + +#if 0 +/* + * cacPushPending() + */ +LOCAL unsigned cacPushPending (cac *pcac) +{ + unsigned pending = FALSE; + unsigned long bytesPending; + netIIU *piiu; + + LOCK (pcac); + for ( piiu = (netIIU *) ellFirst (&pcac->ca_iiuList); + piiu; piiu = (netIIU *) ellNext (&piiu->node) ){ + + if (piiu == pcac->pudpiiu) { + continue; + } + + if ( piiu->state == iiu_connected && piiu->pushPending ) { + bytesPending = cacRingBufferReadSize (&piiu->send, FALSE); + if(bytesPending > 0u){ + pending = TRUE; + break; + } + } + } + UNLOCK (pcac); + + return pending; +} +#endif + +#if 0 +/* + * CAC_MUX_IO() + */ +void cac_mux_io (cac *pcac, double maxDelay, unsigned iocCloseAllowed) +{ + int count; + double timeOut; + unsigned countDown; + + /* + * first check for pending recv's with a zero time out so that + * 1) flow control works correctly (and) + * 2) we queue up sends resulting from recvs properly + * (this results in improved max throughput) + * + * ... but dont allow this to go on forever if a fast + * server is flooding a slow client with monitors ... + */ + countDown = 512u; + while (--countDown) { + /* + * NOTE cac_select_io() will set the + * send flag for a particular iiu irregradless + * of what is requested here if piiu->pushPending + * is set + */ + count = cac_select_io (pcac, 0.0, CA_DO_RECVS); + if (count<=0) { + break; + } + ca_process_input_queue (pcac); + } + + /* + * manage search timers and detect disconnects + */ + manage_conn(pcac); + + /* + * next check for pending writes's with the specified time out + * + * ... but dont allow this to go on forever if a fast + * server is flooding a slow client with monitors ... + */ + countDown = 512u; + timeOut = maxDelay; + while (TRUE) { + count = cac_select_io (pcac, timeOut, CA_DO_RECVS|CA_DO_SENDS); + countDown--; + if (count<=0 || countDown==0u) { + /* + * if its a flush then loop until all + * of the send buffers are empty + */ + if (pcac->ca_flush_pending) { + /* + * complete flush is deferred if we are + * inside an event routine + */ + if (EVENTLOCKTEST(pcac)) { + break; + } + else { + if ( cacPushPending (pcac) ) { + countDown = 512u; + timeOut = cac_fetch_poll_period (pcac); + /* + * allow connection to be marked disconnected + * if we wait too long for the flush to occurr + */ + checkConnWatchdogs (pcac, FALSE); + } + else { + pcac->ca_flush_pending = FALSE; + break; + } + } + } + else { + break; + } + } + else { + timeOut = 0.0; + } + ca_process_input_queue (pcac); + } + + checkConnWatchdogs (pcac); +} +#endif + +tcpiiu *iiuToTCPIIU (baseIIU *pIn) +{ + char *pc = (char *) pIn; + + assert (pIn != &pIn->pcas->localIIU.iiu); + assert (pIn != &pIn->pcas->pudpiiu->niiu.iiu); + pc -= offsetof (tcpiiu, niiu.iiu); + return (tcpiiu *) pc; +} + +/* + * convert a generic IIU pointer to a network IIU + */ +netIIU *iiuToNIIU (baseIIU *pIn) +{ + char *pc = (char *) pIn; + + assert (pIn != &pIn->pcas->localIIU.iiu); + pc -= offsetof (netIIU, iiu); + return (netIIU *) pc; +} + +/* + * convert a generic IIU pointer to a local IIU + */ +lclIIU *iiuToLIIU (baseIIU *pIn) +{ + char *pc = (char *) pIn; + + assert (pIn == &pIn->pcas->localIIU.iiu); + pc -= offsetof (lclIIU, iiu); + return (lclIIU *) pc; +} diff --git a/src/ca/iocinf.h b/src/ca/iocinf.h index 7e910e53b..07ee55828 100644 --- a/src/ca/iocinf.h +++ b/src/ca/iocinf.h @@ -1,199 +1,29 @@ -/* $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. */ -/* */ -/* */ -/* History */ -/* ------- */ -/* .01 08xx87 joh Init Release */ -/* .02 01xx90 joh fd_set in the UNIX version only */ -/* .03 060691 joh Rearanged buffer struct for SPARC port */ -/* .04 072391 joh new lock prevents event preemption on vxWorks */ -/* .05 082791 joh declaration of ca_request_event() */ -/* .06 082791 joh added send message in progress flag */ -/* .07 091691 joh moved channel_state enum to cadef.h for export */ -/* .08 102991 joh added sprintf buffer */ -/* .09 111891 joh added event task id for vxWorks */ -/* .10 111991 joh mobe IODONESUB macro to service.c */ -/* .11 031692 joh added declaration for post_msg() */ -/* .12 031892 joh initial rebroadcast delay is now a #define */ -/* .13 050492 joh added exception channel fd set */ -/* .14 111792 joh increased MAXCONNTRIES from 3 to 30 */ -/* .15 112092 joh added AST lck count var for VMS */ -/* .16 120992 joh switched to dll list routines */ -/* .17 121892 joh added TCP send buf size var */ -/* .18 122192 joh added outstanding ack var */ -/* .19 012094 joh added minor version (for each server) */ -/************************************************************************/ -/* - * $Log$ - * Revision 1.82 2000/01/04 17:09:34 mrk - * dont include osiTime.h +/* + * $Id$ * - * Revision 1.81 1999/11/18 16:15:42 mrk - * changes for iocCore port - * - * Revision 1.80 1999/11/08 17:14:43 jhill - * added prenthesis around arguments to VALID_MSG macro - * - * Revision 1.79 1999/11/08 17:01:43 jhill - * fixed problem with VALID_MSG(PIIU) macro - * - * Revision 1.78 1999/10/14 23:22:13 jhill - * dont detect a flow control situation when select is telling us there is - * something to read when there isnt anything there - * - * Revision 1.77 1999/09/14 23:38:18 jhill - * added ca_vprintf() function - * - * Revision 1.76 1999/09/02 21:44:49 jhill - * improved the way that socket error numbers are converted to strings, - * changed () to (void) in func proto, and fixed missing parameter to - * checkConnWatchdogs() bug resulting from this - * - * Revision 1.75 1999/07/16 17:02:06 jhill - * added congestion thresh parm for search alg - * - * Revision 1.74.4.1 1999/07/15 20:52:38 jhill - * added congestion thresh to search sched alg - * - * Revision 1.74 1998/10/07 14:32:52 jba - * Modified log message. - * - * Revision 1.73 1998/09/24 21:22:53 jhill - * CLR_CA_TIME() now correctly zeros the delay - * - * Revision 1.72 1998/06/16 01:07:56 jhill - * removed caHostFromInetAddr - * - * Revision 1.71 1998/05/05 16:04:19 jhill - * added lock count var - * - * Revision 1.70 1998/04/13 19:14:34 jhill - * fixed task variable problem - * - * Revision 1.69 1998/03/12 20:39:10 jhill - * fixed problem where 3.13.beta11 unable to connect to 3.11 with correct native type - * - * Revision 1.68 1998/02/20 21:52:21 evans - * Added an explicit include of tsDefs.h before cadef.h to avoid the - * functions in it being declared as export and also to avoid its - * allocating space when it should be declaring a reference. - * - * Revision 1.67 1998/02/05 22:30:34 jhill - * fixed dll export problems - * - * Revision 1.66 1997/08/04 23:37:11 jhill - * added beacon anomaly flag init/allow ip 255.255.255.255 - * - * Revision 1.64 1997/06/13 09:14:21 jhill - * connect/search proto changes - * - * Revision 1.63 1997/05/05 04:45:25 jhill - * send_needed => pushPending, and added ca_number_iiu_in_fc - * - * Revision 1.62 1997/04/29 06:11:08 jhill - * use free lists - * - * Revision 1.61 1997/04/23 17:05:07 jhill - * pc port changes - * - * Revision 1.60 1997/04/10 19:26:24 jhill - * asynch connect, faster connect, ... - * - * Revision 1.59 1997/01/22 21:10:26 jhill - * smaller external sym name for VAXC - * - * Revision 1.58 1996/11/02 00:50:56 jhill - * many pc port, const in API, and other changes - * - * Revision 1.57 1996/09/16 16:38:05 jhill - * local except => except handler - * - * Revision 1.56 1996/08/13 23:16:19 jhill - * removed os specific code - * - * Revision 1.55 1996/08/05 19:21:26 jhill - * removed unused proto - * - * Revision 1.54 1996/06/20 21:43:15 jhill - * restored io_done_sem (removed by cleanup) - * - * Revision 1.53 1996/06/20 21:19:35 jhill - * fixed posix signal problem with "cc -Xc" - * - * Revision 1.52 1996/06/19 17:59:07 jhill - * many 3.13 beta changes - * - * Revision 1.51 1995/12/19 19:33:07 jhill - * function prototype changes - * - * Revision 1.50 1995/10/18 16:45:40 jhill - * Use recast delay greater than one vxWorks tick - * - * Revision 1.49 1995/10/12 01:33:12 jhill - * Initial delay between search frames went from .1 to .01 sec, - * Added flush pending flag, Make all usage of port be unsigned short. - * - * Revision 1.48 1995/09/29 21:55:38 jhill - * added func proto for cacDisconnectChannel() - * - * Revision 1.47 1995/08/22 00:20:27 jhill - * added KLUDGE def of S_db_Pending + * + * 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 */ -/*_begin */ -/************************************************************************/ -/* */ -/* Title: channel access TCPIP interface include file */ -/* File: atcs:[ca]iocinf.h */ -/* Environment: VMS, UNIX, VRTX */ -/* */ -/* */ -/* Purpose */ -/* ------- */ -/* */ -/* ioc interface include */ -/* */ -/* */ -/* Special comments */ -/* ------- -------- */ -/* Use GLBLTYPE to define externals so we can change the all at */ -/* once from VAX globals to generic externals */ -/* */ -/************************************************************************/ -/*_end */ #ifndef INCiocinfh #define INCiocinfh #ifdef CA_GLBLSOURCE -# define GLBLTYPE +# define GLBLTYPE #else -# define GLBLTYPE extern +# define GLBLTYPE extern #endif -#if defined(__STDC__) && !defined(VAXC) -# define VERSIONID(NAME,VERS) \ - char *EPICS_CAS_VID_ ## NAME = VERS; -#else /*__STDC__*/ -# define VERSIONID(NAME,VERS) \ - char *EPICS_CAS_VID_/* */NAME = VERS; -#endif /*__STDC__*/ - -#ifdef CAC_VERSION_GLOBAL -# define HDRVERSIONID(NAME,VERS) VERSIONID(NAME,VERS) -#else /*CAC_VERSION_GLOBAL*/ -# define HDRVERSIONID(NAME,VERS) -#endif /*CAC_VERSION_GLOBAL*/ - -HDRVERSIONID(iocinfh, "$Id$") - /* * ANSI C includes * !!!!! MULTINET/VMS breaks if we include ANSI time.h here !!!!! @@ -205,25 +35,25 @@ HDRVERSIONID(iocinfh, "$Id$") #include #include -/* - * OS dependent includes - */ -#include "osiSock.h" -#include "os_depen.h" - -/* - * EPICS includes - */ #if defined(epicsExportSharedSymbols) #error suspect that libCom was not imported #endif +/* + * EPICS includes + */ #include "epicsAssert.h" #include "bucketLib.h" #include "ellLib.h" #include "envDefs.h" #include "epicsPrint.h" #include "tsStamp.h" +#include "osiSock.h" +#include "osiSem.h" +#include "osiThread.h" +#include "osiTimer.h" +#include "osiMutex.h" +#include "osiEvent.h" #if defined(epicsExportSharedSymbols) #error suspect that libCom was not imported @@ -241,25 +71,18 @@ HDRVERSIONID(iocinfh, "$Id$") #include "addrList.h" #include "caProto.h" #include "net_convert.h" +#include "ringBuffer.h" -#ifndef NULL -#define NULL 0 -#endif - #ifndef FALSE #define FALSE 0 +#elif FALSE +#error FALSE isnt boolean false #endif #ifndef TRUE #define TRUE 1 -#endif - -#ifndef OK -#define OK 0 -#endif - -#ifndef ERROR -#define ERROR (-1) +#elif !TRUE +#error TRUE isnt boolean true #endif #ifndef NELEMENTS @@ -270,13 +93,6 @@ HDRVERSIONID(iocinfh, "$Id$") #define LOCAL static #endif -/* - * writable channel_in_use and pending_event pointers - * for internal use - */ -typedef struct channel_in_use *ciu; -typedef struct pending_event *miu; - #ifndef min #define min(A,B) ((A)>(B)?(B):(A)) #endif @@ -285,48 +101,103 @@ typedef struct pending_event *miu; #define max(A,B) ((A)<(B)?(B):(A)) #endif -#define MSEC_PER_SEC 1000L -#define USEC_PER_SEC 1000000L +#define MSEC_PER_SEC 1000L +#define USEC_PER_SEC 1000000L + +#define LOCK(PCAC) semMutexMustTake ((PCAC)->ca_client_lock); +#define UNLOCK(PCAC) semMutexGive ((PCAC)->ca_client_lock); /* * catch when they use really large strings */ -#define STRING_LIMIT 512 - -#define INITCHK \ -if(!ca_static){ \ - int s; \ - s = ca_task_initialize(); \ - if(s != ECA_NORMAL){ \ - return s; \ - } \ -} +#define STRING_LIMIT 512 /* throw out requests prior to last ECA_TIMEOUT from ca_pend */ -#define VALID_MSG(PIIU) ((PIIU)->read_seq == (PIIU)->cur_read_seq) +#define VALID_MSG(PIIU) ((PIIU)->read_seq == (PIIU)->cur_read_seq) -#define SETPENDRECV {pndrecvcnt++;} -#define CLRPENDRECV {if(--pndrecvcnt<1u){POST_IO_EV;}} - -struct udpmsglog{ - long nbytes; - int valid; - struct sockaddr_in addr; +struct baseCIU { + const void *puser; + caCh *pConnFunc; + caArh *pAccessRightsFunc; + struct baseIIU *piiu; }; -struct putCvrtBuf{ - ELLNODE node; - unsigned long size; - void *pBuf; +struct nciu { + ELLNODE node; /* !! MUST be first !! */ + ELLLIST eventq; /* nmiu go here */ + baseCIU ciu; + unsigned long count; /* array element count */ + unsigned sid; /* server id */ + unsigned cid; /* client id */ + unsigned retry; /* search retry number */ + unsigned short retrySeqNo; /* search retry seq number */ + unsigned short nameLength; /* channel name length */ + unsigned short type; /* database field type */ + caar ar; /* access rights */ + unsigned claimPending:1; /* T if claim msg was sent */ + unsigned previousConn:1; /* T if connected in the past */ + unsigned connected:1; /* T if connected */ + /* + * channel name stored directly after this structure in a + * null terminated string. + */ +}; + +struct caPutNotify { + ELLNODE node; + void *dbPutNotify; + unsigned long valueSize; /* size of block pointed to by pValue */ + void (*caUserCallback) (struct event_handler_args); + void *caUserArg; + void *pValue; +}; + +struct lciu { + ELLNODE node; /* !! MUST be first !! */ + ELLLIST eventq; /* lmiu go here */ + baseCIU ciu; + caPutNotify *ppn; + pvId id; +}; + +struct baseMIU { + void (*usr_func) (struct event_handler_args args); + void *usr_arg; + chtype type; /* requested type for local CA */ + unsigned long count; /* requested count for local CA */ + baseCIU *pChan; +}; + +struct nmiu { + ELLNODE node; /* list ptrs */ + baseMIU miu; + ca_real p_delta; + ca_real n_delta; + ca_real timeout; + unsigned id; + unsigned short mask; + unsigned short cmd; +}; + +typedef void * dbEventSubscription; + +struct lmiu { + ELLNODE node; /* list ptrs */ + baseMIU miu; + dbEventSubscription es; +}; + +struct putCvrtBuf { + ELLNODE node; + unsigned long size; + void *pBuf; }; /* * for use with cac_select_io() */ -#define CA_DO_SENDS (1<<0) -#define CA_DO_RECVS (1<<1) - -typedef struct timeval ca_time; +#define CA_DO_SENDS (1<<0) +#define CA_DO_RECVS (1<<1) #define LD_CA_TIME(FLOAT_TIME,PCATIME) \ ((PCATIME)->tv_sec = (long) (FLOAT_TIME), \ @@ -338,14 +209,12 @@ typedef struct timeval ca_time; * these control the duration and period of name resolution * broadcasts */ -#define MAXCONNTRIES 100 /* N conn retries on unchanged net */ +#define MAXCONNTRIES 100 /* N conn retries on unchanged net */ -/* - * A prime number works best here (see comment in retrySearchRequest() - */ -#define INITIALTRIESPERFRAME 1u /* initial UDP frames per search try */ -#define MAXTRIESPERFRAME 64u /* max UDP frames per search try */ +#define INITIALTRIESPERFRAME 1u /* initial UDP frames per search try */ +#define MAXTRIESPERFRAME 64u /* max UDP frames per search try */ +#if 0 /* * NOTE: These must be larger than one vxWorks tick or we will end up * using the CPU. A vxWorks tick is usually 1/60th of a sec. The @@ -357,18 +226,27 @@ typedef struct timeval ca_time; * 113uS (one UDP read/write fd) * 185uS (two UDP read/write fd) * 3 uS are required to call FD_ZERO (&readMask) - * and FD_ZERO (&writeMask) - * 4 uS required to call cac_gettimeval() + * and FD_ZERO (&writeMask) + * 4 uS required to call tsStampGetCurrent () */ #ifdef iocCore -#define SELECT_POLL_SEARCH (0.075) /* units sec - polls for search request (4 ticks)*/ +#define SELECT_POLL_SEARCH (0.075) /* units sec - polls for search request (4 ticks)*/ #else -#define SELECT_POLL_SEARCH (0.025) /* units sec - polls for search request */ +#define SELECT_POLL_SEARCH (0.025) /* units sec - polls for search request */ +#endif +#define SELECT_POLL_NO_SEARCH (0.5) /* units sec - polls for conn heartbeat */ +#endif /* #if 0 */ + +#define CA_RECAST_DELAY 0.5 /* initial delay to next search (sec) */ +#define CA_RECAST_PORT_MASK 0xff /* additional random search interval from port */ +#define CA_RECAST_PERIOD 5.0 /* quiescent search period (sec) */ + +#if defined (CLOCKS_PER_SEC) +#define CAC_SIGNIFICANT_SELECT_DELAY (1.0 / CLOCKS_PER_SEC) +#else +/* on sunos4 GNU does not provide CLOCKS_PER_SEC */ +#define CAC_SIGNIFICANT_SELECT_DELAY (1.0 / 1000000u) #endif -#define SELECT_POLL_NO_SEARCH (0.5) /* units sec - polls for conn heartbeat */ -#define CA_RECAST_DELAY SELECT_POLL_SEARCH /* initial delay to next recast (sec) */ -#define CA_RECAST_PORT_MASK 0xff /* random retry interval off port */ -#define CA_RECAST_PERIOD (5.0) /* ul on retry period long term (sec) */ /* * these two control the period of connection verifies @@ -380,28 +258,16 @@ typedef struct timeval ca_time; * CA_CONN_VERIFY_PERIOD is normally obtained from an * EPICS environment variable. */ -#define CA_ECHO_TIMEOUT 5.0 /* (sec) disconn no echo reply tmo */ -#define CA_CONN_VERIFY_PERIOD 30.0 /* (sec) how often to request echo */ +#define CA_ECHO_TIMEOUT 5.0 /* (sec) disconn no echo reply tmo */ +#define CA_CONN_VERIFY_PERIOD 30.0 /* (sec) how often to request echo */ /* * only used when communicating with old servers */ -#define CA_RETRY_PERIOD 5 /* int sec to next keepalive */ +#define CA_RETRY_PERIOD 5 /* int sec to next keepalive */ -#define N_REPEATER_TRIES_PRIOR_TO_MSG 50 -#define REPEATER_TRY_PERIOD (1.0) - -#ifdef iocCore -typedef struct caclient_put_notify{ - ELLNODE node; - PUTNOTIFY dbPutNotify; - unsigned long valueSize; /* size of block pointed to by dbPutNotify */ - void (*caUserCallback)(struct event_handler_args); - void *caUserArg; - struct CA_STATIC *pcas; - int busy; -}CACLIENTPUTNOTIFY; -#endif /*iocCore*/ +#define N_REPEATER_TRIES_PRIOR_TO_MSG 50 +#define REPEATER_TRY_PERIOD (1.0) /* * this determines the number of messages received @@ -417,397 +283,338 @@ typedef struct caclient_put_notify{ /* * ! lock needs to be applied when an id is allocated ! */ -#define CLIENT_HASH_TBL_SIZE (1<<12) -#define CLIENT_ID_WIDTH 28 /* bits */ -#define CLIENT_ID_COUNT (1<ca_nextFastBucketId++) +#define CLIENT_SLOW_ID_ALLOC(PCAC) (CLIENT_ID_MASK&(PCAC)->ca_nextSlowBucketId++) -#define SEND_RETRY_COUNT_INIT 100 +#define SEND_RETRY_COUNT_INIT 100 -#define iiuList (ca_static->ca_iiuList) -#define piiuCast (ca_static->ca_piiuCast) -#define pndrecvcnt (ca_static->ca_pndrecvcnt) -#define ioeventlist (ca_static->ca_ioeventlist) -#define nxtiiu (ca_static->ca_nxtiiu) -#define pend_read_list (ca_static->ca_pend_read_list) -#define pend_write_list (ca_static->ca_pend_write_list) -#define fd_register_func\ - (ca_static->ca_fd_register_func) -#define fd_register_arg (ca_static->ca_fd_register_arg) -#define post_msg_active (ca_static->ca_post_msg_active) -#define sprintf_buf (ca_static->ca_sprintf_buf) -#define pSlowBucket (ca_static->ca_pSlowBucket) -#define pFastBucket (ca_static->ca_pFastBucket) -#define nextSlowBucketId (ca_static->ca_nextSlowBucketId) -#define nextFastBucketId (ca_static->ca_nextFastBucketId) +enum iiu_conn_state {iiu_connecting, iiu_connected, iiu_disconnected}; -#if defined(iocCore) -#define POLLDELAY .05 -# define io_done_sem (ca_static->ca_io_done_sem) -# define evuser (ca_static->ca_evuser) -# define client_lock (ca_static->ca_client_lock) -# define local_chidlist (ca_static->ca_local_chidlist) -# define lcl_buff_list (ca_static->ca_lcl_buff_list) -# define lock_tid (ca_static->ca_lock_tid) -# define lock_count (ca_static->ca_lock_count) -#endif +extern threadPrivateId cacRecursionLock; -/* - * Ring buffering for both sends and recvs - */ -struct ca_buffer{ - char buf[MAX_MSG_SIZE]; /* from iocmsg.h */ - unsigned long max_msg; - unsigned long rdix; - unsigned long wtix; - int readLast; +struct baseIIU { + struct cac *pcas; }; -#define CAC_RING_BUFFER_INIT(PRBUF, SIZE) \ - assert((SIZE)>sizeof((PRBUF)->buf)); \ - (PRBUF)->max_msg = (SIZE); \ - (PRBUF)->rdix = 0; \ - (PRBUF)->wtix = 0; \ - (PRBUF)->readLast = TRUE; +typedef void *dbEventCtx; -#define CAC_RING_BUFFER_READ_ADVANCE(PRBUF, DELTA) \ -( (PRBUF)->readLast = TRUE, \ - ((PRBUF)->rdix += (DELTA)) >= (PRBUF)->max_msg ? \ - (PRBUF)->rdix = 0 : \ - (PRBUF)->rdix \ -) +struct lclIIU { + ELLLIST chidList; + baseIIU iiu; + dbEventCtx evctx; + const pvAdapter *pva; + void *localSubscrFreeListPVT; + ELLLIST buffList; + ELLLIST putNotifyQue; + semMutexId putNotifyLock; +}; -#define CAC_RING_BUFFER_WRITE_ADVANCE(PRBUF, DELTA) \ -( (PRBUF)->readLast = FALSE, \ - ((PRBUF)->wtix += (DELTA)) >= (PRBUF)->max_msg ? \ - (PRBUF)->wtix = 0 : \ - (PRBUF)->wtix \ -) +struct netIIU { + ELLLIST chidList; + baseIIU iiu; + unsigned long curDataMax; + unsigned long curDataBytes; + caHdr curMsg; + void *pCurData; + unsigned curMsgBytes; +}; -#define TAG_CONN_DOWN(PIIU) \ -( \ -/* ca_printf("Tagging connection down at %d in %s\n", __LINE__, __FILE__), */ \ -(PIIU)->state = iiu_disconnected\ -) +struct udpiiu; -enum iiu_conn_state{iiu_connecting, iiu_connected, iiu_disconnected}; +class searchTimer : public osiTimer { -/* - * One per IOC - */ -typedef struct ioc_in_use{ - ELLNODE node; - ELLLIST chidlist; /* chans on this connection */ - ELLLIST destAddr; - ca_time timeAtLastRecv; - ca_time timeAtSendBlock; - ca_time timeAtEchoRequest; - unsigned long curDataMax; - unsigned long curDataBytes; - struct ca_buffer send; - struct ca_buffer recv; - caHdr curMsg; - struct CA_STATIC *pcas; - void *pCurData; - unsigned long (*sendBytes)(struct ioc_in_use *); - unsigned long (*recvBytes)(struct ioc_in_use *); - void (*procInput)(struct ioc_in_use *); - SOCKET sock_chan; - int sock_proto; - unsigned minor_version_number; - unsigned contiguous_msg_count; - unsigned curMsgBytes; - unsigned read_seq; - unsigned cur_read_seq; - unsigned minfreespace; - char host_name_str[32]; - unsigned char state; /* for use with iiu_conn_state enum */ +public: + searchTimer (udpiiu &iiu, osiTimerQueue &queue); + void notifySearchResponse (nciu *pChan); + void reset (double period); - /* - * bit fields placed together for better packing density - */ - unsigned client_busy:1; - unsigned echoPending:1; - unsigned sendPending:1; - unsigned claimsPending:1; - unsigned recvPending:1; - unsigned pushPending:1; - unsigned beaconAnomaly:1; -}IIU; +private: + virtual void expire (); + virtual void destroy (); + virtual bool again () const; + virtual double delay () const; + virtual void show (unsigned level) const; + virtual const char *name () const; + + void setRetryInterval (unsigned retryNo); + + udpiiu &iiu; + unsigned framesPerTry; /* # of UDP frames per search try */ + unsigned framesPerTryCongestThresh; /* one half N tries w congest */ + unsigned minRetry; /* min retry no so far */ + unsigned retry; + unsigned searchTries; /* num search tries within seq # */ + unsigned searchResponses; /* num valid search resp within seq # */ + unsigned short retrySeqNo; /* search retry seq number */ + unsigned short retrySeqNoAtListBegin; /* search retry seq number at beg of list */ + double period; /* period between tries */ +}; + +class repeaterSubscribeTimer : public osiTimer { +public: + repeaterSubscribeTimer (udpiiu &iiu, osiTimerQueue &queue); + +private: + virtual void expire (); + virtual void destroy (); + virtual bool again () const; + virtual double delay () const; + virtual void show (unsigned level) const; + virtual const char *name () const; + + udpiiu &iiu; +}; + +struct udpiiu { + udpiiu (cac *pcac); + ~udpiiu (); + + char xmitBuf[MAX_UDP]; + char recvBuf[ETHERNET_MAX_UDP]; + searchTimer searchTmr; + repeaterSubscribeTimer repeaterSubscribeTmr; + semMutexId xmitBufLock; + semBinaryId xmitSignal; + ELLLIST dest; + netIIU niiu; + SOCKET sock; + semBinaryId recvThreadExitSignal; + semBinaryId sendThreadExitSignal; + unsigned nBytesInXmitBuf; + unsigned repeaterTries; + unsigned short repeaterPort; + unsigned contactRepeater:1; + unsigned repeaterContacted:1; + + // exceptions + class noSocket {}; + class noMemory {}; +}; + +struct tcpiiu { + ELLNODE node; + ELLNODE recvActivityNode; + netIIU niiu; + char host_name_str[32]; + ringBuffer send; + ringBuffer recv; + osiSockAddr dest; + TS_STAMP timeAtSendBlock; + TS_STAMP timeAtEchoRequest; + TS_STAMP timeAtLastRecv; + struct beaconHashEntry *pBHE; + semBinaryId sendThreadExitSignal; + semBinaryId recvThreadExitSignal; + unsigned minor_version_number; + unsigned contiguous_msg_count; + unsigned read_seq; + unsigned cur_read_seq; + SOCKET sock; + unsigned char state; /* for use with iiu_conn_state enum */ + /* + * bit fields placed together for better packing density + */ + unsigned client_busy:1; + unsigned echoPending:1; + unsigned sendPending:1; + unsigned claimsPending:1; + unsigned recvPending:1; + unsigned pushPending:1; + unsigned beaconAnomaly:1; +}; /* * for the beacon's recvd hash table */ -#define BHT_INET_ADDR_MASK 0xff -typedef struct beaconHashEntry{ - struct beaconHashEntry *pNext; - IIU *piiu; - struct sockaddr_in inetAddr; - ca_time timeStamp; - ca_real averagePeriod; -}bhe; +#define BHT_INET_ADDR_MASK 0xff +typedef struct beaconHashEntry { + struct beaconHashEntry *pNext; + tcpiiu *piiu; + struct sockaddr_in inetAddr; + TS_STAMP timeStamp; + ca_real averagePeriod; +} bhe; + +class caErrorCode { +public: + caErrorCode (int status) : code (status) {}; +private: + int code; +}; /* * This struct allocated off of a free list * so that the select() ctx is thread safe */ -typedef struct { - ELLNODE node; - fd_set readMask; - fd_set writeMask; -}caFDInfo; +struct caFDInfo { + ELLNODE node; + fd_set readMask; + fd_set writeMask; +}; -typedef struct CA_STATIC { - ELLLIST ca_iiuList; - ELLLIST ca_ioeventlist; - ELLLIST ca_pend_read_list; - ELLLIST ca_pend_write_list; - ELLLIST activeCASG; - ELLLIST activeCASGOP; - ELLLIST putCvrtBuf; - ELLLIST fdInfoFreeList; - ELLLIST fdInfoList; - ca_time currentTime; - ca_time programBeginTime; - ca_time ca_conn_next_retry; - ca_time ca_conn_retry_delay; - ca_time ca_last_repeater_try; - ca_real ca_connectTMO; - IIU *ca_piiuCast; - void (*ca_exception_func) - (struct exception_handler_args); - const void *ca_exception_arg; - int (*ca_printf_func)(const char *pformat, va_list args); - void (*ca_fd_register_func) - (void *, int, int); - const void *ca_fd_register_arg; - char *ca_pUserName; - char *ca_pHostName; - BUCKET *ca_pSlowBucket; - BUCKET *ca_pFastBucket; - bhe *ca_beaconHash[BHT_INET_ADDR_MASK+1]; - void *ca_ioBlockFreeListPVT; - void *ca_sgFreeListPVT; - void *ca_sgopFreeListPVT; - ciu ca_pEndOfBCastList; - unsigned ca_search_responses; /* num valid search resp within seq # */ - unsigned ca_search_tries; /* num search tries within seq # */ - unsigned ca_search_tries_congest_thresh; /* one half N tries w congest */ - unsigned ca_search_retry; /* search retry seq number */ - unsigned ca_min_retry; /* min retry no so far */ - unsigned ca_frames_per_try; /* # of UDP frames per search try */ - unsigned ca_pndrecvcnt; - unsigned ca_nextSlowBucketId; - unsigned ca_nextFastBucketId; - unsigned ca_repeater_tries; - unsigned ca_number_iiu_in_fc; - unsigned short ca_server_port; - unsigned short ca_repeater_port; - unsigned short ca_search_retry_seq_no; /* search retry seq number */ - unsigned short ca_seq_no_at_list_begin; /* search retry seq number at beg of list*/ - char ca_sprintf_buf[256]; - char ca_new_err_code_msg_buf[128u]; - unsigned ca_post_msg_active:1; - unsigned ca_manage_conn_active:1; - unsigned ca_repeater_contacted:1; - unsigned ca_flush_pending:1; -#if defined(iocCore) - semId ca_io_done_sem; - semId ca_blockSem; - semId ca_client_lock; - semId ca_putNotifyLock; - ELLLIST ca_local_chidlist; - ELLLIST ca_lcl_buff_list; - ELLLIST ca_putNotifyQue; - ELLLIST ca_taskVarList; - void *ca_evuser; - void *ca_dbMonixFreeList; - threadId ca_lock_tid; - unsigned ca_lock_count; - threadId ca_tid; - threadId recv_tid; -#endif -}CA_STATIC; +class processThread : public osiThread { +public: + processThread (); + ~processThread (); + virtual void entryPoint (); + void signalShutDown (); + void installLabor (tcpiiu &iiu); + void removeLabor (tcpiiu &iiu); +private: + ELLLIST recvActivity; + osiEvent wakeup; + osiEvent exit; + osiMutex mutex; + bool shutDown; +}; +struct cac { + lclIIU localIIU; + osiTimerQueue timerQueue; + ELLLIST ca_iiuList; + ELLLIST activeCASG; + ELLLIST activeCASGOP; + ELLLIST putCvrtBuf; + ELLLIST fdInfoFreeList; + ELLLIST fdInfoList; + TS_STAMP currentTime; + TS_STAMP programBeginTime; + TS_STAMP ca_last_repeater_try; + ca_real ca_connectTMO; + udpiiu *pudpiiu; + void (*ca_exception_func) + (struct exception_handler_args); + void *ca_exception_arg; + int (*ca_printf_func) (const char *pformat, va_list args); + void (*ca_fd_register_func) (void *, int, int); + void *ca_fd_register_arg; + char *ca_pUserName; + char *ca_pHostName; + BUCKET *ca_pSlowBucket; + BUCKET *ca_pFastBucket; + bhe *ca_beaconHash[BHT_INET_ADDR_MASK+1]; + void *ca_ioBlockFreeListPVT; + void *ca_sgFreeListPVT; + void *ca_sgopFreeListPVT; + nciu *ca_pEndOfBCastList; + semBinaryId ca_io_done_sem; + semBinaryId ca_blockSem; + semMutexId ca_client_lock; + processThread procThread; + unsigned ca_pndrecvcnt; + unsigned ca_nextSlowBucketId; + unsigned ca_nextFastBucketId; + unsigned ca_number_iiu_in_fc; + unsigned short ca_server_port; + char ca_sprintf_buf[256]; + char ca_new_err_code_msg_buf[128u]; + unsigned ca_manage_conn_active:1; + unsigned ca_flush_pending:1; + ELLLIST ca_taskVarList; + + cac (); + cac::~cac (); +}; #define CASG_MAGIC 0xFAB4CAFE /* * one per outstanding op */ -typedef struct{ - ELLNODE node; - CA_SYNC_GID id; - void *pValue; - unsigned long magic; - unsigned long seqNo; -}CASGOP; - +struct CASGOP{ + ELLNODE node; + CA_SYNC_GID id; + void *pValue; + unsigned long magic; + unsigned long seqNo; + cac *pcac; +}; /* * one per synch group */ -typedef struct{ - ELLNODE node; - CA_SYNC_GID id; - unsigned long magic; - unsigned long opPendCount; - unsigned long seqNo; - /* - * Asynch Notification - */ -# ifdef iocCore - semId sem; -# endif /*iocCore*/ -}CASG; - - -/* - * GLOBAL VARIABLES - * There should only be one - add others to ca_static above -joh - */ - -#include "caOsDependent.h" -CA_OSD_CA_STATIC +struct CASG { + ELLNODE node; + CA_SYNC_GID id; + unsigned long magic; + unsigned long opPendCount; + unsigned long seqNo; + semBinaryId sem; +}; /* * CA internal functions - * */ +#if 0 +void cac_mux_io (cac *pcac, double maxDelay, unsigned iocCloseAllowed); +#endif /* #if 0 */ +int cac_search_msg (nciu *chix); +int ca_request_event (nciu *pChan, nmiu *monix); +int ca_busy_message (tcpiiu *piiu); +int ca_ready_message (tcpiiu *piiu); +void noop_msg (tcpiiu *piiu); +void echo_request (tcpiiu *piiu); +bool issue_claim_channel (nciu *pchan); +void issue_identify_client (tcpiiu *piiu); +void issue_client_host_name (tcpiiu *piiu); +int ca_defunct (void); +int ca_printf (struct cac *, const char *pformat, ...); +int ca_vPrintf (struct cac *, const char *pformat, va_list args); +void manage_conn (cac *pcac); +void checkConnWatchdogs (cac *pcac); +void mark_server_available (cac *pcac, const struct sockaddr_in *pnet_addr); +void flow_control_on (tcpiiu *piiu); +void flow_control_off (tcpiiu *piiu); +epicsShareFunc void epicsShareAPI ca_repeater (void); +void ca_sg_init (cac *pcac); +void ca_sg_shutdown (cac *pcac); +int cac_select_io (cac *pcac, double maxDelay, int flags); +int post_msg (netIIU *piiu, const struct sockaddr_in *pnet_addr, + char *pInBuf, unsigned long blockSize); -void cac_mux_io( - CA_STATIC *ca_static, - struct timeval *ptimeout, - unsigned iocCloseAllowed); +char *localHostName (void); -int repeater_installed(CA_STATIC *ca_static); -int search_msg( ciu chix, int reply_type); -int ca_request_event( evid monix); -int ca_busy_message(struct ioc_in_use *piiu); -int ca_ready_message(struct ioc_in_use *piiu); -void noop_msg(struct ioc_in_use *piiu); -int echo_request( struct ioc_in_use *piiu, ca_time *pCurrentTime); -int issue_claim_channel(chid pchan); -void issue_identify_client(struct ioc_in_use *piiu); -void issue_client_host_name(struct ioc_in_use *piiu); -int ca_defunct(void); -epicsShareFunc int epicsShareAPI ca_printf (const char *pformat, ...); -epicsShareFunc int epicsShareAPI ca_vPrintf (const char *pformat, va_list args); -void manage_conn(CA_STATIC *ca_static); -void checkConnWatchdogs(CA_STATIC *ca_static, unsigned closeAllowed); -void mark_server_available( - CA_STATIC *ca_static, - const struct sockaddr_in *pnet_addr); +int ca_os_independent_init (cac *pcac, const pvAdapter *ppva); -void flow_control_on(struct ioc_in_use *piiu); -void flow_control_off(struct ioc_in_use *piiu); -epicsShareFunc void epicsShareAPI ca_repeater(void); -#ifdef iocCore -typedef struct cac_recv_taskArgs { - CA_STATIC *pcas; - threadId tcas; -}cac_recv_taskArgs; -void cac_recv_task(cac_recv_taskArgs *args); -#endif -void ca_sg_init(CA_STATIC *ca_static); -void ca_sg_shutdown(CA_STATIC *ca_static); -int cac_select_io(CA_STATIC *ca_static, struct timeval *ptimeout, int flags); -int post_msg( - struct ioc_in_use *piiu, - const struct sockaddr_in *pnet_addr, - char *pInBuf, - unsigned long blockSize -); -int alloc_ioc( - CA_STATIC *ca_static, - const struct sockaddr_in *pina, - struct ioc_in_use **ppiiu -); -unsigned long cacRingBufferWrite( - struct ca_buffer *pRing, - const void *pBuf, - unsigned long nBytes); +void freeBeaconHash (struct cac *ca_temp); +void removeBeaconInetAddr (cac *pcac, + const struct sockaddr_in *pnet_addr); +bhe *lookupBeaconInetAddr(cac *pcac, + const struct sockaddr_in *pnet_addr); +bhe *createBeaconHashEntry (cac *pcac, + const struct sockaddr_in *pnet_addr, unsigned sawBeacon); -unsigned long cacRingBufferRead( - struct ca_buffer *pRing, - void *pBuf, - unsigned long nBytes); +void caIOBlockFree (cac *pcac, unsigned id); +void netChannelDestroy (cac *pcac, unsigned id); +void cacDisconnectChannel (nciu *chix); +void genLocalExcepWFL (cac *pcac, long stat, const char *ctx, + const char *pFile, unsigned line); +#define genLocalExcep(PCAC, STAT, PCTX) \ +genLocalExcepWFL (PCAC, STAT, PCTX, __FILE__, __LINE__) +void cac_reconnect_channel (tcpiiu *piiu, caResId id, unsigned short type, unsigned long count); +void addToChanList (nciu *chan, netIIU *piiu); +void removeFromChanList (nciu *chan); +void cacFlushAllIIU (cac *pcac); +double cac_fetch_poll_period (cac *pcac); +tcpiiu * constructTCPIIU (cac *pcac, const struct sockaddr_in *pina, + unsigned minorVersion); +void cac_destroy (cac *pcac); -unsigned long cacRingBufferWriteSize( - struct ca_buffer *pBuf, - int contiguous); +tcpiiu *iiuToTCPIIU (baseIIU *pIn); +netIIU *iiuToNIIU (baseIIU *pIn); +lclIIU *iiuToLIIU (baseIIU *pIn); +int fetchClientContext (cac **ppcac); +void caRepeaterThread (void *pDummy); +unsigned short caFetchPortConfig + (cac *pcac, const ENV_PARAM *pEnv, unsigned short defaultPort); -unsigned long cacRingBufferReadSize( - struct ca_buffer *pBuf, - int contiguous); - -void caIOBlockListFree( - CA_STATIC *ca_static, - ELLLIST *pList, - chid chan, - int cbRequired, - int status); - -char *localUserName(void); - -char *localHostName(void); - -int create_net_chan( -CA_STATIC *ca_static, -struct ioc_in_use **ppiiu, -const struct sockaddr_in *pina, /* only used by TCP connections */ -int net_proto -); - -void caSetupBCastAddrList ( ELLLIST *pList, SOCKET sock, unsigned short port); - -int ca_os_independent_init (CA_STATIC *ca_static); - -void freeBeaconHash(struct CA_STATIC *ca_temp); -void removeBeaconInetAddr( - CA_STATIC *ca_static, - const struct sockaddr_in *pnet_addr); - -bhe *lookupBeaconInetAddr( - CA_STATIC *ca_static, - const struct sockaddr_in *pnet_addr); - -bhe *createBeaconHashEntry( - CA_STATIC *ca_static, - const struct sockaddr_in *pnet_addr, - unsigned sawBeacon); - -void notify_ca_repeater(CA_STATIC *ca_static); - -void ca_process_input_queue(CA_STATIC *ca_static); -void cac_block_for_io_completion(CA_STATIC *ca_static,struct timeval *pTV); -void ca_process_exit(CA_STATIC *ca_static); -void ca_spawn_repeater(void); -void cac_gettimeval(struct timeval *pt); -/* returns A - B in floating secs */ -ca_real cac_time_diff(ca_time *pTVA, ca_time *pTVB); -/* returns A + B in integer secs & integer usec */ -ca_time cac_time_sum(ca_time *pTVA, ca_time *pTVB); -void caIOBlockFree(CA_STATIC *ca_static,miu pIOBlock); -void clearChannelResources(CA_STATIC *ca_static,unsigned id); -void caSetDefaultPrintfHandler (CA_STATIC *ca_static); -void cacDisconnectChannel( ciu chix); -int caSendMsgPending(CA_STATIC *ca_static); -void genLocalExcepWFL(long stat, const char *ctx, - const char *pFile, unsigned line); -#define genLocalExcep(STAT, PCTX) \ -genLocalExcepWFL (STAT, PCTX, __FILE__, __LINE__) -void cac_reconnect_channel(CA_STATIC *ca_static, caResId id, short type, unsigned short count); -void retryPendingClaims( IIU *piiu); -void cacSetRetryInterval(CA_STATIC *ca_static, unsigned retryNo); -void addToChanList(ciu chan, IIU *piiu); -void removeFromChanList(ciu chan); -void cac_create_udp_fd(CA_STATIC *ca_static); -double cac_fetch_poll_period(CA_STATIC *ca_static); -void cac_close_ioc ( IIU *piiu); +void initiateShutdownTCPIIU (tcpiiu *piiu); +void cacShutdownUDP (udpiiu &iiu); /* * !!KLUDGE!! diff --git a/src/ca/net_convert.h b/src/ca/net_convert.h index 6b730eb30..731354d27 100644 --- a/src/ca/net_convert.h +++ b/src/ca/net_convert.h @@ -47,6 +47,9 @@ extern "C" { #elif (defined(__ALPHA) && defined(VMS) || defined(__alpha)) && defined(VMS) # define CA_FLOAT_MIT # define CA_LITTLE_ENDIAN +#elif (defined(__ALPHA) && defined(__VMS) || defined(__alpha)) && defined(__VMS) +# define CA_FLOAT_MIT +# define CA_LITTLE_ENDIAN #elif (defined(__ALPHA) && defined(UNIX) || defined(__alpha)) && defined(UNIX) # define CA_FLOAT_IEEE # define CA_LITTLE_ENDIAN diff --git a/src/ca/os_depen.h b/src/ca/os_depen.h index f12ff5d38..8eebf9665 100644 --- a/src/ca/os_depen.h +++ b/src/ca/os_depen.h @@ -59,20 +59,7 @@ static char *os_depenhSccsId = "$Id$"; #error Please define one of iocCore, UNIX, VMS, or _WIN32 #endif -#if defined(iocCore) -# define POST_IO_EV semBinaryGive(io_done_sem) -# define VXTASKIDNONE 0 -# define LOCK semMutexMustTake(client_lock); -# define UNLOCK semMutexGive(client_lock); -# define EVENTLOCKTEST (lock_tid==threadGetIdSelf()) -# define VXTHISTASKID threadGetIdSelf(); -# define abort() threadSuspend(threadGetIdSelf()) -#else -# define POST_IO_EV -# define LOCK -# define UNLOCK -# define EVENTLOCKTEST (post_msg_active) -#endif /*defined(iocCore) */ + #endif /* INCos_depenh */ diff --git a/src/ca/processThread.cpp b/src/ca/processThread.cpp new file mode 100644 index 000000000..c1de4f4d3 --- /dev/null +++ b/src/ca/processThread.cpp @@ -0,0 +1,114 @@ + +/* + * $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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +processThread::processThread () : + osiThread ("CAC process", 0x1000, threadPriorityMedium), + shutDown (false) +{ + ellInit (&this->recvActivity); +} + +processThread::~processThread () +{ + this->shutDown = true; + this->exit.signal (); + while ( !this->exit.wait (5.0) ) { + printf ("processThread::~processThread (): Warning, thread object destroyed before thread exit \n"); + } +} + +void processThread::entryPoint () +{ + char *pNode; + tcpiiu *piiu; + + while (!this->shutDown) { + while ( 1 ) { + int status; + unsigned bytesToProcess; + + this->mutex.lock (); + pNode = (char *) ellGet (&this->recvActivity); + if (pNode) { + piiu = (tcpiiu *) (pNode - offsetof (tcpiiu, recvActivityNode) ); + piiu->recvPending = FALSE; + } + this->mutex.unlock (); + + if (!pNode) { + break; + } + + if ( piiu->state == iiu_connected ) { + char *pProto = (char *) cacRingBufferReadReserveNoBlock + (&piiu->recv, &bytesToProcess); + if (pProto) { + status = post_msg (&piiu->niiu, &piiu->dest.ia, + pProto, bytesToProcess); + if (status!=ECA_NORMAL) { + initiateShutdownTCPIIU (piiu); + } + cacRingBufferReadCommit (&piiu->recv, bytesToProcess); + cacRingBufferReadFlush (&piiu->recv); + } + } + } + + this->wakeup.wait (); + } + this->exit.signal (); +} + +void processThread::signalShutDown () +{ + this->shutDown = true; + this->wakeup.signal (); +} + +void processThread::installLabor (tcpiiu &iiu) +{ + bool addedIt; + + this->mutex.lock (); + if ( !iiu.recvPending ) { + iiu.recvPending = TRUE; + ellAdd (&this->recvActivity, &iiu.recvActivityNode); + addedIt = true; + } + else { + addedIt = false; + } + this->mutex.unlock (); + + // + // wakeup after unlock improves performance + // + if (addedIt) { + this->wakeup.signal (); + } +} + +void processThread::removeLabor (tcpiiu &iiu) +{ + this->mutex.lock (); + if (iiu.recvPending) { + ellDelete (&this->recvActivity, &iiu.recvActivityNode); + } + this->mutex.unlock (); +} \ No newline at end of file diff --git a/src/ca/repeater.c b/src/ca/repeater.c deleted file mode 100644 index beee54ede..000000000 --- a/src/ca/repeater.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * $Id$ - * - * REPEATER.C - * - * CA broadcast repeater - * - * Author: Jeff Hill - * Date: 3-27-90 - * - * Control System Software for the GTA Project - * - * Copyright 1988, 1989, the Regents of the University of California. - * - * This software was produced under a U.S. Government contract - * (W-7405-ENG-36) at the Los Alamos National Laboratory, which is - * operated by the University of California for the U.S. Department - * of Energy. - * - * Developed by the Controls and Automation Group (AT-8) - * Accelerator Technology Division - * Los Alamos National Laboratory - * - * Direct inqueries to: - * Jeff HIll, AT-8, Mail Stop H820 - * Los Alamos National Laboratory - * Los Alamos, New Mexico 87545 - * Phone: (505) 665-1831 - * E-mail: johill@lanl.gov - * - * PURPOSE: - * Broadcasts fan out over the LAN, but old IP kernels do not allow - * two processes on the same machine to get the same broadcast - * (and modern IP kernels do not allow two processes on the same machine - * to receive the same unicast). - * - * This code fans out UDP messages sent to the CA repeater port - * to all CA client processes that have subscribed. - * - * - * Modification Log: - * ----------------- - * .01 060691 joh Took out 4 byte count at message begin to - * in preparation for SPARC alignment - * .02 042892 joh No longer checking the status from free - * since it varies from OS to OS - * .03 042892 joh made local routines static - * .04 072392 joh set reuse addr socket option so that - * the repeater will restart if it gets killed - * .05 072392 joh no longer needs to loop waiting for the timeout - * to expire because of the change introduced - * in .04 - * .06 120492 joh removed unnecessary includes - * .07 120992 joh now uses dll list routines - * .08 102993 joh toggle set sock opt to set - * .09 070195 joh discover client has vanished by connecting its - * datagram socket (and watching for ECONNREFUSED) - * - * $Log$ - * Revision 1.48 1999/09/02 21:44:50 jhill - * improved the way that socket error numbers are converted to strings, - * changed () to (void) in func proto, and fixed missing parameter to - * checkConnWatchdogs() bug resulting from this - * - * Revision 1.47 1998/09/29 20:50:37 jhill - * more robust in situations wherelocal IP cant be determined - * - * Revision 1.46 1998/09/24 21:22:54 jhill - * conn.c - * - * Revision 1.45 1998/06/16 00:58:12 jhill - * attach to winsock when its a static build - * - * Revision 1.44 1998/05/29 00:03:20 jhill - * allow CA to run systems w/o local interface query capabilities (ie cygwin32) - * - * Revision 1.43 1998/04/15 21:58:29 jhill - * fixed the doc - * - * Revision 1.42 1998/02/27 01:04:03 jhill - * fixed benign WIN32 message from overwritten errno - * - * Revision 1.41 1998/02/05 22:34:33 jhill - * dont delete client if send returns ECONNREFUSED - * - * Revision 1.40 1997/08/04 23:37:15 jhill - * added beacon anomaly flag init/allow ip 255.255.255.255 - * - * Revision 1.39 1997/04/23 17:05:09 jhill - * pc port changes - * - * Revision 1.38 1996/11/02 00:51:04 jhill - * many pc port, const in API, and other changes - * - * Revision 1.37 1996/09/04 20:02:32 jhill - * fixed gcc warning - * - * Revision 1.36 1996/07/12 00:40:48 jhill - * fixed client disconnect problem under solaris - * - * Revision 1.32.6.1 1996/07/12 00:39:59 jhill - * fixed client disconnect problem under solaris - * - * Revision 1.35 1996/07/10 23:30:11 jhill - * fixed GNU warnings - * - * Revision 1.34 1996/06/19 17:59:24 jhill - * many 3.13 beta changes - * - * Revision 1.33 1995/11/29 19:19:05 jhill - * Added $log$ - * - */ - -/* - * It would be preferable to avoid using the repeater on multicast enhanced IP kernels, but - * this is not going to work in all situations because (according to Steven's TCP/IP - * illustrated volume I) if a broadcast is received it goes to all sockets on the same port, - * but if a unicast is received it goes to only one of the sockets on the same port - * (we can only guess at which one it will be). - * - * I have observed this behavior under winsock II: - * o only one of the sockets on the same port receives the message if we send to the - * loop back address - * o both of the sockets on the same port receives the message if we send to the - * broadcast address - * - */ - -static char *sccsId = "@(#)$Id$"; - -#include "iocinf.h" -#include "bsdSocketResource.h" - -/* - * one socket per client so we will get the ECONNREFUSED - * error code (and then delete the client) - */ -struct one_client{ - ELLNODE node; - struct sockaddr_in from; - SOCKET sock; -}; - -/* - * these can be external since there is only one instance - * per machine so we dont care about reentrancy - */ -static ELLLIST client_list; - -static char buf[ETHERNET_MAX_UDP]; - -#define PORT_ANY 0U -typedef struct { - SOCKET sock; - int errNumber; - const char *pErrStr; -}makeSocketReturn; - -LOCAL void register_new_client(struct sockaddr_in *pLocal, - struct sockaddr_in *pFrom); -LOCAL void verifyClients(); -LOCAL makeSocketReturn makeSocket (unsigned short port, int reuseAddr); -LOCAL void fanOut(struct sockaddr_in *pFrom, const char *pMsg, unsigned msgSize); - - -/* - * - * ca_repeater() - * - * - */ -void epicsShareAPI ca_repeater() -{ - int status; - int size; - SOCKET sock; - struct sockaddr_in from; - struct sockaddr_in local; - int from_size = sizeof from; - unsigned short port; - makeSocketReturn msr; - - assert (bsdSockAttach()); - - port = caFetchPortConfig( - &EPICS_CA_REPEATER_PORT, - CA_REPEATER_PORT); - - ellInit(&client_list); - - msr = makeSocket(port, TRUE); - if (msr.sock==INVALID_SOCKET) { - /* - * test for server was already started - */ - if (msr.errNumber==SOCK_EADDRINUSE) { - bsdSockRelease(); - exit(0); - } - ca_printf("%s: Unable to create repeater socket because %d=\"%s\" - fatal\n", - __FILE__, - msr.errNumber, - msr.pErrStr); - bsdSockRelease(); - exit(0); - } - - sock = msr.sock; - - status = local_addr (sock, &local); - if(status != OK){ - /* - * use the loop back address to communicate with the CA repeater - * if this os does not have interface query capabilities - * - * this will only work with 3.13 beta 12 CA clients or later - */ - local.sin_family = AF_INET; - local.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - local.sin_port = htons (0); - } - -#ifdef DEBUG - ca_printf("CA Repeater: Attached and initialized\n"); -#endif - - while(TRUE){ - caHdr *pMsg; - - size = recvfrom( - sock, - buf, - sizeof(buf), - 0, - (struct sockaddr *)&from, - &from_size); - - if(size < 0){ - int errnoCpy = SOCKERRNO; -# ifdef linux - /* - * Avoid spurious ECONNREFUSED bug - * in linux - */ - if (errnoCpy==SOCK_ECONNREFUSED) { - continue; - } -# endif - ca_printf("CA Repeater: unexpected UDP recv err: %s\n", - SOCKERRSTR(errnoCpy)); - continue; - } - - pMsg = (caHdr *) buf; - - /* - * both zero length message and a registration message - * will register a new client - */ - if( ((size_t)size) >= sizeof(*pMsg)){ - if(ntohs(pMsg->m_cmmd) == REPEATER_REGISTER){ - register_new_client(&local, &from); - - /* - * strip register client message - */ - pMsg++; - size -= sizeof(*pMsg); - if (size==0) { - continue; - } - } - } - else if(size == 0){ - register_new_client(&local, &from); - continue; - } - - fanOut(&from, (char *) pMsg, size); - } -} - - -/* - * fanOut() - */ -LOCAL void fanOut(struct sockaddr_in *pFrom, const char *pMsg, unsigned msgSize) -{ - ELLLIST theClients; - struct one_client *pclient; - int status; - int verify = FALSE; - - ellInit(&theClients); - while ( (pclient=(struct one_client *)ellGet(&client_list)) ) { - ellAdd(&theClients, &pclient->node); - - /* - * Dont reflect back to sender - */ - if(pFrom->sin_port == pclient->from.sin_port && - pFrom->sin_addr.s_addr == pclient->from.sin_addr.s_addr){ - continue; - } - - status = send( - pclient->sock, - (char *)pMsg, - msgSize, - 0); - if (status>=0) { -#ifdef DEBUG - ca_printf ("Sent to %d\n", - ntohs (pclient->from.sin_port)); -#endif - } - if(status < 0){ - int errnoCpy = SOCKERRNO; - if (errnoCpy == SOCK_ECONNREFUSED) { -#ifdef DEBUG - ca_printf ("Deleted client %d\n", - ntohs (pclient->from.sin_port)); -#endif - verify = TRUE; - } - else { - ca_printf( -"CA Repeater: UDP fan out err was \"%s\"\n", - SOCKERRSTR(errnoCpy)); - } - } - } - ellConcat(&client_list, &theClients); - - if (verify) { - verifyClients (); - } -} - - -/* - * verifyClients() - * (this is required because solaris has a half baked version of sockets) - */ -LOCAL void verifyClients() -{ - ELLLIST theClients; - struct one_client *pclient; - makeSocketReturn msr; - - ellInit(&theClients); - while ( (pclient=(struct one_client *)ellGet(&client_list)) ) { - ellAdd(&theClients, &pclient->node); - - msr = makeSocket(ntohs(pclient->from.sin_port), FALSE); - if (msr.sock!=INVALID_SOCKET) { -#ifdef DEBUG - ca_printf("Deleted client %d\n", - ntohs(pclient->from.sin_port)); -#endif - ellDelete(&theClients, &pclient->node); - socket_close(msr.sock); - socket_close(pclient->sock); - free(pclient); - } - else { - /* - * win sock does not set SOCKERRNO when this fails - */ - if (msr.errNumber!=SOCK_EADDRINUSE) { - ca_printf( - "CA Repeater: bind test err was %d=\"%s\"\n", - msr.errNumber, msr.pErrStr); - } - } - } - ellConcat(&client_list, &theClients); -} - - -/* - * makeSocket() - */ -LOCAL makeSocketReturn makeSocket(unsigned short port, int reuseAddr) -{ - int status; - struct sockaddr_in bd; - makeSocketReturn msr; - int true = 1; - - msr.sock = socket( AF_INET, /* domain */ - SOCK_DGRAM, /* type */ - 0); /* deflt proto */ - if (msr.sock == INVALID_SOCKET) { - msr.errNumber = SOCKERRNO; - msr.pErrStr = SOCKERRSTR(msr.errNumber); - return msr; - } - - /* - * no need to bind if unconstrained - */ - if (port != PORT_ANY) { - - memset((char *)&bd, 0, sizeof(bd)); - bd.sin_family = AF_INET; - bd.sin_addr.s_addr = htonl(INADDR_ANY); - bd.sin_port = htons(port); - status = bind(msr.sock, (struct sockaddr *)&bd, (int)sizeof(bd)); - if (status<0) { - msr.errNumber = SOCKERRNO; - msr.pErrStr = SOCKERRSTR(msr.errNumber); - socket_close(msr.sock); - msr.sock = INVALID_SOCKET; - return msr; - } - if (reuseAddr) { - status = setsockopt( - msr.sock, - SOL_SOCKET, - SO_REUSEADDR, - (char *)&true, - sizeof(true)); - if (status<0) { - int errnoCpy = SOCKERRNO; - ca_printf( - "%s: set socket option failed because \"%s\"\n", - __FILE__, SOCKERRSTR(errnoCpy)); - } - } - } - - msr.errNumber = 0; - msr.pErrStr = "no error"; - return msr; -} - - -/* - * register_new_client() - */ -LOCAL void register_new_client( -struct sockaddr_in *pLocal, -struct sockaddr_in *pFrom) -{ - int status; - struct one_client *pclient; - caHdr confirm; - caHdr noop; - int newClient = FALSE; - makeSocketReturn msr; - - if (pFrom->sin_family != AF_INET) { - return; - } - - /* - * the repeater and its clients must be on the same host - */ - if (htonl(INADDR_LOOPBACK) != pFrom->sin_addr.s_addr) { - - /* - * Unfortunately on 3.13 beta 11 and before the - * repeater would not always allow the loopback address - * as a local client address so all clients must continue to - * use the address from the first non-loopback interface - * found to communicate with the CA repeater until all - * CA repeaters have been restarted. - */ - if (pLocal->sin_addr.s_addr != pFrom->sin_addr.s_addr) { - return; - } - } - - for(pclient = (struct one_client *) ellFirst(&client_list); - pclient; pclient = (struct one_client *) ellNext(&pclient->node)){ - - if (pFrom->sin_port == pclient->from.sin_port) { - break; - } - } - - if (!pclient) { - pclient = (struct one_client *)calloc (1, sizeof(*pclient)); - if (!pclient) { - ca_printf("%s: no memory for new client\n", - __FILE__); - return; - } - - msr = makeSocket(PORT_ANY, FALSE); - if (msr.sock==INVALID_SOCKET) { - free(pclient); - ca_printf("%s: no client sock because %d=\"%s\"\n", - __FILE__, - msr.errNumber, - msr.pErrStr); - return; - } - - pclient->sock = msr.sock; - - status = connect(pclient->sock, - (struct sockaddr *)pFrom, - sizeof(*pFrom)); - if (status<0) { - int errnoCpy = SOCKERRNO; - ca_printf( - "%s: unable to connect client sock because \"%s\"\n", - __FILE__, SOCKERRSTR(errnoCpy)); - socket_close(pclient->sock); - free(pclient); - return; - } - - pclient->from = *pFrom; - - ellAdd (&client_list, &pclient->node); - newClient = TRUE; -#ifdef DEBUG - ca_printf ( - "Added %d\n", - ntohs(pFrom->sin_port)); -#endif - } - - memset((char *)&confirm, '\0', sizeof(confirm)); - confirm.m_cmmd = htons(REPEATER_CONFIRM); - confirm.m_available = pFrom->sin_addr.s_addr; - status = send( - pclient->sock, - (char *)&confirm, - sizeof(confirm), - 0); - if (status >= 0) { - assert(status == sizeof(confirm)); - } - else if (SOCKERRNO == SOCK_ECONNREFUSED){ -#ifdef DEBUG - ca_printf("Deleted repeater client=%d sending ack\n", - ntohs(pFrom->sin_port)); -#endif - ellDelete(&client_list, &pclient->node); - socket_close(pclient->sock); - free(pclient); - } - else { - ca_printf("CA Repeater: confirm err was \"%s\"\n", - SOCKERRSTR(SOCKERRNO)); - } - - /* - * send a noop message to all other clients so that we dont - * accumulate sockets when there are no beacons - */ - memset((char *)&noop, '\0', sizeof(noop)); - confirm.m_cmmd = htons(CA_PROTO_NOOP); - fanOut(pFrom, (char *)&noop, sizeof(noop)); - - if (newClient) { - /* - * on solaris we need to verify that the clients - * have not gone away (because ICMP does not - * get through to send() - * - * this is done each time that a new client is - * created - * - * this is done here in order to avoid deleting - * a client prior to sending its confirm message - */ - verifyClients(); - } -} - diff --git a/src/ca/repeater.cpp b/src/ca/repeater.cpp new file mode 100644 index 000000000..3c6830160 --- /dev/null +++ b/src/ca/repeater.cpp @@ -0,0 +1,490 @@ +/* + * $Id$ + * + * REPEATER.C + * + * CA broadcast repeater + * + * Author: Jeff Hill + * Date: 3-27-90 + * + * Control System Software for the GTA Project + * + * Copyright 1988, 1989, the Regents of the University of California. + * + * This software was produced under a U.S. Government contract + * (W-7405-ENG-36) at the Los Alamos National Laboratory, which is + * operated by the University of California for the U.S. Department + * of Energy. + * + * Developed by the Controls and Automation Group (AT-8) + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Direct inqueries to: + * Jeff HIll, AT-8, Mail Stop H820 + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * Phone: (505) 665-1831 + * E-mail: johill@lanl.gov + * + * PURPOSE: + * Broadcasts fan out over the LAN, but old IP kernels do not allow + * two processes on the same machine to get the same broadcast + * (and modern IP kernels do not allow two processes on the same machine + * to receive the same unicast). + * + * This code fans out UDP messages sent to the CA repeater port + * to all CA client processes that have subscribed. + * + */ + +/* + * It would be preferable to avoid using the repeater on multicast enhanced IP kernels, but + * this is not going to work in all situations because (according to Steven's TCP/IP + * illustrated volume I) if a broadcast is received it goes to all sockets on the same port, + * but if a unicast is received it goes to only one of the sockets on the same port + * (we can only guess at which one it will be). + * + * I have observed this behavior under winsock II: + * o only one of the sockets on the same port receives the message if we send to the + * loop back address + * o both of the sockets on the same port receives the message if we send to the + * broadcast address + * + */ + +#include "iocinf.h" +#include "taskwd.h" + +/* + * one socket per client so we will get the ECONNREFUSED + * error code (and then delete the client) + */ +struct one_client { + ELLNODE node; + struct sockaddr_in from; + SOCKET sock; +}; + +/* + * these can be external since there is only one instance + * per machine so we dont care about reentrancy + */ +static ELLLIST client_list; + +static char buf[ETHERNET_MAX_UDP]; + +static const unsigned short PORT_ANY = 0u; + +typedef struct { + SOCKET sock; + int errNumber; + const char *pErrStr; +} makeSocketReturn; + +/* + * makeSocket() + */ +LOCAL makeSocketReturn makeSocket (unsigned short port, int reuseAddr) +{ + int status; + struct sockaddr_in bd; + makeSocketReturn msr; + int flag; + + msr.sock = socket (AF_INET, SOCK_DGRAM, 0); + if (msr.sock == INVALID_SOCKET) { + msr.errNumber = SOCKERRNO; + msr.pErrStr = SOCKERRSTR (msr.errNumber); + return msr; + } + + /* + * no need to bind if unconstrained + */ + if (port != PORT_ANY) { + + memset ( (char *) &bd, 0, sizeof (bd) ); + bd.sin_family = AF_INET; + bd.sin_addr.s_addr = htonl (INADDR_ANY); + bd.sin_port = htons (port); + status = bind (msr.sock, (struct sockaddr *)&bd, (int)sizeof(bd)); + if (status<0) { + msr.errNumber = SOCKERRNO; + msr.pErrStr = SOCKERRSTR (msr.errNumber); + socket_close (msr.sock); + msr.sock = INVALID_SOCKET; + return msr; + } + if (reuseAddr) { + flag = TRUE; + status = setsockopt ( msr.sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&flag, sizeof (flag) ); + if (status<0) { + int errnoCpy = SOCKERRNO; + errlogPrintf( + "%s: set socket option failed because \"%s\"\n", + __FILE__, SOCKERRSTR(errnoCpy)); + } + } + } + + msr.errNumber = 0; + msr.pErrStr = "no error"; + return msr; +} + +/* + * verifyClients() + * (this is required because solaris has a half baked version of sockets) + */ +LOCAL void verifyClients() +{ + ELLLIST theClients; + struct one_client *pclient; + makeSocketReturn msr; + + ellInit(&theClients); + while ( (pclient=(struct one_client *)ellGet(&client_list)) ) { + ellAdd (&theClients, &pclient->node); + + msr = makeSocket ( ntohs (pclient->from.sin_port), FALSE ); + if ( msr.sock != INVALID_SOCKET ) { +#ifdef DEBUG + errlogPrintf ("Deleted client %d\n", + ntohs (pclient->from.sin_port) ); +#endif + ellDelete (&theClients, &pclient->node); + socket_close (msr.sock); + socket_close (pclient->sock); + free (pclient); + } + else { + /* + * win sock does not set SOCKERRNO when this fails + */ + if ( msr.errNumber != SOCK_EADDRINUSE ) { + errlogPrintf ( + "CA Repeater: bind test err was %d=\"%s\"\n", + msr.errNumber, msr.pErrStr); + } + } + } + ellConcat (&client_list, &theClients); +} + +/* + * fanOut() + */ +LOCAL void fanOut (struct sockaddr_in *pFrom, const char *pMsg, unsigned msgSize) +{ + ELLLIST theClients; + struct one_client *pclient; + int status; + int verify = FALSE; + + ellInit(&theClients); + while ( ( pclient = (struct one_client *) ellGet (&client_list) ) ) { + ellAdd(&theClients, &pclient->node); + + /* + * Dont reflect back to sender + */ + if(pFrom->sin_port == pclient->from.sin_port && + pFrom->sin_addr.s_addr == pclient->from.sin_addr.s_addr){ + continue; + } + + status = send ( pclient->sock, (char *)pMsg, msgSize, 0); + if (status>=0) { +#ifdef DEBUG + errlogPrintf ("Sent to %d\n", + ntohs (pclient->from.sin_port)); +#endif + } + if(status < 0){ + int errnoCpy = SOCKERRNO; + if (errnoCpy == SOCK_ECONNREFUSED) { +#ifdef DEBUG + errlogPrintf ("Deleted client %d\n", + ntohs (pclient->from.sin_port)); +#endif + verify = TRUE; + } + else { + errlogPrintf( +"CA Repeater: UDP fan out err was \"%s\"\n", + SOCKERRSTR(errnoCpy)); + } + } + } + ellConcat(&client_list, &theClients); + + if (verify) { + verifyClients (); + } +} + +/* + * register_new_client() + */ +LOCAL void register_new_client (struct sockaddr_in *pFrom) +{ + int status; + struct one_client *pclient; + caHdr confirm; + caHdr noop; + int newClient = FALSE; + makeSocketReturn msr; + + if (pFrom->sin_family != AF_INET) { + return; + } + + /* + * the repeater and its clients must be on the same host + */ + if (htonl(INADDR_LOOPBACK) != pFrom->sin_addr.s_addr) { + static SOCKET testSock = INVALID_SOCKET; + static int init; + struct sockaddr_in ina; + + if (!init) { + msr = makeSocket (PORT_ANY, TRUE); + if ( msr.sock == INVALID_SOCKET ) { + errlogPrintf("%s: Unable to create repeater bind test socket because %d=\"%s\"\n", + __FILE__, msr.errNumber, msr.pErrStr); + } + else { + testSock = msr.sock; + } + init = TRUE; + } + + /* + * Unfortunately on 3.13 beta 11 and before the + * repeater would not always allow the loopback address + * as a local client address so current clients alternate + * between the address of the first non-loopback interface + * found and the loopback addresss when subscribing with + * the CA repeater until all CA repeaters have been updated + * to current code. + */ + if ( testSock != INVALID_SOCKET ) { + ina = *pFrom; + ina.sin_port = PORT_ANY; + + /* we can only bind to a local address */ + status = bind ( testSock, (struct sockaddr *)&ina, (int) sizeof(ina) ); + if (status) { + return; + } + } + else { + return; + } + } + + for (pclient = (struct one_client *) ellFirst (&client_list); + pclient; pclient = (struct one_client *) ellNext (&pclient->node)){ + + if (pFrom->sin_port == pclient->from.sin_port) { + break; + } + } + + if (!pclient) { + pclient = (struct one_client *) calloc (1, sizeof(*pclient)); + if (!pclient) { + errlogPrintf ("%s: no memory for new client\n", __FILE__); + return; + } + + msr = makeSocket (PORT_ANY, FALSE); + if (msr.sock==INVALID_SOCKET) { + free(pclient); + errlogPrintf ("%s: no client sock because %d=\"%s\"\n", + __FILE__, msr.errNumber, msr.pErrStr); + return; + } + + pclient->sock = msr.sock; + + status = connect ( pclient->sock, + (struct sockaddr *) pFrom, + sizeof (*pFrom) ); + if (status<0) { + int errnoCpy = SOCKERRNO; + errlogPrintf ( + "%s: unable to connect client sock because \"%s\"\n", + __FILE__, SOCKERRSTR(errnoCpy)); + socket_close (pclient->sock); + free (pclient); + return; + } + + pclient->from = *pFrom; + + ellAdd (&client_list, &pclient->node); + newClient = TRUE; +#ifdef DEBUG + errlogPrintf ( "Added %d\n", ntohs (pFrom->sin_port) ); +#endif + } + + memset ( (char *) &confirm, '\0', sizeof (confirm) ); + confirm.m_cmmd = htons (REPEATER_CONFIRM); + confirm.m_available = pFrom->sin_addr.s_addr; + status = send( + pclient->sock, + (char *)&confirm, + sizeof(confirm), + 0); + if (status >= 0) { + assert(status == sizeof(confirm)); + } + else if (SOCKERRNO == SOCK_ECONNREFUSED){ +#ifdef DEBUG + errlogPrintf ("Deleted repeater client=%d sending ack\n", + ntohs (pFrom->sin_port) ); +#endif + ellDelete (&client_list, &pclient->node); + socket_close (pclient->sock); + free (pclient); + } + else { + errlogPrintf ("CA Repeater: confirm err was \"%s\"\n", + SOCKERRSTR (SOCKERRNO) ); + } + + /* + * send a noop message to all other clients so that we dont + * accumulate sockets when there are no beacons + */ + memset ( (char *) &noop, '\0', sizeof (noop) ); + confirm.m_cmmd = htons (CA_PROTO_NOOP); + fanOut ( pFrom, (char *)&noop, sizeof (noop) ); + + if (newClient) { + /* + * on solaris we need to verify that the clients + * have not gone away (because ICMP does not + * get through to send() + * + * this is done each time that a new client is + * created + * + * this is done here in order to avoid deleting + * a client prior to sending its confirm message + */ + verifyClients (); + } +} + + +/* + * ca_repeater() + */ +void epicsShareAPI ca_repeater () +{ + int size; + SOCKET sock; + struct sockaddr_in from; + int from_size = sizeof from; + unsigned short port; + makeSocketReturn msr; + + assert (bsdSockAttach()); + + port = caFetchPortConfig ( NULL, &EPICS_CA_REPEATER_PORT, CA_REPEATER_PORT ); + + ellInit(&client_list); + + msr = makeSocket (port, TRUE); + if ( msr.sock == INVALID_SOCKET ) { + /* + * test for server was already started + */ + if ( msr.errNumber == SOCK_EADDRINUSE ) { + bsdSockRelease (); + exit (0); + } + errlogPrintf("%s: Unable to create repeater socket because %d=\"%s\" - fatal\n", + __FILE__, msr.errNumber, msr.pErrStr); + bsdSockRelease (); + exit(0); + } + + sock = msr.sock; + +#ifdef DEBUG + errlogPrintf ("CA Repeater: Attached and initialized\n"); +#endif + + while (TRUE) { + caHdr *pMsg; + + size = recvfrom( + sock, + buf, + sizeof(buf), + 0, + (struct sockaddr *)&from, + &from_size); + + if(size < 0){ + int errnoCpy = SOCKERRNO; +# ifdef linux + /* + * Avoid spurious ECONNREFUSED bug + * in linux + */ + if (errnoCpy==SOCK_ECONNREFUSED) { + continue; + } +# endif + errlogPrintf ("CA Repeater: unexpected UDP recv err: %s\n", + SOCKERRSTR(errnoCpy)); + continue; + } + + pMsg = (caHdr *) buf; + + /* + * both zero length message and a registration message + * will register a new client + */ + if ( ( (size_t) size) >= sizeof (*pMsg) ) { + if ( ntohs(pMsg->m_cmmd) == REPEATER_REGISTER ) { + register_new_client (&from); + + /* + * strip register client message + */ + pMsg++; + size -= sizeof (*pMsg); + if (size==0) { + continue; + } + } + } + else if (size == 0) { + register_new_client (&from); + continue; + } + + fanOut (&from, (char *) pMsg, size); + } +} + +/* + * caRepeaterThread () + */ +void caRepeaterThread (void *pDummy) +{ + taskwdInsert (threadGetIdSelf(), NULL, NULL); + ca_repeater(); +} + + diff --git a/src/ca/repeaterSubscribeTimer.cpp b/src/ca/repeaterSubscribeTimer.cpp new file mode 100644 index 000000000..626cd3e25 --- /dev/null +++ b/src/ca/repeaterSubscribeTimer.cpp @@ -0,0 +1,55 @@ + +/* + * $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" + +repeaterSubscribeTimer::repeaterSubscribeTimer (udpiiu &iiuIn, osiTimerQueue &queueIn) : + osiTimer (queueIn), iiu (iiuIn) +{ +} + +void repeaterSubscribeTimer::expire () +{ + this->iiu.contactRepeater = 1u; + semBinaryGive (this->iiu.xmitSignal); +} + +void repeaterSubscribeTimer::destroy () +{ +} + +bool repeaterSubscribeTimer::again () const +{ + if (this->iiu.repeaterContacted) { + return false; + } + else { + return true; + } +} + +double repeaterSubscribeTimer::delay () const +{ + return REPEATER_TRY_PERIOD; +} + +void repeaterSubscribeTimer::show (unsigned level) const +{ +} + +const char *repeaterSubscribeTimer::name () const +{ + return "repeaterSubscribeTimer"; +} + diff --git a/src/ca/ringBuffer.cpp b/src/ca/ringBuffer.cpp new file mode 100644 index 000000000..73112bb3e --- /dev/null +++ b/src/ca/ringBuffer.cpp @@ -0,0 +1,461 @@ + +/* + * $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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +#include "ringBuffer.h" + +static const unsigned ringIndexMask = nElementsInRing-1; + +/* + * cacRingBufferConstruct () + */ +int cacRingBufferConstruct (ringBuffer *pBuf) +{ + pBuf->shutDown = 0u; + pBuf->rdix = 0u; + pBuf->wtix = 1u; + + pBuf->readSignal = semBinaryCreate (semEmpty); + if (!pBuf->readSignal) { + return -1; + } + + pBuf->writeSignal = semBinaryCreate (semEmpty); + if (!pBuf->writeSignal) { + semBinaryDestroy (pBuf->readSignal); + return -1; + } + + pBuf->readLock = semMutexCreate (); + if (!pBuf->readLock) { + semBinaryDestroy (pBuf->readSignal); + semBinaryDestroy (pBuf->writeSignal); + return -1; + } + + pBuf->writeLock = semMutexCreate (); + if (!pBuf->writeLock) { + semBinaryDestroy (pBuf->readSignal); + semBinaryDestroy (pBuf->writeSignal); + semMutexDestroy (pBuf->readLock); + return -1; + } + + return 0; +} + +/* + * cacRingBufferDestroy () + */ +void cacRingBufferDestroy (ringBuffer *pBuf) +{ + /* + * force any read/write/reserve/commit ops in + * other threads to complete + */ + pBuf->shutDown = 1u; + semBinaryGive (pBuf->readSignal); + semBinaryGive (pBuf->writeSignal); + semMutexMustTake (pBuf->readLock); + semMutexMustTake (pBuf->writeLock); + + /* + * clean up + */ + semBinaryDestroy (pBuf->readSignal); + semBinaryDestroy (pBuf->writeSignal); + semMutexDestroy (pBuf->readLock); + semMutexDestroy (pBuf->writeLock); +} + +/* + * cacRingBufferShutDown (); + */ +void cacRingBufferShutDown (ringBuffer *pBuf) +{ + pBuf->shutDown = 1u; + semBinaryGive (pBuf->readSignal); + semBinaryGive (pBuf->writeSignal); +} + +/* + * cacRingBufferReadSize () + */ +static inline unsigned cacRingBufferReadSize (ringBuffer *pBuf) +{ + unsigned long count; + + if ( pBuf->wtix <= pBuf->rdix ) { + static const unsigned bufSizeM1 = sizeof (pBuf->buf) - 1u; + count = ( bufSizeM1 - pBuf->rdix ) + pBuf->wtix; + } + else { + count = (pBuf->wtix - pBuf->rdix) - 1u; + } + return count; +} + +/* + * cacRingBufferContiguousReadSize () + */ +static inline unsigned cacRingBufferContiguousReadSize (ringBuffer *pBuf) +{ + static const unsigned bufSizeM1 = sizeof (pBuf->buf) - 1u; + unsigned long count; + + if ( pBuf->wtix <= pBuf->rdix ) { + if (pBuf->rdix==bufSizeM1) { + count = pBuf->wtix; + } + else { + count = bufSizeM1 - pBuf->rdix; + } + } + else { + count = (pBuf->wtix - pBuf->rdix) - 1u; + } + return count; +} + +/* + * cacRingBufferWriteSize () + */ +static inline unsigned cacRingBufferWriteSize (ringBuffer *pBuf) +{ + unsigned long count; + + if (pBuf->wtix <= pBuf->rdix) { + count = pBuf->rdix - pBuf->wtix; + } + else { + count = ( sizeof (pBuf->buf) - pBuf->wtix ) + pBuf->rdix; + } + return count; +} + +/* + * cacRingBufferContiguousWriteSize () + */ +static inline unsigned cacRingBufferContiguousWriteSize (ringBuffer *pBuf) +{ + unsigned long count; + + if (pBuf->wtix <= pBuf->rdix) { + count = pBuf->rdix - pBuf->wtix; + } + else { + count = sizeof (pBuf->buf) - pBuf->wtix; + } + return count; +} + + +/* + * cacRingBufferReadPartial () + * + * returns the number of bytes read which may be less than + * the number requested. + */ +static inline unsigned cacRingBufferReadPartial (ringBuffer *pRing, void *pBuf, + unsigned nBytes) +{ + unsigned totalBytes; + + if ( pRing->wtix < pRing->rdix ) { + static const unsigned bufSizeM1 = sizeof (pRing->buf) - 1u; + unsigned nBytesAvail1stBlock, nBytesAvail2ndBlock; + + nBytesAvail1stBlock = bufSizeM1 - pRing->rdix; + nBytesAvail2ndBlock = pRing->wtix; + if ( nBytesAvail1stBlock >= nBytes ) { + totalBytes = nBytes; + memcpy ( pBuf, pRing->buf + pRing->rdix + 1u, totalBytes ); + } + else { + char *pChar = (char *) pBuf; + memcpy ( pBuf, pRing->buf + pRing->rdix + 1u, nBytesAvail1stBlock ); + nBytes -= nBytesAvail1stBlock; + if ( nBytesAvail2ndBlock > nBytes ) { + nBytesAvail2ndBlock = nBytes; + } + memcpy ( pChar + nBytesAvail1stBlock, pRing->buf, nBytesAvail2ndBlock); + totalBytes = nBytesAvail1stBlock + nBytesAvail2ndBlock; + } + pRing->rdix += totalBytes; + pRing->rdix &= ringIndexMask; + } + else if ( pRing->wtix > pRing->rdix ) { + totalBytes = (pRing->wtix - pRing->rdix) - 1; + if ( totalBytes > nBytes ) { + totalBytes = nBytes; + } + memcpy (pBuf, pRing->buf+pRing->rdix+1, totalBytes); + pRing->rdix += totalBytes; + pRing->rdix &= ringIndexMask; + } + else { + totalBytes = 0; + } + + return totalBytes; +} + +/* + * cacRingBufferRead () + * + * returns the number of bytes read which may be less than + * the number requested. + */ +unsigned cacRingBufferRead (ringBuffer *pRing, void *pBuf, + unsigned nBytes) +{ + unsigned char *pBufTmp = (unsigned char *) pBuf; + unsigned totalBytes = 0; + unsigned curBytes; + + semMutexMustTake (pRing->readLock); + + while (totalBytesreadSignal); + if (pRing->shutDown) { + semMutexGive (pRing->readLock); + return totalBytes; + } + } + else { + totalBytes += curBytes; + } + } + + semMutexGive (pRing->readLock); + + return totalBytes; +} + +/* + * cacRingBufferWritePartial () + * + * returns the number of bytes written which may be less than + * the number requested. + */ +static inline unsigned cacRingBufferWritePartial (ringBuffer *pRing, + const void *pBuf, unsigned nBytes) +{ + unsigned totalBytes; + + if ( pRing->wtix < pRing->rdix ) { + totalBytes = pRing->rdix - pRing->wtix; + if ( totalBytes > nBytes ) { + totalBytes = nBytes; + } + memcpy (pRing->buf+pRing->wtix, pBuf, totalBytes); + pRing->wtix += totalBytes; + pRing->wtix &= ringIndexMask; + } + else if ( pRing->wtix > pRing->rdix ) { + unsigned nBytesAvail1stBlock, nBytesAvail2ndBlock; + + nBytesAvail1stBlock = sizeof (pRing->buf) - pRing->wtix; + nBytesAvail2ndBlock = pRing->rdix; + if ( nBytesAvail1stBlock >= nBytes ) { + totalBytes = nBytes; + memcpy ( pRing->buf+pRing->wtix, pBuf, totalBytes ); + } + else { + char *pChar = (char *) pBuf; + memcpy ( pRing->buf+pRing->wtix, pBuf, nBytesAvail1stBlock ); + nBytes -= nBytesAvail1stBlock; + if ( nBytesAvail2ndBlock > nBytes ) { + nBytesAvail2ndBlock = nBytes; + } + memcpy (pRing->buf, pChar+nBytesAvail1stBlock, + nBytesAvail2ndBlock); + totalBytes = nBytesAvail2ndBlock + nBytesAvail1stBlock; + } + pRing->wtix += totalBytes; + pRing->wtix &= ringIndexMask; + } + else { + totalBytes = 0; + } + + return totalBytes; +} + +/* + * cacRingBufferWrite () + * + * returns the number of bytes written which may be less than + * the number requested. + */ +unsigned cacRingBufferWrite (ringBuffer *pRing, const void *pBuf, + unsigned nBytes) +{ + unsigned char *pBufTmp = (unsigned char *) pBuf; + unsigned totalBytes = 0; + unsigned curBytes; + + semMutexMustTake (pRing->writeLock); + + while (totalBytesreadSignal); + semBinaryMustTake (pRing->writeSignal); + if (pRing->shutDown) { + semMutexGive (pRing->writeLock); + return totalBytes; + } + } + else { + totalBytes += curBytes; + } + } + + semMutexGive (pRing->writeLock); + + return totalBytes; +} + +bool cacRingBufferWriteLockNoBlock (ringBuffer *pBuf, unsigned bytesRequired) +{ + semMutexMustTake (pBuf->writeLock); + + if (cacRingBufferWriteSize (pBuf)writeLock); + return false; + } + return true; +} + +void cacRingBufferWriteUnlock (ringBuffer *pBuf) +{ + semMutexGive (pBuf->writeLock); +} + +void *cacRingBufferWriteReserve (ringBuffer *pRing, unsigned *pBytesAvail) +{ + unsigned avail; + + semMutexMustTake (pRing->writeLock); + + avail = cacRingBufferContiguousWriteSize (pRing); + while (avail==0) { + semBinaryGive (pRing->readSignal); + semBinaryMustTake (pRing->writeSignal); + if (pRing->shutDown) { + semMutexGive (pRing->writeLock); + *pBytesAvail = 0u; + return 0; + } + avail = cacRingBufferContiguousWriteSize (pRing); + } + + *pBytesAvail = avail; + + return (void *) &pRing->buf[pRing->wtix]; +} + +void *cacRingBufferWriteReserveNoBlock (ringBuffer *pRing, unsigned *pBytesAvail) +{ + unsigned avail; + + semMutexMustTake (pRing->writeLock); + + avail = cacRingBufferContiguousWriteSize (pRing); + + if ( avail==0 || pRing->shutDown ) { + *pBytesAvail = 0u; + semMutexGive (pRing->writeLock); + return NULL; + } + + *pBytesAvail = avail; + + return (void *) &pRing->buf[pRing->wtix]; +} + +void cacRingBufferWriteCommit (ringBuffer *pRing, unsigned delta) +{ + pRing->wtix += delta; + pRing->wtix &= ringIndexMask; + semMutexGive (pRing->writeLock); +} + +void *cacRingBufferReadReserve (ringBuffer *pRing, unsigned *pBytesAvail) +{ + unsigned avail; + + semMutexMustTake (pRing->readLock); + + avail = cacRingBufferContiguousReadSize (pRing); + while (avail==0) { + semBinaryMustTake (pRing->readSignal); + if (pRing->shutDown) { + semMutexGive (pRing->readLock); + *pBytesAvail = 0u; + return NULL; + } + avail = cacRingBufferContiguousReadSize (pRing); + } + + *pBytesAvail = avail; + + return (void *) &pRing->buf[(pRing->rdix+1) & ringIndexMask]; +} + +void *cacRingBufferReadReserveNoBlock (ringBuffer *pRing, unsigned *pBytesAvail) +{ + unsigned avail; + + semMutexMustTake (pRing->readLock); + + avail = cacRingBufferContiguousReadSize (pRing); + + if ( avail==0 || pRing->shutDown ) { + *pBytesAvail = 0u; + semMutexGive (pRing->readLock); + return NULL; + } + + *pBytesAvail = avail; + + return (void *) &pRing->buf[(pRing->rdix+1) & ringIndexMask]; +} + + +void cacRingBufferReadCommit (ringBuffer *pRing, unsigned delta) +{ + pRing->rdix += delta; + pRing->rdix &= ringIndexMask; + semMutexGive (pRing->readLock); +} + +void cacRingBufferWriteFlush (ringBuffer *pRing) +{ + semBinaryGive (pRing->readSignal); +} + +void cacRingBufferReadFlush (ringBuffer *pRing) +{ + semBinaryGive (pRing->writeSignal); +} diff --git a/src/ca/ringBuffer.h b/src/ca/ringBuffer.h new file mode 100644 index 000000000..63da879be --- /dev/null +++ b/src/ca/ringBuffer.h @@ -0,0 +1,66 @@ + + +/* + * $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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef ringBufferh +#define ringBufferh + +#include "osiSem.h" + +#define nBitsRingIndex 14 +#define nElementsInRing (1<iiu.niiu.iiu.pcas); + this->retry = 0; + this->period = period; + UNLOCK (this->iiu.niiu.iiu.pcas); + + if (this->timeRemaining()>period) { + this->reschedule (0.0); + } +} + +/* + * searchTimer::setRetryInterval () + */ +void searchTimer::setRetryInterval (unsigned retryNo) +{ + unsigned idelay; + double delay; + + LOCK (this->iiu.niiu.iiu.pcas); + + /* + * set the retry number + */ + this->retry = min (retryNo, MAXCONNTRIES+1u); + + /* + * set the retry interval + */ + idelay = 1u << min (this->retry, CHAR_BIT*sizeof(idelay)-1u); + delay = idelay * CA_RECAST_DELAY; /* sec */ + /* + * place upper limit on the retry delay + */ + this->period = min (CA_RECAST_PERIOD, delay); + + UNLOCK (this->iiu.niiu.iiu.pcas); + +#ifdef DEBUG + printf ("new CA search period is %f sec\n", this->period); +#endif +} + +// +// searchTimer::notifySearchResponse () +// +// Reset the delay to the next search request if we get +// at least one response. However, dont reset this delay if we +// get a delayed response to an old search request. +// +void searchTimer::notifySearchResponse (nciu *pChan) +{ + LOCK (this->iiu.niiu.iiu.pcas); + + if ( this->retrySeqNoAtListBegin <= pChan->retrySeqNo ) { + if ( this->searchResponses < ULONG_MAX ) { + this->searchResponses++; + } + } + + UNLOCK (this->iiu.niiu.iiu.pcas); + + if (pChan->retrySeqNo == this->retrySeqNo) { + this->reschedule (0.0); + } +} + + +// +// searchTimer::expire () +// +void searchTimer::expire () +{ + nciu *chan; + nciu *firstChan; + int status; + unsigned nSent=0u; + + /* + * check to see if there is nothing to do here + */ + if (ellCount(&this->iiu.niiu.chidList)==0) { + return; + } + + LOCK (this->iiu.niiu.iiu.pcas); + + /* + * increment the retry sequence number + */ + this->retrySeqNo++; /* allowed to roll over */ + + /* + * dynamically adjust the number of UDP frames per + * try depending how many search requests are not + * replied to + * + * This determines how many search request can be + * sent together (at the same instant in time). + * + * The variable this->framesPerTry + * determines the number of UDP frames to be sent + * each time that retrySearchRequest() is called. + * If this value is too high we will waste some + * network bandwidth. If it is too low we will + * use very little of the incoming UDP message + * buffer associated with the server's port and + * will therefore take longer to connect. We + * initialize this->framesPerTry + * to a prime number so that it is less likely that the + * same channel is in the last UDP frame + * sent every time that this is called (and + * potentially discarded by a CA server with + * a small UDP input queue). + */ + /* + * increase frames per try only if we see better than + * a 93.75% success rate for one pass through the list + */ + if (this->searchResponses > + (this->searchTries-(this->searchTries/16u)) ) { + /* + * increase UDP frames per try if we have a good score + */ + if ( this->framesPerTry < MAXTRIESPERFRAME ) { + /* + * a congestion avoidance threshold similar to TCP is now used + */ + if ( this->framesPerTry < this->framesPerTryCongestThresh ) { + this->framesPerTry += this->framesPerTry; + } + else { + this->framesPerTry += (this->framesPerTry/8) + 1; + } +#if 0 + printf ("Increasing frame count to %u t=%u r=%u\n", + this->framesPerTry, this->searchTries, + this->searchResponses); +#endif + } + } + /* + * if we detect congestion because we have less than a 87.5% success + * rate then gradually reduce the frames per try + */ + else if ( this->searchResponses < + (this->searchTries-(this->searchTries/8u)) ) { + if (this->framesPerTry>1) { + this->framesPerTry--; + } + this->framesPerTryCongestThresh = this->framesPerTry/2 + 1; +#if 0 + printf ("Congestion detected - set frames per try to %u t=%u r=%u\n", + this->framesPerTry, this->searchTries, + this->searchResponses); +#endif + } + + /* + * a successful cac_search_msg() sends channel to + * the end of the list + */ + firstChan = chan = (nciu *) ellFirst (&this->iiu.niiu.chidList); + while (chan) { + + this->minRetry = min (this->minRetry, chan->retry); + + /* + * clear counter when we reach the end of the list + * + * if we are making some progress then + * dont increase the delay between search + * requests + */ + if ( this->iiu.niiu.iiu.pcas->ca_pEndOfBCastList == chan ) { + if ( this->searchResponses == 0u ) { +#if 0 + printf ("increasing search try interval\n"); +#endif + this->setRetryInterval (this->minRetry + 1u); + } + + this->minRetry = UINT_MAX; + + /* + * increment the retry sequence number + * (this prevents the time of the next search + * try from being set to the current time if + * we are handling a response from an old + * search message) + */ + this->retrySeqNo++; /* allowed to roll over */ + + /* + * so that old search tries will not update the counters + */ + this->retrySeqNoAtListBegin = this->retrySeqNo; + + /* + * reset the search try/response counters at the end of the list + * (sequence number) so that we dont overflow, but dont subtract + * out tries that dont have a matching response yet in case they + * are delayed + */ + if ( this->searchTries > this->searchResponses ) { + this->searchTries -= this->searchResponses; + } + else { + this->searchTries = 0; + } + this->searchResponses = 0; + +#if 0 + printf ("saw end of list\n"); +#endif + } + + /* + * this moves the channel to the end of the + * list (if successful) + */ + status = cac_search_msg (chan); + if (status != ECA_NORMAL) { + nSent++; + + if (nSent>=this->framesPerTry) { + break; + } + + /* flush out the search request buffer */ + semBinaryGive (this->iiu.xmitSignal); + + /* try again */ + status = cac_search_msg (chan); + if (status != ECA_NORMAL) { + break; + } + } + + if (this->searchTriessearchTries++; + } + + chan->retrySeqNo = this->retrySeqNo; + chan = (nciu *) ellFirst (&this->iiu.niiu.chidList); + + /* + * dont send any of the channels twice within one try + */ + if (chan==firstChan) { + /* + * add one to nSent because there may be + * one more partial frame to be sent + */ + nSent++; + + /* + * cap this->framesPerTry to + * the number of frames required for all of + * the unresolved channels + */ + if (this->framesPerTry>nSent) { + this->framesPerTry = nSent; + } + + break; + } + } + + UNLOCK (this->iiu.niiu.iiu.pcas); + + /* flush out the search request buffer */ + semBinaryGive (this->iiu.xmitSignal); + +#ifdef DEBUG + printf ("sent %u delay sec=%f\n", nSent, this->period); +#endif + +} + +void searchTimer::destroy () +{ +} + +bool searchTimer::again () const +{ + if (ellCount(&this->iiu.niiu.chidList)==0) { + return false; + } + else { + if (this->retry < MAXCONNTRIES) { + return true; + } + else { + return false; + } + } +} + +double searchTimer::delay () const +{ + return this->period; +} + +void searchTimer::show (unsigned level) const +{ +} + +const char *searchTimer::name () const +{ + return "CAC Search Timer"; +} diff --git a/src/ca/service.c b/src/ca/service.cpp similarity index 51% rename from src/ca/service.c rename to src/ca/service.cpp index ebe5e6dcd..a4eb3d8ed 100644 --- a/src/ca/service.c +++ b/src/ca/service.cpp @@ -14,30 +14,35 @@ #include "iocinf.h" #include "net_convert.h" -#include "bsdSocketResource.h" #ifdef CONVERSION_REQUIRED extern CACVRTFUNC *cac_dbr_cvrt[]; #endif /*CONVERSION_REQUIRED*/ -typedef void (*pProtoStub) ( IIU *piiu, const struct sockaddr_in *pnet_addr); +typedef void (*pProtoStubTCP) (tcpiiu *piiu, const struct sockaddr_in *pnet_addr); +typedef void (*pProtoStubUDP) (udpiiu *piiu, const struct sockaddr_in *pnet_addr); /* - * noop_action () + * tcp_noop_action () */ -LOCAL void noop_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void tcp_noop_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { return; } +/* + * udp_noop_action () + */ +LOCAL void udp_noop_action (udpiiu *piiu, const struct sockaddr_in *pnet_addr) +{ + return; +} + + /* * echo_resp_action () */ -LOCAL void echo_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void echo_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { piiu->echoPending = FALSE; piiu->beaconAnomaly = FALSE; @@ -47,22 +52,19 @@ const struct sockaddr_in *pnet_addr) /* * write_notify_resp_action () */ -LOCAL void write_notify_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void write_notify_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; struct event_handler_args args; - miu monix; + nmiu *monix; /* * run the user's event handler */ - LOCK; - monix = (miu) bucketLookupItemUnsignedId (pFastBucket, - &piiu->curMsg.m_available); - if(!monix){ - UNLOCK; + LOCK (piiu->niiu.iiu.pcas); + monix = (nmiu *) bucketLookupItemUnsignedId (piiu->niiu.iiu.pcas->ca_pFastBucket, + &piiu->niiu.curMsg.m_available); + if (!monix) { + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -71,11 +73,11 @@ const struct sockaddr_in *pnet_addr) * call handler, only if they did not clear the * chid in the interim */ - if (monix->usr_func) { - args.usr = (void *) monix->usr_arg; - args.chid = monix->chan; - args.type = monix->type; - args.count = monix->count; + if (monix->miu.usr_func) { + args.usr = (void *) monix->miu.usr_arg; + args.chid = monix->miu.pChan; + args.type = monix->miu.type; + args.count = monix->miu.count; args.dbr = NULL; /* * the channel id field is abused for @@ -83,13 +85,12 @@ const struct sockaddr_in *pnet_addr) * * write notify was added vo CA V4.1 */ - args.status = ntohl(piiu->curMsg.m_cid); + args.status = ntohl (piiu->niiu.curMsg.m_cid); - (*monix->usr_func) (args); + (*monix->miu.usr_func) (args); } - ellDelete (&pend_write_list, &monix->node); - caIOBlockFree (ca_static, monix); - UNLOCK; + caIOBlockFree (piiu->niiu.iiu.pcas, monix->id); + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -97,23 +98,20 @@ const struct sockaddr_in *pnet_addr) /* * read_notify_resp_action () */ -LOCAL void read_notify_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void read_notify_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; - miu monix; + nmiu *monix; struct event_handler_args args; /* * run the user's event handler */ - LOCK; - monix = (miu) bucketLookupItemUnsignedId( - pFastBucket, - &piiu->curMsg.m_available); + LOCK (piiu->niiu.iiu.pcas); + monix = (nmiu *) bucketLookupItemUnsignedId( + piiu->niiu.iiu.pcas->ca_pFastBucket, + &piiu->niiu.curMsg.m_available); if(!monix){ - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -121,7 +119,7 @@ const struct sockaddr_in *pnet_addr) * call handler, only if they did not clear the * chid in the interim */ - if (monix->usr_func) { + if (monix->miu.usr_func) { int v41; /* @@ -129,23 +127,23 @@ const struct sockaddr_in *pnet_addr) * format to host format */ # ifdef CONVERSION_REQUIRED - if (piiu->curMsg.m_dataTypecurMsg.m_dataType])( - piiu->pCurData, - piiu->pCurData, + if (piiu->niiu.curMsg.m_dataTypeniiu.curMsg.m_dataType])( + piiu->niiu.pCurData, + piiu->niiu.pCurData, FALSE, - piiu->curMsg.m_count); + piiu->niiu.curMsg.m_count); } else { - piiu->curMsg.m_cid = htonl(ECA_BADTYPE); + piiu->niiu.curMsg.m_cid = htonl(ECA_BADTYPE); } # endif - args.usr = (void *) monix->usr_arg; - args.chid = monix->chan; - args.type = piiu->curMsg.m_dataType; - args.count = piiu->curMsg.m_count; - args.dbr = piiu->pCurData; + args.usr = (void *)monix->miu.usr_arg; + args.chid = monix->miu.pChan; + args.type = piiu->niiu.curMsg.m_dataType; + args.count = piiu->niiu.curMsg.m_count; + args.dbr = piiu->niiu.pCurData; /* * the channel id field is abused for * read notify status starting @@ -155,17 +153,16 @@ const struct sockaddr_in *pnet_addr) CA_PROTOCOL_VERSION, piiu->minor_version_number); if(v41){ - args.status = ntohl(piiu->curMsg.m_cid); + args.status = ntohl(piiu->niiu.curMsg.m_cid); } else{ args.status = ECA_NORMAL; } - (*monix->usr_func) (args); + (*monix->miu.usr_func) (args); } - ellDelete (&pend_read_list, &monix->node); - caIOBlockFree (ca_static, monix); - UNLOCK; + caIOBlockFree (piiu->niiu.iiu.pcas, monix->id); + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -173,40 +170,37 @@ const struct sockaddr_in *pnet_addr) /* * event_resp_action () */ -LOCAL void event_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void event_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; struct event_handler_args args; - miu monix; + nmiu *monix; int v41; /* * run the user's event handler */ - LOCK; - monix = (miu) bucketLookupItemUnsignedId( - pFastBucket, &piiu->curMsg.m_available); + LOCK (piiu->niiu.iiu.pcas); + monix = (nmiu *) bucketLookupItemUnsignedId( + piiu->niiu.iiu.pcas->ca_pFastBucket, &piiu->niiu.curMsg.m_available); if (!monix) { - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); return; } /* - * m_postsize = 0 is a confirmation of a - * monitor cancel + * 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) { - ellDelete(&monix->chan->eventq, &monix->node); - caIOBlockFree(ca_static, monix); - UNLOCK; + if (!piiu->niiu.curMsg.m_postsize) { + caIOBlockFree (piiu->niiu.iiu.pcas, monix->id); + UNLOCK (piiu->niiu.iiu.pcas); return; } /* only call if not disabled */ - if (!monix->usr_func) { - UNLOCK; + if (!monix->miu.usr_func) { + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -215,15 +209,15 @@ const struct sockaddr_in *pnet_addr) * format to host format */ # ifdef CONVERSION_REQUIRED - if (piiu->curMsg.m_dataTypecurMsg.m_dataType])( - piiu->pCurData, - piiu->pCurData, + if (piiu->niiu.curMsg.m_dataTypeniiu.curMsg.m_dataType])( + piiu->niiu.pCurData, + piiu->niiu.pCurData, FALSE, - piiu->curMsg.m_count); + piiu->niiu.curMsg.m_count); } else { - piiu->curMsg.m_cid = htonl(ECA_BADTYPE); + piiu->niiu.curMsg.m_cid = htonl(ECA_BADTYPE); } # endif @@ -234,11 +228,11 @@ const struct sockaddr_in *pnet_addr) * structure rather than the structure itself * early on. */ - args.usr = (void *) monix->usr_arg; - args.chid = monix->chan; - args.type = piiu->curMsg.m_dataType; - args.count = piiu->curMsg.m_count; - args.dbr = piiu->pCurData; + args.usr = (void *) monix->miu.usr_arg; + args.chid = monix->miu.pChan; + args.type = piiu->niiu.curMsg.m_dataType; + args.count = piiu->niiu.curMsg.m_count; + args.dbr = piiu->niiu.pCurData; /* * the channel id field is abused for * event status starting @@ -248,15 +242,15 @@ const struct sockaddr_in *pnet_addr) CA_PROTOCOL_VERSION, piiu->minor_version_number); if(v41){ - args.status = ntohl(piiu->curMsg.m_cid); + args.status = ntohl(piiu->niiu.curMsg.m_cid); } else{ args.status = ECA_NORMAL; } /* call their handler */ - (*monix->usr_func) (args); - UNLOCK; + (*monix->miu.usr_func) (args); + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -264,22 +258,18 @@ const struct sockaddr_in *pnet_addr) /* * read_resp_action () */ -LOCAL void read_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void read_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; - miu pIOBlock; + nmiu *pIOBlock; /* * verify the event id */ - LOCK; - pIOBlock = (miu) bucketLookupItemUnsignedId( - pFastBucket, - &piiu->curMsg.m_available); - if(!pIOBlock){ - UNLOCK; + LOCK (piiu->niiu.iiu.pcas); + pIOBlock = (nmiu *) bucketLookupItemUnsignedId (piiu->niiu.iiu.pcas->ca_pFastBucket, + &piiu->niiu.curMsg.m_available); + if (!pIOBlock) { + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -292,38 +282,39 @@ const struct sockaddr_in *pnet_addr) * convert the data buffer from net * format to host format */ - if (piiu->curMsg.m_dataType <= (unsigned) LAST_BUFFER_TYPE) { + if (piiu->niiu.curMsg.m_dataType <= (unsigned) LAST_BUFFER_TYPE) { # ifdef CONVERSION_REQUIRED - (*cac_dbr_cvrt[piiu->curMsg.m_dataType])( - piiu->pCurData, - (void *) pIOBlock->usr_arg, + (*cac_dbr_cvrt[piiu->niiu.curMsg.m_dataType])( + piiu->niiu.pCurData, + pIOBlock->miu.usr_arg, FALSE, - piiu->curMsg.m_count); + piiu->niiu.curMsg.m_count); # else - if (piiu->curMsg.m_dataType == DBR_STRING && - piiu->curMsg.m_count == 1u) { + if (piiu->niiu.curMsg.m_dataType == DBR_STRING && + piiu->niiu.curMsg.m_count == 1u) { strcpy ((char *)pIOBlock->usr_arg, - piiu->pCurData); + piiu->niiu.pCurData); } else { memcpy( (char *)pIOBlock->usr_arg, - piiu->pCurData, + piiu->niiu.pCurData, dbr_size_n ( - piiu->curMsg.m_dataType, - piiu->curMsg.m_count) + piiu->niiu.curMsg.m_dataType, + piiu->niiu.curMsg.m_count) ); } # endif /* * decrement the outstanding IO count */ - CLRPENDRECV; + if (--piiu->niiu.iiu.pcas->ca_pndrecvcnt==0u) { + semBinaryGive (piiu->niiu.iiu.pcas->ca_io_done_sem); + } } } - ellDelete (&pend_read_list, &pIOBlock->node); - caIOBlockFree (ca_static, pIOBlock); - UNLOCK; + caIOBlockFree (piiu->niiu.iiu.pcas, pIOBlock->id); + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -331,16 +322,12 @@ const struct sockaddr_in *pnet_addr) /* * search_resp_action () */ -LOCAL void search_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void search_resp_action (udpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; struct sockaddr_in ina; char rej[64]; - ciu chan; - int status; - IIU *allocpiiu; + nciu *chan; + tcpiiu *allocpiiu; unsigned short *pMinorVersion; unsigned minorVersion; @@ -349,26 +336,11 @@ const struct sockaddr_in *pnet_addr) * * lock required around use of the sprintf buffer */ - LOCK; - chan = (ciu) bucketLookupItemUnsignedId( - pSlowBucket, - &piiu->curMsg.m_available); - if(!chan){ - UNLOCK; - return; - } - - if(!chan->piiu){ - ca_printf("cast reply to local channel??\n"); - UNLOCK; - return; - } - - /* - * Ignore search replies to closing channels - */ - if(chan->state == cs_closed) { - UNLOCK; + LOCK (piiu->niiu.iiu.pcas); + chan = (nciu *) bucketLookupItemUnsignedId (piiu->niiu.iiu.pcas->ca_pSlowBucket, + &piiu->niiu.curMsg.m_available); + if (!chan) { + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -377,9 +349,9 @@ const struct sockaddr_in *pnet_addr) * is appended to the end of each search reply. * This value is ignored by earlier clients. */ - if(piiu->curMsg.m_postsize >= sizeof(*pMinorVersion)){ - pMinorVersion = (unsigned short *)(piiu->pCurData); - minorVersion = ntohs(*pMinorVersion); + if(piiu->niiu.curMsg.m_postsize >= sizeof(*pMinorVersion)){ + pMinorVersion = (unsigned short *)(piiu->niiu.pCurData); + minorVersion = ntohs(*pMinorVersion); } else{ minorVersion = CA_UKN_MINOR_VERSION; @@ -391,105 +363,68 @@ const struct sockaddr_in *pnet_addr) */ ina.sin_family = AF_INET; if (CA_V48 (CA_PROTOCOL_VERSION,minorVersion)) { - if (piiu->curMsg.m_cid != INADDR_BROADCAST) { + if (piiu->niiu.curMsg.m_cid != INADDR_BROADCAST) { /* * Leave address in network byte order (m_cid has not been * converted to the local byte order) */ - ina.sin_addr.s_addr = piiu->curMsg.m_cid; + ina.sin_addr.s_addr = piiu->niiu.curMsg.m_cid; } else { ina.sin_addr = pnet_addr->sin_addr; } - ina.sin_port = htons (piiu->curMsg.m_dataType); + ina.sin_port = htons (piiu->niiu.curMsg.m_dataType); } else if (CA_V45 (CA_PROTOCOL_VERSION,minorVersion)) { - ina.sin_port = htons(piiu->curMsg.m_dataType); + ina.sin_port = htons(piiu->niiu.curMsg.m_dataType); ina.sin_addr = pnet_addr->sin_addr; } else { - ina.sin_port = htons(ca_static->ca_server_port); + ina.sin_port = htons(piiu->niiu.iiu.pcas->ca_server_port); ina.sin_addr = pnet_addr->sin_addr; } /* * Ignore duplicate search replies */ - if (piiuCast != (IIU *) chan->piiu) { - caAddrNode *pNode; - IIU *tcpPIIU = (IIU *) chan->piiu; + if (&piiu->niiu.iiu.pcas->pudpiiu->niiu.iiu != chan->ciu.piiu) { + tcpiiu *ptcpiiu = iiuToTCPIIU (chan->ciu.piiu); - pNode = (caAddrNode *) ellFirst(&tcpPIIU->destAddr); - assert(pNode); - if (pNode->destAddr.in.sin_addr.s_addr != ina.sin_addr.s_addr || - pNode->destAddr.in.sin_port != ina.sin_port) { + if (ptcpiiu->dest.ia.sin_addr.s_addr != ina.sin_addr.s_addr || + ptcpiiu->dest.ia.sin_port != ina.sin_port) { ipAddrToA (pnet_addr, rej, sizeof(rej)); - sprintf( - sprintf_buf, - "Channel: %s Accepted: %s Rejected: %s ", - (char *)(chan + 1), - tcpPIIU->host_name_str, - rej); - genLocalExcep (ECA_DBLCHNL, sprintf_buf); + sprintf (piiu->niiu.iiu.pcas->ca_sprintf_buf, + "Channel: %s Accepted: %s Rejected: %s ", + ca_name (&chan->ciu), ptcpiiu->host_name_str, rej); + genLocalExcep (piiu->niiu.iiu.pcas, ECA_DBLCHNL, piiu->niiu.iiu.pcas->ca_sprintf_buf); } - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); return; } - status = alloc_ioc (ca_static, &ina, &allocpiiu); - switch (status) { - - case ECA_NORMAL: - break; - - case ECA_DISCONN: - /* - * This indicates that the connection is tagged - * for shutdown and we are waiting for - * it to go away. Search replies are ignored - * in the interim. - */ - UNLOCK; + allocpiiu = constructTCPIIU (piiu->niiu.iiu.pcas, &ina, minorVersion); + if (!allocpiiu) { + UNLOCK (piiu->niiu.iiu.pcas); return; - - default: - ipAddrToA (pnet_addr, rej, sizeof(rej)); - ca_printf("CAC: ... %s ...\n", ca_message(status)); - ca_printf("CAC: for %s on %s\n", chan+1, rej); - ca_printf("CAC: ignored search reply- proceeding\n"); - UNLOCK; - return; - } - allocpiiu->minor_version_number = minorVersion; - /* * If this is the first channel to be - * added to this IIU then communicate + * added to this niiu then communicate * the client's name to the server. * (CA V4.1 or higher) */ - if (ellCount(&allocpiiu->chidlist)==0) { - issue_identify_client(allocpiiu); - issue_client_host_name( allocpiiu); + if (ellCount(&allocpiiu->niiu.chidList)==0) { + issue_identify_client (allocpiiu); + issue_client_host_name (allocpiiu); } - /* - * increase the valid search response count only if this - * response matches up with a request since the beginning - * of the search list - */ - if (ca_static->ca_seq_no_at_list_begin <= chan->retrySeqNo) { - if (ca_static->ca_search_responsesca_search_responses++; - } - } + piiu->searchTmr.notifySearchResponse (chan); /* - * remove it from the broadcast IIU + * remove it from the broadcast niiu */ - removeFromChanList(chan); + removeFromChanList (chan); /* * chan->piiu must be correctly set prior to issuing the @@ -501,7 +436,7 @@ const struct sockaddr_in *pnet_addr) * * claim pending flag is set here */ - addToChanList( chan, allocpiiu); + addToChanList (chan, &allocpiiu->niiu); /* * Assume that we have access once connected briefly @@ -515,15 +450,6 @@ const struct sockaddr_in *pnet_addr) chan->ar.read_access = TRUE; chan->ar.write_access = TRUE; - /* - * Reset the delay to the next search request if we get - * at least one response. However, dont reset this delay if we - * get a delayed response to an old search request. - */ - if (chan->retrySeqNo == ca_static->ca_search_retry_seq_no) { - ca_static->ca_conn_next_retry = ca_static->currentTime; - } - /* * claim the resource in the IOC * over TCP so problems with duplicate UDP port @@ -534,23 +460,21 @@ const struct sockaddr_in *pnet_addr) * If this fails then we will wait for the * next search response. */ - chan->id.sid = piiu->curMsg.m_cid; + chan->sid = piiu->niiu.curMsg.m_cid; if (!CA_V42(CA_PROTOCOL_VERSION, minorVersion)) { - chan->privType = piiu->curMsg.m_dataType; - chan->privCount = piiu->curMsg.m_count; + chan->type = piiu->niiu.curMsg.m_dataType; + chan->count = piiu->niiu.curMsg.m_count; } - issue_claim_channel(chan); - UNLOCK + issue_claim_channel (chan); + UNLOCK (piiu->niiu.iiu.pcas); } /* * read_sync_resp_action () */ -LOCAL void read_sync_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void read_sync_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { piiu->read_seq++; return; @@ -559,14 +483,11 @@ const struct sockaddr_in *pnet_addr) /* * beacon_action () */ -LOCAL void beacon_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void beacon_action (udpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; struct sockaddr_in ina; - LOCK; + LOCK (piiu->niiu.iiu.pcas); /* * this allows a fan-out server to potentially @@ -579,29 +500,29 @@ const struct sockaddr_in *pnet_addr) * always set this field to htonl(INADDR_ANY) * * clients always assume that if this - * field is set something that isnt htonl(INADDR_ANY) + * field is set to something that isnt htonl(INADDR_ANY) * then it is the overriding IP address of the server. */ ina.sin_family = AF_INET; - if (piiu->curMsg.m_available != htonl(INADDR_ANY)) { - ina.sin_addr.s_addr = piiu->curMsg.m_available; + if (piiu->niiu.curMsg.m_available != htonl(INADDR_ANY)) { + ina.sin_addr.s_addr = piiu->niiu.curMsg.m_available; } else { ina.sin_addr = pnet_addr->sin_addr; } - if (piiu->curMsg.m_count != 0) { - ina.sin_port = htons (piiu->curMsg.m_count); + if (piiu->niiu.curMsg.m_count != 0) { + ina.sin_port = htons (piiu->niiu.curMsg.m_count); } else { /* * old servers dont supply this and the * default port must be assumed */ - ina.sin_port = htons (ca_static->ca_server_port); + ina.sin_port = htons (piiu->niiu.iiu.pcas->ca_server_port); } - mark_server_available(ca_static, &ina); + mark_server_available (piiu->niiu.iiu.pcas, &ina); - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -609,14 +530,11 @@ const struct sockaddr_in *pnet_addr) /* * repeater_ack_action () */ -LOCAL void repeater_ack_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void repeater_ack_action (udpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; - ca_static->ca_repeater_contacted = TRUE; + piiu->repeaterContacted = 1u; # ifdef DEBUG - ca_printf ("CAC: repeater confirmation recv\n"); + ca_printf (piiu->niiu.iiu.pcas, "CAC: repeater confirmation recv\n"); # endif return; } @@ -624,9 +542,7 @@ const struct sockaddr_in *pnet_addr) /* * not_here_resp_action () */ -LOCAL void not_here_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void not_here_resp_action (udpiiu *piiu, const struct sockaddr_in *pnet_addr) { return; } @@ -634,28 +550,22 @@ const struct sockaddr_in *pnet_addr) /* * clear_channel_resp_action () */ -LOCAL void clear_channel_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void clear_channel_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; - clearChannelResources (ca_static, piiu->curMsg.m_available); + /* presently a noop */ return; } /* * exception_resp_action () */ -LOCAL void exception_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void exception_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; - ELLLIST *pList = NULL; - miu monix; + nmiu *monix; + nciu *pChan; char nameBuf[64]; char context[255]; - caHdr *req = piiu->pCurData; + caHdr *req = (caHdr *) piiu->niiu.pCurData; int op; struct exception_handler_args args; @@ -663,16 +573,14 @@ const struct sockaddr_in *pnet_addr) * dont process the message if they have * disabled notification */ - if (!ca_static->ca_exception_func){ + if (!piiu->niiu.iiu.pcas->ca_exception_func){ return; } ipAddrToA (pnet_addr, nameBuf, sizeof(nameBuf)); - if (piiu->curMsg.m_postsize > sizeof(caHdr)){ - sprintf(context, - "detected by: %s for: %s", - nameBuf, - (char *)(req+1)); + if (piiu->niiu.curMsg.m_postsize > sizeof(caHdr)){ + sprintf (context, "detected by: %s for: %s", + nameBuf, (char *)(req+1)); } else{ sprintf(context, "detected by: %s", nameBuf); @@ -688,78 +596,69 @@ const struct sockaddr_in *pnet_addr) args.addr = NULL; args.pFile = NULL; args.lineNo = 0u; - LOCK; + args.chid = NULL; + + LOCK (piiu->niiu.iiu.pcas); switch (ntohs(req->m_cmmd)) { case CA_PROTO_READ_NOTIFY: - monix = (miu) bucketLookupItemUnsignedId( - pFastBucket, - &req->m_available); - pList = &pend_read_list; + monix = (nmiu *) bucketLookupItemUnsignedId( + piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); op = CA_OP_GET; - return; + break; case CA_PROTO_READ: - monix = (miu) bucketLookupItemUnsignedId( - pFastBucket, - &req->m_available); - if(monix){ - args.addr = (void *) monix->usr_arg; + monix = (nmiu *) bucketLookupItemUnsignedId( + piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); + if (monix) { + args.addr = monix->miu.usr_arg; } - pList = &pend_read_list; op = CA_OP_GET; - return; + break; case CA_PROTO_WRITE_NOTIFY: - monix = (miu) bucketLookupItemUnsignedId( - pFastBucket, - &req->m_available); - pList = &pend_write_list; + monix = (nmiu *) bucketLookupItemUnsignedId( + piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); op = CA_OP_PUT; - return; + break; case CA_PROTO_WRITE: op = CA_OP_PUT; - return; + pChan = (nciu *) bucketLookupItemUnsignedId + (piiu->niiu.iiu.pcas->ca_pSlowBucket, &piiu->niiu.curMsg.m_cid); + args.chid = (chid) &pChan->ciu; + break; case CA_PROTO_SEARCH: op = CA_OP_SEARCH; - return; + pChan = (nciu *) bucketLookupItemUnsignedId + (piiu->niiu.iiu.pcas->ca_pSlowBucket, &piiu->niiu.curMsg.m_cid); + args.chid = (chid) &pChan->ciu; + break; case CA_PROTO_EVENT_ADD: - monix = (miu) bucketLookupItemUnsignedId( - pFastBucket, - &req->m_available); + monix = (nmiu *) bucketLookupItemUnsignedId( + piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); op = CA_OP_ADD_EVENT; - if (monix) { - ciu pChan = (ciu) monix->chan; - pList = &pChan->eventq; - } - return; + break; case CA_PROTO_EVENT_CANCEL: - monix = (miu) bucketLookupItemUnsignedId( - pFastBucket, - &req->m_available); + monix = (nmiu *) bucketLookupItemUnsignedId( + piiu->niiu.iiu.pcas->ca_pFastBucket, &req->m_available); op = CA_OP_CLEAR_EVENT; - return; + break; default: op = CA_OP_OTHER; - return; + break; } if (monix) { - if (pList) { - ellDelete(pList, &monix->node); - } - caIOBlockFree(ca_static, monix); + args.chid = monix->miu.pChan; + caIOBlockFree (piiu->niiu.iiu.pcas, monix->id); } - args.chid = bucketLookupItemUnsignedId - (pSlowBucket, &piiu->curMsg.m_cid); - - args.usr = (void *) ca_static->ca_exception_arg; + args.usr = piiu->niiu.iiu.pcas->ca_exception_arg; args.type = ntohs (req->m_dataType); args.count = ntohs (req->m_count); - args.stat = ntohl (piiu->curMsg.m_available); + args.stat = ntohl (piiu->niiu.curMsg.m_available); args.op = op; args.ctx = context; - (*ca_static->ca_exception_func) (args); - UNLOCK; + (*piiu->niiu.iiu.pcas->ca_exception_func) (args); + UNLOCK (piiu->niiu.iiu.pcas); return; } @@ -767,188 +666,161 @@ const struct sockaddr_in *pnet_addr) /* * access_rights_resp_action () */ -LOCAL void access_rights_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void access_rights_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; int ar; - ciu chan; + nciu *chan; - LOCK; - chan = (ciu) bucketLookupItemUnsignedId( - pSlowBucket, &piiu->curMsg.m_cid); + LOCK (piiu->niiu.iiu.pcas); + chan = (nciu *) bucketLookupItemUnsignedId( + piiu->niiu.iiu.pcas->ca_pSlowBucket, &piiu->niiu.curMsg.m_cid); if (!chan) { /* * end up here if they delete the channel * prior to connecting */ - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); return; } - ar = ntohl (piiu->curMsg.m_available); + ar = ntohl (piiu->niiu.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; - if (chan->pAccessRightsFunc) { - struct access_rights_handler_args args; + if (chan->ciu.pAccessRightsFunc) { + struct access_rights_handler_args args; - args.chid = chan; + args.chid = (chid) &chan->ciu; args.ar = chan->ar; - (*chan->pAccessRightsFunc)(args); + (*chan->ciu.pAccessRightsFunc)(args); } - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); return; } /* * claim_ciu_resp_action () */ -LOCAL void claim_ciu_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void claim_ciu_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; - cac_reconnect_channel (ca_static, piiu->curMsg.m_cid, - piiu->curMsg.m_dataType, piiu->curMsg.m_count); + cac_reconnect_channel (piiu, piiu->niiu.curMsg.m_cid, + piiu->niiu.curMsg.m_dataType, piiu->niiu.curMsg.m_count); return; } /* * verifyAndDisconnectChan () */ -LOCAL void verifyAndDisconnectChan ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void verifyAndDisconnectChan (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - CA_STATIC *ca_static = piiu->pcas; - ciu chan; + nciu *chan; - LOCK; - chan = (ciu) bucketLookupItemUnsignedId( - pSlowBucket, &piiu->curMsg.m_cid); + LOCK (piiu->niiu.iiu.pcas); + chan = (nciu *) bucketLookupItemUnsignedId( + piiu->niiu.iiu.pcas->ca_pSlowBucket, &piiu->niiu.curMsg.m_cid); if (!chan) { /* * end up here if they delete the channel * prior to this response */ - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); return; } /* - * need to move the channel back to the cast IIU + * need to move the channel back to the cast niiu * (so we will be able to reconnect) * - * this marks the IIU for disconnect if the channel + * this marks the niiu for disconnect if the channel * count goes to zero */ cacDisconnectChannel (chan); - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); return; } /* * bad_tcp_resp_action () */ -LOCAL void bad_tcp_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void bad_tcp_resp_action (tcpiiu *piiu, const struct sockaddr_in *pnet_addr) { - ca_printf ("CAC: Bad response code in TCP message = %u\n", - piiu->curMsg.m_cmmd); + ca_printf (piiu->niiu.iiu.pcas, "CAC: Bad response code in TCP message = %u\n", + piiu->niiu.curMsg.m_cmmd); } /* * bad_udp_resp_action () */ -LOCAL void bad_udp_resp_action ( -IIU *piiu, -const struct sockaddr_in *pnet_addr) +LOCAL void bad_udp_resp_action (udpiiu *piiu, const struct sockaddr_in *pnet_addr) { - ca_printf ("CAC: Bad response code in UDP message = %u\n", - piiu->curMsg.m_cmmd); + ca_printf (piiu->niiu.iiu.pcas, "CAC: Bad response code in UDP message = %u\n", + piiu->niiu.curMsg.m_cmmd); } /* * cac_reconnect_channel() */ -void cac_reconnect_channel(CA_STATIC *ca_static, caResId cid, -short type, unsigned short count) +void cac_reconnect_channel (tcpiiu *piiu, caResId cid, unsigned short type, unsigned long count) { - IIU *piiu; - evid pevent; - enum channel_state prev_cs; + nmiu *pevent; + unsigned prevConn; int v41; - ciu chan; + nciu *chan; - LOCK; - chan = (ciu) bucketLookupItemUnsignedId(pSlowBucket, &cid); + LOCK (piiu->niiu.iiu.pcas); + chan = (nciu *) bucketLookupItemUnsignedId (piiu->niiu.iiu.pcas->ca_pSlowBucket, &cid); /* * this test will fail if they delete the channel * prior to the reply from the claim message */ if (!chan) { - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); return; } - piiu = (IIU *) chan->piiu; - - prev_cs = chan->state; - if (prev_cs == cs_conn) { - ca_printf("CAC: Ignored conn resp to conn chan CID=%u SID=%u?\n", - chan->cid, chan->id.sid); - UNLOCK; + if (chan->connected) { + ca_printf(piiu->niiu.iiu.pcas, + "CAC: Ignored conn resp to conn chan CID=%u SID=%u?\n", + chan->cid, chan->sid); + UNLOCK (piiu->niiu.iiu.pcas); return; } v41 = CA_V41(CA_PROTOCOL_VERSION, piiu->minor_version_number); if (CA_V44(CA_PROTOCOL_VERSION, piiu->minor_version_number)) { - chan->id.sid = piiu->curMsg.m_available; + chan->sid = piiu->niiu.curMsg.m_available; } /* * Update rmt chid fields from caHdr fields * if they are valid */ - if (type != TYPENOTCONN) { - chan->privType = type; - chan->privCount = count; + if (type != USHRT_MAX) { + chan->type = type; + chan->count = count; } /* - * set state to cs_conn before caling + * set state to connected before caling * ca_request_event() so their channel * connect tests wont fail */ - chan->state = cs_conn; + chan->connected = 1; + prevConn = chan->previousConn; + chan->previousConn = 1; /* * NOTE: monitor and callback reissue must occur prior to calling * their connection routine otherwise they could be requested twice. + * + * resubscribe for monitors from this channel */ -#ifdef CALLBACK_REISSUE - /* reissue any outstanding get callbacks for this channel */ - if(pend_read_list.count){ - for( pevent = (evid) pend_read_list.node.next; - pevent; - pevent = (evid) pevent->node.next){ - if(pevent->chan == chan){ - issue_get_callback(pevent); - } + if ( ellCount (&chan->eventq) ) { + for (pevent = (nmiu *) ellFirst (&chan->eventq); + pevent; pevent = (nmiu *) ellNext (&pevent->node) ) { + ca_request_event (chan, pevent); } - } -#endif - - /* reissue any events (monitors) for this channel */ - if(chan->eventq.count){ - for( pevent = (evid)chan->eventq.node.next; - pevent; - pevent = (evid)pevent->node.next) - ca_request_event(pevent); } /* @@ -957,39 +829,41 @@ short type, unsigned short count) * will always be access and call their call back * here */ - if (chan->pAccessRightsFunc && !v41) { + if (chan->ciu.pAccessRightsFunc && !v41) { struct access_rights_handler_args args; - args.chid = chan; + args.chid = (chid) &chan->ciu; args.ar = chan->ar; - (*chan->pAccessRightsFunc)(args); + (*chan->ciu.pAccessRightsFunc)(args); } - if(chan->pConnFunc){ + if (chan->ciu.pConnFunc) { struct connection_handler_args args; - args.chid = chan; + args.chid = (chid) &chan->ciu; args.op = CA_OP_CONN_UP; - - (*chan->pConnFunc)(args); + + (*chan->ciu.pConnFunc)(args); } - else if(prev_cs==cs_never_conn){ - /* + else if (!prevConn) { + /* * decrement the outstanding IO count */ - CLRPENDRECV; + if (--piiu->niiu.iiu.pcas->ca_pndrecvcnt==0u) { + semBinaryGive (piiu->niiu.iiu.pcas->ca_io_done_sem); + } } - UNLOCK; + UNLOCK (piiu->niiu.iiu.pcas); } /* * TCP protocol jump table */ -LOCAL const pProtoStub tcpJumpTableCAC[] = +LOCAL const pProtoStubTCP tcpJumpTableCAC[] = { - noop_action, + tcp_noop_action, event_resp_action, bad_tcp_resp_action, read_resp_action, @@ -1022,9 +896,9 @@ LOCAL const pProtoStub tcpJumpTableCAC[] = /* * UDP protocol jump table */ -LOCAL const pProtoStub udpJumpTableCAC[] = +LOCAL const pProtoStubUDP udpJumpTableCAC[] = { - bad_udp_resp_action, + udp_noop_action, bad_udp_resp_action, bad_udp_resp_action, bad_udp_resp_action, @@ -1060,40 +934,32 @@ LOCAL const pProtoStub udpJumpTableCAC[] = * LOCK should be applied when calling this routine * */ -int post_msg ( -struct ioc_in_use *piiu, -const struct sockaddr_in *pnet_addr, -char *pInBuf, -unsigned long blockSize -) +int post_msg (netIIU *piiu, const struct sockaddr_in *pnet_addr, + char *pInBuf, unsigned long blockSize) { unsigned long size; - pProtoStub pStub; - CA_STATIC *ca_static = piiu->pcas; while (blockSize) { /* * fetch a complete message header */ - if(piiu->curMsgBytes < sizeof(piiu->curMsg)){ + if ( piiu->curMsgBytes < sizeof (piiu->curMsg) ) { char *pHdr; size = sizeof (piiu->curMsg) - piiu->curMsgBytes; size = min (size, blockSize); pHdr = (char *) &piiu->curMsg; - memcpy( pHdr + piiu->curMsgBytes, - pInBuf, - size); + memcpy( pHdr + piiu->curMsgBytes, pInBuf, size); piiu->curMsgBytes += size; - if(piiu->curMsgBytes < sizeof(piiu->curMsg)){ + if (piiu->curMsgBytes < sizeof(piiu->curMsg)) { #if 0 printf ("waiting for %d msg hdr bytes\n", sizeof(piiu->curMsg)-piiu->curMsgBytes); #endif - return OK; + return ECA_NORMAL; } pInBuf += size; @@ -1108,13 +974,15 @@ unsigned long blockSize piiu->curMsg.m_dataType = ntohs(piiu->curMsg.m_dataType); piiu->curMsg.m_count = ntohs(piiu->curMsg.m_count); #if 0 - ca_printf("%s Cmd=%3d Type=%3d Count=%4d Size=%4d", + ca_printf (piiu->niiu.iiu.pcas, + "%s Cmd=%3d Type=%3d Count=%4d Size=%4d", piiu->host_name_str, piiu->curMsg.m_cmmd, piiu->curMsg.m_dataType, piiu->curMsg.m_count, piiu->curMsg.m_postsize); - ca_printf(" Avail=%8x Cid=%6d\n", + ca_printf (piiu->niiu.iiu.pcas, + " Avail=%8x Cid=%6d\n", piiu->curMsg.m_available, piiu->curMsg.m_cid); #endif @@ -1125,10 +993,10 @@ unsigned long blockSize * dont allow huge msg body until * the server supports it */ - if(piiu->curMsg.m_postsize>(unsigned)MAX_TCP){ + if (piiu->curMsg.m_postsize>(unsigned)MAX_TCP) { piiu->curMsgBytes = 0; piiu->curDataBytes = 0; - return ERROR; + return ECA_TOLARGE; } /* @@ -1148,7 +1016,7 @@ unsigned long blockSize size = max(piiu->curMsg.m_postsize,MAX_STRING_SIZE); pCurData = (void *) calloc(1u, size); if(!pCurData){ - return ERROR; + return ECA_ALLOCMEM; } if(piiu->pCurData){ free(piiu->pCurData); @@ -1167,17 +1035,15 @@ unsigned long blockSize size = piiu->curMsg.m_postsize - piiu->curDataBytes; size = min(size, blockSize); - pBdy = piiu->pCurData; - memcpy( pBdy+piiu->curDataBytes, - pInBuf, - size); + pBdy = (char *) piiu->pCurData; + memcpy ( pBdy+piiu->curDataBytes, pInBuf, size); piiu->curDataBytes += size; - if(piiu->curDataBytes < piiu->curMsg.m_postsize){ + if (piiu->curDataBytes < piiu->curMsg.m_postsize) { #if 0 - printf ("waiting for %d msg bdy bytes\n", + printf ("waiting for %d msg bdy bytes\n", piiu->curMsg.m_postsize-piiu->curDataBytes); #endif - return OK; + return ECA_NORMAL; } pInBuf += size; blockSize -= size; @@ -1186,29 +1052,33 @@ unsigned long blockSize /* * execute the response message */ - if (piiu == ca_static->ca_piiuCast) { + if (piiu == &piiu->iiu.pcas->pudpiiu->niiu) { + pProtoStubUDP pStub; if (piiu->curMsg.m_cmmd>=NELEMENTS(udpJumpTableCAC)) { pStub = bad_udp_resp_action; } else { pStub = udpJumpTableCAC [piiu->curMsg.m_cmmd]; } + (*pStub) (piiu->iiu.pcas->pudpiiu, pnet_addr); + } else { + pProtoStubTCP pStub; if (piiu->curMsg.m_cmmd>=NELEMENTS(tcpJumpTableCAC)) { pStub = bad_tcp_resp_action; } else { pStub = tcpJumpTableCAC [piiu->curMsg.m_cmmd]; } + (*pStub) (iiuToTCPIIU(&piiu->iiu), pnet_addr); } - (*pStub) (piiu, pnet_addr); piiu->curMsgBytes = 0; piiu->curDataBytes = 0; } - return OK; + return ECA_NORMAL; } diff --git a/src/ca/syncgrp.c b/src/ca/syncgrp.c deleted file mode 100644 index b60406d65..000000000 --- a/src/ca/syncgrp.c +++ /dev/null @@ -1,645 +0,0 @@ -/* - * $Id$ - * Author: Jeffrey O. Hill - * hill@luke.lanl.gov - * (505) 665 1831 - * Date: 9-93 - * - * Experimental Physics and Industrial Control System (EPICS) - * - * Copyright 1991, the Regents of the University of California, - * and the University of Chicago Board of Governors. - * - * This software was produced under U.S. Government contracts: - * (W-7405-ENG-36) at the Los Alamos National Laboratory, - * and (W-31-109-ENG-38) at Argonne National Laboratory. - * - * Initial development by: - * The Controls and Automation Group (AT-8) - * Ground Test Accelerator - * Accelerator Technology Division - * Los Alamos National Laboratory - * - * Co-developed with - * The Controls and Computing Group - * Accelerator Systems Division - * Advanced Photon Source - * Argonne National Laboratory - * - * Modification Log: - * ----------------- - * $Log$ - * Revision 1.27 1999/07/16 17:08:05 jhill - * fixed bug occurring when connection dropped while waiting to send, and - * initialize new search gongestion thresh parm - * - * Revision 1.26.6.1 1999/07/15 21:02:25 jhill - * fixed bug where client disconnects while waiting to send TCP - * - * Revision 1.26 1997/08/04 23:37:18 jhill - * added beacon anomaly flag init/allow ip 255.255.255.255 - * - * Revision 1.24 1997/06/13 09:14:26 jhill - * connect/search proto changes - * - * Revision 1.23 1997/04/29 06:12:42 jhill - * use free lists - * - * Revision 1.22 1996/11/22 19:08:02 jhill - * added const to API - * - * Revision 1.21 1996/11/02 00:51:08 jhill - * many pc port, const in API, and other changes - * - * Revision 1.20 1996/07/10 23:30:12 jhill - * fixed GNU warnings - * - * Revision 1.19 1996/06/19 17:59:29 jhill - * many 3.13 beta changes - * - * Revision 1.18 1995/10/12 01:36:39 jhill - * New ca_flush_io() mechanism - * - * Revision 1.17 1995/09/29 22:13:59 jhill - * check for nill dbr pointer - * - * Revision 1.16 1995/08/22 00:27:55 jhill - * added cvs style mod log - * - * - * NOTES: - * 1) Need to fix if the OP is on a FD that - * becomes disconneted it will stay on the - * queue forever. - */ - -#include "iocinf.h" -#include "freeList.h" - -LOCAL void io_complete(struct event_handler_args args); - - -/* - * ca_sg_init() - */ -void ca_sg_init(CA_STATIC *ca_static) -{ - /* - * init all sync group lists - */ - freeListInitPvt(&ca_static->ca_sgFreeListPVT,sizeof(CASG),32); - freeListInitPvt(&ca_static->ca_sgopFreeListPVT,sizeof(CASGOP),256); - - return; -} - - -/* - * ca_sg_shutdown() - */ -void ca_sg_shutdown(CA_STATIC *ca_static) -{ - CASG *pcasg; - CASG *pnextcasg; - int status; - - /* - * free all sync group lists - */ - LOCK; - pcasg = (CASG *) ellFirst (&ca_static->activeCASG); - while (pcasg) { - pnextcasg = (CASG *) ellNext (&pcasg->node); - status = ca_sg_delete (pcasg->id); - assert (status==ECA_NORMAL); - pcasg = pnextcasg; - } - assert (ellCount(&ca_static->activeCASG)==0); - - /* - * per sync group - */ - freeListCleanup(ca_static->ca_sgFreeListPVT); - - /* - * per sync group op - */ - ellFree (&ca_static->activeCASGOP); - freeListCleanup(ca_static->ca_sgopFreeListPVT); - - UNLOCK; - - return; -} - - -/* - * ca_sg_create() - */ -int epicsShareAPI ca_sg_create(CA_SYNC_GID *pgid) -{ - int status; - CASG *pcasg; - CA_OSD_GET_CA_STATIC - - /* - * Force the CA client id bucket to - * init if needed. - * Return error if unable to init. - */ - INITCHK; - - /* - * first look on a free list. If not there - * allocate dynamic memory for it. - */ - pcasg = (CASG *) freeListMalloc(ca_static->ca_sgFreeListPVT); - if(!pcasg){ - return ECA_ALLOCMEM; - } - - LOCK; - - /* - * setup initial values for all of the fields - * - * lock must be applied when allocating an id - * and using the id bucket - */ - memset((char *)pcasg,0,sizeof(*pcasg)); - pcasg->magic = CASG_MAGIC; - pcasg->opPendCount = 0; - pcasg->seqNo = 0; - -#ifdef iocCore - pcasg->sem = semBinaryCreate(semEmpty); - assert(pcasg->sem ); -#endif - - do { - pcasg->id = CLIENT_SLOW_ID_ALLOC; - status = bucketAddItemUnsignedId (pSlowBucket, - &pcasg->id, pcasg); - } while (status == S_bucket_idInUse); - - if (status == S_bucket_success) { - /* - * place it on the active sync group list - */ - ellAdd (&ca_static->activeCASG, &pcasg->node); - } - else { - /* - * place it back on the free sync group list - */ - freeListFree(ca_static->ca_sgFreeListPVT, pcasg); - UNLOCK; - if (status == S_bucket_noMemory) { - return ECA_ALLOCMEM; - } - else { - return ECA_INTERNAL; - } - } - - UNLOCK; - - *pgid = pcasg->id; - return ECA_NORMAL; -} - - -/* - * ca_sg_delete() - */ -int epicsShareAPI ca_sg_delete(const CA_SYNC_GID gid) -{ - int status; - CASG *pcasg; - CA_OSD_GET_CA_STATIC - - /* - * Force the CA client id bucket to - * init if needed. - * Return error if unable to init. - */ - INITCHK; - - LOCK; - - pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK; - return ECA_BADSYNCGRP; - } - - status = bucketRemoveItemUnsignedId(pSlowBucket, &gid); - assert (status == S_bucket_success); - -#ifdef iocCore - semBinaryDestroy(pcasg->sem); -#endif - pcasg->magic = 0; - ellDelete(&ca_static->activeCASG, &pcasg->node); - UNLOCK; - - freeListFree(ca_static->ca_sgFreeListPVT, pcasg); - - return ECA_NORMAL; -} - - -/* - * ca_sg_block() - */ -int epicsShareAPI ca_sg_block(const CA_SYNC_GID gid, ca_real timeout) -{ - struct timeval beg_time; - ca_real delay; - int status; - CASG *pcasg; - CA_OSD_GET_CA_STATIC - - /* - * Force the CA client id bucket to - * init if needed. - * Return error if unable to init. - */ - INITCHK; - - if(timeout<0.0){ - return ECA_TIMEOUT; - } - - /* - * until CAs input mechanism is - * revised we have to dissallow - * this routine from an event routine - * (to avoid excess recursion) - */ - if(EVENTLOCKTEST){ - return ECA_EVDISALLOW; - } - - LOCK; - pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK; - return ECA_BADSYNCGRP; - } - UNLOCK; - - /* - * always flush at least once. - */ - ca_static->ca_flush_pending = TRUE; - - cac_gettimeval (&ca_static->currentTime); - beg_time = ca_static->currentTime; - delay = 0.0; - - status = ECA_NORMAL; - while(pcasg->opPendCount){ - ca_real remaining; - struct timeval tmo; - - /* - * Exit if the timeout has expired - * (dont wait forever for an itsy bitsy - * delay which will no be updated if - * select is called with no delay) - * - * current time is only updated by - * cac_select_io() if we specify - * at least 1 usec to wait - */ - remaining = timeout-delay; - if (remaining<=(1.0/USEC_PER_SEC)) { - /* - * Make sure that we take care of - * recv backlog at least once - */ - tmo.tv_sec = 0L; - tmo.tv_usec = 0L; - cac_mux_io (ca_static, &tmo, TRUE); - status = ECA_TIMEOUT; - break; - } - - /* - * Allow for CA background labor - */ - remaining = min(cac_fetch_poll_period(ca_static), remaining); - - /* - * wait for asynch notification - */ - tmo.tv_sec = (long) remaining; - tmo.tv_usec = (long) ((remaining-tmo.tv_sec)*USEC_PER_SEC); -#ifndef iocCore - cac_mux_io(ca_static,&tmo, TRUE); -#else - { - struct timeval itimeout; - double waitTime; - itimeout.tv_usec = 0; - itimeout.tv_sec = 0; - cac_mux_io(ca_static,&itimeout, TRUE); - waitTime = tmo.tv_sec + tmo.tv_usec/1000.0; - if(waitTime>POLLDELAY) waitTime = POLLDELAY; - semBinaryTakeTimeout(ca_static->ca_io_done_sem,waitTime); - /* - *force a time update because we are not - *going to get one with a nill timeout in ca_mux_io() - */ - cac_gettimeval (&ca_static->currentTime); - } -#endif - delay = cac_time_diff (&ca_static->currentTime, &beg_time); - } - pcasg->opPendCount = 0; - pcasg->seqNo++; - return status; -} - - -/* - * ca_sg_reset - */ -int epicsShareAPI ca_sg_reset(const CA_SYNC_GID gid) -{ - CASG *pcasg; - CA_OSD_GET_CA_STATIC - - LOCK; - pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK; - return ECA_BADSYNCGRP; - } - - pcasg->opPendCount = 0; - pcasg->seqNo++; - UNLOCK; - - return ECA_NORMAL; -} - - -/* - * ca_sg_stat - */ -int epicsShareAPI ca_sg_stat(const CA_SYNC_GID gid) -{ - CASG *pcasg; - CASGOP *pcasgop; - CA_OSD_GET_CA_STATIC - - LOCK; - pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK; - printf("Bad Sync Group Id\n"); - return ECA_BADSYNCGRP; - } - UNLOCK; - - printf("Sync Group: id=%u, magic=%lu, opPend=%lu, seqNo=%lu\n", - pcasg->id, pcasg->magic, pcasg->opPendCount, - pcasg->seqNo); - - LOCK; - pcasgop = (CASGOP *) ellFirst(&ca_static->activeCASGOP); - while (pcasgop) { - if (pcasg->id == pcasgop->id) { - printf( - "pending op: id=%u pVal=%x, magic=%lu seqNo=%lu\n", - pcasgop->id, (unsigned)pcasgop->pValue, pcasgop->magic, - pcasgop->seqNo); - } - pcasgop = (CASGOP *) ellNext(&pcasgop->node); - } - return ECA_NORMAL; -} - - -/* - * ca_sg_test - */ -int epicsShareAPI ca_sg_test(const CA_SYNC_GID gid) -{ - CASG *pcasg; - CA_OSD_GET_CA_STATIC - - LOCK; - pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK; - return ECA_BADSYNCGRP; - } - UNLOCK; - - if(pcasg->opPendCount){ - return ECA_IOINPROGRESS; - } - else{ - return ECA_IODONE; - } -} - - -/* - * ca_sg_array_put() - */ -int epicsShareAPI ca_sg_array_put( -const CA_SYNC_GID gid, -chtype type, -unsigned long count, -chid chix, -const void *pvalue) -{ - int status; - CASGOP *pcasgop; - CASG *pcasg; - CA_OSD_GET_CA_STATIC - - /* - * first look on a free list. If not there - * allocate dynamic memory for it. - */ - pcasgop = (CASGOP *) freeListMalloc(ca_static->ca_sgopFreeListPVT); - if(!pcasgop){ - return ECA_ALLOCMEM; - } - - LOCK; - pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK; - freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop); - return ECA_BADSYNCGRP; - } - - memset((char *)pcasgop, 0,sizeof(*pcasgop)); - pcasgop->id = gid; - pcasgop->seqNo = pcasg->seqNo; - pcasgop->magic = CASG_MAGIC; - pcasgop->pValue = NULL; /* handler will know its a put */ - ellAdd(&ca_static->activeCASGOP, &pcasgop->node); - pcasg->opPendCount++; - UNLOCK; - - status = ca_array_put_callback( - type, - count, - chix, - pvalue, - io_complete, - pcasgop); - - if(status != ECA_NORMAL){ - LOCK; - assert(pcasg->opPendCount>=1u); - pcasg->opPendCount--; - ellDelete(&ca_static->activeCASGOP, &pcasgop->node); - UNLOCK; - freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop); - } - - - return status; -} - - - -/* - * ca_sg_array_get() - */ -int epicsShareAPI ca_sg_array_get( -const CA_SYNC_GID gid, -chtype type, -unsigned long count, -chid chix, -void *pvalue) -{ - int status; - CASGOP *pcasgop; - CASG *pcasg; - CA_OSD_GET_CA_STATIC - - /* - * first look on a free list. If not there - * allocate dynamic memory for it. - */ - pcasgop = (CASGOP *) freeListMalloc(ca_static->ca_sgopFreeListPVT); - if(!pcasgop){ - return ECA_ALLOCMEM; - } - - LOCK; - pcasg = bucketLookupItemUnsignedId(pSlowBucket, &gid); - if(!pcasg || pcasg->magic != CASG_MAGIC){ - UNLOCK; - freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop); - return ECA_BADSYNCGRP; - } - - memset((char *)pcasgop, 0,sizeof(*pcasgop)); - pcasgop->id = gid; - pcasgop->seqNo = pcasg->seqNo; - pcasgop->magic = CASG_MAGIC; - pcasgop->pValue = pvalue; - ellAdd(&ca_static->activeCASGOP, &pcasgop->node); - pcasg->opPendCount++; - - UNLOCK; - - status = ca_array_get_callback( - type, - count, - chix, - io_complete, - pcasgop); - - if(status != ECA_NORMAL){ - LOCK; - assert(pcasg->opPendCount>=1u); - pcasg->opPendCount--; - ellDelete(&ca_static->activeCASGOP, &pcasgop->node); - UNLOCK; - freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop); - } - return status; -} - - -/* - * io_complete() - */ -LOCAL void io_complete(struct event_handler_args args) -{ - unsigned long size; - CASGOP *pcasgop; - CASG *pcasg; - CA_OSD_GET_CA_STATIC - - pcasgop = args.usr; - assert(pcasgop->magic == CASG_MAGIC); - - LOCK; - ellDelete(&ca_static->activeCASGOP, &pcasgop->node); - pcasgop->magic = 0; - - /* - * ignore stale replies - */ - pcasg = bucketLookupItemUnsignedId(pSlowBucket, &pcasgop->id); - if(!pcasg || pcasg->seqNo != pcasgop->seqNo){ - UNLOCK; - return; - } - - assert(pcasg->magic == CASG_MAGIC); - assert(pcasg->id == pcasgop->id); - - if(!(args.status&CA_M_SUCCESS)){ - ca_printf( - "CA Sync Group (id=%d) request failed because \"%s\"\n", - pcasgop->id, - ca_message(args.status)); - UNLOCK; - freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop); - return; - } - - /* - * Update the user's variable - * (if its a get) - */ - if(pcasgop->pValue && args.dbr){ - size = dbr_size_n(args.type, args.count); - memcpy(pcasgop->pValue, args.dbr, size); - } - - /* - * decrement the outstanding IO ops count - */ - assert(pcasg->opPendCount>=1u); - pcasg->opPendCount--; - - UNLOCK; - - freeListFree(ca_static->ca_sgopFreeListPVT, pcasgop); - - /* - * Wake up any tasks pending - * - * occurs through select on UNIX - */ -#ifdef iocCore - if(pcasg->opPendCount == 0){ - semBinaryGive(pcasg->sem); - } -#endif - return; -} diff --git a/src/ca/syncgrp.cpp b/src/ca/syncgrp.cpp new file mode 100644 index 000000000..5fdeabf26 --- /dev/null +++ b/src/ca/syncgrp.cpp @@ -0,0 +1,619 @@ +/* + * $Id$ + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * Date: 9-93 + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + */ + +#include "freeList.h" + +#include "iocinf.h" + +/* + * ca_sg_init() + */ +void ca_sg_init (cac *pcac) +{ + /* + * init all sync group lists + */ + ellInit (&pcac->activeCASG); + ellInit (&pcac->activeCASGOP); + freeListInitPvt (&pcac->ca_sgFreeListPVT, sizeof(CASG), 32); + freeListInitPvt (&pcac->ca_sgopFreeListPVT, sizeof(CASGOP), 256); + + return; +} + +/* + * ca_sg_shutdown() + */ +void ca_sg_shutdown (cac *pcac) +{ + CASG *pcasg; + CASG *pnextcasg; + int status; + + /* + * free all sync group lists + */ + LOCK (pcac); + pcasg = (CASG *) ellFirst (&pcac->activeCASG); + while (pcasg) { + pnextcasg = (CASG *) ellNext (&pcasg->node); + status = ca_sg_delete (pcasg->id); + assert (status==ECA_NORMAL); + pcasg = pnextcasg; + } + assert (ellCount(&pcac->activeCASG)==0); + + /* + * per sync group + */ + freeListCleanup(pcac->ca_sgFreeListPVT); + + /* + * per sync group op + */ + ellFree (&pcac->activeCASGOP); + freeListCleanup(pcac->ca_sgopFreeListPVT); + + UNLOCK (pcac); + + return; +} + +/* + * ca_sg_create() + */ +int epicsShareAPI ca_sg_create (CA_SYNC_GID *pgid) +{ + int caStatus; + int status; + CASG *pcasg; + cac *pcac; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + /* + * first look on a free list. If not there + * allocate dynamic memory for it. + */ + pcasg = (CASG *) freeListMalloc (pcac->ca_sgFreeListPVT); + if(!pcasg){ + return ECA_ALLOCMEM; + } + + LOCK (pcac); + + /* + * setup initial values for all of the fields + * + * lock must be applied when allocating an id + * and using the id bucket + */ + memset((char *)pcasg,0,sizeof(*pcasg)); + pcasg->magic = CASG_MAGIC; + pcasg->opPendCount = 0; + pcasg->seqNo = 0; + + pcasg->sem = semBinaryMustCreate (semEmpty); + + do { + pcasg->id = CLIENT_SLOW_ID_ALLOC (pcac); + status = bucketAddItemUnsignedId (pcac->ca_pSlowBucket, &pcasg->id, pcasg); + } while (status == S_bucket_idInUse); + + if (status == S_bucket_success) { + /* + * place it on the active sync group list + */ + ellAdd (&pcac->activeCASG, &pcasg->node); + } + else { + /* + * place it back on the free sync group list + */ + freeListFree(pcac->ca_sgFreeListPVT, pcasg); + UNLOCK (pcac); + if (status == S_bucket_noMemory) { + return ECA_ALLOCMEM; + } + else { + return ECA_INTERNAL; + } + } + + UNLOCK (pcac); + + *pgid = pcasg->id; + return ECA_NORMAL; +} + +/* + * ca_sg_delete() + */ +int epicsShareAPI ca_sg_delete(const CA_SYNC_GID gid) +{ + int caStatus; + int status; + CASG *pcasg; + cac *pcac; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + LOCK (pcac); + + pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); + if (!pcasg || pcasg->magic != CASG_MAGIC) { + UNLOCK (pcac); + return ECA_BADSYNCGRP; + } + + status = bucketRemoveItemUnsignedId (pcac->ca_pSlowBucket, &gid); + assert (status == S_bucket_success); + + semBinaryDestroy(pcasg->sem); + pcasg->magic = 0; + ellDelete(&pcac->activeCASG, &pcasg->node); + UNLOCK (pcac); + + freeListFree(pcac->ca_sgFreeListPVT, pcasg); + + return ECA_NORMAL; +} + +/* + * ca_sg_block_private () + */ +static int ca_sg_block_private (cac *pcac, const CA_SYNC_GID gid, ca_real timeout) +{ + TS_STAMP cur_time; + TS_STAMP beg_time; + ca_real delay; + int status; + CASG *pcasg; + unsigned flushCompleted = FALSE; + + if (timeout<0.0) { + return ECA_TIMEOUT; + } + + status = tsStampGetCurrent (&cur_time); + if (status!=0) { + return ECA_INTERNAL; + } + + LOCK (pcac); + + pcac->currentTime = cur_time; + + pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); + if ( !pcasg || pcasg->magic != CASG_MAGIC ) { + UNLOCK (pcac); + return ECA_BADSYNCGRP; + } + + UNLOCK (pcac); + + cacFlushAllIIU (pcac); + + beg_time = cur_time; + delay = 0.0; + + status = ECA_NORMAL; + while (pcasg->opPendCount) { + ca_real remaining; + int tsStatus; + + /* + * Exit if the timeout has expired + * (dont wait forever for an itsy bitsy + * delay which will not be updated if + * select is called with no delay) + * + * current time is only updated by + * cac_select_io() if we specify + * at non-zero delay + */ + remaining = timeout-delay; + if (remaining<=CAC_SIGNIFICANT_SELECT_DELAY) { + /* + * Make sure that we take care of + * recv backlog at least once + */ + status = ECA_TIMEOUT; + break; + } + + remaining = min (60.0, remaining); + + /* + * wait for asynch notification + */ + semBinaryTakeTimeout (pcasg->sem, remaining); + + /* + * force a time update + */ + tsStatus = tsStampGetCurrent (&cur_time); + if (tsStatus!=0) { + status = ECA_INTERNAL; + break; + } + + LOCK (pcac); + pcac->currentTime = cur_time; + UNLOCK (pcac); + + flushCompleted = TRUE; + delay = tsStampDiffInSeconds (&cur_time, &beg_time); + } + pcasg->opPendCount = 0; + pcasg->seqNo++; + return status; +} + +/* + * ca_sg_block () + */ +int epicsShareAPI ca_sg_block (const CA_SYNC_GID gid, ca_real timeout) +{ + cac *pcac; + int status; + + status = fetchClientContext (&pcac); + if ( status != ECA_NORMAL ) { + return status; + } + + /* + * dont allow recursion + */ + { + void *p = threadPrivateGet (cacRecursionLock); + if (p) { + return ECA_EVDISALLOW; + } + threadPrivateSet (cacRecursionLock, &cacRecursionLock); + } + + status = ca_sg_block_private (pcac, gid, timeout); + + threadPrivateSet (cacRecursionLock, NULL); + + return status; +} + +/* + * ca_sg_reset + */ +int epicsShareAPI ca_sg_reset (const CA_SYNC_GID gid) +{ + CASG *pcasg; + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + LOCK (pcac); + pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); + if(!pcasg || pcasg->magic != CASG_MAGIC){ + UNLOCK (pcac); + return ECA_BADSYNCGRP; + } + + pcasg->opPendCount = 0; + pcasg->seqNo++; + UNLOCK (pcac); + + return ECA_NORMAL; +} + +/* + * ca_sg_stat + */ +int epicsShareAPI ca_sg_stat (const CA_SYNC_GID gid) +{ + CASG *pcasg; + CASGOP *pcasgop; + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + LOCK (pcac); + pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); + if (!pcasg || pcasg->magic != CASG_MAGIC) { + UNLOCK (pcac); + printf("Bad Sync Group Id\n"); + return ECA_BADSYNCGRP; + } + UNLOCK (pcac); + + printf("Sync Group: id=%u, magic=%lu, opPend=%lu, seqNo=%lu\n", + pcasg->id, pcasg->magic, pcasg->opPendCount, + pcasg->seqNo); + + LOCK (pcac); + pcasgop = (CASGOP *) ellFirst (&pcac->activeCASGOP); + while (pcasgop) { + if (pcasg->id == pcasgop->id) { + printf( + "pending op: id=%u pVal=%x, magic=%lu seqNo=%lu\n", + pcasgop->id, (unsigned)pcasgop->pValue, pcasgop->magic, + pcasgop->seqNo); + } + pcasgop = (CASGOP *) ellNext(&pcasgop->node); + } + UNLOCK (pcac); + + return ECA_NORMAL; +} + +/* + * ca_sg_test + */ +int epicsShareAPI ca_sg_test (const CA_SYNC_GID gid) +{ + CASG *pcasg; + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + LOCK (pcac); + pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); + if(!pcasg || pcasg->magic != CASG_MAGIC){ + UNLOCK (pcac); + return ECA_BADSYNCGRP; + } + UNLOCK (pcac); + + if(pcasg->opPendCount){ + return ECA_IOINPROGRESS; + } + else{ + return ECA_IODONE; + } +} + +/* + * io_complete() + */ +LOCAL void io_complete (struct event_handler_args args) +{ + unsigned long size; + CASGOP *pcasgop; + CASG *pcasg; + + pcasgop = (CASGOP *) args.usr; + + if (pcasgop->magic != CASG_MAGIC) { + errlogPrintf ("cac: sync group io_complete(): bad sync grp op magic number?\n"); + return; + } + + LOCK (pcasgop->pcac); + ellDelete (&pcasgop->pcac->activeCASGOP, &pcasgop->node); + pcasgop->magic = 0; + + /* + * ignore stale replies + */ + pcasg = (CASG *) bucketLookupItemUnsignedId (pcasgop->pcac->ca_pSlowBucket, &pcasgop->id); + if (!pcasg || pcasg->seqNo != pcasgop->seqNo) { + UNLOCK (pcasgop->pcac); + return; + } + + assert (pcasg->magic == CASG_MAGIC); + assert (pcasg->id == pcasgop->id); + + if ( !( args.status & CA_M_SUCCESS ) ) { + ca_printf ( + pcasgop->pcac, + "CA Sync Group (id=%d) request failed because \"%s\"\n", + pcasgop->id, + ca_message(args.status) ); + UNLOCK (pcasgop->pcac); + freeListFree(pcasgop->pcac->ca_sgopFreeListPVT, pcasgop); + return; + } + + /* + * Update the user's variable + * (if its a get) + */ + if (pcasgop->pValue && args.dbr) { + size = dbr_size_n (args.type, args.count); + memcpy (pcasgop->pValue, args.dbr, size); + } + + /* + * decrement the outstanding IO ops count + */ + assert (pcasg->opPendCount>=1u); + pcasg->opPendCount--; + + UNLOCK (pcasgop->pcac); + + freeListFree (pcasgop->pcac->ca_sgopFreeListPVT, pcasgop); + + /* + * Wake up any tasks pending + * + * occurs through select on UNIX + */ + if (pcasg->opPendCount == 0) { + semBinaryGive(pcasg->sem); + } + return; +} + +/* + * ca_sg_array_put() + */ +int epicsShareAPI ca_sg_array_put ( +const CA_SYNC_GID gid, +chtype type, +unsigned long count, +chid chix, +const void *pvalue) +{ + int status; + CASGOP *pcasgop; + CASG *pcasg; + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + /* + * first look on a free list. If not there + * allocate dynamic memory for it. + */ + pcasgop = (CASGOP *) freeListMalloc (pcac->ca_sgopFreeListPVT); + if(!pcasgop){ + return ECA_ALLOCMEM; + } + + LOCK (pcac); + pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); + if(!pcasg || pcasg->magic != CASG_MAGIC){ + UNLOCK (pcac); + freeListFree(pcac->ca_sgopFreeListPVT, pcasgop); + return ECA_BADSYNCGRP; + } + + memset((char *)pcasgop, 0,sizeof(*pcasgop)); + pcasgop->id = gid; + pcasgop->seqNo = pcasg->seqNo; + pcasgop->magic = CASG_MAGIC; + pcasgop->pValue = NULL; /* handler will know its a put */ + pcasgop->pcac = pcac; + ellAdd (&pcac->activeCASGOP, &pcasgop->node); + pcasg->opPendCount++; + UNLOCK (pcac); + + status = ca_array_put_callback (type, count, chix, + pvalue, io_complete, pcasgop); + if (status != ECA_NORMAL) { + LOCK (pcac); + assert (pcasg->opPendCount>=1u); + pcasg->opPendCount--; + ellDelete (&pcac->activeCASGOP, &pcasgop->node); + UNLOCK (pcac); + freeListFree (pcac->ca_sgopFreeListPVT, pcasgop); + } + + return status; +} + +/* + * ca_sg_array_get() + */ +int epicsShareAPI ca_sg_array_get ( +const CA_SYNC_GID gid, +chtype type, +unsigned long count, +chid chix, +void *pvalue) +{ + int status; + CASGOP *pcasgop; + CASG *pcasg; + cac *pcac; + int caStatus; + + caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + /* + * first look on a free list. If not there + * allocate dynamic memory for it. + */ + pcasgop = (CASGOP *) freeListMalloc (pcac->ca_sgopFreeListPVT); + if (!pcasgop) { + return ECA_ALLOCMEM; + } + + LOCK (pcac); + pcasg = (CASG *) bucketLookupItemUnsignedId (pcac->ca_pSlowBucket, &gid); + if(!pcasg || pcasg->magic != CASG_MAGIC){ + UNLOCK (pcac); + freeListFree(pcac->ca_sgopFreeListPVT, pcasgop); + return ECA_BADSYNCGRP; + } + + memset((char *)pcasgop, 0,sizeof(*pcasgop)); + pcasgop->id = gid; + pcasgop->seqNo = pcasg->seqNo; + pcasgop->magic = CASG_MAGIC; + pcasgop->pValue = pvalue; + pcasgop->pcac = pcac; + ellAdd(&pcac->activeCASGOP, &pcasgop->node); + pcasg->opPendCount++; + + UNLOCK (pcac); + + status = ca_array_get_callback( + type, + count, + chix, + io_complete, + pcasgop); + + if(status != ECA_NORMAL){ + LOCK (pcac); + assert(pcasg->opPendCount>=1u); + pcasg->opPendCount--; + ellDelete(&pcac->activeCASGOP, &pcasgop->node); + UNLOCK (pcac); + freeListFree(pcac->ca_sgopFreeListPVT, pcasgop); + } + return status; +} diff --git a/src/ca/test_event.c b/src/ca/test_event.c deleted file mode 100644 index 591cf651f..000000000 --- a/src/ca/test_event.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * T E S T _ E V E N T . C - * Author: Jeffrey O. Hill - * simple stub for testing monitors - * - * - * History - * joh 031891 printed type in decimal instead of hex - * joh 072792 better messages - * - * - * - * - */ - -static char *sccsId = "$Id$"; - -#include "iocinf.h" - - -void epicsShareAPI ca_test_event(struct event_handler_args args) -{ - ca_printf("CAC: ~~~### in test event for [%s] ###~~~\n",args.chid+1); - ca_printf("CAC: User argument\t%x\n", args.usr); - ca_printf("CAC: Native channel data type\t%d\n", ca_field_type(args.chid)); - ca_printf("CAC: Monitor data type\t%d\n", args.type); - ca_printf("CAC: CA Status \"%s\"\n", ca_message(args.status)); - - if(!args.dbr || !(CA_M_SUCCESS&args.status)){ - return; - } - - switch(args.type){ - case DBR_STRING: - ca_printf("CAC: Value:\t<%s>\n",args.dbr); - break; - case DBR_CHAR: - ca_printf("CAC: Value:\t<%d>\n",*(char *)args.dbr); - break; -#if DBR_INT != DBR_SHORT - case DBR_INT: -#endif - case DBR_SHORT: - case DBR_ENUM: - ca_printf("CAC: Value:\t<%d>\n",*(short *)args.dbr); - break; - case DBR_LONG: - ca_printf("CAC: Value:\t<%d>\n",*(long *)args.dbr); - break; - case DBR_FLOAT: - ca_printf("CAC: Value:\t<%f>\n",*(float *)args.dbr); - break; - case DBR_DOUBLE: - ca_printf("CAC: Value:\t<%lf>\n",*(double *)args.dbr); - break; - case DBR_STS_STRING: - ca_printf("CAC: Value:\t<%s>\n",((struct dbr_sts_string *)args.dbr)->value); - break; - case DBR_STS_INT: - ca_printf("CAC: Value:\t<%d>\n",((struct dbr_sts_int *)args.dbr)->value); - break; - case DBR_STS_FLOAT: - ca_printf("CAC: Value:\t<%f>\n",((struct dbr_sts_float *)args.dbr)->value); - break; - case DBR_STS_ENUM: - ca_printf("CAC: Value:\t<%d>\n",((struct dbr_sts_enum *)args.dbr)->value); - break; - case DBR_GR_FLOAT: - ca_printf("CAC: Value:\t<%f>\n",((struct dbr_gr_float *)args.dbr)->value); - break; - default: - ca_printf( "CAC: Sorry test_event does not handle data type %d yet\n", - args.type); - } -} - diff --git a/src/ca/test_event.cpp b/src/ca/test_event.cpp new file mode 100644 index 000000000..133ece35e --- /dev/null +++ b/src/ca/test_event.cpp @@ -0,0 +1,75 @@ +/* + * + * T E S T _ E V E N T . C + * Author: Jeffrey O. Hill + * simple stub for testing monitors + * + * + * History + * joh 031891 printed type in decimal instead of hex + * joh 072792 better messages + * + * + * + * + */ + +#include "iocinf.h" + + +void epicsShareAPI ca_test_event(struct event_handler_args args) +{ + printf ("CAC: ~~~### in test event for [%s] ###~~~\n", ca_name(args.chid)); + printf ("CAC: User argument\t%p\n", args.usr); + printf ("CAC: Native channel data type\t%d\n", ca_field_type(args.chid)); + printf ("CAC: Monitor data type\t%ld\n", args.type); + printf ("CAC: CA Status \"%s\"\n", ca_message(args.status)); + + if(!args.dbr || !(CA_M_SUCCESS&args.status)){ + return; + } + + switch(args.type){ + case DBR_STRING: + printf ("CAC: Value:\t<%s>\n", (const char *)args.dbr); + break; + case DBR_CHAR: + printf ("CAC: Value:\t<%d>\n",*(char *)args.dbr); + break; +#if DBR_INT != DBR_SHORT + case DBR_INT: +#endif + case DBR_SHORT: + case DBR_ENUM: + printf ("CAC: Value:\t<%d>\n",*(short *)args.dbr); + break; + case DBR_LONG: + printf ("CAC: Value:\t<%ld>\n",*(long *)args.dbr); + break; + case DBR_FLOAT: + printf ("CAC: Value:\t<%f>\n",*(float *)args.dbr); + break; + case DBR_DOUBLE: + printf ("CAC: Value:\t<%f>\n",*(double *)args.dbr); + break; + case DBR_STS_STRING: + printf ("CAC: Value:\t<%s>\n",((struct dbr_sts_string *)args.dbr)->value); + break; + case DBR_STS_INT: + printf ("CAC: Value:\t<%d>\n",((struct dbr_sts_int *)args.dbr)->value); + break; + case DBR_STS_FLOAT: + printf ("CAC: Value:\t<%f>\n",((struct dbr_sts_float *)args.dbr)->value); + break; + case DBR_STS_ENUM: + printf ("CAC: Value:\t<%d>\n",((struct dbr_sts_enum *)args.dbr)->value); + break; + case DBR_GR_FLOAT: + printf ("CAC: Value:\t<%f>\n",((struct dbr_gr_float *)args.dbr)->value); + break; + default: + printf ( "CAC: Sorry test_event does not handle data type %ld yet\n", + args.type); + } +} + diff --git a/src/db/dbAccessDefs.h b/src/db/dbAccessDefs.h index 568d2b6fb..1ac087117 100644 --- a/src/db/dbAccessDefs.h +++ b/src/db/dbAccessDefs.h @@ -191,6 +191,7 @@ struct dbr_alDouble {DBRalDouble}; #define S_db_bkptLogic (M_dbAccess|61) /*Logic error in breakpoint routine*/ #define S_db_cntSpwn (M_dbAccess|63) /*Cannot spawn dbContTask*/ #define S_db_cntCont (M_dbAccess|65) /*Cannot resume dbContTask*/ +#define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/ /* Global Database Access Routines*/ #define dbGetLink(PLNK,DBRTYPE,PBUFFER,OPTIONS,NREQUEST) \