+static int bus_message_enter_struct(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ size_t l;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_valid(contents, false))
+ return -EINVAL;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ 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 -ENXIO;
+
+ r = message_peek_body(m, &m->rindex, 8, 0, NULL);
+ if (r <= 0)
+ return r;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index += 1 + l + 1;
+
+ return 1;
+}
+
+static int bus_message_enter_dict_entry(
+ sd_bus_message *m,
+ struct bus_container *c,
+ const char *contents) {
+
+ size_t l;
+ int r;
+
+ assert(m);
+ assert(c);
+ assert(contents);
+
+ if (!signature_is_pair(contents))
+ return -EINVAL;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ return -ENXIO;
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ 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 -ENXIO;
+
+ r = message_peek_body(m, &m->rindex, 8, 0, NULL);
+ if (r <= 0)
+ return r;
+
+ if (c->enclosing != SD_BUS_TYPE_ARRAY)
+ c->index += 1 + l + 1;
+
+ return 1;
+}
+
+int sd_bus_message_enter_container(sd_bus_message *m, char type, const char *contents) {
+ struct bus_container *c, *w;
+ uint32_t *array_size = NULL;
+ char *signature;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!m->sealed)
+ return -EPERM;
+ if (!contents)
+ return -EINVAL;
+
+ w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1));
+ if (!w)
+ return -ENOMEM;
+ m->containers = w;
+
+ c = message_get_container(m);
+
+ if (!c->signature || c->signature[c->index] == 0)
+ return 0;
+
+ signature = strdup(contents);
+ if (!signature)
+ return -ENOMEM;
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ r = bus_message_enter_array(m, c, contents, &array_size);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ r = bus_message_enter_variant(m, c, contents);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ r = bus_message_enter_struct(m, c, contents);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ r = bus_message_enter_dict_entry(m, c, contents);
+ else
+ r = -EINVAL;
+
+ if (r <= 0) {
+ free(signature);
+ return r;
+ }
+
+ /* OK, let's fill it in */
+ w += m->n_containers++;
+ w->enclosing = type;
+ w->signature = signature;
+ w->index = 0;
+ w->array_size = array_size;
+ w->begin = m->rindex;
+
+ return 1;
+}
+
+int sd_bus_message_exit_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->enclosing == SD_BUS_TYPE_ARRAY) {
+ uint32_t l;
+
+ l = BUS_MESSAGE_BSWAP32(m, *c->array_size);
+ if (c->begin + l != m->rindex)
+ return -EBUSY;
+
+ } else {
+ if (c->signature && c->signature[c->index] != 0)
+ return -EINVAL;
+ }
+
+ free(c->signature);
+ m->n_containers--;
+
+ return 1;
+}
+
+int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char **contents) {
+ struct bus_container *c;
+ int r;
+
+ if (!m)
+ return -EINVAL;
+ if (!m->sealed)
+ return -EPERM;
+
+ c = message_get_container(m);
+
+ if (!c->signature || c->signature[c->index] == 0)
+ goto eof;
+
+ if (message_end_of_array(m, m->rindex))
+ goto eof;
+
+ if (bus_type_is_basic(c->signature[c->index])) {
+ if (contents)
+ *contents = NULL;
+ if (type)
+ *type = c->signature[c->index];
+ return 1;
+ }
+
+ if (c->signature[c->index] == SD_BUS_TYPE_ARRAY) {
+
+ if (contents) {
+ size_t l;
+ char *sig;
+
+ r = signature_element_length(c->signature+c->index+1, &l);
+ if (r < 0)
+ return r;
+
+ sig = strndup(c->signature + c->index + 1, l);
+ if (!sig)
+ return -ENOMEM;
+
+ free(m->peeked_signature);
+ m->peeked_signature = sig;
+
+ *contents = sig;
+ }
+
+ if (type)
+ *type = SD_BUS_TYPE_ARRAY;
+
+ return 1;
+ }
+
+ if (c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ||
+ c->signature[c->index] == SD_BUS_TYPE_DICT_ENTRY_BEGIN) {
+
+ if (contents) {
+ size_t l;
+ char *sig;
+
+ r = signature_element_length(c->signature+c->index, &l);
+ if (r < 0)
+ return r;
+
+ assert(l >= 2);
+ sig = strndup(c->signature + c->index + 1, l - 2);
+ if (!sig)
+ return -ENOMEM;
+
+ free(m->peeked_signature);
+ m->peeked_signature = sig;
+
+ *contents = sig;
+ }
+
+ if (type)
+ *type = c->signature[c->index] == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY;
+
+ return 1;
+ }
+
+ if (c->signature[c->index] == SD_BUS_TYPE_VARIANT) {
+ if (contents) {
+ size_t rindex, l;
+ void *q;
+
+ rindex = m->rindex;
+ r = message_peek_body(m, &rindex, 1, 1, &q);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ goto eof;
+
+ l = *(uint8_t*) q;
+ r = message_peek_body(m, &rindex, 1, l+1, &q);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EBADMSG;
+
+ if (!validate_signature(q, l))
+ return -EBADMSG;
+
+ *contents = q;
+ }
+
+ if (type)
+ *type = SD_BUS_TYPE_VARIANT;
+
+ return 1;
+ }
+
+ return -EINVAL;
+
+eof:
+ if (type)
+ *type = c->enclosing;
+ if (contents)
+ *contents = NULL;
+ return 0;
+}
+
+int sd_bus_message_rewind(sd_bus_message *m, bool complete) {
+ struct bus_container *c;
+
+ if (!m)
+ return -EINVAL;
+ if (!m->sealed)
+ return -EPERM;
+
+ if (complete) {
+ reset_containers(m);
+ m->rindex = 0;
+ m->root_container.index = 0;
+
+ c = message_get_container(m);
+ } else {
+ c = message_get_container(m);
+
+ c->index = 0;
+ m->rindex = c->begin;
+ }
+
+ return !isempty(c->signature);
+}
+
+static int message_read_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:
+ 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_INT64:
+ case SD_BUS_TYPE_UINT64:
+ case SD_BUS_TYPE_DOUBLE:
+ case SD_BUS_TYPE_STRING:
+ case SD_BUS_TYPE_OBJECT_PATH:
+ case SD_BUS_TYPE_SIGNATURE: {
+ void *p;
+
+ p = va_arg(ap, void*);
+ r = sd_bus_message_read_basic(m, *t, p);
+ break;
+ }
+
+ case SD_BUS_TYPE_ARRAY: {
+ size_t k;
+
+ r = signature_element_length(t + 1, &k);
+ if (r < 0)
+ return r;
+
+ {
+ unsigned i, n;
+ char s[k + 1];
+
+ memcpy(s, t + 1, k);
+ s[k] = 0;
+ t += k;
+
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+
+ n = va_arg(ap, unsigned);
+ for (i = 0; i < n; i++) {
+ r = message_read_ap(m, s, ap);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_bus_message_exit_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_enter_container(m, SD_BUS_TYPE_VARIANT, s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+
+ r = message_read_ap(m, s, ap);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+
+ r = sd_bus_message_exit_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_enter_container(m, *t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+
+ t += k - 1;
+
+ r = message_read_ap(m, s, ap);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+
+ r = sd_bus_message_exit_container(m);
+ }
+
+ break;
+ }
+
+ default:
+ r = -EINVAL;
+ }
+
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENXIO;
+ }
+
+ return 1;
+}
+
+int sd_bus_message_read(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_read_ap(m, types, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int bus_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 bus_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);
+}
+
+int bus_message_dump(sd_bus_message *m) {
+ unsigned level = 1;
+ int r;
+
+ assert(m);
+
+ printf("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));
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0) {
+ log_error("Failed to rewind: %s", strerror(-r));
+ return r;
+ }
+
+ printf("BEGIN_MESSAGE \"%s\" {\n", strempty(m->signature));
+
+ for(;;) {
+ _cleanup_free_ char *prefix = NULL;
+ const char *contents = NULL;
+ char type;
+ union {
+ uint8_t u8;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ uint64_t u64;
+ int64_t s64;
+ double d64;
+ const char *string;
+ int i;
+ } basic;
+
+ r = sd_bus_message_peek_type(m, &type, &contents);
+ if (r < 0) {
+ log_error("Failed to peek type: %s", strerror(-r));
+ return r;
+ }
+ if (r == 0) {
+ if (level <= 1)
+ break;
+
+ r = sd_bus_message_exit_container(m);
+ if (r < 0) {
+ log_error("Failed to exit container: %s", strerror(-r));
+ return r;
+ }
+
+ level--;
+
+ prefix = strrep("\t", level);
+ if (!prefix)
+ return log_oom();
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ printf("%s} END_ARRAY \n", prefix);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ printf("%s} END_VARIANT\n", prefix);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ printf("%s} END_STRUCT\n", prefix);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ printf("%s} END_DICT_ENTRY\n", prefix);
+
+ continue;
+ }
+
+ prefix = strrep("\t", level);
+ if (!prefix)
+ return log_oom();
+
+ if (bus_type_is_container(type) > 0) {
+ r = sd_bus_message_enter_container(m, type, contents);
+ if (r < 0) {
+ log_error("Failed to enter container: %s", strerror(-r));
+ return r;
+ }
+
+ if (type == SD_BUS_TYPE_ARRAY)
+ printf("%sBEGIN_ARRAY \"%s\" {\n", prefix, contents);
+ else if (type == SD_BUS_TYPE_VARIANT)
+ printf("%sBEGIN_VARIANT \"%s\" {\n", prefix, contents);
+ else if (type == SD_BUS_TYPE_STRUCT)
+ printf("%sBEGIN_STRUCT \"%s\" {\n", prefix, contents);
+ else if (type == SD_BUS_TYPE_DICT_ENTRY)
+ printf("%sBEGIN_DICT_ENTRY \"%s\" {\n", prefix, contents);
+
+ level ++;
+
+ continue;
+ }
+
+ r = sd_bus_message_read_basic(m, type, &basic);
+ if (r < 0) {
+ log_error("Failed to get basic: %s", strerror(-r));
+ return r;
+ }
+
+ switch (type) {
+
+ case SD_BUS_TYPE_BYTE:
+ printf("%sBYTE: %u\n", prefix, basic.u8);
+ break;
+
+ case SD_BUS_TYPE_BOOLEAN:
+ printf("%sBOOLEAN: %s\n", prefix, yes_no(basic.i));
+ break;
+
+ case SD_BUS_TYPE_INT16:
+ printf("%sINT16: %i\n", prefix, basic.s16);
+ break;
+
+ case SD_BUS_TYPE_UINT16:
+ printf("%sUINT16: %u\n", prefix, basic.u16);
+ break;
+
+ case SD_BUS_TYPE_INT32:
+ printf("%sINT32: %i\n", prefix, basic.s32);
+ break;
+
+ case SD_BUS_TYPE_UINT32:
+ printf("%sUINT32: %u\n", prefix, basic.u32);
+ break;
+
+ case SD_BUS_TYPE_INT64:
+ printf("%sINT64: %lli\n", prefix, (long long) basic.s64);
+ break;
+
+ case SD_BUS_TYPE_UINT64:
+ printf("%sUINT64: %llu\n", prefix, (unsigned long long) basic.u64);
+ break;
+
+ case SD_BUS_TYPE_DOUBLE:
+ printf("%sDOUBLE: %g\n", prefix, basic.d64);
+ break;
+
+ case SD_BUS_TYPE_STRING:
+ printf("%sSTRING: \"%s\"\n", prefix, basic.string);
+ break;
+
+ case SD_BUS_TYPE_OBJECT_PATH:
+ printf("%sOBJECT_PATH: \"%s\"\n", prefix, basic.string);
+ break;
+
+ case SD_BUS_TYPE_SIGNATURE:
+ printf("%sSIGNATURE: \"%s\"\n", prefix, basic.string);
+ break;
+
+ case SD_BUS_TYPE_UNIX_FD:
+ printf("%sUNIX_FD: %i\n", prefix, basic.i);
+ break;
+
+ default:
+ assert_not_reached("Unknown basic type.");
+ }
+ }
+
+ printf("} END_MESSAGE\n");
+ return 0;