1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include "alloc-util.h"
4 #include "bus-internal.h"
5 #include "bus-introspect.h"
6 #include "bus-message.h"
7 #include "bus-objects.h"
8 #include "bus-signature.h"
9 #include "bus-slot.h"
10 #include "bus-type.h"
11 #include "missing_capability.h"
12 #include "string-util.h"
13 #include "strv.h"
14 
node_vtable_get_userdata(sd_bus * bus,const char * path,struct node_vtable * c,void ** userdata,sd_bus_error * error)15 static int node_vtable_get_userdata(
16                 sd_bus *bus,
17                 const char *path,
18                 struct node_vtable *c,
19                 void **userdata,
20                 sd_bus_error *error) {
21 
22         sd_bus_slot *s;
23         void *u, *found_u = NULL;
24         int r;
25 
26         assert(bus);
27         assert(path);
28         assert(c);
29 
30         s = container_of(c, sd_bus_slot, node_vtable);
31         u = s->userdata;
32         if (c->find) {
33                 bus->current_slot = sd_bus_slot_ref(s);
34                 bus->current_userdata = u;
35                 r = c->find(bus, path, c->interface, u, &found_u, error);
36                 bus->current_userdata = NULL;
37                 bus->current_slot = sd_bus_slot_unref(s);
38 
39                 if (r < 0)
40                         return r;
41                 if (sd_bus_error_is_set(error))
42                         return -sd_bus_error_get_errno(error);
43                 if (r == 0)
44                         return r;
45         } else
46                 found_u = u;
47 
48         if (userdata)
49                 *userdata = found_u;
50 
51         return 1;
52 }
53 
vtable_method_convert_userdata(const sd_bus_vtable * p,void * u)54 static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
55         assert(p);
56 
57         if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET))
58                 return SIZE_TO_PTR(p->x.method.offset); /* don't add offset on NULL, to make ubsan happy */
59 
60         return (uint8_t*) u + p->x.method.offset;
61 }
62 
vtable_property_convert_userdata(const sd_bus_vtable * p,void * u)63 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
64         assert(p);
65 
66         if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET))
67                 return SIZE_TO_PTR(p->x.property.offset); /* as above */
68 
69         return (uint8_t*) u + p->x.property.offset;
70 }
71 
vtable_property_get_userdata(sd_bus * bus,const char * path,struct vtable_member * p,void ** userdata,sd_bus_error * error)72 static int vtable_property_get_userdata(
73                 sd_bus *bus,
74                 const char *path,
75                 struct vtable_member *p,
76                 void **userdata,
77                 sd_bus_error *error) {
78 
79         void *u;
80         int r;
81 
82         assert(bus);
83         assert(path);
84         assert(p);
85         assert(userdata);
86 
87         r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
88         if (r <= 0)
89                 return r;
90         if (bus->nodes_modified)
91                 return 0;
92 
93         *userdata = vtable_property_convert_userdata(p->vtable, u);
94         return 1;
95 }
96 
add_enumerated_to_set(sd_bus * bus,const char * prefix,struct node_enumerator * first,OrderedSet * s,sd_bus_error * error)97 static int add_enumerated_to_set(
98                 sd_bus *bus,
99                 const char *prefix,
100                 struct node_enumerator *first,
101                 OrderedSet *s,
102                 sd_bus_error *error) {
103 
104         int r;
105 
106         assert(bus);
107         assert(prefix);
108         assert(s);
109 
110         LIST_FOREACH(enumerators, c, first) {
111                 char **children = NULL;
112                 sd_bus_slot *slot;
113 
114                 if (bus->nodes_modified)
115                         return 0;
116 
117                 slot = container_of(c, sd_bus_slot, node_enumerator);
118 
119                 bus->current_slot = sd_bus_slot_ref(slot);
120                 bus->current_userdata = slot->userdata;
121                 r = c->callback(bus, prefix, slot->userdata, &children, error);
122                 bus->current_userdata = NULL;
123                 bus->current_slot = sd_bus_slot_unref(slot);
124 
125                 if (r < 0)
126                         return r;
127                 if (sd_bus_error_is_set(error))
128                         return -sd_bus_error_get_errno(error);
129 
130                 STRV_FOREACH(k, children) {
131                         if (r < 0) {
132                                 free(*k);
133                                 continue;
134                         }
135 
136                         if (!object_path_is_valid(*k)) {
137                                 free(*k);
138                                 r = -EINVAL;
139                                 continue;
140                         }
141 
142                         if (!object_path_startswith(*k, prefix)) {
143                                 free(*k);
144                                 continue;
145                         }
146 
147                         r = ordered_set_consume(s, *k);
148                         if (r == -EEXIST)
149                                 r = 0;
150                 }
151 
152                 free(children);
153                 if (r < 0)
154                         return r;
155         }
156 
157         return 0;
158 }
159 
160 enum {
161         /* if set, add_subtree() works recursively */
162         CHILDREN_RECURSIVE      = 1 << 0,
163         /* if set, add_subtree() scans object-manager hierarchies recursively */
164         CHILDREN_SUBHIERARCHIES = 1 << 1,
165 };
166 
add_subtree_to_set(sd_bus * bus,const char * prefix,struct node * n,unsigned flags,OrderedSet * s,sd_bus_error * error)167 static int add_subtree_to_set(
168                 sd_bus *bus,
169                 const char *prefix,
170                 struct node *n,
171                 unsigned flags,
172                 OrderedSet *s,
173                 sd_bus_error *error) {
174 
175         int r;
176 
177         assert(bus);
178         assert(prefix);
179         assert(n);
180         assert(s);
181 
182         r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
183         if (r < 0)
184                 return r;
185         if (bus->nodes_modified)
186                 return 0;
187 
188         LIST_FOREACH(siblings, i, n->child) {
189                 char *t;
190 
191                 if (!object_path_startswith(i->path, prefix))
192                         continue;
193 
194                 t = strdup(i->path);
195                 if (!t)
196                         return -ENOMEM;
197 
198                 r = ordered_set_consume(s, t);
199                 if (r < 0 && r != -EEXIST)
200                         return r;
201 
202                 if ((flags & CHILDREN_RECURSIVE) &&
203                     ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) {
204                         r = add_subtree_to_set(bus, prefix, i, flags, s, error);
205                         if (r < 0)
206                                 return r;
207                         if (bus->nodes_modified)
208                                 return 0;
209                 }
210         }
211 
212         return 0;
213 }
214 
get_child_nodes(sd_bus * bus,const char * prefix,struct node * n,unsigned flags,OrderedSet ** ret,sd_bus_error * error)215 static int get_child_nodes(
216                 sd_bus *bus,
217                 const char *prefix,
218                 struct node *n,
219                 unsigned flags,
220                 OrderedSet **ret,
221                 sd_bus_error *error) {
222 
223         _cleanup_ordered_set_free_free_ OrderedSet *s = NULL;
224         int r;
225 
226         assert(bus);
227         assert(prefix);
228         assert(n);
229         assert(ret);
230 
231         s = ordered_set_new(&string_hash_ops);
232         if (!s)
233                 return -ENOMEM;
234 
235         r = add_subtree_to_set(bus, prefix, n, flags, s, error);
236         if (r < 0)
237                 return r;
238 
239         *ret = TAKE_PTR(s);
240         return 0;
241 }
242 
node_callbacks_run(sd_bus * bus,sd_bus_message * m,struct node_callback * first,bool require_fallback,bool * found_object)243 static int node_callbacks_run(
244                 sd_bus *bus,
245                 sd_bus_message *m,
246                 struct node_callback *first,
247                 bool require_fallback,
248                 bool *found_object) {
249 
250         int r;
251 
252         assert(bus);
253         assert(m);
254         assert(found_object);
255 
256         LIST_FOREACH(callbacks, c, first) {
257                 _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
258                 sd_bus_slot *slot;
259 
260                 if (bus->nodes_modified)
261                         return 0;
262 
263                 if (require_fallback && !c->is_fallback)
264                         continue;
265 
266                 *found_object = true;
267 
268                 if (c->last_iteration == bus->iteration_counter)
269                         continue;
270 
271                 c->last_iteration = bus->iteration_counter;
272 
273                 r = sd_bus_message_rewind(m, true);
274                 if (r < 0)
275                         return r;
276 
277                 slot = container_of(c, sd_bus_slot, node_callback);
278 
279                 bus->current_slot = sd_bus_slot_ref(slot);
280                 bus->current_handler = c->callback;
281                 bus->current_userdata = slot->userdata;
282                 r = c->callback(m, slot->userdata, &error_buffer);
283                 bus->current_userdata = NULL;
284                 bus->current_handler = NULL;
285                 bus->current_slot = sd_bus_slot_unref(slot);
286 
287                 r = bus_maybe_reply_error(m, r, &error_buffer);
288                 if (r != 0)
289                         return r;
290         }
291 
292         return 0;
293 }
294 
295 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
296 
check_access(sd_bus * bus,sd_bus_message * m,struct vtable_member * c,sd_bus_error * error)297 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
298         uint64_t cap;
299         int r;
300 
301         assert(bus);
302         assert(m);
303         assert(c);
304 
305         /* If the entire bus is trusted let's grant access */
306         if (bus->trusted)
307                 return 0;
308 
309         /* If the member is marked UNPRIVILEGED let's grant access */
310         if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
311                 return 0;
312 
313         /* Check that the caller has the requested capability set. Note that the flags value contains the
314          * capability number plus one, which we need to subtract here. We do this so that we have 0 as
315          * special value for the default. */
316         cap = CAPABILITY_SHIFT(c->vtable->flags);
317         if (cap == 0)
318                 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
319         if (cap == 0)
320                 cap = CAP_SYS_ADMIN;
321         else
322                 cap--;
323 
324         r = sd_bus_query_sender_privilege(m, cap);
325         if (r < 0)
326                 return r;
327         if (r > 0)
328                 return 0;
329 
330         return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
331 }
332 
method_callbacks_run(sd_bus * bus,sd_bus_message * m,struct vtable_member * c,bool require_fallback,bool * found_object)333 static int method_callbacks_run(
334                 sd_bus *bus,
335                 sd_bus_message *m,
336                 struct vtable_member *c,
337                 bool require_fallback,
338                 bool *found_object) {
339 
340         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
341         const char *signature;
342         void *u;
343         int r;
344 
345         assert(bus);
346         assert(m);
347         assert(c);
348         assert(found_object);
349 
350         if (require_fallback && !c->parent->is_fallback)
351                 return 0;
352 
353         if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
354                 r = sd_bus_message_sensitive(m);
355                 if (r < 0)
356                         return r;
357         }
358 
359         r = check_access(bus, m, c, &error);
360         if (r < 0)
361                 return bus_maybe_reply_error(m, r, &error);
362 
363         r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
364         if (r <= 0)
365                 return bus_maybe_reply_error(m, r, &error);
366         if (bus->nodes_modified)
367                 return 0;
368 
369         u = vtable_method_convert_userdata(c->vtable, u);
370 
371         *found_object = true;
372 
373         if (c->last_iteration == bus->iteration_counter)
374                 return 0;
375 
376         c->last_iteration = bus->iteration_counter;
377 
378         r = sd_bus_message_rewind(m, true);
379         if (r < 0)
380                 return r;
381 
382         signature = sd_bus_message_get_signature(m, true);
383         if (!signature)
384                 return -EINVAL;
385 
386         if (!streq(strempty(c->vtable->x.method.signature), signature))
387                 return sd_bus_reply_method_errorf(
388                                 m,
389                                 SD_BUS_ERROR_INVALID_ARGS,
390                                 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
391                                 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
392 
393         /* Keep track what the signature of the reply to this message
394          * should be, so that this can be enforced when sealing the
395          * reply. */
396         m->enforced_reply_signature = strempty(c->vtable->x.method.result);
397 
398         if (c->vtable->x.method.handler) {
399                 sd_bus_slot *slot;
400 
401                 slot = container_of(c->parent, sd_bus_slot, node_vtable);
402 
403                 bus->current_slot = sd_bus_slot_ref(slot);
404                 bus->current_handler = c->vtable->x.method.handler;
405                 bus->current_userdata = u;
406                 r = c->vtable->x.method.handler(m, u, &error);
407                 bus->current_userdata = NULL;
408                 bus->current_handler = NULL;
409                 bus->current_slot = sd_bus_slot_unref(slot);
410 
411                 return bus_maybe_reply_error(m, r, &error);
412         }
413 
414         /* If the method callback is NULL, make this a successful NOP */
415         r = sd_bus_reply_method_return(m, NULL);
416         if (r < 0)
417                 return r;
418 
419         return 1;
420 }
421 
invoke_property_get(sd_bus * bus,sd_bus_slot * slot,const sd_bus_vtable * v,const char * path,const char * interface,const char * property,sd_bus_message * reply,void * userdata,sd_bus_error * error)422 static int invoke_property_get(
423                 sd_bus *bus,
424                 sd_bus_slot *slot,
425                 const sd_bus_vtable *v,
426                 const char *path,
427                 const char *interface,
428                 const char *property,
429                 sd_bus_message *reply,
430                 void *userdata,
431                 sd_bus_error *error) {
432 
433         const void *p;
434         int r;
435 
436         assert(bus);
437         assert(slot);
438         assert(v);
439         assert(path);
440         assert(interface);
441         assert(property);
442         assert(reply);
443 
444         if (v->x.property.get) {
445 
446                 bus->current_slot = sd_bus_slot_ref(slot);
447                 bus->current_userdata = userdata;
448                 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
449                 bus->current_userdata = NULL;
450                 bus->current_slot = sd_bus_slot_unref(slot);
451 
452                 if (r < 0)
453                         return r;
454                 if (sd_bus_error_is_set(error))
455                         return -sd_bus_error_get_errno(error);
456                 return r;
457         }
458 
459         /* Automatic handling if no callback is defined. */
460 
461         if (streq(v->x.property.signature, "as"))
462                 return sd_bus_message_append_strv(reply, *(char***) userdata);
463 
464         assert(signature_is_single(v->x.property.signature, false));
465         assert(bus_type_is_basic(v->x.property.signature[0]));
466 
467         switch (v->x.property.signature[0]) {
468 
469         case SD_BUS_TYPE_STRING:
470         case SD_BUS_TYPE_SIGNATURE:
471                 p = strempty(*(char**) userdata);
472                 break;
473 
474         case SD_BUS_TYPE_OBJECT_PATH:
475                 p = *(char**) userdata;
476                 assert(p);
477                 break;
478 
479         default:
480                 p = userdata;
481                 break;
482         }
483 
484         return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
485 }
486 
invoke_property_set(sd_bus * bus,sd_bus_slot * slot,const sd_bus_vtable * v,const char * path,const char * interface,const char * property,sd_bus_message * value,void * userdata,sd_bus_error * error)487 static int invoke_property_set(
488                 sd_bus *bus,
489                 sd_bus_slot *slot,
490                 const sd_bus_vtable *v,
491                 const char *path,
492                 const char *interface,
493                 const char *property,
494                 sd_bus_message *value,
495                 void *userdata,
496                 sd_bus_error *error) {
497 
498         int r;
499 
500         assert(bus);
501         assert(slot);
502         assert(v);
503         assert(path);
504         assert(interface);
505         assert(property);
506         assert(value);
507 
508         if (v->x.property.set) {
509 
510                 bus->current_slot = sd_bus_slot_ref(slot);
511                 bus->current_userdata = userdata;
512                 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
513                 bus->current_userdata = NULL;
514                 bus->current_slot = sd_bus_slot_unref(slot);
515 
516                 if (r < 0)
517                         return r;
518                 if (sd_bus_error_is_set(error))
519                         return -sd_bus_error_get_errno(error);
520                 return r;
521         }
522 
523         /*  Automatic handling if no callback is defined. */
524 
525         assert(signature_is_single(v->x.property.signature, false));
526         assert(bus_type_is_basic(v->x.property.signature[0]));
527 
528         switch (v->x.property.signature[0]) {
529 
530         case SD_BUS_TYPE_STRING:
531         case SD_BUS_TYPE_OBJECT_PATH:
532         case SD_BUS_TYPE_SIGNATURE: {
533                 const char *p;
534                 char *n;
535 
536                 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
537                 if (r < 0)
538                         return r;
539 
540                 n = strdup(p);
541                 if (!n)
542                         return -ENOMEM;
543 
544                 free(*(char**) userdata);
545                 *(char**) userdata = n;
546 
547                 break;
548         }
549 
550         default:
551                 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
552                 if (r < 0)
553                         return r;
554 
555                 break;
556         }
557 
558         return 1;
559 }
560 
property_get_set_callbacks_run(sd_bus * bus,sd_bus_message * m,struct vtable_member * c,bool require_fallback,bool is_get,bool * found_object)561 static int property_get_set_callbacks_run(
562                 sd_bus *bus,
563                 sd_bus_message *m,
564                 struct vtable_member *c,
565                 bool require_fallback,
566                 bool is_get,
567                 bool *found_object) {
568 
569         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
570         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
571         sd_bus_slot *slot;
572         void *u = NULL;
573         int r;
574 
575         assert(bus);
576         assert(m);
577         assert(c);
578         assert(found_object);
579 
580         if (require_fallback && !c->parent->is_fallback)
581                 return 0;
582 
583         if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
584                 r = sd_bus_message_sensitive(m);
585                 if (r < 0)
586                         return r;
587         }
588 
589         r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
590         if (r <= 0)
591                 return bus_maybe_reply_error(m, r, &error);
592         if (bus->nodes_modified)
593                 return 0;
594 
595         slot = container_of(c->parent, sd_bus_slot, node_vtable);
596 
597         *found_object = true;
598 
599         r = sd_bus_message_new_method_return(m, &reply);
600         if (r < 0)
601                 return r;
602 
603         if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
604                 r = sd_bus_message_sensitive(reply);
605                 if (r < 0)
606                         return r;
607         }
608 
609         if (is_get) {
610                 /* Note that we do not protect against reexecution
611                  * here (using the last_iteration check, see below),
612                  * should the node tree have changed and we got called
613                  * again. We assume that property Get() calls are
614                  * ultimately without side-effects or if they aren't
615                  * then at least idempotent. */
616 
617                 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
618                 if (r < 0)
619                         return r;
620 
621                 /* Note that we do not do an access check here. Read
622                  * access to properties is always unrestricted, since
623                  * PropertiesChanged signals broadcast contents
624                  * anyway. */
625 
626                 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
627                 if (r < 0)
628                         return bus_maybe_reply_error(m, r, &error);
629 
630                 if (bus->nodes_modified)
631                         return 0;
632 
633                 r = sd_bus_message_close_container(reply);
634                 if (r < 0)
635                         return r;
636 
637         } else {
638                 const char *signature = NULL;
639                 char type = 0;
640 
641                 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
642                         return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
643 
644                 /* Avoid that we call the set routine more than once
645                  * if the processing of this message got restarted
646                  * because the node tree changed. */
647                 if (c->last_iteration == bus->iteration_counter)
648                         return 0;
649 
650                 c->last_iteration = bus->iteration_counter;
651 
652                 r = sd_bus_message_peek_type(m, &type, &signature);
653                 if (r < 0)
654                         return r;
655 
656                 if (type != 'v')
657                         return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_SIGNATURE,
658                                                           "Incorrect signature when setting property '%s', expected 'v', got '%c'.",
659                                                           c->member, type);
660                 if (!streq(strempty(signature), strempty(c->vtable->x.property.signature)))
661                         return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS,
662                                                           "Incorrect parameters for property '%s', expected '%s', got '%s'.",
663                                                           c->member, strempty(c->vtable->x.property.signature), strempty(signature));
664 
665                 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
666                 if (r < 0)
667                         return r;
668 
669                 r = check_access(bus, m, c, &error);
670                 if (r < 0)
671                         return bus_maybe_reply_error(m, r, &error);
672 
673                 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
674                 if (r < 0)
675                         return bus_maybe_reply_error(m, r, &error);
676 
677                 if (bus->nodes_modified)
678                         return 0;
679 
680                 r = sd_bus_message_exit_container(m);
681                 if (r < 0)
682                         return r;
683         }
684 
685         r = sd_bus_send(bus, reply, NULL);
686         if (r < 0)
687                 return r;
688 
689         return 1;
690 }
691 
vtable_append_one_property(sd_bus * bus,sd_bus_message * reply,const char * path,struct node_vtable * c,const sd_bus_vtable * v,void * userdata,sd_bus_error * error)692 static int vtable_append_one_property(
693                 sd_bus *bus,
694                 sd_bus_message *reply,
695                 const char *path,
696                 struct node_vtable *c,
697                 const sd_bus_vtable *v,
698                 void *userdata,
699                 sd_bus_error *error) {
700 
701         sd_bus_slot *slot;
702         int r;
703 
704         assert(bus);
705         assert(reply);
706         assert(path);
707         assert(c);
708         assert(v);
709 
710         if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
711                 r = sd_bus_message_sensitive(reply);
712                 if (r < 0)
713                         return r;
714         }
715 
716         r = sd_bus_message_open_container(reply, 'e', "sv");
717         if (r < 0)
718                 return r;
719 
720         r = sd_bus_message_append(reply, "s", v->x.property.member);
721         if (r < 0)
722                 return r;
723 
724         r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
725         if (r < 0)
726                 return r;
727 
728         slot = container_of(c, sd_bus_slot, node_vtable);
729 
730         r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
731         if (r < 0)
732                 return r;
733         if (bus->nodes_modified)
734                 return 0;
735 
736         r = sd_bus_message_close_container(reply);
737         if (r < 0)
738                 return r;
739 
740         r = sd_bus_message_close_container(reply);
741         if (r < 0)
742                 return r;
743 
744         return 0;
745 }
746 
vtable_append_all_properties(sd_bus * bus,sd_bus_message * reply,const char * path,struct node_vtable * c,void * userdata,sd_bus_error * error)747 static int vtable_append_all_properties(
748                 sd_bus *bus,
749                 sd_bus_message *reply,
750                 const char *path,
751                 struct node_vtable *c,
752                 void *userdata,
753                 sd_bus_error *error) {
754 
755         const sd_bus_vtable *v;
756         int r;
757 
758         assert(bus);
759         assert(reply);
760         assert(path);
761         assert(c);
762 
763         if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
764                 return 1;
765 
766         v = c->vtable;
767         for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
768                 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
769                         continue;
770 
771                 if (v->flags & SD_BUS_VTABLE_HIDDEN)
772                         continue;
773 
774                 /* Let's not include properties marked as "explicit" in any message that contains a generic
775                  * dump of properties, but only in those generated as a response to an explicit request. */
776                 if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
777                         continue;
778 
779                 /* Let's not include properties marked only for invalidation on change (i.e. in contrast to
780                  * those whose new values are included in PropertiesChanges message) in any signals. This is
781                  * useful to ensure they aren't included in InterfacesAdded messages. */
782                 if (reply->header->type != SD_BUS_MESSAGE_METHOD_RETURN &&
783                     FLAGS_SET(v->flags, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
784                         continue;
785 
786                 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
787                 if (r < 0)
788                         return r;
789                 if (bus->nodes_modified)
790                         return 0;
791         }
792 
793         return 1;
794 }
795 
property_get_all_callbacks_run(sd_bus * bus,sd_bus_message * m,struct node_vtable * first,bool require_fallback,const char * iface,bool * found_object)796 static int property_get_all_callbacks_run(
797                 sd_bus *bus,
798                 sd_bus_message *m,
799                 struct node_vtable *first,
800                 bool require_fallback,
801                 const char *iface,
802                 bool *found_object) {
803 
804         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
805         bool found_interface;
806         int r;
807 
808         assert(bus);
809         assert(m);
810         assert(found_object);
811 
812         r = sd_bus_message_new_method_return(m, &reply);
813         if (r < 0)
814                 return r;
815 
816         r = sd_bus_message_open_container(reply, 'a', "{sv}");
817         if (r < 0)
818                 return r;
819 
820         found_interface = !iface || STR_IN_SET(iface,
821                                                "org.freedesktop.DBus.Properties",
822                                                "org.freedesktop.DBus.Peer",
823                                                "org.freedesktop.DBus.Introspectable");
824 
825         LIST_FOREACH(vtables, c, first) {
826                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
827                 void *u;
828 
829                 if (require_fallback && !c->is_fallback)
830                         continue;
831 
832                 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
833                 if (r < 0)
834                         return bus_maybe_reply_error(m, r, &error);
835                 if (bus->nodes_modified)
836                         return 0;
837                 if (r == 0)
838                         continue;
839 
840                 *found_object = true;
841 
842                 if (iface && !streq(c->interface, iface))
843                         continue;
844                 found_interface = true;
845 
846                 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
847                 if (r < 0)
848                         return bus_maybe_reply_error(m, r, &error);
849                 if (bus->nodes_modified)
850                         return 0;
851         }
852 
853         if (!*found_object)
854                 return 0;
855 
856         if (!found_interface) {
857                 r = sd_bus_reply_method_errorf(
858                                 m,
859                                 SD_BUS_ERROR_UNKNOWN_INTERFACE,
860                                 "Unknown interface '%s'.", iface);
861                 if (r < 0)
862                         return r;
863 
864                 return 1;
865         }
866 
867         r = sd_bus_message_close_container(reply);
868         if (r < 0)
869                 return r;
870 
871         r = sd_bus_send(bus, reply, NULL);
872         if (r < 0)
873                 return r;
874 
875         return 1;
876 }
877 
bus_node_exists(sd_bus * bus,struct node * n,const char * path,bool require_fallback)878 static int bus_node_exists(
879                 sd_bus *bus,
880                 struct node *n,
881                 const char *path,
882                 bool require_fallback) {
883 
884         int r;
885 
886         assert(bus);
887         assert(n);
888         assert(path);
889 
890         /* Tests if there's anything attached directly to this node
891          * for the specified path */
892 
893         if (!require_fallback && (n->enumerators || n->object_managers))
894                 return true;
895 
896         LIST_FOREACH(callbacks, k, n->callbacks) {
897                 if (require_fallback && !k->is_fallback)
898                         continue;
899 
900                 return 1;
901         }
902 
903         LIST_FOREACH(vtables, c, n->vtables) {
904                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
905 
906                 if (require_fallback && !c->is_fallback)
907                         continue;
908 
909                 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
910                 if (r != 0)
911                         return r;
912                 if (bus->nodes_modified)
913                         return 0;
914         }
915 
916         return 0;
917 }
918 
introspect_path(sd_bus * bus,const char * path,struct node * n,bool require_fallback,bool ignore_nodes_modified,bool * found_object,char ** ret,sd_bus_error * error)919 int introspect_path(
920                 sd_bus *bus,
921                 const char *path,
922                 struct node *n,
923                 bool require_fallback,
924                 bool ignore_nodes_modified,
925                 bool *found_object,
926                 char **ret,
927                 sd_bus_error *error) {
928 
929         _cleanup_ordered_set_free_free_ OrderedSet *s = NULL;
930         _cleanup_(introspect_free) struct introspect intro = {};
931         bool empty;
932         int r;
933 
934         if (!n) {
935                 n = hashmap_get(bus->nodes, path);
936                 if (!n)
937                         return -ENOENT;
938         }
939 
940         r = get_child_nodes(bus, path, n, 0, &s, error);
941         if (r < 0)
942                 return r;
943         if (bus->nodes_modified && !ignore_nodes_modified)
944                 return 0;
945 
946         r = introspect_begin(&intro, bus->trusted);
947         if (r < 0)
948                 return r;
949 
950         r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
951         if (r < 0)
952                 return r;
953 
954         empty = ordered_set_isempty(s);
955 
956         LIST_FOREACH(vtables, c, n->vtables) {
957                 if (require_fallback && !c->is_fallback)
958                         continue;
959 
960                 r = node_vtable_get_userdata(bus, path, c, NULL, error);
961                 if (r < 0)
962                         return r;
963                 if (bus->nodes_modified && !ignore_nodes_modified)
964                         return 0;
965                 if (r == 0)
966                         continue;
967 
968                 empty = false;
969 
970                 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
971                         continue;
972 
973                 r = introspect_write_interface(&intro, c->interface, c->vtable);
974                 if (r < 0)
975                         return r;
976         }
977 
978         if (empty) {
979                 /* Nothing?, let's see if we exist at all, and if not
980                  * refuse to do anything */
981                 r = bus_node_exists(bus, n, path, require_fallback);
982                 if (r <= 0)
983                         return r;
984                 if (bus->nodes_modified && !ignore_nodes_modified)
985                         return 0;
986         }
987 
988         if (found_object)
989                 *found_object = true;
990 
991         r = introspect_write_child_nodes(&intro, s, path);
992         if (r < 0)
993                 return r;
994 
995         r = introspect_finish(&intro, ret);
996         if (r < 0)
997                 return r;
998 
999         return 1;
1000 }
1001 
process_introspect(sd_bus * bus,sd_bus_message * m,struct node * n,bool require_fallback,bool * found_object)1002 static int process_introspect(
1003                 sd_bus *bus,
1004                 sd_bus_message *m,
1005                 struct node *n,
1006                 bool require_fallback,
1007                 bool *found_object) {
1008 
1009         _cleanup_free_ char *s = NULL;
1010         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1011         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1012         int r;
1013 
1014         assert(bus);
1015         assert(m);
1016         assert(n);
1017         assert(found_object);
1018 
1019         r = introspect_path(bus, m->path, n, require_fallback, false, found_object, &s, &error);
1020         if (r < 0)
1021                 return bus_maybe_reply_error(m, r, &error);
1022         if (r == 0)
1023                 /* nodes_modified == true */
1024                 return 0;
1025 
1026         r = sd_bus_message_new_method_return(m, &reply);
1027         if (r < 0)
1028                 return r;
1029 
1030         r = sd_bus_message_append(reply, "s", s);
1031         if (r < 0)
1032                 return r;
1033 
1034         r = sd_bus_send(bus, reply, NULL);
1035         if (r < 0)
1036                 return r;
1037 
1038         return 1;
1039 }
1040 
object_manager_serialize_path(sd_bus * bus,sd_bus_message * reply,const char * prefix,const char * path,bool require_fallback,sd_bus_error * error)1041 static int object_manager_serialize_path(
1042                 sd_bus *bus,
1043                 sd_bus_message *reply,
1044                 const char *prefix,
1045                 const char *path,
1046                 bool require_fallback,
1047                 sd_bus_error *error) {
1048 
1049         const char *previous_interface = NULL;
1050         bool found_something = false;
1051         struct node *n;
1052         int r;
1053 
1054         assert(bus);
1055         assert(reply);
1056         assert(prefix);
1057         assert(path);
1058         assert(error);
1059 
1060         n = hashmap_get(bus->nodes, prefix);
1061         if (!n)
1062                 return 0;
1063 
1064         LIST_FOREACH(vtables, i, n->vtables) {
1065                 void *u;
1066 
1067                 if (require_fallback && !i->is_fallback)
1068                         continue;
1069 
1070                 r = node_vtable_get_userdata(bus, path, i, &u, error);
1071                 if (r < 0)
1072                         return r;
1073                 if (bus->nodes_modified)
1074                         return 0;
1075                 if (r == 0)
1076                         continue;
1077 
1078                 if (!found_something) {
1079 
1080                         /* Open the object part */
1081 
1082                         r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1083                         if (r < 0)
1084                                 return r;
1085 
1086                         r = sd_bus_message_append(reply, "o", path);
1087                         if (r < 0)
1088                                 return r;
1089 
1090                         r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1091                         if (r < 0)
1092                                 return r;
1093 
1094                         r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
1095                         if (r < 0)
1096                                 return r;
1097 
1098                         r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
1099                         if (r < 0)
1100                                 return r;
1101 
1102                         r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
1103                         if (r < 0)
1104                                 return r;
1105 
1106                         r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1107                         if (r < 0)
1108                                 return r;
1109 
1110                         found_something = true;
1111                 }
1112 
1113                 if (!streq_ptr(previous_interface, i->interface)) {
1114 
1115                         /* Maybe close the previous interface part */
1116 
1117                         if (previous_interface) {
1118                                 r = sd_bus_message_close_container(reply);
1119                                 if (r < 0)
1120                                         return r;
1121 
1122                                 r = sd_bus_message_close_container(reply);
1123                                 if (r < 0)
1124                                         return r;
1125                         }
1126 
1127                         /* Open the new interface part */
1128 
1129                         r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1130                         if (r < 0)
1131                                 return r;
1132 
1133                         r = sd_bus_message_append(reply, "s", i->interface);
1134                         if (r < 0)
1135                                 return r;
1136 
1137                         r = sd_bus_message_open_container(reply, 'a', "{sv}");
1138                         if (r < 0)
1139                                 return r;
1140                 }
1141 
1142                 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1143                 if (r < 0)
1144                         return r;
1145                 if (bus->nodes_modified)
1146                         return 0;
1147 
1148                 previous_interface = i->interface;
1149         }
1150 
1151         if (previous_interface) {
1152                 r = sd_bus_message_close_container(reply);
1153                 if (r < 0)
1154                         return r;
1155 
1156                 r = sd_bus_message_close_container(reply);
1157                 if (r < 0)
1158                         return r;
1159         }
1160 
1161         if (found_something) {
1162                 r = sd_bus_message_close_container(reply);
1163                 if (r < 0)
1164                         return r;
1165 
1166                 r = sd_bus_message_close_container(reply);
1167                 if (r < 0)
1168                         return r;
1169         }
1170 
1171         return 1;
1172 }
1173 
object_manager_serialize_path_and_fallbacks(sd_bus * bus,sd_bus_message * reply,const char * path,sd_bus_error * error)1174 static int object_manager_serialize_path_and_fallbacks(
1175                 sd_bus *bus,
1176                 sd_bus_message *reply,
1177                 const char *path,
1178                 sd_bus_error *error) {
1179 
1180         _cleanup_free_ char *prefix = NULL;
1181         size_t pl;
1182         int r;
1183 
1184         assert(bus);
1185         assert(reply);
1186         assert(path);
1187         assert(error);
1188 
1189         /* First, add all vtables registered for this path */
1190         r = object_manager_serialize_path(bus, reply, path, path, false, error);
1191         if (r < 0)
1192                 return r;
1193         if (bus->nodes_modified)
1194                 return 0;
1195 
1196         /* Second, add fallback vtables registered for any of the prefixes */
1197         pl = strlen(path);
1198         assert(pl <= BUS_PATH_SIZE_MAX);
1199         prefix = new(char, pl + 1);
1200         if (!prefix)
1201                 return -ENOMEM;
1202 
1203         OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1204                 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1205                 if (r < 0)
1206                         return r;
1207                 if (bus->nodes_modified)
1208                         return 0;
1209         }
1210 
1211         return 0;
1212 }
1213 
process_get_managed_objects(sd_bus * bus,sd_bus_message * m,struct node * n,bool require_fallback,bool * found_object)1214 static int process_get_managed_objects(
1215                 sd_bus *bus,
1216                 sd_bus_message *m,
1217                 struct node *n,
1218                 bool require_fallback,
1219                 bool *found_object) {
1220 
1221         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1222         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1223         _cleanup_ordered_set_free_free_ OrderedSet *s = NULL;
1224         char *path;
1225         int r;
1226 
1227         assert(bus);
1228         assert(m);
1229         assert(n);
1230         assert(found_object);
1231 
1232         /* Spec says, GetManagedObjects() is only implemented on the root of a
1233          * sub-tree. Therefore, we require a registered object-manager on
1234          * exactly the queried path, otherwise, we refuse to respond. */
1235 
1236         if (require_fallback || !n->object_managers)
1237                 return 0;
1238 
1239         r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
1240         if (r < 0)
1241                 return bus_maybe_reply_error(m, r, &error);
1242         if (bus->nodes_modified)
1243                 return 0;
1244 
1245         r = sd_bus_message_new_method_return(m, &reply);
1246         if (r < 0)
1247                 return r;
1248 
1249         r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1250         if (r < 0)
1251                 return r;
1252 
1253         ORDERED_SET_FOREACH(path, s) {
1254                 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1255                 if (r < 0)
1256                         return bus_maybe_reply_error(m, r, &error);
1257 
1258                 if (bus->nodes_modified)
1259                         return 0;
1260         }
1261 
1262         r = sd_bus_message_close_container(reply);
1263         if (r < 0)
1264                 return r;
1265 
1266         r = sd_bus_send(bus, reply, NULL);
1267         if (r < 0)
1268                 return r;
1269 
1270         return 1;
1271 }
1272 
object_find_and_run(sd_bus * bus,sd_bus_message * m,const char * p,bool require_fallback,bool * found_object)1273 static int object_find_and_run(
1274                 sd_bus *bus,
1275                 sd_bus_message *m,
1276                 const char *p,
1277                 bool require_fallback,
1278                 bool *found_object) {
1279 
1280         struct node *n;
1281         struct vtable_member vtable_key, *v;
1282         int r;
1283 
1284         assert(bus);
1285         assert(m);
1286         assert(p);
1287         assert(found_object);
1288 
1289         n = hashmap_get(bus->nodes, p);
1290         if (!n)
1291                 return 0;
1292 
1293         /* First, try object callbacks */
1294         r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1295         if (r != 0)
1296                 return r;
1297         if (bus->nodes_modified)
1298                 return 0;
1299 
1300         if (!m->interface || !m->member)
1301                 return 0;
1302 
1303         /* Then, look for a known method */
1304         vtable_key.path = (char*) p;
1305         vtable_key.interface = m->interface;
1306         vtable_key.member = m->member;
1307 
1308         v = hashmap_get(bus->vtable_methods, &vtable_key);
1309         if (v) {
1310                 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1311                 if (r != 0)
1312                         return r;
1313                 if (bus->nodes_modified)
1314                         return 0;
1315         }
1316 
1317         /* Then, look for a known property */
1318         if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1319                 bool get = false;
1320 
1321                 get = streq(m->member, "Get");
1322 
1323                 if (get || streq(m->member, "Set")) {
1324 
1325                         r = sd_bus_message_rewind(m, true);
1326                         if (r < 0)
1327                                 return r;
1328 
1329                         vtable_key.path = (char*) p;
1330 
1331                         r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1332                         if (r < 0)
1333                                 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1334 
1335                         v = hashmap_get(bus->vtable_properties, &vtable_key);
1336                         if (v) {
1337                                 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1338                                 if (r != 0)
1339                                         return r;
1340                         }
1341 
1342                 } else if (streq(m->member, "GetAll")) {
1343                         const char *iface;
1344 
1345                         r = sd_bus_message_rewind(m, true);
1346                         if (r < 0)
1347                                 return r;
1348 
1349                         r = sd_bus_message_read(m, "s", &iface);
1350                         if (r < 0)
1351                                 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1352 
1353                         if (iface[0] == 0)
1354                                 iface = NULL;
1355 
1356                         r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1357                         if (r != 0)
1358                                 return r;
1359                 }
1360 
1361         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1362 
1363                 if (!isempty(sd_bus_message_get_signature(m, true)))
1364                         return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1365 
1366                 r = process_introspect(bus, m, n, require_fallback, found_object);
1367                 if (r != 0)
1368                         return r;
1369 
1370         } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1371 
1372                 if (!isempty(sd_bus_message_get_signature(m, true)))
1373                         return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1374 
1375                 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1376                 if (r != 0)
1377                         return r;
1378         }
1379 
1380         if (bus->nodes_modified)
1381                 return 0;
1382 
1383         if (!*found_object) {
1384                 r = bus_node_exists(bus, n, m->path, require_fallback);
1385                 if (r < 0)
1386                         return bus_maybe_reply_error(m, r, NULL);
1387                 if (bus->nodes_modified)
1388                         return 0;
1389                 if (r > 0)
1390                         *found_object = true;
1391         }
1392 
1393         return 0;
1394 }
1395 
bus_process_object(sd_bus * bus,sd_bus_message * m)1396 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1397         _cleanup_free_ char *prefix = NULL;
1398         int r;
1399         size_t pl;
1400         bool found_object = false;
1401 
1402         assert(bus);
1403         assert(m);
1404 
1405         if (bus->is_monitor)
1406                 return 0;
1407 
1408         if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1409                 return 0;
1410 
1411         if (hashmap_isempty(bus->nodes))
1412                 return 0;
1413 
1414         /* Never respond to broadcast messages */
1415         if (bus->bus_client && !m->destination)
1416                 return 0;
1417 
1418         assert(m->path);
1419         assert(m->member);
1420 
1421         pl = strlen(m->path);
1422         assert(pl <= BUS_PATH_SIZE_MAX);
1423         prefix = new(char, pl + 1);
1424         if (!prefix)
1425                 return -ENOMEM;
1426 
1427         do {
1428                 bus->nodes_modified = false;
1429 
1430                 r = object_find_and_run(bus, m, m->path, false, &found_object);
1431                 if (r != 0)
1432                         return r;
1433 
1434                 /* Look for fallback prefixes */
1435                 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1436 
1437                         if (bus->nodes_modified)
1438                                 break;
1439 
1440                         r = object_find_and_run(bus, m, prefix, true, &found_object);
1441                         if (r != 0)
1442                                 return r;
1443                 }
1444 
1445         } while (bus->nodes_modified);
1446 
1447         if (!found_object)
1448                 return 0;
1449 
1450         if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1451             sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set")) {
1452                 const char *interface = NULL, *property = NULL;
1453 
1454                 (void) sd_bus_message_rewind(m, true);
1455                 (void) sd_bus_message_read_basic(m, 's', &interface);
1456                 (void) sd_bus_message_read_basic(m, 's', &property);
1457 
1458                 r = sd_bus_reply_method_errorf(
1459                                 m,
1460                                 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1461                                 "Unknown interface %s or property %s.", strnull(interface), strnull(property));
1462         } else
1463                 r = sd_bus_reply_method_errorf(
1464                                 m,
1465                                 SD_BUS_ERROR_UNKNOWN_METHOD,
1466                                 "Unknown method %s or interface %s.", m->member, m->interface);
1467 
1468         if (r < 0)
1469                 return r;
1470 
1471         return 1;
1472 }
1473 
bus_node_allocate(sd_bus * bus,const char * path)1474 static struct node* bus_node_allocate(sd_bus *bus, const char *path) {
1475         struct node *n, *parent;
1476         const char *e;
1477         _cleanup_free_ char *s = NULL;
1478         char *p;
1479         int r;
1480 
1481         assert(bus);
1482         assert(path);
1483         assert(path[0] == '/');
1484 
1485         n = hashmap_get(bus->nodes, path);
1486         if (n)
1487                 return n;
1488 
1489         r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1490         if (r < 0)
1491                 return NULL;
1492 
1493         s = strdup(path);
1494         if (!s)
1495                 return NULL;
1496 
1497         if (streq(path, "/"))
1498                 parent = NULL;
1499         else {
1500                 assert_se(e = strrchr(path, '/'));
1501 
1502                 p = strndupa_safe(path, MAX(1, e - path));
1503 
1504                 parent = bus_node_allocate(bus, p);
1505                 if (!parent)
1506                         return NULL;
1507         }
1508 
1509         n = new0(struct node, 1);
1510         if (!n)
1511                 return NULL;
1512 
1513         n->parent = parent;
1514         n->path = TAKE_PTR(s);
1515 
1516         r = hashmap_put(bus->nodes, n->path, n);
1517         if (r < 0) {
1518                 free(n->path);
1519                 return mfree(n);
1520         }
1521 
1522         if (parent)
1523                 LIST_PREPEND(siblings, parent->child, n);
1524 
1525         return n;
1526 }
1527 
bus_node_gc(sd_bus * b,struct node * n)1528 void bus_node_gc(sd_bus *b, struct node *n) {
1529         assert(b);
1530 
1531         if (!n)
1532                 return;
1533 
1534         if (n->child ||
1535             n->callbacks ||
1536             n->vtables ||
1537             n->enumerators ||
1538             n->object_managers)
1539                 return;
1540 
1541         assert_se(hashmap_remove(b->nodes, n->path) == n);
1542 
1543         if (n->parent)
1544                 LIST_REMOVE(siblings, n->parent->child, n);
1545 
1546         free(n->path);
1547         bus_node_gc(b, n->parent);
1548         free(n);
1549 }
1550 
bus_find_parent_object_manager(sd_bus * bus,struct node ** out,const char * path)1551 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1552         struct node *n;
1553 
1554         assert(bus);
1555         assert(path);
1556 
1557         n = hashmap_get(bus->nodes, path);
1558         if (!n) {
1559                 _cleanup_free_ char *prefix = NULL;
1560                 size_t pl;
1561 
1562                 pl = strlen(path);
1563                 assert(pl <= BUS_PATH_SIZE_MAX);
1564                 prefix = new(char, pl + 1);
1565                 if (!prefix)
1566                         return -ENOMEM;
1567 
1568                 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1569                         n = hashmap_get(bus->nodes, prefix);
1570                         if (n)
1571                                 break;
1572                 }
1573         }
1574 
1575         while (n && !n->object_managers)
1576                 n = n->parent;
1577 
1578         if (out)
1579                 *out = n;
1580         return !!n;
1581 }
1582 
bus_add_object(sd_bus * bus,sd_bus_slot ** slot,bool fallback,const char * path,sd_bus_message_handler_t callback,void * userdata)1583 static int bus_add_object(
1584                 sd_bus *bus,
1585                 sd_bus_slot **slot,
1586                 bool fallback,
1587                 const char *path,
1588                 sd_bus_message_handler_t callback,
1589                 void *userdata) {
1590 
1591         sd_bus_slot *s;
1592         struct node *n;
1593         int r;
1594 
1595         assert_return(bus, -EINVAL);
1596         assert_return(bus = bus_resolve(bus), -ENOPKG);
1597         assert_return(object_path_is_valid(path), -EINVAL);
1598         assert_return(callback, -EINVAL);
1599         assert_return(!bus_pid_changed(bus), -ECHILD);
1600 
1601         n = bus_node_allocate(bus, path);
1602         if (!n)
1603                 return -ENOMEM;
1604 
1605         s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1606         if (!s) {
1607                 r = -ENOMEM;
1608                 goto fail;
1609         }
1610 
1611         s->node_callback.callback = callback;
1612         s->node_callback.is_fallback = fallback;
1613 
1614         s->node_callback.node = n;
1615         LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1616         bus->nodes_modified = true;
1617 
1618         if (slot)
1619                 *slot = s;
1620 
1621         return 0;
1622 
1623 fail:
1624         sd_bus_slot_unref(s);
1625         bus_node_gc(bus, n);
1626 
1627         return r;
1628 }
1629 
sd_bus_add_object(sd_bus * bus,sd_bus_slot ** slot,const char * path,sd_bus_message_handler_t callback,void * userdata)1630 _public_ int sd_bus_add_object(
1631                 sd_bus *bus,
1632                 sd_bus_slot **slot,
1633                 const char *path,
1634                 sd_bus_message_handler_t callback,
1635                 void *userdata) {
1636 
1637         return bus_add_object(bus, slot, false, path, callback, userdata);
1638 }
1639 
sd_bus_add_fallback(sd_bus * bus,sd_bus_slot ** slot,const char * prefix,sd_bus_message_handler_t callback,void * userdata)1640 _public_ int sd_bus_add_fallback(
1641                 sd_bus *bus,
1642                 sd_bus_slot **slot,
1643                 const char *prefix,
1644                 sd_bus_message_handler_t callback,
1645                 void *userdata) {
1646 
1647         return bus_add_object(bus, slot, true, prefix, callback, userdata);
1648 }
1649 
vtable_member_hash_func(const struct vtable_member * m,struct siphash * state)1650 static void vtable_member_hash_func(const struct vtable_member *m, struct siphash *state) {
1651         assert(m);
1652 
1653         string_hash_func(m->path, state);
1654         string_hash_func(m->interface, state);
1655         string_hash_func(m->member, state);
1656 }
1657 
vtable_member_compare_func(const struct vtable_member * x,const struct vtable_member * y)1658 static int vtable_member_compare_func(const struct vtable_member *x, const struct vtable_member *y) {
1659         int r;
1660 
1661         assert(x);
1662         assert(y);
1663 
1664         r = strcmp(x->path, y->path);
1665         if (r != 0)
1666                 return r;
1667 
1668         r = strcmp(x->interface, y->interface);
1669         if (r != 0)
1670                 return r;
1671 
1672         return strcmp(x->member, y->member);
1673 }
1674 
1675 DEFINE_PRIVATE_HASH_OPS(vtable_member_hash_ops, struct vtable_member, vtable_member_hash_func, vtable_member_compare_func);
1676 
1677 typedef enum {
1678         NAMES_FIRST_PART        = 1 << 0, /* first part of argument name list (input names). It is reset by names_are_valid() */
1679         NAMES_PRESENT           = 1 << 1, /* at least one argument name is present, so the names will checked.
1680                                              This flag is set and used internally by names_are_valid(), but needs to be stored across calls for 2-parts list  */
1681         NAMES_SINGLE_PART       = 1 << 2, /* argument name list consisting of a single part */
1682 } names_flags;
1683 
names_are_valid(const char * signature,const char ** names,names_flags * flags)1684 static bool names_are_valid(const char *signature, const char **names, names_flags *flags) {
1685         int r;
1686 
1687         if ((*flags & NAMES_FIRST_PART || *flags & NAMES_SINGLE_PART) && **names != '\0')
1688                 *flags |= NAMES_PRESENT;
1689 
1690         for (;*flags & NAMES_PRESENT;) {
1691                 size_t l;
1692 
1693                 if (!*signature)
1694                         break;
1695 
1696                 r = signature_element_length(signature, &l);
1697                 if (r < 0)
1698                         return false;
1699 
1700                 if (**names != '\0') {
1701                         if (!member_name_is_valid(*names))
1702                                 return false;
1703                         *names += strlen(*names) + 1;
1704                 } else if (*flags & NAMES_PRESENT)
1705                         return false;
1706 
1707                 signature += l;
1708         }
1709         /* let's check if there are more argument names specified than the signature allows */
1710         if (*flags & NAMES_PRESENT && **names != '\0' && !(*flags & NAMES_FIRST_PART))
1711                 return false;
1712         *flags &= ~NAMES_FIRST_PART;
1713         return true;
1714 }
1715 
1716 /* the current version of this struct is defined in sd-bus-vtable.h, but we need to list here the historical versions
1717    to make sure the calling code is compatible with one of these */
1718 struct sd_bus_vtable_221 {
1719         uint8_t type:8;
1720         uint64_t flags:56;
1721         union {
1722                 struct {
1723                         size_t element_size;
1724                 } start;
1725                 struct {
1726                         const char *member;
1727                         const char *signature;
1728                         const char *result;
1729                         sd_bus_message_handler_t handler;
1730                         size_t offset;
1731                 } method;
1732                 struct {
1733                         const char *member;
1734                         const char *signature;
1735                 } signal;
1736                 struct {
1737                         const char *member;
1738                         const char *signature;
1739                         sd_bus_property_get_t get;
1740                         sd_bus_property_set_t set;
1741                         size_t offset;
1742                 } property;
1743         } x;
1744 };
1745 /* Structure size up to v241 */
1746 #define VTABLE_ELEMENT_SIZE_221 sizeof(struct sd_bus_vtable_221)
1747 
1748 /* Size of the structure when "features" field was added. If the structure definition is augmented, a copy of
1749  * the structure definition will need to be made (similarly to the sd_bus_vtable_221 above), and this
1750  * definition updated to refer to it. */
1751 #define VTABLE_ELEMENT_SIZE_242 sizeof(struct sd_bus_vtable)
1752 
vtable_features(const sd_bus_vtable * vtable)1753 static int vtable_features(const sd_bus_vtable *vtable) {
1754         if (vtable[0].x.start.element_size < VTABLE_ELEMENT_SIZE_242 ||
1755             !vtable[0].x.start.vtable_format_reference)
1756                 return 0;
1757         return vtable[0].x.start.features;
1758 }
1759 
bus_vtable_has_names(const sd_bus_vtable * vtable)1760 bool bus_vtable_has_names(const sd_bus_vtable *vtable) {
1761         return vtable_features(vtable) & _SD_BUS_VTABLE_PARAM_NAMES;
1762 }
1763 
bus_vtable_next(const sd_bus_vtable * vtable,const sd_bus_vtable * v)1764 const sd_bus_vtable* bus_vtable_next(const sd_bus_vtable *vtable, const sd_bus_vtable *v) {
1765         return (const sd_bus_vtable*) ((char*) v + vtable[0].x.start.element_size);
1766 }
1767 
add_object_vtable_internal(sd_bus * bus,sd_bus_slot ** slot,const char * path,const char * interface,const sd_bus_vtable * vtable,bool fallback,sd_bus_object_find_t find,void * userdata)1768 static int add_object_vtable_internal(
1769                 sd_bus *bus,
1770                 sd_bus_slot **slot,
1771                 const char *path,
1772                 const char *interface,
1773                 const sd_bus_vtable *vtable,
1774                 bool fallback,
1775                 sd_bus_object_find_t find,
1776                 void *userdata) {
1777 
1778         sd_bus_slot *s = NULL;
1779         struct node_vtable *existing = NULL;
1780         const sd_bus_vtable *v;
1781         struct node *n;
1782         int r;
1783         const char *names = "";
1784         names_flags nf;
1785 
1786         assert_return(bus, -EINVAL);
1787         assert_return(bus = bus_resolve(bus), -ENOPKG);
1788         assert_return(object_path_is_valid(path), -EINVAL);
1789         assert_return(interface_name_is_valid(interface), -EINVAL);
1790         assert_return(vtable, -EINVAL);
1791         assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1792         assert_return(vtable[0].x.start.element_size == VTABLE_ELEMENT_SIZE_221 ||
1793                       vtable[0].x.start.element_size >= VTABLE_ELEMENT_SIZE_242,
1794                       -EINVAL);
1795         assert_return(!bus_pid_changed(bus), -ECHILD);
1796         assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1797                       !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1798                       !streq(interface, "org.freedesktop.DBus.Peer") &&
1799                       !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1800 
1801         r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1802         if (r < 0)
1803                 return r;
1804 
1805         r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1806         if (r < 0)
1807                 return r;
1808 
1809         n = bus_node_allocate(bus, path);
1810         if (!n)
1811                 return -ENOMEM;
1812 
1813         LIST_FOREACH(vtables, i, n->vtables) {
1814                 if (i->is_fallback != fallback) {
1815                         r = -EPROTOTYPE;
1816                         goto fail;
1817                 }
1818 
1819                 if (streq(i->interface, interface)) {
1820 
1821                         if (i->vtable == vtable) {
1822                                 r = -EEXIST;
1823                                 goto fail;
1824                         }
1825 
1826                         existing = i;
1827                 }
1828         }
1829 
1830         s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1831         if (!s) {
1832                 r = -ENOMEM;
1833                 goto fail;
1834         }
1835 
1836         s->node_vtable.is_fallback = fallback;
1837         s->node_vtable.vtable = vtable;
1838         s->node_vtable.find = find;
1839 
1840         s->node_vtable.interface = strdup(interface);
1841         if (!s->node_vtable.interface) {
1842                 r = -ENOMEM;
1843                 goto fail;
1844         }
1845 
1846         v = s->node_vtable.vtable;
1847         for (v = bus_vtable_next(vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) {
1848 
1849                 switch (v->type) {
1850 
1851                 case _SD_BUS_VTABLE_METHOD: {
1852                         struct vtable_member *m;
1853                         nf = NAMES_FIRST_PART;
1854 
1855                         if (bus_vtable_has_names(vtable))
1856                                 names = strempty(v->x.method.names);
1857 
1858                         if (!member_name_is_valid(v->x.method.member) ||
1859                             !signature_is_valid(strempty(v->x.method.signature), false) ||
1860                             !signature_is_valid(strempty(v->x.method.result), false) ||
1861                             !names_are_valid(strempty(v->x.method.signature), &names, &nf) ||
1862                             !names_are_valid(strempty(v->x.method.result), &names, &nf) ||
1863                             !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1864                             v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1865                                 r = -EINVAL;
1866                                 goto fail;
1867                         }
1868 
1869                         m = new0(struct vtable_member, 1);
1870                         if (!m) {
1871                                 r = -ENOMEM;
1872                                 goto fail;
1873                         }
1874 
1875                         m->parent = &s->node_vtable;
1876                         m->path = n->path;
1877                         m->interface = s->node_vtable.interface;
1878                         m->member = v->x.method.member;
1879                         m->vtable = v;
1880 
1881                         r = hashmap_put(bus->vtable_methods, m, m);
1882                         if (r < 0) {
1883                                 free(m);
1884                                 goto fail;
1885                         }
1886 
1887                         break;
1888                 }
1889 
1890                 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1891 
1892                         if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1893                                 r = -EINVAL;
1894                                 goto fail;
1895                         }
1896 
1897                         if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1898                                 r = -EINVAL;
1899                                 goto fail;
1900                         }
1901 
1902                         _fallthrough_;
1903                 case _SD_BUS_VTABLE_PROPERTY: {
1904                         struct vtable_member *m;
1905 
1906                         if (!member_name_is_valid(v->x.property.member) ||
1907                             !signature_is_single(v->x.property.signature, false) ||
1908                             !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1909                             (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1910                             (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1911                             ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1912                             (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1913                                 r = -EINVAL;
1914                                 goto fail;
1915                         }
1916 
1917                         m = new0(struct vtable_member, 1);
1918                         if (!m) {
1919                                 r = -ENOMEM;
1920                                 goto fail;
1921                         }
1922 
1923                         m->parent = &s->node_vtable;
1924                         m->path = n->path;
1925                         m->interface = s->node_vtable.interface;
1926                         m->member = v->x.property.member;
1927                         m->vtable = v;
1928 
1929                         r = hashmap_put(bus->vtable_properties, m, m);
1930                         if (r < 0) {
1931                                 free(m);
1932                                 goto fail;
1933                         }
1934 
1935                         break;
1936                 }
1937 
1938                 case _SD_BUS_VTABLE_SIGNAL:
1939                         nf = NAMES_SINGLE_PART;
1940 
1941                         if (bus_vtable_has_names(vtable))
1942                                 names = strempty(v->x.signal.names);
1943 
1944                         if (!member_name_is_valid(v->x.signal.member) ||
1945                             !signature_is_valid(strempty(v->x.signal.signature), false) ||
1946                             !names_are_valid(strempty(v->x.signal.signature), &names, &nf) ||
1947                             v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1948                                 r = -EINVAL;
1949                                 goto fail;
1950                         }
1951 
1952                         break;
1953 
1954                 default:
1955                         r = -EINVAL;
1956                         goto fail;
1957                 }
1958         }
1959 
1960         s->node_vtable.node = n;
1961         LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1962         bus->nodes_modified = true;
1963 
1964         if (slot)
1965                 *slot = s;
1966 
1967         return 0;
1968 
1969 fail:
1970         sd_bus_slot_unref(s);
1971         bus_node_gc(bus, n);
1972 
1973         return r;
1974 }
1975 
1976 /* This symbol exists solely to tell the linker that the "new" vtable format is used. */
1977 _public_ const unsigned sd_bus_object_vtable_format = 242;
1978 
sd_bus_add_object_vtable(sd_bus * bus,sd_bus_slot ** slot,const char * path,const char * interface,const sd_bus_vtable * vtable,void * userdata)1979 _public_ int sd_bus_add_object_vtable(
1980                 sd_bus *bus,
1981                 sd_bus_slot **slot,
1982                 const char *path,
1983                 const char *interface,
1984                 const sd_bus_vtable *vtable,
1985                 void *userdata) {
1986 
1987         return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1988 }
1989 
sd_bus_add_fallback_vtable(sd_bus * bus,sd_bus_slot ** slot,const char * prefix,const char * interface,const sd_bus_vtable * vtable,sd_bus_object_find_t find,void * userdata)1990 _public_ int sd_bus_add_fallback_vtable(
1991                 sd_bus *bus,
1992                 sd_bus_slot **slot,
1993                 const char *prefix,
1994                 const char *interface,
1995                 const sd_bus_vtable *vtable,
1996                 sd_bus_object_find_t find,
1997                 void *userdata) {
1998 
1999         return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
2000 }
2001 
sd_bus_add_node_enumerator(sd_bus * bus,sd_bus_slot ** slot,const char * path,sd_bus_node_enumerator_t callback,void * userdata)2002 _public_ int sd_bus_add_node_enumerator(
2003                 sd_bus *bus,
2004                 sd_bus_slot **slot,
2005                 const char *path,
2006                 sd_bus_node_enumerator_t callback,
2007                 void *userdata) {
2008 
2009         sd_bus_slot *s;
2010         struct node *n;
2011         int r;
2012 
2013         assert_return(bus, -EINVAL);
2014         assert_return(bus = bus_resolve(bus), -ENOPKG);
2015         assert_return(object_path_is_valid(path), -EINVAL);
2016         assert_return(callback, -EINVAL);
2017         assert_return(!bus_pid_changed(bus), -ECHILD);
2018 
2019         n = bus_node_allocate(bus, path);
2020         if (!n)
2021                 return -ENOMEM;
2022 
2023         s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
2024         if (!s) {
2025                 r = -ENOMEM;
2026                 goto fail;
2027         }
2028 
2029         s->node_enumerator.callback = callback;
2030 
2031         s->node_enumerator.node = n;
2032         LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
2033         bus->nodes_modified = true;
2034 
2035         if (slot)
2036                 *slot = s;
2037 
2038         return 0;
2039 
2040 fail:
2041         sd_bus_slot_unref(s);
2042         bus_node_gc(bus, n);
2043 
2044         return r;
2045 }
2046 
emit_properties_changed_on_interface(sd_bus * bus,const char * prefix,const char * path,const char * interface,bool require_fallback,bool * found_interface,char ** names)2047 static int emit_properties_changed_on_interface(
2048                 sd_bus *bus,
2049                 const char *prefix,
2050                 const char *path,
2051                 const char *interface,
2052                 bool require_fallback,
2053                 bool *found_interface,
2054                 char **names) {
2055 
2056         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2057         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2058         bool has_invalidating = false, has_changing = false;
2059         struct vtable_member key = {};
2060         struct node *n;
2061         void *u = NULL;
2062         int r;
2063 
2064         assert(bus);
2065         assert(prefix);
2066         assert(path);
2067         assert(interface);
2068         assert(found_interface);
2069 
2070         n = hashmap_get(bus->nodes, prefix);
2071         if (!n)
2072                 return 0;
2073 
2074         r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
2075         if (r < 0)
2076                 return r;
2077 
2078         r = sd_bus_message_append(m, "s", interface);
2079         if (r < 0)
2080                 return r;
2081 
2082         r = sd_bus_message_open_container(m, 'a', "{sv}");
2083         if (r < 0)
2084                 return r;
2085 
2086         key.path = prefix;
2087         key.interface = interface;
2088 
2089         LIST_FOREACH(vtables, c, n->vtables) {
2090                 if (require_fallback && !c->is_fallback)
2091                         continue;
2092 
2093                 if (!streq(c->interface, interface))
2094                         continue;
2095 
2096                 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2097                 if (r < 0)
2098                         return r;
2099                 if (bus->nodes_modified)
2100                         return 0;
2101                 if (r == 0)
2102                         continue;
2103 
2104                 *found_interface = true;
2105 
2106                 if (names) {
2107                         /* If the caller specified a list of
2108                          * properties we include exactly those in the
2109                          * PropertiesChanged message */
2110 
2111                         STRV_FOREACH(property, names) {
2112                                 struct vtable_member *v;
2113 
2114                                 assert_return(member_name_is_valid(*property), -EINVAL);
2115 
2116                                 key.member = *property;
2117                                 v = hashmap_get(bus->vtable_properties, &key);
2118                                 if (!v)
2119                                         return -ENOENT;
2120 
2121                                 /* If there are two vtables for the same
2122                                  * interface, let's handle this property when
2123                                  * we come to that vtable. */
2124                                 if (c != v->parent)
2125                                         continue;
2126 
2127                                 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
2128                                               v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
2129 
2130                                 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
2131 
2132                                 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2133                                         has_invalidating = true;
2134                                         continue;
2135                                 }
2136 
2137                                 has_changing = true;
2138 
2139                                 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
2140                                 if (r < 0)
2141                                         return r;
2142                                 if (bus->nodes_modified)
2143                                         return 0;
2144                         }
2145                 } else {
2146                         const sd_bus_vtable *v;
2147 
2148                         /* If the caller specified no properties list
2149                          * we include all properties that are marked
2150                          * as changing in the message. */
2151 
2152                         v = c->vtable;
2153                         for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
2154                                 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
2155                                         continue;
2156 
2157                                 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2158                                         continue;
2159 
2160                                 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2161                                         has_invalidating = true;
2162                                         continue;
2163                                 }
2164 
2165                                 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2166                                         continue;
2167 
2168                                 has_changing = true;
2169 
2170                                 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2171                                 if (r < 0)
2172                                         return r;
2173                                 if (bus->nodes_modified)
2174                                         return 0;
2175                         }
2176                 }
2177         }
2178 
2179         if (!has_invalidating && !has_changing)
2180                 return 0;
2181 
2182         r = sd_bus_message_close_container(m);
2183         if (r < 0)
2184                 return r;
2185 
2186         r = sd_bus_message_open_container(m, 'a', "s");
2187         if (r < 0)
2188                 return r;
2189 
2190         if (has_invalidating) {
2191                 LIST_FOREACH(vtables, c, n->vtables) {
2192                         if (require_fallback && !c->is_fallback)
2193                                 continue;
2194 
2195                         if (!streq(c->interface, interface))
2196                                 continue;
2197 
2198                         r = node_vtable_get_userdata(bus, path, c, &u, &error);
2199                         if (r < 0)
2200                                 return r;
2201                         if (bus->nodes_modified)
2202                                 return 0;
2203                         if (r == 0)
2204                                 continue;
2205 
2206                         if (names) {
2207                                 STRV_FOREACH(property, names) {
2208                                         struct vtable_member *v;
2209 
2210                                         key.member = *property;
2211                                         assert_se(v = hashmap_get(bus->vtable_properties, &key));
2212                                         assert(c == v->parent);
2213 
2214                                         if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2215                                                 continue;
2216 
2217                                         r = sd_bus_message_append(m, "s", *property);
2218                                         if (r < 0)
2219                                                 return r;
2220                                 }
2221                         } else {
2222                                 const sd_bus_vtable *v;
2223 
2224                                 v = c->vtable;
2225                                 for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
2226                                         if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
2227                                                 continue;
2228 
2229                                         if (v->flags & SD_BUS_VTABLE_HIDDEN)
2230                                                 continue;
2231 
2232                                         if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2233                                                 continue;
2234 
2235                                         r = sd_bus_message_append(m, "s", v->x.property.member);
2236                                         if (r < 0)
2237                                                 return r;
2238                                 }
2239                         }
2240                 }
2241         }
2242 
2243         r = sd_bus_message_close_container(m);
2244         if (r < 0)
2245                 return r;
2246 
2247         r = sd_bus_send(bus, m, NULL);
2248         if (r < 0)
2249                 return r;
2250 
2251         return 1;
2252 }
2253 
sd_bus_emit_properties_changed_strv(sd_bus * bus,const char * path,const char * interface,char ** names)2254 _public_ int sd_bus_emit_properties_changed_strv(
2255                 sd_bus *bus,
2256                 const char *path,
2257                 const char *interface,
2258                 char **names) {
2259 
2260         _cleanup_free_ char *prefix = NULL;
2261         bool found_interface = false;
2262         size_t pl;
2263         int r;
2264 
2265         assert_return(bus, -EINVAL);
2266         assert_return(bus = bus_resolve(bus), -ENOPKG);
2267         assert_return(object_path_is_valid(path), -EINVAL);
2268         assert_return(interface_name_is_valid(interface), -EINVAL);
2269         assert_return(!bus_pid_changed(bus), -ECHILD);
2270 
2271         if (!BUS_IS_OPEN(bus->state))
2272                 return -ENOTCONN;
2273 
2274         /* A non-NULL but empty names list means nothing needs to be
2275            generated. A NULL list OTOH indicates that all properties
2276            that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2277            included in the PropertiesChanged message. */
2278         if (names && names[0] == NULL)
2279                 return 0;
2280 
2281         BUS_DONT_DESTROY(bus);
2282 
2283         pl = strlen(path);
2284         assert(pl <= BUS_PATH_SIZE_MAX);
2285         prefix = new(char, pl + 1);
2286         if (!prefix)
2287                 return -ENOMEM;
2288 
2289         do {
2290                 bus->nodes_modified = false;
2291 
2292                 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2293                 if (r != 0)
2294                         return r;
2295                 if (bus->nodes_modified)
2296                         continue;
2297 
2298                 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2299                         r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2300                         if (r != 0)
2301                                 return r;
2302                         if (bus->nodes_modified)
2303                                 break;
2304                 }
2305 
2306         } while (bus->nodes_modified);
2307 
2308         return found_interface ? 0 : -ENOENT;
2309 }
2310 
sd_bus_emit_properties_changed(sd_bus * bus,const char * path,const char * interface,const char * name,...)2311 _public_ int sd_bus_emit_properties_changed(
2312                 sd_bus *bus,
2313                 const char *path,
2314                 const char *interface,
2315                 const char *name, ...)  {
2316 
2317         char **names;
2318 
2319         assert_return(bus, -EINVAL);
2320         assert_return(bus = bus_resolve(bus), -ENOPKG);
2321         assert_return(object_path_is_valid(path), -EINVAL);
2322         assert_return(interface_name_is_valid(interface), -EINVAL);
2323         assert_return(!bus_pid_changed(bus), -ECHILD);
2324 
2325         if (!BUS_IS_OPEN(bus->state))
2326                 return -ENOTCONN;
2327 
2328         if (!name)
2329                 return 0;
2330 
2331         names = strv_from_stdarg_alloca(name);
2332 
2333         return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2334 }
2335 
object_added_append_all_prefix(sd_bus * bus,sd_bus_message * m,OrderedSet * s,const char * prefix,const char * path,bool require_fallback)2336 static int object_added_append_all_prefix(
2337                 sd_bus *bus,
2338                 sd_bus_message *m,
2339                 OrderedSet *s,
2340                 const char *prefix,
2341                 const char *path,
2342                 bool require_fallback) {
2343 
2344         const char *previous_interface = NULL;
2345         struct node *n;
2346         int r;
2347 
2348         assert(bus);
2349         assert(m);
2350         assert(s);
2351         assert(prefix);
2352         assert(path);
2353 
2354         n = hashmap_get(bus->nodes, prefix);
2355         if (!n)
2356                 return 0;
2357 
2358         LIST_FOREACH(vtables, c, n->vtables) {
2359                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2360                 void *u = NULL;
2361 
2362                 if (require_fallback && !c->is_fallback)
2363                         continue;
2364 
2365                 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2366                 if (r < 0)
2367                         return r;
2368                 if (bus->nodes_modified)
2369                         return 0;
2370                 if (r == 0)
2371                         continue;
2372 
2373                 if (!streq_ptr(c->interface, previous_interface)) {
2374                         /* If a child-node already handled this interface, we
2375                          * skip it on any of its parents. The child vtables
2376                          * always fully override any conflicting vtables of
2377                          * any parent node. */
2378                         if (ordered_set_get(s, c->interface))
2379                                 continue;
2380 
2381                         r = ordered_set_put(s, c->interface);
2382                         if (r < 0)
2383                                 return r;
2384 
2385                         if (previous_interface) {
2386                                 r = sd_bus_message_close_container(m);
2387                                 if (r < 0)
2388                                         return r;
2389                                 r = sd_bus_message_close_container(m);
2390                                 if (r < 0)
2391                                         return r;
2392                         }
2393 
2394                         r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2395                         if (r < 0)
2396                                 return r;
2397                         r = sd_bus_message_append(m, "s", c->interface);
2398                         if (r < 0)
2399                                 return r;
2400                         r = sd_bus_message_open_container(m, 'a', "{sv}");
2401                         if (r < 0)
2402                                 return r;
2403 
2404                         previous_interface = c->interface;
2405                 }
2406 
2407                 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2408                 if (r < 0)
2409                         return r;
2410                 if (bus->nodes_modified)
2411                         return 0;
2412         }
2413 
2414         if (previous_interface) {
2415                 r = sd_bus_message_close_container(m);
2416                 if (r < 0)
2417                         return r;
2418                 r = sd_bus_message_close_container(m);
2419                 if (r < 0)
2420                         return r;
2421         }
2422 
2423         return 0;
2424 }
2425 
object_added_append_all(sd_bus * bus,sd_bus_message * m,const char * path)2426 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2427         _cleanup_ordered_set_free_ OrderedSet *s = NULL;
2428         _cleanup_free_ char *prefix = NULL;
2429         size_t pl;
2430         int r;
2431 
2432         assert(bus);
2433         assert(m);
2434         assert(path);
2435 
2436         /*
2437          * This appends all interfaces registered on path @path. We first add
2438          * the builtin interfaces, which are always available and handled by
2439          * sd-bus. Then, we add all interfaces registered on the exact node,
2440          * followed by all fallback interfaces registered on any parent prefix.
2441          *
2442          * If an interface is registered multiple times on the same node with
2443          * different vtables, we merge all the properties across all vtables.
2444          * However, if a child node has the same interface registered as one of
2445          * its parent nodes has as fallback, we make the child overwrite the
2446          * parent instead of extending it. Therefore, we keep a "Set" of all
2447          * handled interfaces during parent traversal, so we skip interfaces on
2448          * a parent that were overwritten by a child.
2449          */
2450 
2451         s = ordered_set_new(&string_hash_ops);
2452         if (!s)
2453                 return -ENOMEM;
2454 
2455         r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2456         if (r < 0)
2457                 return r;
2458         r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2459         if (r < 0)
2460                 return r;
2461         r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2462         if (r < 0)
2463                 return r;
2464         r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2465         if (r < 0)
2466                 return r;
2467 
2468         r = object_added_append_all_prefix(bus, m, s, path, path, false);
2469         if (r < 0)
2470                 return r;
2471         if (bus->nodes_modified)
2472                 return 0;
2473 
2474         pl = strlen(path);
2475         assert(pl <= BUS_PATH_SIZE_MAX);
2476         prefix = new(char, pl + 1);
2477         if (!prefix)
2478                 return -ENOMEM;
2479 
2480         OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2481                 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2482                 if (r < 0)
2483                         return r;
2484                 if (bus->nodes_modified)
2485                         return 0;
2486         }
2487 
2488         return 0;
2489 }
2490 
sd_bus_emit_object_added(sd_bus * bus,const char * path)2491 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2492         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2493         struct node *object_manager;
2494         int r;
2495 
2496         /*
2497          * This emits an InterfacesAdded signal on the given path, by iterating
2498          * all registered vtables and fallback vtables on the path. All
2499          * properties are queried and included in the signal.
2500          * This call is equivalent to sd_bus_emit_interfaces_added() with an
2501          * explicit list of registered interfaces. However, unlike
2502          * interfaces_added(), this call can figure out the list of supported
2503          * interfaces itself. Furthermore, it properly adds the builtin
2504          * org.freedesktop.DBus.* interfaces.
2505          */
2506 
2507         assert_return(bus, -EINVAL);
2508         assert_return(bus = bus_resolve(bus), -ENOPKG);
2509         assert_return(object_path_is_valid(path), -EINVAL);
2510         assert_return(!bus_pid_changed(bus), -ECHILD);
2511 
2512         if (!BUS_IS_OPEN(bus->state))
2513                 return -ENOTCONN;
2514 
2515         r = bus_find_parent_object_manager(bus, &object_manager, path);
2516         if (r < 0)
2517                 return r;
2518         if (r == 0)
2519                 return -ESRCH;
2520 
2521         BUS_DONT_DESTROY(bus);
2522 
2523         do {
2524                 bus->nodes_modified = false;
2525                 m = sd_bus_message_unref(m);
2526 
2527                 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2528                 if (r < 0)
2529                         return r;
2530 
2531                 r = sd_bus_message_append_basic(m, 'o', path);
2532                 if (r < 0)
2533                         return r;
2534 
2535                 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2536                 if (r < 0)
2537                         return r;
2538 
2539                 r = object_added_append_all(bus, m, path);
2540                 if (r < 0)
2541                         return r;
2542 
2543                 if (bus->nodes_modified)
2544                         continue;
2545 
2546                 r = sd_bus_message_close_container(m);
2547                 if (r < 0)
2548                         return r;
2549 
2550         } while (bus->nodes_modified);
2551 
2552         return sd_bus_send(bus, m, NULL);
2553 }
2554 
object_removed_append_all_prefix(sd_bus * bus,sd_bus_message * m,OrderedSet * s,const char * prefix,const char * path,bool require_fallback)2555 static int object_removed_append_all_prefix(
2556                 sd_bus *bus,
2557                 sd_bus_message *m,
2558                 OrderedSet *s,
2559                 const char *prefix,
2560                 const char *path,
2561                 bool require_fallback) {
2562 
2563         const char *previous_interface = NULL;
2564         struct node *n;
2565         int r;
2566 
2567         assert(bus);
2568         assert(m);
2569         assert(s);
2570         assert(prefix);
2571         assert(path);
2572 
2573         n = hashmap_get(bus->nodes, prefix);
2574         if (!n)
2575                 return 0;
2576 
2577         LIST_FOREACH(vtables, c, n->vtables) {
2578                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2579                 void *u = NULL;
2580 
2581                 if (require_fallback && !c->is_fallback)
2582                         continue;
2583                 if (streq_ptr(c->interface, previous_interface))
2584                         continue;
2585 
2586                 /* If a child-node already handled this interface, we
2587                  * skip it on any of its parents. The child vtables
2588                  * always fully override any conflicting vtables of
2589                  * any parent node. */
2590                 if (ordered_set_get(s, c->interface))
2591                         continue;
2592 
2593                 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2594                 if (r < 0)
2595                         return r;
2596                 if (bus->nodes_modified)
2597                         return 0;
2598                 if (r == 0)
2599                         continue;
2600 
2601                 r = ordered_set_put(s, c->interface);
2602                 if (r < 0)
2603                         return r;
2604 
2605                 r = sd_bus_message_append(m, "s", c->interface);
2606                 if (r < 0)
2607                         return r;
2608 
2609                 previous_interface = c->interface;
2610         }
2611 
2612         return 0;
2613 }
2614 
object_removed_append_all(sd_bus * bus,sd_bus_message * m,const char * path)2615 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2616         _cleanup_ordered_set_free_ OrderedSet *s = NULL;
2617         _cleanup_free_ char *prefix = NULL;
2618         size_t pl;
2619         int r;
2620 
2621         assert(bus);
2622         assert(m);
2623         assert(path);
2624 
2625         /* see sd_bus_emit_object_added() for details */
2626 
2627         s = ordered_set_new(&string_hash_ops);
2628         if (!s)
2629                 return -ENOMEM;
2630 
2631         r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2632         if (r < 0)
2633                 return r;
2634         r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2635         if (r < 0)
2636                 return r;
2637         r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2638         if (r < 0)
2639                 return r;
2640         r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2641         if (r < 0)
2642                 return r;
2643 
2644         r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2645         if (r < 0)
2646                 return r;
2647         if (bus->nodes_modified)
2648                 return 0;
2649 
2650         pl = strlen(path);
2651         assert(pl <= BUS_PATH_SIZE_MAX);
2652         prefix = new(char, pl + 1);
2653         if (!prefix)
2654                 return -ENOMEM;
2655 
2656         OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2657                 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2658                 if (r < 0)
2659                         return r;
2660                 if (bus->nodes_modified)
2661                         return 0;
2662         }
2663 
2664         return 0;
2665 }
2666 
sd_bus_emit_object_removed(sd_bus * bus,const char * path)2667 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2668         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2669         struct node *object_manager;
2670         int r;
2671 
2672         /*
2673          * This is like sd_bus_emit_object_added(), but emits an
2674          * InterfacesRemoved signal on the given path. This only includes any
2675          * registered interfaces but skips the properties. Note that this will
2676          * call into the find() callbacks of any registered vtable. Therefore,
2677          * you must call this function before destroying/unlinking your object.
2678          * Otherwise, the list of interfaces will be incomplete. However, note
2679          * that this will *NOT* call into any property callback. Therefore, the
2680          * object might be in an "destructed" state, as long as we can find it.
2681          */
2682 
2683         assert_return(bus, -EINVAL);
2684         assert_return(bus = bus_resolve(bus), -ENOPKG);
2685         assert_return(object_path_is_valid(path), -EINVAL);
2686         assert_return(!bus_pid_changed(bus), -ECHILD);
2687 
2688         if (!BUS_IS_OPEN(bus->state))
2689                 return -ENOTCONN;
2690 
2691         r = bus_find_parent_object_manager(bus, &object_manager, path);
2692         if (r < 0)
2693                 return r;
2694         if (r == 0)
2695                 return -ESRCH;
2696 
2697         BUS_DONT_DESTROY(bus);
2698 
2699         do {
2700                 bus->nodes_modified = false;
2701                 m = sd_bus_message_unref(m);
2702 
2703                 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2704                 if (r < 0)
2705                         return r;
2706 
2707                 r = sd_bus_message_append_basic(m, 'o', path);
2708                 if (r < 0)
2709                         return r;
2710 
2711                 r = sd_bus_message_open_container(m, 'a', "s");
2712                 if (r < 0)
2713                         return r;
2714 
2715                 r = object_removed_append_all(bus, m, path);
2716                 if (r < 0)
2717                         return r;
2718 
2719                 if (bus->nodes_modified)
2720                         continue;
2721 
2722                 r = sd_bus_message_close_container(m);
2723                 if (r < 0)
2724                         return r;
2725 
2726         } while (bus->nodes_modified);
2727 
2728         return sd_bus_send(bus, m, NULL);
2729 }
2730 
interfaces_added_append_one_prefix(sd_bus * bus,sd_bus_message * m,const char * prefix,const char * path,const char * interface,bool require_fallback)2731 static int interfaces_added_append_one_prefix(
2732                 sd_bus *bus,
2733                 sd_bus_message *m,
2734                 const char *prefix,
2735                 const char *path,
2736                 const char *interface,
2737                 bool require_fallback) {
2738 
2739         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2740         bool found_interface = false;
2741         struct node *n;
2742         void *u = NULL;
2743         int r;
2744 
2745         assert(bus);
2746         assert(m);
2747         assert(prefix);
2748         assert(path);
2749         assert(interface);
2750 
2751         n = hashmap_get(bus->nodes, prefix);
2752         if (!n)
2753                 return 0;
2754 
2755         LIST_FOREACH(vtables, c, n->vtables) {
2756                 if (require_fallback && !c->is_fallback)
2757                         continue;
2758 
2759                 if (!streq(c->interface, interface))
2760                         continue;
2761 
2762                 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2763                 if (r < 0)
2764                         return r;
2765                 if (bus->nodes_modified)
2766                         return 0;
2767                 if (r == 0)
2768                         continue;
2769 
2770                 if (!found_interface) {
2771                         r = sd_bus_message_append_basic(m, 's', interface);
2772                         if (r < 0)
2773                                 return r;
2774 
2775                         r = sd_bus_message_open_container(m, 'a', "{sv}");
2776                         if (r < 0)
2777                                 return r;
2778 
2779                         found_interface = true;
2780                 }
2781 
2782                 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2783                 if (r < 0)
2784                         return r;
2785                 if (bus->nodes_modified)
2786                         return 0;
2787         }
2788 
2789         if (found_interface) {
2790                 r = sd_bus_message_close_container(m);
2791                 if (r < 0)
2792                         return r;
2793         }
2794 
2795         return found_interface;
2796 }
2797 
interfaces_added_append_one(sd_bus * bus,sd_bus_message * m,const char * path,const char * interface)2798 static int interfaces_added_append_one(
2799                 sd_bus *bus,
2800                 sd_bus_message *m,
2801                 const char *path,
2802                 const char *interface) {
2803 
2804         _cleanup_free_ char *prefix = NULL;
2805         size_t pl;
2806         int r;
2807 
2808         assert(bus);
2809         assert(m);
2810         assert(path);
2811         assert(interface);
2812 
2813         r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2814         if (r != 0)
2815                 return r;
2816         if (bus->nodes_modified)
2817                 return 0;
2818 
2819         pl = strlen(path);
2820         assert(pl <= BUS_PATH_SIZE_MAX);
2821         prefix = new(char, pl + 1);
2822         if (!prefix)
2823                 return -ENOMEM;
2824 
2825         OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2826                 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2827                 if (r != 0)
2828                         return r;
2829                 if (bus->nodes_modified)
2830                         return 0;
2831         }
2832 
2833         return -ENOENT;
2834 }
2835 
sd_bus_emit_interfaces_added_strv(sd_bus * bus,const char * path,char ** interfaces)2836 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2837         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2838         struct node *object_manager;
2839         int r;
2840 
2841         assert_return(bus, -EINVAL);
2842         assert_return(bus = bus_resolve(bus), -ENOPKG);
2843         assert_return(object_path_is_valid(path), -EINVAL);
2844         assert_return(!bus_pid_changed(bus), -ECHILD);
2845 
2846         if (!BUS_IS_OPEN(bus->state))
2847                 return -ENOTCONN;
2848 
2849         if (strv_isempty(interfaces))
2850                 return 0;
2851 
2852         r = bus_find_parent_object_manager(bus, &object_manager, path);
2853         if (r < 0)
2854                 return r;
2855         if (r == 0)
2856                 return -ESRCH;
2857 
2858         BUS_DONT_DESTROY(bus);
2859 
2860         do {
2861                 bus->nodes_modified = false;
2862                 m = sd_bus_message_unref(m);
2863 
2864                 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2865                 if (r < 0)
2866                         return r;
2867 
2868                 r = sd_bus_message_append_basic(m, 'o', path);
2869                 if (r < 0)
2870                         return r;
2871 
2872                 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2873                 if (r < 0)
2874                         return r;
2875 
2876                 STRV_FOREACH(i, interfaces) {
2877                         assert_return(interface_name_is_valid(*i), -EINVAL);
2878 
2879                         r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2880                         if (r < 0)
2881                                 return r;
2882 
2883                         r = interfaces_added_append_one(bus, m, path, *i);
2884                         if (r < 0)
2885                                 return r;
2886 
2887                         if (bus->nodes_modified)
2888                                 break;
2889 
2890                         r = sd_bus_message_close_container(m);
2891                         if (r < 0)
2892                                 return r;
2893                 }
2894 
2895                 if (bus->nodes_modified)
2896                         continue;
2897 
2898                 r = sd_bus_message_close_container(m);
2899                 if (r < 0)
2900                         return r;
2901 
2902         } while (bus->nodes_modified);
2903 
2904         return sd_bus_send(bus, m, NULL);
2905 }
2906 
sd_bus_emit_interfaces_added(sd_bus * bus,const char * path,const char * interface,...)2907 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2908         char **interfaces;
2909 
2910         assert_return(bus, -EINVAL);
2911         assert_return(bus = bus_resolve(bus), -ENOPKG);
2912         assert_return(object_path_is_valid(path), -EINVAL);
2913         assert_return(!bus_pid_changed(bus), -ECHILD);
2914 
2915         if (!BUS_IS_OPEN(bus->state))
2916                 return -ENOTCONN;
2917 
2918         interfaces = strv_from_stdarg_alloca(interface);
2919 
2920         return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2921 }
2922 
sd_bus_emit_interfaces_removed_strv(sd_bus * bus,const char * path,char ** interfaces)2923 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2924         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2925         struct node *object_manager;
2926         int r;
2927 
2928         assert_return(bus, -EINVAL);
2929         assert_return(bus = bus_resolve(bus), -ENOPKG);
2930         assert_return(object_path_is_valid(path), -EINVAL);
2931         assert_return(!bus_pid_changed(bus), -ECHILD);
2932 
2933         if (!BUS_IS_OPEN(bus->state))
2934                 return -ENOTCONN;
2935 
2936         if (strv_isempty(interfaces))
2937                 return 0;
2938 
2939         r = bus_find_parent_object_manager(bus, &object_manager, path);
2940         if (r < 0)
2941                 return r;
2942         if (r == 0)
2943                 return -ESRCH;
2944 
2945         r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2946         if (r < 0)
2947                 return r;
2948 
2949         r = sd_bus_message_append_basic(m, 'o', path);
2950         if (r < 0)
2951                 return r;
2952 
2953         r = sd_bus_message_append_strv(m, interfaces);
2954         if (r < 0)
2955                 return r;
2956 
2957         return sd_bus_send(bus, m, NULL);
2958 }
2959 
sd_bus_emit_interfaces_removed(sd_bus * bus,const char * path,const char * interface,...)2960 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2961         char **interfaces;
2962 
2963         assert_return(bus, -EINVAL);
2964         assert_return(bus = bus_resolve(bus), -ENOPKG);
2965         assert_return(object_path_is_valid(path), -EINVAL);
2966         assert_return(!bus_pid_changed(bus), -ECHILD);
2967 
2968         if (!BUS_IS_OPEN(bus->state))
2969                 return -ENOTCONN;
2970 
2971         interfaces = strv_from_stdarg_alloca(interface);
2972 
2973         return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2974 }
2975 
sd_bus_add_object_manager(sd_bus * bus,sd_bus_slot ** slot,const char * path)2976 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2977         sd_bus_slot *s;
2978         struct node *n;
2979         int r;
2980 
2981         assert_return(bus, -EINVAL);
2982         assert_return(bus = bus_resolve(bus), -ENOPKG);
2983         assert_return(object_path_is_valid(path), -EINVAL);
2984         assert_return(!bus_pid_changed(bus), -ECHILD);
2985 
2986         n = bus_node_allocate(bus, path);
2987         if (!n)
2988                 return -ENOMEM;
2989 
2990         s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2991         if (!s) {
2992                 r = -ENOMEM;
2993                 goto fail;
2994         }
2995 
2996         s->node_object_manager.node = n;
2997         LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2998         bus->nodes_modified = true;
2999 
3000         if (slot)
3001                 *slot = s;
3002 
3003         return 0;
3004 
3005 fail:
3006         sd_bus_slot_unref(s);
3007         bus_node_gc(bus, n);
3008 
3009         return r;
3010 }
3011