Skip to content

Conversation

@Abhinavpv28
Copy link
Contributor

No description provided.

Copilot AI review requested due to automatic review settings January 7, 2026 08:48
@Abhinavpv28 Abhinavpv28 requested a review from a team as a code owner January 7, 2026 08:48
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR simplifies the uploadSTBLogs library structure by flattening nested structures within RuntimeContext and consolidating related source files. The changes remove nested groupings (paths, device, settings, endpoints, flags, retry) in favor of direct field access, and merge several implementation files (strategies, log collection, cleanup) to reduce complexity.

Key changes:

  • Flattened RuntimeContext structure from nested groups to direct fields (e.g., ctx->paths.log_pathctx->log_path)
  • Consolidated strategy implementations from separate files (strategy_dcm.c, strategy_ondemand.c, strategy_reboot.c) into single strategies.c
  • Merged log_collector.c into archive_manager.c and cleanup_manager.c into cleanup_handler.c
  • Added new public API function uploadstblogs_run() for external component integration
  • Updated all test files to match the new flattened structure
  • Modified build system to create both library and binary

Reviewed changes

Copilot reviewed 44 out of 45 changed files in this pull request and generated no comments.

Show a summary per file
File Description
uploadstblogs/src/strategies.c New consolidated file combining DCM, Ondemand, and Reboot strategy implementations
uploadstblogs/src/uploadstblogs.c Added uploadstblogs_run() API and separated main() with conditional compilation
uploadstblogs/src/validation.c Added conditional check for rrd_flag when validating PREV_LOG_PATH
uploadstblogs/src/context_manager.c Updated all structure field accesses to use flattened RuntimeContext
uploadstblogs/src/archive_manager.c Merged log_collector.c functions into archive_manager.c
uploadstblogs/src/cleanup_handler.c Merged cleanup_manager.c functions into cleanup_handler.c
uploadstblogs/src/event_manager.c Added rrd_flag check in emit_no_logs_reboot()
uploadstblogs/unittest/*.cpp Updated all test files to use flattened RuntimeContext fields
uploadstblogs/src/Makefile.am Updated to build both library and binary, simplified binary build
uploadstblogs/include/uploadstblogs.h Added uploadstblogs_run() and uploadstblogs_execute() API documentation

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings January 7, 2026 12:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 45 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +85 to 99
if (ctx->rrd_flag == 0) {
// Check PREV_LOG_PATH - critical for upload (matches script behavior)
if (strlen(ctx->paths.prev_log_path) > 0) {
if (!dir_exists(ctx->paths.prev_log_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] The Previous Logs folder is missing: %s\n",
__FUNCTION__, __LINE__, ctx->paths.prev_log_path);
if (strlen(ctx->prev_log_path) > 0) {
if (!dir_exists(ctx->prev_log_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB, "[%s:%d] The Previous Logs folder is missing: %s\n",
__FUNCTION__, __LINE__, ctx->prev_log_path);
// Script sends MAINT_LOGUPLOAD_ERROR=5 when PREV_LOG_PATH is missing
emit_folder_missing_error();
all_valid = false;
} else {
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, "[%s:%d] PREV_LOG_PATH exists: %s\n",
__FUNCTION__, __LINE__, ctx->paths.prev_log_path);
emit_folder_missing_error();
all_valid = false;
} else {
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB, "[%s:%d] PREV_LOG_PATH exists: %s\n",
__FUNCTION__, __LINE__, ctx->prev_log_path);
}
}
}
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the validation.c file, the condition for checking PREV_LOG_PATH is now wrapped with if (ctx->rrd_flag == 0), but some paths within this block still use the old nested structure ctx->paths.prev_log_path instead of the flattened ctx->prev_log_path. This creates an inconsistency where the code won't compile correctly.

Copilot uses AI. Check for mistakes.
Comment on lines 88 to 567
if (!dir_exists(ctx->paths.prev_log_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] PREV_LOG_PATH does not exist: %s\n",
__FUNCTION__, __LINE__, ctx->paths.prev_log_path);
return -1;
}

if (!has_log_files(ctx->paths.prev_log_path)) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] No .txt or .log files in PREV_LOG_PATH, aborting\n", __FUNCTION__, __LINE__);
emit_no_logs_reboot(ctx);
return -1;
}

// Check system uptime and sleep if needed
// Script lines 818-836: if uptime < 900s, sleep 330s
double uptime_seconds = 0.0;
if (get_system_uptime(&uptime_seconds)) {
if (uptime_seconds < 900.0) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] System uptime %.0f seconds < 900s, sleeping for 330s\n",
__FUNCTION__, __LINE__, uptime_seconds);

// Script checks ENABLE_MAINTENANCE but both paths result in 330s sleep
// For simplicity, just sleep (background job with wait has same effect)
#ifndef L2_TEST_ENABLED
sleep(330);
#endif

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Done sleeping\n", __FUNCTION__, __LINE__);
} else {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Device uptime %.0f seconds >= 900s, skipping sleep\n",
__FUNCTION__, __LINE__, uptime_seconds);
}
} else {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to get system uptime, skipping sleep\n",
__FUNCTION__, __LINE__);
}

// Delete old backup files (3+ days old)
// Remove old timestamp directories and logbackup directories
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Cleaning old backups (3+ days)\n", __FUNCTION__, __LINE__);

int removed = remove_old_directories(ctx->paths.log_path, "*-*-*-*-*M-", 3);
if (removed > 0) {
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB,
"[%s:%d] Removed %d old timestamp directories\n",
__FUNCTION__, __LINE__, removed);
}

removed = remove_old_directories(ctx->paths.log_path, "*-*-*-*-*M-logbackup", 3);
if (removed > 0) {
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB,
"[%s:%d] Removed %d old logbackup directories\n",
__FUNCTION__, __LINE__, removed);
}

// Create timestamp for permanent log path
char timestamp[64];
time_t now = time(NULL);
struct tm* tm_info = localtime(&now);
strftime(timestamp, sizeof(timestamp), "%m-%d-%y-%I-%M%p-logbackup", tm_info);

char perm_log_path[MAX_PATH_LENGTH];
int written = snprintf(perm_log_path, sizeof(perm_log_path), "%s/%s",
ctx->paths.log_path, timestamp);

if (written >= (int)sizeof(perm_log_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Permanent log path too long\n", __FUNCTION__, __LINE__);
return -1;
}

// Store for use in cleanup phase
strncpy(perm_log_path_storage, perm_log_path, sizeof(perm_log_path_storage) - 1);
perm_log_path_storage[sizeof(perm_log_path_storage) - 1] = '\0';

// Log to lastlog_path
char lastlog_path_file[MAX_PATH_LENGTH];
written = snprintf(lastlog_path_file, sizeof(lastlog_path_file), "%s/lastlog_path",
ctx->paths.telemetry_path);

if (written >= (int)sizeof(lastlog_path_file)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Lastlog path file too long\n", __FUNCTION__, __LINE__);
return -1;
}

FILE* fp = fopen(lastlog_path_file, "a");
if (fp) {
fprintf(fp, "%s\n", perm_log_path);
fclose(fp);
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB,
"[%s:%d] Logged to lastlog_path: %s\n",
__FUNCTION__, __LINE__, perm_log_path);
}

// Delete old tar file if exists
char old_tar[MAX_PATH_LENGTH];
written = snprintf(old_tar, sizeof(old_tar), "%s/logs.tar.gz", ctx->paths.prev_log_path);

if (written >= (int)sizeof(old_tar)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Old tar path too long\n", __FUNCTION__, __LINE__);
return -1;
}

if (file_exists(old_tar)) {
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB,
"[%s:%d] Removing old tar file: %s\n",
__FUNCTION__, __LINE__, old_tar);
remove_file(old_tar);
}

// Add timestamps to all files in PREV_LOG_PATH
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Adding timestamps to files in PREV_LOG_PATH\n",
__FUNCTION__, __LINE__);

int ret = add_timestamp_to_files(ctx->paths.prev_log_path);
if (ret != 0) {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to add timestamps to some files\n",
__FUNCTION__, __LINE__);
// Continue anyway, not critical
}

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Setup phase complete\n", __FUNCTION__, __LINE__);

return 0;
}

/**
* @brief Archive phase for REBOOT/NON_DCM strategy
*
* Shell script equivalent (uploadLogOnReboot lines 853-869):
* - Collect PCAP files to PREV_LOG_PATH if mediaclient
* - Create tar.gz archive from PREV_LOG_PATH
* - Sleep 60 seconds
*/
static int reboot_archive(RuntimeContext* ctx, SessionState* session)
{
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Starting archive phase\n", __FUNCTION__, __LINE__);

// Collect PCAP files directly to PREV_LOG_PATH if mediaclient
if (ctx->settings.include_pcap) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Collecting PCAP file to PREV_LOG_PATH\n", __FUNCTION__, __LINE__);
int count = collect_pcap_logs(ctx, ctx->paths.prev_log_path);
if (count > 0) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Collected %d PCAP file\n", __FUNCTION__, __LINE__, count);
}
}

