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.

650 lines
23 KiB

  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1999-2000 Microsoft Corporation
  4. //
  5. // Module Name:
  6. // CService.cpp
  7. //
  8. // Description:
  9. // Contains the definition of the CService class.
  10. //
  11. // Maintained By:
  12. // Vij Vasu (Vvasu) 08-MAR-2000
  13. //
  14. //////////////////////////////////////////////////////////////////////////////
  15. //////////////////////////////////////////////////////////////////////////////
  16. // Include Files
  17. //////////////////////////////////////////////////////////////////////////////
  18. // The precompiled header.
  19. #include "pch.h"
  20. // The header file for this class.
  21. #include "CService.h"
  22. // For the CStr class.
  23. #include "CStr.h"
  24. // For the CStatusReport class
  25. #include "CStatusReport.h"
  26. //////////////////////////////////////////////////////////////////////////////
  27. // Macros
  28. //////////////////////////////////////////////////////////////////////////////
  29. // String added to end of service name to get INF file section for create
  30. #define SERVICE_CREATE_SECTION_SUFFIX L"_Create"
  31. // String added to end of service name to get INF file section for cleanup
  32. #define SERVICE_CLEANUP_SECTION_SUFFIX L"_Cleanup"
  33. //////////////////////////////////////////////////////////////////////////////
  34. //++
  35. //
  36. // void
  37. // CService::Create()
  38. //
  39. // Description:
  40. // This function creates an entry for the service with the SCM. It does
  41. // this by using the SetupAPI to process service and registry related
  42. // entries in a section named <ServiceName>_Create in the INF file that
  43. // is passed in.
  44. //
  45. // For example, if this object represents the ClusSvc service, then,
  46. // when this function is called, the AddService and the AddReg entries
  47. // under the [ClusSvc_Create] section are processed in the INF file
  48. // whose handle is hInfHandleIn.
  49. //
  50. // Arguments:
  51. // hInfHandleIn
  52. // Handle to the INF file that contains the required sections
  53. //
  54. // Return Value:
  55. // None.
  56. //
  57. // Exceptions Thrown:
  58. // CRuntimeError
  59. // If any of the APIs fail.
  60. //
  61. //--
  62. //////////////////////////////////////////////////////////////////////////////
  63. void
  64. CService::Create(
  65. HINF hInfHandleIn
  66. )
  67. {
  68. BCATraceScope1( "Service Name = '%s'", m_strName.PszData() );
  69. LogMsg( "Attempting to create the '%s' service.", m_strName.PszData() );
  70. DWORD dwError = ERROR_SUCCESS;
  71. CStr strSectionName = m_strName + SERVICE_CREATE_SECTION_SUFFIX;
  72. // Process the service section
  73. if ( SetupInstallServicesFromInfSection(
  74. hInfHandleIn
  75. , strSectionName.PszData()
  76. , 0
  77. ) == FALSE
  78. )
  79. {
  80. dwError = TW32( GetLastError() );
  81. goto Cleanup;
  82. } // if: SetupInstallServicesFromInfSection failed
  83. // Process the registry keys.
  84. if ( SetupInstallFromInfSection(
  85. NULL // optional, handle of a parent window
  86. , hInfHandleIn // handle to the INF file
  87. , strSectionName.PszData() // name of the Install section
  88. , SPINST_REGISTRY // which lines to install from section
  89. , NULL // optional, key for registry installs
  90. , NULL // optional, path for source files
  91. , NULL // optional, specifies copy behavior
  92. , NULL // optional, specifies callback routine
  93. , NULL // optional, callback routine context
  94. , NULL // optional, device information set
  95. , NULL // optional, device info structure
  96. ) == FALSE
  97. )
  98. {
  99. dwError = TW32( GetLastError() );
  100. goto Cleanup;
  101. } // if: SetupInstallFromInfSection failed
  102. LogMsg( "The '%s' service has been successfully created.", m_strName.PszData() );
  103. Cleanup:
  104. if ( dwError != ERROR_SUCCESS )
  105. {
  106. BCATraceMsg1( "Setup API returned error %#08x while trying to create the service. Throwing exception.", dwError );
  107. LogMsg( "Error %#08x occurred while trying to create the '%s' service.", dwError, m_strName.PszData() );
  108. THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( dwError ), IDS_ERROR_SERVICE_CREATE );
  109. }
  110. } //*** CService::Create()
  111. //////////////////////////////////////////////////////////////////////////////
  112. //++
  113. //
  114. // void
  115. // CService::Cleanup()
  116. //
  117. // Description:
  118. // This function cleans up a service by deregistering it with the SCM and by
  119. // deleting any required registry entries. It does this by using the
  120. // SetupAPI to process service and registry related entries in a
  121. // section named <ServiceName>_Cleanup in the INF file that is passed in.
  122. //
  123. // For example, if this object represents the ClusSvc service, then,
  124. // when this function is called, the DelService and the DelReg entries
  125. // under the [ClusSvc_Cleanup] section are processed in the INF file
  126. // whose handle is hInfHandleIn.
  127. //
  128. // Arguments:
  129. // hInfHandleIn
  130. // Handle to the INF file that contains the required sections
  131. //
  132. // Return Value:
  133. // None.
  134. //
  135. // Exceptions Thrown:
  136. // CRuntimeError
  137. // If any of the APIs fail.
  138. //
  139. //--
  140. //////////////////////////////////////////////////////////////////////////////
  141. void
  142. CService::Cleanup(
  143. HINF hInfHandleIn
  144. )
  145. {
  146. BCATraceScope1( "Service Name = '%s'", m_strName.PszData() );
  147. LogMsg( "Attempting to clean up the '%s' service.", m_strName.PszData() );
  148. DWORD dwError = ERROR_SUCCESS;
  149. CStr strSectionName = m_strName + SERVICE_CLEANUP_SECTION_SUFFIX;
  150. // Process the service section
  151. if ( SetupInstallServicesFromInfSection(
  152. hInfHandleIn
  153. , strSectionName.PszData()
  154. , 0
  155. ) == FALSE
  156. )
  157. {
  158. dwError = TW32( GetLastError() );
  159. goto Cleanup;
  160. } // if: SetupInstallServicesFromInfSection failed
  161. // Process the registry keys.
  162. if ( SetupInstallFromInfSection(
  163. NULL // optional, handle of a parent window
  164. , hInfHandleIn // handle to the INF file
  165. , strSectionName.PszData() // name of the Install section
  166. , SPINST_REGISTRY // which lines to install from section
  167. , NULL // optional, key for registry installs
  168. , NULL // optional, path for source files
  169. , NULL // optional, specifies copy behavior
  170. , NULL // optional, specifies callback routine
  171. , NULL // optional, callback routine context
  172. , NULL // optional, device information set
  173. , NULL // optional, device info structure
  174. ) == FALSE
  175. )
  176. {
  177. dwError = TW32( GetLastError() );
  178. goto Cleanup;
  179. } // if: SetupInstallFromInfSection failed
  180. LogMsg( "The '%s' service has been successfully cleaned up.", m_strName.PszData() );
  181. Cleanup:
  182. if ( dwError != ERROR_SUCCESS )
  183. {
  184. BCATraceMsg1( "Setup API returned error %#08x while trying to clean up the service. Throwing exception.", dwError );
  185. LogMsg( "Error %#08x occurred while trying to clean up the '%s' service.", dwError, m_strName.PszData() );
  186. THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( dwError ), IDS_ERROR_SERVICE_CLEANUP );
  187. }
  188. } //*** CService::Cleanup()
  189. //////////////////////////////////////////////////////////////////////////////
  190. //++
  191. //
  192. // void
  193. // CService::Start()
  194. //
  195. // Description:
  196. // Instructs the SCM to start the service. If fWaitForServiceStartIn is
  197. // true, this function tests cQueryCountIn times to see if the service has
  198. // started, checking every ulQueryIntervalMilliSecIn milliseconds.
  199. //
  200. // fWaitForServiceStartIn is false, this function returns immediately.
  201. //
  202. // Arguments:
  203. // hServiceControlManagerIn
  204. // Handle to the service control manager.
  205. //
  206. // fWaitForServiceStartIn
  207. // If true, this function waits for the service to finish starting
  208. // before returning. The default value is true.
  209. //
  210. // ulQueryIntervalMilliSecIn
  211. // Number of milliseconds between checking to see if the service
  212. // has started. The default value is 500 milliseconds.
  213. // This argument is used only if fWaitForServiceStartIn is true.
  214. //
  215. // cQueryCountIn
  216. // The number of times this function will query the service to see
  217. // if it has started. An exception is thrown if the service is not
  218. // running even after querying it ulQueryCountIn times. The default
  219. // value is 10 times.
  220. // This argument is used only if fWaitForServiceStartIn is true.
  221. //
  222. // pStatusReportIn
  223. // A pointer to the status report that should be sent while waiting for
  224. // the service to start. This argument is NULL by default.
  225. //
  226. // Return Value:
  227. // None.
  228. //
  229. // Exceptions Thrown:
  230. // CRuntimeError
  231. // If any of the APIs fail.
  232. //
  233. // CConfigError
  234. // If the service is not running even after the timeout has expired.
  235. //
  236. //--
  237. //////////////////////////////////////////////////////////////////////////////
  238. void
  239. CService::Start(
  240. SC_HANDLE hServiceControlManagerIn
  241. , bool fWaitForServiceStartIn
  242. , ULONG ulQueryIntervalMilliSecIn
  243. , UINT cQueryCountIn
  244. , CStatusReport * pStatusReportIn
  245. )
  246. {
  247. BCATraceScope1( "Service Name = '%s'", m_strName.PszData() );
  248. LogMsg( "Attempting to start the '%s' service.", m_strName.PszData() );
  249. DWORD dwError = ERROR_SUCCESS;
  250. bool fStarted = false; // Has the service been started?
  251. UINT cNumberOfQueries; // The number of times we have queried the service.
  252. // Handle to the service.
  253. SmartSCMHandle sscmhServiceHandle(
  254. OpenService(
  255. hServiceControlManagerIn
  256. , m_strName.PszData()
  257. , SERVICE_START | SERVICE_QUERY_STATUS
  258. )
  259. );
  260. if ( sscmhServiceHandle.FIsInvalid() )
  261. {
  262. dwError = TW32( GetLastError() );
  263. BCATraceMsg1( "Error %#08x occurred while trying to open the service. Throwing exception.", dwError );
  264. goto Cleanup;
  265. } // if: the handle to the service could not be opened.
  266. // Try and start the service.
  267. if ( StartService( sscmhServiceHandle.HHandle(), 0, NULL ) == 0 )
  268. {
  269. dwError = GetLastError();
  270. if ( dwError == ERROR_SERVICE_ALREADY_RUNNING )
  271. {
  272. BCATraceMsg( "The service is already running." );
  273. // The service is already running. Change the error code to success.
  274. dwError = ERROR_SUCCESS;
  275. } // if: the service is already running.
  276. else
  277. {
  278. TW32( dwError );
  279. BCATraceMsg1( "Error %#08x occurred while trying to start the service. Throwing exception.", dwError );
  280. }
  281. // There is nothing else to do.
  282. goto Cleanup;
  283. } // if: an error occurred trying to start the service.
  284. // If we are here, then the service may not have started yet.
  285. // Has the caller requested that we wait for the service to start?
  286. if ( ! fWaitForServiceStartIn )
  287. {
  288. LogMsg( "Not waiting to see if the service has started or not." );
  289. BCATraceMsg( "Not waiting to see if the service has started or not." );
  290. goto Cleanup;
  291. } // if: no waiting is required.
  292. // We have to wait for the service to start.
  293. cNumberOfQueries = 0;
  294. do
  295. {
  296. SERVICE_STATUS ssStatus;
  297. ZeroMemory( &ssStatus, sizeof( ssStatus ) );
  298. // Query the service for its status.
  299. if ( QueryServiceStatus(
  300. sscmhServiceHandle.HHandle()
  301. , &ssStatus
  302. )
  303. == 0
  304. )
  305. {
  306. dwError = TW32( GetLastError() );
  307. BCATraceMsg1( "Error %#08x occurred while trying to query service status. Throwing exception.", dwError );
  308. break;
  309. } // if: we could not query the service for its status.
  310. // Check if the service has posted an error.
  311. if ( ssStatus.dwWin32ExitCode != ERROR_SUCCESS )
  312. {
  313. dwError = TW32( ssStatus.dwWin32ExitCode );
  314. if ( dwError == ERROR_SERVICE_SPECIFIC_ERROR )
  315. {
  316. BCATraceMsg( "This is a service specific error code." );
  317. dwError = TW32( ssStatus.dwServiceSpecificExitCode );
  318. }
  319. BCATraceMsg1( "The service has returned error %#08x to query service status. Throwing exception.", dwError );
  320. break;
  321. } // if: the service itself has posted an error.
  322. if ( ssStatus.dwCurrentState == SERVICE_RUNNING )
  323. {
  324. fStarted = true;
  325. break;
  326. } // if: the service is running.
  327. ++cNumberOfQueries;
  328. // Send a progress report if the caller had passed in a valid pointer.
  329. if ( pStatusReportIn != NULL )
  330. {
  331. pStatusReportIn->SendNextStep( S_OK );
  332. } // if: a status report needs to be sent while we wait
  333. // Wait for the specified time.
  334. Sleep( ulQueryIntervalMilliSecIn );
  335. }
  336. while ( cNumberOfQueries < cQueryCountIn ); // while: loop for the required number of queries
  337. if ( dwError != ERROR_SUCCESS )
  338. {
  339. goto Cleanup;
  340. } // if: something went wrong.
  341. if ( ! fStarted )
  342. {
  343. BCATraceMsg1( "The service could not be started. Throwing exception.", cQueryCountIn );
  344. THROW_CONFIG_ERROR( HRESULT_FROM_WIN32( TW32( ERROR_SERVICE_NOT_ACTIVE ) ), IDS_ERROR_SERVICE_START );
  345. } // if: the maximum number of queries have been made and the service is not running.
  346. Cleanup:
  347. if ( dwError != ERROR_SUCCESS )
  348. {
  349. LogMsg( "Error %#08x occurred trying to start the '%s' service.", dwError, m_strName.PszData() );
  350. THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( dwError ), IDS_ERROR_SERVICE_START );
  351. } // if: something has gone wrong
  352. else
  353. {
  354. LogMsg( "The '%s' service has been successfully started.", m_strName.PszData() );
  355. } // else: nothing went wrong.
  356. } //*** CService::Start()
  357. //////////////////////////////////////////////////////////////////////////////
  358. //++
  359. //
  360. // void
  361. // CService::Stop()
  362. //
  363. // Description:
  364. // Instructs the SCM to stop the service. If fWaitForServiceStop is
  365. // true, this function tests cQueryCountIn times to see if the service has
  366. // stopped, checking every ulQueryIntervalMilliSecIn milliseconds.
  367. //
  368. // Arguments:
  369. // hServiceControlManagerIn
  370. // Handle to the service control manager.
  371. //
  372. // ulQueryIntervalMilliSecIn
  373. // Number of milliseconds between checking to see if the service
  374. // has stopped. The default value is 500 milliseconds.
  375. //
  376. // cQueryCountIn
  377. // The number of times this function will query the service to see
  378. // if it has stopped. An exception is thrown if the service is not
  379. // running even after querying it ulQueryCountIn times. The default
  380. // value is 10 times.
  381. //
  382. // pStatusReportIn
  383. // A pointer to the status report that should be sent while waiting for
  384. // the service to stop. This argument is NULL by default.
  385. //
  386. // Return Value:
  387. // None.
  388. //
  389. // Exceptions Thrown:
  390. // CRuntimeError
  391. // If any of the APIs fail.
  392. //
  393. // CConfigError
  394. // If the service has not stopped even after the timeout has expired.
  395. //
  396. //--
  397. //////////////////////////////////////////////////////////////////////////////
  398. void
  399. CService::Stop(
  400. SC_HANDLE hServiceControlManagerIn
  401. , ULONG ulQueryIntervalMilliSecIn
  402. , UINT cQueryCountIn
  403. , CStatusReport * pStatusReportIn
  404. )
  405. {
  406. BCATraceScope( "" );
  407. DWORD dwError = ERROR_SUCCESS;
  408. SERVICE_STATUS ssStatus; // The service status structure.
  409. bool fStopped = false; // Has the service been stopped?
  410. UINT cNumberOfQueries = 0; // The number of times we have queried the service
  411. // (not including the initial state query).
  412. LogMsg( "Attempting to stop the '%s' service.", m_strName.PszData() );
  413. BCATraceMsg1( "Attempting to stop the '%s' service.", m_strName.PszData() );
  414. // Smart handle to the service being stopped.
  415. SmartSCMHandle sscmhServiceHandle(
  416. OpenService(
  417. hServiceControlManagerIn
  418. , m_strName.PszData()
  419. , SERVICE_STOP | SERVICE_QUERY_STATUS
  420. )
  421. );
  422. // Check if we could open a handle to the service.
  423. if ( sscmhServiceHandle.FIsInvalid() )
  424. {
  425. // We could not get a handle to the service.
  426. dwError = GetLastError();
  427. // Check if the service exists.
  428. if ( dwError == ERROR_SERVICE_DOES_NOT_EXIST )
  429. {
  430. // Nothing needs to be done here.
  431. BCATraceMsg1( "The '%s' service does not exist, so it is not running. Nothing needs to be done to stop it.", m_strName.PszData() );
  432. LogMsg( "The '%s' service does not exist, so it is not running. Nothing needs to be done to stop it.", m_strName.PszData() );
  433. dwError = ERROR_SUCCESS;
  434. } // if: the service does not exist
  435. else
  436. {
  437. // Something else has gone wrong.
  438. TW32( dwError );
  439. BCATraceMsg2( "Error %#08x occurred trying to open the '%s' service.", dwError, m_strName.PszData() );
  440. LogMsg( "Error %#08x occurred trying to open the '%s' service.", dwError, m_strName.PszData() );
  441. } // else: the service exists
  442. goto Cleanup;
  443. } // if: the handle to the service could not be opened.
  444. BCATraceMsg( "Querying the service for its initial state." );
  445. // Query the service for its initial state.
  446. ZeroMemory( &ssStatus, sizeof( ssStatus ) );
  447. if ( QueryServiceStatus( sscmhServiceHandle.HHandle(), &ssStatus ) == 0 )
  448. {
  449. dwError = TW32( GetLastError() );
  450. LogMsg( "Error %#08x occurred while trying to query the initial state of the '%s' service.", dwError, m_strName.PszData() );
  451. BCATraceMsg2( "Error %#08x occurred while trying to query the initial state of the '%s' service.", dwError, m_strName.PszData() );
  452. goto Cleanup;
  453. } // if: we could not query the service for its status.
  454. // If the service has stopped, we have nothing more to do.
  455. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  456. {
  457. // Nothing needs to be done here.
  458. BCATraceMsg1( "The '%s' service is not running. Nothing needs to be done to stop it.", m_strName.PszData() );
  459. LogMsg( "The '%s' service is not running. Nothing needs to be done to stop it.", m_strName.PszData() );
  460. goto Cleanup;
  461. } // if: the service has stopped.
  462. // If we are here, the service is running.
  463. BCATraceMsg( "The service is running." );
  464. //
  465. // Try and stop the service.
  466. //
  467. // If the service is stopping on its own, no need to send the stop control code.
  468. if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
  469. {
  470. BCATraceMsg( "The service is stopping on its own. The stop control code will not be sent." );
  471. } // if: the service is stopping already
  472. else
  473. {
  474. BCATraceMsg( "The stop control code will be sent now." );
  475. ZeroMemory( &ssStatus, sizeof( ssStatus ) );
  476. if ( ControlService( sscmhServiceHandle.HHandle(), SERVICE_CONTROL_STOP, &ssStatus ) == 0 )
  477. {
  478. dwError = GetLastError();
  479. if ( dwError == ERROR_SERVICE_NOT_ACTIVE )
  480. {
  481. LogMsg( "The '%s' service is not running. Nothing more needs to be done here.", m_strName.PszData() );
  482. BCATraceMsg1( "The '%s' service is not running. Nothing more needs to be done here.", m_strName.PszData() );
  483. // The service is not running. Change the error code to success.
  484. dwError = ERROR_SUCCESS;
  485. } // if: the service is already running.
  486. else
  487. {
  488. TW32( dwError );
  489. LogMsg( "Error %#08x occurred trying to stop the '%s' service.", dwError, m_strName.PszData() );
  490. BCATraceMsg2( "Error %#08x occurred trying to stop the '%s' service.", dwError, m_strName.PszData() );
  491. }
  492. // There is nothing else to do.
  493. goto Cleanup;
  494. } // if: an error occurred trying to stop the service.
  495. } // else: the service has to be instructed to stop
  496. // Query the service for its state now and wait till the timeout expires
  497. cNumberOfQueries = 0;
  498. do
  499. {
  500. // Query the service for its status.
  501. ZeroMemory( &ssStatus, sizeof( ssStatus ) );
  502. if ( QueryServiceStatus( sscmhServiceHandle.HHandle(), &ssStatus ) == 0 )
  503. {
  504. dwError = TW32( GetLastError() );
  505. LogMsg( "Error %#08x occurred while trying to query the state of the '%s' service.", dwError, m_strName.PszData() );
  506. BCATraceMsg2( "Error %#08x occurred while trying to query the state of the '%s' service.", dwError, m_strName.PszData() );
  507. break;
  508. } // if: we could not query the service for its status.
  509. // If the service has stopped, we have nothing more to do.
  510. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  511. {
  512. // Nothing needs to be done here.
  513. BCATraceMsg( "The service has been stopped." );
  514. fStopped = true;
  515. dwError = ERROR_SUCCESS;
  516. break;
  517. } // if: the service has stopped.
  518. // Check if the timeout has expired
  519. if ( cNumberOfQueries >= cQueryCountIn )
  520. {
  521. BCATraceMsg( "The service stop wait timeout has expired." );
  522. break;
  523. } // if: number of queries has exceeded the maximum specified
  524. BCATraceMsg2(
  525. "Waiting for %d milliseconds before querying service status again. %d such queries remaining."
  526. , ulQueryIntervalMilliSecIn
  527. , cQueryCountIn - cNumberOfQueries
  528. );
  529. ++cNumberOfQueries;
  530. // Send a progress report if the caller had passed in a valid pointer.
  531. if ( pStatusReportIn != NULL )
  532. {
  533. pStatusReportIn->SendNextStep( S_OK );
  534. } // if: a status report needs to be sent while we wait
  535. // Wait for the specified time.
  536. Sleep( ulQueryIntervalMilliSecIn );
  537. }
  538. while( true ); // while: loop infinitely
  539. if ( dwError != ERROR_SUCCESS )
  540. goto Cleanup;
  541. if ( ! fStopped )
  542. {
  543. dwError = TW32( ERROR_SERVICE_REQUEST_TIMEOUT );
  544. LogMsg( "The '%s' service has not stopped even after %d queries.", m_strName.PszData(), cQueryCountIn );
  545. BCATraceMsg2( "The '%s' service has not stopped even after %d queries.", m_strName.PszData(), cQueryCountIn );
  546. goto Cleanup;
  547. } // if: the maximum number of queries have been made and the service is not running.
  548. LogMsg( "The '%s' service was successfully stopped.", m_strName.PszData() );
  549. BCATraceMsg1( "The '%s' service was successfully stopped.", m_strName.PszData() );
  550. Cleanup:
  551. if ( dwError != ERROR_SUCCESS )
  552. {
  553. BCATraceMsg2( "Error %#08x has occurred trying to stop the '%s' service. Throwing exception.", dwError, m_strName.PszData() );
  554. LogMsg( "Error %#08x has occurred trying to stop the '%s' service.", dwError, m_strName.PszData() );
  555. THROW_RUNTIME_ERROR( HRESULT_FROM_WIN32( dwError ), IDS_ERROR_SERVICE_STOP );
  556. } // if: something has gone wrong
  557. } //*** CService::Stop()