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.

2280 lines
54 KiB

  1. /////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1998 Microsoft Corporation
  4. //
  5. // Module Name:
  6. //
  7. // Dummy.cpp
  8. //
  9. // Abstract:
  10. //
  11. // Resource DLL for Dummy (Dummy).
  12. //
  13. // Author:
  14. //
  15. // Galen Barbee (galenb) Sept 03, 1998
  16. //
  17. // Revision History:
  18. //
  19. // Notes:
  20. //
  21. /////////////////////////////////////////////////////////////////////////////
  22. #pragma comment(lib, "clusapi.lib")
  23. #pragma comment(lib, "resutils.lib")
  24. #define UNICODE 1
  25. #pragma warning( disable : 4115 ) // named type definition in parentheses
  26. #pragma warning( disable : 4201 ) // nonstandard extension used : nameless struct/union
  27. #pragma warning( disable : 4214 ) // nonstandard extension used : bit field types other than int
  28. #include <windows.h>
  29. #pragma warning( default : 4214 ) // nonstandard extension used : bit field types other than int
  30. #pragma warning( default : 4201 ) // nonstandard extension used : nameless struct/union
  31. #pragma warning( default : 4115 ) // named type definition in parentheses
  32. #include <clusapi.h>
  33. #include <resapi.h>
  34. #include <stdio.h>
  35. //
  36. // Type and constant definitions.
  37. //
  38. #define DUMMY_RESNAME L"Dummy"
  39. #define DBG_PRINT printf
  40. #define MAX_WAIT (10000) // wait for 10 seconds
  41. #define DUMMY_FLAG_VALID 0x00000001
  42. #define DUMMY_FLAG_ASYNC 0x00000002 // Asynchronous failure mode
  43. #define DUMMY_FLAG_PENDING 0x00000004 // Pending mode on shutdown
  44. #define AsyncMode(Resource) (Resource->Flags & DUMMY_FLAG_ASYNC)
  45. #define PendingMode(Resource) (Resource->Flags & DUMMY_FLAG_PENDING)
  46. #define EnterAsyncMode(Resource) (Resource->Flags |= DUMMY_FLAG_ASYNC)
  47. #define DummyAcquireResourceLock(_res) EnterCriticalSection(&((_res)->Lock))
  48. #define DummyReleaseResourceLock(_res) LeaveCriticalSection(&((_res)->Lock))
  49. #define DummyAcquireGlobalLock() \
  50. { \
  51. DWORD status; \
  52. status = WaitForSingleObject( DummyGlobalMutex, INFINITE ); \
  53. }
  54. #define DummyReleaseGlobalLock() \
  55. { \
  56. BOOLEAN released; \
  57. released = ReleaseMutex( DummyGlobalMutex ); \
  58. }
  59. //
  60. // ADDPARAM: Add new parameters here.
  61. //
  62. #define PARAM_NAME__PENDING L"Pending"
  63. #define PARAM_NAME__PENDTIME L"PendTime"
  64. #define PARAM_NAME__OPENSFAIL L"OpensFail"
  65. #define PARAM_NAME__FAILED L"Failed"
  66. #define PARAM_NAME__ASYNCHRONOUS L"Asynchronous"
  67. #define PARAM_MIN__PENDING (0)
  68. #define PARAM_MAX__PENDING (1)
  69. #define PARAM_DEFAULT__PENDING (0)
  70. #define PARAM_MIN__PENDTIME (0)
  71. #define PARAM_MAX__PENDTIME (4294967295)
  72. #define PARAM_DEFAULT__PENDTIME (0)
  73. #define PARAM_MIN__OPENSFAIL (0)
  74. #define PARAM_MAX__OPENSFAIL (1)
  75. #define PARAM_DEFAULT__OPENSFAIL (0)
  76. #define PARAM_MIN__FAILED (0)
  77. #define PARAM_MAX__FAILED (1)
  78. #define PARAM_DEFAULT__FAILED (0)
  79. #define PARAM_MIN__ASYNCHRONOUS (0)
  80. #define PARAM_MAX__ASYNCHRONOUS (1)
  81. #define PARAM_DEFAULT__ASYNCHRONOUS (0)
  82. typedef enum TimerType
  83. {
  84. TimerNotUsed = 0,
  85. TimerErrorPending,
  86. TimerOnlinePending,
  87. TimerOfflinePending
  88. };
  89. //
  90. // ADDPARAM: Add new parameters here.
  91. //
  92. typedef struct _DUMMY_PARAMS
  93. {
  94. DWORD Pending;
  95. DWORD PendTime;
  96. DWORD OpensFail;
  97. DWORD Failed;
  98. DWORD Asynchronous;
  99. } DUMMY_PARAMS, *PDUMMY_PARAMS;
  100. typedef struct _DUMMY_RESOURCE
  101. {
  102. RESID ResId; // for validation
  103. DUMMY_PARAMS Params;
  104. HKEY ParametersKey;
  105. RESOURCE_HANDLE ResourceHandle;
  106. LPWSTR ResourceName;
  107. CLUS_WORKER OnlineThread;
  108. CLUS_WORKER OfflineThread;
  109. CLUSTER_RESOURCE_STATE State;
  110. DWORD Flags;
  111. HANDLE SignalEvent;
  112. HANDLE TimerThreadWakeup;
  113. DWORD TimerType;
  114. CRITICAL_SECTION Lock;
  115. } DUMMY_RESOURCE, *PDUMMY_RESOURCE;
  116. //
  117. // Global data.
  118. //
  119. // Sync Mutex
  120. HANDLE DummyGlobalMutex = NULL;
  121. // Event Logging routine.
  122. PLOG_EVENT_ROUTINE g_LogEvent = NULL;
  123. // Resource Status routine for pending Online and Offline calls.
  124. PSET_RESOURCE_STATUS_ROUTINE g_SetResourceStatus = NULL;
  125. // Forward reference to our RESAPI function table.
  126. extern CLRES_FUNCTION_TABLE g_DummyFunctionTable;
  127. //
  128. // Dummy resource read-write private properties.
  129. //
  130. RESUTIL_PROPERTY_ITEM
  131. DummyResourcePrivateProperties[] =
  132. {
  133. { PARAM_NAME__PENDING, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__PENDING, PARAM_MIN__PENDING, PARAM_MAX__PENDING, 0, FIELD_OFFSET(DUMMY_PARAMS,Pending) },
  134. { PARAM_NAME__PENDTIME, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__PENDTIME, PARAM_MIN__PENDTIME, PARAM_MAX__PENDTIME, 0, FIELD_OFFSET(DUMMY_PARAMS,PendTime) },
  135. { PARAM_NAME__OPENSFAIL, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__OPENSFAIL, PARAM_MIN__OPENSFAIL, PARAM_MAX__OPENSFAIL, 0, FIELD_OFFSET(DUMMY_PARAMS,OpensFail) },
  136. { PARAM_NAME__FAILED, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__FAILED, PARAM_MIN__FAILED, PARAM_MAX__FAILED, 0, FIELD_OFFSET(DUMMY_PARAMS,Failed) },
  137. { PARAM_NAME__ASYNCHRONOUS, NULL, CLUSPROP_FORMAT_DWORD, PARAM_DEFAULT__ASYNCHRONOUS, PARAM_MIN__ASYNCHRONOUS, PARAM_MAX__ASYNCHRONOUS, 0, FIELD_OFFSET(DUMMY_PARAMS,Asynchronous) },
  138. { 0 }
  139. };
  140. //
  141. // Function prototypes.
  142. //
  143. DWORD WINAPI Startup(
  144. IN LPCWSTR ResourceType,
  145. IN DWORD MinVersionSupported,
  146. IN DWORD MaxVersionSupported,
  147. IN PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus,
  148. IN PLOG_EVENT_ROUTINE LogEvent,
  149. OUT PCLRES_FUNCTION_TABLE *FunctionTable
  150. );
  151. RESID WINAPI DummyOpen(
  152. IN LPCWSTR ResourceName,
  153. IN HKEY ResourceKey,
  154. IN RESOURCE_HANDLE ResourceHandle
  155. );
  156. void WINAPI DummyClose(
  157. IN RESID ResourceId
  158. );
  159. DWORD WINAPI DummyOnline(
  160. IN RESID ResourceId,
  161. IN OUT PHANDLE EventHandle
  162. );
  163. DWORD WINAPI DummyOnlineThread(
  164. IN PCLUS_WORKER WorkerPtr,
  165. IN PDUMMY_RESOURCE ResourceEntry
  166. );
  167. DWORD WINAPI DummyOffline(
  168. IN RESID ResourceId
  169. );
  170. DWORD WINAPI DummyOfflineThread(
  171. PCLUS_WORKER WorkerPtr,
  172. IN PDUMMY_RESOURCE ResourceEntry
  173. );
  174. void WINAPI DummyTerminate(
  175. IN RESID ResourceId
  176. );
  177. DWORD DummyDoTerminate(
  178. IN PDUMMY_RESOURCE ResourceEntry
  179. );
  180. BOOL WINAPI DummyLooksAlive(
  181. IN RESID ResourceId
  182. );
  183. BOOL WINAPI DummyIsAlive(
  184. IN RESID ResourceId
  185. );
  186. BOOL DummyCheckIsAlive(
  187. IN PDUMMY_RESOURCE ResourceEntry
  188. );
  189. DWORD WINAPI DummyResourceControl(
  190. IN RESID ResourceId,
  191. IN DWORD ControlCode,
  192. IN void * InBuffer,
  193. IN DWORD InBufferSize,
  194. OUT void * OutBuffer,
  195. IN DWORD OutBufferSize,
  196. OUT LPDWORD BytesReturned
  197. );
  198. DWORD DummyGetPrivateResProperties(
  199. IN OUT PDUMMY_RESOURCE ResourceEntry,
  200. OUT void * OutBuffer,
  201. IN DWORD OutBufferSize,
  202. OUT LPDWORD BytesReturned
  203. );
  204. DWORD DummyValidatePrivateResProperties(
  205. IN OUT PDUMMY_RESOURCE ResourceEntry,
  206. IN const PVOID InBuffer,
  207. IN DWORD InBufferSize,
  208. OUT PDUMMY_PARAMS Params
  209. );
  210. DWORD DummySetPrivateResProperties(
  211. IN OUT PDUMMY_RESOURCE ResourceEntry,
  212. IN const PVOID InBuffer,
  213. IN DWORD InBufferSize
  214. );
  215. DWORD DummyTimerThread(
  216. IN PDUMMY_RESOURCE ResourceEntry,
  217. IN PCLUS_WORKER WorkerPtr
  218. );
  219. /////////////////////////////////////////////////////////////////////////////
  220. //++
  221. //
  222. // DummyInit
  223. //
  224. // Routine Description:
  225. //
  226. // Process attach initialization routine.
  227. //
  228. // Arguments:
  229. //
  230. // None.
  231. //
  232. // Return Value:
  233. //
  234. // TRUE if initialization succeeded. FALSE otherwise.
  235. //
  236. //--
  237. /////////////////////////////////////////////////////////////////////////////
  238. static BOOLEAN DummyInit(
  239. void
  240. )
  241. {
  242. DummyGlobalMutex = CreateMutex( NULL, FALSE, NULL );
  243. return DummyGlobalMutex != NULL;
  244. } //*** DummyInit()
  245. /////////////////////////////////////////////////////////////////////////////
  246. //++
  247. //
  248. // DummyCleanup
  249. //
  250. // Routine Description:
  251. //
  252. // Process detach cleanup routine.
  253. //
  254. // Arguments:
  255. //
  256. // None.
  257. //
  258. // Return Value:
  259. //
  260. // None.
  261. //
  262. //--
  263. /////////////////////////////////////////////////////////////////////////////
  264. static void DummyCleanup(
  265. void
  266. )
  267. {
  268. if ( DummyGlobalMutex != NULL )
  269. {
  270. CloseHandle( DummyGlobalMutex );
  271. DummyGlobalMutex = NULL;
  272. }
  273. return;
  274. } //*** DummyCleanup()
  275. /////////////////////////////////////////////////////////////////////////////
  276. //++
  277. //
  278. // DllMain
  279. //
  280. // Routine Description:
  281. //
  282. // Main DLL entry point.
  283. //
  284. // Arguments:
  285. //
  286. // DllHandle - DLL instance handle.
  287. //
  288. // Reason - Reason for being called.
  289. //
  290. // Reserved - Reserved argument.
  291. //
  292. // Return Value:
  293. //
  294. // TRUE - Success.
  295. //
  296. // FALSE - Failure.
  297. //
  298. //--
  299. /////////////////////////////////////////////////////////////////////////////
  300. BOOLEAN WINAPI DllMain(
  301. IN HINSTANCE DllHandle,
  302. IN DWORD Reason,
  303. IN void * //Reserved
  304. )
  305. {
  306. BOOLEAN bRet = TRUE;
  307. switch( Reason )
  308. {
  309. case DLL_PROCESS_ATTACH:
  310. DisableThreadLibraryCalls( DllHandle );
  311. bRet = DummyInit();
  312. break;
  313. case DLL_PROCESS_DETACH:
  314. DummyCleanup();
  315. break;
  316. }
  317. return bRet;
  318. } //*** DllMain()
  319. /////////////////////////////////////////////////////////////////////////////
  320. //++
  321. //
  322. // Startup
  323. //
  324. // Routine Description:
  325. //
  326. // Startup the resource DLL. This routine verifies that at least one
  327. // currently supported version of the resource DLL is between
  328. // MinVersionSupported and MaxVersionSupported. If not, then the resource
  329. // DLL should return ERROR_REVISION_MISMATCH.
  330. //
  331. // If more than one version of the resource DLL interface is supported by
  332. // the resource DLL, then the highest version (up to MaxVersionSupported)
  333. // should be returned as the resource DLL's interface. If the returned
  334. // version is not within range, then startup fails.
  335. //
  336. // The ResourceType is passed in so that if the resource DLL supports more
  337. // than one ResourceType, it can pass back the correct function table
  338. // associated with the ResourceType.
  339. //
  340. // Arguments:
  341. //
  342. // ResourceType - The type of resource requesting a function table.
  343. //
  344. // MinVersionSupported - The minimum resource DLL interface version
  345. // supported by the cluster software.
  346. //
  347. // MaxVersionSupported - The maximum resource DLL interface version
  348. // supported by the cluster software.
  349. //
  350. // SetResourceStatus - Pointer to a routine that the resource DLL should
  351. // call to update the state of a resource after the Online or Offline
  352. // routine returns a status of ERROR_IO_PENDING.
  353. //
  354. // LogEvent - Pointer to a routine that handles the reporting of events
  355. // from the resource DLL.
  356. //
  357. // FunctionTable - Returns a pointer to the function table defined for the
  358. // version of the resource DLL interface returned by the resource DLL.
  359. //
  360. // Return Value:
  361. //
  362. // ERROR_SUCCESS - The operation was successful.
  363. //
  364. // ERROR_MOD_NOT_FOUND - The resource type is unknown by this DLL.
  365. //
  366. // ERROR_REVISION_MISMATCH - The version of the cluster service doesn't
  367. // match the versrion of the DLL.
  368. //
  369. // Win32 error code - The operation failed.
  370. //
  371. //--
  372. /////////////////////////////////////////////////////////////////////////////
  373. DWORD WINAPI Startup(
  374. IN LPCWSTR ResourceType,
  375. IN DWORD MinVersionSupported,
  376. IN DWORD MaxVersionSupported,
  377. IN PSET_RESOURCE_STATUS_ROUTINE SetResourceStatus,
  378. IN PLOG_EVENT_ROUTINE LogEvent,
  379. OUT PCLRES_FUNCTION_TABLE * FunctionTable
  380. )
  381. {
  382. if ( ( MinVersionSupported > CLRES_VERSION_V1_00 ) ||
  383. ( MaxVersionSupported < CLRES_VERSION_V1_00 ) )
  384. {
  385. return ERROR_REVISION_MISMATCH;
  386. }
  387. if ( lstrcmpiW( ResourceType, DUMMY_RESNAME ) != 0 )
  388. {
  389. return ERROR_MOD_NOT_FOUND;
  390. }
  391. if ( g_LogEvent == NULL )
  392. {
  393. g_LogEvent = LogEvent;
  394. g_SetResourceStatus = SetResourceStatus;
  395. }
  396. if ( FunctionTable != NULL )
  397. {
  398. *FunctionTable = &g_DummyFunctionTable;
  399. }
  400. return ERROR_SUCCESS;
  401. } //*** Startup()
  402. /////////////////////////////////////////////////////////////////////////////
  403. //++
  404. //
  405. // DummyOpen
  406. //
  407. // Routine Description:
  408. //
  409. // Open routine for Dummy resources.
  410. //
  411. // Open the specified resource (create an instance of the resource).
  412. // Allocate all structures necessary to bring the specified resource
  413. // online.
  414. //
  415. // Arguments:
  416. //
  417. // ResourceName - Supplies the name of the resource to open.
  418. //
  419. // ResourceKey - Supplies handle to the resource's cluster configuration
  420. // database key.
  421. //
  422. // ResourceHandle - A handle that is passed back to the resource monitor
  423. // when the SetResourceStatus or LogEvent method is called. See the
  424. // description of the SetResourceStatus and LogEvent methods on the
  425. // DummyStatup routine. This handle should never be closed or used
  426. // for any purpose other than passing it as an argument back to the
  427. // Resource Monitor in the SetResourceStatus or LogEvent callback.
  428. //
  429. // Return Value:
  430. //
  431. // RESID of created resource.
  432. //
  433. // NULL on failure.
  434. //
  435. //--
  436. /////////////////////////////////////////////////////////////////////////////
  437. RESID WINAPI DummyOpen(
  438. IN LPCWSTR ResourceName,
  439. IN HKEY ResourceKey,
  440. IN RESOURCE_HANDLE ResourceHandle
  441. )
  442. {
  443. DWORD status;
  444. RESID resid = 0;
  445. HKEY parametersKey = NULL;
  446. PDUMMY_RESOURCE ResourceEntry = NULL;
  447. LPWSTR nameOfPropInError;
  448. //
  449. // Open the Parameters registry key for this resource.
  450. //
  451. status = ClusterRegOpenKey( ResourceKey, L"Parameters", KEY_ALL_ACCESS, &parametersKey );
  452. if ( status != ERROR_SUCCESS )
  453. {
  454. (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to open Parameters key. Error: %1!u!.\n", status );
  455. goto exit;
  456. }
  457. //
  458. // Allocate a resource entry.
  459. //
  460. ResourceEntry = (PDUMMY_RESOURCE)LocalAlloc( LMEM_ZEROINIT, sizeof( DUMMY_RESOURCE ) );
  461. if ( ResourceEntry == NULL )
  462. {
  463. status = GetLastError();
  464. (g_LogEvent)(
  465. ResourceHandle,
  466. LOG_ERROR,
  467. L"Unable to allocate resource entry structure. Error: %1!u!.\n",
  468. status
  469. );
  470. goto exit;
  471. }
  472. //
  473. // Initialize the resource entry..
  474. //
  475. ZeroMemory( ResourceEntry, sizeof( DUMMY_RESOURCE ) );
  476. ResourceEntry->ResId = (RESID)ResourceEntry; // for validation
  477. ResourceEntry->ResourceHandle = ResourceHandle;
  478. ResourceEntry->ParametersKey = parametersKey;
  479. ResourceEntry->State = ClusterResourceOffline;
  480. InitializeCriticalSection( &( ResourceEntry->Lock ) );
  481. //
  482. // Save the name of the resource.
  483. //
  484. ResourceEntry->ResourceName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT, (lstrlenW( ResourceName ) + 1 ) * sizeof( WCHAR ) );
  485. if ( ResourceEntry->ResourceName == NULL )
  486. {
  487. goto exit;
  488. }
  489. lstrcpyW( ResourceEntry->ResourceName, ResourceName );
  490. //
  491. // Startup for the resource.
  492. //
  493. // TODO: Add your resource startup code here.
  494. //
  495. // Read parameters.
  496. //
  497. status = ResUtilGetPropertiesToParameterBlock(
  498. ResourceEntry->ParametersKey,
  499. DummyResourcePrivateProperties,
  500. (LPBYTE)&ResourceEntry->Params,
  501. FALSE, // CheckForRequiredProperties
  502. &nameOfPropInError
  503. );
  504. if ( status == ERROR_SUCCESS )
  505. {
  506. if ( ResourceEntry->Params.OpensFail )
  507. {
  508. goto exit;
  509. }
  510. else
  511. {
  512. resid = (RESID)ResourceEntry;
  513. }
  514. }
  515. else
  516. {
  517. goto exit;
  518. }
  519. //
  520. // Create a TimerThreadWakeup event
  521. //
  522. ResourceEntry->TimerThreadWakeup = CreateEvent( NULL, FALSE, FALSE, NULL );
  523. if ( ResourceEntry->TimerThreadWakeup == NULL )
  524. {
  525. (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to create timer thread wakeup event\n" );
  526. resid = 0;
  527. goto exit;
  528. }
  529. if ( ResourceEntry->Params.Pending )
  530. {
  531. ResourceEntry->Flags |= DUMMY_FLAG_PENDING;
  532. }
  533. if ( ResourceEntry->Params.Asynchronous )
  534. {
  535. EnterAsyncMode( ResourceEntry );
  536. ResourceEntry->SignalEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  537. if ( ResourceEntry->SignalEvent == NULL )
  538. {
  539. (g_LogEvent)( ResourceHandle, LOG_ERROR, L"Failed to create a timer event\n");
  540. resid = 0;
  541. goto exit;
  542. }
  543. }
  544. exit:
  545. if ( resid == 0 )
  546. {
  547. if ( parametersKey != NULL )
  548. {
  549. ClusterRegCloseKey( parametersKey );
  550. }
  551. if ( ResourceEntry != NULL )
  552. {
  553. LocalFree( ResourceEntry->ResourceName );
  554. LocalFree( ResourceEntry );
  555. }
  556. }
  557. if ( status != ERROR_SUCCESS )
  558. {
  559. SetLastError( status );
  560. }
  561. return resid;
  562. } //*** DummyOpen()
  563. /////////////////////////////////////////////////////////////////////////////
  564. //++
  565. //
  566. // DummyClose
  567. //
  568. // Routine Description:
  569. //
  570. // Close routine for Dummy resources.
  571. //
  572. // Close the specified resource and deallocate all structures, etc.,
  573. // allocated in the Open call. If the resource is not in the offline state,
  574. // then the resource should be taken offline (by calling Terminate) before
  575. // the close operation is performed.
  576. //
  577. // Arguments:
  578. //
  579. // ResourceId - Supplies the RESID of the resource to close.
  580. //
  581. // Return Value:
  582. //
  583. // None.
  584. //--
  585. /////////////////////////////////////////////////////////////////////////////
  586. void WINAPI DummyClose(
  587. IN RESID ResourceId
  588. )
  589. {
  590. PDUMMY_RESOURCE ResourceEntry;
  591. ResourceEntry = (PDUMMY_RESOURCE)ResourceId;
  592. if ( ResourceEntry == NULL )
  593. {
  594. DBG_PRINT( "Dummy: Close request for a nonexistent resource id 0x%p\n", ResourceId );
  595. return;
  596. }
  597. if ( ResourceEntry->ResId != ResourceId )
  598. {
  599. (g_LogEvent)(
  600. ResourceEntry->ResourceHandle,
  601. LOG_ERROR,
  602. L"Close resource sanity check failed! ResourceId = %1!u!.\n",
  603. ResourceId
  604. );
  605. return;
  606. }
  607. #ifdef LOG_VERBOSE
  608. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Close request.\n" );
  609. #endif
  610. DeleteCriticalSection( &( ResourceEntry->Lock ) );
  611. if ( ResourceEntry->TimerThreadWakeup != NULL )
  612. {
  613. CloseHandle( ResourceEntry->TimerThreadWakeup );
  614. }
  615. if ( ResourceEntry->SignalEvent != NULL )
  616. {
  617. CloseHandle( ResourceEntry->SignalEvent );
  618. }
  619. //
  620. // Close the Parameters key.
  621. //
  622. if ( ResourceEntry->ParametersKey )
  623. {
  624. ClusterRegCloseKey( ResourceEntry->ParametersKey );
  625. }
  626. //
  627. // Deallocate the resource entry.
  628. //
  629. // ADDPARAM: Add new parameters here.
  630. LocalFree( ResourceEntry->ResourceName );
  631. LocalFree( ResourceEntry );
  632. return;
  633. } //*** DummyClose()
  634. /////////////////////////////////////////////////////////////////////////////
  635. //++
  636. //
  637. // DummyOnline
  638. //
  639. // Routine Description:
  640. //
  641. // Online routine for Dummy resources.
  642. //
  643. // Bring the specified resource online (available for use). The resource
  644. // DLL should attempt to arbitrate for the resource if it is present on a
  645. // shared medium, like a shared SCSI bus.
  646. //
  647. // Arguments:
  648. //
  649. // ResourceId - Supplies the resource id for the resource to be brought
  650. // online (available for use).
  651. //
  652. // EventHandle - Returns a signalable handle that is signaled when the
  653. // resource DLL detects a failure on the resource. This argument is
  654. // NULL on input, and the resource DLL returns NULL if asynchronous
  655. // notification of failures is not supported, otherwise this must be
  656. // the address of a handle that is signaled on resource failures.
  657. //
  658. // Return Value:
  659. //
  660. // ERROR_SUCCESS - The operation was successful, and the resource is now
  661. // online.
  662. //
  663. // ERROR_RESOURCE_NOT_FOUND - RESID is not valid.
  664. //
  665. // ERROR_RESOURCE_NOT_AVAILABLE - If the resource was arbitrated with some
  666. // other systems and one of the other systems won the arbitration.
  667. //
  668. // ERROR_IO_PENDING - The request is pending, a thread has been activated
  669. // to process the online request. The thread that is processing the
  670. // online request will periodically report status by calling the
  671. // SetResourceStatus callback method, until the resource is placed into
  672. // the ClusterResourceOnline state (or the resource monitor decides to
  673. // timeout the online request and Terminate the resource. This pending
  674. // timeout value is settable and has a default value of 3 minutes.).
  675. //
  676. // Win32 error code - The operation failed.
  677. //
  678. //--
  679. /////////////////////////////////////////////////////////////////////////////
  680. DWORD WINAPI DummyOnline(
  681. IN RESID ResourceId,
  682. IN OUT PHANDLE //EventHandle
  683. )
  684. {
  685. PDUMMY_RESOURCE ResourceEntry = NULL;
  686. DWORD status;
  687. ResourceEntry = (PDUMMY_RESOURCE)ResourceId;
  688. if ( ResourceEntry == NULL )
  689. {
  690. DBG_PRINT( "Dummy: Online request for a nonexistent resource id 0x%p.\n", ResourceId );
  691. return ERROR_RESOURCE_NOT_FOUND;
  692. }
  693. if ( ResourceEntry->ResId != ResourceId )
  694. {
  695. (g_LogEvent)(
  696. ResourceEntry->ResourceHandle,
  697. LOG_ERROR,
  698. L"Online service sanity check failed! ResourceId = %1!u!.\n",
  699. ResourceId
  700. );
  701. return ERROR_RESOURCE_NOT_FOUND;
  702. }
  703. DummyAcquireResourceLock( ResourceEntry );
  704. #ifdef LOG_VERBOSE
  705. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Online request.\n" );
  706. #endif
  707. ResourceEntry->State = ClusterResourceOffline;
  708. ClusWorkerTerminate( &ResourceEntry->OnlineThread );
  709. ClusWorkerTerminate( &ResourceEntry->OfflineThread );
  710. status = ClusWorkerCreate( &ResourceEntry->OnlineThread, (PWORKER_START_ROUTINE)DummyOnlineThread, ResourceEntry );
  711. if ( status != ERROR_SUCCESS )
  712. {
  713. ResourceEntry->State = ClusterResourceFailed;
  714. (g_LogEvent)(
  715. ResourceEntry->ResourceHandle,
  716. LOG_ERROR,
  717. L"Online: Unable to start thread, status %1!u!.\n",
  718. status
  719. );
  720. }
  721. else
  722. {
  723. status = ERROR_IO_PENDING;
  724. }
  725. DummyReleaseResourceLock( ResourceEntry );
  726. return status;
  727. } //*** DummyOnline()
  728. /////////////////////////////////////////////////////////////////////////////
  729. //++
  730. //
  731. // DummyOnlineThread
  732. //
  733. // Routine Description:
  734. //
  735. // Worker function which brings a resource from the resource table online.
  736. // This function is executed in a separate thread.
  737. //
  738. // Arguments:
  739. //
  740. // WorkerPtr - Supplies the worker structure
  741. //
  742. // ResourceEntry - A pointer to the DUMMY_RESOURCE block for this resource.
  743. //
  744. // Return Value:
  745. //
  746. // ERROR_SUCCESS - The operation completed successfully.
  747. //
  748. // Win32 error code - The operation failed.
  749. //
  750. //--
  751. /////////////////////////////////////////////////////////////////////////////
  752. DWORD WINAPI DummyOnlineThread(
  753. IN PCLUS_WORKER WorkerPtr,
  754. IN PDUMMY_RESOURCE ResourceEntry
  755. )
  756. {
  757. RESOURCE_STATUS resourceStatus;
  758. DWORD status = ERROR_SUCCESS;
  759. LPWSTR nameOfPropInError;
  760. DummyAcquireResourceLock( ResourceEntry );
  761. ResUtilInitializeResourceStatus( &resourceStatus );
  762. resourceStatus.ResourceState = ClusterResourceFailed;
  763. resourceStatus.WaitHint = 0;
  764. resourceStatus.CheckPoint = 1;
  765. //
  766. // Read parameters.
  767. //
  768. status = ResUtilGetPropertiesToParameterBlock(
  769. ResourceEntry->ParametersKey,
  770. DummyResourcePrivateProperties,
  771. (LPBYTE)&ResourceEntry->Params,
  772. TRUE, // CheckForRequiredProperties
  773. &nameOfPropInError
  774. );
  775. if ( status != ERROR_SUCCESS )
  776. {
  777. (g_LogEvent)(
  778. ResourceEntry->ResourceHandle,
  779. LOG_ERROR,
  780. L"Unable to read the '%1' property. Error: %2!u!.\n",
  781. (nameOfPropInError == NULL ? L"" : nameOfPropInError),
  782. status
  783. );
  784. goto exit;
  785. }
  786. //
  787. // Bring the resource online.
  788. //
  789. if ( ResourceEntry->Params.Pending )
  790. {
  791. ResourceEntry->Flags |= DUMMY_FLAG_PENDING;
  792. ResourceEntry->TimerType = TimerOnlinePending;
  793. status = DummyTimerThread( ResourceEntry, WorkerPtr );
  794. }
  795. exit:
  796. if ( status != ERROR_SUCCESS )
  797. {
  798. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Error %1!u! bringing resource online.\n", status );
  799. }
  800. else
  801. {
  802. resourceStatus.ResourceState = ClusterResourceOnline;
  803. }
  804. // _ASSERTE(g_SetResourceStatus != NULL);
  805. g_SetResourceStatus( ResourceEntry->ResourceHandle, &resourceStatus );
  806. ResourceEntry->State = resourceStatus.ResourceState;
  807. DummyReleaseResourceLock( ResourceEntry );
  808. return status;
  809. } //*** DummyOnlineThread()
  810. /////////////////////////////////////////////////////////////////////////////
  811. //++
  812. //
  813. // DummyOffline
  814. //
  815. // Routine Description:
  816. //
  817. // Offline routine for Dummy resources.
  818. //
  819. // Take the specified resource offline gracefully (unavailable for use).
  820. // Wait for any cleanup operations to complete before returning.
  821. //
  822. // Arguments:
  823. //
  824. // ResourceId - Supplies the resource id for the resource to be shutdown
  825. // gracefully.
  826. //
  827. // Return Value:
  828. //
  829. // ERROR_SUCCESS - The request completed successfully and the resource is
  830. // offline.
  831. //
  832. // ERROR_RESOURCE_NOT_FOUND - RESID is not valid.
  833. //
  834. // ERROR_IO_PENDING - The request is still pending, a thread has been
  835. // activated to process the offline request. The thread that is
  836. // processing the offline will periodically report status by calling
  837. // the SetResourceStatus callback method, until the resource is placed
  838. // into the ClusterResourceOffline state (or the resource monitor decides
  839. // to timeout the offline request and Terminate the resource).
  840. //
  841. // Win32 error code - Will cause the resource monitor to log an event and
  842. // call the Terminate routine.
  843. //
  844. //--
  845. /////////////////////////////////////////////////////////////////////////////
  846. DWORD WINAPI DummyOffline(
  847. IN RESID ResourceId
  848. )
  849. {
  850. PDUMMY_RESOURCE ResourceEntry;
  851. DWORD status = ERROR_SUCCESS;
  852. ResourceEntry = (PDUMMY_RESOURCE)ResourceId;
  853. if ( ResourceEntry == NULL )
  854. {
  855. DBG_PRINT( "Dummy: Offline request for a nonexistent resource id 0x%p\n", ResourceId );
  856. return ERROR_RESOURCE_NOT_FOUND;
  857. }
  858. if ( ResourceEntry->ResId != ResourceId )
  859. {
  860. (g_LogEvent)(
  861. ResourceEntry->ResourceHandle,
  862. LOG_ERROR,
  863. L"Offline resource sanity check failed! ResourceId = %1!u!.\n",
  864. ResourceId
  865. );
  866. return ERROR_RESOURCE_NOT_FOUND;
  867. }
  868. DummyAcquireResourceLock( ResourceEntry );
  869. #ifdef LOG_VERBOSE
  870. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Offline request.\n" );
  871. #endif
  872. // TODO: Offline code
  873. // NOTE: Offline should try to shut the resource down gracefully, whereas
  874. // Terminate must shut the resource down immediately. If there are no
  875. // differences between a graceful shut down and an immediate shut down,
  876. // Terminate can be called for Offline, as it is below. However, if there
  877. // are differences, replace the call to Terminate below with your graceful
  878. // shutdown code.
  879. ResourceEntry->State = ClusterResourceOnline;
  880. ClusWorkerTerminate( &ResourceEntry->OfflineThread );
  881. ClusWorkerTerminate( &ResourceEntry->OnlineThread );
  882. status = ClusWorkerCreate( &ResourceEntry->OfflineThread, (PWORKER_START_ROUTINE)DummyOfflineThread, ResourceEntry );
  883. if ( status != ERROR_SUCCESS )
  884. {
  885. ResourceEntry->State = ClusterResourceFailed;
  886. (g_LogEvent)(
  887. ResourceEntry->ResourceHandle,
  888. LOG_ERROR,
  889. L"Online: Unable to start thread, status %1!u!.\n",
  890. status
  891. );
  892. }
  893. else
  894. {
  895. status = ERROR_IO_PENDING;
  896. }
  897. DummyReleaseResourceLock( ResourceEntry );
  898. return status;
  899. } //*** DummyOffline()
  900. /////////////////////////////////////////////////////////////////////////////
  901. //++
  902. //
  903. // DummyOfflineThread
  904. //
  905. // Routine Description:
  906. //
  907. // Worker function which brings a resource from the resource table online.
  908. // This function is executed in a separate thread.
  909. //
  910. // Arguments:
  911. //
  912. // WorkerPtr - Supplies the worker structure
  913. //
  914. // ResourceEntry - A pointer to the DUMMY_RESOURCE block for this resource.
  915. //
  916. // Return Value:
  917. //
  918. // ERROR_SUCCESS - The operation completed successfully.
  919. //
  920. // Win32 error code - The operation failed.
  921. //
  922. //--
  923. /////////////////////////////////////////////////////////////////////////////
  924. DWORD WINAPI DummyOfflineThread(
  925. IN PCLUS_WORKER WorkerPtr,
  926. IN PDUMMY_RESOURCE ResourceEntry
  927. )
  928. {
  929. RESOURCE_STATUS resourceStatus;
  930. DWORD status = ERROR_SUCCESS;
  931. LPWSTR nameOfPropInError;
  932. DummyAcquireResourceLock( ResourceEntry );
  933. ResUtilInitializeResourceStatus( &resourceStatus );
  934. resourceStatus.ResourceState = ClusterResourceFailed;
  935. resourceStatus.WaitHint = 0;
  936. resourceStatus.CheckPoint = 1;
  937. //
  938. // Read parameters.
  939. //
  940. status = ResUtilGetPropertiesToParameterBlock(
  941. ResourceEntry->ParametersKey,
  942. DummyResourcePrivateProperties,
  943. (LPBYTE)&ResourceEntry->Params,
  944. FALSE, // CheckForRequiredProperties
  945. &nameOfPropInError
  946. );
  947. if ( status != ERROR_SUCCESS )
  948. {
  949. (g_LogEvent)(
  950. ResourceEntry->ResourceHandle,
  951. LOG_ERROR,
  952. L"Unable to read the '%1' property. Error: %2!u!.\n",
  953. (nameOfPropInError == NULL ? L"" : nameOfPropInError),
  954. status
  955. );
  956. goto exit;
  957. }
  958. //
  959. // Bring the resource online.
  960. //
  961. if ( ResourceEntry->Params.Pending )
  962. {
  963. ResourceEntry->Flags |= DUMMY_FLAG_PENDING;
  964. ResourceEntry->TimerType = TimerOfflinePending;
  965. status = DummyTimerThread( ResourceEntry, WorkerPtr );
  966. }
  967. exit:
  968. if ( status != ERROR_SUCCESS )
  969. {
  970. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Error %1!u! bringing resource online.\n", status );
  971. }
  972. else
  973. {
  974. resourceStatus.ResourceState = ClusterResourceOffline;
  975. }
  976. // _ASSERTE(g_SetResourceStatus != NULL);
  977. g_SetResourceStatus( ResourceEntry->ResourceHandle, &resourceStatus );
  978. ResourceEntry->State = resourceStatus.ResourceState;
  979. DummyReleaseResourceLock( ResourceEntry );
  980. return status;
  981. } //*** DummyOfflineThread()
  982. /////////////////////////////////////////////////////////////////////////////
  983. //++
  984. //
  985. // DummyTerminate
  986. //
  987. // Routine Description:
  988. //
  989. // Terminate routine for Dummy resources.
  990. //
  991. // Take the specified resource offline immediately (the resource is
  992. // unavailable for use).
  993. //
  994. // Arguments:
  995. //
  996. // ResourceId - Supplies the resource id for the resource to be brought
  997. // offline.
  998. //
  999. // Return Value:
  1000. //
  1001. // None.
  1002. //
  1003. //--
  1004. /////////////////////////////////////////////////////////////////////////////
  1005. void WINAPI DummyTerminate(
  1006. IN RESID ResourceId
  1007. )
  1008. {
  1009. PDUMMY_RESOURCE ResourceEntry;
  1010. ResourceEntry = (PDUMMY_RESOURCE)ResourceId;
  1011. if ( ResourceEntry == NULL )
  1012. {
  1013. DBG_PRINT( "Dummy: Terminate request for a nonexistent resource id 0x%p\n", ResourceId );
  1014. return;
  1015. }
  1016. if ( ResourceEntry->ResId != ResourceId )
  1017. {
  1018. (g_LogEvent)(
  1019. ResourceEntry->ResourceHandle,
  1020. LOG_ERROR,
  1021. L"Terminate resource sanity check failed! ResourceId = %1!u!.\n",
  1022. ResourceId
  1023. );
  1024. return;
  1025. }
  1026. DummyAcquireResourceLock( ResourceEntry );
  1027. #ifdef LOG_VERBOSE
  1028. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Terminate request.\n" );
  1029. #endif
  1030. //
  1031. // Terminate the resource.
  1032. //
  1033. DummyDoTerminate( ResourceEntry );
  1034. ResourceEntry->State = ClusterResourceOffline;
  1035. DummyReleaseResourceLock( ResourceEntry );
  1036. return;
  1037. } //*** DummyTerminate()
  1038. /////////////////////////////////////////////////////////////////////////////
  1039. //++
  1040. //
  1041. // DummyDoTerminate
  1042. //
  1043. // Routine Description:
  1044. //
  1045. // Do the actual Terminate work for Dummy resources.
  1046. //
  1047. // Arguments:
  1048. //
  1049. // ResourceEntry - Supplies resource entry for resource to be terminated
  1050. //
  1051. // Return Value:
  1052. //
  1053. // ERROR_SUCCESS - The request completed successfully and the resource is
  1054. // offline.
  1055. //
  1056. // Win32 error code - Will cause the resource monitor to log an event and
  1057. // call the Terminate routine.
  1058. //
  1059. //--
  1060. /////////////////////////////////////////////////////////////////////////////
  1061. DWORD DummyDoTerminate(
  1062. IN PDUMMY_RESOURCE ResourceEntry
  1063. )
  1064. {
  1065. DWORD status = ERROR_SUCCESS;
  1066. if ( ResourceEntry->TimerType != TimerNotUsed )
  1067. {
  1068. SetEvent( ResourceEntry->TimerThreadWakeup );
  1069. }
  1070. //
  1071. // Kill off any pending threads.
  1072. //
  1073. ClusWorkerTerminate( &ResourceEntry->OnlineThread );
  1074. ClusWorkerTerminate( &ResourceEntry->OfflineThread );
  1075. //
  1076. // Terminate the resource.
  1077. //
  1078. // TODO: Add code to terminate your resource.
  1079. if ( status == ERROR_SUCCESS )
  1080. {
  1081. ResourceEntry->State = ClusterResourceOffline;
  1082. }
  1083. return status;
  1084. } //*** DummyDoTerminate()
  1085. /////////////////////////////////////////////////////////////////////////////
  1086. //++
  1087. //
  1088. // DummyLooksAlive
  1089. //
  1090. // Routine Description:
  1091. //
  1092. // LooksAlive routine for Dummy resources.
  1093. //
  1094. // Perform a quick check to determine if the specified resource is probably
  1095. // online (available for use). This call should not block for more than
  1096. // 300 ms, preferably less than 50 ms.
  1097. //
  1098. // Arguments:
  1099. //
  1100. // ResourceId - Supplies the resource id for the resource to polled.
  1101. //
  1102. // Return Value:
  1103. //
  1104. // TRUE - The specified resource is probably online and available for use.
  1105. //
  1106. // FALSE - The specified resource is not functioning normally.
  1107. //
  1108. //--
  1109. /////////////////////////////////////////////////////////////////////////////
  1110. BOOL WINAPI DummyLooksAlive(
  1111. IN RESID ResourceId
  1112. )
  1113. {
  1114. PDUMMY_RESOURCE ResourceEntry;
  1115. ResourceEntry = (PDUMMY_RESOURCE)ResourceId;
  1116. if ( ResourceEntry == NULL )
  1117. {
  1118. DBG_PRINT("Dummy: LooksAlive request for a nonexistent resource id 0x%p\n", ResourceId );
  1119. return FALSE;
  1120. }
  1121. if ( ResourceEntry->ResId != ResourceId )
  1122. {
  1123. (g_LogEvent)(
  1124. ResourceEntry->ResourceHandle,
  1125. LOG_ERROR,
  1126. L"LooksAlive sanity check failed! ResourceId = %1!u!.\n",
  1127. ResourceId
  1128. );
  1129. return FALSE;
  1130. }
  1131. #ifdef LOG_VERBOSE
  1132. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"LooksAlive request.\n" );
  1133. #endif
  1134. // TODO: LooksAlive code
  1135. // NOTE: LooksAlive should be a quick check to see if the resource is
  1136. // available or not, whereas IsAlive should be a thorough check. If
  1137. // there are no differences between a quick check and a thorough check,
  1138. // IsAlive can be called for LooksAlive, as it is below. However, if there
  1139. // are differences, replace the call to IsAlive below with your quick
  1140. // check code.
  1141. //
  1142. // Check to see if the resource is alive.
  1143. //
  1144. return DummyCheckIsAlive( ResourceEntry );
  1145. } //*** DummyLooksAlive()
  1146. /////////////////////////////////////////////////////////////////////////////
  1147. //++
  1148. //
  1149. // DummyIsAlive
  1150. //
  1151. // Routine Description:
  1152. //
  1153. // IsAlive routine for Dummy resources.
  1154. //
  1155. // Perform a thorough check to determine if the specified resource is online
  1156. // (available for use). This call should not block for more than 400 ms,
  1157. // preferably less than 100 ms.
  1158. //
  1159. // Arguments:
  1160. //
  1161. // ResourceId - Supplies the resource id for the resource to polled.
  1162. //
  1163. // Return Value:
  1164. //
  1165. // TRUE - The specified resource is online and functioning normally.
  1166. //
  1167. // FALSE - The specified resource is not functioning normally.
  1168. //
  1169. //--
  1170. /////////////////////////////////////////////////////////////////////////////
  1171. BOOL WINAPI DummyIsAlive(
  1172. IN RESID ResourceId
  1173. )
  1174. {
  1175. PDUMMY_RESOURCE ResourceEntry;
  1176. ResourceEntry = (PDUMMY_RESOURCE)ResourceId;
  1177. if ( ResourceEntry == NULL )
  1178. {
  1179. DBG_PRINT("Dummy: IsAlive request for a nonexistent resource id 0x%p\n", ResourceId );
  1180. return FALSE;
  1181. }
  1182. if ( ResourceEntry->ResId != ResourceId )
  1183. {
  1184. (g_LogEvent)(
  1185. ResourceEntry->ResourceHandle,
  1186. LOG_ERROR,
  1187. L"IsAlive sanity check failed! ResourceId = %1!u!.\n",
  1188. ResourceId
  1189. );
  1190. return FALSE;
  1191. }
  1192. #ifdef LOG_VERBOSE
  1193. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"IsAlive request.\n" );
  1194. #endif
  1195. //
  1196. // Check to see if the resource is alive.
  1197. //
  1198. return DummyCheckIsAlive( ResourceEntry );
  1199. } //*** DummyIsAlive()
  1200. /////////////////////////////////////////////////////////////////////////////
  1201. //++
  1202. //
  1203. // DummyCheckIsAlive
  1204. //
  1205. // Routine Description:
  1206. //
  1207. // Check to see if the resource is alive for Dummy resources.
  1208. //
  1209. // Arguments:
  1210. //
  1211. // ResourceEntry - Supplies the resource entry for the resource to polled.
  1212. //
  1213. // Return Value:
  1214. //
  1215. // TRUE - The specified resource is online and functioning normally.
  1216. //
  1217. // FALSE - The specified resource is not functioning normally.
  1218. //
  1219. //--
  1220. /////////////////////////////////////////////////////////////////////////////
  1221. BOOL DummyCheckIsAlive(
  1222. IN PDUMMY_RESOURCE ResourceEntry
  1223. )
  1224. {
  1225. DummyAcquireResourceLock( ResourceEntry );
  1226. //
  1227. // Check to see if the resource is alive.
  1228. //
  1229. // TODO: Add code to determine if your resource is alive.
  1230. DummyReleaseResourceLock( ResourceEntry );
  1231. return TRUE;
  1232. } //*** DummyCheckIsAlive()
  1233. /////////////////////////////////////////////////////////////////////////////
  1234. //++
  1235. //
  1236. // DummyResourceControl
  1237. //
  1238. // Routine Description:
  1239. //
  1240. // ResourceControl routine for Dummy resources.
  1241. //
  1242. // Perform the control request specified by ControlCode on the specified
  1243. // resource.
  1244. //
  1245. // Arguments:
  1246. //
  1247. // ResourceId - Supplies the resource id for the specific resource.
  1248. //
  1249. // ControlCode - Supplies the control code that defines the action
  1250. // to be performed.
  1251. //
  1252. // InBuffer - Supplies a pointer to a buffer containing input data.
  1253. //
  1254. // InBufferSize - Supplies the size, in bytes, of the data pointed
  1255. // to by InBuffer.
  1256. //
  1257. // OutBuffer - Supplies a pointer to the output buffer to be filled in.
  1258. //
  1259. // OutBufferSize - Supplies the size, in bytes, of the available space
  1260. // pointed to by OutBuffer.
  1261. //
  1262. // BytesReturned - Returns the number of bytes of OutBuffer actually
  1263. // filled in by the resource. If OutBuffer is too small, BytesReturned
  1264. // contains the total number of bytes for the operation to succeed.
  1265. //
  1266. // Return Value:
  1267. //
  1268. // ERROR_SUCCESS - The function completed successfully.
  1269. //
  1270. // ERROR_RESOURCE_NOT_FOUND - RESID is not valid.
  1271. //
  1272. // ERROR_INVALID_FUNCTION - The requested control code is not supported.
  1273. // In some cases, this allows the cluster software to perform the work.
  1274. //
  1275. // Win32 error code - The function failed.
  1276. //
  1277. //--
  1278. /////////////////////////////////////////////////////////////////////////////
  1279. DWORD WINAPI DummyResourceControl(
  1280. IN RESID ResourceId,
  1281. IN DWORD ControlCode,
  1282. IN void * InBuffer,
  1283. IN DWORD InBufferSize,
  1284. OUT void * OutBuffer,
  1285. IN DWORD OutBufferSize,
  1286. OUT LPDWORD BytesReturned
  1287. )
  1288. {
  1289. DWORD status;
  1290. PDUMMY_RESOURCE ResourceEntry;
  1291. DWORD required;
  1292. ResourceEntry = (PDUMMY_RESOURCE)ResourceId;
  1293. if ( ResourceEntry == NULL )
  1294. {
  1295. DBG_PRINT("Dummy: ResourceControl request for a nonexistent resource id 0x%p\n", ResourceId );
  1296. return ERROR_RESOURCE_NOT_FOUND;
  1297. }
  1298. if ( ResourceEntry->ResId != ResourceId )
  1299. {
  1300. (g_LogEvent)(
  1301. ResourceEntry->ResourceHandle,
  1302. LOG_ERROR,
  1303. L"ResourceControl sanity check failed! ResourceId = %1!u!.\n",
  1304. ResourceId
  1305. );
  1306. return ERROR_RESOURCE_NOT_FOUND;
  1307. }
  1308. DummyAcquireResourceLock( ResourceEntry );
  1309. switch ( ControlCode )
  1310. {
  1311. case CLUSCTL_RESOURCE_UNKNOWN:
  1312. *BytesReturned = 0;
  1313. status = ERROR_SUCCESS;
  1314. break;
  1315. case CLUSCTL_RESOURCE_ENUM_PRIVATE_PROPERTIES:
  1316. status = ResUtilEnumProperties(
  1317. DummyResourcePrivateProperties,
  1318. (LPWSTR)OutBuffer,
  1319. OutBufferSize,
  1320. BytesReturned,
  1321. &required
  1322. );
  1323. if ( status == ERROR_MORE_DATA )
  1324. {
  1325. *BytesReturned = required;
  1326. }
  1327. break;
  1328. case CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES:
  1329. status = DummyGetPrivateResProperties(
  1330. ResourceEntry,
  1331. OutBuffer,
  1332. OutBufferSize,
  1333. BytesReturned
  1334. );
  1335. break;
  1336. case CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES:
  1337. status = DummyValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, NULL );
  1338. break;
  1339. case CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES:
  1340. status = DummySetPrivateResProperties( ResourceEntry, InBuffer, InBufferSize );
  1341. break;
  1342. default:
  1343. status = ERROR_INVALID_FUNCTION;
  1344. break;
  1345. }
  1346. DummyReleaseResourceLock( ResourceEntry );
  1347. return status;
  1348. } //*** DummyResourceControl()
  1349. /////////////////////////////////////////////////////////////////////////////
  1350. //++
  1351. //
  1352. // DummyResourceTypeControl
  1353. //
  1354. // Routine Description:
  1355. //
  1356. // ResourceTypeControl routine for Dummy resources.
  1357. //
  1358. // Perform the control request specified by ControlCode.
  1359. //
  1360. // Arguments:
  1361. //
  1362. // ResourceTypeName - Supplies the name of the resource type.
  1363. //
  1364. // ControlCode - Supplies the control code that defines the action
  1365. // to be performed.
  1366. //
  1367. // InBuffer - Supplies a pointer to a buffer containing input data.
  1368. //
  1369. // InBufferSize - Supplies the size, in bytes, of the data pointed
  1370. // to by InBuffer.
  1371. //
  1372. // OutBuffer - Supplies a pointer to the output buffer to be filled in.
  1373. //
  1374. // OutBufferSize - Supplies the size, in bytes, of the available space
  1375. // pointed to by OutBuffer.
  1376. //
  1377. // BytesReturned - Returns the number of bytes of OutBuffer actually
  1378. // filled in by the resource. If OutBuffer is too small, BytesReturned
  1379. // contains the total number of bytes for the operation to succeed.
  1380. //
  1381. // Return Value:
  1382. //
  1383. // ERROR_SUCCESS - The function completed successfully.
  1384. //
  1385. // ERROR_INVALID_FUNCTION - The requested control code is not supported.
  1386. // In some cases, this allows the cluster software to perform the work.
  1387. //
  1388. // Win32 error code - The function failed.
  1389. //
  1390. //--
  1391. /////////////////////////////////////////////////////////////////////////////
  1392. DWORD WINAPI DummyResourceTypeControl(
  1393. IN LPCWSTR, //ResourceTypeName,
  1394. IN DWORD ControlCode,
  1395. IN void *, //InBuffer,
  1396. IN DWORD, //InBufferSize,
  1397. OUT void * OutBuffer,
  1398. IN DWORD OutBufferSize,
  1399. OUT LPDWORD BytesReturned
  1400. )
  1401. {
  1402. DWORD status;
  1403. DWORD required;
  1404. switch ( ControlCode )
  1405. {
  1406. case CLUSCTL_RESOURCE_TYPE_UNKNOWN:
  1407. *BytesReturned = 0;
  1408. status = ERROR_SUCCESS;
  1409. break;
  1410. case CLUSCTL_RESOURCE_TYPE_ENUM_PRIVATE_PROPERTIES:
  1411. status = ResUtilEnumProperties(
  1412. DummyResourcePrivateProperties,
  1413. (LPWSTR)OutBuffer,
  1414. OutBufferSize,
  1415. BytesReturned,
  1416. &required
  1417. );
  1418. if ( status == ERROR_MORE_DATA )
  1419. {
  1420. *BytesReturned = required;
  1421. }
  1422. break;
  1423. default:
  1424. status = ERROR_INVALID_FUNCTION;
  1425. break;
  1426. }
  1427. return status;
  1428. } //*** DummyResourceTypeControl()
  1429. /////////////////////////////////////////////////////////////////////////////
  1430. //++
  1431. //
  1432. // DummyGetPrivateResProperties
  1433. //
  1434. // Routine Description:
  1435. //
  1436. // Processes the CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES control function
  1437. // for resources of type Dummy.
  1438. //
  1439. // Arguments:
  1440. //
  1441. // ResourceEntry - Supplies the resource entry on which to operate.
  1442. //
  1443. // OutBuffer - Returns the output data.
  1444. //
  1445. // OutBufferSize - Supplies the size, in bytes, of the data pointed
  1446. // to by OutBuffer.
  1447. //
  1448. // BytesReturned - The number of bytes returned in OutBuffer.
  1449. //
  1450. // Return Value:
  1451. //
  1452. // ERROR_SUCCESS - The function completed successfully.
  1453. //
  1454. // ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
  1455. //
  1456. // ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  1457. //
  1458. // Win32 error code - The function failed.
  1459. //
  1460. //--
  1461. /////////////////////////////////////////////////////////////////////////////
  1462. DWORD DummyGetPrivateResProperties(
  1463. IN OUT PDUMMY_RESOURCE ResourceEntry,
  1464. OUT void * OutBuffer,
  1465. IN DWORD OutBufferSize,
  1466. OUT LPDWORD BytesReturned
  1467. )
  1468. {
  1469. DWORD status;
  1470. DWORD required;
  1471. DummyAcquireResourceLock( ResourceEntry );
  1472. status = ResUtilGetAllProperties(
  1473. ResourceEntry->ParametersKey,
  1474. DummyResourcePrivateProperties,
  1475. OutBuffer,
  1476. OutBufferSize,
  1477. BytesReturned,
  1478. &required
  1479. );
  1480. if ( status == ERROR_MORE_DATA )
  1481. {
  1482. *BytesReturned = required;
  1483. }
  1484. DummyReleaseResourceLock( ResourceEntry );
  1485. return status;
  1486. } //*** DummyGetPrivateResProperties()
  1487. /////////////////////////////////////////////////////////////////////////////
  1488. //++
  1489. //
  1490. // DummyValidatePrivateResProperties
  1491. //
  1492. // Routine Description:
  1493. //
  1494. // Processes the CLUSCTL_RESOURCE_VALIDATE_PRIVATE_PROPERTIES control
  1495. // function for resources of type Dummy.
  1496. //
  1497. // Arguments:
  1498. //
  1499. // pResourceEntry - Supplies the resource entry on which to operate.
  1500. //
  1501. // InBuffer - Supplies a pointer to a buffer containing input data.
  1502. //
  1503. // InBufferSize - Supplies the size, in bytes, of the data pointed
  1504. // to by InBuffer.
  1505. //
  1506. // Params - Supplies the parameter block to fill in.
  1507. //
  1508. // Return Value:
  1509. //
  1510. // ERROR_SUCCESS - The function completed successfully.
  1511. //
  1512. // ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
  1513. //
  1514. // ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  1515. //
  1516. // Win32 error code - The function failed.
  1517. //
  1518. //--
  1519. /////////////////////////////////////////////////////////////////////////////
  1520. DWORD DummyValidatePrivateResProperties(
  1521. IN OUT PDUMMY_RESOURCE pResourceEntry,
  1522. IN const PVOID InBuffer,
  1523. IN DWORD InBufferSize,
  1524. OUT PDUMMY_PARAMS Params
  1525. )
  1526. {
  1527. DWORD status = ERROR_SUCCESS;
  1528. DUMMY_PARAMS propsCurrent;
  1529. DUMMY_PARAMS propsNew;
  1530. PDUMMY_PARAMS pParams;
  1531. LPWSTR pszNameOfPropInError;
  1532. DummyAcquireResourceLock( pResourceEntry );
  1533. //
  1534. // Check if there is input data.
  1535. //
  1536. if ( ( InBuffer == NULL ) || ( InBufferSize < sizeof( DWORD ) ) )
  1537. {
  1538. status = ERROR_INVALID_DATA;
  1539. goto exit;
  1540. }
  1541. //
  1542. // Retrieve the current set of private properties from the
  1543. // cluster database.
  1544. //
  1545. ZeroMemory( &propsCurrent, sizeof( propsCurrent ) );
  1546. status = ResUtilGetPropertiesToParameterBlock(
  1547. pResourceEntry->ParametersKey,
  1548. DummyResourcePrivateProperties,
  1549. reinterpret_cast< LPBYTE >( &propsCurrent ),
  1550. FALSE, /*CheckForRequiredProperties*/
  1551. &pszNameOfPropInError
  1552. );
  1553. if ( status != ERROR_SUCCESS )
  1554. {
  1555. (g_LogEvent)(
  1556. pResourceEntry->ResourceHandle,
  1557. LOG_ERROR,
  1558. L"Unable to read the '%1' property. Error: %2!u!.\n",
  1559. (pszNameOfPropInError == NULL ? L"" : pszNameOfPropInError),
  1560. status
  1561. );
  1562. goto exit;
  1563. } // if: error getting properties
  1564. //
  1565. // Duplicate the resource parameter block.
  1566. //
  1567. if ( Params == NULL )
  1568. {
  1569. pParams = &propsNew;
  1570. }
  1571. else
  1572. {
  1573. pParams = Params;
  1574. }
  1575. ZeroMemory( pParams, sizeof(DUMMY_PARAMS) );
  1576. status = ResUtilDupParameterBlock(
  1577. reinterpret_cast< LPBYTE >( pParams ),
  1578. reinterpret_cast< LPBYTE >( &propsCurrent ),
  1579. DummyResourcePrivateProperties
  1580. );
  1581. if ( status != ERROR_SUCCESS )
  1582. {
  1583. goto cleanup;
  1584. }
  1585. //
  1586. // Parse and validate the properties.
  1587. //
  1588. status = ResUtilVerifyPropertyTable(
  1589. DummyResourcePrivateProperties,
  1590. NULL,
  1591. TRUE, // AllowUnknownProperties
  1592. InBuffer,
  1593. InBufferSize,
  1594. (LPBYTE)pParams
  1595. );
  1596. if ( status == ERROR_SUCCESS )
  1597. {
  1598. //
  1599. // Validate the parameter values.
  1600. //
  1601. // TODO: Code to validate interactions between parameters goes here.
  1602. }
  1603. cleanup:
  1604. //
  1605. // Cleanup our parameter block.
  1606. //
  1607. if ( (pParams == &propsNew)
  1608. || ( (status != ERROR_SUCCESS)
  1609. && (pParams != NULL)
  1610. )
  1611. )
  1612. {
  1613. ResUtilFreeParameterBlock(
  1614. reinterpret_cast< LPBYTE >( pParams ),
  1615. reinterpret_cast< LPBYTE >( &propsCurrent ),
  1616. DummyResourcePrivateProperties
  1617. );
  1618. } // if: we duplicated the parameter block
  1619. ResUtilFreeParameterBlock(
  1620. reinterpret_cast< LPBYTE >( &propsCurrent ),
  1621. NULL,
  1622. DummyResourcePrivateProperties
  1623. );
  1624. exit:
  1625. DummyReleaseResourceLock( pResourceEntry );
  1626. return status;
  1627. } //*** DummyValidatePrivateResProperties()
  1628. /////////////////////////////////////////////////////////////////////////////
  1629. //++
  1630. //
  1631. // DummySetPrivateResProperties
  1632. //
  1633. // Routine Description:
  1634. //
  1635. // Processes the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control function
  1636. // for resources of type Dummy.
  1637. //
  1638. // Arguments:
  1639. //
  1640. // ResourceEntry - Supplies the resource entry on which to operate.
  1641. //
  1642. // InBuffer - Supplies a pointer to a buffer containing input data.
  1643. //
  1644. // InBufferSize - Supplies the size, in bytes, of the data pointed
  1645. // to by InBuffer.
  1646. //
  1647. // Return Value:
  1648. //
  1649. // ERROR_SUCCESS - The function completed successfully.
  1650. //
  1651. // ERROR_INVALID_PARAMETER - The data is formatted incorrectly.
  1652. //
  1653. // ERROR_NOT_ENOUGH_MEMORY - An error occurred allocating memory.
  1654. //
  1655. // Win32 error code - The function failed.
  1656. //
  1657. //--
  1658. /////////////////////////////////////////////////////////////////////////////
  1659. DWORD DummySetPrivateResProperties(
  1660. IN OUT PDUMMY_RESOURCE ResourceEntry,
  1661. IN void * InBuffer,
  1662. IN DWORD InBufferSize
  1663. )
  1664. {
  1665. DWORD status = ERROR_SUCCESS;
  1666. DUMMY_PARAMS params;
  1667. DummyAcquireResourceLock( ResourceEntry );
  1668. //
  1669. // Parse the properties so they can be validated together.
  1670. // This routine does individual property validation.
  1671. //
  1672. status = DummyValidatePrivateResProperties( ResourceEntry, InBuffer, InBufferSize, &params );
  1673. if ( status != ERROR_SUCCESS )
  1674. {
  1675. ResUtilFreeParameterBlock( (LPBYTE)&params, (LPBYTE)&ResourceEntry->Params, DummyResourcePrivateProperties );
  1676. goto exit;
  1677. }
  1678. //
  1679. // Save the parameter values.
  1680. //
  1681. status = ResUtilSetPropertyParameterBlock(
  1682. ResourceEntry->ParametersKey,
  1683. DummyResourcePrivateProperties,
  1684. NULL,
  1685. (LPBYTE) &params,
  1686. InBuffer,
  1687. InBufferSize,
  1688. (LPBYTE) &ResourceEntry->Params
  1689. );
  1690. ResUtilFreeParameterBlock( (LPBYTE)&params, (LPBYTE)&ResourceEntry->Params, DummyResourcePrivateProperties );
  1691. //
  1692. // If the resource is online, return a non-success status.
  1693. //
  1694. // TODO: Modify the code below if your resource can handle
  1695. // changes to properties while it is still online.
  1696. if ( status == ERROR_SUCCESS )
  1697. {
  1698. if ( ResourceEntry->State == ClusterResourceOnline )
  1699. {
  1700. status = ERROR_RESOURCE_PROPERTIES_STORED;
  1701. }
  1702. else if ( ResourceEntry->State == ClusterResourceOnlinePending )
  1703. {
  1704. status = ERROR_RESOURCE_PROPERTIES_STORED;
  1705. }
  1706. else
  1707. {
  1708. status = ERROR_SUCCESS;
  1709. }
  1710. }
  1711. exit:
  1712. DummyReleaseResourceLock( ResourceEntry );
  1713. return status;
  1714. } //*** DummySetPrivateResProperties()
  1715. /////////////////////////////////////////////////////////////////////////////
  1716. //++
  1717. //
  1718. // DummyDoPending
  1719. //
  1720. // Routine Description:
  1721. //
  1722. // Does the online and offline pending and waiting processing
  1723. //
  1724. // Arguments:
  1725. //
  1726. // resource - A pointer to the DummyResource block for this resource.
  1727. //
  1728. // nDelay - How long should we wait?
  1729. //
  1730. // WorkerPtr - Supplies the worker structure
  1731. //
  1732. // Return Value:
  1733. //
  1734. // ERROR_SUCCESS if successful.
  1735. //
  1736. // Win32 error code on failure.
  1737. //
  1738. //--
  1739. /////////////////////////////////////////////////////////////////////////////
  1740. DWORD DummyDoPending(
  1741. IN PDUMMY_RESOURCE ResourceEntry,
  1742. IN DWORD nDelay,
  1743. IN PCLUS_WORKER WorkerPtr
  1744. )
  1745. {
  1746. RESOURCE_STATUS resourceStatus;
  1747. DWORD status;
  1748. DWORD nWait = MAX_WAIT;
  1749. RESOURCE_EXIT_STATE exit;
  1750. ResUtilInitializeResourceStatus( &resourceStatus );
  1751. resourceStatus.ResourceState = ( ResourceEntry->TimerType == TimerOnlinePending
  1752. ? ClusterResourceOnlinePending
  1753. : ClusterResourceOfflinePending );
  1754. resourceStatus.WaitHint = 0;
  1755. resourceStatus.CheckPoint = 1;
  1756. (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus );
  1757. if ( nDelay < nWait )
  1758. {
  1759. nWait = nDelay;
  1760. nDelay = 0;
  1761. }
  1762. for ( ; ; )
  1763. {
  1764. status = WaitForSingleObject( ResourceEntry->TimerThreadWakeup, nWait );
  1765. //
  1766. // Check to see if the online operation was aborted while this thread
  1767. // was starting up.
  1768. //
  1769. if ( ClusWorkerCheckTerminate( WorkerPtr ) )
  1770. {
  1771. status = ERROR_OPERATION_ABORTED;
  1772. ResourceEntry->State = ( ResourceEntry->TimerType == TimerOnlinePending )
  1773. ? ClusterResourceOnlinePending
  1774. : ClusterResourceOfflinePending;
  1775. break;
  1776. }
  1777. //
  1778. // Either the terminate routine was called, or we timed out.
  1779. // If we timed out, then indicate that we've completed.
  1780. //
  1781. if ( status == WAIT_TIMEOUT )
  1782. {
  1783. if ( nDelay == 0 )
  1784. {
  1785. status = ERROR_SUCCESS;
  1786. break;
  1787. }
  1788. nDelay -= nWait;
  1789. if ( nDelay < nWait )
  1790. {
  1791. nWait = nDelay;
  1792. nDelay = 0;
  1793. }
  1794. }
  1795. else
  1796. {
  1797. (g_LogEvent)(
  1798. ResourceEntry->ResourceHandle,
  1799. LOG_INFORMATION,
  1800. ( ResourceEntry->TimerType == TimerOnlinePending ) ? L"Online pending terminated\n" : L"Offline pending terminated\n"
  1801. );
  1802. if ( ResourceEntry->State == ClusterResourceOffline )
  1803. {
  1804. ResourceEntry->TimerType = TimerOfflinePending;
  1805. break;
  1806. }
  1807. else if ( ResourceEntry->State == ClusterResourceOnline )
  1808. {
  1809. ResourceEntry->TimerType = TimerOnlinePending;
  1810. break;
  1811. }
  1812. }
  1813. exit = (_RESOURCE_EXIT_STATE)(g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus );
  1814. if ( exit == ResourceExitStateTerminate )
  1815. {
  1816. ResourceEntry->State = ( ResourceEntry->TimerType == TimerOnlinePending )
  1817. ? ClusterResourceOnline
  1818. : ClusterResourceOffline;
  1819. status = ERROR_SUCCESS; //TODO
  1820. if ( ResourceEntry->TimerType == TimerOnlinePending )
  1821. {
  1822. break;
  1823. }
  1824. }
  1825. else
  1826. {
  1827. ResourceEntry->State = ( ResourceEntry->TimerType == TimerOnlinePending )
  1828. ? ClusterResourceOnline
  1829. : ClusterResourceOffline;
  1830. status = ERROR_SUCCESS;
  1831. }
  1832. } // for:
  1833. resourceStatus.ResourceState = ( ResourceEntry->TimerType == TimerOnlinePending ? ClusterResourceOnline : ClusterResourceOffline );
  1834. (g_SetResourceStatus)( ResourceEntry->ResourceHandle, &resourceStatus );
  1835. return status;
  1836. } //*** DummyDoPending()
  1837. /////////////////////////////////////////////////////////////////////////////
  1838. //++
  1839. //
  1840. // DummyTimerThread
  1841. //
  1842. // Routine Description:
  1843. //
  1844. // Starts a timer thread to wait and signal failures
  1845. //
  1846. // Arguments:
  1847. //
  1848. // resource - A pointer to the DummyResource block for this resource.
  1849. //
  1850. // WorkerPtr - Supplies the worker structure
  1851. //
  1852. // Return Value:
  1853. //
  1854. // ERROR_SUCCESS if successful.
  1855. //
  1856. // Win32 error code on failure.
  1857. //
  1858. //--
  1859. /////////////////////////////////////////////////////////////////////////////
  1860. DWORD DummyTimerThread(
  1861. IN PDUMMY_RESOURCE ResourceEntry,
  1862. IN PCLUS_WORKER WorkerPtr
  1863. )
  1864. {
  1865. RESOURCE_STATUS resourceStatus;
  1866. SYSTEMTIME time;
  1867. DWORD delay;
  1868. DWORD status = ERROR_SUCCESS;
  1869. DummyAcquireResourceLock( ResourceEntry );
  1870. (g_LogEvent)( NULL, LOG_INFORMATION, L"TimerThread Entry\n" );
  1871. //
  1872. // If we are not running in async failure mode, or
  1873. // pending mode then exit now.
  1874. //
  1875. if ( !AsyncMode( ResourceEntry ) && !PendingMode( ResourceEntry ) )
  1876. {
  1877. status = ERROR_SUCCESS;
  1878. goto exit;
  1879. }
  1880. //
  1881. // Check to see if the online/offline operation was aborted while this thread
  1882. // was starting up.
  1883. //
  1884. if ( ClusWorkerCheckTerminate( WorkerPtr ) )
  1885. {
  1886. status = ERROR_OPERATION_ABORTED;
  1887. ResourceEntry->State = ClusterResourceOfflinePending;
  1888. goto exit;
  1889. }
  1890. more_pending:
  1891. ResUtilInitializeResourceStatus( &resourceStatus );
  1892. //
  1893. // Otherwise, get system time for random delay.
  1894. //
  1895. if ( ResourceEntry->Params.PendTime == 0 )
  1896. {
  1897. GetSystemTime( &time );
  1898. delay = ( time.wMilliseconds + time.wSecond ) * 6;
  1899. }
  1900. else
  1901. {
  1902. delay = ResourceEntry->Params.PendTime * 1000;
  1903. }
  1904. //
  1905. // Use longer delays for errors
  1906. //
  1907. if ( ResourceEntry->TimerType == TimerErrorPending )
  1908. {
  1909. delay *= 10;
  1910. }
  1911. //
  1912. // This routine is either handling an Offline Pending or an error timeout.
  1913. //
  1914. switch ( ResourceEntry->TimerType )
  1915. {
  1916. case TimerOnlinePending :
  1917. {
  1918. (g_LogEvent)(
  1919. ResourceEntry->ResourceHandle,
  1920. LOG_INFORMATION,
  1921. L"Will complete online in approximately %1!u! seconds\n",
  1922. ( delay + 500 ) / 1000
  1923. );
  1924. status = DummyDoPending( ResourceEntry, delay, WorkerPtr );
  1925. break;
  1926. }
  1927. case TimerOfflinePending :
  1928. {
  1929. (g_LogEvent)(
  1930. ResourceEntry->ResourceHandle,
  1931. LOG_INFORMATION,
  1932. L"Will complete offline in approximately %1!u! seconds\n",
  1933. (delay+500)/1000
  1934. );
  1935. status = DummyDoPending( ResourceEntry, delay, WorkerPtr );
  1936. break;
  1937. }
  1938. case TimerErrorPending :
  1939. {
  1940. (g_LogEvent)(
  1941. ResourceEntry->ResourceHandle,
  1942. LOG_INFORMATION,
  1943. L"Will fail in approximately %1!u! seconds\n",
  1944. ( delay + 500 ) / 1000
  1945. );
  1946. if ( !ResetEvent( ResourceEntry->SignalEvent ) )
  1947. {
  1948. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Failed to reset the signal event\n");
  1949. status = ERROR_GEN_FAILURE;
  1950. goto exit;
  1951. }
  1952. status = WaitForSingleObject( ResourceEntry->TimerThreadWakeup, delay );
  1953. //
  1954. // Either the terminate routine was called, or we timed out.
  1955. // If we timed out, then signal the waiting event.
  1956. //
  1957. if ( status == WAIT_TIMEOUT )
  1958. {
  1959. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Failed randomly\n");
  1960. ResourceEntry->TimerType = TimerNotUsed;
  1961. SetEvent( ResourceEntry->SignalEvent );
  1962. }
  1963. else
  1964. {
  1965. if ( ResourceEntry->State == ClusterResourceOfflinePending )
  1966. {
  1967. ResourceEntry->TimerType = TimerOfflinePending;
  1968. goto more_pending;
  1969. }
  1970. }
  1971. break;
  1972. }
  1973. default:
  1974. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"DummyTimer internal error, timer %1!u!\n", ResourceEntry->TimerType);
  1975. break;
  1976. }
  1977. (g_LogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"TimerThread Exit\n" );
  1978. ResourceEntry->TimerType = TimerNotUsed;
  1979. exit:
  1980. DummyReleaseResourceLock( ResourceEntry );
  1981. return status;
  1982. } // DummyTimerThread
  1983. //***********************************************************
  1984. //
  1985. // Define Function Table
  1986. //
  1987. //***********************************************************
  1988. CLRES_V1_FUNCTION_TABLE(
  1989. g_DummyFunctionTable, // Name
  1990. CLRES_VERSION_V1_00, // Version
  1991. Dummy, // Prefix
  1992. NULL, // Arbitrate
  1993. NULL, // Release
  1994. DummyResourceControl, // ResControl
  1995. DummyResourceTypeControl // ResTypeControl
  1996. );