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.

549 lines
14 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. threads.c
  5. Abstract:
  6. This file contains routines that manage access to a database of
  7. worker thread handles and a database containing the current messenger
  8. status (used to report status to the Service Controller). Access to
  9. these two databases is controled via a Critical Section.
  10. Functions for managing worker threads:
  11. MsgThreadManagerInit
  12. MsgThreadCloseAll
  13. Routines for managing _access to the status information and reporting:
  14. MsgStatusInit
  15. MsgBeginForcedShutdown
  16. MsgStatusUpdate
  17. GetMsgrState
  18. Author:
  19. Dan Lafferty (danl) 17-Jul-1991
  20. Environment:
  21. User Mode -Win32
  22. Notes:
  23. These functions must be used carefully in order to be effective in
  24. shutting the messenger threads down nicely if the shutdown happens
  25. to occur during Messenger Initialization. This note explains when
  26. each function is to be called.
  27. MsgThreadManagerInit
  28. This function must be called early on in the initialization process.
  29. It should be called before NetRegisterCtrlDispatcher. This way,
  30. it is impossible for an UNINSTALL request to be received prior to
  31. initializing the Critical Section and the Messenger State.
  32. Revision History:
  33. 15-Dec-1998 jschwart
  34. Eliminated MsgThreadManagerEnd. The DLL is no longer unloaded by
  35. services.exe, so deleting the critical section can create a race
  36. condition (stop service, new service starts and calls init, first
  37. thread deletes critsec, first thread tries to enter critsec and AVs)
  38. 03-Nov-1992 Danl
  39. Changed status reporting so that we only accept STOP controls if
  40. the service is in the RUNNING state.
  41. 18-Feb-1992 RitaW
  42. Convert to Win32 service control APIs.
  43. 02-Oct-1991 JohnRo
  44. Work toward UNICODE.
  45. 17-Jul-1991 danl
  46. created
  47. --*/
  48. //
  49. // Includes
  50. //
  51. #include "msrv.h"
  52. #include <string.h> // strlen
  53. #include <winsvc.h> // SERVICE_STATUS
  54. #include <netlib.h> // UNUSED Macro
  55. #include "msgdbg.h" // MSG_LOG
  56. #include "msgdata.h"
  57. //
  58. // Global Data
  59. //
  60. RTL_RESOURCE g_StateResource;
  61. SERVICE_STATUS MsgrStatus;
  62. DWORD HintCount;
  63. DWORD MsgrUninstallCode; // reason for uninstalling
  64. BOOL g_fResourceCreated;
  65. DWORD MsgrState;
  66. DWORD
  67. MsgThreadManagerInit(
  68. VOID
  69. )
  70. /*++
  71. Routine Description:
  72. Initializes the critical section that is used to guard access to the
  73. thread and status database. Note that this critsec is created and
  74. never deleted (OK since the DLL is never unloaded by services.exe) to
  75. fix synchronization problems with stopping/restarting the service.
  76. Arguments:
  77. none
  78. Return Value:
  79. NO_ERROR on success, ERROR_NOT_ENOUGH_MEMORY if the init fails
  80. Note:
  81. --*/
  82. {
  83. DWORD dwError = NO_ERROR;
  84. NTSTATUS status;
  85. if (!g_fResourceCreated)
  86. {
  87. status = MsgInitResource(&g_StateResource);
  88. if (NT_SUCCESS(status))
  89. {
  90. g_fResourceCreated = TRUE;
  91. }
  92. else
  93. {
  94. MSG_LOG1(ERROR,
  95. "MsgThreadManagerInit: MsgInitResource failed %#x\n",
  96. status);
  97. dwError = ERROR_NOT_ENOUGH_MEMORY;
  98. }
  99. }
  100. return dwError;
  101. }
  102. VOID
  103. MsgThreadCloseAll(
  104. VOID
  105. )
  106. /*++
  107. Routine Description:
  108. Closes all handles stored in the table of worker thread handles.
  109. Arguments:
  110. none
  111. Return Value:
  112. none
  113. Note:
  114. --*/
  115. {
  116. RtlAcquireResourceExclusive(&g_StateResource, TRUE);
  117. MsgrState = STOPPING;
  118. RtlReleaseResource(&g_StateResource);
  119. }
  120. VOID
  121. MsgStatusInit(VOID)
  122. /*++
  123. Routine Description:
  124. Initializes the status database.
  125. Arguments:
  126. none.
  127. Return Value:
  128. none.
  129. Note:
  130. --*/
  131. {
  132. RtlAcquireResourceExclusive(&g_StateResource, TRUE);
  133. MsgrState = STARTING;
  134. HintCount = 1;
  135. MsgrUninstallCode = 0;
  136. MsgrStatus.dwServiceType = SERVICE_WIN32;
  137. MsgrStatus.dwCurrentState = SERVICE_START_PENDING;
  138. MsgrStatus.dwControlsAccepted = 0;
  139. MsgrStatus.dwCheckPoint = HintCount;
  140. MsgrStatus.dwWaitHint = 20000; // 20 seconds
  141. SET_SERVICE_EXITCODE(
  142. NO_ERROR,
  143. MsgrStatus.dwWin32ExitCode,
  144. MsgrStatus.dwServiceSpecificExitCode
  145. );
  146. RtlReleaseResource(&g_StateResource);
  147. return;
  148. }
  149. DWORD
  150. MsgBeginForcedShutdown(
  151. IN BOOL PendingCode,
  152. IN DWORD ExitCode
  153. )
  154. /*++
  155. Routine Description:
  156. This function is called to set the appropriate status when a shutdown
  157. is to occur due to an error in the Messenger. NOTE: if a shutdown is
  158. based on a request from the Service Controller, MsgStatusUpdate is
  159. called instead.
  160. On a PENDING call, this routine will also wake up all messenger
  161. threads so that they will also shut down.
  162. Arguments:
  163. PendingCode - Indicates if the Shutdown is immediate or pending. If
  164. PENDING, the shutdown will take some time, so a pending status is
  165. sent to the ServiceController.
  166. ExitCode - Indicates the reason for the shutdown.
  167. Return Value:
  168. CurrentState - Contains the current state that the messenger is in
  169. upon exit from this routine. In this case it will be STOPPED
  170. if the PendingCode is PENDING, or STOPPING if the PendingCode
  171. is IMMEDIATE.
  172. Note:
  173. --*/
  174. {
  175. NET_API_STATUS status;
  176. RtlAcquireResourceExclusive(&g_StateResource, TRUE);
  177. //
  178. // See if the messenger is already stopping for some reason.
  179. // It could be that the ControlHandler thread received a control to
  180. // stop the messenger just as we decided to stop ourselves.
  181. //
  182. if ((MsgrState != STOPPING) && (MsgrState != STOPPED)) {
  183. if (PendingCode == PENDING) {
  184. MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
  185. MsgrState = STOPPING;
  186. }
  187. else {
  188. //
  189. // The shutdown is to take immediate effect.
  190. //
  191. MsgrStatus.dwCurrentState = SERVICE_STOPPED;
  192. MsgrStatus.dwControlsAccepted = 0;
  193. MsgrStatus.dwCheckPoint = 0;
  194. MsgrStatus.dwWaitHint = 0;
  195. MsgrState = STOPPED;
  196. }
  197. MsgrUninstallCode = ExitCode;
  198. SET_SERVICE_EXITCODE(
  199. ExitCode,
  200. MsgrStatus.dwWin32ExitCode,
  201. MsgrStatus.dwServiceSpecificExitCode
  202. );
  203. }
  204. //
  205. // Send the new status to the service controller.
  206. //
  207. if (MsgrStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
  208. MSG_LOG(ERROR,
  209. "MsgBeginForcedShutdown, no handle to call SetServiceStatus\n", 0);
  210. }
  211. else if (! SetServiceStatus( MsgrStatusHandle, &MsgrStatus )) {
  212. status = GetLastError();
  213. if (status != NERR_Success) {
  214. MSG_LOG(ERROR,
  215. "MsgBeginForcedShutdown,SetServiceStatus Failed %X\n",
  216. status);
  217. }
  218. }
  219. status = MsgrState;
  220. RtlReleaseResource(&g_StateResource);
  221. return status;
  222. }
  223. DWORD
  224. MsgStatusUpdate(
  225. IN DWORD NewState
  226. )
  227. /*++
  228. Routine Description:
  229. Sends a status to the Service Controller via SetServiceStatus.
  230. The contents of the status message is controlled by this routine.
  231. The caller simply passes in the desired state, and this routine does
  232. the rest. For instance, if the Messenger passes in a STARTING state,
  233. This routine will update the hint count that it maintains, and send
  234. the appropriate information in the SetServiceStatus call.
  235. This routine uses transitions in state to send determine which status
  236. to send. For instance if the status was STARTING, and has changed
  237. to RUNNING, this routine sends out an INSTALLED to the Service
  238. Controller.
  239. Arguments:
  240. NewState - Can be any of the state flags:
  241. UPDATE_ONLY - Simply send out the current status
  242. STARTING - The Messenger is in the process of initializing
  243. RUNNING - The Messenger has finished with initialization
  244. STOPPING - The Messenger is in the process of shutting down
  245. STOPPED - The Messenger has completed the shutdown.
  246. Return Value:
  247. CurrentState - This may not be the same as the NewState that was
  248. passed in. It could be that the main thread is sending in a new
  249. install state just after the Control Handler set the state to
  250. STOPPING. In this case, the STOPPING state will be returned so as
  251. to inform the main thread that a shut-down is in process.
  252. Note:
  253. --*/
  254. {
  255. DWORD status;
  256. BOOL inhibit = FALSE; // Used to inhibit sending the status
  257. // to the service controller.
  258. RtlAcquireResourceExclusive(&g_StateResource, TRUE);
  259. if (NewState == STOPPED) {
  260. if (MsgrState == STOPPED) {
  261. //
  262. // It was already stopped, don't send another SetServiceStatus.
  263. //
  264. inhibit = TRUE;
  265. }
  266. else {
  267. //
  268. // The shut down is complete, indicate that the messenger
  269. // has stopped.
  270. //
  271. MsgrStatus.dwCurrentState = SERVICE_STOPPED;
  272. MsgrStatus.dwControlsAccepted = 0;
  273. MsgrStatus.dwCheckPoint = 0;
  274. MsgrStatus.dwWaitHint = 0;
  275. SET_SERVICE_EXITCODE(
  276. MsgrUninstallCode,
  277. MsgrStatus.dwWin32ExitCode,
  278. MsgrStatus.dwServiceSpecificExitCode
  279. );
  280. }
  281. MsgrState = NewState;
  282. }
  283. else {
  284. //
  285. // We are not being asked to change to the STOPPED state.
  286. //
  287. switch(MsgrState) {
  288. case STARTING:
  289. if (NewState == STOPPING) {
  290. MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
  291. MsgrStatus.dwControlsAccepted = 0;
  292. MsgrStatus.dwCheckPoint = HintCount++;
  293. MsgrStatus.dwWaitHint = 20000; // 20 seconds
  294. MsgrState = NewState;
  295. }
  296. else if (NewState == RUNNING) {
  297. //
  298. // The Messenger Service has completed installation.
  299. //
  300. MsgrStatus.dwCurrentState = SERVICE_RUNNING;
  301. MsgrStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
  302. MsgrStatus.dwCheckPoint = 0;
  303. MsgrStatus.dwWaitHint = 0;
  304. MsgrState = NewState;
  305. }
  306. else {
  307. //
  308. // The NewState must be STARTING. So update the pending
  309. // count
  310. //
  311. MsgrStatus.dwCurrentState = SERVICE_START_PENDING;
  312. MsgrStatus.dwControlsAccepted = 0;
  313. MsgrStatus.dwCheckPoint = HintCount++;
  314. MsgrStatus.dwWaitHint = 20000; // 20 seconds
  315. }
  316. break;
  317. case RUNNING:
  318. if (NewState == STOPPING) {
  319. MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
  320. MsgrStatus.dwControlsAccepted = 0;
  321. MsgrStatus.dwCheckPoint = HintCount++;
  322. MsgrStatus.dwWaitHint = 20000; // 20 seconds
  323. MsgrState = NewState;
  324. }
  325. break;
  326. case STOPPING:
  327. //
  328. // No matter what else was passed in, force the status to
  329. // indicate that a shutdown is pending.
  330. //
  331. MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
  332. MsgrStatus.dwControlsAccepted = 0;
  333. MsgrStatus.dwCheckPoint = HintCount++;
  334. MsgrStatus.dwWaitHint = 20000; // 20 seconds
  335. break;
  336. case STOPPED:
  337. //
  338. // We're already stopped. Therefore, an uninstalled status
  339. // as already been sent. Do nothing.
  340. //
  341. inhibit = TRUE;
  342. break;
  343. }
  344. }
  345. if (!inhibit) {
  346. if (MsgrStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
  347. MSG_LOG(ERROR,
  348. "MsgStatusUpdate, no handle to call SetServiceStatus\n", 0);
  349. }
  350. else if (! SetServiceStatus( MsgrStatusHandle, &MsgrStatus )) {
  351. status = GetLastError();
  352. if (status != NERR_Success) {
  353. MSG_LOG(ERROR,
  354. "MsgStatusUpdate, SetServiceStatus Failed %d\n",
  355. status);
  356. }
  357. }
  358. }
  359. status = MsgrState;
  360. RtlReleaseResource(&g_StateResource);
  361. return status;
  362. }
  363. DWORD
  364. GetMsgrState (
  365. VOID
  366. )
  367. /*++
  368. Routine Description:
  369. Obtains the state of the Messenger Service. This state information
  370. is protected as a critical section such that only one thread can
  371. modify or read it at a time.
  372. Arguments:
  373. none
  374. Return Value:
  375. The Messenger State is returned as the return value.
  376. --*/
  377. {
  378. DWORD status;
  379. RtlAcquireResourceShared(&g_StateResource, TRUE);
  380. status = MsgrState;
  381. RtlReleaseResource(&g_StateResource);
  382. return status;
  383. }
  384. VOID
  385. MsgrBlockStateChange(
  386. VOID
  387. )
  388. {
  389. RtlAcquireResourceExclusive(&g_StateResource, TRUE);
  390. }
  391. VOID
  392. MsgrUnblockStateChange(
  393. VOID
  394. )
  395. {
  396. RtlReleaseResource(&g_StateResource);
  397. }