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.

765 lines
19 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. scmmanager.cxx
  5. Abstract:
  6. Manage SCM
  7. Author:
  8. Bilal Alam (balam) 6-June-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. W3SSL.DLL
  13. --*/
  14. #include "precomp.hxx"
  15. SCM_MANAGER::~SCM_MANAGER( VOID )
  16. {
  17. }
  18. HRESULT
  19. SCM_MANAGER::Initialize(
  20. VOID
  21. )
  22. /*++
  23. Routine Description:
  24. Initialize SCM_MANAGER
  25. Arguments:
  26. pszServiceName - Name of service
  27. pfnShutdown - Function to call on service shutdown
  28. Return Value:
  29. HRESULT
  30. --*/
  31. {
  32. HKEY hKey;
  33. DWORD dwError = NO_ERROR;
  34. BOOL fRet = FALSE;
  35. //
  36. // initialize critical section
  37. //
  38. fRet = InitializeCriticalSectionAndSpinCount(
  39. &_csSCMLock,
  40. 0x80000000 /* precreate event */ |
  41. IIS_DEFAULT_CS_SPIN_COUNT );
  42. if ( !fRet )
  43. {
  44. HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
  45. DBGPRINTF(( DBG_CONTEXT,
  46. "Error calling InitializeCriticalSectionAndSpinCount(). hr = %x\n",
  47. hr ));
  48. return hr;
  49. }
  50. _fInitcsSCMLock = TRUE;
  51. //
  52. // set default value
  53. //
  54. _dwStartupWaitHint = HTTPFILTER_SERVICE_STARTUP_WAIT_HINT;
  55. _dwStopWaitHint = HTTPFILTER_SERVICE_STOP_WAIT_HINT;
  56. //
  57. // read wait hint from registry
  58. //
  59. dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  60. REGISTRY_KEY_HTTPFILTER_PARAMETERS_W,
  61. 0,
  62. KEY_READ,
  63. &hKey );
  64. if ( dwError == NO_ERROR )
  65. {
  66. DWORD dwValue = 0;
  67. DWORD dwType = 0;
  68. DWORD cbData = 0;
  69. DBG_ASSERT( hKey != NULL );
  70. cbData = sizeof( dwValue );
  71. dwError = RegQueryValueEx( hKey,
  72. REGISTRY_VALUE_HTTPFILTER_STARTUP_WAIT_HINT,
  73. NULL,
  74. &dwType,
  75. (LPBYTE) &dwValue,
  76. &cbData );
  77. if ( dwError == NO_ERROR && dwType == REG_DWORD )
  78. {
  79. _dwStartupWaitHint = dwValue;
  80. }
  81. cbData = sizeof( dwValue );
  82. dwError = RegQueryValueEx( hKey,
  83. REGISTRY_VALUE_HTTPFILTER_STOP_WAIT_HINT,
  84. NULL,
  85. &dwType,
  86. (LPBYTE) &dwValue,
  87. &cbData );
  88. if ( dwError == NO_ERROR && dwType == REG_DWORD )
  89. {
  90. _dwStopWaitHint = dwValue;
  91. }
  92. RegCloseKey( hKey );
  93. }
  94. //
  95. // Open SCM
  96. //
  97. _hService = RegisterServiceCtrlHandlerEx( _szServiceName,
  98. GlobalServiceControlHandler,
  99. this );
  100. if ( _hService == NULL )
  101. {
  102. HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
  103. DBGPRINTF(( DBG_CONTEXT,
  104. "Error calling RegisterServiceCtrlHandlerEx(). hr = %x\n",
  105. hr ));
  106. return hr;
  107. }
  108. //
  109. // create the event object. The control handler function signals
  110. // this event when it receives the "stop" control code.
  111. //
  112. _hServiceStopEvent = CreateEvent( NULL, // no security attributes
  113. FALSE, // manual reset event
  114. FALSE, // not-signalled
  115. NULL ); // no name
  116. if ( _hServiceStopEvent == NULL )
  117. {
  118. HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
  119. DBGPRINTF(( DBG_CONTEXT,
  120. "Error in CreateEvent(). hr = %x\n",
  121. hr ));
  122. return hr;
  123. }
  124. //
  125. // create the event object. The control handler function signals
  126. // this event when it receives the "pause" control code.
  127. //
  128. _hServicePauseEvent = CreateEvent( NULL, // no security attributes
  129. FALSE, // manual reset event
  130. FALSE, // not-signalled
  131. NULL ); // no name
  132. if ( _hServicePauseEvent == NULL )
  133. {
  134. HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
  135. DBGPRINTF(( DBG_CONTEXT,
  136. "Error in CreateEvent(). hr = %x\n",
  137. hr ));
  138. return hr;
  139. }
  140. //
  141. // create the event object. The control handler function signals
  142. // this event when it receives the "continue" control code.
  143. //
  144. _hServiceContinueEvent = CreateEvent( NULL, // no security attributes
  145. FALSE, // manual reset event
  146. FALSE, // not-signalled
  147. NULL ); // no name
  148. if ( _hServiceContinueEvent == NULL )
  149. {
  150. HRESULT hr = HRESULT_FROM_WIN32( GetLastError() );
  151. DBGPRINTF(( DBG_CONTEXT,
  152. "Error in CreateEvent(). hr = %x\n",
  153. hr ));
  154. return hr;
  155. }
  156. return NO_ERROR;
  157. }
  158. VOID
  159. SCM_MANAGER::Terminate(
  160. VOID
  161. )
  162. /*++
  163. Routine Description:
  164. Cleanup
  165. Arguments:
  166. None
  167. Return Value:
  168. HRESULT
  169. --*/
  170. {
  171. if ( _hServiceStopEvent != NULL )
  172. {
  173. CloseHandle( _hServiceStopEvent );
  174. _hServiceStopEvent = NULL;
  175. }
  176. if ( _hServicePauseEvent != NULL )
  177. {
  178. CloseHandle( _hServicePauseEvent );
  179. _hServicePauseEvent = NULL;
  180. }
  181. if ( _hServiceContinueEvent != NULL )
  182. {
  183. CloseHandle( _hServiceContinueEvent );
  184. _hServiceContinueEvent = NULL;
  185. }
  186. if ( _fInitcsSCMLock )
  187. {
  188. DeleteCriticalSection( &_csSCMLock );
  189. _fInitcsSCMLock = FALSE;
  190. }
  191. }
  192. HRESULT
  193. SCM_MANAGER::IndicatePending(
  194. DWORD dwPendingState
  195. )
  196. /*++
  197. Routine Description:
  198. Indicate (peridically) that we starting/stopping
  199. Arguments:
  200. dwPendingState -
  201. Return Value:
  202. HRESULT
  203. --*/
  204. {
  205. DWORD dwWaitHint;
  206. if ( dwPendingState != SERVICE_START_PENDING &&
  207. dwPendingState != SERVICE_STOP_PENDING &&
  208. dwPendingState != SERVICE_CONTINUE_PENDING &&
  209. dwPendingState != SERVICE_PAUSE_PENDING )
  210. {
  211. DBG_ASSERT( FALSE );
  212. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  213. }
  214. if ( dwPendingState == SERVICE_START_PENDING ||
  215. dwPendingState == SERVICE_CONTINUE_PENDING )
  216. {
  217. dwWaitHint = _dwStartupWaitHint;
  218. }
  219. else
  220. {
  221. dwWaitHint = _dwStopWaitHint;
  222. }
  223. EnterCriticalSection( &_csSCMLock );
  224. if( _serviceStatus.dwCurrentState == dwPendingState )
  225. {
  226. //
  227. // if last reported status is the same as the one to report now
  228. // then increment the checkpoint
  229. //
  230. _serviceStatus.dwCheckPoint ++;
  231. }
  232. else
  233. {
  234. _serviceStatus.dwCheckPoint = 1;
  235. }
  236. _serviceStatus.dwCurrentState = dwPendingState;
  237. _serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  238. _serviceStatus.dwWin32ExitCode = NO_ERROR;
  239. _serviceStatus.dwWaitHint = dwWaitHint;
  240. SetServiceStatus( _hService,
  241. &_serviceStatus );
  242. LeaveCriticalSection( &_csSCMLock );
  243. return NO_ERROR;
  244. }
  245. HRESULT
  246. SCM_MANAGER::IndicateComplete(
  247. DWORD dwState,
  248. HRESULT hrErrorToReport
  249. )
  250. /*++
  251. Routine Description:
  252. Indicate the service has started/stopped
  253. This means stopping the periodic ping (if any)
  254. Arguments:
  255. dwState - new service state
  256. dwWin32Error - Win32 Error to report to SCM with completion
  257. Return Value:
  258. HRESULT
  259. --*/
  260. {
  261. HRESULT hr = E_FAIL;
  262. //
  263. // If error happened at the begining of the initialization
  264. // it is possible that Service status handle was not established yet.
  265. // In that case use the default reporting function
  266. //
  267. if ( !_fInitcsSCMLock || _hService == NULL )
  268. {
  269. SCM_MANAGER::ReportFatalServiceStartupError(
  270. HTTPFILTER_SERVICE_NAME_W,
  271. ( FAILED( hrErrorToReport ) ?
  272. WIN32_FROM_HRESULT( hrErrorToReport ) :
  273. NO_ERROR ) );
  274. return S_OK;
  275. }
  276. if ( dwState != SERVICE_RUNNING &&
  277. dwState != SERVICE_STOPPED &&
  278. dwState != SERVICE_PAUSED )
  279. {
  280. DBG_ASSERT( FALSE );
  281. return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
  282. }
  283. EnterCriticalSection( &_csSCMLock );
  284. _serviceStatus.dwCurrentState = dwState;
  285. _serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  286. _serviceStatus.dwWin32ExitCode = ( FAILED( hrErrorToReport ) ?
  287. WIN32_FROM_HRESULT( hrErrorToReport ) :
  288. NO_ERROR );
  289. _serviceStatus.dwCheckPoint = 0;
  290. _serviceStatus.dwWaitHint = 0;
  291. if ( _serviceStatus.dwCurrentState == SERVICE_RUNNING )
  292. {
  293. _serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
  294. SERVICE_ACCEPT_PAUSE_CONTINUE |
  295. SERVICE_ACCEPT_SHUTDOWN;
  296. }
  297. DBG_ASSERT( _hService != NULL );
  298. if ( SetServiceStatus( _hService,
  299. &_serviceStatus ) )
  300. {
  301. hr = S_OK;
  302. }
  303. else
  304. {
  305. DBGPRINTF(( DBG_CONTEXT,
  306. "Address = %x\n",
  307. &_serviceStatus ));
  308. hr = HRESULT_FROM_WIN32( GetLastError() );
  309. }
  310. LeaveCriticalSection( &_csSCMLock );
  311. return hr;
  312. }
  313. DWORD
  314. SCM_MANAGER::ControlHandler(
  315. DWORD dwControlCode
  316. )
  317. /*++
  318. Routine Description:
  319. Handle SCM command
  320. Arguments:
  321. dwControlCode - SCM command
  322. Return Value:
  323. None
  324. --*/
  325. {
  326. switch( dwControlCode )
  327. {
  328. case SERVICE_CONTROL_STOP:
  329. case SERVICE_CONTROL_SHUTDOWN:
  330. if ( _serviceStatus.dwCurrentState == SERVICE_STOPPED )
  331. {
  332. UpdateServiceStatus();
  333. return NO_ERROR;
  334. }
  335. IndicatePending( SERVICE_STOP_PENDING );
  336. //
  337. // Initiate shutdown
  338. //
  339. DBG_ASSERT( _hServiceStopEvent );
  340. SetEvent( _hServiceStopEvent );
  341. break;
  342. case SERVICE_CONTROL_PAUSE:
  343. if ( _serviceStatus.dwCurrentState == SERVICE_PAUSED )
  344. {
  345. UpdateServiceStatus();
  346. return NO_ERROR;
  347. }
  348. IndicatePending( SERVICE_PAUSE_PENDING );
  349. //
  350. // Initiate pause
  351. //
  352. DBG_ASSERT( _hServicePauseEvent );
  353. SetEvent( _hServicePauseEvent );
  354. break;
  355. case SERVICE_CONTROL_CONTINUE:
  356. if ( _serviceStatus.dwCurrentState == SERVICE_RUNNING )
  357. {
  358. UpdateServiceStatus();
  359. return NO_ERROR;
  360. }
  361. IndicatePending( SERVICE_CONTINUE_PENDING );
  362. //
  363. // Initiate continue
  364. //
  365. DBG_ASSERT( _hServiceContinueEvent );
  366. SetEvent( _hServiceContinueEvent );
  367. break;
  368. case SERVICE_CONTROL_INTERROGATE:
  369. UpdateServiceStatus();
  370. break;
  371. default:
  372. break;
  373. }
  374. return NO_ERROR;
  375. }
  376. HRESULT
  377. SCM_MANAGER::RunService(
  378. VOID
  379. )
  380. /*++
  381. Routine Description:
  382. Executes the service (initialize, startup, stopping, reporting to SCM
  383. SERVICE implementation class that inherits from SCM_MANAGER
  384. must implement OnStart() and OnStop()
  385. Arguments:
  386. VOID
  387. Return Value:
  388. None
  389. --*/
  390. {
  391. HRESULT hr= E_FAIL;
  392. DWORD dwRet = 0;
  393. HANDLE events[2];
  394. hr = Initialize();
  395. if ( FAILED ( hr ) )
  396. {
  397. DBGPRINTF(( DBG_CONTEXT,
  398. "Error in SCM_MANAGER::Initialize(). hr = %x\n",
  399. hr ));
  400. goto Finished;
  401. }
  402. hr = IndicatePending( SERVICE_START_PENDING );
  403. if ( FAILED ( hr ) )
  404. {
  405. DBGPRINTF(( DBG_CONTEXT,
  406. "Error in SCM_MANAGER::IndicatePending(). hr = %x\n",
  407. hr ));
  408. goto Finished;
  409. }
  410. //
  411. // loop for SCM CONTINUE request
  412. //
  413. for(;;)
  414. {
  415. hr = OnServiceStart();
  416. if ( FAILED ( hr ) )
  417. {
  418. DBGPRINTF(( DBG_CONTEXT,
  419. "Error in OnStart(). hr = %x\n",
  420. hr ));
  421. goto Finished;
  422. }
  423. hr = IndicateComplete( SERVICE_RUNNING );
  424. if ( FAILED( hr ) )
  425. {
  426. DBGPRINTF(( DBG_CONTEXT,
  427. "Error in SCM_MANAGER::IndicateComplete(). hr = %x\n",
  428. hr ));
  429. goto Finished;
  430. }
  431. //
  432. // Wait till service stop is requested
  433. //
  434. events[0] = _hServiceStopEvent;
  435. events[1] = _hServicePauseEvent;
  436. dwRet = WaitForMultipleObjects( 2,
  437. events,
  438. FALSE,
  439. INFINITE );
  440. if ( dwRet == WAIT_OBJECT_0 )
  441. {
  442. // transition from RUNNING to STOPPED
  443. hr = OnServiceStop();
  444. if ( FAILED ( hr ) )
  445. {
  446. DBGPRINTF(( DBG_CONTEXT,
  447. "Error in OnStop(). hr = %x\n",
  448. hr ));
  449. goto Finished;
  450. }
  451. hr = S_OK;
  452. goto Finished;
  453. }
  454. else if ( dwRet == WAIT_OBJECT_0 + 1 )
  455. {
  456. // transition from RUNNING to PAUSED
  457. // this is the request to pause
  458. // we implement pause being similar to stop
  459. //
  460. hr = OnServiceStop();
  461. if ( FAILED ( hr ) )
  462. {
  463. DBGPRINTF(( DBG_CONTEXT,
  464. "Error in OnStop() when trying to pause. hr = %x\n",
  465. hr ));
  466. goto Finished;
  467. }
  468. hr = IndicateComplete( SERVICE_PAUSED,
  469. hr );
  470. hr = S_OK;
  471. }
  472. else
  473. {
  474. DBG_ASSERT( FALSE );
  475. goto Finished;
  476. }
  477. //
  478. // Wait till service stop is requested
  479. //
  480. events[0] = _hServiceStopEvent;
  481. events[1] = _hServiceContinueEvent;
  482. dwRet = WaitForMultipleObjects( 2,
  483. events,
  484. FALSE,
  485. INFINITE );
  486. if ( dwRet == WAIT_OBJECT_0 )
  487. {
  488. // transition from PAUSED to STOPPED
  489. // we are in stopped state already
  490. //
  491. goto Finished;
  492. }
  493. else if ( dwRet == WAIT_OBJECT_0 + 1 )
  494. {
  495. // transition from PAUSED to CONTINUED
  496. // while (TRUE) loop will handle the continue
  497. }
  498. else
  499. {
  500. DBG_ASSERT( FALSE );
  501. goto Finished;
  502. }
  503. }
  504. Finished:
  505. //
  506. // Error means that we must report SCM that service is stopping
  507. // SCM will be notified of error that occured
  508. // Note: even though IndicateComplete received HRESULT for error
  509. // SCM accepts only Win32 errors and it truncates the upper word
  510. // of HRESULT errors sent to it. Hence IndicateComplete
  511. // calls WIN32_FROM_HRESULT to convert hr, but that may mean
  512. // loss of error
  513. //
  514. hr = IndicateComplete( SERVICE_STOPPED,
  515. hr );
  516. if ( FAILED( hr ) )
  517. {
  518. DBGPRINTF(( DBG_CONTEXT,
  519. "Error in IndicateComplete(). hr = %x\n",
  520. hr ));
  521. }
  522. Terminate();
  523. return hr;
  524. }
  525. //static
  526. DWORD
  527. WINAPI
  528. SCM_MANAGER::GlobalServiceControlHandler(
  529. DWORD dwControl,
  530. DWORD /*dwEventType*/,
  531. LPVOID /*pEventData*/,
  532. LPVOID pServiceContext
  533. )
  534. /*++
  535. Routine Description:
  536. SCM callback passed to RegisterServiceCtrlHandlerEx
  537. Arguments:
  538. for details see LPHANDLER_FUNCTION_EX in MSDN
  539. Return Value:
  540. DWORD
  541. --*/
  542. {
  543. if ( pServiceContext == NULL )
  544. {
  545. return ERROR_SUCCESS;
  546. }
  547. SCM_MANAGER * pManager = reinterpret_cast<SCM_MANAGER*>(pServiceContext);
  548. DBG_ASSERT( pManager != NULL );
  549. return pManager->ControlHandler( dwControl );
  550. }
  551. VOID
  552. SCM_MANAGER::UpdateServiceStatus(
  553. BOOL fUpdateCheckpoint
  554. )
  555. /*++
  556. Routine Description:
  557. Resend the last serviceStatus
  558. Arguments:
  559. Return Value:
  560. None
  561. --*/
  562. {
  563. DBG_ASSERT( _hService != NULL );
  564. EnterCriticalSection( &_csSCMLock );
  565. if( fUpdateCheckpoint )
  566. {
  567. _serviceStatus.dwCheckPoint ++;
  568. }
  569. SetServiceStatus( _hService,
  570. &_serviceStatus );
  571. LeaveCriticalSection( &_csSCMLock );
  572. }
  573. //static
  574. VOID
  575. SCM_MANAGER::ReportFatalServiceStartupError(
  576. WCHAR * pszServiceName,
  577. DWORD dwError )
  578. {
  579. SERVICE_STATUS serviceStatus;
  580. ZeroMemory( &serviceStatus, sizeof( serviceStatus ) );
  581. SERVICE_STATUS_HANDLE hService =
  582. RegisterServiceCtrlHandlerEx( pszServiceName,
  583. GlobalServiceControlHandler,
  584. NULL /* indicates that nothing should be
  585. executed on callback*/ );
  586. if ( hService == NULL )
  587. {
  588. DBGPRINTF(( DBG_CONTEXT,
  589. "Failed to get Service Ctrl Handler Win32 = %d\n",
  590. GetLastError() ));
  591. return;
  592. }
  593. serviceStatus.dwCurrentState = SERVICE_STOPPED;
  594. serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  595. serviceStatus.dwWin32ExitCode = dwError;
  596. serviceStatus.dwCheckPoint = 0;
  597. serviceStatus.dwWaitHint = 0;
  598. if ( ! SetServiceStatus( hService,
  599. &serviceStatus ) )
  600. {
  601. DBGPRINTF(( DBG_CONTEXT,
  602. "Failed to set service status Win32 = %d\n",
  603. GetLastError() ));
  604. }
  605. //
  606. // per MSDN the Service Status handle doesn't need to be closed
  607. //
  608. hService = NULL;
  609. }