From 99e62413e1d500bef37a1d9b0e6ce1ec26e64fa4 Mon Sep 17 00:00:00 2001
From: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
Date: Thu, 31 Oct 2024 11:42:58 +0100
Subject: [PATCH] Handle Reboots Object internally

---
 docs/guide/libbbfdm_DeviceInfo_Reboots.md     |  30 +-
 .../share/bbfdm/scripts/bbf_reboot_handler.sh | 206 -------------
 src/fw_images.c                               |   6 +-
 src/reboots.c                                 | 284 +++++++++++++++++-
 src/reboots.h                                 |   2 +
 src/sysmngr.c                                 |   8 +
 src/utils.c                                   | 100 ++++++
 src/utils.h                                   |   6 +
 8 files changed, 404 insertions(+), 238 deletions(-)
 delete mode 100755 src/files/usr/share/bbfdm/scripts/bbf_reboot_handler.sh

diff --git a/docs/guide/libbbfdm_DeviceInfo_Reboots.md b/docs/guide/libbbfdm_DeviceInfo_Reboots.md
index d86662f..004ece5 100644
--- a/docs/guide/libbbfdm_DeviceInfo_Reboots.md
+++ b/docs/guide/libbbfdm_DeviceInfo_Reboots.md
@@ -2,28 +2,28 @@
 
 In TR-181 version 2.18, a new object, Device.DeviceInfo.Reboots, was introduced to track and monitor reboot operations. This object provides details such as reboot reasons, counts, timestamps, and more, offering a comprehensive view of the device's state. It simplifies diagnostics and troubleshooting for applications and processes running on the device.
 
-Currently, there is no standard configuration mapping to this object. However, we propose introducing a custom config called `deviceinfo` to manage this information effectively.
+Currently, there is no standard configuration mapping to this object. However, we propose introducing a custom config called `sysmngr` to manage this information effectively.
 
-The idea is to maintain a 1-to-1 mapping between the parameters and UCI config. To achieve this, we need to create an `init.d` service script that generates a UCI section each time the boot() function is called. Essentially, when the boot() function is executed, it will check the `/tmp/reset_reason` file for specific markers, such as (reset reason) and (reset triggered), to identify the cause of the last boot. And based on these markers, it will calculate the required counter for data model parameters and commit the changes in `deviceinfo.globals` section. Furthermore, if necessary, it will create a UCI reboot section by checking `deviceinfo.globals.max_reboot_entries` and adjusting the config accordingly.
+The idea is to maintain a 1-to-1 mapping between the parameters and UCI config. To achieve this, we need to create an `init.d` service script that generates a UCI section each time the start_service() function is called. Essentially, when the start_service() function is executed, it will check the `/tmp/reset_reason` file for specific markers, such as (reset reason) and (reset triggered), to identify the cause of the last boot. And based on these markers, it will calculate the required counter for data model parameters and commit the changes in `sysmngr.deviceinfo` section. Furthermore, if necessary, it will create a UCI reboot section by checking `sysmngr.deviceinfo.max_reboot_entries` and adjusting the config accordingly.
 
 This approach ensures that the data model maps directly to UCI config as closely as possible, eliminating the need for any adjustments at the data model layer.
 
 ## Parameter Mapping Details
 
-- Device.DeviceInfo.Reboots.BootCount: Maps to deviceinfo.globals.boot_count. This value is determined based on the marker (reset triggered: defaultreset) defined in `/tmp/reset_reason` file.
-- Device.DeviceInfo.Reboots.CurrentVersionBootCount: Maps to deviceinfo.globals.curr_version_boot_count. This value is determined based on the marker (reset triggered: upgrade) defined in `/tmp/reset_reason` file.
-- Device.DeviceInfo.Reboots.WatchdogBootCount: Maps to deviceinfo.globals.watchdog_boot_count. This value is determined based on the marker (reset reason: WATCHDOG) defined in `/tmp/reset_reason` file.
-- Device.DeviceInfo.Reboots.ColdBootCount: 
-- Device.DeviceInfo.Reboots.WarmBootCount: 
-- Device.DeviceInfo.Reboots.MaxRebootEntries: Maps to deviceinfo.globals.max_reboot_entries. Possible values include {-1, 0, etc..}. Each case will be handled internally by bbfdm and default value is 3 and maximum reboot entry supported is 255.
+- Device.DeviceInfo.Reboots.BootCount: Maps to sysmngr.deviceinfo.boot_count. This value is determined based on the marker (reset triggered: defaultreset) defined in `/tmp/reset_reason` file.
+- Device.DeviceInfo.Reboots.CurrentVersionBootCount: Maps to sysmngr.deviceinfo.curr_version_boot_count. This value is determined based on the marker (reset triggered: upgrade) defined in `/tmp/reset_reason` file.
+- Device.DeviceInfo.Reboots.WatchdogBootCount: Maps to sysmngr.deviceinfo.watchdog_boot_count. This value is determined based on the marker (reset reason: WATCHDOG) defined in `/tmp/reset_reason` file.
+- Device.DeviceInfo.Reboots.ColdBootCount: Maps to sysmngr.deviceinfo.cold_boot_count. This value is determined based on the marker (reset reason: POR_RESET) defined in `/tmp/reset_reason` file.
+- Device.DeviceInfo.Reboots.WarmBootCount: Maps to sysmngr.deviceinfo.warm_boot_count. This value is determined based on the marker (reset reason: POR_RESET) defined in `/tmp/reset_reason` file.
+- Device.DeviceInfo.Reboots.MaxRebootEntries: Maps to sysmngr.deviceinfo.max_reboot_entries. Possible values include {-1, 0, etc..}. Each case will be handled internally by bbfdm and default value is 3 and maximum reboot entry supported is 255.
 - Device.DeviceInfo.Reboots.RebootNumberOfEntries: This is an internal bbfdm mechanism used to count the number of reboot entries.
 - Device.DeviceInfo.Reboots.RemoveAllReboots(): An internal bbfdm API to remove all reboot sections.
 - Device.DeviceInfo.Reboots.Reboot.{i}.: Each reboot entry is stored in a 'reboot' section.
