2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-10-11 20:18:10 +02:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2013 Lennart Poettering
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
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"
|
|
|
|
#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;
|
2017-09-16 13:32:59 +02:00
|
|
|
void *u, *found_u;
|
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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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 */
|
|
|
|
CHILDREN_RECURSIVE = (1U << 1),
|
|
|
|
/* if set, add_subtree() scans object-manager hierarchies recursively */
|
|
|
|
CHILDREN_SUBHIERARCHIES = (1U << 0),
|
|
|
|
};
|
|
|
|
|
2013-10-11 21:27:59 +02:00
|
|
|
static int add_subtree_to_set(
|
|
|
|
sd_bus *bus,
|
|
|
|
const char *prefix,
|
|
|
|
struct node *n,
|
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
|
|
|
unsigned int 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,
|
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
|
|
|
unsigned int 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;
|
|
|
|
|
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;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (type != 'v' || !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));
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2013-10-11 20:18:10 +02:00
|
|
|
for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; 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;
|
|
|
|
|
2015-08-25 01:45:33 +02:00
|
|
|
if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
|
|
|
|
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;
|
|
|
|
|
2013-10-30 02:38:52 +01:00
|
|
|
found_interface = !iface ||
|
2013-10-29 21:38:31 +01:00
|
|
|
streq(iface, "org.freedesktop.DBus.Properties") ||
|
|
|
|
streq(iface, "org.freedesktop.DBus.Peer") ||
|
|
|
|
streq(iface, "org.freedesktop.DBus.Introspectable");
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
static int process_introspect(
|
|
|
|
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;
|
2013-11-19 21:12:59 +01:00
|
|
|
const char *previous_interface = NULL;
|
2013-10-11 20:18:10 +02:00
|
|
|
struct introspect intro;
|
|
|
|
struct node_vtable *c;
|
|
|
|
bool empty;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(m);
|
|
|
|
assert(n);
|
|
|
|
assert(found_object);
|
|
|
|
|
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, 0, &s, &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
|
|
|
|
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;
|
|
|
|
|
2013-11-22 01:14:57 +01:00
|
|
|
r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
|
|
|
|
if (r < 0) {
|
|
|
|
r = bus_maybe_reply_error(m, r, &error);
|
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
if (bus->nodes_modified) {
|
|
|
|
r = 0;
|
|
|
|
goto finish;
|
|
|
|
}
|
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;
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (!streq_ptr(previous_interface, c->interface)) {
|
|
|
|
|
|
|
|
if (previous_interface)
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs(" </interface>\n", intro.f);
|
2013-11-19 21:12:59 +01:00
|
|
|
|
|
|
|
fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = introspect_write_interface(&intro, c->vtable);
|
2013-10-11 20:18:10 +02:00
|
|
|
if (r < 0)
|
|
|
|
goto finish;
|
2013-11-19 21:12:59 +01:00
|
|
|
|
|
|
|
previous_interface = c->interface;
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
2013-11-19 21:12:59 +01:00
|
|
|
if (previous_interface)
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs(" </interface>\n", intro.f);
|
2013-11-19 21:12:59 +01:00
|
|
|
|
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 */
|
|
|
|
r = bus_node_exists(bus, n, m->path, require_fallback);
|
2017-06-21 20:42:28 +02:00
|
|
|
if (r <= 0) {
|
|
|
|
r = bus_maybe_reply_error(m, r, &error);
|
2014-09-17 10:32:49 +02:00
|
|
|
goto finish;
|
2017-06-21 20:42:28 +02:00
|
|
|
}
|
2014-09-17 10:32:49 +02:00
|
|
|
if (bus->nodes_modified) {
|
|
|
|
r = 0;
|
2013-10-11 20:18:10 +02:00
|
|
|
goto finish;
|
2014-09-17 10:32:49 +02:00
|
|
|
}
|
2013-10-11 20:18:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
*found_object = true;
|
|
|
|
|
|
|
|
r = introspect_write_child_nodes(&intro, s, m->path);
|
|
|
|
if (r < 0)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
r = introspect_finish(&intro, bus, m, &reply);
|
|
|
|
if (r < 0)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
r = sd_bus_send(bus, reply, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
r = 1;
|
|
|
|
|
|
|
|
finish:
|
|
|
|
introspect_free(&intro);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
2013-10-11 23:20:54 +02:00
|
|
|
char *prefix;
|
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 */
|
2013-10-11 23:20:54 +02:00
|
|
|
prefix = alloca(strlen(path) + 1);
|
|
|
|
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) {
|
|
|
|
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);
|
|
|
|
do {
|
2013-10-11 23:20:54 +02:00
|
|
|
char prefix[pl+1];
|
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") ||
|
|
|
|
sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
|
|
|
|
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,
|
2013-10-11 20:18:10 +02:00
|
|
|
"Unknown property or interface.");
|
|
|
|
else
|
|
|
|
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,
|
2013-10-11 20:18:10 +02:00
|
|
|
"Unknown method '%s' or interface '%s'.", m->member, m->interface);
|
|
|
|
|
|
|
|
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) {
|
|
|
|
char *prefix;
|
|
|
|
|
|
|
|
prefix = alloca(strlen(path) + 1);
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-10-04 00:22:41 +02:00
|
|
|
static void vtable_member_hash_func(const void *a, struct siphash *state) {
|
2013-10-11 20:18:10 +02:00
|
|
|
const struct vtable_member *m = a;
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
static int vtable_member_compare_func(const void *a, const void *b) {
|
|
|
|
const struct vtable_member *x = a, *y = b;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
static const struct hash_ops vtable_member_hash_ops = {
|
|
|
|
.hash = vtable_member_hash_func,
|
|
|
|
.compare = vtable_member_compare_func
|
|
|
|
};
|
|
|
|
|
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;
|
|
|
|
|
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);
|
|
|
|
assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-05-15 01:15:30 +02:00
|
|
|
for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
|
2013-10-11 20:18:10 +02:00
|
|
|
|
|
|
|
switch (v->type) {
|
|
|
|
|
|
|
|
case _SD_BUS_VTABLE_METHOD: {
|
|
|
|
struct vtable_member *m;
|
|
|
|
|
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) ||
|
|
|
|
!(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:
|
|
|
|
|
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) ||
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2013-12-22 03:22:34 +01:00
|
|
|
for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; 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;
|
|
|
|
|
|
|
|
for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; 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) {
|
|
|
|
|
2013-10-14 19:53:56 +02:00
|
|
|
BUS_DONT_DESTROY(bus);
|
2013-12-23 17:30:21 +01:00
|
|
|
bool found_interface = false;
|
2013-10-11 23:20:54 +02:00
|
|
|
char *prefix;
|
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
|
|
|
|
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;
|
|
|
|
|
|
|
|
prefix = alloca(strlen(path) + 1);
|
|
|
|
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;
|
|
|
|
char *prefix;
|
|
|
|
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;
|
|
|
|
|
|
|
|
prefix = alloca(strlen(path) + 1);
|
|
|
|
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) {
|
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
|
|
|
BUS_DONT_DESTROY(bus);
|
|
|
|
|
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;
|
|
|
|
|
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;
|
|
|
|
char *prefix;
|
|
|
|
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;
|
|
|
|
|
|
|
|
prefix = alloca(strlen(path) + 1);
|
|
|
|
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) {
|
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
|
|
|
BUS_DONT_DESTROY(bus);
|
|
|
|
|
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;
|
|
|
|
|
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) {
|
|
|
|
|
|
|
|
char *prefix;
|
|
|
|
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
|
|
|
|
|
|
|
prefix = alloca(strlen(path) + 1);
|
|
|
|
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) {
|
2013-10-14 19:53:56 +02:00
|
|
|
BUS_DONT_DESTROY(bus);
|
|
|
|
|
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;
|
|
|
|
|
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
|
|
|
}
|