From ff129f75b9266e2b3cdc6bb319b12378f9ec5511 Mon Sep 17 00:00:00 2001 From: Amin Ben Romdhane <amin.benromdhane@iopsys.eu> Date: Mon, 25 Nov 2024 08:49:07 +0000 Subject: [PATCH] Added support for DeviceInfo.ProcessStatus.CPU. Object --- src/memory.c | 49 +--- src/processes.c | 605 +++++++++++++++++++++++++++++++++++++++++++++++- src/processes.h | 2 + src/sysmngr.c | 26 ++- src/utils.c | 19 ++ src/utils.h | 2 + 6 files changed, 641 insertions(+), 62 deletions(-) diff --git a/src/memory.c b/src/memory.c index c238064..40cfd13 100644 --- a/src/memory.c +++ b/src/memory.c @@ -120,47 +120,6 @@ static void send_memory_critical_state_event(unsigned int mem_utilization) blob_buf_free(&bb); } -void generate_log_file(time_t log_time, bool critical_state) -{ - FILE *log_file = fopen(g_memory_ctx.log_file, "w"); // Write mode, clears log each time - if (log_file == NULL) { - BBF_ERR("Failed to open log file at '%s'", g_memory_ctx.log_file); - return; - } - - fprintf(log_file, "=== Memory Critical State %s at %s ===\n", - critical_state ? "Reached" : "no longer present", ctime(&log_time)); - - const char *commands[] = { - "top -b -n 1", // Shows a snapshot of top output, including CPU and memory stats - "free", // Shows memory usage statistics in a human-readable format - NULL - }; - - for (int i = 0; commands[i] != NULL; i++) { - FILE *cmd_output = popen(commands[i], "r"); // flawfinder: ignore - if (cmd_output == NULL) { - fprintf(log_file, "Failed to execute command: %s\n", commands[i]); - BBF_ERR("Failed to execute system command: %s", commands[i]); - continue; - } - - fprintf(log_file, "\nOutput of command: %s\n", commands[i]); - char buffer[256]; - while (fgets(buffer, sizeof(buffer), cmd_output) != NULL) { - fprintf(log_file, "%s", buffer); - } - - pclose(cmd_output); - fprintf(log_file, "\n"); - } - - fprintf(log_file, "=== End of Critical Memory Log ===\n\n"); - fclose(log_file); - - BBF_DEBUG("Generated memory log file at: '%s'", g_memory_ctx.log_file); -} - static void run_memory_monitor(void) { unsigned int mem_utilization = calculate_memory_utilization(); @@ -169,7 +128,7 @@ static void run_memory_monitor(void) if ((mem_utilization > g_memory_ctx.critical_rise_threshold) && (g_memory_ctx.critical_fall_time >= g_memory_ctx.critical_rise_time)) { - BBF_INFO("Memory utilization reached critical threshold: %u%%", mem_utilization); + BBF_ERR("Memory utilization reached critical threshold: %u%% !!!!!!!!", mem_utilization); // Update CriticalRiseTimeStamp to the current time g_memory_ctx.critical_rise_time = time(NULL); @@ -178,7 +137,7 @@ static void run_memory_monitor(void) if (g_memory_ctx.enable_critical_log) { // Generate log into the vendor log file referenced by 'VendorLogFileRef' parameter indicating critical condition is reached - generate_log_file(g_memory_ctx.critical_rise_time, true); + sysmngr_generate_critical_log_file(g_memory_ctx.log_file, "Memory", true); } // Send 'MemoryCriticalState!' event @@ -188,7 +147,7 @@ static void run_memory_monitor(void) if ((mem_utilization < g_memory_ctx.critical_fall_threshold) && (g_memory_ctx.critical_rise_time > g_memory_ctx.critical_fall_time)) { - BBF_INFO("Memory utilization has fallen below critical threshold: %u%%", mem_utilization); + BBF_ERR("Memory utilization has fallen below critical threshold: %u%% !!!!!!!!", mem_utilization); // Update CriticalFallTimeStamp to the current time g_memory_ctx.critical_fall_time = time(NULL); @@ -197,7 +156,7 @@ static void run_memory_monitor(void) if (g_memory_ctx.enable_critical_log) { // Generate log into the vendor log file referenced by 'VendorLogFileRef' parameter indicating that the critical condition is no longer present - generate_log_file(g_memory_ctx.critical_fall_time, false); + sysmngr_generate_critical_log_file(g_memory_ctx.log_file, "Memory", false); } } diff --git a/src/processes.c b/src/processes.c index 1025be5..d2d14bc 100644 --- a/src/processes.c +++ b/src/processes.c @@ -12,6 +12,13 @@ #include "utils.h" #include "processes.h" +#define DEFAULT_CPU_NAME "cpu" +#define DEFAULT_CPU_POLL_INTERVAL "5" +#define DEFAULT_CPU_NUM_SAMPLES "30" +#define DEFAULT_CPU_CRITICAL_RISE_THRESHOLD "80" +#define DEFAULT_CPU_CRITICAL_FALL_THRESHOLD "60" +#define DEFAULT_CPU_CRITICAL_LOG_PATH "/var/log/critical_cpu.log" + typedef struct process_entry { struct list_head list; @@ -26,8 +33,10 @@ typedef struct process_entry { typedef struct jiffy_counts_t { unsigned long long usr, nic, sys, idle; unsigned long long iowait, irq, softirq, steal; - unsigned long long total; - unsigned long long busy; + unsigned long long total_time; + unsigned long long idle_time; + unsigned long long sys_time; + unsigned long long busy_time; } jiffy_counts_t; typedef struct process_ctx { @@ -38,7 +47,28 @@ typedef struct process_ctx { int process_num; } process_ctx; +typedef struct cpu_info { + struct uloop_timeout cpu_timer; + jiffy_counts_t jiffy; + bool enable; + bool enable_critical_log; + bool full_samples_reached; + unsigned int poll_interval; + unsigned int critical_rise_threshold; + unsigned int critical_fall_threshold; + unsigned int *user_utilization_samples; + unsigned int *system_utilization_samples; + unsigned int *idle_utilization_samples; + unsigned int *utilization_samples; + unsigned int num_samples; + time_t critical_rise_time; + time_t critical_fall_time; + size_t sample_index; + char log_file[512]; +} cpu_info_t; + static process_ctx g_process_ctx = {0}; +static cpu_info_t g_cpu_info = {0}; /************************************************************* * COMMON FUNCTIONS @@ -57,10 +87,14 @@ static void get_jif_val(jiffy_counts_t *p_jif) &p_jif->iowait, &p_jif->irq, &p_jif->softirq, &p_jif->steal); if (ret >= 4) { - p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle + p_jif->total_time = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle + p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal; - p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait; + p_jif->sys_time = p_jif->sys + p_jif->irq + p_jif->softirq; + + p_jif->idle_time = p_jif->idle + p_jif->iowait; + + p_jif->busy_time = p_jif->total_time - p_jif->idle_time; break; } } @@ -72,12 +106,12 @@ static unsigned int get_cpu_load(jiffy_counts_t *prev_jif, jiffy_counts_t *cur_j { unsigned total_diff, cpu; - total_diff = (unsigned)(cur_jif->total - prev_jif->total); + total_diff = (unsigned)(cur_jif->total_time - prev_jif->total_time); if (total_diff == 0) total_diff = 1; - cpu = 100 * (unsigned)(cur_jif->busy - prev_jif->busy) / total_diff; + cpu = 100 * (unsigned)(cur_jif->busy_time - prev_jif->busy_time) / total_diff; return cpu; } @@ -349,6 +383,200 @@ static void process_refresh_instance_timer(struct uloop_timeout *timeout) } } +static void send_cpu_critical_state_event(unsigned int cpu_utilization) +{ + struct blob_buf bb = {0}; + char buf[32] = {0}; + + snprintf(buf, sizeof(buf), "%u", cpu_utilization); + + memset(&bb, 0, sizeof(struct blob_buf)); + blob_buf_init(&bb, 0); + + blobmsg_add_string(&bb, "name", "Device.DeviceInfo.ProcessStatus.CPU.1.CPUCriticalState!"); + + void *arr = blobmsg_open_array(&bb, "input"); + + void *cpu_table = blobmsg_open_table(&bb, NULL); + blobmsg_add_string(&bb, "path", "CPUUtilization"); + blobmsg_add_string(&bb, "data", buf); + blobmsg_add_string(&bb, "type", DMT_TYPE[DMT_UNINT]); + blobmsg_close_table(&bb, cpu_table); + + void *name_table = blobmsg_open_table(&bb, NULL); + blobmsg_add_string(&bb, "path", "Name"); + blobmsg_add_string(&bb, "data", DEFAULT_CPU_NAME); + blobmsg_add_string(&bb, "type", DMT_TYPE[DMT_STRING]); + blobmsg_close_table(&bb, name_table); + + blobmsg_close_array(&bb, arr); + + if (sysmngr_ubus_invoke_sync("bbfdm", "notify_event", bb.head, NULL, NULL)) { + BBF_ERR("Failed to send 'CPUCriticalState!' event"); + } else { + BBF_DEBUG("'CPUCriticalState!' event sent successfully with utilization at %u%%.", cpu_utilization); + } + + blob_buf_free(&bb); +} + +static unsigned int calculate_average_samples(unsigned int *samples) +{ + unsigned int num_samples = g_cpu_info.full_samples_reached ? g_cpu_info.num_samples : g_cpu_info.sample_index; + unsigned int sum = 0; + + for (size_t i = 0; i < num_samples; i++) { + sum += samples[i]; + } + + return num_samples ? (sum / num_samples) : 0; +} + +static void run_cpu_monitor(void) +{ + char buf[32] = {0}; + + jiffy_counts_t prev_jiffy = { + .total_time = g_cpu_info.jiffy.total_time, + .idle_time = g_cpu_info.jiffy.idle_time, + .sys_time = g_cpu_info.jiffy.sys_time, + .busy_time = g_cpu_info.jiffy.busy_time, + .usr = g_cpu_info.jiffy.usr + }; + + get_jif_val(&g_cpu_info.jiffy); + + unsigned long long total_diff = g_cpu_info.jiffy.total_time - prev_jiffy.total_time; + + if (total_diff == 0) + total_diff = 1; + + g_cpu_info.user_utilization_samples[g_cpu_info.sample_index] = ((g_cpu_info.jiffy.usr - prev_jiffy.usr) * 100) / total_diff; + g_cpu_info.system_utilization_samples[g_cpu_info.sample_index] = ((g_cpu_info.jiffy.sys_time - prev_jiffy.sys_time) * 100) / total_diff; + g_cpu_info.idle_utilization_samples[g_cpu_info.sample_index] = ((g_cpu_info.jiffy.idle_time - prev_jiffy.idle_time) * 100) / total_diff; + g_cpu_info.utilization_samples[g_cpu_info.sample_index] = ((g_cpu_info.jiffy.busy_time - prev_jiffy.busy_time) * 100) / total_diff; + + if (!g_cpu_info.full_samples_reached) { + g_cpu_info.full_samples_reached = ((g_cpu_info.sample_index + 1) >= g_cpu_info.num_samples); + } + + g_cpu_info.sample_index = (g_cpu_info.sample_index + 1) % g_cpu_info.num_samples; + + unsigned int avg_utilization = calculate_average_samples(g_cpu_info.utilization_samples); + + if ((avg_utilization > g_cpu_info.critical_rise_threshold) && + (g_cpu_info.critical_fall_time >= g_cpu_info.critical_rise_time)) { + + BBF_ERR("CPU utilization reached critical threshold: %u%% !!!!!!!!", avg_utilization); + + // Update CriticalRiseTimeStamp to the current time + g_cpu_info.critical_rise_time = time(NULL); + snprintf(buf, sizeof(buf), "%ld", (long int)g_cpu_info.critical_rise_time); + sysmngr_uci_set("sysmngr", "cpu", "critical_rise_time", buf); + + if (g_cpu_info.enable_critical_log) { + // Generate log into the vendor log file referenced by 'VendorLogFileRef' parameter indicating critical condition is reached + sysmngr_generate_critical_log_file(g_cpu_info.log_file, "CPU", true); + } + + // Send 'CPUCriticalState!' event + send_cpu_critical_state_event(avg_utilization); + } + + if ((avg_utilization < g_cpu_info.critical_fall_threshold) && + (g_cpu_info.critical_rise_time > g_cpu_info.critical_fall_time)) { + + BBF_ERR("CPU utilization has fallen below critical threshold: %u%% !!!!!!!!", avg_utilization); + + // Update CriticalFallTimeStamp to the current time + g_cpu_info.critical_fall_time = time(NULL); + snprintf(buf, sizeof(buf), "%ld", (long int)g_cpu_info.critical_fall_time); + sysmngr_uci_set("sysmngr", "cpu", "critical_fall_time", buf); + + if (g_cpu_info.enable_critical_log) { + // Generate log into the vendor log file referenced by 'VendorLogFileRef' parameter indicating that the critical condition is no longer present + sysmngr_generate_critical_log_file(g_cpu_info.log_file, "CPU", false); + } + } + + BBF_INFO("Next memory monitor check scheduled in %d sec...", g_cpu_info.poll_interval); + uloop_timeout_set(&g_cpu_info.cpu_timer, g_cpu_info.poll_interval * 1000); +} + +static void cpu_timer_callback(struct uloop_timeout *timeout) +{ + run_cpu_monitor(); +} + +static int fill_global_cpu_info(void) +{ + char buf[16] = {0}; + + memset(&g_cpu_info, 0, sizeof(struct cpu_info)); + + g_cpu_info.cpu_timer.cb = cpu_timer_callback; + + sysmngr_uci_get("sysmngr", "cpu", "enable", "0", buf, sizeof(buf)); + g_cpu_info.enable = ((int)strtol(buf, NULL, 10) != 0); + BBF_DEBUG("Memory Monitor Config: |Enable| |%d|", g_cpu_info.enable); + + sysmngr_uci_get("sysmngr", "cpu", "enable_critical_log", "0", buf, sizeof(buf)); + g_cpu_info.enable_critical_log = ((int)strtol(buf, NULL, 10) != 0); + BBF_DEBUG("Memory Monitor Config: |EnableCriticalLog| |%d|", g_cpu_info.enable_critical_log); + + sysmngr_uci_get("sysmngr", "cpu", "poll_interval", DEFAULT_CPU_POLL_INTERVAL, buf, sizeof(buf)); + g_cpu_info.poll_interval = strtoul(buf, NULL, 10); + BBF_DEBUG("Memory Monitor Config: |PollInterval| |%lu|", g_cpu_info.poll_interval); + + sysmngr_uci_get("sysmngr", "cpu", "num_samples", DEFAULT_CPU_NUM_SAMPLES, buf, sizeof(buf)); + g_cpu_info.num_samples = strtoul(buf, NULL, 10); + BBF_DEBUG("Memory Monitor Config: |NumSamples| |%lu|", g_cpu_info.num_samples); + + sysmngr_uci_get("sysmngr", "cpu", "critical_rise_threshold", DEFAULT_CPU_CRITICAL_RISE_THRESHOLD, buf, sizeof(buf)); + g_cpu_info.critical_rise_threshold = strtoul(buf, NULL, 10); + BBF_DEBUG("Memory Monitor Config: |CriticalRiseThreshold| |%lu|", g_cpu_info.critical_rise_threshold); + + sysmngr_uci_get("sysmngr", "cpu", "critical_fall_threshold", DEFAULT_CPU_CRITICAL_FALL_THRESHOLD, buf, sizeof(buf)); + g_cpu_info.critical_fall_threshold = strtoul(buf, NULL, 10); + BBF_DEBUG("Memory Monitor Config: |CriticalFallThreshold| |%lu|", g_cpu_info.critical_fall_threshold); + + sysmngr_uci_get("sysmngr", "cpu", "critical_rise_time", "0", buf, sizeof(buf)); + g_cpu_info.critical_rise_time = strtol(buf, NULL, 10); + BBF_DEBUG("Memory Monitor Config: |CriticalRiseTimeStamp| |%lu|", g_cpu_info.critical_rise_time); + + sysmngr_uci_get("sysmngr", "cpu", "critical_fall_time", "0", buf, sizeof(buf)); + g_cpu_info.critical_fall_time = strtol(buf, NULL, 10); + BBF_DEBUG("Memory Monitor Config: |CriticalFallTimeStamp| |%lu|", g_cpu_info.critical_fall_time); + + sysmngr_uci_get("sysmngr", "cpu", "file_path", DEFAULT_CPU_CRITICAL_LOG_PATH, g_cpu_info.log_file, sizeof(g_cpu_info.log_file)); + BBF_DEBUG("Memory Monitor Config: |FilePath| |%s|", g_cpu_info.log_file); + if (!file_exists(g_cpu_info.log_file)) { + // Create empty file if it doesn't exist + create_empty_file(g_cpu_info.log_file); + } + + g_cpu_info.utilization_samples = calloc(g_cpu_info.num_samples, sizeof(unsigned int)); + g_cpu_info.user_utilization_samples = calloc(g_cpu_info.num_samples, sizeof(unsigned int)); + g_cpu_info.system_utilization_samples = calloc(g_cpu_info.num_samples, sizeof(unsigned int)); + g_cpu_info.idle_utilization_samples = calloc(g_cpu_info.num_samples, sizeof(unsigned int)); + if (!g_cpu_info.utilization_samples || !g_cpu_info.user_utilization_samples || + !g_cpu_info.system_utilization_samples || !g_cpu_info.idle_utilization_samples) { + BBF_ERR("Failed to allocate memory for mode utilization samples"); + return -1; + } + + get_jif_val(&g_cpu_info.jiffy); + return 0; +} + +static void free_global_cpu_info(void) +{ + FREE(g_cpu_info.utilization_samples); + FREE(g_cpu_info.user_utilization_samples); + FREE(g_cpu_info.system_utilization_samples); + FREE(g_cpu_info.idle_utilization_samples); +} + /************************************************************* * EXTERNAL APIS **************************************************************/ @@ -369,6 +597,32 @@ void sysmngr_process_clean(struct ubus_context *ubus_ctx) uloop_timeout_cancel(&g_process_ctx.instance_timer); } +void sysmngr_cpu_init(void) +{ + int res = fill_global_cpu_info(); + if (res) { + BBF_ERR("Can't start CPU monitoring!!"); + return; + } + + if (!g_cpu_info.enable) { + BBF_INFO("CPU monitoring is disabled."); + return; + } else { + BBF_INFO("CPU monitoring is enabled"); + } + + BBF_INFO("Next CPU monitor check scheduled in %d sec...", g_cpu_info.poll_interval); + uloop_timeout_set(&g_cpu_info.cpu_timer, g_cpu_info.poll_interval * 1000); +} + +void sysmngr_cpu_clean(void) +{ + free_global_cpu_info(); + uloop_timeout_cancel(&g_cpu_info.cpu_timer); + BBF_INFO("CPU monitoring process stopped"); +} + /************************************************************* * ENTRY METHOD **************************************************************/ @@ -397,12 +651,35 @@ static int browseProcessEntriesInst(struct dmctx *dmctx, DMNODE *parent_node, vo return 0; } +static int browseDeviceInfoProcessStatusCPUInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance) +{ + struct dm_data data = {0}; + + struct uci_section *s = is_dmmap_section_exist("dmmap_sysmngr", "cpu"); + if (!s) dmuci_add_section_bbfdm("dmmap_sysmngr", "cpu", &s); + + data.dmmap_section = s; + + handle_instance(dmctx, parent_node, s, "cpu_instance", "cpu_alias"); + DM_LINK_INST_OBJ(dmctx, parent_node, &data, "1"); + return 0; + + +} + /************************************************************* * GET & SET PARAM **************************************************************/ static int get_process_cpu_usage(char* refparam, struct dmctx *ctx, void *data, char *instance, char **value) { - dmasprintf(value, "%u", get_cpu_usage()); + char *cpu_monitor_enable = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "enable", "0"); + + if (DM_STRCMP(cpu_monitor_enable, "1") == 0) { + dmasprintf(value, "%u", calculate_average_samples(g_cpu_info.utilization_samples)); + } else { + dmasprintf(value, "%u", get_cpu_usage()); + } + return 0; } @@ -413,6 +690,13 @@ static int get_process_number_of_entries(char* refparam, struct dmctx *ctx, void return 0; } +static int get_DeviceInfoProcessStatus_CPUNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + int cnt = get_number_of_entries(ctx, data, instance, browseDeviceInfoProcessStatusCPUInst); + dmasprintf(value, "%d", cnt); + return 0; +} + static int get_process_pid(char* refparam, struct dmctx *ctx, void *data, char *instance, char **value) { *value = data ? ((struct process_entry *)((struct dm_data *)data)->additional_data)->pid : ""; @@ -449,12 +733,287 @@ static int get_process_state(char* refparam, struct dmctx *ctx, void *data, char return 0; } +static int get_DeviceInfoProcessStatusCPU_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "cpu_alias", instance, value); +} + +static int set_DeviceInfoProcessStatusCPU_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + return bbf_set_alias(ctx, ((struct dm_data *)data)->dmmap_section, "cpu_alias", instance, value); +} + +static int get_DeviceInfoProcessStatusCPU_Name(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmstrdup(DEFAULT_CPU_NAME); + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "enable", "0"); + return 0; +} + +static int set_DeviceInfoProcessStatusCPU_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + bool b; + + switch (action) { + case VALUECHECK: + if (bbfdm_validate_boolean(ctx, value)) + return FAULT_9007; + break; + case VALUESET: + string_to_bool(value, &b); + dmuci_set_value("sysmngr", "cpu", "enable", b ? "1" : "0"); + break; + } + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_UpTime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmasprintf(value, "%d", sysmngr_get_uptime()); + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_UserModeUtilization(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmasprintf(value, "%u", calculate_average_samples(g_cpu_info.user_utilization_samples)); + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_SystemModeUtilization(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmasprintf(value, "%u", calculate_average_samples(g_cpu_info.system_utilization_samples)); + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_IdleModeUtilization(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmasprintf(value, "%u", calculate_average_samples(g_cpu_info.idle_utilization_samples)); + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_CPUUtilization(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmasprintf(value, "%u", calculate_average_samples(g_cpu_info.utilization_samples)); + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_PollInterval(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "poll_interval", DEFAULT_CPU_POLL_INTERVAL); + return 0; +} + +static int set_DeviceInfoProcessStatusCPU_PollInterval(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,NULL}}, 1)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value("sysmngr", "cpu", "poll_interval", value); + break; + } + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_NumSamples(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "num_samples", DEFAULT_CPU_NUM_SAMPLES); + return 0; +} + +static int set_DeviceInfoProcessStatusCPU_NumSamples(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"1","300"}}, 1)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value("sysmngr", "cpu", "num_samples", value); + break; + } + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_CriticalRiseThreshold(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "critical_rise_threshold", DEFAULT_CPU_CRITICAL_RISE_THRESHOLD); + return 0; +} + +static int set_DeviceInfoProcessStatusCPU_CriticalRiseThreshold(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,"100"}}, 1)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value("sysmngr", "cpu", "critical_rise_threshold", value); + break; + } + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_CriticalFallThreshold(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "critical_fall_threshold", DEFAULT_CPU_CRITICAL_FALL_THRESHOLD); + return 0; +} + +static int set_DeviceInfoProcessStatusCPU_CriticalFallThreshold(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + switch (action) { + case VALUECHECK: + if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,"100"}}, 1)) + return FAULT_9007; + break; + case VALUESET: + dmuci_set_value("sysmngr", "cpu", "critical_fall_threshold", value); + break; + } + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_CriticalRiseTimeStamp(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + char *rise_time = NULL; + + dmuci_get_option_value_string("sysmngr", "cpu", "critical_rise_time", &rise_time); + + return dm_time_utc_format(DM_STRTOL(rise_time), value); +} + +static int get_DeviceInfoProcessStatusCPU_CriticalFallTimeStamp(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + char *fall_time = NULL; + + dmuci_get_option_value_string("sysmngr", "cpu", "critical_fall_time", &fall_time); + + return dm_time_utc_format(DM_STRTOL(fall_time), value); +} + +static int get_DeviceInfoProcessStatusCPU_EnableCriticalLog(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "enable_critical_log", "0"); + return 0; +} + +static int set_DeviceInfoProcessStatusCPU_EnableCriticalLog(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + bool b; + + switch (action) { + case VALUECHECK: + if (bbfdm_validate_boolean(ctx, value)) + return FAULT_9007; + break; + case VALUESET: + string_to_bool(value, &b); + dmuci_set_value("sysmngr", "cpu", "enable_critical_log", b ? "1" : "0"); + break; + } + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_VendorLogFileRef(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + char *file_path = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "file_path", DEFAULT_CPU_CRITICAL_LOG_PATH); + + if (file_exists(file_path)) { + char file_uri[512] = {0}; + + // if there is a path, then prepend file:// to it to comply with bbf requirement of file URI + snprintf(file_uri, sizeof(file_uri), "file://%s", file_path); + + // get the vendor file path + _bbfdm_get_references(ctx, "Device.DeviceInfo.VendorLogFile.", "Name", file_uri, value); + } + return 0; +} + +static int get_DeviceInfoProcessStatusCPU_FilePath(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "file_path", DEFAULT_CPU_CRITICAL_LOG_PATH); + return 0; +} + +static int set_DeviceInfoProcessStatusCPU_FilePath(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + char *file_path = dmuci_get_option_value_fallback_def("sysmngr", "cpu", "file_path", DEFAULT_CPU_CRITICAL_LOG_PATH); + + switch (action) { + case VALUECHECK: + if (bbfdm_validate_string(ctx, value, -1, -1, NULL, NULL)) + return FAULT_9007; + + // Restriction: The path in `value` must either: + // - Start with "/var/log" for non-persistent logs, or + // - Start with "/log/" for persistent logs. + // Additionally, the path should not contain any '..' sequences + // to prevent directory traversal or invalid file paths. + if (!((strncmp(value, "/var/log", 8) == 0 || strncmp(value, "/log/", 5) == 0) && !strstr(value, ".."))) { + bbfdm_set_fault_message(ctx, ""); + return FAULT_9007; + } + + break; + case VALUESET: + if (file_exists(file_path)) { + struct uci_section *dmmap_sec = NULL; + char file_uri[512] = {0}; + + if (rename(file_path, value) != 0) { + bbfdm_set_fault_message(ctx, "Can't rename file from '%s' -> '%s'", file_path, value); + return FAULT_9007; + } + + // Update VendorLogFile dmmap section + snprintf(file_uri, sizeof(file_uri), "file://%s", file_path); + dmmap_sec = get_dup_section_in_dmmap_opt("dmmap", "vendorlog", "log_file", file_uri); + + snprintf(file_uri, sizeof(file_uri), "file://%s", value); + dmuci_set_value_by_section(dmmap_sec, "log_file", file_uri); + } + + dmuci_set_value("sysmngr", "cpu", "file_path", value); + break; + } + return 0; +} + +/************************************************************* + * EVENTS + *************************************************************/ +static event_args CPUCriticalState_event_args = { + .name = "", // This field is left empty because we are not listening to any external events, The system now operates within a single unified daemon, + // removing the need for separate event listeners. See send_cpu_critical_state_event API for details on implementation. + .param = (const char *[]) { + "CPUUtilization", + "Name", + NULL + } +}; + +static int get_event_CPUCriticalState(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = (char *)&CPUCriticalState_event_args; + return 0; +} + /********************************************************************************************************************************** * OBJ & LEAF DEFINITION ***********************************************************************************************************************************/ /* *** Device.DeviceInfo.ProcessStatus.Process.{i}. *** */ DMLEAF tDeviceInfoProcessStatusProcessParams[] = { -/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/ +/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */ {"PID", &DMREAD, DMT_UNINT, get_process_pid, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE}, {"Command", &DMREAD, DMT_STRING, get_process_command, NULL, BBFDM_BOTH}, {"Size", &DMREAD, DMT_UNINT, get_process_size, NULL, BBFDM_BOTH}, @@ -464,16 +1023,42 @@ DMLEAF tDeviceInfoProcessStatusProcessParams[] = { {0} }; +/* *** Device.DeviceInfo.ProcessStatus.CPU.{i}. *** */ +DMLEAF tDeviceInfoProcessStatusCPUParams[] = { +/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */ +{"Alias", &DMWRITE, DMT_STRING, get_DeviceInfoProcessStatusCPU_Alias, set_DeviceInfoProcessStatusCPU_Alias, BBFDM_BOTH}, +{"Name", &DMREAD, DMT_STRING, get_DeviceInfoProcessStatusCPU_Name, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER}, +{"Enable", &DMWRITE, DMT_BOOL, get_DeviceInfoProcessStatusCPU_Enable, set_DeviceInfoProcessStatusCPU_Enable, BBFDM_BOTH}, +{"UpTime", &DMREAD, DMT_UNINT, get_DeviceInfoProcessStatusCPU_UpTime, NULL, BBFDM_BOTH}, +{"UserModeUtilization", &DMREAD, DMT_UNINT, get_DeviceInfoProcessStatusCPU_UserModeUtilization, NULL, BBFDM_BOTH}, +{"SystemModeUtilization", &DMREAD, DMT_UNINT, get_DeviceInfoProcessStatusCPU_SystemModeUtilization, NULL, BBFDM_BOTH}, +{"IdleModeUtilization", &DMREAD, DMT_UNINT, get_DeviceInfoProcessStatusCPU_IdleModeUtilization, NULL, BBFDM_BOTH}, +{"CPUUtilization", &DMREAD, DMT_UNINT, get_DeviceInfoProcessStatusCPU_CPUUtilization, NULL, BBFDM_BOTH}, +{"PollInterval", &DMWRITE, DMT_UNINT, get_DeviceInfoProcessStatusCPU_PollInterval, set_DeviceInfoProcessStatusCPU_PollInterval, BBFDM_BOTH}, +{"NumSamples", &DMWRITE, DMT_UNINT, get_DeviceInfoProcessStatusCPU_NumSamples, set_DeviceInfoProcessStatusCPU_NumSamples, BBFDM_BOTH}, +{"CriticalRiseThreshold", &DMWRITE, DMT_UNINT, get_DeviceInfoProcessStatusCPU_CriticalRiseThreshold, set_DeviceInfoProcessStatusCPU_CriticalRiseThreshold, BBFDM_BOTH}, +{"CriticalFallThreshold", &DMWRITE, DMT_UNINT, get_DeviceInfoProcessStatusCPU_CriticalFallThreshold, set_DeviceInfoProcessStatusCPU_CriticalFallThreshold, BBFDM_BOTH}, +{"CriticalRiseTimeStamp", &DMREAD, DMT_TIME, get_DeviceInfoProcessStatusCPU_CriticalRiseTimeStamp, NULL, BBFDM_BOTH}, +{"CriticalFallTimeStamp", &DMREAD, DMT_TIME, get_DeviceInfoProcessStatusCPU_CriticalFallTimeStamp, NULL, BBFDM_BOTH}, +{"EnableCriticalLog", &DMWRITE, DMT_BOOL, get_DeviceInfoProcessStatusCPU_EnableCriticalLog, set_DeviceInfoProcessStatusCPU_EnableCriticalLog, BBFDM_BOTH}, +{"VendorLogFileRef", &DMREAD, DMT_STRING, get_DeviceInfoProcessStatusCPU_VendorLogFileRef, NULL, BBFDM_BOTH}, +{"FilePath", &DMWRITE, DMT_STRING, get_DeviceInfoProcessStatusCPU_FilePath, set_DeviceInfoProcessStatusCPU_FilePath, BBFDM_BOTH}, +{"CPUCriticalState!", &DMREAD, DMT_EVENT, get_event_CPUCriticalState, NULL, BBFDM_USP}, +{0} +}; + /* *** Device.DeviceInfo.ProcessStatus. *** */ DMOBJ tDeviceInfoProcessStatusObj[] = { -/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/ +/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys */ {"Process", &DMREAD, NULL, NULL, NULL, browseProcessEntriesInst, NULL, NULL, NULL, tDeviceInfoProcessStatusProcessParams, NULL, BBFDM_BOTH, NULL}, +{"CPU", &DMREAD, NULL, NULL, NULL, browseDeviceInfoProcessStatusCPUInst, NULL, NULL, NULL, tDeviceInfoProcessStatusCPUParams, NULL, BBFDM_BOTH, NULL}, {0} }; DMLEAF tDeviceInfoProcessStatusParams[] = { -/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/ +/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */ {"CPUUsage", &DMREAD, DMT_UNINT, get_process_cpu_usage, NULL, BBFDM_BOTH}, {"ProcessNumberOfEntries", &DMREAD, DMT_UNINT, get_process_number_of_entries, NULL, BBFDM_BOTH}, +{"CPUNumberOfEntries", &DMREAD, DMT_UNINT, get_DeviceInfoProcessStatus_CPUNumberOfEntries, NULL, BBFDM_BOTH}, {0} }; diff --git a/src/processes.h b/src/processes.h index 8c6da5a..eeb50ba 100644 --- a/src/processes.h +++ b/src/processes.h @@ -17,5 +17,7 @@ extern DMLEAF tDeviceInfoProcessStatusParams[]; void sysmngr_process_init(struct ubus_context *ubus_ctx); void sysmngr_process_clean(struct ubus_context *ubus_ctx); +void sysmngr_cpu_init(void); +void sysmngr_cpu_clean(void); #endif //__PROCESSES_H diff --git a/src/sysmngr.c b/src/sysmngr.c index 413686d..249e86c 100644 --- a/src/sysmngr.c +++ b/src/sysmngr.c @@ -20,7 +20,7 @@ #include "reboots.h" #endif -#ifdef SYSMNGR_REBOOTS +#ifdef SYSMNGR_PROCESS_STATUS #include "processes.h" #endif @@ -28,6 +28,8 @@ #include "memory.h" #endif +#define DEFAULT_LOG_LEVEL LOG_INFO + extern DM_MAP_OBJ tDynamicObj[]; static void usage(char *prog) @@ -35,7 +37,7 @@ static void usage(char *prog) fprintf(stderr, "Usage: %s [options]\n", prog); fprintf(stderr, "\n"); fprintf(stderr, "options:\n"); - fprintf(stderr, " -d Use multiple time to get more verbose debug logs (Debug: -dddd)\n"); + fprintf(stderr, " -l <0-7> Set the loglevel\n"); fprintf(stderr, " -h Displays this help\n"); fprintf(stderr, "\n"); } @@ -43,7 +45,12 @@ static void usage(char *prog) static void config_reload_cb(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg) { - BBF_ERR("Reloading sysmngr upon 'sysmngr.reload' event"); + BBF_INFO("Reloading sysmngr upon 'sysmngr.reload' event"); + +#ifdef SYSMNGR_PROCESS_STATUS + sysmngr_cpu_clean(); + sysmngr_cpu_init(); +#endif #ifdef SYSMNGR_MEMORY_STATUS sysmngr_memory_clean(); @@ -58,13 +65,16 @@ int main(int argc, char **argv) struct ubus_event_handler ev = { .cb = config_reload_cb, }; - int log_level = LOG_ERR; + int log_level = DEFAULT_LOG_LEVEL; int c = 0; - while ((c = getopt(argc, argv, "dh")) != -1) { + while ((c = getopt(argc, argv, "hl:")) != -1) { switch (c) { - case 'd': - log_level += 1; + case 'l': + log_level = (int)strtod(optarg, NULL); + if (log_level < 0 || log_level > 7) { + log_level = DEFAULT_LOG_LEVEL; + } break; case 'h': usage(argv[0]); @@ -89,6 +99,7 @@ int main(int argc, char **argv) #ifdef SYSMNGR_PROCESS_STATUS sysmngr_process_init(&bbfdm_ctx.ubus_ctx); + sysmngr_cpu_init(); #endif #ifdef SYSMNGR_MEMORY_STATUS @@ -108,6 +119,7 @@ out: #ifdef SYSMNGR_PROCESS_STATUS sysmngr_process_clean(&bbfdm_ctx.ubus_ctx); + sysmngr_cpu_clean(); #endif #ifdef SYSMNGR_MEMORY_STATUS diff --git a/src/utils.c b/src/utils.c index a9179b1..d378419 100644 --- a/src/utils.c +++ b/src/utils.c @@ -14,6 +14,8 @@ #include <openssl/sha.h> #include <openssl/evp.h> +#define CRITICAL_STATE_LOGGER_PATH "/etc/sysmngr/critical_state_logger.sh" + static bool validate_hash_value(const char *algo, const char *file_path, const char *checksum) { unsigned char buffer[1024 * 16] = {0}; @@ -309,3 +311,20 @@ int sysmngr_get_uptime(void) return uptime; } + +void sysmngr_generate_critical_log_file(const char *log_path, const char *type, bool critical_state) +{ + char cmd[1024] = {0}; + char output[256] = {0}; + + // sh /etc/sysmngr/critical_state_logger.sh 'CPU' '/var/log/critical_memory.log' 'true' + snprintf(cmd, sizeof(cmd), "sh %s %s %s %s", CRITICAL_STATE_LOGGER_PATH, + type, log_path, critical_state ? "true" : false); + + int res = run_cmd(cmd, output, sizeof(output)); + if (!res || strncmp(output, "Success", 7) == 0) + BBF_DEBUG("Critical log generation succeeded: result=%d, output='%s'", res, output); + else + BBF_DEBUG("Critical log generation failed: result=%d, output='%s'", res, output); +} + diff --git a/src/utils.h b/src/utils.h index 4093f1b..6ab5c0f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -34,4 +34,6 @@ int sysmngr_ubus_invoke_async(struct ubus_context *ubus_ctx, const char *obj, co int sysmngr_get_uptime(void); +void sysmngr_generate_critical_log_file(const char *log_path, const char *log_name, bool critical_state); + #endif //__UTILS_H -- GitLab