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.

444 lines
12 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. service.c
  5. Abstract:
  6. Routines that talk to the service controller.
  7. Author:
  8. Billy J. Fuller 11-Apr-1997
  9. Environment
  10. User mode winnt
  11. --*/
  12. #include <ntreppch.h>
  13. #pragma hdrstop
  14. #include <frs.h>
  15. extern SERVICE_STATUS ServiceStatus;
  16. extern CRITICAL_SECTION ServiceLock;
  17. extern SERVICE_STATUS_HANDLE ServiceStatusHandle;
  18. //
  19. // This is a lookup table of legal/illegal service state transitions. FrsSetServiceStatus API
  20. // uses this table to validate the input transition requested and takes appropriate action.
  21. //
  22. DWORD StateTransitionLookup[FRS_SVC_TRANSITION_TABLE_SIZE][FRS_SVC_TRANSITION_TABLE_SIZE] = {
  23. {0, SERVICE_STOPPED, SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING },
  24. {SERVICE_STOPPED, FRS_SVC_TRANSITION_NOOP, FRS_SVC_TRANSITION_NOOP, FRS_SVC_TRANSITION_ILLEGAL,FRS_SVC_TRANSITION_ILLEGAL},
  25. {SERVICE_START_PENDING,FRS_SVC_TRANSITION_LEGAL,FRS_SVC_TRANSITION_LEGAL, FRS_SVC_TRANSITION_LEGAL, FRS_SVC_TRANSITION_LEGAL },
  26. {SERVICE_STOP_PENDING, FRS_SVC_TRANSITION_LEGAL,FRS_SVC_TRANSITION_ILLEGAL,FRS_SVC_TRANSITION_LEGAL, FRS_SVC_TRANSITION_ILLEGAL},
  27. {SERVICE_RUNNING, FRS_SVC_TRANSITION_LEGAL,FRS_SVC_TRANSITION_ILLEGAL,FRS_SVC_TRANSITION_LEGAL, FRS_SVC_TRANSITION_NOOP }
  28. };
  29. SC_HANDLE
  30. FrsOpenServiceHandle(
  31. IN PTCHAR MachineName,
  32. IN PTCHAR ServiceName
  33. )
  34. /*++
  35. Routine Description:
  36. Open a service on a machine.
  37. Arguments:
  38. MachineName - the name of the machine to contact
  39. ServiceName - the service to open
  40. Return Value:
  41. The service's handle or NULL.
  42. --*/
  43. {
  44. #undef DEBSUB
  45. #define DEBSUB "FrsOpenServiceHandle:"
  46. SC_HANDLE SCMHandle;
  47. SC_HANDLE ServiceHandle;
  48. ULONG WStatus;
  49. //
  50. // Attempt to contact the SC manager.
  51. //
  52. SCMHandle = OpenSCManager(MachineName, NULL, SC_MANAGER_CONNECT);
  53. if (!HANDLE_IS_VALID(SCMHandle)) {
  54. WStatus = GetLastError();
  55. DPRINT1_WS(0, ":SC: Couldn't open service control manager on machine %ws;",
  56. MachineName, WStatus);
  57. return NULL;
  58. }
  59. //
  60. // Contact the service.
  61. //
  62. ServiceHandle = OpenService(SCMHandle, ServiceName, SERVICE_ALL_ACCESS);
  63. if (!HANDLE_IS_VALID(ServiceHandle)) {
  64. WStatus = GetLastError();
  65. DPRINT2_WS(0, ":SC: Couldn't open service control manager for service (%ws) on machine %ws;",
  66. ServiceName, MachineName, WStatus);
  67. ServiceHandle = NULL;
  68. }
  69. CloseServiceHandle(SCMHandle);
  70. return ServiceHandle;
  71. }
  72. DWORD
  73. FrsGetServiceState(
  74. IN PWCHAR MachineName,
  75. IN PWCHAR ServiceName
  76. )
  77. /*++
  78. Routine Description:
  79. Return the service's state
  80. Arguments:
  81. MachineName - the name of the machine to contact
  82. ServiceName - the service to check
  83. Return Value:
  84. The service's state or 0 if the state could not be obtained.
  85. --*/
  86. {
  87. #undef DEBSUB
  88. #define DEBSUB "FrsGetServiceState:"
  89. SC_HANDLE ServiceHandle;
  90. SERVICE_STATUS ServiceStatus;
  91. //
  92. // Open the service.
  93. //
  94. ServiceHandle = FrsOpenServiceHandle(MachineName, ServiceName);
  95. if (!HANDLE_IS_VALID(ServiceHandle)) {
  96. return 0;
  97. }
  98. //
  99. // Get the service's status
  100. //
  101. if (!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
  102. DPRINT3(0, ":SC: WARN - QueryServiceStatus(%ws, %ws) returned %d\n",
  103. MachineName, ServiceName, GetLastError());
  104. CloseServiceHandle(ServiceHandle);
  105. return 0;
  106. }
  107. CloseServiceHandle(ServiceHandle);
  108. //
  109. // Successfully retrieved service status; check state
  110. //
  111. return ServiceStatus.dwCurrentState;
  112. }
  113. BOOL
  114. FrsIsServiceRunning(
  115. IN PWCHAR MachineName,
  116. IN PWCHAR ServiceName
  117. )
  118. /*++
  119. Routine Description:
  120. Is a service running on a machine.
  121. Arguments:
  122. MachineName - the name of the machine to contact
  123. ServiceName - the service to check
  124. Return Value:
  125. TRUE - Service is running.
  126. FALSE - Service is not running.
  127. --*/
  128. {
  129. #undef DEBSUB
  130. #define DEBSUB "FrsIsServiceRunning:"
  131. DWORD State;
  132. State = FrsGetServiceState(MachineName, ServiceName);
  133. return (State == SERVICE_RUNNING);
  134. }
  135. DWORD
  136. FrsSetServiceFailureAction(
  137. VOID
  138. )
  139. /*++
  140. Routine Description:
  141. If unset, initialize the service's failure actions.
  142. Arguments:
  143. Return Value:
  144. WIN32 STATUS
  145. --*/
  146. {
  147. #undef DEBSUB
  148. #define DEBSUB "FrsSetServiceFailureAction:"
  149. #define NUM_ACTIONS (3)
  150. #define SERVICE_RESTART_MILLISECONDS (30 * 60 * 1000)
  151. SC_HANDLE ServiceHandle;
  152. DWORD BufSize, BytesNeeded;
  153. SC_ACTION *Actions;
  154. SERVICE_FAILURE_ACTIONS *FailureActions;
  155. ULONG WStatus = ERROR_SUCCESS, i;
  156. if (!RunningAsAService || !HANDLE_IS_VALID(ServiceStatusHandle)) {
  157. return ERROR_SUCCESS;
  158. }
  159. BufSize = sizeof(SERVICE_FAILURE_ACTIONS) + sizeof(SC_ACTION) * NUM_ACTIONS;
  160. FailureActions = FrsAlloc(BufSize);
  161. EnterCriticalSection(&ServiceLock);
  162. //
  163. // Retrieve the current failure actions for the service NtFrs
  164. //
  165. ServiceHandle = FrsOpenServiceHandle(NULL, SERVICE_NAME);
  166. if (!HANDLE_IS_VALID(ServiceHandle)) {
  167. LeaveCriticalSection(&ServiceLock);
  168. FailureActions = FrsFree(FailureActions);
  169. DPRINT(0, ":SC: Failed to open service handle.\n");
  170. return ERROR_OPEN_FAILED;
  171. }
  172. if (!QueryServiceConfig2(ServiceHandle,
  173. SERVICE_CONFIG_FAILURE_ACTIONS,
  174. (PVOID)FailureActions,
  175. BufSize,
  176. &BytesNeeded)) {
  177. WStatus = GetLastError();
  178. CloseServiceHandle(ServiceHandle);
  179. LeaveCriticalSection(&ServiceLock);
  180. if (WIN_BUF_TOO_SMALL(WStatus)) {
  181. DPRINT(0, ":SC: Restart actions for service are already set.\n");
  182. WStatus = ERROR_SUCCESS;
  183. } else {
  184. DPRINT_WS(0, ":SC: Could not query service for restart action;", WStatus);
  185. }
  186. FailureActions = FrsFree(FailureActions);
  187. return WStatus;
  188. }
  189. //
  190. // Check if failure action already set. E.g. by the User.
  191. //
  192. if (FailureActions->cActions) {
  193. CloseServiceHandle(ServiceHandle);
  194. LeaveCriticalSection(&ServiceLock);
  195. DPRINT(0, ":SC: Restart actions for service are already set.\n");
  196. FailureActions = FrsFree(FailureActions);
  197. return ERROR_SUCCESS;
  198. }
  199. //
  200. // Service failure actions are unset; initialize them
  201. //
  202. WStatus = ERROR_SUCCESS;
  203. Actions = (SC_ACTION *)(((PUCHAR)FailureActions) +
  204. sizeof(SERVICE_FAILURE_ACTIONS));
  205. for (i = 0; i < NUM_ACTIONS; ++i) {
  206. Actions[i].Type = SC_ACTION_RESTART;
  207. Actions[i].Delay = SERVICE_RESTART_MILLISECONDS;
  208. }
  209. FailureActions->cActions = NUM_ACTIONS;
  210. FailureActions->lpsaActions = Actions;
  211. if (!ChangeServiceConfig2(ServiceHandle,
  212. SERVICE_CONFIG_FAILURE_ACTIONS,
  213. (PVOID)FailureActions)) {
  214. WStatus = GetLastError();
  215. }
  216. CloseServiceHandle(ServiceHandle);
  217. LeaveCriticalSection(&ServiceLock);
  218. if (!WIN_SUCCESS(WStatus)) {
  219. DPRINT_WS(0, ":SC: Could not set restart actions;", WStatus);
  220. } else {
  221. DPRINT(4, ":SC: Success setting restart actions for service.\n");
  222. }
  223. FailureActions = FrsFree(FailureActions);
  224. return WStatus;
  225. }
  226. BOOL
  227. FrsWaitService(
  228. IN PWCHAR MachineName,
  229. IN PWCHAR ServiceName,
  230. IN INT IntervalMS,
  231. IN INT TotalMS
  232. )
  233. /*++
  234. Routine Description:
  235. This routine determines if the specified NT service is in a running
  236. state or not. This function will sleep and retry once if the service
  237. is not yet running.
  238. Arguments:
  239. MachineName - machine to contact
  240. ServiceName - Name of the NT service to interrogate.
  241. IntervalMS - Check every IntervalMS milliseconds.
  242. TotalMS - Stop checking after this long.
  243. Return Value:
  244. TRUE - Service is running.
  245. FALSE - Service state cannot be determined.
  246. --*/
  247. {
  248. #undef DEBSUB
  249. #define DEBSUB "FrsWaitService:"
  250. do {
  251. if (FrsIsServiceRunning(MachineName, ServiceName)) {
  252. return TRUE;
  253. }
  254. if (FrsIsShuttingDown) {
  255. break;
  256. }
  257. Sleep(IntervalMS);
  258. } while ((TotalMS -= IntervalMS) > 0);
  259. DPRINT2(0, ":SC: %ws is not running on %ws\n", ServiceName, ComputerName);
  260. return FALSE;
  261. }
  262. DWORD
  263. FrsSetServiceStatus(
  264. IN DWORD State,
  265. IN DWORD CheckPoint,
  266. IN DWORD Hint,
  267. IN DWORD ExitCode
  268. )
  269. /*++
  270. Routine Description:
  271. Acquire the service lock, ServiceLock, and set the service's state
  272. using the global service handle and service status set in main.c.
  273. Check if this is a valid state transition using the lookup table.
  274. This will prevent the service from making any invalid state transitions.
  275. Arguments:
  276. Status - Set the state to this value
  277. Hint - Suggested timeout for the service controller
  278. ExitCode - For SERVICE_STOPPED;
  279. Return Value:
  280. WIN32 STATUS
  281. --*/
  282. {
  283. #undef DEBSUB
  284. #define DEBSUB "FrsSetServiceStatus:"
  285. DWORD WStatus = ERROR_SUCCESS;
  286. BOOL Ret;
  287. DWORD FromState,ToState;
  288. DWORD TransitionCheck = FRS_SVC_TRANSITION_ILLEGAL;
  289. //
  290. // Set the service's status after acquiring the lock
  291. //
  292. if (RunningAsAService && HANDLE_IS_VALID(ServiceStatusHandle)) {
  293. EnterCriticalSection(&ServiceLock);
  294. //
  295. // Check if this is a valid service state transition.
  296. //
  297. for (FromState = 0 ; FromState < FRS_SVC_TRANSITION_TABLE_SIZE ; ++FromState) {
  298. for (ToState = 0 ; ToState < FRS_SVC_TRANSITION_TABLE_SIZE ; ++ToState) {
  299. if (StateTransitionLookup[FromState][0] == ServiceStatus.dwCurrentState &&
  300. StateTransitionLookup[0][ToState] == State) {
  301. TransitionCheck = StateTransitionLookup[FromState][ToState];
  302. break;
  303. }
  304. }
  305. }
  306. if (TransitionCheck == FRS_SVC_TRANSITION_LEGAL) {
  307. DPRINT2(4,":SC: Current State = %d, Moving to %d\n", ServiceStatus.dwCurrentState, State);
  308. ServiceStatus.dwCurrentState = State;
  309. ServiceStatus.dwCheckPoint = CheckPoint;
  310. ServiceStatus.dwWaitHint = Hint;
  311. ServiceStatus.dwWin32ExitCode = ExitCode;
  312. //
  313. // Do not accept stop control unless the service is in SERVICE_RUNNING state.
  314. // This prevents the service from getting confused when a stop is called
  315. // while the service is starting.
  316. //
  317. if (ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
  318. ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
  319. } else {
  320. ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
  321. }
  322. Ret = SetServiceStatus(ServiceStatusHandle, &ServiceStatus);
  323. if (!Ret) {
  324. WStatus = GetLastError();
  325. DPRINT1_WS(0, ":SC: ERROR - SetServiceStatus(%d);", ServiceStatus, WStatus);
  326. } else {
  327. DPRINT4(0, ":SC: SetServiceStatus(State %d, CheckPoint %d, Hint %d, ExitCode %d) succeeded\n",
  328. State, CheckPoint, Hint, ExitCode);
  329. }
  330. } else if (TransitionCheck == FRS_SVC_TRANSITION_ILLEGAL) {
  331. DPRINT2(0,":SC: Error - Illegal service state transition request. From State = %d, To %d\n", ServiceStatus.dwCurrentState, State);
  332. WStatus = ERROR_INVALID_PARAMETER;
  333. }
  334. LeaveCriticalSection(&ServiceLock);
  335. }
  336. return WStatus;
  337. }