bus: add support for serializing to gvariant

(deserialization is still missing, hence this is not hooked up to kdbus)
This commit is contained in:
Lennart Poettering 2013-12-05 02:46:54 +01:00
parent 65dae17a2f
commit c1b9d93572
11 changed files with 1146 additions and 239 deletions

1
.gitignore vendored
View file

@ -104,6 +104,7 @@
/test-bus-signature
/test-bus-server
/test-bus-zero-copy
/test-bus-gvariant
/test-calendarspec
/test-catalog
/test-cgroup

View file

@ -1997,6 +1997,8 @@ libsystemd_bus_la_SOURCES = \
src/libsystemd-bus/bus-introspect.h \
src/libsystemd-bus/bus-objects.c \
src/libsystemd-bus/bus-objects.h \
src/libsystemd-bus/bus-gvariant.c \
src/libsystemd-bus/bus-gvariant.h \
src/libsystemd-bus/bus-convenience.c \
src/libsystemd-bus/kdbus.h \
src/libsystemd-bus/sd-memfd.c \
@ -2065,6 +2067,7 @@ tests += \
test-bus-objects \
test-bus-error \
test-bus-creds \
test-bus-gvariant \
test-event
bin_PROGRAMS += \
@ -2149,6 +2152,20 @@ test_bus_error_LDADD = \
libsystemd-daemon-internal.la \
libsystemd-shared.la
test_bus_gvariant_SOURCES = \
src/libsystemd-bus/test-bus-gvariant.c
test_bus_gvariant_LDADD = \
libsystemd-bus-internal.la \
libsystemd-id128-internal.la \
libsystemd-daemon-internal.la \
libsystemd-shared.la \
$(GLIB_LIBS)
test_bus_gvariant_CFLAGS = \
$(AM_CFLAGS) \
$(GLIB_CFLAGS)
test_bus_creds_SOURCES = \
src/libsystemd-bus/test-bus-creds.c

View file

@ -0,0 +1,59 @@
How we use GVariant for serializing D-Bus messages
--------------------------------------------------
We stay as close to the original dbus1 framing as possible. dbus1 has
the following framing:
1. A fixed header of "yyyyuu"
2. Additional header fields of "a(yv)"
3. Padding with NUL bytes to pad up to next 8byte boundary
4. The body
Note that the body is not padded at the end, the complete message
hence might have a non-aligned size. Reading multiple messages at once
will hence result in possibly unaligned messages in memory.
The header consists of the following:
y Endianness, 'l' or 'B'
y Message Type
y Flags
y Protocol version, '1'
u Length of the body, i.e. the length of part 4 above
u Serial number
= 12 bytes
When using GVariant we keep the basic structure in place, only
slightly extend the header, and define protocol version '2'. The new
header:
y Endianness, 'l' or 'B'
y Message Type
y Flags
y Protocol version, '2'
u Length of the body, i.e. the length of part 4 above
u Serial number
u Length of the additional header fields array
= 16 bytes
This has the nice benefit that the beginning of the additional header
fields array is aligned to an 8 byte boundary. Also, in dbus1
marshalling arrays start with a length value of 32bit, which means in
both dbus1 and gvariant marshallings the size of the header fields
array will be at the same location between bytes 12 and 16. To
visualize that:
0 4 8 12 16
Common: | E | T | F | V | Body Length | Serial | Fields Length |
dbus1: | ... (as above) ... | Fields array ...
gvariant: | ... (as above) ... | Fields Length | Fields array ...
And that's already it.
Note: on kdbus only native endian messages marshalled in gvariant may
be sent. If a client receives a message in non-native endianness
or in dbus1 marshalling it shall ignore the message.

View file

