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.

301 lines
9.1 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+-------------------------------------------------------------------------
  3. //
  4. // Microsoft Windows
  5. //
  6. // File: svcctrl.cxx
  7. //
  8. // Contents: Class for service control interface.
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. //
  15. //
  16. // History: 18-Nov-96 BillMo Created.
  17. //
  18. // Notes:
  19. //
  20. // Codework:
  21. //
  22. //--------------------------------------------------------------------------
  23. #include "pch.cxx"
  24. #pragma hdrstop
  25. #include "trklib.hxx"
  26. #define THIS_FILE_NUMBER SVCCTRL_CXX_FILE_NO
  27. // This is static so that we can handle a PNP timing problem
  28. // (see the comment in CSvcCtrlInterface::ServiceHandler).
  29. BOOL CSvcCtrlInterface::_fStoppedOrStopping = TRUE;
  30. //+----------------------------------------------------------------------------
  31. //
  32. // CSvcCtrlInterface::Initialize
  33. //
  34. // Register our service control handler with the control dispatcher, and set our state
  35. // to start-pending.
  36. //
  37. //+----------------------------------------------------------------------------
  38. void
  39. CSvcCtrlInterface::Initialize(const TCHAR *ptszServiceName, IServiceHandler *pServiceHandler)
  40. {
  41. _fInitializeCalled = TRUE;
  42. _pServiceHandler = pServiceHandler;
  43. _fStoppedOrStopping = FALSE;
  44. _dwCheckPoint = 0;
  45. // Register with the control dispatcher.
  46. _ssh = RegisterServiceCtrlHandlerEx(ptszServiceName, ServiceHandler, this );
  47. if (_ssh == 0)
  48. {
  49. TrkReportInternalError( THIS_FILE_NUMBER, __LINE__, HRESULT_FROM_WIN32(GetLastError()),
  50. ptszServiceName );
  51. TrkRaiseLastError();
  52. }
  53. // Go to the start-pending state.
  54. SetServiceStatus(SERVICE_START_PENDING, 0, NO_ERROR);
  55. }
  56. //+----------------------------------------------------------------------------
  57. //
  58. // CSvcCtrlInterface::ServiceHandler
  59. //
  60. // This method is called by the control dispatcher. If we get a stop
  61. // or shutdown request, automatically send a stop-pending before calling
  62. // the service's handler. Interrogate is handled automatically in
  63. // this routine without bothering to call the service.
  64. //
  65. //+----------------------------------------------------------------------------
  66. DWORD // static
  67. CSvcCtrlInterface::ServiceHandler(DWORD dwControl,
  68. DWORD dwEventType,
  69. PVOID EventData,
  70. PVOID pData)
  71. {
  72. // NOTE: In services.exe, this method is called on the one and only ServiceHandler
  73. // thread. So while we execute, no other service in this process can
  74. // receive notifications. Thus it is important that we do nothing
  75. // blocking or time-consuming here.
  76. DWORD dwRet = NO_ERROR;
  77. CSvcCtrlInterface *pThis = (CSvcCtrlInterface*)pData;
  78. #if DBG
  79. if( SERVICE_CONTROL_STOP == dwControl )
  80. TrkLog(( TRKDBG_SVR|TRKDBG_WKS, TEXT("\n") ));
  81. TrkLog(( TRKDBG_SVR|TRKDBG_WKS,
  82. TEXT("ServiceHandler(%s)"),
  83. StringizeServiceControl(dwControl) ));
  84. #endif
  85. // On a stop or shutdown, flag it (e.g. so we don't try to accept new
  86. // requests from clients) and tell the SCM that we're stopping.
  87. switch (dwControl)
  88. {
  89. case SERVICE_CONTROL_STOP:
  90. case SERVICE_CONTROL_SHUTDOWN:
  91. pThis->SetServiceStatus(SERVICE_STOP_PENDING, 0, NO_ERROR);
  92. _fStoppedOrStopping = TRUE;
  93. break;
  94. }
  95. // Check for PNP timing issues. The problem is that during a stop
  96. // or shutdown, we unregister with PNP so that we don't get any more
  97. // notifications. This is fine, except that between now and the time
  98. // we do that unregister, more PNP notifications might get queued. So
  99. // when we get called to process those undesired notifications, we need
  100. // to ignore them here in the static function.
  101. //
  102. // As a quick fix, since only trkwks receives PNP notifications, we'll
  103. // just check to see if it's alive. A better fix (raided) is to have
  104. // a static function for each service, so that only the trkwks has to
  105. // deal with it.
  106. if( SERVICE_CONTROL_DEVICEEVENT == dwControl && _fStoppedOrStopping )
  107. {
  108. TrkLog(( TRKDBG_WARNING, TEXT("Ignoring SERVICE_CONTROL_DEVICEEVENT; service is stopped") ));
  109. return dwRet;
  110. }
  111. // Call this service's service handler. As a final safety measure,
  112. // catch any exceptions (there should be none). We must be sure that we don't
  113. // kill this thread, since it's shared by everyone in services.exe.
  114. __try
  115. {
  116. dwRet = pThis->_pServiceHandler->ServiceHandler(dwControl, dwEventType, EventData, pData);
  117. switch (dwControl)
  118. {
  119. case SERVICE_CONTROL_STOP:
  120. case SERVICE_CONTROL_SHUTDOWN:
  121. break;
  122. case SERVICE_CONTROL_PAUSE:
  123. pThis->SetServiceStatus(SERVICE_PAUSED, pThis->_dwControlsAccepted, NO_ERROR);
  124. break;
  125. case SERVICE_CONTROL_CONTINUE:
  126. pThis->SetServiceStatus(SERVICE_RUNNING, pThis->_dwControlsAccepted, NO_ERROR);
  127. break;
  128. case SERVICE_CONTROL_INTERROGATE:
  129. pThis->SetServiceStatus(pThis->_dwState, pThis->_dwControlsAccepted, NO_ERROR);
  130. break;
  131. case SERVICE_CONTROL_DEVICEEVENT:
  132. break;
  133. }
  134. }
  135. __except( BREAK_THEN_RETURN( EXCEPTION_EXECUTE_HANDLER ))
  136. {
  137. TrkLog(( TRKDBG_ERROR,
  138. TEXT("Unexpected exception in CSvcCtrlInterface::ServiceHandler (%08x)"),
  139. GetExceptionCode() ));
  140. dwRet = ERROR_EXCEPTION_IN_SERVICE;
  141. }
  142. return dwRet;
  143. }
  144. //+----------------------------------------------------------------------------
  145. //
  146. // CSvcCtrlInterface::SetServiceStatus
  147. //
  148. // Send a SetServiceStatus to the SCM. The checkpoint is automatically
  149. // maintained by this class.
  150. //
  151. //+----------------------------------------------------------------------------
  152. void
  153. CSvcCtrlInterface::SetServiceStatus(DWORD dwState, DWORD dwControlsAccepted, DWORD dwWin32ExitCode)
  154. {
  155. SERVICE_STATUS ss;
  156. _dwState = dwState;
  157. _dwControlsAccepted = dwControlsAccepted;
  158. if( SERVICE_START_PENDING != dwState
  159. &&
  160. SERVICE_STOP_PENDING != dwState )
  161. {
  162. _dwCheckPoint = 0;
  163. }
  164. ss.dwServiceType = SERVICE_WIN32; // XX_SC
  165. ss.dwCurrentState = _dwState;
  166. ss.dwControlsAccepted = _dwControlsAccepted;
  167. ss.dwWin32ExitCode = dwWin32ExitCode;
  168. ss.dwServiceSpecificExitCode = 0;
  169. ss.dwCheckPoint = _dwCheckPoint++;
  170. ss.dwWaitHint = DEFAULT_WAIT_HINT;
  171. if (_ssh != 0)
  172. {
  173. if( !::SetServiceStatus(_ssh, &ss) )
  174. {
  175. TrkLog(( TRKDBG_ERROR, TEXT("SetServiceStatus(%s) failed, gle=%lu"),
  176. (const TCHAR*)CDebugString(SServiceState(dwState)), GetLastError() ));
  177. }
  178. else
  179. {
  180. TrkLog(( TRKDBG_MISC, TEXT("SetServiceStatus(%s)"),
  181. (const TCHAR*)CDebugString(SServiceState(dwState)) ));
  182. }
  183. }
  184. }
  185. //+----------------------------------------------------------------------------
  186. //
  187. // CSvcCtrlInterface::UpdateWaitHint
  188. //
  189. // Send a non-default wait hint to the SCM.
  190. //
  191. //+----------------------------------------------------------------------------
  192. void
  193. CSvcCtrlInterface::UpdateWaitHint(DWORD dwMilliseconds)
  194. {
  195. SERVICE_STATUS ss;
  196. ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  197. ss.dwCurrentState = _dwState;
  198. ss.dwControlsAccepted = _dwControlsAccepted;
  199. ss.dwWin32ExitCode = NO_ERROR;
  200. ss.dwServiceSpecificExitCode = 0;
  201. ss.dwCheckPoint = _dwCheckPoint++;
  202. ss.dwWaitHint = dwMilliseconds;
  203. if (_ssh != 0)
  204. ::SetServiceStatus(_ssh, &ss);
  205. }
  206. //+----------------------------------------------------------------------------
  207. //
  208. // StringizeServiceControl (debug only)
  209. //
  210. //+----------------------------------------------------------------------------
  211. #if DBG
  212. TCHAR * StringizeServiceControl( DWORD dwControl )
  213. {
  214. switch( dwControl )
  215. {
  216. case SERVICE_CONTROL_STOP:
  217. return TEXT("SERVICE_CONTROL_STOP");
  218. case SERVICE_CONTROL_PAUSE:
  219. return TEXT("SERVICE_CONTROL_PAUSE");
  220. case SERVICE_CONTROL_CONTINUE:
  221. return TEXT("SERVICE_CONTROL_CONTINUE");
  222. case SERVICE_CONTROL_INTERROGATE:
  223. return TEXT("SERVICE_CONTROL_INTERROGATE");
  224. case SERVICE_CONTROL_SHUTDOWN:
  225. return TEXT("SERVICE_CONTROL_SHUTDOWN");
  226. case SERVICE_CONTROL_PARAMCHANGE:
  227. return TEXT("SERVICE_CONTROL_PARAMCHANGE");
  228. case SERVICE_CONTROL_NETBINDADD:
  229. return TEXT("SERVICE_CONTROL_NETBINDADD");
  230. case SERVICE_CONTROL_NETBINDREMOVE:
  231. return TEXT("SERVICE_CONTROL_NETBINDREMOVE");
  232. case SERVICE_CONTROL_NETBINDENABLE:
  233. return TEXT("SERVICE_CONTROL_NETBINDENABLE");
  234. case SERVICE_CONTROL_NETBINDDISABLE:
  235. return TEXT("SERVICE_CONTROL_NETBINDDISABLE");
  236. case SERVICE_CONTROL_DEVICEEVENT:
  237. return TEXT("SERVICE_CONTROL_DEVICEEVENT");
  238. default:
  239. return TEXT("Unknown");
  240. }
  241. }
  242. #endif