diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b8baff5..e7bb0fee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,20 @@ 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.4](https://github.com/rdkcentral/dcm-agent/compare/2.0.3...2.0.4) + +- 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) +- 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/backup_logs/docs/backup_logs_LLD.md b/backup_logs/docs/backup_logs_LLD.md new file mode 100644 index 000000000..2e4f5f0ef --- /dev/null +++ b/backup_logs/docs/backup_logs_LLD.md @@ -0,0 +1,1029 @@ +# Low-Level Design: backup_logs.sh Migration to C + +## 1. Overview + +This Low-Level Design (LLD) document provides detailed implementation specifications for migrating the `backup_logs.sh` shell script to C code for embedded RDK systems. + +## 2. Detailed Data Structures + +### 2.1 Core Configuration Structure +```c +/* Using implementation constants */ +#define MAX_SPECIAL_FILES 32 +#define MAX_ERROR_MESSAGE_LEN 256 +#define MAX_FUNCTION_NAME_LEN 64 +#define MAX_DESCRIPTION_LEN 128 +#define MAX_CONDITIONAL_LEN 64 +#define LOG_BACKUP_LOGS "LOG.RDK.BACKUPLOGS" + +/* Main backup configuration structure (matches implementation) */ +typedef struct { + char log_path[PATH_MAX]; + char prev_log_path[PATH_MAX]; + char prev_log_backup_path[PATH_MAX]; + char persistent_path[PATH_MAX]; + bool hdd_enabled; +} backup_config_t; +``` + +### 2.2 File Operation Structures +```c +/* Backup operation types (matches implementation) */ +typedef enum { + BACKUP_OP_MOVE, + BACKUP_OP_COPY, + BACKUP_OP_DELETE +} backup_operation_type_t; + +/* Special file handling structures (matches implementation) */ +typedef enum { + SPECIAL_FILE_COPY = 0, // cp operation + SPECIAL_FILE_MOVE = 1 // mv operation +} special_file_operation_t; + +typedef struct { + char source_path[PATH_MAX]; + char destination_path[PATH_MAX]; + special_file_operation_t operation; + char conditional_check[MAX_CONDITIONAL_LEN]; +} special_file_entry_t; + +typedef struct { + special_file_entry_t entries[MAX_SPECIAL_FILES]; + size_t count; + bool config_loaded; +} special_files_config_t; + +/* Configuration flags for advanced options */ +typedef struct { + bool debug_enabled; + bool force_rotation; + bool skip_disk_check; + bool cleanup_enabled; +} backup_flags_t; +``` + +### 2.3 Error Handling Structures +```c +/* Return codes (matches implementation) */ +typedef enum { + BACKUP_SUCCESS = 0, + BACKUP_ERROR_CONFIG = -1, + BACKUP_ERROR_FILESYSTEM = -2, + BACKUP_ERROR_PERMISSIONS = -3, + BACKUP_ERROR_MEMORY = -4, + BACKUP_ERROR_INVALID_PARAM = -5, + BACKUP_ERROR_NOT_FOUND = -6, + BACKUP_ERROR_DISK_FULL = -7, + BACKUP_ERROR_SYSTEM = -8 +} backup_result_t; + +/* Error information structure (matches implementation) */ +typedef struct { + int error_code; + char error_message[MAX_ERROR_MESSAGE_LEN]; + char function_name[MAX_FUNCTION_NAME_LEN]; + int line_number; +} error_info_t; +``` + +### 2.4 Backup Level Tracking +```c +typedef enum { + BACKUP_LEVEL_NONE = -1, + BACKUP_LEVEL_BASE = 0, + BACKUP_LEVEL_BAK1 = 1, + BACKUP_LEVEL_BAK2 = 2, + BACKUP_LEVEL_BAK3 = 3 +} backup_level_t; + +typedef struct backup_state { + backup_level_t current_level; + bool has_existing_backup; + char timestamp_str[32]; // Format: MM-DD-YY-HH-MM-SSAM +} backup_state_t; +``` + +## 3. Module Interface Definitions + +### 3.1 Configuration Manager Module +```c +// config_manager.h (actual implementation interfaces) + +// Load backup configuration from RDK property system +int config_load(backup_config_t* config); + +// Validate loaded configuration +int config_validate(const backup_config_t* config); + +// Get specific configuration values +const char* config_get_log_path(void); +bool config_is_hdd_enabled(void); + +// Special files configuration interface +int special_files_load_config(special_files_config_t* config, const char* config_file); +int special_files_validate_config(const special_files_config_t* config); +void special_files_free_config(special_files_config_t* config); +int special_files_execute_all(const special_files_config_t* config, + const backup_config_t* backup_config); +``` + +### 3.2 Directory Manager Module +```c +// Using RDK system utilities (actual implementation) + +// Create directory if not exists +int createDir(char* path); + +// Check if directory exists +bool dir_exists(const char* path); + +// Clean directory contents +int emptyFolder(char* path); + +// Create all required backup directories +int dir_create_workspace(const backup_config_t* config); + +// Validate directory permissions +int dir_check_permissions(const char* path, int required_perms); +``` + +### 3.3 File Operations Module +```c +// Using RDK system utilities (actual implementation) + +// Check file existence +int filePresentCheck(const char* path); + +// Copy file with verification +int copyFiles(const char* source, const char* dest); + +// Remove file safely +int removeFile(const char* path); + +// Find files matching pattern (implemented in backup engine) +int move_log_files_by_pattern(const char* source_dir, const char* dest_dir); + +// Pattern matching for log files +bool matches_log_pattern(const char* filename); // *.txt*, *.log*, bootlog +``` + +### 3.4 Backup Engine Module +```c +// backup_engine.h (actual implementation) + +// Execute HDD-enabled backup strategy +int backup_execute_hdd_enabled_strategy(const backup_config_t* config); + +// Execute HDD-disabled backup strategy +int backup_execute_hdd_disabled_strategy(const backup_config_t* config); + +// Execute common operations (special files, version files, notifications) +int backup_execute_common_operations(const backup_config_t* config); + +// Helper function to move log files by pattern +int move_log_files_by_pattern(const char* source_dir, const char* dest_dir, + char* timestamp_dir, size_t dir_size); + +// Create last_reboot marker file +int backup_create_reboot_marker(const char* directory); + +// Remove old reboot markers +int backup_remove_old_markers(const char* directory); + +// Cleanup backup engine resources +void backup_cleanup(void); +``` + +### 3.5 System Integration Module +```c +// sys_integration.h (actual implementation) + +// Initialize system integration +int sys_init(void); + +// Send systemd notification +int sys_notify_ready(void); +int sys_notify_status(const char* status); + +// Execute external script safely +int sys_execute_disk_check(void); + +// Create persistent marker file +int sys_create_marker(const char* path); + +// Cleanup system integration resources +void sys_cleanup(void); +``` + +### 3.6 RDK Logger Integration Module +```c +// RDK Logger integration (actual implementation) + +#ifdef RDK_LOGGER_EXT +#define RDK_LOGGER_ENABLED +#endif + +#ifdef RDK_LOGGER_ENABLED +#include "rdk_debug.h" +#include "rdk_logger.h" +#endif + +// Logger initialization +int backup_logs_init_logger(void); + +// Extended logger configuration +typedef struct { + char* pModuleName; + int loglevel; + int output; + int format; + void* pFilePolicy; +} rdk_logger_ext_config_t; + +// RDK Logger constants +#define LOG_BACKUP_LOGS "LOG.RDK.BACKUPLOGS" +#define DEBUG_INI_NAME "/etc/debug.ini" +#define BACKUP_LOGS_VERSION "1.0.0" +#define BACKUP_LOGS_BUILD_DATE __DATE__ + +// Logger convenience macros +#define RDK_LOG(level, module, format, ...) \ + rdk_log(level, module, format, ##__VA_ARGS__) +``` + +### 3.7 Special Files Manager Module +```c +// special_files.h (actual implementation) + +// Initialize special files manager +int special_files_init(void); + +// Cleanup special files manager +void special_files_cleanup(void); + +// Load special files configuration (one filename per line) +int special_files_load_config(special_files_config_t* config, const char* config_file); + +// Validate special file entry +int special_files_validate_entry(const special_file_entry_t* entry); + +// Execute single special file operation +int special_files_execute_entry(const special_file_entry_t* entry, + const backup_config_t* backup_config); + +// Execute all special file operations +int special_files_execute_all(const special_files_config_t* config, + const backup_config_t* backup_config); +``` + +## 4. Build System and Dependencies + +### 4.1 Build Configuration +```makefile +# Required libraries and flags (from Makefile.am) +backup_logs_LDADD = -lm -lrdkloggers -lfwutils -lsystemd +backup_logs_CPPFLAGS = -DRDK_LOGGER_EXT +``` + +### 4.2 Dependencies +- **librdkloggers**: RDK logging framework +- **libfwutils**: RDK firmware utilities for configuration and system operations +- **libsystemd**: Systemd integration for service notifications +- **libm**: Math library for numerical operations + +### 4.3 Build-time Configuration +```c +#ifdef RDK_LOGGER_EXT +#define RDK_LOGGER_ENABLED +#endif +``` + +## 5. Detailed Algorithms + +### 5.1 RDK Configuration Loading Algorithm (Actual Implementation) +```c +int config_load_rdk_properties(backup_config_t* config) { + char buffer[PATH_MAX]; + + // Load LOG_PATH from include properties + if (getIncludePropertyData("LOG_PATH", buffer, sizeof(buffer)) == 0) { + strncpy(config->log_path, buffer, sizeof(config->log_path) - 1); + } else { + // Use default if not found + strcpy(config->log_path, "/opt/logs"); + } + + // Load HDD_ENABLED from device properties + if (getDevicePropertyData("HDD_ENABLED", buffer, sizeof(buffer)) == 0) { + config->hdd_enabled = (strcmp(buffer, "true") == 0); + } else { + config->hdd_enabled = false; // Default to false + } + + // Construct derived paths + snprintf(config->prev_log_path, sizeof(config->prev_log_path), + "%s/PreviousLogs", config->log_path); + snprintf(config->prev_log_backup_path, sizeof(config->prev_log_backup_path), + "%s/PreviousLogs_backup", config->log_path); + + return BACKUP_SUCCESS; +} +``` +### 4.2 HDD-Disabled Backup Level Detection +```c +backup_level_t backup_detect_level_hdd_disabled(const backup_config_t* config) { + char filepath[MAX_PATH_LEN]; + + // Check for messages.txt (base level) + snprintf(filepath, sizeof(filepath), "%s/messages.txt", config->prev_log_path); + if (!fileops_exists(filepath)) { + return BACKUP_LEVEL_NONE; + } + + // Check for bak1_messages.txt + snprintf(filepath, sizeof(filepath), "%s/bak1_messages.txt", config->prev_log_path); + if (!fileops_exists(filepath)) { + return BACKUP_LEVEL_BASE; + } + + // Check for bak2_messages.txt + snprintf(filepath, sizeof(filepath), "%s/bak2_messages.txt", config->prev_log_path); + if (!fileops_exists(filepath)) { + return BACKUP_LEVEL_BAK1; + } + + // Check for bak3_messages.txt + snprintf(filepath, sizeof(filepath), "%s/bak3_messages.txt", config->prev_log_path); + if (!fileops_exists(filepath)) { + return BACKUP_LEVEL_BAK2; + } + + return BACKUP_LEVEL_BAK3; // All levels exist, need rotation +} +``` + +### 4.3 File Pattern Matching Algorithm +```c +int fileops_find_pattern_impl(const char* directory, const char* pattern, + file_list_t* results) { + DIR* dir = opendir(directory); + if (!dir) { + return -1; + } + + struct dirent* entry; + results->count = 0; + + while ((entry = readdir(dir)) != NULL && results->count < results->capacity) { + // Skip . and .. + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + // Check if filename matches any of the patterns + bool matches = false; + + // Support multiple patterns: *.txt*, *.log*, *.bin*, bootlog + if (strstr(pattern, "*.txt*") && + (strstr(entry->d_name, ".txt") || strstr(entry->d_name, ".TXT"))) { + matches = true; + } else if (strstr(pattern, "*.log*") && + (strstr(entry->d_name, ".log") || strstr(entry->d_name, ".LOG"))) { + matches = true; + } else if (strstr(pattern, "*.bin*") && + (strstr(entry->d_name, ".bin") || strstr(entry->d_name, ".BIN"))) { + matches = true; + } else if (strstr(pattern, "bootlog") && + strcmp(entry->d_name, "bootlog") == 0) { + matches = true; + } + + if (matches) { + // Build full path + snprintf(results->files[results->count].filename, + sizeof(results->files[results->count].filename), + "%s", entry->d_name); + snprintf(results->files[results->count].source_path, + sizeof(results->files[results->count].source_path), + "%s/%s", directory, entry->d_name); + results->count++; + } + } + + closedir(dir); + return results->count; +} +``` + +### 4.4 Log Rotation Algorithm for HDD-Disabled Devices +```c +int backup_rotate_files_hdd_disabled(const backup_config_t* config) { + char source_path[MAX_PATH_LEN]; + char dest_path[MAX_PATH_LEN]; + file_list_t file_list = {0}; + file_list.capacity = MAX_FILES_PER_DIR; + + // Step 1: Move bak1_ files to base names (bak1_messages.txt -> messages.txt) + if (fileops_find_pattern(config->prev_log_path, "bak1_*", &file_list) > 0) { + for (int i = 0; i < file_list.count; i++) { + // Remove bak1_ prefix + const char* base_name = file_list.files[i].filename + 5; // Skip "bak1_" + snprintf(dest_path, sizeof(dest_path), "%s/%s", + config->prev_log_path, base_name); + + if (fileops_move(file_list.files[i].source_path, dest_path) != 0) { + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, + "Failed to rotate bak1 file to base"); + return -1; + } + } + } + + // Step 2: Move bak2_ files to bak1_ names + file_list.count = 0; // Reset list + if (fileops_find_pattern(config->prev_log_path, "bak2_*", &file_list) > 0) { + for (int i = 0; i < file_list.count; i++) { + // Replace bak2_ with bak1_ + const char* base_name = file_list.files[i].filename + 5; // Skip "bak2_" + snprintf(dest_path, sizeof(dest_path), "%s/bak1_%s", + config->prev_log_path, base_name); + + if (fileops_move(file_list.files[i].source_path, dest_path) != 0) { + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, + "Failed to rotate bak2 file to bak1"); + return -1; + } + } + } + + // Step 3: Move bak3_ files to bak2_ names + file_list.count = 0; // Reset list + if (fileops_find_pattern(config->prev_log_path, "bak3_*", &file_list) > 0) { + for (int i = 0; i < file_list.count; i++) { + // Replace bak3_ with bak2_ + const char* base_name = file_list.files[i].filename + 5; // Skip "bak3_" + snprintf(dest_path, sizeof(dest_path), "%s/bak2_%s", + config->prev_log_path, base_name); + + if (fileops_move(file_list.files[i].source_path, dest_path) != 0) { + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, + "Failed to rotate bak3 file to bak2"); + return -1; + } + } + } + + // Step 4: Move current logs to bak3_ names + file_list.count = 0; // Reset list + char log_patterns[] = "*.txt*,*.log*,*.bin*,bootlog"; + if (fileops_find_pattern(config->log_path, log_patterns, &file_list) > 0) { + for (int i = 0; i < file_list.count; i++) { + snprintf(dest_path, sizeof(dest_path), "%s/bak3_%s", + config->prev_log_path, file_list.files[i].filename); + + if (fileops_move(file_list.files[i].source_path, dest_path) != 0) { + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, + "Failed to move current log to bak3"); + return -1; + } + } + } + + return 0; // Success +} +``` + +### 4.5 Timestamp Generation Algorithm +```c +int sysint_generate_timestamp(char* buffer, size_t buffer_size) { + time_t raw_time; + struct tm* time_info; + + // Get current time + time(&raw_time); + time_info = localtime(&raw_time); + + if (!time_info) { + return -1; + } + + // Format: MM-DD-YY-HH-MM-SSAM (e.g., 03-05-26-02-30-45PM) + char am_pm = (time_info->tm_hour >= 12) ? 'P' : 'A'; + int hour_12 = time_info->tm_hour; + if (hour_12 == 0) { + hour_12 = 12; // 12 AM + } else if (hour_12 > 12) { + hour_12 -= 12; // Convert to 12-hour format + } + + int bytes_written = snprintf(buffer, buffer_size, + "%02d-%02d-%02d-%02d-%02d-%02d%cM", + time_info->tm_mon + 1, // Month (1-12) + time_info->tm_mday, // Day (1-31) + time_info->tm_year % 100, // Year (2-digit) + hour_12, // Hour (1-12) + time_info->tm_min, // Minute (0-59) + time_info->tm_sec, // Second (0-59) + am_pm); // AM/PM + + if (bytes_written < 0 || bytes_written >= buffer_size) { + return -1; // Buffer overflow or formatting error + } + + return 0; // Success +} +``` + +## 5. Error Handling Implementation + +### 5.1 Error Context Management +```c +static error_context_t g_last_error = {0}; + +void logger_set_error(error_context_t* context, error_code_t code, + const char* function, int line, const char* message) { + if (!context) { + context = &g_last_error; + } + + context->code = code; + context->function_name = function; + context->line_number = line; + time(&context->timestamp); + + // Copy message safely + if (message) { + strncpy(context->message, message, sizeof(context->message) - 1); + context->message[sizeof(context->message) - 1] = '\0'; + } else { + context->message[0] = '\0'; + } +} + +int logger_error(const error_context_t* context) { + struct tm* time_info = localtime(&context->timestamp); + char time_str[64]; + + strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", time_info); + + fprintf(stderr, "[%s] ERROR %d in %s:%d: %s\n", + time_str, context->code, context->function_name, + context->line_number, context->message); + + return context->code; +} +``` + +### 5.2 Recovery Strategies +```c +int backup_recover_from_partial_state(const backup_config_t* config) { + // Check for incomplete operations by looking for temporary files + file_list_t temp_files = {0}; + temp_files.capacity = MAX_FILES_PER_DIR; + + // Look for .tmp, .bak, or other temporary extensions + if (fileops_find_pattern(config->log_path, "*.tmp", &temp_files) > 0) { + LOG_WARN("Found %d temporary files, attempting recovery", temp_files.count); + + for (int i = 0; i < temp_files.count; i++) { + // Try to determine original filename + char original_name[MAX_PATH_LEN]; + strncpy(original_name, temp_files.files[i].filename, + strlen(temp_files.files[i].filename) - 4); // Remove .tmp + original_name[strlen(temp_files.files[i].filename) - 4] = '\0'; + + char original_path[MAX_PATH_LEN]; + snprintf(original_path, sizeof(original_path), "%s/%s", + config->log_path, original_name); + + // If original doesn't exist, restore from temp + if (!fileops_exists(original_path)) { + if (fileops_move(temp_files.files[i].source_path, original_path) == 0) { + LOG_INFO("Recovered file: %s", original_name); + } + } else { + // Original exists, remove temp file + fileops_remove(temp_files.files[i].source_path); + } + } + } + + // Check for incomplete backup directories + DIR* dir = opendir(config->prev_log_path); + if (dir) { + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + // Look for directories with .incomplete suffix + if (strstr(entry->d_name, ".incomplete")) { + char incomplete_path[MAX_PATH_LEN]; + snprintf(incomplete_path, sizeof(incomplete_path), "%s/%s", + config->prev_log_path, entry->d_name); + + LOG_WARN("Found incomplete backup directory: %s", incomplete_path); + // Remove incomplete backup directory + dir_cleanup(incomplete_path, "*"); + rmdir(incomplete_path); + } + } + closedir(dir); + } + + return 0; +} +``` + +## 6. Memory Management Strategy + +### 6.1 Fixed Buffer Pool Implementation +```c +#define BUFFER_POOL_SIZE 10 +#define BUFFER_SIZE 4096 + +static struct { + char buffers[BUFFER_POOL_SIZE][BUFFER_SIZE]; + bool in_use[BUFFER_POOL_SIZE]; + int allocated_count; +} g_buffer_pool = {0}; + +char* buffer_pool_allocate(void) { + for (int i = 0; i < BUFFER_POOL_SIZE; i++) { + if (!g_buffer_pool.in_use[i]) { + g_buffer_pool.in_use[i] = true; + g_buffer_pool.allocated_count++; + return g_buffer_pool.buffers[i]; + } + } + return NULL; // Pool exhausted +} + +void buffer_pool_free(char* buffer) { + for (int i = 0; i < BUFFER_POOL_SIZE; i++) { + if (g_buffer_pool.buffers[i] == buffer) { + g_buffer_pool.in_use[i] = false; + g_buffer_pool.allocated_count--; + return; + } + } +} + +int buffer_pool_get_usage(void) { + return g_buffer_pool.allocated_count; +} +``` + +### 6.2 Stack-based File Operation +```c +int fileops_move_safe(const char* source, const char* dest) { + char temp_dest[MAX_PATH_LEN]; // Stack allocation + error_context_t error_ctx = {0}; // Stack allocation + + // Create temporary destination name + snprintf(temp_dest, sizeof(temp_dest), "%s.tmp", dest); + + // Step 1: Copy to temporary location + if (fileops_copy(source, temp_dest) != 0) { + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, "Failed to copy to temporary location"); + return -1; + } + + // Step 2: Verify copy integrity + if (fileops_get_size(source) != fileops_get_size(temp_dest)) { + fileops_remove(temp_dest); + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, "File size mismatch after copy"); + return -1; + } + + // Step 3: Atomic rename + if (rename(temp_dest, dest) != 0) { + fileops_remove(temp_dest); + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, "Failed to rename to final destination"); + return -1; + } + + // Step 4: Remove original + if (fileops_remove(source) != 0) { + // Log warning but don't fail the operation + LOG_WARN("Failed to remove source file: %s", source); + } + + return 0; // Success +} +``` + +## 7. Performance Optimization Techniques + +### 7.1 Batch File Operations +```c +int fileops_batch_move(const file_list_t* file_list, const char* dest_dir) { + int success_count = 0; + int total_files = file_list->count; + + // Pre-allocate destination paths to avoid repeated allocations + char dest_paths[MAX_FILES_PER_DIR][MAX_PATH_LEN]; + + // Prepare all destination paths first + for (int i = 0; i < total_files; i++) { + snprintf(dest_paths[i], sizeof(dest_paths[i]), "%s/%s", + dest_dir, file_list->files[i].filename); + } + + // Execute moves in batch with progress tracking + for (int i = 0; i < total_files; i++) { + if (fileops_move_safe(file_list->files[i].source_path, dest_paths[i]) == 0) { + success_count++; + } else { + LOG_WARN("Failed to move file %d of %d: %s", + i + 1, total_files, file_list->files[i].filename); + } + + // Report progress every 100 files for large operations + if (total_files > 100 && (i + 1) % 100 == 0) { + LOG_INFO("Moved %d of %d files (%d%%)", success_count, i + 1, + ((i + 1) * 100) / total_files); + } + } + + LOG_INFO("Batch move completed: %d of %d files successful", + success_count, total_files); + + return (success_count == total_files) ? 0 : -1; +} +``` + +### 7.2 Efficient Directory Traversal +```c +int fileops_find_pattern_optimized(const char* directory, const char* pattern, + file_list_t* results) { + DIR* dir = opendir(directory); + if (!dir) { + return -1; + } + + struct dirent* entry; + results->count = 0; + + // Pre-compile pattern matching criteria for efficiency + bool match_txt = strstr(pattern, "*.txt*") != NULL; + bool match_log = strstr(pattern, "*.log*") != NULL; + bool match_bin = strstr(pattern, "*.bin*") != NULL; + bool match_bootlog = strstr(pattern, "bootlog") != NULL; + + while ((entry = readdir(dir)) != NULL && results->count < results->capacity) { + // Quick checks first (most common rejects) + if (entry->d_name[0] == '.') { + continue; // Skip hidden files and . / .. + } + + bool matches = false; + const char* name = entry->d_name; + size_t name_len = strlen(name); + + // Optimized pattern matching + if (match_bootlog && name_len == 7 && strcmp(name, "bootlog") == 0) { + matches = true; + } else if (name_len >= 4) { // Minimum length for extensions + // Check extensions efficiently + if (match_txt && (strcasestr(name, ".txt") != NULL)) { + matches = true; + } else if (match_log && (strcasestr(name, ".log") != NULL)) { + matches = true; + } else if (match_bin && (strcasestr(name, ".bin") != NULL)) { + matches = true; + } + } + + if (matches) { + // Use pointer arithmetic for efficiency + snprintf(results->files[results->count].filename, MAX_PATH_LEN, "%s", name); + snprintf(results->files[results->count].source_path, MAX_PATH_LEN, + "%s/%s", directory, name); + results->count++; + } + } + + closedir(dir); + return results->count; +} +``` + +## 8. Resource Management + +### 8.1 Resource Cleanup Framework +```c +#define MAX_CLEANUP_HANDLERS 16 + +typedef struct cleanup_handler { + void (*cleanup_func)(void*); + void* resource; + bool in_use; +} cleanup_handler_t; + +static cleanup_handler_t g_cleanup_handlers[MAX_CLEANUP_HANDLERS]; + +int register_cleanup(void (*cleanup_func)(void*), void* resource) { + int i; + + if (cleanup_func == NULL) { + return -1; + } + + for (i = 0; i < MAX_CLEANUP_HANDLERS; ++i) { + if (!g_cleanup_handlers[i].in_use) { + g_cleanup_handlers[i].cleanup_func = cleanup_func; + g_cleanup_handlers[i].resource = resource; + g_cleanup_handlers[i].in_use = true; + return 0; + } + } + + /* No free slot available */ + return -1; +} + +void execute_all_cleanup(void) { + int i; + + for (i = 0; i < MAX_CLEANUP_HANDLERS; ++i) { + if (g_cleanup_handlers[i].in_use && g_cleanup_handlers[i].cleanup_func != NULL) { + g_cleanup_handlers[i].cleanup_func(g_cleanup_handlers[i].resource); + g_cleanup_handlers[i].cleanup_func = NULL; + g_cleanup_handlers[i].resource = NULL; + g_cleanup_handlers[i].in_use = false; + } + } +} + +// Signal handler for graceful shutdown +void signal_handler(int sig) { + LOG_INFO("Received signal %d, cleaning up resources", sig); + execute_all_cleanup(); + exit(sig); +} +``` + +### 8.2 File Descriptor Management +```c +#define MAX_OPEN_FILES 64 + +static struct { + FILE* handles[MAX_OPEN_FILES]; + char paths[MAX_OPEN_FILES][MAX_PATH_LEN]; + int count; +} g_file_registry = {0}; + +FILE* managed_fopen(const char* path, const char* mode) { + if (g_file_registry.count >= MAX_OPEN_FILES) { + LOG_ERROR(NULL, ERROR_RESOURCE, "Too many open files"); + return NULL; + } + + FILE* fp = fopen(path, mode); + if (fp) { + g_file_registry.handles[g_file_registry.count] = fp; + strncpy(g_file_registry.paths[g_file_registry.count], path, MAX_PATH_LEN - 1); + g_file_registry.count++; + } + + return fp; +} + +void managed_fclose_all(void) { + for (int i = 0; i < g_file_registry.count; i++) { + if (g_file_registry.handles[i]) { + fclose(g_file_registry.handles[i]); + g_file_registry.handles[i] = NULL; + } + } + g_file_registry.count = 0; +} +``` + +## 9. Main Program Structure + +### 9.1 Main Function Implementation +```c +int main(int argc, char* argv[]) { + backup_config_t config = {0}; + error_context_t error_ctx = {0}; + int exit_code = ERROR_SUCCESS; + + // Setup signal handlers for graceful shutdown + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + do { + // Initialize all subsystems + if (logger_init("backup_logs") != 0) { + fprintf(stderr, "Failed to initialize logging system\n"); + exit_code = ERROR_SYSTEM; + break; + } + + if (config_init() != 0) { + LOG_ERROR(&error_ctx, ERROR_SYSTEM, "Failed to initialize configuration system"); + exit_code = ERROR_SYSTEM; + break; + } + + // Load and validate configuration + if (config_load(&config) != 0) { + LOG_ERROR(&error_ctx, ERROR_CONFIG_INVALID, "Failed to load configuration"); + exit_code = ERROR_CONFIG_INVALID; + break; + } + + if (config_validate(&config) != 0) { + LOG_ERROR(&error_ctx, ERROR_CONFIG_INVALID, "Configuration validation failed"); + exit_code = ERROR_CONFIG_INVALID; + break; + } + + LOG_INFO("Configuration loaded successfully"); + + // Create workspace directories + if (dir_create_workspace(&config) != 0) { + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, "Failed to create workspace directories"); + exit_code = ERROR_FILESYSTEM; + break; + } + + // Check disk threshold + if (sysint_check_disk_threshold() != 0) { + LOG_WARN("Disk threshold check failed or reported issues"); + // Continue execution - not a fatal error + } + + // Attempt recovery from any partial state + if (backup_recover_from_partial_state(&config) != 0) { + LOG_WARN("Partial state recovery had issues"); + // Continue execution + } + + // Execute appropriate backup strategy + if (config.hdd_enabled) { + LOG_INFO("Executing HDD-enabled backup strategy"); + if (backup_execute_hdd_enabled(&config) != 0) { + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, "HDD-enabled backup failed"); + exit_code = ERROR_FILESYSTEM; + break; + } + } else { + LOG_INFO("Executing HDD-disabled backup strategy"); + if (backup_execute_hdd_disabled(&config) != 0) { + LOG_ERROR(&error_ctx, ERROR_FILESYSTEM, "HDD-disabled backup failed"); + exit_code = ERROR_FILESYSTEM; + break; + } + } + + // Clean current log directory + if (dir_cleanup(config.log_path, "*.txt*,*.log*,*-*-*-*-*M-") != 0) { + LOG_WARN("Failed to clean current log directory"); + // Continue - not fatal + } + + // Copy version files + if (backup_copy_version_files(&config) != 0) { + LOG_WARN("Failed to copy some version files"); + // Continue - not fatal + } + + // Handle special log files + if (backup_handle_special_files(&config) != 0) { + LOG_WARN("Failed to handle some special log files"); + // Continue - not fatal + } + + // Create persistent marker + if (sysint_create_persistent_marker(config.persistent_path) != 0) { + LOG_WARN("Failed to create persistent marker"); + // Continue - not fatal + } + + // Send systemd notification + if (sysint_notify_systemd("Logs Backup Done..!") != 0) { + LOG_WARN("Failed to send systemd notification"); + // Continue - not fatal + } + + LOG_INFO("Backup operation completed successfully"); + + } while (0); // Single execution with break-based error handling + + // Cleanup all resources + execute_all_cleanup(); + managed_fclose_all(); + config_cleanup(&config); + logger_cleanup(); + + // Log final status + if (exit_code != ERROR_SUCCESS) { + logger_error(&error_ctx); + } + + return exit_code; +} +``` + +This LLD provides comprehensive implementation details for migrating the backup_logs.sh script to C, including detailed algorithms, data structures, error handling, and performance optimizations specifically designed for embedded RDK systems. diff --git a/backup_logs/docs/backup_logs_migration_HLD.md b/backup_logs/docs/backup_logs_migration_HLD.md new file mode 100644 index 000000000..4e23ee51c --- /dev/null +++ b/backup_logs/docs/backup_logs_migration_HLD.md @@ -0,0 +1,634 @@ +# High-Level Design: backup_logs.sh Migration to C + +## 1. Overview + +### 1.1 Purpose +This document outlines the High-Level Design (HLD) for migrating the existing `backup_logs.sh` shell script to a C implementation. The migration aims to improve performance, reduce memory footprint, and enhance reliability for embedded RDK systems. + +### 1.2 Scope +- Migration of all functionality from `backup_logs.sh` to C code +- Support for both HDD-enabled and HDD-disabled devices +- Maintain compatibility with existing systemd integration +- Preserve log backup and rotation functionality + +### 1.3 Constraints +- Target embedded systems with limited memory (few KBs to few MBs) +- CPU resources are constrained with low clock speeds +- Must be platform-neutral and portable across multiple architectures +- Minimize dynamic memory allocation +- Avoid floating-point arithmetic where possible +- Thread-safe implementation required + +## 2. System Architecture + +### 2.1 Architecture Overview +The C implementation will follow a modular design with the following key components: + +``` +backup_logs (main executable) +├── Configuration Manager +├── Directory Manager +├── Log Backup Engine +├── File Operations Manager +├── Disk Threshold Monitor +├── System Integration Module +└── Error Handler & Logger +``` + +### 2.2 Component Description + +#### 2.2.1 Configuration Manager +- **Purpose**: Load and parse system configuration files +- **Responsibilities**: + - Parse `/etc/include.properties` + - Parse `/etc/device.properties` + - Parse `/etc/env_setup.sh` if available + - Parse `/etc/special_files.properties` for special files handling + - Validate configuration parameters + - Provide configuration data to other modules + - Load and manage special files configuration for `/tmp` and `/etc` operations + +#### 2.2.2 Directory Manager +- **Purpose**: Handle directory creation and validation +- **Responsibilities**: + - Create log workspace directories + - Validate directory permissions + - Manage directory path resolution + - Handle directory cleanup operations + +#### 2.2.3 Log Backup Engine +- **Purpose**: Core backup logic implementation +- **Responsibilities**: + - Implement HDD-enabled device backup strategy + - Implement HDD-disabled device backup strategy with rotation + - Handle log file identification and filtering + - Execute backup operations based on device type + +#### 2.2.4 File Operations Manager +- **Purpose**: Low-level file operations +- **Responsibilities**: + - File moving and copying operations + - File existence checking + - Pattern-based file finding + - Timestamp generation and management + +#### 2.2.5 Disk Threshold Monitor +- **Purpose**: Monitor disk usage and trigger cleanup +- **Responsibilities**: + - Check disk usage percentages + - Trigger cleanup scripts when thresholds exceed + - Integration with existing disk_threshold_check.sh + +#### 2.2.6 System Integration Module +- **Purpose**: System-level integrations +- **Responsibilities**: + - Systemd notification handling + - Integration with external scripts + - Process status reporting + +#### 2.2.7 Error Handler & Logger +- **Purpose**: Centralized error handling and logging +- **Responsibilities**: + - Structured error reporting + - Log message formatting with timestamps + - Error code standardization + +## 3. Data Structures + +### 3.1 Core Data Structures + +```c +typedef struct { + char log_path[PATH_MAX]; + char prev_log_path[PATH_MAX]; + char prev_log_backup_path[PATH_MAX]; + char persistent_path[PATH_MAX]; + bool hdd_enabled; +} backup_config_t; + +typedef enum { + BACKUP_OP_MOVE, + BACKUP_OP_COPY, + BACKUP_OP_DELETE +} backup_operation_type_t; + +typedef struct { + char source_path[PATH_MAX]; + char dest_path[PATH_MAX]; + backup_operation_type_t operation; + char source_extension[32]; + char dest_extension[32]; +} backup_operation_t; +typedef struct { + int error_code; + char error_message[256]; + const char* function_name; + int line_number; +} error_info_t; +``` + +### 3.3 Special Files Configuration + +#### 3.3.1 Special Files Data Structure +```c +typedef enum { + SPECIAL_FILE_COPY = 0, // cp operation + SPECIAL_FILE_MOVE = 1 // mv operation +} special_file_operation_t; + +typedef struct { + char source_path[PATH_MAX]; + char destination_path[PATH_MAX]; + special_file_operation_t operation; + char conditional_check[64]; // Optional condition variable name +} special_file_entry_t; + +typedef struct { + special_file_entry_t entries[MAX_SPECIAL_FILES]; + size_t count; + bool config_loaded; +} special_files_config_t; + +#define MAX_SPECIAL_FILES 32 // Maximum number of special files to handle +``` + +### 3.2 Memory Management Strategy +- Use fixed-size buffers to avoid dynamic allocation +- Implement memory pools for temporary operations +- Stack-based allocation for small, short-lived data +- Pre-allocated arrays for file lists and paths + +#### 3.3.2 Special Files Configuration Format +The special files configuration follows a simple one-filename-per-line format for embedded system efficiency: +```properties +# Special Files Configuration for backup_logs +# Format: One source file path per line +# Comments start with # and empty lines are ignored +# +# Operation determination is handled by the implementation: +# - Files in /tmp are typically moved (mv operation) +# - Configuration and version files are typically copied (cp operation) +# - Destination is automatically determined based on source filename + +# Files from /tmp directory (will be moved) +/tmp/disk_cleanup.log +/tmp/mount_log.txt +/tmp/mount-ta_log.txt + +# Files from /etc directory (will be copied) +/etc/skyversion.txt +/etc/rippleversion.txt + +# Version file from root (will be copied) +/version.txt +``` + +## 3.4 Special Files Configuration Management + +### 3.4.1 Configuration Loading +The special files configuration is loaded from `/etc/special_files.properties` using a simple line-based parser that: +- Supports one filename per line format with comments (lines starting with #) +- Automatically determines operation type based on source file location +- Automatically determines destination filename from source pathname +- Uses LOG_PATH from backup configuration for destination directory +- Provides error reporting for missing or invalid files + +### 3.4.2 Configuration Processing +- Files in `/tmp/` directory are moved (mv operation) to preserve space +- Configuration and version files are copied (cp operation) to preserve originals +- Destination directory is automatically set to the configured LOG_PATH +- Destination filename is extracted from the source file path +- Log warnings for entries with non-existent source files but continue processing + +## 4. Module Interfaces + +### 4.1 Configuration Manager Interface +```c +int config_load(backup_config_t* config); +int config_validate(const backup_config_t* config); +const char* config_get_log_path(void); +bool config_is_hdd_enabled(void); + +// Special files configuration interface +int special_files_load_config(special_files_config_t* config, const char* config_file); +int special_files_validate_entry(const special_file_entry_t* entry); +int special_files_execute_entry(const special_file_entry_t* entry, + const backup_config_t* backup_config); +int special_files_execute_all(const special_files_config_t* config, + const backup_config_t* backup_config); +void special_files_cleanup(void); +``` + +### 4.2 Directory Manager Interface +```c +// Implemented using RDK system utilities +int createDir(char* path); // Create directory if not exists +int emptyFolder(char* path); // Clean directory contents +int filePresentCheck(const char* path); // Check if file/directory exists +int removeFile(const char* path); // Remove file or directory +``` + +### 4.3 Log Backup Engine Interface +```c +int backup_execute_hdd_enabled_strategy(const backup_config_t* config); +int backup_execute_hdd_disabled_strategy(const backup_config_t* config); +int backup_execute_common_operations(const backup_config_t* config); +int move_log_files_by_pattern(const char* source_dir, const char* dest_dir); +``` + +### 4.4 File Operations Interface +```c +// Implemented using RDK system utilities +int copyFiles(const char* source, const char* dest); // Copy file operation +int removeFile(const char* path); // Remove file operation +int filePresentCheck(const char* path); // Check file existence +// Move is implemented as copy + remove sequence +``` + +## 5. Data Flow + +### 5.1 Main Execution Flow (As Implemented) +1. **Initialization Phase** + - Initialize RDK logging system with extended configuration + - Load system configuration from RDK property APIs (`getIncludePropertyData`, `getDevicePropertyData`) + - Create required directories: LOG_PATH, PREV_LOG_PATH, PREV_LOG_BACKUP_PATH + - Clean PREV_LOG_BACKUP_PATH directory + - Create persistent file marker (`logFileBackup`) + - Run disk threshold check script if available + +2. **Pre-Execution Phase** + - Find and remove existing `last_reboot` marker files + - Determine backup strategy based on HDD_ENABLED configuration + +3. **Backup Execution Phase** + - Execute appropriate backup strategy (HDD-enabled or HDD-disabled) + - Move log files using pattern matching (*.txt*, *.log*, bootlog) + - Handle log rotation for HDD-disabled devices + +4. **Common Operations Phase** + - Execute special files operations based on configuration + - Copy system version files (skyversion.txt, rippleversion.txt, version.txt) + - Create new `last_reboot` marker file + - Send systemd notification if available + +5. **Cleanup Phase** + - Cleanup special files manager resources + - Release all allocated resources + - Report final execution status + +### 5.2 Error Handling Flow +- Centralized error handling through error_info_t structure +- Error propagation through return codes +- Logging of all error conditions with context +- Graceful degradation on non-critical failures + +### 5.3 Visual Flow Representation + +The system workflow is represented through detailed diagrams stored in the [diagrams directory](diagrams/backup_logs_flowcharts.md). This includes both Mermaid-format diagrams and text-based alternatives for environments with limited diagram rendering capabilities. + +#### 5.3.1 Main Backup Process Flow +The primary execution flow showing the complete backup process from initialization to completion, including decision points for HDD-enabled vs HDD-disabled devices. Available in both Mermaid and text formats. + +#### 5.3.2 Component Interaction Sequence +A detailed sequence diagram illustrating the interactions between all system components, showing the order of operations and data flow between modules. Includes timing annotations and error paths. + +#### 5.3.3 HDD Disabled Strategy Detail +A specialized flowchart focusing on the complex log rotation logic for HDD-disabled devices, showing the 4-level backup rotation mechanism with all decision points clearly marked. + +#### 5.3.4 Error Handling and Recovery Flow +A comprehensive error handling flowchart showing how different types of errors are categorized, handled, and recovered from in the system. Includes recovery strategies and escalation paths. + +## 6. Key Algorithms + +### 6.1 HDD-Disabled Backup Algorithm +``` +1. Check for existing backup levels (messages.txt, bak1_messages.txt, etc.) +2. If no existing backups: + - Move all logs to PreviousLogs +3. If backup level 1 exists but not level 2: + - Move current logs to PreviousLogs with bak1_ prefix +4. If backup levels 1-2 exist but not level 3: + - Move current logs to PreviousLogs with bak2_ prefix +5. If all backup levels exist: + - Rotate: bak1->root, bak2->bak1, bak3->bak2, current->bak3 +6. Create last_reboot marker file +``` + +### 6.2 HDD-Enabled Backup Algorithm +``` +1. If no messages.txt in PreviousLogs: + - Move all logs to PreviousLogs + - Create last_reboot marker +2. If messages.txt exists: + - Remove existing last_reboot markers + - Create timestamped backup directory + - Move current logs to timestamped directory + - Create last_reboot marker in timestamped directory +``` + +### 6.3 File Pattern Matching Algorithm +- Use POSIX-compliant pattern matching +- Support for wildcard patterns (*.txt, *.log, etc.) +- Efficient directory traversal with depth control +- Filter by file type (regular files vs. symbolic links) + +### 6.5 Special Files Processing Algorithm +``` +1. Load special files configuration from /etc/special_files.properties +2. Parse each line as a single source file path +3. Skip comment lines (starting with #) and empty lines +4. For each special file entry: + a. Check if source file exists (log warning if not found, continue) + b. Determine operation based on source path: + - Files in /tmp/: move operation (copy + delete) + - All other files: copy operation + c. Extract filename from source path for destination + d. Build full destination path using ${LOG_PATH}/filename + e. Verify destination directory exists, create if needed + f. Execute operation (copy or move) based on determination + g. Log operation result and any errors +5. Update operation statistics and cleanup resources +``` + +### 6.6 Special Files Configuration Parser Algorithm +``` +1. Open /etc/special_files.properties file +2. For each line: + a. Skip empty lines and comments (lines starting with #) + b. Trim whitespace and newline characters + c. Store entire line as source_path in special_file_entry_t structure + d. Extract filename from source path for destination_path + e. Set default operation to SPECIAL_FILE_COPY (will be determined at execution) + f. Set conditional_check to empty string +3. Return parsed configuration with entry count or error code +``` + +# Additional Data Structures + +### 3.5 Enhanced Error Handling and Configuration Flags + +```c +/* Complete error code enumeration */ +typedef enum { + BACKUP_SUCCESS = 0, + BACKUP_ERROR_CONFIG = -1, + BACKUP_ERROR_FILESYSTEM = -2, + BACKUP_ERROR_PERMISSIONS = -3, + BACKUP_ERROR_MEMORY = -4, + BACKUP_ERROR_INVALID_PARAM = -5, + BACKUP_ERROR_NOT_FOUND = -6, + BACKUP_ERROR_DISK_FULL = -7, + BACKUP_ERROR_SYSTEM = -8 +} backup_result_t; + +/* Configuration flags for advanced options */ +typedef struct { + bool debug_enabled; + bool force_rotation; + bool skip_disk_check; + bool cleanup_enabled; +} backup_flags_t; + +/* Additional constants */ +#define MAX_ERROR_MESSAGE_LEN 256 +#define MAX_FUNCTION_NAME_LEN 64 +#define MAX_DESCRIPTION_LEN 128 +#define MAX_CONDITIONAL_LEN 64 +#define LOG_BACKUP_LOGS "LOG.RDK.BACKUPLOGS" +``` +The special files processing is integrated into the main backup flow as follows: +- **Phase 1**: Load special files configuration during initialization +- **Phase 2**: Execute special file copy operations before log backup +- **Phase 3**: Execute special file move operations during cleanup phase +- **Phase 4**: Report special files operation status in final logging + +## 7. Threading and Concurrency + +### 7.1 Threading Strategy +- Single-threaded design for simplicity and reliability +- Thread-safe utility functions for potential future extensions +- Use of atomic operations for shared state (if any) + +### 7.2 Synchronization +- File locking for critical operations +- Mutex protection for shared resources (if threading is added later) +- Process-level coordination through lockfiles + +## 8. Performance Considerations + +### 8.1 Memory Optimization +- Fixed-size buffers with compile-time sizing +- Stack allocation preference over heap allocation +- Minimal memory fragmentation through planned allocation patterns +- Efficient string handling with bounded operations + +### 8.2 I/O Optimization +- Batch file operations where possible +- Minimize system calls through buffered operations +- Efficient directory traversal algorithms +- Streaming operations for large files + +## 8.5 RDK Logger Integration + +The implementation includes comprehensive RDK logging framework integration: + +### 8.5.1 Logger Configuration +```c +#ifdef RDK_LOGGER_EXT + rdk_logger_ext_config_t logger_config = { + .pModuleName = "LOG.RDK.BACKUPLOGS", + .loglevel = RDK_LOG_INFO, + .output = RDKLOG_OUTPUT_CONSOLE, + .format = RDKLOG_FORMAT_WITH_TS, + .pFilePolicy = NULL + }; + rdk_logger_ext_init(&logger_config); +#endif +``` + +### 8.5.2 Logger Features +- **Extended Logger Support**: Uses RDK_LOGGER_EXT for enhanced configuration +- **Timestamped Output**: All log messages include timestamps +- **Console Fallback**: Graceful handling when logger initialization fails +- **Debug INI Integration**: Reads configuration from `/etc/debug.ini` +- **Module-Specific Logging**: Uses dedicated "LOG.RDK.BACKUPLOGS" component + +### 8.5.3 Build-time Configuration +```c +#ifdef RDK_LOGGER_EXT +#define RDK_LOGGER_ENABLED +#endif +``` + +The system supports both basic RDK logger and extended RDK logger configurations through compile-time flags. +### 8.6 CPU Optimization +- Avoid expensive operations in loops +- Use bit operations for flags and states +- Minimize string operations and use const strings where possible +- Efficient pattern matching algorithms + +## 9. Integration Points + +### 9.1 RDK System Integration +- **RDK Property System**: Integration with `getIncludePropertyData()` and `getDevicePropertyData()` APIs +- **RDK Logger Framework**: Full support for RDK logging with extended configuration +- **RDK Firmware Utils**: Integration with `fwutils` library for system operations +- **Systemd Integration**: Maintain compatibility with existing service files +- **External Scripts**: Integration with `disk_threshold_check.sh` and other system scripts + +### 9.2 Configuration File Dependencies +- **Include Properties**: `/etc/include.properties` for LOG_PATH and other system paths +- **Device Properties**: `/etc/device.properties` for HDD_ENABLED and device-specific settings +- **Debug Configuration**: `/etc/debug.ini` for RDK logger configuration +- **Special Files**: `/etc/special_files.properties` for configurable file operations (one filename per line format) +- **Environment Setup**: `/etc/env_setup.sh` if available for additional environment variables + +### 9.3 Build System Dependencies +```makefile +# Required libraries and flags +backup_logs_LDADD = -lm -lrdkloggers -lfwutils -lsystemd +backup_logs_CPPFLAGS = -DRDK_LOGGER_EXT +``` + +**Required Dependencies**: +- `librdkloggers` - RDK logging framework +- `libfwutils` - RDK firmware utilities for configuration and system operations +- `libsystemd` - Systemd integration for service notifications +- `libm` - Math library for any mathematical operations + +### 9.4 Backward Compatibility +- Maintain existing directory structure and naming conventions +- Preserve log file formats and timestamps +- Keep existing environment variable usage +- Maintain compatibility with log analysis tools + +## 10. Error Handling Strategy + +### 10.1 Error Categories +- **Fatal Errors**: Configuration failures, permission issues +- **Recoverable Errors**: Individual file operation failures +- **Warnings**: Non-critical issues that don't prevent execution + +### 10.2 Error Reporting +- Structured error codes for programmatic handling +- Human-readable error messages for debugging +- Integration with existing logging infrastructure +- Syslog integration for system-level error reporting + +## 11. Testing Strategy + +### 11.1 Unit Testing +- Test individual modules in isolation +- Mock external dependencies (file system, system calls) +- Comprehensive error condition testing +- Memory leak detection and prevention +- Special files configuration parser testing with various input formats +- Special files operation testing with different file permissions and paths + +### 11.2 Integration Testing +- Test complete backup scenarios +- Verify compatibility with existing system +- Performance benchmarking against shell script +- Multi-platform validation +- Special files configuration end-to-end testing +- Variable substitution testing for different environment setups + +### 11.3 System Testing +- End-to-end functionality verification +- Stress testing with large log volumes +- Resource constraint testing +- Recovery testing after various failure scenarios + +## 12. Deployment Considerations + +### 12.1 Build System +- Integration with existing autotools configuration +- Cross-compilation support for multiple architectures +- Compiler optimization flags for embedded targets +- Static linking considerations for deployment +- **RDK-Specific Build Requirements**: + - RDK Logger framework integration (`-lrdkloggers`) + - RDK Firmware utilities integration (`-lfwutils`) + - Systemd integration (`-lsystemd`) + - Extended logger compile flag (`-DRDK_LOGGER_EXT`) + +### 12.2 Installation +- Backward-compatible installation process +- Service file updates for systemd integration +- Configuration migration support +- Rollback capability + +### 12.3 Monitoring +- Health check mechanisms +- Performance metrics collection +- Resource usage monitoring +- Integration with existing monitoring infrastructure + +## 13. Future Enhancements + +### 13.1 Planned Features +- Configuration hot-reloading capability +- Enhanced compression for archived logs +- Remote log backup capability +- Advanced filtering and retention policies + +### 13.2 Extensibility +- Plugin architecture for custom backup strategies +- Configurable backup policies +- API for external tools integration +- Event-driven architecture support + +## 14. Risk Analysis + +### 14.1 Technical Risks +- **Memory Management**: Risk of memory leaks in embedded environment +- **File System Operations**: Race conditions with concurrent access +- **Configuration Parsing**: Compatibility issues with shell variable expansion +- **Performance**: Potential performance regression compared to shell script + +### 14.2 Mitigation Strategies +- Comprehensive testing with memory analysis tools +- File locking and atomic operations for critical sections +- Robust configuration parsing with validation +- Performance benchmarking and optimization + +## 15. Success Criteria + +### 15.1 Functional Requirements +- ✅ Complete feature parity with existing shell script +- ✅ Support for both HDD-enabled and HDD-disabled devices +- ✅ Proper log rotation and backup functionality +- ✅ Integration with systemd and existing infrastructure + +### 15.2 Non-Functional Requirements +- ✅ Memory usage reduction of at least 20% compared to shell process +- ✅ Startup time improvement of at least 30% +- ✅ CPU usage reduction during backup operations +- ✅ Cross-platform compatibility across target embedded systems + +## 16. Implementation Status + +### 16.1 Completed Features +- ✅ Complete modular architecture with defined components +- ✅ Core backup strategies for HDD-enabled and HDD-disabled devices +- ✅ RDK Logger framework integration with extended configuration +- ✅ Configuration management using RDK property APIs +- ✅ Special files handling with simplified configuration format +- ✅ Error handling with comprehensive error codes +- ✅ Build system integration with RDK dependencies +- ✅ Pattern-based log file identification and movement +- ✅ Systemd integration and external script execution + +### 16.2 Current Limitations / Future Work +- ⚠️ **Command Line Options**: Function definitions exist but CLI parsing not implemented +- ⚠️ **Special Files Format**: Simplified one-filename-per-line format instead of full pipe-separated specification +- ⚠️ **Configuration Validation**: Basic validation implemented, could be enhanced +- ⚠️ **Advanced Configuration**: Variable substitution not implemented in special files + +### 16.3 Architecture Decisions Made +- **Configuration Format**: Chose simplicity over full pipe-separated format for embedded efficiency +- **RDK Integration**: Deep integration with RDK APIs rather than generic POSIX-only approach +- **Error Handling**: Comprehensive error codes with graceful degradation on non-critical failures +- **Logging**: Full RDK logger integration with extended configuration options + +This HLD provides a comprehensive roadmap for migrating the backup_logs.sh script to a robust, efficient C implementation suitable for embedded RDK environments. diff --git a/backup_logs/docs/backup_logs_requirements.md b/backup_logs/docs/backup_logs_requirements.md new file mode 100644 index 000000000..375ef5012 --- /dev/null +++ b/backup_logs/docs/backup_logs_requirements.md @@ -0,0 +1,327 @@ +# Functional Requirements: backup_logs.sh Migration + +## 1. Overview + +This document outlines the detailed functional requirements for migrating the `backup_logs.sh` shell script to a C implementation for embedded RDK systems. + +## 2. Functional Requirements + +### 2.1 Configuration Management (REQ-001) +**Description**: The system must load and parse configuration from RDK property APIs +**Requirements**: +- Use `getIncludePropertyData()` API to retrieve `LOG_PATH` configuration +- Use `getDevicePropertyData()` API to retrieve `HDD_ENABLED` and device-specific settings +- Use `APP_PERSISTENT_PATH` for persistent marker file location +- Construct derived paths: `$LOG_PATH/PreviousLogs`, `$LOG_PATH/PreviousLogs_backup` +- Validate all configuration parameters before proceeding +- Handle missing configuration gracefully with sensible defaults +- Integrate with RDK logging framework for configuration status + +**Input**: RDK property system APIs +**Output**: Structured `backup_config_t` configuration data +**Error Handling**: Log configuration errors using RDK logger and exit with appropriate error code + +### 2.2 Directory Management (REQ-002) +**Description**: Create and manage required log directory structures using RDK utilities +**Requirements**: +- Use `createDir()` function to create `$LOG_PATH` directory if it doesn't exist +- Create `$LOG_PATH/PreviousLogs` directory structure +- Create `$LOG_PATH/PreviousLogs_backup` directory structure +- Use `emptyFolder()` to clean existing backup directory contents before use +- Set appropriate permissions on created directories +- Handle directory creation failures gracefully with proper error reporting +- Use `filePresentCheck()` for directory existence validation + +**Input**: Configuration paths from RDK property system +**Output**: Created directory structures with proper permissions +**Constraints**: Must work with various filesystem types and embedded system constraints + +### 2.3 Disk Threshold Monitoring (REQ-003) +**Description**: Monitor disk usage and trigger cleanup when necessary +**Requirements**: +- Execute disk threshold check if `/lib/rdk/disk_threshold_check.sh` exists +- Pass parameter `0` to the disk check script +- Handle script execution failures without stopping backup process +- Log disk check results for monitoring + +**Input**: Disk check script path +**Output**: Disk status information +**Dependencies**: External `disk_threshold_check.sh` script + +### 2.4 HDD-Disabled Device Backup Strategy (REQ-004) +**Description**: Implement 4-level log rotation for devices without HDD +**Requirements**: +- Support up to 4 backup levels: base, bak1_, bak2_, bak3_ +- Move files based on existing backup level: + - Level 0: Move current logs to PreviousLogs + - Level 1: Move current logs with `bak1_` prefix + - Level 2: Move current logs with `bak2_` prefix + - Level 3: Rotate all levels (bak1→base, bak2→bak1, bak3→bak2, current→bak3) +- Create `last_reboot` marker file after each backup +- Clean current log directory after backup completion + +**Input**: Current log files and existing backup state +**Output**: Rotated backup files with appropriate naming +**File Patterns**: `*.txt*`, `*.log*`, `*.bin*`, `bootlog` files + +### 2.5 HDD-Enabled Device Backup Strategy (REQ-005) +**Description**: Implement timestamped backup for devices with HDD +**Requirements**: +- Check for existing `messages.txt` in PreviousLogs directory +- If no existing backup: Move all logs to PreviousLogs directory +- If backup exists: Create timestamped backup directory (`logbackup-MM-DD-YY-HH-MM-SSAM`) +- Move current logs to timestamped directory +- Remove any existing `last_reboot` markers before creating new one +- Create `last_reboot` marker in appropriate location + +**Input**: Current log files and existing backup state +**Output**: Timestamped backup directories with organized log files +**File Patterns**: `*.txt*`, `*.log*`, `bootlog` files (no `.bin*` files) + +### 2.6 File Operations (REQ-006) +**Description**: Perform reliable file and directory operations +**Requirements**: +- Move files with error handling and validation +- Support pattern-based file finding (find with depth and type constraints) +- Handle both regular files and symbolic links +- Implement atomic file operations where possible +- Validate file operations and report failures +- Support large numbers of files efficiently + +**Input**: Source and destination paths, file patterns +**Output**: Moved/copied files with status reporting +**Constraints**: Must handle filesystem limitations and permissions + +### 2.7 Version File Management (REQ-007) +**Description**: Copy system version information to log directory +**Requirements**: +- Copy `/version.txt` to current log directory +- Copy `/etc/skyversion.txt` to current log directory as `skyversion.txt` +- Copy `/etc/rippleversion.txt` to current log directory as `rippleversion.txt` +- Handle missing version files gracefully (non-fatal errors) +- Preserve file timestamps and permissions where possible + +**Input**: System version files +**Output**: Version files in log directory +**Error Handling**: Log warnings for missing files but continue execution + +### 2.8 Special Log File Handling (REQ-008) - Updated +**Description**: Handle special files using simplified configuration format +**Requirements**: +- Load special files configuration from `/etc/special_files.properties` (one filename per line) +- Support comment lines starting with `#` and empty line skipping +- Automatically determine operation based on source file location: + - Files in `/tmp/`: move operation (preserves space) + - All other files: copy operation (preserves originals) +- Extract destination filename from source path automatically +- Handle atomic file operations using `copyFiles()` and `removeFile()` utilities +- Continue execution if special files are missing (non-fatal) +- Destination directory is automatically set to `$LOG_PATH` + +**Configuration Format**: +``` +# Special files to handle (one per line) +/tmp/disk_cleanup.log +/tmp/mount_log.txt +/tmp/mount-ta_log.txt +/etc/skyversion.txt +/etc/rippleversion.txt +/version.txt +``` + +**Input**: Configuration file with one source path per line +**Output**: Special files moved/copied to log directory +**Timing**: Execute during common operations phase after main backup + +### 2.9 System Integration (REQ-009) +**Description**: Integrate with systemd and system services +**Requirements**: +- Send systemd ready notification upon completion +- Set systemd status message: "Logs Backup Done..!" +- Create persistent marker file at `$PERSISTENT_PATH/logFileBackup` +- Handle systemd notification failures gracefully +- Support operation in non-systemd environments + +**Input**: Completion status +**Output**: System notifications and marker files +**Dependencies**: systemd-notify command availability + +### 2.10 Logging and Monitoring (REQ-010) - Updated +**Description**: Provide comprehensive logging using RDK Logger framework +**Requirements**: +- Initialize RDK Logger with extended configuration if available +- Use `LOG.RDK.BACKUPLOGS` component name for all log messages +- Support different log levels (RDK_LOG_INFO, RDK_LOG_WARN, RDK_LOG_ERROR, RDK_LOG_DEBUG) +- Read logger configuration from `/etc/debug.ini` +- Include timestamped output format when extended logger is enabled +- Fallback to console output if RDK logger initialization fails +- Use `RDK_LOG()` macro for structured logging throughout application +- Log all major operations with appropriate detail level + +**RDK Logger Configuration**: +```c +rdk_logger_ext_config_t logger_config = { + .pModuleName = "LOG.RDK.BACKUPLOGS", + .loglevel = RDK_LOG_INFO, + .output = RDKLOG_OUTPUT_CONSOLE, + .format = RDKLOG_FORMAT_WITH_TS, + .pFilePolicy = NULL +}; +``` + +**Input**: Operation status and error conditions +**Output**: Structured log messages with RDK-compatible format +**Format**: Compatible with RDK logging standards and systemd journal + +## 3. Non-Functional Requirements + +### 3.1 Performance Requirements (NFR-001) +- Memory usage must be ≤ 512KB peak during operation +- Startup time must be ≤ 2 seconds on target hardware +- File operations must complete within 30 seconds for typical log volumes +- CPU usage should not exceed 10% during backup operations + +### 3.2 Reliability Requirements (NFR-002) +- System must handle unexpected shutdowns gracefully +- Backup operations must be atomic (complete or rollback) +- Must recover from partial backup states on restart +- Handle filesystem full conditions without data loss + +### 3.3 Portability Requirements (NFR-003) +- Support ARM, MIPS, and x86 architectures +- Compatible with various embedded Linux distributions +- Work with different filesystem types (ext4, JFFS2, UBIFS) +- Support cross-compilation toolchains + +### 3.4 Security Requirements (NFR-004) +- Validate all file paths to prevent directory traversal +- Handle file permissions correctly without privilege escalation +- Sanitize all inputs from configuration files +- Protect against symlink attacks during file operations + +## 4. Input/Output Specifications + +### 4.1 Inputs (Updated) +- **RDK Property System**: Configuration via `getIncludePropertyData()` and `getDevicePropertyData()` APIs +- **Log Files**: Files matching patterns `*.txt*`, `*.log*`, `bootlog` +- **Version Files**: `/version.txt`, `/etc/skyversion.txt`, `/etc/rippleversion.txt` +- **Special Files Config**: `/etc/special_files.properties` (one filename per line format) +- **Debug Configuration**: `/etc/debug.ini` for RDK logger setup + +### 4.2 Outputs +- **Backup Directories**: Organized log file backups with appropriate naming +- **Marker Files**: `last_reboot` markers for tracking backup cycles +- **System Notifications**: systemd ready notifications and status messages +- **Log Messages**: Timestamped operation logs for monitoring + +### 4.3 Error Codes (Updated) +- **0**: Success (BACKUP_SUCCESS) - All operations completed successfully +- **-1**: Configuration Error (BACKUP_ERROR_CONFIG) - Invalid or missing configuration +- **-2**: Filesystem Error (BACKUP_ERROR_FILESYSTEM) - Directory creation or file operation failure +- **-3**: Permission Error (BACKUP_ERROR_PERMISSIONS) - Insufficient permissions for required operations +- **-4**: Memory Error (BACKUP_ERROR_MEMORY) - Memory allocation failures +- **-5**: Invalid Parameter Error (BACKUP_ERROR_INVALID_PARAM) - Invalid function parameters +- **-6**: Not Found Error (BACKUP_ERROR_NOT_FOUND) - Required files or directories not found +- **-7**: Disk Full Error (BACKUP_ERROR_DISK_FULL) - Insufficient disk space +- **-8**: System Error (BACKUP_ERROR_SYSTEM) - External script or system call failure + +## 5. Dependencies + +### 5.1 RDK System Dependencies +- **RDK Logger Framework**: `librdkloggers` for comprehensive logging +- **RDK Firmware Utils**: `libfwutils` for configuration management and system operations +- **RDK Property APIs**: `getIncludePropertyData()`, `getDevicePropertyData()` for configuration +- **RDK System Utilities**: `createDir()`, `emptyFolder()`, `filePresentCheck()`, `copyFiles()`, `removeFile()` + +### 5.2 System Dependencies +- POSIX-compliant filesystem +- Standard C library (libc) +- systemd integration (`libsystemd`) for service notifications +- systemd-notify utility (optional) +- Math library (`libm`) for numerical operations + +### 5.3 Build Dependencies +```makefile +# Required libraries and flags +backup_logs_LDADD = -lm -lrdkloggers -lfwutils -lsystemd +backup_logs_CPPFLAGS = -DRDK_LOGGER_EXT +``` + +### 5.4 Configuration Dependencies +- `/etc/debug.ini` for RDK logger configuration +- `/etc/special_files.properties` for special files configuration (optional) +- RDK property system for LOG_PATH and HDD_ENABLED configuration +- Access to `/proc` filesystem for system information + +### 5.5 External Scripts +- `/lib/rdk/disk_threshold_check.sh` - Disk usage monitoring +- Configuration parsing utilities for shell variable format + +### 5.6 File System Requirements +- Write access to log directories +- Sufficient disk space for log rotation (minimum 2x current log size) +- Support for atomic file operations (rename) + +## 6. Constraints + +### 6.1 Timing Constraints +- Must complete within systemd service timeout (typically 90 seconds) +- Backup rotation should complete within 10 seconds for typical volumes +- Configuration loading must complete within 1 second + +### 6.2 Memory Constraints +- Peak memory usage limited to 512KB on embedded systems +- No dynamic memory allocation for file lists exceeding 100MB +- Stack usage limited to 64KB maximum depth + +### 6.3 Storage Constraints +- Must work with log directories up to 1GB in size +- Support up to 10,000 individual log files +- Handle filenames up to 255 characters (filesystem limit) + +## 7. Edge Cases and Error Scenarios + +### 7.1 Configuration Edge Cases +- Missing configuration files +- Malformed shell variable syntax +- Invalid path specifications +- Conflicting configuration values +- Unicode characters in paths + +### 7.2 Filesystem Edge Cases +- Disk full during backup operations +- Permission changes during execution +- Network filesystem disconnections +- Corrupted filesystem states +- Very large individual log files (>100MB) + +### 7.3 System Edge Cases +- System shutdown during backup +- Multiple backup processes running simultaneously +- Clock adjustments affecting timestamps +- Filesystem readonly states +- Missing system utilities + +## 8. Acceptance Criteria + +### 8.1 Functional Acceptance +- [ ] All backup strategies work correctly for both HDD configurations +- [ ] Log rotation maintains proper sequence and naming +- [ ] Version file copying works without data loss +- [ ] System integrations (systemd) function properly +- [ ] Error handling provides useful diagnostic information + +### 8.2 Performance Acceptance +- [ ] Memory usage stays within embedded system constraints +- [ ] Startup and completion times meet target requirements +- [ ] File operations scale appropriately with log volume +- [ ] CPU usage remains reasonable during operation + +### 8.3 Reliability Acceptance +- [ ] Operations complete successfully in normal conditions +- [ ] System handles error conditions gracefully +- [ ] Recovery from partial states works correctly +- [ ] No data loss occurs during operations +- [ ] Cross-platform compatibility verified + +This requirements document provides the foundation for implementing a robust, efficient C replacement for the backup_logs.sh script that meets the needs of embedded RDK systems. diff --git a/backup_logs/docs/diagrams/backup_logs_flowcharts.md b/backup_logs/docs/diagrams/backup_logs_flowcharts.md new file mode 100644 index 000000000..7d605d42f --- /dev/null +++ b/backup_logs/docs/diagrams/backup_logs_flowcharts.md @@ -0,0 +1,522 @@ +# Backup Logs Migration - Flowcharts and Diagrams + +## Text-Based Flowchart Alternatives + +### 1. Main Backup Process Flow (Text Alternative) + +``` +START backup_logs + | + v +Initialize RDK Logger (Extended Config) + | + v +Logger Init Success? --> NO --> Fallback to Console Logging + | | + v YES v +Load Configuration from RDK APIs + | + v +getIncludePropertyData("LOG_PATH") + | + v +getDevicePropertyData("HDD_ENABLED") + | + v +Configuration Valid? --> NO --> Log Error & Exit --> END + | + v YES +Create Log Workspace (createDir) + | + v +Create Previous Log Directories (createDir) + | + v +Clean Backup Directory (emptyFolder) + | + v +Create Persistent Marker File + | + v +Check Disk Threshold (/lib/rdk/disk_threshold_check.sh) + | + v +Remove Existing last_reboot Markers + | + v +HDD Enabled? + | + +-- YES --> Execute HDD Enabled Strategy + | | + | v + | Check for Existing messages.txt + | | + | v + | messages.txt Exists? + | | + | +-- NO --> Move All Logs to Previous --> Create Last Reboot Marker + | | + | +-- YES --> Create Timestamped Directory + | | + | v + | Move Logs to Timestamped Dir + | | + | v + | Create Last Reboot Marker + | + +-- NO --> Execute HDD Disabled Strategy + | + v + Check Backup Levels + | + v + Which Level? + | + +-- Level 0 --> Move to Previous Logs --> Create Last Reboot Marker + | + +-- Level 1 --> Move with bak1_ prefix --> Create Last Reboot Marker + | + +-- Level 2 --> Move with bak2_ prefix --> Create Last Reboot Marker + | + +-- Level 3 --> Rotate All Backup Levels --> Create Last Reboot Marker + +All paths converge to: + | + v +Execute Common Operations + | + v +Load Special Files Config (/etc/special_files.properties) + | + v +Process Special Files (one filename per line) + | + v +Copy Version Files (skyversion.txt, rippleversion.txt, version.txt) + | + v +Send Systemd Notification + | + v +Cleanup Resources + | + v +END +``` + +### 2. HDD Disabled Strategy Detail (Text Alternative) + +``` +START HDD Disabled Strategy + | + v +Remove existing last_reboot marker + | + v +Check for messages.txt in Previous Logs + | + v +messages.txt exists? + | + +-- NO --> Find all *.txt, *.log, *.bin, bootlog files + | | + | v + | Move files from LOG_PATH to PREV_LOG_PATH + | | + | v + | Create last_reboot marker --> END STRATEGY + | + +-- YES --> Check for bak1_messages.txt + | + v + bak1_messages.txt exists? + | + +-- NO --> Move current logs with bak1_ prefix --> Create last_reboot marker --> END STRATEGY + | + +-- YES --> Check for bak2_messages.txt + | + v + bak2_messages.txt exists? + | + +-- NO --> Move current logs with bak2_ prefix --> Create last_reboot marker --> END STRATEGY + | + +-- YES --> Check for bak3_messages.txt + | + v + bak3_messages.txt exists? + | + +-- NO --> Move current logs with bak3_ prefix --> Create last_reboot marker --> END STRATEGY + | + +-- YES --> Start Rotation Process + | + v + Move bak1_ files to root names + | + v + Move bak2_ files to bak1_ names + | + v + Move bak3_ files to bak2_ names + | + v + Move current logs to bak3_ names + | + v + Create last_reboot marker --> END STRATEGY +``` + +### 3. Component Interaction Sequence (Text Alternative) + +``` +Main Process -> Logger: Initialize logging system +Main Process -> Config Manager: Load configuration files +Config Manager -> Config Manager: Parse /etc/include.properties +Config Manager -> Config Manager: Parse /etc/device.properties +Config Manager -> Config Manager: Parse /etc/env_setup.sh +Config Manager --> Main Process: Configuration data + +Main Process -> Directory Manager: Create log workspace directories +Directory Manager -> File Operations: Create directory if not exists +File Operations --> Directory Manager: Directory creation status +Directory Manager --> Main Process: Workspace ready + +Main Process -> Disk Monitor: Check disk threshold +Disk Monitor -> File Operations: Execute disk_threshold_check.sh +File Operations --> Disk Monitor: Threshold check result +Disk Monitor --> Main Process: Disk status + +Main Process -> Backup Engine: Execute backup strategy + +IF HDD Enabled Device: + Backup Engine -> File Operations: Check for existing messages.txt + File Operations --> Backup Engine: File existence result + + IF No existing backup: + Backup Engine -> File Operations: Move all logs to Previous + ELSE IF Existing backup found: + Backup Engine -> Directory Manager: Create timestamped directory + Backup Engine -> File Operations: Move logs to timestamped directory + +ELSE IF HDD Disabled Device: + Backup Engine -> File Operations: Check backup levels + File Operations --> Backup Engine: Current backup level + + IF Level 0-2: + Backup Engine -> File Operations: Move with appropriate prefix + ELSE IF Level 3: + Backup Engine -> File Operations: Rotate all backup levels + +Backup Engine -> File Operations: Create last_reboot marker +File Operations --> Backup Engine: Marker creation status +Backup Engine --> Main Process: Backup operation complete + +Main Process -> File Operations: Clean current log directory +Main Process -> File Operations: Copy version files +Main Process -> File Operations: Handle special log files + +Main Process -> System Integration: Send systemd notification +System Integration --> Main Process: Notification sent + +Main Process -> Logger: Log completion status +Logger --> Main Process: Logging complete +``` + +### 4. Special Files Processing Flow (Actual Implementation) + +``` +START Special Files Processing + | + v +Load /etc/special_files.properties + | + v +File Exists? + | + +-- NO --> Log Warning --> END (Non-fatal) + | + +-- YES --> Read File Line by Line + | + v + For Each Line: + | + v + Skip Comments (#) and Empty Lines + | + v + Extract Source Path (entire line) + | + v + Extract Destination Filename from Source Path + | + v + Determine Operation Based on Source Path: + | + +-- /tmp/* --> Move Operation (copyFiles + remove) + | + +-- Other --> Copy Operation (copyFiles only) + | + v + Check Source File Exists? + | + +-- NO --> Log Warning --> Continue Next File + | + +-- YES --> Build Destination Path (LOG_PATH + filename) + | + v + Execute Operation + | + v + Log Operation Result + | + v + Continue Next File + | + v + Process Complete + | + v + END Special Files Processing +``` + +``` +Function Call + | + v +Operation Successful? + | + +-- YES --> Return Success Code --> End + | + +-- NO --> Capture Error Context + | + v + Determine Error Severity + | + v + Error Type? + | + +-- Fatal --> Log Fatal Error + | | + | v + | Cleanup Resources + | | + | v + | Send Emergency Notification + | | + | v + | Exit Process --> End + | + +-- Recoverable --> Log Warning + | | + | v + | Log Error Details + | | + | v + | Attempt Recovery + | | + | v + | Recovery Successful? + | | + | +-- YES --> Continue Operation --> Return Success Code --> End + | | + | +-- NO --> Escalate to Critical + | | + | v + | Log Critical Error (see below) + | + +-- Critical System --> Log Critical Error + | + v + Log to Syslog + | + v + Notify System Monitor + | + v + Attempt Graceful Shutdown + | + v + Return Error Code --> End +``` + +## Mermaid Diagram Sources + +### Main Backup Process Flow (Mermaid) +```mermaid +flowchart TD + A[Start backup_logs] --> B[Initialize Logging] + B --> C[Load Configuration] + C --> D{Configuration Valid?} + D -->|No| E[Log Error & Exit] + D -->|Yes| F[Create Log Workspace] + F --> G[Create Previous Log Directories] + G --> H[Check Disk Threshold] + + H --> I{HDD Enabled?} + I -->|Yes| J[Execute HDD Enabled Strategy] + I -->|No| K[Execute HDD Disabled Strategy] + + J --> L[Check for Existing Backup] + L --> M{Backup Exists?} + M -->|No| N[Move All Logs to Previous] + M -->|Yes| O[Create Timestamped Directory] + O --> P[Move Logs to Timestamped Dir] + P --> Q[Create Last Reboot Marker] + + K --> R[Check Backup Levels] + R --> S{Which Level?} + S -->|Level 0| T[Move to Previous Logs] + S -->|Level 1| U[Move with bak1_ prefix] + S -->|Level 2| V[Move with bak2_ prefix] + S -->|Level 3| W[Rotate All Backup Levels] + + T --> X[Create Last Reboot Marker] + U --> X + V --> X + W --> X + N --> Q + Q --> X + + X --> Y[Clean Current Log Directory] + Y --> Z[Copy Version Files] + Z --> AA[Handle Special Log Files] + AA --> BB[Send Systemd Notification] + BB --> CC[End] + + E --> CC +``` + +### HDD Disabled Strategy Detail (Mermaid) +```mermaid +flowchart TD + A[Start HDD Disabled Strategy] --> B[Remove existing last_bootfile] + B --> C[Check for messages.txt in Previous Logs] + + C --> D{messages.txt exists?} + D -->|No| E[Find all *.txt, *.log, *.bin, bootlog files] + E --> F[Move files from LOG_PATH to PREV_LOG_PATH] + F --> G[Create last_reboot marker] + G --> Z[End Strategy] + + D -->|Yes| H[Check for bak1_messages.txt] + H --> I{bak1_messages.txt exists?} + I -->|No| J[Move current logs with bak1_ prefix] + J --> G + + I -->|Yes| K[Check for bak2_messages.txt] + K --> L{bak2_messages.txt exists?} + L -->|No| M[Move current logs with bak2_ prefix] + M --> G + + L -->|Yes| N[Check for bak3_messages.txt] + N --> O{bak3_messages.txt exists?} + O -->|No| P[Move current logs with bak3_ prefix] + P --> G + + O -->|Yes| Q[Start Rotation Process] + Q --> R[Move bak1_ files to root names] + R --> S[Move bak2_ files to bak1_ names] + S --> T[Move bak3_ files to bak2_ names] + T --> U[Move current logs to bak3_ names] + U --> G +``` + +### Component Interaction Sequence (Mermaid) +```mermaid +sequenceDiagram + participant Main as Main Process + participant Config as Configuration Manager + participant Dir as Directory Manager + participant Backup as Log Backup Engine + participant FileOps as File Operations Manager + participant Disk as Disk Threshold Monitor + participant SysInt as System Integration Module + participant Logger as Error Handler & Logger + + Main->>Logger: Initialize logging system + Main->>Config: Load configuration files + Config->>Config: Parse /etc/include.properties + Config->>Config: Parse /etc/device.properties + Config->>Config: Parse /etc/env_setup.sh + Config-->>Main: Configuration data + + Main->>Dir: Create log workspace directories + Dir->>FileOps: Create directory if not exists + FileOps-->>Dir: Directory creation status + Dir-->>Main: Workspace ready + + Main->>Disk: Check disk threshold + Disk->>FileOps: Execute disk_threshold_check.sh + FileOps-->>Disk: Threshold check result + Disk-->>Main: Disk status + + Main->>Backup: Execute backup strategy + + alt HDD Enabled Device + Backup->>FileOps: Check for existing messages.txt + FileOps-->>Backup: File existence result + alt No existing backup + Backup->>FileOps: Move all logs to Previous + else Existing backup found + Backup->>Dir: Create timestamped directory + Backup->>FileOps: Move logs to timestamped directory + end + else HDD Disabled Device + Backup->>FileOps: Check backup levels + FileOps-->>Backup: Current backup level + alt Level 0-2 + Backup->>FileOps: Move with appropriate prefix + else Level 3 + Backup->>FileOps: Rotate all backup levels + end + end + + Backup->>FileOps: Create last_reboot marker + FileOps-->>Backup: Marker creation status + Backup-->>Main: Backup operation complete + + Main->>FileOps: Clean current log directory + Main->>FileOps: Copy version files + Main->>FileOps: Handle special log files + + Main->>SysInt: Send systemd notification + SysInt-->>Main: Notification sent + + Main->>Logger: Log completion status + Logger-->>Main: Logging complete +``` + +### Error Handling and Recovery Flow (Mermaid) +```mermaid +flowchart TD + A[Function Call] --> B{Operation Successful?} + B -->|Yes| C[Return Success Code] + B -->|No| D[Capture Error Context] + + D --> E[Determine Error Severity] + E --> F{Error Type?} + + F -->|Fatal| G[Log Fatal Error] + F -->|Recoverable| H[Log Warning] + F -->|Critical System| I[Log Critical Error] + + G --> J[Cleanup Resources] + J --> K[Send Emergency Notification] + K --> L[Exit Process] + + H --> M[Log Error Details] + M --> N[Attempt Recovery] + N --> O{Recovery Successful?} + O -->|Yes| P[Continue Operation] + O -->|No| Q[Escalate to Critical] + Q --> I + + I --> R[Log to Syslog] + R --> S[Notify System Monitor] + S --> T[Attempt Graceful Shutdown] + T --> U[Return Error Code] + + P --> C + C --> V[End] + L --> V + U --> V +``` diff --git a/dcm_parseconf.c b/dcm_parseconf.c index 75f736f34..a2adbd5b4 100755 --- a/dcm_parseconf.c +++ b/dcm_parseconf.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "dcm_types.h" @@ -67,6 +68,10 @@ static INT32 dcmSettingGetValueFromFile(INT8 *buf, INT8 *file_path, buf[strcspn( buf, "\n" )] = 0; buf[strcspn( buf, "," )] = 0; tempStr = strstr( buf, delim ); + if(tempStr == NULL) { + DCMError("Delimiter '%s' not found in buffer\n", delim); + continue; + } tempStr++; if(tempStr[0] == '\"') @@ -247,6 +252,7 @@ static INT32 dcmSettingStoreTempConf(INT8 *pConffile, INT8 *pTempConf, INT8 *pOp INT32 i = 0; INT32 ret = DCM_SUCCESS; FILE *fp_out_opt = NULL; + size_t bytes_read = 0; FILE *fp_in = fopen(pConffile, "r"); if (fp_in == NULL) { @@ -262,14 +268,20 @@ static INT32 dcmSettingStoreTempConf(INT8 *pConffile, INT8 *pTempConf, INT8 *pOp } fp_out_opt = fopen(pOptConf, "w"); - if (fp_out == NULL) { + if (fp_out_opt == NULL) { ret = DCM_FAILURE; DCMError("Unable to open out file: %s\n", pOptConf); goto exit2; } fseek(fp_in, 0, SEEK_END); + errno = 0; file_len = ftell(fp_in); + if (file_len < 0) { + ret = DCM_FAILURE; + DCMError("Failed to get file size using ftell(): errno=%d (%s)\n", errno, strerror(errno)); + goto exit3; + } fseek(fp_in, 0, SEEK_SET); buff = calloc(file_len+1, 1); @@ -279,7 +291,15 @@ static INT32 dcmSettingStoreTempConf(INT8 *pConffile, INT8 *pTempConf, INT8 *pOp goto exit3; } - fread(buff, 1, file_len, fp_in); + + errno = 0; + bytes_read = fread(buff, 1, file_len, fp_in); + if (bytes_read != (size_t)file_len) { + ret = DCM_FAILURE; + DCMError("Failed to read the entire file. Expected %d bytes, got %zu bytes (errno=%d, %s, ferror=%d, feof=%d)\n", + file_len, bytes_read, errno, strerror(errno), ferror(fp_in), feof(fp_in)); + goto exit4; + } pJson = cJSON_Parse(buff); if (pJson == NULL) { @@ -356,32 +376,67 @@ static INT32 dcmSettingStoreTempConf(INT8 *pConffile, INT8 *pTempConf, INT8 *pOp fprintf(fp_out_opt, "\"%s\":\"%s\",", tprochitem->string, tprochitem->valuestring); } } - fseek(fp_out, -1, SEEK_CUR); - fseek(fp_out_opt, -1, SEEK_CUR); + errno = 0; + if (fseek(fp_out, -1, SEEK_CUR) != 0) { + ret = DCM_FAILURE; + DCMError("fseek failed on fp_out: errno=%d (%s)\n", errno, strerror(errno)); + goto exit4; + } + errno = 0; + if (fseek(fp_out_opt, -1, SEEK_CUR) != 0) { + ret = DCM_FAILURE; + DCMError("fseek failed on fp_out_opt: errno=%d (%s)\n", errno, strerror(errno)); + goto exit4; + } fprintf(fp_out, "},"); fprintf(fp_out_opt, "},"); } //for (k) - fseek(fp_out, -1, SEEK_CUR); - fseek(fp_out_opt, -1, SEEK_CUR); + errno = 0; + if (fseek(fp_out, -1, SEEK_CUR) != 0) { + ret = DCM_FAILURE; + DCMError("fseek failed on fp_out: errno=%d (%s)\n", errno, strerror(errno)); + goto exit4; + } + errno = 0; + if (fseek(fp_out_opt, -1, SEEK_CUR) != 0) { + ret = DCM_FAILURE; + DCMError("fseek failed on fp_out_opt: errno=%d (%s)\n", errno, strerror(errno)); + goto exit4; + } fprintf(fp_out, "],"); fprintf(fp_out_opt, "],"); } //else if(cJSON_IsArray(titem)) } //for (j) - fseek(fp_out, -1, SEEK_CUR); - fseek(fp_out_opt, -1, SEEK_CUR); + errno = 0; + if (fseek(fp_out, -1, SEEK_CUR) != 0) { + ret = DCM_FAILURE; + DCMError("fseek failed on fp_out: errno=%d (%s)\n", errno, strerror(errno)); + goto exit4; + } + errno = 0; + if (fseek(fp_out_opt, -1, SEEK_CUR) != 0) { + ret = DCM_FAILURE; + DCMError("fseek failed on fp_out_opt: errno=%d (%s)\n", errno, strerror(errno)); + goto exit4; + } fprintf(fp_out, "}\n"); fprintf(fp_out_opt, "}\n"); } //else if (cJSON_IsObject(item)) } //for (i) - cJSON_Delete(pJson); exit4: + if (pJson) { + cJSON_Delete(pJson); + pJson = NULL; + } free(buff); exit3: - fclose(fp_out_opt); + if (fp_out_opt) { + fclose(fp_out_opt); + } exit2: fclose(fp_out); exit1: diff --git a/dcm_rbus.c b/dcm_rbus.c index e487dd457..6953b15af 100644 --- a/dcm_rbus.c +++ b/dcm_rbus.c @@ -89,11 +89,16 @@ static VOID rbusSetConf(rbusHandle_t handle, if(configPath) { const INT8 *filePath = rbusValue_GetString(configPath, NULL); - strcpy(pDCMRbusHandle->confPath, filePath); - DCMInfo("configPath: %s\n", filePath); + if(filePath != NULL) { + strncpy(pDCMRbusHandle->confPath, filePath, DCM_CONF_SIZE - 1); + pDCMRbusHandle->confPath[DCM_CONF_SIZE - 1] = '\0'; + DCMInfo("configPath: %s\n", filePath); + } else { + DCMError("configPath value is NULL or invalid\n"); + } } - DCMInfo("Recieved eventName: %s, Event type: %d, Event Name: %s\n", subscription->eventName, event->type, event->name); + DCMInfo("Received eventName: %s, Event type: %d, Event Name: %s\n", subscription->eventName, event->type, event->name); } @@ -129,7 +134,7 @@ static VOID rbusProcConf(rbusHandle_t handle, return; } - DCMInfo("Recieved eventName: %s, Event type: %d, Event Name: %s\n", subscription->eventName, event->type, event->name); + DCMInfo("Received eventName: %s, Event type: %d, Event Name: %s\n", subscription->eventName, event->type, event->name); pDCMRbusHandle->schedJob = 1; } diff --git a/dcm_schedjob.c b/dcm_schedjob.c index 51347e59b..4501afde0 100644 --- a/dcm_schedjob.c +++ b/dcm_schedjob.c @@ -52,13 +52,32 @@ void* dcmSchedulerThread(void *arg) struct timespec _now; time_t timeOffset, currentTime; - while(!pDCMSched->terminated) { + while(1) { pthread_mutex_lock(&pDCMSched->tMutex); - if(!pDCMSched->startSched) { + // Check termination condition while holding the lock + if(pDCMSched->terminated) { + pthread_mutex_unlock(&pDCMSched->tMutex); + break; + } + + // Wait for scheduling to start - use proper loop for spurious wakeups + while(!pDCMSched->startSched && !pDCMSched->terminated) { n = pthread_cond_wait(&pDCMSched->tCond, &pDCMSched->tMutex); + if(n != 0) { + DCMWarn("%s pthread_cond_wait failed: %d (%s)\n", pDCMSched->name, n, strerror(n)); + pthread_mutex_unlock(&pDCMSched->tMutex); + goto thread_exit; + } } - else { + + // Check termination again after wait + if(pDCMSched->terminated) { + pthread_mutex_unlock(&pDCMSched->tMutex); + break; + } + + if(pDCMSched->startSched) { memset(&_now, 0, sizeof(struct timespec)); clock_gettime(CLOCK_REALTIME, &_now); @@ -67,7 +86,25 @@ void* dcmSchedulerThread(void *arg) timeOffset = dcmCronParseGetNext(&pDCMSched->parseData, currentTime); _now.tv_sec += (timeOffset - currentTime); - n = pthread_cond_timedwait(&pDCMSched->tCond, &pDCMSched->tMutex, &_now); + // Wait with predicate re-check under lock to handle spurious wakeups + while(pDCMSched->startSched && !pDCMSched->terminated) { + n = pthread_cond_timedwait(&pDCMSched->tCond, &pDCMSched->tMutex, &_now); + + if(n == ETIMEDOUT) { + break; + } + + if(n != 0) { + DCMWarn("%s pthread_cond_timedwait failed: %d (%s)\n", pDCMSched->name, n, strerror(n)); + pthread_mutex_unlock(&pDCMSched->tMutex); + goto thread_exit; + } + } + + if(pDCMSched->terminated) { + pthread_mutex_unlock(&pDCMSched->tMutex); + goto thread_exit; + } if(n == ETIMEDOUT) { DCMInfo("Scheduling %s Job handle: %p\n", pDCMSched->name, pDCMSched->pUserData); @@ -87,6 +124,7 @@ void* dcmSchedulerThread(void *arg) } pthread_mutex_unlock(&pDCMSched->tMutex); } +thread_exit: return NULL; } diff --git a/dcm_utils.c b/dcm_utils.c index 45e7665cb..a48e0d183 100644 --- a/dcm_utils.c +++ b/dcm_utils.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "dcm_types.h" #include "dcm_utils.h" @@ -96,9 +97,9 @@ VOID dcmUtilsCopyCommandOutput (INT8 *cmd, INT8 *out, INT32 len) if (fp) { if(out) { if (fgets (out, len, fp) != NULL) { - size_t len = strlen (out); - if ((len > 0) && (out[len - 1] == '\n')) - out[len - 1] = 0; + size_t str_len = strlen (out); + if ((str_len > 0) && (out[str_len - 1] == '\n')) + out[str_len - 1] = 0; } } pclose (fp); @@ -185,7 +186,13 @@ VOID dcmUtilsRemovePIDfile() fp = fopen(DCM_PID_FILE, "r"); if(fp) { fclose(fp); - remove(DCM_PID_FILE); + errno = 0; + if (remove(DCM_PID_FILE) != 0) { + if (errno != ENOENT) { + DCMError("Failed to remove PID file: %s errno=%d (%s)\n", + DCM_PID_FILE, errno, strerror(errno)); + } + } } } diff --git a/uploadstblogs/src/archive_manager.c b/uploadstblogs/src/archive_manager.c index b6357c8e3..28f736bf4 100755 --- a/uploadstblogs/src/archive_manager.c +++ b/uploadstblogs/src/archive_manager.c @@ -700,7 +700,7 @@ static int create_archive_with_options(RuntimeContext* ctx, SessionState* sessio RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, "[%s:%d] Creating archive with MAC='%s', prefix='%s'\n", __FUNCTION__, __LINE__, - ctx->mac_address ? ctx->mac_address : "(NULL)", + (ctx->mac_address[0] != '\0') ? ctx->mac_address : "(NULL)", prefix); char archive_filename[MAX_FILENAME_LENGTH]; @@ -736,10 +736,36 @@ static int create_archive_with_options(RuntimeContext* ctx, SessionState* sessio // Write two 512-byte blocks of zeros (TAR EOF marker) char eof_blocks[TAR_BLOCK_SIZE * 2]; memset(eof_blocks, 0, sizeof(eof_blocks)); - gzwrite(gz, eof_blocks, sizeof(eof_blocks)); + if (gzwrite(gz, eof_blocks, sizeof(eof_blocks)) != sizeof(eof_blocks)) { + int zerr = Z_OK; + const char* zmsg = gzerror(gz, &zerr); + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, + "[%s:%d] gzwrite failed to write EOF blocks (zerr=%d, msg=%s)\n", + __FUNCTION__, __LINE__, zerr, zmsg ? zmsg : "(null)"); + ret = -1; + } // Close gzip file - gzclose(gz); + int gzclose_ret = gzclose(gz); + if (gzclose_ret != Z_OK) { + const char* zmsg = zError(gzclose_ret); + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, + "[%s:%d] gzclose failed (zret=%d, msg=%s)\n", + __FUNCTION__, __LINE__, gzclose_ret, zmsg ? zmsg : "(null)"); + ret = -1; + } + + if (ret != 0 && file_exists(archive_path)) { + RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB, + "[%s:%d] Removing incomplete archive: %s\n", + __FUNCTION__, __LINE__, archive_path); + errno = 0; + if (!remove_file(archive_path)) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, + "[%s:%d] Failed to remove incomplete archive: %s (errno=%d, %s)\n", + __FUNCTION__, __LINE__, archive_path, errno, strerror(errno)); + } + } if (ret == 0 && file_exists(archive_path)) { long size = get_archive_size(archive_path); diff --git a/uploadstblogs/src/file_operations.c b/uploadstblogs/src/file_operations.c index 2bd70cf3b..b3eb9cad6 100755 --- a/uploadstblogs/src/file_operations.c +++ b/uploadstblogs/src/file_operations.c @@ -126,6 +126,7 @@ bool create_directory(const char* dirpath) if (createDir(path_copy) != RDK_API_SUCCESS) { RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] Failed to create directory %s\n", __FUNCTION__, __LINE__, path_copy); + // coverity[MISSING_RESTORE : FALSE] Restore is not needed because function returns immediately. return false; } } diff --git a/uploadstblogs/src/path_handler.c b/uploadstblogs/src/path_handler.c index ac81f305f..3172eab76 100755 --- a/uploadstblogs/src/path_handler.c +++ b/uploadstblogs/src/path_handler.c @@ -24,6 +24,7 @@ #include #include +#include #include "path_handler.h" #include "verification.h" #include "md5_utils.h" @@ -505,8 +506,27 @@ static UploadResult perform_s3_put_with_fallback(RuntimeContext* ctx, SessionSta FILE* curl_info = fopen("/tmp/logupload_curl_info", "r"); if (curl_info) { long http_code = 0; - fscanf(curl_info, "%ld", &http_code); - session->http_code = (int)http_code; + int scan_result; + errno = 0; + scan_result = fscanf(curl_info, "%ld", &http_code); + if (scan_result == 1) { + session->http_code = (int)http_code; + } else { + if (ferror(curl_info)) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, + "[%s:%d] Failed to read HTTP code from curl info file due to I/O error errno=%d (%s)\n", + __FUNCTION__, __LINE__, errno, strerror(errno)); + } else if (feof(curl_info)) { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, + "[%s:%d] Failed to read HTTP code from curl info file: unexpected EOF\n", + __FUNCTION__, __LINE__); + } else { + RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, + "[%s:%d] Failed to parse HTTP code from curl info file (scan_result=%d)\n", + __FUNCTION__, __LINE__, scan_result); + } + session->http_code = -1; + } fclose(curl_info); } session->curl_code = s3_result; diff --git a/uploadstblogs/src/strategies.c b/uploadstblogs/src/strategies.c index ca7e84f7c..1112d7d75 100755 --- a/uploadstblogs/src/strategies.c +++ b/uploadstblogs/src/strategies.c @@ -475,8 +475,8 @@ static int ondemand_archive(RuntimeContext* ctx, SessionState* session) "[%s:%d] Context before create_archive: ctx=%p, MAC='%s', device_type='%s'\n", __FUNCTION__, __LINE__, (void*)ctx, - ctx && ctx->mac_address ? ctx->mac_address : "(NULL/INVALID)", - (ctx && strlen(ctx->device_type) > 0) ? ctx->device_type : "(empty/NULL)"); + (ctx && ctx->mac_address[0] != '\0') ? ctx->mac_address : "(NULL/INVALID)", + (ctx && ctx->device_type[0] != '\0') ? ctx->device_type : "(empty/NULL)"); // Create archive from temp directory (NO timestamp modification) int ret = create_archive(ctx, session, ONDEMAND_TEMP_DIR); diff --git a/uploadstblogs/src/strategy_selector.c b/uploadstblogs/src/strategy_selector.c index b0a54077f..0b983838f 100755 --- a/uploadstblogs/src/strategy_selector.c +++ b/uploadstblogs/src/strategy_selector.c @@ -218,6 +218,7 @@ void decide_paths(const RuntimeContext* ctx, SessionState* session) // Direct blocked: CodeBig primary, no fallback else if (direct_blocked && !codebig_blocked) { session->primary = PATH_CODEBIG; + // coverity[copy_paste_error : FALSE] Intentional fallback is PATH_NONE when direct is blocked and CodeBig is primary. session->fallback = PATH_NONE; RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB, "[%s:%d] Paths: Primary=CODEBIG, Fallback=NONE (direct blocked)\n", diff --git a/uploadstblogs/unittest/mocks/mock_file_operations.cpp b/uploadstblogs/unittest/mocks/mock_file_operations.cpp index 4fd8eb098..83abb8aa1 100755 --- a/uploadstblogs/unittest/mocks/mock_file_operations.cpp +++ b/uploadstblogs/unittest/mocks/mock_file_operations.cpp @@ -64,6 +64,14 @@ bool copy_file(const char* src, const char* dest) { return true; } +bool remove_file(const char* filepath) { + if (g_mockFileOperations) { + return g_mockFileOperations->remove_file(filepath); + } + if (!filepath) return false; + return true; +} + void emit_system_validation_event(const char* component, bool success) { if (g_mockFileOperations) { g_mockFileOperations->emit_system_validation_event(component, success); diff --git a/uploadstblogs/unittest/mocks/mock_file_operations.h b/uploadstblogs/unittest/mocks/mock_file_operations.h index eedc4dddd..c30f2cc7e 100755 --- a/uploadstblogs/unittest/mocks/mock_file_operations.h +++ b/uploadstblogs/unittest/mocks/mock_file_operations.h @@ -31,6 +31,7 @@ bool file_exists(const char* filepath); bool dir_exists(const char* dirpath); bool create_directory(const char* dirpath); bool copy_file(const char* src, const char* dest); +bool remove_file(const char* filepath); void emit_system_validation_event(const char* component, bool success); void emit_folder_missing_error(void); int v_secure_system(const char* command, ...); @@ -47,6 +48,7 @@ class MockFileOperations { MOCK_METHOD1(dir_exists, bool(const char* dirpath)); MOCK_METHOD1(create_directory, bool(const char* dirpath)); MOCK_METHOD2(copy_file, bool(const char* src, const char* dest)); + MOCK_METHOD1(remove_file, bool(const char* filepath)); MOCK_METHOD2(emit_system_validation_event, void(const char* component, bool success)); MOCK_METHOD0(emit_folder_missing_error, void(void)); MOCK_METHOD1(v_secure_system, int(const char* command));