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

1153 lines
32 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 IISADMIN Service.
  8. Functions exported by this module:
  9. ServiceEntry
  10. FILE HISTORY:
  11. michth - created
  12. */
  13. #include <nt.h>
  14. #include <ntrtl.h>
  15. #include <nturtl.h>
  16. #define DEFAULT_TRACE_FLAGS (DEBUG_ERROR)
  17. #include <dbgutil.h>
  18. #include <apiutil.h>
  19. #include <loadadm.hxx>
  20. #include <ole2.h>
  21. #include <inetsvcs.h>
  22. #include <ntsec.h>
  23. #include <iadmext.h>
  24. #include <string.hxx>
  25. #include <admsub.hxx>
  26. #include <imd.h>
  27. #include <irtlmisc.h>
  28. #include "mdwriter.hxx"
  29. #define IISADMIN_SERVICE_NAME TEXT("IISADMIN")
  30. #define QueryServiceName() IISADMIN_SERVICE_NAME
  31. #define NULL_SERVICE_STATUS_HANDLE ((SERVICE_STATUS_HANDLE)NULL)
  32. #define IISADMIN_SVC_KEY "SYSTEM\\CurrentControlSet\\Services\\IISADMIN"
  33. #define IISADMIN_STARTUP_WAITHINT_VALUE "StartupWaitHintInMilliseconds"
  34. //
  35. // Note: Due to how the system starts up, we can not have another thread that lies to the SCM
  36. // and tells it we are still starting, so instead we are going to have a hard coded startup time
  37. // limit of 3 minutes. However, there is also a registry key that can override this value if we
  38. // ever need to bump the start time limit to a larger value.
  39. //
  40. #define SERVICE_START_WAIT_HINT (180000) // milliseconds = 180 seconds = 3 minutes
  41. //
  42. // For shutdown it is fine to have the thread lie-ing to the SCM because it will not block any
  43. // vital system operations ( like startup )
  44. //
  45. #define SERVICE_STOP_WAIT_HINT (10000) // milliseconds = 10 seconds
  46. #define SERVICE_UPDATE_STATUS (9000) // milliseconds = 9 seconds (must be less that the stop wait hint)
  47. //
  48. // Default timeout for SaveMetabase
  49. //
  50. #define MB_SAVE_TIMEOUT (10000) // milliseconds
  51. DECLARE_PLATFORM_TYPE();
  52. SERVICE_STATUS g_svcStatus;
  53. SERVICE_STATUS_HANDLE g_hsvcStatus;
  54. HANDLE g_hShutdownEvent = NULL;
  55. HANDLE g_hSCMNotifyThread = NULL;
  56. HANDLE g_hSCMNotifyEvent = NULL;
  57. //
  58. // Debugging stuff
  59. //
  60. DECLARE_DEBUG_PRINTS_OBJECT();
  61. typedef struct _THREAD_PARAMS
  62. {
  63. HANDLE hInitEvent;
  64. BOOL bInitSuccess;
  65. } THREAD_PARAMS, *PTHREAD_PARAMS;
  66. extern "C"
  67. {
  68. BOOL
  69. WINAPI
  70. DLLEntry(
  71. HINSTANCE hDll,
  72. DWORD dwReason,
  73. LPVOID );
  74. }
  75. DWORD
  76. GetStartupWaitHint();
  77. VOID
  78. StartUpIndicateClientActivity(VOID);
  79. DWORD
  80. SCMNotifyThread(
  81. PVOID pv);
  82. BOOL
  83. WINAPI
  84. DLLEntry(
  85. HINSTANCE ,
  86. DWORD dwReason,
  87. LPVOID )
  88. /*++
  89. Routine Description:
  90. DLL entrypoint.
  91. Arguments:
  92. hDLL - Instance handle.
  93. Reason - The reason the entrypoint was called.
  94. DLL_PROCESS_ATTACH
  95. DLL_PROCESS_DETACH
  96. DLL_THREAD_ATTACH
  97. DLL_THREAD_DETACH
  98. Reserved - Reserved.
  99. Return Value:
  100. BOOL - TRUE if the action succeeds.
  101. --*/
  102. {
  103. BOOL bReturn = TRUE;
  104. switch ( dwReason )
  105. {
  106. case DLL_PROCESS_ATTACH:
  107. INITIALIZE_PLATFORM_TYPE();
  108. CREATE_DEBUG_PRINT_OBJECT( "iisadmin" );
  109. LOAD_DEBUG_FLAGS_FROM_REG_STR( "System\\CurrentControlSet\\Services\\iisadmin\\Parameters", 0 );
  110. //
  111. // Create shutdown event.
  112. //
  113. g_hShutdownEvent = IIS_CREATE_EVENT( "g_hShutdownEvent",
  114. &g_hShutdownEvent,
  115. TRUE, // fManualReset
  116. FALSE ); // fInitialState
  117. if( g_hShutdownEvent == NULL )
  118. {
  119. // Last Error is all ready set because the create event failed.
  120. return FALSE;
  121. }
  122. break;
  123. case DLL_PROCESS_DETACH:
  124. CloseHandle(g_hShutdownEvent);
  125. g_hShutdownEvent = NULL;
  126. DELETE_DEBUG_PRINT_OBJECT( );
  127. break;
  128. default:
  129. break;
  130. }
  131. return bReturn;
  132. }
  133. DWORD
  134. ReportServiceStatus( VOID)
  135. /*++
  136. Description:
  137. Wraps the call to SetServiceStatus() function.
  138. Prints the service status data if need be
  139. Arguments:
  140. None
  141. Returns:
  142. NO_ERROR if successful. other Win32 error code on failure.
  143. If successfull the new status has been reported to the service
  144. controller.
  145. --*/
  146. {
  147. DWORD err = NO_ERROR;
  148. IF_DEBUG( DLL_SERVICE_INFO)
  149. {
  150. DBGPRINTF(( DBG_CONTEXT, "dwServiceType = %08lX\n",
  151. g_svcStatus.dwServiceType ));
  152. DBGPRINTF(( DBG_CONTEXT, "dwCurrentState = %08lX\n",
  153. g_svcStatus.dwCurrentState ));
  154. DBGPRINTF(( DBG_CONTEXT, "dwControlsAccepted = %08lX\n",
  155. g_svcStatus.dwControlsAccepted ));
  156. DBGPRINTF(( DBG_CONTEXT, "dwWin32ExitCode = %08lX\n",
  157. g_svcStatus.dwWin32ExitCode ));
  158. DBGPRINTF(( DBG_CONTEXT, "dwServiceSpecificExitCode = %08lX\n",
  159. g_svcStatus.dwServiceSpecificExitCode ));
  160. DBGPRINTF(( DBG_CONTEXT, "dwCheckPoint = %08lX\n",
  161. g_svcStatus.dwCheckPoint ));
  162. DBGPRINTF(( DBG_CONTEXT, "dwWaitHint = %08lX\n",
  163. g_svcStatus.dwWaitHint ));
  164. }
  165. if( !SetServiceStatus( g_hsvcStatus, &g_svcStatus ) )
  166. {
  167. err = GetLastError();
  168. }
  169. else
  170. {
  171. err = NO_ERROR;
  172. }
  173. return err;
  174. } // ReportServiceStatus()
  175. DWORD
  176. UpdateServiceStatus(
  177. IN DWORD dwState,
  178. IN DWORD dwWin32ExitCode,
  179. IN DWORD dwServiceSpecificExitCode,
  180. IN DWORD dwCheckPoint,
  181. IN DWORD dwWaitHint
  182. )
  183. /*++
  184. Description:
  185. Updates the local copy status of service controller status
  186. and reports it to the service controller.
  187. Arguments:
  188. dwState - New service state.
  189. dwWin32ExitCode - Service exit code.
  190. dwCheckPoint - Check point for lengthy state transitions.
  191. dwWaitHint - Wait hint for lengthy state transitions.
  192. Returns:
  193. NO_ERROR on success and returns Win32 error if failure.
  194. On success the status is reported to service controller.
  195. --*/
  196. {
  197. g_svcStatus.dwCurrentState = dwState;
  198. g_svcStatus.dwWin32ExitCode = dwWin32ExitCode;
  199. g_svcStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
  200. g_svcStatus.dwCheckPoint = dwCheckPoint;
  201. g_svcStatus.dwWaitHint = dwWaitHint;
  202. return ReportServiceStatus();
  203. } // UpdateServiceStatus()
  204. VOID
  205. InterrogateService( VOID )
  206. /*++
  207. Description:
  208. This function interrogates with the service status.
  209. Actually, nothing needs to be done here; the
  210. status is always updated after a service control.
  211. We have this function here to provide useful
  212. debug info.
  213. HISTORY:
  214. KeithMo 07-Mar-1993 Created.
  215. MuraliK 15-Nov-1994 Ported to Tcpsvcs.dll
  216. --*/
  217. {
  218. IF_DEBUG( DLL_SERVICE_INFO)
  219. {
  220. DBGPRINTF(( DBG_CONTEXT, "Interrogating service status for %s\n",
  221. QueryServiceName() ));
  222. }
  223. } // InterrogateService()
  224. VOID
  225. PauseService( VOID )
  226. /*++
  227. Description:
  228. This function pauses the service. When the service is paused,
  229. no new user sessions are to be accepted, but existing connections
  230. are not effected.
  231. This function must update the SERVICE_STATUS::dwCurrentState
  232. field before returning.
  233. Returns:
  234. None. If successful the service is paused.
  235. --*/
  236. {
  237. IF_DEBUG( DLL_SERVICE_INFO)
  238. {
  239. DBGPRINTF(( DBG_CONTEXT, "pausing service %s\n",
  240. QueryServiceName() ));
  241. }
  242. g_svcStatus.dwCurrentState = SERVICE_PAUSED;
  243. } // PauseService()
  244. VOID
  245. ContinueService( VOID )
  246. /*++
  247. Description:
  248. This function restarts ( continues) a paused service. This
  249. will return the service to the running state.
  250. This function must update the g_svcStatus.dwCurrentState
  251. field to running mode before returning.
  252. Returns:
  253. None. If successful then the service is running.
  254. --*/
  255. {
  256. IF_DEBUG( DLL_SERVICE_INFO)
  257. {
  258. DBGPRINTF(( DBG_CONTEXT, "continuing service %s\n",
  259. QueryServiceName() ));
  260. }
  261. g_svcStatus.dwCurrentState = SERVICE_RUNNING;
  262. } // ContinueService()
  263. VOID
  264. StopService( VOID )
  265. /*++
  266. Description:
  267. This function performs the shutdown on a service.
  268. This is called during system shutdown.
  269. This function is time constrained. The service controller gives a
  270. maximum of 20 seconds for shutdown for all active services.
  271. Only timely operations should be performed in this function.
  272. Returns:
  273. None. If successful, the service is shutdown.
  274. --*/
  275. {
  276. IF_DEBUG( DLL_SERVICE_INFO)
  277. {
  278. DBGPRINTF(( DBG_CONTEXT, "shutting down service %s\n",
  279. QueryServiceName() ));
  280. }
  281. // Transition the server status now to stop
  282. // pending so that net.exe sees that we are
  283. // planning on listening to them. This has to
  284. // be done before the set event, because the set
  285. // event will startup the other thread which will
  286. // start updating these values as well.
  287. // I considered if there was any other way for this
  288. // event to get set that would cause a race with this call.
  289. // If the exe exits on it's own while we are being asked to
  290. // stop, but have not started stopping the service yet, it is
  291. // possible, but this situation all ready exists even if you
  292. // are transitioning to paused or continue. So I did not worry
  293. // about it here.
  294. UpdateServiceStatus( SERVICE_STOP_PENDING,
  295. NO_ERROR,
  296. S_OK,
  297. 1,
  298. SERVICE_STOP_WAIT_HINT );
  299. SetEvent( g_hShutdownEvent );
  300. } // StopService()
  301. BOOL
  302. SaveMetabase( VOID )
  303. /*++
  304. Description:
  305. This function tells the metabase to save itself.
  306. Returns:
  307. If TRUE, the metabase has been saved.
  308. --*/
  309. {
  310. HRESULT hRes;
  311. METADATA_HANDLE mdhRoot;
  312. IMDCOM * pMDCom = NULL;
  313. hRes = CoCreateInstance( CLSID_MDCOM,
  314. NULL,
  315. CLSCTX_INPROC_SERVER,
  316. IID_IMDCOM,
  317. (void**) &pMDCom);
  318. if (SUCCEEDED(hRes))
  319. {
  320. hRes = pMDCom->ComMDInitialize();
  321. if (SUCCEEDED(hRes))
  322. {
  323. //
  324. // Try to lock the tree
  325. //
  326. hRes = pMDCom->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE,
  327. NULL,
  328. METADATA_PERMISSION_READ,
  329. MB_SAVE_TIMEOUT,
  330. &mdhRoot );
  331. //
  332. // If failed, then someone has a write handle open,
  333. // and there might be an inconsistent data state, so don't save.
  334. //
  335. if (SUCCEEDED(hRes))
  336. {
  337. //
  338. // Call metadata com api to save
  339. //
  340. hRes = pMDCom->ComMDSaveData(mdhRoot);
  341. pMDCom->ComMDCloseMetaObject(mdhRoot);
  342. }
  343. pMDCom->ComMDTerminate(TRUE);
  344. }
  345. pMDCom->Release();
  346. }
  347. if ( SUCCEEDED( hRes ))
  348. {
  349. return TRUE;
  350. }
  351. DBGPRINTF(( DBG_CONTEXT, "IISAdmin Service, SaveMetabase() failed, hr=%lu\n", hRes ));
  352. SetLastError( HRESULTTOWIN32( hRes ));
  353. return FALSE;
  354. } // SaveMetabase()
  355. VOID
  356. ServiceControlHandler(
  357. IN DWORD dwOpCode)
  358. /*++
  359. Description:
  360. This function received control requests from the service controller.
  361. It runs in the context of service controller's dispatcher thread and
  362. performs the requested function.
  363. ( Note: Avoid time consuming operations in this function.)
  364. Arguments:
  365. dwOpCode
  366. indicates the requested operation. This should be
  367. one of the SERVICE_CONTROL_* manifests.
  368. Returns:
  369. None. If successful, then the state of the service might be changed.
  370. Note:
  371. if an operation ( especially SERVICE_CONTROL_STOP) is very lengthy,
  372. then this routine should report a STOP_PENDING status and create
  373. a worker thread to do the dirty work. The worker thread would then
  374. perform the necessary work and for reporting timely wait hints and
  375. final SERVICE_STOPPED status.
  376. History:
  377. KeithMo 07-March-1993 Created
  378. MuraliK 15-Nov-1994 Generalized it for all services.
  379. --*/
  380. {
  381. //
  382. // Interpret the opcode.
  383. //
  384. switch( dwOpCode )
  385. {
  386. case SERVICE_CONTROL_INTERROGATE :
  387. InterrogateService();
  388. break;
  389. case SERVICE_CONTROL_STOP :
  390. DBGPRINTF(( DBG_CONTEXT, "IISAdmin Service received stop notice\n" ));
  391. StopService();
  392. break;
  393. case SERVICE_CONTROL_PAUSE :
  394. PauseService();
  395. break;
  396. case SERVICE_CONTROL_CONTINUE :
  397. ContinueService();
  398. break;
  399. case SERVICE_CONTROL_SHUTDOWN :
  400. #if 0
  401. //
  402. // On shutdown, service controller doesn't respect ordering so
  403. // this call can block here or force unloading of some stuff that
  404. // a subsequent service needs.
  405. StopService();
  406. #else
  407. //
  408. // Although we aren't cleanly shutting down everything, we want
  409. // to at least make sure the metabase has been saved.
  410. //
  411. DBGPRINTF(( DBG_CONTEXT, "IISAdmin Service saving metabase\n" ));
  412. SaveMetabase();
  413. DBGPRINTF(( DBG_CONTEXT, "IISAdmin Service IGNORING shutdown notice\n" ));
  414. #endif
  415. break;
  416. default :
  417. DBGPRINTF(( DBG_CONTEXT, "Unrecognized Service Opcode %lu\n",
  418. dwOpCode ));
  419. break;
  420. }
  421. //
  422. // Report the current service status back to the Service
  423. // Controller. The workers called to implement the OpCodes
  424. // should set the g_svcStatus.dwCurrentState field if
  425. // the service status changed.
  426. //
  427. if ((dwOpCode != SERVICE_CONTROL_STOP) && (dwOpCode != SERVICE_CONTROL_SHUTDOWN))
  428. {
  429. // there is a race condition between this thread and the main thread, which
  430. // was kicked off in StopService.
  431. // The dll can get unloaded while this call is in progress.
  432. // Main thread reports status anyways, so don't report it.
  433. ReportServiceStatus();
  434. }
  435. } // ServiceControlHandler()
  436. //
  437. // While we are in the process of shutting down the timer
  438. // code will call us so we can let the SCM no that we are still
  439. // alive.
  440. //
  441. VOID CALLBACK ShutdownCallback(
  442. PVOID ,
  443. BOOLEAN )
  444. {
  445. UpdateServiceStatus( SERVICE_STOP_PENDING,
  446. 0,
  447. 0,
  448. g_svcStatus.dwCheckPoint + 1,
  449. SERVICE_STOP_WAIT_HINT );
  450. }
  451. BOOL
  452. StartThread(
  453. IN LPTHREAD_START_ROUTINE pStartAddress,
  454. OUT PHANDLE phThread)
  455. {
  456. BOOL bReturn = FALSE;
  457. HANDLE hThread = NULL;
  458. DWORD dwThreadID;
  459. DWORD dwWaitReturn;
  460. THREAD_PARAMS tpParams;
  461. tpParams.bInitSuccess = FALSE;
  462. tpParams.hInitEvent = IIS_CREATE_EVENT( "THREAD_PARAMS::hInitEvent",
  463. &tpParams,
  464. TRUE, // fManualReset
  465. FALSE ); // fInitialState
  466. if( tpParams.hInitEvent != NULL )
  467. {
  468. hThread = CreateThread( NULL,
  469. 0,
  470. pStartAddress,
  471. (PVOID)&tpParams,
  472. 0,
  473. &dwThreadID );
  474. if (hThread != NULL)
  475. {
  476. //
  477. // Wait for the init event.
  478. //
  479. dwWaitReturn = WaitForSingleObject( tpParams.hInitEvent,
  480. 10000 );
  481. bReturn = tpParams.bInitSuccess;
  482. }
  483. CloseHandle(tpParams.hInitEvent);
  484. }
  485. *phThread = hThread;
  486. return(bReturn);
  487. }
  488. VOID
  489. ServiceEntry(
  490. DWORD ,
  491. LPWSTR [],
  492. PTCPSVCS_GLOBAL_DATA pGlobalData)
  493. /*++
  494. Routine:
  495. This is the "real" entrypoint for the service. When
  496. the Service Controller dispatcher is requested to
  497. start a service, it creates a thread that will begin
  498. executing this routine.
  499. Arguments:
  500. cArgs - Number of command line arguments to this service.
  501. pArgs - Pointers to the command line arguments.
  502. Returns:
  503. None. Does not return until service is stopped.
  504. --*/
  505. {
  506. DWORD err = NO_ERROR;
  507. HRESULT hr = S_OK;
  508. HANDLE hShutdownCallbackTimer = NULL;
  509. HANDLE hMDWriterThread = NULL;
  510. RPC_STATUS rpcStatus;
  511. BOOL bTempFlag = FALSE;
  512. DWORD dwWait;
  513. BOOL fComInitialized = FALSE;
  514. //
  515. // We are running as a service, tell the SCM what we are doing.
  516. //
  517. //
  518. // First initalize the global status structure. This shouldn't be used
  519. // if we are not running as a service, but since this is legacy code I
  520. // will continue to initialize this here.
  521. //
  522. g_svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  523. g_svcStatus.dwCurrentState = SERVICE_STOPPED;
  524. g_svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
  525. | SERVICE_ACCEPT_PAUSE_CONTINUE
  526. | SERVICE_ACCEPT_SHUTDOWN;
  527. g_svcStatus.dwWin32ExitCode = NO_ERROR;
  528. g_svcStatus.dwServiceSpecificExitCode = NO_ERROR;
  529. g_svcStatus.dwCheckPoint = 0;
  530. g_svcStatus.dwWaitHint = 0;
  531. //
  532. // We are running as a service then we need to register
  533. // with SCM.
  534. //
  535. g_hsvcStatus = RegisterServiceCtrlHandler( QueryServiceName(),
  536. ServiceControlHandler );
  537. //
  538. // Register the Control Handler routine.
  539. //
  540. if( g_hsvcStatus == NULL_SERVICE_STATUS_HANDLE )
  541. {
  542. hr = HRESULT_FROM_WIN32( GetLastError() );
  543. goto Cleanup;
  544. }
  545. //
  546. // Update the service status. (This will not update
  547. // the service if we are not running as a service, but
  548. // it will adjust the global service status
  549. //
  550. err = UpdateServiceStatus( SERVICE_START_PENDING,
  551. NO_ERROR,
  552. S_OK,
  553. 1,
  554. GetStartupWaitHint() );
  555. if( err != NO_ERROR )
  556. {
  557. hr = HRESULT_FROM_WIN32(err);
  558. goto Cleanup;
  559. }
  560. InitializeIISRTL();
  561. //
  562. // Do the OLE security hack to setup the NT desktop
  563. //
  564. hr = InitDesktopWinsta();
  565. if ( FAILED( hr ) )
  566. {
  567. goto Cleanup;
  568. }
  569. hr = CoInitializeEx( NULL, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE );
  570. if ( FAILED( hr ) )
  571. {
  572. // This should be the 1st call in the thread.
  573. DBG_ASSERT( hr != RPC_E_CHANGED_MODE );
  574. goto Cleanup;
  575. }
  576. fComInitialized = TRUE;
  577. // Now initialize Com security
  578. // When called CoInitializeSecurity will make sure that the Ole Resolver connects to the OleSCM,
  579. // which will cache the IIS WindowStation\Desktop in the Resolver
  580. hr = InitComSecurity();
  581. if ( FAILED( hr ) )
  582. {
  583. goto Cleanup;
  584. }
  585. // Revert back to the service winsta\desktop
  586. hr = RevertToServiceDesktopWinsta();
  587. if ( FAILED( hr ) )
  588. {
  589. goto Cleanup;
  590. }
  591. // Initalize ABO
  592. // This should happen before any other pieces
  593. // of code attempt to use the metabase.
  594. hr = InitComAdmindata();
  595. if( FAILED( hr ) )
  596. {
  597. DBGPRINTF(( DBG_CONTEXT,
  598. "Failed to initialize ABO 0x%08x\n",
  599. hr ));
  600. goto Cleanup;
  601. }
  602. //
  603. // Initialize IIS metabase writer.
  604. //
  605. hr = InitializeMDWriter( &hMDWriterThread );
  606. if( FAILED( hr ) )
  607. {
  608. DBGPRINTF(( DBG_CONTEXT,
  609. "Failed to initialize IIS metabase writer %x\n",
  610. hr ));
  611. goto Cleanup;
  612. }
  613. // if this is a dc then wait for it to be ready before starting extensions
  614. // the reason for this is that the svcext.dll will try to create a user accounts
  615. // but it cannot do this on a dc, unless the dc is ready.
  616. //
  617. // this is also needed in the case where this is not a DC.
  618. // when a replica DC gets demoted, the server will restart
  619. // as a server not a DC and the create user/sync iwam stuff
  620. // needs to run and can timeout the SCM
  621. g_hSCMNotifyEvent = IIS_CREATE_EVENT( "g_hSCMNotifyEvent",
  622. &g_hSCMNotifyEvent,
  623. TRUE, // fManualReset
  624. FALSE ); // fInitialState
  625. if( g_hSCMNotifyEvent == NULL )
  626. {
  627. hr = HRESULT_FROM_WIN32(GetLastError());
  628. goto Cleanup;
  629. }
  630. // start up thread to let scm know we are still alive
  631. // the StartServiceExtensions() function will take longer than
  632. // usual on a domain controlller
  633. bTempFlag = StartThread( SCMNotifyThread, &g_hSCMNotifyThread );
  634. hr = StartServiceExtensions();
  635. if ( FAILED( hr ) )
  636. {
  637. goto Cleanup;
  638. }
  639. if (bTempFlag)
  640. {
  641. if ( g_hSCMNotifyThread != NULL )
  642. {
  643. if(g_hSCMNotifyEvent)
  644. {
  645. SetEvent(g_hSCMNotifyEvent);
  646. }
  647. dwWait = WaitForSingleObject(g_hSCMNotifyThread,10000);
  648. if ( dwWait != WAIT_OBJECT_0 )
  649. {
  650. IIS_PRINTF((buff, "Wait for SCMNotifyThread death returns %d[err %d]\n",err, GetLastError()));
  651. }
  652. CloseHandle(g_hSCMNotifyThread);
  653. g_hSCMNotifyThread = NULL;
  654. }
  655. bTempFlag = FALSE;
  656. }
  657. StartUpIndicateClientActivity();
  658. RegisterServiceExtensionCLSIDs();
  659. //
  660. // Start service RPC listening
  661. //
  662. if( pGlobalData != NULL )
  663. {
  664. rpcStatus = pGlobalData->StartRpcServerListen();
  665. if( rpcStatus != RPC_S_OK )
  666. {
  667. hr = HRESULT_FROM_WIN32( rpcStatus );
  668. goto Cleanup;
  669. }
  670. }
  671. //
  672. // Update the service status.
  673. // it is officially running now.
  674. //
  675. err = UpdateServiceStatus( SERVICE_RUNNING,
  676. NO_ERROR,
  677. S_OK,
  678. 0,
  679. 0 );
  680. if( err != NO_ERROR )
  681. {
  682. hr = HRESULT_FROM_WIN32(err);
  683. goto Cleanup;
  684. }
  685. //
  686. // Wait for the shutdown event.
  687. //
  688. err = WaitForSingleObject( g_hShutdownEvent,
  689. INFINITE );
  690. if ( err != WAIT_OBJECT_0 )
  691. {
  692. //
  693. // Error. Unable to wait for single object.
  694. //
  695. }
  696. //
  697. // Stop time. Tell the Service Controller that we're stopping,
  698. // then terminate the various service components.
  699. //
  700. err = UpdateServiceStatus( SERVICE_STOP_PENDING,
  701. NO_ERROR,
  702. S_OK,
  703. 1,
  704. SERVICE_STOP_WAIT_HINT );
  705. if ( err != NO_ERROR )
  706. {
  707. hr = HRESULT_FROM_WIN32(err);
  708. //
  709. // The event has already been logged.
  710. //
  711. }
  712. //
  713. // Now setup a timer callback function that will tell
  714. // the service we are still stopping. Note, the COM
  715. // thread will all ready be stopping too, but it won't
  716. // setup the callback function. Since both this routine
  717. // and the com thread routine must finish before the
  718. // timer is cancelled, it is fine that we just create it
  719. // here.
  720. //
  721. DBG_ASSERT ( hShutdownCallbackTimer == NULL );
  722. if ( !CreateTimerQueueTimer( &hShutdownCallbackTimer,
  723. NULL, // handle to timer queue, use default queue
  724. &ShutdownCallback,
  725. NULL,
  726. SERVICE_UPDATE_STATUS,
  727. SERVICE_UPDATE_STATUS,
  728. WT_EXECUTEINIOTHREAD) )
  729. {
  730. //
  731. // Spew out that there was an error, but don't propogate
  732. // the error, we should still try and shutdown.
  733. //
  734. DBGPRINTF(( DBG_CONTEXT,
  735. "Failed to create the timer queue for shutdown %x\n",
  736. HRESULT_FROM_WIN32(GetLastError()) ));
  737. }
  738. Cleanup:
  739. //
  740. // Stop service RPC listening
  741. //
  742. if( pGlobalData != NULL )
  743. {
  744. pGlobalData->StopRpcServerListen();
  745. }
  746. StopServiceExtensions();
  747. //
  748. // Terminate IIS metabase wirter
  749. //
  750. if( hMDWriterThread != NULL )
  751. {
  752. TerminateMDWriter( hMDWriterThread );
  753. CloseHandle( hMDWriterThread );
  754. hMDWriterThread = NULL;
  755. }
  756. // This will only terminate the metabase if
  757. // it was initialized in the first place.
  758. TerminateComAdmindata();
  759. TerminateIISRTL();
  760. if ( fComInitialized )
  761. {
  762. CoUninitialize();
  763. }
  764. // Close the IIS windows station and desktop
  765. ShutdownDesktopWinsta();
  766. //
  767. // Now tell the SCM that we have shutdown the service
  768. // and mean it!!
  769. //
  770. if ( g_hsvcStatus != NULL_SERVICE_STATUS_HANDLE )
  771. {
  772. DWORD ExitErr = NO_ERROR;
  773. //
  774. // Setup the error codes as they will be reported back
  775. // to the SCM.
  776. //
  777. if ( FAILED ( hr ) )
  778. {
  779. if ( HRESULT_FACILITY( hr ) == FACILITY_WIN32 )
  780. {
  781. ExitErr = HRESULT_CODE ( hr );
  782. }
  783. else
  784. {
  785. ExitErr = ERROR_SERVICE_SPECIFIC_ERROR;
  786. }
  787. }
  788. //
  789. // If the shutdown timer is still in play we need to
  790. // shut it down before we start the service. Note that
  791. // we should never have both timers running at the same
  792. // time.
  793. //
  794. if ( hShutdownCallbackTimer )
  795. {
  796. //
  797. // Ping the server for more time, so we know we will
  798. // have enough time to shutdown the callback timer before
  799. // we end the service.
  800. //
  801. UpdateServiceStatus( SERVICE_STOP_PENDING,
  802. NO_ERROR,
  803. S_OK,
  804. g_svcStatus.dwCheckPoint + 1,
  805. SERVICE_STOP_WAIT_HINT );
  806. //
  807. // Stop the callback function.
  808. //
  809. if ( !DeleteTimerQueueTimer( NULL,
  810. hShutdownCallbackTimer,
  811. INVALID_HANDLE_VALUE ) )
  812. {
  813. //
  814. // Spew out that there was an error, but don't propogate
  815. // the error, we should still try and shutdown.
  816. //
  817. DBGPRINTF(( DBG_CONTEXT,
  818. "Failed to delete the timer queue for shutdown %x\n",
  819. HRESULT_FROM_WIN32(GetLastError()) ));
  820. }
  821. hShutdownCallbackTimer = NULL;
  822. }
  823. //
  824. // Now let SCM know that we are done.
  825. //
  826. UpdateServiceStatus( SERVICE_STOPPED,
  827. ExitErr,
  828. hr,
  829. 0,
  830. 0 );
  831. }
  832. } // ServiceEntry()
  833. /***************************************************************************++
  834. Routine Description:
  835. Checks the registry to see if there is an override set for the
  836. iisadmin startup time limit. If there is we use that value, if there
  837. is not we will use the default 3 minutes.
  838. Arguments:
  839. None.
  840. Return Value:
  841. DWORD - time to tell scm to wait for our startup.
  842. --***************************************************************************/
  843. DWORD
  844. GetStartupWaitHint(VOID)
  845. {
  846. HKEY hKey;
  847. DWORD dwType;
  848. DWORD cbData;
  849. DWORD dwWaitHint;
  850. DWORD dwActualWaitHint = SERVICE_START_WAIT_HINT;
  851. //
  852. // Read the registry key to see if we are trying to
  853. // override the startup wait hint limit. If there are
  854. // any problems just return the default.
  855. //
  856. if ( !RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  857. IISADMIN_SVC_KEY,
  858. 0,
  859. KEY_QUERY_VALUE,
  860. &hKey ) )
  861. {
  862. cbData = sizeof( DWORD );
  863. if( !RegQueryValueEx( hKey,
  864. IISADMIN_STARTUP_WAITHINT_VALUE,
  865. NULL,
  866. &dwType,
  867. ( LPBYTE )&dwWaitHint,
  868. &cbData ) )
  869. {
  870. if( dwType == REG_DWORD && dwWaitHint != 0 )
  871. {
  872. dwActualWaitHint = dwWaitHint;
  873. }
  874. }
  875. RegCloseKey( hKey );
  876. }
  877. return dwActualWaitHint;
  878. }
  879. VOID
  880. StartUpIndicateClientActivity(VOID)
  881. {
  882. if (g_svcStatus.dwCurrentState == SERVICE_START_PENDING)
  883. {
  884. UpdateServiceStatus( SERVICE_START_PENDING,
  885. 0,
  886. 0,
  887. g_svcStatus.dwCheckPoint + 1,
  888. GetStartupWaitHint());
  889. }
  890. }
  891. DWORD
  892. SCMNotifyThread(
  893. PVOID pv)
  894. {
  895. DWORD dwWaitReturn = WAIT_TIMEOUT;
  896. BOOL bInitSucceeded = FALSE;
  897. DWORD dwCount = 0;
  898. PTHREAD_PARAMS ptpParams = (PTHREAD_PARAMS)pv;
  899. const DWORD dwMaxTime = 1000 * 60 * 3; // max wait is 3 minutes
  900. const DWORD dwTimeOut = 10000; // check every 10 seconds
  901. bInitSucceeded = ptpParams->bInitSuccess = TRUE;
  902. SetEvent(ptpParams->hInitEvent);
  903. do
  904. {
  905. // tell SCM we're still alive
  906. StartUpIndicateClientActivity();
  907. // wait 10 seconds
  908. dwWaitReturn = WaitForSingleObject(g_hSCMNotifyEvent,dwTimeOut);
  909. dwCount=dwCount+dwTimeOut;
  910. }
  911. while ( (dwWaitReturn != WAIT_OBJECT_0) && dwCount < dwMaxTime );
  912. return 0;
  913. }
  914. /************************ End Of File ************************/