summaryrefslogtreecommitdiffstats
path: root/src
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 /src
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)
Diffstat (limited to 'src')
-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
5 files changed, 420 insertions, 3 deletions
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;
}