diff --git a/README.md b/README.md index 45d39417ccbfdd872092ad05b0f60951a05736eb..d2d3375a0121e84589249006a29aba9adcf8887c 100644 --- a/README.md +++ b/README.md @@ -1025,7 +1025,43 @@ root@iopsys:~# ubus call usp.raw list_inform root@iopsys:~# ubus call usp.raw init_notify '{"proto":"cwmp", "amd_version":5, "instance_mode": 0}' root@iopsys:~# - +root@iopsys:~# ubus call usp set '{"path":"Device.Users.User.[Username==\"xyz1\"].", "values":{"Username":"xyz1", "Enable":"dummy", "Password":"yzssssx"}, "proto":"usp"}' +{ + "parameters": [ + { + "path": "Device.Users.User.2.Username", + "status": true + }, + { + "path": "Device.Users.User.2.Enable", + "status": false, + "fault": 7012 + }, + { + "path": "Device.Users.User.2.Password", + "status": true + } + ] +} +root@iopsys:~# +root@iopsys:~# ubus call usp.raw setm_values '{"pv_tuple":[{"path":"Device.Users.User.2.Username", "value":"xzzz"}, {"path":"Device.Users.User.2.RemoteAccessCapable", "value":"2"}, {"path":"Device.Users.User.2.Password", "value":"zzzzzzz"}], "proto":"usp"}' +{ + "parameters": [ + { + "path": "Device.Users.User.2.Username", + "status": true + }, + { + "path": "Device.Users.User.2.RemoteAccessCapable", + "status": false, + "fault": 7012 + }, + { + "path": "Device.Users.User.2.Password", + "status": true + } + ] +} ```` - For more info on the usp ubus API see [link](./docs/api/usp.md) diff --git a/src/add_delete.h b/src/add_delete.h index 085d4c6fe05e8fd1cccc4f8699efbc358ad287e7..f5cef531f5c305a0de5e8f6dff1c0722642502ca 100644 --- a/src/add_delete.h +++ b/src/add_delete.h @@ -1,5 +1,14 @@ #ifndef ADD_DEL_H #define ADD_DEL_H + +enum { + DM_ADD_PATH, + DM_ADD_PROTO, + DM_ADD_PARAMETER_KEY, + DM_ADD_INSTANCE, + __DM_ADD_MAX +}; + void create_add_response(usp_data_t *data, struct blob_buf *bb); void create_del_response(usp_data_t *data, struct blob_buf *bb); #endif /* ADD_DEL_H */ diff --git a/src/get.h b/src/get.h index 02c5ee895afb581812e706915f15a3fbfc9ec955..a602a68d9659ed134676d993ccba4757ef8f26eb 100644 --- a/src/get.h +++ b/src/get.h @@ -2,6 +2,23 @@ #define GET_H #include "common.h" +enum { + DM_GET_PATH, + DM_GET_PROTO, + DM_GET_MAXDEPTH, + DM_GET_NXT_LVL, + DM_GET_INSTANCE, + __DM_GET_MAX +}; + +enum { + DM_GET_SAFE_PATHS, + DM_GET_SAFE_PROTO, + DM_GET_SAFE_NXT_LVL, + DM_GET_SAFE_INSTANCE, + __DM_GET_SAFE_MAX +}; + void usp_validate_path(usp_data_t *data); void usp_get_value(usp_data_t *data); void usp_get_instance(usp_data_t *data); diff --git a/src/set.c b/src/set.c index 42673593ce52ae9e56a4112b75a13b7f350a10a8..405ec0b0f4506122721a6a49e4b48188f5f54f2e 100644 --- a/src/set.c +++ b/src/set.c @@ -28,15 +28,17 @@ #include <libbbfdm/dmentry.h> +static const struct blobmsg_policy dm_setm_value_policy[]= { + [DM_SET_V_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [DM_SET_V_VALUE] = { .name = "value", .type = BLOBMSG_TYPE_STRING }, + [DM_SET_V_KEY] = { .name = "key", .type = BLOBMSG_TYPE_STRING }, +}; + void usp_set_value(usp_data_t *data) { struct blob_buf bb; struct ubus_context *ctx; struct ubus_request_data *req; - char *qpath; - struct list_head *val_pv_list; - char *key; - char temp[MAX_DM_PATH] = {0}; void *array; struct pathNode *rv; struct pvNode *pv; @@ -51,9 +53,6 @@ void usp_set_value(usp_data_t *data) ctx = data->ctx; req = data->req; - qpath = data->qpath; - val_pv_list = data->pv_list; - key = data->set_key; LIST_HEAD(resolved_paths); @@ -80,23 +79,27 @@ void usp_set_value(usp_data_t *data) return; } - fault = get_resolved_paths(&bbf_ctx, qpath, &resolved_paths); - if (fault) { - fill_err_code(&bb, fault); - } else { - array = blobmsg_open_array(&bb, "parameters"); - list_for_each_entry(rv, &resolved_paths, list) { - list_for_each_entry(pv, val_pv_list, list) { - snprintf(temp, MAX_DM_PATH, "%s%s", rv->path, pv->param); - INFO("## set (%s)::(%s)::(%s) ##", temp, pv->val, key); - fault = usp_dm_set(&bbf_ctx, &bb, temp, pv->val, key); + array = blobmsg_open_array(&bb, "parameters"); + list_for_each_entry(pv, data->pv_list, list) { + // free fault list so that it won't interfere with the next set + dm_ctx_clean_list_parameter(&bbf_ctx); + fault = get_resolved_paths(&bbf_ctx, pv->param, &resolved_paths); + if (fault) { + DEBUG("fault in resolved path %d", fault); + bb_add_string(&bb, "path", pv->param); + blobmsg_add_u8(&bb, "status", false); + fill_err_code(&bb, fault); + } else { + list_for_each_entry(rv, &resolved_paths, list) { + fault = usp_dm_set(&bbf_ctx, &bb, rv->path, pv->val, data->set_key); if (fault != USP_ERR_OK && fault_occured != true) fault_occured = true; - } } - blobmsg_close_array(&bb, array); + free_path_list(&resolved_paths); } + + blobmsg_close_array(&bb, array); ubus_send_reply(ctx, req, bb.head); // Restart all the affected services @@ -110,15 +113,15 @@ void usp_set_value(usp_data_t *data) // free blob_buf_free(&bb); - free_path_list(&resolved_paths); bbf_cleanup(&bbf_ctx); } -bool get_values_in_pvlist(struct blob_attr *blob_value, struct list_head *pv_list) +bool get_values_in_pvlist(const char *bpath, struct blob_attr *blob_value, struct list_head *pv_list) { struct blob_attr *attr; char value[MAX_DM_VALUE]; struct blobmsg_hdr *hdr; + char path[MAX_DM_PATH]; size_t tlen = (size_t)blobmsg_data_len(blob_value); __blob_for_each_attr(attr, blobmsg_data(blob_value), tlen) { @@ -145,8 +148,39 @@ bool get_values_in_pvlist(struct blob_attr *blob_value, struct list_head *pv_lis INFO("Unhandled set request type|%x|", blob_id(attr)); return false; } - add_pv_node((char *)hdr->name, value, NULL, pv_list); + + snprintf(path, MAX_DM_PATH, "%s%s", bpath, (char *)hdr->name); + add_pv_node(path, value, NULL, pv_list); } return true; } + +int fill_pvlist_from_tuple(struct blob_attr *blob, struct list_head *pv_list) +{ + size_t rem; + struct blob_attr *cur; + + blobmsg_for_each_attr(cur, blob, rem) { + struct blob_attr *tb[__DM_SET_V_MAX]; + char *path, *value, *key; + + key = NULL; + blobmsg_parse(dm_setm_value_policy, __DM_SET_V_MAX, tb, + blobmsg_data(cur), blobmsg_len(cur)); + + // ignore the tuples which does not have path and values + if (!tb[DM_SET_V_PATH] || !tb[DM_SET_V_VALUE]) + continue; + + path = blobmsg_get_string(tb[DM_SET_V_PATH]); + value = blobmsg_get_string(tb[DM_SET_V_VALUE]); + + if (tb[DM_SET_V_KEY]) + key = blobmsg_get_string(tb[DM_SET_V_KEY]); + + add_pv_node(path, value, key, pv_list); + } + + return 0; +} diff --git a/src/set.h b/src/set.h index 605759664cca991a27f7607aeb828f2b1249a7e7..f95b4ff3986ed9eb7b71b6525101345b54f8ebf7 100644 --- a/src/set.h +++ b/src/set.h @@ -2,13 +2,51 @@ #define SET_H #include "common.h" +enum { + DM_SETS_PATHS, + DM_SETS_PROTO, + DM_SETS_INSTANCE, + __DM_SETS_MAX +}; + +enum { + DM_SETS_A_NOTIF_PATH, + DM_SETS_A_NOTIF_VALUE, + DM_SETS_A_NOTIF_CHANGE, + __DM_SETS_A_NOTIF_MAX +}; + +enum { + DM_SET_PATH, + DM_SET_VALUE, + DM_SET_VALUE_TABLE, + DM_SET_PROTO, + DM_SET_PARAMETER_KEY, + DM_SET_INSTANCE, + __DM_SET_MAX, +}; + +enum { + DM_SET_V_PATH, + DM_SET_V_VALUE, + DM_SET_V_KEY, + __DM_SET_V_MAX +}; + +enum { + DM_SET_MULTI_TUPLE, + DM_SET_MULTI_PROTO, + DM_SET_MULTI_INSTANCE, + __DM_SET_MULTI_MAX +}; + void create_set_response(struct blob_buf *bb, char *value, char *key); void set_multiple_values(struct blob_buf *bb, struct blob_attr *blob_value, char *key); -bool get_values_in_pvlist(struct blob_attr *blob_value, - struct list_head *pv_list); +bool get_values_in_pvlist(const char *path, struct blob_attr *blob_value, struct list_head *pv_list); void usp_set_value(usp_data_t *data); +int fill_pvlist_from_tuple(struct blob_attr *blob, struct list_head *pv_list); #endif /* SET_H */ diff --git a/src/usp.c b/src/usp.c index 12c7237754f9303da23fc2493197d1d8e69cc55d..6660757c3ecd439d9854ef6c0ea7d6ffdbe0624e 100644 --- a/src/usp.c +++ b/src/usp.c @@ -55,47 +55,6 @@ struct uspd_async_req { struct blob_buf g_schema_bb; LIST_HEAD(g_ubus_obj_list); -enum { - DM_GET_PATH, - DM_GET_PROTO, - DM_GET_MAXDEPTH, - DM_GET_NXT_LVL, - DM_GET_INSTANCE, - __DM_GET_MAX -}; - -enum { - DM_SETS_PATHS, - DM_SETS_PROTO, - DM_SETS_INSTANCE, - __DM_SETS_MAX -}; - -enum { - DM_SETS_A_NOTIF_PATH, - DM_SETS_A_NOTIF_VALUE, - DM_SETS_A_NOTIF_CHANGE, - __DM_SETS_A_NOTIF_MAX -}; - -enum { - DM_ADD_PATH, - DM_ADD_PROTO, - DM_ADD_PARAMETER_KEY, - DM_ADD_INSTANCE, - __DM_ADD_MAX -}; - -enum { - DM_SET_PATH, - DM_SET_VALUE, - DM_SET_VALUE_TABLE, - DM_SET_PROTO, - DM_SET_PARAMETER_KEY, - DM_SET_INSTANCE, - __DM_SET_MAX, -}; - enum { DM_INIT_NOTIFY_PROTO, DM_INIT_NOTIFY_AMD, @@ -103,57 +62,6 @@ enum { __DM_INIT_NOTIFY_MAX, }; -enum { - DM_GET_SAFE_PATHS, - DM_GET_SAFE_PROTO, - DM_GET_SAFE_NXT_LVL, - DM_GET_SAFE_INSTANCE, - __DM_GET_SAFE_MAX -}; - -static const struct blobmsg_policy dm_get_safe_policy[] = { - [DM_GET_SAFE_PATHS] = { .name = "paths", .type = BLOBMSG_TYPE_ARRAY }, - [DM_GET_SAFE_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, - [DM_GET_SAFE_NXT_LVL] = { .name = "next-level", .type = BLOBMSG_TYPE_INT8 }, - [DM_GET_SAFE_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, -}; - -static const struct blobmsg_policy dm_get_policy[] = { - [DM_GET_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, - [DM_GET_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, - [DM_GET_MAXDEPTH] = { .name = "maxdepth", .type = BLOBMSG_TYPE_INT32 }, - [DM_GET_NXT_LVL] = { .name = "next-level", .type = BLOBMSG_TYPE_INT8 }, - [DM_GET_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, -}; - -static const struct blobmsg_policy dm_sets_policy[] = { - [DM_SETS_PATHS] = { .name = "paths", .type = BLOBMSG_TYPE_ARRAY }, - [DM_SETS_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, - [DM_SETS_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, -}; - -static const struct blobmsg_policy dm_sets_attrib_policy[] = { - [DM_SETS_A_NOTIF_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, - [DM_SETS_A_NOTIF_VALUE] = { .name = "notify-type", .type = BLOBMSG_TYPE_STRING }, - [DM_SETS_A_NOTIF_CHANGE] = { .name = "notify", .type = BLOBMSG_TYPE_STRING }, -}; - -static const struct blobmsg_policy dm_add_policy[] = { - [DM_ADD_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, - [DM_ADD_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, - [DM_ADD_PARAMETER_KEY] = { .name = "key", .type = BLOBMSG_TYPE_STRING }, - [DM_ADD_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, -}; - -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_VALUE_TABLE] = { .name = "values", .type = BLOBMSG_TYPE_TABLE }, - [DM_SET_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, - [DM_SET_PARAMETER_KEY] = { .name = "key", .type = BLOBMSG_TYPE_STRING }, - [DM_SET_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, -}; - struct obNode { struct ubus_object *obj; struct list_head list; @@ -229,6 +137,13 @@ static int get_instance_mode(struct blob_attr *ins) return instance_mode; } +static const struct blobmsg_policy dm_get_safe_policy[] = { + [DM_GET_SAFE_PATHS] = { .name = "paths", .type = BLOBMSG_TYPE_ARRAY }, + [DM_GET_SAFE_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, + [DM_GET_SAFE_NXT_LVL] = { .name = "next-level", .type = BLOBMSG_TYPE_INT8 }, + [DM_GET_SAFE_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, +}; + int get_safe(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, @@ -274,7 +189,6 @@ int get_safe(struct ubus_context *ctx, data.plist = &paths_list; data.dm_cmd = bbf_cmd; data.instance = get_instance_mode(tb[DM_GET_SAFE_INSTANCE]); - data.is_raw = is_str_eq(obj->name, USPRAW); get_mpath(&data); @@ -310,6 +224,18 @@ int usp_get_safe_names(struct ubus_context *ctx, return get_safe(ctx, obj, req, msg, CMD_GET_NAME); } +static const struct blobmsg_policy dm_sets_attrib_policy[] = { + [DM_SETS_A_NOTIF_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [DM_SETS_A_NOTIF_VALUE] = { .name = "notify-type", .type = BLOBMSG_TYPE_STRING }, + [DM_SETS_A_NOTIF_CHANGE] = { .name = "notify", .type = BLOBMSG_TYPE_STRING }, +}; + +static const struct blobmsg_policy dm_sets_policy[] = { + [DM_SETS_PATHS] = { .name = "paths", .type = BLOBMSG_TYPE_ARRAY }, + [DM_SETS_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, + [DM_SETS_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, +}; + int usp_set_safe_attributes(struct ubus_context *ctx, __attribute__((unused)) struct ubus_object *obj, struct ubus_request_data *req, @@ -374,6 +300,13 @@ int usp_set_safe_attributes(struct ubus_context *ctx, return 0; } +static const struct blobmsg_policy dm_add_policy[] = { + [DM_ADD_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [DM_ADD_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, + [DM_ADD_PARAMETER_KEY] = { .name = "key", .type = BLOBMSG_TYPE_STRING }, + [DM_ADD_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, +}; + int usp_add_del_handler(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) @@ -448,6 +381,14 @@ static bool is_subprocess_required(const char *path) return ret; } +static const struct blobmsg_policy dm_get_policy[] = { + [DM_GET_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, + [DM_GET_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, + [DM_GET_MAXDEPTH] = { .name = "maxdepth", .type = BLOBMSG_TYPE_INT32 }, + [DM_GET_NXT_LVL] = { .name = "next-level", .type = BLOBMSG_TYPE_INT8 }, + [DM_GET_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, +}; + int usp_get_handler(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method __attribute__((unused)), struct blob_attr *msg) @@ -513,6 +454,15 @@ int usp_get_handler(struct ubus_context *ctx, struct ubus_object *obj, return 0; } +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_VALUE_TABLE] = { .name = "values", .type = BLOBMSG_TYPE_TABLE }, + [DM_SET_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, + [DM_SET_PARAMETER_KEY] = { .name = "key", .type = BLOBMSG_TYPE_STRING }, + [DM_SET_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, +}; + int usp_set(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) @@ -526,6 +476,7 @@ int usp_set(struct ubus_context *ctx, struct ubus_object *obj, usp_data_t data; int fault = USP_ERR_OK; struct list_head pv_list; + int proto; if (blobmsg_parse(dm_set_policy, __DM_SET_MAX, tb, blob_data(msg), blob_len(msg))) { ERR("Failed to parse blob"); @@ -554,39 +505,30 @@ int usp_set(struct ubus_context *ctx, struct ubus_object *obj, INIT_LIST_HEAD(&pv_list); + plen = strlen(path); if (tb[DM_SET_VALUE]) { - char *tmp, tpath[MAX_DM_PATH] = {0}; - - plen = strlen(path); if (path[plen - 1] == '.') { fault = USP_FAULT_INVALID_PATH; } else { - tmp = strrchr(path, '.'); - snprintf(tpath, plen - strlen(tmp) + 2, "%s", path); - } - - if (fault == USP_ERR_OK) { blob_msg = blobmsg_data(tb[DM_SET_VALUE]); strncpyt(value, blob_msg, sizeof(value)); - add_pv_node(&tmp[1], value, NULL, &pv_list); - strcpy(path, tpath); + add_pv_node(path, value, NULL, &pv_list); } } - if (tb[DM_SET_VALUE_TABLE]) { - if (get_values_in_pvlist(tb[DM_SET_VALUE_TABLE], &pv_list) == false) + if (tb[DM_SET_VALUE_TABLE] && fault == USP_ERR_OK) { + if (get_values_in_pvlist(path, tb[DM_SET_VALUE_TABLE], &pv_list) == false) fault = USP_FAULT_INVALID_PATH; } + proto = get_bbf_proto_type(tb[DM_SET_PROTO]); if (fault) { memset(&bb, 0, sizeof(struct blob_buf)); blob_buf_init(&bb, 0); ERR("Fault in set path |%s|", path); - if (get_bbf_proto_type(tb[DM_SET_PROTO]) == BBFDM_CWMP) - fill_err_code(&bb, FAULT_9005); - else - fill_err_code(&bb, fault); + set_bbfdatamodel_type(proto); + fill_err_code(&bb, FAULT_9005); ubus_send_reply(ctx, req, bb.head); blob_buf_free(&bb); free_pv_list(&pv_list); @@ -600,7 +542,7 @@ int usp_set(struct ubus_context *ctx, struct ubus_object *obj, data.qpath = path; data.pv_list = &pv_list; data.set_key = key; - data.proto = get_bbf_proto_type(tb[DM_SET_PROTO]); + data.proto = proto; data.instance = get_instance_mode(tb[DM_SET_INSTANCE]); data.is_raw = is_str_eq(obj->name, USPRAW); @@ -961,6 +903,46 @@ int usp_transaction_abort(struct ubus_context *actx, struct ubus_object *obj, return 0; } +static const struct blobmsg_policy dm_set_multi_policy[] = { + [DM_SET_MULTI_TUPLE] = { .name = "pv_tuple", .type = BLOBMSG_TYPE_ARRAY }, + [DM_SET_MULTI_PROTO] = { .name = "proto", .type = BLOBMSG_TYPE_STRING }, + [DM_SET_MULTI_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, +}; + +int handle_set_multi_value(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method __attribute__((unused)), + struct blob_attr *msg) +{ + struct blob_attr *tb[__DM_SET_MULTI_MAX]; + usp_data_t data; + struct list_head pv_list; + + memset(&data, 0, sizeof(usp_data_t)); + blobmsg_parse(dm_set_multi_policy, __DM_SET_MULTI_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (tb[DM_SET_MULTI_TUPLE] == NULL) + return UBUS_STATUS_INVALID_ARGUMENT; + + INIT_LIST_HEAD(&pv_list); + + fill_pvlist_from_tuple(tb[DM_SET_MULTI_TUPLE], &pv_list); + if (list_empty(&pv_list)) + return UBUS_STATUS_INVALID_ARGUMENT; + + data.ctx = ctx; + data.req = req; + data.proto = get_bbf_proto_type(tb[DM_SET_MULTI_PROTO]); + data.is_raw = is_str_eq(obj->name, USPRAW); + data.instance = get_instance_mode(tb[DM_SET_MULTI_INSTANCE]); + data.pv_list = &pv_list; + + usp_set_value(&data); + + free_pv_list(&pv_list); + + return 0; +} static struct ubus_method usp_methods[] = { UBUS_METHOD_NOARG("list_operate", usp_list_operate), UBUS_METHOD("get", usp_get_handler, dm_get_policy), @@ -988,6 +970,7 @@ static struct ubus_method usp_raw_methods[] = { UBUS_METHOD("getm_names", usp_get_safe_names, dm_get_safe_policy), UBUS_METHOD("getm_attributes", usp_get_attributes, dm_get_safe_policy), UBUS_METHOD("setm_attributes", usp_set_safe_attributes, dm_sets_policy), + UBUS_METHOD("setm_values", handle_set_multi_value, dm_set_multi_policy), UBUS_METHOD("init_notify", usp_init_notify_file, dm_init_notify_policy), UBUS_METHOD_NOARG("transaction_start", usp_transaction_start), UBUS_METHOD_NOARG("transaction_commit", usp_transaction_commit),