@ -0,0 +1,189 @@
/*-*- 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 "bus-type.h"
#include "bus-gvariant.h"
#include "bus-signature.h"
int bus_gvariant_get_size(char c) {
switch (c) {
case SD_BUS_TYPE_BOOLEAN:
case SD_BUS_TYPE_BYTE:
return 1;
case SD_BUS_TYPE_INT16:
case SD_BUS_TYPE_UINT16:
return 2;
case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32:
case SD_BUS_TYPE_UNIX_FD:
return 4;
case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64:
case SD_BUS_TYPE_DOUBLE:
return 8;
}
return -EINVAL;
}
int bus_gvariant_get_alignment(const char *signature) {
size_t alignment = 1;
const char *p;
int r;
p = signature;
while (*p != 0 && alignment < 8) {
size_t n;
int a;
r = signature_element_length(p, &n);
if (r < 0)
return r;
switch (*p) {
case SD_BUS_TYPE_BYTE:
case SD_BUS_TYPE_BOOLEAN:
case SD_BUS_TYPE_STRING:
case SD_BUS_TYPE_OBJECT_PATH:
case SD_BUS_TYPE_SIGNATURE:
a = 1;
break;
case SD_BUS_TYPE_INT16:
case SD_BUS_TYPE_UINT16:
a = 2;
break;
case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32:
case SD_BUS_TYPE_UNIX_FD:
a = 4;
break;
case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64:
case SD_BUS_TYPE_DOUBLE:
case SD_BUS_TYPE_VARIANT:
a = 8;
break;
case SD_BUS_TYPE_ARRAY: {
char t[n];
memcpy(t, p + 1, n - 1);
t[n - 1] = 0;
a = bus_gvariant_get_alignment(t);
break;
}
case SD_BUS_TYPE_STRUCT_BEGIN:
case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
char t[n-1];
memcpy(t, p + 1, n - 2);
t[n - 2] = 0;
a = bus_gvariant_get_alignment(t);
break;
}
default:
assert_not_reached("Unknown signature type");
}
if (a < 0)
return a;
assert(a > 0 && a <= 8);
if ((size_t) a > alignment)
alignment = (size_t) a;
p += n;
}
return alignment;
}
int bus_gvariant_is_fixed_size(const char *signature) {
const char *p;
int r;
assert(signature);
p = signature;
while (*p != 0) {
size_t n;
r = signature_element_length(p, &n);
if (r < 0)
return r;
switch (*p) {
case SD_BUS_TYPE_STRING:
case SD_BUS_TYPE_OBJECT_PATH:
case SD_BUS_TYPE_SIGNATURE:
case SD_BUS_TYPE_ARRAY:
case SD_BUS_TYPE_VARIANT:
return 0;
case SD_BUS_TYPE_BYTE:
case SD_BUS_TYPE_BOOLEAN:
case SD_BUS_TYPE_INT16:
case SD_BUS_TYPE_UINT16:
case SD_BUS_TYPE_INT32:
case SD_BUS_TYPE_UINT32:
case SD_BUS_TYPE_UNIX_FD:
case SD_BUS_TYPE_INT64:
case SD_BUS_TYPE_UINT64:
case SD_BUS_TYPE_DOUBLE:
break;
case SD_BUS_TYPE_STRUCT_BEGIN:
case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
char t[n-1];
memcpy(t, p + 1, n - 2);
t[n - 2] = 0;
r = bus_gvariant_is_fixed_size(t);
if (r <= 0)
return r;
break;
}
default:
assert_not_reached("Unknown signature type");
}
p += n;
}
return true;
}

View file

@ -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/>.
***/
int bus_gvariant_get_size(char c);
int bus_gvariant_get_alignment(const char *signature);
int bus_gvariant_is_fixed_size(const char *signature);

View file

