]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-introspect.c
bus-introspect: write <interface> from within introspect_write_interface()
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-introspect.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
29ddb38f 2
29ddb38f 3#include "bus-internal.h"
3ffd4af2 4#include "bus-introspect.h"
856ad2a8 5#include "bus-objects.h"
0461f8cd 6#include "bus-protocol.h"
07630cea 7#include "bus-signature.h"
3ffd4af2 8#include "fd-util.h"
0d39fa9c 9#include "fileio.h"
0a970718 10#include "memory-util.h"
07630cea 11#include "string-util.h"
29ddb38f 12
7fb411f0 13int introspect_begin(struct introspect *i, bool trusted) {
29ddb38f
LP
14 assert(i);
15
41ab8c67
LP
16 *i = (struct introspect) {
17 .trusted = trusted,
18 };
29ddb38f 19
2fe21124 20 i->f = open_memstream_unlocked(&i->introspection, &i->size);
29ddb38f
LP
21 if (!i->f)
22 return -ENOMEM;
23
0d536673
LP
24 fputs(BUS_INTROSPECT_DOCTYPE
25 "<node>\n", i->f);
29ddb38f
LP
26
27 return 0;
28}
29
30int introspect_write_default_interfaces(struct introspect *i, bool object_manager) {
31 assert(i);
32
0d536673
LP
33 fputs(BUS_INTROSPECT_INTERFACE_PEER
34 BUS_INTROSPECT_INTERFACE_INTROSPECTABLE
35 BUS_INTROSPECT_INTERFACE_PROPERTIES, i->f);
29ddb38f
LP
36
37 if (object_manager)
0d536673 38 fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->f);
29ddb38f
LP
39
40 return 0;
41}
42
61d0df39
ZJS
43static int set_interface_name(struct introspect *intro, const char *interface_name) {
44 if (streq_ptr(intro->interface_name, interface_name))
45 return 0;
46
47 if (intro->interface_name)
48 fputs(" </interface>\n", intro->f);
49
50 if (interface_name)
51 fprintf(intro->f, " <interface name=\"%s\">\n", interface_name);
52
53 return free_and_strdup(&intro->interface_name, interface_name);
54}
55
29ddb38f
LP
56int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) {
57 char *node;
58
59 assert(i);
60 assert(prefix);
61
61d0df39
ZJS
62 assert_se(set_interface_name(i, NULL) >= 0);
63
29ddb38f
LP
64 while ((node = set_steal_first(s))) {
65 const char *e;
66
67 e = object_path_startswith(node, prefix);
dd8243a3 68 if (e && e[0])
29ddb38f
LP
69 fprintf(i->f, " <node name=\"%s\"/>\n", e);
70
71 free(node);
72 }
73
74 return 0;
75}
76
affaed1e 77static void introspect_write_flags(struct introspect *i, int type, uint64_t flags) {
29ddb38f 78 if (flags & SD_BUS_VTABLE_DEPRECATED)
0d536673 79 fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
29ddb38f 80
adacb957 81 if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY))
0d536673 82 fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i->f);
29ddb38f 83
945c2931 84 if (IN_SET(type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY)) {
33702051 85 if (flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
0d536673 86 fputs(" <annotation name=\"org.freedesktop.systemd1.Explicit\" value=\"true\"/>\n", i->f);
33702051 87
df98a87b 88 if (flags & SD_BUS_VTABLE_PROPERTY_CONST)
0d536673 89 fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i->f);
df98a87b 90 else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)
0d536673 91 fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i->f);
df98a87b 92 else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
0d536673 93 fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i->f);
29ddb38f 94 }
adacb957 95
7fb411f0 96 if (!i->trusted &&
945c2931 97 IN_SET(type, _SD_BUS_VTABLE_METHOD, _SD_BUS_VTABLE_WRITABLE_PROPERTY) &&
7fb411f0 98 !(flags & SD_BUS_VTABLE_UNPRIVILEGED))
0d536673 99 fputs(" <annotation name=\"org.freedesktop.systemd1.Privileged\" value=\"true\"/>\n", i->f);
29ddb38f
LP
100}
101
856ad2a8
GC
102/* Note that "names" is both an input and an output parameter. It initially points to the first argument name in a
103 NULL-separated list of strings, and is then advanced with each argument, and the resulting pointer is returned. */
104static int introspect_write_arguments(struct introspect *i, const char *signature, const char **names, const char *direction) {
29ddb38f
LP
105 int r;
106
107 for (;;) {
108 size_t l;
109
110 if (!*signature)
111 return 0;
112
113 r = signature_element_length(signature, &l);
114 if (r < 0)
115 return r;
116
117 fprintf(i->f, " <arg type=\"%.*s\"", (int) l, signature);
118
856ad2a8
GC
119 if (**names != '\0') {
120 fprintf(i->f, " name=\"%s\"", *names);
121 *names += strlen(*names) + 1;
122 }
123
29ddb38f 124 if (direction)
6014597d 125 fprintf(i->f, " direction=\"%s\"/>\n", direction);
29ddb38f 126 else
0d536673 127 fputs("/>\n", i->f);
29ddb38f
LP
128
129 signature += l;
130 }
131}
132
61d0df39
ZJS
133int introspect_write_interface(
134 struct introspect *i,
135 const char *interface_name,
136 const sd_bus_vtable *v) {
137
856ad2a8
GC
138 const sd_bus_vtable *vtable = v;
139 const char *names = "";
61d0df39 140 int r;
856ad2a8 141
29ddb38f 142 assert(i);
61d0df39 143 assert(interface_name);
29ddb38f
LP
144 assert(v);
145
61d0df39
ZJS
146 r = set_interface_name(i, interface_name);
147 if (r < 0)
148 return r;
149
856ad2a8 150 for (; v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) {
29ddb38f 151
7fb411f0
LP
152 /* Ignore methods, signals and properties that are
153 * marked "hidden", but do show the interface
154 * itself */
155
6e8df5f0
LP
156 if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN))
157 continue;
158
29ddb38f
LP
159 switch (v->type) {
160
161 case _SD_BUS_VTABLE_START:
162 if (v->flags & SD_BUS_VTABLE_DEPRECATED)
0d536673 163 fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
29ddb38f
LP
164 break;
165
166 case _SD_BUS_VTABLE_METHOD:
77a874a3 167 fprintf(i->f, " <method name=\"%s\">\n", v->x.method.member);
856ad2a8
GC
168 if (bus_vtable_has_names(vtable))
169 names = strempty(v->x.method.names);
170 introspect_write_arguments(i, strempty(v->x.method.signature), &names, "in");
171 introspect_write_arguments(i, strempty(v->x.method.result), &names, "out");
29ddb38f 172 introspect_write_flags(i, v->type, v->flags);
0d536673 173 fputs(" </method>\n", i->f);
29ddb38f
LP
174 break;
175
176 case _SD_BUS_VTABLE_PROPERTY:
177 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
178 fprintf(i->f, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n",
77a874a3
LP
179 v->x.property.member,
180 v->x.property.signature,
29ddb38f
LP
181 v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read");
182 introspect_write_flags(i, v->type, v->flags);
0d536673 183 fputs(" </property>\n", i->f);
29ddb38f
LP
184 break;
185
186 case _SD_BUS_VTABLE_SIGNAL:
77a874a3 187 fprintf(i->f, " <signal name=\"%s\">\n", v->x.signal.member);
856ad2a8 188 if (bus_vtable_has_names(vtable))
58abbbcc 189 names = strempty(v->x.signal.names);
856ad2a8 190 introspect_write_arguments(i, strempty(v->x.signal.signature), &names, NULL);
29ddb38f 191 introspect_write_flags(i, v->type, v->flags);
0d536673 192 fputs(" </signal>\n", i->f);
29ddb38f
LP
193 break;
194 }
195
196 }
197
29ddb38f
LP
198 return 0;
199}
200
dff9e25a 201int introspect_finish(struct introspect *i, char **ret) {
29ddb38f
LP
202 int r;
203
204 assert(i);
29ddb38f 205
61d0df39
ZJS
206 assert_se(set_interface_name(i, NULL) >= 0);
207
0d536673 208 fputs("</node>\n", i->f);
29ddb38f 209
dacd6cee
LP
210 r = fflush_and_check(i->f);
211 if (r < 0)
212 return r;
29ddb38f 213
dff9e25a
ZJS
214 i->f = safe_fclose(i->f);
215 *ret = TAKE_PTR(i->introspection);
29ddb38f 216
29ddb38f
LP
217 return 0;
218}
219
220void introspect_free(struct introspect *i) {
221 assert(i);
222
dff9e25a 223 /* Normally introspect_finish() does all the work, this is just a backup for error paths */
29ddb38f 224
dff9e25a 225 safe_fclose(i->f);
61d0df39 226 free(i->interface_name);
dacd6cee 227 free(i->introspection);
29ddb38f 228}