/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library 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
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *
 ***********************************************************************/

//===[ Include files ]=====================================================

#include "ipcint.h"

extern "C" {
#include "casa_s_ipc.h"
}

#include "schannel.h"
#include "serverthread.h"
#include "serverreq.h"


//===[ Type definitions ]==================================================

#define MAX_FILE_PATH_LEN  1024

//
// Socket Mapping definitions
//
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define LINGER struct linger
#define SOCKADDR_IN struct sockaddr_in
#define closesocket close


//===[ Function prototypes ]===============================================


extern unsigned long numServerReqObjects;
extern unsigned long numSChannelObjects;


//===[ Global variables ]==================================================

// Debug variables
int   DebugLevel = 0;
bool  UseSyslog = false;

// Application Name for logging purposes
char  unInitialized[] = "Uninitialized";
char  *pAppName = unInitialized;

// Indicators
bool  svcInitialized = false;
bool  svcStarted = false;
bool  serverAddressSet = false;

// Listen Port Number
bool                 use_AF_INET;
bool                 use_PF_UNIX;
unsigned short int   listenPortNumber = 0;
char                 listenSocketFile[MAX_FILE_PATH_LEN];

// Listening Socket
int   listenSocket = INVALID_SOCKET;

// Operating parameters
bool  acceptingConnections = true;
bool  terminating = false;

// SChannel list
list<SChannel*>      sChannelList;
pthread_mutex_t      serverMutex;

// Mutex for interlocked operations
pthread_mutex_t      interlockedMutex;

//
// Active Server Request Map - This map contains all of the active ServerReq objects.
//                             The key used to obtain ServerReq object in the map
//                             is the request Id. Note, this id is associated with
//                             a request when ProcessRequest() is called and it does
//                             not have anything to do with the requests rpc id.
//
typedef map<int32_t, ServerReq*> RSMap;
typedef RSMap::iterator RSMapIter;
typedef pair<RSMapIter, bool> RSIterBoolPair;
RSMap                rsMap;
int   numActiveRequests = 0;

//
// Next request id (Can not be zero)
// 
int32_t  nextReqId = 1;

//
// Pending ServerRequests List and count - Server requests are staged on this lists until
//                                         they become active.
// 
list<ServerReq*> pendingServerReqList;
int   numPendingRequests = 0;

// ServerThreads operating parameters
list<ServerThread*> waitingServerThreadList;
int   waitingServerThreads = 0;


//++=======================================================================
int
ServiceRequest(
   ServerReq *pServerReq)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int   retStatus = -1;
   
   DbgTrace(1, "ServiceRequest- Start\n", 0);

   // Obtain server mutex
   pthread_mutex_lock(&serverMutex);

   // Do not process if trying to terminate
   if (!terminating)
   {
       try {

          // Insert the request into the pending server request list
          pendingServerReqList.push_back(pServerReq);
          numPendingRequests ++;

          // Check if there is a thread that can be awaken to process the request
          if (waitingServerThreads)
          {
             // There are waiting ServerThreads, remove one from the list
             // and reduce the waitingServerThreads count.
             list<ServerThread*>::iterator iter = waitingServerThreadList.begin();
             ServerThread *pServerThread = *iter;
             waitingServerThreadList.erase(iter);
             waitingServerThreads --;

             // Wake up the server thread
             pServerThread->awaken();
          }

          // Success
          retStatus = 0;
       }
       catch (...) {
          DbgTrace(0, "ServiceRequest- Exception caught\n", 0);
       }
   }

   // Release server mutex
   pthread_mutex_unlock(&serverMutex);

   DbgTrace(1, "ServiceRequest- End, retStatus = %08X\n", retStatus);

   return retStatus;

}  /*-- ServiceRequest() --*/


//++=======================================================================
void
AbortPendingRequests(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "AbortPendingRequests- Start\n", 0);

   // Obtain server mutex
   pthread_mutex_lock(&serverMutex);

   // Abort and delete all of the ServerReq in the pendingServerReqList
   while (numPendingRequests)
   {
      list<ServerReq*>::iterator iter = pendingServerReqList.begin();
      ServerReq *pServerReq = *iter;
      pendingServerReqList.erase(iter);
      numPendingRequests --;

      pServerReq->abort();
      delete pServerReq;
   }

   // Release server mutex
   pthread_mutex_unlock(&serverMutex);

   DbgTrace(1, "AbortPendingRequests- End\n", 0);

}  /*-- AbortPendingRequests() --*/