// Create archive from PREV_LOG_PATH (files already have timestamps)
int ret = create_archive(ctx, session, ctx->paths.prev_log_path);
if (ret != 0) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Failed to create archive\n", __FUNCTION__, __LINE__);
return -1;
}
#ifndef L2_TEST_ENABLED
sleep(60);
#endif

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Archive phase complete\n", __FUNCTION__, __LINE__);

return 0;
}

/**
* @brief Upload phase for REBOOT/NON_DCM strategy
*
* Shell script equivalent (uploadLogOnReboot lines 853-890):
* - Check reboot reason and RFC settings
* - Upload main logs if allowed
* - Upload DRI logs if directory exists
* - Clear old packet captures
*/
static int reboot_upload(RuntimeContext* ctx, SessionState* session)
{
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Starting upload phase\n", __FUNCTION__, __LINE__);

// Check reboot reason and RFC settings (matches script logic)
// Script: if [ "$uploadLog" == "true" ] || [ -z "$reboot_reason" -a "$DISABLE_UPLOAD_LOGS_UNSHEDULED_REBOOT" == "false" ]
// Note: When DCM_FLAG=0 (Non-DCM), script ALWAYS passes "true" regardless of UploadOnReboot value
// When DCM_FLAG=1 (DCM mode), upload_on_reboot determines the behavior
bool should_upload = false;
const char* reboot_info_path = "/opt/secure/reboot/previousreboot.info";

// Non-DCM mode (DCM_FLAG=0): Always upload (script line 999: uploadLogOnReboot true)
if (ctx->flags.dcm_flag == 0) {
should_upload = true;
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Non-DCM mode (dcm_flag=0), will always upload logs\n",
__FUNCTION__, __LINE__);
}
// DCM mode (DCM_FLAG=1): Check upload_on_reboot flag
else if (ctx->flags.upload_on_reboot) {
should_upload = true;
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] DCM mode: Upload enabled from settings (upload_on_reboot=true)\n",
__FUNCTION__, __LINE__);
} else {
// Check reboot reason file for scheduled reboot (grep -i "Scheduled Reboot\|MAINTENANCE_REBOOT")
bool is_scheduled_reboot = false;
FILE* reboot_file = fopen(reboot_info_path, "r");
if (reboot_file) {
char line[512];
while (fgets(line, sizeof(line), reboot_file)) {
// Look for "Scheduled Reboot" or "MAINTENANCE_REBOOT" (case insensitive)
if (strcasestr(line, "Scheduled Reboot") || strcasestr(line, "MAINTENANCE_REBOOT")) {
is_scheduled_reboot = true;
break;
}
}
fclose(reboot_file);
}

// Get RFC setting for unscheduled reboot upload via RBUS
bool disable_unscheduled_upload = false;
if (!rbus_get_bool_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.UploadLogsOnUnscheduledReboot.Disable",
&disable_unscheduled_upload)) {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to get UploadLogsOnUnscheduledReboot.Disable RFC, assuming false\n",
__FUNCTION__, __LINE__);
disable_unscheduled_upload = false;
}

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Reboot reason check - Scheduled: %d, Disable unscheduled RFC: %d\n",
__FUNCTION__, __LINE__, is_scheduled_reboot, disable_unscheduled_upload);