-- Device.DeviceInfo.Reboots.Reboot.{i}.Alias: Maps to deviceinfo.reboot[i].alias. This is managed internally by bbfdm.
-- Device.DeviceInfo.Reboots.Reboot.{i}.TimeStamp: Maps to deviceinfo.reboot[i].time_stamp. This value is based on system uptime.
-- Device.DeviceInfo.Reboots.Reboot.{i}.FirmwareUpdated: Maps to deviceinfo.reboot[i].firmware_updated.
-- Device.DeviceInfo.Reboots.Reboot.{i}.Cause: Maps to deviceinfo.reboot[i].cause. Possible values include {LocalReboot, RemoteReboot, FactoryReset, LocalFactoryReset, RemoteFactoryReset}.
-- Device.DeviceInfo.Reboots.Reboot.{i}.Reason: Maps to deviceinfo.reboot[i].reason. This value is determined based on the marker (reset reason) defined in `/tmp/reset_reason` file. 
+- Device.DeviceInfo.Reboots.Reboot.{i}.Alias: Maps to sysmngr.reboot[i].alias. This is managed internally by bbfdm.
+- Device.DeviceInfo.Reboots.Reboot.{i}.TimeStamp: Maps to sysmngr.reboot[i].time_stamp. This value is based on system uptime.
+- Device.DeviceInfo.Reboots.Reboot.{i}.FirmwareUpdated: Maps to sysmngr.reboot[i].firmware_updated.
+- Device.DeviceInfo.Reboots.Reboot.{i}.Cause: Maps to sysmngr.reboot[i].cause. Possible values include {LocalReboot, RemoteReboot, FactoryReset, LocalFactoryReset, RemoteFactoryReset}.
+- Device.DeviceInfo.Reboots.Reboot.{i}.Reason: Maps to sysmngr.reboot[i].reason. This value is determined based on the marker (reset reason) defined in `/tmp/reset_reason` file. 
 - Device.DeviceInfo.Reboots.Reboot.{i}.Remove(): An internal bbfdm API to remove the current 'reboot' section.
 
 
