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
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 @@
 
 #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 @@
 
 } 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 @@
 
     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 @@
         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 @@
     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 @@
     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 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 @@
       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 @@
     "  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 @@
 
 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 @@
     return convertMBToBytes(imageMB);
 }
 
-#ifdef CONFIG_STANDALONE_UI
-
 // Base console port
 #define CORE_BASE_PORT          5554
 
@@ -789,6 +794,13 @@
     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 @@
     }
 }
 
-#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 @@
         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 @@
 }
 
 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 @@
         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 @@
         }
         buffer[read_chars++] = ch;
         if (ch == '\n') {
-            return (int)read_chars;
+            return read_chars;
         }
     }
 
@@ -186,7 +265,7 @@
     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 @@
 
 /*
  * 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_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 @@
  *  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,28 +129,66 @@
  *  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 reads a line terminated with '\n' from the socket.
- * Note: syncsocket_start_read must be called before first call to this routine.
+ * 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.
- *  buffer - Buffer where to read line.
- *  size - Number of characters the buffer can contain.
- *  deadline - Absoulte deadline time to complete the reading.
+ *  buf - Buffer containing data to write.
+ *  size - Number of bytes to write.
+ *  deadline - Absoulte deadline time to complete the writing.
  * Return:
- *  Number of chracters read on success, 0 on deadline expiration,
- *  or -1 on failure.
+ *  Number of bytes written on success,or -1 on failure.
  */
-int syncsocket_read_line_absolute(SyncSocket* ssocket,
-                                  char* buffer,
+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.
+ * Note: syncsocket_start_read must be called before first call to this routine.
+ * Param:
+ *  ssocket - SyncSocket descriptor obtained from syncsocket_connect routine.
+ *  buffer - Buffer where to read line.
+ *  size - Number of characters the buffer can contain.
+ *  deadline - Absoulte deadline time to complete the reading.
+ * Return:
+ *  Number of chracters read on success, 0 on deadline expiration,
+ *  or -1 on failure.
+ */
+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.
  * Note: syncsocket_start_read must be called before first call to this routine.
  * Param:
@@ -139,9 +200,9 @@
  *  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