/*!
  @file           vos905.c
  @author         RaymondR
  @brief          sqlxconnect
  @see            

\if EMIT_LICENCE

    ========== licence begin  GPL
    Copyright (c) 2001-2005 SAP AG

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end



\endif
*/




/*
 * INCLUDE FILES
 */
#include "gos00.h"
#include "heo00.h"
#include "heo46.h"
#include "vsp004.h"
#include "gsp09.h"
#include "gos003.h"
#include "geo007_1.h"
#include "geo007_2.h"
#include "gos906.h"


/*
 *  DEFINES
 */
#define MOD__  "VOS905C : "
#define MF__   MOD__"UNDEFINED"

#define LINGER_TIME            15
#define READY_MESSAGE_LEN       5

/*
 *  MACROS
 */


/*
 *  LOCAL TYPE AND STRUCT DEFINITIONS
 */


/*
 * EXTERNAL VARIABLES
 */


/*
 *  EXPORTED VARIABLES
 */


/*
 * LOCAL VARIABLES
 */

/*
 * LOCAL FUNCTION PROTOTYPES
 */

VOID sql03u_xconnect (PSZ               pszServerNode,
                      PSZ               pszServerDB,
                      PSZ               pszServerDBRoot,
                      PSZ               pszServerPgm,
                      TOSCM_COMMSTRUCTOR_FT fpCommstructor,
                      PULONG            pulMaxCmdDataLen,
                      PVOID             pSQLPacketList[],
                      PULONG            pulClientRef,
                      PULONG            pRC,
                      tsp00_ErrTextc    pErrText );

static TOSCM_COMM_CLASS * sql905c_dllcommstructor (
                      void             *commInfo,
                      tsp00_ErrText       errtext,
                      tsp01_CommErr   *returncode);

static TOSCM_COMM_CLASS * sql905c_shmclient_constructor (
                    void             *commInfo,
                    tsp00_ErrText       errtext,
                    tsp01_CommErr   *returncode);

/*
 * ========================== GLOBAL FUNCTIONS ================================
 */
VOID sqlx2connectc (PROCESS_ID                     pid,
                   PSZ                             servernode,
                   PSZ                             serverdb,
                   PATHNAME                        dbroot,
                   PATHNAME                        serverpgm,
                   INT4                            *reference,
                   INT4                            *sql_packet_size,
                   PVOID                           sql_packet_list[],
                   tsp00_ErrTextc                  pErrText,
                   tsp01_CommErr                  *returncode )
  {
  #undef  MF__
  #define MF__ MOD__"sqlx2connectc"
  ULONG         rc;
  PATHNAME      locDbroot;

  strcpy(locDbroot, dbroot);

  sql03u_xconnect (servernode, serverdb, locDbroot, serverpgm,
                   sql905c_shmclient_constructor, sql_packet_size,
                   sql_packet_list, reference, &rc, pErrText);

  *returncode = (tsp01_CommErr) rc;
  }


/*----------------------------------------*/
  
VOID sqlx2connectp (PROCESS_ID                      pid,
                   SQL_NODEID                      servernode,
                   SQL_DBNAME                      serverdb,
                   PATHNAME                        dbroot,
                   PATHNAME                        serverpgm,
                   INT4                            *reference,
                   INT4                            *sql_packet_size,
                   PVOID                           sql_packet_list[],
                   tsp00_ErrText                   errtext,
                   tsp01_CommErr                  *returncode )
  {
  #undef  MF__
  #define MF__ MOD__"sqlx2connectp"
  ULONG         rc;
  SQL_NODEIDC     szServerNode;
  SQL_DBNAMEC     szServerDB;
  tsp00_ErrTextc  pErrText;

  DBGIN;

  // ---  convert pascal names to null terminated strings
  eo46PtoC ( szServerNode, servernode,  sizeof(SQL_NODEID) );
  eo46PtoC ( szServerDB,   serverdb,    sizeof(SQL_DBNAME) );


  // ---  If no node name is given, split database name into node/dbname parts.
  if ( szServerNode [ 0 ] == '\0' )
    sql17u_split_dbname ( szServerDB, szServerNode );

  DBG3 (( MF__, "pszServerNode '%s'", szServerNode ));
  DBG3 (( MF__, "pszServerDB   '%s'", szServerDB ));

  sql03u_xconnect (servernode, serverdb, dbroot, serverpgm,
                   sql905c_shmclient_constructor, sql_packet_size, 
                   sql_packet_list, reference, &rc, pErrText);
  *returncode = (tsp01_CommErr) rc;
  if ( *returncode != commErrOk_esp01 )
        eo46CtoP ( errtext, pErrText,  sizeof(tsp00_ErrText) );
  }

