1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "bus-introspect.h"
4 #include "bus-object.h"
5 #include "macro.h"
6 #include "string-util.h"
7 #include "strv.h"
8
bus_add_implementation(sd_bus * bus,const BusObjectImplementation * impl,void * userdata)9 int bus_add_implementation(sd_bus *bus, const BusObjectImplementation *impl, void *userdata) {
10 int r;
11
12 log_debug("Registering bus object implementation for path=%s iface=%s", impl->path, impl->interface);
13
14 for (const sd_bus_vtable **p = impl->vtables; p && *p; p++) {
15 r = sd_bus_add_object_vtable(bus, NULL,
16 impl->path,
17 impl->interface,
18 *p,
19 userdata);
20 if (r < 0)
21 return log_error_errno(r, "Failed to register bus path %s with interface %s: %m",
22 impl->path,
23 impl->interface);
24 }
25
26 for (const BusObjectVtablePair *p = impl->fallback_vtables; p && p->vtable; p++) {
27 r = sd_bus_add_fallback_vtable(bus, NULL,
28 impl->path,
29 impl->interface,
30 p->vtable,
31 p->object_find,
32 userdata);
33 if (r < 0)
34 return log_error_errno(r, "Failed to register bus path %s with interface %s: %m",
35 impl->path,
36 impl->interface);
37 }
38
39 if (impl->node_enumerator) {
40 r = sd_bus_add_node_enumerator(bus, NULL,
41 impl->path,
42 impl->node_enumerator,
43 userdata);
44 if (r < 0)
45 return log_error_errno(r, "Failed to add node enumerator for %s: %m",
46 impl->path);
47 }
48
49 if (impl->manager) {
50 r = sd_bus_add_object_manager(bus, NULL, impl->path);
51 if (r < 0)
52 return log_error_errno(r, "Failed to add object manager for %s: %m", impl->path);
53 }
54
55 for (size_t i = 0; impl->children && impl->children[i]; i++) {
56 r = bus_add_implementation(bus, impl->children[i], userdata);
57 if (r < 0)
58 return r;
59 }
60
61 return 0;
62 }
63
find_implementation(const char * pattern,const BusObjectImplementation * const * bus_objects)64 static const BusObjectImplementation* find_implementation(
65 const char *pattern,
66 const BusObjectImplementation* const* bus_objects) {
67
68 for (size_t i = 0; bus_objects && bus_objects[i]; i++) {
69 const BusObjectImplementation *impl = bus_objects[i];
70
71 if (STR_IN_SET(pattern, impl->path, impl->interface))
72 return impl;
73
74 impl = find_implementation(pattern, impl->children);
75 if (impl)
76 return impl;
77 }
78
79 return NULL;
80 }
81
bus_introspect_implementation(struct introspect * intro,const BusObjectImplementation * impl)82 static int bus_introspect_implementation(
83 struct introspect *intro,
84 const BusObjectImplementation *impl) {
85 int r;
86
87 for (const sd_bus_vtable **p = impl->vtables; p && *p; p++) {
88 r = introspect_write_interface(intro, impl->interface, *p);
89 if (r < 0)
90 return log_error_errno(r, "Failed to write introspection data: %m");
91 }
92
93 for (const BusObjectVtablePair *p = impl->fallback_vtables; p && p->vtable; p++) {
94 r = introspect_write_interface(intro, impl->interface, p->vtable);
95 if (r < 0)
96 return log_error_errno(r, "Failed to write introspection data: %m");
97 }
98
99 return 0;
100 }
101
list_paths(FILE * out,const BusObjectImplementation * const * bus_objects)102 static void list_paths(
103 FILE *out,
104 const BusObjectImplementation* const* bus_objects) {
105
106 for (size_t i = 0; bus_objects[i]; i++) {
107 fprintf(out, "%s\t%s\n", bus_objects[i]->path, bus_objects[i]->interface);
108 if (bus_objects[i]->children)
109 list_paths(out, bus_objects[i]->children);
110 }
111 }
112
bus_introspect_implementations(FILE * out,const char * pattern,const BusObjectImplementation * const * bus_objects)113 int bus_introspect_implementations(
114 FILE *out,
115 const char *pattern,
116 const BusObjectImplementation* const* bus_objects) {
117
118 const BusObjectImplementation *impl, *main_impl = NULL;
119 _cleanup_free_ char *s = NULL;
120 int r;
121
122 if (streq(pattern, "list")) {
123 list_paths(out, bus_objects);
124 return 0;
125 }
126
127 struct introspect intro = {};
128 bool is_interface = sd_bus_interface_name_is_valid(pattern);
129
130 impl = find_implementation(pattern, bus_objects);
131 if (!impl)
132 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
133 "%s %s not found",
134 is_interface ? "Interface" : "Object path",
135 pattern);
136
137 /* We use trusted=false here to get all the @org.freedesktop.systemd1.Privileged annotations. */
138 r = introspect_begin(&intro, false);
139 if (r < 0)
140 return log_error_errno(r, "Failed to write introspection data: %m");
141
142 r = introspect_write_default_interfaces(&intro, impl->manager);
143 if (r < 0)
144 return log_error_errno(r, "Failed to write introspection data: %m");
145
146 /* Check if there is a non-fallback path that applies to the given interface, also
147 * print it. This is useful in the case of units: o.fd.systemd1.Service is declared
148 * as a fallback vtable for o/fd/systemd1/unit, and we also want to print
149 * o.fd.systemd1.Unit, which is the non-fallback implementation. */
150 if (impl->fallback_vtables && is_interface)
151 main_impl = find_implementation(impl->path, bus_objects);
152
153 if (main_impl)
154 bus_introspect_implementation(&intro, main_impl);
155
156 if (impl != main_impl)
157 bus_introspect_implementation(&intro, impl);
158
159 _cleanup_ordered_set_free_ OrderedSet *nodes = NULL;
160
161 for (size_t i = 0; impl->children && impl->children[i]; i++) {
162 r = ordered_set_put_strdup(&nodes, impl->children[i]->path);
163 if (r < 0)
164 return log_oom();
165 }
166
167 r = introspect_write_child_nodes(&intro, nodes, impl->path);
168 if (r < 0)
169 return r;
170
171 r = introspect_finish(&intro, &s);
172 if (r < 0)
173 return log_error_errno(r, "Failed to write introspection data: %m");
174
175 fputs(s, out);
176 return 0;
177 }
178