• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
30 static 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  */
configurePreload(void)38 static 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  */
configureEnvironment()71 static 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  */
redirectStdio(const char * fileName)120 static 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  */
launch(char * argv[],const char * outputFile)149 static 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  */
launchWithWait(char * argv[],const char * outputFile)161 static 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  */
main(int argc,char ** argv)246 int 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 
296 bail:
297     if (result != 0)
298         sleep(2);
299     return result;
300 }
301 
302