diff --git a/client/castApp/src/Makefile b/client/castApp/src/Makefile index 8d95a8e..0cb31e8 100644 --- a/client/castApp/src/Makefile +++ b/client/castApp/src/Makefile @@ -48,6 +48,11 @@ testAddEnvVars_SRCS += testAddEnvVars.c testAddEnvVars_SYS_LIBS_WIN32 = ws2_32 TESTS += testAddEnvVars +TESTPROD_HOST += testAddExcludePattern +testAddExcludePattern_SRCS += testAddExcludePattern.c +testAddExcludePattern_SYS_LIBS_WIN32 = ws2_32 +TESTS += testAddExcludePattern + TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) TESTPROD = $(TESTPROD_HOST) diff --git a/client/castApp/src/caster.c b/client/castApp/src/caster.c index 7ae2cea..b838797 100644 --- a/client/castApp/src/caster.c +++ b/client/castApp/src/caster.c @@ -114,6 +114,7 @@ void casterInit(caster_t *self) self->onmsg = &casterShowMsgDefault; self->current = casterStateInit; self->timeout = reccastTimeout; + ellInit(&self->exclude_patterns); if(shSocketPair(self->wakeup)) errlogPrintf("Error: casterInit failed to create shutdown socket: %d\n", SOCKERRNO); @@ -142,6 +143,10 @@ void casterShutdown(caster_t *self) free(self->extra_envs); epicsMutexUnlock(self->lock); + epicsMutexMustLock(self->lock); + ellFree(&self->exclude_patterns); + epicsMutexUnlock(self->lock); + epicsEventDestroy(self->shutdownEvent); self->shutdownEvent = NULL; if (self->wakeup[0] != INVALID_SOCKET) { diff --git a/client/castApp/src/caster.h b/client/castApp/src/caster.h index a4172df..4199f6d 100644 --- a/client/castApp/src/caster.h +++ b/client/castApp/src/caster.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "sockhelpers.h" @@ -35,6 +36,11 @@ typedef enum { casterStateDone, } casterState; +typedef struct { + ELLNODE node; + char *item_str; +} string_list_t; + typedef struct _caster_t { double timeout; @@ -71,6 +77,8 @@ typedef struct _caster_t { char **extra_envs; int num_extra_envs; + ELLLIST exclude_patterns; + } caster_t; epicsShareFunc @@ -101,6 +109,9 @@ int casterPushPDB(void *junk, caster_t *caster); epicsShareFunc void addReccasterEnvVars(caster_t* self, int argc, char **argv); +epicsShareFunc +void addReccasterExcludePattern(caster_t* self, int argc, char **argv); + /* internal */ epicsShareFunc diff --git a/client/castApp/src/castinit.c b/client/castApp/src/castinit.c index 4efe4e7..34da6c1 100644 --- a/client/castApp/src/castinit.c +++ b/client/castApp/src/castinit.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -200,6 +202,69 @@ static void addReccasterEnvVarsCallFunc(const iocshArgBuf *args) addReccasterEnvVars(&thecaster, args[0].aval.ac, args[0].aval.av); } +void addReccasterExcludePattern(caster_t* self, int argc, char **argv) { + size_t i; + int dup; + ELLNODE *cur; + argv++; argc--; /* skip function arg */ + if (argc < 1) { + errlogSevPrintf(errlogMinor, "At least one argument expected for addReccasterExcludePattern\n"); + return; + } + epicsMutexMustLock(self->lock); + if (self->shutdown) { + /* shutdown in progress, silent no-op */ + epicsMutexUnlock(self->lock); + return; + } + /* error if called after iocInit() */ + if (self->current != casterStateInit) { + errlogSevPrintf(errlogMinor, "addReccasterExcludePattern called after iocInit() when reccaster might already be connected. Not supported\n"); + epicsMutexUnlock(self->lock); + return; + } + + for (i = 0; i < argc; i++) { + const size_t arg_len = strlen(argv[i]) + 1; + if (argv[i][0] == '\0') { + errlogSevPrintf(errlogMinor, "Arg is empty for addReccasterExcludePattern\n"); + continue; + } + /* check duplicates */ + dup = 0; + for(cur = ellFirst(&self->exclude_patterns); cur; cur = ellNext(cur)) { + const string_list_t *ppattern = CONTAINER(cur, string_list_t, node); + if (strcmp(argv[i], ppattern->item_str) == 0) { + dup = 1; + break; + } + } + if (dup) { + errlogSevPrintf(errlogMinor, "Duplicate pattern %s in addReccasterExcludePattern\n", argv[i]); + continue; + } + string_list_t *new_node = mallocMustSucceed(sizeof(string_list_t) + arg_len, "addReccasterExcludePattern"); + new_node->item_str = (char *)(new_node + 1); + memcpy(new_node->item_str, argv[i], arg_len); + + ellAdd(&self->exclude_patterns, &new_node->node); + } + + epicsMutexUnlock(self->lock); +} + +static const iocshArg addReccasterExcludePatternArg0 = { "excludePattern", iocshArgArgv }; +static const iocshArg * const addReccasterExcludePatternArgs[] = { &addReccasterExcludePatternArg0 }; +static const iocshFuncDef addReccasterExcludePatternFuncDef = { + "addReccasterExcludePattern", + 1, + addReccasterExcludePatternArgs +}; + +static void addReccasterExcludePatternCallFunc(const iocshArgBuf *args) { + addReccasterExcludePattern(&thecaster, args[0].aval.ac, args[0].aval.av); +} + static void reccasterRegistrar(void) { osiSockAttach(); @@ -210,6 +275,7 @@ static void reccasterRegistrar(void) thepriv.laststate=casterStateInit; strcpy(thepriv.lastmsg, "Initializing"); iocshRegister(&addReccasterEnvVarsFuncDef,addReccasterEnvVarsCallFunc); + iocshRegister(&addReccasterExcludePatternFuncDef,addReccasterExcludePatternCallFunc); } static long init_record(void* prec) diff --git a/client/castApp/src/dbcb.c b/client/castApp/src/dbcb.c index 4d9cc04..7559e19 100644 --- a/client/castApp/src/dbcb.c +++ b/client/castApp/src/dbcb.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -87,12 +88,19 @@ static int pushRecord(caster_t *caster, DBENTRY *pent) { dbCommon *prec = pent->precnode->precord; ssize_t rid; + ELLNODE *cur; int ret = 0; long status; if(dbIsAlias(pent)) return 0; + for(cur = ellFirst(&caster->exclude_patterns); cur; cur = ellNext(cur)) { + const string_list_t *ppattern = CONTAINER(cur, string_list_t, node); + if(epicsStrGlobMatch(prec->name, ppattern->item_str)) + return 0; + } + rid = casterSendRecord(caster, prec->rdes->name, prec->name); if(rid<=0) return rid; diff --git a/client/castApp/src/testAddExcludePattern.c b/client/castApp/src/testAddExcludePattern.c new file mode 100644 index 0000000..8f0a9d6 --- /dev/null +++ b/client/castApp/src/testAddExcludePattern.c @@ -0,0 +1,173 @@ +#include + +#include +#include + +#include "caster.h" + +void* epicsRtemsFSImage; + +static void testLog(void* arg, struct _caster_t* self) +{ + testDiag("ERR %s", self->lastmsg); +} + +static void testAddExcludePatternX(void) +{ + int i = 0; + caster_t caster; + casterInit(&caster); + caster.onmsg = &testLog; + + int argc; + char *argvlist[5]; + argvlist[0] = "addReccasterExcludePattern"; + + char *expectedPatterns[] = + { + "*_", + "*__", + "*:Intrnl:*", + "*_internal", + "*exclude_me" + }; + int expectedNumPatterns = 0; + + testDiag("Testing addReccasterExcludePattern with one good env"); + argvlist[1] = "*_"; + argc = 2; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + addReccasterExcludePattern(&caster, argc, argvlist); + expectedNumPatterns++; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + ELLNODE *cur; + cur = ellFirst(&caster.exclude_patterns); + while (cur != NULL) { + string_list_t *temp = (string_list_t *)cur; + testOk1(strcmp(temp->item_str, expectedPatterns[i]) == 0); + i++; + cur = ellNext(cur); + } + + testDiag("Testing addReccasterExcludePattern with two more patterns"); + argvlist[1] = "*__"; + argvlist[2] = "*:Intrnl:*"; + argc = 3; + i = 0; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + addReccasterExcludePattern(&caster, argc, argvlist); + expectedNumPatterns += 2; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + cur = ellFirst(&caster.exclude_patterns); + while (cur != NULL) { + string_list_t *temp = (string_list_t *)cur; + testOk1(strcmp(temp->item_str, expectedPatterns[i]) == 0); + i++; + cur = ellNext(cur); + } + + testDiag("Testing addReccasterExcludePattern with a duplicate pattern"); + argvlist[1] = "*_"; + argc = 2; + i = 0; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + addReccasterExcludePattern(&caster, argc, argvlist); + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + cur = ellFirst(&caster.exclude_patterns); + while (cur != NULL) { + string_list_t *temp = (string_list_t *)cur; + testOk1(strcmp(temp->item_str, expectedPatterns[i]) == 0); + i++; + cur = ellNext(cur); + } + + testDiag("Testing addReccasterExcludePattern with a new and a duplicate"); + argvlist[1] = "*_internal"; + argvlist[2] = "*__"; + argc = 3; + i = 0; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + addReccasterExcludePattern(&caster, argc, argvlist); + expectedNumPatterns++; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + cur = ellFirst(&caster.exclude_patterns); + while (cur != NULL) { + string_list_t *temp = (string_list_t *)cur; + testOk1(strcmp(temp->item_str, expectedPatterns[i]) == 0); + i++; + cur = ellNext(cur); + } + + testDiag("Testing addReccasterExcludePattern with two of the same pattern"); + argvlist[1] = "*exclude_me"; + argvlist[2] = "*exclude_me"; + argc = 3; + i = 0; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + addReccasterExcludePattern(&caster, argc, argvlist); + expectedNumPatterns++; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + cur = ellFirst(&caster.exclude_patterns); + while (cur != NULL) { + string_list_t *temp = (string_list_t *)cur; + testOk1(strcmp(temp->item_str, expectedPatterns[i]) == 0); + i++; + cur = ellNext(cur); + } + + testDiag("Testing addReccasterExcludePattern with duplicates in argv and exclude pattern list"); + argvlist[1] = "*__"; + argvlist[2] = "*__"; + argc = 3; + i = 0; + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + addReccasterExcludePattern(&caster, argc, argvlist); + testOk1(caster.exclude_patterns.count==expectedNumPatterns); + cur = ellFirst(&caster.exclude_patterns); + while (cur != NULL) { + string_list_t *temp = (string_list_t *)cur; + testOk1(strcmp(temp->item_str, expectedPatterns[i]) == 0); + i++; + cur = ellNext(cur); + } + + epicsEventSignal(caster.shutdownEvent); + casterShutdown(&caster); +} + +static void testAddExcludePatternBadInput() +{ + caster_t caster; + casterInit(&caster); + caster.onmsg = &testLog; + + int argc; + char *argvlist[2]; + argvlist[0] = "addReccasterExcludePattern"; + + testDiag("Testing addReccasterExcludePattern with no arguments"); + argc = 1; + testOk1(caster.exclude_patterns.count==0); + addReccasterExcludePattern(&caster, argc, argvlist); + testOk1(caster.exclude_patterns.count==0); + + testDiag("Testing addReccasterExcludePattern with empty string argument"); + argvlist[1] = ""; + argc = 2; + testOk1(caster.exclude_patterns.count==0); + addReccasterExcludePattern(&caster, argc, argvlist); + testOk1(caster.exclude_patterns.count==0); + + epicsEventSignal(caster.shutdownEvent); + casterShutdown(&caster); +} + +MAIN(testAddExcludePattern) +{ + testPlan(37); + osiSockAttach(); + testAddExcludePatternX(); + testAddExcludePatternBadInput(); + osiSockRelease(); + return testDone(); +} diff --git a/client/iocBoot/iocdemo/st.cmd b/client/iocBoot/iocdemo/st.cmd index fe81b08..35c72fa 100755 --- a/client/iocBoot/iocdemo/st.cmd +++ b/client/iocBoot/iocdemo/st.cmd @@ -23,6 +23,9 @@ epicsEnvSet("SECTOR", "mysector") addReccasterEnvVars("CONTACT", "SECTOR") addReccasterEnvVars("BUILDING") +addReccasterExcludePattern("*_", "*__") +addReccasterExcludePattern("*exclude_this") + ## Load record instances dbLoadRecords("../../db/reccaster.db", "P=$(IOCSH_NAME):") dbLoadRecords("../../db/somerecords.db","P=$(IOCSH_NAME):")