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.

1259 lines
37 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: N C S V C . C P P
  7. //
  8. // Contents: Implementation of non-inline CService and CServiceManager
  9. // methods.
  10. //
  11. // Notes:
  12. //
  13. // Author: mikemi 6 Mar 1997
  14. //
  15. //----------------------------------------------------------------------------
  16. #include <pch.h>
  17. #pragma hdrstop
  18. #include "ncstring.h"
  19. #include "ncsvc.h"
  20. #include "ncmisc.h"
  21. #include "ncperms.h"
  22. struct CSCTX
  23. {
  24. SC_HANDLE hScm;
  25. const CSFLAGS* pFlags;
  26. DWORD dwErr;
  27. // This just allows us to save some stack space otherwise wasted by
  28. // recursion.
  29. //
  30. SERVICE_STATUS status;
  31. };
  32. VOID
  33. SvcControlServicesAndWait (
  34. CSCTX* pCtx,
  35. UINT cServices,
  36. const PCWSTR* apszServices);
  37. VOID
  38. StopDependentServices (
  39. SC_HANDLE hSvc,
  40. PCWSTR pszService,
  41. CSCTX* pCtx)
  42. {
  43. // Try a first guess of 256 bytes for the buffer needed to hold the
  44. // dependent information. If that fails, retry with the buffer size
  45. // returned from EnumDependentServices.
  46. //
  47. DWORD cbBuf = 256;
  48. ENUM_SERVICE_STATUS* aess = NULL;
  49. DWORD cess = 0;
  50. DWORD dwErr = ERROR_SUCCESS;
  51. INT cLoop = 0;
  52. const INT cLoopMax = 2;
  53. do
  54. {
  55. // Allocate the needed space if we know it.
  56. //
  57. if (cbBuf)
  58. {
  59. MemFree (aess);
  60. aess = reinterpret_cast<ENUM_SERVICE_STATUS*>(MemAlloc (cbBuf));
  61. if (!aess)
  62. {
  63. dwErr = ERROR_OUTOFMEMORY;
  64. break;
  65. }
  66. }
  67. dwErr = ERROR_SUCCESS;
  68. if (!EnumDependentServices (hSvc, SERVICE_ACTIVE, aess, cbBuf,
  69. &cbBuf, &cess))
  70. {
  71. dwErr = GetLastError ();
  72. }
  73. }
  74. while ((ERROR_MORE_DATA == dwErr) && (++cLoop < cLoopMax));
  75. // If we have some services to stop, stop them and wait.
  76. //
  77. if ((ERROR_SUCCESS == dwErr) && cess)
  78. {
  79. // The array of ENUM_SERVICE_STATUS has the service names but not
  80. // in a form that can be passed directly to
  81. // SvcControlServicesAndWait, so we must transform the data into
  82. // an array of string pointers.
  83. //
  84. PCWSTR* apszServices = reinterpret_cast<PCWSTR*>(
  85. PvAllocOnStack (cess * sizeof(PCWSTR)));
  86. for (UINT i = 0; i < cess; i++)
  87. {
  88. apszServices[i] = aess[i].lpServiceName;
  89. }
  90. Assert (SERVICE_CONTROL_STOP == pCtx->pFlags->dwControl);
  91. TraceTag (ttidSvcCtl, "Stopping dependents of %S...", pszService);
  92. SvcControlServicesAndWait (pCtx, cess, apszServices);
  93. }
  94. // Otherwise, if we've had an error, but there is no context error yet,
  95. // propagate our error to the context error for the caller.
  96. //
  97. else if ((ERROR_SUCCESS != dwErr) && (ERROR_SUCCESS == pCtx->dwErr))
  98. {
  99. pCtx->dwErr = dwErr;
  100. }
  101. MemFree (aess);
  102. }
  103. VOID
  104. SvcControlServicesAndWait (
  105. CSCTX* pCtx,
  106. UINT cServices,
  107. const PCWSTR* apszServices)
  108. {
  109. BOOL fr = TRUE;
  110. DWORD dwErr;
  111. // We only set this to TRUE if we successfuly open and control
  112. // at least one service in the first phase.
  113. //
  114. BOOL fWaitIfNeeded = FALSE;
  115. // Allocate a buffer (on the stack) to place the opened service
  116. // handles in and zero it.
  117. //
  118. size_t cb = cServices * sizeof(SC_HANDLE);
  119. SC_HANDLE* ahSvc = reinterpret_cast<SC_HANDLE*>
  120. (PvAllocOnStack (cb));
  121. ZeroMemory (ahSvc, cb);
  122. // For each service, open it and apply the requested control
  123. // (if requested). If the control succeeds, add the handle to
  124. // our array for later use.
  125. //
  126. for (UINT i = 0; i < cServices; i++)
  127. {
  128. // Open the service.
  129. //
  130. SC_HANDLE hSvc = OpenService (pCtx->hScm,
  131. apszServices[i],
  132. SERVICE_QUERY_CONFIG |
  133. SERVICE_QUERY_STATUS |
  134. SERVICE_ENUMERATE_DEPENDENTS |
  135. SERVICE_START | SERVICE_STOP |
  136. SERVICE_USER_DEFINED_CONTROL);
  137. if (hSvc)
  138. {
  139. // If we're to ignore demand-start and disabled services,
  140. // check for it now and skip if needed. Remember to close
  141. // the service handle because we're going to open the next if
  142. // we skip this one.
  143. //
  144. if (pCtx->pFlags->fIgnoreDisabledAndDemandStart)
  145. {
  146. BOOL fSkip = FALSE;
  147. LPQUERY_SERVICE_CONFIG pConfig;
  148. if (SUCCEEDED(HrQueryServiceConfigWithAlloc (hSvc, &pConfig)))
  149. {
  150. if ((pConfig->dwStartType == SERVICE_DEMAND_START) ||
  151. (pConfig->dwStartType == SERVICE_DISABLED))
  152. {
  153. fSkip = TRUE;
  154. TraceTag (ttidSvcCtl, "Skipping %S because its start "
  155. "type is %d.",
  156. apszServices[i],
  157. pConfig->dwStartType);
  158. }
  159. // Free our memory before we continue.
  160. //
  161. MemFree (pConfig);
  162. if (fSkip)
  163. {
  164. CloseServiceHandle (hSvc);
  165. continue;
  166. }
  167. }
  168. }
  169. // Initialize fr and dwErr assuming that something goes wrong.
  170. // fr and dwErr should always be set to something in the following
  171. // if,else statement.
  172. //
  173. fr = FALSE;
  174. dwErr = ERROR_INVALID_DATA;
  175. // Start or Control the service if requested. (Or do nothing
  176. // if we just want to wait.
  177. //
  178. if (pCtx->pFlags->fStart)
  179. {
  180. TraceTag (ttidSvcCtl, "Starting %S", apszServices[i]);
  181. fr = StartService (hSvc, 0, NULL);
  182. if (!fr)
  183. {
  184. dwErr = GetLastError ();
  185. }
  186. }
  187. else if (pCtx->pFlags->dwControl)
  188. {
  189. // Stop dependent services if we're stopping the service.
  190. //
  191. if (SERVICE_CONTROL_STOP == pCtx->pFlags->dwControl)
  192. {
  193. // We don't need to worry about the success or failure
  194. // of this call here. It simply recurses into this
  195. // function so pCtx->dwErr will be set however we set
  196. // it in this function on the next recursion.
  197. //
  198. StopDependentServices (hSvc, apszServices[i], pCtx);
  199. //
  200. // Now handle any special cases
  201. //
  202. if (0 == _wcsicmp(L"Netbios", apszServices[i]))
  203. {
  204. TraceTag (ttidSvcCtl, "Running special-case code to stop NetBIOS");
  205. ScStopNetbios();
  206. }
  207. TraceTag (ttidSvcCtl, "Stopping %S", apszServices[i]);
  208. }
  209. fr = ControlService (hSvc, pCtx->pFlags->dwControl,
  210. &pCtx->status);
  211. if (!fr)
  212. {
  213. dwErr = GetLastError ();
  214. }
  215. TraceTag(ttidSvcCtl,
  216. "Just issued control (0x%x) to %S. ret=%u (dwErr=%u), status.dwCurrentState=0x%x",
  217. pCtx->pFlags->dwControl,
  218. apszServices[i],
  219. fr,
  220. (!fr) ? dwErr : ERROR_SUCCESS,
  221. pCtx->status.dwCurrentState);
  222. if (!fr)
  223. {
  224. if ((SERVICE_CONTROL_STOP == pCtx->pFlags->dwControl) &&
  225. ((ERROR_INVALID_SERVICE_CONTROL == dwErr) ||
  226. (ERROR_SERVICE_CANNOT_ACCEPT_CTRL == dwErr)))
  227. {
  228. if (SERVICE_STOP_PENDING == pCtx->status.dwCurrentState)
  229. {
  230. TraceTag(ttidSvcCtl,
  231. "Issued stop to service %S which is pending stop",
  232. apszServices[i]);
  233. // This is an okay condition. We want to wait on
  234. // this service below.
  235. //
  236. fr = TRUE;
  237. dwErr = ERROR_SUCCESS;
  238. }
  239. }
  240. }
  241. }
  242. if (fr)
  243. {
  244. // We have at least one handle, indicate we may
  245. // need to wait below and save the handle so the
  246. // the wait code will use it.
  247. //
  248. fWaitIfNeeded = TRUE;
  249. ahSvc[i] = hSvc;
  250. }
  251. else
  252. {
  253. Assert (!ahSvc[i]); // don't want to wait on this index
  254. Assert (ERROR_SUCCESS != dwErr); // obtained above
  255. if (SERVICE_CONTROL_STOP == pCtx->pFlags->dwControl)
  256. {
  257. // We can ignore service not running errors.
  258. //
  259. // the first part of the OR is for the service case,
  260. // the 2nd handles the driver and service cases respectively.
  261. //
  262. if ((ERROR_SERVICE_NOT_ACTIVE == dwErr) ||
  263. (((ERROR_INVALID_SERVICE_CONTROL == dwErr) ||
  264. (ERROR_SERVICE_CANNOT_ACCEPT_CTRL == dwErr)) &&
  265. (SERVICE_STOPPED == pCtx->status.dwCurrentState)))
  266. {
  267. TraceTag(ttidSvcCtl,
  268. "Issued stop to service %S which is already stopped",
  269. apszServices[i]);
  270. dwErr = ERROR_SUCCESS;
  271. }
  272. }
  273. else if (pCtx->pFlags->fStart)
  274. {
  275. // We can ignore service already running errors.
  276. //
  277. if (ERROR_SERVICE_ALREADY_RUNNING == dwErr)
  278. {
  279. TraceTag(ttidSvcCtl,
  280. "Issued start to service %S which is already running",
  281. apszServices[i]);
  282. dwErr = ERROR_SUCCESS;
  283. }
  284. }
  285. // If we still have an error, time to remember it and move on.
  286. //
  287. if (ERROR_SUCCESS != dwErr)
  288. {
  289. // Keep going, but note that we have an error.
  290. //
  291. pCtx->dwErr = dwErr;
  292. TraceHr (ttidError, FAL,
  293. HRESULT_FROM_WIN32 (dwErr), FALSE,
  294. "SvcControlServicesAndWait: %s (%S)",
  295. (pCtx->pFlags->fStart) ?
  296. "StartService" : "ControlService",
  297. apszServices[i]);
  298. }
  299. CloseServiceHandle (hSvc);
  300. }
  301. }
  302. #ifdef ENABLETRACE
  303. else
  304. {
  305. TraceHr (ttidError, FAL, HrFromLastWin32Error (), FALSE,
  306. "SvcControlServicesAndWait: OpenService (%S)",
  307. apszServices[i]);
  308. }
  309. #endif
  310. }
  311. // For each service, wait for it to enter the requested state
  312. // (if requested).
  313. //
  314. if (fWaitIfNeeded &&
  315. pCtx->pFlags->dwMaxWaitMilliseconds && pCtx->pFlags->dwStateToWaitFor)
  316. {
  317. // We wait in increments of 100 milliseconds. Therefore, the
  318. // total number of checks to perform is dwMaxWaitMilliseconds
  319. // divided by 100 with a minimum of one check.
  320. //
  321. const UINT cmsWait = 100;
  322. UINT cLoop = pCtx->pFlags->dwMaxWaitMilliseconds / cmsWait;
  323. if (0 == cLoop)
  324. {
  325. cLoop = 1;
  326. }
  327. // Wait the request number of times...
  328. // (Assume we timeout)
  329. //
  330. dwErr = ERROR_TIMEOUT;
  331. for (UINT nLoop = 0; nLoop < cLoop; nLoop++, Sleep (cmsWait))
  332. {
  333. // Querying the state of the service to see if its entered
  334. // the requested state. We can quit the outer loop early
  335. // if all services have entered the requested state.
  336. //
  337. BOOL fAllDone = TRUE;
  338. for (i = 0; i < cServices; i++)
  339. {
  340. // Skip services that have already entered the state or
  341. // that we never opened.
  342. //
  343. if (!ahSvc[i])
  344. {
  345. continue;
  346. }
  347. fr = QueryServiceStatus (ahSvc[i], &pCtx->status);
  348. if (fr)
  349. {
  350. if (pCtx->status.dwCurrentState !=
  351. pCtx->pFlags->dwStateToWaitFor)
  352. {
  353. // Not there yet. We'll need to check this
  354. // again and we now know we're definately not
  355. // all done.
  356. //
  357. fAllDone = FALSE;
  358. }
  359. else
  360. {
  361. // No need to check this service anymore,
  362. // its in the right state.
  363. //
  364. CloseServiceHandle (ahSvc[i]);
  365. ahSvc[i] = NULL;
  366. }
  367. }
  368. #ifdef ENABLETRACE
  369. else
  370. {
  371. TraceHr (ttidError, FAL, HrFromLastWin32Error (), FALSE,
  372. "SvcControlServicesAndWait: QueryServiceStatus (%S)",
  373. apszServices[i]);
  374. }
  375. #endif
  376. }
  377. if (fAllDone)
  378. {
  379. dwErr = ERROR_SUCCESS;
  380. break;
  381. }
  382. }
  383. // If we had an error in the above wait (like a timeout), and
  384. // we haven't had any prior errors, remember this new one for the
  385. // caller.
  386. //
  387. if ((ERROR_SUCCESS != dwErr) && (ERROR_SUCCESS == pCtx->dwErr))
  388. {
  389. pCtx->dwErr = dwErr;
  390. }
  391. }
  392. // Close the remaining open service handles.
  393. //
  394. for (i = 0; i < cServices; i++)
  395. {
  396. if (ahSvc[i])
  397. {
  398. CloseServiceHandle (ahSvc[i]);
  399. #ifdef ENABLETRACE
  400. if (fWaitIfNeeded &&
  401. pCtx->pFlags->dwMaxWaitMilliseconds &&
  402. pCtx->pFlags->dwStateToWaitFor)
  403. {
  404. TraceTag (ttidSvcCtl, "%S did not %s within %i milliseconds",
  405. apszServices[i],
  406. (SERVICE_RUNNING == pCtx->pFlags->dwStateToWaitFor)
  407. ? "start" : "stop",
  408. pCtx->pFlags->dwMaxWaitMilliseconds);
  409. }
  410. #endif
  411. }
  412. }
  413. }
  414. HRESULT
  415. HrQueryServiceConfigWithAlloc (
  416. SC_HANDLE hService,
  417. LPQUERY_SERVICE_CONFIG* ppConfig)
  418. {
  419. // Initial guess for the buffer size is the structure size plus
  420. // room for 5 strings of 32 characters each. (since there are
  421. // 5 strings in the structure.)
  422. //
  423. static DWORD cbBufGuess = sizeof (QUERY_SERVICE_CONFIG) +
  424. 5 * (32 * sizeof(WCHAR));
  425. DWORD cbBuf = cbBufGuess;
  426. LPQUERY_SERVICE_CONFIG pConfig = NULL;
  427. DWORD dwErr = ERROR_SUCCESS;
  428. INT cLoop = 0;
  429. const INT cLoopMax = 2;
  430. do
  431. {
  432. // If we require more room, allocate the needed space.
  433. //
  434. MemFree (pConfig);
  435. pConfig = (LPQUERY_SERVICE_CONFIG)MemAlloc (cbBuf);
  436. if (!pConfig)
  437. {
  438. dwErr = ERROR_OUTOFMEMORY;
  439. break;
  440. }
  441. BOOL fr = QueryServiceConfig (hService, pConfig, cbBuf, &cbBuf);
  442. if (fr)
  443. {
  444. dwErr = ERROR_SUCCESS;
  445. // Update our guess for next time to be what QueryServiceConfig
  446. // says we needed. But only do so if we needed more than our
  447. // guess.
  448. //
  449. if (cbBuf > cbBufGuess)
  450. {
  451. cbBufGuess = cbBuf;
  452. }
  453. }
  454. else
  455. {
  456. dwErr = GetLastError ();
  457. #ifdef ENABLETRACE
  458. if (ERROR_INSUFFICIENT_BUFFER == dwErr)
  459. {
  460. TraceTag (ttidSvcCtl,
  461. "Perf: Guessed buffer size incorrectly calling "
  462. "QueryServiceConfig.\nNeeded %d bytes. "
  463. "(Guessed %d bytes.)",
  464. cbBuf,
  465. cbBufGuess);
  466. }
  467. #endif
  468. }
  469. }
  470. while ((ERROR_INSUFFICIENT_BUFFER == dwErr) && (++cLoop < cLoopMax));
  471. AssertSz (cLoop < cLoopMax, "Why can we never allocate a buffer big "
  472. "enough for QueryServiceConfig when its telling us how big "
  473. "the buffer should be?");
  474. HRESULT hr = HRESULT_FROM_WIN32 (dwErr);
  475. if (S_OK == hr)
  476. {
  477. *ppConfig = pConfig;
  478. }
  479. else
  480. {
  481. MemFree (pConfig);
  482. *ppConfig = NULL;
  483. }
  484. TraceError ("HrQueryServiceConfigWithAlloc", hr);
  485. return hr;
  486. }
  487. //+---------------------------------------------------------------------------
  488. //
  489. // Function: HrChangeServiceStartType
  490. //
  491. // Purpose: Changes the start type of the given service to the given type.
  492. //
  493. // Arguments:
  494. // pszServiceName [in] Name of service to change.
  495. // dwStartType [in] New start type for service. See the Win32
  496. // documentation on ChangeServiceConfig for the valid
  497. // service start type values.
  498. //
  499. // Returns: S_OK if succeeded, HRESULT_FROM_WIN32 error code otherwise.
  500. //
  501. // Author: danielwe 25 Feb 1997
  502. //
  503. // Notes: Don't call this function too many times. It is fairly
  504. // inefficient.
  505. //
  506. HRESULT
  507. HrChangeServiceStartType (
  508. IN PCWSTR pszServiceName,
  509. IN DWORD dwStartType)
  510. {
  511. CServiceManager scm;
  512. CService svc;
  513. HRESULT hr = scm.HrOpenService (&svc, pszServiceName, WITH_LOCK);
  514. if (S_OK == hr)
  515. {
  516. hr = svc.HrSetStartType(dwStartType);
  517. }
  518. TraceHr (ttidError, FAL, hr,
  519. HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr,
  520. "HrChangeServiceStartType");
  521. return hr;
  522. }
  523. //+---------------------------------------------------------------------------
  524. //
  525. // Function: HrChangeServiceStartTypeOptional
  526. //
  527. // Purpose: Changes the start type of the given service to the given type.
  528. //
  529. // Arguments:
  530. // pszServiceName [in] Name of service to change.
  531. // dwStartType [in] New start type for service. See the Win32
  532. // documentation on ChangeServiceConfig for the valid
  533. // service start type values.
  534. //
  535. // Returns: S_OK if succeeded, NETCFG_E_SVC_* error otherwise.
  536. //
  537. // Author: danielwe 25 Feb 1997
  538. //
  539. // Notes: If the service does not exist, nothing is done.
  540. //
  541. HRESULT
  542. HrChangeServiceStartTypeOptional (
  543. IN PCWSTR pszServiceName,
  544. IN DWORD dwStartType)
  545. {
  546. HRESULT hr = HrChangeServiceStartType (pszServiceName, dwStartType);
  547. if (HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr)
  548. {
  549. hr = S_OK;
  550. }
  551. TraceError ("HrChangeServiceStartTypeOptional", hr);
  552. return hr;
  553. }
  554. HRESULT
  555. HrSvcQueryStatus (
  556. IN PCWSTR pszService,
  557. OUT DWORD* pdwState)
  558. {
  559. Assert (pszService);
  560. Assert (pdwState);
  561. *pdwState = 0;
  562. CServiceManager scm;
  563. CService svc;
  564. HRESULT hr = scm.HrOpenService (&svc, pszService, NO_LOCK,
  565. SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS);
  566. if (S_OK == hr)
  567. {
  568. hr = svc.HrQueryState (pdwState);
  569. }
  570. TraceHr (ttidError, FAL, hr,
  571. HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr,
  572. "HrSvcQueryStatus");
  573. return hr;
  574. }
  575. VOID
  576. CService::Close ()
  577. {
  578. if (_schandle)
  579. {
  580. BOOL fr = ::CloseServiceHandle( _schandle );
  581. AssertSz(fr, "CloseServiceHandle failed!");
  582. _schandle = NULL;
  583. }
  584. }
  585. HRESULT
  586. CService::HrControl (
  587. IN DWORD dwControl)
  588. {
  589. Assert (_schandle);
  590. HRESULT hr = S_OK;
  591. SERVICE_STATUS status;
  592. if (!::ControlService (_schandle, dwControl, &status))
  593. {
  594. hr = HrFromLastWin32Error ();
  595. }
  596. TraceError ("CService::HrControl", hr);
  597. return hr;
  598. }
  599. HRESULT
  600. CService::HrRequestStop ()
  601. {
  602. Assert (_schandle);
  603. HRESULT hr = S_OK;
  604. SERVICE_STATUS status;
  605. if (!::ControlService (_schandle, SERVICE_CONTROL_STOP, &status))
  606. {
  607. hr = HrFromLastWin32Error ();
  608. // Don't consider it an error if the service is not running.
  609. //
  610. if (HRESULT_FROM_WIN32 (ERROR_SERVICE_NOT_ACTIVE) == hr)
  611. {
  612. hr = S_OK;
  613. }
  614. // (driver case) ERROR_INVALID_SERVICE_CONTROL is returned if the service
  615. // is not running - which may mean pending_stop.
  616. // (non-driver case) ERROR_SERVICE_CANNOT_ACCEPT_CTRL is returned if the
  617. // service is either stop_pending or stopped.
  618. // ... so in either case we need to query for the state.
  619. //
  620. if (((HRESULT_FROM_WIN32 (ERROR_INVALID_SERVICE_CONTROL) == hr) ||
  621. (HRESULT_FROM_WIN32 (ERROR_SERVICE_CANNOT_ACCEPT_CTRL) == hr)) &&
  622. (SERVICE_STOPPED == status.dwCurrentState))
  623. {
  624. hr = S_OK;
  625. }
  626. }
  627. TraceError ("CService::HrRequestStop", hr);
  628. return hr;
  629. }
  630. HRESULT
  631. CService::HrQueryState (
  632. OUT DWORD* pdwState)
  633. {
  634. Assert (pdwState);
  635. Assert (_schandle);
  636. SERVICE_STATUS sStatus;
  637. if (!::QueryServiceStatus( _schandle, &sStatus ))
  638. {
  639. *pdwState = 0;
  640. return ::HrFromLastWin32Error();
  641. }
  642. *pdwState = sStatus.dwCurrentState;
  643. return S_OK;
  644. }
  645. HRESULT
  646. CService::HrQueryStartType (
  647. OUT DWORD* pdwStartType)
  648. {
  649. Assert (pdwStartType);
  650. *pdwStartType = 0;
  651. LPQUERY_SERVICE_CONFIG pConfig;
  652. HRESULT hr = HrQueryServiceConfig (&pConfig);
  653. if (S_OK == hr)
  654. {
  655. *pdwStartType = pConfig->dwStartType;
  656. MemFree (pConfig);
  657. }
  658. TraceError ("CService::HrQueryStartType", hr);
  659. return hr;
  660. }
  661. HRESULT
  662. CService::HrSetServiceRestartRecoveryOption(
  663. IN SERVICE_FAILURE_ACTIONS *psfa
  664. )
  665. {
  666. HRESULT hr = S_OK;
  667. if (!ChangeServiceConfig2(_schandle,
  668. SERVICE_CONFIG_FAILURE_ACTIONS,
  669. (LPVOID)psfa))
  670. {
  671. hr = HrFromLastWin32Error();
  672. }
  673. TraceError("CService::HrSetServiceRestartRecoveryOption", hr);
  674. return hr;
  675. }
  676. CServiceManager::~CServiceManager ()
  677. {
  678. if (_sclock)
  679. {
  680. Unlock();
  681. }
  682. if (_schandle)
  683. {
  684. Close();
  685. }
  686. }
  687. VOID
  688. CServiceManager::Close ()
  689. {
  690. Assert (_schandle);
  691. BOOL fr = ::CloseServiceHandle (_schandle);
  692. AssertSz (fr, "CloseServiceHandle failed!");
  693. _schandle = NULL;
  694. }
  695. HRESULT
  696. CServiceManager::HrControlServicesAndWait (
  697. IN UINT cServices,
  698. IN const PCWSTR* apszServices,
  699. IN const CSFLAGS* pFlags)
  700. {
  701. Assert (cServices);
  702. Assert (apszServices);
  703. Assert (pFlags);
  704. // Make sure we have something to do before wasting time.
  705. //
  706. Assert ( (pFlags->fStart || pFlags->dwControl)
  707. || (pFlags->dwMaxWaitMilliseconds && pFlags->dwStateToWaitFor));
  708. HRESULT hr = S_OK;
  709. if (!_schandle)
  710. {
  711. hr = HrOpen (NO_LOCK, SC_MANAGER_CONNECT);
  712. }
  713. if (S_OK == hr)
  714. {
  715. Assert (_schandle);
  716. // Setup the context structure and call the internal routine (which
  717. // may recurse which is why we use the context structure).
  718. //
  719. CSCTX ctx;
  720. ZeroMemory (&ctx, sizeof(ctx));
  721. ctx.hScm = _schandle;
  722. ctx.pFlags = pFlags;
  723. SvcControlServicesAndWait (&ctx, cServices, apszServices);
  724. hr = HRESULT_FROM_WIN32 (ctx.dwErr);
  725. }
  726. TraceError ("CServiceManager::HrControlServicesAndWait", hr);
  727. return hr;
  728. }
  729. HRESULT
  730. CServiceManager::HrStartServicesNoWait (
  731. IN UINT cServices,
  732. IN const PCWSTR* apszServices)
  733. {
  734. CSFLAGS flags =
  735. { TRUE, 0, 0, SERVICE_RUNNING, FALSE };
  736. HRESULT hr = HrControlServicesAndWait (cServices, apszServices, &flags);
  737. TraceError ("CServiceManager::HrStartServicesNoWait", hr);
  738. return hr;
  739. }
  740. HRESULT
  741. CServiceManager::HrStartServicesAndWait (
  742. IN UINT cServices,
  743. IN const PCWSTR* apszServices,
  744. IN DWORD dwWaitMilliseconds /*= 15000*/)
  745. {
  746. CSFLAGS flags =
  747. { TRUE, 0, dwWaitMilliseconds, SERVICE_RUNNING, FALSE };
  748. HRESULT hr = HrControlServicesAndWait (cServices, apszServices, &flags);
  749. TraceError ("CServiceManager::HrStartServicesAndWait", hr);
  750. return hr;
  751. }
  752. HRESULT
  753. CServiceManager::HrStopServicesNoWait (
  754. IN UINT cServices,
  755. IN const PCWSTR* apszServices)
  756. {
  757. CSFLAGS flags =
  758. { FALSE, SERVICE_CONTROL_STOP, 0, SERVICE_STOPPED, FALSE };
  759. HRESULT hr = HrControlServicesAndWait (cServices, apszServices, &flags);
  760. TraceError ("CServiceManager::HrStopServicesNoWait", hr);
  761. return hr;
  762. }
  763. HRESULT
  764. CServiceManager::HrStopServicesAndWait (
  765. IN UINT cServices,
  766. IN const PCWSTR* apszServices,
  767. IN DWORD dwWaitMilliseconds /*= 15000*/)
  768. {
  769. CSFLAGS flags =
  770. { FALSE, SERVICE_CONTROL_STOP, dwWaitMilliseconds, SERVICE_STOPPED, FALSE };
  771. HRESULT hr = HrControlServicesAndWait (cServices, apszServices, &flags);
  772. TraceError ("CServiceManager::HrStopServicesAndWait", hr);
  773. return hr;
  774. }
  775. HRESULT
  776. CServiceManager::HrCreateService (
  777. IN CService* pcsService,
  778. IN PCWSTR pszServiceName,
  779. IN PCWSTR pszDisplayName,
  780. IN DWORD dwServiceType,
  781. IN DWORD dwStartType,
  782. IN DWORD dwErrorControl,
  783. IN PCWSTR pszBinaryPathName,
  784. IN PCWSTR pslzDependencies,
  785. IN PCWSTR pszLoadOrderGroup,
  786. IN PDWORD pdwTagId,
  787. IN DWORD dwDesiredAccess,
  788. IN PCWSTR pszServiceStartName,
  789. IN PCWSTR pszPassword,
  790. IN PCWSTR pszDescription)
  791. {
  792. HRESULT hr = S_OK;
  793. // Open the service control manager if needed.
  794. //
  795. if (!_schandle)
  796. {
  797. hr = HrOpen ();
  798. }
  799. if (S_OK == hr)
  800. {
  801. // make sure the service is not in use
  802. //
  803. if (pcsService->_schandle)
  804. {
  805. pcsService->Close();
  806. }
  807. pcsService->_schandle = ::CreateService (_schandle,
  808. pszServiceName,
  809. pszDisplayName,
  810. dwDesiredAccess,
  811. dwServiceType,
  812. dwStartType,
  813. dwErrorControl,
  814. pszBinaryPathName,
  815. pszLoadOrderGroup,
  816. pdwTagId,
  817. pslzDependencies,
  818. pszServiceStartName,
  819. pszPassword );
  820. if (!pcsService->_schandle)
  821. {
  822. hr = HrFromLastWin32Error ();
  823. }
  824. else
  825. {
  826. // Set the description is one is supplied
  827. //
  828. if (pszDescription)
  829. {
  830. SERVICE_DESCRIPTION sd = {0};
  831. sd.lpDescription = (PWSTR)pszDescription;
  832. (VOID)ChangeServiceConfig2(pcsService->_schandle,
  833. SERVICE_CONFIG_DESCRIPTION, &sd);
  834. }
  835. }
  836. }
  837. TraceError ("CServiceManager::HrCreateService", hr);
  838. return hr;
  839. }
  840. HRESULT
  841. CServiceManager::HrQueryLocked (
  842. OUT BOOL* pfLocked)
  843. {
  844. LPQUERY_SERVICE_LOCK_STATUS pqslStatus = NULL;
  845. DWORD cbNeeded = sizeof( QUERY_SERVICE_LOCK_STATUS );
  846. DWORD cbSize;
  847. BOOL frt;
  848. Assert(_schandle != NULL );
  849. Assert(pfLocked != NULL);
  850. *pfLocked = FALSE;
  851. // loop, allocating the needed size
  852. do
  853. {
  854. pqslStatus = (LPQUERY_SERVICE_LOCK_STATUS) MemAlloc (cbNeeded);
  855. if (pqslStatus == NULL)
  856. {
  857. return E_OUTOFMEMORY;
  858. }
  859. cbSize = cbNeeded;
  860. frt = ::QueryServiceLockStatus( _schandle,
  861. pqslStatus,
  862. cbSize,
  863. &cbNeeded );
  864. *pfLocked = pqslStatus->fIsLocked;
  865. MemFree (pqslStatus);
  866. pqslStatus = NULL;
  867. if (!frt && (cbNeeded == cbSize))
  868. {
  869. // if an error, treat this as a lock
  870. return ::HrFromLastWin32Error();
  871. }
  872. } while (!frt && (cbNeeded != cbSize));
  873. return S_OK;
  874. }
  875. HRESULT
  876. CServiceManager::HrLock ()
  877. {
  878. INT cRetries = 30;
  879. const INT c_msecWait = 1000;
  880. Assert (_schandle != NULL);
  881. Assert (_sclock == NULL);
  882. while (cRetries--)
  883. {
  884. _sclock = ::LockServiceDatabase( _schandle );
  885. if (_sclock)
  886. {
  887. return S_OK;
  888. }
  889. else
  890. {
  891. HRESULT hr = HrFromLastWin32Error();
  892. if ((HRESULT_FROM_WIN32(ERROR_SERVICE_DATABASE_LOCKED) != hr) ||
  893. (0 == cRetries))
  894. {
  895. return hr;
  896. }
  897. TraceTag(ttidSvcCtl, "SCM is locked, waiting for %d "
  898. "seconds before retrying...", c_msecWait / 1000);
  899. // wait for a bit to see if the database unlocks in that
  900. // time.
  901. Sleep (c_msecWait);
  902. }
  903. }
  904. AssertSz (FALSE, "Lock me Amadeus! I'm not supposed to get here!");
  905. return S_OK;
  906. }
  907. HRESULT
  908. CServiceManager::HrOpen (
  909. CSLOCK eLock, // = NO_LOCK
  910. DWORD dwDesiredAccess, // = SC_MANAGER_ALL_ACCESS
  911. PCWSTR pszMachineName, // = NULL
  912. PCWSTR pszDatabaseName // = NULL
  913. )
  914. {
  915. HRESULT hr = S_OK;
  916. if (_schandle)
  917. {
  918. Close();
  919. }
  920. _schandle = ::OpenSCManager (pszMachineName, pszDatabaseName,
  921. dwDesiredAccess );
  922. if (_schandle)
  923. {
  924. if (WITH_LOCK == eLock)
  925. {
  926. hr = HrLock ();
  927. }
  928. }
  929. else
  930. {
  931. hr = ::HrFromLastWin32Error();
  932. }
  933. TraceHr (ttidError, FAL, hr, FALSE,
  934. "CServiceManager::HrOpen failed. eLock=%d dwDesiredAccess=0x%08x",
  935. eLock, dwDesiredAccess);
  936. return hr;
  937. }
  938. HRESULT
  939. CServiceManager::HrOpenService (
  940. CService* pcsService,
  941. PCWSTR pszServiceName,
  942. CSLOCK eLock, // = NO_LOCK
  943. DWORD dwScmAccess, // = SC_MANAGER_ALL_ACCESS
  944. DWORD dwSvcAccess // = SERVICE_ALL_ACCESS
  945. )
  946. {
  947. HRESULT hr = S_OK;
  948. // Open the service control manager if needed.
  949. //
  950. if (!_schandle)
  951. {
  952. hr = HrOpen (eLock, dwScmAccess);
  953. }
  954. if (S_OK == hr)
  955. {
  956. // make sure the service is not in use
  957. //
  958. if (pcsService->_schandle)
  959. {
  960. pcsService->Close();
  961. }
  962. pcsService->_schandle = ::OpenService (_schandle,
  963. pszServiceName,
  964. dwSvcAccess);
  965. if (!pcsService->_schandle)
  966. {
  967. hr = HrFromLastWin32Error();
  968. }
  969. }
  970. TraceHr (ttidError, FAL, hr,
  971. (HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr),
  972. "CServiceManager::HrOpenService failed opening '%S'", pszServiceName);
  973. return hr;
  974. }
  975. //+---------------------------------------------------------------------------
  976. //
  977. // Member: CServiceManager::HrAddRemoveServiceDependency
  978. //
  979. // Purpose: Add/remove dependency to a service
  980. //
  981. // Arguments:
  982. // pszService [in] Name of service
  983. // pszDependency [in] Dependency to add
  984. // enumFlag [in] Indicates add or remove
  985. //
  986. // Returns: S_OK if success, Win32 HRESULT otherwise.
  987. //
  988. // Author: tongl 17 Jun 1997
  989. //
  990. // Notes: this function is not for adding/removing group dependency
  991. //
  992. HRESULT
  993. CServiceManager::HrAddRemoveServiceDependency (
  994. PCWSTR pszServiceName,
  995. PCWSTR pszDependency,
  996. DEPENDENCY_ADDREMOVE enumFlag)
  997. {
  998. HRESULT hr = S_OK;
  999. Assert(pszServiceName);
  1000. Assert(pszDependency);
  1001. Assert((enumFlag == DEPENDENCY_ADD) || (enumFlag == DEPENDENCY_REMOVE));
  1002. // If either string is empty, do nothing
  1003. if (*pszServiceName && *pszDependency)
  1004. {
  1005. hr = HrLock();
  1006. if (S_OK == hr)
  1007. {
  1008. PCWSTR pszSrv = pszDependency;
  1009. CService svc;
  1010. // Check if the dependency service exists
  1011. hr = HrOpenService(&svc, pszDependency);
  1012. if (S_OK == hr)
  1013. {
  1014. // Open the service we are changing dependency on
  1015. pszSrv = pszServiceName;
  1016. hr = HrOpenService(&svc, pszServiceName);
  1017. if (S_OK == hr)
  1018. {
  1019. LPQUERY_SERVICE_CONFIG pConfig;
  1020. hr = svc.HrQueryServiceConfig (&pConfig);
  1021. if (S_OK == hr)
  1022. {
  1023. BOOL fChanged = FALSE;
  1024. if (enumFlag == DEPENDENCY_ADD)
  1025. {
  1026. PWSTR pmszNewDependencies;
  1027. hr = HrAddSzToMultiSz(
  1028. pszDependency,
  1029. pConfig->lpDependencies,
  1030. STRING_FLAG_DONT_MODIFY_IF_PRESENT |
  1031. STRING_FLAG_ENSURE_AT_END, 0,
  1032. &pmszNewDependencies,
  1033. &fChanged);
  1034. if ((S_OK == hr) && fChanged)
  1035. {
  1036. Assert (pmszNewDependencies);
  1037. hr = svc.HrSetDependencies (pmszNewDependencies);
  1038. MemFree (pmszNewDependencies);
  1039. }
  1040. }
  1041. else if (enumFlag == DEPENDENCY_REMOVE)
  1042. {
  1043. RemoveSzFromMultiSz(
  1044. pszDependency,
  1045. pConfig->lpDependencies,
  1046. STRING_FLAG_REMOVE_ALL,
  1047. &fChanged);
  1048. if (fChanged)
  1049. {
  1050. hr = svc.HrSetDependencies (pConfig->lpDependencies);
  1051. }
  1052. }
  1053. MemFree (pConfig);
  1054. }
  1055. }
  1056. }
  1057. if (HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr) // If either services do not exist
  1058. {
  1059. TraceTag(ttidSvcCtl, "CServiceManager::HrAddServiceDependency, Service %s does not exist.", pszSrv);
  1060. hr = S_OK;
  1061. }
  1062. Unlock();
  1063. }
  1064. } // if szDependency is not empty string
  1065. TraceError("CServiceManager::HrAddRemoveServiceDependency", hr);
  1066. return hr;
  1067. }
  1068. VOID
  1069. CServiceManager::Unlock ()
  1070. {
  1071. Assert (_schandle);
  1072. Assert (_sclock);
  1073. BOOL fr = ::UnlockServiceDatabase (_sclock);
  1074. AssertSz (fr, "UnlockServiceDatabase failed!");
  1075. _sclock = NULL;
  1076. }
  1077. //+---------------------------------------------------------------------------
  1078. //
  1079. // Function: AllocateAndInitializeAcl
  1080. //
  1081. // Purpose: Combine the common operation of allocation and initialization
  1082. // of an ACL. Similiar to AllocateAndInitializeSid.
  1083. //
  1084. // Arguments:
  1085. // cbAcl [in] size in bytes of ACL
  1086. // dwAclRevision [in] ACL_REVISION
  1087. // ppAcl [out] the returned ACL
  1088. //
  1089. // Returns: TRUE if successful, FALSE if not.
  1090. //
  1091. // Author: shaunco 4 Sep 1997
  1092. //
  1093. // Notes:
  1094. //
  1095. BOOL
  1096. AllocateAndInitializeAcl (
  1097. DWORD cbAcl,
  1098. DWORD dwAclRevision,
  1099. PACL* ppAcl)
  1100. {
  1101. Assert (ppAcl);
  1102. *ppAcl = reinterpret_cast<PACL>(LocalAlloc (LPTR,
  1103. static_cast<UINT>(cbAcl)));
  1104. if (*ppAcl)
  1105. {
  1106. return InitializeAcl (*ppAcl, cbAcl, dwAclRevision);
  1107. }
  1108. return FALSE;
  1109. }