mirror of
https://github.com/systemd/systemd.git
synced 2024-10-27 18:55:40 +03:00
sd-bus: change serialization of kdbus messages to qualify in their entirety as gvariant objects
Previously, we only minimally altered the dbus1 framing for kdbus, and while the header and its fields where compliant Gvariant objects, and so was the body, the entire message together was not. As result of discussions with Ryan Lortie this is now changed, so that the messages in there entirely are fully compliant GVariants. This follows the framing description described here: https://wiki.gnome.org/Projects/GLib/GDBus/Version2 Note that this change changes the framing of *all* messages sent via kdbus, this means you have to reboot your kdbus system, after compiling and installing this new version.
This commit is contained in:
parent
ee04388a54
commit
b381de4197
@ -18,11 +18,8 @@ Known differences between dbus1 and kdbus:
|
||||
|
||||
- Serial numbers of synthesized messages are always (uint32_t) -1.
|
||||
|
||||
- The org.freedesktop.DBus "driver" service is not special on
|
||||
kdbus. It is a bus activated service like any other with its own
|
||||
unique name.
|
||||
|
||||
- NameOwnerChanged is a synthetic message, generated locally and not
|
||||
by the driver.
|
||||
by the driver. On dbus1 only the Disconnected message was
|
||||
synthesized like this.
|
||||
|
||||
- There's no standard per-session bus anymore. Only a per-user bus.
|
||||
|
@ -1,8 +1,9 @@
|
||||
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:
|
||||
We stay close to the original dbus1 framing as possible, but make
|
||||
certain changes to adapt for GVariant. dbus1 has the following
|
||||
framing:
|
||||
|
||||
1. A fixed header of "yyyyuu"
|
||||
2. Additional header fields of "a(yv)"
|
||||
@ -20,40 +21,86 @@ The header consists of the following:
|
||||
y Flags
|
||||
y Protocol version, '1'
|
||||
u Length of the body, i.e. the length of part 4 above
|
||||
u Serial number
|
||||
u 32bit Serial number
|
||||
|
||||
= 12 bytes
|
||||
|
||||
This header is then followed by the the fields array, whose first
|
||||
value is a 32bit array size.
|
||||
|
||||
When using GVariant we keep the basic structure in place, only
|
||||
slightly extend the header, and define protocol version '2'. The new
|
||||
slightly alter 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
|
||||
u Reserved, must be 0
|
||||
t 64bit Cookie
|
||||
|
||||
= 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:
|
||||
This is then followed by the GVariant fields array ("a{tv}"), and
|
||||
finally the actual body as variant (v). Putting this altogether a
|
||||
packet on dbus2 hence qualifies as a fully compliant GVariant
|
||||
structure of (yyyyuta{tv}v).
|
||||
|
||||
For details on gvariant, see:
|
||||
|
||||
https://people.gnome.org/~desrt/gvariant-serialisation.pdf
|
||||
|
||||
Regarding the framing of dbus2, also see:
|
||||
|
||||
https://wiki.gnome.org/Projects/GLib/GDBus/Version2
|
||||
|
||||
The first four bytes of the header are defined the same way for dbus1
|
||||
and dbus2. The first bytes contain the endianess field and the
|
||||
protocol version, so that the remainder of the message can be safely
|
||||
made sense of just by looking at the first 32bit.
|
||||
|
||||
Note that the length of the body is no longer included in the header
|
||||
on dbus2! In fact, the message size must be known in advance, from the
|
||||
underlying transport in order to parse dbus2 messages, while it is
|
||||
directly included in dbus1 message headers. This change of semantics
|
||||
is an effect of GVariant's basic design.
|
||||
|
||||
The serial number has been renamed cookie and has been extended from
|
||||
32bit to 64bit. It is recommended to avoid the higher 32bit of the
|
||||
cookie field though, to simplify compatibility with dbus1 peers. Note
|
||||
that not only the cookie/serial field in the fixed header, but also
|
||||
the reply_cookie/reply_serial additional header field has been
|
||||
increased from 32bit to 64bit, too!
|
||||
|
||||
The header field identifiers have been extended from 8bit to
|
||||
64bit. This has been done to simplify things (as kdbus otherwise uses
|
||||
exclusively 64bit types, unless there is a strong reason not to), and
|
||||
has no effect on the serialization size, as due to alignment for each
|
||||
8bit header field identifier 56 bits of padding had to be added.
|
||||
|
||||
Note that the header size changed, due to these changes. However,
|
||||
consider that on dbus1 the beginning of the fields array contains the
|
||||
32bit array size (since that is how arrays are encoded on dbus1),
|
||||
thus, if one considers that size part of the header, instead of the
|
||||
array, the size of the header on dbus1 and dbus2 stays identical, at
|
||||
16 bytes.
|
||||
|
||||
0 4 8 12 16
|
||||
Common: | E | T | F | V | Body Length | Serial | Fields Length |
|
||||
Common: | E | T | F | V | ...
|
||||
|
||||
dbus1: | ... (as above) ... | Fields array ...
|
||||
dbus1: | (as above) | Body Length | Serial | Fields Length | Fields array ...
|
||||
|
||||
gvariant: | ... (as above) ... | Fields Length | Fields array ...
|
||||
gvariant: | (as above) | Reserved | Cookie | Fields array ...
|
||||
|
||||
And that's already it.
|
||||
|
||||
Note: to simplify parsing, valid kdbus/dbus2 messages must include the
|
||||
entire fixed header and additional header fields in a single non-memfd
|
||||
message part. Also, the signature string of the body variant all the
|
||||
way to the end of the message must be in a single non-memfd part
|
||||
too. The parts for this extended header and footer can be the same
|
||||
one, and can also continue any amount of additional body bytes.
|
||||
|
||||
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.
|
||||
|
@ -247,3 +247,63 @@ int bus_gvariant_is_fixed_size(const char *signature) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t bus_gvariant_determine_word_size(size_t sz, size_t extra) {
|
||||
if (sz + extra <= 0xFF)
|
||||
return 1;
|
||||
else if (sz + extra*2 <= 0xFFFF)
|
||||
return 2;
|
||||
else if (sz + extra*4 <= 0xFFFFFFFF)
|
||||
return 4;
|
||||
else
|
||||
return 8;
|
||||
}
|
||||
|
||||
size_t bus_gvariant_read_word_le(void *p, size_t sz) {
|
||||
union {
|
||||
uint16_t u16;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
} x;
|
||||
|
||||
assert(p);
|
||||
|
||||
if (sz == 1)
|
||||
return *(uint8_t*) p;
|
||||
|
||||
memcpy(&x, p, sz);
|
||||
|
||||
if (sz == 2)
|
||||
return le16toh(x.u16);
|
||||
else if (sz == 4)
|
||||
return le32toh(x.u32);
|
||||
else if (sz == 8)
|
||||
return le64toh(x.u64);
|
||||
|
||||
assert_not_reached("unknown word width");
|
||||
}
|
||||
|
||||
void bus_gvariant_write_word_le(void *p, size_t sz, size_t value) {
|
||||
union {
|
||||
uint16_t u16;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
} x;
|
||||
|
||||
assert(p);
|
||||
assert(sz == 8 || (value < (1ULL << (sz*8))));
|
||||
|
||||
if (sz == 1) {
|
||||
*(uint8_t*) p = value;
|
||||
return;
|
||||
} else if (sz == 2)
|
||||
x.u16 = htole16((uint16_t) value);
|
||||
else if (sz == 4)
|
||||
x.u32 = htole32((uint32_t) value);
|
||||
else if (sz == 8)
|
||||
x.u64 = htole64((uint64_t) value);
|
||||
else
|
||||
assert_not_reached("unknown word width");
|
||||
|
||||
memcpy(p, &x, sz);
|
||||
}
|
||||
|
@ -21,6 +21,12 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
int bus_gvariant_get_size(const char *signature) _pure_;
|
||||
int bus_gvariant_get_alignment(const char *signature) _pure_;
|
||||
int bus_gvariant_is_fixed_size(const char *signature) _pure_;
|
||||
|
||||
size_t bus_gvariant_determine_word_size(size_t sz, size_t extra);
|
||||
void bus_gvariant_write_word_le(void *p, size_t sz, size_t value);
|
||||
size_t bus_gvariant_read_word_le(void *p, size_t sz);
|
||||
|
@ -309,7 +309,7 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) {
|
||||
m->kdbus->dst_id = destination ? unique : KDBUS_DST_ID_BROADCAST;
|
||||
|
||||
m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS;
|
||||
m->kdbus->cookie = (uint64_t) m->header->serial;
|
||||
m->kdbus->cookie = m->header->dbus2.cookie;
|
||||
m->kdbus->priority = m->priority;
|
||||
|
||||
if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
|
||||
@ -411,9 +411,12 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
|
||||
struct kdbus_item *d;
|
||||
unsigned n_fds = 0;
|
||||
_cleanup_free_ int *fds = NULL;
|
||||
struct bus_header *h = NULL;
|
||||
size_t total, n_bytes = 0, idx = 0;
|
||||
struct bus_header *header = NULL;
|
||||
void *footer = NULL;
|
||||
size_t header_size = 0, footer_size = 0;
|
||||
size_t n_bytes = 0, idx = 0;
|
||||
const char *destination = NULL, *seclabel = NULL;
|
||||
bool last_was_memfd = false;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
@ -428,21 +431,24 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
|
||||
switch (d->type) {
|
||||
|
||||
case KDBUS_ITEM_PAYLOAD_OFF:
|
||||
if (!h) {
|
||||
h = (struct bus_header *)((uint8_t *)bus->kdbus_buffer + d->vec.offset);
|
||||
|
||||
if (!bus_header_is_complete(h, d->vec.size))
|
||||
return -EBADMSG;
|
||||
if (!header) {
|
||||
header = (struct bus_header *)((uint8_t *) bus->kdbus_buffer + d->vec.offset);
|
||||
header_size = d->vec.size;
|
||||
}
|
||||
|
||||
footer = (uint8_t*) bus->kdbus_buffer + d->vec.offset;
|
||||
footer_size = d->vec.size;
|
||||
|
||||
n_bytes += d->vec.size;
|
||||
last_was_memfd = false;
|
||||
break;
|
||||
|
||||
case KDBUS_ITEM_PAYLOAD_MEMFD:
|
||||
if (!h)
|
||||
if (!header) /* memfd cannot be first part */
|
||||
return -EBADMSG;
|
||||
|
||||
n_bytes += d->memfd.size;
|
||||
last_was_memfd = true;
|
||||
break;
|
||||
|
||||
case KDBUS_ITEM_FDS: {
|
||||
@ -466,23 +472,29 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!h)
|
||||
if (last_was_memfd) /* memfd cannot be last part */
|
||||
return -EBADMSG;
|
||||
|
||||
r = bus_header_message_size(h, &total);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!header)
|
||||
return -EBADMSG;
|
||||
|
||||
if (n_bytes != total)
|
||||
if (header_size < sizeof(struct bus_header))
|
||||
return -EBADMSG;
|
||||
|
||||
/* on kdbus we only speak native endian gvariant, never dbus1
|
||||
* marshalling or reverse endian */
|
||||
if (h->version != 2 ||
|
||||
h->endian != BUS_NATIVE_ENDIAN)
|
||||
if (header->version != 2 ||
|
||||
header->endian != BUS_NATIVE_ENDIAN)
|
||||
return -EPROTOTYPE;
|
||||
|
||||
r = bus_message_from_header(bus, h, sizeof(struct bus_header), fds, n_fds, NULL, seclabel, 0, &m);
|
||||
r = bus_message_from_header(
|
||||
bus,
|
||||
header, header_size,
|
||||
footer, footer_size,
|
||||
n_bytes,
|
||||
fds, n_fds,
|
||||
NULL,
|
||||
seclabel, 0, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -562,10 +574,11 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
|
||||
case KDBUS_ITEM_PIDS:
|
||||
|
||||
/* The PID/TID might be missing, when the data
|
||||
* is faked by some data bus proxy and it
|
||||
* lacks that information about the real
|
||||
* client since SO_PEERCRED is used for
|
||||
* that. */
|
||||
* is faked by a bus proxy and it lacks that
|
||||
* information about the real client (since
|
||||
* SO_PEERCRED is used for that). Also kernel
|
||||
* namespacing might make some of this data
|
||||
* unavailable when untranslatable. */
|
||||
|
||||
if (d->pids.pid > 0) {
|
||||
m->creds.pid = (pid_t) d->pids.pid;
|
||||
@ -581,7 +594,8 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
|
||||
|
||||
case KDBUS_ITEM_CREDS:
|
||||
|
||||
/* EUID/SUID/FSUID/EGID/SGID/FSGID might be missing too (see above). */
|
||||
/* EUID/SUID/FSUID/EGID/SGID/FSGID might be
|
||||
* missing too (see above). */
|
||||
|
||||
if ((uid_t) d->creds.uid != UID_INVALID) {
|
||||
m->creds.uid = (uid_t) d->creds.uid;
|
||||
@ -659,7 +673,6 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
|
||||
goto fail;
|
||||
|
||||
m->creds.cgroup_root = bus->cgroup_root;
|
||||
|
||||
break;
|
||||
|
||||
case KDBUS_ITEM_AUDIT:
|
||||
@ -762,7 +775,7 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) {
|
||||
goto fail;
|
||||
|
||||
/* Refuse messages if kdbus and dbus1 cookie doesn't match up */
|
||||
if ((uint64_t) m->header->serial != k->cookie) {
|
||||
if ((uint64_t) m->header->dbus2.cookie != k->cookie) {
|
||||
r = -EBADMSG;
|
||||
goto fail;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -99,7 +99,18 @@ struct sd_bus_message {
|
||||
bool release_kdbus:1;
|
||||
bool poisoned:1;
|
||||
|
||||
/* The first and last bytes of the message */
|
||||
struct bus_header *header;
|
||||
void *footer;
|
||||
|
||||
/* How many bytes are accessible in the above pointers */
|
||||
size_t header_accessible;
|
||||
size_t footer_accessible;
|
||||
|
||||
size_t fields_size;
|
||||
size_t body_size;
|
||||
size_t user_body_size;
|
||||
|
||||
struct bus_body_part body;
|
||||
struct bus_body_part *body_end;
|
||||
unsigned n_body_parts;
|
||||
@ -112,7 +123,7 @@ struct sd_bus_message {
|
||||
int *fds;
|
||||
|
||||
struct bus_container root_container, *containers;
|
||||
unsigned n_containers;
|
||||
size_t n_containers;
|
||||
size_t containers_allocated;
|
||||
|
||||
struct iovec *iovec;
|
||||
@ -138,7 +149,9 @@ struct sd_bus_message {
|
||||
unsigned n_header_offsets;
|
||||
};
|
||||
|
||||
#define BUS_MESSAGE_NEED_BSWAP(m) ((m)->header->endian != BUS_NATIVE_ENDIAN)
|
||||
static inline bool BUS_MESSAGE_NEED_BSWAP(sd_bus_message *m) {
|
||||
return m->header->endian != BUS_NATIVE_ENDIAN;
|
||||
}
|
||||
|
||||
static inline uint16_t BUS_MESSAGE_BSWAP16(sd_bus_message *m, uint16_t u) {
|
||||
return BUS_MESSAGE_NEED_BSWAP(m) ? bswap_16(u) : u;
|
||||
@ -153,29 +166,23 @@ static inline uint64_t BUS_MESSAGE_BSWAP64(sd_bus_message *m, uint64_t u) {
|
||||
}
|
||||
|
||||
static inline uint64_t BUS_MESSAGE_COOKIE(sd_bus_message *m) {
|
||||
/* Note that we return the serial converted to a 64bit value here */
|
||||
return BUS_MESSAGE_BSWAP32(m, m->header->serial);
|
||||
if (m->header->version == 2)
|
||||
return BUS_MESSAGE_BSWAP64(m, m->header->dbus2.cookie);
|
||||
|
||||
return BUS_MESSAGE_BSWAP32(m, m->header->dbus1.serial);
|
||||
}
|
||||
|
||||
static inline uint32_t BUS_MESSAGE_BODY_SIZE(sd_bus_message *m) {
|
||||
return BUS_MESSAGE_BSWAP32(m, m->header->body_size);
|
||||
}
|
||||
|
||||
static inline uint32_t BUS_MESSAGE_FIELDS_SIZE(sd_bus_message *m) {
|
||||
return BUS_MESSAGE_BSWAP32(m, m->header->fields_size);
|
||||
}
|
||||
|
||||
static inline uint32_t BUS_MESSAGE_SIZE(sd_bus_message *m) {
|
||||
static inline size_t BUS_MESSAGE_SIZE(sd_bus_message *m) {
|
||||
return
|
||||
sizeof(struct bus_header) +
|
||||
ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) +
|
||||
BUS_MESSAGE_BODY_SIZE(m);
|
||||
ALIGN8(m->fields_size) +
|
||||
m->body_size;
|
||||
}
|
||||
|
||||
static inline uint32_t BUS_MESSAGE_BODY_BEGIN(sd_bus_message *m) {
|
||||
static inline size_t BUS_MESSAGE_BODY_BEGIN(sd_bus_message *m) {
|
||||
return
|
||||
sizeof(struct bus_header) +
|
||||
ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m));
|
||||
ALIGN8(m->fields_size);
|
||||
}
|
||||
|
||||
static inline void* BUS_MESSAGE_FIELDS(sd_bus_message *m) {
|
||||
@ -193,7 +200,10 @@ int bus_message_read_strv_extend(sd_bus_message *m, char ***l);
|
||||
int bus_message_from_header(
|
||||
sd_bus *bus,
|
||||
void *header,
|
||||
size_t length,
|
||||
size_t header_accessible,
|
||||
void *footer,
|
||||
size_t footer_accessible,
|
||||
size_t message_size,
|
||||
int *fds,
|
||||
unsigned n_fds,
|
||||
const struct ucred *ucred,
|
||||
@ -217,9 +227,6 @@ int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap);
|
||||
|
||||
int bus_message_parse_fields(sd_bus_message *m);
|
||||
|
||||
bool bus_header_is_complete(struct bus_header *h, size_t size);
|
||||
int bus_header_message_size(struct bus_header *h, size_t *sum);
|
||||
|
||||
struct bus_body_part *message_append_part(sd_bus_message *m);
|
||||
|
||||
#define MESSAGE_FOREACH_PART(part, i, m) \
|
||||
|
@ -23,21 +23,38 @@
|
||||
|
||||
#include <endian.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* Packet header */
|
||||
|
||||
struct bus_header {
|
||||
struct _packed_ bus_header {
|
||||
/* The first four fields are identical for dbus1, and dbus2 */
|
||||
uint8_t endian;
|
||||
uint8_t type;
|
||||
uint8_t flags;
|
||||
uint8_t version;
|
||||
uint32_t body_size;
|
||||
|
||||
/* Note that what the bus spec calls "serial" we'll call
|
||||
"cookie" instead, because we don't want to imply that the
|
||||
cookie was in any way monotonically increasing. */
|
||||
uint32_t serial;
|
||||
uint32_t fields_size;
|
||||
} _packed_;
|
||||
union _packed_ {
|
||||
/* dbus1: Used for SOCK_STREAM connections */
|
||||
struct _packed_ {
|
||||
uint32_t body_size;
|
||||
|
||||
/* Note that what the bus spec calls "serial" we'll call
|
||||
"cookie" instead, because we don't want to imply that the
|
||||
cookie was in any way monotonically increasing. */
|
||||
uint32_t serial;
|
||||
uint32_t fields_size;
|
||||
} dbus1;
|
||||
|
||||
/* dbus2: Used for kdbus connections */
|
||||
struct _packed_ {
|
||||
uint32_t _reserved;
|
||||
uint64_t cookie;
|
||||
} dbus2;
|
||||
|
||||
/* Note that both header versions have the same size! */
|
||||
};
|
||||
};
|
||||
|
||||
/* Endianness */
|
||||
|
||||
|
@ -132,7 +132,7 @@ static void test_bus_gvariant_get_alignment(void) {
|
||||
|
||||
static void test_marshal(void) {
|
||||
_cleanup_bus_message_unref_ sd_bus_message *m = NULL, *n = NULL;
|
||||
_cleanup_bus_unref_ sd_bus *bus = NULL;
|
||||
_cleanup_bus_close_unref_ sd_bus *bus = NULL;
|
||||
_cleanup_free_ void *blob;
|
||||
size_t sz;
|
||||
int r;
|
||||
@ -145,6 +145,8 @@ static void test_marshal(void) {
|
||||
|
||||
assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path/which/is/really/really/long/so/that/we/hit/the/eight/bit/boundary/by/quite/some/margin/to/test/this/stuff/that/it/really/works", "an.interface.name", "AMethodName") >= 0);
|
||||
|
||||
assert_cc(sizeof(struct bus_header) == 16);
|
||||
|
||||
assert_se(sd_bus_message_append(m,
|
||||
"a(usv)", 3,
|
||||
4711, "first-string-parameter", "(st)", "X", (uint64_t) 1111,
|
||||
@ -162,13 +164,15 @@ static void test_marshal(void) {
|
||||
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);
|
||||
v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuta{tv})"), m->header, sizeof(struct bus_header) + m->fields_size, false, NULL, NULL);
|
||||
assert_se(g_variant_is_normal_form(v));
|
||||
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);
|
||||
v = g_variant_new_from_data(G_VARIANT_TYPE("(a(usv))"), m->body.data, m->user_body_size, false, NULL, NULL);
|
||||
assert_se(g_variant_is_normal_form(v));
|
||||
t = g_variant_print(v, TRUE);
|
||||
printf("%s\n", t);
|
||||
g_free(t);
|
||||
@ -180,6 +184,20 @@ static void test_marshal(void) {
|
||||
|
||||
assert_se(bus_message_get_blob(m, &blob, &sz) >= 0);
|
||||
|
||||
#ifdef HAVE_GLIB
|
||||
{
|
||||
GVariant *v;
|
||||
char *t;
|
||||
|
||||
v = g_variant_new_from_data(G_VARIANT_TYPE("(yyyyuta{tv}v)"), blob, sz, false, NULL, NULL);
|
||||
assert_se(g_variant_is_normal_form(v));
|
||||
t = g_variant_print(v, TRUE);
|
||||
printf("%s\n", t);
|
||||
g_free(t);
|
||||
g_variant_unref(v);
|
||||
}
|
||||
#endif
|
||||
|
||||
assert_se(bus_message_from_malloc(bus, blob, sz, NULL, 0, NULL, NULL, &n) >= 0);
|
||||
blob = NULL;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user