From a3ec3b0139fbca57ea3e9e098277a95f5c540d39 Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Wed, 25 Nov 2020 09:17:54 -0500 Subject: [PATCH] New upstream version 2.1.2 --- .ci/travis-build.sh | 2 +- .gitignore | 1 + .travis.yml | 6 +- README.md | 6 + RELEASE_NOTES.md | 16 +- configure/CONFIG_SITE | 19 +- configure/RELEASE | 4 + src/Makefile | 57 +---- src/gateAs.cc | 140 +++++------ src/gateAs.h | 36 +-- src/gateAsCa.cc | 50 ++-- src/gateAsyncIO.cc | 6 +- src/gateAsyncIO.h | 12 +- src/gatePv.cc | 224 ++++-------------- src/gatePv.h | 70 +++--- src/gateResources.cc | 146 ++++++------ src/gateResources.h | 32 +-- src/gateServer.cc | 150 ++++-------- src/gateServer.h | 14 +- src/gateStat.cc | 8 +- src/gateStat.h | 16 +- src/gateVc.cc | 161 ++++++++----- src/gateVc.h | 15 +- src/gateVersion.h | 11 +- src/gateway.cc | 133 +++++------ src/tsHash.h | 4 +- testTop/pyTestsApp/GatewayControl.py | 21 +- testTop/pyTestsApp/IOCControl.py | 6 +- testTop/pyTestsApp/Makefile | 51 ++-- testTop/pyTestsApp/TestCSStudio.py | 122 ++++++++++ testTop/pyTestsApp/TestDBEAlarm.py | 4 +- testTop/pyTestsApp/TestDBELog.py | 4 +- testTop/pyTestsApp/TestDBEProp.py | 8 +- testTop/pyTestsApp/TestDBEValue.py | 4 +- ...mPropCache.py => TestEnumPropertyCache.py} | 2 + testTop/pyTestsApp/TestStructures.py | 9 +- .../TestWaveformWithCAMaxArrayBytes.py | 87 +++++++ testTop/pyTestsApp/gwtests.py | 8 +- testTop/pyTestsApp/test.db | 7 + 39 files changed, 845 insertions(+), 827 deletions(-) create mode 100644 testTop/pyTestsApp/TestCSStudio.py rename testTop/pyTestsApp/{TestEnumPropCache.py => TestEnumPropertyCache.py} (98%) create mode 100644 testTop/pyTestsApp/TestWaveformWithCAMaxArrayBytes.py diff --git a/.ci/travis-build.sh b/.ci/travis-build.sh index 3ef2b84..5324eea 100644 --- a/.ci/travis-build.sh +++ b/.ci/travis-build.sh @@ -10,7 +10,7 @@ make -j2 $EXTRA # Configure pyepics and IOC wrapper eval `grep -m 1 "EPICS_BASE[[:space:]]*=" configure/RELEASE.local` EPICS_HOST_ARCH=`sh $EPICS_BASE/startup/EpicsHostArch` -export PYEPICS_LIBCA=$EPICS_BASE/lib/$EPICS_HOST_ARCH/libca.so +[ "$CMPLR" = "clang" ] || export PYEPICS_LIBCA=$EPICS_BASE/lib/$EPICS_HOST_ARCH/libca.so export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$EPICS_BASE/lib/$EPICS_HOST_ARCH export PATH=$PATH:$EPICS_BASE/bin/$EPICS_HOST_ARCH diff --git a/.gitignore b/.gitignore index a89cb5b..ac67464 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ html/ envPaths QtC-* *.orig +__pycache__ # Gateway generated scriptlets gateway.killer diff --git a/.travis.yml b/.travis.yml index cfa52ad..8d165d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,8 @@ language: cpp env: - BASE=7.0 - BASE=7.0 STATIC=YES - - BASE=3.16 - - BASE=3.16 STATIC=YES - - BASE=3.16 STATIC=YES EXTRA=CMD_CXXFLAGS=-std=c++11 - - BASE=3.16 CMPLR=clang + - BASE=7.0 STATIC=YES EXTRA=CMD_CXXFLAGS=-std=c++11 + - BASE=7.0 CMPLR=clang - BASE=3.15 - BASE=3.15 STATIC=YES - BASE=3.14 diff --git a/README.md b/README.md index 305ebcc..6c92e9d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,12 @@ to needed process variables. The Gateway typically runs on a machine with multiple network cards, and the clients and the server may be on different subnets. +## Dependencies + +The CA Gateway is using the PCAS server library and needs the PCAS module (https://github.com/epics-modules/pcas) that was unbundled from Base back in 3.16. + +If you use caPutLog (https://github.com/epics-modules/caPutLog), CA Gateway now requires it to be greater or equal R3.5. + ## Continuous Integration The CI jobs for CA Gateway are provided by diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 244c9b1..da55ca0 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,11 +1,23 @@ CA Gateway Release Notes ======================== -## 2.1.2 (not released yet) -[View diff](https://github.com/epics-extensions/ca-gateway/compare/R2-1-1-0...master) +## 2.1.3 (not released yet) +[View diff](https://github.com/epics-extensions/ca-gateway/compare/R2-1-2-0...master) * Ehhh... +## 2.1.2 (25 Oct 2019) +[View diff](https://github.com/epics-extensions/ca-gateway/compare/R2-1-1-0...R2-1-2-0) + +* Crash reported and fixed by Diamond (on MAX_ARRAY_BYTES mismatch). +* Improve handling of DBR_CTRL requests. +* Remove support for EPICS Base 3.13, Solaris +* Improve tests, make them compatible with python3 +* Properly depend on PCAS for EPICS 7 builds +* Update to support caPutLog >= 3.5 (older versions not supported) +* Add support for 64bit integers +* Raise PV name length limit to 256 characters + ## 2.1.1 (17 Oct 2018) [View diff](https://github.com/epics-extensions/ca-gateway/compare/R2-1-0-0...R2-1-1-0) diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE index f295c35..5dee672 100644 --- a/configure/CONFIG_SITE +++ b/configure/CONFIG_SITE @@ -47,12 +47,6 @@ USE_DENY_FROM=YES # Compiler options #USR_CXXFLAGS += -xsb -# Use Purify -#PURIFY=YES - -# Use Quantify -#QUANTIFY=YES - # Turn on debug mode #USR_CXXFLAGS += -DDEBUG_MODE @@ -89,21 +83,12 @@ USR_CXXFLAGS += -DHANDLE_EXCEPTIONS # continue building anyway if conflicts are found. CHECK_RELEASE = YES -# Set this when you only want to compile this application -# for a subset of the cross-compiled target architectures -# that Base is built for. -#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 +# Disable cross-builds for this application +CROSS_COMPILER_TARGET_ARCHS = # To install files into a location other than $(TOP) define # INSTALL_LOCATION here. #INSTALL_LOCATION= -# Set this when your IOC and the host use different paths -# to access the application. This will be needed to boot -# from a Microsoft FTP server or with some NFS mounts. -# You must rebuild in the iocBoot directory for this to -# take effect. -#IOCS_APPL_TOP = - -include $(TOP)/../CONFIG_SITE.local -include $(TOP)/configure/CONFIG_SITE.local diff --git a/configure/RELEASE b/configure/RELEASE index ea20dcb..92c25ad 100644 --- a/configure/RELEASE +++ b/configure/RELEASE @@ -21,6 +21,10 @@ #CAPUTLOG=/usr/lib/epics +# PCAS was unbundled from EPICS Base and is needed for building the +# ca-gateway with EPICS 7 +#PCAS=/your/path/to/modules/pcas/ + # EPICS_BASE usually appears last so other apps can override stuff: #EPICS_BASE=/usr/lib/epics diff --git a/src/Makefile b/src/Makefile index a49e58e..be48017 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,41 +7,12 @@ # Operator of Los Alamos National Laboratory. # Copyright (c) 2014-2016 ITER Organization. # This file is distributed subject to a Software License Agreement found -# in the file LICENSE that is included with this distribution. +# in the file LICENSE that is included with this distribution. #************************************************************************* TOP = .. include $(TOP)/configure/CONFIG -ifeq ($(PURIFY),YES) - ifeq ($(OS_CLASS),solaris) - PURIFY_FLAGS = -first-only -chain-length=26 -max_threads=256 - # Put the cache files in the appropriate bin directory - PURIFY_FLAGS += -always-use-cache-dir -cache-dir=$(shell $(PERL) $(TOOLS)/fullPathName.pl .) - DEBUGCMD = purify $(PURIFY_FLAGS) - endif -endif - -ifeq ($(QUANTIFY),YES) - ifeq ($(OS_CLASS),solaris) - #QUANTIFY_FLAGS += -measure-timed-calls=user+system - QUANTIFY_FLAGS += -collection-granularity=function - QUANTIFY_FLAGS += -use-machine=UltraSparcIII:1002MHz - QUANTIFY_FLAGS += -max_threads=160 - - #QUANTIFY_FLAGS += -record-system-calls=no - - # -measure-timed-calls=elapsed-time (default) gives wall clock time - # for system calls - # -measure-timed-calls=user+system gives user+system time - # -record-system-calls=no gives 0 time for system calls - # -collection-granularity=function runs faster than default=line - # -use-machine=UltraSparc:168MHz timing for Nike - # -use-machine=UltraSparcIII:1002MHz timing for Ctlapps1 - DEBUGCMD = quantify $(QUANTIFY_FLAGS) - endif -endif - ifeq ($(USE_DENY_FROM),YES) USR_CXXFLAGS += -DUSE_DENYFROM endif @@ -50,9 +21,6 @@ ifeq ($(USE_NEG_REGEXP),YES) USR_CXXFLAGS += -DUSE_NEG_REGEXP endif -# Reserve file descriptor for fopen to avoid fd limit of 256 on Solaris -USR_CXXFLAGS_solaris += -DRESERVE_FOPEN_FD - ifeq ($(CMPLR_CLASS),msvc) USR_LDFLAGS_WIN32 += /SUBSYSTEM:CONSOLE endif @@ -103,7 +71,7 @@ USR_INCLUDES += -I$(EPICS_BASE)/src/ca/legacy/pcas/generic # To compile in caPutLog functionality, define the location of the caPutLog # module as 'CAPUTLOG' in the appropriate extensions configure/RELEASE* file ifdef CAPUTLOG - USR_LIBS += caPutLog dbIoc + USR_LIBS += caPutLog $(EPICS_BASE_IOC_LIBS) USR_CXXFLAGS += -DWITH_CAPUTLOG USR_CFLAGS += -DWITH_CAPUTLOG endif @@ -114,31 +82,12 @@ include $(TOP)/configure/RULES xxxx: @echo HOST_OPT: $(HOST_OPT) - @echo PURIFY: $(PURIFY) - @echo PURIFY_FLAGS: $(PURIFY_FLAGS) - @echo PURIFYCMD: $(PURIFYCMD) - @echo QUANTIFY: $(QUANTIFY) - @echo QUANTIFYCMD: $(QUANTIFYCMD) @echo CXX $(CXX) @echo CXXFLAGS $(CXXFLAGS) @echo LINK.cc: $(LINK.cc) @echo LINK.c: $(LINK.c) - @echo TARGET_OBJS: $(TARGET_OBJS) - @echo PRODNAME_OBJS: $(PRODNAME_OBJS) - @echo PROD_LD_OBJS: $(PROD_LD_OBJS) - @echo PRODUCT_OBJS: $(PRODUCT_OBJS) - @echo PROD_OBJS: $(PROD_OBJS) @echo EPICS_BASE: $(EPICS_BASE) - @echo HOST_ARCH: $(HOST_ARCH) - @echo ARCH_DEP_LDFLAGS_ML_NO: $(ARCH_DEP_LDFLAGS_ML_NO) - @echo ARCH_DEP_LDFLAGS_ML_YES: $(ARCH_DEP_LDFLAGS_ML_YES) - @echo ARCH_DEP_LDFLAGS_ML: $(ARCH_DEP_LDFLAGS_ML) - @echo ARCH_DEP_LDFLAGS_MD_NO: $(ARCH_DEP_LDFLAGS_MD_NO) - @echo ARCH_DEP_LDFLAGS_MD_YES: $(ARCH_DEP_LDFLAGS_MD_YES) - @echo ARCH_DEP_LDFLAGS_MD: $(ARCH_DEP_LDFLAGS_MD) - @echo ACC_SFLAGS_YES: $(ACC_SFLAGS_YES) - @echo ACC_SFLAGS_NO: $(ACC_SFLAGS_NO) + @echo EPICS_HOST_ARCH: $(EPICS_HOST_ARCH) @echo SHARED_LIBRARIES: $(SHARED_LIBRARIES) - @echo DEBUGCMD: $(DEBUGCMD) @echo CAPUTLOG: $(CAPUTLOG) @echo EPICS_BASE_HOST_LIBS: $(EPICS_BASE_HOST_LIBS) diff --git a/src/gateAs.cc b/src/gateAs.cc index 92fc10e..962347d 100644 --- a/src/gateAs.cc +++ b/src/gateAs.cc @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ /*+********************************************************************* @@ -187,14 +187,14 @@ aitBool gateAsEntry::init(gateAsList& n, int line) { } return aitTrue; } - + #ifdef USE_DENYFROM aitBool gateAsEntry::init(const char* host, // Host name to deny tsHash& h, // Where this entry is added to gateHostList& hl, // Where a new key should be added int line) { // Line number gateAsList* l; - + if(compilePattern(line)==aitFalse) return aitFalse; if(h.find(host,l)==0) { l->add(*this); @@ -210,12 +210,12 @@ aitBool gateAsEntry::init(const char* host, // Host name to deny aitBool gateAsEntry::compilePattern(int line) { const char *err; - + #ifdef USE_NEG_REGEXP negate_pattern = (pattern[0] == '!'); if (negate_pattern) pattern++; #endif - + #ifdef USE_PCRE int erroffset; pat_buff = pcre_compile(pattern, 0, &err, &erroffset, NULL); @@ -324,7 +324,7 @@ gateAs::gateAs(const char* lfile, const char* afile) if(initialize(afile)) fprintf(stderr,"Failed to install access security file %s\n",afile); } - + readPvList(lfile); } @@ -335,7 +335,7 @@ gateAs::~gateAs(void) // security including the pvlist. tsSLIter pi = host_list.firstIter(); gateAsList * l = NULL; - + gateAsHost *pNode; while(pi.pointer()) { pNode=pi.pointer(); @@ -345,7 +345,7 @@ gateAs::~gateAs(void) } clearHostList(host_list); #endif - + clearAsList(deny_list); clearAsList(allow_list); clearAsList(line_list); @@ -386,20 +386,20 @@ void gateAs::clearHostList(gateHostList& list) gateAsEntry* gateAs::findEntryInList(const char* pv, gateAsList& list) const { tsSLIter pi = list.firstIter(); - + while(pi.pointer()) { int len = (int) strlen(pv); #ifdef USE_PCRE pi->substrings=pcre_exec(pi->pat_buff, NULL, pv, len, 0, PCRE_ANCHORED, pi->ovector, 30); if((pi->substrings>=0 && pi->ovector[1] == len) -#ifdef USE_NEG_REGEXP +#ifdef USE_NEG_REGEXP ^ pi->negate_pattern #endif ) break; #else if((re_match(&pi->pat_buff, pv, len, 0, &pi->regs) == len) -#ifdef USE_NEG_REGEXP +#ifdef USE_NEG_REGEXP ^ pi->negate_pattern #endif ) break; @@ -415,8 +415,8 @@ int gateAs::readPvList(const char* lfile) int line=0; FILE* fd; char inbuf[GATE_MAX_PVLIST_LINE_LENGTH]; -#ifdef USE_DENYFROM - char inbufWithIPs[GATE_MAX_PVLIST_LINE_LENGTH]; +#ifdef USE_DENYFROM + char inbufWithIPs[GATE_MAX_PVLIST_LINE_LENGTH]; char tempInbuf[GATE_MAX_PVLIST_LINE_LENGTH]; #endif const char *pattern,*rname,*hname; @@ -430,11 +430,7 @@ int gateAs::readPvList(const char* lfile) if(lfile) { errno=0; -#ifdef RESERVE_FOPEN_FD - fd=global_resources->fopen(lfile,"r"); -#else fd=fopen(lfile,"r"); -#endif if(fd == NULL) { fprintf(stderr,"Failed to open PV list file %s\n",lfile); fflush(stderr); @@ -446,51 +442,51 @@ int gateAs::readPvList(const char* lfile) // Create a ".* allow" rule if no file is specified pe = new gateAsEntry(".*",NULL,default_group,1); if(pe->init(allow_list,line)==aitFalse) delete pe; - + return 0; } - + // Read all PV file lines while(fgets(inbuf,sizeof(inbuf),fd)) { - - + + ++line; - pattern=rname=hname=NULL; - -#ifdef USE_DENYFROM - //All deny from rules with host names will be conveted to ip addresses + pattern=rname=hname=NULL; + +#ifdef USE_DENYFROM + //All deny from rules with host names will be conveted to ip addresses strncpy(tempInbuf,inbuf,strlen(inbuf)); tempInbuf[strlen(inbuf)-1]='\0'; - - if((ptr=strchr(inbuf,'#'))) *ptr='\0'; // Take care of comments - + + if((ptr=strchr(inbuf,'#'))) *ptr='\0'; // Take care of comments + if(!(pattern=strtok(inbuf," \t\n"))) continue; - + if(!(cmd=strtok(NULL," \t\n"))) { fprintf(stderr,"Error in PV list file (line %d): " "missing command\n",line); continue; } - if(strcasecmp(cmd,"DENY")==0) { + if(strcasecmp(cmd,"DENY")==0) { // Arbitrary number of arguments: [from] host names if((hname=strtok(NULL,", \t\n")) && strcasecmp(hname,"FROM")==0) hname=strtok(NULL,", \t\n"); if(hname) { // host pattern(s) present struct sockaddr_in sockAdd; - struct sockaddr_in* pSockAdd; - char hostname[GATE_MAX_HOSTNAME_LENGTH]; - int status; + struct sockaddr_in* pSockAdd; + char hostname[GATE_MAX_HOSTNAME_LENGTH]; + int status; char *ch; char *pIPInput; - + pSockAdd = &sockAdd; - + strncpy(inbufWithIPs,tempInbuf,hname - inbuf); - + pIPInput = inbufWithIPs + (hname - inbuf); - do { - /*convert all host names to ip addresses*/ + do { + /*convert all host names to ip addresses*/ status = aToIPAddr(hname,0,pSockAdd); if(status != -1) @@ -498,7 +494,7 @@ int gateAs::readPvList(const char* lfile) ipAddrToDottedIP(pSockAdd,hostname,sizeof(hostname)); ch=strchr(hostname,':'); if(ch != NULL) hostname[ch-hostname]=0; - + strncpy(pIPInput,hostname,strlen(hostname)); *(pIPInput+strlen(hostname)) = ' '; pIPInput = pIPInput + strlen(hostname) + 1; @@ -506,37 +502,37 @@ int gateAs::readPvList(const char* lfile) else{ fprintf(stderr,"Error in PV list file (line %d): " "cannot resolve host name >%s<\n",line,hname); - } + } } while((hname=strtok(NULL,", \t\n"))); - + *(pIPInput)='\0'; - + }else { strncpy(inbufWithIPs,tempInbuf,strlen(tempInbuf)); inbufWithIPs[strlen(tempInbuf)]='\0'; } - + }else { strncpy(inbufWithIPs,tempInbuf,strlen(tempInbuf)); inbufWithIPs[strlen(tempInbuf)]='\0'; } - pl=new gateAsLine(inbufWithIPs,strlen(inbufWithIPs),line_list); + pl=new gateAsLine(inbufWithIPs,strlen(inbufWithIPs),line_list); #else - if((ptr=strchr(inbuf,'#'))) *ptr='\0'; // Take care of comments + if((ptr=strchr(inbuf,'#'))) *ptr='\0'; // Take care of comments pl=new gateAsLine(inbuf,strlen(inbuf),line_list); #endif if(!(pattern=strtok(pl->buf," \t\n"))) continue; - + if(!(cmd=strtok(NULL," \t\n"))) { fprintf(stderr,"Error in PV list file (line %d): " "missing command\n",line); continue; - } - + } + #ifdef USE_DENYFROM if(strcasecmp(cmd,"DENY")==0) { // DENY [FROM] @@ -577,7 +573,7 @@ int gateAs::readPvList(const char* lfile) continue; } #endif - + if(strcasecmp(cmd,"ORDER")==0) { // ORDER // Arguments: "allow, deny" or "deny, allow" if(!(hname=strtok(NULL,", \t\n")) || @@ -598,7 +594,7 @@ int gateAs::readPvList(const char* lfile) } continue; } - + if(strcasecmp(cmd,"ALIAS")==0) { // ALIAS extra arg // Additional (first) argument: real PV name if(!(rname=strtok(NULL," \t\n"))) { @@ -607,7 +603,7 @@ int gateAs::readPvList(const char* lfile) continue; } } - + if((asg=strtok(NULL," \t\n"))) { // ASG / ASL if((asl=strtok(NULL," \t\n")) && (sscanf(asl,"%d",&lev)!=1)) lev=1; @@ -615,7 +611,7 @@ int gateAs::readPvList(const char* lfile) asg=(char*)default_group; lev=1; } - + if(strcasecmp(cmd,"ALLOW")==0 || // ALLOW / ALIAS strcasecmp(cmd,"ALIAS")==0 || strcasecmp(cmd,"PATTERN")==0 || @@ -629,12 +625,8 @@ int gateAs::readPvList(const char* lfile) "invalid command '%s'\n",line,cmd); } } - -#ifdef RESERVE_FOPEN_FD - global_resources->fclose(fd); -#else + fclose(fd); -#endif return 0; } @@ -646,14 +638,10 @@ long gateAs::initialize(const char* afile) fprintf(stderr,"Access security rules already installed\n"); return -1; } - + if(afile) { errno=0; -#ifdef RESERVE_FOPEN_FD - rules_fd=global_resources->fopen(afile,"r"); -#else rules_fd=fopen(afile,"r"); -#endif if(rules_fd == NULL) { // Open failed fprintf(stderr,"Failed to open security file: %s\n",afile); @@ -672,11 +660,7 @@ long gateAs::initialize(const char* afile) // Open succeeded rc=asInitialize(::readFunc); if(rc) fprintf(stderr,"Failed to read security file: %s\n",afile); -#ifdef RESERVE_FOPEN_FD - global_resources->fclose(rules_fd); -#else fclose(rules_fd); -#endif } } else { // afile is NULL @@ -684,7 +668,7 @@ long gateAs::initialize(const char* afile) rc=asInitialize(::readFunc); if(rc) fprintf(stderr,"Failed to set default security rules\n"); } - + if(rc==0) rules_installed=aitTrue; return rc; } @@ -725,7 +709,7 @@ long gateAs::reInitialize(const char* afile, const char* lfile) if(initialize(afile)) fprintf(stderr,"Failed to install access security file %s\n",afile); } - + // Restart INP PV clients gateAsCa(); @@ -745,7 +729,7 @@ int gateAs::readFunc(char* buf, size_t max) if(rptr==NULL) { rbuf[0]='\0'; rptr=rbuf; - + if(use_default_rules==aitTrue) { if(one_pass==aitFalse) { strcpy(rbuf,"ASG(DEFAULT) { RULE(1,READ) }"); @@ -757,17 +741,17 @@ int gateAs::readFunc(char* buf, size_t max) n=0; } } - + l=strlen(rptr); n = (l <= max) ? l : max; if(n) { memcpy(buf,rptr,n); rptr+=n; } - + if(rptr[0]=='\0') rptr=NULL; - + return (int) n; } @@ -775,7 +759,7 @@ void gateAs::report(FILE* fd) { time_t t; time(&t); - + fprintf(fd,"---------------------------------------------------------------------------\n" "Configuration Report: %s",ctime(&t)); fprintf(fd,"\n============================ Allowed PV Report ============================\n"); @@ -789,7 +773,7 @@ void gateAs::report(FILE* fd) else fprintf(fd,"\n"); pi1++; } - + fprintf(fd,"\n============================ Denied PV Report ============================\n"); tsSLIter pi2 = deny_list.firstIter(); gateAsEntry *pEntry2; @@ -801,7 +785,7 @@ void gateAs::report(FILE* fd) pi2++; } } - + #ifdef USE_DENYFROM tsSLIter pi3 = host_list.firstIter(); gateAsHost *pEntry3; @@ -821,16 +805,16 @@ void gateAs::report(FILE* fd) pi3++; } #endif - + if(eval_order==GATE_DENY_FIRST) fprintf(fd,"\nEvaluation order: deny, allow\n"); else fprintf(fd,"\nEvaluation order: allow, deny\n"); - + if(rules_installed==aitTrue) fprintf(fd,"Access Rules are installed.\n"); if(use_default_rules==aitTrue) fprintf(fd,"Using default access rules.\n"); -#if (EPICS_REVISION == 14 && EPICS_MODIFICATION >= 6) || EPICS_REVISION > 14 +#if (EPICS_VERSION > 3 || (EPICS_REVISION == 14 && EPICS_MODIFICATION >= 6) || EPICS_REVISION > 14) // Dumping to a file pointer became available sometime during 3.14.5. fprintf(fd,"\n============================ Access Security Dump =========================\n"); asDumpFP(fd,NULL,NULL,TRUE); diff --git a/src/gateAs.h b/src/gateAs.h index 28f6505..374ecc3 100644 --- a/src/gateAs.h +++ b/src/gateAs.h @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef _GATEAS_H_ @@ -36,7 +36,7 @@ extern "C" { #include "asLib.h" #include "errMdef.h" #include "gpHash.h" -#include "asTrapWrite.h" +#include "asTrapWrite.h" } // KE: Put these here to avoid redefining RE_DUP_MAX as defined in @@ -90,7 +90,7 @@ class gateAsLine; typedef tsSLList gateAsList; typedef tsSLList gateLineList; -// ----------------- AS host (to build up host list) ------------------ +// ----------------- AS host (to build up host list) ------------------ #ifdef USE_DENYFROM class gateAsHost; @@ -105,7 +105,7 @@ class gateAsHost : public tsSLNode }; #endif -// ------------ AS entry (deny or deny from or alias or allow) ------------ +// ------------ AS entry (deny or deny from or alias or allow) ------------ class gateAsEntry : public tsSLNode { @@ -120,9 +120,9 @@ class gateAsEntry : public tsSLNode int line); #endif long removeMember(void); - + void getRealName(const char* pv, char* real, int len); - + const char* pattern; const char* alias; const char* group; @@ -141,7 +141,7 @@ class gateAsEntry : public tsSLNode #ifdef USE_NEG_REGEXP bool negate_pattern; #endif - + private: aitBool compilePattern(int line); }; @@ -154,12 +154,12 @@ class gateAsClient gateAsClient(void); gateAsClient(gateAsEntry *pase, const char *user, const char *host); ~gateAsClient(void); - + aitBool readAccess(void) const { return (asclientpvt==NULL||asCheckGet(asclientpvt))?aitTrue:aitFalse; } aitBool writeAccess(void) const { return (asclientpvt&&asCheckPut(asclientpvt))?aitTrue:aitFalse; } - + gateAsEntry* getEntry(void) { return asentry; } @@ -170,20 +170,20 @@ class gateAsClient long changeInfo(const char* user, const char* host) { return asChangeClient(asclientpvt,asentry->level,(char*)user,(char*)host);} #endif - + const char *user(void) { return (const char*)asclientpvt->user; } const char *host(void) { return (const char*)asclientpvt->host; } ASCLIENTPVT clientPvt(void) { return asclientpvt; } - + void setUserFunction(void (*ufunc)(void*),void* uarg) { user_arg=uarg; user_func=ufunc; } - + private: ASCLIENTPVT asclientpvt; gateAsEntry* asentry; void* user_arg; void (*user_func)(void*); - + public: static void clientCallback(ASCLIENTPVT p, asClientStatus s); }; @@ -217,7 +217,7 @@ class gateAs #else inline gateAsEntry* findEntry(const char* pv); #endif - + int readPvList(const char* pvlist_file); void report(FILE*); long reInitialize(const char* as_file, const char* pvlist_file); @@ -239,9 +239,9 @@ class gateAs long initialize(const char* as_file); void clearAsList(gateAsList& list); void clearAsList(gateLineList& list); -#ifdef USE_DENYFROM +#ifdef USE_DENYFROM void clearHostList(gateHostList& list); -#endif +#endif gateAsEntry* findEntryInList(const char* pv, gateAsList& list) const; // These are static only so they can be used in readFunc callback @@ -258,10 +258,10 @@ class gateAs inline gateAsEntry* gateAs::findEntry(const char* pv, const char* host) { gateAsList* pl=NULL; - + if(host && deny_from_table.find(host,pl)==0 && // DENY FROM findEntryInList(pv, *pl)) return NULL; - + if(eval_order == GATE_ALLOW_FIRST && // DENY takes precedence findEntryInList(pv, deny_list)) return NULL; diff --git a/src/gateAsCa.cc b/src/gateAsCa.cc index d069192..20cbd28 100644 --- a/src/gateAsCa.cc +++ b/src/gateAsCa.cc @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ #define DEBUG_DELAY 0 @@ -44,7 +44,7 @@ static void connectCB(struct connection_handler_args arg) chid chid = arg.chid; ASGINP *pasginp = (ASGINP *)ca_puser(chid);; ASG *pasg = pasginp->pasg;; - + #if DEBUG_DELAY printf("gateAsCa-connectCB: ca_state=%d [cs_conn=%d] for %s\n", ca_state(chid),cs_conn,ca_name(chid)); @@ -70,7 +70,7 @@ static void eventCB(struct event_handler_args arg) chid chid = pcapvt->ch_id; int caStatus = arg.status; struct dbr_sts_double *pdata = (struct dbr_sts_double*)arg.dbr; - + #if DEBUG_DELAY printf("gateAsCa-eventCB: ca_state=%d [cs_conn=%d] for %s\n", ca_state(chid),cs_conn,ca_name(chid)); @@ -79,13 +79,13 @@ static void eventCB(struct event_handler_args arg) { --count; #if DEBUG_DELAY - printf(" !ready && !pcapvt->gotFirstEvent count=%d\n",count); + printf(" !ready && !pcapvt->gotFirstEvent count=%d\n",count); #endif pcapvt->gotFirstEvent=TRUE; } if(ca_state(chid)!=cs_conn || !ca_read_access(chid)) { #if DEBUG_DELAY - printf(" ca_state(chid)!=cs_conn || !ca_read_access(chid) count=%d\n",count); + printf(" ca_state(chid)!=cs_conn || !ca_read_access(chid) count=%d\n",count); #endif if(!(pasg->inpBad & (1<inpIndex))) { // was good so lets make it bad @@ -95,12 +95,12 @@ static void eventCB(struct event_handler_args arg) } else { if(caStatus!=ECA_NORMAL) { #if DEBUG_DELAY - printf(" caStatus!=ECA_NORMAL count=%d\n",count); + printf(" caStatus!=ECA_NORMAL count=%d\n",count); #endif epicsPrintf("asCa: eventCallback error %s\n",ca_message(caStatus)); } else { #if DEBUG_DELAY - printf(" caStatus == ECA_NORMAL count=%d\n",count); + printf(" caStatus == ECA_NORMAL count=%d\n",count); #endif pcapvt->rtndata = *pdata; // structure copy if(pdata->severity==INVALID_ALARM) { @@ -124,11 +124,11 @@ void gateAsCa(void) ASGINP *pasginp; CAPVT *pcapvt; time_t cur_time; - + ready=0; count=0; time(&start_time); - + // CA must be initialized by this time - hackery if(!pasbase) { fprintf(stderr,"%s gateAsCa: Invalid access security\n", @@ -146,47 +146,29 @@ void gateAsCa(void) pcapvt=(CAPVT*)pasginp->capvt; ++count; gateDebug1(11,"Access security searching for %s\n",pasginp->inp); - + // Note calls gateAsCB immediately called for local Pvs -#ifdef USE_313 - int status=ca_search_and_connect(pasginp->inp,&pcapvt->ch_id, - connectCB,pasginp); - if(status != ECA_NORMAL) { - fprintf(stderr,"%s gateAsCa: ca_search_and_connect failed:\n" - " %s\n",timeStamp(),ca_message(status)); - } -#else int status=ca_create_channel(pasginp->inp,connectCB,pasginp, CA_PRIORITY_DEFAULT,&pcapvt->ch_id); if(status != ECA_NORMAL) { fprintf(stderr,"%s gateAsCa: ca_create_channel failed:\n" " %s\n",timeStamp(),ca_message(status)); } -#endif - + // Note calls eventCB immediately called for local Pvs -#ifdef USE_313 - status=ca_add_event(DBR_STS_DOUBLE,pcapvt->ch_id, - eventCB,pasginp,0); - if(status != ECA_NORMAL) { - fprintf(stderr,"%s gateAsCa: ca_add_event failed:\n" - " %s\n",timeStamp(),ca_message(status)); - } -#else status=ca_create_subscription(DBR_STS_DOUBLE,1,pcapvt->ch_id, DBE_VALUE|DBE_ALARM,eventCB,pasginp,NULL); if(status != ECA_NORMAL) { fprintf(stderr,"%s gateAsCa: ca_create_subscription failed:\n" " %s\n",timeStamp(),ca_message(status)); } -#endif - + pasginp=(ASGINP*)ellNext((ELLNODE*)pasginp); } pasg=(ASG*)ellNext((ELLNODE*)pasg); } time(&cur_time); - + while(count>0 && (cur_time-start_time)<4) { ca_pend_event(1.0); @@ -222,7 +204,7 @@ void gateAsCa(void) connectedCount,totalCount); } fflush(stdout); - + // We are now ready for the eventCBs to do asComputeAsg. (Put // this before the call to asComputeAllAsg in case asComputeAllAsg // blocks an eventCB so that it finishes after asComputeAllAsg. @@ -237,7 +219,7 @@ void gateAsCaClear(void) ASG *pasg; ASGINP *pasginp; CAPVT *pcapvt; - + if(!pasbase) { fprintf(stderr,"gateAsCaClear: Invalid access security\n"); return; @@ -250,7 +232,7 @@ void gateAsCaClear(void) { pasg->inpBad |= (1<inpIndex); pcapvt=(CAPVT*)pasginp->capvt; - + gateDebug1(11,"Access security clearing channel %s\n",pasginp->inp); if (pcapvt->ch_id) { int status=ca_clear_channel(pcapvt->ch_id); diff --git a/src/gateAsyncIO.cc b/src/gateAsyncIO.cc index c75986d..d641809 100644 --- a/src/gateAsyncIO.cc +++ b/src/gateAsyncIO.cc @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ /*+********************************************************************* @@ -53,8 +53,8 @@ gateAsyncW::~gateAsyncW(void) listRemove (); } -smartConstGDDPointer gateAsyncW::extractDD () -{ +smartConstGDDPointer gateAsyncW::extractDD () +{ smartConstGDDPointer pDD; if ( _pDD.valid () ) { pDD.swap ( _pDD ); diff --git a/src/gateAsyncIO.h b/src/gateAsyncIO.h index bec86c7..dc386d6 100644 --- a/src/gateAsyncIO.h +++ b/src/gateAsyncIO.h @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef _GATEASYNCIO_H_ #define _GATEASYNCIO_H_ @@ -78,16 +78,16 @@ class gateAsyncR : public casAsyncReadIO, public tsDLNode class gateAsyncW : public casAsyncWriteIO, public tsDLNode { public: - gateAsyncW (const casCtx &ctx, + gateAsyncW (const casCtx &ctx, const gdd& wdd, bool isPutNotify ) : casAsyncWriteIO(ctx), _pList(0), _pDD(wdd), - _isPutNotify(isPutNotify) {} - + _isPutNotify(isPutNotify) {} + virtual ~gateAsyncW(void); - - smartConstGDDPointer extractDD (); + + smartConstGDDPointer extractDD (); bool isPutNotify () const { return _isPutNotify; } void listAdd ( tsDLList & list ); void listRemove (); diff --git a/src/gatePv.cc b/src/gatePv.cc index 9190ea5..b4e4166 100644 --- a/src/gatePv.cc +++ b/src/gatePv.cc @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ /*+********************************************************************* @@ -95,10 +95,10 @@ extern "C" { } extern void logEventCB(EVENT_ARGS args) { // log event callback gatePvData::logEventCB(args); - } + } extern void propEventCB(EVENT_ARGS args) { // prop event callback gatePvData::propEventCB(args); - } + } } // quick access to global_resources @@ -297,12 +297,8 @@ void gatePvData::init(gateServer* m,gateAsEntry* pase, const char* name) status=-1; else { -#ifdef USE_313 - status=ca_search_and_connect(pv_name,&chID,::connectCB,this); -#else status=ca_create_channel(pv_name,::connectCB,this, CA_PRIORITY_DEFAULT,&chID); -#endif if(status != ECA_NORMAL) { fprintf(stderr,"gatePvData::init: ca_search_and_connect for %s:\n" " %s\n", @@ -333,7 +329,7 @@ void gatePvData::init(gateServer* m,gateAsEntry* pase, const char* name) #endif status=-1; } - + if(status) { // what do I do here? Nothing for now, let creator fix trouble @@ -353,7 +349,7 @@ void gatePvData::init(gateServer* m,gateAsEntry* pase, const char* name) #endif } - + #if OMIT_CHECK_EVENT #else checkEvent(); // do ca_pend_event @@ -369,9 +365,9 @@ int gatePvData::activate(gateVcData* vcd) { gateDebug2(5,"gatePvData::activate(gateVcData=%p) name=%s\n", (void *)vcd,name()); - + int rc=-1; - + #if DEBUG_DELAY if(!strncmp("Xorbit",name(),6)) { printf("%s gatePvData::activate: %s state=%d\n",timeStamp(),name(), @@ -675,17 +671,6 @@ int gatePvData::unmonitor(void) if(monitored()) { -#ifdef USE_313 - rc=ca_clear_event(evID); - if(rc != ECA_NORMAL) { - fprintf(stderr,"%s gatePvData::unmonitor: ca_clear_event failed " - "for %s:\n" - " %s\n", - timeStamp(),name()?name():"Unknown",ca_message(rc)); - } else { - rc=0; - } -#else rc=ca_clear_subscription(evID); if(rc != ECA_NORMAL) { fprintf(stderr,"%s gatePvData::unmonitor: ca_clear_subscription failed " @@ -695,7 +680,6 @@ int gatePvData::unmonitor(void) } else { rc=0; } -#endif markNotMonitored(); } return rc; @@ -708,17 +692,6 @@ int gatePvData::logUnmonitor(void) if(logMonitored()) { -#ifdef USE_313 - rc=ca_clear_event(logID); - if(rc != ECA_NORMAL) { - fprintf(stderr,"%s gatePvData::logUnmonitor: ca_clear_event failed " - "for %s:\n" - " %s\n", - timeStamp(),name()?name():"Unknown",ca_message(rc)); - } else { - rc=0; - } -#else rc=ca_clear_subscription(logID); if(rc != ECA_NORMAL) { fprintf(stderr,"%s gatePvData::logUnmonitor: ca_clear_subscription failed " @@ -728,7 +701,6 @@ int gatePvData::logUnmonitor(void) } else { rc=0; } -#endif markLogNotMonitored(); } return rc; @@ -741,17 +713,6 @@ int gatePvData::propUnmonitor(void) if(propMonitored()) { -#ifdef USE_313 - rc=ca_clear_event(propID); - if(rc != ECA_NORMAL) { - fprintf(stderr,"%s gatePvData::propUnmonitor: ca_clear_event failed " - "for %s:\n" - " %s\n", - timeStamp(),name()?name():"Unknown",ca_message(rc)); - } else { - rc=0; - } -#else rc=ca_clear_subscription(propID); if(rc != ECA_NORMAL) { fprintf(stderr,"%s gatePvData::propUnmonitor: ca_clear_subscription failed " @@ -761,7 +722,6 @@ int gatePvData::propUnmonitor(void) } else { rc=0; } -#endif markPropNotMonitored(); } return rc; @@ -776,17 +736,6 @@ int gatePvData::alhUnmonitor(void) if(alhMonitored()) { -#ifdef USE_313 - rc=ca_clear_event(alhID); - if(rc != ECA_NORMAL) { - fprintf(stderr,"%s gatePvData::alhUnmonitor: ca_clear_event failed " - "for %s:\n" - " %s\n", - timeStamp(),name()?name():"Unknown",ca_message(rc)); - } else { - rc=0; - } -#else rc=ca_clear_subscription(alhID); if(rc != ECA_NORMAL) { fprintf(stderr,"%s gatePvData::alhUnmonitor: " @@ -796,7 +745,6 @@ int gatePvData::alhUnmonitor(void) } else { rc=0; } -#endif markAlhNotMonitored(); } return rc; @@ -822,24 +770,6 @@ int gatePvData::monitor(void) if(ca_read_access(chID)) { gateDebug1(5,"gatePvData::monitor() type=%ld\n",eventType()); -#ifdef USE_313 - rc=ca_add_masked_array_event(eventType(),0,chID,::eventCB,this, - 0.0,0.0,0.0,&evID,GR->eventMask()); - if(rc != ECA_NORMAL) { - fprintf(stderr,"%s gatePvData::monitor: " - "ca_add_masked_array_event failed for %s:\n" - " %s\n", - timeStamp(),name()?name():"Unknown",ca_message(rc)); - rc=-1; - } else { - rc=0; - markMonitored(); -#if OMIT_CHECK_EVENT -#else - checkEvent(); -#endif - } -#else rc=ca_create_subscription(eventType(),0,chID,GR->eventMask(), ::eventCB,this,&evID); if(rc != ECA_NORMAL) { @@ -856,7 +786,6 @@ int gatePvData::monitor(void) checkEvent(); #endif } -#endif } else { rc=-1; } @@ -884,24 +813,6 @@ int gatePvData::logMonitor(void) if(ca_read_access(chID)) { gateDebug1(5,"gatePvData::logMonitor() type=%ld\n",eventType()); -#ifdef USE_313 - rc=ca_add_masked_array_event(eventType(),0,chID,::logEventCB,this, - 0.0,0.0,0.0,&logID,DBE_LOG); - if(rc != ECA_NORMAL) { - fprintf(stderr,"%s gatePvData::logMonitor: " - "ca_add_masked_array_event failed for %s:\n" - " %s\n", - timeStamp(),name()?name():"Unknown",ca_message(rc)); - rc=-1; - } else { - rc=0; - markLogMonitored(); -#if OMIT_CHECK_EVENT -#else - checkEvent(); -#endif - } -#else rc=ca_create_subscription(eventType(),0,chID,DBE_LOG, ::logEventCB,this,&logID); if(rc != ECA_NORMAL) { @@ -918,7 +829,6 @@ int gatePvData::logMonitor(void) checkEvent(); #endif } -#endif } else { rc=-1; } @@ -946,24 +856,6 @@ int gatePvData::propMonitor(void) if(ca_read_access(chID)) { gateDebug1(5,"gatePvData::propMonitor() type=%ld\n",dataType()); -#ifdef USE_313 - rc=ca_add_masked_array_event(dataType(),0,chID,::propEventCB,this, - 0.0,0.0,0.0,&propID,DBE_PROPERTY); - if(rc != ECA_NORMAL) { - fprintf(stderr,"%s gatePvData::propMonitor: " - "ca_add_masked_array_event failed for %s:\n" - " %s\n", - timeStamp(),name()?name():"Unknown",ca_message(rc)); - rc=-1; - } else { - rc=0; - markpropMonitored(); -#if OMIT_CHECK_EVENT -#else - checkEvent(); -#endif - } -#else rc=ca_create_subscription(dataType(),0,chID,DBE_PROPERTY, ::propEventCB,this,&propID); if(rc != ECA_NORMAL) { @@ -980,7 +872,6 @@ int gatePvData::propMonitor(void) checkEvent(); #endif } -#endif } else { rc=-1; } @@ -998,24 +889,6 @@ int gatePvData::alhMonitor(void) if(ca_read_access(chID)) { gateDebug1(5,"gatePvData::alhMonitor() type=%d\n",DBR_STSACK_STRING); -#ifdef USE_313 - rc=ca_add_masked_array_event(DBR_STSACK_STRING,0,chID,::alhCB,this, - 0.0,0.0,0.0,&alhID,DBE_ALARM); - if(rc != ECA_NORMAL) { - fprintf(stderr,"%s gatePvData::alhMonitor: " - "ca_add_masked_array_event failed for %s:\n" - " %s\n", - timeStamp(),name()?name():"Unknown",ca_message(rc)); - rc=-1; - } else { - rc=0; - markAlhMonitored(); -#if OMIT_CHECK_EVENT -#else - checkEvent(); -#endif - } -#else rc=ca_create_subscription(DBR_STSACK_STRING,0,chID,DBE_ALARM, ::alhCB,this,&alhID); if(rc != ECA_NORMAL) { @@ -1032,7 +905,6 @@ int gatePvData::alhMonitor(void) checkEvent(); #endif } -#endif } else { rc=-1; } @@ -1044,20 +916,20 @@ int gatePvData::get(readType read_type) { gateDebug1(5,"gatePvData::get() name=%s\n",name()); int rc=ECA_NORMAL; - + // only one active get allowed at once switch(getState()) { case gatePvActive: gateDebug1(3,"gatePvData::get() %s PV\n",getStateName()); - + if(global_resources->getCacheMode()) /* caching enabled */ { if(!pendingCtrlGet()) { gateDebug1(3,"gatePvData::get() CACHE doing ca_array_get_callback of type CTRL (%ld)\n",dataType()); setTransTime(); - markCtrlGetPending(); + markCtrlGetPending(); rc=ca_array_get_callback(dataType(), 1/*totalElements()*/, chID,::getCB,this); if(rc != ECA_NORMAL) { @@ -1078,7 +950,7 @@ int gatePvData::get(readType read_type) if(global_resources->getMaxBytes() >= (unsigned long)(bytes*totalElements()+sizeof(caHdr) + 2 * sizeof ( ca_uint32_t ))){ gateDebug1(3,"gatePvData::get() NO_CACHE doing ca_array_get_callback of type CTRL (%ld)\n",dataType()); setTransTime(); - markCtrlGetPending(); + markCtrlGetPending(); rc = ca_array_get_callback(dataType(), 0, chID, ::getCB, this); if(rc != ECA_NORMAL) { fprintf(stderr,"%s gatePvData::get: ca_array_get_callback " @@ -1093,8 +965,8 @@ int gatePvData::get(readType read_type) "Set EPICS_CA_MAX_ARRAY_BYTES to at least %lu\n", timeStamp(),name()?name():"Unknown", (unsigned long) bytes*totalElements() + sizeof(caHdr) + 2*sizeof(ca_uint32_t)); - } - } + } + } } else { @@ -1103,7 +975,7 @@ int gatePvData::get(readType read_type) if(global_resources->getMaxBytes() >= (unsigned long)(bytes*totalElements()+sizeof(caHdr) + 2 * sizeof ( ca_uint32_t ))){ gateDebug1(3,"gatePvData::get() NO_CACHE doing ca_array_get_callback of type TIME (%ld)\n", eventType()); setTransTime(); - markTimeGetPending(); + markTimeGetPending(); rc = ca_array_get_callback(eventType(), 0, chID, ::getTimeCB, this); if(rc != ECA_NORMAL) { fprintf(stderr,"%s gatePvData::get: ca_array_get_callback for DBR_TIME " @@ -1119,8 +991,8 @@ int gatePvData::get(readType read_type) timeStamp(),name()?name():"Unknown", (unsigned long) bytes*totalElements() + sizeof(caHdr) + 2*sizeof(ca_uint32_t)); } - } - } + } + } #if OMIT_CHECK_EVENT @@ -1179,7 +1051,7 @@ int gatePvData::put(const gdd & dd, class gateAsyncW * pWIO ) fieldType(),dbr_type_to_text(fieldType()), ca_name(chID)); #endif - + switch(getState()) { case gatePvActive: @@ -1238,7 +1110,7 @@ int gatePvData::put(const gdd & dd, class gateAsyncW * pWIO ) #if DEBUG_PUT printf("gatePvData::put: cbid=%p this=%p dbr=%ld id=%ld pv=%p\n", cbid,this,cht,cbid->getID(),cbid->getPV()); -#endif +#endif if(!cbid) return S_casApp_noMemory; callback_list.add(*cbid); #if DEBUG_SLIDER @@ -1362,8 +1234,8 @@ void gatePvData::connectCB(CONNECT_ARGS args) #endif // send message to user concerning connection - if(ca_state(args.chid)==cs_conn) -{ + if(ca_state(args.chid)==cs_conn) + { gateDebug0(9,"gatePvData::connectCB() connection ok\n"); switch(ca_field_type(args.chid)) @@ -1430,7 +1302,7 @@ void gatePvData::connectCB(CONNECT_ARGS args) "Unhandled field type[%s] for %s\n", dbr_type_to_text(ca_field_type(args.chid)), ca_name(args.chid)); -#endif +#endif pv->event_type=(chtype)-1; pv->data_type=(chtype)-1; pv->event_func=(gateCallback)NULL; @@ -1489,12 +1361,12 @@ void gatePvData::putCB(EVENT_ARGS args) #if DEBUG_PUT printf("gatePvData::putCB: cbid=%p user=%p id=%ld pv=%p\n", cbid,ca_puser(args.chid),cbid->getID(),cbid->getPV()); -#endif - +#endif + // We are through with the callback id. Remove it from the // callback_list and delete it. pv->callback_list.remove(*cbid); - gateAsyncW * pWIO = reinterpret_cast < gateAsyncW * > + gateAsyncW * pWIO = reinterpret_cast < gateAsyncW * > ( cbid->getPrivatePtr () ); delete cbid; @@ -1509,7 +1381,7 @@ void gatePvData::putCB(EVENT_ARGS args) pv->vc->putCB(args.status,*pWIO); } -// This is the callback registered with ca_add_subscription in the +// This is the callback registered with ca_create_subscription in the // monitor routine. If conditions are right, it calls the routines // that copy the data into the GateVcData's event_data. void gatePvData::eventCB(EVENT_ARGS args) @@ -1567,7 +1439,7 @@ void gatePvData::eventCB(EVENT_ARGS args) } else { - + if(global_resources->getArchiveMode()){ // Post the event if(stat_sevr_changed) @@ -1580,7 +1452,7 @@ void gatePvData::eventCB(EVENT_ARGS args) if(stat_sevr_changed) pv->vc->vcPostEvent(pv->select_mask); else - pv->vc->vcPostEvent(pv->value_log_mask); + pv->vc->vcPostEvent(pv->value_log_mask); } } } @@ -1683,7 +1555,7 @@ void gatePvData::propEventCB(EVENT_ARGS args) if(pv->active()) { gateDebug2(5,"gatePvData::propEventCB() %s PV %d\n",pv->getStateName(), pv->propGetPending()); - if(pv->propGetPending()) { + if(pv->propGetPending()) { gateDebug1(5,"gatePvData::propEventCB() Ignore first event %s PV\n",pv->getStateName()); pv->markPropNoGetPending(); return; @@ -1830,14 +1702,14 @@ void gatePvData::getCB(EVENT_ARGS args) dd = pv->runValueDataCB(&args); if (dd) pv->vc->setEventData(dd); - } - if (pv->needAddRemove() && !pv->vc->needPosting()) { - gateDebug0(5,"gatePvData::getCB() need add/remove\n"); - pv->markAddRemoveNotNeeded(); - pv->vc->vcAdd(read_type); - } else - pv->vc->vcData(read_type); + if (pv->needAddRemove() && !pv->vc->needPosting()) { + gateDebug0(5, "gatePvData::getCB() need add/remove\n"); + pv->markAddRemoveNotNeeded(); + pv->vc->vcAdd(read_type); + } else + pv->vc->vcData(read_type); + } if (pv->vc->needPosting()) { // enable monitor only if requested gateDebug0(5,"gatePvData::getCB() [NO_CACHE] Enable value monitor\n"); @@ -1886,15 +1758,15 @@ void gatePvData::getTimeCB(EVENT_ARGS args) pv->markNoTimeGetPending(); if(args.status==ECA_NORMAL) { - - + + if(pv->active()) { gateDebug1(5,"gatePvData::getTimeCB() %s PV\n",pv->getStateName()); dd = pv->runEventCB(&args); if (dd) pv->vc->setEventData(dd); - + /* flush async get request */ if(pv->needAddRemove() && !pv->vc->needPosting()) { @@ -1903,26 +1775,26 @@ void gatePvData::getTimeCB(EVENT_ARGS args) pv->vc->vcAdd(read_type); } else - pv->vc->vcData(read_type); - + pv->vc->vcData(read_type); + if(pv->vc->needPosting() && !pv->monitored()) // do monitor only if requested { pv->monitor(); } - + if(pv->vc->needPosting() && // do archive monitor only if requested global_resources->getArchiveMode() && !pv->logMonitored() && - (pv->vc->client_mask == DBE_LOG)) { + (pv->vc->client_mask == DBE_LOG)) { gateDebug0(5,"gatePvData::getCB() Starting log monitor timecb\n"); - pv->logMonitor(); + pv->logMonitor(); } if(pv->vc->needPosting() && // do property monitor only if requested !pv->propMonitored() && - (pv->vc->client_mask == DBE_PROPERTY)) { + (pv->vc->client_mask == DBE_PROPERTY)) { gateDebug0(5,"gatePvData::getCB() Starting prop monitor timecb\n"); - pv->propMonitor(); + pv->propMonitor(); } } @@ -2362,9 +2234,9 @@ gdd* gatePvData::eventSTSAckStringCB(dbr_stsack_string *ts) // DBR_STSACK_STRING response // (the value gdd carries the severity and status information) - - // change type of value gdd to native type of pv - dd[gddAppTypeIndex_dbr_stsack_string_value].setPrimType(nativeType()); + + // change type of value gdd to native type of pv + dd[gddAppTypeIndex_dbr_stsack_string_value].setPrimType(nativeType()); dd[gddAppTypeIndex_dbr_stsack_string_ackt] = ts->ackt; dd[gddAppTypeIndex_dbr_stsack_string_acks] = ts->acks; diff --git a/src/gatePv.h b/src/gatePv.h index 42ce906..52c3fcd 100644 --- a/src/gatePv.h +++ b/src/gatePv.h @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef _GATEPV_H_ #define _GATEPV_H_ @@ -83,7 +83,7 @@ class gateAsEntry; class gatePvCallbackId : public tsDLNode { public: - gatePvCallbackId(unsigned long idIn, + gatePvCallbackId(unsigned long idIn, gatePvData *pvIn, void * pPrivateIn=0 ) : id(idIn),pv(pvIn),pPrivate(pPrivateIn) {}; unsigned long getID(void) const { return id; } @@ -106,25 +106,25 @@ class gatePvData ~gatePvData(void); typedef gdd* (gatePvData::*gateCallback)(EVENT_ARGS *); - + int active(void) const { return (pv_state==gatePvActive)?1:0; } int inactive(void) const { return (pv_state==gatePvInactive)?1:0; } int disconnected(void) const { return (pv_state==gatePvDisconnect)?1:0; } int dead(void) const { return (pv_state==gatePvDead)?1:0; } int pendingConnect(void) const { return (pv_state==gatePvConnect)?1:0; } - + int pendingCtrlGet(void) const { return (ctrl_get_state)?1:0; } int pendingTimeGet(void) const { return (time_get_state)?1:0; } int monitored(void) const { return (mon_state)?1:0; } - int logMonitored(void) const { return (log_mon_state)?1:0; } - int propMonitored(void) const { return (prop_mon_state)?1:0; } + int logMonitored(void) const { return (log_mon_state)?1:0; } + int propMonitored(void) const { return (prop_mon_state)?1:0; } int alhMonitored(void) const { return (alh_mon_state)?1:0; } int alhGetPending(void) const { return (alh_get_state)?1:0; } int propGetPending(void) const { return (prop_get_state)?1:0; } int logGetPending(void) const { return (log_get_state)?1:0; } int needAddRemove(void) const { return (complete_flag)?1:0; } int abort(void) const { return (abort_flag)?1:0; } - + const char* name(void) const { return pv_name; } gateVcData* VC(void) const { return vc; } gateAsEntry* getEntry(void) const { return asentry; } @@ -144,34 +144,34 @@ class gatePvData chtype eventType(void) const { return event_type; } void checkEvent(void) { ca_poll(); } double eventRate(void); - + int activate(gateVcData* from); // set to active (CAS connect) int deactivate(void); // set to inactive (CAS disconnect) int death(void); // set to not connected (CAC disconnect) int life(void); // set to connected (CAC connect) int monitor(void); // add monitor - int logMonitor(void); // add log monitor - int propMonitor(void); // add prop monitor + int logMonitor(void); // add log monitor + int propMonitor(void); // add prop monitor int unmonitor(void); // delete monitor - int logUnmonitor(void); // delete log monitor - int propUnmonitor(void); // delete prop monitor + int logUnmonitor(void); // delete log monitor + int propUnmonitor(void); // delete prop monitor int alhMonitor(void); // add alh info monitor int alhUnmonitor(void); // delete alh info monitor int get(readType read_type); // get callback int put(const gdd &, class gateAsyncW * ); // put - + time_t timeAlive(void) const; time_t timeActive(void) const; - time_t timeInactive(void) const; + time_t timeInactive(void) const; time_t timeConnecting(void) const; time_t timeDead(void) const; time_t timeDisconnected(void) const; - + #if 0 // KE: Unused time_t timeSinceLastTrans(void) const; #endif - + void setVC(gateVcData* t) { vc=t; } void setTransTime(void); void addET(const casCtx&); @@ -183,8 +183,8 @@ class gatePvData void markPropNoGetPending(void) { prop_get_state=0; } void markLogGetPending(void) { log_get_state=1; } void markLogNoGetPending(void) { log_get_state=0; } - - + + protected: void init(gateServer*,gateAsEntry *pase, const char* name); void initClear(void); @@ -201,8 +201,8 @@ class gatePvData void markNotMonitored(void) { mon_state=0; } void markLogMonitored(void) { log_mon_state=1; } void markPropMonitored(void) { prop_mon_state=1; } - void markLogNotMonitored(void) { log_mon_state=0; } - void markPropNotMonitored(void) { prop_mon_state=0; } + void markLogNotMonitored(void) { log_mon_state=0; } + void markPropNotMonitored(void) { prop_mon_state=0; } void markCtrlGetPending(void) { ctrl_get_state=1; } void markNoCtrlGetPending(void) { ctrl_get_state=0; } void markTimeGetPending(void) { time_get_state=1; } @@ -213,16 +213,16 @@ class gatePvData void markAddRemoveNotNeeded(void) { complete_flag=0; } void markAbort(void) { abort_flag=1; } void markNoAbort(void) { abort_flag=0; } - + void setState(gatePvState s) { pv_state=s; } - + gdd* runEventCB(EVENT_ARGS *pArgs) { return (this->*event_func)(pArgs); } gdd* runDataCB(EVENT_ARGS *pArgs) { return (this->*data_func)(pArgs); } gdd* runValueDataCB(EVENT_ARGS *pArgs) { return (this->*value_data_func)(pArgs); } - + tsDLList eio; // pending exist test list tsDLList callback_list; // callback list for puts - + gateServer* mrg; // The gateServer that manages this gatePvData gateVcData* vc; // Pointer to the associated gateVcData, NULL if none gateAsEntry* asentry; @@ -231,8 +231,8 @@ class gatePvData char* pv_name; // Name of the process variable chid chID; // Channel access ID evid evID; // Channel access event id - evid logID; // Channel access event id - evid propID; // Channel access event id + evid logID; // Channel access event id + evid propID; // Channel access event id evid alhID; // Channel access alh info event id chtype event_type; // DBR type associated with eventCB (event_data) chtype data_type; // DBR type associated with getCB (pv_data) @@ -243,10 +243,10 @@ class gatePvData gateCallback event_func; // Function called in eventCB for event_data gateCallback data_func; // Function called in getCB for pv_data gateCallback value_data_func; // Function called in getCB for pv_data - + int mon_state; // 0=not monitored, 1=is monitored - int log_mon_state; // 0=not log monitored, 1=is log monitored - int prop_mon_state; // 0=not prop monitored, 1=is prop monitored + int log_mon_state; // 0=not log monitored, 1=is log monitored + int prop_mon_state; // 0=not prop monitored, 1=is prop monitored int ctrl_get_state; // 0=no ctrl get pending, 1=ctrl get pending int time_get_state; // 0=no time get pending, 1=time get pending int alh_mon_state; // 0=alh info not monitored, 1=alh info is monitored @@ -255,19 +255,19 @@ class gatePvData int log_get_state; // 0=no log info get pending, 1=log info get pending int abort_flag; // true if activate-connect sequence should be aborted int complete_flag; // true if ADD/REMOVE required after completion - + time_t no_connect_time; // when no one connected to held PV time_t dead_alive_time; // when PV went from dead to alive time_t last_trans_time; // last transaction (put or get) occurred at this time - + //gjansa: until something better is found out unsigned int bytes; - + casEventMask select_mask; casEventMask alh_mask; casEventMask value_mask; casEventMask value_alarm_mask; - casEventMask value_log_mask; + casEventMask value_log_mask; // Callback functions used in eventCB gdd* eventStringCB(EVENT_ARGS *pArgs); @@ -287,7 +287,7 @@ class gatePvData gdd* dataDoubleCB(EVENT_ARGS *pArgs); gdd* dataCharCB(EVENT_ARGS *pArgs); gdd* dataLongCB(EVENT_ARGS *pArgs); - + // Callback functions used in getCB for value gdd* valueDataStringCB(EVENT_ARGS *pArgs); gdd* valueDataEnumCB(EVENT_ARGS *pArgs); @@ -302,7 +302,7 @@ class gatePvData static void accessCB(ACCESS_ARGS args); // access security callback static void eventCB(EVENT_ARGS args); // value-changed callback static void logEventCB(EVENT_ARGS args); // value-changed callback - static void propEventCB(EVENT_ARGS args); // value-changed callback + static void propEventCB(EVENT_ARGS args); // value-changed callback static void alhCB(EVENT_ARGS args); // alh info value-changed callback static void putCB(EVENT_ARGS args); // put callback static void getCB(EVENT_ARGS args); // get callback diff --git a/src/gateResources.cc b/src/gateResources.cc index ec34ae4..369ffe5 100644 --- a/src/gateResources.cc +++ b/src/gateResources.cc @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ // Author: Jim Kowalkowski // Date: 2/96 @@ -60,11 +60,11 @@ char *timeStamp(void) static char timeStampStr[20]; time_t now; struct tm *tblock; - + time(&now); tblock=localtime(&now); strftime(timeStampStr,sizeof(timeStampStr),"%b %d %H:%M:%S",tblock); - + return timeStampStr; } @@ -159,44 +159,62 @@ static int gddToVALUE(const gdd *gddVal, short ourdbrtype, VALUE *valueStruct) case OUR_DBR_CHAR: { aitInt8 x; gddVal->get(x); - valueStruct->v_char = x; + valueStruct->v_int8 = x; } return(0); case OUR_DBR_UCHAR: { aitUint8 x; gddVal->get(x); - valueStruct->v_uchar = x; + valueStruct->v_uint8 = x; } return(0); case OUR_DBR_SHORT: { aitInt16 x; gddVal->get(x); - valueStruct->v_short = x; + valueStruct->v_int16 = x; } return(0); case OUR_DBR_USHORT: { aitUint16 x; gddVal->get(x); - valueStruct->v_ushort = x; + valueStruct->v_uint16 = x; } return(0); case OUR_DBR_LONG: { aitInt32 x; gddVal->get(x); - valueStruct->v_long = x; + valueStruct->v_int32 = x; } return(0); case OUR_DBR_ULONG: { aitUint32 x; gddVal->get(x); - valueStruct->v_ulong = x; + valueStruct->v_uint32 = x; + } + return(0); + +#ifdef DBR_INT64 + case OUR_DBR_INT64: { + aitInt64 x; + gddVal->get(x); + valueStruct->v_int64 = x; } return(0); +#endif + +#ifdef DBR_UINT64 + case OUR_DBR_UINT64: { + aitUint64 x; + gddVal->get(x); + valueStruct->v_uint64 = x; + } + return(0); +#endif case OUR_DBR_FLOAT: { aitFloat32 x; @@ -229,26 +247,27 @@ static int gddToVALUE(const gdd *gddVal, short ourdbrtype, VALUE *valueStruct) } } +#if 0 static char *debugVALUEString(VALUE *v, int ourdbrtype, char *buffer) { switch (ourdbrtype) { case OUR_DBR_CHAR: - sprintf(buffer,"v_char %d",v->v_char); + sprintf(buffer,"v_int8 %d",v->v_int8); break; case OUR_DBR_UCHAR: - sprintf(buffer,"v_uchar %d",v->v_uchar); + sprintf(buffer,"v_uint8 %d",v->v_uint8); break; case OUR_DBR_SHORT: - sprintf(buffer,"v_short %hd",v->v_short); + sprintf(buffer,"v_int16 %hd",v->v_int16); break; case OUR_DBR_USHORT: - sprintf(buffer,"v_ushort %hu",v->v_ushort); + sprintf(buffer,"v_uint16 %hu",v->v_uint16); break; case OUR_DBR_LONG: - sprintf(buffer,"v_long %ld",v->v_long); + sprintf(buffer,"v_int32 %d",v->v_int32); break; case OUR_DBR_ULONG: - sprintf(buffer,"v_ulong %lu",v->v_ulong); + sprintf(buffer,"v_uint32 %u",v->v_uint32); break; case OUR_DBR_FLOAT: sprintf(buffer,"v_float %g",v->v_float); @@ -264,6 +283,7 @@ static char *debugVALUEString(VALUE *v, int ourdbrtype, char *buffer) } return(buffer); } +#endif #endif // WITH_CAPUTLOG @@ -274,19 +294,19 @@ gateResources::gateResources(void) access_file=strDup(GATE_PV_ACCESS_FILE); else access_file=NULL; - + if(access(GATE_PV_LIST_FILE,F_OK)==0) pvlist_file=strDup(GATE_PV_LIST_FILE); else pvlist_file=NULL; - + if(access(GATE_COMMAND_FILE,F_OK)==0) command_file=strDup(GATE_COMMAND_FILE); else command_file=NULL; - - - + + + // Miscellaneous initializations putlog_file=NULL; #ifdef WITH_CAPUTLOG @@ -297,21 +317,18 @@ gateResources::gateResources(void) debug_level=0; ro=0; serverMode=false; -#ifdef RESERVE_FOPEN_FD - reserveFp = NULL; -#endif - + setEventMask(DBE_VALUE | DBE_ALARM); setConnectTimeout(GATE_CONNECT_TIMEOUT); setInactiveTimeout(GATE_INACTIVE_TIMEOUT); setDeadTimeout(GATE_DEAD_TIMEOUT); setDisconnectTimeout(GATE_DISCONNECT_TIMEOUT); setReconnectInhibit(GATE_RECONNECT_INHIBIT); - + gddApplicationTypeTable& tt = gddApplicationTypeTable::AppTable(); - + gddMakeMapDBR(tt); - + appValue=tt.getApplicationType("value"); appUnits=tt.getApplicationType("units"); appEnum=tt.getApplicationType("enums"); @@ -427,6 +444,41 @@ void gateResources::caPutLog_Send } caPutLogTaskSend(pdata); } + +void gateResources::putLog( + FILE * fp, + const char * user, + const char * host, + const char * pvname, + const gdd * old_value, + const gdd * new_value ) +{ + if(fp) { + VALUE oldVal, newVal; + char acOldVal[20], acNewVal[20]; + if ( old_value == NULL ) + { + acOldVal[0] = '?'; + acOldVal[1] = '\0'; + } + else + { + gddToVALUE( old_value, gddGetOurType(old_value), &oldVal ); + VALUE_to_string( acOldVal, 20, &oldVal, gddGetOurType(old_value) ); + } + gddToVALUE( new_value, gddGetOurType(new_value), &newVal ); + VALUE_to_string( acNewVal, 20, &newVal, gddGetOurType(new_value) ); + fprintf(fp,"%s %s@%s %s %s old=%s\n", + timeStamp(), + user?user:"Unknown", + host?host:"Unknown", + pvname, + acNewVal, + acOldVal ); + fflush(fp); + } +} + #endif // WITH_CAPUTLOG int gateResources::setReportFile(const char* file) @@ -448,46 +500,6 @@ int gateResources::setUpAccessSecurity(void) return 0; } -#ifdef RESERVE_FOPEN_FD -// Functions to try to reserve a file descriptor to use for fopen. On -// Solaris, at least, fopen is limited to FDs < 256. These could all -// be used by CA and CAS sockets if there are connections to enough -// IOCs These functions try to reserve a FD < 256. -FILE *gateResources::fopen(const char *filename, const char *mode) -{ - // Close the dummy file holding the FD open - if(reserveFp) ::fclose(reserveFp); - reserveFp=NULL; - - // Open the file. It should use the lowest available FD, that is, - // the one we just made available. - FILE *fp=::fopen(filename,mode); - if(!fp) { - // Try to get the reserved one back - reserveFp=::fopen(GATE_RESERVE_FILE,"w"); - } - - return fp; -} - -int gateResources::fclose(FILE *stream) -{ - // Close the file - int ret=::fclose(stream); - - // Open the dummy file to reserve the FD just made available - reserveFp=::fopen(GATE_RESERVE_FILE,"w"); - - return ret; -} - -FILE *gateResources::openReserveFile(void) -{ - reserveFp=::fopen(GATE_RESERVE_FILE,"w"); - return reserveFp; -} -#endif - gateAs* gateResources::getAs(void) { if(as==NULL) setUpAccessSecurity(); diff --git a/src/gateResources.h b/src/gateResources.h index 94143f8..7d404db 100644 --- a/src/gateResources.h +++ b/src/gateResources.h @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Jim Kowalkowski * Date: 2/96 */ @@ -22,9 +22,6 @@ #define GATE_COMMAND_FILE "gateway.command" #define GATE_PUTLOG_FILE "gateway.putlog" #define GATE_REPORT_FILE "gateway.report" -#ifdef RESERVE_FOPEN_FD -# define GATE_RESERVE_FILE "gateway.reserve" -#endif #define GATE_CONNECT_TIMEOUT 1 #define GATE_INACTIVE_TIMEOUT (60*60*2) @@ -35,7 +32,7 @@ #define GATE_REALLY_SMALL 0.0000001 #define GATE_CONNECT_SECONDS 1 -#define GATE_MAX_PVNAME_LENGTH 64u +#define GATE_MAX_PVNAME_LENGTH 256u #define GATE_MAX_HOSTNAME_LENGTH 64u #define GATE_MAX_PVLIST_LINE_LENGTH 1024u @@ -104,12 +101,6 @@ class gateResources time_t disconnectTimeout(void) const { return disconnect_timeout; } time_t reconnectInhibit(void) const { return reconnect_inhibit; } -#ifdef RESERVE_FOPEN_FD - FILE *openReserveFile(void); - FILE *fopen(const char *filename, const char *mode); - int fclose(FILE *stream); -#endif - const char* listFile(void) const { return pvlist_file?pvlist_file:"NULL"; } const char* accessFile(void) const { return access_file?access_file:"NULL"; } const char* commandFile(void) const { return command_file?command_file:"NULL"; } @@ -121,15 +112,15 @@ class gateResources void setServerMode(bool mode) { serverMode=mode; } bool getServerMode(void) const { return serverMode; } - + void setCacheMode(bool mode) { cacheMode=mode; } bool getCacheMode(void) const { return cacheMode; } - + void setArchiveMode(bool mode) { archiveMode=mode; } - bool getArchiveMode(void) const { return archiveMode; } - + bool getArchiveMode(void) const { return archiveMode; } + void setMaxBytes(unsigned long bytes){ maxBytes=bytes; } - unsigned long getMaxBytes() const { return maxBytes; } + unsigned long getMaxBytes() const { return maxBytes; } gateAs* getAs(void); bool isAsSetUp(void) const { return as?true:false; } @@ -145,6 +136,12 @@ class gateResources const gdd *old_value, const gdd *new_value); void caPutLog_Term(void); + void putLog( FILE *fp, + const char *user, + const char *host, + const char *pvname, + const gdd *old_value, + const gdd *new_value); #endif // here for convenience @@ -173,9 +170,6 @@ class gateResources time_t disconnect_timeout,reconnect_inhibit; gateAs* as; FILE *putlogFp; -#ifdef RESERVE_FOPEN_FD - FILE *reserveFp; -#endif }; #ifndef GATE_RESOURCE_FILE diff --git a/src/gateServer.cc b/src/gateServer.cc index 16c02c8..b7a1f82 100644 --- a/src/gateServer.cc +++ b/src/gateServer.cc @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ /*+********************************************************************* @@ -90,11 +90,6 @@ #include -#ifdef SOLARIS -// Is in stdlib.h elsewhere, not available on WIN32 -#include -#endif - #ifdef _WIN32 #else # include @@ -246,7 +241,7 @@ void gateServer::mainLoop(void) #if defined(RATE_STATS) || defined(CAS_DIAGNOSTICS) // Start a default timer queue (true to use shared queue, false to // have a private one) - epicsTimerQueueActive &queue = + epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate(true); gateRateStatsTimer *statTimer = new gateRateStatsTimer(queue, RATE_STATS_INTERVAL, this); @@ -324,7 +319,7 @@ void gateServer::mainLoop(void) cleanTime=0.0; } #endif - + // Make sure messages get out fflush(stderr); fflush(stdout); @@ -379,7 +374,7 @@ void gateServer::mainLoop(void) } else { exit(0); } -#endif +#endif } else { // Doesn't have a server, just quit exit(0); @@ -427,11 +422,7 @@ void gateServer::gateCommands(const char* cfile) printf("%s Reading command file: %s\n",timeStamp(),cfile); errno=0; -#ifdef RESERVE_FOPEN_FD - fp=global_resources->fopen(cfile,"r"); -#else fp=fopen(cfile,"r"); -#endif if(fp == NULL) { fprintf(stderr,"%s Failed to open command file: %s\n", timeStamp(),cfile); @@ -459,14 +450,7 @@ void gateServer::gateCommands(const char* cfile) cmd=strtok(NULL," \t\n"); } } - - // Free the reserved file descriptor before we read access - // security or write reports -#ifdef RESERVE_FOPEN_FD - global_resources->fclose(fp); -#else fclose(fp); -#endif // Now do the commands if(r1Flag) { @@ -479,7 +463,7 @@ void gateServer::gateCommands(const char* cfile) } if(asFlag) { printf("%s Reading access security files\n",timeStamp()); - newAs(); + newAs(); fflush(stdout); } // Do the report after the new access security @@ -487,7 +471,7 @@ void gateServer::gateCommands(const char* cfile) report3(); fflush(stdout); } - + return; } @@ -502,7 +486,7 @@ void gateServer::newAs(void) printf("gateServer::newAs pv_list: %d con_list: %d\n", (int)pv_list.count(),(int)pv_con_list.count()); int count=0; -#endif +#endif // We need to eliminate all the members (gateAsEntry's) and // clients (gateAsClient's). The clients must be removed before @@ -545,7 +529,7 @@ void gateServer::newAs(void) if(!stat_table[i].pv) { #if DEBUG_ACCESS printf(" i=%d No pv for %s\n",i,stat_table[i].name); -#endif +#endif } else { // NULL the entry in the gateStat and its channels and delete // the gateAsClients @@ -559,7 +543,7 @@ void gateServer::newAs(void) if(!stat_table[i].descPv) { #if DEBUG_ACCESS printf(" i=%d No descPv for %s\n",i,stat_table[i].name); -#endif +#endif } else { // NULL the entry in the gateStat and its channels and delete // the gateAsClients @@ -595,16 +579,16 @@ void gateServer::newAs(void) gatePvData *pv=pNode->getData(); tsDLIter tmpIter = iter; tmpIter++; - + #if DEBUG_ACCESS printf(" i=%d count=%d %s\n",i,count,pv->name()?pv->name():"NULL"); -#endif +#endif // See if it is allowed pEntry=getAs()->findEntry(pv->name()); if(!pEntry) { #if DEBUG_ACCESS printf(" Not allowed\n"); -#endif +#endif // Denied, kill it (handles statistics) then remove it // now, rather than leaving it for inactiveDeadCleanup pv->death(); @@ -616,7 +600,7 @@ void gateServer::newAs(void) } else { #if DEBUG_ACCESS printf(" Allowed\n"); -#endif +#endif // Allowed, replace gateAsEntry pv->resetEntry(pEntry); // Replace the entry in the gateVcData and its channels @@ -636,7 +620,7 @@ void gateServer::newAs(void) if(!stat_table[i].pv) { #if DEBUG_ACCESS printf(" i=%d No pv for %s\n",i,stat_table[i].name); -#endif +#endif } else { // Replace the entry in the gateStat and its channels #if DEBUG_ACCESS @@ -657,7 +641,7 @@ void gateServer::newAs(void) if(!stat_table[i].descPv) { #if DEBUG_ACCESS printf(" i=%d No descPv for %s\n",i,stat_table[i].name); -#endif +#endif } else { // Replace the entry in the gateStat and its channels #if DEBUG_ACCESS @@ -698,11 +682,7 @@ void gateServer::report1(void) printf(" Report1: Bad report filename\n"); return; } -#ifdef RESERVE_FOPEN_FD - fp=global_resources->fopen(filename,"a"); -#else fp=fopen(filename,"a"); -#endif if(!fp) { printf(" Report1: Cannot open %s for appending\n",filename); return; @@ -728,12 +708,8 @@ void gateServer::report1(void) } fprintf(fp,"---------------------------------------" "------------------------------------\n"); -#ifdef RESERVE_FOPEN_FD - global_resources->fclose(fp); -#else fclose(fp); -#endif - + printf(" Report1 written to %s\n",filename); } @@ -761,11 +737,7 @@ void gateServer::report2(void) printf(" Report2: Bad report filename\n"); return; } -#ifdef RESERVE_FOPEN_FD - fp=global_resources->fopen(filename,"a"); -#else fp=fopen(filename,"a"); -#endif if(!fp) { printf(" Report2: Cannot open %s for appending\n",filename); return; @@ -822,7 +794,7 @@ void gateServer::report2(void) pEntry->pattern?pEntry->pattern:"None"); } else { fprintf(fp,"\n"); - + } } } @@ -864,7 +836,7 @@ void gateServer::report2(void) } iter++; } - + fprintf(fp,"\nDead PVs [DEA]:\n" " State Name Time Group Level Pattern\n"); iter=pv_list.firstIter(); @@ -955,12 +927,8 @@ void gateServer::report2(void) fprintf(fp,"---------------------------------------" "------------------------------------\n"); -#ifdef RESERVE_FOPEN_FD - global_resources->fclose(fp); -#else fclose(fp); -#endif - + printf(" Report2 written to %s\n",filename); } @@ -977,11 +945,7 @@ void gateServer::report3(void) printf(" Report3: Bad report filename\n"); return; } -#ifdef RESERVE_FOPEN_FD - fp=global_resources->fopen(filename,"a"); -#else fp=fopen(filename,"a"); -#endif if(!fp) { printf(" Report3: Cannot open %s for appending\n",filename); return; @@ -989,12 +953,8 @@ void gateServer::report3(void) as->report(fp); -#ifdef RESERVE_FOPEN_FD - global_resources->fclose(fp); -#else fclose(fp); -#endif - + printf(" Report3 written to %s\n",filename); } @@ -1042,8 +1002,8 @@ void gateFd::callBack(void) #ifdef USE_FDS #if DEBUG_TIMES int gateFd::count(0); -#endif -#endif +#endif +#endif // ----------------------- server methods -------------------- @@ -1067,24 +1027,16 @@ gateServer::gateServer(char *prefix ) : last_inactive_cleanup(time(NULL)), last_connect_cleanup(time(NULL)), last_beacon_time(time(NULL)), - suppressed_refresh_flag(0) + suppressed_refresh_flag(0) { gateDebug0(5,"gateServer()\n"); // Initialize channel access -#ifdef USE_313 - int status=ca_task_initialize(); - if(status != ECA_NORMAL) { - fprintf(stderr,"%s gateServer::gateServer: ca_task_initialize failed:\n" - " %s\n",timeStamp(),ca_message(status)); - } -#else int status=ca_context_create(ca_disable_preemptive_callback); if(status != ECA_NORMAL) { fprintf(stderr,"%s gateServer::gateServer: ca_context_create failed:\n" " %s\n",timeStamp(),ca_message(status)); } -#endif #ifdef USE_FDS status=ca_add_fd_registration(::fdCB,this); if(status != ECA_NORMAL) { @@ -1114,7 +1066,7 @@ gateServer::gateServer(char *prefix ) : #if 0 // Jeff did not implement setting counts setEventsProcessed(0); - setEventsPosted(0); + setEventsPosted(0); #endif #endif #endif @@ -1169,15 +1121,7 @@ gateServer::~gateServer(void) fprintf(stderr,"%s gateServer::~gateServer: ca_flush_io failed:\n" " %s\n",timeStamp(),ca_message(status)); } -#ifdef USE_313 - status=ca_task_exit(); - if(status != ECA_NORMAL) { - fprintf(stderr,"%s gateServer::~gateServer: ca_task_exit failed:\n" - " %s\n",timeStamp(),ca_message(status)); - } -#else ca_context_destroy(); -#endif } void gateServer::checkEvent(void) @@ -1258,7 +1202,7 @@ void gateServer::exCB(EXCEPT_ARGS /*args*/) // Handle these cases with less output and no limits since they // are common - + // Virtual circuit disconnect // Virtual circuit unresponsive if (args.stat == ECA_DISCONN || args.stat == ECA_UNRESPTMO) { @@ -1332,7 +1276,7 @@ void gateServer::connectCleanup(void) gateDebug0(51,"gateServer::connectCleanup()\n"); gatePvData* pv; - if(global_resources->connectTimeout()>0 && + if(global_resources->connectTimeout()>0 && timeConnectCleanup()connectTimeout()) return; @@ -1340,8 +1284,8 @@ void gateServer::connectCleanup(void) unsigned long pos=1; unsigned long total=pv_con_list.count(); #endif - -#if DEBUG_PV_CONNNECT_CLEANUP + +#if DEBUG_PV_CONNNECT_CLEANUP printf("\ngateServer::connectCleanup: " "timeConnectCleanup=%ld timeDeadCheck=%ld\n" " timeInactiveCheck=%ld elapsedTime=%ld\n", @@ -1554,11 +1498,11 @@ pvExistReturn gateServer::pvExistTest(const casCtx& ctx, const char* pvname) // deny_from_list is used if(getAs()->isDenyFromListUsed()) { char hostname[GATE_MAX_HOSTNAME_LENGTH]; - + // Get the hostname and check if it is allowed //getClientHostName(ctx, hostname, sizeof(hostname)); //clientAddress.stringConvert(hostname, sizeof(hostname)) - + struct sockaddr_in sockAdd = clientAddress.getSockIP(); struct sockaddr_in* pSockAdd; pSockAdd = &sockAdd; @@ -1566,9 +1510,9 @@ pvExistReturn gateServer::pvExistTest(const casCtx& ctx, const char* pvname) ipAddrToDottedIP(pSockAdd,hostname,sizeof(hostname)); char * ch; ch=strchr(hostname,':'); - if(ch != NULL) hostname[ch-hostname]=0; - - + if(ch != NULL) hostname[ch-hostname]=0; + + // See if requested name is allowed and check for aliases if ( !(pEntry = getAs()->findEntry(pvname, hostname)) ) { @@ -1629,7 +1573,7 @@ pvExistReturn gateServer::pvExistTest(const casCtx& ctx, const char* pvname) pEntry->pattern?pEntry->pattern:"NULL", pEntry->alias?pEntry->alias:"NULL", pEntry->group?pEntry->group:"NULL"); - printf(" pverExistsHere\n"); + printf(" pverExistsHere\n"); #endif #if DEBUG_FDMGR endTime=epicsTime::getCurrent(); @@ -1639,7 +1583,7 @@ pvExistReturn gateServer::pvExistTest(const casCtx& ctx, const char* pvname) } if(strcmp(real_name,stat_table[i].descPvName)==0) { #if DEBUG_DESC - printf(" pverExistsHere\n"); + printf(" pverExistsHere\n"); #endif #if DEBUG_FDMGR endTime=epicsTime::getCurrent(); @@ -1722,7 +1666,7 @@ pvExistReturn gateServer::pvExistTest(const casCtx& ctx, const char* pvname) timeStamp(),loop_count,pvname); } #endif - + switch(pv->getState()) { case gatePvConnect: @@ -1744,16 +1688,16 @@ pvExistReturn gateServer::pvExistTest(const casCtx& ctx, const char* pvname) break; } } - + #if DEBUG_DELAY if(rc.getStatus() == pverAsyncCompletion) { printf(" pverAsyncCompletion\n"); } else if(rc.getStatus() == pverExistsHere) { - printf(" pverExistsHere\n"); + printf(" pverExistsHere\n"); } else if(rc.getStatus() == pverDoesNotExistHere) { - printf(" pverDoesNotExistHere\n"); + printf(" pverDoesNotExistHere\n"); } else { - printf(" Other return code\n"); + printf(" Other return code\n"); } #endif #if DEBUG_HISTORY @@ -1761,15 +1705,15 @@ pvExistReturn gateServer::pvExistTest(const casCtx& ctx, const char* pvname) if(rc.getStatus() == pverAsyncCompletion) { printf(" pverAsyncCompletion\n"); } else if(rc.getStatus() == pverExistsHere) { - printf(" pverExistsHere\n"); + printf(" pverExistsHere\n"); } else if(rc.getStatus() == pverDoesNotExistHere) { - printf(" pverDoesNotExistHere\n"); + printf(" pverDoesNotExistHere\n"); } else { - printf(" Other return code\n"); + printf(" Other return code\n"); } } #endif - + #if DEBUG_FDMGR endTime=epicsTime::getCurrent(); cumTime+=(endTime-startTime); @@ -1825,7 +1769,7 @@ pvAttachReturn gateServer::pvAttach(const casCtx& /*c*/,const char* pvname) timeStamp(),loop_count,real_name); } #endif - + // See if we have a gateVcData if(vcFind(real_name,rc) < 0) { @@ -1901,7 +1845,7 @@ caStatus gateServer::processStat(int type, double val) retVal=S_casApp_noSupport; break; } - + #if DEBUG_PROCESS_STAT print("gateServer::processStat:\n" "val=%.2f nCheck=%d nPrint=%d nSigma=%d nLimit=%.2f\n", @@ -2268,7 +2212,7 @@ gateRateStatsTimer::expire(const epicsTime &curTime) mrg->setStat(statLoad,load[N_LOAD-1]); #endif #endif - + #ifdef CAS_DIAGNOSTICS // Calculate the server event rate seRate=(delTime > 0)?(double)(ULONG_DIFF(seCurCount,sePrevCount))/ diff --git a/src/gateServer.h b/src/gateServer.h index f39566b..09847dc 100644 --- a/src/gateServer.h +++ b/src/gateServer.h @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef _GATESERVER_H_ #define _GATESERVER_H_ @@ -83,7 +83,7 @@ class gateFd : public fdReg #if DEBUG_TIMES static int count; -#endif +#endif }; #endif @@ -105,7 +105,7 @@ class gateFd : public fdReg # ifdef USE_FDS # define statFd 9 # define NEXT_STAT_PV 10 -# else +# else # define NEXT_STAT_PV 9 # endif #else @@ -177,7 +177,7 @@ class gateRateStatsTimer : public epicsTimerNotify { public: gateRateStatsTimer(epicsTimerQueue &queue, - double intervalIn, gateServer *m) : + double intervalIn, gateServer *m) : interval(intervalIn), startTime(epicsTime::getCurrent()), mrg(m), timer(queue.createTimer()) {} virtual expireStatus expire(const epicsTime &curTime); @@ -237,7 +237,7 @@ class gateServer : public caServer char* stat_prefix; size_t stat_prefix_len; gateServerStats stat_table[statCount]; -#endif +#endif #ifdef STAT_PVS unsigned long total_vc; unsigned long total_pv; @@ -256,7 +256,7 @@ class gateServer : public caServer unsigned long client_event_count; unsigned long post_event_count; unsigned long loop_count; -#endif +#endif // CAS application management functions void checkEvent(void); @@ -328,7 +328,7 @@ class gateServer : public caServer static volatile unsigned long newAs_flag; static volatile unsigned long quit_flag; static volatile unsigned long quitserver_flag; - + public: #ifndef _WIN32 static void sig_usr1(int); diff --git a/src/gateStat.cc b/src/gateStat.cc index 1d36232..eb01f95 100644 --- a/src/gateStat.cc +++ b/src/gateStat.cc @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ // Author: Jim Kowalkowski // Date: 7/96 @@ -102,7 +102,7 @@ caStatus gateStatChan::write(const casCtx &ctx, const gdd &value) } #endif } - + // Call the non-virtual-function write() in the gateStat if(pStat) return pStat->write(ctx,value,*this); else return S_casApp_noSupport; @@ -213,7 +213,7 @@ gateStat::~gateStat(void) const char *gateStat::getName() const { - return name; + return name; } casChannel* gateStat::createChannel(const casCtx &ctx, @@ -256,7 +256,7 @@ caStatus gateStat::write(const casCtx& ctx, const gdd& dd, gateChan &/*chan*/) { gddApplicationTypeTable& table=gddApplicationTypeTable::AppTable(); caStatus retVal=S_casApp_noSupport; - + if(value) { table.smartCopy(value,&dd); double val; diff --git a/src/gateStat.h b/src/gateStat.h index 5e62694..e057c2d 100644 --- a/src/gateStat.h +++ b/src/gateStat.h @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ // Author: Jim Kowalkowski // Date: 7/96 @@ -40,7 +40,7 @@ class gateStatChan : public gateChan, public tsDLNode gateStatChan(const casCtx &ctx, casPV *pvIn, gateAsEntry *asentryIn, const char * const user, const char * const host); ~gateStatChan(void); - + virtual caStatus write(const casCtx &ctx, const gdd &value); virtual bool readAccess(void) const; virtual bool writeAccess(void) const; @@ -51,7 +51,7 @@ class gateStat : public casPV public: gateStat(gateServer *s, gateAsEntry *e, const char *n, int t); virtual ~gateStat(void); - + // CA server interface functions virtual caStatus interestRegister(void); virtual void interestDelete(void); @@ -62,7 +62,7 @@ class gateStat : public casPV virtual const char *getName() const; virtual casChannel *createChannel (const casCtx &ctx, const char* const pUserName, const char* const pHostName); - + caStatus write(const casCtx &ctx, const gdd &dd, gateChan &chan); gateAsEntry* getEntry(void) const { return asentry; } @@ -71,11 +71,11 @@ class gateStat : public casPV void addChan(gateStatChan *chan) { chan_list.add(*chan); } void removeChan(gateStatChan *chan) { chan_list.remove(*chan); chan->setCasPv(NULL); } - + void postData(long val); void postData(unsigned long val); void postData(double val); - + void report(FILE *fp); gdd* pvData(void) const { return value; } @@ -101,7 +101,7 @@ class gateStatDesc : public gateStat public: gateStatDesc(gateServer *s, gateAsEntry *e, const char *n, int t); virtual ~gateStatDesc(void); - + // CA server interface functions virtual aitEnum bestExternalType(void) const { return aitEnumString; } virtual caStatus read(const casCtx &ctx, gdd &prototype); @@ -116,7 +116,7 @@ class gateStatDesc : public gateStat void postData(long val) {}; void postData(unsigned long val) {}; void postData(double val) {}; - + private: }; diff --git a/src/gateVc.cc b/src/gateVc.cc index b1cb911..6775827 100644 --- a/src/gateVc.cc +++ b/src/gateVc.cc @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ /*+********************************************************************* @@ -119,7 +119,7 @@ gateChan::gateChan(const casCtx &ctx, casPV *casPvIn, gateAsEntry *asentry, user(userIn), host(hostIn) { - + asclient=new gateAsClient(asentry,user,host); if(asclient) asclient->setUserFunction(post_rights,this); } @@ -183,7 +183,7 @@ void gateChan::resetAsClient(gateAsEntry *asentry) // Don't do anything else if the casPv is NULL, indicating it is // being destroyed or if the asentry is NULL if(!casPv || !asentry) return; - + asclient=new gateAsClient(asentry,user,host); if(asclient) { asclient->setUserFunction(post_rights,this); @@ -236,6 +236,7 @@ caStatus gateVcChan::write(const casCtx &ctx, const gdd &value) // Trap writes if(asclient && asclient->clientPvt()->trapMask) { FILE *fp=global_resources->getPutlogFp(); +#ifndef WITH_CAPUTLOG if(fp) { fprintf(fp,"%s %s@%s %s\n", timeStamp(), @@ -244,7 +245,14 @@ caStatus gateVcChan::write(const casCtx &ctx, const gdd &value) vc && vc->getName()?vc->getName():"Unknown"); fflush(fp); } -#ifdef WITH_CAPUTLOG +#else /* WITH_CAPUTLOG */ + if(fp) { + global_resources->putLog( fp, user ? user : "Unknown", + host ? host : "Unknown", + (vc && vc->getName()) ? vc->getName() : "Unknown", + (vc) ? vc->eventData() : NULL, + &value); + } if (global_resources->hasCaPutlogAddress()) { global_resources->caPutLog_Send(user ? user : "Unknown", host ? host : "Unknown", @@ -254,7 +262,7 @@ caStatus gateVcChan::write(const casCtx &ctx, const gdd &value) } #endif } - + // Call the non-virtual-function write() in the gateVcData if(vc) return vc->write(ctx,value,*this); else return S_casApp_noSupport; @@ -272,15 +280,23 @@ caStatus gateVcChan::writeNotify(const casCtx &ctx, const gdd &value) // Trap writes if(asclient && asclient->clientPvt()->trapMask) { FILE *fp=global_resources->getPutlogFp(); +#ifndef WITH_CAPUTLOG if(fp) { fprintf(fp,"%s %s@%s %s\n", timeStamp(), user?user:"Unknown", host?host:"Unknown", - vc && vc->getName()?vc->getName():"Unknown"); + vc && vc->getName()?vc->getName():"Unknown" ); fflush(fp); } -#ifdef WITH_CAPUTLOG +#else /* WITH_CAPUTLOG */ + if(fp) { + global_resources->putLog( fp, user ? user : "Unknown", + host ? host : "Unknown", + (vc && vc->getName()) ? vc->getName() : "Unknown", + (vc) ? vc->eventData() : NULL, + &value); + } if (global_resources->hasCaPutlogAddress()) { global_resources->caPutLog_Send(user ? user : "Unknown", host ? host : "Unknown", @@ -290,7 +306,7 @@ caStatus gateVcChan::writeNotify(const casCtx &ctx, const gdd &value) } #endif } - + // Call the non-virtual-function write() in the gateVcData if(vc) return vc->writeNotify(ctx,value,*this); else return S_casApp_noSupport; @@ -351,7 +367,8 @@ gateVcData::gateVcData(gateServer* m,const char* name) : prev_post_value_changes(0), post_value_changes(0), pv_data(NULL), - event_data(NULL) + event_data(NULL), + highestGddAppType(0) { gateDebug2(5,"gateVcData(gateServer=%p,name=%s)\n",(void *)m,name); @@ -447,7 +464,7 @@ gateVcData::~gateVcData(void) } while((asyncr=time_rio.first())) { asyncr->removeFromQueue(); - } + } while((asyncr=alhRio.first())) { asyncr->removeFromQueue(); } @@ -579,7 +596,7 @@ int gateVcData::setEventData(gdd* dd) dbr_short_t newSevr; dbr_short_t oldStat; dbr_short_t newStat; - + // Containers get special treatment (for performance reasons) if(event_data->isContainer()) { @@ -592,9 +609,9 @@ int gateVcData::setEventData(gdd* dd) } ndd[gddAppTypeIndex_dbr_stsack_string_value].getStatSevr(oldStat,oldSevr); - dd->getStatSevr(newStat,newSevr); + dd->getStatSevr(newStat,newSevr); if(oldSevr == newSevr && oldStat == newStat) stat_sevr_changed=0; - + // Fill in the new value app_table.smartCopy(ndd,dd); event_data = ndd; @@ -604,9 +621,9 @@ int gateVcData::setEventData(gdd* dd) else { event_data->getStatSevr(oldStat,oldSevr); - dd->getStatSevr(newStat,newSevr); - if(oldSevr == newSevr && oldStat == newStat) stat_sevr_changed=0; - + dd->getStatSevr(newStat,newSevr); + if(oldSevr == newSevr && oldStat == newStat) stat_sevr_changed=0; + event_data = dd; ndd->unreference(); } @@ -645,7 +662,7 @@ int gateVcData::setEventData(gdd* dd) heading("*** gateVcData::setEventData: end",name()); } #endif - + return stat_sevr_changed; } @@ -668,7 +685,7 @@ void gateVcData::setAlhData(gdd* dd) if(event_data) { - + // If the event_data is already an ALH Container, ackt/acks are adjusted if(event_data->applicationType() == gddAppType_dbr_stsack_string) { @@ -716,7 +733,7 @@ void gateVcData::setAlhData(gdd* dd) { event_data = dd; } - + #if DEBUG_GDD dumpdd(4,"event_data(after)",name(),event_data); #endif @@ -798,29 +815,33 @@ void gateVcData::copyState(gdd &dd) // pv_data is NULL. For DBF_STRING pv_data has application type // enums, and is the list of strings. See the dataXxxCB // gatePvData routines. - if(pv_data) table.smartCopy(&dd,pv_data); - + if ( pv_data ) { + table.smartCopy(&dd,pv_data); #if DEBUG_GDD || DEBUG_ENUM - dumpdd(2,"pv_data",name(),pv_data); - dumpdd(3,"dd(after pv_data)",name(),&dd); + dumpdd(2,"pv_data",name(),pv_data); + dumpdd(3,"dd(after pv_data)",name(),&dd); #endif + } // The event_data gdd has an application type of value for all DBF // types. The primitive type and whether it is scalar or atomic // varies with the DBR type. See the eventXxxCB gatePvData // routines. If the pv is alh monitored, the event_data is a // container type (gddAppType_dbr_stsack_string) - if(event_data) table.smartCopy(&dd,event_data); - + if ( event_data ) { + table.smartCopy(&dd,event_data); #if DEBUG_GDD || DEBUG_ENUM - if(event_data) dumpdd(4,"event_data",name(),event_data); - dumpdd(5,"dd(after event_data)",name(),&dd); + dumpdd(4,"event_data",name(),event_data); + dumpdd(5,"dd(after event_data)",name(),&dd); #endif #if DEBUG_EVENT_DATA - if(pv->fieldType() == DBF_ENUM) { - dumpdd(99,"event_data",name(),event_data); - heading("*** gateVcData::copyState: end",name()); + if(pv->fieldType() == DBF_ENUM) { + dumpdd(99,"event_data",name(),event_data); + } +#endif } +#if DEBUG_GDD || DEBUG_ENUM + heading("*** gateVcData::copyState: end",name()); #endif } @@ -837,9 +858,11 @@ void gateVcData::vcNew(readType read_type) // Flush any accumulated reads and writes flushAsyncWriteQueue(); - if(ctrl_rio.count() || time_rio.count()) flushAsyncReadQueue(read_type); + if(ctrl_rio.count() || time_rio.count()) + flushAsyncReadQueue(read_type); if(!pv->alhGetPending()) { - if(alhRio.count()) flushAsyncAlhReadQueue(); + if(alhRio.count()) + flushAsyncAlhReadQueue(); } #if DEBUG_EVENT_DATA @@ -874,7 +897,7 @@ void gateVcData::flushAsyncWriteQueue() assert ( ready() ); smartConstGDDPointer pDD = pWIO->extractDD (); assert ( pDD.valid () ); - caStatus stat = + caStatus stat = pv->put( *pDD, pWIO->isPutNotify() ? pWIO : 0 ); pDD.set ( 0 ); if ( pWIO->isPutNotify() ) { @@ -909,7 +932,7 @@ void gateVcData::flushAsyncReadQueue(readType read_type) "posting asyncr %p (DD at %p)\n", (void *)asyncr,(void *)&asyncr->DD()); asyncr->removeFromQueue(); - + #if DEBUG_GDD heading("gateVcData::flushAsyncReadQueue",name()); dumpdd(1,"asyncr->DD()(before)",name(),&asyncr->DD()); @@ -932,7 +955,7 @@ void gateVcData::flushAsyncReadQueue(readType read_type) "posting asyncr %p (DD at %p)\n", (void *)asyncr,(void *)&asyncr->DD()); asyncr->removeFromQueue(); - + #if DEBUG_GDD heading("gateVcData::flushAsyncReadQueue",name()); dumpdd(1,"asyncr->DD()(before)",name(),&asyncr->DD()); @@ -947,7 +970,7 @@ void gateVcData::flushAsyncReadQueue(readType read_type) } #endif asyncr->postIOCompletion(S_casApp_success,asyncr->DD()); - } + } } } @@ -963,7 +986,7 @@ void gateVcData::flushAsyncAlhReadQueue(void) gateDebug2(5,"gateVcData::flushAsyncAlhReadQueue() posting asyncr %p (DD at %p)\n", (void *)asyncr,(void *)&asyncr->DD()); asyncr->removeFromQueue(); - + #if DEBUG_GDD heading("gateVcData::flushAsyncAlhReadQueue",name()); dumpdd(1,"asyncr->DD()(before)",name(),&asyncr->DD()); @@ -987,6 +1010,14 @@ void gateVcData::flushAsyncAlhReadQueue(void) void gateVcData::vcPostEvent(casEventMask event_mask) { gateDebug1(10,"gateVcData::vcPostEvent() name=%s\n",name()); + gdd *local_event_data = event_data; + + if (highestGddAppType) { + gateDebug1(10, "gateVcData::vcPostEvent() creating new %s DD\n", gddApplicationTypeTable::AppTable().getName(highestGddAppType)); + local_event_data = gddApplicationTypeTable::AppTable().getDD(highestGddAppType); + copyState(*local_event_data); + } + // time_t t; #if DEBUG_DELAY @@ -999,22 +1030,22 @@ void gateVcData::vcPostEvent(casEventMask event_mask) if(needPosting()) { gateDebug1(2,"gateVcData::vcPostEvent() posting event (event_data at %p)\n", - (void *)event_data); - - if(event_data->isAtomic()) + (void *)local_event_data); + + if(local_event_data->isAtomic()) { #if DEBUG_EVENT_DATA if(pv->fieldType() == DBF_ENUM) { heading("gateVcData::vcPostEvent",name()); - dumpdd(99,"event_data",name(),event_data); + dumpdd(99,"event_data",name(),local_event_data); } #elif DEBUG_GDD heading("gateVcData::vcPostEvent(1)",name()); - dumpdd(1,"event_data",name(),event_data); + dumpdd(1,"event_data",name(),local_event_data); #endif - postEvent(event_mask,*event_data); - + postEvent(event_mask,*local_event_data); + #ifdef RATE_STATS mrg->post_event_count++; #endif @@ -1026,30 +1057,33 @@ void gateVcData::vcPostEvent(casEventMask event_mask) #if DEBUG_EVENT_DATA && 0 if(pv->fieldType() == DBF_ENUM) { heading("gateVcData::vcPostEvent",name()); - dumpdd(99,"event_data",name(),event_data); + dumpdd(99,"event_data",name(),local_event_data); } #endif #if DEBUG_GDD heading("gateVcData::vcPostEvent(2)",name()); - dumpdd(1,"event_data",name(),event_data); + dumpdd(1,"event_data",name(),local_event_data); #endif - postEvent(event_mask,*event_data); + postEvent(event_mask,*local_event_data); #ifdef RATE_STATS mrg->post_event_count++; #endif } } + + if (local_event_data != event_data) + local_event_data->unreference(); } void gateVcData::vcData(readType read_type) { // pv_data just appeared - don't really care gateDebug1(10,"gateVcData::vcData() name=%s\n",name()); - + //flush any accumulated reads if caching disabled - if(!global_resources->getCacheMode()){ + if(!global_resources->getCacheMode()){ if(ctrl_rio.count() || time_rio.count()) flushAsyncReadQueue(read_type); } } @@ -1073,17 +1107,16 @@ void gateVcData::interestDelete(void) markNotInterested(); } -caStatus gateVcData::read(const casCtx& ctx, gdd& dd) +caStatus gateVcData::read( const casCtx& ctx, gdd& dd ) { gateDebug1(10,"gateVcData::read() name=%s\n",name()); static const aitString str = "Not Supported by Gateway"; readType read_type = ctrlType; - - + /* This is to obtain mask from client */ if(ctx.getMsg()->m_cmmd == CA_PROTO_EVENT_ADD){ struct mon_info *pMonInfo = (struct mon_info *) ctx.getData(); - //If build with R3.14.9 next two lines must be switched + //If build with R3.14.9 next two lines must be switched ca_uint16_t caProtoMask = AlignedWireRef < epicsUInt16 >(pMonInfo->m_mask); //ca_uint16_t caProtoMask = epicsNTOH16 (pMonInfo->m_mask); @@ -1093,6 +1126,14 @@ caStatus gateVcData::read(const casCtx& ctx, gdd& dd) if (caProtoMask & DBE_PROPERTY) { client_mask = DBE_PROPERTY; } + + unsigned at=dd.applicationType(); + if (highestGddAppType < at) { + if (at >= gddDbrToAit[DBR_CTRL_SHORT].app && at <= gddDbrToAit[DBR_CTRL_DOUBLE].app) { + highestGddAppType = at; + gateDebug1(10, "gateVcData::read() increasing highestGddAppType to %u\n", highestGddAppType); + } + } } #if DEBUG_GDD || DEBUG_ENUM @@ -1154,7 +1195,7 @@ caStatus gateVcData::read(const casCtx& ctx, gdd& dd) // return S_casApp_noSupport; return S_casApp_success; case gddAppType_dbr_stsack_string: - if((event_data && + if((event_data && !(event_data->applicationType()==gddAppType_dbr_stsack_string)) || !pv->alhMonitored()) { @@ -1170,7 +1211,7 @@ caStatus gateVcData::read(const casCtx& ctx, gdd& dd) if(!global_resources->getCacheMode()) { pv->get(timeType); - } + } return S_casApp_asyncCompletion; } else if(pendWio.count()||nrWio.count()) { // Pending write in progress, don't read now @@ -1191,7 +1232,7 @@ caStatus gateVcData::read(const casCtx& ctx, gdd& dd) ctrl_rio.add(*(new gateAsyncR(ctx,dd,&ctrl_rio))); else time_rio.add(*(new gateAsyncR(ctx,dd,&time_rio))); - if(!global_resources->getCacheMode()) + if(!global_resources->getCacheMode()) { pv->get(read_type); } @@ -1226,7 +1267,7 @@ caStatus gateVcData::read(const casCtx& ctx, gdd& dd) if(read_type == ctrlType) ctrl_rio.add(*(new gateAsyncR(ctx,dd,&ctrl_rio))); else - time_rio.add(*(new gateAsyncR(ctx,dd,&time_rio))); + time_rio.add(*(new gateAsyncR(ctx,dd,&time_rio))); pv->get(read_type); return S_casApp_asyncCompletion; @@ -1305,7 +1346,7 @@ caStatus gateVcData::writeSpecifyingCBMechanism( heading("gateVcData::write",name()); dumpdd(1,"dd(incoming)",name(),&dd); #endif - + // Branch on application type unsigned at=dd.applicationType(); switch(at) { @@ -1330,7 +1371,7 @@ caStatus gateVcData::writeSpecifyingCBMechanism( if ( nrWio.count () > maxSimlWriteIO ) { return S_casApp_postponeAsyncIO; } - gateAsyncW * pGateAsyncW = + gateAsyncW * pGateAsyncW = new ( std :: nothrow ) gateAsyncW ( ctx,dd,isPutNotify ); if ( ! pGateAsyncW ) { @@ -1352,7 +1393,7 @@ caStatus gateVcData::writeSpecifyingCBMechanism( #endif return S_casApp_postponeAsyncIO; } else { - gateAsyncW * pGateAsyncW = 0; + gateAsyncW * pGateAsyncW = 0; if ( isPutNotify ) { #if DEBUG_GDD || DEBUG_SLIDER fflush(stderr); diff --git a/src/gateVc.h b/src/gateVc.h index e32ddbc..02f96c1 100644 --- a/src/gateVc.h +++ b/src/gateVc.h @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef _GATEVC_H_ #define _GATEVC_H_ @@ -83,7 +83,7 @@ class gateChan : public casChannel // implemented in base. virtual void setOwner(const char * const user, const char * const host); #endif - + void report(FILE *fp); const char *getUser(void) const { return user; }; @@ -93,12 +93,12 @@ class gateChan : public casChannel gateAsClient *getAsClient(void) const { return asclient; } void deleteAsClient(void); void resetAsClient(gateAsEntry *asentry); - + void setCasPv(casPV *casPvIn) { casPv=casPvIn; } static void post_rights(void *userPvt); -protected: +protected: casPV *casPv; gateAsClient *asclient; // Must be deleted when done using it const char *user; @@ -199,7 +199,7 @@ class gateVcData : public casPV, public tsDLHashNode void setAlhTransTime(void); time_t timeSinceLastAlhTrans(void) const; #endif - + int needPosting(void); int needInitialPosting(void); void markInterested(void); @@ -209,9 +209,9 @@ class gateVcData : public casPV, public tsDLHashNode casEventMask select_mask; casEventMask alh_mask; - + ca_uint16_t client_mask; - + protected: void setState(gateVcState s) { pv_state=s; } gatePvData* pv; // Pointer to the associated gatePvData @@ -242,6 +242,7 @@ class gateVcData : public casPV, public tsDLHashNode // The state of the process variable is kept in these two gdd's gdd* pv_data; // Filled in by gatePvData::getCB on activation gdd* event_data; // Filled in by gatePvData::eventCB on channel change + unsigned highestGddAppType; caStatus writeSpecifyingCBMechanism(const casCtx &ctx, const gdd &value, bool isPutNotify ); static const unsigned maxSimlWriteIO = 100u; diff --git a/src/gateVersion.h b/src/gateVersion.h index 8734007..01fa3e5 100644 --- a/src/gateVersion.h +++ b/src/gateVersion.h @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef _GATEVERSION_H_ #define _GATEVERSION_H_ @@ -26,10 +26,13 @@ #define GATEWAY_VERSION 2 #define GATEWAY_REVISION 1 -#define GATEWAY_MODIFICATION 1 -#define GATEWAY_UPDATE_LEVEL 0 +#define GATEWAY_MODIFICATION 2 +#define GATEWAY_DEV_SNAPSHOT "" -#define GATEWAY_VERSION_STRING "PV Gateway Version 2.1.1" +#define stringOf(TOKEN) #TOKEN +#define GATEWAY_VERSION_STRING "PV Gateway Version " \ + stringOf(GATEWAY_VERSION) "." stringOf(GATEWAY_REVISION) "." \ + stringOf(GATEWAY_MODIFICATION) GATEWAY_DEV_SNAPSHOT #define GATEWAY_CREDITS_STRING \ "Originally developed at Argonne National Laboratory and BESSY\n\n" \ diff --git a/src/gateway.cc b/src/gateway.cc index 56c84de..8cc83fb 100644 --- a/src/gateway.cc +++ b/src/gateway.cc @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ // Author: Jim Kowalkowski // Date: 2/96 @@ -78,7 +78,7 @@ static int nStart=0; static time_t startTime[NRESTARTS]; static gateServer *server=NULL; -// still need to add client and server IP addr info using +// still need to add client and server IP addr info using // the CA environment variables. // still need to add ability to load and run user servers dynamically @@ -138,7 +138,7 @@ void operator delete(void* x) // sip = nothing, the normal interface // // Precedence: -// (1) Command line parameter +// (1) Command line parameter // (2) environment variables // (3) defaults @@ -236,7 +236,7 @@ static PARM_STUFF ptable[] = { { "-prefix", 7, PARM_PREFIX, "statistics_prefix" }, { "-mask", 5, PARM_MASK, "event_mask" }, { "-no_cache", 9, PARM_CACHE, "(no caching)" }, - { "-archive", 8, PARM_ARCHIVE, "archive monitor" }, + { "-archive", 8, PARM_ARCHIVE, "archive monitor" }, { "-help", 5, PARM_HELP, NULL }, { NULL, -1, -1, NULL } }; @@ -257,7 +257,7 @@ static void sig_end(int sig) { fflush(stdout); fflush(stderr); - + switch(sig) { #ifndef _WIN32 case SIGHUP: @@ -318,7 +318,7 @@ static void sig_end(int sig) timeStamp()); break; } - + // Have it stop itself if possible by setting the quit_flag stopEverything(); } @@ -326,11 +326,7 @@ static void sig_end(int sig) #ifndef _WIN32 static void sig_chld(int /*sig*/) { -#ifdef SOLARIS - while(waitpid(-1,NULL,WNOHANG)>0); -#else while(wait3(NULL,WNOHANG,NULL)>0); -#endif signal(SIGCHLD,sig_chld); } #endif //#ifndef _WIN32 @@ -340,7 +336,7 @@ static void sig_stop(int /*sig*/) { if(gate_pid) kill(gate_pid,SIGTERM); - + death_flag=1; } #endif //#ifndef _WIN32 @@ -374,6 +370,7 @@ static int startEverything(char *prefix) if(tempBuff != NULL){ if(strcasecmp(tempBuff,"NO")){ setEnv("EPICS_CAS_AUTO_BEACON_ADDR_LIST","YES",&gate_beacon_ca_auto_list); + gateDebug1(15,"gateway setting <%s>\n",gate_beacon_ca_auto_list); } } gateDebug1(15,"gateway setting <%s>\n",gate_ca_auto_list); @@ -406,15 +403,6 @@ static int startEverything(char *prefix) sid=getpid(); -#ifdef RESERVE_FOPEN_FD - // Open a dummy file to keep a file descriptor open to use for - // fopen to avoid its limit of 256 on Solaris - if(!global_resources->openReserveFile()) { - fprintf(stderr,"Opening reserve file failed: %s\n", - GATE_RESERVE_FILE); - } -#endif - #ifndef _WIN32 // Make script file ("gateway.killer" by default) errno=0; @@ -448,7 +436,7 @@ static int startEverything(char *prefix) fprintf(fp,"# user id=%ld\n",(long)getuid()); fprintf(fp,"# group id=%ld\n",(long)getgid()); fprintf(fp,"# caching=%s\n",global_resources->getCacheMode() ? "enabled" : "disabled" ); - fprintf(fp,"# archive monitor=%s\n",global_resources->getArchiveMode() ? "enabled" : "disabled" ); + fprintf(fp,"# archive monitor=%s\n",global_resources->getArchiveMode() ? "enabled" : "disabled" ); // Print command-line arguments fprintf(fp,"# \n"); @@ -478,11 +466,11 @@ static int startEverything(char *prefix) fprintf(fp,"\n kill %ld # to kill everything\n\n",(long)parent_pid); fprintf(fp,"\n # kill %u # to kill off this gateway\n\n",sid); fflush(fp); - + if(fp!=stderr) fclose(fp); chmod(GATE_SCRIPT_FILE,00755); #endif //#ifndef _WIN32 - + #ifndef _WIN32 // Make script file ("gateway.restart" by default) errno=0; @@ -494,14 +482,14 @@ static int startEverything(char *prefix) fflush(stderr); fp=stderr; } - + fprintf(fp,"\n kill %d # to kill off this gateway\n\n",sid); fflush(fp); - + if(fp!=stderr) fclose(fp); chmod(GATE_RESTART_FILE,00755); #endif //#ifndef _WIN32 - + // Increase process limits to max #ifdef _WIN32 // Set open file limit (512 by default, 2048 is max) @@ -551,7 +539,7 @@ static int startEverything(char *prefix) } #endif } - + if(getrlimit(RLIMIT_CORE,&lim)<0) { fprintf(stderr,"Cannot retrieve the process FD limits\n"); } else { @@ -572,7 +560,7 @@ static int startEverything(char *prefix) # endif } #endif - + #ifndef _WIN32 save_hup=signal(SIGHUP,sig_end); save_bus=signal(SIGBUS,sig_end); @@ -600,6 +588,7 @@ static int startEverything(char *prefix) #else printf("%s PID=%d\n",EPICS_VERSION_STRING,sid); #endif + printf("CA Protocol version %s\n", ca_version()); printEnv(stdout,"EPICS_CA_ADDR_LIST"); printEnv(stdout,"EPICS_CA_AUTO_ADDR_LIST"); printEnv(stdout,"EPICS_CA_SERVER_PORT"); @@ -607,6 +596,8 @@ static int startEverything(char *prefix) printEnv(stdout,"EPICS_CAS_INTF_ADDR_LIST"); printEnv(stdout,"EPICS_CAS_SERVER_PORT"); printEnv(stdout,"EPICS_CAS_IGNORE_ADDR_LIST"); + printEnv(stdout,"EPICS_CAS_AUTO_BEACON_ADDR_LIST"); + printEnv(stdout,"EPICS_CAS_BEACON_ADDR_LIST"); // Get user name char userName[21]; @@ -615,7 +606,7 @@ static int startEverything(char *prefix) strcpy(userName,"Unknown"); } userName[20]='\0'; - + // Get host name char *hostName=getComputerName(); if(!hostName) hostName=strDup("Unknown"); @@ -626,7 +617,7 @@ static int startEverything(char *prefix) FILE *putFp=global_resources->getPutlogFp(); if(putFp) { fprintf(putFp,"%s %s [%s %s]\n", - timeStamp(),GATEWAY_VERSION_STRING,__DATE__,__TIME__); + timeStamp(),GATEWAY_VERSION_STRING,__DATE__,__TIME__); fprintf(putFp,"%s PID=%d\n",EPICS_VERSION_STRING,sid); fprintf(putFp,"Attempted Writes:\n"); fflush(putFp); @@ -783,11 +774,11 @@ int main(int argc, char** argv) case PARM_CACHE: cache=0; not_done=0; - break; + break; case PARM_ARCHIVE: archive=1; not_done=0; - break; + break; case PARM_RO: read_only=1; not_done=0; @@ -1119,7 +1110,7 @@ int main(int argc, char** argv) fprintf(stderr,"\tinactive=%ld\n",gr->inactiveTimeout()); fprintf(stderr,"\tmask=%s\n",gr->eventMaskString()); fprintf(stderr,"\tcaching = %s\n",gr->getCacheMode() ? "enabled" : "disabled" ); - fprintf(stderr,"\tarchive monitor = %s\n",gr->getArchiveMode() ? "enabled" : "disabled" ); + fprintf(stderr,"\tarchive monitor = %s\n",gr->getArchiveMode() ? "enabled" : "disabled" ); #ifndef _WIN32 fprintf(stderr,"\tuser id=%ld\n",(long)getuid()); fprintf(stderr,"\tgroup id=%ld\n",(long)getgid()); @@ -1130,7 +1121,7 @@ int main(int argc, char** argv) "files exist in home)\n"); return -1; } - + // Change to the specified home directory if(home_dir) { @@ -1166,18 +1157,18 @@ int main(int argc, char** argv) if(caputlog_address) gr->setCaPutlogAddress(caputlog_address); #endif if(report_file) gr->setReportFile(report_file); - - //set caching and archive mode + + //set caching and archive mode gr->setCacheMode(cache); gr->setArchiveMode(archive); - + //get EPICS_CA_MAX_ARRAY_BYTES long maxBytesAsALong; - long byteStatus = envGetLongConfigParam ( & EPICS_CA_MAX_ARRAY_BYTES, & maxBytesAsALong ); + long byteStatus = envGetLongConfigParam ( & EPICS_CA_MAX_ARRAY_BYTES, & maxBytesAsALong ); if ( byteStatus || maxBytesAsALong <= 0 || ((unsigned)maxBytesAsALong < MAX_TCP)) maxBytesAsALong = MAX_TCP; - gr->setMaxBytes((unsigned)maxBytesAsALong); + gr->setMaxBytes((unsigned)maxBytesAsALong); + - #ifndef _WIN32 gr->setServerMode(make_server); #endif @@ -1327,7 +1318,7 @@ int main(int argc, char** argv) fprintf(stderr," dead timeout = %ld\n",gr->deadTimeout()); fprintf(stderr," event mask = %s\n",gr->eventMaskString()); fprintf(stderr," caching = %s\n",gr->getCacheMode() ? "enabled" : "disabled" ); - fprintf(stderr," archive monitor = %s\n",gr->getArchiveMode() ? "enabled" : "disabled" ); + fprintf(stderr," archive monitor = %s\n",gr->getArchiveMode() ? "enabled" : "disabled" ); #ifndef _WIN32 fprintf(stderr," user id= %ld\n",(long)getuid()); fprintf(stderr," group id= %ld\n",(long)getgid()); @@ -1350,7 +1341,7 @@ int main(int argc, char** argv) } gr->setPutlogFp(fp); } - + startEverything(stat_prefix); delete global_resources; return 0; @@ -1374,65 +1365,65 @@ static void print_instructions(void) { pr(stderr,"-debug value: Enter value between 0-100. 50 gives lots of\n"); pr(stderr," info, 1 gives small amount.\n\n"); - + pr(stderr,"-pvlist file_name: Name of file with all the allowed PVs in it\n"); pr(stderr," See the sample file gateway.pvlist in the source distribution\n"); pr(stderr," for a description of how to create this file.\n"); - + pr(stderr,"-access file_name: Name of file with all the EPICS access\n"); pr(stderr," security rules in it. PVs in the pvlist file use groups\n"); pr(stderr," and rules defined in this file.\n"); - + pr(stderr,"-log file_name: Name of file where all messages from the\n"); pr(stderr," gateway go, including stderr and stdout.\n\n"); - + pr(stderr,"-command file_name: Name of file where gateway command(s) go\n"); pr(stderr," Commands are executed when a USR1 signal is sent to gateway.\n\n"); - + pr(stderr,"-putlog file_name: Name of file where gateway put logging goes.\n"); pr(stderr," Put logging is specified with TRAPWRITE in the access file.\n\n"); - + pr(stderr,"-report file_name: Name of file where gateway reports go.\n"); pr(stderr," Reports are appended to this file if it exists.\n\n"); - + pr(stderr,"-home directory: Home directory where all your gateway\n"); pr(stderr," configuration files are kept where log and command files go.\n\n"); - + pr(stderr,"-sip IP_address: IP address that gateway's CA server listens\n"); pr(stderr," for PV requests. Sets env variable EPICS_CAS_INTF_ADDR.\n\n"); - + pr(stderr,"-signore IP_address_list: IP address that gateway's CA server\n"); pr(stderr," ignores. Sets env variable EPICS_CAS_IGNORE_ADDR_LIST.\n\n"); - + pr(stderr,"-cip IP_address_list: IP address list that the gateway's CA\n"); pr(stderr," client uses to find the real PVs. See CA reference manual.\n"); pr(stderr," This sets environment variables EPICS_CA_AUTO_LIST=NO and\n"); pr(stderr," EPICS_CA_ADDR_LIST.\n\n"); - + pr(stderr,"-sport CA_server_port: The port which the gateway's CA server\n"); pr(stderr," uses to listen for PV requests. Sets environment variable\n"); pr(stderr," EPICS_CAS_SERVER_PORT.\n\n"); - + pr(stderr,"-cport CA_client_port: The port which the gateway's CA client\n"); pr(stderr," uses to find the real PVs. Sets environment variable\n"); pr(stderr," EPICS_CA_SERVER_PORT.\n\n"); - + pr(stderr,"-connect_timeout seconds: The amount of time that the\n"); pr(stderr," gateway will allow a PV search to continue before marking the\n"); pr(stderr," PV as being not found.\n\n"); - + pr(stderr,"-inactive_timeout seconds: The amount of time that the gateway\n"); pr(stderr," will hold the real connection to an unused PV. If no gateway\n"); pr(stderr," clients are using the PV, the real connection will still be\n"); pr(stderr," held for this long.\n\n"); - + pr(stderr,"-dead_timeout seconds: The amount of time that the gateway\n"); pr(stderr," will hold requests for PVs that are not found on the real\n"); pr(stderr," network that the gateway is using. Even if a client's\n"); pr(stderr," requested PV is not found on the real network, the gateway\n"); pr(stderr," marks the PV dead, holds the request and continues trying\n"); pr(stderr," to connect for this long.\n\n"); - + pr(stderr,"-disconnect_timeout seconds: The amount of time that the gateway\n"); pr(stderr," will hold requests for PVs that were connected but have been\n"); pr(stderr," disconnected. When a disconnected PV reconnects, the gateway will\n"); @@ -1446,25 +1437,25 @@ static void print_instructions(void) pr(stderr,"-server: Start as server. Detach from controlling terminal\n"); pr(stderr," and start a daemon that watches the gateway and automatically\n"); pr(stderr," restarts it if it dies.\n"); - + pr(stderr,"-mask event_mask: Event mask that is used for connections on the\n"); pr(stderr," real network: use any combination of v (value), a (alarm), l (log).\n"); pr(stderr," Default is va (forward value and alarm change events).\n"); - + pr(stderr,"-prefix string: Set the prefix for the gateway statistics PVs.\n"); pr(stderr," Defaults to the hostname the gateway is running on.\n"); - + pr(stderr,"-uid number: Run the server with this id, server does a\n"); pr(stderr," setuid(2) to this user id number.\n\n"); pr(stderr,"-gid number: Run the server with this id, server does a\n"); pr(stderr," setgid(2) to this group id number.\n\n"); - + pr(stderr,"-no_cache: Disables caching. Every get request will be forwarded to\n"); pr(stderr," the ioc and monitor will be created only if needed.\n"); - + pr(stderr,"-archive: Enables archive monitor. Additional log event monitor is \n"); - pr(stderr," is created.\n"); + pr(stderr," is created.\n"); } @@ -1487,7 +1478,7 @@ static int manage_gateway(void) save_hup=signal(SIGHUP,sig_stop); save_term=signal(SIGTERM,sig_stop); save_int=signal(SIGINT,sig_stop); - + // Fork. Parent will exit, child will be a session leader pid_t pid=fork(); switch(pid) { @@ -1509,10 +1500,10 @@ static int manage_gateway(void) // Parent return 1; // Will cause main to return 0; } - + parent_pid=getpid(); - - // + + // do { // Don't allow runaway restarts time(&t); @@ -1537,7 +1528,7 @@ static int manage_gateway(void) startTime[NRESTARTS-1]=t; } } - + // Don't respawn faster than every RESTART_DELAY seconds if((t-prevt) < RESTART_DELAY) sleep(RESTART_DELAY); prevt=t; @@ -1566,7 +1557,7 @@ static int manage_gateway(void) break; } } while(gate_pid && death_flag==0); - + if(death_flag || gate_pid==-1) rc=1; else rc=0; @@ -1650,7 +1641,7 @@ void printRecentHistory(void) static void printEnv(FILE *fp, const char *var) { if(!fp || !var) return; - + char *value=getenv(var); fprintf(fp,"%s=%s\n", var, value?value:"Not specified"); } diff --git a/src/tsHash.h b/src/tsHash.h index 51c0dd5..bee55f2 100644 --- a/src/tsHash.h +++ b/src/tsHash.h @@ -6,7 +6,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * This file is distributed subject to a Software License Agreement found -* in the file LICENSE that is included with this distribution. +* in the file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Jim Kowalkowski * Date: 7/96 */ @@ -38,7 +38,7 @@ class tsHash { gphFreeMem(hash_table); } - + int add(const char* key, T& item) { GPHENTRY* entry; diff --git a/testTop/pyTestsApp/GatewayControl.py b/testTop/pyTestsApp/GatewayControl.py index 680718f..d7a64c3 100644 --- a/testTop/pyTestsApp/GatewayControl.py +++ b/testTop/pyTestsApp/GatewayControl.py @@ -10,18 +10,26 @@ class GatewayControl: gatewayProcess = None DEVNULL = None - def startGateway(self): - '''Starts the CA Gateway''' + def startGateway(self, extra_args=None): + ''' + Starts the CA Gateway + + Params: + extra_args (str): Extra arguments passed to the gateway process + ''' gateway_commands = [gwtests.gwExecutable] gateway_commands.extend(["-sip", "localhost", "-sport", str(gwtests.gwPort)]) gateway_commands.extend(["-cip", "localhost", "-cport", str(gwtests.iocPort)]) gateway_commands.extend(["-access", "access.txt", "-pvlist", "pvlist.txt"]) gateway_commands.extend(["-archive", "-prefix", gwtests.gwStatsPrefix]) + # Append extra arguments + if extra_args is not None: + gateway_commands.extend(extra_args.split(" ")) if gwtests.verboseGateway: gateway_commands.extend(["-debug", str(gwtests.gwDebug)]); if gwtests.verbose: - print "Starting the CA Gateway using\n", " ".join(gateway_commands) + print("Starting the CA Gateway using\n", " ".join(gateway_commands)) if not gwtests.verboseGateway and not gwtests.verbose: self.DEVNULL = open(os.devnull, 'wb') self.gatewayProcess = subprocess.Popen(gateway_commands, stdout=self.DEVNULL, stderr=subprocess.STDOUT) @@ -35,13 +43,16 @@ def stop(self): if self.DEVNULL: self.DEVNULL.close() + def poll(self): + return self.gatewayProcess.poll() + if __name__ == "__main__": gwtests.setup() - print "Running the test CA Gateway in verbose mode for {0} seconds".format(gwtests.gwRunDuration) + print("Running the test CA Gateway in verbose mode for {0} seconds".format(gwtests.gwRunDuration)) gwtests.verbose = True gwtests.verboseGateway = True gatewayControl = GatewayControl() - gatewayControl.startGateway() + gatewayControl.startGateway(extra_args="-no_cache") time.sleep(gwtests.gwRunDuration) gatewayControl.stop() diff --git a/testTop/pyTestsApp/IOCControl.py b/testTop/pyTestsApp/IOCControl.py index 04a98f1..1c9e8e6 100644 --- a/testTop/pyTestsApp/IOCControl.py +++ b/testTop/pyTestsApp/IOCControl.py @@ -17,7 +17,7 @@ def startIOC(self, arglist=None): childEnviron['EPICS_CA_ADDR_LIST'] = "localhost" childEnviron['EPICS_CA_AUTO_ADDR_LIST'] = "NO" # IOC_ environment overrides - for v in os.environ.keys(): + for v in list(os.environ.keys()): if v.startswith("IOC_"): childEnviron[v.replace("IOC_", "", 1)] = os.environ[v] @@ -35,7 +35,7 @@ def startIOC(self, arglist=None): iocCommand.extend(arglist) if gwtests.verbose: - print "Starting the IOC using\n", " ".join(iocCommand) + print("Starting the IOC using\n", " ".join(iocCommand)) self.iocProcess = subprocess.Popen(iocCommand, env=childEnviron, stdin=subprocess.PIPE, stdout=self.DEVNULL, stderr=subprocess.STDOUT) time.sleep(.5) @@ -52,7 +52,7 @@ def stop(self): if __name__ == "__main__": gwtests.setup() - print "Running the test IOC in verbose mode for {0} seconds".format(gwtests.gwRunDuration) + print("Running the test IOC in verbose mode for {0} seconds".format(gwtests.gwRunDuration)) gwtests.verbose = True iocControl = IOCControl() iocControl.startIOC() diff --git a/testTop/pyTestsApp/Makefile b/testTop/pyTestsApp/Makefile index ef19a85..7af8f6d 100644 --- a/testTop/pyTestsApp/Makefile +++ b/testTop/pyTestsApp/Makefile @@ -5,17 +5,27 @@ include $(TOP)/configure/CONFIG # ADD MACRO DEFINITIONS AFTER THIS LINE #============================= -TESTSCRIPTS_HOST += gwtests.py TestDBEValue.py TestDBEAlarm.py TestDBELog.py TestDBEProp.py -TESTSCRIPTS_HOST += TestPropertyCache.py -TESTSCRIPTS_HOST += TestEnumPropCache.py -TESTSCRIPTS_HOST += TestStructures.py -TESTSCRIPTS_HOST += IOCControl.py GatewayControl.py +# Test programs +PYTESTS += TestDBEAlarm.py +PYTESTS += TestDBELog.py +PYTESTS += TestDBEProp.py +PYTESTS += TestDBEValue.py +PYTESTS += TestEnumPropertyCache.py +PYTESTS += TestPropertyCache.py +PYTESTS += TestStructures.py +PYTESTS += TestWaveformWithCAMaxArrayBytes.py + +TAPFILES = $(PYTESTS:.py=.tap) TESTFILES += test.db TESTFILES += access.txt -#TESTFILES += pvlist.txt # Done in an explicit recipe line below -CLEANS += *.pyc $(TESTFILES) pvlist.txt gateway.* +# Which pvlist.txt file depends on USE_PCRE +USE_PCRE ?= NO +PVLIST_YES = pvlist_pcre.txt +PVLIST_NO = pvlist_bre.txt + +CLEANS += *.pyc $(TESTFILES) pvlist.txt gateway.* *.tap include $(TOP)/configure/RULES #---------------------------------------- @@ -23,32 +33,29 @@ include $(TOP)/configure/RULES ifeq ($(T_A),$(EPICS_HOST_ARCH)) +# Environment required by some test programs +export BASE ?= $(EPICS_VERSION).$(EPICS_REVISION) +export EPICS_BASE + ifeq ($(BASE_3_14),YES) clean:: @$(RM) $(CLEANS) endif runtests: - nosetests - -tapfiles: - nosetests --with-tap + nosetests $(PYTESTS:%=../%) -%.py: ../%.py - $(ECHO) "Copying test script $@" - @$(INSTALL) -m $(INSTALL_PERMISSIONS) $< . +$(TAPFILES): Test%.tap: ../Test%.py + nosetests --with-tap $< # Workaround for 3.14 make rules: special target to avoid circular dependencies -build runtests: testfiles +build runtests tapfiles: testfiles pvlist.txt testfiles: $(TESTFILES:%=../%) $(ECHO) "Copying test files $(TESTFILES)" @$(CP) $^ . -ifeq ($(USE_PCRE),YES) - $(ECHO) "Using pvlist.txt for PCRE" - @$(CP) ../pvlist_pcre.txt pvlist.txt -else - $(ECHO) "Using pvlist.txt for BRE" - @$(CP) ../pvlist_bre.txt pvlist.txt -endif + +pvlist.txt: ../$(PVLIST_$(USE_PCRE)) + $(ECHO) "Copying $< to $@ (USE_PCRE: $(USE_PCRE))" + @$(CP) $< $@ endif diff --git a/testTop/pyTestsApp/TestCSStudio.py b/testTop/pyTestsApp/TestCSStudio.py new file mode 100644 index 0000000..6f9560f --- /dev/null +++ b/testTop/pyTestsApp/TestCSStudio.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +import sys +import os +import unittest +import epics +from epics import ca, dbr +import IOCControl +import GatewayControl +import gwtests +import time +import subprocess + +class TestCSStudio(unittest.TestCase): + '''Test CS-Studio workflow through the Gateway + Set up a TIME_DOUBLE (DBE_VALUE | DBE_ALARM) and a CTRL_DOUBLE (DBE_PROPERTY) connection directly and through the Gateway - change value and property - check consistency of data + ''' + + def setUp(self): + gwtests.setup() + self.iocControl = IOCControl.IOCControl() + self.gatewayControl = GatewayControl.GatewayControl() + self.iocControl.startIOC() + self.gatewayControl.startGateway() + self.propSupported = False + self.eventsReceivedIOC = 0 + self.eventsReceivedGW = 0 + self.iocStruct = dict() + self.gwStruct = dict() + os.environ["EPICS_CA_AUTO_ADDR_LIST"] = "NO" + os.environ["EPICS_CA_ADDR_LIST"] = "localhost:{0} localhost:{1}".format(gwtests.iocPort,gwtests.gwPort) + ca.initialize_libca() + + def tearDown(self): + ca.finalize_libca() + self.gatewayControl.stop() + self.iocControl.stop() + + def onChangeIOC(self, pvname=None, **kws): + self.eventsReceivedIOC += 1 + self.iocStruct.update(kws) + if gwtests.verbose: + fmt = 'New IOC Value for %s value=%s, kw=%s\n' + sys.stdout.write(fmt % (pvname, str(kws['value']), repr(kws))) + sys.stdout.flush() + + def onChangeGW(self, pvname=None, **kws): + self.eventsReceivedGW += 1 + self.gwStruct.update(kws) + if gwtests.verbose: + fmt = 'New GW Value for %s value=%s, kw=%s\n' + sys.stdout.write(fmt % (pvname, str(kws['value']), repr(kws))) + sys.stdout.flush() + + def compareStructures(self): + are_diff = False + diffs = [] + for k in list(self.iocStruct.keys()): + if k != "chid" and (self.iocStruct[k] != self.gwStruct[k]): + are_diff = True + diffs.append("Element '{0}' : GW has '{1}', IOC has '{2}'" + .format(k, str(self.gwStruct[k]), str(self.iocStruct[k]))) + return are_diff, diffs + + def testCSStudio_ValueAndPropMonitor(self): + '''Monitor PV (imitating CS-Studio) through GW - change value and properties directly - check CTRL structure consistency''' + diffs = [] + + if gwtests.verbose: + print() + # gwcachetest is an ai record with full set of alarm limits: -100 -10 10 100 + gw = ca.create_channel("gateway:gwcachetest") + connected = ca.connect_channel(gw, timeout=.5) + self.assertTrue(connected, "Could not connect to gateway channel " + ca.name(gw)) + (gw_cbref, gw_uaref, gw_eventid) = ca.create_subscription(gw, mask=dbr.DBE_VALUE | dbr.DBE_ALARM, use_time=True, callback=self.onChangeGW) + (gw_cbref2, gw_uaref2, gw_eventid2) = ca.create_subscription(gw, mask=dbr.DBE_PROPERTY, use_ctrl=True, callback=self.onChangeGW) + ioc = ca.create_channel("ioc:gwcachetest") + connected = ca.connect_channel(ioc, timeout=.5) + self.assertTrue(connected, "Could not connect to ioc channel " + ca.name(ioc)) + (ioc_cbref, ioc_uaref, ioc_eventid) = ca.create_subscription(ioc, mask=dbr.DBE_VALUE | dbr.DBE_ALARM, use_time=True, callback=self.onChangeIOC) + (ioc_cbref2, ioc_uaref2, ioc_eventid2) = ca.create_subscription(ioc, mask=dbr.DBE_PROPERTY, use_ctrl=True, callback=self.onChangeIOC) + + time.sleep(.1) + + # set value on IOC + ioc_value = ca.create_channel("ioc:gwcachetest") + ca.put(ioc_value, 10.0, wait=True) + time.sleep(.1) + if gwtests.verbose: + print() + + self.assertTrue(self.eventsReceivedIOC == self.eventsReceivedGW, + "After setting value, no. of received updates differ: GW {0}, IOC {1}" + .format(str(self.eventsReceivedGW), str(self.eventsReceivedIOC))) + + (are_diff, diffs) = self.compareStructures() + self.assertTrue(are_diff == False, + "At update {0} (change value), received structure updates differ:\n\t{1}" + .format(str(self.eventsReceivedIOC), "\n\t".join(diffs))) + + # set property on IOC + ioc_hihi = ca.create_channel("ioc:gwcachetest.HIHI") + ca.put(ioc_hihi, 123.0, wait=True) + time.sleep(.1) + if gwtests.verbose: + print() + ca.put(ioc_value, 11.0, wait=True) + time.sleep(.1) + if gwtests.verbose: + print() + + self.assertTrue(self.eventsReceivedIOC == self.eventsReceivedGW, + "After setting property, no. of received updates differ: GW {0}, IOC {1}" + .format(str(self.eventsReceivedGW), str(self.eventsReceivedIOC))) + + (are_diff, diffs) = self.compareStructures() + self.assertTrue(are_diff == False, + "At update {0} (change property), received structure updates differ:\n\t{1}" + .format(str(self.eventsReceivedIOC), "\n\t".join(diffs))) + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/testTop/pyTestsApp/TestDBEAlarm.py b/testTop/pyTestsApp/TestDBEAlarm.py index 07e7a0f..504c7bb 100644 --- a/testTop/pyTestsApp/TestDBEAlarm.py +++ b/testTop/pyTestsApp/TestDBEAlarm.py @@ -31,7 +31,7 @@ def tearDown(self): def onChange(self, pvname=None, **kws): self.eventsReceived += 1 if gwtests.verbose: - print pvname, " changed to ", kws['value'], kws['severity'] + print(pvname, " changed to ", kws['value'], kws['severity']) if self.lastSeverity == kws['severity']: self.severityUnchanged += 1 self.lastSeverity = kws['severity'] @@ -46,7 +46,7 @@ def testAlarmLevel(self): gw.get() for val in [0,1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1,0]: ioc.put(val, wait=True) - time.sleep(.05) + time.sleep(.1) # We get 6 events: at connection (INVALID), at first write (NO_ALARM), # and at the level crossings MINOR-MAJOR-MINOR-NO_ALARM. self.assertTrue(self.eventsReceived == 6, 'events expected: 6; events received: ' + str(self.eventsReceived)) diff --git a/testTop/pyTestsApp/TestDBELog.py b/testTop/pyTestsApp/TestDBELog.py index de3c438..49454c7 100644 --- a/testTop/pyTestsApp/TestDBELog.py +++ b/testTop/pyTestsApp/TestDBELog.py @@ -31,7 +31,7 @@ def tearDown(self): def onChange(self, pvname=None, **kws): self.eventsReceived += 1 if gwtests.verbose: - print pvname, " changed to ", kws['value'], kws['severity'] + print(pvname, " changed to ", kws['value'], kws['severity']) if (kws['value'] != 0.0) and (abs(self.lastValue - kws['value']) <= 10.0): self.diffInsideDeadband += 1 self.lastValue = kws['value'] @@ -46,7 +46,7 @@ def testLogDeadband(self): gw.get() for val in range(35): ioc.put(val, wait=True) - time.sleep(.05) + time.sleep(.1) # We get 5 events: at connection, first put, then at 11 22 33 self.assertTrue(self.eventsReceived == 5, 'events expected: 5; events received: ' + str(self.eventsReceived)) # Any updates inside deadband are an error diff --git a/testTop/pyTestsApp/TestDBEProp.py b/testTop/pyTestsApp/TestDBEProp.py index 9daaa9b..70efeab 100644 --- a/testTop/pyTestsApp/TestDBEProp.py +++ b/testTop/pyTestsApp/TestDBEProp.py @@ -30,12 +30,12 @@ def tearDown(self): def onChangeGW(self, pvname=None, **kws): self.eventsReceivedGW += 1 if gwtests.verbose: - print " GW update: ", pvname, " changed to ", kws['value'] + print(" GW update: ", pvname, " changed to ", kws['value']) def onChangeIOC(self, pvname=None, **kws): self.eventsReceivedIOC += 1 if gwtests.verbose: - print "IOC update: ", pvname, " changed to ", kws['value'] + print("IOC update: ", pvname, " changed to ", kws['value']) def testPropAlarmLevels(self): '''DBE_PROPERTY monitor on an ai - value changes generate no events; property changes generate events.''' @@ -53,7 +53,7 @@ def testPropAlarmLevels(self): for val in range(10): ioc.put(val, wait=True) - time.sleep(.05) + time.sleep(.1) # We get 1 event: at connection self.assertTrue(self.eventsReceivedGW == 1, 'GW events expected: 1; received: ' + str(self.eventsReceivedGW)) self.assertTrue(self.eventsReceivedIOC == 1, 'IOC events expected: 1; received: ' + str(self.eventsReceivedIOC)) @@ -63,7 +63,7 @@ def testPropAlarmLevels(self): pvhigh.put(18.0, wait=True) pvlolo.put(10.0, wait=True) pvlow.put(12.0, wait=True) - time.sleep(.05) + time.sleep(.1) # Depending on the IOC (supporting PROPERTY changes on limits or not) we get 0 or 4 events. # Pass test if updates from IOC act the same as updates from GW diff --git a/testTop/pyTestsApp/TestDBEValue.py b/testTop/pyTestsApp/TestDBEValue.py index 9777931..357bd7f 100644 --- a/testTop/pyTestsApp/TestDBEValue.py +++ b/testTop/pyTestsApp/TestDBEValue.py @@ -29,7 +29,7 @@ def tearDown(self): def onChange(self, pvname=None, **kws): self.eventsReceived += 1 if gwtests.verbose: - print pvname, " changed to ", kws['value'] + print(pvname, " changed to ", kws['value']) def testValueNoDeadband(self): '''DBE_VALUE monitor on an ai - value changes generate events.''' @@ -42,7 +42,7 @@ def testValueNoDeadband(self): for val in range(10): ioc.put(val, wait=True) - time.sleep(.05) + time.sleep(.1) # We get 11 events: at connection, then at 10 value changes (puts) self.assertTrue(self.eventsReceived == 11, 'events expected: 11; events received: ' + str(self.eventsReceived)) diff --git a/testTop/pyTestsApp/TestEnumPropCache.py b/testTop/pyTestsApp/TestEnumPropertyCache.py similarity index 98% rename from testTop/pyTestsApp/TestEnumPropCache.py rename to testTop/pyTestsApp/TestEnumPropertyCache.py index c8d18e6..46807a9 100644 --- a/testTop/pyTestsApp/TestEnumPropCache.py +++ b/testTop/pyTestsApp/TestEnumPropertyCache.py @@ -66,6 +66,7 @@ def onChange(self, pvname=None, **kws): a = 1 + @unittest.skip("detects CAS bug lp:1510955 which is not fixed yet") def testEnumPropCache_ValueMonitorCTRLget(self): '''Monitor PV (value events) through GW - change ENUM string directly - get the DBR_CTRL of the PV through GW''' # gateway should show no VC (client side connection) and no PV (IOC side connection) @@ -120,6 +121,7 @@ def testEnumPropCache_ValueMonitorCTRLget(self): self.assertTrue(oneStr == gw_expected, "Expected GW enum[1]: {0}; actual enum[1]: {1}".format(gw_expected, oneStr)) + @unittest.skip("detects CAS bug lp:1510955 which is not fixed yet") def testEnumPropCache_ValueGetCTRLGet(self): '''Get PV (value) through GW - change ENUM string directly - get the DBR_CTRL of the PV through GW''' # gateway should show no VC (client side connection) and no PV (IOC side connection) diff --git a/testTop/pyTestsApp/TestStructures.py b/testTop/pyTestsApp/TestStructures.py index 4e65439..caa0a27 100644 --- a/testTop/pyTestsApp/TestStructures.py +++ b/testTop/pyTestsApp/TestStructures.py @@ -37,7 +37,7 @@ def onChangeIOC(self, pvname=None, **kws): self.eventsReceivedIOC += 1 self.iocStruct = kws if gwtests.verbose: - fmt = 'New Value for %s value=%s, kw=%s\n' + fmt = 'New IOC Value for %s value=%s, kw=%s\n' sys.stdout.write(fmt % (pvname, str(kws['value']), repr(kws))) sys.stdout.flush() @@ -45,20 +45,21 @@ def onChangeGW(self, pvname=None, **kws): self.eventsReceivedGW += 1 self.gwStruct = kws if gwtests.verbose: - fmt = 'New Value for %s value=%s, kw=%s\n' + fmt = 'New GW Value for %s value=%s, kw=%s\n' sys.stdout.write(fmt % (pvname, str(kws['value']), repr(kws))) sys.stdout.flush() def compareStructures(self): are_diff = False diffs = [] - for k in self.iocStruct.keys(): + for k in list(self.iocStruct.keys()): if k != "chid" and (self.iocStruct[k] != self.gwStruct[k]): are_diff = True diffs.append("Element '{0}' : GW has '{1}', IOC has '{2}'" .format(k, str(self.gwStruct[k]), str(self.iocStruct[k]))) return are_diff, diffs + @unittest.skipIf(os.environ['BASE'] == "3.14", "updates of CTRL structures are buggy in 3.14 PCAS") def testCtrlStruct_ValueMonitor(self): '''Monitor PV (value events) through GW - change value and properties directly - check CTRL structure consistency''' diffs = [] @@ -81,6 +82,7 @@ def testCtrlStruct_ValueMonitor(self): self.assertTrue(self.eventsReceivedIOC == self.eventsReceivedGW, "After setting value, no. of received updates differ: GW {0}, IOC {1}" .format(str(self.eventsReceivedGW), str(self.eventsReceivedIOC))) + (are_diff, diffs) = self.compareStructures() self.assertTrue(are_diff == False, "At update {0} (change value), received structure updates differ:\n\t{1}" @@ -95,6 +97,7 @@ def testCtrlStruct_ValueMonitor(self): self.assertTrue(self.eventsReceivedIOC == self.eventsReceivedGW, "After setting property, no. of received updates differ: GW {0}, IOC {1}" .format(str(self.eventsReceivedGW), str(self.eventsReceivedIOC))) + (are_diff, diffs) = self.compareStructures() self.assertTrue(are_diff == False, "At update {0} (change property), received structure updates differ:\n\t{1}" diff --git a/testTop/pyTestsApp/TestWaveformWithCAMaxArrayBytes.py b/testTop/pyTestsApp/TestWaveformWithCAMaxArrayBytes.py new file mode 100644 index 0000000..040e74a --- /dev/null +++ b/testTop/pyTestsApp/TestWaveformWithCAMaxArrayBytes.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +import os +import unittest +from epics import caget, caput, PV +import IOCControl +import GatewayControl +import gwtests + + +MAX_ARRAY_BYTES_KEY = "IOC_EPICS_CA_MAX_ARRAY_BYTES" + +class TestWaveformWithCAMaxArrayBytes(unittest.TestCase): + ''' + Tests for a bug where the gateway will segfault when a waveform is + requested through the gateway and the value of + EPICS_CA_MAX_ARRAY_BYTES in the IOC is too small. + + Reference https://github.com/epics-extensions/ca-gateway/issues/20 + ''' + + def test_run_at_least_one_test(self): + pass + + @unittest.skip("FIXME: test fails with unmanaged segfault, breaking the build") + def test_gateway_does_not_crash_after_requesting_waveform_when_max_array_bytes_too_small(self): + gwtests.setup() + self.iocControl = IOCControl.IOCControl() + self.gatewayControl = GatewayControl.GatewayControl() + + # If the bug is present this test is designed to pass the first case + # and fail the second case + max_array_bytes_cases = ["6000000", "16384"] + for max_array_bytes in max_array_bytes_cases: + print(("\n\n\n>>>>>{}={}\n\n\n" + .format(MAX_ARRAY_BYTES_KEY, max_array_bytes))) + + # The bug crashes the gateway when EPICS_CA_MAX_ARRAY_BYTES + # on the IOC is too small. Set it here + os.environ[MAX_ARRAY_BYTES_KEY] = max_array_bytes + self.iocControl.startIOC() + + # The no_cache argument is required to trigger the bug + self.gatewayControl.startGateway(extra_args="-no_cache") + os.environ["EPICS_CA_AUTO_ADDR_LIST"] = "NO" + os.environ["EPICS_CA_ADDR_LIST"] = ( + "localhost:{0} localhost:{1}".format( + gwtests.iocPort, gwtests.gwPort) + ) + + # First check that a simple PV can be put and got through gateway + put_value = 5 + caput("gateway:passive0", put_value, wait=True) + result = caget("gateway:passive0") + + self.assertIsNotNone(result) + self.assertEqual( + result, put_value, + msg="Initial get: got {} expected {}".format( + result, put_value + ) + ) + + # Then try to get waveform through gateway + try: + w = PV("gateway:bigpassivewaveform").get( + count=3000, + # CTRL type is required to trigger the bug + with_ctrlvars=True + ) + self.gatewayControl.poll() + except TypeError as e: + raise RuntimeError("Gateway has crashed - " + "exception from pyepics: %s", e) + except OSError as e: + raise RuntimeError("Gateway has crashed - " + "exception from subprocess: %s", e) + else: + waveform_from_gateway = w + print(waveform_from_gateway) + print("waveform_from_gateway") + finally: + self.gatewayControl.stop() + self.iocControl.stop() + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/testTop/pyTestsApp/gwtests.py b/testTop/pyTestsApp/gwtests.py index 731a076..58b13e7 100644 --- a/testTop/pyTestsApp/gwtests.py +++ b/testTop/pyTestsApp/gwtests.py @@ -47,16 +47,16 @@ def setup(): elif 'T_A' in os.environ: hostArch = os.environ['T_A'] else: - print "Warning: EPICS_HOST_ARCH not set. Using default value of '{0}'".format(hostArch) + print("Warning: EPICS_HOST_ARCH not set. Using default value of '{0}'".format(hostArch)) if 'TOP' in os.environ: gwLocation = os.path.join(os.environ['TOP'], '..') else: - print "Warning: TOP not set. Using default value of '..'" + print("Warning: TOP not set. Using default value of '..'") gwExecutable = os.path.join(gwLocation, 'bin', hostArch, 'gateway') if not os.path.exists(gwExecutable): - print "Cannot find the gateway executable at", gwExecutable + print("Cannot find the gateway executable at", gwExecutable) sys.exit(1) if 'IOC_EPICS_BASE' in os.environ: @@ -64,4 +64,4 @@ def setup(): elif 'EPICS_BASE' in os.environ: iocExecutable = os.path.join(os.environ['EPICS_BASE'], 'bin', hostArch, 'softIoc') else: - print "Warning: IOC_EPICS_BASE or EPICS_BASE not set. Running 'softIoc' executable in PATH" + print("Warning: IOC_EPICS_BASE or EPICS_BASE not set. Running 'softIoc' executable in PATH") diff --git a/testTop/pyTestsApp/test.db b/testTop/pyTestsApp/test.db index fd08d72..f3f8018 100644 --- a/testTop/pyTestsApp/test.db +++ b/testTop/pyTestsApp/test.db @@ -60,6 +60,13 @@ record(waveform, "ioc:passivewaveform") { field(FTVL, "DOUBLE") } +record(waveform, "ioc:bigpassivewaveform") { + field(DESC, "Big passive waveform") + field(SCAN, "Passive") + field(NELM, "3000") + field(FTVL, "DOUBLE") +} + record(ai, "ioc:gwcachetest") { field(PREC, "3") field(EGU, "wobbles")