From 9c13aa15cbe377158356b7ec87e98c0fec0e5791 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Wed, 20 May 2015 14:14:53 +0200
Subject: [PATCH 3/4] SBUS: Add support for <node /> in introspection

---
 src/sbus/sssd_dbus.h            |  10 ++++
 src/sbus/sssd_dbus_connection.c |   7 +++
 src/sbus/sssd_dbus_interface.c  | 115 ++++++++++++++++++++++++++++++++++++++++
 src/sbus/sssd_dbus_introspect.c |  41 ++++++++++++--
 src/sbus/sssd_dbus_private.h    |  11 ++++
 src/tests/sbus_tests.c          |   2 +-
 6 files changed, 181 insertions(+), 5 deletions(-)

diff --git a/src/sbus/sssd_dbus.h b/src/sbus/sssd_dbus.h
index 61c18ae28f4617239ed0e941f6cf5dd9fe0b24db..818070fa5eb28c8dc9bcb71ac0fb2008a10c454a 100644
--- a/src/sbus/sssd_dbus.h
+++ b/src/sbus/sssd_dbus.h
@@ -80,6 +80,10 @@ typedef void (*sbus_conn_reconn_callback_fn)(struct sbus_connection *, int, void
  */
 typedef int (*sbus_server_conn_init_fn)(struct sbus_connection *, void *);
 
+typedef const char ** (* sbus_nodes_fn)(TALLOC_CTX *mem_ctx,
+                                        const char *path,
+                                        void *data);
+
 enum {
     SBUS_CONN_TYPE_PRIVATE = 1,
     SBUS_CONN_TYPE_SHARED,
@@ -177,6 +181,12 @@ int sbus_conn_register_iface(struct sbus_connection *conn,
                              const char *object_path,
                              void *handler_data);
 
+void
+sbus_conn_register_nodes(struct sbus_connection *conn,
+                         const char *path,
+                         sbus_nodes_fn nodes_fn,
+                         void *data);
+
 errno_t
 sbus_conn_reregister_paths(struct sbus_connection *conn);
 
diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c
index 8ff279c1bf5f20d5396e1e41c6a162328657091f..d0df95bb2e5d3bbdf75f71e84c1901a40171ed12 100644
--- a/src/sbus/sssd_dbus_connection.c
+++ b/src/sbus/sssd_dbus_connection.c
@@ -168,6 +168,13 @@ int sbus_init_connection(TALLOC_CTX *ctx,
         return EIO;
     }
 
+    ret = sbus_nodes_hash_init(conn, conn, &conn->nodes_fns);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create node functions hash table\n");
+        talloc_free(conn);
+        return EIO;
+    }
+
     ret = sss_hash_create(conn, 32, &conn->clients);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create clients hash table\n");
diff --git a/src/sbus/sssd_dbus_interface.c b/src/sbus/sssd_dbus_interface.c
index 1651478a898e0272312dc910b66be40c1bace435..51b0bbf0c4c04f65309d87d78f43abaef7588e47 100644
--- a/src/sbus/sssd_dbus_interface.c
+++ b/src/sbus/sssd_dbus_interface.c
@@ -732,6 +732,106 @@ done:
     return ret;
 }
 
