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.

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