diff options
author | David 'Digit' Turner <digit@android.com> | 2011-02-02 14:05:23 +0100 |
---|---|---|
committer | David 'Digit' Turner <digit@android.com> | 2011-02-02 14:05:23 +0100 |
commit | e993126c6704029cb1c656922a66a32bd09b6089 (patch) | |
tree | 64df4e534e0364cb3d45b4eef564e401932a118e /android/protocol/core-connection.c | |
parent | 74d7acec6643694132a127feb5ccadda7ea793d6 (diff) | |
download | external_qemu-e993126c6704029cb1c656922a66a32bd09b6089.zip external_qemu-e993126c6704029cb1c656922a66a32bd09b6089.tar.gz external_qemu-e993126c6704029cb1c656922a66a32bd09b6089.tar.bz2 |
Move core-connection.c from to android/protocol/
Change-Id: I1f04ed1f00fccdea043f4a4fbf5ba745b36bbcc7
Diffstat (limited to 'android/protocol/core-connection.c')
-rw-r--r-- | android/protocol/core-connection.c | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/android/protocol/core-connection.c b/android/protocol/core-connection.c new file mode 100644 index 0000000..6de5386 --- /dev/null +++ b/android/protocol/core-connection.c @@ -0,0 +1,363 @@ +/* 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/utils/system.h" +#include "android/protocol/core-connection.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 >= 1) { + if (buf[handshake_len - 1] == '\r' || buf[handshake_len - 1] == '\n') { + buf[handshake_len - 1] = '\0'; + if (handshake_len >= 2 && (buf[handshake_len - 2] == '\r' || + buf[handshake_len - 2] == '\n')) { + buf[handshake_len - 2] = '\0'; + } + } + } + // 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; + } +} + +CoreConnection* +core_connection_create_and_switch(SockAddress* console_socket, + const char* stream_name, + char** handshake) +{ + char switch_cmd[256]; + CoreConnection* connection = NULL; + + // Connect to the console service. + connection = core_connection_create(console_socket); + if (connection == NULL) { + return NULL; + } + if (core_connection_open(connection)) { + core_connection_free(connection); + return NULL; + } + + // Perform the switch. + snprintf(switch_cmd, sizeof(switch_cmd), "%s", stream_name); + if (core_connection_switch_stream(connection, switch_cmd, handshake)) { + core_connection_close(connection); + core_connection_free(connection); + return NULL; + } + + return connection; +} + +void +core_connection_detach(CoreConnection* desc) +{ + core_connection_write(desc, "\n", 1, NULL); +} + +int +core_connection_get_socket(CoreConnection* desc) +{ + return (desc != NULL) ? syncsocket_get_socket(desc->ssocket) : -1; +} |