+errno_t
+sbus_nodes_hash_init(TALLOC_CTX *mem_ctx,
+                     struct sbus_connection *conn,
+                     hash_table_t **_table)
+{
+    return sss_hash_create_ex(mem_ctx, 10, _table, 0, 0, 0, 0,
+                              NULL, conn);
+}
+
+struct sbus_nodes_data {
+    sbus_nodes_fn nodes_fn;
+    void *handler_data;
+};
+
+static errno_t
+sbus_nodes_hash_add(hash_table_t *table,
+                    const char *object_path,
+                    sbus_nodes_fn nodes_fn,
+                    void *handler_data)
+{
+    TALLOC_CTX *tmp_ctx;
+    struct sbus_nodes_data *data;
+    hash_key_t key;
+    hash_value_t value;
+    errno_t ret;
+    bool has_key;
+    int hret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    key.type = HASH_KEY_STRING;
+    key.str = talloc_strdup(tmp_ctx, object_path);
+    if (key.str == NULL) {
+        return ENOMEM;
+    }
+
+    has_key = hash_has_key(table, &key);
+    if (has_key) {
+        ret = EEXIST;
+        goto done;
+    }
+
+    data = talloc_zero(tmp_ctx, struct sbus_nodes_data);
+    if (data == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    data->handler_data = handler_data;
+    data->nodes_fn = nodes_fn;
+
+    value.type = HASH_VALUE_PTR;
+    value.ptr = data;
+
+    hret = hash_enter(table, &key, &value);
+    if (hret != HASH_SUCCESS) {
+        ret = EIO;
+        goto done;
+    }
+
+    talloc_steal(table, key.str);
+    talloc_steal(table, data);
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+const char **
+sbus_nodes_hash_lookup(TALLOC_CTX *mem_ctx,
+                       hash_table_t *table,
+                       const char *object_path)
+{
+    struct sbus_nodes_data *data;
+    hash_key_t key;
+    hash_value_t value;
+    int hret;
+
+    key.type = HASH_KEY_STRING;
+    key.str = discard_const(object_path);
+
+    hret = hash_lookup(table, &key, &value);
+    if (hret == HASH_ERROR_KEY_NOT_FOUND) {
+        return NULL;
+    } else if (hret != HASH_SUCCESS) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unable to search hash table: hret=%d\n", hret);
+        return NULL;
+    }
+
+    data = (struct sbus_nodes_data *)(value.ptr);
+
+    return data->nodes_fn(mem_ctx, object_path, data->handler_data);
+}
+
 static struct sbus_interface *
 sbus_new_interface(TALLOC_CTX *mem_ctx,
                    const char *object_path,
@@ -870,6 +970,21 @@ sbus_conn_register_iface(struct sbus_connection *conn,
     return ret;
 }
 
+void
+sbus_conn_register_nodes(struct sbus_connection *conn,
+                         const char *path,
+                         sbus_nodes_fn nodes_fn,
+                         void *data)
+{
+    errno_t ret;
+
+    ret = sbus_nodes_hash_add(conn->nodes_fns, path, nodes_fn, data);
+    if (ret != EOK && ret != EEXIST) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Unable to register node function with "
+              "%s. Introspection may not work correctly.\n", path);
+    }
+}
+
 errno_t
 sbus_conn_reregister_paths(struct sbus_connection *conn)
 {
diff --git a/src/sbus/sssd_dbus_introspect.c b/src/sbus/sssd_dbus_introspect.c
index 34279c8d4f398287c4bd4e1e81be47aaa79c1dd4..a66432dd97fecea81e9f5f543bba6ca6a9a13107 100644
--- a/src/sbus/sssd_dbus_introspect.c
+++ b/src/sbus/sssd_dbus_introspect.c
@@ -34,7 +34,7 @@
     "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
     " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
 
-#define FMT_NODE         "<node>\n"
+#define FMT_NODE         "<node name=\"%s\">\n"
 #define FMT_IFACE        "  <interface name=\"%s\">\n"
 #define FMT_METHOD       "    <method name=\"%s\">\n"
 #define FMT_METHOD_NOARG "    <method name=\"%s\" />\n"
@@ -46,6 +46,7 @@
 #define FMT_SIGNAL_CLOSE "    </signal>\n"
 #define FMT_PROPERTY     "    <property name=\"%s\" type=\"%s\" access=\"%s\" />\n"
 #define FMT_IFACE_CLOSE  "  </interface>\n"
+#define FMT_CHILD_NODE   "  <node name=\"%s\" />\n"
 #define FMT_NODE_CLOSE   "</node>\n"
 
 #define WRITE_OR_FAIL(file, ret, label, fmt, ...) do { \
@@ -294,8 +295,31 @@ done:
     return ret;
 }
 
+static int
+sbus_introspect_generate_nodes(FILE *file, const char **nodes)
+{
+    int ret;
+    int i;
+
+    if (nodes == NULL) {
+        return EOK;
+    }
+
+    for (i = 0; nodes[i] != NULL; i++) {
+        WRITE_OR_FAIL(file, ret, done, FMT_CHILD_NODE, nodes[i]);
+    }
+
+    ret = EOK;
+
+done:
+    return ret;
+}
+
 static char *
