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.

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