aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2010-12-06 10:55:11 -0800
committerVladimir Chtchetkine <vchtchetkine@google.com>2010-12-06 10:56:24 -0800
commitd87b080495e71ada650b165a1f06616b433e6073 (patch)
treeb33edad3c18c7e962539d8c94250cd9ee8baedf1
parentfced4df82222b898580557de7313d86b5d6934f3 (diff)
downloadexternal_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
-rw-r--r--Makefile.android1
-rw-r--r--android/cmdline-options.h1
-rw-r--r--android/console.c50
-rw-r--r--android/core-connection.c323
-rw-r--r--android/core-connection.h133
-rw-r--r--android/help.c15
-rw-r--r--android/main-ui.c124
-rw-r--r--android/sync-utils.c87
-rw-r--r--android/sync-utils.h93
-rw-r--r--vl-android-ui.c12
10 files changed, 808 insertions, 31 deletions
diff --git a/Makefile.android b/Makefile.android
index fef3531..a8274dd 100644
--- a/Makefile.android
+++ b/Makefile.android
@@ -1194,6 +1194,7 @@ VL_SOURCES := framebuffer.c \
android/display.c \
android/looper-generic.c \
android/snapshot.c \
+ android/core-connection.c \
android/main-ui.c \
qemu-timer-ui.c \
vl-android-ui.c \
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
diff --git a/vl-android-ui.c b/vl-android-ui.c
index d693d83..316902c 100644
--- a/vl-android-ui.c
+++ b/vl-android-ui.c
@@ -33,6 +33,7 @@
#include "qemu-timer.h"
#include "qemu-char.h"
#include "block.h"
+#include "sockets.h"
#include "audio/audio.h"
#include "android/android.h"
@@ -40,6 +41,7 @@
#include "android/globals.h"
#include "android/utils/bufprint.h"
#include "android/utils/system.h"
+#include "android/core-connection.h"
#ifdef CONFIG_MEMCHECK
#include "memcheck/memcheck.h"
@@ -201,6 +203,9 @@ extern int android_display_bpp;
extern void dprint( const char* format, ... );
+/* Instance of the "attach UI" Emulator's core console client. */
+extern CoreConnection* attach_client;
+
#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
/* compute with 96 bit intermediate result: (a*b)/c */
@@ -588,6 +593,13 @@ int main(int argc, char **argv, char **envp)
//qemu_chr_initial_reset();
main_loop();
+
+ if (attach_client != NULL) {
+ core_connection_detach(attach_client);
+ core_connection_close(attach_client);
+ core_connection_free(attach_client);
+ }
+
quit_timers();
return 0;
}