From f0e143b907acd5606086048236d76b01addd178c Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 11:02:18 +0200 Subject: [PATCH 01/29] ca/client: fix possible null pointer dereference (found by sonar/cppcheck) --- src/ca/client/access.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ca/client/access.cpp b/src/ca/client/access.cpp index a36899c02..ec873dd5b 100644 --- a/src/ca/client/access.cpp +++ b/src/ca/client/access.cpp @@ -605,7 +605,7 @@ void epicsShareAPI ca_signal_formated ( long ca_status, const char *pfilenm, } else { fprintf ( stderr, "CA exception in thread w/o CA ctx: status=%s file=%s line=%d: \n", - ca_message ( ca_status ), pfilenm, lineno ); + ca_message ( ca_status ), pfilenm ? pfilenm : "", lineno ); if ( pFormat ) { vfprintf ( stderr, pFormat, theArgs ); } From 7d1ff1411fa33997c910bebfc953dccc24814844 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 11:09:59 +0200 Subject: [PATCH 02/29] ca/tools: fix type errors in option parsing (found by sonar/cppcheck) --- src/ca/client/tools/cainfo.c | 2 +- src/ca/client/tools/camonitor.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ca/client/tools/cainfo.c b/src/ca/client/tools/cainfo.c index fc18ccd3f..b12a30aac 100644 --- a/src/ca/client/tools/cainfo.c +++ b/src/ca/client/tools/cainfo.c @@ -157,7 +157,7 @@ int main (int argc, char *argv[]) } break; case 's': /* ca_client_status interest level */ - if (sscanf(optarg,"%du", &statLevel) != 1) + if (sscanf(optarg,"%u", &statLevel) != 1) { fprintf(stderr, "'%s' is not a valid interest level " "- ignored. ('cainfo -h' for help.)\n", optarg); diff --git a/src/ca/client/tools/camonitor.c b/src/ca/client/tools/camonitor.c index 6799182be..1493a1888 100644 --- a/src/ca/client/tools/camonitor.c +++ b/src/ca/client/tools/camonitor.c @@ -258,7 +258,7 @@ int main (int argc, char *argv[]) } break; case '#': /* Array count */ - if (sscanf(optarg,"%ld", &reqElems) != 1) + if (sscanf(optarg,"%lu", &reqElems) != 1) { fprintf(stderr, "'%s' is not a valid array element count " "- ignored. ('camonitor -h' for help.)\n", optarg); From 69d4c238e745e630f97bd80d401c265ce9dfb0df Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 11:23:42 +0200 Subject: [PATCH 03/29] ca/tools: free() all allocated buffers (found by sonar/cppcheck) --- src/ca/client/tools/caput.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ca/client/tools/caput.c b/src/ca/client/tools/caput.c index 79ffef8c3..8f88c5398 100644 --- a/src/ca/client/tools/caput.c +++ b/src/ca/client/tools/caput.c @@ -437,6 +437,7 @@ int main (int argc, char *argv[]) dbuf = calloc (count, sizeof(double)); if(!sbuf || !dbuf) { fprintf(stderr, "Memory allocation failed\n"); + free(sbuf); free(dbuf); return 1; } @@ -450,6 +451,7 @@ int main (int argc, char *argv[]) result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) { fprintf(stderr, "Read operation timed out: ENUM data was not read.\n"); + free(sbuf); free(dbuf); return 1; } @@ -460,6 +462,7 @@ int main (int argc, char *argv[]) if (*(argv+optind+i) == pend) { /* Conversion didn't work */ fprintf(stderr, "Enum index value '%s' is not a number.\n", *(argv+optind+i)); + free(sbuf); free(dbuf); return 1; } if (dbuf[i] >= bufGrEnum.no_str) { @@ -486,6 +489,7 @@ int main (int argc, char *argv[]) dbuf[i] = epicsStrtod(sbuf[i], &pend); if (sbuf[i] == pend || enumAsString) { fprintf(stderr, "Enum string value '%s' invalid.\n", sbuf[i]); + free(sbuf); free(dbuf); return 1; } if (dbuf[i] >= bufGrEnum.no_str) { @@ -503,6 +507,7 @@ int main (int argc, char *argv[]) ebuf = calloc(len, sizeof(char)); if(!ebuf) { fprintf(stderr, "Memory allocation failed\n"); + free(sbuf); free(dbuf); free(ebuf); return 1; } count = epicsStrnRawFromEscaped(ebuf, len, cbuf, len-1) + 1; @@ -537,12 +542,14 @@ int main (int argc, char *argv[]) } if (result != ECA_NORMAL) { fprintf(stderr, "Error from put operation: %s\n", ca_message(result)); + free(sbuf); free(dbuf); free(ebuf); return 1; } result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) { fprintf(stderr, "Write operation timed out: Data was not written.\n"); + free(sbuf); free(dbuf); free(ebuf); return 1; } if (request == callback) { /* Also wait for callbacks */ @@ -556,6 +563,7 @@ int main (int argc, char *argv[]) if (result != ECA_NORMAL) { fprintf(stderr, "Error occured writing data: %s\n", ca_message(result)); + free(sbuf); free(dbuf); free(ebuf); return 1; } @@ -567,6 +575,7 @@ int main (int argc, char *argv[]) /* Shut down Channel Access */ ca_context_destroy(); + free(sbuf); free(dbuf); free(ebuf); return result; } From 30f5c3b3017c7b98a315191ee8687a3faf1ded74 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 11:39:47 +0200 Subject: [PATCH 04/29] db: fix incomplete initialization in dbChannelOpen() (found by sonar/cppcheck) --- src/ioc/db/dbChannel.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 399d13dab..a6fd64450 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -38,6 +38,7 @@ #include "link.h" #include "recSup.h" #include "special.h" +#include "alarm.h" typedef struct parseContext { dbChannel *chan; @@ -619,6 +620,11 @@ long dbChannelOpen(dbChannel *chan) probe.field_type = dbChannelExportType(chan); probe.no_elements = dbChannelElements(chan); probe.field_size = dbChannelFieldSize(chan); + probe.sevr = NO_ALARM; + probe.stat = NO_ALARM; + probe.time.secPastEpoch = 0; + probe.time.nsec = 0; + p = probe; /* From 801710b8c7569f7d275e5c7bd5d6ce46a79cfdc0 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 11:50:11 +0200 Subject: [PATCH 05/29] db: fix possible null pointer dereference (found by sonar/cppcheck) --- src/ioc/db/recGbl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/recGbl.c b/src/ioc/db/recGbl.c index 6406b3773..29b7c19ed 100644 --- a/src/ioc/db/recGbl.c +++ b/src/ioc/db/recGbl.c @@ -67,7 +67,7 @@ void recGblDbaddrError(long status, const struct dbAddr *paddr, errPrintf(status,0,0, "PV: %s.%s " "error detected in routine: %s\n", - (paddr ? precord->name : "Unknown"), + (precord ? precord->name : "Unknown"), (pdbFldDes ? pdbFldDes->name : ""), (pmessage ? pmessage : "Unknown")); return; @@ -104,7 +104,7 @@ void recGblRecSupError(long status, const struct dbAddr *paddr, " %s\n", (psupport_name ? psupport_name : "Unknown"), (pdbRecordType ? pdbRecordType->name : "Unknown"), - (paddr ? precord->name : "Unknown"), + (precord ? precord->name : "Unknown"), (pdbFldDes ? pdbFldDes->name : ""), (pmessage ? pmessage : "")); return; From 97b29129af9224f19ff5d1c1ce2a315fab1c0704 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 11:52:03 +0200 Subject: [PATCH 06/29] libcom/test: don't modify operand in assert() statement (code smell found by sonar/cppcheck) --- src/libCom/cxxTemplates/test/tsSLListTest.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libCom/cxxTemplates/test/tsSLListTest.cc b/src/libCom/cxxTemplates/test/tsSLListTest.cc index 32f96359d..311ffee49 100644 --- a/src/libCom/cxxTemplates/test/tsSLListTest.cc +++ b/src/libCom/cxxTemplates/test/tsSLListTest.cc @@ -46,6 +46,10 @@ int main () tsSLIter iter1 = list.firstIter (); tsSLIter iter2 = iter1; tsSLIter iter3 = iter1; + tsSLIter itert = iter3++ + assert ( iter1 == itert ); + itert = ++iter2; + assert ( iter3 == itert ); assert ( iter1 == iter3++ ); assert ( iter3 == ++iter2 ); list.remove ( *pFredII ); // removes pFred From d5eb055bb79a2fc5451d0da5414d3ea7028b4424 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 12:02:24 +0200 Subject: [PATCH 07/29] libcom/flex: fix sscanf() argument types (code smell found by sonar/cppcheck) --- src/libCom/flex/misc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libCom/flex/misc.c b/src/libCom/flex/misc.c index b8a7a0399..b41668f3a 100644 --- a/src/libCom/flex/misc.c +++ b/src/libCom/flex/misc.c @@ -438,7 +438,7 @@ int htoi(unsigned char *str) { int result; - (void) sscanf( (char *) str, "%x", &result ); + (void) sscanf( (char *) str, "%x", (unsigned *) &result ); return ( result ); } @@ -653,7 +653,7 @@ int otoi(Char *str) { int result; - (void) sscanf( (char *) str, "%o", &result ); + (void) sscanf( (char *) str, "%o", (unsigned *) &result ); return ( result ); } From d3d40689c8c4ed4637d47e723e41a5d997500903 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 13:12:03 +0200 Subject: [PATCH 08/29] libcom/osi: fix dangerous usage of strncat (WIN32) (found by sonar/cppcheck) --- src/libCom/osi/os/WIN32/osdSock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libCom/osi/os/WIN32/osdSock.c b/src/libCom/osi/os/WIN32/osdSock.c index b8c8363fb..8b9869423 100644 --- a/src/libCom/osi/os/WIN32/osdSock.c +++ b/src/libCom/osi/os/WIN32/osdSock.c @@ -64,10 +64,10 @@ epicsShareFunc int epicsShareAPI osiSockAttach() DWORD titleLength = GetConsoleTitle(title, sizeof(title)); if (titleLength) { titleLength = strlen (title); - strncat (title, " " EPICS_VERSION_STRING, sizeof(title)); + strncat (title, " " EPICS_VERSION_STRING, sizeof(title)-1); } else { - strncpy(title, EPICS_VERSION_STRING, sizeof(title)); + strncpy(title, EPICS_VERSION_STRING, sizeof(title)-1); } title[sizeof(title)-1]= '\0'; SetConsoleTitle(title); From cd47bbf99b7121245aba8ec76114f334693f4dbc Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 13:15:03 +0200 Subject: [PATCH 09/29] libcom/osi: fix debug printf() in default/osdNetIntf.c (found by sonar/cppcheck) --- src/libCom/osi/os/default/osdNetIntf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libCom/osi/os/default/osdNetIntf.c b/src/libCom/osi/os/default/osdNetIntf.c index fbee6b03e..702660e15 100644 --- a/src/libCom/osi/os/default/osdNetIntf.c +++ b/src/libCom/osi/os/default/osdNetIntf.c @@ -207,7 +207,7 @@ epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses pNewNode->addr.sa = pIfreqList->ifr_broadaddr; ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) ); } else { - ifDepenDebugPrintf ( ( "Ignoring broadcast addr = \n", ntohl ( baddr.ia.sin_addr.s_addr ) ) ); + ifDepenDebugPrintf ( ( "Ignoring broadcast addr = %x\n", ntohl ( baddr.ia.sin_addr.s_addr ) ) ); free ( pNewNode ); continue; } From 579fc9d0c768dee74fc73b43fd1ffb3ef29564ee Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 13:18:00 +0200 Subject: [PATCH 10/29] libcom/osi: fix potential leak in default/osdThreadHooks.c (found by sonar/cppcheck) --- src/libCom/osi/os/default/osdThreadHooks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libCom/osi/os/default/osdThreadHooks.c b/src/libCom/osi/os/default/osdThreadHooks.c index cbe28a4ff..3c2f49667 100644 --- a/src/libCom/osi/os/default/osdThreadHooks.c +++ b/src/libCom/osi/os/default/osdThreadHooks.c @@ -72,6 +72,7 @@ epicsShareFunc int epicsThreadHookAdd(EPICS_THREAD_HOOK_ROUTINE hook) return 0; } fprintf(stderr, "epicsThreadHookAdd: Locking problem\n"); + free(pHook); return -1; } From 92374b2be2579bf8d11f56aa76ecea3de5026ed9 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 9 Jun 2020 14:04:50 +0200 Subject: [PATCH 11/29] libcom/osi: fix potential leak in vxWorks/osdThread.c (found by sonar/cppcheck) --- src/libCom/osi/os/vxWorks/osdThread.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libCom/osi/os/vxWorks/osdThread.c b/src/libCom/osi/os/vxWorks/osdThread.c index ce01ea609..cc8a1a539 100644 --- a/src/libCom/osi/os/vxWorks/osdThread.c +++ b/src/libCom/osi/os/vxWorks/osdThread.c @@ -348,8 +348,9 @@ epicsShareFunc void epicsThreadMap ( EPICS_THREAD_HOOK_ROUTINE func ) while (noTasks == 0) { noTasks = taskIdListGet(taskIdList, taskIdListSize); if (noTasks == taskIdListSize) { - taskIdList = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int)); - assert(taskIdList); + int *newlist = realloc(taskIdList, (taskIdListSize+ID_LIST_CHUNK)*sizeof(int)); + assert(newlist); + taskIdList = newlist; taskIdListSize += ID_LIST_CHUNK; noTasks = 0; } From 416061088567f401cfc3cfe9acbc82c7cbcb173d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 19 Jun 2020 00:18:55 -0700 Subject: [PATCH 12/29] libCom: test osdSockAddrReuse Ensure that epicsSocketEnableAddressReuseDuringTimeWaitState() and epicsSocketEnableAddressUseForDatagramFanout() have the desired effects. --- modules/libcom/test/osiSockTest.c | 264 +++++++++++++++++++++++++++++- 1 file changed, 262 insertions(+), 2 deletions(-) diff --git a/modules/libcom/test/osiSockTest.c b/modules/libcom/test/osiSockTest.c index 6150370bd..171022c9f 100644 --- a/modules/libcom/test/osiSockTest.c +++ b/modules/libcom/test/osiSockTest.c @@ -7,12 +7,18 @@ #include #include +#include +#include "epicsAssert.h" +#include "dbDefs.h" #include "osiSock.h" +#include "epicsTime.h" +#include "epicsThread.h" #include "epicsUnitTest.h" #include "testMain.h" /* This could easily be generalized to test more options */ +static void udpBroadcast(SOCKET s, int put) { int status; @@ -27,6 +33,7 @@ void udpBroadcast(SOCKET s, int put) "getsockopt BROADCAST => %d", flag); } +static void multiCastLoop(SOCKET s, int put) { int status; @@ -42,6 +49,7 @@ void multiCastLoop(SOCKET s, int put) "getsockopt MULTICAST_LOOP => %d", (int) flag); } +static void multiCastTTL(SOCKET s, int put) { int status; @@ -57,10 +65,13 @@ void multiCastTTL(SOCKET s, int put) "getsockopt IP_MULTICAST_TTL => %d", (int) flag); } +static void udpSockTest(void) { SOCKET s; + testDiag("udpSockTest()"); + s = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); testOk(s != INVALID_SOCKET, "epicsSocketCreate INET, DGRAM, 0"); @@ -107,11 +118,43 @@ int doBind(int expect, SOCKET S, unsigned* port) } } -void udpSockFanoutTest(void) +static +void tcpSockReuseBindTest(int reuse) +{ + SOCKET A, B; + unsigned port=0; /* choose random port */ + + testDiag("tcpSockReuseBindTest(%d)", reuse); + + A = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); + B = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); + + if(A==INVALID_SOCKET || B==INVALID_SOCKET) + testAbort("Insufficient sockets"); + + if(reuse) { + testDiag("epicsSocketEnableAddressReuseDuringTimeWaitState"); + epicsSocketEnableAddressReuseDuringTimeWaitState(A); + epicsSocketEnableAddressReuseDuringTimeWaitState(B); + } + + doBind(0, A, &port); + if(listen(A, 4)) + testFail("listen(A) -> %d", (int)SOCKERRNO); + doBind(1, B, &port); + + epicsSocketDestroy(A); + epicsSocketDestroy(B); +} + +static +void udpSockFanoutBindTest(void) { SOCKET A, B, C; unsigned port=0; /* choose random port */ + testDiag("udpSockFanoutBindTest()"); + A = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); B = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); C = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); @@ -138,16 +181,233 @@ void udpSockFanoutTest(void) epicsSocketDestroy(C); } +struct CASearch { + epicsUInt16 cmd, size, dtype, dcnt; + epicsUInt32 p1, p2; + char body[16]; +}; + +STATIC_ASSERT(sizeof(struct CASearch)==32); + +union CASearchU { + struct CASearch msg; + char bytes[sizeof(struct CASearch)]; +}; + +static +unsigned nsuccess; + +static +const unsigned nrepeat = 6u; + +struct TInfo { + SOCKET sock; + unsigned id; + epicsUInt32 key; + epicsUInt8 rxmask; + epicsUInt8 dupmask; +}; + +static +void udpSockFanoutTestRx(void* raw) +{ + struct TInfo *info = raw; + epicsTimeStamp start, now; +#ifdef _WIN32 + /* ms */ + DWORD timeout = 10000; +#else + struct timeval timeout; + memset(&timeout, 0, sizeof(struct timeval)); + timeout.tv_sec = 10; + timeout.tv_usec = 0; +#endif + unsigned nremain = nrepeat; + + (void)epicsTimeGetCurrent(&start); + now = start; + testDiag("RX%u start", info->id); + + if(setsockopt(info->sock, SOL_SOCKET, SO_RCVTIMEO, (void*)&timeout, sizeof(timeout))) { + testFail("Unable to set socket timeout"); + return; + } + + while(epicsTimeDiffInSeconds(&now, &start)<=5.0) { + union CASearchU buf; + osiSockAddr src; + osiSocklen_t srclen = sizeof(src); + + int n = recvfrom(info->sock, buf.bytes, sizeof(buf.bytes), 0, &src.sa, &srclen); + buf.bytes[sizeof(buf.bytes)-1] = '\0'; + + if(n<0) { + testDiag("recvfrom error (%d)", (int)SOCKERRNO); + break; + } else if((n==sizeof(buf.bytes)) && buf.msg.cmd==htons(6) && buf.msg.size==htons(16) + &&buf.msg.dtype==htons(5) && buf.msg.dcnt==0 && strcmp(buf.msg.body, "totallyinvalid")==0) + { + unsigned ord = ntohl(buf.msg.p1)-info->key; + testDiag("RX%u success %u", info->id, ord); + if(ord<8) { + const epicsUInt8 mask = 1u<rxmask&mask) + info->dupmask|=mask; + info->rxmask|=mask; + } + if(0==--nremain) + break; + } else { + testDiag("RX ignore"); + } + } + testDiag("RX%u end", info->id); +} + +static +void udpSockFanoutTestIface(const osiSockAddr* addr) +{ + SOCKET sender; + struct TInfo rx1, rx2; + epicsThreadId trx1, trx2; + epicsThreadOpts topts = EPICS_THREAD_OPTS_INIT; + int opt = 1; + unsigned i; + osiSockAddr any; + epicsUInt32 key = 0xdeadbeef ^ ntohl(addr->ia.sin_addr.s_addr); + union CASearchU buf; + + topts.joinable = 1; + + /* we bind to any for lack of a portable way to find the + * interface address from the interface broadcast address + */ + memset(&any, 0, sizeof(any)); + any.ia.sin_family = AF_INET; + any.ia.sin_addr.s_addr = htonl(INADDR_ANY); + any.ia.sin_port = addr->ia.sin_port; + + buf.msg.cmd = htons(6); + buf.msg.size = htons(16); + buf.msg.dtype = htons(5); + buf.msg.dcnt = htons(0); /* version 0, which newer servers should ignore */ + /* .p1 and .p2 set below */ + memcpy(buf.msg.body, "tota" "llyi" "nval" "id\0\0", 16); + + sender = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); + rx1.sock = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); + rx2.sock = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0); + if((sender==INVALID_SOCKET) || (rx1.sock==INVALID_SOCKET) || (rx2.sock==INVALID_SOCKET)) + testAbort("Unable to allocate test socket(s)"); + + rx1.id = 1; + rx2.id = 2; + rx1.key = rx2.key = key; + rx1.rxmask = rx2.rxmask = 0u; + rx1.dupmask = rx2.dupmask = 0u; + + if(setsockopt(sender, SOL_SOCKET, SO_BROADCAST, (void*)&opt, sizeof(opt))!=0) { + testFail("setsockopt SOL_SOCKET, SO_BROADCAST error -> %d", (int)SOCKERRNO); + } + + epicsSocketEnableAddressUseForDatagramFanout(rx1.sock); + epicsSocketEnableAddressUseForDatagramFanout(rx2.sock); + + if(bind(rx1.sock, &any.sa, sizeof(any))) + testFail("Can't bind test socket rx1 %d", (int)SOCKERRNO); + if(bind(rx2.sock, &any.sa, sizeof(any))) + testFail("Can't bind test socket rx2 %d", (int)SOCKERRNO); + + trx1 = epicsThreadCreateOpt("rx1", &udpSockFanoutTestRx, &rx1, &topts); + trx2 = epicsThreadCreateOpt("rx2", &udpSockFanoutTestRx, &rx2, &topts); + + for(i=0; isa, sizeof(*addr)); + if(ret!=(int)sizeof(buf.bytes)) + testDiag("sendto() error %d (%d)", ret, (int)SOCKERRNO); + } + + epicsThreadMustJoin(trx1); + epicsThreadMustJoin(trx2); + + testDiag("Result: RX1 %x:%x RX2 %x:%x", + rx1.rxmask, rx1.dupmask, rx2.rxmask, rx2.dupmask); + /* success if any one packet was seen by both sockets */ + if(rx1.rxmask & rx2.rxmask) + nsuccess++; + + epicsSocketDestroy(sender); + epicsSocketDestroy(rx1.sock); + epicsSocketDestroy(rx2.sock); +} + +/* This test violates the principle of unittest isolation by broadcasting + * on the well known CA search port on all interfaces. There is no + * portable way to avoid this. (eg. 127.255.255.255 is Linux specific) + */ +static +void udpSockFanoutTest() +{ + ELLLIST ifaces = ELLLIST_INIT; + ELLNODE *cur; + SOCKET dummy; + osiSockAddr match; + int foundNotLo = 0; + + testDiag("udpSockFanoutTest()"); + + memset(&match, 0, sizeof(match)); + match.ia.sin_family = AF_INET; + match.ia.sin_addr.s_addr = htonl(INADDR_ANY); + + if((dummy = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0))==INVALID_SOCKET) + testAbort("Unable to allocate discovery socket"); + + osiSockDiscoverBroadcastAddresses(&ifaces, dummy, &match); + + for(cur = ellFirst(&ifaces); cur; cur = ellNext(cur)) { + char name[64]; + osiSockAddrNode* node = CONTAINER(cur, osiSockAddrNode, node); + + node->addr.ia.sin_port = htons(5064); + (void)sockAddrToDottedIP(&node->addr.sa, name, sizeof(name)); + + testDiag("Interface %s", name); + if(node->addr.ia.sin_addr.s_addr!=htonl(INADDR_LOOPBACK)) { + testDiag("Not LO"); + foundNotLo = 1; + } + + udpSockFanoutTestIface(&node->addr); + } + + ellFree(&ifaces); + + testOk(foundNotLo, "Found non-loopback interface"); + testOk(nsuccess>0, "Successes %u", nsuccess); + + epicsSocketDestroy(dummy); +} + MAIN(osiSockTest) { int status; - testPlan(18); + testPlan(24); status = osiSockAttach(); testOk(status, "osiSockAttach"); udpSockTest(); + udpSockFanoutBindTest(); udpSockFanoutTest(); + tcpSockReuseBindTest(0); + tcpSockReuseBindTest(1); osiSockRelease(); return testDone(); From a4bdee82c3a8a340b81b31bb911c04e33e3d44f8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 19 Jun 2020 20:46:04 -0700 Subject: [PATCH 13/29] travis fixup networking --- .ci-local/travis-fixup.sh | 19 +++++++++++++++++++ .travis.yml | 1 + 2 files changed, 20 insertions(+) create mode 100755 .ci-local/travis-fixup.sh diff --git a/.ci-local/travis-fixup.sh b/.ci-local/travis-fixup.sh new file mode 100755 index 000000000..7d1ea8235 --- /dev/null +++ b/.ci-local/travis-fixup.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -e -u -x + +env|grep TRAVIS + +[ "$TRAVIS_OS_NAME" = "linux" ] || exit 0 + +# Ensure there is an interface with a (correct) broadcast address +# eg. 'trusty' VMs have interface broadcast address mis-configured +# (why oh why do people insist on setting this explicitly?) + +sudo ip tuntap add dev tap42 mode tap + +sudo ip addr add 192.168.240.1/24 broadcast + dev tap42 + +sudo ip link set dev tap42 up + +# note that this device will be UP but not RUNNING +# so java will see it as not UP since java confuses UP and RUNNING diff --git a/.travis.yml b/.travis.yml index a2f16784b..45f3a0073 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,7 @@ addons: update: true install: + - ./.ci-local/travis-fixup.sh - ./.ci/travis/prepare.sh script: From 49889d8549446c9bd240b8b9589c386b4cbc9498 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 10 Jun 2020 10:09:50 -0700 Subject: [PATCH 14/29] Extend record name validation Begin enforcing earlier check (and tab). Add new warning for record names beginning with numeric or '{'. --- modules/database/src/ioc/dbStatic/dbLexRoutines.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 91b3acc68..311d7079b 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -1045,10 +1045,15 @@ static void dbRecordHead(char *recordType, char *name, int visible) yyerrorAbort("dbRecordHead: Record name can't be empty"); return; } - badch = strpbrk(name, " \"'.$"); + badch = strpbrk(name, " \t\"'.$"); if (badch) { - epicsPrintf("Bad character '%c' in record name \"%s\"\n", + epicsPrintf("Error: Bad character '%c' in record name \"%s\"\n", *badch, name); + yyerrorAbort(NULL); + return; + } else if((*name >= '0' && *name <= '9') || *name=='{') { + epicsPrintf("Warning: Bad character '%c' begins record name \"%s\"\n", + *name, name); } pdbentry = dbAllocEntry(pdbbase); From 3124d972bf7d70c58e46bf5faee2828af779555e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 10 Jun 2020 10:10:20 -0700 Subject: [PATCH 15/29] fix crash on bad record name --- modules/database/src/ioc/dbStatic/dbLexRoutines.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 311d7079b..e6082d85f 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -136,12 +136,14 @@ static void allocTemp(void *pvoid) static void *popFirstTemp(void) { tempListNode *ptempListNode; - void *ptemp; + void *ptemp = NULL; ptempListNode = (tempListNode *)ellFirst(&tempList); - ptemp = ptempListNode->item; - ellDelete(&tempList,(ELLNODE *)ptempListNode); - freeListFree(freeListPvt,ptempListNode); + if(ptempListNode) { + ptemp = ptempListNode->item; + ellDelete(&tempList,(ELLNODE *)ptempListNode); + freeListFree(freeListPvt,ptempListNode); + } return(ptemp); } From b34aa594c87605b7846f6f06275ca0dba34ec7be Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 10 Jun 2020 10:17:37 -0700 Subject: [PATCH 16/29] popFirstTemp() may return NULL This indicates an earlier error, which should already been printed. --- modules/database/src/ioc/dbStatic/dbLexRoutines.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index e6082d85f..bd827ff43 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -479,12 +479,16 @@ static void dbMenuBody(void) return; } pnewMenu = (dbMenu *)popFirstTemp(); + if(!pnewMenu) + return; pnewMenu->nChoice = nChoice = ellCount(&tempList)/2; pnewMenu->papChoiceName = dbCalloc(pnewMenu->nChoice,sizeof(char *)); pnewMenu->papChoiceValue = dbCalloc(pnewMenu->nChoice,sizeof(char *)); for(i=0; ipapChoiceName[i] = (char *)popFirstTemp(); pnewMenu->papChoiceValue[i] = (char *)popFirstTemp(); + if(!pnewMenu->papChoiceName[i] || !pnewMenu->papChoiceValue[i]) + return; } if(ellCount(&tempList)) yyerrorAbort("dbMenuBody: tempList not empty"); /* Add menu in sorted order */ @@ -705,6 +709,8 @@ static void dbRecordtypeBody(void) return; } pdbRecordType= (dbRecordType *)popFirstTemp(); + if(!pdbRecordType) + return; pdbRecordType->no_fields = no_fields = ellCount(&tempList); pdbRecordType->papFldDes = dbCalloc(no_fields,sizeof(dbFldDes *)); pdbRecordType->papsortFldName = dbCalloc(no_fields,sizeof(char *)); @@ -712,6 +718,8 @@ static void dbRecordtypeBody(void) no_prompt = no_links = 0; for(i=0; ipdbRecordType = pdbRecordType; pdbFldDes->indRecordType = i; pdbRecordType->papFldDes[i] = pdbFldDes; @@ -976,6 +984,8 @@ static void dbBreakBody(void) return; } pnewbrkTable = (brkTable *)popFirstTemp(); + if(!pnewbrkTable) + return; number = ellCount(&tempList); if (number % 2) { yyerrorAbort("breaktable: Raw value missing"); @@ -992,10 +1002,14 @@ static void dbBreakBody(void) char *str; str = (char *)popFirstTemp(); + if(!str) + return; (void) epicsScanDouble(str, &paBrkInt[i].raw); free(str); str = (char *)popFirstTemp(); + if(!str) + return; (void) epicsScanDouble(str, &paBrkInt[i].eng); free(str); } From 0fbfc74182280e3c7fd959fed20222672bc3154d Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 23 Jun 2020 11:23:57 +0200 Subject: [PATCH 17/29] Fix missing deletion in 97b29129 from 'fix/misc' - fixes 97b29129 that was replacing two assert() statements without removing the original (offending) lines --- src/libCom/cxxTemplates/test/tsSLListTest.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libCom/cxxTemplates/test/tsSLListTest.cc b/src/libCom/cxxTemplates/test/tsSLListTest.cc index 311ffee49..2845ed596 100644 --- a/src/libCom/cxxTemplates/test/tsSLListTest.cc +++ b/src/libCom/cxxTemplates/test/tsSLListTest.cc @@ -50,8 +50,6 @@ int main () assert ( iter1 == itert ); itert = ++iter2; assert ( iter3 == itert ); - assert ( iter1 == iter3++ ); - assert ( iter3 == ++iter2 ); list.remove ( *pFredII ); // removes pFred } list.add ( *pFred ); From bf7a1605c6df6a195d77752e528fa5b1dcb0a68f Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 23 Jun 2020 13:47:33 +0200 Subject: [PATCH 18/29] Name generated junitfiles '-results.xml' - following an idea by Freddie Akeroyd, to allow better distinction from other xml files --- configure/RULES_BUILD | 4 ++-- documentation/RELEASE_NOTES.md | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index c7f3ba7b6..d23a0684d 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -113,7 +113,7 @@ ifneq (,$(filter $(T_A), $(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS))) RUNTESTS_ENABLED = YES TESTSCRIPTS.t = $(filter %.t, $(TESTSCRIPTS)) TAPFILES.t += $(TESTSCRIPTS.t:.t=.tap) -JUNITFILES.t += $(TESTSCRIPTS.t:.t=.xml) +JUNITFILES.t += $(TESTSCRIPTS.t:.t=-results.xml) TAPFILES += $(TAPFILES.t) JUNITFILES += $(JUNITFILES.t) endif @@ -371,7 +371,7 @@ ifdef RUNTESTS_ENABLED -$(PERL) $< -tap > $@ endif -$(JUNITFILES.t): %.xml: %.tap +$(JUNITFILES.t): %-results.xml: %.tap $(TAPTOJUNIT) --puretap --output $@ --input $< $* # If there's a perl test script (.plt) available, use it diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 0dfb8866c..374152589 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -6,6 +6,12 @@ This version of EPICS Base has not been released yet. +### Change to the `junitfiles` self-test build target + +The names of the generated junit xml test output files have been changed +from `.xml` to `-results.xml`, to allow better +distinction from other xml files. (I.e., for easy wildcard matching.) + ## Changes made between 3.15.7 and 3.15.8 @@ -46,7 +52,7 @@ results; previously the `-k` flag to make was needed and even that didn't always work. Continuous Integration systems are recommended to run `make tapfiles` (or if -they can read junittest output instead of TAP `make junitests`) followed by +they can read junittest output instead of TAP `make junitfiles`) followed by `make -s test-results` to display the results of the tests. If multiple CPUs are available the `-j` flag can be used to run tests in parallel, giving the maximum jobs that should be allowed so `make -j4 tapfiles` for a system with 4 CPUs say. From 7f9fefc2a42170eae3f72cdfe6f43650940235be Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 26 Jun 2020 13:14:58 -0700 Subject: [PATCH 19/29] Further record name validation --- .../database/src/ioc/dbStatic/dbLexRoutines.c | 55 ++++++++++++++----- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index bd827ff43..c40e40549 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -1050,28 +1050,53 @@ static void dbBreakBody(void) } pgphentry->userPvt = pnewbrkTable; } - -static void dbRecordHead(char *recordType, char *name, int visible) + +static +int dbRecordNameValidate(const char *name) { - char *badch; - DBENTRY *pdbentry; - long status; + size_t i=0u; + const char *pos = name; if (!*name) { yyerrorAbort("dbRecordHead: Record name can't be empty"); - return; + return 1; } - badch = strpbrk(name, " \t\"'.$"); - if (badch) { - epicsPrintf("Error: Bad character '%c' in record name \"%s\"\n", - *badch, name); - yyerrorAbort(NULL); - return; - } else if((*name >= '0' && *name <= '9') || *name=='{') { - epicsPrintf("Warning: Bad character '%c' begins record name \"%s\"\n", - *name, name); + + for(; *pos; i++, pos++) { + char c = *pos; + if(i==0) { + /* first charactor restrictions */ + if(c >= '0' && c <= '9') { + errlogPrintf("Warning: Record name '%s' should not begin with a number\n", name); + + } else if(c=='-' || c=='+' || c=='[' || c=='{') { + errlogPrintf("Warning: Record name '%s' should not begin with '%c'\n", name, c); + } + } + /* any charactor restrictions */ + if(c < ' ') { + errlogPrintf("Warning: Record name '%s' should not contain non-printable 0x%02u\n", + name, (unsigned)c); + + } else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') { + epicsPrintf("Error: Bad character '%c' in record name \"%s\"\n", + c, name); + yyerrorAbort(NULL); + return 1; + } } + return 0; +} + +static void dbRecordHead(char *recordType, char *name, int visible) +{ + DBENTRY *pdbentry; + long status; + + if(dbRecordNameValidate(name)) + return; + pdbentry = dbAllocEntry(pdbbase); if (ellCount(&tempList)) yyerrorAbort("dbRecordHead: tempList not empty"); From fddd65ccb10b7a3e2ab3db373107bc9476ae7588 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 29 Jun 2020 11:32:51 -0700 Subject: [PATCH 20/29] yet more record name validation --- modules/database/src/ioc/dbStatic/dbLexRoutines.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index c40e40549..93fd52fc3 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -1065,15 +1065,12 @@ int dbRecordNameValidate(const char *name) for(; *pos; i++, pos++) { char c = *pos; if(i==0) { - /* first charactor restrictions */ - if(c >= '0' && c <= '9') { - errlogPrintf("Warning: Record name '%s' should not begin with a number\n", name); - - } else if(c=='-' || c=='+' || c=='[' || c=='{') { + /* first character restrictions */ + if(c=='-' || c=='+' || c=='[' || c=='{') { errlogPrintf("Warning: Record name '%s' should not begin with '%c'\n", name, c); } } - /* any charactor restrictions */ + /* any character restrictions */ if(c < ' ') { errlogPrintf("Warning: Record name '%s' should not contain non-printable 0x%02u\n", name, (unsigned)c); From dcee015f717e144615a4102cf4befc50544e1a2e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 29 Jun 2020 14:43:46 -0700 Subject: [PATCH 21/29] validate alias names as well --- .../database/src/ioc/dbStatic/dbLexRoutines.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 93fd52fc3..531943d08 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -1058,7 +1058,7 @@ int dbRecordNameValidate(const char *name) const char *pos = name; if (!*name) { - yyerrorAbort("dbRecordHead: Record name can't be empty"); + yyerrorAbort("Error: Record/Alias name can't be empty"); return 1; } @@ -1067,16 +1067,16 @@ int dbRecordNameValidate(const char *name) if(i==0) { /* first character restrictions */ if(c=='-' || c=='+' || c=='[' || c=='{') { - errlogPrintf("Warning: Record name '%s' should not begin with '%c'\n", name, c); + errlogPrintf("Warning: Record/Alias name '%s' should not begin with '%c'\n", name, c); } } /* any character restrictions */ if(c < ' ') { - errlogPrintf("Warning: Record name '%s' should not contain non-printable 0x%02u\n", + errlogPrintf("Warning: Record/Alias name '%s' should not contain non-printable 0x%02u\n", name, (unsigned)c); } else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') { - epicsPrintf("Error: Bad character '%c' in record name \"%s\"\n", + epicsPrintf("Error: Bad character '%c' in Record/Alias name \"%s\"\n", c, name); yyerrorAbort(NULL); return 1; @@ -1223,10 +1223,9 @@ static void dbRecordAlias(char *name) tempListNode *ptempListNode; long status; - if (!*name) { - yyerrorAbort("dbRecordAlias: Alias name can't be empty"); + if(dbRecordNameValidate(name)) return; - } + if (duplicate) return; ptempListNode = (tempListNode *)ellFirst(&tempList); pdbentry = ptempListNode->item; @@ -1244,10 +1243,9 @@ static void dbAlias(char *name, char *alias) DBENTRY dbEntry; DBENTRY *pdbEntry = &dbEntry; - if (!*alias) { - yyerrorAbort("dbAlias: Alias name can't be empty"); + if(dbRecordNameValidate(alias)) return; - } + dbInitEntry(pdbbase, pdbEntry); if (dbFindRecord(pdbEntry, name)) { epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n", From 25681eca4c509e76df5ccb34646c5da42332f537 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 21 Jun 2020 07:04:48 -0700 Subject: [PATCH 22/29] ci: set EPICS_TEST_IMPRECISE_TIMING --- .appveyor.yml | 1 + .travis.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index f98fb81bb..594e89887 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -49,6 +49,7 @@ environment: # common / default variables for all jobs SETUP_PATH: .ci-local:.ci BASE: SELF + EPICS_TEST_IMPRECISE_TIMING: YES matrix: - CMP: vs2019 diff --git a/.travis.yml b/.travis.yml index 45f3a0073..062bf4b8c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ env: global: - SETUP_PATH=.ci-local:.ci - BASE=SELF + - EPICS_TEST_IMPRECISE_TIMING=YES addons: apt: From 32ff3b2ed99bc27b9f5f876f2a9a48a29c40f2fe Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 22 Jun 2020 12:31:45 -0700 Subject: [PATCH 23/29] update ci-scripts 3.0 --- .appveyor.yml | 12 +++++++----- .ci | 2 +- .travis.yml | 16 ++++++++-------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 594e89887..eba6f4aaa 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -37,6 +37,8 @@ skip_commits: # build matrix configuration # #---------------------------------# +image: Visual Studio 2015 + # Build Configurations: dll/static, regular/debug configuration: - dynamic @@ -60,7 +62,7 @@ environment: - CMP: vs2013 - CMP: vs2012 - CMP: vs2010 - - CMP: mingw + - CMP: gcc # Platform: processor architecture platform: @@ -90,17 +92,17 @@ matrix: install: - cmd: git submodule update --init --recursive - - cmd: python .ci/appveyor/do.py prepare + - cmd: python .ci/cue.py prepare build_script: - - cmd: python .ci/appveyor/do.py build + - cmd: python .ci/cue.py build test_script: - - cmd: python .ci/appveyor/do.py test + - cmd: python .ci/cue.py test on_finish: - ps: Get-ChildItem *.tap -Recurse -Force | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } - - cmd: python .ci/appveyor/do.py build test-results -s + - cmd: python .ci/cue.py build test-results -s #---------------------------------# # debugging # diff --git a/.ci b/.ci index 55038b731..b3091e6d6 160000 --- a/.ci +++ b/.ci @@ -1 +1 @@ -Subproject commit 55038b731516f22584b7e1cede4ef0a05b8b647e +Subproject commit b3091e6d6f75838ad370395d7265fb303850a202 diff --git a/.travis.yml b/.travis.yml index 062bf4b8c..c4ee7adf3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,10 +37,12 @@ addons: install: - ./.ci-local/travis-fixup.sh - - ./.ci/travis/prepare.sh + - python .ci/cue.py prepare script: - - ./.ci/travis/build.sh + - python .ci/cue.py build + - python .ci/cue.py test + - python .ci/cue.py test-results # Define build jobs @@ -53,7 +55,7 @@ jobs: - dist: xenial - dist: bionic - env: STATIC=YES EXTRA="CMD_CXXFLAGS=-std=c++11" + env: BCFG=static EXTRA="CMD_CXXFLAGS=-std=c++11" - dist: trusty env: EXTRA="CMD_CXXFLAGS=-std=c++11" @@ -65,15 +67,13 @@ jobs: - dist: trusty compiler: clang - env: STATIC=YES + env: BCFG=static # Cross-compilations to Windows using MinGW and WINE - - env: WINE=32 TEST=NO STATIC=YES - compiler: mingw + - env: WINE=32 TEST=NO BCFG=static - - env: WINE=32 TEST=NO STATIC=NO - compiler: mingw + - env: WINE=32 TEST=NO # Cross-compilation to RTEMS From 1eeac6da2f3c1d5c626e4876f2bad22abd523317 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 2 Jul 2020 16:14:08 -0500 Subject: [PATCH 24/29] Switch RTEMS to the default osdMessageQueue --- .../libcom/src/osi/os/RTEMS/osdMessageQueue.c | 250 ------------------ .../libcom/src/osi/os/RTEMS/osdMessageQueue.h | 29 -- modules/libcom/test/epicsMessageQueueTest.cpp | 6 - 3 files changed, 285 deletions(-) delete mode 100644 modules/libcom/src/osi/os/RTEMS/osdMessageQueue.c delete mode 100644 modules/libcom/src/osi/os/RTEMS/osdMessageQueue.h diff --git a/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.c b/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.c deleted file mode 100644 index 29398e205..000000000 --- a/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.c +++ /dev/null @@ -1,250 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ -/* - * Author W. Eric Norum - * norume@aps.anl.gov - * 630 252 4793 - */ - -/* - * We want to access information which is - * normally hidden from application programs. - */ -#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 - -#include -#include -#include -#include -#include -#include -#include "epicsMessageQueue.h" -#include "errlog.h" - -LIBCOM_API epicsMessageQueueId epicsStdCall -epicsMessageQueueCreate(unsigned int capacity, unsigned int maximumMessageSize) -{ - rtems_status_code sc; - epicsMessageQueueId id = calloc(1, sizeof(*id)); - rtems_interrupt_level level; - static char c1 = 'a'; - static char c2 = 'a'; - static char c3 = 'a'; - - if(!id) - return NULL; - - sc = rtems_message_queue_create (rtems_build_name ('Q', c3, c2, c1), - capacity, - maximumMessageSize, - RTEMS_FIFO|RTEMS_LOCAL, - &id->id); - if (sc != RTEMS_SUCCESSFUL) { - free(id); - errlogPrintf ("Can't create message queue: %s\n", rtems_status_text (sc)); - return NULL; - } - id->maxSize = maximumMessageSize; - id->localBuf = NULL; - rtems_interrupt_disable (level); - if (c1 == 'z') { - if (c2 == 'z') { - if (c3 == 'z') { - c3 = 'a'; - } - else { - c3++; - } - c2 = 'a'; - } - else { - c2++; - } - c1 = 'a'; - } - else { - c1++; - } - rtems_interrupt_enable (level); - return id; -} - -static rtems_status_code rtems_message_queue_send_timeout( - rtems_id id, - void *buffer, - uint32_t size, - rtems_interval timeout) -{ - Message_queue_Control *the_message_queue; - Objects_Locations location; - CORE_message_queue_Status msg_status; - - the_message_queue = _Message_queue_Get( id, &location ); - switch ( location ) - { - case OBJECTS_ERROR: - return RTEMS_INVALID_ID; - - case OBJECTS_LOCAL: - msg_status = _CORE_message_queue_Send( - &the_message_queue->message_queue, - buffer, - size, - id, - NULL, - 1, - timeout - ); - - _Thread_Enable_dispatch(); - - /* - * If we had to block, then this is where the task returns - * after it wakes up. The returned status is correct for - * non-blocking operations but if we blocked, then we need - * to look at the status in our TCB. - */ - - if ( msg_status == CORE_MESSAGE_QUEUE_STATUS_UNSATISFIED_WAIT ) - msg_status = _Thread_Executing->Wait.return_code; - return _Message_queue_Translate_core_message_queue_return_code( msg_status ); - } - return RTEMS_INTERNAL_ERROR; /* unreached - only to remove warnings */ -} - -LIBCOM_API int epicsStdCall epicsMessageQueueSend( - epicsMessageQueueId id, - void *message, - unsigned int messageSize) -{ - if (rtems_message_queue_send_timeout(id->id, message, messageSize, RTEMS_NO_TIMEOUT) == RTEMS_SUCCESSFUL) - return 0; - else - return -1; -} - -LIBCOM_API int epicsStdCall epicsMessageQueueSendWithTimeout( - epicsMessageQueueId id, - void *message, - unsigned int messageSize, - double timeout) -{ - rtems_interval delay; - extern double rtemsTicksPerSecond_double; - - /* - * Convert time to ticks - */ - if (timeout <= 0.0) - return epicsMessageQueueTrySend(id, message, messageSize); - delay = (int)(timeout * rtemsTicksPerSecond_double); - if (delay == 0) - delay++; - if (rtems_message_queue_send_timeout(id->id, message, messageSize, delay) == RTEMS_SUCCESSFUL) - return 0; - else - return -1; -} - -static int receiveMessage( - epicsMessageQueueId id, - void *buffer, - uint32_t size, - uint32_t wait, - rtems_interval delay) -{ - size_t rsize; - rtems_status_code sc; - - if (size < id->maxSize) { - if (id->localBuf == NULL) { - id->localBuf = malloc(id->maxSize); - if (id->localBuf == NULL) - return -1; - } - rsize = receiveMessage(id, id->localBuf, id->maxSize, wait, delay); - if (rsize > size) - return -1; - memcpy(buffer, id->localBuf, rsize); - } - else { - sc = rtems_message_queue_receive(id->id, buffer, &rsize, wait, delay); - if (sc != RTEMS_SUCCESSFUL) - return -1; - } - return rsize; -} - -LIBCOM_API int epicsStdCall epicsMessageQueueTryReceive( - epicsMessageQueueId id, - void *message, - unsigned int size) -{ - return receiveMessage(id, message, size, RTEMS_NO_WAIT, 0); -} - -LIBCOM_API int epicsStdCall epicsMessageQueueReceive( - epicsMessageQueueId id, - void *message, - unsigned int size) -{ - return receiveMessage(id, message, size, RTEMS_WAIT, RTEMS_NO_TIMEOUT); -} - -LIBCOM_API int epicsStdCall epicsMessageQueueReceiveWithTimeout( - epicsMessageQueueId id, - void *message, - unsigned int size, - double timeout) -{ - rtems_interval delay; - uint32_t wait; - extern double rtemsTicksPerSecond_double; - - /* - * Convert time to ticks - */ - if (timeout <= 0.0) { - wait = RTEMS_NO_WAIT; - delay = 0; - } - else { - wait = RTEMS_WAIT; - delay = (int)(timeout * rtemsTicksPerSecond_double); - if (delay == 0) - delay++; - } - return receiveMessage(id, message, size, wait, delay); -} - -LIBCOM_API int epicsStdCall epicsMessageQueuePending( - epicsMessageQueueId id) -{ - uint32_t count; - rtems_status_code sc; - - sc = rtems_message_queue_get_number_pending(id->id, &count); - if (sc != RTEMS_SUCCESSFUL) { - errlogPrintf("Message queue %x get number pending failed: %s\n", - (unsigned int)id, - rtems_status_text(sc)); - return -1; - } - return count; -} - -LIBCOM_API void epicsStdCall epicsMessageQueueShow( - epicsMessageQueueId id, - int level) -{ - int pending = epicsMessageQueuePending(id); - if (pending >= 0) - printf ("Message queue %lx -- Pending: %d\n", (unsigned long)id, pending); -} diff --git a/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.h b/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.h deleted file mode 100644 index b3c4e8897..000000000 --- a/modules/libcom/src/osi/os/RTEMS/osdMessageQueue.h +++ /dev/null @@ -1,29 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ -/* - * Author W. Eric Norum - * norume@aps.anl.gov - * 630 252 4793 - */ - -/* - * Very thin shims around RTEMS routines - */ -#include - -struct epicsMessageQueueOSD { - rtems_id id; - unsigned int maxSize; - void *localBuf; - -}; -#define epicsMessageQueueDestroy(q) (rtems_message_queue_delete((q)->id)) - -#define epicsMessageQueueTrySend(q,m,l) (rtems_message_queue_send((q)->id, (m), (l)) == RTEMS_SUCCESSFUL ? 0 : -1) diff --git a/modules/libcom/test/epicsMessageQueueTest.cpp b/modules/libcom/test/epicsMessageQueueTest.cpp index d46a3b9e5..f23683c84 100644 --- a/modules/libcom/test/epicsMessageQueueTest.cpp +++ b/modules/libcom/test/epicsMessageQueueTest.cpp @@ -208,14 +208,8 @@ void sleepyReceiver(double delay) epicsThreadSleep(delay); } -#ifdef __rtems__ - testTodoBegin("RTEMS failure expected"); -#endif testOk(numSent == SLEEPY_TESTS, "Sent %d (should be %d)", numSent, SLEEPY_TESTS); -#ifdef __rtems__ - testTodoEnd(); -#endif testOk(numReceived == SLEEPY_TESTS, "Received %d (should be %d)", numReceived, SLEEPY_TESTS); From 5c03f8ba799c0cccc5067e875bec76f31fa1339b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 2 Jul 2020 16:17:04 -0500 Subject: [PATCH 25/29] Simplify epicsMessageQueueTest using joinable threads Fixes issues with thread shutdown --- modules/libcom/test/epicsMessageQueueTest.cpp | 187 ++++++++++-------- 1 file changed, 103 insertions(+), 84 deletions(-) diff --git a/modules/libcom/test/epicsMessageQueueTest.cpp b/modules/libcom/test/epicsMessageQueueTest.cpp index f23683c84..340ae6b58 100644 --- a/modules/libcom/test/epicsMessageQueueTest.cpp +++ b/modules/libcom/test/epicsMessageQueueTest.cpp @@ -25,12 +25,9 @@ static const char *msg1 = "1234567890This is a very long message."; static volatile int sendExit = 0; static volatile int recvExit = 0; -static epicsEventId finished; -static unsigned int mediumStack; #define SLEEPY_TESTS 500 static int numSent, numReceived; -static epicsEventId complete; /* * In Numerical Recipes in C: The Art of Scientific Computing (William H. @@ -116,7 +113,6 @@ receiver(void *arg) if (!testOk1(errors == 0)) testDiag("Error count was %d", errors); testDiag("%s exiting", myName); - epicsEventSignal(finished); } extern "C" void @@ -133,15 +129,18 @@ fastReceiver(void *arg) } } recvExit = 0; - epicsEventSignal(complete); } void sleepySender(double delay) { + epicsThreadOpts opts = {epicsThreadPriorityMedium, epicsThreadStackMedium, 1}; + epicsThreadId rxThread; + testDiag("sleepySender: sending every %.3f seconds", delay); epicsMessageQueue q(4, 20); - epicsThreadCreate("Fast Receiver", epicsThreadPriorityMedium, - mediumStack, fastReceiver, &q); + rxThread = epicsThreadCreateOpt("Fast Receiver", fastReceiver, &q, &opts); + if (!rxThread) + testAbort("Task create failed"); numSent = 0; for (int i = 0 ; i < SLEEPY_TESTS ; i++) { @@ -159,7 +158,7 @@ void sleepySender(double delay) recvExit = 1; while (q.send((void *)msg1, 4) != 0) epicsThreadSleep(0.01); - epicsEventMustWait(complete); + epicsThreadMustJoin(rxThread); } extern "C" void @@ -179,11 +178,14 @@ fastSender(void *arg) } } sendExit = 0; - epicsEventSignal(complete); } void sleepyReceiver(double delay) { + epicsThreadOpts opts = {epicsThreadPriorityMedium, + epicsThreadStackMedium, 1}; + epicsThreadId txThread; + testDiag("sleepyReceiver: acquiring every %.3f seconds", delay); epicsMessageQueue q(4, 20); @@ -192,8 +194,9 @@ void sleepyReceiver(double delay) q.send((void *)msg1, 4); } - epicsThreadCreate("Fast Sender", epicsThreadPriorityMedium, - mediumStack, fastSender, &q); + txThread = epicsThreadCreateOpt("Fast Sender", fastSender, &q, &opts); + if (!txThread) + testAbort("Task create failed"); epicsThreadSleep(0.5); char cbuf[80]; @@ -216,7 +219,7 @@ void sleepyReceiver(double delay) sendExit = 1; while (q.receive(cbuf, sizeof cbuf) <= 0) epicsThreadSleep(0.01); - epicsEventMustWait(complete); + epicsThreadMustJoin(txThread); } extern "C" void @@ -236,130 +239,136 @@ sender(void *arg) testDiag("%s exiting, sent %d messages", epicsThreadGetNameSelf(), i); } +#define NUM_SENDERS 4 extern "C" void messageQueueTest(void *parm) { epicsThreadId myThreadId = epicsThreadGetIdSelf(); + epicsThreadId rxThread; + epicsThreadId senderId[NUM_SENDERS]; + epicsThreadOpts opts = {epicsThreadPriorityMedium, + epicsThreadStackMedium, 1}; + unsigned int i; char cbuf[80]; int len; int pass; int want; - epicsMessageQueue *q1 = new epicsMessageQueue(4, 20); + epicsMessageQueue q1(4, 20); testDiag("Simple single-thread tests:"); - i = 0; - testOk1(q1->pending() == 0); - while (q1->trySend((void *)msg1, i ) == 0) { - i++; - testOk(q1->pending() == i, "q1->pending() == %d", i); + testOk1(q1.pending() == 0); + for (i = 0; i < 4;) { + int ret = q1.trySend((void *)msg1, i++); + testOk(ret == 0, "trySend succeeded (%d == 0)", ret); + testOk(q1.pending() == i, "loop: q1.pending() == %d", i); } - testOk1(q1->pending() == 4); + testOk1(q1.pending() == 4); want = 0; - len = q1->receive(cbuf, sizeof cbuf); - testOk1(q1->pending() == 3); + len = q1.receive(cbuf, sizeof cbuf); + testOk1(q1.pending() == 3); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); want++; - len = q1->receive(cbuf, sizeof cbuf); - testOk1(q1->pending() == 2); + len = q1.receive(cbuf, sizeof cbuf); + testOk1(q1.pending() == 2); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); - q1->trySend((void *)msg1, i++); + q1.trySend((void *)msg1, i++); want++; - len = q1->receive(cbuf, sizeof cbuf); - testOk1(q1->pending() == 2); + len = q1.receive(cbuf, sizeof cbuf); + testOk1(q1.pending() == 2); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); - q1->trySend((void *)msg1, i++); - testOk1(q1->pending() == 3); + q1.trySend((void *)msg1, i++); + testOk1(q1.pending() == 3); i = 3; - while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) { + while ((len = q1.receive(cbuf, sizeof cbuf, 1.0)) >= 0) { --i; - testOk(q1->pending() == i, "q1->pending() == %d", i); + testOk(q1.pending() == i, "loop: q1.pending() == %d", i); want++; if (!testOk1((len == want) & (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); } - testOk1(q1->pending() == 0); + testOk1(q1.pending() == 0); testDiag("Test sender timeout:"); i = 0; - testOk1(q1->pending() == 0); - while (q1->send((void *)msg1, i, 1.0 ) == 0) { + testOk1(q1.pending() == 0); + while (q1.send((void *)msg1, i, 1.0 ) == 0) { i++; - testOk(q1->pending() == i, "q1->pending() == %d", i); + testOk(q1.pending() == i, "loop: q1.pending() == %d", i); } - testOk1(q1->pending() == 4); + testOk1(q1.pending() == 4); want = 0; - len = q1->receive(cbuf, sizeof cbuf); - testOk1(q1->pending() == 3); + len = q1.receive(cbuf, sizeof cbuf); + testOk1(q1.pending() == 3); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); want++; - len = q1->receive(cbuf, sizeof cbuf); - testOk1(q1->pending() == 2); + len = q1.receive(cbuf, sizeof cbuf); + testOk1(q1.pending() == 2); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); - q1->send((void *)msg1, i++, 1.0); + q1.send((void *)msg1, i++, 1.0); want++; - len = q1->receive(cbuf, sizeof cbuf); - testOk1(q1->pending() == 2); + len = q1.receive(cbuf, sizeof cbuf); + testOk1(q1.pending() == 2); if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); - q1->send((void *)msg1, i++, 1.0); - testOk1(q1->pending() == 3); + q1.send((void *)msg1, i++, 1.0); + testOk1(q1.pending() == 3); i = 3; - while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) { + while ((len = q1.receive(cbuf, sizeof cbuf, 1.0)) >= 0) { --i; - testOk(q1->pending() == i, "q1->pending() == %d", i); + testOk(q1.pending() == i, "loop: q1.pending() == %d", i); want++; if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); } - testOk1(q1->pending() == 0); + testOk1(q1.pending() == 0); testDiag("Test receiver with timeout:"); for (i = 0 ; i < 4 ; i++) - testOk1 (q1->send((void *)msg1, i, 1.0) == 0); - testOk1(q1->pending() == 4); + testOk1 (q1.send((void *)msg1, i, 1.0) == 0); + testOk1(q1.pending() == 4); for (i = 0 ; i < 4 ; i++) - testOk(q1->receive((void *)cbuf, sizeof cbuf, 1.0) == (int)i, - "q1->receive(...) == %d", i); - testOk1(q1->pending() == 0); - testOk1(q1->receive((void *)cbuf, sizeof cbuf, 1.0) < 0); - testOk1(q1->pending() == 0); + testOk(q1.receive((void *)cbuf, sizeof cbuf, 1.0) == (int)i, + "q1.receive(...) == %d", i); + testOk1(q1.pending() == 0); + testOk1(q1.receive((void *)cbuf, sizeof cbuf, 1.0) < 0); + testOk1(q1.pending() == 0); testDiag("Single receiver with invalid size, single sender tests:"); - epicsThreadCreate("Bad Receiver", epicsThreadPriorityMedium, - mediumStack, badReceiver, q1); + rxThread = epicsThreadCreateOpt("Bad Receiver", badReceiver, &q1, &opts); + if (!rxThread) + testAbort("epicsThreadCreate failed"); epicsThreadSleep(1.0); - testOk(q1->send((void *)msg1, 10) == 0, "Send with waiting receiver"); - epicsThreadSleep(2.0); - testOk(q1->send((void *)msg1, 10) == 0, "Send with no receiver"); + testOk(q1.send((void *)msg1, 10) == 0, "Send with waiting receiver"); epicsThreadSleep(2.0); + testOk(q1.send((void *)msg1, 10) == 0, "Send with no receiver"); + epicsThreadMustJoin(rxThread); testDiag("6 Single receiver single sender 'Sleepy timeout' tests,"); testDiag(" these should take about %.2f seconds each:", SLEEPY_TESTS * 0.010); - complete = epicsEventMustCreate(epicsEventEmpty); sleepySender(0.009); sleepySender(0.010); sleepySender(0.011); @@ -369,11 +378,12 @@ extern "C" void messageQueueTest(void *parm) testDiag("Single receiver, single sender tests:"); epicsThreadSetPriority(myThreadId, epicsThreadPriorityHigh); - epicsThreadCreate("Receiver one", epicsThreadPriorityMedium, - mediumStack, receiver, q1); + rxThread = epicsThreadCreateOpt("Receiver one", receiver, &q1, &opts); + if (!rxThread) + testAbort("epicsThreadCreate failed"); for (pass = 1 ; pass <= 3 ; pass++) { for (i = 0 ; i < 10 ; i++) { - if (q1->trySend((void *)msg1, i) < 0) + if (q1.trySend((void *)msg1, i) < 0) break; if (pass >= 3) epicsThreadSleep(0.5); @@ -402,18 +412,20 @@ extern "C" void messageQueueTest(void *parm) */ testDiag("Single receiver, multiple sender tests:"); testDiag("This test lasts 30 seconds..."); - testOk(!!epicsThreadCreate("Sender 1", epicsThreadPriorityLow, - mediumStack, sender, q1), - "Created Sender 1"); - testOk(!!epicsThreadCreate("Sender 2", epicsThreadPriorityMedium, - mediumStack, sender, q1), - "Created Sender 2"); - testOk(!!epicsThreadCreate("Sender 3", epicsThreadPriorityHigh, - mediumStack, sender, q1), - "Created Sender 3"); - testOk(!!epicsThreadCreate("Sender 4", epicsThreadPriorityHigh, - mediumStack, sender, q1), - "Created Sender 4"); + for (i=0; i Date: Mon, 20 Jul 2020 18:20:45 -0500 Subject: [PATCH 26/29] ca: Fix capr.pl to handle missing fields properly When using an EPICS 7 softIoc.dbd file with a PV from an older version of Base which didn't have all the fields, our attempts to read those fields will time out, but the internal logic was buggy. Don't try to print the values of timed out fields at all. The user will still see a warning about connection timeouts at the top. --- modules/ca/src/perl/capr.pl | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/modules/ca/src/perl/capr.pl b/modules/ca/src/perl/capr.pl index 21e56ecd9..1e571bec7 100644 --- a/modules/ca/src/perl/capr.pl +++ b/modules/ca/src/perl/capr.pl @@ -280,7 +280,7 @@ sub caget { my $err = (@chans > 1) ? 'some PV(s)' : '"' . $chans[0]->name . '"'; print "Channel connect timed out: $err not found.\n"; foreach my $chan (@chans) { - $timed_out{$chan->name} = $chan->is_connected; + $timed_out{$chan->name} = !$chan->is_connected; } @chans = grep { $_->is_connected } @chans; } else { @@ -346,15 +346,10 @@ sub printRecord { my $field = $fields_pr[$i]; my $fToGet = $readlist[$i]; my ($fType, $data, $base); - if ($timed_out{$fToGet}) { - $fType = $fieldType{DBF_STRING}; - $data = ''; - } - else { - $fType = $ftypes[$i]; - $base = $bases[$i]; - $data = $callback_data{$fToGet}; - } + next if $timed_out{$fToGet}; + $fType = $ftypes[$i]; + $base = $bases[$i]; + $data = $callback_data{$fToGet}; $col = printField($field, $data, $fType, $base, $col); } print("\n"); # Final newline From c55a95fc9860545fb3c6fc16849bcd74ecc4fad1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 20 Jul 2020 18:21:46 -0500 Subject: [PATCH 27/29] ca: Support DBF_INT64 and DBF_UINT64 in capr.pl --- modules/ca/src/perl/capr.pl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/ca/src/perl/capr.pl b/modules/ca/src/perl/capr.pl index 1e571bec7..fd0c076b9 100644 --- a/modules/ca/src/perl/capr.pl +++ b/modules/ca/src/perl/capr.pl @@ -35,9 +35,11 @@ my %fieldType = ( DBF_DOUBLE => 'DBF_FLOAT', DBF_FLOAT => 'DBF_FLOAT', DBF_LONG => 'DBF_LONG', + DBF_INT64 => 'DBF_FLOAT', DBF_SHORT => 'DBF_LONG', DBF_ULONG => 'DBF_LONG', DBF_USHORT => 'DBF_LONG', + DBF_UINT64 => 'DBF_FLOAT', DBF_DEVICE => 'DBF_STRING', DBF_ENUM => 'DBF_STRING', DBF_FWDLINK => 'DBF_STRING', @@ -235,8 +237,9 @@ sub printField { $outStr = sprintf('%-5s %.8f', $field, $fieldData); } elsif ( $dataType eq 'DBF_CHAR' ) { $outStr = sprintf('%-5s %d', $field, ord($fieldData)); - }else { - # DBF_LONG, DBF_SHORT, DBF_UCHAR, DBF_ULONG, DBF_USHORT + } else { + # DBF_INT64, DBF_LONG, DBF_SHORT, + # DBF_UINT64, DBF_ULONG, DBF_USHORT, DBF_UCHAR, $outStr = sprintf('%-5s %d', $field, $fieldData); } From f99bb637b95103bcf082412ed2452af29c54071f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 20 Jul 2020 18:22:37 -0500 Subject: [PATCH 28/29] ca: Minor cleanups in capr.pl --- modules/ca/src/perl/capr.pl | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/ca/src/perl/capr.pl b/modules/ca/src/perl/capr.pl index fd0c076b9..87a058c6f 100644 --- a/modules/ca/src/perl/capr.pl +++ b/modules/ca/src/perl/capr.pl @@ -88,7 +88,7 @@ if (@ARGV) { printRecord($_, @ARGV); } else { if (m/^ \s* ([]+:;<>0-9A-Za-z[-]+) (?:\. \w+)? \s* , \s* (\d+) \s* $/x) { - # Recognizes ",n" as an interest leve, drops any ".FIELD" part + # Recognizes ",n" as an interest level, drops any ".FIELD" part printRecord($1, $2); } else { # Drop any ".FIELD" part @@ -273,14 +273,15 @@ sub printField { sub caget { my @chans = map { CA->new($_); } @_; - #clear results; + #clear any previous results; %callback_data = (); %timed_out = (); eval { CA->pend_io($opt_w); }; if ($@) { if ($@ =~ m/^ECA_TIMEOUT/) { - my $err = (@chans > 1) ? 'some PV(s)' : '"' . $chans[0]->name . '"'; + my $name = $chans[0]->name; + my $err = (@chans > 1) ? 'some fields' : "'$name'"; print "Channel connect timed out: $err not found.\n"; foreach my $chan (@chans) { $timed_out{$chan->name} = !$chan->is_connected; @@ -292,14 +293,12 @@ sub caget { } map { - my $type; - $type = $_->field_type; - $_->get_callback(\&caget_callback, $type); + $_->get_callback(\&caget_callback, $_->field_type); } @chans; - my $fields_read = @chans; - $callback_incomplete = @chans; - CA->pend_event(0.1) while $callback_incomplete; + my $fields_read = $callback_incomplete = @chans; + CA->pend_event(0.1) + while $callback_incomplete; return $fields_read; } @@ -328,9 +327,10 @@ sub printRecord { my @bases = (); #bases, from parser foreach my $field (sort keys %{$record{$recType}}) { # Skip DTYP field if this rec type doesn't have device support defined - if ($field eq 'DTYP' && !(exists($device{$recType}))) { next; } + next if $field eq 'DTYP' && !exists($device{$recType}); my ($fType, $fInterest, $base) = getFieldParams($recType, $field); + # FIXME: Support waveform.VAL fields etc. unless( $fType eq 'DBF_NOACCESS' ) { if ($interest >= $fInterest ) { my $fToGet = "$name.$field"; @@ -385,8 +385,8 @@ sub printRecordList { if (exists($record{$type}) ) { print("Record type - $type\n"); foreach my $fkey (sort keys %{$record{$type}}) { - printf('%-4s', $fkey); - printf(" interest = $record{$type}{$fkey}[$iIdx]"); + printf('%-8s', $fkey); + printf(" interest = $record{$type}{$fkey}[$iIdx]"); printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]); print (" base = $record{$type}{$fkey}[$bIdx]\n"); } From 10d472202dc2bf1fa5c569d2a14d460e95030564 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 21 Jul 2020 15:17:37 -0500 Subject: [PATCH 29/29] Fix some ca/perl builds Use the pre-expanded Perl configuration variables. Conda builds of Perl need these (and a fix to the Perl config files, see https://github.com/conda/conda/issues/8425 for details). --- modules/ca/src/perl/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ca/src/perl/Makefile b/modules/ca/src/perl/Makefile index 65619e93e..532780d4c 100644 --- a/modules/ca/src/perl/Makefile +++ b/modules/ca/src/perl/Makefile @@ -13,11 +13,11 @@ ifdef T_A PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname) PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME) - PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlib) + PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlibexp) PERL_h = $(PERL_ARCHLIB)/CORE/perl.h - EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils - PERLBIN := $(shell $(PERL) ../perlConfig.pl bin) + EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlibexp)/ExtUtils + PERLBIN := $(shell $(PERL) ../perlConfig.pl binexp) XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp)) # Special settings for Darwin: