2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2013-10-11 20:18:10 +02:00
|
|
|
#include "bus-internal.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "bus-introspect.h"
|
2013-10-11 20:18:10 +02:00
|
|
|
#include "bus-message.h"
|
2015-11-16 22:09:36 +01:00
|
|
|
#include "bus-objects.h"
|
2013-10-11 20:18:10 +02:00
|
|
|
#include "bus-signature.h"
|
2014-05-15 01:15:30 +02:00
|
|
|
#include "bus-slot.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "bus-type.h"
|
|
|
|
#include "bus-util.h"
|
2018-12-06 07:20:07 +01:00
|
|
|
#include "missing_capability.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "set.h"
|
|
|
|
#include "string-util.h"
|
|
|
|
#include "strv.h"
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
static int node_vtable_get_userdata(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
struct node_vtable *c,
|
2013-11-22 01:14:57 +01:00
|
|
|
void **userdata,
|
|
|
|
sd_bus_error *error) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot *s;
|
2019-04-21 22:25:03 +02:00
|
|
|
void *u, *found_u = NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(path);
|
|
|
|
assert(c);
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s = container_of(c, sd_bus_slot, node_vtable);
|
|
|
|
u = s->userdata;
|
2013-10-11 20:18:10 +02:00
|
|
|
if (c->find) {
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_ref(s);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = u;
|
2017-09-16 13:32:59 +02:00
|
|
|
r = c->find(bus, path, c->interface, u, &found_u, error);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = NULL;
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_unref(s);
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (sd_bus_error_is_set(error))
|
2013-12-15 04:18:49 +01:00
|
|
|
return -sd_bus_error_get_errno(error);
|
2013-11-22 01:14:57 +01:00
|
|
|
if (r == 0)
|
2013-10-11 20:18:10 +02:00
|
|
|
return r;
|
2017-09-16 13:32:59 +02:00
|
|
|
} else
|
|
|
|
found_u = u;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
if (userdata)
|
2017-09-16 13:32:59 +02:00
|
|
|
*userdata = found_u;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-07-24 14:22:54 +02:00
|
|
|
static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
|
|
|
|
assert(p);
|
|
|
|
|
2020-04-28 17:04:08 +02:00
|
|
|
if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET))
|
2020-04-22 19:32:09 +02:00
|
|
|
return SIZE_TO_PTR(p->x.method.offset); /* don't add offset on NULL, to make ubsan happy */
|
|
|
|
|
2015-07-24 14:22:54 +02:00
|
|
|
return (uint8_t*) u + p->x.method.offset;
|
|
|
|
}
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
|
|
|
|
assert(p);
|
|
|
|
|
2020-04-28 17:04:08 +02:00
|
|
|
if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET))
|
2020-04-22 19:32:09 +02:00
|
|
|
return SIZE_TO_PTR(p->x.property.offset); /* as above */
|
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
return (uint8_t*) u + p->x.property.offset;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int vtable_property_get_userdata(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
struct vtable_member *p,
|
2013-11-22 01:14:57 +01:00
|
|
|
void **userdata,
|
|
|
|
sd_bus_error *error) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
void *u;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(path);
|
|
|
|
assert(p);
|
|
|
|
assert(userdata);
|
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r <= 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
*userdata = vtable_property_convert_userdata(p->vtable, u);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
static int add_enumerated_to_set(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *prefix,
|
|
|
|
struct node_enumerator *first,
|
2013-11-22 01:14:57 +01:00
|
|
|
Set *s,
|
|
|
|
sd_bus_error *error) {
|
2013-10-11 21:27:59 +02:00
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node_enumerator *c;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(prefix);
|
|
|
|
assert(s);
|
|
|
|
|
|
|
|
LIST_FOREACH(enumerators, c, first) {
|
|
|
|
char **children = NULL, **k;
|
2014-05-15 17:08:24 +02:00
|
|
|
sd_bus_slot *slot;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
|
2014-05-15 17:08:24 +02:00
|
|
|
slot = container_of(c, sd_bus_slot, node_enumerator);
|
|
|
|
|
|
|
|
bus->current_slot = sd_bus_slot_ref(slot);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = slot->userdata;
|
2014-05-15 17:08:24 +02:00
|
|
|
r = c->callback(bus, prefix, slot->userdata, &children, error);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = NULL;
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_unref(slot);
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-11-22 01:14:57 +01:00
|
|
|
if (sd_bus_error_is_set(error))
|
2013-12-15 04:18:49 +01:00
|
|
|
return -sd_bus_error_get_errno(error);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
STRV_FOREACH(k, children) {
|
|
|
|
if (r < 0) {
|
|
|
|
free(*k);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-02-23 18:52:52 +01:00
|
|
|
if (!object_path_is_valid(*k)) {
|
2013-10-11 20:18:10 +02:00
|
|
|
free(*k);
|
|
|
|
r = -EINVAL;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-11-05 21:24:33 +01:00
|
|
|
if (!object_path_startswith(*k, prefix)) {
|
|
|
|
free(*k);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = set_consume(s, *k);
|
2013-11-22 01:51:19 +01:00
|
|
|
if (r == -EEXIST)
|
|
|
|
r = 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
free(children);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
sd-bus: make introspection data non-recursive
Currently, our introspection data looks like this:
<node>
<interface name="org.freedesktop.DBus.Peer">
...
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
...
</interface>
<interface name="org.freedesktop.DBus.Properties">
...
</interface>
<node name="org"/>
<node name="org/freedesktop"/>
<node name="org/freedesktop/login1"/>
<node name="org/freedesktop/login1/user"/>
<node name="org/freedesktop/login1/user/self"/>
<node name="org/freedesktop/login1/user/_1000"/>
<node name="org/freedesktop/login1/seat"/>
<node name="org/freedesktop/login1/seat/self"/>
<node name="org/freedesktop/login1/seat/seat0"/>
<node name="org/freedesktop/login1/session"/>
<node name="org/freedesktop/login1/session/self"/>
<node name="org/freedesktop/login1/session/c1"/>
</node>
(ordered alphabetically for better visibility)
This is grossly incorrect. The spec says that we're allowed to return
non-directed children, however, it does not allow us to return data
recursively in multiple parents. If we return "org", then we must not
return anything else that starts with "org/".
It is unclear, whether we can include child-nodes as a tree. Moreover, it
is usually not what the caller wants. Hence, this patch changes sd-bus to
never return introspection data recursively. Instead, only a single
child-layer is returned.
This patch relies on enumerators to never return hierarchies. If someone
registers an enumerator via sd_bus_add_enumerator, they better register
sub-enumerators if they support *TRUE* hierarchies. Each enumerator is
treated as a single layer and not filtered.
Enumerators are still allowed to return nested data. However, that data
is still required to be a single hierarchy. For instance, returning
"/org/foo" and "/com/bar" is fine, but including "/com" or "/org" in that
dataset is not.
This should be the default for enumerators and I see no reason to filter
in sd-bus. Moreover, filtering that data-set would require to sort the
strv by path and then do prefix-filtering. This is O(n log n), which
would be fine, but still better to avoid.
Fixes #664.
2015-09-05 19:43:29 +02:00
|
|
|
enum {
|
|
|
|
/* if set, add_subtree() works recursively */
|
2018-06-07 16:03:43 +02:00
|
|
|
CHILDREN_RECURSIVE = 1 << 0,
|
sd-bus: make introspection data non-recursive
Currently, our introspection data looks like this:
<node>
<interface name="org.freedesktop.DBus.Peer">
...
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
...
</interface>
<interface name="org.freedesktop.DBus.Properties">
...
</interface>
<node name="org"/>
<node name="org/freedesktop"/>
<node name="org/freedesktop/login1"/>
<node name="org/freedesktop/login1/user"/>
<node name="org/freedesktop/login1/user/self"/>
<node name="org/freedesktop/login1/user/_1000"/>
<node name="org/freedesktop/login1/seat"/>
<node name="org/freedesktop/login1/seat/self"/>
<node name="org/freedesktop/login1/seat/seat0"/>
<node name="org/freedesktop/login1/session"/>
<node name="org/freedesktop/login1/session/self"/>
<node name="org/freedesktop/login1/session/c1"/>
</node>
(ordered alphabetically for better visibility)
This is grossly incorrect. The spec says that we're allowed to return
non-directed children, however, it does not allow us to return data
recursively in multiple parents. If we return "org", then we must not
return anything else that starts with "org/".
It is unclear, whether we can include child-nodes as a tree. Moreover, it
is usually not what the caller wants. Hence, this patch changes sd-bus to
never return introspection data recursively. Instead, only a single
child-layer is returned.
This patch relies on enumerators to never return hierarchies. If someone
registers an enumerator via sd_bus_add_enumerator, they better register
sub-enumerators if they support *TRUE* hierarchies. Each enumerator is
treated as a single layer and not filtered.
Enumerators are still allowed to return nested data. However, that data
is still required to be a single hierarchy. For instance, returning
"/org/foo" and "/com/bar" is fine, but including "/com" or "/org" in that
dataset is not.
This should be the default for enumerators and I see no reason to filter
in sd-bus. Moreover, filtering that data-set would require to sort the
strv by path and then do prefix-filtering. This is O(n log n), which
would be fine, but still better to avoid.
Fixes #664.
2015-09-05 19:43:29 +02:00
|
|
|
/* if set, add_subtree() scans object-manager hierarchies recursively */
|
2018-06-07 16:03:43 +02:00
|
|
|
CHILDREN_SUBHIERARCHIES = 1 << 1,
|
sd-bus: make introspection data non-recursive
Currently, our introspection data looks like this:
<node>
<interface name="org.freedesktop.DBus.Peer">
...
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
...
</interface>
<interface name="org.freedesktop.DBus.Properties">
...
</interface>
<node name="org"/>
<node name="org/freedesktop"/>
<node name="org/freedesktop/login1"/>
<node name="org/freedesktop/login1/user"/>
<node name="org/freedesktop/login1/user/self"/>
<node name="org/freedesktop/login1/user/_1000"/>
<node name="org/freedesktop/login1/seat"/>
<node name="org/freedesktop/login1/seat/self"/>
<node name="org/freedesktop/login1/seat/seat0"/>
<node name="org/freedesktop/login1/session"/>
<node name="org/freedesktop/login1/session/self"/>
<node name="org/freedesktop/login1/session/c1"/>
</node>
(ordered alphabetically for better visibility)
This is grossly incorrect. The spec says that we're allowed to return
non-directed children, however, it does not allow us to return data
recursively in multiple parents. If we return "org", then we must not
return anything else that starts with "org/".
It is unclear, whether we can include child-nodes as a tree. Moreover, it
is usually not what the caller wants. Hence, this patch changes sd-bus to
never return introspection data recursively. Instead, only a single
child-layer is returned.
This patch relies on enumerators to never return hierarchies. If someone
registers an enumerator via sd_bus_add_enumerator, they better register
sub-enumerators if they support *TRUE* hierarchies. Each enumerator is
treated as a single layer and not filtered.
Enumerators are still allowed to return nested data. However, that data
is still required to be a single hierarchy. For instance, returning
"/org/foo" and "/com/bar" is fine, but including "/com" or "/org" in that
dataset is not.
This should be the default for enumerators and I see no reason to filter
in sd-bus. Moreover, filtering that data-set would require to sort the
strv by path and then do prefix-filtering. This is O(n log n), which
would be fine, but still better to avoid.
Fixes #664.
2015-09-05 19:43:29 +02:00
|
|
|
};
|
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
static int add_subtree_to_set(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *prefix,
|
|
|
|
struct node *n,
|
2018-10-19 20:00:46 +02:00
|
|
|
unsigned flags,
|
2013-11-22 01:14:57 +01:00
|
|
|
Set *s,
|
|
|
|
sd_bus_error *error) {
|
2013-10-11 21:27:59 +02:00
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node *i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(prefix);
|
|
|
|
assert(n);
|
|
|
|
assert(s);
|
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
LIST_FOREACH(siblings, i, n->child) {
|
|
|
|
char *t;
|
|
|
|
|
2013-11-05 21:24:33 +01:00
|
|
|
if (!object_path_startswith(i->path, prefix))
|
|
|
|
continue;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
t = strdup(i->path);
|
|
|
|
if (!t)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
r = set_consume(s, t);
|
|
|
|
if (r < 0 && r != -EEXIST)
|
|
|
|
return r;
|
|
|
|
|
sd-bus: make introspection data non-recursive
Currently, our introspection data looks like this:
<node>
<interface name="org.freedesktop.DBus.Peer">
...
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
...
</interface>
<interface name="org.freedesktop.DBus.Properties">
...
</interface>
<node name="org"/>
<node name="org/freedesktop"/>
<node name="org/freedesktop/login1"/>
<node name="org/freedesktop/login1/user"/>
<node name="org/freedesktop/login1/user/self"/>
<node name="org/freedesktop/login1/user/_1000"/>
<node name="org/freedesktop/login1/seat"/>
<node name="org/freedesktop/login1/seat/self"/>
<node name="org/freedesktop/login1/seat/seat0"/>
<node name="org/freedesktop/login1/session"/>
<node name="org/freedesktop/login1/session/self"/>
<node name="org/freedesktop/login1/session/c1"/>
</node>
(ordered alphabetically for better visibility)
This is grossly incorrect. The spec says that we're allowed to return
non-directed children, however, it does not allow us to return data
recursively in multiple parents. If we return "org", then we must not
return anything else that starts with "org/".
It is unclear, whether we can include child-nodes as a tree. Moreover, it
is usually not what the caller wants. Hence, this patch changes sd-bus to
never return introspection data recursively. Instead, only a single
child-layer is returned.
This patch relies on enumerators to never return hierarchies. If someone
registers an enumerator via sd_bus_add_enumerator, they better register
sub-enumerators if they support *TRUE* hierarchies. Each enumerator is
treated as a single layer and not filtered.
Enumerators are still allowed to return nested data. However, that data
is still required to be a single hierarchy. For instance, returning
"/org/foo" and "/com/bar" is fine, but including "/com" or "/org" in that
dataset is not.
This should be the default for enumerators and I see no reason to filter
in sd-bus. Moreover, filtering that data-set would require to sort the
strv by path and then do prefix-filtering. This is O(n log n), which
would be fine, but still better to avoid.
Fixes #664.
2015-09-05 19:43:29 +02:00
|
|
|
if ((flags & CHILDREN_RECURSIVE) &&
|
|
|
|
((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) {
|
|
|
|
r = add_subtree_to_set(bus, prefix, i, flags, s, error);
|
2015-07-21 12:59:56 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
static int get_child_nodes(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *prefix,
|
|
|
|
struct node *n,
|
2018-10-19 20:00:46 +02:00
|
|
|
unsigned flags,
|
2013-11-22 01:14:57 +01:00
|
|
|
Set **_s,
|
|
|
|
sd_bus_error *error) {
|
2013-10-11 21:27:59 +02:00
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
Set *s = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert(prefix);
|
2013-10-11 20:18:10 +02:00
|
|
|
assert(n);
|
|
|
|
assert(_s);
|
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
s = set_new(&string_hash_ops);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (!s)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
sd-bus: make introspection data non-recursive
Currently, our introspection data looks like this:
<node>
<interface name="org.freedesktop.DBus.Peer">
...
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
...
</interface>
<interface name="org.freedesktop.DBus.Properties">
...
</interface>
<node name="org"/>
<node name="org/freedesktop"/>
<node name="org/freedesktop/login1"/>
<node name="org/freedesktop/login1/user"/>
<node name="org/freedesktop/login1/user/self"/>
<node name="org/freedesktop/login1/user/_1000"/>
<node name="org/freedesktop/login1/seat"/>
<node name="org/freedesktop/login1/seat/self"/>
<node name="org/freedesktop/login1/seat/seat0"/>
<node name="org/freedesktop/login1/session"/>
<node name="org/freedesktop/login1/session/self"/>
<node name="org/freedesktop/login1/session/c1"/>
</node>
(ordered alphabetically for better visibility)
This is grossly incorrect. The spec says that we're allowed to return
non-directed children, however, it does not allow us to return data
recursively in multiple parents. If we return "org", then we must not
return anything else that starts with "org/".
It is unclear, whether we can include child-nodes as a tree. Moreover, it
is usually not what the caller wants. Hence, this patch changes sd-bus to
never return introspection data recursively. Instead, only a single
child-layer is returned.
This patch relies on enumerators to never return hierarchies. If someone
registers an enumerator via sd_bus_add_enumerator, they better register
sub-enumerators if they support *TRUE* hierarchies. Each enumerator is
treated as a single layer and not filtered.
Enumerators are still allowed to return nested data. However, that data
is still required to be a single hierarchy. For instance, returning
"/org/foo" and "/com/bar" is fine, but including "/com" or "/org" in that
dataset is not.
This should be the default for enumerators and I see no reason to filter
in sd-bus. Moreover, filtering that data-set would require to sort the
strv by path and then do prefix-filtering. This is O(n log n), which
would be fine, but still better to avoid.
Fixes #664.
2015-09-05 19:43:29 +02:00
|
|
|
r = add_subtree_to_set(bus, prefix, n, flags, s, error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0) {
|
|
|
|
set_free_free(s);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
*_s = s;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int node_callbacks_run(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
struct node_callback *first,
|
|
|
|
bool require_fallback,
|
|
|
|
bool *found_object) {
|
|
|
|
|
|
|
|
struct node_callback *c;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(found_object);
|
|
|
|
|
|
|
|
LIST_FOREACH(callbacks, c, first) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
|
2014-05-15 17:08:24 +02:00
|
|
|
sd_bus_slot *slot;
|
2013-11-21 19:34:37 +01:00
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
if (require_fallback && !c->is_fallback)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
*found_object = true;
|
|
|
|
|
|
|
|
if (c->last_iteration == bus->iteration_counter)
|
|
|
|
continue;
|
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
c->last_iteration = bus->iteration_counter;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = sd_bus_message_rewind(m, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-05-15 17:08:24 +02:00
|
|
|
slot = container_of(c, sd_bus_slot, node_callback);
|
|
|
|
|
|
|
|
bus->current_slot = sd_bus_slot_ref(slot);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_handler = c->callback;
|
|
|
|
bus->current_userdata = slot->userdata;
|
2015-04-29 18:35:10 +02:00
|
|
|
r = c->callback(m, slot->userdata, &error_buffer);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = NULL;
|
|
|
|
bus->current_handler = NULL;
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_unref(slot);
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2013-11-21 19:34:37 +01:00
|
|
|
r = bus_maybe_reply_error(m, r, &error_buffer);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-10 17:41:39 +01:00
|
|
|
#define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
|
|
|
|
|
|
|
|
static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
|
|
|
|
uint64_t cap;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(c);
|
|
|
|
|
|
|
|
/* If the entire bus is trusted let's grant access */
|
|
|
|
if (bus->trusted)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* If the member is marked UNPRIVILEGED let's grant access */
|
|
|
|
if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Check have the caller has the requested capability
|
|
|
|
* set. Note that the flags value contains the capability
|
|
|
|
* number plus one, which we need to subtract here. We do this
|
|
|
|
* so that we have 0 as special value for "default
|
|
|
|
* capability". */
|
|
|
|
cap = CAPABILITY_SHIFT(c->vtable->flags);
|
|
|
|
if (cap == 0)
|
|
|
|
cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
|
|
|
|
if (cap == 0)
|
|
|
|
cap = CAP_SYS_ADMIN;
|
|
|
|
else
|
2016-02-23 05:32:04 +01:00
|
|
|
cap--;
|
2013-12-10 17:41:39 +01:00
|
|
|
|
2014-08-15 20:08:51 +02:00
|
|
|
r = sd_bus_query_sender_privilege(m, cap);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-12-10 17:41:39 +01:00
|
|
|
if (r > 0)
|
2014-08-15 20:08:51 +02:00
|
|
|
return 0;
|
2013-12-10 17:41:39 +01:00
|
|
|
|
|
|
|
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
|
|
|
|
}
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
static int method_callbacks_run(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
struct vtable_member *c,
|
|
|
|
bool require_fallback,
|
|
|
|
bool *found_object) {
|
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
const char *signature;
|
|
|
|
void *u;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(c);
|
|
|
|
assert(found_object);
|
|
|
|
|
|
|
|
if (require_fallback && !c->parent->is_fallback)
|
|
|
|
return 0;
|
|
|
|
|
2019-08-19 20:28:34 +02:00
|
|
|
if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
|
|
|
|
r = sd_bus_message_sensitive(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-12-10 17:41:39 +01:00
|
|
|
r = check_access(bus, m, c, &error);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r <= 0)
|
2013-11-22 01:14:57 +01:00
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2015-07-24 14:22:54 +02:00
|
|
|
u = vtable_method_convert_userdata(c->vtable, u);
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
*found_object = true;
|
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
if (c->last_iteration == bus->iteration_counter)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
c->last_iteration = bus->iteration_counter;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = sd_bus_message_rewind(m, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-10-16 06:10:04 +02:00
|
|
|
signature = sd_bus_message_get_signature(m, true);
|
|
|
|
if (!signature)
|
|
|
|
return -EINVAL;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
if (!streq(strempty(c->vtable->x.method.signature), signature))
|
2013-11-21 20:05:49 +01:00
|
|
|
return sd_bus_reply_method_errorf(
|
|
|
|
m,
|
|
|
|
SD_BUS_ERROR_INVALID_ARGS,
|
|
|
|
"Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
|
|
|
|
signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-02 15:28:20 +01:00
|
|
|
/* Keep track what the signature of the reply to this message
|
|
|
|
* should be, so that this can be enforced when sealing the
|
|
|
|
* reply. */
|
|
|
|
m->enforced_reply_signature = strempty(c->vtable->x.method.result);
|
|
|
|
|
2013-11-21 19:34:37 +01:00
|
|
|
if (c->vtable->x.method.handler) {
|
2014-05-15 17:08:24 +02:00
|
|
|
sd_bus_slot *slot;
|
|
|
|
|
|
|
|
slot = container_of(c->parent, sd_bus_slot, node_vtable);
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_ref(slot);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_handler = c->vtable->x.method.handler;
|
|
|
|
bus->current_userdata = u;
|
2015-04-29 18:35:10 +02:00
|
|
|
r = c->vtable->x.method.handler(m, u, &error);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = NULL;
|
|
|
|
bus->current_handler = NULL;
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_unref(slot);
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
2013-11-21 19:34:37 +01:00
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
/* If the method callback is NULL, make this a successful NOP */
|
2013-11-21 01:51:16 +01:00
|
|
|
r = sd_bus_reply_method_return(m, NULL);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int invoke_property_get(
|
|
|
|
sd_bus *bus,
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot *slot,
|
2013-10-11 20:18:10 +02:00
|
|
|
const sd_bus_vtable *v,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
2013-11-21 19:34:37 +01:00
|
|
|
sd_bus_message *reply,
|
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-21 00:18:00 +02:00
|
|
|
const void *p;
|
2013-11-22 01:14:57 +01:00
|
|
|
int r;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
assert(bus);
|
2014-05-15 01:15:30 +02:00
|
|
|
assert(slot);
|
2013-10-11 20:18:10 +02:00
|
|
|
assert(v);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert(path);
|
|
|
|
assert(interface);
|
|
|
|
assert(property);
|
2013-11-21 19:34:37 +01:00
|
|
|
assert(reply);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
if (v->x.property.get) {
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_ref(slot);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = userdata;
|
2013-11-22 01:14:57 +01:00
|
|
|
r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = NULL;
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_unref(slot);
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (sd_bus_error_is_set(error))
|
2013-12-15 04:18:49 +01:00
|
|
|
return -sd_bus_error_get_errno(error);
|
2013-11-22 01:14:57 +01:00
|
|
|
return r;
|
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
/* Automatic handling if no callback is defined. */
|
|
|
|
|
2013-11-05 00:49:50 +01:00
|
|
|
if (streq(v->x.property.signature, "as"))
|
2013-11-21 19:34:37 +01:00
|
|
|
return sd_bus_message_append_strv(reply, *(char***) userdata);
|
2013-11-05 00:49:50 +01:00
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
assert(signature_is_single(v->x.property.signature, false));
|
|
|
|
assert(bus_type_is_basic(v->x.property.signature[0]));
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
switch (v->x.property.signature[0]) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
case SD_BUS_TYPE_STRING:
|
2013-10-22 13:40:54 +02:00
|
|
|
case SD_BUS_TYPE_SIGNATURE:
|
|
|
|
p = strempty(*(char**) userdata);
|
2013-10-21 00:18:00 +02:00
|
|
|
break;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
case SD_BUS_TYPE_OBJECT_PATH:
|
|
|
|
p = *(char**) userdata;
|
2013-10-22 13:40:54 +02:00
|
|
|
assert(p);
|
2013-10-11 20:18:10 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
p = userdata;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-11-21 19:34:37 +01:00
|
|
|
return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int invoke_property_set(
|
|
|
|
sd_bus *bus,
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot *slot,
|
2013-10-11 20:18:10 +02:00
|
|
|
const sd_bus_vtable *v,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *property,
|
|
|
|
sd_bus_message *value,
|
2013-11-21 19:34:37 +01:00
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
2014-05-15 01:15:30 +02:00
|
|
|
assert(slot);
|
2013-10-11 20:18:10 +02:00
|
|
|
assert(v);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert(path);
|
|
|
|
assert(interface);
|
|
|
|
assert(property);
|
|
|
|
assert(value);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
if (v->x.property.set) {
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_ref(slot);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = userdata;
|
2013-11-22 01:14:57 +01:00
|
|
|
r = v->x.property.set(bus, path, interface, property, value, userdata, error);
|
2014-08-18 17:41:56 +02:00
|
|
|
bus->current_userdata = NULL;
|
2014-05-15 17:08:24 +02:00
|
|
|
bus->current_slot = sd_bus_slot_unref(slot);
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (sd_bus_error_is_set(error))
|
2013-12-15 04:18:49 +01:00
|
|
|
return -sd_bus_error_get_errno(error);
|
2013-11-22 01:14:57 +01:00
|
|
|
return r;
|
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
/* Automatic handling if no callback is defined. */
|
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
assert(signature_is_single(v->x.property.signature, false));
|
|
|
|
assert(bus_type_is_basic(v->x.property.signature[0]));
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
switch (v->x.property.signature[0]) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
case SD_BUS_TYPE_STRING:
|
|
|
|
case SD_BUS_TYPE_OBJECT_PATH:
|
|
|
|
case SD_BUS_TYPE_SIGNATURE: {
|
|
|
|
const char *p;
|
|
|
|
char *n;
|
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
n = strdup(p);
|
|
|
|
if (!n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
free(*(char**) userdata);
|
|
|
|
*(char**) userdata = n;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2013-10-11 20:32:16 +02:00
|
|
|
r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int property_get_set_callbacks_run(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
struct vtable_member *c,
|
|
|
|
bool require_fallback,
|
|
|
|
bool is_get,
|
|
|
|
bool *found_object) {
|
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot *slot;
|
2014-02-20 17:14:50 +01:00
|
|
|
void *u = NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert(c);
|
2013-10-11 20:18:10 +02:00
|
|
|
assert(found_object);
|
|
|
|
|
|
|
|
if (require_fallback && !c->parent->is_fallback)
|
|
|
|
return 0;
|
|
|
|
|
2019-08-19 20:28:34 +02:00
|
|
|
if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
|
|
|
|
r = sd_bus_message_sensitive(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r <= 0)
|
2013-11-22 01:14:57 +01:00
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
slot = container_of(c->parent, sd_bus_slot, node_vtable);
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
*found_object = true;
|
|
|
|
|
2013-11-21 01:51:16 +01:00
|
|
|
r = sd_bus_message_new_method_return(m, &reply);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-08-19 20:28:34 +02:00
|
|
|
if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
|
|
|
|
r = sd_bus_message_sensitive(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
if (is_get) {
|
2013-10-14 21:58:06 +02:00
|
|
|
/* Note that we do not protect against reexecution
|
|
|
|
* here (using the last_iteration check, see below),
|
|
|
|
* should the node tree have changed and we got called
|
|
|
|
* again. We assume that property Get() calls are
|
|
|
|
* ultimately without side-effects or if they aren't
|
|
|
|
* then at least idempotent. */
|
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-12-10 17:41:39 +01:00
|
|
|
/* Note that we do not do an access check here. Read
|
|
|
|
* access to properties is always unrestricted, since
|
|
|
|
* PropertiesChanged signals broadcast contents
|
|
|
|
* anyway. */
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
2013-11-22 01:14:57 +01:00
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
} else {
|
2014-11-20 20:58:39 +01:00
|
|
|
const char *signature = NULL;
|
|
|
|
char type = 0;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
|
2013-11-21 19:34:37 +01:00
|
|
|
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
|
2013-10-14 21:58:06 +02:00
|
|
|
|
2013-11-21 19:34:37 +01:00
|
|
|
/* Avoid that we call the set routine more than once
|
|
|
|
* if the processing of this message got restarted
|
|
|
|
* because the node tree changed. */
|
|
|
|
if (c->last_iteration == bus->iteration_counter)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-11-21 19:34:37 +01:00
|
|
|
c->last_iteration = bus->iteration_counter;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-11-20 20:58:39 +01:00
|
|
|
r = sd_bus_message_peek_type(m, &type, &signature);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-05-21 17:36:55 +02:00
|
|
|
if (type != 'v')
|
|
|
|
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_SIGNATURE,
|
|
|
|
"Incorrect signature when setting property '%s', expected 'v', got '%c'.",
|
|
|
|
c->member, type);
|
|
|
|
if (!streq(strempty(signature), strempty(c->vtable->x.property.signature)))
|
|
|
|
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS,
|
|
|
|
"Incorrect parameters for property '%s', expected '%s', got '%s'.",
|
|
|
|
c->member, strempty(c->vtable->x.property.signature), strempty(signature));
|
2014-11-20 20:58:39 +01:00
|
|
|
|
2013-11-21 19:34:37 +01:00
|
|
|
r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-10 17:41:39 +01:00
|
|
|
r = check_access(bus, m, c, &error);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
|
2013-11-21 19:34:37 +01:00
|
|
|
if (r < 0)
|
2013-11-22 01:14:57 +01:00
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = sd_bus_message_exit_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_send(bus, reply, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
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) {
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot *slot;
|
2013-12-22 03:22:34 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
|
|
|
assert(path);
|
|
|
|
assert(c);
|
|
|
|
assert(v);
|
|
|
|
|
2019-08-19 20:28:34 +02:00
|
|
|
if (FLAGS_SET(c->vtable->flags, SD_BUS_VTABLE_SENSITIVE)) {
|
|
|
|
r = sd_bus_message_sensitive(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
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;
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
slot = container_of(c, sd_bus_slot, node_vtable);
|
|
|
|
|
|
|
|
r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
|
2013-12-22 03:22:34 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
static int vtable_append_all_properties(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
const char *path,
|
|
|
|
struct node_vtable *c,
|
|
|
|
void *userdata,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
|
|
|
const sd_bus_vtable *v;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert(path);
|
2013-10-11 20:18:10 +02:00
|
|
|
assert(c);
|
|
|
|
|
2013-12-10 17:49:00 +01:00
|
|
|
if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
|
|
|
|
return 1;
|
|
|
|
|
2019-01-08 12:14:37 +01:00
|
|
|
v = c->vtable;
|
|
|
|
for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
|
2017-09-28 10:17:04 +02:00
|
|
|
if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
|
2013-10-11 20:18:10 +02:00
|
|
|
continue;
|
|
|
|
|
2013-12-10 17:49:00 +01:00
|
|
|
if (v->flags & SD_BUS_VTABLE_HIDDEN)
|
|
|
|
continue;
|
|
|
|
|
2020-04-16 17:50:21 +02:00
|
|
|
/* Let's not include properties marked as "explicit" in any message that contains a generic
|
2019-08-20 15:35:53 +02:00
|
|
|
* dump of properties, but only in those generated as a response to an explicit request. */
|
2015-08-25 01:45:33 +02:00
|
|
|
if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
|
|
|
|
continue;
|
|
|
|
|
2019-08-20 15:35:53 +02:00
|
|
|
/* Let's not include properties marked only for invalidation on change (i.e. in contrast to
|
|
|
|
* those whose new values are included in PropertiesChanges message) in any signals. This is
|
|
|
|
* useful to ensure they aren't included in InterfacesAdded messages. */
|
|
|
|
if (reply->header->type != SD_BUS_MESSAGE_METHOD_RETURN &&
|
|
|
|
FLAGS_SET(v->flags, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
|
|
|
|
continue;
|
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
|
2013-11-22 01:14:57 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int property_get_all_callbacks_run(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
struct node_vtable *first,
|
|
|
|
bool require_fallback,
|
|
|
|
const char *iface,
|
|
|
|
bool *found_object) {
|
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node_vtable *c;
|
2013-10-29 21:38:31 +01:00
|
|
|
bool found_interface;
|
2013-10-11 20:18:10 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(found_object);
|
|
|
|
|
2013-11-21 01:51:16 +01:00
|
|
|
r = sd_bus_message_new_method_return(m, &reply);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'a', "{sv}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2020-04-13 15:04:06 +02:00
|
|
|
found_interface = !iface || STR_IN_SET(iface,
|
|
|
|
"org.freedesktop.DBus.Properties",
|
|
|
|
"org.freedesktop.DBus.Peer",
|
|
|
|
"org.freedesktop.DBus.Introspectable");
|
2013-10-29 21:38:31 +01:00
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
LIST_FOREACH(vtables, c, first) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
void *u;
|
|
|
|
|
|
|
|
if (require_fallback && !c->is_fallback)
|
|
|
|
continue;
|
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
2013-11-22 01:14:57 +01:00
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
*found_object = true;
|
|
|
|
|
|
|
|
if (iface && !streq(c->interface, iface))
|
|
|
|
continue;
|
|
|
|
found_interface = true;
|
|
|
|
|
|
|
|
r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
|
|
|
|
if (r < 0)
|
2013-11-22 01:14:57 +01:00
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2017-09-16 15:39:22 +02:00
|
|
|
if (!*found_object)
|
|
|
|
return 0;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
if (!found_interface) {
|
|
|
|
r = sd_bus_reply_method_errorf(
|
2013-11-21 01:51:16 +01:00
|
|
|
m,
|
2013-10-16 06:10:04 +02:00
|
|
|
SD_BUS_ERROR_UNKNOWN_INTERFACE,
|
2013-10-11 20:18:10 +02:00
|
|
|
"Unknown interface '%s'.", iface);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_send(bus, reply, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-09-17 10:32:49 +02:00
|
|
|
static int bus_node_exists(
|
2013-10-11 21:27:59 +02:00
|
|
|
sd_bus *bus,
|
|
|
|
struct node *n,
|
|
|
|
const char *path,
|
|
|
|
bool require_fallback) {
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node_vtable *c;
|
|
|
|
struct node_callback *k;
|
2014-09-17 10:32:49 +02:00
|
|
|
int r;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(n);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert(path);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
/* Tests if there's anything attached directly to this node
|
|
|
|
* for the specified path */
|
|
|
|
|
2014-09-17 10:32:49 +02:00
|
|
|
if (!require_fallback && (n->enumerators || n->object_managers))
|
|
|
|
return true;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
LIST_FOREACH(callbacks, k, n->callbacks) {
|
|
|
|
if (require_fallback && !k->is_fallback)
|
|
|
|
continue;
|
|
|
|
|
2014-09-17 10:32:49 +02:00
|
|
|
return 1;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
LIST_FOREACH(vtables, c, n->vtables) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
if (require_fallback && !c->is_fallback)
|
|
|
|
continue;
|
|
|
|
|
2014-09-17 10:32:49 +02:00
|
|
|
r = node_vtable_get_userdata(bus, path, c, NULL, &error);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
2014-09-17 10:32:49 +02:00
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2014-09-17 10:32:49 +02:00
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2019-04-19 12:14:35 +02:00
|
|
|
int introspect_path(
|
2013-10-11 20:18:10 +02:00
|
|
|
sd_bus *bus,
|
2019-04-19 12:14:35 +02:00
|
|
|
const char *path,
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node *n,
|
|
|
|
bool require_fallback,
|
2019-04-19 13:30:09 +02:00
|
|
|
bool ignore_nodes_modified,
|
2019-04-19 12:14:35 +02:00
|
|
|
bool *found_object,
|
|
|
|
char **ret,
|
|
|
|
sd_bus_error *error) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
_cleanup_set_free_free_ Set *s = NULL;
|
2019-04-19 11:28:36 +02:00
|
|
|
_cleanup_(introspect_free) struct introspect intro = {};
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node_vtable *c;
|
|
|
|
bool empty;
|
|
|
|
int r;
|
|
|
|
|
2019-04-19 13:30:09 +02:00
|
|
|
if (!n) {
|
|
|
|
n = hashmap_get(bus->nodes, path);
|
|
|
|
if (!n)
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2019-04-19 12:14:35 +02:00
|
|
|
r = get_child_nodes(bus, path, n, 0, &s, error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
2019-04-19 12:14:35 +02:00
|
|
|
return r;
|
2019-04-19 13:30:09 +02:00
|
|
|
if (bus->nodes_modified && !ignore_nodes_modified)
|
2013-10-14 21:58:06 +02:00
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-21 18:08:39 +01:00
|
|
|
r = introspect_begin(&intro, bus->trusted);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-09-17 09:28:09 +02:00
|
|
|
r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
empty = set_isempty(s);
|
|
|
|
|
|
|
|
LIST_FOREACH(vtables, c, n->vtables) {
|
|
|
|
if (require_fallback && !c->is_fallback)
|
|
|
|
continue;
|
|
|
|
|
2019-04-19 12:14:35 +02:00
|
|
|
r = node_vtable_get_userdata(bus, path, c, NULL, error);
|
2019-04-19 11:28:36 +02:00
|
|
|
if (r < 0)
|
2019-04-19 12:14:35 +02:00
|
|
|
return r;
|
2019-04-19 13:30:09 +02:00
|
|
|
if (bus->nodes_modified && !ignore_nodes_modified)
|
2019-04-19 11:28:36 +02:00
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
empty = false;
|
|
|
|
|
2013-12-10 17:49:00 +01:00
|
|
|
if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
|
|
|
|
continue;
|
|
|
|
|
2020-04-13 13:34:09 +02:00
|
|
|
r = introspect_write_interface(&intro, c->interface, c->vtable);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
2019-04-19 11:28:36 +02:00
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (empty) {
|
|
|
|
/* Nothing?, let's see if we exist at all, and if not
|
|
|
|
* refuse to do anything */
|
2019-04-19 12:14:35 +02:00
|
|
|
r = bus_node_exists(bus, n, path, require_fallback);
|
2019-04-19 11:28:36 +02:00
|
|
|
if (r <= 0)
|
2019-04-19 12:14:35 +02:00
|
|
|
return r;
|
2019-04-19 13:30:09 +02:00
|
|
|
if (bus->nodes_modified && !ignore_nodes_modified)
|
2019-04-19 11:28:36 +02:00
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2019-04-19 13:30:09 +02:00
|
|
|
if (found_object)
|
|
|
|
*found_object = true;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2019-04-19 12:14:35 +02:00
|
|
|
r = introspect_write_child_nodes(&intro, s, path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = introspect_finish(&intro, ret);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_introspect(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
struct node *n,
|
|
|
|
bool require_fallback,
|
|
|
|
bool *found_object) {
|
|
|
|
|
|
|
|
_cleanup_free_ char *s = NULL;
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(n);
|
|
|
|
assert(found_object);
|
|
|
|
|
2019-04-19 13:30:09 +02:00
|
|
|
r = introspect_path(bus, m->path, n, require_fallback, false, found_object, &s, &error);
|
2019-04-19 12:14:35 +02:00
|
|
|
if (r < 0)
|
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
|
|
|
if (r == 0)
|
|
|
|
/* nodes_modified == true */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
r = sd_bus_message_new_method_return(m, &reply);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
2019-04-19 11:28:36 +02:00
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2019-04-19 12:14:35 +02:00
|
|
|
r = sd_bus_message_append(reply, "s", s);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
2019-04-19 11:28:36 +02:00
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
r = sd_bus_send(bus, reply, NULL);
|
|
|
|
if (r < 0)
|
2019-04-19 11:28:36 +02:00
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2019-04-19 11:28:36 +02:00
|
|
|
return 1;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int object_manager_serialize_path(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
const char *prefix,
|
|
|
|
const char *path,
|
|
|
|
bool require_fallback,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
const char *previous_interface = NULL;
|
|
|
|
bool found_something = false;
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node_vtable *i;
|
|
|
|
struct node *n;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
|
|
|
assert(prefix);
|
|
|
|
assert(path);
|
|
|
|
assert(error);
|
|
|
|
|
|
|
|
n = hashmap_get(bus->nodes, prefix);
|
|
|
|
if (!n)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
LIST_FOREACH(vtables, i, n->vtables) {
|
2013-10-11 23:21:22 +02:00
|
|
|
void *u;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
if (require_fallback && !i->is_fallback)
|
|
|
|
continue;
|
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = node_vtable_get_userdata(bus, path, i, &u, error);
|
2013-10-11 23:21:22 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 23:21:22 +02:00
|
|
|
if (r == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!found_something) {
|
2013-11-19 21:12:59 +01:00
|
|
|
|
|
|
|
/* Open the object part */
|
|
|
|
|
2013-10-11 23:21:22 +02:00
|
|
|
r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(reply, "o", path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2017-04-26 02:36:30 +02:00
|
|
|
r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-10-11 23:21:22 +02:00
|
|
|
found_something = true;
|
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!streq_ptr(previous_interface, i->interface)) {
|
|
|
|
|
|
|
|
/* Maybe close the previous interface part */
|
|
|
|
|
|
|
|
if (previous_interface) {
|
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open the new interface part */
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(reply, "s", i->interface);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'a', "{sv}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = vtable_append_all_properties(bus, reply, path, i, u, error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-11-19 21:12:59 +01:00
|
|
|
|
|
|
|
previous_interface = i->interface;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (previous_interface) {
|
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2013-10-11 23:21:22 +02:00
|
|
|
if (found_something) {
|
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-11 23:21:22 +02:00
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int object_manager_serialize_path_and_fallbacks(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *reply,
|
|
|
|
const char *path,
|
|
|
|
sd_bus_error *error) {
|
|
|
|
|
2019-02-04 14:29:28 +01:00
|
|
|
_cleanup_free_ char *prefix = NULL;
|
|
|
|
size_t pl;
|
2013-10-11 20:18:10 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(reply);
|
|
|
|
assert(path);
|
|
|
|
assert(error);
|
|
|
|
|
|
|
|
/* First, add all vtables registered for this path */
|
|
|
|
r = object_manager_serialize_path(bus, reply, path, path, false, error);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
/* Second, add fallback vtables registered for any of the prefixes */
|
2019-02-04 14:29:28 +01:00
|
|
|
pl = strlen(path);
|
|
|
|
assert(pl <= BUS_PATH_SIZE_MAX);
|
|
|
|
prefix = new(char, pl + 1);
|
|
|
|
if (!prefix)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2013-10-11 23:20:54 +02:00
|
|
|
OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
|
|
|
|
r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int process_get_managed_objects(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
struct node *n,
|
|
|
|
bool require_fallback,
|
|
|
|
bool *found_object) {
|
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
_cleanup_set_free_free_ Set *s = NULL;
|
2014-09-17 09:28:09 +02:00
|
|
|
Iterator i;
|
|
|
|
char *path;
|
2013-10-11 20:18:10 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(n);
|
|
|
|
assert(found_object);
|
|
|
|
|
2014-09-17 09:28:09 +02:00
|
|
|
/* Spec says, GetManagedObjects() is only implemented on the root of a
|
|
|
|
* sub-tree. Therefore, we require a registered object-manager on
|
|
|
|
* exactly the queried path, otherwise, we refuse to respond. */
|
|
|
|
|
|
|
|
if (require_fallback || !n->object_managers)
|
2013-10-11 20:18:10 +02:00
|
|
|
return 0;
|
|
|
|
|
sd-bus: make introspection data non-recursive
Currently, our introspection data looks like this:
<node>
<interface name="org.freedesktop.DBus.Peer">
...
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
...
</interface>
<interface name="org.freedesktop.DBus.Properties">
...
</interface>
<node name="org"/>
<node name="org/freedesktop"/>
<node name="org/freedesktop/login1"/>
<node name="org/freedesktop/login1/user"/>
<node name="org/freedesktop/login1/user/self"/>
<node name="org/freedesktop/login1/user/_1000"/>
<node name="org/freedesktop/login1/seat"/>
<node name="org/freedesktop/login1/seat/self"/>
<node name="org/freedesktop/login1/seat/seat0"/>
<node name="org/freedesktop/login1/session"/>
<node name="org/freedesktop/login1/session/self"/>
<node name="org/freedesktop/login1/session/c1"/>
</node>
(ordered alphabetically for better visibility)
This is grossly incorrect. The spec says that we're allowed to return
non-directed children, however, it does not allow us to return data
recursively in multiple parents. If we return "org", then we must not
return anything else that starts with "org/".
It is unclear, whether we can include child-nodes as a tree. Moreover, it
is usually not what the caller wants. Hence, this patch changes sd-bus to
never return introspection data recursively. Instead, only a single
child-layer is returned.
This patch relies on enumerators to never return hierarchies. If someone
registers an enumerator via sd_bus_add_enumerator, they better register
sub-enumerators if they support *TRUE* hierarchies. Each enumerator is
treated as a single layer and not filtered.
Enumerators are still allowed to return nested data. However, that data
is still required to be a single hierarchy. For instance, returning
"/org/foo" and "/com/bar" is fine, but including "/com" or "/org" in that
dataset is not.
This should be the default for enumerators and I see no reason to filter
in sd-bus. Moreover, filtering that data-set would require to sort the
strv by path and then do prefix-filtering. This is O(n log n), which
would be fine, but still better to avoid.
Fixes #664.
2015-09-05 19:43:29 +02:00
|
|
|
r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
2017-06-21 20:42:28 +02:00
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-11-21 01:51:16 +01:00
|
|
|
r = sd_bus_message_new_method_return(m, &reply);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-09-17 09:28:09 +02:00
|
|
|
SET_FOREACH(path, s, i) {
|
|
|
|
r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
|
|
|
|
if (r < 0)
|
2017-06-21 20:42:28 +02:00
|
|
|
return bus_maybe_reply_error(m, r, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-09-17 09:28:09 +02:00
|
|
|
if (bus->nodes_modified)
|
2013-10-11 20:18:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(reply);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_send(bus, reply, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int object_find_and_run(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
const char *p,
|
|
|
|
bool require_fallback,
|
|
|
|
bool *found_object) {
|
|
|
|
|
|
|
|
struct node *n;
|
|
|
|
struct vtable_member vtable_key, *v;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(p);
|
|
|
|
assert(found_object);
|
|
|
|
|
|
|
|
n = hashmap_get(bus->nodes, p);
|
|
|
|
if (!n)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* First, try object callbacks */
|
|
|
|
r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
if (!m->interface || !m->member)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Then, look for a known method */
|
|
|
|
vtable_key.path = (char*) p;
|
|
|
|
vtable_key.interface = m->interface;
|
|
|
|
vtable_key.member = m->member;
|
|
|
|
|
|
|
|
v = hashmap_get(bus->vtable_methods, &vtable_key);
|
|
|
|
if (v) {
|
|
|
|
r = method_callbacks_run(bus, m, v, require_fallback, found_object);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Then, look for a known property */
|
|
|
|
if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
|
|
|
|
bool get = false;
|
|
|
|
|
|
|
|
get = streq(m->member, "Get");
|
|
|
|
|
|
|
|
if (get || streq(m->member, "Set")) {
|
|
|
|
|
|
|
|
r = sd_bus_message_rewind(m, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
vtable_key.path = (char*) p;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
|
|
|
|
if (r < 0)
|
2013-11-25 22:32:18 +01:00
|
|
|
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
v = hashmap_get(bus->vtable_properties, &vtable_key);
|
|
|
|
if (v) {
|
|
|
|
r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (streq(m->member, "GetAll")) {
|
|
|
|
const char *iface;
|
|
|
|
|
|
|
|
r = sd_bus_message_rewind(m, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_read(m, "s", &iface);
|
|
|
|
if (r < 0)
|
2013-11-25 22:32:18 +01:00
|
|
|
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
if (iface[0] == 0)
|
|
|
|
iface = NULL;
|
|
|
|
|
|
|
|
r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
|
|
|
|
|
2013-11-25 22:32:18 +01:00
|
|
|
if (!isempty(sd_bus_message_get_signature(m, true)))
|
|
|
|
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = process_introspect(bus, m, n, require_fallback, found_object);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
|
|
|
|
|
2013-11-25 22:32:18 +01:00
|
|
|
if (!isempty(sd_bus_message_get_signature(m, true)))
|
|
|
|
return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
if (!*found_object) {
|
|
|
|
r = bus_node_exists(bus, n, m->path, require_fallback);
|
|
|
|
if (r < 0)
|
2017-06-21 20:42:28 +02:00
|
|
|
return bus_maybe_reply_error(m, r, NULL);
|
2014-09-17 10:32:49 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r > 0)
|
|
|
|
*found_object = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bus_process_object(sd_bus *bus, sd_bus_message *m) {
|
2019-02-04 14:29:28 +01:00
|
|
|
_cleanup_free_ char *prefix = NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
int r;
|
|
|
|
size_t pl;
|
|
|
|
bool found_object = false;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
|
2017-12-18 14:21:34 +01:00
|
|
|
if (bus->is_monitor)
|
2014-03-19 04:17:00 +01:00
|
|
|
return 0;
|
|
|
|
|
2013-10-16 06:10:04 +02:00
|
|
|
if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
|
2013-10-11 20:18:10 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (hashmap_isempty(bus->nodes))
|
|
|
|
return 0;
|
|
|
|
|
2013-12-11 00:11:10 +01:00
|
|
|
/* Never respond to broadcast messages */
|
|
|
|
if (bus->bus_client && !m->destination)
|
|
|
|
return 0;
|
|
|
|
|
2013-12-10 17:41:39 +01:00
|
|
|
assert(m->path);
|
|
|
|
assert(m->member);
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
pl = strlen(m->path);
|
2019-02-04 14:29:28 +01:00
|
|
|
assert(pl <= BUS_PATH_SIZE_MAX);
|
|
|
|
prefix = new(char, pl + 1);
|
|
|
|
if (!prefix)
|
|
|
|
return -ENOMEM;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2019-02-04 14:29:28 +01:00
|
|
|
do {
|
2013-10-11 20:18:10 +02:00
|
|
|
bus->nodes_modified = false;
|
|
|
|
|
|
|
|
r = object_find_and_run(bus, m, m->path, false, &found_object);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
/* Look for fallback prefixes */
|
2013-10-11 23:20:54 +02:00
|
|
|
OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
break;
|
|
|
|
|
2013-10-11 23:20:54 +02:00
|
|
|
r = object_find_and_run(bus, m, prefix, true, &found_object);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (bus->nodes_modified);
|
|
|
|
|
|
|
|
if (!found_object)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
|
2019-05-31 15:33:46 +02:00
|
|
|
sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set")) {
|
|
|
|
const char *interface = NULL, *property = NULL;
|
|
|
|
|
|
|
|
(void) sd_bus_message_rewind(m, true);
|
|
|
|
(void) sd_bus_message_read_basic(m, 's', &interface);
|
|
|
|
(void) sd_bus_message_read_basic(m, 's', &property);
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = sd_bus_reply_method_errorf(
|
2013-11-21 01:51:16 +01:00
|
|
|
m,
|
2013-10-16 06:10:04 +02:00
|
|
|
SD_BUS_ERROR_UNKNOWN_PROPERTY,
|
2019-05-31 15:33:46 +02:00
|
|
|
"Unknown interface %s or property %s.", strnull(interface), strnull(property));
|
|
|
|
} else
|
2013-10-11 20:18:10 +02:00
|
|
|
r = sd_bus_reply_method_errorf(
|
2013-11-21 01:51:16 +01:00
|
|
|
m,
|
2013-10-16 06:10:04 +02:00
|
|
|
SD_BUS_ERROR_UNKNOWN_METHOD,
|
2019-05-31 15:33:46 +02:00
|
|
|
"Unknown method %s or interface %s.", m->member, m->interface);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
|
|
|
|
struct node *n, *parent;
|
|
|
|
const char *e;
|
2013-12-15 22:25:04 +01:00
|
|
|
_cleanup_free_ char *s = NULL;
|
|
|
|
char *p;
|
2013-10-11 20:18:10 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(path);
|
|
|
|
assert(path[0] == '/');
|
|
|
|
|
|
|
|
n = hashmap_get(bus->nodes, path);
|
|
|
|
if (n)
|
|
|
|
return n;
|
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
s = strdup(path);
|
|
|
|
if (!s)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (streq(path, "/"))
|
|
|
|
parent = NULL;
|
|
|
|
else {
|
|
|
|
e = strrchr(path, '/');
|
|
|
|
assert(e);
|
|
|
|
|
2015-07-15 14:35:15 +02:00
|
|
|
p = strndupa(path, MAX(1, e - path));
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
parent = bus_node_allocate(bus, p);
|
2013-12-15 22:25:04 +01:00
|
|
|
if (!parent)
|
2013-10-11 20:18:10 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = new0(struct node, 1);
|
|
|
|
if (!n)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
n->parent = parent;
|
2018-03-22 16:53:26 +01:00
|
|
|
n->path = TAKE_PTR(s);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-16 02:23:23 +01:00
|
|
|
r = hashmap_put(bus->nodes, n->path, n);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0) {
|
2013-12-15 22:25:04 +01:00
|
|
|
free(n->path);
|
2017-11-24 11:31:49 +01:00
|
|
|
return mfree(n);
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (parent)
|
2013-10-14 06:10:14 +02:00
|
|
|
LIST_PREPEND(siblings, parent->child, n);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
void bus_node_gc(sd_bus *b, struct node *n) {
|
2013-10-11 20:18:10 +02:00
|
|
|
assert(b);
|
|
|
|
|
|
|
|
if (!n)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (n->child ||
|
|
|
|
n->callbacks ||
|
|
|
|
n->vtables ||
|
|
|
|
n->enumerators ||
|
2014-05-15 01:15:30 +02:00
|
|
|
n->object_managers)
|
2013-10-11 20:18:10 +02:00
|
|
|
return;
|
|
|
|
|
2017-11-07 14:04:20 +01:00
|
|
|
assert_se(hashmap_remove(b->nodes, n->path) == n);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
if (n->parent)
|
2013-10-14 06:10:14 +02:00
|
|
|
LIST_REMOVE(siblings, n->parent->child, n);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
free(n->path);
|
|
|
|
bus_node_gc(b, n->parent);
|
|
|
|
free(n);
|
|
|
|
}
|
|
|
|
|
2015-07-21 12:59:56 +02:00
|
|
|
static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
|
|
|
|
struct node *n;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
n = hashmap_get(bus->nodes, path);
|
|
|
|
if (!n) {
|
2019-02-04 14:29:28 +01:00
|
|
|
_cleanup_free_ char *prefix = NULL;
|
|
|
|
size_t pl;
|
|
|
|
|
|
|
|
pl = strlen(path);
|
|
|
|
assert(pl <= BUS_PATH_SIZE_MAX);
|
|
|
|
prefix = new(char, pl + 1);
|
|
|
|
if (!prefix)
|
|
|
|
return -ENOMEM;
|
2015-07-21 12:59:56 +02:00
|
|
|
|
|
|
|
OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
|
|
|
|
n = hashmap_get(bus->nodes, prefix);
|
|
|
|
if (n)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (n && !n->object_managers)
|
|
|
|
n = n->parent;
|
|
|
|
|
|
|
|
if (out)
|
|
|
|
*out = n;
|
|
|
|
return !!n;
|
|
|
|
}
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
static int bus_add_object(
|
2013-10-11 21:27:59 +02:00
|
|
|
sd_bus *bus,
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot **slot,
|
2013-10-11 20:18:10 +02:00
|
|
|
bool fallback,
|
|
|
|
const char *path,
|
|
|
|
sd_bus_message_handler_t callback,
|
|
|
|
void *userdata) {
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot *s;
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node *n;
|
|
|
|
int r;
|
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(callback, -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
n = bus_node_allocate(bus, path);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (!n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
|
|
|
|
if (!s) {
|
2013-10-11 20:18:10 +02:00
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s->node_callback.callback = callback;
|
|
|
|
s->node_callback.is_fallback = fallback;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s->node_callback.node = n;
|
|
|
|
LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
|
2013-10-14 21:58:06 +02:00
|
|
|
bus->nodes_modified = true;
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
if (slot)
|
|
|
|
*slot = s;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot_unref(s);
|
2013-10-11 21:27:59 +02:00
|
|
|
bus_node_gc(bus, n);
|
2014-05-15 01:15:30 +02:00
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
_public_ int sd_bus_add_object(
|
2013-10-11 20:18:10 +02:00
|
|
|
sd_bus *bus,
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot **slot,
|
2013-10-11 20:18:10 +02:00
|
|
|
const char *path,
|
|
|
|
sd_bus_message_handler_t callback,
|
|
|
|
void *userdata) {
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
return bus_add_object(bus, slot, false, path, callback, userdata);
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
_public_ int sd_bus_add_fallback(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_slot **slot,
|
|
|
|
const char *prefix,
|
|
|
|
sd_bus_message_handler_t callback,
|
|
|
|
void *userdata) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
return bus_add_object(bus, slot, true, prefix, callback, userdata);
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
static void vtable_member_hash_func(const struct vtable_member *m, struct siphash *state) {
|
2013-10-11 21:27:59 +02:00
|
|
|
assert(m);
|
|
|
|
|
2015-10-04 00:22:41 +02:00
|
|
|
string_hash_func(m->path, state);
|
|
|
|
string_hash_func(m->interface, state);
|
|
|
|
string_hash_func(m->member, state);
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
static int vtable_member_compare_func(const struct vtable_member *x, const struct vtable_member *y) {
|
2013-10-11 20:18:10 +02:00
|
|
|
int r;
|
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
assert(x);
|
|
|
|
assert(y);
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = strcmp(x->path, y->path);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = strcmp(x->interface, y->interface);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return strcmp(x->member, y->member);
|
|
|
|
}
|
|
|
|
|
2018-11-27 14:25:20 +01:00
|
|
|
DEFINE_PRIVATE_HASH_OPS(vtable_member_hash_ops, struct vtable_member, vtable_member_hash_func, vtable_member_compare_func);
|
2014-08-13 01:00:18 +02:00
|
|
|
|
2019-01-08 12:14:37 +01:00
|
|
|
typedef enum {
|
|
|
|
NAMES_FIRST_PART = 1 << 0, /* first part of argument name list (input names). It is reset by names_are_valid() */
|
|
|
|
NAMES_PRESENT = 1 << 1, /* at least one argument name is present, so the names will checked.
|
|
|
|
This flag is set and used internally by names_are_valid(), but needs to be stored across calls for 2-parts list */
|
|
|
|
NAMES_SINGLE_PART = 1 << 2, /* argument name list consisting of a single part */
|
|
|
|
} names_flags;
|
|
|
|
|
|
|
|
static bool names_are_valid(const char *signature, const char **names, names_flags *flags) {
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if ((*flags & NAMES_FIRST_PART || *flags & NAMES_SINGLE_PART) && **names != '\0')
|
|
|
|
*flags |= NAMES_PRESENT;
|
|
|
|
|
|
|
|
for (;*flags & NAMES_PRESENT;) {
|
|
|
|
size_t l;
|
|
|
|
|
|
|
|
if (!*signature)
|
|
|
|
break;
|
|
|
|
|
|
|
|
r = signature_element_length(signature, &l);
|
|
|
|
if (r < 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (**names != '\0') {
|
|
|
|
if (!member_name_is_valid(*names))
|
|
|
|
return false;
|
|
|
|
*names += strlen(*names) + 1;
|
|
|
|
} else if (*flags & NAMES_PRESENT)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
signature += l;
|
|
|
|
}
|
|
|
|
/* let's check if there are more argument names specified than the signature allows */
|
|
|
|
if (*flags & NAMES_PRESENT && **names != '\0' && !(*flags & NAMES_FIRST_PART))
|
|
|
|
return false;
|
|
|
|
*flags &= ~NAMES_FIRST_PART;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the current version of this struct is defined in sd-bus-vtable.h, but we need to list here the historical versions
|
|
|
|
to make sure the calling code is compatible with one of these */
|
2019-04-18 13:42:25 +02:00
|
|
|
struct sd_bus_vtable_221 {
|
2019-01-08 12:14:37 +01:00
|
|
|
uint8_t type:8;
|
|
|
|
uint64_t flags:56;
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
size_t element_size;
|
|
|
|
} start;
|
|
|
|
struct {
|
|
|
|
const char *member;
|
|
|
|
const char *signature;
|
|
|
|
const char *result;
|
|
|
|
sd_bus_message_handler_t handler;
|
|
|
|
size_t offset;
|
|
|
|
} method;
|
|
|
|
struct {
|
|
|
|
const char *member;
|
|
|
|
const char *signature;
|
|
|
|
} signal;
|
|
|
|
struct {
|
|
|
|
const char *member;
|
|
|
|
const char *signature;
|
|
|
|
sd_bus_property_get_t get;
|
|
|
|
sd_bus_property_set_t set;
|
|
|
|
size_t offset;
|
|
|
|
} property;
|
|
|
|
} x;
|
|
|
|
};
|
|
|
|
/* Structure size up to v241 */
|
2019-04-18 13:42:25 +02:00
|
|
|
#define VTABLE_ELEMENT_SIZE_221 sizeof(struct sd_bus_vtable_221)
|
|
|
|
|
|
|
|
/* Size of the structure when "features" field was added. If the structure definition is augmented, a copy of
|
|
|
|
* the structure definition will need to be made (similarly to the sd_bus_vtable_221 above), and this
|
|
|
|
* definition updated to refer to it. */
|
|
|
|
#define VTABLE_ELEMENT_SIZE_242 sizeof(struct sd_bus_vtable)
|
2019-01-08 12:14:37 +01:00
|
|
|
|
|
|
|
static int vtable_features(const sd_bus_vtable *vtable) {
|
2019-04-18 13:42:25 +02:00
|
|
|
if (vtable[0].x.start.element_size < VTABLE_ELEMENT_SIZE_242 ||
|
sd-bus: add symbol to tell linker that new vtable functions are used
In 856ad2a86bd9b3e264a090fcf4b0d05bfaa91030 sd_bus_add_object_vtable() and
sd_bus_add_fallback_vtable() were changed to take an updated sd_bus_vtable[]
array with additional 'features' and 'names' fields in the union.
The commit tried to check whether the old or the new table format is used, by
looking at the vtable[0].x.start.element_size field, on the assumption that the
added fields caused the structure size to grow. Unfortunately, this assumption
was false, and on arm32 (at least), the structure size is unchanged.
In libsystemd we use symbol versioning and a major.minor.patch semantic
versioning of the library name (major equals the number in the so-name). When
systemd-242 was released, the minor number was (correctly) bumped, but this is
not enough, because no new symbols were added or symbol versions changed. This
means that programs compiled with the new systemd headers and library could be
successfully linked to older versions of the library. For example rpm only
looks at the so-name and the list of versioned symbols, completely ignoring the
major.minor numbers in the library name. But the older library does not
understand the new vtable format, and would return -EINVAL after failing the
size check (on those architectures where the structure size did change, i.e.
all 64 bit architectures).
To force new libsystemd (with the functions that take the updated
sd_bus_vtable[] format) to be used, let's pull in a dummy symbol from the table
definition. This is a bit wasteful, because a dummy pointer has to be stored,
but the effect is negligible. In particular, the pointer doesn't even change
the size of the structure because if fits in an unused area in the union.
The number stored in the new unsigned integer is not checked anywhere. If the
symbol exists, we already know we have the new version of the library, so an
additional check would not tell us anything.
An alternative would be to make sd_bus_add_{object,fallback}_vtable() versioned
symbols, using .symver linker annotations. We would provide
sd_bus_add_{object,fallback}_vtable@LIBSYSTEMD_221 (for backwards
compatibility) and e.g. sd_bus_add_{object,fallback}_vtable@@LIBSYSTEMD_242
(the default) with the new implementation. This would work too, but is more
work. We would have to version at least those two functions. And it turns out
that the .symver linker instructions have to located in the same compilation
unit as the function being annotated. We first compile libsystemd.a, and then
link it into libsystemd.so and various other targets, including
libsystemd-shared.so, and the nss modules. If the .symver annotations were
placed next to the function definitions (in bus-object.c), they would influence
all targets that link libsystemd.a, and cause problems, because those functions
should not be exported there. To export them only in libsystemd.so, compilation
would have to be rearranged, so that the functions exported in libsystemd.so
would not be present in libsystemd.a, but a separate compilation unit containg
them and the .symver annotations would be linked solely into libsystemd.so.
This is certainly possible, but more work than the approach in this patch.
856ad2a86bd9b3e264a090fcf4b0d05bfaa91030 has one more issue: it relies on the
undefined fields in sd_bus_vtable[] array to be zeros. But the structure
contains a union, and fields of the union do not have to be zero-initalized by
the compiler. This means that potentially, we could have garbarge values there,
for example when reading the old vtable format definition from the new function
implementation. In practice this should not be an issue at all, because vtable
definitions are static data and are placed in the ro-data section, which is
fully initalized, so we know that those undefined areas will be zero. Things
would be different if somebody defined the vtable array on the heap or on the
stack. Let's just document that they should zero-intialize the unused areas
in this case.
The symbol checking code had to be updated because otherwise gcc warns about a
cast from unsigned to a pointer.
2019-04-18 13:06:41 +02:00
|
|
|
!vtable[0].x.start.vtable_format_reference)
|
2019-01-08 12:14:37 +01:00
|
|
|
return 0;
|
|
|
|
return vtable[0].x.start.features;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bus_vtable_has_names(const sd_bus_vtable *vtable) {
|
|
|
|
return vtable_features(vtable) & _SD_BUS_VTABLE_PARAM_NAMES;
|
|
|
|
}
|
|
|
|
|
|
|
|
const sd_bus_vtable* bus_vtable_next(const sd_bus_vtable *vtable, const sd_bus_vtable *v) {
|
2019-04-18 13:42:25 +02:00
|
|
|
return (const sd_bus_vtable*) ((char*) v + vtable[0].x.start.element_size);
|
2019-01-08 12:14:37 +01:00
|
|
|
}
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
static int add_object_vtable_internal(
|
|
|
|
sd_bus *bus,
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot **slot,
|
2013-10-11 20:18:10 +02:00
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const sd_bus_vtable *vtable,
|
|
|
|
bool fallback,
|
|
|
|
sd_bus_object_find_t find,
|
|
|
|
void *userdata) {
|
|
|
|
|
2014-05-16 05:27:59 +02:00
|
|
|
sd_bus_slot *s = NULL;
|
2014-05-15 01:15:30 +02:00
|
|
|
struct node_vtable *i, *existing = NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
const sd_bus_vtable *v;
|
|
|
|
struct node *n;
|
|
|
|
int r;
|
2019-01-08 12:14:37 +01:00
|
|
|
const char *names = "";
|
|
|
|
names_flags nf;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(interface_name_is_valid(interface), -EINVAL);
|
|
|
|
assert_return(vtable, -EINVAL);
|
|
|
|
assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
|
2019-04-18 13:42:25 +02:00
|
|
|
assert_return(vtable[0].x.start.element_size == VTABLE_ELEMENT_SIZE_221 ||
|
|
|
|
vtable[0].x.start.element_size >= VTABLE_ELEMENT_SIZE_242,
|
2019-03-20 13:28:29 +01:00
|
|
|
-EINVAL);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
2013-11-19 21:12:59 +01:00
|
|
|
assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
|
|
|
|
!streq(interface, "org.freedesktop.DBus.Introspectable") &&
|
|
|
|
!streq(interface, "org.freedesktop.DBus.Peer") &&
|
|
|
|
!streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
n = bus_node_allocate(bus, path);
|
|
|
|
if (!n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
LIST_FOREACH(vtables, i, n->vtables) {
|
|
|
|
if (i->is_fallback != fallback) {
|
|
|
|
r = -EPROTOTYPE;
|
|
|
|
goto fail;
|
|
|
|
}
|
2013-11-19 21:12:59 +01:00
|
|
|
|
|
|
|
if (streq(i->interface, interface)) {
|
|
|
|
|
|
|
|
if (i->vtable == vtable) {
|
|
|
|
r = -EEXIST;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
existing = i;
|
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
|
|
|
|
if (!s) {
|
2013-10-11 20:18:10 +02:00
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s->node_vtable.is_fallback = fallback;
|
|
|
|
s->node_vtable.vtable = vtable;
|
|
|
|
s->node_vtable.find = find;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s->node_vtable.interface = strdup(interface);
|
|
|
|
if (!s->node_vtable.interface) {
|
2013-10-11 20:18:10 +02:00
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2019-01-08 12:14:37 +01:00
|
|
|
v = s->node_vtable.vtable;
|
|
|
|
for (v = bus_vtable_next(vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
switch (v->type) {
|
|
|
|
|
|
|
|
case _SD_BUS_VTABLE_METHOD: {
|
|
|
|
struct vtable_member *m;
|
2019-01-08 12:14:37 +01:00
|
|
|
nf = NAMES_FIRST_PART;
|
|
|
|
|
|
|
|
if (bus_vtable_has_names(vtable))
|
|
|
|
names = strempty(v->x.method.names);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
if (!member_name_is_valid(v->x.method.member) ||
|
|
|
|
!signature_is_valid(strempty(v->x.method.signature), false) ||
|
|
|
|
!signature_is_valid(strempty(v->x.method.result), false) ||
|
2019-01-08 12:14:37 +01:00
|
|
|
!names_are_valid(strempty(v->x.method.signature), &names, &nf) ||
|
|
|
|
!names_are_valid(strempty(v->x.method.result), &names, &nf) ||
|
2013-10-11 20:32:16 +02:00
|
|
|
!(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
|
2013-12-22 00:12:54 +01:00
|
|
|
v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
|
2013-10-11 20:18:10 +02:00
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = new0(struct vtable_member, 1);
|
|
|
|
if (!m) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
m->parent = &s->node_vtable;
|
2013-10-11 20:18:10 +02:00
|
|
|
m->path = n->path;
|
2014-05-15 01:15:30 +02:00
|
|
|
m->interface = s->node_vtable.interface;
|
2013-10-11 20:32:16 +02:00
|
|
|
m->member = v->x.method.member;
|
2013-10-11 20:18:10 +02:00
|
|
|
m->vtable = v;
|
|
|
|
|
|
|
|
r = hashmap_put(bus->vtable_methods, m, m);
|
|
|
|
if (r < 0) {
|
|
|
|
free(m);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
|
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
|
2013-10-11 20:18:10 +02:00
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
2014-11-19 20:52:47 +01:00
|
|
|
|
|
|
|
if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
|
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2017-11-19 19:06:10 +01:00
|
|
|
_fallthrough_;
|
2013-10-11 20:18:10 +02:00
|
|
|
case _SD_BUS_VTABLE_PROPERTY: {
|
|
|
|
struct vtable_member *m;
|
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
if (!member_name_is_valid(v->x.property.member) ||
|
|
|
|
!signature_is_single(v->x.property.signature, false) ||
|
2013-11-05 00:49:50 +01:00
|
|
|
!(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
|
2015-08-25 01:45:33 +02:00
|
|
|
(v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
|
2013-12-22 00:12:54 +01:00
|
|
|
(!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
|
2015-08-25 01:45:33 +02:00
|
|
|
((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
|
2013-12-10 17:41:39 +01:00
|
|
|
(v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
|
2013-10-11 20:18:10 +02:00
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
m = new0(struct vtable_member, 1);
|
|
|
|
if (!m) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
m->parent = &s->node_vtable;
|
2013-10-11 20:18:10 +02:00
|
|
|
m->path = n->path;
|
2014-05-15 01:15:30 +02:00
|
|
|
m->interface = s->node_vtable.interface;
|
2013-10-11 20:32:16 +02:00
|
|
|
m->member = v->x.property.member;
|
2013-10-11 20:18:10 +02:00
|
|
|
m->vtable = v;
|
|
|
|
|
|
|
|
r = hashmap_put(bus->vtable_properties, m, m);
|
|
|
|
if (r < 0) {
|
|
|
|
free(m);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case _SD_BUS_VTABLE_SIGNAL:
|
2019-01-08 12:14:37 +01:00
|
|
|
nf = NAMES_SINGLE_PART;
|
|
|
|
|
|
|
|
if (bus_vtable_has_names(vtable))
|
|
|
|
names = strempty(v->x.signal.names);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-11 20:32:16 +02:00
|
|
|
if (!member_name_is_valid(v->x.signal.member) ||
|
2013-12-10 17:41:39 +01:00
|
|
|
!signature_is_valid(strempty(v->x.signal.signature), false) ||
|
2019-01-08 12:14:37 +01:00
|
|
|
!names_are_valid(strempty(v->x.signal.signature), &names, &nf) ||
|
2013-12-10 17:41:39 +01:00
|
|
|
v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
|
2013-10-11 20:18:10 +02:00
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
r = -EINVAL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s->node_vtable.node = n;
|
|
|
|
LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
|
2013-10-14 21:58:06 +02:00
|
|
|
bus->nodes_modified = true;
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
if (slot)
|
|
|
|
*slot = s;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot_unref(s);
|
2013-10-11 21:27:59 +02:00
|
|
|
bus_node_gc(bus, n);
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
sd-bus: add symbol to tell linker that new vtable functions are used
In 856ad2a86bd9b3e264a090fcf4b0d05bfaa91030 sd_bus_add_object_vtable() and
sd_bus_add_fallback_vtable() were changed to take an updated sd_bus_vtable[]
array with additional 'features' and 'names' fields in the union.
The commit tried to check whether the old or the new table format is used, by
looking at the vtable[0].x.start.element_size field, on the assumption that the
added fields caused the structure size to grow. Unfortunately, this assumption
was false, and on arm32 (at least), the structure size is unchanged.
In libsystemd we use symbol versioning and a major.minor.patch semantic
versioning of the library name (major equals the number in the so-name). When
systemd-242 was released, the minor number was (correctly) bumped, but this is
not enough, because no new symbols were added or symbol versions changed. This
means that programs compiled with the new systemd headers and library could be
successfully linked to older versions of the library. For example rpm only
looks at the so-name and the list of versioned symbols, completely ignoring the
major.minor numbers in the library name. But the older library does not
understand the new vtable format, and would return -EINVAL after failing the
size check (on those architectures where the structure size did change, i.e.
all 64 bit architectures).
To force new libsystemd (with the functions that take the updated
sd_bus_vtable[] format) to be used, let's pull in a dummy symbol from the table
definition. This is a bit wasteful, because a dummy pointer has to be stored,
but the effect is negligible. In particular, the pointer doesn't even change
the size of the structure because if fits in an unused area in the union.
The number stored in the new unsigned integer is not checked anywhere. If the
symbol exists, we already know we have the new version of the library, so an
additional check would not tell us anything.
An alternative would be to make sd_bus_add_{object,fallback}_vtable() versioned
symbols, using .symver linker annotations. We would provide
sd_bus_add_{object,fallback}_vtable@LIBSYSTEMD_221 (for backwards
compatibility) and e.g. sd_bus_add_{object,fallback}_vtable@@LIBSYSTEMD_242
(the default) with the new implementation. This would work too, but is more
work. We would have to version at least those two functions. And it turns out
that the .symver linker instructions have to located in the same compilation
unit as the function being annotated. We first compile libsystemd.a, and then
link it into libsystemd.so and various other targets, including
libsystemd-shared.so, and the nss modules. If the .symver annotations were
placed next to the function definitions (in bus-object.c), they would influence
all targets that link libsystemd.a, and cause problems, because those functions
should not be exported there. To export them only in libsystemd.so, compilation
would have to be rearranged, so that the functions exported in libsystemd.so
would not be present in libsystemd.a, but a separate compilation unit containg
them and the .symver annotations would be linked solely into libsystemd.so.
This is certainly possible, but more work than the approach in this patch.
856ad2a86bd9b3e264a090fcf4b0d05bfaa91030 has one more issue: it relies on the
undefined fields in sd_bus_vtable[] array to be zeros. But the structure
contains a union, and fields of the union do not have to be zero-initalized by
the compiler. This means that potentially, we could have garbarge values there,
for example when reading the old vtable format definition from the new function
implementation. In practice this should not be an issue at all, because vtable
definitions are static data and are placed in the ro-data section, which is
fully initalized, so we know that those undefined areas will be zero. Things
would be different if somebody defined the vtable array on the heap or on the
stack. Let's just document that they should zero-intialize the unused areas
in this case.
The symbol checking code had to be updated because otherwise gcc warns about a
cast from unsigned to a pointer.
2019-04-18 13:06:41 +02:00
|
|
|
/* This symbol exists solely to tell the linker that the "new" vtable format is used. */
|
|
|
|
_public_ const unsigned sd_bus_object_vtable_format = 242;
|
|
|
|
|
2013-11-06 08:33:42 +01:00
|
|
|
_public_ int sd_bus_add_object_vtable(
|
2013-10-11 20:18:10 +02:00
|
|
|
sd_bus *bus,
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot **slot,
|
2013-10-11 20:18:10 +02:00
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const sd_bus_vtable *vtable,
|
|
|
|
void *userdata) {
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2013-11-06 08:33:42 +01:00
|
|
|
_public_ int sd_bus_add_fallback_vtable(
|
2013-10-11 20:18:10 +02:00
|
|
|
sd_bus *bus,
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot **slot,
|
|
|
|
const char *prefix,
|
2013-11-19 21:12:59 +01:00
|
|
|
const char *interface,
|
|
|
|
const sd_bus_vtable *vtable,
|
|
|
|
sd_bus_object_find_t find,
|
|
|
|
void *userdata) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2013-11-06 08:33:42 +01:00
|
|
|
_public_ int sd_bus_add_node_enumerator(
|
2013-10-11 20:18:10 +02:00
|
|
|
sd_bus *bus,
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot **slot,
|
2013-10-11 20:18:10 +02:00
|
|
|
const char *path,
|
|
|
|
sd_bus_node_enumerator_t callback,
|
|
|
|
void *userdata) {
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot *s;
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node *n;
|
|
|
|
int r;
|
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(callback, -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
n = bus_node_allocate(bus, path);
|
|
|
|
if (!n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
|
|
|
|
if (!s) {
|
2013-10-11 20:18:10 +02:00
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s->node_enumerator.callback = callback;
|
2013-10-14 21:58:06 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s->node_enumerator.node = n;
|
|
|
|
LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
|
2013-10-14 21:58:06 +02:00
|
|
|
bus->nodes_modified = true;
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
if (slot)
|
|
|
|
*slot = s;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
2014-05-15 01:15:30 +02:00
|
|
|
sd_bus_slot_unref(s);
|
2013-10-11 20:18:10 +02:00
|
|
|
bus_node_gc(bus, n);
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int emit_properties_changed_on_interface(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *prefix,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
bool require_fallback,
|
2013-12-23 17:30:21 +01:00
|
|
|
bool *found_interface,
|
2013-10-11 20:18:10 +02:00
|
|
|
char **names) {
|
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
2013-11-19 21:12:59 +01:00
|
|
|
bool has_invalidating = false, has_changing = false;
|
|
|
|
struct vtable_member key = {};
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node_vtable *c;
|
|
|
|
struct node *n;
|
|
|
|
char **property;
|
|
|
|
void *u = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert(prefix);
|
2013-10-11 20:18:10 +02:00
|
|
|
assert(path);
|
|
|
|
assert(interface);
|
2013-12-23 17:30:21 +01:00
|
|
|
assert(found_interface);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
n = hashmap_get(bus->nodes, prefix);
|
|
|
|
if (!n)
|
|
|
|
return 0;
|
|
|
|
|
2014-02-19 23:54:58 +01:00
|
|
|
r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(m, "s", interface);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(m, 'a', "{sv}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
key.path = prefix;
|
|
|
|
key.interface = interface;
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
LIST_FOREACH(vtables, c, n->vtables) {
|
|
|
|
if (require_fallback && !c->is_fallback)
|
|
|
|
continue;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!streq(c->interface, interface))
|
2013-10-11 20:18:10 +02:00
|
|
|
continue;
|
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = node_vtable_get_userdata(bus, path, c, &u, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-11-19 21:12:59 +01:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
if (r == 0)
|
|
|
|
continue;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-23 17:30:21 +01:00
|
|
|
*found_interface = true;
|
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
if (names) {
|
|
|
|
/* If the caller specified a list of
|
|
|
|
* properties we include exactly those in the
|
|
|
|
* PropertiesChanged message */
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
STRV_FOREACH(property, names) {
|
|
|
|
struct vtable_member *v;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
assert_return(member_name_is_valid(*property), -EINVAL);
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
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;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
|
|
|
|
v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
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;
|
2013-11-19 21:12:59 +01:00
|
|
|
}
|
2013-12-22 03:22:34 +01:00
|
|
|
} else {
|
|
|
|
const sd_bus_vtable *v;
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
/* If the caller specified no properties list
|
|
|
|
* we include all properties that are marked
|
|
|
|
* as changing in the message. */
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2019-01-08 12:14:37 +01:00
|
|
|
v = c->vtable;
|
|
|
|
for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
|
2017-09-28 10:17:04 +02:00
|
|
|
if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
|
2013-12-22 03:22:34 +01:00
|
|
|
continue;
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
if (v->flags & SD_BUS_VTABLE_HIDDEN)
|
|
|
|
continue;
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
|
|
|
|
has_invalidating = true;
|
|
|
|
continue;
|
|
|
|
}
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
|
|
|
|
continue;
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
has_changing = true;
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
}
|
2013-11-19 21:12:59 +01:00
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!has_invalidating && !has_changing)
|
|
|
|
return 0;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(m, 'a', "s");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (has_invalidating) {
|
2013-11-19 21:12:59 +01:00
|
|
|
LIST_FOREACH(vtables, c, n->vtables) {
|
|
|
|
if (require_fallback && !c->is_fallback)
|
|
|
|
continue;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!streq(c->interface, interface))
|
2013-10-11 20:18:10 +02:00
|
|
|
continue;
|
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = node_vtable_get_userdata(bus, path, c, &u, &error);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-11-19 21:12:59 +01:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
if (r == 0)
|
|
|
|
continue;
|
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
if (names) {
|
|
|
|
STRV_FOREACH(property, names) {
|
|
|
|
struct vtable_member *v;
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
key.member = *property;
|
|
|
|
assert_se(v = hashmap_get(bus->vtable_properties, &key));
|
|
|
|
assert(c == v->parent);
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
|
|
|
|
continue;
|
2013-11-19 21:12:59 +01:00
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
r = sd_bus_message_append(m, "s", *property);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const sd_bus_vtable *v;
|
|
|
|
|
2019-01-08 12:14:37 +01:00
|
|
|
v = c->vtable;
|
|
|
|
for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
|
2017-09-28 10:17:04 +02:00
|
|
|
if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
|
2013-12-22 03:22:34 +01:00
|
|
|
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;
|
|
|
|
}
|
2013-11-19 21:12:59 +01:00
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_send(bus, m, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-11-06 08:33:42 +01:00
|
|
|
_public_ int sd_bus_emit_properties_changed_strv(
|
2013-10-11 21:27:59 +02:00
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
char **names) {
|
|
|
|
|
2019-02-04 14:29:28 +01:00
|
|
|
_cleanup_free_ char *prefix = NULL;
|
2013-12-23 17:30:21 +01:00
|
|
|
bool found_interface = false;
|
2019-02-04 14:29:28 +01:00
|
|
|
size_t pl;
|
2013-10-11 20:18:10 +02:00
|
|
|
int r;
|
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(interface_name_is_valid(interface), -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
|
|
|
|
2014-03-19 21:41:21 +01:00
|
|
|
if (!BUS_IS_OPEN(bus->state))
|
|
|
|
return -ENOTCONN;
|
2013-12-22 03:22:34 +01:00
|
|
|
|
|
|
|
/* 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)
|
2013-10-11 21:27:59 +02:00
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2018-07-18 12:16:33 +02:00
|
|
|
BUS_DONT_DESTROY(bus);
|
|
|
|
|
2019-02-04 14:29:28 +01:00
|
|
|
pl = strlen(path);
|
|
|
|
assert(pl <= BUS_PATH_SIZE_MAX);
|
|
|
|
prefix = new(char, pl + 1);
|
|
|
|
if (!prefix)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
do {
|
|
|
|
bus->nodes_modified = false;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-23 17:30:21 +01:00
|
|
|
r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
|
2013-10-11 23:20:54 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
|
2013-12-23 17:30:21 +01:00
|
|
|
r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
|
2013-10-14 21:58:06 +02:00
|
|
|
if (r != 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (bus->nodes_modified);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-12-23 17:30:21 +01:00
|
|
|
return found_interface ? 0 : -ENOENT;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2013-11-06 08:33:42 +01:00
|
|
|
_public_ int sd_bus_emit_properties_changed(
|
2013-10-11 21:27:59 +02:00
|
|
|
sd_bus *bus,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
const char *name, ...) {
|
|
|
|
|
2013-10-29 19:53:43 +01:00
|
|
|
char **names;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(interface_name_is_valid(interface), -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
|
|
|
|
2014-03-19 21:41:21 +01:00
|
|
|
if (!BUS_IS_OPEN(bus->state))
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
if (!name)
|
|
|
|
return 0;
|
|
|
|
|
2013-10-29 19:53:43 +01:00
|
|
|
names = strv_from_stdarg_alloca(name);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
|
|
|
|
}
|
|
|
|
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
static int object_added_append_all_prefix(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
Set *s,
|
|
|
|
const char *prefix,
|
|
|
|
const char *path,
|
|
|
|
bool require_fallback) {
|
|
|
|
|
|
|
|
const char *previous_interface = NULL;
|
|
|
|
struct node_vtable *c;
|
|
|
|
struct node *n;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(s);
|
|
|
|
assert(prefix);
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
n = hashmap_get(bus->nodes, prefix);
|
|
|
|
if (!n)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
LIST_FOREACH(vtables, c, n->vtables) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
void *u = NULL;
|
|
|
|
|
|
|
|
if (require_fallback && !c->is_fallback)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = node_vtable_get_userdata(bus, path, c, &u, &error);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
if (r == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!streq_ptr(c->interface, previous_interface)) {
|
|
|
|
/* If a child-node already handled this interface, we
|
|
|
|
* skip it on any of its parents. The child vtables
|
|
|
|
* always fully override any conflicting vtables of
|
|
|
|
* any parent node. */
|
|
|
|
if (set_get(s, c->interface))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = set_put(s, c->interface);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (previous_interface) {
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(m, 'e', "sa{sv}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_append(m, "s", c->interface);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_open_container(m, 'a', "{sv}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
previous_interface = c->interface;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = vtable_append_all_properties(bus, m, path, c, u, &error);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (previous_interface) {
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
|
|
|
|
_cleanup_set_free_ Set *s = NULL;
|
2019-02-04 14:29:28 +01:00
|
|
|
_cleanup_free_ char *prefix = NULL;
|
|
|
|
size_t pl;
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This appends all interfaces registered on path @path. We first add
|
|
|
|
* the builtin interfaces, which are always available and handled by
|
|
|
|
* sd-bus. Then, we add all interfaces registered on the exact node,
|
|
|
|
* followed by all fallback interfaces registered on any parent prefix.
|
|
|
|
*
|
|
|
|
* If an interface is registered multiple times on the same node with
|
|
|
|
* different vtables, we merge all the properties across all vtables.
|
|
|
|
* However, if a child node has the same interface registered as one of
|
|
|
|
* its parent nodes has as fallback, we make the child overwrite the
|
|
|
|
* parent instead of extending it. Therefore, we keep a "Set" of all
|
|
|
|
* handled interfaces during parent traversal, so we skip interfaces on
|
|
|
|
* a parent that were overwritten by a child.
|
|
|
|
*/
|
|
|
|
|
|
|
|
s = set_new(&string_hash_ops);
|
|
|
|
if (!s)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = object_added_append_all_prefix(bus, m, s, path, path, false);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
|
2019-02-04 14:29:28 +01:00
|
|
|
pl = strlen(path);
|
|
|
|
assert(pl <= BUS_PATH_SIZE_MAX);
|
|
|
|
prefix = new(char, pl + 1);
|
|
|
|
if (!prefix)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
|
|
|
|
r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-21 13:44:35 +02:00
|
|
|
_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
2015-07-21 12:59:56 +02:00
|
|
|
struct node *object_manager;
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This emits an InterfacesAdded signal on the given path, by iterating
|
|
|
|
* all registered vtables and fallback vtables on the path. All
|
|
|
|
* properties are queried and included in the signal.
|
|
|
|
* This call is equivalent to sd_bus_emit_interfaces_added() with an
|
|
|
|
* explicit list of registered interfaces. However, unlike
|
|
|
|
* interfaces_added(), this call can figure out the list of supported
|
|
|
|
* interfaces itself. Furthermore, it properly adds the builtin
|
|
|
|
* org.freedesktop.DBus.* interfaces.
|
|
|
|
*/
|
|
|
|
|
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
|
|
|
|
|
|
|
if (!BUS_IS_OPEN(bus->state))
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2015-07-21 12:59:56 +02:00
|
|
|
r = bus_find_parent_object_manager(bus, &object_manager, path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
return -ESRCH;
|
|
|
|
|
2018-07-18 12:16:33 +02:00
|
|
|
BUS_DONT_DESTROY(bus);
|
|
|
|
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
do {
|
|
|
|
bus->nodes_modified = false;
|
|
|
|
m = sd_bus_message_unref(m);
|
|
|
|
|
2015-07-21 12:59:56 +02:00
|
|
|
r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, 'o', path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = object_added_append_all(bus, m, path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
} while (bus->nodes_modified);
|
|
|
|
|
|
|
|
return sd_bus_send(bus, m, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int object_removed_append_all_prefix(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
Set *s,
|
|
|
|
const char *prefix,
|
|
|
|
const char *path,
|
|
|
|
bool require_fallback) {
|
|
|
|
|
|
|
|
const char *previous_interface = NULL;
|
|
|
|
struct node_vtable *c;
|
|
|
|
struct node *n;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(s);
|
|
|
|
assert(prefix);
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
n = hashmap_get(bus->nodes, prefix);
|
|
|
|
if (!n)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
LIST_FOREACH(vtables, c, n->vtables) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
void *u = NULL;
|
|
|
|
|
|
|
|
if (require_fallback && !c->is_fallback)
|
|
|
|
continue;
|
|
|
|
if (streq_ptr(c->interface, previous_interface))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* If a child-node already handled this interface, we
|
|
|
|
* skip it on any of its parents. The child vtables
|
|
|
|
* always fully override any conflicting vtables of
|
|
|
|
* any parent node. */
|
|
|
|
if (set_get(s, c->interface))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = node_vtable_get_userdata(bus, path, c, &u, &error);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
if (r == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = set_put(s, c->interface);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(m, "s", c->interface);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
previous_interface = c->interface;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
|
|
|
|
_cleanup_set_free_ Set *s = NULL;
|
2019-02-04 14:29:28 +01:00
|
|
|
_cleanup_free_ char *prefix = NULL;
|
|
|
|
size_t pl;
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(path);
|
|
|
|
|
|
|
|
/* see sd_bus_emit_object_added() for details */
|
|
|
|
|
|
|
|
s = set_new(&string_hash_ops);
|
|
|
|
if (!s)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = object_removed_append_all_prefix(bus, m, s, path, path, false);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
|
2019-02-04 14:29:28 +01:00
|
|
|
pl = strlen(path);
|
|
|
|
assert(pl <= BUS_PATH_SIZE_MAX);
|
|
|
|
prefix = new(char, pl + 1);
|
|
|
|
if (!prefix)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
|
|
|
|
r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-21 13:44:35 +02:00
|
|
|
_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
2015-07-21 12:59:56 +02:00
|
|
|
struct node *object_manager;
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is like sd_bus_emit_object_added(), but emits an
|
|
|
|
* InterfacesRemoved signal on the given path. This only includes any
|
|
|
|
* registered interfaces but skips the properties. Note that this will
|
|
|
|
* call into the find() callbacks of any registered vtable. Therefore,
|
|
|
|
* you must call this function before destroying/unlinking your object.
|
|
|
|
* Otherwise, the list of interfaces will be incomplete. However, note
|
|
|
|
* that this will *NOT* call into any property callback. Therefore, the
|
|
|
|
* object might be in an "destructed" state, as long as we can find it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
|
|
|
|
|
|
|
if (!BUS_IS_OPEN(bus->state))
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2015-07-21 12:59:56 +02:00
|
|
|
r = bus_find_parent_object_manager(bus, &object_manager, path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
return -ESRCH;
|
|
|
|
|
2018-07-18 12:16:33 +02:00
|
|
|
BUS_DONT_DESTROY(bus);
|
|
|
|
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
do {
|
|
|
|
bus->nodes_modified = false;
|
|
|
|
m = sd_bus_message_unref(m);
|
|
|
|
|
2015-07-21 12:59:56 +02:00
|
|
|
r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
|
bus: add sd_bus_emit_object_{added/removed}()
This implements two new helpers, discussed on systemd-devel about 1 year
ago:
sd_bus_emit_object_added()
sd_bus_emit_object_removed()
Both calls are equivalent to their respective counterpart
sd_bus_emit_interfaces_{added/removed}(), but can figure out the list of
interfaces themselves, instead of requiring the caller to provide them.
Furthermore, both calls properly deal with builtin interfaces provided via
org.freedesktop.DBus.* and alike.
Both calls simply traverse a node and all its parent nodes to figure out a
list of all interfaces registered as vtable or fallback. It then appends
each of them, similar to the interfaces_{added/removed}() helpers.
Note that interfaces_{added/removed}() runs a parent traversal for *each*
passed interface. Therefore, it can simply bail out, once it found a
parent node that implements a given interface.
With object_{added/removed}() we cannot know the registered interfaces in
advance, thus, we cannot run one traversal per node. Instead, we run a
single traversal and remember all interfaces that we added. Therefore, a
child-interface overrides all conflicting parent-interfaces. We keep a
"Set *s" context to track those while climbing up the tree.
2014-12-30 11:37:35 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, 'o', path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(m, 'a', "s");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = object_removed_append_all(bus, m, path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
} while (bus->nodes_modified);
|
|
|
|
|
|
|
|
return sd_bus_send(bus, m, NULL);
|
|
|
|
}
|
|
|
|
|
2013-10-11 23:22:29 +02:00
|
|
|
static int interfaces_added_append_one_prefix(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
const char *prefix,
|
|
|
|
const char *path,
|
|
|
|
const char *interface,
|
|
|
|
bool require_fallback) {
|
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-11-19 21:12:59 +01:00
|
|
|
bool found_interface = false;
|
2013-10-11 23:22:29 +02:00
|
|
|
struct node_vtable *c;
|
|
|
|
struct node *n;
|
|
|
|
void *u = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(prefix);
|
|
|
|
assert(path);
|
|
|
|
assert(interface);
|
|
|
|
|
|
|
|
n = hashmap_get(bus->nodes, prefix);
|
|
|
|
if (!n)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
LIST_FOREACH(vtables, c, n->vtables) {
|
|
|
|
if (require_fallback && !c->is_fallback)
|
|
|
|
continue;
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!streq(c->interface, interface))
|
|
|
|
continue;
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = node_vtable_get_userdata(bus, path, c, &u, &error);
|
2013-11-19 21:12:59 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
if (r == 0)
|
|
|
|
continue;
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!found_interface) {
|
|
|
|
r = sd_bus_message_append_basic(m, 's', interface);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = sd_bus_message_open_container(m, 'a', "{sv}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
found_interface = true;
|
|
|
|
}
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
r = vtable_append_all_properties(bus, m, path, c, u, &error);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
|
|
|
}
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (found_interface) {
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
return found_interface;
|
2013-10-11 23:22:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int interfaces_added_append_one(
|
|
|
|
sd_bus *bus,
|
|
|
|
sd_bus_message *m,
|
|
|
|
const char *path,
|
|
|
|
const char *interface) {
|
|
|
|
|
2019-02-04 14:29:28 +01:00
|
|
|
_cleanup_free_ char *prefix = NULL;
|
|
|
|
size_t pl;
|
2013-10-11 23:22:29 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(path);
|
|
|
|
assert(interface);
|
|
|
|
|
|
|
|
r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2019-02-04 14:29:28 +01:00
|
|
|
pl = strlen(path);
|
|
|
|
assert(pl <= BUS_PATH_SIZE_MAX);
|
|
|
|
prefix = new(char, pl + 1);
|
|
|
|
if (!prefix)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2013-10-11 23:22:29 +02:00
|
|
|
OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
|
|
|
|
r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
|
|
|
|
if (r != 0)
|
|
|
|
return r;
|
2013-10-14 21:58:06 +02:00
|
|
|
if (bus->nodes_modified)
|
|
|
|
return 0;
|
2013-10-11 23:22:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOENT;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2013-11-06 08:33:42 +01:00
|
|
|
_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
2015-07-21 12:59:56 +02:00
|
|
|
struct node *object_manager;
|
2013-10-11 23:22:29 +02:00
|
|
|
char **i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 23:22:29 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
|
|
|
|
2014-03-19 21:41:21 +01:00
|
|
|
if (!BUS_IS_OPEN(bus->state))
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2013-10-11 23:22:29 +02:00
|
|
|
if (strv_isempty(interfaces))
|
|
|
|
return 0;
|
|
|
|
|
2015-07-21 12:59:56 +02:00
|
|
|
r = bus_find_parent_object_manager(bus, &object_manager, path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
return -ESRCH;
|
|
|
|
|
2018-07-18 12:16:33 +02:00
|
|
|
BUS_DONT_DESTROY(bus);
|
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
do {
|
|
|
|
bus->nodes_modified = false;
|
2014-04-23 06:57:00 +02:00
|
|
|
m = sd_bus_message_unref(m);
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2015-07-21 12:59:56 +02:00
|
|
|
r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
|
2013-10-14 21:58:06 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-10-11 23:22:29 +02:00
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
r = sd_bus_message_append_basic(m, 'o', path);
|
2013-10-11 23:22:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
|
2013-10-11 23:22:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
STRV_FOREACH(i, interfaces) {
|
|
|
|
assert_return(interface_name_is_valid(*i), -EINVAL);
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(m, 'e', "sa{sv}");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = interfaces_added_append_one(bus, m, path, *i);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
break;
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bus->nodes_modified)
|
|
|
|
continue;
|
|
|
|
|
2013-10-11 23:22:29 +02:00
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-10-14 21:58:06 +02:00
|
|
|
} while (bus->nodes_modified);
|
2013-10-11 23:22:29 +02:00
|
|
|
|
|
|
|
return sd_bus_send(bus, m, NULL);
|
|
|
|
}
|
|
|
|
|
2013-11-06 08:33:42 +01:00
|
|
|
_public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
|
2013-10-29 19:53:43 +01:00
|
|
|
char **interfaces;
|
2013-10-11 23:22:29 +02:00
|
|
|
|
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 23:22:29 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
|
|
|
|
2014-03-19 21:41:21 +01:00
|
|
|
if (!BUS_IS_OPEN(bus->state))
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2013-10-29 19:53:43 +01:00
|
|
|
interfaces = strv_from_stdarg_alloca(interface);
|
2013-10-11 23:22:29 +02:00
|
|
|
|
|
|
|
return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
|
|
|
|
}
|
|
|
|
|
2013-11-06 08:33:42 +01:00
|
|
|
_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
2015-07-21 12:59:56 +02:00
|
|
|
struct node *object_manager;
|
2013-10-11 23:22:29 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 23:22:29 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
|
|
|
|
2014-03-19 21:41:21 +01:00
|
|
|
if (!BUS_IS_OPEN(bus->state))
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2013-10-11 23:22:29 +02:00
|
|
|
if (strv_isempty(interfaces))
|
|
|
|
return 0;
|
|
|
|
|
2015-07-21 12:59:56 +02:00
|
|
|
r = bus_find_parent_object_manager(bus, &object_manager, path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
|
2013-10-11 23:22:29 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append_basic(m, 'o', path);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append_strv(m, interfaces);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return sd_bus_send(bus, m, NULL);
|
|
|
|
}
|
|
|
|
|
2013-11-06 08:33:42 +01:00
|
|
|
_public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
|
2013-10-29 19:53:43 +01:00
|
|
|
char **interfaces;
|
2013-10-11 23:22:29 +02:00
|
|
|
|
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 23:22:29 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
|
|
|
|
2014-03-19 21:41:21 +01:00
|
|
|
if (!BUS_IS_OPEN(bus->state))
|
|
|
|
return -ENOTCONN;
|
|
|
|
|
2013-10-29 19:53:43 +01:00
|
|
|
interfaces = strv_from_stdarg_alloca(interface);
|
2013-10-11 23:22:29 +02:00
|
|
|
|
|
|
|
return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
_public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
|
|
|
|
sd_bus_slot *s;
|
2013-10-11 20:18:10 +02:00
|
|
|
struct node *n;
|
2014-05-15 01:15:30 +02:00
|
|
|
int r;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(bus, -EINVAL);
|
2018-01-22 22:26:44 +01:00
|
|
|
assert_return(bus = bus_resolve(bus), -ENOPKG);
|
2013-10-11 21:27:59 +02:00
|
|
|
assert_return(object_path_is_valid(path), -EINVAL);
|
|
|
|
assert_return(!bus_pid_changed(bus), -ECHILD);
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
n = bus_node_allocate(bus, path);
|
|
|
|
if (!n)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
|
|
|
|
if (!s) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
s->node_object_manager.node = n;
|
|
|
|
LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
|
|
|
|
bus->nodes_modified = true;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
if (slot)
|
|
|
|
*slot = s;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
return 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
fail:
|
|
|
|
sd_bus_slot_unref(s);
|
2013-10-11 20:18:10 +02:00
|
|
|
bus_node_gc(bus, n);
|
2013-10-11 21:27:59 +02:00
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
return r;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|