1/* Copyright (C) 2010 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10** GNU General Public License for more details.
11*/
12
13#include <unistd.h>
14
15#include "sockets.h"
16#include "qemu-common.h"
17#include "errno.h"
18#include "iolooper.h"
19#include "android/android.h"
20#include "android/utils/debug.h"
21#include "android/globals.h"
22#include "android/utils/system.h"
23#include "android/protocol/core-connection.h"
24
25/* Descriptor for a client, connected to the core via console port. */
26struct CoreConnection {
27    /* Socket address of the console. */
28    SockAddress console_address;
29
30    // Helper for performing sync I/O on the console socket.
31    SyncSocket* ssocket;
32
33    /* Stream name. Can be:
34     *  - NULL for the console itself.
35     *  - "attach-UI" for the attached UI client.
36     */
37    char* stream_name;
38};
39
40/*
41 * Zero-terminates string buffer.
42 * Param:
43 *  buf - Buffer containing the string.
44 *  buf_size - Buffer size.
45 *  eos - String size.
46 */
47static inline void
48_zero_terminate(char* buf, size_t buf_size, size_t eos)
49{
50    if (eos < buf_size) {
51        buf[eos] = '\0';
52    } else {
53        buf[buf_size - 1] = '\0';
54    }
55}
56
57/*
58 * Checks if console has replied with "OK"
59 * Param:
60 *  reply - String containing console's reply
61 * Return:
62 *  boolean: true if reply was "OK", or false otherwise.
63 */
64static int
65_is_reply_ok(const char* reply, int reply_size)
66{
67    return (reply_size < 2) ? 0 : (reply[0] == 'O' && reply[1] == 'K');
68}
69
70/*
71 * Checks if console has replied with "KO"
72 * Param:
73 *  reply - String containing console's reply
74 * Return:
75 *  boolean: true if reply was "KO", or false otherwise.
76 */
77static int
78_is_reply_ko(const char* reply, int reply_size)
79{
80    return (reply_size < 2) ? 0 : (reply[0] == 'K' && reply[1] == 'O');
81}
82
83SyncSocket*
84core_connection_open_socket(SockAddress* sockaddr)
85{
86    SyncSocket* ssocket;
87    int status;
88    int64_t deadline;
89    char buf[512];
90
91    int fd = socket_create(sock_address_get_family(sockaddr), SOCKET_STREAM);
92    if (fd < 0) {
93        return NULL;
94    }
95
96    socket_set_xreuseaddr(fd);
97
98    // Create sync connection to the console.
99    ssocket = syncsocket_connect(fd, sockaddr, CORE_PORT_TIMEOUT_MS);
100    if (ssocket == NULL) {
101        derror("syncsocket_connect has failed: %s\n", errno_str);
102        socket_close(fd);
103        return NULL;
104    }
105
106    // Upon successful connection the console will reply with two strings:
107    // "Android Console....", and "OK\r\n". Read them and check.
108    status = syncsocket_start_read(ssocket);
109    if (status < 0) {
110        derror("syncsocket_start_read has failed: %s\n", errno_str);
111        syncsocket_free(ssocket);
112        return NULL;
113    }
114
115    deadline = iolooper_now() + CORE_PORT_TIMEOUT_MS;
116    // Read first line.
117    status = syncsocket_read_line_absolute(ssocket, buf, sizeof(buf), deadline);
118    if (status <= 0) {
119        derror("syncsocket_read_line_absolute has failed: %s\n", errno_str);
120        syncsocket_free(ssocket);
121        return NULL;
122    }
123    if (status < 15 || memcmp(buf, "Android Console", 15)) {
124        _zero_terminate(buf, sizeof(buf), status);
125        derror("console has failed the connection: %s\n", buf);
126        syncsocket_free(ssocket);
127        return NULL;
128    }
129    // Read second line
130    status = syncsocket_read_line_absolute(ssocket, buf, sizeof(buf), deadline);
131    syncsocket_stop_read(ssocket);
132    if (status < 2 || !_is_reply_ok(buf, status)) {
133        _zero_terminate(buf, sizeof(buf), status);
134        derror("unexpected reply from the console: %s\n", buf);
135        syncsocket_free(ssocket);
136        return NULL;
137    }
138
139    return ssocket;
140}
141
142CoreConnection*
143core_connection_create(SockAddress* console_address)
144{
145    CoreConnection* desc;
146    ANEW0(desc);
147    desc->console_address = console_address[0];
148    desc->ssocket = NULL;
149    desc->stream_name = NULL;
150
151    return desc;
152}
153
154void
155core_connection_free(CoreConnection* desc)
156{
157    if (desc == NULL) {
158        return;
159    }
160    if (desc->ssocket != NULL) {
161        syncsocket_free(desc->ssocket);
162    }
163    if (desc->stream_name != NULL) {
164        free(desc->stream_name);
165    }
166    free(desc);
167}
168
169int
170core_connection_open(CoreConnection* desc)
171{
172    if (desc == NULL) {
173        errno = EINVAL;
174        return -1;
175    }
176    if (desc->ssocket != NULL) {
177        return 0;
178    }
179
180    desc->ssocket = core_connection_open_socket(&desc->console_address);
181
182    return (desc->ssocket != NULL) ? 0 : -1;
183}
184
185void
186core_connection_close(CoreConnection* desc)
187{
188    if (desc == NULL) {
189        return;
190    }
191    if (desc->ssocket != NULL) {
192        syncsocket_close(desc->ssocket);
193    }
194}
195
196int
197core_connection_write(CoreConnection* desc,
198                      const void* buffer,
199                      size_t to_write,
200                      size_t* written_bytes)
201{
202    ssize_t written;
203
204    int status = syncsocket_start_write(desc->ssocket);
205    if (status < 0) {
206        derror("syncsocket_start_write failed: %s\n", errno_str);
207        return status;
208    }
209
210    written =
211        syncsocket_write(desc->ssocket, buffer, to_write, CORE_PORT_TIMEOUT_MS);
212    syncsocket_stop_write(desc->ssocket);
213    if (written <= 0) {
214        derror("syncsocket_write failed: %s\n", errno_str);
215        return -1;
216    }
217    if (written_bytes != NULL) {
218        *written_bytes = written;
219    }
220
221    return 0;
222}
223
224int
225core_connection_read(CoreConnection* desc,
226                     void* buffer,
227                     size_t to_read,
228                     size_t* read_bytes)
229{
230    ssize_t read_size;
231
232    int status = syncsocket_start_read(desc->ssocket);
233    if (status < 0) {
234        derror("syncsocket_start_read failed: %s\n", errno_str);
235        return status;
236    }
237
238    read_size =
239        syncsocket_read(desc->ssocket, buffer, to_read, CORE_PORT_TIMEOUT_MS);
240    syncsocket_stop_read(desc->ssocket);
241    if (read_size <= 0) {
242        derror("syncsocket_read failed: %s\n", errno_str);
243        return -1;
244    }
245
246    if (read_bytes != NULL) {
247        *read_bytes = read_size;
248    }
249    return 0;
250}
251
252int
253core_connection_switch_stream(CoreConnection* desc,
254                              const char* stream_name,
255                              char** handshake)
256{
257    char buf[4096];
258    int handshake_len;
259    int status;
260    int64_t deadline;
261
262    *handshake = NULL;
263    if (desc == NULL || desc->stream_name != NULL || stream_name == NULL) {
264        errno = EINVAL;
265        return -1;
266    }
267
268    // Prepare and write "switch" command.
269    snprintf(buf, sizeof(buf), "qemu %s\r\n", stream_name);
270    if (core_connection_write(desc, buf, strlen(buf), NULL)) {
271        return -1;
272    }
273
274    // Read result / handshake
275    status = syncsocket_start_read(desc->ssocket);
276    if (status < 0) {
277        return -1;
278    }
279    deadline = iolooper_now() + CORE_PORT_TIMEOUT_MS;
280    handshake_len =
281        syncsocket_read_line_absolute(desc->ssocket, buf, sizeof(buf), deadline);
282    _zero_terminate(buf, sizeof(buf), handshake_len);
283    // Replace terminating "\r\n" with 0
284    if (handshake_len >= 1) {
285        if (buf[handshake_len - 1] == '\r' || buf[handshake_len - 1] == '\n') {
286            buf[handshake_len - 1] = '\0';
287            if (handshake_len >= 2 && (buf[handshake_len - 2] == '\r' ||
288                                       buf[handshake_len - 2] == '\n')) {
289                buf[handshake_len - 2] = '\0';
290            }
291        }
292    }
293    // Lets see what kind of response we've got here.
294    if (_is_reply_ok(buf, handshake_len)) {
295        *handshake = strdup(buf + 3);
296        desc->stream_name = strdup(stream_name);
297        // We expect an "OK" string here
298        status = syncsocket_read_line_absolute(desc->ssocket, buf, sizeof(buf),
299                                               deadline);
300        syncsocket_stop_read(desc->ssocket);
301        if (status < 0) {
302            derror("error reading console reply on stream switch: %s\n", errno_str);
303            return -1;
304        } else if (!_is_reply_ok(buf, status)) {
305            _zero_terminate(buf, sizeof(buf), status);
306            derror("unexpected console reply when switching streams: %s\n", buf);
307            return -1;
308        }
309        return 0;
310    } else if (_is_reply_ko(buf, handshake_len)) {
311        derror("console has rejected stream switch: %s\n", buf);
312        syncsocket_stop_read(desc->ssocket);
313        *handshake = strdup(buf + 3);
314        return -1;
315    } else {
316        // No OK, no KO? Should be an error!
317        derror("unexpected console reply when switching streams: %s\n", buf);
318        syncsocket_stop_read(desc->ssocket);
319        *handshake = strdup(buf);
320        return -1;
321    }
322}
323
324CoreConnection*
325core_connection_create_and_switch(SockAddress* console_socket,
326                                  const char* stream_name,
327                                  char** handshake)
328{
329    char switch_cmd[256];
330    CoreConnection* connection = NULL;
331
332    // Connect to the console service.
333    connection = core_connection_create(console_socket);
334    if (connection == NULL) {
335        return NULL;
336    }
337    if (core_connection_open(connection)) {
338        core_connection_free(connection);
339        return NULL;
340    }
341
342    // Perform the switch.
343    snprintf(switch_cmd, sizeof(switch_cmd), "%s", stream_name);
344    if (core_connection_switch_stream(connection, switch_cmd, handshake)) {
345        core_connection_close(connection);
346        core_connection_free(connection);
347        return NULL;
348    }
349
350    return connection;
351}
352
353void
354core_connection_detach(CoreConnection* desc)
355{
356    core_connection_write(desc, "\n", 1, NULL);
357}
358
359int
360core_connection_get_socket(CoreConnection* desc)
361{
362    return (desc != NULL) ? syncsocket_get_socket(desc->ssocket) : -1;
363}
364