Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

573 lines
14 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. CONTROL.C
  5. Abstract:
  6. This file contains the control handler for the eventlog service.
  7. Author:
  8. Rajen Shah (rajens) 16-Jul-1991
  9. Revision History:
  10. --*/
  11. //
  12. // INCLUDES
  13. //
  14. #include <eventp.h>
  15. //
  16. // DEFINITIONS
  17. //
  18. //
  19. // Controls accepted by the service
  20. //
  21. #define ELF_CONTROLS_ACCEPTED SERVICE_ACCEPT_SHUTDOWN;
  22. //
  23. // GLOBALS
  24. //
  25. CRITICAL_SECTION StatusCriticalSection = {0};
  26. SERVICE_STATUS ElStatus = {0};
  27. DWORD HintCount = 0;
  28. DWORD ElUninstallCode = 0; // reason for uninstalling
  29. DWORD ElSpecificCode = 0;
  30. DWORD ElState = STARTING;
  31. VOID
  32. ElfControlResponse(
  33. DWORD opCode
  34. )
  35. {
  36. DWORD state;
  37. ELF_LOG1(TRACE,
  38. "ElfControlResponse: Received control %d\n",
  39. opCode);
  40. //
  41. // Determine the type of service control message and modify the
  42. // service status, if necessary.
  43. //
  44. switch(opCode)
  45. {
  46. case SERVICE_CONTROL_SHUTDOWN:
  47. {
  48. HKEY hKey;
  49. ULONG ValueSize;
  50. ULONG ShutdownReason = 0xFF;
  51. ULONG rc;
  52. //
  53. // If the service is installed, shut it down and exit.
  54. //
  55. ElfStatusUpdate(STOPPING);
  56. GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
  57. //
  58. // Cause the timestamp writing thread to exit
  59. //
  60. if (g_hTimestampEvent != NULL)
  61. {
  62. SetEvent (g_hTimestampEvent);
  63. }
  64. //
  65. // Indicate a normal shutdown in the registry
  66. //
  67. ElfWriteTimeStamp(EVENT_NormalShutdown,
  68. FALSE);
  69. //
  70. // Determine the reason for this normal shutdown
  71. //
  72. rc = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  73. REGSTR_PATH_RELIABILITY,
  74. 0,
  75. NULL,
  76. REG_OPTION_NON_VOLATILE,
  77. KEY_ALL_ACCESS,
  78. NULL,
  79. &hKey,
  80. NULL);
  81. if (rc == ERROR_SUCCESS)
  82. {
  83. ValueSize = sizeof(ULONG);
  84. rc = RegQueryValueEx(hKey,
  85. REGSTR_VAL_SHUTDOWNREASON,
  86. 0,
  87. NULL,
  88. (PUCHAR) &ShutdownReason,
  89. &ValueSize);
  90. if (rc == ERROR_SUCCESS)
  91. {
  92. RegDeleteValue (hKey, REGSTR_VAL_SHUTDOWNREASON);
  93. }
  94. RegCloseKey (hKey);
  95. }
  96. //
  97. // Log an event that says we're stopping
  98. //
  99. ElfpCreateElfEvent(EVENT_EventlogStopped,
  100. EVENTLOG_INFORMATION_TYPE,
  101. 0, // EventCategory
  102. 0, // NumberOfStrings
  103. NULL, // Strings
  104. &ShutdownReason, // Data
  105. sizeof(ULONG), // Datalength
  106. 0,
  107. FALSE); // flags
  108. //
  109. // Now force it to be written before we shut down
  110. //
  111. WriteQueuedEvents();
  112. ReleaseGlobalResource();
  113. //
  114. // If the RegistryMonitor is started, wakeup that
  115. // worker thread and have it handle the rest of the
  116. // shutdown.
  117. //
  118. // Otherwise The main thread should pick up the
  119. // fact that a shutdown during startup is occuring.
  120. //
  121. if (EventFlags & ELF_STARTED_REGISTRY_MONITOR)
  122. {
  123. StopRegistryMonitor();
  124. }
  125. break ;
  126. }
  127. case SERVICE_CONTROL_INTERROGATE:
  128. ElfStatusUpdate(UPDATE_ONLY);
  129. break;
  130. default:
  131. //
  132. // This should never happen.
  133. //
  134. ELF_LOG1(ERROR,
  135. "ElfControlResponse: Received unexpected control %d\n",
  136. opCode);
  137. ASSERT(FALSE);
  138. break ;
  139. }
  140. return;
  141. }
  142. DWORD
  143. ElfBeginForcedShutdown(
  144. IN BOOL PendingCode,
  145. IN DWORD ExitCode,
  146. IN DWORD ServiceSpecificCode
  147. )
  148. /*++
  149. Routine Description:
  150. Arguments:
  151. Return Value:
  152. --*/
  153. {
  154. DWORD status;
  155. EnterCriticalSection(&StatusCriticalSection);
  156. ELF_LOG2(ERROR,
  157. "ElfBeginForcedShutdown: error %d, service-specific error %d\n",
  158. ExitCode,
  159. ServiceSpecificCode);
  160. //
  161. // See if the eventlog is already stopping for some reason.
  162. // It could be that the ControlHandler thread received a control to
  163. // stop the eventlog just as we decided to stop ourselves.
  164. //
  165. if ((ElState != STOPPING) && (ElState != STOPPED))
  166. {
  167. if (PendingCode == PENDING)
  168. {
  169. ELF_LOG0(TRACE,
  170. "ElfBeginForcedShutdown: Starting pending shutdown\n");
  171. ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
  172. ElState = STOPPING;
  173. }
  174. else
  175. {
  176. //
  177. // The shutdown is to take immediate effect.
  178. //
  179. ELF_LOG0(TRACE,
  180. "ElfBeginForcedShutdown: Starting immediate shutdown\n");
  181. ElStatus.dwCurrentState = SERVICE_STOPPED;
  182. ElStatus.dwControlsAccepted = 0;
  183. ElStatus.dwCheckPoint = 0;
  184. ElStatus.dwWaitHint = 0;
  185. ElState = STOPPED;
  186. }
  187. ElUninstallCode = ExitCode;
  188. ElSpecificCode = ServiceSpecificCode;
  189. ElStatus.dwWin32ExitCode = ExitCode;
  190. ElStatus.dwServiceSpecificExitCode = ServiceSpecificCode;
  191. }
  192. //
  193. // Cause the timestamp writing thread to exit
  194. //
  195. if (g_hTimestampEvent != NULL)
  196. {
  197. SetEvent (g_hTimestampEvent);
  198. }
  199. //
  200. // Send the new status to the service controller.
  201. //
  202. ASSERT(ElfServiceStatusHandle != 0);
  203. if (!SetServiceStatus( ElfServiceStatusHandle, &ElStatus ))
  204. {
  205. ELF_LOG1(ERROR,
  206. "ElfBeginForcedShutdown: SetServicestatus failed %d\n",
  207. GetLastError());
  208. }
  209. status = ElState;
  210. ELF_LOG1(TRACE,
  211. "ElfBeginForcedShutdown: New state is %d\n",
  212. status);
  213. LeaveCriticalSection(&StatusCriticalSection);
  214. return status;
  215. }
  216. DWORD
  217. ElfStatusUpdate(
  218. IN DWORD NewState
  219. )
  220. /*++
  221. Routine Description:
  222. Sends a status to the Service Controller via SetServiceStatus.
  223. The contents of the status message is controlled by this routine.
  224. The caller simply passes in the desired state, and this routine does
  225. the rest. For instance, if the Eventlog passes in a STARTING state,
  226. This routine will update the hint count that it maintains, and send
  227. the appropriate information in the SetServiceStatus call.
  228. This routine uses transitions in state to determine which status
  229. to send. For instance if the status was STARTING, and has changed
  230. to RUNNING, this routine sends SERVICE_RUNNING to the Service
  231. Controller.
  232. Arguments:
  233. NewState - Can be any of the state flags:
  234. UPDATE_ONLY - Simply send out the current status
  235. STARTING - The Eventlog is in the process of initializing
  236. RUNNING - The Eventlog has finished with initialization
  237. STOPPING - The Eventlog is in the process of shutting down
  238. STOPPED - The Eventlog has completed the shutdown.
  239. Return Value:
  240. CurrentState - This may not be the same as the NewState that was
  241. passed in. It could be that the main thread is sending in a new
  242. install state just after the Control Handler set the state to
  243. STOPPING. In this case, the STOPPING state will be returned so as
  244. to inform the main thread that a shut-down is in process.
  245. --*/
  246. {
  247. DWORD status;
  248. BOOL inhibit = FALSE; // Used to inhibit sending the status
  249. // to the service controller.
  250. EnterCriticalSection(&StatusCriticalSection);
  251. ELF_LOG2(TRACE,
  252. "ElfStatusUpdate: old state = %d, new state = %d\n",
  253. ElState,
  254. NewState);
  255. if (NewState == STOPPED)
  256. {
  257. if (ElState == STOPPED)
  258. {
  259. //
  260. // It was already stopped, don't send another SetServiceStatus.
  261. //
  262. inhibit = TRUE;
  263. }
  264. else
  265. {
  266. //
  267. // The shut down is complete, indicate that the eventlog
  268. // has stopped.
  269. //
  270. ElStatus.dwCurrentState = SERVICE_STOPPED;
  271. ElStatus.dwControlsAccepted = 0;
  272. ElStatus.dwCheckPoint = 0;
  273. ElStatus.dwWaitHint = 0;
  274. ElStatus.dwWin32ExitCode = ElUninstallCode;
  275. ElStatus.dwServiceSpecificExitCode = ElSpecificCode;
  276. }
  277. ElState = NewState;
  278. }
  279. else if (NewState != UPDATE_ONLY)
  280. {
  281. //
  282. // We are not being asked to change to the STOPPED state.
  283. //
  284. switch(ElState)
  285. {
  286. case STARTING:
  287. if (NewState == STOPPING)
  288. {
  289. ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
  290. ElStatus.dwControlsAccepted = 0;
  291. ElStatus.dwCheckPoint = HintCount++;
  292. ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
  293. ElState = NewState;
  294. EventlogShutdown = TRUE;
  295. }
  296. else if (NewState == RUNNING)
  297. {
  298. //
  299. // The Eventlog Service has completed installation.
  300. //
  301. ElStatus.dwCurrentState = SERVICE_RUNNING;
  302. ElStatus.dwCheckPoint = 0;
  303. ElStatus.dwWaitHint = 0;
  304. ElStatus.dwControlsAccepted = ELF_CONTROLS_ACCEPTED;
  305. ElState = NewState;
  306. }
  307. else
  308. {
  309. //
  310. // The NewState must be STARTING. So update the pending
  311. // count
  312. //
  313. ElStatus.dwCurrentState = SERVICE_START_PENDING;
  314. ElStatus.dwControlsAccepted = 0;
  315. ElStatus.dwCheckPoint = HintCount++;
  316. ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
  317. }
  318. break;
  319. case RUNNING:
  320. if (NewState == STOPPING)
  321. {
  322. ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
  323. ElStatus.dwControlsAccepted = 0;
  324. EventlogShutdown = TRUE;
  325. }
  326. ElStatus.dwCheckPoint = HintCount++;
  327. ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
  328. ElState = NewState;
  329. break;
  330. case STOPPING:
  331. //
  332. // No matter what else was passed in, force the status to
  333. // indicate that a shutdown is pending.
  334. //
  335. ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
  336. ElStatus.dwControlsAccepted = 0;
  337. ElStatus.dwCheckPoint = HintCount++;
  338. ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
  339. EventlogShutdown = TRUE;
  340. break;
  341. case STOPPED:
  342. ASSERT(NewState == STARTING);
  343. //
  344. // The Eventlog Service is starting up after being stopped.
  345. // This can occur if the service is manually started after
  346. // failing to start.
  347. //
  348. ElStatus.dwCurrentState = SERVICE_START_PENDING;
  349. ElStatus.dwCheckPoint = 0;
  350. ElStatus.dwWaitHint = 0;
  351. ElStatus.dwControlsAccepted = ELF_CONTROLS_ACCEPTED;
  352. ElState = NewState;
  353. break;
  354. }
  355. }
  356. if (!inhibit)
  357. {
  358. ASSERT(ElfServiceStatusHandle != 0);
  359. if (!SetServiceStatus(ElfServiceStatusHandle, &ElStatus))
  360. {
  361. ELF_LOG1(ERROR,
  362. "ElfStatusUpdate: SetServiceStatus failed %d\n",
  363. GetLastError());
  364. }
  365. }
  366. status = ElState;
  367. ELF_LOG1(TRACE,
  368. "ElfStatusUpdate: Exiting with state = %d\n",
  369. ElState);
  370. LeaveCriticalSection(&StatusCriticalSection);
  371. return status;
  372. }
  373. DWORD
  374. GetElState (
  375. VOID
  376. )
  377. /*++
  378. Routine Description:
  379. Obtains the state of the Eventlog Service. This state information
  380. is protected as a critical section such that only one thread can
  381. modify or read it at a time.
  382. Arguments:
  383. none
  384. Return Value:
  385. The Eventlog State is returned as the return value.
  386. --*/
  387. {
  388. DWORD status;
  389. EnterCriticalSection(&StatusCriticalSection);
  390. status = ElState;
  391. LeaveCriticalSection(&StatusCriticalSection);
  392. return status;
  393. }
  394. NTSTATUS
  395. ElfpInitStatus(
  396. VOID
  397. )
  398. /*++
  399. Routine Description:
  400. Initializes the critical section that is used to guard access to the
  401. status database.
  402. Arguments:
  403. none
  404. Return Value:
  405. none
  406. --*/
  407. {
  408. ElStatus.dwCurrentState = SERVICE_START_PENDING;
  409. ElStatus.dwServiceType = SERVICE_WIN32;
  410. return ElfpInitCriticalSection(&StatusCriticalSection);
  411. }
  412. VOID
  413. ElCleanupStatus(VOID)
  414. /*++
  415. Routine Description:
  416. Deletes the critical section used to control access to the thread and
  417. status database.
  418. Arguments:
  419. none
  420. Return Value:
  421. none
  422. Note:
  423. --*/
  424. {
  425. DeleteCriticalSection(&StatusCriticalSection);
  426. }