blob: 9a41c4da6fd7bccf8380624bdc54fc770779052b [file] [log] [blame]
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -07001/* Copyright (C) 2006-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 "libslirp.h"
14#include "qemu-common.h"
15#include "sysemu.h"
16#include "modem_driver.h"
17#include "proxy_http.h"
18
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -070019#include "android/android.h"
20#include "android/globals.h"
21#include "android/hw-sensors.h"
22#include "android/utils/debug.h"
23#include "android/utils/path.h"
24#include "android/utils/system.h"
25#include "android/utils/bufprint.h"
26
27#define D(...) do { if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
28
29#ifdef ANDROID_SDK_TOOLS_REVISION
30# define VERSION_STRING STRINGIFY(ANDROID_SDK_TOOLS_REVISION)".0"
31#else
32# define VERSION_STRING "standalone"
33#endif
34
35extern int control_console_start( int port ); /* in control.c */
36
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -070037/* Contains arguments for -android-ports option. */
38char* android_op_ports = NULL;
39/* Contains arguments for -android-port option. */
40char* android_op_port = NULL;
41/* Contains arguments for -android-report-console option. */
42char* android_op_report_console = NULL;
43/* Contains arguments for -http-proxy option. */
44char* op_http_proxy = NULL;
Vladimir Chtchetkine2fa51732010-07-16 11:19:48 -070045/* Base port for the emulated system. */
46int android_base_port;
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -070047
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -070048/*** APPLICATION DIRECTORY
49 *** Where are we ?
50 ***/
51
52const char* get_app_dir(void)
53{
54 char buffer[1024];
55 char* p = buffer;
56 char* end = p + sizeof(buffer);
57 p = bufprint_app_dir(p, end);
58 if (p >= end)
59 return NULL;
60
61 return strdup(buffer);
62}
63
64enum {
65 REPORT_CONSOLE_SERVER = (1 << 0),
66 REPORT_CONSOLE_MAX = (1 << 1)
67};
68
69static int
70get_report_console_options( char* end, int *maxtries )
71{
72 int flags = 0;
73
74 if (end == NULL || *end == 0)
75 return 0;
76
77 if (end[0] != ',') {
78 derror( "socket port/path can be followed by [,<option>]+ only\n");
79 exit(3);
80 }
81 end += 1;
82 while (*end) {
83 char* p = strchr(end, ',');
84 if (p == NULL)
85 p = end + strlen(end);
86
87 if (memcmp( end, "server", p-end ) == 0)
88 flags |= REPORT_CONSOLE_SERVER;
89 else if (memcmp( end, "max=", 4) == 0) {
90 end += 4;
91 *maxtries = strtol( end, NULL, 10 );
92 flags |= REPORT_CONSOLE_MAX;
93 } else {
94 derror( "socket port/path can be followed by [,server][,max=<count>] only\n");
95 exit(3);
96 }
97
98 end = p;
99 if (*end)
100 end += 1;
101 }
102 return flags;
103}
104
105static void
106report_console( const char* proto_port, int console_port )
107{
108 int s = -1, s2;
109 int maxtries = 10;
110 int flags = 0;
111 signal_state_t sigstate;
112
113 disable_sigalrm( &sigstate );
114
115 if ( !strncmp( proto_port, "tcp:", 4) ) {
116 char* end;
117 long port = strtol(proto_port + 4, &end, 10);
118
119 flags = get_report_console_options( end, &maxtries );
120
121 if (flags & REPORT_CONSOLE_SERVER) {
122 s = socket_loopback_server( port, SOCKET_STREAM );
123 if (s < 0) {
124 fprintf(stderr, "could not create server socket on TCP:%ld: %s\n",
125 port, errno_str);
126 exit(3);
127 }
128 } else {
129 for ( ; maxtries > 0; maxtries-- ) {
130 D("trying to find console-report client on tcp:%d", port);
131 s = socket_loopback_client( port, SOCKET_STREAM );
132 if (s >= 0)
133 break;
134
135 sleep_ms(1000);
136 }
137 if (s < 0) {
138 fprintf(stderr, "could not connect to server on TCP:%ld: %s\n",
139 port, errno_str);
140 exit(3);
141 }
142 }
143 } else if ( !strncmp( proto_port, "unix:", 5) ) {
144#ifdef _WIN32
145 fprintf(stderr, "sorry, the unix: protocol is not supported on Win32\n");
146 exit(3);
147#else
148 char* path = strdup(proto_port+5);
149 char* end = strchr(path, ',');
150 if (end != NULL) {
151 flags = get_report_console_options( end, &maxtries );
152 *end = 0;
153 }
154 if (flags & REPORT_CONSOLE_SERVER) {
155 s = socket_unix_server( path, SOCKET_STREAM );
156 if (s < 0) {
157 fprintf(stderr, "could not bind unix socket on '%s': %s\n",
158 proto_port+5, errno_str);
159 exit(3);
160 }
161 } else {
162 for ( ; maxtries > 0; maxtries-- ) {
163 s = socket_unix_client( path, SOCKET_STREAM );
164 if (s >= 0)
165 break;
166
167 sleep_ms(1000);
168 }
169 if (s < 0) {
170 fprintf(stderr, "could not connect to unix socket on '%s': %s\n",
171 path, errno_str);
172 exit(3);
173 }
174 }
175 free(path);
176#endif
177 } else {
178 fprintf(stderr, "-report-console must be followed by a 'tcp:<port>' or 'unix:<path>'\n");
179 exit(3);
180 }
181
182 if (flags & REPORT_CONSOLE_SERVER) {
183 int tries = 3;
184 D( "waiting for console-reporting client" );
185 do {
186 s2 = socket_accept(s, NULL);
187 } while (s2 < 0 && --tries > 0);
188
189 if (s2 < 0) {
190 fprintf(stderr, "could not accept console-reporting client connection: %s\n",
191 errno_str);
192 exit(3);
193 }
194
195 socket_close(s);
196 s = s2;
197 }
198
199 /* simply send the console port in text */
200 {
201 char temp[12];
202 snprintf( temp, sizeof(temp), "%d", console_port );
203
204 if (socket_send(s, temp, strlen(temp)) < 0) {
205 fprintf(stderr, "could not send console number report: %d: %s\n",
206 errno, errno_str );
207 exit(3);
208 }
209 socket_close(s);
210 }
211 D( "console port number sent to remote. resuming boot" );
212
213 restore_sigalrm (&sigstate);
214}
215
216/* this function is called from qemu_main() once all arguments have been parsed
217 * it should be used to setup any Android-specific items in the emulation before the
218 * main loop runs
219 */
220void android_emulation_setup( void )
221{
222 int tries = 16;
223 int base_port = 5554;
224 int adb_host_port = 5037; // adb's default
225 int success = 0;
226 int s;
227 uint32_t guest_ip;
228
229 /* Set the port where the emulator expects adb to run on the host
230 * machine */
231 char* adb_host_port_str = getenv( "ANDROID_ADB_SERVER_PORT" );
232 if ( adb_host_port_str && strlen( adb_host_port_str ) > 0 ) {
233 adb_host_port = (int) strtol( adb_host_port_str, NULL, 0 );
234 if ( adb_host_port <= 0 ) {
235 derror( "env var ANDROID_ADB_SERVER_PORT must be a number > 0. Got \"%s\"\n",
236 adb_host_port_str );
237 exit(1);
238 }
239 }
240
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700241 inet_strtoip("10.0.2.15", &guest_ip);
242
243#if 0
244 if (opts->adb_port) {
245 fprintf( stderr, "option -adb-port is obsolete, use -port instead\n" );
246 exit(1);
247 }
248#endif
249
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -0700250 if (android_op_port && android_op_ports) {
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700251 fprintf( stderr, "options -port and -ports cannot be used together.\n");
252 exit(1);
253 }
254
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -0700255 if (android_op_ports) {
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700256 char* comma_location;
257 char* end;
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -0700258 int console_port = strtol( android_op_ports, &comma_location, 0 );
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700259
260 if ( comma_location == NULL || *comma_location != ',' ) {
261 derror( "option -ports must be followed by two comma separated positive integer numbers" );
262 exit(1);
263 }
264
265 int adb_port = strtol( comma_location+1, &end, 0 );
266
267 if ( end == NULL || *end ) {
268 derror( "option -ports must be followed by two comma separated positive integer numbers" );
269 exit(1);
270 }
271
272 if ( console_port == adb_port ) {
273 derror( "option -ports must be followed by two different integer numbers" );
274 exit(1);
275 }
276
277 // Set up redirect from host to guest system. adbd on the guest listens
278 // on 5555.
279 slirp_redir( 0, adb_port, guest_ip, 5555 );
280 if ( control_console_start( console_port ) < 0 ) {
281 slirp_unredir( 0, adb_port );
282 }
283
284 base_port = console_port;
285 } else {
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -0700286 if (android_op_port) {
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700287 char* end;
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -0700288 int port = strtol( android_op_port, &end, 0 );
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700289 if ( end == NULL || *end ||
290 (unsigned)((port - base_port) >> 1) >= (unsigned)tries ) {
291 derror( "option -port must be followed by an even integer number between %d and %d\n",
292 base_port, base_port + (tries-1)*2 );
293 exit(1);
294 }
295 if ( (port & 1) != 0 ) {
296 port &= ~1;
297 dwarning( "option -port must be followed by an even integer, using port number %d\n",
298 port );
299 }
300 base_port = port;
301 tries = 1;
302 }
303
304 for ( ; tries > 0; tries--, base_port += 2 ) {
305
306 /* setup first redirection for ADB, the Android Debug Bridge */
307 if ( slirp_redir( 0, base_port+1, guest_ip, 5555 ) < 0 )
308 continue;
309
310 /* setup second redirection for the emulator console */
311 if ( control_console_start( base_port ) < 0 ) {
312 slirp_unredir( 0, base_port+1 );
313 continue;
314 }
315
316 D( "control console listening on port %d, ADB on port %d", base_port, base_port+1 );
317 success = 1;
318 break;
319 }
320
321 if (!success) {
322 fprintf(stderr, "it seems too many emulator instances are running on this machine. Aborting\n" );
323 exit(1);
324 }
325 }
326
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -0700327 if (android_op_report_console) {
328 report_console(android_op_report_console, base_port);
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700329 }
330
331 android_modem_init( base_port );
332
333 android_base_port = base_port;
334 /* send a simple message to the ADB host server to tell it we just started.
335 * it should be listening on port 5037. if we can't reach it, don't bother
336 */
337 do
338 {
339 SockAddress addr;
340 char tmp[32];
341
342 s = socket_create_inet( SOCKET_STREAM );
343 if (s < 0) {
344 D("can't create socket to talk to the ADB server");
345 break;
346 }
347
348 sock_address_init_inet( &addr, SOCK_ADDRESS_INET_LOOPBACK, adb_host_port );
349 if (socket_connect( s, &addr ) < 0) {
350 D("can't connect to ADB server: %s", errno_str );
351 break;
352 }
353
354 sprintf(tmp,"0012host:emulator:%d",base_port+1);
355 socket_send(s, tmp, 18+4);
356 D("sent '%s' to ADB server", tmp);
357 }
358 while (0);
359
360 if (s >= 0)
361 socket_close(s);
362
363 /* setup the http proxy, if any */
364 if (VERBOSE_CHECK(proxy))
365 proxy_set_verbose(1);
366
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -0700367 if (!op_http_proxy) {
368 op_http_proxy = getenv("http_proxy");
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700369 }
370
371 do
372 {
Vladimir Chtchetkined81e6d12010-06-15 16:46:32 -0700373 const char* env = op_http_proxy;
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700374 int envlen;
375 ProxyOption option_tab[4];
376 ProxyOption* option = option_tab;
377 char* p;
378 char* q;
379 const char* proxy_name;
380 int proxy_name_len;
381 int proxy_port;
382
383 if (!env)
384 break;
385
386 envlen = strlen(env);
387
388 /* skip the 'http://' header, if present */
389 if (envlen >= 7 && !memcmp(env, "http://", 7)) {
390 env += 7;
391 envlen -= 7;
392 }
393
394 /* do we have a username:password pair ? */
395 p = strchr(env, '@');
396 if (p != 0) {
397 q = strchr(env, ':');
398 if (q == NULL) {
399 BadHttpProxyFormat:
400 dprint("http_proxy format unsupported, try 'proxy:port' or 'username:password@proxy:port'");
401 break;
402 }
403
404 option->type = PROXY_OPTION_AUTH_USERNAME;
405 option->string = env;
406 option->string_len = q - env;
407 option++;
408
409 option->type = PROXY_OPTION_AUTH_PASSWORD;
410 option->string = q+1;
411 option->string_len = p - (q+1);
412 option++;
413
414 env = p+1;
415 }
416
417 p = strchr(env,':');
418 if (p == NULL)
419 goto BadHttpProxyFormat;
420
421 proxy_name = env;
422 proxy_name_len = p - env;
423 proxy_port = atoi(p+1);
424
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700425 D( "setting up http proxy: server=%.*s port=%d",
426 proxy_name_len, proxy_name, proxy_port );
427
David 'Digit' Turner9b98dbd2010-07-30 15:35:00 -0700428 /* Check that we can connect to the proxy in the next second.
429 * If not, the proxy setting is probably garbage !!
430 */
431 if ( proxy_check_connection( proxy_name, proxy_name_len, proxy_port, 1000 ) < 0) {
432 dprint("Could not connect to proxy at %.*s:%d: %s !",
433 proxy_name_len, proxy_name, proxy_port, errno_str);
434 dprint("Proxy will be ignored !");
435 break;
436 }
437
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700438 if ( proxy_http_setup( proxy_name, proxy_name_len, proxy_port,
439 option - option_tab, option_tab ) < 0 )
440 {
David 'Digit' Turner9b98dbd2010-07-30 15:35:00 -0700441 dprint( "Http proxy setup failed for '%.*s:%d': %s",
442 proxy_name_len, proxy_name, proxy_port, errno_str);
443 dprint( "Proxy will be ignored !");
Vladimir Chtchetkined27aca12010-05-13 11:04:42 -0700444 }
445 }
446 while (0);
447
448 /* initialize sensors, this must be done here due to timer issues */
449 android_hw_sensors_init();
450
451 /* cool, now try to run the "ddms ping" command, which will take care of pinging usage
452 * if the user agreed for it. the emulator itself never sends anything to any outside
453 * machine
454 */
455 {
456#ifdef _WIN32
457# define _ANDROID_PING_PROGRAM "ddms.bat"
458#else
459# define _ANDROID_PING_PROGRAM "ddms"
460#endif
461
462 char tmp[PATH_MAX];
463 const char* appdir = get_app_dir();
464
465 if (snprintf( tmp, PATH_MAX, "%s%s%s", appdir, PATH_SEP,
466 _ANDROID_PING_PROGRAM ) >= PATH_MAX) {
467 dprint( "Application directory too long: %s", appdir);
468 return;
469 }
470
471 /* if the program isn't there, don't bother */
472 D( "ping program: %s", tmp);
473 if (path_exists(tmp)) {
474#ifdef _WIN32
475 STARTUPINFO startup;
476 PROCESS_INFORMATION pinfo;
477
478 ZeroMemory( &startup, sizeof(startup) );
479 startup.cb = sizeof(startup);
480 startup.dwFlags = STARTF_USESHOWWINDOW;
481 startup.wShowWindow = SW_SHOWMINIMIZED;
482
483 ZeroMemory( &pinfo, sizeof(pinfo) );
484
485 char* comspec = getenv("COMSPEC");
486 if (!comspec) comspec = "cmd.exe";
487
488 // Run
489 char args[PATH_MAX + 30];
490 if (snprintf( args, PATH_MAX, "/C \"%s\" ping emulator " VERSION_STRING,
491 tmp) >= PATH_MAX ) {
492 D( "DDMS path too long: %s", tmp);
493 return;
494 }
495
496 CreateProcess(
497 comspec, /* program path */
498 args, /* command line args */
499 NULL, /* process handle is not inheritable */
500 NULL, /* thread handle is not inheritable */
501 FALSE, /* no, don't inherit any handles */
502 DETACHED_PROCESS, /* the new process doesn't have a console */
503 NULL, /* use parent's environment block */
504 NULL, /* use parent's starting directory */
505 &startup, /* startup info, i.e. std handles */
506 &pinfo );
507
508 D( "ping command: %s %s", comspec, args );
509#else
510 int pid;
511
512 /* disable SIGALRM for the fork(), the periodic signal seems to
513 * interefere badly with the fork() implementation on Linux running
514 * under VMWare.
515 */
516 BEGIN_NOSIGALRM
517 pid = fork();
518 if (pid == 0) {
519 int fd = open("/dev/null", O_WRONLY);
520 dup2(fd, 1);
521 dup2(fd, 2);
522 execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, NULL );
523 }
524 END_NOSIGALRM
525
526 /* don't do anything in the parent or in case of error */
527 strncat( tmp, " ping emulator " VERSION_STRING, PATH_MAX - strlen(tmp) );
528 D( "ping command: %s", tmp );
529#endif
530 }
531 }
532}
533
534