version 1.6, 2006/11/14 18:34:57
|
version 1.6.2.13, 2007/01/12 06:31:04
|
|
|
#include <Pegasus/Common/Thread.h> | #include <Pegasus/Common/Thread.h> |
#include <Pegasus/Common/MessageQueueService.h> | #include <Pegasus/Common/MessageQueueService.h> |
#include <Pegasus/Config/ConfigManager.h> | #include <Pegasus/Config/ConfigManager.h> |
|
#include <Pegasus/Common/Executor.h> |
| |
#if defined (PEGASUS_OS_TYPE_WINDOWS) | #if defined (PEGASUS_OS_TYPE_WINDOWS) |
# include <windows.h> // For CreateProcess() | # include <windows.h> // For CreateProcess() |
|
|
{ | { |
public: | public: |
ProviderAgentContainer( | ProviderAgentContainer( |
|
const SessionKey& sessionKey, |
const String & moduleName, | const String & moduleName, |
const String & userName, | const String & userName, |
Uint16 userContext, | Uint16 userContext, |
|
|
Mutex _agentMutex; | Mutex _agentMutex; |
| |
/** | /** |
|
Session key of the user on whose behalf this provider agent was loaded. |
|
*/ |
|
SessionKey _sessionKey; |
|
|
|
/** |
Name of the provider module served by this Provider Agent. | Name of the provider module served by this Provider Agent. |
*/ | */ |
String _moduleName; | String _moduleName; |
|
|
ProviderManagerRouter::_subscriptionInitComplete member variable. | ProviderManagerRouter::_subscriptionInitComplete member variable. |
*/ | */ |
Boolean _subscriptionInitComplete; | Boolean _subscriptionInitComplete; |
|
|
|
/** |
|
This SessionKey is used for processing upcalls from the out-of-process |
|
provider module received from _pipeFromAgent. This session key was |
|
assigned by Executor::startProviderAgent() as the |
|
providerAgentSessionKey output argument. |
|
*/ |
|
SessionKey _providerAgentSessionKey; |
}; | }; |
| |
Uint32 ProviderAgentContainer::_numProviderProcesses = 0; | Uint32 ProviderAgentContainer::_numProviderProcesses = 0; |
|
|
reinterpret_cast<CIMResponseMessage*>(&_REQUEST_NOT_PROCESSED); | reinterpret_cast<CIMResponseMessage*>(&_REQUEST_NOT_PROCESSED); |
| |
ProviderAgentContainer::ProviderAgentContainer( | ProviderAgentContainer::ProviderAgentContainer( |
|
const SessionKey& sessionKey, |
const String & moduleName, | const String & moduleName, |
const String & userName, | const String & userName, |
Uint16 userContext, | Uint16 userContext, |
|
|
PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback, | PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback, |
PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T providerModuleFailCallback, | PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T providerModuleFailCallback, |
Boolean subscriptionInitComplete) | Boolean subscriptionInitComplete) |
: _moduleName(moduleName), |
: |
|
_sessionKey(sessionKey), |
|
_moduleName(moduleName), |
_userName(userName), | _userName(userName), |
_userContext(userContext), | _userContext(userContext), |
_indicationCallback(indicationCallback), | _indicationCallback(indicationCallback), |
|
|
_isInitialized(false), | _isInitialized(false), |
_subscriptionInitComplete(subscriptionInitComplete) | _subscriptionInitComplete(subscriptionInitComplete) |
{ | { |
|
|
PEG_METHOD_ENTER(TRC_PROVIDERMANAGER, | PEG_METHOD_ENTER(TRC_PROVIDERMANAGER, |
"ProviderAgentContainer::ProviderAgentContainer"); | "ProviderAgentContainer::ProviderAgentContainer"); |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
|
|
| |
void ProviderAgentContainer::_startAgentProcess() | void ProviderAgentContainer::_startAgentProcess() |
{ | { |
PEG_METHOD_ENTER(TRC_PROVIDERMANAGER, |
PEG_METHOD_ENTER( |
"ProviderAgentContainer::_startAgentProcess"); |
TRC_PROVIDERMANAGER, "ProviderAgentContainer::_startAgentProcess"); |
|
|
// |
|
// Serialize the starting of agent processes. If two agent processes are |
|
// started at the same time, they may get copies of each other's pipe |
|
// descriptors. If this happens, the cimserver will not get a pipe read |
|
// error when one of the agent processes exits, because the pipe will |
|
// still be writable by the other process. This locking control needs to |
|
// cover the period from where the pipes are created to where the agent |
|
// ends of the pipes are closed by the cimserver. |
|
// |
|
static Mutex agentStartupMutex; |
|
AutoMutex lock(agentStartupMutex); |
|
|
|
AutoPtr<AnonymousPipe> pipeFromAgent(new AnonymousPipe()); |
|
AutoPtr<AnonymousPipe> pipeToAgent(new AnonymousPipe()); |
|
|
|
// |
|
// Start a cimprovagt process for this provider module |
|
// |
|
|
|
#if defined (PEGASUS_OS_TYPE_WINDOWS) |
|
// |
|
// Set up members of the PROCESS_INFORMATION structure |
|
// |
|
PROCESS_INFORMATION piProcInfo; |
|
ZeroMemory (&piProcInfo, sizeof (PROCESS_INFORMATION)); |
|
|
|
// |
|
// Set up members of the STARTUPINFO structure |
|
// |
|
STARTUPINFO siStartInfo; |
|
ZeroMemory (&siStartInfo, sizeof (STARTUPINFO)); |
|
siStartInfo.cb = sizeof (STARTUPINFO); |
|
|
|
// |
|
// Generate the command line |
|
// |
|
char cmdLine[2048]; |
|
char readHandle[32]; |
|
char writeHandle[32]; |
|
pipeToAgent->exportReadHandle(readHandle); |
|
pipeFromAgent->exportWriteHandle(writeHandle); |
|
|
|
sprintf(cmdLine, "\"%s\" %s %s \"%s\"", |
|
(const char*)ConfigManager::getHomedPath( |
|
PEGASUS_PROVIDER_AGENT_PROC_NAME).getCString(), |
|
readHandle, writeHandle, (const char*)_moduleName.getCString()); |
|
|
|
// |
|
// Create the child process |
|
// |
|
if (!CreateProcess ( |
|
NULL, // |
|
cmdLine, // command line |
|
NULL, // process security attributes |
|
NULL, // primary thread security attributes |
|
TRUE, // handles are inherited |
|
0, // creation flags |
|
NULL, // use parent's environment |
|
NULL, // use parent's current directory |
|
&siStartInfo, // STARTUPINFO |
|
&piProcInfo)) // PROCESS_INFORMATION |
|
{ |
|
Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2, |
|
"CreateProcess() failed. errno = %d.", GetLastError()); |
|
PEG_METHOD_EXIT(); |
|
throw Exception(MessageLoaderParms( |
|
"ProviderManager.OOPProviderManagerRouter.CIMPROVAGT_START_FAILED", |
|
"Failed to start cimprovagt \"$0\".", |
|
_moduleName)); |
|
} |
|
|
|
CloseHandle(piProcInfo.hProcess); |
|
CloseHandle(piProcInfo.hThread); |
|
|
|
#elif defined (PEGASUS_OS_VMS) |
|
|
|
// |
|
// fork and exec the child process |
|
// |
|
int status; |
|
|
|
status = vfork (); |
|
switch (status) |
|
{ |
|
case 0: |
|
try |
|
{ |
|
// |
|
// Execute the cimprovagt program |
|
// |
|
String agentCommandPath = |
|
ConfigManager::getHomedPath(PEGASUS_PROVIDER_AGENT_PROC_NAME); |
|
CString agentCommandPathCString = agentCommandPath.getCString(); |
|
|
|
char readHandle[32]; |
|
char writeHandle[32]; |
|
pipeToAgent->exportReadHandle(readHandle); |
|
pipeFromAgent->exportWriteHandle(writeHandle); |
|
|
|
if ((status = execl(agentCommandPathCString, agentCommandPathCString, |
|
readHandle, writeHandle, |
|
(const char*)_moduleName.getCString(), (char*)0)) == -1); |
|
{ |
|
// If we're still here, there was an error |
|
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
|
"execl() failed. errno = %d.", errno); |
|
_exit(1); |
|
} |
|
} |
|
catch (...) |
|
{ |
|
// There's not much we can do here in no man's land |
|
try |
|
{ |
|
PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
|
"Caught exception before calling execl()."); |
|
} |
|
catch (...) |
|
{ |
|
} |
|
_exit(1); |
|
} |
|
PEG_METHOD_EXIT(); |
|
return; |
|
break; |
|
|
|
case -1: |
|
Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2, |
|
"fork() failed. errno = %d.", errno); |
|
PEG_METHOD_EXIT(); |
|
throw Exception(MessageLoaderParms( |
|
"ProviderManager.OOPProviderManagerRouter.CIMPROVAGT_START_FAILED", |
|
"Failed to start cimprovagt \"$0\".", |
|
_moduleName)); |
|
break; |
|
|
|
default: |
|
// Close our copies of the agent's ends of the pipes |
|
pipeToAgent->closeReadHandle(); |
|
pipeFromAgent->closeWriteHandle(); |
|
|
|
_pipeToAgent.reset(pipeToAgent.release()); |
|
_pipeFromAgent.reset(pipeFromAgent.release()); |
|
| |
PEG_METHOD_EXIT(); |
PEGASUS_UID_T newUid = (PEGASUS_UID_T)-1; |
} |
PEGASUS_GID_T newGid = (PEGASUS_UID_T)-1; |
#elif defined (PEGASUS_OS_OS400) |
|
| |
//Out of process provider support for OS400 goes here when needed. |
# ifndef PEGASUS_DISABLE_PROV_USERCTXT |
| |
#else |
newUid = getuid(); |
|
newGid = getgid(); |
| |
# ifndef PEGASUS_DISABLE_PROV_USERCTXT |
|
// Get and save the effective user name and the uid/gid for the user | // Get and save the effective user name and the uid/gid for the user |
// context of the agent process | // context of the agent process |
| |
String effectiveUserName = System::getEffectiveUserName(); | String effectiveUserName = System::getEffectiveUserName(); |
PEGASUS_UID_T newUid = (PEGASUS_UID_T) -1; |
|
PEGASUS_GID_T newGid = (PEGASUS_GID_T) -1; |
|
if (_userName != effectiveUserName) | if (_userName != effectiveUserName) |
{ | { |
if (!System::lookupUserId(_userName.getCString(), newUid, newGid)) | if (!System::lookupUserId(_userName.getCString(), newUid, newGid)) |
|
|
"Unable to change user context to \"$0\".", _userName)); | "Unable to change user context to \"$0\".", _userName)); |
} | } |
} | } |
# endif |
|
| |
pid_t pid = fork(); |
# endif /* PEGASUS_DISABLE_PROV_USERCTXT */ |
if (pid < 0) |
|
|
// Start the provider agent. |
|
|
|
int pid; |
|
SessionKey providerAgentSessionKey; |
|
AnonymousPipe* readPipe; |
|
AnonymousPipe* writePipe; |
|
|
|
int status = Executor::startProviderAgent( |
|
_sessionKey, |
|
(const char*)_moduleName.getCString(), |
|
newUid, |
|
newGid, |
|
pid, |
|
_providerAgentSessionKey, |
|
readPipe, |
|
writePipe); |
|
|
|
if (status != 0) |
{ | { |
Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2, | Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2, |
"fork() failed. errno = %d.", errno); |
"Executor::createProviderAgent() failed"); |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
throw Exception(MessageLoaderParms( | throw Exception(MessageLoaderParms( |
"ProviderManager.OOPProviderManagerRouter.CIMPROVAGT_START_FAILED", | "ProviderManager.OOPProviderManagerRouter.CIMPROVAGT_START_FAILED", |
"Failed to start cimprovagt \"$0\".", | "Failed to start cimprovagt \"$0\".", |
_moduleName)); | _moduleName)); |
} | } |
else if (pid == 0) |
|
{ |
|
// |
|
// Child side of the fork |
|
// |
|
|
|
try |
|
{ |
|
// Close our copies of the parent's ends of the pipes |
|
pipeToAgent->closeWriteHandle(); |
|
pipeFromAgent->closeReadHandle(); |
|
|
|
// |
|
// Execute the cimprovagt program |
|
// |
|
String agentCommandPath = |
|
ConfigManager::getHomedPath(PEGASUS_PROVIDER_AGENT_PROC_NAME); |
|
CString agentCommandPathCString = agentCommandPath.getCString(); |
|
|
|
char readHandle[32]; |
|
char writeHandle[32]; |
|
pipeToAgent->exportReadHandle(readHandle); |
|
pipeFromAgent->exportWriteHandle(writeHandle); |
|
|
|
# ifndef PEGASUS_DISABLE_PROV_USERCTXT |
|
// Set the user context of the Provider Agent process |
|
if (_userName != effectiveUserName) |
|
{ |
|
if (!System::changeUserContext(newUid, newGid)) |
|
{ |
|
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
|
"System::changeUserContext() failed. userName = %s.", |
|
(const char*)_userName.getCString()); |
|
Logger::put_l(Logger::ERROR_LOG, System::CIMSERVER, |
|
Logger::WARNING, |
|
"ProviderManager.OOPProviderManagerRouter." |
|
"USER_CONTEXT_CHANGE_FAILED", |
|
"Unable to change user context to \"$0\".", _userName); |
|
_exit(1); |
|
} |
|
} |
|
# 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, |
// Set the session key to be used for requests emanating from this read |
readHandle, writeHandle, |
// pipe (i.e., the provider agent). Examples include requests made by the |
(const char*)_moduleName.getCString(), (char*)0); |
// provider with the CIMOMHandle or indications delivered by the provider. |
| |
// If we're still here, there was an error |
|
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
|
"execl() failed. errno = %d.", errno); |
|
_exit(1); |
|
} |
|
catch (...) |
|
{ |
|
// There's not much we can do here in no man's land |
|
try |
|
{ |
|
PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
|
"Caught exception before calling execl()."); |
|
} |
|
catch (...) {} |
|
_exit(1); |
|
} |
|
} |
|
# if defined(PEGASUS_HAS_SIGNALS) | # if defined(PEGASUS_HAS_SIGNALS) |
_pid = pid; | _pid = pid; |
# endif | # endif |
#endif |
|
|
|
// |
|
// CIM Server process |
|
// |
|
| |
// Close our copies of the agent's ends of the pipes |
_pipeFromAgent.reset(readPipe); |
pipeToAgent->closeReadHandle(); |
_pipeToAgent.reset(writePipe); |
pipeFromAgent->closeWriteHandle(); |
|
| |
_pipeToAgent.reset(pipeToAgent.release()); |
|
_pipeFromAgent.reset(pipeFromAgent.release()); |
|
| |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
} | } |
|
|
do | do |
{ | { |
readStatus = _pipeFromAgent->readMessage(message); | readStatus = _pipeFromAgent->readMessage(message); |
|
|
} while (readStatus == AnonymousPipe::STATUS_INTERRUPT); | } while (readStatus == AnonymousPipe::STATUS_INTERRUPT); |
| |
if (readStatus != AnonymousPipe::STATUS_SUCCESS) | if (readStatus != AnonymousPipe::STATUS_SUCCESS) |
|
|
| |
PEGASUS_ASSERT(message == 0); | PEGASUS_ASSERT(message == 0); |
| |
|
// Request messages must bear the session key of the originating pipe. |
|
{ |
|
CIMRequestMessage* m = dynamic_cast<CIMRequestMessage*>(message); |
|
|
|
if (m) |
|
m->sessionKey = _providerAgentSessionKey; |
|
} |
|
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
} | } |
| |
|
|
try | try |
{ | { |
_startAgentProcess(); | _startAgentProcess(); |
|
|
_isInitialized = true; | _isInitialized = true; |
|
|
_sendInitializationData(); | _sendInitializationData(); |
| |
// Start a thread to read and process responses from the Provider Agent | // Start a thread to read and process responses from the Provider Agent |
|
|
} | } |
catch (...) | catch (...) |
{ | { |
|
SessionKey sessionKey = _providerAgentSessionKey; |
|
|
// Closing the connection causes the agent process to exit | // Closing the connection causes the agent process to exit |
_pipeToAgent.reset(); | _pipeToAgent.reset(); |
_pipeFromAgent.reset(); | _pipeFromAgent.reset(); |
|
|
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 |
pid_t status = 0; |
pid_t status = Executor::reapProviderAgent(sessionKey, _pid); |
do |
|
{ |
|
status = waitpid(_pid, 0, 0); |
|
} while ((status == -1) && (errno == EINTR)); |
|
| |
if (status == -1) | if (status == -1) |
{ | { |
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, | Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
"ProviderAgentContainer::_initialize(): " | "ProviderAgentContainer::_initialize(): " |
"waitpid failed; errno = %d.", errno); |
"Executor::reapProviderAgent() failed"); |
} | } |
} | } |
#endif | #endif |
|
|
| |
try | try |
{ | { |
|
SessionKey sessionKey = _providerAgentSessionKey; |
|
|
// 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 | // Harvest the status of the agent process to prevent a zombie |
pid_t status = 0; |
pid_t status = Executor::reapProviderAgent(sessionKey, _pid); |
do |
|
{ |
|
status = waitpid(_pid, 0, 0); |
|
} while ((status == -1) && (errno == EINTR)); |
|
| |
if (status == -1) | if (status == -1) |
{ | { |
Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, | Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2, |
"ProviderAgentContainer::_uninitialize(): " | "ProviderAgentContainer::_uninitialize(): " |
"waitpid failed; errno = %d.", errno); |
"Executor::reapProviderAgent() failed."); |
} | } |
#endif | #endif |
| |
|
|
return; | return; |
} | } |
| |
|
// Request messages must bear the session key of the |
|
// originating pipe. |
|
{ |
|
CIMRequestMessage* m = |
|
dynamic_cast<CIMRequestMessage*>(message); |
|
|
|
if (m) |
|
m->sessionKey = _providerAgentSessionKey; |
|
} |
|
|
|
// It is a CIM_PROCESS_INDICATION_REQUEST_MESSAGE? |
|
|
if (message->getType() == CIM_PROCESS_INDICATION_REQUEST_MESSAGE) | if (message->getType() == CIM_PROCESS_INDICATION_REQUEST_MESSAGE) |
{ | { |
// Forward indications to the indication callback | // Forward indications to the indication callback |
|
|
if (!_providerAgentTable.lookup(key, pa)) | if (!_providerAgentTable.lookup(key, pa)) |
{ | { |
pa = new ProviderAgentContainer( | pa = new ProviderAgentContainer( |
moduleName, userName, userContext, |
request->sessionKey, moduleName, userName, userContext, |
_indicationCallback, _responseChunkCallback, | _indicationCallback, _responseChunkCallback, |
_providerModuleFailCallback, | _providerModuleFailCallback, |
_subscriptionInitComplete); | _subscriptionInitComplete); |