302 lines
7.9 KiB
C
302 lines
7.9 KiB
C
/*
|
|
* 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;
|
|
}
|
|
|