diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile.am | 17 | ||||
-rw-r--r-- | src/libsystemd-bus/GVARIANT-SERIALIZATION | 59 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-gvariant.c | 189 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-gvariant.h | 26 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-internal.h | 1 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-message.c | 900 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-message.h | 9 | ||||
-rw-r--r-- | src/libsystemd-bus/bus-type.h | 1 | ||||
-rw-r--r-- | src/libsystemd-bus/test-bus-gvariant.c | 134 | ||||
-rw-r--r-- | src/libsystemd-bus/test-bus-marshal.c | 4 |
11 files changed, 1124 insertions, 217 deletions
diff --git a/.gitignore b/.gitignore index db2641ff83..f00cb8ee1a 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,7 @@ /test-bus-signature /test-bus-server /test-bus-zero-copy +/test-bus-gvariant /test-calendarspec /test-catalog /test-cgroup diff --git a/Makefile.am b/Makefile.am index acf511bc4f..194aaa8fbf 100644 --- a/Makefile.am +++ b/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 diff --git a/src/libsystemd-bus/GVARIANT-SERIALIZATION b/src/libsystemd-bus/GVARIANT-SERIALIZATION new file mode 100644 index 0000000000..b23c7e4edb --- /dev/null +++ b/src/libsystemd-bus/GVARIANT-SERIALIZATION @@ -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. diff --git a/src/libsystemd-bus/bus-gvariant.c b/src/libsystemd-bus/bus-gvariant.c new file mode 100644 index 0000000000..d2e134e0e2 --- /dev/null +++ b/src/libsystemd-bus/bus-gvariant.c @@ -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; +} diff --git a/src/libsystemd-bus/bus-gvariant.h b/src/libsystemd-bus/bus-gvariant.h new file mode 100644 index 0000000000..091a47213d --- /dev/null +++ b/src/libsystemd-bus/bus-gvariant.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/>. +***/ + +int bus_gvariant_get_size(char c); +int bus_gvariant_get_alignment(const char *signature); +int bus_gvariant_is_fixed_size(const char *signature); diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h index 4881e0427c..d3db13b1de 100644 --- a/src/libsystemd-bus/bus-internal.h +++ b/src/libsystemd-bus/bus-internal.h @@ -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; diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c index 0cfbf5a65b..73b0bab8f5 100644 --- a/src/libsystemd-bus/bus-message.c +++ b/src/libsystemd-bus/bus-message.c @@ -34,6 +34,7 @@ #include "bus-internal.h" #include "bus-type.h" #include "bus-signature.h" +#include "bus-gvariant.h" static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored); @@ -152,7 +153,7 @@ static void message_free(sd_bus_message *m) { free(m); } -static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz) { +static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz, bool add_offset) { void *op, *np; size_t old_size, new_size, start; @@ -205,6 +206,13 @@ static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz) { m->free_header = true; + if (add_offset) { + if (m->n_header_offsets >= ELEMENTSOF(m->header_offsets)) + goto poison; + + m->header_offsets[m->n_header_offsets++] = new_size - sizeof(struct bus_header); + } + return (uint8_t*) np + start; poison: @@ -224,25 +232,48 @@ static int message_append_field_string( assert(m); + /* dbus1 doesn't allow strings over 32bit, let's enforce this + * globally, to not risk convertability */ l = strlen(s); if (l > (size_t) (uint32_t) -1) return -EINVAL; - /* field id byte + signature length + signature 's' + NUL + string length + string + NUL */ - p = message_extend_fields(m, 8, 4 + 4 + l + 1); - if (!p) - return -ENOMEM; + /* Signature "(yv)" where the variant contains "s" */ - p[0] = h; - p[1] = 1; - p[2] = type; - p[3] = 0; + if (m->is_gvariant) { - ((uint32_t*) p)[1] = l; - memcpy(p + 8, s, l + 1); + /* (field id byte + 7x padding, ((string + NUL) + NUL + signature string 's') */ + p = message_extend_fields(m, 8, 1 + 7 + l + 1 + 1 + 1, true); + if (!p) + return -ENOMEM; - if (ret) - *ret = (char*) p + 8; + p[0] = h; + memset(p+1, 0, 7); + memcpy(p+8, s, l); + p[8+l] = 0; + p[8+l+1] = 0; + p[8+l+2] = type; + + if (ret) + *ret = (char*) p + 1; + + } else { + /* (field id byte + (signature length + signature 's' + NUL) + (string length + string + NUL)) */ + p = message_extend_fields(m, 8, 4 + 4 + l + 1, false); + if (!p) + return -ENOMEM; + + p[0] = h; + p[1] = 1; + p[2] = type; + p[3] = 0; + + ((uint32_t*) p)[1] = l; + memcpy(p + 8, s, l + 1); + + if (ret) + *ret = (char*) p + 8; + } return 0; } @@ -258,24 +289,33 @@ static int message_append_field_signature( assert(m); + /* dbus1 doesn't allow signatures over 32bit, let's enforce + * this globally, to not risk convertability */ l = strlen(s); if (l > 255) return -EINVAL; - /* field id byte + signature length + signature 'g' + NUL + string length + string + NUL */ - p = message_extend_fields(m, 8, 4 + 1 + l + 1); - if (!p) - return -ENOMEM; + /* Signature "(yv)" where the variant contains "g" */ - p[0] = h; - p[1] = 1; - p[2] = SD_BUS_TYPE_SIGNATURE; - p[3] = 0; - p[4] = l; - memcpy(p + 5, s, l + 1); + if (m->is_gvariant) + /* For gvariant the serialization is the same as for normal strings */ + return message_append_field_string(m, h, 'g', s, ret); + else { + /* (field id byte + (signature length + signature 'g' + NUL) + (string length + string + NUL)) */ + p = message_extend_fields(m, 8, 4 + 1 + l + 1, false); + if (!p) + return -ENOMEM; - if (ret) - *ret = (const char*) p + 5; + p[0] = h; + p[1] = 1; + p[2] = SD_BUS_TYPE_SIGNATURE; + p[3] = 0; + p[4] = l; + memcpy(p + 5, s, l + 1); + + if (ret) + *ret = (const char*) p + 5; + } return 0; } @@ -285,17 +325,31 @@ static int message_append_field_uint32(sd_bus_message *m, uint8_t h, uint32_t x) assert(m); - /* field id byte + signature length + signature 'u' + NUL + value */ - p = message_extend_fields(m, 8, 4 + 4); - if (!p) - return -ENOMEM; + if (m->is_gvariant) { + /* (field id byte + 7x padding + ((value + NUL + signature string 'u') */ + + p = message_extend_fields(m, 8, 1 + 7 + 4 + 1 + 1, true); + if (!p) + return -ENOMEM; + + p[0] = h; + memset(p+1, 0, 7); + *((uint32_t*) (p + 8)) = x; + p[12] = 0; + p[13] = 'u'; + } else { + /* (field id byte + (signature length + signature 'u' + NUL) + value) */ + p = message_extend_fields(m, 8, 4 + 4, false); + if (!p) + return -ENOMEM; - p[0] = h; - p[1] = 1; - p[2] = SD_BUS_TYPE_UINT32; - p[3] = 0; + p[0] = h; + p[1] = 1; + p[2] = SD_BUS_TYPE_UINT32; + p[3] = 0; - ((uint32_t*) p)[1] = x; + ((uint32_t*) p)[1] = x; + } return 0; } @@ -440,6 +494,7 @@ static sd_bus_message *message_new(sd_bus *bus, uint8_t type) { m->header->type = type; m->header->version = bus ? bus->message_version : 1; m->allow_fds = !bus || bus->can_fds || (bus->state != BUS_HELLO && bus->state != BUS_RUNNING); + m->root_container.need_offsets = m->is_gvariant = bus ? bus->use_gvariant : false; if (bus) m->bus = sd_bus_ref(bus); @@ -1034,6 +1089,27 @@ static int part_make_space( return 0; } +static int message_add_offset(sd_bus_message *m, size_t offset) { + struct bus_container *c; + + assert(m); + assert(m->is_gvariant); + + /* Add offset to current container, unless this is the first + * item in it, which will have the 0 offset, which we can + * ignore. */ + c = message_get_container(m); + + if (!c->need_offsets) + return 0; + + if (!GREEDY_REALLOC(c->offsets, c->n_allocated, c->n_offsets + 1)) + return -ENOMEM; + + c->offsets[c->n_offsets++] = offset; + return 0; +} + static void message_extend_containers(sd_bus_message *m, size_t expand) { struct bus_container *c; @@ -1043,12 +1119,14 @@ static void message_extend_containers(sd_bus_message *m, size_t expand) { return; /* Update counters */ - for (c = m->containers; c < m->containers + m->n_containers; c++) + for (c = m->containers; c < m->containers + m->n_containers; c++) { + if (c->array_size) *c->array_size += expand; + } } -static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) { +static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz, bool add_offset) { struct bus_body_part *part = NULL; size_t start_body, end_body, padding, start_part, end_part, added; bool add_new_part; @@ -1126,17 +1204,51 @@ static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) { m->header->body_size = end_body; message_extend_containers(m, added); + if (add_offset) { + r = message_add_offset(m, end_body); + if (r < 0) { + m->poisoned = true; + return NULL; + } + } + return p; } +static int message_push_fd(sd_bus_message *m, int fd) { + int *f, copy; + + assert(m); + + if (fd < 0) + return -EINVAL; + + if (!m->allow_fds) + return -ENOTSUP; + + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) + return -errno; + + f = realloc(m->fds, sizeof(int) * (m->n_fds + 1)); + if (!f) { + m->poisoned = true; + close_nointr_nofail(copy); + return -ENOMEM; + } + + m->fds = f; + m->fds[m->n_fds] = copy; + m->free_fds = true; + + return copy; +} + int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored) { + _cleanup_close_ int fd = -1; struct bus_container *c; ssize_t align, sz; - uint32_t k; void *a; - int fd = -1; - uint32_t fdi = 0; - int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); @@ -1164,150 +1276,157 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void } } - switch (type) { + if (m->is_gvariant) { + uint8_t u8; + uint32_t u32; - case SD_BUS_TYPE_STRING: - /* To make things easy we'll serialize a NULL string - * into the empty string */ - p = strempty(p); + switch (type) { - /* Fall through... */ - case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: + case SD_BUS_TYPE_STRING: + p = strempty(p); - if (!p) { - r = -EINVAL; - goto fail; - } + /* Fall through... */ + case SD_BUS_TYPE_OBJECT_PATH: + if (!p) + return -EINVAL; - align = 4; - sz = 4 + strlen(p) + 1; - break; + align = 1; + sz = strlen(p) + 1; + break; - case SD_BUS_TYPE_SIGNATURE: + case SD_BUS_TYPE_BOOLEAN: - if (!p) { - r = -EINVAL; - goto fail; - } + u8 = p && *(int*) p; + p = &u8; - align = 1; - sz = 1 + strlen(p) + 1; - break; + align = sz = 1; + break; - case SD_BUS_TYPE_BOOLEAN: + case SD_BUS_TYPE_UNIX_FD: - if (!p) { - r = -EINVAL; - goto fail; - } + if (!p) + return -EINVAL; - align = sz = 4; + fd = message_push_fd(m, *(int*) p); + if (fd < 0) + return fd; - assert_cc(sizeof(int) == sizeof(uint32_t)); - memcpy(&k, p, 4); - k = !!k; - p = &k; - break; + u32 = m->n_fds; + p = &u32; - case SD_BUS_TYPE_UNIX_FD: { - int z, *f; + align = sz = 4; + break; - if (!p) { - r = -EINVAL; - goto fail; + default: + align = bus_gvariant_get_alignment(CHAR_TO_STR(type)); + sz = bus_gvariant_get_size(type); + break; } - if (!m->allow_fds) { - r = -ENOTSUP; - goto fail; - } + assert(align > 0); + assert(sz > 0); - align = sz = 4; + a = message_extend_body(m, align, sz, true); + if (!a) + return -ENOMEM; - z = *(int*) p; - if (z < 0) { - r = -EINVAL; - goto fail; - } + memcpy(a, p, sz); - fd = fcntl(z, F_DUPFD_CLOEXEC, 3); - if (fd < 0) { - r = -errno; - goto fail; - } + if (stored) + *stored = (const uint8_t*) a; - f = realloc(m->fds, sizeof(int) * (m->n_fds + 1)); - if (!f) { - m->poisoned = true; - r = -ENOMEM; - goto fail; - } + } else { + uint32_t u32; - fdi = m->n_fds; - f[fdi] = fd; - m->fds = f; - m->free_fds = true; - break; - } + switch (type) { - default: - if (!p) { - r = -EINVAL; - goto fail; - } + case SD_BUS_TYPE_STRING: + /* To make things easy we'll serialize a NULL string + * into the empty string */ + p = strempty(p); - align = bus_type_get_alignment(type); - sz = bus_type_get_size(type); - break; - } + /* Fall through... */ + case SD_BUS_TYPE_OBJECT_PATH: - assert(align > 0); - assert(sz > 0); + if (!p) + return -EINVAL; - a = message_extend_body(m, align, sz); - if (!a) { - r = -ENOMEM; - goto fail; - } + align = 4; + sz = 4 + strlen(p) + 1; + break; - if (type == SD_BUS_TYPE_STRING || type == SD_BUS_TYPE_OBJECT_PATH) { - *(uint32_t*) a = sz - 5; - memcpy((uint8_t*) a + 4, p, sz - 4); + case SD_BUS_TYPE_SIGNATURE: - if (stored) - *stored = (const uint8_t*) a + 4; + p = strempty(p); - } else if (type == SD_BUS_TYPE_SIGNATURE) { - *(uint8_t*) a = sz - 1; - memcpy((uint8_t*) a + 1, p, sz - 1); + align = 1; + sz = 1 + strlen(p) + 1; + break; - if (stored) - *stored = (const uint8_t*) a + 1; - } else if (type == SD_BUS_TYPE_UNIX_FD) { - *(uint32_t*) a = fdi; + case SD_BUS_TYPE_BOOLEAN: - if (stored) - *stored = a; + u32 = p && *(int*) p; + align = sz = 4; + break; - m->n_fds ++; + case SD_BUS_TYPE_UNIX_FD: - } else { - memcpy(a, p, sz); + if (!p) + return -EINVAL; - if (stored) - *stored = a; + fd = message_push_fd(m, *(int*) p); + if (fd < 0) + return fd; + + u32 = m->n_fds; + p = &u32; + + align = sz = 4; + break; + + default: + align = bus_type_get_alignment(type); + sz = bus_type_get_size(type); + break; + } + + assert(align > 0); + assert(sz > 0); + + a = message_extend_body(m, align, sz, false); + if (!a) + return -ENOMEM; + + if (type == SD_BUS_TYPE_STRING || type == SD_BUS_TYPE_OBJECT_PATH) { + *(uint32_t*) a = sz - 5; + memcpy((uint8_t*) a + 4, p, sz - 4); + + if (stored) + *stored = (const uint8_t*) a + 4; + + } else if (type == SD_BUS_TYPE_SIGNATURE) { + *(uint8_t*) a = sz - 1; + memcpy((uint8_t*) a + 1, p, sz - 1); + + if (stored) + *stored = (const uint8_t*) a + 1; + } else { + memcpy(a, p, sz); + + if (stored) + *stored = a; + } } + if (type == SD_BUS_TYPE_UNIX_FD) + m->n_fds ++; + if (c->enclosing != SD_BUS_TYPE_ARRAY) c->index++; + fd = -1; return 0; - -fail: - if (fd >= 0) - close_nointr_nofail(fd); - - return r; } _public_ int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p) { @@ -1348,12 +1467,20 @@ _public_ int sd_bus_message_append_string_space( } } - a = message_extend_body(m, 4, 4 + size + 1); - if (!a) - return -ENOMEM; + if (m->is_gvariant) { + a = message_extend_body(m, 1, size + 1, true); + if (!a) + return -ENOMEM; + + *s = a; + } else { + a = message_extend_body(m, 4, 4 + size + 1, false); + if (!a) + return -ENOMEM; - *(uint32_t*) a = size; - *s = (char*) a + 4; + *(uint32_t*) a = size; + *s = (char*) a + 4; + } (*s)[size] = 0; @@ -1401,26 +1528,23 @@ static int bus_message_open_array( sd_bus_message *m, struct bus_container *c, const char *contents, - uint32_t **array_size) { + uint32_t **array_size, + size_t *begin, + bool *need_offsets) { unsigned nindex; - void *a, *op; - int alignment; - size_t os; - struct bus_body_part *o; + int alignment, r; assert(m); assert(c); assert(contents); assert(array_size); + assert(begin); + assert(need_offsets); if (!signature_is_single(contents, true)) return -EINVAL; - alignment = bus_type_get_alignment(contents[0]); - if (alignment < 0) - return alignment; - if (c->signature && c->signature[c->index]) { /* Verify the existing signature */ @@ -1449,27 +1573,53 @@ static int bus_message_open_array( nindex = e - c->signature; } - a = message_extend_body(m, 4, 4); - if (!a) - return -ENOMEM; + if (m->is_gvariant) { + alignment = bus_gvariant_get_alignment(contents); + if (alignment < 0) + return alignment; - o = m->body_end; - op = m->body_end->data; - os = m->body_end->size; + /* Add alignment padding and add to offset list */ + if (!message_extend_body(m, alignment, 0, false)) + return -ENOMEM; - /* Add alignment between size and first element */ - if (!message_extend_body(m, alignment, 0)) - return -ENOMEM; + r = bus_gvariant_is_fixed_size(contents); + if (r < 0) + return r; + + *begin = m->header->body_size; + *need_offsets = r == 0; + } else { + void *a, *op; + size_t os; + struct bus_body_part *o; + + alignment = bus_type_get_alignment(contents[0]); + if (alignment < 0) + return alignment; + + a = message_extend_body(m, 4, 4, false); + if (!a) + return -ENOMEM; + + o = m->body_end; + op = m->body_end->data; + os = m->body_end->size; + + /* Add alignment between size and first element */ + if (!message_extend_body(m, alignment, 0, false)) + return -ENOMEM; + + /* location of array size might have changed so let's readjust a */ + if (o == m->body_end) + a = adjust_pointer(a, op, os, m->body_end->data); + + *(uint32_t*) a = 0; + *array_size = a; + } if (c->enclosing != SD_BUS_TYPE_ARRAY) c->index = nindex; - /* location of array size might have changed so let's readjust a */ - if (o == m->body_end) - a = adjust_pointer(a, op, os, m->body_end->data); - - *(uint32_t*) a = 0; - *array_size = a; return 0; } @@ -1478,9 +1628,6 @@ static int bus_message_open_variant( struct bus_container *c, const char *contents) { - size_t l; - void *a; - assert(m); assert(c); assert(contents); @@ -1509,13 +1656,24 @@ static int bus_message_open_variant( } } - l = strlen(contents); - a = message_extend_body(m, 1, 1 + l + 1); - if (!a) - return -ENOMEM; + if (m->is_gvariant) { + /* Variants are always aligned to 8 */ + + if (!message_extend_body(m, 8, 0, false)) + return -ENOMEM; + + } else { + size_t l; + void *a; + + l = strlen(contents); + a = message_extend_body(m, 1, 1 + l + 1, false); + if (!a) + return -ENOMEM; - *(uint8_t*) a = l; - memcpy((uint8_t*) a + 1, contents, l + 1); + *(uint8_t*) a = l; + memcpy((uint8_t*) a + 1, contents, l + 1); + } if (c->enclosing != SD_BUS_TYPE_ARRAY) c->index++; @@ -1526,13 +1684,18 @@ static int bus_message_open_variant( static int bus_message_open_struct( sd_bus_message *m, struct bus_container *c, - const char *contents) { + const char *contents, + size_t *begin, + bool *need_offsets) { size_t nindex; + int r; assert(m); assert(c); assert(contents); + assert(begin); + assert(need_offsets); if (!signature_is_valid(contents, false)) return -EINVAL; @@ -1563,9 +1726,27 @@ static int bus_message_open_struct( nindex = e - c->signature; } - /* Align contents to 8 byte boundary */ - if (!message_extend_body(m, 8, 0)) - return -ENOMEM; + if (m->is_gvariant) { + int alignment; + + alignment = bus_gvariant_get_alignment(contents); + if (alignment < 0) + return alignment; + + if (!message_extend_body(m, alignment, 0, false)) + return -ENOMEM; + + r = bus_gvariant_is_fixed_size(contents); + if (r < 0) + return r; + + *begin = m->header->body_size; + *need_offsets = r == 0; + } else { + /* Align contents to 8 byte boundary */ + if (!message_extend_body(m, 8, 0, false)) + return -ENOMEM; + } if (c->enclosing != SD_BUS_TYPE_ARRAY) c->index = nindex; @@ -1576,13 +1757,17 @@ static int bus_message_open_struct( static int bus_message_open_dict_entry( sd_bus_message *m, struct bus_container *c, - const char *contents) { + const char *contents, + size_t *begin, + bool *need_offsets) { - size_t nindex; + int r; assert(m); assert(c); assert(contents); + assert(begin); + assert(need_offsets); if (!signature_is_pair(contents)) return -EINVAL; @@ -1599,17 +1784,30 @@ static int bus_message_open_dict_entry( !startswith(c->signature + c->index + 1, contents) || c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END) return -ENXIO; - - nindex = c->index + 1 + l + 1; } else return -ENXIO; - /* Align contents to 8 byte boundary */ - if (!message_extend_body(m, 8, 0)) - return -ENOMEM; + if (m->is_gvariant) { + int alignment; - if (c->enclosing != SD_BUS_TYPE_ARRAY) - c->index = nindex; + alignment = bus_gvariant_get_alignment(contents); + if (alignment < 0) + return alignment; + + if (!message_extend_body(m, alignment, 0, false)) + return -ENOMEM; + + r = bus_gvariant_is_fixed_size(contents); + if (r < 0) + return r; + + *begin = m->header->body_size; + *need_offsets = r == 0; + } else { + /* Align contents to 8 byte boundary */ + if (!message_extend_body(m, 8, 0, false)) + return -ENOMEM; + } return 0; } @@ -1622,7 +1820,8 @@ _public_ int sd_bus_message_open_container( struct bus_container *c, *w; uint32_t *array_size = NULL; char *signature; - size_t before; + size_t before, begin; + bool need_offsets = false; int r; assert_return(m, -EINVAL); @@ -1653,13 +1852,13 @@ _public_ int sd_bus_message_open_container( before = m->header->body_size; if (type == SD_BUS_TYPE_ARRAY) - r = bus_message_open_array(m, c, contents, &array_size); + r = bus_message_open_array(m, c, contents, &array_size, &begin, &need_offsets); else if (type == SD_BUS_TYPE_VARIANT) r = bus_message_open_variant(m, c, contents); else if (type == SD_BUS_TYPE_STRUCT) - r = bus_message_open_struct(m, c, contents); + r = bus_message_open_struct(m, c, contents, &begin, &need_offsets); else if (type == SD_BUS_TYPE_DICT_ENTRY) - r = bus_message_open_dict_entry(m, c, contents); + r = bus_message_open_dict_entry(m, c, contents, &begin, &need_offsets); else r = -EINVAL; @@ -1675,13 +1874,207 @@ _public_ int sd_bus_message_open_container( w->index = 0; w->array_size = array_size; w->before = before; - w->begin = m->rindex; + w->begin = begin; + w->n_offsets = w->n_allocated = 0; + w->offsets = NULL; + w->need_offsets = need_offsets; + + return 0; +} + +static int bus_message_close_array(sd_bus_message *m, struct bus_container *c) { + + assert(m); + assert(c); + + if (!m->is_gvariant) + return 0; + + if (c->need_offsets) { + uint8_t *a; + unsigned i; + size_t sz; + + /* Variable-width arrays */ + + sz = c->n_offsets > 0 ? c->offsets[c->n_offsets-1] - c->begin : 0; + if (sz + c->n_offsets <= 0xFF) { + a = message_extend_body(m, 1, c->n_offsets, true); + if (!a) + return -ENOMEM; + + for (i = 0; i < c->n_offsets; i++) + a[i] = (uint8_t) (c->offsets[i] - c->begin); + + } else if (sz + c->n_offsets*2 <= 0xFFFF) { + a = message_extend_body(m, 1, c->n_offsets * 2, true); + if (!a) + return -ENOMEM; + + for (i = 0; i < c->n_offsets; i++) { + uint16_t x = (uint16_t) (c->offsets[i] - c->begin); + memcpy(a + (i*2), &x, 2); + } + + } else if (sz + c->n_offsets*4 <= 0xFFFFFFFF) { + a = message_extend_body(m, 1, c->n_offsets * 4, true); + if (!a) + return -ENOMEM; + + for (i = 0; i < c->n_offsets; i++) { + uint32_t x = (uint32_t) (c->offsets[i] - c->begin); + memcpy(a + (i*4), &x, 4); + } + } else { + a = message_extend_body(m, 1, c->n_offsets * 8, true); + if (!a) + return -ENOMEM; + + for (i = 0; i < c->n_offsets; i++) { + uint64_t x = (uint64_t) (c->offsets[i] - c->begin); + memcpy(a + (i*8), &x, 8); + } + } + + } else { + void *a; + + /* Fixed-width or empty arrays */ + + a = message_extend_body(m, 1, 0, true); /* let's add offset to parent */ + if (!a) + return -ENOMEM; + } + + return 0; +} + +static int bus_message_close_variant(sd_bus_message *m, struct bus_container *c) { + uint8_t *a; + size_t l; + + assert(m); + assert(c); + + if (!m->is_gvariant) + return 0; + + l = strlen(c->signature); + + a = message_extend_body(m, 1, 1 + l, true); + if (!a) + return -ENOMEM; + + a[0] = 0; + memcpy(a+1, c->signature, l); + + return 0; +} + +static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c, bool add_offset) { + size_t n_variable = 0; + unsigned i = 0; + const char *p; + uint8_t *a; + int r; + + assert(m); + assert(c); + + if (!m->is_gvariant) + return 0; + + p = c->signature; + while (*p != 0) { + size_t n; + + r = signature_element_length(p, &n); + if (r < 0) + return r; + else { + char t[n+1]; + + memcpy(t, p, n); + t[n] = 0; + + r = bus_gvariant_is_fixed_size(t); + if (r < 0) + return r; + } + + assert(i <= c->n_offsets); + + /* We need to add an offset for each item that has a + * variable size and that is not the last one in the + * list */ + if (r == 0 && p[n] != 0) + n_variable++; + else + c->offsets[i] = (size_t) -1; + + i++; + p += n; + } + + if (n_variable <= 0) { + a = message_extend_body(m, 1, 0, true); + if (!a) + return -ENOMEM; + } else { + size_t sz, z; + unsigned j; + + assert(c->offsets[c->n_offsets-1] == m->header->body_size || + c->offsets[c->n_offsets-1] == (size_t) -1); + + sz = m->header->body_size - c->begin; + if (sz + n_variable <= 0xFF) + z = 1; + else if (sz + (n_variable)*2 <= 0xFFFF) + z = 2; + else if (sz + (n_variable)*4 <= 0xFFFFFFFF) + z = 4; + else + z = 8; + + a = message_extend_body(m, 1, z * n_variable, add_offset); + if (!a) + return -ENOMEM; + + for (i = 0, j = 0; i < c->n_offsets; i++) { + size_t v; + unsigned k; + + if (c->offsets[i] == (size_t) -1) + continue; + + v = c->offsets[i] - c->begin; + k = n_variable - 1 - j; + + if (z == 1) + ((uint8_t*) a)[k] = (uint8_t) v; + else if (z == 2) { + uint16_t x = (uint16_t) v; + memcpy(a + k * 2, &x, 2); + } else if (z == 4) { + uint32_t x = (uint32_t) v; + memcpy(a + k * 4, &x, 4); + } else if (z == 8) { + uint64_t x = (uint64_t) v; + memcpy(a + k * 8, &x, 8); + } else + assert_not_reached("Wrong offset width"); + + j++; + } + } return 0; } _public_ int sd_bus_message_close_container(sd_bus_message *m) { struct bus_container *c; + int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); @@ -1689,14 +2082,24 @@ _public_ int sd_bus_message_close_container(sd_bus_message *m) { assert_return(!m->poisoned, -ESTALE); c = message_get_container(m); + if (c->enclosing != SD_BUS_TYPE_ARRAY) if (c->signature && c->signature[c->index] != 0) return -EINVAL; - free(c->signature); m->n_containers--; - return 0; + if (c->enclosing == SD_BUS_TYPE_ARRAY) + r = bus_message_close_array(m, c); + else if (c->enclosing == SD_BUS_TYPE_VARIANT) + r = bus_message_close_variant(m, c); + else if (c->enclosing == SD_BUS_TYPE_STRUCT || c->enclosing == SD_BUS_TYPE_DICT_ENTRY) + r = bus_message_close_struct(m, c, true); + else + assert_not_reached("Unknown container type"); + + free(c->signature); + return r; } typedef struct { @@ -1951,10 +2354,12 @@ _public_ int sd_bus_message_append(sd_bus_message *m, const char *types, ...) { return r; } -_public_ int sd_bus_message_append_array_space(sd_bus_message *m, - char type, - size_t size, - void **ptr) { +_public_ int sd_bus_message_append_array_space( + sd_bus_message *m, + char type, + size_t size, + void **ptr) { + ssize_t align, sz; void *a; int r; @@ -1978,7 +2383,7 @@ _public_ int sd_bus_message_append_array_space(sd_bus_message *m, if (r < 0) return r; - a = message_extend_body(m, align, size); + a = message_extend_body(m, align, size, false); if (!a) return -ENOMEM; @@ -2098,7 +2503,7 @@ _public_ int sd_bus_message_append_array_memfd(sd_bus_message *m, if (r < 0) return r; - a = message_extend_body(m, align, 0); + a = message_extend_body(m, align, 0, false); if (!a) return -ENOMEM; @@ -2169,11 +2574,13 @@ _public_ int sd_bus_message_append_string_memfd(sd_bus_message *m, sd_memfd *mem } } - a = message_extend_body(m, 4, 4); - if (!a) - return -ENOMEM; + if (!m->is_gvariant) { + a = message_extend_body(m, 4, 4, false); + if (!a) + return -ENOMEM; - *(uint32_t*) a = size - 1; + *(uint32_t*) a = size - 1; + } part = message_append_part(m); if (!part) @@ -2184,8 +2591,16 @@ _public_ int sd_bus_message_append_string_memfd(sd_bus_message *m, sd_memfd *mem part->size = size; copy_fd = -1; - message_extend_containers(m, size); m->header->body_size += size; + message_extend_containers(m, size); + + if (m->is_gvariant) { + r = message_add_offset(m, m->header->body_size); + if (r < 0) { + m->poisoned = true; + return -ENOMEM; + } + } if (c->enclosing != SD_BUS_TYPE_ARRAY) c->index++; @@ -3946,6 +4361,52 @@ int bus_message_parse_fields(sd_bus_message *m) { return 0; } +static int bus_message_close_header(sd_bus_message *m) { + uint8_t *a; + size_t z, i; + + assert(m); + + if (!m->is_gvariant) + return 0; + + if (m->n_header_offsets <= 1) + return 0; + + assert(m->header->fields_size == m->header_offsets[m->n_header_offsets-1]); + + if (m->header->fields_size + m->n_header_offsets <= 0xFF) + z = 1; + else if (m->header->fields_size + 2*m->n_header_offsets <= 0xFFFF) + z = 2; + else if (m->header->fields_size + 4*m->n_header_offsets <= 0xFFFFFFFF) + z = 4; + else + z = 8; + + a = message_extend_fields(m, 1, z * m->n_header_offsets, false); + if (!a) + return -ENOMEM; + + for (i = 0; i < m->n_header_offsets; i++) { + if (z == 1) + ((uint8_t*) a)[i] = (uint8_t) m->header_offsets[i]; + else if (z == 2) { + uint16_t x = (uint16_t) m->header_offsets[i]; + memcpy(a + 2*i, &x, 2); + } else if (z == 4) { + uint32_t x = (uint32_t) m->header_offsets[i]; + memcpy(a + 4*i, &x, 4); + } else if (z == 8) { + uint64_t x = (uint64_t) m->header_offsets[i]; + memcpy(a + 8*i, &x, 8); + } else + assert_not_reached("unknown type"); + } + + return 0; +} + int bus_message_seal(sd_bus_message *m, uint64_t serial) { struct bus_body_part *part; size_t l, a; @@ -3970,6 +4431,11 @@ int bus_message_seal(sd_bus_message *m, uint64_t serial) { !streq(strempty(m->root_container.signature), m->enforced_reply_signature)) return -ENOMSG; + /* If gvariant marshalling is used we need to close the body structure */ + r = bus_message_close_struct(m, &m->root_container, false); + if (r < 0) + return r; + /* If there's a non-trivial signature set, then add it in here */ if (!isempty(m->root_container.signature)) { r = message_append_field_signature(m, BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL); @@ -3983,6 +4449,10 @@ int bus_message_seal(sd_bus_message *m, uint64_t serial) { return r; } + r = bus_message_close_header(m); + if (r < 0) + return r; + /* Add padding at the end of the fields part, since we know * the body needs to start at an 8 byte alignment. We made * sure we allocated enough space for this, so all we need to diff --git a/src/libsystemd-bus/bus-message.h b/src/libsystemd-bus/bus-message.h index 55956adf1d..b862cb9443 100644 --- a/src/libsystemd-bus/bus-message.h +++ b/src/libsystemd-bus/bus-message.h @@ -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) diff --git a/src/libsystemd-bus/bus-type.h b/src/libsystemd-bus/bus-type.h index 122628c66b..d7b96b1102 100644 --- a/src/libsystemd-bus/bus-type.h +++ b/src/libsystemd-bus/bus-type.h @@ -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); diff --git a/src/libsystemd-bus/test-bus-gvariant.c b/src/libsystemd-bus/test-bus-gvariant.c new file mode 100644 index 0000000000..a5f8a17503 --- /dev/null +++ b/src/libsystemd-bus/test-bus-gvariant.c @@ -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; +} diff --git a/src/libsystemd-bus/test-bus-marshal.c b/src/libsystemd-bus/test-bus-marshal.c index da072c707f..79d693992a 100644 --- a/src/libsystemd-bus/test-bus-marshal.c +++ b/src/libsystemd-bus/test-bus-marshal.c @@ -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 |