[SSSD] [PATCH] SBUS: Generate introspection from the interface meta

Jakub Hrozek jhrozek at redhat.com
Thu Apr 10 09:03:20 UTC 2014


The attached patch implements introspecting the sbus interfaces as
tracked by #2234.

There is one part of the patch I dislike, but I wanted to get other
opinions, too -- the discard_const in sbus_message_handler(). I was
going back and forth on whether sbus_introspect() should be an sbus
handler like the other handlers or whether I should special case it (and
then pass the interface directly and not through a void pointer).
Special casing the introspection might be a bit cleaner, but would cause
some duplication in sbus_message_handler().

The other question I have is whether there should be a way to mark an
interface as not introspectable? I can't think of a reason, so by
default all interfaces can be introspected.

The introspection also doesn't generate DocString annotations yet, I
will send a patch for that separately.
-------------- next part --------------
>From 50a07ca1556c0ab97b32bbd0ab2342dce1ef374e Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Thu, 10 Apr 2014 09:11:12 +0200
Subject: [PATCH] SBUS: Generate introspection from the interface meta
 structure

https://fedorahosted.org/sssd/ticket/2234

This patch generates the introspection data from the sbus interface meta
structure. The generated XML conforms to
http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format

The XML description of the interface also always includes the
org.freedesktop.DBus.Introspectable interface, which this patch also allows
in the policy settings.
---
 Makefile.am                                        |   1 +
 .../ifp/org.freedesktop.sssd.infopipe.conf         |   3 +
 src/sbus/sssd_dbus_connection.c                    |  14 +-
 src/sbus/sssd_dbus_introspect.c                    | 324 +++++++++++++++++++++
 src/sbus/sssd_dbus_private.h                       |   2 +
 src/tests/sbus_tests.c                             |  49 ++++
 6 files changed, 392 insertions(+), 1 deletion(-)
 create mode 100644 src/sbus/sssd_dbus_introspect.c

diff --git a/Makefile.am b/Makefile.am
index b925dbd08bb1046d5196a9dcb9f00351d19ed5f5..bd5a1925d8c983cbe8eff243856b6abf50e55c30 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -600,6 +600,7 @@ libsss_util_la_SOURCES = \
     src/sbus/sssd_dbus_common.c \
     src/sbus/sssd_dbus_connection.c \
     src/sbus/sssd_dbus_meta.c \
+    src/sbus/sssd_dbus_introspect.c \
     src/sbus/sssd_dbus_request.c \
     src/sbus/sssd_dbus_server.c \
     src/util/util.c \
diff --git a/src/responder/ifp/org.freedesktop.sssd.infopipe.conf b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
index fea847ceed6b3d0f999f4bd747cc7b16df2124a9..56e460aa11c2534e95092fb10216b05c50c04d59 100644
--- a/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
+++ b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf
@@ -16,6 +16,9 @@
   <!-- Right now, this will be handled by a limited ACL
        within the InfoPipe Daemon. -->
   <policy context="default">
+    <allow send_destination="org.freedesktop.sssd.infopipe"
+           send_interface="org.freedesktop.DBus.Introspectable"/>
+
     <allow send_interface="org.freedesktop.sssd.infopipe"/>
   </policy>
 
diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c
index 150b5469e32155a812a10a893466c8d3a3d31f46..281280ba72e2785eb099ccc9dca00b9ed0bf4650 100644
--- a/src/sbus/sssd_dbus_connection.c
+++ b/src/sbus/sssd_dbus_connection.c
@@ -378,6 +378,7 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn,
     const struct sbus_interface_meta *interface;
     struct sbus_request *dbus_req = NULL;
     sbus_msg_handler_fn handler_fn = NULL;
+    void *handler_data = NULL;
     DBusHandlerResult result;
     int ret;
 
@@ -406,6 +407,7 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn,
         method = sbus_meta_find_method(interface, msg_method);
         if (method && method->vtable_offset)
             handler_fn = VTABLE_FUNC(intf_p->intf->vtable, method->vtable_offset);
+            handler_data = intf_p->intf->instance_data;
 
         if (!method) {
             /* Reply DBUS_ERROR_UNKNOWN_METHOD */
@@ -425,6 +427,16 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn,
             dbus_message_unref(reply);
             result = DBUS_HANDLER_RESULT_HANDLED;
         }
