1 karl 1.23 //%2006////////////////////////////////////////////////////////////////////////
|
2 kumpf 1.1 //
|
3 karl 1.5 // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
4 // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
5 // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
|
6 kumpf 1.1 // IBM Corp.; EMC Corporation, The Open Group.
|
7 karl 1.5 // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
8 // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
|
9 karl 1.7 // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
10 // EMC Corporation; VERITAS Software Corporation; The Open Group.
|
11 karl 1.23 // Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.;
12 // EMC Corporation; Symantec Corporation; The Open Group.
|
13 kumpf 1.1 //
14 // Permission is hereby granted, free of charge, to any person obtaining a copy
15 // of this software and associated documentation files (the "Software"), to
16 // deal in the Software without restriction, including without limitation the
17 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
18 // sell copies of the Software, and to permit persons to whom the Software is
19 // furnished to do so, subject to the following conditions:
|
20 karl 1.5 //
|
21 kumpf 1.1 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
22 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
23 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
25 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 //==============================================================================
31 //
32 // Author: Roger Kumpf, Hewlett-Packard Company (roger_kumpf@hp.com)
33 // Jenny Yu, Hewlett-Packard Company (jenny_yu@hp.com)
34 //
|
35 gs.keenan 1.8 // Modified By: Sean Keenan, Hewlett-Packard Company (sean.keenan@hp.com)
|
36 carolann.graves 1.14 // Carol Ann Krug Graves, Hewlett-Packard Company
37 // (carolann_graves@hp.com)
|
38 kumpf 1.1 //
39 //%/////////////////////////////////////////////////////////////////////////////
40
41 #include <Pegasus/Common/Config.h>
42 #include <Pegasus/Common/Constants.h>
43 #include <Pegasus/Common/AutoPtr.h>
44 #include <Pegasus/Common/ArrayInternal.h>
45 #include <Pegasus/Common/CIMMessage.h>
46 #include <Pegasus/Common/CIMMessageSerializer.h>
47 #include <Pegasus/Common/CIMMessageDeserializer.h>
48 #include <Pegasus/Common/OperationContextInternal.h>
49 #include <Pegasus/Common/System.h>
50 #include <Pegasus/Common/AnonymousPipe.h>
51 #include <Pegasus/Common/Tracer.h>
52 #include <Pegasus/Common/Logger.h>
53 #include <Pegasus/Common/Thread.h>
54 #include <Pegasus/Common/MessageQueueService.h>
55 #include <Pegasus/Config/ConfigManager.h>
56
57 #if defined (PEGASUS_OS_TYPE_WINDOWS)
|
58 gs.keenan 1.8 # include <windows.h> // For CreateProcess()
59 #elif defined (PEGASUS_OS_OS400)
60 # include <unistd.cleinc>
61 #elif defined (PEGASUS_OS_VMS)
62 # include <perror.h>
63 # include <climsgdef.h>
64 # include <stdio.h>
65 # include <stdlib.h>
66 # include <string.h>
67 # include <processes.h>
68 # include <unixio.h>
|
69 kumpf 1.1 #else
|
70 gs.keenan 1.8 # include <unistd.h> // For fork(), exec(), and _exit()
71 # include <errno.h>
|
72 kumpf 1.18 # include <sys/types.h>
73 # if defined(PEGASUS_HAS_SIGNALS)
74 # include <sys/wait.h>
75 # endif
|
76 kumpf 1.1 #endif
77
78 #include "OOPProviderManagerRouter.h"
79
80 PEGASUS_USING_STD;
81
82 PEGASUS_NAMESPACE_BEGIN
83
84 /////////////////////////////////////////////////////////////////////////////
85 // OutstandingRequestTable and OutstandingRequestEntry
86 /////////////////////////////////////////////////////////////////////////////
87
88 /**
89 An OutstandingRequestEntry represents a request message sent to a
90 Provider Agent for which no response has been received. The request
91 sender provides the message ID and a location for the response to be
92 returned, and then waits on the semaphore. When a response matching
93 the message ID is received, it is placed into the specified location
94 and the semaphore is signaled.
95 */
96 class OutstandingRequestEntry
97 kumpf 1.1 {
98 public:
99 OutstandingRequestEntry(
|
100 kumpf 1.30 String originalMessageId_,
|
101 kumpf 1.26 CIMRequestMessage* requestMessage_,
|
102 kumpf 1.1 CIMResponseMessage*& responseMessage_,
103 Semaphore* responseReady_)
|
104 kumpf 1.30 : originalMessageId(originalMessageId_),
|
105 kumpf 1.26 requestMessage(requestMessage_),
|
106 kumpf 1.1 responseMessage(responseMessage_),
107 responseReady(responseReady_)
108 {
109 }
110
|
111 kumpf 1.30 /**
112 A unique value is substituted as the request messageId attribute to
113 allow responses to be definitively correllated with requests.
114 The original messageId value is stored here to avoid a race condition
115 between the processing of a response chunk and the resetting of the
116 original messageId in the request message.
117 */
118 String originalMessageId;
|
119 kumpf 1.26 CIMRequestMessage* requestMessage;
|
120 kumpf 1.1 CIMResponseMessage*& responseMessage;
121 Semaphore* responseReady;
122 };
123
124 typedef HashTable<String, OutstandingRequestEntry*, EqualFunc<String>,
125 HashFunc<String> > OutstandingRequestTable;
126
127
128 /////////////////////////////////////////////////////////////////////////////
129 // ProviderAgentContainer
130 /////////////////////////////////////////////////////////////////////////////
131
132 class ProviderAgentContainer
133 {
134 public:
135 ProviderAgentContainer(
136 const String & moduleName,
|
137 kumpf 1.6 const String & userName,
|
138 carolann.graves 1.29 Uint16 userContext,
|
139 kumpf 1.26 PEGASUS_INDICATION_CALLBACK_T indicationCallback,
140 PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback,
|
141 carolann.graves 1.29 PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T providerModuleFailCallback,
|
142 carolann.graves 1.14 Boolean subscriptionInitComplete);
|
143 kumpf 1.1
144 ~ProviderAgentContainer();
145
146 Boolean isInitialized();
147
|
148 kumpf 1.6 String getModuleName() const;
149
|
150 kumpf 1.1 CIMResponseMessage* processMessage(CIMRequestMessage* request);
151 void unloadIdleProviders();
152
153 private:
154 //
155 // Private methods
156 //
157
158 /** Unimplemented */
159 ProviderAgentContainer();
160 /** Unimplemented */
161 ProviderAgentContainer(const ProviderAgentContainer& pa);
162 /** Unimplemented */
163 ProviderAgentContainer& operator=(const ProviderAgentContainer& pa);
164
165 /**
166 Start a Provider Agent process and establish a pipe connection with it.
167 Note: The caller must lock the _agentMutex.
168 */
169 void _startAgentProcess();
170
171 kumpf 1.1 /**
172 Send initialization data to the Provider Agent.
173 Note: The caller must lock the _agentMutex.
174 */
175 void _sendInitializationData();
176
177 /**
178 Initialize the ProviderAgentContainer if it is not already
179 initialized. Initialization includes starting the Provider Agent
180 process, establishing a pipe connection with it, and starting a
181 thread to read response messages from the Provider Agent.
182
183 Note: The caller must lock the _agentMutex.
184 */
185 void _initialize();
186
187 /**
188 Uninitialize the ProviderAgentContainer if it is initialized.
189 The connection is closed and outstanding requests are completed
190 with an error result.
191
192 kumpf 1.1 Note: The caller must lock the _agentMutex.
|
193 kumpf 1.22
194 @param cleanShutdown Indicates whether the provider agent process
195 exited cleanly. A value of true indicates that responses have been
196 sent for all requests that have been processed. A value of false
197 indicates that one or more requests may have been partially processed.
198 */
199 void _uninitialize(Boolean cleanShutdown);
200
201 /**
202 Performs the processMessage work, but does not retry on a transient
203 error.
|
204 kumpf 1.1 */
|
205 kumpf 1.22 CIMResponseMessage* _processMessage(CIMRequestMessage* request);
|
206 kumpf 1.1
207 /**
208 Read and process response messages from the Provider Agent until
209 the connection is closed.
210 */
211 void _processResponses();
212 static PEGASUS_THREAD_RETURN PEGASUS_THREAD_CDECL
213 _responseProcessor(void* arg);
214
215 //
216 // Private data
217 //
218
219 /**
220 The _agentMutex must be locked whenever writing to the Provider
221 Agent connection, accessing the _isInitialized flag, or changing
222 the Provider Agent state.
223 */
224 Mutex _agentMutex;
225
226 /**
227 kumpf 1.1 Name of the provider module served by this Provider Agent.
228 */
229 String _moduleName;
230
231 /**
|
232 kumpf 1.6 The user context in which this Provider Agent operates.
233 */
234 String _userName;
235
236 /**
|
237 carolann.graves 1.29 User Context setting of the provider module served by this Provider
238 Agent.
239 */
240 Uint16 _userContext;
241
242 /**
|
243 kumpf 1.1 Callback function to which all generated indications are sent for
244 processing.
245 */
|
246 kumpf 1.26 PEGASUS_INDICATION_CALLBACK_T _indicationCallback;
247
248 /**
249 Callback function to which response chunks are sent for processing.
250 */
251 PEGASUS_RESPONSE_CHUNK_CALLBACK_T _responseChunkCallback;
|
252 kumpf 1.1
253 /**
|
254 carolann.graves 1.29 Callback function to be called upon detection of failure of a
255 provider module.
256 */
257 PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T _providerModuleFailCallback;
258
259 /**
|
260 kumpf 1.1 Indicates whether the Provider Agent is active.
261 */
262 Boolean _isInitialized;
263
264 /**
265 Pipe connection used to read responses from the Provider Agent.
266 */
267 AutoPtr<AnonymousPipe> _pipeFromAgent;
268 /**
269 Pipe connection used to write requests to the Provider Agent.
270 */
271 AutoPtr<AnonymousPipe> _pipeToAgent;
272
|
273 kumpf 1.18 #if defined(PEGASUS_HAS_SIGNALS)
274 /**
275 Process ID of the active Provider Agent.
276 */
277 pid_t _pid;
278 #endif
279
|
280 kumpf 1.1 /**
281 The _outstandingRequestTable holds an entry for each request that has
282 been sent to this Provider Agent for which no response has been
283 received. Entries are added (by the writing thread) when a request
284 is sent, and are removed (by the reading thread) when the response is
285 received (or when it is determined that no response is forthcoming).
286 */
287 OutstandingRequestTable _outstandingRequestTable;
288 /**
289 The _outstandingRequestTableMutex must be locked whenever reading or
290 updating the _outstandingRequestTable.
291 */
292 Mutex _outstandingRequestTableMutex;
|
293 kumpf 1.2
294 /**
295 Holds the last provider module instance sent to the Provider Agent in
296 a ProviderIdContainer. Since the provider module instance rarely
297 changes, an optimization is used to send it only when it differs from
298 the last provider module instance sent.
299 */
300 CIMInstance _providerModuleCache;
|
301 kumpf 1.6
302 /**
303 The number of Provider Agent processes that are currently initialized
304 (active).
305 */
306 static Uint32 _numProviderProcesses;
307
308 /**
309 The _numProviderProcessesMutex must be locked whenever reading or
310 updating the _numProviderProcesses count.
311 */
312 static Mutex _numProviderProcessesMutex;
313
314 /**
315 The maximum number of Provider Agent processes that may be initialized
316 (active) at one time.
317 */
318 static Uint32 _maxProviderProcesses;
|
319 carolann.graves 1.14
320 /**
|
321 kumpf 1.22 A value indicating that a request message has not been processed.
322 A CIMResponseMessage pointer with this value indicates that the
323 corresponding CIMRequestMessage has not been processed. This is
324 used to indicate that a provider agent exited without starting to
325 process the request, and that the request should be retried.
326 */
327 static CIMResponseMessage* _REQUEST_NOT_PROCESSED;
328
329 /**
|
330 carolann.graves 1.14 Indicates whether the Indication Service has completed initialization.
331
332 For more information, please see the description of the
333 ProviderManagerRouter::_subscriptionInitComplete member variable.
334 */
335 Boolean _subscriptionInitComplete;
|
336 kumpf 1.1 };
337
|
338 kumpf 1.6 Uint32 ProviderAgentContainer::_numProviderProcesses = 0;
339 Mutex ProviderAgentContainer::_numProviderProcessesMutex;
340 Uint32 ProviderAgentContainer::_maxProviderProcesses = PEG_NOT_FOUND;
341
|
342 kumpf 1.22 // Set this to a value that no valid CIMResponseMessage* will have.
343 CIMResponseMessage* ProviderAgentContainer::_REQUEST_NOT_PROCESSED =
344 reinterpret_cast<CIMResponseMessage*>(&_REQUEST_NOT_PROCESSED);
345
|
346 kumpf 1.1 ProviderAgentContainer::ProviderAgentContainer(
347 const String & moduleName,
|
348 kumpf 1.6 const String & userName,
|
349 carolann.graves 1.29 Uint16 userContext,
|
350 kumpf 1.26 PEGASUS_INDICATION_CALLBACK_T indicationCallback,
351 PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback,
|
352 carolann.graves 1.29 PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T providerModuleFailCallback,
|
353 carolann.graves 1.14 Boolean subscriptionInitComplete)
|
354 kumpf 1.1 : _moduleName(moduleName),
|
355 kumpf 1.6 _userName(userName),
|
356 carolann.graves 1.29 _userContext(userContext),
|
357 kumpf 1.1 _indicationCallback(indicationCallback),
|
358 kumpf 1.26 _responseChunkCallback(responseChunkCallback),
|
359 carolann.graves 1.29 _providerModuleFailCallback(providerModuleFailCallback),
|
360 carolann.graves 1.14 _isInitialized(false),
361 _subscriptionInitComplete(subscriptionInitComplete)
|
362 kumpf 1.1 {
363 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
364 "ProviderAgentContainer::ProviderAgentContainer");
365 PEG_METHOD_EXIT();
366 }
367
368 ProviderAgentContainer::~ProviderAgentContainer()
369 {
370 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
371 "ProviderAgentContainer::~ProviderAgentContainer");
372
373 // Ensure the destructor does not throw an exception
374 try
375 {
|
376 kumpf 1.17 if (isInitialized())
377 {
378 // Stop the responseProcessor thread by closing its connection
379 _pipeFromAgent->closeReadHandle();
|
380 kumpf 1.1
|
381 kumpf 1.17 // Wait for the responseProcessor thread to exit
382 while (isInitialized())
383 {
384 pegasus_yield();
385 }
|
386 kumpf 1.1 }
387 }
388 catch (...)
389 {
390 }
391
392 PEG_METHOD_EXIT();
393 }
394
395 void ProviderAgentContainer::_startAgentProcess()
396 {
397 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
398 "ProviderAgentContainer::_startAgentProcess");
399
|
400 kumpf 1.21 //
401 // Serialize the starting of agent processes. If two agent processes are
402 // started at the same time, they may get copies of each other's pipe
403 // descriptors. If this happens, the cimserver will not get a pipe read
404 // error when one of the agent processes exits, because the pipe will
405 // still be writable by the other process. This locking control needs to
406 // cover the period from where the pipes are created to where the agent
407 // ends of the pipes are closed by the cimserver.
408 //
409 static Mutex agentStartupMutex;
410 AutoMutex lock(agentStartupMutex);
411
|
412 kumpf 1.1 AutoPtr<AnonymousPipe> pipeFromAgent(new AnonymousPipe());
413 AutoPtr<AnonymousPipe> pipeToAgent(new AnonymousPipe());
414
415 //
416 // Start a cimprovagt process for this provider module
417 //
418
419 #if defined (PEGASUS_OS_TYPE_WINDOWS)
420 //
421 // Set up members of the PROCESS_INFORMATION structure
422 //
423 PROCESS_INFORMATION piProcInfo;
424 ZeroMemory (&piProcInfo, sizeof (PROCESS_INFORMATION));
425
426 //
427 // Set up members of the STARTUPINFO structure
428 //
429 STARTUPINFO siStartInfo;
430 ZeroMemory (&siStartInfo, sizeof (STARTUPINFO));
431 siStartInfo.cb = sizeof (STARTUPINFO);
432
433 kumpf 1.1 //
434 // Generate the command line
435 //
436 char cmdLine[2048];
437 char readHandle[32];
438 char writeHandle[32];
439 pipeToAgent->exportReadHandle(readHandle);
440 pipeFromAgent->exportWriteHandle(writeHandle);
441
442 sprintf(cmdLine, "\"%s\" %s %s \"%s\"",
443 (const char*)ConfigManager::getHomedPath(
444 PEGASUS_PROVIDER_AGENT_PROC_NAME).getCString(),
445 readHandle, writeHandle, (const char*)_moduleName.getCString());
446
447 //
448 // Create the child process
449 //
450 if (!CreateProcess (
451 NULL, //
452 cmdLine, // command line
453 NULL, // process security attributes
454 kumpf 1.1 NULL, // primary thread security attributes
455 TRUE, // handles are inherited
456 0, // creation flags
457 NULL, // use parent's environment
458 NULL, // use parent's current directory
459 &siStartInfo, // STARTUPINFO
460 &piProcInfo)) // PROCESS_INFORMATION
461 {
462 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
463 "CreateProcess() failed. errno = %d.", GetLastError());
464 PEG_METHOD_EXIT();
465 throw Exception(MessageLoaderParms(
466 "ProviderManager.OOPProviderManagerRouter.CIMPROVAGT_START_FAILED",
467 "Failed to start cimprovagt \"$0\".",
468 _moduleName));
469 }
470
471 CloseHandle(piProcInfo.hProcess);
472 CloseHandle(piProcInfo.hThread);
|
473 gs.keenan 1.8
474 #elif defined (PEGASUS_OS_VMS)
475
|
476 gs.keenan 1.10 //
477 // fork and exec the child process
478 //
479 int status;
|
480 gs.keenan 1.8
|
481 gs.keenan 1.10 status = vfork ();
482 switch (status)
483 {
484 case 0:
485 try
|
486 gs.keenan 1.8 {
|
487 gs.keenan 1.10 //
488 // Execute the cimprovagt program
489 //
490 String agentCommandPath =
491 ConfigManager::getHomedPath(PEGASUS_PROVIDER_AGENT_PROC_NAME);
492 CString agentCommandPathCString = agentCommandPath.getCString();
493
494 char readHandle[32];
495 char writeHandle[32];
496 pipeToAgent->exportReadHandle(readHandle);
497 pipeFromAgent->exportWriteHandle(writeHandle);
498
499 if ((status = execl(agentCommandPathCString, agentCommandPathCString,
500 readHandle, writeHandle,
501 (const char*)_moduleName.getCString(), (char*)0)) == -1);
502 {
503 // If we're still here, there was an error
504 Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
505 "execl() failed. errno = %d.", errno);
506 _exit(1);
507 }
508 gs.keenan 1.10 }
509 catch (...)
510 {
511 // There's not much we can do here in no man's land
512 try
513 {
514 PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
515 "Caught exception before calling execl().");
516 }
517 catch (...)
518 {
519 }
520 _exit(1);
521 }
522 PEG_METHOD_EXIT();
523 return;
524 break;
525
526 case -1:
527 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
528 "fork() failed. errno = %d.", errno);
529 gs.keenan 1.10 PEG_METHOD_EXIT();
530 throw Exception(MessageLoaderParms(
531 "ProviderManager.OOPProviderManagerRouter.CIMPROVAGT_START_FAILED",
532 "Failed to start cimprovagt \"$0\".",
533 _moduleName));
534 break;
535
536 default:
537 // Close our copies of the agent's ends of the pipes
538 pipeToAgent->closeReadHandle();
539 pipeFromAgent->closeWriteHandle();
|
540 gs.keenan 1.8
|
541 gs.keenan 1.10 _pipeToAgent.reset(pipeToAgent.release());
542 _pipeFromAgent.reset(pipeFromAgent.release());
|
543 gs.keenan 1.8
|
544 gs.keenan 1.10 PEG_METHOD_EXIT();
545 }
|
546 chuck 1.16 #elif defined (PEGASUS_OS_OS400)
547
|
548 carolann.graves 1.29 //Out of process provider support for OS400 goes here when needed.
|
549 chuck 1.16
|
550 kumpf 1.1 #else
|
551 kumpf 1.28
552 # ifndef PEGASUS_DISABLE_PROV_USERCTXT
553 // Get and save the effective user name and the uid/gid for the user
554 // context of the agent process
555
556 String effectiveUserName = System::getEffectiveUserName();
557 PEGASUS_UID_T newUid = (PEGASUS_UID_T) -1;
558 PEGASUS_GID_T newGid = (PEGASUS_GID_T) -1;
559 if (_userName != effectiveUserName)
560 {
561 if (!System::lookupUserId(_userName.getCString(), newUid, newGid))
562 {
563 throw PEGASUS_CIM_EXCEPTION_L(
564 CIM_ERR_FAILED,
565 MessageLoaderParms(
566 "ProviderManager.OOPProviderManagerRouter."
567 "USER_CONTEXT_CHANGE_FAILED",
568 "Unable to change user context to \"$0\".", _userName));
569 }
570 }
571 # endif
572 kumpf 1.28
|
573 kumpf 1.1 pid_t pid = fork();
574 if (pid < 0)
575 {
576 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
577 "fork() failed. errno = %d.", errno);
578 PEG_METHOD_EXIT();
579 throw Exception(MessageLoaderParms(
580 "ProviderManager.OOPProviderManagerRouter.CIMPROVAGT_START_FAILED",
581 "Failed to start cimprovagt \"$0\".",
582 _moduleName));
583 }
584 else if (pid == 0)
585 {
586 //
587 // Child side of the fork
588 //
589
590 try
591 {
592 // Close our copies of the parent's ends of the pipes
593 pipeToAgent->closeWriteHandle();
594 kumpf 1.1 pipeFromAgent->closeReadHandle();
595
596 //
597 // Execute the cimprovagt program
598 //
599 String agentCommandPath =
600 ConfigManager::getHomedPath(PEGASUS_PROVIDER_AGENT_PROC_NAME);
601 CString agentCommandPathCString = agentCommandPath.getCString();
602
603 char readHandle[32];
604 char writeHandle[32];
605 pipeToAgent->exportReadHandle(readHandle);
606 pipeFromAgent->exportWriteHandle(writeHandle);
607
|
608 kumpf 1.28 # ifndef PEGASUS_DISABLE_PROV_USERCTXT
|
609 kumpf 1.6 // Set the user context of the Provider Agent process
|
610 kumpf 1.28 if (_userName != effectiveUserName)
|
611 kumpf 1.6 {
|
612 kumpf 1.28 if (!System::changeUserContext(newUid, newGid))
|
613 kumpf 1.6 {
614 Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
615 "System::changeUserContext() failed. userName = %s.",
|
616 kumpf 1.9 (const char*)_userName.getCString());
|
617 kumpf 1.6 Logger::put_l(Logger::ERROR_LOG, System::CIMSERVER,
618 Logger::WARNING,
619 "ProviderManager.OOPProviderManagerRouter."
620 "USER_CONTEXT_CHANGE_FAILED",
621 "Unable to change user context to \"$0\".", _userName);
622 _exit(1);
623 }
624 }
|
625 kumpf 1.28 # endif
|
626 kumpf 1.6
|
627 kumpf 1.1 execl(agentCommandPathCString, agentCommandPathCString,
628 readHandle, writeHandle,
629 (const char*)_moduleName.getCString(), (char*)0);
630
631 // If we're still here, there was an error
632 Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
633 "execl() failed. errno = %d.", errno);
634 _exit(1);
635 }
636 catch (...)
637 {
638 // There's not much we can do here in no man's land
639 try
640 {
641 PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
642 "Caught exception before calling execl().");
643 }
644 catch (...) {}
645 _exit(1);
646 }
647 }
|
648 kumpf 1.18 # if defined(PEGASUS_HAS_SIGNALS)
649 _pid = pid;
650 # endif
|
651 kumpf 1.1 #endif
652
653 //
654 // CIM Server process
655 //
656
657 // Close our copies of the agent's ends of the pipes
658 pipeToAgent->closeReadHandle();
659 pipeFromAgent->closeWriteHandle();
660
661 _pipeToAgent.reset(pipeToAgent.release());
662 _pipeFromAgent.reset(pipeFromAgent.release());
663
|
664 gs.keenan 1.10 PEG_METHOD_EXIT();
|
665 kumpf 1.1 }
666
667 // Note: Caller must lock _agentMutex
668 void ProviderAgentContainer::_sendInitializationData()
669 {
670 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
671 "ProviderAgentContainer::_sendInitializationData");
672
673 //
674 // Gather config properties to pass to the Provider Agent
675 //
676 ConfigManager* configManager = ConfigManager::getInstance();
677 Array<Pair<String, String> > configProperties;
678
679 Array<String> configPropertyNames;
680 configManager->getAllPropertyNames(configPropertyNames, true);
681 for (Uint32 i = 0; i < configPropertyNames.size(); i++)
682 {
683 String configPropertyValue =
684 configManager->getCurrentValue(configPropertyNames[i]);
685 String configPropertyDefaultValue =
686 kumpf 1.1 configManager->getDefaultValue(configPropertyNames[i]);
687 if (configPropertyValue != configPropertyDefaultValue)
688 {
689 configProperties.append(Pair<String, String>(
690 configPropertyNames[i], configPropertyValue));
691 }
692 }
693
694 //
695 // Create a Provider Agent initialization message
696 //
697 AutoPtr<CIMInitializeProviderAgentRequestMessage> request(
698 new CIMInitializeProviderAgentRequestMessage(
699 String("0"), // messageId
700 configManager->getPegasusHome(),
701 configProperties,
702 System::bindVerbose,
|
703 carolann.graves 1.14 _subscriptionInitComplete,
|
704 kumpf 1.1 QueueIdStack()));
705
706 //
707 // Write the initialization message to the pipe
708 //
709 AnonymousPipe::Status writeStatus =
710 _pipeToAgent->writeMessage(request.get());
711
712 if (writeStatus != AnonymousPipe::STATUS_SUCCESS)
713 {
714 PEG_METHOD_EXIT();
715 throw Exception(MessageLoaderParms(
716 "ProviderManager.OOPProviderManagerRouter."
717 "CIMPROVAGT_COMMUNICATION_FAILED",
718 "Failed to communicate with cimprovagt \"$0\".",
719 _moduleName));
720 }
721
|
722 kumpf 1.25 // Wait for a null response from the Provider Agent indicating it has
723 // initialized successfully.
724
725 CIMMessage* message;
726 AnonymousPipe::Status readStatus;
727 do
728 {
729 readStatus = _pipeFromAgent->readMessage(message);
730 } while (readStatus == AnonymousPipe::STATUS_INTERRUPT);
731
732 if (readStatus != AnonymousPipe::STATUS_SUCCESS)
733 {
734 PEG_METHOD_EXIT();
735 throw Exception(MessageLoaderParms(
736 "ProviderManager.OOPProviderManagerRouter."
737 "CIMPROVAGT_COMMUNICATION_FAILED",
738 "Failed to communicate with cimprovagt \"$0\".",
739 _moduleName));
740 }
741
742 PEGASUS_ASSERT(message == 0);
|
743 kumpf 1.1
744 PEG_METHOD_EXIT();
745 }
746
747 // Note: Caller must lock _agentMutex
748 void ProviderAgentContainer::_initialize()
749 {
750 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
751 "ProviderAgentContainer::_initialize");
752
753 if (_isInitialized)
754 {
755 PEGASUS_ASSERT(0);
756 PEG_METHOD_EXIT();
757 return;
758 }
759
|
760 kumpf 1.6 if (_maxProviderProcesses == PEG_NOT_FOUND)
761 {
|
762 kumpf 1.25 String maxProviderProcesses = ConfigManager::getInstance()->
763 getCurrentValue("maxProviderProcesses");
|
764 kumpf 1.6 CString maxProviderProcessesString = maxProviderProcesses.getCString();
765 char* end = 0;
766 _maxProviderProcesses = strtol(maxProviderProcessesString, &end, 10);
767 }
768
769 {
770 AutoMutex lock(_numProviderProcessesMutex);
771 if ((_maxProviderProcesses != 0) &&
772 (_numProviderProcesses >= _maxProviderProcesses))
773 {
774 throw PEGASUS_CIM_EXCEPTION(
775 CIM_ERR_FAILED,
776 MessageLoaderParms(
777 "ProviderManager.OOPProviderManagerRouter."
778 "MAX_PROVIDER_PROCESSES_REACHED",
779 "The maximum number of cimprovagt processes has been "
780 "reached."));
781 }
782 else
783 {
784 _numProviderProcesses++;
785 kumpf 1.6 }
786 }
787
|
788 kumpf 1.1 try
789 {
790 _startAgentProcess();
791
|
792 kumpf 1.18 _isInitialized = true;
793
|
794 kumpf 1.1 _sendInitializationData();
795
796 // Start a thread to read and process responses from the Provider Agent
|
797 kumpf 1.20 ThreadStatus rtn = PEGASUS_THREAD_OK;
798 while ((rtn = MessageQueueService::get_thread_pool()->
799 allocate_and_awaken(this, _responseProcessor)) !=
800 PEGASUS_THREAD_OK)
801 {
802 if (rtn == PEGASUS_THREAD_INSUFFICIENT_RESOURCES)
803 {
804 pegasus_yield();
805 }
806 else
807 {
808 Logger::put(
809 Logger::STANDARD_LOG, System::CIMSERVER, Logger::TRACE,
810 "Not enough threads to process responses from the "
811 "provider agent.");
|
812 konrad.r 1.19
|
813 kumpf 1.20 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
814 "Could not allocate thread to process responses from the "
815 "provider agent.");
816
817 throw Exception(MessageLoaderParms(
818 "ProviderManager.OOPProviderManagerRouter."
819 "CIMPROVAGT_THREAD_ALLOCATION_FAILED",
820 "Failed to allocate thread for cimprovagt \"$0\".",
821 _moduleName));
822 }
823 }
|
824 kumpf 1.1 }
825 catch (...)
826 {
|
827 kumpf 1.20 // Closing the connection causes the agent process to exit
828 _pipeToAgent.reset();
829 _pipeFromAgent.reset();
830
|
831 kumpf 1.18 #if defined(PEGASUS_HAS_SIGNALS)
832 if (_isInitialized)
833 {
834 // Harvest the status of the agent process to prevent a zombie
835 Boolean keepWaiting = false;
836 do
837 {
838 pid_t status = waitpid(_pid, 0, 0);
839 if (status == -1)
840 {
841 if (errno == EINTR)
842 {
843 keepWaiting = true;
844 }
845 else
846 {
847 Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
848 "ProviderAgentContainer::_initialize(): "
849 "waitpid failed; errno = %d.", errno);
850 }
851 }
852 kumpf 1.18 } while (keepWaiting);
853 }
854 #endif
855
|
856 kumpf 1.1 _isInitialized = false;
|
857 kumpf 1.6
858 {
859 AutoMutex lock(_numProviderProcessesMutex);
860 _numProviderProcesses--;
861 }
862
|
863 kumpf 1.1 PEG_METHOD_EXIT();
864 throw;
865 }
866
867 PEG_METHOD_EXIT();
868 }
869
870 Boolean ProviderAgentContainer::isInitialized()
871 {
872 AutoMutex lock(_agentMutex);
873 return _isInitialized;
874 }
875
876 // Note: Caller must lock _agentMutex
|
877 kumpf 1.22 void ProviderAgentContainer::_uninitialize(Boolean cleanShutdown)
|
878 kumpf 1.1 {
879 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
880 "ProviderAgentContainer::_uninitialize");
881
882 if (!_isInitialized)
883 {
884 PEGASUS_ASSERT(0);
885 PEG_METHOD_EXIT();
886 return;
887 }
888
889 try
890 {
891 // Close the connection with the Provider Agent
892 _pipeFromAgent.reset();
893 _pipeToAgent.reset();
894
|
895 kumpf 1.2 _providerModuleCache = CIMInstance();
896
|
897 kumpf 1.6 {
898 AutoMutex lock(_numProviderProcessesMutex);
899 _numProviderProcesses--;
900 }
901
|
902 kumpf 1.18 #if defined(PEGASUS_HAS_SIGNALS)
903 // Harvest the status of the agent process to prevent a zombie
904 Boolean keepWaiting = false;
905 do
906 {
907 pid_t status = waitpid(_pid, 0, 0);
908 if (status == -1)
909 {
910 if (errno == EINTR)
911 {
912 keepWaiting = true;
913 }
914 else
915 {
916 Tracer::trace(TRC_DISCARDED_DATA, Tracer::LEVEL2,
917 "ProviderAgentContainer::_uninitialize(): "
918 "waitpid failed; errno = %d.", errno);
919 }
920 }
921 } while (keepWaiting);
922 #endif
923 kumpf 1.18
|
924 kumpf 1.1 _isInitialized = false;
925
926 //
927 // Complete with null responses all outstanding requests on this
928 // connection
929 //
930 {
931 AutoMutex tableLock(_outstandingRequestTableMutex);
932
|
933 kumpf 1.22 CIMResponseMessage* response =
934 cleanShutdown ? _REQUEST_NOT_PROCESSED : 0;
935
|
936 kumpf 1.1 for (OutstandingRequestTable::Iterator i =
937 _outstandingRequestTable.start();
938 i != 0; i++)
939 {
940 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
|
941 kumpf 1.30 String("Completing messageId \"") + i.key() +
|
942 kumpf 1.1 "\" with a null response.");
|
943 kumpf 1.22 i.value()->responseMessage = response;
|
944 kumpf 1.1 i.value()->responseReady->signal();
945 }
946
947 _outstandingRequestTable.clear();
|
948 carolann.graves 1.29
949 //
950 // If not a clean shutdown, call the provider module failure
951 // callback
952 //
953 if (!cleanShutdown)
954 {
955 //
956 // Call the provider module failure callback to
957 // communicate the failure to the Provider Manager Service
958 // Provider Manager Service will inform Indication Service
959 //
960 _providerModuleFailCallback (_moduleName, _userName,
961 _userContext);
962 }
|
963 kumpf 1.1 }
964 }
965 catch (...)
966 {
967 // We're uninitializing, so do not propagate the exception
968 PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
969 "Ignoring _uninitialize() exception.");
970 }
971
972 PEG_METHOD_EXIT();
973 }
974
|
975 kumpf 1.6 String ProviderAgentContainer::getModuleName() const
976 {
977 return _moduleName;
978 }
979
|
980 kumpf 1.1 CIMResponseMessage* ProviderAgentContainer::processMessage(
981 CIMRequestMessage* request)
982 {
983 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
984 "ProviderAgentContainer::processMessage");
985
986 CIMResponseMessage* response;
|
987 kumpf 1.22
988 do
989 {
990 response = _processMessage(request);
|
991 kumpf 1.25
992 if (response == _REQUEST_NOT_PROCESSED)
993 {
994 // Check for request message types that should not be retried.
995 if ((request->getType() ==
996 CIM_STOP_ALL_PROVIDERS_REQUEST_MESSAGE) ||
997 (request->getType() ==
998 CIM_NOTIFY_CONFIG_CHANGE_REQUEST_MESSAGE) ||
999 (request->getType() ==
1000 CIM_SUBSCRIPTION_INIT_COMPLETE_REQUEST_MESSAGE) ||
1001 (request->getType() ==
1002 CIM_DELETE_SUBSCRIPTION_REQUEST_MESSAGE))
1003 {
1004 response = request->buildResponse();
1005 break;
1006 }
1007 else if (request->getType() == CIM_DISABLE_MODULE_REQUEST_MESSAGE)
1008 {
1009 CIMDisableModuleResponseMessage* dmResponse =
1010 dynamic_cast<CIMDisableModuleResponseMessage*>(response);
1011 PEGASUS_ASSERT(dmResponse != 0);
1012 kumpf 1.25
1013 Array<Uint16> operationalStatus;
1014 operationalStatus.append(CIM_MSE_OPSTATUS_VALUE_STOPPED);
1015 dmResponse->operationalStatus = operationalStatus;
1016 break;
1017 }
1018 }
|
1019 kumpf 1.22 } while (response == _REQUEST_NOT_PROCESSED);
1020
1021 PEG_METHOD_EXIT();
1022 return response;
1023 }
1024
1025 CIMResponseMessage* ProviderAgentContainer::_processMessage(
1026 CIMRequestMessage* request)
1027 {
1028 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1029 "ProviderAgentContainer::_processMessage");
1030
1031 CIMResponseMessage* response;
|
1032 kumpf 1.1 String originalMessageId = request->messageId;
1033
|
1034 kumpf 1.2 // These three variables are used for the provider module optimization.
1035 // See the _providerModuleCache member description for more information.
1036 AutoPtr<ProviderIdContainer> origProviderId;
1037 Boolean doProviderModuleOptimization = false;
1038 Boolean updateProviderModuleCache = false;
1039
|
1040 kumpf 1.1 try
1041 {
1042 // The messageId attribute is used to correlate response messages
1043 // from the Provider Agent with request messages, so it is imperative
1044 // that the ID is unique for each request. The incoming ID cannot be
1045 // trusted to be unique, so we substitute a unique one. The memory
1046 // address of the request is used as the source of a unique piece of
1047 // data. (The message ID is only required to be unique while the
1048 // request is outstanding.)
1049 char messagePtrString[20];
1050 sprintf(messagePtrString, "%p", request);
1051 String uniqueMessageId = messagePtrString;
1052
1053 //
1054 // Set up the OutstandingRequestEntry for this request
1055 //
1056 Semaphore waitSemaphore(0);
1057 OutstandingRequestEntry outstandingRequestEntry(
|
1058 kumpf 1.30 originalMessageId, request, response, &waitSemaphore);
|
1059 kumpf 1.1
1060 //
1061 // Lock the Provider Agent Container while initializing the
1062 // agent and writing the request to the connection
1063 //
1064 {
1065 AutoMutex lock(_agentMutex);
1066
1067 //
1068 // Initialize the Provider Agent, if necessary
1069 //
1070 if (!_isInitialized)
1071 {
1072 _initialize();
1073 }
1074
1075 //
1076 // Add an entry to the OutstandingRequestTable for this request
1077 //
1078 {
1079 AutoMutex tableLock(_outstandingRequestTableMutex);
1080 kumpf 1.1
1081 _outstandingRequestTable.insert(
1082 uniqueMessageId, &outstandingRequestEntry);
1083 }
1084
|
1085 kumpf 1.2 // Get the provider module from the ProviderIdContainer to see if
1086 // we can optimize out the transmission of this instance to the
1087 // Provider Agent. (See the _providerModuleCache description.)
|
1088 a.dunfey 1.31 if(request->operationContext.contains(ProviderIdContainer::NAME))
|
1089 kumpf 1.2 {
1090 ProviderIdContainer pidc = request->operationContext.get(
1091 ProviderIdContainer::NAME);
1092 origProviderId.reset(new ProviderIdContainer(
1093 pidc.getModule(), pidc.getProvider(),
1094 pidc.isRemoteNameSpace(), pidc.getRemoteInfo()));
1095 if (_providerModuleCache.isUninitialized() ||
1096 (!pidc.getModule().identical(_providerModuleCache)))
1097 {
1098 // We haven't sent this provider module instance to the
1099 // Provider Agent yet. Update our cache after we send it.
1100 updateProviderModuleCache = true;
1101 }
1102 else
1103 {
1104 // Replace the provider module in the ProviderIdContainer
1105 // with an uninitialized instance. We'll need to put the
1106 // original one back after the message is sent.
1107 request->operationContext.set(ProviderIdContainer(
1108 CIMInstance(), pidc.getProvider(),
1109 pidc.isRemoteNameSpace(), pidc.getRemoteInfo()));
1110 kumpf 1.2 doProviderModuleOptimization = true;
1111 }
1112 }
1113
|
1114 kumpf 1.1 //
1115 // Write the message to the pipe
1116 //
1117 try
1118 {
1119 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL3,
1120 String("Sending request to agent with messageId ") +
1121 uniqueMessageId);
1122
1123 request->messageId = uniqueMessageId;
1124 AnonymousPipe::Status writeStatus =
1125 _pipeToAgent->writeMessage(request);
1126 request->messageId = originalMessageId;
1127
|
1128 kumpf 1.2 if (doProviderModuleOptimization)
1129 {
1130 request->operationContext.set(*origProviderId.get());
1131 }
1132
|
1133 kumpf 1.1 if (writeStatus != AnonymousPipe::STATUS_SUCCESS)
1134 {
1135 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1136 "Failed to write message to pipe. writeStatus = %d.",
1137 writeStatus);
|
1138 kumpf 1.25
1139 request->messageId = originalMessageId;
1140
1141 if (doProviderModuleOptimization)
1142 {
1143 request->operationContext.set(*origProviderId.get());
1144 }
1145
1146 // Remove this OutstandingRequestTable entry
1147 {
1148 AutoMutex tableLock(_outstandingRequestTableMutex);
1149 Boolean removed =
1150 _outstandingRequestTable.remove(uniqueMessageId);
1151 PEGASUS_ASSERT(removed);
1152 }
1153
1154 // A response value of _REQUEST_NOT_PROCESSED indicates
1155 // that the request was not processed by the provider
1156 // agent, so it can be retried safely.
1157 PEG_METHOD_EXIT();
1158 return _REQUEST_NOT_PROCESSED;
|
1159 kumpf 1.1 }
|
1160 kumpf 1.2
1161 if (updateProviderModuleCache)
1162 {
1163 _providerModuleCache = origProviderId->getModule();
1164 }
|
1165 kumpf 1.1 }
1166 catch (...)
1167 {
1168 request->messageId = originalMessageId;
|
1169 kumpf 1.2
1170 if (doProviderModuleOptimization)
1171 {
1172 request->operationContext.set(*origProviderId.get());
1173 }
1174
|
1175 kumpf 1.1 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1176 "Failed to write message to pipe.");
1177 // Remove the OutstandingRequestTable entry for this request
1178 {
1179 AutoMutex tableLock(_outstandingRequestTableMutex);
1180 Boolean removed =
1181 _outstandingRequestTable.remove(uniqueMessageId);
1182 PEGASUS_ASSERT(removed);
1183 }
|
1184 kumpf 1.25 PEG_METHOD_EXIT();
|
1185 kumpf 1.1 throw;
1186 }
1187 }
1188
1189 //
1190 // Wait for the response
1191 //
1192 try
1193 {
1194 // Must not hold _agentMutex while waiting for the response
1195 waitSemaphore.wait();
1196 }
1197 catch (...)
1198 {
1199 // Remove the OutstandingRequestTable entry for this request
1200 {
1201 AutoMutex tableLock(_outstandingRequestTableMutex);
1202 Boolean removed =
1203 _outstandingRequestTable.remove(uniqueMessageId);
1204 PEGASUS_ASSERT(removed);
1205 }
|
1206 kumpf 1.25 PEG_METHOD_EXIT();
|
1207 kumpf 1.1 throw;
1208 }
1209
|
1210 kumpf 1.22 // A response value of _REQUEST_NOT_PROCESSED indicates that the
1211 // provider agent process was terminating when the request was sent.
1212 // The request was not processed by the provider agent, so it can be
1213 // retried safely.
1214 if (response == _REQUEST_NOT_PROCESSED)
1215 {
|
1216 kumpf 1.25 PEG_METHOD_EXIT();
|
1217 kumpf 1.22 return response;
1218 }
1219
|
1220 kumpf 1.1 // A null response is returned when an agent connection is closed
1221 // while requests remain outstanding.
1222 if (response == 0)
1223 {
1224 response = request->buildResponse();
1225 response->cimException = PEGASUS_CIM_EXCEPTION(
1226 CIM_ERR_FAILED,
1227 MessageLoaderParms(
1228 "ProviderManager.OOPProviderManagerRouter."
1229 "CIMPROVAGT_CONNECTION_LOST",
1230 "Lost connection with cimprovagt \"$0\".",
1231 _moduleName));
1232 }
1233 }
1234 catch (CIMException& e)
1235 {
1236 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1237 String("Caught exception: ") + e.getMessage());
1238 response = request->buildResponse();
1239 response->cimException = e;
1240 }
1241 kumpf 1.1 catch (Exception& e)
1242 {
1243 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1244 String("Caught exception: ") + e.getMessage());
1245 response = request->buildResponse();
1246 response->cimException = PEGASUS_CIM_EXCEPTION(
1247 CIM_ERR_FAILED, e.getMessage());
1248 }
1249 catch (...)
1250 {
1251 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1252 "Caught unknown exception");
1253 response = request->buildResponse();
1254 response->cimException = PEGASUS_CIM_EXCEPTION(
1255 CIM_ERR_FAILED, String::EMPTY);
1256 }
1257
1258 response->messageId = originalMessageId;
1259
1260 PEG_METHOD_EXIT();
1261 return response;
1262 kumpf 1.1 }
1263
1264 void ProviderAgentContainer::unloadIdleProviders()
1265 {
1266 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
|
1267 carolann.graves 1.4 "ProviderAgentContainer::unloadIdleProviders");
|
1268 kumpf 1.1
1269 AutoMutex lock(_agentMutex);
1270 if (_isInitialized)
1271 {
1272 // Send a "wake up" message to the Provider Agent.
1273 // Don't bother checking whether the operation is successful.
1274 Uint32 messageLength = 0;
1275 _pipeToAgent->writeBuffer((const char*)&messageLength, sizeof(Uint32));
1276 }
1277
1278 PEG_METHOD_EXIT();
1279 }
1280
1281 void ProviderAgentContainer::_processResponses()
1282 {
1283 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1284 "ProviderAgentContainer::_processResponses");
1285
1286 //
1287 // Process responses until the pipe is closed
1288 //
1289 kumpf 1.1 while (1)
1290 {
1291 try
1292 {
1293 CIMMessage* message;
1294
1295 //
1296 // Read a response from the Provider Agent
1297 //
1298 AnonymousPipe::Status readStatus =
1299 _pipeFromAgent->readMessage(message);
1300
1301 // Ignore interrupts
1302 if (readStatus == AnonymousPipe::STATUS_INTERRUPT)
1303 {
1304 continue;
1305 }
1306
1307 // Handle an error the same way as a closed connection
1308 if ((readStatus == AnonymousPipe::STATUS_ERROR) ||
1309 (readStatus == AnonymousPipe::STATUS_CLOSED))
1310 kumpf 1.1 {
1311 AutoMutex lock(_agentMutex);
|
1312 kumpf 1.22 _uninitialize(false);
1313 return;
1314 }
1315
1316 // A null message indicates that the provider agent process has
1317 // finished its processing and is ready to exit.
1318 if (message == 0)
1319 {
1320 AutoMutex lock(_agentMutex);
1321 _uninitialize(true);
|
1322 kumpf 1.1 return;
1323 }
1324
1325 if (message->getType() == CIM_PROCESS_INDICATION_REQUEST_MESSAGE)
1326 {
1327 // Forward indications to the indication callback
1328 _indicationCallback(
1329 reinterpret_cast<CIMProcessIndicationRequestMessage*>(
1330 message));
1331 }
|
1332 kumpf 1.26 else if (!message->isComplete())
1333 {
1334 CIMResponseMessage* response;
1335 response = dynamic_cast<CIMResponseMessage*>(message);
1336 PEGASUS_ASSERT(response != 0);
1337
1338 // Get the OutstandingRequestEntry for this response chunk
1339 OutstandingRequestEntry* _outstandingRequestEntry = 0;
1340 {
1341 AutoMutex tableLock(_outstandingRequestTableMutex);
1342 Boolean foundEntry = _outstandingRequestTable.lookup(
1343 response->messageId, _outstandingRequestEntry);
1344 PEGASUS_ASSERT(foundEntry);
1345 }
1346
1347 // Put the original message ID into the response
1348 response->messageId =
|
1349 kumpf 1.30 _outstandingRequestEntry->originalMessageId;
|
1350 kumpf 1.26
1351 // Call the response chunk callback to process the chunk
1352 _responseChunkCallback(
1353 _outstandingRequestEntry->requestMessage, response);
1354 }
|
1355 kumpf 1.1 else
1356 {
1357 CIMResponseMessage* response;
1358 response = dynamic_cast<CIMResponseMessage*>(message);
1359 PEGASUS_ASSERT(response != 0);
1360
1361 // Give the response to the waiting OutstandingRequestEntry
1362 OutstandingRequestEntry* _outstandingRequestEntry = 0;
1363 {
1364 AutoMutex tableLock(_outstandingRequestTableMutex);
1365 Boolean foundEntry = _outstandingRequestTable.lookup(
1366 response->messageId, _outstandingRequestEntry);
1367 PEGASUS_ASSERT(foundEntry);
1368
1369 // Remove the completed request from the table
1370 Boolean removed =
1371 _outstandingRequestTable.remove(response->messageId);
1372 PEGASUS_ASSERT(removed);
1373 }
1374
1375 _outstandingRequestEntry->responseMessage = response;
1376 kumpf 1.1 _outstandingRequestEntry->responseReady->signal();
1377 }
1378 }
1379 catch (Exception& e)
1380 {
1381 PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
1382 String("Ignoring exception: ") + e.getMessage());
1383 }
1384 catch (...)
1385 {
1386 PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
1387 "Ignoring exception");
1388 }
1389 }
1390
1391 }
1392
1393 PEGASUS_THREAD_RETURN PEGASUS_THREAD_CDECL
1394 ProviderAgentContainer::_responseProcessor(void* arg)
1395 {
1396 ProviderAgentContainer* pa =
1397 kumpf 1.1 reinterpret_cast<ProviderAgentContainer*>(arg);
1398
1399 pa->_processResponses();
1400
1401 return(PEGASUS_THREAD_RETURN(0));
1402 }
1403
1404 /////////////////////////////////////////////////////////////////////////////
1405 // OOPProviderManagerRouter
1406 /////////////////////////////////////////////////////////////////////////////
1407
1408 OOPProviderManagerRouter::OOPProviderManagerRouter(
|
1409 kumpf 1.26 PEGASUS_INDICATION_CALLBACK_T indicationCallback,
|
1410 carolann.graves 1.29 PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback,
1411 PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T providerModuleFailCallback)
|
1412 kumpf 1.1 {
1413 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1414 "OOPProviderManagerRouter::OOPProviderManagerRouter");
1415
1416 _indicationCallback = indicationCallback;
|
1417 kumpf 1.26 _responseChunkCallback = responseChunkCallback;
|
1418 carolann.graves 1.29 _providerModuleFailCallback = providerModuleFailCallback;
|
1419 carolann.graves 1.14 _subscriptionInitComplete = false;
|
1420 kumpf 1.1
1421 PEG_METHOD_EXIT();
1422 }
1423
1424 OOPProviderManagerRouter::~OOPProviderManagerRouter()
1425 {
1426 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1427 "OOPProviderManagerRouter::~OOPProviderManagerRouter");
1428
1429 try
1430 {
1431 // Clean up the ProviderAgentContainers
1432 AutoMutex lock(_providerAgentTableMutex);
1433 ProviderAgentTable::Iterator i = _providerAgentTable.start();
1434 for(; i != 0; i++)
1435 {
1436 delete i.value();
1437 }
1438 }
1439 catch (...) {}
1440
1441 kumpf 1.1 PEG_METHOD_EXIT();
1442 }
1443
1444 Message* OOPProviderManagerRouter::processMessage(Message* message)
1445 {
1446 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1447 "OOPProviderManagerRouter::processMessage");
1448
1449 CIMRequestMessage* request = dynamic_cast<CIMRequestMessage *>(message);
1450 PEGASUS_ASSERT(request != 0);
1451
1452 AutoPtr<CIMResponseMessage> response;
1453
1454 //
1455 // Get the provider information from the request
1456 //
1457 CIMInstance providerModule;
1458
1459 if ((dynamic_cast<CIMOperationRequestMessage*>(request) != 0) ||
1460 (dynamic_cast<CIMIndicationRequestMessage*>(request) != 0) ||
1461 (request->getType() == CIM_EXPORT_INDICATION_REQUEST_MESSAGE))
1462 kumpf 1.1 {
1463 // Provider information is in the OperationContext
1464 ProviderIdContainer pidc = (ProviderIdContainer)
1465 request->operationContext.get(ProviderIdContainer::NAME);
1466 providerModule = pidc.getModule();
1467 }
1468 else if (request->getType() == CIM_ENABLE_MODULE_REQUEST_MESSAGE)
1469 {
1470 CIMEnableModuleRequestMessage* emReq =
1471 dynamic_cast<CIMEnableModuleRequestMessage*>(request);
1472 providerModule = emReq->providerModule;
1473 }
1474 else if (request->getType() == CIM_DISABLE_MODULE_REQUEST_MESSAGE)
1475 {
1476 CIMDisableModuleRequestMessage* dmReq =
1477 dynamic_cast<CIMDisableModuleRequestMessage*>(request);
1478 providerModule = dmReq->providerModule;
1479 }
1480 else if ((request->getType() == CIM_STOP_ALL_PROVIDERS_REQUEST_MESSAGE) ||
|
1481 carolann.graves 1.14 (request->getType() ==
1482 CIM_SUBSCRIPTION_INIT_COMPLETE_REQUEST_MESSAGE) ||
|
1483 kumpf 1.1 (request->getType() == CIM_NOTIFY_CONFIG_CHANGE_REQUEST_MESSAGE))
1484 {
1485 // This operation is not provider-specific
1486 }
1487 else
1488 {
1489 // Unrecognized message type. This should never happen.
1490 PEGASUS_ASSERT(0);
1491 response.reset(request->buildResponse());
1492 response->cimException = PEGASUS_CIM_EXCEPTION(
1493 CIM_ERR_FAILED, "Unrecognized message type.");
1494 PEG_METHOD_EXIT();
1495 return response.release();
1496 }
1497
1498 //
1499 // Process the request message
1500 //
1501 if (request->getType() == CIM_STOP_ALL_PROVIDERS_REQUEST_MESSAGE)
1502 {
1503 // Forward the CIMStopAllProvidersRequest to all providers
1504 kumpf 1.1 response.reset(_forwardRequestToAllAgents(request));
1505
1506 // Note: Do not uninitialize the ProviderAgentContainers here.
1507 // Just let the selecting thread notice when the agent connections
1508 // are closed.
1509 }
|
1510 carolann.graves 1.14 else if (request->getType () ==
1511 CIM_SUBSCRIPTION_INIT_COMPLETE_REQUEST_MESSAGE)
1512 {
1513 _subscriptionInitComplete = true;
1514
1515 //
1516 // Forward the CIMSubscriptionInitCompleteRequestMessage to
1517 // all providers
1518 //
1519 response.reset (_forwardRequestToAllAgents (request));
1520 }
|
1521 kumpf 1.1 else if (request->getType() == CIM_NOTIFY_CONFIG_CHANGE_REQUEST_MESSAGE)
1522 {
1523 CIMNotifyConfigChangeRequestMessage* notifyRequest =
1524 dynamic_cast<CIMNotifyConfigChangeRequestMessage*>(request);
1525 PEGASUS_ASSERT(notifyRequest != 0);
1526
1527 if (notifyRequest->currentValueModified)
1528 {
1529 // Forward the CIMNotifyConfigChangeRequestMessage to all providers
1530 response.reset(_forwardRequestToAllAgents(request));
1531 }
1532 else
1533 {
1534 // No need to notify provider agents about changes to planned value
1535 response.reset(request->buildResponse());
1536 }
1537 }
|
1538 kumpf 1.6 else if (request->getType() == CIM_DISABLE_MODULE_REQUEST_MESSAGE)
|
1539 kumpf 1.1 {
|
1540 kumpf 1.6 // Fan out the request to all Provider Agent processes for this module
1541
|
1542 kumpf 1.1 // Retrieve the provider module name
1543 String moduleName;
1544 CIMValue nameValue = providerModule.getProperty(
1545 providerModule.findProperty("Name")).getValue();
1546 nameValue.get(moduleName);
1547
|
1548 kumpf 1.6 // Look up the Provider Agents for this module
1549 Array<ProviderAgentContainer*> paArray =
1550 _lookupProviderAgents(moduleName);
|
1551 kumpf 1.1
|
1552 kumpf 1.6 for (Uint32 i=0; i<paArray.size(); i++)
|
1553 kumpf 1.1 {
1554 //
1555 // Do not start up an agent process just to disable the module
1556 //
|
1557 kumpf 1.6 if (paArray[i]->isInitialized())
1558 {
1559 //
1560 // Forward the request to the provider agent
1561 //
1562 response.reset(paArray[i]->processMessage(request));
1563
1564 // Note: Do not uninitialize the ProviderAgentContainer here
1565 // when a disable module operation is successful. Just let the
1566 // selecting thread notice when the agent connection is closed.
1567
1568 // Determine the success of the disable module operation
1569 CIMDisableModuleResponseMessage* dmResponse =
1570 dynamic_cast<CIMDisableModuleResponseMessage*>(
1571 response.get());
1572 PEGASUS_ASSERT(dmResponse != 0);
1573
1574 Boolean isStopped = false;
1575 for (Uint32 i=0; i < dmResponse->operationalStatus.size(); i++)
1576 {
1577 if (dmResponse->operationalStatus[i] ==
1578 kumpf 1.6 CIM_MSE_OPSTATUS_VALUE_STOPPED)
1579 {
1580 isStopped = true;
1581 break;
1582 }
1583 }
1584
1585 // If the operation is unsuccessful, stop and return the error
1586 if ((dmResponse->cimException.getCode() != CIM_ERR_SUCCESS) ||
1587 !isStopped)
1588 {
1589 break;
1590 }
1591 }
1592 }
1593
1594 // Use a default response if no Provider Agents were called
1595 if (!response.get())
1596 {
|
1597 kumpf 1.1 response.reset(request->buildResponse());
1598
1599 CIMDisableModuleResponseMessage* dmResponse =
1600 dynamic_cast<CIMDisableModuleResponseMessage*>(response.get());
1601 PEGASUS_ASSERT(dmResponse != 0);
1602
1603 Array<Uint16> operationalStatus;
1604 operationalStatus.append(CIM_MSE_OPSTATUS_VALUE_STOPPED);
1605 dmResponse->operationalStatus = operationalStatus;
1606 }
|
1607 kumpf 1.6 }
1608 else if (request->getType() == CIM_ENABLE_MODULE_REQUEST_MESSAGE)
1609 {
1610 // Fan out the request to all Provider Agent processes for this module
1611
1612 // Retrieve the provider module name
1613 String moduleName;
1614 CIMValue nameValue = providerModule.getProperty(
1615 providerModule.findProperty("Name")).getValue();
1616 nameValue.get(moduleName);
1617
1618 // Look up the Provider Agents for this module
1619 Array<ProviderAgentContainer*> paArray =
1620 _lookupProviderAgents(moduleName);
1621
1622 for (Uint32 i=0; i<paArray.size(); i++)
|
1623 kumpf 1.1 {
1624 //
1625 // Do not start up an agent process just to enable the module
1626 //
|
1627 kumpf 1.6 if (paArray[i]->isInitialized())
1628 {
1629 //
1630 // Forward the request to the provider agent
1631 //
1632 response.reset(paArray[i]->processMessage(request));
1633
1634 // Determine the success of the enable module operation
1635 CIMEnableModuleResponseMessage* emResponse =
1636 dynamic_cast<CIMEnableModuleResponseMessage*>(
1637 response.get());
1638 PEGASUS_ASSERT(emResponse != 0);
1639
1640 Boolean isOk = false;
1641 for (Uint32 i=0; i < emResponse->operationalStatus.size(); i++)
1642 {
1643 if (emResponse->operationalStatus[i] ==
1644 CIM_MSE_OPSTATUS_VALUE_OK)
1645 {
1646 isOk = true;
1647 break;
1648 kumpf 1.6 }
1649 }
1650
1651 // If the operation is unsuccessful, stop and return the error
1652 if ((emResponse->cimException.getCode() != CIM_ERR_SUCCESS) ||
1653 !isOk)
1654 {
1655 break;
1656 }
1657 }
1658 }
1659
1660 // Use a default response if no Provider Agents were called
1661 if (!response.get())
1662 {
|
1663 kumpf 1.1 response.reset(request->buildResponse());
1664
1665 CIMEnableModuleResponseMessage* emResponse =
1666 dynamic_cast<CIMEnableModuleResponseMessage*>(response.get());
1667 PEGASUS_ASSERT(emResponse != 0);
1668
1669 Array<Uint16> operationalStatus;
1670 operationalStatus.append(CIM_MSE_OPSTATUS_VALUE_OK);
1671 emResponse->operationalStatus = operationalStatus;
1672 }
|
1673 kumpf 1.6 }
1674 else
1675 {
|
1676 carolann.graves 1.29 //
1677 // Look up the Provider Agent for this module instance and requesting
1678 // user
1679 //
1680 ProviderAgentContainer* pa = _lookupProviderAgent(providerModule,
1681 request);
1682 PEGASUS_ASSERT(pa != 0);
1683
1684 //
1685 // Forward the request to the provider agent
1686 //
1687 response.reset(pa->processMessage(request));
1688 }
1689
1690 response->syncAttributes(request);
1691
1692 PEG_METHOD_EXIT();
1693 return response.release();
1694 }
1695
1696 ProviderAgentContainer* OOPProviderManagerRouter::_lookupProviderAgent(
1697 carolann.graves 1.29 const CIMInstance& providerModule,
1698 CIMRequestMessage* request)
1699 {
1700 // Retrieve the provider module name
1701 String moduleName;
1702 CIMValue nameValue = providerModule.getProperty(
1703 providerModule.findProperty("Name")).getValue();
1704 nameValue.get(moduleName);
|
1705 kumpf 1.6
|
1706 carolann.graves 1.29 // Retrieve the provider user context configuration
1707 Uint16 userContext = 0;
1708 Uint32 pos = providerModule.findProperty(
1709 PEGASUS_PROPERTYNAME_MODULE_USERCONTEXT);
1710 if (pos != PEG_NOT_FOUND)
1711 {
1712 CIMValue userContextValue =
1713 providerModule.getProperty(pos).getValue();
1714 if (!userContextValue.isNull())
|
1715 kumpf 1.6 {
|
1716 carolann.graves 1.29 userContextValue.get(userContext);
|
1717 kumpf 1.6 }
|
1718 carolann.graves 1.29 }
|
1719 kumpf 1.6
|
1720 carolann.graves 1.29 if (userContext == 0)
1721 {
1722 userContext = PEGASUS_DEFAULT_PROV_USERCTXT;
1723 }
|
1724 kumpf 1.6
|
1725 carolann.graves 1.29 String userName;
|
1726 kumpf 1.6
|
1727 carolann.graves 1.29 if (userContext == PG_PROVMODULE_USERCTXT_REQUESTOR)
1728 {
|
1729 a.dunfey 1.31 if(request->operationContext.contains(IdentityContainer::NAME))
|
1730 kumpf 1.1 {
|
1731 carolann.graves 1.29 // User Name is in the OperationContext
1732 IdentityContainer ic = (IdentityContainer)
1733 request->operationContext.get(IdentityContainer::NAME);
1734 userName = ic.getUserName();
|
1735 kumpf 1.6 }
|
1736 a.dunfey 1.31 //else
1737 //{
1738 // If no IdentityContainer is present, default to the CIM
1739 // Server's user context
1740 //}
|
1741 carolann.graves 1.29
1742 // If authentication is disabled, use the CIM Server's user context
1743 if (!userName.size())
|
1744 kumpf 1.6 {
1745 userName = System::getEffectiveUserName();
1746 }
|
1747 carolann.graves 1.29 }
1748 else if (userContext == PG_PROVMODULE_USERCTXT_DESIGNATED)
1749 {
1750 // Retrieve the provider module designated user property value
1751 providerModule.getProperty(providerModule.findProperty(
1752 PEGASUS_PROPERTYNAME_MODULE_DESIGNATEDUSER)).getValue().
1753 get(userName);
1754 }
1755 else if (userContext == PG_PROVMODULE_USERCTXT_CIMSERVER)
1756 {
1757 userName = System::getEffectiveUserName();
1758 }
1759 else // Privileged User
1760 {
1761 PEGASUS_ASSERT(userContext == PG_PROVMODULE_USERCTXT_PRIVILEGED);
1762 userName = System::getPrivilegedUserName();
|
1763 kumpf 1.1 }
1764
|
1765 carolann.graves 1.29 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL4,
1766 "Module name = " + moduleName);
1767 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL4,
1768 "User context = %hd.", userContext);
1769 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL4,
1770 "User name = " + userName);
|
1771 kumpf 1.1
1772 ProviderAgentContainer* pa = 0;
|
1773 kumpf 1.6 String key = moduleName + ":" + userName;
|
1774 kumpf 1.1
1775 AutoMutex lock(_providerAgentTableMutex);
|
1776 kumpf 1.6 if (!_providerAgentTable.lookup(key, pa))
|
1777 kumpf 1.1 {
|
1778 kumpf 1.6 pa = new ProviderAgentContainer(
|
1779 carolann.graves 1.29 moduleName, userName, userContext,
1780 _indicationCallback, _responseChunkCallback,
1781 _providerModuleFailCallback,
|
1782 carolann.graves 1.14 _subscriptionInitComplete);
|
1783 kumpf 1.6 _providerAgentTable.insert(key, pa);
|
1784 kumpf 1.1 }
1785 return pa;
1786 }
1787
|
1788 kumpf 1.6 Array<ProviderAgentContainer*> OOPProviderManagerRouter::_lookupProviderAgents(
1789 const String& moduleName)
1790 {
1791 Array<ProviderAgentContainer*> paArray;
1792
1793 AutoMutex lock(_providerAgentTableMutex);
1794 for (ProviderAgentTable::Iterator i = _providerAgentTable.start(); i; i++)
1795 {
1796 if (i.value()->getModuleName() == moduleName)
1797 {
1798 paArray.append(i.value());
1799 }
1800 }
1801 return paArray;
1802 }
1803
|
1804 kumpf 1.1 CIMResponseMessage* OOPProviderManagerRouter::_forwardRequestToAllAgents(
1805 CIMRequestMessage* request)
1806 {
1807 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1808 "OOPProviderManagerRouter::_forwardRequestToAllAgents");
1809
1810 // Get a list of the ProviderAgentContainers. We need our own array copy
1811 // because we cannot hold the _providerAgentTableMutex while calling
1812 // _ProviderAgentContainer::processMessage().
1813 Array<ProviderAgentContainer*> paContainerArray;
1814 {
1815 AutoMutex tableLock(_providerAgentTableMutex);
1816 for (ProviderAgentTable::Iterator i = _providerAgentTable.start();
1817 i != 0; i++)
1818 {
1819 paContainerArray.append(i.value());
1820 }
1821 }
1822
1823 CIMException responseException;
1824
1825 kumpf 1.1 // Forward the request to each of the initialized provider agents
1826 for (Uint32 j = 0; j < paContainerArray.size(); j++)
1827 {
1828 ProviderAgentContainer* pa = paContainerArray[j];
1829 if (pa->isInitialized())
1830 {
1831 // Note: The ProviderAgentContainer could become uninitialized
1832 // before _ProviderAgentContainer::processMessage() processes
1833 // this request. In this case, the Provider Agent process will
1834 // (unfortunately) be started to process this message.
1835 AutoPtr<CIMResponseMessage> response;
1836 response.reset(pa->processMessage(request));
1837 if (response.get() != 0)
1838 {
1839 // If the operation failed, save the exception data
1840 if ((response->cimException.getCode() != CIM_ERR_SUCCESS) &&
1841 (responseException.getCode() == CIM_ERR_SUCCESS))
1842 {
1843 responseException = response->cimException;
1844 }
1845 }
1846 kumpf 1.1 }
1847 }
1848
1849 CIMResponseMessage* response = request->buildResponse();
1850 response->cimException = responseException;
1851
1852 PEG_METHOD_EXIT();
1853 return response;
1854 }
1855
1856 Boolean OOPProviderManagerRouter::hasActiveProviders()
1857 {
1858 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1859 "OOPProviderManagerRouter::hasActiveProviders");
1860
1861 // Iterate through the _providerAgentTable looking for initialized agents
1862 AutoMutex lock(_providerAgentTableMutex);
1863 ProviderAgentTable::Iterator i = _providerAgentTable.start();
1864 for(; i != 0; i++)
1865 {
1866 if (i.value()->isInitialized())
1867 kumpf 1.1 {
1868 PEG_METHOD_EXIT();
1869 return true;
1870 }
1871 }
1872
1873 // No initialized Provider Agents were found
1874 PEG_METHOD_EXIT();
1875 return false;
1876 }
1877
1878 void OOPProviderManagerRouter::unloadIdleProviders()
1879 {
1880 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1881 "OOPProviderManagerRouter::unloadIdleProviders");
1882
1883 // Iterate through the _providerAgentTable unloading idle providers
1884 AutoMutex lock(_providerAgentTableMutex);
1885 ProviderAgentTable::Iterator i = _providerAgentTable.start();
1886 for(; i != 0; i++)
1887 {
1888 kumpf 1.1 i.value()->unloadIdleProviders();
1889 }
1890
1891 PEG_METHOD_EXIT();
1892 }
1893
1894 PEGASUS_NAMESPACE_END
|