/*----------------------------------------*/


VOID sqlxconnectc (PROCESS_ID                      pid,
                   char                           *servernode,
                   char                           *serverdb,
                   PATHNAME                        dbroot,
                   INT4                            *reference,
                   INT4                            *sql_packet_size,
                   PVOID                           sql_packet_list[],
                   char                           *errtext,
                   tsp01_CommErr                  *returncode )
  {
  #undef  MF__
  #define MF__ MOD__"sqlxconnectc"
  sqlx2connectc (pid, servernode, serverdb, dbroot, NULL, reference, 
                 sql_packet_size, sql_packet_list, errtext, returncode);
  }

/*----------------------------------------*/

VOID sqlxconnectp (PROCESS_ID                      pid,
                   SQL_NODEID                      servernode,
                   SQL_DBNAME                      serverdb,
                   PATHNAME                        dbroot,
                   PATHNAME                        serverpgm,
                   INT4                            *reference,
                   INT4                            *sql_packet_size,
                   PVOID                           sql_packet_list[],
                   tsp00_ErrText                   errtext,
                   tsp01_CommErr                  *returncode )
  {
  #undef  MF__
  #define MF__ MOD__"sqlxconnectp"
  sqlx2connectp (pid, servernode, serverdb, dbroot, serverpgm, reference, 
                 sql_packet_size, sql_packet_list, errtext, returncode);
  }
  

/*
 * ========================== LOCAL FUNCTIONS ================================
 */

typedef  void serverOpen_ft ( void** vcontrolData,
                              tsp9_cstr dbname,
                              tsp9_cstr dbroot);

typedef  void serverClose_ft (void     *serverData);

typedef int serverCommand_ft (void     *serverData,
                              char     *requestData,
                              int       requestLen,
                              char     *replyData,
                              int      *replyLen,
                              int       replyLenmax);

typedef void serverCancel_ft (void);

typedef struct t_dllcomm_class {
    TOSCM_COMM_VMT      *vmt;
    void                *serverData;
    void                *packetMem;
    char                *replyPtr;
    tsp00_Int4             replyLen;
    serverClose_ft      *serverClose;
    serverCommand_ft    *serverCommand;
    serverCancel_ft     *serverCancel;
    HINSTANCE            dllHandle;
    HANDLE               commThread;
    HANDLE               requestEvent;
    HANDLE               replyEvent;
    BOOL                 threadRequired;
    BOOL                 replyAvailable;
    char                *requestPacket;
    tsp00_Int4             requestLen;
} t_dllcomm_class;

/*----------------------------------------*/

