1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "bus-internal.h"
4 #include "bus-introspect.h"
5 #include "bus-objects.h"
6 #include "bus-protocol.h"
7 #include "bus-signature.h"
10 #include "memory-util.h"
11 #include "string-util.h"
13 #define BUS_INTROSPECT_DOCTYPE \
14 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
15 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
17 #define BUS_INTROSPECT_INTERFACE_PEER \
18 " <interface name=\"org.freedesktop.DBus.Peer\">\n" \
19 " <method name=\"Ping\"/>\n" \
20 " <method name=\"GetMachineId\">\n" \
21 " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
25 #define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \
26 " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \
27 " <method name=\"Introspect\">\n" \
28 " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
32 #define BUS_INTROSPECT_INTERFACE_PROPERTIES \
33 " <interface name=\"org.freedesktop.DBus.Properties\">\n" \
34 " <method name=\"Get\">\n" \
35 " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
36 " <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
37 " <arg name=\"value\" direction=\"out\" type=\"v\"/>\n" \
39 " <method name=\"GetAll\">\n" \
40 " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
41 " <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n" \
43 " <method name=\"Set\">\n" \
44 " <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n" \
45 " <arg name=\"property\" direction=\"in\" type=\"s\"/>\n" \
46 " <arg name=\"value\" direction=\"in\" type=\"v\"/>\n" \
48 " <signal name=\"PropertiesChanged\">\n" \
49 " <arg type=\"s\" name=\"interface\"/>\n" \
50 " <arg type=\"a{sv}\" name=\"changed_properties\"/>\n" \
51 " <arg type=\"as\" name=\"invalidated_properties\"/>\n" \
55 #define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \
56 " <interface name=\"org.freedesktop.DBus.ObjectManager\">\n" \
57 " <method name=\"GetManagedObjects\">\n" \
58 " <arg type=\"a{oa{sa{sv}}}\" name=\"object_paths_interfaces_and_properties\" direction=\"out\"/>\n" \
60 " <signal name=\"InterfacesAdded\">\n" \
61 " <arg type=\"o\" name=\"object_path\"/>\n" \
62 " <arg type=\"a{sa{sv}}\" name=\"interfaces_and_properties\"/>\n" \
64 " <signal name=\"InterfacesRemoved\">\n" \
65 " <arg type=\"o\" name=\"object_path\"/>\n" \
66 " <arg type=\"as\" name=\"interfaces\"/>\n" \
70 int introspect_begin(struct introspect
*i
, bool trusted
) {
73 *i
= (struct introspect
) {
77 i
->f
= open_memstream_unlocked(&i
->introspection
, &i
->size
);
81 fputs(BUS_INTROSPECT_DOCTYPE
87 int introspect_write_default_interfaces(struct introspect
*i
, bool object_manager
) {
90 fputs(BUS_INTROSPECT_INTERFACE_PEER
91 BUS_INTROSPECT_INTERFACE_INTROSPECTABLE
92 BUS_INTROSPECT_INTERFACE_PROPERTIES
, i
->f
);
95 fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER
, i
->f
);
100 static int set_interface_name(struct introspect
*intro
, const char *interface_name
) {
101 if (streq_ptr(intro
->interface_name
, interface_name
))
104 if (intro
->interface_name
)
105 fputs(" </interface>\n", intro
->f
);
108 fprintf(intro
->f
, " <interface name=\"%s\">\n", interface_name
);
110 return free_and_strdup(&intro
->interface_name
, interface_name
);
113 int introspect_write_child_nodes(struct introspect
*i
, Set
*s
, const char *prefix
) {
119 assert_se(set_interface_name(i
, NULL
) >= 0);
121 while ((node
= set_steal_first(s
))) {
124 e
= object_path_startswith(node
, prefix
);
126 fprintf(i
->f
, " <node name=\"%s\"/>\n", e
);
134 static void introspect_write_flags(struct introspect
*i
, int type
, uint64_t flags
) {
135 if (flags
& SD_BUS_VTABLE_DEPRECATED
)
136 fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i
->f
);
138 if (type
== _SD_BUS_VTABLE_METHOD
&& (flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
))
139 fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i
->f
);
141 if (IN_SET(type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
)) {
142 if (flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)
143 fputs(" <annotation name=\"org.freedesktop.systemd1.Explicit\" value=\"true\"/>\n", i
->f
);
145 if (flags
& SD_BUS_VTABLE_PROPERTY_CONST
)
146 fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i
->f
);
147 else if (flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)
148 fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i
->f
);
149 else if (!(flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
150 fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i
->f
);
154 IN_SET(type
, _SD_BUS_VTABLE_METHOD
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
) &&
155 !(flags
& SD_BUS_VTABLE_UNPRIVILEGED
))
156 fputs(" <annotation name=\"org.freedesktop.systemd1.Privileged\" value=\"true\"/>\n", i
->f
);
159 /* Note that "names" is both an input and an output parameter. It initially points to the first argument name in a
160 NULL-separated list of strings, and is then advanced with each argument, and the resulting pointer is returned. */
161 static int introspect_write_arguments(struct introspect
*i
, const char *signature
, const char **names
, const char *direction
) {
170 r
= signature_element_length(signature
, &l
);
174 fprintf(i
->f
, " <arg type=\"%.*s\"", (int) l
, signature
);
176 if (**names
!= '\0') {
177 fprintf(i
->f
, " name=\"%s\"", *names
);
178 *names
+= strlen(*names
) + 1;
182 fprintf(i
->f
, " direction=\"%s\"/>\n", direction
);
190 int introspect_write_interface(
191 struct introspect
*i
,
192 const char *interface_name
,
193 const sd_bus_vtable
*v
) {
195 const sd_bus_vtable
*vtable
= v
;
196 const char *names
= "";
200 assert(interface_name
);
203 r
= set_interface_name(i
, interface_name
);
207 for (; v
->type
!= _SD_BUS_VTABLE_END
; v
= bus_vtable_next(vtable
, v
)) {
209 /* Ignore methods, signals and properties that are
210 * marked "hidden", but do show the interface
213 if (v
->type
!= _SD_BUS_VTABLE_START
&& (v
->flags
& SD_BUS_VTABLE_HIDDEN
))
218 case _SD_BUS_VTABLE_START
:
219 if (v
->flags
& SD_BUS_VTABLE_DEPRECATED
)
220 fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i
->f
);
223 case _SD_BUS_VTABLE_METHOD
:
224 fprintf(i
->f
, " <method name=\"%s\">\n", v
->x
.method
.member
);
225 if (bus_vtable_has_names(vtable
))
226 names
= strempty(v
->x
.method
.names
);
227 introspect_write_arguments(i
, strempty(v
->x
.method
.signature
), &names
, "in");
228 introspect_write_arguments(i
, strempty(v
->x
.method
.result
), &names
, "out");
229 introspect_write_flags(i
, v
->type
, v
->flags
);
230 fputs(" </method>\n", i
->f
);
233 case _SD_BUS_VTABLE_PROPERTY
:
234 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
235 fprintf(i
->f
, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n",
236 v
->x
.property
.member
,
237 v
->x
.property
.signature
,
238 v
->type
== _SD_BUS_VTABLE_WRITABLE_PROPERTY
? "readwrite" : "read");
239 introspect_write_flags(i
, v
->type
, v
->flags
);
240 fputs(" </property>\n", i
->f
);
243 case _SD_BUS_VTABLE_SIGNAL
:
244 fprintf(i
->f
, " <signal name=\"%s\">\n", v
->x
.signal
.member
);
245 if (bus_vtable_has_names(vtable
))
246 names
= strempty(v
->x
.signal
.names
);
247 introspect_write_arguments(i
, strempty(v
->x
.signal
.signature
), &names
, NULL
);
248 introspect_write_flags(i
, v
->type
, v
->flags
);
249 fputs(" </signal>\n", i
->f
);
258 int introspect_finish(struct introspect
*i
, char **ret
) {
263 assert_se(set_interface_name(i
, NULL
) >= 0);
265 fputs("</node>\n", i
->f
);
267 r
= fflush_and_check(i
->f
);
271 i
->f
= safe_fclose(i
->f
);
272 *ret
= TAKE_PTR(i
->introspection
);
277 void introspect_free(struct introspect
*i
) {
280 /* Normally introspect_finish() does all the work, this is just a backup for error paths */
283 free(i
->interface_name
);
284 free(i
->introspection
);