qemu-setup.c revision 777eb68eb60cac18f4b62e2e1b14a906875cbe7a
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
35extern int  control_console_start( int  port );  /* in control.c */
36
37/* 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;
45/* Base port for the emulated system. */
46int    android_base_port;
47
48/*** 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
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