mirror of
https://github.com/systemd/systemd.git
synced 2025-02-01 09:47:35 +03:00
bus: add support for serializing to gvariant
(deserialization is still missing, hence this is not hooked up to kdbus)
This commit is contained in:
parent
65dae17a2f
commit
c1b9d93572
1
.gitignore
vendored
1
.gitignore
vendored
@ -104,6 +104,7 @@
|
||||
/test-bus-signature
|
||||
/test-bus-server
|
||||
/test-bus-zero-copy
|
||||
/test-bus-gvariant
|
||||
/test-calendarspec
|
||||
/test-catalog
|
||||
/test-cgroup
|
||||
|
17
Makefile.am
17
Makefile.am
@ -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
|
||||
|
||||
|
59
src/libsystemd-bus/GVARIANT-SERIALIZATION
Normal file
59
src/libsystemd-bus/GVARIANT-SERIALIZATION
Normal 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.
|
189
src/libsystemd-bus/bus-gvariant.c
Normal file
189
src/libsystemd-bus/bus-gvariant.c
Normal 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;
|
||||
}
|
26
src/libsystemd-bus/bus-gvariant.h
Normal file
26
src/libsystemd-bus/bus-gvariant.h
Normal 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);
|
@ -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
@ -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)
|
||||
|
@ -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);
|
||||
|
134
src/libsystemd-bus/test-bus-gvariant.c
Normal file
134
src/libsystemd-bus/test-bus-gvariant.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user