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