blob: c4f0efbb5e4f25768602648a997573109d63de10 [file] [log] [blame]
The Android Open Source Project52d4c302009-03-03 19:29:09 -08001/*
2 * Copyright 2007 The Android Open Source Project
3 *
4 * Launch the specified program and, if "-wait" was specified, wait for it
5 * to exit.
6 *
7 * When in "wait mode", print a message indicating the exit status, then
8 * wait for Ctrl-C before we exit. This is useful if we were launched
9 * with "xterm -e", because it lets us see the output before the xterm bails.
10 *
11 * We want to ignore signals while waiting, so Ctrl-C kills the child rather
12 * than us, but we need to configure the signals *after* the fork() so we
13 * don't block them for the child too.
14 */
15#include <stdlib.h>
16#include <stdio.h>
17#include <unistd.h>
18#include <string.h>
19#include <sys/types.h>
20#include <sys/wait.h>
21#include <limits.h>
22#include <fcntl.h>
23#include <errno.h>
24#include <assert.h>
25
26/*
27 * This is appended to $ANDROID_PRODUCT_OUT,
28 * e.g. "/work/device/out/debug/host/linux-x8x/product/sim".
29 */
30static const char* kWrapLib = "/system/lib/libwrapsim.so";
31
32
33/*
34 * Configure LD_PRELOAD if possible.
35 *
36 * Returns newly-allocated storage with the preload path.
37 */
38static char* configurePreload(void)
39{
40 const char* outEnv = getenv("ANDROID_PRODUCT_OUT");
41 const char* preloadEnv = getenv("LD_PRELOAD");
42 char* preload = NULL;
43
44 if (preloadEnv != NULL) {
45 /* TODO: append our stuff to existing LD_PRELOAD string */
46 fprintf(stderr,
47 "LW WARNING: LD_PRELOAD already set, not adding libwrapsim\n");
48 } else if (outEnv == NULL || *outEnv == '\0') {
49 fprintf(stderr, "LW WARNING: "
50 "$ANDROID_PRODUCT_OUT not in env, not setting LD_PRELOAD\n");
51 } else {
52 preload = (char*) malloc(strlen(outEnv) + strlen(kWrapLib) +1);
53 sprintf(preload, "%s%s", outEnv, kWrapLib);
54 setenv("LD_PRELOAD", preload, 1);
55 printf("LW: launching with LD_PRELOAD=%s\n", preload);
56 }
57
58 /* Let the process know that it's executing inside this LD_PRELOAD
59 * wrapper.
60 */
61 setenv("ANDROID_WRAPSIM", "1", 1);
62
63 return preload;
64}
65
66/*
67 * Configure some environment variables that the runtime wants.
68 *
69 * Returns 0 if all goes well.
70 */
71static int configureEnvironment()
72{
73 const char* outEnv = getenv("ANDROID_PRODUCT_OUT");
74 char pathBuf[PATH_MAX];
75 int outLen;
76
77 if (outEnv == NULL || *outEnv == '\0') {
78 fprintf(stderr, "LW WARNING: "
79 "$ANDROID_PRODUCT_OUT not in env, not configuring environment\n");
80 return 1;
81 }
82 outLen = strlen(outEnv);
83 assert(outLen + 64 < PATH_MAX);
84 memcpy(pathBuf, outEnv, outLen);
85 strcpy(pathBuf + outLen, "/system/lib");
86
87 /*
88 * Linux wants LD_LIBRARY_PATH
89 * Mac OS X wants DYLD_LIBRARY_PATH
90 * gdb under Mac OS X tramples on both of the above, so we added
91 * ANDROID_LIBRARY_PATH as a workaround.
92 *
93 * We're only supporting Linux now, so just set LD_LIBRARY_PATH. Note
94 * this stomps the existing value, if any.
95 *
96 * If we only needed this for System.loadLibrary() we could do it later,
97 * but we need it to get the runtime started.
98 */
99 printf("LW: setting LD_LIBRARY_PATH=%s\n", pathBuf);
100 setenv("LD_LIBRARY_PATH", pathBuf, 1);
101
102 /*
103 * Trusted certificates are found, for some bizarre reason, through
104 * the JAVA_HOME environment variable. We don't need to set this
105 * here, but it's convenient to do so.
106 */
107 strcpy(pathBuf /*+ outLen*/, "/system");
108 printf("LW: setting JAVA_HOME=%s\n", pathBuf);
109 setenv("JAVA_HOME", pathBuf, 1);
110
111 return 0;
112}
113
114/*
115 * Redirect stdout/stderr to the specified file. If "fileName" is NULL,
116 * this returns successfully without doing anything.
117 *
118 * Returns 0 on success.
119 */
120static int redirectStdio(const char* fileName)
121{
122 int fd;
123
124 if (fileName == NULL)
125 return 0;
126
127 printf("Redirecting stdio to append to '%s'\n", fileName);
128 fflush(stdout);
129 fflush(stderr);
130
131 fd = open(fileName, O_WRONLY | O_APPEND | O_CREAT, 0666);
132 if (fd < 0) {
133 fprintf(stderr, "ERROR: unable to open '%s' for writing\n",
134 fileName);
135 return 1;
136 }
137 dup2(fd, 1);
138 dup2(fd, 2);
139 close(fd);
140
141 return 0;
142}
143
144/*
145 * Launch the requested process directly.
146 *
147 * On success this does not return (ever).
148 */
149static int launch(char* argv[], const char* outputFile)
150{
151 (void) configurePreload();
152 (void) redirectStdio(outputFile);
153 execvp(argv[0], argv);
154 fprintf(stderr, "execvp %s failed: %s\n", argv[0], strerror(errno));
155 return 1;
156}
157
158/*
159 * Launch in a sub-process and wait for it to finish.
160 */
161static int launchWithWait(char* argv[], const char* outputFile)
162{
163 pid_t child;
164
165 child = fork();
166 if (child < 0) {
167 fprintf(stderr, "fork() failed: %s\n", strerror(errno));
168 return 1;
169 } else if (child == 0) {
170 /*
171 * This is the child, set up LD_PRELOAD if possible and launch.
172 */
173 (void) configurePreload();
174 (void) redirectStdio(outputFile);
175 execvp(argv[0], argv);
176 fprintf(stderr, "execvp %s failed: %s\n", argv[0], strerror(errno));
177 return 1;
178 } else {
179 pid_t result;
180 int status;
181
182 signal(SIGINT, SIG_IGN);
183 signal(SIGQUIT, SIG_IGN);
184
185 while (1) {
186 printf("LW: in pid %d (grp=%d), waiting on pid %d\n",
187 (int) getpid(), (int) getpgrp(), (int) child);
188 result = waitpid(child, &status, 0);
189 if (result < 0) {
190 if (errno == EINTR) {
191 printf("Hiccup!\n");
192 continue;
193 } else {
194 fprintf(stderr, "waitpid failed: %s\n", strerror(errno));
195 return 1;
196 }
197 } else if (result != child) {
198 fprintf(stderr, "bizarre: waitpid returned %d (wanted %d)\n",
199 result, child);
200 return 1;
201 } else {
202 break;
203 }
204 }
205
206 printf("\n");
207 if (WIFEXITED(status)) {
208 printf("LW: process exited (status=%d)", WEXITSTATUS(status));
209 } else if (WIFSIGNALED(status)) {
210 printf("LW: process killed by signal %d", WTERMSIG(status));
211 } else {
212 printf("LW: process freaked out, status=0x%x\n", status);
213 }
214 if (WCOREDUMP(status)) {
215 printf(" (core dumped)");
216 }
217 printf("\n");
218
219 signal(SIGINT, SIG_DFL);
220 signal(SIGQUIT, SIG_DFL);
221
222 /*
223 * The underlying process may have changed process groups and pulled
224 * itself into the foreground. Now that it's gone, pull ourselves
225 * back into the foreground.
226 */
227 signal(SIGTTOU, SIG_IGN);
228 if (tcsetpgrp(fileno(stdin), getpgrp()) != 0)
229 fprintf(stderr, "WARNING: tcsetpgrp failed\n");
230
231 printf("\nHit Ctrl-C or close window.\n");
232
233 while (1) {
234 sleep(10);
235 }
236
237 /* not reached */
238 return 0;
239 }
240}
241
242
243/*
244 * All args are passed through.
245 */
246int main(int argc, char** argv)
247{
248 int waitForChild = 0;
249 const char* outputFile = NULL;
250 int result;
251
252 /*
253 * Skip past the reference to ourselves, and check for args.
254 */
255 argv++;
256 argc--;
257 while (argc > 0) {
258 if (strcmp(argv[0], "-wait") == 0) {
259 waitForChild = 1;
260 } else if (strcmp(argv[0], "-output") == 0 && argc > 1) {
261 argv++;
262 argc--;
263 outputFile = argv[0];
264 } else {
265 /* no more args for us */
266 break;
267 }
268
269 argv++;
270 argc--;
271 }
272
273 if (argc == 0) {
274 fprintf(stderr,
275 "Usage: launch-wrapper [-wait] [-output filename] <cmd> [args...]\n");
276 result = 2;
277 goto bail;
278 }
279
280 /*
281 * Configure some environment variables.
282 */
283 if (configureEnvironment() != 0) {
284 result = 1;
285 goto bail;
286 }
287
288 /*
289 * Launch.
290 */
291 if (waitForChild)
292 result = launchWithWait(argv, outputFile);
293 else
294 result = launch(argv, outputFile);
295
296bail:
297 if (result != 0)
298 sleep(2);
299 return result;
300}
301