backup_logs is a standalone C utility that migrates the functionality of backup_logs.sh to a compiled binary for RDK-based embedded devices. It preserves device log files across reboots by rotating them into a structured backup hierarchy (PreviousLogs/PreviousLogs_backup), supporting both HDD-enabled (timestamped directories) and HDD-disabled (4-level prefixed rotation) device configurations. The module also handles version file capture, special file processing, disk threshold checks, and systemd integration.
- Architecture
- Modules
- Data Structures and Types
- Backup Strategies
- API Reference
- Special Files Configuration
- Error Handling
- Memory Management
- Build Instructions
- Unit Testing
- Configuration Files and Paths
- Platform Notes
- See Also
The module is a single executable (backup_logs) built from five C source files. It follows a strictly sequential, single-threaded execution model with no dynamic memory allocation beyond what is provided by the RDK utility layer.
graph TD
A[backup_logs_main] --> B[backup_logs_init\nLogger + Config]
B --> C{Config valid?}
C -- no --> Z[Exit with error]
C -- yes --> D[Create workspace dirs\ncreateDir]
D --> E[emptyFolder\nPreviousLogs_backup]
E --> F[sys_execute_disk_check]
F --> G{hdd_enabled?}
G -- yes --> H[backup_execute_hdd_enabled_strategy]
G -- no --> I[backup_execute_hdd_disabled_strategy]
H --> J[backup_execute_common_operations]
I --> J
J --> K[special_files_execute_all]
K --> L[Copy version files]
L --> M[sys_send_systemd_notification]
M --> N[Create persistent marker]
N --> O[backup_logs_cleanup]
O --> P[Exit 0]
graph TB
MAIN[backup_logs\nbackup_logs.c]
CFG[config_manager\nconfig_manager.c]
ENG[backup_engine\nbackup_engine.c]
SF[special_files\nspecial_files.c]
SYS[sys_integration\nsys_integration.c]
RDK[libfwutils\nRDK property APIs]
LOG[librdkloggers\nRDK_LOG]
SYSD[libsystemd\nsd_notify]
MAIN --> CFG
MAIN --> ENG
MAIN --> SF
MAIN --> SYS
CFG --> RDK
CFG --> LOG
ENG --> LOG
SF --> LOG
SYS --> SYSD
SYS --> LOG
| File | Role |
|---|---|
src/backup_logs.c |
Main entry point, top-level lifecycle orchestration |
include/backup_logs.h |
Public API: backup_logs_main(), backup_logs_init(), backup_logs_execute(), backup_logs_cleanup() |
Performs initialization of the RDK logger (with optional extended file-output configuration), loads configuration, drives the backup strategies in sequence, and ensures resources are released on all exit paths.
Top-level API:
int backup_logs_main(int argc, char *argv[]);
int backup_logs_init(backup_config_t *config);
int backup_logs_execute(const backup_config_t *config);
int backup_logs_cleanup(backup_config_t *config);Logger initialization (two modes, selected at compile-time):
| Mode | Flag | Output | Notes |
|---|---|---|---|
| Extended | -DRDK_LOGGER_EXT |
/tmp/backup_logs.log (50 KB, 5 rotations) |
Timestamped, preferred on production |
| Standard | -DRDK_LOGGER_ENABLED |
Controlled by /etc/debug.ini |
Fallback |
| None | Neither flag | stdout/stderr |
Development/CI only |
| File | Role |
|---|---|
src/config_manager.c |
Reads RDK property system, constructs and validates all paths |
include/config_manager.h |
config_load(), special_files_config_load(), special_files_execute_operations() |
Uses the libfwutils APIs getIncludePropertyData() and getDevicePropertyData() to resolve the following properties:
| Property | Source | Default |
|---|---|---|
LOG_PATH |
include.properties |
/opt/logs |
HDD_ENABLED |
device.properties |
false |
APP_PERSISTENT_PATH |
device.properties |
/opt |
Derived paths are assembled in-struct (no heap allocation):
log_path → LOG_PATH (e.g. /opt/logs)
prev_log_path → LOG_PATH/PreviousLogs
prev_log_backup_path→ LOG_PATH/PreviousLogs_backup
persistent_path → APP_PERSISTENT_PATH
All snprintf() return values are checked and an error is returned if truncation would occur.
Public API:
int config_load(backup_config_t* config);
int special_files_config_load(special_files_config_t* config,
const char* config_file);
int special_files_config_validate(const special_files_config_t* config);
void special_files_config_free(special_files_config_t* config);
int special_files_execute_operations(const special_files_config_t* config,
const backup_config_t* backup_config);
int config_parse_environment(backup_config_t* config);| File | Role |
|---|---|
src/backup_engine.c |
Implements both backup strategies, file move/copy helpers |
include/backup_engine.h |
Strategy and helper function declarations |
The engine selects the appropriate strategy from hdd_enabled in backup_config_t and delegates through two well-defined strategy functions. File discovery uses opendir/readdir with fnmatch-style pattern matching against *.txt*, *.log*, and bootlog.
Public API:
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 backup_and_recover_logs(const char* source, const char* dest,
backup_operation_type_t op,
const char* s_ext, const char* d_ext);
int move_log_files_by_pattern(const char* source_dir, const char* dest_dir);| File | Role |
|---|---|
src/special_files.c |
Parses /etc/backup_logs/special_files.conf, executes move/copy per entry |
include/special_files.h |
Init, load, validate, execute declarations |
The configuration file format is one source path per line. Comments (#) and blank lines are skipped. The operation type is determined automatically from the source path prefix: files under /tmp/ are moved; all others are copied to LOG_PATH.
Public API:
int special_files_init(void);
void special_files_cleanup(void);
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);| File | Role |
|---|---|
src/sys_integration.c |
Sends sd_notify messages for service readiness and status |
include/sys_integration.h |
sys_send_systemd_notification() |
Wraps libsystemd to send READY=1 and STATUS=Logs Backup Done..! at completion. Runs gracefully in non-systemd environments (notification errors are logged but do not fail the backup).
int sys_send_systemd_notification(const char* message);All types are defined in include/backup_types.h.
Central configuration structure passed through the entire call chain.
typedef struct {
char log_path[PATH_MAX]; /* Primary log directory */
char prev_log_path[PATH_MAX]; /* LOG_PATH/PreviousLogs */
char prev_log_backup_path[PATH_MAX];/* LOG_PATH/PreviousLogs_backup */
char persistent_path[PATH_MAX]; /* APP_PERSISTENT_PATH */
bool hdd_enabled; /* Device has HDD */
} backup_config_t;| Code | Value | Meaning |
|---|---|---|
BACKUP_SUCCESS |
0 |
Operation completed successfully |
BACKUP_ERROR_CONFIG |
-1 |
Invalid or missing configuration (e.g. path truncation) |
BACKUP_ERROR_FILESYSTEM |
-2 |
Directory or file operation failure |
BACKUP_ERROR_PERMISSIONS |
-3 |
Insufficient filesystem permissions |
BACKUP_ERROR_MEMORY |
-4 |
Memory allocation failure |
BACKUP_ERROR_INVALID_PARAM |
-5 |
NULL or invalid function argument |
BACKUP_ERROR_NOT_FOUND |
-6 |
Required file or directory absent |
BACKUP_ERROR_DISK_FULL |
-7 |
Insufficient disk space |
BACKUP_ERROR_SYSTEM |
-8 |
External script or system call failure |
typedef enum {
BACKUP_OP_MOVE,
BACKUP_OP_COPY,
BACKUP_OP_DELETE
} backup_operation_type_t;typedef enum {
SPECIAL_FILE_COPY = 0,
SPECIAL_FILE_MOVE = 1
} 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]; /* unused, reserved */
} special_file_entry_t;
typedef struct {
special_file_entry_t entries[MAX_SPECIAL_FILES]; /* MAX_SPECIAL_FILES = 32 */
size_t count;
bool config_loaded;
} special_files_config_t;typedef struct {
bool debug_enabled;
bool force_rotation;
bool skip_disk_check;
bool cleanup_enabled;
} backup_flags_t;Used on devices without persistent disk (hdd_enabled = false). The current backup level is detected by probing for messages.txt, bak1_messages.txt, bak2_messages.txt, and bak3_messages.txt in PreviousLogs.
stateDiagram-v2
[*] --> Level0 : No messages.txt
Level0 --> Level1 : After rotation\n(bak1_ prefix added)
Level1 --> Level2 : After rotation\n(bak2_ prefix added)
Level2 --> Level3 : After rotation\n(bak3_ prefix added)
Level3 --> Level0 : Full rotation:\nbak1→base, bak2→bak1,\nbak3→bak2, current→bak3
Rotation cascade at Level 3:
| Step | Action |
|---|---|
| 1 | bak1_* → rename without prefix (becomes base) |
| 2 | bak2_* → rename with bak1_ prefix |
| 3 | bak3_* → rename with bak2_ prefix |
| 4 | Current logs → PreviousLogs/bak3_<filename> |
File patterns matched: *.txt*, *.log*, *.bin*, bootlog
Used on devices with persistent storage (hdd_enabled = true).
flowchart TD
A[Check for messages.txt\nin PreviousLogs]
A -->|Not found| B[Move all logs\ndirectly to PreviousLogs]
A -->|Found| C[Generate timestamp\nMM-DD-YY-HH-MM-SSAM]
C --> D[Create logbackup-timestamp dir\nin PreviousLogs]
D --> E[Move logs into\ntimestamped directory]
B --> F[Create last_reboot marker]
E --> F
File patterns matched: *.txt*, *.log*, bootlog (no .bin* files)
After the device-specific strategy completes, backup_execute_common_operations() runs:
- Loads and processes
/etc/backup_logs/special_files.conf - Copies version files:
/version.txt,/etc/skyversion.txt,/etc/rippleversion.txt - Removes old
last_rebootmarkers - Creates new
last_rebootmarker atpersistent_path/logFileBackup - Sends systemd
READY=1+ status notification
Initialises the RDK logger and loads configuration from the RDK property system.
Signature:
int backup_logs_init(backup_config_t *config);Parameters:
config— Pre-allocatedbackup_config_t; populated on return (must be non-NULL)
Returns: BACKUP_SUCCESS, BACKUP_ERROR_INVALID_PARAM, or BACKUP_ERROR_CONFIG
Thread Safety: Not thread-safe. Call once from the main thread.
Example:
backup_config_t config;
memset(&config, 0, sizeof(config));
int ret = backup_logs_init(&config);
if (ret != BACKUP_SUCCESS) {
/* logger has already been called with the reason */
return ret;
}Runs the complete backup workflow: workspace setup, strategy selection, common operations.
Signature:
int backup_logs_execute(const backup_config_t *config);Parameters:
config— Populated configuration (frombackup_logs_init())
Returns: BACKUP_SUCCESS or error code from the first failing step
Notes:
- A failure in disk threshold check is logged but does not abort execution.
- Special file failures are non-fatal; execution continues with remaining entries.
Releases any resources acquired during execution and resets configuration.
Signature:
int backup_logs_cleanup(backup_config_t *config);Resolves all configuration from the RDK property system and constructs derived paths.
Signature:
int config_load(backup_config_t* config);Returns: BACKUP_SUCCESS, BACKUP_ERROR_INVALID_PARAM, or BACKUP_ERROR_CONFIG (path truncation)
Implements the timestamped-directory backup for HDD-capable devices.
Signature:
int backup_execute_hdd_enabled_strategy(const backup_config_t* config);Implements the 4-level prefixed rotation for non-HDD devices.
Signature:
int backup_execute_hdd_disabled_strategy(const backup_config_t* config);Moves all files matching *.txt*, *.log*, or bootlog from source to destination directory.
Signature:
int move_log_files_by_pattern(const char* source_dir, const char* dest_dir);Returns: BACKUP_SUCCESS or BACKUP_ERROR_FILESYSTEM if source cannot be opened
Notes:
- Each
snprintf()building the full path is bounds-checked; oversized names are skipped with a log warning. - Uses
filePresentCheck()to verify each candidate is a regular file.
Processes all entries in the special files configuration, executing move or copy per entry.
Signature:
int special_files_execute_all(const special_files_config_t* config,
const backup_config_t* backup_config);Returns: BACKUP_SUCCESS; individual entry failures are logged and skipped (non-fatal).
Sends a notification string to the systemd service manager.
Signature:
int sys_send_systemd_notification(const char* message);Typical calls:
sys_send_systemd_notification("Logs Backup Done..!");/etc/backup_logs/special_files.conf lists additional files to capture during the common operations phase. The format is one absolute source path per line.
# Special Files Configuration for backup_logs
# Lines starting with # are comments; blank lines are ignored.
#
# Operation is determined automatically:
# /tmp/* → moved (frees space)
# other → copied (preserves original)
# Destination is always LOG_PATH/<basename>
/tmp/disk_cleanup.log
/tmp/mount_log.txt
/tmp/mount-ta_log.txt
/etc/skyversion.txt
/etc/rippleversion.txt
/version.txtProcessing rules:
| Source prefix | Operation | Destination |
|---|---|---|
/tmp/ |
move (frees flash) |
LOG_PATH/<basename> |
| Other | copy (preserves src) |
LOG_PATH/<basename> |
The maximum configurable entries is MAX_SPECIAL_FILES (32). Missing source files generate a warning log entry but do not abort the backup.
All functions return BACKUP_SUCCESS (0) on success or a negative backup_result_t value on failure. The convention in every module is:
if (!config) {
RDK_LOG(RDK_LOG_ERROR, LOG_BACKUP_LOGS,
"<function>: NULL config parameter\n");
return BACKUP_ERROR_INVALID_PARAM;
}Non-fatal vs fatal failures:
| Condition | Behaviour |
|---|---|
| Disk threshold check script absent | Logged, execution continues |
| Special file entry missing | Logged as warning, next entry processed |
| Version file missing | Logged as warning, execution continues |
| systemd notification failure | Logged, execution continues |
| Config load failure | Fatal: backup_logs_main() returns error |
| Directory creation failure | Fatal: execution aborted |
Logging levels used:
| Macro | When |
|---|---|
RDK_LOG(RDK_LOG_ERROR, ...) |
Fatal conditions, invalid parameters |
RDK_LOG(RDK_LOG_WARN, ...) |
Non-fatal issues, missing optional files |
RDK_LOG(RDK_LOG_INFO, ...) |
Progress milestones, loaded values |
RDK_LOG(RDK_LOG_DEBUG, ...) |
Entry/exit of functions, intermediate values |
All messages use component name LOG_BACKUP_LOGS ("LOG.RDK.BACKUPLOGS").
backup_logs uses exclusively static-size buffers; there is no heap allocation in the application code itself.
graph TD
A[backup_logs_main\nstack: backup_config_t ~4 KB] --> B[config_load\nstack buffers ≤32 B each]
A --> C[special_files_config_t\nstack: ~MAX_SPECIAL_FILES × PATH_MAX]
A --> D[backup_engine\nstack: per-file path buffers PATH_MAX]
Allocation summary:
| Variable | Location | Size | Lifetime |
|---|---|---|---|
backup_config_t |
Stack (main) |
≤ 4 × PATH_MAX + bool |
Duration of main() |
special_files_config_t |
Stack (caller) | 32 × sizeof(special_file_entry_t) ≈ ~256 KB max |
Duration of caller scope |
Per-file path buffers in move_log_files_by_pattern |
Stack | 2 × PATH_MAX |
Single iteration |
Temporary property read buffers in config_load |
Stack | 32 B each | Duration of config_load() |
Peak heap use: Near zero (only what librdkloggers, libfwutils, and the C runtime allocate internally).
Ownership rules:
backup_config_tis owned bymain()and passed by pointer throughout; no module frees it.special_files_config_tis owned by the caller ofspecial_files_load_config(); callspecial_files_config_free()when done, even if populated only partially.- All string fields inside config structures are fixed-length arrays — no pointer ownership to manage.
| Dependency | Package | Notes |
|---|---|---|
| GCC / cross-compiler | Build environment | std=c99, -Wall -Wextra |
| Autotools | autoconf 2.69+, automake 1.15+ | |
librdkloggers |
RDK sysroot | Optional; enables RDK_LOG |
libfwutils |
RDK sysroot | Required for property APIs |
libsystemd |
sysroot or host | For sd_notify |
libsecure_wrapper |
RDK sysroot | Safe string/IO operations |
libm |
Standard libc | Math functions |
# From the repo root
autoreconf -i
# Native build
./configure
make
# Cross-compile (ARM RDK target)
./configure --host=arm-linux-gnueabihf \
PKG_CONFIG_SYSROOT_DIR=/path/to/sysroot
make
# Install
make install| Flag | Effect |
|---|---|
-DRDK_LOGGER_EXT |
Enable extended RDK logger with file output to /tmp/backup_logs.log |
-DRDK_LOGGER_ENABLED |
Enable standard RDK logger (controlled by /etc/debug.ini) |
Both flags are set in backup_logs/Makefile.am:
backup_logs_CPPFLAGS = -I... -DRDK_LOGGER_EXT
backup_logs_LDADD = -lm -lrdkloggers -lfwutils -lsystemd -lsecure_wrapperUnit tests use Google Test and Google Mock and reside in backup_logs/unittest/.
| Test File | Module Covered |
|---|---|
backup_engine_gtest.cpp |
backup_engine.c — strategies, file pattern helpers |
backup_logs_gtest.cpp |
backup_logs.c — init/execute/cleanup lifecycle |
config_manager_gtest.cpp |
config_manager.c — property loading, path derivation |
special_files_gtest.cpp |
special_files.c — config parsing, entry execution |
sys_integration_gtest.cpp |
sys_integration.c — systemd notification paths |
RBUS, RDK property, and file-system calls are stubbed using mock control variables (global struct pattern) so tests run without a live RDK environment.
# In the Docker CI container
docker run --rm \
-v "$(pwd):/workspace" \
ghcr.io/rdkcentral/docker-rdk-ci:latest \
bash /workspace/unit_test.sh≥ 80% line coverage. Tests cover:
- Normal paths for both HDD strategies
- All 4 rotation levels in the HDD-disabled strategy
- NULL and invalid parameter guards on every public function
snprintftruncation paths in config loading- Missing source files in special file processing
- Systemd notification success and failure paths
| File | Default Path | Purpose |
|---|---|---|
| Include properties | /etc/include.properties |
Source of LOG_PATH |
| Device properties | /etc/device.properties |
Source of HDD_ENABLED, APP_PERSISTENT_PATH |
| Special files list | /etc/backup_logs/special_files.conf |
Additional files to capture |
| Disk check script | /lib/rdk/disk_threshold_check.sh |
Optional pre-backup disk threshold check |
| Debug configuration | /etc/debug.ini |
RDK logger level settings |
| Logger output | /tmp/backup_logs.log |
Extended logger file output (when -DRDK_LOGGER_EXT) |
| Persistent marker | $APP_PERSISTENT_PATH/logFileBackup |
Signals backup completion across reboots |
Runtime directory layout after a successful backup:
$LOG_PATH/
├── PreviousLogs/
│ ├── messages.txt (HDD-disabled: base level)
│ ├── bak1_messages.txt (HDD-disabled: level 1)
│ ├── bak2_messages.txt (HDD-disabled: level 2)
│ ├── bak3_messages.txt (HDD-disabled: level 3)
│ ├── logbackup-04-03-26-… (HDD-enabled: timestamped dir)
│ └── last_reboot (marker file)
├── PreviousLogs_backup/ (cleaned before use)
├── skyversion.txt
├── rippleversion.txt
└── version.txt
ARMv7, MIPS, x86 (cross-compilation via --host=).
Designed for ext4, JFFS2, and UBIFS. All directory operations use createDir() from libfwutils, which handles filesystem-specific permission and inode constraints.
| Resource | Limit |
|---|---|
| Peak memory (application) | ≤ 512 KB |
| Startup time | ≤ 2 s on target hardware |
| File operation window | ≤ 30 s for typical log volumes |
| CPU % during backup | ≤ 10% |
MAX_SPECIAL_FILES |
32 entries |
- All paths are constructed with
snprintf()and bounds-checked; truncation returns an error rather than a silently-clipped path. - Source file paths in
special_files.confare processed without shell expansion, preventing command injection. secure_wrapper(libsecure_wrapper) is linked to harden string and I/O operations.- Symlink safety:
filePresentCheck()usesstat()(follows symlinks by design, consistent with the original shell script behaviour); callers validate the resolved path remains under expected directories.
- backup_logs_requirements.md — Functional and non-functional requirements
- backup_logs_migration_HLD.md — High-level design
- backup_logs_LLD.md — Low-level design with detailed algorithms
- diagrams/backup_logs_flowcharts.md — Text-based process flowcharts
- ../../README.md — DCM Agent top-level overview
- ../../special_files.conf — Example special files configuration installed to
/etc/backup_logs/