X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-bus%2Fbus-objects.c;h=b116a5dd10146ddc729abade07fd4bf7d11b3c05;hb=4cdf07519a9582afa9d2deeb6d9f54597b8268a1;hp=c3889b794948b9486d82542c476448f3f8744470;hpb=7fb411f035e68b5f3f5e2893157739c9da9917b0;p=elogind.git diff --git a/src/libsystemd-bus/bus-objects.c b/src/libsystemd-bus/bus-objects.c index c3889b794..b116a5dd1 100644 --- a/src/libsystemd-bus/bus-objects.c +++ b/src/libsystemd-bus/bus-objects.c @@ -617,6 +617,52 @@ static int property_get_set_callbacks_run( return 1; } +static int vtable_append_one_property( + sd_bus *bus, + sd_bus_message *reply, + const char *path, + struct node_vtable *c, + const sd_bus_vtable *v, + void *userdata, + sd_bus_error *error) { + + int r; + + assert(bus); + assert(reply); + assert(path); + assert(c); + assert(v); + + r = sd_bus_message_open_container(reply, 'e', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "s", v->x.property.member); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'v', v->x.property.signature); + if (r < 0) + return r; + + r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return 0; +} + static int vtable_append_all_properties( sd_bus *bus, sd_bus_message *reply, @@ -643,31 +689,11 @@ static int vtable_append_all_properties( if (v->flags & SD_BUS_VTABLE_HIDDEN) continue; - r = sd_bus_message_open_container(reply, 'e', "sv"); - if (r < 0) - return r; - - r = sd_bus_message_append(reply, "s", v->x.property.member); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'v', v->x.property.signature); - if (r < 0) - return r; - - r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error); + r = vtable_append_one_property(bus, reply, path, c, v, userdata, error); if (r < 0) return r; if (bus->nodes_modified) return 0; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; } return 1; @@ -1567,15 +1593,25 @@ static void free_node_vtable(sd_bus *bus, struct node_vtable *w) { free(w); } -static unsigned vtable_member_hash_func(const void *a) { +static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) { const struct vtable_member *m = a; + uint8_t hash_key2[HASH_KEY_SIZE]; + unsigned long ret; assert(m); - return - string_hash_func(m->path) ^ - string_hash_func(m->interface) ^ - string_hash_func(m->member); + ret = string_hash_func(m->path, hash_key); + + /* Use a slightly different hash key for the interface */ + memcpy(hash_key2, hash_key, HASH_KEY_SIZE); + hash_key2[0]++; + ret ^= string_hash_func(m->interface, hash_key2); + + /* And an even different one for the member */ + hash_key2[0]++; + ret ^= string_hash_func(m->member, hash_key2); + + return ret; } static int vtable_member_compare_func(const void *a, const void *b) { @@ -1680,7 +1716,7 @@ static int add_object_vtable_internal( !signature_is_valid(strempty(v->x.method.signature), false) || !signature_is_valid(strempty(v->x.method.result), false) || !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) || - v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) { + v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) { r = -EINVAL; goto fail; } @@ -1722,13 +1758,12 @@ static int add_object_vtable_internal( !signature_is_single(v->x.property.signature, false) || !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) || v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY || - (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) || + (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 || (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) { r = -EINVAL; goto fail; } - m = new0(struct vtable_member, 1); if (!m) { r = -ENOMEM; @@ -1946,6 +1981,7 @@ static int emit_properties_changed_on_interface( const char *path, const char *interface, bool require_fallback, + bool *found_interface, char **names) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1962,6 +1998,7 @@ static int emit_properties_changed_on_interface( assert(prefix); assert(path); assert(interface); + assert(found_interface); n = hashmap_get(bus->nodes, prefix); if (!n) @@ -1997,56 +2034,77 @@ static int emit_properties_changed_on_interface( if (r == 0) continue; - STRV_FOREACH(property, names) { - struct vtable_member *v; + *found_interface = true; + + if (names) { + /* If the caller specified a list of + * properties we include exactly those in the + * PropertiesChanged message */ - assert_return(member_name_is_valid(*property), -EINVAL); + STRV_FOREACH(property, names) { + struct vtable_member *v; - key.member = *property; - v = hashmap_get(bus->vtable_properties, &key); - if (!v) - return -ENOENT; + assert_return(member_name_is_valid(*property), -EINVAL); - /* If there are two vtables for the same - * interface, let's handle this property when - * we come to that vtable. */ - if (c != v->parent) - continue; + key.member = *property; + v = hashmap_get(bus->vtable_properties, &key); + if (!v) + return -ENOENT; + + /* If there are two vtables for the same + * interface, let's handle this property when + * we come to that vtable. */ + if (c != v->parent) + continue; - assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM); + assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE || + v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM); - if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) { - has_invalidating = true; - continue; + assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM); + + if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) { + has_invalidating = true; + continue; + } + + has_changing = true; + + r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; } + } else { + const sd_bus_vtable *v; - has_changing = true; + /* If the caller specified no properties list + * we include all properties that are marked + * as changing in the message. */ - r = sd_bus_message_open_container(m, 'e', "sv"); - if (r < 0) - return r; + for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) + continue; - r = sd_bus_message_append(m, "s", *property); - if (r < 0) - return r; + if (v->flags & SD_BUS_VTABLE_HIDDEN) + continue; - r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature); - if (r < 0) - return r; + if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) { + has_invalidating = true; + continue; + } - r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error); - if (r < 0) - return r; - if (bus->nodes_modified) - return 0; + if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) + continue; - r = sd_bus_message_close_container(m); - if (r < 0) - return r; + has_changing = true; - r = sd_bus_message_close_container(m); - if (r < 0) - return r; + r = vtable_append_one_property(bus, m, m->path, c, v, u, &error); + if (r < 0) + return r; + if (bus->nodes_modified) + return 0; + } } } @@ -2077,19 +2135,38 @@ static int emit_properties_changed_on_interface( if (r == 0) continue; - STRV_FOREACH(property, names) { - struct vtable_member *v; + if (names) { + STRV_FOREACH(property, names) { + struct vtable_member *v; - key.member = *property; - assert_se(v = hashmap_get(bus->vtable_properties, &key)); - assert(c == v->parent); + key.member = *property; + assert_se(v = hashmap_get(bus->vtable_properties, &key)); + assert(c == v->parent); - if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) - continue; + if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) + continue; - r = sd_bus_message_append(m, "s", *property); - if (r < 0) - return r; + r = sd_bus_message_append(m, "s", *property); + if (r < 0) + return r; + } + } else { + const sd_bus_vtable *v; + + for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) + continue; + + if (v->flags & SD_BUS_VTABLE_HIDDEN) + continue; + + if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) + continue; + + r = sd_bus_message_append(m, "s", v->x.property.member); + if (r < 0) + return r; + } } } } @@ -2112,6 +2189,7 @@ _public_ int sd_bus_emit_properties_changed_strv( char **names) { BUS_DONT_DESTROY(bus); + bool found_interface = false; char *prefix; int r; @@ -2121,13 +2199,18 @@ _public_ int sd_bus_emit_properties_changed_strv( assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); assert_return(!bus_pid_changed(bus), -ECHILD); - if (strv_isempty(names)) + + /* A non-NULL but empty names list means nothing needs to be + generated. A NULL list OTOH indicates that all properties + that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be + included in the PropertiesChanged message. */ + if (names && names[0] == NULL) return 0; do { bus->nodes_modified = false; - r = emit_properties_changed_on_interface(bus, path, path, interface, false, names); + r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names); if (r != 0) return r; if (bus->nodes_modified) @@ -2135,7 +2218,7 @@ _public_ int sd_bus_emit_properties_changed_strv( prefix = alloca(strlen(path) + 1); OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names); + r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names); if (r != 0) return r; if (bus->nodes_modified) @@ -2144,7 +2227,7 @@ _public_ int sd_bus_emit_properties_changed_strv( } while (bus->nodes_modified); - return -ENOENT; + return found_interface ? 0 : -ENOENT; } _public_ int sd_bus_emit_properties_changed(