631 lines
18 KiB
C++
631 lines
18 KiB
C++
|
/***********************************************************************
|
||
|
*
|
||
|
* 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_c_ipc.h"
|
||
|
}
|
||
|
|
||
|
#include "cchannel.h"
|
||
|
#include "clientreq.h"
|
||
|
|
||
|
|
||
|
//===[ External data ]=====================================================
|
||
|
|
||
|
//===[ External prototypes ]===============================================
|
||
|
|
||
|
//===[ Manifest constants ]================================================
|
||
|
|
||
|
#define MAX_RPC_RETRIES 3
|
||
|
|
||
|
#define MAX_CHANNELS 3
|
||
|
|
||
|
//===[ Type definitions ]==================================================
|
||
|
|
||
|
//
|
||
|
// Class for maintaining SmartCChannel pointers within the daemonVector.
|
||
|
//
|
||
|
class SmartCChannelPointer
|
||
|
{
|
||
|
private:
|
||
|
SmartCChannel *m_pSmartCChannel;
|
||
|
public:
|
||
|
|
||
|
SmartCChannelPointer() : m_pSmartCChannel(NULL) {}
|
||
|
~SmartCChannelPointer() { if (m_pSmartCChannel != NULL) delete m_pSmartCChannel; }
|
||
|
SmartCChannel* getPointer() { return m_pSmartCChannel; }
|
||
|
void setPointer(SmartCChannel *pSmartCChannel) { m_pSmartCChannel = pSmartCChannel; }
|
||
|
};
|
||
|
|
||
|
//===[ Function prototypes ]===============================================
|
||
|
|
||
|
//===[ Global variables ]==================================================
|
||
|
|
||
|
// Debug Level
|
||
|
int DebugLevel = 0;
|
||
|
bool UseSyslog = false;
|
||
|
|
||
|
// Application Name for logging purposes
|
||
|
char unInitialized[] = "Uninitialized";
|
||
|
char *pAppName = unInitialized;
|
||
|
|
||
|
vector<SmartCChannelPointer> cchannelVector;
|
||
|
int numCChannels;
|
||
|
int numChannelSubmits = 0;
|
||
|
|
||
|
// Client mutex
|
||
|
pthread_mutex_t clientMutex;
|
||
|
|
||
|
// Mutex for interlocked operations
|
||
|
pthread_mutex_t interlockedMutex;
|
||
|
|
||
|
// Indications
|
||
|
bool svcInitialized = false;
|
||
|
bool serverAddressSet = false;
|
||
|
|
||
|
// Server address variables
|
||
|
bool use_AF_INET;
|
||
|
bool use_PF_UNIX;
|
||
|
struct sockaddr_in serverInAddr = {0};
|
||
|
struct sockaddr_un serverUnAddr = {0};
|
||
|
|
||
|
|
||
|
//++=======================================================================
|
||
|
void
|
||
|
ReInitializeIpc(void)
|
||
|
//
|
||
|
// Arguments In: None.
|
||
|
//
|
||
|
// Arguments Out: None.
|
||
|
//
|
||
|
// Returns: Nothing.
|
||
|
//
|
||
|
// Abstract: Method to re-initialize the IPC infrastructure for process.
|
||
|
//
|
||
|
// L2
|
||
|
//=======================================================================--
|
||
|
{
|
||
|
CChannel *pCChannel;
|
||
|
|
||
|
DbgTrace(1, "ReInitializeIpc- Start\n", 0);
|
||
|
|
||
|
// Clean up all allocated SmartCChannel objects
|
||
|
for (int i = 0; i < cchannelVector.size(); i++)
|
||
|
{
|
||
|
// Close the channel if present
|
||
|
if (cchannelVector[i].getPointer() != NULL)
|
||
|
{
|
||
|
pCChannel = *(cchannelVector[i].getPointer());
|
||
|
pCChannel->closeChannel();
|
||
|
|
||
|
// Free the SmartCChannel
|
||
|
delete cchannelVector[i].getPointer();
|
||
|
cchannelVector[i].setPointer(NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DbgTrace(1, "ReInitializeIpc- End\n", 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
//++=======================================================================
|
||
|
extern "C"
|
||
|
int
|
||
|
IpcClientInit(
|
||
|
IN char *pName,
|
||
|
IN bool multithreaded,
|
||
|
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.
|
||
|
//
|
||
|
// multithreaded - Set to TRUE if the process is
|
||
|
// multithreaded.
|
||
|
//
|
||
|
// 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 appropriate function to
|
||
|
// set the server address before a request can be submitted.
|
||
|
//
|
||
|
// L0
|
||
|
//=======================================================================--
|
||
|
{
|
||
|
int retStatus = -1;
|
||
|
|
||
|
DbgTrace(1, "IpcClientInit- Start\n", 0);
|
||
|
|
||
|
// Check input parameters
|
||
|
if (pAppName == NULL)
|
||
|
{
|
||
|
DbgTrace(0, "IpcClientInit- Invalid parameter\n", 0);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
// Save a copy of the application name
|
||
|
pAppName = new char[strlen(pName) + 1];
|
||
|
if (pAppName == NULL)
|
||
|
{
|
||
|
DbgTrace(0, "IpcClientInit- Memory allocation failure\n", 0);
|
||
|
goto exit;
|
||
|
}
|
||
|
strcpy(pAppName, pName);
|
||
|
|
||
|
// Save the rest of the debug settings
|
||
|
DebugLevel = debugLevel;
|
||
|
UseSyslog = useSyslog;
|
||
|
|
||
|
// Initialize our mutexes
|
||
|
pthread_mutex_init(&clientMutex, NULL);
|
||
|
pthread_mutex_init(&interlockedMutex, NULL);
|
||
|
|
||
|
// Proceed based on whether or not we have already instantiated
|
||
|
// SmartCChannel vectors.
|
||
|
if (cchannelVector.size() == 0)
|
||
|
{
|
||
|
// SmartCChannel entries have not been instantiated
|
||
|
//
|
||
|
// Setup the number of channels that we may have based on
|
||
|
// whether the application is multi-threaded or not.
|
||
|
if (multithreaded)
|
||
|
numCChannels = MAX_CHANNELS;
|
||
|
else
|
||
|
numCChannels = 1;
|
||
|
|
||
|
// Instantiate entries in SmartCChannel vector
|
||
|
try {
|
||
|
for (int i = 0; i < numCChannels; i++)
|
||
|
cchannelVector.push_back(SmartCChannelPointer());
|
||
|
|
||
|
// Done initializing
|
||
|
svcInitialized = true;
|
||
|
retStatus = 0;
|
||
|
|
||
|
} catch (...) {
|
||
|
DbgTrace(0, "IpcClientInit- Exception caught while initializing the cchannelVector\n", 0);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// SmartCChannel vector has already been instantiated
|
||
|
ReInitializeIpc();
|
||
|
retStatus = 0;
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
|
||
|
DbgTrace(1, "IpcClientInit- End, status = %08X\n", retStatus);
|
||
|
|
||
|
return retStatus;
|
||
|
}
|
||
|
|
||
|
|
||
|
//++=======================================================================
|
||
|
extern "C"
|
||
|
int
|
||
|
IpcClientSetUnAddress(
|
||
|
IN char *pSocketFileName)
|
||
|
//
|
||
|
// Arguments In: pSocketFileName - Pointer to string containing the name
|
||
|
// of the socket file.
|
||
|
//
|
||
|
// 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 should have been initialized before calling
|
||
|
// this procedure.
|
||
|
//
|
||
|
// L0
|
||
|
//=======================================================================--
|
||
|
{
|
||
|
int retStatus = -1;
|
||
|
|
||
|
DbgTrace(1, "IpcClientSetUnAddress- Start\n", 0);
|
||
|
|
||
|
// Verify that we have been initialized
|
||
|
if (svcInitialized)
|
||
|
{
|
||
|
// Verify that the address has not already been set
|
||
|
if (serverAddressSet == false)
|
||
|
{
|
||
|
// Set the necessary information in the serverUnAddr variable
|
||
|
serverUnAddr.sun_family = AF_UNIX;
|
||
|
strcpy(serverUnAddr.sun_path, pSocketFileName);
|
||
|
|
||
|
// Set the necessary flags to indicate that DOMAIN sockets
|
||
|
// should be used for communications.
|
||
|
use_PF_UNIX = true;
|
||
|
use_AF_INET = false;
|
||
|
|
||
|
// Success
|
||
|
serverAddressSet = true;
|
||
|
retStatus = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DbgTrace(0, "IpcClientSetUnAddress- Already set\n", 0);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DbgTrace(0, "IpcClientSetUnAddress- Not initialized\n", 0);
|
||
|
}
|
||
|
|
||
|
DbgTrace(1, "IpcClientSetUnAddress- End, status = %08X\n", retStatus);
|
||
|
|
||
|
return retStatus;
|
||
|
}
|
||
|
|
||
|
|
||
|
//++=======================================================================
|
||
|
extern "C"
|
||
|
int
|
||
|
IpcClientSetInAddress(
|
||
|
IN unsigned short int serverPort,
|
||
|
IN uint32_t serverAddress)
|
||
|
//
|
||
|
// Arguments In: serverPort - Server's listening port number.
|
||
|
//
|
||
|
// serverAddress - The server's IP Address. Use
|
||
|
// 0x7F000001 if the server is local.
|
||
|
//
|
||
|
// 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 should have been initialized before calling
|
||
|
// this procedure.
|
||
|
//
|
||
|
// L0
|
||
|
//=======================================================================--
|
||
|
{
|
||
|
int retStatus = -1;
|
||
|
|
||
|
DbgTrace(1, "IpcClientSetInAddress- Start\n", 0);
|
||
|
|
||
|
// Verify that we have been initialized
|
||
|
if (svcInitialized)
|
||
|
{
|
||
|
// Verify that the address has not already been set
|
||
|
if (serverAddressSet == false)
|
||
|
{
|
||
|
// Set the necessary information in the serverInAddr variable
|
||
|
serverInAddr.sin_family = AF_INET;
|
||
|
serverInAddr.sin_port = htons(serverPort);
|
||
|
serverInAddr.sin_addr.s_addr = htonl(serverAddress);
|
||
|
|
||
|
// Set the necessary flags to indicate that TCP sockets
|
||
|
// should be used for communications.
|
||
|
use_AF_INET = true;
|
||
|
use_PF_UNIX = false;
|
||
|
|
||
|
// Success
|
||
|
serverAddressSet = true;
|
||
|
retStatus = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DbgTrace(0, "IpcClientSetInAddress- Already set\n", 0);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DbgTrace(0, "IpcClientSetInAddress- Not initialized\n", 0);
|
||
|
}
|
||
|
|
||
|
DbgTrace(1, "IpcClientSetInAddress- End, status = %08X\n", retStatus);
|
||
|
|
||
|
return retStatus;
|
||
|
}
|
||
|
|
||
|
|
||
|
//++=======================================================================
|
||
|
extern "C"
|
||
|
void
|
||
|
IpcClientShutdown(void)
|
||
|
//
|
||
|
// Arguments In: None.
|
||
|
//
|
||
|
// Arguments Out: None.
|
||
|
//
|
||
|
// Returns: Nothing.
|
||
|
//
|
||
|
// Abstract: Method to shutdown the IPC infrastructure for process.
|
||
|
//
|
||
|
// L2
|
||
|
//=======================================================================--
|
||
|
{
|
||
|
DbgTrace(1, "IpcClientShutdown- Start\n", 0);
|
||
|
|
||
|
ReInitializeIpc();
|
||
|
|
||
|
// Free the AppName string if necessary
|
||
|
if (pAppName != unInitialized)
|
||
|
{
|
||
|
delete[] pAppName;
|
||
|
pAppName = unInitialized;
|
||
|
}
|
||
|
|
||
|
DbgTrace(1, "IpcClientShutdown- End\n", 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
//++=======================================================================
|
||
|
SmartCChannel *
|
||
|
getCChannel(void)
|
||
|
//
|
||
|
// Arguments In: Nothing.
|
||
|
//
|
||
|
// Arguments Out: Nothing.
|
||
|
//
|
||
|
// Returns: Pointer to SmartCChannel object if successful, otherwise
|
||
|
// NULL.
|
||
|
//
|
||
|
// Abstract: Method to get a SmartCChannel for submitting a request.
|
||
|
//
|
||
|
// L2
|
||
|
//=======================================================================--
|
||
|
{
|
||
|
SmartCChannel *pSmartCChannel = NULL;
|
||
|
int channelSelector = (numChannelSubmits++) % numCChannels;
|
||
|
|
||
|
DbgTrace(1, "IPCCLNT -getCChannel- Start\n", 0);
|
||
|
|
||
|
// Just exit if the server address has not been set
|
||
|
if (!serverAddressSet)
|
||
|
{
|
||
|
DbgTrace(0, "IPCCLNT -getCChannel- Server address not set\n", 0);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
// Obtain client mutex
|
||
|
pthread_mutex_lock(&clientMutex);
|
||
|
|
||
|
// Check if there is an available and usable channel for the client
|
||
|
if (cchannelVector[channelSelector].getPointer() != NULL
|
||
|
&& (*cchannelVector[channelSelector].getPointer())->ok())
|
||
|
{
|
||
|
// Use the available channel
|
||
|
pSmartCChannel = new SmartCChannel(*cchannelVector[channelSelector].getPointer());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// The channel is either unavailable or unusable, clean up
|
||
|
// the channel if it is indeed unusable.
|
||
|
if (cchannelVector[channelSelector].getPointer() != NULL)
|
||
|
{
|
||
|
// Clean up the channel
|
||
|
CChannel *pCChannel = *cchannelVector[channelSelector].getPointer();
|
||
|
pCChannel->closeChannel();
|
||
|
delete cchannelVector[channelSelector].getPointer();
|
||
|
cchannelVector[channelSelector].setPointer(NULL);
|
||
|
}
|
||
|
|
||
|
CChannel *pCChannel;
|
||
|
try {
|
||
|
|
||
|
// Use the appropriate server address when instantiating
|
||
|
// the CChannel object.
|
||
|
if (use_PF_UNIX)
|
||
|
{
|
||
|
// PF_UNIX
|
||
|
pCChannel = new CChannel(&serverUnAddr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Assume AF_INET
|
||
|
pCChannel = new CChannel(&serverInAddr);
|
||
|
}
|
||
|
|
||
|
// CChannel object created, now associate a SmartCChannel
|
||
|
// object with it. It is important to do this to keep
|
||
|
// the object from being deleted as we initialize it.
|
||
|
cchannelVector[channelSelector].setPointer(new SmartCChannel(pCChannel));
|
||
|
|
||
|
// Initialize the CChannel
|
||
|
if (pCChannel->init() == 0)
|
||
|
{
|
||
|
// CChannel initialization succeeded, use it to
|
||
|
// satisfy the caller.
|
||
|
pSmartCChannel = new SmartCChannel(*cchannelVector[channelSelector].getPointer());
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// CChannel initialization failed
|
||
|
delete cchannelVector[channelSelector].getPointer();
|
||
|
cchannelVector[channelSelector].setPointer(NULL);
|
||
|
}
|
||
|
}
|
||
|
catch (...) {
|
||
|
DbgTrace(0, "getCChannel- Exception caught\n", 0);
|
||
|
|
||
|
// Try to clean things up just in case
|
||
|
if (cchannelVector[channelSelector].getPointer())
|
||
|
{
|
||
|
delete cchannelVector[channelSelector].getPointer();
|
||
|
cchannelVector[channelSelector].setPointer(NULL);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pCChannel != NULL)
|
||
|
delete pCChannel;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Release client mutex
|
||
|
pthread_mutex_unlock(&clientMutex);
|
||
|
|
||
|
exit:
|
||
|
|
||
|
DbgTrace(1, "getCChannel- End, Obj = %08X\n", pSmartCChannel);
|
||
|
|
||
|
return pSmartCChannel;
|
||
|
}
|
||
|
|
||
|
|
||
|
//++=======================================================================
|
||
|
extern "C"
|
||
|
int
|
||
|
IpcClientSubmitReq(
|
||
|
IN char *pClientData,
|
||
|
IN int clientDataLen,
|
||
|
INOUT char **ppServerData,
|
||
|
INOUT int *pServerDataLen)
|
||
|
//
|
||
|
// Arguments In: pClientData - Pointer to client data that must be sent to
|
||
|
// the server. Buffer is NEVER released by the
|
||
|
// procedure.
|
||
|
//
|
||
|
// clientDataLen - Length of the client data.
|
||
|
//
|
||
|
// Arguments Out: ppServerData - Pointer to variable that will receive a
|
||
|
// pointer to the buffer containing the data
|
||
|
// received from the server.
|
||
|
//
|
||
|
// pServerDataLen - Pointer to variable that will receive the
|
||
|
// length of the data received from the server.
|
||
|
//
|
||
|
// Returns: 0 == Request completed gracefully
|
||
|
// -1 == Request did not complete gracefully
|
||
|
//
|
||
|
// Abstract: Method to submit a request.
|
||
|
//
|
||
|
// Note: The routine blocks until the request completes.
|
||
|
//
|
||
|
// L2
|
||
|
//=======================================================================--
|
||
|
{
|
||
|
int retStatus = -1;
|
||
|
|
||
|
DbgTrace(1, "IpcClientSubmitReq- Start\n", 0);
|
||
|
|
||
|
try {
|
||
|
SmartCChannel *pSmartCChannel;
|
||
|
|
||
|
// Perform the following in a loop to deal with abnormal connection terminations
|
||
|
unsigned long rpcRetryCount = 0;
|
||
|
while (rpcRetryCount < MAX_RPC_RETRIES)
|
||
|
{
|
||
|
// Get SmartCChannel
|
||
|
pSmartCChannel = getCChannel();
|
||
|
if (pSmartCChannel != NULL)
|
||
|
{
|
||
|
// Get pointer to channel object
|
||
|
CChannel *pCChannel = *pSmartCChannel;
|
||
|
|
||
|
// Allocate a requestId
|
||
|
uint32_t reqId = pCChannel->allocReqId();
|
||
|
|
||
|
// Allocate client request object.
|
||
|
ClientReq clientReq(reqId);
|
||
|
|
||
|
// Submit the request via the channel
|
||
|
if (pCChannel->submitReq(reqId,
|
||
|
clientReq,
|
||
|
pClientData,
|
||
|
clientDataLen) == 0)
|
||
|
{
|
||
|
// Request submission over the channel succeeded, now
|
||
|
// wait for the completion of the request.
|
||
|
clientReq.waitForCompletion(ppServerData,
|
||
|
pServerDataLen);
|
||
|
|
||
|
// Remove the request from the channel
|
||
|
pCChannel->removeReq(reqId);
|
||
|
|
||
|
// Now proceed based on the completion status
|
||
|
ClientReq::CompletionStatus compStatus = clientReq.completionStatus();
|
||
|
if (compStatus == ClientReq::SuccessCompletionStatus)
|
||
|
{
|
||
|
// Success
|
||
|
retStatus = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DbgTrace(0, "IpcClientSubmitReq- Request submittion over the channel failed\n", 0);
|
||
|
|
||
|
// Remove the request from the channel
|
||
|
pCChannel->removeReq(reqId);
|
||
|
}
|
||
|
|
||
|
// Delete the SmartCChannel
|
||
|
delete pSmartCChannel;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DbgTrace(0, "IpcClientSubmitReq- Channel unavailable\n", 0);
|
||
|
}
|
||
|
|
||
|
// Stop trying if the RPC succeeded
|
||
|
if (retStatus == 0)
|
||
|
break;
|
||
|
|
||
|
// Account for this RPC try
|
||
|
rpcRetryCount ++;
|
||
|
DbgTrace(0, "IpcClientSubmitReq- Will attempt to retry RPC, count = %d\n", rpcRetryCount);
|
||
|
}
|
||
|
}
|
||
|
catch(...) {
|
||
|
|
||
|
DbgTrace(0, "IpcClientSubmitReq-- Exception caught\n", 0);
|
||
|
}
|
||
|
|
||
|
DbgTrace(1, "IpcClientSubmitReq- End, retStatus = %08X\n", retStatus);
|
||
|
|
||
|
return retStatus;
|
||
|
}
|
||
|
|
||
|
//=========================================================================
|
||
|
//=========================================================================
|
||
|
|