| /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
| |
| /*** |
| This file is part of systemd. |
| |
| Copyright 2013 Lennart Poettering |
| |
| systemd is free software; you can redistribute it and/or modify it |
| under the terms of the GNU Lesser General Public License as published by |
| the Free Software Foundation; either version 2.1 of the License, or |
| (at your option) any later version. |
| |
| systemd 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 |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with systemd; If not, see <http://www.gnu.org/licenses/>. |
| ***/ |
| |
| #include "util.h" |
| #include "bus-introspect.h" |
| #include "bus-signature.h" |
| #include "bus-internal.h" |
| #include "bus-protocol.h" |
| |
| int introspect_begin(struct introspect *i, bool trusted) { |
| assert(i); |
| |
| zero(*i); |
| i->trusted = trusted; |
| |
| i->f = open_memstream(&i->introspection, &i->size); |
| if (!i->f) |
| return -ENOMEM; |
| |
| fputs(BUS_INTROSPECT_DOCTYPE |
| "<node>\n", i->f); |
| |
| return 0; |
| } |
| |
| int introspect_write_default_interfaces(struct introspect *i, bool object_manager) { |
| assert(i); |
| |
| fputs(BUS_INTROSPECT_INTERFACE_PEER |
| BUS_INTROSPECT_INTERFACE_INTROSPECTABLE |
| BUS_INTROSPECT_INTERFACE_PROPERTIES, i->f); |
| |
| if (object_manager) |
| fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->f); |
| |
| return 0; |
| } |
| |
| int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) { |
| char *node; |
| |
| assert(i); |
| assert(prefix); |
| |
| while ((node = set_steal_first(s))) { |
| const char *e; |
| |
| e = object_path_startswith(node, prefix); |
| if (e && e[0]) |
| fprintf(i->f, " <node name=\"%s\"/>\n", e); |
| |
| free(node); |
| } |
| |
| return 0; |
| } |
| |
| static void introspect_write_flags(struct introspect *i, int type, int flags) { |
| if (flags & SD_BUS_VTABLE_DEPRECATED) |
| fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f); |
| |
| if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY)) |
| fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i->f); |
| |
| if (type == _SD_BUS_VTABLE_PROPERTY || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) { |
| if (flags & SD_BUS_VTABLE_PROPERTY_CONST) |
| fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i->f); |
| else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) |
| fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i->f); |
| else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) |
| fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i->f); |
| } |
| |
| if (!i->trusted && |
| (type == _SD_BUS_VTABLE_METHOD || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) && |
| !(flags & SD_BUS_VTABLE_UNPRIVILEGED)) |
| fputs(" <annotation name=\"org.freedesktop.systemd1.Privileged\" value=\"true\"/>\n", i->f); |
| } |
| |
| static int introspect_write_arguments(struct introspect *i, const char *signature, const char *direction) { |
| int r; |
| |
| for (;;) { |
| size_t l; |
| |
| if (!*signature) |
| return 0; |
| |
| r = signature_element_length(signature, &l); |
| if (r < 0) |
| return r; |
| |
| fprintf(i->f, " <arg type=\"%.*s\"", (int) l, signature); |
| |
| if (direction) |
| fprintf(i->f, " direction=\"%s\"/>\n", direction); |
| else |
| fputs("/>\n", i->f); |
| |
| signature += l; |
| } |
| } |
| |
| int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) { |
| assert(i); |
| assert(v); |
| |
| for (; v->type != _SD_BUS_VTABLE_END; v++) { |
| |
| /* Ignore methods, signals and properties that are |
| * marked "hidden", but do show the interface |
| * itself */ |
| |
| if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN)) |
| continue; |
| |
| switch (v->type) { |
| |
| case _SD_BUS_VTABLE_START: |
| if (v->flags & SD_BUS_VTABLE_DEPRECATED) |
| fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f); |
| break; |
| |
| case _SD_BUS_VTABLE_METHOD: |
| fprintf(i->f, " <method name=\"%s\">\n", v->x.method.member); |
| introspect_write_arguments(i, strempty(v->x.method.signature), "in"); |
| introspect_write_arguments(i, strempty(v->x.method.result), "out"); |
| introspect_write_flags(i, v->type, v->flags); |
| fputs(" </method>\n", i->f); |
| break; |
| |
| case _SD_BUS_VTABLE_PROPERTY: |
| case _SD_BUS_VTABLE_WRITABLE_PROPERTY: |
| fprintf(i->f, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n", |
| v->x.property.member, |
| v->x.property.signature, |
| v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read"); |
| introspect_write_flags(i, v->type, v->flags); |
| fputs(" </property>\n", i->f); |
| break; |
| |
| case _SD_BUS_VTABLE_SIGNAL: |
| fprintf(i->f, " <signal name=\"%s\">\n", v->x.signal.member); |
| introspect_write_arguments(i, strempty(v->x.signal.signature), NULL); |
| introspect_write_flags(i, v->type, v->flags); |
| fputs(" </signal>\n", i->f); |
| break; |
| } |
| |
| } |
| |
| return 0; |
| } |
| |
| int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply) { |
| sd_bus_message *q; |
| int r; |
| |
| assert(i); |
| assert(m); |
| assert(reply); |
| |
| fputs("</node>\n", i->f); |
| fflush(i->f); |
| |
| if (ferror(i->f)) |
| return -ENOMEM; |
| |
| r = sd_bus_message_new_method_return(m, &q); |
| if (r < 0) |
| return r; |
| |
| r = sd_bus_message_append(q, "s", i->introspection); |
| if (r < 0) { |
| sd_bus_message_unref(q); |
| return r; |
| } |
| |
| *reply = q; |
| return 0; |
| } |
| |
| void introspect_free(struct introspect *i) { |
| assert(i); |
| |
| if (i->f) |
| fclose(i->f); |
| |
| if (i->introspection) |
| free(i->introspection); |
| |
| zero(*i); |
| } |