// Upload if: reboot reason is empty (unscheduled) AND RFC doesn't disable it
// Script logic: [ -z "$reboot_reason" -a "$DISABLE_UPLOAD_LOGS_UNSHEDULED_REBOOT" == "false" ]
if (!is_scheduled_reboot && !disable_unscheduled_upload) {
should_upload = true;
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Unscheduled reboot and RFC allows upload\n", __FUNCTION__, __LINE__);
}
}

if (!should_upload) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Upload not allowed based on reboot reason and RFC settings\n",
__FUNCTION__, __LINE__);
return 0;
}

// Construct full archive path using session archive filename
char archive_path[MAX_PATH_LENGTH];
int written = snprintf(archive_path, sizeof(archive_path), "%s/%s",
ctx->paths.prev_log_path, session->archive_file);

if (written >= (int)sizeof(archive_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Archive path too long\n", __FUNCTION__, __LINE__);
return -1;
}

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Uploading main logs: %s\n",
__FUNCTION__, __LINE__, archive_path);

// Upload main logs (session->success is set by execute_upload_cycle)
int ret = upload_archive(ctx, session, archive_path);

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Main log upload complete (result=%d)\n",
__FUNCTION__, __LINE__, ret);

// Upload DRI logs if directory exists (using separate session to avoid state corruption)
if (ctx->settings.include_dri && dir_exists(ctx->paths.dri_log_path)) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] DRI log directory exists, uploading DRI logs\n",
__FUNCTION__, __LINE__);

