X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-bus%2Fbus-message.c;h=2cf1a7a538af3b3b0a9090b8d0a289b5e444c1ef;hb=29ddb38fea134c6132e4f2dd608e9da3871eaebe;hp=c1e1c468b81a46016d19359d0488148ae8294f11;hpb=c91cb83c4dbc9a95a0a2a38e7028e0a7f4af3b35;p=elogind.git diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c index c1e1c468b..2cf1a7a53 100644 --- a/src/libsystemd-bus/bus-message.c +++ b/src/libsystemd-bus/bus-message.c @@ -59,17 +59,21 @@ static void message_free_part(sd_bus_message *m, struct bus_body_part *part) { assert(part); if (part->memfd >= 0) { + /* If we can reuse the memfd, try that. For that it + * can't be sealed yet. */ if (!part->sealed) bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped); else { - if (part->size > 0) - assert_se(munmap(part->data, PAGE_ALIGN(part->size)) == 0); + if (part->mapped > 0) + assert_se(munmap(part->data, part->mapped) == 0); close_nointr_nofail(part->memfd); } - } else if (part->free_this) + } else if (part->munmap_this) + munmap(part->data, part->mapped); + else if (part->free_this) free(part->data); if (part != &m->body) @@ -121,17 +125,21 @@ static void message_free(sd_bus_message *m) { if (m->free_kdbus) free(m->kdbus); - if (m->release_kdbus) - ioctl(m->bus->input_fd, KDBUS_CMD_MSG_RELEASE, m->kdbus); + if (m->release_kdbus) { + uint64_t off; - if (m->free_fds) { - close_many(m->fds, m->n_fds); - free(m->fds); + off = (uint8_t *)m->kdbus - (uint8_t *)m->bus->kdbus_buffer; + ioctl(m->bus->input_fd, KDBUS_CMD_MSG_RELEASE, &off); } if (m->bus) sd_bus_unref(m->bus); + if (m->free_fds) { + close_many(m->fds, m->n_fds); + free(m->fds); + } + if (m->iovec != m->iovec_fixed) free(m->iovec); @@ -488,9 +496,13 @@ int sd_bus_message_new_method_call( sd_bus_message *t; int r; - if (!path) + if (destination && !service_name_is_valid(destination)) return -EINVAL; - if (!member) + if (!object_path_is_valid(path)) + return -EINVAL; + if (interface && !interface_name_is_valid(interface)) + return -EINVAL; + if (!member_name_is_valid(member)) return -EINVAL; if (!m) return -EINVAL; @@ -560,7 +572,7 @@ static int message_new_reply( goto fail; if (call->sender) { - r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->sender); + r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, call->sender, &t->destination); if (r < 0) goto fail; } @@ -619,6 +631,95 @@ fail: return r; } +int sd_bus_message_new_method_errorf( + sd_bus *bus, + sd_bus_message *call, + sd_bus_message **m, + const char *name, + const char *format, + ...) { + + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *t; + va_list ap; + int r; + + if (!name) + return -EINVAL; + if (!m) + return -EINVAL; + + r = message_new_reply(bus, call, SD_BUS_MESSAGE_TYPE_METHOD_ERROR, &t); + if (r < 0) + return r; + + r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, name, &t->error.name); + if (r < 0) + goto fail; + + if (format) { + _cleanup_free_ char *message = NULL; + + va_start(ap, format); + r = vasprintf(&message, format, ap); + va_end(ap); + + if (r < 0) { + r = -ENOMEM; + goto fail; + } + + r = message_append_basic(t, SD_BUS_TYPE_STRING, message, (const void**) &t->error.message); + if (r < 0) + goto fail; + } + + *m = t; + return 0; + +fail: + message_free(t); + return r; +} + + +int bus_message_new_synthetic_error( + sd_bus *bus, + uint64_t serial, + const sd_bus_error *e, + sd_bus_message **m) { + + sd_bus_message *t; + int r; + + assert(sd_bus_error_is_set(e)); + assert(m); + + t = message_new(bus, SD_BUS_MESSAGE_TYPE_METHOD_ERROR); + if (!t) + return -ENOMEM; + + t->header->flags |= SD_BUS_MESSAGE_NO_REPLY_EXPECTED; + t->reply_serial = serial; + + r = message_append_field_uint32(t, SD_BUS_MESSAGE_HEADER_REPLY_SERIAL, t->reply_serial); + if (r < 0) + goto fail; + + if (bus && bus->unique_name) { + r = message_append_field_string(t, SD_BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, bus->unique_name, &t->destination); + 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; @@ -1119,8 +1220,13 @@ static void part_zero(struct bus_body_part *part, size_t sz) { assert(sz > 0); assert(sz < 8); - part->data = NULL; + /* All other fields can be left in their defaults */ + assert(!part->data); + assert(part->memfd < 0); + part->size = sz; + part->is_zero = true; + part->sealed = true; } static int part_make_space( @@ -1151,8 +1257,8 @@ static int part_make_space( return -errno; } - if (sz > part->mapped) { - size_t psz = PAGE_ALIGN(sz); + if (!part->data || sz > part->mapped) { + size_t psz = PAGE_ALIGN(sz > 0 ? sz : 1); if (part->mapped <= 0) n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0); @@ -1167,6 +1273,8 @@ static int part_make_space( part->mapped = psz; part->data = n; } + + part->munmap_this = true; } else { n = realloc(part->data, sz); if (!n) { @@ -1185,8 +1293,21 @@ static int part_make_space( return 0; } -static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) { +static void message_extend_containers(sd_bus_message *m, size_t expand) { struct bus_container *c; + + assert(m); + + if (expand <= 0) + return; + + /* Update counters */ + 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) { struct bus_body_part *part = NULL; size_t start_body, end_body, padding, start_part, end_part, added; bool add_new_part; @@ -1234,6 +1355,7 @@ static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) { if (r < 0) return NULL; } else { + struct bus_container *c; void *op; size_t os; @@ -1260,12 +1382,8 @@ static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz) { m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data); } - /* Update counters */ - for (c = m->containers; c < m->containers + m->n_containers; c++) - if (c->array_size) - *c->array_size += added; - m->header->body_size = end_body; + message_extend_containers(m, added); return p; } @@ -1275,7 +1393,6 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void ssize_t align, sz; uint32_t k; void *a; - char *e = NULL; int fd = -1; uint32_t fdi = 0; int r; @@ -1299,6 +1416,8 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void if (c->signature[c->index] != type) return -ENXIO; } else { + char *e; + /* Maybe we can append to the signature? But only if this is the top-level container*/ if (c->enclosing != 0) return -ENXIO; @@ -1431,7 +1550,6 @@ int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p) { int sd_bus_message_append_string_space(sd_bus_message *m, size_t size, char **s) { struct bus_container *c; - char *e; void *a; if (!m) @@ -1451,6 +1569,8 @@ int sd_bus_message_append_string_space(sd_bus_message *m, size_t size, char **s) if (c->signature[c->index] != SD_BUS_TYPE_STRING) return -ENXIO; } else { + char *e; + /* Maybe we can append to the signature? But only if this is the top-level container*/ if (c->enclosing != 0) return -ENXIO; @@ -1462,7 +1582,6 @@ int sd_bus_message_append_string_space(sd_bus_message *m, size_t size, char **s) } } - a = message_extend_body(m, 4, 4 + size + 1); if (!a) return -ENOMEM; @@ -1485,7 +1604,6 @@ static int bus_message_open_array( uint32_t **array_size) { unsigned nindex; - char *e = NULL; void *a, *op; int alignment; size_t os; @@ -1496,7 +1614,7 @@ static int bus_message_open_array( assert(contents); assert(array_size); - if (!signature_is_single(contents)) + if (!signature_is_single(contents, true)) return -EINVAL; alignment = bus_type_get_alignment(contents[0]); @@ -1515,6 +1633,8 @@ static int bus_message_open_array( nindex = c->index + 1 + strlen(contents); } else { + char *e; + if (c->enclosing != 0) return -ENXIO; @@ -1558,7 +1678,6 @@ static int bus_message_open_variant( struct bus_container *c, const char *contents) { - char *e = NULL; size_t l; void *a; @@ -1566,7 +1685,7 @@ static int bus_message_open_variant( assert(c); assert(contents); - if (!signature_is_single(contents)) + if (!signature_is_single(contents, false)) return -EINVAL; if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN) @@ -1578,6 +1697,8 @@ static int bus_message_open_variant( return -ENXIO; } else { + char *e; + if (c->enclosing != 0) return -ENXIO; @@ -1608,7 +1729,6 @@ static int bus_message_open_struct( const char *contents) { size_t nindex; - char *e = NULL; assert(m); assert(c); @@ -1629,6 +1749,8 @@ static int bus_message_open_struct( nindex = c->index + 1 + l + 1; } else { + char *e; + if (c->enclosing != 0) return -ENXIO; @@ -2099,6 +2221,212 @@ int sd_bus_message_append_array(sd_bus_message *m, char type, const void *ptr, s return 0; } +int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, sd_memfd *memfd) { + _cleanup_close_ int copy_fd = -1; + struct bus_body_part *part; + ssize_t align, sz; + uint64_t size; + void *a; + int r; + + if (!m) + return -EINVAL; + if (!memfd) + return -EINVAL; + if (m->sealed) + return -EPERM; + if (!bus_type_is_trivial(type)) + return -EINVAL; + if (m->poisoned) + return -ESTALE; + + r = sd_memfd_set_sealed(memfd, true); + if (r < 0) + return r; + + copy_fd = sd_memfd_dup_fd(memfd); + if (copy_fd < 0) + return copy_fd; + + r = sd_memfd_get_size(memfd, &size); + if (r < 0) + return r; + + align = bus_type_get_alignment(type); + sz = bus_type_get_size(type); + + assert_se(align > 0); + assert_se(sz > 0); + + if (size % sz != 0) + return -EINVAL; + + if (size > (uint64_t) (uint32_t) -1) + return -EINVAL; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); + if (r < 0) + return r; + + a = message_extend_body(m, align, 0); + if (!a) + return -ENOMEM; + + part = message_append_part(m); + if (!part) + return -ENOMEM; + + part->memfd = copy_fd; + part->sealed = true; + part->size = size; + copy_fd = -1; + + message_extend_containers(m, size); + m->header->body_size += size; + + return sd_bus_message_close_container(m); +} + +int sd_bus_message_append_string_memfd(sd_bus_message *m, sd_memfd *memfd) { + _cleanup_close_ int copy_fd = -1; + struct bus_body_part *part; + struct bus_container *c; + uint64_t size; + void *a; + int r; + + if (!m) + return -EINVAL; + if (!memfd) + return -EINVAL; + if (m->sealed) + return -EPERM; + if (m->poisoned) + return -ESTALE; + + r = sd_memfd_set_sealed(memfd, true); + if (r < 0) + return r; + + copy_fd = sd_memfd_dup_fd(memfd); + if (copy_fd < 0) + return copy_fd; + + r = sd_memfd_get_size(memfd, &size); + if (r < 0) + return r; + + /* We require this to be NUL terminated */ + if (size == 0) + return -EINVAL; + + if (size > (uint64_t) (uint32_t) -1) + return -EINVAL; + + c = message_get_container(m); + if (c->signature && c->signature[c->index]) { + /* Container signature is already set */ + + if (c->signature[c->index] != SD_BUS_TYPE_STRING) + return -ENXIO; + } else { + char *e; + + /* Maybe we can append to the signature? But only if this is the top-level container*/ + if (c->enclosing != 0) + return -ENXIO; + + e = strextend(&c->signature, CHAR_TO_STR(SD_BUS_TYPE_STRING), NULL); + if (!e) { + m->poisoned = true; + return -ENOMEM; + } + } + + a = message_extend_body(m, 4, 4); + if (!a) + return -ENOMEM; + + *(uint32_t*) a = size - 1; + + part = message_append_part(m); + if (!part) + return -ENOMEM; + + part->memfd = copy_fd; + part->sealed = true; + part->size = size; + copy_fd = -1; + + message_extend_containers(m, size); + m->header->body_size += size; + + if (c->enclosing != SD_BUS_TYPE_ARRAY) + c->index++; + + return 0; +} + +int bus_body_part_map(struct bus_body_part *part) { + void *p; + size_t psz; + + assert_se(part); + + if (part->data) + return 0; + + if (part->size <= 0) + return 0; + + /* For smaller zero parts (as used for padding) we don't need to map anything... */ + if (part->memfd < 0 && part->is_zero && part->size < 8) { + static const uint8_t zeroes[7] = { }; + part->data = (void*) zeroes; + return 0; + } + + psz = PAGE_ALIGN(part->size); + + if (part->memfd >= 0) + p = mmap(NULL, psz, PROT_READ, MAP_SHARED, part->memfd, 0); + else if (part->is_zero) + p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + else + return -EINVAL; + + if (p == MAP_FAILED) + return -errno; + + part->mapped = psz; + part->data = p; + part->munmap_this = true; + + return 0; +} + +void bus_body_part_unmap(struct bus_body_part *part) { + + assert_se(part); + + if (part->memfd < 0) + return; + + if (!part->data) + return; + + if (!part->munmap_this) + return; + + assert_se(munmap(part->data, part->mapped) == 0); + + part->data = NULL; + part->mapped = 0; + part->munmap_this = false; + + return; +} + static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) { size_t k, start, end; @@ -2139,6 +2467,8 @@ static bool message_end_of_array(sd_bus_message *m, size_t index) { static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t sz, void **p) { struct bus_body_part *part; size_t begin; + int r; + assert(m); if (m->cached_rindex_part && index >= m->cached_rindex_part_begin) { @@ -2154,8 +2484,13 @@ static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t s return NULL; if (index + sz <= begin + part->size) { + + r = bus_body_part_map(part); + if (r < 0) + return NULL; + if (p) - *p = part->data ? (uint8_t*) part->data + index - begin : NULL; + *p = (uint8_t*) part->data + index - begin; m->cached_rindex_part = part; m->cached_rindex_part_begin = begin; @@ -2163,6 +2498,7 @@ static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t s return part; } + begin += part->size; part = part->next; } @@ -2424,7 +2760,7 @@ static int bus_message_enter_array( assert(contents); assert(array_size); - if (!signature_is_single(contents)) + if (!signature_is_single(contents, true)) return -EINVAL; alignment = bus_type_get_alignment(contents[0]); @@ -2478,7 +2814,7 @@ static int bus_message_enter_variant( assert(c); assert(contents); - if (!signature_is_single(contents)) + if (!signature_is_single(contents, false)) return -EINVAL; if (*contents == SD_BUS_TYPE_DICT_ENTRY_BEGIN) @@ -3086,7 +3422,7 @@ int sd_bus_message_read_array(sd_bus_message *m, char type, const void **ptr, si return align; r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type)); - if (r < 0) + if (r <= 0) return r; c = message_get_container(m); @@ -3577,19 +3913,27 @@ int bus_message_seal(sd_bus_message *m, uint64_t serial) { return r; } - /* Add padding at the end, since we know the body - * needs to start at an 8 byte alignment. */ - + /* 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 + * do here is to zero it out. */ l = BUS_MESSAGE_FIELDS_SIZE(m); a = ALIGN8(l) - l; if (a > 0) memset((uint8_t*) BUS_MESSAGE_FIELDS(m) + l, 0, a); - MESSAGE_FOREACH_PART(part, i, m) - if (part->memfd >= 0 && !part->sealed) { - ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1); - part->sealed = true; - } + /* If this is something we can send as memfd, then let's seal + the memfd now. Note that we can send memfds as payload only + for directed messages, and not for broadcasts. */ + 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)) { + bus_body_part_unmap(part); + + if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0) + part->sealed = true; + } + } m->header->serial = serial; m->sealed = true; @@ -3638,7 +3982,8 @@ int bus_message_dump(sd_bus_message *m) { "\treply_serial=%u\n" "\terror.name=%s\n" "\terror.message=%s\n" - "\tsealed=%s\n", + "\tsealed=%s\n" + "\tn_body_parts=%u\n", m, m->n_ref, m->header->endian, @@ -3657,7 +4002,8 @@ int bus_message_dump(sd_bus_message *m) { m->reply_serial, strna(m->error.name), strna(m->error.message), - yes_no(m->sealed)); + yes_no(m->sealed), + m->n_body_parts); if (m->pid != 0) printf("\tpid=%lu\n", (unsigned long) m->pid); @@ -3991,3 +4337,25 @@ int bus_header_message_size(struct bus_header *h, size_t *sum) { *sum = sizeof(struct bus_header) + ALIGN8(fs) + bs; return 0; } + +int bus_message_to_errno(sd_bus_message *m) { + assert(m); + + if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR) + return 0; + + return bus_error_to_errno(&m->error); +} + +int sd_bus_message_get_signature(sd_bus_message *m, int complete, const char **signature) { + struct bus_container *c; + + if (!m) + return -EINVAL; + if (!signature) + return -EINVAL; + + c = complete ? &m->root_container : message_get_container(m); + *signature = c->signature ?: ""; + return 0; +}