#define _GNU_SOURCE
#include <dlfcn.h>
#define _FCNTL_H
#include <bits/fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <oci.h>

// Replace open with my own version
extern int errorno;

int (*_open)(const char * pathname, int flags, ...);

int open(const char * pathname, int flags, mode_t mode)
{
    _open = (int (*)(const char * pathname, int flags, ...)) dlsym(RTLD_NEXT, "open");
    if(strcmp(pathname, "/proc/self/cmdline") == 0)
        return _open("/home/slavik/tmp/mycmd", flags, mode);
    else
        return _open(pathname, flags, mode);
} 

// Hard code everything
#define PROGRAM_SQL     "select program from v$session where sid = userenv('SID')"
#define USERNAME        "SYSTEM"
#define PASSWORD        "oracle"
#define CONN_STR        "localhost:1521/db11106"

// For simplicity, just declare everything global
OCIEnv          *envhp;
OCIError        *errhp;
OCISession      *authp;
OCIServer       *srvhp;
OCISvcCtx       *svchp;

void err(sword status)
{
    text            errbuf[1024];
    sb4             errcode;

    switch (status)
    {
        case OCI_SUCCESS_WITH_INFO:
        case OCI_ERROR:
            OCIErrorGet ((dvoid *) errhp, (ub4) 1, (text *) NULL, &errcode,
                errbuf, (ub4)sizeof(errbuf), (ub4) OCI_HTYPE_ERROR); 
            fprintf(stderr, "Error while executing OCI command %s\n", errbuf);
            exit(9);
        case OCI_NEED_DATA:
        case OCI_NO_DATA:
        case OCI_INVALID_HANDLE:
        case OCI_STILL_EXECUTING:
        case OCI_CONTINUE:
        case OCI_SUCCESS:
        default:
            break;
    }
}

void selectSingleValue(
    const text * sqlStatement,
    int sqlType,
    dvoid * result,
    int resLen)
{
    OCIStmt *stmthp = 0;
    OCIDefine *defnp = (OCIDefine *) 0;
    sword status;
    
    if ((status = OCIHandleAlloc((dvoid *) envhp,
        (dvoid **) &stmthp,
        OCI_HTYPE_STMT,
        (size_t) 0,
        (dvoid **) 0)) != 0)
    {
        err(status);
    }
    if ((status = OCIStmtPrepare(
        stmthp,
        errhp,
        sqlStatement,
        (ub4) strlen((char *) sqlStatement),
        (ub4) OCI_NTV_SYNTAX,
        (ub4) OCI_DEFAULT)) != 0)
    {
        OCIHandleFree(stmthp, OCI_HTYPE_STMT);
        err(status);
    }
        
    if ((status = OCIDefineByPos(
        stmthp,
        &defnp,
        errhp,
        1,
        result,
        resLen,
        sqlType,
        (dvoid *) 0,
        (ub2 *) 0,
        (ub2 *) 0,
        OCI_DEFAULT)) != 0)
    {
        OCIHandleFree(stmthp, OCI_HTYPE_STMT);
        err(status);
    }
    // execute and fetch 
    if ((status = OCIStmtExecute(
        svchp,
        stmthp,
        errhp,
        (ub4) 1,
        (ub4) 0,
        (CONST OCISnapshot *) NULL,
        (OCISnapshot *) NULL,
        OCI_DEFAULT | OCI_EXACT_FETCH)) != 0)
    {
        OCIHandleFree(stmthp, OCI_HTYPE_STMT);
        err(status);
    }
    OCIHandleFree(stmthp, OCI_HTYPE_STMT);
}

void selectSingleString(
    const text * sqlStatement,
    char* result,
    ub4 len)
{
    selectSingleValue(
        sqlStatement,
        SQLT_STR,
        (dvoid *) result,
        len);
}

void selectSingleInt(
    const text * sqlStatement,
    int* result)
{
    return selectSingleValue(
        sqlStatement,
        SQLT_INT,
        (dvoid *) result,
        sizeof(int));
}

