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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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