Browse Source

libsystemd-bus: add lightweight object vtable implementation for exposing objects on the bus

This adds a lightweight scheme how to define interfaces in static fixed
arrays which then can be easily registered on a bus connection. This
makes it much easier to write bus services.

This automatically handles implementation of the Properties,
ObjectManager, and Introspection bus interfaces.
keep-around/ba91431154ad7bac82ddf0a540ec1b40db62d782
Lennart Poettering 10 years ago
parent
commit
29ddb38fea
  1. 2
      .gitignore
  2. 28
      Makefile.am
  3. 2
      TODO
  4. 33
      src/libsystemd-bus/bus-error.c
  5. 23
      src/libsystemd-bus/bus-internal.c
  6. 65
      src/libsystemd-bus/bus-internal.h
  7. 200
      src/libsystemd-bus/bus-introspect.c
  8. 41
      src/libsystemd-bus/bus-introspect.h
  9. 81
      src/libsystemd-bus/bus-message.c
  10. 6
      src/libsystemd-bus/bus-signature.c
  11. 2
      src/libsystemd-bus/bus-signature.h
  12. 2697
      src/libsystemd-bus/sd-bus.c
  13. 63
      src/libsystemd-bus/test-bus-introspect.c
  14. 374
      src/libsystemd-bus/test-bus-objects.c
  15. 67
      src/libsystemd-bus/test-bus-signature.c
  16. 39
      src/systemd/sd-bus-protocol.h
  17. 127
      src/systemd/sd-bus-vtable.h
  18. 54
      src/systemd/sd-bus.h

2
.gitignore

@ -87,12 +87,14 @@
/tags
/test-boot-timestamp
/test-bus-chat
/test-bus-introspect
/test-bus-kernel
/test-bus-kernel-bloom
/test-bus-kernel-benchmark
/test-bus-marshal
/test-bus-match
/test-bus-memfd
/test-bus-objects
/test-bus-signature
/test-bus-server
/test-bus-zero-copy

28
Makefile.am

@ -1956,6 +1956,8 @@ libsystemd_bus_la_SOURCES = \
src/libsystemd-bus/bus-match.h \
src/libsystemd-bus/bus-bloom.c \
src/libsystemd-bus/bus-bloom.h \
src/libsystemd-bus/bus-introspect.c \
src/libsystemd-bus/bus-introspect.h \
src/libsystemd-bus/kdbus.h \
src/libsystemd-bus/sd-memfd.c
@ -1981,7 +1983,9 @@ tests += \
test-bus-kernel-bloom \
test-bus-kernel-benchmark \
test-bus-memfd \
test-bus-zero-copy
test-bus-zero-copy \
test-bus-introspect \
test-bus-objects
noinst_PROGRAMS += \
busctl
@ -2031,6 +2035,18 @@ test_bus_server_LDADD = \
libsystemd-bus.la \
libsystemd-id128-internal.la
test_bus_objects_SOURCES = \
src/libsystemd-bus/test-bus-objects.c
test_bus_objects_CFLAGS = \
$(AM_CFLAGS) \
-pthread
test_bus_objects_LDADD = \
libsystemd-shared.la \
libsystemd-bus.la \
libsystemd-id128-internal.la
test_bus_match_SOURCES = \
src/libsystemd-bus/test-bus-match.c
@ -2095,6 +2111,16 @@ test_bus_zero_copy_LDADD = \
libsystemd-shared.la \
libsystemd-bus.la
test_bus_introspect_SOURCES = \
src/libsystemd-bus/test-bus-introspect.c
test_bus_introspect_CFLAGS = \
$(AM_CFLAGS)
test_bus_introspect_LDADD = \
libsystemd-shared.la \
libsystemd-bus.la
busctl_SOURCES = \
src/libsystemd-bus/busctl.c

2
TODO

@ -54,6 +54,8 @@ Features:
* tmpfiles: when applying ownership to /run/log/journal also do this for the journal fails contained in it
* rework list.h to use typeof() and thus simplify most linked list macros by not requring the type to be specified
* we probably should replace the left-over uses of strv_append() and replace them by strv_push() or strv_extend()
* move config_parse_path_strv() out of conf-parser.c

33
src/libsystemd-bus/bus-error.c

