summaryrefslogtreecommitdiff
path: root/src/libsystemd-bus/bus-message.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-bus/bus-message.c')
-rw-r--r--src/libsystemd-bus/bus-message.c1413
1 files changed, 1413 insertions, 0 deletions
diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c
new file mode 100644
index 0000000000..4c5588cf8a
--- /dev/null
+++ b/src/libsystemd-bus/bus-message.c
@@ -0,0 +1,1413 @@
+/*-*- 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 <errno.h>
+
+#include "util.h"
+
+#include "bus-message.h"
+#include "sd-bus.h"
+#include "bus-internal.h"
+#include "bus-type.h"
+#include "bus-signature.h"
+
+int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored);
+
+static void message_free(sd_bus_message *m) {
+ unsigned i;
+
+ assert(m);
+
+ if (m->free_header)
+ free(m->header);
+
+ if (m->free_fields)
+ free(m->fields);
+
+ if (m->free_body)
+ free(m->body);
+
+ for (i = 0; i < m->n_fds; i++)
+ close_nointr_nofail(m->fds[i]);
+
+ for (i = 0; i < m->n_containers; i++)
+ free(m->sub_containers[i].signature);
+
+ free(m->sub_containers);
+ free(m);
+}
+
+static void* buffer_extend(void **p, uint32_t *sz, size_t align, size_t extend) {
+ size_t start, n;
+ void *k;
+
+ assert(p);
+ assert(sz);
+ assert(align > 0);
+
+ start = ALIGN_TO((size_t) *sz, align);
+ n = start + extend;
+
+ if (n == *sz)
+ return (uint8_t*) *p + start;
+
+ if (n > (size_t) ((uint32_t) -1))
+ return NULL;
+
+ k = realloc(*p, n);
+ if (!k)
+ return NULL;
+
+ /* Zero out padding */
+ if (start > *sz)
+ memset((uint8_t*) k + *sz, 0, start - *sz);
+
+ *p = k;
+ *sz = n;
+
+ return (uint8_t*) k + start;
+}
+
+static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz) {
+ void *p, *o;
+
+ assert(m);
+
+ o = m->fields;
+ p = buffer_extend(&m->fields, &m->header->fields_size, align, sz);
+ if (!p)
+ return NULL;
+
+ if (o != m->fields) {
+ /* Adjust quick access pointers */
+
+ if (m->path)
+ m->path = (const char*) m->fields + (m->path - (const char*) o);
+ if (m->interface)
+ m->interface = (const char*) m->fields + (m->interface - (const char*) o);
+ if (m->member)
+ m->member = (const char*) m->fields + (m->member - (const char*) o);
+ if (m->destination)
+ m->destination = (const char*) m->fields + (m->destination - (const char*) o);
+ if (m->sender)
+ m->sender = (const char*) m->fields + (m->sender - (const char*) o);
+ if (m->signature)
+ m->signature = (const char*) m->fields + (m->signature - (const char*) o);
+ if (m->error.name)
+ m->error.name = (const char*) m->fields + (m->error.name - (const char*) o);
+ }
+
+ m->free_fields = true;
+
+ return p;
+}
+
+static int message_append_field_string(
+ sd_bus_message *m,
+ uint8_t h,
+ char type,
+ const char *s,
+ const char **ret) {
+
+ size_t l;
+ uint8_t *p;
+
+ assert(m);
+
+ 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;
+
+ 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 = (const char*) p + 8;
+
+ return 0;
+}
+
+static int message_append_field_signature(
+ sd_bus_message *m,
+ uint8_t h,
+ const char *s,
+ const char **ret) {
+
+ size_t l;
+ uint8_t *p;
+
+ assert(m);
+
+ 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;
+
+ 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;
+}
+
+static int message_append_field_uint32(sd_bus_message *m, uint8_t h, uint32_t x) {
+ uint8_t *p;
+
+ assert(m);
+
+ /* field id byte + signature length + signature 'u' + NUL + value */
+ p = message_extend_fields(m, 8, 4 + 4);
+ if (!p)
+ return -ENOMEM;
+
+ p[0] = h;
+ p[1] = 1;
+ p[2] = SD_BUS_TYPE_UINT32;
+ p[3] = 0;
+
+ ((uint32_t*) p)[1] = x;
+
+ return 0;
+}
+
+static sd_bus_message *message_new(sd_bus *bus, uint8_t type) {
+ sd_bus_message *m;
+
+ m = malloc0(ALIGN(sizeof(struct sd_bus_message)) + sizeof(struct bus_header));
+ if (!m)
+ return NULL;
+
+ m->n_ref = 1;
+ m->header = (struct bus_header*) ((uint8_t*) m + ALIGN(sizeof(struct sd_bus_message)));
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+ m->header->endian = SD_BUS_BIG_ENDIAN;
+#else
+ m->header->endian = SD_BUS_LITTLE_ENDIAN;
+#endif
+ m->header->type = type;
+ m->header->version = bus ? bus->message_version : 1;
+
+ return m;
+}
+
+int sd_bus_message_new_signal(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ if (!path)
+ return -EINVAL;
+ if (!interface)
+ return -EINVAL;
+ if (!member)
+ return -EINVAL;
+
+ t = message_new(bus, SD_BUS_MESSAGE_TYPE_SIGNAL);
+ if (!t)
+ return -ENOMEM;
+
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
+ if (r < 0)
+ goto fail;
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
+ if (r < 0)
+ goto fail;
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
+ if (r < 0)
+ goto fail;
+
+ *m = t;
+ return 0;
+
+fail:
+ sd_bus_message_unref(t);
+ return r;
+}
+
+int sd_bus_message_new_method_call(
+ sd_bus *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ if (!path)
+ return -EINVAL;
+ if (!member)
+ return -EINVAL;
+
+ t = message_new(bus, SD_BUS_MESSAGE_TYPE_METHOD_CALL);
+ if (!t)
+ return -ENOMEM;
+
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_PATH, SD_BUS_TYPE_OBJECT_PATH, path, &t->path);
+ if (r < 0)
+ goto fail;
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_MEMBER, SD_BUS_TYPE_STRING, member, &t->member);
+ if (r < 0)
+ goto fail;
+
+ if (interface) {
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_INTERFACE, SD_BUS_TYPE_STRING, interface, &t->interface);
+ if (r < 0)
+ goto fail;
+ }
+
+ if (destination) {
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &t->destination);
+ if (r < 0)
+ goto fail;
+ }
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+int sd_bus_message_new_method_return(
+ sd_bus *bus,
+ sd_bus_message *call,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ if (!call)
+ return -EINVAL;
+ if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EINVAL;
+
+ t = message_new(bus, SD_BUS_MESSAGE_TYPE_METHOD_RETURN);
+ if (!t)
+ return -ENOMEM;
+
+ t->reply_serial = BUS_MESSAGE_SERIAL(call);
+ r = message_append_field_uint32(t, SD_BUS_MESSAGE_HEADER_REPLY_SERIAL, t->reply_serial);
+ if (r < 0)
+ goto fail;
+
+ if (call->sender) {
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->sender);
+ if (r < 0)
+ goto fail;
+ }
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+int sd_bus_message_new_method_error(
+ sd_bus *bus,
+ sd_bus_message *call,
+ const sd_bus_error *e,
+ sd_bus_message **m) {
+
+ sd_bus_message *t;
+ int r;
+
+ if (!call)
+ return -EINVAL;
+ if (call->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EINVAL;
+ if (!e)
+ return -EINVAL;
+ if (!e->name)
+ return -EINVAL;
+
+ t = message_new(bus, SD_BUS_MESSAGE_TYPE_METHOD_ERROR);
+ if (!t)
+ return -ENOMEM;
+
+ t->reply_serial = BUS_MESSAGE_SERIAL(call);
+ r = message_append_field_uint32(t, SD_BUS_MESSAGE_HEADER_REPLY_SERIAL, t->reply_serial);
+ if (r < 0)
+ goto fail;
+
+ if (call->sender) {
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->sender);
+ if (r < 0)
+ goto fail;
+ }
+
+ r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, e->name, &t->error.name);
+ if (r < 0)
+ goto fail;
+
+ if (e->message) {
+ r = message_append_basic(t, SD_BUS_TYPE_STRING, e->message, (const void**) &t->error.message);
+ if (r < 0)
+ goto fail;
+ }
+
+ *m = t;
+ return 0;
+
+fail:
+ message_free(t);
+ return r;
+}
+
+sd_bus_message* sd_bus_message_ref(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ assert(m->n_ref > 0);
+ m->n_ref++;
+
+ return m;
+}
+
+sd_bus_message* sd_bus_message_unref(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ assert(m->n_ref > 0);
+ m->n_ref--;
+
+ if (m->n_ref <= 0)
+ message_free(m);
+
+ return NULL;
+}
+
+int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type) {
+ if (!m)
+ return -EINVAL;
+ if (!type)
+ return -EINVAL;
+
+ *type = m->header->type;
+ return 0;
+}
+
+int sd_bus_message_get_serial(sd_bus_message *m, uint64_t *serial) {
+ if (!m)
+ return -EINVAL;
+ if (!serial)
+ return -EINVAL;
+ if (m->header->serial == 0)
+ return -ENOENT;
+
+ *serial = BUS_MESSAGE_SERIAL(m);
+ return 0;
+}
+
+int sd_bus_message_get_reply_serial(sd_bus_message *m, uint64_t *serial) {
+ if (!m)
+ return -EINVAL;
+ if (!serial)
+ return -EINVAL;
+ if (m->reply_serial == 0)
+ return -ENOENT;
+
+ *serial = m->reply_serial;
+ return 0;
+}
+
+int sd_bus_message_get_no_reply(sd_bus_message *m) {
+ if (!m)
+ return -EINVAL;
+
+ return m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_CALL ? !!(m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) : 0;
+}
+
+const char *sd_bus_message_get_path(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->path;
+}
+
+const char *sd_bus_message_get_interface(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->interface;
+}
+
+const char *sd_bus_message_get_member(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->member;
+}
+const char *sd_bus_message_get_destination(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->destination;
+}
+
+const char *sd_bus_message_get_sender(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ return m->sender;
+}
+
+const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m) {
+ if (!m)
+ return NULL;
+
+ if (!sd_bus_error_is_set(&m->error))
+ return NULL;
+
+ return &m->error;
+}
+
+int sd_bus_message_get_uid(sd_bus_message *m, uid_t *uid) {
+ if (!m)
+ return -EINVAL;
+ if (!m->uid_valid)
+ return -ENOENT;
+
+ *uid = m->uid;
+ return 0;
+}
+
+int sd_bus_message_get_gid(sd_bus_message *m, gid_t *gid) {
+ if (!m)
+ return -EINVAL;
+ if (!m->gid_valid)
+ return -ENOENT;
+
+ *gid = m->gid;
+ return 0;
+}
+
+int sd_bus_message_get_pid(sd_bus_message *m, pid_t *pid) {
+ if (!m)
+ return -EINVAL;
+ if (m->pid <= 0)
+ return -ENOENT;
+
+ *pid = m->pid;
+ return 0;
+}
+
+int sd_bus_message_get_tid(sd_bus_message *m, pid_t *tid) {
+ if (!m)
+ return -EINVAL;
+ if (m->tid <= 0)
+ return -ENOENT;
+
+ *tid = m->tid;
+ return 0;
+}
+
+int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member) {
+ if (!m)
+ return -EINVAL;
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_SIGNAL)
+ return 0;
+
+ if (interface && (!m->interface || !streq(m->interface, interface)))
+ return 0;
+
+ if (member && (!m->member || !streq(m->member, member)))
+ return 0;
+
+ return 1;
+}
+
+int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member) {
+ if (!m)
+ return -EINVAL;
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return 0;
+
+ if (interface && (!m->interface || !streq(m->interface, interface)))
+ return 0;
+
+ if (member && (!m->member || !streq(m->member, member)))
+ return 0;
+
+ return 1;
+}
+
+int sd_bus_message_is_method_error(sd_bus_message *m, const char *name) {
+ if (!m)
+ return -EINVAL;
+
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR)
+ return 0;
+
+ if (name && (!m->error.name || !streq(m->error.name, name)))
+ return 0;
+
+ return 1;
+}
+
+int sd_bus_message_set_no_reply(sd_bus_message *m, int b) {
+ if (!m)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
+ return -EPERM;
+
+ if (b)
+ m->header->flags |= SD_BUS_MESSAGE_NO_REPLY_EXPECTED;
+ else
+ m->header->flags &= ~SD_BUS_MESSAGE_NO_REPLY_EXPECTED;
+
+ return 0;
+}
+
+static struct bus_container *message_get_container(sd_bus_message *m) {
+ assert(m);
+
+ if (m->n_containers == 0)
+ return &m->root_container;
+
+ assert(m->sub_containers);
+ return m->sub_containers + m->n_containers - 1;
+}
+
+static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) {
+ void *p, *o;
+ size_t added;
+ struct bus_container *c;
+
+ assert(m);
+
+ o = m->body;
+ added = m->header->body_size;
+
+ p = buffer_extend(&m->body, &m->header->body_size, align, sz);
+ if (!p)
+ return NULL;
+
+ added = m->header->body_size - added;
+
+ for (c = m->sub_containers; c < m->sub_containers + m->n_containers; c++)
+ if (c->array_size) {
+ c->array_size = (uint32_t*) ((uint8_t*) m->body + ((uint8_t*) c->array_size - (uint8_t*) o));
+ *c->array_size += added;
+ }
+
+ if (o != m->body) {
+ if (m->error.message)
+ m->error.message = (const char*) m->body + (m->error.message - (const char*) o);
+ }
+
+ m->free_body = true;
+
+ return p;
+}
+
+int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored) {
+ struct bus_container *c;
+ size_t sz, align, nindex;
+ uint32_t k;
+ void *a;
+ char *e = NULL;
+
+ if (!m)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (!bus_type_is_basic(type))
+ return -EINVAL;
+
+ c = message_get_container(m);
+
+ if (c->signature && c->signature[c->index]) {
+ /* Container signature is already set */
+
+ if (c->signature[c->index] != type)
+ return -EINVAL;
+ } else {
+ /* Maybe we can append to the signature? But only if this is the top-level container*/
+ if (c->enclosing != 0)
+ return -EINVAL;
+
+ e = strextend(&c->signature, CHAR_TO_STR(type), NULL);
+ if (!e)
+ return -ENOMEM;
+ }
+
+ nindex = c->index + 1;
+
+ switch (type) {
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ align = 4;
+ sz = 4 + strlen(p) + 1;
+ break;
+
+ case SD_BUS_TYPE_SIGNATURE:
+ align = 1;
+ sz = 1 + strlen(p) + 1;
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ align = sz = 4;
+
+ assert_cc(sizeof(int) == sizeof(uint32_t));
+ memcpy(&k, p, 4);
+ k = !!k;
+ p = &k;
+ 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);
+ if (!a) {
+ /* Truncate extended signature again */
+ if (e)
+ c->signature[c->index] = 0;
+
+ 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 (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ return 0;
+}
+
+int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p) {
+ return message_append_basic(m, type, p, NULL);
+}
+
+static int bus_message_open_array(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents,
+ uint32_t **array_size) {
+
+ char *e = NULL;
+ size_t nindex;
+ void *a, *b;
+ int alignment;
+ size_t saved;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+ assert(array_size);
+
+ if (!signature_is_single(contents))
+ 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 */
+
+ if (c->signature[c->index] != SD_BUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ if (!startswith(c->signature + c->index + 1, contents))
+ return -EINVAL;
+
+ nindex = c->index + 1 + strlen(contents);
+ } else {
+ if (c->enclosing != 0)
+ return -EINVAL;
+
+ /* Extend the existing signature */
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_ARRAY), contents, NULL);
+ if (!e)
+ return -ENOMEM;
+
+ nindex = e - c->signature;
+ }
+
+ saved = m->header->body_size;
+ a = message_extend_body(m, 4, 4);
+ if (!a) {
+ /* Truncate extended signature again */
+ if (e)
+ c->signature[c->index] = 0;
+
+ return -ENOMEM;
+ }
+ b = m->body;
+
+ if (!message_extend_body(m, alignment, 0)) {
+ /* Add alignment between size and first element */
+ if (e)
+ c->signature[c->index] = 0;
+
+ m->header->body_size = saved;
+ return -ENOMEM;
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ /* m->body might have changed so let's readjust a */
+ a = (uint8_t*) m->body + ((uint8_t*) a - (uint8_t*) b);
+ *(uint32_t*) a = 0;
+
+ *array_size = a;
+ return 0;
+}
+
+static int bus_message_open_variant(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ char *e = NULL;
+ size_t l, nindex;
+ void *a;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_single(contents))
+ return -EINVAL;
+
+ if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN)
+ return -EINVAL;
+
+ if (c->signature && c->signature[c->index]) {
+
+ if (c->signature[c->index] != SD_BUS_TYPE_VARIANT)
+ return -EINVAL;
+
+ } else {
+ if (c->enclosing != 0)
+ return -EINVAL;
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_VARIANT), NULL);
+ if (!e)
+ return -ENOMEM;
+ }
+
+ nindex = c->index + 1;
+
+ l = strlen(contents);
+ a = message_extend_body(m, 1, 1 + l + 1);
+ if (!a) {
+ /* Truncate extended signature again */
+ if (e)
+ c->signature[c->index] = 0;
+
+ return -ENOMEM;
+ }
+
+ *(uint8_t*) a = l;
+ memcpy((uint8_t*) a + 1, contents, l + 1);
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ return 0;
+}
+
+static int bus_message_open_struct(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ size_t nindex;
+ char *e = NULL;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_valid(contents, false))
+ return -EINVAL;
+
+ if (c->signature && c->signature[c->index]) {
+ size_t l;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_STRUCT_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_STRUCT_END)
+ return -EINVAL;
+
+ nindex = c->index + 1 + l + 1;
+ } else {
+ if (c->enclosing != 0)
+ return -EINVAL;
+
+ e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_BEGIN), contents, CHAR_TO_STR(SD_BUS_TYPE_STRUCT_END), NULL);
+ if (!e)
+ return -ENOMEM;
+
+ nindex = e - c->signature;
+ }
+
+ /* Align contents to 8 byte boundary */
+ if (!message_extend_body(m, 8, 0)) {
+ if (e)
+ c->signature[c->index] = 0;
+
+ return -ENOMEM;
+ }
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ return 0;
+}
+
+static int bus_message_open_dict_entry(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ size_t nindex;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_pair(contents))
+ return -EINVAL;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ if (c->signature && c->signature[c->index]) {
+ size_t l;
+
+ l = strlen(contents);
+
+ if (c->signature[c->index] != SD_BUS_TYPE_DICT_ENTRY_BEGIN ||
+ !startswith(c->signature + c->index + 1, contents) ||
+ c->signature[c->index + 1 + l] != SD_BUS_TYPE_DICT_ENTRY_END)
+ return -EINVAL;
+
+ nindex = c->index + 1 + l + 1;
+ } else
+ return -EINVAL;
+
+ /* Align contents to 8 byte boundary */
+ if (!message_extend_body(m, 8, 0))
+ return -ENOMEM;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index = nindex;
+
+ return 0;
+}
+
+int sd_bus_message_open_container(
+ sd_bus_message *m,
+ char type,
+ const char *contents) {
+
+ struct bus_container *c, *sub;
+ char *signature;
+ uint32_t *array_size = NULL;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (!contents)
+ return -EINVAL;
+
+ /* Make sure we have space for one more container */
+ sub = realloc(m->sub_containers, sizeof(struct bus_container) * (m->n_containers + 1));
+ if (!sub)
+ return -ENOMEM;
+
+ m->sub_containers = sub;
+
+ c = message_get_container(m);
+
+ signature = strdup(contents);
+ if (!signature)
+ return -ENOMEM;
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ r = bus_message_open_array(m, c, contents, &array_size);
+ 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);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ r = bus_message_open_dict_entry(m, c, contents);
+ else
+ r = -EINVAL;
+
+ if (r < 0) {
+ free(signature);
+ return r;
+ }
+
+ /* OK, let's fill it in */
+ sub += m->n_containers++;
+
+ sub->enclosing = type;
+ sub->signature = signature;
+ sub->index = 0;
+ sub->array_size = array_size;
+
+ return 0;
+}
+
+int sd_bus_message_close_container(sd_bus_message *m) {
+ struct bus_container *c;
+
+ if (!m)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (m->n_containers <= 0)
+ return -EINVAL;
+
+ c = message_get_container(m);
+
+ if (!c->signature)
+ return -EINVAL;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ if (c->signature[c->index] != 0)
+ return -EINVAL;
+
+ free(c->signature);
+ m->n_containers--;
+
+ return 0;
+}
+
+static int message_append_ap(
+ sd_bus_message *m,
+ const char *types,
+ va_list ap) {
+
+ const char *t;
+ int r;
+
+ assert(m);
+ assert(types);
+
+ for (t = types; *t; t++) {
+ switch (*t) {
+
+ case SD_BUS_TYPE_BYTE: {
+ uint8_t x;
+
+ x = (uint8_t) va_arg(ap, int);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_BOOLEAN:
+ case SD_BUS_TYPE_INT32:
+ case SD_BUS_TYPE_UINT32: {
+ uint32_t x;
+
+ x = va_arg(ap, uint32_t);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT16:
+ case SD_BUS_TYPE_UINT16: {
+ uint16_t x;
+
+ x = (uint16_t) va_arg(ap, int);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE: {
+ uint64_t x;
+
+ x = va_arg(ap, uint64_t);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE: {
+ const char *x;
+
+ x = va_arg(ap, const char*);
+ r = sd_bus_message_append_basic(m, *t, x);
+ break;
+ }
+
+ case SD_BUS_TYPE_UNIX_FD: {
+ int x;
+
+ x = va_arg(ap, int);
+ r = sd_bus_message_append_basic(m, *t, &x);
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY: {
+ unsigned i, n;
+ size_t k;
+
+ r = signature_element_length(t + 1, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k + 1];
+
+ memcpy(s, t + 1, k);
+ s[k] = 0;
+ t += k;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s);
+ if (r < 0)
+ return r;
+
+ n = va_arg(ap, unsigned);
+
+ for (i = 0; i < n; i++) {
+ r = message_append_ap(m, s, ap);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_close_container(m);
+ }
+
+ break;
+ }
+
+ case SD_BUS_TYPE_VARIANT: {
+ const char *s;
+
+ s = va_arg(ap, const char*);
+ if (!s)
+ return -EINVAL;
+
+ r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, s);
+ if (r < 0)
+ return r;
+
+ r = message_append_ap(m, s, ap);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ break;
+ }
+
+ case SD_BUS_TYPE_STRUCT_BEGIN:
+ case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
+ size_t k;
+
+ r = signature_element_length(t, &k);
+ if (r < 0)
+ return r;
+
+ {
+ char s[k - 1];
+
+ memcpy(s, t + 1, k - 2);
+ s[k - 2] = 0;
+
+ r = sd_bus_message_open_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+ if (r < 0)
+ return r;
+
+ t += k - 1;
+
+ r = message_append_ap(m, s, ap);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_close_container(m);
+ }
+
+ break;
+ }
+
+ default:
+ r = -EINVAL;
+ }
+
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+int sd_bus_message_append(sd_bus_message *m, const char *types, ...) {
+ va_list ap;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (!types)
+ return -EINVAL;
+
+ va_start(ap, types);
+ r = message_append_ap(m, types, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int sd_bus_message_read_type(sd_bus_message *m, char *type, char *element, size_t *length) {
+ return -ENOTSUP;
+}
+
+int sd_bus_message_read_basic(sd_bus_message *m, char type, char element, const void **p, size_t *length) {
+ return -ENOTSUP;
+}
+
+int sd_bus_message_read(sd_bus_message *m, const char *types, ...) {
+ return -ENOTSUP;
+}
+
+int message_parse(sd_bus_message *m) {
+ assert(m);
+
+ if (m->header->version != 1)
+ return -EIO;
+
+ if (m->header->endian != SD_BUS_BIG_ENDIAN &&
+ m->header->endian != SD_BUS_LITTLE_ENDIAN)
+ return -EIO;
+
+ return 0;
+}
+
+static void setup_iovec(sd_bus_message *m) {
+ assert(m);
+ assert(m->sealed);
+
+ m->n_iovec = 0;
+
+ m->iovec[m->n_iovec].iov_base = m->header;
+ m->iovec[m->n_iovec].iov_len = sizeof(*m->header);
+ m->n_iovec++;
+
+ if (m->fields) {
+ m->iovec[m->n_iovec].iov_base = m->fields;
+ m->iovec[m->n_iovec].iov_len = m->header->fields_size;
+ m->n_iovec++;
+
+ if (m->header->fields_size % 8 != 0) {
+ static const uint8_t padding[7] = { 0, 0, 0, 0, 0, 0, 0 };
+
+ m->iovec[m->n_iovec].iov_base = (void*) padding;
+ m->iovec[m->n_iovec].iov_len = 8 - m->header->fields_size % 8;
+ m->n_iovec++;
+ }
+ }
+
+ if (m->body) {
+ m->iovec[m->n_iovec].iov_base = m->body;
+ m->iovec[m->n_iovec].iov_len = m->header->body_size;
+ m->n_iovec++;
+ }
+}
+
+int message_seal(sd_bus_message *m, uint64_t serial) {
+ int r;
+
+ assert(m);
+
+ if (m->sealed)
+ return -EPERM;
+
+ if (m->n_containers > 0)
+ return -EBADMSG;
+
+ /* 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, SD_BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, &m->signature);
+ if (r < 0)
+ return r;
+ }
+
+ if (m->n_fds > 0) {
+ r = message_append_field_uint32(m, SD_BUS_MESSAGE_HEADER_UNIX_FDS, m->n_fds);
+ if (r < 0)
+ return r;
+ }
+
+ m->header->serial = serial;
+ m->sealed = true;
+
+ setup_iovec(m);
+
+ return 0;
+}
+
+int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) {
+ if (!m)
+ return -EINVAL;
+ if (!destination)
+ return -EINVAL;
+ if (m->sealed)
+ return -EPERM;
+ if (m->destination)
+ return -EEXIST;
+
+ return message_append_field_string(m, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination);
+}
+
+void message_dump(sd_bus_message *m) {
+
+ log_info("Message %p\n"
+ "\tn_ref=%u\n"
+ "\tendian=%c\n"
+ "\ttype=%i\n"
+ "\tflags=%u\n"
+ "\tversion=%u\n"
+ "\tserial=%u\n"
+ "\tfields_size=%u\n"
+ "\tbody_size=%u\n"
+ "\tpath=%s\n"
+ "\tinterface=%s\n"
+ "\tmember=%s\n"
+ "\tdestination=%s\n"
+ "\tsender=%s\n"
+ "\tsignature=%s\n"
+ "\treply_serial=%u\n"
+ "\terror.name=%s\n"
+ "\terror.message=%s\n"
+ "\tsealed=%s\n",
+ m,
+ m->n_ref,
+ m->header->endian,
+ m->header->type,
+ m->header->flags,
+ m->header->version,
+ BUS_MESSAGE_SERIAL(m),
+ BUS_MESSAGE_FIELDS_SIZE(m),
+ BUS_MESSAGE_BODY_SIZE(m),
+ strna(m->path),
+ strna(m->interface),
+ strna(m->member),
+ strna(m->destination),
+ strna(m->sender),
+ strna(m->signature),
+ m->reply_serial,
+ strna(m->error.name),
+ strna(m->error.message),
+ yes_no(m->sealed));
+}
+
+int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) {
+ size_t total;
+ unsigned i;
+ void *p, *e;
+
+ assert(m);
+ assert(buffer);
+ assert(sz);
+
+ for (i = 0, total = 0; i < m->n_iovec; i++)
+ total += m->iovec[i].iov_len;
+
+ p = malloc(total);
+ if (!p)
+ return -ENOMEM;
+
+ for (i = 0, e = p; i < m->n_iovec; i++)
+ e = mempcpy(e, m->iovec[i].iov_base, m->iovec[i].iov_len);
+
+ *buffer = p;
+ *sz = total;
+
+ return 0;
+}