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.

620 lines
16 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997 - 1998
  5. //
  6. // File: C S E R V I C E . 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 "stdafx.h"
  17. #include "cservice.h"
  18. size_t CchMsz(const TCHAR * msz)
  19. {
  20. TCHAR * pch= (TCHAR *) msz;
  21. while (*pch)
  22. {
  23. pch += lstrlen(pch)+1;
  24. }
  25. return (size_t) (pch-msz+1);
  26. }
  27. BOOL FIsSzInMultiSzSafe(LPCTSTR sz, LPCTSTR szMultiSz)
  28. {
  29. ULONG ulLen;
  30. if (!szMultiSz || !sz)
  31. return FALSE;
  32. while (*szMultiSz)
  33. {
  34. ulLen = lstrlen(szMultiSz);
  35. if (lstrcmpi(szMultiSz, sz)==0)
  36. return TRUE;
  37. szMultiSz += (ulLen + 1);
  38. }
  39. return FALSE;
  40. }
  41. HRESULT HrAddSzToMultiSz(LPCTSTR sz,
  42. LPCTSTR mszIn,
  43. LPTSTR * pmszOut)
  44. {
  45. HRESULT hr = S_OK;
  46. Assert(pmszOut);
  47. if (!FIsSzInMultiSzSafe(sz, mszIn)) // We need to add the string
  48. {
  49. size_t cchMszIn = CchMsz(mszIn);
  50. size_t cchMszOut = cchMszIn + lstrlen(sz) + 1;
  51. TCHAR * mszOut = new TCHAR[(int)cchMszOut];
  52. ZeroMemory(mszOut, cchMszOut * sizeof(TCHAR));
  53. // Copy the existing string
  54. CopyMemory(mszOut, mszIn, (cchMszIn-1) * sizeof(TCHAR) );
  55. // Add the new string
  56. TCHAR * pchOut = mszOut;
  57. pchOut += cchMszIn -1;
  58. lstrcpy(pchOut, sz);
  59. // Add the last '\0' for the output multisz
  60. pchOut += lstrlen(sz) + 1;
  61. *pchOut = '\0';
  62. *pmszOut = mszOut;
  63. }
  64. else // We just make a copy of the input string
  65. {
  66. size_t cchMszOut = CchMsz(mszIn);
  67. TCHAR * mszOut = new TCHAR[(int)cchMszOut];
  68. // Copy the existing string
  69. CopyMemory(mszOut, mszIn, cchMszOut*sizeof(TCHAR) );
  70. *pmszOut = mszOut;
  71. }
  72. Trace1("HrAddSzToMultiSz %08lx", hr);
  73. return hr;
  74. }
  75. //+---------------------------------------------------------------------------
  76. //
  77. // Function: HrRemoveSzFromMultiSz
  78. //
  79. // Purpose: Remove a NULL terminated sz to a double NULL terminated Multi_Sz
  80. //
  81. // Arguments:
  82. // sz [in] The string to remove
  83. // mszIn [in] The Multi_Sz to remove from
  84. // mszOut [out] The result Multi_Sz
  85. //
  86. // Returns: Always return S_OK for now
  87. // Only possible failure is out of memory, which will throw exception
  88. //
  89. // Author: tongl 17 June 1997
  90. //
  91. // Notes: 1) This function only removes the first occurrance of the sz
  92. // 2) The result multi_sz should be released using delete
  93. //
  94. HRESULT HrRemoveSzFromMultiSz(LPCTSTR sz,
  95. LPCTSTR mszIn,
  96. LPTSTR * pmszOut)
  97. {
  98. HRESULT hr = S_OK;
  99. Assert(pmszOut);
  100. if(FIsSzInMultiSzSafe(sz, mszIn)) // We need to remove the string
  101. {
  102. size_t cchIn = CchMsz(mszIn);
  103. size_t cchOut = cchIn - lstrlen(sz)-1; // we assume the can string only appeared once
  104. // Construct the output multi-sz
  105. TCHAR * mszOut = new TCHAR[(int)cchOut];
  106. ZeroMemory(mszOut, cchOut*sizeof(TCHAR));
  107. TCHAR * pchIn = (TCHAR*) mszIn;
  108. TCHAR * pchOut = mszOut;
  109. while(*pchIn) // for each substring in mszIn
  110. {
  111. if(lstrcmpi(pchIn, sz) != 0) // if not the same as the string we are removing
  112. {
  113. lstrcpy(pchOut, pchIn);
  114. pchIn += lstrlen(pchIn) + 1;
  115. pchOut += lstrlen(pchOut) + 1;
  116. }
  117. else // skip the string we are deleting
  118. {
  119. pchIn += lstrlen(pchIn) + 1;
  120. }
  121. }
  122. // Add the last '\0' of the multi-sz
  123. *pchOut = '\0';
  124. *pmszOut = mszOut;
  125. }
  126. else // We simply make a copy of the input string
  127. {
  128. size_t cchMszOut = CchMsz(mszIn);
  129. TCHAR * mszOut = new TCHAR[(int)cchMszOut];
  130. // Copy the existing string
  131. CopyMemory(mszOut, mszIn, cchMszOut*sizeof(TCHAR));
  132. *pmszOut = mszOut;
  133. }
  134. Trace1("HrRemoveSzFromMultiSz %08lx", hr);
  135. return hr;
  136. }
  137. //-------------------------------------------------------------------
  138. HRESULT CService::HrMoveOutOfState(DWORD dwState)
  139. {
  140. HRESULT hr = S_OK;
  141. SERVICE_STATUS sStatus;
  142. // Give the service a maximum of 30 seconds to start
  143. UINT cTimeout = 30;
  144. AssertSz((NULL != _schandle), "We don't have a service handle");
  145. do
  146. {
  147. DWORD dwWait = 0;
  148. // Get the status of the service
  149. if (!::QueryServiceStatus(_schandle, &sStatus))
  150. {
  151. hr = HRESULT_FROM_WIN32(GetLastError());
  152. break;
  153. }
  154. // We are not longer in the state we were waiting for
  155. if (sStatus.dwCurrentState != dwState)
  156. {
  157. hr = S_OK;
  158. break;
  159. }
  160. // Wait a second and or less for the service to start
  161. dwWait = min((sStatus.dwWaitHint / 10), 1*(1000));
  162. ::Sleep(dwWait);
  163. }
  164. while(cTimeout--); // Make sure we don't get in an endless loop.
  165. // Return an error if we timeout
  166. if (0 == cTimeout)
  167. {
  168. hr = HRESULT_FROM_WIN32(ERROR_NOT_READY);
  169. AssertSz(FALSE,
  170. "We timed out on waiting for a service. This is bad.");
  171. }
  172. Trace1("CService::HrMoveOutOfState", hr);
  173. return hr;
  174. }
  175. //-------------------------------------------------------------------
  176. HRESULT CService::HrQueryState( DWORD* pdwState )
  177. {
  178. SERVICE_STATUS sStatus;
  179. Assert(_schandle != NULL );
  180. Assert(pdwState != NULL );
  181. if (!::QueryServiceStatus( _schandle, &sStatus ))
  182. {
  183. *pdwState = 0;
  184. return HRESULT_FROM_WIN32(GetLastError());
  185. }
  186. *pdwState = sStatus.dwCurrentState;
  187. return S_OK;
  188. }
  189. //-------------------------------------------------------------------
  190. HRESULT CService::HrQueryStartType( DWORD* pdwStartType )
  191. {
  192. LPQUERY_SERVICE_CONFIG pqsConfig = NULL;
  193. DWORD cbNeeded = sizeof( QUERY_SERVICE_CONFIG );
  194. DWORD cbSize;
  195. BOOL frt;
  196. Assert(_schandle != NULL );
  197. Assert(pdwStartType != NULL );
  198. *pdwStartType = 0;
  199. // loop, allocating the needed size
  200. do
  201. {
  202. delete [] (PBYTE)pqsConfig;
  203. pqsConfig = (LPQUERY_SERVICE_CONFIG) new BYTE[cbNeeded];
  204. if (pqsConfig == NULL)
  205. {
  206. return E_OUTOFMEMORY;
  207. }
  208. cbSize = cbNeeded;
  209. frt = ::QueryServiceConfig( _schandle,
  210. pqsConfig,
  211. cbSize,
  212. &cbNeeded );
  213. *pdwStartType = pqsConfig->dwStartType;
  214. delete [] (PBYTE)pqsConfig;
  215. pqsConfig = NULL;
  216. if (!frt && (cbNeeded == cbSize))
  217. {
  218. // error
  219. *pdwStartType = 0;
  220. return HRESULT_FROM_WIN32(GetLastError());
  221. }
  222. } while (!frt && (cbNeeded != cbSize));
  223. return S_OK;
  224. }
  225. //-------------------------------------------------------------------
  226. HRESULT CService::HrQueryDependencies(OUT LPTSTR * pmszDependencyList)
  227. {
  228. HRESULT hr = S_OK;
  229. LPQUERY_SERVICE_CONFIG pqsConfig = NULL;
  230. DWORD cbNeeded = sizeof( QUERY_SERVICE_CONFIG );
  231. DWORD cbSize;
  232. BOOL frt;
  233. Assert(_schandle != NULL );
  234. Assert(pmszDependencyList);
  235. // loop, allocating the needed size
  236. do
  237. {
  238. delete [] (PBYTE)pqsConfig;
  239. pqsConfig = (LPQUERY_SERVICE_CONFIG) new BYTE[cbNeeded];
  240. if (pqsConfig == NULL)
  241. {
  242. hr = E_OUTOFMEMORY;
  243. Trace1("CService::HrQueryDependencies", hr);
  244. return hr;
  245. }
  246. cbSize = cbNeeded;
  247. frt = ::QueryServiceConfig( _schandle,
  248. pqsConfig,
  249. cbSize,
  250. &cbNeeded );
  251. if (!frt && (cbNeeded == cbSize)) // error
  252. {
  253. delete [] (PBYTE)pqsConfig;
  254. pqsConfig = NULL;
  255. pmszDependencyList = NULL;
  256. hr = HRESULT_FROM_WIN32(GetLastError());
  257. Trace1("CService::HrQueryDependencies", hr);
  258. return hr;
  259. }
  260. else if (frt && (cbNeeded != cbSize)) // We just need more space
  261. {
  262. delete [] (PBYTE)pqsConfig;
  263. pqsConfig = NULL;
  264. }
  265. } while (!frt && (cbNeeded != cbSize));
  266. // Copy pqsConfig->lpDependencies to *pmszDependencyList
  267. // Allocating space
  268. // int cch = CchMsz(pqsConfig->lpDependencies);
  269. size_t cch=0;
  270. TCHAR * pch= pqsConfig->lpDependencies;
  271. while (*pch)
  272. {
  273. pch += lstrlen(pch)+1;
  274. }
  275. cch = (size_t)(pch - pqsConfig->lpDependencies +1);
  276. TCHAR * mszOut;
  277. mszOut = new TCHAR[(int)cch];
  278. if (mszOut == NULL)
  279. {
  280. hr = E_OUTOFMEMORY;
  281. Trace1("CService::HrQueryDependencies", hr);
  282. return hr;
  283. }
  284. else
  285. {
  286. ZeroMemory(mszOut, cch * sizeof(TCHAR));
  287. // Copy dependency list to mszOut
  288. *pmszDependencyList = mszOut;
  289. pch = pqsConfig->lpDependencies;
  290. while (*pch)
  291. {
  292. lstrcpy(mszOut, pch);
  293. mszOut += lstrlen(pch)+1;
  294. pch += lstrlen(pch)+1;
  295. }
  296. mszOut = '\0';
  297. }
  298. delete [] (PBYTE)pqsConfig;
  299. Trace1("CService::HrQueryDependencies", hr);
  300. return hr;
  301. }
  302. //-------------------------------------------------------------------
  303. HRESULT CServiceManager::HrQueryLocked(BOOL *pfLocked)
  304. {
  305. LPQUERY_SERVICE_LOCK_STATUS pqslStatus = NULL;
  306. DWORD cbNeeded = sizeof( QUERY_SERVICE_LOCK_STATUS );
  307. DWORD cbSize;
  308. BOOL frt;
  309. Assert(_schandle != NULL );
  310. Assert(pfLocked != NULL);
  311. *pfLocked = FALSE;
  312. // loop, allocating the needed size
  313. do
  314. {
  315. pqslStatus = (LPQUERY_SERVICE_LOCK_STATUS) new BYTE[cbNeeded];
  316. if (pqslStatus == NULL)
  317. {
  318. return E_OUTOFMEMORY;
  319. }
  320. cbSize = cbNeeded;
  321. frt = ::QueryServiceLockStatus( _schandle,
  322. pqslStatus,
  323. cbSize,
  324. &cbNeeded );
  325. *pfLocked = pqslStatus->fIsLocked;
  326. delete [] (PBYTE)pqslStatus;
  327. pqslStatus = NULL;
  328. if (!frt && (cbNeeded == cbSize))
  329. {
  330. // if an error, treat this as a lock
  331. return HRESULT_FROM_WIN32(GetLastError());
  332. }
  333. } while (!frt && (cbNeeded != cbSize));
  334. return S_OK;
  335. }
  336. //+---------------------------------------------------------------------------
  337. //
  338. // Member: CServiceManager::HrStartServiceHelper
  339. //
  340. // Purpose: Starts the given service
  341. //
  342. // Arguments:
  343. // szService [in] Name of service to start.
  344. // eCriteria [in] if SERVICE_ONLY_AUTO_START, the service is only
  345. // started if it is configured as auto-start
  346. //
  347. // Returns: S_OK if success, Win32 HRESULT otherwise.
  348. //
  349. // Author: danielwe 13 Jun 1997
  350. //
  351. // Notes:
  352. //
  353. HRESULT
  354. CServiceManager::HrStartServiceHelper(LPCTSTR szService,
  355. SERVICE_START_CRITERIA eCriteria)
  356. {
  357. HRESULT hr = S_OK;
  358. CService service;
  359. hr = HrOpenService(&service, szService);
  360. if (SUCCEEDED(hr))
  361. {
  362. BOOL fStart = TRUE;
  363. if (SERVICE_ONLY_AUTO_START == eCriteria)
  364. {
  365. DWORD dwStartType;
  366. // only start services that are not disabled and not manual
  367. hr = service.HrQueryStartType(&dwStartType);
  368. if (FAILED(hr) ||
  369. (SERVICE_DEMAND_START == dwStartType) ||
  370. (SERVICE_DISABLED == dwStartType))
  371. {
  372. fStart = FALSE;
  373. }
  374. }
  375. // If everything is okay to start then start it!
  376. if (fStart)
  377. {
  378. hr = service.HrStart();
  379. if (SUCCEEDED(hr))
  380. {
  381. // Make sure the service has started.
  382. hr = service.HrMoveOutOfState(SERVICE_START_PENDING);
  383. // Normalize result
  384. if (SUCCEEDED(hr))
  385. {
  386. hr = S_OK;
  387. }
  388. }
  389. else if (HRESULT_FROM_WIN32(ERROR_SERVICE_ALREADY_RUNNING) == hr)
  390. {
  391. // Ignore error if service is already running
  392. hr = S_OK;
  393. }
  394. }
  395. service.Close();
  396. }
  397. return hr;
  398. }
  399. //+---------------------------------------------------------------------------
  400. //
  401. // Member: CServiceManager::HrStopService
  402. //
  403. // Purpose: Stops the given service.
  404. //
  405. // Arguments:
  406. // szService [in] Name of service to stop.
  407. //
  408. // Returns: S_OK if success, Win32 HRESULT otherwise.
  409. //
  410. // Author: danielwe 17 Jun 1997
  411. //
  412. // Notes: If service is not running, this returns S_OK.
  413. //
  414. HRESULT CServiceManager::HrStopService(LPCTSTR szService)
  415. {
  416. HRESULT hr = S_OK;
  417. CService service;
  418. hr = HrOpenService(&service, szService);
  419. if (SUCCEEDED(hr))
  420. {
  421. hr = service.HrControl(SERVICE_CONTROL_STOP);
  422. if (HRESULT_FROM_WIN32(ERROR_SERVICE_NOT_ACTIVE) == hr)
  423. {
  424. // ignore error if the service is not running
  425. hr = S_OK;
  426. }
  427. service.Close();
  428. }
  429. Trace1("CServiceManager::HrStopService", hr);
  430. return hr;
  431. }
  432. //+---------------------------------------------------------------------------
  433. //
  434. // Member: CServiceManager::HrAddRemoveServiceDependency
  435. //
  436. // Purpose: Add/remove dependency to a service
  437. //
  438. // Arguments:
  439. // szService [in] Name of service
  440. // szDependency [in] Dependency to add
  441. // enumFlag [in] Indicates add or remove
  442. //
  443. // Returns: S_OK if success, Win32 HRESULT otherwise.
  444. //
  445. // Author: tongl 17 Jun 1997
  446. //
  447. // Notes: this function is not for adding/removing group dependency
  448. //
  449. HRESULT CServiceManager::HrAddRemoveServiceDependency(LPCTSTR szServiceName,
  450. LPCTSTR szDependency,
  451. DEPENDENCY_ADDREMOVE enumFlag)
  452. {
  453. HRESULT hr = S_OK;
  454. Assert(szServiceName);
  455. Assert(szDependency);
  456. Assert((enumFlag == DEPENDENCY_ADD) || (enumFlag == DEPENDENCY_REMOVE));
  457. // If either string is empty, do nothing
  458. if ((lstrlen(szDependency)>0) && (lstrlen(szServiceName)>0))
  459. {
  460. hr = HrLock();
  461. if (SUCCEEDED(hr))
  462. {
  463. LPCTSTR szSrv = szDependency;
  464. CService svc;
  465. // Check if the dependency service exists
  466. hr = HrOpenService(&svc, szDependency);
  467. if SUCCEEDED(hr)
  468. {
  469. svc.Close();
  470. // Open the service we are changing dependency on
  471. szSrv = szServiceName;
  472. hr = HrOpenService(&svc, szServiceName);
  473. if (SUCCEEDED(hr))
  474. {
  475. LPTSTR mszDependencies;
  476. hr = svc.HrQueryDependencies(&mszDependencies);
  477. if(SUCCEEDED(hr) && mszDependencies)
  478. {
  479. TCHAR * mszNewDependencies;
  480. if (enumFlag == DEPENDENCY_ADD)
  481. {
  482. hr = HrAddSzToMultiSz(szDependency, mszDependencies,
  483. &mszNewDependencies);
  484. }
  485. else if (enumFlag == DEPENDENCY_REMOVE)
  486. {
  487. hr = HrRemoveSzFromMultiSz(szDependency, mszDependencies,
  488. &mszNewDependencies);
  489. }
  490. if (SUCCEEDED(hr))
  491. {
  492. // Now set the new dependency
  493. hr = svc.HrSetDependencies(const_cast<LPCTSTR>(mszNewDependencies));
  494. delete [] mszNewDependencies;
  495. }
  496. }
  497. delete [] mszDependencies;
  498. svc.Close();
  499. }
  500. }
  501. if (HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr) // If either services do not exist
  502. {
  503. #ifdef DEBUG
  504. Trace1("CServiceManager::HrAddServiceDependency, Service %s does not exist.", szSrv);
  505. #endif
  506. hr = S_OK;
  507. }
  508. }
  509. Unlock();
  510. } // if szDependency is not empty string
  511. Trace1("CServiceManager::HrAddServiceDependency", hr);
  512. return hr;
  513. }