1/* Copyright (C) 2011 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/* This is the source code to the tiny "emulator" launcher program
14 * that is in charge of starting the target-specific emulator binary
15 * for a given AVD, i.e. either 'emulator-arm' or 'emulator-x86'
16 *
17 * This program will be replaced in the future by what is currently
18 * known as 'emulator-ui', but is a good placeholder until this
19 * migration is completed.
20 */
21
22#include <errno.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27#include <android/utils/panic.h>
28#include <android/utils/path.h>
29#include <android/utils/bufprint.h>
30#include <android/avd/util.h>
31
32/* Required by android/utils/debug.h */
33int android_verbose;
34
35
36#define DEBUG 1
37
38#if DEBUG
39#  define D(...)  do { if (android_verbose) printf("emulator:" __VA_ARGS__); } while (0)
40#else
41#  define D(...)  do{}while(0)
42#endif
43
44/* The extension used by dynamic libraries on the host platform */
45#ifdef _WIN32
46#  define DLL_EXTENSION  ".dll"
47#elif defined(__APPLE__)
48#  define DLL_EXTENSION  ".dylib"
49#else
50#  define DLL_EXTENSION  ".so"
51#endif
52
53#if defined(__x86_64__)
54/* Normally emulator is compiled in 32-bit.  In standalone it can be compiled
55   in 64-bit (with ,/android-configure.sh --try-64).  In this case, emulator-$ARCH
56   are also compiled in 64-bit and will search for lib64*.so instead of lib*so */
57#define  GLES_EMULATION_LIB  "lib64OpenglRender" DLL_EXTENSION
58#elif defined(__i386__)
59#define  GLES_EMULATION_LIB  "libOpenglRender" DLL_EXTENSION
60#else
61#error Unknown architecture for codegen
62#endif
63
64
65/* Forward declarations */
66static char* getTargetEmulatorPath(const char* progName, const char* avdArch, const int force_32bit);
67static char* getSharedLibraryPath(const char* progName, const char* libName);
68static void  prependSharedLibraryPath(const char* prefix);
69
70#ifdef _WIN32
71static char* quotePath(const char* path);
72#endif
73
74/* The execv() definition in mingw is slightly bogus.
75 * It takes a second argument of type 'const char* const*'
76 * while POSIX mandates char** instead.
77 *
78 * To avoid compiler warnings, define the safe_execv macro
79 * to perform an explicit cast with mingw.
80 */
81#ifdef _WIN32
82#  define safe_execv(_filepath,_argv)  execv((_filepath),(const char* const*)(_argv))
83#else
84#  define safe_execv(_filepath,_argv)  execv((_filepath),(_argv))
85#endif
86
87/* Main routine */
88int main(int argc, char** argv)
89{
90    const char* avdName = NULL;
91    char*       avdArch = NULL;
92    char*       emulatorPath;
93    int         force_32bit = 0;
94
95    /* Define ANDROID_EMULATOR_DEBUG to 1 in your environment if you want to
96     * see the debug messages from this launcher program.
97     */
98    const char* debug = getenv("ANDROID_EMULATOR_DEBUG");
99
100    if (debug != NULL && *debug && *debug != '0')
101        android_verbose = 1;
102
103    /* Parse command-line and look for
104     * 1) an avd name either in the form or '-avd <name>' or '@<name>'
105     * 2) '-force-32bit' which always use 32-bit emulator on 64-bit platforms
106     */
107    int  nn;
108    for (nn = 1; nn < argc; nn++) {
109        const char* opt = argv[nn];
110
111        if (!strcmp(opt,"-qemu"))
112            break;
113
114        if (!strcmp(opt,"-force-32bit")) {
115            force_32bit = 1;
116            continue;
117        }
118
119        if (!avdName) {
120            if (!strcmp(opt,"-avd") && nn+1 < argc) {
121                avdName = argv[nn+1];
122            }
123            else if (opt[0] == '@' && opt[1] != '\0') {
124                avdName = opt+1;
125            }
126        }
127    }
128
129    /* If there is an AVD name, we're going to extract its target architecture
130     * by looking at its config.ini
131     */
132    if (avdName != NULL) {
133        D("Found AVD name '%s'\n", avdName);
134        avdArch = path_getAvdTargetArch(avdName);
135        D("Found AVD target architecture: %s\n", avdArch);
136    } else {
137        /* Otherwise, using the ANDROID_PRODUCT_OUT directory */
138        const char* androidOut = getenv("ANDROID_PRODUCT_OUT");
139
140        if (androidOut != NULL && *androidOut != '\0') {
141            D("Found ANDROID_PRODUCT_OUT: %s\n", androidOut);
142            avdArch = path_getBuildTargetArch(androidOut);
143            D("Found build target architecture: %s\n", avdArch);
144        }
145    }
146
147    if (avdArch == NULL) {
148        avdArch = "arm";
149        D("Can't determine target AVD architecture: defaulting to %s\n", avdArch);
150    }
151
152    /* Find the architecture-specific program in the same directory */
153    emulatorPath = getTargetEmulatorPath(argv[0], avdArch, force_32bit);
154    D("Found target-specific emulator binary: %s\n", emulatorPath);
155
156    /* Replace it in our command-line */
157    argv[0] = emulatorPath;
158
159#ifdef _WIN32
160    /* Looks like execv() in mingw (or is it MSVCRT.DLL?) doesn't
161     * support a space in argv[0] unless we explicitely quote it.
162     * IMPORTANT: do not quote the first argument to execv() or it will fail.
163     * This was tested on a 32-bit Vista installation.
164     */
165    if (strchr(emulatorPath, ' ')) {
166        argv[0] = quotePath(emulatorPath);
167        D("Quoted emulator binary path: %s\n", emulatorPath);
168    }
169#endif
170
171    /* We need to find the location of the GLES emulation shared libraries
172     * and modify either LD_LIBRARY_PATH or PATH accordingly
173     */
174    {
175        char*  sharedLibPath = getSharedLibraryPath(emulatorPath, GLES_EMULATION_LIB);
176
177        if (sharedLibPath != NULL) {
178            D("Found OpenGLES emulation libraries in %s\n", sharedLibPath);
179            prependSharedLibraryPath(sharedLibPath);
180        } else {
181            D("Could not find OpenGLES emulation host libraries!\n");
182        }
183    }
184
185    /* Launch it with the same set of options ! */
186    safe_execv(emulatorPath, argv);
187
188    /* We could not launch the program ! */
189    fprintf(stderr, "Could not launch '%s': %s\n", emulatorPath, strerror(errno));
190    return errno;
191}
192
193static int
194getHostOSBitness()
195{
196  /*
197     This function returns 64 if host is running 64-bit OS, or 32 otherwise.
198
199     It uses the same technique in ndk/build/core/ndk-common.sh.
200     Here are comments from there:
201
202  ## On Linux or Darwin, a 64-bit kernel (*) doesn't mean that the user-land
203  ## is always 32-bit, so use "file" to determine the bitness of the shell
204  ## that invoked us. The -L option is used to de-reference symlinks.
205  ##
206  ## Note that on Darwin, a single executable can contain both x86 and
207  ## x86_64 machine code, so just look for x86_64 (darwin) or x86-64 (Linux)
208  ## in the output.
209
210    (*) ie. The following code doesn't always work:
211        struct utsname u;
212        int host_runs_64bit_OS = (uname(&u) == 0 && strcmp(u.machine, "x86_64") == 0);
213  */
214    return system("file -L \"$SHELL\" | grep -q \"x86[_-]64\"") == 0 ? 64 : 32;
215}
216
217/* Find the target-specific emulator binary. This will be something
218 * like  <programDir>/emulator-<targetArch>, where <programDir> is
219 * the directory of the current program.
220 */
221static char*
222getTargetEmulatorPath(const char* progName, const char* avdArch, const int force_32bit)
223{
224    char*  progDir;
225    char   path[PATH_MAX], *pathEnd=path+sizeof(path), *p;
226    const char* emulatorPrefix = "emulator-";
227    const char* emulator64Prefix = "emulator64-";
228#ifdef _WIN32
229    const char* exeExt = ".exe";
230    /* ToDo: currently amd64-mingw32msvc-gcc doesn't work (http://b/issue?id=5949152)
231             which prevents us from generating 64-bit emulator for Windows */
232    int search_for_64bit_emulator = 0;
233#else
234    const char* exeExt = "";
235    int search_for_64bit_emulator = !force_32bit && getHostOSBitness() == 64;
236#endif
237
238    /* Get program's directory name in progDir */
239    path_split(progName, &progDir, NULL);
240
241    if (search_for_64bit_emulator) {
242        /* Find 64-bit emulator first */
243        p = bufprint(path, pathEnd, "%s/%s%s%s", progDir, emulator64Prefix, avdArch, exeExt);
244        if (p >= pathEnd) {
245            APANIC("Path too long: %s\n", progName);
246        }
247        if (path_exists(path)) {
248            free(progDir);
249            return strdup(path);
250        }
251    }
252
253    /* Find 32-bit emulator */
254    p = bufprint(path, pathEnd, "%s/%s%s%s", progDir, emulatorPrefix, avdArch, exeExt);
255    free(progDir);
256    if (p >= pathEnd) {
257        APANIC("Path too long: %s\n", progName);
258    }
259
260    if (path_exists(path)) {
261        return strdup(path);
262    }
263
264    /* Mmm, the file doesn't exist, If there is no slash / backslash
265     * in our path, we're going to try to search it in our path.
266     */
267#ifdef _WIN32
268    if (strchr(progName, '/') == NULL && strchr(progName, '\\') == NULL) {
269#else
270    if (strchr(progName, '/') == NULL) {
271#endif
272        if (search_for_64bit_emulator) {
273           p = bufprint(path, pathEnd, "%s%s%s", emulator64Prefix, avdArch, exeExt);
274           if (p < pathEnd) {
275               char*  resolved = path_search_exec(path);
276               if (resolved != NULL)
277                   return resolved;
278           }
279        }
280
281        p = bufprint(path, pathEnd, "%s%s%s", emulatorPrefix, avdArch, exeExt);
282        if (p < pathEnd) {
283            char*  resolved = path_search_exec(path);
284            if (resolved != NULL)
285                return resolved;
286        }
287    }
288
289    /* Otherwise, the program is missing */
290    APANIC("Missing arch-specific emulator program: %s\n", path);
291    return NULL;
292}
293
294/* return 1 iff <path>/<filename> exists */
295static int
296probePathForFile(const char* path, const char* filename)
297{
298    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
299    p = bufprint(temp, end, "%s/%s", path, filename);
300    D("Probing for: %s\n", temp);
301    return (p < end && path_exists(temp));
302}
303
304/* Find the directory containing a given shared library required by the
305 * emulator (for GLES emulation). We will probe several directories
306 * that correspond to various use-cases.
307 *
308 * Caller must free() result string. NULL if not found.
309 */
310
311static char*
312getSharedLibraryPath(const char* progName, const char* libName)
313{
314    char* progDir;
315    char* result = NULL;
316    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
317
318    /* Get program's directory name */
319    path_split(progName, &progDir, NULL);
320
321    /* First, try to probe the program's directory itself, this corresponds
322     * to the standalone build with ./android-configure.sh where the script
323     * will copy the host shared library under external/qemu/objs where
324     * the binaries are located.
325     */
326    if (probePathForFile(progDir, libName)) {
327        return progDir;
328    }
329
330    /* Try under $progDir/lib/, this should correspond to the SDK installation
331     * where the binary is under tools/, and the libraries under tools/lib/
332     */
333    {
334        p = bufprint(temp, end, "%s/lib", progDir);
335        if (p < end && probePathForFile(temp, libName)) {
336            result = strdup(temp);
337            goto EXIT;
338        }
339    }
340
341    /* try in $progDir/../lib, this corresponds to the platform build
342     * where the emulator binary is under out/host/<system>/bin and
343     * the libraries are under out/host/<system>/lib
344     */
345    {
346        char* parentDir = path_parent(progDir, 1);
347
348        if (parentDir == NULL) {
349            parentDir = strdup(".");
350        }
351        p = bufprint(temp, end, "%s/lib", parentDir);
352        free(parentDir);
353        if (p < end && probePathForFile(temp, libName)) {
354            result = strdup(temp);
355            goto EXIT;
356        }
357    }
358
359    /* Nothing found! */
360EXIT:
361    free(progDir);
362    return result;
363}
364
365/* Prepend the path in 'prefix' to either LD_LIBRARY_PATH or PATH to
366 * ensure that the shared libraries inside the path will be available
367 * through dlopen() to the emulator program being launched.
368 */
369static void
370prependSharedLibraryPath(const char* prefix)
371{
372    char temp[2048], *p=temp, *end=p+sizeof(temp);
373#ifdef _WIN32
374    const char* path = getenv("PATH");
375    if (path == NULL || path[0] == '\0') {
376        p = bufprint(temp, end, "PATH=%s", prefix);
377    } else {
378        p = bufprint(temp, end, "PATH=%s;%s", path, prefix);
379    }
380    /* Ignore overflow, this will push some paths out of the variable, but
381     * so be it. */
382    D("Setting %s\n", temp);
383    putenv(strdup(temp));
384#else
385    const char* path = getenv("LD_LIBRARY_PATH");
386    if (path != NULL && path[0] != '\0') {
387        p = bufprint(temp, end, "%s:%s", prefix, path);
388        prefix = temp;
389    }
390    setenv("LD_LIBRARY_PATH",prefix,1);
391    D("Setting LD_LIBRARY_PATH=%s\n", prefix);
392#endif
393}
394
395#ifdef _WIN32
396static char*
397quotePath(const char* path)
398{
399    int   len = strlen(path);
400    char* ret = malloc(len+3);
401
402    ret[0] = '"';
403    memcpy(ret+1, path, len);
404    ret[len+1] = '"';
405    ret[len+2] = '\0';
406
407    return ret;
408}
409#endif /* _WIN32 */
410