+    } else {
+        /* Special case: check for Introspection request
+         * This is usually only useful for system bus connections
+         */
+        if (strcmp(msg_interface, DBUS_INTROSPECT_INTERFACE) == 0 &&
+                strcmp(msg_method, DBUS_INTROSPECT_METHOD) == 0) {
+            DEBUG(SSSDBG_TRACE_LIBS, "Got introspection request\n");
+            handler_fn = sbus_introspect;
+            handler_data = discard_const(interface);
+        }
     }
 
     if (handler_fn) {
@@ -433,7 +445,7 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn,
             ret = ENOMEM;
         } else {
             dbus_req->method = method;
-            ret = handler_fn(dbus_req, intf_p->intf->instance_data);
+            ret = handler_fn(dbus_req, handler_data);
         }
         if (ret != EOK) {
             if (dbus_req)
diff --git a/src/sbus/sssd_dbus_introspect.c b/src/sbus/sssd_dbus_introspect.c
new file mode 100644
index 0000000000000000000000000000000000000000..6114293b2ae1cbf0ea00a43604d9c6688ed5884b
--- /dev/null
+++ b/src/sbus/sssd_dbus_introspect.c
@@ -0,0 +1,324 @@
+/*
+    Authors:
+        Jakub Hrozek <jhrozek at redhat.com>
+
+    Copyright (C) 2014 Red Hat
+
+    SBUS: Interface introspection
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    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.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/time.h>
+#include <dbus/dbus.h>
+
+#include "util/util.h"
+#include "sbus/sssd_dbus.h"
+#include "sbus/sssd_dbus_private.h"
+#include "sbus/sssd_dbus_meta.h"
+
+#define SSS_INTROSPECT_DOCTYPE  \
+    "<!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 SSS_INTROSPECT_INTERFACE_INTROSPECTABLE                      \
+     " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"   \
+     "   <method name=\"Introspect\">\n"                             \
+     "     <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"      \
+     "   </method>\n"                                                \
+     " </interface>\n"
+
+
+struct introspect_ctx {
+    FILE *f;
+    char *buf;
+    size_t size;
+
+    const struct sbus_interface_meta *iface;
+};
+
+static int introspect_ctx_destructor(struct introspect_ctx *ictx)
+{
+    free(ictx->buf);
+    ictx->buf = NULL;
+    return 0;
+}
+
+static errno_t introspect_begin(struct introspect_ctx *ictx)
+{
+    errno_t ret;
+
+    ictx->f = open_memstream(&ictx->buf, &ictx->size);
+    if (ictx->f == NULL) {
+        return ENOMEM;
+    }
+
+    ret = fputs(SSS_INTROSPECT_DOCTYPE, ictx->f);
+    if (ret < 0) return EIO;
+    ret = fputs("<node>\n", ictx->f);
+    if (ret < 0) return EIO;
+
+    ret = fprintf(ictx->f, "  <interface name=\"%s\">\n", ictx->iface->name);
+    if (ret <= 0) return EIO;
+
+    return EOK;
+}
+
+static errno_t introspect_add_arg(struct introspect_ctx *ictx,
+                                  const struct sbus_arg_meta *a,
+                                  const char *direction)
+{
+    errno_t ret;
+
+    ret = fprintf(ictx->f,
+                  "      <arg type=\"%s\" name=\"%s\"",
+                  a->type, a->name);
+    if (ret <= 0) return EIO;
+
+    if (direction) {
+        ret = fprintf(ictx->f, " direction=\"%s\"", direction);
+        if (ret <= 0) return EIO;
+    }
+
+    ret = fprintf(ictx->f, "/>\n");
+    if (ret <= 0) return EIO;
+
+    return EOK;
+}
+
+#define introspect_add_in_arg(i, a) introspect_add_arg(i, a, "in");
+#define introspect_add_out_arg(i, a) introspect_add_arg(i, a, "out");
+#define introspect_add_sig_arg(i, a) introspect_add_arg(i, a, NULL);
+
+static errno_t introspect_add_meth(struct introspect_ctx *ictx,
+                                   const struct sbus_method_meta *m)
+{
+    errno_t ret;
+    int i;
+
+    ret = fprintf(ictx->f, "    <method name=\"%s\">\n", m->name);
+    if (ret <= 0) return EIO;
+
+    if (m->in_args != NULL) {
+        for (i = 0; m->in_args[i].name != NULL; i++) {
+            ret = introspect_add_in_arg(ictx, &m->in_args[i]);
+            if (ret != EOK) {
+                continue;
+            }
+        }
+    }
+
+    if (m->out_args != NULL) {
+        for (i = 0; m->out_args[i].name != NULL; i++) {
+            ret = introspect_add_out_arg(ictx, &m->out_args[i]);
+            if (ret != EOK) {
+                continue;
+            }
+        }
+    }
+
+    ret = fputs("    </method>\n", ictx->f);
+    if (ret < 0) return EIO;
+
+    return EOK;
+}
+
+static errno_t introspect_add_methods(struct introspect_ctx *ictx)
+{
+    errno_t ret;
+    int i;
+
+    if (ictx->iface->methods == NULL) {
+        /* An interface with no methods */
+        return EOK;
+    }
+
+    for (i = 0; ictx->iface->methods[i].name != NULL; i++) {
+        ret = introspect_add_meth(ictx, &ictx->iface->methods[i]);
+        if (ret != EOK) {
+            continue;
+        }
+    }
+
+    return EOK;
+}
+
+static errno_t introspect_add_sig(struct introspect_ctx *ictx,
+                                  const struct sbus_signal_meta *s)
+{
+    errno_t ret;
+    int i;
+
+    ret = fprintf(ictx->f, "    <signal name=\"%s\">\n", s->name);
+    if (ret <= 0) return EIO;
+
+    if (s->args != NULL) {
+        for (i = 0; s->args[i].name != NULL; i++) {
+            ret = introspect_add_sig_arg(ictx, &s->args[i]);
+            if (ret != EOK) {
+                continue;
+            }
+        }
+    }
+
+    ret = fputs("    </signal>\n", ictx->f);
+    if (ret < 0) return EIO;
+
+    return EOK;
+}
+
+static errno_t introspect_add_signals(struct introspect_ctx *ictx)
+{
+    errno_t ret;
+    int i;
+
+    if (ictx->iface->signals == NULL) {
+        /* An interface with no signals */
+        return EOK;
+    }
+
+    for (i = 0; ictx->iface->signals[i].name != NULL; i++) {
+        ret = introspect_add_sig(ictx, &ictx->iface->signals[i]);
+        if (ret != EOK) {
+            continue;
+        }
+    }
+
+    return EOK;
+}
+
+static errno_t introspect_add_prop(struct introspect_ctx *ictx,
+                                   const struct sbus_property_meta *p)
+{
+    errno_t ret;
+
+    ret = fprintf(ictx->f, "    <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n",
+                           p->name, p->type,
+                           p->flags & SBUS_PROPERTY_WRITABLE ? "readwrite" : "read");
+    if (ret <= 0) return EIO;
+
+    return EOK;
+}
+
+static errno_t introspect_add_properties(struct introspect_ctx *ictx)
+{
+    errno_t ret;
+    int i;
+
+    if (ictx->iface->properties == NULL) {
+        /* An interface with no properties */
+        return EOK;
+    }
+
+    for (i = 0; ictx->iface->properties[i].name != NULL; i++) {
+        ret = introspect_add_prop(ictx, &ictx->iface->properties[i]);
+        if (ret != EOK) {
+            continue;
+        }
+    }
+
+    return EOK;
+}
+
+static errno_t introspect_finish(struct introspect_ctx *ictx)
+{
+    errno_t ret;
+
+    ret = fputs("  </interface>\n", ictx->f);
+    if (ret < 0) return EIO;
+
+    ret = fputs(SSS_INTROSPECT_INTERFACE_INTROSPECTABLE, ictx->f);
+    if (ret < 0) return EIO;
+
+    ret = fputs("</node>\n", ictx->f);
+    if (ret < 0) return EIO;
+
+    fflush(ictx->f);
+    return EOK;
+}
+
+static char *sbus_introspect_xml(TALLOC_CTX *mem_ctx,
+                                 const struct sbus_interface_meta *iface)
+{
+    struct introspect_ctx *ictx;
+    char *buf_out = NULL;
+    errno_t ret;
+
+    ictx = talloc_zero(mem_ctx, struct introspect_ctx);
+    if (ictx == NULL) {
+        return NULL;
+    }
+    ictx->iface = iface;
+    talloc_set_destructor(ictx, introspect_ctx_destructor);
+
+    ret = introspect_begin(ictx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "introspect_begin failed: %d\n", ret);
+        goto done;
+    }
+
+    ret = introspect_add_methods(ictx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "introspect_add_methods failed: %d\n", ret);
+        goto done;
+    }
+
+    ret = introspect_add_signals(ictx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "introspect_add_signals failed: %d\n", ret);
+        goto done;
+    }
+
+    ret = introspect_add_properties(ictx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "introspect_add_properties failed: %d\n", ret);
+        goto done;
+    }
+
+    ret = introspect_finish(ictx);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "introspect_finish failed: %d\n", ret);
+        goto done;
+    }
+
+    buf_out = talloc_memdup(mem_ctx, ictx->buf, ictx->size + 1);
+    DEBUG(SSSDBG_TRACE_LIBS, "Introspection: \n%s\n", buf_out);
+done:
+    talloc_free(ictx);
+    return buf_out;
+}
+
+int sbus_introspect(struct sbus_request *dbus_req, void *pvt)
+{
+    char *xml;
+    DBusError dberr;
+    const struct sbus_interface_meta *iface;
+
+    iface = (struct sbus_interface_meta *) pvt;
+
+    xml = sbus_introspect_xml(dbus_req, iface);
+    if (xml == NULL) {
+        dbus_error_init(&dberr);
+        dbus_set_error_const(&dberr,
+                             DBUS_ERROR_NO_MEMORY,
+                             "Failed to generate introspection data\n");
+        return sbus_request_fail_and_finish(dbus_req, &dberr);
+    }
+
+    return sbus_request_return_and_finish(dbus_req,
+                                          DBUS_TYPE_STRING, &xml,
+                                          DBUS_TYPE_INVALID);
+
+}
diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h
index 88230cfca4527c326c05efbe052d04b15dd00e88..1bd0f11c9f1e1483f76645ca8820aa1aff3df1cb 100644
--- a/src/sbus/sssd_dbus_private.h
+++ b/src/sbus/sssd_dbus_private.h
@@ -98,4 +98,6 @@ struct sbus_request *
 sbus_new_request(struct sbus_connection *conn, struct sbus_interface *intf,
                  DBusMessage *message);
 
