diff options
author | Vladimir Chtchetkine <vchtchetkine@google.com> | 2010-12-06 10:55:11 -0800 |
---|---|---|
committer | Vladimir Chtchetkine <vchtchetkine@google.com> | 2010-12-06 10:56:24 -0800 |
commit | d87b080495e71ada650b165a1f06616b433e6073 (patch) | |
tree | b33edad3c18c7e962539d8c94250cd9ee8baedf1 /android | |
parent | fced4df82222b898580557de7313d86b5d6934f3 (diff) | |
download | external_qemu-d87b080495e71ada650b165a1f06616b433e6073.zip external_qemu-d87b080495e71ada650b165a1f06616b433e6073.tar.gz external_qemu-d87b080495e71ada650b165a1f06616b433e6073.tar.bz2 |
Submit merged
Squashed commit of the following:
commit aeefab810c6331e2f96e81f20e4408b39dd3a2ca
Author: Vladimir Chtchetkine <vchtchetkine@google.com>
Date: Thu Dec 2 07:40:34 2010 -0800
Implement -attach-core UI option
Change-Id: I4168e2d707cab1b4873ee16d86d5126c1a316abf
Change-Id: I2da1ef5d53641f3c60d83d8d5ddf3aff34b0c6c7
Diffstat (limited to 'android')
-rw-r--r-- | android/cmdline-options.h | 1 | ||||
-rw-r--r-- | android/console.c | 50 | ||||
-rw-r--r-- | android/core-connection.c | 323 | ||||
-rw-r--r-- | android/core-connection.h | 133 | ||||
-rw-r--r-- | android/help.c | 15 | ||||
-rw-r--r-- | android/main-ui.c | 124 | ||||
-rw-r--r-- | android/sync-utils.c | 87 | ||||
-rw-r--r-- | android/sync-utils.h | 93 |
8 files changed, 795 insertions, 31 deletions
diff --git a/android/cmdline-options.h b/android/cmdline-options.h index fc30ebc..b65b29a 100644 --- a/android/cmdline-options.h +++ b/android/cmdline-options.h @@ -156,6 +156,7 @@ OPT_PARAM( memcheck, "<flags>", "enable memory access checking" ) #ifdef CONFIG_STANDALONE_UI OPT_PARAM( list_cores, "<host>", "list running core process" ) +OPT_PARAM( attach_core, "<console socket>", "attach to a running core process" ) #endif // CONFIG_STANDALONE_UI #undef CFG_FLAG diff --git a/android/console.c b/android/console.c index 42addba..5e37060 100644 --- a/android/console.c +++ b/android/console.c @@ -112,6 +112,8 @@ typedef struct ControlGlobalRec_ } ControlGlobalRec; +/* UI client currently attached to the core. */ +ControlClient attached_ui_client = NULL; static int control_global_add_redir( ControlGlobal global, @@ -212,6 +214,10 @@ control_client_destroy( ControlClient client ) D(( "destroying control client %p\n", client )); + if (client == attached_ui_client) { + attached_ui_client = NULL; + } + sock = control_client_detach( client ); if (sock >= 0) socket_close(sock); @@ -392,8 +398,9 @@ control_client_do_command( ControlClient client ) CommandDef subcmd; if (cmd->handler) { - if ( !cmd->handler( client, args ) ) + if ( !cmd->handler( client, args ) ) { control_write( client, "OK\r\n" ); + } break; } @@ -503,8 +510,8 @@ control_client_read( void* _client ) size = socket_recv( client->sock, buf, sizeof(buf) ); if (size < 0) { D(( "size < 0, exiting with %d: %s\n", errno, errno_str )); - if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) - control_client_destroy( client ); + if (errno != EWOULDBLOCK && errno != EAGAIN) + control_client_destroy( client ); return; } @@ -2364,12 +2371,43 @@ do_qemu_monitor( ControlClient client, char* args ) return 0; } +/* UI settings, passed to the core via -ui-settings command line parameter. */ +extern char* android_op_ui_settings; + +static int +do_attach_ui( ControlClient client, char* args ) +{ + // Make sure that there are no UI already attached to this console. + if (attached_ui_client != NULL) { + control_write( client, "KO: Another UI is attached to this core!\r\n" ); + control_client_destroy(client); + return -1; + } + + attached_ui_client = client; + + if (android_op_ui_settings != NULL) { + // Reply "OK" with the saved -ui-settings property. + char reply_buf[4096]; + snprintf(reply_buf, sizeof(reply_buf), "OK: %s\r\n", android_op_ui_settings); + control_write( client, reply_buf); + } else { + control_write( client, "OK\r\n"); + } + + return 0; +} + static const CommandDefRec qemu_commands[] = { { "monitor", "enter QEMU monitor", "Enter the QEMU virtual machine monitor\r\n", NULL, do_qemu_monitor, NULL }, + { "attach UI", "attach UI to the core", + "Attach UI to the core\r\n", + NULL, do_attach_ui, NULL }, + { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -2385,8 +2423,8 @@ static const CommandDefRec qemu_commands[] = static int do_kill( ControlClient client, char* args ) { - control_write( client, "OK: killing emulator, bye bye\r\n" ); - exit(0); + control_write( client, "OK: killing emulator, bye bye\r\n" ); + exit(0); } static const CommandDefRec main_commands[] = @@ -2410,7 +2448,7 @@ static const CommandDefRec main_commands[] = NULL, cdma_commands }, { "kill", "kill the emulator instance", NULL, NULL, - do_kill, NULL }, + do_kill, NULL }, { "network", "manage network settings", "allows you to manage the settings related to the network data connection of the\r\n" diff --git a/android/core-connection.c b/android/core-connection.c new file mode 100644 index 0000000..6e05be0 --- /dev/null +++ b/android/core-connection.c @@ -0,0 +1,323 @@ +/* Copyright (C) 2010 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ + +#include <unistd.h> + +#include "sockets.h" +#include "qemu-common.h" +#include "errno.h" +#include "iolooper.h" +#include "android/android.h" +#include "android/utils/debug.h" +#include "android/globals.h" +#include "android/core-connection.h" +#include "android/utils/system.h" + +/* Descriptor for a client, connected to the core via console port. */ +struct CoreConnection { + /* Socket address of the console. */ + SockAddress console_address; + + // Helper for performing sync I/O on the console socket. + SyncSocket* ssocket; + + /* Stream name. Can be: + * - NULL for the console itself. + * - "attach UI" for the attached UI client. + */ + char* stream_name; +}; + +/* + * Zero-terminates string buffer. + * Param: + * buf - Buffer containing the string. + * buf_size - Buffer size. + * eos - String size. + */ +static inline void +_zero_terminate(char* buf, size_t buf_size, size_t eos) +{ + if (eos < buf_size) { + buf[eos] = '\0'; + } else { + buf[buf_size - 1] = '\0'; + } +} + +/* + * Checks if console has replied with "OK" + * Param: + * reply - String containing console's reply + * Return: + * boolean: true if reply was "OK", or false otherwise. + */ +static int +_is_reply_ok(const char* reply, int reply_size) +{ + return (reply_size < 2) ? 0 : (reply[0] == 'O' && reply[1] == 'K'); +} + +/* + * Checks if console has replied with "KO" + * Param: + * reply - String containing console's reply + * Return: + * boolean: true if reply was "KO", or false otherwise. + */ +static int +_is_reply_ko(const char* reply, int reply_size) +{ + return (reply_size < 2) ? 0 : (reply[0] == 'K' && reply[1] == 'O'); +} + +SyncSocket* +core_connection_open_socket(SockAddress* sockaddr) +{ + SyncSocket* ssocket; + int status; + int64_t deadline; + char buf[512]; + + int fd = socket_create(sock_address_get_family(sockaddr), SOCKET_STREAM); + if (fd < 0) { + return NULL; + } + + socket_set_xreuseaddr(fd); + + // Create sync connection to the console. + ssocket = syncsocket_connect(fd, sockaddr, CORE_PORT_TIMEOUT_MS); + if (ssocket == NULL) { + derror("syncsocket_connect has failed: %s\n", errno_str); + socket_close(fd); + return NULL; + } + + // Upon successful connection the console will reply with two strings: + // "Android Console....", and "OK\r\n". Read them and check. + status = syncsocket_start_read(ssocket); + if (status < 0) { + derror("syncsocket_start_read has failed: %s\n", errno_str); + syncsocket_free(ssocket); + return NULL; + } + + deadline = iolooper_now() + CORE_PORT_TIMEOUT_MS; + // Read first line. + status = syncsocket_read_line_absolute(ssocket, buf, sizeof(buf), deadline); + if (status <= 0) { + derror("syncsocket_read_line_absolute has failed: %s\n", errno_str); + syncsocket_free(ssocket); + return NULL; + } + if (status < 15 || memcmp(buf, "Android Console", 15)) { + _zero_terminate(buf, sizeof(buf), status); + derror("console has failed the connection: %s\n", buf); + syncsocket_free(ssocket); + return NULL; + } + // Read second line + status = syncsocket_read_line_absolute(ssocket, buf, sizeof(buf), deadline); + syncsocket_stop_read(ssocket); + if (status < 2 || !_is_reply_ok(buf, status)) { + _zero_terminate(buf, sizeof(buf), status); + derror("unexpected reply from the console: %s\n", buf); + syncsocket_free(ssocket); + return NULL; + } + + return ssocket; +} + +CoreConnection* +core_connection_create(SockAddress* console_address) +{ + CoreConnection* desc; + ANEW0(desc); + desc->console_address = console_address[0]; + desc->ssocket = NULL; + desc->stream_name = NULL; + + return desc; +} + +void +core_connection_free(CoreConnection* desc) +{ + if (desc == NULL) { + return; + } + if (desc->ssocket != NULL) { + syncsocket_free(desc->ssocket); + } + if (desc->stream_name != NULL) { + free(desc->stream_name); + } + free(desc); +} + +int +core_connection_open(CoreConnection* desc) +{ + if (desc == NULL) { + errno = EINVAL; + return -1; + } + if (desc->ssocket != NULL) { + return 0; + } + + desc->ssocket = core_connection_open_socket(&desc->console_address); + + return (desc->ssocket != NULL) ? 0 : -1; +} + +void +core_connection_close(CoreConnection* desc) +{ + if (desc == NULL) { + return; + } + if (desc->ssocket != NULL) { + syncsocket_close(desc->ssocket); + } +} + +int +core_connection_write(CoreConnection* desc, + const void* buffer, + size_t to_write, + size_t* written_bytes) +{ + ssize_t written; + + int status = syncsocket_start_write(desc->ssocket); + if (status < 0) { + derror("syncsocket_start_write failed: %s\n", errno_str); + return status; + } + + written = + syncsocket_write(desc->ssocket, buffer, to_write, CORE_PORT_TIMEOUT_MS); + syncsocket_stop_write(desc->ssocket); + if (written <= 0) { + derror("syncsocket_write failed: %s\n", errno_str); + return -1; + } + if (written_bytes != NULL) { + *written_bytes = written; + } + + return 0; +} + +int +core_connection_read(CoreConnection* desc, + void* buffer, + size_t to_read, + size_t* read_bytes) +{ + ssize_t read_size; + + int status = syncsocket_start_read(desc->ssocket); + if (status < 0) { + derror("syncsocket_start_read failed: %s\n", errno_str); + return status; + } + + read_size = + syncsocket_read(desc->ssocket, buffer, to_read, CORE_PORT_TIMEOUT_MS); + syncsocket_stop_read(desc->ssocket); + if (read_size <= 0) { + derror("syncsocket_read failed: %s\n", errno_str); + return -1; + } + + if (read_bytes != NULL) { + *read_bytes = read_size; + } + return 0; +} + +int +core_connection_switch_stream(CoreConnection* desc, + const char* stream_name, + char** handshake) +{ + char buf[4096]; + int handshake_len; + int status; + int64_t deadline; + + *handshake = NULL; + if (desc == NULL || desc->stream_name != NULL || stream_name == NULL) { + errno = EINVAL; + return -1; + } + + // Prepare and write "switch" command. + snprintf(buf, sizeof(buf), "qemu %s\r\n", stream_name); + if (core_connection_write(desc, buf, strlen(buf), NULL)) { + return -1; + } + + // Read result / handshake + status = syncsocket_start_read(desc->ssocket); + if (status < 0) { + return -1; + } + deadline = iolooper_now() + CORE_PORT_TIMEOUT_MS; + handshake_len = + syncsocket_read_line_absolute(desc->ssocket, buf, sizeof(buf), deadline); + _zero_terminate(buf, sizeof(buf), handshake_len); + // Replace terminating "\r\n" with 0 + if (handshake_len >= 2 && buf[handshake_len - 2] == '\r') { + buf[handshake_len - 2] = '\0'; + } + printf("Handshake: %s\n", buf); + // Lets see what kind of response we've got here. + if (_is_reply_ok(buf, handshake_len)) { + *handshake = strdup(buf + 3); + desc->stream_name = strdup(stream_name); + // We expect an "OK" string here + status = syncsocket_read_line_absolute(desc->ssocket, buf, sizeof(buf), + deadline); + syncsocket_stop_read(desc->ssocket); + if (status < 0) { + derror("error reading console reply on stream switch: %s\n", errno_str); + return -1; + } else if (!_is_reply_ok(buf, status)) { + _zero_terminate(buf, sizeof(buf), status); + derror("unexpected console reply when switching streams: %s\n", buf); + return -1; + } + return 0; + } else if (_is_reply_ko(buf, handshake_len)) { + derror("console has rejected stream switch: %s\n", buf); + syncsocket_stop_read(desc->ssocket); + *handshake = strdup(buf + 3); + return -1; + } else { + // No OK, no KO? Should be an error! + derror("unexpected console reply when switching streams: %s\n", buf); + syncsocket_stop_read(desc->ssocket); + *handshake = strdup(buf); + return -1; + } +} + +void +core_connection_detach(CoreConnection* desc) +{ + core_connection_write(desc, "\n", 1, NULL); +} diff --git a/android/core-connection.h b/android/core-connection.h new file mode 100644 index 0000000..fc1be43 --- /dev/null +++ b/android/core-connection.h @@ -0,0 +1,133 @@ +/* Copyright (C) 2010 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ + +/* + * This file contains declaration related to communication between emulator's + * UI and core through a console port. + */ + +#ifndef QEMU_ANDROID_CORE_CONNECTION_H +#define QEMU_ANDROID_CORE_CONNECTION_H + +#include "android/sync-utils.h" + +// Opaque CoreConnection structure. +typedef struct CoreConnection CoreConnection; + +// Base console port +#define CORE_BASE_PORT 5554 + +// Maximum number of core porocesses running simultaneously on a machine. +#define MAX_CORE_PROCS 16 + +// Socket timeout in millisec (set to half a second) +#define CORE_PORT_TIMEOUT_MS 500 + +/* Opens core console socket. + * Param: + * sockaddr Socket address to the core console. + * Return: + * Sync socket descriptor on success, or -1 on failure, with errno appropriately + * set. + */ +SyncSocket* core_connection_open_socket(SockAddress* sockaddr); + +/* Creates descriptor for a console client. + * Param: + * console_socket Socket address for the console. + * Return: + * Allocated and initialized descriptor for the client on success, or NULL + * on failure. + */ +CoreConnection* core_connection_create(SockAddress* console_socket); + +/* Frees descriptor allocated with core_connection_create. + * Param: + * desc Descriptor to free. Note that this routine will simply free the memory + * used by the descriptor. + */ +void core_connection_free(CoreConnection* desc); + +/* Opens a socket handle to the console. + * Param: + * desc Console client descriptor. Note that if the descriptor has been already + * opened, this routine will simply return with success. + * Return: + * 0 on success, or -1 on failure with errno properly set. This routine will + * return in at most one second. + */ +int core_connection_open(CoreConnection* desc); + +/* Closes a socket handle to the console opened with core_connection_open. + * Param: + * desc Console client descriptor opened with core_connection_open. + */ +void core_connection_close(CoreConnection* desc); + +/* Synchronously writes to the console. See CORE_PORT_TIMEOUT_MS for the timeout + * value used to wait for the write operation to complete. + * Param: + * desc Console client descriptor opened with core_connection_open. + * buffer Buffer to write. + * to_write Number of bytes to write. + * written_bytes Upon success, contains number of bytes written. This parameter + * is optional, and can be NULL. + * Return: + * 0 on success, or -1 on failure. + */ +int core_connection_write(CoreConnection* desc, + const void* buffer, + size_t to_write, + size_t* written_bytes); + +/* Synchronously reads from the console. See CORE_PORT_TIMEOUT_MS for the + * timeout value used to wait for the read operation to complete. + * Param: + * desc Console client descriptor opened with core_connection_open. + * buffer Buffer to read data to. + * to_read Number of bytes to read. + * read_bytes Upon success, contains number of bytes that have been actually + * read. This parameter is optional, and can be NULL. + * Return: + * 0 on success, or -1 on failure. + */ +int core_connection_read(CoreConnection* desc, + void* buffer, + size_t to_read, + size_t* read_bytes); + +/* Switches opened console client to a given stream. + * Param: + * desc Console client descriptor opened with core_connection_open. Note + * that this descriptor should represent console itself. In other words, + * there must have been no previous calls to this routine for that + * descriptor. + * stream_name Name of the stream to switch to. + * handshake Address of a string to allocate for a handshake message on + * success, or an error message on failure. If upon return from this + * routine that string is not NULL, its buffer must be freed with 'free'. + * Return: + * 0 on success, or -1 on failure. + */ +int core_connection_switch_stream(CoreConnection* desc, + const char* stream_name, + char** handshake); + +/* Detaches opened console client from the console. + * By console protocol, writing "\r\n" string to the console will destroy the + * console client. + * Param: + * desc Console client descriptor opened with core_connection_open. + */ +void core_connection_detach(CoreConnection* desc); + +#endif // QEMU_ANDROID_CORE_CONNECTION_H diff --git a/android/help.c b/android/help.c index f49e3fa..93ae8b1 100644 --- a/android/help.c +++ b/android/help.c @@ -1040,6 +1040,21 @@ help_list_cores(stralloc_t* out) " a remote machine.\n" ); } + +static void +help_attach_core(stralloc_t* out) +{ + PRINTF( + " the -attach-core <console socket> options attaches the UI to a running emulator core process.\n\n" + + " the <console socket> parameter must be in the form [host:]port, where 'host' addresses the\n" + " machine on which the core process is running, and 'port' addresses the console port number for\n" + " the running core process. Note that 'host' value must be in the form that can be resolved\n" + " into an IP address.\n\n" + + " Use -list-cores to enumerate console ports for all currently running core processes.\n" + ); +} #endif // CONFIG_STANDALONE_UI static void diff --git a/android/main-ui.c b/android/main-ui.c index 16762a5..b0cd538 100644 --- a/android/main-ui.c +++ b/android/main-ui.c @@ -60,6 +60,7 @@ #include "android/display.h" #include "android/snapshot.h" +#include "android/core-connection.h" #include "framebuffer.h" #include "iolooper.h" @@ -99,6 +100,12 @@ extern void stop_tracing(void); unsigned long android_verbose; +/* Instance of the "attach UI" Emulator's core console client. */ +CoreConnection* attach_client = NULL; + +/* -ui-settings parameters received from the core on UI attachment. */ +char* core_ui_settings = ""; + /***********************************************************************/ /***********************************************************************/ /***** *****/ @@ -735,8 +742,6 @@ _adjustPartitionSize( const char* description, return convertMBToBytes(imageMB); } -#ifdef CONFIG_STANDALONE_UI - // Base console port #define CORE_BASE_PORT 5554 @@ -789,6 +794,13 @@ coreconsole_done(CoreConsole* cc) loopIo_done(cc->io); } +/* List emulator core processes running on the given machine. + * This routine is called from main() if -list-cores parameter is set in the + * command line. + * Param: + * host Value passed with -list-core parameter. Must be either "localhost", or + * an IP address of a machine where core processes must be enumerated. + */ static void list_running_cores(const char* host) { @@ -832,7 +844,102 @@ list_running_cores(const char* host) } } -#endif // CONFIG_STANDALONE_UI +/* Attaches starting UI to a running core process. + * This routine is called from main() when -attach-core parameter is set, + * indicating that this UI instance should attach to a running core, rather than + * start a new core process. + * Param: + * opts Android options containing non-NULL attach_core. + * Return: + * 0 on success, or -1 on failure. + */ +static int +attach_to_core(AndroidOptions* opts) { + int iter; + SockAddress console_socket; + SockAddress** sockaddr_list; + + // Parse attach_core param extracting the host name, and the port name. + char* console_address = strdup(opts->attach_core); + char* host_name = console_address; + char* port_num = strchr(console_address, ':'); + if (port_num == NULL) { + // The host name is ommited, indicating the localhost + host_name = "localhost"; + port_num = console_address; + } else if (port_num == console_address) { + // Invalid. + derror("Invalid value %s for -attach-core parameter\n", + opts->attach_core); + return -1; + } else { + *port_num = '\0'; + port_num++; + if (*port_num == '\0') { + // Invalid. + derror("Invalid value %s for -attach-core parameter\n", + opts->attach_core); + return -1; + } + } + + /* Create socket address list for the given address, and pull appropriate + * address to use for connection. Note that we're fine copying that address + * out of the list, since INET and IN6 will entirely fit into SockAddress + * structure. */ + sockaddr_list = + sock_address_list_create(host_name, port_num, SOCKET_LIST_FORCE_INET); + free(console_address); + if (sockaddr_list == NULL) { + derror("Unable to resolve address %s: %s\n", + opts->attach_core, errno_str); + return -1; + } + for (iter = 0; sockaddr_list[iter] != NULL; iter++) { + if (sock_address_get_family(sockaddr_list[iter]) == SOCKET_INET || + sock_address_get_family(sockaddr_list[iter]) == SOCKET_IN6) { + memcpy(&console_socket, sockaddr_list[iter], sizeof(SockAddress)); + break; + } + } + if (sockaddr_list[iter] == NULL) { + derror("Unable to resolve address %s. Note that 'port' parameter passed to -attach-core\n" + "must be resolvable into an IP address.\n", opts->attach_core); + sock_address_list_free(sockaddr_list); + return -1; + } + sock_address_list_free(sockaddr_list); + + attach_client = core_connection_create(&console_socket); + if (attach_client != NULL) { + if (!core_connection_open(attach_client)) { + if (!core_connection_switch_stream(attach_client, "attach UI", + &core_ui_settings)) { + fprintf(stdout, "UI is now attached to the core %s\n", + sock_address_to_string(&console_socket)); + if (*core_ui_settings != '\0') { + fprintf(stdout, "UI setting for the core%s:\n", + core_ui_settings); + } + return 0; + } else { + derror("Unable to attach to the core %s: %s\n", + sock_address_to_string(&console_socket), + core_ui_settings); + core_connection_close(attach_client); + core_connection_free(attach_client); + attach_client = NULL; + return -1; + } + } else { + core_connection_free(attach_client); + attach_client = NULL; + return -1; + } + } else { + return -1; + } +} int main(int argc, char **argv) { @@ -920,14 +1027,21 @@ int main(int argc, char **argv) exit(1); } -#ifdef CONFIG_STANDALONE_UI // Lets see if user just wants to list core process. if (opts->list_cores) { fprintf(stdout, "Enumerating running core processes.\n"); list_running_cores(opts->list_cores); exit(0); } -#endif // CONFIG_STANDALONE_UI + + // Lets see if we're attaching to a running core process here. + if (opts->attach_core) { + /* TODO: This is just for the testing and debugging purposes. Later, + * when code develops, there will be more meat on that bone. */ + if (attach_to_core(opts)) { + return -1; + } + } if (android_charmap_setup(opts->charmap)) { exit(1); diff --git a/android/sync-utils.c b/android/sync-utils.c index 1695615..8d55823 100644 --- a/android/sync-utils.c +++ b/android/sync-utils.c @@ -130,6 +130,28 @@ syncsocket_stop_read(SyncSocket* ssocket) } int +syncsocket_start_write(SyncSocket* ssocket) +{ + if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) { + errno = EINVAL; + return -1; + } + iolooper_add_write(ssocket->iolooper, ssocket->fd); + return 0; +} + +int +syncsocket_stop_write(SyncSocket* ssocket) +{ + if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) { + errno = EINVAL; + return -1; + } + iolooper_del_write(ssocket->iolooper, ssocket->fd); + return 0; +} + +ssize_t syncsocket_read_absolute(SyncSocket* ssocket, void* buf, size_t size, @@ -151,17 +173,74 @@ syncsocket_read_absolute(SyncSocket* ssocket, do { ret = read(ssocket->fd, buf, size); } while( ret < 0 && errno == EINTR); + } else if (ret == 0) { + // Timed out + errno = ETIMEDOUT; + ret = -1; } return ret; } -int +ssize_t syncsocket_read(SyncSocket* ssocket, void* buf, size_t size, int timeout) { return syncsocket_read_absolute(ssocket, buf, size, iolooper_now() + timeout); } -int +ssize_t +syncsocket_write_absolute(SyncSocket* ssocket, + const void* buf, + size_t size, + int64_t deadline) +{ + int ret; + size_t written = 0; + + if (ssocket == NULL || ssocket->fd < 0 || ssocket->iolooper == NULL) { + errno = EINVAL; + return -1; + } + + do { + ret = iolooper_wait_absolute(ssocket->iolooper, deadline); + if (ret < 0) { + return ret; + } else if (ret == 0) { + // Timeout. + errno = ETIMEDOUT; + return -1; + } + if (!iolooper_is_write(ssocket->iolooper, ssocket->fd)) { + D("%s: Internal error, iolooper_is_write() not set!", __FUNCTION__); + return -1; + } + + do { + ret = write(ssocket->fd, (const char*)buf + written, size - written); + } while( ret < 0 && errno == EINTR); + + if (ret > 0) { + written += ret; + } else if (ret < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + return -1; + } + } else { + // Disconnected. + errno = ECONNRESET; + return -1; + } + } while (written < size); + return (int)written; +} + +ssize_t +syncsocket_write(SyncSocket* ssocket, const void* buf, size_t size, int timeout) +{ + return syncsocket_write_absolute(ssocket, buf, size, iolooper_now() + timeout); +} + +ssize_t syncsocket_read_line_absolute(SyncSocket* ssocket, char* buffer, size_t size, @@ -177,7 +256,7 @@ syncsocket_read_line_absolute(SyncSocket* ssocket, } buffer[read_chars++] = ch; if (ch == '\n') { - return (int)read_chars; + return read_chars; } } @@ -186,7 +265,7 @@ syncsocket_read_line_absolute(SyncSocket* ssocket, return -1; } -int +ssize_t syncsocket_read_line(SyncSocket* ssocket, char* buffer, size_t size, int timeout) { return syncsocket_read_line_absolute(ssocket, buffer, size, diff --git a/android/sync-utils.h b/android/sync-utils.h index 8456e9f..f522e27 100644 --- a/android/sync-utils.h +++ b/android/sync-utils.h @@ -47,7 +47,8 @@ void syncsocket_close(SyncSocket* ssocket); /* * Frees memory allocated for SyncSocket descriptor obtained from - * syncsocket_connect routine. + * syncsocket_connect routine. Note that this routine will also close socket + * connection. * Param: * ssocket - SyncSocket descriptor obtained from syncsocket_connect routine. */ @@ -76,6 +77,28 @@ int syncsocket_start_read(SyncSocket* ssocket); int syncsocket_stop_read(SyncSocket* ssocket); /* + * Prepares the socket for write. + * Note: this routine must be called before calling into syncsocket_write_xxx + * routines. + * Param: + * ssocket - SyncSocket descriptor obtained from syncsocket_connect routine. + * Return: + * 0 on success, or -1 on failure. + */ +int syncsocket_start_write(SyncSocket* ssocket); + +/* + * Clears the socket after writing. + * Note: this routine must be called after all data has been written to the + * socket. + * Param: + * ssocket - SyncSocket descriptor obtained from syncsocket_connect routine. + * Return: + * 0 on success, or -1 on failure. + */ +int syncsocket_stop_write(SyncSocket* ssocket); + +/* * Synchronously reads from the socket. * Note: syncsocket_start_read must be called before first call to this routine. * Once syncsocket_start_read has been called, multiple syncsocket_read_xxx can @@ -87,12 +110,12 @@ int syncsocket_stop_read(SyncSocket* ssocket); * size - Number of bytes to read. * deadline - Absoulte deadline time to complete the reading. * Return: - * Number of bytes read on success, 0 on deadline expiration, or -1 on failure. + * Number of bytes read on success, or -1 on failure. */ -int syncsocket_read_absolute(SyncSocket* ssocket, - void* buf, - size_t size, - int64_t deadline); +ssize_t syncsocket_read_absolute(SyncSocket* ssocket, + void* buf, + size_t size, + int64_t deadline); /* * Synchronously reads from the socket. @@ -106,9 +129,47 @@ int syncsocket_read_absolute(SyncSocket* ssocket, * size - Number of bytes to read. * timeout - Timeout (in milliseconds) to complete the reading. * Return: - * Number of bytes read on success, 0 on timeout expiration, or -1 on failure. + * Number of bytes read on success, or -1 on failure. */ -int syncsocket_read(SyncSocket* ssocket, void* buf, size_t size, int timeout); +ssize_t syncsocket_read(SyncSocket* ssocket, void* buf, size_t size, int timeout); + +/* + * Synchronously writes to the socket. + * Note: syncsocket_start_write must be called before first call to this routine. + * Once syncsocket_start_write has been called, multiple syncsocket_write_xxx can + * be called to write all necessary data to the socket. When all necessary data + * has been written, syncsocket_stop_write must be called. + * Param: + * ssocket - SyncSocket descriptor obtained from syncsocket_connect routine. + * buf - Buffer containing data to write. + * size - Number of bytes to write. + * deadline - Absoulte deadline time to complete the writing. + * Return: + * Number of bytes written on success,or -1 on failure. + */ +ssize_t syncsocket_write_absolute(SyncSocket* ssocket, + const void* buf, + size_t size, + int64_t deadline); + +/* + * Synchronously writes to the socket. + * Note: syncsocket_start_write must be called before first call to this routine. + * Once syncsocket_start_write has been called, multiple syncsocket_write_xxx can + * be called to write all necessary data to the socket. When all necessary data + * has been written, syncsocket_stop_write must be called. + * Param: + * ssocket - SyncSocket descriptor obtained from syncsocket_connect routine. + * buf - Buffer containing data to write. + * size - Number of bytes to write. + * timeout - Timeout (in milliseconds) to complete the writing. + * Return: + * Number of bytes written on success, or -1 on failure. + */ +ssize_t syncsocket_write(SyncSocket* ssocket, + const void* buf, + size_t size, + int timeout); /* * Synchronously reads a line terminated with '\n' from the socket. @@ -122,10 +183,10 @@ int syncsocket_read(SyncSocket* ssocket, void* buf, size_t size, int timeout); * Number of chracters read on success, 0 on deadline expiration, * or -1 on failure. */ -int syncsocket_read_line_absolute(SyncSocket* ssocket, - char* buffer, - size_t size, - int64_t deadline); +ssize_t syncsocket_read_line_absolute(SyncSocket* ssocket, + char* buffer, + size_t size, + int64_t deadline); /* * Synchronously reads a line terminated with '\n' from the socket. @@ -139,9 +200,9 @@ int syncsocket_read_line_absolute(SyncSocket* ssocket, * Number of chracters read on success, 0 on deadline expiration, * or -1 on failure. */ -int syncsocket_read_line(SyncSocket* ssocket, - char* buffer, - size_t size, - int timeout); +ssize_t syncsocket_read_line(SyncSocket* ssocket, + char* buffer, + size_t size, + int timeout); #endif // ANDROID_SYNC_UTILS_H |