//++=======================================================================
void
AwakenSuspendedServerThreads(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "AwakenSuspendedServerThreads- Start\n", 0);

   // Obtain server mutex
   pthread_mutex_lock(&serverMutex);

   // Awaken all of the ServerThreads in the waitingServerThreadsList
   while (waitingServerThreads)
   {
      list<ServerThread*>::iterator iter = waitingServerThreadList.begin();
      ServerThread *pServerThread = *iter;
      waitingServerThreadList.erase(iter);
      waitingServerThreads --;

      // Wake up the server thread
      pServerThread->awaken();
   }

   // Release server mutex
   pthread_mutex_unlock(&serverMutex);

   DbgTrace(1, "AwakenSuspendedServerThreads- End\n", 0);

}  /*-- AwakenSuspendedServerThreads() --*/


//++=======================================================================
void
RemoveFromSChannelList(
   SChannel *pSChannel)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   list<SChannel*>::iterator  iter;
   bool                       objectRemoved = false;
   
   DbgTrace(1, "RemoveFromSChannelList- Start, Obj = %08X\n", pSChannel);

   // Obtain server mutex
   pthread_mutex_lock(&serverMutex);

   // Find the object in the list
   if (!sChannelList.empty())
   {
      iter = sChannelList.begin();
      while (iter != sChannelList.end())
      {
         if (*iter == pSChannel)
         {
            // Object found, remove it from the list.
            sChannelList.erase(iter);

            // Remember that we removed the object
            objectRemoved = true;
            break;
         }

         // Proceed to the next item
         iter ++;
      }
   }

   // Release server mutex
   pthread_mutex_unlock(&serverMutex);

   // Check if the object was removed
   if (objectRemoved == false)
   {
      DbgTrace(0, "RemoveFromSChannelList- Error, did not find object in list\n", 0);
   }

   DbgTrace(1, "RemoveFromSChannelList- End\n", 0);

}  /*-- RemoveFromSChannelList() --*/


//++=======================================================================
void
ShutdownSChannels(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   list<SChannel*>::iterator  iter;
   SChannel                   *pSChannel;
   struct timespec            waitTime = {0};

   DbgTrace(1, "ShutdownSChannels- Start\n", 0);

   // Obtain server mutex
   pthread_mutex_lock(&serverMutex);

   // Close all of the SChannels in the SChannelList
   if (!sChannelList.empty())
   {
      iter = sChannelList.begin();
      while (iter != sChannelList.end())
      {
         // Found SChannel in the idle list, close it.
         (*iter)->closeChannel();

         // Move on to the next item in the list
         iter ++;
      }
   }

   // Release server mutex
   pthread_mutex_unlock(&serverMutex);

   // Loop until all SChannels are gone
   while (numSChannelObjects != 0)
   {
      // Wait for sometime
      waitTime.tv_sec = 1;   // One second
      nanosleep(&waitTime, NULL);
   }

   DbgTrace(1, "ShutdownSChannels- End\n", 0);

}  /*-- ShutdownSChannels() --*/


//++=======================================================================
int
OpenSocket(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int   openedSocket;

   // Open a domain socket if a listen port number has not
   // been configured else open a Tcp socket.
   if (use_PF_UNIX)
      openedSocket = socket(PF_UNIX, SOCK_STREAM, 0);
   else
      openedSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

   return openedSocket;

}  /*-- OpenSocket() --*/


//++=======================================================================
int
BindSocket(int socketToBind)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int   retStatus;

   // Bind domain socket if a listen port number has not
   // been configured else open a Tcp socket.
   if (use_PF_UNIX)
   {
      struct sockaddr_un listenAddr;

      // Set the file creation mask to 000
      mode_t prevMask = umask(000);

      // Remove pre-existing socket
      unlink(listenSocketFile);

      // Setup the address that the daemon will use to listen
      // for connections.
      listenAddr.sun_family = AF_UNIX;
      strcpy(listenAddr.sun_path, listenSocketFile);

      // Perform the bind operation
      retStatus = bind(socketToBind,
                       (const sockaddr*) &listenAddr,
                       sizeof(listenAddr.sun_family) + strlen(listenAddr.sun_path));

      // Return the file creation mask to its previous value
      umask(prevMask);
   }
   else
   {
      struct sockaddr_in listenAddr = {0};
      int on = 1;

      // Setup the address that the daemon will use to listen
      // for connections.
      listenAddr.sin_family = AF_INET;
      listenAddr.sin_addr.s_addr = htonl(INADDR_ANY);
      listenAddr.sin_port = htons(listenPortNumber);

      // Set the SO_REUSEADDR option on the socket to avoid
      // problems in case of a re-start.
      setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

      // Perform the bind operation
      retStatus = bind(socketToBind,
                       (const sockaddr*) &listenAddr,
                       sizeof(struct sockaddr_in));
   }

   return retStatus;

}  /*-- BindSocket() --*/