int main(int argc, char* argv[], char* envp[])
{
    sword           status;
    char            program[64];
    int             prglen;

    prglen = strlen(argv[0]);
    strncpy(argv[0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", prglen);
    if (OCIEnvCreate(&envhp,
        (ub4) OCI_DEFAULT,
        (dvoid *) 0,
        (dvoid * (*)(dvoid *, size_t)) 0,
        (dvoid * (*)(dvoid *, dvoid *, size_t)) 0,
        (void (*)(dvoid *, dvoid *)) 0,
        (size_t) 0,
        (dvoid **) 0))
    {
        fprintf(stderr, "Error creating environment\n");
        return 1;
    }    
    
    if (OCIHandleAlloc((dvoid *) envhp,
        (dvoid **) &errhp,
        OCI_HTYPE_ERROR,
        (size_t) 0,
        (dvoid **) 0))
    {
        fprintf(stderr, "Unable to create database error handle\n");
        return 2;
    }
    if (OCIHandleAlloc((dvoid *) envhp,
        (dvoid **) &srvhp,
        OCI_HTYPE_SERVER,
        (size_t) 0,
        (dvoid **) 0))
    {
        fprintf(stderr, "Unable to create database server handle\n");
        return 3;
    }
    if (OCIHandleAlloc((dvoid *) envhp,
        (dvoid **) &svchp,
        OCI_HTYPE_SVCCTX,
        (size_t) 0,
        (dvoid **) 0))
    {
        fprintf(stderr, "Unable to create database service handle\n");
        return 4;
    }
    if (OCIHandleAlloc((dvoid *) envhp,
        (dvoid **) &authp,
        OCI_HTYPE_SESSION,
        (size_t) 0,
        (dvoid **) 0))
    {
        fprintf(stderr, "Unable to create database session handle\n");
        return 5;
    }
    OCIAttrSet((dvoid *)authp,
        (ub4) OCI_HTYPE_SESSION,
        (dvoid *) USERNAME,
        (ub4) strlen(USERNAME),
        OCI_ATTR_USERNAME,
        errhp);
    OCIAttrSet((dvoid *)authp,
        (ub4) OCI_HTYPE_SESSION,
        (dvoid *) PASSWORD, 
        (ub4) strlen(PASSWORD),
        OCI_ATTR_PASSWORD,
        errhp); 
    if ((status = OCIServerAttach(srvhp,
        errhp,
        (text *) CONN_STR,
        (sb4) strlen(CONN_STR),
        (ub4) OCI_DEFAULT)) != 0)
    {
        err(status);
    }
    OCIAttrSet((dvoid *) svchp,
        (ub4) OCI_HTYPE_SVCCTX,
        (dvoid *) srvhp,
        (ub4) 0,
        (ub4) OCI_ATTR_SERVER,
        errhp);

    if ((status = OCISessionBegin(svchp,
        errhp,
        authp,
        OCI_CRED_RDBMS,
        OCI_DEFAULT)) != 0)
    {
        err(status);
    }

    OCIAttrSet((dvoid *) svchp,
        (ub4) OCI_HTYPE_SVCCTX,
        (dvoid *) authp,
        (ub4) 0,
        OCI_ATTR_SESSION,
        errhp);

    selectSingleString(PROGRAM_SQL, program, sizeof(program));
    printf("The program in Oracle is - %s\n", program);

    if ((status = OCISessionEnd(svchp,
        errhp,
        authp,
        OCI_DEFAULT)) != 0)
    {
        err(status);
    }
    if ((status = OCIServerDetach(srvhp,
        errhp,
        OCI_DEFAULT)) != 0)
    {
        err(status);
    }

    OCIHandleFree((dvoid *) envhp, OCI_HTYPE_ENV);
    OCITerminate(OCI_DEFAULT);
    return 0;
}


