/*
 * Copyright 2007 The Android Open Source Project
 *
 * Initialize the intercepts.
 */
#include "Common.h"

#define __USE_GNU       /* need RTLD_NEXT */
#include <dlfcn.h>

#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/stat.h>
#include <sys/types.h>


/*
 * Global state.
 */
struct WrapSimGlobals gWrapSim;
pthread_once_t gWrapSimInitialized = PTHREAD_ONCE_INIT;

/*
 * Initialize our global state.
 */
static void initGlobals(void)
{
    memset(&gWrapSim, 0xdd, sizeof(gWrapSim));
    gWrapSim.logFd = -1;
    gWrapSim.keyMap = NULL;

    /*
     * Find the original version of functions we override.
     */
    _ws_access = dlsym(RTLD_NEXT, "access");
    _ws_open = dlsym(RTLD_NEXT, "open");
    _ws_open64 = dlsym(RTLD_NEXT, "open64");

    _ws_close = dlsym(RTLD_NEXT, "close");
    _ws_read = dlsym(RTLD_NEXT, "read");
    _ws_readv = dlsym(RTLD_NEXT, "readv");
    _ws_write = dlsym(RTLD_NEXT, "write");
    _ws_writev = dlsym(RTLD_NEXT, "writev");
    _ws_mmap = dlsym(RTLD_NEXT, "mmap");
    _ws_mmap64 = dlsym(RTLD_NEXT, "mmap64");
    _ws_ioctl = dlsym(RTLD_NEXT, "ioctl");

    _ws_chdir = dlsym(RTLD_NEXT, "chdir");
    _ws_chmod = dlsym(RTLD_NEXT, "chmod");
    _ws_chown = dlsym(RTLD_NEXT, "chown");
    _ws_creat = dlsym(RTLD_NEXT, "creat");
    _ws_execve = dlsym(RTLD_NEXT, "execve");
    _ws_getcwd = dlsym(RTLD_NEXT, "getcwd");
    _ws_lchown = dlsym(RTLD_NEXT, "lchown");
    _ws_link = dlsym(RTLD_NEXT, "link");
    _ws_lstat = dlsym(RTLD_NEXT, "lstat");
    _ws_lstat64 = dlsym(RTLD_NEXT, "lstat64");
    _ws___lxstat = dlsym(RTLD_NEXT, "__lxstat");
    _ws___lxstat64 = dlsym(RTLD_NEXT, "__lxstat64");
    _ws_mkdir = dlsym(RTLD_NEXT, "mkdir");
    _ws_readlink = dlsym(RTLD_NEXT, "readlink");
    _ws_rename = dlsym(RTLD_NEXT, "rename");
    _ws_rmdir = dlsym(RTLD_NEXT, "rmdir");
    _ws_stat = dlsym(RTLD_NEXT, "stat");
    _ws_stat64 = dlsym(RTLD_NEXT, "stat64");
    _ws___xstat = dlsym(RTLD_NEXT, "__xstat");
    _ws___xstat64 = dlsym(RTLD_NEXT, "__xstat64");
    _ws_statfs = dlsym(RTLD_NEXT, "statfs");
    _ws_statfs64 = dlsym(RTLD_NEXT, "statfs64");
    _ws_symlink = dlsym(RTLD_NEXT, "symlink");
    _ws_unlink = dlsym(RTLD_NEXT, "unlink");
    _ws_utime = dlsym(RTLD_NEXT, "utime");
    _ws_utimes = dlsym(RTLD_NEXT, "utimes");

    _ws_execl = dlsym(RTLD_NEXT, "execl");
    _ws_execle = dlsym(RTLD_NEXT, "execle");
    _ws_execlp = dlsym(RTLD_NEXT, "execlp");
    _ws_execv = dlsym(RTLD_NEXT, "execv");
    _ws_execvp = dlsym(RTLD_NEXT, "execvp");
    _ws_fopen = dlsym(RTLD_NEXT, "fopen");
    _ws_fopen64 = dlsym(RTLD_NEXT, "fopen64");
    _ws_freopen = dlsym(RTLD_NEXT, "freopen");
    _ws_ftw = dlsym(RTLD_NEXT, "ftw");
    _ws_opendir = dlsym(RTLD_NEXT, "opendir");
    _ws_dlopen = dlsym(RTLD_NEXT, "dlopen");

    _ws_setpriority = dlsym(RTLD_NEXT, "setpriority");
    //_ws_pipe = dlsym(RTLD_NEXT, "pipe");

    const char* logFileName = getenv("WRAPSIM_LOG");
    if (logFileName != NULL ){
        gWrapSim.logFd = _ws_open(logFileName, O_WRONLY|O_APPEND|O_CREAT, 0664);
    }

    /* log messages now work; say hello */
    wsLog("--- initializing sim wrapper ---\n");

    gWrapSim.simulatorFd = -1;

    pthread_mutex_init(&gWrapSim.startLock, NULL);
    pthread_cond_init(&gWrapSim.startCond, NULL);
    gWrapSim.startReady = 0;

    pthread_mutex_init(&gWrapSim.fakeFdLock, NULL);
    gWrapSim.fakeFdMap = wsAllocBitVector(kMaxFakeFdCount, 0);
    memset(gWrapSim.fakeFdList, 0, sizeof(gWrapSim.fakeFdList));

    gWrapSim.numDisplays = 0;

    gWrapSim.keyInputDevice = NULL;

    /*
     * Get target for remapped "/system" and "/data".
     *
     * The ANDROID_PRODUCT_OUT env var *must* be set for rewriting to work.
     */
    const char* outEnv = getenv("ANDROID_PRODUCT_OUT");
    if (outEnv == NULL) {
        gWrapSim.remapBaseDir = NULL;
        wsLog("--- $ANDROID_PRODUCT_OUT not set, "
                "filename remapping disabled\n");
    } else {
        /* grab string and append '/' -- note this never gets freed */
        gWrapSim.remapBaseDirLen = strlen(outEnv);
        gWrapSim.remapBaseDir = strdup(outEnv);
        wsLog("--- name remap to %s\n", gWrapSim.remapBaseDir);
    }

    gWrapSim.initialized = 1;
}

/*
 * Creates a directory, or prints a log message if it fails.
 */
static int createTargetDirectory(const char *path, mode_t mode)
{
    int ret;

    ret = mkdir(path, mode);
    if (ret == 0 || errno == EEXIST) {
        return 0;
    }
    wsLog("--- could not create target directory %s: %s\n",
            path, strerror(errno));
    return ret;
}

/*
 * Any setup that would normally be done by init(8).
 * Note that since the syscall redirects have been installed
 * at this point, we are effectively operating within the
 * simulation context.
 */
static void initGeneral(void)
{
    wsLog("--- preparing system\n");

    /* Try to make sure that certain directories exist.
     * If we fail to create them, the errors will show up in the log,
     * but we keep going.
     */
    createTargetDirectory("/data", 0777);
    createTargetDirectory("/data/dalvik-cache", 0777);
}

/*
 * Initialize all necessary state, and indicate that we're ready to go.
 */
static void initOnce(void)
{
    initGlobals();
    initGeneral();
}

/*
 * Shared object initializer.  glibc guarantees that this function is
 * called before dlopen() returns.  It may be called multiple times.
 */
__attribute__((constructor))
static void initialize(void)
{
    pthread_once(&gWrapSimInitialized, initOnce);
}


