Ejemplo n.º 1
0
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 != 'h')
			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");
			return -1;
		}
	}

	return 0;
}
Ejemplo n.º 2
0
static void
decrease_closure_args_refcount(struct wl_closure *closure)
{
	const char *signature;
	struct argument_details arg;
	int i, count;
	struct wl_proxy *proxy;

	signature = closure->message->signature;
	count = arg_count_for_signature(signature);
	for (i = 0; i < count; i++) {
		signature = get_next_argument(signature, &arg);
		switch (arg.type) {
		case 'n':
		case 'o':
			proxy = (struct wl_proxy *) closure->args[i].o;
			if (proxy) {
				if (proxy->flags & WL_PROXY_FLAG_DESTROYED)
					closure->args[i].o = NULL;

				proxy->refcount--;
				if (!proxy->refcount)
					free(proxy);
			}
			break;
		default:
			break;
		}
	}
}
Ejemplo n.º 3
0
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] = &target;

	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);
}
Ejemplo n.º 4
0
static int
create_proxies(struct wl_proxy *sender, struct wl_closure *closure)
{
	struct wl_proxy *proxy;
	const char *signature;
	struct argument_details arg;
	uint32_t id;
	int i;
	int count;

	signature = closure->message->signature;
	count = arg_count_for_signature(signature);
	for (i = 0; i < count; i++) {
		signature = get_next_argument(signature, &arg);
		switch (arg.type) {
		case 'n':
			id = closure->args[i].n;
			if (id == 0) {
				closure->args[i].o = NULL;
				break;
			}
			proxy = wl_proxy_create_for_id(sender, id,
						       closure->message->types[i]);
			if (proxy == NULL)
				return -1;
			closure->args[i].o = (struct wl_object *)proxy;
			break;
		default:
			break;
		}
	}

	return 0;
}
Ejemplo n.º 5
0
static struct wl_proxy *
create_outgoing_proxy(struct wl_proxy *proxy, const struct wl_message *message,
		      union wl_argument *args,
		      const struct wl_interface *interface)
{
	int i, count;
	const char *signature;
	struct argument_details arg;
	struct wl_proxy *new_proxy = NULL;

	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 'n':
			new_proxy = proxy_create(proxy, interface);
			if (new_proxy == NULL)
				return NULL;

			args[i].o = &new_proxy->object;
			break;
		}
	}

	return new_proxy;
}
Ejemplo n.º 6
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 'h':
			break;
		case 'u':
		case 'i':
		case 'f':
		case 'o':
		case 'n':
			buffer_size++;
			break;
		case 's':
			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 'a':
			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;
}
Ejemplo n.º 7
0
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);
		switch (arg.type) {
		case 'o':
			id = closure->args[i].n;
			closure->args[i].o = NULL;

			object = wl_map_lookup(objects, id);
			if (object == WL_ZOMBIE_OBJECT) {
				/* 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);
				object = NULL;
				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;
}
Ejemplo n.º 8
0
static void
increase_closure_args_refcount(struct wl_closure *closure)
{
	const char *signature;
	struct argument_details arg;
	int i, count;
	struct wl_proxy *proxy;

	signature = closure->message->signature;
	count = arg_count_for_signature(signature);
	for (i = 0; i < count; i++) {
		signature = get_next_argument(signature, &arg);
		switch (arg.type) {
		case 'n':
		case 'o':
			proxy = (struct wl_proxy *) closure->args[i].o;
			if (proxy)
				proxy->refcount++;
			break;
		default:
			break;
		}
	}
}
Ejemplo n.º 9
0
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, id;
	int fd;
	char *s;
	unsigned int i, count, num_arrays;
	const char *signature;
	struct argument_details arg;
	struct wl_closure *closure;
	struct wl_array *array, *array_extra;

	count = arg_count_for_signature(message->signature);
	if (count > WL_CLOSURE_MAX_ARGS) {
		wl_log("too many args (%d)\n", count);
		errno = EINVAL;
		wl_connection_consume(connection, size);
		return NULL;
	}

	num_arrays = wl_message_count_arrays(message);
	closure = malloc(sizeof *closure + size + num_arrays * sizeof *array);
	if (closure == NULL) {
		errno = ENOMEM;
		wl_connection_consume(connection, size);
		return NULL;
	}

	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 != 'h' && p + 1 > end) {
			wl_log("message too short, "
			       "object (%d), message %s(%s)\n",
			       *p, message->name, message->signature);
			errno = EINVAL;
			goto err;
		}

		switch (arg.type) {
		case 'u':
			closure->args[i].u = *p++;
			break;
		case 'i':
			closure->args[i].i = *p++;
			break;
		case 'f':
			closure->args[i].f = *p++;
			break;
		case 's':
			length = *p++;

			if (length == 0) {
				closure->args[i].s = NULL;
				break;
			}

			next = p + DIV_ROUNDUP(length, sizeof *p);
			if (next > end) {
				wl_log("message too short, "
				       "object (%d), message %s(%s)\n",
				       closure->sender_id, message->name,
				       message->signature);
				errno = EINVAL;
				goto err;
			}

			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 'o':
			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 'n':
			id = *p++;
			closure->args[i].n = id;

			if (id == 0 && !arg.nullable) {
				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) {
				wl_log("not a valid new object id (%u), "
				       "message %s(%s)\n",
				       id, message->name, message->signature);
				errno = EINVAL;
				goto err;
			}

			break;
		case 'a':
			length = *p++;

			next = p + DIV_ROUNDUP(length, sizeof *p);
			if (next > end) {
				wl_log("message too short, "
				       "object (%d), message %s(%s)\n",
				       closure->sender_id, message->name,
				       message->signature);
				errno = EINVAL;
				goto err;
			}

			array_extra->size = length;
			array_extra->alloc = 0;
			array_extra->data = p;

			closure->args[i].a = array_extra++;
			p = next;
			break;
		case 'h':
			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;
			}

			wl_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;
		}
	}

	closure->count = count;
	closure->message = message;

	wl_connection_consume(connection, size);

	return closure;

 err:
	wl_closure_destroy(closure);
	wl_connection_consume(connection, size);

	return NULL;
}
Ejemplo n.º 10
0
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;

	count = arg_count_for_signature(message->signature);
	if (count > WL_CLOSURE_MAX_ARGS) {
		wl_log("too many args (%d)\n", count);
		errno = EINVAL;
		return NULL;
	}

	closure = malloc(sizeof *closure);
	if (closure == NULL) {
		errno = ENOMEM;
		return NULL;
	}

	memcpy(closure->args, args, count * sizeof *args);

	signature = message->signature;
	for (i = 0; i < count; i++) {
		signature = get_next_argument(signature, &arg);

		switch (arg.type) {
		case 'f':
		case 'u':
		case 'i':
			break;
		case 's':
			if (!arg.nullable && args[i].s == NULL)
				goto err_null;
			break;
		case 'o':
			if (!arg.nullable && args[i].o == NULL)
				goto err_null;
			break;
		case 'n':
			object = args[i].o;
			if (!arg.nullable && object == NULL)
				goto err_null;

			closure->args[i].n = object ? object->id : 0;
			break;
		case 'a':
			if (!arg.nullable && args[i].a == NULL)
				goto err_null;
			break;
		case 'h':
			fd = args[i].h;
			dup_fd = wl_os_dupfd_cloexec(fd, 0);
			if (dup_fd < 0)
				wl_abort("dup failed: %s\n", strerror(errno));
			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;
	closure->message = message;
	closure->count = count;

	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;
}
Ejemplo n.º 11
0
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 == 'h')
			continue;

		if (p + 1 > end)
			goto overflow;

		switch (arg.type) {
		case 'u':
			*p++ = closure->args[i].u;
			break;
		case 'i':
			*p++ = closure->args[i].i;
			break;
		case 'f':
			*p++ = closure->args[i].f;
			break;
		case 'o':
			*p++ = closure->args[i].o ? closure->args[i].o->id : 0;
			break;
		case 'n':
			*p++ = closure->args[i].n;
			break;
		case 's':
			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 'a':
			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;

			memcpy(p, closure->args[i].a->data, size);
			p += DIV_ROUNDUP(size, sizeof *p);
			break;
		default:
			break;
		}
	}

	size = (p - buffer) * sizeof *p;

	buffer[0] = closure->sender_id;
	buffer[1] = size << 16 | (closure->opcode & 0x0000ffff);

	return size;

overflow:
	errno = ERANGE;
	return -1;
}
Ejemplo n.º 12
0
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, **id;
	int *fd;
	char *extra, **s;
	unsigned int i, count, extra_space;
	const char *signature = message->signature;
	struct argument_details arg;
	struct wl_object **object;
	struct wl_array **array;
	struct wl_closure *closure;

	count = arg_count_for_signature(signature) + 2;
	if (count > ARRAY_LENGTH(closure->types)) {
		printf("too many args (%d)\n", count);
		errno = EINVAL;
		wl_connection_consume(connection, size);
		return NULL;
	}

	extra_space = wl_message_size_extra(message);
	closure = malloc(sizeof *closure + 8 + size + extra_space);
	if (closure == NULL)
		return NULL;

	closure->message = message;
	closure->types[0] = &ffi_type_pointer;
	closure->types[1] = &ffi_type_pointer;
	closure->start = closure->buffer;

	wl_connection_copy(connection, closure->buffer, size);
	p = &closure->buffer[2];
	end = (uint32_t *) ((char *) p + size);
	extra = (char *) end;
	for (i = 2; i < count; i++) {
		signature = get_next_argument(signature, &arg);

		if (p + 1 > end) {
			printf("message too short, "
			       "object (%d), message %s(%s)\n",
			       *p, message->name, message->signature);
			errno = EINVAL;
			goto err;
		}

		switch (arg.type) {
		case 'u':
			closure->types[i] = &ffi_type_uint32;
			closure->args[i] = p++;
			break;
		case 'i':
			closure->types[i] = &ffi_type_sint32;
			closure->args[i] = p++;
			break;
		case 'f':
			closure->types[i] = &ffi_type_sint32;
			closure->args[i] = p++;
			break;
		case 's':
			closure->types[i] = &ffi_type_pointer;
			length = *p++;

			next = p + DIV_ROUNDUP(length, sizeof *p);
			if (next > end) {
				printf("message too short, "
				       "object (%d), message %s(%s)\n",
				       *p, message->name, message->signature);
				errno = EINVAL;
				goto err;
			}

			s = (char **) extra;
			extra += sizeof *s;
			closure->args[i] = s;

			if (length == 0) {
				*s = NULL;
			} else {
				*s = (char *) p;
			}

			if (length > 0 && (*s)[length - 1] != '\0') {
				printf("string not nul-terminated, "
				       "message %s(%s)\n",
				       message->name, message->signature);
				errno = EINVAL;
				goto err;
			}
			p = next;
			break;
		case 'o':
			closure->types[i] = &ffi_type_pointer;
			object = (struct wl_object **) extra;
			extra += sizeof *object;
			closure->args[i] = object;

			if (*p == 0 && !arg.nullable) {
				printf("NULL new ID received on non-nullable "
				       "type, message %s(%s)\n", message->name,
				       message->signature);
				*object = NULL;
				errno = EINVAL;
				goto err;
			}

			*object = wl_map_lookup(objects, *p);
			if (*object == WL_ZOMBIE_OBJECT) {
				/* references object we've already
				 * destroyed client side */
				*object = NULL;
			} else if (*object == NULL && *p != 0) {
				printf("unknown object (%u), message %s(%s)\n",
				       *p, message->name, message->signature);
				*object = NULL;
				errno = EINVAL;
				goto err;
			}

			if (*object != NULL && message->types[i-2] != NULL &&
			    (*object)->interface != message->types[i-2]) {
				printf("invalid object (%u), type (%s), "
					"message %s(%s)\n",
				       *p, (*object)->interface->name,
				       message->name, message->signature);
				errno = EINVAL;
				goto err;
			}

			p++;
			break;
		case 'n':
			closure->types[i] = &ffi_type_pointer;
			id = (uint32_t **) extra;
			extra += sizeof *id;
			closure->args[i] = id;
			*id = p;

			if (*id == 0 && !arg.nullable) {
				printf("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, *p) < 0) {
				printf("not a valid new object id (%d), "
				       "message %s(%s)\n",
				       *p, message->name, message->signature);
				errno = EINVAL;
				goto err;
			}

			p++;
			break;
		case 'a':
			closure->types[i] = &ffi_type_pointer;
			length = *p++;

			next = p + DIV_ROUNDUP(length, sizeof *p);
			if (next > end) {
				printf("message too short, "
				       "object (%d), message %s(%s)\n",
				       *p, message->name, message->signature);
				errno = EINVAL;
				goto err;
			}

			array = (struct wl_array **) extra;
			extra += sizeof *array;
			closure->args[i] = array;

			*array = (struct wl_array *) extra;
			extra += sizeof **array;

			(*array)->size = length;
			(*array)->alloc = 0;
			(*array)->data = p;
			p = next;
			break;
		case 'h':
			closure->types[i] = &ffi_type_sint;

			fd = (int *) extra;
			extra += sizeof *fd;
			closure->args[i] = fd;

			wl_buffer_copy(&connection->fds_in, fd, sizeof *fd);
			connection->fds_in.tail += sizeof *fd;
			break;
		default:
			printf("unknown type\n");
			assert(0);
			break;
		}
	}

	closure->count = i;

	ffi_prep_cif(&closure->cif, FFI_DEFAULT_ABI,
		     closure->count, &ffi_type_void, closure->types);

	wl_connection_consume(connection, size);

	return closure;

 err:
	closure->count = i;
	wl_closure_destroy(closure);
	wl_connection_consume(connection, size);

	return NULL;
}
Ejemplo n.º 13
0
struct wl_closure *
wl_closure_vmarshal(struct wl_object *sender,
		    uint32_t opcode, va_list ap,
		    const struct wl_message *message)
{
	struct wl_closure *closure;
	struct wl_object **objectp, *object;
	uint32_t length, aligned, *p, *start, size, *end;
	int dup_fd;
	struct wl_array **arrayp, *array;
	const char **sp, *s;
	const char *signature = message->signature;
	struct argument_details arg;
	char *extra;
	int i, count, fd, extra_size, *fd_ptr;

	/* FIXME: Match old fixed allocation for now */
	closure = malloc(sizeof *closure + 1024);
	if (closure == NULL)
		return NULL;

	extra_size = wl_message_size_extra(message);
	count = arg_count_for_signature(signature) + 2;
	extra = (char *) closure->buffer;
	start = &closure->buffer[DIV_ROUNDUP(extra_size, sizeof *p)];
	end = &closure->buffer[256];
	p = &start[2];

	closure->types[0] = &ffi_type_pointer;
	closure->types[1] = &ffi_type_pointer;

	for (i = 2; i < count; i++) {
		signature = get_next_argument(signature, &arg);

		switch (arg.type) {
		case 'f':
			closure->types[i] = &ffi_type_sint32;
			closure->args[i] = p;
			if (end - p < 1)
				goto err;
			*p++ = va_arg(ap, wl_fixed_t);
			break;
		case 'u':
			closure->types[i] = &ffi_type_uint32;
			closure->args[i] = p;
			if (end - p < 1)
				goto err;
			*p++ = va_arg(ap, uint32_t);
			break;
		case 'i':
			closure->types[i] = &ffi_type_sint32;
			closure->args[i] = p;
			if (end - p < 1)
				goto err;
			*p++ = va_arg(ap, int32_t);
			break;
		case 's':
			closure->types[i] = &ffi_type_pointer;
			closure->args[i] = extra;
			sp = (const char **) extra;
			extra += sizeof *sp;

			s = va_arg(ap, const char *);

			if (!arg.nullable && s == NULL)
				goto err_null;

			length = s ? strlen(s) + 1: 0;
			aligned = (length + 3) & ~3;
			if (p + aligned / sizeof *p + 1 > end)
				goto err;
			*p++ = length;

			if (length > 0)
				*sp = (const char *) p;
			else
				*sp = NULL;

			memcpy(p, s, length);
			memset((char *) p + length, 0, aligned - length);
			p += aligned / sizeof *p;
			break;
		case 'o':
			closure->types[i] = &ffi_type_pointer;
			closure->args[i] = extra;
			objectp = (struct wl_object **) extra;
			extra += sizeof *objectp;

			object = va_arg(ap, struct wl_object *);

			if (!arg.nullable && object == NULL)
				goto err_null;

			*objectp = object;
			if (end - p < 1)
				goto err;
			*p++ = object ? object->id : 0;
			break;

		case 'n':
			closure->types[i] = &ffi_type_uint32;
			closure->args[i] = p;
			object = va_arg(ap, struct wl_object *);
			if (end - p < 1)
				goto err;

			if (!arg.nullable && object == NULL)
				goto err_null;

			*p++ = object ? object->id : 0;
			break;

		case 'a':
			closure->types[i] = &ffi_type_pointer;
			closure->args[i] = extra;
			arrayp = (struct wl_array **) extra;
			extra += sizeof *arrayp;

			*arrayp = (struct wl_array *) extra;
			extra += sizeof **arrayp;

			array = va_arg(ap, struct wl_array *);

			if (!arg.nullable && array == NULL)
				goto err_null;

			if (array == NULL || array->size == 0) {
				if (end - p < 1)
					goto err;
				*p++ = 0;
				break;
			}
			if (p + DIV_ROUNDUP(array->size, sizeof *p) + 1 > end)
				goto err;
			*p++ = array->size;
			memcpy(p, array->data, array->size);

			(*arrayp)->size = array->size;
			(*arrayp)->alloc = array->alloc;
			(*arrayp)->data = p;

			p += DIV_ROUNDUP(array->size, sizeof *p);
			break;

		case 'h':
			closure->types[i] = &ffi_type_sint;
			closure->args[i] = extra;
			fd_ptr = (int *) extra;
			extra += sizeof *fd_ptr;

			fd = va_arg(ap, int);
			dup_fd = wl_os_dupfd_cloexec(fd, 0);
			if (dup_fd < 0) {
				fprintf(stderr, "dup failed: %m");
				abort();
			}
			*fd_ptr = dup_fd;
			break;
		default:
			fprintf(stderr, "unhandled format code: '%c'\n",
				arg.type);
			assert(0);
			break;
		}
	}

	size = (p - start) * sizeof *p;
	start[0] = sender->id;
	start[1] = opcode | (size << 16);

	closure->start = start;
	closure->message = message;
	closure->count = count;

	ffi_prep_cif(&closure->cif, FFI_DEFAULT_ABI,
		     closure->count, &ffi_type_void, closure->types);

	return closure;

err:
	printf("request too big to marshal, maximum size is %zu\n",
	       sizeof closure->buffer);
	errno = ENOMEM;

	return NULL;

err_null:
	free(closure);
	wl_log("error marshalling arguments for %s:%i.%s (signature %s): "
	       "null value passed for arg %i\n",
	       sender->interface->name, sender->id, message->name,
	       message->signature, i);
	errno = EINVAL;
	return NULL;
}