X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fubus.git;a=blobdiff_plain;f=ubusd_event.c;h=dfffdeeadd051394da7da1e99d21b0adece98a40;hp=da0aed80273ec89e505c102de5c098a418cd18da;hb=f9f602533a454891dc5a6f1df1bd0e624295ab1f;hpb=fa989780bda3ad10fcbb637390d59afe2b32ba81 diff --git a/ubusd_event.c b/ubusd_event.c index da0aed8..dfffdee 100644 --- a/ubusd_event.c +++ b/ubusd_event.c @@ -1,41 +1,204 @@ #include "ubusd.h" static struct avl_tree patterns; +static LIST_HEAD(catch_all); static struct ubus_object *event_obj; +static int event_seq = 0; -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 { + EVREG_PATTERN, + EVREG_OBJECT, + EVREG_LAST, +}; + +static struct blobmsg_policy evr_policy[] = { + [EVREG_PATTERN] = { .name = "pattern", .type = BLOBMSG_TYPE_STRING }, + [EVREG_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) +{ + struct event_source *ev; + struct ubus_object *obj; + struct blob_attr *attr[EVREG_LAST]; + const char *pattern; + uint32_t id; + + blobmsg_parse(evr_policy, EVREG_LAST, attr, blob_data(msg), blob_len(msg)); + if (!attr[EVREG_OBJECT]) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = blobmsg_get_u32(attr[EVREG_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[EVREG_PATTERN]) + return ubusd_alloc_catchall(obj); + + pattern = blobmsg_data(attr[EVREG_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; +} + +enum { + EVMSG_ID, + EVMSG_DATA, + EVMSG_LAST, +}; + +static struct blobmsg_policy ev_policy[] = { + [EVMSG_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING }, + [EVMSG_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, +}; + +static void ubusd_send_event_msg(struct ubus_msg_buf **ub, struct ubus_object *obj, + const char *id, struct blob_attr *msg) { - fprintf(stderr, "event: call to method '%s'\n", method); + uint32_t *objid_ptr; + + if (*ub) { + objid_ptr = blob_data(blob_data((*ub)->data)); + *objid_ptr = htonl(obj->id.id); + } else { + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + blob_put_string(&b, UBUS_ATTR_METHOD, id); + blob_put(&b, UBUS_ATTR_DATA, blobmsg_data(msg), blobmsg_data_len(msg)); + + *ub = ubus_msg_new(b.head, blob_raw_len(b.head), true); + + (*ub)->hdr.type = UBUS_MSG_INVOKE; + (*ub)->hdr.peer = 0; + } + (*ub)->hdr.seq = ++event_seq; + ubus_msg_send(obj->client, *ub, false); +} + +static int ubusd_send_event(struct ubus_client *cl, struct blob_attr *msg) +{ + struct ubus_msg_buf *ub = NULL; + struct event_source *ev; + struct blob_attr *attr[EVMSG_LAST]; + const char *id; + + blobmsg_parse(ev_policy, EVMSG_LAST, attr, blob_data(msg), blob_len(msg)); + if (!attr[EVMSG_ID] || !attr[EVMSG_DATA]) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = blobmsg_data(attr[EVMSG_ID]); + list_for_each_entry(ev, &catch_all, catchall.list) { + /* do not loop back events */ + if (ev->obj->client == cl) + continue; + + ubusd_send_event_msg(&ub, ev->obj, id, attr[EVMSG_DATA]); + } + + if (ub) + ubus_msg_free(ub); + 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); + + if (!strcmp(method, "send")) + return ubusd_send_event(cl, msg); + + return UBUS_STATUS_INVALID_COMMAND; +} + void ubusd_event_init(void) { ubus_init_string_tree(&patterns, true);