summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/client.c9
-rw-r--r--libubus-internal.h1
-rw-r--r--libubus-sub.c18
-rw-r--r--libubus.c4
-rw-r--r--libubus.h5
-rw-r--r--ubusd_obj.c9
-rw-r--r--ubusd_obj.h1
-rw-r--r--ubusd_proto.c14
-rw-r--r--ubusmsg.h9
9 files changed, 69 insertions, 1 deletions
diff --git a/examples/client.c b/examples/client.c
index 315a8e3..418fb15 100644
--- a/examples/client.c
+++ b/examples/client.c
@@ -18,7 +18,14 @@
static struct ubus_context *ctx;
static struct blob_buf b;
-static struct ubus_object test_client_object = {};
+static void test_client_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj)
+{
+ fprintf(stderr, "Subscribers active: %d\n", obj->has_subscribers);
+}
+
+static struct ubus_object test_client_object = {
+ .subscribe_cb = test_client_subscribe_cb,
+};
static void client_main(void)
{
diff --git a/libubus-internal.h b/libubus-internal.h
index f3e2a73..072bf8e 100644
--- a/libubus-internal.h
+++ b/libubus-internal.h
@@ -27,5 +27,6 @@ void ubus_process_invoke(struct ubus_context *ctx, struct ubus_msghdr *hdr);
int __hidden ubus_start_request(struct ubus_context *ctx, struct ubus_request *req,
struct blob_attr *msg, int cmd, uint32_t peer);
void ubus_process_unsubscribe(struct ubus_context *ctx, struct ubus_msghdr *hdr);
+void ubus_process_notify(struct ubus_context *ctx, struct ubus_msghdr *hdr);
#endif
diff --git a/libubus-sub.c b/libubus-sub.c
index 2bfb483..87c8128 100644
--- a/libubus-sub.c
+++ b/libubus-sub.c
@@ -91,4 +91,22 @@ void __hidden ubus_process_unsubscribe(struct ubus_context *ctx, struct ubus_msg
s->remove_cb(ctx, s, blob_get_u32(attrbuf[UBUS_ATTR_TARGET]));
}
+void __hidden ubus_process_notify(struct ubus_context *ctx, struct ubus_msghdr *hdr)
+{
+ struct blob_attr **attrbuf;
+ struct ubus_object *obj;
+ uint32_t objid;
+
+ attrbuf = ubus_parse_msg(hdr->data);
+ if (!attrbuf[UBUS_ATTR_OBJID] || !attrbuf[UBUS_ATTR_ACTIVE])
+ return;
+ objid = blob_get_u32(attrbuf[UBUS_ATTR_OBJID]);
+ obj = avl_find_element(&ctx->objects, &objid, obj, avl);
+ if (!obj)
+ return;
+
+ obj->has_subscribers = blob_get_u8(attrbuf[UBUS_ATTR_ACTIVE]);
+ if (obj->subscribe_cb)
+ obj->subscribe_cb(ctx, obj);
+}
diff --git a/libubus.c b/libubus.c
index 03899d6..dffbfeb 100644
--- a/libubus.c
+++ b/libubus.c
@@ -232,6 +232,10 @@ void __hidden ubus_process_msg(struct ubus_context *ctx, struct ubus_msghdr *hdr
case UBUS_MSG_UNSUBSCRIBE:
ubus_process_unsubscribe(ctx, hdr);
break;
+
+ case UBUS_MSG_NOTIFY:
+ ubus_process_notify(ctx, hdr);
+ break;
}
}
diff --git a/libubus.h b/libubus.h
index 17c4952..19a4217 100644
--- a/libubus.h
+++ b/libubus.h
@@ -37,6 +37,7 @@ typedef void (*ubus_lookup_handler_t)(struct ubus_context *ctx,
typedef int (*ubus_handler_t)(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req,
const char *method, struct blob_attr *msg);
+typedef void (*ubus_state_handler_t)(struct ubus_context *ctx, struct ubus_object *obj);
typedef void (*ubus_remove_handler_t)(struct ubus_context *ctx,
struct ubus_subscriber *obj, uint32_t id);
typedef void (*ubus_event_handler_t)(struct ubus_context *ctx, struct ubus_event_handler *ev,
@@ -86,6 +87,9 @@ struct ubus_object {
const char *path;
struct ubus_object_type *type;
+ ubus_state_handler_t subscribe_cb;
+ bool has_subscribers;
+
const struct ubus_method *methods;
int n_methods;
};
@@ -134,6 +138,7 @@ struct ubus_request_data {
uint32_t peer;
uint32_t seq;
bool deferred;
+ bool notify;
};
struct ubus_request {
diff --git a/ubusd_obj.c b/ubusd_obj.c
index 8b1b18f..69ca8b8 100644
--- a/ubusd_obj.c
+++ b/ubusd_obj.c
@@ -167,6 +167,7 @@ free:
void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target, const char *method)
{
struct ubus_subscription *s;
+ bool first = list_empty(&target->subscribers);
s = calloc(1, sizeof(*s) + strlen(method) + 1);
if (!s)
@@ -177,13 +178,21 @@ void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target, const c
list_add(&s->list, &target->subscribers);
list_add(&s->target_list, &obj->target_list);
strcpy(s->method, method);
+
+ if (first)
+ ubus_notify_subscription(target);
}
void ubus_unsubscribe(struct ubus_subscription *s)
{
+ struct ubus_object *obj = s->target;
+
list_del(&s->list);
list_del(&s->target_list);
free(s);
+
+ if (list_empty(&obj->subscribers))
+ ubus_notify_subscription(obj);
}
void ubusd_free_object(struct ubus_object *obj)
diff --git a/ubusd_obj.h b/ubusd_obj.h
index 5a82e8d..8e80078 100644
--- a/ubusd_obj.h
+++ b/ubusd_obj.h
@@ -79,5 +79,6 @@ static inline struct ubus_object *ubusd_find_object(uint32_t objid)
void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target, const char *method);
void ubus_unsubscribe(struct ubus_subscription *s);
void ubus_notify_unsubscribe(struct ubus_subscription *s);
+void ubus_notify_subscription(struct ubus_object *obj);
#endif
diff --git a/ubusd_proto.c b/ubusd_proto.c
index 59920ed..283cb77 100644
--- a/ubusd_proto.c
+++ b/ubusd_proto.c
@@ -416,6 +416,20 @@ void ubusd_proto_free_client(struct ubus_client *cl)
ubus_free_id(&clients, &cl->id);
}
+void ubus_notify_subscription(struct ubus_object *obj)
+{
+ bool active = !list_empty(&obj->subscribers);
+ struct ubus_msg_buf *ub;
+
+ blob_buf_init(&b, 0);
+ blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id);
+ blob_put_int8(&b, UBUS_ATTR_ACTIVE, active);
+
+ ub = ubus_msg_from_blob(false);
+ ubus_msg_init(ub, UBUS_MSG_NOTIFY, ++obj->invoke_seq, 0);
+ ubus_msg_send(obj->client, ub, true);
+}
+
void ubus_notify_unsubscribe(struct ubus_subscription *s)
{
struct ubus_msg_buf *ub;
diff --git a/ubusmsg.h b/ubusmsg.h
index 833d7bf..fc4eedd 100644
--- a/ubusmsg.h
+++ b/ubusmsg.h
@@ -62,6 +62,13 @@ enum ubus_msg_type {
UBUS_MSG_SUBSCRIBE,
UBUS_MSG_UNSUBSCRIBE,
+ /*
+ * send a notification to all subscribers of an object.
+ * when sent from the server, it indicates a subscription
+ * status change
+ */
+ UBUS_MSG_NOTIFY,
+
/* must be last */
__UBUS_MSG_LAST,
};
@@ -81,6 +88,8 @@ enum ubus_msg_attr {
UBUS_ATTR_DATA,
UBUS_ATTR_TARGET,
+ UBUS_ATTR_ACTIVE,
+
/* must be last */
UBUS_ATTR_MAX,
};