>From 50a07ca1556c0ab97b32bbd0ab2342dce1ef374e Mon Sep 17 00:00:00 2001 From: Jakub Hrozek 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 @@ + + 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 + + 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 . +*/ + +#include +#include + +#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 \ + "\n" + +#define SSS_INTROSPECT_INTERFACE_INTROSPECTABLE \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \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("\n", ictx->f); + if (ret < 0) return EIO; + + ret = fprintf(ictx->f, " \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, + " 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, " \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(" \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, " \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(" \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, " \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(" \n", ictx->f); + if (ret < 0) return EIO; + + ret = fputs(SSS_INTROSPECT_INTERFACE_INTROSPECTABLE, ictx->f); + if (ret < 0) return EIO; + + ret = fputs("\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 \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + "\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