Source code of Windows XP (NT5)
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.

968 lines
24 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /**********************************************************************/
  5. /*
  6. main.cxx
  7. This module contains the main startup code for the W3 Service.
  8. FILE HISTORY:
  9. KeithMo 07-Mar-1993 Created.
  10. JohnL ????
  11. MuraliK 11-July-1995 Used Ipc() functions from Inetsvcs.dll
  12. */
  13. #include "w3p.hxx"
  14. #include "wamexec.hxx"
  15. #include "mtacb.h"
  16. #include "gip.h"
  17. //
  18. // RPC related includes
  19. //
  20. extern "C" {
  21. #include "inetinfo.h"
  22. #include <timer.h>
  23. #include "w3svci_s.h"
  24. #include <inetsvcs.h>
  25. //
  26. // system event log id
  27. //
  28. #include "w3msg.h"
  29. };
  30. #include <dsgetdc.h>
  31. //
  32. // Private constants.
  33. //
  34. BOOL fAnySecureFilters = FALSE;
  35. BOOL fComInitialized = FALSE;
  36. HANDLE g_hPhaseSync = NULL;
  37. //
  38. // for PDC hack
  39. //
  40. #define VIRTUAL_ROOTS_KEY_A "Virtual Roots"
  41. #define HTTP_EXT_MAPS "Script Map"
  42. //
  43. // Private globals.
  44. //
  45. DEFINE_TSVC_INFO_INTERFACE();
  46. #ifndef _NO_TRACING_
  47. #include <initguid.h>
  48. DEFINE_GUID(IisW3ServerGuid,
  49. 0x784d8919, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e);
  50. DECLARE_DEBUG_PRINTS_OBJECT();
  51. #else
  52. DECLARE_DEBUG_VARIABLE();
  53. DECLARE_PLATFORM_TYPE();
  54. #endif
  55. //
  56. // The following critical section synchronizes execution in ServiceEntry().
  57. // This is necessary because the NT Service Controller may reissue a service
  58. // start notification immediately after we have set our status to stopped.
  59. // This can lead to an unpleasant race condition in ServiceEntry() as one
  60. // thread cleans up global state as another thread is initializing it.
  61. //
  62. CRITICAL_SECTION g_csServiceEntryLock;
  63. //
  64. // Private prototypes.
  65. //
  66. APIERR InitializeService( LPVOID pContext );
  67. APIERR TerminateService( LPVOID pContext );
  68. VOID LoopCheckingForDrainOfConnections(VOID);
  69. DWORD InitializeExtension( VOID);
  70. extern DWORD InitializeWriteState(VOID);
  71. extern VOID TerminateWriteState(VOID);
  72. //
  73. // Public functions.
  74. //
  75. VOID
  76. ServiceEntry(
  77. DWORD cArgs,
  78. LPSTR pArgs[],
  79. PTCPSVCS_GLOBAL_DATA pGlobalData // unused
  80. )
  81. /*++
  82. Routine:
  83. This is the "real" entrypoint for the service. When
  84. the Service Controller dispatcher is requested to
  85. start a service, it creates a thread that will begin
  86. executing this routine.
  87. Arguments:
  88. cArgs - Number of command line arguments to this service.
  89. pArgs - Pointers to the command line arguments.
  90. Returns:
  91. None. Does not return until service is stopped.
  92. --*/
  93. {
  94. APIERR err = NO_ERROR;
  95. BOOL fInitSvcObject = FALSE;
  96. EnterCriticalSection( &g_csServiceEntryLock );
  97. if ( !InitCommonDlls() )
  98. {
  99. err = GetLastError();
  100. LeaveCriticalSection( &g_csServiceEntryLock );
  101. goto notify_scm;
  102. }
  103. //
  104. // Initialize Globals
  105. //
  106. err = InitializeGlobals();
  107. if ( err != NO_ERROR ) {
  108. goto exit;
  109. }
  110. //
  111. // Initialize phase synchronization event
  112. //
  113. g_hPhaseSync = CreateEvent( NULL, FALSE, FALSE, NULL );
  114. if ( g_hPhaseSync == NULL )
  115. {
  116. err = GetLastError();
  117. goto exit;
  118. }
  119. //
  120. // Initialize the service status structure.
  121. //
  122. g_pInetSvc = new W3_IIS_SERVICE(
  123. W3_SERVICE_NAME_A,
  124. W3_MODULE_NAME,
  125. W3_PARAMETERS_KEY,
  126. INET_HTTP_SVC_ID,
  127. INET_W3_SVCLOC_ID,
  128. TRUE,
  129. DEF_ACCEPTEX_RECV_BUFFER_SIZE,
  130. W3OnConnect,
  131. W3OnConnectEx,
  132. W3Completion
  133. );
  134. //
  135. // If we couldn't allocate memory for the service info struct, then the
  136. // machine is really hosed.
  137. //
  138. if ( (g_pInetSvc != NULL) &&
  139. ((W3_IIS_SERVICE *) g_pInetSvc)->QueryGlobalFilterList() &&
  140. g_pInetSvc->IsActive() )
  141. {
  142. fInitSvcObject = TRUE;
  143. if ( g_pstrMovedMessage = new STR )
  144. {
  145. g_pInetSvc->LoadStr( *g_pstrMovedMessage, IDS_URL_MOVED );
  146. err = g_pInetSvc->StartServiceOperation(
  147. SERVICE_CTRL_HANDLER(),
  148. InitializeService,
  149. TerminateService
  150. );
  151. }
  152. else
  153. {
  154. err = ERROR_NOT_ENOUGH_MEMORY;
  155. }
  156. if ( err )
  157. {
  158. //
  159. // The event has already been logged
  160. //
  161. DBGPRINTF(( DBG_CONTEXT,
  162. "HTTP ServiceEntry: StartServiceOperation returned %d\n",
  163. err ));
  164. }
  165. } else if ( g_pInetSvc == NULL) {
  166. err = ERROR_NOT_ENOUGH_MEMORY;
  167. } else {
  168. err = g_pInetSvc->QueryCurrentServiceError();
  169. }
  170. exit:
  171. if ( g_pInetSvc != NULL ) {
  172. g_pInetSvc->CloseService( );
  173. }
  174. if ( g_hPhaseSync != NULL ) {
  175. CloseHandle( g_hPhaseSync );
  176. g_hPhaseSync = NULL;
  177. }
  178. if ( g_pstrMovedMessage )
  179. {
  180. delete g_pstrMovedMessage;
  181. }
  182. TerminateGlobals( );
  183. TerminateCommonDlls();
  184. LeaveCriticalSection( &g_csServiceEntryLock );
  185. notify_scm:
  186. //
  187. // We need to tell the Service Control Manager that the service
  188. // is stopped if we haven't called g_pInetSvc->StartServiceOperation.
  189. // 1) InitCommonDlls fails, or
  190. // 2) InitializeGlobals failed, or
  191. // 3) new operator failed, or
  192. // 4) W3_IIS_SERVICE constructor couldn't initialize properly
  193. //
  194. if ( !fInitSvcObject ) {
  195. SERVICE_STATUS_HANDLE hsvcStatus;
  196. SERVICE_STATUS svcStatus;
  197. hsvcStatus = RegisterServiceCtrlHandler( W3_SERVICE_NAME,
  198. SERVICE_CTRL_HANDLER() );
  199. if ( hsvcStatus != NULL_SERVICE_STATUS_HANDLE ) {
  200. svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  201. svcStatus.dwCurrentState = SERVICE_STOPPED;
  202. svcStatus.dwWin32ExitCode = err;
  203. svcStatus.dwServiceSpecificExitCode = err;
  204. svcStatus.dwControlsAccepted = 0;
  205. svcStatus.dwCheckPoint = 0;
  206. svcStatus.dwWaitHint = 0;
  207. SetServiceStatus( hsvcStatus, (LPSERVICE_STATUS) &svcStatus );
  208. }
  209. }
  210. } // ServiceEntry
  211. //
  212. // Private functions.
  213. //
  214. APIERR
  215. InitializeService(
  216. LPVOID pContext
  217. )
  218. /*++
  219. Routine:
  220. This function initializes the various W3 Service components.
  221. Arguments:
  222. pContext - Pointer to the service object
  223. Returns:
  224. NO_ERROR if successful, otherwise a Win32
  225. status code.
  226. --*/
  227. {
  228. APIERR err;
  229. PW3_IIS_SERVICE psi = (PW3_IIS_SERVICE)pContext;
  230. PDOMAIN_CONTROLLER_INFO pDomainControllerInfo;
  231. IF_DEBUG( SERVICE_CTRL )
  232. {
  233. DBGPRINTF(( DBG_CONTEXT,
  234. "initializing service\n" ));
  235. }
  236. //
  237. // Initialize the Digest Authentication and NT Cert Mapper Capabilities here. We did not
  238. // initialize them with other capability checks in AdvertiseServiceInfo because net calls
  239. // take a significant amount of time resulting in Service Control Manager timeouts.
  240. //
  241. // The criterion for NT Cert Mapper is that htis machine is a member of a NT5 domain.
  242. // In addition, for Digest Authentication, the PDC needs to be running Directory Services
  243. // should have the "Capture Clear Text Password" flag set (Presently we only check the first
  244. // 2 requirements).
  245. //
  246. if (NO_ERROR == DsGetDcName( NULL,
  247. NULL,
  248. NULL,
  249. NULL,
  250. DS_PDC_REQUIRED | DS_DIRECTORY_SERVICE_PREFERRED,
  251. &pDomainControllerInfo))
  252. {
  253. CHAR szServiceKey[MAX_PATH+1];
  254. DWORD capabilities = 0;
  255. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  256. GUID nullguid = {0};
  257. if ( nullguid != pDomainControllerInfo->DomainGuid)
  258. {
  259. //
  260. // NT5 DC. Add the NT Certmap Support capability to the already advertized capabilities.
  261. //
  262. strcpy( szServiceKey, IIS_MD_LOCAL_MACHINE_PATH "/" "W3SVC");
  263. if ( mb.Open( szServiceKey, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ))
  264. {
  265. mb.GetDword(IIS_MD_SVC_INFO_PATH,
  266. MD_SERVER_CAPABILITIES,
  267. IIS_MD_UT_SERVER,
  268. &capabilities,
  269. 0);
  270. capabilities |= IIS_CAP1_NT_CERTMAP_SUPPORT;
  271. if ( pDomainControllerInfo->Flags & DS_DS_FLAG)
  272. {
  273. capabilities |= IIS_CAP1_DIGEST_SUPPORT;
  274. }
  275. if ( !mb.SetDword( IIS_MD_SVC_INFO_PATH,
  276. MD_SERVER_CAPABILITIES,
  277. IIS_MD_UT_SERVER,
  278. capabilities,
  279. 0 ))
  280. {
  281. DBGPRINTF((DBG_CONTEXT,
  282. "Error %d setting capabilities flag %x\n",
  283. GetLastError(), capabilities));
  284. }
  285. mb.Close();
  286. }
  287. else
  288. {
  289. DBGPRINTF(( DBG_CONTEXT,
  290. "[InitializeService] Cannot open MB path %s, error %lu\n",
  291. szServiceKey, GetLastError() ));
  292. }
  293. }
  294. NetApiBufferFree( pDomainControllerInfo);
  295. }
  296. //
  297. // init com - we will need it to co-create wams
  298. //
  299. if( FAILED( err = CoInitializeEx(
  300. NULL, // void * pvReserved, //Reserved - must be NULL
  301. COINIT_MULTITHREADED // DWORD dwCoInit //COINIT value
  302. | COINIT_SPEED_OVER_MEMORY // UNDONE are these the right flags?
  303. )))
  304. {
  305. if ( err != E_INVALIDARG ) {
  306. DBGPRINTF(( DBG_CONTEXT,
  307. "W3SVC: CoInitializeEx failed, error %lu\n",
  308. err ));
  309. return err;
  310. }
  311. }
  312. fComInitialized = TRUE;
  313. //
  314. // Initialize FTM instance
  315. //
  316. if( FAILED( err = CFTMImplementation::Init() ) ) {
  317. DBGPRINTF( (DBG_CONTEXT,
  318. "W3SVC: Failure in CFTMImplementation::Init, error %x\n",
  319. err ));
  320. return err;
  321. }
  322. //
  323. // Initialize MTA callback and GIP table for calling WAMs
  324. //
  325. if( FAILED( err = InitMTACallbacks() ) ) {
  326. DBGPRINTF( (DBG_CONTEXT,
  327. "W3SVC: Failure to initialize MTACB API, error %08x\n",
  328. err ));
  329. return err;
  330. }
  331. if( FAILED( err = g_GIPAPI.Init() ) ) {
  332. DBGPRINTF( (DBG_CONTEXT,
  333. "W3SVC: Failure to initialize GIP API, error %08x\n",
  334. err ));
  335. return err;
  336. }
  337. //
  338. // Initialize various components. The ordering of the
  339. // components is somewhat limited. Globals should be
  340. // initialized first, then the event logger. After
  341. // the event logger is initialized, the other components
  342. // may be initialized in any order with one exception.
  343. // InitializeSockets must be the last initialization
  344. // routine called. It kicks off the main socket connection
  345. // thread.
  346. //
  347. if( ( err = CLIENT_CONN::Initialize() ) ||
  348. ( err = HTTP_REQUEST::Initialize() ) ||
  349. ( err = InitializeWriteState() ) ||
  350. ( err = InitializeOleHack()) ||
  351. ( err = InitializeExtension() ) ||
  352. ( err = InitializeCGI() ) ||
  353. ( err = psi->InitializeDiscovery( )) ||
  354. ( err = ReadRegistryExtMap()) ||
  355. #if defined(CAL_ENABLED)
  356. ( err = InitializeCal( psi->QueryGlobalStatistics(),
  357. psi->QueryCalVcPerLicense(),
  358. psi->QueryCalAuthReserveTimeout(),
  359. psi->QueryCalSslReserveTimeout() )) ||
  360. #endif
  361. ( err = psi->InitializeSockets( )) ||
  362. ( err = WriteConfiguration()) )
  363. {
  364. DBGPRINTF(( DBG_CONTEXT,
  365. "cannot initialize service, error %lu\n",
  366. err ));
  367. return err;
  368. }
  369. //
  370. // Read and activate all the instances configured
  371. //
  372. DBG_ASSERT(g_pW3Stats);
  373. g_pW3Stats->UpdateStartTime();
  374. InitializeInstances( psi );
  375. //
  376. // Success!
  377. //
  378. IF_DEBUG( SERVICE_CTRL )
  379. {
  380. DBGPRINTF(( DBG_CONTEXT, "Service initialized\n" ));
  381. }
  382. return NO_ERROR;
  383. } // InitializeService
  384. DWORD
  385. TerminateServicePhase1(
  386. IN LPVOID pvContext
  387. )
  388. /*++
  389. Routine Description:
  390. Terminate W3 Service Phase 1: Stop all current connections/requests
  391. Arguments:
  392. pvContext - Service
  393. Return Value:
  394. ERROR_SUCCESS if successful, else Win32 Error
  395. --*/
  396. {
  397. //
  398. // Stop processing job limits and work items.
  399. //
  400. W3_LIMIT_JOB_THREAD::StopLimitJobThread();
  401. W3_JOB_QUEUE::StopJobQueue();
  402. //
  403. // Stop all CGI processes
  404. //
  405. KillCGIProcess();
  406. //
  407. // Blow away any connected users. We do this before calling the WAM
  408. // shutdown code so ASP apps with long network IOs will abort in a
  409. // timely fashion.
  410. //
  411. CLIENT_CONN::DisconnectAllUsers();
  412. //
  413. // Start shutdown of wam dictator, do other stuff,
  414. // then uninit wam dictator below
  415. //
  416. DBG_ASSERT(g_pWamDictator);
  417. g_pWamDictator->StartShutdown();
  418. //
  419. // Uninit WamDictator.
  420. //
  421. DBG_ASSERT(g_pWamDictator);
  422. g_pWamDictator->UninitWamDictator();
  423. //
  424. // By now all connections should have gone away
  425. // However, we carry a large amount of inconsistencies in our shutdown code
  426. // => some connections may not have shutdown.
  427. // Let us loop and check for this straggler requests
  428. //
  429. LoopCheckingForDrainOfConnections();
  430. //
  431. // Allow the phase 2 shutdown to start
  432. //
  433. SetEvent( g_hPhaseSync );
  434. return ERROR_SUCCESS;
  435. }
  436. DWORD
  437. TerminateServicePhase2(
  438. IN LPVOID pvContext
  439. )
  440. /*++
  441. Routine Description:
  442. Terminate W3 Service Phase 2: Cleanup state
  443. Arguments:
  444. pvContext - Service
  445. Return Value:
  446. ERROR_SUCCESS if successful, else Win32 Error
  447. --*/
  448. {
  449. PW3_IIS_SERVICE psi = (PW3_IIS_SERVICE)pvContext;
  450. DWORD err;
  451. //
  452. // Wait for phase 1 completion
  453. //
  454. WaitForSingleObject( g_hPhaseSync, INFINITE );
  455. //
  456. // Stop all networking I/O components now
  457. //
  458. g_pInetSvc->ShutdownService( );
  459. (VOID)psi->CleanupSockets( );
  460. //
  461. // Cleanup state now
  462. //
  463. TerminateOleHack();
  464. if ( (err = psi->TerminateDiscovery()) != NO_ERROR)
  465. {
  466. DBGPRINTF(( DBG_CONTEXT, "TerminateDiscovery() failed. Error = %u\n",
  467. err));
  468. }
  469. CLIENT_CONN::Terminate();
  470. HTTP_REQUEST::Terminate();
  471. TerminateWriteState();
  472. FreeRegistryExtMap();
  473. TerminateCGI();
  474. // delay the destruction of WamDictator.
  475. // Uninit wamreq allocation cache is done in the destruction.
  476. delete g_pWamDictator;
  477. IF_DEBUG( SERVICE_CTRL )
  478. {
  479. DBGPRINTF(( DBG_CONTEXT,"service terminated\n" ));
  480. }
  481. //
  482. // Delete the metacache items
  483. //
  484. TsCacheFlush( INET_HTTP_SVC_ID );
  485. TsFlushMetaCache(METACACHE_W3_SERVER_ID, TRUE);
  486. #if defined(CAL_ENABLED)
  487. TerminateCal();
  488. #endif
  489. //
  490. // cleanup MTA callback & GIP table
  491. //
  492. UnInitMTACallbacks();
  493. g_GIPAPI.UnInit();
  494. CFTMImplementation::UnInit();
  495. if( fComInitialized )
  496. {
  497. CoUninitialize();
  498. }
  499. psi->IndicateShutdownComplete();
  500. return NO_ERROR;
  501. }
  502. DWORD
  503. TerminateService(
  504. IN PVOID pvContext
  505. )
  506. /*++
  507. Routine Description:
  508. Called to shutdown service
  509. Arguments:
  510. pvContext - Service
  511. Return Value:
  512. ERROR_SUCCESS if successful, else Win32 Error
  513. --*/
  514. {
  515. PW3_IIS_SERVICE psi = (PW3_IIS_SERVICE)pvContext;
  516. DBG_ASSERT(g_pW3Stats);
  517. g_pW3Stats->UpdateStopTime();
  518. TerminateServicePhase1( pvContext );
  519. return W3_IIS_SERVICE::DereferenceW3Service( pvContext );
  520. }
  521. DWORD
  522. InitializeExtension( VOID)
  523. {
  524. // Init wam dictator
  525. g_pWamDictator = new WAM_DICTATOR;
  526. if (!g_pWamDictator)
  527. {
  528. return (ERROR_NOT_ENOUGH_MEMORY);
  529. }
  530. return ( g_pWamDictator->InitWamDictator() );
  531. } // InitializeExtensions()
  532. VOID
  533. PrintClientConnStateOnDrain(IN CLIENT_CONN * pConn)
  534. {
  535. DBGPRINTF(( DBG_CONTEXT,
  536. "[%d]"
  537. "[OnDrainOfConns] Stale CC @ 0x%p (HR: 0x%p), "
  538. "CC:State = %d (HR:State =%d), CC:Ref = %u;"
  539. " W3Endpoint = 0x%p,"
  540. " AtqContext = 0x%p,"
  541. " WamRequest = 0x%p,"
  542. " URL=%s\n; "
  543. ,
  544. GetCurrentThreadId(),
  545. pConn,
  546. (pConn ? pConn->QueryHttpReq() : NULL ),
  547. (pConn ? pConn->QueryState() : -1 ),
  548. (pConn ? pConn->QueryHttpReq()->QueryState() : NULL ),
  549. (pConn ? pConn->QueryRefCount() : -1 ),
  550. (pConn ? pConn->QueryW3Endpoint() : NULL ),
  551. (pConn ? pConn->QueryAtqContext() : NULL ),
  552. (pConn ? (((HTTP_REQUEST * ) pConn->QueryHttpReq())->
  553. QueryWamRequest()) : NULL ),
  554. (pConn ? pConn->QueryHttpReq()->QueryURL() : NULL )
  555. ));
  556. } // PrintClientConnStateOnDrain()
  557. VOID
  558. LoopCheckingForDrainOfConnections(VOID)
  559. {
  560. CHAR rgchConns[1024];
  561. //
  562. // This chunck of code is instrumentation to help catch any client
  563. // requests that don't get cleaned up properly
  564. //
  565. LockGlobals();
  566. if ( !IsListEmpty( &listConnections ))
  567. {
  568. LIST_ENTRY * pEntry;
  569. DWORD cConns = 0;
  570. for ( pEntry = listConnections.Flink;
  571. pEntry != &listConnections;
  572. pEntry = pEntry->Flink )
  573. {
  574. CLIENT_CONN * pConn = CONTAINING_RECORD( pEntry,
  575. CLIENT_CONN,
  576. ListEntry );
  577. PrintClientConnStateOnDrain( pConn);
  578. cConns++;
  579. }
  580. UnlockGlobals();
  581. wsprintf( rgchConns,
  582. "\n--------------------\n"
  583. "[W3Svc: TerminateService] Waiting for 2 minutes for %d straggler"
  584. " conns to drain\n",
  585. cConns );
  586. OutputDebugString( rgchConns);
  587. //
  588. // loop for 2 minutes trying to drain clients. Bail if it takes longer.
  589. //
  590. CONST DWORD cWaitMax = 120000; // in mseconds
  591. CONST DWORD cSleepInterval = 2000; // in mseconds
  592. for(DWORD cWait = 0; cWait <= cWaitMax; cWait += cSleepInterval)
  593. {
  594. Sleep( cSleepInterval );
  595. LockGlobals();
  596. if ( IsListEmpty( &listConnections ))
  597. {
  598. UnlockGlobals();
  599. break;
  600. }
  601. for ( pEntry = listConnections.Flink, cConns = 0;
  602. pEntry != &listConnections;
  603. pEntry = pEntry->Flink )
  604. {
  605. CLIENT_CONN * pConn = CONTAINING_RECORD( pEntry,
  606. CLIENT_CONN,
  607. ListEntry );
  608. PrintClientConnStateOnDrain( pConn);
  609. cConns++;
  610. }
  611. UnlockGlobals();
  612. wsprintf( rgchConns,
  613. "\n--------------------\n"
  614. "[W3Svc] Waited for %d seconds for %d straggler"
  615. " conns to drain\n",
  616. cWait/1000, cConns );
  617. OutputDebugString( rgchConns);
  618. }
  619. //
  620. // Check for stragglers and log them to the system event log.
  621. //
  622. if (cWait > cWaitMax)
  623. {
  624. DWORD cStragglerCount = 0;
  625. STR strStragglerConnections = "\r\n";
  626. STR strConnectionURL;
  627. LockGlobals();
  628. for ( pEntry = listConnections.Flink;
  629. pEntry != &listConnections;
  630. pEntry = pEntry->Flink)
  631. {
  632. CLIENT_CONN * pConn = CONTAINING_RECORD ( pEntry,
  633. CLIENT_CONN,
  634. ListEntry);
  635. if ( pConn->IsValid() && pConn->CheckSignature() &&
  636. (NULL != pConn->QueryHttpReq()) &&
  637. (NULL != pConn->QueryHttpReq()->QueryURL()))
  638. {
  639. CHAR *pCh = NULL;
  640. strConnectionURL.Copy("\r\n");
  641. strConnectionURL.Append(pConn->QueryHttpReq()->QueryURL());
  642. strConnectionURL.Append("\r\n");
  643. //
  644. // Check if we already have this URL in the list. If not add it
  645. //
  646. if ( NULL == strstr( strStragglerConnections.QueryStr(), strConnectionURL.QueryStr()))
  647. {
  648. strStragglerConnections.Append(strConnectionURL.QueryStr()+2); //Don't copy extra \r\n
  649. cStragglerCount++;
  650. }
  651. }
  652. }
  653. if (cStragglerCount > 0)
  654. {
  655. const CHAR *pszEventDescription[1];
  656. pszEventDescription[0] = strStragglerConnections.QueryStr(),
  657. g_pInetSvc->LogEvent(W3_EVENT_FAILED_CLOSE_CC_SHUTDOWN,
  658. 1,
  659. pszEventDescription,
  660. 0);
  661. }
  662. UnlockGlobals();
  663. }
  664. }
  665. else
  666. {
  667. UnlockGlobals();
  668. }
  669. return;
  670. } // LoopCheckingForDrainOfConnections()
  671. extern "C"
  672. BOOL
  673. WINAPI
  674. DllMain(
  675. HINSTANCE hDll,
  676. DWORD dwReason,
  677. LPVOID lpvReserved
  678. )
  679. {
  680. switch ( dwReason ) {
  681. case DLL_PROCESS_ATTACH:
  682. #ifdef _NO_TRACING_
  683. CREATE_DEBUG_PRINT_OBJECT( W3_MODULE_NAME );
  684. SET_DEBUG_FLAGS( DEBUG_ERROR );
  685. #else
  686. CREATE_DEBUG_PRINT_OBJECT( W3_MODULE_NAME, IisW3ServerGuid );
  687. #endif
  688. DBG_REQUIRE( DisableThreadLibraryCalls( hDll ) );
  689. INITIALIZE_CRITICAL_SECTION( &g_csServiceEntryLock );
  690. break;
  691. case DLL_PROCESS_DETACH:
  692. DELETE_DEBUG_PRINT_OBJECT();
  693. DeleteCriticalSection( &g_csServiceEntryLock );
  694. break;
  695. }
  696. return TRUE;
  697. } // DllMain
  698. APIERR
  699. W3_IIS_SERVICE::ReferenceW3Service(
  700. IN PVOID pService
  701. )
  702. /*++
  703. Routine Description:
  704. Reference W3 service
  705. Arguments:
  706. pService - Service to reference
  707. Returns:
  708. None
  709. --*/
  710. {
  711. InterlockedIncrement( &( ((PW3_IIS_SERVICE)pService)->m_cReferences ) );
  712. return ERROR_SUCCESS;
  713. }
  714. APIERR
  715. W3_IIS_SERVICE::DereferenceW3Service(
  716. IN PVOID pService
  717. )
  718. /*++
  719. Routine Description:
  720. De-reference W3 service
  721. Arguments:
  722. pService - Service to reference
  723. Returns:
  724. None
  725. --*/
  726. {
  727. if ( !InterlockedDecrement( &( ((PW3_IIS_SERVICE)pService)->m_cReferences ) ) )
  728. {
  729. return TerminateServicePhase2( pService );
  730. }
  731. else if ( ((PW3_IIS_SERVICE)pService)->QueryCurrentServiceState() == SERVICE_STOP_PENDING )
  732. {
  733. return ERROR_IO_PENDING;
  734. }
  735. else
  736. {
  737. return ERROR_SUCCESS;
  738. }
  739. }