1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "sd-bus.h"
4 
5 #include "alloc-util.h"
6 #include "bus-control.h"
7 #include "bus-objects.h"
8 #include "bus-slot.h"
9 #include "string-util.h"
10 
bus_slot_allocate(sd_bus * bus,bool floating,BusSlotType type,size_t extra,void * userdata)11 sd_bus_slot *bus_slot_allocate(
12                 sd_bus *bus,
13                 bool floating,
14                 BusSlotType type,
15                 size_t extra,
16                 void *userdata) {
17 
18         sd_bus_slot *slot;
19 
20         assert(bus);
21 
22         slot = malloc0(offsetof(sd_bus_slot, reply_callback) + extra);
23         if (!slot)
24                 return NULL;
25 
26         slot->n_ref = 1;
27         slot->type = type;
28         slot->bus = bus;
29         slot->floating = floating;
30         slot->userdata = userdata;
31 
32         if (!floating)
33                 sd_bus_ref(bus);
34 
35         LIST_PREPEND(slots, bus->slots, slot);
36 
37         return slot;
38 }
39 
bus_slot_disconnect(sd_bus_slot * slot,bool unref)40 void bus_slot_disconnect(sd_bus_slot *slot, bool unref) {
41         sd_bus *bus;
42 
43         assert(slot);
44 
45         if (!slot->bus)
46                 return;
47 
48         switch (slot->type) {
49 
50         case BUS_REPLY_CALLBACK:
51 
52                 if (slot->reply_callback.cookie != 0)
53                         ordered_hashmap_remove(slot->bus->reply_callbacks, &slot->reply_callback.cookie);
54 
55                 if (slot->reply_callback.timeout_usec != 0)
56                         prioq_remove(slot->bus->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
57 
58                 break;
59 
60         case BUS_FILTER_CALLBACK:
61                 slot->bus->filter_callbacks_modified = true;
62                 LIST_REMOVE(callbacks, slot->bus->filter_callbacks, &slot->filter_callback);
63                 break;
64 
65         case BUS_MATCH_CALLBACK:
66 
67                 if (slot->match_added)
68                         (void) bus_remove_match_internal(slot->bus, slot->match_callback.match_string);
69 
70                 if (slot->match_callback.install_slot) {
71                         bus_slot_disconnect(slot->match_callback.install_slot, true);
72                         slot->match_callback.install_slot = sd_bus_slot_unref(slot->match_callback.install_slot);
73                 }
74 
75                 slot->bus->match_callbacks_modified = true;
76                 bus_match_remove(&slot->bus->match_callbacks, &slot->match_callback);
77 
78                 slot->match_callback.match_string = mfree(slot->match_callback.match_string);
79 
80                 break;
81 
82         case BUS_NODE_CALLBACK:
83 
84                 if (slot->node_callback.node) {
85                         LIST_REMOVE(callbacks, slot->node_callback.node->callbacks, &slot->node_callback);
86                         slot->bus->nodes_modified = true;
87 
88                         bus_node_gc(slot->bus, slot->node_callback.node);
89                 }
90 
91                 break;
92 
93         case BUS_NODE_ENUMERATOR:
94 
95                 if (slot->node_enumerator.node) {
96                         LIST_REMOVE(enumerators, slot->node_enumerator.node->enumerators, &slot->node_enumerator);
97                         slot->bus->nodes_modified = true;
98 
99                         bus_node_gc(slot->bus, slot->node_enumerator.node);
100                 }
101 
102                 break;
103 
104         case BUS_NODE_OBJECT_MANAGER:
105 
106                 if (slot->node_object_manager.node) {
107                         LIST_REMOVE(object_managers, slot->node_object_manager.node->object_managers, &slot->node_object_manager);
108                         slot->bus->nodes_modified = true;
109 
110                         bus_node_gc(slot->bus, slot->node_object_manager.node);
111                 }
112 
113                 break;
114 
115         case BUS_NODE_VTABLE:
116 
117                 if (slot->node_vtable.node && slot->node_vtable.interface && slot->node_vtable.vtable) {
118                         const sd_bus_vtable *v;
119 
120                         for (v = slot->node_vtable.vtable; v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(slot->node_vtable.vtable, v)) {
121                                 struct vtable_member *x = NULL;
122 
123                                 switch (v->type) {
124 
125                                 case _SD_BUS_VTABLE_METHOD: {
126                                         struct vtable_member key;
127 
128                                         key.path = slot->node_vtable.node->path;
129                                         key.interface = slot->node_vtable.interface;
130                                         key.member = v->x.method.member;
131 
132                                         x = hashmap_remove(slot->bus->vtable_methods, &key);
133                                         break;
134                                 }
135 
136                                 case _SD_BUS_VTABLE_PROPERTY:
137                                 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
138                                         struct vtable_member key;
139 
140                                         key.path = slot->node_vtable.node->path;
141                                         key.interface = slot->node_vtable.interface;
142                                         key.member = v->x.method.member;
143 
144                                         x = hashmap_remove(slot->bus->vtable_properties, &key);
145                                         break;
146                                 }}
147 
148                                 free(x);
149                         }
150                 }
151 
152                 slot->node_vtable.interface = mfree(slot->node_vtable.interface);
153 
154                 if (slot->node_vtable.node) {
155                         LIST_REMOVE(vtables, slot->node_vtable.node->vtables, &slot->node_vtable);
156                         slot->bus->nodes_modified = true;
157 
158                         bus_node_gc(slot->bus, slot->node_vtable.node);
159                 }
160 
161                 break;
162 
163         default:
164                 assert_not_reached();
165         }
166 
167         bus = slot->bus;
168 
169         slot->type = _BUS_SLOT_INVALID;
170         slot->bus = NULL;
171         LIST_REMOVE(slots, bus->slots, slot);
172 
173         if (!slot->floating)
174                 sd_bus_unref(bus);
175         else if (unref)
176                 sd_bus_slot_unref(slot);
177 }
178 
bus_slot_free(sd_bus_slot * slot)179 static sd_bus_slot* bus_slot_free(sd_bus_slot *slot) {
180         assert(slot);
181 
182         bus_slot_disconnect(slot, false);
183 
184         if (slot->destroy_callback)
185                 slot->destroy_callback(slot->userdata);
186 
187         free(slot->description);
188         return mfree(slot);
189 }
190 
191 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_slot, sd_bus_slot, bus_slot_free);
192 
sd_bus_slot_get_bus(sd_bus_slot * slot)193 _public_ sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot) {
194         assert_return(slot, NULL);
195 
196         return slot->bus;
197 }
198 
sd_bus_slot_get_userdata(sd_bus_slot * slot)199 _public_ void *sd_bus_slot_get_userdata(sd_bus_slot *slot) {
200         assert_return(slot, NULL);
201 
202         return slot->userdata;
203 }
204 
sd_bus_slot_set_userdata(sd_bus_slot * slot,void * userdata)205 _public_ void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata) {
206         void *ret;
207 
208         assert_return(slot, NULL);
209 
210         ret = slot->userdata;
211         slot->userdata = userdata;
212 
213         return ret;
214 }
215 
sd_bus_slot_set_destroy_callback(sd_bus_slot * slot,sd_bus_destroy_t callback)216 _public_ int sd_bus_slot_set_destroy_callback(sd_bus_slot *slot, sd_bus_destroy_t callback) {
217         assert_return(slot, -EINVAL);
218 
219         slot->destroy_callback = callback;
220         return 0;
221 }
222 
sd_bus_slot_get_destroy_callback(sd_bus_slot * slot,sd_bus_destroy_t * callback)223 _public_ int sd_bus_slot_get_destroy_callback(sd_bus_slot *slot, sd_bus_destroy_t *callback) {
224         assert_return(slot, -EINVAL);
225 
226         if (callback)
227                 *callback = slot->destroy_callback;
228 
229         return !!slot->destroy_callback;
230 }
231 
sd_bus_slot_get_current_message(sd_bus_slot * slot)232 _public_ sd_bus_message *sd_bus_slot_get_current_message(sd_bus_slot *slot) {
233         assert_return(slot, NULL);
234         assert_return(slot->type >= 0, NULL);
235 
236         if (slot->bus->current_slot != slot)
237                 return NULL;
238 
239         return slot->bus->current_message;
240 }
241 
sd_bus_slot_get_current_handler(sd_bus_slot * slot)242 _public_ sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *slot) {
243         assert_return(slot, NULL);
244         assert_return(slot->type >= 0, NULL);
245 
246         if (slot->bus->current_slot != slot)
247                 return NULL;
248 
249         return slot->bus->current_handler;
250 }
251 
sd_bus_slot_get_current_userdata(sd_bus_slot * slot)252 _public_ void* sd_bus_slot_get_current_userdata(sd_bus_slot *slot) {
253         assert_return(slot, NULL);
254         assert_return(slot->type >= 0, NULL);
255 
256         if (slot->bus->current_slot != slot)
257                 return NULL;
258 
259         return slot->bus->current_userdata;
260 }
261 
sd_bus_slot_get_floating(sd_bus_slot * slot)262 _public_ int sd_bus_slot_get_floating(sd_bus_slot *slot) {
263         assert_return(slot, -EINVAL);
264 
265         return slot->floating;
266 }
267 
sd_bus_slot_set_floating(sd_bus_slot * slot,int b)268 _public_ int sd_bus_slot_set_floating(sd_bus_slot *slot, int b) {
269         assert_return(slot, -EINVAL);
270 
271         if (slot->floating == !!b)
272                 return 0;
273 
274         if (!slot->bus) /* already disconnected slots can't be reconnected */
275                 return -ESTALE;
276 
277         slot->floating = b;
278 
279         /* When a slot is "floating" then the bus references the slot. Otherwise the slot references the bus. Hence,
280          * when we move from one to the other, let's increase one reference and decrease the other. */
281 
282         if (b) {
283                 sd_bus_slot_ref(slot);
284                 sd_bus_unref(slot->bus);
285         } else {
286                 sd_bus_ref(slot->bus);
287                 sd_bus_slot_unref(slot);
288         }
289 
290         return 1;
291 }
292 
sd_bus_slot_set_description(sd_bus_slot * slot,const char * description)293 _public_ int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description) {
294         assert_return(slot, -EINVAL);
295 
296         return free_and_strdup(&slot->description, description);
297 }
298 
sd_bus_slot_get_description(sd_bus_slot * slot,const char ** description)299 _public_ int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description) {
300         assert_return(slot, -EINVAL);
301         assert_return(description, -EINVAL);
302 
303         if (slot->description)
304                 *description = slot->description;
305         else if (slot->type == BUS_MATCH_CALLBACK)
306                 *description = slot->match_callback.match_string;
307         else
308                 return -ENXIO;
309 
310         return 0;
311 }
312