//++=======================================================================
int
AcceptConnection(int acceptPendingSocket)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int         connectionSocket;
   socklen_t   remoteAddrLen;

   // Use the appropriate address structure when Accepting connection
   if (use_PF_UNIX)
   {
      struct sockaddr_un remoteAddr;
      remoteAddrLen = sizeof(remoteAddr);

      // Perform the accept operation
      connectionSocket = accept(acceptPendingSocket,
                                (struct sockaddr*) &remoteAddr,
                                &remoteAddrLen);
   }
   else
   {
      struct sockaddr_in   remoteAddr;
      remoteAddrLen = sizeof(remoteAddr);

      // Perform the accept operation
      connectionSocket = accept(acceptPendingSocket,
                                (struct sockaddr*) &remoteAddr,
                                &remoteAddrLen);
   }

   return connectionSocket;

}  /*-- AcceptConnection() --*/


//++=======================================================================
void
ServiceConnections(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   int                  connSocket;
   SChannel             *pSChannel;
   bool                 sChannelAddedToList = false;

   DbgTrace(1, "ServiceConnections- Start\n", 0);

   // Open listening Tcp socket
   listenSocket = OpenSocket();
   if (listenSocket != INVALID_SOCKET)
   {
      // Bind the socket
      if (!BindSocket(listenSocket))
      {
         // Socket has been bound to our listen address, now set
         // the socket in listen mode.
         if (listen(listenSocket, SOMAXCONN) != SOCKET_ERROR)
         {
            // The socket is now in listen mode, start accepting connections.
            while (acceptingConnections)
            {
               pSChannel = NULL;
               connSocket = AcceptConnection(listenSocket);
               if (connSocket != INVALID_SOCKET)
               {
                  // We received a new connection
                  //
                  // Obtain server mutex
                  pthread_mutex_lock(&serverMutex);
                  
                  // Create a SChannel object to service the connection
                  try {

                     pSChannel = new SChannel(connSocket);

                     // Associate a smart pointer with the channel to make sure
                     // that it does not go away prematurely while we execute
                     // SChannel::init(), also, this will allow the object to
                     // get cleaned up if its initialization fails.
                     SmartSChannel  smartSChannel = pSChannel;

                     // SChannel created, insert it into the SChannel list.
                     sChannelList.push_back(pSChannel);
                     sChannelAddedToList = true;

                     // Initialize the SChannel
                     if (pSChannel->init())
                     {
                        DbgTrace(0, "ServiceConnections- SChannel intialization failed\n", 0);

                        // Remove the SChannel object from the SChannel list
                        sChannelList.pop_back();
                        sChannelAddedToList = false;
                     }
                  }
                  catch (...) {

                     DbgTrace(0, "ServiceConnections- Exception caught\n", 0);

                     // Free necessary resources
                     if (pSChannel)
                     {
                        if (sChannelAddedToList)
                           sChannelList.pop_back();
                        delete pSChannel;
                     }
                     else
                        closesocket(connSocket);
                  }

                  // Release server mutex
                  pthread_mutex_unlock(&serverMutex);
               }
               else
               {
                  // Check if accept failed because we got interrupted
                  if (errno == EINTR)
                  {
                     // We got interrupted during the accept, try again.
                     continue;
                  }
                  else
                  {
                     // This could be because the listen socket got closed.
                     DbgTrace(1, "ServiceConnections- Accept failed, error = %d\n", errno);

                     // Break out of the accept loop if the socket indeed got closed
                     if (listenSocket == INVALID_SOCKET)
                        break;
                  }
               }
            }
         }
         else
         {
            DbgTrace(0, "ServiceConnections- Listen failed, error = %d\n", errno);
         }
      }
      else
      {
         DbgTrace(0, "ServiceConnections- Unable to bind socket, error = %d\n", errno);
      }

      // Close listening socket if necessary
      if (listenSocket != INVALID_SOCKET)
         closesocket(listenSocket);
   }
   else
   {
      DbgTrace(0, "ServiceConnections- Unable to open socket, error = %d\n", errno);
   }

   DbgTrace(1, "ServiceConnections- End\n", 0);

}  /*-- ServiceConnections() --*/


