/* * Copyright (c) 2023 Genexis B.V. All rights reserved. * * This Software and its content are protected by the Dutch Copyright Act * ('Auteurswet'). All and any copying and distribution of the software * and its content without authorization by Genexis B.V. is * prohibited. The prohibition includes every form of reproduction and * distribution. * */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include "dm_api.h" #include "dm_log.h" #define METHOD_NAME "bbfdm.bridgemngr" #define BBF_EVENT "bbfdm.event" #define BBF_ASYNC_COMPLETE_EVENT "bbfdm.dmf.async_complete" #define DEFAULT_TRANS_TIMEOUT 300*1000 struct agent_context { struct ubus_context ubus_ctx; struct list_head async_req_list; unsigned int last_async_id; unsigned int trans_id; struct uloop_timeout trans_timer; }; struct async_operate_request { struct ubus_request_data req; unsigned int async_id; struct list_head list; }; struct dm_ubus_event_handler { struct ubus_event_handler uhandler; dm_node_id_t node_id; }; enum { GET_VALUE = 0, GET_NAME, GET_OBJ_NAME, GET_INSTANCE }; enum instance_mode { INSTANCE_MODE_NUMBER, INSTANCE_MODE_ALIAS }; enum dm_proto_type { DM_PROTO_CWMP, DM_PROTO_USP, DM_PROTO_BOTH }; typedef struct { enum dm_proto_type dm_type; enum instance_mode instance_mode; bool is_raw; int trans_id; bool command; bool event; bool parameter; int maxdepth; } bbf_options_t; static struct agent_context agent_ctx; // Helper function to check if a parameter is a key parameter static int is_key_parameter(const dm_node_t *node) { if (!dm_node_is_parameter(node->id)) { return 0; } // Get the parent object node dm_node_id_t parent_id = dm_node_i_parent_id(node->id); if (parent_id == 0) { return 0; } // Check if parent is an object list (multi-instance object) if (!dm_node_is_objectlist(parent_id)) { return 0; } // Get the keys for the parent object const char *keys = dm_node_object_keys(parent_id); if (!keys) { return 0; } // Get the parameter name from the node path dm_path_t node_path; dm_node2name(node, node_path, sizeof(dm_path_t)); // Extract just the parameter name (last part after the last dot) char *param_name = strrchr(node_path, '.'); if (!param_name) { return 0; } param_name++; // Skip the dot // Check if the parameter name is in the keys list char *keys_str = strdup(keys); char *key = strtok(keys_str, ","); while (key) { if (strcmp(key, param_name) == 0) { free(keys_str); return 1; } key = strtok(NULL, ","); } free(keys_str); return 0; } // return 1 if the proto is allowed for the node, otherwise return 0 static int check_dm_proto(int proto, dm_node_id_t node_id) { if (dm_node_is_internal(node_id)) { return 0; } if (proto == DM_PROTO_BOTH) { return 1; } if (proto == DM_PROTO_CWMP) { if (dm_node_is_command(node_id)) { return 0; } if (dm_node_is_usp_only(node_id)) { return 0; } } else { // USP if (dm_node_is_cwmp_only(node_id)) { return 0; } } return 1; } static int get_dm_nodes(const bbf_options_t *opts, const dm_node_t *node, struct blob_buf *bb, int get_type, int depth) { if (opts->maxdepth > 0 && depth > opts->maxdepth) { return 0; } if (!check_dm_proto(opts->dm_type, node->id)) { return 0; } if (dm_node_is_command(node->id) || dm_node_is_event(node->id)) { return 0; } if (dm_node_is_parameter(node->id)) { if (get_type == GET_INSTANCE) return 0; void *table = blobmsg_open_table(bb, NULL); dm_path_t node_path; dm_node2name(node, node_path, sizeof(dm_path_t)); blobmsg_add_string(bb, "path", (char *)node_path); if (get_type != GET_OBJ_NAME) { char *value = NULL; if (dmapi_param_get(node, &value) < 0 || value == NULL) { blobmsg_add_string(bb, "data", ""); } else { blobmsg_add_string(bb, "data", value); free(value); } } else { blobmsg_add_string(bb, "data", dm_node_is_writable(node->id) ? "1" : "0"); } blobmsg_add_string(bb, "type", dm_node_get_param_xsd_type(node->id)); // Add flags array for key parameters if (is_key_parameter(node)) { void *flags_array = blobmsg_open_array(bb, "flags"); blobmsg_add_string(bb, NULL, "Unique"); blobmsg_close_array(bb, flags_array); } blobmsg_close_table(bb, table); } else { const struct dm_object *obj = dm_node_get_object(node->id); if (obj == NULL) return 0; if (get_type == GET_NAME || get_type == GET_OBJ_NAME) { void *table = blobmsg_open_table(bb, NULL); dm_path_t node_path; dm_node2name(node, node_path, sizeof(dm_path_t)); char last = node_path[strlen(node_path) - 1]; if (last == '.') { // remove the {i} node_path[strlen(node_path) - 4] = '\0'; } else { strcat(node_path, "."); } blobmsg_add_string(bb, "path", (char *)node_path); if (get_type == GET_OBJ_NAME) { blobmsg_add_string(bb, "data", dm_node_is_writable(node->id) ? "1" : "0"); } else { blobmsg_add_string(bb, "data", "0"); } blobmsg_add_string(bb, "type", "xsd:object"); blobmsg_close_table(bb, table); } if (dm_node_is_index_complete(node)) { if (get_type == GET_INSTANCE && dm_node_is_objectlist(node->id)) { void *table = blobmsg_open_table(bb, NULL); dm_path_t node_path; dm_node2name(node, node_path, sizeof(dm_path_t)); blobmsg_add_string(bb, "path", (char *)node_path); blobmsg_close_table(bb, table); } if (get_type != GET_INSTANCE) { for (int i = 0; i < obj->param_num; i++) { dm_node_t child = *node; const struct dm_node_info *info = obj->param_list[i]; child.id = info->node_id; get_dm_nodes(opts, &child, bb, get_type, depth+1); } } for (int i = 0; i < obj->object_num; i++) { dm_node_t child = *node; const struct dm_node_info *info = obj->object_list[i]; child.id = info->node_id; get_dm_nodes(opts, &child, bb, get_type, depth + 1); } } else { if (!dm_node_is_objectlist(node->id)) { dmlog_error("unexpected path"); return -1; } dm_nodelist_h list = dm_nodelist_get(node); const dm_node_t *n = NULL; nodelist_for_each_node(n, list) { get_dm_nodes(opts, n, bb, get_type, depth); } dm_nodelist_free(list); } } return 0; } int get_supported_dm(const bbf_options_t *opts, dm_node_id_t id, struct blob_buf *bb) { if (!check_dm_proto(opts->dm_type, id)) { return 0; } if (!opts->command && dm_node_is_command(id)) { return 0; } if (!opts->parameter && dm_node_is_parameter(id)) { return 0; } if (!opts->event && dm_node_is_event(id)) { return 0; } if (dm_node_is_parameter(id) || dm_node_is_objectlist(id) || dm_node_is_command(id) || dm_node_is_event(id)) { void *table = blobmsg_open_table(bb, NULL); dm_path_t node_path; dm_node_t node = {id}; dm_node2name(&node, node_path, sizeof(dm_path_t)); blobmsg_add_string(bb, "path", (char *)node_path); if (!dm_node_is_command(id) && !dm_node_is_event(id)) { blobmsg_add_string(bb, "data", dm_node_is_writable(id) ? "1" : "0"); } const char *type; if (dm_node_is_objectlist(id)) { type = "xsd:object"; } else if (dm_node_is_command(id)) { type = "xsd:command"; } else if (dm_node_is_event(id)) { type = "xsd:event"; } else { type = dm_node_get_param_xsd_type(id); } blobmsg_add_string(bb, "type", type); // Add flags array for key parameters if (dm_node_is_parameter(id) && is_key_parameter(&node)) { void *flags_array = blobmsg_open_array(bb, "flags"); blobmsg_add_string(bb, NULL, "Unique"); blobmsg_close_array(bb, flags_array); } // if (dm_node_is_objectlist(id)) { // const char *keys = dm_node_object_keys(id); // if (keys) { // void *array = blobmsg_open_array(bb, "input"); // char *keys_str = strdup(keys); // char *key = strtok(keys_str, ","); // void *tbl = blobmsg_open_table(bb, NULL); // blobmsg_add_string(bb, "path", key); // blobmsg_close_table(bb, tbl); // while ((key = strtok(NULL, ","))) { // tbl = blobmsg_open_table(bb, NULL); // blobmsg_add_string(bb, "path", key); // blobmsg_close_table(bb, tbl); // } // blobmsg_close_table(bb, array); // free(keys_str); // } // } if (dm_node_is_command(id)) { const struct dm_command *cmd = dm_node_get_command(id); blobmsg_add_string(bb, "data", cmd->async ? "async" : "sync"); if (cmd->inputs_cnt > 0) { void *array = blobmsg_open_array(bb, "input"); for (int i = 0; i < cmd->inputs_cnt; i++) { void *tbl = blobmsg_open_table(bb, NULL); blobmsg_add_string(bb, "path", cmd->inputs[i].name); blobmsg_close_table(bb, tbl); } blobmsg_close_table(bb, array); } if (cmd->outputs_cnt > 0) { void *array = blobmsg_open_array(bb, "output"); for (int i = 0; i < cmd->outputs_cnt; i++) { void *tbl = blobmsg_open_table(bb, NULL); blobmsg_add_string(bb, "path", cmd->outputs[i].name); blobmsg_close_table(bb, tbl); } blobmsg_close_table(bb, array); } } else if (dm_node_is_event(id)) { const struct dm_event *evt = dm_node_get_event(id); if (evt->args_cnt > 0) { void *array = blobmsg_open_array(bb, "input"); for (int i = 0; i < evt->args_cnt; i++) { void *tbl = blobmsg_open_table(bb, NULL); blobmsg_add_string(bb, "path", evt->args[i].name); blobmsg_close_table(bb, tbl); } blobmsg_close_table(bb, array); } } blobmsg_close_table(bb, table); } if (dm_node_is_object(id) || dm_node_is_objectlist(id)) { const struct dm_object *obj = dm_node_get_object(id); int i; if (obj == NULL) return -1; for (i = 0; i < obj->param_num; i++) { const struct dm_node_info *info = obj->param_list[i]; get_supported_dm(opts, info->node_id, bb); } for (i = 0; i < obj->object_num; i++) { const struct dm_node_info *info = obj->object_list[i]; get_supported_dm(opts, info->node_id, bb); } for (i = 0; i < obj->command_num; i++) { const struct dm_node_info *info = obj->command_list[i]; get_supported_dm(opts, info->node_id, bb); } for (i = 0; i < obj->event_num; i++) { const struct dm_node_info *info = obj->event_list[i]; get_supported_dm(opts, info->node_id, bb); } } return 0; } enum { DM_SCHEMA_PATH, DM_SCHEMA_OPTIONAL, __DM_SCHEMA_MAX }; static const struct blobmsg_policy dm_schema_policy[] = { [DM_SCHEMA_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, [DM_SCHEMA_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE}, }; static int get_proto_type(struct blob_attr *proto) { int type = DM_PROTO_BOTH; if (proto) { const char *val = blobmsg_get_string(proto); if (strcmp("cwmp", val) == 0) type = DM_PROTO_CWMP; else if (strcmp("usp", val) == 0) type = DM_PROTO_USP; else type = DM_PROTO_BOTH; } return type; } static int get_instance_mode(struct blob_attr *ins) { int instance_mode = INSTANCE_MODE_NUMBER; if (ins) instance_mode = blobmsg_get_u32(ins); if (instance_mode > INSTANCE_MODE_ALIAS) instance_mode = INSTANCE_MODE_NUMBER; return instance_mode; } static void get_bbf_options(bbf_options_t *options, struct blob_attr *msg) { struct blob_attr *attr; size_t rem; // set default options->dm_type = DM_PROTO_BOTH; options->instance_mode = INSTANCE_MODE_NUMBER; options->is_raw = true; options->command = true; options->event = true; options->parameter = true; blobmsg_for_each_attr(attr, msg, rem) { if (!blobmsg_name(attr)[0]) continue; if (strcmp(blobmsg_name(attr), "proto") == 0) options->dm_type = get_proto_type(attr); if (strcmp(blobmsg_name(attr), "instance_mode") == 0) options->instance_mode = get_instance_mode(attr); if (strcmp(blobmsg_name(attr), "transaction_id") == 0) options->trans_id = blobmsg_get_u32(attr); if (strcmp(blobmsg_name(attr), "format") == 0) options->is_raw = strcmp(blobmsg_get_string(attr), "raw") == 0 ? true : false; if (strcmp(blobmsg_name(attr), "maxdepth") == 0) options->maxdepth = blobmsg_get_u32(attr); if (strcmp(blobmsg_name(attr), "commands") == 0) options->command = blobmsg_get_bool(attr); if (strcmp(blobmsg_name(attr), "events") == 0) options->event = blobmsg_get_bool(attr); if (strcmp(blobmsg_name(attr), "params") == 0) options->parameter = blobmsg_get_bool(attr); } } static int usp_schema_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)), struct ubus_request_data *req, const char *method __attribute__((unused)), struct blob_attr *msg) { struct blob_attr *tb[__DM_SCHEMA_MAX]; bbf_options_t options; memset(&options, 0, sizeof(bbf_options_t)); struct blob_buf bb; dm_node_t node; memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); if (blobmsg_parse(dm_schema_policy, __DM_SCHEMA_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_error("usp_schema_handler, failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[DM_SCHEMA_PATH]) { dmlog_error("usp_schema_handler, missing path parameter"); return UBUS_STATUS_INVALID_ARGUMENT; } get_bbf_options(&options, tb[DM_SCHEMA_OPTIONAL]); unsigned int dm_type = options.dm_type; bool param_in_opts = false; bool cmd_in_opts = false; bool evt_in_opts = false; if (tb[DM_SCHEMA_OPTIONAL]) { struct blob_attr *attr; size_t rem; blobmsg_for_each_attr(attr, tb[DM_SCHEMA_OPTIONAL], rem) { if (strcmp(blobmsg_name(attr), "params") == 0) param_in_opts = true; if (strcmp(blobmsg_name(attr), "commands") == 0) cmd_in_opts = true; if (strcmp(blobmsg_name(attr), "events") == 0) evt_in_opts = true; } } if (!param_in_opts) options.parameter = true; if (!cmd_in_opts) options.command = (dm_type == DM_PROTO_CWMP) ? false : true; if (!evt_in_opts) options.event = (dm_type == DM_PROTO_CWMP) ? false : true; void *array = blobmsg_open_array(&bb, "results"); const char *path = blobmsg_get_string(tb[DM_SCHEMA_PATH]); if (dm_path2node(path, &node) == 0) { if (options.dm_type == DM_PROTO_CWMP) { get_dm_nodes(&options, &node, &bb, GET_OBJ_NAME, 0); } else { get_supported_dm(&options, node.id, &bb); } } else { dmlog_error("usp_schema_handler, invalid path: %s", path); } blobmsg_close_array(&bb, array); ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); return 0; } enum { DM_GET_PATH, DM_GET_OPTIONAL, __DM_GET_MAX }; static const struct blobmsg_policy dm_get_policy[] = { [DM_GET_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, [DM_GET_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE}, }; static int usp_get_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)), struct ubus_request_data *req, const char *method __attribute__((unused)), struct blob_attr *msg) { struct blob_attr *tb[__DM_GET_MAX]; bbf_options_t options; struct blob_buf bb; dm_node_t node; memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); memset(&options, 0, sizeof(bbf_options_t)); if (blobmsg_parse(dm_get_policy, __DM_GET_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_error("usp_get_handler, failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[DM_GET_PATH]) { dmlog_error("usp_get_handler, missing the path argument"); return UBUS_STATUS_INVALID_ARGUMENT; } get_bbf_options(&options, tb[DM_GET_OPTIONAL]); void *array = blobmsg_open_array(&bb, "results"); char *path_str = blobmsg_get_string(tb[DM_GET_PATH]); if (dm_path2node(path_str, &node) == 0) { get_dm_nodes(&options, &node, &bb, GET_VALUE, 0); } else { // dmlog_error("usp_get_handler, invalid path: %s", path_str); } blobmsg_close_array(&bb, array); ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); return 0; } enum { DM_INSTANCES_PATH, DM_INSTANCES_OPTIONAL, __DM_INSTANCES_MAX }; static const struct blobmsg_policy dm_instances_policy[] = { [DM_INSTANCES_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, [DM_INSTANCES_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE }, }; static int usp_instances_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)), struct ubus_request_data *req, const char *method __attribute__((unused)), struct blob_attr *msg) { struct blob_attr *tb[__DM_INSTANCES_MAX]; bbf_options_t options; struct blob_buf bb; dm_node_t node; memset(&options, 0, sizeof(bbf_options_t)); memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); if (blobmsg_parse(dm_instances_policy, __DM_INSTANCES_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_debug("usp_instances_handler, failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[DM_INSTANCES_PATH]) { dmlog_debug("usp_instances_handler, missing the path"); return UBUS_STATUS_INVALID_ARGUMENT; } get_bbf_options(&options, tb[DM_INSTANCES_OPTIONAL]); void *array = blobmsg_open_array(&bb, "results"); char *path_str = blobmsg_get_string(tb[DM_INSTANCES_PATH]); if (dm_path2node(path_str, &node) == 0) { get_dm_nodes(&options, &node, &bb, GET_INSTANCE, 0); } else { dmlog_error("usp_get_handler, invalid path: %s", path_str); } blobmsg_close_array(&bb, array); ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); return 0; } static int get_random_id(void) { int ret; srand(time(0)); ret = rand(); if (!ret) ret = 1; return ret; } static void trans_timeout_handler(struct uloop_timeout *t __attribute__((unused))) { agent_ctx.trans_id = 0; dmapi_session_end(TRANX_ROLLBACK); } static int start_trans(int max_timeout) { if (dmapi_in_session()) { dmlog_error("failed to start transaction."); return -1; } dmapi_session_start(); agent_ctx.trans_id = get_random_id(); memset(&agent_ctx.trans_timer, 0, sizeof(agent_ctx.trans_timer)); agent_ctx.trans_timer.cb = trans_timeout_handler; int timeout = DEFAULT_TRANS_TIMEOUT; if (max_timeout > 0) { timeout = max_timeout*1000; } uloop_timeout_set(&agent_ctx.trans_timer, timeout); return agent_ctx.trans_id; } static int commit_trans(int restart_service) { int ret; if (restart_service) { ret = dmapi_session_end(TRANX_COMMIT_AND_APPLY); } else { ret = dmapi_session_end(TRANX_COMMIT); } uloop_timeout_cancel(&agent_ctx.trans_timer); agent_ctx.trans_id = 0; return ret; } static int abort_trans() { uloop_timeout_cancel(&agent_ctx.trans_timer); agent_ctx.trans_id = 0; return dmapi_session_end(TRANX_ROLLBACK); } enum { TRANS_CMD, TRANS_OPTIONAL, __TRANS_MAX, }; static const struct blobmsg_policy transaction_policy[] = { [TRANS_CMD] = { .name = "cmd", .type = BLOBMSG_TYPE_STRING }, [TRANS_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE }, }; static int usp_transaction_handler(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { int ret; struct blob_attr *tb[__TRANS_MAX] = {NULL}; bbf_options_t options; bool is_service_restart = true; uint32_t max_timeout = 0; char *trans_cmd = "status"; struct blob_buf bb; memset(&options, 0, sizeof(bbf_options_t)); memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); if (blobmsg_parse(transaction_policy, __TRANS_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_debug("usp_transaction_handlerm, failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[TRANS_CMD]) { dmlog_error("missing transaction cmd"); return UBUS_STATUS_INVALID_ARGUMENT; } if (tb[TRANS_CMD]) trans_cmd = blobmsg_get_string(tb[TRANS_CMD]); get_bbf_options(&options, tb[TRANS_OPTIONAL]); struct blob_attr *attr; size_t rem; blobmsg_for_each_attr(attr, tb[TRANS_OPTIONAL], rem) { if (strcmp(blobmsg_name(attr), "timeout") == 0) max_timeout = blobmsg_get_u32(attr); if (strcmp(blobmsg_name(attr), "restart_services") == 0) is_service_restart = blobmsg_get_bool(attr); } if (strcmp(trans_cmd, "start") != 0 && strcmp(trans_cmd, "status") != 0 && (agent_ctx.trans_id == 0 || agent_ctx.trans_id != options.trans_id)) { dmlog_error("invalid transaction id for cmd: %s. tid: %d", trans_cmd, options.trans_id); return UBUS_STATUS_INVALID_ARGUMENT; } if (strcmp(trans_cmd, "start") == 0) { ret = start_trans(max_timeout); // ret = transaction_start(max_timeout); if (ret > 0) { blobmsg_add_u8(&bb, "status", true); blobmsg_add_u32(&bb, "transaction_id", ret); } else { blobmsg_add_u8(&bb, "status", false); } } else if (strcmp(trans_cmd, "commit") == 0) { ret = commit_trans(is_service_restart); blobmsg_add_u8(&bb, "status", (ret == 0)); } else if (strcmp(trans_cmd, "abort") == 0) { ret = abort_trans(); blobmsg_add_u8(&bb, "status", (ret == 0)); } else if (strcmp(trans_cmd, "status") == 0) { int64_t rem = uloop_timeout_remaining64(&agent_ctx.trans_timer); blobmsg_add_string(&bb, "status", "on-going"); blobmsg_add_u32(&bb, "remaining_time", rem / 1000); } else { dmlog_error("unknown trans method(%s)", method); return UBUS_STATUS_INVALID_ARGUMENT; } ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); return 0; } enum { DM_SET_PATH, DM_SET_VALUE, DM_SET_OBJ_PATH, DM_SET_OPTIONAL, __DM_SET_MAX, }; static int set_blob_value(const dm_node_t *node, struct blob_attr *attr) { int ret; if (blob_id(attr) == BLOBMSG_TYPE_STRING) { ret = dmapi_param_set(node, blobmsg_get_string(attr)); } else { char * value = NULL; switch (blob_id(attr)) { case BLOBMSG_TYPE_INT8: asprintf(&value, "%d", blobmsg_get_u8(attr)); break; case BLOBMSG_TYPE_INT16: asprintf(&value, "%d", blobmsg_get_u16(attr)); break; case BLOBMSG_TYPE_INT32: asprintf(&value, "%u", blobmsg_get_u32(attr)); break; case BLOBMSG_TYPE_INT64: asprintf(&value, "%"PRIu64"", blobmsg_get_u64(attr)); break; default: dmlog_error("Unhandled set request type|%x|", blob_id(attr)); return -1; } ret = dmapi_param_set(node, value); free(value); } return ret; } static void set_result(struct blob_buf *bb, const char *path, int fault, const char *info) { void *table = blobmsg_open_table(bb, NULL); blobmsg_add_string(bb, "path", path); if (fault != 0) { blobmsg_add_string(bb, "data", ""); blobmsg_add_u8(bb, "status", false); blobmsg_add_u32(bb, "fault", fault); blobmsg_add_string(bb, "fault_msg", info); } else { blobmsg_add_string(bb, "data", info); blobmsg_add_u8(bb, "status", true); } blobmsg_close_table(bb, table); } static const struct blobmsg_policy dm_set_policy[] = { [DM_SET_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, [DM_SET_VALUE] = { .name = "value", .type = BLOBMSG_TYPE_STRING }, [DM_SET_OBJ_PATH] = { .name = "obj_path", .type = BLOBMSG_TYPE_TABLE }, [DM_SET_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE }, }; int usp_set_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[__DM_SET_MAX] = {NULL}; bbf_options_t opts; struct blob_buf bb; dm_node_t node; memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); memset(&opts, 0, sizeof(opts)); if (blobmsg_parse(dm_set_policy, __DM_SET_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_error("usp_set_handler, failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[DM_SET_PATH]) { dmlog_error("usp_set_handler, missing the path"); return UBUS_STATUS_INVALID_ARGUMENT; } get_bbf_options(&opts, tb[DM_SET_OPTIONAL]); char *path_str = blobmsg_get_string(tb[DM_SET_PATH]); if (dm_path2node(path_str, &node) != 0 || dm_node_is_command(node.id) || dm_node_is_event(node.id)) { dmlog_error("usp_set_handler, invalid path %s", path_str); return UBUS_STATUS_INVALID_ARGUMENT; } int is_param = dm_node_is_parameter(node.id); if ((is_param && !tb[DM_SET_VALUE]) || (!is_param && !tb[DM_SET_OBJ_PATH])) { dmlog_error("usp_set_handler, missing value or obj_path argument"); return UBUS_STATUS_INVALID_ARGUMENT; } int internal_trans = 0; int res = 0; if (agent_ctx.trans_id == 0) { internal_trans = 1; if (start_trans(0) < 0) { return UBUS_STATUS_UNKNOWN_ERROR; } } else if (opts.trans_id != agent_ctx.trans_id) { dmlog_error("in transaction or transaction id is invalid"); return UBUS_STATUS_INVALID_ARGUMENT; } void *array = blobmsg_open_array(&bb, "results"); if (dm_node_is_parameter(node.id)) { if (set_blob_value(&node, tb[DM_SET_VALUE]) != 0) { if (opts.dm_type == DM_PROTO_CWMP) set_result(&bb, path_str, 9007, "Invalid parameter value"); else set_result(&bb, path_str, 7012, "Invalid value"); res = -1; } else { set_result(&bb, path_str, 0, "1"); } } else { struct blob_attr *attr; struct blobmsg_hdr *hdr; size_t tlen = (size_t)blobmsg_data_len(tb[DM_SET_OBJ_PATH]); __blob_for_each_attr(attr, blobmsg_data(tb[DM_SET_OBJ_PATH]), tlen) { hdr = blob_data(attr); char *path=NULL; dm_node_t param_node; asprintf(&path, "%s%s", path_str, (char *)hdr->name); if (dm_path2node(path, ¶m_node) != 0 || !dm_node_is_parameter(param_node.id)) { dmlog_error("usp_set_handler, invalid path %s", path); if (opts.dm_type == DM_PROTO_CWMP) set_result(&bb, path_str, 9005, "Invalid parameter name"); else set_result(&bb, path_str, 7026, "Invalid path"); res = -1; } else { if (set_blob_value(¶m_node, attr) != 0) { if (opts.dm_type == DM_PROTO_CWMP) set_result(&bb, path_str, 9007, "Invalid parameter value"); else set_result(&bb, path_str, 7012, "Invalid value"); res = -1; } else { set_result(&bb, path_str, 0, "1"); } } free(path); if (res < 0) { break; } } } if (internal_trans) { // Internal transaction: need to commit or revert the changes if (res == 0 && commit_trans(1) != 0) { dmlog_error("usp_set_handler, failed to commit"); } else if (res < 0 && abort_trans() < 0) { dmlog_error("usp_set_handler, failed to abort"); } } blobmsg_close_array(&bb, array); ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); return 0; } enum { DM_ADD_PATH, DM_ADD_OBJ_PATH, DM_ADD_OPTIONAL, __DM_ADD_MAX }; static const struct blobmsg_policy dm_add_policy[] = { [DM_ADD_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, [DM_ADD_OBJ_PATH] = { .name = "obj_path", .type = BLOBMSG_TYPE_TABLE }, [DM_ADD_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE }, }; int usp_add_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[__DM_ADD_MAX]; bbf_options_t opts; struct blob_buf bb; dm_node_t node; bool allow_partial = false; memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); memset(&opts, 0, sizeof(opts)); if (blobmsg_parse(dm_add_policy, __DM_ADD_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_error("usp_add_handler, failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[DM_ADD_PATH]) { dmlog_error("usp_add_handler, missing path"); return UBUS_STATUS_INVALID_ARGUMENT; } get_bbf_options(&opts, tb[DM_ADD_OPTIONAL]); if (tb[DM_ADD_OPTIONAL]) { struct blob_attr *attr; size_t rem; blobmsg_for_each_attr(attr, tb[DM_ADD_OPTIONAL], rem) { if (strcmp(blobmsg_name(attr), "allow_partial") == 0) allow_partial = blobmsg_get_bool(attr); } } char *path_str = blobmsg_get_string(tb[DM_ADD_PATH]); if (dm_path2node(path_str, &node) != 0 || !dm_node_is_objectlist(node.id)) { dmlog_error("usp_add_handler, invalid path %s", path_str); return UBUS_STATUS_INVALID_ARGUMENT; } int internal_trans = 0; int res = 0; if (agent_ctx.trans_id == 0) { internal_trans = 1; if (start_trans(0) < 0) { return UBUS_STATUS_UNKNOWN_ERROR; } } else if (opts.trans_id != agent_ctx.trans_id) { dmlog_error("in transaction or transaction id is invalid"); return UBUS_STATUS_INVALID_ARGUMENT; } void *array = blobmsg_open_array(&bb, "results"); if (dmapi_object_add(&node) == 0) { dm_index_t idx = dm_node_last_index(&node); char *data = NULL; asprintf(&data, "%d", idx); set_result(&bb, path_str, 0, data); free(data); } else { res = -1; if (opts.dm_type == DM_PROTO_CWMP) set_result(&bb, path_str, 9005, "Invalid parameter name"); else set_result(&bb, path_str, 7026, "Invalid path"); blobmsg_close_array(&bb, array); goto end; } if (tb[DM_ADD_OBJ_PATH]) { struct blob_attr *attr; struct blobmsg_hdr *hdr; size_t tlen = (size_t)blobmsg_data_len(tb[DM_ADD_OBJ_PATH]); dm_path_t inst_path; dm_node2name(&node, inst_path, sizeof(inst_path)); __blob_for_each_attr(attr, blobmsg_data(tb[DM_ADD_OBJ_PATH]), tlen) { hdr = blob_data(attr); char *path=NULL; dm_node_t param_node; asprintf(&path, "%s.%s", inst_path, (char *)hdr->name); if (dm_path2node(path, ¶m_node) != 0 || !dm_node_is_parameter(param_node.id)) { dmlog_error("usp_add_handler, invalid path %s", path); if (opts.dm_type == DM_PROTO_CWMP) set_result(&bb, path, 9005, "Invalid parameter name"); else set_result(&bb, path, 7026, "Invalid path"); res = -1; } else { if (set_blob_value(¶m_node, attr) != 0) { if (opts.dm_type == DM_PROTO_CWMP) set_result(&bb, path, 9007, "Invalid parameter value"); else set_result(&bb, path, 7012, "Invalid value"); res = -1; } else { set_result(&bb, path, 0, "1"); } } free(path); if (res < 0) { if (!allow_partial) { blobmsg_close_array(&bb, array); goto end; } res = 0; // clear error for partial } } } blobmsg_close_array(&bb, array); end: if (internal_trans) { // Internal transaction: need to commit or revert the changes if (res == 0 && commit_trans(1) != 0) { dmlog_error("usp_add_handler, failed to commit"); } else if (res < 0 && abort_trans() < 0) { dmlog_error("usp_add_handler, failed to abort"); } } ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); return 0; } enum { DM_DEL_PATH, DM_DEL_OPTIONAL, __DM_DEL_MAX }; static const struct blobmsg_policy dm_del_policy[] = { [DM_DEL_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, [DM_DEL_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE }, }; int usp_del_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[__DM_DEL_MAX]; bbf_options_t opts; struct blob_buf bb; dm_node_t node; memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); memset(&opts, 0, sizeof(opts)); if (blobmsg_parse(dm_del_policy, __DM_DEL_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_error("usp_del_handler, failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[DM_DEL_PATH]) { dmlog_error("usp_del_handler, missing path"); return UBUS_STATUS_INVALID_ARGUMENT; } get_bbf_options(&opts, tb[DM_DEL_OPTIONAL]); int internal_trans = 0; int res = 0; if (agent_ctx.trans_id == 0) { internal_trans = 1; if (start_trans(0) < 0) { return UBUS_STATUS_UNKNOWN_ERROR; } } else if (opts.trans_id != agent_ctx.trans_id) { dmlog_error("in transaction or transaction id is invalid"); return UBUS_STATUS_INVALID_ARGUMENT; } void *array = blobmsg_open_array(&bb, "results"); char *path = blobmsg_get_string(tb[DM_DEL_PATH]); if (dm_path2node(path, &node) != 0 || dmapi_object_del(&node) < 0) { set_result(&bb, path, 9005, "Invalid path"); res = -1; goto end; } set_result(&bb, path, 0, "1"); end: blobmsg_close_array(&bb, array); if (internal_trans) { // Internal transaction: need to commit or revert the changes if (res == 0 && commit_trans(1) != 0) { dmlog_error("usp_add_handler, failed to commit"); } else if (res < 0 && abort_trans() < 0) { dmlog_error("usp_add_handler, failed to abort"); } } ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); return 0; } enum { DM_OPERATE_COMMAND, DM_OPERATE_OPTIONAL, __DM_OPERATE_MAX, }; static const struct blobmsg_policy dm_operate_policy[__DM_OPERATE_MAX] = { [DM_OPERATE_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING }, [DM_OPERATE_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE }, }; // check if mandatory name is present in the input static int check_mandatory_input_name(struct blob_attr *input, const char *mandatory_name) { if (input == NULL) { return 0; } struct blob_attr *head = blobmsg_data(input); int len = blobmsg_data_len(input); struct blob_attr *attr; __blob_for_each_attr(attr, head, len) { if (dm_node_compare_command_arg_name(blobmsg_name(attr), mandatory_name) == 0) { return 1; } } return 0; } // check if the input names and values are expected as defined in the datamodel // return 1 if successful, otherwise 0 static int check_operate_inputs(const struct dm_command *cmd, struct blob_attr *input) { // check mandatory input for (int i = 0; i < cmd->inputs_cnt; i++) { if (cmd->inputs[i].mandatory) { if (check_mandatory_input_name(input, cmd->inputs[i].name) != 1) { dmlog_error("mandatory input name not found: %s", cmd->inputs[i].name); return 0; } } } if (input == NULL) { return 1; } struct blob_attr *head = blobmsg_data(input); int len = blobmsg_data_len(input); struct blob_attr *attr; __blob_for_each_attr(attr, head, len) { const char *name = blobmsg_name(attr); if (strcmp(name, "Internal_TimeRef") == 0) { continue; } if (dm_node_verify_command_input(cmd->node.node_id, name, blobmsg_get_string(attr)) != 1) { dmlog_error("invalid argument value: %s: %s", name, blobmsg_get_string(attr)); return 0; } } return 1; } void async_operate(struct ubus_context *ctx, const dm_node_t *node, const char *path, const char *cmd_key, char *input_json, struct ubus_request_data *req) { agent_ctx.last_async_id++; pid_t child = fork(); if (child == -1) { dmlog_error("fork failed"); } else if (child == 0) { struct ubus_context *ctx = ubus_connect(NULL); struct json_object *json_output = NULL; struct blob_buf bb = {}; memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); blobmsg_add_u32(&bb, "async_id", agent_ctx.last_async_id); void *array = blobmsg_open_array(&bb, "results"); void *table = blobmsg_open_table(&bb, NULL); if (dmapi_operate(node, input_json, &json_output) < 0) { dmlog_error("async_operate failed"); set_result(&bb, path, 7022, "Operation failed"); } else { blobmsg_add_string(&bb, "path", path); blobmsg_add_string(&bb, "data", cmd_key); if (json_output != NULL) { blobmsg_add_json_element(&bb, "output", json_output); json_object_put(json_output); } blobmsg_close_table(&bb, table); } blobmsg_close_array(&bb, array); ubus_send_event(ctx, BBF_ASYNC_COMPLETE_EVENT, bb.head); blob_buf_free(&bb); _exit(0); } else { struct async_operate_request *async_req = calloc(1, sizeof(struct async_operate_request)); async_req->async_id = agent_ctx.last_async_id; ubus_defer_request(ctx, req, &async_req->req); list_add_tail(&async_req->list, &agent_ctx.async_req_list); } } static int contains_invalid_input_status(const struct dm_command *cmd) { for (int i = 0; i < cmd->outputs_cnt; i++) { if (strcmp(cmd->outputs[i].name, "Status") == 0) { if (cmd->outputs[i].enum_values) { for (int j = 0; cmd->outputs[i].enum_values[j]; j++) { if (strcmp(cmd->outputs[i].enum_values[j], "Error_Invalid_Input") == 0) { return 1; } } } } } return 0; } static void set_invalid_input_status(struct blob_buf *bb, const char *path) { void *table = blobmsg_open_table(bb, NULL); blobmsg_add_string(bb, "path", path); blobmsg_add_string(bb, "data", ""); void *output = blobmsg_open_array(bb, "output"); void *table2 = blobmsg_open_table(bb, NULL); blobmsg_add_string(bb, "path", "Status"); blobmsg_add_string(bb, "data", "Error_Invalid_Input"); blobmsg_add_string(bb, "type", "xsd:string"); blobmsg_close_table(bb, table2); blobmsg_close_array(bb, output); blobmsg_close_table(bb, table); } static int usp_operate_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)), struct ubus_request_data *req, const char *method __attribute__((unused)), struct blob_attr *msg) { struct blob_attr *tb[__DM_OPERATE_MAX] = {NULL}; bbf_options_t opts; struct blob_buf bb; dm_node_t node; struct blob_attr *op_input = NULL; const char *cmd_key = ""; memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); memset(&opts, 0, sizeof(opts)); if (blobmsg_parse(dm_operate_policy, __DM_OPERATE_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_error("usp_operate_handler, failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[DM_OPERATE_COMMAND]) { dmlog_error("usp_operate_handler, missing the operate pathname"); return UBUS_STATUS_INVALID_ARGUMENT; } get_bbf_options(&opts, tb[DM_OPERATE_OPTIONAL]); if (tb[DM_OPERATE_OPTIONAL]) { struct blob_attr *attr; size_t rem; blobmsg_for_each_attr(attr, tb[DM_OPERATE_OPTIONAL], rem) { if (strcmp(blobmsg_name(attr), "command_key") == 0) cmd_key = blobmsg_get_string(attr); if (strcmp(blobmsg_name(attr), "input") == 0) op_input = attr; } } void *array = blobmsg_open_array(&bb, "results"); char *path = blobmsg_get_string(tb[DM_OPERATE_COMMAND]); if (dm_path2node(path, &node) != 0 || !dm_node_is_command(node.id)) { dmlog_error("usp_operate_handler, invalid operate pathname %s", path); if (opts.dm_type == DM_PROTO_CWMP) set_result(&bb, path, 9005, "Invalid parameter name"); else set_result(&bb, path, 7026, "Invalid path"); goto end; } const struct dm_command *cmd = dm_node_get_command(node.id); if (check_operate_inputs(cmd, op_input) != 1) { dmlog_error("failed to verify input arguments"); if (contains_invalid_input_status(cmd)) { set_invalid_input_status(&bb, path); } else { dmlog_error("send 7027"); set_result(&bb, path, 7027, "Invalid command arguments"); } goto end; } char *input_json = NULL; if (op_input) { input_json = blobmsg_format_json(op_input, true); } if (cmd->async) { async_operate(ctx, &node, path, cmd_key, input_json, req); blob_buf_free(&bb); if (input_json) { free(input_json); } return 0; } struct json_object *json_output = NULL; if (dmapi_operate(&node, input_json, &json_output) < 0) { dmlog_error("dmapi_operate failed"); set_result(&bb, path, 7022, "Command failure"); goto end; } else { void *table = blobmsg_open_table(&bb, NULL); blobmsg_add_string(&bb, "path", path); if (json_output != NULL) { blobmsg_add_string(&bb, "data", cmd_key); blobmsg_add_json_element(&bb, "output", json_output); json_object_put(json_output); } blobmsg_add_u8(&bb, "status", true); blobmsg_close_table(&bb, table); } if (input_json) { free(input_json); } end: blobmsg_close_array(&bb, array); ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); return 0; } enum { BBF_NOTIFY_NAME, BBF_NOTIFY_PRAMS, __BBF_NOTIFY_MAX, }; static const struct blobmsg_policy dm_notify_event_policy[] = { [BBF_NOTIFY_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, [BBF_NOTIFY_PRAMS] = { .name = "parameters", .type = BLOBMSG_TYPE_TABLE }, }; static int usp_notify_event(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req __attribute__((unused)), const char *method, struct blob_attr *msg) { struct blob_attr *tb[__BBF_NOTIFY_MAX] = {NULL}; if (blobmsg_parse(dm_notify_event_policy, __BBF_NOTIFY_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_error("usp_notify_event,failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[BBF_NOTIFY_NAME]) { dmlog_error("usp_notify_event, missing notify name"); return UBUS_STATUS_INVALID_ARGUMENT; } ubus_send_event(ctx, BBF_EVENT, msg); return 0; } static void async_operate_complete_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg) { (void)ev; struct blob_attr *tb[1] = {NULL}; const struct blobmsg_policy policy[] = { [0] = {.name = "async_id", .type = BLOBMSG_TYPE_INT32} }; if (blobmsg_parse(policy, 1, tb, blob_data(msg), blob_len(msg)) || !tb[0]) { dmlog_error("Failed to parse msg in async_operate_complete_event_handler"); return; } unsigned int async_id = blobmsg_get_u32(tb[0]); struct async_operate_request *async_req; list_for_each_entry(async_req, &agent_ctx.async_req_list, list) { if (async_req->async_id == async_id) { ubus_send_reply(ctx, &async_req->req, msg); ubus_complete_deferred_request(ctx, &async_req->req, 0); list_del(&async_req->list); free(async_req); return; } } dmlog_error("unknown aysnc id from the notify %d", async_id); } static int register_async_event() { printf("DEBUG: register_async_event() starting\n"); struct ubus_event_handler *ev = (struct ubus_event_handler *)malloc(sizeof(struct ubus_event_handler)); if (!ev) { printf("DEBUG: malloc failed in register_async_event()\n"); return -1; } memset(ev, 0, sizeof(struct ubus_event_handler)); ev->cb = async_operate_complete_event_handler; if (0 != ubus_register_event_handler(&agent_ctx.ubus_ctx, ev, BBF_ASYNC_COMPLETE_EVENT)) { printf("DEBUG: Failed to register ubus event usp_async_complete\n"); dmlog_error("Failed to register ubus event usp_async_complete"); return -1; } printf("DEBUG: register_async_event() completed successfully\n"); return 0; } static void dm_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg) { struct dm_ubus_event_handler *myhandler = container_of(ev, struct dm_ubus_event_handler, uhandler); char *json_str = blobmsg_format_json(msg, true); dmlog_debug("dm_event_handler ubus event: %s, for node: %s, msg: %s", type, dm_node_id_str(myhandler->node_id), json_str); if (json_str == NULL) { return; } int len = strlen(json_str); if (len < 2) { return; } char * json_input = json_str; if (json_str[0] == '{' && json_str[1] == '{') { // object without name, which is invalid JSON format, remove the outer "{}". json_str[0] = '\0'; json_str[len - 1] = '\0'; json_input = json_str + 1; } struct json_object *res = NULL; dmapi_handle_ubus_event(myhandler->node_id, json_input, &res); if (json_str) { free(json_str); } if (!res) { dmlog_error("dm_event_handler, event not handled: %s", type); return; } const struct dm_event *evt_obj = dm_node_get_event(myhandler->node_id); if (!evt_obj) { dmlog_error("dm_event_handler, event not found: %d", myhandler->node_id); return; } struct blob_buf bb; memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); blobmsg_add_string(&bb, "name", dm_node_id_str(myhandler->node_id)); blobmsg_add_json_element(&bb, "input", res); ubus_send_event(ctx, "bbfdm.event", bb.head); json_object_put(res); blob_buf_free(&bb); } static void register_dm_event(const struct dm_object * obj) { printf("DEBUG: register_dm_event() starting, obj=%p\n", obj); if (!obj) { printf("DEBUG: register_dm_event() obj is NULL\n"); return; } printf("DEBUG: register_dm_event() event_num=%d\n", obj->event_num); for (int i = 0; i < obj->event_num; i++) { const struct dm_event * evt = (const struct dm_event*)obj->event_list[i]; if (evt->ubus_event) { printf("DEBUG: registering ubus event: %s\n", evt->ubus_event); dmlog_debug("registerred ubus event: %s for node %s", evt->ubus_event, dm_node_id_str(evt->node.node_id)); struct dm_ubus_event_handler *ev = (struct dm_ubus_event_handler *)malloc(sizeof(struct dm_ubus_event_handler)); if (!ev) { printf("DEBUG: malloc failed for dm_ubus_event_handler\n"); continue; } memset(ev, 0, sizeof(struct dm_ubus_event_handler)); ev->uhandler.cb = dm_event_handler; ev->node_id = evt->node.node_id; if (0 != ubus_register_event_handler(&agent_ctx.ubus_ctx, &ev->uhandler, evt->ubus_event)) { printf("DEBUG: Failed to register ubus event %s\n", evt->ubus_event); dmlog_error("Failed to register ubus event %s", evt->ubus_event); } } } printf("DEBUG: register_dm_event() object_num=%d\n", obj->object_num); for (int i = 0; i < obj->object_num; i++) { const struct dm_object * child_obj = (const struct dm_object*)obj->object_list[i]; register_dm_event(child_obj); } printf("DEBUG: register_dm_event() completed\n"); } enum { DM_DUMP_BUF_PATH, __DM_DUMP_BUF_MAX }; static const struct blobmsg_policy dm_dump_buf_policy[] = { [DM_DUMP_BUF_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, }; static int usp_dump_buf_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)), struct ubus_request_data *req, const char *method __attribute__((unused)), struct blob_attr *msg) { struct blob_attr *tb[__DM_DUMP_BUF_MAX]; dm_node_t node; if (blobmsg_parse(dm_dump_buf_policy, __DM_DUMP_BUF_MAX, tb, blob_data(msg), blob_len(msg))) { dmlog_error("usp_dump_buf_handler, failed to parse blob"); return UBUS_STATUS_UNKNOWN_ERROR; } if (!tb[DM_DUMP_BUF_PATH]) { dmlog_error("usp_dump_buf_handler, missing path parameter"); return UBUS_STATUS_INVALID_ARGUMENT; } if (dm_path2node(blobmsg_get_string(tb[DM_DUMP_BUF_PATH]), &node) != 0) { dmlog_error("usp_dump_buf_handler, missing path parameter"); return UBUS_STATUS_INVALID_ARGUMENT; } dmapi_dump_node_buffer(&node); return 0; } // just to be compatible with bbfdm int bbfdm_refresh_references_db(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { struct blob_buf bb = {0}; memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); blobmsg_add_u8(&bb, "status", true); ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); return 0; } static struct ubus_method bbf_methods[] = { UBUS_METHOD("schema", usp_schema_handler, dm_schema_policy), UBUS_METHOD("get", usp_get_handler, dm_get_policy), UBUS_METHOD("instances", usp_instances_handler, dm_instances_policy), UBUS_METHOD("transaction", usp_transaction_handler, transaction_policy), UBUS_METHOD("set", usp_set_handler, dm_set_policy), UBUS_METHOD("add", usp_add_handler, dm_add_policy), UBUS_METHOD("del", usp_del_handler, dm_del_policy), UBUS_METHOD("operate", usp_operate_handler, dm_operate_policy), UBUS_METHOD("dump-buffer", usp_dump_buf_handler, dm_dump_buf_policy), UBUS_METHOD("notify_event", usp_notify_event, dm_notify_event_policy), UBUS_METHOD_NOARG("refresh_references_db", bbfdm_refresh_references_db) }; static struct ubus_object_type bbf_type = UBUS_OBJECT_TYPE(METHOD_NAME, bbf_methods); static struct ubus_object bbf_object = { .name = METHOD_NAME, .type = &bbf_type, .methods = bbf_methods, .n_methods = ARRAY_SIZE(bbf_methods) }; static int usp_init(struct agent_context *u) { int ret; printf("DEBUG: usp_init() starting\n"); dmlog_debug("Registering bbfdm ubus objects...."); ret = ubus_add_object(&u->ubus_ctx, &bbf_object); if (ret) { printf("DEBUG: ubus_add_object failed, ret=%d\n", ret); dmlog_error("ubus_add_object failed"); return ret; } printf("DEBUG: usp_init() completed successfully\n"); return ret; } int start_dm_agent() { int ret = 0; const char *ubus_socket = NULL; printf("DEBUG: start_dm_agent() called\n"); memset(&agent_ctx, 0, sizeof(struct agent_context)); INIT_LIST_HEAD(&agent_ctx.async_req_list); printf("DEBUG: agent_ctx initialized\n"); uloop_init(); printf("DEBUG: uloop_init() completed\n"); ret = ubus_connect_ctx(&agent_ctx.ubus_ctx, ubus_socket); if (ret != UBUS_STATUS_OK) { printf("DEBUG: Failed to connect to ubus, ret=%d\n", ret); dmlog_error("Failed to connect to ubus"); return -1; } printf("DEBUG: ubus_connect_ctx() successful\n"); printf("DEBUG: calling register_async_event()\n"); register_async_event(); printf("DEBUG: calling register_dm_event()\n"); register_dm_event(dm_node_get_object(0)); printf("DEBUG: calling ubus_add_uloop()\n"); ubus_add_uloop(&agent_ctx.ubus_ctx); printf("DEBUG: calling usp_init()\n"); ret = usp_init(&agent_ctx); if (ret != UBUS_STATUS_OK) { printf("DEBUG: usp_init failed, ret=%d\n", ret); dmlog_error("usp_init failed"); ret = UBUS_STATUS_UNKNOWN_ERROR; goto exit; } printf("DEBUG: usp_init() successful\n"); dmlog_info("dm-agent started"); printf("DEBUG: entering uloop_run()\n"); uloop_run(); printf("DEBUG: uloop_run() exited\n"); exit: printf("DEBUG: cleaning up and exiting\n"); ubus_shutdown(&agent_ctx.ubus_ctx); uloop_done(); return ret; }