+int sbus_introspect(struct sbus_request *dbus_req, void *pvt);
+
 #endif /* _SSSD_DBUS_PRIVATE_H_ */
diff --git a/src/tests/sbus_tests.c b/src/tests/sbus_tests.c
index b8cd4abb0d306d0b9dbf7896f0e1709c1dc7fd40..10f0d65773c81c83c3c6945ca1d5ce8573b50552 100644
--- a/src/tests/sbus_tests.c
+++ b/src/tests/sbus_tests.c
@@ -44,6 +44,23 @@
 #define PILOT_BLINK "Blink"
 #define PILOT_EAT "Eat"
 
+#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"                                                                           \
+        "  <interface name=\"test.Pilot\">\n"                                                \
+        "    <method name=\"Blink\">\n"                                                      \
+        "    </method>\n"                                                                    \
+        "    <method name=\"Eat\">\n"                                                        \
+        "    </method>\n"                                                                    \
+        "  </interface>\n"                                                                   \
+        " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"                        \
+        "   <method name=\"Introspect\">\n"                                                  \
+        "     <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"                           \
+        "   </method>\n"                                                                     \
+        " </interface>\n"                                                                    \
+        "</node>\n"
+
 /* our vtable */
 struct pilot_vtable {
     struct sbus_vtable vtable;
@@ -276,6 +293,37 @@ START_TEST(test_request_parse_bad_args)
 }
 END_TEST
 