//++=======================================================================
void* ServiceConnectionsThread(void)
//
//  Arguments: 
//
//  Returns:   
//
//  Abstract:  
//
//  Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "ServiceConnectionsThread- Start\n", 0);

   // Set the thread in the detached state so that it is cleaned up when it exits
   pthread_detach(pthread_self());

   // Service connections until no longer necessary
   ServiceConnections();

   // Clean up
   ShutdownSChannels();

   DbgTrace(1, "ServiceConnectionsThread- End\n", 0);
   
   // Exit
   pthread_exit(NULL);

   return 0;   // never-reached!

}  /*-- ServiceConnectionsThread() --*/


//++=======================================================================
extern "C"
int32_t
IpcServerGetRequest(void)
//
// Arguments In:  None.
//
// Arguments Out: None.
//
// Returns:       The id of the pending request.
//                0 == Not able to wait for request.
//
// Abstract:      A server thread invokes this method to be informed when
//                a request is received that needs to be acted upon.
//
// Notes:         The routine blocks until a request becomes available or
//                until the IpcServer is shutdown.
//
//                An application can execute this method from multiple
//                threads to allow requests to be process concurrently.
// 
// L2
//=======================================================================--
{
   int32_t  requestId = 0;

   DbgTrace(1, "IpcServerGetRequest- Start\n", 0);

   // Make sure that the service has been started
   if (!svcStarted)
   {
      DbgTrace(0, "IpcServerGetRequest- Service has not been started\n", 0);
      goto exit;
   }

   try {

      // Instantiate ServerThread object
      ServerThread serverThread;

      // Obtain server mutex
      pthread_mutex_lock(&serverMutex);

      // Try to find a request to process
      while (!terminating)
      {
         // Make sure that this is initialized to zero
         requestId = 0;

         // Check if there is a request for us to process
         if (numPendingRequests)
         {
            // Obtain pending request and place it in the active map
            list<ServerReq*>::iterator iter = pendingServerReqList.begin();
            ServerReq *pServerReq = *iter;
            pendingServerReqList.erase(iter);
            numPendingRequests --;

            // Assign an id to this request and increment
            // the nextReqId.
            //
            // Protect against zero since it is not valid.
            if (nextReqId != 0)
            {
               requestId = nextReqId;
               nextReqId ++;
            }
            else
            {
               requestId = 1;
               nextReqId = 2;
            }

            // Place the request in the active request map
            RSIterBoolPair insertResult = rsMap.insert(make_pair(requestId, pServerReq));
            if (!insertResult.second)
            {
               // Insertion failed
               DbgTrace(0, "IpcServerGetRequest- Unable to insert ServerReq into map\n", 0);

               // Abort and free the request
               pServerReq->abort();
               delete pServerReq;

               // Try again
               continue;
            }
            else
            {
               // Increment the number of active requests
               numActiveRequests ++;

               // Exit to allow the calling thread to process the request
               break;
            }
         }
         else
         {
            // There is not a request for us to process, place us on the waiting
            // server thread list and wait to be awaken.
            waitingServerThreadList.push_back(&serverThread);
            waitingServerThreads ++;
            serverThread.suspend(&serverMutex);
         }
      }

      // Release server mutex
      pthread_mutex_unlock(&serverMutex);
   }
   catch (...) {

      DbgTrace(1, "IpcServerGetRequest- Exception caught\n", 0);
   }

exit:

   DbgTrace(1, "IpcServerGetRequest- End, requestId = %08X\n", requestId);

   return requestId;

}  /*-- IpcServerGetRequest() --*/


//++=======================================================================
extern "C"
int32_t
IpcServerGetRequestData(
   IN int32_t requestId,
   INOUT char **ppReqData)
