]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
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" |
2485b7e2 | 11 | #include "memstream-util.h" |
07630cea | 12 | #include "string-util.h" |
29ddb38f | 13 | |
2b6a1d15 ZJS |
14 | #define BUS_INTROSPECT_DOCTYPE \ |
15 | "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \ | |
41d6f3bf | 16 | "\"https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" |
2b6a1d15 ZJS |
17 | |
18 | #define BUS_INTROSPECT_INTERFACE_PEER \ | |
19 | " <interface name=\"org.freedesktop.DBus.Peer\">\n" \ | |
20 | " <method name=\"Ping\"/>\n" \ | |
21 | " <method name=\"GetMachineId\">\n" \ | |
22 | " <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \ | |
23 | " </method>\n" \ | |
24 | " </interface>\n" | |
25 | ||
26 | #define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \ | |
27 | " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \ | |
28 | " <method name=\"Introspect\">\n" \ | |
98503c6d | 29 | " <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n" \ |
2b6a1d15 ZJS |
30 | " </method>\n" \ |
31 | " </interface>\n" | |
32 | ||
33 | #define BUS_INTROSPECT_INTERFACE_PROPERTIES \ | |
34 | " <interface name=\"org.freedesktop.DBus.Properties\">\n" \ | |
35 | " <method name=\"Get\">\n" \ | |
98503c6d MK |
36 | " <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n" \ |
37 | " <arg name=\"property_name\" direction=\"in\" type=\"s\"/>\n" \ | |
2b6a1d15 ZJS |
38 | " <arg name=\"value\" direction=\"out\" type=\"v\"/>\n" \ |
39 | " </method>\n" \ | |
40 | " <method name=\"GetAll\">\n" \ | |
98503c6d MK |
41 | " <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n" \ |
42 | " <arg name=\"props\" direction=\"out\" type=\"a{sv}\"/>\n" \ | |
2b6a1d15 ZJS |
43 | " </method>\n" \ |
44 | " <method name=\"Set\">\n" \ | |
98503c6d MK |
45 | " <arg name=\"interface_name\" direction=\"in\" type=\"s\"/>\n" \ |
46 | " <arg name=\"property_name\" direction=\"in\" type=\"s\"/>\n" \ | |
2b6a1d15 ZJS |
47 | " <arg name=\"value\" direction=\"in\" type=\"v\"/>\n" \ |
48 | " </method>\n" \ | |
49 | " <signal name=\"PropertiesChanged\">\n" \ | |
98503c6d | 50 | " <arg type=\"s\" name=\"interface_name\"/>\n" \ |
2b6a1d15 ZJS |
51 | " <arg type=\"a{sv}\" name=\"changed_properties\"/>\n" \ |
52 | " <arg type=\"as\" name=\"invalidated_properties\"/>\n" \ | |
53 | " </signal>\n" \ | |
54 | " </interface>\n" | |
55 | ||
56 | #define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \ | |
57 | " <interface name=\"org.freedesktop.DBus.ObjectManager\">\n" \ | |
58 | " <method name=\"GetManagedObjects\">\n" \ | |
59 | " <arg type=\"a{oa{sa{sv}}}\" name=\"object_paths_interfaces_and_properties\" direction=\"out\"/>\n" \ | |
60 | " </method>\n" \ | |
61 | " <signal name=\"InterfacesAdded\">\n" \ | |
62 | " <arg type=\"o\" name=\"object_path\"/>\n" \ | |
63 | " <arg type=\"a{sa{sv}}\" name=\"interfaces_and_properties\"/>\n" \ | |
64 | " </signal>\n" \ | |
65 | " <signal name=\"InterfacesRemoved\">\n" \ | |
66 | " <arg type=\"o\" name=\"object_path\"/>\n" \ | |
67 | " <arg type=\"as\" name=\"interfaces\"/>\n" \ | |
68 | " </signal>\n" \ | |
69 | " </interface>\n" | |
70 | ||
7fb411f0 | 71 | int introspect_begin(struct introspect *i, bool trusted) { |
2485b7e2 YW |
72 | FILE *f; |
73 | ||
29ddb38f LP |
74 | assert(i); |
75 | ||
41ab8c67 LP |
76 | *i = (struct introspect) { |
77 | .trusted = trusted, | |
78 | }; | |
29ddb38f | 79 | |
2485b7e2 YW |
80 | f = memstream_init(&i->m); |
81 | if (!f) | |
29ddb38f LP |
82 | return -ENOMEM; |
83 | ||
0d536673 | 84 | fputs(BUS_INTROSPECT_DOCTYPE |
2485b7e2 | 85 | "<node>\n", f); |
29ddb38f LP |
86 | |
87 | return 0; | |
88 | } | |
89 | ||
90 | int introspect_write_default_interfaces(struct introspect *i, bool object_manager) { | |
91 | assert(i); | |
2485b7e2 | 92 | assert(i->m.f); |
29ddb38f | 93 | |
0d536673 LP |
94 | fputs(BUS_INTROSPECT_INTERFACE_PEER |
95 | BUS_INTROSPECT_INTERFACE_INTROSPECTABLE | |
2485b7e2 | 96 | BUS_INTROSPECT_INTERFACE_PROPERTIES, i->m.f); |
29ddb38f LP |
97 | |
98 | if (object_manager) | |
2485b7e2 | 99 | fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->m.f); |
29ddb38f LP |
100 | |
101 | return 0; | |
102 | } | |
103 | ||
2485b7e2 YW |
104 | static int set_interface_name(struct introspect *i, const char *interface_name) { |
105 | assert(i); | |
106 | assert(i->m.f); | |
107 | ||
108 | if (streq_ptr(i->interface_name, interface_name)) | |
61d0df39 ZJS |
109 | return 0; |
110 | ||
2485b7e2 YW |
111 | if (i->interface_name) |
112 | fputs(" </interface>\n", i->m.f); | |
61d0df39 ZJS |
113 | |
114 | if (interface_name) | |
2485b7e2 | 115 | fprintf(i->m.f, " <interface name=\"%s\">\n", interface_name); |
61d0df39 | 116 | |
2485b7e2 | 117 | return free_and_strdup(&i->interface_name, interface_name); |
61d0df39 ZJS |
118 | } |
119 | ||
acac8834 | 120 | int introspect_write_child_nodes(struct introspect *i, OrderedSet *s, const char *prefix) { |
29ddb38f LP |
121 | char *node; |
122 | ||
123 | assert(i); | |
2485b7e2 | 124 | assert(i->m.f); |
29ddb38f LP |
125 | assert(prefix); |
126 | ||
61d0df39 ZJS |
127 | assert_se(set_interface_name(i, NULL) >= 0); |
128 | ||
acac8834 | 129 | while ((node = ordered_set_steal_first(s))) { |
29ddb38f LP |
130 | const char *e; |
131 | ||
132 | e = object_path_startswith(node, prefix); | |
dd8243a3 | 133 | if (e && e[0]) |
2485b7e2 | 134 | fprintf(i->m.f, " <node name=\"%s\"/>\n", e); |
29ddb38f LP |
135 | |
136 | free(node); | |
137 | } | |
138 | ||
139 | return 0; | |
140 | } | |
141 | ||
affaed1e | 142 | static void introspect_write_flags(struct introspect *i, int type, uint64_t flags) { |
2485b7e2 YW |
143 | assert(i); |
144 | assert(i->m.f); | |
145 | ||
29ddb38f | 146 | if (flags & SD_BUS_VTABLE_DEPRECATED) |
2485b7e2 | 147 | fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->m.f); |
29ddb38f | 148 | |
adacb957 | 149 | if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY)) |
2485b7e2 | 150 | fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i->m.f); |
29ddb38f | 151 | |
945c2931 | 152 | if (IN_SET(type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY)) { |
33702051 | 153 | if (flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) |
2485b7e2 | 154 | fputs(" <annotation name=\"org.freedesktop.systemd1.Explicit\" value=\"true\"/>\n", i->m.f); |
33702051 | 155 | |
df98a87b | 156 | if (flags & SD_BUS_VTABLE_PROPERTY_CONST) |
2485b7e2 | 157 | fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i->m.f); |
df98a87b | 158 | else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) |
2485b7e2 | 159 | fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i->m.f); |
df98a87b | 160 | else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) |
2485b7e2 | 161 | fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i->m.f); |
29ddb38f | 162 | } |
adacb957 | 163 | |
7fb411f0 | 164 | if (!i->trusted && |
945c2931 | 165 | IN_SET(type, _SD_BUS_VTABLE_METHOD, _SD_BUS_VTABLE_WRITABLE_PROPERTY) && |
7fb411f0 | 166 | !(flags & SD_BUS_VTABLE_UNPRIVILEGED)) |
2485b7e2 | 167 | fputs(" <annotation name=\"org.freedesktop.systemd1.Privileged\" value=\"true\"/>\n", i->m.f); |
29ddb38f LP |
168 | } |
169 | ||
856ad2a8 GC |
170 | /* Note that "names" is both an input and an output parameter. It initially points to the first argument name in a |
171 | NULL-separated list of strings, and is then advanced with each argument, and the resulting pointer is returned. */ | |
172 | static int introspect_write_arguments(struct introspect *i, const char *signature, const char **names, const char *direction) { | |
29ddb38f LP |
173 | int r; |
174 | ||
2485b7e2 YW |
175 | assert(i); |
176 | assert(i->m.f); | |
177 | ||
29ddb38f LP |
178 | for (;;) { |
179 | size_t l; | |
180 | ||
181 | if (!*signature) | |
182 | return 0; | |
183 | ||
184 | r = signature_element_length(signature, &l); | |
185 | if (r < 0) | |
186 | return r; | |
187 | ||
2485b7e2 | 188 | fprintf(i->m.f, " <arg type=\"%.*s\"", (int) l, signature); |
29ddb38f | 189 | |
856ad2a8 | 190 | if (**names != '\0') { |
2485b7e2 | 191 | fprintf(i->m.f, " name=\"%s\"", *names); |
856ad2a8 GC |
192 | *names += strlen(*names) + 1; |
193 | } | |
194 | ||
29ddb38f | 195 | if (direction) |
2485b7e2 | 196 | fprintf(i->m.f, " direction=\"%s\"/>\n", direction); |
29ddb38f | 197 | else |
2485b7e2 | 198 | fputs("/>\n", i->m.f); |
29ddb38f LP |
199 | |
200 | signature += l; | |
201 | } | |
202 | } | |
203 | ||
61d0df39 ZJS |
204 | int introspect_write_interface( |
205 | struct introspect *i, | |
206 | const char *interface_name, | |
207 | const sd_bus_vtable *v) { | |
208 | ||
99534007 | 209 | const sd_bus_vtable *vtable = ASSERT_PTR(v); |
856ad2a8 | 210 | const char *names = ""; |
61d0df39 | 211 | int r; |
856ad2a8 | 212 | |
29ddb38f | 213 | assert(i); |
2485b7e2 | 214 | assert(i->m.f); |
61d0df39 | 215 | assert(interface_name); |
29ddb38f | 216 | |
61d0df39 ZJS |
217 | r = set_interface_name(i, interface_name); |
218 | if (r < 0) | |
219 | return r; | |
220 | ||
856ad2a8 | 221 | for (; v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) { |
29ddb38f | 222 | |
7fb411f0 LP |
223 | /* Ignore methods, signals and properties that are |
224 | * marked "hidden", but do show the interface | |
225 | * itself */ | |
226 | ||
6e8df5f0 LP |
227 | if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN)) |
228 | continue; | |
229 | ||
29ddb38f LP |
230 | switch (v->type) { |
231 | ||
232 | case _SD_BUS_VTABLE_START: | |
233 | if (v->flags & SD_BUS_VTABLE_DEPRECATED) | |
2485b7e2 | 234 | fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->m.f); |
29ddb38f LP |
235 | break; |
236 | ||
237 | case _SD_BUS_VTABLE_METHOD: | |
2485b7e2 | 238 | fprintf(i->m.f, " <method name=\"%s\">\n", v->x.method.member); |
856ad2a8 GC |
239 | if (bus_vtable_has_names(vtable)) |
240 | names = strempty(v->x.method.names); | |
241 | introspect_write_arguments(i, strempty(v->x.method.signature), &names, "in"); | |
242 | introspect_write_arguments(i, strempty(v->x.method.result), &names, "out"); | |
29ddb38f | 243 | introspect_write_flags(i, v->type, v->flags); |
2485b7e2 | 244 | fputs(" </method>\n", i->m.f); |
29ddb38f LP |
245 | break; |
246 | ||
247 | case _SD_BUS_VTABLE_PROPERTY: | |
248 | case _SD_BUS_VTABLE_WRITABLE_PROPERTY: | |
2485b7e2 | 249 | fprintf(i->m.f, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n", |
77a874a3 LP |
250 | v->x.property.member, |
251 | v->x.property.signature, | |
29ddb38f LP |
252 | v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read"); |
253 | introspect_write_flags(i, v->type, v->flags); | |
2485b7e2 | 254 | fputs(" </property>\n", i->m.f); |
29ddb38f LP |
255 | break; |
256 | ||
257 | case _SD_BUS_VTABLE_SIGNAL: | |
2485b7e2 | 258 | fprintf(i->m.f, " <signal name=\"%s\">\n", v->x.signal.member); |
856ad2a8 | 259 | if (bus_vtable_has_names(vtable)) |
58abbbcc | 260 | names = strempty(v->x.signal.names); |
856ad2a8 | 261 | introspect_write_arguments(i, strempty(v->x.signal.signature), &names, NULL); |
29ddb38f | 262 | introspect_write_flags(i, v->type, v->flags); |
2485b7e2 | 263 | fputs(" </signal>\n", i->m.f); |
29ddb38f LP |
264 | break; |
265 | } | |
266 | ||
267 | } | |
268 | ||
29ddb38f LP |
269 | return 0; |
270 | } | |
271 | ||
dff9e25a | 272 | int introspect_finish(struct introspect *i, char **ret) { |
29ddb38f | 273 | assert(i); |
2485b7e2 | 274 | assert(i->m.f); |
29ddb38f | 275 | |
61d0df39 ZJS |
276 | assert_se(set_interface_name(i, NULL) >= 0); |
277 | ||
2485b7e2 | 278 | fputs("</node>\n", i->m.f); |
29ddb38f | 279 | |
2485b7e2 | 280 | return memstream_finalize(&i->m, ret, NULL); |
29ddb38f LP |
281 | } |
282 | ||
3256e202 | 283 | void introspect_done(struct introspect *i) { |
29ddb38f LP |
284 | assert(i); |
285 | ||
dff9e25a | 286 | /* Normally introspect_finish() does all the work, this is just a backup for error paths */ |
29ddb38f | 287 | |
2485b7e2 | 288 | memstream_done(&i->m); |
61d0df39 | 289 | free(i->interface_name); |
29ddb38f | 290 | } |