diff --git a/src/rrdCommon.h b/src/rrdCommon.h index b91ff6f7b..8f5544b77 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*/ diff --git a/src/rrdEventProcess.c b/src/rrdEventProcess.c index 5164e7832..91bb6cdeb 100644 --- a/src/rrdEventProcess.c +++ b/src/rrdEventProcess.c @@ -29,7 +29,7 @@ static void processIssueTypeInDynamicProfile(data_buf *rbuf, issueNodeData *pIss static void processIssueTypeInStaticProfile(data_buf *rbuf, issueNodeData *pIssueNode); static void processIssueTypeInInstalledPackage(data_buf *rbuf, issueNodeData *pIssueNode); static void removeSpecialCharacterfromIssueTypeList(char *str); -static int issueTypeSplitter(char *input_str, const char delimeter, char ***args); +static int issueTypeSplitter(char *input_str, char *outsuffix, const char delimeter, char ***args); static void freeParsedJson(cJSON *jsonParsed); /* @@ -62,13 +62,18 @@ void processIssueTypeEvent(data_buf *rbuf) char **cmdMap = NULL; int index = 0, count = 0, dataMsgLen = 0; data_buf *cmdBuff = NULL; + char suffix[128] = {0}; RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: ...Entering.. \n", __FUNCTION__, __LINE__); if (NULL != rbuf) { RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: IssueType List [%s]... \n", __FUNCTION__, __LINE__, rbuf->mdata); - count = issueTypeSplitter(rbuf->mdata, ',', &cmdMap); - + count = issueTypeSplitter(rbuf->mdata, suffix, ',', &cmdMap); + if (rbuf->suffix) { + free(rbuf->suffix); + } + rbuf->suffix = (suffix[0] != '\0') ? strdup(suffix) : NULL; + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Assigned rbuf->suffix='%s' after issueTypeSplitter\n", __FUNCTION__, __LINE__, rbuf->suffix ? rbuf->suffix : "(null)"); if (count > 0) { RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: IssueType Count [%d]... \n", __FUNCTION__, __LINE__, count); @@ -88,6 +93,13 @@ void processIssueTypeEvent(data_buf *rbuf) } cmdBuff->appendMode = rbuf->appendMode; cmdBuff->mdata = (char *)calloc(1, dataMsgLen); + + /* Persist suffix */ + if (rbuf->suffix) { + cmdBuff->suffix = strdup(rbuf->suffix); + } else { + cmdBuff->suffix = NULL; + } if (cmdBuff->mdata) { strncpy((char *)cmdBuff->mdata, cmdMap[index], dataMsgLen); @@ -97,11 +109,16 @@ void processIssueTypeEvent(data_buf *rbuf) { RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: Memory Allocation Failed... \n", __FUNCTION__, __LINE__); } - if(cmdBuff) - { + if(cmdBuff) + { + if (cmdBuff->suffix) + { + free(cmdBuff->suffix); + cmdBuff->suffix = NULL; + } free(cmdBuff); - cmdBuff = NULL; - } + cmdBuff = NULL; + } } else { @@ -140,7 +157,8 @@ static void processIssueType(data_buf *rbuf) issueData *staticprofiledata = NULL; RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: ...Entering.. \n", __FUNCTION__, __LINE__); - if (rbuf->mdata != NULL) // issue data exits + + if (rbuf->mdata != NULL) // issue data exists { pIssueNode = (issueNodeData *)malloc(sizeof(issueNodeData)); if(pIssueNode) @@ -653,15 +671,33 @@ static void removeSpecialCharacterfromIssueTypeList(char *str) * @function issueTypeSplitter * @brief Splits a given string into tokens based on a specified delimiter, and removes any * special characters from the string before splitting. - * @param char *input_str - The input string to be split. - * @param const char delimiter - The character used to split the string. + * The first underscore in input_str is treated as a suffix separator: the base + * (part before the underscore) is written back into input_str, and the suffix + * (underscore + remainder) is written into outsuffix. + * @param char *input_str - The input string to be split (modified in place: base written back). + * @param char *outsuffix - Buffer (at least BUF_LEN_128 bytes) to receive the suffix portion, + * or NULL if the caller does not need the suffix. + * @param const char delimeter - The character used to split the string. * @param char ***args - Pointer to an array of strings where the tokens will be stored. * @return int - The number of tokens found in the string. */ -static int issueTypeSplitter(char *input_str, const char delimeter, char ***args) +static int issueTypeSplitter(char *input_str, char *outsuffix, const char delimeter, char ***args) { int cnt = 1, i = 0; char *str = input_str; + char base[ BUF_LEN_128] = {0}; + char suffix[ BUF_LEN_128] = {0}; + split_issue_type(str, base, sizeof(base), suffix, sizeof(suffix)); + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: issueTypeSplitter (pre-clean): input='%s', base='%s', suffix='%s'\n", __FUNCTION__, __LINE__, str, base, suffix); + + /* Copy only the actual base string back into str. base is always a prefix + * of the original str so it fits within the original allocation. */ + memmove(str, base, strlen(base) + 1); + + if (outsuffix != NULL) + { + snprintf(outsuffix, BUF_LEN_128, "%s", suffix); + } removeSpecialCharacterfromIssueTypeList(str); while (*str == delimeter) @@ -698,4 +734,3 @@ static int issueTypeSplitter(char *input_str, const char delimeter, char ***args return cnt; } - diff --git a/src/rrdInterface.c b/src/rrdInterface.c index b69dd8936..30291cd84 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); } } diff --git a/src/rrdJsonParser.c b/src/rrdJsonParser.c index e06d93ac2..e483f3b0b 100644 --- a/src/rrdJsonParser.c +++ b/src/rrdJsonParser.c @@ -46,6 +46,49 @@ void removeSpecialChar(char *str) } } +/* + * @function split_issue_type + * @brief Utility to split base and suffix from issue type string. + * Example: Input: Device.DeviceTime_Search-b6877385-9463-45fc-b19d-a24d77fd0790 + * Output: base = Device.DeviceTime, suffix = _Search-b6877385-9463-45fc-b19d-a24d77fd0790 + * @param const char *input - The input string to split. + * @param char *base - Buffer to store the base part (before the first underscore). + * @param size_t base_len - Size of the base buffer. + * @param char *suffix - Buffer to store the suffix part (from the first underscore onwards). + * @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 (!input || !base || !suffix) return; + + if (base_len == 0 || suffix_len == 0) { + if (base && base_len > 0) { + base[0] = '\0'; + } + if (suffix && suffix_len > 0) { + suffix[0] = '\0'; + } + return; + } + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: split_issue_type called with input='%s'\n", __FUNCTION__, __LINE__, input); + const char *underscore = strchr(input, '_'); + if (underscore) { + size_t b_len = underscore - input; + if (b_len >= base_len) b_len = base_len - 1; + strncpy(base, input, b_len); + base[b_len] = '\0'; + strncpy(suffix, underscore, suffix_len - 1); + suffix[suffix_len - 1] = '\0'; + } else { + strncpy(base, input, base_len - 1); + base[base_len - 1] = '\0'; + suffix[0] = '\0'; + } + RDK_LOG(RDK_LOG_DEBUG, LOG_REMDEBUG, "[%s:%d]: split_issue_type result: base='%s', suffix='%s'\n", __FUNCTION__, __LINE__, base, suffix); +} + + /* * @function getParamcount * @brief Calculates the total number of nodes (elements) in the input string, excluding delimiters. @@ -515,7 +558,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 +582,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 +627,14 @@ 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}; + if (buff->suffix && buff->suffix[0] != '\0') { + snprintf(tarName, sizeof(tarName), "%s%s", buff->mdata, buff->suffix); + } else { + snprintf(tarName, sizeof(tarName), "%s", buff->mdata); + } + 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 +646,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/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..8e34452ed 100644 --- a/src/unittest/rrdUnitTestRunner.cpp +++ b/src/unittest/rrdUnitTestRunner.cpp @@ -1799,11 +1799,13 @@ TEST_F(RemoveItemTest, HandlesCacheNotNullAndCacheNotEqualsRrdCachecnode) node->mdata = strdup("PkgData"); node->issueString = strdup("IssueString"); node->next = NULL; + node->prev = NULL; cacheDataNode = node; cacheData *node_dummy = (cacheData *)malloc(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); @@ -1865,7 +1867,7 @@ TEST(IssueTypeSplitterTest, HandlesStringWithSpecialCharacters) { char str[] = "a@,b,&,cd,ef"; char **args = NULL; - int count = issueTypeSplitter(str, ',', &args); + int count = issueTypeSplitter(str, NULL, ',', &args); ASSERT_EQ(count, 4); ASSERT_STREQ(args[0], "a"); @@ -1884,7 +1886,7 @@ TEST(IssueTypeSplitterTest, HandlesStringWithNoSpecialCharacters) { char str[] = "abcd"; char **args = NULL; - int count = issueTypeSplitter(str, ',', &args); + int count = issueTypeSplitter(str, NULL, ',', &args); ASSERT_EQ(count, 1); ASSERT_STREQ(args[0], "abcd"); @@ -1900,13 +1902,48 @@ TEST(IssueTypeSplitterTest, HandlesEmptyString) { char str[] = ""; char **args = NULL; - int count = issueTypeSplitter(str, ',', &args); + int count = issueTypeSplitter(str, NULL, ',', &args); ASSERT_EQ(count, 1); free(args); } +TEST(IssueTypeSplitterTest, ExtractsSuffixFromIssueType) +{ + char str[] = "Device.DeviceTime_Search"; + char outsuffix[128] = {0}; + char **args = NULL; + int count = issueTypeSplitter(str, outsuffix, ',', &args); + + ASSERT_EQ(count, 1); + ASSERT_STREQ(args[0], "Device.DeviceTime"); + ASSERT_STREQ(outsuffix, "_Search"); + + for (int i = 0; i < count; i++) + { + free(args[i]); + } + free(args); +} + +TEST(IssueTypeSplitterTest, NoSuffixWhenNoUnderscore) +{ + char str[] = "Device.Network"; + char outsuffix[128] = {0}; + char **args = NULL; + int count = issueTypeSplitter(str, outsuffix, ',', &args); + + ASSERT_EQ(count, 1); + ASSERT_STREQ(outsuffix, ""); + + for (int i = 0; i < count; i++) + { + free(args[i]); + } + free(args); +} + /* --------------- Test processIssueTypeInDynamicProfile() from rrdEventProcess --------------- */ class ProcessIssueTypeInDynamicProfileTest : public ::testing::Test { @@ -1989,11 +2026,13 @@ 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; } /* ======================== rrdExecuteScript ==============*/ @@ -3684,6 +3723,7 @@ TEST_F(RRDEventThreadFuncTest, MessageReceiveSuccessEventMsgType) { rbuf.mdata = strdup("Test"); rbuf.inDynamic = true; rbuf.jsonPath = nullptr; + rbuf.suffix = nullptr; msgRRDHdr msgHdr; msgHdr.mbody = malloc(sizeof(data_buf)); ASSERT_NE(msgHdr.mbody, nullptr);