• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
33 int 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 #define  GLES_EMULATION_LIB  "libOpenglRender" DLL_EXTENSION
54 
55 /* Forward declarations */
56 static char* getTargetEmulatorPath(const char* progName, const char* avdArch);
57 static char* getSharedLibraryPath(const char* progName, const char* libName);
58 static void  prependSharedLibraryPath(const char* prefix);
59 
60 #ifdef _WIN32
61 static char* quotePath(const char* path);
62 #endif
63 
64 /* The execv() definition in mingw is slightly bogus.
65  * It takes a second argument of type 'const char* const*'
66  * while POSIX mandates char** instead.
67  *
68  * To avoid compiler warnings, define the safe_execv macro
69  * to perform an explicit cast with mingw.
70  */
71 #ifdef _WIN32
72 #  define safe_execv(_filepath,_argv)  execv((_filepath),(const char* const*)(_argv))
73 #else
74 #  define safe_execv(_filepath,_argv)  execv((_filepath),(_argv))
75 #endif
76 
77 /* Main routine */
main(int argc,char ** argv)78 int main(int argc, char** argv)
79 {
80     const char* avdName = NULL;
81     char*       avdArch = NULL;
82     char*       emulatorPath;
83 
84     /* Define ANDROID_EMULATOR_DEBUG to 1 in your environment if you want to
85      * see the debug messages from this launcher program.
86      */
87     const char* debug = getenv("ANDROID_EMULATOR_DEBUG");
88 
89     if (debug != NULL && *debug && *debug != '0')
90         android_verbose = 1;
91 
92     /* Parse command-line and look for an avd name
93      * Either in the form or '-avd <name>' or '@<name>'
94      */
95     int  nn;
96     for (nn = 1; nn < argc; nn++) {
97         const char* opt = argv[nn];
98 
99         if (!strcmp(opt,"-qemu"))
100             break;
101 
102         if (!strcmp(opt,"-avd") && nn+1 < argc) {
103             avdName = argv[nn+1];
104             break;
105         }
106         else if (opt[0] == '@' && opt[1] != '\0') {
107             avdName = opt+1;
108             break;
109         }
110     }
111 
112     /* If there is an AVD name, we're going to extract its target architecture
113      * by looking at its config.ini
114      */
115     if (avdName != NULL) {
116         D("Found AVD name '%s'\n", avdName);
117         avdArch = path_getAvdTargetArch(avdName);
118         D("Found AVD target architecture: %s\n", avdArch);
119     } else {
120         /* Otherwise, using the ANDROID_PRODUCT_OUT directory */
121         const char* androidOut = getenv("ANDROID_PRODUCT_OUT");
122 
123         if (androidOut != NULL && *androidOut != '\0') {
124             D("Found ANDROID_PRODUCT_OUT: %s\n", androidOut);
125             avdArch = path_getBuildTargetArch(androidOut);
126             D("Found build target architecture: %s\n", avdArch);
127         }
128     }
129 
130     if (avdArch == NULL) {
131         avdArch = "arm";
132         D("Can't determine target AVD architecture: defaulting to %s\n", avdArch);
133     }
134 
135     /* Find the architecture-specific program in the same directory */
136     emulatorPath = getTargetEmulatorPath(argv[0], avdArch);
137     D("Found target-specific emulator binary: %s\n", emulatorPath);
138 
139     /* Replace it in our command-line */
140     argv[0] = emulatorPath;
141 
142 #ifdef _WIN32
143     /* Looks like execv() in mingw (or is it MSVCRT.DLL?) doesn't
144      * support a space in argv[0] unless we explicitely quote it.
145      * IMPORTANT: do not quote the first argument to execv() or it will fail.
146      * This was tested on a 32-bit Vista installation.
147      */
148     if (strchr(emulatorPath, ' ')) {
149         argv[0] = quotePath(emulatorPath);
150         D("Quoted emulator binary path: %s\n", emulatorPath);
151     }
152 #endif
153 
154     /* We need to find the location of the GLES emulation shared libraries
155      * and modify either LD_LIBRARY_PATH or PATH accordingly
156      */
157     {
158         char*  sharedLibPath = getSharedLibraryPath(emulatorPath, GLES_EMULATION_LIB);
159 
160         if (sharedLibPath != NULL) {
161             D("Found OpenGLES emulation libraries in %s\n", sharedLibPath);
162             prependSharedLibraryPath(sharedLibPath);
163         } else {
164             D("Could not find OpenGLES emulation host libraries!\n");
165         }
166     }
167 
168     /* Launch it with the same set of options ! */
169     safe_execv(emulatorPath, argv);
170 
171     /* We could not launch the program ! */
172     fprintf(stderr, "Could not launch '%s': %s\n", emulatorPath, strerror(errno));
173     return errno;
174 }
175 
176 
177 /* Find the target-specific emulator binary. This will be something
178  * like  <programDir>/emulator-<targetArch>, where <programDir> is
179  * the directory of the current program.
180  */
181 static char*
getTargetEmulatorPath(const char * progName,const char * avdArch)182 getTargetEmulatorPath(const char* progName, const char* avdArch)
183 {
184     char*  progDir;
185     char   temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
186 #ifdef _WIN32
187     const char* exeExt = ".exe";
188 #else
189     const char* exeExt = "";
190 #endif
191 
192     /* Get program's directory name in progDir */
193     path_split(progName, &progDir, NULL);
194 
195     p = bufprint(temp, end, "%s/emulator-%s%s", progDir, avdArch, exeExt);
196     free(progDir);
197     if (p >= end) {
198         APANIC("Path too long: %s\n", progName);
199     }
200 
201     if (path_exists(temp)) {
202         return strdup(temp);
203     }
204 
205     /* Mmm, the file doesn't exist, If there is no slash / backslash
206      * in our path, we're going to try to search it in our path.
207      */
208 #ifdef _WIN32
209     if (strchr(progName, '/') == NULL && strchr(progName, '\\') == NULL) {
210 #else
211     if (strchr(progName, '/') == NULL) {
212 #endif
213         p = bufprint(temp, end, "emulator-%s%s", avdArch, exeExt);
214         if (p < end) {
215             char*  resolved = path_search_exec(temp);
216             if (resolved != NULL)
217                 return resolved;
218         }
219     }
220 
221     /* Otherwise, the program is missing */
222     APANIC("Missing arch-specific emulator program: %s\n", temp);
223     return NULL;
224 }
225 
226 /* return 1 iff <path>/<filename> exists */
227 static int
228 probePathForFile(const char* path, const char* filename)
229 {
230     char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
231     p = bufprint(temp, end, "%s/%s", path, filename);
232     D("Probing for: %s\n", temp);
233     return (p < end && path_exists(temp));
234 }
235 
236 /* Find the directory containing a given shared library required by the
237  * emulator (for GLES emulation). We will probe several directories
238  * that correspond to various use-cases.
239  *
240  * Caller must free() result string. NULL if not found.
241  */
242 
243 static char*
244 getSharedLibraryPath(const char* progName, const char* libName)
245 {
246     char* progDir;
247     char* result = NULL;
248     char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
249 
250     /* Get program's directory name */
251     path_split(progName, &progDir, NULL);
252 
253     /* First, try to probe the program's directory itself, this corresponds
254      * to the standalone build with ./android-configure.sh where the script
255      * will copy the host shared library under external/qemu/objs where
256      * the binaries are located.
257      */
258     if (probePathForFile(progDir, libName)) {
259         return progDir;
260     }
261 
262     /* Try under $progDir/lib/, this should correspond to the SDK installation
263      * where the binary is under tools/, and the libraries under tools/lib/
264      */
265     {
266         p = bufprint(temp, end, "%s/lib", progDir);
267         if (p < end && probePathForFile(temp, libName)) {
268             result = strdup(temp);
269             goto EXIT;
270         }
271     }
272 
273     /* try in $progDir/../lib, this corresponds to the platform build
274      * where the emulator binary is under out/host/<system>/lib and
275      * the libraries are under out/host/<system>/lib
276      */
277     {
278         char* parentDir = path_parent(progDir, 1);
279 
280         if (parentDir == NULL) {
281             parentDir = strdup(".");
282         }
283         p = bufprint(temp, end, "%s/lib", parentDir);
284         free(parentDir);
285         if (p < end && probePathForFile(temp, libName)) {
286             result = strdup(temp);
287             goto EXIT;
288         }
289     }
290 
291     /* Nothing found! */
292 EXIT:
293     free(progDir);
294     return result;
295 }
296 
297 /* Prepend the path in 'prefix' to either LD_LIBRARY_PATH or PATH to
298  * ensure that the shared libraries inside the path will be available
299  * through dlopen() to the emulator program being launched.
300  */
301 static void
302 prependSharedLibraryPath(const char* prefix)
303 {
304     char temp[2048], *p=temp, *end=p+sizeof(temp);
305 #ifdef _WIN32
306     const char* path = getenv("PATH");
307     if (path == NULL || path[0] == '\0') {
308         p = bufprint(temp, end, "PATH=%s", prefix);
309     } else {
310         p = bufprint(temp, end, "PATH=%s;%s", path, prefix);
311     }
312     /* Ignore overflow, this will push some paths out of the variable, but
313      * so be it. */
314     D("Setting %s\n", temp);
315     putenv(strdup(temp));
316 #else
317     const char* path = getenv("LD_LIBRARY_PATH");
318     if (path != NULL && path[0] != '\0') {
319         p = bufprint(temp, end, "%s:%s", prefix, path);
320         prefix = temp;
321     }
322     setenv("LD_LIBRARY_PATH",prefix,1);
323     D("Setting LD_LIBRARY_PATH=%s\n", prefix);
324 #endif
325 }
326 
327 #ifdef _WIN32
328 static char*
329 quotePath(const char* path)
330 {
331     int   len = strlen(path);
332     char* ret = malloc(len+3);
333 
334     ret[0] = '"';
335     memcpy(ret+1, path, len);
336     ret[len+1] = '"';
337     ret[len+2] = '\0';
338 
339     return ret;
340 }
341 #endif /* _WIN32 */
342