summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2014-04-10 09:11:12 +0200
committerJakub Hrozek <jhrozek@redhat.com>2014-06-03 13:16:26 +0200
commite00e430c6eb82673daa2c248e642dec264f211cc (patch)
treec332988a610757d60086fa7672e92c02a36d564d
parent9cd08bf4b843529c14f71655d62687589301f198 (diff)
downloadsssd-e00e430c6eb82673daa2c248e642dec264f211cc.tar.gz
sssd-e00e430c6eb82673daa2c248e642dec264f211cc.tar.xz
sssd-e00e430c6eb82673daa2c248e642dec264f211cc.zip
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. (cherry picked from commit 42c28b9424b6ef8a0021b124773e171dd5defadd)
-rw-r--r--Makefile.am1
-rw-r--r--src/responder/ifp/org.freedesktop.sssd.infopipe.conf3
-rw-r--r--src/sbus/sssd_dbus_connection.c35
-rw-r--r--src/sbus/sssd_dbus_introspect.c330
-rw-r--r--src/sbus/sssd_dbus_private.h6
-rw-r--r--src/tests/sbus_tests.c49
6 files changed, 421 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am
index 9e77e6f38..1ddf2ff18 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -576,6 +576,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 fea847cee..56e460aa1 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 1d927c847..27e1f3ea8 100644
--- a/src/sbus/sssd_dbus_connection.c
+++ b/src/sbus/sssd_dbus_connection.c
@@ -377,6 +377,8 @@ 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; /* Must be a talloc pointer! */
+ struct sbus_introspect_ctx *ictx = NULL;
DBusHandlerResult result;
int ret;
@@ -424,19 +426,46 @@ 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");
+ ictx = talloc(intf_p->conn, struct sbus_introspect_ctx);
+ if (ictx == NULL) {
+ result = sbus_reply_internal_error(message, intf_p->conn);
+ } else {
+ handler_fn = sbus_introspect;
+ ictx->iface = interface;
+ handler_data = ictx;
+ }
+ }
}
if (handler_fn) {
dbus_req = sbus_new_request(intf_p->conn, intf_p->intf, message);
if (!dbus_req) {
+ talloc_zfree(handler_data);
ret = ENOMEM;
} else {
dbus_req->method = method;
- ret = handler_fn(dbus_req, intf_p->intf->instance_data);
+ if (handler_data) {
+ /* If the handler uses private instance data, make
+ * sure they go away when the request does
+ */
+ talloc_steal(dbus_req, handler_data);
+ } else {
+ /* If no custom handler data is set, pass on the
+ * interface data
+ */
+ handler_data = intf_p->intf->instance_data;
+ }
+ ret = handler_fn(dbus_req, handler_data);
}
if (ret != EOK) {
- if (dbus_req)
- talloc_free(dbus_req);
+ talloc_free(dbus_req);
result = sbus_reply_internal_error(message, intf_p->conn);
} else {
result = DBUS_HANDLER_RESULT_HANDLED;
diff --git a/src/sbus/sssd_dbus_introspect.c b/src/sbus/sssd_dbus_introspect.c
new file mode 100644
index 000000000..ffc1f4d38
--- /dev/null
+++ b/src/sbus/sssd_dbus_introspect.c
@@ -0,0 +1,330 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@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;
+ struct sbus_introspect_ctx *ictx;
+
+ ictx = talloc_get_type(pvt, struct sbus_introspect_ctx);
+ if (ictx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n");
+ return sbus_request_return_and_finish(dbus_req, DBUS_TYPE_INVALID);
+ }
+ iface = ictx->iface;
+
+ 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 88230cfca..929caeccb 100644
--- a/src/sbus/sssd_dbus_private.h
+++ b/src/sbus/sssd_dbus_private.h
@@ -98,4 +98,10 @@ struct sbus_request *
sbus_new_request(struct sbus_connection *conn, struct sbus_interface *intf,
DBusMessage *message);
+struct sbus_introspect_ctx {
+ const struct sbus_interface_meta *iface;
+};
+
+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 b8cd4abb0..10f0d6577 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;
}