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