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

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

No CVS admin address has been configured
Powered by
ViewCVS 0.9.2