-sbus_introspect_generate(TALLOC_CTX *mem_ctx, struct sbus_interface_list *list)
+sbus_introspect_generate(TALLOC_CTX *mem_ctx,
+                         const char *node,
+                         const char **nodes,
+                         struct sbus_interface_list *list)
 {
     struct sbus_interface_list *item;
     char *introspect = NULL;
@@ -310,7 +334,7 @@ sbus_introspect_generate(TALLOC_CTX *mem_ctx, struct sbus_interface_list *list)
     }
 
     WRITE_OR_FAIL(memstream, ret, done, FMT_DOCTYPE);
-    WRITE_OR_FAIL(memstream, ret, done, FMT_NODE);
+    WRITE_OR_FAIL(memstream, ret, done, FMT_NODE, node);
 
     DLIST_FOR_EACH(item, list) {
         ret = sbus_introspect_generate_iface(memstream, item->interface);
@@ -319,6 +343,11 @@ sbus_introspect_generate(TALLOC_CTX *mem_ctx, struct sbus_interface_list *list)
         }
     }
 
+    ret = sbus_introspect_generate_nodes(memstream, nodes);
+    if (ret != EOK) {
+        goto done;
+    }
+
     WRITE_OR_FAIL(memstream, ret, done, FMT_NODE_CLOSE);
 
     fflush(memstream);
@@ -341,6 +370,7 @@ sbus_introspect(struct sbus_request *sbus_req, void *pvt)
     DBusError *error;
     struct sbus_interface_list *list;
     struct sbus_connection *conn;
+    const char **nodes;
     char *introspect;
     errno_t ret;
 
@@ -354,7 +384,10 @@ sbus_introspect(struct sbus_request *sbus_req, void *pvt)
         return sbus_request_fail_and_finish(sbus_req, error);
     }
 
-    introspect = sbus_introspect_generate(sbus_req, list);
+    nodes = sbus_nodes_hash_lookup(sbus_req, conn->nodes_fns, sbus_req->path);
+
+    introspect = sbus_introspect_generate(sbus_req, sbus_req->path,
+                                          nodes, list);
     if (introspect == NULL) {
         ret = ENOMEM;
         goto done;
diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h
index 24b6bec18cf8331942480312505b2dce77d2c2ac..0493e1536573051d22f3c4b0ba50c977fcf7e269 100644
--- a/src/sbus/sssd_dbus_private.h
+++ b/src/sbus/sssd_dbus_private.h
@@ -48,6 +48,7 @@ struct sbus_connection {
     int disconnect;
 
     hash_table_t *managed_paths;
+    hash_table_t *nodes_fns;
 
     /* reconnect settings */
     int retries;
@@ -129,6 +130,16 @@ sbus_opath_hash_lookup_supported(TALLOC_CTX *mem_ctx,
                                  const char *object_path,
                                  struct sbus_interface_list **_list);
 
+errno_t
+sbus_nodes_hash_init(TALLOC_CTX *mem_ctx,
+                     struct sbus_connection *conn,
+                     hash_table_t **_table);
+
+const char **
+sbus_nodes_hash_lookup(TALLOC_CTX *mem_ctx,
+                       hash_table_t *table,
+                       const char *object_path);
+
 void
 sbus_request_invoke_or_finish(struct sbus_request *dbus_req,
                               sbus_msg_handler_fn handler_fn,
diff --git a/src/tests/sbus_tests.c b/src/tests/sbus_tests.c
index 47d0556cfd42acb98be5a3dc977e3d4f3ad671f4..598cc536db436032b6d14c6515cd411b1bd2279e 100644
--- a/src/tests/sbus_tests.c
+++ b/src/tests/sbus_tests.c
@@ -47,7 +47,7 @@
 #define PILOT_IFACE_INTROSPECT \
         "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
         " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" \
-        "<node>\n" \
+        "<node name=\"/test/leela\">\n" \
         "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \
         "    <method name=\"Introspect\">\n" \
         "      <arg type=\"s\" name=\"data\" direction=\"out\" />\n" \
-- 
2.1.0

