Home | History | Annotate | Download | only in wrapsim
      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