]>
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 | ||
43 | int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) { | |
44 | char *node; | |
45 | ||
46 | assert(i); | |
47 | assert(prefix); | |
48 | ||
49 | while ((node = set_steal_first(s))) { | |
50 | const char *e; | |
51 | ||
52 | e = object_path_startswith(node, prefix); | |
dd8243a3 | 53 | if (e && e[0]) |
29ddb38f LP |
54 | fprintf(i->f, " <node name=\"%s\"/>\n", e); |
55 | ||
56 | free(node); | |
57 | } | |
58 | ||
59 | return 0; | |
60 | } | |
61 | ||
affaed1e | 62 | static void introspect_write_flags(struct introspect *i, int type, uint64_t flags) { |
29ddb38f | 63 | if (flags & SD_BUS_VTABLE_DEPRECATED) |
0d536673 | 64 | fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f); |
29ddb38f | 65 | |
adacb957 | 66 | if (type == _SD_BUS_VTABLE_METHOD && (flags & SD_BUS_VTABLE_METHOD_NO_REPLY)) |
0d536673 | 67 | fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i->f); |
29ddb38f | 68 | |
945c2931 | 69 | if (IN_SET(type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY)) { |
33702051 | 70 | if (flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT) |
0d536673 | 71 | fputs(" <annotation name=\"org.freedesktop.systemd1.Explicit\" value=\"true\"/>\n", i->f); |
33702051 | 72 | |
df98a87b | 73 | if (flags & SD_BUS_VTABLE_PROPERTY_CONST) |
0d536673 | 74 | fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"const\"/>\n", i->f); |
df98a87b | 75 | else if (flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) |
0d536673 | 76 | fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i->f); |
df98a87b | 77 | else if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) |
0d536673 | 78 | fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i->f); |
29ddb38f | 79 | } |
adacb957 | 80 | |
7fb411f0 | 81 | if (!i->trusted && |
945c2931 | 82 | IN_SET(type, _SD_BUS_VTABLE_METHOD, _SD_BUS_VTABLE_WRITABLE_PROPERTY) && |
7fb411f0 | 83 | !(flags & SD_BUS_VTABLE_UNPRIVILEGED)) |
0d536673 | 84 | fputs(" <annotation name=\"org.freedesktop.systemd1.Privileged\" value=\"true\"/>\n", i->f); |
29ddb38f LP |
85 | } |
86 | ||
856ad2a8 GC |
87 | /* Note that "names" is both an input and an output parameter. It initially points to the first argument name in a |
88 | NULL-separated list of strings, and is then advanced with each argument, and the resulting pointer is returned. */ | |
89 | static int introspect_write_arguments(struct introspect *i, const char *signature, const char **names, const char *direction) { | |
29ddb38f LP |
90 | int r; |
91 | ||
92 | for (;;) { | |
93 | size_t l; | |
94 | ||
95 | if (!*signature) | |
96 | return 0; | |
97 | ||
98 | r = signature_element_length(signature, &l); | |
99 | if (r < 0) | |
100 | return r; | |
101 | ||
102 | fprintf(i->f, " <arg type=\"%.*s\"", (int) l, signature); | |
103 | ||
856ad2a8 GC |
104 | if (**names != '\0') { |
105 | fprintf(i->f, " name=\"%s\"", *names); | |
106 | *names += strlen(*names) + 1; | |
107 | } | |
108 | ||
29ddb38f | 109 | if (direction) |
6014597d | 110 | fprintf(i->f, " direction=\"%s\"/>\n", direction); |
29ddb38f | 111 | else |
0d536673 | 112 | fputs("/>\n", i->f); |
29ddb38f LP |
113 | |
114 | signature += l; | |
115 | } | |
116 | } | |
117 | ||
718db961 | 118 | int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) { |
856ad2a8 GC |
119 | const sd_bus_vtable *vtable = v; |
120 | const char *names = ""; | |
121 | ||
29ddb38f | 122 | assert(i); |
29ddb38f LP |
123 | assert(v); |
124 | ||
856ad2a8 | 125 | for (; v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) { |
29ddb38f | 126 | |
7fb411f0 LP |
127 | /* Ignore methods, signals and properties that are |
128 | * marked "hidden", but do show the interface | |
129 | * itself */ | |
130 | ||
6e8df5f0 LP |
131 | if (v->type != _SD_BUS_VTABLE_START && (v->flags & SD_BUS_VTABLE_HIDDEN)) |
132 | continue; | |
133 | ||
29ddb38f LP |
134 | switch (v->type) { |
135 | ||
136 | case _SD_BUS_VTABLE_START: | |
137 | if (v->flags & SD_BUS_VTABLE_DEPRECATED) | |
0d536673 | 138 | fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f); |
29ddb38f LP |
139 | break; |
140 | ||
141 | case _SD_BUS_VTABLE_METHOD: | |
77a874a3 | 142 | fprintf(i->f, " <method name=\"%s\">\n", v->x.method.member); |
856ad2a8 GC |
143 | if (bus_vtable_has_names(vtable)) |
144 | names = strempty(v->x.method.names); | |
145 | introspect_write_arguments(i, strempty(v->x.method.signature), &names, "in"); | |
146 | introspect_write_arguments(i, strempty(v->x.method.result), &names, "out"); | |
29ddb38f | 147 | introspect_write_flags(i, v->type, v->flags); |
0d536673 | 148 | fputs(" </method>\n", i->f); |
29ddb38f LP |
149 | break; |
150 | ||
151 | case _SD_BUS_VTABLE_PROPERTY: | |
152 | case _SD_BUS_VTABLE_WRITABLE_PROPERTY: | |
153 | fprintf(i->f, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n", | |
77a874a3 LP |
154 | v->x.property.member, |
155 | v->x.property.signature, | |
29ddb38f LP |
156 | v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read"); |
157 | introspect_write_flags(i, v->type, v->flags); | |
0d536673 | 158 | fputs(" </property>\n", i->f); |
29ddb38f LP |
159 | break; |
160 | ||
161 | case _SD_BUS_VTABLE_SIGNAL: | |
77a874a3 | 162 | fprintf(i->f, " <signal name=\"%s\">\n", v->x.signal.member); |
856ad2a8 | 163 | if (bus_vtable_has_names(vtable)) |
58abbbcc | 164 | names = strempty(v->x.signal.names); |
856ad2a8 | 165 | introspect_write_arguments(i, strempty(v->x.signal.signature), &names, NULL); |
29ddb38f | 166 | introspect_write_flags(i, v->type, v->flags); |
0d536673 | 167 | fputs(" </signal>\n", i->f); |
29ddb38f LP |
168 | break; |
169 | } | |
170 | ||
171 | } | |
172 | ||
29ddb38f LP |
173 | return 0; |
174 | } | |
175 | ||
dff9e25a | 176 | int introspect_finish(struct introspect *i, char **ret) { |
29ddb38f LP |
177 | int r; |
178 | ||
179 | assert(i); | |
29ddb38f | 180 | |
0d536673 | 181 | fputs("</node>\n", i->f); |
29ddb38f | 182 | |
dacd6cee LP |
183 | r = fflush_and_check(i->f); |
184 | if (r < 0) | |
185 | return r; | |
29ddb38f | 186 | |
dff9e25a ZJS |
187 | i->f = safe_fclose(i->f); |
188 | *ret = TAKE_PTR(i->introspection); | |
29ddb38f | 189 | |
29ddb38f LP |
190 | return 0; |
191 | } | |
192 | ||
193 | void introspect_free(struct introspect *i) { | |
194 | assert(i); | |
195 | ||
dff9e25a | 196 | /* Normally introspect_finish() does all the work, this is just a backup for error paths */ |
29ddb38f | 197 | |
dff9e25a | 198 | safe_fclose(i->f); |
dacd6cee | 199 | free(i->introspection); |
29ddb38f | 200 | } |