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 */