The Android Open Source Project | 52d4c30 | 2009-03-03 19:29:09 -0800 | [diff] [blame] | 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 | |