diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..35f4728e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Build artifacts +*.o +*.lo +*.la +*.a +*.so +*.so.* +*.gcda +*.gcno +*.Po +remotedebugger +remotedebugger_gtest + +# Autotools generated files +Makefile +Makefile.in +aclocal.m4 +autom4te.cache/ +compile +config.guess +config.h +config.h.in +config.log +config.status +config.sub +configure +configure~ +configure.ac~ +depcomp +install-sh +libtool +ltmain.sh +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +missing +stamp-h1 +.deps/ + +# Test runtime artifacts +src/unittest/dummy_*/ diff --git a/src/rrdCommon.h b/src/rrdCommon.h index b91ff6f7b..bc7915232 100644 --- a/src/rrdCommon.h +++ b/src/rrdCommon.h @@ -97,6 +97,7 @@ typedef struct mbuffer { bool inDynamic; bool appendMode; deepsleep_event_et dsEvent; + char *suffix; // Holds the suffix split from issue type string, if any } data_buf; /*Structure for Message Header*/ @@ -124,6 +125,7 @@ typedef struct cache { char *issueString; struct cache *next; struct cache *prev; + char *suffix; // Persist the suffix for later use } cacheData; /*Structure for Device Propertiesr*/ diff --git a/src/rrdDynamic.c b/src/rrdDynamic.c index 2deee32ab..85416fcd6 100644 --- a/src/rrdDynamic.c +++ b/src/rrdDynamic.c @@ -244,12 +244,12 @@ void RRDRdmManagerDownloadRequest(issueNodeData *pissueStructNode, char *dynJSON strcpy(appendData,rbuf->mdata); strcat(appendData,APPEND_SUFFIX); RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Cache String updated in appendmode IssueStr:%s Length:%d\n", __FUNCTION__, __LINE__, appendData, strlen(appendData)); - append_item(strdup(msgDataString), strdup(appendData)); + append_item(strdup(msgDataString), strdup(appendData), rbuf->suffix); RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Setting Parameters Success and Cache Updated ...%s IssueStr:%s Length:%d\n", __FUNCTION__, __LINE__, msgDataString, appendData, strlen(appendData)); } else { - append_item(strdup(msgDataString), strdup((char *)rbuf->mdata)); + append_item(strdup(msgDataString), strdup((char *)rbuf->mdata), rbuf->suffix); RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Setting Parameters Success and Cache Updated ...%s IssueStr:%s Length:%d\n", __FUNCTION__, __LINE__, msgDataString, (char *)rbuf->mdata, strlen((char *)rbuf->mdata)); } } diff --git a/src/rrdEventProcess.c b/src/rrdEventProcess.c index 5164e7832..5210fae6c 100644 --- a/src/rrdEventProcess.c +++ b/src/rrdEventProcess.c @@ -79,7 +79,38 @@ void processIssueTypeEvent(data_buf *rbuf) cmdBuff = (data_buf *)malloc(sizeof(data_buf)); if (cmdBuff) { - dataMsgLen = strlen(cmdMap[index]) + 1; + char base[128] = {0}; + char local_suffix[128] = {0}; + split_issue_type(cmdMap[index], base, sizeof(base), local_suffix, sizeof(local_suffix)); + if (base[0] == '\0') + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Empty issue type after parsing token [%s], skipping... \n", __FUNCTION__, __LINE__, cmdMap[index]); + free(cmdBuff); + cmdBuff = NULL; + if (cmdMap[index]) + { + free(cmdMap[index]); + cmdMap[index] = NULL; + } + continue; + } + if (strstr(base, APPEND_SUFFIX) == NULL) + { + removeSpecialCharacterfromIssueTypeList(base); + } + if (base[0] == '\0') + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Empty base after sanitization for token [%s], skipping... \n", __FUNCTION__, __LINE__, cmdMap[index]); + free(cmdBuff); + cmdBuff = NULL; + if (cmdMap[index]) + { + free(cmdMap[index]); + cmdMap[index] = NULL; + } + continue; + } + dataMsgLen = strlen(base) + 1; RRD_data_buff_init(cmdBuff, EVENT_MSG, RRD_DEEPSLEEP_INVALID_DEFAULT); /* Setting Deafult Values*/ cmdBuff->inDynamic = rbuf->inDynamic; if(cmdBuff->inDynamic) @@ -88,9 +119,19 @@ void processIssueTypeEvent(data_buf *rbuf) } cmdBuff->appendMode = rbuf->appendMode; cmdBuff->mdata = (char *)calloc(1, dataMsgLen); + + /* Store suffix for this issue type */ + cmdBuff->suffix = NULL; + if (local_suffix[0] != '\0') { + cmdBuff->suffix = strdup(local_suffix); + if (cmdBuff->suffix == NULL) + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to allocate memory for suffix... \n", __FUNCTION__, __LINE__); + } + } if (cmdBuff->mdata) { - strncpy((char *)cmdBuff->mdata, cmdMap[index], dataMsgLen); + strncpy((char *)cmdBuff->mdata, base, dataMsgLen); processIssueType(cmdBuff); } else @@ -99,6 +140,11 @@ void processIssueTypeEvent(data_buf *rbuf) } if(cmdBuff) { + if (cmdBuff->suffix) + { + free(cmdBuff->suffix); + cmdBuff->suffix = NULL; + } free(cmdBuff); cmdBuff = NULL; } @@ -476,7 +522,14 @@ issueData* processIssueTypeInDynamicProfileappend(data_buf *rbuf, issueNodeData free(dynJSONPath); // Get the command for received Issue Type of the Issue Category dynamicdata = getIssueCommandInfo(pIssueNode, jsonParsed, rbuf->mdata); - RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Dynamic Profile Data: RFCValue: %s, Command: %s, Timeout: %d... \n", __FUNCTION__, __LINE__, dynamicdata->rfcvalue, dynamicdata->command, dynamicdata->timeout); + if (dynamicdata != NULL) + { + RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Dynamic Profile Data: RFCValue: %s, Command: %s, Timeout: %d... \n", __FUNCTION__, __LINE__, dynamicdata->rfcvalue, dynamicdata->command, dynamicdata->timeout); + } + else + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Dynamic Profile command info is empty/invalid for issue %s, skip append path... \n", __FUNCTION__, __LINE__, rbuf->mdata); + } } else { @@ -514,7 +567,14 @@ issueData* processIssueTypeInStaticProfileappend(data_buf *rbuf, issueNodeData * RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Issue Data Node: %s and Sub-Node: %s found in Static JSON File %s... \n", __FUNCTION__, __LINE__, pIssueNode->Node, pIssueNode->subNode, RRD_JSON_FILE); // Get the command for received Issue Type of the Issue Category staticdata = getIssueCommandInfo(pIssueNode, jsonParsed, rbuf->mdata); - RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Static Profile Data: RFCValue: %s, Command: %s, Timeout: %d... \n", __FUNCTION__, __LINE__, staticdata->rfcvalue, staticdata->command, staticdata->timeout); + if (staticdata != NULL) + { + RDK_LOG(RDK_LOG_INFO, LOG_REMDEBUG, "[%s:%d]: Static Profile Data: RFCValue: %s, Command: %s, Timeout: %d... \n", __FUNCTION__, __LINE__, staticdata->rfcvalue, staticdata->command, staticdata->timeout); + } + else + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Static Profile command info is empty/invalid for issue %s... \n", __FUNCTION__, __LINE__, rbuf->mdata); + } } else { @@ -555,6 +615,29 @@ static void processIssueTypeInInstalledPackage(data_buf *rbuf, issueNodeData *pI suffixlen = strlen(RDM_PKG_SUFFIX); dynJSONPath = (char *)malloc(persistentAppslen + prefixlen + suffixlen + strlen(pIssueNode->Node) + rrdjsonlen + 1); #else + if ((rbuf == NULL) || (rbuf->jsonPath == NULL)) + { + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: jsonPath is NULL, skipping installed package check... \n", __FUNCTION__, __LINE__); + if (rbuf != NULL) + { + if (rbuf->mdata != NULL) + { + free(rbuf->mdata); + rbuf->mdata = NULL; + } + if (rbuf->suffix != NULL) + { + free(rbuf->suffix); + rbuf->suffix = NULL; + } + if (rbuf->jsonPath != NULL) + { + free(rbuf->jsonPath); + rbuf->jsonPath = NULL; + } + } + return; + } int utjsonlen = strlen(rbuf->jsonPath); dynJSONPath = (char *)malloc(utjsonlen + 1); #endif @@ -639,7 +722,7 @@ static void removeSpecialCharacterfromIssueTypeList(char *str) while (str[source] != '\0') { - if (isalnum(str[source]) || str[source] == ',' || str[source] == '.') + if (isalnum(str[source]) || str[source] == ',' || str[source] == '.') { str[destination] = str[source]; ++destination; @@ -663,7 +746,6 @@ static int issueTypeSplitter(char *input_str, const char delimeter, char ***args int cnt = 1, i = 0; char *str = input_str; - removeSpecialCharacterfromIssueTypeList(str); while (*str == delimeter) str++; diff --git a/src/rrdIarmEvents.c b/src/rrdIarmEvents.c index 323ba051a..71c58069d 100644 --- a/src/rrdIarmEvents.c +++ b/src/rrdIarmEvents.c @@ -338,6 +338,29 @@ void _rdmManagerEventHandler(const char *owner, IARM_EventId_t eventId, void *da } RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Cache.issueString=%s Cache.issueString.Len=%d\n", __FUNCTION__, __LINE__, cache->issueString, strlen(cache->issueString)); strncpy((char *)sendbuf->mdata, cache->issueString, recPkgNamelen); + if (cache->suffix && cache->suffix[0] != '\0') + { + sendbuf->suffix = strdup(cache->suffix); + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Restored suffix from cache struct: %s\n", __FUNCTION__, __LINE__, cache->suffix); + // Append suffix to mdata + size_t mdata_len = strlen(sendbuf->mdata); + size_t suffix_len = strlen(sendbuf->suffix); + size_t total_len = mdata_len + suffix_len + 1; + char *new_mdata = realloc(sendbuf->mdata, total_len); + if (new_mdata) + { + sendbuf->mdata = new_mdata; + strncat(sendbuf->mdata, sendbuf->suffix, suffix_len); + } + else + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to realloc mdata for suffix append\n", __FUNCTION__, __LINE__); + } + } + else + { + sendbuf->suffix = NULL; + } RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: IssueType: %s...\n", __FUNCTION__, __LINE__, (char *)sendbuf->mdata); snprintf(sendbuf->jsonPath, strlen(eventData->rdm_pkg_info.pkg_inst_path) + rrdjsonlen + 1, "%s%s", eventData->rdm_pkg_info.pkg_inst_path, RRD_JSON_FILE); sendbuf->inDynamic = true; diff --git a/src/rrdInterface.c b/src/rrdInterface.c index b69dd8936..f048fca2a 100644 --- a/src/rrdInterface.c +++ b/src/rrdInterface.c @@ -275,6 +275,7 @@ void RRD_data_buff_init(data_buf *sbuf, message_type_et sndtype, deepsleep_event sbuf->inDynamic = false; sbuf->appendMode = false; sbuf->dsEvent = deepSleepEvent; + sbuf->suffix = NULL; } /*Function: RRD_data_buff_deAlloc @@ -295,6 +296,10 @@ void RRD_data_buff_deAlloc(data_buf *sbuf) { free(sbuf->jsonPath); } + if (sbuf->suffix) + { + free(sbuf->suffix); + } free(sbuf); } } @@ -382,6 +387,29 @@ void _rdmDownloadEventHandler(rbusHandle_t handle, rbusEvent_t const* event, rbu } RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Cache.issueString=%s Cache.issueString.Len=%d\n", __FUNCTION__, __LINE__, cache->issueString, strlen(cache->issueString)); strncpy((char *)sendbuf->mdata, cache->issueString, recPkgNamelen); + if (cache->suffix && cache->suffix[0] != '\0') + { + sendbuf->suffix = strdup(cache->suffix); + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Restored suffix from cache struct: %s\n", __FUNCTION__, __LINE__, cache->suffix); + // Append suffix to mdata + size_t mdata_len = strlen(sendbuf->mdata); + size_t suffix_len = strlen(sendbuf->suffix); + size_t total_len = mdata_len + suffix_len + 1; + char *new_mdata = realloc(sendbuf->mdata, total_len); + if (new_mdata) + { + sendbuf->mdata = new_mdata; + strncat(sendbuf->mdata, sendbuf->suffix, suffix_len); + } + else + { + RDK_LOG(RDK_LOG_ERROR, LOG_REMDEBUG, "[%s:%d]: Failed to realloc mdata for suffix append\n", __FUNCTION__, __LINE__); + } + } + else + { + sendbuf->suffix = NULL; + } RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: IssueType: %s...\n", __FUNCTION__, __LINE__, (char *)sendbuf->mdata); snprintf(sendbuf->jsonPath, strlen(pkg_inst_path) + rrdjsonlen + 1, "%s%s", pkg_inst_path, RRD_JSON_FILE); sendbuf->inDynamic = true; diff --git a/src/rrdJsonParser.c b/src/rrdJsonParser.c index e06d93ac2..655646724 100644 --- a/src/rrdJsonParser.c +++ b/src/rrdJsonParser.c @@ -24,6 +24,7 @@ #include #include #include +#define RRD_MAX_SUFFIX_LEN 10 /* * @function removeSpecialChar @@ -46,6 +47,7 @@ void removeSpecialChar(char *str) } } + /* * @function getParamcount * @brief Calculates the total number of nodes (elements) in the input string, excluding delimiters. @@ -118,6 +120,110 @@ char * readJsonFile(char *jsonfile) return jsonfile_content; } + +/* + * @function split_issue_type + * @brief Utility to split base and suffix from issue type string. + * The input is always split at the first '_'. The base is the part + * before the underscore (never contains '_'). The suffix is only + * kept when its total length (including the leading '_') is at most + * RRD_MAX_SUFFIX_LEN (9) characters; longer suffixes are discarded. + * After length validation the suffix is sanitized: only the characters + * [A-Za-z0-9_-] are retained so that the value is safe to use in file + * names and shell command arguments without risk of injection. + * If no underscore is present the full input is the base. + * Examples: + * "Device.DeviceTime_ab12345" → base="Device.DeviceTime", + * suffix="_ab12345" (8 chars, accepted) + * "Device.DeviceTime_Search-uuid-very-long" + * → base="Device.DeviceTime", + * suffix="" (>9 chars, discarded) + * "Device.DeviceTime" → base="Device.DeviceTime", + * suffix="" + * "Device.DeviceTime_ab;rm" → base="Device.DeviceTime", + * suffix="_abrm" (unsafe ';' stripped) + * @param const char *input - The input string to split. + * @param char *base - Buffer to store the base part (never contains '_'). + * @param size_t base_len - Size of the base buffer. + * @param char *suffix - Buffer to store the suffix part when valid, else "". + * @param size_t suffix_len - Size of the suffix buffer. + * @return void + */ + +void split_issue_type(const char *input, char *base, size_t base_len, char *suffix, size_t suffix_len) +{ + if (base && base_len > 0) + { + base[0] = '\0'; + } + if (suffix && suffix_len > 0) + { + suffix[0] = '\0'; + } + + if (!input || !base || !suffix) + { + return; + } + + if (base_len == 0 || suffix_len == 0) + { + return; + } + + const char *p = input; + const char *split = NULL; + size_t appnd_len = strlen(APPEND_SUFFIX); + + while ((p = strchr(p, '_')) != NULL) { + // If this underscore starts APPEND_SUFFIX, skip over the whole APPEND_SUFFIX and keep searching + if (strncmp(p, APPEND_SUFFIX, appnd_len) == 0) { + p += appnd_len; + continue; + } else { + split = p; + break; + } + } + + if (split) + { + size_t b_len = (size_t)(split - input); + if (b_len >= base_len) b_len = base_len - 1; + strncpy(base, input, b_len); + base[b_len] = '\0'; + + // Suffix logic as before + if (strlen(split) <= RRD_MAX_SUFFIX_LEN) + { + size_t si = 0, di = 0; + size_t max_copy = suffix_len - 1; + while (split[si] != '\0' && di < max_copy) + { + char ch = split[si]; + if (isalnum((unsigned char)ch) || ch == '_' || ch == '-') + { + suffix[di++] = ch; + } + si++; + } + suffix[di] = '\0'; + } + else + { + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Suffix after '%s' exceeds max length (%zu > %d); discarding\n", + __FUNCTION__, __LINE__, base, strlen(split), RRD_MAX_SUFFIX_LEN); + suffix[0] = '\0'; + } + } + else + { + // No split point found, so base is the whole input (including APPEND_SUFFIX and its underscore if present) + strncpy(base, input, base_len - 1); + base[base_len - 1] = '\0'; + suffix[0] = '\0'; + } +} /* * @function readAndParseJSON * @brief Reads and parses the JSON file. @@ -327,7 +433,7 @@ issueData * getIssueCommandInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, tmpCommand = cJSON_Print(elem); if(tmpCommand) { - if(issuestdata->command != NULL) + if(issuestdata->command != NULL) { free(issuestdata->command); // Free previous command before overwriting } @@ -515,7 +621,11 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf { RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Memory allocation failed for rfcbuf\n",__FUNCTION__,__LINE__); free(buff->mdata); // free rfc data + buff->mdata = NULL; free(buff->jsonPath); // free rrd path info + buff->jsonPath = NULL; + free(buff->suffix); // free suffix + buff->suffix = NULL; return; } @@ -535,7 +645,11 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: %s Directory creation failed!!!\n",__FUNCTION__,__LINE__,outdir); free(rfcbuf); // free duplicated rfc data free(buff->mdata); // free rfc data + buff->mdata = NULL; free(buff->jsonPath); // free rrd path info + buff->jsonPath = NULL; + free(buff->suffix); // free suffix + buff->suffix = NULL; return; } else @@ -576,7 +690,26 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf else { RDK_LOG(RDK_LOG_DEBUG,LOG_REMDEBUG,"[%s:%d]: Continue uploading Debug Report for %s from %s... \n",__FUNCTION__,__LINE__,buff->mdata,outdir); - status = uploadDebugoutput(outdir,buff->mdata); + // Use the persisted suffix from buff for upload + char tarName[512] = {0}; + int tar_name_len = 0; + if (buff->suffix && buff->suffix[0] != '\0') + { + tar_name_len = snprintf(tarName, sizeof(tarName), "%s%s", buff->mdata, buff->suffix); + } + else + { + tar_name_len = snprintf(tarName, sizeof(tarName), "%s", buff->mdata); + } + if ((tar_name_len < 0) || ((size_t)tar_name_len >= sizeof(tarName))) + { + RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: Failed to build upload file name for %s. snprintf result:%d, buffer size:%zu\n", __FUNCTION__,__LINE__,buff->mdata,tar_name_len,sizeof(tarName)); + status = -1; + } + else + { + status = uploadDebugoutput(outdir, tarName); + } if(status != 0) { RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: RRD Upload Script Execution Failed!!! status:%d\n",__FUNCTION__,__LINE__,status); @@ -588,14 +721,22 @@ void checkIssueNodeInfo(issueNodeData *issuestructNode, cJSON *jsoncfg, data_buf } free(rfcbuf); // free duplicated rfc data free(buff->mdata); // free rfc data + buff->mdata = NULL; free(buff->jsonPath); // free rrd path info + buff->jsonPath = NULL; + free(buff->suffix); // free suffix + buff->suffix = NULL; } else { RDK_LOG(RDK_LOG_ERROR,LOG_REMDEBUG,"[%s:%d]: No Command excuted as RRD Failed to change directory:%s\n",__FUNCTION__,__LINE__,outdir); free(rfcbuf); // free duplicated rfc data free(buff->mdata); // free rfc data + buff->mdata = NULL; free(buff->jsonPath); // free rrd path info + buff->jsonPath = NULL; + free(buff->suffix); // free suffix + buff->suffix = NULL; } } } diff --git a/src/rrdJsonParser.h b/src/rrdJsonParser.h index 299436101..87c4a2622 100644 --- a/src/rrdJsonParser.h +++ b/src/rrdJsonParser.h @@ -47,6 +47,8 @@ issueData* getIssueCommandInfo(issueNodeData *issuestructNode, cJSON *jsoncfg,ch bool processAllDebugCommand(cJSON *jsoncfg, issueNodeData *issuestructNode, char *rfcbuf); bool processAllDeepSleepAwkMetricsCommands(cJSON *jsoncfg, issueNodeData *issuestructNode, char *rfcbuf); +void split_issue_type(const char *input, char *base, size_t base_len, char *suffix, size_t suffix_len); + #ifdef __cplusplus } #endif diff --git a/src/rrdRunCmdThread.c b/src/rrdRunCmdThread.c index ce873d5d5..3b209b3f4 100644 --- a/src/rrdRunCmdThread.c +++ b/src/rrdRunCmdThread.c @@ -86,19 +86,17 @@ void print_items(cacheData *node) * @param char *issueTypeData - The issue type data to store in the cache node. * @return cacheData* - Pointer to the created cache node, or NULL on failure. */ -cacheData* createCache( char *pkgData, char *issueTypeData) +cacheData* createCache(char *pkgData, char *issueTypeData, char *suffix) { cacheData *cache = NULL; cache = (cacheData *)malloc(sizeof(cacheData)); - /*Check if memory alloacted to Cache*/ if(cache) { - cache->mdata = NULL; - cache->issueString = NULL; - cache->next = NULL; - cache->prev = NULL; cache->mdata = pkgData; cache->issueString = issueTypeData; + cache->suffix = suffix ? strdup(suffix) : NULL; + cache->next = NULL; + cache->prev = NULL; } return cache; } @@ -110,32 +108,25 @@ cacheData* createCache( char *pkgData, char *issueTypeData) * @param char *issueTypeData - The issue type data to append. * @return void */ -void append_item(char *pkgData, char *issueTypeData) +void append_item(char *pkgData, char *issueTypeData, char *suffix) { RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Append Item with PkgData: %s and issue Type: %s to Cache \n",__FUNCTION__,__LINE__,pkgData,issueTypeData); - cacheData *rrdCachecnode = NULL; int i=0; i = pthread_mutex_lock(&rrdCacheMut); RDK_LOG(RDK_LOG_DEBUG,LOG_REMDEBUG,"[%s:%d]: RRD Mutex Lock...%d\n",__FUNCTION__,__LINE__,i); - - cacheData *tmp = createCache(pkgData,issueTypeData); - /* Check If the memory is allocated for new node*/ + cacheData *tmp = createCache(pkgData, issueTypeData, suffix); if(!tmp) { RDK_LOG(RDK_LOG_INFO,LOG_REMDEBUG,"[%s:%d]: Memory Allocation Failed : Cannot Append Item to Cache\n", __FUNCTION__, __LINE__); pthread_mutex_unlock(&rrdCacheMut); - return; + return; } - /* create Cache and store in node's data*/ - rrdCachecnode = cacheDataNode; - - /*Valid Cache, add node to list*/ if(rrdCachecnode != NULL) { tmp->next = rrdCachecnode; - rrdCachecnode->prev = tmp; + rrdCachecnode->prev = tmp; } cacheDataNode = tmp; pthread_mutex_unlock(&rrdCacheMut); @@ -235,6 +226,10 @@ void freecacheDataCacheNode(cacheData **node) { free(rrdCachetmpnode->mdata); free(rrdCachetmpnode->issueString); + if (rrdCachetmpnode->suffix) { + free(rrdCachetmpnode->suffix); + rrdCachetmpnode->suffix = NULL; + } rrdCachetmpnode->mdata = NULL; rrdCachetmpnode->issueString = NULL; free(rrdCachetmpnode); diff --git a/src/rrdRunCmdThread.h b/src/rrdRunCmdThread.h index a1bd9816a..4ef4cdda3 100644 --- a/src/rrdRunCmdThread.h +++ b/src/rrdRunCmdThread.h @@ -51,9 +51,9 @@ extern "C" /*Public Function*/ void initCache(void); -cacheData* createCache( char *pkgData, char *issueTypeData); +cacheData* createCache(char *pkgData, char *issueTypeData, char *suffix); void print_items(cacheData *node); -void append_item(char *pkgData, char *issueTypeData); +void append_item(char *pkgData, char *issueTypeData, char *suffix); void remove_item(cacheData *cache); void freecacheDataCacheNode(cacheData **node); cacheData* findPresentInCache(char *pkgData); diff --git a/src/rrd_logproc.c b/src/rrd_logproc.c index 30c0cff1c..a33aee466 100644 --- a/src/rrd_logproc.c +++ b/src/rrd_logproc.c @@ -121,7 +121,8 @@ int rrd_logproc_convert_issue_type(const char *input, char *output, size_t size) for (size_t i = 0; input[i] && j < size-1; ++i) { char c = input[i]; if (isalnum((unsigned char)c)) output[j++] = toupper((unsigned char)c); - else if (c == '_' || c == '-' || c == '.') output[j++] = '_'; + else if (c == '_' || c == '.') output[j++] = '_'; + else if (c == '-') output[j++] = '-'; // preserve hyphens so suffix UUID tokens remain distinct // skip other chars } output[j] = 0; diff --git a/src/unittest/UTJson/device.properties b/src/unittest/UTJson/device.properties new file mode 100644 index 000000000..e69de29bb diff --git a/src/unittest/rrdUnitTestRunner.cpp b/src/unittest/rrdUnitTestRunner.cpp index 7b8c6c837..c4ca02838 100644 --- a/src/unittest/rrdUnitTestRunner.cpp +++ b/src/unittest/rrdUnitTestRunner.cpp @@ -1051,6 +1051,8 @@ class RRDRdmManagerDownloadRequestTest : public ::testing::Test void SetUp() override { originalDevPropData = devPropData; + pthread_mutex_init(&rrdCacheMut, NULL); + cacheDataNode = NULL; string test_name = getCurrentTestName(); if (test_name == "DeepSleepAwakeEventIsFalse_SetParamReturnsFailure" || test_name == "DeepSleepAwakeEventIsTrue_SetParamReturnsFailure") { @@ -1062,6 +1064,15 @@ class RRDRdmManagerDownloadRequestTest : public ::testing::Test { devPropData = originalDevPropData; SetParamWrapper::clearImpl(); + pthread_mutex_lock(&rrdCacheMut); + while (cacheDataNode != NULL) + { + cacheData *next = cacheDataNode->next; + freecacheDataCacheNode(&cacheDataNode); + cacheDataNode = next; + } + pthread_mutex_unlock(&rrdCacheMut); + pthread_mutex_destroy(&rrdCacheMut); string test_name = getCurrentTestName(); if (test_name == "DeepSleepAwakeEventIsFalse_SetParamReturnsFailure") { @@ -1073,7 +1084,7 @@ class RRDRdmManagerDownloadRequestTest : public ::testing::Test TEST_F(RRDRdmManagerDownloadRequestTest, IssueStructNodeIsNull) { issueNodeData *issuestructNode = NULL; - data_buf buff; + data_buf buff = {}; buff.mdata = NULL; buff.jsonPath = NULL; buff.inDynamic = false; @@ -1087,7 +1098,7 @@ TEST_F(RRDRdmManagerDownloadRequestTest, DeepSleepAwakeEventIsFalse_SetParamRetu issueNodeData issuestructNode; issuestructNode.Node = strdup("MainNode"); issuestructNode.subNode = strdup("SubNode"); - data_buf buff; + data_buf buff = {}; buff.mdata = NULL; buff.jsonPath = strdup("UTJson/validJson.json"); buff.inDynamic = false; @@ -1112,7 +1123,7 @@ TEST_F(RRDRdmManagerDownloadRequestTest, DeepSleepAwakeEventIsTrue_SetParamRetur issueNodeData issuestructNode; issuestructNode.Node = strdup("MainNode"); issuestructNode.subNode = strdup("SubNode"); - data_buf buff; + data_buf buff = {}; buff.mdata = NULL; buff.jsonPath = strdup("UTJson/validJson.json"); buff.inDynamic = false; @@ -1132,8 +1143,41 @@ TEST_F(RRDRdmManagerDownloadRequestTest, DeepSleepAwakeEventIsFalse_SetParamRetu issueNodeData issuestructNode; issuestructNode.Node = strdup("MainNode"); issuestructNode.subNode = strdup("SubNode"); - data_buf buff; + data_buf buff = {}; + buff.mdata = strdup("ValidIssueTypeData"); + buff.suffix = strdup("_Search"); + buff.jsonPath = strdup("UTJson/validJson.json"); + buff.inDynamic = false; + EXPECT_CALL(mock_rbus_api, rbusValue_Init(_)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + EXPECT_CALL(mock_rbus_api, rbusValue_SetString(_, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + EXPECT_CALL(mock_rbus_api, rbus_set(_, _, _, _)) + .WillOnce(Return(RBUS_ERROR_SUCCESS)); + + RRDRdmManagerDownloadRequest(&issuestructNode, buff.jsonPath, &buff, false); + + cacheData *cache = findPresentInCache("RDK-RRD-MainNode"); + ASSERT_NE(cache, nullptr); + EXPECT_STREQ(cache->issueString, "ValidIssueTypeData"); + EXPECT_STREQ(cache->suffix, "_Search"); + remove_item(cache); + + free(buff.jsonPath); + free(buff.mdata); + free(buff.suffix); +} + +TEST_F(RRDRdmManagerDownloadRequestTest, DeepSleepAwakeEventIsFalse_SetParamReturnsSuccessInAppendModeWithSuffix) +{ + issueNodeData issuestructNode; + issuestructNode.Node = strdup("MainNode"); + issuestructNode.subNode = strdup("SubNode"); + data_buf buff = {}; buff.mdata = strdup("ValidIssueTypeData"); + buff.suffix = strdup("_Search"); + buff.appendMode = true; buff.jsonPath = strdup("UTJson/validJson.json"); buff.inDynamic = false; EXPECT_CALL(mock_rbus_api, rbusValue_Init(_)) @@ -1146,8 +1190,15 @@ TEST_F(RRDRdmManagerDownloadRequestTest, DeepSleepAwakeEventIsFalse_SetParamRetu RRDRdmManagerDownloadRequest(&issuestructNode, buff.jsonPath, &buff, false); + cacheData *cache = findPresentInCache("RDK-RRD-MainNode"); + ASSERT_NE(cache, nullptr); + EXPECT_STREQ(cache->issueString, "ValidIssueTypeData_apnd"); + EXPECT_STREQ(cache->suffix, "_Search"); + remove_item(cache); + free(buff.jsonPath); free(buff.mdata); + free(buff.suffix); } /* --------------- Test RRDProcessDeepSleepAwakeEvents() from rrdDeepSleep --------------- */ @@ -1177,7 +1228,7 @@ TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDataNull) TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDsEventIsInvalidDefault) { - data_buf rbuf; + data_buf rbuf = {}; rbuf.mdata = "Sample data"; rbuf.dsEvent = RRD_DEEPSLEEP_INVALID_DEFAULT; @@ -1186,7 +1237,7 @@ TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDsEventIsInvalidDefault) TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDsEventIsRdmDownloadPkgInitiateSetParamSuccess) { - data_buf rbuf; + data_buf rbuf = {}; rbuf.mdata = strdup("IssueNode"); rbuf.dsEvent = RRD_DEEPSLEEP_RDM_DOWNLOAD_PKG_INITIATE; RRDProcessDeepSleepAwakeEvents(&rbuf); @@ -1194,7 +1245,7 @@ TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDsEventIsRdmDownloadPkgInitiateSe TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDsEventIsRdmDownloadPkgInitiateSetParamFail) { - data_buf rbuf; + data_buf rbuf = {}; rbuf.mdata = strdup("IssueNode"); rbuf.dsEvent = RRD_DEEPSLEEP_RDM_DOWNLOAD_PKG_INITIATE; RRDProcessDeepSleepAwakeEvents(&rbuf); @@ -1202,7 +1253,7 @@ TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDsEventIsRdmDownloadPkgInitiateSe TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDsEventIsRdmPkgInstallCompleteInDynamicFalse) { - data_buf rbuf; + data_buf rbuf = {}; rbuf.mdata = strdup("IssueNode"); rbuf.dsEvent = RRD_DEEPSLEEP_RDM_PKG_INSTALL_COMPLETE; rbuf.inDynamic = false; @@ -1211,7 +1262,7 @@ TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDsEventIsRdmPkgInstallCompleteInD TEST_F(RRDProcessDeepSleepAwakeEventsTest, RbufDsEventIsRdmPkgInstallCompleteInDynamicTrue) { - data_buf rbuf; + data_buf rbuf = {}; rbuf.mdata = strdup("IssueNode"); rbuf.dsEvent = RRD_DEEPSLEEP_RDM_PKG_INSTALL_COMPLETE; rbuf.inDynamic = true; @@ -1347,7 +1398,7 @@ TEST(CreateCacheTest, HandlesNullPkgDataAndValidIssueTypeData) { char *pkgData = NULL; char *issueTypeData = strdup("ValidIssueTypeData"); - cacheData *result = createCache(pkgData, issueTypeData); + cacheData *result = createCache(pkgData, issueTypeData, NULL); ASSERT_NE(result, nullptr); ASSERT_EQ(result->mdata, nullptr); @@ -1363,7 +1414,7 @@ TEST(CreateCacheTest, HandlesValidPkgDataAndIssueTypeData) { char *pkgData = strdup("ValidPkgData"); char *issueTypeData = strdup("ValidIssueTypeData"); - cacheData *result = createCache(pkgData, issueTypeData); + cacheData *result = createCache(pkgData, issueTypeData, NULL); ASSERT_NE(result, nullptr); ASSERT_STREQ(result->mdata, "ValidPkgData"); @@ -1380,7 +1431,7 @@ TEST(CreateCacheTest, HandlesValidPkgDataAndNullIssueTypeData) { char *pkgData = strdup("ValidPkgData"); char *issueTypeData = NULL; - cacheData *result = createCache(pkgData, issueTypeData); + cacheData *result = createCache(pkgData, issueTypeData, NULL); ASSERT_NE(result, nullptr); ASSERT_STREQ(result->mdata, "ValidPkgData"); @@ -1396,7 +1447,7 @@ TEST(CreateCacheTest, HandlesNullPkgDataAndIssueTypeData) { char *pkgData = NULL; char *issueTypeData = NULL; - cacheData *result = createCache(pkgData, issueTypeData); + cacheData *result = createCache(pkgData, issueTypeData, NULL); ASSERT_NE(result, nullptr); ASSERT_EQ(result->mdata, nullptr); @@ -1443,7 +1494,7 @@ TEST_F(AppendItemTest, HandlesRrdCachecnodeNullAndPkgDataNullAndIssueTypeDataNul { char *pkgData = NULL; char *issueTypeData = NULL; - append_item(pkgData, issueTypeData); + append_item(pkgData, issueTypeData, NULL); ASSERT_NE(cacheDataNode, nullptr); ASSERT_EQ(cacheDataNode->mdata, nullptr); @@ -1454,7 +1505,7 @@ TEST_F(AppendItemTest, HandlesRrdCachecnodeNullAndPkgDataNullAndIssueTypeDataNot { char *pkgData = NULL; char *issueTypeData = strdup("ValidIssueTypeData"); - append_item(pkgData, issueTypeData); + append_item(pkgData, issueTypeData, NULL); ASSERT_NE(cacheDataNode, nullptr); ASSERT_EQ(cacheDataNode->mdata, nullptr); @@ -1465,7 +1516,7 @@ TEST_F(AppendItemTest, HandlesRrdCachecnodeNullAndPkgDataNotNullAndIssueTypeData { char *pkgData = strdup("ValidPkgData"); char *issueTypeData = NULL; - append_item(pkgData, issueTypeData); + append_item(pkgData, issueTypeData, NULL); ASSERT_NE(cacheDataNode, nullptr); ASSERT_STREQ(cacheDataNode->mdata, pkgData); @@ -1476,7 +1527,7 @@ TEST_F(AppendItemTest, HandlesRrdCachecnodeNullAndPkgDataAndIssueTypeDataNotNull { char *pkgData = strdup("ValidPkgData"); char *issueTypeData = strdup("ValidIssueTypeData"); - append_item(pkgData, issueTypeData); + append_item(pkgData, issueTypeData, NULL); ASSERT_NE(cacheDataNode, nullptr); ASSERT_STREQ(cacheDataNode->mdata, pkgData); @@ -1485,14 +1536,14 @@ TEST_F(AppendItemTest, HandlesRrdCachecnodeNullAndPkgDataAndIssueTypeDataNotNull TEST_F(AppendItemTest, HandlesRrdCachecnodeNotNullAndPkgDataNullAndIssueTypeDataNull) { - cacheDataNode = (cacheData *)malloc(sizeof(cacheData)); + cacheDataNode = (cacheData *)calloc(1, sizeof(cacheData)); cacheDataNode->mdata = strdup("ExistingPkgData"); cacheDataNode->issueString = strdup("ExistingIssueTypeData"); cacheDataNode->next = NULL; cacheDataNode->prev = NULL; char *pkgData = NULL; char *issueTypeData = NULL; - append_item(pkgData, issueTypeData); + append_item(pkgData, issueTypeData, NULL); ASSERT_NE(cacheDataNode, nullptr); ASSERT_EQ(cacheDataNode->mdata, nullptr); @@ -1501,14 +1552,14 @@ TEST_F(AppendItemTest, HandlesRrdCachecnodeNotNullAndPkgDataNullAndIssueTypeData TEST_F(AppendItemTest, HandlesRrdCachecnodeNotNullAndPkgDataNullAndIssueTypeDataNotNull) { - cacheDataNode = (cacheData *)malloc(sizeof(cacheData)); + cacheDataNode = (cacheData *)calloc(1, sizeof(cacheData)); cacheDataNode->mdata = strdup("ExistingPkgData"); cacheDataNode->issueString = strdup("ExistingIssueTypeData"); cacheDataNode->next = NULL; cacheDataNode->prev = NULL; char *pkgData = NULL; char *issueTypeData = strdup("ValidIssueTypeData"); - append_item(pkgData, issueTypeData); + append_item(pkgData, issueTypeData, NULL); ASSERT_NE(cacheDataNode, nullptr); ASSERT_EQ(cacheDataNode->mdata, nullptr); @@ -1517,14 +1568,14 @@ TEST_F(AppendItemTest, HandlesRrdCachecnodeNotNullAndPkgDataNullAndIssueTypeData TEST_F(AppendItemTest, HandlesRrdCachecnodeNotNullAndPkgDataNotNullAndIssueTypeDataNull) { - cacheDataNode = (cacheData *)malloc(sizeof(cacheData)); + cacheDataNode = (cacheData *)calloc(1, sizeof(cacheData)); cacheDataNode->mdata = strdup("ExistingPkgData"); cacheDataNode->issueString = strdup("ExistingIssueTypeData"); cacheDataNode->next = NULL; cacheDataNode->prev = NULL; char *pkgData = strdup("ValidPkgData"); char *issueTypeData = NULL; - append_item(pkgData, issueTypeData); + append_item(pkgData, issueTypeData, NULL); ASSERT_NE(cacheDataNode, nullptr); ASSERT_STREQ(cacheDataNode->mdata, pkgData); @@ -1533,14 +1584,14 @@ TEST_F(AppendItemTest, HandlesRrdCachecnodeNotNullAndPkgDataNotNullAndIssueTypeD TEST_F(AppendItemTest, HandlesRrdCachecnodeNotNullAndPkgDataAndIssueTypeDataNotNull) { - cacheDataNode = (cacheData *)malloc(sizeof(cacheData)); + cacheDataNode = (cacheData *)calloc(1, sizeof(cacheData)); cacheDataNode->mdata = strdup("ExistingPkgData"); cacheDataNode->issueString = strdup("ExistingIssueTypeData"); cacheDataNode->next = NULL; cacheDataNode->prev = NULL; char *pkgData = strdup("ValidPkgData"); char *issueTypeData = strdup("ValidIssueTypeData"); - append_item(pkgData, issueTypeData); + append_item(pkgData, issueTypeData, NULL); ASSERT_NE(cacheDataNode, nullptr); ASSERT_STREQ(cacheDataNode->mdata, pkgData); @@ -1550,7 +1601,7 @@ TEST_F(AppendItemTest, HandlesRrdCachecnodeNotNullAndPkgDataAndIssueTypeDataNotN /* --------------- Test freecacheDataCacheNode() from rrdRunCmdThread --------------- */ TEST(FreecacheDataCacheNodeTest, HandlesNodeNotNullAndMdataNotNullAndIssueStringNotNull) { - cacheData *node = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node = (cacheData *)calloc(1, sizeof(cacheData)); node->mdata = strdup("ValidMdata"); node->issueString = strdup("ValidIssueString"); freecacheDataCacheNode(&node); @@ -1568,7 +1619,7 @@ TEST(FreecacheDataCacheNodeTest, HandlesNodeNull) TEST(FreecacheDataCacheNodeTest, HandlesNodeNotNullAndMdataNullAndIssueStringNotNull) { - cacheData *node = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node = (cacheData *)calloc(1, sizeof(cacheData)); node->mdata = NULL; node->issueString = strdup("ValidIssueString"); freecacheDataCacheNode(&node); @@ -1698,11 +1749,11 @@ class FindPresentInCacheTest : public ::testing::Test TEST_F(FindPresentInCacheTest, HandlesPkgDataFoundInSecondElement) { - cacheData *firstNode = (cacheData *)malloc(sizeof(cacheData)); + cacheData *firstNode = (cacheData *)calloc(1, sizeof(cacheData)); firstNode->mdata = strdup("FirstPkgData"); firstNode->issueString = strdup("FirstIssueString"); firstNode->next = NULL; - cacheData *secondNode = (cacheData *)malloc(sizeof(cacheData)); + cacheData *secondNode = (cacheData *)calloc(1, sizeof(cacheData)); secondNode->mdata = strdup("SecondPkgData"); secondNode->issueString = strdup("SecondIssueString"); secondNode->next = NULL; @@ -1715,7 +1766,7 @@ TEST_F(FindPresentInCacheTest, HandlesPkgDataFoundInSecondElement) TEST_F(FindPresentInCacheTest, HandlesPkgDataFoundInFirstElement) { - cacheData *node = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node = (cacheData *)calloc(1, sizeof(cacheData)); node->mdata = strdup("PkgData"); node->issueString = strdup("IssueString"); node->next = NULL; @@ -1733,7 +1784,7 @@ TEST_F(FindPresentInCacheTest, HandlesRrdCachecnodeNull) TEST_F(FindPresentInCacheTest, HandlesPkgDataNotFoundInRrdCachecnode) { - cacheData *node = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node = (cacheData *)calloc(1, sizeof(cacheData)); node->mdata = strdup("PkgData"); node->issueString = strdup("IssueString"); node->next = NULL; @@ -1783,7 +1834,7 @@ TEST_F(RemoveItemTest, HandlesCacheNull) TEST_F(RemoveItemTest, HandlesCacheNotNullAndCacheEqualsRrdCachecnode) { - cacheData *node = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node = (cacheData *)calloc(1, sizeof(cacheData)); node->mdata = strdup("PkgData"); node->issueString = strdup("IssueString"); node->next = NULL; @@ -1795,15 +1846,17 @@ TEST_F(RemoveItemTest, HandlesCacheNotNullAndCacheEqualsRrdCachecnode) TEST_F(RemoveItemTest, HandlesCacheNotNullAndCacheNotEqualsRrdCachecnode) { - cacheData *node = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node = (cacheData *)calloc(1, sizeof(cacheData)); node->mdata = strdup("PkgData"); node->issueString = strdup("IssueString"); node->next = NULL; + node->prev = NULL; cacheDataNode = node; - cacheData *node_dummy = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node_dummy = (cacheData *)calloc(1, sizeof(cacheData)); node_dummy->mdata = strdup("PkgData"); node_dummy->issueString = strdup("IssueString"); node_dummy->next = NULL; + node_dummy->prev = NULL; remove_item(node_dummy); EXPECT_NE(cacheDataNode, nullptr); @@ -1863,15 +1916,19 @@ TEST(RemoveSpecialCharacterfromIssueTypeListTest, HandlesStringWithConsecutiveSp /* --------------- Test issueTypeSplitter() from rrdEventProcess --------------- */ TEST(IssueTypeSplitterTest, HandlesStringWithSpecialCharacters) { + /* issueTypeSplitter now performs pure token splitting only; special-character + * removal is done separately by the caller (processIssueTypeEvent) on the + * extracted base, so raw tokens including special chars are returned here. */ char str[] = "a@,b,&,cd,ef"; char **args = NULL; int count = issueTypeSplitter(str, ',', &args); - ASSERT_EQ(count, 4); - ASSERT_STREQ(args[0], "a"); + ASSERT_EQ(count, 5); + ASSERT_STREQ(args[0], "a@"); ASSERT_STREQ(args[1], "b"); - ASSERT_STREQ(args[2], "cd"); - ASSERT_STREQ(args[3], "ef"); + ASSERT_STREQ(args[2], "&"); + ASSERT_STREQ(args[3], "cd"); + ASSERT_STREQ(args[4], "ef"); for (int i = 0; i < count; i++) { @@ -1907,6 +1964,237 @@ TEST(IssueTypeSplitterTest, HandlesEmptyString) free(args); } +/* --------------- Test split_issue_type() from rrdJsonParser --------------- */ +TEST(SplitIssueTypeTest, NoUnderscoreReturnsFull) +{ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, UnderscoreSplitsBaseAndSuffix) +{ + /* Short suffix (total length including '_' is ≤ 9) is accepted and preserved */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_ab12345", + base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, "_ab12345"); +} + +TEST(SplitIssueTypeTest, MultipleUnderscoresSplitsAtFirst) +{ + /* "abc_def_ghi": suffix "_def_ghi" is 8 chars (≤ 9) → accepted and preserved. + * Only the first '_' is used as the split point; base never contains '_'. */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_def_ghi", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, "_def_ghi"); +} + +TEST(SplitIssueTypeTest, EmptyInputProducesEmptyOutputs) +{ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, NullInputDoesNotCrash) +{ + char base[64] = {0}; + char suffix[64] = {0}; + /* Should return without crashing and clear provided outputs to empty strings */ + split_issue_type(NULL, base, sizeof(base), suffix, sizeof(suffix)); + /* NULL input clears the output buffers when buffer pointers are provided */ + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, BaseTruncatedWhenTooSmall) +{ + /* "abc_suffix": suffix "_suffix" is 7 chars (≤ 9) → accepted. + * Base = "abc" (before '_'); with a 4-byte buffer this fits exactly. */ + char base[4] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_suffix", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, "_suffix"); +} + +TEST(SplitIssueTypeTest, SuffixTruncatedWhenTooSmall) +{ + /* "abc_12345678": suffix "_12345678" is 9 chars (≤ 9, accepted). Suffix buffer + * is only 5 bytes so suffix is truncated to "_123" + NUL. */ + char base[64] = {0}; + char suffix[5] = {0}; + split_issue_type("abc_12345678", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, "_123"); + EXPECT_EQ(suffix[sizeof(suffix) - 1], '\0'); + EXPECT_EQ(strlen(suffix), (size_t)(sizeof(suffix) - 1)); +} + +TEST(SplitIssueTypeTest, LeadingUnderscoreGivesEmptyBase) +{ + /* "_suffixonly": split at '_' gives empty base; suffix "_suffixonly" is 11 chars + * (> 9) so it is discarded */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("_suffixonly", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, NullBaseDoesNotCrash) +{ + char suffix[64] = {0}; + /* NULL base pointer: should return without crashing */ + split_issue_type("Device.DeviceTime_Search", NULL, 64, suffix, sizeof(suffix)); + /* suffix remains unchanged when base is NULL */ + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, NullSuffixDoesNotCrash) +{ + char base[64] = {0}; + /* NULL suffix pointer: should return without crashing */ + split_issue_type("Device.DeviceTime_Search", base, sizeof(base), NULL, 64); + /* base remains unchanged when suffix is NULL */ + EXPECT_STREQ(base, ""); +} + +TEST(SplitIssueTypeTest, ZeroBaseLenDoesNotCrash) +{ + char base[64] = {0}; + char suffix[64] = {0}; + /* base_len == 0: should return without writing anything */ + split_issue_type("abc_def", base, 0, suffix, sizeof(suffix)); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, ZeroSuffixLenDoesNotCrash) +{ + char base[64] = {0}; + char suffix[64] = {0}; + /* suffix_len == 0: should return without writing anything */ + split_issue_type("abc_def", base, sizeof(base), suffix, 0); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, ExactFitBase) +{ + /* "abc_suffix": suffix "_suffix" is 7 chars (≤ 9, accepted). + * Base = "abc" (before '_'); 4-byte buffer fits exactly. */ + char base[4] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_suffix", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_EQ(base[3], '\0'); + EXPECT_STREQ(suffix, "_suffix"); +} + +TEST(SplitIssueTypeTest, OnlyUnderscoreInput) +{ + /* "_": split at '_' gives empty base; suffix is "_" (1 char, ≤ 9 → accepted) */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("_", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, ""); + EXPECT_STREQ(suffix, "_"); +} + +TEST(SplitIssueTypeTest, NineCharSuffixIsAccepted) +{ + /* Suffix of exactly 9 chars (the upper boundary, inclusive) must be accepted */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_12345678", + base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, "_12345678"); +} + +TEST(SplitIssueTypeTest, LongSuffixIsDiscarded) +{ + /* Suffix longer than 9 chars is discarded regardless of its content */ + char base[64] = {0}; + char suffix[128] = {0}; + split_issue_type("Device.DeviceInfo_1234567890", + base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceInfo"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, SuffixExceedingMaxLengthDiscarded) +{ + /* "_Random-token" is 13 chars (> 9) → suffix discarded */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_Random-token", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, SuffixSeventeenCharsDiscarded) +{ + /* "_Search_something" is 17 chars (> 9) → suffix discarded */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_Search_something", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, SuffixTwentyCharsDiscarded) +{ + /* "_LogSearch_something" is 20 chars (> 9) → suffix discarded */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_LogSearch_something", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, ""); +} + +TEST(SplitIssueTypeTest, SuffixWithUnsafeCharsIsSanitized) +{ + /* "_ab;rm" is 6 chars (≤ 9, accepted) but ';' is unsafe and must be stripped. + * Expected sanitized suffix: "_abrm" */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_ab;rm", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, "_abrm"); +} + +TEST(SplitIssueTypeTest, SuffixWithOnlyUnsafeCharsBecomesUnderscore) +{ + /* "_!@#" is 4 chars (≤ 9, accepted length-wise) but all payload chars are unsafe. + * After sanitization only the leading '_' remains → suffix="_" */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("abc_!@#", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "abc"); + EXPECT_STREQ(suffix, "_"); +} + +TEST(SplitIssueTypeTest, SuffixHyphensPreserved) +{ + /* "_ab-cd" is 6 chars (≤ 9, accepted) and '-' is in the safe set → preserved */ + char base[64] = {0}; + char suffix[64] = {0}; + split_issue_type("Device.DeviceTime_ab-cd", base, sizeof(base), suffix, sizeof(suffix)); + EXPECT_STREQ(base, "Device.DeviceTime"); + EXPECT_STREQ(suffix, "_ab-cd"); +} + /* --------------- Test processIssueTypeInDynamicProfile() from rrdEventProcess --------------- */ class ProcessIssueTypeInDynamicProfileTest : public ::testing::Test { @@ -1989,11 +2277,93 @@ TEST(ProcessIssueTypeEvntTest, RBufIsNull){ } TEST(ProcessIssueTypeEvntTest, inDynamic_NoJson){ - data_buf rbuf; + data_buf rbuf = {}; rbuf.mdata = strdup("a"); rbuf.inDynamic = true; rbuf.jsonPath = nullptr; processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, IssueTypeWithSearchSuffix_inDynamic_NoJson){ + /* Issue type with a long suffix (> 9 chars): suffix is discarded; base "Device.DeviceTime" is used */ + data_buf rbuf = {}; + rbuf.mdata = strdup("Device.DeviceTime_Search-b6877385-9463-45fc-b19d-a24d77fd0790"); + rbuf.inDynamic = true; + rbuf.jsonPath = nullptr; + /* Should not crash; long suffix is discarded, base is processed normally */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, IssueTypeWithLogSearchSuffix_inDynamic_NoJson){ + /* Issue type with a long suffix (> 9 chars): suffix is discarded; base "Device.DeviceInfo" is used */ + data_buf rbuf = {}; + rbuf.mdata = strdup("Device.DeviceInfo_LogSearch-9abc1def-0000-1111-2222-3333aaaabbbb"); + rbuf.inDynamic = true; + rbuf.jsonPath = nullptr; + /* Should not crash; long suffix is discarded, base is processed normally */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, IssueTypeWithInvalidSuffixTreatedAsBase) +{ + /* "_Random-token" is 13 chars (> 9): suffix discarded; base = "Device.DeviceTime" */ + data_buf rbuf = {}; + rbuf.mdata = strdup("Device.DeviceTime_Random-token"); + rbuf.inDynamic = false; + rbuf.jsonPath = nullptr; + /* Should not crash; long suffix is discarded, base is processed normally */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, MultipleIssueTypesWithAndWithoutSuffix){ + /* Comma-separated list: one plain type, one with long suffix (> 9, discarded), + * one with another long suffix (> 9, discarded) */ + data_buf rbuf = {}; + rbuf.mdata = strdup("Device.DeviceTime,Device.DeviceInfo_Search-1234,Device.Net_BadSuffix"); + rbuf.inDynamic = true; + rbuf.jsonPath = nullptr; + /* Should not crash; all entries are processed without leaks */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, WhitespaceOnlyIssueTypeIsSkipped) +{ + /* When the IssueType value from RBUS is whitespace (e.g. a space), + * removeSpecialCharacterfromIssueTypeList() strips it to an empty string. + * processIssueTypeEvent() must detect the post-sanitization empty base + * and skip processing without crashing or invoking processIssueType. */ + data_buf rbuf = {}; + rbuf.mdata = strdup(" "); /* single space — all-special after split */ + rbuf.inDynamic = false; + rbuf.jsonPath = nullptr; + /* Must not crash and must not reach getIssueInfo with an empty mdata */ + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; +} + +TEST(ProcessIssueTypeEvntTest, EmptyStringIssueTypeIsSkipped) +{ + /* An empty-string mdata must be handled gracefully — issueTypeSplitter + * returns 1 token that is an empty string, which split_issue_type then + * maps to an empty base, causing the entry to be skipped. */ + data_buf rbuf = {}; + rbuf.mdata = strdup(""); + rbuf.inDynamic = false; + rbuf.jsonPath = nullptr; + processIssueTypeEvent(&rbuf); + free(rbuf.mdata); + rbuf.mdata = NULL; } /* ======================== rrdExecuteScript ==============*/ @@ -2117,12 +2487,22 @@ TEST(RRDDataBuffInitTest, InitializeDataBuff) EXPECT_EQ(sbuf.dsEvent, deepSleepEvent); } +TEST(RRDDataBuffInitTest, SuffixInitializedToNull) +{ + /* Verify that the newly added suffix field is initialised to NULL */ + data_buf sbuf; + sbuf.suffix = reinterpret_cast(0xDEADBEEF); /* pre-fill with garbage */ + RRD_data_buff_init(&sbuf, EVENT_MSG, RRD_DEEPSLEEP_INVALID_DEFAULT); + EXPECT_EQ(sbuf.suffix, nullptr); +} + /* --------------- Test RRD_data_buff_deAlloc() from rrdIarm --------------- */ TEST(RRDDataBuffDeAllocTest, DeallocateDataBuff) { data_buf *sbuf = (data_buf *)malloc(sizeof(data_buf)); sbuf->mdata = (char *)malloc(10 * sizeof(char)); sbuf->jsonPath = (char *)malloc(10 * sizeof(char)); + sbuf->suffix = nullptr; ASSERT_NO_FATAL_FAILURE(RRD_data_buff_deAlloc(sbuf)); } @@ -2134,6 +2514,28 @@ TEST(RRDDataBuffDeAllocTest, NullPointer) ASSERT_NO_FATAL_FAILURE(RRD_data_buff_deAlloc(sbuf)); } +TEST(RRDDataBuffDeAllocTest, DeallocateWithSuffixSet) +{ + /* Verify that suffix is freed without crash when it is non-NULL */ + data_buf *sbuf = (data_buf *)malloc(sizeof(data_buf)); + sbuf->mdata = strdup("IssueType"); + sbuf->jsonPath = nullptr; + sbuf->suffix = strdup("_Search-b6877385-9463-45fc-b19d-a24d77fd0790"); + + ASSERT_NO_FATAL_FAILURE(RRD_data_buff_deAlloc(sbuf)); +} + +TEST(RRDDataBuffDeAllocTest, DeallocateWithAllFieldsNull) +{ + /* All pointer fields NULL: should not crash */ + data_buf *sbuf = (data_buf *)malloc(sizeof(data_buf)); + sbuf->mdata = nullptr; + sbuf->jsonPath = nullptr; + sbuf->suffix = nullptr; + + ASSERT_NO_FATAL_FAILURE(RRD_data_buff_deAlloc(sbuf)); +} + /* --------------- Test RRD_unsubscribe() from rrdIarm --------------- */ class RRDUnsubscribeTest : public ::testing::Test @@ -2526,7 +2928,7 @@ TEST_F(RDMMgrEventHandlerTest, TestFoundInCacheDownloadNotComplete) { const char *owner = IARM_BUS_RDMMGR_NAME; IARM_EventId_t eventId = IARM_BUS_RDMMGR_EVENT_APP_INSTALLATION_STATUS; - cacheData *node = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node = (cacheData *)calloc(1, sizeof(cacheData)); node->mdata = strdup("PkgData"); node->issueString = strdup("IssueString"); node->next = NULL; @@ -2543,7 +2945,7 @@ TEST_F(RDMMgrEventHandlerTest, TestFoundInCacheDownloadIsCompleteAndDEEPSLEEPIss { const char *owner = IARM_BUS_RDMMGR_NAME; IARM_EventId_t eventId = IARM_BUS_RDMMGR_EVENT_APP_INSTALLATION_STATUS; - cacheData *node = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node = (cacheData *)calloc(1, sizeof(cacheData)); node->mdata = strdup("PkgData"); node->issueString = strdup("DEEPSLEEP"); node->next = NULL; @@ -2560,7 +2962,7 @@ TEST_F(RDMMgrEventHandlerTest, TestFoundInCacheDownloadIsCompleteAndNotDEEPSLEEP { const char *owner = IARM_BUS_RDMMGR_NAME; IARM_EventId_t eventId = IARM_BUS_RDMMGR_EVENT_APP_INSTALLATION_STATUS; - cacheData *node = (cacheData *)malloc(sizeof(cacheData)); + cacheData *node = (cacheData *)calloc(1, sizeof(cacheData)); node->mdata = strdup("PkgData"); node->issueString = strdup("NotDeepSleepIssue"); node->next = NULL; @@ -3684,6 +4086,7 @@ TEST_F(RRDEventThreadFuncTest, MessageReceiveSuccessEventMsgType) { rbuf.mdata = strdup("Test"); rbuf.inDynamic = true; rbuf.jsonPath = nullptr; + rbuf.suffix = strdup("_ab12345"); msgRRDHdr msgHdr; msgHdr.mbody = malloc(sizeof(data_buf)); ASSERT_NE(msgHdr.mbody, nullptr); @@ -4444,12 +4847,22 @@ TEST_F(RRDUploadOrchestrationTest, SpecialCharactersInIssueType) { char sanitized[64]; int result = rrd_logproc_convert_issue_type("test-issue.sub@special!", sanitized, sizeof(sanitized)); EXPECT_EQ(result, 0); - // Should only contain alphanumeric and underscore + // Should only contain alphanumeric, underscore, and hyphen for (const char *p = sanitized; *p; ++p) { - EXPECT_TRUE(isalnum(*p) || *p == '_'); + EXPECT_TRUE(isalnum(*p) || *p == '_' || *p == '-'); } } +// Suffix with hyphens: hyphens must be preserved so portal can parse filename +TEST_F(RRDUploadOrchestrationTest, IssueTypeWithSuffixHyphensPreserved) { + char sanitized[128]; + // Simulates issue type after normalizeIssueName: dots→underscore, hyphens kept + int result = rrd_logproc_convert_issue_type("Device_DeviceIP_Search-67768-67", sanitized, sizeof(sanitized)); + EXPECT_EQ(result, 0); + // Hyphens in suffix must survive so the archive filename delimiter structure is intact + EXPECT_STREQ(sanitized, "DEVICE_DEVICEIP_SEARCH-67768-67"); +} + // Performance test: Large directory TEST_F(RRDUploadOrchestrationTest, LargeDirectoryHandling) { // Create multiple log files @@ -5819,10 +6232,3 @@ TEST_F(RRDProfileHandlerTest, SetHandler_MaxLengthString) - - - - - - -