diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b8baff5..ad2b03cf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,74 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [2.0.3v6](https://github.com/rdkcentral/dcm-agent/compare/2.1.3...2.0.3v6) + +- RDKEMW-19237: Cleanup of stale archives and log backups to the uploadSTBLogs [`#134`](https://github.com/rdkcentral/dcm-agent/pull/134) +- [8.5] RDKEMW-19218 : Add missing Logs in Log Upload C module Logs [`#133`](https://github.com/rdkcentral/dcm-agent/pull/133) +- RDKEMW-18351 : [8.5.3]Log upload success logs not observed after scheduled reboot [`#129`](https://github.com/rdkcentral/dcm-agent/pull/129) +- Revert "Revert "RDKEMW-17725 : [support/8.5] [RDKEMW][ALPACA IT] Device not G…" [`#125`](https://github.com/rdkcentral/dcm-agent/pull/125) +- Revert "RDKEMW-17725 : [support/8.5] [RDKEMW][ALPACA IT] Device not Going to …" [`#124`](https://github.com/rdkcentral/dcm-agent/pull/124) +- RDKEMW-17725 : [support/8.5] [RDKEMW][ALPACA IT] Device not Going to Deepsleep. [`#123`](https://github.com/rdkcentral/dcm-agent/pull/123) +- Add log [`#121`](https://github.com/rdkcentral/dcm-agent/pull/121) +- [support/8.5] Log-backup generated is named with local timestamp instead of UTC timestamp [`#117`](https://github.com/rdkcentral/dcm-agent/pull/117) +- dcmagent 2.0.3v1 hotfix release [`75c80d1`](https://github.com/rdkcentral/dcm-agent/commit/75c80d1701dd932e8e885aa3f418ae1633d6b1eb) +- DCM agent 2.0.3v2 hotfix release [`877e615`](https://github.com/rdkcentral/dcm-agent/commit/877e615fc3d17a8eb8f1de0cdbdf4d9e053a5e32) +- DCM agent 8.5 2.0.3v5 hotfix release [`11d62dd`](https://github.com/rdkcentral/dcm-agent/commit/11d62dd0644a697a8e7a9f4985b2c0d417d982c3) + +#### [2.1.3](https://github.com/rdkcentral/dcm-agent/compare/2.1.2...2.1.3) + +> 18 May 2026 + +- RDKEMW-18510: [develop]Log upload success logs not observed after scheduled reboot [`#127`](https://github.com/rdkcentral/dcm-agent/pull/127) +- DCM Agent 2.1.3 release changelog updates [`fe80498`](https://github.com/rdkcentral/dcm-agent/commit/fe804982965fb1db55917fb9d1f91573b65e6e0c) +- Merge tag '2.1.2' into develop [`7461693`](https://github.com/rdkcentral/dcm-agent/commit/7461693af0d9c1c8fbd3fb4fd374ef8858041608) + +#### [2.1.2](https://github.com/rdkcentral/dcm-agent/compare/2.1.1...2.1.2) + +> 29 April 2026 + +- RDKEMW-17638 : [RDKEMW][ALPACA IT] Device not Going to Deepsleep. [`#122`](https://github.com/rdkcentral/dcm-agent/pull/122) +- RDKEMW-17582: [develop]UploadLogsOnUnscheduledReboot.Disable RFC state not logged / not honored in 8.5 builds after Scheduled Reboot [`#119`](https://github.com/rdkcentral/dcm-agent/pull/119) +- [RDKEMW-17616] Log-backup generated is named with local timestamp instead of UTC timestamp [`#114`](https://github.com/rdkcentral/dcm-agent/pull/114) +- RDKEMW-14842 [Logupload] Sha value need to be print in dcmscript.log for all types of logupload [`#111`](https://github.com/rdkcentral/dcm-agent/pull/111) +- DCM Agent Documentaion updated for the module [`#110`](https://github.com/rdkcentral/dcm-agent/pull/110) +- RDKEMW-17026 : Remove OEM/SOC references from the module [`#113`](https://github.com/rdkcentral/dcm-agent/pull/113) +- DCM Agent 2.1.2 release changelog updates [`00ac749`](https://github.com/rdkcentral/dcm-agent/commit/00ac749be80b9d7cbfc47e60e0dcfc11005e9886) +- Merge tag '2.1.1' into develop [`be1a984`](https://github.com/rdkcentral/dcm-agent/commit/be1a9843bd5631ee54d0c1d750e827b50e9ba848) + +#### [2.1.1](https://github.com/rdkcentral/dcm-agent/compare/2.1.0...2.1.1) + +> 26 March 2026 + +- RDK-61010,RDKEMW-13361: Replace Memcapture Report Upload Script with UploadSTB Binary [`#80`](https://github.com/rdkcentral/dcm-agent/pull/80) +- DCM Agent 2.1.1 release changelog updates [`68443e9`](https://github.com/rdkcentral/dcm-agent/commit/68443e98816b1bef98089fa6640488bd27617568) + +#### [2.1.0](https://github.com/rdkcentral/dcm-agent/compare/2.0.4...2.1.0) + +> 25 March 2026 + +- RDK-61009 : [RDKE] Port Log Backup Scripts to Source code [`#100`](https://github.com/rdkcentral/dcm-agent/pull/100) +- Add tools and skills for agentic development [`#102`](https://github.com/rdkcentral/dcm-agent/pull/102) +- Changelog updates for 2.1.0 release [`2750242`](https://github.com/rdkcentral/dcm-agent/commit/2750242f81cdd6400aa331328f46a1a80abd8f16) +- Merge tag '2.0.4' into develop [`fc29d06`](https://github.com/rdkcentral/dcm-agent/commit/fc29d06b82c73b374527cbdb8bef93eab5ccfbdb) + +#### [2.0.4](https://github.com/rdkcentral/dcm-agent/compare/2.0.3...2.0.4) + +> 18 March 2026 + +- RDK-60634 [dcm-agent] RDK Coverity Defect Resolution for Device Management [`#81`](https://github.com/rdkcentral/dcm-agent/pull/81) +- RDK-61009 : [RDKE] Port Log Backup Scripts to Source code [`#95`](https://github.com/rdkcentral/dcm-agent/pull/95) +- RDK-60497 : Port USB Log Upload Scripts to Source code [`#91`](https://github.com/rdkcentral/dcm-agent/pull/91) +- RDK-60497 : Port USB Log Upload Scripts to Source code [`#79`](https://github.com/rdkcentral/dcm-agent/pull/79) +- tr69hostif 2.0.4 release changelog updates [`10f09d2`](https://github.com/rdkcentral/dcm-agent/commit/10f09d27d7200e5f8474303bbc7689f6bf8eeefa) +- Merge tag '2.0.3' into develop [`86c4755`](https://github.com/rdkcentral/dcm-agent/commit/86c47550324d871f804446459dbb0a30814d5a2a) + #### [2.0.3](https://github.com/rdkcentral/dcm-agent/compare/2.0.2...2.0.3) +> 11 February 2026 + - Update context_manager.c [`#73`](https://github.com/rdkcentral/dcm-agent/pull/73) +- DCM Agent 2.0.3 release changelog updates [`45018b7`](https://github.com/rdkcentral/dcm-agent/commit/45018b7808de12690a91b45447b372ebd4af0b11) #### [2.0.2](https://github.com/rdkcentral/dcm-agent/compare/2.0.1...2.0.2) diff --git a/uploadstblogs/include/archive_manager.h b/uploadstblogs/include/archive_manager.h index 18af21d9d..2c275fe0f 100755 --- a/uploadstblogs/include/archive_manager.h +++ b/uploadstblogs/include/archive_manager.h @@ -111,7 +111,7 @@ int create_archive(RuntimeContext* ctx, SessionState* session, const char* sourc * * Creates tar.gz archive containing DRI logs from DRI_LOG_PATH */ -int create_dri_archive(RuntimeContext* ctx, const char* archive_path); +int create_dri_archive(RuntimeContext* ctx, SessionState* session, const char* archive_path); /** * @brief Generate archive filename with MAC and timestamp diff --git a/uploadstblogs/src/archive_manager.c b/uploadstblogs/src/archive_manager.c index b6357c8e3..d39e7b872 100755 --- a/uploadstblogs/src/archive_manager.c +++ b/uploadstblogs/src/archive_manager.c @@ -415,18 +415,20 @@ bool generate_archive_name(char* buffer, size_t buffer_size, } time_t now = time(NULL); - struct tm* tm_info = localtime(&now); - - if (!tm_info) { - RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, - "[%s:%d] Failed to get local time\n", __FUNCTION__, __LINE__); + + struct tm tm_utc; + if (gmtime_r(&now, &tm_utc) == NULL) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Failed to get UTC time\n", __FUNCTION__, __LINE__); return false; } char timestamp[32]; - // Format: MM-DD-YY-HH-MMAM/PM (matches script: date "+%m-%d-%y-%I-%M%p") - strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p", tm_info); - + // Format UTC timestamp as MM-DD-YY-HH-MMAM/PM. + if (strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p", &tm_utc) == 0) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, + "[%s:%d] Failed to format timestamp\n", __FUNCTION__, __LINE__); + return false; + } // Remove colons from MAC address for filename (A8:4A:63 -> A84A63) char mac_clean[32]; const char* src = mac_address; @@ -447,8 +449,8 @@ bool generate_archive_name(char* buffer, size_t buffer_size, __FUNCTION__, __LINE__, buffer, mac_address, prefix); return true; -} +} /** * @brief Calculate TAR checksum */ @@ -765,9 +767,9 @@ static int create_archive_with_options(RuntimeContext* ctx, SessionState* sessio * @param archive_path Output archive file path (directory portion used) * @return 0 on success, -1 on failure */ -int create_dri_archive(RuntimeContext* ctx, const char* archive_path) +int create_dri_archive(RuntimeContext* ctx, SessionState* session, const char* archive_path) { - if (!ctx || !archive_path) { + if (!ctx || !session || !archive_path) { RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Invalid parameters\n", __FUNCTION__, __LINE__); return -1; @@ -786,20 +788,9 @@ int create_dri_archive(RuntimeContext* ctx, const char* archive_path) return -1; } - // Extract output directory from archive_path - char output_dir[MAX_PATH_LENGTH]; - const char* last_slash = strrchr(archive_path, '/'); - if (last_slash) { - size_t dir_len = last_slash - archive_path; - snprintf(output_dir, sizeof(output_dir), "%.*s", (int)dir_len, archive_path); - } else { - strcpy(output_dir, "/tmp"); - } - RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, "[%s:%d] Creating DRI archive from %s to %s\n", - __FUNCTION__, __LINE__, ctx->dri_log_path, output_dir); + __FUNCTION__, __LINE__, ctx->dri_log_path, ctx->dri_log_path); - // Use the common archive creation with DRI_Logs prefix - return create_archive_with_options(ctx, NULL, ctx->dri_log_path, output_dir, "DRI_Logs"); + return create_archive_with_options(ctx, session, ctx->dri_log_path, ctx->dri_log_path, "DRI_Logs"); } diff --git a/uploadstblogs/src/cleanup_handler.c b/uploadstblogs/src/cleanup_handler.c index b06886215..770310db1 100755 --- a/uploadstblogs/src/cleanup_handler.c +++ b/uploadstblogs/src/cleanup_handler.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "cleanup_handler.h" #include "context_manager.h" #include "event_manager.h" @@ -224,36 +225,71 @@ int cleanup_old_archives(const char *log_path) return -1; } + int dfd = dirfd(dir); + if (dfd < 0) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] dirfd() failed for: %s\n", __FUNCTION__, __LINE__, log_path); + closedir(dir); + return -1; + } + int removed_count = 0; struct dirent *entry; char fullpath[512]; while ((entry = readdir(dir)) != NULL) { - // Check if file ends with .tgz - size_t len = strlen(entry->d_name); - if (len < 5 || strcmp(entry->d_name + len - 4, ".tgz") != 0) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } - snprintf(fullpath, sizeof(fullpath), "%s/%s", log_path, entry->d_name); - - RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, - "[%s:%d] Removing old archive: %s\n", - __FUNCTION__, __LINE__, fullpath); - - // Use unlink to remove file (more explicit than remove) - if (unlink(fullpath) == 0) { - removed_count++; - } else { - RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB, - "[%s:%d] Failed to remove: %s\n", - __FUNCTION__, __LINE__, fullpath); + struct stat st; + /* fstatat with AT_SYMLINK_NOFOLLOW on the open dir FD: check and subsequent + * unlinkat both refer to the same dir entry, eliminating the TOCTOU race. */ + if (fstatat(dfd, entry->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) { + continue; + } + + if (S_ISDIR(st.st_mode)) { + /* Recurse into subdirectories (matches shell: find $LOG_PATH -name "*.tgz") */ + snprintf(fullpath, sizeof(fullpath), "%s/%s", log_path, entry->d_name); + int sub_count = cleanup_old_archives(fullpath); + if (sub_count > 0) { + removed_count += sub_count; + } + } else if (S_ISREG(st.st_mode)) { + size_t len = strlen(entry->d_name); + if (len < 5 || strcmp(entry->d_name + len - 4, ".tgz") != 0) { + continue; + } + + int path_written = snprintf(fullpath, sizeof(fullpath), "%s/%s", log_path, entry->d_name); + + if (path_written < 0 || path_written >= (int)sizeof(fullpath)) { + + RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB, + + "[%s:%d] Path too long, skipping file: %s/%s\n", + + __FUNCTION__, __LINE__, log_path, entry->d_name); + + continue; + + } + + RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, "[%s:%d] Removing old archive: %s\n", __FUNCTION__, __LINE__, fullpath); + /* unlinkat operates on the same dir FD — no path race possible */ + if (unlinkat(dfd, entry->d_name, 0) == 0) { + removed_count++; + } else { + RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB, + "[%s:%d] Failed to remove: %s\n", + __FUNCTION__, __LINE__, fullpath); + } } } closedir(dir); - RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, + RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, "[%s:%d] Archive cleanup complete: removed %d .tgz files from %s\n", __FUNCTION__, __LINE__, removed_count, log_path); diff --git a/uploadstblogs/src/event_manager.c b/uploadstblogs/src/event_manager.c index d22b35bfd..f55336c05 100755 --- a/uploadstblogs/src/event_manager.c +++ b/uploadstblogs/src/event_manager.c @@ -184,10 +184,9 @@ void emit_upload_failure(const RuntimeContext* ctx, const SessionState* session) void emit_upload_aborted(void) { RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB, - "[%s:%d] Upload operation was aborted\n", __FUNCTION__, __LINE__); + "[%s:%d] Not Uploading Logs with DCM \n", __FUNCTION__, __LINE__); - // Send abort events - send_iarm_event("LogUploadEvent", LOG_UPLOAD_ABORTED); + send_iarm_event("LogUploadEvent", LOG_UPLOAD_FAILED); send_iarm_event_maintenance(MAINT_LOGUPLOAD_ERROR); } @@ -432,7 +431,6 @@ void emit_folder_missing_error(void) RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Required folder missing for log upload\n", __FUNCTION__, __LINE__); - // Send maintenance error event (matches script behavior) - send_iarm_event_maintenance(MAINT_LOGUPLOAD_ERROR); + // Send maintenance complete event (matches script behavior) + send_iarm_event_maintenance(MAINT_LOGUPLOAD_COMPLETE); } - diff --git a/uploadstblogs/src/file_operations.c b/uploadstblogs/src/file_operations.c index 2bd70cf3b..89a40e1cf 100755 --- a/uploadstblogs/src/file_operations.c +++ b/uploadstblogs/src/file_operations.c @@ -348,13 +348,26 @@ int add_timestamp_to_files(const char* dir_path) // Get current timestamp in script format: MM-DD-YY-HH-MMAM/PM- time_t now = time(NULL); - struct tm* tm_info = localtime(&now); + struct tm tm_utc; + if (gmtime_r(&now, &tm_utc) == NULL) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Failed to get UTC time\n", __FUNCTION__, __LINE__); + return -1; + } char timestamp[32]; - strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p-", tm_info); - + size_t timestamp_len = strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p-", &tm_utc); + if (timestamp_len == 0) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, + "[%s:%d] Failed to format UTC timestamp\n", + __FUNCTION__, __LINE__); + return -1; + } + // Store timestamp prefix globally for removal later (matches script behavior) + strncpy(g_timestamp_prefix, timestamp, sizeof(g_timestamp_prefix) - 1); + g_timestamp_prefix[sizeof(g_timestamp_prefix) - 1] = '\0'; + DIR* dir = opendir(dir_path); if (!dir) { RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, @@ -412,7 +425,6 @@ int add_timestamp_to_files(const char* dir_path) return (error_count > 0) ? -1 : 0; } - /** * @brief Remove timestamp prefix from all files in directory * @param dir_path Directory containing files to rename @@ -538,9 +550,13 @@ int add_timestamp_to_files_uploadlogsnow(const char* dir_path) // Get current timestamp in script format: MM-DD-YY-HH-MMAM/PM- time_t now = time(NULL); - struct tm* tm_info = localtime(&now); + struct tm tm_utc; + if (gmtime_r(&now, &tm_utc) == NULL) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Failed to get UTC time\n", __FUNCTION__, __LINE__); + return -1; + } char timestamp[32]; - strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p-", tm_info); + strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p-", &tm_utc); // Store timestamp prefix globally for removal later (matches script behavior) strncpy(g_timestamp_prefix, timestamp, sizeof(g_timestamp_prefix) - 1); @@ -641,7 +657,6 @@ int add_timestamp_to_files_uploadlogsnow(const char* dir_path) return (error_count > 0) ? -1 : 0; } - /** * @brief Move all contents from source directory to destination directory * @param src_dir Source directory diff --git a/uploadstblogs/src/path_handler.c b/uploadstblogs/src/path_handler.c index ac81f305f..cadde5675 100755 --- a/uploadstblogs/src/path_handler.c +++ b/uploadstblogs/src/path_handler.c @@ -531,6 +531,7 @@ static UploadResult perform_s3_put_with_fallback(RuntimeContext* ctx, SessionSta if (s3_verified == UPLOADSTB_SUCCESS) { t2_count_notify("TEST_lu_success"); // Script line 616 session->success = true; + RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, "[%s:%d] Direct log upload Success: httpcode= %d\n", __FUNCTION__, __LINE__, session->http_code); return UPLOADSTB_SUCCESS; } diff --git a/uploadstblogs/src/strategies.c b/uploadstblogs/src/strategies.c old mode 100755 new mode 100644 index ca7e84f7c..8629dc82f --- a/uploadstblogs/src/strategies.c +++ b/uploadstblogs/src/strategies.c @@ -46,6 +46,7 @@ #include "rbus_interface.h" #include "rdk_debug.h" #include "event_manager.h" +#include "cleanup_handler.h" #define ONDEMAND_TEMP_DIR "/tmp/log_on_demand" @@ -406,11 +407,22 @@ static int ondemand_setup(RuntimeContext* ctx, SessionState* session) // Create timestamp for permanent log path (for logging purposes only) char timestamp[64]; time_t now = time(NULL); - struct tm* tm_info = localtime(&now); - strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p-logbackup", tm_info); + struct tm tm_utc; + size_t timestamp_len; + if (gmtime_r(&now, &tm_utc) == NULL) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Failed to get UTC time\n", __FUNCTION__, __LINE__); + return -1; + } + timestamp_len = strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p-logbackup", &tm_utc); + if (timestamp_len == 0U) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, + "[%s:%d] Failed to format timestamp for permanent log path\n", + __FUNCTION__, __LINE__); + return -1; + } char perm_log_path[MAX_PATH_LENGTH]; - int written = snprintf(perm_log_path, sizeof(perm_log_path), "%s/%s", + int written = snprintf(perm_log_path, sizeof(perm_log_path), "%s/%s", ctx->log_path, timestamp); if (written >= (int)sizeof(perm_log_path)) { @@ -456,7 +468,6 @@ static int ondemand_setup(RuntimeContext* ctx, SessionState* session) return 0; } - /** * @brief Archive phase for ONDEMAND strategy * @@ -674,30 +685,29 @@ static int reboot_setup(RuntimeContext* ctx, SessionState* session) __FUNCTION__, __LINE__); } - // Delete old backup files (3+ days old) - // Remove old timestamp directories and logbackup directories - RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, - "[%s:%d] Cleaning old backups (3+ days)\n", __FUNCTION__, __LINE__); - - int removed = remove_old_directories(ctx->log_path, "*-*-*-*-*M-", 3); - if (removed > 0) { - RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, - "[%s:%d] Removed %d old timestamp directories\n", - __FUNCTION__, __LINE__, removed); + // Clean up old log backup directories (older than 3 days) + RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, "[%s:%d] Cleaning old log backup directories (3+ days)\n", __FUNCTION__, __LINE__); + int removed_dirs = cleanup_old_log_backups(ctx->log_path, 3); + if (removed_dirs > 0) { + RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, "[%s:%d] Removed %d old log backup directories\n", __FUNCTION__, __LINE__, removed_dirs); + } else { + RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, "[%s:%d] No old log backup directories removed\n", __FUNCTION__, __LINE__); } - removed = remove_old_directories(ctx->log_path, "*-*-*-*-*M-logbackup", 3); - if (removed > 0) { - RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, - "[%s:%d] Removed %d old logbackup directories\n", - __FUNCTION__, __LINE__, removed); - } - // Create timestamp for permanent log path char timestamp[64]; time_t now = time(NULL); - struct tm* tm_info = localtime(&now); - strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p-logbackup", tm_info); + struct tm tm_utc; + size_t timestamp_len; + if (gmtime_r(&now, &tm_utc) == NULL) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Failed to get UTC time\n", __FUNCTION__, __LINE__); + return -1; + } + timestamp_len = strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p-logbackup", &tm_utc); + if (timestamp_len == 0U) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Failed to format timestamp for permanent log path\n", __FUNCTION__, __LINE__); + return -1; + } char perm_log_path[MAX_PATH_LENGTH]; int written = snprintf(perm_log_path, sizeof(perm_log_path), "%s/%s", @@ -824,6 +834,7 @@ static int reboot_upload(RuntimeContext* ctx, SessionState* session) RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, "[%s:%d] REBOOT/NON_DCM: Starting upload phase\n", __FUNCTION__, __LINE__); + RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, "[%s:%d] UploadOnReboot set to %s\n", __FUNCTION__, __LINE__, ctx->upload_on_reboot ? "true" : "false"); // Check reboot reason and RFC settings (matches script logic) // Script: if [ "$uploadLog" == "true" ] || [ -z "$reboot_reason" -a "$DISABLE_UPLOAD_LOGS_UNSHEDULED_REBOOT" == "false" ] // Note: When DCM_FLAG=0 (Non-DCM), script ALWAYS passes "true" regardless of UploadOnReboot value @@ -838,13 +849,7 @@ static int reboot_upload(RuntimeContext* ctx, SessionState* session) "[%s:%d] Non-DCM mode (dcm_flag=0), will always upload logs\n", __FUNCTION__, __LINE__); } - // DCM mode (DCM_FLAG=1): Check upload_on_reboot flag - else if (ctx->upload_on_reboot) { - should_upload = true; - RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, - "[%s:%d] DCM mode: Upload enabled from settings (upload_on_reboot=true)\n", - __FUNCTION__, __LINE__); - } else { + else { // Check reboot reason file for scheduled reboot (grep -i "Scheduled Reboot\|MAINTENANCE_REBOOT") bool is_scheduled_reboot = false; FILE* reboot_file = fopen(reboot_info_path, "r"); @@ -853,11 +858,14 @@ static int reboot_upload(RuntimeContext* ctx, SessionState* session) while (fgets(line, sizeof(line), reboot_file)) { // Look for "Scheduled Reboot" or "MAINTENANCE_REBOOT" (case insensitive) if (strcasestr(line, "Scheduled Reboot") || strcasestr(line, "MAINTENANCE_REBOOT")) { + RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, "[%s:%d] reboot_reason: %s \n", __FUNCTION__, __LINE__,line); is_scheduled_reboot = true; break; } } fclose(reboot_file); + } else { + RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB, "[%s:%d] Could not open reboot reason file: %s\n", __FUNCTION__, __LINE__, reboot_info_path); } // Get RFC setting for unscheduled reboot upload via RBUS @@ -870,36 +878,36 @@ static int reboot_upload(RuntimeContext* ctx, SessionState* session) disable_unscheduled_upload = false; } - RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, - "[%s:%d] Reboot reason check - Scheduled: %d, Disable unscheduled RFC: %d\n", - __FUNCTION__, __LINE__, is_scheduled_reboot, disable_unscheduled_upload); + RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, "[%s:%d] uploadLog:%s and UploadLogsOnUnscheduledReboot.Disable RFC: %s\n", __FUNCTION__, __LINE__, ctx->upload_on_reboot ? "true" : "false", disable_unscheduled_upload ? "true" : "false"); - // Upload if: reboot reason is empty (unscheduled) AND RFC doesn't disable it - // Script logic: [ -z "$reboot_reason" -a "$DISABLE_UPLOAD_LOGS_UNSHEDULED_REBOOT" == "false" ] - if (!is_scheduled_reboot && !disable_unscheduled_upload) { + // Upload if upload_on_reboot is enabled, OR if the reboot is unscheduled + // and the UploadLogsOnUnscheduledReboot.Disable RFC does not disable it. + // Script logic for the unscheduled reboot path: + // [ -z "$reboot_reason" -a "$DISABLE_UPLOAD_LOGS_UNSHEDULED_REBOOT" == "false" ] + if ( ctx->upload_on_reboot==1 || (!is_scheduled_reboot && !disable_unscheduled_upload)) { should_upload = true; - RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, - "[%s:%d] Unscheduled reboot and RFC allows upload\n", __FUNCTION__, __LINE__); } } - - if (!should_upload) { - RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, - "[%s:%d] Upload not allowed based on reboot reason and RFC settings\n", - __FUNCTION__, __LINE__); - return 0; - } - // Construct full archive path using session archive filename + // Construct full archive path using session archive filename char archive_path[MAX_PATH_LENGTH]; - int written = snprintf(archive_path, sizeof(archive_path), "%s/%s", - ctx->prev_log_path, session->archive_file); + int written = snprintf(archive_path, sizeof(archive_path), "%s/%s", ctx->prev_log_path, session->archive_file); if (written >= (int)sizeof(archive_path)) { RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Archive path too long\n", __FUNCTION__, __LINE__); return -1; } + + if (!should_upload) { + RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, + "[%s:%d] Upload not allowed based on reboot reason and RFC settings\n", + __FUNCTION__, __LINE__); + strncpy(session->archive_file, archive_path, sizeof(session->archive_file) - 1); + session->archive_file[sizeof(session->archive_file) - 1] = '\0'; + emit_upload_aborted(); + return 0; + } RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, "[%s:%d] Uploading main logs: %s\n", @@ -1002,21 +1010,11 @@ static int reboot_cleanup(RuntimeContext* ctx, SessionState* session, bool uploa sleep(5); // Delete tar file - char tar_path[MAX_PATH_LENGTH]; - int written = snprintf(tar_path, sizeof(tar_path), "%s/%s", - ctx->prev_log_path, session->archive_file); - - if (written >= (int)sizeof(tar_path)) { - RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, - "[%s:%d] Tar path too long\n", __FUNCTION__, __LINE__); - return -1; - } - - if (file_exists(tar_path)) { + if (file_exists(session->archive_file)) { RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, "[%s:%d] Removing tar file: %s\n", - __FUNCTION__, __LINE__, tar_path); - remove_file(tar_path); + __FUNCTION__, __LINE__, session->archive_file); + remove_file(session->archive_file); } // Remove timestamps from filenames (restore original names) @@ -1067,7 +1065,7 @@ static int reboot_cleanup(RuntimeContext* ctx, SessionState* session, bool uploa // Script lines 900-902: rm -rf + mkdir -p PREV_LOG_BACKUP_PATH // PREV_LOG_BACKUP_PATH = $LOG_PATH/PreviousLogs_backup/ char prev_log_backup_path[MAX_PATH_LENGTH]; - written = snprintf(prev_log_backup_path, sizeof(prev_log_backup_path), "%s/PreviousLogs_backup", + int written = snprintf(prev_log_backup_path, sizeof(prev_log_backup_path), "%s/PreviousLogs_backup", ctx->log_path); if (written >= (int)sizeof(prev_log_backup_path)) { @@ -1112,4 +1110,3 @@ static int reboot_cleanup(RuntimeContext* ctx, SessionState* session, bool uploa return 0; } - diff --git a/uploadstblogs/src/strategy_handler.c b/uploadstblogs/src/strategy_handler.c index 0496026a9..9a0086a2c 100755 --- a/uploadstblogs/src/strategy_handler.c +++ b/uploadstblogs/src/strategy_handler.c @@ -24,6 +24,7 @@ #include #include "strategy_handler.h" +#include "cleanup_handler.h" #include "rdk_debug.h" #include @@ -70,6 +71,8 @@ int execute_strategy_workflow(RuntimeContext* ctx, SessionState* session) return -1; } + // Remove stale .tgz archives from log path before any strategy runs. + cleanup_old_archives(ctx->log_path); // Verify context has valid data RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, "[%s:%d] Context check: ctx=%p, MAC='%s', device_type='%s'\n", @@ -157,4 +160,3 @@ int execute_strategy_workflow(RuntimeContext* ctx, SessionState* session) return ret; } - diff --git a/uploadstblogs/src/uploadstblogs.c b/uploadstblogs/src/uploadstblogs.c index 40b43bbc8..6edf36d59 100755 --- a/uploadstblogs/src/uploadstblogs.c +++ b/uploadstblogs/src/uploadstblogs.c @@ -127,7 +127,7 @@ bool parse_args(int argc, char** argv, RuntimeContext* ctx) if (argc >= 5 && argv[4]) { // Parse UploadOnReboot - ctx->upload_on_reboot = (strcmp(argv[4], "true") == 0) ? 1 : 0; + ctx->upload_on_reboot = (strcmp(argv[4], "true") == 0 || strcmp(argv[4], "1") == 0) ? 1 : 0; fprintf(stderr, "DEBUG: UploadOnReboot (argv[4]) = '%s' -> %d\n", argv[4], ctx->upload_on_reboot); } diff --git a/uploadstblogs/unittest/cleanup_handler_gtest.cpp b/uploadstblogs/unittest/cleanup_handler_gtest.cpp index f558f65fe..c491b9e1b 100755 --- a/uploadstblogs/unittest/cleanup_handler_gtest.cpp +++ b/uploadstblogs/unittest/cleanup_handler_gtest.cpp @@ -68,7 +68,10 @@ void regfree(regex_t *preg) { DIR* opendir(const char *dirname); struct dirent* readdir(DIR *dirp); int closedir(DIR *dirp); +int dirfd(DIR *dirp); int stat(const char *pathname, struct stat *statbuf); +int fstatat(int dfd, const char *pathname, struct stat *statbuf, int flags); +int unlinkat(int dfd, const char *pathname, int flags); int remove(const char *pathname); int rmdir(const char *pathname); @@ -126,6 +129,40 @@ int closedir(DIR *dirp) { return 0; } +int dirfd(DIR *dirp) { + // Return a dummy fd for the fake DIR pointer + return 5; +} + +int fstatat(int dfd, const char *pathname, struct stat *statbuf, int flags) { + if (stat_fail || !pathname || !statbuf) { + return -1; + } + memset(statbuf, 0, sizeof(struct stat)); + + time_t now = time(NULL); + if (strstr(pathname, "11-30-25-03-45PM") || strstr(pathname, "old_archive")) { + statbuf->st_mtime = now - (5 * 24 * 60 * 60); // 5 days ago + } else { + statbuf->st_mtime = now - (1 * 24 * 60 * 60); // 1 day ago + } + + if (strstr(pathname, "logbackup") || strstr(pathname, "normal_folder")) { + statbuf->st_mode = S_IFDIR | 0755; + } else { + statbuf->st_mode = S_IFREG | 0644; + } + + return 0; +} + +int unlinkat(int dfd, const char *pathname, int flags) { + if (remove_fail || !pathname) { + return -1; + } + return 0; +} + int stat(const char *pathname, struct stat *statbuf) { if (stat_fail || !pathname || !statbuf) { return -1; diff --git a/uploadstblogs/unittest/strategies_gtest.cpp b/uploadstblogs/unittest/strategies_gtest.cpp index e4308e0f2..38ccab8c2 100755 --- a/uploadstblogs/unittest/strategies_gtest.cpp +++ b/uploadstblogs/unittest/strategies_gtest.cpp @@ -60,6 +60,7 @@ bool rbus_get_bool_param(const char* param_name, bool* value); bool generate_archive_name(char* buffer, size_t buffer_size, const char* type, const char* timestamp); int create_dri_archive(RuntimeContext* ctx, const char* archive_path); void t2_count_notify(char* marker); +int cleanup_old_log_backups(const char* log_path, int max_age_days); // Mock sleep function to avoid delays in tests unsigned int sleep(unsigned int seconds); @@ -166,6 +167,11 @@ void emit_no_logs_reboot(const RuntimeContext* ctx) { // No-op for tests } +// Mock for emit_upload_aborted used by strategies.c +void emit_upload_aborted(void) { + // No-op for tests +} + int remove_timestamp_from_files(const char* dirpath) { return 0; // Success } @@ -199,6 +205,10 @@ void t2_count_notify(char* marker) { // No-op for tests } +int cleanup_old_log_backups(const char* log_path, int max_age_days) { + return 0; // Success +} + // Include the actual implementation for testing #ifdef GTEST_ENABLE #include "../src/strategies.c" @@ -665,4 +675,4 @@ TEST_F(StrategiesIntegrationTest, ErrorHandling_UploadFailure) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/uploadstblogs/unittest/strategy_handler_gtest.cpp b/uploadstblogs/unittest/strategy_handler_gtest.cpp index 6bbcf05c8..7b26499ec 100755 --- a/uploadstblogs/unittest/strategy_handler_gtest.cpp +++ b/uploadstblogs/unittest/strategy_handler_gtest.cpp @@ -28,6 +28,7 @@ extern "C" { #include "uploadstblogs_types.h" #include "strategy_handler.h" +int cleanup_old_archives(const char* log_path); } // Mock strategy handlers for testing @@ -97,6 +98,11 @@ static const StrategyHandler mock_dcm_handler = { .cleanup_phase = mock_cleanup_phase }; +// Mock implementation for cleanup_old_archives +extern "C" int cleanup_old_archives(const char* log_path) { + return 0; // Success +} + // Override the external strategy handlers const StrategyHandler ondemand_strategy_handler = mock_ondemand_handler; const StrategyHandler reboot_strategy_handler = mock_reboot_handler; @@ -439,4 +445,4 @@ TEST_F(StrategyHandlerTest, ExecuteWorkflow_PhaseSequencing) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +}