From 962be7797030997c99eb978c33d7b6b809fd2a1d Mon Sep 17 00:00:00 2001
From: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
Date: Fri, 22 Nov 2024 10:10:22 +0100
Subject: [PATCH] Expose fwbank object over ubus using sysmngr

---
 src/Makefile     |  18 +-
 src/deviceinfo.c |   2 +-
 src/fw_images.c  | 226 +++++++++-----
 src/fw_images.h  |   1 +
 src/fwbank.c     | 779 +++++++++++++++++++++++++++++++++++++++++++++++
 src/fwbank.h     |  30 ++
 src/sysmngr.c    |  21 ++
 7 files changed, 995 insertions(+), 82 deletions(-)
 create mode 100644 src/fwbank.c
 create mode 100644 src/fwbank.h

diff --git a/src/Makefile b/src/Makefile
index 1ab5a52..a8d795b 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -27,11 +27,6 @@ OBJS += supported_dm.o
 PROG_CFLAGS += -DSYSMNGR_SUPPORTED_DATA_MODEL
 endif
 
-ifeq ($(SYSMNGR_FIRMWARE_IMAGE),y)
-OBJS += fw_images.o
-PROG_CFLAGS += -DSYSMNGR_FIRMWARE_IMAGE
-endif
-
 ifeq ($(SYSMNGR_REBOOTS),y)
 OBJS += reboots.o
 PROG_CFLAGS += -DSYSMNGR_REBOOTS
@@ -46,6 +41,19 @@ ifeq ($(SYSMNGR_VENDOR_EXTENSIONS),y)
 PROG_CFLAGS += -DSYSMNGR_VENDOR_EXTENSIONS
 endif
 
+ifneq ($(filter y,$(SYSMNGR_FIRMWARE_IMAGE) $(SYSMNGR_FWBANK_UBUS_SUPPORT)),)
+OBJS += fwbank.o
+endif
+
+ifeq ($(SYSMNGR_FIRMWARE_IMAGE),y)
+OBJS += fw_images.o
+PROG_CFLAGS += -DSYSMNGR_FIRMWARE_IMAGE
+endif
+
+ifeq ($(SYSMNGR_FWBANK_UBUS_SUPPORT),y)
+PROG_CFLAGS += -DSYSMNGR_FWBANK_UBUS_SUPPORT
+endif
+
 .PHONY: all clean
 
 %.o: %.c
