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.

2806 lines
85 KiB

  1. /*++
  2. Copyright (c) 1990-1992 Microsoft Corporation
  3. Module Name:
  4. scapi.c
  5. Abstract:
  6. Contains the Service-related API that are implemented solely in
  7. DLL form. These include:
  8. StartServiceCtrlDispatcherA
  9. StartServiceCtrlDispatcherW
  10. RegisterServiceCtrlHandlerW
  11. RegisterServiceCtrlHandlerA
  12. RegisterServiceCtrlHandlerExW
  13. RegisterServiceCtrlHandlerExA
  14. This file also contains the following local support routines:
  15. ScDispatcherLoop
  16. ScCreateDispatchTableW
  17. ScCreateDispatchTableA
  18. ScReadServiceParms
  19. ScConnectServiceController
  20. ScExpungeMessage
  21. ScGetPipeInput
  22. ScGetDispatchEntry
  23. ScNormalizeCmdLineArgs
  24. ScSendResponse
  25. ScSvcctrlThreadW
  26. ScSvcctrlThreadA
  27. RegisterServiceCtrlHandlerHelp
  28. Author:
  29. Dan Lafferty (danl) 09 Apr-1991
  30. Environment:
  31. User Mode -Win32
  32. Revision History:
  33. 12-May-1999 jschwart
  34. Convert SERVICE_STATUS_HANDLE to a context handle to fix security
  35. hole (any service can call SetServiceStatus with another service's
  36. handle and succeed)
  37. 10-May-1999 jschwart
  38. Create a separate named pipe per service to fix security hole (any
  39. process could flood the pipe since the name was well-known -- pipe
  40. access is now limited to the service account and LocalSystem)
  41. 10-Mar-1998 jschwart
  42. Add code to allow services to receive Plug-and-Play control messages
  43. and unpack the message arguments from the pipe
  44. 30-Sep-1997 jschwart
  45. StartServiceCtrlDispatcher: If this function [A or W] has already been
  46. called from within this process, return ERROR_SERVICE_ALREADY_RUNNING.
  47. Otherwise, can be destructive (e.g., overwrite AnsiFlag, etc.)
  48. 06-Aug-1997 jschwart
  49. ScDispatcherLoop: If the service is processing a shutdown command from
  50. the SCM, it no longer writes the status back to the SCM since the SCM is
  51. now using WriteFile on system shutdown (see control.cxx for more info).
  52. 03-Jun-1996 AnirudhS
  53. ScGetPipeInput: If the message received from the service controller
  54. is not a SERVICE_CONTROL_START message, don't allocate space for the
  55. arguments, since there are none, and since that space never gets freed.
  56. 22-Sep-1995 AnirudhS
  57. Return codes from InitializeStatusBinding were not being handled
  58. correctly; success was sometimes reported on failure. Fixed this.
  59. 12-Aug-1993 Danl
  60. ScGetDispatchEntry: When the first entry in the table is marked as
  61. OwnProcess, then this function should just return the pointer to the
  62. top of the table. It should ignore the ServiceName. In all cases,
  63. when the service is started as an OWN_PROCESS, only the first entry
  64. in the dispath table should be used.
  65. 04-Aug-1992 Danl
  66. When starting a service, always pass the service name as the
  67. first parameter in the argument list.
  68. 27-May-1992 JohnRo
  69. RAID 9829: winsvc.h and related file cleanup.
  70. 09 Apr-1991 danl
  71. created
  72. --*/
  73. //
  74. // INCLUDES
  75. //
  76. #include <scpragma.h>
  77. extern "C"
  78. {
  79. #include <nt.h> // DbgPrint prototype
  80. #include <ntrtl.h> // DbgPrint prototype
  81. #include <nturtl.h> // needed for winbase.h
  82. }
  83. #include <rpc.h> // DataTypes and runtime APIs
  84. #include <windef.h> // windows types needed for winbase.h
  85. #include <winbase.h> // CreateFile
  86. #include <winuser.h> // wsprintf()
  87. #include <winreg.h> // RegOpenKeyEx / RegQueryValueEx
  88. #include <string.h> // strcmp
  89. #include <stdlib.h> // wide character c runtimes.
  90. #include <tstr.h> // WCSSIZE().
  91. #include <winsvc.h> // public Service Controller Interface.
  92. #include "scbind.h" // InitializeStatusBinding
  93. #include <valid.h> // MAX_SERVICE_NAME_LENGTH
  94. #include <control.h> // CONTROL_PIPE_NAME
  95. #include <scdebug.h> // STATIC
  96. #include <sclib.h> // ScConvertToUnicode
  97. //
  98. // Internal Dispatch Table.
  99. //
  100. //
  101. // Bit flags for the dispatch table's dwFlags field
  102. //
  103. #define SERVICE_OWN_PROCESS 0x00000001
  104. #define SERVICE_EX_HANDLER 0x00000002
  105. typedef union _START_ROUTINE_TYPE {
  106. LPSERVICE_MAIN_FUNCTIONW U; // unicode type
  107. LPSERVICE_MAIN_FUNCTIONA A; // ansi type
  108. } START_ROUTINE_TYPE, *LPSTART_ROUTINE_TYPE;
  109. typedef union _HANDLER_FUNCTION_TYPE {
  110. LPHANDLER_FUNCTION Regular; // Regular version
  111. LPHANDLER_FUNCTION_EX Ex; // Extended version
  112. } HANDLER_FUNCTION_TYPE, *LPHANDLER_FUNCTION_TYPE;
  113. typedef struct _INTERNAL_DISPATCH_ENTRY {
  114. LPWSTR ServiceName;
  115. LPWSTR ServiceRealName; // In case the names are different
  116. START_ROUTINE_TYPE ServiceStartRoutine;
  117. HANDLER_FUNCTION_TYPE ControlHandler;
  118. SC_HANDLE StatusHandle;
  119. DWORD dwFlags;
  120. PVOID pContext;
  121. } INTERNAL_DISPATCH_ENTRY, *LPINTERNAL_DISPATCH_ENTRY;
  122. //
  123. // This structure is passed to the internal
  124. // startup thread which calls the real user
  125. // startup routine with argv, argc parameters.
  126. //
  127. typedef struct _THREAD_STARTUP_PARMSW {
  128. DWORD NumArgs;
  129. LPSERVICE_MAIN_FUNCTIONW ServiceStartRoutine;
  130. LPWSTR VectorTable;
  131. } THREAD_STARTUP_PARMSW, *LPTHREAD_STARTUP_PARMSW;
  132. typedef struct _THREAD_STARTUP_PARMSA {
  133. DWORD NumArgs;
  134. LPSERVICE_MAIN_FUNCTIONA ServiceStartRoutine;
  135. LPSTR VectorTable;
  136. } THREAD_STARTUP_PARMSA, *LPTHREAD_STARTUP_PARMSA;
  137. //
  138. // This structure contains the arguments passed
  139. // to a service's extended control handler
  140. //
  141. typedef struct _HANDLEREX_PARMS {
  142. DWORD dwEventType;
  143. LPVOID lpEventData;
  144. } HANDLEREX_PARMS, *LPHANDLEREX_PARMS;
  145. //
  146. // This union contains the arguments to the service
  147. // passed from the server via named pipe
  148. //
  149. typedef union _SERVICE_PARAMS {
  150. THREAD_STARTUP_PARMSW ThreadStartupParms;
  151. HANDLEREX_PARMS HandlerExParms;
  152. } SERVICE_PARAMS, *LPSERVICE_PARAMS;
  153. //
  154. // The following is the amount of time we will wait for the named pipe
  155. // to become available from the Service Controller.
  156. //
  157. #ifdef DEBUG
  158. #define CONTROL_WAIT_PERIOD NMPWAIT_WAIT_FOREVER
  159. #else
  160. #define CONTROL_WAIT_PERIOD 15000 // 15 seconds
  161. #endif
  162. //
  163. // This is the number of times we will continue to loop when pipe read
  164. // failures occur. After this many tries, we cease to read the pipe.
  165. //
  166. #define MAX_RETRY_COUNT 30
  167. //
  168. // Globals
  169. //
  170. LPINTERNAL_DISPATCH_ENTRY DispatchTable=NULL; // table head.
  171. //
  172. // This flag is set to TRUE if the control dispatcher is to support
  173. // ANSI calls. Otherwise the flag is set to FALSE.
  174. //
  175. BOOL AnsiFlag = FALSE;
  176. //
  177. // This variable makes sure StartServiceCtrlDispatcher[A,W] doesn't
  178. // get called twice by the same process, since this is destructive.
  179. // It is initialized to 0 by the linker
  180. //
  181. LONG g_fCalledBefore;
  182. //
  183. // Are we running in the security process?
  184. //
  185. BOOL g_fIsSecProc;
  186. #if defined(_X86_)
  187. //
  188. // Are we running inside a Wow64 process (on Win64)
  189. //
  190. BOOL g_fWow64Process = FALSE;
  191. #endif
  192. //
  193. // Internal Functions
  194. //
  195. DWORD
  196. ScCreateDispatchTableW(
  197. IN CONST SERVICE_TABLE_ENTRYW *UserDispatchTable,
  198. OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
  199. );
  200. DWORD
  201. ScCreateDispatchTableA(
  202. IN CONST SERVICE_TABLE_ENTRYA *UserDispatchTable,
  203. OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
  204. );
  205. DWORD
  206. ScReadServiceParms(
  207. IN LPCTRL_MSG_HEADER Msg,
  208. IN DWORD dwNumBytesRead,
  209. OUT LPBYTE *ppServiceParams,
  210. OUT LPBYTE *ppTempArgPtr,
  211. OUT LPDWORD lpdwRemainingArgBytes
  212. );
  213. VOID
  214. ScDispatcherLoop(
  215. IN HANDLE PipeHandle,
  216. IN LPCTRL_MSG_HEADER Msg,
  217. IN DWORD dwBufferSize
  218. );
  219. DWORD
  220. ScConnectServiceController (
  221. OUT LPHANDLE pipeHandle
  222. );
  223. VOID
  224. ScExpungeMessage(
  225. IN HANDLE PipeHandle
  226. );
  227. DWORD
  228. ScGetPipeInput (
  229. IN HANDLE PipeHandle,
  230. IN OUT LPCTRL_MSG_HEADER Msg,
  231. IN DWORD dwBufferSize,
  232. OUT LPSERVICE_PARAMS *ppServiceParams
  233. );
  234. DWORD
  235. ScGetDispatchEntry (
  236. OUT LPINTERNAL_DISPATCH_ENTRY *DispatchEntry,
  237. IN LPWSTR ServiceName
  238. );
  239. VOID
  240. ScNormalizeCmdLineArgs(
  241. IN OUT LPCTRL_MSG_HEADER Msg,
  242. IN OUT LPTHREAD_STARTUP_PARMSW ThreadStartupParms
  243. );
  244. VOID
  245. ScSendResponse (
  246. IN HANDLE pipeHandle,
  247. IN DWORD Response,
  248. IN DWORD dwHandlerRetVal
  249. );
  250. DWORD
  251. ScSvcctrlThreadW(
  252. IN LPTHREAD_STARTUP_PARMSW lpThreadStartupParms
  253. );
  254. DWORD
  255. ScSvcctrlThreadA(
  256. IN LPTHREAD_STARTUP_PARMSA lpThreadStartupParms
  257. );
  258. SERVICE_STATUS_HANDLE
  259. WINAPI
  260. RegisterServiceCtrlHandlerHelp (
  261. IN LPCWSTR ServiceName,
  262. IN HANDLER_FUNCTION_TYPE ControlHandler,
  263. IN PVOID pContext,
  264. IN DWORD dwFlags
  265. );
  266. #if defined(_X86_)
  267. //
  268. // Detect if the current process is a 32-bit process running on Win64.
  269. //
  270. VOID DetectWow64Process()
  271. {
  272. NTSTATUS NtStatus;
  273. PVOID Peb32;
  274. NtStatus = NtQueryInformationProcess(NtCurrentProcess(),
  275. ProcessWow64Information,
  276. &Peb32,
  277. sizeof(Peb32),
  278. NULL);
  279. if (NT_SUCCESS(NtStatus) && (Peb32 != NULL))
  280. {
  281. g_fWow64Process = TRUE;
  282. }
  283. }
  284. #endif
  285. extern "C" {
  286. //
  287. // Private function for lsass.exe that tells us we're running
  288. // in the security process
  289. //
  290. VOID
  291. I_ScIsSecurityProcess(
  292. VOID
  293. )
  294. {
  295. g_fIsSecProc = TRUE;
  296. }
  297. //
  298. // Private function for PnP that looks up a service's REAL
  299. // name given its context handle
  300. //
  301. DWORD
  302. I_ScPnPGetServiceName(
  303. IN SERVICE_STATUS_HANDLE hServiceStatus,
  304. OUT LPWSTR lpServiceName,
  305. IN DWORD cchBufSize
  306. )
  307. {
  308. DWORD dwError = ERROR_SERVICE_NOT_IN_EXE;
  309. ASSERT(cchBufSize >= MAX_SERVICE_NAME_LENGTH);
  310. //
  311. // Search the dispatch table.
  312. //
  313. if (DispatchTable != NULL)
  314. {
  315. LPINTERNAL_DISPATCH_ENTRY dispatchEntry;
  316. for (dispatchEntry = DispatchTable;
  317. dispatchEntry->ServiceName != NULL;
  318. dispatchEntry++)
  319. {
  320. //
  321. // Note: SC_HANDLE and SERVICE_STATUS_HANDLE were originally
  322. // different handle types -- they are now the same as
  323. // per the fix for bug #120359 (SERVICE_STATUS_HANDLE
  324. // fix outlined in the file comments above), so this
  325. // cast is OK.
  326. //
  327. if (dispatchEntry->StatusHandle == (SC_HANDLE)hServiceStatus)
  328. {
  329. ASSERT(dispatchEntry->ServiceRealName != NULL);
  330. ASSERT(wcslen(dispatchEntry->ServiceRealName) < cchBufSize);
  331. wcscpy(lpServiceName, dispatchEntry->ServiceRealName);
  332. dwError = NO_ERROR;
  333. break;
  334. }
  335. }
  336. }
  337. return dwError;
  338. }
  339. } // extern "C"
  340. BOOL
  341. WINAPI
  342. StartServiceCtrlDispatcherA (
  343. IN CONST SERVICE_TABLE_ENTRYA * lpServiceStartTable
  344. )
  345. /*++
  346. Routine Description:
  347. This function provides the ANSI interface for the
  348. StartServiceCtrlDispatcher function.
  349. Arguments:
  350. Return Value:
  351. --*/
  352. {
  353. DWORD status;
  354. NTSTATUS ntstatus;
  355. HANDLE pipeHandle;
  356. //
  357. // Make sure this is the only time the control dispatcher is being called
  358. //
  359. if (InterlockedExchange(&g_fCalledBefore, 1) == 1) {
  360. //
  361. // Problem -- the control dispatcher was already called from this process
  362. //
  363. SetLastError(ERROR_SERVICE_ALREADY_RUNNING);
  364. return(FALSE);
  365. }
  366. //
  367. // Set the AnsiFlag to indicate that the control dispatcher must support
  368. // ansi function calls only.
  369. //
  370. AnsiFlag = TRUE;
  371. //
  372. // Create an internal DispatchTable.
  373. //
  374. status = ScCreateDispatchTableA(
  375. (LPSERVICE_TABLE_ENTRYA)lpServiceStartTable,
  376. (LPINTERNAL_DISPATCH_ENTRY *)&DispatchTable);
  377. if (status != NO_ERROR) {
  378. SetLastError(status);
  379. return(FALSE);
  380. }
  381. //
  382. // Allocate a buffer big enough to contain at least the control message
  383. // header and a service name. This ensures that if the message is not
  384. // a message with arguments, it can be read in a single ReadFile.
  385. //
  386. BYTE bPipeMessageHeader[sizeof(CTRL_MSG_HEADER) +
  387. (MAX_SERVICE_NAME_LENGTH+1) * sizeof(WCHAR)];
  388. //
  389. // Connect to the Service Controller
  390. //
  391. status = ScConnectServiceController (&pipeHandle);
  392. if (status != NO_ERROR) {
  393. goto CleanExit;
  394. }
  395. //
  396. // Initialize the binding for the status interface (NetServiceStatus).
  397. //
  398. SCC_LOG(TRACE,"Initialize the Status binding\n",0);
  399. ntstatus = InitializeStatusBinding();
  400. if (ntstatus != STATUS_SUCCESS) {
  401. status = RtlNtStatusToDosError(ntstatus);
  402. CloseHandle(pipeHandle);
  403. goto CleanExit;
  404. }
  405. //
  406. // Enter the dispatcher loop where we service control requests until
  407. // all services in the service table have terminated.
  408. //
  409. ScDispatcherLoop(pipeHandle,
  410. (LPCTRL_MSG_HEADER)&bPipeMessageHeader,
  411. sizeof(bPipeMessageHeader));
  412. CloseHandle(pipeHandle);
  413. CleanExit:
  414. //
  415. // Clean up the dispatch table. Since we created unicode versions
  416. // of all the service names, in ScCreateDispatchTableA, we now need to
  417. // free them.
  418. //
  419. if (DispatchTable != NULL) {
  420. LPINTERNAL_DISPATCH_ENTRY dispatchEntry;
  421. //
  422. // If they're different, it's because we allocated the real name.
  423. // Only check the first entry since this can only happen in the
  424. // SERVICE_OWN_PROCESS case
  425. //
  426. if (DispatchTable->ServiceName != DispatchTable->ServiceRealName) {
  427. LocalFree(DispatchTable->ServiceRealName);
  428. }
  429. //
  430. // Do not free the dispatch table and the service name field if this
  431. // API is successful. This is because after exiting this API successfully on the last stop
  432. // service, a service can still be in the SetServiceStatus API at ScRemoveDispatchEntry.
  433. // That function walks the dispatch table and touches the ServiceName and StatusHandle
  434. // fields in the table.
  435. //
  436. if (status != NO_ERROR) {
  437. for (dispatchEntry = DispatchTable;
  438. dispatchEntry->ServiceName != NULL;
  439. dispatchEntry++) {
  440. LocalFree(dispatchEntry->ServiceName);
  441. }
  442. LocalFree(DispatchTable);
  443. }
  444. }
  445. if (status != NO_ERROR) {
  446. SetLastError(status);
  447. return(FALSE);
  448. }
  449. return(TRUE);
  450. }
  451. BOOL
  452. WINAPI
  453. StartServiceCtrlDispatcherW (
  454. IN CONST SERVICE_TABLE_ENTRYW * lpServiceStartTable
  455. )
  456. /*++
  457. Routine Description:
  458. This is the Control Dispatcher thread. We do not return from this
  459. function call until the Control Dispatcher is told to shut down.
  460. The Control Dispatcher is responsible for connecting to the Service
  461. Controller's control pipe, and receiving messages from that pipe.
  462. The Control Dispatcher then dispatches the control messages to the
  463. correct control handling routine.
  464. Arguments:
  465. lpServiceStartTable - This is a pointer to the top of a service dispatch
  466. table that the service main process passes in. Each table entry
  467. contains pointers to the ServiceName, and the ServiceStartRotuine.
  468. Return Value:
  469. NO_ERROR - The Control Dispatcher successfully terminated.
  470. ERROR_INVALID_DATA - The specified dispatch table does not contain
  471. entries in the proper format.
  472. ERROR_FAILED_SERVICE_CONTROLLER_CONNECT - The Control Dispatcher
  473. could not connect with the Service Controller.
  474. ERROR_SERVICE_ALREADY_RUNNING - The function has already been called
  475. from within the current process
  476. --*/
  477. {
  478. DWORD status;
  479. NTSTATUS ntStatus;
  480. HANDLE pipeHandle;
  481. //
  482. // Make sure this is the only time the control dispatcher is being called
  483. //
  484. if (InterlockedExchange(&g_fCalledBefore, 1) == 1) {
  485. //
  486. // Problem -- the control dispatcher was already called from this process
  487. //
  488. SetLastError(ERROR_SERVICE_ALREADY_RUNNING);
  489. return(FALSE);
  490. }
  491. //
  492. // Create the Real Dispatch Table
  493. //
  494. __try {
  495. status = ScCreateDispatchTableW((LPSERVICE_TABLE_ENTRYW)lpServiceStartTable, &DispatchTable);
  496. }
  497. __except (EXCEPTION_EXECUTE_HANDLER) {
  498. status = GetExceptionCode();
  499. if (status != EXCEPTION_ACCESS_VIOLATION) {
  500. SCC_LOG(ERROR,"StartServiceCtrlDispatcherW:Unexpected Exception 0x%lx\n",status);
  501. }
  502. }
  503. if (status != NO_ERROR) {
  504. SetLastError(status);
  505. return(FALSE);
  506. }
  507. //
  508. // Allocate a buffer big enough to contain at least the control message
  509. // header and a service name. This ensures that if the message is not
  510. // a message with arguments, it can be read in a single ReadFile.
  511. //
  512. BYTE bPipeMessageHeader[sizeof(CTRL_MSG_HEADER) +
  513. (MAX_SERVICE_NAME_LENGTH+1) * sizeof(WCHAR)];
  514. //
  515. // Connect to the Service Controller
  516. //
  517. status = ScConnectServiceController(&pipeHandle);
  518. if (status != NO_ERROR) {
  519. //
  520. // If they're different, it's because we allocated the real name.
  521. // Only check the first entry since this can only happen in the
  522. // SERVICE_OWN_PROCESS case
  523. //
  524. if (DispatchTable->ServiceName != DispatchTable->ServiceRealName) {
  525. LocalFree(DispatchTable->ServiceRealName);
  526. }
  527. LocalFree(DispatchTable);
  528. SetLastError(status);
  529. return(FALSE);
  530. }
  531. //
  532. // Initialize the binding for the status interface (NetServiceStatus).
  533. //
  534. SCC_LOG(TRACE,"Initialize the Status binding\n",0);
  535. ntStatus = InitializeStatusBinding();
  536. if (ntStatus != STATUS_SUCCESS) {
  537. status = RtlNtStatusToDosError(ntStatus);
  538. CloseHandle(pipeHandle);
  539. //
  540. // If they're different, it's because we allocated the real name.
  541. // Only check the first entry since this can only happen in the
  542. // SERVICE_OWN_PROCESS case
  543. //
  544. if (DispatchTable->ServiceName != DispatchTable->ServiceRealName) {
  545. LocalFree(DispatchTable->ServiceRealName);
  546. }
  547. LocalFree(DispatchTable);
  548. SetLastError(status);
  549. return(FALSE);
  550. }
  551. //
  552. // Enter the dispatcher loop where we service control requests until
  553. // all services in the service table have terminated.
  554. //
  555. ScDispatcherLoop(pipeHandle,
  556. (LPCTRL_MSG_HEADER)&bPipeMessageHeader,
  557. sizeof(bPipeMessageHeader));
  558. CloseHandle(pipeHandle);
  559. //
  560. // If they're different, it's because we allocated the real name.
  561. // Only check the first entry since this can only happen in the
  562. // SERVICE_OWN_PROCESS case
  563. //
  564. if (DispatchTable->ServiceName != DispatchTable->ServiceRealName) {
  565. LocalFree(DispatchTable->ServiceRealName);
  566. }
  567. return(TRUE);
  568. }
  569. VOID
  570. ScDispatcherLoop(
  571. IN HANDLE PipeHandle,
  572. IN LPCTRL_MSG_HEADER Msg,
  573. IN DWORD dwBufferSize
  574. )
  575. /*++
  576. Routine Description:
  577. This is the input loop that the Control Dispatcher stays in through-out
  578. its life. Only two types of events will cause us to leave this loop:
  579. 1) The service controller instructed the dispatcher to exit.
  580. 2) The dispatcher can no longer communicate with the the
  581. service controller.
  582. Arguments:
  583. PipeHandle: This is a handle to the pipe over which control
  584. requests are received.
  585. Return Value:
  586. none
  587. --*/
  588. {
  589. DWORD status;
  590. DWORD controlStatus;
  591. BOOL continueDispatch;
  592. LPWSTR serviceName = NULL;
  593. LPSERVICE_PARAMS lpServiceParams = NULL;
  594. LPTHREAD_START_ROUTINE threadAddress = NULL;
  595. LPVOID threadParms = NULL;
  596. LPTHREAD_STARTUP_PARMSA lpspAnsiParms;
  597. LPINTERNAL_DISPATCH_ENTRY dispatchEntry = NULL;
  598. DWORD threadId;
  599. HANDLE threadHandle;
  600. DWORD i;
  601. DWORD errorCount = 0;
  602. DWORD dwHandlerRetVal = NO_ERROR;
  603. BOOL fCreatedThread;
  604. BOOL fOpenedService;
  605. //
  606. // Input Loop
  607. //
  608. continueDispatch = TRUE;
  609. #if defined(_X86_)
  610. //
  611. // Detect if this is a Wow64 Process ?
  612. //
  613. DetectWow64Process();
  614. #endif
  615. do
  616. {
  617. status = NO_ERROR;
  618. fCreatedThread = FALSE;
  619. fOpenedService = FALSE;
  620. //
  621. // Wait for input
  622. //
  623. controlStatus = ScGetPipeInput(PipeHandle,
  624. Msg,
  625. dwBufferSize,
  626. &lpServiceParams);
  627. //
  628. // If we received good input, check to see if we are to shut down
  629. // the ControlDispatcher. If not, then obtain the dispatchEntry
  630. // from the dispatch table.
  631. //
  632. if (controlStatus == NO_ERROR)
  633. {
  634. //
  635. // Clear the error count
  636. //
  637. errorCount = 0;
  638. serviceName = (LPWSTR) ((LPBYTE)Msg + Msg->ServiceNameOffset);
  639. SCC_LOG(TRACE, "Read from pipe succeeded for service %ws\n", serviceName);
  640. if ((serviceName[0] == L'\0') &&
  641. (Msg->OpCode == SERVICE_STOP))
  642. {
  643. //
  644. // The Dispatcher is being asked to shut down.
  645. // (security check not required for this operation)
  646. // although perhaps it would be a good idea to verify
  647. // that the request came from the Service Controller.
  648. //
  649. controlStatus = NO_ERROR;
  650. continueDispatch = FALSE;
  651. }
  652. else
  653. {
  654. dispatchEntry = DispatchTable;
  655. if (Msg->OpCode == SERVICE_CONTROL_START_OWN)
  656. {
  657. dispatchEntry->dwFlags |= SERVICE_OWN_PROCESS;
  658. }
  659. //
  660. // Search the dispatch table to find the service's entry
  661. //
  662. if (!(dispatchEntry->dwFlags & SERVICE_OWN_PROCESS))
  663. {
  664. controlStatus = ScGetDispatchEntry(&dispatchEntry, serviceName);
  665. }
  666. if (controlStatus != NO_ERROR)
  667. {
  668. SCC_LOG(TRACE,"Service Name not in Dispatch Table\n",0);
  669. }
  670. }
  671. }
  672. else
  673. {
  674. if (controlStatus != ERROR_NOT_ENOUGH_MEMORY)
  675. {
  676. //
  677. // If an error occured and it is not an out-of-memory error,
  678. // then the pipe read must have failed.
  679. // In this case we Increment the error count.
  680. // When this count reaches the MAX_RETRY_COUNT, then
  681. // the service controller must be gone. We want to log an
  682. // error and notify an administrator. Then go to sleep forever.
  683. // Only a re-boot will solve this problem.
  684. //
  685. // We should be able to report out-of-memory errors back to
  686. // the caller. It should be noted that out-of-memory errors
  687. // do not clear the error count. But they don't add to it
  688. // either.
  689. //
  690. errorCount++;
  691. if (errorCount > MAX_RETRY_COUNT)
  692. {
  693. Sleep(0xffffffff);
  694. }
  695. }
  696. //
  697. // Service params aren't allocated -- avoid freeing them later
  698. //
  699. lpServiceParams = NULL;
  700. }
  701. //
  702. // Dispatch the request
  703. //
  704. if ((continueDispatch == TRUE) && (controlStatus == NO_ERROR))
  705. {
  706. switch(Msg->OpCode)
  707. {
  708. case SERVICE_CONTROL_START_SHARE:
  709. case SERVICE_CONTROL_START_OWN:
  710. {
  711. SC_HANDLE hScManager = OpenSCManagerW(NULL,
  712. NULL,
  713. SC_MANAGER_CONNECT);
  714. if (hScManager == NULL)
  715. {
  716. status = GetLastError();
  717. SCC_LOG1(ERROR,
  718. "ScDispatcherLoop: OpenSCManagerW FAILED %d\n",
  719. status);
  720. }
  721. else
  722. {
  723. //
  724. // Update the StatusHandle in the dispatch entry table
  725. //
  726. dispatchEntry->StatusHandle = OpenServiceW(hScManager,
  727. serviceName,
  728. SERVICE_SET_STATUS);
  729. if (dispatchEntry->StatusHandle == NULL)
  730. {
  731. status = GetLastError();
  732. SCC_LOG1(ERROR,
  733. "ScDispatcherLoop: OpenServiceW FAILED %d\n",
  734. status);
  735. }
  736. else
  737. {
  738. fOpenedService = TRUE;
  739. }
  740. CloseServiceHandle(hScManager);
  741. }
  742. if (status == NO_ERROR
  743. &&
  744. (dispatchEntry->dwFlags & SERVICE_OWN_PROCESS)
  745. &&
  746. (_wcsicmp(dispatchEntry->ServiceName, serviceName) != 0))
  747. {
  748. //
  749. // Since we don't look up the dispatch record in the OWN_PROCESS
  750. // case (and can't since it will break existing services), there's
  751. // no guarantee that the name in the dispatch table (acquired from
  752. // the RegisterServiceCtrlHandler call) is the real key name of
  753. // the service. Since the SCM passes across the real name when
  754. // the service is started, save it away here if necessary.
  755. //
  756. dispatchEntry->ServiceRealName = (LPWSTR)LocalAlloc(
  757. LMEM_FIXED,
  758. WCSSIZE(serviceName)
  759. );
  760. if (dispatchEntry->ServiceRealName == NULL)
  761. {
  762. //
  763. // In case somebody comes searching for the handle (though
  764. // they shouldn't), it's nicer to return an incorrect name
  765. // than to AV trying to copy a NULL pointer.
  766. //
  767. SCC_LOG1(ERROR,
  768. "ScDispatcherLoop: Could not duplicate name for service %ws\n",
  769. serviceName);
  770. dispatchEntry->ServiceRealName = dispatchEntry->ServiceName;
  771. status = ERROR_NOT_ENOUGH_MEMORY;
  772. }
  773. else
  774. {
  775. wcscpy(dispatchEntry->ServiceRealName, serviceName);
  776. }
  777. }
  778. if (status == NO_ERROR)
  779. {
  780. //
  781. // The Control Dispatcher is to start a service.
  782. // start the new thread.
  783. //
  784. lpServiceParams->ThreadStartupParms.ServiceStartRoutine =
  785. dispatchEntry->ServiceStartRoutine.U;
  786. threadAddress = (LPTHREAD_START_ROUTINE)ScSvcctrlThreadW;
  787. threadParms = (LPVOID)&lpServiceParams->ThreadStartupParms;
  788. //
  789. // If the service needs to be called with ansi parameters,
  790. // then do the conversion here.
  791. //
  792. if (AnsiFlag)
  793. {
  794. lpspAnsiParms = (LPTHREAD_STARTUP_PARMSA)
  795. &lpServiceParams->ThreadStartupParms;
  796. for (i = 0;
  797. i < lpServiceParams->ThreadStartupParms.NumArgs;
  798. i++)
  799. {
  800. if (!ScConvertToAnsi(
  801. *(&lpspAnsiParms->VectorTable + i),
  802. *(&lpServiceParams->ThreadStartupParms.VectorTable + i))) {
  803. //
  804. // Conversion error occured.
  805. //
  806. SCC_LOG0(ERROR,
  807. "ScDispatcherLoop: Could not convert "
  808. "args to ANSI\n");
  809. status = ERROR_NOT_ENOUGH_MEMORY;
  810. }
  811. }
  812. threadAddress = (LPTHREAD_START_ROUTINE)ScSvcctrlThreadA;
  813. threadParms = lpspAnsiParms;
  814. }
  815. }
  816. if (status == NO_ERROR)
  817. {
  818. //
  819. // Create the new thread
  820. //
  821. threadHandle = CreateThread (
  822. NULL, // Thread Attributes.
  823. 0L, // Stack Size
  824. threadAddress, // lpStartAddress
  825. threadParms, // lpParameter
  826. 0L, // Creation Flags
  827. &threadId); // lpThreadId
  828. if (threadHandle == NULL)
  829. {
  830. SCC_LOG(ERROR,
  831. "ScDispatcherLoop: CreateThread failed %d\n",
  832. GetLastError());
  833. status = ERROR_SERVICE_NO_THREAD;
  834. }
  835. else
  836. {
  837. fCreatedThread = TRUE;
  838. CloseHandle(threadHandle);
  839. }
  840. }
  841. break;
  842. }
  843. default:
  844. if (dispatchEntry->ControlHandler.Ex != NULL)
  845. {
  846. __try
  847. {
  848. //
  849. // Call the proper ControlHandler routine.
  850. //
  851. if (dispatchEntry->dwFlags & SERVICE_EX_HANDLER)
  852. {
  853. SCC_LOG2(TRACE,
  854. "Calling extended ControlHandler routine %x "
  855. "for service %ws\n",
  856. Msg->OpCode,
  857. serviceName);
  858. if (lpServiceParams)
  859. {
  860. dwHandlerRetVal = dispatchEntry->ControlHandler.Ex(
  861. Msg->OpCode,
  862. lpServiceParams->HandlerExParms.dwEventType,
  863. lpServiceParams->HandlerExParms.lpEventData,
  864. dispatchEntry->pContext);
  865. }
  866. else
  867. {
  868. dwHandlerRetVal = dispatchEntry->ControlHandler.Ex(
  869. Msg->OpCode,
  870. 0,
  871. NULL,
  872. dispatchEntry->pContext);
  873. }
  874. SCC_LOG3(TRACE,
  875. "Extended ControlHandler routine %x "
  876. "returned %d from call to service %ws\n",
  877. Msg->OpCode,
  878. dwHandlerRetVal,
  879. serviceName);
  880. }
  881. else if (IS_NON_EX_CONTROL(Msg->OpCode))
  882. {
  883. SCC_LOG2(TRACE,
  884. "Calling ControlHandler routine %x "
  885. "for service %ws\n",
  886. Msg->OpCode,
  887. serviceName);
  888. #if defined(_X86_)
  889. //
  890. // Hack for __CDECL callbacks. The Windows NT 3.1
  891. // SDK didn't prototype control handler functions
  892. // as WINAPI, so a number of existing 3rd-party
  893. // services have their control handler functions
  894. // built as __cdecl instead. This is a workaround.
  895. // Note that the call to the control handler must
  896. // be the only code between the _asm statements
  897. //
  898. DWORD SaveEdi;
  899. _asm mov SaveEdi, edi;
  900. _asm mov edi, esp; // Both __cdecl and WINAPI
  901. // functions preserve EDI
  902. #endif
  903. //
  904. // Do not add code here
  905. //
  906. dispatchEntry->ControlHandler.Regular(Msg->OpCode);
  907. //
  908. // Do not add code here
  909. //
  910. #if defined(_X86_)
  911. _asm mov esp, edi;
  912. _asm mov edi, SaveEdi;
  913. #endif
  914. SCC_LOG2(TRACE,
  915. "ControlHandler routine %x returned from "
  916. "call to service %ws\n",
  917. Msg->OpCode,
  918. serviceName);
  919. }
  920. else
  921. {
  922. //
  923. // Service registered for an extended control without
  924. // registering an extended handler. The call into the
  925. // service process succeeded, so keep status as NO_ERROR.
  926. // Return an error from the "handler" to notify anybody
  927. // watching for the return code (especially PnP).
  928. //
  929. dwHandlerRetVal = ERROR_CALL_NOT_IMPLEMENTED;
  930. }
  931. }
  932. __except (GetExceptionCode() != STATUS_POSSIBLE_DEADLOCK ?
  933. EXCEPTION_EXECUTE_HANDLER :
  934. EXCEPTION_CONTINUE_SEARCH)
  935. {
  936. SCC_LOG2(ERROR,
  937. "ScDispatcherLoop: Exception 0x%lx "
  938. "occurred in service %ws\n",
  939. GetExceptionCode(),
  940. serviceName);
  941. status = ERROR_EXCEPTION_IN_SERVICE;
  942. }
  943. //
  944. // Just in case messages come across from the SCM in a weird
  945. // order (e.g., two threads racing through ScSendControl where
  946. // the one that wins sends SERVICE_CONTROL_STOP), make sure
  947. // that we don't send controls to a service after we've sent
  948. // it a stop code. Ignore the handler return value in this
  949. // case since no service should be rejecting/failing a stop
  950. // control and the SCM's marked the service as stopped anyhow.
  951. // We just want to make sure that no controls get to the service
  952. // after SERVICE_CONTROL_STOP until it's restarted.
  953. //
  954. if (Msg->OpCode == SERVICE_CONTROL_STOP)
  955. {
  956. dispatchEntry->ControlHandler.Ex = NULL;
  957. }
  958. }
  959. else
  960. {
  961. //
  962. // There is no control handling routine
  963. // registered for this service
  964. //
  965. status = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
  966. }
  967. //
  968. // If status is not good here, then an exception occured
  969. // either because the pointer to the control handling
  970. // routine was bad, or because an exception occured
  971. // inside the control handling routine.
  972. //
  973. // ??EVENTLOG??
  974. //
  975. break;
  976. } // end switch.
  977. //
  978. // Send the status back to the sevice controller.
  979. //
  980. if (Msg->OpCode != SERVICE_CONTROL_SHUTDOWN)
  981. {
  982. SCC_LOG(TRACE, "Service %ws about to send response\n", serviceName);
  983. ScSendResponse (PipeHandle, status, dwHandlerRetVal);
  984. SCC_LOG(TRACE, "Service %ws returned from sending response\n", serviceName);
  985. }
  986. }
  987. else
  988. {
  989. //
  990. // The controlStatus indicates failure, we always want to try
  991. // to send the status back to the Service Controller.
  992. //
  993. SCC_LOG2(TRACE,
  994. "Service %ws about to send response on error %lu\n",
  995. serviceName,
  996. controlStatus);
  997. ScSendResponse(PipeHandle, controlStatus, dwHandlerRetVal);
  998. SCC_LOG2(TRACE,
  999. "Service %ws returned from sending response on error %lu\n",
  1000. serviceName,
  1001. controlStatus);
  1002. switch (controlStatus)
  1003. {
  1004. case ERROR_SERVICE_NOT_IN_EXE:
  1005. case ERROR_SERVICE_NO_THREAD:
  1006. //
  1007. // The Service Name is not in this .exe's dispatch table.
  1008. // Or a thread for a new service couldn't be created.
  1009. // ignore it. The Service Controller will tell us to
  1010. // shut down if necessary.
  1011. //
  1012. controlStatus = NO_ERROR;
  1013. break;
  1014. default:
  1015. //
  1016. // If the error is not specifically recognized, continue.
  1017. //
  1018. controlStatus = NO_ERROR;
  1019. break;
  1020. }
  1021. }
  1022. if (!fCreatedThread && lpServiceParams != NULL)
  1023. {
  1024. LocalFree(lpServiceParams);
  1025. lpServiceParams = NULL;
  1026. }
  1027. if (fOpenedService && status != NO_ERROR)
  1028. {
  1029. CloseServiceHandle(dispatchEntry->StatusHandle);
  1030. dispatchEntry->StatusHandle = NULL;
  1031. }
  1032. }
  1033. while (continueDispatch == TRUE);
  1034. return;
  1035. }
  1036. SERVICE_STATUS_HANDLE
  1037. WINAPI
  1038. RegisterServiceCtrlHandlerHelp (
  1039. IN LPCWSTR ServiceName,
  1040. IN HANDLER_FUNCTION_TYPE ControlHandler,
  1041. IN PVOID pContext,
  1042. IN DWORD dwFlags
  1043. )
  1044. /*++
  1045. Routine Description:
  1046. This helper function enters a pointer to a control handling routine
  1047. and a pointer to a security descriptor into the Control Dispatcher's
  1048. dispatch table. It does the work for the RegisterServiceCtrlHandler*
  1049. family of APIs
  1050. Arguments:
  1051. ServiceName - This is a pointer to the Service Name string.
  1052. ControlHandler - This is a pointer to the service's control handling
  1053. routine.
  1054. pContext - This is a pointer to context data supplied by the user.
  1055. dwFlags - This is a set of flags that give information about the
  1056. control handling routine (currently only discerns between extended
  1057. and non-extended handlers)
  1058. Return Value:
  1059. This function returns a handle to the service that is to be used in
  1060. subsequent calls to SetServiceStatus. If the return value is NULL,
  1061. an error has occured, and GetLastError can be used to obtain the
  1062. error value. Possible values for error are:
  1063. NO_ERROR - If the operation was successful.
  1064. ERROR_INVALID_PARAMETER - The pointer to the control handler function
  1065. is NULL.
  1066. ERROR_INVALID_DATA -
  1067. ERROR_SERVICE_NOT_IN_EXE - The serviceName could not be found in
  1068. the dispatch table. This indicates that the configuration database
  1069. says the serice is in this process, but the service name doesn't
  1070. exist in the dispatch table.
  1071. --*/
  1072. {
  1073. DWORD status;
  1074. LPINTERNAL_DISPATCH_ENTRY dispatchEntry;
  1075. //
  1076. // Find the service in the dispatch table.
  1077. //
  1078. dispatchEntry = DispatchTable;
  1079. __try {
  1080. status = ScGetDispatchEntry(&dispatchEntry, (LPWSTR) ServiceName);
  1081. }
  1082. __except (EXCEPTION_EXECUTE_HANDLER) {
  1083. status = GetExceptionCode();
  1084. if (status != EXCEPTION_ACCESS_VIOLATION) {
  1085. SCC_LOG(ERROR,
  1086. "RegisterServiceCtrlHandlerHelp: Unexpected Exception 0x%lx\n",
  1087. status);
  1088. }
  1089. }
  1090. if(status != NO_ERROR) {
  1091. SCC_LOG(ERROR,
  1092. "RegisterServiceCtrlHandlerHelp: can't find dispatch entry\n",0);
  1093. SetLastError(status);
  1094. return(0L);
  1095. }
  1096. //
  1097. // Insert the ControlHandler pointer
  1098. //
  1099. if (ControlHandler.Ex == NULL) {
  1100. SCC_LOG(ERROR,
  1101. "RegisterServiceCtrlHandlerHelp: Ptr to ctrlhandler is NULL\n",
  1102. 0);
  1103. SetLastError(ERROR_INVALID_PARAMETER);
  1104. return(0L);
  1105. }
  1106. //
  1107. // Insert the entries into the table
  1108. //
  1109. if (dwFlags & SERVICE_EX_HANDLER) {
  1110. dispatchEntry->dwFlags |= SERVICE_EX_HANDLER;
  1111. dispatchEntry->ControlHandler.Ex = ControlHandler.Ex;
  1112. dispatchEntry->pContext = pContext;
  1113. }
  1114. else {
  1115. dispatchEntry->dwFlags &= ~(SERVICE_EX_HANDLER);
  1116. dispatchEntry->ControlHandler.Regular = ControlHandler.Regular;
  1117. }
  1118. //
  1119. // This cast is OK -- see comment in I_ScPnPGetServiceName
  1120. //
  1121. return( (SERVICE_STATUS_HANDLE) dispatchEntry->StatusHandle );
  1122. }
  1123. SERVICE_STATUS_HANDLE
  1124. WINAPI
  1125. RegisterServiceCtrlHandlerW (
  1126. IN LPCWSTR ServiceName,
  1127. IN LPHANDLER_FUNCTION ControlHandler
  1128. )
  1129. /*++
  1130. Routine Description:
  1131. This function enters a pointer to a control handling
  1132. routine into the Control Dispatcher's dispatch table.
  1133. Arguments:
  1134. ServiceName -- The service's name
  1135. ControlHandler -- Pointer to the control handling routine
  1136. Return Value:
  1137. Anything returned by RegisterServiceCtrlHandlerHelp
  1138. --*/
  1139. {
  1140. HANDLER_FUNCTION_TYPE Handler;
  1141. Handler.Regular = ControlHandler;
  1142. return RegisterServiceCtrlHandlerHelp(ServiceName,
  1143. Handler,
  1144. NULL,
  1145. 0);
  1146. }
  1147. SERVICE_STATUS_HANDLE
  1148. WINAPI
  1149. RegisterServiceCtrlHandlerA (
  1150. IN LPCSTR ServiceName,
  1151. IN LPHANDLER_FUNCTION ControlHandler
  1152. )
  1153. /*++
  1154. Routine Description:
  1155. This is the ansi entry point for RegisterServiceCtrlHandler.
  1156. Arguments:
  1157. Return Value:
  1158. --*/
  1159. {
  1160. LPWSTR ServiceNameW;
  1161. SERVICE_STATUS_HANDLE statusHandle;
  1162. if (!ScConvertToUnicode(&ServiceNameW, ServiceName)) {
  1163. //
  1164. // This can only fail because of a failed LocalAlloc call
  1165. // or else the ansi string is garbage.
  1166. //
  1167. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1168. return(0L);
  1169. }
  1170. statusHandle = RegisterServiceCtrlHandlerW(ServiceNameW, ControlHandler);
  1171. LocalFree(ServiceNameW);
  1172. return(statusHandle);
  1173. }
  1174. SERVICE_STATUS_HANDLE
  1175. WINAPI
  1176. RegisterServiceCtrlHandlerExW (
  1177. IN LPCWSTR ServiceName,
  1178. IN LPHANDLER_FUNCTION_EX ControlHandler,
  1179. IN PVOID pContext
  1180. )
  1181. /*++
  1182. Routine Description:
  1183. This function enters a pointer to an extended control handling
  1184. routine into the Control Dispatcher's dispatch table. It is
  1185. analogous to RegisterServiceCtrlHandlerW.
  1186. Arguments:
  1187. ServiceName -- The service's name
  1188. ControlHandler -- A pointer to an extended control handling routine
  1189. pContext -- User-supplied data that is passed to the control handler
  1190. Return Value:
  1191. Anything returned by RegisterServiceCtrlHandlerHelp
  1192. --*/
  1193. {
  1194. HANDLER_FUNCTION_TYPE Handler;
  1195. Handler.Ex = ControlHandler;
  1196. return RegisterServiceCtrlHandlerHelp(ServiceName,
  1197. Handler,
  1198. pContext,
  1199. SERVICE_EX_HANDLER);
  1200. }
  1201. SERVICE_STATUS_HANDLE
  1202. WINAPI
  1203. RegisterServiceCtrlHandlerExA (
  1204. IN LPCSTR ServiceName,
  1205. IN LPHANDLER_FUNCTION_EX ControlHandler,
  1206. IN PVOID pContext
  1207. )
  1208. /*++
  1209. Routine Description:
  1210. This is the ansi entry point for RegisterServiceCtrlHandlerEx.
  1211. Arguments:
  1212. Return Value:
  1213. --*/
  1214. {
  1215. LPWSTR ServiceNameW;
  1216. SERVICE_STATUS_HANDLE statusHandle;
  1217. if(!ScConvertToUnicode(&ServiceNameW, ServiceName)) {
  1218. //
  1219. // This can only fail because of a failed LocalAlloc call
  1220. // or else the ansi string is garbage.
  1221. //
  1222. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1223. return(0L);
  1224. }
  1225. statusHandle = RegisterServiceCtrlHandlerExW(ServiceNameW,
  1226. ControlHandler,
  1227. pContext);
  1228. LocalFree(ServiceNameW);
  1229. return(statusHandle);
  1230. }
  1231. DWORD
  1232. ScCreateDispatchTableW(
  1233. IN CONST SERVICE_TABLE_ENTRYW *lpServiceStartTable,
  1234. OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
  1235. )
  1236. /*++
  1237. Routine Description:
  1238. This routine allocates space for the Control Dispatchers Dispatch Table.
  1239. It also initializes the table with the data that the service main
  1240. routine passed in with the lpServiceStartTable parameter.
  1241. This routine expects that pointers in the user's dispatch table point
  1242. to valid information. And that that information will stay valid and
  1243. fixed through out the life of the Control Dispatcher. In otherwords,
  1244. the ServiceName string better not move or get cleared.
  1245. Arguments:
  1246. lpServiceStartTable - This is a pointer to the first entry in the
  1247. dispatch table that the service's main routine passed in .
  1248. DispatchTablePtr - This is a pointer to the location where the
  1249. Service Controller's dispatch table is to be stored.
  1250. Return Value:
  1251. NO_ERROR - The operation was successful.
  1252. ERROR_NOT_ENOUGH_MEMORY - The memory allocation failed.
  1253. ERROR_INVALID_PARAMETER - There are no entries in the dispatch table.
  1254. --*/
  1255. {
  1256. DWORD numEntries;
  1257. LPINTERNAL_DISPATCH_ENTRY dispatchTable;
  1258. const SERVICE_TABLE_ENTRYW * entryPtr;
  1259. //
  1260. // Count the number of entries in the user dispatch table
  1261. //
  1262. numEntries = 0;
  1263. entryPtr = lpServiceStartTable;
  1264. while (entryPtr->lpServiceName != NULL) {
  1265. numEntries++;
  1266. entryPtr++;
  1267. }
  1268. if (numEntries == 0) {
  1269. SCC_LOG(ERROR,"ScCreateDispatchTable:No entries in Dispatch table!\n",0);
  1270. return(ERROR_INVALID_PARAMETER);
  1271. }
  1272. //
  1273. // Allocate space for the Control Dispatcher's Dispatch Table
  1274. //
  1275. dispatchTable = (LPINTERNAL_DISPATCH_ENTRY)LocalAlloc(LMEM_ZEROINIT,
  1276. sizeof(INTERNAL_DISPATCH_ENTRY) * (numEntries + 1));
  1277. if (dispatchTable == NULL) {
  1278. SCC_LOG(ERROR,"ScCreateDispatchTable: Local Alloc failed rc = %d\n",
  1279. GetLastError());
  1280. return (ERROR_NOT_ENOUGH_MEMORY);
  1281. }
  1282. //
  1283. // Move user dispatch info into the Control Dispatcher's table.
  1284. //
  1285. *DispatchTablePtr = dispatchTable;
  1286. entryPtr = lpServiceStartTable;
  1287. while (entryPtr->lpServiceName != NULL) {
  1288. dispatchTable->ServiceName = entryPtr->lpServiceName;
  1289. dispatchTable->ServiceRealName = entryPtr->lpServiceName;
  1290. dispatchTable->ServiceStartRoutine.U= entryPtr->lpServiceProc;
  1291. dispatchTable->ControlHandler.Ex = NULL;
  1292. dispatchTable->StatusHandle = NULL;
  1293. dispatchTable->dwFlags = 0;
  1294. entryPtr++;
  1295. dispatchTable++;
  1296. }
  1297. return (NO_ERROR);
  1298. }
  1299. DWORD
  1300. ScCreateDispatchTableA(
  1301. IN CONST SERVICE_TABLE_ENTRYA *lpServiceStartTable,
  1302. OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
  1303. )
  1304. /*++
  1305. Routine Description:
  1306. This routine allocates space for the Control Dispatchers Dispatch Table.
  1307. It also initializes the table with the data that the service main
  1308. routine passed in with the lpServiceStartTable parameter.
  1309. This routine expects that pointers in the user's dispatch table point
  1310. to valid information. And that that information will stay valid and
  1311. fixed through out the life of the Control Dispatcher. In otherwords,
  1312. the ServiceName string better not move or get cleared.
  1313. Arguments:
  1314. lpServiceStartTable - This is a pointer to the first entry in the
  1315. dispatch table that the service's main routine passed in .
  1316. DispatchTablePtr - This is a pointer to the location where the
  1317. Service Controller's dispatch table is to be stored.
  1318. Return Value:
  1319. NO_ERROR - The operation was successful.
  1320. ERROR_NOT_ENOUGH_MEMORY - The memory allocation failed.
  1321. ERROR_INVALID_PARAMETER - There are no entries in the dispatch table.
  1322. --*/
  1323. {
  1324. DWORD numEntries;
  1325. DWORD status = NO_ERROR;
  1326. LPINTERNAL_DISPATCH_ENTRY dispatchTable;
  1327. const SERVICE_TABLE_ENTRYA * entryPtr;
  1328. //
  1329. // Count the number of entries in the user dispatch table
  1330. //
  1331. numEntries = 0;
  1332. entryPtr = lpServiceStartTable;
  1333. while (entryPtr->lpServiceName != NULL) {
  1334. numEntries++;
  1335. entryPtr++;
  1336. }
  1337. if (numEntries == 0) {
  1338. SCC_LOG(ERROR,"ScCreateDispatchTable:No entries in Dispatch table!\n",0);
  1339. return(ERROR_INVALID_PARAMETER);
  1340. }
  1341. //
  1342. // Allocate space for the Control Dispatcher's Dispatch Table
  1343. //
  1344. dispatchTable = (LPINTERNAL_DISPATCH_ENTRY)LocalAlloc(LMEM_ZEROINIT,
  1345. sizeof(INTERNAL_DISPATCH_ENTRY) * (numEntries + 1));
  1346. if (dispatchTable == NULL) {
  1347. SCC_LOG(ERROR,"ScCreateDispatchTableA: Local Alloc failed rc = %d\n",
  1348. GetLastError());
  1349. return (ERROR_NOT_ENOUGH_MEMORY);
  1350. }
  1351. //
  1352. // Move user dispatch info into the Control Dispatcher's table.
  1353. //
  1354. *DispatchTablePtr = dispatchTable;
  1355. entryPtr = lpServiceStartTable;
  1356. while (entryPtr->lpServiceName != NULL) {
  1357. //
  1358. // Convert the service name to unicode
  1359. //
  1360. __try {
  1361. if (!ScConvertToUnicode(
  1362. &(dispatchTable->ServiceName),
  1363. entryPtr->lpServiceName)) {
  1364. //
  1365. // The convert failed.
  1366. //
  1367. SCC_LOG(ERROR,"ScCreateDispatcherTableA:ScConvertToUnicode failed\n",0);
  1368. //
  1369. // This is the only reason for failure that I can think of.
  1370. //
  1371. status = ERROR_NOT_ENOUGH_MEMORY;
  1372. }
  1373. }
  1374. __except (EXCEPTION_EXECUTE_HANDLER) {
  1375. status = GetExceptionCode();
  1376. if (status != EXCEPTION_ACCESS_VIOLATION) {
  1377. SCC_LOG(ERROR,
  1378. "ScCreateDispatchTableA: Unexpected Exception 0x%lx\n",status);
  1379. }
  1380. }
  1381. if (status != NO_ERROR) {
  1382. //
  1383. // If an error occured, free up the allocated resources.
  1384. //
  1385. dispatchTable = *DispatchTablePtr;
  1386. while (dispatchTable->ServiceName != NULL) {
  1387. LocalFree(dispatchTable->ServiceName);
  1388. dispatchTable++;
  1389. }
  1390. LocalFree(*DispatchTablePtr);
  1391. return(status);
  1392. }
  1393. //
  1394. // Fill in the rest of the dispatch entry.
  1395. //
  1396. dispatchTable->ServiceRealName = dispatchTable->ServiceName;
  1397. dispatchTable->ServiceStartRoutine.A= entryPtr->lpServiceProc;
  1398. dispatchTable->ControlHandler.Ex = NULL;
  1399. dispatchTable->StatusHandle = NULL;
  1400. dispatchTable->dwFlags = 0;
  1401. entryPtr++;
  1402. dispatchTable++;
  1403. }
  1404. return (NO_ERROR);
  1405. }
  1406. DWORD
  1407. ScReadServiceParms(
  1408. IN LPCTRL_MSG_HEADER Msg,
  1409. IN DWORD dwNumBytesRead,
  1410. OUT LPBYTE *ppServiceParams,
  1411. OUT LPBYTE *ppTempArgPtr,
  1412. OUT LPDWORD lpdwRemainingArgBytes
  1413. )
  1414. /*++
  1415. Routine Description:
  1416. This routine calculates the number of bytes needed for the service's
  1417. control parameters by using the arg count information in the
  1418. message header. The parameter structure is allocated and
  1419. as many bytes of argument information as have been captured so far
  1420. are placed into the buffer. A second read of the pipe may be necessary
  1421. to obtain the remaining bytes of argument information.
  1422. NOTE: This function allocates enough space in the startup parameter
  1423. buffer for the service name and pointer as well as the rest of the
  1424. arguments. However, it does not put the service name into the argument
  1425. list. This is because it may take two calls to this routine to
  1426. get all the argument information. We can't insert the service name
  1427. string until we have all the rest of the argument data.
  1428. [serviceNamePtr][argv1][argv2]...[argv1Str][argv2Str]...[serviceNameStr]
  1429. or
  1430. [serviceNamePtr][dwEventType][EventData][serviceNameStr]
  1431. Arguments:
  1432. Msg - A pointer to the pipe message header.
  1433. dwNumBytesRead - The number of bytes read in the first pipe read.
  1434. ppServiceParams - A pointer to a location where the pointer to the
  1435. thread startup parameter structure is to be placed.
  1436. ppTempArgPtr - A location that will contain the pointer to where
  1437. more argument data can be placed by a second read of the pipe.
  1438. lpdwRemainingArgBytes - Returns with a count of the number of argument
  1439. bytes that remain to be read from the pipe.
  1440. Return Value:
  1441. NO_ERROR - If the operation was successful.
  1442. ERROR_NOT_ENOUGH_MEMORY - If the memory allocation was unsuccessful.
  1443. Note:
  1444. --*/
  1445. {
  1446. DWORD dwNameSize; // num bytes in ServiceName.
  1447. DWORD dwBufferSize; // num bytes for parameter buffer
  1448. LONG lArgBytesRead; // number of arg bytes in first read.
  1449. LPSERVICE_PARAMS lpTempParams;
  1450. //
  1451. // Set out pointer to no arguments unless we discover otherwise
  1452. //
  1453. *ppTempArgPtr = NULL;
  1454. SCC_LOG(TRACE,"ScReadServiceParms: Get service parameters from pipe\n",0);
  1455. //
  1456. // Note: Here we assume that the service name was read into the buffer
  1457. // in its entirety.
  1458. //
  1459. dwNameSize = (DWORD) WCSSIZE((LPWSTR) ((LPBYTE) Msg + Msg->ServiceNameOffset));
  1460. //
  1461. // Calculate the size of buffer needed. This will consist of a
  1462. // SERVICE_PARAMS structure, plus the service name and a pointer
  1463. // for it, plus the rest of the arg info sent in the message
  1464. // (We are wasting 4 bytes here since the first pointer in
  1465. // the vector table is accounted for twice - but what the heck!).
  1466. //
  1467. dwBufferSize = Msg->Count -
  1468. sizeof(CTRL_MSG_HEADER) +
  1469. sizeof(SERVICE_PARAMS) +
  1470. sizeof(LPWSTR);
  1471. //
  1472. // Allocate the memory for the service parameters
  1473. //
  1474. lpTempParams = (LPSERVICE_PARAMS) LocalAlloc (LMEM_ZEROINIT, dwBufferSize);
  1475. if (lpTempParams == NULL)
  1476. {
  1477. SCC_LOG1(ERROR,
  1478. "ScReadServiceParms: LocalAlloc failed rc = %d\n",
  1479. GetLastError());
  1480. return ERROR_NOT_ENOUGH_MEMORY;
  1481. }
  1482. lArgBytesRead = dwNumBytesRead - sizeof(CTRL_MSG_HEADER) - dwNameSize;
  1483. *lpdwRemainingArgBytes = Msg->Count - dwNumBytesRead;
  1484. //
  1485. // Unpack message-specific arguments
  1486. //
  1487. switch (Msg->OpCode) {
  1488. case SERVICE_CONTROL_START_OWN:
  1489. case SERVICE_CONTROL_START_SHARE:
  1490. SCC_LOG(TRACE,"ScReadServiceParms: Starting a service\n", 0);
  1491. if (Msg->NumCmdArgs != 0) {
  1492. //
  1493. // There's only a vector table and ThreadStartupParms
  1494. // when the service starts up
  1495. //
  1496. *ppTempArgPtr = (LPBYTE) &lpTempParams->ThreadStartupParms.VectorTable;
  1497. //
  1498. // Leave the first vector location blank for the service name
  1499. // pointer.
  1500. //
  1501. (*ppTempArgPtr) += sizeof(LPWSTR);
  1502. //
  1503. // adjust lArgBytesRead to remove any extra bytes that are
  1504. // there for alignment. If a name that is not in the dispatch
  1505. // table is passed in, it could be larger than our buffer.
  1506. // This could cause lArgBytesRead to become negative.
  1507. // However it should fail safe anyway since the name simply
  1508. // won't be recognized and an error will be returned.
  1509. //
  1510. lArgBytesRead -= (Msg->ArgvOffset - Msg->ServiceNameOffset - dwNameSize);
  1511. //
  1512. // Copy any portion of the command arg info from the first read
  1513. // into the buffer that is to be used for the second read.
  1514. //
  1515. if (lArgBytesRead > 0) {
  1516. RtlCopyMemory(*ppTempArgPtr,
  1517. (LPBYTE)Msg + Msg->ArgvOffset,
  1518. lArgBytesRead);
  1519. *ppTempArgPtr += lArgBytesRead;
  1520. }
  1521. }
  1522. break;
  1523. case SERVICE_CONTROL_DEVICEEVENT:
  1524. case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
  1525. case SERVICE_CONTROL_POWEREVENT:
  1526. case SERVICE_CONTROL_SESSIONCHANGE:
  1527. {
  1528. //
  1529. // This is a PnP, power, or TS message
  1530. //
  1531. SCC_LOG1(TRACE,
  1532. "ScReadServiceParms: Receiving PnP/power/TS event %x\n",
  1533. Msg->OpCode);
  1534. //
  1535. // adjust lArgBytesRead to remove any extra bytes that are
  1536. // there for alignment. If a name that is not in the dispatch
  1537. // table is passed in, it could be larger than our buffer.
  1538. // This could cause lArgBytesRead to become negative.
  1539. // However it should fail safe anyway since the name simply
  1540. // won't be recognized and an error will be returned.
  1541. //
  1542. lArgBytesRead -= (Msg->ArgvOffset - Msg->ServiceNameOffset - dwNameSize);
  1543. *ppTempArgPtr = (LPBYTE) &lpTempParams->HandlerExParms.dwEventType;
  1544. if (lArgBytesRead > 0)
  1545. {
  1546. LPBYTE lpArgs;
  1547. LPHANDLEREX_PARMS lpHandlerExParms = (LPHANDLEREX_PARMS) (*ppTempArgPtr);
  1548. lpArgs = (LPBYTE) Msg + Msg->ArgvOffset;
  1549. lpHandlerExParms->dwEventType = *(LPDWORD) lpArgs;
  1550. lpArgs += sizeof(DWORD);
  1551. lArgBytesRead -= sizeof(DWORD);
  1552. RtlCopyMemory(lpHandlerExParms + 1,
  1553. lpArgs,
  1554. lArgBytesRead);
  1555. lpHandlerExParms->lpEventData = lpHandlerExParms + 1;
  1556. *ppTempArgPtr = (LPBYTE) (lpHandlerExParms + 1) + lArgBytesRead;
  1557. }
  1558. break;
  1559. }
  1560. }
  1561. *ppServiceParams = (LPBYTE) lpTempParams;
  1562. return NO_ERROR;
  1563. }
  1564. DWORD
  1565. ScConnectServiceController(
  1566. OUT LPHANDLE PipeHandle
  1567. )
  1568. /*++
  1569. Routine Description:
  1570. This function connects to the Service Controller Pipe.
  1571. Arguments:
  1572. PipeHandle - This is a pointer to the location where the PipeHandle
  1573. is to be placed.
  1574. Return Value:
  1575. NO_ERROR - if the operation was successful.
  1576. ERROR_FAILED_SERVICE_CONTROLLER_CONNECT - if we failed to connect.
  1577. --*/
  1578. {
  1579. BOOL status;
  1580. DWORD apiStatus;
  1581. DWORD response;
  1582. DWORD pipeMode;
  1583. DWORD numBytesWritten;
  1584. WCHAR wszPipeName[sizeof(CONTROL_PIPE_NAME) / sizeof(WCHAR) + PID_LEN] = CONTROL_PIPE_NAME;
  1585. //
  1586. // Generate the pipe name -- Security process uses PID 0 since the
  1587. // SCM doesn't have the PID at connect-time (it gets it from the
  1588. // pipe transaction with the LSA)
  1589. //
  1590. if (g_fIsSecProc) {
  1591. response = 0;
  1592. }
  1593. else {
  1594. //
  1595. // Read this process's pipe ID from the registry.
  1596. //
  1597. HKEY hCurrentValueKey;
  1598. status = RegOpenKeyEx(
  1599. HKEY_LOCAL_MACHINE,
  1600. "System\\CurrentControlSet\\Control\\ServiceCurrent",
  1601. 0,
  1602. KEY_QUERY_VALUE,
  1603. &hCurrentValueKey);
  1604. if (status == ERROR_SUCCESS)
  1605. {
  1606. DWORD ValueType;
  1607. DWORD cbData = sizeof(response);
  1608. status = RegQueryValueEx(
  1609. hCurrentValueKey,
  1610. NULL, // Use key's unnamed value
  1611. 0,
  1612. &ValueType,
  1613. (LPBYTE) &response,
  1614. &cbData);
  1615. RegCloseKey(hCurrentValueKey);
  1616. if (status != ERROR_SUCCESS || ValueType != REG_DWORD)
  1617. {
  1618. SCC_LOG(ERROR,
  1619. "ScConnectServiceController: RegQueryValueEx FAILED %d\n",
  1620. status);
  1621. return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
  1622. }
  1623. }
  1624. else
  1625. {
  1626. SCC_LOG(ERROR,
  1627. "ScConnectServiceController: RegOpenKeyEx FAILED %d\n",
  1628. status);
  1629. return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
  1630. }
  1631. }
  1632. _itow(response, wszPipeName + sizeof(CONTROL_PIPE_NAME) / sizeof(WCHAR) - 1, 10);
  1633. status = WaitNamedPipeW (
  1634. wszPipeName,
  1635. CONTROL_WAIT_PERIOD);
  1636. if (status != TRUE) {
  1637. SCC_LOG(ERROR,"ScConnectServiceController:WaitNamedPipe failed rc = %d\n",
  1638. GetLastError());
  1639. }
  1640. SCC_LOG(TRACE,"ScConnectServiceController:WaitNamedPipe success\n",0);
  1641. *PipeHandle = CreateFileW(
  1642. wszPipeName, // lpFileName
  1643. GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
  1644. FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
  1645. NULL, // lpSecurityAttributes
  1646. OPEN_EXISTING, // dwCreationDisposition
  1647. FILE_ATTRIBUTE_NORMAL, // dwFileAttributes
  1648. 0L); // hTemplateFile
  1649. if (*PipeHandle == INVALID_HANDLE_VALUE) {
  1650. SCC_LOG(ERROR,"ScConnectServiceController:CreateFile failed rc = %d\n",
  1651. GetLastError());
  1652. return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
  1653. }
  1654. SCC_LOG(TRACE,"ScConnectServiceController:CreateFile success\n",0);
  1655. //
  1656. // Set pipe mode
  1657. //
  1658. pipeMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
  1659. status = SetNamedPipeHandleState (
  1660. *PipeHandle,
  1661. &pipeMode,
  1662. NULL,
  1663. NULL);
  1664. if (status != TRUE) {
  1665. SCC_LOG(ERROR,"ScConnectServiceController:SetNamedPipeHandleState failed rc = %d\n",
  1666. GetLastError());
  1667. return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
  1668. }
  1669. else {
  1670. SCC_LOG(TRACE,
  1671. "ScConnectServiceController SetNamedPipeHandleState Success\n",0);
  1672. }
  1673. //
  1674. // Send initial status - This is the process Id for the service process.
  1675. //
  1676. response = GetCurrentProcessId();
  1677. apiStatus = WriteFile (
  1678. *PipeHandle,
  1679. &response,
  1680. sizeof(response),
  1681. &numBytesWritten,
  1682. NULL);
  1683. if (apiStatus != TRUE) {
  1684. //
  1685. // If this fails, there is a chance that the pipe is still in good
  1686. // shape. So we just go on.
  1687. //
  1688. // ??EVENTLOG??
  1689. //
  1690. SCC_LOG(ERROR,"ScConnectServiceController: WriteFile failed, rc= %d\n", GetLastError());
  1691. }
  1692. else {
  1693. SCC_LOG(TRACE,
  1694. "ScConnectServiceController: WriteFile success, bytes Written= %d\n",
  1695. numBytesWritten);
  1696. }
  1697. return(NO_ERROR);
  1698. }
  1699. VOID
  1700. ScExpungeMessage(
  1701. IN HANDLE PipeHandle
  1702. )
  1703. /*++
  1704. Routine Description:
  1705. This routine cleans the remaining portion of a message out of the pipe.
  1706. It is called in response to an unsuccessful attempt to allocate the
  1707. correct buffer size from the heap. In this routine a small buffer is
  1708. allocated on the stack, and successive reads are made until a status
  1709. other than ERROR_MORE_DATA is received.
  1710. Arguments:
  1711. PipeHandle - This is a handle to the pipe in which the message resides.
  1712. Return Value:
  1713. none - If this operation fails, there is not much I can do about
  1714. the data in the pipe.
  1715. --*/
  1716. {
  1717. #define EXPUNGE_BUF_SIZE 100
  1718. DWORD status;
  1719. DWORD dwNumBytesRead = 0;
  1720. BYTE msg[EXPUNGE_BUF_SIZE];
  1721. do {
  1722. status = ReadFile (
  1723. PipeHandle,
  1724. msg,
  1725. EXPUNGE_BUF_SIZE,
  1726. &dwNumBytesRead,
  1727. NULL);
  1728. }
  1729. while( status == ERROR_MORE_DATA);
  1730. }
  1731. DWORD
  1732. ScGetPipeInput (
  1733. IN HANDLE PipeHandle,
  1734. IN OUT LPCTRL_MSG_HEADER Msg,
  1735. IN DWORD dwBufferSize,
  1736. OUT LPSERVICE_PARAMS *ppServiceParams
  1737. )
  1738. /*++
  1739. Routine Description:
  1740. This routine reads a control message from the pipe and places it into
  1741. a message buffer. This routine also allocates a structure for
  1742. the service thread information. This structure will eventually
  1743. contain everything that is needed to invoke the service startup
  1744. routine in the context of a new thread. Items contained in the
  1745. structure are:
  1746. 1) The pointer to the startup routine,
  1747. 2) The number of arguments, and
  1748. 3) The table of vectors to the arguments.
  1749. Since this routine has knowledge about the buffer size needed for
  1750. the arguments, the allocation is done here.
  1751. Arguments:
  1752. PipeHandle - This is the handle for the pipe that is to be read.
  1753. Msg - This is a pointer to a buffer where the data is to be placed.
  1754. dwBufferSize - This is the size (in bytes) of the buffer that data is to
  1755. be placed in.
  1756. ppServiceParams - This is the location where the command args will
  1757. be placed
  1758. Return Value:
  1759. NO_ERROR - if the operation was successful.
  1760. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to create a large
  1761. enough buffer for the command line arguments.
  1762. ERROR_INVALID_DATA - This is returned if we did not receive a complete
  1763. CTRL_MESSAGE_HEADER on the first read.
  1764. Any error that ReadFile might return could be returned by this function.
  1765. (We may want to return something more specific like ERROR_READ_FAULT)
  1766. --*/
  1767. {
  1768. DWORD status;
  1769. BOOL readStatus;
  1770. DWORD dwNumBytesRead = 0;
  1771. DWORD dwRemainingArgBytes;
  1772. LPBYTE pTempArgPtr;
  1773. *ppServiceParams = NULL;
  1774. //
  1775. // Read the header and name string from the pipe.
  1776. // NOTE: The number of bytes for the name string is determined by
  1777. // the longest service name in the service process. If the actual
  1778. // string read is shorter, then the beginning of the command arg
  1779. // data may be read with this read.
  1780. // Also note: The buffer is large enough to accommodate the longest
  1781. // permissible service name.
  1782. //
  1783. readStatus = ReadFile(PipeHandle,
  1784. Msg,
  1785. dwBufferSize,
  1786. &dwNumBytesRead,
  1787. NULL);
  1788. SCC_LOG(TRACE,"ScGetPipeInput:ReadFile buffer size = %ld\n",dwBufferSize);
  1789. SCC_LOG(TRACE,"ScGetPipeInput:ReadFile dwNumBytesRead = %ld\n",dwNumBytesRead);
  1790. if ((readStatus == TRUE) && (dwNumBytesRead > sizeof(CTRL_MSG_HEADER))) {
  1791. //
  1792. // The Read File read the complete message in one read. So we
  1793. // can return with the data.
  1794. //
  1795. SCC_LOG(TRACE,"ScGetPipeInput: Success!\n",0);
  1796. switch (Msg->OpCode) {
  1797. case SERVICE_CONTROL_START_OWN:
  1798. case SERVICE_CONTROL_START_SHARE:
  1799. //
  1800. // Read in any start arguments for the service
  1801. //
  1802. status = ScReadServiceParms(Msg,
  1803. dwNumBytesRead,
  1804. (LPBYTE *)ppServiceParams,
  1805. &pTempArgPtr,
  1806. &dwRemainingArgBytes);
  1807. if (status != NO_ERROR) {
  1808. return status;
  1809. }
  1810. //
  1811. // Change the offsets back into pointers.
  1812. //
  1813. ScNormalizeCmdLineArgs(Msg,
  1814. &(*ppServiceParams)->ThreadStartupParms);
  1815. break;
  1816. case SERVICE_CONTROL_DEVICEEVENT:
  1817. case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
  1818. case SERVICE_CONTROL_POWEREVENT:
  1819. case SERVICE_CONTROL_SESSIONCHANGE:
  1820. //
  1821. // Read in the service's PnP/power arguments
  1822. //
  1823. status = ScReadServiceParms(Msg,
  1824. dwNumBytesRead,
  1825. (LPBYTE *)ppServiceParams,
  1826. &pTempArgPtr,
  1827. &dwRemainingArgBytes);
  1828. if (status != NO_ERROR) {
  1829. return status;
  1830. }
  1831. break;
  1832. default:
  1833. ASSERT(Msg->NumCmdArgs == 0);
  1834. break;
  1835. }
  1836. return NO_ERROR;
  1837. }
  1838. else {
  1839. //
  1840. // An error was returned from ReadFile. ERROR_MORE_DATA
  1841. // means that we need to read some arguments from the buffer.
  1842. // Any other error is unexpected, and generates an internal error.
  1843. //
  1844. if (readStatus != TRUE) {
  1845. status = GetLastError();
  1846. if (status != ERROR_MORE_DATA) {
  1847. SCC_LOG(ERROR,"ScGetPipeInput:Unexpected return code, rc= %ld\n",
  1848. status);
  1849. return status;
  1850. }
  1851. }
  1852. else {
  1853. //
  1854. // The read was successful, but we didn't get a complete
  1855. // CTRL_MESSAGE_HEADER.
  1856. //
  1857. return ERROR_INVALID_DATA;
  1858. }
  1859. }
  1860. //
  1861. // We must have received an ERROR_MORE_DATA to go down this
  1862. // path. This means that the message contains more data. Namely,
  1863. // service arguments must be present. Therefore, the pipe must
  1864. // be read again. Since the header indicates how many bytes are
  1865. // needed, we will allocate a buffer large enough to hold all the
  1866. // service arguments.
  1867. //
  1868. // If a portion of the arguments was read in the first read,
  1869. // they will be put in this new buffer. That is so that all the
  1870. // command line arg info is in one place.
  1871. //
  1872. status = ScReadServiceParms(Msg,
  1873. dwNumBytesRead,
  1874. (LPBYTE *)ppServiceParams,
  1875. &pTempArgPtr,
  1876. &dwRemainingArgBytes);
  1877. if (status != NO_ERROR)
  1878. {
  1879. ScExpungeMessage(PipeHandle);
  1880. LocalFree(*ppServiceParams);
  1881. *ppServiceParams = NULL;
  1882. return status;
  1883. }
  1884. readStatus = ReadFile(PipeHandle,
  1885. pTempArgPtr,
  1886. dwRemainingArgBytes,
  1887. &dwNumBytesRead,
  1888. NULL);
  1889. if ((readStatus != TRUE) || (dwNumBytesRead < dwRemainingArgBytes)) {
  1890. if (readStatus != TRUE) {
  1891. status = GetLastError();
  1892. SCC_LOG1(ERROR,
  1893. "ScGetPipeInput: ReadFile error (2nd read), rc = %ld\n",
  1894. status);
  1895. }
  1896. else {
  1897. status = ERROR_BAD_LENGTH;
  1898. }
  1899. SCC_LOG2(ERROR,
  1900. "ScGetPipeInput: ReadFile read: %d, expected: %d\n",
  1901. dwNumBytesRead,
  1902. dwRemainingArgBytes);
  1903. LocalFree(*ppServiceParams);
  1904. *ppServiceParams = NULL;
  1905. return status;
  1906. }
  1907. if (Msg->OpCode == SERVICE_CONTROL_START_OWN ||
  1908. Msg->OpCode == SERVICE_CONTROL_START_SHARE) {
  1909. //
  1910. // Change the offsets back into pointers.
  1911. //
  1912. ScNormalizeCmdLineArgs(Msg, &(*ppServiceParams)->ThreadStartupParms);
  1913. }
  1914. return NO_ERROR;
  1915. }
  1916. DWORD
  1917. ScGetDispatchEntry (
  1918. IN OUT LPINTERNAL_DISPATCH_ENTRY *DispatchEntryPtr,
  1919. IN LPWSTR ServiceName
  1920. )
  1921. /*++
  1922. Routine Description:
  1923. Finds an entry in the Dispatch Table for a particular service which
  1924. is identified by a service name string.
  1925. Arguments:
  1926. DispatchEntryPtr - As an input, the is a location where a pointer to
  1927. the top of the DispatchTable is placed. On return, this is the
  1928. location where the pointer to the specific dispatch entry is to
  1929. be placed. This is an opaque pointer because it could be either
  1930. ansi or unicode depending on the operational state of the dispatcher.
  1931. ServiceName - This is a pointer to the service name string that was
  1932. supplied by the service. Note that it may not be the service's
  1933. real name since we never check services that run in their own
  1934. process (bug that can never be fixed since it will break existing
  1935. services). We must check for this name instead of the real
  1936. one.
  1937. Return Value:
  1938. NO_ERROR - The operation was successful.
  1939. ERROR_SERVICE_NOT_IN_EXE - The serviceName could not be found in
  1940. the dispatch table. This indicates that the configuration database
  1941. says the serice is in this process, but the service name doesn't
  1942. exist in the dispatch table.
  1943. --*/
  1944. {
  1945. LPINTERNAL_DISPATCH_ENTRY entryPtr;
  1946. DWORD found = FALSE;
  1947. entryPtr = *DispatchEntryPtr;
  1948. if (entryPtr->dwFlags & SERVICE_OWN_PROCESS) {
  1949. return (NO_ERROR);
  1950. }
  1951. while (entryPtr->ServiceName != NULL) {
  1952. if (_wcsicmp(entryPtr->ServiceName, ServiceName) == 0) {
  1953. found = TRUE;
  1954. break;
  1955. }
  1956. entryPtr++;
  1957. }
  1958. if (found) {
  1959. *DispatchEntryPtr = entryPtr;
  1960. }
  1961. else {
  1962. SCC_LOG(ERROR,"ScGetDispatchEntry: DispatchEntry not found\n"
  1963. " Configuration error - the %ws service is not in this .exe file!\n"
  1964. " Check the table passed to StartServiceCtrlDispatcher.\n", ServiceName);
  1965. return(ERROR_SERVICE_NOT_IN_EXE);
  1966. }
  1967. return(NO_ERROR);
  1968. }
  1969. DWORD
  1970. ScRemoveDispatchEntry(
  1971. IN SC_HANDLE hStatusHandle
  1972. )
  1973. /*++
  1974. Routine Description:
  1975. Clears out the status handle for an entry in the Dispatch Table.
  1976. Arguments:
  1977. hStatusHandle - service status handle given by RegisterServiceCtrlHandler/Ex.
  1978. Return Value:
  1979. NO_ERROR - The operation was successful.
  1980. ERROR_SERVICE_NOT_IN_EXE - The service of StatusHandle could not be found in
  1981. the dispatch table. This indicates that the configuration database
  1982. says the serice is in this process, but the service doesn't exist in
  1983. the dispatch table.
  1984. --*/
  1985. {
  1986. LPINTERNAL_DISPATCH_ENTRY dispatchEntry;
  1987. //
  1988. // This should only be called from SetServiceStatus when stopping
  1989. // a service, so the table should already be there.
  1990. //
  1991. ASSERT(DispatchTable != NULL);
  1992. dispatchEntry = DispatchTable;
  1993. while (dispatchEntry->ServiceName != NULL)
  1994. {
  1995. if (dispatchEntry->StatusHandle == hStatusHandle)
  1996. {
  1997. dispatchEntry->StatusHandle = NULL;
  1998. return NO_ERROR;
  1999. }
  2000. dispatchEntry++;
  2001. }
  2002. //
  2003. // Shouldn't be called with a SERVICE_STATUS_HANDLE that's not in the table.
  2004. //
  2005. ASSERT(FALSE);
  2006. return ERROR_SERVICE_NOT_IN_EXE;
  2007. }
  2008. VOID
  2009. ScNormalizeCmdLineArgs(
  2010. IN OUT LPCTRL_MSG_HEADER Msg,
  2011. IN OUT LPTHREAD_STARTUP_PARMSW ThreadStartupParms
  2012. )
  2013. /*++
  2014. Routine Description:
  2015. Normalizes the command line argument information that came across in
  2016. the pipe. The argument information is stored in a buffer that consists
  2017. of an array of string pointers followed by the strings themselves.
  2018. However, in the pipe, the pointers are replaced with offsets. This
  2019. routine transforms the offsets into real pointers.
  2020. This routine also puts the service name into the array of argument
  2021. vectors, and adds the service name string to the end of the
  2022. buffer (space has already been allocated for it).
  2023. Arguments:
  2024. Msg - This is a pointer to the Message. Useful information from this
  2025. includes the NumCmdArgs and the service name.
  2026. ThreadStartupParms - A pointer to the thread startup parameter structure.
  2027. Return Value:
  2028. none.
  2029. --*/
  2030. {
  2031. DWORD i;
  2032. LPWSTR *argv;
  2033. DWORD numCmdArgs;
  2034. LPWSTR *serviceNameVector;
  2035. LPWSTR serviceNamePtr;
  2036. #if defined(_X86_)
  2037. PULONG64 argv64 = NULL;
  2038. #endif
  2039. numCmdArgs = Msg->NumCmdArgs;
  2040. argv = &(ThreadStartupParms->VectorTable);
  2041. //
  2042. // Save the first argv for the service name.
  2043. //
  2044. serviceNameVector = argv;
  2045. argv++;
  2046. //
  2047. // Normalize the Command Line Argument information by replacing
  2048. // offsets in buffer with pointers.
  2049. //
  2050. // NOTE: The elaborate casting that takes place here is because we
  2051. // are taking some (pointer sized) offsets, and turning them back
  2052. // into pointers to strings. The offsets are in bytes, and are
  2053. // relative to the beginning of the vector table which contains
  2054. // pointers to the various command line arg strings.
  2055. //
  2056. #if defined(_X86_)
  2057. if (g_fWow64Process) {
  2058. //
  2059. // Pointers on the 64-bit land are 64-bit so make argv
  2060. // point to the 1st arg after the service name offset
  2061. //
  2062. argv64 = (PULONG64)argv;
  2063. }
  2064. #endif
  2065. for (i = 0; i < numCmdArgs; i++) {
  2066. #if defined(_X86_)
  2067. if (g_fWow64Process)
  2068. argv[i] = (LPWSTR)((LPBYTE)argv + PtrToUlong(argv64[i]));
  2069. else
  2070. #endif
  2071. argv[i] = (LPWSTR)((LPBYTE)argv + PtrToUlong(argv[i]));
  2072. }
  2073. //
  2074. // If we are starting a service, then we need to add the service name
  2075. // to the argument vectors.
  2076. //
  2077. if ((Msg->OpCode == SERVICE_CONTROL_START_SHARE) ||
  2078. (Msg->OpCode == SERVICE_CONTROL_START_OWN)) {
  2079. numCmdArgs++;
  2080. if (numCmdArgs > 1) {
  2081. //
  2082. // Find the location for the service name string by finding
  2083. // the pointer to the last argument adding its string length
  2084. // to it.
  2085. //
  2086. serviceNamePtr = argv[i-1];
  2087. serviceNamePtr += (wcslen(serviceNamePtr) + 1);
  2088. }
  2089. else {
  2090. serviceNamePtr = (LPWSTR)argv;
  2091. }
  2092. wcscpy(serviceNamePtr, (LPWSTR) ((LPBYTE)Msg + Msg->ServiceNameOffset));
  2093. *serviceNameVector = serviceNamePtr;
  2094. }
  2095. ThreadStartupParms->NumArgs = numCmdArgs;
  2096. }
  2097. VOID
  2098. ScSendResponse (
  2099. IN HANDLE PipeHandle,
  2100. IN DWORD Response,
  2101. IN DWORD dwHandlerRetVal
  2102. )
  2103. /*++
  2104. Routine Description:
  2105. This routine sends a status response to the Service Controller's pipe.
  2106. Arguments:
  2107. Response - This is the status message that is to be sent.
  2108. dwHandlerRetVal - This is the return value from the service's control
  2109. handler function (NO_ERROR for non-Ex handlers)
  2110. Return Value:
  2111. none.
  2112. --*/
  2113. {
  2114. DWORD numBytesWritten;
  2115. PIPE_RESPONSE_MSG prmResponse;
  2116. prmResponse.dwDispatcherStatus = Response;
  2117. prmResponse.dwHandlerRetVal = dwHandlerRetVal;
  2118. if (!WriteFile(PipeHandle,
  2119. &prmResponse,
  2120. sizeof(PIPE_RESPONSE_MSG),
  2121. &numBytesWritten,
  2122. NULL))
  2123. {
  2124. SCC_LOG1(ERROR,
  2125. "ScSendResponse: WriteFile failed, rc= %d\n",
  2126. GetLastError());
  2127. }
  2128. }
  2129. DWORD
  2130. ScSvcctrlThreadW(
  2131. IN LPTHREAD_STARTUP_PARMSW lpThreadStartupParms
  2132. )
  2133. /*++
  2134. Routine Description:
  2135. This is the thread for the newly started service. This code
  2136. calls the service's main thread with parameters from the
  2137. ThreadStartupParms structure.
  2138. NOTE: The first item in the argument vector table is the pointer to
  2139. the service registry path string.
  2140. Arguments:
  2141. lpThreadStartupParms - This is a pointer to the ThreadStartupParms
  2142. structure. (This is a unicode structure);
  2143. Return Value:
  2144. --*/
  2145. {
  2146. //
  2147. // Call the Service's Main Routine.
  2148. //
  2149. ((LPSERVICE_MAIN_FUNCTIONW)lpThreadStartupParms->ServiceStartRoutine) (
  2150. lpThreadStartupParms->NumArgs,
  2151. &lpThreadStartupParms->VectorTable);
  2152. LocalFree(lpThreadStartupParms);
  2153. return(0);
  2154. }
  2155. DWORD
  2156. ScSvcctrlThreadA(
  2157. IN LPTHREAD_STARTUP_PARMSA lpThreadStartupParms
  2158. )
  2159. /*++
  2160. Routine Description:
  2161. This is the thread for the newly started service. This code
  2162. calls the service's main thread with parameters from the
  2163. ThreadStartupParms structure.
  2164. NOTE: The first item in the argument vector table is the pointer to
  2165. the service registry path string.
  2166. Arguments:
  2167. lpThreadStartupParms - This is a pointer to the ThreadStartupParms
  2168. structure. (This is a unicode structure);
  2169. Return Value:
  2170. --*/
  2171. {
  2172. //
  2173. // Call the Service's Main Routine.
  2174. //
  2175. // NOTE: The first item in the argument vector table is the pointer to
  2176. // the service registry path string.
  2177. //
  2178. ((LPSERVICE_MAIN_FUNCTIONA)lpThreadStartupParms->ServiceStartRoutine) (
  2179. lpThreadStartupParms->NumArgs,
  2180. &lpThreadStartupParms->VectorTable);
  2181. LocalFree(lpThreadStartupParms);
  2182. return(0);
  2183. }