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