diff --git a/src/deviceinfo.c b/src/deviceinfo.c
index 34c935b..d1e18c9 100644
--- a/src/deviceinfo.c
+++ b/src/deviceinfo.c
@@ -251,7 +251,7 @@ DMOBJ tDeviceInfoObj[] = {
 #endif
 
 #ifdef SYSMNGR_FIRMWARE_IMAGE
-{"FirmwareImage", &DMREAD, NULL, NULL, "file:/usr/libexec/rpcd/fwbank", browseDeviceInfoFirmwareImageInst, NULL, NULL, NULL, tDeviceInfoFirmwareImageParams, NULL, BBFDM_BOTH},
+{"FirmwareImage", &DMREAD, NULL, NULL, fw_image_dependency, browseDeviceInfoFirmwareImageInst, NULL, NULL, NULL, tDeviceInfoFirmwareImageParams, NULL, BBFDM_BOTH},
 #endif
 
 #ifdef SYSMNGR_REBOOTS
diff --git a/src/fw_images.c b/src/fw_images.c
index 9b166a6..6fd8daa 100644
--- a/src/fw_images.c
+++ b/src/fw_images.c
@@ -10,6 +10,7 @@
  */
 
 #include "utils.h"
+#include "fwbank.h"
 
 #define MAX_TIME_WINDOW 5
 
@@ -21,6 +22,105 @@ struct sysupgrade_ev_data {
 /*************************************************************
 * COMMON FUNCTIONS
 **************************************************************/
+static char *get_blobmsg_option_value(struct blob_attr *entry, const char *option_name)
+{
+	struct blob_attr *tb[9] = {0};
+	char *option_value = NULL;
+
+	if (!entry)
+		return "";
+
+	blobmsg_parse(sysmngr_bank_policy, 9, tb, blobmsg_data(entry), blobmsg_len(entry));
+
+	if (DM_STRCMP(sysmngr_bank_policy[0].name, option_name) == 0 && tb[0]) // Name
+		option_value = dmstrdup(blobmsg_get_string(tb[0]));
+	else if (DM_STRCMP(sysmngr_bank_policy[1].name, option_name) == 0 && tb[1]) // ID
+		dmasprintf(&option_value, "%d", blobmsg_get_u32(tb[1]));
+	else if (DM_STRCMP(sysmngr_bank_policy[2].name, option_name) == 0 && tb[2]) // Active
+		option_value = dmstrdup(blobmsg_get_bool(tb[2]) ? "true" : "false");
+	else if (DM_STRCMP(sysmngr_bank_policy[3].name, option_name) == 0 && tb[3]) // Boot
+		option_value = dmstrdup(blobmsg_get_bool(tb[3]) ? "true" : "false");
+	else if (DM_STRCMP(sysmngr_bank_policy[4].name, option_name) == 0 && tb[4]) // Upgrade
+		option_value = dmstrdup(blobmsg_get_bool(tb[4]) ? "true" : "false");
+	else if (DM_STRCMP(sysmngr_bank_policy[5].name, option_name) == 0 && tb[5]) // Firmware Version
+		option_value = dmstrdup(blobmsg_get_string(tb[5]));
+	else if (DM_STRCMP(sysmngr_bank_policy[6].name, option_name) == 0 && tb[6]) // Software Version
+		option_value = dmstrdup(blobmsg_get_string(tb[6]));
+	else if (DM_STRCMP(sysmngr_bank_policy[7].name, option_name) == 0 && tb[7]) // OMCI Software Version
+		option_value = dmstrdup(blobmsg_get_string(tb[7]));
+	else if (DM_STRCMP(sysmngr_bank_policy[8].name, option_name) == 0 && tb[8]) // Status
+		option_value = dmstrdup(blobmsg_get_string(tb[8]));
+	else // Otherwise
+		option_value = "";
+
+	return option_value ? option_value : "";
+}
+
+static char *get_fwbank_option_value(void *data, const char *option_name)
+{
+	char *option_value = NULL;
+
+	option_value = get_blobmsg_option_value((struct blob_attr *)((struct dm_data *)data)->additional_data, option_name);
+
+	return option_value ? option_value : "";
+}
+
+static char *get_fwbank_bank_id(const char *option_name)
+{
+	char *bank_id = NULL;
+
+	struct blob_buf *dump_bb = sysmngr_fwbank_dump();
+	if (!dump_bb) //dump output is empty
+		return "";
+
+	struct blob_attr *tb[1] = {0};
+
+	blobmsg_parse(sysmngr_dump_policy, 1, tb, blobmsg_data(dump_bb->head), blobmsg_len(dump_bb->head));
+
+	if (!tb[0]) // bank array is not found
+		return "";
+
+	struct blob_attr *entry = NULL;
+	int rem = 0;
+
+	blobmsg_for_each_attr(entry, tb[0], rem) { // parse bank array
+		 char *is_true = get_blobmsg_option_value(entry, option_name);
+		 if (DM_LSTRCMP(is_true, "true") == 0) {
+			bank_id = get_blobmsg_option_value(entry, "id");
+			break;
+		 }
+	}
+
+	return bank_id ? bank_id : "";
+}
+
+static bool fwbank_set_bootbank(char *bank_id)
+{
+	int res = sysmngr_fwbank_set_bootbank((uint32_t)DM_STRTOUL(bank_id), NULL);
+
+	return !res ? true : false;
+}
+
+static bool fwbank_upgrade(const char *path, const char *auto_activate, const char *bank_id, const char *keep_settings)
+{
+	json_object *json_obj = NULL;
+	int res = 0;
+
+	dmubus_call_blocking("fwbank", "upgrade", UBUS_ARGS{{"path", path, String}, {"auto_activate", auto_activate, Boolean}, {"bank", bank_id, Integer}, {"keep_settings", keep_settings, Boolean}}, 4, &json_obj);
+
+	if (json_obj) {
+		char *result = dmjson_get_value(json_obj, 1, "result");
+		res = (DM_LSTRCMP(result, "ok") == 0) ? true : false;
+	} else {
+		res = false;
+	}
+
+	if (json_obj != NULL)
+		json_object_put(json_obj);
+
+	return res;
+}
+
 static void _exec_reboot(const void *arg1, void *arg2)
 {
 	char config_name[16] = {0};
@@ -63,6 +163,7 @@ static void dmubus_receive_sysupgrade(struct ubus_context *ctx, struct ubus_even
 
 	size_t msg_len = (size_t)blobmsg_data_len(msg);
 	__blob_for_each_attr(msg_attr, blobmsg_data(msg), msg_len) {
+
 		if (DM_STRCMP("bank_id", blobmsg_name(msg_attr)) == 0) {
 			char *attr_val = (char *)blobmsg_data(msg_attr);
 			if (DM_STRCMP(attr_val, ev_data->bank_id) != 0)
@@ -77,7 +178,6 @@ static void dmubus_receive_sysupgrade(struct ubus_context *ctx, struct ubus_even
 				ev_data->status = true;
 			else
 				ev_data->status = false;
-
 		}
 	}
 
@@ -87,7 +187,7 @@ static void dmubus_receive_sysupgrade(struct ubus_context *ctx, struct ubus_even
 
 static int bbf_fw_image_download(const char *url, const char *auto_activate, const char *username, const char *password,
 		const char *file_size, const char *checksum_algorithm, const char *checksum,
-		const char *bank_id, const char *command, const char *obj_path, const char *commandKey, char *keep)
+		const char *bank_id, const char *command, const char *obj_path, const char *commandKey, const char *keep)
 {
 	char fw_image_path[256] = {0};
 	json_object *json_obj = NULL;
@@ -133,9 +233,6 @@ static int bbf_fw_image_download(const char *url, const char *auto_activate, con
 		goto end;
 	}
 
-	string_to_bool(auto_activate, &activate);
-	char *act = (activate) ? "1" : "0";
-
 	dmubus_call_blocking("system", "validate_firmware_image", UBUS_ARGS{{"path", fw_image_path, String}}, 1, &json_obj);
 	if (json_obj == NULL) {
 		res = -1;
@@ -145,20 +242,21 @@ static int bbf_fw_image_download(const char *url, const char *auto_activate, con
 
 	char *val = dmjson_get_value(json_obj, 1, "valid");
 	string_to_bool(val, &valid);
+
+	// Free json_obj
 	json_object_put(json_obj);
 	json_obj = NULL;
+
 	if (valid == false) {
 		snprintf(fault_msg, sizeof(fault_msg), "File is not a valid firmware image");
 		res = -1;
 		goto end;
 	}
 
-	// default state is to preserve the config over firmware upgrades
-	char *keep_config = DM_STRLEN((char *)keep) ? keep : "1";
+	string_to_bool(auto_activate, &activate);
 
 	// Apply Firmware Image
-	dmubus_call_blocking("fwbank", "upgrade", UBUS_ARGS{{"path", fw_image_path, String}, {"auto_activate", act, Boolean}, {"bank", bank_id, Integer}, {"keep_settings", keep_config, Boolean}}, 4, &json_obj);
-	if (json_obj == NULL) {
+	if (!fwbank_upgrade(fw_image_path, (activate) ? "1" : "0", bank_id, DM_STRLEN(keep) ? keep : "1")) {
 		res = 1;
 		snprintf(fault_msg, sizeof(fault_msg), "Internal error occurred when applying the firmware");
 		goto end;
@@ -187,13 +285,8 @@ end:
 	send_transfer_complete_event(command, obj_path, url, fault_msg, start_time, complete_time, commandKey, "Download");
 
 	// Remove temporary file if ubus upgrade failed and file exists
-	if (!json_obj && file_exists(fw_image_path) && strncmp(url, FILE_URI, strlen(FILE_URI))) {
+	if (file_exists(fw_image_path) && strncmp(url, FILE_URI, strlen(FILE_URI)))
 		remove(fw_image_path);
-		res = -1;
-	}
-
-	if (json_obj != NULL)
-		json_object_put(json_obj);
 
 	return res;
 }
@@ -203,22 +296,32 @@ end:
 **************************************************************/
 int browseDeviceInfoFirmwareImageInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
 {
-	json_object *res = NULL, *bank_obj = NULL, *arrobj = NULL;
 	struct dm_data curr_data = {0};
+	struct blob_attr *tb[1] = {0};
 	char *inst = NULL;
-	int id = 0, i = 0;
+	int id = 0;
 
-	dmubus_call("fwbank", "dump", UBUS_ARGS{0}, 0, &res);
+	struct blob_buf *dump_bb = sysmngr_fwbank_dump();
+	if (!dump_bb) //dump output is empty
+		return 0;
 
-	dmjson_foreach_obj_in_array(res, arrobj, bank_obj, i, 1, "bank") {
+	blobmsg_parse(sysmngr_dump_policy, 1, tb, blobmsg_data(dump_bb->head), blobmsg_len(dump_bb->head));
 
-		curr_data.json_object = bank_obj;
+	if (tb[0]) { // bank array defined
+		struct blob_attr *entry = NULL;
+		int rem = 0;
 
-		inst = handle_instance_without_section(dmctx, parent_node, ++id);
+		blobmsg_for_each_attr(entry, tb[0], rem) { // parse bank array
 
-		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
-			break;
+			curr_data.additional_data = (void *)entry;
+
+			inst = handle_instance_without_section(dmctx, parent_node, ++id);
+
+			if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
+				break;
+		}
 	}
+
 	return 0;
 }
 
@@ -227,50 +330,30 @@ int browseDeviceInfoFirmwareImageInst(struct dmctx *dmctx, DMNODE *parent_node,
 **************************************************************/
 int get_device_active_fwimage(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	json_object *res = NULL, *bank_obj = NULL, *arrobj = NULL;
-	char linker[16] = {0}, *id = NULL;
-	int i = 0;
-
-	dmubus_call("fwbank", "dump", UBUS_ARGS{0}, 0, &res);
-	dmjson_foreach_obj_in_array(res, arrobj, bank_obj, i, 1, "bank") {
-		char *active = dmjson_get_value(bank_obj, 1, "active");
-		if (active && DM_LSTRCMP(active, "true") == 0) {
-			id = dmjson_get_value(bank_obj, 1, "id");
-			break;
-		}
-	}
-
-	if (DM_STRLEN(id) == 0) {
+	char *bank_id = get_fwbank_bank_id("active");
+	if (DM_STRLEN(bank_id) == 0) {
 		*value = dmstrdup("");
 		return 0;
 	}
 
-	snprintf(linker, sizeof(linker), "cpe-%s", id);
+	char linker[16] = {0};
+
+	snprintf(linker, sizeof(linker), "cpe-%s", bank_id);
 	_bbfdm_get_references(ctx, "Device.DeviceInfo.FirmwareImage.", "Alias", linker, value);
 	return 0;
 }
 
 int get_device_boot_fwimage(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	json_object *res = NULL, *bank_obj = NULL, *arrobj = NULL;
-	char linker[16] = {0}, *id = NULL;
-	int i = 0;
-
-	dmubus_call("fwbank", "dump", UBUS_ARGS{0}, 0, &res);
-	dmjson_foreach_obj_in_array(res, arrobj, bank_obj, i, 1, "bank") {
-		char *boot = dmjson_get_value(bank_obj, 1, "boot");
-		if (boot && DM_LSTRCMP(boot, "true") == 0) {
-			id = dmjson_get_value(bank_obj, 1, "id");
-			break;
-		}
-	}
-
-	if (DM_STRLEN(id) == 0) {
+	char *bank_id = get_fwbank_bank_id("boot");
+	if (DM_STRLEN(bank_id) == 0) {
 		*value = dmstrdup("");
 		return 0;
 	}
 
-	snprintf(linker, sizeof(linker), "cpe-%s", id);
+	char linker[16] = {0};
+
+	snprintf(linker, sizeof(linker), "cpe-%s", bank_id);
 	_bbfdm_get_references(ctx, "Device.DeviceInfo.FirmwareImage.", "Alias", linker, value);
 	return 0;
 }
@@ -294,7 +377,6 @@ int set_device_boot_fwimage(char *refparam, struct dmctx *ctx, void *data, char
 		case VALUESET:
 			if (DM_STRLEN(reference.value)) {
 				struct uci_section *dmmap_s = NULL;
-				json_object *res = NULL;
 				char *available = NULL;
 
 				char *bank_id = DM_STRCHR(reference.value, '-'); // Get bank id 'X' which is linker from Alias prefix 'cpe-X'
@@ -306,11 +388,8 @@ int set_device_boot_fwimage(char *refparam, struct dmctx *ctx, void *data, char
 				if (DM_LSTRCMP(available, "false") == 0)
 					return FAULT_9001;
 
-				dmubus_call("fwbank", "set_bootbank", UBUS_ARGS{{"bank", bank_id + 1, Integer}}, 1, &res);
-				char *success = dmjson_get_value(res, 1, "success");
-				if (DM_LSTRCMP(success, "true") != 0)
+				if (!fwbank_set_bootbank(bank_id + 1))
 					return FAULT_9001;
-
 			}
 			break;
 	}
@@ -332,7 +411,7 @@ int get_DeviceInfo_FirmwareImageNumberOfEntries(char *refparam, struct dmctx *ct
 
 static int get_DeviceInfoFirmwareImage_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	char *id = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "id");
+	char *id = get_fwbank_option_value(data, "id");
 	dmasprintf(value, "cpe-%s", id ? id : instance);
 	return 0;
 }
@@ -353,9 +432,7 @@ static int set_DeviceInfoFirmwareImage_Alias(char *refparam, struct dmctx *ctx,
 
 static int get_DeviceInfoFirmwareImage_Name(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	char *name;
-
-	name = dmstrdup(dmjson_get_value(((struct dm_data *)data)->json_object, 1, "fwver"));
+	char *name = get_fwbank_option_value(data, "fwver");
 	if (DM_STRLEN(name) > 64 ) {
 		name[64] = '\0';
 	}
@@ -366,7 +443,7 @@ static int get_DeviceInfoFirmwareImage_Name(char *refparam, struct dmctx *ctx, v
 
 static int get_DeviceInfoFirmwareImage_Version(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	*value = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "swver");
+	*value = get_fwbank_option_value(data, "swver");
 	return 0;
 }
 
@@ -374,7 +451,7 @@ static int get_DeviceInfoFirmwareImage_Available(char *refparam, struct dmctx *c
 {
 	struct uci_section *s = NULL;
 
-	char *id = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "id");
+	char *id = get_fwbank_option_value(data, "id");
 
 	uci_path_foreach_option_eq(bbfdm, "dmmap_fw_image", "fw_image", "id", id, s) {
 		dmuci_get_value_by_section_string(s, "available", value);
@@ -401,13 +478,13 @@ static int set_DeviceInfoFirmwareImage_Available(char *refparam, struct dmctx *c
 			string_to_bool(value, &b);
 
 			if (!b) {
-				char *boot = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "boot");
-				char *active = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "active");
+				char *boot = get_fwbank_option_value(data, "boot");
+				char *active = get_fwbank_option_value(data, "active");
 				if (DM_LSTRCMP(boot, "true") == 0 || DM_LSTRCMP(active, "true") == 0)
 					return FAULT_9001;
 			}
 
-			id = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "id");
+			id = get_fwbank_option_value(data, "id");
 
 			uci_path_foreach_option_eq(bbfdm, "dmmap_fw_image", "fw_image", "id", id, s) {
 				dmuci_set_value_by_section_bbfdm(s, "available", b ? "true" : "false");
@@ -424,7 +501,7 @@ static int set_DeviceInfoFirmwareImage_Available(char *refparam, struct dmctx *c
 
 static int get_DeviceInfoFirmwareImage_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
 {
-	*value = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "status");
+	*value = get_fwbank_option_value(data, "status");
 	return 0;
 }
 
@@ -465,12 +542,12 @@ static int operate_DeviceInfoFirmwareImage_Download(char *refparam, struct dmctx
 		snprintf(obj_path, ret - refparam + 2, "%s", refparam);
 
 	char *url = dmjson_get_value((json_object *)value, 1, "URL");
-	char *auto_activate = dmjson_get_value((json_object *)value, 1, "AutoActivate");
 	if (url[0] == '\0')
 		return USP_FAULT_INVALID_ARGUMENT;
 
 	// Assuming auto activate as false, if not provided by controller, in case of strict validation,
 	// this should result into a fault
+	char *auto_activate = dmjson_get_value((json_object *)value, 1, "AutoActivate");
 	if (DM_STRLEN(auto_activate) == 0)
 		auto_activate = dmstrdup("0");
 
@@ -482,7 +559,7 @@ static int operate_DeviceInfoFirmwareImage_Download(char *refparam, struct dmctx
 	char *commandKey = dmjson_get_value((json_object *)value, 1, "CommandKey");
 	char *keep_config = dmjson_get_value((json_object *)value, 1, BBF_VENDOR_PREFIX"KeepConfig");
 
-	char *bank_id = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "id");
+	char *bank_id = get_fwbank_option_value(data, "id");
 
 	int res = bbf_fw_image_download(url, auto_activate, username, password, file_size, checksum_algorithm, checksum, bank_id, command, obj_path, commandKey, keep_config);
 
@@ -569,7 +646,8 @@ static int operate_DeviceInfoFirmwareImage_Activate(char *refparam, struct dmctx
 		last_idx++;
 	}
 
-	char *bank_id = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "id");
+	char *bank_id = get_fwbank_option_value(data, "id");
+
 	if (!DM_STRLEN(bank_id))
 		return USP_FAULT_COMMAND_FAILURE;
 
@@ -605,11 +683,7 @@ static int operate_DeviceInfoFirmwareImage_Activate(char *refparam, struct dmctx
 
 		res = dmcmd_no_wait("/etc/init.d/cron", 1, "restart");
 	} else {
-		json_object *json_obj = NULL;
-
-		dmubus_call("fwbank", "set_bootbank", UBUS_ARGS{{"bank", bank_id, Integer}}, 1, &json_obj);
-		char *status = dmjson_get_value(json_obj, 1, "success");
-		if (strcasecmp(status, "true") != 0)
+		if (!fwbank_set_bootbank(bank_id))
 			return USP_FAULT_COMMAND_FAILURE;
 
 		bbfdm_task_fork(_exec_reboot, NULL, NULL, NULL);
diff --git a/src/fw_images.h b/src/fw_images.h
index 23047d9..b79255f 100644
--- a/src/fw_images.h
+++ b/src/fw_images.h
@@ -13,6 +13,7 @@
 #define __FW_IMAGES_H
 
 extern DMLEAF tDeviceInfoFirmwareImageParams[];
+extern char fw_image_dependency[];
 
 int browseDeviceInfoFirmwareImageInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance);
 
diff --git a/src/fwbank.c b/src/fwbank.c
new file mode 100644
index 0000000..469a615
--- /dev/null
+++ b/src/fwbank.c
@@ -0,0 +1,779 @@
+/*
+ * Copyright (C) 2024 iopsys Software Solutions AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ *	  Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
+ *
+ */
+
+#include "utils.h"
+#include "fwbank.h"
+
+#ifdef SYSMNGR_FWBANK_UBUS_SUPPORT
+char fw_image_dependency[] = "file:/etc/sysmngr/fwbank";
+#define FWBANK_FILE_PATH "/etc/sysmngr/fwbank"
+#else
+char fw_image_dependency[] = "file:/usr/libexec/rpcd/fwbank";
+#define FWBANK_FILE_PATH "/usr/libexec/rpcd/fwbank"
+#endif
+
+#define FWBANK_DUMP_CMD FWBANK_FILE_PATH " call dump"
+#define FWBANK_SET_BOOTBANK_CMD FWBANK_FILE_PATH " call set_bootbank"
+#define FWBANK_UPGRADE_CMD FWBANK_FILE_PATH " call upgrade"
+
+#define FWBANK_DUMP_MAX_RETRIES 10
+#define FWBANK_DUMP_RETRY_DELAY 3
+
+struct fwbank_event_data {
+	struct ubus_context *ctx;
+	struct uloop_timeout tm;
+	struct ubus_event_handler ev;
+	uint32_t band_id;
+	int timeout;
+};
+
+struct fwbank_dump_data {
+	struct ubus_context *ctx;
+	struct uloop_timeout tm;
+	struct blob_buf output;
+};
+
+static struct fwbank_dump_data g_fwbank_dump = {0};
+static int g_retry_count = 0;
+
+typedef void (*sysmngr_task_callback_t)(struct ubus_context *ctx, struct ubus_request_data *req, int *pipe_fds, uint32_t bank_id);
+
+typedef struct sysmngr_task_data {
+	struct ubus_context *ctx;
+	struct ubus_request_data *req;
+	struct ubus_request_data new_req;
+	struct uloop_process process; // Used for forked task
+	struct uloop_timeout timeoutcb; // Timeout for the task
+	sysmngr_task_callback_t finishcb; // Finish callback for parent process
+	const char *command; // Command to execute
+	int pipe_fds[2];
+	uint32_t bank_id;
+} sysmngr_task_data_t;
+
+struct blobmsg_policy sysmngr_dump_policy[] = {
+	{ "bank", BLOBMSG_TYPE_ARRAY }
+};
+
+struct blobmsg_policy sysmngr_bank_policy[] = {
+	{ "name", BLOBMSG_TYPE_STRING },
+	{ "id", BLOBMSG_TYPE_INT32 },
+	{ "active", BLOBMSG_TYPE_BOOL },
+	{ "boot", BLOBMSG_TYPE_BOOL },
+	{ "upgrade", BLOBMSG_TYPE_BOOL },
+	{ "fwver", BLOBMSG_TYPE_STRING },
+	{ "swver", BLOBMSG_TYPE_STRING },
+	{ "omci_swver", BLOBMSG_TYPE_STRING },
+	{ "status", BLOBMSG_TYPE_STRING }
+};
+
+static void _sysmngr_task_finish_callback(struct uloop_process *p, int ret)
+{
+	struct sysmngr_task_data *task = NULL;
+
+	task = container_of(p, struct sysmngr_task_data, process);
+
+	if (task == NULL) {
+		BBF_ERR("Failed to decode forked task");
+		return;
+	}
+
+	if (task->timeoutcb.pending) {
+		// Cancel timeout if the task finishes on its own
+		uloop_timeout_cancel(&task->timeoutcb);
+	}
+
+	if (task->finishcb) {
+		task->finishcb(task->ctx, &task->new_req, task->pipe_fds, task->bank_id);
+	}
+
+	if (task->ctx && task->req) {
+		ubus_complete_deferred_request(task->ctx, &task->new_req, 0);
+	}
+
+	FREE(task);
+}
+
+static void task_callback(const char *command, int *pipe_fds)
+{
+	// Redirect stdout to the pipe's write end
+	dup2(pipe_fds[1], STDOUT_FILENO);
+	close(pipe_fds[0]); // Close unused read end
+	close(pipe_fds[1]); // Close write end after dup2
+
+	if (!command)
+		exit(EXIT_FAILURE);
+
+	char *line = NULL;
+	ssize_t read;
+	size_t len;
+
+	FILE *pipe = popen(command, "r"); // flawfinder: ignore
+	if (pipe == NULL)
+		exit(EXIT_FAILURE);
+
+	while ((read = getline(&line, &len, pipe)) != -1) {
+		// Output the command's result to stdout
+		line[read - 1] = '\0'; // Remove trailing newline
+		printf("%s", line);
+	}
+
+	free(line);
+
+	if (pclose(pipe) == -1)
+		exit(EXIT_FAILURE);
+
+	exit(EXIT_SUCCESS);
+}
+
+static void timeout_callback(struct uloop_timeout *t)
+{
+	sysmngr_task_data_t *task = container_of(t, sysmngr_task_data_t, timeoutcb);
+
+	if (task && task->process.pid > 0) {
+		BBF_ERR("Task timed out. Killing process with PID %d\n", task->process.pid);
+		kill(task->process.pid, SIGKILL);
+	}
+}
+
+static int sysmngr_task_fork(sysmngr_task_callback_t finishcb, const char *command, int timeout, struct ubus_request_data *req, uint32_t bank_id)
+{
+	sysmngr_task_data_t *task;
+	pid_t child;
+
+	task = (sysmngr_task_data_t *)calloc(sizeof(sysmngr_task_data_t), 1);
+	if (task == NULL) {
+		return -1;
+	}
+
+	if (pipe(task->pipe_fds) == -1) {
+		BBF_ERR("pipe failed");
+		FREE(task);
+		return -1;
+	}
+
+	task->command = command;
+	task->ctx = g_fwbank_dump.ctx;
+	task->req = req;
+	task->bank_id = bank_id;
+
+	child = fork();
+	if (child == -1) {
+		BBF_ERR("Failed to fork a child for task");
+		FREE(task);
+		return -1;
+	} else if (child == 0) {
+		/* free fd's and memory inherited from parent */
+		uloop_done();
+		fclose(stdin);
+		fclose(stderr);
+
+		task_callback(task->command, task->pipe_fds);
+
+		/* write result and exit */
+		exit(EXIT_SUCCESS);
+	}
+
+	if (finishcb) {
+		task->finishcb = finishcb;
+		task->process.pid = child;
+		task->process.cb = _sysmngr_task_finish_callback;
+		uloop_process_add(&task->process);
+	}
+
+	if (task->ctx && task->req) {
+		ubus_defer_request(task->ctx, task->req, &task->new_req);
+	}
+
+	if (timeout > 0) {
+		task->timeoutcb.cb = timeout_callback;
+		uloop_timeout_set(&task->timeoutcb, 1000 * timeout);
+	}
+
+	return 0;
+}
+
+static void fwbank_dump_timer(struct uloop_timeout *timeout)
+{
+	struct fwbank_dump_data *data = NULL;
+
+	BBF_DEBUG("fwbank_dump_timer triggered");
+
+	data = container_of(timeout, struct fwbank_dump_data, tm);
+	if (data == NULL)
+		return;
+
+	sysmngr_init_fwbank_dump(data->ctx);
+}
+
+static int validate_global_fwbank_dump(struct blob_buf *fwbank_dump_bb)
+{
+	if (!fwbank_dump_bb->head || !blob_len(fwbank_dump_bb->head)) {
+		BBF_ERR("fwbank dump output is empty");
+		return -1;
+	}
+
+	BBF_DEBUG("Validating global fwbank dump");
+	struct blob_attr *tb[1] = {0};
+
+	if (blobmsg_parse(sysmngr_dump_policy, 1, tb, blobmsg_data(fwbank_dump_bb->head), blobmsg_len(fwbank_dump_bb->head))) {
+		BBF_ERR("Failed to parse fwbank dump blob");
+		return -1;
+	}
+
+	if (!tb[0]) { // bank array is not found
+		BBF_ERR("Bank array not found in fwbank dump");
+		return -1;
+	}
+
+	struct blob_attr *entry = NULL;
+	int rem = 0;
+
+	blobmsg_for_each_attr(entry, tb[0], rem) { // parse bank array
+		struct blob_attr *t[9] = {0};
+
+		if (blobmsg_parse(sysmngr_bank_policy, 9, t, blobmsg_data(entry), blobmsg_len(entry))) {
+			BBF_ERR("Failed to parse bank entry");
+			return -1;
+		}
+
+		if (!t[0] || !t[1] || !t[2] || !t[3] || !t[4] || !t[5] || !t[6] || !t[7] || !t[8])
+			return -1;
+	}
+
+	BBF_DEBUG("Global fwbank dump validation passed");
+	return 0;
+}
+
+static void fwbank_dump_finish_callback(struct ubus_context *ctx, struct ubus_request_data *req, int *pipe_fds, uint32_t bank_id)
+{
+	BBF_DEBUG("Task finished Line=%d && func=%s", __LINE__, __func__);
+
+	close(pipe_fds[1]); // Close unused write end
+
+	char buffer[1024] = {0};
+	ssize_t bytes_read;
+
+	BBF_DEBUG("Reading script output...");
+
+	// Read the output from the script
+	while ((bytes_read = read(pipe_fds[0], buffer, sizeof(buffer) - 1)) > 0) {
+		buffer[bytes_read] = '\0'; // Null-terminate the buffer
+		BBF_DEBUG("Script output: %s", buffer);
+	}
+
+	close(pipe_fds[0]); // Close read end
+
+	if (bytes_read < 0) {
+		BBF_ERR("Failed to read from pipe");
+		goto retry;
+	}
+
+	memset(&g_fwbank_dump.output, 0, sizeof(struct blob_buf));
+	blob_buf_init(&g_fwbank_dump.output, 0);
+
+	if (!blobmsg_add_json_from_string(&g_fwbank_dump.output, buffer)) {
+		BBF_ERR("Failed to create blob buf");
+		goto retry;
+	}
+
+	int res = validate_global_fwbank_dump(&g_fwbank_dump.output);
+	if (res) {
+		BBF_ERR("Failed to validate 'fwbank' output");
+		goto retry;
+	}
+
+	return;
+
+retry:
+	if (g_retry_count < FWBANK_DUMP_MAX_RETRIES) {
+		g_retry_count++;
+		uloop_timeout_set(&g_fwbank_dump.tm, FWBANK_DUMP_RETRY_DELAY * 1000);
+
+		BBF_ERR("Attempt %d/%d: fwbank dump blob buf is empty. Retrying in %d second(s)...",
+					g_retry_count, FWBANK_DUMP_MAX_RETRIES, FWBANK_DUMP_RETRY_DELAY);
+	} else {
+		BBF_ERR("Max retries (%d) reached: The fwbank dump buffer is empty. Unable to register 'fwbank' ubus object",
+				FWBANK_DUMP_MAX_RETRIES);
+	}
+}
+
+static int init_global_fwbank_dump(void)
+{
+	BBF_DEBUG("Initializing global fwbank dump");
+
+	int res = sysmngr_task_fork(fwbank_dump_finish_callback, FWBANK_DUMP_CMD, 10, NULL, 0);
+	if (res) {
+		BBF_ERR("Failed to start task for fwbank dump command");
+		return -1;
+	}
+
+	BBF_DEBUG("fwbank dump blob initialized successfully");
+	return 0;
+}
+
+static int free_global_fwbank_dump(struct blob_buf *fwbank_dump_bb)
+{
+	if (fwbank_dump_bb->head && blob_len(fwbank_dump_bb->head)) {
+		BBF_DEBUG("Freeing fwbank dump blob buffer");
+		blob_buf_free(fwbank_dump_bb);
+	} else {
+		BBF_DEBUG("fwbank dump blob buffer is already empty");
+	}
+
+	return 0;
+}
+
+static void fwbank_listen_timeout(struct uloop_timeout *timeout)
+{
+	struct fwbank_event_data *data = NULL;
+
+	BBF_DEBUG("fwbank listen timeout triggered");
+
+	data = container_of(timeout, struct fwbank_event_data, tm);
+	if (data == NULL)
+		return;
+
+	ubus_unregister_event_handler(data->ctx, &data->ev);
+}
+
+static void fwbank_receive_sysupgrade(struct ubus_context *ctx, struct ubus_event_handler *ev,
+				const char *type, struct blob_attr *msg)
+{
+	struct fwbank_event_data *data = NULL;
+	struct blob_attr *cur = NULL;
+	char bank_id_str[16] = {0};
+	int rem;
+
+	if (!msg || !ev) {
+		BBF_ERR("Invalid event data in sysupgrade handler");
+		return;
+	}
+
+	data = container_of(ev, struct fwbank_event_data, ev);
+	if (data == NULL)
+		return;
+
+	if (data->band_id < 0) { // bank_id should be a valid id
+		BBF_ERR("Invalid bank_id: %d", data->band_id);
+		return;
+	}
+
+	snprintf(bank_id_str, sizeof(bank_id_str), "%u", data->band_id);
+
+	blobmsg_for_each_attr(cur, msg, rem) {
+		if (DM_STRCMP("bank_id", blobmsg_name(cur)) == 0) {
+			char *attr_val = (char *)blobmsg_data(cur);
+			if (DM_STRCMP(attr_val, bank_id_str) != 0) {
+				BBF_ERR("Mismatched bank_id (%s != %s)", attr_val, bank_id_str);
+				return;
+			}
+		}
+
+		if (DM_STRCMP("status", blobmsg_name(cur)) == 0) {
+			char *attr_val = (char *)blobmsg_data(cur);
+
+			if (DM_STRCMP(attr_val, "Downloading") == 0) {
+				BBF_DEBUG("Sysupgrade status: Downloading");
+				return;
+			}
+
+			if (DM_STRCMP(attr_val, "Available") == 0) {
+				BBF_DEBUG("Sysupgrade status: Available. Refreshing fwbank dump.");
+				sysmngr_fwbank_refresh_global_dump();
+				break;
+			}
+		}
+	}
+
+	if (data->tm.pending) {
+		// Cancel timeout if the sysupgrade status available is found
+		uloop_timeout_cancel(&data->tm);
+	}
+
+	ubus_unregister_event_handler(data->ctx, &data->ev);
+	return;
+}
+
+static struct fwbank_event_data g_fwbank_event_data = {
+	.tm.cb = fwbank_listen_timeout,
+	.ev.cb = fwbank_receive_sysupgrade,
+	.timeout = 120
+};
+
+static void fwbank_wait_for_sysupgrade_event(struct ubus_context *ctx, uint32_t band_id)
+{
+	BBF_DEBUG("Waiting for sysupgrade event for bank_id: %u", band_id);
+
+	g_fwbank_event_data.ctx = ctx;
+	g_fwbank_event_data.band_id = band_id;
+
+	ubus_register_event_handler(ctx, &g_fwbank_event_data.ev, "sysupgrade");
+	uloop_timeout_set(&g_fwbank_event_data.tm, g_fwbank_event_data.timeout * 1000);
+}
+
+static bool is_set_bootbank_success(struct blob_buf *output_bb)
+{
+	struct blob_attr *tb = NULL;
+	struct blobmsg_policy policy = {
+		.name = "success",
+		.type = BLOBMSG_TYPE_BOOL,
+	};
+
+	// Parse the blob buffer for the "success" field
+	if (blobmsg_parse(&policy, 1, &tb, blobmsg_data(output_bb->head), blobmsg_len(output_bb->head)) != 0) {
+		BBF_ERR("Failed to parse blobmsg data");
+		return false;
+	}
+
+	// Check if the "success" field exists and is of the correct type
+	if (tb && blobmsg_type(tb) == BLOBMSG_TYPE_BOOL)
+		return blobmsg_get_bool(tb);
+
+	return false;
+}
+
+static bool is_upgrade_success(struct blob_buf *output_bb)
+{
+	struct blob_attr *tb = NULL;
+	struct blobmsg_policy policy = {
+		.name = "result",
+		.type = BLOBMSG_TYPE_STRING,
+	};
+
+	// Parse the blob buffer for the "result" field
+	if (blobmsg_parse(&policy, 1, &tb, blobmsg_data(output_bb->head), blobmsg_len(output_bb->head)) != 0) {
+		BBF_ERR("Failed to parse blobmsg data");
+		return false;
+	}
+
+	// Check if the "result" field exists and is of the correct type
+	if (tb && blobmsg_type(tb) == BLOBMSG_TYPE_STRING)
+		return (strcmp(blobmsg_get_string(tb), "ok") == 0) ? true : false;
+
+	return false;
+}
+
+struct blob_buf *sysmngr_fwbank_dump(void)
+{
+	return &g_fwbank_dump.output;
+}
+
+static void fwbank_set_bootbank_finish_callback(struct ubus_context *ctx, struct ubus_request_data *req, int *pipe_fds, uint32_t bank_id)
+{
+	BBF_DEBUG("Task finished Line=%d && func=%s", __LINE__, __func__);
+
+	close(pipe_fds[1]); // Close unused write end
+
+	char buffer[1024] = {0};
+	ssize_t bytes_read;
+
+	BBF_DEBUG("Reading script output...");
+
+	// Read the output from the script
+	while ((bytes_read = read(pipe_fds[0], buffer, sizeof(buffer) - 1)) > 0) {
+		buffer[bytes_read] = '\0'; // Null-terminate the buffer
+		BBF_DEBUG("Script output: %s", buffer);
+	}
+
+	close(pipe_fds[0]); // Close read end
+
+	if (bytes_read < 0) {
+		BBF_ERR("Failed to read from pipe");
+		return;
+	}
+
+	struct blob_buf setbootbank_bb = {0};
+
+	memset(&setbootbank_bb, 0, sizeof(struct blob_buf));
+	blob_buf_init(&setbootbank_bb, 0);
+
+	if (!blobmsg_add_json_from_string(&setbootbank_bb, buffer)) {
+		BBF_ERR("Failed to create blob buf");
+		blob_buf_free(&setbootbank_bb);
+		return;
+	}
+
+	bool is_success = is_set_bootbank_success(&setbootbank_bb);
+
+	if (ctx && req) {
+		BBF_DEBUG("Send ubus output");
+		ubus_send_reply(ctx, req, setbootbank_bb.head);
+	}
+
+	blob_buf_free(&setbootbank_bb);
+
+	if (is_success) {
+		// Update fwbank dump output
+		sysmngr_fwbank_refresh_global_dump();
+	}
+
+	return;
+}
+
+int sysmngr_fwbank_set_bootbank(uint32_t bank_id, struct ubus_request_data *req)
+{
+	char cmd[128] = {0};
+
+	snprintf(cmd, sizeof(cmd), "echo '{\"bank\":%u}' | %s 2>/dev/null", bank_id, FWBANK_SET_BOOTBANK_CMD);
+
+	int res = sysmngr_task_fork(fwbank_set_bootbank_finish_callback, cmd, 5, req, 0);
+	if (res) {
+		BBF_ERR("Failed to start task for fwbank set bootbank command");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void fwbank_upgrade_finish_callback(struct ubus_context *ctx, struct ubus_request_data *req, int *pipe_fds, uint32_t bank_id)
+{
+	BBF_DEBUG("Task finished Line=%d && func=%s", __LINE__, __func__);
+
+	close(pipe_fds[1]); // Close unused write end
+
+	char buffer[1024] = {0};
+	ssize_t bytes_read;
+
+	BBF_DEBUG("Reading script output...");
+
+	// Read the output from the script
+	while ((bytes_read = read(pipe_fds[0], buffer, sizeof(buffer) - 1)) > 0) {
+		buffer[bytes_read] = '\0'; // Null-terminate the buffer
+		BBF_DEBUG("Script output: %s", buffer);
+	}
+
+	close(pipe_fds[0]); // Close read end
+
+	if (bytes_read < 0) {
+		BBF_ERR("Failed to read from pipe");
+		return;
+	}
+
+	struct blob_buf upgrade_bb = {0};
+
+	memset(&upgrade_bb, 0, sizeof(struct blob_buf));
+	blob_buf_init(&upgrade_bb, 0);
+
+	if (!blobmsg_add_json_from_string(&upgrade_bb, buffer)) {
+		BBF_ERR("Failed to create blob buf");
+		blob_buf_free(&upgrade_bb);
+		return;
+	}
+
+	bool is_success = is_upgrade_success(&upgrade_bb);
+
+	if (ctx && req) {
+		BBF_DEBUG("Send ubus output");
+		ubus_send_reply(ctx, req, upgrade_bb.head);
+	}
+
+	blob_buf_free(&upgrade_bb);
+
+	if (ctx && is_success) {
+		// Update fwbank dump output after getting sysupgrade event with status available
+		// { "sysupgrade": {"status":"Available","bank_id":"x"} }
+		fwbank_wait_for_sysupgrade_event(ctx, bank_id);
+	}
+
+	return;
+}
+
+int sysmngr_fwbank_upgrade(const char *path, bool auto_activate, uint32_t bank_id, bool keep_settings, struct ubus_request_data *req)
+{
+	char cmd[1024] = {0};
+
+	snprintf(cmd, sizeof(cmd), "echo '{\"path\":\"%s\", \"auto_activate\":%d, \"bank\":%u, \"keep_settings\":%d}' | %s 2>/dev/null",
+			path, auto_activate, bank_id, keep_settings, FWBANK_UPGRADE_CMD);
+
+	int res = sysmngr_task_fork(fwbank_upgrade_finish_callback, cmd, 10, req, bank_id);
+	if (res) {
+		BBF_ERR("Failed to start task for fwbank upgrade command");
+		return -1;
+	}
+
+	return 0;
+}
+
+void sysmngr_fwbank_refresh_global_dump(void)
+{
+	BBF_INFO("fwbank refresh global dump blob buf");
+
+	free_global_fwbank_dump(&g_fwbank_dump.output);
+	init_global_fwbank_dump();
+}
+
+static int dump_handler(struct ubus_context *ctx, struct ubus_object *obj,
+		    struct ubus_request_data *req, const char *method,
+		    struct blob_attr *msg)
+{
+	ubus_send_reply(ctx, req, g_fwbank_dump.output.head);
+	return 0;
+}
+
+enum {
+	SET_BOOT_BANK,
+	__SET_BOOT_MAX
+};
+
+static const struct blobmsg_policy set_bootbank_policy[] = {
+	[SET_BOOT_BANK] = { .name = "bank", .type = BLOBMSG_TYPE_INT32 },
+};
+
+static int set_bootbank_handler(struct ubus_context *ctx, struct ubus_object *obj,
+		    struct ubus_request_data *req, const char *method,
+		    struct blob_attr *msg)
+{
+	struct blob_attr *tb[__SET_BOOT_MAX];
+	uint32_t band_id = 0;
+	int res = 0;
+
+	if (blobmsg_parse(set_bootbank_policy, __SET_BOOT_MAX, tb, blob_data(msg), blob_len(msg))) {
+		BBF_ERR("Failed to parse the 'set_bootbank' message");
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	}
+
+	if (tb[SET_BOOT_BANK])
+		band_id = blobmsg_get_u32(tb[SET_BOOT_BANK]);
+
+	res = sysmngr_fwbank_set_bootbank(band_id, req);
+
+	if (res) {
+		struct blob_buf bb = {0};
+
+		memset(&bb, 0, sizeof(struct blob_buf));
+		blob_buf_init(&bb, 0);
+		blobmsg_add_u8(&bb, "status", false);
+		ubus_send_reply(ctx, req, bb.head);
+		blob_buf_free(&bb);
+	}
+
+	return 0;
+}
+
+enum {
+	UPGRADE_PATH,
+	UPGRADE_AUTO_ACTIVATE,
+	UPGRADE_BANK,
+	UPGRADE_KEEP_SETTINGS,
+	__UPGRADE_MAX
+};
+
+static const struct blobmsg_policy upgrade_policy[] = {
+	[UPGRADE_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
+	[UPGRADE_AUTO_ACTIVATE] = { .name = "auto_activate", .type = BLOBMSG_TYPE_BOOL },
+	[UPGRADE_BANK] = { .name = "bank", .type = BLOBMSG_TYPE_INT32 },
+	[UPGRADE_KEEP_SETTINGS] = { .name = "keep_settings", .type = BLOBMSG_TYPE_BOOL},
+};
+
+static int upgrade_handler(struct ubus_context *ctx, struct ubus_object *obj,
+		    struct ubus_request_data *req, const char *method,
+		    struct blob_attr *msg)
+{
+	struct blob_attr *tb[__UPGRADE_MAX];
+	char *fw_path = NULL;
+	uint32_t bank_id = 0;
+	bool auto_activate = false;
+	bool keep_settings = false;
+	int res = 0;
+
+	if (blobmsg_parse(upgrade_policy, __UPGRADE_MAX, tb, blob_data(msg), blob_len(msg))) {
+		BBF_ERR("Failed to parse the 'upgrade' message");
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	}
+
+	if (tb[UPGRADE_PATH])
+		fw_path = blobmsg_get_string(tb[UPGRADE_PATH]);
+
+	if (tb[UPGRADE_AUTO_ACTIVATE])
+		auto_activate = blobmsg_get_bool(tb[UPGRADE_AUTO_ACTIVATE]);
+
+	if (tb[UPGRADE_BANK])
+		bank_id = blobmsg_get_u32(tb[UPGRADE_BANK]);
+
+	if (tb[UPGRADE_KEEP_SETTINGS])
+		keep_settings = blobmsg_get_bool(tb[UPGRADE_KEEP_SETTINGS]);
+
+	res = sysmngr_fwbank_upgrade(fw_path, auto_activate, bank_id, keep_settings, req);
+
+	if (res) {
+		struct blob_buf bb = {0};
+
+		memset(&bb, 0, sizeof(struct blob_buf));
+		blob_buf_init(&bb, 0);
+		blobmsg_add_string(&bb, "result", "failure");
+		ubus_send_reply(ctx, req, bb.head);
+		blob_buf_free(&bb);
+	}
+
+	return 0;
+}
+
+static struct ubus_method fwbank_methods[] = {
+	UBUS_METHOD_NOARG("dump", dump_handler),
+	UBUS_METHOD("set_bootbank", set_bootbank_handler, set_bootbank_policy),
+	UBUS_METHOD("upgrade", upgrade_handler, upgrade_policy),
+};
+
+static struct ubus_object_type fwbank_type = UBUS_OBJECT_TYPE("fwbank", fwbank_methods);
+
+static struct ubus_object fwbank_object = {
+	.name = "fwbank",
+	.type = &fwbank_type,
+	.methods = fwbank_methods,
+	.n_methods = ARRAY_SIZE(fwbank_methods)
+};
+
+int sysmngr_register_fwbank(struct ubus_context *ubus_ctx)
+{
+	int res = ubus_add_object(ubus_ctx, &fwbank_object);
+	if (res) {
+		BBF_ERR("Failed to register 'fwbank' ubus object!!!!!!");
+		return -1;
+	}
+
+	BBF_INFO("'fwbank' ubus object was registered");
+	return res;
+}
+
+int sysmngr_unregister_fwbank(struct ubus_context *ubus_ctx)
+{
+	ubus_remove_object(ubus_ctx, &fwbank_object);
+
+	BBF_INFO("'fwbank' ubus object was unregistered, and resources were freed");
+	return 0;
+}
+
+int sysmngr_init_fwbank_dump(struct ubus_context *ubus_ctx)
+{
+	int res = 0;
+
+	g_fwbank_dump.ctx = ubus_ctx;
+	g_fwbank_dump.tm.cb = fwbank_dump_timer;
+
+	if (!file_exists(FWBANK_FILE_PATH)) {
+		BBF_ERR("The fwbank file (%s) is missing", FWBANK_FILE_PATH);
+		return -1;
+	}
+
+	res = init_global_fwbank_dump();
+	if (res) {
+		BBF_ERR("Failed to fetch 'fwbank' output or no data available");
+		return -1;
+	}
+
+	return 0;
+}
+
+int sysmngr_clean_fwbank_dump(struct ubus_context *ubus_ctx)
+{
+	free_global_fwbank_dump(&g_fwbank_dump.output);
+	return 0;
+}
diff --git a/src/fwbank.h b/src/fwbank.h
new file mode 100644
index 0000000..c73ce84
--- /dev/null
+++ b/src/fwbank.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 iopsys Software Solutions AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ *	  Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
+ *
+ */
+
+#ifndef __FWBANK_H
+#define __FWBANK_H
+
+extern struct blobmsg_policy sysmngr_dump_policy[];
+extern struct blobmsg_policy sysmngr_bank_policy[];
+
+struct blob_buf *sysmngr_fwbank_dump(void);
+int sysmngr_fwbank_set_bootbank(uint32_t bank_id, struct ubus_request_data *req);
+int sysmngr_fwbank_upgrade(const char *path, bool auto_activate, uint32_t bank_id, bool keep_settings, struct ubus_request_data *req);
+
+void sysmngr_fwbank_refresh_global_dump(void);
+
+int sysmngr_register_fwbank(struct ubus_context *ubus_ctx);
+int sysmngr_unregister_fwbank(struct ubus_context *ubus_ctx);
+
+int sysmngr_init_fwbank_dump(struct ubus_context *ubus_ctx);
+int sysmngr_clean_fwbank_dump(struct ubus_context *ubus_ctx);
+
+#endif //__FWBANK_H
diff --git a/src/sysmngr.c b/src/sysmngr.c
index 249e86c..5541cb0 100644
--- a/src/sysmngr.c
+++ b/src/sysmngr.c
@@ -28,6 +28,10 @@
 #include "memory.h"
 #endif
 
+#if defined(SYSMNGR_FWBANK_UBUS_SUPPORT) || defined(SYSMNGR_FIRMWARE_IMAGE)
+#include "fwbank.h"
+#endif
+
 #define DEFAULT_LOG_LEVEL LOG_INFO
 
 extern DM_MAP_OBJ tDynamicObj[];
@@ -97,6 +101,10 @@ int main(int argc, char **argv)
 	sysmngr_reboots_init();
 #endif
 
+#if defined(SYSMNGR_FWBANK_UBUS_SUPPORT) || defined(SYSMNGR_FIRMWARE_IMAGE)
+	sysmngr_init_fwbank_dump(&bbfdm_ctx.ubus_ctx);
+#endif
+
 #ifdef SYSMNGR_PROCESS_STATUS
 	sysmngr_process_init(&bbfdm_ctx.ubus_ctx);
 	sysmngr_cpu_init();
@@ -109,6 +117,11 @@ int main(int argc, char **argv)
 	if (bbfdm_ubus_regiter_init(&bbfdm_ctx))
 		goto out;
 
+#ifdef SYSMNGR_FWBANK_UBUS_SUPPORT
+	if (sysmngr_register_fwbank(&bbfdm_ctx.ubus_ctx))
+		goto out;
+#endif
+
 	if (ubus_register_event_handler(&bbfdm_ctx.ubus_ctx, &ev, "sysmngr.reload"))
 		goto out;
 
@@ -126,6 +139,14 @@ out:
 	sysmngr_memory_clean();
 #endif
 
+#ifdef SYSMNGR_FWBANK_UBUS_SUPPORT
+	sysmngr_unregister_fwbank(&bbfdm_ctx.ubus_ctx);
+#endif
+
+#if defined(SYSMNGR_FWBANK_UBUS_SUPPORT) || defined(SYSMNGR_MEMORY_STATUS)
+	sysmngr_clean_fwbank_dump(&bbfdm_ctx.ubus_ctx);
+#endif
+
 	closelog();
 
 	return 0;
-- 
GitLab