| /* |
| * Copyright 2007 The Android Open Source Project |
| * |
| * Launch the specified program and, if "-wait" was specified, wait for it |
| * to exit. |
| * |
| * When in "wait mode", print a message indicating the exit status, then |
| * wait for Ctrl-C before we exit. This is useful if we were launched |
| * with "xterm -e", because it lets us see the output before the xterm bails. |
| * |
| * We want to ignore signals while waiting, so Ctrl-C kills the child rather |
| * than us, but we need to configure the signals *after* the fork() so we |
| * don't block them for the child too. |
| */ |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include <limits.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <assert.h> |
| |
| /* |
| * This is appended to $ANDROID_PRODUCT_OUT, |
| * e.g. "/work/device/out/debug/host/linux-x8x/product/sim". |
| */ |
| static const char* kWrapLib = "/system/lib/libwrapsim.so"; |
| |
| |
| /* |
| * Configure LD_PRELOAD if possible. |
| * |
| * Returns newly-allocated storage with the preload path. |
| */ |
| static char* configurePreload(void) |
| { |
| const char* outEnv = getenv("ANDROID_PRODUCT_OUT"); |
| const char* preloadEnv = getenv("LD_PRELOAD"); |
| char* preload = NULL; |
| |
| if (preloadEnv != NULL) { |
| /* TODO: append our stuff to existing LD_PRELOAD string */ |
| fprintf(stderr, |
| "LW WARNING: LD_PRELOAD already set, not adding libwrapsim\n"); |
| } else if (outEnv == NULL || *outEnv == '\0') { |
| fprintf(stderr, "LW WARNING: " |
| "$ANDROID_PRODUCT_OUT not in env, not setting LD_PRELOAD\n"); |
| } else { |
| preload = (char*) malloc(strlen(outEnv) + strlen(kWrapLib) +1); |
| sprintf(preload, "%s%s", outEnv, kWrapLib); |
| setenv("LD_PRELOAD", preload, 1); |
| printf("LW: launching with LD_PRELOAD=%s\n", preload); |
| } |
| |
| /* Let the process know that it's executing inside this LD_PRELOAD |
| * wrapper. |
| */ |
| setenv("ANDROID_WRAPSIM", "1", 1); |
| |
| return preload; |
| } |
| |
| /* |
| * Configure some environment variables that the runtime wants. |
| * |
| * Returns 0 if all goes well. |
| */ |
| static int configureEnvironment() |
| { |
| const char* outEnv = getenv("ANDROID_PRODUCT_OUT"); |
| char pathBuf[PATH_MAX]; |
| int outLen; |
| |
| if (outEnv == NULL || *outEnv == '\0') { |
| fprintf(stderr, "LW WARNING: " |
| "$ANDROID_PRODUCT_OUT not in env, not configuring environment\n"); |
| return 1; |
| } |
| outLen = strlen(outEnv); |
| assert(outLen + 64 < PATH_MAX); |
| memcpy(pathBuf, outEnv, outLen); |
| strcpy(pathBuf + outLen, "/system/lib"); |
| |
| /* |
| * Linux wants LD_LIBRARY_PATH |
| * Mac OS X wants DYLD_LIBRARY_PATH |
| * gdb under Mac OS X tramples on both of the above, so we added |
| * ANDROID_LIBRARY_PATH as a workaround. |
| * |
| * We're only supporting Linux now, so just set LD_LIBRARY_PATH. Note |
| * this stomps the existing value, if any. |
| * |
| * If we only needed this for System.loadLibrary() we could do it later, |
| * but we need it to get the runtime started. |
| */ |
| printf("LW: setting LD_LIBRARY_PATH=%s\n", pathBuf); |
| setenv("LD_LIBRARY_PATH", pathBuf, 1); |
| |
| /* |
| * Trusted certificates are found, for some bizarre reason, through |
| * the JAVA_HOME environment variable. We don't need to set this |
| * here, but it's convenient to do so. |
| */ |
| strcpy(pathBuf /*+ outLen*/, "/system"); |
| printf("LW: setting JAVA_HOME=%s\n", pathBuf); |
| setenv("JAVA_HOME", pathBuf, 1); |
| |
| return 0; |
| } |
| |
| /* |
| * Redirect stdout/stderr to the specified file. If "fileName" is NULL, |
| * this returns successfully without doing anything. |
| * |
| * Returns 0 on success. |
| */ |
| static int redirectStdio(const char* fileName) |
| { |
| int fd; |
| |
| if (fileName == NULL) |
| return 0; |
| |
| printf("Redirecting stdio to append to '%s'\n", fileName); |
| fflush(stdout); |
| fflush(stderr); |
| |
| fd = open(fileName, O_WRONLY | O_APPEND | O_CREAT, 0666); |
| if (fd < 0) { |
| fprintf(stderr, "ERROR: unable to open '%s' for writing\n", |
| fileName); |
| return 1; |
| } |
| dup2(fd, 1); |
| dup2(fd, 2); |
| close(fd); |
| |
| return 0; |
| } |
| |
| /* |
| * Launch the requested process directly. |
| * |
| * On success this does not return (ever). |
| */ |
| static int launch(char* argv[], const char* outputFile) |
| { |
| (void) configurePreload(); |
| (void) redirectStdio(outputFile); |
| execvp(argv[0], argv); |
| fprintf(stderr, "execvp %s failed: %s\n", argv[0], strerror(errno)); |
| return 1; |
| } |
| |
| /* |
| * Launch in a sub-process and wait for it to finish. |
| */ |
| static int launchWithWait(char* argv[], const char* outputFile) |
| { |
| pid_t child; |
| |
| child = fork(); |
| if (child < 0) { |
| fprintf(stderr, "fork() failed: %s\n", strerror(errno)); |
| return 1; |
| } else if (child == 0) { |
| /* |
| * This is the child, set up LD_PRELOAD if possible and launch. |
| */ |
| (void) configurePreload(); |
| (void) redirectStdio(outputFile); |
| execvp(argv[0], argv); |
| fprintf(stderr, "execvp %s failed: %s\n", argv[0], strerror(errno)); |
| return 1; |
| } else { |
| pid_t result; |
| int status; |
| |
| signal(SIGINT, SIG_IGN); |
| signal(SIGQUIT, SIG_IGN); |
| |
| while (1) { |
| printf("LW: in pid %d (grp=%d), waiting on pid %d\n", |
| (int) getpid(), (int) getpgrp(), (int) child); |
| result = waitpid(child, &status, 0); |
| if (result < 0) { |
| if (errno == EINTR) { |
| printf("Hiccup!\n"); |
| continue; |
| } else { |
| fprintf(stderr, "waitpid failed: %s\n", strerror(errno)); |
| return 1; |
| } |
| } else if (result != child) { |
| fprintf(stderr, "bizarre: waitpid returned %d (wanted %d)\n", |
| result, child); |
| return 1; |
| } else { |
| break; |
| } |
| } |
| |
| printf("\n"); |
| if (WIFEXITED(status)) { |
| printf("LW: process exited (status=%d)", WEXITSTATUS(status)); |
| } else if (WIFSIGNALED(status)) { |
| printf("LW: process killed by signal %d", WTERMSIG(status)); |
| } else { |
| printf("LW: process freaked out, status=0x%x\n", status); |
| } |
| if (WCOREDUMP(status)) { |
| printf(" (core dumped)"); |
| } |
| printf("\n"); |
| |
| signal(SIGINT, SIG_DFL); |
| signal(SIGQUIT, SIG_DFL); |
| |
| /* |
| * The underlying process may have changed process groups and pulled |
| * itself into the foreground. Now that it's gone, pull ourselves |
| * back into the foreground. |
| */ |
| signal(SIGTTOU, SIG_IGN); |
| if (tcsetpgrp(fileno(stdin), getpgrp()) != 0) |
| fprintf(stderr, "WARNING: tcsetpgrp failed\n"); |
| |
| printf("\nHit Ctrl-C or close window.\n"); |
| |
| while (1) { |
| sleep(10); |
| } |
| |
| /* not reached */ |
| return 0; |
| } |
| } |
| |
| |
| /* |
| * All args are passed through. |
| */ |
| int main(int argc, char** argv) |
| { |
| int waitForChild = 0; |
| const char* outputFile = NULL; |
| int result; |
| |
| /* |
| * Skip past the reference to ourselves, and check for args. |
| */ |
| argv++; |
| argc--; |
| while (argc > 0) { |
| if (strcmp(argv[0], "-wait") == 0) { |
| waitForChild = 1; |
| } else if (strcmp(argv[0], "-output") == 0 && argc > 1) { |
| argv++; |
| argc--; |
| outputFile = argv[0]; |
| } else { |
| /* no more args for us */ |
| break; |
| } |
| |
| argv++; |
| argc--; |
| } |
| |
| if (argc == 0) { |
| fprintf(stderr, |
| "Usage: launch-wrapper [-wait] [-output filename] <cmd> [args...]\n"); |
| result = 2; |
| goto bail; |
| } |
| |
| /* |
| * Configure some environment variables. |
| */ |
| if (configureEnvironment() != 0) { |
| result = 1; |
| goto bail; |
| } |
| |
| /* |
| * Launch. |
| */ |
| if (waitForChild) |
| result = launchWithWait(argv, outputFile); |
| else |
| result = launch(argv, outputFile); |
| |
| bail: |
| if (result != 0) |
| sleep(2); |
| return result; |
| } |
| |