//
// Arguments In:  requestId - The id of the request being processed.
//
// Arguments Out: ppReqData - Pointer to variable that will receive a
//                            pointer to the buffer containing the request
//                            data the client.
//
// Returns:       The length of the request data returned.
//
// Abstract:      Method to obtain the data associated with a particular
//                request.
//
// Notes:         The returned buffer SHOULD NOT be released by the calling
//                application.
//
//                The returned buffer always contains a NULL after the
//                data indicated. You may be able to leverage this to
//                treat the data as a NULL terminated string in cases
//                where the request consists of ASCII characters.
// L2
//=======================================================================--
{
   int32_t  reqDataLen = 0;

   DbgTrace(1, "IpcServerGetRequestData- Start, requestId = %08X\n", requestId);

   // Make sure that the service has been started
   if (svcStarted)
   {
      // Obtain server mutex
      pthread_mutex_lock(&serverMutex);

      // Find request in the active map
      RSMapIter iter = rsMap.find(requestId);
      if (iter != rsMap.end())
      {
         // Request was found in the map, obtain a reference to it.
         ServerReq *pServerReq = iter->second;

         // Release server mutex
         pthread_mutex_unlock(&serverMutex);

         // Obtain the request data associated with the request
         reqDataLen = pServerReq->getReqData(ppReqData);
      }
      else
      {
         DbgTrace(0, "IpcServerGetRequestData- Request not found in map\n", 0);

         // Release server mutex
         pthread_mutex_unlock(&serverMutex);
      }
   }
   else
   {
      DbgTrace(0, "IpcServerGetRequestData- Service has not been started\n", 0);
   }

   DbgTrace(1, "IpcServerGetRequestData- End, reqDataLen = %08X\n", reqDataLen);

   return reqDataLen;

}  /*-- IpcServerGetRequestData() --*/


//++=======================================================================
extern "C"
void
IpcServerCompleteRequest(
   IN int32_t requestId,
   IN char *pReplyData)
//
// Arguments In:  requestId - The id of the request being completed.
// 
//                pReplyData - Pointer to reply data that must be sent to
//                             the client for this request.
//
// Arguments Out: None.
//
// Returns:       Nothing.
//
// Abstract:      Method to complete a request being processed.
//
// Notes:         The returned buffer will not NOT be released by the method.
//
// L2
//=======================================================================--
{
   DbgTrace(1, "IpcServerCompleteRequest- Start, requestId = %08X\n", requestId);

   // Make sure that the service has been started
   if (svcStarted)
   {
      // Obtain server mutex
      pthread_mutex_lock(&serverMutex);

      // Find request in the active map
      RSMapIter iter = rsMap.find(requestId);
      if (iter != rsMap.end())
      {
         // Request was found in the map, get a reference to it and
         // remove it from the map>
         ServerReq *pServerReq = iter->second;
         rsMap.erase(iter);
         numActiveRequests --;

         // Release server mutex
         pthread_mutex_unlock(&serverMutex);

         // Coomplete the request and delete it.
         pServerReq->complete(pReplyData);
         delete pServerReq;
      }
      else
      {
         DbgTrace(0, "IpcServerCompleteRequest- Request not found in map\n", 0);

         // Release server mutex
         pthread_mutex_unlock(&serverMutex);
      }
   }
   else
   {
      DbgTrace(0, "IpcServerCompleteRequest- Service has not been started\n", 0);
   }

   DbgTrace(1, "IpcServerCompleteRequest- End\n", 0);

}  /*-- IpcServerCompleteRequest() --*/


//++=======================================================================
extern "C"
void
IpcServerAbortRequest(
   IN int32_t requestId)
//
// Arguments In:  requestId - The id of the request being aborted.
// 
// Arguments Out: None.
//
// Returns:       Nothing.
//
// Abstract:      Method to abort a request being processed.
//
// Notes:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "IpcServerAbortRequest- Start, requestId = %08X\n", requestId);

   // Make sure that the service has been started
   if (svcStarted)
   {
      // Obtain server mutex
      pthread_mutex_lock(&serverMutex);

      // Find request in the active map
      RSMapIter iter = rsMap.find(requestId);
      if (iter != rsMap.end())
      {
         // Request was found in the map, get a reference to it and
         // remove it from the map>
         ServerReq *pServerReq = iter->second;
         rsMap.erase(iter);
         numActiveRequests --;

         // Release server mutex
         pthread_mutex_unlock(&serverMutex);

         // Coomplete the request and delete it.
         pServerReq->abort();
         delete pServerReq;
      }
      else
      {
         DbgTrace(0, "IpcServerAbortRequest- Request not found in map\n", 0);

         // Release server mutex
         pthread_mutex_unlock(&serverMutex);
      }
   }
   else
   {
      DbgTrace(0, "IpcServerAbortRequest- Service has not been started\n", 0);
   }

   DbgTrace(1, "IpcServerAbortRequest- End\n", 0);

}  /*-- IpcServerAbortRequest() --*/


