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.25.2.4 String originalMessageId_,
|
101 kumpf 1.25.2.1 CIMRequestMessage* requestMessage_,
|
102 kumpf 1.1 CIMResponseMessage*& responseMessage_,
103 Semaphore* responseReady_)
|
104 kumpf 1.25.2.4 : originalMessageId(originalMessageId_),
|
105 kumpf 1.25.2.1 requestMessage(requestMessage_),
|
106 kumpf 1.1 responseMessage(responseMessage_),
107 responseReady(responseReady_)
108 {
109 }
110
|
111 kumpf 1.25.2.4 /**
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.25.2.1 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.25.2.5 Uint16 userContext,
|
139 kumpf 1.25.2.1 PEGASUS_INDICATION_CALLBACK_T indicationCallback,
140 PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback,
|
141 carolann.graves 1.25.2.5 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.25.2.5 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.25.2.1 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.25.2.5 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.25.2.5 Uint16 userContext,
|
350 kumpf 1.25.2.1 PEGASUS_INDICATION_CALLBACK_T indicationCallback,
351 PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback,
|
352 carolann.graves 1.25.2.5 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.25.2.5 _userContext(userContext),
|
357 kumpf 1.1 _indicationCallback(indicationCallback),
|
358 kumpf 1.25.2.1 _responseChunkCallback(responseChunkCallback),
|
359 carolann.graves 1.25.2.5 _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.25.2.5 //Out of process provider support for OS400 goes here when needed.
|
549 chuck 1.16
|
550 kumpf 1.1 #else
|
551 kumpf 1.25.2.3
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.25.2.3
|
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.25.2.3 # ifndef PEGASUS_DISABLE_PROV_USERCTXT
|
609 kumpf 1.6 // Set the user context of the Provider Agent process
|
610 kumpf 1.25.2.3 if (_userName != effectiveUserName)
|
611 kumpf 1.6 {
|
612 kumpf 1.25.2.3 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.25.2.3 # 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.25.2.4 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.25.2.5
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.25.2.4 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 try
1089 {
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 kumpf 1.2 // original one back after the message is sent.
1107 request->operationContext.set(ProviderIdContainer(
1108 CIMInstance(), pidc.getProvider(),
1109 pidc.isRemoteNameSpace(), pidc.getRemoteInfo()));
1110 doProviderModuleOptimization = true;
1111 }
1112 }
1113 catch (...)
1114 {
1115 // No ProviderIdContainer to optimize
1116 }
1117
|
1118 kumpf 1.1 //
1119 // Write the message to the pipe
1120 //
1121 try
1122 {
1123 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL3,
1124 String("Sending request to agent with messageId ") +
1125 uniqueMessageId);
1126
1127 request->messageId = uniqueMessageId;
1128 AnonymousPipe::Status writeStatus =
1129 _pipeToAgent->writeMessage(request);
1130 request->messageId = originalMessageId;
1131
|
1132 kumpf 1.2 if (doProviderModuleOptimization)
1133 {
1134 request->operationContext.set(*origProviderId.get());
1135 }
1136
|
1137 kumpf 1.1 if (writeStatus != AnonymousPipe::STATUS_SUCCESS)
1138 {
1139 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1140 "Failed to write message to pipe. writeStatus = %d.",
1141 writeStatus);
|
1142 kumpf 1.25
1143 request->messageId = originalMessageId;
1144
1145 if (doProviderModuleOptimization)
1146 {
1147 request->operationContext.set(*origProviderId.get());
1148 }
1149
1150 // Remove this OutstandingRequestTable entry
1151 {
1152 AutoMutex tableLock(_outstandingRequestTableMutex);
1153 Boolean removed =
1154 _outstandingRequestTable.remove(uniqueMessageId);
1155 PEGASUS_ASSERT(removed);
1156 }
1157
1158 // A response value of _REQUEST_NOT_PROCESSED indicates
1159 // that the request was not processed by the provider
1160 // agent, so it can be retried safely.
1161 PEG_METHOD_EXIT();
1162 return _REQUEST_NOT_PROCESSED;
|
1163 kumpf 1.1 }
|
1164 kumpf 1.2
1165 if (updateProviderModuleCache)
1166 {
1167 _providerModuleCache = origProviderId->getModule();
1168 }
|
1169 kumpf 1.1 }
1170 catch (...)
1171 {
1172 request->messageId = originalMessageId;
|
1173 kumpf 1.2
1174 if (doProviderModuleOptimization)
1175 {
1176 request->operationContext.set(*origProviderId.get());
1177 }
1178
|
1179 kumpf 1.1 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1180 "Failed to write message to pipe.");
1181 // Remove the OutstandingRequestTable entry for this request
1182 {
1183 AutoMutex tableLock(_outstandingRequestTableMutex);
1184 Boolean removed =
1185 _outstandingRequestTable.remove(uniqueMessageId);
1186 PEGASUS_ASSERT(removed);
1187 }
|
1188 kumpf 1.25 PEG_METHOD_EXIT();
|
1189 kumpf 1.1 throw;
1190 }
1191 }
1192
1193 //
1194 // Wait for the response
1195 //
1196 try
1197 {
1198 // Must not hold _agentMutex while waiting for the response
1199 waitSemaphore.wait();
1200 }
1201 catch (...)
1202 {
1203 // Remove the OutstandingRequestTable entry for this request
1204 {
1205 AutoMutex tableLock(_outstandingRequestTableMutex);
1206 Boolean removed =
1207 _outstandingRequestTable.remove(uniqueMessageId);
1208 PEGASUS_ASSERT(removed);
1209 }
|
1210 kumpf 1.25 PEG_METHOD_EXIT();
|
1211 kumpf 1.1 throw;
1212 }
1213
|
1214 kumpf 1.22 // A response value of _REQUEST_NOT_PROCESSED indicates that the
1215 // provider agent process was terminating when the request was sent.
1216 // The request was not processed by the provider agent, so it can be
1217 // retried safely.
1218 if (response == _REQUEST_NOT_PROCESSED)
1219 {
|
1220 kumpf 1.25 PEG_METHOD_EXIT();
|
1221 kumpf 1.22 return response;
1222 }
1223
|
1224 kumpf 1.1 // A null response is returned when an agent connection is closed
1225 // while requests remain outstanding.
1226 if (response == 0)
1227 {
1228 response = request->buildResponse();
1229 response->cimException = PEGASUS_CIM_EXCEPTION(
1230 CIM_ERR_FAILED,
1231 MessageLoaderParms(
1232 "ProviderManager.OOPProviderManagerRouter."
1233 "CIMPROVAGT_CONNECTION_LOST",
1234 "Lost connection with cimprovagt \"$0\".",
1235 _moduleName));
1236 }
1237 }
1238 catch (CIMException& e)
1239 {
1240 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1241 String("Caught exception: ") + e.getMessage());
1242 response = request->buildResponse();
1243 response->cimException = e;
1244 }
1245 kumpf 1.1 catch (Exception& e)
1246 {
1247 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1248 String("Caught exception: ") + e.getMessage());
1249 response = request->buildResponse();
1250 response->cimException = PEGASUS_CIM_EXCEPTION(
1251 CIM_ERR_FAILED, e.getMessage());
1252 }
1253 catch (...)
1254 {
1255 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL2,
1256 "Caught unknown exception");
1257 response = request->buildResponse();
1258 response->cimException = PEGASUS_CIM_EXCEPTION(
1259 CIM_ERR_FAILED, String::EMPTY);
1260 }
1261
1262 response->messageId = originalMessageId;
1263
1264 PEG_METHOD_EXIT();
1265 return response;
1266 kumpf 1.1 }
1267
1268 void ProviderAgentContainer::unloadIdleProviders()
1269 {
1270 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
|
1271 carolann.graves 1.4 "ProviderAgentContainer::unloadIdleProviders");
|
1272 kumpf 1.1
1273 AutoMutex lock(_agentMutex);
1274 if (_isInitialized)
1275 {
1276 // Send a "wake up" message to the Provider Agent.
1277 // Don't bother checking whether the operation is successful.
1278 Uint32 messageLength = 0;
1279 _pipeToAgent->writeBuffer((const char*)&messageLength, sizeof(Uint32));
1280 }
1281
1282 PEG_METHOD_EXIT();
1283 }
1284
1285 void ProviderAgentContainer::_processResponses()
1286 {
1287 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1288 "ProviderAgentContainer::_processResponses");
1289
1290 //
1291 // Process responses until the pipe is closed
1292 //
1293 kumpf 1.1 while (1)
1294 {
1295 try
1296 {
1297 CIMMessage* message;
1298
1299 //
1300 // Read a response from the Provider Agent
1301 //
1302 AnonymousPipe::Status readStatus =
1303 _pipeFromAgent->readMessage(message);
1304
1305 // Ignore interrupts
1306 if (readStatus == AnonymousPipe::STATUS_INTERRUPT)
1307 {
1308 continue;
1309 }
1310
1311 // Handle an error the same way as a closed connection
1312 if ((readStatus == AnonymousPipe::STATUS_ERROR) ||
1313 (readStatus == AnonymousPipe::STATUS_CLOSED))
1314 kumpf 1.1 {
1315 AutoMutex lock(_agentMutex);
|
1316 kumpf 1.22 _uninitialize(false);
1317 return;
1318 }
1319
1320 // A null message indicates that the provider agent process has
1321 // finished its processing and is ready to exit.
1322 if (message == 0)
1323 {
1324 AutoMutex lock(_agentMutex);
1325 _uninitialize(true);
|
1326 kumpf 1.1 return;
1327 }
1328
1329 if (message->getType() == CIM_PROCESS_INDICATION_REQUEST_MESSAGE)
1330 {
1331 // Forward indications to the indication callback
1332 _indicationCallback(
1333 reinterpret_cast<CIMProcessIndicationRequestMessage*>(
1334 message));
1335 }
|
1336 kumpf 1.25.2.1 else if (!message->isComplete())
1337 {
1338 CIMResponseMessage* response;
1339 response = dynamic_cast<CIMResponseMessage*>(message);
1340 PEGASUS_ASSERT(response != 0);
1341
1342 // Get the OutstandingRequestEntry for this response chunk
1343 OutstandingRequestEntry* _outstandingRequestEntry = 0;
1344 {
1345 AutoMutex tableLock(_outstandingRequestTableMutex);
1346 Boolean foundEntry = _outstandingRequestTable.lookup(
1347 response->messageId, _outstandingRequestEntry);
1348 PEGASUS_ASSERT(foundEntry);
1349 }
1350
1351 // Put the original message ID into the response
1352 response->messageId =
|
1353 kumpf 1.25.2.4 _outstandingRequestEntry->originalMessageId;
|
1354 kumpf 1.25.2.1
1355 // Call the response chunk callback to process the chunk
1356 _responseChunkCallback(
1357 _outstandingRequestEntry->requestMessage, response);
1358 }
|
1359 kumpf 1.1 else
1360 {
1361 CIMResponseMessage* response;
1362 response = dynamic_cast<CIMResponseMessage*>(message);
1363 PEGASUS_ASSERT(response != 0);
1364
1365 // Give the response to the waiting OutstandingRequestEntry
1366 OutstandingRequestEntry* _outstandingRequestEntry = 0;
1367 {
1368 AutoMutex tableLock(_outstandingRequestTableMutex);
1369 Boolean foundEntry = _outstandingRequestTable.lookup(
1370 response->messageId, _outstandingRequestEntry);
1371 PEGASUS_ASSERT(foundEntry);
1372
1373 // Remove the completed request from the table
1374 Boolean removed =
1375 _outstandingRequestTable.remove(response->messageId);
1376 PEGASUS_ASSERT(removed);
1377 }
1378
1379 _outstandingRequestEntry->responseMessage = response;
1380 kumpf 1.1 _outstandingRequestEntry->responseReady->signal();
1381 }
1382 }
1383 catch (Exception& e)
1384 {
1385 PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
1386 String("Ignoring exception: ") + e.getMessage());
1387 }
1388 catch (...)
1389 {
1390 PEG_TRACE_STRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
1391 "Ignoring exception");
1392 }
1393 }
1394
1395 }
1396
1397 PEGASUS_THREAD_RETURN PEGASUS_THREAD_CDECL
1398 ProviderAgentContainer::_responseProcessor(void* arg)
1399 {
1400 ProviderAgentContainer* pa =
1401 kumpf 1.1 reinterpret_cast<ProviderAgentContainer*>(arg);
1402
1403 pa->_processResponses();
1404
1405 return(PEGASUS_THREAD_RETURN(0));
1406 }
1407
1408 /////////////////////////////////////////////////////////////////////////////
1409 // OOPProviderManagerRouter
1410 /////////////////////////////////////////////////////////////////////////////
1411
1412 OOPProviderManagerRouter::OOPProviderManagerRouter(
|
1413 kumpf 1.25.2.1 PEGASUS_INDICATION_CALLBACK_T indicationCallback,
|
1414 carolann.graves 1.25.2.5 PEGASUS_RESPONSE_CHUNK_CALLBACK_T responseChunkCallback,
1415 PEGASUS_PROVIDERMODULEFAIL_CALLBACK_T providerModuleFailCallback)
|
1416 kumpf 1.1 {
1417 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1418 "OOPProviderManagerRouter::OOPProviderManagerRouter");
1419
1420 _indicationCallback = indicationCallback;
|
1421 kumpf 1.25.2.1 _responseChunkCallback = responseChunkCallback;
|
1422 carolann.graves 1.25.2.5 _providerModuleFailCallback = providerModuleFailCallback;
|
1423 carolann.graves 1.14 _subscriptionInitComplete = false;
|
1424 kumpf 1.1
1425 PEG_METHOD_EXIT();
1426 }
1427
1428 OOPProviderManagerRouter::~OOPProviderManagerRouter()
1429 {
1430 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1431 "OOPProviderManagerRouter::~OOPProviderManagerRouter");
1432
1433 try
1434 {
1435 // Clean up the ProviderAgentContainers
1436 AutoMutex lock(_providerAgentTableMutex);
1437 ProviderAgentTable::Iterator i = _providerAgentTable.start();
1438 for(; i != 0; i++)
1439 {
1440 delete i.value();
1441 }
1442 }
1443 catch (...) {}
1444
1445 kumpf 1.1 PEG_METHOD_EXIT();
1446 }
1447
1448 Message* OOPProviderManagerRouter::processMessage(Message* message)
1449 {
1450 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1451 "OOPProviderManagerRouter::processMessage");
1452
1453 CIMRequestMessage* request = dynamic_cast<CIMRequestMessage *>(message);
1454 PEGASUS_ASSERT(request != 0);
1455
1456 AutoPtr<CIMResponseMessage> response;
1457
1458 //
1459 // Get the provider information from the request
1460 //
1461 CIMInstance providerModule;
1462
1463 if ((dynamic_cast<CIMOperationRequestMessage*>(request) != 0) ||
1464 (dynamic_cast<CIMIndicationRequestMessage*>(request) != 0) ||
1465 (request->getType() == CIM_EXPORT_INDICATION_REQUEST_MESSAGE))
1466 kumpf 1.1 {
1467 // Provider information is in the OperationContext
1468 ProviderIdContainer pidc = (ProviderIdContainer)
1469 request->operationContext.get(ProviderIdContainer::NAME);
1470 providerModule = pidc.getModule();
1471 }
1472 else if (request->getType() == CIM_ENABLE_MODULE_REQUEST_MESSAGE)
1473 {
1474 CIMEnableModuleRequestMessage* emReq =
1475 dynamic_cast<CIMEnableModuleRequestMessage*>(request);
1476 providerModule = emReq->providerModule;
1477 }
1478 else if (request->getType() == CIM_DISABLE_MODULE_REQUEST_MESSAGE)
1479 {
1480 CIMDisableModuleRequestMessage* dmReq =
1481 dynamic_cast<CIMDisableModuleRequestMessage*>(request);
1482 providerModule = dmReq->providerModule;
1483 }
1484 else if ((request->getType() == CIM_STOP_ALL_PROVIDERS_REQUEST_MESSAGE) ||
|
1485 carolann.graves 1.14 (request->getType() ==
1486 CIM_SUBSCRIPTION_INIT_COMPLETE_REQUEST_MESSAGE) ||
|
1487 kumpf 1.1 (request->getType() == CIM_NOTIFY_CONFIG_CHANGE_REQUEST_MESSAGE))
1488 {
1489 // This operation is not provider-specific
1490 }
1491 else
1492 {
1493 // Unrecognized message type. This should never happen.
1494 PEGASUS_ASSERT(0);
1495 response.reset(request->buildResponse());
1496 response->cimException = PEGASUS_CIM_EXCEPTION(
1497 CIM_ERR_FAILED, "Unrecognized message type.");
1498 PEG_METHOD_EXIT();
1499 return response.release();
1500 }
1501
1502 //
1503 // Process the request message
1504 //
1505 if (request->getType() == CIM_STOP_ALL_PROVIDERS_REQUEST_MESSAGE)
1506 {
1507 // Forward the CIMStopAllProvidersRequest to all providers
1508 kumpf 1.1 response.reset(_forwardRequestToAllAgents(request));
1509
1510 // Note: Do not uninitialize the ProviderAgentContainers here.
1511 // Just let the selecting thread notice when the agent connections
1512 // are closed.
1513 }
|
1514 carolann.graves 1.14 else if (request->getType () ==
1515 CIM_SUBSCRIPTION_INIT_COMPLETE_REQUEST_MESSAGE)
1516 {
1517 _subscriptionInitComplete = true;
1518
1519 //
1520 // Forward the CIMSubscriptionInitCompleteRequestMessage to
1521 // all providers
1522 //
1523 response.reset (_forwardRequestToAllAgents (request));
1524 }
|
1525 kumpf 1.1 else if (request->getType() == CIM_NOTIFY_CONFIG_CHANGE_REQUEST_MESSAGE)
1526 {
1527 CIMNotifyConfigChangeRequestMessage* notifyRequest =
1528 dynamic_cast<CIMNotifyConfigChangeRequestMessage*>(request);
1529 PEGASUS_ASSERT(notifyRequest != 0);
1530
1531 if (notifyRequest->currentValueModified)
1532 {
1533 // Forward the CIMNotifyConfigChangeRequestMessage to all providers
1534 response.reset(_forwardRequestToAllAgents(request));
1535 }
1536 else
1537 {
1538 // No need to notify provider agents about changes to planned value
1539 response.reset(request->buildResponse());
1540 }
1541 }
|
1542 kumpf 1.6 else if (request->getType() == CIM_DISABLE_MODULE_REQUEST_MESSAGE)
|
1543 kumpf 1.1 {
|
1544 kumpf 1.6 // Fan out the request to all Provider Agent processes for this module
1545
|
1546 kumpf 1.1 // Retrieve the provider module name
1547 String moduleName;
1548 CIMValue nameValue = providerModule.getProperty(
1549 providerModule.findProperty("Name")).getValue();
1550 nameValue.get(moduleName);
1551
|
1552 kumpf 1.6 // Look up the Provider Agents for this module
1553 Array<ProviderAgentContainer*> paArray =
1554 _lookupProviderAgents(moduleName);
|
1555 kumpf 1.1
|
1556 kumpf 1.6 for (Uint32 i=0; i<paArray.size(); i++)
|
1557 kumpf 1.1 {
1558 //
1559 // Do not start up an agent process just to disable the module
1560 //
|
1561 kumpf 1.6 if (paArray[i]->isInitialized())
1562 {
1563 //
1564 // Forward the request to the provider agent
1565 //
1566 response.reset(paArray[i]->processMessage(request));
1567
1568 // Note: Do not uninitialize the ProviderAgentContainer here
1569 // when a disable module operation is successful. Just let the
1570 // selecting thread notice when the agent connection is closed.
1571
1572 // Determine the success of the disable module operation
1573 CIMDisableModuleResponseMessage* dmResponse =
1574 dynamic_cast<CIMDisableModuleResponseMessage*>(
1575 response.get());
1576 PEGASUS_ASSERT(dmResponse != 0);
1577
1578 Boolean isStopped = false;
1579 for (Uint32 i=0; i < dmResponse->operationalStatus.size(); i++)
1580 {
1581 if (dmResponse->operationalStatus[i] ==
1582 kumpf 1.6 CIM_MSE_OPSTATUS_VALUE_STOPPED)
1583 {
1584 isStopped = true;
1585 break;
1586 }
1587 }
1588
1589 // If the operation is unsuccessful, stop and return the error
1590 if ((dmResponse->cimException.getCode() != CIM_ERR_SUCCESS) ||
1591 !isStopped)
1592 {
1593 break;
1594 }
1595 }
1596 }
1597
1598 // Use a default response if no Provider Agents were called
1599 if (!response.get())
1600 {
|
1601 kumpf 1.1 response.reset(request->buildResponse());
1602
1603 CIMDisableModuleResponseMessage* dmResponse =
1604 dynamic_cast<CIMDisableModuleResponseMessage*>(response.get());
1605 PEGASUS_ASSERT(dmResponse != 0);
1606
1607 Array<Uint16> operationalStatus;
1608 operationalStatus.append(CIM_MSE_OPSTATUS_VALUE_STOPPED);
1609 dmResponse->operationalStatus = operationalStatus;
1610 }
|
1611 kumpf 1.6 }
1612 else if (request->getType() == CIM_ENABLE_MODULE_REQUEST_MESSAGE)
1613 {
1614 // Fan out the request to all Provider Agent processes for this module
1615
1616 // Retrieve the provider module name
1617 String moduleName;
1618 CIMValue nameValue = providerModule.getProperty(
1619 providerModule.findProperty("Name")).getValue();
1620 nameValue.get(moduleName);
1621
1622 // Look up the Provider Agents for this module
1623 Array<ProviderAgentContainer*> paArray =
1624 _lookupProviderAgents(moduleName);
1625
1626 for (Uint32 i=0; i<paArray.size(); i++)
|
1627 kumpf 1.1 {
1628 //
1629 // Do not start up an agent process just to enable the module
1630 //
|
1631 kumpf 1.6 if (paArray[i]->isInitialized())
1632 {
1633 //
1634 // Forward the request to the provider agent
1635 //
1636 response.reset(paArray[i]->processMessage(request));
1637
1638 // Determine the success of the enable module operation
1639 CIMEnableModuleResponseMessage* emResponse =
1640 dynamic_cast<CIMEnableModuleResponseMessage*>(
1641 response.get());
1642 PEGASUS_ASSERT(emResponse != 0);
1643
1644 Boolean isOk = false;
1645 for (Uint32 i=0; i < emResponse->operationalStatus.size(); i++)
1646 {
1647 if (emResponse->operationalStatus[i] ==
1648 CIM_MSE_OPSTATUS_VALUE_OK)
1649 {
1650 isOk = true;
1651 break;
1652 kumpf 1.6 }
1653 }
1654
1655 // If the operation is unsuccessful, stop and return the error
1656 if ((emResponse->cimException.getCode() != CIM_ERR_SUCCESS) ||
1657 !isOk)
1658 {
1659 break;
1660 }
1661 }
1662 }
1663
1664 // Use a default response if no Provider Agents were called
1665 if (!response.get())
1666 {
|
1667 kumpf 1.1 response.reset(request->buildResponse());
1668
1669 CIMEnableModuleResponseMessage* emResponse =
1670 dynamic_cast<CIMEnableModuleResponseMessage*>(response.get());
1671 PEGASUS_ASSERT(emResponse != 0);
1672
1673 Array<Uint16> operationalStatus;
1674 operationalStatus.append(CIM_MSE_OPSTATUS_VALUE_OK);
1675 emResponse->operationalStatus = operationalStatus;
1676 }
|
1677 kumpf 1.6 }
1678 else
1679 {
|
1680 carolann.graves 1.25.2.5 //
1681 // Look up the Provider Agent for this module instance and requesting
1682 // user
1683 //
1684 ProviderAgentContainer* pa = _lookupProviderAgent(providerModule,
1685 request);
|
1686 kumpf 1.6 PEGASUS_ASSERT(pa != 0);
1687
1688 //
1689 // Forward the request to the provider agent
1690 //
1691 response.reset(pa->processMessage(request));
|
1692 kumpf 1.1 }
1693
1694 response->syncAttributes(request);
1695
1696 PEG_METHOD_EXIT();
1697 return response.release();
1698 }
1699
1700 ProviderAgentContainer* OOPProviderManagerRouter::_lookupProviderAgent(
|
1701 carolann.graves 1.25.2.5 const CIMInstance& providerModule,
1702 CIMRequestMessage* request)
|
1703 kumpf 1.1 {
|
1704 carolann.graves 1.25.2.5 // Retrieve the provider module name
1705 String moduleName;
1706 CIMValue nameValue = providerModule.getProperty(
1707 providerModule.findProperty("Name")).getValue();
1708 nameValue.get(moduleName);
1709
1710 // Retrieve the provider user context configuration
1711 Uint16 userContext = 0;
1712 Uint32 pos = providerModule.findProperty(
1713 PEGASUS_PROPERTYNAME_MODULE_USERCONTEXT);
1714 if (pos != PEG_NOT_FOUND)
1715 {
1716 CIMValue userContextValue =
1717 providerModule.getProperty(pos).getValue();
1718 if (!userContextValue.isNull())
1719 {
1720 userContextValue.get(userContext);
1721 }
1722 }
1723
1724 if (userContext == 0)
1725 carolann.graves 1.25.2.5 {
1726 userContext = PEGASUS_DEFAULT_PROV_USERCTXT;
1727 }
1728
1729 String userName;
1730
1731 if (userContext == PG_PROVMODULE_USERCTXT_REQUESTOR)
1732 {
1733 try
1734 {
1735 // User Name is in the OperationContext
1736 IdentityContainer ic = (IdentityContainer)
1737 request->operationContext.get(IdentityContainer::NAME);
1738 userName = ic.getUserName();
1739 }
1740 catch (Exception&)
1741 {
1742 // If no IdentityContainer is present, default to the CIM
1743 // Server's user context
1744 }
1745
1746 carolann.graves 1.25.2.5 // If authentication is disabled, use the CIM Server's user context
1747 if (!userName.size())
1748 {
1749 userName = System::getEffectiveUserName();
1750 }
1751 }
1752 else if (userContext == PG_PROVMODULE_USERCTXT_DESIGNATED)
1753 {
1754 // Retrieve the provider module designated user property value
1755 providerModule.getProperty(providerModule.findProperty(
1756 PEGASUS_PROPERTYNAME_MODULE_DESIGNATEDUSER)).getValue().
1757 get(userName);
1758 }
1759 else if (userContext == PG_PROVMODULE_USERCTXT_CIMSERVER)
1760 {
1761 userName = System::getEffectiveUserName();
1762 }
1763 else // Privileged User
1764 {
1765 PEGASUS_ASSERT(userContext == PG_PROVMODULE_USERCTXT_PRIVILEGED);
1766 userName = System::getPrivilegedUserName();
1767 carolann.graves 1.25.2.5 }
1768
1769 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL4,
1770 "Module name = " + moduleName);
1771 Tracer::trace(TRC_PROVIDERMANAGER, Tracer::LEVEL4,
1772 "User context = %hd.", userContext);
1773 PEG_TRACE_STRING(TRC_PROVIDERMANAGER, Tracer::LEVEL4,
1774 "User name = " + userName);
1775
|
1776 kumpf 1.1 ProviderAgentContainer* pa = 0;
|
1777 kumpf 1.6 String key = moduleName + ":" + userName;
|
1778 kumpf 1.1
1779 AutoMutex lock(_providerAgentTableMutex);
|
1780 kumpf 1.6 if (!_providerAgentTable.lookup(key, pa))
|
1781 kumpf 1.1 {
|
1782 kumpf 1.6 pa = new ProviderAgentContainer(
|
1783 carolann.graves 1.25.2.5 moduleName, userName, userContext,
1784 _indicationCallback, _responseChunkCallback,
1785 _providerModuleFailCallback,
|
1786 carolann.graves 1.14 _subscriptionInitComplete);
|
1787 kumpf 1.6 _providerAgentTable.insert(key, pa);
|
1788 kumpf 1.1 }
1789 return pa;
1790 }
1791
|
1792 kumpf 1.6 Array<ProviderAgentContainer*> OOPProviderManagerRouter::_lookupProviderAgents(
1793 const String& moduleName)
1794 {
1795 Array<ProviderAgentContainer*> paArray;
1796
1797 AutoMutex lock(_providerAgentTableMutex);
1798 for (ProviderAgentTable::Iterator i = _providerAgentTable.start(); i; i++)
1799 {
1800 if (i.value()->getModuleName() == moduleName)
1801 {
1802 paArray.append(i.value());
1803 }
1804 }
1805 return paArray;
1806 }
1807
|
1808 kumpf 1.1 CIMResponseMessage* OOPProviderManagerRouter::_forwardRequestToAllAgents(
1809 CIMRequestMessage* request)
1810 {
1811 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1812 "OOPProviderManagerRouter::_forwardRequestToAllAgents");
1813
1814 // Get a list of the ProviderAgentContainers. We need our own array copy
1815 // because we cannot hold the _providerAgentTableMutex while calling
1816 // _ProviderAgentContainer::processMessage().
1817 Array<ProviderAgentContainer*> paContainerArray;
1818 {
1819 AutoMutex tableLock(_providerAgentTableMutex);
1820 for (ProviderAgentTable::Iterator i = _providerAgentTable.start();
1821 i != 0; i++)
1822 {
1823 paContainerArray.append(i.value());
1824 }
1825 }
1826
1827 CIMException responseException;
1828
1829 kumpf 1.1 // Forward the request to each of the initialized provider agents
1830 for (Uint32 j = 0; j < paContainerArray.size(); j++)
1831 {
1832 ProviderAgentContainer* pa = paContainerArray[j];
1833 if (pa->isInitialized())
1834 {
1835 // Note: The ProviderAgentContainer could become uninitialized
1836 // before _ProviderAgentContainer::processMessage() processes
1837 // this request. In this case, the Provider Agent process will
1838 // (unfortunately) be started to process this message.
1839 AutoPtr<CIMResponseMessage> response;
1840 response.reset(pa->processMessage(request));
1841 if (response.get() != 0)
1842 {
1843 // If the operation failed, save the exception data
1844 if ((response->cimException.getCode() != CIM_ERR_SUCCESS) &&
1845 (responseException.getCode() == CIM_ERR_SUCCESS))
1846 {
1847 responseException = response->cimException;
1848 }
1849 }
1850 kumpf 1.1 }
1851 }
1852
1853 CIMResponseMessage* response = request->buildResponse();
1854 response->cimException = responseException;
1855
1856 PEG_METHOD_EXIT();
1857 return response;
1858 }
1859
1860 Boolean OOPProviderManagerRouter::hasActiveProviders()
1861 {
1862 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1863 "OOPProviderManagerRouter::hasActiveProviders");
1864
1865 // Iterate through the _providerAgentTable looking for initialized agents
1866 AutoMutex lock(_providerAgentTableMutex);
1867 ProviderAgentTable::Iterator i = _providerAgentTable.start();
1868 for(; i != 0; i++)
1869 {
1870 if (i.value()->isInitialized())
1871 kumpf 1.1 {
1872 PEG_METHOD_EXIT();
1873 return true;
1874 }
1875 }
1876
1877 // No initialized Provider Agents were found
1878 PEG_METHOD_EXIT();
1879 return false;
1880 }
1881
1882 void OOPProviderManagerRouter::unloadIdleProviders()
1883 {
1884 PEG_METHOD_ENTER(TRC_PROVIDERMANAGER,
1885 "OOPProviderManagerRouter::unloadIdleProviders");
1886
1887 // Iterate through the _providerAgentTable unloading idle providers
1888 AutoMutex lock(_providerAgentTableMutex);
1889 ProviderAgentTable::Iterator i = _providerAgentTable.start();
1890 for(; i != 0; i++)
1891 {
1892 kumpf 1.1 i.value()->unloadIdleProviders();
1893 }
1894
1895 PEG_METHOD_EXIT();
1896 }
1897
1898 PEGASUS_NAMESPACE_END
|