/* * Copyright © 2008 Kristian Høgsberg * Copyright © 2013 Jason Ekstrand * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wayland-util.h" #include "wayland-private.h" #include "wayland-os.h" static inline uint32_t div_roundup(uint32_t n, size_t a) { /* The cast to uint64_t is necessary to prevent overflow when rounding * values close to UINT32_MAX. After the division it is again safe to * cast back to uint32_t. */ return (uint32_t) (((uint64_t) n + (a - 1)) / a); } struct wl_ring_buffer { char *data; size_t head, tail; uint32_t size_bits; uint32_t max_size_bits; /* 0 for unlimited */ }; #define MAX_FDS_OUT 28 #define CLEN (CMSG_LEN(MAX_FDS_OUT * sizeof(int32_t))) struct wl_connection { struct wl_ring_buffer in, out; struct wl_ring_buffer fds_in, fds_out; int fd; int want_flush; }; static inline size_t size_pot(uint32_t size_bits) { assert(size_bits < 8 * sizeof(size_t)); return ((size_t)1) << size_bits; } static size_t ring_buffer_capacity(const struct wl_ring_buffer *b) { return size_pot(b->size_bits); } static size_t ring_buffer_mask(const struct wl_ring_buffer *b, size_t i) { size_t m = ring_buffer_capacity(b) - 1; return i & m; } static int ring_buffer_put(struct wl_ring_buffer *b, const void *data, size_t count) { size_t head, size; if (count == 0) return 0; head = ring_buffer_mask(b, b->head); if (head + count <= ring_buffer_capacity(b)) { memcpy(b->data + head, data, count); } else { size = ring_buffer_capacity(b) - head; memcpy(b->data + head, data, size); memcpy(b->data, (const char *) data + size, count - size); } b->head += count; return 0; } static void ring_buffer_put_iov(struct wl_ring_buffer *b, struct iovec *iov, int *count) { size_t head, tail; head = ring_buffer_mask(b, b->head); tail = ring_buffer_mask(b, b->tail); if (head < tail) { iov[0].iov_base = b->data + head; iov[0].iov_len = tail - head; *count = 1; } else if (tail == 0) { iov[0].iov_base = b->data + head; iov[0].iov_len = ring_buffer_capacity(b) - head; *count = 1; } else { iov[0].iov_base = b->data + head; iov[0].iov_len = ring_buffer_capacity(b) - head; iov[1].iov_base = b->data; iov[1].iov_len = tail; *count = 2; } } static void ring_buffer_get_iov(struct wl_ring_buffer *b, struct iovec *iov, int *count) { size_t head, tail; head = ring_buffer_mask(b, b->head); tail = ring_buffer_mask(b, b->tail); if (tail < head) { iov[0].iov_base = b->data + tail; iov[0].iov_len = head - tail; *count = 1; } else if (head == 0) { iov[0].iov_base = b->data + tail; iov[0].iov_len = ring_buffer_capacity(b) - tail; *count = 1; } else { iov[0].iov_base = b->data + tail; iov[0].iov_len = ring_buffer_capacity(b) - tail; iov[1].iov_base = b->data; iov[1].iov_len = head; *count = 2; } } static void ring_buffer_copy(struct wl_ring_buffer *b, void *data, size_t count) { size_t tail, size; if (count == 0) return; tail = ring_buffer_mask(b, b->tail); if (tail + count <= ring_buffer_capacity(b)) { memcpy(data, b->data + tail, count); } else { size = ring_buffer_capacity(b) - tail; memcpy(data, b->data + tail, size); memcpy((char *) data + size, b->data, count - size); } } static size_t ring_buffer_size(struct wl_ring_buffer *b) { return b->head - b->tail; } static char * ring_buffer_tail(const struct wl_ring_buffer *b) { return b->data + ring_buffer_mask(b, b->tail); } static uint32_t get_max_size_bits_for_size(size_t buffer_size) { uint32_t max_size_bits = WL_BUFFER_DEFAULT_SIZE_POT; /* buffer_size == 0 means unbound buffer size */ if (buffer_size == 0) return 0; while (max_size_bits < 8 * sizeof(size_t) && size_pot(max_size_bits) < buffer_size) max_size_bits++; return max_size_bits; } static int ring_buffer_allocate(struct wl_ring_buffer *b, size_t size_bits) { char *new_data; new_data = calloc(size_pot(size_bits), 1); if (!new_data) return -1; ring_buffer_copy(b, new_data, ring_buffer_size(b)); free(b->data); b->data = new_data; b->size_bits = size_bits; b->head = ring_buffer_size(b); b->tail = 0; return 0; } static size_t ring_buffer_get_bits_for_size(struct wl_ring_buffer *b, size_t net_size) { size_t max_size_bits = get_max_size_bits_for_size(net_size); if (max_size_bits < WL_BUFFER_DEFAULT_SIZE_POT) max_size_bits = WL_BUFFER_DEFAULT_SIZE_POT; if (b->max_size_bits > 0 && max_size_bits > b->max_size_bits) max_size_bits = b->max_size_bits; return max_size_bits; } static bool ring_buffer_is_max_size_reached(struct wl_ring_buffer *b) { size_t net_size = ring_buffer_size(b) + 1; size_t size_bits = ring_buffer_get_bits_for_size(b, net_size); return net_size >= size_pot(size_bits); } static int ring_buffer_ensure_space(struct wl_ring_buffer *b, size_t count) { size_t net_size = ring_buffer_size(b) + count; size_t size_bits = ring_buffer_get_bits_for_size(b, net_size); /* The 'size_bits' value represents the required size (in POT) to store * 'net_size', which depending whether the buffers are bounded or not * might not be sufficient (i.e. we might have reached the maximum size * allowed). */ if (net_size > size_pot(size_bits)) { wl_log("Data too big for buffer (%d + %zd > %zd).\n", ring_buffer_size(b), count, size_pot(size_bits)); errno = E2BIG; return -1; } /* The following test here is a short-cut to avoid reallocating a buffer * of the same size. */ if (size_bits == b->size_bits) return 0; /* Otherwise, we (re)allocate the buffer to match the required size */ return ring_buffer_allocate(b, size_bits); } static void ring_buffer_close_fds(struct wl_ring_buffer *buffer, int32_t count) { int32_t i, *p; size_t size, tail; size = ring_buffer_capacity(buffer); tail = ring_buffer_mask(buffer, buffer->tail); p = (int32_t *) (buffer->data + tail); for (i = 0; i < count; i++) { if (p >= (int32_t *) (buffer->data + size)) p = (int32_t *) buffer->data; close(*p++); } } void wl_connection_set_max_buffer_size(struct wl_connection *connection, size_t max_buffer_size) { uint32_t max_size_bits; max_size_bits = get_max_size_bits_for_size(max_buffer_size); connection->fds_in.max_size_bits = max_size_bits; ring_buffer_ensure_space(&connection->fds_in, 0); connection->fds_out.max_size_bits = max_size_bits; ring_buffer_ensure_space(&connection->fds_out, 0); connection->in.max_size_bits = max_size_bits; ring_buffer_ensure_space(&connection->in, 0); connection->out.max_size_bits = max_size_bits; ring_buffer_ensure_space(&connection->out, 0); } struct wl_connection * wl_connection_create(int fd, size_t max_buffer_size) { struct wl_connection *connection; connection = zalloc(sizeof *connection); if (connection == NULL) return NULL; wl_connection_set_max_buffer_size(connection, max_buffer_size); connection->fd = fd; return connection; } static void close_fds(struct wl_ring_buffer *buffer, int max) { size_t size; int32_t count; size = ring_buffer_size(buffer); if (size == 0) return; count = size / sizeof(int32_t); if (max > 0 && max < count) count = max; ring_buffer_close_fds(buffer, count); size = count * sizeof(int32_t); buffer->tail += size; } void wl_connection_close_fds_in(struct wl_connection *connection, int max) { close_fds(&connection->fds_in, max); } int wl_connection_destroy(struct wl_connection *connection) { int fd = connection->fd; close_fds(&connection->fds_out, -1); free(connection->fds_out.data); free(connection->out.data); close_fds(&connection->fds_in, -1); free(connection->fds_in.data); free(connection->in.data); free(connection); return fd; } void wl_connection_copy(struct wl_connection *connection, void *data, size_t size) { ring_buffer_copy(&connection->in, data, size); } void wl_connection_consume(struct wl_connection *connection, size_t size) { connection->in.tail += size; } static void build_cmsg(struct wl_ring_buffer *buffer, char *data, size_t *clen) { struct cmsghdr *cmsg; size_t size; size = ring_buffer_size(buffer); if (size > MAX_FDS_OUT * sizeof(int32_t)) size = MAX_FDS_OUT * sizeof(int32_t); if (size > 0) { cmsg = (struct cmsghdr *) data; cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(size); ring_buffer_copy(buffer, CMSG_DATA(cmsg), size); *clen = cmsg->cmsg_len; } else { *clen = 0; } } static int decode_cmsg(struct wl_ring_buffer *buffer, struct msghdr *msg) { struct cmsghdr *cmsg; size_t size, i; int overflow = 0; for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) continue; size = cmsg->cmsg_len - CMSG_LEN(0); if (ring_buffer_ensure_space(buffer, size) < 0 || overflow) { overflow = 1; size /= sizeof(int32_t); for (i = 0; i < size; i++) close(((int*)CMSG_DATA(cmsg))[i]); } else if (ring_buffer_put(buffer, CMSG_DATA(cmsg), size) < 0) { return -1; } } if (overflow) { errno = EOVERFLOW; return -1; } return 0; } int wl_connection_flush(struct wl_connection *connection) { struct iovec iov[2]; struct msghdr msg = {0}; char cmsg[CLEN]; int len = 0, count; size_t clen; size_t tail; if (!connection->want_flush) return 0; tail = connection->out.tail; while (ring_buffer_size(&connection->out) > 0) { build_cmsg(&connection->fds_out, cmsg, &clen); if (clen >= CLEN) { /* UNIX domain sockets allows to send file descriptors * using ancillary data. * * As per the UNIX domain sockets man page (man 7 unix), * "at least one byte of real data should be sent when * sending ancillary data". * * This is why we send only a single byte here, to ensure * all file descriptors are sent before the bytes are * cleared out. * * Otherwise This can fail to clear the file descriptors * first if individual messages are allowed to have 224 * (8 bytes * MAX_FDS_OUT = 224) file descriptors . */ iov[0].iov_base = ring_buffer_tail(&connection->out); iov[0].iov_len = 1; count = 1; } else { ring_buffer_get_iov(&connection->out, iov, &count); } msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = count; msg.msg_control = (clen > 0) ? cmsg : NULL; msg.msg_controllen = clen; do { len = sendmsg(connection->fd, &msg, MSG_NOSIGNAL | MSG_DONTWAIT); } while (len == -1 && errno == EINTR); if (len == -1) return -1; close_fds(&connection->fds_out, MAX_FDS_OUT); connection->out.tail += len; } connection->want_flush = 0; return connection->out.head - tail; } uint32_t wl_connection_pending_input(struct wl_connection *connection) { return ring_buffer_size(&connection->in); } int wl_connection_read(struct wl_connection *connection) { struct iovec iov[2]; struct msghdr msg; char cmsg[CLEN]; int len, count, ret; while (1) { int data_size = ring_buffer_size(&connection->in); /* Stop once we've read the max buffer size. */ if (ring_buffer_is_max_size_reached(&connection->in)) return data_size; if (ring_buffer_ensure_space(&connection->in, 1) < 0) return -1; ring_buffer_put_iov(&connection->in, iov, &count); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = count; msg.msg_control = cmsg; msg.msg_controllen = sizeof cmsg; msg.msg_flags = 0; do { len = wl_os_recvmsg_cloexec(connection->fd, &msg, MSG_DONTWAIT); } while (len < 0 && errno == EINTR); if (len == 0) { /* EOF, return previously read data first */ return data_size; } if (len < 0) { if (errno == EAGAIN && data_size > 0) { /* nothing new read, return previously read data */ return data_size; } return len; } ret = decode_cmsg(&connection->fds_in, &msg); if (ret) return -1; connection->in.head += len; } } int wl_connection_write(struct wl_connection *connection, const void *data, size_t count) { if (wl_connection_queue(connection, data, count) < 0) return -1; connection->want_flush = 1; return 0; } int wl_connection_queue(struct wl_connection *connection, const void *data, size_t count) { /* We want to try to flush when the buffer reaches the default maximum * size even if the buffer has been previously expanded. * * Otherwise the larger buffer will cause us to flush less frequently, * which could increase lag. * * We'd like to flush often and get the buffer size back down if possible. */ if (ring_buffer_size(&connection->out) + count > WL_BUFFER_DEFAULT_MAX_SIZE) { connection->want_flush = 1; if (wl_connection_flush(connection) < 0 && errno != EAGAIN) return -1; } if (ring_buffer_ensure_space(&connection->out, count) < 0) return -1; return ring_buffer_put(&connection->out, data, count); } int wl_message_count_arrays(const struct wl_message *message) { int i, arrays; for (i = 0, arrays = 0; message->signature[i]; i++) { if (message->signature[i] == WL_ARG_ARRAY) arrays++; } return arrays; } int wl_connection_get_fd(struct wl_connection *connection) { return connection->fd; } static int wl_connection_put_fd(struct wl_connection *connection, int32_t fd) { if (ring_buffer_size(&connection->fds_out) >= MAX_FDS_OUT * sizeof fd) { connection->want_flush = 1; if (wl_connection_flush(connection) < 0 && errno != EAGAIN) return -1; } if (ring_buffer_ensure_space(&connection->fds_out, sizeof fd) < 0) return -1; return ring_buffer_put(&connection->fds_out, &fd, sizeof fd); } const char * get_next_argument(const char *signature, struct argument_details *details) { details->nullable = 0; for(; *signature; ++signature) { switch(*signature) { case WL_ARG_INT: case WL_ARG_UINT: case WL_ARG_FIXED: case WL_ARG_STRING: case WL_ARG_OBJECT: case WL_ARG_NEW_ID: case WL_ARG_ARRAY: case WL_ARG_FD: details->type = *signature; return signature + 1; case '?': details->nullable = 1; } } details->type = '\0'; return signature; } int arg_count_for_signature(const char *signature) { int count = 0; for(; *signature; ++signature) { switch(*signature) { case WL_ARG_INT: case WL_ARG_UINT: case WL_ARG_FIXED: case WL_ARG_STRING: case WL_ARG_OBJECT: case WL_ARG_NEW_ID: case WL_ARG_ARRAY: case WL_ARG_FD: ++count; } } return count; } int wl_message_get_since(const struct wl_message *message) { int since; since = atoi(message->signature); if (since == 0) since = 1; return since; } void wl_argument_from_va_list(const char *signature, union wl_argument *args, int count, va_list ap) { int i; const char *sig_iter; struct argument_details arg; sig_iter = signature; for (i = 0; i < count; i++) { sig_iter = get_next_argument(sig_iter, &arg); switch(arg.type) { case WL_ARG_INT: args[i].i = va_arg(ap, int32_t); break; case WL_ARG_UINT: args[i].u = va_arg(ap, uint32_t); break; case WL_ARG_FIXED: args[i].f = va_arg(ap, wl_fixed_t); break; case WL_ARG_STRING: args[i].s = va_arg(ap, const char *); break; case WL_ARG_OBJECT: args[i].o = va_arg(ap, struct wl_object *); break; case WL_ARG_NEW_ID: args[i].o = va_arg(ap, struct wl_object *); break; case WL_ARG_ARRAY: args[i].a = va_arg(ap, struct wl_array *); break; case WL_ARG_FD: args[i].h = va_arg(ap, int32_t); break; } } } static void wl_closure_clear_fds(struct wl_closure *closure) { const char *signature = closure->message->signature; struct argument_details arg; int i; for (i = 0; i < closure->count; i++) { signature = get_next_argument(signature, &arg); if (arg.type == WL_ARG_FD) closure->args[i].h = -1; } } static struct wl_closure * wl_closure_init(const struct wl_message *message, uint32_t size, int *num_arrays, union wl_argument *args) { struct wl_closure *closure; int count; count = arg_count_for_signature(message->signature); if (count > WL_CLOSURE_MAX_ARGS) { wl_log("too many args (%d) for %s (signature %s)\n", count, message->name, message->signature); errno = EINVAL; return NULL; } int size_to_allocate; if (size) { *num_arrays = wl_message_count_arrays(message); size_to_allocate = sizeof *closure + size + *num_arrays * sizeof(struct wl_array); } else { size_to_allocate = sizeof *closure; } closure = zalloc(size_to_allocate); if (!closure) { wl_log("could not allocate closure of size (%d) for " "%s (signature %s)\n", size_to_allocate, message->name, message->signature); errno = ENOMEM; return NULL; } if (args) memcpy(closure->args, args, count * sizeof *args); closure->message = message; closure->count = count; /* Set these all to -1 so we can close any that have been * set to a real value during wl_closure_destroy(). * We may have copied a bunch of fds into the closure with * memcpy previously, but those are undup()d client fds * that we would have replaced anyway. */ wl_closure_clear_fds(closure); return closure; } struct wl_closure * wl_closure_marshal(struct wl_object *sender, uint32_t opcode, union wl_argument *args, const struct wl_message *message) { struct wl_closure *closure; struct wl_object *object; int i, count, fd, dup_fd; const char *signature; struct argument_details arg; closure = wl_closure_init(message, 0, NULL, args); if (closure == NULL) return NULL; count = closure->count; signature = message->signature; for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case WL_ARG_FIXED: case WL_ARG_UINT: case WL_ARG_INT: break; case WL_ARG_STRING: if (!arg.nullable && args[i].s == NULL) goto err_null; break; case WL_ARG_OBJECT: if (!arg.nullable && args[i].o == NULL) goto err_null; break; case WL_ARG_NEW_ID: object = args[i].o; if (object == NULL) goto err_null; closure->args[i].n = object ? object->id : 0; break; case WL_ARG_ARRAY: if (args[i].a == NULL) goto err_null; break; case WL_ARG_FD: fd = args[i].h; dup_fd = wl_os_dupfd_cloexec(fd, 0); if (dup_fd < 0) { wl_closure_destroy(closure); wl_log("error marshalling arguments for %s: dup failed: %s\n", message->name, strerror(errno)); return NULL; } closure->args[i].h = dup_fd; break; default: wl_abort("unhandled format code: '%c'\n", arg.type); break; } } closure->sender_id = sender->id; closure->opcode = opcode; return closure; err_null: wl_closure_destroy(closure); wl_log("error marshalling arguments for %s (signature %s): " "null value passed for arg %i\n", message->name, message->signature, i); errno = EINVAL; return NULL; } struct wl_closure * wl_closure_vmarshal(struct wl_object *sender, uint32_t opcode, va_list ap, const struct wl_message *message) { union wl_argument args[WL_CLOSURE_MAX_ARGS]; wl_argument_from_va_list(message->signature, args, WL_CLOSURE_MAX_ARGS, ap); return wl_closure_marshal(sender, opcode, args, message); } struct wl_closure * wl_connection_demarshal(struct wl_connection *connection, uint32_t size, struct wl_map *objects, const struct wl_message *message) { uint32_t *p, *next, *end, length, length_in_u32, id; int fd; char *s; int i, count, num_arrays; const char *signature; struct argument_details arg; struct wl_closure *closure; struct wl_array *array_extra; /* Space for sender_id and opcode */ if (size < 2 * sizeof *p) { wl_log("message too short, invalid header\n"); wl_connection_consume(connection, size); errno = EINVAL; return NULL; } closure = wl_closure_init(message, size, &num_arrays, NULL); if (closure == NULL) { wl_connection_consume(connection, size); return NULL; } count = closure->count; array_extra = closure->extra; p = (uint32_t *)(closure->extra + num_arrays); end = p + size / sizeof *p; wl_connection_copy(connection, p, size); closure->sender_id = *p++; closure->opcode = *p++ & 0x0000ffff; signature = message->signature; for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); if (arg.type != WL_ARG_FD && p + 1 > end) { wl_log("message too short, " "object (%d), message %s(%s)\n", closure->sender_id, message->name, message->signature); errno = EINVAL; goto err; } switch (arg.type) { case WL_ARG_UINT: closure->args[i].u = *p++; break; case WL_ARG_INT: closure->args[i].i = *p++; break; case WL_ARG_FIXED: closure->args[i].f = *p++; break; case WL_ARG_STRING: length = *p++; if (length == 0 && !arg.nullable) { wl_log("NULL string received on non-nullable " "type, message %s(%s)\n", message->name, message->signature); errno = EINVAL; goto err; } if (length == 0) { closure->args[i].s = NULL; break; } length_in_u32 = div_roundup(length, sizeof *p); if ((uint32_t) (end - p) < length_in_u32) { wl_log("message too short, " "object (%d), message %s(%s)\n", closure->sender_id, message->name, message->signature); errno = EINVAL; goto err; } next = p + length_in_u32; s = (char *) p; if (length > 0 && s[length - 1] != '\0') { wl_log("string not nul-terminated, " "message %s(%s)\n", message->name, message->signature); errno = EINVAL; goto err; } closure->args[i].s = s; p = next; break; case WL_ARG_OBJECT: id = *p++; closure->args[i].n = id; if (id == 0 && !arg.nullable) { wl_log("NULL object received on non-nullable " "type, message %s(%s)\n", message->name, message->signature); errno = EINVAL; goto err; } break; case WL_ARG_NEW_ID: id = *p++; closure->args[i].n = id; if (id == 0) { wl_log("NULL new ID received on non-nullable " "type, message %s(%s)\n", message->name, message->signature); errno = EINVAL; goto err; } if (wl_map_reserve_new(objects, id) < 0) { if (errno == EINVAL) { wl_log("not a valid new object id (%u), " "message %s(%s)\n", id, message->name, message->signature); } goto err; } break; case WL_ARG_ARRAY: length = *p++; length_in_u32 = div_roundup(length, sizeof *p); if ((uint32_t) (end - p) < length_in_u32) { wl_log("message too short, " "object (%d), message %s(%s)\n", closure->sender_id, message->name, message->signature); errno = EINVAL; goto err; } next = p + length_in_u32; array_extra->size = length; array_extra->alloc = 0; array_extra->data = p; closure->args[i].a = array_extra++; p = next; break; case WL_ARG_FD: if (connection->fds_in.tail == connection->fds_in.head) { wl_log("file descriptor expected, " "object (%d), message %s(%s)\n", closure->sender_id, message->name, message->signature); errno = EINVAL; goto err; } ring_buffer_copy(&connection->fds_in, &fd, sizeof fd); connection->fds_in.tail += sizeof fd; closure->args[i].h = fd; break; default: wl_abort("unknown type\n"); break; } } wl_connection_consume(connection, size); return closure; err: wl_closure_destroy(closure); wl_connection_consume(connection, size); return NULL; } bool wl_object_is_zombie(struct wl_map *map, uint32_t id) { uint32_t flags; /* Zombie objects only exist on the client side. */ if (map->side == WL_MAP_SERVER_SIDE) return false; /* Zombie objects can only have been created by the client. */ if (id >= WL_SERVER_ID_START) return false; flags = wl_map_lookup_flags(map, id); return !!(flags & WL_MAP_ENTRY_ZOMBIE); } int wl_closure_lookup_objects(struct wl_closure *closure, struct wl_map *objects) { struct wl_object *object; const struct wl_message *message; const char *signature; struct argument_details arg; int i, count; uint32_t id; message = closure->message; signature = message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); if (arg.type != WL_ARG_OBJECT) continue; id = closure->args[i].n; closure->args[i].o = NULL; object = wl_map_lookup(objects, id); if (wl_object_is_zombie(objects, id)) { /* references object we've already * destroyed client side */ object = NULL; } else if (object == NULL && id != 0) { wl_log("unknown object (%u), message %s(%s)\n", id, message->name, message->signature); errno = EINVAL; return -1; } if (object != NULL && message->types[i] != NULL && !wl_interface_equal((object)->interface, message->types[i])) { wl_log("invalid object (%u), type (%s), " "message %s(%s)\n", id, (object)->interface->name, message->name, message->signature); errno = EINVAL; return -1; } closure->args[i].o = object; } return 0; } static void convert_arguments_to_ffi(const char *signature, uint32_t flags, union wl_argument *args, int count, ffi_type **ffi_types, void** ffi_args) { int i; const char *sig_iter; struct argument_details arg; sig_iter = signature; for (i = 0; i < count; i++) { sig_iter = get_next_argument(sig_iter, &arg); switch(arg.type) { case WL_ARG_INT: ffi_types[i] = &ffi_type_sint32; ffi_args[i] = &args[i].i; break; case WL_ARG_UINT: ffi_types[i] = &ffi_type_uint32; ffi_args[i] = &args[i].u; break; case WL_ARG_FIXED: ffi_types[i] = &ffi_type_sint32; ffi_args[i] = &args[i].f; break; case WL_ARG_STRING: ffi_types[i] = &ffi_type_pointer; ffi_args[i] = &args[i].s; break; case WL_ARG_OBJECT: ffi_types[i] = &ffi_type_pointer; ffi_args[i] = &args[i].o; break; case WL_ARG_NEW_ID: if (flags & WL_CLOSURE_INVOKE_CLIENT) { ffi_types[i] = &ffi_type_pointer; ffi_args[i] = &args[i].o; } else { ffi_types[i] = &ffi_type_uint32; ffi_args[i] = &args[i].n; } break; case WL_ARG_ARRAY: ffi_types[i] = &ffi_type_pointer; ffi_args[i] = &args[i].a; break; case WL_ARG_FD: ffi_types[i] = &ffi_type_sint32; ffi_args[i] = &args[i].h; break; default: wl_abort("unknown type\n"); break; } } } void wl_closure_invoke(struct wl_closure *closure, uint32_t flags, struct wl_object *target, uint32_t opcode, void *data) { int count; ffi_cif cif; ffi_type *ffi_types[WL_CLOSURE_MAX_ARGS + 2]; void * ffi_args[WL_CLOSURE_MAX_ARGS + 2]; void (* const *implementation)(void); count = arg_count_for_signature(closure->message->signature); ffi_types[0] = &ffi_type_pointer; ffi_args[0] = &data; ffi_types[1] = &ffi_type_pointer; ffi_args[1] = ⌖ convert_arguments_to_ffi(closure->message->signature, flags, closure->args, count, ffi_types + 2, ffi_args + 2); ffi_prep_cif(&cif, FFI_DEFAULT_ABI, count + 2, &ffi_type_void, ffi_types); implementation = target->implementation; if (!implementation[opcode]) { wl_abort("listener function for opcode %u of %s is NULL\n", opcode, target->interface->name); } ffi_call(&cif, implementation[opcode], NULL, ffi_args); wl_closure_clear_fds(closure); } void wl_closure_dispatch(struct wl_closure *closure, wl_dispatcher_func_t dispatcher, struct wl_object *target, uint32_t opcode) { dispatcher(target->implementation, target, opcode, closure->message, closure->args); wl_closure_clear_fds(closure); } static int copy_fds_to_connection(struct wl_closure *closure, struct wl_connection *connection) { const struct wl_message *message = closure->message; uint32_t i, count; struct argument_details arg; const char *signature = message->signature; int fd; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); if (arg.type != WL_ARG_FD) continue; fd = closure->args[i].h; if (wl_connection_put_fd(connection, fd)) { wl_log("request could not be marshaled: " "can't send file descriptor\n"); return -1; } closure->args[i].h = -1; } return 0; } static uint32_t buffer_size_for_closure(struct wl_closure *closure) { const struct wl_message *message = closure->message; int i, count; struct argument_details arg; const char *signature; uint32_t size, buffer_size = 0; signature = message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); switch (arg.type) { case WL_ARG_FD: break; case WL_ARG_UINT: case WL_ARG_INT: case WL_ARG_FIXED: case WL_ARG_OBJECT: case WL_ARG_NEW_ID: buffer_size++; break; case WL_ARG_STRING: if (closure->args[i].s == NULL) { buffer_size++; break; } size = strlen(closure->args[i].s) + 1; buffer_size += 1 + div_roundup(size, sizeof(uint32_t)); break; case WL_ARG_ARRAY: if (closure->args[i].a == NULL) { buffer_size++; break; } size = closure->args[i].a->size; buffer_size += (1 + div_roundup(size, sizeof(uint32_t))); break; default: break; } } return buffer_size + 2; } static int serialize_closure(struct wl_closure *closure, uint32_t *buffer, size_t buffer_count) { const struct wl_message *message = closure->message; unsigned int i, count, size; uint32_t *p, *end; struct argument_details arg; const char *signature; if (buffer_count < 2) goto overflow; p = buffer + 2; end = buffer + buffer_count; signature = message->signature; count = arg_count_for_signature(signature); for (i = 0; i < count; i++) { signature = get_next_argument(signature, &arg); if (arg.type == WL_ARG_FD) continue; if (p + 1 > end) goto overflow; switch (arg.type) { case WL_ARG_UINT: *p++ = closure->args[i].u; break; case WL_ARG_INT: *p++ = closure->args[i].i; break; case WL_ARG_FIXED: *p++ = closure->args[i].f; break; case WL_ARG_OBJECT: *p++ = closure->args[i].o ? closure->args[i].o->id : 0; break; case WL_ARG_NEW_ID: *p++ = closure->args[i].n; break; case WL_ARG_STRING: if (closure->args[i].s == NULL) { *p++ = 0; break; } size = strlen(closure->args[i].s) + 1; *p++ = size; if (p + div_roundup(size, sizeof *p) > end) goto overflow; memcpy(p, closure->args[i].s, size); p += div_roundup(size, sizeof *p); break; case WL_ARG_ARRAY: if (closure->args[i].a == NULL) { *p++ = 0; break; } size = closure->args[i].a->size; *p++ = size; if (p + div_roundup(size, sizeof *p) > end) goto overflow; if (size != 0) memcpy(p, closure->args[i].a->data, size); p += div_roundup(size, sizeof *p); break; case WL_ARG_FD: break; } } size = (p - buffer) * sizeof *p; buffer[0] = closure->sender_id; buffer[1] = size << 16 | (closure->opcode & 0x0000ffff); return size; overflow: wl_log("serialize_closure overflow for %s (signature %s)\n", message->name, message->signature); errno = ERANGE; return -1; } int wl_closure_send(struct wl_closure *closure, struct wl_connection *connection) { int size; uint32_t buffer_size; uint32_t *buffer; int result; if (copy_fds_to_connection(closure, connection)) return -1; buffer_size = buffer_size_for_closure(closure); buffer = zalloc(buffer_size * sizeof buffer[0]); if (buffer == NULL) { wl_log("wl_closure_send error: buffer allocation failure of " "size %d\n for %s (signature %s)", buffer_size * sizeof buffer[0], closure->message->name, closure->message->signature); return -1; } size = serialize_closure(closure, buffer, buffer_size); if (size < 0) { free(buffer); return -1; } result = wl_connection_write(connection, buffer, size); free(buffer); return result; } int wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection) { int size; uint32_t buffer_size; uint32_t *buffer; int result; if (copy_fds_to_connection(closure, connection)) return -1; buffer_size = buffer_size_for_closure(closure); buffer = malloc(buffer_size * sizeof buffer[0]); if (buffer == NULL) { wl_log("wl_closure_queue error: buffer allocation failure of " "size %d\n for %s (signature %s)", buffer_size * sizeof buffer[0], closure->message->name, closure->message->signature); return -1; } size = serialize_closure(closure, buffer, buffer_size); if (size < 0) { free(buffer); return -1; } result = wl_connection_queue(connection, buffer, size); free(buffer); return result; } void wl_closure_print(struct wl_closure *closure, struct wl_object *target, int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg), const char *queue_name) { int i; struct argument_details arg; const char *signature = closure->message->signature; struct timespec tp; unsigned int time; uint32_t nval; FILE *f; char *buffer; size_t buffer_length; f = open_memstream(&buffer, &buffer_length); if (f == NULL) return; clock_gettime(CLOCK_REALTIME, &tp); time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); fprintf(f, "[%7u.%03u] ", time / 1000, time % 1000); if (queue_name) fprintf(f, "{%s} ", queue_name); fprintf(f, "%s%s%s#%u.%s(", discarded ? "discarded " : "", send ? " -> " : "", target->interface->name, target->id, closure->message->name); for (i = 0; i < closure->count; i++) { signature = get_next_argument(signature, &arg); if (i > 0) fprintf(f, ", "); switch (arg.type) { case WL_ARG_UINT: fprintf(f, "%u", closure->args[i].u); break; case WL_ARG_INT: fprintf(f, "%d", closure->args[i].i); break; case WL_ARG_FIXED: /* The magic number 390625 is 1e8 / 256 */ if (closure->args[i].f >= 0) { fprintf(f, "%d.%08d", closure->args[i].f / 256, 390625 * (closure->args[i].f % 256)); } else { fprintf(f, "-%d.%08d", closure->args[i].f / -256, -390625 * (closure->args[i].f % 256)); } break; case WL_ARG_STRING: if (closure->args[i].s) fprintf(f, "\"%s\"", closure->args[i].s); else fprintf(f, "nil"); break; case WL_ARG_OBJECT: if (closure->args[i].o) fprintf(f, "%s#%u", closure->args[i].o->interface->name, closure->args[i].o->id); else fprintf(f, "nil"); break; case WL_ARG_NEW_ID: if (n_parse) nval = n_parse(&closure->args[i]); else nval = closure->args[i].n; fprintf(f, "new id %s#", (closure->message->types[i]) ? closure->message->types[i]->name : "[unknown]"); if (nval != 0) fprintf(f, "%u", nval); else fprintf(f, "nil"); break; case WL_ARG_ARRAY: fprintf(f, "array[%zu]", closure->args[i].a->size); break; case WL_ARG_FD: fprintf(f, "fd %d", closure->args[i].h); break; } } fprintf(f, ")\n"); if (fclose(f) == 0) { fprintf(stderr, "%s", buffer); free(buffer); } } static int wl_closure_close_fds(struct wl_closure *closure) { int i; struct argument_details arg; const char *signature = closure->message->signature; for (i = 0; i < closure->count; i++) { signature = get_next_argument(signature, &arg); if (arg.type == WL_ARG_FD && closure->args[i].h != -1) close(closure->args[i].h); } return 0; } void wl_closure_destroy(struct wl_closure *closure) { /* wl_closure_destroy has free() semantics */ if (!closure) return; wl_closure_close_fds(closure); free(closure); }