blob: 90d136433effb35115eea17766def473e5f3983a [file] [log] [blame]
/* Copyright (C) 2014 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** Description
**
** Implementation of 'tcp' and 'unix' Android pipes. These can be used
** to connect a guest process directly with a host TCP or Unix socket.
**
** For TCP, connections are limited to localhost (127.0.0.1) ports for
** security reasons (doing otherwise might allow any application to
** sneakily connect to the Internet when running under emulation).
**
** This is also used by the 'opengles' Android pipe service to send
** wire protocol data to the GPU emulation libraries.
*/
#include "qemu/main-loop.h"
#include "qemu/sockets.h"
#include "hw/misc/android_pipe.h"
#include "hw/misc/android_opengles.h"
#include <errno.h>
//#include <sys/select.h>
/* Set to 1 or 2 for debug traces */
#define DEBUG 1
#if DEBUG >= 1
# define D(...) printf(__VA_ARGS__), printf("\n")
#else
# define D(...) ((void)0)
#endif
#if DEBUG >= 2
# define DD(...) printf(__VA_ARGS__), printf("\n")
# define DDASSERT(cond) _ANDROID_ASSERT(cond, "Assertion failure: ", #cond)
# define DDASSERT_INT_OP(cond,val,op) _ANDROID_ASSERT_INT_OP(cond,val,op)
#else
# define DD(...) ((void)0)
# define DDASSERT(cond) ((void)0)
# define DDASSERT_INT_OP(cond,val,op) ((void)0)
#endif
#define DDASSERT_INT_LT(cond,val) DDASSERT_INT_OP(cond,val,<)
#define DDASSERT_INT_LTE(cond,val) DDASSERT_INT_OP(cond,val,<=)
#define DDASSERT_INT_GT(cond,val) DDASSERT_INT_OP(cond,val,>)
#define DDASSERT_INT_GTE(cond,val) DDASSERT_INT_OP(cond,val,>=)
#define DDASSERT_INT_EQ(cond,val) DDASSERT_INT_OP(cond,val,==)
#define DDASSERT_INT_NEQ(cond,val) DDASSERT_INT_OP(cond,val,!=)
enum {
STATE_INIT,
STATE_CONNECTING,
STATE_CONNECTED,
STATE_CLOSING_GUEST,
STATE_CLOSING_SOCKET
};
typedef struct {
void *hwpipe;
int state;
int wakeWanted;
int wakeActual;
int fd;
} NetPipe;
/* Free a network pipe */
static void net_pipe_free(NetPipe *pipe)
{
/* Close the socket */
int fd = pipe->fd;
if (fd >= 0) {
qemu_set_fd_handler(fd, NULL, NULL, NULL);
closesocket(fd);
pipe->fd = -1;
}
g_free(pipe);
}
static void net_pipe_read_handler(void* opaque);
static void net_pipe_write_handler(void* opaque);
static void net_pipe_reset_state(NetPipe *pipe)
{
IOHandler *read_handler = NULL;
IOHandler *write_handler = NULL;
if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) {
write_handler = net_pipe_write_handler;
}
if (pipe->state == STATE_CONNECTED &&
(pipe->wakeWanted & PIPE_WAKE_READ) != 0) {
read_handler = net_pipe_read_handler;
}
qemu_set_fd_handler(pipe->fd, read_handler, write_handler, pipe);
}
static void net_pipe_close_from_socket(void *opaque)
{
NetPipe *pipe = opaque;
/* If the guest already ordered the pipe to be closed,
* delete immediately */
if (pipe->state == STATE_CLOSING_GUEST) {
net_pipe_free(pipe);
return;
}
/* Force the closure of the pipe channel - if a guest is blocked
* waiting for a wake signal, it will receive an error. */
if (pipe->hwpipe != NULL) {
android_pipe_close(pipe->hwpipe);
pipe->hwpipe = NULL;
}
pipe->state = STATE_CLOSING_SOCKET;
net_pipe_reset_state(pipe);
}
/* Called when data arrives on the pipe's socket. */
static void net_pipe_read_handler(void* opaque)
{
NetPipe *pipe = opaque;
pipe->wakeActual |= PIPE_WAKE_READ;
if ((pipe->wakeWanted & PIPE_WAKE_READ) != 0) {
android_pipe_wake(pipe->hwpipe, pipe->wakeActual);
pipe->wakeWanted &= ~PIPE_WAKE_READ;
}
net_pipe_reset_state(pipe);
}
/* Called when the pipe's file socket becomes writable. */
static void net_pipe_write_handler(void* opaque)
{
NetPipe *pipe = opaque;
pipe->wakeActual |= PIPE_WAKE_WRITE;
if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) {
android_pipe_wake(pipe->hwpipe, pipe->wakeActual);
pipe->wakeWanted &= ~PIPE_WAKE_WRITE;
}
net_pipe_reset_state(pipe);
}
static void net_pipe_close_from_guest(void *opaque)
{
NetPipe *pipe = opaque;
net_pipe_free(pipe);
}
static int net_pipe_ready_send(NetPipe *pipe)
{
if (pipe->state == STATE_CONNECTED) {
return 0;
} else if (pipe->state == STATE_CONNECTING) {
return PIPE_ERROR_AGAIN;
} else if (pipe->hwpipe == NULL) {
return PIPE_ERROR_INVAL;
} else {
return PIPE_ERROR_IO;
}
}
static int net_pipe_send_buffers(void* opaque,
const AndroidPipeBuffer *buffers,
int num_buffers)
{
NetPipe *pipe = opaque;
int ret = net_pipe_ready_send(pipe);
if (ret != 0) {
return ret;
}
const AndroidPipeBuffer* buff = buffers;
const AndroidPipeBuffer* buff_end = buff + num_buffers;
int count = 0;
for (; buff < buff_end; ++buff) {
count += buff->size;
}
buff = buffers;
int buff_start = 0;
ret = 0;
while (count > 0) {
int avail = buff->size - buff_start;
ssize_t len = send(pipe->fd, (const void*)(buff->data + buff_start), avail, 0);
if (len > 0) {
/* the write succeeded */
buff_start += len;
if (buff_start >= buff->size) {
buff++;
buff_start = 0;
}
count -= (int)len;
ret += (int)len;
continue;
}
if (len == 0) {
/* reached the end of stream? */
if (ret == 0) {
ret = PIPE_ERROR_IO;
}
break;
}
if (ret > 0) {
break;
}
if (errno == EAGAIN || errno == EWOULDBLOCK) {
ret = PIPE_ERROR_AGAIN;
} else {
ret = PIPE_ERROR_IO;
}
break;
}
return ret;
}
static int net_pipe_recv_buffers(void *opaque,
AndroidPipeBuffer *buffers,
int num_buffers)
{
NetPipe *pipe = opaque;
AndroidPipeBuffer* buff = buffers;
AndroidPipeBuffer* buff_end = buff + num_buffers;
int count = 0;
for (; buff < buff_end; ++buff) {
count += buff->size;
}
buff = buffers;
int ret = 0;
int buff_start = 0;
while (count > 0) {
int avail = buff->size - buff_start;
ssize_t len = recv(pipe->fd, (void*)(buff->data + buff_start), avail, 0);
if (len > 0) {
/* the read succeeded. */
buff_start += len;
if (buff_start >= buff->size) {
buff++;
buff_start = 0;
}
count -= (int) len;
ret += (int) len;
continue;
}
if (len == 0) {
/* reached the end of stream? */
if (ret == 0)
ret = PIPE_ERROR_IO;
break;
}
if (ret > 0) {
/* if data was already read, just return it. */
break;
}
/* need to return an appropriate error code. */
if (errno == EAGAIN || errno == EWOULDBLOCK) {
ret = PIPE_ERROR_AGAIN;
} else {
ret = PIPE_ERROR_IO;
}
break;
}
return ret;
}
static unsigned net_pipe_poll(void *opaque)
{
NetPipe *pipe = opaque;
#if 1
return pipe->wakeActual;
#else
unsigned result = 0;
do {
fd_set read_fds, write_fds;
struct timeval tv = {0, 0};
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_SET(fd, &read_fds);
FD_SET(fd, &write_fds);
ret = select(fd + 1, &read_fds, &write_fds, NULL, &tv);
} while (ret < 0 && errno == EINTR);
if (ret == 1) {
if (FD_ISSET(fd, &read_fds)) {
result |= PIPE_WAKE_READ;
}
if (FD_ISSET(fd, &write_fds)) {
result |= PIPE_WAKE_WRITE;
}
}
#endif
return 0;
}
static void net_pipe_wake_on(void *opaque, int flags)
{
NetPipe *pipe = opaque;
DD("%s: flags=%d", __FUNCTION__, flags);
pipe->wakeWanted |= flags;
pipe->wakeActual &= ~flags;
net_pipe_reset_state(pipe);
}
/* Called when the pipe finished connecting to its target. |fd| will be -1
* to indicate that the connection failed. */
static void net_pipe_connect_handler(int fd, void* opaque)
{
NetPipe *pipe = opaque;
if (fd < 0) {
net_pipe_close_from_socket(pipe);
return;
}
pipe->state = STATE_CONNECTED;
net_pipe_reset_state(pipe);
}
static void *net_pipe_init_from(void *hwpipe,
const char* args,
bool is_unix)
{
if (args == NULL || args[0] == '\0') {
D("%s: Missing address!", __FUNCTION__);
return NULL;
}
NetPipe *pipe = (NetPipe *)g_new0(NetPipe, 1);
pipe->hwpipe = hwpipe;
pipe->state = STATE_CONNECTING;
Error *err = NULL;
if (is_unix) {
D("%s: Unix path is '%s'", __FUNCTION__, args);
pipe->fd = unix_nonblocking_connect(args,
net_pipe_connect_handler,
pipe,
&err);
if (pipe->fd < 0) {
D("%s: Could not connect pipe to %s: %s\n",
__FUNCTION__, args, error_get_pretty(err));
}
} else {
D("%s: TCP port is '%s'", __FUNCTION__, args);
char address[64];
snprintf(address, sizeof(address), "127.0.0.1:%s", args);
pipe->fd = inet_nonblocking_connect(address,
net_pipe_connect_handler,
pipe,
&err);
if (pipe->fd < 0) {
D("%s: Could not connect pipe to %s: %s\n",
__FUNCTION__, address, error_get_pretty(err));
}
}
if (pipe->fd < 0) {
error_free(err);
g_free(pipe);
return NULL;
}
return pipe;
}
static void *net_pipe_init_tcp(void *hwpipe, void *opaque, const char *args)
{
return net_pipe_init_from(hwpipe, args, false);
}
#ifndef _WIN32
static void *net_pipe_init_unix(void *hwpipe, void *opaque, const char *args)
{
return net_pipe_init_from(hwpipe, args, true);
}
#endif
static const AndroidPipeFuncs net_pipe_tcp_funcs = {
net_pipe_init_tcp,
net_pipe_close_from_guest,
net_pipe_send_buffers,
net_pipe_recv_buffers,
net_pipe_poll,
net_pipe_wake_on,
NULL, /* we can't save these */
NULL, /* we can't load these */
};
#ifndef _WIN32
static const AndroidPipeFuncs net_pipe_unix_funcs = {
net_pipe_init_unix,
net_pipe_close_from_guest,
net_pipe_send_buffers,
net_pipe_recv_buffers,
net_pipe_poll,
net_pipe_wake_on,
NULL, /* we can't save these */
NULL, /* we can't load these */
};
#endif
static void *opengles_pipe_init(void *hwpipe,
void *opaque,
const char *args)
{
char server_addr[PATH_MAX];
android_gles_server_path(server_addr, sizeof(server_addr));
NetPipe *pipe;
#ifndef _WIN32
// Use a Unix pipe on Posix systems.
pipe = (NetPipe *)net_pipe_init_unix(hwpipe, opaque, server_addr);
#else
// Use a TCP pipe on Windows.
pipe = (NetPipe *)net_pipe_init_tcp(hwpipe, opaque, server_addr);
#endif
if (!pipe) {
return NULL;
}
// Disable TCP nagle algorithm to improve through put of small
// packets.
socket_set_nodelay(pipe->fd);
#ifdef _WIN32
// On Windows, adjust buffer sizes.
int sndbuf = 128 * 1024;
int len = sizeof(sndbuf);
if (setsockopt(pipe->fd,
SOL_SOCKET,
SO_SNDBUF,
(char *)&sndbuf,
(int) sizeof(sndbuf)) == SOCKET_ERROR) {
D("Failed to set SO_SNDBUF to %d error=0x%x\n",
sndbuf, WSAGetLastError());
}
#endif
return pipe;
}
static const AndroidPipeFuncs opengles_pipe_funcs = {
opengles_pipe_init,
net_pipe_close_from_guest,
net_pipe_send_buffers,
net_pipe_recv_buffers,
net_pipe_poll,
net_pipe_wake_on,
NULL, /* we can't save these */
NULL, /* we can't save these */
};
void
android_net_pipe_init(void)
{
android_pipe_add_type("tcp", NULL, &net_pipe_tcp_funcs);
#ifndef _WIN32
android_pipe_add_type("unix", NULL, &net_pipe_unix_funcs);
#endif
android_pipe_add_type("opengles", NULL, &opengles_pipe_funcs);
}