/* * Copyright (C) 2022-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 * */ #include "utils.h" #include "fwbank.h" struct sysupgrade_ev_data { const char *bank_id; bool status; }; #define CRONTABS_ROOT "/etc/crontabs/root" #define ACTIVATE_HANDLER_FILE "/usr/share/bbfdm/scripts/bbf_activate_handler.sh" #define COPY_CONFIG_CMD "/etc/sysmngr/fwbank call copy_config 2> /dev/null" #define MAX_TIME_WINDOW 5 /************************************************************* * 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])); 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 void fwbank_copy_config(void) { char output[64] = {0}; run_cmd(COPY_CONFIG_CMD, output, sizeof(output)); } static bool fwbank_set_bootbank(const 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, bool activate, const char *bank_id, const char *keep_settings) { json_object *json_obj = NULL; bool res = true; if (activate == false) { dmubus_call_blocking("fwbank", "upgrade", UBUS_ARGS{{"path", path, String}, {"auto_activate", "0", Boolean}, {"bank", bank_id, Integer}, {"keep_settings", "0", Boolean}}, 4, &json_obj); } else { dmubus_call_blocking("fwbank", "upgrade", UBUS_ARGS{{"path", path, String}, {"auto_activate", "1", 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; } 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}; 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, "reboots", "last_reboot_cause", "RemoteReboot"); dmuci_commit_package(config_name); sleep(3); dmubus_call_set("rpc-sys", "reboot", UBUS_ARGS{0}, 0); sleep(5); // Wait for reboot to happen BBFDM_ERR("Reboot call failed with rpc-sys, trying again with system"); dmubus_call_set("system", "reboot", UBUS_ARGS{0}, 0); sleep(5); // Wait for reboot BBFDM_ERR("Reboot call failed!!!"); // Set last_reboot_cause to empty because there is a problem in the system reboot dmuci_set_value(config_name, "reboots", "last_reboot_cause", ""); dmuci_commit_package(config_name); } static void dmubus_receive_sysupgrade(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg) { struct dmubus_event_data *data; struct blob_attr *msg_attr; if (!msg || !ev) return; data = container_of(ev, struct dmubus_event_data, ev); if (data == NULL) return; struct sysupgrade_ev_data *ev_data = (struct sysupgrade_ev_data *)data->ev_data; if (ev_data == NULL) return; 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) return; } if (DM_STRCMP("status", blobmsg_name(msg_attr)) == 0) { char *attr_val = (char *)blobmsg_data(msg_attr); if (DM_STRCMP(attr_val, "Downloading") == 0) return; else if (DM_STRCMP(attr_val, "Available") == 0) ev_data->status = true; else ev_data->status = false; } } uloop_end(); return; } static int bbf_fw_image_download(struct ubus_context *ctx, 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, const char *keep) { char fw_image_path[256] = {0}; json_object *json_obj = NULL; bool activate = false, valid = false; int res = 0; char fault_msg[128] = {0}; time_t complete_time = 0; time_t start_time = time(NULL); DM_STRNCPY(fw_image_path, "/tmp/firmware-XXXXXX", sizeof(fw_image_path)); // Check the file system size if there is sufficient space for downloading the firmware image if (!validate_file_system_size(file_size)) { res = -1; snprintf(fault_msg, sizeof(fault_msg), "Available memory space is lower than required for downloading"); goto end; } res = mkstemp(fw_image_path); if (res == -1) { snprintf(fault_msg, sizeof(fault_msg), "Operation failed due to some internal failure"); goto end; } else { close(res); // close the fd, as only filename required res = 0; } // Download the firmware image long res_code = download_file(fw_image_path, url, username, password); complete_time = time(NULL); // Check if the download operation was successful if (!validate_server_response_code(url, res_code)) { snprintf(fault_msg, sizeof(fault_msg), "Download operation is failed, fault code (%ld)", res_code); res = -1; goto end; } // Validate the CheckSum value according to its algorithm if (!validate_checksum_value(fw_image_path, checksum_algorithm, checksum)) { res = -1; snprintf(fault_msg, sizeof(fault_msg), "Checksum of the file is not matched with the specified value"); goto end; } dmubus_call_blocking("system", "validate_firmware_image", UBUS_ARGS{{"path", fw_image_path, String}}, 1, &json_obj); if (json_obj == NULL) { res = -1; snprintf(fault_msg, sizeof(fault_msg), "Failed in validation of the file"); goto end; } 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; } string_to_bool(auto_activate, &activate); // Apply Firmware Image if (!fwbank_upgrade(fw_image_path, activate, bank_id, DM_STRLEN(keep) ? keep : "1")) { res = 1; snprintf(fault_msg, sizeof(fault_msg), "Internal error occurred when applying the firmware"); goto end; } struct sysupgrade_ev_data ev_data = { .bank_id = bank_id, .status = false, }; dmubus_wait_for_event("sysupgrade", 120, &ev_data, dmubus_receive_sysupgrade, NULL); if (ev_data.status == false) { res = 1; snprintf(fault_msg, sizeof(fault_msg), "Failed to apply the downloaded image file"); goto end; } // Schedule a device Reboot, if auto activation is true if (activate) { bbfdm_task_fork(_exec_reboot, NULL, NULL, NULL); } end: // Send the transfer complete event send_transfer_complete_event(ctx, command, obj_path, url, fault_msg, start_time, complete_time, commandKey, "Download"); // Remove temporary file if ubus upgrade failed and file exists if (file_exists(fw_image_path) && strncmp(url, FILE_URI, strlen(FILE_URI))) remove(fw_image_path); return res; } /************************************************************* * ENTRY METHOD **************************************************************/ int browseDeviceInfoFirmwareImageInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance) { struct dm_data curr_data = {0}; struct blob_attr *tb[1] = {0}; char *inst = NULL; int id = 0; struct blob_buf *dump_bb = sysmngr_fwbank_dump(); if (!dump_bb) //dump output is empty return 0; blobmsg_parse(sysmngr_dump_policy, 1, tb, blobmsg_data(dump_bb->head), blobmsg_len(dump_bb->head)); if (tb[0]) { // bank array defined struct blob_attr *entry = NULL; int rem = 0; blobmsg_for_each_attr(entry, tb[0], rem) { // parse bank array 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; } /************************************************************* * GET & SET PARAM **************************************************************/ int get_device_active_fwimage(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { char *bank_id = get_fwbank_bank_id("active"); if (DM_STRLEN(bank_id) == 0) { *value = dmstrdup(""); return 0; } 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) { char *bank_id = get_fwbank_bank_id("boot"); if (DM_STRLEN(bank_id) == 0) { *value = dmstrdup(""); return 0; } 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 set_device_boot_fwimage(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) { char *allowed_objects[] = {"Device.DeviceInfo.FirmwareImage.", NULL}; struct dm_reference reference = {0}; bbfdm_get_reference_linker(ctx, value, &reference); switch (action) { case VALUECHECK: if (bbfdm_validate_string(ctx, reference.path, -1, -1, NULL, NULL)) return FAULT_9007; if (dm_validate_allowed_objects(ctx, &reference, allowed_objects)) return FAULT_9007; break; case VALUESET: if (DM_STRLEN(reference.value)) { struct uci_section *dmmap_s = NULL; char *available = NULL; char *bank_id = DM_STRCHR(reference.value, '-'); // Get bank id 'X' which is linker from Alias prefix 'cpe-X' if (!bank_id) return FAULT_9001; get_dmmap_section_of_config_section_cont("dmmap_fw_image", "fw_image", "id", bank_id + 1, &dmmap_s); dmuci_get_value_by_section_string(dmmap_s, "available", &available); if (DM_LSTRCMP(available, "false") == 0) return FAULT_9001; if (!fwbank_set_bootbank(bank_id + 1)) return FAULT_9001; } break; } return 0; } int get_DeviceInfo_MaxNumberOfActivateTimeWindows(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { dmasprintf(value, "%d", MAX_TIME_WINDOW); return 0; } int get_DeviceInfo_FirmwareImageNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { int cnt = get_number_of_entries(ctx, data, instance, browseDeviceInfoFirmwareImageInst); dmasprintf(value, "%d", cnt); return 0; } static int get_DeviceInfoFirmwareImage_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { char *id = get_fwbank_option_value(data, "id"); dmasprintf(value, "cpe-%s", id ? id : instance); return 0; } static int set_DeviceInfoFirmwareImage_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) { switch (action) { case VALUECHECK: if (bbfdm_validate_string(ctx, value, -1, 64, NULL, NULL)) return FAULT_9007; break; case VALUESET: bbfdm_set_fault_message(ctx, "Internal designated unique identifier, not allowed to update"); return FAULT_9007; } return 0; } static int get_DeviceInfoFirmwareImage_Name(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { char *name = get_fwbank_option_value(data, "fwver"); if (DM_STRLEN(name) > 64 ) { name[64] = '\0'; } *value = name; return 0; } static int get_DeviceInfoFirmwareImage_Version(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { *value = get_fwbank_option_value(data, "swver"); return 0; } static int get_DeviceInfoFirmwareImage_Available(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { struct uci_section *s = NULL; 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); break; } if ((*value)[0] == '\0') *value = dmstrdup("true"); return 0; } static int set_DeviceInfoFirmwareImage_Available(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) { struct uci_section *s = NULL, *dmmap = NULL; char *id = NULL; bool b; switch (action) { case VALUECHECK: if (bbfdm_validate_boolean(ctx, value)) return FAULT_9007; break; case VALUESET: string_to_bool(value, &b); if (!b) { 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 = 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"); return 0; } dmuci_add_section_bbfdm("dmmap_fw_image", "fw_image", &dmmap); dmuci_set_value_by_section(dmmap, "id", id); dmuci_set_value_by_section(dmmap, "available", b ? "true" : "false"); break; } return 0; } static int get_DeviceInfoFirmwareImage_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { *value = get_fwbank_option_value(data, "status"); return 0; } /************************************************************* * OPERATE COMMANDS *************************************************************/ static operation_args firmware_image_download_args = { .in = (const char *[]) { "URL", "AutoActivate", "Username", "Password", "FileSize", "CheckSumAlgorithm", "CheckSum", "CommandKey", #ifdef SYSMNGR_VENDOR_EXTENSIONS CUSTOM_PREFIX"KeepConfig", #endif NULL } }; static int get_operate_args_DeviceInfoFirmwareImage_Download(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { *value = (char *)&firmware_image_download_args; return 0; } static int operate_DeviceInfoFirmwareImage_Download(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) { const char *command = "Download()"; char obj_path[256] = {0}; char *keep_config = NULL; char *ret = DM_STRRCHR(refparam, '.'); if (!ret) return USP_FAULT_INVALID_ARGUMENT; if ((ret - refparam + 2) < sizeof(obj_path)) snprintf(obj_path, ret - refparam + 2, "%s", refparam); char *url = dmjson_get_value((json_object *)value, 1, "URL"); 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"); char *username = dmjson_get_value((json_object *)value, 1, "Username"); char *password = dmjson_get_value((json_object *)value, 1, "Password"); char *file_size = dmjson_get_value((json_object *)value, 1, "FileSize"); char *checksum_algorithm = dmjson_get_value((json_object *)value, 1, "CheckSumAlgorithm"); char *checksum = dmjson_get_value((json_object *)value, 1, "CheckSum"); char *commandKey = dmjson_get_value((json_object *)value, 1, "CommandKey"); #ifdef SYSMNGR_VENDOR_EXTENSIONS keep_config = dmjson_get_value((json_object *)value, 1, CUSTOM_PREFIX"KeepConfig"); #endif char *bank_id = get_fwbank_option_value(data, "id"); int res = bbf_fw_image_download(ctx->ubus_ctx, url, auto_activate, username, password, file_size, checksum_algorithm, checksum, bank_id, command, obj_path, commandKey, keep_config); if (res == 1) { bbfdm_set_fault_message(ctx, "Firmware validation failed"); } return res ? USP_FAULT_COMMAND_FAILURE : 0; } static operation_args firmware_image_activate_args = { .in = (const char *[]) { "TimeWindow.{i}.Start", "TimeWindow.{i}.End", "TimeWindow.{i}.Mode", "TimeWindow.{i}.UserMessage", "TimeWindow.{i}.MaxRetries", #ifdef SYSMNGR_VENDOR_EXTENSIONS CUSTOM_PREFIX"KeepConfig", #endif NULL } }; static int get_operate_args_DeviceInfoFirmwareImage_Activate(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { *value = (char *)&firmware_image_activate_args; return 0; } static int operate_DeviceInfoFirmwareImage_Activate(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) { char *FW_Mode[] = {"AnyTime", "Immediately", "WhenIdle", "ConfirmationNeeded", NULL}; char *start_time[MAX_TIME_WINDOW] = {0}; char *end_time[MAX_TIME_WINDOW] = {0}; char *mode[MAX_TIME_WINDOW] = {0}; char *user_message[MAX_TIME_WINDOW] = {0}; char *max_retries[MAX_TIME_WINDOW] = {0}; char *keep_config = NULL; bool bKeepConfig = false; int res = 0, last_idx = -1; #ifdef SYSMNGR_VENDOR_EXTENSIONS keep_config = dmjson_get_value((json_object *)value, 1, CUSTOM_PREFIX"KeepConfig"); #endif if (DM_STRLEN(keep_config) == 0) { bKeepConfig = true; } else { string_to_bool(keep_config, &bKeepConfig); } for (int i = 0; i < MAX_TIME_WINDOW; i++) { char buf[32] = {0}; snprintf(buf, sizeof(buf), "TimeWindow.%d.Start", i + 1); start_time[i] = dmjson_get_value((json_object *)value, 1, buf); snprintf(buf, sizeof(buf), "TimeWindow.%d.End", i + 1); end_time[i] = dmjson_get_value((json_object *)value, 1, buf); snprintf(buf, sizeof(buf), "TimeWindow.%d.Mode", i + 1); mode[i] = dmjson_get_value((json_object *)value, 1, buf); snprintf(buf, sizeof(buf), "TimeWindow.%d.UserMessage", i + 1); user_message[i] = dmjson_get_value((json_object *)value, 1, buf); snprintf(buf, sizeof(buf), "TimeWindow.%d.MaxRetries", i + 1); max_retries[i] = dmjson_get_value((json_object *)value, 1, buf); if (!DM_STRLEN(start_time[i])) break; if (!DM_STRLEN(end_time[i]) || !DM_STRLEN(mode[i])) return USP_FAULT_INVALID_ARGUMENT; if (bbfdm_validate_unsignedInt(ctx, start_time[i], RANGE_ARGS{{NULL,NULL}}, 1)) return USP_FAULT_INVALID_ARGUMENT; if (bbfdm_validate_unsignedInt(ctx, end_time[i], RANGE_ARGS{{NULL,NULL}}, 1)) return USP_FAULT_INVALID_ARGUMENT; if (DM_STRLEN(max_retries[i]) && bbfdm_validate_int(ctx, max_retries[i], RANGE_ARGS{{"-1","10"}}, 1)) return USP_FAULT_INVALID_ARGUMENT; if (bbfdm_validate_string(ctx, mode[i], -1, -1, FW_Mode, NULL)) return USP_FAULT_INVALID_ARGUMENT; if (DM_STRTOL(start_time[i]) > DM_STRTOL(end_time[i])) return USP_FAULT_INVALID_ARGUMENT; if (i != 0 && DM_STRTOL(end_time[i - 1]) > DM_STRTOL(start_time[i])) return USP_FAULT_INVALID_ARGUMENT; last_idx++; } char *bank_id = get_fwbank_option_value(data, "id"); if (!DM_STRLEN(bank_id)) return USP_FAULT_COMMAND_FAILURE; if (DM_STRLEN(start_time[0])) { // cppcheck-suppress cert-MSC24-C FILE *file = fopen(CRONTABS_ROOT, "a"); if (!file) return USP_FAULT_COMMAND_FAILURE; for (int i = 0; i < MAX_TIME_WINDOW && DM_STRLEN(start_time[i]); i++) { char buffer[512] = {0}; time_t t_time = time(NULL); long int start_t = (DM_STRTOL(start_time[i]) > 60) ? DM_STRTOL(start_time[i]) : 60; t_time += start_t; struct tm *tm_local = localtime(&t_time); size_t len; snprintf(buffer, sizeof(buffer), "%d %d %d %d * sh %s", tm_local->tm_min, tm_local->tm_hour, tm_local->tm_mday, tm_local->tm_mon + 1, ACTIVATE_HANDLER_FILE); len = strlen(buffer); snprintf(buffer+len, sizeof(buffer)-len, " '%s' '%s' '%ld' '%d' '%s' '%d' '%s'\n", mode[i], bank_id, (DM_STRTOL(end_time[i]) - DM_STRTOL(start_time[i])), (i == last_idx), max_retries[i], bKeepConfig, user_message[i]); fprintf(file, "%s", buffer); } fclose(file); res = dmcmd_no_wait("/etc/init.d/cron", 1, "restart"); } else { if (!fwbank_set_bootbank(bank_id)) return USP_FAULT_COMMAND_FAILURE; if (bKeepConfig == true) { fwbank_copy_config(); } bbfdm_task_fork(_exec_reboot, NULL, NULL, NULL); } return res ? USP_FAULT_COMMAND_FAILURE : 0; } /********************************************************************************************************************************** * OBJ & LEAF DEFINITION ***********************************************************************************************************************************/ /* *** Device.DeviceInfo.FirmwareImage.{i}. *** */ DMLEAF tDeviceInfoFirmwareImageParams[] = { /* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/ {"Alias", &DMWRITE, DMT_STRING, get_DeviceInfoFirmwareImage_Alias, set_DeviceInfoFirmwareImage_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER}, {"Name", &DMREAD, DMT_STRING, get_DeviceInfoFirmwareImage_Name, NULL, BBFDM_BOTH}, {"Version", &DMREAD, DMT_STRING, get_DeviceInfoFirmwareImage_Version, NULL, BBFDM_BOTH}, {"Available", &DMWRITE, DMT_BOOL, get_DeviceInfoFirmwareImage_Available, set_DeviceInfoFirmwareImage_Available, BBFDM_BOTH}, {"Status", &DMREAD, DMT_STRING, get_DeviceInfoFirmwareImage_Status, NULL, BBFDM_BOTH}, {"BootFailureLog", &DMREAD, DMT_STRING, get_empty, NULL, BBFDM_BOTH}, {"Download()", &DMASYNC, DMT_COMMAND, get_operate_args_DeviceInfoFirmwareImage_Download, operate_DeviceInfoFirmwareImage_Download, BBFDM_USP}, {"Activate()", &DMASYNC, DMT_COMMAND, get_operate_args_DeviceInfoFirmwareImage_Activate, operate_DeviceInfoFirmwareImage_Activate, BBFDM_USP}, {0} };