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