//++=======================================================================
extern "C"
int
IpcServerStart(void)
//
// Arguments In:  None.
//
// Arguments Out: None.
//
// Returns:       0 == Success
//                -1 == Failure
//
// Abstract:      Method to enable the reception of server requests.
//
// Note:          The service needs to be initialized and the listen address
//                needs to be set before calling this procedure.
//
// L2
//=======================================================================--
{
   int         retStatus = -1;

   DbgTrace(1, "IpcServerStart- Start\n", 0);

   // Make sure that the listen address has been set
   if (serverAddressSet)
   {
      // Do not do anything if we have already been started
      if (!svcStarted)
      {
         // Create a threat to service connections
         pthread_t   thread;
         int         threadCreateStatus;
         threadCreateStatus = pthread_create(&thread,
                                             NULL,
                                             (void*(*)(void*))ServiceConnectionsThread,
                                             (void*)NULL);
         if (threadCreateStatus != 0)
         {
            DbgTrace(0, "IpcServerStart- Unable to create service connections thread, error = %08X\n", threadCreateStatus);
            retStatus = -1;
         }
         else
         {
            // Success
            svcStarted = true;
            retStatus = 0;
         }
      }
   }
   else
   {
      DbgTrace(0, "IpcServerStart- Either not initialized or the address has not been set\n", 0);
   }

   DbgTrace(1, "IpcServerStart- End, retStatus = %08X\n", retStatus);

   return retStatus;

}  /*-- IpcServerStart() --*/


//++=======================================================================
extern "C"
int
IpcServerSetUnAddress(
   IN char *pSocketFileName)
//
// Arguments In:  pSocketFileName - Pointer to string containing the name
//                                  of the socket file to listen on.
//
// Arguments Out: None.
//
// Returns:       0 == Success
//                -1 == Failure
//
// Abstract:      Method to set the socket file name to utilize for
//                communicating with the server via DOMAIN sockets.
//
// Note:          The service needs to be initialized before calling this procedure.
//
// L2
//=======================================================================--
{
   int   retStatus = -1;

   DbgTrace(1, "IpcServerSetUnAddress- Start\n", 0);

   // Verify the input parameters
   if (pSocketFileName == NULL
       || strlen(pSocketFileName) >= sizeof(listenSocketFile))
   {
      DbgTrace(0, "IpcServerSetUnAddress- Invalid input parameter\n", 0);
      goto exit;
   }

   // Make sure that we have been initialized
   if (svcInitialized)
   {
      // Make sure that the address has not already been set.
      if (serverAddressSet == false)
      {
         // Save a copy of the socket file name
         strcpy(listenSocketFile, pSocketFileName);

         // Remember this
         serverAddressSet = true;
         use_AF_INET = false;
         use_PF_UNIX = true;

         // Success
         retStatus = 0;
      }
      else
      {
         DbgTrace(0, "IpcServerSetUnAddress- Already set\n", 0);
      }
   }
   else
   {
      DbgTrace(0, "IpcServerSetUnAddress- Service not yet initialized\n", 0);
   }

exit:

   DbgTrace(1, "IpcServerSetUnAddress- End, retStatus = %08X\n", retStatus);

   return retStatus;

}  /*-- IpcServerSetUnAddress() --*/


//++=======================================================================
extern "C"
int
IpcServerSetInAddress(
   IN unsigned short int listenPort)
