From: Felix Fietkau Date: Sat, 5 Feb 2011 18:53:14 +0000 (+0100) Subject: add functionality for registering anonymous objects as event listeners X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=f6a6b0d492900b7a087a8a89d11fa5b94f4c5cb5;p=project%2Fubus.git add functionality for registering anonymous objects as event listeners --- diff --git a/cli.c b/cli.c index 59e62ed..ea1ec4d 100644 --- a/cli.c +++ b/cli.c @@ -39,6 +39,32 @@ static int usage(char *prog) return 1; } +static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv) +{ + static struct ubus_object listener; + const char *event; + int ret = 0; + + if (!argc) { + event = "*"; + ret = ubus_register_event_handler(ctx, &listener, NULL); + } + + for (;argc;argv++, argc--) { + event = argv[0]; + ret = ubus_register_event_handler(ctx, &listener, argv[0]); + if (ret) + break; + } + + if (ret) { + fprintf(stderr, "Error while registering for event '%s': %s\n", + event, ubus_strerror(ret)); + } + + return 0; +} + int main(int argc, char **argv) { static struct ubus_context *ctx; @@ -72,7 +98,7 @@ int main(int argc, char **argv) if (!ret) ret = ubus_invoke(ctx, id, argv[3], NULL, receive_data, NULL); } else if (!strcmp(cmd, "listen")) { - ret = ubus_invoke(ctx, UBUS_SYSTEM_OBJECT_EVENT, "listen", NULL, receive_data, NULL); + ret = ubus_cli_listen(ctx, argc - 2, argv + 2); } else { return usage(argv[0]); } diff --git a/libubus.c b/libubus.c index 12bdb37..223a6bb 100644 --- a/libubus.c +++ b/libubus.c @@ -27,6 +27,7 @@ const char *__ubus_strerror[__UBUS_STATUS_LAST] = { [UBUS_STATUS_METHOD_NOT_FOUND] = "Method not found", [UBUS_STATUS_NOT_FOUND] = "Not found", [UBUS_STATUS_NO_DATA] = "No response", + [UBUS_STATUS_PERMISSION_DENIED] = "Permission denied", }; static struct blob_buf b; @@ -605,21 +606,21 @@ static bool ubus_push_object_type(struct ubus_object_type *type) return true; } -int ubus_publish(struct ubus_context *ctx, struct ubus_object *obj) +static int __ubus_publish(struct ubus_context *ctx, struct ubus_object *obj) { struct ubus_request req; int ret; - if (obj->id || !obj->name || !obj->type) - return UBUS_STATUS_INVALID_ARGUMENT; - blob_buf_init(&b, 0); - blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->name); - if (obj->type->id) - blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id); - else if (!ubus_push_object_type(obj->type)) - return UBUS_STATUS_INVALID_ARGUMENT; + if (obj->name && obj->type) { + blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->name); + + if (obj->type->id) + blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id); + else if (!ubus_push_object_type(obj->type)) + return UBUS_STATUS_INVALID_ARGUMENT; + } ubus_start_request(ctx, &req, b.head, UBUS_MSG_PUBLISH, 0); req.raw_data_cb = ubus_publish_cb; @@ -634,6 +635,43 @@ int ubus_publish(struct ubus_context *ctx, struct ubus_object *obj) return 0; } +int ubus_publish(struct ubus_context *ctx, struct ubus_object *obj) +{ + if (!obj->name || !obj->type) + return UBUS_STATUS_INVALID_ARGUMENT; + + return __ubus_publish(ctx, obj); +} + +int ubus_register_event_handler(struct ubus_context *ctx, struct ubus_object *obj, + const char *pattern) +{ + struct blob_buf b2; + int ret; + + if (!obj->id) { + if (!!obj->name ^ !!obj->type) + return UBUS_STATUS_INVALID_ARGUMENT; + + ret = __ubus_publish(ctx, obj); + if (ret) + return ret; + } + + /* use a second buffer, ubus_invoke() overwrites the primary one */ + memset(&b2, 0, sizeof(b2)); + blob_buf_init(&b2, 0); + blobmsg_add_u32(&b2, "object", obj->id); + if (pattern) + blobmsg_add_string(&b2, "pattern", pattern); + + ret = ubus_invoke(ctx, UBUS_SYSTEM_OBJECT_EVENT, "register", b2.head, + NULL, NULL); + + return 0; +} + + void ubus_default_connection_lost(struct ubus_context *ctx) { if (ctx->sock.registered) diff --git a/libubus.h b/libubus.h index c93ad8f..48ee056 100644 --- a/libubus.h +++ b/libubus.h @@ -171,3 +171,7 @@ int ubus_publish(struct ubus_context *ctx, struct ubus_object *obj); /* send a reply to an incoming object method call */ int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, struct blob_attr *msg); + +/* ----------- events ----------- */ +int ubus_register_event_handler(struct ubus_context *ctx, struct ubus_object *obj, + const char *pattern); diff --git a/ubusd_event.c b/ubusd_event.c index da0aed8..3396ebf 100644 --- a/ubusd_event.c +++ b/ubusd_event.c @@ -1,41 +1,140 @@ #include "ubusd.h" static struct avl_tree patterns; +static LIST_HEAD(catch_all); static struct ubus_object *event_obj; -struct event_pattern { - struct avl_node avl; +enum evs_type { + EVS_PATTERN, + EVS_CATCHALL +}; +struct event_source { + struct list_head list; struct ubus_object *obj; + enum evs_type type; + union { + struct { + struct avl_node avl; + } pattern; + struct { + struct list_head list; + } catchall; + }; +}; + +struct event_pattern { + struct event_source evs; struct list_head list; +}; + +struct event_catchall { + struct event_source evs; - const char *path; + struct list_head list; + struct ubus_object *obj; }; -static void ubusd_delete_event_pattern(struct event_pattern *ev) +static void ubusd_delete_event_source(struct event_source *evs) { - list_del(&ev->list); - avl_delete(&patterns, &ev->avl); - free(ev); + list_del(&evs->list); + switch (evs->type) { + case EVS_PATTERN: + avl_delete(&patterns, &evs->pattern.avl); + break; + case EVS_CATCHALL: + list_del(&evs->catchall.list); + break; + } + free(evs); } void ubusd_event_cleanup_object(struct ubus_object *obj) { - struct event_pattern *ev; + struct event_source *ev; - while (!list_empty(&obj->event_patterns)) { - ev = list_first_entry(&obj->event_patterns, - struct event_pattern, list); - ubusd_delete_event_pattern(ev); + while (!list_empty(&obj->events)) { + ev = list_first_entry(&obj->events, struct event_source, list); + ubusd_delete_event_source(ev); } } -static int ubusd_event_recv(struct ubus_client *cl, const char *method, struct blob_attr *msg) +enum { + EVMSG_PATTERN, + EVMSG_OBJECT, + EVMSG_LAST, +}; + +static struct blobmsg_policy ev_policy[] = { + [EVMSG_PATTERN] = { .name = "pattern", .type = BLOBMSG_TYPE_STRING }, + [EVMSG_OBJECT] = { .name = "object", .type = BLOBMSG_TYPE_INT32 }, +}; + + +static struct event_source *ubusd_alloc_event_source(struct ubus_object *obj, enum evs_type type, int datalen) +{ + struct event_source *evs; + + evs = calloc(1, sizeof(*evs) + datalen); + list_add(&evs->list, &obj->events); + evs->obj = obj; + evs->type = type; + return evs; +} + +static int ubusd_alloc_catchall(struct ubus_object *obj) +{ + struct event_source *evs; + + evs = ubusd_alloc_event_source(obj, EVS_CATCHALL, 0); + list_add(&evs->catchall.list, &catch_all); + + return 0; +} + +static int ubusd_alloc_event_pattern(struct ubus_client *cl, struct blob_attr *msg) { - fprintf(stderr, "event: call to method '%s'\n", method); + struct event_source *ev; + struct ubus_object *obj; + struct blob_attr *attr[EVMSG_LAST]; + const char *pattern; + uint32_t id; + + blobmsg_parse(ev_policy, EVMSG_LAST, attr, blob_data(msg), blob_len(msg)); + if (!attr[EVMSG_OBJECT]) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = blobmsg_get_u32(attr[EVMSG_OBJECT]); + if (id < UBUS_SYSTEM_OBJECT_MAX) + return UBUS_STATUS_PERMISSION_DENIED; + + obj = ubusd_find_object(id); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + if (obj->client != cl) + return UBUS_STATUS_PERMISSION_DENIED; + + if (!attr[EVMSG_PATTERN]) + return ubusd_alloc_catchall(obj); + + pattern = blobmsg_data(attr[EVMSG_PATTERN]); + ev = ubusd_alloc_event_source(obj, EVS_PATTERN, strlen(pattern) + 1); + ev->pattern.avl.key = (void *) (ev + 1); + strcpy(ev->pattern.avl.key, pattern); + avl_insert(&patterns, &ev->pattern.avl); + return 0; } +static int ubusd_event_recv(struct ubus_client *cl, const char *method, struct blob_attr *msg) +{ + if (!strcmp(method, "register")) + return ubusd_alloc_event_pattern(cl, msg); + + return UBUS_STATUS_INVALID_COMMAND; +} + void ubusd_event_init(void) { ubus_init_string_tree(&patterns, true); diff --git a/ubusd_obj.c b/ubusd_obj.c index a6be182..15f43ad 100644 --- a/ubusd_obj.c +++ b/ubusd_obj.c @@ -118,11 +118,12 @@ struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr else if (attr[UBUS_ATTR_SIGNATURE]) type = ubus_create_obj_type(attr[UBUS_ATTR_SIGNATURE]); - if (!type) + if (!!type ^ !!attr[UBUS_ATTR_OBJPATH]) return NULL; obj = ubusd_create_object_internal(type, 0); - ubus_unref_object_type(type); + if (type) + ubus_unref_object_type(type); if (!obj) return NULL; @@ -141,6 +142,8 @@ struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr obj->client = cl; list_add(&obj->list, &cl->objects); + INIT_LIST_HEAD(&obj->events); + return obj; free: @@ -150,6 +153,7 @@ free: void ubusd_free_object(struct ubus_object *obj) { + ubusd_event_cleanup_object(obj); if (obj->path.key) { avl_delete(&path, &obj->path); free(obj->path.key); diff --git a/ubusd_obj.h b/ubusd_obj.h index 6e5c2c9..b268792 100644 --- a/ubusd_obj.h +++ b/ubusd_obj.h @@ -26,7 +26,7 @@ struct ubus_object { struct ubus_id id; struct list_head list; - struct list_head event_patterns; + struct list_head events; struct ubus_object_type *type; struct avl_node path; @@ -39,4 +39,17 @@ struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr struct ubus_object *ubusd_create_object_internal(struct ubus_object_type *type, uint32_t id); void ubusd_free_object(struct ubus_object *obj); +static inline struct ubus_object *ubusd_find_object(uint32_t objid) +{ + struct ubus_object *obj; + struct ubus_id *id; + + id = ubus_find_id(&objects, objid); + if (!id) + return NULL; + + obj = container_of(id, struct ubus_object, id); + return obj; +} + #endif diff --git a/ubusd_proto.c b/ubusd_proto.c index 07bb9fa..2e84556 100644 --- a/ubusd_proto.c +++ b/ubusd_proto.c @@ -203,18 +203,16 @@ static int ubusd_handle_invoke(struct ubus_client *cl, struct ubus_msg_buf *ub, static int ubusd_handle_response(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) { struct ubus_object *obj; - struct ubus_id *id; if (!attr[UBUS_ATTR_OBJID] || (ub->hdr.type == UBUS_MSG_STATUS && !attr[UBUS_ATTR_STATUS]) || (ub->hdr.type == UBUS_MSG_DATA && !attr[UBUS_ATTR_DATA])) goto error; - id = ubus_find_id(&objects, blob_get_int32(attr[UBUS_ATTR_OBJID])); - if (!id) + obj = ubusd_find_object(blob_get_int32(attr[UBUS_ATTR_OBJID])); + if (!obj) goto error; - obj = container_of(id, struct ubus_object, id); if (cl != obj->client) goto error; diff --git a/ubusmsg.h b/ubusmsg.h index 9da330e..32cee6b 100644 --- a/ubusmsg.h +++ b/ubusmsg.h @@ -70,6 +70,7 @@ enum ubus_msg_status { UBUS_STATUS_METHOD_NOT_FOUND, UBUS_STATUS_NOT_FOUND, UBUS_STATUS_NO_DATA, + UBUS_STATUS_PERMISSION_DENIED, __UBUS_STATUS_LAST };