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.

2582 lines
72 KiB

  1. /*++
  2. Copyright (c) 1992-2000 Microsoft Corporation
  3. Module Name:
  4. gensvc.c
  5. Abstract:
  6. Resource DLL to control and monitor NT services.
  7. Author:
  8. Robs 3/28/96, based on RodGa's generic resource dll
  9. Revision History:
  10. --*/
  11. #define UNICODE 1
  12. #include "clusres.h"
  13. #include "clusrtl.h"
  14. #include "userenv.h"
  15. // Uncomment the next line to test the Terminate() function when
  16. // a shutdown is in progress.
  17. //#define TEST_TERMINATE_ON_SHUTDOWN
  18. #define LOG_CURRENT_MODULE LOG_MODULE_GENSVC
  19. #define SERVICES_ROOT L"SYSTEM\\CurrentControlSet\\Services\\"
  20. #define DBG_PRINT printf
  21. #define PARAM_NAME__SERVICENAME CLUSREG_NAME_GENSVC_SERVICE_NAME
  22. #define PARAM_NAME__STARTUPPARAMETERS CLUSREG_NAME_GENSVC_STARTUP_PARAMS
  23. #define PARAM_NAME__USENETWORKNAME CLUSREG_NAME_GENSVC_USE_NETWORK_NAME
  24. #define PARAM_MIN__USENETWORKNAME 0
  25. #define PARAM_MAX__USENETWORKNAME 1
  26. #define PARAM_DEFAULT__USENETWORKNAME 0
  27. typedef struct _GENSVC_PARAMS {
  28. PWSTR ServiceName;
  29. PWSTR StartupParameters;
  30. DWORD UseNetworkName;
  31. } GENSVC_PARAMS, *PGENSVC_PARAMS;
  32. typedef struct _GENSVC_RESOURCE {
  33. GENSVC_PARAMS Params;
  34. HRESOURCE hResource;
  35. HANDLE ServiceHandle;
  36. RESOURCE_HANDLE ResourceHandle;
  37. HKEY ResourceKey;
  38. HKEY ParametersKey;
  39. CLUS_WORKER PendingThread;
  40. BOOL Online;
  41. DWORD dwServicePid;
  42. HANDLE hSem;
  43. } GENSVC_RESOURCE, *PGENSVC_RESOURCE;
  44. //
  45. // Global Data
  46. //
  47. // Handle to service controller, set by the first create resource call.
  48. SC_HANDLE g_ScHandle = NULL;
  49. // Log Event Routine
  50. #define g_LogEvent ClusResLogEvent
  51. #define g_SetResourceStatus ClusResSetResourceStatus
  52. // Forward reference to our RESAPI function table.
  53. extern CLRES_FUNCTION_TABLE GenSvcFunctionTable;
  54. //
  55. // Generic Service resource read-write private properties.
  56. //
  57. RESUTIL_PROPERTY_ITEM
  58. GenSvcResourcePrivateProperties[] = {
  59. { PARAM_NAME__SERVICENAME, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, RESUTIL_PROPITEM_REQUIRED, FIELD_OFFSET(GENSVC_PARAMS,ServiceName) },
  60. { PARAM_NAME__STARTUPPARAMETERS, NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0, FIELD_OFFSET(GENSVC_PARAMS,StartupParameters) },
  61. { PARAM_NAME__USENETWORKNAME, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__USENETWORKNAME, PARAM_MIN__USENETWORKNAME, PARAM_MAX__USENETWORKNAME, 0, FIELD_OFFSET(GENSVC_PARAMS,UseNetworkName) },
  62. { 0 }
  63. };
  64. //
  65. // Forward routines
  66. //
  67. BOOL
  68. VerifyService(
  69. IN RESID ResourceId,
  70. IN BOOL IsAliveFlag
  71. );
  72. void
  73. wparse_cmdline (
  74. WCHAR *cmdstart,
  75. WCHAR **argv,
  76. WCHAR *args,
  77. int *numargs,
  78. int *numchars
  79. );
  80. DWORD
  81. GenSvcGetPrivateResProperties(
  82. IN const PGENSVC_RESOURCE ResourceEntry,
  83. OUT PVOID OutBuffer,
  84. IN DWORD OutBufferSize,
  85. OUT LPDWORD BytesReturned
  86. );
  87. DWORD
  88. GenSvcValidatePrivateResProperties(
  89. IN const PGENSVC_RESOURCE ResourceEntry,
  90. IN const PVOID InBuffer,
  91. IN DWORD InBufferSize,
  92. OUT PGENSVC_PARAMS Params
  93. );
  94. DWORD
  95. GenSvcSetPrivateResProperties(
  96. IN OUT PGENSVC_RESOURCE ResourceEntry,
  97. IN const PVOID InBuffer,
  98. IN DWORD InBufferSize
  99. );
  100. DWORD
  101. GenSvcInvalidGenericServiceCheck(
  102. IN OUT PGENSVC_RESOURCE ResourceEntry,
  103. IN LPCWSTR ServiceName,
  104. IN HKEY ServiceKey
  105. );
  106. DWORD
  107. GenSvcIsValidService(
  108. IN OUT PGENSVC_RESOURCE ResourceEntry,
  109. IN LPCWSTR ServiceName
  110. );
  111. DWORD
  112. GenSvcOfflineThread(
  113. PCLUS_WORKER pWorker,
  114. IN PGENSVC_RESOURCE ResourceEntry
  115. );
  116. BOOLEAN
  117. GenSvcInit(
  118. VOID
  119. )
  120. {
  121. return(TRUE);
  122. }
  123. BOOLEAN
  124. WINAPI
  125. GenSvcDllEntryPoint(
  126. IN HINSTANCE DllHandle,
  127. IN DWORD Reason,
  128. IN LPVOID Reserved
  129. )
  130. {
  131. switch( Reason ) {
  132. case DLL_PROCESS_ATTACH:
  133. if ( !GenSvcInit() ) {
  134. return(FALSE);
  135. }
  136. break;
  137. case DLL_PROCESS_DETACH:
  138. break;
  139. default:
  140. break;
  141. }
  142. return(TRUE);
  143. } // GenSvc DllEntryPoint
  144. DWORD
  145. GenSvcOnlineThread(
  146. PCLUS_WORKER pWorker,
  147. IN PGENSVC_RESOURCE ResourceEntry
  148. )
  149. /*++
  150. Routine Description:
  151. Brings a disk resource online.
  152. Arguments:
  153. Worker - Supplies the worker structure
  154. Context - A pointer to the DiskInfo block for this resource.
  155. Returns:
  156. ERROR_SUCCESS if successful.
  157. Win32 error code on failure.
  158. --*/
  159. {
  160. SERVICE_STATUS_PROCESS ServiceStatus;
  161. DWORD status = ERROR_SUCCESS;
  162. DWORD numchars;
  163. LPWSTR * serviceArgArray = NULL;
  164. DWORD serviceArgCount;
  165. SC_HANDLE serviceHandle;
  166. RESOURCE_STATUS resourceStatus;
  167. DWORD valueSize;
  168. LPVOID Environment = NULL;
  169. WCHAR * p;
  170. LPWSTR nameOfPropInError;
  171. LPSERVICE_FAILURE_ACTIONS pSvcFailureActions = NULL;
  172. DWORD cbBytesNeeded, i;
  173. LPQUERY_SERVICE_CONFIG lpquerysvcconfig=NULL;
  174. HANDLE processToken = NULL;
  175. DWORD dwRetryCount = 2400; // Try 10 min max.
  176. DWORD dwRetryTick = 250; // msec
  177. ResUtilInitializeResourceStatus( &resourceStatus );
  178. resourceStatus.ResourceState = ClusterResourceFailed;
  179. resourceStatus.CheckPoint = 1;
  180. //set it to NULL, when it is brought online and if the
  181. //service is not running in the system or lsa process
  182. //then store the process id for forceful termination
  183. ResourceEntry->dwServicePid = 0;
  184. //
  185. // Read our parameters.
  186. //
  187. status = ResUtilGetPropertiesToParameterBlock( ResourceEntry->ParametersKey,
  188. GenSvcResourcePrivateProperties,
  189. (LPBYTE) &ResourceEntry->Params,
  190. TRUE, // CheckForRequiredProperties
  191. &nameOfPropInError );
  192. if ( status != ERROR_SUCCESS ) {
  193. (g_LogEvent)(
  194. ResourceEntry->ResourceHandle,
  195. LOG_ERROR,
  196. L"Unable to read the '%1!ls!' property. Error: %2!u!.\n",
  197. (nameOfPropInError == NULL ? L"" : nameOfPropInError),
  198. status );
  199. goto error_exit;
  200. }
  201. //
  202. // Parse the startup parameters
  203. //
  204. if ( ResourceEntry->Params.StartupParameters != NULL ) {
  205. //
  206. // Crack the startup parameters into its component arguments because
  207. // the service controller is not good enough to do this for us.
  208. // First, find out how many args we have.
  209. //
  210. wparse_cmdline( ResourceEntry->Params.StartupParameters, NULL, NULL, &serviceArgCount, &numchars );
  211. //
  212. // Allocate space for vector and strings
  213. //
  214. serviceArgArray = LocalAlloc( LMEM_FIXED,
  215. serviceArgCount * sizeof(WCHAR *) +
  216. numchars * sizeof(WCHAR) );
  217. if ( serviceArgArray == NULL ) {
  218. status = ERROR_NOT_ENOUGH_MEMORY;
  219. goto error_exit;
  220. }
  221. wparse_cmdline( ResourceEntry->Params.StartupParameters,
  222. serviceArgArray,
  223. (WCHAR *)(((char *)serviceArgArray) + serviceArgCount * sizeof(WCHAR *)),
  224. &serviceArgCount,
  225. &numchars );
  226. } else {
  227. serviceArgCount = 0;
  228. serviceArgArray = NULL;
  229. }
  230. //
  231. // Now open the requested service
  232. //
  233. ResourceEntry->ServiceHandle = OpenService( g_ScHandle,
  234. ResourceEntry->Params.ServiceName,
  235. SERVICE_ALL_ACCESS );
  236. if ( ResourceEntry->ServiceHandle == NULL ) {
  237. status = GetLastError();
  238. ClusResLogSystemEventByKeyData(ResourceEntry->ResourceKey,
  239. LOG_CRITICAL,
  240. RES_GENSVC_OPEN_FAILED,
  241. sizeof(status),
  242. &status);
  243. (g_LogEvent)(
  244. ResourceEntry->ResourceHandle,
  245. LOG_ERROR,
  246. L"Failed to open service, error = %1!u!.\n",
  247. status );
  248. goto error_exit;
  249. }
  250. valueSize = sizeof(QUERY_SERVICE_CONFIG);
  251. AllocSvcConfig:
  252. // Query the service to make sure it is not disabled
  253. lpquerysvcconfig=(LPQUERY_SERVICE_CONFIG)LocalAlloc(LMEM_FIXED, valueSize);
  254. if(lpquerysvcconfig==NULL){
  255. status = GetLastError();
  256. (g_LogEvent)(
  257. ResourceEntry->ResourceHandle,
  258. LOG_ERROR,
  259. L"[Gensvc] Failed to allocate memory for query_service_config, error = %1!u!.\n",
  260. status );
  261. goto error_exit;
  262. }
  263. if (!QueryServiceConfig(ResourceEntry->ServiceHandle,
  264. lpquerysvcconfig,
  265. valueSize,
  266. &cbBytesNeeded))
  267. {
  268. status=GetLastError();
  269. if (status != ERROR_INSUFFICIENT_BUFFER){
  270. (g_LogEvent)(ResourceEntry->ResourceHandle,
  271. LOG_ERROR,
  272. L"svc: Failed to query service configuration, error= %1!u!.\n",
  273. status );
  274. goto error_exit;
  275. }
  276. status=ERROR_SUCCESS;
  277. LocalFree(lpquerysvcconfig);
  278. lpquerysvcconfig=NULL;
  279. valueSize = cbBytesNeeded;
  280. goto AllocSvcConfig;
  281. }
  282. if (lpquerysvcconfig->dwStartType == SERVICE_DISABLED)
  283. {
  284. (g_LogEvent)(
  285. ResourceEntry->ResourceHandle,
  286. LOG_ERROR,
  287. L"svc:The service is DISABLED\n");
  288. status=ERROR_SERVICE_DISABLED;
  289. goto error_exit;
  290. }
  291. //
  292. // Make sure service is set to manual start.
  293. //
  294. ChangeServiceConfig( ResourceEntry->ServiceHandle,
  295. SERVICE_NO_CHANGE,
  296. SERVICE_DEMAND_START, // Manual start
  297. SERVICE_NO_CHANGE,
  298. NULL,
  299. NULL,
  300. NULL,
  301. NULL,
  302. NULL,
  303. NULL,
  304. NULL );
  305. //if any of the service action is set to service restart, set it to
  306. // none
  307. if (!(QueryServiceConfig2(ResourceEntry->ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS,
  308. (LPBYTE)&valueSize, sizeof(DWORD), &cbBytesNeeded)))
  309. {
  310. status = GetLastError();
  311. if (status != ERROR_INSUFFICIENT_BUFFER)
  312. {
  313. (g_LogEvent)(
  314. ResourceEntry->ResourceHandle,
  315. LOG_ERROR,
  316. L"svc: Failed to query service configuration for size, error= %1!u!.\n",
  317. status );
  318. goto error_exit;
  319. }
  320. else
  321. status = ERROR_SUCCESS;
  322. }
  323. pSvcFailureActions = (LPSERVICE_FAILURE_ACTIONS)LocalAlloc(LMEM_FIXED, cbBytesNeeded);
  324. if ( pSvcFailureActions == NULL ) {
  325. status = GetLastError();
  326. (g_LogEvent)(
  327. ResourceEntry->ResourceHandle,
  328. LOG_ERROR,
  329. L"[Gensvc] Failed to allocate memory, error = %1!u!.\n",
  330. status );
  331. goto error_exit;
  332. }
  333. if (!(QueryServiceConfig2(ResourceEntry->ServiceHandle, SERVICE_CONFIG_FAILURE_ACTIONS,
  334. (LPBYTE)pSvcFailureActions, cbBytesNeeded, &cbBytesNeeded)))
  335. {
  336. (g_LogEvent)(
  337. ResourceEntry->ResourceHandle,
  338. LOG_ERROR,
  339. L"[Gensvc] Failed to query service configuration, error = %1!u!.\n",
  340. status );
  341. goto error_exit;
  342. }
  343. for (i=0; i<pSvcFailureActions->cActions;i++)
  344. {
  345. if (pSvcFailureActions->lpsaActions[i].Type == SC_ACTION_RESTART)
  346. pSvcFailureActions->lpsaActions[i].Type = SC_ACTION_NONE;
  347. }
  348. ChangeServiceConfig2(ResourceEntry->ServiceHandle,
  349. SERVICE_CONFIG_FAILURE_ACTIONS,
  350. pSvcFailureActions);
  351. if ( ResourceEntry->Params.UseNetworkName )
  352. {
  353. //
  354. // Create the new environment with the simulated net name.
  355. //
  356. Environment = ResUtilGetEnvironmentWithNetName( ResourceEntry->hResource );
  357. }
  358. else
  359. {
  360. BOOL success;
  361. //
  362. // get the current process token. If it fails, we revert to using just the
  363. // system environment area
  364. //
  365. OpenProcessToken( GetCurrentProcess(), MAXIMUM_ALLOWED, &processToken );
  366. //
  367. // Clone the current environment, picking up any changes that might have
  368. // been made after resmon started
  369. //
  370. success = CreateEnvironmentBlock(&Environment, processToken, FALSE );
  371. if ( processToken != NULL ) {
  372. CloseHandle( processToken );
  373. }
  374. if ( !success ) {
  375. status = GetLastError();
  376. goto error_exit;
  377. }
  378. }
  379. if (Environment != NULL) {
  380. HKEY ServicesKey;
  381. HKEY hKey;
  382. //
  383. // Compute the size of the environment. We are looking for
  384. // the double NULL terminator that ends the environment block.
  385. //
  386. p = (WCHAR *)Environment;
  387. while (*p) {
  388. while (*p++) {
  389. }
  390. }
  391. valueSize = (DWORD)((PUCHAR)p - (PUCHAR)Environment) +
  392. sizeof(WCHAR);
  393. //
  394. // Set the environment value in the service's registry key.
  395. //
  396. status = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  397. LOCAL_SERVICES,
  398. 0,
  399. KEY_READ,
  400. &ServicesKey );
  401. if (status != ERROR_SUCCESS) {
  402. (g_LogEvent)(
  403. ResourceEntry->ResourceHandle,
  404. LOG_ERROR,
  405. L"Failed to open services key, error = %1!u!.\n",
  406. status );
  407. goto error_exit;
  408. }
  409. status = RegOpenKeyExW( ServicesKey,
  410. ResourceEntry->Params.ServiceName,
  411. 0,
  412. KEY_READ | KEY_WRITE,
  413. &hKey );
  414. RegCloseKey(ServicesKey);
  415. if (status != ERROR_SUCCESS) {
  416. (g_LogEvent)(
  417. ResourceEntry->ResourceHandle,
  418. LOG_ERROR,
  419. L"Failed to open service key, error = %1!u!.\n",
  420. status );
  421. goto error_exit;
  422. }
  423. status = RegSetValueExW( hKey,
  424. L"Environment",
  425. 0,
  426. REG_MULTI_SZ,
  427. Environment,
  428. valueSize );
  429. RegCloseKey(hKey);
  430. if (status != ERROR_SUCCESS) {
  431. (g_LogEvent)(
  432. ResourceEntry->ResourceHandle,
  433. LOG_ERROR,
  434. L"Failed to set service environment value, error = %1!u!.\n",
  435. status );
  436. goto error_exit;
  437. }
  438. }
  439. if ( !StartServiceW( ResourceEntry->ServiceHandle,
  440. serviceArgCount,
  441. serviceArgArray ) )
  442. {
  443. status = GetLastError();
  444. if (status != ERROR_SERVICE_ALREADY_RUNNING) {
  445. ClusResLogSystemEventByKeyData(ResourceEntry->ResourceKey,
  446. LOG_CRITICAL,
  447. RES_GENSVC_START_FAILED,
  448. sizeof(status),
  449. &status);
  450. (g_LogEvent)(ResourceEntry->ResourceHandle,
  451. LOG_ERROR,
  452. L"Failed to start service. Error: %1!u!.\n",
  453. status );
  454. goto error_exit;
  455. } else {
  456. status = ERROR_SUCCESS;
  457. }
  458. }
  459. //wait for the service to comeonline unless we are asked to terminate
  460. while (!ClusWorkerCheckTerminate(pWorker) && dwRetryCount--) {
  461. //
  462. // Tell the Resource Monitor that we are still working.
  463. //
  464. resourceStatus.ResourceState = ClusterResourceOnlinePending;
  465. resourceStatus.CheckPoint++;
  466. (g_SetResourceStatus)( ResourceEntry->ResourceHandle,
  467. &resourceStatus );
  468. resourceStatus.ResourceState = ClusterResourceFailed;
  469. if ( !QueryServiceStatusEx( ResourceEntry->ServiceHandle,
  470. SC_STATUS_PROCESS_INFO, (LPBYTE)&ServiceStatus,
  471. sizeof(SERVICE_STATUS_PROCESS), &cbBytesNeeded ) )
  472. {
  473. status = GetLastError();
  474. (g_LogEvent)(
  475. ResourceEntry->ResourceHandle,
  476. LOG_ERROR,
  477. L"Query Service Status failed %1!u!.\n",
  478. status );
  479. goto error_exit;
  480. }
  481. if ( ServiceStatus.dwCurrentState != SERVICE_START_PENDING ) {
  482. break;
  483. }
  484. Sleep(dwRetryTick);
  485. }
  486. //
  487. // If we terminated the loop above before setting ServiceStatus,
  488. // then return now.
  489. //
  490. if (ClusWorkerCheckTerminate(pWorker) || (dwRetryCount == (DWORD)-1)) {
  491. (g_LogEvent)(
  492. ResourceEntry->ResourceHandle,
  493. LOG_ERROR,
  494. L"GensvcOnlineThread: Asked to terminate or retry period expired...\n");
  495. goto error_exit;
  496. }
  497. if ( ServiceStatus.dwCurrentState != SERVICE_RUNNING ) {
  498. if ( ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR ) {
  499. status = ServiceStatus.dwServiceSpecificExitCode;
  500. } else {
  501. status = ServiceStatus.dwWin32ExitCode;
  502. }
  503. ClusResLogSystemEventByKeyData(ResourceEntry->ResourceKey,
  504. LOG_CRITICAL,
  505. RES_GENSVC_FAILED_AFTER_START,
  506. sizeof(status),
  507. &status);
  508. (g_LogEvent)(
  509. ResourceEntry->ResourceHandle,
  510. LOG_ERROR,
  511. L"Service failed during initialization. Error: %1!u!.\n",
  512. status );
  513. goto error_exit;
  514. }
  515. resourceStatus.ResourceState = ClusterResourceOnline;
  516. if (!(ServiceStatus.dwServiceFlags & SERVICE_RUNS_IN_SYSTEM_PROCESS)) {
  517. ResourceEntry->dwServicePid = ServiceStatus.dwProcessId;
  518. }
  519. (g_LogEvent)(
  520. ResourceEntry->ResourceHandle,
  521. LOG_INFORMATION,
  522. L"Service is now on line.\n" );
  523. error_exit:
  524. (g_SetResourceStatus)( ResourceEntry->ResourceHandle,
  525. &resourceStatus );
  526. if ( resourceStatus.ResourceState == ClusterResourceOnline ) {
  527. ResourceEntry->Online = TRUE;
  528. } else {
  529. ResourceEntry->Online = FALSE;
  530. }
  531. //cleanup
  532. if (pSvcFailureActions)
  533. LocalFree(pSvcFailureActions);
  534. if (lpquerysvcconfig)
  535. LocalFree(lpquerysvcconfig);
  536. LocalFree( serviceArgArray );
  537. if (Environment != NULL) {
  538. RtlDestroyEnvironment(Environment);
  539. }
  540. return(status);
  541. } // GenSvcOnlineThread
  542. RESID
  543. WINAPI
  544. GenSvcOpen(
  545. IN LPCWSTR ResourceName,
  546. IN HKEY ResourceKey,
  547. IN RESOURCE_HANDLE ResourceHandle
  548. )
  549. /*++
  550. Routine Description:
  551. Open routine for generic service resource.
  552. This routine gets a handle to the service controller, if we don't already have one,
  553. and then gets a handle to the specified service. The service handle is saved
  554. in the GENSVC_RESOURCE structure.
  555. Arguments:
  556. ResourceName - supplies the resource name
  557. ResourceKey - supplies a handle to the resource's cluster registry key
  558. ResourceHandle - the resource handle to be supplied with SetResourceStatus
  559. is called.
  560. Return Value:
  561. RESID of created resource
  562. Zero on failure
  563. --*/
  564. {
  565. RESID svcResid = 0;
  566. DWORD status;
  567. HKEY parametersKey = NULL;
  568. HKEY resKey = NULL;
  569. PGENSVC_RESOURCE resourceEntry = NULL;
  570. DWORD paramNameSize = 0;
  571. DWORD paramNameMaxSize = 0;
  572. HCLUSTER hCluster;
  573. LPWSTR nameOfPropInError;
  574. LPWSTR lpwTemp=NULL;
  575. //
  576. // Open registry parameters key for this resource.
  577. //
  578. status = ClusterRegOpenKey( ResourceKey,
  579. CLUSREG_KEYNAME_PARAMETERS,
  580. KEY_READ,
  581. &parametersKey );
  582. if ( status != NO_ERROR ) {
  583. (g_LogEvent)(
  584. ResourceHandle,
  585. LOG_ERROR,
  586. L"Unable to open parameters key. Error: %1!u!.\n",
  587. status);
  588. goto error_exit;
  589. }
  590. //
  591. // Get a handle to our resource key so that we can get our name later
  592. // if we need to log an event.
  593. //
  594. status = ClusterRegOpenKey( ResourceKey,
  595. L"",
  596. KEY_READ,
  597. &resKey);
  598. if (status != ERROR_SUCCESS) {
  599. (g_LogEvent)(ResourceHandle,
  600. LOG_ERROR,
  601. L"Unable to open resource key. Error: %1!u!.\n",
  602. status );
  603. goto error_exit;
  604. }
  605. //
  606. // First get a handle to the service controller.
  607. //
  608. if ( g_ScHandle == NULL ) {
  609. g_ScHandle = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
  610. if ( g_ScHandle == NULL ) {
  611. status = GetLastError();
  612. (g_LogEvent)(
  613. ResourceHandle,
  614. LOG_ERROR,
  615. L"Failed to open service control manager, error %1!u!.\n",
  616. status);
  617. goto error_exit;
  618. }
  619. }
  620. resourceEntry = LocalAlloc( LMEM_FIXED, sizeof(GENSVC_RESOURCE) );
  621. if ( resourceEntry == NULL ) {
  622. (g_LogEvent)(
  623. ResourceHandle,
  624. LOG_ERROR,
  625. L"Failed to allocate a service info structure.\n");
  626. status = ERROR_NOT_ENOUGH_MEMORY;
  627. goto error_exit;
  628. }
  629. ZeroMemory( resourceEntry, sizeof(GENSVC_RESOURCE) );
  630. resourceEntry->ResourceHandle = ResourceHandle;
  631. resourceEntry->ResourceKey = resKey;
  632. resourceEntry->ParametersKey = parametersKey;
  633. resourceEntry->hSem= NULL;
  634. status = ResUtilGetPropertiesToParameterBlock( resourceEntry->ParametersKey,
  635. GenSvcResourcePrivateProperties,
  636. (LPBYTE) &resourceEntry->Params,
  637. TRUE, // CheckForRequiredProperties
  638. &nameOfPropInError );
  639. if ( status == ERROR_SUCCESS ) {
  640. // Create the Semaphore - this will be executed only if this is not a new
  641. // GenericService Type resource being created for the first time
  642. lpwTemp = (LPWSTR)LocalAlloc(LMEM_FIXED,
  643. (lstrlenW(resourceEntry->Params.ServiceName)+
  644. lstrlenW(L"GenSvc$")+1)*sizeof(WCHAR));
  645. if (lpwTemp==NULL)
  646. {
  647. status=GetLastError();
  648. (g_LogEvent)(
  649. ResourceHandle,
  650. LOG_ERROR,
  651. L"Service '%1!ls!': Not enough memory for storing semaphore name. Error: %2!u!.\n",
  652. resourceEntry->Params.ServiceName,
  653. status );
  654. goto error_exit;
  655. }
  656. lstrcpyW(lpwTemp,L"GenSvc$");
  657. lstrcat(lpwTemp,resourceEntry->Params.ServiceName);
  658. resourceEntry->hSem=CreateSemaphore(NULL,0,1,lpwTemp);
  659. status=GetLastError();
  660. if(resourceEntry->hSem)
  661. {
  662. // Check if there is another resource controlling the same service
  663. if(status==ERROR_OBJECT_ALREADY_EXISTS)
  664. {
  665. (g_LogEvent)(
  666. ResourceHandle,
  667. LOG_ERROR,
  668. L"Service '%1!ls!' is controlled by another resource. Error: %2!u!.\n",
  669. resourceEntry->Params.ServiceName,
  670. status );
  671. CloseHandle(resourceEntry->hSem);
  672. resourceEntry->hSem = NULL;
  673. goto error_exit;
  674. }
  675. }
  676. else
  677. {
  678. (g_LogEvent)(
  679. ResourceHandle,
  680. LOG_ERROR,
  681. L"Unable to create semaphore for Service '%1!ls!' . Error: %2!u!.\n",
  682. resourceEntry->Params.ServiceName,
  683. status );
  684. goto error_exit;
  685. }
  686. }
  687. else {
  688. (g_LogEvent)(
  689. ResourceHandle,
  690. LOG_ERROR,
  691. L"Unable to read parameters from registry for Service '%1!ls!' . Error: %2!u!, property in error is '%3!ls!' .\n",
  692. resourceEntry->Params.ServiceName,
  693. status,
  694. nameOfPropInError);
  695. }
  696. status = ERROR_SUCCESS;
  697. hCluster = OpenCluster(NULL);
  698. if (hCluster == NULL) {
  699. status = GetLastError();
  700. (g_LogEvent)(
  701. ResourceHandle,
  702. LOG_ERROR,
  703. L"Failed to open cluster, error %1!u!.\n",
  704. status );
  705. goto error_exit;
  706. }
  707. resourceEntry->hResource = OpenClusterResource(hCluster, ResourceName);
  708. status = GetLastError();
  709. CloseCluster(hCluster);
  710. if ( resourceEntry->hResource == NULL ) {
  711. (g_LogEvent)(
  712. ResourceHandle,
  713. LOG_ERROR,
  714. L"Failed to open resource, error %1!u!.\n", status );
  715. goto error_exit;
  716. }
  717. svcResid = (RESID)resourceEntry;
  718. return(svcResid);
  719. error_exit:
  720. if ( parametersKey != NULL ) {
  721. ClusterRegCloseKey( parametersKey );
  722. }
  723. if ( resKey != NULL) {
  724. ClusterRegCloseKey( resKey );
  725. }
  726. if ( resourceEntry != NULL) {
  727. LocalFree( resourceEntry );
  728. }
  729. if (lpwTemp) {
  730. LocalFree(lpwTemp);
  731. }
  732. SetLastError( status );
  733. return((RESID)NULL);
  734. } // GenSvcOpen
  735. DWORD
  736. WINAPI
  737. GenSvcOnline(
  738. IN RESID ResourceId,
  739. IN OUT PHANDLE EventHandle
  740. )
  741. /*++
  742. Routine Description:
  743. Online routine for Generic Service resource.
  744. Arguments:
  745. ResourceId - Supplies resource id to be brought online
  746. EventHandle - Supplies a pointer to a handle to signal on error.
  747. Return Value:
  748. ERROR_SUCCESS if successful.
  749. ERROR_RESOURCE_NOT_FOUND if RESID is not valid.
  750. ERROR_RESOURCE_NOT_AVAILABLE if resource was arbitrated but failed to
  751. acquire 'ownership'.
  752. Win32 error code if other failure.
  753. --*/
  754. {
  755. DWORD status;
  756. PGENSVC_RESOURCE resourceEntry;
  757. resourceEntry = (PGENSVC_RESOURCE)ResourceId;
  758. if ( resourceEntry == NULL ) {
  759. DBG_PRINT( "GenSvc: Online request for a nonexistent resource id 0x%p.\n",
  760. ResourceId );
  761. return(ERROR_RESOURCE_NOT_FOUND);
  762. }
  763. ClusWorkerTerminate( &resourceEntry->PendingThread );
  764. status = ClusWorkerCreate( &resourceEntry->PendingThread,
  765. GenSvcOnlineThread,
  766. resourceEntry );
  767. if ( status == ERROR_SUCCESS ) {
  768. status = ERROR_IO_PENDING;
  769. }
  770. return(status);
  771. } // GenSvcOnline
  772. VOID
  773. WINAPI
  774. GenSvcTerminate(
  775. IN RESID ResourceId
  776. )
  777. /*++
  778. Routine Description:
  779. Terminate routine for Generic Application resource.
  780. Arguments:
  781. ResourceId - Supplies resource id to be terminated
  782. Return Value:
  783. None.
  784. --*/
  785. {
  786. SERVICE_STATUS ServiceStatus;
  787. PGENSVC_RESOURCE resourceEntry;
  788. resourceEntry = (PGENSVC_RESOURCE)ResourceId;
  789. if ( resourceEntry == NULL ) {
  790. DBG_PRINT( "GenSvc: Terminate request for a nonexistent resource id 0x%p.\n",
  791. ResourceId );
  792. return;
  793. }
  794. (g_LogEvent)(
  795. resourceEntry->ResourceHandle,
  796. LOG_INFORMATION,
  797. L"Terminate request.\n" );
  798. #ifdef TEST_TERMINATE_ON_SHUTDOWN
  799. {
  800. DWORD dwStatus;
  801. BOOLEAN fWasEnabled;
  802. //
  803. // Test terminate on shutdown code.
  804. //
  805. (g_LogEvent)(
  806. resourceEntry->ResourceHandle,
  807. LOG_ERROR,
  808. L"GenSvcTerminate: TEST_TERMINATE_ON_SHUTDOWN - enabling shutdown privilege.\n"
  809. );
  810. dwStatus = ClRtlEnableThreadPrivilege(
  811. SE_SHUTDOWN_PRIVILEGE,
  812. &fWasEnabled
  813. );
  814. if ( dwStatus != ERROR_SUCCESS ) {
  815. (g_LogEvent)(
  816. resourceEntry->ResourceHandle,
  817. LOG_ERROR,
  818. L"GetSvcTerminate: TEST_TERMINATE_ON_SHUTDOWN - Unable to enable shutdown privilege. Error: %1!u!...\n",
  819. dwStatus
  820. );
  821. } else {
  822. AbortSystemShutdown( NULL );
  823. (g_LogEvent)(
  824. resourceEntry->ResourceHandle,
  825. LOG_ERROR,
  826. L"GenSvcTerminate: TEST_TERMINATE_ON_SHUTDOWN - initiating system shutdown.\n"
  827. );
  828. if ( ! InitiateSystemShutdown(
  829. NULL, // lpMachineName
  830. L"Testing Generic Service cluster resource DLL",
  831. 0, // dwTimeout
  832. TRUE, // bForceAppsClosed
  833. TRUE // bRebootAfterShutdown
  834. ) ) {
  835. dwStatus = GetLastError();
  836. (g_LogEvent)(
  837. resourceEntry->ResourceHandle,
  838. LOG_ERROR,
  839. L"GenSvcTerminate: TEST_TERMINATE_ON_SHUTDOWN - Unable to shutdown the system. Error: %1!u!.\n",
  840. dwStatus
  841. );
  842. } else {
  843. Sleep( 30000 );
  844. }
  845. ClRtlRestoreThreadPrivilege(
  846. SE_SHUTDOWN_PRIVILEGE,
  847. fWasEnabled
  848. );
  849. }
  850. }
  851. #endif
  852. //if there is a pending thread close it
  853. //if the online pending thread is active, the service may be online
  854. //if the offline pending thread is active, the service might be offline
  855. ClusWorkerTerminate( &resourceEntry->PendingThread );
  856. //if the service isnt gone by now, terminate it forcibly
  857. if ( resourceEntry->ServiceHandle != NULL )
  858. {
  859. DWORD dwRetryCount= 100;
  860. BOOL didStop = FALSE;
  861. DWORD dwRetryTick = 300; // 300 msec at a time
  862. DWORD dwStatus;
  863. while (dwRetryCount--)
  864. {
  865. (g_LogEvent)(
  866. resourceEntry->ResourceHandle,
  867. LOG_INFORMATION,
  868. L"GenSvcTerminate : calling SCM (didStop=%1!d!)\n",
  869. didStop
  870. );
  871. dwStatus = (ControlService(
  872. resourceEntry->ServiceHandle,
  873. (didStop
  874. ? SERVICE_CONTROL_INTERROGATE
  875. : SERVICE_CONTROL_STOP),
  876. &ServiceStatus )
  877. ? NO_ERROR
  878. : GetLastError());
  879. if (dwStatus == NO_ERROR)
  880. {
  881. didStop = TRUE;
  882. if (ServiceStatus.dwCurrentState == SERVICE_STOPPED)
  883. {
  884. (g_LogEvent)(
  885. resourceEntry->ResourceHandle,
  886. LOG_INFORMATION,
  887. L"Service stopped.\n" );
  888. //set the status
  889. resourceEntry->Online = FALSE;
  890. resourceEntry->dwServicePid = 0;
  891. break;
  892. }
  893. }
  894. //
  895. // Chittur Subbaraman (chitturs) - 2/21/2000
  896. //
  897. // Since SCM doesn't accept any control requests during
  898. // windows shutdown, don't send any more control
  899. // requests. Just exit from this loop and terminate
  900. // the process brute force.
  901. //
  902. if ( dwStatus == ERROR_SHUTDOWN_IN_PROGRESS )
  903. {
  904. (g_LogEvent)(
  905. resourceEntry->ResourceHandle,
  906. LOG_INFORMATION,
  907. L"System shutdown in progress. Will try to terminate process brute force...\n" );
  908. break;
  909. }
  910. if (dwStatus == ERROR_EXCEPTION_IN_SERVICE ||
  911. dwStatus == ERROR_PROCESS_ABORTED ||
  912. dwStatus == ERROR_SERVICE_NOT_ACTIVE)
  913. {
  914. (g_LogEvent)(
  915. resourceEntry->ResourceHandle,
  916. LOG_INFORMATION,
  917. L"Service died; status = %1!u!.\n",
  918. dwStatus);
  919. //set the status
  920. resourceEntry->Online = FALSE;
  921. resourceEntry->dwServicePid = 0;
  922. break;
  923. }
  924. (g_LogEvent)(
  925. resourceEntry->ResourceHandle,
  926. LOG_INFORMATION,
  927. L"GenSvcTerminate: retrying...\n" );
  928. Sleep(dwRetryTick);
  929. }
  930. //declare this service is offline
  931. //if there is a pid for this, try and terminate that process
  932. //note that terminating a process doesnt terminate all
  933. //the child processes
  934. //also if it is running in a system process, we do nothing
  935. //about it
  936. if (resourceEntry->dwServicePid)
  937. {
  938. DWORD dwResourceState;
  939. (g_LogEvent)(
  940. resourceEntry->ResourceHandle,
  941. LOG_INFORMATION,
  942. L"GenSvcTerminate: Attempting to terminate process with pid=%1!u!...\n",
  943. resourceEntry->dwServicePid );
  944. ResUtilTerminateServiceProcessFromResDll( resourceEntry->dwServicePid,
  945. FALSE, // bOffline
  946. &dwResourceState,
  947. g_LogEvent,
  948. resourceEntry->ResourceHandle );
  949. }
  950. CloseServiceHandle( resourceEntry->ServiceHandle );
  951. resourceEntry->ServiceHandle = NULL;
  952. resourceEntry->dwServicePid = 0;
  953. }
  954. resourceEntry->Online = FALSE;
  955. return;
  956. } // GenSvcTerminate
  957. DWORD
  958. WINAPI
  959. GenSvcOffline(
  960. IN RESID ResourceId
  961. )
  962. /*++
  963. Routine Description:
  964. Offline routine for Generic Service resource.
  965. Arguments:
  966. ResourceId - Supplies the resource to be taken offline
  967. Return Value:
  968. ERROR_SUCCESS - always successful.
  969. --*/
  970. {
  971. PGENSVC_RESOURCE resourceEntry;
  972. DWORD status;
  973. resourceEntry = (PGENSVC_RESOURCE)ResourceId;
  974. if ( resourceEntry == NULL ) {
  975. DBG_PRINT( "GenSvc: Offline request for a nonexistent resource id 0x%p.\n",
  976. ResourceId );
  977. return(ERROR_RESOURCE_NOT_FOUND);
  978. }
  979. ClusWorkerTerminate( &resourceEntry->PendingThread );
  980. status = ClusWorkerCreate( &resourceEntry->PendingThread,
  981. GenSvcOfflineThread,
  982. resourceEntry );
  983. if ( status == ERROR_SUCCESS ) {
  984. status = ERROR_IO_PENDING;
  985. }
  986. return(status);
  987. } // GenSvcOffline
  988. DWORD
  989. GenSvcOfflineThread(
  990. PCLUS_WORKER pWorker,
  991. IN PGENSVC_RESOURCE ResourceEntry
  992. )
  993. /*++
  994. Routine Description:
  995. Brings a generic resource offline
  996. Arguments:
  997. Worker - Supplies the worker structure
  998. Context - A pointer to the DiskInfo block for this resource.
  999. Returns:
  1000. ERROR_SUCCESS if successful.
  1001. Win32 error code on failure.
  1002. --*/
  1003. {
  1004. RESOURCE_STATUS resourceStatus;
  1005. DWORD retryTick = 300; // 300 msec at a time
  1006. DWORD status = ERROR_SUCCESS;
  1007. BOOL didStop = FALSE;
  1008. SERVICE_STATUS ServiceStatus;
  1009. DWORD dwRetryCount = 2000; // Try 10 min max.
  1010. ResUtilInitializeResourceStatus( &resourceStatus );
  1011. resourceStatus.ResourceState = ClusterResourceFailed;
  1012. resourceStatus.CheckPoint = 1;
  1013. //check if the service has gone offline or was never brought online
  1014. if ( ResourceEntry->ServiceHandle == NULL )
  1015. {
  1016. resourceStatus.ResourceState = ClusterResourceOffline;
  1017. goto FnExit;
  1018. }
  1019. //try to stop the cluster service, wait for it to be terminated
  1020. //as long as we are not asked to terminate
  1021. while (!ClusWorkerCheckTerminate(pWorker) && dwRetryCount--) {
  1022. //
  1023. // Tell the Resource Monitor that we are still working.
  1024. //
  1025. resourceStatus.ResourceState = ClusterResourceOfflinePending;
  1026. resourceStatus.CheckPoint++;
  1027. (g_SetResourceStatus)( ResourceEntry->ResourceHandle,
  1028. &resourceStatus );
  1029. resourceStatus.ResourceState = ClusterResourceFailed;
  1030. //
  1031. // Request that the service be stopped, or if we already did that,
  1032. // request the current status of the service.
  1033. //
  1034. status = (ControlService(
  1035. ResourceEntry->ServiceHandle,
  1036. (didStop
  1037. ? SERVICE_CONTROL_INTERROGATE
  1038. : SERVICE_CONTROL_STOP),
  1039. &ServiceStatus )
  1040. ? NO_ERROR
  1041. : GetLastError());
  1042. if (status == NO_ERROR) {
  1043. didStop = TRUE;
  1044. if (ServiceStatus.dwCurrentState == SERVICE_STOPPED) {
  1045. (g_LogEvent)(
  1046. ResourceEntry->ResourceHandle,
  1047. LOG_INFORMATION,
  1048. L"Service stopped.\n" );
  1049. //set the status
  1050. ResourceEntry->Online = FALSE;
  1051. resourceStatus.ResourceState = ClusterResourceOffline;
  1052. CloseServiceHandle( ResourceEntry->ServiceHandle );
  1053. ResourceEntry->ServiceHandle = NULL;
  1054. ResourceEntry->dwServicePid = 0;
  1055. (g_LogEvent)(
  1056. ResourceEntry->ResourceHandle,
  1057. LOG_INFORMATION,
  1058. L"Service is now offline.\n" );
  1059. break;
  1060. }
  1061. }
  1062. if (status == ERROR_EXCEPTION_IN_SERVICE ||
  1063. status == ERROR_PROCESS_ABORTED ||
  1064. status == ERROR_SERVICE_NOT_ACTIVE) {
  1065. (g_LogEvent)(
  1066. ResourceEntry->ResourceHandle,
  1067. LOG_INFORMATION,
  1068. L"Service died or not active any more; status = %1!u!.\n",
  1069. status);
  1070. //set the status
  1071. ResourceEntry->Online = FALSE;
  1072. resourceStatus.ResourceState = ClusterResourceOffline;
  1073. CloseServiceHandle( ResourceEntry->ServiceHandle );
  1074. ResourceEntry->ServiceHandle = NULL;
  1075. ResourceEntry->dwServicePid = 0;
  1076. (g_LogEvent)(
  1077. ResourceEntry->ResourceHandle,
  1078. LOG_INFORMATION,
  1079. L"Service is now offline.\n" );
  1080. break;
  1081. }
  1082. //
  1083. // Chittur Subbaraman (chitturs) - 2/21/2000
  1084. //
  1085. // Handle the case in which the SCM refuses to accept control
  1086. // requests since windows is shutting down.
  1087. //
  1088. if ( status == ERROR_SHUTDOWN_IN_PROGRESS )
  1089. {
  1090. DWORD dwResourceState;
  1091. (g_LogEvent)(
  1092. ResourceEntry->ResourceHandle,
  1093. LOG_INFORMATION,
  1094. L"GenSvcOfflineThread: System shutting down. Attempt to terminate service process %1!u!...\n",
  1095. ResourceEntry->dwServicePid );
  1096. status = ResUtilTerminateServiceProcessFromResDll( ResourceEntry->dwServicePid,
  1097. TRUE, // bOffline
  1098. &dwResourceState,
  1099. g_LogEvent,
  1100. ResourceEntry->ResourceHandle );
  1101. if ( status == ERROR_SUCCESS )
  1102. {
  1103. CloseServiceHandle( ResourceEntry->ServiceHandle );
  1104. ResourceEntry->ServiceHandle = NULL;
  1105. ResourceEntry->dwServicePid = 0;
  1106. ResourceEntry->Online = FALSE;
  1107. }
  1108. resourceStatus.ResourceState = dwResourceState;
  1109. break;
  1110. }
  1111. (g_LogEvent)(
  1112. ResourceEntry->ResourceHandle,
  1113. LOG_INFORMATION,
  1114. L"Offline: retrying...\n" );
  1115. Sleep(retryTick);
  1116. }
  1117. FnExit:
  1118. (g_SetResourceStatus)( ResourceEntry->ResourceHandle,
  1119. &resourceStatus );
  1120. return(status);
  1121. } // GenSvcOfflineThread
  1122. BOOL
  1123. WINAPI
  1124. GenSvcIsAlive(
  1125. IN RESID ResourceId
  1126. )
  1127. /*++
  1128. Routine Description:
  1129. IsAlive routine for Generic service resource.
  1130. Arguments:
  1131. ResourceId - Supplies the resource id to be polled.
  1132. Return Value:
  1133. TRUE - if service is running
  1134. FALSE - if service is in any other state
  1135. --*/
  1136. {
  1137. return( VerifyService( ResourceId, TRUE ) );
  1138. } // GenSvcIsAlive
  1139. BOOL
  1140. VerifyService(
  1141. IN RESID ResourceId,
  1142. IN BOOL IsAliveFlag
  1143. )
  1144. /*++
  1145. Routine Description:
  1146. Verify that a specified service is running
  1147. Arguments:
  1148. ResourceId - Supplies the resource id
  1149. IsAliveFlag - Says this is an IsAlive call - used only for debug print
  1150. Return Value:
  1151. TRUE - if service is running or starting
  1152. FALSE - service is in any other state
  1153. --*/
  1154. {
  1155. SERVICE_STATUS ServiceStatus;
  1156. PGENSVC_RESOURCE resourceEntry;
  1157. DWORD status = TRUE;
  1158. resourceEntry = (PGENSVC_RESOURCE)ResourceId;
  1159. if ( resourceEntry == NULL ) {
  1160. DBG_PRINT( "GenSvc: IsAlive request for a nonexistent resource id 0x%p.\n",
  1161. ResourceId );
  1162. return(FALSE);
  1163. }
  1164. #ifdef TEST_TERMINATE_ON_SHUTDOWN
  1165. //
  1166. // Test terminate on shutdown.
  1167. //
  1168. if ( IsAliveFlag ) {
  1169. (g_LogEvent)(
  1170. resourceEntry->ResourceHandle,
  1171. LOG_ERROR,
  1172. L"GenSvcIsAlive: TEST_TERMINATE_ON_SHUTDOWN - Artificially failing IsAlive call.\n",
  1173. GetLastError() );
  1174. return FALSE;
  1175. }
  1176. #endif
  1177. if ( !QueryServiceStatus( resourceEntry->ServiceHandle,
  1178. &ServiceStatus ) ) {
  1179. (g_LogEvent)(
  1180. resourceEntry->ResourceHandle,
  1181. LOG_ERROR,
  1182. L"Query Service Status failed %1!u!.\n",
  1183. GetLastError() );
  1184. return(FALSE);
  1185. }
  1186. //
  1187. // Now check the status of the service
  1188. //
  1189. if ( (ServiceStatus.dwCurrentState != SERVICE_RUNNING) &&
  1190. (ServiceStatus.dwCurrentState != SERVICE_START_PENDING) ) {
  1191. status = FALSE;
  1192. }
  1193. if (!status) {
  1194. (g_LogEvent)(
  1195. resourceEntry->ResourceHandle,
  1196. LOG_ERROR,
  1197. L"Failed the IsAlive test. Current State is %1!u!.\n",
  1198. ServiceStatus.dwCurrentState );
  1199. }
  1200. return(status);
  1201. } // Verify Service
  1202. BOOL
  1203. WINAPI
  1204. GenSvcLooksAlive(
  1205. IN RESID ResourceId
  1206. )
  1207. /*++
  1208. Routine Description:
  1209. LooksAlive routine for Generic Service resource.
  1210. Arguments:
  1211. ResourceId - Supplies the resource id to be polled.
  1212. Return Value:
  1213. TRUE - Resource looks like it is alive and well
  1214. FALSE - Resource looks like it is toast.
  1215. --*/
  1216. {
  1217. return( VerifyService( ResourceId, FALSE ) );
  1218. } // GenSvcLooksAlive
  1219. VOID
  1220. WINAPI
  1221. GenSvcClose(
  1222. IN RESID ResourceId
  1223. )
  1224. /*++
  1225. Routine Description:
  1226. Close routine for Generic Applications resource.
  1227. This routine will stop the service, and delete the cluster
  1228. information regarding that service.
  1229. Arguments:
  1230. ResourceId - Supplies resource id to be closed
  1231. Return Value:
  1232. None.
  1233. --*/
  1234. {
  1235. PGENSVC_RESOURCE resourceEntry;
  1236. resourceEntry = (PGENSVC_RESOURCE)ResourceId;
  1237. if ( resourceEntry == NULL ) {
  1238. DBG_PRINT( "GenSvc: Close request for a nonexistent resource id 0x%p\n",
  1239. ResourceId );
  1240. return;
  1241. }
  1242. (g_LogEvent)(
  1243. resourceEntry->ResourceHandle,
  1244. LOG_INFORMATION,
  1245. L"Close request.\n" );
  1246. //
  1247. // Shut it down if it's on line
  1248. //
  1249. GenSvcTerminate( ResourceId );
  1250. ResUtilFreeParameterBlock( (LPBYTE) &(resourceEntry->Params),
  1251. NULL,
  1252. GenSvcResourcePrivateProperties );
  1253. ClusterRegCloseKey( resourceEntry->ParametersKey );
  1254. ClusterRegCloseKey( resourceEntry->ResourceKey );
  1255. CloseClusterResource( resourceEntry->hResource );
  1256. CloseHandle(resourceEntry->hSem);
  1257. LocalFree( resourceEntry );
  1258. } // GenSvcClose
  1259. //
  1260. // Following logic stolen from the CRTs so that our command line parsing
  1261. // works the same as the standard CRT parsing.
  1262. //
  1263. void
  1264. wparse_cmdline (
  1265. WCHAR *cmdstart,
  1266. WCHAR **argv,
  1267. WCHAR *args,
  1268. int *numargs,
  1269. int *numchars
  1270. )
  1271. {
  1272. WCHAR *p;
  1273. WCHAR c;
  1274. int inquote; /* 1 = inside quotes */
  1275. int copychar; /* 1 = copy char to *args */
  1276. unsigned numslash; /* num of backslashes seen */
  1277. *numchars = 0;
  1278. *numargs = 0;
  1279. p = cmdstart;
  1280. inquote = 0;
  1281. /* loop on each argument */
  1282. for(;;) {
  1283. if ( *p ) {
  1284. while (*p == L' ' || *p == L'\t')
  1285. ++p;
  1286. }
  1287. if (*p == L'\0')
  1288. break; /* end of args */
  1289. /* scan an argument */
  1290. if (argv)
  1291. *argv++ = args; /* store ptr to arg */
  1292. ++*numargs;
  1293. /* loop through scanning one argument */
  1294. for (;;) {
  1295. copychar = 1;
  1296. /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
  1297. 2N+1 backslashes + " ==> N backslashes + literal "
  1298. N backslashes ==> N backslashes */
  1299. numslash = 0;
  1300. while (*p == L'\\') {
  1301. /* count number of backslashes for use below */
  1302. ++p;
  1303. ++numslash;
  1304. }
  1305. if (*p == L'\"') {
  1306. /* if 2N backslashes before, start/end quote, otherwise
  1307. copy literally */
  1308. if (numslash % 2 == 0) {
  1309. if (inquote) {
  1310. if (p[1] == L'\"')
  1311. p++; /* Double quote inside quoted string */
  1312. else /* skip first quote char and copy second */
  1313. copychar = 0;
  1314. } else
  1315. copychar = 0; /* don't copy quote */
  1316. inquote = !inquote;
  1317. }
  1318. numslash /= 2; /* divide numslash by two */
  1319. }
  1320. /* copy slashes */
  1321. while (numslash--) {
  1322. if (args)
  1323. *args++ = L'\\';
  1324. ++*numchars;
  1325. }
  1326. /* if at end of arg, break loop */
  1327. if (*p == L'\0' || (!inquote && (*p == L' ' || *p == L'\t')))
  1328. break;
  1329. /* copy character into argument */
  1330. if (copychar) {
  1331. if (args)
  1332. *args++ = *p;
  1333. ++*numchars;
  1334. }
  1335. ++p;
  1336. }
  1337. /* null-terminate the argument */
  1338. if (args)
  1339. *args++ = L'\0'; /* terminate string */
  1340. ++*numchars;
  1341. }
  1342. } // wparse_cmdline
  1343. DWORD
  1344. GenSvcResourceControl(
  1345. IN RESID ResourceId,
  1346. IN DWORD ControlCode,
  1347. IN PVOID InBuffer,
  1348. IN DWORD InBufferSize,
  1349. OUT PVOID OutBuffer,
  1350. IN DWORD OutBufferSize,
  1351. OUT LPDWORD BytesReturned
  1352. )
  1353. /*++
  1354. Routine Description:
  1355. ResourceControl routine for Generic Service resources.
  1356. Perform the control request specified by ControlCode on the specified
  1357. resource.
  1358. Arguments:
  1359. ResourceId - Supplies the resource id for the specific resource.
  1360. ControlCode - Supplies the control code that defines the action
  1361. to be performed.
  1362. InBuffer - Supplies a pointer to a buffer containing input data.
  1363. InBufferSize - Supplies the size, in bytes, of the data pointed
  1364. to by InBuffer.
  1365. OutBuffer - Supplies a pointer to the output buffer to be filled in.
  1366. OutBufferSize - Supplies the size, in bytes, of the available space
  1367. pointed to by OutBuffer.
  1368. BytesReturned - Returns the number of bytes of OutBuffer actually
  1369. filled in by the resource. If OutBuffer is too small, BytesReturned
  1370. contains the total number of bytes for the operation to succeed.
  1371. Return Value:
  1372. ERROR_SUCCESS - The function completed successfully.
  1373. ERROR_INVALID_FUNCTION - The requested control code is not supported.
  1374. In some cases, this allows the cluster software to perform the work.
  1375. Win32 error code - The function failed.
  1376. --*/
  1377. {
  1378. DWORD status;
  1379. PGENSVC_RESOURCE resourceEntry;
  1380. DWORD required;
  1381. resourceEntry = (PGENSVC_RESOURCE)ResourceId;
  1382. if ( resourceEntry == NULL ) {
  1383. DBG_PRINT( "GenSvc: ResourceControl request for a nonexistent resource id 0x%p\n",
  1384. ResourceId );
  1385. return(FALSE);
  1386. }
  1387. switch ( ControlCode ) {
  1388. case CLUSCTL_RESOURCE_UNKNOWN:
  1389. *BytesReturned = 0;
  1390. status = ERROR_SUCCESS;
  1391. break;
  1392. case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTY_FMTS:
  1393. status = ResUtilGetPropertyFormats( GenSvcResourcePrivateProperties,
  1394. OutBuffer,
  1395. OutBufferSize,
  1396. BytesReturned,
  1397. &required );
  1398. if ( status == ERROR_MORE_DATA ) {
  1399. *BytesReturned = required;
  1400. }
  1401. break;
  1402. case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES:
  1403. status = ResUtilEnumProperties( GenSvcResourcePrivateProperties,
  1404. OutBuffer,
  1405. OutBufferSize,
  1406. BytesReturned,
  1407. &required );
  1408. if ( status == ERROR_MORE_DATA ) {
  1409. *BytesReturned = required;
  1410. }
  1411. break;
  1412. case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES:
  1413. status = GenSvcGetPrivateResProperties( resourceEntry,
  1414. OutBuffer,
  1415. OutBufferSize,
  1416. BytesReturned );
  1417. break;
  1418. case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES:
  1419. status = GenSvcValidatePrivateResProperties( resourceEntry,
  1420. InBuffer,
  1421. InBufferSize,
  1422. NULL );
  1423. break;
  1424. case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES:
  1425. status = GenSvcSetPrivateResProperties( resourceEntry,
  1426. InBuffer,
  1427. InBufferSize );
  1428. break;
  1429. default:
  1430. status = ERROR_INVALID_FUNCTION;
  1431. break;
  1432. }
  1433. return(status);
  1434. } // GenSvcResourceControl
  1435. DWORD
  1436. GenSvcResourceTypeControl(
  1437. IN LPCWSTR ResourceTypeName,
  1438. IN DWORD ControlCode,
  1439. IN PVOID InBuffer,
  1440. IN DWORD InBufferSize,
  1441. OUT PVOID OutBuffer,
  1442. IN DWORD OutBufferSize,
  1443. OUT LPDWORD BytesReturned
  1444. )
  1445. /*++
  1446. Routine Description:
  1447. ResourceTypeControl routine for Generic Service resources.
  1448. Perform the control request specified by ControlCode on this resource type.
  1449. Arguments:
  1450. ResourceTypeName - Supplies the resource type name.
  1451. ControlCode - Supplies the control code that defines the action
  1452. to be performed.
  1453. InBuffer - Supplies a pointer to a buffer containing input data.
  1454. InBufferSize - Supplies the size, in bytes, of the data pointed
  1455. to by InBuffer.
  1456. OutBuffer - Supplies a pointer to the output buffer to be filled in.
  1457. OutBufferSize - Supplies the size, in bytes, of the available space
  1458. pointed to by OutBuffer.
  1459. BytesReturned - Returns the number of bytes of OutBuffer actually
  1460. filled in by the resource. If OutBuffer is too small, BytesReturned
  1461. contains the total number of bytes for the operation to succeed.
  1462. Return Value:
  1463. ERROR_SUCCESS - The function completed successfully.
  1464. ERROR_INVALID_FUNCTION - The requested control code is not supported.
  1465. In some cases, this allows the cluster software to perform the work.
  1466. Win32 error code - The function failed.
  1467. --*/
  1468. {
  1469. DWORD status;
  1470. DWORD required;
  1471. switch ( ControlCode ) {
  1472. case CLUSCTL_RESOURCE_TYPE_UNKNOWN:
  1473. *BytesReturned = 0;
  1474. status = ERROR_SUCCESS;
  1475. break;
  1476. case CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_RESOURCE_PROPERTY_FMTS:
  1477. status = ResUtilGetPropertyFormats( GenSvcResourcePrivateProperties,
  1478. OutBuffer,
  1479. OutBufferSize,
  1480. BytesReturned,
  1481. &required );
  1482. if ( status == ERROR_MORE_DATA ) {
  1483. *BytesReturned = required;
  1484. }
  1485. break;
  1486. case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES:
  1487. status = ResUtilEnumProperties( GenSvcResourcePrivateProperties,
  1488. OutBuffer,
  1489. OutBufferSize,
  1490. BytesReturned,
  1491. &required );
  1492. if ( status == ERROR_MORE_DATA ) {
  1493. *BytesReturned = required;
  1494. }
  1495. break;
  1496. default:
  1497. status = ERROR_INVALID_FUNCTION;
  1498. break;
  1499. }
  1500. return(status);
  1501. } // GenSvcResourceTypeControl
  1502. DWORD
  1503. GenSvcGetPrivateResProperties(
  1504. IN const PGENSVC_RESOURCE ResourceEntry,
  1505. OUT PVOID OutBuffer,
  1506. IN DWORD OutBufferSize,
  1507. OUT LPDWORD BytesReturned
  1508. )
  1509. /*++
  1510. Routine Description:
  1511. Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control function
  1512. for resources of type Generic Service.
  1513. Arguments:
  1514. ResourceEntry - Supplies the resource entry on which to operate.
  1515. OutBuffer - Returns the output data.
  1516. OutBufferSize - Supplies the size, in bytes, of the data pointed
  1517. to by OutBuffer.
  1518. BytesReturned - The number of bytes returned in OutBuffer.
  1519. Return Value:
  1520. ERROR_SUCCESS - The function completed successfully.
  1521. ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
  1522. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  1523. Win32 error code - The function failed.
  1524. --*/
  1525. {
  1526. DWORD status;
  1527. DWORD required;
  1528. status = ResUtilGetAllProperties( ResourceEntry->ParametersKey,
  1529. GenSvcResourcePrivateProperties,
  1530. OutBuffer,
  1531. OutBufferSize,
  1532. BytesReturned,
  1533. &required );
  1534. if ( status == ERROR_MORE_DATA ) {
  1535. *BytesReturned = required;
  1536. }
  1537. return(status);
  1538. } // GenSvcGetPrivateResProperties
  1539. DWORD
  1540. GenSvcValidatePrivateResProperties(
  1541. IN const PGENSVC_RESOURCE ResourceEntry,
  1542. IN const PVOID InBuffer,
  1543. IN DWORD InBufferSize,
  1544. OUT PGENSVC_PARAMS Params
  1545. )
  1546. /*++
  1547. Routine Description:
  1548. Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control
  1549. function for resources of type Generic Service.
  1550. Arguments:
  1551. ResourceEntry - Supplies the resource entry on which to operate.
  1552. InBuffer - Supplies a pointer to a buffer containing input data.
  1553. InBufferSize - Supplies the size, in bytes, of the data pointed
  1554. to by InBuffer.
  1555. Params - Supplies the parameter block to fill in.
  1556. Return Value:
  1557. ERROR_SUCCESS - The function completed successfully.
  1558. ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
  1559. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  1560. ERROR_DEPENDENCY_NOT_FOUND - Trying to set UseNetworkName when there
  1561. is no dependency on a Network Name resource.
  1562. Win32 error code - The function failed.
  1563. --*/
  1564. {
  1565. DWORD status;
  1566. GENSVC_PARAMS currentProps;
  1567. GENSVC_PARAMS newProps;
  1568. PGENSVC_PARAMS pParams = NULL;
  1569. BOOL hResDependency;
  1570. LPWSTR lpwTemp=NULL;
  1571. LPWSTR nameOfPropInError;
  1572. WCHAR netnameBuffer[ MAX_PATH + 1 ];
  1573. DWORD netnameBufferSize = sizeof( netnameBuffer ) / sizeof( WCHAR );
  1574. //
  1575. // Check if there is input data.
  1576. //
  1577. if ( (InBuffer == NULL) ||
  1578. (InBufferSize < sizeof(DWORD)) ) {
  1579. return(ERROR_INVALID_DATA);
  1580. }
  1581. //
  1582. // Retrieve the current set of private properties from the
  1583. // cluster database.
  1584. //
  1585. ZeroMemory( &currentProps, sizeof(currentProps) );
  1586. status = ResUtilGetPropertiesToParameterBlock(
  1587. ResourceEntry->ParametersKey,
  1588. GenSvcResourcePrivateProperties,
  1589. (LPBYTE) &currentProps,
  1590. FALSE, /*CheckForRequiredProperties*/
  1591. &nameOfPropInError
  1592. );
  1593. if ( status != ERROR_SUCCESS ) {
  1594. (g_LogEvent)(
  1595. ResourceEntry->ResourceHandle,
  1596. LOG_ERROR,
  1597. L"Unable to read the '%1!ls!' property. Error: %2!u!.\n",
  1598. (nameOfPropInError == NULL ? L"" : nameOfPropInError),
  1599. status
  1600. );
  1601. goto FnExit;
  1602. }
  1603. //
  1604. // Duplicate the resource parameter block.
  1605. //
  1606. if ( Params == NULL ) {
  1607. pParams = &newProps;
  1608. } else {
  1609. pParams = Params;
  1610. }
  1611. ZeroMemory( pParams, sizeof(GENSVC_PARAMS) );
  1612. status = ResUtilDupParameterBlock( (LPBYTE) pParams,
  1613. (LPBYTE) &currentProps,
  1614. GenSvcResourcePrivateProperties
  1615. );
  1616. if ( status != ERROR_SUCCESS ) {
  1617. return(status);
  1618. }
  1619. //
  1620. // Parse and validate the properties.
  1621. //
  1622. status = ResUtilVerifyPropertyTable( GenSvcResourcePrivateProperties,
  1623. NULL,
  1624. TRUE, // Allow unknowns
  1625. InBuffer,
  1626. InBufferSize,
  1627. (LPBYTE) pParams
  1628. );
  1629. if ( status != ERROR_SUCCESS ) {
  1630. goto FnExit;
  1631. }
  1632. //
  1633. // Validate the parameter values.
  1634. //
  1635. status = GenSvcIsValidService( ResourceEntry, pParams->ServiceName );
  1636. if ( status != ERROR_SUCCESS ) {
  1637. goto FnExit;
  1638. }
  1639. //
  1640. // If the resource should use the network name as the computer
  1641. // name, make sure there is a dependency on a Network Name
  1642. // resource.
  1643. //
  1644. if ( pParams->UseNetworkName ) {
  1645. hResDependency = GetClusterResourceNetworkName( ResourceEntry->hResource,
  1646. netnameBuffer,
  1647. &netnameBufferSize
  1648. );
  1649. if ( ! hResDependency ) {
  1650. status = ERROR_DEPENDENCY_NOT_FOUND;
  1651. }
  1652. }
  1653. if ( status != ERROR_SUCCESS ) {
  1654. goto FnExit;
  1655. }
  1656. if ( ResourceEntry->hSem == NULL ) {
  1657. // This is executed only if this is a new resource being created
  1658. lpwTemp = (LPWSTR) LocalAlloc( LMEM_FIXED,
  1659. ( lstrlenW( pParams->ServiceName ) + lstrlenW( L"GenSvc$" ) + 1 ) * sizeof(WCHAR) );
  1660. if ( lpwTemp == NULL ) {
  1661. status = GetLastError();
  1662. (g_LogEvent)(
  1663. ResourceEntry->ResourceHandle,
  1664. LOG_ERROR,
  1665. L"Service '%1!ls!': Not enough memory for storing semaphore name. Error: %2!u!.\n",
  1666. pParams->ServiceName,
  1667. status
  1668. );
  1669. goto FnExit;
  1670. }
  1671. lstrcpyW( lpwTemp, L"GenSvc$" );
  1672. lstrcat( lpwTemp, pParams->ServiceName );
  1673. ResourceEntry->hSem = CreateSemaphore( NULL, 0, 1,lpwTemp );
  1674. status=GetLastError();
  1675. if ( ResourceEntry->hSem ) {
  1676. // Check if there is another resource controlling the same service
  1677. if ( status == ERROR_OBJECT_ALREADY_EXISTS ) {
  1678. (g_LogEvent)(
  1679. ResourceEntry->ResourceHandle,
  1680. LOG_ERROR,
  1681. L"Service '%1!ls!' is controlled by another resource. Error: %2!u!.\n",
  1682. pParams->ServiceName,
  1683. status
  1684. );
  1685. CloseHandle( ResourceEntry->hSem );
  1686. ResourceEntry->hSem = NULL;
  1687. goto FnExit;
  1688. }
  1689. } else {
  1690. (g_LogEvent)(
  1691. ResourceEntry->ResourceHandle,
  1692. LOG_ERROR,
  1693. L"SetPrivateProperties: Unable to create Semaphore %1!ls! for Service '%2!ls!' . Error: %3!u!.\n",
  1694. lpwTemp,
  1695. pParams->ServiceName,
  1696. status
  1697. );
  1698. return(status);
  1699. }
  1700. }
  1701. FnExit:
  1702. //
  1703. // Cleanup our parameter block.
  1704. //
  1705. if ( ( (status != ERROR_SUCCESS)
  1706. && (pParams != NULL) )
  1707. || ( pParams == &newProps )
  1708. ) {
  1709. ResUtilFreeParameterBlock( (LPBYTE) pParams,
  1710. (LPBYTE) &currentProps,
  1711. GenSvcResourcePrivateProperties
  1712. );
  1713. }
  1714. ResUtilFreeParameterBlock(
  1715. (LPBYTE) &currentProps,
  1716. NULL,
  1717. GenSvcResourcePrivateProperties
  1718. );
  1719. if ( lpwTemp ) {
  1720. LocalFree( lpwTemp );
  1721. }
  1722. return(status);
  1723. } // GenSvcValidatePrivateResProperties
  1724. DWORD
  1725. GenSvcSetPrivateResProperties(
  1726. IN OUT PGENSVC_RESOURCE ResourceEntry,
  1727. IN const PVOID InBuffer,
  1728. IN DWORD InBufferSize
  1729. )
  1730. /*++
  1731. Routine Description:
  1732. Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function
  1733. for resources of type Generic Service.
  1734. Arguments:
  1735. ResourceEntry - Supplies the resource entry on which to operate.
  1736. InBuffer - Supplies a pointer to a buffer containing input data.
  1737. InBufferSize - Supplies the size, in bytes, of the data pointed
  1738. to by InBuffer.
  1739. Return Value:
  1740. ERROR_SUCCESS - The function completed successfully.
  1741. ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
  1742. ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  1743. Win32 error code - The function failed.
  1744. --*/
  1745. {
  1746. DWORD status;
  1747. GENSVC_PARAMS params;
  1748. ZeroMemory( &params, sizeof(GENSVC_PARAMS) );
  1749. //
  1750. // Parse and validate the properties.
  1751. //
  1752. status = GenSvcValidatePrivateResProperties( ResourceEntry,
  1753. InBuffer,
  1754. InBufferSize,
  1755. &params );
  1756. if ( status != ERROR_SUCCESS ) {
  1757. return(status);
  1758. }
  1759. //
  1760. // Save the parameter values.
  1761. //
  1762. status = ResUtilSetPropertyParameterBlock( ResourceEntry->ParametersKey,
  1763. GenSvcResourcePrivateProperties,
  1764. NULL,
  1765. (LPBYTE) &params,
  1766. InBuffer,
  1767. InBufferSize,
  1768. (LPBYTE) &ResourceEntry->Params );
  1769. ResUtilFreeParameterBlock( (LPBYTE) &params,
  1770. (LPBYTE) &ResourceEntry->Params,
  1771. GenSvcResourcePrivateProperties );
  1772. //
  1773. // If the resource is online, return a non-success status.
  1774. //
  1775. if (status == ERROR_SUCCESS) {
  1776. if ( ResourceEntry->Online ) {
  1777. status = ERROR_RESOURCE_PROPERTIES_STORED;
  1778. } else {
  1779. status = ERROR_SUCCESS;
  1780. }
  1781. }
  1782. return status;
  1783. } // GenSvcSetPrivateResProperties
  1784. DWORD
  1785. GenSvcInvalidGenericServiceCheck(
  1786. IN OUT PGENSVC_RESOURCE ResourceEntry,
  1787. IN LPCWSTR ServiceName,
  1788. IN HKEY ServiceKey
  1789. )
  1790. /*++
  1791. Routine Description:
  1792. Determines if the specified service is an invalid service for
  1793. use as a generic service. Invalid services include the cluster
  1794. service and any services upon which it depends.
  1795. Arguments:
  1796. ResourceEntry - Supplies the resource entry on which to operate.
  1797. ServiceName - Service name to verify.
  1798. ServiceKey - Key for the root to begin checking at. If specified as
  1799. NULL, the cluster service key will be opened.
  1800. Return Value:
  1801. ERROR_SUCCESS - Service is valid for a Generic Service resource.
  1802. ERROR_NOT_SUPPORTED - Service can not be used for a Generic Service resource.
  1803. --*/
  1804. {
  1805. DWORD status;
  1806. DWORD valueType;
  1807. DWORD valueLength;
  1808. LPWSTR value = NULL;
  1809. LPWSTR currentValue;
  1810. HKEY clusterServiceKey = NULL;
  1811. HKEY serviceKey = NULL;
  1812. WCHAR serviceKeyName[2048];
  1813. //
  1814. // If no key specified, check against cluster service.
  1815. //
  1816. if ( ServiceKey == NULL ) {
  1817. if ( lstrcmpiW( ServiceName, L"ClusSvc" ) == 0 ) {
  1818. (g_LogEvent)(
  1819. ResourceEntry->ResourceHandle,
  1820. LOG_ERROR,
  1821. L"Resource for ClusSvc is not supported.\n"
  1822. );
  1823. return(ERROR_NOT_SUPPORTED);
  1824. }
  1825. status = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1826. SERVICES_ROOT L"ClusSvc",
  1827. 0,
  1828. KEY_READ,
  1829. &clusterServiceKey
  1830. );
  1831. if ( status != ERROR_SUCCESS ) {
  1832. (g_LogEvent)(
  1833. ResourceEntry->ResourceHandle,
  1834. LOG_ERROR,
  1835. L"Unable to open the '" SERVICES_ROOT L"ClusSvc' registry key. Error: %1!u!.\n",
  1836. status
  1837. );
  1838. return(status);
  1839. }
  1840. ServiceKey = clusterServiceKey;
  1841. }
  1842. //
  1843. // Read the DependOnService value.
  1844. //
  1845. status = RegQueryValueEx( ServiceKey,
  1846. L"DependOnService",
  1847. NULL,
  1848. &valueType,
  1849. NULL,
  1850. &valueLength
  1851. );
  1852. if ( status != ERROR_SUCCESS ) {
  1853. if ( status == ERROR_FILE_NOT_FOUND ) {
  1854. status = ERROR_SUCCESS;
  1855. } else {
  1856. (g_LogEvent)(
  1857. ResourceEntry->ResourceHandle,
  1858. LOG_ERROR,
  1859. L"Unable to get the length of the DependOnService value. Error: %1!u!.\n",
  1860. status
  1861. );
  1862. }
  1863. goto done;
  1864. }
  1865. value = LocalAlloc( LMEM_FIXED, valueLength );
  1866. if ( value == NULL ) {
  1867. status = GetLastError();
  1868. goto done;
  1869. }
  1870. status = RegQueryValueEx( ServiceKey,
  1871. L"DependOnService",
  1872. NULL,
  1873. &valueType,
  1874. (LPBYTE) value,
  1875. &valueLength
  1876. );
  1877. if ( status != ERROR_SUCCESS ) {
  1878. if ( status == ERROR_FILE_NOT_FOUND ) {
  1879. status = ERROR_SUCCESS;
  1880. } else {
  1881. (g_LogEvent)(
  1882. ResourceEntry->ResourceHandle,
  1883. LOG_ERROR,
  1884. L"Unable to read the DependOnService value. Error: %1!u!.\n",
  1885. status
  1886. );
  1887. }
  1888. goto done;
  1889. }
  1890. //
  1891. // Loop through each entry in the string.
  1892. //
  1893. for ( currentValue = value ;
  1894. ( ( currentValue-value ) * sizeof ( WCHAR ) < valueLength );
  1895. currentValue += lstrlenW( currentValue ) + 1 ) {
  1896. //
  1897. // Break out once you reach the end of the REG_MULTI_SZ
  1898. //
  1899. if ( *currentValue == L'\0' ) {
  1900. status = ERROR_SUCCESS;
  1901. goto done;
  1902. }
  1903. //
  1904. // Compare against this service.
  1905. //
  1906. if ( lstrcmpiW( ServiceName, currentValue ) == 0 ) {
  1907. (g_LogEvent)(
  1908. ResourceEntry->ResourceHandle,
  1909. LOG_ERROR,
  1910. L"Creating a resource for a service (%1!ls!) upon which ClusSvc is dependent, either directly or indirectly, is not supported.\n",
  1911. ServiceName
  1912. );
  1913. status = ERROR_NOT_SUPPORTED;
  1914. goto done;
  1915. }
  1916. //
  1917. // Open this service's key.
  1918. //
  1919. if ( (lstrlenW( ServiceName ) * sizeof( WCHAR )) + sizeof( SERVICES_ROOT ) > sizeof( serviceKeyName ) ) {
  1920. (g_LogEvent)(
  1921. ResourceEntry->ResourceHandle,
  1922. LOG_ERROR,
  1923. L"Registry key for the '%1!ls!' service is longer than %2!d! bytes.\n",
  1924. ServiceName,
  1925. sizeof( serviceKeyName )
  1926. );
  1927. status = ERROR_NOT_ENOUGH_MEMORY;
  1928. goto done;
  1929. }
  1930. wsprintfW( serviceKeyName, SERVICES_ROOT L"%s", currentValue );
  1931. status = RegOpenKey( HKEY_LOCAL_MACHINE,
  1932. serviceKeyName,
  1933. &serviceKey
  1934. );
  1935. if ( status != ERROR_SUCCESS ) {
  1936. if ( status == ERROR_FILE_NOT_FOUND ) {
  1937. // Ignore services that don't exist in the registry.
  1938. (g_LogEvent)(
  1939. ResourceEntry->ResourceHandle,
  1940. LOG_INFORMATION,
  1941. L"Unable to open the '%1!ls!' registry key. Error: %2!u!. Error ignored.\n",
  1942. serviceKeyName,
  1943. status
  1944. );
  1945. status = ERROR_SUCCESS;
  1946. continue;
  1947. } else {
  1948. (g_LogEvent)(
  1949. ResourceEntry->ResourceHandle,
  1950. LOG_ERROR,
  1951. L"Unable to open the '%1!ls!' registry key. Error: %2!u!.\n",
  1952. serviceKeyName,
  1953. status
  1954. );
  1955. goto done;
  1956. }
  1957. }
  1958. //
  1959. // Check this service's dependencies.
  1960. //
  1961. status = GenSvcInvalidGenericServiceCheck( ResourceEntry, ServiceName, serviceKey );
  1962. if ( status != ERROR_SUCCESS ) {
  1963. goto done;
  1964. }
  1965. RegCloseKey( serviceKey );
  1966. serviceKey = NULL;
  1967. }
  1968. done:
  1969. if ( clusterServiceKey != NULL ) {
  1970. RegCloseKey( clusterServiceKey );
  1971. }
  1972. if ( serviceKey != NULL ) {
  1973. RegCloseKey( serviceKey );
  1974. }
  1975. if ( value != NULL ) {
  1976. LocalFree( value );
  1977. }
  1978. return(status);
  1979. } // GenSvcInvalidGenericServiceCheck
  1980. DWORD
  1981. GenSvcIsValidService(
  1982. IN OUT PGENSVC_RESOURCE ResourceEntry,
  1983. IN LPCWSTR ServiceName
  1984. )
  1985. /*++
  1986. Routine Description:
  1987. Determines if the specified service is a valid service or not.
  1988. Arguments:
  1989. ResourceEntry - Supplies the resource entry on which to operate.
  1990. ServiceName - Service name to verify.
  1991. Return Value:
  1992. ERROR_SUCCESS - Service is valid for a Generic Service resource.
  1993. Any status returned by OpenSCManager(), OpenService(), or
  1994. GenSvcInvalidGenericServiceCheck.
  1995. --*/
  1996. {
  1997. DWORD status;
  1998. HANDLE scManagerHandle;
  1999. HANDLE serviceHandle;
  2000. scManagerHandle = OpenSCManager( NULL, // local machine
  2001. NULL, // ServicesActive database
  2002. SC_MANAGER_ALL_ACCESS ); // all access
  2003. if ( scManagerHandle == NULL ) {
  2004. status = GetLastError();
  2005. (g_LogEvent)(
  2006. ResourceEntry->ResourceHandle,
  2007. LOG_ERROR,
  2008. L"Cannot access service controller for validating service '%1!ws!'. Error: %2!u!....\n",
  2009. ServiceName,
  2010. status
  2011. );
  2012. return(status);
  2013. }
  2014. serviceHandle = OpenService( scManagerHandle,
  2015. ServiceName,
  2016. SERVICE_ALL_ACCESS );
  2017. if ( serviceHandle == NULL ) {
  2018. status = GetLastError();
  2019. (g_LogEvent)(
  2020. ResourceEntry->ResourceHandle,
  2021. LOG_ERROR,
  2022. L"Cannot open service '%1!ws!'. Error: %2!u!....\n",
  2023. ServiceName,
  2024. status
  2025. );
  2026. } else {
  2027. status = GenSvcInvalidGenericServiceCheck( ResourceEntry, ServiceName, NULL );
  2028. }
  2029. CloseServiceHandle( serviceHandle );
  2030. CloseServiceHandle( scManagerHandle );
  2031. return(status);
  2032. } // GenSvcIsValidService
  2033. //***********************************************************
  2034. //
  2035. // Define Function Table
  2036. //
  2037. //***********************************************************
  2038. CLRES_V1_FUNCTION_TABLE( GenSvcFunctionTable, // Name
  2039. CLRES_VERSION_V1_00, // Version
  2040. GenSvc, // Prefix
  2041. NULL, // Arbitrate
  2042. NULL, // Release
  2043. GenSvcResourceControl,// ResControl
  2044. GenSvcResourceTypeControl ); // ResTypeControl