Line data Source code
1 : /*
2 : Authors:
3 : Jakub Hrozek <jhrozek@redhat.com>
4 : Pavel Březina <pbrezina@redhat.com>
5 :
6 : Copyright (C) 2014 Red Hat
7 :
8 : SBUS: Interface introspection
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : #include "config.h"
25 :
26 : #include <stdio.h>
27 :
28 : #include "util/util.h"
29 : #include "sbus/sssd_dbus.h"
30 : #include "sbus/sssd_dbus_meta.h"
31 : #include "sbus/sssd_dbus_private.h"
32 :
33 : #define FMT_DOCTYPE \
34 : "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
35 : " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
36 :
37 : #define FMT_NODE "<node name=\"%s\">\n"
38 : #define FMT_IFACE " <interface name=\"%s\">\n"
39 : #define FMT_METHOD " <method name=\"%s\">\n"
40 : #define FMT_METHOD_NOARG " <method name=\"%s\" />\n"
41 : #define FMT_METHOD_ARG " <arg type=\"%s\" name=\"%s\" direction=\"%s\" />\n"
42 : #define FMT_METHOD_CLOSE " </method>\n"
43 : #define FMT_SIGNAL " <signal name=\"%s\">\n"
44 : #define FMT_SIGNAL_NOARG " <signal name=\"%s\" />\n"
45 : #define FMT_SIGNAL_ARG " <arg type=\"%s\" name=\"%s\" />\n"
46 : #define FMT_SIGNAL_CLOSE " </signal>\n"
47 : #define FMT_PROPERTY " <property name=\"%s\" type=\"%s\" access=\"%s\" />\n"
48 : #define FMT_IFACE_CLOSE " </interface>\n"
49 : #define FMT_CHILD_NODE " <node name=\"%s\" />\n"
50 : #define FMT_NODE_CLOSE "</node>\n"
51 :
52 : #define WRITE_OR_FAIL(file, ret, label, fmt, ...) do { \
53 : ret = fprintf(file, fmt, ##__VA_ARGS__); \
54 : if (ret < 0) { \
55 : ret = EIO; \
56 : goto label; \
57 : } \
58 : } while (0)
59 :
60 : #define METHOD_HAS_ARGS(m) ((m)->in_args != NULL || (m)->out_args != NULL)
61 : #define SIGNAL_HAS_ARGS(s) ((s)->args != NULL)
62 :
63 : enum sbus_arg_type {
64 : SBUS_ARG_IN,
65 : SBUS_ARG_OUT,
66 : SBUS_ARG_SIGNAL
67 : };
68 :
69 : static int
70 0 : iface_Introspect_finish(struct sbus_request *req, const char *arg_data)
71 : {
72 0 : return sbus_request_return_and_finish(req,
73 : DBUS_TYPE_STRING, &arg_data,
74 : DBUS_TYPE_INVALID);
75 : }
76 :
77 : struct iface_introspectable {
78 : struct sbus_vtable vtable; /* derive from sbus_vtable */
79 : int (*Introspect)(struct sbus_request *req, void *data);
80 : };
81 :
82 : static int sbus_introspect(struct sbus_request *sbus_req, void *pvt);
83 :
84 : struct sbus_vtable *
85 0 : sbus_introspect_vtable(void)
86 : {
87 : static const struct sbus_arg_meta iface_out[] = {
88 : {"data", "s"},
89 : {NULL, NULL}
90 : };
91 :
92 : static const struct sbus_method_meta iface_methods[] = {
93 : {"Introspect", NULL, iface_out,
94 : offsetof(struct iface_introspectable, Introspect), NULL},
95 : {NULL, }
96 : };
97 :
98 : static const struct sbus_interface_meta iface_meta = {
99 : "org.freedesktop.DBus.Introspectable", /* name */
100 : iface_methods,
101 : NULL, /* no signals */
102 : NULL, /* no properties */
103 : NULL, /* no GetAll invoker */
104 : };
105 :
106 : static struct iface_introspectable iface = {
107 : { &iface_meta, 0 },
108 : .Introspect = sbus_introspect
109 : };
110 :
111 0 : return &iface.vtable;
112 : }
113 :
114 : static int
115 0 : sbus_introspect_generate_args(FILE *file,
116 : const struct sbus_arg_meta *args,
117 : enum sbus_arg_type type)
118 : {
119 : const struct sbus_arg_meta *arg;
120 : int ret;
121 : int i;
122 :
123 0 : if (args == NULL) {
124 0 : return EOK;
125 : }
126 :
127 0 : for (i = 0; args[i].name != NULL; i++) {
128 0 : arg = &args[i];
129 :
130 0 : switch (type) {
131 : case SBUS_ARG_SIGNAL:
132 0 : WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_ARG,
133 : arg->type, arg->name);
134 0 : break;
135 : case SBUS_ARG_IN:
136 0 : WRITE_OR_FAIL(file, ret, done, FMT_METHOD_ARG,
137 : arg->type, arg->name, "in");
138 0 : break;
139 : case SBUS_ARG_OUT:
140 0 : WRITE_OR_FAIL(file, ret, done, FMT_METHOD_ARG,
141 : arg->type, arg->name, "out");
142 0 : break;
143 : }
144 : }
145 :
146 0 : ret = EOK;
147 :
148 : done:
149 0 : return ret;
150 : }
151 :
152 : #define sbus_introspect_generate_in_args(file, args) \
153 : sbus_introspect_generate_args(file, args, SBUS_ARG_IN)
154 :
155 : #define sbus_introspect_generate_out_args(file, args) \
156 : sbus_introspect_generate_args(file, args, SBUS_ARG_OUT)
157 :
158 : #define sbus_introspect_generate_signal_args(file, args) \
159 : sbus_introspect_generate_args(file, args, SBUS_ARG_SIGNAL)
160 :
161 : static int
162 0 : sbus_introspect_generate_methods(FILE *file,
163 : const struct sbus_method_meta *methods)
164 : {
165 : const struct sbus_method_meta *method;
166 : int ret;
167 : int i;
168 :
169 0 : if (methods == NULL) {
170 0 : return EOK;
171 : }
172 :
173 0 : for (i = 0; methods[i].name != NULL; i++) {
174 0 : method = &methods[i];
175 :
176 0 : if (!METHOD_HAS_ARGS(method)) {
177 0 : WRITE_OR_FAIL(file, ret, done, FMT_METHOD_NOARG, method->name);
178 0 : continue;
179 : }
180 :
181 0 : WRITE_OR_FAIL(file, ret, done, FMT_METHOD, method->name);
182 :
183 0 : ret = sbus_introspect_generate_in_args(file, method->in_args);
184 0 : if (ret != EOK) {
185 0 : goto done;
186 : }
187 :
188 0 : ret = sbus_introspect_generate_out_args(file, method->out_args);
189 0 : if (ret != EOK) {
190 0 : goto done;
191 : }
192 :
193 0 : WRITE_OR_FAIL(file, ret, done, FMT_METHOD_CLOSE);
194 : }
195 :
196 0 : ret = EOK;
197 :
198 : done:
199 0 : return ret;
200 : }
201 :
202 : static int
203 0 : sbus_introspect_generate_signals(FILE *file,
204 : const struct sbus_signal_meta *signals)
205 : {
206 : const struct sbus_signal_meta *a_signal;
207 : int ret;
208 : int i;
209 :
210 0 : if (signals == NULL) {
211 0 : return EOK;
212 : }
213 :
214 0 : for (i = 0; signals[i].name != NULL; i++) {
215 0 : a_signal = &signals[i];
216 :
217 0 : if (!SIGNAL_HAS_ARGS(a_signal)) {
218 0 : WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_NOARG, a_signal->name);
219 0 : continue;
220 : }
221 :
222 0 : WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL, a_signal->name);
223 :
224 0 : ret = sbus_introspect_generate_signal_args(file, a_signal->args);
225 0 : if (ret != EOK) {
226 0 : goto done;
227 : }
228 :
229 0 : WRITE_OR_FAIL(file, ret, done, FMT_SIGNAL_CLOSE);
230 : }
231 :
232 0 : ret = EOK;
233 :
234 : done:
235 0 : return ret;
236 : }
237 :
238 : static int
239 0 : sbus_introspect_generate_properties(FILE *file,
240 : const struct sbus_property_meta *props)
241 : {
242 : const struct sbus_property_meta *prop;
243 : const char *access_mode;
244 : int ret;
245 : int i;
246 :
247 0 : if (props == NULL) {
248 0 : return EOK;
249 : }
250 :
251 0 : for (i = 0; props[i].name != NULL; i++) {
252 0 : prop = &props[i];
253 :
254 0 : access_mode = prop->flags & SBUS_PROPERTY_WRITABLE
255 0 : ? "readwrite" : "read";
256 0 : WRITE_OR_FAIL(file, ret, done, FMT_PROPERTY,
257 : prop->name, prop->type, access_mode);
258 : }
259 :
260 0 : ret = EOK;
261 :
262 : done:
263 0 : return ret;
264 : }
265 :
266 : static int
267 0 : sbus_introspect_generate_iface(FILE *file, struct sbus_interface *iface)
268 : {
269 : const struct sbus_interface_meta *meta;
270 : int ret;
271 :
272 0 : meta = iface->vtable->meta;
273 :
274 0 : WRITE_OR_FAIL(file, ret, done, FMT_IFACE, meta->name);
275 :
276 0 : ret = sbus_introspect_generate_methods(file, meta->methods);
277 0 : if (ret != EOK) {
278 0 : goto done;
279 : }
280 :
281 0 : ret = sbus_introspect_generate_signals(file, meta->signals);
282 0 : if (ret != EOK) {
283 0 : goto done;
284 : }
285 :
286 0 : ret = sbus_introspect_generate_properties(file, meta->properties);
287 0 : if (ret != EOK) {
288 0 : goto done;
289 : }
290 :
291 0 : WRITE_OR_FAIL(file, ret, done, FMT_IFACE_CLOSE);
292 :
293 0 : ret = EOK;
294 :
295 : done:
296 0 : return ret;
297 : }
298 :
299 : static int
300 0 : sbus_introspect_generate_nodes(FILE *file, const char **nodes)
301 : {
302 : int ret;
303 : int i;
304 :
305 0 : if (nodes == NULL) {
306 0 : return EOK;
307 : }
308 :
309 0 : for (i = 0; nodes[i] != NULL; i++) {
310 0 : WRITE_OR_FAIL(file, ret, done, FMT_CHILD_NODE, nodes[i]);
311 : }
312 :
313 0 : ret = EOK;
314 :
315 : done:
316 0 : return ret;
317 : }
318 :
319 : static char *
320 0 : sbus_introspect_generate(TALLOC_CTX *mem_ctx,
321 : const char *node,
322 : const char **nodes,
323 : struct sbus_interface_list *list)
324 : {
325 : struct sbus_interface_list *item;
326 0 : char *introspect = NULL;
327 : FILE *memstream;
328 : char *buffer;
329 : size_t size;
330 : int ret;
331 :
332 0 : memstream = open_memstream(&buffer, &size);
333 0 : if (memstream == NULL) {
334 0 : goto done;
335 : }
336 :
337 0 : WRITE_OR_FAIL(memstream, ret, done, FMT_DOCTYPE);
338 0 : WRITE_OR_FAIL(memstream, ret, done, FMT_NODE, node);
339 :
340 0 : DLIST_FOR_EACH(item, list) {
341 0 : ret = sbus_introspect_generate_iface(memstream, item->interface);
342 0 : if (ret != EOK) {
343 0 : goto done;
344 : }
345 : }
346 :
347 0 : ret = sbus_introspect_generate_nodes(memstream, nodes);
348 0 : if (ret != EOK) {
349 0 : goto done;
350 : }
351 :
352 0 : WRITE_OR_FAIL(memstream, ret, done, FMT_NODE_CLOSE);
353 :
354 0 : fflush(memstream);
355 0 : introspect = talloc_memdup(mem_ctx, buffer, size + 1);
356 :
357 0 : DEBUG(SSSDBG_TRACE_ALL, "Introspection: \n%s\n", introspect);
358 :
359 : done:
360 0 : if (memstream != NULL) {
361 0 : fclose(memstream);
362 0 : free(buffer);
363 : }
364 :
365 0 : return introspect;
366 : }
367 :
368 : static int
369 0 : sbus_introspect(struct sbus_request *sbus_req, void *pvt)
370 : {
371 : DBusError *error;
372 : struct sbus_interface_list *list;
373 : struct sbus_connection *conn;
374 : const char **nodes;
375 : char *introspect;
376 : errno_t ret;
377 :
378 0 : conn = talloc_get_type(pvt, struct sbus_connection);
379 :
380 0 : ret = sbus_opath_hash_lookup_supported(sbus_req, conn->managed_paths,
381 : sbus_req->path, &list);
382 0 : if (ret != EOK) {
383 0 : error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED,
384 : "%s", sss_strerror(ret));
385 0 : return sbus_request_fail_and_finish(sbus_req, error);
386 : }
387 :
388 0 : nodes = sbus_nodes_hash_lookup(sbus_req, conn->nodes_fns, sbus_req->path);
389 :
390 0 : introspect = sbus_introspect_generate(sbus_req, sbus_req->path,
391 : nodes, list);
392 0 : if (introspect == NULL) {
393 0 : ret = ENOMEM;
394 0 : goto done;
395 : }
396 :
397 0 : ret = EOK;
398 :
399 : done:
400 0 : if (ret != EOK) {
401 0 : error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED,
402 : "%s", sss_strerror(ret));
403 0 : return sbus_request_fail_and_finish(sbus_req, error);
404 : }
405 :
406 0 : return iface_Introspect_finish(sbus_req, introspect);
407 : }
|