@ -51,7 +51,34 @@ void sd_bus_error_free(sd_bus_error *e) {
e->need_free = false;
}
int sd_bus_error_set(sd_bus_error *e, const char *name, const char *format, ...) {
int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
char *n, *m = NULL;
if (!e)
return 0;
if (bus_error_is_dirty(e))
return -EINVAL;
if (!name)
return -EINVAL;
n = strdup(name);
if (!n)
return -ENOMEM;
if (message) {
m = strdup(message);
if (!m)
return -ENOMEM;
}
e->name = n;
e->message = m;
e->need_free = true;
return 0;
}
int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
char *n, *m = NULL;
va_list ap;
int r;
@ -119,9 +146,7 @@ void sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *messa
if (bus_error_is_dirty(e))
return;
e->name = name;
e->message = message;
e->need_free = false;
*e = SD_BUS_ERROR_MAKE(name, message);
}
int sd_bus_error_is_set(const sd_bus_error *e) {

23
src/libsystemd-bus/bus-internal.c

@ -61,6 +61,29 @@ bool object_path_is_valid(const char *p) {
return true;
}
char* object_path_startswith(const char *a, const char *b) {
const char *p;
if (!object_path_is_valid(a) ||
!object_path_is_valid(b))
return NULL;
if (streq(b, "/"))
return (char*) a + 1;
p = startswith(a, b);
if (!p)
return NULL;
if (*p == 0)
return (char*) p;
if (*p == '/')
return (char*) p + 1;
return NULL;
}
bool interface_name_is_valid(const char *p) {
const char *q;
bool dot, found_dot = false;

65
src/libsystemd-bus/bus-internal.h

@ -54,14 +54,63 @@ struct filter_callback {
LIST_FIELDS(struct filter_callback, callbacks);
};
struct object_callback {
struct node {
char *path;
struct node *parent;
LIST_HEAD(struct node, child);
LIST_FIELDS(struct node, siblings);
LIST_HEAD(struct node_callback, callbacks);
LIST_HEAD(struct node_vtable, vtables);
LIST_HEAD(struct node_enumerator, enumerators);
bool object_manager;
};
struct node_callback {
struct node *node;
bool is_fallback;
sd_bus_message_handler_t callback;
void *userdata;
char *path;
unsigned last_iteration;
LIST_FIELDS(struct node_callback, callbacks);
};
struct node_enumerator {
struct node *node;
sd_bus_node_enumerator_t callback;
void *userdata;
unsigned last_iteration;
LIST_FIELDS(struct node_enumerator, enumerators);
};
struct node_vtable {
struct node *node;
char *interface;
bool is_fallback;
const sd_bus_vtable *vtable;
void *userdata;
sd_bus_object_find_t find;
unsigned last_iteration;
LIST_FIELDS(struct node_vtable, vtables);
};
struct vtable_member {
const char *path;
const char *interface;
const char *member;
struct node_vtable *parent;
unsigned last_iteration;
const sd_bus_vtable *vtable;
};
enum bus_state {
@ -109,7 +158,7 @@ struct sd_bus {
bool processing:1;
bool match_callbacks_modified:1;
bool filter_callbacks_modified:1;
bool object_callbacks_modified:1;
bool nodes_modified:1;
int use_memfd;
@ -131,7 +180,12 @@ struct sd_bus {
Prioq *reply_callbacks_prioq;
Hashmap *reply_callbacks;
LIST_HEAD(struct filter_callback, filter_callbacks);
Hashmap *object_callbacks;
Hashmap *nodes;
Hashmap *vtable_methods;
Hashmap *vtable_properties;
union {
struct sockaddr sa;
@ -213,10 +267,11 @@ static inline void bus_unrefp(sd_bus **b) {
#define BUS_EXEC_ARGV_MAX 256
bool object_path_is_valid(const char *p);
bool interface_name_is_valid(const char *p);
bool service_name_is_valid(const char *p);
bool member_name_is_valid(const char *p);
bool object_path_is_valid(const char *p);
char *object_path_startswith(const char *a, const char *b);
bool namespace_complex_pattern(const char *pattern, const char *value);
bool path_complex_pattern(const char *pattern, const char *value);

200
src/libsystemd-bus/bus-introspect.c

@ -0,0 +1,200 @@
/*-*- 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 "util.h"
#include "sd-bus-protocol.h"
#include "bus-introspect.h"
#include "bus-signature.h"
#include "bus-internal.h"
int introspect_begin(struct introspect *i) {
assert(i);
zero(*i);
i->f = open_memstream(&i->introspection, &i->size);
if (!i->f)
return -ENOMEM;
fputs(SD_BUS_INTROSPECT_DOCTYPE
"<node>\n", i->f);
return 0;
}
int introspect_write_default_interfaces(struct introspect *i, bool object_manager) {
assert(i);
fputs(SD_BUS_INTROSPECT_INTERFACE_PEER
SD_BUS_INTROSPECT_INTERFACE_INTROSPECTABLE
SD_BUS_INTROSPECT_INTERFACE_PROPERTIES, i->f);
if (object_manager)
fputs(SD_BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER, i->f);
return 0;
}
int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) {
char *node;
assert(i);
assert(prefix);
while ((node = set_steal_first(s))) {
const char *e;
e = object_path_startswith(node, prefix);
if (e)
fprintf(i->f, " <node name=\"%s\"/>\n", e);
free(node);
}
return 0;
}
static void introspect_write_flags(struct introspect *i, int type, int flags) {
if (flags & SD_BUS_VTABLE_DEPRECATED)
fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
if (type == _SD_BUS_VTABLE_METHOD && flags & SD_BUS_VTABLE_METHOD_NO_REPLY)
fputs(" <annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n", i->f);
if (type == _SD_BUS_VTABLE_PROPERTY || type == _SD_BUS_VTABLE_WRITABLE_PROPERTY) {
if (!(flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"false\"/>\n", i->f);
else if (flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)
fputs(" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"invalidates\"/>\n", i->f);
}
}
static int introspect_write_arguments(struct introspect *i, const char *signature, const char *direction) {
int r;
for (;;) {
size_t l;
if (!*signature)
return 0;
r = signature_element_length(signature, &l);
if (r < 0)
return r;
fprintf(i->f, " <arg type=\"%.*s\"", (int) l, signature);
if (direction)
fprintf(i->f, " direction=\"%s\">\n", direction);
else
fputs(">\n", i->f);
signature += l;
}
}
int introspect_write_interface(struct introspect *i, const char *interface, const sd_bus_vtable *v) {
assert(i);
assert(interface);
assert(v);
fprintf(i->f, " <interface name=\"%s\">\n", interface);
for (; v->type != _SD_BUS_VTABLE_END; v++) {
switch (v->type) {
case _SD_BUS_VTABLE_START:
if (v->flags & SD_BUS_VTABLE_DEPRECATED)
fputs(" <annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n", i->f);
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");
introspect_write_flags(i, v->type, v->flags);
fputs(" </method>\n", i->f);
break;
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->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);
introspect_write_flags(i, v->type, v->flags);
fputs(" </signal>\n", i->f);
break;
}
}
fputs(" </interface>\n", i->f);
return 0;
}
int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply) {
sd_bus_message *q;
int r;
assert(i);
assert(m);
assert(reply);
fputs("</node>\n", i->f);
fflush(i->f);
if (ferror(i->f))
return -ENOMEM;
r = sd_bus_message_new_method_return(bus, m, &q);
if (r < 0)
return r;
r = sd_bus_message_append(q, "s", i->introspection);
if (r < 0) {
sd_bus_message_unref(q);
return r;
}
*reply = q;
return 0;
}
void introspect_free(struct introspect *i) {
assert(i);
if (i->f)
fclose(i->f);
if (i->introspection)
free(i->introspection);
zero(*i);
}

41
src/libsystemd-bus/bus-introspect.h

@ -0,0 +1,41 @@
/*-*- 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 <sys/types.h>
#include <stdio.h>
#include "sd-bus.h"
#include "set.h"
struct introspect {
FILE *f;
char *introspection;
size_t size;
};
int introspect_begin(struct introspect *i);
int introspect_write_default_interfaces(struct introspect *i, bool object_manager);
int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix);
int introspect_write_interface(struct introspect *i, const char *interface, const sd_bus_vtable *v);
int introspect_finish(struct introspect *i, sd_bus *bus, sd_bus_message *m, sd_bus_message **reply);
void introspect_free(struct introspect *i);

81
src/libsystemd-bus/bus-message.c

@ -496,9 +496,13 @@ int sd_bus_message_new_method_call(
sd_bus_message *t;
int r;
if (!path)
if (destination && !service_name_is_valid(destination))
return -EINVAL;
if (!member)
if (!object_path_is_valid(path))
return -EINVAL;
if (interface && !interface_name_is_valid(interface))
return -EINVAL;
if (!member_name_is_valid(member))
return -EINVAL;
if (!m)
return -EINVAL;
@ -627,6 +631,58 @@ fail:
return r;
}
int sd_bus_message_new_method_errorf(
sd_bus *bus,
sd_bus_message *call,
sd_bus_message **m,
const char *name,
const char *format,
...) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *t;
va_list ap;
int r;
if (!name)
return -EINVAL;
if (!m)
return -EINVAL;
r = message_new_reply(bus, call, SD_BUS_MESSAGE_TYPE_METHOD_ERROR, &t);
if (r < 0)
return r;
r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, name, &t->error.name);
if (r < 0)
goto fail;
if (format) {
_cleanup_free_ char *message = NULL;
va_start(ap, format);
r = vasprintf(&message, format, ap);
va_end(ap);
if (r < 0) {
r = -ENOMEM;
goto fail;
}
r = message_append_basic(t, SD_BUS_TYPE_STRING, message, (const void**) &t->error.message);
if (r < 0)
goto fail;
}
*m = t;
return 0;
fail:
message_free(t);
return r;
}
int bus_message_new_synthetic_error(
sd_bus *bus,
uint64_t serial,
@ -1558,7 +1614,7 @@ static int bus_message_open_array(
assert(contents);
assert(array_size);
if (!signature_is_single(contents))
if (!signature_is_single(contents, true))
return -EINVAL;
alignment = bus_type_get_alignment(contents[0]);
@ -1629,7 +1685,7 @@ static int bus_message_open_variant(
assert(c);
assert(contents);
if (!signature_is_single(contents))
if (!signature_is_single(contents, false))
return -EINVAL;
if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
@ -2704,7 +2760,7 @@ static int bus_message_enter_array(
assert(contents);
assert(array_size);
if (!signature_is_single(contents))
if (!signature_is_single(contents, true))
return -EINVAL;
alignment = bus_type_get_alignment(contents[0]);
@ -2758,7 +2814,7 @@ static int bus_message_enter_variant(
assert(c);
assert(contents);
if (!signature_is_single(contents))
if (!signature_is_single(contents, false))
return -EINVAL;
if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
@ -4290,3 +4346,16 @@ int bus_message_to_errno(sd_bus_message *m) {
return bus_error_to_errno(&m->error);
}
int sd_bus_message_get_signature(sd_bus_message *m, int complete, const char **signature) {
struct bus_container *c;
if (!m)
return -EINVAL;
if (!signature)
return -EINVAL;
c = complete ? &m->root_container : message_get_container(m);
*signature = c->signature ?: "";
return 0;
}

6
src/libsystemd-bus/bus-signature.c

@ -110,13 +110,13 @@ int signature_element_length(const char *s, size_t *l) {
return signature_element_length_internal(s, true, 0, 0, l);
}
bool signature_is_single(const char *s) {
bool signature_is_single(const char *s, bool allow_dict_entry) {
int r;
size_t t;
assert(s);
r = signature_element_length(s, &t);
r = signature_element_length_internal(s, allow_dict_entry, 0, 0, &t);
if (r < 0)
return false;
@ -129,7 +129,7 @@ bool signature_is_pair(const char *s) {
if (!bus_type_is_basic(*s))
return false;
return signature_is_single(s + 1);
return signature_is_single(s + 1, false);
}
bool signature_is_valid(const char *s, bool allow_dict_entry) {

2
src/libsystemd-bus/bus-signature.h

@ -24,7 +24,7 @@
#include <stdbool.h>
#include <sys/types.h>
bool signature_is_single(const char *s);
bool signature_is_single(const char *s, bool allow_dict_entry);
bool signature_is_pair(const char *s);
bool signature_is_valid(const char *s, bool allow_dict_entry);

2697
src/libsystemd-bus/sd-bus.c

File diff suppressed because it is too large

63
src/libsystemd-bus/test-bus-introspect.c

@ -0,0 +1,63 @@
/*-*- 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 "util.h"
#include "log.h"
#include "bus-introspect.h"
static int prop_get(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata) {
return -EINVAL;
}
static int prop_set(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata) {
return -EINVAL;
}
static const sd_bus_vtable vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("Hello", "ssas", "a(uu)", 0, NULL),
SD_BUS_METHOD("DeprecatedHello", "", "", SD_BUS_VTABLE_DEPRECATED, NULL),
SD_BUS_METHOD("DeprecatedHelloNoReply", "", "", SD_BUS_VTABLE_DEPRECATED|SD_BUS_VTABLE_METHOD_NO_REPLY, NULL),
SD_BUS_SIGNAL("Wowza", "sss", 0),
SD_BUS_SIGNAL("DeprecatedWowza", "ut", SD_BUS_VTABLE_DEPRECATED),
SD_BUS_WRITABLE_PROPERTY("AProperty", "s", prop_get, prop_set, 0, 0),
SD_BUS_PROPERTY("AReadOnlyDeprecatedProperty", "(ut)", prop_get, 0, SD_BUS_VTABLE_DEPRECATED),
SD_BUS_PROPERTY("ChangingProperty", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Invalidating", "t", prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY),
SD_BUS_VTABLE_END
};
int main(int argc, char *argv[]) {
struct introspect intro;
log_set_max_level(LOG_DEBUG);
assert_se(introspect_begin(&intro) >= 0);
assert_se(introspect_write_interface(&intro, "org.foo", vtable) >= 0);
fflush(intro.f);
fputs(intro.introspection, stdout);
introspect_free(&intro);
return 0;
}

374
src/libsystemd-bus/test-bus-objects.c

@ -0,0 +1,374 @@
/*-*- 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 <assert.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include "log.h"
#include "util.h"
#include "macro.h"
#include "strv.h"
#include "sd-bus.h"
#include "bus-internal.h"
#include "bus-message.h"
/* Test:
*
* sd_bus_add_object_manager()
* sd_bus_emit_properties_changed()
*
* Add in:
*
* automatic properties
* node hierarchy updates during dispatching
* emit_interfaces_added/emit_interfaces_removed
*
*/
struct context {
int fds[2];
bool quit;
char *something;
};
static int something_handler(sd_bus *bus, sd_bus_message *m, void *userdata) {
struct context *c = userdata;
const char *s;
char *n = NULL;
int r;
r = sd_bus_message_read(m, "s", &s);
assert_se(r > 0);
n = strjoin("<<<", s, ">>>", NULL);
assert_se(n);
free(c->something);
c->something = n;
log_info("AlterSomething() called, got %s, returning %s", s, n);
r = sd_bus_reply_method_return(bus, m, "s", n);
assert_se(r >= 0);
return 1;
}
static int exit_handler(sd_bus *bus, sd_bus_message *m, void *userdata) {
struct context *c = userdata;
int r;
c->quit = true;
log_info("Exit called");
r = sd_bus_reply_method_return(bus, m, "");
assert_se(r >= 0);
return 1;
}
static int get_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata) {
struct context *c = userdata;
int r;
log_info("property get for %s called", property);
r = sd_bus_message_append(reply, "s", c->something);
assert_se(r >= 0);
return 1;
}
static int set_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, sd_bus_error *error, void *userdata) {
struct context *c = userdata;
const char *s;
char *n;
int r;
log_info("property set for %s called", property);
r = sd_bus_message_read(value, "s", &s);
assert_se(r >= 0);
n = strdup(s);
assert_se(n);
free(c->something);
c->something = n;
return 1;
}
static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata) {
_cleanup_free_ char *s = NULL;
const char *x;
int r;
assert_se(asprintf(&s, "object %p, path %s", userdata, path) >= 0);
r = sd_bus_message_append(reply, "s", s);
assert_se(r >= 0);
assert_se(x = startswith(path, "/value/"));
assert_se(PTR_TO_UINT(userdata) == 30);
return 1;
}
static const sd_bus_vtable vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("AlterSomething", "s", "s", 0, something_handler),
SD_BUS_METHOD("Exit", "", "", 0, exit_handler),
SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0),
SD_BUS_VTABLE_END
};
static const sd_bus_vtable vtable2[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Value", "s", value_handler, 10, 0),
SD_BUS_VTABLE_END
};
static int enumerator_callback(sd_bus *b, const char *path, char ***nodes, void *userdata) {
if (object_path_startswith("/value", path))
assert_se(*nodes = strv_new("/value/a", "/value/b", "/value/c", NULL));
return 1;
}
static void *server(void *p) {
struct context *c = p;
sd_bus *bus = NULL;
sd_id128_t id;
int r;
c->quit = false;
assert_se(sd_id128_randomize(&id) >= 0);
assert_se(sd_bus_new(&bus) >= 0);
assert_se(sd_bus_set_fd(bus, c->fds[0], c->fds[0]) >= 0);
assert_se(sd_bus_set_server(bus, 1, id) >= 0);
assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test", vtable, c) >= 0);
assert_se(sd_bus_add_object_vtable(bus, "/foo", "org.freedesktop.systemd.test2", vtable, c) >= 0);
assert_se(sd_bus_add_fallback_vtable(bus, "/value", "org.freedesktop.systemd.ValueTest", vtable2, NULL, UINT_TO_PTR(20)) >= 0);
assert_se(sd_bus_add_node_enumerator(bus, "/value", enumerator_callback, NULL) >= 0);
assert_se(sd_bus_start(bus) >= 0);
log_error("Entering event loop on server");
while (!c->quit) {
log_error("Loop!");
r = sd_bus_process(bus, NULL);
if (r < 0) {
log_error("Failed to process requests: %s", strerror(-r));
goto fail;
}
if (r == 0) {
r = sd_bus_wait(bus, (uint64_t) -1);
if (r < 0) {
log_error("Failed to wait: %s", strerror(-r));
goto fail;
}
continue;
}
}
r = 0;
fail:
if (bus) {
sd_bus_flush(bus);
sd_bus_unref(bus);
}
return INT_TO_PTR(r);
}
static int client(struct context *c) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_unref_ sd_bus *bus = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
const char *s;
int r;
assert_se(sd_bus_new(&bus) >= 0);
assert_se(sd_bus_set_fd(bus, c->fds[1], c->fds[1]) >= 0);
assert_se(sd_bus_start(bus) >= 0);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "s", "hallo");
assert_se(r >= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
assert_se(streq(s, "<<<hallo>>>"));
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, "");
assert_se(r < 0);
assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownMethod"));
sd_bus_error_free(&error);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo");
assert_se(r < 0);
assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.InvalidArgs"));
sd_bus_error_free(&error);
r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
assert_se(r >= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
assert_se(streq(s, "<<<hallo>>>"));
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test");
assert_se(r >= 0);
r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, &reply, "s");
assert_se(r >= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
assert_se(streq(s, "test"));
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
assert_se(r <= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
fputs(s, stdout);
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s");
assert_se(r >= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
log_info("read %s", s);
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
assert_se(r <= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
fputs(s, stdout);
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
assert_se(r <= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
fputs(s, stdout);
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
assert_se(r <= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
fputs(s, stdout);
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
assert_se(r <= 0);
bus_message_dump(reply);
sd_bus_message_unref(reply);
reply = NULL;
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2");
assert_se(r < 0);
assert_se(sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.UnknownInterface"));
sd_bus_error_free(&error);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, "");
assert_se(r >= 0);
sd_bus_flush(bus);
return 0;
}
int main(int argc, char *argv[]) {
struct context c;
pthread_t s;
void *p;
int r, q;
zero(c);
assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, c.fds) >= 0);
r = pthread_create(&s, NULL, server, &c);
if (r != 0)
return -r;
r = client(&c);
q = pthread_join(s, &p);
if (q != 0)
return -q;
if (r < 0)
return r;
if (PTR_TO_INT(p) < 0)
return PTR_TO_INT(p);
free(c.something);
return EXIT_SUCCESS;
}

67
src/libsystemd-bus/test-bus-signature.c

@ -28,29 +28,30 @@
int main(int argc, char *argv[]) {
assert_se(signature_is_single("y"));
assert_se(signature_is_single("u"));
assert_se(signature_is_single("v"));
assert_se(signature_is_single("as"));
assert_se(signature_is_single("(ss)"));
assert_se(signature_is_single("()"));
assert_se(signature_is_single("(()()()()())"));
assert_se(signature_is_single("(((())))"));
assert_se(signature_is_single("((((s))))"));
assert_se(signature_is_single("{ss}"));
assert_se(signature_is_single("a{ss}"));
assert_se(!signature_is_single("uu"));
assert_se(!signature_is_single(""));
assert_se(!signature_is_single("("));
assert_se(!signature_is_single(")"));
assert_se(!signature_is_single("())"));
assert_se(!signature_is_single("((())"));
assert_se(!signature_is_single("{)"));
assert_se(!signature_is_single("{}"));
assert_se(!signature_is_single("{sss}"));
assert_se(!signature_is_single("{s}"));
assert_se(!signature_is_single("{ass}"));
assert_se(!signature_is_single("a}"));
assert_se(signature_is_single("y", false));
assert_se(signature_is_single("u", false));
assert_se(signature_is_single("v", false));
assert_se(signature_is_single("as", false));
assert_se(signature_is_single("(ss)", false));
assert_se(signature_is_single("()", false));
assert_se(signature_is_single("(()()()()())", false));
assert_se(signature_is_single("(((())))", false));
assert_se(signature_is_single("((((s))))", false));
assert_se(signature_is_single("{ss}", true));
assert_se(signature_is_single("a{ss}", false));
assert_se(!signature_is_single("uu", false));
assert_se(!signature_is_single("", false));
assert_se(!signature_is_single("(", false));
assert_se(!signature_is_single(")", false));
assert_se(!signature_is_single("())", false));
assert_se(!signature_is_single("((())", false));
assert_se(!signature_is_single("{)", false));
assert_se(!signature_is_single("{}", true));
assert_se(!signature_is_single("{sss}", true));
assert_se(!signature_is_single("{s}", true));
assert_se(!signature_is_single("{ss}", false));
assert_se(!signature_is_single("{ass}", true));
assert_se(!signature_is_single("a}", true));
assert_se(signature_is_pair("yy"));
assert_se(signature_is_pair("ss"));
@ -112,5 +113,25 @@ int main(int argc, char *argv[]) {
assert_se(!namespace_simple_pattern("", "foo"));
assert_se(!namespace_simple_pattern("foo", ""));
assert_se(streq(object_path_startswith("/foo/bar", "/foo"), "bar"));
assert_se(streq(object_path_startswith("/foo", "/foo"), ""));
assert_se(streq(object_path_startswith("/foo", "/"), "foo"));
assert_se(streq(object_path_startswith("/", "/"), ""));
assert_se(!object_path_startswith("/foo", "/bar"));
assert_se(!object_path_startswith("/", "/bar"));
assert_se(!object_path_startswith("/foo", ""));
assert_se(object_path_is_valid("/foo/bar"));
assert_se(object_path_is_valid("/foo"));
assert_se(object_path_is_valid("/"));
assert_se(object_path_is_valid("/foo5"));
assert_se(object_path_is_valid("/foo_5"));
assert_se(!object_path_is_valid(""));
assert_se(!object_path_is_valid("/foo/"));
assert_se(!object_path_is_valid("//"));
assert_se(!object_path_is_valid("//foo"));
assert_se(!object_path_is_valid("/foo//bar"));
assert_se(!object_path_is_valid("/foo/aaaäöä"));
return 0;
}

39
src/systemd/sd-bus-protocol.h

@ -108,6 +108,21 @@ enum {
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
#define SD_BUS_INTROSPECT_INTERFACE_PEER \
" <interface name=\"org.freedesktop.DBus.Peer\">\n" \
" <method name=\"Ping\"/>\n" \
" <method name=\"GetMachineId\">\n" \
" <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
" </method>\n" \
" </interface>\n"
#define SD_BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \
" <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \
" <method name=\"Introspect\">\n" \
" <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
" </method>\n" \
" </interface>\n"
#define SD_BUS_INTROSPECT_INTERFACE_PROPERTIES \
" <interface name=\"org.freedesktop.DBus.Properties\">\n" \
" <method name=\"Get\">\n" \
@ -131,21 +146,21 @@ enum {
" </signal>\n" \
" </interface>\n"
#define SD_BUS_INTROSPECT_INTERFACE_INTROSPECTABLE \
" <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \
" <method name=\"Introspect\">\n" \
" <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
#define SD_BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER \
" <interface name=\"org.freedesktop.DBus.ObjectManager\">\n" \
" <method name=\"GetManagedObjects\">\n" \
" <arg type=\"a{oa{sa{sv}}}\" name=\"object_paths_interfaces_and_properties\" direction=\"out\"/>\n" \
" </method>\n" \
" <signal name=\"InterfacesAdded\">\n" \
" <arg type=\"o\" name=\"object_path\"/>\n" \
" <arg type=\"a{sa{sv}}\" name=\"interfaces_and_properties\"/>\n" \
" </signal>\n" \
" <signal name=\"InterfacesRemoved\">\n" \
" <arg type=\"o\" name=\"object_path\"/>\n" \
" <arg type=\"as\" name=\"interfaces\"/>\n" \
" </signal>\n" \
" </interface>\n"
#define SD_BUS_INTROSPECT_INTERFACE_PEER \
"<interface name=\"org.freedesktop.DBus.Peer\">\n" \
" <method name=\"Ping\"/>\n" \
" <method name=\"GetMachineId\">\n" \
" <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
" </method>\n" \
"</interface>\n"
#ifdef __cplusplus
}
#endif

127
src/systemd/sd-bus-vtable.h

@ -0,0 +1,127 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#ifndef foosdbusvtablehfoo
#define foosdbusvtablehfoo
/***
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/>.
***/
typedef struct sd_bus_vtable sd_bus_vtable;
#include "sd-bus.h"
enum {
_SD_BUS_VTABLE_START = '<',
_SD_BUS_VTABLE_END = '>',
_SD_BUS_VTABLE_METHOD = 'M',
_SD_BUS_VTABLE_SIGNAL = 'S',
_SD_BUS_VTABLE_PROPERTY = 'P',
_SD_BUS_VTABLE_WRITABLE_PROPERTY = 'W',
_SD_BUS_VTABLE_CHILDREN = 'C'
};
enum {
SD_BUS_VTABLE_DEPRECATED = 1,
SD_BUS_VTABLE_METHOD_NO_REPLY = 2,
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE = 4,
SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY = 8,
};
struct sd_bus_vtable {
/* Please do not initialize this structure directly, use the
* macros below instead */
int type;
int flags;
union {
struct {
size_t element_size;
} start;
struct {
const char *member;
const char *signature;
const char *result;
sd_bus_message_handler_t handler;
} method;
struct {
const char *member;
const char *signature;
} signal;
struct {
const char *member;
const char *signature;
sd_bus_property_get_t get;
sd_bus_property_set_t set;
size_t offset;
} property;
};
};
#define SD_BUS_VTABLE_START(_flags) \
{ \
.type = _SD_BUS_VTABLE_START, \
.flags = _flags, \
.start.element_size = sizeof(sd_bus_vtable), \
}
#define SD_BUS_METHOD(_member, _signature, _result, _flags, _handler) \
{ \
.type = _SD_BUS_VTABLE_METHOD, \
.flags = _flags, \
.method.member = _member, \
.method.signature = _signature, \
.method.result = _result, \
.method.handler = _handler, \
}
#define SD_BUS_SIGNAL(_member, _signature, _flags) \
{ \
.type = _SD_BUS_VTABLE_SIGNAL, \
.flags = _flags, \
.signal.member = _member, \
.signal.signature = _signature, \
}
#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, \
}
#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, \
}
#define SD_BUS_VTABLE_END \
{ \
.type = _SD_BUS_VTABLE_END, \
}
#endif

54
src/systemd/sd-bus.h

@ -26,8 +26,6 @@
#include <sys/types.h>
#include <sd-id128.h>
#include "sd-bus-protocol.h"
#include "sd-memfd.h"
#ifdef __cplusplus
extern "C" {
@ -41,6 +39,8 @@ extern "C" {
# endif
#endif
/* Types */
typedef struct sd_bus sd_bus;
typedef struct sd_bus_message sd_bus_message;
@ -50,8 +50,21 @@ typedef struct {
int need_free;
} sd_bus_error;
/* Callbacks */
typedef int (*sd_bus_message_handler_t)(sd_bus *bus, sd_bus_message *m, void *userdata);
typedef int (*sd_bus_property_get_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, sd_bus_error *error, void *userdata);
typedef int (*sd_bus_property_set_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, sd_bus_error *error, void *userdata);
typedef int (*sd_bus_object_find_t) (sd_bus *bus, const char *path, const char *interface, void **found, void *userdata);
typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *path, char ***nodes, void *userdata);
#include "sd-bus-protocol.h"
#include "sd-bus-vtable.h"
#include "sd-memfd.h"
/* Connections */
int sd_bus_open_system(sd_bus **ret);
@ -98,21 +111,33 @@ int sd_bus_flush(sd_bus *bus);
int sd_bus_add_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_remove_filter(sd_bus *bus, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_add_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_remove_match(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata);
int sd_bus_add_object_vtable(sd_bus *bus, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata);
int sd_bus_remove_object_vtable(sd_bus *bus, const char *path, const char *interface);
/* Message object */