// Generate DRI archive filename: {MAC}_DRI_Logs_{timestamp}.tgz
char dri_filename[MAX_FILENAME_LENGTH];
if (!generate_archive_name(dri_filename, sizeof(dri_filename),
ctx->device.mac_address, "DRI_Logs")) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Failed to generate DRI archive filename\n",
__FUNCTION__, __LINE__);
} else {
char dri_archive[MAX_PATH_LENGTH];
int written = snprintf(dri_archive, sizeof(dri_archive), "%s/%s",
ctx->paths.prev_log_path, dri_filename);

if (written >= (int)sizeof(dri_archive)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] DRI archive path too long\n", __FUNCTION__, __LINE__);
} else {
// Create DRI archive
int dri_ret = create_dri_archive(ctx, dri_archive);

if (dri_ret == 0) {
#ifndef L2_TEST_ENABLED
sleep(60);
#endif

// Upload DRI logs using separate session state
SessionState dri_session = *session; // Copy current session config
dri_session.direct_attempts = 0; // Reset attempt counters
dri_session.codebig_attempts = 0;
dri_ret = upload_archive(ctx, &dri_session, dri_archive);

// Send telemetry for DRI upload (matches script lines 883, 886)
// Script sends SYST_INFO_PDRILogUpload for both success and failure
t2_count_notify("SYST_INFO_PDRILogUpload");

if (dri_ret == 0) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] DRI log upload succeeded, removing DRI directory\n",
__FUNCTION__, __LINE__);
remove_directory(ctx->paths.dri_log_path);
} else {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] DRI log upload failed\n", __FUNCTION__, __LINE__);
}

// Clean up DRI archive
remove_file(dri_archive);
}
}
}
}

// Clear old packet captures
if (ctx->settings.include_pcap) {
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB,
"[%s:%d] Clearing old packet captures\n", __FUNCTION__, __LINE__);
clear_old_packet_captures(ctx->paths.log_path);
}

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Upload phase complete\n", __FUNCTION__, __LINE__);

return ret;
}

/**
* @brief Cleanup phase for REBOOT/NON_DCM strategy
*
* Shell script equivalent (uploadLogOnReboot lines 893-906):
* - Always runs (regardless of upload success)
* - Delete tar file
* - Remove timestamps from filenames (restore original names)
* - Create permanent backup directory
* - Move all files to permanent backup
* - Clean PREV_LOG_PATH
*/
static int reboot_cleanup(RuntimeContext* ctx, SessionState* session, bool upload_success)
{
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Starting cleanup phase (upload_success=%d)\n",
__FUNCTION__, __LINE__, upload_success);