//
// Arguments In:  serverPort - Server's listening port number.
//
// Arguments Out: None.
//
// Returns:       0 == Success
//                -1 == Failure
//
// Abstract:      Method to set the address to utilize for communicating
//                with the server via TCP sockets.
//
// Note:          The service needs to be initialized before calling this procedure.
//
// L2
//=======================================================================--
{
   int   retStatus = -1;

   DbgTrace(1, "IpcServerSetInAddress- Start\n", 0);

   // Verify the input parameters
   if (listenPort == 0)
   {
      DbgTrace(0, "IpcServerSetInAddress- Invalid input parameter\n", 0);
      goto exit;
   }

   // Make sure that we have been initialized
   if (svcInitialized)
   {
      // Make sure that the address has not already been set.
      if (serverAddressSet == false)
      {
         // Save the listen port number
         listenPortNumber = listenPort;

         // Remember this
         serverAddressSet = true;
         use_AF_INET = true;
         use_PF_UNIX = false;

         // Success
         retStatus = 0;
      }
      else
      {
         DbgTrace(0, "IpcServerSetInAddress- Already set\n", 0);
      }
   }
   else
   {
      DbgTrace(0, "IpcServerSetInAddress- Service not yet initialized\n", 0);
   }

exit:

   DbgTrace(1, "IpcServerSetInAddress- End, retStatus = %08X\n", retStatus);

   return retStatus;

}  /*-- IpcServerSetInAddress() --*/


//++=======================================================================
extern "C"
int
IpcServerInit(
   IN char *pName,
   IN int debugLevel,
   IN bool useSyslog)
//
// Arguments In:  pName - Pointer to string containing the name that the
//                        calling application wants associated with the
//                        debug logs emitted by the library.
// 
//                debugLevel - The level that the library should use for
//                             determining what information should be logged
//                             for debugging purposes. 0 being the lowest
//                             level.
// 
//                useSyslog - Set to TRUE to log debug statements using Syslog,
//                            else debugs are log to stderr.
//
// Arguments Out: None.
//
// Returns:       0 == Success
//                -1 == Failure
//
// Abstract:      Method to initialize the IPC infrastructure for process.
// 
// Note:          It is necessary to call the start procedure to start
//                servicing requests.
//
// L2
//=======================================================================--
{
   int   retStatus = -1;

   DbgTrace(1, "IpcServerInit- Start\n", 0);

   // Check input parameters
   if (pAppName == NULL)
   {
      DbgTrace(0, "IpcServerInit- Invalid parameter\n", 0);
      goto exit;
   }

   // Save a copy of the application name
   pAppName = new char[strlen(pName) + 1];
   if (pAppName == NULL)
   {
      DbgTrace(0, "IpcServerInit- Memory allocation failure\n", 0);
      goto exit;
   }
   strcpy(pAppName, pName);

   // Save the rest of the debug settings
   DebugLevel = debugLevel;
   UseSyslog = useSyslog;

   // Set to ignore SIGPIPE signals
   signal(SIGPIPE, SIG_IGN);

   // Initialize our mutexes
   pthread_mutex_init(&serverMutex, NULL);
   pthread_mutex_init(&interlockedMutex, NULL);

   // Success
   svcInitialized = true;
   retStatus = 0;

exit:

   DbgTrace(1, "IpcServerInit- End, retStatus = %08X\n", retStatus);

   return retStatus;

}  /*-- IpcServerInit() --*/


//++=======================================================================
extern "C"
void
IpcServerShutdown(void)
//
// Arguments In:  None.
//
// Arguments Out: None.
//
// Returns:       Nothing.
//
// Abstract:      Method to shutdown the IPC service.
//
// Note:
//
// L2
//=======================================================================--
{
   DbgTrace(1, "IpcServerShutdown- Start\n", 0);

   // Verify that we have been initialized
   if (svcInitialized)
   {
      // We are being asked to terminate
      terminating = true;

      // Abort all pending requests
      AbortPendingRequests();

      // Awaken any suspended server threads
      AwakenSuspendedServerThreads();

      // Wait for all of the active requests to complete
      while (numActiveRequests)
         sleep(1);

      // Close the listen socket if not already closed. This will
      // cause the ServiceConnectionsThread to clean things up and
      // shutdown.
      if (listenSocket != INVALID_SOCKET)
      {
         // Close the socket
         closesocket(listenSocket);
         listenSocket = INVALID_SOCKET;
      }

      // Forget about having being initialized
      svcInitialized = false;
   }

   // Free the AppName string if necessary
   if (pAppName != unInitialized)
   {
      delete[] pAppName;
      pAppName = unInitialized;
   }

   // Forget about some things
   serverAddressSet = false;
   svcInitialized = false;

   DbgTrace(1, "IpcServerShutdown- End\n", 0);

}  /*-- IpcServerShutdown() --*/

//=========================================================================
//=========================================================================