>From d0d89638cd384a79f499ee523c2394dc63229c24 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 | 35 ++- src/sbus/sssd_dbus_introspect.c | 330 +++++++++++++++++++++ src/sbus/sssd_dbus_private.h | 6 + src/tests/sbus_tests.c | 49 +++ 6 files changed, 421 insertions(+), 3 deletions(-) create mode 100644 src/sbus/sssd_dbus_introspect.c diff --git a/Makefile.am b/Makefile.am index ace2b6528bc11764f6600ac7278970fe07ed7385..de86d2bc1de10cd2e6722ce7c45f642787d32f23 100644 --- a/Makefile.am +++ b/Makefile.am @@ -598,6 +598,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..64777fbe815c78d8c893ee1a3e8f5ab327c6701e 100644 --- a/src/sbus/sssd_dbus_connection.c +++ b/src/sbus/sssd_dbus_connection.c @@ -378,6 +378,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; @@ -425,19 +427,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 0000000000000000000000000000000000000000..ffc1f4d38ba70f9074d6731280136e899374c866 --- /dev/null +++ b/src/sbus/sssd_dbus_introspect.c @@ -0,0 +1,330 @@ +/* + 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; + 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 88230cfca4527c326c05efbe052d04b15dd00e88..929caeccbcc0aca2e42fc44eacf3603826cbe329 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 e4cab454dbb892593f5faa98d1c1a609d96128a0..194ba60ef4c128a628e07e45ca33a922136d31bf 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