]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
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" |
07630cea | 11 | #include "string-util.h" |
29ddb38f | 12 | |
7fb411f0 | 13 | int introspect_begin(struct introspect *i, bool trusted) { |
29ddb38f LP |
14 | assert(i); |
15 | ||
41ab8c67 LP |
16 | *i = (struct introspect) { |
17 | .trusted = trusted, | |
18 | }; | |
29ddb38f | 19 | |
2fe21124 | 20 | i->f = open_memstream_unlocked(&i->introspection, &i->size); |
29ddb38f LP |
21 | if (!i->f) |
22 | return -ENOMEM; | |
23 | ||
0d536673 LP |
24 | fputs(BUS_INTROSPECT_DOCTYPE |
25 | "<node>\n", i->f); | |
29ddb38f LP |
26 | |
27 | return 0; | |
28 | } | |
29 | ||
30 | int introspect_write_default_interfaces(struct introspect *i, bool object_manager) { | |
31 | assert(i); | |
32 | ||
0d536673 LP |
33 | fputs(BUS_INTROSPECT_INTERFACE_PEER |
34 | BUS_INTROSPECT_INTERFACE_INTROSPECTABLE | |
35 | BUS_INTROSPECT_INTERFACE_PROPERTIES, i->f); | |
29ddb38f LP |
36 | |
37 | if (object_manager) | |
0d536673 | 38 | fputs(BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->f); |
29ddb38f LP |
39 | |
40 | return 0; | |
41 | } | |
42 | ||
61d0df39 ZJS |
43 | static int set_interface_name(struct introspect *intro, const char *interface_name) { |
44 | if (streq_ptr(intro->interface_name, interface_name)) | |
45 | return 0; | |
46 | ||
47 | if (intro->interface_name) | |
48 | fputs(" </interface>\n", intro->f); | |
49 | ||
50 | if (interface_name) | |
51 | fprintf(intro->f, " <interface name=\"%s\">\n", interface_name); | |
52 | ||
53 | return free_and_strdup(&intro->interface_name, interface_name); | |
54 | } | |
55 | ||
29ddb38f LP |
56 | int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) { |
57 | char *node; | |
58 | ||
59 | assert(i); | |
60 | assert(prefix); | |
61 | ||
61d0df39 ZJS |
62 | assert_se(set_interface_name(i, NULL) >= 0); |
63 | ||
29ddb38f LP |
64 | while ((node = set_steal_first(s))) { |
65 | const char *e; | |
66 | ||
67 | e = object_path_startswith(node, prefix); | |
dd8243a3 | 68 | if (e && e[0]) |
29ddb38f LP |
69 | fprintf(i->f, " <node name=\"%s\"/>\n", e); |
70 | ||
71 | free(node); | |
72 | } | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
affaed1e | 77 | static void introspect_write_flags(struct introspect *i, int type, uint64_t flags) { |
29ddb38f | 78 | if (flags & SD_BUS_VTABLE_DEPRECATED) |
0d536673 | 79 | fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f); |
29ddb38f | 80 | |
adacb957 | 81 | if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY)) |
0d536673 | 82 | fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i->f); |
29ddb38f | 83 | |
945c2931 | 84 | if (IN_SET(type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY)) { |
33702051 | 85 | if (flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) |
0d536673 | 86 | fputs(" <annotation name=\"org.freedesktop.systemd1.Explicit\" value=\"true\"/>\n", i->f); |
33702051 | 87 | |
df98a87b | 88 | if (flags & SD_BUS_VTABLE_PROPERTY_CONST) |
0d536673 | 89 | fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i->f); |
df98a87b | 90 | else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) |
0d536673 | 91 | fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i->f); |
df98a87b | 92 | else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) |
0d536673 | 93 | fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i->f); |
29ddb38f | 94 | } |
adacb957 | 95 | |
7fb411f0 | 96 | if (!i->trusted && |
945c2931 | 97 | IN_SET(type, _SD_BUS_VTABLE_METHOD, _SD_BUS_VTABLE_WRITABLE_PROPERTY) && |
7fb411f0 | 98 | !(flags & SD_BUS_VTABLE_UNPRIVILEGED)) |
0d536673 | 99 | fputs(" <annotation name=\"org.freedesktop.systemd1.Privileged\" value=\"true\"/>\n", i->f); |
29ddb38f LP |
100 | } |
101 | ||
856ad2a8 GC |
102 | /* Note that "names" is both an input and an output parameter. It initially points to the first argument name in a |
103 | NULL-separated list of strings, and is then advanced with each argument, and the resulting pointer is returned. */ | |
104 | static int introspect_write_arguments(struct introspect *i, const char *signature, const char **names, const char *direction) { | |
29ddb38f LP |
105 | int r; |
106 | ||
107 | for (;;) { | |
108 | size_t l; | |
109 | ||
110 | if (!*signature) | |
111 | return 0; | |
112 | ||
113 | r = signature_element_length(signature, &l); | |
114 | if (r < 0) | |
115 | return r; | |
116 | ||
117 | fprintf(i->f, " <arg type=\"%.*s\"", (int) l, signature); | |
118 | ||
856ad2a8 GC |
119 | if (**names != '\0') { |
120 | fprintf(i->f, " name=\"%s\"", *names); | |
121 | *names += strlen(*names) + 1; | |
122 | } | |
123 | ||
29ddb38f | 124 | if (direction) |
6014597d | 125 | fprintf(i->f, " direction=\"%s\"/>\n", direction); |
29ddb38f | 126 | else |
0d536673 | 127 | fputs("/>\n", i->f); |
29ddb38f LP |
128 | |
129 | signature += l; | |
130 | } | |
131 | } | |
132 | ||
61d0df39 ZJS |
133 | int introspect_write_interface( |
134 | struct introspect *i, | |
135 | const char *interface_name, | |
136 | const sd_bus_vtable *v) { | |
137 | ||
856ad2a8 GC |
138 | const sd_bus_vtable *vtable = v; |
139 | const char *names = ""; | |
61d0df39 | 140 | int r; |
856ad2a8 | 141 | |
29ddb38f | 142 | assert(i); |
61d0df39 | 143 | assert(interface_name); |
29ddb38f LP |
144 | assert(v); |
145 | ||
61d0df39 ZJS |
146 | r = set_interface_name(i, interface_name); |
147 | if (r < 0) | |
148 | return r; | |
149 | ||
856ad2a8 | 150 | for (; v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) { |
29ddb38f | 151 | |
7fb411f0 LP |
152 | /* Ignore methods, signals and properties that are |
153 | * marked "hidden", but do show the interface | |
154 | * itself */ | |
155 | ||
6e8df5f0 LP |
156 | if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN)) |
157 | continue; | |
158 | ||
29ddb38f LP |
159 | switch (v->type) { |
160 | ||
161 | case _SD_BUS_VTABLE_START: | |
162 | if (v->flags & SD_BUS_VTABLE_DEPRECATED) | |
0d536673 | 163 | fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f); |
29ddb38f LP |
164 | break; |
165 | ||
166 | case _SD_BUS_VTABLE_METHOD: | |
77a874a3 | 167 | fprintf(i->f, " <method name=\"%s\">\n", v->x.method.member); |
856ad2a8 GC |
168 | if (bus_vtable_has_names(vtable)) |
169 | names = strempty(v->x.method.names); | |
170 | introspect_write_arguments(i, strempty(v->x.method.signature), &names, "in"); | |
171 | introspect_write_arguments(i, strempty(v->x.method.result), &names, "out"); | |
29ddb38f | 172 | introspect_write_flags(i, v->type, v->flags); |
0d536673 | 173 | fputs(" </method>\n", i->f); |
29ddb38f LP |
174 | break; |
175 | ||
176 | case _SD_BUS_VTABLE_PROPERTY: | |
177 | case _SD_BUS_VTABLE_WRITABLE_PROPERTY: | |
178 | fprintf(i->f, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n", | |
77a874a3 LP |
179 | v->x.property.member, |
180 | v->x.property.signature, | |
29ddb38f LP |
181 | v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read"); |
182 | introspect_write_flags(i, v->type, v->flags); | |
0d536673 | 183 | fputs(" </property>\n", i->f); |
29ddb38f LP |
184 | break; |
185 | ||
186 | case _SD_BUS_VTABLE_SIGNAL: | |
77a874a3 | 187 | fprintf(i->f, " <signal name=\"%s\">\n", v->x.signal.member); |
856ad2a8 | 188 | if (bus_vtable_has_names(vtable)) |
58abbbcc | 189 | names = strempty(v->x.signal.names); |
856ad2a8 | 190 | introspect_write_arguments(i, strempty(v->x.signal.signature), &names, NULL); |
29ddb38f | 191 | introspect_write_flags(i, v->type, v->flags); |
0d536673 | 192 | fputs(" </signal>\n", i->f); |
29ddb38f LP |
193 | break; |
194 | } | |
195 | ||
196 | } | |
197 | ||
29ddb38f LP |
198 | return 0; |
199 | } | |
200 | ||
dff9e25a | 201 | int introspect_finish(struct introspect *i, char **ret) { |
29ddb38f LP |
202 | int r; |
203 | ||
204 | assert(i); | |
29ddb38f | 205 | |
61d0df39 ZJS |
206 | assert_se(set_interface_name(i, NULL) >= 0); |
207 | ||
0d536673 | 208 | fputs("</node>\n", i->f); |
29ddb38f | 209 | |
dacd6cee LP |
210 | r = fflush_and_check(i->f); |
211 | if (r < 0) | |
212 | return r; | |
29ddb38f | 213 | |
dff9e25a ZJS |
214 | i->f = safe_fclose(i->f); |
215 | *ret = TAKE_PTR(i->introspection); | |
29ddb38f | 216 | |
29ddb38f LP |
217 | return 0; |
218 | } | |
219 | ||
220 | void introspect_free(struct introspect *i) { | |
221 | assert(i); | |
222 | ||
dff9e25a | 223 | /* Normally introspect_finish() does all the work, this is just a backup for error paths */ |
29ddb38f | 224 | |
dff9e25a | 225 | safe_fclose(i->f); |
61d0df39 | 226 | free(i->interface_name); |
dacd6cee | 227 | free(i->introspection); |
29ddb38f | 228 | } |