| /* |
| * 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 <assert.h> |
| #include <math.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <sys/uio.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <time.h> |
| #include <ffi.h> |
| |
| #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); |
| } |