diff --git a/README.md b/README.md index ef4b00a29d70503728f7d007f8795210691a19a9..63c879afaed1f971f8f8b535eef961d3f5dceadb 100644 --- a/README.md +++ b/README.md @@ -81,27 +81,29 @@ root@iopsys:~# ubus -v list usp "del_object":{"path":"String","proto":"String","key":"String","instance_mode":"Integer"} root@iopsys:~# root@iopsys:~# ubus -v list usp.raw -'usp.raw' @fcd59dcd - "dump_schema":{} - "list_operate":{} - "get":{"path":"String","proto":"String","maxdepth":"Integer","next-level":"Boolean","instance_mode":"Integer"} - "getm_values":{"paths":"Array","proto":"String","next-level":"Boolean","instance_mode":"Integer"} - "getm_names":{"paths":"Array","proto":"String","next-level":"Boolean","instance_mode":"Integer"} - "getm_attributes":{"paths":"Array","proto":"String","next-level":"Boolean","instance_mode":"Integer"} - "object_names":{"path":"String","proto":"String","maxdepth":"Integer","next-level":"Boolean","instance_mode":"Integer"} - "instances":{"path":"String","proto":"String","maxdepth":"Integer","next-level":"Boolean","instance_mode":"Integer"} - "validate":{"path":"String","proto":"String","maxdepth":"Integer","next-level":"Boolean","instance_mode":"Integer"} - "transaction_start":{"app":"String"} - "set":{"path":"String","value":"String","values":"Table","proto":"String","key":"String","instance_mode":"Integer","transaction_id":"Integer"} - "operate":{"path":"String","action":"String","input":"Table","proto":"String","instance_mode":"Integer"} - "add_object":{"path":"String","proto":"String","key":"String","instance_mode":"Integer","transaction_id":"Integer"} - "del_object":{"path":"String","proto":"String","key":"String","instance_mode":"Integer","transaction_id":"Integer"} - "setm_values":{"pv_tuple":"Array","proto":"String","instance_mode":"Integer","key":"String","transaction_id":"Integer"} - "setm_attributes":{"paths":"Array","proto":"String","instance_mode":"Integer","transaction_id":"Integer"} - "transaction_commit":{"transaction_id":"Integer"} - "transaction_abort":{"transaction_id":"Integer"} - "transaction_status":{"transaction_id":"Integer"} - "list_notify":{"instance_mode":"Integer"} +'usp.raw' @08a13407 + "dump_schema":{} + "list_operate":{} + "list_events":{} + "get":{"path":"String","proto":"String","maxdepth":"Integer","next-level":"Boolean","instance_mode":"Integer"} + "getm_values":{"paths":"Array","proto":"String","next-level":"Boolean","instance_mode":"Integer"} + "getm_names":{"paths":"Array","proto":"String","next-level":"Boolean","instance_mode":"Integer"} + "getm_attributes":{"paths":"Array","proto":"String","next-level":"Boolean","instance_mode":"Integer"} + "object_names":{"path":"String","proto":"String","maxdepth":"Integer","next-level":"Boolean","instance_mode":"Integer"} + "instances":{"path":"String","proto":"String","maxdepth":"Integer","next-level":"Boolean","instance_mode":"Integer"} + "validate":{"path":"String","proto":"String","maxdepth":"Integer","next-level":"Boolean","instance_mode":"Integer"} + "transaction_start":{"app":"String"} + "set":{"path":"String","value":"String","values":"Table","proto":"String","key":"String","instance_mode":"Integer","transaction_id":"Integer"} + "operate":{"path":"String","action":"String","input":"Table","proto":"String","instance_mode":"Integer"} + "add_object":{"path":"String","proto":"String","key":"String","instance_mode":"Integer","transaction_id":"Integer"} + "del_object":{"path":"String","proto":"String","key":"String","instance_mode":"Integer","transaction_id":"Integer"} + "setm_values":{"pv_tuple":"Array","proto":"String","instance_mode":"Integer","key":"String","transaction_id":"Integer"} + "setm_attributes":{"paths":"Array","proto":"String","instance_mode":"Integer","transaction_id":"Integer"} + "transaction_commit":{"transaction_id":"Integer"} + "transaction_abort":{"transaction_id":"Integer"} + "transaction_status":{"transaction_id":"Integer"} + "list_notify":{"instance_mode":"Integer"} + "notify_event":{"name":"String","input":"Table"} root@iopsys:~# ``` @@ -233,6 +235,8 @@ root@iopsys:~# 17. Transaction commit 18. Transaction abort 19. List notification objects + 20. List supported usp events + 21. Send notification for an event It also provide a granularity layer which can be configured using uci parameter and provide additional ubus objects. @@ -953,6 +957,32 @@ root@iopsys:~# ubus call usp.raw list_notify } ``` +### 20. List supported events +API to list down the data-model events for usp notification. + +```bash +root@iopsys:~# ubus call usp.raw list_events +{ + "parameters": [ + { + "parameter": "Device.LocalAgent.TransferComplete!", + "in": [ + "Command", + "CommandKey", + "Requestor", + "TransferType", + "Affected", + "TransferURL", + "StartTime", + "CompleteTime", + "FaultCode", + "FaultString" + ] + } + ] +} +``` + ## Path syntax and possible error cases Please note some error scenerios with the uspd. diff --git a/schemas/ubus/usp.raw.json b/schemas/ubus/usp.raw.json index da1b99f7d8e6677e3e8edb9fee027b6dc1cbc4f4..3d45a9ecbfbc1c6deccc3ee96720c6463f7054bb 100644 --- a/schemas/ubus/usp.raw.json +++ b/schemas/ubus/usp.raw.json @@ -243,6 +243,46 @@ } } }, + "list_events": { + "title": "List down supported usp events", + "description": "events will be shown in schema format", + "type": "object", + "properties": { + "input": { + "type": "object", + "properties": {} + }, + "output": { + "type": "object", + "required": [ + "parameters" + ], + "properties": { + "parameters": { + "type": "array", + "items": [{ + "type": "object", + "required": [ + "parameter", + "type" + ], + "properties": { + "parameter": { + "$ref": "#/definitions/operate_path_t" + }, + "in": { + "type": "array", + "items": [{ + "type": "string" + }] + } + } + }] + } + } + } + } + }, "get": { "title": "Get handler", "description": "Query the datamodel object", diff --git a/src/Makefile b/src/Makefile index a3c2b186fd79668a6b8528bd759f56db6cb1b30d..e09af2c2938fbb7a6b68af41574e51c5b85951c4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ PROG = uspd -OBJS =common.o get.o get_helper.o set.o operate.o add_delete.o pretty_print.o usp.o +OBJS =common.o get.o get_helper.o set.o operate.o add_delete.o pretty_print.o usp.o events.o CP=cp -f PROG_CFLAGS = $(CFLAGS) \ diff --git a/src/events.c b/src/events.c new file mode 100644 index 0000000000000000000000000000000000000000..bd640b9caafacba578deb21fdab50c57b40e529b --- /dev/null +++ b/src/events.c @@ -0,0 +1,88 @@ +/* + * events.c: Handler to generate usp events on ubus + * + * Copyright (C) 2021 iopsys Software Solutions AB. All rights reserved. + * + * Author: Vivek Dutta <vivek.dutta@iopsys.eu> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "common.h" +#include "events.h" +#include <libubus.h> + +struct event { + const char *path; + const char **params; +}; + +static const struct event supp_events[] = { + { + .path = "Device.LocalAgent.TransferComplete!", + .params = (const char *[]) { + "Command", + "CommandKey", + "Requestor", + "TransferType", + "Affected", + "TransferURL", + "StartTime", + "CompleteTime", + "FaultCode", + "FaultString", + NULL + } + } +}; + +void list_event_schema(struct blob_buf *bb) +{ + void *array, *table, *array_arg; + size_t i, j; + const struct event *ev; + + array = blobmsg_open_array(bb, "parameters"); + for (i = 0; i < ARRAY_SIZE(supp_events); i++) { + ev = &supp_events[i]; + table = blobmsg_open_table(bb, NULL); + blobmsg_add_string(bb, "parameter", ev->path); + // filling params + if (ev->params) { + array_arg = blobmsg_open_array(bb, "in"); + for (j = 0; ev->params[j] != NULL; j++) + blobmsg_add_string(bb, NULL, ev->params[j]); + + blobmsg_close_array(bb, array_arg); + } + blobmsg_close_table(bb, table); + } + + blobmsg_close_array(bb, array); +} + +bool is_registered_event(char *name) +{ + const struct event *ev; + size_t i; + + for (i = 0; i < ARRAY_SIZE(supp_events); i++) { + ev = &supp_events[i]; + if (strcmp(name, ev->path) == 0) + return true; + } + + return false; +} diff --git a/src/events.h b/src/events.h new file mode 100644 index 0000000000000000000000000000000000000000..31c28fc0f067f914ac4d38ad2ca791dc8d01475f --- /dev/null +++ b/src/events.h @@ -0,0 +1,9 @@ +#ifndef EVENT_H +#define EVENT_H + +#include "usp.h" +#include "common.h" + +void list_event_schema(struct blob_buf *bb); +bool is_registered_event(char *name); +#endif /* EVENT_H */ diff --git a/src/usp.c b/src/usp.c index 85a3ee3d7298008c42121efff7592f2a423ad180..98e55ff8105f2e6ef0c7a6ea2e4db1c939252ed9 100644 --- a/src/usp.c +++ b/src/usp.c @@ -1,7 +1,7 @@ /* * usp.c: USP deamon * - * Copyright (C) 2019 iopsys Software Solutions AB. All rights reserved. + * Copyright (C) 2021 iopsys Software Solutions AB. All rights reserved. * * Author: Vivek Dutta <vivek.dutta@iopsys.eu> * @@ -42,6 +42,7 @@ #include "add_delete.h" #include "strncpyt.h" #include "ipc.h" +#include "events.h" static int uspd_start_deferred(usp_data_t *data, @@ -867,6 +868,50 @@ int usp_list_operate(struct ubus_context *actx, struct ubus_object *obj, return 0; } +static const struct blobmsg_policy dm_notify_event_policy[] = { + [DM_NOTIFY_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, + [DM_NOTIFY_PRAMS] = { .name = "input", .type = BLOBMSG_TYPE_TABLE }, +}; + +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[__DM_NOTIFY_MAX] = {NULL}; + char *event_name; + + if (blobmsg_parse(dm_notify_event_policy, __DM_NOTIFY_MAX, tb, blob_data(msg), blob_len(msg))) { + ERR("Failed to parse blob"); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + if (!tb[DM_NOTIFY_NAME]) + return UBUS_STATUS_INVALID_ARGUMENT; + + INFO("ubus method|%s|, name|%s|", method, obj->name); + event_name = blobmsg_get_string(tb[DM_NOTIFY_NAME]); + if (is_registered_event(event_name)) { + ubus_send_event(ctx, "usp.event", msg); + } else { + WARNING("Event %s not registered", event_name); + } + + return 0; +} + +int usp_list_events(struct ubus_context *actx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg __attribute__((unused))) +{ + struct usp_context *u; + + INFO("ubus method|%s|, name|%s|", method, obj->name); + u = container_of(actx, struct usp_context, ubus_ctx); + ubus_send_reply(actx, req, u->event_schema_bb.head); + + return 0; +} + static const struct blobmsg_policy dm_list_notify_policy[] = { [DM_LIST_NOTIFY_INSTANCE] = { .name = "instance_mode", .type = BLOBMSG_TYPE_INT32 }, }; @@ -1050,6 +1095,7 @@ int usp_transaction_handler(struct ubus_context *ctx, struct ubus_object *obj, static struct ubus_method usp_raw_methods[] = { UBUS_METHOD_NOARG("dump_schema", usp_list_schema), UBUS_METHOD_NOARG("list_operate", usp_list_operate), + UBUS_METHOD_NOARG("list_events", usp_list_events), UBUS_METHOD("get", usp_get_handler, dm_get_policy), UBUS_METHOD("getm_values", usp_getm_values, dm_getm_policy), UBUS_METHOD("getm_names", usp_getm_names, dm_getm_policy), @@ -1068,6 +1114,7 @@ static struct ubus_method usp_raw_methods[] = { UBUS_METHOD("transaction_abort", usp_transaction_handler, trans_policy), UBUS_METHOD("transaction_status", usp_transaction_handler, trans_policy), UBUS_METHOD("list_notify", usp_list_notify, dm_list_notify_policy), + UBUS_METHOD("notify_event", usp_notify_event, dm_notify_event_policy), }; static void test_client_subscribe_cb(USP_ATTR_UNUSED struct ubus_context *ctx, @@ -1202,9 +1249,11 @@ bool usp_pre_init(struct usp_context *u) // Initialise blobs blob_buf_init(&u->schema_bb, 0); blob_buf_init(&u->operate_schema_bb, 0); + blob_buf_init(&u->event_schema_bb, 0); // Get schema list_operate_schema(&u->operate_schema_bb); + list_event_schema(&u->event_schema_bb); ret = bbf_dm_get_schema(&u->schema_bb); if (ret != 0) return false; @@ -1283,6 +1332,7 @@ bool usp_cleanup(struct usp_context *u) { blob_buf_free(&u->schema_bb); blob_buf_free(&u->operate_schema_bb); + blob_buf_free(&u->event_schema_bb); free_ubus_obj_list(&u->obj_list); free_dynamic_arrays(); diff --git a/src/usp.h b/src/usp.h index 69c02f799759d04d20accbb7c2dc42f54d447870..3c4b9a932a379b757e52b3ef127ebe249b3b3f6c 100644 --- a/src/usp.h +++ b/src/usp.h @@ -22,6 +22,7 @@ struct usp_context { struct ubus_context ubus_ctx; struct blob_buf schema_bb; struct blob_buf operate_schema_bb; + struct blob_buf event_schema_bb; struct uloop_timeout schema_timer; struct ubus_object *notify_object; struct list_head obj_list; @@ -50,6 +51,12 @@ enum { __DM_LIST_NOTIFY_MAX, }; +enum { + DM_NOTIFY_NAME, + DM_NOTIFY_PRAMS, + __DM_NOTIFY_MAX, +}; + struct obNode { struct ubus_object *obj; struct list_head list; diff --git a/test/files/etc/config/cwmp b/test/files/etc/config/cwmp index 567717d596c15b55aa3d4b9f4337950055ff3799..d44752ccc1f0e2d8dd67b8abb35adb6383155cda 100644 --- a/test/files/etc/config/cwmp +++ b/test/files/etc/config/cwmp @@ -29,7 +29,6 @@ config cpe 'cpe' option userid '' #$OUI-$SER option passwd 'iopsys' option port '7547' - option ubus_socket '/var/run/ubus.sock' option provisioning_code '' option amd_version '5' # compression possible configs: InstanceNumber, InstanceAlias diff --git a/test/python/validate_ubus_event.py b/test/python/validate_ubus_event.py new file mode 100755 index 0000000000000000000000000000000000000000..84601605aec52a220ee43dc11355bc24d211624c --- /dev/null +++ b/test/python/validate_ubus_event.py @@ -0,0 +1,25 @@ +#!/usr/bin/python3 + +import ubus +import json + +TEST_NAME = "Validate ubus event usp.raw" + +print("Running: " + TEST_NAME) + +def callback(event, data): + print("PASS: " + TEST_NAME) + ubus.disconnect() + exit(0) + +assert ubus.connect() + +ubus.listen(("usp.event", callback)) + +ubus.call("usp.raw", "notify_event", {"name":"Device.LocalAgent.TransferComplete!", "input":{"param1":"val1", "param2":"val2"}}) + +ubus.loop() + +ubus.disconnect() + +print("FAIL: " + TEST_NAME)