sleep(5);

// Delete tar file
char tar_path[MAX_PATH_LENGTH];
int written = snprintf(tar_path, sizeof(tar_path), "%s/%s",
ctx->paths.prev_log_path, session->archive_file);

if (written >= (int)sizeof(tar_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Tar path too long\n", __FUNCTION__, __LINE__);
return -1;
}

if (file_exists(tar_path)) {
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB,
"[%s:%d] Removing tar file: %s\n",
__FUNCTION__, __LINE__, tar_path);
remove_file(tar_path);
}

// Remove timestamps from filenames (restore original names)
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Removing timestamps from filenames\n", __FUNCTION__, __LINE__);

int ret = remove_timestamp_from_files(ctx->paths.prev_log_path);
if (ret != 0) {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to remove timestamps from some files\n",
__FUNCTION__, __LINE__);
// Continue anyway
}

// Get permanent backup path (stored in setup phase)
const char* perm_log_path = perm_log_path_storage;

// Create permanent backup directory
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Creating permanent backup directory: %s\n",
__FUNCTION__, __LINE__, perm_log_path);

if (!create_directory(perm_log_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Failed to create permanent backup directory\n",
__FUNCTION__, __LINE__);
return -1;
}

// Move all files from PREV_LOG_PATH to permanent backup
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Moving files to permanent backup\n", __FUNCTION__, __LINE__);

ret = move_directory_contents(ctx->paths.prev_log_path, perm_log_path);
if (ret != 0) {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to move some files to permanent backup\n",
__FUNCTION__, __LINE__);
}

// Clean PREV_LOG_PATH
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Cleaning PREV_LOG_PATH\n", __FUNCTION__, __LINE__);

clean_directory(ctx->paths.prev_log_path);

// Recreate PREV_LOG_BACKUP_PATH for next boot cycle
// Script lines 900-902: rm -rf + mkdir -p PREV_LOG_BACKUP_PATH
// PREV_LOG_BACKUP_PATH = $LOG_PATH/PreviousLogs_backup/
char prev_log_backup_path[MAX_PATH_LENGTH];
written = snprintf(prev_log_backup_path, sizeof(prev_log_backup_path), "%s/PreviousLogs_backup",
ctx->paths.log_path);

if (written >= (int)sizeof(prev_log_backup_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] PREV_LOG_BACKUP_PATH too long\n", __FUNCTION__, __LINE__);
} else {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Recreating PREV_LOG_BACKUP_PATH for next boot: %s\n",
__FUNCTION__, __LINE__, prev_log_backup_path);

if (dir_exists(prev_log_backup_path)) {
remove_directory(prev_log_backup_path);
}

if (!create_directory(prev_log_backup_path)) {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to create PREV_LOG_BACKUP_PATH\n", __FUNCTION__, __LINE__);
}
}

// If DCM mode with upload_on_reboot=false, add permanent path to DCM batch list
// Script line 1019: echo $PERM_LOG_PATH >> $DCM_UPLOAD_LIST
if (ctx->flags.dcm_flag == 1 && ctx->flags.upload_on_reboot == 0) {
char dcm_upload_list[MAX_PATH_LENGTH];
int written = snprintf(dcm_upload_list, sizeof(dcm_upload_list), "%s/dcm_upload", ctx->paths.log_path);

if (written >= (int)sizeof(dcm_upload_list)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] DCM upload list path too long\n", __FUNCTION__, __LINE__);
} else {
FILE* fp = fopen(dcm_upload_list, "a");
if (fp) {
fprintf(fp, "%s\n", perm_log_path);
fclose(fp);
}
}
}

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Cleanup phase complete. Logs backed up to: %s\n",
__FUNCTION__, __LINE__, perm_log_path);

return 0;
}
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The strategy_reboot.c file still uses the old nested structure ctx->paths.* in multiple places (e.g., lines 88-92, 135-147, 172-212, 239-250, etc.) instead of the flattened structure used elsewhere in the codebase. This creates an inconsistency with the refactoring done in other files where ctx.paths.prev_log_path was changed to ctx.prev_log_path.