@ -159,6 +159,7 @@ struct sd_bus {
bool match_callbacks_modified:1;
bool filter_callbacks_modified:1;
bool nodes_modified:1;
bool use_gvariant:1;
int use_memfd;

File diff suppressed because it is too large Load diff

View file

@ -41,6 +41,11 @@ struct bus_container {
uint32_t *array_size;
size_t before, begin;
size_t *offsets;
size_t n_offsets, n_allocated;
bool need_offsets;
};
struct bus_header {
@ -93,6 +98,7 @@ struct sd_bus_message {
bool free_fds:1;
bool release_kdbus:1;
bool poisoned:1;
bool is_gvariant:1;
struct bus_header *header;
struct bus_body_part body;
@ -126,6 +132,9 @@ struct sd_bus_message {
char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1];
size_t header_offsets[_BUS_MESSAGE_HEADER_MAX];
unsigned n_header_offsets;
};
#define BUS_MESSAGE_NEED_BSWAP(m) ((m)->header->endian != BUS_NATIVE_ENDIAN)

View file

@ -31,5 +31,6 @@ bool bus_type_is_valid_in_signature(char c);
bool bus_type_is_basic(char c);
bool bus_type_is_trivial(char c);
bool bus_type_is_container(char c);
int bus_type_get_alignment(char c);
int bus_type_get_size(char c);

View file

@ -0,0 +1,134 @@
/*-*- 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/>.
***/
#ifdef HAVE_GLIB
#include <glib.h>
#endif
#include "util.h"
#include "sd-bus.h"
#include "bus-gvariant.h"
#include "bus-util.h"
#include "bus-internal.h"
#include "bus-message.h"
static void test_bus_gvariant_is_fixed_size(void) {
assert(bus_gvariant_is_fixed_size("") > 0);
assert(bus_gvariant_is_fixed_size("y") > 0);
assert(bus_gvariant_is_fixed_size("u") > 0);
assert(bus_gvariant_is_fixed_size("b") > 0);
assert(bus_gvariant_is_fixed_size("n") > 0);
assert(bus_gvariant_is_fixed_size("q") > 0);
assert(bus_gvariant_is_fixed_size("i") > 0);
assert(bus_gvariant_is_fixed_size("t") > 0);
assert(bus_gvariant_is_fixed_size("d") > 0);
assert(bus_gvariant_is_fixed_size("s") == 0);
assert(bus_gvariant_is_fixed_size("o") == 0);
assert(bus_gvariant_is_fixed_size("g") == 0);
assert(bus_gvariant_is_fixed_size("h") > 0);
assert(bus_gvariant_is_fixed_size("ay") == 0);
assert(bus_gvariant_is_fixed_size("v") == 0);
assert(bus_gvariant_is_fixed_size("(u)") > 0);
assert(bus_gvariant_is_fixed_size("(uuuuy)") > 0);
assert(bus_gvariant_is_fixed_size("(uusuuy)") == 0);
assert(bus_gvariant_is_fixed_size("a{ss}") == 0);
assert(bus_gvariant_is_fixed_size("((u)yyy(b(iiii)))") > 0);
assert(bus_gvariant_is_fixed_size("((u)yyy(b(iiivi)))") == 0);
}
static void test_bus_gvariant_get_alignment(void) {
assert(bus_gvariant_get_alignment("") == 1);
assert(bus_gvariant_get_alignment("y") == 1);
assert(bus_gvariant_get_alignment("b") == 1);
assert(bus_gvariant_get_alignment("u") == 4);
assert(bus_gvariant_get_alignment("s") == 1);
assert(bus_gvariant_get_alignment("o") == 1);
assert(bus_gvariant_get_alignment("g") == 1);
assert(bus_gvariant_get_alignment("v") == 8);
assert(bus_gvariant_get_alignment("h") == 4);
assert(bus_gvariant_get_alignment("i") == 4);
assert(bus_gvariant_get_alignment("t") == 8);
assert(bus_gvariant_get_alignment("x") == 8);
assert(bus_gvariant_get_alignment("q") == 2);
assert(bus_gvariant_get_alignment("n") == 2);
assert(bus_gvariant_get_alignment("d") == 8);
assert(bus_gvariant_get_alignment("ay") == 1);
assert(bus_gvariant_get_alignment("as") == 1);
assert(bus_gvariant_get_alignment("au") == 4);
assert(bus_gvariant_get_alignment("an") == 2);
assert(bus_gvariant_get_alignment("ans") == 2);
assert(bus_gvariant_get_alignment("ant") == 8);
assert(bus_gvariant_get_alignment("(ss)") == 1);
assert(bus_gvariant_get_alignment("(ssu)") == 4);
assert(bus_gvariant_get_alignment("a(ssu)") == 4);
}
static void test_marshal(void) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
_cleanup_bus_unref_ sd_bus *bus = NULL;
_cleanup_free_ void *p = NULL;
assert_se(sd_bus_open_system(&bus) >= 0);
bus->use_gvariant = true; /* dirty hack */
assert_se(sd_bus_message_new_method_call(bus, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName", &m) >= 0);
/* assert_se(sd_bus_message_append(m, "ssy(sts)v", "first-string-parameter", "second-string-parameter", 9, "a", (uint64_t) 7777, "b", "(su)", "xxx", 4712) >= 0); */
assert_se(sd_bus_message_append(m,
"a(usv)", 2,
4711, "first-string-parameter", "(st)", "X", (uint64_t) 1111,
4712, "second-string-parameter", "(a(si))", 2, "Y", 5, "Z", 6) >= 0);
assert_se(bus_message_seal(m, 4711) >= 0);
#ifdef HAVE_GLIB
{
GVariant *v;
char *t;
#if !defined(GLIB_VERSION_2_36)
g_type_init();
#endif
v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuuua(yv))"), m->header, sizeof(struct bus_header) + BUS_MESSAGE_FIELDS_SIZE(m), false, NULL, NULL);
t = g_variant_print(v, TRUE);
printf("%s\n", t);
g_free(t);
g_variant_unref(v);
v = g_variant_new_from_data(G_VARIANT_TYPE("(a(usv))"), m->body.data, BUS_MESSAGE_BODY_SIZE(m), false, NULL, NULL);
t = g_variant_print(v, TRUE);
printf("%s\n", t);
g_free(t);
g_variant_unref(v);
}
#endif
}
int main(int argc, char *argv[]) {
test_bus_gvariant_is_fixed_size();
test_bus_gvariant_get_alignment();
test_marshal();
return 0;
}

View file

@ -163,9 +163,9 @@ int main(int argc, char *argv[]) {
dbus_error_init(&error);
w = dbus_message_demarshal(buffer, sz, &error);
if (!w) {
if (!w)
log_error("%s", error.message);
} else
else
dbus_message_unref(w);
}
#endif