/*********************************************************************** * * 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 * ***********************************************************************/ //===[ 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 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; } //========================================================================= //=========================================================================