1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "sd-bus-vtable.h"
7 #include "bus-internal.h"
8 #include "bus-introspect.h"
9 #include "bus-objects.h"
10 #include "bus-signature.h"
11 #include "memstream-util.h"
12 #include "ordered-set.h"
13 #include "string-util.h"
15 #define BUS_INTROSPECT_DOCTYPE \
16 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
17 "\"https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
19 #define BUS_INTROSPECT_INTERFACE_PEER \
20 " <interface name=\"org.freedesktop.DBus.Peer\">\n" \
21 " <method name=\"Ping\"/>\n" \
22 " <method name=\"GetMachineId\">\n" \
23 " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
27 #define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \
28 " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \
29 " <method name=\"Introspect\">\n" \
30 " <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n" \
34 #define BUS_INTROSPECT_INTERFACE_PROPERTIES \
35 " <interface name=\"org.freedesktop.DBus.Properties\">\n" \
36 " <method name=\"Get\">\n" \
37 " <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n" \
38 " <arg name=\"property_name\" direction=\"in\" type=\"s\"/>\n" \
39 " <arg name=\"value\" direction=\"out\" type=\"v\"/>\n" \
41 " <method name=\"GetAll\">\n" \
42 " <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n" \
43 " <arg name=\"props\" direction=\"out\" type=\"a{sv}\"/>\n" \
45 " <method name=\"Set\">\n" \
46 " <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n" \
47 " <arg name=\"property_name\" direction=\"in\" type=\"s\"/>\n" \
48 " <arg name=\"value\" direction=\"in\" type=\"v\"/>\n" \
50 " <signal name=\"PropertiesChanged\">\n" \
51 " <arg type=\"s\" name=\"interface_name\"/>\n" \
52 " <arg type=\"a{sv}\" name=\"changed_properties\"/>\n" \
53 " <arg type=\"as\" name=\"invalidated_properties\"/>\n" \
57 #define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \
58 " <interface name=\"org.freedesktop.DBus.ObjectManager\">\n" \
59 " <method name=\"GetManagedObjects\">\n" \
60 " <arg type=\"a{oa{sa{sv}}}\" name=\"object_paths_interfaces_and_properties\" direction=\"out\"/>\n" \
62 " <signal name=\"InterfacesAdded\">\n" \
63 " <arg type=\"o\" name=\"object_path\"/>\n" \
64 " <arg type=\"a{sa{sv}}\" name=\"interfaces_and_properties\"/>\n" \
66 " <signal name=\"InterfacesRemoved\">\n" \
67 " <arg type=\"o\" name=\"object_path\"/>\n" \
68 " <arg type=\"as\" name=\"interfaces\"/>\n" \
72 int introspect_begin(BusIntrospect
*i
, bool trusted
) {
77 *i
= (BusIntrospect
) {
81 f
= memstream_init(&i
->m
);
85 fputs(BUS_INTROSPECT_DOCTYPE
91 int introspect_write_default_interfaces(BusIntrospect
*i
, bool object_manager
) {
95 fputs(BUS_INTROSPECT_INTERFACE_PEER
96 BUS_INTROSPECT_INTERFACE_INTROSPECTABLE
97 BUS_INTROSPECT_INTERFACE_PROPERTIES
, i
->m
.f
);
100 fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER
, i
->m
.f
);
105 static int set_interface_name(BusIntrospect
*i
, const char *interface_name
) {
109 if (streq_ptr(i
->interface_name
, interface_name
))
112 if (i
->interface_name
)
113 fputs(" </interface>\n", i
->m
.f
);
116 fprintf(i
->m
.f
, " <interface name=\"%s\">\n", interface_name
);
118 return free_and_strdup(&i
->interface_name
, interface_name
);
121 int introspect_write_child_nodes(BusIntrospect
*i
, OrderedSet
*s
, const char *prefix
) {
128 assert_se(set_interface_name(i
, NULL
) >= 0);
130 while ((node
= ordered_set_steal_first(s
))) {
133 e
= object_path_startswith(node
, prefix
);
135 fprintf(i
->m
.f
, " <node name=\"%s\"/>\n", e
);
143 static void introspect_write_flags(BusIntrospect
*i
, int type
, uint64_t flags
) {
147 if (flags
& SD_BUS_VTABLE_DEPRECATED
)
148 fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i
->m
.f
);
150 if (type
== _SD_BUS_VTABLE_METHOD
&& (flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
))
151 fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i
->m
.f
);
153 if (IN_SET(type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
)) {
154 if (flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)
155 fputs(" <annotation name=\"org.freedesktop.systemd1.Explicit\" value=\"true\"/>\n", i
->m
.f
);
157 if (flags
& SD_BUS_VTABLE_PROPERTY_CONST
)
158 fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i
->m
.f
);
159 else if (flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)
160 fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i
->m
.f
);
161 else if (!(flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
162 fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i
->m
.f
);
166 IN_SET(type
, _SD_BUS_VTABLE_METHOD
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
) &&
167 !(flags
& SD_BUS_VTABLE_UNPRIVILEGED
))
168 fputs(" <annotation name=\"org.freedesktop.systemd1.Privileged\" value=\"true\"/>\n", i
->m
.f
);
171 /* Note that "names" is both an input and an output parameter. It initially points to the first argument name in a
172 NULL-separated list of strings, and is then advanced with each argument, and the resulting pointer is returned. */
173 static int introspect_write_arguments(BusIntrospect
*i
, const char *signature
, const char **names
, const char *direction
) {
185 r
= signature_element_length(signature
, &l
);
189 fprintf(i
->m
.f
, " <arg type=\"%.*s\"", (int) l
, signature
);
191 if (**names
!= '\0') {
192 fprintf(i
->m
.f
, " name=\"%s\"", *names
);
193 *names
+= strlen(*names
) + 1;
197 fprintf(i
->m
.f
, " direction=\"%s\"/>\n", direction
);
199 fputs("/>\n", i
->m
.f
);
205 int introspect_write_interface(
207 const char *interface_name
,
208 const sd_bus_vtable
*v
) {
210 const sd_bus_vtable
*vtable
= ASSERT_PTR(v
);
211 const char *names
= "";
216 assert(interface_name
);
218 r
= set_interface_name(i
, interface_name
);
222 for (; v
->type
!= _SD_BUS_VTABLE_END
; v
= bus_vtable_next(vtable
, v
)) {
224 /* Ignore methods, signals and properties that are
225 * marked "hidden", but do show the interface
228 if (v
->type
!= _SD_BUS_VTABLE_START
&& (v
->flags
& SD_BUS_VTABLE_HIDDEN
))
233 case _SD_BUS_VTABLE_START
:
234 if (v
->flags
& SD_BUS_VTABLE_DEPRECATED
)
235 fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i
->m
.f
);
238 case _SD_BUS_VTABLE_METHOD
:
239 fprintf(i
->m
.f
, " <method name=\"%s\">\n", v
->x
.method
.member
);
240 if (bus_vtable_has_names(vtable
))
241 names
= strempty(v
->x
.method
.names
);
242 introspect_write_arguments(i
, strempty(v
->x
.method
.signature
), &names
, "in");
243 introspect_write_arguments(i
, strempty(v
->x
.method
.result
), &names
, "out");
244 introspect_write_flags(i
, v
->type
, v
->flags
);
245 fputs(" </method>\n", i
->m
.f
);
248 case _SD_BUS_VTABLE_PROPERTY
:
249 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
250 fprintf(i
->m
.f
, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n",
251 v
->x
.property
.member
,
252 v
->x
.property
.signature
,
253 v
->type
== _SD_BUS_VTABLE_WRITABLE_PROPERTY
? "readwrite" : "read");
254 introspect_write_flags(i
, v
->type
, v
->flags
);
255 fputs(" </property>\n", i
->m
.f
);
258 case _SD_BUS_VTABLE_SIGNAL
:
259 fprintf(i
->m
.f
, " <signal name=\"%s\">\n", v
->x
.signal
.member
);
260 if (bus_vtable_has_names(vtable
))
261 names
= strempty(v
->x
.signal
.names
);
262 introspect_write_arguments(i
, strempty(v
->x
.signal
.signature
), &names
, NULL
);
263 introspect_write_flags(i
, v
->type
, v
->flags
);
264 fputs(" </signal>\n", i
->m
.f
);
273 int introspect_finish(BusIntrospect
*i
, char **ret
) {
277 assert_se(set_interface_name(i
, NULL
) >= 0);
279 fputs("</node>\n", i
->m
.f
);
281 return memstream_finalize(&i
->m
, ret
, NULL
);
284 void introspect_done(BusIntrospect
*i
) {
287 /* Normally introspect_finish() does all the work, this is just a backup for error paths */
289 memstream_done(&i
->m
);
290 free(i
->interface_name
);