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