[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