libubus: do not register/unregister with uloop during sync requests
[project/ubus.git] / ubusd_obj.c
index a702fa6..0831473 100644 (file)
@@ -1,3 +1,16 @@
+/*
+ * Copyright (C) 2011 Felix Fietkau <nbd@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * 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.
+ */
+
 #include "ubusd.h"
 #include "ubusd_obj.h"
 
@@ -31,7 +44,7 @@ static bool ubus_create_obj_method(struct ubus_object_type *type, struct blob_at
        if (!m)
                return false;
 
-       list_add(&m->list, &type->methods);
+       list_add_tail(&m->list, &type->methods);
        memcpy(m->data, attr, bloblen);
        m->name = blobmsg_name(m->data);
 
@@ -45,9 +58,12 @@ static struct ubus_object_type *ubus_create_obj_type(struct blob_attr *sig)
        int rem;
 
        type = calloc(1, sizeof(*type));
+       if (!type)
+               return NULL;
+
        type->refcount = 1;
 
-       if (!ubus_alloc_id(&obj_types, &type->id))
+       if (!ubus_alloc_id(&obj_types, &type->id, 0))
                goto error_free;
 
        INIT_LIST_HEAD(&type->methods);
@@ -85,64 +101,136 @@ static struct ubus_object_type *ubus_get_obj_type(uint32_t obj_id)
        return type;
 }
 
+struct ubus_object *ubusd_create_object_internal(struct ubus_object_type *type, uint32_t id)
+{
+       struct ubus_object *obj;
+
+       obj = calloc(1, sizeof(*obj));
+       if (!obj)
+               return NULL;
+
+       if (!ubus_alloc_id(&objects, &obj->id, id))
+               goto error_free;
+
+       obj->type = type;
+       INIT_LIST_HEAD(&obj->list);
+       INIT_LIST_HEAD(&obj->events);
+       INIT_LIST_HEAD(&obj->subscribers);
+       INIT_LIST_HEAD(&obj->target_list);
+       if (type)
+               type->refcount++;
+
+       return obj;
+
+error_free:
+       free(obj);
+       return NULL;
+}
+
 struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr)
 {
        struct ubus_object *obj;
        struct ubus_object_type *type = NULL;
 
        if (attr[UBUS_ATTR_OBJTYPE])
-               type = ubus_get_obj_type(blob_get_int32(attr[UBUS_ATTR_OBJTYPE]));
+               type = ubus_get_obj_type(blob_get_u32(attr[UBUS_ATTR_OBJTYPE]));
        else if (attr[UBUS_ATTR_SIGNATURE])
                type = ubus_create_obj_type(attr[UBUS_ATTR_SIGNATURE]);
 
-       if (!type)
-               return NULL;
+       obj = ubusd_create_object_internal(type, 0);
+       if (type)
+               ubus_unref_object_type(type);
 
-       obj = calloc(1, sizeof(*obj));
-       if (!ubus_alloc_id(&objects, &obj->id))
-               goto error_free;
+       if (!obj)
+               return NULL;
 
        if (attr[UBUS_ATTR_OBJPATH]) {
+               if (ubusd_acl_check(cl, blob_data(attr[UBUS_ATTR_OBJPATH]), NULL, UBUS_ACL_PUBLISH))
+                       goto free;
+
                obj->path.key = strdup(blob_data(attr[UBUS_ATTR_OBJPATH]));
-               if (avl_insert(&path, &obj->path) != 0)
-                       goto error_del_id;
+               if (!obj->path.key)
+                       goto free;
+
+               if (avl_insert(&path, &obj->path) != 0) {
+                       free((void *) obj->path.key);
+                       obj->path.key = NULL;
+                       goto free;
+               }
+               ubusd_send_obj_event(obj, true);
        }
 
-       obj->type = type;
        obj->client = cl;
        list_add(&obj->list, &cl->objects);
 
        return obj;
 
-error_del_id:
-       free(obj->path.key);
-       ubus_free_id(&objects, &obj->id);
-error_free:
-       ubus_unref_object_type(type);
-       free(obj);
+free:
+       ubusd_free_object(obj);
        return NULL;
 }
 
+void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target)
+{
+       struct ubus_subscription *s;
+       bool first = list_empty(&target->subscribers);
+
+       s = calloc(1, sizeof(*s));
+       if (!s)
+               return;
+
+       s->subscriber = obj;
+       s->target = target;
+       list_add(&s->list, &target->subscribers);
+       list_add(&s->target_list, &obj->target_list);
+
+       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)
 {
+       struct ubus_subscription *s, *tmp;
+
+       list_for_each_entry_safe(s, tmp, &obj->target_list, target_list) {
+               ubus_unsubscribe(s);
+       }
+       list_for_each_entry_safe(s, tmp, &obj->subscribers, list) {
+               ubus_notify_unsubscribe(s);
+       }
+
+       ubusd_event_cleanup_object(obj);
        if (obj->path.key) {
+               ubusd_send_obj_event(obj, false);
                avl_delete(&path, &obj->path);
-               free(obj->path.key);
+               free((void *) obj->path.key);
        }
-       list_del(&obj->list);
+       if (!list_empty(&obj->list))
+               list_del(&obj->list);
        ubus_free_id(&objects, &obj->id);
-       ubus_unref_object_type(obj->type);
+       if (obj->type)
+               ubus_unref_object_type(obj->type);
        free(obj);
 }
 
-static int ubus_cmp_path(const void *k1, const void *k2, void *ptr)
-{
-       return strcmp(k1, k2);
-}
-
-static void __init ubusd_obj_init(void)
+static void __constructor ubusd_obj_init(void)
 {
        ubus_init_id_tree(&objects);
        ubus_init_id_tree(&obj_types);
-       avl_init(&path, ubus_cmp_path, false, NULL);
+       ubus_init_string_tree(&path, false);
+       ubusd_event_init();
+       ubusd_acl_init();
+       ubusd_monitor_init();
 }