diff --git a/README.md b/README.md index d3a5e00c..901a2a75 100644 --- a/README.md +++ b/README.md @@ -436,7 +436,7 @@ BBF_VENDOR_PREFIX="X_TEST_COM_" - The directory **'dmtree/vendor/test/'** contains an example of **test** vendor implementation -## BBFDM Dynamic Object/Parameter/Operate +## BBFDM Dynamic Object/Parameter/Operate/Event `bbfdm` library allows all applications installed on the box to import its own Data Model parameters at run time in two formats: @@ -682,6 +682,131 @@ The application should bring its JSON file under **'/etc/bbfdm/json/'** path wit } ``` +**5. Object with Event and Operate command:** + +```bash +{ + "Device.X_IOPSYS_Test.": { + "type": "object", + "protocols": [ + "cwmp", + "usp" + ], + "array": false, + "Push!": { + "type": "event", + "version": "2.13", + "protocols": [ + "usp" + ], + "data": { + "type": "string", + "read": true, + "write": true, + "version": "2.13", + "protocols": [ + "usp" + ], + "datatype": "string" + } + }, + "Status()": { + "type": "command", + "async": true, + "version" : "2.12", + "protocols": [ + "usp" + ], + "input": { + "Option" : { + "type" : "string", + "read" : "true", + "write" : "true", + "protocol" : [ + "usp" + ], + "datatype" : "string" + } + }, + "output": { + "Result" : { + "type" : "string", + "read" : "true", + "write" : "false", + "protocol" : [ + "usp" + ], + "datatype" : "string" + } + }, + "mapping": [ + { + "type" : "ubus", + "ubus" : { + "object" : "test", + "method" : "status" + } + } + ] + } + } +} +``` + +- **UBUS command:** ubus call usp operate '{"path":"Device.X_IOPSYS_Test.", "action":"Status()", "input":{"Option":"Last"}}' + +```bash +{ + "Results": [ + { + "path": "Device.X_IOPSYS_Test.Status()", + "result": [ + { + "Result": "Success" + } + ] + } + ] +} +``` + +- **UBUS command:** ubus call usp get_supported_dm + +```bash +{ + "parameters": [ + { + "parameter": "Device.X_IOPSYS_Test.Push!", + "type": "xsd:event", + "in": [ + "data" + ] + }, + ... + ] +} +``` + +- **UBUS command:** ubus call usp list_operate + +```bash +{ + "parameters": [ + { + "parameter": "Device.X_IOPSYS_Test.Status()", + "type": "async", + "in": [ + "Option" + ], + "out": [ + "Result" + ] + }, + ... + ] +} +``` + > Note1: JSON File can only add vendor or standard objects that are not implemented by `libbbfdm` > Note2: JSON File is not allowed to overwrite objects/parameters diff --git a/dmdynamicjson.c b/dmdynamicjson.c index 25e6ffdf..b09d1e2b 100644 --- a/dmdynamicjson.c +++ b/dmdynamicjson.c @@ -19,6 +19,14 @@ static LIST_HEAD(loaded_json_files); static LIST_HEAD(json_list); static LIST_HEAD(json_memhead); +static operation_args empty_cmd = { + .in = (const char**)NULL, + .out = (const char**)NULL +}; + +static event_args empty_event = { + .param = (const char**)NULL, +}; struct loaded_json_file { @@ -30,17 +38,36 @@ struct dm_json_obj { struct list_head list; json_object *data; char *name; + operation_args command_arg; + event_args event_arg; }; -static void save_json_data(struct list_head *json_list, char *name, json_object *data) +static void save_json_data(struct list_head *json_list, char *name, json_object *data, + const char **in_p, const char **out_p, const char **ev_arg) { struct dm_json_obj *dm_json_obj = dm_dynamic_calloc(&json_memhead, 1, sizeof(struct dm_json_obj)); if (name) dm_json_obj->name = dm_dynamic_strdup(&json_memhead, name); if (data) dm_json_obj->data = data; + dm_json_obj->command_arg.in = in_p; + dm_json_obj->command_arg.out = out_p; + dm_json_obj->event_arg.param = ev_arg; list_add_tail(&dm_json_obj->list, json_list); } +static void free_event_command_args(const char **arg_p) +{ + if (arg_p) { + int i = 0; + while (arg_p[i]) { + dmfree((char *)arg_p[i]); + i++; + } + + free((char **)arg_p); + } +} + static void free_json_data(struct list_head *json_list) { struct dm_json_obj *dm_json_obj = NULL; @@ -49,6 +76,9 @@ static void free_json_data(struct list_head *json_list) dm_json_obj = list_entry(json_list->next, struct dm_json_obj, list); list_del(&dm_json_obj->list); dmfree(dm_json_obj->name); + free_event_command_args(dm_json_obj->command_arg.in); + free_event_command_args(dm_json_obj->command_arg.out); + free_event_command_args(dm_json_obj->event_arg.param); dmfree(dm_json_obj); } } @@ -539,6 +569,118 @@ static int getvalue_param(char *refparam, struct dmctx *ctx, void *data, char *i return 0; } +static int ubus_set_operate(json_object *mapping_obj, struct dmctx *ctx, void *data, void *value, char *instance) +{ + struct json_object *ubus_obj = NULL; + struct json_object *object = NULL; + struct json_object *method = NULL; + struct json_object *res = NULL; + char obj_name[128] = {0}, *opt = NULL; + + json_object_object_get_ex(mapping_obj, "ubus", &ubus_obj); + json_object_object_get_ex(ubus_obj, "object", &object); + json_object_object_get_ex(ubus_obj, "method", &method); + + if ((opt = strstr(json_object_get_string(object), "@Name"))) { + *opt = '\0'; + snprintf(obj_name, sizeof(obj_name), "%s%s", json_object_get_string(object), section_name((struct uci_section *)data)); + } else if ((opt = strstr(json_object_get_string(object), "@i-1"))) { + *opt = '\0'; + snprintf(obj_name, sizeof(obj_name), "%s%d", json_object_get_string(object), atoi(instance) - 1); + } else { + DM_STRNCPY(obj_name, json_object_get_string(object), sizeof(obj_name)); + } + + dmubus_operate_blob_set(obj_name, json_object_get_string(method), value, &res); + + if (res) { + json_object_object_foreach(res, key, val) { + add_list_parameter(ctx, dmstrdup(key), dmstrdup(json_object_to_json_string(val)), DMT_TYPE[DMT_STRING], NULL); + } + json_object_put(res); + } + + return CMD_SUCCESS; +} + +static int setcommand_param(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action) +{ + struct dm_json_obj *leaf_node = NULL; + struct json_object *p_obj = NULL, *map_arr = NULL, *map_obj = NULL, *type = NULL; + + char *obj = generate_path_without_instance(refparam, false); + list_for_each_entry(leaf_node, &json_list, list) { + if (strcmp(leaf_node->name, obj) == 0) { + p_obj = leaf_node->data; + break; + } + } + + if (p_obj == NULL) { + return CMD_FAIL; + } + + json_object_object_get_ex(p_obj, "mapping", &map_arr); + if (map_arr && json_object_get_type(map_arr) == json_type_array) + map_obj = json_object_array_get_idx(map_arr, 0); + + if (!map_obj) { + return CMD_FAIL; + } + + json_object_object_get_ex(map_obj, "type", &type); + + if (type && strcmp(json_object_get_string(type), "ubus") == 0) { + return ubus_set_operate(map_obj, ctx, data, value, instance); + } + + return CMD_FAIL; +} + +static int getcommand_param(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + struct dm_json_obj *leaf = NULL; + bool found = false; + + char *obj = generate_path_without_instance(refparam, false); + list_for_each_entry(leaf, &json_list, list) { + if (strcmp(leaf->name, obj) == 0) { + found = true; + break; + } + } + + if (found) { + *value = (char *)&leaf->command_arg; + } else { + *value = (char *)&empty_cmd; + } + + return 0; +} + +static int getevent_param(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + struct dm_json_obj *leaf = NULL; + bool found = false; + + char *obj = generate_path_without_instance(refparam, false); + list_for_each_entry(leaf, &json_list, list) { + if (strcmp(leaf->name, obj) == 0) { + found = true; + break; + } + } + + if (found) { + *value = (char *)&leaf->event_arg; + } else { + *value = (char *)&empty_event; + } + + return 0; +} + static int fill_all_arguments(struct json_object *range, struct range_args range_arg[], int range_len) { for (int i = 0; i < range_len; i++) { @@ -841,15 +983,53 @@ static void parse_mapping_obj(char *object, json_object *mapping_obj, struct lis if (!mapping_obj) return; - save_json_data(list, object, mapping_obj); + save_json_data(list, object, mapping_obj, NULL, NULL, NULL); +} + +static bool valid_event_param(char *param) +{ + bool ret; + + if (strcmp(param, "type") == 0) { + ret = false; + } else if (strcmp(param, "version") == 0) { + ret = false; + } else if (strcmp(param, "protocols") == 0) { + ret = false; + } else { + ret = true; + } + + return ret; +} + +static char** fill_command_param(int count, struct json_object *obj) +{ + char **res_p = NULL; + if (!obj) + return res_p; + + res_p = malloc(sizeof(char *) * (count + 1)); + if (res_p) { + res_p[count] = NULL; + int id = 0; + + json_object_object_foreach(obj, key, res_obj) { + res_p[id] = dm_dynamic_strdup(&json_memhead, key); + id++; + } + } + + return res_p; } static void parse_param(char *object, char *param, json_object *jobj, DMLEAF *pleaf, int i, struct list_head *list) { /* PARAM, permission, type, getvalue, setvalue, bbfdm_type(6)*/ - struct json_object *type = NULL, *protocols = NULL, *write = NULL; + struct json_object *type = NULL, *protocols = NULL, *write = NULL, *async = NULL; char full_param[512] = {0}; size_t n_proto; + char **in_p = NULL, **out_p = NULL, **ev_arg = NULL; if (!jobj || !pleaf) return; @@ -857,10 +1037,6 @@ static void parse_param(char *object, char *param, json_object *jobj, DMLEAF *pl //PARAM pleaf[i].parameter = dm_dynamic_strdup(&json_memhead, param); - //permission - json_object_object_get_ex(jobj, "write", &write); - pleaf[i].permission = (write && json_object_get_boolean(write)) ? &DMWRITE : &DMREAD; - //type json_object_object_get_ex(jobj, "type", &type); if (type && strcmp(json_object_get_string(type), "boolean") == 0) @@ -879,14 +1055,84 @@ static void parse_param(char *object, char *param, json_object *jobj, DMLEAF *pl pleaf[i].type = DMT_TIME; else if (type && strcmp(json_object_get_string(type), "base64") == 0) pleaf[i].type = DMT_BASE64; + else if (type && strcmp(json_object_get_string(type), "command") == 0) + pleaf[i].type = DMT_COMMAND; + else if (type && strcmp(json_object_get_string(type), "event") == 0) + pleaf[i].type = DMT_EVENT; else pleaf[i].type = DMT_STRING; + //permission + if (pleaf[i].type == DMT_EVENT) { + pleaf[i].permission = &DMREAD; + } else if (pleaf[i].type == DMT_COMMAND) { + json_object_object_get_ex(jobj, "async", &async); + pleaf[i].permission = (async && json_object_get_boolean(async)) ? &DMASYNC : &DMSYNC; + } else { + json_object_object_get_ex(jobj, "write", &write); + pleaf[i].permission = (write && json_object_get_boolean(write)) ? &DMWRITE : &DMREAD; + } + //getvalue - pleaf[i].getvalue = getvalue_param; + if (pleaf[i].type == DMT_EVENT) { + int param_count = 0; + json_object_object_foreach(jobj, param, val) { + if (valid_event_param(param)) { + param_count++; + if (!ev_arg) { + ev_arg = malloc(sizeof(char*) * param_count); + if (!ev_arg) + break; + } else { + ev_arg = realloc(ev_arg, sizeof(char*) * param_count); + if (!ev_arg) + break; + } + + ev_arg[param_count - 1] = dm_dynamic_strdup(&json_memhead, param); + } + } + + if (ev_arg) { + param_count++; + ev_arg = realloc(ev_arg, sizeof(char*) * param_count); + ev_arg[param_count - 1] = NULL; + } + + pleaf[i].getvalue = getevent_param; + } else if (pleaf[i].type == DMT_COMMAND) { + struct json_object *input_obj = NULL, *output_obj = NULL; + + json_object_object_get_ex(jobj, "input", &input_obj); + json_object_object_get_ex(jobj, "output", &output_obj); + + if (input_obj && json_object_get_type(input_obj) == json_type_object) { + int count = json_object_object_length(input_obj); + if (count) { + in_p = fill_command_param(count, input_obj); + } + } + + if (output_obj && json_object_get_type(output_obj) == json_type_object) { + int count = json_object_object_length(output_obj); + if (count) { + out_p = fill_command_param(count, output_obj); + } + } + + pleaf[i].getvalue = getcommand_param; + } else { + pleaf[i].getvalue = getvalue_param; + } //setvalue - pleaf[i].setvalue = (write && json_object_get_boolean(write)) ? setvalue_param : NULL; + if (pleaf[i].type == DMT_EVENT) { + pleaf[i].setvalue = NULL; + } else if (pleaf[i].type == DMT_COMMAND) { + pleaf[i].setvalue = setcommand_param; + } else { + pleaf[i].setvalue = (write && json_object_get_boolean(write)) ? setvalue_param : NULL; + } //bbfdm_type json_object_object_get_ex(jobj, "protocols", &protocols); @@ -905,7 +1151,7 @@ static void parse_param(char *object, char *param, json_object *jobj, DMLEAF *pl pleaf[i].bbfdm_type = BBFDM_BOTH; snprintf(full_param, sizeof(full_param), "%s%s", object, param); - save_json_data(list, full_param, jobj); + save_json_data(list, full_param, jobj, (const char**)in_p, (const char**)out_p, (const char**)ev_arg); } static void count_obj_param_under_jsonobj(json_object *jsonobj, int *obj_number, int *param_number) diff --git a/libbbf_api/dmubus.c b/libbbf_api/dmubus.c index a9f30e08..8d75e58c 100644 --- a/libbbf_api/dmubus.c +++ b/libbbf_api/dmubus.c @@ -123,6 +123,42 @@ int dmubus_call_set(char *obj, char *method, struct ubus_arg u_args[], int u_arg return rc; } +int dmubus_operate_blob_set(char *obj, char *method, void *value, json_object **resp) +{ + uint32_t id; + struct blob_buf blob; + int rc = -1; + + json_res = NULL; + *resp = NULL; + + if (ubus_ctx == NULL) { + ubus_ctx = dm_libubus_init(); + if (ubus_ctx == NULL) + return rc; + } + + memset(&blob, 0, sizeof(struct blob_buf)); + blob_buf_init(&blob, 0); + + if (value != NULL) { + if (!blobmsg_add_object(&blob, (json_object *)value)) { + blob_buf_free(&blob); + return rc; + } + } + + if (!ubus_lookup_id(ubus_ctx, obj, &id)) { + rc = ubus_invoke(ubus_ctx, id, method, blob.head, + receive_call_result_data, NULL, timeout); + } + + *resp = json_res; + blob_buf_free(&blob); + return rc; + +} + static inline json_object *ubus_call_req(char *obj, char *method, struct ubus_arg u_args[], int u_args_size) { __dm_ubus_call(obj, method, u_args, u_args_size); diff --git a/libbbf_api/dmubus.h b/libbbf_api/dmubus.h index 89f4fd1a..e0b32520 100644 --- a/libbbf_api/dmubus.h +++ b/libbbf_api/dmubus.h @@ -31,6 +31,7 @@ struct ubus_arg { int dmubus_call(char *obj, char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res); int dmubus_call_set(char *obj, char *method, struct ubus_arg u_args[], int u_args_size); +int dmubus_operate_blob_set(char *obj, char *method, void *value, json_object **resp); bool dmubus_object_method_exists(const char *obj); void dmubus_free();