LCOV - code coverage report
Current view: top level - libsystemd/sd-bus - bus-objects.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 1264 1690 74.8 %
Date: 2019-08-22 15:41:25 Functions: 55 56 98.2 %

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

Generated by: LCOV version 1.14