[systemd-commits] 2 commits - Makefile.am src/libsystemd-bus src/systemd
Lennart Poettering
lennart at kemper.freedesktop.org
Fri Oct 11 11:35:30 PDT 2013
Makefile.am | 3
src/libsystemd-bus/bus-convenience.c | 280 +++
src/libsystemd-bus/bus-introspect.c | 14
src/libsystemd-bus/bus-objects.c | 1938 ++++++++++++++++++++++++++
src/libsystemd-bus/bus-objects.h | 26
src/libsystemd-bus/sd-bus.c | 2590 ++---------------------------------
src/systemd/sd-bus-vtable.h | 36
src/systemd/sd-bus.h | 5
8 files changed, 2485 insertions(+), 2407 deletions(-)
New commits:
commit 77a874a3fb6f65d2308c20827f005c43bb075752
Author: Lennart Poettering <lennart at poettering.net>
Date: Fri Oct 11 20:32:16 2013 +0200
bus: don't rely on gccisms/c11 in public header files.
One day sd-bus.h should become a public header file. We generally try to
be conservative in language features we use in public headers (much
unlike in private code), hence don't make use of anonymous unions in
structs for the vtable definitions.
diff --git a/src/libsystemd-bus/bus-introspect.c b/src/libsystemd-bus/bus-introspect.c
index 8dc9a2d..c04d9b5 100644
--- a/src/libsystemd-bus/bus-introspect.c
+++ b/src/libsystemd-bus/bus-introspect.c
@@ -128,9 +128,9 @@ int introspect_write_interface(struct introspect *i, const char *interface, cons
break;
case _SD_BUS_VTABLE_METHOD:
- fprintf(i->f, " <method name=\"%s\">\n", v->method.member);
- introspect_write_arguments(i, v->method.signature, "in");
- introspect_write_arguments(i, v->method.result, "out");
+ fprintf(i->f, " <method name=\"%s\">\n", v->x.method.member);
+ introspect_write_arguments(i, v->x.method.signature, "in");
+ introspect_write_arguments(i, v->x.method.result, "out");
introspect_write_flags(i, v->type, v->flags);
fputs(" </method>\n", i->f);
break;
@@ -138,16 +138,16 @@ int introspect_write_interface(struct introspect *i, const char *interface, cons
case _SD_BUS_VTABLE_PROPERTY:
case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
fprintf(i->f, " <property name=\"%s\" type=\"%s\" access=\"%s\">\n",
- v->property.member,
- v->property.signature,
+ v->x.property.member,
+ v->x.property.signature,
v->type == _SD_BUS_VTABLE_WRITABLE_PROPERTY ? "readwrite" : "read");
introspect_write_flags(i, v->type, v->flags);
fputs(" </property>\n", i->f);
break;
case _SD_BUS_VTABLE_SIGNAL:
- fprintf(i->f, " <signal name=\"%s\">\n", v->signal.member);
- introspect_write_arguments(i, v->signal.signature, NULL);
+ fprintf(i->f, " <signal name=\"%s\">\n", v->x.signal.member);
+ introspect_write_arguments(i, v->x.signal.signature, NULL);
introspect_write_flags(i, v->type, v->flags);
fputs(" </signal>\n", i->f);
break;
diff --git a/src/libsystemd-bus/bus-objects.c b/src/libsystemd-bus/bus-objects.c
index da49d75..8d95376 100644
--- a/src/libsystemd-bus/bus-objects.c
+++ b/src/libsystemd-bus/bus-objects.c
@@ -57,7 +57,7 @@ static int node_vtable_get_userdata(
static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
assert(p);
- return (uint8_t*) u + p->property.offset;
+ return (uint8_t*) u + p->x.property.offset;
}
static int vtable_property_get_userdata(
@@ -242,19 +242,19 @@ static int method_callbacks_run(
if (r < 0)
return r;
- if (!streq(strempty(c->vtable->method.signature), signature)) {
+ if (!streq(strempty(c->vtable->x.method.signature), signature)) {
r = sd_bus_reply_method_errorf(bus, m,
"org.freedesktop.DBus.Error.InvalidArgs",
"Invalid arguments '%s' to call %s:%s, expecting '%s'.",
- signature, c->interface, c->member, strempty(c->vtable->method.signature));
+ signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
if (r < 0)
return r;
return 1;
}
- if (c->vtable->method.handler)
- return c->vtable->method.handler(bus, m, u);
+ if (c->vtable->x.method.handler)
+ return c->vtable->x.method.handler(bus, m, u);
/* If the method callback is NULL, make this a successful NOP */
r = sd_bus_reply_method_return(bus, m, NULL);
@@ -280,14 +280,15 @@ static int invoke_property_get(
assert(bus);
assert(v);
- if (v->property.get)
- return v->property.get(bus, path, interface, property, m, error, userdata);
+ if (v->x.property.get)
+ return v->x.property.get(bus, path, interface, property, m, error, userdata);
/* Automatic handling if no callback is defined. */
- assert(bus_type_is_basic(v->property.signature[0]));
+ assert(signature_is_single(v->x.property.signature, false));
+ assert(bus_type_is_basic(v->x.property.signature[0]));
- switch (v->property.signature[0]) {
+ switch (v->x.property.signature[0]) {
case SD_BUS_TYPE_STRING:
case SD_BUS_TYPE_OBJECT_PATH:
@@ -300,7 +301,7 @@ static int invoke_property_get(
break;
}
- r = sd_bus_message_append_basic(m, v->property.signature[0], p);
+ r = sd_bus_message_append_basic(m, v->x.property.signature[0], p);
if (r < 0)
return r;
@@ -322,15 +323,15 @@ static int invoke_property_set(
assert(bus);
assert(v);
- if (v->property.set)
- return v->property.set(bus, path, interface, property, value, error, userdata);
+ if (v->x.property.set)
+ return v->x.property.set(bus, path, interface, property, value, error, userdata);
/* Automatic handling if no callback is defined. */
- assert(signature_is_single(v->property.signature, false));
- assert(bus_type_is_basic(v->property.signature[0]));
+ assert(signature_is_single(v->x.property.signature, false));
+ assert(bus_type_is_basic(v->x.property.signature[0]));
- switch (v->property.signature[0]) {
+ switch (v->x.property.signature[0]) {
case SD_BUS_TYPE_STRING:
case SD_BUS_TYPE_OBJECT_PATH:
@@ -338,7 +339,7 @@ static int invoke_property_set(
const char *p;
char *n;
- r = sd_bus_message_read_basic(value, v->property.signature[0], &p);
+ r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
if (r < 0)
return r;
@@ -353,7 +354,7 @@ static int invoke_property_set(
}
default:
- r = sd_bus_message_read_basic(value, v->property.signature[0], userdata);
+ r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
if (r < 0)
return r;
@@ -396,7 +397,7 @@ static int property_get_set_callbacks_run(
c->last_iteration = bus->iteration_counter;
if (is_get) {
- r = sd_bus_message_open_container(reply, 'v', c->vtable->property.signature);
+ r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
if (r < 0)
return r;
@@ -420,7 +421,7 @@ static int property_get_set_callbacks_run(
if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
sd_bus_error_setf(&error, "org.freedesktop.DBus.Error.PropertyReadOnly", "Property '%s' is not writable.", c->member);
else {
- r = sd_bus_message_enter_container(m, 'v', c->vtable->property.signature);
+ r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
if (r < 0)
return r;
@@ -476,11 +477,11 @@ static int vtable_append_all_properties(
if (r < 0)
return r;
- r = sd_bus_message_open_container(reply, 'v', v->property.signature);
+ r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
if (r < 0)
return r;
- r = invoke_property_get(bus, v, path, c->interface, v->property.member, reply, error, vtable_property_convert_userdata(v, userdata));
+ r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, error, vtable_property_convert_userdata(v, userdata));
if (r < 0)
return r;
@@ -1321,7 +1322,7 @@ static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
key.path = w->node->path;
key.interface = w->interface;
- key.member = v->method.member;
+ key.member = v->x.method.member;
x = hashmap_remove(bus->vtable_methods, &key);
break;
@@ -1333,7 +1334,7 @@ static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
key.path = w->node->path;
key.interface = w->interface;
- key.member = v->property.member;
+ key.member = v->x.property.member;
x = hashmap_remove(bus->vtable_properties, &key);
break;
}}
@@ -1390,7 +1391,7 @@ static int add_object_vtable_internal(
return -EINVAL;
if (!interface_name_is_valid(interface))
return -EINVAL;
- if (!vtable || vtable[0].type != _SD_BUS_VTABLE_START || vtable[0].start.element_size != sizeof(struct sd_bus_vtable))
+ if (!vtable || vtable[0].type != _SD_BUS_VTABLE_START || vtable[0].x.start.element_size != sizeof(struct sd_bus_vtable))
return -EINVAL;
if (bus_pid_changed(bus))
return -ECHILD;
@@ -1444,10 +1445,10 @@ static int add_object_vtable_internal(
case _SD_BUS_VTABLE_METHOD: {
struct vtable_member *m;
- if (!member_name_is_valid(v->method.member) ||
- !signature_is_valid(strempty(v->method.signature), false) ||
- !signature_is_valid(strempty(v->method.result), false) ||
- !(v->method.handler || (isempty(v->method.signature) && isempty(v->method.result))) ||
+ 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))) ||
v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
r = -EINVAL;
goto fail;
@@ -1462,7 +1463,7 @@ static int add_object_vtable_internal(
m->parent = c;
m->path = n->path;
m->interface = c->interface;
- m->member = v->method.member;
+ m->member = v->x.method.member;
m->vtable = v;
r = hashmap_put(bus->vtable_methods, m, m);
@@ -1476,7 +1477,7 @@ static int add_object_vtable_internal(
case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
- if (!(v->property.set || bus_type_is_basic(v->property.signature[0]))) {
+ if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
r = -EINVAL;
goto fail;
}
@@ -1486,9 +1487,9 @@ static int add_object_vtable_internal(
case _SD_BUS_VTABLE_PROPERTY: {
struct vtable_member *m;
- if (!member_name_is_valid(v->property.member) ||
- !signature_is_single(v->property.signature, false) ||
- !(v->property.get || bus_type_is_basic(v->property.signature[0])) ||
+ if (!member_name_is_valid(v->x.property.member) ||
+ !signature_is_single(v->x.property.signature, false) ||
+ !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
(v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
r = -EINVAL;
@@ -1505,7 +1506,7 @@ static int add_object_vtable_internal(
m->parent = c;
m->path = n->path;
m->interface = c->interface;
- m->member = v->property.member;
+ m->member = v->x.property.member;
m->vtable = v;
r = hashmap_put(bus->vtable_properties, m, m);
@@ -1519,8 +1520,8 @@ static int add_object_vtable_internal(
case _SD_BUS_VTABLE_SIGNAL:
- if (!member_name_is_valid(v->signal.member) ||
- !signature_is_single(strempty(v->signal.signature), false)) {
+ if (!member_name_is_valid(v->x.signal.member) ||
+ !signature_is_single(strempty(v->x.signal.signature), false)) {
r = -EINVAL;
goto fail;
}
@@ -1776,7 +1777,7 @@ static int emit_properties_changed_on_interface(
if (r < 0)
return r;
- r = sd_bus_message_open_container(m, 'v', v->vtable->property.signature);
+ r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
if (r < 0)
return r;
diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h
index 5203bf1..82e50d2 100644
--- a/src/systemd/sd-bus-vtable.h
+++ b/src/systemd/sd-bus-vtable.h
@@ -70,53 +70,53 @@ struct sd_bus_vtable {
sd_bus_property_set_t set;
size_t offset;
} property;
- };
+ } x;
};
#define SD_BUS_VTABLE_START(_flags) \
{ \
.type = _SD_BUS_VTABLE_START, \
.flags = _flags, \
- .start.element_size = sizeof(sd_bus_vtable), \
+ .x.start.element_size = sizeof(sd_bus_vtable), \
}
#define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags) \
{ \
.type = _SD_BUS_VTABLE_METHOD, \
.flags = _flags, \
- .method.member = _member, \
- .method.signature = _signature, \
- .method.result = _result, \
- .method.handler = _handler, \
+ .x.method.member = _member, \
+ .x.method.signature = _signature, \
+ .x.method.result = _result, \
+ .x.method.handler = _handler, \
}
#define SD_BUS_SIGNAL(_member, _signature, _flags) \
{ \
.type = _SD_BUS_VTABLE_SIGNAL, \
.flags = _flags, \
- .signal.member = _member, \
- .signal.signature = _signature, \
+ .x.signal.member = _member, \
+ .x.signal.signature = _signature, \
}
-#define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \
+#define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \
{ \
.type = _SD_BUS_VTABLE_PROPERTY, \
.flags = _flags, \
- .property.member = _member, \
- .property.signature = _signature, \
- .property.get = _get, \
- .property.offset = _offset, \
+ .x.property.member = _member, \
+ .x.property.signature = _signature, \
+ .x.property.get = _get, \
+ .x.property.offset = _offset, \
}
#define SD_BUS_WRITABLE_PROPERTY(_member, _signature, _get, _set, _offset, _flags) \
{ \
.type = _SD_BUS_VTABLE_WRITABLE_PROPERTY, \
.flags = _flags, \
- .property.member = _member, \
- .property.signature = _signature, \
- .property.get = _get, \
- .property.set = _set, \
- .property.offset = _offset, \
+ .x.property.member = _member, \
+ .x.property.signature = _signature, \
+ .x.property.get = _get, \
+ .x.property.set = _set, \
+ .x.property.offset = _offset, \
}
#define SD_BUS_VTABLE_END \
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index 2c12cf0..bbeec13 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -147,6 +147,7 @@ int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type);
int sd_bus_message_get_serial(sd_bus_message *m, uint64_t *serial);
int sd_bus_message_get_reply_serial(sd_bus_message *m, uint64_t *serial);
int sd_bus_message_get_no_reply(sd_bus_message *m);
+int sd_bus_message_get_signature(sd_bus_message *m, int complete, const char **signature);
const char *sd_bus_message_get_path(sd_bus_message *m);
const char *sd_bus_message_get_interface(sd_bus_message *m);
@@ -201,8 +202,6 @@ int sd_bus_message_exit_container(sd_bus_message *m);
int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents);
int sd_bus_message_rewind(sd_bus_message *m, int complete);
-int sd_bus_message_get_signature(sd_bus_message *m, int complete, const char **signature);
-
/* Convenience calls */
int sd_bus_call_method(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *error, sd_bus_message **reply, const char *types, ...);
@@ -235,8 +234,8 @@ int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machi
/* Error structures */
-#define SD_BUS_ERROR_NULL ((sd_bus_error) {NULL, NULL, 0})
#define SD_BUS_ERROR_MAKE(name, message) ((sd_bus_error) {(name), (message), 0})
+#define SD_BUS_ERROR_NULL SD_BUS_ERROR_MAKE(NULL, NULL)
void sd_bus_error_free(sd_bus_error *e);
int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) _sd_printf_attr_(3, 0);
commit 992c052c34d180dd9fe6bd4f89fff3a481a729dc
Author: Lennart Poettering <lennart at poettering.net>
Date: Fri Oct 11 20:18:10 2013 +0200
bus: split up overly long sd-bus.c into three files
diff --git a/Makefile.am b/Makefile.am
index 12c7d7c..e5cd7a5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1966,6 +1966,9 @@ libsystemd_bus_la_SOURCES = \
src/libsystemd-bus/bus-bloom.h \
src/libsystemd-bus/bus-introspect.c \
src/libsystemd-bus/bus-introspect.h \
+ src/libsystemd-bus/bus-objects.c \
+ src/libsystemd-bus/bus-objects.h \
+ src/libsystemd-bus/bus-convenience.c \
src/libsystemd-bus/kdbus.h \
src/libsystemd-bus/sd-memfd.c \
src/libsystemd-bus/sd-event.c
diff --git a/src/libsystemd-bus/bus-convenience.c b/src/libsystemd-bus/bus-convenience.c
new file mode 100644
index 0000000..3d223c0
--- /dev/null
+++ b/src/libsystemd-bus/bus-convenience.c
@@ -0,0 +1,280 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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/>.
+***/
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-signature.h"
+
+int sd_bus_emit_signal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *types, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ r = sd_bus_message_new_signal(bus, path, interface, member, &m);
+ if (r < 0)
+ return r;
+
+ if (!isempty(types)) {
+ va_list ap;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_send(bus, m, NULL);
+}
+
+int sd_bus_call_method(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ sd_bus_message **reply,
+ const char *types, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ if (!bus)
+
+ return -EINVAL;
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ r = sd_bus_message_new_method_call(bus, destination, path, interface, member, &m);
+ if (r < 0)
+ return r;
+
+ if (!isempty(types)) {
+ va_list ap;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
+}
+
+int sd_bus_reply_method_return(
+ sd_bus *bus,
+ sd_bus_message *call,
+ const char *types, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!call)
+ return -EINVAL;
+ if (!call->sealed)
+ return -EPERM;
+ if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EINVAL;
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 0;
+
+ r = sd_bus_message_new_method_return(bus, call, &m);
+ if (r < 0)
+ return r;
+
+ if (!isempty(types)) {
+ va_list ap;
+
+ va_start(ap, types);
+ r = bus_message_append_ap(m, types, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_send(bus, m, NULL);
+}
+
+int sd_bus_reply_method_error(
+ sd_bus *bus,
+ sd_bus_message *call,
+ const sd_bus_error *e) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!call)
+ return -EINVAL;
+ if (!call->sealed)
+ return -EPERM;
+ if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EINVAL;
+ if (!sd_bus_error_is_set(e))
+ return -EINVAL;
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
+ return 0;
+
+ r = sd_bus_message_new_method_error(bus, call, e, &m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send(bus, m, NULL);
+}
+
+int sd_bus_reply_method_errorf(
+ sd_bus *bus,
+ sd_bus_message *call,
+ const char *name,
+ const char *format,
+ ...) {
+
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ va_list ap;
+ int r;
+
+ error.name = strdup(name);
+ if (!error.name)
+ return -ENOMEM;
+
+ error.need_free = true;
+
+ if (format) {
+ va_start(ap, format);
+ r = vasprintf((char**) &error.message, format, ap);
+ va_end(ap);
+
+ if (r < 0)
+ return -ENOMEM;
+ }
+
+ return sd_bus_reply_method_error(bus, call, &error);
+}
+
+int sd_bus_get_property(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ sd_bus_message **reply,
+ const char *type) {
+
+ sd_bus_message *rep = NULL;
+ int r;
+
+ if (interface && !interface_name_is_valid(interface))
+ return -EINVAL;
+ if (!member_name_is_valid(member))
+ return -EINVAL;
+ if (!signature_is_single(type, false))
+ return -EINVAL;
+ if (!reply)
+ return -EINVAL;
+
+ r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(rep, 'v', type);
+ if (r < 0) {
+ sd_bus_message_unref(rep);
+ return r;
+ }
+
+ *reply = rep;
+ return 0;
+}
+
+int sd_bus_set_property(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_error *error,
+ const char *type, ...) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ va_list ap;
+ int r;
+
+ if (interface && !interface_name_is_valid(interface))
+ return -EINVAL;
+ if (!member_name_is_valid(member))
+ return -EINVAL;
+ if (!signature_is_single(type, false))
+ return -EINVAL;
+
+ r = sd_bus_message_new_method_call(bus, destination, path, "org.freedesktop.DBus.Properties", "Set", &m);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "ss", strempty(interface), member);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'v', type);
+ if (r < 0)
+ return r;
+
+ va_start(ap, type);
+ r = bus_message_append_ap(m, type, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ if (r < 0)
+ return r;
+
+ return sd_bus_send_with_reply_and_block(bus, m, 0, error, NULL);
+}
diff --git a/src/libsystemd-bus/bus-objects.c b/src/libsystemd-bus/bus-objects.c
new file mode 100644
index 0000000..da49d75
--- /dev/null
+++ b/src/libsystemd-bus/bus-objects.c
@@ -0,0 +1,1937 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ 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/>.
+***/
+
+#include "strv.h"
+#include "set.h"
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-type.h"
+#include "bus-signature.h"
+#include "bus-introspect.h"
+#include "bus-objects.h"
+
+static int node_vtable_get_userdata(
+ sd_bus *bus,
+ const char *path,
+ struct node_vtable *c,
+ void **userdata) {
+
+ void *u;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(c);
+
+ u = c->userdata;
+ if (c->find) {
+ r = c->find(bus, path, c->interface, &u, u);
+ if (r <= 0)
+ return r;
+ }
+
+ if (userdata)
+ *userdata = u;
+
+ return 1;
+}
+
+static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
+ assert(p);
+
+ return (uint8_t*) u + p->property.offset;
+}
+
+static int vtable_property_get_userdata(
+ sd_bus *bus,
+ const char *path,
+ struct vtable_member *p,
+ void **userdata) {
+
+ void *u;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(p);
+ assert(userdata);
+
+ r = node_vtable_get_userdata(bus, path, p->parent, &u);
+ if (r <= 0)
+ return r;
+
+ *userdata = vtable_property_convert_userdata(p->vtable, u);
+ return 1;
+}
+
+static int add_enumerated_to_set(sd_bus *bus, const char *prefix, struct node_enumerator *first, Set *s) {
+ struct node_enumerator *c;
+ int r;
+
+ assert(bus);
+ assert(prefix);
+ assert(s);
+
+ LIST_FOREACH(enumerators, c, first) {
+ char **children = NULL, **k;
+
+ r = c->callback(bus, prefix, &children, c->userdata);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(k, children) {
+ if (r < 0) {
+ free(*k);
+ continue;
+ }
+
+ if (!object_path_is_valid(*k) && object_path_startswith(*k, prefix)) {
+ free(*k);
+ r = -EINVAL;
+ continue;
+ }
+
+ r = set_consume(s, *k);
+ }
+
+ free(children);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int add_subtree_to_set(sd_bus *bus, const char *prefix, struct node *n, Set *s) {
+ struct node *i;
+ int r;
+
+ assert(bus);
+ assert(prefix);
+ assert(n);
+ assert(s);
+
+ r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(siblings, i, n->child) {
+ char *t;
+
+ t = strdup(i->path);
+ if (!t)
+ return -ENOMEM;
+
+ r = set_consume(s, t);
+ if (r < 0 && r != -EEXIST)
+ return r;
+
+ r = add_subtree_to_set(bus, prefix, i, s);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int get_child_nodes(sd_bus *bus, const char *prefix, struct node *n, Set **_s) {
+ Set *s = NULL;
+ int r;
+
+ assert(bus);
+ assert(n);
+ assert(_s);
+
+ s = set_new(string_hash_func, string_compare_func);
+ if (!s)
+ return -ENOMEM;
+
+ r = add_subtree_to_set(bus, prefix, n, s);
+ 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) {
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ *found_object = true;
+
+ if (c->last_iteration == bus->iteration_counter)
+ continue;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = c->callback(bus, m, c->userdata);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int method_callbacks_run(
+ sd_bus *bus,
+ sd_bus_message *m,
+ struct vtable_member *c,
+ bool require_fallback,
+ bool *found_object) {
+
+ 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;
+
+ r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
+ if (r <= 0)
+ return r;
+
+ *found_object = true;
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_get_signature(m, true, &signature);
+ if (r < 0)
+ return r;
+
+ if (!streq(strempty(c->vtable->method.signature), signature)) {
+ r = sd_bus_reply_method_errorf(bus, m,
+ "org.freedesktop.DBus.Error.InvalidArgs",
+ "Invalid arguments '%s' to call %s:%s, expecting '%s'.",
+ signature, c->interface, c->member, strempty(c->vtable->method.signature));
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ if (c->vtable->method.handler)
+ return c->vtable->method.handler(bus, m, u);
+
+ /* If the method callback is NULL, make this a successful NOP */
+ r = sd_bus_reply_method_return(bus, m, NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int invoke_property_get(
+ sd_bus *bus,
+ const sd_bus_vtable *v,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ int r;
+ void *p;
+
+ assert(bus);
+ assert(v);
+
+ if (v->property.get)
+ return v->property.get(bus, path, interface, property, m, error, userdata);
+
+ /* Automatic handling if no callback is defined. */
+
+ assert(bus_type_is_basic(v->property.signature[0]));
+
+ switch (v->property.signature[0]) {
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE:
+ p = *(char**) userdata;
+ break;
+
+ default:
+ p = userdata;
+ break;
+ }
+
+ r = sd_bus_message_append_basic(m, v->property.signature[0], p);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int invoke_property_set(
+ sd_bus *bus,
+ const sd_bus_vtable *v,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *value,
+ sd_bus_error *error,
+ void *userdata) {
+
+ int r;
+
+ assert(bus);
+ assert(v);
+
+ if (v->property.set)
+ return v->property.set(bus, path, interface, property, value, error, userdata);
+
+ /* Automatic handling if no callback is defined. */
+
+ assert(signature_is_single(v->property.signature, false));
+ assert(bus_type_is_basic(v->property.signature[0]));
+
+ switch (v->property.signature[0]) {
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE: {
+ const char *p;
+ char *n;
+
+ r = sd_bus_message_read_basic(value, v->property.signature[0], &p);
+ if (r < 0)
+ return r;
+
+ n = strdup(p);
+ if (!n)
+ return -ENOMEM;
+
+ free(*(char**) userdata);
+ *(char**) userdata = n;
+
+ break;
+ }
+
+ default:
+ r = sd_bus_message_read_basic(value, v->property.signature[0], userdata);
+ 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) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ void *u;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(found_object);
+
+ if (require_fallback && !c->parent->is_fallback)
+ return 0;
+
+ r = vtable_property_get_userdata(bus, m->path, c, &u);
+ if (r <= 0)
+ return r;
+
+ *found_object = true;
+
+ r = sd_bus_message_new_method_return(bus, m, &reply);
+ if (r < 0)
+ return r;
+
+ c->last_iteration = bus->iteration_counter;
+
+ if (is_get) {
+ r = sd_bus_message_open_container(reply, 'v', c->vtable->property.signature);
+ if (r < 0)
+ return r;
+
+ r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
+ if (r < 0)
+ return r;
+
+ if (sd_bus_error_is_set(&error)) {
+ r = sd_bus_reply_method_error(bus, m, &error);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ } else {
+ if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
+ sd_bus_error_setf(&error, "org.freedesktop.DBus.Error.PropertyReadOnly", "Property '%s' is not writable.", c->member);
+ else {
+ r = sd_bus_message_enter_container(m, 'v', c->vtable->property.signature);
+ if (r < 0)
+ return r;
+
+ r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
+ if (r < 0)
+ return r;
+ }
+
+ if (sd_bus_error_is_set(&error)) {
+ r = sd_bus_reply_method_error(bus, m, &error);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+
+ 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;
+}
+
+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);
+ assert(c);
+
+ for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
+ if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
+ continue;
+
+ r = sd_bus_message_open_container(reply, 'e', "sv");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", c->interface);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'v', v->property.signature);
+ if (r < 0)
+ return r;
+
+ r = invoke_property_get(bus, v, path, c->interface, v->property.member, reply, error, vtable_property_convert_userdata(v, userdata));
+ if (r < 0)
+ return r;
+
+ if (sd_bus_error_is_set(error))
+ return 0;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
+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) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ struct node_vtable *c;
+ bool found_interface = false;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(found_object);
+
+ r = sd_bus_message_new_method_return(bus, m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "{sv}");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(vtables, c, first) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ void *u;
+
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ r = node_vtable_get_userdata(bus, m->path, c, &u);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ *found_object = true;
+
+ if (iface && !streq(c->interface, iface))
+ continue;
+ found_interface = true;
+
+ c->last_iteration = bus->iteration_counter;
+
+ r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
+ if (r < 0)
+ return r;
+
+ if (sd_bus_error_is_set(&error)) {
+ r = sd_bus_reply_method_error(bus, m, &error);
+ if (r < 0)
+ return r;
+
+ return 1;
+ }
+ }
+
+ if (!found_interface) {
+ r = sd_bus_reply_method_errorf(
+ bus, m,
+ "org.freedesktop.DBus.Error.UnknownInterface",
+ "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;
+}
+
+static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
+ assert(bus);
+
+ if (n->object_manager)
+ return true;
+
+ if (n->parent)
+ return bus_node_with_object_manager(bus, n->parent);
+
+ return false;
+}
+
+static bool bus_node_exists(sd_bus *bus, struct node *n, const char *path, bool require_fallback) {
+ struct node_vtable *c;
+ struct node_callback *k;
+
+ assert(bus);
+ assert(n);
+
+ /* Tests if there's anything attached directly to this node
+ * for the specified path */
+
+ LIST_FOREACH(callbacks, k, n->callbacks) {
+ if (require_fallback && !k->is_fallback)
+ continue;
+
+ return true;
+ }
+
+ LIST_FOREACH(vtables, c, n->vtables) {
+
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
+ return true;
+ }
+
+ return !require_fallback && (n->enumerators || n->object_manager);
+}
+
+static int process_introspect(
+ sd_bus *bus,
+ sd_bus_message *m,
+ struct node *n,
+ bool require_fallback,
+ bool *found_object) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_set_free_free_ Set *s = NULL;
+ struct introspect intro;
+ struct node_vtable *c;
+ bool empty;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(n);
+ assert(found_object);
+
+ r = get_child_nodes(bus, m->path, n, &s);
+ if (r < 0)
+ return r;
+
+ r = introspect_begin(&intro);
+ if (r < 0)
+ return r;
+
+ r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
+ if (r < 0)
+ return r;
+
+ empty = set_isempty(s);
+
+ LIST_FOREACH(vtables, c, n->vtables) {
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ r = node_vtable_get_userdata(bus, m->path, c, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ empty = false;
+
+ r = introspect_write_interface(&intro, c->interface, c->vtable);
+ if (r < 0)
+ goto finish;
+ }
+
+ 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);
+ if (r < 0)
+ return r;
+
+ if (r == 0)
+ goto finish;
+ }
+
+ *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_vtable(
+ sd_bus *bus,
+ sd_bus_message *reply,
+ const char *path,
+ struct node_vtable *c,
+ sd_bus_error *error) {
+
+ void *u;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(path);
+ assert(c);
+ assert(error);
+
+ r = node_vtable_get_userdata(bus, path, c, &u);
+ if (r <= 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", c->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, c, u, error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int object_manager_serialize_path(
+ sd_bus *bus,
+ sd_bus_message *reply,
+ const char *prefix,
+ const char *path,
+ bool require_fallback,
+ sd_bus_error *error) {
+
+ 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;
+
+ 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;
+
+ LIST_FOREACH(vtables, i, n->vtables) {
+
+ if (require_fallback && !i->is_fallback)
+ continue;
+
+ r = object_manager_serialize_vtable(bus, reply, path, i, error);
+ if (r < 0)
+ return r;
+ if (sd_bus_error_is_set(error))
+ return 0;
+ }
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(reply);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int object_manager_serialize_path_and_fallbacks(
+ sd_bus *bus,
+ sd_bus_message *reply,
+ const char *path,
+ sd_bus_error *error) {
+
+ size_t pl;
+ 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;
+ if (sd_bus_error_is_set(error))
+ return 0;
+
+ /* Second, add fallback vtables registered for any of the prefixes */
+ pl = strlen(path);
+ if (pl > 1) {
+ char p[pl + 1];
+ strcpy(p, path);
+
+ for (;;) {
+ char *e;
+
+ e = strrchr(p, '/');
+ if (e == p || !e)
+ break;
+
+ *e = 0;
+
+ r = object_manager_serialize_path(bus, reply, p, path, true, error);
+ if (r < 0)
+ return r;
+
+ if (sd_bus_error_is_set(error))
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int process_get_managed_objects(
+ sd_bus *bus,
+ sd_bus_message *m,
+ struct node *n,
+ bool require_fallback,
+ bool *found_object) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+ _cleanup_set_free_free_ Set *s = NULL;
+ bool empty;
+ int r;
+
+ assert(bus);
+ assert(m);
+ assert(n);
+ assert(found_object);
+
+ if (!bus_node_with_object_manager(bus, n))
+ return 0;
+
+ r = get_child_nodes(bus, m->path, n, &s);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_new_method_return(bus, m, &reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
+ if (r < 0)
+ return r;
+
+ empty = set_isempty(s);
+ if (empty) {
+ struct node_vtable *c;
+
+ /* Hmm, so we have no children? Then let's check
+ * whether we exist at all, i.e. whether at least one
+ * vtable exists. */
+
+ LIST_FOREACH(vtables, c, n->vtables) {
+
+ if (require_fallback && !c->is_fallback)
+ continue;
+
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ empty = false;
+ break;
+ }
+
+ if (empty)
+ return 0;
+ } else {
+ Iterator i;
+ char *path;
+
+ SET_FOREACH(path, s, i) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+ r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
+ if (r < 0)
+ return -ENOMEM;
+
+ if (sd_bus_error_is_set(&error)) {
+ r = sd_bus_reply_method_error(bus, m, &error);
+ 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;
+}
+
+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;
+
+ 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;
+ }
+
+ /* 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)
+ return r;
+
+ 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)
+ return r;
+
+ 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")) {
+
+ 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")) {
+
+ r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
+ if (r != 0)
+ return r;
+ }
+
+ if (!*found_object) {
+ r = bus_node_exists(bus, n, m->path, require_fallback);
+ if (r < 0)
+ return r;
+
+ 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);
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return 0;
+
+ if (!m->path)
+ return 0;
+
+ if (hashmap_isempty(bus->nodes))
+ return 0;
+
+ pl = strlen(m->path);
+ do {
+ char p[pl+1];
+
+ 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 */
+ strcpy(p, m->path);
+ for (;;) {
+ char *e;
+
+ if (streq(p, "/"))
+ break;
+
+ if (bus->nodes_modified)
+ break;
+
+ e = strrchr(p, '/');
+ assert(e);
+ if (e == p)
+ *(e+1) = 0;
+ else
+ *e = 0;
+
+ r = object_find_and_run(bus, m, p, true, &found_object);
+ 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(
+ bus, m,
+ "org.freedesktop.DBus.Error.UnknownProperty",
+ "Unknown property or interface.");
+ else
+ r = sd_bus_reply_method_errorf(
+ bus, m,
+ "org.freedesktop.DBus.Error.UnknownMethod",
+ "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;
+ char *s, *p;
+ int r;
+
+ assert(bus);
+ assert(path);
+ assert(path[0] == '/');
+
+ n = hashmap_get(bus->nodes, path);
+ if (n)
+ return n;
+
+ r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
+ if (r < 0)
+ return NULL;
+
+ s = strdup(path);
+ if (!s)
+ return NULL;
+
+ if (streq(path, "/"))
+ parent = NULL;
+ else {
+ e = strrchr(path, '/');
+ assert(e);
+
+ p = strndupa(path, MAX(1, path - e));
+
+ parent = bus_node_allocate(bus, p);
+ if (!parent) {
+ free(s);
+ return NULL;
+ }
+ }
+
+ n = new0(struct node, 1);
+ if (!n)
+ return NULL;
+
+ n->parent = parent;
+ n->path = s;
+
+ r = hashmap_put(bus->nodes, s, n);
+ if (r < 0) {
+ free(s);
+ free(n);
+ return NULL;
+ }
+
+ if (parent)
+ LIST_PREPEND(struct node, siblings, parent->child, n);
+
+ return n;
+}
+
+static void bus_node_gc(sd_bus *b, struct node *n) {
+ assert(b);
+
+ if (!n)
+ return;
+
+ if (n->child ||
+ n->callbacks ||
+ n->vtables ||
+ n->enumerators ||
+ n->object_manager)
+ return;
+
+ assert(hashmap_remove(b->nodes, n->path) == n);
+
+ if (n->parent)
+ LIST_REMOVE(struct node, siblings, n->parent->child, n);
+
+ free(n->path);
+ bus_node_gc(b, n->parent);
+ free(n);
+}
+
+static int bus_add_object(
+ sd_bus *b,
+ bool fallback,
+ const char *path,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct node_callback *c;
+ struct node *n;
+ int r;
+
+ if (!b)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (!callback)
+ return -EINVAL;
+ if (bus_pid_changed(b))
+ return -ECHILD;
+
+ n = bus_node_allocate(b, path);
+ if (!n)
+ return -ENOMEM;
+
+ c = new0(struct node_callback, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->node = n;
+ c->callback = callback;
+ c->userdata = userdata;
+ c->is_fallback = fallback;
+
+ LIST_PREPEND(struct node_callback, callbacks, n->callbacks, c);
+ return 0;
+
+fail:
+ free(c);
+ bus_node_gc(b, n);
+ return r;
+}
+
+static int bus_remove_object(
+ sd_bus *bus,
+ bool fallback,
+ const char *path,
+ sd_bus_message_handler_t callback,
+ void *userdata) {
+
+ struct node_callback *c;
+ struct node *n;
+
+ if (!bus)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (!callback)
+ return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ n = hashmap_get(bus->nodes, path);
+ if (!n)
+ return 0;
+
+ LIST_FOREACH(callbacks, c, n->callbacks)
+ if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
+ break;
+ if (!c)
+ return 0;
+
+ LIST_REMOVE(struct node_callback, callbacks, n->callbacks, c);
+ free(c);
+
+ bus_node_gc(bus, n);
+
+ return 1;
+}
+
+int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
+ return bus_add_object(bus, false, path, callback, userdata);
+}
+
+int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
+ return bus_remove_object(bus, false, path, callback, userdata);
+}
+
+int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
+ return bus_add_object(bus, true, prefix, callback, userdata);
+}
+
+int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
+ return bus_remove_object(bus, true, prefix, callback, userdata);
+}
+
+static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
+ assert(bus);
+
+ if (!w)
+ return;
+
+ if (w->interface && w->node && w->vtable) {
+ const sd_bus_vtable *v;
+
+ for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
+ struct vtable_member *x = NULL;
+
+ switch (v->type) {
+
+ case _SD_BUS_VTABLE_METHOD: {
+ struct vtable_member key;
+
+ key.path = w->node->path;
+ key.interface = w->interface;
+ key.member = v->method.member;
+
+ x = hashmap_remove(bus->vtable_methods, &key);
+ break;
+ }
+
+ case _SD_BUS_VTABLE_PROPERTY:
+ case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
+ struct vtable_member key;
+
+ key.path = w->node->path;
+ key.interface = w->interface;
+ key.member = v->property.member;
+ x = hashmap_remove(bus->vtable_properties, &key);
+ break;
+ }}
+
+ free(x);
+ }
+ }
+
+ free(w->interface);
+ free(w);
+}
+
+static unsigned vtable_member_hash_func(const void *a) {
+ const struct vtable_member *m = a;
+
+ return
+ string_hash_func(m->path) ^
+ string_hash_func(m->interface) ^
+ string_hash_func(m->member);
+}
+
+static int vtable_member_compare_func(const void *a, const void *b) {
+ const struct vtable_member *x = a, *y = b;
+ int r;
+
+ 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);
+}
+
+static int add_object_vtable_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ bool fallback,
+ sd_bus_object_find_t find,
+ void *userdata) {
+
+ struct node_vtable *c = NULL, *i;
+ const sd_bus_vtable *v;
+ struct node *n;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (!interface_name_is_valid(interface))
+ return -EINVAL;
+ if (!vtable || vtable[0].type != _SD_BUS_VTABLE_START || vtable[0].start.element_size != sizeof(struct sd_bus_vtable))
+ return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
+ if (r < 0)
+ return r;
+
+ n = bus_node_allocate(bus, path);
+ if (!n)
+ return -ENOMEM;
+
+ LIST_FOREACH(vtables, i, n->vtables) {
+ if (streq(i->interface, interface)) {
+ r = -EEXIST;
+ goto fail;
+ }
+
+ if (i->is_fallback != fallback) {
+ r = -EPROTOTYPE;
+ goto fail;
+ }
+ }
+
+ c = new0(struct node_vtable, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->node = n;
+ c->is_fallback = fallback;
+ c->vtable = vtable;
+ c->userdata = userdata;
+ c->find = find;
+
+ c->interface = strdup(interface);
+ if (!c->interface) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
+
+ switch (v->type) {
+
+ case _SD_BUS_VTABLE_METHOD: {
+ struct vtable_member *m;
+
+ if (!member_name_is_valid(v->method.member) ||
+ !signature_is_valid(strempty(v->method.signature), false) ||
+ !signature_is_valid(strempty(v->method.result), false) ||
+ !(v->method.handler || (isempty(v->method.signature) && isempty(v->method.result))) ||
+ v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ m = new0(struct vtable_member, 1);
+ if (!m) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ m->parent = c;
+ m->path = n->path;
+ m->interface = c->interface;
+ m->member = v->method.member;
+ 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:
+
+ if (!(v->property.set || bus_type_is_basic(v->property.signature[0]))) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ /* Fall through */
+
+ case _SD_BUS_VTABLE_PROPERTY: {
+ struct vtable_member *m;
+
+ if (!member_name_is_valid(v->property.member) ||
+ !signature_is_single(v->property.signature, false) ||
+ !(v->property.get || bus_type_is_basic(v->property.signature[0])) ||
+ v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
+ (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+
+ m = new0(struct vtable_member, 1);
+ if (!m) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ m->parent = c;
+ m->path = n->path;
+ m->interface = c->interface;
+ m->member = v->property.member;
+ m->vtable = v;
+
+ r = hashmap_put(bus->vtable_properties, m, m);
+ if (r < 0) {
+ free(m);
+ goto fail;
+ }
+
+ break;
+ }
+
+ case _SD_BUS_VTABLE_SIGNAL:
+
+ if (!member_name_is_valid(v->signal.member) ||
+ !signature_is_single(strempty(v->signal.signature), false)) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ break;
+
+ default:
+ r = -EINVAL;
+ goto fail;
+ }
+ }
+
+ LIST_PREPEND(struct node_vtable, vtables, n->vtables, c);
+ return 0;
+
+fail:
+ if (c)
+ free_node_vtable(bus, c);
+
+ bus_node_gc(bus, n);
+ return r;
+}
+
+static int remove_object_vtable_internal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ bool fallback) {
+
+ struct node_vtable *c;
+ struct node *n;
+
+ if (!bus)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (!interface_name_is_valid(interface))
+ return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ n = hashmap_get(bus->nodes, path);
+ if (!n)
+ return 0;
+
+ LIST_FOREACH(vtables, c, n->vtables)
+ if (streq(c->interface, interface) && c->is_fallback == fallback)
+ break;
+
+ if (!c)
+ return 0;
+
+ LIST_REMOVE(struct node_vtable, vtables, n->vtables, c);
+
+ free_node_vtable(bus, c);
+ return 1;
+}
+
+int sd_bus_add_object_vtable(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ void *userdata) {
+
+ return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
+}
+
+int sd_bus_remove_object_vtable(
+ sd_bus *bus,
+ const char *path,
+ const char *interface) {
+
+ return remove_object_vtable_internal(bus, path, interface, false);
+}
+
+int sd_bus_add_fallback_vtable(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const sd_bus_vtable *vtable,
+ sd_bus_object_find_t find,
+ void *userdata) {
+
+ return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
+}
+
+int sd_bus_remove_fallback_vtable(
+ sd_bus *bus,
+ const char *path,
+ const char *interface) {
+
+ return remove_object_vtable_internal(bus, path, interface, true);
+}
+
+int sd_bus_add_node_enumerator(
+ sd_bus *bus,
+ const char *path,
+ sd_bus_node_enumerator_t callback,
+ void *userdata) {
+
+ struct node_enumerator *c;
+ struct node *n;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (!callback)
+ return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ n = bus_node_allocate(bus, path);
+ if (!n)
+ return -ENOMEM;
+
+ c = new0(struct node_enumerator, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->node = n;
+ c->callback = callback;
+ c->userdata = userdata;
+
+ LIST_PREPEND(struct node_enumerator, enumerators, n->enumerators, c);
+ return 0;
+
+fail:
+ free(c);
+ bus_node_gc(bus, n);
+ return r;
+}
+
+int sd_bus_remove_node_enumerator(
+ sd_bus *bus,
+ const char *path,
+ sd_bus_node_enumerator_t callback,
+ void *userdata) {
+
+ struct node_enumerator *c;
+ struct node *n;
+
+ if (!bus)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (!callback)
+ return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ n = hashmap_get(bus->nodes, path);
+ if (!n)
+ return 0;
+
+ LIST_FOREACH(enumerators, c, n->enumerators)
+ if (c->callback == callback && c->userdata == userdata)
+ break;
+
+ if (!c)
+ return 0;
+
+ LIST_REMOVE(struct node_enumerator, enumerators, n->enumerators, c);
+ free(c);
+
+ bus_node_gc(bus, n);
+
+ return 1;
+}
+
+static int emit_properties_changed_on_interface(
+ sd_bus *bus,
+ const char *prefix,
+ const char *path,
+ const char *interface,
+ bool require_fallback,
+ char **names) {
+
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ bool has_invalidating = false;
+ struct vtable_member key;
+ struct node_vtable *c;
+ struct node *n;
+ char **property;
+ void *u = NULL;
+ int r;
+
+ assert(bus);
+ 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;
+
+ if (streq(c->interface, interface))
+ break;
+ }
+
+ if (!c)
+ return 0;
+
+ r = node_vtable_get_userdata(bus, path, c, &u);
+ if (r <= 0)
+ return r;
+
+ r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
+ 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;
+
+ STRV_FOREACH(property, names) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ struct vtable_member *v;
+
+ key.member = *property;
+ v = hashmap_get(bus->vtable_properties, &key);
+ if (!v)
+ return -ENOENT;
+
+ assert(c == v->parent);
+
+ if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
+ return -EDOM;
+ if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
+ has_invalidating = true;
+ continue;
+ }
+
+ r = sd_bus_message_open_container(m, 'e', "sv");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "s", *property);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(m, 'v', v->vtable->property.signature);
+ if (r < 0)
+ return r;
+
+ r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
+ if (r < 0)
+ return r;
+
+ if (sd_bus_error_is_set(&error))
+ return bus_error_to_errno(&error);
+
+ 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_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) {
+ STRV_FOREACH(property, names) {
+ struct vtable_member *v;
+
+ key.member = *property;
+ assert_se(v = hashmap_get(bus->vtable_properties, &key));
+ assert(c == v->parent);
+
+ if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
+ continue;
+
+ r = sd_bus_message_append(m, "s", *property);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ 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;
+}
+
+int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) {
+ size_t pl;
+ int r;
+
+ if (!bus)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (!interface_name_is_valid(interface))
+ return -EINVAL;
+
+ r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
+ if (r != 0)
+ return r;
+
+ pl = strlen(path);
+ if (pl > 1 ) {
+ char p[pl+1];
+
+ strcpy(p, path);
+ for (;;) {
+ char *e;
+
+ if (streq(p, "/"))
+ break;
+
+ e = strrchr(p, '/');
+ assert(e);
+ if (e == p)
+ *(e+1) = 0;
+ else
+ *e = 0;
+
+ r = emit_properties_changed_on_interface(bus, p, path, interface, true, names);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ return -ENOENT;
+}
+
+int sd_bus_emit_properties_changed(sd_bus *bus, const char *path, const char *interface, const char *name, ...) {
+ _cleanup_strv_free_ char **names = NULL;
+ va_list ap;
+
+ va_start(ap, name);
+ names = strv_new_ap(name, ap);
+ va_end(ap);
+
+ if (!names)
+ return -ENOMEM;
+
+ return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
+}
+
+int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interfaces, ...) {
+ return -ENOSYS;
+}
+
+int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interfaces, ...) {
+ return -ENOSYS;
+}
+
+int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
+ struct node *n;
+
+ if (!bus)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ n = bus_node_allocate(bus, path);
+ if (!n)
+ return -ENOMEM;
+
+ n->object_manager = true;
+ return 0;
+}
+
+int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
+ struct node *n;
+
+ if (!bus)
+ return -EINVAL;
+ if (!object_path_is_valid(path))
+ return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
+
+ n = hashmap_get(bus->nodes, path);
+ if (!n)
+ return 0;
+
+ if (!n->object_manager)
+ return 0;
+
+ n->object_manager = false;
+ bus_node_gc(bus, n);
+ return 1;
+}
diff --git a/src/libsystemd-bus/bus-objects.h b/src/libsystemd-bus/bus-objects.h
new file mode 100644
index 0000000..420edd9
--- /dev/null
+++ b/src/libsystemd-bus/bus-objects.h
@@ -0,0 +1,26 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ 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/>.
+***/
+
+#include "bus-internal.h"
+
+int bus_process_object(sd_bus *bus, sd_bus_message *m);
diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
index cebb073..ca687c0 100644
--- a/src/libsystemd-bus/sd-bus.c
+++ b/src/libsystemd-bus/sd-bus.c
@@ -44,6 +44,7 @@
#include "bus-control.h"
#include "bus-introspect.h"
#include "bus-signature.h"
+#include "bus-objects.h"
static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
@@ -1875,2522 +1876,353 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) {
return 1;
}
-static int node_vtable_get_userdata(
- sd_bus *bus,
- const char *path,
- struct node_vtable *c,
- void **userdata) {
-
- void *u;
+static int process_message(sd_bus *bus, sd_bus_message *m) {
int r;
assert(bus);
- assert(path);
- assert(c);
-
- u = c->userdata;
- if (c->find) {
- r = c->find(bus, path, c->interface, &u, u);
- if (r <= 0)
- return r;
- }
-
- if (userdata)
- *userdata = u;
-
- return 1;
-}
+ assert(m);
-static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
- assert(p);
+ bus->iteration_counter++;
- return (uint8_t*) u + p->property.offset;
-}
+ r = process_hello(bus, m);
+ if (r != 0)
+ return r;
-static int vtable_property_get_userdata(
- sd_bus *bus,
- const char *path,
- struct vtable_member *p,
- void **userdata) {
+ r = process_reply(bus, m);
+ if (r != 0)
+ return r;
- void *u;
- int r;
+ r = process_filter(bus, m);
+ if (r != 0)
+ return r;
- assert(bus);
- assert(path);
- assert(p);
- assert(userdata);
+ r = process_match(bus, m);
+ if (r != 0)
+ return r;
- r = node_vtable_get_userdata(bus, path, p->parent, &u);
- if (r <= 0)
+ r = process_builtin(bus, m);
+ if (r != 0)
return r;
- *userdata = vtable_property_convert_userdata(p->vtable, u);
- return 1;
+ return bus_process_object(bus, m);
}
-static int add_enumerated_to_set(sd_bus *bus, const char *prefix, struct node_enumerator *first, Set *s) {
- struct node_enumerator *c;
+static int process_running(sd_bus *bus, sd_bus_message **ret) {
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
int r;
assert(bus);
- assert(prefix);
- assert(s);
-
- LIST_FOREACH(enumerators, c, first) {
- char **children = NULL, **k;
+ assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
- r = c->callback(bus, prefix, &children, c->userdata);
- if (r < 0)
- return r;
+ r = process_timeout(bus);
+ if (r != 0)
+ goto null_message;
- STRV_FOREACH(k, children) {
- if (r < 0) {
- free(*k);
- continue;
- }
+ r = dispatch_wqueue(bus);
+ if (r != 0)
+ goto null_message;
- if (!object_path_is_valid(*k) && object_path_startswith(*k, prefix)) {
- free(*k);
- r = -EINVAL;
- continue;
- }
+ r = dispatch_rqueue(bus, &m);
+ if (r < 0)
+ return r;
+ if (!m)
+ goto null_message;
- r = set_consume(s, *k);
- }
+ r = process_message(bus, m);
+ if (r != 0)
+ goto null_message;
- free(children);
+ if (ret) {
+ r = sd_bus_message_rewind(m, true);
if (r < 0)
return r;
- }
-
- return 0;
-}
-
-static int add_subtree_to_set(sd_bus *bus, const char *prefix, struct node *n, Set *s) {
- struct node *i;
- int r;
-
- assert(bus);
- assert(prefix);
- assert(n);
- assert(s);
-
- r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
- if (r < 0)
- return r;
- LIST_FOREACH(siblings, i, n->child) {
- char *t;
-
- t = strdup(i->path);
- if (!t)
- return -ENOMEM;
+ *ret = m;
+ m = NULL;
+ return 1;
+ }
- r = set_consume(s, t);
- if (r < 0 && r != -EEXIST)
- return r;
+ if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL) {
- r = add_subtree_to_set(bus, prefix, i, s);
+ r = sd_bus_reply_method_errorf(
+ bus, m,
+ "org.freedesktop.DBus.Error.UnknownObject",
+ "Unknown object '%s'.", m->path);
if (r < 0)
return r;
}
- return 0;
-}
-
-static int get_child_nodes(sd_bus *bus, const char *prefix, struct node *n, Set **_s) {
- Set *s = NULL;
- int r;
-
- assert(bus);
- assert(n);
- assert(_s);
-
- s = set_new(string_hash_func, string_compare_func);
- if (!s)
- return -ENOMEM;
+ return 1;
- r = add_subtree_to_set(bus, prefix, n, s);
- if (r < 0) {
- set_free_free(s);
- return r;
- }
+null_message:
+ if (r >= 0 && ret)
+ *ret = NULL;
- *_s = s;
- return 0;
+ return r;
}
-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 sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
int r;
- assert(bus);
- assert(m);
- assert(found_object);
+ /* Returns 0 when we didn't do anything. This should cause the
+ * caller to invoke sd_bus_wait() before returning the next
+ * time. Returns > 0 when we did something, which possibly
+ * means *ret is filled in with an unprocessed message. */
- LIST_FOREACH(callbacks, c, first) {
- if (require_fallback && !c->is_fallback)
- continue;
+ if (!bus)
+ return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
- *found_object = true;
+ /* We don't allow recursively invoking sd_bus_process(). */
+ if (bus->processing)
+ return -EBUSY;
- if (c->last_iteration == bus->iteration_counter)
- continue;
+ switch (bus->state) {
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
+ case BUS_UNSET:
+ case BUS_CLOSED:
+ return -ENOTCONN;
- r = c->callback(bus, m, c->userdata);
- if (r != 0)
+ case BUS_OPENING:
+ r = bus_socket_process_opening(bus);
+ if (r < 0)
return r;
- }
-
- return 0;
-}
-
-static int method_callbacks_run(
- sd_bus *bus,
- sd_bus_message *m,
- struct vtable_member *c,
- bool require_fallback,
- bool *found_object) {
-
- 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;
-
- r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
- if (r <= 0)
- return r;
-
- *found_object = true;
-
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
+ if (ret)
+ *ret = NULL;
return r;
- r = sd_bus_message_get_signature(m, true, &signature);
- if (r < 0)
- return r;
+ case BUS_AUTHENTICATING:
- if (!streq(strempty(c->vtable->method.signature), signature)) {
- r = sd_bus_reply_method_errorf(bus, m,
- "org.freedesktop.DBus.Error.InvalidArgs",
- "Invalid arguments '%s' to call %s:%s, expecting '%s'.",
- signature, c->interface, c->member, strempty(c->vtable->method.signature));
+ r = bus_socket_process_authenticating(bus);
if (r < 0)
return r;
+ if (ret)
+ *ret = NULL;
+ return r;
- return 1;
- }
+ case BUS_RUNNING:
+ case BUS_HELLO:
- if (c->vtable->method.handler)
- return c->vtable->method.handler(bus, m, u);
+ bus->processing = true;
+ r = process_running(bus, ret);
+ bus->processing = false;
- /* If the method callback is NULL, make this a successful NOP */
- r = sd_bus_reply_method_return(bus, m, NULL);
- if (r < 0)
return r;
+ }
- return 1;
+ assert_not_reached("Unknown state");
}
-static int invoke_property_get(
- sd_bus *bus,
- const sd_bus_vtable *v,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *m,
- sd_bus_error *error,
- void *userdata) {
-
- int r;
- void *p;
+static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
+ struct pollfd p[2] = {};
+ int r, e, n;
+ struct timespec ts;
+ usec_t until, m;
assert(bus);
- assert(v);
- if (v->property.get)
- return v->property.get(bus, path, interface, property, m, error, userdata);
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
- /* Automatic handling if no callback is defined. */
+ e = sd_bus_get_events(bus);
+ if (e < 0)
+ return e;
- assert(bus_type_is_basic(v->property.signature[0]));
+ if (need_more)
+ e |= POLLIN;
- switch (v->property.signature[0]) {
+ r = sd_bus_get_timeout(bus, &until);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ m = (uint64_t) -1;
+ else {
+ usec_t nw;
+ nw = now(CLOCK_MONOTONIC);
+ m = until > nw ? until - nw : 0;
+ }
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_SIGNATURE:
- p = *(char**) userdata;
- break;
+ if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
+ m = timeout_usec;
- default:
- p = userdata;
- break;
+ p[0].fd = bus->input_fd;
+ if (bus->output_fd == bus->input_fd) {
+ p[0].events = e;
+ n = 1;
+ } else {
+ p[0].events = e & POLLIN;
+ p[1].fd = bus->output_fd;
+ p[1].events = e & POLLOUT;
+ n = 2;
}
- r = sd_bus_message_append_basic(m, v->property.signature[0], p);
+ r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
if (r < 0)
- return r;
+ return -errno;
- return 1;
+ return r > 0 ? 1 : 0;
}
-static int invoke_property_set(
- sd_bus *bus,
- const sd_bus_vtable *v,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *value,
- sd_bus_error *error,
- void *userdata) {
+int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
- int r;
+ if (!bus)
+ return -EINVAL;
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
- assert(bus);
- assert(v);
+ if (bus->rqueue_size > 0)
+ return 0;
- if (v->property.set)
- return v->property.set(bus, path, interface, property, value, error, userdata);
+ return bus_poll(bus, false, timeout_usec);
+}
- /* Automatic handling if no callback is defined. */
+int sd_bus_flush(sd_bus *bus) {
+ int r;
- assert(signature_is_single(v->property.signature, false));
- assert(bus_type_is_basic(v->property.signature[0]));
+ if (!bus)
+ return -EINVAL;
+ if (!BUS_IS_OPEN(bus->state))
+ return -ENOTCONN;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
- switch (v->property.signature[0]) {
+ r = bus_ensure_running(bus);
+ if (r < 0)
+ return r;
- case SD_BUS_TYPE_STRING:
- case SD_BUS_TYPE_OBJECT_PATH:
- case SD_BUS_TYPE_SIGNATURE: {
- const char *p;
- char *n;
+ if (bus->wqueue_size <= 0)
+ return 0;
- r = sd_bus_message_read_basic(value, v->property.signature[0], &p);
+ for (;;) {
+ r = dispatch_wqueue(bus);
if (r < 0)
return r;
- n = strdup(p);
- if (!n)
- return -ENOMEM;
-
- free(*(char**) userdata);
- *(char**) userdata = n;
-
- break;
- }
+ if (bus->wqueue_size <= 0)
+ return 0;
- default:
- r = sd_bus_message_read_basic(value, v->property.signature[0], userdata);
+ r = bus_poll(bus, false, (uint64_t) -1);
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) {
-
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- void *u;
- int r;
-
- assert(bus);
- assert(m);
- assert(found_object);
-
- if (require_fallback && !c->parent->is_fallback)
- return 0;
-
- r = vtable_property_get_userdata(bus, m->path, c, &u);
- if (r <= 0)
- return r;
-
- *found_object = true;
-
- r = sd_bus_message_new_method_return(bus, m, &reply);
- if (r < 0)
- return r;
-
- c->last_iteration = bus->iteration_counter;
-
- if (is_get) {
- r = sd_bus_message_open_container(reply, 'v', c->vtable->property.signature);
- if (r < 0)
- return r;
-
- r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
- if (r < 0)
- return r;
-
- if (sd_bus_error_is_set(&error)) {
- r = sd_bus_reply_method_error(bus, m, &error);
- if (r < 0)
- return r;
-
- return 1;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- } else {
- if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
- sd_bus_error_setf(&error, "org.freedesktop.DBus.Error.PropertyReadOnly", "Property '%s' is not writable.", c->member);
- else {
- r = sd_bus_message_enter_container(m, 'v', c->vtable->property.signature);
- if (r < 0)
- return r;
-
- r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
- if (r < 0)
- return r;
- }
-
- if (sd_bus_error_is_set(&error)) {
- r = sd_bus_reply_method_error(bus, m, &error);
- if (r < 0)
- return r;
-
- return 1;
- }
-
- 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;
-}
-
-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);
- assert(c);
-
- for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
- if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
- continue;
-
- r = sd_bus_message_open_container(reply, 'e', "sv");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(reply, "s", c->interface);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'v', v->property.signature);
- if (r < 0)
- return r;
-
- r = invoke_property_get(bus, v, path, c->interface, v->property.member, reply, error, vtable_property_convert_userdata(v, userdata));
- if (r < 0)
- return r;
-
- if (sd_bus_error_is_set(error))
- return 0;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
- }
-
- return 1;
-}
-
-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) {
-
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- struct node_vtable *c;
- bool found_interface = false;
- int r;
-
- assert(bus);
- assert(m);
- assert(found_object);
-
- r = sd_bus_message_new_method_return(bus, m, &reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'a', "{sv}");
- if (r < 0)
- return r;
-
- LIST_FOREACH(vtables, c, first) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- void *u;
-
- if (require_fallback && !c->is_fallback)
- continue;
-
- r = node_vtable_get_userdata(bus, m->path, c, &u);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- *found_object = true;
-
- if (iface && !streq(c->interface, iface))
- continue;
- found_interface = true;
-
- c->last_iteration = bus->iteration_counter;
-
- r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
- if (r < 0)
- return r;
-
- if (sd_bus_error_is_set(&error)) {
- r = sd_bus_reply_method_error(bus, m, &error);
- if (r < 0)
- return r;
-
- return 1;
- }
- }
-
- if (!found_interface) {
- r = sd_bus_reply_method_errorf(
- bus, m,
- "org.freedesktop.DBus.Error.UnknownInterface",
- "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;
-}
-
-static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
- assert(bus);
-
- if (n->object_manager)
- return true;
-
- if (n->parent)
- return bus_node_with_object_manager(bus, n->parent);
-
- return false;
-}
-
-static bool bus_node_exists(sd_bus *bus, struct node *n, const char *path, bool require_fallback) {
- struct node_vtable *c;
- struct node_callback *k;
-
- assert(bus);
- assert(n);
-
- /* Tests if there's anything attached directly to this node
- * for the specified path */
-
- LIST_FOREACH(callbacks, k, n->callbacks) {
- if (require_fallback && !k->is_fallback)
- continue;
-
- return true;
- }
-
- LIST_FOREACH(vtables, c, n->vtables) {
-
- if (require_fallback && !c->is_fallback)
- continue;
-
- if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
- return true;
- }
-
- return !require_fallback && (n->enumerators || n->object_manager);
-}
-
-static int process_introspect(
- sd_bus *bus,
- sd_bus_message *m,
- struct node *n,
- bool require_fallback,
- bool *found_object) {
-
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_set_free_free_ Set *s = NULL;
- struct introspect intro;
- struct node_vtable *c;
- bool empty;
- int r;
-
- assert(bus);
- assert(m);
- assert(n);
- assert(found_object);
-
- r = get_child_nodes(bus, m->path, n, &s);
- if (r < 0)
- return r;
-
- r = introspect_begin(&intro);
- if (r < 0)
- return r;
-
- r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
- if (r < 0)
- return r;
-
- empty = set_isempty(s);
-
- LIST_FOREACH(vtables, c, n->vtables) {
- if (require_fallback && !c->is_fallback)
- continue;
-
- r = node_vtable_get_userdata(bus, m->path, c, NULL);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- empty = false;
-
- r = introspect_write_interface(&intro, c->interface, c->vtable);
- if (r < 0)
- goto finish;
- }
-
- 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);
- if (r < 0)
- return r;
-
- if (r == 0)
- goto finish;
- }
-
- *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_vtable(
- sd_bus *bus,
- sd_bus_message *reply,
- const char *path,
- struct node_vtable *c,
- sd_bus_error *error) {
-
- void *u;
- int r;
-
- assert(bus);
- assert(reply);
- assert(path);
- assert(c);
- assert(error);
-
- r = node_vtable_get_userdata(bus, path, c, &u);
- if (r <= 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(reply, "s", c->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, c, u, error);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- return 0;
-}
-
-static int object_manager_serialize_path(
- sd_bus *bus,
- sd_bus_message *reply,
- const char *prefix,
- const char *path,
- bool require_fallback,
- sd_bus_error *error) {
-
- 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;
-
- 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;
-
- LIST_FOREACH(vtables, i, n->vtables) {
-
- if (require_fallback && !i->is_fallback)
- continue;
-
- r = object_manager_serialize_vtable(bus, reply, path, i, error);
- if (r < 0)
- return r;
- if (sd_bus_error_is_set(error))
- return 0;
- }
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(reply);
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int object_manager_serialize_path_and_fallbacks(
- sd_bus *bus,
- sd_bus_message *reply,
- const char *path,
- sd_bus_error *error) {
-
- size_t pl;
- 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;
- if (sd_bus_error_is_set(error))
- return 0;
-
- /* Second, add fallback vtables registered for any of the prefixes */
- pl = strlen(path);
- if (pl > 1) {
- char p[pl + 1];
- strcpy(p, path);
-
- for (;;) {
- char *e;
-
- e = strrchr(p, '/');
- if (e == p || !e)
- break;
-
- *e = 0;
-
- r = object_manager_serialize_path(bus, reply, p, path, true, error);
- if (r < 0)
- return r;
-
- if (sd_bus_error_is_set(error))
- return 0;
- }
- }
-
- return 0;
-}
-
-static int process_get_managed_objects(
- sd_bus *bus,
- sd_bus_message *m,
- struct node *n,
- bool require_fallback,
- bool *found_object) {
-
- _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_set_free_free_ Set *s = NULL;
- bool empty;
- int r;
-
- assert(bus);
- assert(m);
- assert(n);
- assert(found_object);
-
- if (!bus_node_with_object_manager(bus, n))
- return 0;
-
- r = get_child_nodes(bus, m->path, n, &s);
- if (r < 0)
- return r;
-
- r = sd_bus_message_new_method_return(bus, m, &reply);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
- if (r < 0)
- return r;
-
- empty = set_isempty(s);
- if (empty) {
- struct node_vtable *c;
-
- /* Hmm, so we have no children? Then let's check
- * whether we exist at all, i.e. whether at least one
- * vtable exists. */
-
- LIST_FOREACH(vtables, c, n->vtables) {
-
- if (require_fallback && !c->is_fallback)
- continue;
-
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- empty = false;
- break;
- }
-
- if (empty)
- return 0;
- } else {
- Iterator i;
- char *path;
-
- SET_FOREACH(path, s, i) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-
- r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
- if (r < 0)
- return -ENOMEM;
-
- if (sd_bus_error_is_set(&error)) {
- r = sd_bus_reply_method_error(bus, m, &error);
- 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;
-}
-
-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;
-
- 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;
- }
-
- /* 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)
- return r;
-
- 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)
- return r;
-
- 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")) {
-
- 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")) {
-
- r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
- if (r != 0)
- return r;
- }
-
- if (!*found_object) {
- r = bus_node_exists(bus, n, m->path, require_fallback);
- if (r < 0)
- return r;
-
- if (r > 0)
- *found_object = true;
- }
-
- return 0;
-}
-
-static int process_object(sd_bus *bus, sd_bus_message *m) {
- int r;
- size_t pl;
- bool found_object = false;
-
- assert(bus);
- assert(m);
-
- if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
- return 0;
-
- if (!m->path)
- return 0;
-
- if (hashmap_isempty(bus->nodes))
- return 0;
-
- pl = strlen(m->path);
- do {
- char p[pl+1];
-
- 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 */
- strcpy(p, m->path);
- for (;;) {
- char *e;
-
- if (streq(p, "/"))
- break;
-
- if (bus->nodes_modified)
- break;
-
- e = strrchr(p, '/');
- assert(e);
- if (e == p)
- *(e+1) = 0;
- else
- *e = 0;
-
- r = object_find_and_run(bus, m, p, true, &found_object);
- 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(
- bus, m,
- "org.freedesktop.DBus.Error.UnknownProperty",
- "Unknown property or interface.");
- else
- r = sd_bus_reply_method_errorf(
- bus, m,
- "org.freedesktop.DBus.Error.UnknownMethod",
- "Unknown method '%s' or interface '%s'.", m->member, m->interface);
-
- if (r < 0)
- return r;
-
- return 1;
-}
-
-static int process_message(sd_bus *bus, sd_bus_message *m) {
- int r;
-
- assert(bus);
- assert(m);
-
- bus->iteration_counter++;
-
- r = process_hello(bus, m);
- if (r != 0)
- return r;
-
- r = process_reply(bus, m);
- if (r != 0)
- return r;
-
- r = process_filter(bus, m);
- if (r != 0)
- return r;
-
- r = process_match(bus, m);
- if (r != 0)
- return r;
-
- r = process_builtin(bus, m);
- if (r != 0)
- return r;
-
- return process_object(bus, m);
-}
-
-static int process_running(sd_bus *bus, sd_bus_message **ret) {
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- assert(bus);
- assert(bus->state == BUS_RUNNING || bus->state == BUS_HELLO);
-
- r = process_timeout(bus);
- if (r != 0)
- goto null_message;
-
- r = dispatch_wqueue(bus);
- if (r != 0)
- goto null_message;
-
- r = dispatch_rqueue(bus, &m);
- if (r < 0)
- return r;
- if (!m)
- goto null_message;
-
- r = process_message(bus, m);
- if (r != 0)
- goto null_message;
-
- if (ret) {
- r = sd_bus_message_rewind(m, true);
- if (r < 0)
- return r;
-
- *ret = m;
- m = NULL;
- return 1;
- }
-
- if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL) {
-
- r = sd_bus_reply_method_errorf(
- bus, m,
- "org.freedesktop.DBus.Error.UnknownObject",
- "Unknown object '%s'.", m->path);
- if (r < 0)
- return r;
- }
-
- return 1;
-
-null_message:
- if (r >= 0 && ret)
- *ret = NULL;
-
- return r;
-}
-
-int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
- int r;
-
- /* Returns 0 when we didn't do anything. This should cause the
- * caller to invoke sd_bus_wait() before returning the next
- * time. Returns > 0 when we did something, which possibly
- * means *ret is filled in with an unprocessed message. */
-
- if (!bus)
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- /* We don't allow recursively invoking sd_bus_process(). */
- if (bus->processing)
- return -EBUSY;
-
- switch (bus->state) {
-
- case BUS_UNSET:
- case BUS_CLOSED:
- return -ENOTCONN;
-
- case BUS_OPENING:
- r = bus_socket_process_opening(bus);
- if (r < 0)
- return r;
- if (ret)
- *ret = NULL;
- return r;
-
- case BUS_AUTHENTICATING:
-
- r = bus_socket_process_authenticating(bus);
- if (r < 0)
- return r;
- if (ret)
- *ret = NULL;
- return r;
-
- case BUS_RUNNING:
- case BUS_HELLO:
-
- bus->processing = true;
- r = process_running(bus, ret);
- bus->processing = false;
-
- return r;
- }
-
- assert_not_reached("Unknown state");
-}
-
-static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
- struct pollfd p[2] = {};
- int r, e, n;
- struct timespec ts;
- usec_t until, m;
-
- assert(bus);
-
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
-
- e = sd_bus_get_events(bus);
- if (e < 0)
- return e;
-
- if (need_more)
- e |= POLLIN;
-
- r = sd_bus_get_timeout(bus, &until);
- if (r < 0)
- return r;
- if (r == 0)
- m = (uint64_t) -1;
- else {
- usec_t nw;
- nw = now(CLOCK_MONOTONIC);
- m = until > nw ? until - nw : 0;
- }
-
- if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
- m = timeout_usec;
-
- p[0].fd = bus->input_fd;
- if (bus->output_fd == bus->input_fd) {
- p[0].events = e;
- n = 1;
- } else {
- p[0].events = e & POLLIN;
- p[1].fd = bus->output_fd;
- p[1].events = e & POLLOUT;
- n = 2;
- }
-
- r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
- if (r < 0)
- return -errno;
-
- return r > 0 ? 1 : 0;
-}
-
-int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
-
- if (!bus)
- return -EINVAL;
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- if (bus->rqueue_size > 0)
- return 0;
-
- return bus_poll(bus, false, timeout_usec);
-}
-
-int sd_bus_flush(sd_bus *bus) {
- int r;
-
- if (!bus)
- return -EINVAL;
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- r = bus_ensure_running(bus);
- if (r < 0)
- return r;
-
- if (bus->wqueue_size <= 0)
- return 0;
-
- for (;;) {
- r = dispatch_wqueue(bus);
- if (r < 0)
- return r;
-
- if (bus->wqueue_size <= 0)
- return 0;
-
- r = bus_poll(bus, false, (uint64_t) -1);
- if (r < 0)
- return r;
- }
-}
-
-int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
- struct filter_callback *f;
-
- if (!bus)
- return -EINVAL;
- if (!callback)
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- f = new0(struct filter_callback, 1);
- if (!f)
- return -ENOMEM;
- f->callback = callback;
- f->userdata = userdata;
-
- bus->filter_callbacks_modified = true;
- LIST_PREPEND(struct filter_callback, callbacks, bus->filter_callbacks, f);
- return 0;
-}
-
-int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
- struct filter_callback *f;
-
- if (!bus)
- return -EINVAL;
- if (!callback)
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- LIST_FOREACH(callbacks, f, bus->filter_callbacks) {
- if (f->callback == callback && f->userdata == userdata) {
- bus->filter_callbacks_modified = true;
- LIST_REMOVE(struct filter_callback, callbacks, bus->filter_callbacks, f);
- free(f);
- return 1;
- }
- }
-
- return 0;
-}
-
-static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
- struct node *n, *parent;
- const char *e;
- char *s, *p;
- int r;
-
- assert(bus);
- assert(path);
- assert(path[0] == '/');
-
- n = hashmap_get(bus->nodes, path);
- if (n)
- return n;
-
- r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
- if (r < 0)
- return NULL;
-
- s = strdup(path);
- if (!s)
- return NULL;
-
- if (streq(path, "/"))
- parent = NULL;
- else {
- e = strrchr(path, '/');
- assert(e);
-
- p = strndupa(path, MAX(1, path - e));
-
- parent = bus_node_allocate(bus, p);
- if (!parent) {
- free(s);
- return NULL;
- }
- }
-
- n = new0(struct node, 1);
- if (!n)
- return NULL;
-
- n->parent = parent;
- n->path = s;
-
- r = hashmap_put(bus->nodes, s, n);
- if (r < 0) {
- free(s);
- free(n);
- return NULL;
- }
-
- if (parent)
- LIST_PREPEND(struct node, siblings, parent->child, n);
-
- return n;
-}
-
-static void bus_node_gc(sd_bus *b, struct node *n) {
- assert(b);
-
- if (!n)
- return;
-
- if (n->child ||
- n->callbacks ||
- n->vtables ||
- n->enumerators ||
- n->object_manager)
- return;
-
- assert(hashmap_remove(b->nodes, n->path) == n);
-
- if (n->parent)
- LIST_REMOVE(struct node, siblings, n->parent->child, n);
-
- free(n->path);
- bus_node_gc(b, n->parent);
- free(n);
-}
-
-static int bus_add_object(
- sd_bus *b,
- bool fallback,
- const char *path,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct node_callback *c;
- struct node *n;
- int r;
-
- if (!b)
- return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
- if (!callback)
- return -EINVAL;
- if (bus_pid_changed(b))
- return -ECHILD;
-
- n = bus_node_allocate(b, path);
- if (!n)
- return -ENOMEM;
-
- c = new0(struct node_callback, 1);
- if (!c) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->node = n;
- c->callback = callback;
- c->userdata = userdata;
- c->is_fallback = fallback;
-
- LIST_PREPEND(struct node_callback, callbacks, n->callbacks, c);
- return 0;
-
-fail:
- free(c);
- bus_node_gc(b, n);
- return r;
-}
-
-static int bus_remove_object(
- sd_bus *bus,
- bool fallback,
- const char *path,
- sd_bus_message_handler_t callback,
- void *userdata) {
-
- struct node_callback *c;
- struct node *n;
-
- if (!bus)
- return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
- if (!callback)
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- n = hashmap_get(bus->nodes, path);
- if (!n)
- return 0;
-
- LIST_FOREACH(callbacks, c, n->callbacks)
- if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
- break;
- if (!c)
- return 0;
-
- LIST_REMOVE(struct node_callback, callbacks, n->callbacks, c);
- free(c);
-
- bus_node_gc(bus, n);
-
- return 1;
-}
-
-int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
- return bus_add_object(bus, false, path, callback, userdata);
-}
-
-int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
- return bus_remove_object(bus, false, path, callback, userdata);
-}
-
-int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
- return bus_add_object(bus, true, prefix, callback, userdata);
-}
-
-int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
- return bus_remove_object(bus, true, prefix, callback, userdata);
-}
-
-int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) {
- struct bus_match_component *components = NULL;
- unsigned n_components = 0;
- uint64_t cookie = 0;
- int r = 0;
-
- if (!bus)
- return -EINVAL;
- if (!match)
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- r = bus_match_parse(match, &components, &n_components);
- if (r < 0)
- goto finish;
-
- if (bus->bus_client) {
- cookie = ++bus->match_cookie;
-
- r = bus_add_match_internal(bus, match, components, n_components, cookie);
- if (r < 0)
- goto finish;
- }
-
- bus->match_callbacks_modified = true;
- r = bus_match_add(&bus->match_callbacks, components, n_components, callback, userdata, cookie, NULL);
- if (r < 0) {
- if (bus->bus_client)
- bus_remove_match_internal(bus, match, cookie);
- }
-
-finish:
- bus_match_parse_free(components, n_components);
- return r;
-}
-
-int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) {
- struct bus_match_component *components = NULL;
- unsigned n_components = 0;
- int r = 0, q = 0;
- uint64_t cookie = 0;
-
- if (!bus)
- return -EINVAL;
- if (!match)
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- r = bus_match_parse(match, &components, &n_components);
- if (r < 0)
- return r;
-
- bus->match_callbacks_modified = true;
- r = bus_match_remove(&bus->match_callbacks, components, n_components, callback, userdata, &cookie);
-
- if (bus->bus_client)
- q = bus_remove_match_internal(bus, match, cookie);
-
- bus_match_parse_free(components, n_components);
-
- return r < 0 ? r : q;
-}
-
-int sd_bus_emit_signal(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const char *member,
- const char *types, ...) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- if (!bus)
- return -EINVAL;
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- r = sd_bus_message_new_signal(bus, path, interface, member, &m);
- if (r < 0)
- return r;
-
- if (!isempty(types)) {
- va_list ap;
-
- va_start(ap, types);
- r = bus_message_append_ap(m, types, ap);
- va_end(ap);
- if (r < 0)
- return r;
- }
-
- return sd_bus_send(bus, m, NULL);
-}
-
-int sd_bus_call_method(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_error *error,
- sd_bus_message **reply,
- const char *types, ...) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- if (!bus)
-
- return -EINVAL;
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- r = sd_bus_message_new_method_call(bus, destination, path, interface, member, &m);
- if (r < 0)
- return r;
-
- if (!isempty(types)) {
- va_list ap;
-
- va_start(ap, types);
- r = bus_message_append_ap(m, types, ap);
- va_end(ap);
- if (r < 0)
- return r;
- }
-
- return sd_bus_send_with_reply_and_block(bus, m, 0, error, reply);
-}
-
-int sd_bus_reply_method_return(
- sd_bus *bus,
- sd_bus_message *call,
- const char *types, ...) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- if (!bus)
- return -EINVAL;
- if (!call)
- return -EINVAL;
- if (!call->sealed)
- return -EPERM;
- if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
- return -EINVAL;
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
- return 0;
-
- r = sd_bus_message_new_method_return(bus, call, &m);
- if (r < 0)
- return r;
-
- if (!isempty(types)) {
- va_list ap;
-
- va_start(ap, types);
- r = bus_message_append_ap(m, types, ap);
- va_end(ap);
- if (r < 0)
- return r;
- }
-
- return sd_bus_send(bus, m, NULL);
-}
-
-int sd_bus_reply_method_error(
- sd_bus *bus,
- sd_bus_message *call,
- const sd_bus_error *e) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- int r;
-
- if (!bus)
- return -EINVAL;
- if (!call)
- return -EINVAL;
- if (!call->sealed)
- return -EPERM;
- if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
- return -EINVAL;
- if (!sd_bus_error_is_set(e))
- return -EINVAL;
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- if (call->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED)
- return 0;
-
- r = sd_bus_message_new_method_error(bus, call, e, &m);
- if (r < 0)
- return r;
-
- return sd_bus_send(bus, m, NULL);
-}
-
-int sd_bus_reply_method_errorf(
- sd_bus *bus,
- sd_bus_message *call,
- const char *name,
- const char *format,
- ...) {
-
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- char *n, *m;
- va_list ap;
- int r;
-
- n = strdup(name);
- if (!n)
- return -ENOMEM;
-
- if (format) {
- va_start(ap, format);
- r = vasprintf(&m, format, ap);
- va_end(ap);
-
- if (r < 0) {
- free(n);
- return -ENOMEM;
- }
- }
-
- error.name = n;
- error.message = m;
- error.need_free = true;
-
- return sd_bus_reply_method_error(bus, call, &error);
-}
-
-bool bus_pid_changed(sd_bus *bus) {
- assert(bus);
-
- /* We don't support people creating a bus connection and
- * keeping it around over a fork(). Let's complain. */
-
- return bus->original_pid != getpid();
-}
-
-static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
- assert(bus);
-
- if (!w)
- return;
-
- if (w->interface && w->node && w->vtable) {
- const sd_bus_vtable *v;
-
- for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
- struct vtable_member *x = NULL;
-
- switch (v->type) {
-
- case _SD_BUS_VTABLE_METHOD: {
- struct vtable_member key;
-
- key.path = w->node->path;
- key.interface = w->interface;
- key.member = v->method.member;
-
- x = hashmap_remove(bus->vtable_methods, &key);
- break;
- }
-
- case _SD_BUS_VTABLE_PROPERTY:
- case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
- struct vtable_member key;
-
- key.path = w->node->path;
- key.interface = w->interface;
- key.member = v->property.member;
- x = hashmap_remove(bus->vtable_properties, &key);
- break;
- }}
-
- free(x);
- }
- }
-
- free(w->interface);
- free(w);
-}
-
-static unsigned vtable_member_hash_func(const void *a) {
- const struct vtable_member *m = a;
-
- return
- string_hash_func(m->path) ^
- string_hash_func(m->interface) ^
- string_hash_func(m->member);
-}
-
-static int vtable_member_compare_func(const void *a, const void *b) {
- const struct vtable_member *x = a, *y = b;
- int r;
-
- 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);
-}
-
-static int add_object_vtable_internal(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const sd_bus_vtable *vtable,
- bool fallback,
- sd_bus_object_find_t find,
- void *userdata) {
-
- struct node_vtable *c = NULL, *i;
- const sd_bus_vtable *v;
- struct node *n;
- int r;
-
- if (!bus)
- return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
- if (!interface_name_is_valid(interface))
- return -EINVAL;
- if (!vtable || vtable[0].type != _SD_BUS_VTABLE_START || vtable[0].start.element_size != sizeof(struct sd_bus_vtable))
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
- if (r < 0)
- return r;
-
- n = bus_node_allocate(bus, path);
- if (!n)
- return -ENOMEM;
-
- LIST_FOREACH(vtables, i, n->vtables) {
- if (streq(i->interface, interface)) {
- r = -EEXIST;
- goto fail;
- }
-
- if (i->is_fallback != fallback) {
- r = -EPROTOTYPE;
- goto fail;
- }
- }
-
- c = new0(struct node_vtable, 1);
- if (!c) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->node = n;
- c->is_fallback = fallback;
- c->vtable = vtable;
- c->userdata = userdata;
- c->find = find;
-
- c->interface = strdup(interface);
- if (!c->interface) {
- r = -ENOMEM;
- goto fail;
- }
-
- for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
-
- switch (v->type) {
-
- case _SD_BUS_VTABLE_METHOD: {
- struct vtable_member *m;
-
- if (!member_name_is_valid(v->method.member) ||
- !signature_is_valid(strempty(v->method.signature), false) ||
- !signature_is_valid(strempty(v->method.result), false) ||
- !(v->method.handler || (isempty(v->method.signature) && isempty(v->method.result))) ||
- v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
- r = -EINVAL;
- goto fail;
- }
-
- m = new0(struct vtable_member, 1);
- if (!m) {
- r = -ENOMEM;
- goto fail;
- }
-
- m->parent = c;
- m->path = n->path;
- m->interface = c->interface;
- m->member = v->method.member;
- 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:
-
- if (!(v->property.set || bus_type_is_basic(v->property.signature[0]))) {
- r = -EINVAL;
- goto fail;
- }
-
- /* Fall through */
-
- case _SD_BUS_VTABLE_PROPERTY: {
- struct vtable_member *m;
-
- if (!member_name_is_valid(v->property.member) ||
- !signature_is_single(v->property.signature, false) ||
- !(v->property.get || bus_type_is_basic(v->property.signature[0])) ||
- v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
- (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
- r = -EINVAL;
- goto fail;
- }
-
-
- m = new0(struct vtable_member, 1);
- if (!m) {
- r = -ENOMEM;
- goto fail;
- }
-
- m->parent = c;
- m->path = n->path;
- m->interface = c->interface;
- m->member = v->property.member;
- m->vtable = v;
-
- r = hashmap_put(bus->vtable_properties, m, m);
- if (r < 0) {
- free(m);
- goto fail;
- }
-
- break;
- }
-
- case _SD_BUS_VTABLE_SIGNAL:
-
- if (!member_name_is_valid(v->signal.member) ||
- !signature_is_single(strempty(v->signal.signature), false)) {
- r = -EINVAL;
- goto fail;
- }
-
- break;
-
- default:
- r = -EINVAL;
- goto fail;
- }
- }
-
- LIST_PREPEND(struct node_vtable, vtables, n->vtables, c);
- return 0;
-
-fail:
- if (c)
- free_node_vtable(bus, c);
-
- bus_node_gc(bus, n);
- return r;
-}
-
-static int remove_object_vtable_internal(
- sd_bus *bus,
- const char *path,
- const char *interface,
- bool fallback) {
-
- struct node_vtable *c;
- struct node *n;
-
- if (!bus)
- return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
- if (!interface_name_is_valid(interface))
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- n = hashmap_get(bus->nodes, path);
- if (!n)
- return 0;
-
- LIST_FOREACH(vtables, c, n->vtables)
- if (streq(c->interface, interface) && c->is_fallback == fallback)
- break;
-
- if (!c)
- return 0;
-
- LIST_REMOVE(struct node_vtable, vtables, n->vtables, c);
-
- free_node_vtable(bus, c);
- return 1;
-}
-
-int sd_bus_add_object_vtable(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const sd_bus_vtable *vtable,
- void *userdata) {
-
- return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
-}
-
-int sd_bus_remove_object_vtable(
- sd_bus *bus,
- const char *path,
- const char *interface) {
-
- return remove_object_vtable_internal(bus, path, interface, false);
-}
-
-int sd_bus_add_fallback_vtable(
- sd_bus *bus,
- const char *path,
- const char *interface,
- const sd_bus_vtable *vtable,
- sd_bus_object_find_t find,
- void *userdata) {
-
- return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
-}
-
-int sd_bus_remove_fallback_vtable(
- sd_bus *bus,
- const char *path,
- const char *interface) {
-
- return remove_object_vtable_internal(bus, path, interface, true);
-}
-
-int sd_bus_add_node_enumerator(
- sd_bus *bus,
- const char *path,
- sd_bus_node_enumerator_t callback,
- void *userdata) {
-
- struct node_enumerator *c;
- struct node *n;
- int r;
+int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
+ struct filter_callback *f;
if (!bus)
return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
if (!callback)
return -EINVAL;
if (bus_pid_changed(bus))
return -ECHILD;
- n = bus_node_allocate(bus, path);
- if (!n)
+ f = new0(struct filter_callback, 1);
+ if (!f)
return -ENOMEM;
+ f->callback = callback;
+ f->userdata = userdata;
- c = new0(struct node_enumerator, 1);
- if (!c) {
- r = -ENOMEM;
- goto fail;
- }
-
- c->node = n;
- c->callback = callback;
- c->userdata = userdata;
-
- LIST_PREPEND(struct node_enumerator, enumerators, n->enumerators, c);
+ bus->filter_callbacks_modified = true;
+ LIST_PREPEND(struct filter_callback, callbacks, bus->filter_callbacks, f);
return 0;
-
-fail:
- free(c);
- bus_node_gc(bus, n);
- return r;
}
-int sd_bus_remove_node_enumerator(
- sd_bus *bus,
- const char *path,
- sd_bus_node_enumerator_t callback,
- void *userdata) {
-
- struct node_enumerator *c;
- struct node *n;
+int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata) {
+ struct filter_callback *f;
if (!bus)
return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
if (!callback)
return -EINVAL;
if (bus_pid_changed(bus))
return -ECHILD;
- n = hashmap_get(bus->nodes, path);
- if (!n)
- return 0;
-
- LIST_FOREACH(enumerators, c, n->enumerators)
- if (c->callback == callback && c->userdata == userdata)
- break;
-
- if (!c)
- return 0;
-
- LIST_REMOVE(struct node_enumerator, enumerators, n->enumerators, c);
- free(c);
-
- bus_node_gc(bus, n);
-
- return 1;
-}
-
-static int emit_properties_changed_on_interface(
- sd_bus *bus,
- const char *prefix,
- const char *path,
- const char *interface,
- bool require_fallback,
- char **names) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- bool has_invalidating = false;
- struct vtable_member key;
- struct node_vtable *c;
- struct node *n;
- char **property;
- void *u = NULL;
- int r;
-
- assert(bus);
- 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;
-
- if (streq(c->interface, interface))
- break;
- }
-
- if (!c)
- return 0;
-
- r = node_vtable_get_userdata(bus, path, c, &u);
- if (r <= 0)
- return r;
-
- r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
- 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;
-
- STRV_FOREACH(property, names) {
- _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
- struct vtable_member *v;
-
- key.member = *property;
- v = hashmap_get(bus->vtable_properties, &key);
- if (!v)
- return -ENOENT;
-
- assert(c == v->parent);
-
- if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
- return -EDOM;
- if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
- has_invalidating = true;
- continue;
- }
-
- r = sd_bus_message_open_container(m, 'e', "sv");
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "s", *property);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(m, 'v', v->vtable->property.signature);
- if (r < 0)
- return r;
-
- r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
- if (r < 0)
- return r;
-
- if (sd_bus_error_is_set(&error))
- return bus_error_to_errno(&error);
-
- 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_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) {
- STRV_FOREACH(property, names) {
- struct vtable_member *v;
-
- key.member = *property;
- assert_se(v = hashmap_get(bus->vtable_properties, &key));
- assert(c == v->parent);
-
- if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
- continue;
-
- r = sd_bus_message_append(m, "s", *property);
- if (r < 0)
- return r;
+ LIST_FOREACH(callbacks, f, bus->filter_callbacks) {
+ if (f->callback == callback && f->userdata == userdata) {
+ bus->filter_callbacks_modified = true;
+ LIST_REMOVE(struct filter_callback, callbacks, bus->filter_callbacks, f);
+ free(f);
+ return 1;
}
}
- 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;
+ return 0;
}
-int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) {
- size_t pl;
- int r;
+int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) {
+ struct bus_match_component *components = NULL;
+ unsigned n_components = 0;
+ uint64_t cookie = 0;
+ int r = 0;
if (!bus)
return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
- if (!interface_name_is_valid(interface))
+ if (!match)
return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
- r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
- if (r != 0)
- return r;
-
- pl = strlen(path);
- if (pl > 1 ) {
- char p[pl+1];
-
- strcpy(p, path);
- for (;;) {
- char *e;
-
- if (streq(p, "/"))
- break;
+ r = bus_match_parse(match, &components, &n_components);
+ if (r < 0)
+ goto finish;
- e = strrchr(p, '/');
- assert(e);
- if (e == p)
- *(e+1) = 0;
- else
- *e = 0;
+ if (bus->bus_client) {
+ cookie = ++bus->match_cookie;
- r = emit_properties_changed_on_interface(bus, p, path, interface, true, names);
- if (r != 0)
- return r;
- }
+ r = bus_add_match_internal(bus, match, components, n_components, cookie);
+ if (r < 0)
+ goto finish;
}
- return -ENOENT;
-}
-
-int sd_bus_emit_properties_changed(sd_bus *bus, const char *path, const char *interface, const char *name, ...) {
- _cleanup_strv_free_ char **names = NULL;
- va_list ap;
-
- va_start(ap, name);
- names = strv_new_ap(name, ap);
- va_end(ap);
-
- if (!names)
- return -ENOMEM;
-
- return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
-}
-
-int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interfaces, ...) {
- return -ENOSYS;
-}
-
-int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interfaces, ...) {
- return -ENOSYS;
-}
-
-int sd_bus_get_property(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_error *error,
- sd_bus_message **reply,
- const char *type) {
-
- sd_bus_message *rep = NULL;
- int r;
-
- if (interface && !interface_name_is_valid(interface))
- return -EINVAL;
- if (!member_name_is_valid(member))
- return -EINVAL;
- if (!signature_is_single(type, false))
- return -EINVAL;
- if (!reply)
- return -EINVAL;
-
- r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
- if (r < 0)
- return r;
-
- r = sd_bus_message_enter_container(rep, 'v', type);
+ bus->match_callbacks_modified = true;
+ r = bus_match_add(&bus->match_callbacks, components, n_components, callback, userdata, cookie, NULL);
if (r < 0) {
- sd_bus_message_unref(rep);
- return r;
+ if (bus->bus_client)
+ bus_remove_match_internal(bus, match, cookie);
}
- *reply = rep;
- return 0;
+finish:
+ bus_match_parse_free(components, n_components);
+ return r;
}
-int sd_bus_set_property(
- sd_bus *bus,
- const char *destination,
- const char *path,
- const char *interface,
- const char *member,
- sd_bus_error *error,
- const char *type, ...) {
-
- _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
- va_list ap;
- int r;
+int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata) {
+ struct bus_match_component *components = NULL;
+ unsigned n_components = 0;
+ int r = 0, q = 0;
+ uint64_t cookie = 0;
- if (interface && !interface_name_is_valid(interface))
- return -EINVAL;
- if (!member_name_is_valid(member))
+ if (!bus)
return -EINVAL;
- if (!signature_is_single(type, false))
+ if (!match)
return -EINVAL;
+ if (bus_pid_changed(bus))
+ return -ECHILD;
- r = sd_bus_message_new_method_call(bus, destination, path, "org.freedesktop.DBus.Properties", "Set", &m);
- if (r < 0)
- return r;
-
- r = sd_bus_message_append(m, "ss", strempty(interface), member);
- if (r < 0)
- return r;
-
- r = sd_bus_message_open_container(m, 'v', type);
- if (r < 0)
- return r;
-
- va_start(ap, type);
- r = bus_message_append_ap(m, type, ap);
- va_end(ap);
- if (r < 0)
- return r;
-
- r = sd_bus_message_close_container(m);
+ r = bus_match_parse(match, &components, &n_components);
if (r < 0)
return r;
- return sd_bus_send_with_reply_and_block(bus, m, 0, error, NULL);
-}
-
-int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
- struct node *n;
+ bus->match_callbacks_modified = true;
+ r = bus_match_remove(&bus->match_callbacks, components, n_components, callback, userdata, &cookie);
- if (!bus)
- return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
+ if (bus->bus_client)
+ q = bus_remove_match_internal(bus, match, cookie);
- n = bus_node_allocate(bus, path);
- if (!n)
- return -ENOMEM;
+ bus_match_parse_free(components, n_components);
- n->object_manager = true;
- return 0;
+ return r < 0 ? r : q;
}
-int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
- struct node *n;
-
- if (!bus)
- return -EINVAL;
- if (!object_path_is_valid(path))
- return -EINVAL;
- if (bus_pid_changed(bus))
- return -ECHILD;
-
- n = hashmap_get(bus->nodes, path);
- if (!n)
- return 0;
+bool bus_pid_changed(sd_bus *bus) {
+ assert(bus);
- if (!n->object_manager)
- return 0;
+ /* We don't support people creating a bus connection and
+ * keeping it around over a fork(). Let's complain. */
- n->object_manager = false;
- bus_node_gc(bus, n);
- return 1;
+ return bus->original_pid != getpid();
}
More information about the systemd-commits
mailing list