(file) Return to CIMListener.cpp CVS log (file) (dir) Up to [Pegasus] / pegasus / src / Pegasus / Listener

  1 karl  1.28 //%2005////////////////////////////////////////////////////////////////////////
  2 kumpf 1.1  //
  3 karl  1.25 // 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 karl  1.12 // IBM Corp.; EMC Corporation, The Open Group.
  7 karl  1.25 // 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.28 // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
 10            // EMC Corporation; VERITAS Software Corporation; The Open Group.
 11 kumpf 1.1  //
 12            // Permission is hereby granted, free of charge, to any person obtaining a copy
 13            // of this software and associated documentation files (the "Software"), to
 14            // deal in the Software without restriction, including without limitation the
 15            // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 16            // sell copies of the Software, and to permit persons to whom the Software is
 17            // furnished to do so, subject to the following conditions:
 18 david.dillard 1.31 //
 19 kumpf         1.1  // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
 20                    // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
 21                    // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 22                    // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 23                    // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24                    // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 25                    // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 26                    // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 27                    //
 28                    //==============================================================================
 29                    //
 30 tony          1.8  // Author: Dong Xiang, EMC Corporation (xiang_dong@emc.com)
 31 kumpf         1.1  //
 32 dj.gorey      1.14 // Modified By:   Dan Gorey (djgorey@us.ibm.com)
 33 a.arora       1.21 //                Amit K Arora, IBM (amita@in.ibm.com) for PEP#183
 34 kumpf         1.26 //                Roger Kumpf, Hewlett-Packard Company (roger_kumpf@hp.com)
 35 david.dillard 1.31 //                David Dillard, VERITAS Software Corp.
 36                    //                    (david.dillard@veritas.com)
 37 kumpf         1.1  //
 38                    //%/////////////////////////////////////////////////////////////////////////////
 39                    
 40 tony          1.8  #include "CIMListener.h"
 41 kumpf         1.1  
 42 tony          1.8  #include <Pegasus/Common/Exception.h>
 43                    #include <Pegasus/Common/SSLContext.h>
 44                    #include <Pegasus/Common/Monitor.h>
 45 kumpf         1.1  #include <Pegasus/Common/HTTPAcceptor.h>
 46 kumpf         1.11 #include <Pegasus/Common/PegasusVersion.h>
 47 kumpf         1.2  
 48 kumpf         1.1  #include <Pegasus/ExportServer/CIMExportResponseEncoder.h>
 49                    #include <Pegasus/ExportServer/CIMExportRequestDecoder.h>
 50                    
 51 tony          1.8  #include <Pegasus/Consumer/CIMIndicationConsumer.h>
 52                    #include <Pegasus/Listener/CIMListenerIndicationDispatcher.h>
 53                    
 54                    PEGASUS_NAMESPACE_BEGIN
 55                    /////////////////////////////////////////////////////////////////////////////
 56                    // CIMListenerService
 57                    /////////////////////////////////////////////////////////////////////////////
 58                    class CIMListenerService
 59                    {
 60                    public:
 61                    	CIMListenerService(Uint32 portNumber, SSLContext* sslContext=NULL);
 62                    	CIMListenerService(CIMListenerService& svc);
 63                      ~CIMListenerService();
 64                    
 65                    	void				init();
 66                    	/** bind to the port
 67                    	*/
 68                    	void				bind();
 69                    	/** runForever Main runloop for the server.
 70                    	*/
 71                    	void runForever();
 72 david.dillard 1.31 
 73 tony          1.8  	/** Call to gracefully shutdown the server.  The server connection socket
 74                    	will be closed to disable new connections from clients.
 75                    	*/
 76                    	void stopClientConnection();
 77 david.dillard 1.31 
 78 tony          1.8  	/** Call to gracefully shutdown the server.  It is called when the server
 79                    	has been stopped and is ready to be shutdown.  Next time runForever()
 80                    	is called, the server shuts down.
 81                    	*/
 82                    	void shutdown();
 83 david.dillard 1.31 
 84 tony          1.8  	/** Return true if the server has shutdown, false otherwise.
 85                    	*/
 86                    	Boolean terminated() { return _dieNow; };
 87 david.dillard 1.31 
 88 tony          1.8  	/** Call to resume the sever.
 89                    	*/
 90                    	void resume();
 91 david.dillard 1.31 
 92 tony          1.8  	/** Call to set the CIMServer state.  Also inform the appropriate
 93                    	message queues about the current state of the CIMServer.
 94                    	*/
 95                    	void setState(Uint32 state);
 96 david.dillard 1.31 
 97 tony          1.8  	Uint32 getOutstandingRequestCount();
 98                    
 99                    	/** Returns the indication listener dispatcher
100                    	 */
101                    	CIMListenerIndicationDispatcher* getIndicationDispatcher() const;
102                    
103                      /** Returns the indication listener dispatcher
104                    	 */
105                    	void setIndicationDispatcher(CIMListenerIndicationDispatcher* dispatcher);
106                    
107                    	static PEGASUS_THREAD_RETURN PEGASUS_THREAD_CDECL _listener_routine(void *param);
108                    
109                    private:
110                    	Uint32			_portNumber;
111                    	SSLContext* _sslContext;
112                    	Monitor*				_monitor;
113                      HTTPAcceptor*   _acceptor;
114 dj.gorey      1.14 
115                      Boolean					_dieNow;
116 tony          1.8  
117                      CIMListenerIndicationDispatcher* _dispatcher;
118                    
119                      CIMExportResponseEncoder* _responseEncoder;
120                      CIMExportRequestDecoder*  _requestDecoder;
121                    
122                    };
123                    
124                    CIMListenerService::CIMListenerService(Uint32 portNumber, SSLContext* sslContext)
125                    :_portNumber(portNumber)
126                    ,_sslContext(sslContext)
127                    ,_monitor(NULL)
128                    ,_acceptor(NULL)
129                    ,_dieNow(false)
130                    ,_dispatcher(NULL)
131                    ,_responseEncoder(NULL)
132                    ,_requestDecoder(NULL)
133                    {
134                    }
135                    
136                    CIMListenerService::CIMListenerService(CIMListenerService& svc)
137 tony          1.8  :_portNumber(svc._portNumber)
138                    ,_sslContext(svc._sslContext)
139                    ,_monitor(NULL)
140                    ,_acceptor(NULL)
141                    ,_dieNow(svc._dieNow)
142                    ,_dispatcher(NULL)
143                    ,_responseEncoder(NULL)
144                    ,_requestDecoder(NULL)
145                    {
146                    }
147                    CIMListenerService::~CIMListenerService()
148                    {
149                    	// if port is alive, clean up the port
150                    	//if(_sslContext!=NULL)
151                    	//	delete _sslContext;
152                    
153                    	if(_responseEncoder!=NULL)
154                    		delete _responseEncoder;
155                    
156                    	if(_requestDecoder!=NULL)
157                    		delete _requestDecoder;
158 kumpf         1.1  
159 tony          1.8  	//if(_dispatcher!=NULL)
160                    	//	delete _dispatcher;
161 kumpf         1.1  
162 tony          1.8  	if(_monitor!=NULL)
163                    		delete _monitor;
164 kumpf         1.1  
165 tony          1.8  	if(_acceptor!=NULL)
166                    		delete _acceptor;
167                    }
168 kumpf         1.1  
169 tony          1.8  void CIMListenerService::init()
170 kumpf         1.1  {
171 tony          1.8  	PEG_METHOD_ENTER(TRC_LISTENER, "CIMListenerService::init");
172 kumpf         1.1  
173 kumpf         1.27   _monitor = new Monitor();
174 david.dillard 1.31 
175 tony          1.8  	//_dispatcher = new CIMListenerIndicationDispatcher();
176 kumpf         1.1  
177 chuck         1.20   _responseEncoder = new CIMExportResponseEncoder();
178 tony          1.8    _requestDecoder = new CIMExportRequestDecoder(
179                    		_dispatcher,
180                    		_responseEncoder->getQueueId());
181                    
182 dj.gorey      1.14   _acceptor = new HTTPAcceptor(
183 david.dillard 1.31 		 _monitor,
184                    		 _requestDecoder,
185                    		 false,
186                    		 _portNumber,
187 kumpf         1.19 		 _sslContext,
188                                     false);
189 kumpf         1.1  
190 chuck         1.20   bind();
191 kumpf         1.1  
192 chuck         1.20   PEG_METHOD_EXIT();
193 tony          1.8  }
194                    void CIMListenerService::bind()
195                    {
196 chuck         1.20   if(_acceptor!=NULL)
197                        { // Bind to the port
198                          _acceptor->bind();
199 tony          1.8  
200 chuck         1.20       PEGASUS_STD(cout) << "Listening on HTTP port " << _portNumber << PEGASUS_STD(endl);
201 david.dillard 1.31 
202 chuck         1.20       //listener.addAcceptor(false, portNumberHttp, false);
203                          Logger::put(Logger::STANDARD_LOG, System::CIMLISTENER, Logger::INFORMATION,
204 tony          1.8                          "Listening on HTTP port $0.", _portNumber);
205 kumpf         1.1  
206 chuck         1.20     }
207 tony          1.8  }
208 chuck         1.20 
209 tony          1.8  void CIMListenerService::runForever()
210                    {
211 chuck         1.20   static int modulator = 0;
212 kumpf         1.1  
213 chuck         1.20   if(!_dieNow)
214 dj.gorey      1.14     {
215 david.dillard 1.31       if(false == _monitor->run(500000))
216                    	{
217 chuck         1.20 	  modulator++;
218 david.dillard 1.31       try
219 a.arora       1.21       {
220                    	     //MessageQueueService::_check_idle_flag = 1;
221                    		 //MessageQueueService::_polling_sem.signal();
222 kumpf         1.30 		 MessageQueueService::get_thread_pool()->cleanupIdleThreads();
223 a.arora       1.21       }
224                    	  catch(...)
225                          {
226                          }
227 chuck         1.20 	}
228                    /*
229                          if (handleShutdownSignal)
230                          {
231                            Tracer::trace(TRC_SERVER, Tracer::LEVEL3,
232                    	"CIMServer::runForever - signal received.  Shutting down.");
233                    
234                    	ShutdownService::getInstance(this)->shutdown(true, 10, false);
235                    	handleShutdownSignal = false;
236                          }
237 tony          1.8  */
238 chuck         1.20     }
239 tony          1.8  }
240 kumpf         1.1  
241 tony          1.8  void CIMListenerService::shutdown()
242                    {
243                        PEG_METHOD_ENTER(TRC_LISTENER, "CIMListenerService::shutdown()");
244 kumpf         1.1  
245 tony          1.8      _dieNow = true;
246 a.arora       1.21     _monitor->tickle();
247 kumpf         1.1  
248 kumpf         1.4      PEG_METHOD_EXIT();
249 kumpf         1.1  }
250                    
251 tony          1.8  void CIMListenerService::resume()
252 kumpf         1.1  {
253 tony          1.8      PEG_METHOD_ENTER(TRC_LISTENER, "CIMListenerService::resume()");
254 kumpf         1.1  
255 tony          1.8      if(_acceptor!=NULL)
256                            _acceptor->reopenConnectionSocket();
257 kumpf         1.1  
258 kumpf         1.4      PEG_METHOD_EXIT();
259 kumpf         1.1  }
260                    
261 tony          1.8  void CIMListenerService::stopClientConnection()
262 kumpf         1.1  {
263 tony          1.8      PEG_METHOD_ENTER(TRC_LISTENER, "CIMListenerService::stopClientConnection()");
264 kumpf         1.1  
265 kumpf         1.10     // tell Monitor to stop listening for client connections
266 chuck         1.22     _monitor->stopListeningForConnections(true);
267 kumpf         1.10 
268                        //
269                        // Wait 150 milliseconds to allow time for the Monitor to stop
270                        // listening for client connections.
271                        //
272                        // This wait time is the timeout value for the select() call
273                        // in the Monitor's run() method (currently set to 100
274                        // milliseconds) plus a delta of 50 milliseconds.  The reason
275                        // for the wait here is to make sure that the Monitor entries
276                        // are updated before closing the connection sockets.
277                        //
278 a.arora       1.21     // pegasus_sleep(150); Not needed now due to the semaphore in the Monitor
279 david.dillard 1.31 
280 tony          1.8      if(_acceptor!=NULL)
281                        _acceptor->closeConnectionSocket();
282 kumpf         1.1  
283 kumpf         1.4      PEG_METHOD_EXIT();
284 kumpf         1.1  }
285                    
286 chuck         1.23 Uint32 CIMListenerService::getOutstandingRequestCount()
287                    {
288                        return _acceptor->getOutstandingRequestCount();
289                    }
290 tony          1.8  
291                    CIMListenerIndicationDispatcher* CIMListenerService::getIndicationDispatcher() const
292 kumpf         1.1  {
293 david.dillard 1.32     return _dispatcher;
294 tony          1.8  }
295                    void CIMListenerService::setIndicationDispatcher(CIMListenerIndicationDispatcher* dispatcher)
296                    {
297 david.dillard 1.32     _dispatcher = dispatcher;
298 kumpf         1.1  }
299                    
300 tony          1.8  PEGASUS_THREAD_RETURN PEGASUS_THREAD_CDECL CIMListenerService::_listener_routine(void *param)
301 kumpf         1.1  {
302 david.dillard 1.32     AutoPtr<CIMListenerService> svc(reinterpret_cast<CIMListenerService *>(param));
303 kumpf         1.1  
304 david.dillard 1.32     //svc->init(); bug 1394
305                        while(!svc->terminated())
306                        {
307 chuck         1.20 #if defined(PEGASUS_PLATFORM_DARWIN_PPC_GNU)
308 david.dillard 1.32         pthread_testcancel();
309 chuck         1.20 #endif
310 david.dillard 1.32         svc->runForever();
311                        }
312 tony          1.8  
313 david.dillard 1.32     return 0;
314 tony          1.8  }
315                    
316                    /////////////////////////////////////////////////////////////////////////////
317                    // CIMListenerRep
318                    /////////////////////////////////////////////////////////////////////////////
319                    class CIMListenerRep
320                    {
321                    public:
322                    	CIMListenerRep(Uint32 portNumber, SSLContext* sslContext=NULL);
323                      ~CIMListenerRep();
324                    
325                    	Uint32 getPortNumber() const;
326                    
327                    	SSLContext* getSSLContext() const;
328                    	void setSSLContext(SSLContext* sslContext);
329 david.dillard 1.31 
330 tony          1.8  	void start();
331                    	void stop();
332                    
333                    	Boolean isAlive();
334                    
335                    	Boolean addConsumer(CIMIndicationConsumer* consumer);
336                    	Boolean removeConsumer(CIMIndicationConsumer* consumer);
337                    
338                    private:
339 chuck         1.23   Boolean waitForPendingRequests(Uint32 shutdownTimeout);
340                    
341                      Uint32 _portNumber;
342                      SSLContext* _sslContext;
343 tony          1.8  
344                      CIMListenerIndicationDispatcher* _dispatcher;
345 chuck         1.23   ThreadPool* _thread_pool;
346 david.dillard 1.31   CIMListenerService* _svc;
347 chuck         1.20   Semaphore *_listener_sem;
348 tony          1.8  };
349                    
350                    CIMListenerRep::CIMListenerRep(Uint32 portNumber, SSLContext* sslContext)
351                    :_portNumber(portNumber)
352                    ,_sslContext(sslContext)
353                    ,_dispatcher(new CIMListenerIndicationDispatcher())
354                    ,_thread_pool(NULL)
355 chuck         1.20 ,_svc(NULL)
356                    ,_listener_sem(NULL)
357 tony          1.8  {
358                    }
359 david.dillard 1.31 
360 tony          1.8  CIMListenerRep::~CIMListenerRep()
361                    {
362 david.dillard 1.31     // if port is alive, clean up the port
363                        if (_thread_pool != 0)
364                        {
365                            // Block incoming export requests and unbind the port
366                            _svc->stopClientConnection();
367                    
368                            // Wait until pending export requests in the server are done.
369                            waitForPendingRequests(10);
370 chuck         1.23 
371 david.dillard 1.31         // Shutdown the CIMListenerService
372                            _svc->shutdown();
373                        }
374 chuck         1.20 
375                        delete _sslContext;
376                        delete _dispatcher;
377                        delete _thread_pool;
378                        delete _listener_sem;
379 tony          1.8  
380 chuck         1.20   // don't delete _svc, this is deleted by _listener_routine
381 tony          1.8  }
382                    
383                    Uint32 CIMListenerRep::getPortNumber() const
384                    {
385 david.dillard 1.31     return _portNumber;
386 tony          1.8  }
387 kumpf         1.1  
388 tony          1.8  SSLContext* CIMListenerRep::getSSLContext() const
389                    {
390 david.dillard 1.31     return _sslContext;
391 kumpf         1.1  }
392 david.dillard 1.31 
393 tony          1.8  void CIMListenerRep::setSSLContext(SSLContext* sslContext)
394                    {
395 david.dillard 1.31     delete _sslContext;
396                        _sslContext = sslContext;
397                    }
398 kumpf         1.1  
399 tony          1.8  void CIMListenerRep::start()
400 kumpf         1.1  {
401 david.dillard 1.31     // spawn a thread to do this
402                        if(_thread_pool==0)
403 chuck         1.20     {
404 david.dillard 1.31         AutoPtr<CIMListenerService> svc(new CIMListenerService(_portNumber,_sslContext));
405 chuck         1.20 
406 david.dillard 1.31         svc->setIndicationDispatcher(_dispatcher);
407                            svc->init();
408 chuck         1.20 
409 david.dillard 1.31         struct timeval deallocateWait = {15, 0};
410                            AutoPtr<ThreadPool> threadPool(new ThreadPool(0, "Listener", 0, 1, deallocateWait));
411                            AutoPtr<Semaphore> sem(new Semaphore(0));
412                            threadPool->allocate_and_awaken(svc.get(), CIMListenerService::_listener_routine, sem.get());
413                            Logger::put(Logger::STANDARD_LOG,System::CIMLISTENER, Logger::INFORMATION,
414                                            "CIMListener started");
415                    
416                            PEGASUS_STD(cerr) << "CIMlistener started" << PEGASUS_STD(endl);
417                    
418                            _svc = svc.release();
419                            _thread_pool = threadPool.release();
420                            _listener_sem = sem.release();
421                        }
422 tony          1.8  }
423 kumpf         1.1  
424 tony          1.8  void CIMListenerRep::stop()
425                    {
426 chuck         1.20   if(_thread_pool!=NULL)
427 david.dillard 1.31   {
428 chuck         1.20     //
429                        // Graceful shutdown of the listener service
430                        //
431                    
432                        // Block incoming export requests and unbind the port
433                        _svc->stopClientConnection();
434 chuck         1.23 
435                        // Wait until pending export requests in the server are done.
436 chuck         1.24     waitForPendingRequests(10);
437 david.dillard 1.31 
438 chuck         1.20     // Shutdown the CIMListenerService
439                        _svc->shutdown();
440                    
441                        // Wait for the _listener_routine thread to exit.
442                        // The thread could be delivering an export, so give it 3sec.
443                        // Note that _listener_routine deletes the CIMListenerService,
444                        // so no need to delete _svc.
445                        try
446                        {
447 david.dillard 1.31       _listener_sem->time_wait(3000);
448 chuck         1.20     }
449                        catch (TimeOut &)
450                        {
451                          // No need to do anything, the thread pool will be deleted below
452                          // to cancel the _listener_routine thread if it is still running.
453                        }
454                    
455                        delete _listener_sem;
456                        _listener_sem = NULL;
457 david.dillard 1.31 
458 chuck         1.20     // Delete the thread pool.  This cancels the listener thread if it is still
459                        // running.
460                        delete _thread_pool;
461                        _thread_pool = NULL;
462                    
463                        Logger::put(Logger::STANDARD_LOG,System::CIMLISTENER,
464                    		Logger::INFORMATION,
465                    		"CIMListener stopped");
466                      }
467 kumpf         1.1  }
468                    
469 tony          1.8  Boolean CIMListenerRep::isAlive()
470 kumpf         1.1  {
471 tony          1.8  	return (_thread_pool!=NULL)?true:false;
472                    }
473 kumpf         1.1  
474 tony          1.8  Boolean CIMListenerRep::addConsumer(CIMIndicationConsumer* consumer)
475                    {
476                    	return _dispatcher->addConsumer(consumer);
477                    }
478                    Boolean CIMListenerRep::removeConsumer(CIMIndicationConsumer* consumer)
479                    {
480                    	return _dispatcher->removeConsumer(consumer);
481                    }
482 kumpf         1.1  
483 chuck         1.23 Boolean CIMListenerRep::waitForPendingRequests(Uint32 shutdownTimeout)
484                    {
485                      // Wait for 10 sec max
486                      Uint32 reqCount;
487                      Uint32 countDown = shutdownTimeout * 10;
488                      for (; countDown > 0; countDown--)
489                      {
490                        reqCount = _svc->getOutstandingRequestCount();
491                        if (reqCount > 0)
492                          pegasus_sleep(100);
493                        else
494                          return true;
495                      }
496 david.dillard 1.31 
497 chuck         1.23   return false;
498 david.dillard 1.31 }
499 chuck         1.23 
500 tony          1.8  /////////////////////////////////////////////////////////////////////////////
501                    // CIMListener
502                    /////////////////////////////////////////////////////////////////////////////
503                    CIMListener::CIMListener(Uint32 portNumber, SSLContext* sslContext)
504                    :_rep(new CIMListenerRep(portNumber,sslContext))
505                    {
506                    }
507                    CIMListener::~CIMListener()
508                    {
509                    	if(_rep!=NULL)
510 tony          1.9  		delete static_cast<CIMListenerRep*>(_rep);
511 tony          1.8  	_rep=NULL;
512                    }
513 david.dillard 1.31 
514 tony          1.8  Uint32 CIMListener::getPortNumber() const
515                    {
516                    	return static_cast<CIMListenerRep*>(_rep)->getPortNumber();
517 kumpf         1.1  }
518                    
519 tony          1.8  SSLContext* CIMListener::getSSLContext() const
520                    {
521                    	return static_cast<CIMListenerRep*>(_rep)->getSSLContext();
522                    }
523                    void CIMListener::setSSLContext(SSLContext* sslContext)
524                    {
525                    	static_cast<CIMListenerRep*>(_rep)->setSSLContext(sslContext);
526                    }
527                    void CIMListener::start()
528 kumpf         1.1  {
529 tony          1.8  	static_cast<CIMListenerRep*>(_rep)->start();
530                    }
531                    void CIMListener::stop()
532                    {
533                    	static_cast<CIMListenerRep*>(_rep)->stop();
534                    }
535 kumpf         1.1  
536 tony          1.8  Boolean CIMListener::isAlive()
537                    {
538                    	return static_cast<CIMListenerRep*>(_rep)->isAlive();
539                    }
540 kumpf         1.1  
541 tony          1.8  Boolean CIMListener::addConsumer(CIMIndicationConsumer* consumer)
542                    {
543                    	return static_cast<CIMListenerRep*>(_rep)->addConsumer(consumer);
544                    }
545                    Boolean CIMListener::removeConsumer(CIMIndicationConsumer* consumer)
546                    {
547                    	return static_cast<CIMListenerRep*>(_rep)->removeConsumer(consumer);
548 kumpf         1.1  }
549                    
550                    PEGASUS_NAMESPACE_END

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2