Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3488 lines
87 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name :
  4. iissvc.cxx
  5. Abstract:
  6. Defines the IIS_SERVICE class
  7. FILE HISTORY:
  8. MuraliK 15-Nov-1994 Created.
  9. CezaryM 11-May-2000 Added events:
  10. started/stopped/paused/resumed
  11. --*/
  12. #include "tcpdllp.hxx"
  13. #include <rpc.h>
  14. #include <tsunami.hxx>
  15. #include <iistypes.hxx>
  16. #include <iisendp.hxx>
  17. #include "inetreg.h"
  18. #include "tcpcons.h"
  19. #include "apiutil.h"
  20. #include <imd.h>
  21. #include <ole2.h>
  22. #include <inetsvcs.h>
  23. #include <issched.hxx>
  24. #include "reftrce2.h"
  25. /************************************************************
  26. * Symbolic Constants
  27. ************************************************************/
  28. //
  29. // What we assume to be the last winsock error
  30. //
  31. #define WSA_MAX_ERROR (WSABASEERR + 3000)
  32. //
  33. // For socket errors, we return the numeric socket error
  34. //
  35. #define SOCK_ERROR_STR_W L"Socket error %d"
  36. #define SOCK_ERROR_STR_A "Socket error %d"
  37. #define LM_PREFIX "/" IIS_MD_LOCAL_MACHINE_PATH "/"
  38. #define LM_PREFIX_CCH sizeof(LM_PREFIX) - sizeof(CHAR)
  39. //
  40. // The time indicating how long it will take for IIS to start up a service
  41. // <-- Service controller will wait for this duration before telling user
  42. // that there is some problem.
  43. // For PDC 1996, a hacked value of 90 seconds used.
  44. // The new value of 30 seconds is plugged in on 7/7/97
  45. //
  46. # define IIS_SERVICE_START_WAIT_HINT_SECONDS (30) // 30 seconds
  47. # define IIS_SERVICE_START_WAIT_HINT (IIS_SERVICE_START_WAIT_HINT_SECONDS * 1000) // 30 seconds
  48. # define IIS_SERVICE_START_WAIT_HINT_EXTENDED (IIS_SERVICE_START_WAIT_HINT * 4) // 2 minutes
  49. # define IIS_SERVICE_START_INDICATOR_INTERVAL (IIS_SERVICE_START_WAIT_HINT_EXTENDED / 2) // 1 minute
  50. # define IIS_SERVICE_START_INDICATOR_INTERVAL_SECONDS (IIS_SERVICE_START_INDICATOR_INTERVAL / 1000)
  51. # define MAX_NUMBER_OF_START_HINT_REPETITIONS 200 // 50 minutes
  52. //
  53. // MS_SERVICE_SHUTDOWN_INDICATOR_TIME_INTERVAL
  54. // - specifies the time interval in milli-seconds for the interval
  55. // to notify the service controller that a service is shutting down.
  56. //
  57. # define MS_SERVICE_SHUTDOWN_INDICATOR_TIME_INTERVAL \
  58. (SERVICE_STOP_WAIT_HINT/2)
  59. #ifdef _KNFOCOMM
  60. //
  61. // List of "known" services that use knfocomm -
  62. // This is needed to break deadlocks between infocomm & knfocomm..
  63. //
  64. static char* rgKnfoServices[] = {
  65. TEXT("pop3svc"),
  66. TEXT("imap4svc")
  67. };
  68. static DWORD gNumKnfoServices = 3;
  69. #endif // _KNFOCOMM
  70. //
  71. // Deferred metabase change notify
  72. //
  73. VOID
  74. WINAPI
  75. DeferredMDChange(
  76. PVOID pv
  77. );
  78. BOOL
  79. I_StopInstanceEndpoint( PVOID pvContext1,
  80. PVOID pvContext2,
  81. IIS_SERVER_INSTANCE * pInstance );
  82. VOID
  83. WINAPI
  84. ServiceShutdownIndicator( VOID * pSvcContext);
  85. //
  86. // Critical section used for locking the list of IIS_SERVICE objects
  87. // during insertion and deletion
  88. //
  89. CRITICAL_SECTION IIS_SERVICE::sm_csLock;
  90. LIST_ENTRY IIS_SERVICE::sm_ServiceInfoListHead;
  91. BOOL IIS_SERVICE::sm_fInitialized = FALSE;
  92. PISRPC IIS_SERVICE::sm_isrpc = NULL;
  93. IUnknown * IIS_SERVICE::sm_MDObject = NULL;
  94. IUnknown * IIS_SERVICE::sm_MDNseObject = NULL;
  95. #if SERVICE_REF_TRACKING
  96. //
  97. // Ref count trace log size
  98. //
  99. #define C_SERVICE_REFTRACES 400
  100. #define C_LOCAL_SERVICE_REFTRACES 40
  101. #endif // SERVICE_REF_TRACKING
  102. //
  103. PTRACE_LOG IIS_SERVICE::sm_pDbgRefTraceLog = NULL;
  104. /************************************************************
  105. * Functions
  106. ************************************************************/
  107. DWORD
  108. InitMetadataDCom(
  109. PVOID Context,
  110. PVOID NseContext
  111. );
  112. //
  113. // LOCAL Functions
  114. //
  115. extern MIME_MAP * g_pMimeMap;
  116. #define MAX_ADDRESSES_SUPPORTED 20
  117. #define SIZEOF_IP_SEC_LIST( IPList ) (sizeof(INET_INFO_IP_SEC_LIST) + \
  118. (IPList)->cEntries * \
  119. sizeof(INET_INFO_IP_SEC_ENTRY))
  120. /************************************************************
  121. * Functions
  122. ************************************************************/
  123. //
  124. // These 2 functions cannot be inline as they reference sm_csLock
  125. // which is a static non-exported member of IIS_SERVICE
  126. // Having them inline causes build to break when compiled with /Od
  127. //
  128. VOID
  129. IIS_SERVICE::AcquireGlobalLock( )
  130. {
  131. EnterCriticalSection(&sm_csLock);
  132. }
  133. VOID
  134. IIS_SERVICE::ReleaseGlobalLock( )
  135. {
  136. LeaveCriticalSection(&sm_csLock);
  137. }
  138. IIS_SERVICE::IIS_SERVICE(
  139. IN LPCSTR pszServiceName,
  140. IN LPCSTR pszModuleName,
  141. IN LPCSTR pszRegParamKey,
  142. IN DWORD dwServiceId,
  143. IN ULONGLONG SvcLocId,
  144. IN BOOL MultipleInstanceSupport,
  145. IN DWORD cbAcceptExRecvBuffer,
  146. IN ATQ_CONNECT_CALLBACK pfnConnect,
  147. IN ATQ_COMPLETION pfnConnectEx,
  148. IN ATQ_COMPLETION pfnIoCompletion
  149. )
  150. /*++
  151. Description:
  152. Contructor for IIS_SERVICE class.
  153. This constructs a new service info object for the service specified.
  154. Arguments:
  155. pszServiceName - name of the service to be created.
  156. pszModuleName - name of the module for loading string resources.
  157. pszRegParamKey - fully qualified name of the registry key that
  158. contains the common service data for this server
  159. dwServiceId - DWORD containing the bitflag id for service.
  160. SvcLocId - Service locator id
  161. MultipleInstanceSupport - Does this service support multiple instances
  162. cbAcceptExRecvBuffer, pfnConnect, pfnConnectEx, pfnIoCompletion
  163. - parameters for ATQ Endpoint
  164. On success it initializes all the members of the object,
  165. inserts itself to the global list of service info objects and
  166. returns with success.
  167. Note:
  168. The caller of this function should check the validity by
  169. invoking the member function IsValid() after constructing
  170. this object.
  171. --*/
  172. :
  173. m_state ( BlockStateInvalid), // state is invalid at start
  174. m_reference ( 1),
  175. m_pDbgRefTraceLog ( NULL),
  176. m_nInstance ( 0),
  177. m_nStartedInstances ( 0),
  178. m_maxInstanceId ( 1),
  179. m_fIpcStarted ( FALSE ),
  180. m_fSvcLocationDone ( FALSE ),
  181. m_fSocketsInitialized ( FALSE ),
  182. m_fEnableSvcLocation ( INETA_DEF_ENABLE_SVC_LOCATION ),
  183. m_fMultiInstance ( MultipleInstanceSupport ),
  184. m_SvcLocId ( SvcLocId ),
  185. m_dwServiceId ( dwServiceId),
  186. m_strServiceName ( pszServiceName),
  187. m_strServiceComment ( ),
  188. m_strParametersKey ( pszRegParamKey),
  189. m_hsvcStatus ( NULL_SERVICE_STATUS_HANDLE),
  190. m_pMimeMap ( g_pMimeMap),
  191. m_strModuleName ( pszModuleName),
  192. m_hModule ( NULL),
  193. m_dwDownlevelInstance ( 1),
  194. m_EventLog ( pszServiceName ),
  195. m_fIsDBCS ( FALSE ),
  196. m_hShutdownEvent ( NULL),
  197. m_dwShutdownScheduleId( 0),
  198. m_nShutdownIndicatorCalls (0),
  199. m_dwStartUpIndicatorCalls (0),
  200. m_dwClientStartActivityIndicator (0),
  201. m_dwNextSCMUpdateTime ( 0),
  202. m_hPendingShutdownEvent (NULL),
  203. //
  204. // Initialize ATQ callbacks
  205. //
  206. m_pfnConnect ( pfnConnect),
  207. m_pfnConnectEx ( pfnConnectEx),
  208. m_pfnIoCompletion ( pfnIoCompletion),
  209. m_cbRecvBuffer ( cbAcceptExRecvBuffer)
  210. {
  211. MB mb( (IMDCOM*) QueryMDObject() );
  212. DWORD errInit = NO_ERROR;
  213. //
  214. // Initialize endpoint list
  215. //
  216. InitializeListHead( &m_EndpointListHead );
  217. InitializeListHead( &m_InstanceListHead );
  218. INITIALIZE_CRITICAL_SECTION( &m_lock );
  219. #if SERVICE_REF_TRACKING
  220. m_pDbgRefTraceLog = CreateRefTraceLog(C_LOCAL_SERVICE_REFTRACES, 0);
  221. #endif // SERVICE_REF_TRACKING
  222. //
  223. // Initialize the service metapath
  224. //
  225. strcpy( m_achServiceMetaPath, "/" IIS_MD_LOCAL_MACHINE_PATH "/" );
  226. strcat( m_achServiceMetaPath, QueryServiceName() );
  227. strcat( m_achServiceMetaPath, "/" );
  228. DBG_ASSERT( strlen(m_achServiceMetaPath) < sizeof(m_achServiceMetaPath) );
  229. //
  230. // Read the downlevel instance
  231. //
  232. if ( mb.Open( QueryMDPath() ) )
  233. {
  234. mb.GetDword( "",
  235. MD_DOWNLEVEL_ADMIN_INSTANCE,
  236. IIS_MD_UT_SERVER,
  237. 0xffffffff, // default value
  238. &m_dwDownlevelInstance
  239. );
  240. mb.Close( );
  241. }
  242. else
  243. {
  244. errInit = GetLastError();
  245. }
  246. //
  247. // Is this a DBCS locale?
  248. //
  249. WORD wPrimaryLangID = PRIMARYLANGID( GetSystemDefaultLangID() );
  250. m_fIsDBCS = ((wPrimaryLangID == LANG_JAPANESE) ||
  251. (wPrimaryLangID == LANG_CHINESE) ||
  252. (wPrimaryLangID == LANG_KOREAN) );
  253. if ( !m_EventLog.Success() ) {
  254. DBGPRINTF(( DBG_CONTEXT,
  255. " Eventlog not initialized\n"));
  256. if ( GetLastError() != ERROR_ACCESS_DENIED )
  257. {
  258. DBG_ASSERT( m_state != BlockStateActive);
  259. errInit = GetLastError();
  260. //
  261. // Skip anything else that might fail since we don't have an
  262. // event log object
  263. //
  264. goto Exit;
  265. }
  266. }
  267. //
  268. // If we failed to open the service path in the metabase above, bail
  269. // out of the initialization
  270. //
  271. if ( errInit )
  272. {
  273. const CHAR * apsz[1];
  274. apsz[0] = QueryMDPath();
  275. LogEvent( INET_SVC_INVALID_MB_PATH,
  276. 1,
  277. (const CHAR **) apsz,
  278. errInit );
  279. goto Exit;
  280. }
  281. //
  282. // Get module name
  283. //
  284. m_hModule = GetModuleHandle( pszModuleName);
  285. if ( m_hModule == NULL ) {
  286. CHAR * apsz[1];
  287. errInit = GetLastError();
  288. apsz[0] = (PCHAR)pszModuleName;
  289. m_EventLog.LogEvent( INET_SVC_GET_MODULE_FAILED,
  290. 1,
  291. (const CHAR**)apsz,
  292. errInit );
  293. DBG_ASSERT( m_state != BlockStateActive);
  294. goto Exit;
  295. }
  296. //
  297. // Init others
  298. //
  299. if ( MultipleInstanceSupport ) {
  300. m_strServiceComment.Copy(DEF_MULTI_SERVER_COMMENT_A);
  301. }
  302. //
  303. // Create pending shutdown event
  304. //
  305. m_hPendingShutdownEvent = CreateEvent( NULL,
  306. FALSE,
  307. FALSE,
  308. NULL );
  309. if ( m_hPendingShutdownEvent == NULL )
  310. {
  311. errInit = GetLastError();
  312. goto Exit;
  313. }
  314. Exit:
  315. //
  316. // Add ourself to the list - Note we must always get on this list as the
  317. // destructor assumes this.
  318. //
  319. AcquireGlobalLock( );
  320. InsertHeadList( & sm_ServiceInfoListHead, &m_ServiceListEntry );
  321. if ( errInit == NO_ERROR )
  322. {
  323. //
  324. // put service information into metabase
  325. //
  326. AdvertiseServiceInformationInMB( );
  327. //
  328. // we're on. now set the state to be active!
  329. //
  330. m_state = BlockStateActive;
  331. }
  332. ReleaseGlobalLock( );
  333. //
  334. // Initialize the service status structure.
  335. //
  336. m_svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  337. m_svcStatus.dwCurrentState = SERVICE_STOPPED;
  338. m_svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
  339. | SERVICE_ACCEPT_PAUSE_CONTINUE
  340. | SERVICE_ACCEPT_SHUTDOWN;
  341. m_svcStatus.dwWin32ExitCode = errInit;
  342. m_svcStatus.dwServiceSpecificExitCode = errInit;
  343. m_svcStatus.dwCheckPoint = 0;
  344. m_svcStatus.dwWaitHint = 0;
  345. return;
  346. } // IIS_SERVICE::IIS_SERVICE()
  347. IIS_SERVICE::~IIS_SERVICE( VOID)
  348. /*++
  349. Description:
  350. Cleanup the TsvcInfo object. If the service is not already
  351. terminated, it terminates the service before cleanup.
  352. Arguments:
  353. None
  354. Returns:
  355. None
  356. --*/
  357. {
  358. IF_DEBUG(INSTANCE) {
  359. DBGPRINTF((DBG_CONTEXT,"~IIS_SERVICE: nRef %d nInstances %d\n",
  360. m_reference, m_nInstance));
  361. }
  362. DBG_ASSERT( m_reference == 0 );
  363. DBG_ASSERT( IsListEmpty(&m_InstanceListHead) );
  364. DBG_ASSERT( IsListEmpty(&m_EndpointListHead) );
  365. DBG_ASSERT( m_dwShutdownScheduleId == 0);
  366. if ( m_hShutdownEvent != NULL ) {
  367. DBG_REQUIRE(CloseHandle(m_hShutdownEvent));
  368. m_hShutdownEvent = NULL;
  369. }
  370. if ( m_hPendingShutdownEvent != NULL )
  371. {
  372. DBG_REQUIRE( CloseHandle( m_hPendingShutdownEvent ) );
  373. m_hPendingShutdownEvent = NULL;
  374. }
  375. //
  376. // remove from global list
  377. //
  378. AcquireGlobalLock( );
  379. RemoveEntryList( &m_ServiceListEntry );
  380. ReleaseGlobalLock( );
  381. #if SERVICE_REF_TRACKING
  382. DestroyRefTraceLog( m_pDbgRefTraceLog );
  383. #endif // SERVICE_REF_TRACKING
  384. DeleteCriticalSection( &m_lock );
  385. } // IIS_SERVICE::~IIS_SERVICE()
  386. DWORD
  387. IIS_SERVICE::StartServiceOperation(
  388. IN PFN_SERVICE_CTRL_HANDLER pfnCtrlHandler,
  389. IN PFN_SERVICE_SPECIFIC_INITIALIZE pfnInitialize,
  390. IN PFN_SERVICE_SPECIFIC_CLEANUP pfnCleanup
  391. )
  392. /*++
  393. Description:
  394. Starts the operation of service instantiated in the given
  395. Service Info Object.
  396. Arguments:
  397. pfnCtrlHandler
  398. pointer to a callback function for handling dispatch of
  399. service controller requests. A separate function is required
  400. since Service Controller call back function does not send
  401. context information.
  402. pfnInitialize
  403. pointer to a callback function implemented by the service DLL;
  404. the callback is responsible for all ServiceSpecific initializations
  405. pfnCleanup
  406. pointer to a callback function implemented by the service DLL;
  407. the callback is responsible for all ServiceSpecific Cleanups
  408. Returns:
  409. NO_ERROR on success and Win32 error code if any failure.
  410. --*/
  411. {
  412. DWORD err;
  413. DWORD cbBuffer;
  414. BOOL fInitCalled = FALSE;
  415. DBG_ASSERT((pfnInitialize != NULL) && (pfnCleanup != NULL));
  416. if ( !IsActive()) {
  417. //
  418. // Not successfully initialized.
  419. //
  420. DBGPRINTF((DBG_CONTEXT,
  421. "Service not ready. Failing StartServiceOperation\n"));
  422. return ( ERROR_NOT_READY );
  423. }
  424. //
  425. // Create shutdown event.
  426. //
  427. DBG_ASSERT(m_hShutdownEvent == NULL);
  428. m_hShutdownEvent = CreateEvent( NULL, // lpsaSecurity
  429. TRUE, // fManualReset
  430. FALSE, // fInitialState
  431. NULL
  432. );
  433. if( m_hShutdownEvent == NULL ) {
  434. err = GetLastError();
  435. DBGPRINTF(( DBG_CONTEXT,
  436. "InitializeService(): Cannot create shutdown event,"
  437. " error %lu\n", err ));
  438. goto Cleanup;
  439. }
  440. m_hsvcStatus = RegisterServiceCtrlHandler(
  441. QueryServiceName(),
  442. pfnCtrlHandler
  443. );
  444. //
  445. // Register the Control Handler routine.
  446. //
  447. if( m_hsvcStatus == NULL_SERVICE_STATUS_HANDLE ) {
  448. err = GetLastError();
  449. DBGPRINTF( ( DBG_CONTEXT,
  450. "cannot connect to register ctrl handler, error %lu\n",
  451. err )
  452. );
  453. goto Cleanup;
  454. }
  455. //
  456. // Indicate to the service that we are starting up,
  457. // Update the service status.
  458. //
  459. err = UpdateServiceStatus( SERVICE_START_PENDING,
  460. NO_ERROR,
  461. 1,
  462. IIS_SERVICE_START_WAIT_HINT
  463. );
  464. if( err != NO_ERROR ) {
  465. DBGPRINTF( ( DBG_CONTEXT,
  466. "StartServiceOperation(): cannot update service status,"
  467. " error %lu\n",
  468. err )
  469. );
  470. goto Cleanup;
  471. }
  472. //
  473. // Initialize the various service specific components.
  474. //
  475. m_dwNextSCMUpdateTime = GetCurrentTimeInSeconds() + IIS_SERVICE_START_WAIT_HINT_SECONDS / 2;
  476. m_dwStartUpIndicatorCalls = 0;
  477. m_dwClientStartActivityIndicator = 1;
  478. if ( pfnInitialize != NULL ) {
  479. err = ( *pfnInitialize)( this);
  480. fInitCalled = TRUE;
  481. if( err != NO_ERROR ) {
  482. DBGPRINTF( ( DBG_CONTEXT,
  483. " Initialization of service failed with %d\n",
  484. err));
  485. goto Cleanup;
  486. }
  487. }
  488. //
  489. // We are done with all initializatios, Update the service status.
  490. //
  491. err = UpdateServiceStatus( SERVICE_RUNNING,
  492. NO_ERROR,
  493. 0,
  494. 0 );
  495. if( err != NO_ERROR ) {
  496. DBGPRINTF( ( DBG_CONTEXT, "cannot update service status, error %lu\n",
  497. err )
  498. );
  499. goto Cleanup;
  500. }
  501. //
  502. // Wait for the shutdown event.
  503. //
  504. DBGPRINTF( ( DBG_CONTEXT,
  505. "IIS_SERVICE(%08p) %s - Waiting for ShutDown Event ...\n",
  506. this, QueryServiceName()
  507. ));
  508. #if 0
  509. err = WaitForSingleObject( m_hShutdownEvent,
  510. INFINITE );
  511. if ( err != WAIT_OBJECT_0) {
  512. //
  513. // Error. Unable to wait for single object.
  514. //
  515. DBGPRINTF( ( DBG_CONTEXT,
  516. "Wait for single object failed with Error %lu\n",
  517. err )
  518. );
  519. }
  520. #else
  521. while ( TRUE ) {
  522. MSG msg;
  523. //
  524. // Need to do MsgWait instead of WaitForSingleObject
  525. // to process windows msgs. We now have a window
  526. // because of COM.
  527. //
  528. err = MsgWaitForMultipleObjects( 1,
  529. &m_hShutdownEvent,
  530. FALSE,
  531. INFINITE,
  532. QS_ALLINPUT );
  533. if ( err == WAIT_OBJECT_0 ) {
  534. break;
  535. }
  536. while ( PeekMessage( &msg,
  537. NULL,
  538. 0,
  539. 0,
  540. PM_REMOVE ))
  541. {
  542. DispatchMessage( &msg );
  543. }
  544. }
  545. #endif
  546. err = NO_ERROR;
  547. //
  548. // Stop time. Tell the Service Controller that we're stopping,
  549. // then terminate the various service components.
  550. //
  551. UpdateServiceStatus( SERVICE_STOP_PENDING,
  552. 0,
  553. 1,
  554. SERVICE_STOP_WAIT_HINT );
  555. Cleanup:
  556. if ( fInitCalled && (pfnCleanup != NULL) ) {
  557. //
  558. // 1. Register a scheduled work item for periodic update to the
  559. // Service Controller while shutdown is happening in this thread
  560. // (Reason: Shutdown takes far longer time
  561. // than SERVICE_STOP_WAIT_HINT)
  562. //
  563. m_nShutdownIndicatorCalls = 0;
  564. DBG_ASSERT( m_dwShutdownScheduleId == 0);
  565. m_dwShutdownScheduleId =
  566. ScheduleWorkItem( ServiceShutdownIndicator,
  567. this,
  568. MS_SERVICE_SHUTDOWN_INDICATOR_TIME_INTERVAL,
  569. TRUE ); // Periodic
  570. if ( m_dwShutdownScheduleId == 0) {
  571. DBGPRINTF(( DBG_CONTEXT,
  572. "ScheduleShutdown for Service(%s) failed."
  573. " Error = %d\n",
  574. QueryServiceName(),
  575. GetLastError()
  576. ));
  577. }
  578. //
  579. // 2. Stop all endpoints for the service
  580. //
  581. IF_DEBUG( INSTANCE ) {
  582. DBGPRINTF(( DBG_CONTEXT,
  583. "IIS_SERVICE(%08p) Stopping all endpoints for %s\n",
  584. this, QueryServiceName()
  585. ));
  586. }
  587. DBG_REQUIRE( EnumServiceInstances( NULL, NULL,
  588. I_StopInstanceEndpoint)
  589. );
  590. //
  591. // 3. Cleanup partially initialized modules
  592. //
  593. DWORD err1 = ( *pfnCleanup)( this);
  594. // calls MB.Save so that next MB.Save will be fast
  595. // and will not cause delay during shutdown
  596. {
  597. DBGPRINTF(( DBG_CONTEXT,"[IIS_SERVICE::StartServiceOperation] Pre-Saving Metabase\n" ));
  598. MB mb( (IMDCOM*) IIS_SERVICE::QueryMDObject() );
  599. mb.Save();
  600. }
  601. DBGPRINTF((DBG_CONTEXT,"Cleanup done\n"));
  602. if ( err1 != NO_ERROR )
  603. {
  604. if ( err1 != ERROR_IO_PENDING )
  605. {
  606. //
  607. // Compound errors possible
  608. //
  609. if ( err != NO_ERROR) {
  610. DBGPRINTF( ( DBG_CONTEXT,
  611. " Error %d occured during cleanup of service %s\n",
  612. err1, QueryServiceName()));
  613. }
  614. }
  615. }
  616. if ( err1 == ERROR_IO_PENDING )
  617. {
  618. //
  619. // Shutdown is not complete yet. Wait for it to complete
  620. //
  621. WaitForSingleObject( m_hPendingShutdownEvent, INFINITE );
  622. }
  623. //
  624. // 4. If present, remove the scheduled work item
  625. //
  626. if ( m_dwShutdownScheduleId != 0) {
  627. RemoveWorkItem( m_dwShutdownScheduleId);
  628. m_dwShutdownScheduleId = 0;
  629. }
  630. }
  631. //
  632. // If we managed to actually connect to the Service Controller,
  633. // then tell it that we're stopped.
  634. //
  635. if( m_hsvcStatus != NULL_SERVICE_STATUS_HANDLE ) {
  636. UpdateServiceStatus( SERVICE_STOPPED,
  637. err,
  638. 0,
  639. 0 );
  640. }
  641. return ( err);
  642. } // IIS_SERVICE::StartServiceOperation()
  643. VOID
  644. IIS_SERVICE::IndicateShutdownComplete(
  645. VOID
  646. )
  647. /*++
  648. Routine Description:
  649. Used by services which return ERROR_IO_PENDING in their TerminateService
  650. routines. In this case, they should use this method to indicate
  651. that shutdown is complete.
  652. Arguments:
  653. None
  654. Return Value:
  655. None
  656. --*/
  657. {
  658. if ( m_hPendingShutdownEvent )
  659. {
  660. SetEvent( m_hPendingShutdownEvent );
  661. }
  662. }
  663. DWORD
  664. IIS_SERVICE::UpdateServiceStatus(
  665. IN DWORD dwState,
  666. IN DWORD dwWin32ExitCode,
  667. IN DWORD dwCheckPoint,
  668. IN DWORD dwWaitHint
  669. )
  670. /*++
  671. Description:
  672. Updates the local copy status of service controller status
  673. and reports it to the service controller.
  674. Arguments:
  675. dwState - New service state.
  676. dwWin32ExitCode - Service exit code.
  677. dwCheckPoint - Check point for lengthy state transitions.
  678. dwWaitHint - Wait hint for lengthy state transitions.
  679. Returns:
  680. NO_ERROR on success and returns Win32 error if failure.
  681. On success the status is reported to service controller.
  682. --*/
  683. {
  684. m_svcStatus.dwCurrentState = dwState;
  685. m_svcStatus.dwWin32ExitCode = dwWin32ExitCode;
  686. m_svcStatus.dwCheckPoint = dwCheckPoint;
  687. m_svcStatus.dwWaitHint = dwWaitHint;
  688. return ReportServiceStatus();
  689. } // IIS_SERVICE::UpdateServiceStatus()
  690. DWORD
  691. IIS_SERVICE::ReportServiceStatus( VOID)
  692. /*++
  693. Description:
  694. Wraps the call to SetServiceStatus() function.
  695. Prints the service status data if need be
  696. Arguments:
  697. None
  698. Returns:
  699. NO_ERROR if successful. other Win32 error code on failure.
  700. If successfull the new status has been reported to the service
  701. controller.
  702. --*/
  703. {
  704. DWORD err = NO_ERROR;
  705. IF_DEBUG( DLL_SERVICE_INFO) {
  706. DBGPRINTF(( DBG_CONTEXT, "dwServiceType = %08lX\n",
  707. m_svcStatus.dwServiceType ));
  708. DBGPRINTF(( DBG_CONTEXT, "dwCurrentState = %08lX\n",
  709. m_svcStatus.dwCurrentState ));
  710. DBGPRINTF(( DBG_CONTEXT, "dwControlsAccepted = %08lX\n",
  711. m_svcStatus.dwControlsAccepted ));
  712. DBGPRINTF(( DBG_CONTEXT, "dwWin32ExitCode = %08lX\n",
  713. m_svcStatus.dwWin32ExitCode ));
  714. DBGPRINTF(( DBG_CONTEXT, "dwServiceSpecificExitCode = %08lX\n",
  715. m_svcStatus.dwServiceSpecificExitCode ));
  716. DBGPRINTF(( DBG_CONTEXT, "dwCheckPoint = %08lX\n",
  717. m_svcStatus.dwCheckPoint ));
  718. DBGPRINTF(( DBG_CONTEXT, "dwWaitHint = %08lX\n",
  719. m_svcStatus.dwWaitHint ));
  720. }
  721. IF_DEBUG(DLL_SERVICE_INFO) {
  722. DBGPRINTF(( DBG_CONTEXT,
  723. " Setting Service Status for %s to %d\n",
  724. QueryServiceName(), m_svcStatus.dwCurrentState)
  725. );
  726. }
  727. if( !SetServiceStatus( m_hsvcStatus, &m_svcStatus ) ) {
  728. err = GetLastError();
  729. }
  730. return err;
  731. } // IIS_SERVICE::ReportServiceStatus()
  732. VOID
  733. IIS_SERVICE::ServiceCtrlHandler (
  734. IN DWORD dwOpCode
  735. )
  736. /*++
  737. Description:
  738. This function received control requests from the service controller.
  739. It runs in the context of service controller's dispatcher thread and
  740. performs the requested function.
  741. ( Note: Avoid time consuming operations in this function.)
  742. Arguments:
  743. dwOpCode
  744. indicates the requested operation. This should be
  745. one of the SERVICE_CONTROL_* manifests.
  746. Returns:
  747. None. If successful, then the state of the service might be changed.
  748. Note:
  749. if an operation ( especially SERVICE_CONTROL_STOP) is very lengthy,
  750. then this routine should report a STOP_PENDING status and create
  751. a worker thread to do the dirty work. The worker thread would then
  752. perform the necessary work and for reporting timely wait hints and
  753. final SERVICE_STOPPED status.
  754. History:
  755. KeithMo 07-March-1993 Created
  756. MuraliK 15-Nov-1994 Generalized it for all services.
  757. --*/
  758. {
  759. //
  760. // Interpret the opcode and let the worker functions update the state.
  761. // Also let the workers to update service state as appropriate
  762. //
  763. switch( dwOpCode ) {
  764. case SERVICE_CONTROL_INTERROGATE :
  765. InterrogateService();
  766. break;
  767. case SERVICE_CONTROL_STOP :
  768. case SERVICE_CONTROL_SHUTDOWN :
  769. StopService();
  770. break;
  771. case SERVICE_CONTROL_PAUSE :
  772. PauseService();
  773. break;
  774. case SERVICE_CONTROL_CONTINUE :
  775. ContinueService();
  776. break;
  777. default :
  778. DBGPRINTF(( DBG_CONTEXT, "Unrecognized Service Opcode %lu\n",
  779. dwOpCode ));
  780. break;
  781. }
  782. return;
  783. } // IIS_SERVICE::ServiceCtrlHandler()
  784. VOID
  785. IIS_SERVICE::InterrogateService( VOID )
  786. /*++
  787. Description:
  788. This function interrogates with the service status.
  789. Actually, nothing needs to be done here; the
  790. status is always updated after a service control.
  791. We have this function here to provide useful
  792. debug info.
  793. HISTORY:
  794. KeithMo 07-Mar-1993 Created.
  795. MuraliK 15-Nov-1994 Ported to Tcpsvcs.dll
  796. --*/
  797. {
  798. IF_DEBUG( DLL_SERVICE_INFO) {
  799. DBGPRINTF(( DBG_CONTEXT, "Interrogating service status for %s\n",
  800. QueryServiceName())
  801. );
  802. }
  803. ReportServiceStatus();
  804. return;
  805. } // IIS_SERVICE::InterrogateService()
  806. VOID
  807. IIS_SERVICE::PauseService( VOID )
  808. /*++
  809. Description:
  810. This function pauses the service. When the service is paused,
  811. no new user sessions are to be accepted, but existing connections
  812. are not effected.
  813. This function must update the SERVICE_STATUS::dwCurrentState
  814. field before returning.
  815. Returns:
  816. None. If successful the service is paused.
  817. --*/
  818. {
  819. PLIST_ENTRY entry;
  820. IIS_SERVER_INSTANCE *instance;
  821. DWORD status;
  822. IF_DEBUG( DLL_SERVICE_INFO) {
  823. DBGPRINTF(( DBG_CONTEXT, "pausing service %s\n",
  824. QueryServiceName())
  825. );
  826. }
  827. //
  828. // Scan all installed instances. For each instance, save its current
  829. // state (so we can retrieve it in ContinueService()) and, if the
  830. // current state is "started", then pause the instance.
  831. //
  832. AcquireServiceLock( TRUE );
  833. for( entry = m_InstanceListHead.Flink ;
  834. entry != &m_InstanceListHead ;
  835. entry = entry->Flink ) {
  836. instance = CONTAINING_RECORD(
  837. entry,
  838. IIS_SERVER_INSTANCE,
  839. m_InstanceListEntry
  840. );
  841. instance->SaveServerState();
  842. if( instance->QueryServerState() == MD_SERVER_STATE_STARTED ) {
  843. status = instance->PauseInstance();
  844. DBG_ASSERT( status == NO_ERROR );
  845. }
  846. }
  847. ReleaseServiceLock( TRUE );
  848. //
  849. // Set the *service* state to paused.
  850. //
  851. m_svcStatus.dwCurrentState = SERVICE_PAUSED;
  852. ReportServiceStatus();
  853. return;
  854. } // IIS_SERVICE::PauseService()
  855. VOID
  856. IIS_SERVICE::ContinueService( VOID )
  857. /*++
  858. Description:
  859. This function restarts ( continues) a paused service. This
  860. will return the service to the running state.
  861. This function must update the m_svcStatus.dwCurrentState
  862. field to running mode before returning.
  863. Returns:
  864. None. If successful then the service is running.
  865. --*/
  866. {
  867. PLIST_ENTRY entry;
  868. IIS_SERVER_INSTANCE *instance;
  869. DWORD status;
  870. IF_DEBUG( DLL_SERVICE_INFO) {
  871. DBGPRINTF(( DBG_CONTEXT, "continuing service %s\n",
  872. QueryServiceName())
  873. );
  874. }
  875. //
  876. // Scan all installed instances. For each instance, if its current
  877. // state is "paused" and its saved state is "running", then we know
  878. // the instanced was paused in PauseService(), so continue it.
  879. //
  880. AcquireServiceLock( TRUE );
  881. for( entry = m_InstanceListHead.Flink ;
  882. entry != &m_InstanceListHead ;
  883. entry = entry->Flink ) {
  884. instance = CONTAINING_RECORD(
  885. entry,
  886. IIS_SERVER_INSTANCE,
  887. m_InstanceListEntry
  888. );
  889. if( instance->QueryServerState() == MD_SERVER_STATE_PAUSED &&
  890. instance->QuerySavedState() == MD_SERVER_STATE_STARTED ) {
  891. status = instance->ContinueInstance();
  892. DBG_ASSERT( status == NO_ERROR );
  893. }
  894. }
  895. ReleaseServiceLock( TRUE );
  896. //
  897. // Set the *service* state to running.
  898. //
  899. m_svcStatus.dwCurrentState = SERVICE_RUNNING;
  900. ReportServiceStatus();
  901. return;
  902. } // IIS_SERVICE::ContinueService()
  903. VOID
  904. IIS_SERVICE::StopService( VOID )
  905. /*++
  906. Description:
  907. This function performs the shutdown on a service.
  908. This is called during system shutdown.
  909. This function is time constrained. The service controller gives a
  910. maximum of 20 seconds for shutdown for all active services.
  911. Only timely operations should be performed in this function.
  912. What we really do in IIS is, this thread sets the Shutdown Event
  913. inside the IIS_SERVICE structure. The shutdown event will wake
  914. the intial thread that started this service (see
  915. IIS_SERVICE::StartServiceOperation()) => some other thread does
  916. the bulk of cleanup operations.
  917. Returns:
  918. None. If successful, the service is shutdown.
  919. --*/
  920. {
  921. IF_DEBUG( DLL_SERVICE_INFO) {
  922. DBGPRINTF(( DBG_CONTEXT, "shutting down service %s\n",
  923. QueryServiceName())
  924. );
  925. }
  926. m_svcStatus.dwCurrentState = SERVICE_STOP_PENDING;
  927. m_svcStatus.dwCheckPoint = 0;
  928. //
  929. // Update state before setting the event to wake up the waiting thread
  930. //
  931. ReportServiceStatus();
  932. DBG_REQUIRE( SetEvent( m_hShutdownEvent ));
  933. return;
  934. } // IIS_SERVICE::StopService()
  935. DWORD
  936. IIS_SERVICE::InitializeSockets( VOID )
  937. /*++
  938. Initializes Socket access.
  939. It is responsible for connecting to WinSock.
  940. Returns:
  941. NO_ERROR on success
  942. Otherwise returns a Win32 error code.
  943. Limitations:
  944. This is for a single thread and not mult-thread safe.
  945. This function should be called after initializing globals.
  946. --*/
  947. {
  948. #ifndef ATQ_FORGOT_TO_CALL_WSASTARTUP
  949. return ( NO_ERROR);
  950. #else
  951. DWORD dwError = NO_ERROR;
  952. WSADATA wsaData;
  953. INT serr;
  954. //
  955. // Connect to WinSock
  956. //
  957. serr = WSAStartup( MAKEWORD( 2, 0), & wsaData);
  958. if( serr != 0 ) {
  959. SetServiceSpecificExitCode( ( DWORD) serr);
  960. dwError = ( ERROR_SERVICE_SPECIFIC_ERROR);
  961. m_EventLog.LogEvent( INET_SVC_WINSOCK_INIT_FAILED,
  962. 0,
  963. (const CHAR **) NULL,
  964. serr);
  965. }
  966. m_fSocketsInitialized = ( dwError == NO_ERROR);
  967. return ( dwError);
  968. #endif // ATQ_FORGOT_TO_CALL_WSASTARTUP
  969. } // IIS_SERVICE::InitializeSockets()
  970. DWORD
  971. IIS_SERVICE::CleanupSockets( VOID)
  972. /*++
  973. Cleansup the static information of sockets
  974. Returns:
  975. 0 if no errors,
  976. non-zero error code for any socket errors
  977. Limitations:
  978. This is for a single thread and not mult-thread safe.
  979. This function should be called after initializing globals.
  980. Note:
  981. This function should be called after shutting down all
  982. active socket connections.
  983. --*/
  984. {
  985. #ifndef ATQ_FORGOT_TO_CALL_WSASTARTUP
  986. return ( NO_ERROR);
  987. #else
  988. DWORD dwError = NO_ERROR;
  989. if ( m_fSocketsInitialized ) {
  990. INT serr = WSACleanup();
  991. if ( serr != 0) {
  992. SetServiceSpecificExitCode( ( DWORD) serr);
  993. dwError = ( ERROR_SERVICE_SPECIFIC_ERROR);
  994. }
  995. }
  996. m_fSocketsInitialized = FALSE;
  997. return (dwError);
  998. #endif // ATQ_FORGOT_TO_CALL_WSASTARTUP
  999. } // IIS_SERVICE::CleanupSockets()
  1000. # if 0
  1001. VOID
  1002. IIS_SERVICE::Print( VOID) const
  1003. {
  1004. IIS_SERVICE::Print();
  1005. DBGPRINTF( ( DBG_CONTEXT,
  1006. " Printing IIS_SERVICE object ( %08p) \n"
  1007. " State = %u. SocketsInitFlag = %u\n"
  1008. " ServiceStatusHandle = %08p. ShutDownEvent = %08p\n"
  1009. " MimeMap = %08p\n"
  1010. /* " InitFunction = %08x. CleanupFunction = %08x.\n" */
  1011. ,
  1012. this,
  1013. m_state, m_fSocketsInitialized,
  1014. m_hsvcStatus, m_hShutdownEvent,
  1015. m_pMimeMap
  1016. ));
  1017. DBGPRINTF(( DBG_CONTEXT,
  1018. " Printing IIS_SERVICE object (%08p)\n"
  1019. " IpcStarted = %u\n"
  1020. " EnableSvcLoc = %u; SvcLocationDone = %u\n"
  1021. " Service Id = %u. Service Name = %s\n"
  1022. " Module handle = %08p. ModuleName = %s\n"
  1023. " Reg Parameters Key = %s\n"
  1024. ,
  1025. this,
  1026. m_fIpcStarted,
  1027. m_fEnableSvcLocation, m_fSvcLocationDone,
  1028. m_dwServiceId, m_strServiceName.QueryStr(),
  1029. m_hModule, m_strModuleName.QueryStr(),
  1030. m_strParametersKey.QueryStr()
  1031. ));
  1032. DBGPRINTF(( DBG_CONTEXT,
  1033. " Eventlog = %08p\n",
  1034. &m_EventLog
  1035. ));
  1036. return;
  1037. } // IIS_SERVICE::Print()
  1038. #endif // DBG
  1039. // Former inline functions that make of class static variables
  1040. BOOL
  1041. IIS_SERVICE::CheckAndReference( )
  1042. {
  1043. AcquireServiceLock( );
  1044. if ( m_state == BlockStateActive ) {
  1045. InterlockedIncrement( &m_reference );
  1046. ReleaseServiceLock( );
  1047. LONG lEntry = SHARED_LOG_REF_COUNT();
  1048. LOCAL_LOG_REF_COUNT();
  1049. IF_DEBUG( INSTANCE )
  1050. DBGPRINTF((DBG_CONTEXT,"IIS_SERVICE ref count %ld (%ld)\n",
  1051. m_reference, lEntry));
  1052. return(TRUE);
  1053. }
  1054. ReleaseServiceLock( );
  1055. return(FALSE);
  1056. }
  1057. VOID
  1058. IIS_SERVICE::Dereference( )
  1059. {
  1060. LONG lEntry = SHARED_EARLY_LOG_REF_COUNT();
  1061. LOCAL_EARLY_LOG_REF_COUNT();
  1062. LONG Reference = InterlockedDecrement( &m_reference );
  1063. if ( 0 == Reference) {
  1064. IF_DEBUG( INSTANCE )
  1065. DBGPRINTF((DBG_CONTEXT,"deleting IIS_SERVICE %p (%ld)\n",
  1066. this, lEntry));
  1067. delete this;
  1068. } else {
  1069. IF_DEBUG( INSTANCE )
  1070. DBGPRINTF((DBG_CONTEXT,"IIS_SERVICE deref count %ld (%ld)\n",
  1071. Reference, lEntry));
  1072. }
  1073. }
  1074. PISRPC
  1075. IIS_SERVICE::QueryInetInfoRpc( VOID )
  1076. {
  1077. return sm_isrpc;
  1078. }
  1079. //
  1080. // Static Functions belonging to IIS_SERVICE class
  1081. //
  1082. BOOL
  1083. IIS_SERVICE::InitializeServiceInfo( VOID)
  1084. /*++
  1085. Description:
  1086. This function initializes all necessary local data for IIS_SERVICE class
  1087. Only the first initialization call does the initialization.
  1088. Others return without any effect.
  1089. Should be called from the entry function for DLL.
  1090. Arguments:
  1091. None
  1092. Returns:
  1093. TRUE on success and FALSE if any failure.
  1094. --*/
  1095. {
  1096. DWORD dwError = NO_ERROR;
  1097. if ( !IIS_SERVICE::sm_fInitialized) {
  1098. //
  1099. // The static data was Not Already initialized
  1100. //
  1101. #if SERVICE_REF_TRACKING
  1102. sm_pDbgRefTraceLog = CreateRefTraceLog(C_SERVICE_REFTRACES, 0);
  1103. IF_DEBUG( INSTANCE ) {
  1104. DBGPRINTF((DBG_CONTEXT,"IIS_SERVICE RefTraceLog=%p\n",
  1105. sm_pDbgRefTraceLog));
  1106. }
  1107. #endif // SERVICE_REF_TRACKING
  1108. INITIALIZE_CRITICAL_SECTION( & IIS_SERVICE::sm_csLock);
  1109. InitializeListHead( & IIS_SERVICE::sm_ServiceInfoListHead);
  1110. IIS_SERVICE::sm_fInitialized = TRUE;
  1111. IIS_SERVER_INSTANCE::Initialize();
  1112. dwError = ISRPC::Initialize();
  1113. if ( dwError != NO_ERROR) {
  1114. SetLastError( dwError);
  1115. }
  1116. }
  1117. return ( dwError == NO_ERROR);
  1118. } // IIS_SERVICE::InitializeServiceInfo()
  1119. VOID
  1120. IIS_SERVICE::CleanupServiceInfo(
  1121. VOID
  1122. )
  1123. /*++
  1124. Description:
  1125. Cleanup the data stored and services running.
  1126. This function should be called only after freeing all the
  1127. services running using this DLL.
  1128. This function is called typically when the DLL is unloaded.
  1129. Arguments:
  1130. None
  1131. Returns:
  1132. None
  1133. --*/
  1134. {
  1135. RPC_STATUS rpcerr;
  1136. DBG_REQUIRE( ISRPC::Cleanup() == NO_ERROR);
  1137. //
  1138. // Should we walk down the list of all services and stop them?
  1139. // Are should we expect the caller to have done that? NYI
  1140. //
  1141. DBG_ASSERT( IsListEmpty(&sm_ServiceInfoListHead) );
  1142. IIS_SERVER_INSTANCE::Cleanup();
  1143. //
  1144. // The DLL is going away so make sure all of the threads get terminated
  1145. // here
  1146. //
  1147. DeleteCriticalSection( & sm_csLock);
  1148. #if SERVICE_REF_TRACKING
  1149. if (sm_pDbgRefTraceLog != NULL)
  1150. {
  1151. IF_DEBUG( INSTANCE ) {
  1152. DBGPRINTF((DBG_CONTEXT,
  1153. "IIS_SERVICE: Closing RefTraceLog=%p\n",
  1154. sm_pDbgRefTraceLog));
  1155. }
  1156. DestroyRefTraceLog( sm_pDbgRefTraceLog );
  1157. }
  1158. sm_pDbgRefTraceLog = NULL;
  1159. #endif // SERVICE_REF_TRACKING
  1160. IIS_SERVICE::sm_fInitialized = FALSE;
  1161. } // IIS_SERVICE::CleanupServiceInfo()
  1162. BOOL
  1163. IIS_SERVICE::InitializeServiceRpc(
  1164. IN LPCSTR pszServiceName,
  1165. IN RPC_IF_HANDLE hRpcInterface
  1166. )
  1167. /*++
  1168. Description:
  1169. Initializes the rpc endpoint for the infocomm service.
  1170. Arguments:
  1171. pszServiceName - pointer to null-terminated string containing the name
  1172. of the service.
  1173. hRpcInterface - Handle for RPC interface.
  1174. Returns:
  1175. Win32 Error Code.
  1176. --*/
  1177. {
  1178. DWORD dwError = NO_ERROR;
  1179. PISRPC pIsrpc = NULL;
  1180. DBG_ASSERT( pszServiceName != NULL);
  1181. DBG_ASSERT( sm_isrpc == NULL );
  1182. pIsrpc = new ISRPC( pszServiceName);
  1183. if ( pIsrpc == NULL) {
  1184. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1185. goto exit;
  1186. }
  1187. //
  1188. // bind over Named pipe only.
  1189. // If needed to bind over TCP, bind with bit flag ISRPC_OVER_TCPIP on.
  1190. //
  1191. dwError = pIsrpc->AddProtocol( ISRPC_OVER_TCPIP
  1192. | ISRPC_OVER_NP | ISRPC_OVER_LPC
  1193. );
  1194. if( (dwError == RPC_S_DUPLICATE_ENDPOINT) ||
  1195. (dwError == RPC_S_OK)
  1196. ) {
  1197. dwError = pIsrpc->RegisterInterface(hRpcInterface);
  1198. }
  1199. if ( dwError != RPC_S_OK ) {
  1200. goto exit;
  1201. }
  1202. //
  1203. // Start the RPC listen thread
  1204. //
  1205. dwError = pIsrpc->StartServer( );
  1206. exit:
  1207. if ( dwError != NO_ERROR ) {
  1208. DBGPRINTF(( DBG_CONTEXT,
  1209. "Cannot start RPC Server for %s, error %lu\n",
  1210. pszServiceName, dwError ));
  1211. delete pIsrpc;
  1212. SetLastError(dwError);
  1213. return(FALSE);
  1214. }
  1215. sm_isrpc = pIsrpc;
  1216. return(TRUE);
  1217. } // IIS_SERVICE::InitializeServiceRpc
  1218. IIS_SERVICE::CleanupServiceRpc(
  1219. VOID
  1220. )
  1221. /*++
  1222. Description:
  1223. Cleanup the data stored and services running.
  1224. This function should be called only after freeing all the
  1225. services running using this DLL.
  1226. This function is called typically when the DLL is unloaded.
  1227. Arguments:
  1228. pszServiceName - pointer to null-terminated string containing the name
  1229. of the service.
  1230. hRpcInterface - Handle for RPC interface.
  1231. Returns:
  1232. Win32 Error Code.
  1233. --*/
  1234. {
  1235. DWORD dwError = NO_ERROR;
  1236. if ( sm_isrpc == NULL ) {
  1237. DBGPRINTF((DBG_CONTEXT,
  1238. "no isrpc object to cleanup. Returning success\n"));
  1239. return(TRUE);
  1240. }
  1241. (VOID) sm_isrpc->StopServer( );
  1242. dwError = sm_isrpc->CleanupData();
  1243. if( dwError != RPC_S_OK ) {
  1244. DBGPRINTF(( DBG_CONTEXT,
  1245. "ISRPC(%08p) Cleanup returns %lu\n", sm_isrpc, dwError ));
  1246. DBG_ASSERT( !"RpcServerUnregisterIf failure" );
  1247. SetLastError( dwError);
  1248. }
  1249. delete sm_isrpc;
  1250. sm_isrpc = NULL;
  1251. return TRUE;
  1252. } // CleanupServiceRpc
  1253. BOOL
  1254. IIS_SERVICE::InitializeMetabaseComObject(
  1255. VOID
  1256. )
  1257. /*++
  1258. Description:
  1259. This function initializes the metabase object
  1260. Arguments:
  1261. None
  1262. Returns:
  1263. TRUE on success and FALSE if any failure.
  1264. --*/
  1265. {
  1266. HANDLE hThreadHandle = NULL;
  1267. DWORD dwTemp;
  1268. BOOL fRet = FALSE;
  1269. IF_DEBUG(METABASE) {
  1270. DBGPRINTF((DBG_CONTEXT,"Initializing metabase object\n"));
  1271. }
  1272. fRet = InitMetadataDCom( (PVOID)&IIS_SERVICE::sm_MDObject,
  1273. (PVOID)&IIS_SERVICE::sm_MDNseObject );
  1274. if ( fRet )
  1275. {
  1276. fRet = InitializeMetabaseSink( sm_MDObject );
  1277. }
  1278. return(fRet);
  1279. } // IIS_SERVICE::InitializeMetabaseComObject
  1280. BOOL
  1281. IIS_SERVICE::CleanupMetabaseComObject(
  1282. VOID
  1283. )
  1284. /*++
  1285. Description:
  1286. This function initializes the metabase object
  1287. Arguments:
  1288. None
  1289. Returns:
  1290. TRUE on success and FALSE if any failure.
  1291. --*/
  1292. {
  1293. IF_DEBUG(METABASE) {
  1294. DBGPRINTF((DBG_CONTEXT,"Cleaning up metabase object %p\n",
  1295. IIS_SERVICE::sm_MDObject));
  1296. }
  1297. TerminateMetabaseSink();
  1298. if ( IIS_SERVICE::sm_MDObject != NULL ) {
  1299. ((IMDCOM*)IIS_SERVICE::sm_MDObject)->ComMDTerminate(TRUE);
  1300. IIS_SERVICE::sm_MDObject = NULL;
  1301. }
  1302. if ( IIS_SERVICE::sm_MDNseObject != NULL ) {
  1303. ((IMDCOM*)IIS_SERVICE::sm_MDNseObject)->ComMDTerminate(TRUE);
  1304. ((IMDCOM*)IIS_SERVICE::sm_MDNseObject)->Release();
  1305. IIS_SERVICE::sm_MDNseObject = NULL;
  1306. }
  1307. return(TRUE);
  1308. } // IIS_SERVICE::CleanupMetabaseComObject
  1309. VOID
  1310. IIS_SERVICE::MDChangeNotify(
  1311. DWORD dwMDNumElements,
  1312. MD_CHANGE_OBJECT pcoChangeList[]
  1313. )
  1314. /*++
  1315. This method handles the metabase change notification for the running
  1316. services. Note that since we're not allowed to reenter the metabase from
  1317. this notification, we do not directly notify the running services here.
  1318. Rather, we capture the state of this notification and queue the request
  1319. to a worker thread.
  1320. Arguments:
  1321. hMDHandle - Metabase handle generating the change notification
  1322. dwMDNumElements - Number of change elements in pcoChangeList
  1323. pcoChangeList - Array of paths and ids that have changed
  1324. --*/
  1325. {
  1326. DWORD totalLength;
  1327. DWORD i;
  1328. LPDWORD nextIdArray;
  1329. LPBYTE prevString;
  1330. PMD_CHANGE_OBJECT mdScan;
  1331. PMD_CHANGE_OBJECT nextObject;
  1332. PDEFERRED_MD_CHANGE pdeferredChange;
  1333. #if DO_NOTIFICATION_DEFERRED
  1334. //
  1335. // First off, we need to calculate the size of the buffer required
  1336. // to capture the change data. We'll start off with the known
  1337. // fixed-size data.
  1338. //
  1339. totalLength = sizeof(DEFERRED_MD_CHANGE) +
  1340. ( sizeof(MD_CHANGE_OBJECT) * dwMDNumElements );
  1341. //
  1342. // Now, we'll scan the change list and accumulate the lengths
  1343. // of the metadata paths and the ID arrays.
  1344. //
  1345. for( i = dwMDNumElements, mdScan = pcoChangeList ;
  1346. i > 0 ;
  1347. i--, mdScan++ ) {
  1348. totalLength += (DWORD)strlen( (CHAR *)mdScan->pszMDPath ) + 1;
  1349. totalLength += mdScan->dwMDNumDataIDs * sizeof(DWORD);
  1350. }
  1351. //
  1352. // Now we can actually allocate the work item.
  1353. //
  1354. pdeferredChange = (PDEFERRED_MD_CHANGE) TCP_ALLOC( totalLength );
  1355. if( pdeferredChange == NULL ) {
  1356. DBGPRINTF((
  1357. DBG_CONTEXT,
  1358. "MDChangeNotify: Cannot allocate work item (%lu)\n",
  1359. totalLength
  1360. ));
  1361. } else {
  1362. //
  1363. // Capture the change information.
  1364. //
  1365. nextObject = (PMD_CHANGE_OBJECT)( pdeferredChange + 1 );
  1366. prevString = (LPBYTE)pdeferredChange + totalLength;
  1367. nextIdArray = (LPDWORD)( (LPBYTE)nextObject +
  1368. ( sizeof(MD_CHANGE_OBJECT) * dwMDNumElements ) );
  1369. for( i = 0, mdScan = pcoChangeList ;
  1370. i < dwMDNumElements ;
  1371. i++, mdScan++, nextObject++ ) {
  1372. DWORD cchPath;
  1373. //
  1374. // Initialize the object.
  1375. //
  1376. cchPath = (DWORD)strlen( (CHAR *)mdScan->pszMDPath ) + 1;
  1377. prevString -= cchPath;
  1378. nextObject->pszMDPath = prevString;
  1379. memcpy(
  1380. nextObject->pszMDPath,
  1381. mdScan->pszMDPath,
  1382. cchPath
  1383. );
  1384. nextObject->dwMDChangeType = mdScan->dwMDChangeType;
  1385. nextObject->dwMDNumDataIDs = mdScan->dwMDNumDataIDs;
  1386. nextObject->pdwMDDataIDs = nextIdArray;
  1387. memcpy(
  1388. nextObject->pdwMDDataIDs,
  1389. mdScan->pdwMDDataIDs,
  1390. nextObject->dwMDNumDataIDs * sizeof(DWORD)
  1391. );
  1392. nextIdArray += nextObject->dwMDNumDataIDs;
  1393. }
  1394. //
  1395. // Ensure we didn't mess up the buffer.
  1396. //
  1397. DBG_ASSERT( (LPBYTE)nextIdArray == prevString );
  1398. //
  1399. // Now, just enqueue the request.
  1400. //
  1401. pdeferredChange->dwMDNumElements = dwMDNumElements;
  1402. if( !ScheduleWorkItem( DeferredMDChange,
  1403. pdeferredChange,
  1404. 0 ) ) {
  1405. DBGPRINTF((
  1406. DBG_CONTEXT,
  1407. "MDChangeNotify: cannot queue work item\n"
  1408. ));
  1409. TCP_FREE( pdeferredChange );
  1410. }
  1411. }
  1412. #else
  1413. IIS_SERVICE::DeferredMDChangeNotify( dwMDNumElements,
  1414. pcoChangeList );
  1415. IIS_SERVICE::DeferredGlobalConfig( dwMDNumElements,
  1416. pcoChangeList );
  1417. #endif
  1418. } // IIS_SERVICE::MDChangeNotify
  1419. #if DO_NOTIFICATION_DEFERRED
  1420. VOID
  1421. WINAPI
  1422. DeferredMDChange(
  1423. PVOID pv
  1424. )
  1425. {
  1426. PDEFERRED_MD_CHANGE pdmc = (PDEFERRED_MD_CHANGE) pv;
  1427. IF_DEBUG(METABASE) {
  1428. DBGPRINTF((DBG_CONTEXT,"DeferredMDChange(%p)\n", pdmc));
  1429. }
  1430. IIS_SERVICE::DeferredMDChangeNotify( pdmc->dwMDNumElements,
  1431. (PMD_CHANGE_OBJECT)(pdmc + 1) );
  1432. IIS_SERVICE::DeferredGlobalConfig( pdmc->dwMDNumElements,
  1433. (PMD_CHANGE_OBJECT)(pdmc + 1 ) );
  1434. TCP_FREE( pdmc );
  1435. }
  1436. #endif
  1437. VOID
  1438. IIS_SERVICE::DeferredGlobalConfig(
  1439. DWORD dwMDNumElements,
  1440. MD_CHANGE_OBJECT pcoChangeList[]
  1441. )
  1442. /*++
  1443. Update configuration of options that are above the service name in the
  1444. metabase (global to all services). For example, global Bandwidth
  1445. Throttling
  1446. Arguments:
  1447. dwMDNumElements - Number of change elements in pcoChangeList
  1448. pcoChangeList - Array of paths and ids that have changed
  1449. --*/
  1450. {
  1451. DWORD i;
  1452. BOOL fUpdateGlobalConfig = FALSE;
  1453. for ( i = 0; i < dwMDNumElements; i++ )
  1454. {
  1455. if ( !_stricmp( (CHAR*) pcoChangeList[i].pszMDPath, LM_PREFIX ) )
  1456. {
  1457. fUpdateGlobalConfig = TRUE;
  1458. break;
  1459. }
  1460. }
  1461. if ( fUpdateGlobalConfig )
  1462. {
  1463. DWORD dwVal;
  1464. AcquireGlobalLock();
  1465. MB mb( (IMDCOM*) IIS_SERVICE::QueryMDObject() );
  1466. if (!mb.Open("/lm", METADATA_PERMISSION_READ) ||
  1467. !mb.GetDword("", MD_MAX_BANDWIDTH, IIS_MD_UT_SERVER, &dwVal))
  1468. {
  1469. dwVal = INETA_DEF_BANDWIDTH_LEVEL;
  1470. }
  1471. AtqSetInfo( AtqBandwidthThrottle, (ULONG_PTR)dwVal);
  1472. if ( mb.GetDword("", MD_MAX_BANDWIDTH_BLOCKED, IIS_MD_UT_SERVER, &dwVal))
  1473. {
  1474. AtqSetInfo( AtqBandwidthThrottleMaxBlocked, (ULONG_PTR)dwVal );
  1475. }
  1476. ReleaseGlobalLock();
  1477. }
  1478. }
  1479. VOID
  1480. IIS_SERVICE::DeferredMDChangeNotify(
  1481. DWORD dwMDNumElements,
  1482. MD_CHANGE_OBJECT pcoChangeList[]
  1483. )
  1484. /*++
  1485. This method handles the metabase change notification for the running services
  1486. and notifies the appropriate service. This is a static method, invoked by
  1487. the deferred worker thread.
  1488. Arguments:
  1489. dwMDNumElements - Number of change elements in pcoChangeList
  1490. pcoChangeList - Array of paths and ids that have changed
  1491. --*/
  1492. {
  1493. LIST_ENTRY * pEntry;
  1494. #ifdef _KNFOCOMM
  1495. //
  1496. // Knfocomm sink will only process MB notifications whose path
  1497. // corresponds to services that use knfocomm...
  1498. //
  1499. const CHAR * pszKnfoSvcName;
  1500. DWORD cchKnfoSvcName;
  1501. DWORD i,j;
  1502. BOOL fMatch = FALSE;
  1503. for( j=0; j < gNumKnfoServices; j++ )
  1504. {
  1505. pszKnfoSvcName = rgKnfoServices[j];
  1506. cchKnfoSvcName = strlen( pszKnfoSvcName );
  1507. for ( i = 0; i < dwMDNumElements; i++ )
  1508. {
  1509. if ( !_strnicmp( (CHAR *) pcoChangeList[i].pszMDPath + LM_PREFIX_CCH,
  1510. pszKnfoSvcName,
  1511. cchKnfoSvcName ))
  1512. {
  1513. // MB change list contains a path that matches one of the known
  1514. // knfocomm services.
  1515. fMatch = TRUE;
  1516. }
  1517. }
  1518. }
  1519. if( !fMatch ) {
  1520. // Knfocomm has nothing to do with this notification...
  1521. return;
  1522. }
  1523. #endif
  1524. //
  1525. // Walk the list of services and change notifications looking for a match
  1526. //
  1527. AcquireGlobalLock();
  1528. for ( pEntry = sm_ServiceInfoListHead.Flink;
  1529. pEntry != &sm_ServiceInfoListHead;
  1530. pEntry = pEntry->Flink )
  1531. {
  1532. const CHAR * pszSvcName;
  1533. DWORD cchSvcName;
  1534. DWORD i;
  1535. IIS_SERVICE * pService = CONTAINING_RECORD( pEntry,
  1536. IIS_SERVICE,
  1537. m_ServiceListEntry );
  1538. pszSvcName = pService->QueryServiceName();
  1539. cchSvcName = strlen( pszSvcName );
  1540. for ( i = 0; i < dwMDNumElements; i++ )
  1541. {
  1542. if ( !_strnicmp( (CHAR *) pcoChangeList[i].pszMDPath + LM_PREFIX_CCH,
  1543. pszSvcName,
  1544. cchSvcName ))
  1545. {
  1546. if( pService->CheckAndReference() ) {
  1547. pService->MDChangeNotify( &pcoChangeList[i] );
  1548. pService->Dereference();
  1549. }
  1550. }
  1551. }
  1552. }
  1553. ReleaseGlobalLock();
  1554. } // IIS_SERVICE::DeferredMDChangeNotify
  1555. VOID
  1556. IIS_SERVICE::MDChangeNotify(
  1557. MD_CHANGE_OBJECT * pco
  1558. )
  1559. /*++
  1560. This method handles the metabase change notification for this server instance
  1561. Arguments:
  1562. pco - path and id that has changed
  1563. --*/
  1564. {
  1565. LIST_ENTRY * pEntry;
  1566. LPSTR serviceName;
  1567. DWORD serviceNameLength;
  1568. DWORD instanceId;
  1569. LPSTR instanceIdString;
  1570. LPSTR stringEnd;
  1571. BOOL parentChange;
  1572. BOOL didAddOrDelete;
  1573. DWORD i;
  1574. //
  1575. // Find the instance ID in the path.
  1576. //
  1577. serviceName = (LPSTR)QueryServiceName();
  1578. serviceNameLength = (DWORD)strlen( serviceName );
  1579. DBG_ASSERT( !_strnicmp(
  1580. (CHAR *)pco->pszMDPath,
  1581. LM_PREFIX,
  1582. LM_PREFIX_CCH
  1583. ) );
  1584. DBG_ASSERT( !_strnicmp(
  1585. (CHAR *)pco->pszMDPath + LM_PREFIX_CCH,
  1586. serviceName,
  1587. (size_t)serviceNameLength
  1588. ) );
  1589. instanceIdString = (LPSTR)pco->pszMDPath + LM_PREFIX_CCH + serviceNameLength;
  1590. //
  1591. // Lock the service before we start mucking with things too much.
  1592. //
  1593. parentChange = TRUE;
  1594. didAddOrDelete = FALSE;
  1595. AcquireServiceLock();
  1596. if( instanceIdString[0] == '/' &&
  1597. instanceIdString[1] != '\0' ) {
  1598. parentChange = FALSE;
  1599. instanceId = strtoul( instanceIdString + 1, &stringEnd, 10 );
  1600. //
  1601. // If this is an "instance add" or "instance delete", then Do The
  1602. // Right Thing. Note that strtoul() will set stringEnd to point to
  1603. // the character that "stopped" the conversion. This will point to
  1604. // the string terminator ('\0') if the converted ulong is at the end
  1605. // of the string. In our case, this would indicate the string is of
  1606. // the form:
  1607. //
  1608. // /LM/{service_name}/{instance_id}/
  1609. //
  1610. // Note there are no path components beyond the instance ID. This is
  1611. // our indication that an instance is getting created/deleted.
  1612. //
  1613. if( ( pco->dwMDChangeType & MD_CHANGE_TYPE_ADD_OBJECT ) &&
  1614. stringEnd[0] == '/' &&
  1615. stringEnd[1] == '\0' ) {
  1616. didAddOrDelete = TRUE;
  1617. if( !AddInstanceInfo( instanceId ) ) {
  1618. DBGPRINTF((
  1619. DBG_CONTEXT,
  1620. "MDChangeNotify: cannot add instance %lu, error %lu\n",
  1621. instanceId,
  1622. GetLastError()
  1623. ));
  1624. }
  1625. } else
  1626. if( ( pco->dwMDChangeType & MD_CHANGE_TYPE_DELETE_OBJECT ) &&
  1627. stringEnd[0] == '/' &&
  1628. stringEnd[1] == '\0' ) {
  1629. didAddOrDelete = TRUE;
  1630. if( !DeleteInstanceInfo( instanceId ) ) {
  1631. DBGPRINTF((
  1632. DBG_CONTEXT,
  1633. "MDChangeNotify: cannot delete instance %lu, error %lu\n",
  1634. instanceId,
  1635. GetLastError()
  1636. ));
  1637. }
  1638. }
  1639. }
  1640. if( !didAddOrDelete ) {
  1641. //
  1642. // Walk the list of instances and change notifications looking
  1643. // for a match on the metabase path or a path that is above the
  1644. // instance (to make sure any inherited changes are picked up).
  1645. //
  1646. DWORD pathLength = strlen( (CHAR *)pco->pszMDPath );
  1647. for ( pEntry = m_InstanceListHead.Flink;
  1648. pEntry != &m_InstanceListHead;
  1649. pEntry = pEntry->Flink )
  1650. {
  1651. IIS_SERVER_INSTANCE * pInstance = CONTAINING_RECORD( pEntry,
  1652. IIS_SERVER_INSTANCE,
  1653. m_InstanceListEntry );
  1654. if ( parentChange ||
  1655. ( pInstance->QueryMDPathLen() <= pathLength &&
  1656. !_strnicmp( (CHAR *) pco->pszMDPath,
  1657. pInstance->QueryMDPath(),
  1658. pInstance->QueryMDPathLen() ) &&
  1659. ( pco->pszMDPath[pInstance->QueryMDPathLen()] == '\0' ||
  1660. pco->pszMDPath[pInstance->QueryMDPathLen()] == '/' ) ) )
  1661. {
  1662. pInstance->MDChangeNotify( pco );
  1663. if ( !parentChange )
  1664. break;
  1665. }
  1666. }
  1667. }
  1668. //
  1669. // Watch for the downlevel instance changing
  1670. //
  1671. for ( i = 0; i < pco->dwMDNumDataIDs; i++ )
  1672. {
  1673. switch ( pco->pdwMDDataIDs[i] )
  1674. {
  1675. case MD_DOWNLEVEL_ADMIN_INSTANCE:
  1676. {
  1677. MB mb( (IMDCOM*) QueryMDObject() );
  1678. IIS_SERVER_INSTANCE * pInst;
  1679. if ( mb.Open( QueryMDPath() ) )
  1680. {
  1681. if ( !mb.GetDword( "",
  1682. MD_DOWNLEVEL_ADMIN_INSTANCE,
  1683. IIS_MD_UT_SERVER,
  1684. &m_dwDownlevelInstance ))
  1685. {
  1686. m_dwDownlevelInstance = 0xffffffff;
  1687. }
  1688. }
  1689. //
  1690. // Mirror the new vroots to the registry
  1691. //
  1692. if ( pInst = FindIISInstance( m_dwDownlevelInstance ))
  1693. {
  1694. pInst->MDMirrorVirtualRoots();
  1695. }
  1696. }
  1697. default:
  1698. break;
  1699. }
  1700. }
  1701. ReleaseServiceLock();
  1702. } // IIS_SERVICE::MDChangeNotify
  1703. BOOL
  1704. IIS_SERVICE::LoadStr(
  1705. OUT STR & str,
  1706. IN DWORD dwResId,
  1707. IN BOOL fForceEnglish ) const
  1708. /*++
  1709. This function loads the string, whose resource id is ( dwResId), into
  1710. the string str passed.
  1711. Arguments:
  1712. str reference to string object into which the string specified
  1713. by resource id is loaded
  1714. dwResId DWORD containing the resource id for string to be loaded.
  1715. Returns:
  1716. TRUE on success and FALSE if there is any failure.
  1717. --*/
  1718. {
  1719. BOOL fReturn = FALSE;
  1720. if ( (dwResId >= WSABASEERR) && (dwResId < WSA_MAX_ERROR) ) {
  1721. if (( fReturn = str.Resize((sizeof(SOCK_ERROR_STR_A) + 11) *
  1722. sizeof( WCHAR )))) {
  1723. wsprintfA( str.QueryStr(), SOCK_ERROR_STR_A, dwResId );
  1724. } // if ( Resize()
  1725. } else {
  1726. //
  1727. // Try to load the string from current module or system table
  1728. // depending upon if the Id < STR_RES_ID_BASE.
  1729. // System table contains strings for id's < STR_RES_ID_BASE.
  1730. //
  1731. if ( dwResId < STR_RES_ID_BASE) {
  1732. // Use English strings for System table
  1733. fReturn = str.LoadString( dwResId, (LPCTSTR ) NULL,
  1734. ( m_fIsDBCS && fForceEnglish ) ? 0x409 : 0);
  1735. } else {
  1736. fReturn = str.LoadString( dwResId, m_hModule );
  1737. }
  1738. }
  1739. if ( !fReturn ) {
  1740. DBGPRINTF((DBG_CONTEXT,"Error %d in load string[%d]\n",
  1741. GetLastError(), dwResId ));
  1742. }
  1743. return ( fReturn);
  1744. } // IIS_SERVICE::LoadStr()
  1745. DWORD
  1746. IIS_SERVICE::InitializeDiscovery(
  1747. VOID
  1748. )
  1749. /*++
  1750. Register this server and service with service discoverer.
  1751. It will discover us using these information for administering us.
  1752. Arguments:
  1753. None.
  1754. Return Value:
  1755. Win32 Error Code;
  1756. --*/
  1757. {
  1758. DWORD dwError = NO_ERROR;
  1759. PISRPC pIsrpc;
  1760. //
  1761. // Only enable on server as we don't have remove admin on
  1762. // the PWS. -jra !!! of course, we could change our minds again.
  1763. //
  1764. if ( g_hSvcLocDll == NULL ) {
  1765. m_fEnableSvcLocation = FALSE;
  1766. return(NO_ERROR);
  1767. }
  1768. INET_BINDINGS TotalBindings = { 0, NULL};
  1769. HKEY hkey = NULL;
  1770. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1771. QueryRegParamKey(),
  1772. 0,
  1773. KEY_READ,
  1774. &hkey );
  1775. if ( dwError != NO_ERROR )
  1776. {
  1777. IF_DEBUG( ERROR) {
  1778. DBGPRINTF(( DBG_CONTEXT,
  1779. "IIS_SERVICE::InitializeDiscovery() "
  1780. " RegOpenKeyEx returned error %d\n",
  1781. dwError ));
  1782. }
  1783. return (dwError);
  1784. }
  1785. m_fEnableSvcLocation = !!ReadRegistryDword( hkey,
  1786. INETA_ENABLE_SVC_LOCATION,
  1787. INETA_DEF_ENABLE_SVC_LOCATION);
  1788. if ( hkey != NULL) {
  1789. RegCloseKey( hkey);
  1790. }
  1791. if ( !m_fEnableSvcLocation ) {
  1792. //
  1793. // Service Location is not enabled (by admin presumably).
  1794. // So Let us not register ourselves now.
  1795. //
  1796. return ( NO_ERROR);
  1797. }
  1798. //
  1799. // Form the global binding information
  1800. //
  1801. pIsrpc = QueryInetInfoRpc( );
  1802. dwError = pIsrpc->EnumBindingStrings( &TotalBindings);
  1803. if ( dwError == NO_ERROR) {
  1804. dwError = pfnInetRegisterSvc(
  1805. m_SvcLocId,
  1806. INetServiceRunning,
  1807. m_strServiceComment.QueryStr(),
  1808. &TotalBindings
  1809. );
  1810. IF_DEBUG( DLL_RPC) {
  1811. DBGPRINTF(( DBG_CONTEXT,
  1812. "INetRegisterService( %u), Running, returns %u\n",
  1813. QueryServiceId(),
  1814. dwError));
  1815. }
  1816. }
  1817. //
  1818. // Log the error then ignore it as it only affects service discovery
  1819. //
  1820. if ( dwError != NO_ERROR ) {
  1821. m_EventLog.LogEvent( INET_SVC_SERVICE_REG_FAILED,
  1822. 0,
  1823. (const CHAR **) NULL,
  1824. dwError );
  1825. dwError = NO_ERROR; // Ignore the error .....
  1826. } else {
  1827. m_fSvcLocationDone = TRUE;
  1828. }
  1829. pIsrpc->FreeBindingStrings( &TotalBindings);
  1830. return( dwError);
  1831. } // IIS_SERVICE::InitializeDiscovery()
  1832. DWORD
  1833. IIS_SERVICE::TerminateDiscovery(
  1834. VOID
  1835. )
  1836. {
  1837. DWORD dwError = NO_ERROR;
  1838. //
  1839. // Deregister the service from the Discovery Service. This will
  1840. // prevent admins from picking up our machine for administration.
  1841. //
  1842. if ( m_fEnableSvcLocation && m_fSvcLocationDone) {
  1843. dwError = pfnInetDeregisterSvc(m_SvcLocId);
  1844. DBG_ASSERT( dwError == NO_ERROR);
  1845. m_fSvcLocationDone = FALSE;
  1846. }
  1847. return( dwError);
  1848. } // IIS_SERVICE::TerminateDiscovery()
  1849. VOID
  1850. IIS_SERVICE::DestroyAllServerInstances(
  1851. VOID
  1852. )
  1853. /*++
  1854. Description:
  1855. Destroys all server instanes of this service.
  1856. Arguments:
  1857. None.
  1858. Returns:
  1859. None.
  1860. --*/
  1861. {
  1862. PLIST_ENTRY listEntry;
  1863. PIIS_SERVER_INSTANCE pInstance;
  1864. IF_DEBUG( INSTANCE ) {
  1865. DBGPRINTF(( DBG_CONTEXT, "DestroyAllServerInstances called\n"));
  1866. }
  1867. //
  1868. // Loop and delete all instances
  1869. //
  1870. AcquireServiceLock( );
  1871. while ( !IsListEmpty(&m_InstanceListHead) ) {
  1872. listEntry = RemoveHeadList( &m_InstanceListHead );
  1873. m_nInstance--;
  1874. ReleaseServiceLock( );
  1875. pInstance = CONTAINING_RECORD(
  1876. listEntry,
  1877. IIS_SERVER_INSTANCE,
  1878. m_InstanceListEntry
  1879. );
  1880. //
  1881. // Close and dereference the instance.
  1882. //
  1883. pInstance->CloseInstance();
  1884. pInstance->Dereference();
  1885. AcquireServiceLock( );
  1886. }
  1887. ReleaseServiceLock( );
  1888. } // IIS_SERVICE::DestroyAllServerInstances
  1889. BOOL
  1890. IIS_SERVICE::EnumServiceInstances(
  1891. PVOID pvContext,
  1892. PVOID pvContext2,
  1893. PFN_INSTANCE_ENUM pfnEnum
  1894. )
  1895. /*++
  1896. Description:
  1897. Enumerates all instances on this service
  1898. Arguments:
  1899. pvContext - Context to pass back to the caller
  1900. pvContext2 - 2nd context to pass back to the caller
  1901. pfnEnum - Callback to make for each instance
  1902. Returns:
  1903. TRUE if no errors were returned, FALSE if a callback returned
  1904. an error
  1905. --*/
  1906. {
  1907. PLIST_ENTRY listEntry;
  1908. PIIS_SERVER_INSTANCE pInstance;
  1909. BOOL fRet = TRUE;
  1910. IF_DEBUG( INSTANCE ) {
  1911. DBGPRINTF(( DBG_CONTEXT, "EnumServiceInstances called\n"));
  1912. }
  1913. //
  1914. // Loop and delete all instances
  1915. //
  1916. AcquireServiceLock( TRUE );
  1917. for ( listEntry = m_InstanceListHead.Flink;
  1918. listEntry != &m_InstanceListHead;
  1919. listEntry = listEntry->Flink ) {
  1920. pInstance = CONTAINING_RECORD(
  1921. listEntry,
  1922. IIS_SERVER_INSTANCE,
  1923. m_InstanceListEntry
  1924. );
  1925. if ( !(fRet = pfnEnum( pvContext,
  1926. pvContext2,
  1927. pInstance )))
  1928. {
  1929. break;
  1930. }
  1931. }
  1932. ReleaseServiceLock( TRUE );
  1933. return fRet;
  1934. } // IIS_SERVICE::EnumServerInstances
  1935. VOID
  1936. IIS_SERVICE::CloseService(
  1937. VOID
  1938. )
  1939. /*++
  1940. Description:
  1941. This function cleans up the service object.
  1942. Returns:
  1943. None.
  1944. --*/
  1945. {
  1946. IF_DEBUG( INSTANCE ) {
  1947. DBGPRINTF(( DBG_CONTEXT,
  1948. "IIS_SERVICE:Destroy service object %p ref %d\n",this,m_reference));
  1949. }
  1950. DBG_ASSERT(m_state != BlockStateActive);
  1951. DestroyAllServerInstances( );
  1952. //
  1953. // We can't return from this function before the refcount hits zero, because
  1954. // after we return TerminateGlobals will destroy structures that the other
  1955. // threads might need while cleaning up. To prevent this we busy wait here
  1956. // until the reference count reaches 1.
  1957. //
  1958. #if DBG
  1959. int cRetries = 0;
  1960. const int nDelay = 1000;
  1961. #else
  1962. const int nDelay = 200;
  1963. #endif
  1964. while ( m_reference > 1 )
  1965. {
  1966. DBGPRINTF(( DBG_CONTEXT,
  1967. "IIS_SERVICE:Destroy service object %p ref %d\n",this,m_reference ));
  1968. #if DBG
  1969. ++cRetries;
  1970. #endif
  1971. Sleep(nDelay);
  1972. }
  1973. Dereference( );
  1974. } // IIS_SERVICE::CloseService
  1975. BOOL
  1976. IIS_SERVICE::AddServerInstance(
  1977. IN PIIS_SERVER_INSTANCE pInstance
  1978. )
  1979. /*++
  1980. Description:
  1981. References a new instane of this service
  1982. Arguments:
  1983. pInstance - instance to link.
  1984. Returns:
  1985. NO_ERROR on success and Win32 error code if any failure.
  1986. --*/
  1987. {
  1988. DWORD err;
  1989. IF_DEBUG( INSTANCE ) {
  1990. DBGPRINTF((DBG_CONTEXT,"AddServerInstance called with %p\n",
  1991. pInstance ));
  1992. }
  1993. //
  1994. // Insert instance into service list
  1995. //
  1996. AcquireServiceLock( );
  1997. if ( !IsActive() ) {
  1998. err = ERROR_NOT_READY;
  1999. goto error_exit;
  2000. }
  2001. //
  2002. // reference the instance since we now have a link to it.
  2003. //
  2004. pInstance->Reference();
  2005. InsertTailList( &m_InstanceListHead, &pInstance->m_InstanceListEntry );
  2006. m_nInstance++;
  2007. ReleaseServiceLock( );
  2008. return(TRUE);
  2009. error_exit:
  2010. ReleaseServiceLock( );
  2011. SetLastError(err);
  2012. return(FALSE);
  2013. } // AddServerInstance
  2014. BOOL
  2015. IIS_SERVICE::RemoveServerInstance(
  2016. IN PIIS_SERVER_INSTANCE pInstance
  2017. )
  2018. /*++
  2019. Description:
  2020. References a new instane of this service
  2021. Arguments:
  2022. pInstance - instance to link.
  2023. Returns:
  2024. NO_ERROR on success and Win32 error code if any failure.
  2025. --*/
  2026. {
  2027. IF_DEBUG( INSTANCE ) {
  2028. DBGPRINTF((DBG_CONTEXT,"RemoveServerInstance called with %p\n",
  2029. pInstance ));
  2030. }
  2031. //
  2032. // Remove instance from service list
  2033. //
  2034. AcquireServiceLock( );
  2035. RemoveEntryList( &pInstance->m_InstanceListEntry );
  2036. m_nInstance--;
  2037. ReleaseServiceLock( );
  2038. pInstance->Dereference( );
  2039. return(TRUE);
  2040. } // RemoveServerInstance
  2041. IIS_ENDPOINT *
  2042. IIS_SERVICE::FindAndReferenceEndpoint(
  2043. IN USHORT Port,
  2044. IN DWORD IpAddress,
  2045. IN BOOL CreateIfNotFound,
  2046. IN BOOL IsSecure,
  2047. IN BOOL fDisableSocketPooling
  2048. )
  2049. /*++
  2050. Description:
  2051. Searches the service's endpoint list looking for one bound to
  2052. the specified port.
  2053. Arguments:
  2054. Port - The port to search for.
  2055. IpAddress - The IP Address to search for.
  2056. CreateIfNotFound - If TRUE, and the port cannot be found in
  2057. the endpoint list, then a new endpoint is created and
  2058. attached to the list.
  2059. IsSecure - TRUE for secure ports. Only used when a new
  2060. endpoint is created.
  2061. fDisableSocketPooling - Used only if CreateIfNotFound is TRUE.
  2062. If TRUE, create an endpoint qualified by both Port & IP.
  2063. Else create an endpoint qualified only by Port.
  2064. Returns:
  2065. IIS_ENDPOINT * - Pointer to the endpoint if successful, NULL
  2066. otherwise. If !NULL, then the endpoint is referenced and it
  2067. is the caller's responsibility to dereference the endpoint
  2068. at a later time.
  2069. --*/
  2070. {
  2071. PLIST_ENTRY listEntry;
  2072. PIIS_ENDPOINT endpoint = NULL;
  2073. DWORD searchIpAddress = IpAddress;
  2074. //
  2075. // Walk the list looking for a matching port. Note that the endpoints
  2076. // are stored in ascending port order.
  2077. //
  2078. // Initially we search for an endpoint that is qualified by both IpAddress
  2079. // and Port.
  2080. AcquireServiceLock();
  2081. SearchEndpointList:
  2082. for( listEntry = m_EndpointListHead.Flink ;
  2083. listEntry != &m_EndpointListHead ;
  2084. listEntry = listEntry->Flink ) {
  2085. endpoint = CONTAINING_RECORD(
  2086. listEntry,
  2087. IIS_ENDPOINT,
  2088. m_EndpointListEntry
  2089. );
  2090. if( endpoint->m_Port > Port ) {
  2091. break;
  2092. }
  2093. if( endpoint->m_Port == Port &&
  2094. endpoint->m_IpAddress == searchIpAddress
  2095. )
  2096. {
  2097. endpoint->Reference();
  2098. goto done;
  2099. }
  2100. }
  2101. //
  2102. // The search failed. If this was a search qualified by IpAddress,
  2103. // we need to re-search using INADDR_ANY as the IP Address
  2104. //
  2105. if (INADDR_ANY != searchIpAddress)
  2106. {
  2107. searchIpAddress = INADDR_ANY;
  2108. goto SearchEndpointList;
  2109. }
  2110. //
  2111. // The port is not in the list. Create a new endpoint if required.
  2112. //
  2113. if( CreateIfNotFound ) {
  2114. //
  2115. // CODEWORK: It may be necessary in the future to move this
  2116. // endpoint creation to a virtual method so that classes derived
  2117. // from IIS_SERVICE can create specific types of endpoints.
  2118. //
  2119. endpoint = new IIS_ENDPOINT(
  2120. this,
  2121. Port,
  2122. fDisableSocketPooling ? IpAddress : INADDR_ANY,
  2123. IsSecure
  2124. );
  2125. if( endpoint != NULL ) {
  2126. //
  2127. // Insert it into the list.
  2128. //
  2129. listEntry = listEntry->Blink;
  2130. InsertHeadList(
  2131. listEntry,
  2132. &endpoint->m_EndpointListEntry
  2133. );
  2134. goto done;
  2135. }
  2136. }
  2137. //
  2138. // If we made it this far, then we could not find the endpoint and
  2139. // either could not create a new one OR were not asked to create one.
  2140. //
  2141. endpoint = NULL;
  2142. done:
  2143. ReleaseServiceLock();
  2144. return endpoint;
  2145. } // IIS_SERVICE::FindAndReferenceEndpoint
  2146. BOOL
  2147. IIS_SERVICE::AddInstanceInfoHelper(
  2148. IN IIS_SERVER_INSTANCE * pInstance
  2149. )
  2150. /*++
  2151. Description:
  2152. Helper routine called by the service-specific AddInstanceInfo()
  2153. virtual routine. This helper just commonizes some startup code
  2154. that all services need to do.
  2155. Arguments:
  2156. pInstance - The instance to associate with the service.
  2157. Returns:
  2158. BOOL - TRUE if successful, FALSE otherwise.
  2159. Notes:
  2160. If this routine returns FALSE, then the instance object passed in
  2161. is properly destroyed and extended error information is available
  2162. via GetLastError().
  2163. --*/
  2164. {
  2165. DWORD status;
  2166. if( pInstance == NULL ) {
  2167. status = ERROR_NOT_ENOUGH_MEMORY;
  2168. DBGPRINTF((
  2169. DBG_CONTEXT,
  2170. "AddInstanceInfoHelper: cannot create new instance, error %lu\n",
  2171. status
  2172. ));
  2173. //
  2174. // The memory allocation failed, so we've nothing to delete.
  2175. //
  2176. } else
  2177. if( pInstance->QueryServerState() == MD_SERVER_STATE_INVALID ) {
  2178. //
  2179. // Unfortunately, I don't think we can depend on "last error"
  2180. // getting set correctly on a constructor failure, so we'll
  2181. // just kludge up an error code.
  2182. //
  2183. status = ERROR_NOT_ENOUGH_MEMORY;
  2184. DBGPRINTF((
  2185. DBG_CONTEXT,
  2186. "AddInstanceInfoHelper: constructor failed, error %lu\n",
  2187. status
  2188. ));
  2189. //
  2190. // The constructor failed. The instance may or may not be on
  2191. // the service's instance list. If the base constructor failed,
  2192. // then the instance is NOT on the list. If the derived constructor
  2193. // failed, then the instance IS on the list.
  2194. //
  2195. // CleanupAfterConstructorFailure() will Do The Right Thing
  2196. // to properly destroy the partially constructed instance.
  2197. //
  2198. pInstance->CleanupAfterConstructorFailure();
  2199. } else
  2200. if( pInstance->IsAutoStart() && !AssociateInstance( pInstance ) ) {
  2201. status = GetLastError();
  2202. DBGPRINTF((
  2203. DBG_CONTEXT,
  2204. "AddInstanceInfoHelper: cannot associate instance, error %lu\n",
  2205. status
  2206. ));
  2207. //
  2208. // The constructor succeeded, but the instance could not be
  2209. // associated with the service. The reference count should be
  2210. // exactly one. We can't just delete the object as the destructor
  2211. // will assert because the reference count is non-zero, so we'll
  2212. // call RemoveServerInstance(), which will remove the instance from
  2213. // the service's list and then dereference the instance.
  2214. //
  2215. RemoveServerInstance( pInstance );
  2216. } else {
  2217. return TRUE;
  2218. }
  2219. SetLastError( status );
  2220. return FALSE;
  2221. } // IIS_SERVICE::AddInstanceInfoHelper
  2222. BOOL
  2223. IIS_SERVICE::IsService()
  2224. {
  2225. return TRUE;
  2226. }
  2227. VOID
  2228. IIS_SERVICE::AdvertiseServiceInformationInMB(
  2229. VOID
  2230. )
  2231. {
  2232. MB mb( (IMDCOM*) QueryMDObject() );
  2233. CHAR szServiceKey[MAX_PATH+1];
  2234. DWORD capabilities = 0;
  2235. DWORD version = 0;
  2236. DWORD productType = 0;
  2237. HKEY hkey;
  2238. strcpy( szServiceKey, IIS_MD_LOCAL_MACHINE_PATH "/" );
  2239. strcat( szServiceKey, QueryServiceName() );
  2240. if ( !mb.Open( szServiceKey,
  2241. METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ))
  2242. {
  2243. DBGPRINTF(( DBG_CONTEXT,
  2244. "[AddCapabilityFlag] Cannot open path %s, error %lu\n",
  2245. szServiceKey, GetLastError() ));
  2246. return;
  2247. }
  2248. //
  2249. // set version
  2250. //
  2251. if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
  2252. MD_SERVER_VERSION_MAJOR,
  2253. IIS_MD_UT_SERVER,
  2254. #ifdef _IIS_5_1
  2255. IIS_VERSION_MAJOR,
  2256. #else
  2257. IIS_SERVER_VERSION_MAJOR,
  2258. #endif
  2259. 0 ))
  2260. {
  2261. DBGPRINTF((DBG_CONTEXT,
  2262. "Error %d setting major version %x\n",
  2263. GetLastError(), IIS_VERSION_MAJOR));
  2264. }
  2265. if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
  2266. MD_SERVER_VERSION_MINOR,
  2267. IIS_MD_UT_SERVER,
  2268. #ifdef _IIS_5_1
  2269. IIS_VERSION_MINOR,
  2270. #else
  2271. IIS_SERVER_VERSION_MINOR,
  2272. #endif
  2273. 0 ))
  2274. {
  2275. DBGPRINTF((DBG_CONTEXT,
  2276. "Error %d setting minor version %x\n",
  2277. GetLastError(), IIS_VERSION_MINOR));
  2278. }
  2279. //
  2280. // set platform type
  2281. //
  2282. switch (IISGetPlatformType()) {
  2283. case PtNtServer:
  2284. productType = INET_INFO_PRODUCT_NTSERVER;
  2285. capabilities = IIS_CAP1_NTS;
  2286. break;
  2287. case PtNtWorkstation:
  2288. productType = INET_INFO_PRODUCT_NTWKSTA;
  2289. capabilities = IIS_CAP1_NTW;
  2290. break;
  2291. default:
  2292. productType = INET_INFO_PRODUCT_UNKNOWN;
  2293. capabilities = IIS_CAP1_NTW;
  2294. }
  2295. if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
  2296. MD_SERVER_PLATFORM,
  2297. IIS_MD_UT_SERVER,
  2298. productType,
  2299. 0 ))
  2300. {
  2301. DBGPRINTF((DBG_CONTEXT,
  2302. "Error %d setting platform type %x\n",
  2303. GetLastError(), productType));
  2304. }
  2305. //
  2306. // Check to see if FrontPage is installed
  2307. //
  2308. if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  2309. REG_FP_PATH,
  2310. 0,
  2311. KEY_READ,
  2312. &hkey ))
  2313. {
  2314. capabilities |= IIS_CAP1_FP_INSTALLED;
  2315. DBG_REQUIRE( !RegCloseKey( hkey ));
  2316. }
  2317. //
  2318. // We also need to determine the IIS_CAP1_DIGEST_SUPPORT and IIS_CAP1_NT_CERTMAP_SUPPORT
  2319. // bits but we don't do it here because Net Api calls take forever resulting in Service Control
  2320. // Manager timeouts. Hence we do that check in the InitializeService method (only for W3C).
  2321. //
  2322. //
  2323. // Set the capabilities flag
  2324. //
  2325. if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
  2326. MD_SERVER_CAPABILITIES,
  2327. IIS_MD_UT_SERVER,
  2328. capabilities,
  2329. 0 ))
  2330. {
  2331. DBGPRINTF((DBG_CONTEXT,
  2332. "Error %d setting capabilities flag %x\n",
  2333. GetLastError(), capabilities));
  2334. }
  2335. mb.Close();
  2336. return;
  2337. } // IIS_SERVICE::AdvertiseServiceInformationInMB
  2338. IUnknown *
  2339. IIS_SERVICE::QueryMDObject(
  2340. VOID
  2341. )
  2342. {
  2343. return IIS_SERVICE::sm_MDObject;
  2344. } // IIS_SERVICE::QueryMDObject
  2345. IUnknown *
  2346. IIS_SERVICE::QueryMDNseObject(
  2347. VOID
  2348. )
  2349. {
  2350. return IIS_SERVICE::sm_MDNseObject;
  2351. } // IIS_SERVICE::QueryMDObject
  2352. DWORD
  2353. InitMetadataDCom(
  2354. PVOID Context,
  2355. PVOID NseContext
  2356. )
  2357. /*++
  2358. Routine:
  2359. A dummy thread, used only to create the Metadata DCOM object in
  2360. the right fashion.
  2361. Arguments:
  2362. Context - Pointer to the global md object pointer
  2363. NseContext - Pointer to the global md NSE object pointer
  2364. Note: NSE (NSEPM) is legacy not supported on IIS6 any more.
  2365. It will always be set to NULL
  2366. Returns:
  2367. TRUE if we initialized DCOM properly, FALSE otherwise.
  2368. --*/
  2369. {
  2370. HRESULT hRes;
  2371. IMDCOM* pcCom;
  2372. BOOL fRet = FALSE;
  2373. IMDCOM ** pMetaObject = (IMDCOM**)Context;
  2374. IMDCOM ** pNseMetaObject = (IMDCOM**)NseContext;
  2375. hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  2376. if (FAILED(hRes) && (hRes != E_INVALIDARG) ) {
  2377. DBGPRINTF((DBG_CONTEXT,"CoInitializeEx failed %x\n", hRes));
  2378. DBG_ASSERT(FALSE);
  2379. return FALSE;
  2380. }
  2381. hRes = CoCreateInstance(
  2382. #ifndef _KETADATA
  2383. GETMDCLSID(TRUE),
  2384. #else
  2385. GETMDPCLSID(TRUE),
  2386. #endif
  2387. NULL,
  2388. CLSCTX_SERVER,
  2389. IID_IMDCOM,
  2390. (void**) &pcCom
  2391. );
  2392. if (!FAILED(hRes)) {
  2393. hRes = pcCom->ComMDInitialize();
  2394. if (FAILED(hRes)) {
  2395. DBGPRINTF((DBG_CONTEXT,"MDInitialize failed with %x\n", hRes));
  2396. pcCom->Release();
  2397. goto exit;
  2398. }
  2399. *pMetaObject = pcCom;
  2400. *pNseMetaObject = NULL;
  2401. IF_DEBUG(METABASE) {
  2402. DBGPRINTF((DBG_CONTEXT,"CoCreateInstance returns %x\n", pcCom));
  2403. }
  2404. fRet = TRUE;
  2405. goto exit;
  2406. } else {
  2407. DBGPRINTF((DBG_CONTEXT,"CoCreateInstance failed with %x\n", hRes));
  2408. }
  2409. exit:
  2410. CoUninitialize( );
  2411. return(fRet);
  2412. } // InitMetadataDCom
  2413. BOOL
  2414. IIS_SERVICE::RecordInstanceStart( VOID )
  2415. /*++
  2416. Description:
  2417. Records that an instance is starting.
  2418. Arguments:
  2419. None.
  2420. Returns:
  2421. BOOL - TRUE if it's OK to start the instance, FALSE if it
  2422. must not be started.
  2423. --*/
  2424. {
  2425. LONG result;
  2426. result = InterlockedIncrement( &m_nStartedInstances );
  2427. if( !TsIsNtServer() && result > 1 ) {
  2428. InterlockedDecrement( &m_nStartedInstances );
  2429. return FALSE;
  2430. }
  2431. return TRUE;
  2432. } // IIS_SERVICE::RecordInstanceStart
  2433. VOID
  2434. IIS_SERVICE::RecordInstanceStop( VOID )
  2435. /*++
  2436. Description:
  2437. Records that an instance is stopping.
  2438. Arguments:
  2439. None.
  2440. Returns:
  2441. None.
  2442. --*/
  2443. {
  2444. LONG result;
  2445. result = InterlockedDecrement( &m_nStartedInstances );
  2446. DBG_ASSERT( result >= 0 );
  2447. } // IIS_SERVICE::RecordInstanceStop
  2448. BOOL
  2449. I_StopInstanceEndpoint( PVOID pvContext1,
  2450. PVOID pvContext2,
  2451. IIS_SERVER_INSTANCE * pInstance )
  2452. {
  2453. IF_DEBUG( INSTANCE) {
  2454. DBGPRINTF(( DBG_CONTEXT,
  2455. "I_StopInstanceEndpoint( %p, %p, %08p)\n",
  2456. pvContext1, pvContext2, pInstance));
  2457. }
  2458. DBG_ASSERT( NULL == pvContext1);
  2459. DBG_ASSERT( NULL == pvContext2);
  2460. return ( pInstance->StopEndpoints());
  2461. } // I_StopInstanceEndpoint()
  2462. VOID
  2463. WINAPI
  2464. ServiceShutdownIndicator( VOID * pSvcContext)
  2465. {
  2466. IIS_SERVICE * pIisService = (IIS_SERVICE * ) pSvcContext;
  2467. IF_DEBUG( INSTANCE) {
  2468. DBGPRINTF(( DBG_CONTEXT,
  2469. "ServiceShutdownIndicator(%p)\n", pSvcContext));
  2470. }
  2471. if ( pSvcContext == NULL) {
  2472. DBGPRINTF(( DBG_CONTEXT,
  2473. " ServiceShutdownIndicator() called with NULL service\n"));
  2474. }
  2475. // Do Shutdown processing work ...
  2476. DBG_ASSERT( pIisService->QueryShutdownScheduleId() != 0);
  2477. pIisService->ShutdownScheduleCallback();
  2478. return;
  2479. } // ServiceShutdownIndicator()
  2480. DWORD
  2481. IIS_SERVICE::ShutdownScheduleCallback(VOID)
  2482. /*++
  2483. Description:
  2484. This function is the periodic callback from scheduler for IIS_SERVICE to
  2485. tell the Service Control Manager that we are shutting down
  2486. and need more time.
  2487. Problem: IIS_SERVICE shutdown operation takes long time. The time is
  2488. highly dependent on the number of components to be shutdown, number
  2489. of IO operations to be cancelled and cleaned up, etc.
  2490. Service Control Manager(SCM) in NT allows the Service shutdown to
  2491. happen within a specified time limit - usually less than 20 seconds.
  2492. If the shutdown did not happen within this window, NT SCM will report
  2493. shutdown failure.
  2494. This function will indicate to SCM that we will need more time to shutdown.
  2495. Arguments:
  2496. None
  2497. Returns:
  2498. NO_ERROR on success. DWORD on error.
  2499. --*/
  2500. {
  2501. m_nShutdownIndicatorCalls++;
  2502. # define NUM_SHUTDOWN_INDICATOR_CALLS_FOR_ONE_MINUTE \
  2503. ( (60 * 1000) / MS_SERVICE_SHUTDOWN_INDICATOR_TIME_INTERVAL)
  2504. if ( (m_nShutdownIndicatorCalls %
  2505. NUM_SHUTDOWN_INDICATOR_CALLS_FOR_ONE_MINUTE)
  2506. == 0) {
  2507. char rgchShutdown[256];
  2508. //
  2509. // Generate a message telling that shutdown is in progress
  2510. //
  2511. wsprintf( rgchShutdown,
  2512. "[%d]Service (%s) shutting down for %d minutes ... \n",
  2513. GetCurrentThreadId(),
  2514. QueryServiceName(),
  2515. (m_nShutdownIndicatorCalls /
  2516. NUM_SHUTDOWN_INDICATOR_CALLS_FOR_ONE_MINUTE
  2517. )
  2518. );
  2519. OutputDebugString( rgchShutdown);
  2520. }
  2521. DBG_ASSERT( m_dwShutdownScheduleId);
  2522. //
  2523. // Indicate to the SCM that we are in shutdown
  2524. //
  2525. DBG_REQUIRE( NO_ERROR ==
  2526. DelayCurrentServiceCtrlOperation( SERVICE_STOP_WAIT_HINT)
  2527. );
  2528. return (NO_ERROR);
  2529. } // IIS_SERVICE::ShutdownScheduleCallback()
  2530. VOID
  2531. IIS_SERVICE::StartUpIndicateClientActivity(VOID)
  2532. {
  2533. DWORD dwCurrentTime;
  2534. if ( m_svcStatus.dwCurrentState == SERVICE_START_PENDING)
  2535. {
  2536. m_dwClientStartActivityIndicator++;
  2537. dwCurrentTime = GetCurrentTimeInSeconds();
  2538. if (dwCurrentTime > m_dwNextSCMUpdateTime)
  2539. {
  2540. m_dwNextSCMUpdateTime = dwCurrentTime + IIS_SERVICE_START_INDICATOR_INTERVAL_SECONDS;
  2541. m_dwStartUpIndicatorCalls++;
  2542. if (m_dwStartUpIndicatorCalls < MAX_NUMBER_OF_START_HINT_REPETITIONS)
  2543. {
  2544. UpdateServiceStatus( SERVICE_START_PENDING,
  2545. NO_ERROR,
  2546. m_dwClientStartActivityIndicator,
  2547. IIS_SERVICE_START_WAIT_HINT_EXTENDED + IIS_SERVICE_START_INDICATOR_INTERVAL);
  2548. }
  2549. else
  2550. {
  2551. DBGPRINTF(( DBG_CONTEXT," StartUpIndicateClientActivity max startup extension periods exceeded\n"));
  2552. }
  2553. }
  2554. }
  2555. }