X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-bus%2Fbus-message.c;h=d6888738e1063989594f2bc762146551f16a3925;hb=ff2ea19264b016bd34232cd9c4820c9547413a9a;hp=9b4da3d3d3be90690f971dceec5ff24ab610af69;hpb=18f5b48f3f0679be4d23d0c279d5d780a9fa4e30;p=elogind.git diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c index 9b4da3d3d..d6888738e 100644 --- a/src/libsystemd-bus/bus-message.c +++ b/src/libsystemd-bus/bus-message.c @@ -35,6 +35,7 @@ #include "bus-type.h" #include "bus-signature.h" #include "bus-gvariant.h" +#include "bus-util.h" static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored); @@ -64,7 +65,7 @@ static void message_free_part(sd_bus_message *m, struct bus_body_part *part) { * can't be sealed yet. */ if (!part->sealed) - bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped); + bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated); else { if (part->mapped > 0) assert_se(munmap(part->data, part->mapped) == 0); @@ -113,7 +114,7 @@ static void message_reset_containers(sd_bus_message *m) { free(m->containers); m->containers = NULL; - m->n_containers = 0; + m->n_containers = m->containers_allocated = 0; m->root_container.index = 0; } @@ -791,7 +792,9 @@ _public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) { } _public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) { - assert_return(m, NULL); + + if (!m) + return NULL; assert(m->n_ref > 0); m->n_ref--; @@ -1046,20 +1049,27 @@ static int part_make_space( return -ENOMEM; if (!part->data && part->memfd < 0) - part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped); + part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped, &part->allocated); if (part->memfd >= 0) { - uint64_t u = sz; - r = ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &u); - if (r < 0) { - m->poisoned = true; - return -errno; + if (part->allocated == 0 || sz > part->allocated) { + uint64_t new_allocated; + + new_allocated = PAGE_ALIGN(sz > 0 ? 2 * sz : 1); + r = ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &new_allocated); + if (r < 0) { + m->poisoned = true; + return -errno; + } + + part->allocated = new_allocated; } if (!part->data || sz > part->mapped) { - size_t psz = PAGE_ALIGN(sz > 0 ? sz : 1); + size_t psz; + psz = PAGE_ALIGN(sz > 0 ? sz : 1); if (part->mapped <= 0) n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0); else @@ -1076,14 +1086,20 @@ static int part_make_space( part->munmap_this = true; } else { - n = realloc(part->data, MAX(sz, 1u)); - if (!n) { - m->poisoned = true; - return -ENOMEM; - } + if (part->allocated == 0 || sz > part->allocated) { + size_t new_allocated; - part->data = n; - part->free_this = true; + new_allocated = sz > 0 ? 2 * sz : 64; + n = realloc(part->data, new_allocated); + if (!n) { + m->poisoned = true; + return -ENOMEM; + } + + part->data = n; + part->allocated = new_allocated; + part->free_this = true; + } } if (q) @@ -1107,7 +1123,7 @@ static int message_add_offset(sd_bus_message *m, size_t offset) { if (!c->need_offsets) return 0; - if (!GREEDY_REALLOC(c->offsets, c->n_offsets_allocated, c->n_offsets + 1)) + if (!GREEDY_REALLOC(c->offsets, c->offsets_allocated, c->n_offsets + 1)) return -ENOMEM; c->offsets[c->n_offsets++] = offset; @@ -1131,9 +1147,7 @@ static void message_extend_containers(sd_bus_message *m, size_t expand) { } 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; + size_t start_body, end_body, padding, added; void *p; int r; @@ -1156,54 +1170,61 @@ static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz, boo return NULL; } - add_new_part = - m->n_body_parts <= 0 || - m->body_end->sealed || - padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size; + if (added > 0) { + struct bus_body_part *part = NULL; + bool add_new_part; + + add_new_part = + m->n_body_parts <= 0 || + m->body_end->sealed || + padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size; + + if (add_new_part) { + if (padding > 0) { + part = message_append_part(m); + if (!part) + return NULL; + + part_zero(part, padding); + } - if (add_new_part) { - if (padding > 0) { part = message_append_part(m); if (!part) return NULL; - part_zero(part, padding); - } + r = part_make_space(m, part, sz, &p); + if (r < 0) + return NULL; + } else { + struct bus_container *c; + void *op; + size_t os, start_part, end_part; - part = message_append_part(m); - if (!part) - return NULL; + part = m->body_end; + op = part->data; + os = part->size; - r = part_make_space(m, part, sz, &p); - if (r < 0) - return NULL; - } else { - struct bus_container *c; - void *op; - size_t os; + start_part = ALIGN_TO(part->size, align); + end_part = start_part + sz; - part = m->body_end; - op = part->data; - os = part->size; + r = part_make_space(m, part, end_part, &p); + if (r < 0) + return NULL; - start_part = ALIGN_TO(part->size, align); - end_part = start_part + sz; + if (padding > 0) { + memset(p, 0, padding); + p = (uint8_t*) p + padding; + } - r = part_make_space(m, part, end_part, &p); - if (r < 0) - return NULL; + /* Readjust pointers */ + for (c = m->containers; c < m->containers + m->n_containers; c++) + c->array_size = adjust_pointer(c->array_size, op, os, part->data); - if (padding > 0) { - memset(p, 0, padding); - p = (uint8_t*) p + padding; + m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data); } - - /* Readjust pointers */ - for (c = m->containers; c < m->containers + m->n_containers; c++) - c->array_size = adjust_pointer(c->array_size, op, os, part->data); - - m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data); - } + } else + /* Return something that is not NULL and is aligned */ + p = (uint8_t *) NULL + align; m->header->body_size = end_body; message_extend_containers(m, added); @@ -1836,14 +1857,11 @@ _public_ int sd_bus_message_open_container( assert_return(!m->poisoned, -ESTALE); /* Make sure we have space for one more container */ - w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1)); - if (!w) { + if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) { m->poisoned = true; return -ENOMEM; } - m->containers = w; - c = message_get_container(m); signature = strdup(contents); @@ -1874,14 +1892,14 @@ _public_ int sd_bus_message_open_container( } /* OK, let's fill it in */ - w += m->n_containers++; + w = m->containers + m->n_containers++; w->enclosing = type; w->signature = signature; w->index = 0; w->array_size = array_size; w->before = before; w->begin = begin; - w->n_offsets = w->n_offsets_allocated = 0; + w->n_offsets = w->offsets_allocated = 0; w->offsets = NULL; w->need_offsets = need_offsets; @@ -1889,9 +1907,7 @@ _public_ int sd_bus_message_open_container( } static size_t determine_word_size(size_t sz, size_t extra) { - if (sz <= 0 && extra == 0) - return 0; - else if (sz + extra <= 0xFF) + if (sz + extra <= 0xFF) return 1; else if (sz + extra*2 <= 0xFFFF) return 2; @@ -2021,7 +2037,7 @@ static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c, if (!BUS_MESSAGE_IS_GVARIANT(m)) return 0; - p = c->signature; + p = strempty(c->signature); while (*p != 0) { size_t n; @@ -2069,7 +2085,7 @@ static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c, if (!a) return -ENOMEM; - p = c->signature; + p = strempty(c->signature); for (i = 0, j = 0; i < c->n_offsets; i++) { unsigned k; size_t n; @@ -2399,10 +2415,12 @@ _public_ int sd_bus_message_append_array_space( assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - assert_return(bus_type_is_trivial(type), -EINVAL); + assert_return(bus_type_is_trivial(type) && type != SD_BUS_TYPE_BOOLEAN, -EINVAL); assert_return(ptr || size == 0, -EINVAL); assert_return(!m->poisoned, -ESTALE); + /* alignment and size of the trivial types (except bool) is + * identical for gvariant and dbus1 marshalling */ align = bus_type_get_alignment(type); sz = bus_type_get_size(type); @@ -2549,8 +2567,8 @@ _public_ int sd_bus_message_append_array_memfd(sd_bus_message *m, part->size = size; copy_fd = -1; - message_extend_containers(m, size); m->header->body_size += size; + message_extend_containers(m, size); return sd_bus_message_close_container(m); } @@ -2688,7 +2706,7 @@ static int bus_message_close_header(sd_bus_message *m) { return 0; } -int bus_message_seal(sd_bus_message *m, uint64_t serial) { +int bus_message_seal(sd_bus_message *m, uint64_t serial, usec_t timeout) { struct bus_body_part *part; size_t l, a; unsigned i; @@ -2735,6 +2753,7 @@ int bus_message_seal(sd_bus_message *m, uint64_t serial) { return r; m->header->serial = serial; + m->timeout = m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED ? 0 : timeout; /* Add padding at the end of the fields part, since we know * the body needs to start at an 8 byte alignment. We made @@ -2751,8 +2770,19 @@ int bus_message_seal(sd_bus_message *m, uint64_t serial) { if (m->destination && m->bus && m->bus->use_memfd) { MESSAGE_FOREACH_PART(part, i, m) if (part->memfd >= 0 && !part->sealed && (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0)) { + uint64_t sz; + + /* Try to seal it if that makes + * sense. First, unmap our own map to + * make sure we don't keep it busy. */ bus_body_part_unmap(part); + /* Then, sync up real memfd size */ + sz = part->size; + if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &sz) < 0) + return -errno; + + /* Finally, try to seal */ if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0) part->sealed = true; } @@ -2964,8 +2994,12 @@ static int container_next_item(sd_bus_message *m, struct bus_container *c, size_ *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment); c->item_size = c->offsets[c->offset_index+1] - *rindex; } else { + + if (c->offset_index+1 >= (c->end-c->begin)/sz) + goto end; + /* Fixed-size array */ - *rindex += sz; + *rindex = c->begin + (c->offset_index+1) * sz; c->item_size = sz; } @@ -3052,7 +3086,7 @@ static int message_peek_body( } part = find_part(m, start, nbytes, (void**) &q); - if (!part || !q) + if (!part || (nbytes > 0 && !q)) return -EBADMSG; *rindex = end; @@ -3549,12 +3583,20 @@ static int build_struct_offsets( int r; assert(m); - assert(signature); assert(item_size); assert(offsets); assert(n_offsets); + if (isempty(signature)) { + *item_size = 0; + *offsets = NULL; + *n_offsets = 0; + return 0; + } + sz = determine_word_size(size, 0); + if (sz <= 0) + return -EBADMSG; /* First, loop over signature and count variable elements and * elements in general. We use this to know how large the @@ -3834,10 +3876,8 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m, if (m->n_containers >= BUS_CONTAINER_DEPTH) return -EBADMSG; - w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1)); - if (!w) + if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) return -ENOMEM; - m->containers = w; if (message_end_of_signature(m)) return -ENXIO; @@ -3872,7 +3912,7 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m, } /* OK, let's fill it in */ - w += m->n_containers++; + w = m->containers + m->n_containers++; w->enclosing = type; w->signature = signature; w->index = 0; @@ -3892,6 +3932,7 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m, _public_ int sd_bus_message_exit_container(sd_bus_message *m) { struct bus_container *c; + unsigned saved; int r; assert_return(m, -EINVAL); @@ -3923,7 +3964,10 @@ _public_ int sd_bus_message_exit_container(sd_bus_message *m) { c = message_get_container(m); + saved = c->index; + c->index = c->saved_index; r = container_next_item(m, c, &m->rindex); + c->index = saved; if (r < 0) return r; @@ -4127,6 +4171,9 @@ _public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) { m->rindex = c->begin; } + c->offset_index = 0; + c->item_size = (c->n_offsets > 0 ? c->offsets[0] : c->end) - c->begin; + return !isempty(c->signature); } @@ -4825,7 +4872,7 @@ int bus_message_parse_fields(sd_bus_message *m) { uint32_t unix_fds = 0; void *offsets = NULL; unsigned n_offsets = 0; - size_t sz; + size_t sz = 0; unsigned i = 0; assert(m); @@ -5059,9 +5106,6 @@ int bus_message_parse_fields(sd_bus_message *m) { if (m->n_fds != unix_fds) return -EBADMSG; - if (isempty(m->root_container.signature) != (BUS_MESSAGE_BODY_SIZE(m) == 0)) - return -EBADMSG; - switch (m->header->type) { case SD_BUS_MESSAGE_SIGNAL: @@ -5276,7 +5320,7 @@ _public_ const char* sd_bus_message_get_signature(sd_bus_message *m, int complet assert_return(m, NULL); c = complete ? &m->root_container : message_get_container(m); - return c->signature ?: ""; + return strempty(c->signature); } _public_ int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all) { @@ -5388,3 +5432,87 @@ _public_ sd_bus *sd_bus_message_get_bus(sd_bus_message *m) { return m->bus; } + +int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) { + _cleanup_bus_message_unref_ sd_bus_message *n = NULL; + usec_t timeout; + int r; + + assert(bus); + assert(m); + assert(*m); + + switch ((*m)->header->type) { + + case SD_BUS_MESSAGE_SIGNAL: + r = sd_bus_message_new_signal(bus, (*m)->path, (*m)->interface, (*m)->member, &n); + if (r < 0) + return r; + + break; + + case SD_BUS_MESSAGE_METHOD_CALL: + r = sd_bus_message_new_method_call(bus, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member, &n); + if (r < 0) + return r; + + break; + + case SD_BUS_MESSAGE_METHOD_RETURN: + case SD_BUS_MESSAGE_METHOD_ERROR: + + n = message_new(bus, (*m)->header->type); + if (!n) + return -ENOMEM; + + n->reply_serial = (*m)->reply_serial; + r = message_append_field_uint32(n, BUS_MESSAGE_HEADER_REPLY_SERIAL, n->reply_serial); + if (r < 0) + return r; + + if ((*m)->header->type == SD_BUS_MESSAGE_METHOD_ERROR && (*m)->error.name) { + r = message_append_field_string(n, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, (*m)->error.name, &n->error.message); + if (r < 0) + return r; + + n->error._need_free = -1; + } + + break; + + default: + return -EINVAL; + } + + if ((*m)->destination && !n->destination) { + r = message_append_field_string(n, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, (*m)->destination, &n->destination); + if (r < 0) + return r; + } + + if ((*m)->sender && !n->sender) { + r = message_append_field_string(n, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, (*m)->sender, &n->sender); + if (r < 0) + return r; + } + + n->header->flags |= (*m)->header->flags & (BUS_MESSAGE_NO_REPLY_EXPECTED|BUS_MESSAGE_NO_AUTO_START); + + r = sd_bus_message_copy(n, *m, true); + if (r < 0) + return r; + + timeout = (*m)->timeout; + if (timeout == 0 && !((*m)->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)) + timeout = BUS_DEFAULT_TIMEOUT; + + r = bus_message_seal(n, (*m)->header->serial, timeout); + if (r < 0) + return r; + + sd_bus_message_unref(*m); + *m = n; + n = NULL; + + return 0; +}