Copilot uses AI. Check for mistakes.
Comment on lines +786 to +1112
if (ctx->include_pcap) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Collecting PCAP file to PREV_LOG_PATH\n", __FUNCTION__, __LINE__);
int count = collect_pcap_logs(ctx, ctx->prev_log_path);
if (count > 0) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Collected %d PCAP file\n", __FUNCTION__, __LINE__, count);
}
}

// Create archive from PREV_LOG_PATH (files already have timestamps)
int ret = create_archive(ctx, session, ctx->prev_log_path);
if (ret != 0) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Failed to create archive\n", __FUNCTION__, __LINE__);
return -1;
}
#ifndef L2_TEST_ENABLED
sleep(60);
#endif

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Archive phase complete\n", __FUNCTION__, __LINE__);

return 0;
}

/**
* @brief Upload phase for REBOOT/NON_DCM strategy
*
* Shell script equivalent (uploadLogOnReboot lines 853-890):
* - Check reboot reason and RFC settings
* - Upload main logs if allowed
* - Upload DRI logs if directory exists
* - Clear old packet captures
*/
static int reboot_upload(RuntimeContext* ctx, SessionState* session)
{
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Starting upload phase\n", __FUNCTION__, __LINE__);

// Check reboot reason and RFC settings (matches script logic)
// Script: if [ "$uploadLog" == "true" ] || [ -z "$reboot_reason" -a "$DISABLE_UPLOAD_LOGS_UNSHEDULED_REBOOT" == "false" ]
// Note: When DCM_FLAG=0 (Non-DCM), script ALWAYS passes "true" regardless of UploadOnReboot value
// When DCM_FLAG=1 (DCM mode), upload_on_reboot determines the behavior
bool should_upload = false;
const char* reboot_info_path = "/opt/secure/reboot/previousreboot.info";

// Non-DCM mode (DCM_FLAG=0): Always upload (script line 999: uploadLogOnReboot true)
if (ctx->dcm_flag == 0) {
should_upload = true;
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Non-DCM mode (dcm_flag=0), will always upload logs\n",
__FUNCTION__, __LINE__);
}
// DCM mode (DCM_FLAG=1): Check upload_on_reboot flag
else if (ctx->upload_on_reboot) {
should_upload = true;
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] DCM mode: Upload enabled from settings (upload_on_reboot=true)\n",
__FUNCTION__, __LINE__);
} else {
// Check reboot reason file for scheduled reboot (grep -i "Scheduled Reboot\|MAINTENANCE_REBOOT")
bool is_scheduled_reboot = false;
FILE* reboot_file = fopen(reboot_info_path, "r");
if (reboot_file) {
char line[512];
while (fgets(line, sizeof(line), reboot_file)) {
// Look for "Scheduled Reboot" or "MAINTENANCE_REBOOT" (case insensitive)
if (strcasestr(line, "Scheduled Reboot") || strcasestr(line, "MAINTENANCE_REBOOT")) {
is_scheduled_reboot = true;
break;
}
}
fclose(reboot_file);
}

// Get RFC setting for unscheduled reboot upload via RBUS
bool disable_unscheduled_upload = false;
if (!rbus_get_bool_param("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.UploadLogsOnUnscheduledReboot.Disable",
&disable_unscheduled_upload)) {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to get UploadLogsOnUnscheduledReboot.Disable RFC, assuming false\n",
__FUNCTION__, __LINE__);
disable_unscheduled_upload = false;
}

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Reboot reason check - Scheduled: %d, Disable unscheduled RFC: %d\n",
__FUNCTION__, __LINE__, is_scheduled_reboot, disable_unscheduled_upload);

// Upload if: reboot reason is empty (unscheduled) AND RFC doesn't disable it
// Script logic: [ -z "$reboot_reason" -a "$DISABLE_UPLOAD_LOGS_UNSHEDULED_REBOOT" == "false" ]
if (!is_scheduled_reboot && !disable_unscheduled_upload) {
should_upload = true;
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Unscheduled reboot and RFC allows upload\n", __FUNCTION__, __LINE__);
}
}