+START_TEST(test_introspection)
+{
+    TALLOC_CTX *ctx;
+    DBusConnection *client;
+    DBusError error = DBUS_ERROR_INIT;
+    DBusMessage *reply;
+    char *xml;
+
+    ctx = talloc_new(NULL);
+    client = test_dbus_setup_mock(ctx, NULL, pilot_test_server_init, NULL);
+
+    reply = test_dbus_call_sync(client,
+                                "/test/leela",
+                                DBUS_INTROSPECT_INTERFACE,
+                                DBUS_INTROSPECT_METHOD,
+                                &error,
+                                DBUS_TYPE_INVALID); /* bad agruments */
+
+    ck_assert(reply != NULL);
+    ck_assert(!dbus_error_is_set(&error));
+    ck_assert(dbus_message_get_args(reply, NULL,
+                                    DBUS_TYPE_STRING, &xml,
+                                    DBUS_TYPE_INVALID));
+    ck_assert_str_eq(PILOT_IFACE_INTROSPECT, xml);
+
+    dbus_message_unref(reply);
+
+    talloc_free(ctx);
+}
+END_TEST
+
 TCase *create_sbus_tests(void)
 {
     TCase *tc = tcase_create("tests");
@@ -283,6 +331,7 @@ TCase *create_sbus_tests(void)
     tcase_add_test(tc, test_raw_handler);
     tcase_add_test(tc, test_request_parse_ok);
     tcase_add_test(tc, test_request_parse_bad_args);
+    tcase_add_test(tc, test_introspection);
 
     return tc;
 }
-- 
1.9.0



More information about the sssd-devel mailing list