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.

491 lines
12 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. SVCSLIB.C
  5. Abstract:
  6. Contains code for attaching services to the service controller process.
  7. This file contains the following functions:
  8. SvcStartLocalDispatcher
  9. SvcServiceEntry
  10. SvcLoadDllAndStartSvc
  11. DummyCtrlHandler
  12. AbortService
  13. Author:
  14. Dan Lafferty (danl) 25-Oct-1993
  15. Environment:
  16. User Mode - Win32
  17. Revision History:
  18. 25-Oct-1993 Danl
  19. created
  20. --*/
  21. //
  22. // INCLUDES
  23. //
  24. #include <scpragma.h>
  25. extern "C"
  26. {
  27. #include <nt.h>
  28. #include <ntrtl.h>
  29. #include <nturtl.h>
  30. }
  31. #include <windows.h>
  32. #include <winsvc.h> // Service control APIs
  33. #include <scdebug.h>
  34. #include <svcsp.h> // SVCS_ENTRY_POINT, SVCS_GLOBAL_DATA
  35. #include <scseclib.h> //
  36. #include <lmsname.h> // Lanman Service Names
  37. #include <ntrpcp.h> // Rpcp... function prototypes
  38. #include <svcslib.h> // SetupInProgress
  39. //--------------------------
  40. // Definitions and Typedefs
  41. //--------------------------
  42. #define THREAD_WAIT_TIMEOUT 100 // 100 msec timeout
  43. typedef struct _SVCDLL_TABLE_ENTRY {
  44. LPCWSTR lpServiceName;
  45. LPCWSTR lpDllName;
  46. LPCSTR lpServiceEntrypoint;
  47. }SVCDLL_TABLE_ENTRY, *PSVCDLL_TABLE_ENTRY;
  48. //
  49. // Storage for well-known SIDs. Passed to each service entry point.
  50. //
  51. SVCS_GLOBAL_DATA GlobalData;
  52. //--------------------------
  53. // FUNCTION PROTOTYPES
  54. //--------------------------
  55. VOID
  56. SvcServiceEntry ( // Ctrl Dispatcher calls here to start service.
  57. IN DWORD argc,
  58. IN LPTSTR *argv
  59. );
  60. VOID
  61. SvcLoadDllAndStartSvc ( // Loads and invokes service DLL
  62. IN CONST SVCDLL_TABLE_ENTRY * pDllEntry,
  63. IN DWORD argc,
  64. IN LPTSTR argv[]
  65. );
  66. VOID
  67. DummyCtrlHandler( // used if cant find Services Dll or entry pt.
  68. DWORD Opcode
  69. );
  70. VOID
  71. AbortService( // used if cant find Services Dll or entry pt.
  72. LPWSTR ServiceName,
  73. DWORD Error
  74. );
  75. VOID
  76. DispatcherThread(
  77. VOID
  78. );
  79. //--------------------------
  80. // GLOBALS
  81. //--------------------------
  82. //
  83. // Dispatch table for all services. Passed to StartServiceCtrlDispatcher.
  84. //
  85. // Add new service entries here and in the DLL name list.
  86. //
  87. const SERVICE_TABLE_ENTRY SvcServiceDispatchTable[] = {
  88. { L"EVENTLOG", SvcServiceEntry },
  89. { L"PlugPlay", SvcServiceEntry },
  90. //
  91. // Do NOT add new services here.
  92. //
  93. { NULL, NULL }
  94. };
  95. //
  96. // DLL names for all services.
  97. //
  98. const SVCDLL_TABLE_ENTRY SvcDllTable[] = {
  99. { L"EVENTLOG", L"eventlog.dll", "SvcEntry_Eventlog" },
  100. { L"PlugPlay", L"umpnpmgr.dll", "SvcEntry_PlugPlay" },
  101. //
  102. // Do NOT add new services here.
  103. //
  104. { NULL, NULL }
  105. };
  106. DWORD
  107. SvcStartLocalDispatcher(
  108. VOID
  109. )
  110. /*++
  111. Routine Description:
  112. This function initializes global data for the services to use, and
  113. then starts a thread for the service control dispatcher.
  114. Arguments:
  115. Return Value:
  116. NO_ERROR - If the dispatcher was started successfully.
  117. otherwise - Errors due to thread creation, or starting the dispatcher
  118. can be returned.
  119. --*/
  120. {
  121. DWORD status = NO_ERROR;
  122. DWORD waitStatus = NO_ERROR;
  123. DWORD threadId;
  124. HANDLE hThread;
  125. //
  126. // Populate the global data structure.
  127. //
  128. GlobalData.NullSid = NullSid;
  129. GlobalData.WorldSid = WorldSid;
  130. GlobalData.LocalSid = LocalSid;
  131. GlobalData.NetworkSid = NetworkSid;
  132. GlobalData.LocalSystemSid = LocalSystemSid;
  133. GlobalData.LocalServiceSid = LocalServiceSid;
  134. GlobalData.NetworkServiceSid = NetworkServiceSid;
  135. GlobalData.BuiltinDomainSid = BuiltinDomainSid;
  136. GlobalData.AuthenticatedUserSid = AuthenticatedUserSid;
  137. GlobalData.AliasAdminsSid = AliasAdminsSid;
  138. GlobalData.AliasUsersSid = AliasUsersSid;
  139. GlobalData.AliasGuestsSid = AliasGuestsSid;
  140. GlobalData.AliasPowerUsersSid = AliasPowerUsersSid;
  141. GlobalData.AliasAccountOpsSid = AliasAccountOpsSid;
  142. GlobalData.AliasSystemOpsSid = AliasSystemOpsSid;
  143. GlobalData.AliasPrintOpsSid = AliasPrintOpsSid;
  144. GlobalData.AliasBackupOpsSid = AliasBackupOpsSid;
  145. GlobalData.StartRpcServer = RpcpStartRpcServer;
  146. GlobalData.StopRpcServer = RpcpStopRpcServer;
  147. GlobalData.SvcsRpcPipeName = SVCS_RPC_PIPE;
  148. GlobalData.fSetupInProgress = SetupInProgress(NULL, NULL);
  149. //--------------------------------------------------
  150. // Create the thread for the dispatcher to run in.
  151. //--------------------------------------------------
  152. hThread = CreateThread (
  153. NULL, // Thread Attributes.
  154. 0L, // Stack Size
  155. (LPTHREAD_START_ROUTINE)DispatcherThread, // lpStartAddress
  156. NULL, // lpParameter
  157. 0L, // Creation Flags
  158. &threadId); // lpThreadId
  159. if (hThread == (HANDLE) NULL) {
  160. status = GetLastError();
  161. SC_LOG1(ERROR,"[SERVICES]CreateThread failed %d\n",status);
  162. return(status);
  163. }
  164. //
  165. // Wait on Thread handle for a moment to make sure the dispatcher is
  166. // running.
  167. //
  168. waitStatus = WaitForSingleObject(hThread, THREAD_WAIT_TIMEOUT);
  169. if (waitStatus != WAIT_TIMEOUT) {
  170. GetExitCodeThread(hThread, &status);
  171. }
  172. CloseHandle(hThread);
  173. return(status);
  174. }
  175. VOID
  176. SvcServiceEntry (
  177. IN DWORD argc,
  178. IN LPTSTR *argv
  179. )
  180. /*++
  181. Routine Description:
  182. This is the thunk routine for the Alerter service. It loads the DLL
  183. that contains the service and calls its main routine.
  184. Arguments:
  185. argc - Argument Count
  186. argv - Array of pointers to argument strings. The first is always
  187. the name of the service.
  188. Return Value:
  189. None.
  190. --*/
  191. {
  192. const SVCDLL_TABLE_ENTRY * pDllEntry = SvcDllTable;
  193. if (argc == 0) {
  194. SC_LOG0(ERROR,"[SERVICES]SvcServiceEntry: ServiceName was not passed in\n");
  195. return;
  196. }
  197. while (pDllEntry->lpServiceName != NULL) {
  198. if (_wcsicmp(pDllEntry->lpServiceName, argv[0]) == 0) {
  199. SC_LOG3(TRACE, "[SERVICES]SvcServiceEntry: "
  200. "Service = %ws, Dll = %ws, argv[0] = %ws\n",
  201. pDllEntry->lpServiceName, pDllEntry->lpDllName, argv[0]);
  202. SvcLoadDllAndStartSvc( pDllEntry, argc, argv );
  203. return;
  204. }
  205. pDllEntry++;
  206. }
  207. AbortService(argv[0], ERROR_MOD_NOT_FOUND);
  208. return;
  209. }
  210. VOID
  211. SvcLoadDllAndStartSvc (
  212. IN CONST SVCDLL_TABLE_ENTRY * pDllEntry,
  213. IN DWORD argc,
  214. IN LPTSTR argv[]
  215. )
  216. /*++
  217. Routine Description:
  218. This routine loads the DLL that contains a service and calls its
  219. main routine. Note that if a service is stopped and restarted,
  220. we simply call LoadLibrary again since it increments a refcount
  221. for already-loaded DLLs.
  222. Arguments:
  223. DllName - name of the DLL
  224. argc, argv - Passed through to the service
  225. Return Value:
  226. None.
  227. --*/
  228. {
  229. PSVCS_SERVICE_DLL_ENTRY serviceEntry;
  230. HINSTANCE dllHandle = NULL;
  231. DWORD Error;
  232. //
  233. // Load the DLL that contains the service.
  234. //
  235. dllHandle = LoadLibrary( pDllEntry->lpDllName );
  236. if ( dllHandle == NULL ) {
  237. Error = GetLastError();
  238. SC_LOG2(ERROR,
  239. "SERVICES: Failed to load DLL %ws: %ld\n",
  240. pDllEntry->lpDllName,
  241. Error);
  242. AbortService(argv[0], Error);
  243. return;
  244. }
  245. //
  246. // Get the address of the service's main entry point. First try the
  247. // new, servicename-specific entrypoint naming scheme
  248. //
  249. serviceEntry = (PSVCS_SERVICE_DLL_ENTRY)GetProcAddress(
  250. dllHandle,
  251. pDllEntry->lpServiceEntrypoint
  252. );
  253. if (serviceEntry == NULL) {
  254. SC_LOG3(TRACE,
  255. "SERVICES: Can't find entry %s in DLL %ws: %ld\n",
  256. pDllEntry->lpServiceEntrypoint,
  257. pDllEntry->lpDllName,
  258. GetLastError());
  259. //
  260. // That didn't work -- let's try the well-known entrypoint
  261. //
  262. serviceEntry = (PSVCS_SERVICE_DLL_ENTRY)GetProcAddress(
  263. dllHandle,
  264. SVCS_ENTRY_POINT_STRING
  265. );
  266. if ( serviceEntry == NULL ) {
  267. Error = GetLastError();
  268. SC_LOG3(ERROR,
  269. "SERVICES: Can't find entry %s in DLL %ws: %ld\n",
  270. SVCS_ENTRY_POINT_STRING,
  271. pDllEntry->lpDllName,
  272. Error);
  273. AbortService(argv[0], Error);
  274. return;
  275. }
  276. }
  277. //
  278. // We found the service's main entry point -- call it.
  279. //
  280. serviceEntry( argc, argv, &GlobalData, NULL);
  281. return;
  282. } // SvcLoadDllAndStartSvc
  283. VOID
  284. DummyCtrlHandler(
  285. DWORD Opcode
  286. )
  287. /*++
  288. Routine Description:
  289. This is a dummy control handler which is only used if we can't load
  290. a services DLL entry point. Then we need this so we can send the
  291. status back to the service controller saying we are stopped, and why.
  292. Arguments:
  293. OpCode - Ignored
  294. Return Value:
  295. None.
  296. --*/
  297. {
  298. return;
  299. } // DummyCtrlHandler
  300. VOID
  301. AbortService(
  302. LPWSTR ServiceName,
  303. DWORD Error)
  304. /*++
  305. Routine Description:
  306. This is called if we can't load the entry point for a service. It
  307. gets a handle so it can call SetServiceStatus saying we are stopped
  308. and why.
  309. Arguments:
  310. ServiceName - the name of the service that couldn't be started
  311. Error - the reason it couldn't be started
  312. Return Value:
  313. None.
  314. --*/
  315. {
  316. SERVICE_STATUS_HANDLE GenericServiceStatusHandle;
  317. SERVICE_STATUS GenericServiceStatus;
  318. GenericServiceStatus.dwServiceType = SERVICE_WIN32;
  319. GenericServiceStatus.dwCurrentState = SERVICE_STOPPED;
  320. GenericServiceStatus.dwControlsAccepted = SERVICE_CONTROL_STOP;
  321. GenericServiceStatus.dwCheckPoint = 0;
  322. GenericServiceStatus.dwWaitHint = 0;
  323. GenericServiceStatus.dwWin32ExitCode = Error;
  324. GenericServiceStatus.dwServiceSpecificExitCode = 0;
  325. GenericServiceStatusHandle = RegisterServiceCtrlHandler(
  326. ServiceName,
  327. DummyCtrlHandler);
  328. if (GenericServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) {
  329. SC_LOG1(ERROR,"[SERVICES] RegisterServiceCtrlHandler failed %d\n",
  330. GetLastError());
  331. }
  332. else if (!SetServiceStatus (GenericServiceStatusHandle,
  333. &GenericServiceStatus)) {
  334. SC_LOG1(ERROR,"[SERVICES] SetServiceStatus error %ld\n", GetLastError());
  335. }
  336. return;
  337. }
  338. VOID
  339. DispatcherThread(
  340. VOID
  341. )
  342. /*++
  343. Routine Description:
  344. Arguments:
  345. Return Value:
  346. --*/
  347. {
  348. DWORD status=NO_ERROR;
  349. //
  350. // Call StartServiceCtrlDispatcher to set up the control interface.
  351. // The API won't return until all services have been terminated. At that
  352. // point, we just exit.
  353. //
  354. if (! StartServiceCtrlDispatcher (
  355. SvcServiceDispatchTable
  356. )) {
  357. status = GetLastError();
  358. SC_LOG1(ERROR, "SERVICES: Failed to start control dispatcher %lu\n",
  359. status);
  360. }
  361. ExitThread(status);
  362. }