@@ -32,9 +32,9 @@ This approach ensures that the data model maps directly to UCI config as closely
 Below is an example of the configuration file:
 
 ```bash
-cat /etc/config/deviceinfo
+cat /etc/config/sysmngr
 
-config globals 'globals'
+config device-info 'deviceinfo'
 	option boot_count '2'
 	option curr_version_boot_count '4'
 	option watchdog_boot_count '3'
diff --git a/src/files/usr/share/bbfdm/scripts/bbf_reboot_handler.sh b/src/files/usr/share/bbfdm/scripts/bbf_reboot_handler.sh
deleted file mode 100755
index c2eb7bc..0000000
--- a/src/files/usr/share/bbfdm/scripts/bbf_reboot_handler.sh
+++ /dev/null
@@ -1,206 +0,0 @@
-#!/bin/sh
-
-# Script to handle Reboots Object
-#
-# Copyright © 2024 IOPSYS Software Solutions AB
-# Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
-#
-
-. /lib/functions.sh
-
-RESET_REASON_PATH="/tmp/reset_reason"
-MAX_RETRIES=3
-RETRY_DELAY=1
-
-log() {
-	echo "$@" | logger -t bbf_reboot -p info
-}
-
-reset_option_counter() {
-	local option_name=$1
-	local option_value=$2
-	uci_set "deviceinfo" "globals" "${option_name}" "${option_value}"
-}
-
-increment_option_counter() {
-	local option_name=$1
-	local option_value=$(uci_get "deviceinfo" "globals" "${option_name}" "0")
-	local counter=$((option_value + 1))
-	uci_set "deviceinfo" "globals" "${option_name}" "$counter"
-}
-
-get_boot_trigger() {
-	local trigger
-	trigger=$(grep "triggered" ${RESET_REASON_PATH} | cut -d ':' -f2 | xargs)
-	echo "${trigger}"
-}
-
-get_boot_reason() {
-	local reason
-	reason=$(grep "reason" ${RESET_REASON_PATH} | cut -d ':' -f2 | xargs)
-	echo "${reason}"
-}
-
-calculate_boot_time() {
-	# Get current time and uptime in seconds
-	local current_time uptime_seconds boot_time boot_time_formatted
-
-	current_time=$(date +%s)
-	uptime_seconds=$(awk '{print $1}' /proc/uptime | cut -d. -f1)
-
-	# Calculate the boot time by subtracting the uptime from the current time
-	boot_time=$((current_time - uptime_seconds))
-
-	# Convert the boot time to a human-readable format
-	boot_time_formatted=$(date -d "@$boot_time" +"%Y-%m-%dT%H:%M:%SZ")
-
-	echo "${boot_time_formatted}"
-}
-
-boot_reason_message() {
-	# Generate a human-readable message based on the boot reason and trigger
-	local trigger reason
-
-	trigger=$(get_boot_trigger)
-
-	if [ -n "${trigger}" ]; then
-		case "${trigger}" in
-			"defaultreset")
-				echo "FACTORY RESET"
-				;;
-			"upgrade")
-				echo "FIRMWARE UPGRADE"
-				;;
-			*)
-				echo "${trigger}"
-				;;
-		esac
-	else
-		reason=$(get_boot_reason)
-		case "${reason}" in
-			"POR_RESET")
-				echo "POWER ON RESET"
-				;;
-			*)
-				echo "${reason}"
-				;;
-		esac
-	fi
-}
-
-create_reboot_section() {
-	local trigger=$1
-	local reboot_sec
-
-	reboot_sec="reboot_$(date +%Y%m%d%H%M%S)"
-	uci_add "deviceinfo" "reboot" "${reboot_sec}"
-	uci_set "deviceinfo" "${reboot_sec}" "time_stamp" "$(calculate_boot_time)"
-
-	if [ "${trigger}" = "upgrade" ]; then
-		uci_set "deviceinfo" "${reboot_sec}" "firmware_updated" "1"
-	else
-		uci_set "deviceinfo" "${reboot_sec}" "firmware_updated" "0"
-	fi
-
-	if [ "${trigger}" = "defaultreset" ]; then
-		uci_set "deviceinfo" "${reboot_sec}" "cause" "FactoryReset"
-	else
-		local last_reboot_cause
-		last_reboot_cause=$(uci_get "deviceinfo" "globals" "last_reboot_cause" "LocalReboot")
-		uci_set "deviceinfo" "${reboot_sec}" "cause" "${last_reboot_cause}"
-		uci_set "deviceinfo" "globals" "last_reboot_cause" ""
-	fi
-
-	uci_set "deviceinfo" "${reboot_sec}" "reason" "$(boot_reason_message)"
-}
-
-handle_reboot_action() {
-	local trigger reason max_reboot_entries retry_count reboot_sec_num
-
-	retry_count=0
-
-	# Retry fetching the reset reason file
-	while [ ! -f "${RESET_REASON_PATH}" ] && [ $retry_count -lt $MAX_RETRIES ]; do
-		log "Warning: '${RESET_REASON_PATH}' not found. Attempt $((retry_count + 1)) of ${MAX_RETRIES}"
-		sleep $RETRY_DELAY
-		retry_count=$((retry_count + 1))
-	done
-
-	if [ ! -f "${RESET_REASON_PATH}" ]; then
-		log "Error: '${RESET_REASON_PATH}' is not generated after ${MAX_RETRIES} attempts!!!"
-		return 1
-	fi
-
-	uci_load "deviceinfo"
-
-	trigger=$(get_boot_trigger)
-	reason=$(get_boot_reason)
-
-	# Reset or increment boot counter based on trigger
-	if [ "${trigger}" = "defaultreset" ]; then
-		## Reset all counters ##
-		reset_option_counter "boot_count" "1"
-		reset_option_counter "curr_version_boot_count" "0"
-		reset_option_counter "watchdog_boot_count" "0"
-		reset_option_counter "cold_boot_count" "0"
-		reset_option_counter "warm_boot_count" "0"
-	else
-		# Incrementing boot counter
-		increment_option_counter "boot_count"
-	fi
-
-	# Reset or increment current version boot counter based on trigger
-	if [ "${trigger}" = "upgrade" ]; then
-		# Resetting current version boot counter
-		reset_option_counter "curr_version_boot_count" "1"
-	else
-		# Incrementing current version boot counter
-		increment_option_counter "curr_version_boot_count"
-	fi
-
-	# Increment watchdog boot counter if the reason indicates a watchdog reset
-	if echo "${reason}" | grep -qi "watchdog"; then
-		# Incrementing watchdog boot counter
-		increment_option_counter "watchdog_boot_count"
-	fi
-
-	# Increment cold or warm boot counter based on the reason
-	if [ "${reason}" = "POR_RESET" ]; then
-		increment_option_counter "cold_boot_count"
-	else
-		increment_option_counter "warm_boot_count"
-	fi
-
-	# Get the max reboot entries
-	max_reboot_entries=$(uci_get "deviceinfo" "globals" "max_reboot_entries" "3")
-
-	if [ "${max_reboot_entries}" -eq 0 ]; then
-		# Commit the UCI changes to persist the configuration
-		uci_commit "deviceinfo"
-		return 0
-	fi
-
-	if [ $max_reboot_entries -gt 0 ]; then
-		# Calculate the number of reboot sections in the config
-		reboot_sec_num=$(uci -q show deviceinfo | grep "=reboot" | wc -l)
-
-		# Delete excess reboot sections if they exceed the max reboot entries
-		if [ "${reboot_sec_num}" -ge "${max_reboot_entries}" ]; then
-			local diff=$((reboot_sec_num - max_reboot_entries + 1))
-
-			for i in $(seq 1 $diff); do
-				uci_remove "deviceinfo" "@reboot[0]"
-			done
-		fi
-	fi
-
-	# Create a new reboot section with the current boot information
-	create_reboot_section "${trigger}"
-
-	# Commit the UCI changes to persist the configuration
-	uci_commit "deviceinfo"
-}
-
-# Run the main function
-handle_reboot_action
-exit 0
diff --git a/src/fw_images.c b/src/fw_images.c
index 1f91399..4ea4f3e 100644
--- a/src/fw_images.c
+++ b/src/fw_images.c
@@ -25,10 +25,10 @@ static void _exec_reboot(const void *arg1, void *arg2)
 {
 	char config_name[16] = {0};
 
-	snprintf(config_name, sizeof(config_name), "%s", "deviceinfo");
+	snprintf(config_name, sizeof(config_name), "%s", "sysmngr");
 
 	// Set last_reboot_cause to 'RemoteReboot' because the upcoming reboot will be initiated by USP Operate
-	dmuci_set_value(config_name, "globals", "last_reboot_cause", "RemoteReboot");
+	dmuci_set_value(config_name, "deviceinfo", "last_reboot_cause", "RemoteReboot");
 	dmuci_commit_package(config_name);
 
 	sleep(3);
@@ -40,7 +40,7 @@ static void _exec_reboot(const void *arg1, void *arg2)
 	BBF_ERR("Reboot call failed!!!");
 
 	// Set last_reboot_cause to empty because there is a problem in the system reboot
-	dmuci_set_value(config_name, "globals", "last_reboot_cause", "");
+	dmuci_set_value(config_name, "deviceinfo", "last_reboot_cause", "");
 	dmuci_commit_package(config_name);
 }
 
diff --git a/src/reboots.c b/src/reboots.c
index dc71b8c..8aab709 100644
--- a/src/reboots.c
+++ b/src/reboots.c
@@ -10,6 +10,262 @@
  */
 
 #include "utils.h"
+#include "reboots.h"
+
+#include <uci.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define REBOOT_LOCK_FILE "/tmp/bbf_reboot_handler.lock" // Lock file indicating that the boot action has already been executed
+#define RESET_REASON_PATH "/tmp/reset_reason" // Path to the file containing the reason for the most recent boot/reset
+#define REBOOT_MAX_RETRIES 10 // Maximum number of retries for checking if RESET_REASON_PATH has been generated
+#define REBOOT_RETRY_DELAY 2 // Delay in seconds between retries when checking for the existence of RESET_REASON_PATH
+#define REBOOT_MAX_ENTRIES 32 // Maximum number of reboot entries to display in Device.DeviceInfo.Reboots.Reboot.{i}
+
+static int g_retry_count = 0;
+
+static void reset_option_counter(const char *option_name, const char *option_value)
+{
+	sysmngr_uci_set("sysmngr", "deviceinfo", option_name, option_value);
+}
+
+static void increment_option_counter(const char *option_name)
+{
+	char buf[16] = {0};
+
+	sysmngr_uci_get("sysmngr", "deviceinfo", option_name, "0", buf, sizeof(buf));
+
+	int counter = (int)strtol(buf, NULL, 10) + 1;
+
+	snprintf(buf, sizeof(buf), "%d", counter);
+	sysmngr_uci_set("sysmngr", "deviceinfo", option_name, buf);
+}
+
+static void get_boot_option_value(const char *option_name, char *buffer, size_t buffer_size)
+{
+	char line[256] = {0};
+
+	if (!option_name || !buffer || !buffer_size)
+		return;
+
+	buffer[0] = '\0';
+
+	FILE *file = fopen(RESET_REASON_PATH, "r");
+	if (!file)
+		return;
+
+	while (fgets(line, sizeof(line), file)) {
+		remove_new_line(line);
+
+		if (strstr(line, option_name)) {
+			char *p = strchr(line, ':');
+			snprintf(buffer, buffer_size, "%s", p ? p + 2 : "");
+			break;
+		}
+	}
+
+	fclose(file);
+}
+
+static const char *boot_reason_message(const char *trigger, const char *reason)
+{
+	// Generate a human-readable message based on the boot reason and trigger
+	if (strlen(trigger)) {
+		if (strcmp(trigger, "defaultreset") == 0)
+			return "FACTORY RESET";
+		else if (strcmp(trigger, "upgrade") == 0)
+			return "FIRMWARE UPGRADE";
+		else
+			return trigger;
+	} else if (strlen(reason)) {
+		if (strcmp(reason, "POR_RESET") == 0)
+			return "POWER ON RESET";
+		else
+			return reason;
+	} else {
+		return "Unknown";
+	}
+}
+
+static void calculate_boot_time(char *buffer, size_t buffer_size)
+{
+	// Get current time and uptime in seconds
+	time_t current_time = time(NULL);
+	int uptime = sysmngr_get_uptime();
+
+	// Calculate the boot time by subtracting the uptime from the current time
+	current_time -= uptime;
+
+	struct tm *tm_info = gmtime(&current_time);
+
+	// Convert the boot time to a human-readable format
+	strftime(buffer, buffer_size, "%Y-%m-%dT%H:%M:%SZ", tm_info);
+}
+
+static void delete_excess_reboot_sections(int max_reboot_entries)
+{
+	struct uci_ptr ptr = {0};
+	char uci_str[16] = {0};
+
+	struct uci_context *ctx = uci_alloc_context();
+	if (!ctx) {
+		BBF_ERR("Failed to allocate UCI context.");
+		return;
+	}
+
+	snprintf(uci_str, sizeof(uci_str), "%s", "sysmngr");
+
+	if (uci_lookup_ptr(ctx, &ptr, uci_str, true) != UCI_OK) {
+		BBF_ERR("Failed to lookup for sysmngr config");
+		uci_free_context(ctx);
+		return;
+	}
+
+	int total_reboot_sections = 0;
+	struct uci_element *e, *tmp;
+
+	// First pass to count total reboot sections
+	uci_foreach_element(&ptr.p->sections, e) {
+		struct uci_section *section = uci_to_section(e);
+		if (strcmp(section->type, "reboot") == 0) {
+			total_reboot_sections++;
+		}
+	}
+
+	// Calculate number of sections to remove
+	int sections_to_remove = total_reboot_sections - ((max_reboot_entries > 0) ? max_reboot_entries : REBOOT_MAX_ENTRIES);
+	if (sections_to_remove < 0) {  // No need to delete sections
+		uci_free_context(ctx);
+		return;
+	}
+
+	int removed_count = 0;
+	// Second pass to remove excess sections
+	uci_foreach_element_safe(&ptr.p->sections, tmp, e) {
+		struct uci_section *section = uci_to_section(e);
+
+		if (strcmp(section->type, "reboot") == 0 && removed_count <= sections_to_remove) {
+
+			if (sysmngr_uci_delete(ctx, "sysmngr", section->e.name)) {
+				uci_free_context(ctx);
+				return;
+			}
+
+			removed_count++;
+		}
+	}
+
+	// Commit changes to save deletions
+	if (uci_commit(ctx, &ptr.p, false) != UCI_OK) {
+		BBF_ERR("Failed to commit changes");
+	}
+
+	uci_free_context(ctx);
+}
+
+static void create_reboot_section(const char *trigger, const char *reason)
+{
+	char boot_time[sizeof("0001-01-01T00:00:00Z")] = {0};
+	char sec_name[32] = {0};
+
+	if (!trigger || !reason)
+		return;
+
+	snprintf(sec_name, sizeof(sec_name), "reboot_%ld", time(NULL));
+	calculate_boot_time(boot_time, sizeof(boot_time));
+
+	sysmngr_uci_set("sysmngr", sec_name, NULL, "reboot");
+	sysmngr_uci_set("sysmngr", sec_name, "time_stamp", boot_time);
+	sysmngr_uci_set("sysmngr", sec_name, "firmware_updated", strcmp(trigger, "upgrade") == 0 ? "1" : "0");
+
+	if (strcmp(trigger, "defaultreset") == 0) {
+		sysmngr_uci_set("sysmngr", sec_name, "cause", "FactoryReset");
+	} else {
+		char last_reboot_cause[32] = {0};
+		sysmngr_uci_get("sysmngr", "deviceinfo", "last_reboot_cause", "LocalReboot", last_reboot_cause, sizeof(last_reboot_cause));
+		sysmngr_uci_set("sysmngr", sec_name, "cause", last_reboot_cause);
+		sysmngr_uci_set("sysmngr", "deviceinfo", "last_reboot_cause", "");
+	}
+
+	sysmngr_uci_set("sysmngr", sec_name, "reason", boot_reason_message(trigger, reason));
+}
+
+static void sysmngr_register_boot_action(void)
+{
+	char trigger[16] = {0}, reason[16] = {0}, max_entries[16] = {0};
+
+	// Check if boot action was already executed
+	if (file_exists(REBOOT_LOCK_FILE)) {
+		BBF_INFO("Boot action already completed previously. Skipping registration.");
+		return;
+	}
+
+	get_boot_option_value("triggered", trigger, sizeof(trigger));
+	get_boot_option_value("reason", reason, sizeof(reason));
+
+	if (strcmp(trigger, "defaultreset") == 0) {
+		reset_option_counter("boot_count", "1");
+		reset_option_counter("curr_version_boot_count", "0");
+	} else {
+		increment_option_counter("boot_count");
+		increment_option_counter("curr_version_boot_count");
+	}
+
+	if (strstr(reason, "watchdog")) {
+		increment_option_counter("watchdog_boot_count");
+	}
+
+	if (strcmp(reason, "POR_RESET") == 0) {
+		increment_option_counter("cold_boot_count");
+	} else {
+		increment_option_counter("warm_boot_count");
+	}
+
+	sysmngr_uci_get("sysmngr", "deviceinfo", "max_reboot_entries", "3", max_entries, sizeof(max_entries));
+	int max_reboot_entries = (int)strtol(max_entries, NULL, 10);
+
+	if (max_reboot_entries != 0) {
+		delete_excess_reboot_sections(max_reboot_entries);
+		create_reboot_section(trigger, reason);
+	}
+
+	// Create a lock file to mark boot action as executed
+	create_empty_file(REBOOT_LOCK_FILE);
+}
+
+static void reboot_check_timer(struct uloop_timeout *timeout)
+{
+	sysmngr_reboots_init();
+}
+
+static struct uloop_timeout reboot_timer = { .cb = reboot_check_timer };
+
+/*************************************************************
+* EXTERNAL APIS
+**************************************************************/
+void sysmngr_reboots_init(void)
+{
+	if (file_exists(RESET_REASON_PATH)) {
+		BBF_INFO("Reset reason file '%s' found. Proceeding to register boot action", RESET_REASON_PATH);
+		sysmngr_register_boot_action();
+		return;
+	}
+
+	if (g_retry_count < REBOOT_MAX_RETRIES) {
+		g_retry_count++;
+		uloop_timeout_set(&reboot_timer, REBOOT_RETRY_DELAY * 1000);
+
+		BBF_WARNING("Attempt %d/%d: Reset reason file '%s' not found. Retrying in %d second(s)...",
+		            g_retry_count, REBOOT_MAX_RETRIES, RESET_REASON_PATH, REBOOT_RETRY_DELAY);
+	} else {
+		BBF_ERR("Max retries reached (%d). Reset reason file '%s' not found. Proceeding with boot action registration",
+		        REBOOT_MAX_RETRIES, RESET_REASON_PATH);
+		sysmngr_register_boot_action();
+	}
+}
 
 /*************************************************************
 * ENTRY METHOD
@@ -20,7 +276,7 @@ static int browseDeviceInfoRebootsRebootInst(struct dmctx *dmctx, DMNODE *parent
 	char *inst = NULL;
 	LIST_HEAD(dup_list);
 
-	synchronize_specific_config_sections_with_dmmap("deviceinfo", "reboot", "dmmap_deviceinfo", &dup_list);
+	synchronize_specific_config_sections_with_dmmap("sysmngr", "reboot", "dmmap_sysmngr", &dup_list);
 	list_for_each_entry(p, &dup_list, list) {
 
 		inst = handle_instance(dmctx, parent_node, p->dmmap_section, "reboot_instance", "reboot_alias");
@@ -37,37 +293,37 @@ static int browseDeviceInfoRebootsRebootInst(struct dmctx *dmctx, DMNODE *parent
 **************************************************************/
 static int get_DeviceInfoReboots_BootCount(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	dmuci_get_option_value_string("deviceinfo", "globals", "boot_count", value);
+	dmuci_get_option_value_string("sysmngr", "deviceinfo", "boot_count", value);
 	return 0;
 }
 
 static int get_DeviceInfoReboots_CurrentVersionBootCount(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	dmuci_get_option_value_string("deviceinfo", "globals", "curr_version_boot_count", value);
+	dmuci_get_option_value_string("sysmngr", "deviceinfo", "curr_version_boot_count", value);
 	return 0;
 }
 
 static int get_DeviceInfoReboots_WatchdogBootCount(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	dmuci_get_option_value_string("deviceinfo", "globals", "watchdog_boot_count", value);
+	dmuci_get_option_value_string("sysmngr", "deviceinfo", "watchdog_boot_count", value);
 	return 0;
 }
 
 static int get_DeviceInfoReboots_ColdBootCount(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	dmuci_get_option_value_string("deviceinfo", "globals", "cold_boot_count", value);
+	dmuci_get_option_value_string("sysmngr", "deviceinfo", "cold_boot_count", value);
 	return 0;
 }
 
 static int get_DeviceInfoReboots_WarmBootCount(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	dmuci_get_option_value_string("deviceinfo", "globals", "warm_boot_count", value);
+	dmuci_get_option_value_string("sysmngr", "deviceinfo", "warm_boot_count", value);
 	return 0;
 }
 
 static int get_DeviceInfoReboots_MaxRebootEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	*value = dmuci_get_option_value_fallback_def("deviceinfo", "globals", "max_reboot_entries", "3");
+	*value = dmuci_get_option_value_fallback_def("sysmngr", "deviceinfo", "max_reboot_entries", "3");
 	return 0;
 }
 
@@ -84,24 +340,24 @@ static int set_DeviceInfoReboots_MaxRebootEntries(char *refparam, struct dmctx *
 		case VALUESET:
 			if (max_entries == 0) {
 				// Delete all sections if value is "0"
-				uci_foreach_sections_safe("deviceinfo", "reboot", tmp_s, s) {
+				uci_foreach_sections_safe("sysmngr", "reboot", tmp_s, s) {
 					dmuci_delete_by_section(s, NULL, NULL);
 				}
-			} else if (max_entries > 0) {
+			} else {
 				// Step 1: Count total sections
 				int total_sections = 0;
 
-				uci_foreach_sections_safe("deviceinfo", "reboot", tmp_s, s) {
+				uci_foreach_sections_safe("sysmngr", "reboot", tmp_s, s) {
 					total_sections++;
 				}
 
 				// Step 2: Calculate how many sections to delete (earliest sections)
-				int to_delete = total_sections - max_entries;
+				int to_delete = total_sections - ((max_entries > 0) ? max_entries : REBOOT_MAX_ENTRIES);
 
 				// Step 3: Delete the earliest sections that exceed max_entries
 				if (to_delete > 0) {
 					int idx = 0;
-					uci_foreach_sections_safe("deviceinfo", "reboot", tmp_s, s) {
+					uci_foreach_sections_safe("sysmngr", "reboot", tmp_s, s) {
 						if (idx++ < to_delete) {
 							dmuci_delete_by_section(s, NULL, NULL);
 						}
@@ -109,7 +365,7 @@ static int set_DeviceInfoReboots_MaxRebootEntries(char *refparam, struct dmctx *
 				}
 			}
 
-			dmuci_set_value("deviceinfo", "globals", "max_reboot_entries", value);
+			dmuci_set_value("sysmngr", "deviceinfo", "max_reboot_entries", value);
 			break;
 	}
 	return 0;
@@ -163,7 +419,7 @@ static int operate_DeviceInfoReboots_RemoveAllReboots(char *refparam, struct dmc
 {
 	struct uci_section *s = NULL, *tmp_s = NULL;
 
-	uci_foreach_sections_safe("deviceinfo", "reboot", tmp_s, s) {
+	uci_foreach_sections_safe("sysmngr", "reboot", tmp_s, s) {
 		dmuci_delete_by_section(s, NULL, NULL);
 	}
     return 0;
diff --git a/src/reboots.h b/src/reboots.h
index 5e76a4c..b3320a8 100644
--- a/src/reboots.h
+++ b/src/reboots.h
@@ -15,4 +15,6 @@
 extern DMOBJ tDeviceInfoRebootsObj[];
 extern DMLEAF tDeviceInfoRebootsParams[];
 
+void sysmngr_reboots_init(void);
+
 #endif //__REBOOTS_H
diff --git a/src/sysmngr.c b/src/sysmngr.c
index 360f0ca..d644695 100644
--- a/src/sysmngr.c
+++ b/src/sysmngr.c
@@ -16,6 +16,10 @@
 #include <libubox/uloop.h>
 #include <libbbfdm-ubus/bbfdm-ubus.h>
 
+#ifdef SYSMNGR_REBOOTS
+#include "reboots.h"
+#endif
+
 extern DM_MAP_OBJ tDynamicObj[];
 
 static void usage(char *prog)
@@ -56,6 +60,10 @@ int main(int argc, char **argv)
 
 	openlog("sysmngr", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
 
+#ifdef SYSMNGR_REBOOTS
+	sysmngr_reboots_init();
+#endif
+
 	if (bbfdm_ubus_regiter_init(&bbfdm_ctx))
 		goto out;
 
diff --git a/src/utils.c b/src/utils.c
index 33408d4..2c38b69 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -147,3 +147,103 @@ void send_transfer_complete_event(const char *command, const char *obj_path, con
 
 	blob_buf_free(&bb);
 }
+
+int sysmngr_uci_get(const char *package, const char *section, const char *option, const char *default_value, char *buffer, size_t buffer_size)
+{
+	struct uci_ptr ptr = {0};
+	char uci_str[128] = {0};
+
+	if (!package || !section || !option || !default_value || !buffer || !buffer_size)
+		return -1;
+
+	struct uci_context *ctx = uci_alloc_context();
+	if (!ctx) {
+		BBF_ERR("UCI context allocation failed");
+		return -1;
+	}
+
+	snprintf(uci_str, sizeof(uci_str), "%s.%s.%s", package, section, option);
+
+	if (uci_lookup_ptr(ctx, &ptr, uci_str, true) != UCI_OK || ptr.o == NULL) {
+		snprintf(buffer, buffer_size, "%s", default_value);
+		uci_free_context(ctx);
+		return -1;
+	}
+
+	snprintf(buffer, buffer_size, "%s", ptr.o->v.string);
+	uci_free_context(ctx);
+	return 0;
+}
+
+int sysmngr_uci_set(const char *package, const char *section, const char *option, const char *value)
+{
+	struct uci_ptr ptr = {0};
+	char uci_str[128] = {0};
+
+	if (!package || !section || !value)
+		return -1;
+
+	struct uci_context *ctx = uci_alloc_context();
+	if (!ctx) {
+		BBF_ERR("UCI context allocation failed");
+		return -1;
+	}
+
+	snprintf(uci_str, sizeof(uci_str), "%s.%s%s%s=%s",
+			package,
+			section,
+			option ? "." : "",
+			option ? option : "",
+			value);
+
+	if (uci_lookup_ptr(ctx, &ptr, uci_str, true) != UCI_OK ||
+		uci_set(ctx, &ptr) != UCI_OK ||
+		uci_save(ctx, ptr.p) != UCI_OK ||
+		uci_commit(ctx, &ptr.p, false) != UCI_OK) {
+
+		uci_free_context(ctx);
+		return -1;
+	}
+
+	uci_free_context(ctx);
+	return 0;
+}
+
+int sysmngr_uci_delete(struct uci_context *uci_ctx, const char *package, const char *section)
+{
+	struct uci_ptr ptr = {0};
+	char uci_str[64] = {0};
+
+	if (!package || !section)
+		return -1;
+
+	snprintf(uci_str, sizeof(uci_str), "%s.%s", package, section);
+
+	if (uci_lookup_ptr(uci_ctx, &ptr, uci_str, true) != UCI_OK ||
+		uci_delete(uci_ctx, &ptr) != UCI_OK ||
+		uci_save(uci_ctx, ptr.p) != UCI_OK) {
+
+		return -1;
+	}
+
+	return 0;
+}
+
+int sysmngr_get_uptime(void)
+{
+	// cppcheck-suppress cert-MSC24-C
+	FILE *fp = fopen("/proc/uptime", "r");
+	int uptime = 0;
+
+	if (fp != NULL) {
+		char *pch = NULL, *spch = NULL, buf[64] = {0};
+
+		if (fgets(buf, sizeof(buf), fp) != NULL) {
+			pch = strtok_r(buf, ".", &spch);
+			uptime = (pch) ? (int)strtol(pch, NULL, 10) : 0;
+		}
+		fclose(fp);
+	}
+
+	return uptime;
+}
diff --git a/src/utils.h b/src/utils.h
index 734ea2e..9a937a8 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -21,4 +21,10 @@ bool validate_server_response_code(const char *url, int response_code);
 void send_transfer_complete_event(const char *command, const char *obj_path, const char *transfer_url,
 	char *fault_string, time_t start_t, time_t complete_t, const char *commandKey, const char *transfer_type);
 
+int sysmngr_uci_get(const char *package, const char *section, const char *option, const char *default_value, char *buffer, size_t buffer_size);
+int sysmngr_uci_set(const char *package, const char *section, const char *option, const char *value);
+int sysmngr_uci_delete(struct uci_context *uci_ctx, const char *package, const char *section);
+
+int sysmngr_get_uptime(void);
+
 #endif //__UTILS_H
-- 
GitLab