version 1.25, 2006/02/17 19:20:11
|
version 1.25.2.9, 2007/03/09 18:47:03
|
|
|
# include <unistd.h> // For fork(), exec(), and _exit() | # include <unistd.h> // For fork(), exec(), and _exit() |
# include <errno.h> | # include <errno.h> |
# include <sys/types.h> | # include <sys/types.h> |
|
# include <sys/resource.h> |
# if defined(PEGASUS_HAS_SIGNALS) | # if defined(PEGASUS_HAS_SIGNALS) |
# include <sys/wait.h> | # include <sys/wait.h> |
# endif | # endif |
|
|
{ | { |
public: | public: |
OutstandingRequestEntry( | OutstandingRequestEntry( |
String messageId_, |
String originalMessageId_, |
|
CIMRequestMessage* requestMessage_, |
CIMResponseMessage*& responseMessage_, | CIMResponseMessage*& responseMessage_, |
Semaphore* responseReady_) | Semaphore* responseReady_) |
: messageId(messageId_), |
: originalMessageId(originalMessageId_), |
|
requestMessage(requestMessage_), |
responseMessage(responseMessage_), | responseMessage(responseMessage_), |
responseReady(responseReady_) | responseReady(responseReady_) |
{ | { |
} | } |
| |
String messageId; |
/** |
|
A unique value is substituted as the request messageId attribute to |
|
allow responses to be definitively correllated with requests. |
|
The original messageId value is stored here to avoid a race condition |
|
between the processing of a response chunk and the resetting of the |
|
original messageId in the request message. |
|
*/ |
|
String originalMessageId; |
|
CIMRequestMessage* requestMessage; |
CIMResponseMessage*& responseMessage; | CIMResponseMessage*& responseMessage; |
Semaphore* responseReady; | Semaphore* responseReady; |
}; | }; |
|
|
ProviderAgentContainer( | ProviderAgentContainer( |
const String & moduleName, | const String & moduleName, |
const String & userName, | const String & userName, |
PEGASUS_INDICATION_CALLBACK indicationCallback, |
Uint16 userContext, |
|
PEGASUS_INDICATION_CALLBACK_T indicationCallback, |
|
PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback, |
|
PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T providerModuleFailCallback, |
Boolean subscriptionInitComplete); | Boolean subscriptionInitComplete); |
| |
~ProviderAgentContainer(); | ~ProviderAgentContainer(); |
|
|
The connection is closed and outstanding requests are completed | The connection is closed and outstanding requests are completed |
with an error result. | with an error result. |
| |
Note: The caller must lock the _agentMutex. |
|
|
|
@param cleanShutdown Indicates whether the provider agent process | @param cleanShutdown Indicates whether the provider agent process |
exited cleanly. A value of true indicates that responses have been | exited cleanly. A value of true indicates that responses have been |
sent for all requests that have been processed. A value of false | sent for all requests that have been processed. A value of false |
|
|
String _userName; | String _userName; |
| |
/** | /** |
|
User Context setting of the provider module served by this Provider |
|
Agent. |
|
*/ |
|
Uint16 _userContext; |
|
|
|
/** |
Callback function to which all generated indications are sent for | Callback function to which all generated indications are sent for |
processing. | processing. |
*/ | */ |
PEGASUS_INDICATION_CALLBACK _indicationCallback; |
PEGASUS_INDICATION_CALLBACK_T _indicationCallback; |
|
|
|
/** |
|
Callback function to which response chunks are sent for processing. |
|
*/ |
|
PEGASUS_RESPONSE_CHUNK_CALLBACK_T _responseChunkCallback; |
|
|
|
/** |
|
Callback function to be called upon detection of failure of a |
|
provider module. |
|
*/ |
|
PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T _providerModuleFailCallback; |
| |
/** | /** |
Indicates whether the Provider Agent is active. | Indicates whether the Provider Agent is active. |
|
|
ProviderAgentContainer::ProviderAgentContainer( | ProviderAgentContainer::ProviderAgentContainer( |
const String & moduleName, | const String & moduleName, |
const String & userName, | const String & userName, |
PEGASUS_INDICATION_CALLBACK indicationCallback, |
Uint16 userContext, |
|
PEGASUS_INDICATION_CALLBACK_T indicationCallback, |
|
PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback, |
|
PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T providerModuleFailCallback, |
Boolean subscriptionInitComplete) | Boolean subscriptionInitComplete) |
: _moduleName(moduleName), | : _moduleName(moduleName), |
_userName(userName), | _userName(userName), |
|
_userContext(userContext), |
_indicationCallback(indicationCallback), | _indicationCallback(indicationCallback), |
|
_responseChunkCallback(responseChunkCallback), |
|
_providerModuleFailCallback(providerModuleFailCallback), |
_isInitialized(false), | _isInitialized(false), |
_subscriptionInitComplete(subscriptionInitComplete) | _subscriptionInitComplete(subscriptionInitComplete) |
{ | { |
|
|
} | } |
#elif defined (PEGASUS_OS_OS400) | #elif defined (PEGASUS_OS_OS400) |
| |
//Out of provider support for OS400 goes here when needed. |
//Out of process provider support for OS400 goes here when needed. |
| |
#else | #else |
|
|
|
# ifndef PEGASUS_DISABLE_PROV_USERCTXT |
|
// Get and save the effective user name and the uid/gid for the user |
|
// context of the agent process |
|
|
|
String effectiveUserName = System::getEffectiveUserName(); |
|
PEGASUS_UID_T newUid = (PEGASUS_UID_T) -1; |
|
PEGASUS_GID_T newGid = (PEGASUS_GID_T) -1; |
|
if (_userName != effectiveUserName) |
|
{ |
|
if (!System::lookupUserId(_userName.getCString(), newUid, newGid)) |
|
{ |
|
throw PEGASUS_CIM_EXCEPTION_L( |
|
CIM_ERR_FAILED, |
|
MessageLoaderParms( |
|
"ProviderManager.OOPProviderManagerRouter." |
|
"USER_CONTEXT_CHANGE_FAILED", |
|
"Unable to change user context to \"$0\".", _userName)); |
|
} |
|
} |
|
# endif |
|
|
pid_t pid = fork(); | pid_t pid = fork(); |
if (pid < 0) | if (pid < 0) |
{ | { |
|
|
| |
#ifndef PEGASUS_DISABLE_PROV_USERCTXT | #ifndef PEGASUS_DISABLE_PROV_USERCTXT |
// Set the user context of the Provider Agent process | // Set the user context of the Provider Agent process |
if (_userName != System::getEffectiveUserName()) |
if (_userName != effectiveUserName) |
{ | { |
if (!System::changeUserContext(_userName.getCString())) |
if (!System::changeUserContext(newUid, newGid)) |
{ | { |
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, | Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
"System::changeUserContext() failed. userName = %s.", | "System::changeUserContext() failed. userName = %s.", |
|
|
} | } |
#endif | #endif |
| |
|
// Close all file descriptors except stdin/stdout/stderr |
|
// and the pipe handles needed by the Provider Agent process. |
|
|
|
Uint32 readFd = atoi(readHandle); |
|
Uint32 writeFd = atoi(writeHandle); |
|
struct rlimit fileLimit; |
|
|
|
if (getrlimit(RLIMIT_NOFILE, &fileLimit) == 0) |
|
{ |
|
Uint32 maxFd = (Uint32)fileLimit.rlim_cur; |
|
for (Uint32 i = 3; i < maxFd - 1; i++) |
|
{ |
|
if ((i != readFd) && (i != writeFd)) |
|
{ |
|
close(i); |
|
} |
|
} |
|
} |
|
|
execl(agentCommandPathCString, agentCommandPathCString, | execl(agentCommandPathCString, agentCommandPathCString, |
readHandle, writeHandle, | readHandle, writeHandle, |
(const char*)_moduleName.getCString(), (char*)0); | (const char*)_moduleName.getCString(), (char*)0); |
|
|
if (_isInitialized) | if (_isInitialized) |
{ | { |
// Harvest the status of the agent process to prevent a zombie | // Harvest the status of the agent process to prevent a zombie |
Boolean keepWaiting = false; |
pid_t status = 0; |
do | do |
{ | { |
pid_t status = waitpid(_pid, 0, 0); |
status = waitpid(_pid, 0, 0); |
|
} while ((status == -1) && (errno == EINTR)); |
|
|
if (status == -1) | if (status == -1) |
{ | { |
if (errno == EINTR) |
|
{ |
|
keepWaiting = true; |
|
} |
|
else |
|
{ |
|
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, | Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
"ProviderAgentContainer::_initialize(): " | "ProviderAgentContainer::_initialize(): " |
"waitpid failed; errno = %d.", errno); | "waitpid failed; errno = %d.", errno); |
} | } |
} | } |
} while (keepWaiting); |
|
} |
|
#endif | #endif |
| |
_isInitialized = false; | _isInitialized = false; |
|
|
return _isInitialized; | return _isInitialized; |
} | } |
| |
// Note: Caller must lock _agentMutex |
|
void ProviderAgentContainer::_uninitialize(Boolean cleanShutdown) | void ProviderAgentContainer::_uninitialize(Boolean cleanShutdown) |
{ | { |
PEG_METHOD_ENTER(TRC_PROVIDERMANAGER, | PEG_METHOD_ENTER(TRC_PROVIDERMANAGER, |
"ProviderAgentContainer::_uninitialize"); | "ProviderAgentContainer::_uninitialize"); |
| |
if (!_isInitialized) |
#if defined(PEGASUS_HAS_SIGNALS) |
{ |
pid_t pid; |
PEGASUS_ASSERT(0); |
#endif |
PEG_METHOD_EXIT(); |
|
return; |
|
} |
|
| |
try | try |
{ | { |
|
AutoMutex lock(_agentMutex); |
|
|
|
PEGASUS_ASSERT(_isInitialized); |
|
|
// Close the connection with the Provider Agent | // Close the connection with the Provider Agent |
_pipeFromAgent.reset(); | _pipeFromAgent.reset(); |
_pipeToAgent.reset(); | _pipeToAgent.reset(); |
|
|
} | } |
| |
#if defined(PEGASUS_HAS_SIGNALS) | #if defined(PEGASUS_HAS_SIGNALS) |
// Harvest the status of the agent process to prevent a zombie |
// Save the _pid so we can use it after we've released the _agentMutex |
Boolean keepWaiting = false; |
pid = _pid; |
do |
|
{ |
|
pid_t status = waitpid(_pid, 0, 0); |
|
if (status == -1) |
|
{ |
|
if (errno == EINTR) |
|
{ |
|
keepWaiting = true; |
|
} |
|
else |
|
{ |
|
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
|
"ProviderAgentContainer::_uninitialize(): " |
|
"waitpid failed; errno = %d.", errno); |
|
} |
|
} |
|
} while (keepWaiting); |
|
#endif | #endif |
| |
_isInitialized = false; | _isInitialized = false; |
|
|
i != 0; i++) | i != 0; i++) |
{ | { |
PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL2, | PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL2, |
String("Completing messageId \"") + i.value()->messageId + |
String("Completing messageId \"") + i.key() + |
"\" with a null response."); | "\" with a null response."); |
i.value()->responseMessage = response; | i.value()->responseMessage = response; |
i.value()->responseReady->signal(); | i.value()->responseReady->signal(); |
} | } |
| |
_outstandingRequestTable.clear(); | _outstandingRequestTable.clear(); |
|
|
|
// |
|
// If not a clean shutdown, call the provider module failure |
|
// callback |
|
// |
|
if (!cleanShutdown) |
|
{ |
|
// |
|
// Call the provider module failure callback to |
|
// communicate the failure to the Provider Manager Service |
|
// Provider Manager Service will inform Indication Service |
|
// |
|
_providerModuleFailCallback (_moduleName, _userName, |
|
_userContext); |
|
} |
} | } |
} | } |
catch (...) | catch (...) |
|
|
"Ignoring _uninitialize() exception."); | "Ignoring _uninitialize() exception."); |
} | } |
| |
|
#if defined(PEGASUS_HAS_SIGNALS) |
|
// Harvest the status of the agent process to prevent a zombie. Do not |
|
// hold the _agentMutex during this operation. |
|
pid_t status = 0; |
|
do |
|
{ |
|
status = waitpid(pid, 0, 0); |
|
} while ((status == -1) && (errno == EINTR)); |
|
|
|
if (status == -1) |
|
{ |
|
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
|
"ProviderAgentContainer::_uninitialize(): " |
|
"waitpid failed; errno = %d.", errno); |
|
} |
|
#endif |
|
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
} | } |
| |
|
|
} | } |
} while (response == _REQUEST_NOT_PROCESSED); | } while (response == _REQUEST_NOT_PROCESSED); |
| |
|
if (request->getType() == CIM_SUBSCRIPTION_INIT_COMPLETE_REQUEST_MESSAGE) |
|
{ |
|
_subscriptionInitComplete = true; |
|
} |
|
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
return response; | return response; |
} | } |
|
|
// | // |
Semaphore waitSemaphore(0); | Semaphore waitSemaphore(0); |
OutstandingRequestEntry outstandingRequestEntry( | OutstandingRequestEntry outstandingRequestEntry( |
uniqueMessageId, response, &waitSemaphore); |
originalMessageId, request, response, &waitSemaphore); |
| |
// | // |
// Lock the Provider Agent Container while initializing the | // Lock the Provider Agent Container while initializing the |
|
|
if ((readStatus == AnonymousPipe::STATUS_ERROR) || | if ((readStatus == AnonymousPipe::STATUS_ERROR) || |
(readStatus == AnonymousPipe::STATUS_CLOSED)) | (readStatus == AnonymousPipe::STATUS_CLOSED)) |
{ | { |
AutoMutex lock(_agentMutex); |
|
_uninitialize(false); | _uninitialize(false); |
return; | return; |
} | } |
|
|
// finished its processing and is ready to exit. | // finished its processing and is ready to exit. |
if (message == 0) | if (message == 0) |
{ | { |
AutoMutex lock(_agentMutex); |
|
_uninitialize(true); | _uninitialize(true); |
return; | return; |
} | } |
|
|
reinterpret_cast<CIMProcessIndicationRequestMessage*>( | reinterpret_cast<CIMProcessIndicationRequestMessage*>( |
message)); | message)); |
} | } |
|
else if (!message->isComplete()) |
|
{ |
|
CIMResponseMessage* response; |
|
response = dynamic_cast<CIMResponseMessage*>(message); |
|
PEGASUS_ASSERT(response != 0); |
|
|
|
// Get the OutstandingRequestEntry for this response chunk |
|
OutstandingRequestEntry* _outstandingRequestEntry = 0; |
|
{ |
|
AutoMutex tableLock(_outstandingRequestTableMutex); |
|
Boolean foundEntry = _outstandingRequestTable.lookup( |
|
response->messageId, _outstandingRequestEntry); |
|
PEGASUS_ASSERT(foundEntry); |
|
} |
|
|
|
// Put the original message ID into the response |
|
response->messageId = |
|
_outstandingRequestEntry->originalMessageId; |
|
|
|
// Call the response chunk callback to process the chunk |
|
_responseChunkCallback( |
|
_outstandingRequestEntry->requestMessage, response); |
|
} |
else | else |
{ | { |
CIMResponseMessage* response; | CIMResponseMessage* response; |
|
|
///////////////////////////////////////////////////////////////////////////// | ///////////////////////////////////////////////////////////////////////////// |
| |
OOPProviderManagerRouter::OOPProviderManagerRouter( | OOPProviderManagerRouter::OOPProviderManagerRouter( |
PEGASUS_INDICATION_CALLBACK indicationCallback) |
PEGASUS_INDICATION_CALLBACK_T indicationCallback, |
|
PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback, |
|
PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T providerModuleFailCallback) |
{ | { |
PEG_METHOD_ENTER(TRC_PROVIDERMANAGER, | PEG_METHOD_ENTER(TRC_PROVIDERMANAGER, |
"OOPProviderManagerRouter::OOPProviderManagerRouter"); | "OOPProviderManagerRouter::OOPProviderManagerRouter"); |
| |
_indicationCallback = indicationCallback; | _indicationCallback = indicationCallback; |
|
_responseChunkCallback = responseChunkCallback; |
|
_providerModuleFailCallback = providerModuleFailCallback; |
_subscriptionInitComplete = false; | _subscriptionInitComplete = false; |
| |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
|
|
} | } |
else | else |
{ | { |
|
// |
|
// Look up the Provider Agent for this module instance and requesting |
|
// user |
|
// |
|
ProviderAgentContainer* pa = _lookupProviderAgent(providerModule, |
|
request); |
|
PEGASUS_ASSERT(pa != 0); |
|
|
|
// |
|
// Forward the request to the provider agent |
|
// |
|
response.reset(pa->processMessage(request)); |
|
} |
|
|
|
response->syncAttributes(request); |
|
|
|
PEG_METHOD_EXIT(); |
|
return response.release(); |
|
} |
|
|
|
ProviderAgentContainer* OOPProviderManagerRouter::_lookupProviderAgent( |
|
const CIMInstance& providerModule, |
|
CIMRequestMessage* request) |
|
{ |
// Retrieve the provider module name | // Retrieve the provider module name |
String moduleName; | String moduleName; |
CIMValue nameValue = providerModule.getProperty( | CIMValue nameValue = providerModule.getProperty( |
|
|
| |
if (userContext == 0) | if (userContext == 0) |
{ | { |
userContext = PG_PROVMODULE_USERCTXT_PRIVILEGED; |
userContext = PEGASUS_DEFAULT_PROV_USERCTXT; |
} | } |
| |
String userName; | String userName; |
|
|
request->operationContext.get(IdentityContainer::NAME); | request->operationContext.get(IdentityContainer::NAME); |
userName = ic.getUserName(); | userName = ic.getUserName(); |
} | } |
catch (Exception& e) |
catch (Exception&) |
{ | { |
// If no IdentityContainer is present, default to the CIM | // If no IdentityContainer is present, default to the CIM |
// Server's user context | // Server's user context |
|
|
} | } |
else if (userContext == PG_PROVMODULE_USERCTXT_DESIGNATED) | else if (userContext == PG_PROVMODULE_USERCTXT_DESIGNATED) |
{ | { |
// Retrieve the provider module name |
// Retrieve the provider module designated user property value |
providerModule.getProperty(providerModule.findProperty( | providerModule.getProperty(providerModule.findProperty( |
PEGASUS_PROPERTYNAME_MODULE_DESIGNATEDUSER)).getValue(). | PEGASUS_PROPERTYNAME_MODULE_DESIGNATEDUSER)).getValue(). |
get(userName); | get(userName); |
|
|
PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL4, | PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL4, |
"User name = " + userName); | "User name = " + userName); |
| |
// Look up the Provider Agent for this module and user |
|
ProviderAgentContainer* pa = _lookupProviderAgent(moduleName, userName); |
|
PEGASUS_ASSERT(pa != 0); |
|
|
|
// |
|
// Forward the request to the provider agent |
|
// |
|
response.reset(pa->processMessage(request)); |
|
} |
|
|
|
response->syncAttributes(request); |
|
|
|
PEG_METHOD_EXIT(); |
|
return response.release(); |
|
} |
|
|
|
ProviderAgentContainer* OOPProviderManagerRouter::_lookupProviderAgent( |
|
const String& moduleName, |
|
const String& userName) |
|
{ |
|
ProviderAgentContainer* pa = 0; | ProviderAgentContainer* pa = 0; |
String key = moduleName + ":" + userName; | String key = moduleName + ":" + userName; |
| |
|
|
if (!_providerAgentTable.lookup(key, pa)) | if (!_providerAgentTable.lookup(key, pa)) |
{ | { |
pa = new ProviderAgentContainer( | pa = new ProviderAgentContainer( |
moduleName, userName, _indicationCallback, |
moduleName, userName, userContext, |
|
_indicationCallback, _responseChunkCallback, |
|
_providerModuleFailCallback, |
_subscriptionInitComplete); | _subscriptionInitComplete); |
_providerAgentTable.insert(key, pa); | _providerAgentTable.insert(key, pa); |
} | } |
|
|
PEG_METHOD_ENTER(TRC_PROVIDERMANAGER, | PEG_METHOD_ENTER(TRC_PROVIDERMANAGER, |
"OOPProviderManagerRouter::unloadIdleProviders"); | "OOPProviderManagerRouter::unloadIdleProviders"); |
| |
|
// Get a list of the ProviderAgentContainers. We need our own array copy |
|
// because we cannot hold the _providerAgentTableMutex while calling |
|
// ProviderAgentContainer::unloadIdleProviders(). |
|
Array<ProviderAgentContainer*> paContainerArray; |
|
{ |
|
AutoMutex tableLock(_providerAgentTableMutex); |
|
for (ProviderAgentTable::Iterator i = _providerAgentTable.start(); |
|
i != 0; i++) |
|
{ |
|
paContainerArray.append(i.value()); |
|
} |
|
} |
|
|
// Iterate through the _providerAgentTable unloading idle providers | // Iterate through the _providerAgentTable unloading idle providers |
AutoMutex lock(_providerAgentTableMutex); |
for (Uint32 j = 0; j < paContainerArray.size(); j++) |
ProviderAgentTable::Iterator i = _providerAgentTable.start(); |
|
for(; i != 0; i++) |
|
{ | { |
i.value()->unloadIdleProviders(); |
paContainerArray[j]->unloadIdleProviders(); |
} | } |
| |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |