Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4513 lines
117 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. idletsks.c
  5. Abstract:
  6. This module implements the idle detection / task server.
  7. Author:
  8. Dave Fields (davidfie) 26-July-1998
  9. Cenk Ergan (cenke) 14-June-2000
  10. Revision History:
  11. --*/
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <windows.h>
  16. #include <stdlib.h>
  17. #include <stdio.h>
  18. #include <time.h>
  19. #include "idletsks.h"
  20. //
  21. // Define WMI Guids, e.g. DiskPerfGuid.
  22. //
  23. #ifndef INITGUID
  24. #define INITGUID 1
  25. #endif
  26. #include <wmiguid.h>
  27. //
  28. // Global variables.
  29. //
  30. //
  31. // This is the idle detection global context. It is declared as a
  32. // single element array so that ItSrvGlobalContext can be used as a
  33. // pointer (allowing us to switch to allocating it from heap etc. in
  34. // the future).
  35. //
  36. ITSRV_GLOBAL_CONTEXT ItSrvGlobalContext[1] = {0};
  37. //
  38. // Implementation of server side exposed functions.
  39. //
  40. DWORD
  41. ItSrvInitialize (
  42. VOID
  43. )
  44. /*++
  45. Routine Description:
  46. Initializes the global idle detection context. This function
  47. should be called after at least one ncalrpc binding has been
  48. registered.
  49. Arguments:
  50. None.
  51. Return Value:
  52. Win32 error code.
  53. --*/
  54. {
  55. DWORD ErrorCode;
  56. PITSRV_GLOBAL_CONTEXT GlobalContext;
  57. PIT_IDLE_DETECTION_PARAMETERS Parameters;
  58. ULONG StatusIdx;
  59. BOOLEAN StartedServer;
  60. BOOLEAN AcquiredLock;
  61. //
  62. // Initialize locals.
  63. //
  64. GlobalContext = ItSrvGlobalContext;
  65. StartedServer = FALSE;
  66. AcquiredLock = FALSE;
  67. DBGPR((ITID,ITTRC,"IDLE: SrvInitialize(%p)\n",GlobalContext));
  68. //
  69. // Initialize the server context structure. Before we do anything
  70. // that can fail we initialize fields to reasonable values so we
  71. // know what to cleanup. The following fields really have to be
  72. // initialized to zero:
  73. //
  74. // StatusVersion
  75. // GlobalLock
  76. // IdleDetectionTimerHandle
  77. // StopIdleDetection
  78. // IdleDetectionStopped
  79. // RemovedRunningIdleTask
  80. // DiskPerfWmiHandle
  81. // WmiQueryBuffer
  82. // WmiQueryBufferSize
  83. // NumProcessors
  84. // IsIdleDetectionCallbackRunning
  85. // Parameters
  86. // ProcessIdleTasksNotifyRoutine
  87. // RpcBindingVector
  88. // RegisteredRPCInterface
  89. // RegisteredRPCEndpoint
  90. //
  91. RtlZeroMemory(GlobalContext, sizeof(ITSRV_GLOBAL_CONTEXT));
  92. //
  93. // Initialize the idle tasks list.
  94. //
  95. InitializeListHead(&GlobalContext->IdleTasksList);
  96. //
  97. // Initialize the status now (so cleanup does not complain). From
  98. // this point on, UpdateStatus should be called to set the status.
  99. //
  100. GlobalContext->Status = ItSrvStatusInitializing;
  101. for (StatusIdx = 0; StatusIdx < ITSRV_GLOBAL_STATUS_HISTORY_SIZE; StatusIdx++) {
  102. GlobalContext->LastStatus[StatusIdx] = ItSrvStatusInitializing;
  103. }
  104. //
  105. // Initialize the system snapshot buffer.
  106. //
  107. ItSpInitializeSystemSnapshot(&GlobalContext->LastSystemSnapshot);
  108. //
  109. // Initialize the server global context lock. We need at least a
  110. // lock to protect the idle task list. Since nearly all operations
  111. // will involve the list, to make the code simple, we just have a
  112. // single lock for the list and for other operations on the global
  113. // context (e.g. uninitialization etc).
  114. //
  115. GlobalContext->GlobalLock = CreateMutex(NULL, FALSE, NULL);
  116. if (GlobalContext->GlobalLock == NULL) {
  117. ErrorCode = GetLastError();
  118. goto cleanup;
  119. }
  120. //
  121. // Initialize the event that will be set when we should not be
  122. // doing idle detection anymore (e.g. due to server shutdown, or
  123. // no more idle tasks remaining). It is set by default, since
  124. // there are no idle tasks to start with. It signals a running
  125. // idle detection callback to quickly exit.
  126. //
  127. GlobalContext->StopIdleDetection = CreateEvent(NULL,
  128. TRUE,
  129. TRUE,
  130. NULL);
  131. if (GlobalContext->StopIdleDetection == NULL) {
  132. ErrorCode = GetLastError();
  133. goto cleanup;
  134. }
  135. //
  136. // Initialize the event that gets set when idle detection is fully
  137. // stopped and it is OK to start a new callback. It is set by
  138. // default.
  139. //
  140. GlobalContext->IdleDetectionStopped = CreateEvent(NULL,
  141. TRUE,
  142. TRUE,
  143. NULL);
  144. if (GlobalContext->IdleDetectionStopped == NULL) {
  145. ErrorCode = GetLastError();
  146. goto cleanup;
  147. }
  148. //
  149. // Initialize the event that gets set when a currently running
  150. // idle task is removed/unregistered to signal the idle detection
  151. // callback to move on to other idle tasks.
  152. //
  153. GlobalContext->RemovedRunningIdleTask = CreateEvent(NULL,
  154. TRUE,
  155. FALSE,
  156. NULL);
  157. if (GlobalContext->RemovedRunningIdleTask == NULL) {
  158. ErrorCode = GetLastError();
  159. goto cleanup;
  160. }
  161. //
  162. // Set the default parameters.
  163. //
  164. Parameters = &GlobalContext->Parameters;
  165. Parameters->IdleDetectionPeriod = IT_DEFAULT_IDLE_DETECTION_PERIOD;
  166. Parameters->IdleVerificationPeriod = IT_DEFAULT_IDLE_VERIFICATION_PERIOD;
  167. Parameters->NumVerifications = IT_DEFAULT_NUM_IDLE_VERIFICATIONS;
  168. Parameters->IdleInputCheckPeriod = IT_DEFAULT_IDLE_USER_INPUT_CHECK_PERIOD;
  169. Parameters->IdleTaskRunningCheckPeriod = IT_DEFAULT_IDLE_TASK_RUNNING_CHECK_PERIOD;
  170. Parameters->MinCpuIdlePercentage = IT_DEFAULT_MIN_CPU_IDLE_PERCENTAGE;
  171. Parameters->MinDiskIdlePercentage = IT_DEFAULT_MIN_DISK_IDLE_PERCENTAGE;
  172. Parameters->MaxNumRegisteredTasks = IT_DEFAULT_MAX_REGISTERED_TASKS;
  173. //
  174. // Acquire the lock to avoid any race conditions after we mark the
  175. // server started.
  176. //
  177. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  178. AcquiredLock = TRUE;
  179. //
  180. // We are done until an idle task that we can run gets
  181. // registered. We will start idle detection (e.g. get initial
  182. // snapshot, queue a timer etc) then.
  183. //
  184. ItSpUpdateStatus(GlobalContext, ItSrvStatusWaitingForIdleTasks);
  185. //
  186. // After this point, if we fail, we cannot just cleanup: we have
  187. // to stop the server.
  188. //
  189. StartedServer = TRUE;
  190. //
  191. // We have to start up the RPC server only after we have
  192. // initialized everything else, so when RPC calls come the server
  193. // is ready.
  194. //
  195. //
  196. // We don't want to register any well known end points because
  197. // each LPC endpoint will cause another thread to be spawned to
  198. // listen on it. We try to bind through only the existing
  199. // bindings.
  200. //
  201. ErrorCode = RpcServerInqBindings(&GlobalContext->RpcBindingVector);
  202. if (ErrorCode != RPC_S_OK) {
  203. //
  204. // At least one binding should have been registered before we
  205. // got called. It would be cool if we could check to see if
  206. // there is an ncalrpc binding registered.
  207. //
  208. IT_ASSERT(ErrorCode != RPC_S_NO_BINDINGS);
  209. goto cleanup;
  210. }
  211. ErrorCode = RpcEpRegister(idletask_ServerIfHandle,
  212. GlobalContext->RpcBindingVector,
  213. NULL,
  214. NULL);
  215. if (ErrorCode != RPC_S_OK) {
  216. goto cleanup;
  217. }
  218. GlobalContext->RegisteredRPCEndpoint = TRUE;
  219. //
  220. // Register an auto-listen interface so we are not dependant on
  221. // others calling RpcMgmtStart/Stop listening.
  222. //
  223. ErrorCode = RpcServerRegisterIfEx(idletask_ServerIfHandle,
  224. NULL,
  225. NULL,
  226. RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_SECURE_ONLY,
  227. RPC_C_LISTEN_MAX_CALLS_DEFAULT,
  228. ItSpRpcSecurityCallback);
  229. if (ErrorCode != RPC_S_OK) {
  230. goto cleanup;
  231. }
  232. //
  233. // Register default security principal name for this process, e.g.
  234. // NT Authority\LocalSystem.
  235. //
  236. ErrorCode = RpcServerRegisterAuthInfo(NULL, RPC_C_AUTHN_WINNT, NULL, NULL);
  237. if (ErrorCode != RPC_S_OK) {
  238. goto cleanup;
  239. }
  240. GlobalContext->RegisteredRPCInterface = TRUE;
  241. //
  242. // We are done.
  243. //
  244. ErrorCode = ERROR_SUCCESS;
  245. cleanup:
  246. if (AcquiredLock) {
  247. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  248. }
  249. if (ErrorCode != ERROR_SUCCESS) {
  250. if (StartedServer) {
  251. ItSrvUninitialize();
  252. } else {
  253. ItSpCleanupGlobalContext(GlobalContext);
  254. }
  255. }
  256. DBGPR((ITID,ITTRC,"IDLE: SrvInitialize(%p)=%x\n",GlobalContext,ErrorCode));
  257. return ErrorCode;
  258. }
  259. VOID
  260. ItSrvUninitialize (
  261. VOID
  262. )
  263. /*++
  264. Routine Description:
  265. Winds down and uninitializes the server global context.
  266. Do not call this from the idle detection timer callback function
  267. thread, because there will be a deadlock when we try to delete the
  268. timer.
  269. This function should not be called before ItSrvInitialize
  270. completes. It should be called only once per ItSrvInitialize.
  271. Arguments:
  272. None.
  273. Return Value:
  274. None.
  275. --*/
  276. {
  277. PITSRV_GLOBAL_CONTEXT GlobalContext;
  278. BOOLEAN AcquiredLock;
  279. DWORD ErrorCode;
  280. //
  281. // Initialize locals.
  282. //
  283. GlobalContext = ItSrvGlobalContext;
  284. AcquiredLock = FALSE;
  285. DBGPR((ITID,ITTRC,"IDLE: SrvUninitialize(%p)\n",GlobalContext));
  286. // NOTICE-2002/03/26-ScottMa -- It is assumed that initialization was
  287. // successful prior to calling Uninitialize, or the acquire below
  288. // might not be safe.
  289. //
  290. // Acquire the global lock and update status.
  291. //
  292. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  293. AcquiredLock = TRUE;
  294. //
  295. // Make sure we get uninitialized only once.
  296. //
  297. IT_ASSERT(GlobalContext->Status != ItSrvStatusUninitializing);
  298. IT_ASSERT(GlobalContext->Status != ItSrvStatusUninitialized);
  299. ItSpUpdateStatus(GlobalContext, ItSrvStatusUninitializing);
  300. //
  301. // If idle detection is alive, we need to stop it before we tell
  302. // RPCs to go away. We need to do this even if there are
  303. // registered idle tasks. Since we have set the state to
  304. // ItSrvStatusUninitializing, new "register idle task"s
  305. // won't get stuck.
  306. //
  307. if (GlobalContext->IdleDetectionTimerHandle) {
  308. ItSpStopIdleDetection(GlobalContext);
  309. }
  310. //
  311. // Release the lock so rpc call-ins can grab the lock to
  312. // uninitialize/exit as necessary.
  313. //
  314. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  315. AcquiredLock = FALSE;
  316. //
  317. // Make sure incoming client register/unregister calls are stopped.
  318. //
  319. if (GlobalContext->RegisteredRPCInterface) {
  320. //
  321. // If we registered an interface, we should have registered
  322. // our endpoint in the local database too.
  323. //
  324. IT_ASSERT(GlobalContext->RegisteredRPCEndpoint);
  325. //
  326. // Calling UnregisterIfEx makes sure all the context handles
  327. // are run down so we don't get rundown calls after we have
  328. // uninitialized.
  329. //
  330. RpcServerUnregisterIfEx(idletask_ServerIfHandle, NULL, TRUE);
  331. }
  332. if (GlobalContext->RegisteredRPCEndpoint) {
  333. //
  334. // We could have registered an endpoint only if we
  335. // successfully queried bindings.
  336. //
  337. IT_ASSERT(GlobalContext->RpcBindingVector);
  338. RpcEpUnregister(idletask_ServerIfHandle,
  339. GlobalContext->RpcBindingVector,
  340. NULL);
  341. }
  342. //
  343. // Wait until idle detection is fully stopped (e.g. the callback
  344. // exits, the timer is dequeued etc.)
  345. //
  346. WaitForSingleObject(GlobalContext->IdleDetectionStopped, INFINITE);
  347. //
  348. // At this point no workers should be active and no new requests
  349. // should be coming. Now we will be breaking down the global state
  350. // structure (e.g. freeing memory, closing events etc.) they
  351. // would be using.
  352. //
  353. ItSpCleanupGlobalContext(GlobalContext);
  354. DBGPR((ITID,ITTRC,"IDLE: SrvUninitialize(%p)=0\n",GlobalContext));
  355. return;
  356. }
  357. DWORD
  358. ItSrvRegisterIdleTask (
  359. ITRPC_HANDLE Reserved,
  360. IT_HANDLE *ItHandle,
  361. PIT_IDLE_TASK_PROPERTIES IdleTaskProperties
  362. )
  363. /*++
  364. Routine Description:
  365. Adds a new idle task to be run when the system is idle.
  366. Arguments:
  367. Reserved - Ignored.
  368. ItHandle - Context handle returned to the client.
  369. IdleTaskProperties - Pointer to properties for the idle task.
  370. Return Value:
  371. Status.
  372. --*/
  373. {
  374. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  375. PITSRV_GLOBAL_CONTEXT GlobalContext;
  376. HANDLE ClientProcess;
  377. ULONG FailedCheckId;
  378. DWORD ErrorCode;
  379. DWORD WaitResult;
  380. LONG StatusVersion;
  381. ULONG NumLoops;
  382. BOOL Result;
  383. BOOLEAN AcquiredLock;
  384. BOOLEAN ImpersonatingClient;
  385. //
  386. // Initialize locals.
  387. //
  388. IdleTask = NULL;
  389. AcquiredLock = FALSE;
  390. ImpersonatingClient = FALSE;
  391. ClientProcess = NULL;
  392. GlobalContext = ItSrvGlobalContext;
  393. //
  394. // Initialize parameters.
  395. //
  396. *ItHandle = NULL;
  397. DBGPR((ITID,ITTRC,"IDLE: SrvRegisterTask(%p)\n",IdleTaskProperties));
  398. //
  399. // Allocate a new idle task context.
  400. //
  401. IdleTask = IT_ALLOC(sizeof(ITSRV_IDLE_TASK_CONTEXT));
  402. if (!IdleTask) {
  403. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  404. goto cleanup;
  405. }
  406. //
  407. // Initialize the fields to safe values so we know what to
  408. // cleanup.
  409. //
  410. IdleTask->Status = ItIdleTaskInitializing;
  411. IdleTask->StartEvent = NULL;
  412. IdleTask->StopEvent = NULL;
  413. //
  414. // Copy and verify input buffer.
  415. //
  416. IdleTask->Properties = *IdleTaskProperties;
  417. FailedCheckId = ItpVerifyIdleTaskProperties(&IdleTask->Properties);
  418. if (FailedCheckId != 0) {
  419. ErrorCode = ERROR_BAD_FORMAT;
  420. goto cleanup;
  421. }
  422. //
  423. // Impersonate the client to open the start/stop events. They
  424. // should have been created by the client.
  425. //
  426. ErrorCode = RpcImpersonateClient(NULL);
  427. if (ErrorCode != RPC_S_OK) {
  428. goto cleanup;
  429. }
  430. ImpersonatingClient = TRUE;
  431. //
  432. // Open the client process. Since we impersonated the client, it
  433. // is safe to use the client id it specifies.
  434. //
  435. // ISSUE-2002/03/26-ScottMa -- If possible, change PROCESS_ALL_ACCESS to
  436. // PROCESS_DUP_HANDLE to follow principle of least priveledge.
  437. ClientProcess = OpenProcess(PROCESS_ALL_ACCESS,
  438. FALSE,
  439. IdleTaskProperties->ProcessId);
  440. if (!ClientProcess) {
  441. ErrorCode = GetLastError();
  442. goto cleanup;
  443. }
  444. //
  445. // Get handle to the start event.
  446. //
  447. // ISSUE-2002/03/26-ScottMa -- If possible, change EVENT_ALL_ACCESS to
  448. // EVENT_MODIFY_STATE to follow principle of least priveledge.
  449. Result = DuplicateHandle(ClientProcess,
  450. (HANDLE) IdleTaskProperties->StartEventHandle,
  451. GetCurrentProcess(),
  452. &IdleTask->StartEvent,
  453. EVENT_ALL_ACCESS,
  454. FALSE,
  455. 0);
  456. if (!Result) {
  457. ErrorCode = GetLastError();
  458. goto cleanup;
  459. }
  460. //
  461. // Get handle to the stop event.
  462. //
  463. // ISSUE-2002/03/26-ScottMa -- If possible, change EVENT_ALL_ACCESS to
  464. // EVENT_MODIFY_STATE to follow principle of least priveledge.
  465. Result = DuplicateHandle(ClientProcess,
  466. (HANDLE) IdleTaskProperties->StopEventHandle,
  467. GetCurrentProcess(),
  468. &IdleTask->StopEvent,
  469. EVENT_ALL_ACCESS,
  470. FALSE,
  471. 0);
  472. if (!Result) {
  473. ErrorCode = GetLastError();
  474. goto cleanup;
  475. }
  476. //
  477. // No need to impersonate any longer.
  478. //
  479. RpcRevertToSelf();
  480. ImpersonatingClient = FALSE;
  481. //
  482. // Make sure the handle is for an event and it is in the right
  483. // state.
  484. //
  485. if (!ResetEvent(IdleTask->StartEvent)) {
  486. ErrorCode = GetLastError();
  487. goto cleanup;
  488. }
  489. if (!SetEvent(IdleTask->StopEvent)) {
  490. ErrorCode = GetLastError();
  491. goto cleanup;
  492. }
  493. //
  494. // Acquire the server lock to check the status and insert new task
  495. // into the list.
  496. //
  497. NumLoops = 0;
  498. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  499. AcquiredLock = TRUE;
  500. do {
  501. //
  502. // We should be holding the GlobalLock when we come here the
  503. // first time or after looping. Inside the loop we may let go
  504. // of the lock, but we have to reacquire it before we loop.
  505. //
  506. IT_ASSERT(AcquiredLock);
  507. //
  508. // If there are already too many idle tasks registered, bail
  509. // out.
  510. //
  511. if (GlobalContext->NumIdleTasks >=
  512. GlobalContext->Parameters.MaxNumRegisteredTasks) {
  513. ErrorCode = ERROR_TOO_MANY_OPEN_FILES;
  514. goto cleanup;
  515. }
  516. switch (GlobalContext->Status) {
  517. case ItSrvStatusInitializing:
  518. //
  519. // We should not have gotten called if the server is still
  520. // initializing!
  521. //
  522. IT_ASSERT(FALSE);
  523. ErrorCode = ERROR_NOT_READY;
  524. goto cleanup;
  525. case ItSrvStatusUninitializing:
  526. //
  527. // If the server is uninitializing, we should not add a
  528. // new idle task.
  529. //
  530. ErrorCode = ERROR_NOT_READY;
  531. goto cleanup;
  532. break;
  533. case ItSrvStatusUninitialized:
  534. //
  535. // The server should not have uninitialized while we could be
  536. // running.
  537. //
  538. IT_ASSERT(FALSE);
  539. ErrorCode = ERROR_NOT_READY;
  540. goto cleanup;
  541. case ItSrvStatusStoppingIdleDetection:
  542. //
  543. // The idle task list should be empty if we are stopping
  544. // idle detection. There should not be a
  545. // IdleDetectionTimerHandle either.
  546. //
  547. IT_ASSERT(IsListEmpty(&GlobalContext->IdleTasksList));
  548. IT_ASSERT(GlobalContext->IdleDetectionTimerHandle == NULL);
  549. //
  550. // We must wait until idle detection is fully stopped. We
  551. // need to release our lock to do so. But note the status
  552. // version first.
  553. //
  554. StatusVersion = GlobalContext->StatusVersion;
  555. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  556. AcquiredLock = FALSE;
  557. WaitResult = WaitForSingleObject(GlobalContext->IdleDetectionStopped,
  558. INFINITE);
  559. if (WaitResult != WAIT_OBJECT_0) {
  560. DBGPR((ITID,ITERR,"IDLE: SrvRegisterTask-FailedWaitStop\n"));
  561. ErrorCode = ERROR_INVALID_FUNCTION;
  562. goto cleanup;
  563. }
  564. //
  565. // Reacquire the lock.
  566. //
  567. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  568. AcquiredLock = TRUE;
  569. //
  570. // If nobody woke before us and updated the status, update
  571. // it now.
  572. //
  573. if (StatusVersion == GlobalContext->StatusVersion) {
  574. IT_ASSERT(GlobalContext->Status == ItSrvStatusStoppingIdleDetection);
  575. ItSpUpdateStatus(GlobalContext, ItSrvStatusWaitingForIdleTasks);
  576. }
  577. //
  578. // Loop to do what is necessary next.
  579. //
  580. break;
  581. case ItSrvStatusWaitingForIdleTasks:
  582. //
  583. // The idle task list should be empty if we are waiting
  584. // for idle tasks.
  585. //
  586. IT_ASSERT(IsListEmpty(&GlobalContext->IdleTasksList));
  587. //
  588. // If we are waiting for idle tasks, start idle detection
  589. // so we can add our task.
  590. //
  591. ErrorCode = ItSpStartIdleDetection(GlobalContext);
  592. if (ErrorCode != ERROR_SUCCESS) {
  593. goto cleanup;
  594. }
  595. //
  596. // Update the status.
  597. //
  598. ItSpUpdateStatus(GlobalContext, ItSrvStatusDetectingIdle);
  599. //
  600. // Loop and insert our idle task into the list. Note that
  601. // we are not releasing the lock, so we will not be in a
  602. // state where the status is ItSrvStatusDetectingIdle but
  603. // there are no idle tasks in the list.
  604. //
  605. break;
  606. case ItSrvStatusDetectingIdle:
  607. case ItSrvStatusRunningIdleTasks:
  608. //
  609. // If we are detecting idle, we just need to insert our
  610. // task into the list and break out.
  611. //
  612. //
  613. // This operation currently does not fail. If in the
  614. // future it may, make sure to handle the case we started
  615. // idle detection for this task. It is not an acceptable
  616. // state to have idle detection but no tasks in the list.
  617. //
  618. //
  619. // Insert the task into the list. We do not check for
  620. // duplicates and such as RPC helps us maintain context
  621. // per registration.
  622. //
  623. GlobalContext->NumIdleTasks++;
  624. InsertTailList(&GlobalContext->IdleTasksList,
  625. &IdleTask->IdleTaskLink);
  626. IdleTask->Status = ItIdleTaskQueued;
  627. break;
  628. default:
  629. //
  630. // We should be handling all valid cases above.
  631. //
  632. IT_ASSERT(FALSE);
  633. ErrorCode = ERROR_INVALID_FUNCTION;
  634. goto cleanup;
  635. }
  636. //
  637. // We should be still holding the global lock.
  638. //
  639. IT_ASSERT(AcquiredLock);
  640. //
  641. // Break out if we could queue our task.
  642. //
  643. if (IdleTask->Status == ItIdleTaskQueued) {
  644. break;
  645. }
  646. //
  647. // We should not loop too many times.
  648. //
  649. NumLoops++;
  650. // FUTURE-2002/03/26-ScottMa -- This value for the maximum loop
  651. // count seems rather large... Does this function even need to
  652. // be written as a loop (it seems very sequential)?
  653. if (NumLoops > 128) {
  654. DBGPR((ITID,ITERR,"IDLE: SrvRegisterTask-LoopTooMuch\n"));
  655. ErrorCode = ERROR_INVALID_FUNCTION;
  656. goto cleanup;
  657. }
  658. } while (TRUE);
  659. //
  660. // We should be still holding the lock.
  661. //
  662. IT_ASSERT(AcquiredLock);
  663. //
  664. // If we came here, we successfully inserted the task into the
  665. // list.
  666. //
  667. IT_ASSERT(!IsListEmpty(&GlobalContext->IdleTasksList));
  668. IT_ASSERT(IdleTask->Status == ItIdleTaskQueued);
  669. //
  670. // Release the lock.
  671. //
  672. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  673. AcquiredLock = FALSE;
  674. //
  675. // We are done.
  676. //
  677. ErrorCode = ERROR_SUCCESS;
  678. cleanup:
  679. if (ClientProcess) {
  680. CloseHandle(ClientProcess);
  681. }
  682. if (ImpersonatingClient) {
  683. RpcRevertToSelf();
  684. }
  685. if (ErrorCode != ERROR_SUCCESS) {
  686. if (IdleTask) {
  687. ItSpCleanupIdleTask(IdleTask);
  688. IT_FREE(IdleTask);
  689. }
  690. } else {
  691. *ItHandle = (IT_HANDLE)IdleTask;
  692. }
  693. if (AcquiredLock) {
  694. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  695. }
  696. DBGPR((ITID,ITTRC,"IDLE: SrvRegisterTask(%p)=%x\n",IdleTaskProperties,ErrorCode));
  697. return ErrorCode;
  698. }
  699. VOID
  700. ItSrvUnregisterIdleTask (
  701. ITRPC_HANDLE Reserved,
  702. IT_HANDLE *ItHandle
  703. )
  704. /*++
  705. Routine Description:
  706. This function is a stub for ItSpUnregisterIdleTask that does the
  707. real work. Please see that function for comments.
  708. Arguments:
  709. See ItSpUnregisterIdleTask.
  710. Return Value:
  711. See ItSpUnregisterIdleTask.
  712. --*/
  713. {
  714. ItSpUnregisterIdleTask(Reserved, ItHandle, FALSE);
  715. }
  716. DWORD
  717. ItSrvSetDetectionParameters (
  718. ITRPC_HANDLE Reserved,
  719. PIT_IDLE_DETECTION_PARAMETERS Parameters
  720. )
  721. /*++
  722. Routine Description:
  723. This debug routine is used by test programs to set the idle detection
  724. parameters. It will return ERROR_INVALID_FUNCTION on retail build.
  725. Arguments:
  726. Reserved - Ignored.
  727. Parameters - New idle detection parameters.
  728. Return Value:
  729. Win32 error code.
  730. --*/
  731. {
  732. DWORD ErrorCode;
  733. PITSRV_GLOBAL_CONTEXT GlobalContext;
  734. //
  735. // Initialize locals.
  736. //
  737. GlobalContext = ItSrvGlobalContext;
  738. DBGPR((ITID,ITTRC,"IDLE: SrvSetParameters(%p)\n",Parameters));
  739. #ifndef IT_DBG
  740. //
  741. // This is a debug only API.
  742. //
  743. ErrorCode = ERROR_INVALID_FUNCTION;
  744. #else // !IT_DBG
  745. //
  746. // Acquire the lock and copy the new parameters.
  747. //
  748. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  749. // NOTICE-2002/03/26-ScottMa -- The supplied parameters are unchecked.
  750. GlobalContext->Parameters = *Parameters;
  751. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  752. ErrorCode = ERROR_SUCCESS;
  753. #endif // !IT_DBG
  754. DBGPR((ITID,ITTRC,"IDLE: SrvSetParameters(%p)=%d\n",Parameters,ErrorCode));
  755. return ErrorCode;
  756. }
  757. DWORD
  758. ItSrvProcessIdleTasks (
  759. ITRPC_HANDLE Reserved
  760. )
  761. /*++
  762. Routine Description:
  763. This routine forces all queued tasks (if there are any) to be processed
  764. right away.
  765. Arguments:
  766. Reserved - Ignored.
  767. Return Value:
  768. Win32 error code.
  769. --*/
  770. {
  771. // ISSUE-2002/03/26-ScottMa -- Is this function safe to be re-entrant?
  772. PITSRV_GLOBAL_CONTEXT GlobalContext;
  773. ITSRV_IDLE_DETECTION_OVERRIDE Overrides;
  774. DWORD ErrorCode;
  775. DWORD WaitResult;
  776. BOOLEAN AcquiredLock;
  777. //
  778. // Initialize locals.
  779. //
  780. AcquiredLock = FALSE;
  781. GlobalContext = ItSrvGlobalContext;
  782. DBGPR((ITID,ITTRC,"IDLE: SrvProcessAll()\n"));
  783. //
  784. // If a notification routine was specified, call it.
  785. //
  786. if (GlobalContext->ProcessIdleTasksNotifyRoutine) {
  787. GlobalContext->ProcessIdleTasksNotifyRoutine();
  788. }
  789. //
  790. // Acquire the server lock.
  791. //
  792. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  793. AcquiredLock = TRUE;
  794. //
  795. // The server should not have shutdown while we could be called.
  796. //
  797. IT_ASSERT(GlobalContext->Status != ItSrvStatusUninitialized);
  798. //
  799. // If the server is shutting down, we should not do anything.
  800. //
  801. if (GlobalContext->Status == ItSrvStatusUninitializing) {
  802. ErrorCode = ERROR_NOT_READY;
  803. goto cleanup;
  804. }
  805. //
  806. // If there are no tasks queued, we are done.
  807. //
  808. if (IsListEmpty(&GlobalContext->IdleTasksList)) {
  809. ErrorCode = ERROR_SUCCESS;
  810. goto cleanup;
  811. }
  812. //
  813. // There should be a timer queued if the list is not empty.
  814. //
  815. IT_ASSERT(GlobalContext->IdleDetectionTimerHandle);
  816. //
  817. // Set idle detection overrides:
  818. //
  819. Overrides = 0;
  820. Overrides |= ItSrvOverrideIdleDetection;
  821. Overrides |= ItSrvOverrideIdleVerification;
  822. Overrides |= ItSrvOverrideUserInputCheckToStopTask;
  823. Overrides |= ItSrvOverridePostTaskIdleCheck;
  824. Overrides |= ItSrvOverrideLongRequeueTime;
  825. Overrides |= ItSrvOverrideBatteryCheckToStopTask;
  826. Overrides |= ItSrvOverrideAutoPowerCheckToStopTask;
  827. GlobalContext->IdleDetectionOverride = Overrides;
  828. //
  829. // If an idle detection callback is not running, try to start the next one
  830. // sooner (e.g. after 50ms).
  831. //
  832. if (!GlobalContext->IsIdleDetectionCallbackRunning) {
  833. if (!ChangeTimerQueueTimer(NULL,
  834. GlobalContext->IdleDetectionTimerHandle,
  835. 50,
  836. IT_VERYLONG_TIMER_PERIOD)) {
  837. ErrorCode = GetLastError();
  838. goto cleanup;
  839. }
  840. }
  841. //
  842. // Release the lock.
  843. //
  844. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  845. AcquiredLock = FALSE;
  846. //
  847. // Wait for all tasks to be processed: i.e. when StopIdleDetection event is set.
  848. //
  849. WaitResult = WaitForSingleObject(GlobalContext->StopIdleDetection, INFINITE);
  850. if (WaitResult != WAIT_OBJECT_0) {
  851. ErrorCode = GetLastError();
  852. goto cleanup;
  853. }
  854. ErrorCode = ERROR_SUCCESS;
  855. cleanup:
  856. GlobalContext->IdleDetectionOverride = 0;
  857. if (AcquiredLock) {
  858. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  859. }
  860. DBGPR((ITID,ITTRC,"IDLE: SrvProcessAll()=%x\n",ErrorCode));
  861. return ErrorCode;
  862. }
  863. VOID
  864. __RPC_USER
  865. IT_HANDLE_rundown (
  866. IT_HANDLE ItHandle
  867. )
  868. /*++
  869. Routine Description:
  870. This routine gets called by RPC when a client dies without
  871. unregistering.
  872. Arguments:
  873. ItHandle - Context handle for the client.
  874. Return Value:
  875. None.
  876. --*/
  877. {
  878. DWORD ErrorCode;
  879. //
  880. // Unregister the registered task.
  881. //
  882. ItSpUnregisterIdleTask(NULL, &ItHandle, TRUE);
  883. DBGPR((ITID,ITTRC,"IDLE: SrvHandleRundown(%p)\n",ItHandle));
  884. return;
  885. }
  886. //
  887. // Implementation of server side support functions.
  888. //
  889. RPC_STATUS
  890. RPC_ENTRY
  891. ItSpRpcSecurityCallback (
  892. IN RPC_IF_HANDLE *Interface,
  893. IN PVOID Context
  894. )
  895. /*++
  896. Routine Description:
  897. Arguments:
  898. Return Value:
  899. Win32 error code.
  900. --*/
  901. {
  902. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  903. PSID AdministratorsSid;
  904. HANDLE ThreadToken;
  905. WCHAR *BindingString;
  906. WCHAR *ProtocolSequence;
  907. DWORD ErrorCode;
  908. BOOL ClientIsAdmin;
  909. BOOLEAN ImpersonatingClient;
  910. BOOLEAN OpenedThreadToken;
  911. //
  912. // Initialize locals.
  913. //
  914. BindingString = NULL;
  915. ProtocolSequence = NULL;
  916. ImpersonatingClient = FALSE;
  917. OpenedThreadToken = FALSE;
  918. AdministratorsSid = NULL;
  919. //
  920. // Make sure that the caller is calling over LRPC. We do this by
  921. // determining the protocol sequence used from the string binding.
  922. //
  923. ErrorCode = RpcBindingToStringBinding(Context, &BindingString);
  924. if (ErrorCode != RPC_S_OK) {
  925. ErrorCode = RPC_S_ACCESS_DENIED;
  926. goto cleanup;
  927. }
  928. ErrorCode = RpcStringBindingParse(BindingString,
  929. NULL,
  930. &ProtocolSequence,
  931. NULL,
  932. NULL,
  933. NULL);
  934. if (ErrorCode != RPC_S_OK) {
  935. ErrorCode = RPC_S_ACCESS_DENIED;
  936. goto cleanup;
  937. }
  938. if (_wcsicmp(ProtocolSequence, L"ncalrpc") != 0) {
  939. ErrorCode = RPC_S_ACCESS_DENIED;
  940. goto cleanup;
  941. }
  942. //
  943. // Make sure the caller has admin priviliges:
  944. //
  945. //
  946. // Build the Administrators group SID so we can check if the
  947. // caller is has administrator priviliges.
  948. //
  949. if (!AllocateAndInitializeSid(&NtAuthority,
  950. 2,
  951. SECURITY_BUILTIN_DOMAIN_RID,
  952. DOMAIN_ALIAS_RID_ADMINS,
  953. 0, 0, 0, 0, 0, 0,
  954. &AdministratorsSid)) {
  955. ErrorCode = GetLastError();
  956. goto cleanup;
  957. }
  958. //
  959. // Impersonate the client to check for membership/privilige.
  960. //
  961. ErrorCode = RpcImpersonateClient(NULL);
  962. if (ErrorCode != RPC_S_OK) {
  963. goto cleanup;
  964. }
  965. ImpersonatingClient = TRUE;
  966. //
  967. // Get the thread token and check to see if the client has admin
  968. // priviliges.
  969. //
  970. if (!OpenThreadToken(GetCurrentThread(),
  971. TOKEN_READ,
  972. FALSE,
  973. &ThreadToken)) {
  974. ErrorCode = GetLastError();
  975. goto cleanup;
  976. }
  977. OpenedThreadToken = TRUE;
  978. if (!CheckTokenMembership(ThreadToken,
  979. AdministratorsSid,
  980. &ClientIsAdmin)) {
  981. ErrorCode = GetLastError();
  982. goto cleanup;
  983. }
  984. if (!ClientIsAdmin) {
  985. ErrorCode = ERROR_ACCESS_DENIED;
  986. goto cleanup;
  987. }
  988. //
  989. // Everything looks good: allow this call to proceed.
  990. //
  991. ErrorCode = RPC_S_OK;
  992. cleanup:
  993. if (BindingString) {
  994. RpcStringFree(&BindingString);
  995. }
  996. if (ProtocolSequence) {
  997. RpcStringFree(&ProtocolSequence);
  998. }
  999. if (OpenedThreadToken) {
  1000. CloseHandle(ThreadToken);
  1001. }
  1002. if (AdministratorsSid) {
  1003. FreeSid (AdministratorsSid);
  1004. }
  1005. if (ImpersonatingClient) {
  1006. RpcRevertToSelf();
  1007. }
  1008. return ErrorCode;
  1009. };
  1010. VOID
  1011. ItSpUnregisterIdleTask (
  1012. ITRPC_HANDLE Reserved,
  1013. IT_HANDLE *ItHandle,
  1014. BOOLEAN CalledInternally
  1015. )
  1016. /*++
  1017. Routine Description:
  1018. Removes the specified idle task from the idle tasks list.
  1019. As well as from a client RPC, this function can also be called
  1020. from the idle detection callback to unregister an unresponsive
  1021. idle task etc.
  1022. Arguments:
  1023. Reserved - Ignored.
  1024. ItHandle - Registration RPC Context handle. NULL on return.
  1025. CalledInternally - Whether this function was called internally and
  1026. not from an RPC client.
  1027. Return Value:
  1028. None.
  1029. --*/
  1030. {
  1031. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1032. PITSRV_GLOBAL_CONTEXT GlobalContext;
  1033. HANDLE ClientProcess;
  1034. DWORD ErrorCode;
  1035. BOOLEAN AcquiredLock;
  1036. BOOLEAN ImpersonatingClient;
  1037. //
  1038. // Initialize locals.
  1039. //
  1040. GlobalContext = ItSrvGlobalContext;
  1041. AcquiredLock = FALSE;
  1042. ImpersonatingClient = FALSE;
  1043. ClientProcess = NULL;
  1044. DBGPR((ITID,ITTRC,"IDLE: SrvUnregisterTask(%p)\n",(ItHandle)?*ItHandle:0));
  1045. //
  1046. // Grab the lock to walk through the list.
  1047. //
  1048. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  1049. AcquiredLock = TRUE;
  1050. //
  1051. // The server should not have shutdown while we could be called.
  1052. //
  1053. IT_ASSERT(GlobalContext->Status != ItSrvStatusUninitialized);
  1054. //
  1055. // If the server is shutting down, we should not do anything.
  1056. //
  1057. if (GlobalContext->Status == ItSrvStatusUninitializing) {
  1058. ErrorCode = ERROR_NOT_READY;
  1059. goto cleanup;
  1060. }
  1061. //
  1062. // Find the task.
  1063. //
  1064. IdleTask = ItSpFindIdleTask(GlobalContext, *ItHandle);
  1065. if (!IdleTask) {
  1066. ErrorCode = ERROR_NOT_FOUND;
  1067. goto cleanup;
  1068. }
  1069. if (!CalledInternally) {
  1070. //
  1071. // To check security, impersonate the client and try to open the
  1072. // client process for this idle task.
  1073. //
  1074. ErrorCode = RpcImpersonateClient(NULL);
  1075. if (ErrorCode != RPC_S_OK) {
  1076. goto cleanup;
  1077. }
  1078. ImpersonatingClient = TRUE;
  1079. ClientProcess = OpenProcess(PROCESS_ALL_ACCESS,
  1080. FALSE,
  1081. IdleTask->Properties.ProcessId);
  1082. if (!ClientProcess) {
  1083. ErrorCode = GetLastError();
  1084. goto cleanup;
  1085. }
  1086. //
  1087. // If we could open the client process for this task, it is safe
  1088. // to go on with unregistration. Now revert back to ourselves.
  1089. //
  1090. CloseHandle(ClientProcess);
  1091. ClientProcess = NULL;
  1092. RpcRevertToSelf();
  1093. ImpersonatingClient = FALSE;
  1094. }
  1095. //
  1096. // Remove it from the list, cleanup its fields and free
  1097. // the memory allocated for it.
  1098. //
  1099. GlobalContext->NumIdleTasks--;
  1100. RemoveEntryList(&IdleTask->IdleTaskLink);
  1101. //
  1102. // As a safety feature, to prevent from holding back
  1103. // someone from running, set the run event and clear the
  1104. // stop event from the task to be removed.
  1105. //
  1106. ResetEvent(IdleTask->StopEvent);
  1107. SetEvent(IdleTask->StartEvent);
  1108. //
  1109. // If this is a running task, set the event that signals
  1110. // we are removing the running idle task. This way some
  1111. // other idle task may be started if the system is still
  1112. // idle.
  1113. //
  1114. if (IdleTask->Status == ItIdleTaskRunning) {
  1115. SetEvent(GlobalContext->RemovedRunningIdleTask);
  1116. }
  1117. ItSpCleanupIdleTask(IdleTask);
  1118. IT_FREE(IdleTask);
  1119. //
  1120. // Check if the list is empty.
  1121. //
  1122. if (IsListEmpty(&GlobalContext->IdleTasksList)) {
  1123. //
  1124. // If we removed the task and the list became empty, we have
  1125. // to update the status.
  1126. //
  1127. //
  1128. // The current status should not be "waiting for idle
  1129. // tasks" or "stopping idle detection", since the list was
  1130. // NOT empty.
  1131. //
  1132. IT_ASSERT(GlobalContext->Status != ItSrvStatusWaitingForIdleTasks);
  1133. IT_ASSERT(GlobalContext->Status != ItSrvStatusStoppingIdleDetection);
  1134. //
  1135. // Update the status.
  1136. //
  1137. ItSpUpdateStatus(GlobalContext, ItSrvStatusStoppingIdleDetection);
  1138. //
  1139. // Stop idle detection (e.g. close the timer handle, set event
  1140. // etc.)
  1141. //
  1142. ItSpStopIdleDetection(GlobalContext);
  1143. } else {
  1144. //
  1145. // The status should not be waiting for idle tasks or stopping
  1146. // idle detection if the list is not empty.
  1147. //
  1148. IT_ASSERT(GlobalContext->Status != ItSrvStatusWaitingForIdleTasks &&
  1149. GlobalContext->Status != ItSrvStatusStoppingIdleDetection);
  1150. }
  1151. ErrorCode = ERROR_SUCCESS;
  1152. cleanup:
  1153. if (ClientProcess) {
  1154. CloseHandle(ClientProcess);
  1155. }
  1156. if (ImpersonatingClient) {
  1157. RpcRevertToSelf();
  1158. }
  1159. if (AcquiredLock) {
  1160. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  1161. }
  1162. //
  1163. // NULL the context handle, so RPC stubs know to end the
  1164. // connection.
  1165. //
  1166. *ItHandle = NULL;
  1167. DBGPR((ITID,ITTRC,"IDLE: SrvUnregisterTask(%p)=%x\n",(ItHandle)?*ItHandle:0,ErrorCode));
  1168. return;
  1169. }
  1170. VOID
  1171. ItSpUpdateStatus (
  1172. PITSRV_GLOBAL_CONTEXT GlobalContext,
  1173. ITSRV_GLOBAL_CONTEXT_STATUS NewStatus
  1174. )
  1175. /*++
  1176. Routine Description:
  1177. This routine updates the current status and status history on the
  1178. global context. Global contexts GlobalLock should be held while
  1179. calling this function.
  1180. Arguments:
  1181. GlobalContext - Pointer to server context structure.
  1182. NewStatus - New status.
  1183. Return Value:
  1184. None.
  1185. --*/
  1186. {
  1187. LONG StatusIdx;
  1188. DBGPR((ITID,ITTRC,"IDLE: SrvUpdateStatus(%p,%x)\n",GlobalContext,NewStatus));
  1189. //
  1190. // Verify new status.
  1191. //
  1192. IT_ASSERT(NewStatus > ItSrvStatusMinStatus);
  1193. IT_ASSERT(NewStatus < ItSrvStatusMaxStatus);
  1194. //
  1195. // Update history.
  1196. //
  1197. IT_ASSERT(ITSRV_GLOBAL_STATUS_HISTORY_SIZE >= 1);
  1198. for (StatusIdx = ITSRV_GLOBAL_STATUS_HISTORY_SIZE - 1;
  1199. StatusIdx > 0;
  1200. StatusIdx--) {
  1201. IT_ASSERT(GlobalContext->LastStatus[StatusIdx - 1] > ItSrvStatusMinStatus);
  1202. IT_ASSERT(GlobalContext->LastStatus[StatusIdx - 1] < ItSrvStatusMaxStatus);
  1203. GlobalContext->LastStatus[StatusIdx] = GlobalContext->LastStatus[StatusIdx - 1];
  1204. }
  1205. //
  1206. // Verify current status and save it.
  1207. //
  1208. IT_ASSERT(GlobalContext->Status > ItSrvStatusMinStatus);
  1209. IT_ASSERT(GlobalContext->Status < ItSrvStatusMaxStatus);
  1210. GlobalContext->LastStatus[0] = GlobalContext->Status;
  1211. //
  1212. // Update current status.
  1213. //
  1214. GlobalContext->Status = NewStatus;
  1215. //
  1216. // Update status version.
  1217. //
  1218. GlobalContext->StatusVersion++;
  1219. DBGPR((ITID,ITTRC,"IDLE: SrvUpdateStatus(%p,%x)=%d\n",
  1220. GlobalContext, NewStatus,GlobalContext->StatusVersion));
  1221. return;
  1222. }
  1223. VOID
  1224. ItSpCleanupGlobalContext (
  1225. PITSRV_GLOBAL_CONTEXT GlobalContext
  1226. )
  1227. /*++
  1228. Routine Description:
  1229. This function cleans up the various fields of the ITSRV_GLOBAL_CONTEXT
  1230. structure passed in. It does not free the structure itself.
  1231. You should not be holding the global lock when calling this
  1232. function, as it will be freed too. No workers etc. should be
  1233. active. The idle detection timer should have already been
  1234. removed. The structure should not be used after cleanup until it
  1235. is initialized again.
  1236. The current status of the global context should either be
  1237. initializing or uninitializing. It will be set to uninitialized.
  1238. Arguments:
  1239. GlobalContext - Pointer to server context structure.
  1240. Return Value:
  1241. None.
  1242. --*/
  1243. {
  1244. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1245. PLIST_ENTRY ListHead;
  1246. HANDLE EventHandle;
  1247. DBGPR((ITID,ITSRVD,"IDLE: SrvCleanupContext(%p)\n",GlobalContext));
  1248. //
  1249. // Make sure there is no active idle detection timer.
  1250. //
  1251. if (GlobalContext->IdleDetectionTimerHandle) {
  1252. IT_ASSERT(FALSE);
  1253. return;
  1254. }
  1255. //
  1256. // Verify the status of the global context.
  1257. //
  1258. if (GlobalContext->Status != ItSrvStatusInitializing &&
  1259. GlobalContext->Status != ItSrvStatusUninitializing) {
  1260. IT_ASSERT(FALSE);
  1261. return;
  1262. }
  1263. //
  1264. // Close the handle to global lock.
  1265. //
  1266. if (GlobalContext->GlobalLock) {
  1267. CloseHandle(GlobalContext->GlobalLock);
  1268. }
  1269. //
  1270. // Close the handle to the various events.
  1271. //
  1272. if (GlobalContext->StopIdleDetection) {
  1273. CloseHandle(GlobalContext->StopIdleDetection);
  1274. }
  1275. if (GlobalContext->IdleDetectionStopped) {
  1276. CloseHandle(GlobalContext->IdleDetectionStopped);
  1277. }
  1278. if (GlobalContext->RemovedRunningIdleTask) {
  1279. CloseHandle(GlobalContext->RemovedRunningIdleTask);
  1280. }
  1281. //
  1282. // Close WMI DiskPerf handle.
  1283. //
  1284. if (GlobalContext->DiskPerfWmiHandle) {
  1285. WmiCloseBlock(GlobalContext->DiskPerfWmiHandle);
  1286. }
  1287. //
  1288. // Free WMI query buffer.
  1289. //
  1290. if (GlobalContext->WmiQueryBuffer) {
  1291. IT_FREE(GlobalContext->WmiQueryBuffer);
  1292. }
  1293. //
  1294. // Cleanup the snapshot buffer.
  1295. //
  1296. ItSpCleanupSystemSnapshot(&GlobalContext->LastSystemSnapshot);
  1297. //
  1298. // Walk through the list of registered idle tasks.
  1299. //
  1300. while (!IsListEmpty(&GlobalContext->IdleTasksList)) {
  1301. GlobalContext->NumIdleTasks--;
  1302. ListHead = RemoveHeadList(&GlobalContext->IdleTasksList);
  1303. IdleTask = CONTAINING_RECORD(ListHead,
  1304. ITSRV_IDLE_TASK_CONTEXT,
  1305. IdleTaskLink);
  1306. //
  1307. // Cleanup and free the idle task structure.
  1308. //
  1309. ItSpCleanupIdleTask(IdleTask);
  1310. IT_FREE(IdleTask);
  1311. }
  1312. //
  1313. // Free the RPC binding vector.
  1314. //
  1315. if (GlobalContext->RpcBindingVector) {
  1316. RpcBindingVectorFree(&GlobalContext->RpcBindingVector);
  1317. }
  1318. //
  1319. // Update status.
  1320. //
  1321. ItSpUpdateStatus(GlobalContext, ItSrvStatusUninitialized);
  1322. DBGPR((ITID,ITSRVD,"IDLE: SrvCleanupContext(%p)=0\n",GlobalContext));
  1323. return;
  1324. }
  1325. VOID
  1326. ItSpCleanupIdleTask (
  1327. PITSRV_IDLE_TASK_CONTEXT IdleTask
  1328. )
  1329. /*++
  1330. Routine Description:
  1331. This function cleans up the various fields of the ITSRV_IDLE_TASK_CONTEXT
  1332. structure. It does not free the structure itself.
  1333. Arguments:
  1334. IdleTask - Pointer to idle task server context.
  1335. Return Value:
  1336. None.
  1337. --*/
  1338. {
  1339. DBGPR((ITID,ITSRVD,"IDLE: SrvCleanupTask(%p)\n",IdleTask));
  1340. //
  1341. // Close handles to start/stop events.
  1342. //
  1343. if (IdleTask->StartEvent)
  1344. {
  1345. CloseHandle(IdleTask->StartEvent);
  1346. IdleTask->StartEvent = NULL;
  1347. }
  1348. if (IdleTask->StopEvent)
  1349. {
  1350. CloseHandle(IdleTask->StopEvent);
  1351. IdleTask->StopEvent = NULL;
  1352. }
  1353. }
  1354. ULONG
  1355. ItpVerifyIdleTaskProperties (
  1356. PIT_IDLE_TASK_PROPERTIES IdleTaskProperties
  1357. )
  1358. /*++
  1359. Routine Description:
  1360. Verifies the specified structure.
  1361. Arguments:
  1362. IdleTaskProperties - Pointer to properties for the idle task.
  1363. Return Value:
  1364. 0 - Verification passed.
  1365. FailedCheckId - Id of the check that failed.
  1366. --*/
  1367. {
  1368. ULONG FailedCheckId;
  1369. //
  1370. // Initialize locals.
  1371. //
  1372. FailedCheckId = 1;
  1373. //
  1374. // Verify the structure size/version.
  1375. //
  1376. if (IdleTaskProperties->Size != sizeof(*IdleTaskProperties)) {
  1377. FailedCheckId = 10;
  1378. goto cleanup;
  1379. }
  1380. //
  1381. // Verify that the idle task ID is valid.
  1382. //
  1383. if (IdleTaskProperties->IdleTaskId < 0 ||
  1384. IdleTaskProperties->IdleTaskId >= ItMaxIdleTaskId) {
  1385. FailedCheckId = 20;
  1386. goto cleanup;
  1387. }
  1388. // FUTURE-2002/03/26-ScottMa -- Should the handles and ProcessId fields
  1389. // also be validated?
  1390. //
  1391. // We passed all the checks.
  1392. //
  1393. FailedCheckId = 0;
  1394. cleanup:
  1395. DBGPR((ITID,ITSRVD,"IDLE: SrvVerifyTaskProp(%p)=%d\n",
  1396. IdleTaskProperties, FailedCheckId));
  1397. return FailedCheckId;
  1398. }
  1399. DWORD
  1400. ItSpStartIdleDetection (
  1401. PITSRV_GLOBAL_CONTEXT GlobalContext
  1402. )
  1403. /*++
  1404. Routine Description:
  1405. This function is called to start idle detection.
  1406. GlobalContext's GlobalLock should be held while calling this
  1407. function.
  1408. The current state should be ItSrvStatusWaitingForIdleTasks when
  1409. calling this function. The caller should ensure that stopping
  1410. previous idle detection has been completed.
  1411. The caller should update the state to "detecting idle" etc, if
  1412. this function returns success.
  1413. Arguments:
  1414. GlobalContext - Pointer to server context structure.
  1415. Return Value:
  1416. Win32 error code.
  1417. --*/
  1418. {
  1419. DWORD ErrorCode;
  1420. NTSTATUS Status;
  1421. SYSTEM_BASIC_INFORMATION BasicInfo;
  1422. DWORD TimerPeriod;
  1423. DBGPR((ITID,ITTRC,"IDLE: SrvStartIdleDetection(%p)\n",GlobalContext));
  1424. //
  1425. // Make sure the current status is ItSrvStatusWaitingForIdleTasks.
  1426. //
  1427. IT_ASSERT(GlobalContext->Status == ItSrvStatusWaitingForIdleTasks);
  1428. //
  1429. // If we do not already have a diskperf wmi handle, try to get
  1430. // one.
  1431. //
  1432. if (!GlobalContext->DiskPerfWmiHandle) {
  1433. //
  1434. // Get WMI context so we can query disk performance.
  1435. //
  1436. ErrorCode = WmiOpenBlock((GUID *)&DiskPerfGuid,
  1437. GENERIC_READ,
  1438. &GlobalContext->DiskPerfWmiHandle);
  1439. if (ErrorCode != ERROR_SUCCESS) {
  1440. //
  1441. // Disk counters may not be initiated. We'll have to do
  1442. // without them.
  1443. //
  1444. GlobalContext->DiskPerfWmiHandle = NULL;
  1445. }
  1446. }
  1447. //
  1448. // Determine the number of processors on the system.
  1449. //
  1450. if (!GlobalContext->NumProcessors) {
  1451. Status = NtQuerySystemInformation(SystemBasicInformation,
  1452. &BasicInfo,
  1453. sizeof(BasicInfo),
  1454. NULL);
  1455. if (!NT_SUCCESS(Status)) {
  1456. ErrorCode = RtlNtStatusToDosError(Status);
  1457. goto cleanup;
  1458. }
  1459. GlobalContext->NumProcessors = BasicInfo.NumberOfProcessors;
  1460. }
  1461. //
  1462. // Get initial snapshot only when this is the very first time we
  1463. // are starting idle detection. Otherwise we'll keep the last
  1464. // snapshot we got.
  1465. //
  1466. IT_ASSERT(ITSRV_GLOBAL_STATUS_HISTORY_SIZE >= 1);
  1467. if (GlobalContext->LastStatus[0] == ItSrvStatusInitializing) {
  1468. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  1469. &GlobalContext->LastSystemSnapshot);
  1470. if (ErrorCode != ERROR_SUCCESS) {
  1471. goto cleanup;
  1472. }
  1473. }
  1474. //
  1475. // Make sure the StopIdleDetection event is cleared.
  1476. //
  1477. ResetEvent(GlobalContext->StopIdleDetection);
  1478. //
  1479. // There should not be a timer-queue timer. We'll create one.
  1480. //
  1481. IT_ASSERT(!GlobalContext->IdleDetectionTimerHandle);
  1482. //
  1483. // Set the default timer period.
  1484. //
  1485. TimerPeriod = GlobalContext->Parameters.IdleDetectionPeriod;
  1486. //
  1487. // Adjust timer period to something small if we were idling when
  1488. // idle detection was stopped.
  1489. //
  1490. IT_ASSERT(ITSRV_GLOBAL_STATUS_HISTORY_SIZE >= 2);
  1491. if (GlobalContext->LastStatus[0] == ItSrvStatusStoppingIdleDetection &&
  1492. GlobalContext->LastStatus[1] == ItSrvStatusRunningIdleTasks) {
  1493. //
  1494. // Set small wake up time in ms. We'll still check if we were idle
  1495. // since the last snapshot and verify it over small periods.
  1496. //
  1497. if (TimerPeriod > (60 * 1000)) {
  1498. TimerPeriod = 60 * 1000; // 1 minute.
  1499. }
  1500. }
  1501. DBGPR((ITID,ITTRC,"IDLE: SrvStartIdleDetection(%p)-TimerPeriod=%d\n",GlobalContext,TimerPeriod));
  1502. if (!CreateTimerQueueTimer(&GlobalContext->IdleDetectionTimerHandle,
  1503. NULL,
  1504. ItSpIdleDetectionCallbackRoutine,
  1505. GlobalContext,
  1506. TimerPeriod,
  1507. IT_VERYLONG_TIMER_PERIOD,
  1508. WT_EXECUTELONGFUNCTION)) {
  1509. GlobalContext->IdleDetectionTimerHandle = NULL;
  1510. ErrorCode = GetLastError();
  1511. goto cleanup;
  1512. }
  1513. //
  1514. // We are done.
  1515. //
  1516. ErrorCode = ERROR_SUCCESS;
  1517. cleanup:
  1518. DBGPR((ITID,ITTRC,"IDLE: SrvStartIdleDetection(%p)=%x\n",GlobalContext,ErrorCode));
  1519. return ErrorCode;
  1520. }
  1521. VOID
  1522. ItSpStopIdleDetection (
  1523. PITSRV_GLOBAL_CONTEXT GlobalContext
  1524. )
  1525. /*++
  1526. Routine Description:
  1527. This function is called to stop idle detection. Even though it
  1528. returns immediately, idle detection may not have stopped
  1529. completely (i.e. the callback may be running). The
  1530. IdleDetectionStopped event on the GlobalContext will be set when
  1531. the idle detection will be fully stopped.
  1532. GlobalContext's GlobalLock should be held while calling this
  1533. function.
  1534. The status before calling this function should be set to
  1535. ItSrvStatusStoppingIdleDetection or ItSrvStatusUninitializing.
  1536. Arguments:
  1537. GlobalContext - Pointer to server context structure.
  1538. Return Value:
  1539. None.
  1540. --*/
  1541. {
  1542. DBGPR((ITID,ITTRC,"IDLE: SrvStopIdleDetection(%p)\n",GlobalContext));
  1543. //
  1544. // Make sure the status is set right.
  1545. //
  1546. IT_ASSERT(GlobalContext->Status == ItSrvStatusStoppingIdleDetection ||
  1547. GlobalContext->Status == ItSrvStatusUninitializing);
  1548. //
  1549. // Clear the event that will be set when idle detection has been
  1550. // fully stoped.
  1551. //
  1552. ResetEvent(GlobalContext->IdleDetectionStopped);
  1553. //
  1554. // Signal the event that signals the idle detection callback to go
  1555. // away asap.
  1556. //
  1557. if (GlobalContext->StopIdleDetection) {
  1558. SetEvent(GlobalContext->StopIdleDetection);
  1559. }
  1560. //
  1561. // Close the handle to the timer-queue timer.
  1562. //
  1563. IT_ASSERT(GlobalContext->IdleDetectionTimerHandle);
  1564. DeleteTimerQueueTimer(NULL,
  1565. GlobalContext->IdleDetectionTimerHandle,
  1566. GlobalContext->IdleDetectionStopped);
  1567. GlobalContext->IdleDetectionTimerHandle = NULL;
  1568. DBGPR((ITID,ITTRC,"IDLE: SrvStopIdleDetection(%p)=0\n",GlobalContext));
  1569. return;
  1570. }
  1571. PITSRV_IDLE_TASK_CONTEXT
  1572. ItSpFindRunningIdleTask (
  1573. PITSRV_GLOBAL_CONTEXT GlobalContext
  1574. )
  1575. /*++
  1576. Routine Description:
  1577. If there is an idle task marked "running" this routine finds and
  1578. returns it. GlobalContext's GlobalLock should be held while
  1579. calling this function.
  1580. Arguments:
  1581. GlobalContext - Pointer to server context structure.
  1582. Return Value:
  1583. Pointer to running idle task or NULL if no idle tasks are marked
  1584. running.
  1585. --*/
  1586. {
  1587. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1588. PITSRV_IDLE_TASK_CONTEXT RunningIdleTask;
  1589. PLIST_ENTRY HeadEntry;
  1590. PLIST_ENTRY NextEntry;
  1591. //
  1592. // Initialize locals.
  1593. //
  1594. RunningIdleTask = NULL;
  1595. HeadEntry = &GlobalContext->IdleTasksList;
  1596. NextEntry = HeadEntry->Flink;
  1597. while (NextEntry != HeadEntry) {
  1598. IdleTask = CONTAINING_RECORD(NextEntry,
  1599. ITSRV_IDLE_TASK_CONTEXT,
  1600. IdleTaskLink);
  1601. NextEntry = NextEntry->Flink;
  1602. if (IdleTask->Status == ItIdleTaskRunning) {
  1603. //
  1604. // There should be a single running task.
  1605. //
  1606. IT_ASSERT(RunningIdleTask == NULL);
  1607. //
  1608. // We found it. Loop through the remaining entries to make
  1609. // sure there is really only one if not debugging.
  1610. //
  1611. RunningIdleTask = IdleTask;
  1612. #ifndef IT_DBG
  1613. break;
  1614. #endif // !IT_DBG
  1615. }
  1616. }
  1617. //
  1618. // Fall through with RunningIdleTask found when walking the list
  1619. // or NULL as initialized at the top.
  1620. //
  1621. DBGPR((ITID,ITSRVD,"IDLE: SrvFindRunningTask(%p)=%p\n",GlobalContext,RunningIdleTask));
  1622. return RunningIdleTask;
  1623. }
  1624. PITSRV_IDLE_TASK_CONTEXT
  1625. ItSpFindIdleTask (
  1626. PITSRV_GLOBAL_CONTEXT GlobalContext,
  1627. IT_HANDLE ItHandle
  1628. )
  1629. /*++
  1630. Routine Description:
  1631. If there is an idle task registered with ItHandle, this routine
  1632. finds and returns it. GlobalContext's GlobalLock should be held
  1633. while calling this function.
  1634. Arguments:
  1635. GlobalContext - Pointer to server context structure.
  1636. ItHandle - Registration handle.
  1637. Return Value:
  1638. Pointer to found idle task or NULL if no matching idle tasks were
  1639. found.
  1640. --*/
  1641. {
  1642. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1643. PITSRV_IDLE_TASK_CONTEXT FoundIdleTask;
  1644. PLIST_ENTRY HeadEntry;
  1645. PLIST_ENTRY NextEntry;
  1646. //
  1647. // Initialize locals.
  1648. //
  1649. FoundIdleTask = NULL;
  1650. HeadEntry = &GlobalContext->IdleTasksList;
  1651. NextEntry = HeadEntry->Flink;
  1652. while (NextEntry != HeadEntry) {
  1653. IdleTask = CONTAINING_RECORD(NextEntry,
  1654. ITSRV_IDLE_TASK_CONTEXT,
  1655. IdleTaskLink);
  1656. NextEntry = NextEntry->Flink;
  1657. if ((IT_HANDLE) IdleTask == ItHandle) {
  1658. FoundIdleTask = IdleTask;
  1659. break;
  1660. }
  1661. }
  1662. //
  1663. // Fall through with FoundIdleTask found when walking the list or
  1664. // NULL as initialized at the top.
  1665. //
  1666. DBGPR((ITID,ITSRVD,"IDLE: SrvFindTask(%p,%p)=%p\n",GlobalContext,ItHandle,FoundIdleTask));
  1667. return FoundIdleTask;
  1668. }
  1669. VOID
  1670. CALLBACK
  1671. ItSpIdleDetectionCallbackRoutine (
  1672. PVOID Parameter,
  1673. BOOLEAN TimerOrWaitFired
  1674. )
  1675. /*++
  1676. Routine Description:
  1677. While there are idle tasks to run, this function is called every
  1678. IdleDetectionPeriod to determine if the system is idle. It uses
  1679. the last system snapshot saved in the global context. If it
  1680. determines that the system was idle in the time it was called, it
  1681. samples system activity for smaller intervals, to make sure system
  1682. activity that started as the (possible very long) idle detection
  1683. period expired, is not ignored.
  1684. As long as we are not told to go away (checked by the macro
  1685. ITSP_SHOULD_STOP_IDLE_DETECTION) this function always tries to
  1686. queue a timer to get called back in IdleDetectionPeriod. This
  1687. macro should be called each time the lock is acquired in this
  1688. function. Also, we should not sleep blindly when we need to let
  1689. time pass, but wait on an event that will get signaled when we are
  1690. asked to stop.
  1691. Arguments:
  1692. Parameter - Pointer to an idle detection context.
  1693. TimerOrWaitFired - Reason why we were called. This should be TRUE
  1694. (i.e. our timer expired)
  1695. Return Value:
  1696. None.
  1697. --*/
  1698. {
  1699. DWORD ErrorCode;
  1700. PITSRV_GLOBAL_CONTEXT GlobalContext;
  1701. // FUTURE-2002/03/26-ScottMa -- Adding the CurrentSystemSnapshot to the
  1702. // global context would remove the need to repeatedly initialize and
  1703. // cleanup the stack variable in the ItSpIdleDetectionCallbackRoutine.
  1704. // Since the calls to that function are already protected against
  1705. // re-entrancy issues, adding it to the global context is safe.
  1706. ITSRV_SYSTEM_SNAPSHOT CurrentSystemSnapshot;
  1707. SYSTEM_POWER_STATUS SystemPowerStatus;
  1708. LASTINPUTINFO LastUserInput;
  1709. LASTINPUTINFO CurrentLastUserInput;
  1710. BOOLEAN SystemIsIdle;
  1711. BOOLEAN AcquiredLock;
  1712. BOOLEAN MarkedIdleTaskRunning;
  1713. BOOLEAN OrderedToStop;
  1714. ULONG VerificationIdx;
  1715. DWORD WaitResult;
  1716. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1717. ULONG NumTasksRun;
  1718. ULONG DuePeriod;
  1719. BOOLEAN NotIdleBecauseOfUserInput;
  1720. BOOLEAN MisfiredCallback;
  1721. NTSTATUS Status;
  1722. SYSTEM_POWER_INFORMATION PowerInfo;
  1723. //
  1724. // Initialize locals.
  1725. //
  1726. GlobalContext = Parameter;
  1727. AcquiredLock = FALSE;
  1728. MarkedIdleTaskRunning = FALSE;
  1729. ItSpInitializeSystemSnapshot(&CurrentSystemSnapshot);
  1730. LastUserInput.cbSize = sizeof(LASTINPUTINFO);
  1731. CurrentLastUserInput.cbSize = sizeof(LASTINPUTINFO);
  1732. NumTasksRun = 0;
  1733. SystemIsIdle = FALSE;
  1734. MisfiredCallback = FALSE;
  1735. NotIdleBecauseOfUserInput = FALSE;
  1736. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback(%p)\n",GlobalContext));
  1737. //
  1738. // We should not be called without an idle detection context.
  1739. //
  1740. IT_ASSERT(GlobalContext);
  1741. //
  1742. // We should be called only because IdleDetectionPeriod passed and
  1743. // our timer expired.
  1744. //
  1745. IT_ASSERT(TimerOrWaitFired);
  1746. //
  1747. // Get the server lock.
  1748. //
  1749. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  1750. AcquiredLock = TRUE;
  1751. //
  1752. // If there is an idle detection callback already running simply
  1753. // exit without doing anything.
  1754. //
  1755. // FUTURE-2002/03/26-ScottMa -- Consider changing this method of setting
  1756. // a flag and then going to cleanup, only to skip several pages of code
  1757. // within a single conditional, with a cleaner solution. One such
  1758. // method would be to make this case "goto misfiredcallback", setting
  1759. // that label in the correct location.
  1760. if (GlobalContext->IsIdleDetectionCallbackRunning) {
  1761. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-Misfired!\n"));
  1762. MisfiredCallback = TRUE;
  1763. ErrorCode = ERROR_ALREADY_EXISTS;
  1764. goto cleanup;
  1765. }
  1766. GlobalContext->IsIdleDetectionCallbackRunning = TRUE;
  1767. //
  1768. // Make sure the current state is feasible if we are running.
  1769. //
  1770. // FUTURE-2002/03/26-ScottMa -- This assert is overactive:
  1771. // Since the status is updated to ItSrvStatusDetectingIdle AFTER the
  1772. // callback is queued via ItSpStartIdleDetection, it is possible --
  1773. // albeit extremely unlikely -- that this assert could fire prematurely.
  1774. IT_ASSERT(GlobalContext->Status == ItSrvStatusDetectingIdle ||
  1775. GlobalContext->Status == ItSrvStatusUninitializing ||
  1776. GlobalContext->Status == ItSrvStatusStoppingIdleDetection);
  1777. //
  1778. // If we are told to go away, do so.
  1779. //
  1780. if (ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext)) {
  1781. ErrorCode = ERROR_SUCCESS;
  1782. goto cleanup;
  1783. }
  1784. //
  1785. // Get initial last input time that will be used later if we
  1786. // decide to run idle tasks.
  1787. //
  1788. ErrorCode = ItSpGetLastInputInfo(&LastUserInput);
  1789. if (ErrorCode != ERROR_SUCCESS) {
  1790. goto cleanup;
  1791. }
  1792. //
  1793. // Perform idle detection over the period we've been sleeping (if
  1794. // it is not overriden.)
  1795. //
  1796. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideIdleDetection)) {
  1797. //
  1798. // Get current system snapshot.
  1799. //
  1800. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  1801. &CurrentSystemSnapshot);
  1802. if (ErrorCode != ERROR_SUCCESS) {
  1803. goto cleanup;
  1804. }
  1805. //
  1806. // See if system looks idle since the last snapshot.
  1807. //
  1808. SystemIsIdle = ItSpIsSystemIdle(GlobalContext,
  1809. &CurrentSystemSnapshot,
  1810. &GlobalContext->LastSystemSnapshot,
  1811. ItSrvInitialIdleCheck);
  1812. //
  1813. // If the last input times don't match and that's why we are not
  1814. // idle, make a note.
  1815. //
  1816. if ((CurrentSystemSnapshot.GotLastInputInfo &&
  1817. GlobalContext->LastSystemSnapshot.GotLastInputInfo) &&
  1818. (CurrentSystemSnapshot.LastInputInfo.dwTime !=
  1819. GlobalContext->LastSystemSnapshot.LastInputInfo.dwTime)) {
  1820. NotIdleBecauseOfUserInput = TRUE;
  1821. ASSERT(!SystemIsIdle);
  1822. }
  1823. //
  1824. // Save snapshot.
  1825. //
  1826. ErrorCode = ItSpCopySystemSnapshot(&GlobalContext->LastSystemSnapshot,
  1827. &CurrentSystemSnapshot);
  1828. if (ErrorCode != ERROR_SUCCESS) {
  1829. goto cleanup;
  1830. }
  1831. //
  1832. // If the system does not look idle over the detection period
  1833. // we'll poll again later.
  1834. //
  1835. if (!SystemIsIdle) {
  1836. ErrorCode = ERROR_SUCCESS;
  1837. goto cleanup;
  1838. }
  1839. }
  1840. //
  1841. // If we were not asked to override idle verification, verify that
  1842. // the system is idle for a while.
  1843. //
  1844. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideIdleVerification)) {
  1845. //
  1846. // Loop for a while getting system snapshots over shorter
  1847. // durations. This helps us catch recent significant system
  1848. // activity that seemed insignificant when viewed over the whole
  1849. // IdleDetectionPeriod.
  1850. //
  1851. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-Verifying\n"));
  1852. for (VerificationIdx = 0;
  1853. VerificationIdx < GlobalContext->Parameters.NumVerifications;
  1854. VerificationIdx ++) {
  1855. // FUTURE-2002/03/26-ScottMa -- The following block of code is
  1856. // almost 100% identical to the verification code that occurs
  1857. // below [search for identical]. Consider breaking it into
  1858. // a function for better maintainability and readability.
  1859. //
  1860. // Release the lock.
  1861. //
  1862. IT_ASSERT(AcquiredLock);
  1863. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  1864. AcquiredLock = FALSE;
  1865. //
  1866. // Sleep for the verification period.
  1867. //
  1868. WaitResult = WaitForSingleObject(GlobalContext->StopIdleDetection,
  1869. GlobalContext->Parameters.IdleVerificationPeriod);
  1870. if (WaitResult != WAIT_TIMEOUT) {
  1871. if (WaitResult == WAIT_OBJECT_0) {
  1872. ErrorCode = ERROR_SUCCESS;
  1873. } else {
  1874. ErrorCode = GetLastError();
  1875. }
  1876. goto cleanup;
  1877. }
  1878. //
  1879. // Acquire the lock.
  1880. //
  1881. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  1882. AcquiredLock = TRUE;
  1883. //
  1884. // Are we told to go away (this may happen from the time the
  1885. // wait returns till we acquire the lock.)
  1886. //
  1887. if (ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext)) {
  1888. ErrorCode = ERROR_SUCCESS;
  1889. goto cleanup;
  1890. }
  1891. //
  1892. // Get the new snapshot.
  1893. //
  1894. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  1895. &CurrentSystemSnapshot);
  1896. if (ErrorCode != ERROR_SUCCESS) {
  1897. goto cleanup;
  1898. }
  1899. //
  1900. // See if system looks idle since the last snapshot.
  1901. //
  1902. SystemIsIdle = ItSpIsSystemIdle(GlobalContext,
  1903. &CurrentSystemSnapshot,
  1904. &GlobalContext->LastSystemSnapshot,
  1905. ItSrvIdleVerificationCheck);
  1906. //
  1907. // Save snapshot.
  1908. //
  1909. ErrorCode = ItSpCopySystemSnapshot(&GlobalContext->LastSystemSnapshot,
  1910. &CurrentSystemSnapshot);
  1911. if (ErrorCode != ERROR_SUCCESS) {
  1912. goto cleanup;
  1913. }
  1914. //
  1915. // If the system was not idle over the detection period we'll
  1916. // try again later.
  1917. //
  1918. if (!SystemIsIdle) {
  1919. ErrorCode = ERROR_SUCCESS;
  1920. goto cleanup;
  1921. }
  1922. }
  1923. }
  1924. //
  1925. // The system has become idle. Update the status.
  1926. //
  1927. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-RunningIdleTasks\n"));
  1928. IT_ASSERT(GlobalContext->Status == ItSrvStatusDetectingIdle);
  1929. ItSpUpdateStatus(GlobalContext, ItSrvStatusRunningIdleTasks);
  1930. //
  1931. // While we are not told to go away...
  1932. //
  1933. while (!ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext)) {
  1934. //
  1935. // We should be holding the lock when we are making the above
  1936. // check and whenever we come here.
  1937. //
  1938. IT_ASSERT(AcquiredLock);
  1939. //
  1940. // The list should not be empty.
  1941. //
  1942. IT_ASSERT(!IsListEmpty(&GlobalContext->IdleTasksList));
  1943. if (IsListEmpty(&GlobalContext->IdleTasksList)) {
  1944. ErrorCode = ERROR_INVALID_FUNCTION;
  1945. goto cleanup;
  1946. }
  1947. //
  1948. // Mark the first idle task in the list running and signal its
  1949. // event.
  1950. //
  1951. IdleTask = CONTAINING_RECORD(GlobalContext->IdleTasksList.Flink,
  1952. ITSRV_IDLE_TASK_CONTEXT,
  1953. IdleTaskLink);
  1954. //
  1955. // It should not be uninitialized or already running!
  1956. //
  1957. IT_ASSERT(IdleTask->Status == ItIdleTaskQueued);
  1958. IdleTask->Status = ItIdleTaskRunning;
  1959. MarkedIdleTaskRunning = TRUE;
  1960. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-Running %d\n",IdleTask->Properties.IdleTaskId));
  1961. NumTasksRun++;
  1962. //
  1963. // Signal its events.
  1964. //
  1965. ResetEvent(IdleTask->StopEvent);
  1966. SetEvent(IdleTask->StartEvent);
  1967. //
  1968. // Reset the event that will get set when the idle task we
  1969. // mark running gets unregistered.
  1970. //
  1971. ResetEvent(GlobalContext->RemovedRunningIdleTask);
  1972. //
  1973. // Release the lock.
  1974. //
  1975. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  1976. AcquiredLock = FALSE;
  1977. //
  1978. // Poll frequently for user input while system background
  1979. // activity should be taking place. We cannot poll for
  1980. // anything else because the running idle task is supposed to
  1981. // be using CPU, issuing I/Os etc. As soon as user input comes
  1982. // we want to signal background threads to stop their
  1983. // activity. We will do this until the running idle task is
  1984. // completed and unregistered.
  1985. //
  1986. do {
  1987. //
  1988. // We should not be holding the lock while polling.
  1989. //
  1990. IT_ASSERT(!AcquiredLock);
  1991. //
  1992. // Note that since we set MarkedIdleTaskRunning, going to
  1993. // "cleanup" will end up marking this idle task not
  1994. // running and setting the stop event.
  1995. //
  1996. // FUTURE-2002/03/26-ScottMa -- The following block of code and
  1997. // the related block that occurs after the wait [search for
  1998. // related] should be converted into a single call to the
  1999. // ItIsSystemIdle function to reduce code duplication and
  2000. // improve both maintainability and readability.
  2001. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideUserInputCheckToStopTask)) {
  2002. //
  2003. // Get last user input.
  2004. //
  2005. ErrorCode = ItSpGetLastInputInfo(&CurrentLastUserInput);
  2006. if (ErrorCode != ERROR_SUCCESS) {
  2007. goto cleanup;
  2008. }
  2009. if (LastUserInput.dwTime != CurrentLastUserInput.dwTime) {
  2010. //
  2011. // There is new input.
  2012. //
  2013. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-NewUserInput\n"));
  2014. SystemIsIdle = FALSE;
  2015. ErrorCode = ERROR_SUCCESS;
  2016. goto cleanup;
  2017. }
  2018. //
  2019. // We don't need to update last input since it should
  2020. // be same as current.
  2021. //
  2022. }
  2023. //
  2024. // Wait for a while to poll for user input again. We
  2025. // should not be holding the lock while waiting.
  2026. //
  2027. IT_ASSERT(!AcquiredLock);
  2028. WaitResult = WaitForSingleObject(GlobalContext->RemovedRunningIdleTask,
  2029. GlobalContext->Parameters.IdleInputCheckPeriod);
  2030. if (WaitResult == WAIT_OBJECT_0) {
  2031. //
  2032. // Break out of this loop to pick up a new idle
  2033. // task to run.
  2034. //
  2035. MarkedIdleTaskRunning = FALSE;
  2036. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-TaskRemoved\n"));
  2037. break;
  2038. }
  2039. if (WaitResult != WAIT_TIMEOUT) {
  2040. //
  2041. // Something went wrong...
  2042. //
  2043. ErrorCode = ERROR_INVALID_FUNCTION;
  2044. goto cleanup;
  2045. }
  2046. // FUTURE-2002/03/26-ScottMa -- The following block of code and
  2047. // the related block that occurs before the wait [search for
  2048. // related] should be converted into a single call to the
  2049. // ItIsSystemIdle function to reduce code duplication and
  2050. // improve both maintainability and readability.
  2051. //
  2052. // Check to see if the system has started running from battery.
  2053. //
  2054. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideBatteryCheckToStopTask)) {
  2055. if (GetSystemPowerStatus(&SystemPowerStatus)) {
  2056. if (SystemPowerStatus.ACLineStatus == 0) {
  2057. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-SystemOnBattery\n"));
  2058. SystemIsIdle = FALSE;
  2059. ErrorCode = ERROR_SUCCESS;
  2060. goto cleanup;
  2061. }
  2062. }
  2063. }
  2064. //
  2065. // If the kernel is about to enter standby or hibernate because
  2066. // it has also detected the system idle, stop this task.
  2067. //
  2068. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideAutoPowerCheckToStopTask)) {
  2069. Status = NtPowerInformation(SystemPowerInformation,
  2070. NULL,
  2071. 0,
  2072. &PowerInfo,
  2073. sizeof(PowerInfo));
  2074. if (NT_SUCCESS(Status)) {
  2075. if (PowerInfo.TimeRemaining < IT_DEFAULT_MAX_TIME_REMAINING_TO_SLEEP) {
  2076. SystemIsIdle = FALSE;
  2077. ErrorCode = ERROR_SUCCESS;
  2078. goto cleanup;
  2079. }
  2080. }
  2081. }
  2082. //
  2083. // Are we stopping the service?
  2084. //
  2085. if (ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext)) {
  2086. SystemIsIdle = TRUE;
  2087. ErrorCode = ERROR_SUCCESS;
  2088. goto cleanup;
  2089. }
  2090. //
  2091. // The idle task is still running. Loop to check for user
  2092. // input.
  2093. //
  2094. } while (TRUE);
  2095. // FUTURE-2002/03/26-ScottMa -- This conditional is a tautology:
  2096. // The lock could not have been acquired through any codepath
  2097. // ending here, it should be an assert instead.
  2098. if (!AcquiredLock) {
  2099. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  2100. AcquiredLock = TRUE;
  2101. }
  2102. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverridePostTaskIdleCheck)) {
  2103. //
  2104. // Get the latest snapshot of the system. This snapshot will
  2105. // be used to determine if the system is still idle before
  2106. // picking up a new task.
  2107. //
  2108. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  2109. &GlobalContext->LastSystemSnapshot);
  2110. if (ErrorCode != ERROR_SUCCESS) {
  2111. goto cleanup;
  2112. }
  2113. // FUTURE-2002/03/26-ScottMa -- The following block of code is
  2114. // almost 100% identical to the verification code that occurs
  2115. // above [search for identical]. Consider breaking it into
  2116. // a function for better maintainability and readability.
  2117. //
  2118. // Release the lock.
  2119. //
  2120. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  2121. AcquiredLock = FALSE;
  2122. //
  2123. // Wait for the verification period.
  2124. //
  2125. WaitResult = WaitForSingleObject(GlobalContext->StopIdleDetection,
  2126. GlobalContext->Parameters.IdleVerificationPeriod);
  2127. if (WaitResult != WAIT_TIMEOUT) {
  2128. if (WaitResult == WAIT_OBJECT_0) {
  2129. ErrorCode = ERROR_SUCCESS;
  2130. } else {
  2131. ErrorCode = GetLastError();
  2132. }
  2133. goto cleanup;
  2134. }
  2135. //
  2136. // Acquire the lock and get new snapshot.
  2137. //
  2138. IT_ASSERT(!AcquiredLock);
  2139. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  2140. AcquiredLock = TRUE;
  2141. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  2142. &CurrentSystemSnapshot);
  2143. if (ErrorCode != ERROR_SUCCESS) {
  2144. goto cleanup;
  2145. }
  2146. //
  2147. // See if system looks idle since the last snapshot.
  2148. //
  2149. SystemIsIdle = ItSpIsSystemIdle(GlobalContext,
  2150. &CurrentSystemSnapshot,
  2151. &GlobalContext->LastSystemSnapshot,
  2152. ItSrvIdleVerificationCheck);
  2153. //
  2154. // Save snapshot.
  2155. //
  2156. ErrorCode = ItSpCopySystemSnapshot(&GlobalContext->LastSystemSnapshot,
  2157. &CurrentSystemSnapshot);
  2158. if (ErrorCode != ERROR_SUCCESS) {
  2159. goto cleanup;
  2160. }
  2161. //
  2162. // If the system is no longer idle, we should not start a new task.
  2163. //
  2164. if (!SystemIsIdle) {
  2165. ErrorCode = ERROR_SUCCESS;
  2166. goto cleanup;
  2167. }
  2168. }
  2169. //
  2170. // Loop to try to run more idle tasks. The lock should be acquired.
  2171. //
  2172. IT_ASSERT(AcquiredLock);
  2173. }
  2174. //
  2175. // We should come here only if we were asked to stop, i.e. the
  2176. // check in while() causes us to break from looping.
  2177. //
  2178. IT_ASSERT(AcquiredLock);
  2179. IT_ASSERT(ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext));
  2180. cleanup:
  2181. //
  2182. // Simply cleanup and exit if this is a misfired callback.
  2183. //
  2184. if (!MisfiredCallback) {
  2185. //
  2186. // We'll have to check status to see if we have to requeue
  2187. // ourselves. Make sure we have the lock.
  2188. //
  2189. if (AcquiredLock == FALSE) {
  2190. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  2191. AcquiredLock = TRUE;
  2192. }
  2193. //
  2194. // If we marked an idle task running, make sure we reset its state
  2195. // back to queued.
  2196. //
  2197. if (MarkedIdleTaskRunning) {
  2198. //
  2199. // We may have gone to cleanup after the idle task we were
  2200. // running was removed, but before we realized it. See if
  2201. // the running idle task was removed. We are not waiting,
  2202. // we are just checking if the event has been signaled.
  2203. //
  2204. WaitResult = WaitForSingleObject(GlobalContext->RemovedRunningIdleTask,
  2205. 0);
  2206. if (WaitResult != WAIT_OBJECT_0) {
  2207. //
  2208. // The running idle was not removed. Reset its state.
  2209. //
  2210. IdleTask = ItSpFindRunningIdleTask(GlobalContext);
  2211. //
  2212. // To be safe, we try to cleanup even if the above
  2213. // check fails with another result. We don't want the
  2214. // assert to fire then, but only if the event is
  2215. // really not set.
  2216. //
  2217. if (WaitResult == WAIT_TIMEOUT) {
  2218. IT_ASSERT(IdleTask);
  2219. }
  2220. if (IdleTask) {
  2221. ResetEvent(IdleTask->StartEvent);
  2222. SetEvent(IdleTask->StopEvent);
  2223. IdleTask->Status = ItIdleTaskQueued;
  2224. //
  2225. // Put this task to the end of the list. If a single task
  2226. // is taking too long to run, this gives more chance to other
  2227. // tasks.
  2228. //
  2229. RemoveEntryList(&IdleTask->IdleTaskLink);
  2230. InsertTailList(&GlobalContext->IdleTasksList, &IdleTask->IdleTaskLink);
  2231. }
  2232. }
  2233. }
  2234. //
  2235. // If we set the status to running idle tasks, revert it to
  2236. // detecting idle.
  2237. //
  2238. if (GlobalContext->Status == ItSrvStatusRunningIdleTasks) {
  2239. ItSpUpdateStatus(GlobalContext, ItSrvStatusDetectingIdle);
  2240. }
  2241. //
  2242. // Queue ourselves to fire up after another idle detection
  2243. // period. We'll try every once a while until we get it or we
  2244. // are ordered to stop.
  2245. //
  2246. // ISSUE-2002/03/26-ScottMa -- This should not be a loop. Looking
  2247. // at the codepath, it appears as though it will be broken out of
  2248. // as soon as the timer is requeued. Otherwise, it appears that
  2249. // bad things might happen. The IsIdleDetectionCallbackRunning
  2250. // flag should be set to FALSE here, before trying to requeue
  2251. // the timer, so that a new call can progress. Further, the
  2252. // OrderedToStop variable should be moved to an else clause
  2253. // (assuming that the below while is converted to an if).
  2254. while (!ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext)) {
  2255. IT_ASSERT(GlobalContext->IdleDetectionTimerHandle);
  2256. DuePeriod = GlobalContext->Parameters.IdleDetectionPeriod;
  2257. //
  2258. // Try to detect idle quicker for the case when the last user
  2259. // input was just seconds after the last snapshot. In that case
  2260. // instead of waiting for another full "DetectionPeriod", we'll
  2261. // wait up to "DetectionPeriod" after the last user input. Note
  2262. // that we'll attempt this optimization only if the reason we
  2263. // say the system is not idle is recent user input. E.g. We don't
  2264. // want to poll more often if we are on battery and that's why
  2265. // we say that the system is not idle.
  2266. //
  2267. // ISSUE-2002/03/26-ScottMa -- This test will catch any time that
  2268. // the system was marked as not idle EVEN if it is also on
  2269. // battery or had some OTHER reason for being considered idle.
  2270. // If this codepath is taken out of the loop, the cost of
  2271. // doing this adjustment once is probably acceptable to perform
  2272. // *any* time the input changed, ignoring the NotIdleBecause...
  2273. // flag altogether, and just checking LastInputInfo.
  2274. if (NotIdleBecauseOfUserInput &&
  2275. (ERROR_SUCCESS == ItSpGetLastInputInfo(&LastUserInput))) {
  2276. ULONG DuePeriod2;
  2277. ULONG TimeSinceLastInput;
  2278. //
  2279. // Calculate how long it's been since last user input.
  2280. //
  2281. TimeSinceLastInput = GetTickCount() - LastUserInput.dwTime;
  2282. //
  2283. // Subtract this time from the idle detection period to account
  2284. // for time that has already past since last input.
  2285. //
  2286. DuePeriod2 = 0;
  2287. if (TimeSinceLastInput < GlobalContext->Parameters.IdleDetectionPeriod) {
  2288. DuePeriod2 = GlobalContext->Parameters.IdleDetectionPeriod - TimeSinceLastInput;
  2289. }
  2290. //
  2291. // The last user input we check gets updated only every so
  2292. // often (e.g. every minute). Add a fudge factor for this and to
  2293. // protect us from scheduling the next idle check too soon.
  2294. //
  2295. #ifdef IT_DBG
  2296. if (ItSrvGlobalContext->Parameters.IdleDetectionPeriod >= 60*1000) {
  2297. #endif // IT_DBG
  2298. DuePeriod2 += 65 * 1000;
  2299. #ifdef IT_DBG
  2300. }
  2301. #endif // IT_DBG
  2302. if (DuePeriod > DuePeriod2) {
  2303. DuePeriod = DuePeriod2;
  2304. }
  2305. }
  2306. //
  2307. // If we are forcing all tasks to be processed, requeue ourself to
  2308. // run again in a short time.
  2309. //
  2310. if (GlobalContext->IdleDetectionOverride & ItSrvOverrideLongRequeueTime) {
  2311. DuePeriod = 50;
  2312. }
  2313. if (ChangeTimerQueueTimer(NULL,
  2314. GlobalContext->IdleDetectionTimerHandle,
  2315. DuePeriod,
  2316. IT_VERYLONG_TIMER_PERIOD)) {
  2317. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-Requeued: DuePeriod=%d\n", DuePeriod));
  2318. break;
  2319. }
  2320. //
  2321. // Release the lock.
  2322. //
  2323. IT_ASSERT(AcquiredLock);
  2324. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  2325. AcquiredLock = FALSE;
  2326. //
  2327. // Sleep for sometime and try again.
  2328. //
  2329. WaitResult = WaitForSingleObject(GlobalContext->StopIdleDetection,
  2330. GlobalContext->Parameters.IdleDetectionPeriod);
  2331. //
  2332. // Get the lock again.
  2333. //
  2334. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  2335. AcquiredLock = TRUE;
  2336. //
  2337. // Now check the result of the wait.
  2338. //
  2339. if (WaitResult != WAIT_OBJECT_0 &&
  2340. WaitResult != WAIT_TIMEOUT) {
  2341. //
  2342. // This is an error too! The world is going down on us,
  2343. // let us get carried away... This will make it easier for
  2344. // the server to shutdown (i.e. no callback running).
  2345. //
  2346. break;
  2347. }
  2348. }
  2349. IT_ASSERT(AcquiredLock);
  2350. //
  2351. // Check if we were ordered to stop.
  2352. //
  2353. OrderedToStop = ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext);
  2354. //
  2355. // Mark us not running anymore.
  2356. //
  2357. GlobalContext->IsIdleDetectionCallbackRunning = FALSE;
  2358. }
  2359. //
  2360. // Release the lock if we are holding it.
  2361. //
  2362. if (AcquiredLock) {
  2363. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  2364. }
  2365. //
  2366. // Cleanup intermediate snapshot structure if necessary.
  2367. //
  2368. ItSpCleanupSystemSnapshot(&CurrentSystemSnapshot);
  2369. DBGPR((ITID,ITSRVD,"IDLE: SrvIdleDetectionCallback(%p)=%d,%d,%d,%d\n",
  2370. GlobalContext,ErrorCode,OrderedToStop,SystemIsIdle,NumTasksRun));
  2371. return;
  2372. }
  2373. VOID
  2374. ItSpInitializeSystemSnapshot (
  2375. PITSRV_SYSTEM_SNAPSHOT SystemSnapshot
  2376. )
  2377. /*++
  2378. Routine Description:
  2379. This routine initializes a system snapshot structure.
  2380. Arguments:
  2381. SystemSnapshot - Pointer to structure.
  2382. Return Value:
  2383. None.
  2384. --*/
  2385. {
  2386. //
  2387. // Initialize the disk performance data array.
  2388. //
  2389. SystemSnapshot->NumPhysicalDisks = 0;
  2390. SystemSnapshot->DiskPerfData = NULL;
  2391. //
  2392. // We don't have any valid data.
  2393. //
  2394. SystemSnapshot->GotLastInputInfo = 0;
  2395. SystemSnapshot->GotSystemPerformanceInfo = 0;
  2396. SystemSnapshot->GotDiskPerformanceInfo = 0;
  2397. SystemSnapshot->GotSystemPowerStatus = 0;
  2398. SystemSnapshot->GotSystemPowerInfo = 0;
  2399. SystemSnapshot->GotSystemExecutionState = 0;
  2400. SystemSnapshot->GotDisplayPowerStatus = 0;
  2401. SystemSnapshot->SnapshotTime = -1;
  2402. }
  2403. VOID
  2404. ItSpCleanupSystemSnapshot (
  2405. PITSRV_SYSTEM_SNAPSHOT SystemSnapshot
  2406. )
  2407. /*++
  2408. Routine Description:
  2409. This routine cleans up fields of a system snapshot structure. It
  2410. does not free the structure itself. The structure should have been
  2411. initialized with a call to ItSpCleanupSystemSnapshot.
  2412. Arguments:
  2413. SystemSnapshot - Pointer to structure.
  2414. Return Value:
  2415. None.
  2416. --*/
  2417. {
  2418. //
  2419. // If a disk performance data array is allocated free it.
  2420. //
  2421. if (SystemSnapshot->DiskPerfData) {
  2422. IT_ASSERT(SystemSnapshot->NumPhysicalDisks);
  2423. IT_FREE(SystemSnapshot->DiskPerfData);
  2424. }
  2425. }
  2426. DWORD
  2427. ItSpCopySystemSnapshot (
  2428. PITSRV_SYSTEM_SNAPSHOT DestSnapshot,
  2429. PITSRV_SYSTEM_SNAPSHOT SourceSnapshot
  2430. )
  2431. /*++
  2432. Routine Description:
  2433. This routine attempts to copy SourceSnapshot to DestSnapshot. If
  2434. the copy fails, DestSnapshot remains intact.
  2435. Arguments:
  2436. DestSnapshot - Pointer to snapshot to be updated.
  2437. SourceSnapshot - Pointer to snapshot to copy.
  2438. Return Value:
  2439. Win32 error code.
  2440. --*/
  2441. {
  2442. DWORD ErrorCode;
  2443. ULONG OrgNumDisks;
  2444. PITSRV_DISK_PERFORMANCE_DATA OrgDiskPerfData;
  2445. PITSRV_DISK_PERFORMANCE_DATA NewDiskPerfData;
  2446. ULONG AllocationSize;
  2447. ULONG CopySize;
  2448. //
  2449. // Initialize locals.
  2450. //
  2451. NewDiskPerfData = NULL;
  2452. //
  2453. // Do we have to copy disk performance data?
  2454. //
  2455. if (SourceSnapshot->GotDiskPerformanceInfo) {
  2456. //
  2457. // Allocate a new array if the disk performance data won't fit.
  2458. //
  2459. if (SourceSnapshot->NumPhysicalDisks > DestSnapshot->NumPhysicalDisks) {
  2460. AllocationSize = SourceSnapshot->NumPhysicalDisks *
  2461. sizeof(ITSRV_DISK_PERFORMANCE_DATA);
  2462. NewDiskPerfData = IT_ALLOC(AllocationSize);
  2463. if (!NewDiskPerfData) {
  2464. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  2465. goto cleanup;
  2466. }
  2467. }
  2468. }
  2469. //
  2470. // Beyond this point we should not fail because we modify
  2471. // DestSnapshot.
  2472. //
  2473. //
  2474. // Save original disk performance data array.
  2475. //
  2476. OrgDiskPerfData = DestSnapshot->DiskPerfData;
  2477. OrgNumDisks = DestSnapshot->NumPhysicalDisks;
  2478. //
  2479. // Copy the whole structure over and put back original disk
  2480. // performance data array.
  2481. //
  2482. RtlCopyMemory(DestSnapshot,
  2483. SourceSnapshot,
  2484. sizeof(ITSRV_SYSTEM_SNAPSHOT));
  2485. DestSnapshot->DiskPerfData = OrgDiskPerfData;
  2486. DestSnapshot->NumPhysicalDisks = OrgNumDisks;
  2487. //
  2488. // Determine if/how we will copy over disk performance data.
  2489. //
  2490. if (SourceSnapshot->GotDiskPerformanceInfo) {
  2491. if (SourceSnapshot->NumPhysicalDisks > DestSnapshot->NumPhysicalDisks) {
  2492. //
  2493. // Free old array and use the new one we allocated above.
  2494. //
  2495. if (DestSnapshot->DiskPerfData) {
  2496. IT_FREE(DestSnapshot->DiskPerfData);
  2497. }
  2498. DestSnapshot->DiskPerfData = NewDiskPerfData;
  2499. NewDiskPerfData = NULL;
  2500. }
  2501. if (SourceSnapshot->NumPhysicalDisks == 0) {
  2502. //
  2503. // This does not make sense... (They got disk data and
  2504. // there are 0 physical disks in the system?) Yet we go
  2505. // with it and to be consistent, free our data array.
  2506. //
  2507. if (DestSnapshot->DiskPerfData) {
  2508. IT_FREE(DestSnapshot->DiskPerfData);
  2509. }
  2510. DestSnapshot->DiskPerfData = NULL;
  2511. } else {
  2512. //
  2513. // Copy over their disk data and update NumPhysicalDisks.
  2514. //
  2515. CopySize = SourceSnapshot->NumPhysicalDisks *
  2516. sizeof(ITSRV_DISK_PERFORMANCE_DATA);
  2517. RtlCopyMemory(DestSnapshot->DiskPerfData,
  2518. SourceSnapshot->DiskPerfData,
  2519. CopySize);
  2520. }
  2521. DestSnapshot->NumPhysicalDisks = SourceSnapshot->NumPhysicalDisks;
  2522. }
  2523. //
  2524. // Done.
  2525. //
  2526. ErrorCode = ERROR_SUCCESS;
  2527. cleanup:
  2528. if (NewDiskPerfData) {
  2529. IT_FREE(NewDiskPerfData);
  2530. }
  2531. DBGPR((ITID,ITSRVDD,"IDLE: SrvCopySnapshot()=%d\n",ErrorCode));
  2532. return ErrorCode;
  2533. }
  2534. DWORD
  2535. ItSpGetSystemSnapshot (
  2536. PITSRV_GLOBAL_CONTEXT GlobalContext,
  2537. PITSRV_SYSTEM_SNAPSHOT SystemSnapshot
  2538. )
  2539. /*++
  2540. Routine Description:
  2541. This routine fills input system snapshot with data queried from
  2542. various sources. The snapshot structure should have been
  2543. initialized by ItSpInitializeSystemSnapshot. The output
  2544. SystemSnapshot can be passed back in.
  2545. Arguments:
  2546. GlobalContext - Pointer to idle detection context.
  2547. SystemSnapshot - Pointer to structure to fill.
  2548. Return Value:
  2549. Win32 error code.
  2550. --*/
  2551. {
  2552. DWORD ErrorCode;
  2553. NTSTATUS Status;
  2554. //
  2555. // Query disk performance counters.
  2556. //
  2557. if (GlobalContext->DiskPerfWmiHandle) {
  2558. ErrorCode = ItSpGetWmiDiskPerformanceData(GlobalContext->DiskPerfWmiHandle,
  2559. &SystemSnapshot->DiskPerfData,
  2560. &SystemSnapshot->NumPhysicalDisks,
  2561. &GlobalContext->WmiQueryBuffer,
  2562. &GlobalContext->WmiQueryBufferSize);
  2563. if (ErrorCode == ERROR_SUCCESS) {
  2564. SystemSnapshot->GotDiskPerformanceInfo = TRUE;
  2565. } else {
  2566. SystemSnapshot->GotDiskPerformanceInfo = FALSE;
  2567. }
  2568. } else {
  2569. SystemSnapshot->GotDiskPerformanceInfo = FALSE;
  2570. }
  2571. //
  2572. // Get system performance information.
  2573. //
  2574. Status = NtQuerySystemInformation(SystemPerformanceInformation,
  2575. &SystemSnapshot->SystemPerformanceInfo,
  2576. sizeof(SYSTEM_PERFORMANCE_INFORMATION),
  2577. NULL);
  2578. if (NT_SUCCESS(Status)) {
  2579. SystemSnapshot->GotSystemPerformanceInfo = TRUE;
  2580. } else {
  2581. SystemSnapshot->GotSystemPerformanceInfo = FALSE;
  2582. }
  2583. //
  2584. // Get last input time.
  2585. //
  2586. SystemSnapshot->LastInputInfo.cbSize = sizeof(LASTINPUTINFO);
  2587. ErrorCode = ItSpGetLastInputInfo(&SystemSnapshot->LastInputInfo);
  2588. if (ErrorCode == ERROR_SUCCESS) {
  2589. SystemSnapshot->GotLastInputInfo = TRUE;
  2590. } else {
  2591. SystemSnapshot->GotLastInputInfo = FALSE;
  2592. }
  2593. //
  2594. // Get system power status to determine if we are running on
  2595. // battery etc.
  2596. //
  2597. if (GetSystemPowerStatus(&SystemSnapshot->SystemPowerStatus)) {
  2598. SystemSnapshot->GotSystemPowerStatus = TRUE;
  2599. } else {
  2600. SystemSnapshot->GotSystemPowerStatus = FALSE;
  2601. }
  2602. //
  2603. // Get system power information to see if the system is close to
  2604. // entering standby or hibernate automatically.
  2605. //
  2606. Status = NtPowerInformation(SystemPowerInformation,
  2607. NULL,
  2608. 0,
  2609. &SystemSnapshot->PowerInfo,
  2610. sizeof(SYSTEM_POWER_INFORMATION));
  2611. if (NT_SUCCESS(Status)) {
  2612. SystemSnapshot->GotSystemPowerInfo = TRUE;
  2613. } else {
  2614. SystemSnapshot->GotSystemPowerInfo = FALSE;
  2615. }
  2616. //
  2617. // Get system execution state to determine if somebody's running a
  2618. // presentation, burning a cd etc.
  2619. //
  2620. Status = NtPowerInformation(SystemExecutionState,
  2621. NULL,
  2622. 0,
  2623. &SystemSnapshot->ExecutionState,
  2624. sizeof(EXECUTION_STATE));
  2625. if (NT_SUCCESS(Status)) {
  2626. SystemSnapshot->GotSystemExecutionState = TRUE;
  2627. } else {
  2628. SystemSnapshot->GotSystemExecutionState = FALSE;
  2629. }
  2630. //
  2631. // Get display power status.
  2632. //
  2633. ErrorCode = ItSpGetDisplayPowerStatus(&SystemSnapshot->ScreenSaverIsRunning);
  2634. if (ErrorCode == ERROR_SUCCESS) {
  2635. SystemSnapshot->GotDisplayPowerStatus = TRUE;
  2636. } else {
  2637. SystemSnapshot->GotDisplayPowerStatus = FALSE;
  2638. }
  2639. //
  2640. // Fill in the time when this snapshot was taken as the last thing
  2641. // to be conservative. This function may have taken long, and the
  2642. // values we snapshoted may have changed by now.
  2643. //
  2644. SystemSnapshot->SnapshotTime = GetTickCount();
  2645. DBGPR((ITID,ITSRVDD,"IDLE: SrvGetSnapshot()=%d,%d,%d\n",
  2646. (ULONG) SystemSnapshot->GotLastInputInfo,
  2647. (ULONG) SystemSnapshot->GotSystemPerformanceInfo,
  2648. (ULONG) SystemSnapshot->GotDiskPerformanceInfo));
  2649. return ERROR_SUCCESS;
  2650. }
  2651. // FUTURE-2002/03/26-ScottMa -- If the CurrentSystemSnapshot is added to the
  2652. // global context, both parameters no longer need to be passed to this
  2653. // function. It is only called from within ItSpIdleDetectionCallbackRoutine,
  2654. // and always uses the same values for current & last snapshot.
  2655. BOOLEAN
  2656. ItSpIsSystemIdle (
  2657. PITSRV_GLOBAL_CONTEXT GlobalContext,
  2658. PITSRV_SYSTEM_SNAPSHOT CurrentSnapshot,
  2659. PITSRV_SYSTEM_SNAPSHOT LastSnapshot,
  2660. ITSRV_IDLE_CHECK_REASON IdleCheckReason
  2661. )
  2662. /*++
  2663. Routine Description:
  2664. This routine compares two snapshots to determine if the system has
  2665. been idle between them.
  2666. This function acts very conservatively in saying that the system
  2667. is idle.
  2668. Arguments:
  2669. GlobalContext - Pointer to server context structure.
  2670. CurrentSnapshot - Pointer to system snapshot.
  2671. LastSnapshot - Pointer to system snapshot taken before
  2672. CurrentSnapshot.
  2673. IdleCheckReason - Where this function is being called from. We may
  2674. do things differently when we get called to do the initial check
  2675. to see if the system idle, or to verify it is really idle, or to
  2676. make sure the idle task we started is still running and is not
  2677. stuck.
  2678. Return Value:
  2679. TRUE - System was idle between the two snapshots.
  2680. FALSE - The system was not idle between two snapshots.
  2681. --*/
  2682. {
  2683. DWORD SnapshotTimeDifference;
  2684. BOOLEAN SystemIsIdle;
  2685. LARGE_INTEGER IdleProcessRunTime;
  2686. ULONG CpuIdlePercentage;
  2687. ULONG DiskIdx;
  2688. ULONG DiskIdleTimeDifference;
  2689. ULONG DiskIdlePercentage;
  2690. PIT_IDLE_DETECTION_PARAMETERS Parameters;
  2691. //
  2692. // Initialize locals.
  2693. //
  2694. Parameters = &GlobalContext->Parameters;
  2695. SystemIsIdle = FALSE;
  2696. SnapshotTimeDifference = CurrentSnapshot->SnapshotTime -
  2697. LastSnapshot->SnapshotTime;
  2698. //
  2699. // Verify parameters.
  2700. //
  2701. IT_ASSERT(IdleCheckReason < ItSrvMaxIdleCheckReason);
  2702. //
  2703. // If system tick count wrapped or they are passing in bogus
  2704. // times, or the snapshots seem to be taken nearly at the same
  2705. // time, say the system is not idle to avoid any weird issues.
  2706. //
  2707. if (CurrentSnapshot->SnapshotTime <= LastSnapshot->SnapshotTime) {
  2708. goto cleanup;
  2709. }
  2710. IT_ASSERT(SnapshotTimeDifference);
  2711. //
  2712. // If either snapshot does not have last user input (mouse or
  2713. // keyboard) info, we cannot reliably say the system was idle.
  2714. //
  2715. if (!CurrentSnapshot->GotLastInputInfo ||
  2716. !LastSnapshot->GotLastInputInfo) {
  2717. goto cleanup;
  2718. }
  2719. //
  2720. // If there has been user input between the two snapshots, the
  2721. // system was not idle. We don't care when the user input
  2722. // happened (e.g. right after the last snapshot).
  2723. //
  2724. DBGPR((ITID,ITSNAP,"IDLE: UserInput: Last %u Current %u\n",
  2725. LastSnapshot->LastInputInfo.dwTime,
  2726. CurrentSnapshot->LastInputInfo.dwTime));
  2727. if (LastSnapshot->LastInputInfo.dwTime !=
  2728. CurrentSnapshot->LastInputInfo.dwTime) {
  2729. goto cleanup;
  2730. }
  2731. //
  2732. // If we are running on battery, don't run idle tasks.
  2733. //
  2734. if (CurrentSnapshot->GotSystemPowerStatus) {
  2735. if (CurrentSnapshot->SystemPowerStatus.ACLineStatus == 0) {
  2736. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: Running on battery\n"));
  2737. goto cleanup;
  2738. }
  2739. }
  2740. //
  2741. // If system will automatically enter standby or hibernate soon
  2742. // it is too late for us to run our tasks.
  2743. //
  2744. if (CurrentSnapshot->GotSystemPowerInfo) {
  2745. // FUTURE-2002/03/26-ScottMa -- This constant doesn't have a
  2746. // corresponding parameter in the IT_IDLE_DETECTION_PARAMETERS
  2747. // structure. Should it be added to the structure like the others?
  2748. if (CurrentSnapshot->PowerInfo.TimeRemaining < IT_DEFAULT_MAX_TIME_REMAINING_TO_SLEEP) {
  2749. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: System will standby / hibernate soon\n"));
  2750. goto cleanup;
  2751. }
  2752. }
  2753. //
  2754. // If the screen saver is running, assume the system is
  2755. // idle. Otherwise, if a heavy-weight OpenGL screen saver is
  2756. // running our CPU checks may bail us out of realizing that the
  2757. // system is idle. We skip this check when trying to determine an
  2758. // idle task is stuck or if it is really running. Note that the
  2759. // screen saver activity may make us think the idle task is still
  2760. // running, even if it is stuck.
  2761. //
  2762. if (IdleCheckReason != ItSrvIdleTaskRunningCheck) {
  2763. if (CurrentSnapshot->GotDisplayPowerStatus) {
  2764. if (CurrentSnapshot->ScreenSaverIsRunning) {
  2765. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: ScreenSaverRunning\n"));
  2766. SystemIsIdle = TRUE;
  2767. goto cleanup;
  2768. }
  2769. }
  2770. }
  2771. //
  2772. // If system may look idle but somebody's running a powerpoint
  2773. // presentation, watching hardware-decoded DVD etc don't run idle
  2774. // tasks. Note that we do not check for ES_SYSTEM_REQUIRED, since
  2775. // it is set by fax servers, answering machines and such as well.
  2776. // ES_DISPLAY_REQUIRED is the one supposed to be used by
  2777. // multimedia/presentation applications.
  2778. //
  2779. if (CurrentSnapshot->GotSystemExecutionState) {
  2780. if ((CurrentSnapshot->ExecutionState & ES_DISPLAY_REQUIRED)) {
  2781. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: Execution state:%x\n",CurrentSnapshot->ExecutionState));
  2782. goto cleanup;
  2783. }
  2784. }
  2785. //
  2786. // We definitely want CPU & general system performance data as
  2787. // well.
  2788. //
  2789. if (!CurrentSnapshot->GotSystemPerformanceInfo ||
  2790. !LastSnapshot->GotSystemPerformanceInfo) {
  2791. goto cleanup;
  2792. }
  2793. //
  2794. // Calculate how many ms the idle process ran. The IdleProcessTime
  2795. // on system performance information structures is in 100ns. To
  2796. // convert it to ms we divide by (10 * 1000).
  2797. //
  2798. // ISSUE-2002/03/26-ScottMa -- What happens here if IdleProcessTime wraps?
  2799. IdleProcessRunTime.QuadPart =
  2800. (CurrentSnapshot->SystemPerformanceInfo.IdleProcessTime.QuadPart -
  2801. LastSnapshot->SystemPerformanceInfo.IdleProcessTime.QuadPart);
  2802. IdleProcessRunTime.QuadPart = IdleProcessRunTime.QuadPart / 10000;
  2803. //
  2804. // Adjust it for the number of CPUs in the system.
  2805. //
  2806. IT_ASSERT(GlobalContext->NumProcessors);
  2807. if (GlobalContext->NumProcessors) {
  2808. IdleProcessRunTime.QuadPart = IdleProcessRunTime.QuadPart / GlobalContext->NumProcessors;
  2809. }
  2810. //
  2811. // Calculate idle cpu percentage this translates to.
  2812. //
  2813. CpuIdlePercentage = (ULONG) (IdleProcessRunTime.QuadPart * 100 / SnapshotTimeDifference);
  2814. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: CPU %d\n", CpuIdlePercentage));
  2815. if (CpuIdlePercentage < Parameters->MinCpuIdlePercentage) {
  2816. goto cleanup;
  2817. }
  2818. //
  2819. // We may not have disk performance data because WMI thingies were
  2820. // not initiated etc.
  2821. //
  2822. if (CurrentSnapshot->GotDiskPerformanceInfo &&
  2823. LastSnapshot->GotDiskPerformanceInfo) {
  2824. //
  2825. // If a new disk was added / removed since last snapshot, say
  2826. // the system is not idle.
  2827. //
  2828. if (CurrentSnapshot->NumPhysicalDisks !=
  2829. LastSnapshot->NumPhysicalDisks) {
  2830. goto cleanup;
  2831. }
  2832. //
  2833. // We assume that the disk data is in the same order in both
  2834. // snapshots. If the ordering of disks changed etc, it will
  2835. // most likely cause us to say the system is not idle. It may
  2836. // cause us to ignore some real activity with very low
  2837. // possibility. That is why we verify several times when we
  2838. // see system idle.
  2839. //
  2840. for (DiskIdx = 0;
  2841. DiskIdx < CurrentSnapshot->NumPhysicalDisks;
  2842. DiskIdx++) {
  2843. DiskIdleTimeDifference =
  2844. CurrentSnapshot->DiskPerfData[DiskIdx].DiskIdleTime -
  2845. LastSnapshot->DiskPerfData[DiskIdx].DiskIdleTime;
  2846. DiskIdlePercentage = (DiskIdleTimeDifference * 100) /
  2847. SnapshotTimeDifference;
  2848. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: Disk %d:%d\n",
  2849. DiskIdx, DiskIdlePercentage));
  2850. if (DiskIdlePercentage < Parameters->MinDiskIdlePercentage) {
  2851. goto cleanup;
  2852. }
  2853. }
  2854. }
  2855. //
  2856. // We passed all the checks.
  2857. //
  2858. SystemIsIdle = TRUE;
  2859. cleanup:
  2860. DBGPR((ITID,ITSRVDD,"IDLE: SrvIsSystemIdle()=%d\n",(ULONG)SystemIsIdle));
  2861. return SystemIsIdle;
  2862. }
  2863. DWORD
  2864. ItSpGetLastInputInfo (
  2865. PLASTINPUTINFO LastInputInfo
  2866. )
  2867. /*++
  2868. Routine Description:
  2869. This function retrieves the time of the last user input event.
  2870. Arguments:
  2871. LastInputInfo - Pointer to structure to update.
  2872. Return Value:
  2873. Win32 error code.
  2874. --*/
  2875. {
  2876. DWORD ErrorCode;
  2877. //
  2878. // Verify parameter.
  2879. //
  2880. if (LastInputInfo->cbSize != sizeof(LASTINPUTINFO)) {
  2881. ErrorCode = ERROR_BAD_FORMAT;
  2882. goto cleanup;
  2883. }
  2884. //
  2885. // We get the last input info from the shared system page that is
  2886. // updated by all terminal server sessions.
  2887. //
  2888. LastInputInfo->dwTime = USER_SHARED_DATA->LastSystemRITEventTickCount;
  2889. #ifdef IT_DBG
  2890. //
  2891. // On the checked build, if we are stressing, we will set the detection
  2892. // period below the period with which the system last input time is
  2893. // updated. If it is so, use the Win32 GetLastInputInfo call. This call
  2894. // will get the user input info only for the current session, but when
  2895. // stressing this is OK.
  2896. //
  2897. if (ItSrvGlobalContext->Parameters.IdleDetectionPeriod < 60*1000) {
  2898. if (!GetLastInputInfo(LastInputInfo)) {
  2899. ErrorCode = GetLastError();
  2900. goto cleanup;
  2901. }
  2902. }
  2903. #endif // IT_DBG
  2904. ErrorCode = ERROR_SUCCESS;
  2905. cleanup:
  2906. return ErrorCode;
  2907. }
  2908. BOOLEAN
  2909. ItSpIsPhysicalDrive (
  2910. PDISK_PERFORMANCE DiskPerformanceData
  2911. )
  2912. /*++
  2913. Routine Description:
  2914. This function attempts to determine if the specified disk is a
  2915. logical or physical disk.
  2916. Arguments:
  2917. DiskPerformanceData - Pointer to disk performance data for the disk.
  2918. Return Value:
  2919. TRUE - The disk is a physical disk.
  2920. FALSE - The disk is not a physical disk.
  2921. --*/
  2922. {
  2923. ULONG ComparisonLength;
  2924. //
  2925. // Initialize locals.
  2926. //
  2927. ComparisonLength =
  2928. sizeof(DiskPerformanceData->StorageManagerName) / sizeof(WCHAR);
  2929. //
  2930. // We have to determine if this is a physical disk or not from the
  2931. // storage manager's name.
  2932. //
  2933. if (!wcsncmp(DiskPerformanceData->StorageManagerName,
  2934. L"Partmgr ",
  2935. ComparisonLength)) {
  2936. return TRUE;
  2937. }
  2938. if (!wcsncmp(DiskPerformanceData->StorageManagerName,
  2939. L"PhysDisk",
  2940. ComparisonLength)) {
  2941. return TRUE;
  2942. }
  2943. return FALSE;
  2944. }
  2945. // FUTURE-2002/03/26-ScottMa -- The InputQueryBuffer (and Size) parameters
  2946. // are always supplied, and don't need to be optional to this function.
  2947. DWORD
  2948. ItSpGetWmiDiskPerformanceData(
  2949. IN WMIHANDLE DiskPerfWmiHandle,
  2950. IN OUT PITSRV_DISK_PERFORMANCE_DATA *DiskPerfData,
  2951. IN OUT ULONG *NumPhysicalDisks,
  2952. OPTIONAL IN OUT PVOID *InputQueryBuffer,
  2953. OPTIONAL IN OUT ULONG *InputQueryBufferSize
  2954. )
  2955. /*++
  2956. Routine Description:
  2957. This function queries disk performance counters and updates input
  2958. parameters.
  2959. Arguments:
  2960. DiskPerfWmiHandle - WMI handle to DiskPerf.
  2961. DiskPerfData - This array is updated with data from all registered
  2962. physical disks' WMI performance data blocks. If it is not big
  2963. enough, it is freed and reallocated using IT_FREE/IT_ALLOC.
  2964. NumPhysicalDisks - This is the size of DiskPerfData array on
  2965. input. If the number of registered physical disks change, it is
  2966. updated on return.
  2967. InputQueryBuffer, InputQueryBufferSize - If specified, they describe a
  2968. query buffer to be used and updated when querying WMI. The
  2969. buffer must be allocated with IT_ALLOC. The returned buffer may
  2970. be relocated/resized and should be freed with IT_FREE. The
  2971. buffer's contents on input and output are trash.
  2972. Return Value:
  2973. Win32 error code.
  2974. --*/
  2975. {
  2976. DWORD ErrorCode;
  2977. PVOID QueryBuffer;
  2978. ULONG QueryBufferSize;
  2979. ULONG RequiredSize;
  2980. ULONG NumTries;
  2981. PWNODE_ALL_DATA DiskWmiDataCursor;
  2982. PDISK_PERFORMANCE DiskPerformanceData;
  2983. LARGE_INTEGER PerformanceCounterFrequency;
  2984. BOOLEAN UsingInputBuffer;
  2985. ULONG NumDiskData;
  2986. PITSRV_DISK_PERFORMANCE_DATA NewDataBuffer;
  2987. //
  2988. // Initialize locals.
  2989. //
  2990. QueryBuffer = NULL;
  2991. QueryBufferSize = 0;
  2992. UsingInputBuffer = FALSE;
  2993. NewDataBuffer = NULL;
  2994. //
  2995. // Determine if we will be using the query buffer input by the
  2996. // user. In case we are using them it is crucial that QueryBuffer
  2997. // and QueryBufferSize are not set to bogus values during the
  2998. // function.
  2999. //
  3000. if (InputQueryBuffer && InputQueryBufferSize) {
  3001. UsingInputBuffer = TRUE;
  3002. QueryBuffer = *InputQueryBuffer;
  3003. QueryBufferSize = *InputQueryBufferSize;
  3004. }
  3005. //
  3006. // Query disk performance data.
  3007. //
  3008. NumTries = 0;
  3009. do {
  3010. RequiredSize = QueryBufferSize;
  3011. __try {
  3012. ErrorCode = WmiQueryAllData(DiskPerfWmiHandle,
  3013. &RequiredSize,
  3014. QueryBuffer);
  3015. } __except (EXCEPTION_EXECUTE_HANDLER) {
  3016. //
  3017. // There is something wrong if we got an exception.
  3018. //
  3019. ErrorCode = GetExceptionCode();
  3020. if (ErrorCode == ERROR_SUCCESS) {
  3021. ErrorCode = ERROR_INVALID_FUNCTION;
  3022. }
  3023. goto cleanup;
  3024. }
  3025. if (ErrorCode == ERROR_SUCCESS) {
  3026. //
  3027. // We got the data.
  3028. //
  3029. break;
  3030. }
  3031. //
  3032. // Check to see if we failed for a real reason other than that
  3033. // our input buffer was too small.
  3034. //
  3035. if (ErrorCode != ERROR_INSUFFICIENT_BUFFER) {
  3036. goto cleanup;
  3037. }
  3038. //
  3039. // Reallocate the buffer to the required size.
  3040. //
  3041. if (QueryBufferSize) {
  3042. IT_FREE(QueryBuffer);
  3043. QueryBufferSize = 0;
  3044. }
  3045. QueryBuffer = IT_ALLOC(RequiredSize);
  3046. if (!QueryBuffer) {
  3047. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  3048. goto cleanup;
  3049. }
  3050. QueryBufferSize = RequiredSize;
  3051. //
  3052. // Don't loop forever...
  3053. //
  3054. NumTries++;
  3055. if (NumTries >= 16) {
  3056. ErrorCode = ERROR_INVALID_FUNCTION;
  3057. goto cleanup;
  3058. }
  3059. } while (TRUE);
  3060. //
  3061. // We have gotten WMI disk performance data. Verify it makes sense.
  3062. //
  3063. DiskWmiDataCursor = QueryBuffer;
  3064. if (DiskWmiDataCursor->InstanceCount == 0) {
  3065. //
  3066. // There are no disks?
  3067. //
  3068. ErrorCode = ERROR_BAD_FORMAT;
  3069. goto cleanup;
  3070. }
  3071. //
  3072. // Determine the number of disks we have data for.
  3073. //
  3074. NumDiskData = 0;
  3075. do {
  3076. if (DiskWmiDataCursor->WnodeHeader.BufferSize < sizeof(WNODE_ALL_DATA)) {
  3077. IT_ASSERT(FALSE);
  3078. ErrorCode = ERROR_BAD_FORMAT;
  3079. goto cleanup;
  3080. }
  3081. DiskPerformanceData = (PDISK_PERFORMANCE)
  3082. ((PUCHAR) DiskWmiDataCursor + DiskWmiDataCursor->DataBlockOffset);
  3083. //
  3084. // Count only physical disk data. Otherwise we will double
  3085. // count disk I/Os for logical disks on the physical disk.
  3086. //
  3087. if (ItSpIsPhysicalDrive(DiskPerformanceData)) {
  3088. NumDiskData++;
  3089. }
  3090. if (DiskWmiDataCursor->WnodeHeader.Linkage == 0) {
  3091. break;
  3092. }
  3093. DiskWmiDataCursor = (PWNODE_ALL_DATA)
  3094. ((LPBYTE)DiskWmiDataCursor + DiskWmiDataCursor->WnodeHeader.Linkage);
  3095. } while (TRUE);
  3096. //
  3097. // Do we have enough space in the input buffer?
  3098. //
  3099. if (NumDiskData > *NumPhysicalDisks) {
  3100. NewDataBuffer = IT_ALLOC(NumDiskData *
  3101. sizeof(ITSRV_DISK_PERFORMANCE_DATA));
  3102. if (!NewDataBuffer) {
  3103. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  3104. goto cleanup;
  3105. }
  3106. //
  3107. // Update old buffer & its max size.
  3108. //
  3109. if (*DiskPerfData) {
  3110. IT_FREE(*DiskPerfData);
  3111. }
  3112. *DiskPerfData = NewDataBuffer;
  3113. NewDataBuffer = NULL;
  3114. *NumPhysicalDisks = NumDiskData;
  3115. }
  3116. //
  3117. // Reset cursor and walk through the WMI data copying into the
  3118. // target buffer.
  3119. //
  3120. DiskWmiDataCursor = QueryBuffer;
  3121. *NumPhysicalDisks = 0;
  3122. do {
  3123. DiskPerformanceData = (PDISK_PERFORMANCE)
  3124. ((PUCHAR) DiskWmiDataCursor + DiskWmiDataCursor->DataBlockOffset);
  3125. //
  3126. // Count only physical disk data. Otherwise we will double
  3127. // count disk I/Os for logical disks on the physical disk.
  3128. //
  3129. if (ItSpIsPhysicalDrive(DiskPerformanceData)) {
  3130. if (*NumPhysicalDisks >= NumDiskData) {
  3131. //
  3132. // We calculated this above. Did the data change
  3133. // beneath our feet?
  3134. //
  3135. IT_ASSERT(FALSE);
  3136. ErrorCode = ERROR_INVALID_FUNCTION;
  3137. goto cleanup;
  3138. }
  3139. //
  3140. // Convert idle time in 100ns to ms.
  3141. //
  3142. // ISSUE-2002/03/26-ScottMa -- DiskPerformanceData->IdleTime
  3143. // could be larger than MAX_ULONG * 10000, overflowing the
  3144. // DiskIdleTime variable.
  3145. (*DiskPerfData)[*NumPhysicalDisks].DiskIdleTime =
  3146. (ULONG) (DiskPerformanceData->IdleTime.QuadPart / 10000);
  3147. (*NumPhysicalDisks)++;
  3148. }
  3149. if (DiskWmiDataCursor->WnodeHeader.Linkage == 0) {
  3150. break;
  3151. }
  3152. DiskWmiDataCursor = (PWNODE_ALL_DATA)
  3153. ((LPBYTE)DiskWmiDataCursor + DiskWmiDataCursor->WnodeHeader.Linkage);
  3154. } while (TRUE);
  3155. IT_ASSERT(*NumPhysicalDisks == NumDiskData);
  3156. ErrorCode = ERROR_SUCCESS;
  3157. cleanup:
  3158. if (UsingInputBuffer) {
  3159. //
  3160. // Update the callers query buffer info.
  3161. //
  3162. *InputQueryBuffer = QueryBuffer;
  3163. *InputQueryBufferSize = QueryBufferSize;
  3164. } else {
  3165. //
  3166. // Free temporary buffer.
  3167. //
  3168. if (QueryBuffer) {
  3169. IT_FREE(QueryBuffer);
  3170. }
  3171. }
  3172. if (NewDataBuffer) {
  3173. IT_FREE(NewDataBuffer);
  3174. }
  3175. DBGPR((ITID,ITSRVDD,"IDLE: SrvGetDiskData()=%d\n",ErrorCode));
  3176. return ErrorCode;
  3177. }
  3178. DWORD
  3179. ItSpGetDisplayPowerStatus(
  3180. PBOOL ScreenSaverIsRunning
  3181. )
  3182. /*++
  3183. Routine Description:
  3184. This routine determines power status of the default display.
  3185. Arguments:
  3186. ScreenSaverIsRunning - Whether a screen saver is running is
  3187. returned here.
  3188. Return Value:
  3189. Win32 error code.
  3190. --*/
  3191. {
  3192. DWORD ErrorCode;
  3193. //
  3194. // Determine whether the screen saver is running.
  3195. //
  3196. if (!SystemParametersInfo(SPI_GETSCREENSAVERRUNNING,
  3197. 0,
  3198. ScreenSaverIsRunning,
  3199. 0)) {
  3200. ErrorCode = GetLastError();
  3201. goto cleanup;
  3202. }
  3203. ErrorCode = ERROR_SUCCESS;
  3204. cleanup:
  3205. return ErrorCode;
  3206. }
  3207. BOOL
  3208. ItSpSetProcessIdleTasksNotifyRoutine (
  3209. PIT_PROCESS_IDLE_TASKS_NOTIFY_ROUTINE NotifyRoutine
  3210. )
  3211. /*++
  3212. Routine Description:
  3213. This routine is called by an internal component (prefetcher) to set a
  3214. notification routine that will get called if processing of all idle
  3215. tasks are requested. The routine should be set once, and it cannot be
  3216. removed.
  3217. Arguments:
  3218. NotifyRoutine - Routine to be called. This routine will be called
  3219. and has to return before we start launching queued idle tasks.
  3220. Return Value:
  3221. Success.
  3222. --*/
  3223. {
  3224. BOOL Success;
  3225. if (!ItSrvGlobalContext->ProcessIdleTasksNotifyRoutine) {
  3226. ItSrvGlobalContext->ProcessIdleTasksNotifyRoutine = NotifyRoutine;
  3227. }
  3228. return (ItSrvGlobalContext->ProcessIdleTasksNotifyRoutine == NotifyRoutine);
  3229. }