1 /* 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/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 35 extern int control_console_start( int port ); /* in control.c */ 36 37 /* Contains arguments for -android-ports option. */ 38 char* android_op_ports = NULL; 39 /* Contains arguments for -android-port option. */ 40 char* android_op_port = NULL; 41 /* Contains arguments for -android-report-console option. */ 42 char* android_op_report_console = NULL; 43 /* Contains arguments for -http-proxy option. */ 44 char* op_http_proxy = NULL; 45 /* Base port for the emulated system. */ 46 int android_base_port; 47 48 /*** APPLICATION DIRECTORY 49 *** Where are we ? 50 ***/ 51 52 const 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 64 enum { 65 REPORT_CONSOLE_SERVER = (1 << 0), 66 REPORT_CONSOLE_MAX = (1 << 1) 67 }; 68 69 static int 70 get_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 105 static void 106 report_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 */ 220 void 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 241 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 250 if (android_op_port && android_op_ports) { 251 fprintf( stderr, "options -port and -ports cannot be used together.\n"); 252 exit(1); 253 } 254 255 if (android_op_ports) { 256 char* comma_location; 257 char* end; 258 int console_port = strtol( android_op_ports, &comma_location, 0 ); 259 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 { 286 if (android_op_port) { 287 char* end; 288 int port = strtol( android_op_port, &end, 0 ); 289 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 327 if (android_op_report_console) { 328 report_console(android_op_report_console, base_port); 329 } 330 331 android_modem_init( base_port ); 332 333 /* Save base port. */ 334 android_base_port = base_port; 335 336 /* send a simple message to the ADB host server to tell it we just started. 337 * it should be listening on port 5037. if we can't reach it, don't bother 338 */ 339 do 340 { 341 SockAddress addr; 342 char tmp[32]; 343 344 s = socket_create_inet( SOCKET_STREAM ); 345 if (s < 0) { 346 D("can't create socket to talk to the ADB server"); 347 break; 348 } 349 350 sock_address_init_inet( &addr, SOCK_ADDRESS_INET_LOOPBACK, adb_host_port ); 351 if (socket_connect( s, &addr ) < 0) { 352 D("can't connect to ADB server: %s", errno_str ); 353 break; 354 } 355 356 sprintf(tmp,"0012host:emulator:%d",base_port+1); 357 socket_send(s, tmp, 18+4); 358 D("sent '%s' to ADB server", tmp); 359 } 360 while (0); 361 362 if (s >= 0) 363 socket_close(s); 364 365 /* setup the http proxy, if any */ 366 if (VERBOSE_CHECK(proxy)) 367 proxy_set_verbose(1); 368 369 if (!op_http_proxy) { 370 op_http_proxy = getenv("http_proxy"); 371 } 372 373 do 374 { 375 const char* env = op_http_proxy; 376 int envlen; 377 ProxyOption option_tab[4]; 378 ProxyOption* option = option_tab; 379 char* p; 380 char* q; 381 const char* proxy_name; 382 int proxy_name_len; 383 int proxy_port; 384 385 if (!env) 386 break; 387 388 envlen = strlen(env); 389 390 /* skip the 'http://' header, if present */ 391 if (envlen >= 7 && !memcmp(env, "http://", 7)) { 392 env += 7; 393 envlen -= 7; 394 } 395 396 /* do we have a username:password pair ? */ 397 p = strchr(env, '@'); 398 if (p != 0) { 399 q = strchr(env, ':'); 400 if (q == NULL) { 401 BadHttpProxyFormat: 402 dprint("http_proxy format unsupported, try 'proxy:port' or 'username:password@proxy:port'"); 403 break; 404 } 405 406 option->type = PROXY_OPTION_AUTH_USERNAME; 407 option->string = env; 408 option->string_len = q - env; 409 option++; 410 411 option->type = PROXY_OPTION_AUTH_PASSWORD; 412 option->string = q+1; 413 option->string_len = p - (q+1); 414 option++; 415 416 env = p+1; 417 } 418 419 p = strchr(env,':'); 420 if (p == NULL) 421 goto BadHttpProxyFormat; 422 423 proxy_name = env; 424 proxy_name_len = p - env; 425 proxy_port = atoi(p+1); 426 427 D( "setting up http proxy: server=%.*s port=%d", 428 proxy_name_len, proxy_name, proxy_port ); 429 430 /* Check that we can connect to the proxy in the next second. 431 * If not, the proxy setting is probably garbage !! 432 */ 433 if ( proxy_check_connection( proxy_name, proxy_name_len, proxy_port, 1000 ) < 0) { 434 dprint("Could not connect to proxy at %.*s:%d: %s !", 435 proxy_name_len, proxy_name, proxy_port, errno_str); 436 dprint("Proxy will be ignored !"); 437 break; 438 } 439 440 if ( proxy_http_setup( proxy_name, proxy_name_len, proxy_port, 441 option - option_tab, option_tab ) < 0 ) 442 { 443 dprint( "Http proxy setup failed for '%.*s:%d': %s", 444 proxy_name_len, proxy_name, proxy_port, errno_str); 445 dprint( "Proxy will be ignored !"); 446 } 447 } 448 while (0); 449 450 /* initialize sensors, this must be done here due to timer issues */ 451 android_hw_sensors_init(); 452 453 /* cool, now try to run the "ddms ping" command, which will take care of pinging usage 454 * if the user agreed for it. the emulator itself never sends anything to any outside 455 * machine 456 */ 457 { 458 #ifdef _WIN32 459 # define _ANDROID_PING_PROGRAM "ddms.bat" 460 #else 461 # define _ANDROID_PING_PROGRAM "ddms" 462 #endif 463 464 char tmp[PATH_MAX]; 465 const char* appdir = get_app_dir(); 466 467 if (snprintf( tmp, PATH_MAX, "%s%s%s", appdir, PATH_SEP, 468 _ANDROID_PING_PROGRAM ) >= PATH_MAX) { 469 dprint( "Application directory too long: %s", appdir); 470 return; 471 } 472 473 /* if the program isn't there, don't bother */ 474 D( "ping program: %s", tmp); 475 if (path_exists(tmp)) { 476 #ifdef _WIN32 477 STARTUPINFO startup; 478 PROCESS_INFORMATION pinfo; 479 480 ZeroMemory( &startup, sizeof(startup) ); 481 startup.cb = sizeof(startup); 482 startup.dwFlags = STARTF_USESHOWWINDOW; 483 startup.wShowWindow = SW_SHOWMINIMIZED; 484 485 ZeroMemory( &pinfo, sizeof(pinfo) ); 486 487 char* comspec = getenv("COMSPEC"); 488 if (!comspec) comspec = "cmd.exe"; 489 490 // Run 491 char args[PATH_MAX + 30]; 492 if (snprintf( args, PATH_MAX, "/C \"%s\" ping emulator " VERSION_STRING, 493 tmp) >= PATH_MAX ) { 494 D( "DDMS path too long: %s", tmp); 495 return; 496 } 497 498 CreateProcess( 499 comspec, /* program path */ 500 args, /* command line args */ 501 NULL, /* process handle is not inheritable */ 502 NULL, /* thread handle is not inheritable */ 503 FALSE, /* no, don't inherit any handles */ 504 DETACHED_PROCESS, /* the new process doesn't have a console */ 505 NULL, /* use parent's environment block */ 506 NULL, /* use parent's starting directory */ 507 &startup, /* startup info, i.e. std handles */ 508 &pinfo ); 509 510 D( "ping command: %s %s", comspec, args ); 511 #else 512 int pid; 513 514 /* disable SIGALRM for the fork(), the periodic signal seems to 515 * interefere badly with the fork() implementation on Linux running 516 * under VMWare. 517 */ 518 BEGIN_NOSIGALRM 519 pid = fork(); 520 if (pid == 0) { 521 int fd = open("/dev/null", O_WRONLY); 522 dup2(fd, 1); 523 dup2(fd, 2); 524 execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, NULL ); 525 } 526 END_NOSIGALRM 527 528 /* don't do anything in the parent or in case of error */ 529 strncat( tmp, " ping emulator " VERSION_STRING, PATH_MAX - strlen(tmp) ); 530 D( "ping command: %s", tmp ); 531 #endif 532 } 533 } 534 } 535 536 537