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