if (!should_upload) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Upload not allowed based on reboot reason and RFC settings\n",
__FUNCTION__, __LINE__);
return 0;
}

// Construct full archive path using session archive filename
char archive_path[MAX_PATH_LENGTH];
int written = snprintf(archive_path, sizeof(archive_path), "%s/%s",
ctx->prev_log_path, session->archive_file);

if (written >= (int)sizeof(archive_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Archive path too long\n", __FUNCTION__, __LINE__);
return -1;
}

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Uploading main logs: %s\n",
__FUNCTION__, __LINE__, archive_path);

// Upload main logs (session->success is set by execute_upload_cycle)
int ret = upload_archive(ctx, session, archive_path);

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Main log upload complete (result=%d)\n",
__FUNCTION__, __LINE__, ret);

// Upload DRI logs if directory exists (using separate session to avoid state corruption)
if (ctx->include_dri && dir_exists(ctx->dri_log_path)) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] DRI log directory exists, uploading DRI logs\n",
__FUNCTION__, __LINE__);

// Generate DRI archive filename: {MAC}_DRI_Logs_{timestamp}.tgz
char dri_filename[MAX_FILENAME_LENGTH];
if (!generate_archive_name(dri_filename, sizeof(dri_filename),
ctx->mac_address, "DRI_Logs")) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Failed to generate DRI archive filename\n",
__FUNCTION__, __LINE__);
} else {
char dri_archive[MAX_PATH_LENGTH];
int written = snprintf(dri_archive, sizeof(dri_archive), "%s/%s",
ctx->prev_log_path, dri_filename);

if (written >= (int)sizeof(dri_archive)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] DRI archive path too long\n", __FUNCTION__, __LINE__);
} else {
// Create DRI archive
int dri_ret = create_dri_archive(ctx, dri_archive);

if (dri_ret == 0) {
sleep(60);

// Upload DRI logs using separate session state
SessionState dri_session = *session; // Copy current session config
dri_session.direct_attempts = 0; // Reset attempt counters
dri_session.codebig_attempts = 0;
dri_ret = upload_archive(ctx, &dri_session, dri_archive);

// Send telemetry for DRI upload (matches script lines 883, 886)
// Script sends SYST_INFO_PDRILogUpload for both success and failure
t2_count_notify("SYST_INFO_PDRILogUpload");

if (dri_ret == 0) {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] DRI log upload succeeded, removing DRI directory\n",
__FUNCTION__, __LINE__);
remove_directory(ctx->dri_log_path);
} else {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] DRI log upload failed\n", __FUNCTION__, __LINE__);
}

// Clean up DRI archive
remove_file(dri_archive);
}
}
}
}

// Clear old packet captures
if (ctx->include_pcap) {
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB,
"[%s:%d] Clearing old packet captures\n", __FUNCTION__, __LINE__);
clear_old_packet_captures(ctx->log_path);
}

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Upload phase complete\n", __FUNCTION__, __LINE__);

return ret;
}

/**
* @brief Cleanup phase for REBOOT/NON_DCM strategy
*
* Shell script equivalent (uploadLogOnReboot lines 893-906):
* - Always runs (regardless of upload success)
* - Delete tar file
* - Remove timestamps from filenames (restore original names)
* - Create permanent backup directory
* - Move all files to permanent backup
* - Clean PREV_LOG_PATH
*/
static int reboot_cleanup(RuntimeContext* ctx, SessionState* session, bool upload_success)
{
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Starting cleanup phase (upload_success=%d)\n",
__FUNCTION__, __LINE__, upload_success);
#ifndef L2_TEST_ENABLED
sleep(5);
#endif
// Delete tar file
char tar_path[MAX_PATH_LENGTH];
int written = snprintf(tar_path, sizeof(tar_path), "%s/%s",
ctx->prev_log_path, session->archive_file);

if (written >= (int)sizeof(tar_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Tar path too long\n", __FUNCTION__, __LINE__);
return -1;
}

if (file_exists(tar_path)) {
RDK_LOG(RDK_LOG_DEBUG, LOG_UPLOADSTB,
"[%s:%d] Removing tar file: %s\n",
__FUNCTION__, __LINE__, tar_path);
remove_file(tar_path);
}

// Remove timestamps from filenames (restore original names)
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Removing timestamps from filenames\n", __FUNCTION__, __LINE__);

int ret = remove_timestamp_from_files(ctx->prev_log_path);
if (ret != 0) {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to remove timestamps from some files\n",
__FUNCTION__, __LINE__);
// Continue anyway
}

