chiark / gitweb /
c95778673b17f077586e94b708ca96218b9b23fd
[elogind.git] / src / libelogind / sd-bus / bus-introspect.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   Copyright 2013 Lennart Poettering
4 ***/
5
6 #include <stdio_ext.h>
7
8 #include "bus-internal.h"
9 #include "bus-introspect.h"
10 #include "bus-protocol.h"
11 #include "bus-signature.h"
12 #include "fd-util.h"
13 #include "fileio.h"
14 #include "string-util.h"
15 #include "util.h"
16
17 int introspect_begin(struct introspect *i, bool trusted) {
18         assert(i);
19
20         zero(*i);
21         i->trusted = trusted;
22
23         i->f = open_memstream(&i->introspection, &i->size);
24         if (!i->f)
25                 return -ENOMEM;
26
27         (void) __fsetlocking(i->f, FSETLOCKING_BYCALLER);
28
29         fputs(BUS_INTROSPECT_DOCTYPE
30               "<node>\n", i->f);
31
32         return 0;
33 }
34
35 int introspect_write_default_interfaces(struct introspect *i, bool object_manager) {
36         assert(i);
37
38         fputs(BUS_INTROSPECT_INTERFACE_PEER
39               BUS_INTROSPECT_INTERFACE_INTROSPECTABLE
40               BUS_INTROSPECT_INTERFACE_PROPERTIES, i->f);
41
42         if (object_manager)
43                 fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->f);
44
45         return 0;
46 }
47
48 int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) {
49         char *node;
50
51         assert(i);
52         assert(prefix);
53
54         while ((node = set_steal_first(s))) {
55                 const char *e;
56
57                 e = object_path_startswith(node, prefix);
58                 if (e && e[0])
59                         fprintf(i->f, " <node name=\"%s\"/>\n", e);
60
61                 free(node);
62         }
63
64         return 0;
65 }
66
67 static void introspect_write_flags(struct introspect *i, int type, int flags) {
68         if (flags & SD_BUS_VTABLE_DEPRECATED)
69                 fputs("   <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
70
71         if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY))
72                 fputs("   <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i->f);
73
74         if (IN_SET(type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY)) {
75                 if (flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
76                         fputs("   <annotation name=\"org.freedesktop.elogind1.Explicit\" value=\"true\"/>\n", i->f);
77
78                 if (flags & SD_BUS_VTABLE_PROPERTY_CONST)
79                         fputs("   <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i->f);
80                 else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)
81                         fputs("   <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i->f);
82                 else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
83                         fputs("   <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i->f);
84         }
85
86         if (!i->trusted &&
87             IN_SET(type, _SD_BUS_VTABLE_METHOD, _SD_BUS_VTABLE_WRITABLE_PROPERTY) &&
88             !(flags & SD_BUS_VTABLE_UNPRIVILEGED))
89                 fputs("   <annotation name=\"org.freedesktop.elogind1.Privileged\" value=\"true\"/>\n", i->f);
90 }
91
92 static int introspect_write_arguments(struct introspect *i, const char *signature, const char *direction) {
93         int r;
94
95         for (;;) {
96                 size_t l;
97
98                 if (!*signature)
99                         return 0;
100
101                 r = signature_element_length(signature, &l);
102                 if (r < 0)
103                         return r;
104
105                 fprintf(i->f, "   <arg type=\"%.*s\"", (int) l, signature);
106
107                 if (direction)
108                         fprintf(i->f, " direction=\"%s\"/>\n", direction);
109                 else
110                         fputs("/>\n", i->f);
111
112                 signature += l;
113         }
114 }
115
116 int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) {
117         assert(i);
118         assert(v);
119
120         for (; v->type != _SD_BUS_VTABLE_END; v++) {
121
122                 /* Ignore methods, signals and properties that are
123                  * marked "hidden", but do show the interface
124                  * itself */
125
126                 if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN))
127                         continue;
128
129                 switch (v->type) {
130
131                 case _SD_BUS_VTABLE_START:
132                         if (v->flags & SD_BUS_VTABLE_DEPRECATED)
133                                 fputs("  <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
134                         break;
135
136                 case _SD_BUS_VTABLE_METHOD:
137                         fprintf(i->f, "  <method name=\"%s\">\n", v->x.method.member);
138                         introspect_write_arguments(i, strempty(v->x.method.signature), "in");
139                         introspect_write_arguments(i, strempty(v->x.method.result), "out");
140                         introspect_write_flags(i, v->type, v->flags);
141                         fputs("  </method>\n", i->f);
142                         break;
143
144                 case _SD_BUS_VTABLE_PROPERTY:
145                 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
146                         fprintf(i->f, "  <property name=\"%s\" type=\"%s\" access=\"%s\">\n",
147                                 v->x.property.member,
148                                 v->x.property.signature,
149                                 v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read");
150                         introspect_write_flags(i, v->type, v->flags);
151                         fputs("  </property>\n", i->f);
152                         break;
153
154                 case _SD_BUS_VTABLE_SIGNAL:
155                         fprintf(i->f, "  <signal name=\"%s\">\n", v->x.signal.member);
156                         introspect_write_arguments(i, strempty(v->x.signal.signature), NULL);
157                         introspect_write_flags(i, v->type, v->flags);
158                         fputs("  </signal>\n", i->f);
159                         break;
160                 }
161
162         }
163
164         return 0;
165 }
166
167 int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply) {
168         sd_bus_message *q;
169         int r;
170
171         assert(i);
172         assert(m);
173         assert(reply);
174
175         fputs("</node>\n", i->f);
176
177         r = fflush_and_check(i->f);
178         if (r < 0)
179                 return r;
180
181         r = sd_bus_message_new_method_return(m, &q);
182         if (r < 0)
183                 return r;
184
185         r = sd_bus_message_append(q, "s", i->introspection);
186         if (r < 0) {
187                 sd_bus_message_unref(q);
188                 return r;
189         }
190
191         *reply = q;
192         return 0;
193 }
194
195 void introspect_free(struct introspect *i) {
196         assert(i);
197
198         safe_fclose(i->f);
199
200         free(i->introspection);
201         zero(*i);
202 }