From c64438b32e5d8e821ce0577ccbaa2e14eb4b1471 Mon Sep 17 00:00:00 2001 From: "Kadiyala, Jayasri" Date: Wed, 6 May 2026 10:17:19 -0700 Subject: [PATCH] Add ismodulerestarted DMCLI parameter to DeviceFingerPrint section Implements a new read-only boolean parameter 'ismodulerestarted' under the Device.DeviceInfo.X_RDKCENTRAL-COM_DeviceFingerPrint TR-181 object. The parameter indicates whether the cujo-agent process has restarted at any point after system boot by comparing its process start time against the system uptime. If the process started more than 300 seconds after boot, it is considered to have been restarted. Resolves #66 --- config/TR181-AdvSecurity.xml | 6 ++ source/AdvSecurityDml/cosa_adv_security_dml.c | 8 ++ .../cosa_adv_security_internal.c | 95 +++++++++++++++++++ .../cosa_adv_security_internal.h | 5 + 4 files changed, 114 insertions(+) diff --git a/config/TR181-AdvSecurity.xml b/config/TR181-AdvSecurity.xml index e705ee4..a8d9a4b 100644 --- a/config/TR181-AdvSecurity.xml +++ b/config/TR181-AdvSecurity.xml @@ -66,6 +66,12 @@ uint32 true + + ismodulerestarted + boolean + bool + false + diff --git a/source/AdvSecurityDml/cosa_adv_security_dml.c b/source/AdvSecurityDml/cosa_adv_security_dml.c index 36da4b6..bddd135 100644 --- a/source/AdvSecurityDml/cosa_adv_security_dml.c +++ b/source/AdvSecurityDml/cosa_adv_security_dml.c @@ -141,6 +141,14 @@ DeviceFingerPrint_GetParamBoolValue return TRUE; } + rc = strcmp_s("ismodulerestarted", strlen("ismodulerestarted"), ParamName, &ind); + ERR_CHK(rc); + if((rc == EOK) && (!ind)) + { + *pBool = CosaAdvSecIsModuleRestarted(); + return TRUE; + } + CcspTraceWarning(("%s: Unsupported parameter '%s'\n", __FUNCTION__, ParamName)); return FALSE; } diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.c b/source/AdvSecurityDml/cosa_adv_security_internal.c index f722552..8bbe585 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.c +++ b/source/AdvSecurityDml/cosa_adv_security_internal.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "safec_lib_common.h" #include "secure_wrapper.h" #include @@ -2277,6 +2278,100 @@ ULONG CosaAdvSecGetLookupTimeoutExceededCount() return lcount; } +BOOL CosaAdvSecIsModuleRestarted() +{ + struct sysinfo info = {0}; + unsigned long uptime_seconds = 0; + unsigned long long starttime_ticks = 0; + unsigned long process_runtime = 0; + long clock_ticks_per_sec = 0; + FILE *fp = NULL; + char buf[COMMAND_MAX] = {0}; + char proc_stat_path[COMMAND_MAX] = {0}; + char pid_buf[MAX_VALUE] = {0}; + int i; + + /* Get system uptime */ + if (sysinfo(&info) != 0) { + CcspTraceError(("%s: sysinfo fetch failed\n", __FUNCTION__)); + return FALSE; + } + uptime_seconds = info.uptime; + + /* Get cujo-agent PID using /var/run/cujo-agent.pid or pidof */ + fp = popen("pidof cujo-agent", "r"); + if (fp == NULL) { + CcspTraceWarning(("%s: Failed to get cujo-agent PID\n", __FUNCTION__)); + return FALSE; + } + if (fgets(pid_buf, sizeof(pid_buf), fp) == NULL) { + pclose(fp); + CcspTraceWarning(("%s: cujo-agent process not found\n", __FUNCTION__)); + return FALSE; + } + pclose(fp); + + /* Remove trailing newline */ + for (i = 0; pid_buf[i] != '\0'; i++) { + if (pid_buf[i] == '\n' || pid_buf[i] == ' ') { + pid_buf[i] = '\0'; + break; + } + } + + /* Read /proc//stat to get process start time (field 22) */ + snprintf(proc_stat_path, sizeof(proc_stat_path), "/proc/%s/stat", pid_buf); + fp = fopen(proc_stat_path, "r"); + if (fp == NULL) { + CcspTraceWarning(("%s: Failed to open %s\n", __FUNCTION__, proc_stat_path)); + return FALSE; + } + if (fgets(buf, sizeof(buf), fp) == NULL) { + fclose(fp); + CcspTraceWarning(("%s: Failed to read %s\n", __FUNCTION__, proc_stat_path)); + return FALSE; + } + fclose(fp); + + /* Parse field 22 (starttime) - skip past the comm field (enclosed in parentheses) */ + { + char *ptr = strrchr(buf, ')'); + if (ptr == NULL) { + CcspTraceWarning(("%s: Failed to parse proc stat\n", __FUNCTION__)); + return FALSE; + } + ptr++; /* move past ')' */ + + /* starttime is field 22; after ')' we are at field 2, need to skip to field 22 */ + /* Fields 3-21 = 19 fields to skip */ + for (i = 0; i < 19; i++) { + while (*ptr == ' ') ptr++; + while (*ptr != ' ' && *ptr != '\0') ptr++; + } + while (*ptr == ' ') ptr++; + starttime_ticks = strtoull(ptr, NULL, 10); + } + + clock_ticks_per_sec = sysconf(_SC_CLK_TCK); + if (clock_ticks_per_sec <= 0) { + CcspTraceWarning(("%s: Failed to get clock ticks per second\n", __FUNCTION__)); + return FALSE; + } + + /* Process runtime = uptime - (starttime / clock_ticks_per_sec) */ + process_runtime = uptime_seconds - (unsigned long)(starttime_ticks / (unsigned long long)clock_ticks_per_sec); + + /* + * If the process runtime is less than system uptime beyond the normal boot + * startup threshold (300 seconds), the module was restarted after boot. + */ + if ((uptime_seconds - process_runtime) > 300) { + return TRUE; + } + + return FALSE; +} + static BOOL AdvsecSysEventHandlerStarted=FALSE; static int sysevent_fd = 0; static token_t sysEtoken; diff --git a/source/AdvSecurityDml/cosa_adv_security_internal.h b/source/AdvSecurityDml/cosa_adv_security_internal.h index 9fcce96..b32f348 100644 --- a/source/AdvSecurityDml/cosa_adv_security_internal.h +++ b/source/AdvSecurityDml/cosa_adv_security_internal.h @@ -270,6 +270,11 @@ CosaAdvSecGetLookupTimeoutExceededCount ( ); +BOOL +CosaAdvSecIsModuleRestarted + ( + ); + ANSC_STATUS CosaStartAdvParentalControl (