static int
sql905c_localCommThread (
    t_dllcomm_class *self)
{
#undef MF__
#define MF__ MOD__"sql905c_localCommThread"
    int rc;
    int maxReply;
    PCOMM_PACKET_REC packetPtr;

    // __try
    do {
        WaitForSingleObject (self->requestEvent, INFINITE);
        if (self->threadRequired) {
            // calculate reply pointer and size
            self->replyPtr = (char*)self->requestPacket
                    + self->requestLen; // assume data is aligned
            maxReply = csp9_ctrl_packet_size - self->requestLen;
            packetPtr = (PCOMM_PACKET_REC)self->replyPtr;
            // call function in dll
            rc = self->serverCommand (self->serverData, self->requestPacket,
                    self->requestLen, packetPtr->pDataPart,
                    &self->replyLen, maxReply);
            self->replyAvailable = TRUE;
            SetEvent (self->replyEvent);
        }
    } while (self->threadRequired);
    // __except
    if (self->threadRequired) {
        self->commThread = NULL;
    }
    ExitThread (0);
    return 0;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_spawnThread (
    t_dllcomm_class *self)
{
#undef MF__
#define MF__ MOD__"sql905c_spawnThread"
    tsp01_CommErr         result;
    
    self->commThread = CreateThread (NULL, 0,
            (LPTHREAD_START_ROUTINE) sql905c_localCommThread,
            (LPVOID) self, CREATE_SUSPENDED, NULL);
    self->threadRequired = TRUE;
    self->requestEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    self->replyEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (self->commThread != NULL)
        ResumeThread (self->commThread);
    if ((self->commThread != NULL)
            && (self->requestEvent != NULL)
            && (self->replyEvent != NULL))
    {
        result = commErrOk_esp01;
    }
    else
    {
        result = commErrNotOk_esp01;
    }
    return result;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_dllcomm_connect (
    TOSCM_COMM_CLASS *commClass,
    CONNECT_PARAM_REC*pConnParam,
    PSZ               pszServerDB,
    PSZ               pszDBRoot,
    PSZ               pszServerPgm,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_dllcomm_connect"
    t_dllcomm_class *self = (t_dllcomm_class*) commClass;
    BOOLEAN                ok;
    PATHNAME               dllpath;
    serverOpen_ft         *serverOpen;
    LONG                   rc = NO_ERROR;
    PCHAR                  pCommPackets;
    ULONG                  ulCnt;
    tsp01_CommErr         result;

    // find dbroot
    ok = sql97cc_findControlServer (dllpath, pszServerDB, 
                                    pszServerPgm, pszDBRoot, errtext);
    if (!ok)
    {
        return commErrNotOk_esp01;
    }

    // load dll
    self->dllHandle = LoadLibraryEx (dllpath, NULL, 0);
    if (self->dllHandle == 0)
    {
        strcat (errtext, "Loading of DLL failed");
        return commErrNotOk_esp01;
    }
    // load function pointer
    self->serverClose = (serverClose_ft*) GetProcAddress (self->dllHandle, "ctrlservclose");
    self->serverCommand = (serverCommand_ft*) GetProcAddress (self->dllHandle, "ctrlservcommand");
    self->serverCancel = (serverCancel_ft*) GetProcAddress (self->dllHandle, "ctrlservcancel");
    serverOpen = (serverOpen_ft*) GetProcAddress (self->dllHandle, "ctrlservopen");
    if ((self->serverClose == NULL)
        || (self->serverCommand == NULL)
        || (serverOpen == NULL))
    {
        strcat (errtext, "Loading of DLL failed");
        FreeLibrary (self->dllHandle);
        self->dllHandle = NULL;
        return commErrNotOk_esp01;
    }
    // call ctrlservopen
    serverOpen (&self->serverData, pszServerDB, pszDBRoot);
    self->replyPtr = NULL;
    self->replyLen = 0;
    // alloc packets
    pConnParam->ulPacketSize = csp9_ctrl_packet_size;
    pConnParam->ulMinReplySize = 100;
    pConnParam->ulMaxDataLen = pConnParam->ulPacketSize
        - (2 * RTE_HEADER_SIZE);
    rc = ALLOC_MEM( (PPVOID)&pCommPackets,
                    (ULONG)(pConnParam->ulPacketCnt * pConnParam->ulPacketSize));
    if ( rc != NO_ERROR )
    {
        sql46c_build_error_string ( errtext, ERRMSG_ALLOC_MEMORY, rc );
        return commErrNotOk_esp01;
    }
    for ( ulCnt = 0; ulCnt < pConnParam->ulPacketCnt; ulCnt++ )
    {
      pConnParam->pCommPacketList[ulCnt]     = (PCOMM_PACKET_REC)pCommPackets;

      pCommPackets += pConnParam->ulPacketSize;
    }
    self->packetMem = pCommPackets;
    result = sql905c_spawnThread (self);
    return result;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_dllcomm_release (
    TOSCM_COMM_CLASS *commClass)
{
#undef MF__
#define MF__ MOD__"sql905c_dllcomm_release"
    t_dllcomm_class *self = (t_dllcomm_class*) commClass;

    if (self->serverClose != NULL) {
        self->serverClose (self->serverData);
    }
    if (self->dllHandle != NULL) {
        FreeLibrary (self->dllHandle);
    }
    if (self->packetMem != NULL) {
        free (self->packetMem);
    }
    if (self->commThread != NULL) {
        self->threadRequired = FALSE;
        SetEvent (self->requestEvent);
    }
    if (self->requestEvent != NULL) {
        CloseHandle (self->requestEvent);
    }
    if (self->replyEvent != NULL) {
        CloseHandle (self->replyEvent);
    }
    free (self);
    return commErrOk_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_dllcomm_request (
    TOSCM_COMM_CLASS *commClass,
    void             *sql_packet,
    tsp00_Int4          length,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_dllcomm_request"
    t_dllcomm_class *self = (t_dllcomm_class*) commClass;

    if (self->commThread == NULL) {
        strcpy (errtext, "Connection broken");
        return commErrNotOk_esp01;
    }
    self->requestPacket = sql_packet;
    self->requestLen = length;
    self->replyAvailable = FALSE;
    SetEvent (self->requestEvent);
    errtext [0] = '\0';
    return commErrOk_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_dllcomm_receive (
    TOSCM_COMM_CLASS *commClass,
    void            **sql_packet,
    tsp00_Int4         *length,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_dllcomm_receive"
    t_dllcomm_class *self = (t_dllcomm_class*) commClass;

    if (self->commThread == NULL) {
        strcpy (errtext, "Connection broken");
        return commErrNotOk_esp01;
    }
    WaitForSingleObject (self->replyEvent, INFINITE);
    *sql_packet = self->replyPtr;
    *length = self->replyLen;
    errtext [0] = '\0';
    return commErrOk_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_dllcomm_replyavailable (
    TOSCM_COMM_CLASS *commClass,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_dllcomm_replyavailable"
    t_dllcomm_class *self = (t_dllcomm_class*) commClass;
    
    errtext [0] = '\0';
    if (self->replyAvailable)
        return commErrOk_esp01;
    else
        return commErrWouldBlock_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_dllcomm_cancel (
    TOSCM_COMM_CLASS *commClass,
    tsp00_ErrText       errtext)
{
    return commErrNotOk_esp01;
}

/*----------------------------------------*/

static TOSCM_COMM_VMT dllcommVMT = {
    "DLL-Communication (vos905c)",
    sql905c_dllcomm_connect,
    sql905c_dllcomm_release,
    sql905c_dllcomm_request,
    sql905c_dllcomm_receive,
    sql905c_dllcomm_replyavailable,
    sql905c_dllcomm_cancel
};

/*----------------------------------------*/

static TOSCM_COMM_CLASS *
sql905c_dllcommstructor (
    void             *commInfo,
    tsp00_ErrText       errtext,
    tsp01_CommErr   *returncode)
{
#undef MF__
#define MF__ MOD__"sql905c_dllcommstructor"
    t_dllcomm_class *self;
    APIRET                 rc;

    // alloc self
    rc = ALLOC_MEM ((PPVOID)&self, sizeof(t_dllcomm_class) );
    if( rc != NO_ERROR ) {
        sql46c_build_error_string ( errtext, ERRMSG_ALLOC_MEMORY, rc );
        *returncode = commErrNotOk_esp01;
        return NULL;
    }
    memset (self, '\0', sizeof (t_dllcomm_class));
    self->vmt = &dllcommVMT;
    *returncode = commErrOk_esp01;
    return (TOSCM_COMM_CLASS*) self;
}

/*----------------------------------------*/

typedef struct t_shmclient_class {
    TOSCM_COMM_VMT      *vmt;
    HANDLE               shmHandle;
    HANDLE               requestEvent;
    HANDLE               replyEvent;
    tos906c_shmServer   *server;
    char                *packetMem;
    HANDLE               serverProcess;
    DWORD                serverId;
} t_shmclient_class;

/*----------------------------------------*/

__inline static tsp00_Longuint
sql905c_pointerDiff (
   void *p1,
   void *p2)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_connect"
    return (char*) p1 - (char*) p2;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_shmclient_allocmem (
   t_shmclient_class *self,
   CONNECT_PARAM_REC*pConnParam,
   tsp00_ErrText          errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_allocmem"
    ULONG               ulCnt;
    ULONG               shmSize;
    
    // alloc server struct and packets in shared memory
    shmSize = (sizeof (tos906c_shmServer) + SQL_PACKET_ALIGNMENT - 1)
            / SQL_PACKET_ALIGNMENT * SQL_PACKET_ALIGNMENT;
    pConnParam->ulPacketSize = csp9_ctrl_packet_size;
    pConnParam->ulMinReplySize = 100;
    pConnParam->ulMaxDataLen = pConnParam->ulPacketSize
        - (2 * RTE_HEADER_SIZE);
    shmSize += pConnParam->ulPacketCnt * pConnParam->ulPacketSize;
    self->shmHandle = CreateFileMapping ((HANDLE)-1, NULL,
            PAGE_READWRITE, 0, shmSize, NULL);
    if (self->shmHandle == NULL) {
        sql46c_build_error_string ( errtext,
                ERRMSG_COM_CANT_ALLOC_COM_SEG, GetLastError () );
        return commErrNotOk_esp01;
    }
    // fill all structures with this information
    self->server = (tos906c_shmServer*) MapViewOfFile (self->shmHandle,
            FILE_MAP_WRITE, 0, 0, 0);
    self->packetMem = (char*) (self->server + 1); // starts after server struct
    for ( ulCnt = 0; ulCnt < pConnParam->ulPacketCnt; ulCnt++ )
    {
      pConnParam->pCommPacketList[ulCnt] = (PCOMM_PACKET_REC)
              (self->packetMem + (ulCnt * pConnParam->ulPacketSize));
    }
    return commErrOk_esp01;
}

/*------------------------------*/

#if !defined DEBUGGER
#define DEBUGGER ""
#elif defined _WIN32
#undef DEBUGGER
#define DEBUGGER "msdev "
#endif
  
static BOOL sql905c_start_process (
   t_shmclient_class   *self,
   PSZ              szExecutable,
   PSZ              szDBRoot,
   PSZ              szDBName,
   HANDLE           shmHandle,
   HANDLE           writePipe)
{
#undef  MF__
#define MF__ MOD__"sql905c_start_process"
  STARTUPINFO         startupInfo;
  PROCESS_INFORMATION procInfo;
  BOOL                success;
  char                cmdline [500];
  char                dbnameArg [100];

  GetStartupInfo (&startupInfo);
# if defined (_WIN64)
   sprintf (cmdline, "%s%s -M %p -P 0,%p -R \"%s\" ", DEBUGGER,
            szExecutable, shmHandle, writePipe, szDBRoot);
# else
   sprintf (cmdline, "%s%s -M %x -P 0,%x -R \"%s\" ", DEBUGGER,
            szExecutable, shmHandle, writePipe, szDBRoot);
# endif
  if ((szDBName != NULL) && (szDBName [0] != '\0')) 
    {
    sprintf (dbnameArg, "-d %s", szDBName);
    strcat (cmdline, dbnameArg);
    }
  startupInfo.dwFlags = STARTF_USESHOWWINDOW;
  startupInfo.wShowWindow = SW_HIDE;
  success = CreateProcess (0, cmdline, 0, 0, TRUE, 0,
      0, 0, &startupInfo, &procInfo);
  self->serverProcess = procInfo.hProcess;
  self->serverId = procInfo.dwProcessId;
  CloseHandle (procInfo.hThread);
  return success;
}

/*----------------------------------------*/

static HANDLE sql905c_changeHandle (
   HANDLE      *handlerAddr,
   int          access)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_connect"
    HANDLE  currentProcess = GetCurrentProcess ();

    DuplicateHandle (currentProcess, *handlerAddr, currentProcess,
            handlerAddr, access, TRUE, DUPLICATE_CLOSE_SOURCE);
    return *handlerAddr;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_shmclient_spawnServer (
   t_shmclient_class   *self,
   CONNECT_PARAM_REC   *pConnParam,
   const char          *exepath,
   PSZ                  pszServerDB,
   PSZ                  pszDBRoot,
   PSZ                  pszServerPgm,
   tsp00_ErrText          errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_spawnServer"
  HANDLE        parentRead;
  HANDLE        childWrite;
  BOOLEAN       ok;
  char          readbuf [READY_MESSAGE_LEN];
  DWORD         bytesRead;
  PATHNAME      executable;
  SECURITY_ATTRIBUTES secAttr;
  secAttr.nLength = sizeof (secAttr);
  secAttr.lpSecurityDescriptor = NULL;
  secAttr.bInheritHandle = TRUE;
  // look for server
  ok = sql97cc_findControlServer (executable, pszServerDB, 
                                  pszServerPgm, pszDBRoot, errtext);
  if (!ok)
    {
    return commErrNotOk_esp01;
    }
  // create low level pipes
  ok = CreatePipe (&parentRead, &childWrite, &secAttr, 0);
  if (!ok)
    {
    strcpy (errtext, "Error creating pipes");
    return commErrNotOk_esp01;
    }
  // start server
  ok = sql905c_start_process (self, executable, pszDBRoot,
          pConnParam->pszServerDB, self->shmHandle, childWrite);
  CloseHandle (childWrite);
  if (!ok)
    {
    strcpy (errtext, "Error starting ");
    strcat (errtext, pszServerPgm);
    CloseHandle (parentRead);
    return commErrNotOk_esp01;
    }
  // receive ready message from server
  ok = ReadFile (parentRead, readbuf, READY_MESSAGE_LEN, &bytesRead, NULL);
  CloseHandle (parentRead);
  if (!ok || (bytesRead != READY_MESSAGE_LEN))
    {
    strcpy (errtext, pszServerPgm);
    strcat (errtext, " died");
    return commErrNotOk_esp01;
    }
  return commErrOk_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_shmclient_connect (
    TOSCM_COMM_CLASS *commClass,
    CONNECT_PARAM_REC*pConnParam,
    PSZ               pszServerDB,
    PSZ               pszDBRoot,
    PSZ               pszServerPgm,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_connect"
    t_shmclient_class *self = (t_shmclient_class*) commClass;
    LONG                rc = NO_ERROR;
    PATHNAME            exepath;
    tsp01_CommErr      result;

    result = sql905c_shmclient_allocmem (self, pConnParam, errtext);
    if (result != commErrOk_esp01)
        return result;
    self->server->isAlive = FALSE;
    self->requestEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    self->replyEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    self->server->clientProcess = GetCurrentProcess ();
    sql905c_changeHandle (&self->server->clientProcess, PROCESS_QUERY_INFORMATION);
    if ((self->requestEvent == NULL)
            || (self->replyEvent == NULL))
    {
        strcpy (errtext, "Creation of events failed");
        return commErrNotOk_esp01;
    }
    sql905c_changeHandle (&self->requestEvent, EVENT_ALL_ACCESS);
    sql905c_changeHandle (&self->replyEvent, EVENT_ALL_ACCESS);
    self->server->requestEvent = self->requestEvent;
    self->server->replyEvent = self->replyEvent;
    sql905c_changeHandle (&self->shmHandle, FILE_MAP_ALL_ACCESS);
    result = sql905c_shmclient_spawnServer (self, pConnParam, exepath,
            pszServerDB, pszDBRoot, pszServerPgm, errtext);
    return result;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_shmclient_release (
    TOSCM_COMM_CLASS *commClass)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_release"
    t_shmclient_class * self = (t_shmclient_class*) commClass;
    DWORD               waitRC;
    tsp01_CommErr       result = commErrOk_esp01;
    BOOL                terminateOK;       

    waitRC = WaitForSingleObject (self->serverProcess, 5000);

    if (waitRC == WAIT_TIMEOUT) {
        terminateOK = TerminateProcess (self->serverProcess, 100);
        if (terminateOK) {
            waitRC = WaitForSingleObject (self->serverProcess, 2000);
        }
        else {
            result = commErrNotOk_esp01;
        }
    }
    CloseHandle (self->serverProcess);
    if (self->requestEvent != NULL) {
        CloseHandle (self->requestEvent);
    }
    if (self->replyEvent != NULL) {
        CloseHandle (self->replyEvent);
    }
    UnmapViewOfFile (self->server);
    CloseHandle (self->shmHandle);
    FREE_MEM (self);
    return result;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_shmclient_request (
    TOSCM_COMM_CLASS *commClass,
    void             *sql_packet,
    tsp00_Int4          length,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_request"
    t_shmclient_class *self = (t_shmclient_class*) commClass;

    if (!self->server->isAlive) {
        strcpy (errtext, "Connection broken");
        return commErrNotOk_esp01;
    }
    self->server->requestOffset =
            (int)sql905c_pointerDiff (sql_packet, self->packetMem);
    self->server->requestLen = length;
    self->server->replyAvailable = FALSE;
    SetEvent (self->requestEvent);
    if (!self->server->isAlive) {
        strcpy (errtext, "Connection broken");
        return commErrNotOk_esp01;
    } else {
        errtext [0] = '\0';
        return commErrOk_esp01;
    }
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_shmclient_receive (
    TOSCM_COMM_CLASS *commClass,
    void            **sql_packet,
    tsp00_Int4         *length,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_receive"
    t_shmclient_class *self = (t_shmclient_class*) commClass;
    BOOL               eventSignaled;

    if (!self->server->isAlive) {
        strcpy (errtext, "Connection broken");
        return commErrNotOk_esp01;
    }
    sql906c_waitForEvent (self->replyEvent, self->serverProcess,
            &eventSignaled);
    if (!eventSignaled) {
        strcat (errtext, "Connection broken");
        return commErrNotOk_esp01;
    }
    *sql_packet = self->packetMem + self->server->replyOffset;
    *length = self->server->replyLen;
    errtext [0] = '\0';
    return commErrOk_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_shmclient_replyavailable (
    TOSCM_COMM_CLASS *commClass,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_replyavailable"
    t_shmclient_class *self = (t_shmclient_class*) commClass;
    
    errtext [0] = '\0';
    if (self->server->replyAvailable)
        return commErrOk_esp01;
    else if (!self->server->isAlive)
        return commErrOk_esp01;
    else
        return commErrWouldBlock_esp01;
}

/*----------------------------------------*/

static tsp01_CommErr
sql905c_shmclient_cancel (
    TOSCM_COMM_CLASS *commClass,
    tsp00_ErrText       errtext)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_cancel"
    t_shmclient_class *self = (t_shmclient_class*) commClass;

    return sql906c_shmSendCancel (self->serverId, errtext);
}

static TOSCM_COMM_VMT shmclientVMT = {
    "SharedMemory-Communication (vos905c)",
    sql905c_shmclient_connect,
    sql905c_shmclient_release,
    sql905c_shmclient_request,
    sql905c_shmclient_receive,
    sql905c_shmclient_replyavailable,
    sql905c_shmclient_cancel
};

/*----------------------------------------*/

static TOSCM_COMM_CLASS *
sql905c_shmclient_constructor (
    void             *commInfo,
    tsp00_ErrText       errtext,
    tsp01_CommErr   *returncode)
{
#undef MF__
#define MF__ MOD__"sql905c_shmclient_constructor"
    t_shmclient_class  *self;
    APIRET              rc;

    // alloc self
    rc = ALLOC_MEM ((PPVOID)&self, sizeof(t_shmclient_class) );
    if( rc != NO_ERROR ) {
        sql46c_build_error_string ( errtext, ERRMSG_ALLOC_MEMORY, rc );
        *returncode = commErrNotOk_esp01;
        return NULL;
    }
    memset (self, '\0', sizeof (t_shmclient_class));
    self->vmt = &shmclientVMT;
    *returncode = commErrOk_esp01;
    return (TOSCM_COMM_CLASS*) self;
}

/*
 * =============================== END ========================================
 */
