From 16f2e31f245c246d0be0a2dfcb21806dc6aceefa Mon Sep 17 00:00:00 2001 From: David Turner Date: Tue, 12 Jul 2011 06:30:56 +0200 Subject: [PATCH] emulator:opengl: input event redirection This patch allows the emulator_test_renderer program to redirect input events (mouse, keyboard) to the emulator's console program. This in order to make testing easier, while we wait for integration of the display into the emulator program. For now, this is hard-coded to use localhost port 5554, and only a few keys are properly translated (Dpad, HOME, BACK, MENU). Change-Id: Ie6e37c85c291f53de49220a181cb8724f66519ea --- tools/emulator/opengl/Android.mk | 1 + .../tests/emulator_test_renderer/Android.mk | 2 +- .../tests/emulator_test_renderer/main.cpp | 76 +- .../opengl/tests/event_injector/Android.mk | 13 + .../tests/event_injector/EventInjector.cpp | 76 + .../tests/event_injector/EventInjector.h | 59 + .../tests/event_injector/emulator-console.c | 345 ++++ .../tests/event_injector/emulator-console.h | 55 + .../tests/event_injector/iolooper-select.c | 257 +++ .../opengl/tests/event_injector/iolooper.h | 91 + .../opengl/tests/event_injector/sockets.c | 1573 +++++++++++++++++ .../opengl/tests/event_injector/sockets.h | 432 +++++ 12 files changed, 2975 insertions(+), 5 deletions(-) create mode 100644 tools/emulator/opengl/tests/event_injector/Android.mk create mode 100644 tools/emulator/opengl/tests/event_injector/EventInjector.cpp create mode 100644 tools/emulator/opengl/tests/event_injector/EventInjector.h create mode 100644 tools/emulator/opengl/tests/event_injector/emulator-console.c create mode 100644 tools/emulator/opengl/tests/event_injector/emulator-console.h create mode 100644 tools/emulator/opengl/tests/event_injector/iolooper-select.c create mode 100644 tools/emulator/opengl/tests/event_injector/iolooper.h create mode 100644 tools/emulator/opengl/tests/event_injector/sockets.c create mode 100644 tools/emulator/opengl/tests/event_injector/sockets.h diff --git a/tools/emulator/opengl/Android.mk b/tools/emulator/opengl/Android.mk index c7f7b7015..cd5b1b722 100644 --- a/tools/emulator/opengl/Android.mk +++ b/tools/emulator/opengl/Android.mk @@ -85,6 +85,7 @@ include $(EMUGL_PATH)/host/renderer/Android.mk # Host unit-test for the renderer. this one uses its own small # EGL host wrapper. +include $(EMUGL_PATH)/tests/event_injector/Android.mk include $(EMUGL_PATH)/tests/EGL_host_wrapper/Android.mk include $(EMUGL_PATH)/tests/emulator_test_renderer/Android.mk include $(EMUGL_PATH)/tests/ut_renderer/Android.mk diff --git a/tools/emulator/opengl/tests/emulator_test_renderer/Android.mk b/tools/emulator/opengl/tests/emulator_test_renderer/Android.mk index 9d524c130..12a09f128 100644 --- a/tools/emulator/opengl/tests/emulator_test_renderer/Android.mk +++ b/tools/emulator/opengl/tests/emulator_test_renderer/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH:=$(call my-dir) $(call emugl-begin-host-executable,emulator_test_renderer) -$(call emugl-import,libOpenglRender) +$(call emugl-import,libOpenglRender event_injector) LOCAL_SRC_FILES := main.cpp diff --git a/tools/emulator/opengl/tests/emulator_test_renderer/main.cpp b/tools/emulator/opengl/tests/emulator_test_renderer/main.cpp index cf521d4e4..c0b553502 100644 --- a/tools/emulator/opengl/tests/emulator_test_renderer/main.cpp +++ b/tools/emulator/opengl/tests/emulator_test_renderer/main.cpp @@ -19,6 +19,9 @@ #include #include #include "libOpenglRender/render_api.h" +#include + +static int convert_keysym(int sym); // forward #ifdef _WIN32 #include @@ -31,8 +34,11 @@ int main(int argc, char *argv[]) int winWidth = 320; int winHeight = 480; int width, height; + int mouseDown = 0; const char* env = getenv("ANDROID_WINDOW_SIZE"); FBNativeWindowType windowId = NULL; + EventInjector* injector; + int consolePort = 5554; if (env && sscanf(env, "%dx%d", &width, &height) == 2) { winWidth = width; @@ -81,14 +87,53 @@ int main(int argc, char *argv[]) } printf("renderer process started\n"); + injector = new EventInjector(consolePort); + // Just wait until the window is closed SDL_Event ev; - while( SDL_WaitEvent(&ev) ) { - if (ev.type == SDL_QUIT) { - break; + + for (;;) { + injector->wait(1000/15); + injector->poll(); + + while (SDL_PollEvent(&ev)) { + switch (ev.type) { + case SDL_MOUSEBUTTONDOWN: + if (!mouseDown) { + injector->sendMouseDown(ev.button.x, ev.button.y); + mouseDown = 1; + } + break; + case SDL_MOUSEBUTTONUP: + if (mouseDown) { + injector->sendMouseUp(ev.button.x,ev.button.y); + mouseDown = 0; + } + break; + case SDL_MOUSEMOTION: + if (mouseDown) + injector->sendMouseMotion(ev.button.x,ev.button.y); + break; + + case SDL_KEYDOWN: +#ifdef __APPLE__ + /* special code to deal with Command-Q properly */ + if (ev.key.keysym.sym == SDLK_q && + ev.key.keysym.mod & KMOD_META) { + goto EXIT; + } +#endif + injector->sendKeyDown(convert_keysym(ev.key.keysym.sym)); + break; + case SDL_KEYUP: + injector->sendKeyUp(convert_keysym(ev.key.keysym.sym)); + break; + case SDL_QUIT: + goto EXIT; + } } } - +EXIT: // // stop the renderer // @@ -97,3 +142,26 @@ int main(int argc, char *argv[]) return 0; } + +static int convert_keysym(int sym) +{ +#define EE(x,y) SDLK_##x, EventInjector::KEY_##y, + static const int keymap[] = { + EE(LEFT,LEFT) + EE(RIGHT,RIGHT) + EE(DOWN,DOWN) + EE(UP,UP) + EE(RETURN,ENTER) + EE(F1,SOFT1) + EE(ESCAPE,BACK) + EE(HOME,HOME) + -1 + }; + int nn; + for (nn = 0; keymap[nn] >= 0; nn += 2) { + if (keymap[nn] == sym) + return keymap[nn+1]; + } + return sym; +} + diff --git a/tools/emulator/opengl/tests/event_injector/Android.mk b/tools/emulator/opengl/tests/event_injector/Android.mk new file mode 100644 index 000000000..26eb47691 --- /dev/null +++ b/tools/emulator/opengl/tests/event_injector/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH := $(call my-dir) + +$(call emugl-begin-host-static-library,event_injector) + +LOCAL_SRC_FILES := \ + EventInjector.cpp \ + sockets.c \ + emulator-console.c \ + iolooper-select.c + +$(call emugl-export,C_INCLUDES,$(LOCAL_PATH)) + +$(call emugl-end-module) diff --git a/tools/emulator/opengl/tests/event_injector/EventInjector.cpp b/tools/emulator/opengl/tests/event_injector/EventInjector.cpp new file mode 100644 index 000000000..6dade6e49 --- /dev/null +++ b/tools/emulator/opengl/tests/event_injector/EventInjector.cpp @@ -0,0 +1,76 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "EventInjector.h" +#include "emulator-console.h" + +#define PRIVATE EventInjectorPrivate + +class PRIVATE +{ +public: + IoLooper* mLooper; + EmulatorConsole* mConsole; + + EventInjectorPrivate(int port) { + mLooper = iolooper_new(); + mConsole = emulatorConsole_new(port, mLooper); + } +}; + +EventInjector::EventInjector(int consolePort) +{ + mPrivate = new PRIVATE(consolePort); +} + +EventInjector::~EventInjector() +{ + delete mPrivate; +} + +void EventInjector::wait(int timeout_ms) +{ + iolooper_wait(mPrivate->mLooper, timeout_ms); +} + +void EventInjector::poll(void) +{ + emulatorConsole_poll(mPrivate->mConsole); +} + +void EventInjector::sendMouseDown( int x, int y ) +{ + emulatorConsole_sendMouseDown(mPrivate->mConsole, x, y); +} + +void EventInjector::sendMouseUp( int x, int y ) +{ + emulatorConsole_sendMouseUp(mPrivate->mConsole, x, y); +} + +void EventInjector::sendMouseMotion( int x, int y ) +{ + emulatorConsole_sendMouseMotion(mPrivate->mConsole, x, y); +} + +void EventInjector::sendKeyDown( int keycode ) +{ + emulatorConsole_sendKey(mPrivate->mConsole, keycode, 1); +} + +void EventInjector::sendKeyUp( int keycode ) +{ + emulatorConsole_sendKey(mPrivate->mConsole, keycode, 0); +} diff --git a/tools/emulator/opengl/tests/event_injector/EventInjector.h b/tools/emulator/opengl/tests/event_injector/EventInjector.h new file mode 100644 index 000000000..a8fded701 --- /dev/null +++ b/tools/emulator/opengl/tests/event_injector/EventInjector.h @@ -0,0 +1,59 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* Event redirector is used to inject user events from a GL window + * into the emulated program. + */ +#ifndef EVENT_INJECTOR_H +#define EVENT_INJECTOR_H + +class EventInjectorPrivate; + +class EventInjector +{ +public: + EventInjector(int consolePort); + virtual ~EventInjector(); + + void wait( int timeout_ms ); + void poll( void ); + + void sendMouseDown( int x, int y ); + void sendMouseUp( int x, int y ); + void sendMouseMotion( int x, int y ); + void sendKeyDown( int keycode ); + void sendKeyUp( int keycode ); + + /* Keycode values expected by the Linux kernel, and the emulator */ + enum { + KEY_BACK = 158, + KEY_HOME = 102, + KEY_SOFT1 = 229, + KEY_LEFT = 105, + KEY_UP = 103, + KEY_DOWN = 108, + KEY_RIGHT = 106, + KEY_VOLUMEUP = 115, + KEY_VOLUMEDOWN = 114, + KEY_SEND = 231, + KEY_END = 107, + KEY_ENTER = 28, + }; + +private: + EventInjectorPrivate* mPrivate; +}; + +#endif /* EVENT_INJECTOR_H */ diff --git a/tools/emulator/opengl/tests/event_injector/emulator-console.c b/tools/emulator/opengl/tests/event_injector/emulator-console.c new file mode 100644 index 000000000..a8c49b291 --- /dev/null +++ b/tools/emulator/opengl/tests/event_injector/emulator-console.c @@ -0,0 +1,345 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "emulator-console.h" +#include "sockets.h" +#include +#include +#include + +#define DEBUG 0 +#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") +#else +# define DD(...) ((void)0) +#endif + +#define ANEW0(p) (p) = calloc(sizeof(*(p)), 1) + +enum { + STATE_CONNECTING = 0, + STATE_CONNECTED, + STATE_WAITING, + STATE_ERROR = 2 +}; + +typedef struct Msg { + const char* data; // pointer to data + int size; // size of data + int sent; // already sent (so sent..size remain in buffer). + struct Msg* next; // next message in queue. +} Msg; + +static Msg* +msg_alloc( const char* data, int datalen ) +{ + Msg* msg; + + msg = malloc(sizeof(*msg) + datalen); + msg->data = (const char*)(msg + 1); + msg->size = datalen; + msg->sent = 0; + memcpy((char*)msg->data, data, datalen); + msg->next = NULL; + + return msg; +} + +static void +msg_free( Msg* msg ) +{ + free(msg); +} + +struct EmulatorConsole { + int fd; + IoLooper* looper; + int state; + Msg* out_msg; + SockAddress address; + int64_t waitUntil; +}; + +/* Read as much from the input as possible, ignoring it. + */ +static int +emulatorConsole_eatInput( EmulatorConsole* con ) +{ + for (;;) { + char temp[64]; + int ret = socket_recv(con->fd, temp, sizeof temp); + if (ret < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + return -1; + } + if (ret == 0) { + return -1; + } + DD("Console received: '%.*s'", ret, temp); + } +} + +static int +emulatorConsole_sendOutput( EmulatorConsole* con ) +{ + if (con->state != STATE_CONNECTED) { + errno = EINVAL; + return -1; + } + + while (con->out_msg != NULL) { + Msg* msg = con->out_msg; + int ret; + + ret = socket_send(con->fd, + msg->data + msg->sent, + msg->size - msg->sent); + if (ret > 0) { + DD("Console sent: '%.*s'", ret, msg->data + msg->sent); + + msg->sent += ret; + if (msg->sent == msg->size) { + con->out_msg = msg->next; + msg_free(msg); + } + continue; + } + if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + return 0; + } + con->state = STATE_ERROR; + D("Console error when sending: %s", strerror(errno)); + return -1; + } + iolooper_del_write(con->looper, con->fd); + return 0; +} + +static void +emulatorConsole_completeConnect(EmulatorConsole* con) +{ + D("Console connected!"); + iolooper_add_read(con->looper, con->fd); + iolooper_del_write(con->looper, con->fd); + con->state = STATE_CONNECTED; + if (con->out_msg != NULL) { + iolooper_add_write(con->looper, con->fd); + emulatorConsole_sendOutput(con); + } +} + +static void +emulatorConsole_retry(EmulatorConsole* con) +{ + /* Not possible yet, wait one second */ + D("Could not connect to emulator, waiting 1 second: %s", errno_str); + con->state = STATE_WAITING; + con->waitUntil = iolooper_now() + 5000; +} + +static void +emulatorConsole_connect(EmulatorConsole* con) +{ + D("Trying to connect!"); + if (con->fd < 0) { + con->fd = socket_create_inet( SOCKET_STREAM ); + if (con->fd < 0) { + D("ERROR: Could not create socket: %s", errno_str); + con->state = STATE_ERROR; + return; + } + socket_set_nonblock(con->fd); + } + con->state = STATE_CONNECTING; + if (socket_connect(con->fd, &con->address) < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) { + iolooper_add_write(con->looper, con->fd); + } else { + emulatorConsole_retry(con); + } + return; + } + + emulatorConsole_completeConnect(con); +} + +static void +emulatorConsole_reset( EmulatorConsole* con ) +{ + D("Resetting console connection"); + while (con->out_msg) { + Msg* msg = con->out_msg; + con->out_msg = msg->next; + msg_free(msg); + } + iolooper_del_read(con->looper, con->fd); + iolooper_del_write(con->looper, con->fd); + socket_close(con->fd); + con->fd = -1; + emulatorConsole_connect(con); +} + +/* Create a new EmulatorConsole object to connect asynchronously to + * a given emulator port. Note that this should always succeeds since + * the connection is asynchronous. + */ +EmulatorConsole* +emulatorConsole_new(int port, IoLooper* looper) +{ + EmulatorConsole* con; + SockAddress addr; + + ANEW0(con); + con->looper = looper; + con->fd = -1; + sock_address_init_inet(&con->address, SOCK_ADDRESS_INET_LOOPBACK, port); + + emulatorConsole_connect(con); + return con; +} + +int +emulatorConsole_poll( EmulatorConsole* con ) +{ + int ret; + + if (con->state == STATE_WAITING) { + if (iolooper_now() >= con->waitUntil) + emulatorConsole_connect(con); + return 0; + } + + if (!iolooper_is_read(con->looper, con->fd) && + !iolooper_is_write(con->looper, con->fd)) + { + return 0; + } + +LOOP: + switch (con->state) { + case STATE_ERROR: + return -1; + + case STATE_CONNECTING: + // read socket error to determine success / error. + if (socket_get_error(con->fd) != 0) { + emulatorConsole_retry(con); + } else { + emulatorConsole_completeConnect(con); + } + return 0; + + case STATE_CONNECTED: + /* ignore input, if any */ + if (iolooper_is_read(con->looper, con->fd)) { + if (emulatorConsole_eatInput(con) < 0) { + goto SET_ERROR; + } + } + /* send outgoing data, if any */ + if (iolooper_is_write(con->looper, con->fd)) { + if (emulatorConsole_sendOutput(con) < 0) { + goto SET_ERROR; + } + } + return 0; + + default: + D("UNSUPPORTED STATE!"); + break; + } + +SET_ERROR: + D("Console ERROR!: %s\n", errno_str); + con->state = STATE_ERROR; + emulatorConsole_reset(con); + return -1; +} + +/* Send a message to the console asynchronously. Any answer will be + * ignored. */ +void +emulatorConsole_send( EmulatorConsole* con, const char* command ) +{ + int cmdlen = strlen(command); + Msg* msg; + Msg** plast; + + if (cmdlen == 0) + return; + + /* Append new message at end of outgoing list */ + msg = msg_alloc(command, cmdlen); + plast = &con->out_msg; + while (*plast) { + plast = &(*plast)->next; + } + *plast = msg; + if (con->out_msg == msg) { + iolooper_add_write(con->looper, con->fd); + } + emulatorConsole_sendOutput(con); +} + + +void +emulatorConsole_sendMouseDown( EmulatorConsole* con, int x, int y ) +{ + char temp[128]; + + D("sendMouseDown(%d,%d)", x, y); + snprintf(temp, sizeof temp, + "event send 3:0:%d 3:1:%d 1:330:1 0:0:0\r\n", + x, y); + emulatorConsole_send(con, temp); +} + +void +emulatorConsole_sendMouseMotion( EmulatorConsole* con, int x, int y ) +{ + /* Same as mouse down */ + emulatorConsole_sendMouseDown(con, x, y); +} + +void +emulatorConsole_sendMouseUp( EmulatorConsole* con, int x, int y ) +{ + char temp[128]; + + D("sendMouseUp(%d,%d)", x, y); + snprintf(temp, sizeof temp, + "event send 3:0:%d 3:1:%d 1:330:0 0:0:0\r\n", + x, y); + emulatorConsole_send(con, temp); +} + +#define EE(x,y) if (keycode == x) return y; + +void +emulatorConsole_sendKey( EmulatorConsole* con, int keycode, int down ) +{ + char temp[128]; + + snprintf(temp, sizeof temp, + "event send EV_KEY:%d:%d 0:0:0\r\n", keycode, down); + emulatorConsole_send(con, temp); +} diff --git a/tools/emulator/opengl/tests/event_injector/emulator-console.h b/tools/emulator/opengl/tests/event_injector/emulator-console.h new file mode 100644 index 000000000..19e9687d5 --- /dev/null +++ b/tools/emulator/opengl/tests/event_injector/emulator-console.h @@ -0,0 +1,55 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef ANDROID_EMULATOR_CONSOLE_H +#define ANDROID_EMULATOR_CONSOLE_H + +#include "iolooper.h" +#include "sockets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct EmulatorConsole EmulatorConsole; + +/* Create a new EmulatorConsole object to connect asynchronously to + * a given emulator port. Note that this always succeeds since the + * connection is asynchronous. + */ +EmulatorConsole* emulatorConsole_new(int port, IoLooper* looper); + +/* Call this after an iolooper_poll() or iolooper_wait() to check + * the status of the console's socket and act upon it. + * + * Returns 0 on success, or -1 on error (which indicates disconnection!) + */ +int emulatorConsole_poll( EmulatorConsole* console ); + +/* Send a message to the console asynchronously. Any answer will be + * ignored. */ +void emulatorConsole_send( EmulatorConsole* console, const char* command ); + +void emulatorConsole_sendMouseDown( EmulatorConsole* con, int x, int y ); +void emulatorConsole_sendMouseMotion( EmulatorConsole* con, int x, int y ); +void emulatorConsole_sendMouseUp( EmulatorConsole* con, int x, int y ); + +void emulatorConsole_sendKey( EmulatorConsole* con, int keycode, int down ); + +#ifdef __cplusplus +} +#endif + +#endif /* ANDROID_EMULATOR_CONSOLE_H */ diff --git a/tools/emulator/opengl/tests/event_injector/iolooper-select.c b/tools/emulator/opengl/tests/event_injector/iolooper-select.c new file mode 100644 index 000000000..b751bddcf --- /dev/null +++ b/tools/emulator/opengl/tests/event_injector/iolooper-select.c @@ -0,0 +1,257 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include +#include +#include "iolooper.h" + +/* An implementation of iolooper.h based on Unix select() */ +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include +#endif + +struct IoLooper { + fd_set reads[1]; + fd_set writes[1]; + fd_set reads_result[1]; + fd_set writes_result[1]; + int max_fd; + int max_fd_valid; +}; + +IoLooper* +iolooper_new(void) +{ + IoLooper* iol = malloc(sizeof(*iol)); + iolooper_reset(iol); + return iol; +} + +void +iolooper_free( IoLooper* iol ) +{ + free(iol); +} + +void +iolooper_reset( IoLooper* iol ) +{ + FD_ZERO(iol->reads); + FD_ZERO(iol->writes); + iol->max_fd = -1; + iol->max_fd_valid = 1; +} + +static void +iolooper_add_fd( IoLooper* iol, int fd ) +{ + if (iol->max_fd_valid && fd > iol->max_fd) { + iol->max_fd = fd; + } +} + +static void +iolooper_del_fd( IoLooper* iol, int fd ) +{ + if (iol->max_fd_valid && fd == iol->max_fd) + iol->max_fd_valid = 0; +} + +void +iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags ) +{ + if (fd < 0) + return; + + int changed = oldflags ^ newflags; + + if ((changed & IOLOOPER_READ) != 0) { + if ((newflags & IOLOOPER_READ) != 0) + iolooper_add_read(iol, fd); + else + iolooper_del_read(iol, fd); + } + if ((changed & IOLOOPER_WRITE) != 0) { + if ((newflags & IOLOOPER_WRITE) != 0) + iolooper_add_write(iol, fd); + else + iolooper_del_write(iol, fd); + } +} + + +static int +iolooper_fd_count( IoLooper* iol ) +{ + int max_fd = iol->max_fd; + int fd; + + if (iol->max_fd_valid) + return max_fd + 1; + + /* recompute max fd */ + for (fd = 0; fd < FD_SETSIZE; fd++) { + if (!FD_ISSET(fd, iol->reads) && !FD_ISSET(fd, iol->writes)) + continue; + + max_fd = fd; + } + iol->max_fd = max_fd; + iol->max_fd_valid = 1; + + return max_fd + 1; +} + +void +iolooper_add_read( IoLooper* iol, int fd ) +{ + if (fd >= 0) { + iolooper_add_fd(iol, fd); + FD_SET(fd, iol->reads); + } +} + +void +iolooper_add_write( IoLooper* iol, int fd ) +{ + if (fd >= 0) { + iolooper_add_fd(iol, fd); + FD_SET(fd, iol->writes); + } +} + +void +iolooper_del_read( IoLooper* iol, int fd ) +{ + if (fd >= 0) { + iolooper_del_fd(iol, fd); + FD_CLR(fd, iol->reads); + } +} + +void +iolooper_del_write( IoLooper* iol, int fd ) +{ + if (fd >= 0) { + iolooper_del_fd(iol, fd); + FD_CLR(fd, iol->writes); + } +} + +int +iolooper_poll( IoLooper* iol ) +{ + int count = iolooper_fd_count(iol); + int ret; + fd_set errs; + + if (count == 0) + return 0; + + FD_ZERO(&errs); + + do { + struct timeval tv; + + tv.tv_sec = tv.tv_usec = 0; + + iol->reads_result[0] = iol->reads[0]; + iol->writes_result[0] = iol->writes[0]; + + ret = select( count, iol->reads_result, iol->writes_result, &errs, &tv); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +int +iolooper_wait( IoLooper* iol, int64_t duration ) +{ + int count = iolooper_fd_count(iol); + int ret; + fd_set errs; + struct timeval tm0, *tm = NULL; + + if (count == 0) + return 0; + + if (duration < 0) + tm = NULL; + else { + tm = &tm0; + tm->tv_sec = duration / 1000; + tm->tv_usec = (duration - 1000*tm->tv_sec) * 1000; + } + + FD_ZERO(&errs); + + do { + iol->reads_result[0] = iol->reads[0]; + iol->writes_result[0] = iol->writes[0]; + + ret = select( count, iol->reads_result, iol->writes_result, &errs, tm); + if (ret == 0) { + // Indicates timeout + errno = ETIMEDOUT; + } + } while (ret < 0 && errno == EINTR); + + return ret; +} + + +int +iolooper_is_read( IoLooper* iol, int fd ) +{ + return FD_ISSET(fd, iol->reads_result); +} + +int +iolooper_is_write( IoLooper* iol, int fd ) +{ + return FD_ISSET(fd, iol->writes_result); +} + +int +iolooper_has_operations( IoLooper* iol ) +{ + return iolooper_fd_count(iol) > 0; +} + +int64_t +iolooper_now(void) +{ + struct timeval time_now; + return gettimeofday(&time_now, NULL) ? -1 : (int64_t)time_now.tv_sec * 1000LL + + time_now.tv_usec / 1000; +} + +int +iolooper_wait_absolute(IoLooper* iol, int64_t deadline) +{ + int64_t timeout = deadline - iolooper_now(); + + /* If the deadline has passed, set the timeout to 0, this allows us + * to poll the file descriptor nonetheless */ + if (timeout < 0) + timeout = 0; + + return iolooper_wait(iol, timeout); +} diff --git a/tools/emulator/opengl/tests/event_injector/iolooper.h b/tools/emulator/opengl/tests/event_injector/iolooper.h new file mode 100644 index 000000000..4aa3db728 --- /dev/null +++ b/tools/emulator/opengl/tests/event_injector/iolooper.h @@ -0,0 +1,91 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifndef IOLOOPER_H +#define IOLOOPER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* An IOLooper is an abstraction for select() */ + +typedef struct IoLooper IoLooper; + +IoLooper* iolooper_new(void); +void iolooper_free( IoLooper* iol ); +void iolooper_reset( IoLooper* iol ); + +void iolooper_add_read( IoLooper* iol, int fd ); +void iolooper_add_write( IoLooper* iol, int fd ); +void iolooper_del_read( IoLooper* iol, int fd ); +void iolooper_del_write( IoLooper* iol, int fd ); + +enum { + IOLOOPER_READ = (1<<0), + IOLOOPER_WRITE = (1<<1), +}; +void iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags); + +int iolooper_poll( IoLooper* iol ); +/* Wrapper around select() + * Return: + * > 0 in case an I/O has occurred, or < 0 on error, or 0 on timeout with + * errno set to ETIMEDOUT. + */ +int iolooper_wait( IoLooper* iol, int64_t duration ); + +int iolooper_is_read( IoLooper* iol, int fd ); +int iolooper_is_write( IoLooper* iol, int fd ); +/* Returns 1 if this IoLooper has one or more file descriptor to interact with */ +int iolooper_has_operations( IoLooper* iol ); +/* Gets current time in milliseconds. + * Return: + * Number of milliseconds corresponded to the current time on success, or -1 + * on failure. + */ +int64_t iolooper_now(void); +/* Waits for an I/O to occur before specific absolute time. + * This routine should be used (instead of iolooper_wait) in cases when multiple + * sequential I/O should be completed within given time interval. For instance, + * consider the scenario, when "server" does two sequential writes, and "client" + * now has to read data transferred with these two distinct writes. It might be + * wasteful to do two reads, each with the same (large) timeout. Instead, it + * would be better to assign a deadline for both reads before the first read, + * and call iolooper_wait_absoulte with the same deadline value: + * int64_t deadline = iolooper_now() + TIMEOUT; + * if (iolooper_wait_absoulte(iol, deadline)) { + * // Process first buffer. + * (iolooper_wait_absoulte(iol, deadline)) { + * // Process second read + * } + * } + * Param: + * iol IoLooper instance for an I/O. + * deadline Deadline (absoulte time in milliseconds) before which an I/O should + * occur. + * Return: + * Number of I/O descriptors set in iol, if an I/O has occurred, 0 if no I/O + * occurred before the deadline, or -1 on error. + */ +int iolooper_wait_absolute(IoLooper* iol, int64_t deadline); + +#ifdef __cplusplus +} +#endif + +#endif /* IOLOOPER_H */ diff --git a/tools/emulator/opengl/tests/event_injector/sockets.c b/tools/emulator/opengl/tests/event_injector/sockets.c new file mode 100644 index 000000000..93dde3012 --- /dev/null +++ b/tools/emulator/opengl/tests/event_injector/sockets.c @@ -0,0 +1,1573 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#ifdef __linux__ /* Recent versions of glibc only define EAI_NODATA, which is an + extension to the POSIX standard, if _GNU_SOURCE is defined. */ +# define _GNU_SOURCE 1 +#endif + +#include "sockets.h" +#include +#include +#include +#include +#include +//#include "android/utils/path.h" +//#include "android/utils/debug.h" +//#include "android/utils/misc.h" +//#include "android/utils/system.h" + +#define D(...) ((void)0) + +#ifdef _WIN32 +# define xxWIN32_LEAN_AND_MEAN +# include +# include +# include +#else /* !_WIN32 */ +# include +# include +# include +# include +# include +# if HAVE_UNIX_SOCKETS +# include +# ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX (sizeof(((struct sockaddr_un*)0)->sun_path)-1) +# endif +# endif +#endif /* !_WIN32 */ + +#define MIN(x,y) ({ typeof(x) _x = (x); typeof(y) _y = (y); _x <= _y ? _x : _y; }) +#define AFREE(p) free(p) +#define AARRAY_NEW(p,count) (p) = malloc(sizeof(*(p))*(count)) +#define AARRAY_NEW0(p,count) (p) = calloc(sizeof(*(p)),(count)) + +/* QSOCKET_CALL is used to deal with the fact that EINTR happens pretty + * easily in QEMU since we use SIGALRM to implement periodic timers + */ +#ifdef _WIN32 +# define QSOCKET_CALL(_ret,_cmd) \ + do { _ret = (_cmd); } while ( _ret < 0 && WSAGetLastError() == WSAEINTR ) +#else +# define QSOCKET_CALL(_ret,_cmd) \ + do { \ + errno = 0; \ + do { _ret = (_cmd); } while ( _ret < 0 && errno == EINTR ); \ + } while (0); +#endif + +#ifdef _WIN32 + +#include + +static int winsock_error; + +#define WINSOCK_ERRORS_LIST \ + EE(WSA_INVALID_HANDLE,EINVAL,"invalid handle") \ + EE(WSA_NOT_ENOUGH_MEMORY,ENOMEM,"not enough memory") \ + EE(WSA_INVALID_PARAMETER,EINVAL,"invalid parameter") \ + EE(WSAEINTR,EINTR,"interrupted function call") \ + EE(WSAEALREADY,EALREADY,"operation already in progress") \ + EE(WSAEBADF,EBADF,"bad file descriptor") \ + EE(WSAEACCES,EACCES,"permission denied") \ + EE(WSAEFAULT,EFAULT,"bad address") \ + EE(WSAEINVAL,EINVAL,"invalid argument") \ + EE(WSAEMFILE,EMFILE,"too many opened files") \ + EE(WSAEWOULDBLOCK,EWOULDBLOCK,"resource temporarily unavailable") \ + EE(WSAEINPROGRESS,EINPROGRESS,"operation now in progress") \ + EE(WSAEALREADY,EAGAIN,"operation already in progress") \ + EE(WSAENOTSOCK,EBADF,"socket operation not on socket") \ + EE(WSAEDESTADDRREQ,EDESTADDRREQ,"destination address required") \ + EE(WSAEMSGSIZE,EMSGSIZE,"message too long") \ + EE(WSAEPROTOTYPE,EPROTOTYPE,"wrong protocol type for socket") \ + EE(WSAENOPROTOOPT,ENOPROTOOPT,"bad protocol option") \ + EE(WSAEADDRINUSE,EADDRINUSE,"address already in use") \ + EE(WSAEADDRNOTAVAIL,EADDRNOTAVAIL,"cannot assign requested address") \ + EE(WSAENETDOWN,ENETDOWN,"network is down") \ + EE(WSAENETUNREACH,ENETUNREACH,"network unreachable") \ + EE(WSAENETRESET,ENETRESET,"network dropped connection on reset") \ + EE(WSAECONNABORTED,ECONNABORTED,"software caused connection abort") \ + EE(WSAECONNRESET,ECONNRESET,"connection reset by peer") \ + EE(WSAENOBUFS,ENOBUFS,"no buffer space available") \ + EE(WSAEISCONN,EISCONN,"socket is already connected") \ + EE(WSAENOTCONN,ENOTCONN,"socket is not connected") \ + EE(WSAESHUTDOWN,ESHUTDOWN,"cannot send after socket shutdown") \ + EE(WSAETOOMANYREFS,ETOOMANYREFS,"too many references") \ + EE(WSAETIMEDOUT,ETIMEDOUT,"connection timed out") \ + EE(WSAECONNREFUSED,ECONNREFUSED,"connection refused") \ + EE(WSAELOOP,ELOOP,"cannot translate name") \ + EE(WSAENAMETOOLONG,ENAMETOOLONG,"name too long") \ + EE(WSAEHOSTDOWN,EHOSTDOWN,"host is down") \ + EE(WSAEHOSTUNREACH,EHOSTUNREACH,"no route to host") \ + +typedef struct { + int winsock; + int unix; + const char* string; +} WinsockError; + +static const WinsockError _winsock_errors[] = { +#define EE(w,u,s) { w, u, s }, + WINSOCK_ERRORS_LIST +#undef EE + { -1, -1, NULL } +}; + +/* this function reads the latest winsock error code and updates + * errno to a matching value. It also returns the new value of + * errno. + */ +static int +_fix_errno( void ) +{ + const WinsockError* werr = _winsock_errors; + int unix = EINVAL; /* generic error code */ + + winsock_error = WSAGetLastError(); + + for ( ; werr->string != NULL; werr++ ) { + if (werr->winsock == winsock_error) { + unix = werr->unix; + break; + } + } + errno = unix; + return -1; +} + +static int +_set_errno( int code ) +{ + winsock_error = -1; + errno = code; + return -1; +} + +/* this function returns a string describing the latest Winsock error */ +const char* +_errno_str(void) +{ + const WinsockError* werr = _winsock_errors; + const char* result = NULL; + + for ( ; werr->string; werr++ ) { + if (werr->winsock == winsock_error) { + result = werr->string; + break; + } + } + + if (result == NULL) { + result = tempstr_format( + "Unkown socket error (Winsock=0x%08x) errno=%d: %s", + winsock_error, errno, strerror(errno)); + } + return result; +} +#else +static int +_fix_errno( void ) +{ + return -1; +} + +static int +_set_errno( int code ) +{ + errno = code; + return -1; +} +#endif + +/* socket types */ + +static int +socket_family_to_bsd( SocketFamily family ) +{ + switch (family) { + case SOCKET_INET: return AF_INET; + case SOCKET_IN6: return AF_INET6; +#if HAVE_UNIX_SOCKETS + case SOCKET_UNIX: return AF_LOCAL; +#endif + default: return -1; + } +} + +static int +socket_type_to_bsd( SocketType type ) +{ + switch (type) { + case SOCKET_DGRAM: return SOCK_DGRAM; + case SOCKET_STREAM: return SOCK_STREAM; + default: return 0; + } +} + +static SocketType +socket_type_from_bsd( int type ) +{ + switch (type) { + case SOCK_DGRAM: return SOCKET_DGRAM; + case SOCK_STREAM: return SOCKET_STREAM; + default: return (SocketType) SOCKET_UNSPEC; + } +} + +#if 0 +static int +socket_type_check( SocketType type ) +{ + return (type == SOCKET_DGRAM || type == SOCKET_STREAM); +} +#endif + +typedef union { + struct sockaddr sa[1]; + struct sockaddr_in in[1]; +#if HAVE_IN6_SOCKETS + struct sockaddr_in6 in6[1]; +#endif +#if HAVE_UNIX_SOCKETS + struct sockaddr_un un[1]; +#endif +} sockaddr_storage; + +/* socket addresses */ + +void +sock_address_init_inet( SockAddress* a, uint32_t ip, uint16_t port ) +{ + a->family = SOCKET_INET; + a->u.inet.port = port; + a->u.inet.address = ip; +} + +void +sock_address_init_in6 ( SockAddress* a, const uint8_t* ip6[16], uint16_t port ) +{ + a->family = SOCKET_IN6; + a->u.in6.port = port; + memcpy( a->u.in6.address, ip6, sizeof(a->u.in6.address) ); +} + +void +sock_address_init_unix( SockAddress* a, const char* path ) +{ + a->family = SOCKET_UNIX; + a->u._unix.path = strdup(path ? path : ""); + a->u._unix.owner = 1; +} + +void sock_address_done( SockAddress* a ) +{ + if (a->family == SOCKET_UNIX && a->u._unix.owner) { + a->u._unix.owner = 0; + free((char*)a->u._unix.path); + } +} + +static char* +format_char( char* buf, char* end, int c ) +{ + if (buf < end) { + if (buf+1 == end) { + *buf++ = 0; + } else { + *buf++ = (char) c; + *buf = 0; + } + } + return buf; +} + +static char* +format_str( char* buf, char* end, const char* str ) +{ + int len = strlen(str); + int avail = end - buf; + + if (len > avail) + len = avail; + + memcpy( buf, str, len ); + buf += len; + + if (buf == end) + buf[-1] = 0; + else + buf[0] = 0; + + return buf; +} + +static char* +format_unsigned( char* buf, char* end, unsigned val ) +{ + char temp[16]; + int nn; + + for ( nn = 0; val != 0; nn++ ) { + int rem = val % 10; + temp[nn] = '0'+rem; + val /= 10; + } + + if (nn == 0) + temp[nn++] = '0'; + + while (nn > 0) + buf = format_char(buf, end, temp[--nn]); + + return buf; +} + +static char* +format_hex( char* buf, char* end, unsigned val, int ndigits ) +{ + int shift = 4*ndigits; + static const char hex[16] = "0123456789abcdef"; + + while (shift >= 0) { + buf = format_char(buf, end, hex[(val >> shift) & 15]); + shift -= 4; + } + return buf; +} + +static char* +format_ip4( char* buf, char* end, uint32_t ip ) +{ + buf = format_unsigned( buf, end, (unsigned)(ip >> 24) ); + buf = format_char( buf, end, '.'); + buf = format_unsigned( buf, end, (unsigned)((ip >> 16) & 255)); + buf = format_char( buf, end, '.'); + buf = format_unsigned( buf, end, (unsigned)((ip >> 8) & 255)); + buf = format_char( buf, end, '.'); + buf = format_unsigned( buf, end, (unsigned)(ip & 255)); + return buf; +} + +static char* +format_ip6( char* buf, char* end, const uint8_t* ip6 ) +{ + int nn; + for (nn = 0; nn < 8; nn++) { + int val = (ip6[0] << 16) | ip6[1]; + ip6 += 2; + if (nn > 0) + buf = format_char(buf, end, ':'); + if (val == 0) + continue; + buf = format_hex(buf, end, val, 4); + } + return buf; +} + +const char* +sock_address_to_string( const SockAddress* a ) +{ + static char buf0[PATH_MAX]; + char *buf = buf0, *end = buf + sizeof(buf0); + + switch (a->family) { + case SOCKET_INET: + buf = format_ip4( buf, end, a->u.inet.address ); + buf = format_char( buf, end, ':' ); + buf = format_unsigned( buf, end, (unsigned) a->u.inet.port ); + break; + + case SOCKET_IN6: + buf = format_ip6( buf, end, a->u.in6.address ); + buf = format_char( buf, end, ':' ); + buf = format_unsigned( buf, end, (unsigned) a->u.in6.port ); + break; + + case SOCKET_UNIX: + buf = format_str( buf, end, a->u._unix.path ); + break; + + default: + return NULL; + } + + return buf0; +} + +int +sock_address_equal( const SockAddress* a, const SockAddress* b ) +{ + if (a->family != b->family) + return 0; + + switch (a->family) { + case SOCKET_INET: + return (a->u.inet.address == b->u.inet.address && + a->u.inet.port == b->u.inet.port); + + case SOCKET_IN6: + return (!memcmp(a->u.in6.address, b->u.in6.address, 16) && + a->u.in6.port == b->u.in6.port); + + case SOCKET_UNIX: + return (!strcmp(a->u._unix.path, b->u._unix.path)); + + default: + return 0; + } +} + +int +sock_address_get_port( const SockAddress* a ) +{ + switch (a->family) { + case SOCKET_INET: + return a->u.inet.port; + case SOCKET_IN6: + return a->u.in6.port; + default: + return -1; + } +} + +void +sock_address_set_port( SockAddress* a, uint16_t port ) +{ + switch (a->family) { + case SOCKET_INET: + a->u.inet.port = port; + break; + case SOCKET_IN6: + a->u.in6.port = port; + break; + default: + ; + } +} + +const char* +sock_address_get_path( const SockAddress* a ) +{ + if (a->family == SOCKET_UNIX) + return a->u._unix.path; + else + return NULL; +} + +int +sock_address_get_ip( const SockAddress* a ) +{ + if (a->family == SOCKET_INET) + return a->u.inet.address; + + return -1; +} + +#if 0 +char* +bufprint_sock_address( char* p, char* end, const SockAddress* a ) +{ + switch (a->family) { + case SOCKET_INET: + { + uint32_t ip = a->u.inet.address; + + return bufprint( p, end, "%d.%d.%d.%d:%d", + (ip >> 24) & 255, (ip >> 16) & 255, + (ip >> 8) & 255, ip & 255, + a->u.inet.port ); + } + case SOCKET_IN6: + { + int nn = 0; + const char* column = ""; + const uint8_t* tab = a->u.in6.address; + for (nn = 0; nn < 16; nn += 2) { + p = bufprint(p, end, "%s%04x", column, (tab[n] << 8) | tab[n+1]); + column = ":"; + } + return bufprint(p, end, ":%d", a->u.in6.port); + } + case SOCKET_UNIX: + { + return bufprint(p, end, "%s", a->u._unix.path); + } + default: + return p; + } +} +#endif + +static int +sock_address_to_bsd( const SockAddress* a, sockaddr_storage* paddress, socklen_t *psize ) +{ + switch (a->family) { + case SOCKET_INET: + { + struct sockaddr_in* dst = paddress->in; + + *psize = sizeof(*dst); + + memset( paddress, 0, *psize ); + + dst->sin_family = AF_INET; + dst->sin_port = htons(a->u.inet.port); + dst->sin_addr.s_addr = htonl(a->u.inet.address); + } + break; + +#if HAVE_IN6_SOCKETS + case SOCKET_IN6: + { + struct sockaddr_in6* dst = paddress->in6; + + *psize = sizeof(*dst); + + memset( paddress, 0, *psize ); + + dst->sin6_family = AF_INET6; + dst->sin6_port = htons(a->u.in6.port); + memcpy( dst->sin6_addr.s6_addr, a->u.in6.address, 16 ); + } + break; +#endif /* HAVE_IN6_SOCKETS */ + +#if HAVE_UNIX_SOCKETS + case SOCKET_UNIX: + { + int slen = strlen(a->u._unix.path); + struct sockaddr_un* dst = paddress->un; + + if (slen >= (int)UNIX_PATH_MAX) + return -1; + + memset( dst, 0, sizeof(*dst) ); + + dst->sun_family = AF_LOCAL; + memcpy( dst->sun_path, a->u._unix.path, slen ); + dst->sun_path[slen] = 0; + + *psize = (char*)&dst->sun_path[slen+1] - (char*)dst; + } + break; +#endif /* HAVE_UNIX_SOCKETS */ + + default: + return _set_errno(EINVAL); + } + + return 0; +} + +static int +sock_address_from_bsd( SockAddress* a, const void* from, size_t fromlen ) +{ + switch (((struct sockaddr *)from)->sa_family) { + case AF_INET: + { + const struct sockaddr_in* src = from; + + if (fromlen < sizeof(*src)) + return _set_errno(EINVAL); + + a->family = SOCKET_INET; + a->u.inet.port = ntohs(src->sin_port); + a->u.inet.address = ntohl(src->sin_addr.s_addr); + } + break; + +#ifdef HAVE_IN6_SOCKETS + case AF_INET6: + { + const struct sockaddr_in6* src = from; + + if (fromlen < sizeof(*src)) + return _set_errno(EINVAL); + + a->family = SOCKET_IN6; + a->u.in6.port = ntohs(src->sin6_port); + memcpy(a->u.in6.address, src->sin6_addr.s6_addr, 16); + } + break; +#endif + +#ifdef HAVE_UNIX_SOCKETS + case AF_LOCAL: + { + const struct sockaddr_un* src = from; + char* end; + + if (fromlen < sizeof(*src)) + return _set_errno(EINVAL); + + /* check that the path is zero-terminated */ + end = memchr(src->sun_path, 0, UNIX_PATH_MAX); + if (end == NULL) + return _set_errno(EINVAL); + + a->family = SOCKET_UNIX; + a->u._unix.owner = 1; + a->u._unix.path = strdup(src->sun_path); + } + break; +#endif + + default: + return _set_errno(EINVAL); + } + return 0; +} + + +int +sock_address_init_resolve( SockAddress* a, const char* hostname, uint16_t port, int preferIn6 ) +{ + struct addrinfo hints[1]; + struct addrinfo* res; + int ret; + + memset(hints, 0, sizeof(hints)); + hints->ai_family = preferIn6 ? AF_INET6 : AF_UNSPEC; + + ret = getaddrinfo(hostname, NULL, hints, &res); + if (ret != 0) { + int err; + + switch (ret) { + case EAI_AGAIN: /* server is down */ + case EAI_FAIL: /* server is sick */ + err = EHOSTDOWN; + break; + +#ifdef EAI_NODATA + case EAI_NODATA: +#endif + case EAI_NONAME: + err = ENOENT; + break; + + case EAI_MEMORY: + err = ENOMEM; + break; + + default: + err = EINVAL; + } + return _set_errno(err); + } + + /* Parse the returned list of addresses. */ + { + struct addrinfo* res_ipv4 = NULL; + struct addrinfo* res_ipv6 = NULL; + struct addrinfo* r; + + /* If preferIn6 is false, we stop on the first IPv4 address, + * otherwise, we stop on the first IPv6 one + */ + for (r = res; r != NULL; r = r->ai_next) { + if (r->ai_family == AF_INET && res_ipv4 == NULL) { + res_ipv4 = r; + if (!preferIn6) + break; + } + else if (r->ai_family == AF_INET6 && res_ipv6 == NULL) { + res_ipv6 = r; + if (preferIn6) + break; + } + } + + /* Select the best address in 'r', which will be NULL + * if there is no corresponding address. + */ + if (preferIn6) { + r = res_ipv6; + if (r == NULL) + r = res_ipv4; + } else { + r = res_ipv4; + if (r == NULL) + r = res_ipv6; + } + + if (r == NULL) { + ret = _set_errno(ENOENT); + goto Exit; + } + + /* Convert to a SockAddress */ + ret = sock_address_from_bsd( a, r->ai_addr, r->ai_addrlen ); + if (ret < 0) + goto Exit; + } + + /* need to set the port */ + switch (a->family) { + case SOCKET_INET: a->u.inet.port = port; break; + case SOCKET_IN6: a->u.in6.port = port; break; + default: ; + } + +Exit: + freeaddrinfo(res); + return ret; +} + +/* The Winsock headers for mingw lack some definitions */ +#ifndef AI_ADDRCONFIG +# define AI_ADDRCONFIG 0 +#endif + +SockAddress** +sock_address_list_create( const char* hostname, + const char* port, + unsigned flags ) +{ + SockAddress** list = NULL; + SockAddress* addr; + int nn, count, ret; + struct addrinfo ai, *res, *e; + + memset(&ai, 0, sizeof(ai)); + ai.ai_flags |= AI_ADDRCONFIG; + ai.ai_family = PF_UNSPEC; + + if (flags & SOCKET_LIST_FORCE_INET) + ai.ai_family = PF_INET; + else if (flags & SOCKET_LIST_FORCE_IN6) + ai.ai_family = PF_INET6; + + if (flags & SOCKET_LIST_PASSIVE) + ai.ai_flags |= AI_PASSIVE; + else + ai.ai_flags |= AI_CANONNAME; + + if (flags & SOCKET_LIST_DGRAM) + ai.ai_socktype = SOCK_DGRAM; + + while (1) { + struct addrinfo hints = ai; + + ret = getaddrinfo(hostname, port, &hints, &res); + if (ret == 0) + break; + + switch (ret) { +#ifdef EAI_ADDRFAMILY + case EAI_ADDRFAMILY: +#endif + case EAI_NODATA: + _set_errno(ENOENT); + break; + case EAI_FAMILY: + _set_errno(EAFNOSUPPORT); + break; + case EAI_AGAIN: + _set_errno(EAGAIN); + break; +#ifdef EAI_SYSTEM + case EAI_SYSTEM: + if (errno == EINTR) + continue; + break; +#endif + default: + _set_errno(EINVAL); + } + return NULL; + } + + /* allocate result list */ + for (count = 0, e = res; e != NULL; e = e->ai_next) + count += 1; + + AARRAY_NEW(list, count+1); + AARRAY_NEW(addr, count); + + for (nn = 0, e = res; e != NULL; e = e->ai_next) { + + ret = sock_address_from_bsd(addr, e->ai_addr, e->ai_addrlen); + if (ret < 0) + continue; + + list[nn++] = addr++; + } + list[nn] = NULL; + freeaddrinfo(res); + return list; +} + +SockAddress** +sock_address_list_create2(const char* host_and_port, unsigned flags ) +{ + char host_name[512]; + const char* actual_host_name = "localhost"; + // Parse host and port name. + const char* port_name = strchr(host_and_port, ':'); + if (port_name != NULL) { + int to_copy = MIN((int)sizeof(host_name)-1, port_name - host_and_port); + if (to_copy != 0) { + memcpy(host_name, host_and_port, to_copy); + host_name[to_copy] = '\0'; + actual_host_name = host_name; + port_name++; + } else { + return NULL; + } + } else { + port_name = host_and_port; + } + // Make sure that port_name is not empty. + if (port_name[0] == '\0') { + return NULL; + } + return sock_address_list_create(actual_host_name, port_name, flags); +} + +void +sock_address_list_free( SockAddress** list ) +{ + int nn; + SockAddress* addr; + + if (list == NULL) + return; + + addr = list[0]; + for (nn = 0; list[nn] != NULL; nn++) { + sock_address_done(list[nn]); + list[nn] = NULL; + } + AFREE(addr); + AFREE(list); +} + +int +sock_address_get_numeric_info( SockAddress* a, + char* host, + size_t hostlen, + char* serv, + size_t servlen ) +{ + struct sockaddr* saddr; + socklen_t slen; + int ret; + + switch (a->family) { + case SOCKET_INET: + saddr = (struct sockaddr*) &a->u.inet.address; + slen = sizeof(a->u.inet.address); + break; + +#if HAVE_IN6_SOCKET + case SOCKET_IN6: + saddr = (struct sockaddr*) &a->u.in6.address; + slen = sizeof(a->u.in6.address); + break; +#endif + default: + return _set_errno(EINVAL); + } + + ret = getnameinfo( saddr, slen, host, hostlen, serv, servlen, + NI_NUMERICHOST | NI_NUMERICSERV ); + + switch (ret) { + case 0: + break; + case EAI_AGAIN: + ret = EAGAIN; + break; + default: + ret = EINVAL; + } + return ret; +} + +int +socket_create( SocketFamily family, SocketType type ) +{ + int ret; + int sfamily = socket_family_to_bsd(family); + int stype = socket_type_to_bsd(type); + + if (sfamily < 0 || stype < 0) { + return _set_errno(EINVAL); + } + + QSOCKET_CALL(ret, socket(sfamily, stype, 0)); + if (ret < 0) + return _fix_errno(); + + return ret; +} + + +int +socket_create_inet( SocketType type ) +{ + return socket_create( SOCKET_INET, type ); +} + +#if HAVE_IN6_SOCKETS +int +socket_create_in6 ( SocketType type ) +{ + return socket_create( SOCKET_IN6, type ); +} +#endif + +#if HAVE_UNIX_SOCKETS +int +socket_create_unix( SocketType type ) +{ + return socket_create( SOCKET_UNIX, type ); +} +#endif + +int socket_can_read(int fd) +{ +#ifdef _WIN32 + unsigned long opt; + + if (ioctlsocket(fd, FIONREAD, &opt) < 0) + return 0; + + return opt; +#else + int opt; + + if (ioctl(fd, FIONREAD, &opt) < 0) + return 0; + + return opt; +#endif +} + +#define SOCKET_CALL(cmd) \ + int ret; \ + QSOCKET_CALL(ret, (cmd)); \ + if (ret < 0) \ + return _fix_errno(); \ + return ret; \ + +int +socket_send(int fd, const void* buf, int buflen) +{ + SOCKET_CALL(send(fd, buf, buflen, 0)) +} + +int +socket_send_oob( int fd, const void* buf, int buflen ) +{ + SOCKET_CALL(send(fd, buf, buflen, MSG_OOB)); +} + +int +socket_sendto(int fd, const void* buf, int buflen, const SockAddress* to) +{ + sockaddr_storage sa; + socklen_t salen; + + if (sock_address_to_bsd(to, &sa, &salen) < 0) + return -1; + + SOCKET_CALL(sendto(fd, buf, buflen, 0, sa.sa, salen)); +} + +int +socket_recv(int fd, void* buf, int len) +{ + SOCKET_CALL(recv(fd, buf, len, 0)); +} + +int +socket_recvfrom(int fd, void* buf, int len, SockAddress* from) +{ + sockaddr_storage sa; + socklen_t salen = sizeof(sa); + int ret; + + QSOCKET_CALL(ret,recvfrom(fd,buf,len,0,sa.sa,&salen)); + if (ret < 0) + return _fix_errno(); + + if (sock_address_from_bsd(from, &sa, salen) < 0) + return -1; + + return ret; +} + +int +socket_connect( int fd, const SockAddress* address ) +{ + sockaddr_storage addr; + socklen_t addrlen; + + if (sock_address_to_bsd(address, &addr, &addrlen) < 0) + return -1; + + SOCKET_CALL(connect(fd,addr.sa,addrlen)); +} + +int +socket_bind( int fd, const SockAddress* address ) +{ + sockaddr_storage addr; + socklen_t addrlen; + + if (sock_address_to_bsd(address, &addr, &addrlen) < 0) + return -1; + + SOCKET_CALL(bind(fd, addr.sa, addrlen)); +} + +int +socket_get_address( int fd, SockAddress* address ) +{ + sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + int ret; + + QSOCKET_CALL(ret, getsockname(fd, addr.sa, &addrlen)); + if (ret < 0) + return _fix_errno(); + + return sock_address_from_bsd(address, &addr, addrlen); +} + +int +socket_get_peer_address( int fd, SockAddress* address ) +{ + sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + int ret; + + QSOCKET_CALL(ret, getpeername(fd, addr.sa, &addrlen)); + if (ret < 0) + return _fix_errno(); + + return sock_address_from_bsd(address, &addr, addrlen); +} + +int +socket_listen( int fd, int backlog ) +{ + SOCKET_CALL(listen(fd, backlog)); +} + +int +socket_accept( int fd, SockAddress* address ) +{ + sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + int ret; + + QSOCKET_CALL(ret, accept(fd, addr.sa, &addrlen)); + if (ret < 0) + return _fix_errno(); + + if (address) { + if (sock_address_from_bsd(address, &addr, addrlen) < 0) { + socket_close(ret); + return -1; + } + } + return ret; +} + +static int +socket_getoption(int fd, int domain, int option, int defaut) +{ + int ret; + while (1) { +#ifdef _WIN32 + DWORD opt = (DWORD)-1; +#else + int opt = -1; +#endif + socklen_t optlen = sizeof(opt); + ret = getsockopt(fd, domain, option, (char*)&opt, &optlen); + if (ret == 0) + return (int)opt; + if (errno != EINTR) + return defaut; + } +#undef OPT_CAST +} + + +SocketType socket_get_type(int fd) +{ + int so_type = socket_getoption(fd, SOL_SOCKET, SO_TYPE, -1); + return socket_type_from_bsd(so_type); +} + +int socket_set_nonblock(int fd) +{ +#ifdef _WIN32 + unsigned long opt = 1; + return ioctlsocket(fd, FIONBIO, &opt); +#else + int flags = fcntl(fd, F_GETFL); + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +#endif +} + +int socket_set_blocking(int fd) +{ +#ifdef _WIN32 + unsigned long opt = 0; + return ioctlsocket(fd, FIONBIO, &opt); +#else + int flags = fcntl(fd, F_GETFL); + return fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); +#endif +} + +static int +socket_setoption(int fd, int domain, int option, int _flag) +{ +#ifdef _WIN32 + DWORD flag = (DWORD) _flag; +#else + int flag = _flag; +#endif + return setsockopt( fd, domain, option, (const char*)&flag, sizeof(flag) ); +} + +int socket_set_xreuseaddr(int fd) +{ +#ifdef _WIN32 + /* on Windows, SO_REUSEADDR is used to indicate that several programs can + * bind to the same port. this is completely different from the Unix + * semantics. instead of SO_EXCLUSIVEADDR to ensure that explicitely prevent + * this. + */ + return socket_setoption(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1); +#else + return socket_setoption(fd, SOL_SOCKET, SO_REUSEADDR, 1); +#endif +} + + +int socket_set_oobinline(int fd) +{ + return socket_setoption(fd, SOL_SOCKET, SO_OOBINLINE, 1); +} + + +int socket_set_nodelay(int fd) +{ + return socket_setoption(fd, IPPROTO_TCP, TCP_NODELAY, 1); +} + +int socket_set_ipv6only(int fd) +{ +/* IPV6_ONLY is only supported since Vista on Windows, + * and the Mingw headers lack its definition anyway. + */ +#if defined(_WIN32) && !defined(IPV6_V6ONLY) + return 0; +#else + return socket_setoption(fd, IPPROTO_IPV6, IPV6_V6ONLY, 1); +#endif +} + + +int socket_get_error(int fd) +{ + return socket_getoption(fd, SOL_SOCKET, SO_ERROR, -1); +} + +#ifdef _WIN32 +#include + +static void socket_cleanup(void) +{ + WSACleanup(); +} + +int socket_init(void) +{ + WSADATA Data; + int ret, err; + + ret = WSAStartup(MAKEWORD(2,2), &Data); + if (ret != 0) { + err = WSAGetLastError(); + return -1; + } + atexit(socket_cleanup); + return 0; +} + +#else /* !_WIN32 */ + +int socket_init(void) +{ + return 0; /* nothing to do on Unix */ +} + +#endif /* !_WIN32 */ + +#ifdef _WIN32 + +static void +socket_close_handler( void* _fd ) +{ + int fd = (int)_fd; + int ret; + char buff[64]; + + /* we want to drain the read side of the socket before closing it */ + do { + ret = recv( fd, buff, sizeof(buff), 0 ); + } while (ret < 0 && WSAGetLastError() == WSAEINTR); + + if (ret < 0 && WSAGetLastError() == EWOULDBLOCK) + return; + + qemu_set_fd_handler( fd, NULL, NULL, NULL ); + closesocket( fd ); +} + +void +socket_close( int fd ) +{ + int old_errno = errno; + + shutdown( fd, SD_BOTH ); + /* we want to drain the socket before closing it */ + qemu_set_fd_handler( fd, socket_close_handler, NULL, (void*)fd ); + + errno = old_errno; +} + +#else /* !_WIN32 */ + +#include + +void +socket_close( int fd ) +{ + int old_errno = errno; + + shutdown( fd, SHUT_RDWR ); + close( fd ); + + errno = old_errno; +} + +#endif /* !_WIN32 */ + + +static int +socket_bind_server( int s, const SockAddress* to, SocketType type ) +{ + socket_set_xreuseaddr(s); + + if (socket_bind(s, to) < 0) { + D("could not bind server socket address %s: %s", + sock_address_to_string(to), errno_str); + goto FAIL; + } + + if (type == SOCKET_STREAM) { + if (socket_listen(s, 4) < 0) { + D("could not listen server socket %s: %s", + sock_address_to_string(to), errno_str); + goto FAIL; + } + } + return s; + +FAIL: + socket_close(s); + return -1; +} + + +static int +socket_connect_client( int s, const SockAddress* to ) +{ + if (socket_connect(s, to) < 0) { + D( "could not connect client socket to %s: %s\n", + sock_address_to_string(to), errno_str ); + socket_close(s); + return -1; + } + + socket_set_nonblock( s ); + return s; +} + + +static int +socket_in_server( int address, int port, SocketType type ) +{ + SockAddress addr; + int s; + + sock_address_init_inet( &addr, address, port ); + s = socket_create_inet( type ); + if (s < 0) + return -1; + + return socket_bind_server( s, &addr, type ); +} + + +static int +socket_in_client( SockAddress* to, SocketType type ) +{ + int s; + + s = socket_create_inet( type ); + if (s < 0) return -1; + + return socket_connect_client( s, to ); +} + + +int +socket_loopback_server( int port, SocketType type ) +{ + return socket_in_server( SOCK_ADDRESS_INET_LOOPBACK, port, type ); +} + +int +socket_loopback_client( int port, SocketType type ) +{ + SockAddress addr; + + sock_address_init_inet( &addr, SOCK_ADDRESS_INET_LOOPBACK, port ); + return socket_in_client( &addr, type ); +} + + +int +socket_network_client( const char* host, int port, SocketType type ) +{ + SockAddress addr; + + if (sock_address_init_resolve( &addr, host, port, 0) < 0) + return -1; + + return socket_in_client( &addr, type ); +} + + +int +socket_anyaddr_server( int port, SocketType type ) +{ + return socket_in_server( SOCK_ADDRESS_INET_ANY, port, type ); +} + +int +socket_accept_any( int server_fd ) +{ + int fd; + + QSOCKET_CALL(fd, accept( server_fd, NULL, 0 )); + if (fd < 0) { + D( "could not accept client connection from fd %d: %s", + server_fd, errno_str ); + return -1; + } + + /* set to non-blocking */ + socket_set_nonblock( fd ); + return fd; +} + + +#if HAVE_UNIX_SOCKETS + +int +socket_unix_server( const char* name, SocketType type ) +{ + SockAddress addr; + int s, ret; + + s = socket_create_unix( type ); + if (s < 0) + return -1; + + sock_address_init_unix( &addr, name ); + + do { + ret = unlink( name ); + } while (ret < 0 && errno == EINTR); + + ret = socket_bind_server( s, &addr, type ); + + sock_address_done( &addr ); + return ret; +} + +int +socket_unix_client( const char* name, SocketType type ) +{ + SockAddress addr; + int s, ret; + + s = socket_create_unix(type); + if (s < 0) + return -1; + + sock_address_init_unix( &addr, name ); + + ret = socket_connect_client( s, &addr ); + + sock_address_done( &addr ); + return ret; +} + +#endif /* HAVE_UNIX_SOCKETS */ + + + +int +socket_pair(int *fd1, int *fd2) +{ +#ifndef _WIN32 + int fds[2]; + int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + + if (!ret) { + socket_set_nonblock(fds[0]); + socket_set_nonblock(fds[1]); + *fd1 = fds[0]; + *fd2 = fds[1]; + } + return ret; +#else /* _WIN32 */ + /* on Windows, select() only works with network sockets, which + * means we absolutely cannot use Win32 PIPEs to implement + * socket pairs with the current event loop implementation. + * We're going to do like Cygwin: create a random pair + * of localhost TCP sockets and connect them together + */ + int s0, s1, s2, port; + struct sockaddr_in sockin; + socklen_t len; + + /* first, create the 'server' socket. + * a port number of 0 means 'any port between 1024 and 5000. + * see Winsock bind() documentation for details */ + s0 = socket_loopback_server( 0, SOCK_STREAM ); + if (s0 < 0) + return -1; + + /* now connect a client socket to it, we first need to + * extract the server socket's port number */ + len = sizeof sockin; + if (getsockname(s0, (struct sockaddr*) &sockin, &len) < 0) { + closesocket (s0); + return -1; + } + + port = ntohs(sockin.sin_port); + s2 = socket_loopback_client( port, SOCK_STREAM ); + if (s2 < 0) { + closesocket(s0); + return -1; + } + + /* we need to accept the connection on the server socket + * this will create the second socket for the pair + */ + len = sizeof sockin; + s1 = accept(s0, (struct sockaddr*) &sockin, &len); + if (s1 == INVALID_SOCKET) { + closesocket (s0); + closesocket (s2); + return -1; + } + socket_set_nonblock(s1); + + /* close server socket */ + closesocket(s0); + *fd1 = s1; + *fd2 = s2; + return 0; +#endif /* _WIN32 */ +} + + + +int +socket_mcast_inet_add_membership( int s, uint32_t ip ) +{ + struct ip_mreq imr; + + imr.imr_multiaddr.s_addr = htonl(ip); + imr.imr_interface.s_addr = htonl(INADDR_ANY); + + if ( setsockopt( s, IPPROTO_IP, IP_ADD_MEMBERSHIP, + (const char *)&imr, + sizeof(struct ip_mreq)) < 0 ) + { + return _fix_errno(); + } + return 0; +} + +int +socket_mcast_inet_drop_membership( int s, uint32_t ip ) +{ + struct ip_mreq imr; + + imr.imr_multiaddr.s_addr = htonl(ip); + imr.imr_interface.s_addr = htonl(INADDR_ANY); + + if ( setsockopt( s, IPPROTO_IP, IP_DROP_MEMBERSHIP, + (const char *)&imr, + sizeof(struct ip_mreq)) < 0 ) + { + return _fix_errno(); + } + return 0; +} + +int +socket_mcast_inet_set_loop( int s, int enabled ) +{ + return socket_setoption( s, IPPROTO_IP, IP_MULTICAST_LOOP, !!enabled ); +} + +int +socket_mcast_inet_set_ttl( int s, int ttl ) +{ + return socket_setoption( s, IPPROTO_IP, IP_MULTICAST_TTL, ttl ); +} + + +char* +host_name( void ) +{ + static char buf[256]; /* 255 is the max host name length supported by DNS */ + int ret; + + QSOCKET_CALL(ret, gethostname(buf, sizeof(buf))); + + if (ret < 0) + return "localhost"; + else + return buf; +} diff --git a/tools/emulator/opengl/tests/event_injector/sockets.h b/tools/emulator/opengl/tests/event_injector/sockets.h new file mode 100644 index 000000000..ea48c5f16 --- /dev/null +++ b/tools/emulator/opengl/tests/event_injector/sockets.h @@ -0,0 +1,432 @@ +/* +* Copyright (C) 2011 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +/* headers to use the BSD sockets */ +#ifndef ANDROID_SOCKET_H +#define ANDROID_SOCKET_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* we're going to hide the implementation details of sockets behind + * a simple wrapper interface declared here. + * + * all socket operations set the global 'errno' variable on error. + * this is unlike Winsock which instead modifies another internal + * variable accessed through WSAGetLastError() and WSASetLastError() + */ + +/* the wrapper will convert any Winsock error message into an errno + * code for you. There are however a few standard Unix error codes + * that are not defined by the MS C library headers, so we add them + * here. We use the official Winsock error codes, which are documented + * even though we don't want to include the Winsock headers + */ +#ifdef _WIN32 +# ifndef EINTR +# define EINTR 10004 +# endif +# ifndef EAGAIN +# define EAGAIN 10035 +# endif +# ifndef EWOULDBLOCK +# define EWOULDBLOCK EAGAIN +# endif +# ifndef EINPROGRESS +# define EINPROGRESS 10036 +# endif +# ifndef EALREADY +# define EALREADY 10037 +# endif +# ifndef EDESTADDRREQ +# define EDESTADDRREQ 10039 +# endif +# ifndef EMSGSIZE +# define EMSGSIZE 10040 +# endif +# ifndef EPROTOTYPE +# define EPROTOTYPE 10041 +# endif +# ifndef ENOPROTOOPT +# define ENOPROTOOPT 10042 +# endif +# ifndef EAFNOSUPPORT +# define EAFNOSUPPORT 10047 +# endif +# ifndef EADDRINUSE +# define EADDRINUSE 10048 +# endif +# ifndef EADDRNOTAVAIL +# define EADDRNOTAVAIL 10049 +# endif +# ifndef ENETDOWN +# define ENETDOWN 10050 +# endif +# ifndef ENETUNREACH +# define ENETUNREACH 10051 +# endif +# ifndef ENETRESET +# define ENETRESET 10052 +# endif +# ifndef ECONNABORTED +# define ECONNABORTED 10053 +# endif +# ifndef ECONNRESET +# define ECONNRESET 10054 +# endif +# ifndef ENOBUFS +# define ENOBUFS 10055 +# endif +# ifndef EISCONN +# define EISCONN 10056 +# endif +# ifndef ENOTCONN +# define ENOTCONN 10057 +# endif +# ifndef ESHUTDOWN +# define ESHUTDOWN 10058 +# endif +# ifndef ETOOMANYREFS +# define ETOOMANYREFS 10059 +# endif +# ifndef ETIMEDOUT +# define ETIMEDOUT 10060 +# endif +# ifndef ECONNREFUSED +# define ECONNREFUSED 10061 +# endif +# ifndef ELOOP +# define ELOOP 10062 +# endif +# ifndef EHOSTDOWN +# define EHOSTDOWN 10064 +# endif +# ifndef EHOSTUNREACH +# define EHOSTUNREACH 10065 +# endif +#endif /* _WIN32 */ + +/* Define 'errno_str' as a handy macro to return the string + * corresponding to a given errno code. On Unix, this is + * equivalent to strerror(errno), but on Windows, this will + * take care of Winsock-originated errors as well. + */ +#ifdef _WIN32 + extern const char* _errno_str(void); +# define errno_str _errno_str() +#else +# define errno_str strerror(errno) +#endif + +/* always enable IPv6 sockets for now. + * the QEMU internal router is not capable of + * supporting them, but we plan to replace it + * with something better in the future. + */ +#define HAVE_IN6_SOCKETS 1 + +/* Unix sockets are not available on Win32 */ +#ifndef _WIN32 +# define HAVE_UNIX_SOCKETS 1 +#endif + +/* initialize the socket sub-system. this must be called before + * using any of the declarations below. + */ +int socket_init( void ); + +/* return the name of the current host */ +char* host_name( void ); + +/* supported socket types */ +typedef enum { + SOCKET_DGRAM = 0, + SOCKET_STREAM +} SocketType; + +/* supported socket families */ +typedef enum { + SOCKET_UNSPEC, + SOCKET_INET, + SOCKET_IN6, + SOCKET_UNIX +} SocketFamily; + +/* Generic socket address structure. Note that for Unix + * sockets, the path is stored in a heap-allocated block, + * unless the 'owner' field is cleared. If this is the case, + */ +typedef struct { + SocketFamily family; + union { + struct { + uint16_t port; + uint32_t address; + } inet; + struct { + uint16_t port; + uint8_t address[16]; + } in6; + struct { + int owner; + const char* path; + } _unix; + } u; +} SockAddress; + +#define SOCK_ADDRESS_INET_ANY 0x00000000 +#define SOCK_ADDRESS_INET_LOOPBACK 0x7f000001 + +/* initialize a new IPv4 socket address, the IP address and port are + * in host endianess. + */ +void sock_address_init_inet( SockAddress* a, uint32_t ip, uint16_t port ); + +/* Initialize an IPv6 socket address, the address is in network order + * and the port in host endianess. + */ +#if HAVE_IN6_SOCKETS +void sock_address_init_in6 ( SockAddress* a, const uint8_t* ip6[16], uint16_t port ); +#endif + +/* Intialize a Unix socket address, this will copy the 'path' string into the + * heap. You need to call sock_address_done() to release the copy + */ +#if HAVE_UNIX_SOCKETS +void sock_address_init_unix( SockAddress* a, const char* path ); +#endif + +/* Finalize a socket address, only needed for now for Unix addresses */ +void sock_address_done( SockAddress* a ); + +int sock_address_equal( const SockAddress* a, const SockAddress* b ); + +/* return a static string describing the address */ +const char* sock_address_to_string( const SockAddress* a ); + +static __inline__ +SocketFamily sock_address_get_family( const SockAddress* a ) +{ + return a->family; +} + +/* return the port number of a given socket address, or -1 if it's a Unix one */ +int sock_address_get_port( const SockAddress* a ); + +/* set the port number of a given socket address, don't do anything for Unix ones */ +void sock_address_set_port( SockAddress* a, uint16_t port ); + +/* return the path of a given Unix socket, returns NULL for non-Unix ones */ +const char* sock_address_get_path( const SockAddress* a ); + +/* return the inet address, or -1 if it's not SOCKET_INET */ +int sock_address_get_ip( const SockAddress* a ); + +/* bufprint a socket address into a human-readable string */ +char* bufprint_sock_address( char* p, char* end, const SockAddress* a ); + +/* resolve a hostname or decimal IPv4/IPv6 address into a socket address. + * returns 0 on success, or -1 on failure. Note that the values or errno + * set by this function are the following: + * + * EINVAL : invalid argument + * EHOSTDOWN : could not reach DNS server + * ENOENT : no host with this name, or host doesn't have any IP address + * ENOMEM : not enough memory to perform request + */ +int sock_address_init_resolve( SockAddress* a, + const char* hostname, + uint16_t port, + int preferIn6 ); + +int sock_address_get_numeric_info( SockAddress* a, + char* host, + size_t hostlen, + char* serv, + size_t servlen ); + +/* Support for listing all socket addresses of a given host */ +enum { + SOCKET_LIST_PASSIVE = (1 << 0), + SOCKET_LIST_FORCE_INET = (1 << 1), + SOCKET_LIST_FORCE_IN6 = (1 << 2), + SOCKET_LIST_DGRAM = (1 << 3), +}; + +/* resolve a host and service/port name into a list of SockAddress objects. + * returns a NULL-terminated array of SockAddress pointers on success, + * or NULL in case of failure, with the value of errno set to one of the + * following: + * + * EINVAL : invalid argument + * EHOSTDOWN : could not reach DNS server + * ENOENT : no host with this name, or host doesn't have IP address + * ENOMEM : not enough memory to perform request + * + * other system-level errors can also be set depending on the host sockets + * implementation. + * + * This function loops on EINTR so the caller shouldn't have to check for it. + */ +SockAddress** sock_address_list_create( const char* hostname, + const char* port, + unsigned flags ); + +/* resolve a string containing host and port name into a list of SockAddress + * objects. Parameter host_and_port should be in format [host:]port, where + * 'host' addresses the machine and must be resolvable into an IP address, and + * 'port' is a decimal numeric value for the port. 'host' is optional, and if + * ommited, localhost will be used. + * returns a NULL-terminated array of SockAddress pointers on success, + * or NULL in case of failure, with the value of errno set to one of the + * following: + * + * EINVAL : invalid argument + * EHOSTDOWN : could not reach DNS server + * ENOENT : no host with this name, or host doesn't have IP address + * ENOMEM : not enough memory to perform request + * + * other system-level errors can also be set depending on the host sockets + * implementation. + * + * This function loops on EINTR so the caller shouldn't have to check for it. + */ +SockAddress** sock_address_list_create2(const char* host_and_port, + unsigned flags ); + +void sock_address_list_free( SockAddress** list ); + +/* create a new socket, return the socket number of -1 on failure */ +int socket_create( SocketFamily family, SocketType type ); + +/* create a new socket intended for IPv4 communication. returns the socket number, + * or -1 on failure. + */ +int socket_create_inet( SocketType type ); + +/* create a new socket intended for IPv6 communication. returns the socket number, + * or -1 on failure. + */ +#if HAVE_IN6_SOCKETS +int socket_create_in6 ( SocketType type ); +#endif + +/* create a unix/local domain socket. returns the socket number, + * or -1 on failure. + */ +#if HAVE_UNIX_SOCKETS +int socket_create_unix( SocketType type ); +#endif + +/* return the type of a given socket */ +SocketType socket_get_type(int fd); + +/* set SO_REUSEADDR on Unix, SO_EXCLUSIVEADDR on Windows */ +int socket_set_xreuseaddr(int fd); + +/* set socket in non-blocking mode */ +int socket_set_nonblock(int fd); + +/* set socket in blocking mode */ +int socket_set_blocking(int fd); + +/* disable the TCP Nagle algorithm for lower latency */ +int socket_set_nodelay(int fd); + +/* send OOB data inline for this socket */ +int socket_set_oobinline(int fd); + +/* force listening to IPv6 interfaces only */ +int socket_set_ipv6only(int fd); + +/* retrieve last socket error code */ +int socket_get_error(int fd); + +/* close an opened socket. Note that this is unlike the Unix 'close' because: + * - it will properly shutdown the socket in the background + * - it does not modify errno + */ +void socket_close( int fd ); + +/* the following functions are equivalent to the BSD sockets ones + */ +int socket_recv ( int fd, void* buf, int buflen ); +int socket_recvfrom( int fd, void* buf, int buflen, SockAddress* from ); + +int socket_send ( int fd, const void* buf, int buflen ); +int socket_send_oob( int fd, const void* buf, int buflen ); +int socket_sendto( int fd, const void* buf, int buflen, const SockAddress* to ); + +int socket_connect( int fd, const SockAddress* address ); +int socket_bind( int fd, const SockAddress* address ); +int socket_get_address( int fd, SockAddress* address ); +int socket_get_peer_address( int fd, SockAddress* address ); +int socket_listen( int fd, int backlog ); +int socket_accept( int fd, SockAddress* address ); + +/* returns the number of bytes that can be read from a socket */ +int socket_can_read( int fd ); + +/* this call creates a pair of non-blocking sockets connected + * to each other. this is equivalent to calling the Unix function: + * socketpair(AF_LOCAL,SOCK_STREAM,0,&fds) + * + * on Windows, this will use a pair of TCP loopback sockets instead + * returns 0 on success, -1 on error. + */ +int socket_pair(int *fd1, int *fd2); + +/* create a server socket listening on the host's loopback interface */ +int socket_loopback_server( int port, SocketType type ); + +/* connect to a port on the host's loopback interface */ +int socket_loopback_client( int port, SocketType type ); + +/* create a server socket listening to a Unix domain path */ +#if HAVE_UNIX_SOCKETS +int socket_unix_server( const char* name, SocketType type ); +#endif + +/* create a Unix sockets and connects it to a Unix server */ +#if HAVE_UNIX_SOCKETS +int socket_unix_client( const char* name, SocketType type ); +#endif + +/* create an IPv4 client socket and connect it to a given host */ +int socket_network_client( const char* host, int port, SocketType type ); + +/* create an IPv4 socket and binds it to a given port of the host's interface */ +int socket_anyaddr_server( int port, SocketType type ); + +/* accept a connection from the host's any interface, return the new socket + * descriptor or -1 */ +int socket_accept_any( int server_fd ); + + +int socket_mcast_inet_add_membership( int s, uint32_t ip ); +int socket_mcast_inet_drop_membership( int s, uint32_t ip ); +int socket_mcast_inet_set_loop( int s, int enabled ); +int socket_mcast_inet_set_ttl( int s, int ttl ); + +#ifdef __cplusplus +} +#endif + +#endif /* ANDROID_SOCKET_H */