Handle Reboots Object internally

This commit is contained in:
Amin Ben Romdhane 2024-10-31 11:42:58 +01:00
parent 91b7aa9e38
commit 99e62413e1
8 changed files with 404 additions and 238 deletions

View file

@ -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'

View file

@ -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

View file

@ -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);
}

View file

@ -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;

View file

@ -15,4 +15,6 @@
extern DMOBJ tDeviceInfoRebootsObj[];
extern DMLEAF tDeviceInfoRebootsParams[];
void sysmngr_reboots_init(void);
#endif //__REBOOTS_H

View file

@ -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;

View file

@ -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;
}

View file

@ -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