From 5f43a8986ca59b11b9424411f2fe232b1a7bef8b Mon Sep 17 00:00:00 2001 From: Madeline Park Date: Mon, 7 Jul 2025 10:30:30 -0700 Subject: [PATCH 1/5] add new iocsh function addReccasterExcludePattern --- client/castApp/src/Makefile | 5 + client/castApp/src/caster.c | 5 + client/castApp/src/caster.h | 11 ++ client/castApp/src/castinit.c | 69 ++++++++ client/castApp/src/dbcb.c | 9 ++ client/castApp/src/testAddExcludePattern.c | 173 +++++++++++++++++++++ client/iocBoot/iocdemo/st.cmd | 3 + 7 files changed, 275 insertions(+) create mode 100644 client/castApp/src/testAddExcludePattern.c diff --git a/client/castApp/src/Makefile b/client/castApp/src/Makefile index 8d95a8e3..0cb31e89 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 7ae2cea3..b838797a 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 a4172df2..4199f6db 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 4efe4e79..0b268ca7 100644 --- a/client/castApp/src/castinit.c +++ b/client/castApp/src/castinit.c @@ -200,6 +200,74 @@ 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; + 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 (int i = 0; i < argc; i++) { + if (argv[i][0] == '\0') { + errlogSevPrintf(errlogMajor, "Arg is empty for addReccasterExcludePattern\n"); + continue; + } + /* check duplicates */ + int dup = 0; + ELLNODE *cur = ellFirst(&self->exclude_patterns); + while (cur != NULL) { + string_list_t *temp = (string_list_t *)cur; + if (strcmp(argv[i], temp->item_str) == 0) { + dup = 1; + break; + } + cur = ellNext(cur); + } + if (dup) { + errlogSevPrintf(errlogMinor, "Duplicate pattern %s in addReccasterExcludePattern\n", argv[i]); + continue; + } + string_list_t *new = malloc(sizeof(string_list_t)); + if (new == NULL) { + errlogSevPrintf(errlogMajor, "Error in addReccasterExcludePattern - malloc error for creating linked list node"); + break; + } + new->item_str = strdup(argv[i]); + if (new->item_str == NULL) { + errlogSevPrintf(errlogMajor, "Error in addReccasterExcludePattern - strdup error for copying %s to new->item_str from addReccasterExcludePattern\n", argv[i]); + free(new); /* frees if strdup fails */ + break; + } + ellAdd(&self->exclude_patterns, &new->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 +278,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 4d9cc046..4f6a8463 100644 --- a/client/castApp/src/dbcb.c +++ b/client/castApp/src/dbcb.c @@ -87,12 +87,21 @@ 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; + cur = ellFirst(&caster->exclude_patterns); + while (cur != NULL) { + string_list_t *temp = (string_list_t *)cur; + if(epicsStrGlobMatch(prec->name, temp->item_str)) + return 0; + cur = ellNext(cur); + } + 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 00000000..8f0a9d67 --- /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 fe81b085..35c72fa0 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):") From f93c0cf4579ba9693fb223e97c1e6849bc047d03 Mon Sep 17 00:00:00 2001 From: Madeline Park Date: Wed, 10 Sep 2025 17:34:04 -0700 Subject: [PATCH 2/5] naming change --- client/castApp/src/castinit.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/castApp/src/castinit.c b/client/castApp/src/castinit.c index 0b268ca7..406d2fe2 100644 --- a/client/castApp/src/castinit.c +++ b/client/castApp/src/castinit.c @@ -222,7 +222,7 @@ void addReccasterExcludePattern(caster_t* self, int argc, char **argv) { for (int i = 0; i < argc; i++) { if (argv[i][0] == '\0') { - errlogSevPrintf(errlogMajor, "Arg is empty for addReccasterExcludePattern\n"); + errlogSevPrintf(errlogMinor, "Arg is empty for addReccasterExcludePattern\n"); continue; } /* check duplicates */ @@ -240,18 +240,18 @@ void addReccasterExcludePattern(caster_t* self, int argc, char **argv) { errlogSevPrintf(errlogMinor, "Duplicate pattern %s in addReccasterExcludePattern\n", argv[i]); continue; } - string_list_t *new = malloc(sizeof(string_list_t)); - if (new == NULL) { + string_list_t *new_list = malloc(sizeof(string_list_t)); + if (new_list == NULL) { errlogSevPrintf(errlogMajor, "Error in addReccasterExcludePattern - malloc error for creating linked list node"); break; } - new->item_str = strdup(argv[i]); - if (new->item_str == NULL) { + new_list->item_str = strdup(argv[i]); + if (new_list->item_str == NULL) { errlogSevPrintf(errlogMajor, "Error in addReccasterExcludePattern - strdup error for copying %s to new->item_str from addReccasterExcludePattern\n", argv[i]); - free(new); /* frees if strdup fails */ + free(new_list); /* frees if strdup fails */ break; } - ellAdd(&self->exclude_patterns, &new->node); + ellAdd(&self->exclude_patterns, &new_list->node); } epicsMutexUnlock(self->lock); } From 925515dbfdd9627bedb436125d1f605aee50c4eb Mon Sep 17 00:00:00 2001 From: Madeline Park Date: Thu, 11 Sep 2025 11:07:46 -0700 Subject: [PATCH 3/5] fixed strdup memory leak --- client/castApp/src/caster.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/castApp/src/caster.c b/client/castApp/src/caster.c index b838797a..c4709567 100644 --- a/client/castApp/src/caster.c +++ b/client/castApp/src/caster.c @@ -120,6 +120,12 @@ void casterInit(caster_t *self) errlogPrintf("Error: casterInit failed to create shutdown socket: %d\n", SOCKERRNO); } +static void nodeFree(void *node) { + string_list_t *temp = (string_list_t *)node; + free(temp->item_str); + free(temp); +} + void casterShutdown(caster_t *self) { int i; @@ -144,7 +150,7 @@ void casterShutdown(caster_t *self) epicsMutexUnlock(self->lock); epicsMutexMustLock(self->lock); - ellFree(&self->exclude_patterns); + ellFree2(&self->exclude_patterns, &nodeFree); epicsMutexUnlock(self->lock); epicsEventDestroy(self->shutdownEvent); From 5836a6d3ce6632f5ed350532e72af790e8e19c60 Mon Sep 17 00:00:00 2001 From: Madeline Park Date: Thu, 11 Sep 2025 12:17:50 -0700 Subject: [PATCH 4/5] fixed loop to be compatible with C89 --- client/castApp/src/castinit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/castApp/src/castinit.c b/client/castApp/src/castinit.c index 406d2fe2..1014257a 100644 --- a/client/castApp/src/castinit.c +++ b/client/castApp/src/castinit.c @@ -201,7 +201,7 @@ static void addReccasterEnvVarsCallFunc(const iocshArgBuf *args) } void addReccasterExcludePattern(caster_t* self, int argc, char **argv) { - // size_t i; + size_t i; argv++; argc--; /* skip function arg */ if (argc < 1) { errlogSevPrintf(errlogMinor, "At least one argument expected for addReccasterExcludePattern\n"); @@ -220,7 +220,7 @@ void addReccasterExcludePattern(caster_t* self, int argc, char **argv) { return; } - for (int i = 0; i < argc; i++) { + for (i = 0; i < argc; i++) { if (argv[i][0] == '\0') { errlogSevPrintf(errlogMinor, "Arg is empty for addReccasterExcludePattern\n"); continue; From 31d290b50f8f02fceef931c3a9b5dbb8b20e66d7 Mon Sep 17 00:00:00 2001 From: Tynan Ford Date: Thu, 25 Sep 2025 01:42:57 -0700 Subject: [PATCH 5/5] Combine memory allocation for exclude_pattern items, simplify loops --- client/castApp/src/caster.c | 8 +------- client/castApp/src/castinit.c | 33 +++++++++++++++------------------ client/castApp/src/dbcb.c | 9 ++++----- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/client/castApp/src/caster.c b/client/castApp/src/caster.c index c4709567..b838797a 100644 --- a/client/castApp/src/caster.c +++ b/client/castApp/src/caster.c @@ -120,12 +120,6 @@ void casterInit(caster_t *self) errlogPrintf("Error: casterInit failed to create shutdown socket: %d\n", SOCKERRNO); } -static void nodeFree(void *node) { - string_list_t *temp = (string_list_t *)node; - free(temp->item_str); - free(temp); -} - void casterShutdown(caster_t *self) { int i; @@ -150,7 +144,7 @@ void casterShutdown(caster_t *self) epicsMutexUnlock(self->lock); epicsMutexMustLock(self->lock); - ellFree2(&self->exclude_patterns, &nodeFree); + ellFree(&self->exclude_patterns); epicsMutexUnlock(self->lock); epicsEventDestroy(self->shutdownEvent); diff --git a/client/castApp/src/castinit.c b/client/castApp/src/castinit.c index 1014257a..34da6c18 100644 --- a/client/castApp/src/castinit.c +++ b/client/castApp/src/castinit.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -202,6 +204,8 @@ static void addReccasterEnvVarsCallFunc(const iocshArgBuf *args) 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"); @@ -221,38 +225,31 @@ void addReccasterExcludePattern(caster_t* self, int argc, char **argv) { } 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 */ - int dup = 0; - ELLNODE *cur = ellFirst(&self->exclude_patterns); - while (cur != NULL) { - string_list_t *temp = (string_list_t *)cur; - if (strcmp(argv[i], temp->item_str) == 0) { + 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; } - cur = ellNext(cur); } if (dup) { errlogSevPrintf(errlogMinor, "Duplicate pattern %s in addReccasterExcludePattern\n", argv[i]); continue; } - string_list_t *new_list = malloc(sizeof(string_list_t)); - if (new_list == NULL) { - errlogSevPrintf(errlogMajor, "Error in addReccasterExcludePattern - malloc error for creating linked list node"); - break; - } - new_list->item_str = strdup(argv[i]); - if (new_list->item_str == NULL) { - errlogSevPrintf(errlogMajor, "Error in addReccasterExcludePattern - strdup error for copying %s to new->item_str from addReccasterExcludePattern\n", argv[i]); - free(new_list); /* frees if strdup fails */ - break; - } - ellAdd(&self->exclude_patterns, &new_list->node); + 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); } diff --git a/client/castApp/src/dbcb.c b/client/castApp/src/dbcb.c index 4f6a8463..7559e19b 100644 --- a/client/castApp/src/dbcb.c +++ b/client/castApp/src/dbcb.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -94,12 +95,10 @@ static int pushRecord(caster_t *caster, DBENTRY *pent) if(dbIsAlias(pent)) return 0; - cur = ellFirst(&caster->exclude_patterns); - while (cur != NULL) { - string_list_t *temp = (string_list_t *)cur; - if(epicsStrGlobMatch(prec->name, temp->item_str)) + 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; - cur = ellNext(cur); } rid = casterSendRecord(caster, prec->rdes->name, prec->name);