// Get permanent backup path (stored in setup phase)
const char* perm_log_path = perm_log_path_storage;

// Create permanent backup directory
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Creating permanent backup directory: %s\n",
__FUNCTION__, __LINE__, perm_log_path);

if (!create_directory(perm_log_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] Failed to create permanent backup directory\n",
__FUNCTION__, __LINE__);
return -1;
}

// Move all files from PREV_LOG_PATH to permanent backup
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Moving files to permanent backup\n", __FUNCTION__, __LINE__);

ret = move_directory_contents(ctx->prev_log_path, perm_log_path);
if (ret != 0) {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to move some files to permanent backup\n",
__FUNCTION__, __LINE__);
}

// Clean PREV_LOG_PATH
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Cleaning PREV_LOG_PATH\n", __FUNCTION__, __LINE__);

clean_directory(ctx->prev_log_path);

// Recreate PREV_LOG_BACKUP_PATH for next boot cycle
// Script lines 900-902: rm -rf + mkdir -p PREV_LOG_BACKUP_PATH
// PREV_LOG_BACKUP_PATH = $LOG_PATH/PreviousLogs_backup/
char prev_log_backup_path[MAX_PATH_LENGTH];
written = snprintf(prev_log_backup_path, sizeof(prev_log_backup_path), "%s/PreviousLogs_backup",
ctx->log_path);

if (written >= (int)sizeof(prev_log_backup_path)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] PREV_LOG_BACKUP_PATH too long\n", __FUNCTION__, __LINE__);
} else {
RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] Recreating PREV_LOG_BACKUP_PATH for next boot: %s\n",
__FUNCTION__, __LINE__, prev_log_backup_path);

if (dir_exists(prev_log_backup_path)) {
remove_directory(prev_log_backup_path);
}

if (!create_directory(prev_log_backup_path)) {
RDK_LOG(RDK_LOG_WARN, LOG_UPLOADSTB,
"[%s:%d] Failed to create PREV_LOG_BACKUP_PATH\n", __FUNCTION__, __LINE__);
}
}

// If DCM mode with upload_on_reboot=false, add permanent path to DCM batch list
// Script line 1019: echo $PERM_LOG_PATH >> $DCM_UPLOAD_LIST
if (ctx->dcm_flag == 1 && ctx->upload_on_reboot == 0) {
char dcm_upload_list[MAX_PATH_LENGTH];
int written = snprintf(dcm_upload_list, sizeof(dcm_upload_list), "%s/dcm_upload", ctx->log_path);

if (written >= (int)sizeof(dcm_upload_list)) {
RDK_LOG(RDK_LOG_ERROR, LOG_UPLOADSTB,
"[%s:%d] DCM upload list path too long\n", __FUNCTION__, __LINE__);
} else {
FILE* fp = fopen(dcm_upload_list, "a");
if (fp) {
fprintf(fp, "%s\n", perm_log_path);
fclose(fp);
}
}
}

RDK_LOG(RDK_LOG_INFO, LOG_UPLOADSTB,
"[%s:%d] REBOOT/NON_DCM: Cleanup phase complete. Logs backed up to: %s\n",
__FUNCTION__, __LINE__, perm_log_path);

return 0;
}
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The strategies.c file (reboot strategy section) uses the flattened structure for most fields but still uses nested structure for some fields like ctx->flags.dcm_flag and ctx->flags.upload_on_reboot (lines 835, 842, 1091), and ctx->settings.include_pcap and ctx->settings.include_dri (lines 786, 916) and ctx->device.mac_address (line 924). These should be changed to ctx->dcm_flag, ctx->upload_on_reboot, ctx->include_pcap, ctx->include_dri, and ctx->mac_address respectively to be consistent with the refactoring.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings January 7, 2026 13:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 44 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants