return true;
}
-static int __ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj)
+int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj)
{
struct ubus_request req;
int ret;
return 0;
}
-int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj)
-{
- if (!obj->name || !obj->type)
- return UBUS_STATUS_INVALID_ARGUMENT;
-
- return __ubus_add_object(ctx, obj);
-}
-
static void ubus_remove_object_cb(struct ubus_request *req, int type, struct blob_attr *msg)
{
struct ubus_object *obj = req->priv;
if (!!obj->name ^ !!obj->type)
return UBUS_STATUS_INVALID_ARGUMENT;
- ret = __ubus_add_object(ctx, obj);
+ ret = ubus_add_object(ctx, obj);
if (ret)
return ret;
}
NULL, NULL, 0);
}
+enum {
+ WATCH_ID,
+ WATCH_NOTIFY,
+ __WATCH_MAX
+};
+
+static const struct blobmsg_policy watch_policy[] = {
+ [WATCH_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
+ [WATCH_NOTIFY] = { .name = "notify", .type = BLOBMSG_TYPE_STRING },
+};
+
+
+static int ubus_watch_cb(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req,
+ const char *method, struct blob_attr *msg)
+{
+ struct ubus_watch_object *w;
+ struct blob_attr *tb[__WATCH_MAX];
+
+ blobmsg_parse(watch_policy, ARRAY_SIZE(watch_policy), tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[WATCH_ID] || !tb[WATCH_NOTIFY])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ if (req->peer)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ w = container_of(obj, struct ubus_watch_object, obj);
+ w->cb(ctx, w, blobmsg_get_u32(tb[WATCH_ID]));
+ return 0;
+}
+
+static const struct ubus_method watch_method = {
+ .name = NULL,
+ .handler = ubus_watch_cb,
+};
+
+int ubus_register_watch_object(struct ubus_context *ctx, struct ubus_watch_object *w_obj)
+{
+ struct ubus_object *obj = &w_obj->obj;
+
+ obj->methods = &watch_method;
+ obj->n_methods = 1;
+
+ return ubus_add_object(ctx, obj);
+}
+
+static int
+__ubus_watch_request(struct ubus_context *ctx, struct ubus_object *obj, uint32_t id, const char *method, int type)
+{
+ struct ubus_request req;
+
+ blob_buf_init(&b, 0);
+ blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id);
+ blob_put_int32(&b, UBUS_ATTR_TARGET, id);
+ if (method)
+ blob_put_string(&b, UBUS_ATTR_METHOD, method);
+
+ if (ubus_start_request(ctx, &req, b.head, type, 0) < 0)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ return ubus_complete_request(ctx, &req, 0);
+
+}
+
+int ubus_watch_object_add(struct ubus_context *ctx, struct ubus_watch_object *obj, uint32_t id)
+{
+ return __ubus_watch_request(ctx, &obj->obj, id, "event", UBUS_MSG_ADD_WATCH);
+}
+
+int ubus_watch_object_remove(struct ubus_context *ctx, struct ubus_watch_object *obj, uint32_t id)
+{
+ return __ubus_watch_request(ctx, &obj->obj, id, NULL, UBUS_MSG_REMOVE_WATCH);
+}
+
int ubus_send_event(struct ubus_context *ctx, const char *id,
struct blob_attr *data)
{
struct ubus_request_data;
struct ubus_object_data;
struct ubus_event_handler;
+struct ubus_watch_object;
typedef void (*ubus_lookup_handler_t)(struct ubus_context *ctx,
struct ubus_object_data *obj,
const char *method, struct blob_attr *msg);
typedef void (*ubus_event_handler_t)(struct ubus_context *ctx, struct ubus_event_handler *ev,
const char *type, struct blob_attr *msg);
+typedef void (*ubus_watch_handler_t)(struct ubus_context *ctx, struct ubus_watch_object *w,
+ uint32_t id);
typedef void (*ubus_data_handler_t)(struct ubus_request *req,
int type, struct blob_attr *msg);
typedef void (*ubus_complete_handler_t)(struct ubus_request *req, int ret);
int n_methods;
};
+struct ubus_watch_object {
+ struct ubus_object obj;
+
+ ubus_watch_handler_t cb;
+};
+
struct ubus_event_handler {
struct ubus_object obj;
/* remove the object from the ubus connection */
int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj);
+/* add an object for watching other object state changes */
+int ubus_register_watch_object(struct ubus_context *ctx, struct ubus_watch_object *obj);
+
+int ubus_watch_object_add(struct ubus_context *ctx, struct ubus_watch_object *obj, uint32_t id);
+
+int ubus_watch_object_remove(struct ubus_context *ctx, struct ubus_watch_object *obj, uint32_t id);
+
/* ----------- rpc ----------- */
/* invoke a method on a specific object */
obj->type = type;
INIT_LIST_HEAD(&obj->list);
INIT_LIST_HEAD(&obj->events);
+ INIT_LIST_HEAD(&obj->watchers);
+ INIT_LIST_HEAD(&obj->watched);
if (type)
type->refcount++;
return NULL;
}
+void ubus_watch_new(struct ubus_object *obj, struct ubus_object *target, const char *method)
+{
+ struct ubus_watch *w;
+
+ w = calloc(1, sizeof(*w) + strlen(method) + 1);
+ if (!w)
+ return;
+
+ w->watcher = obj;
+ w->watched = target;
+ list_add(&w->watcher_list, &target->watchers);
+ list_add(&w->watched_list, &obj->watched);
+ strcpy(w->method, method);
+}
+
+void ubus_watch_free(struct ubus_watch *w)
+{
+ list_del(&w->watcher_list);
+ list_del(&w->watched_list);
+ free(w);
+}
+
void ubusd_free_object(struct ubus_object *obj)
{
+ struct ubus_watch *w, *tmp;
+
+ list_for_each_entry_safe(w, tmp, &obj->watched, watched_list) {
+ ubus_watch_free(w);
+ }
+ list_for_each_entry_safe(w, tmp, &obj->watchers, watcher_list) {
+ ubus_proto_notify_watch(w);
+ }
+
ubusd_event_cleanup_object(obj);
if (obj->path.key) {
ubusd_send_obj_event(obj, false);
struct blob_attr data[];
};
+struct ubus_watch {
+ struct list_head watcher_list, watched_list;
+ struct ubus_object *watcher, *watched;
+ char method[];
+};
+
struct ubus_object {
struct ubus_id id;
struct list_head list;
struct list_head events;
+ struct list_head watchers, watched;
+
struct ubus_object_type *type;
struct avl_node path;
int (*recv_msg)(struct ubus_client *client, const char *method, struct blob_attr *msg);
int event_seen;
+ unsigned int invoke_seq;
};
struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr);
return obj;
}
+void ubus_watch_new(struct ubus_object *obj, struct ubus_object *target, const char *method);
+void ubus_watch_free(struct ubus_watch *w);
+void ubus_proto_notify_watch(struct ubus_watch *w);
+
#endif
return -1;
}
+static int ubusd_handle_add_watch(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr)
+{
+ struct ubus_object *obj, *target;
+
+ if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_TARGET] ||
+ !attr[UBUS_ATTR_METHOD]) {
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+
+ obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID]));
+ if (!obj)
+ return UBUS_STATUS_NOT_FOUND;
+
+ if (cl != obj->client)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ target = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_TARGET]));
+ if (!target)
+ return UBUS_STATUS_NOT_FOUND;
+
+ if (cl == target->client)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ ubus_watch_new(obj, target, blob_data(attr[UBUS_ATTR_METHOD]));
+ return 0;
+}
+
+static int ubusd_handle_remove_watch(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr)
+{
+ struct ubus_object *obj;
+ struct ubus_watch *w;
+ uint32_t id;
+
+ if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_TARGET])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID]));
+ if (!obj)
+ return UBUS_STATUS_NOT_FOUND;
+
+ if (cl != obj->client)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+
+ id = blob_get_u32(attr[UBUS_ATTR_TARGET]);
+ list_for_each_entry(w, &obj->watched, watched_list) {
+ if (w->watched->id.id != id)
+ continue;
+
+ ubus_watch_free(w);
+ return 0;
+ }
+
+ return UBUS_STATUS_NOT_FOUND;
+}
+
static const ubus_cmd_cb handlers[__UBUS_MSG_LAST] = {
[UBUS_MSG_PING] = ubusd_send_pong,
[UBUS_MSG_ADD_OBJECT] = ubusd_handle_add_object,
[UBUS_MSG_INVOKE] = ubusd_handle_invoke,
[UBUS_MSG_STATUS] = ubusd_handle_response,
[UBUS_MSG_DATA] = ubusd_handle_response,
+ [UBUS_MSG_ADD_WATCH] = ubusd_handle_add_watch,
+ [UBUS_MSG_REMOVE_WATCH] = ubusd_handle_remove_watch,
};
void ubusd_proto_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub)
ubus_free_id(&clients, &cl->id);
}
+void ubus_proto_notify_watch(struct ubus_watch *w)
+{
+ struct ubus_msg_buf *ub;
+ void *data;
+
+ blob_buf_init(&b, 0);
+ blob_put_int32(&b, UBUS_ATTR_OBJID, w->watcher->id.id);
+ blob_put_string(&b, UBUS_ATTR_METHOD, w->method);
+
+ data = blob_nest_start(&b, UBUS_ATTR_DATA);
+ blobmsg_add_string(&b, "notify", "remove");
+ blobmsg_add_u32(&b, "id", w->watched->id.id);
+ blobmsg_add_u32(&b, "peer", w->watched->client->id.id);
+ blob_nest_end(&b, data);
+
+ ub = ubus_msg_from_blob(false);
+ ubus_msg_init(ub, UBUS_MSG_INVOKE, ++w->watcher->invoke_seq, 0);
+ ubus_msg_send(w->watcher->client, ub, true);
+
+ ubus_watch_free(w);
+}
+
static void __init ubusd_proto_init(void)
{
ubus_init_id_tree(&clients);
UBUS_MSG_ADD_OBJECT,
UBUS_MSG_REMOVE_OBJECT,
+ /* watch an object, notify on remove */
+ UBUS_MSG_ADD_WATCH,
+ UBUS_MSG_REMOVE_WATCH,
+
/* must be last */
__UBUS_MSG_LAST,
};
UBUS_ATTR_SIGNATURE,
UBUS_ATTR_DATA,
+ UBUS_ATTR_TARGET,
/* must be last */
UBUS_ATTR_MAX,