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