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.

2679 lines
78 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. for (dispatchEntry = DispatchTable;
  430. dispatchEntry->ServiceName != NULL;
  431. dispatchEntry++) {
  432. LocalFree(dispatchEntry->ServiceName);
  433. }
  434. LocalFree(DispatchTable);
  435. }
  436. if (status != NO_ERROR) {
  437. SetLastError(status);
  438. return(FALSE);
  439. }
  440. return(TRUE);
  441. }
  442. BOOL
  443. WINAPI
  444. StartServiceCtrlDispatcherW (
  445. IN CONST SERVICE_TABLE_ENTRYW * lpServiceStartTable
  446. )
  447. /*++
  448. Routine Description:
  449. This is the Control Dispatcher thread. We do not return from this
  450. function call until the Control Dispatcher is told to shut down.
  451. The Control Dispatcher is responsible for connecting to the Service
  452. Controller's control pipe, and receiving messages from that pipe.
  453. The Control Dispatcher then dispatches the control messages to the
  454. correct control handling routine.
  455. Arguments:
  456. lpServiceStartTable - This is a pointer to the top of a service dispatch
  457. table that the service main process passes in. Each table entry
  458. contains pointers to the ServiceName, and the ServiceStartRotuine.
  459. Return Value:
  460. NO_ERROR - The Control Dispatcher successfully terminated.
  461. ERROR_INVALID_DATA - The specified dispatch table does not contain
  462. entries in the proper format.
  463. ERROR_FAILED_SERVICE_CONTROLLER_CONNECT - The Control Dispatcher
  464. could not connect with the Service Controller.
  465. ERROR_SERVICE_ALREADY_RUNNING - The function has already been called
  466. from within the current process
  467. --*/
  468. {
  469. DWORD status;
  470. NTSTATUS ntStatus;
  471. HANDLE pipeHandle;
  472. //
  473. // Make sure this is the only time the control dispatcher is being called
  474. //
  475. if (InterlockedExchange(&g_fCalledBefore, 1) == 1) {
  476. //
  477. // Problem -- the control dispatcher was already called from this process
  478. //
  479. SetLastError(ERROR_SERVICE_ALREADY_RUNNING);
  480. return(FALSE);
  481. }
  482. //
  483. // Create the Real Dispatch Table
  484. //
  485. __try {
  486. status = ScCreateDispatchTableW((LPSERVICE_TABLE_ENTRYW)lpServiceStartTable, &DispatchTable);
  487. }
  488. __except (EXCEPTION_EXECUTE_HANDLER) {
  489. status = GetExceptionCode();
  490. if (status != EXCEPTION_ACCESS_VIOLATION) {
  491. SCC_LOG(ERROR,"StartServiceCtrlDispatcherW:Unexpected Exception 0x%lx\n",status);
  492. }
  493. }
  494. if (status != NO_ERROR) {
  495. SetLastError(status);
  496. return(FALSE);
  497. }
  498. //
  499. // Allocate a buffer big enough to contain at least the control message
  500. // header and a service name. This ensures that if the message is not
  501. // a message with arguments, it can be read in a single ReadFile.
  502. //
  503. BYTE bPipeMessageHeader[sizeof(CTRL_MSG_HEADER) +
  504. (MAX_SERVICE_NAME_LENGTH+1) * sizeof(WCHAR)];
  505. //
  506. // Connect to the Service Controller
  507. //
  508. status = ScConnectServiceController(&pipeHandle);
  509. if (status != NO_ERROR) {
  510. //
  511. // If they're different, it's because we allocated the real name.
  512. // Only check the first entry since this can only happen in the
  513. // SERVICE_OWN_PROCESS case
  514. //
  515. if (DispatchTable->ServiceName != DispatchTable->ServiceRealName) {
  516. LocalFree(DispatchTable->ServiceRealName);
  517. }
  518. LocalFree(DispatchTable);
  519. SetLastError(status);
  520. return(FALSE);
  521. }
  522. //
  523. // Initialize the binding for the status interface (NetServiceStatus).
  524. //
  525. SCC_LOG(TRACE,"Initialize the Status binding\n",0);
  526. ntStatus = InitializeStatusBinding();
  527. if (ntStatus != STATUS_SUCCESS) {
  528. status = RtlNtStatusToDosError(ntStatus);
  529. CloseHandle(pipeHandle);
  530. //
  531. // If they're different, it's because we allocated the real name.
  532. // Only check the first entry since this can only happen in the
  533. // SERVICE_OWN_PROCESS case
  534. //
  535. if (DispatchTable->ServiceName != DispatchTable->ServiceRealName) {
  536. LocalFree(DispatchTable->ServiceRealName);
  537. }
  538. LocalFree(DispatchTable);
  539. SetLastError(status);
  540. return(FALSE);
  541. }
  542. //
  543. // Enter the dispatcher loop where we service control requests until
  544. // all services in the service table have terminated.
  545. //
  546. ScDispatcherLoop(pipeHandle,
  547. (LPCTRL_MSG_HEADER)&bPipeMessageHeader,
  548. sizeof(bPipeMessageHeader));
  549. CloseHandle(pipeHandle);
  550. //
  551. // If they're different, it's because we allocated the real name.
  552. // Only check the first entry since this can only happen in the
  553. // SERVICE_OWN_PROCESS case
  554. //
  555. if (DispatchTable->ServiceName != DispatchTable->ServiceRealName) {
  556. LocalFree(DispatchTable->ServiceRealName);
  557. }
  558. return(TRUE);
  559. }
  560. VOID
  561. ScDispatcherLoop(
  562. IN HANDLE PipeHandle,
  563. IN LPCTRL_MSG_HEADER Msg,
  564. IN DWORD dwBufferSize
  565. )
  566. /*++
  567. Routine Description:
  568. This is the input loop that the Control Dispatcher stays in through-out
  569. its life. Only two types of events will cause us to leave this loop:
  570. 1) The service controller instructed the dispatcher to exit.
  571. 2) The dispatcher can no longer communicate with the the
  572. service controller.
  573. Arguments:
  574. PipeHandle: This is a handle to the pipe over which control
  575. requests are received.
  576. Return Value:
  577. none
  578. --*/
  579. {
  580. DWORD status;
  581. DWORD controlStatus;
  582. BOOL continueDispatch;
  583. LPWSTR serviceName = NULL;
  584. LPSERVICE_PARAMS lpServiceParams;
  585. LPTHREAD_START_ROUTINE threadAddress = NULL;
  586. LPVOID threadParms = NULL;
  587. LPTHREAD_STARTUP_PARMSA lpspAnsiParms;
  588. LPINTERNAL_DISPATCH_ENTRY dispatchEntry = NULL;
  589. DWORD threadId;
  590. HANDLE threadHandle;
  591. DWORD i;
  592. DWORD errorCount = 0;
  593. DWORD dwHandlerRetVal = NO_ERROR;
  594. //
  595. // Input Loop
  596. //
  597. continueDispatch = TRUE;
  598. #if defined(_X86_)
  599. //
  600. // Detect if this is a Wow64 Process ?
  601. //
  602. DetectWow64Process();
  603. #endif
  604. do {
  605. //
  606. // Wait for input
  607. //
  608. controlStatus = ScGetPipeInput(PipeHandle,
  609. Msg,
  610. dwBufferSize,
  611. &lpServiceParams);
  612. //
  613. // If we received good input, check to see if we are to shut down
  614. // the ControlDispatcher. If not, then obtain the dispatchEntry
  615. // from the dispatch table.
  616. //
  617. if (controlStatus == NO_ERROR) {
  618. //
  619. // Clear the error count
  620. //
  621. errorCount = 0;
  622. serviceName = (LPWSTR) ((LPBYTE)Msg + Msg->ServiceNameOffset);
  623. SCC_LOG(TRACE, "Read from pipe succeeded for service %ws\n", serviceName);
  624. if ((serviceName[0] == L'\0') &&
  625. (Msg->OpCode == SERVICE_STOP)) {
  626. //
  627. // The Dispatcher is being asked to shut down.
  628. // (security check not required for this operation)
  629. // although perhaps it would be a good idea to verify
  630. // that the request came from the Service Controller.
  631. //
  632. controlStatus = NO_ERROR;
  633. continueDispatch = FALSE;
  634. }
  635. else {
  636. dispatchEntry = DispatchTable;
  637. if (Msg->OpCode == SERVICE_CONTROL_START_OWN) {
  638. dispatchEntry->dwFlags |= SERVICE_OWN_PROCESS;
  639. }
  640. //
  641. // Search the dispatch table to find the service's entry
  642. //
  643. if (!(dispatchEntry->dwFlags & SERVICE_OWN_PROCESS)) {
  644. controlStatus = ScGetDispatchEntry(&dispatchEntry, serviceName);
  645. }
  646. if (controlStatus != NO_ERROR) {
  647. SCC_LOG(TRACE,"Service Name not in Dispatch Table\n",0);
  648. }
  649. }
  650. }
  651. else {
  652. if (controlStatus != ERROR_NOT_ENOUGH_MEMORY) {
  653. //
  654. // If an error occured and it is not an out-of-memory error,
  655. // then the pipe read must have failed.
  656. // In this case we Increment the error count.
  657. // When this count reaches the MAX_RETRY_COUNT, then
  658. // the service controller must be gone. We want to log an
  659. // error and notify an administrator. Then go to sleep forever.
  660. // Only a re-boot will solve this problem.
  661. //
  662. // We should be able to report out-of-memory errors back to
  663. // the caller. It should be noted that out-of-memory errors
  664. // do not clear the error count. But they don't add to it
  665. // either.
  666. //
  667. errorCount++;
  668. if (errorCount > MAX_RETRY_COUNT) {
  669. Sleep(0xffffffff);
  670. }
  671. }
  672. }
  673. //
  674. // Dispatch the request
  675. //
  676. if ((continueDispatch == TRUE) && (controlStatus == NO_ERROR)) {
  677. status = NO_ERROR;
  678. switch(Msg->OpCode) {
  679. case SERVICE_CONTROL_START_SHARE:
  680. case SERVICE_CONTROL_START_OWN:
  681. {
  682. SC_HANDLE hScManager = OpenSCManagerW(NULL,
  683. NULL,
  684. SC_MANAGER_CONNECT);
  685. if (hScManager == NULL) {
  686. status = GetLastError();
  687. SCC_LOG1(ERROR,
  688. "ScDispatcherLoop: OpenSCManagerW FAILED %d\n",
  689. status);
  690. }
  691. else {
  692. //
  693. // Update the StatusHandle in the dispatch entry table
  694. //
  695. dispatchEntry->StatusHandle = OpenServiceW(hScManager,
  696. serviceName,
  697. SERVICE_SET_STATUS);
  698. if (dispatchEntry->StatusHandle == NULL) {
  699. status = GetLastError();
  700. SCC_LOG1(ERROR,
  701. "ScDispatcherLoop: OpenServiceW FAILED %d\n",
  702. status);
  703. }
  704. CloseServiceHandle(hScManager);
  705. }
  706. if (status == NO_ERROR
  707. &&
  708. (dispatchEntry->dwFlags & SERVICE_OWN_PROCESS)
  709. &&
  710. (_wcsicmp(dispatchEntry->ServiceName, serviceName) != 0))
  711. {
  712. //
  713. // Since we don't look up the dispatch record in the OWN_PROCESS
  714. // case (and can't since it will break existing services), there's
  715. // no guarantee that the name in the dispatch table (acquired from
  716. // the RegisterServiceCtrlHandler call) is the real key name of
  717. // the service. Since the SCM passes across the real name when
  718. // the service is started, save it away here if necessary.
  719. //
  720. dispatchEntry->ServiceRealName = (LPWSTR)LocalAlloc(
  721. LMEM_FIXED,
  722. WCSSIZE(serviceName)
  723. );
  724. if (dispatchEntry->ServiceRealName == NULL) {
  725. //
  726. // In case somebody comes searching for the handle (though
  727. // they shouldn't), it's nicer to return an incorrect name
  728. // than to AV trying to copy a NULL pointer.
  729. //
  730. SCC_LOG1(ERROR,
  731. "ScDispatcherLoop: Could not duplicate name for service %ws\n",
  732. serviceName);
  733. dispatchEntry->ServiceRealName = dispatchEntry->ServiceName;
  734. status = ERROR_NOT_ENOUGH_MEMORY;
  735. }
  736. else {
  737. wcscpy(dispatchEntry->ServiceRealName, serviceName);
  738. }
  739. }
  740. if (status == NO_ERROR) {
  741. //
  742. // The Control Dispatcher is to start a service.
  743. // start the new thread.
  744. //
  745. lpServiceParams->ThreadStartupParms.ServiceStartRoutine =
  746. dispatchEntry->ServiceStartRoutine.U;
  747. threadAddress = (LPTHREAD_START_ROUTINE)ScSvcctrlThreadW;
  748. threadParms = (LPVOID)&lpServiceParams->ThreadStartupParms;
  749. //
  750. // If the service needs to be called with ansi parameters,
  751. // then do the conversion here.
  752. //
  753. if (AnsiFlag) {
  754. lpspAnsiParms = (LPTHREAD_STARTUP_PARMSA)
  755. &lpServiceParams->ThreadStartupParms;
  756. for (i = 0;
  757. i < lpServiceParams->ThreadStartupParms.NumArgs;
  758. i++) {
  759. if (!ScConvertToAnsi(
  760. *(&lpspAnsiParms->VectorTable + i),
  761. *(&lpServiceParams->ThreadStartupParms.VectorTable + i))) {
  762. //
  763. // Conversion error occured.
  764. //
  765. SCC_LOG0(ERROR,
  766. "ScDispatcherLoop: Could not convert "
  767. "args to ANSI\n");
  768. status = ERROR_NOT_ENOUGH_MEMORY;
  769. }
  770. }
  771. threadAddress = (LPTHREAD_START_ROUTINE)ScSvcctrlThreadA;
  772. threadParms = lpspAnsiParms;
  773. }
  774. }
  775. if (status == NO_ERROR){
  776. //
  777. // Create the new thread
  778. //
  779. threadHandle = CreateThread (
  780. NULL, // Thread Attributes.
  781. 0L, // Stack Size
  782. threadAddress, // lpStartAddress
  783. threadParms, // lpParameter
  784. 0L, // Creation Flags
  785. &threadId); // lpThreadId
  786. if (threadHandle == NULL) {
  787. SCC_LOG(ERROR,
  788. "ScDispatcherLoop: CreateThread failed %d\n",
  789. GetLastError());
  790. status = ERROR_SERVICE_NO_THREAD;
  791. }
  792. else {
  793. CloseHandle(threadHandle);
  794. }
  795. }
  796. break;
  797. }
  798. default:
  799. if (dispatchEntry->ControlHandler.Ex != NULL) {
  800. __try {
  801. //
  802. // Call the proper ControlHandler routine.
  803. //
  804. if (dispatchEntry->dwFlags & SERVICE_EX_HANDLER)
  805. {
  806. SCC_LOG2(TRACE,
  807. "Calling extended ControlHandler routine %x "
  808. "for service %ws\n",
  809. Msg->OpCode,
  810. serviceName);
  811. if (lpServiceParams)
  812. {
  813. dwHandlerRetVal = dispatchEntry->ControlHandler.Ex(
  814. Msg->OpCode,
  815. lpServiceParams->HandlerExParms.dwEventType,
  816. lpServiceParams->HandlerExParms.lpEventData,
  817. dispatchEntry->pContext);
  818. }
  819. else
  820. {
  821. dwHandlerRetVal = dispatchEntry->ControlHandler.Ex(
  822. Msg->OpCode,
  823. 0,
  824. NULL,
  825. dispatchEntry->pContext);
  826. }
  827. SCC_LOG3(TRACE,
  828. "Extended ControlHandler routine %x "
  829. "returned %d from call to service %ws\n",
  830. Msg->OpCode,
  831. dwHandlerRetVal,
  832. serviceName);
  833. }
  834. else if (IS_NON_EX_CONTROL(Msg->OpCode))
  835. {
  836. SCC_LOG2(TRACE,
  837. "Calling ControlHandler routine %x "
  838. "for service %ws\n",
  839. Msg->OpCode,
  840. serviceName);
  841. #if defined(_X86_)
  842. //
  843. // Hack for __CDECL callbacks. The Windows NT 3.1
  844. // SDK didn't prototype control handler functions
  845. // as WINAPI, so a number of existing 3rd-party
  846. // services have their control handler functions
  847. // built as __cdecl instead. This is a workaround.
  848. // Note that the call to the control handler must
  849. // be the only code between the _asm statements
  850. //
  851. DWORD SaveEdi;
  852. _asm mov SaveEdi, edi;
  853. _asm mov edi, esp; // Both __cdecl and WINAPI
  854. // functions preserve EDI
  855. #endif
  856. //
  857. // Do not add code here
  858. //
  859. dispatchEntry->ControlHandler.Regular(Msg->OpCode);
  860. //
  861. // Do not add code here
  862. //
  863. #if defined(_X86_)
  864. _asm mov esp, edi;
  865. _asm mov edi, SaveEdi;
  866. #endif
  867. SCC_LOG2(TRACE,
  868. "ControlHandler routine %x returned from "
  869. "call to service %ws\n",
  870. Msg->OpCode,
  871. serviceName);
  872. }
  873. else
  874. {
  875. //
  876. // Service registered for an extended control without
  877. // registering an extended handler. The call into the
  878. // service process succeeded, so keep status as NO_ERROR.
  879. // Return an error from the "handler" to notify anybody
  880. // watching for the return code (especially PnP).
  881. //
  882. dwHandlerRetVal = ERROR_CALL_NOT_IMPLEMENTED;
  883. }
  884. }
  885. __except (EXCEPTION_EXECUTE_HANDLER)
  886. {
  887. SCC_LOG2(ERROR,
  888. "ScDispatcherLoop: Exception 0x%lx "
  889. "occurred in service %ws\n",
  890. GetExceptionCode(),
  891. serviceName);
  892. status = ERROR_EXCEPTION_IN_SERVICE;
  893. }
  894. }
  895. else
  896. {
  897. //
  898. // There is no control handling routine
  899. // registered for this service
  900. //
  901. status = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
  902. }
  903. LocalFree(lpServiceParams);
  904. lpServiceParams = NULL;
  905. // If status is not good here, then an exception occured
  906. // either because the pointer to the control handling
  907. // routine was bad, or because an exception occured
  908. // inside the control handling routine.
  909. //
  910. // ??EVENTLOG??
  911. //
  912. break;
  913. } // end switch.
  914. //
  915. // Send the status back to the sevice controller.
  916. //
  917. if (Msg->OpCode != SERVICE_CONTROL_SHUTDOWN)
  918. {
  919. SCC_LOG(TRACE, "Service %ws about to send response\n", serviceName);
  920. ScSendResponse (PipeHandle, status, dwHandlerRetVal);
  921. SCC_LOG(TRACE, "Service %ws returned from sending response\n", serviceName);
  922. }
  923. }
  924. else {
  925. //
  926. // The controlStatus indicates failure, we always want to try
  927. // to send the status back to the Service Controller.
  928. //
  929. SCC_LOG2(TRACE,
  930. "Service %ws about to send response on error %lu\n",
  931. serviceName,
  932. controlStatus);
  933. ScSendResponse(PipeHandle, controlStatus, dwHandlerRetVal);
  934. SCC_LOG2(TRACE,
  935. "Service %ws returned from sending response on error %lu\n",
  936. serviceName,
  937. controlStatus);
  938. switch (controlStatus) {
  939. case ERROR_SERVICE_NOT_IN_EXE:
  940. case ERROR_SERVICE_NO_THREAD:
  941. //
  942. // The Service Name is not in this .exe's dispatch table.
  943. // Or a thread for a new service couldn't be created.
  944. // ignore it. The Service Controller will tell us to
  945. // shut down if necessary.
  946. //
  947. controlStatus = NO_ERROR;
  948. break;
  949. default:
  950. //
  951. // If the error is not specifically recognized, continue.
  952. //
  953. controlStatus = NO_ERROR;
  954. break;
  955. }
  956. }
  957. }
  958. while (continueDispatch == TRUE);
  959. return;
  960. }
  961. SERVICE_STATUS_HANDLE
  962. WINAPI
  963. RegisterServiceCtrlHandlerHelp (
  964. IN LPCWSTR ServiceName,
  965. IN HANDLER_FUNCTION_TYPE ControlHandler,
  966. IN PVOID pContext,
  967. IN DWORD dwFlags
  968. )
  969. /*++
  970. Routine Description:
  971. This helper function enters a pointer to a control handling routine
  972. and a pointer to a security descriptor into the Control Dispatcher's
  973. dispatch table. It does the work for the RegisterServiceCtrlHandler*
  974. family of APIs
  975. Arguments:
  976. ServiceName - This is a pointer to the Service Name string.
  977. ControlHandler - This is a pointer to the service's control handling
  978. routine.
  979. pContext - This is a pointer to context data supplied by the user.
  980. dwFlags - This is a set of flags that give information about the
  981. control handling routine (currently only discerns between extended
  982. and non-extended handlers)
  983. Return Value:
  984. This function returns a handle to the service that is to be used in
  985. subsequent calls to SetServiceStatus. If the return value is NULL,
  986. an error has occured, and GetLastError can be used to obtain the
  987. error value. Possible values for error are:
  988. NO_ERROR - If the operation was successful.
  989. ERROR_INVALID_PARAMETER - The pointer to the control handler function
  990. is NULL.
  991. ERROR_INVALID_DATA -
  992. ERROR_SERVICE_NOT_IN_EXE - The serviceName could not be found in
  993. the dispatch table. This indicates that the configuration database
  994. says the serice is in this process, but the service name doesn't
  995. exist in the dispatch table.
  996. --*/
  997. {
  998. DWORD status;
  999. LPINTERNAL_DISPATCH_ENTRY dispatchEntry;
  1000. //
  1001. // Find the service in the dispatch table.
  1002. //
  1003. dispatchEntry = DispatchTable;
  1004. __try {
  1005. status = ScGetDispatchEntry(&dispatchEntry, (LPWSTR) ServiceName);
  1006. }
  1007. __except (EXCEPTION_EXECUTE_HANDLER) {
  1008. status = GetExceptionCode();
  1009. if (status != EXCEPTION_ACCESS_VIOLATION) {
  1010. SCC_LOG(ERROR,
  1011. "RegisterServiceCtrlHandlerHelp: Unexpected Exception 0x%lx\n",
  1012. status);
  1013. }
  1014. }
  1015. if(status != NO_ERROR) {
  1016. SCC_LOG(ERROR,
  1017. "RegisterServiceCtrlHandlerHelp: can't find dispatch entry\n",0);
  1018. SetLastError(status);
  1019. return(0L);
  1020. }
  1021. //
  1022. // Insert the ControlHandler pointer
  1023. //
  1024. if (ControlHandler.Ex == NULL) {
  1025. SCC_LOG(ERROR,
  1026. "RegisterServiceCtrlHandlerHelp: Ptr to ctrlhandler is NULL\n",
  1027. 0);
  1028. SetLastError(ERROR_INVALID_PARAMETER);
  1029. return(0L);
  1030. }
  1031. //
  1032. // Insert the entries into the table
  1033. //
  1034. if (dwFlags & SERVICE_EX_HANDLER) {
  1035. dispatchEntry->dwFlags |= SERVICE_EX_HANDLER;
  1036. dispatchEntry->ControlHandler.Ex = ControlHandler.Ex;
  1037. dispatchEntry->pContext = pContext;
  1038. }
  1039. else {
  1040. dispatchEntry->dwFlags &= ~(SERVICE_EX_HANDLER);
  1041. dispatchEntry->ControlHandler.Regular = ControlHandler.Regular;
  1042. }
  1043. //
  1044. // This cast is OK -- see comment in I_ScPnPGetServiceName
  1045. //
  1046. return( (SERVICE_STATUS_HANDLE) dispatchEntry->StatusHandle );
  1047. }
  1048. SERVICE_STATUS_HANDLE
  1049. WINAPI
  1050. RegisterServiceCtrlHandlerW (
  1051. IN LPCWSTR ServiceName,
  1052. IN LPHANDLER_FUNCTION ControlHandler
  1053. )
  1054. /*++
  1055. Routine Description:
  1056. This function enters a pointer to a control handling
  1057. routine into the Control Dispatcher's dispatch table.
  1058. Arguments:
  1059. ServiceName -- The service's name
  1060. ControlHandler -- Pointer to the control handling routine
  1061. Return Value:
  1062. Anything returned by RegisterServiceCtrlHandlerHelp
  1063. --*/
  1064. {
  1065. HANDLER_FUNCTION_TYPE Handler;
  1066. Handler.Regular = ControlHandler;
  1067. return RegisterServiceCtrlHandlerHelp(ServiceName,
  1068. Handler,
  1069. NULL,
  1070. 0);
  1071. }
  1072. SERVICE_STATUS_HANDLE
  1073. WINAPI
  1074. RegisterServiceCtrlHandlerA (
  1075. IN LPCSTR ServiceName,
  1076. IN LPHANDLER_FUNCTION ControlHandler
  1077. )
  1078. /*++
  1079. Routine Description:
  1080. This is the ansi entry point for RegisterServiceCtrlHandler.
  1081. Arguments:
  1082. Return Value:
  1083. --*/
  1084. {
  1085. LPWSTR ServiceNameW;
  1086. SERVICE_STATUS_HANDLE statusHandle;
  1087. if (!ScConvertToUnicode(&ServiceNameW, ServiceName)) {
  1088. //
  1089. // This can only fail because of a failed LocalAlloc call
  1090. // or else the ansi string is garbage.
  1091. //
  1092. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1093. return(0L);
  1094. }
  1095. statusHandle = RegisterServiceCtrlHandlerW(ServiceNameW, ControlHandler);
  1096. LocalFree(ServiceNameW);
  1097. return(statusHandle);
  1098. }
  1099. SERVICE_STATUS_HANDLE
  1100. WINAPI
  1101. RegisterServiceCtrlHandlerExW (
  1102. IN LPCWSTR ServiceName,
  1103. IN LPHANDLER_FUNCTION_EX ControlHandler,
  1104. IN PVOID pContext
  1105. )
  1106. /*++
  1107. Routine Description:
  1108. This function enters a pointer to an extended control handling
  1109. routine into the Control Dispatcher's dispatch table. It is
  1110. analogous to RegisterServiceCtrlHandlerW.
  1111. Arguments:
  1112. ServiceName -- The service's name
  1113. ControlHandler -- A pointer to an extended control handling routine
  1114. pContext -- User-supplied data that is passed to the control handler
  1115. Return Value:
  1116. Anything returned by RegisterServiceCtrlHandlerHelp
  1117. --*/
  1118. {
  1119. HANDLER_FUNCTION_TYPE Handler;
  1120. Handler.Ex = ControlHandler;
  1121. return RegisterServiceCtrlHandlerHelp(ServiceName,
  1122. Handler,
  1123. pContext,
  1124. SERVICE_EX_HANDLER);
  1125. }
  1126. SERVICE_STATUS_HANDLE
  1127. WINAPI
  1128. RegisterServiceCtrlHandlerExA (
  1129. IN LPCSTR ServiceName,
  1130. IN LPHANDLER_FUNCTION_EX ControlHandler,
  1131. IN PVOID pContext
  1132. )
  1133. /*++
  1134. Routine Description:
  1135. This is the ansi entry point for RegisterServiceCtrlHandlerEx.
  1136. Arguments:
  1137. Return Value:
  1138. --*/
  1139. {
  1140. LPWSTR ServiceNameW;
  1141. SERVICE_STATUS_HANDLE statusHandle;
  1142. if(!ScConvertToUnicode(&ServiceNameW, ServiceName)) {
  1143. //
  1144. // This can only fail because of a failed LocalAlloc call
  1145. // or else the ansi string is garbage.
  1146. //
  1147. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  1148. return(0L);
  1149. }
  1150. statusHandle = RegisterServiceCtrlHandlerExW(ServiceNameW,
  1151. ControlHandler,
  1152. pContext);
  1153. LocalFree(ServiceNameW);
  1154. return(statusHandle);
  1155. }
  1156. DWORD
  1157. ScCreateDispatchTableW(
  1158. IN CONST SERVICE_TABLE_ENTRYW *lpServiceStartTable,
  1159. OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
  1160. )
  1161. /*++
  1162. Routine Description:
  1163. This routine allocates space for the Control Dispatchers Dispatch Table.
  1164. It also initializes the table with the data that the service main
  1165. routine passed in with the lpServiceStartTable parameter.
  1166. This routine expects that pointers in the user's dispatch table point
  1167. to valid information. And that that information will stay valid and
  1168. fixed through out the life of the Control Dispatcher. In otherwords,
  1169. the ServiceName string better not move or get cleared.
  1170. Arguments:
  1171. lpServiceStartTable - This is a pointer to the first entry in the
  1172. dispatch table that the service's main routine passed in .
  1173. DispatchTablePtr - This is a pointer to the location where the
  1174. Service Controller's dispatch table is to be stored.
  1175. Return Value:
  1176. NO_ERROR - The operation was successful.
  1177. ERROR_NOT_ENOUGH_MEMORY - The memory allocation failed.
  1178. ERROR_INVALID_PARAMETER - There are no entries in the dispatch table.
  1179. --*/
  1180. {
  1181. DWORD numEntries;
  1182. LPINTERNAL_DISPATCH_ENTRY dispatchTable;
  1183. const SERVICE_TABLE_ENTRYW * entryPtr;
  1184. //
  1185. // Count the number of entries in the user dispatch table
  1186. //
  1187. numEntries = 0;
  1188. entryPtr = lpServiceStartTable;
  1189. while (entryPtr->lpServiceName != NULL) {
  1190. numEntries++;
  1191. entryPtr++;
  1192. }
  1193. if (numEntries == 0) {
  1194. SCC_LOG(ERROR,"ScCreateDispatchTable:No entries in Dispatch table!\n",0);
  1195. return(ERROR_INVALID_PARAMETER);
  1196. }
  1197. //
  1198. // Allocate space for the Control Dispatcher's Dispatch Table
  1199. //
  1200. dispatchTable = (LPINTERNAL_DISPATCH_ENTRY)LocalAlloc(LMEM_ZEROINIT,
  1201. sizeof(INTERNAL_DISPATCH_ENTRY) * (numEntries + 1));
  1202. if (dispatchTable == NULL) {
  1203. SCC_LOG(ERROR,"ScCreateDispatchTable: Local Alloc failed rc = %d\n",
  1204. GetLastError());
  1205. return (ERROR_NOT_ENOUGH_MEMORY);
  1206. }
  1207. //
  1208. // Move user dispatch info into the Control Dispatcher's table.
  1209. //
  1210. *DispatchTablePtr = dispatchTable;
  1211. entryPtr = lpServiceStartTable;
  1212. while (entryPtr->lpServiceName != NULL) {
  1213. dispatchTable->ServiceName = entryPtr->lpServiceName;
  1214. dispatchTable->ServiceRealName = entryPtr->lpServiceName;
  1215. dispatchTable->ServiceStartRoutine.U= entryPtr->lpServiceProc;
  1216. dispatchTable->ControlHandler.Ex = NULL;
  1217. dispatchTable->StatusHandle = NULL;
  1218. dispatchTable->dwFlags = 0;
  1219. entryPtr++;
  1220. dispatchTable++;
  1221. }
  1222. return (NO_ERROR);
  1223. }
  1224. DWORD
  1225. ScCreateDispatchTableA(
  1226. IN CONST SERVICE_TABLE_ENTRYA *lpServiceStartTable,
  1227. OUT LPINTERNAL_DISPATCH_ENTRY *DispatchTablePtr
  1228. )
  1229. /*++
  1230. Routine Description:
  1231. This routine allocates space for the Control Dispatchers Dispatch Table.
  1232. It also initializes the table with the data that the service main
  1233. routine passed in with the lpServiceStartTable parameter.
  1234. This routine expects that pointers in the user's dispatch table point
  1235. to valid information. And that that information will stay valid and
  1236. fixed through out the life of the Control Dispatcher. In otherwords,
  1237. the ServiceName string better not move or get cleared.
  1238. Arguments:
  1239. lpServiceStartTable - This is a pointer to the first entry in the
  1240. dispatch table that the service's main routine passed in .
  1241. DispatchTablePtr - This is a pointer to the location where the
  1242. Service Controller's dispatch table is to be stored.
  1243. Return Value:
  1244. NO_ERROR - The operation was successful.
  1245. ERROR_NOT_ENOUGH_MEMORY - The memory allocation failed.
  1246. ERROR_INVALID_PARAMETER - There are no entries in the dispatch table.
  1247. --*/
  1248. {
  1249. DWORD numEntries;
  1250. DWORD status = NO_ERROR;
  1251. LPINTERNAL_DISPATCH_ENTRY dispatchTable;
  1252. const SERVICE_TABLE_ENTRYA * entryPtr;
  1253. //
  1254. // Count the number of entries in the user dispatch table
  1255. //
  1256. numEntries = 0;
  1257. entryPtr = lpServiceStartTable;
  1258. while (entryPtr->lpServiceName != NULL) {
  1259. numEntries++;
  1260. entryPtr++;
  1261. }
  1262. if (numEntries == 0) {
  1263. SCC_LOG(ERROR,"ScCreateDispatchTable:No entries in Dispatch table!\n",0);
  1264. return(ERROR_INVALID_PARAMETER);
  1265. }
  1266. //
  1267. // Allocate space for the Control Dispatcher's Dispatch Table
  1268. //
  1269. dispatchTable = (LPINTERNAL_DISPATCH_ENTRY)LocalAlloc(LMEM_ZEROINIT,
  1270. sizeof(INTERNAL_DISPATCH_ENTRY) * (numEntries + 1));
  1271. if (dispatchTable == NULL) {
  1272. SCC_LOG(ERROR,"ScCreateDispatchTableA: Local Alloc failed rc = %d\n",
  1273. GetLastError());
  1274. return (ERROR_NOT_ENOUGH_MEMORY);
  1275. }
  1276. //
  1277. // Move user dispatch info into the Control Dispatcher's table.
  1278. //
  1279. *DispatchTablePtr = dispatchTable;
  1280. entryPtr = lpServiceStartTable;
  1281. while (entryPtr->lpServiceName != NULL) {
  1282. //
  1283. // Convert the service name to unicode
  1284. //
  1285. __try {
  1286. if (!ScConvertToUnicode(
  1287. &(dispatchTable->ServiceName),
  1288. entryPtr->lpServiceName)) {
  1289. //
  1290. // The convert failed.
  1291. //
  1292. SCC_LOG(ERROR,"ScCreateDispatcherTableA:ScConvertToUnicode failed\n",0);
  1293. //
  1294. // This is the only reason for failure that I can think of.
  1295. //
  1296. status = ERROR_NOT_ENOUGH_MEMORY;
  1297. }
  1298. }
  1299. __except (EXCEPTION_EXECUTE_HANDLER) {
  1300. status = GetExceptionCode();
  1301. if (status != EXCEPTION_ACCESS_VIOLATION) {
  1302. SCC_LOG(ERROR,
  1303. "ScCreateDispatchTableA: Unexpected Exception 0x%lx\n",status);
  1304. }
  1305. }
  1306. if (status != NO_ERROR) {
  1307. //
  1308. // If an error occured, free up the allocated resources.
  1309. //
  1310. dispatchTable = *DispatchTablePtr;
  1311. while (dispatchTable->ServiceName != NULL) {
  1312. LocalFree(dispatchTable->ServiceName);
  1313. dispatchTable++;
  1314. }
  1315. LocalFree(*DispatchTablePtr);
  1316. return(status);
  1317. }
  1318. //
  1319. // Fill in the rest of the dispatch entry.
  1320. //
  1321. dispatchTable->ServiceRealName = dispatchTable->ServiceName;
  1322. dispatchTable->ServiceStartRoutine.A= entryPtr->lpServiceProc;
  1323. dispatchTable->ControlHandler.Ex = NULL;
  1324. dispatchTable->StatusHandle = NULL;
  1325. dispatchTable->dwFlags = 0;
  1326. entryPtr++;
  1327. dispatchTable++;
  1328. }
  1329. return (NO_ERROR);
  1330. }
  1331. DWORD
  1332. ScReadServiceParms(
  1333. IN LPCTRL_MSG_HEADER Msg,
  1334. IN DWORD dwNumBytesRead,
  1335. OUT LPBYTE *ppServiceParams,
  1336. OUT LPBYTE *ppTempArgPtr,
  1337. OUT LPDWORD lpdwRemainingArgBytes
  1338. )
  1339. /*++
  1340. Routine Description:
  1341. This routine calculates the number of bytes needed for the service's
  1342. control parameters by using the arg count information in the
  1343. message header. The parameter structure is allocated and
  1344. as many bytes of argument information as have been captured so far
  1345. are placed into the buffer. A second read of the pipe may be necessary
  1346. to obtain the remaining bytes of argument information.
  1347. NOTE: This function allocates enough space in the startup parameter
  1348. buffer for the service name and pointer as well as the rest of the
  1349. arguments. However, it does not put the service name into the argument
  1350. list. This is because it may take two calls to this routine to
  1351. get all the argument information. We can't insert the service name
  1352. string until we have all the rest of the argument data.
  1353. [serviceNamePtr][argv1][argv2]...[argv1Str][argv2Str]...[serviceNameStr]
  1354. or
  1355. [serviceNamePtr][dwEventType][EventData][serviceNameStr]
  1356. Arguments:
  1357. Msg - A pointer to the pipe message header.
  1358. dwNumBytesRead - The number of bytes read in the first pipe read.
  1359. ppServiceParams - A pointer to a location where the pointer to the
  1360. thread startup parameter structure is to be placed.
  1361. ppTempArgPtr - A location that will contain the pointer to where
  1362. more argument data can be placed by a second read of the pipe.
  1363. lpdwRemainingArgBytes - Returns with a count of the number of argument
  1364. bytes that remain to be read from the pipe.
  1365. Return Value:
  1366. NO_ERROR - If the operation was successful.
  1367. ERROR_NOT_ENOUGH_MEMORY - If the memory allocation was unsuccessful.
  1368. Note:
  1369. --*/
  1370. {
  1371. DWORD dwNameSize; // num bytes in ServiceName.
  1372. DWORD dwBufferSize; // num bytes for parameter buffer
  1373. LONG lArgBytesRead; // number of arg bytes in first read.
  1374. LPSERVICE_PARAMS lpTempParams;
  1375. //
  1376. // Set out pointer to no arguments unless we discover otherwise
  1377. //
  1378. *ppTempArgPtr = NULL;
  1379. SCC_LOG(TRACE,"ScReadServiceParms: Get service parameters from pipe\n",0);
  1380. //
  1381. // Note: Here we assume that the service name was read into the buffer
  1382. // in its entirety.
  1383. //
  1384. dwNameSize = (DWORD) WCSSIZE((LPWSTR) ((LPBYTE) Msg + Msg->ServiceNameOffset));
  1385. //
  1386. // Calculate the size of buffer needed. This will consist of a
  1387. // SERVICE_PARAMS structure, plus the service name and a pointer
  1388. // for it, plus the rest of the arg info sent in the message
  1389. // (We are wasting 4 bytes here since the first pointer in
  1390. // the vector table is accounted for twice - but what the heck!).
  1391. //
  1392. dwBufferSize = Msg->Count -
  1393. sizeof(CTRL_MSG_HEADER) +
  1394. sizeof(SERVICE_PARAMS) +
  1395. sizeof(LPWSTR);
  1396. //
  1397. // Allocate the memory for the service parameters
  1398. //
  1399. lpTempParams = (LPSERVICE_PARAMS)LocalAlloc (LMEM_ZEROINIT, dwBufferSize);
  1400. if (lpTempParams == NULL)
  1401. {
  1402. SCC_LOG1(ERROR,
  1403. "ScReadServiceParms: LocalAlloc failed rc = %d\n",
  1404. GetLastError());
  1405. return ERROR_NOT_ENOUGH_MEMORY;
  1406. }
  1407. lArgBytesRead = dwNumBytesRead - sizeof(CTRL_MSG_HEADER) - dwNameSize;
  1408. *lpdwRemainingArgBytes = Msg->Count - dwNumBytesRead;
  1409. //
  1410. // Unpack message-specific arguments
  1411. //
  1412. switch (Msg->OpCode) {
  1413. case SERVICE_CONTROL_START_OWN:
  1414. case SERVICE_CONTROL_START_SHARE:
  1415. SCC_LOG(TRACE,"ScReadServiceParms: Starting a service\n", 0);
  1416. if (Msg->NumCmdArgs != 0) {
  1417. //
  1418. // There's only a vector table and ThreadStartupParms
  1419. // when the service starts up
  1420. //
  1421. *ppTempArgPtr = (LPBYTE)&lpTempParams->ThreadStartupParms.VectorTable;
  1422. //
  1423. // Leave the first vector location blank for the service name
  1424. // pointer.
  1425. //
  1426. (*ppTempArgPtr) += sizeof(LPWSTR);
  1427. //
  1428. // adjust lArgBytesRead to remove any extra bytes that are
  1429. // there for alignment. If a name that is not in the dispatch
  1430. // table is passed in, it could be larger than our buffer.
  1431. // This could cause lArgBytesRead to become negative.
  1432. // However it should fail safe anyway since the name simply
  1433. // won't be recognized and an error will be returned.
  1434. //
  1435. lArgBytesRead -= (Msg->ArgvOffset - Msg->ServiceNameOffset - dwNameSize);
  1436. //
  1437. // Copy any portion of the command arg info from the first read
  1438. // into the buffer that is to be used for the second read.
  1439. //
  1440. if (lArgBytesRead > 0) {
  1441. RtlCopyMemory(*ppTempArgPtr,
  1442. (LPBYTE)Msg + Msg->ArgvOffset,
  1443. lArgBytesRead);
  1444. *ppTempArgPtr += lArgBytesRead;
  1445. }
  1446. }
  1447. break;
  1448. case SERVICE_CONTROL_DEVICEEVENT:
  1449. case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
  1450. case SERVICE_CONTROL_POWEREVENT:
  1451. case SERVICE_CONTROL_SESSIONCHANGE:
  1452. {
  1453. //
  1454. // This is a PnP, power, or TS message
  1455. //
  1456. SCC_LOG1(TRACE,
  1457. "ScReadServiceParms: Receiving PnP/power/TS event %x\n",
  1458. Msg->OpCode);
  1459. //
  1460. // adjust lArgBytesRead to remove any extra bytes that are
  1461. // there for alignment. If a name that is not in the dispatch
  1462. // table is passed in, it could be larger than our buffer.
  1463. // This could cause lArgBytesRead to become negative.
  1464. // However it should fail safe anyway since the name simply
  1465. // won't be recognized and an error will be returned.
  1466. //
  1467. lArgBytesRead -= (Msg->ArgvOffset - Msg->ServiceNameOffset - dwNameSize);
  1468. *ppTempArgPtr = (LPBYTE) &lpTempParams->HandlerExParms.dwEventType;
  1469. if (lArgBytesRead > 0)
  1470. {
  1471. LPBYTE lpArgs;
  1472. LPHANDLEREX_PARMS lpHandlerExParms = (LPHANDLEREX_PARMS) (*ppTempArgPtr);
  1473. lpArgs = (LPBYTE) Msg + Msg->ArgvOffset;
  1474. lpHandlerExParms->dwEventType = *(LPDWORD) lpArgs;
  1475. lpArgs += sizeof(DWORD);
  1476. lArgBytesRead -= sizeof(DWORD);
  1477. RtlCopyMemory(lpHandlerExParms + 1,
  1478. lpArgs,
  1479. lArgBytesRead);
  1480. lpHandlerExParms->lpEventData = lpHandlerExParms + 1;
  1481. *ppTempArgPtr = (LPBYTE) (lpHandlerExParms + 1) + lArgBytesRead;
  1482. }
  1483. break;
  1484. }
  1485. }
  1486. *ppServiceParams = (LPBYTE) lpTempParams;
  1487. return NO_ERROR;
  1488. }
  1489. DWORD
  1490. ScConnectServiceController(
  1491. OUT LPHANDLE PipeHandle
  1492. )
  1493. /*++
  1494. Routine Description:
  1495. This function connects to the Service Controller Pipe.
  1496. Arguments:
  1497. PipeHandle - This is a pointer to the location where the PipeHandle
  1498. is to be placed.
  1499. Return Value:
  1500. NO_ERROR - if the operation was successful.
  1501. ERROR_FAILED_SERVICE_CONTROLLER_CONNECT - if we failed to connect.
  1502. --*/
  1503. {
  1504. BOOL status;
  1505. DWORD apiStatus;
  1506. DWORD response;
  1507. DWORD pipeMode;
  1508. DWORD numBytesWritten;
  1509. WCHAR wszPipeName[sizeof(CONTROL_PIPE_NAME) / sizeof(WCHAR) + PID_LEN] = CONTROL_PIPE_NAME;
  1510. //
  1511. // Generate the pipe name -- Security process uses PID 0 since the
  1512. // SCM doesn't have the PID at connect-time (it gets it from the
  1513. // pipe transaction with the LSA)
  1514. //
  1515. if (g_fIsSecProc) {
  1516. response = 0;
  1517. }
  1518. else {
  1519. //
  1520. // Read this process's pipe ID from the registry.
  1521. //
  1522. HKEY hCurrentValueKey;
  1523. status = RegOpenKeyEx(
  1524. HKEY_LOCAL_MACHINE,
  1525. "System\\CurrentControlSet\\Control\\ServiceCurrent",
  1526. 0,
  1527. KEY_QUERY_VALUE,
  1528. &hCurrentValueKey);
  1529. if (status == ERROR_SUCCESS)
  1530. {
  1531. DWORD ValueType;
  1532. DWORD cbData = sizeof(response);
  1533. status = RegQueryValueEx(
  1534. hCurrentValueKey,
  1535. NULL, // Use key's unnamed value
  1536. 0,
  1537. &ValueType,
  1538. (LPBYTE) &response,
  1539. &cbData);
  1540. RegCloseKey(hCurrentValueKey);
  1541. if (status != ERROR_SUCCESS || ValueType != REG_DWORD)
  1542. {
  1543. SCC_LOG(ERROR,
  1544. "ScConnectServiceController: RegQueryValueEx FAILED %d\n",
  1545. status);
  1546. return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
  1547. }
  1548. }
  1549. else
  1550. {
  1551. SCC_LOG(ERROR,
  1552. "ScConnectServiceController: RegOpenKeyEx FAILED %d\n",
  1553. status);
  1554. return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
  1555. }
  1556. }
  1557. _itow(response, wszPipeName + sizeof(CONTROL_PIPE_NAME) / sizeof(WCHAR) - 1, 10);
  1558. status = WaitNamedPipeW (
  1559. wszPipeName,
  1560. CONTROL_WAIT_PERIOD);
  1561. if (status != TRUE) {
  1562. SCC_LOG(ERROR,"ScConnectServiceController:WaitNamedPipe failed rc = %d\n",
  1563. GetLastError());
  1564. }
  1565. SCC_LOG(TRACE,"ScConnectServiceController:WaitNamedPipe success\n",0);
  1566. *PipeHandle = CreateFileW(
  1567. wszPipeName, // lpFileName
  1568. GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
  1569. FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
  1570. NULL, // lpSecurityAttributes
  1571. OPEN_EXISTING, // dwCreationDisposition
  1572. FILE_ATTRIBUTE_NORMAL, // dwFileAttributes
  1573. 0L); // hTemplateFile
  1574. if (*PipeHandle == INVALID_HANDLE_VALUE) {
  1575. SCC_LOG(ERROR,"ScConnectServiceController:CreateFile failed rc = %d\n",
  1576. GetLastError());
  1577. return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
  1578. }
  1579. SCC_LOG(TRACE,"ScConnectServiceController:CreateFile success\n",0);
  1580. //
  1581. // Set pipe mode
  1582. //
  1583. pipeMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
  1584. status = SetNamedPipeHandleState (
  1585. *PipeHandle,
  1586. &pipeMode,
  1587. NULL,
  1588. NULL);
  1589. if (status != TRUE) {
  1590. SCC_LOG(ERROR,"ScConnectServiceController:SetNamedPipeHandleState failed rc = %d\n",
  1591. GetLastError());
  1592. return(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT);
  1593. }
  1594. else {
  1595. SCC_LOG(TRACE,
  1596. "ScConnectServiceController SetNamedPipeHandleState Success\n",0);
  1597. }
  1598. //
  1599. // Send initial status - This is the process Id for the service process.
  1600. //
  1601. response = GetCurrentProcessId();
  1602. apiStatus = WriteFile (
  1603. *PipeHandle,
  1604. &response,
  1605. sizeof(response),
  1606. &numBytesWritten,
  1607. NULL);
  1608. if (apiStatus != TRUE) {
  1609. //
  1610. // If this fails, there is a chance that the pipe is still in good
  1611. // shape. So we just go on.
  1612. //
  1613. // ??EVENTLOG??
  1614. //
  1615. SCC_LOG(ERROR,"ScConnectServiceController: WriteFile failed, rc= %d\n", GetLastError());
  1616. }
  1617. else {
  1618. SCC_LOG(TRACE,
  1619. "ScConnectServiceController: WriteFile success, bytes Written= %d\n",
  1620. numBytesWritten);
  1621. }
  1622. return(NO_ERROR);
  1623. }
  1624. VOID
  1625. ScExpungeMessage(
  1626. IN HANDLE PipeHandle
  1627. )
  1628. /*++
  1629. Routine Description:
  1630. This routine cleans the remaining portion of a message out of the pipe.
  1631. It is called in response to an unsuccessful attempt to allocate the
  1632. correct buffer size from the heap. In this routine a small buffer is
  1633. allocated on the stack, and successive reads are made until a status
  1634. other than ERROR_MORE_DATA is received.
  1635. Arguments:
  1636. PipeHandle - This is a handle to the pipe in which the message resides.
  1637. Return Value:
  1638. none - If this operation fails, there is not much I can do about
  1639. the data in the pipe.
  1640. --*/
  1641. {
  1642. #define EXPUNGE_BUF_SIZE 100
  1643. DWORD status;
  1644. DWORD dwNumBytesRead = 0;
  1645. BYTE msg[EXPUNGE_BUF_SIZE];
  1646. do {
  1647. status = ReadFile (
  1648. PipeHandle,
  1649. msg,
  1650. EXPUNGE_BUF_SIZE,
  1651. &dwNumBytesRead,
  1652. NULL);
  1653. }
  1654. while( status == ERROR_MORE_DATA);
  1655. }
  1656. DWORD
  1657. ScGetPipeInput (
  1658. IN HANDLE PipeHandle,
  1659. IN OUT LPCTRL_MSG_HEADER Msg,
  1660. IN DWORD dwBufferSize,
  1661. OUT LPSERVICE_PARAMS *ppServiceParams
  1662. )
  1663. /*++
  1664. Routine Description:
  1665. This routine reads a control message from the pipe and places it into
  1666. a message buffer. This routine also allocates a structure for
  1667. the service thread information. This structure will eventually
  1668. contain everything that is needed to invoke the service startup
  1669. routine in the context of a new thread. Items contained in the
  1670. structure are:
  1671. 1) The pointer to the startup routine,
  1672. 2) The number of arguments, and
  1673. 3) The table of vectors to the arguments.
  1674. Since this routine has knowledge about the buffer size needed for
  1675. the arguments, the allocation is done here.
  1676. Arguments:
  1677. PipeHandle - This is the handle for the pipe that is to be read.
  1678. Msg - This is a pointer to a buffer where the data is to be placed.
  1679. dwBufferSize - This is the size (in bytes) of the buffer that data is to
  1680. be placed in.
  1681. ppServiceParams - This is the location where the command args will
  1682. be placed
  1683. Return Value:
  1684. NO_ERROR - if the operation was successful.
  1685. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to create a large
  1686. enough buffer for the command line arguments.
  1687. ERROR_INVALID_DATA - This is returned if we did not receive a complete
  1688. CTRL_MESSAGE_HEADER on the first read.
  1689. Any error that ReadFile might return could be returned by this function.
  1690. (We may want to return something more specific like ERROR_READ_FAULT)
  1691. --*/
  1692. {
  1693. DWORD status;
  1694. BOOL readStatus;
  1695. DWORD dwNumBytesRead = 0;
  1696. DWORD dwRemainingArgBytes;
  1697. LPBYTE pTempArgPtr;
  1698. *ppServiceParams = NULL;
  1699. //
  1700. // Read the header and name string from the pipe.
  1701. // NOTE: The number of bytes for the name string is determined by
  1702. // the longest service name in the service process. If the actual
  1703. // string read is shorter, then the beginning of the command arg
  1704. // data may be read with this read.
  1705. // Also note: The buffer is large enough to accommodate the longest
  1706. // permissible service name.
  1707. //
  1708. readStatus = ReadFile(PipeHandle,
  1709. Msg,
  1710. dwBufferSize,
  1711. &dwNumBytesRead,
  1712. NULL);
  1713. SCC_LOG(TRACE,"ScGetPipeInput:ReadFile buffer size = %ld\n",dwBufferSize);
  1714. SCC_LOG(TRACE,"ScGetPipeInput:ReadFile dwNumBytesRead = %ld\n",dwNumBytesRead);
  1715. if ((readStatus == TRUE) && (dwNumBytesRead > sizeof(CTRL_MSG_HEADER))) {
  1716. //
  1717. // The Read File read the complete message in one read. So we
  1718. // can return with the data.
  1719. //
  1720. SCC_LOG(TRACE,"ScGetPipeInput: Success!\n",0);
  1721. switch (Msg->OpCode) {
  1722. case SERVICE_CONTROL_START_OWN:
  1723. case SERVICE_CONTROL_START_SHARE:
  1724. //
  1725. // Read in any start arguments for the service
  1726. //
  1727. status = ScReadServiceParms(Msg,
  1728. dwNumBytesRead,
  1729. (LPBYTE *)ppServiceParams,
  1730. &pTempArgPtr,
  1731. &dwRemainingArgBytes);
  1732. if (status != NO_ERROR) {
  1733. return status;
  1734. }
  1735. //
  1736. // Change the offsets back into pointers.
  1737. //
  1738. ScNormalizeCmdLineArgs(Msg,
  1739. &(*ppServiceParams)->ThreadStartupParms);
  1740. break;
  1741. case SERVICE_CONTROL_DEVICEEVENT:
  1742. case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
  1743. case SERVICE_CONTROL_POWEREVENT:
  1744. case SERVICE_CONTROL_SESSIONCHANGE:
  1745. //
  1746. // Read in the service's PnP/power arguments
  1747. //
  1748. status = ScReadServiceParms(Msg,
  1749. dwNumBytesRead,
  1750. (LPBYTE *)ppServiceParams,
  1751. &pTempArgPtr,
  1752. &dwRemainingArgBytes);
  1753. if (status != NO_ERROR) {
  1754. return status;
  1755. }
  1756. break;
  1757. default:
  1758. ASSERT(Msg->NumCmdArgs == 0);
  1759. break;
  1760. }
  1761. return NO_ERROR;
  1762. }
  1763. else {
  1764. //
  1765. // An error was returned from ReadFile. ERROR_MORE_DATA
  1766. // means that we need to read some arguments from the buffer.
  1767. // Any other error is unexpected, and generates an internal error.
  1768. //
  1769. if (readStatus != TRUE) {
  1770. status = GetLastError();
  1771. if (status != ERROR_MORE_DATA) {
  1772. SCC_LOG(ERROR,"ScGetPipeInput:Unexpected return code, rc= %ld\n",
  1773. status);
  1774. return status;
  1775. }
  1776. }
  1777. else {
  1778. //
  1779. // The read was successful, but we didn't get a complete
  1780. // CTRL_MESSAGE_HEADER.
  1781. //
  1782. return ERROR_INVALID_DATA;
  1783. }
  1784. }
  1785. //
  1786. // We must have received an ERROR_MORE_DATA to go down this
  1787. // path. This means that the message contains more data. Namely,
  1788. // service arguments must be present. Therefore, the pipe must
  1789. // be read again. Since the header indicates how many bytes are
  1790. // needed, we will allocate a buffer large enough to hold all the
  1791. // service arguments.
  1792. //
  1793. // If a portion of the arguments was read in the first read,
  1794. // they will be put in this new buffer. That is so that all the
  1795. // command line arg info is in one place.
  1796. //
  1797. status = ScReadServiceParms(Msg,
  1798. dwNumBytesRead,
  1799. (LPBYTE *)ppServiceParams,
  1800. &pTempArgPtr,
  1801. &dwRemainingArgBytes);
  1802. if (status != NO_ERROR) {
  1803. ScExpungeMessage(PipeHandle);
  1804. return status;
  1805. }
  1806. readStatus = ReadFile(PipeHandle,
  1807. pTempArgPtr,
  1808. dwRemainingArgBytes,
  1809. &dwNumBytesRead,
  1810. NULL);
  1811. if ((readStatus != TRUE) || (dwNumBytesRead < dwRemainingArgBytes)) {
  1812. if (readStatus != TRUE) {
  1813. status = GetLastError();
  1814. SCC_LOG1(ERROR,
  1815. "ScGetPipeInput: ReadFile error (2nd read), rc = %ld\n",
  1816. status);
  1817. }
  1818. else {
  1819. status = ERROR_BAD_LENGTH;
  1820. }
  1821. SCC_LOG2(ERROR,
  1822. "ScGetPipeInput: ReadFile read: %d, expected: %d\n",
  1823. dwNumBytesRead,
  1824. dwRemainingArgBytes);
  1825. LocalFree(*ppServiceParams);
  1826. return status;
  1827. }
  1828. if (Msg->OpCode == SERVICE_CONTROL_START_OWN ||
  1829. Msg->OpCode == SERVICE_CONTROL_START_SHARE) {
  1830. //
  1831. // Change the offsets back into pointers.
  1832. //
  1833. ScNormalizeCmdLineArgs(Msg, &(*ppServiceParams)->ThreadStartupParms);
  1834. }
  1835. return NO_ERROR;
  1836. }
  1837. DWORD
  1838. ScGetDispatchEntry (
  1839. IN OUT LPINTERNAL_DISPATCH_ENTRY *DispatchEntryPtr,
  1840. IN LPWSTR ServiceName
  1841. )
  1842. /*++
  1843. Routine Description:
  1844. Finds an entry in the Dispatch Table for a particular service which
  1845. is identified by a service name string.
  1846. Arguments:
  1847. DispatchEntryPtr - As an input, the is a location where a pointer to
  1848. the top of the DispatchTable is placed. On return, this is the
  1849. location where the pointer to the specific dispatch entry is to
  1850. be placed. This is an opaque pointer because it could be either
  1851. ansi or unicode depending on the operational state of the dispatcher.
  1852. ServiceName - This is a pointer to the service name string that was
  1853. supplied by the service. Note that it may not be the service's
  1854. real name since we never check services that run in their own
  1855. process (bug that can never be fixed since it will break existing
  1856. services). We must check for this name instead of the real
  1857. one.
  1858. Return Value:
  1859. NO_ERROR - The operation was successful.
  1860. ERROR_SERVICE_NOT_IN_EXE - The serviceName could not be found in
  1861. the dispatch table. This indicates that the configuration database
  1862. says the serice is in this process, but the service name doesn't
  1863. exist in the dispatch table.
  1864. --*/
  1865. {
  1866. LPINTERNAL_DISPATCH_ENTRY entryPtr;
  1867. DWORD found = FALSE;
  1868. entryPtr = *DispatchEntryPtr;
  1869. if (entryPtr->dwFlags & SERVICE_OWN_PROCESS) {
  1870. return (NO_ERROR);
  1871. }
  1872. while (entryPtr->ServiceName != NULL) {
  1873. if (_wcsicmp(entryPtr->ServiceName, ServiceName) == 0) {
  1874. found = TRUE;
  1875. break;
  1876. }
  1877. entryPtr++;
  1878. }
  1879. if (found) {
  1880. *DispatchEntryPtr = entryPtr;
  1881. }
  1882. else {
  1883. SCC_LOG(ERROR,"ScGetDispatchEntry: DispatchEntry not found\n"
  1884. " Configuration error - the %ws service is not in this .exe file!\n"
  1885. " Check the table passed to StartServiceCtrlDispatcher.\n", ServiceName);
  1886. return(ERROR_SERVICE_NOT_IN_EXE);
  1887. }
  1888. return(NO_ERROR);
  1889. }
  1890. VOID
  1891. ScNormalizeCmdLineArgs(
  1892. IN OUT LPCTRL_MSG_HEADER Msg,
  1893. IN OUT LPTHREAD_STARTUP_PARMSW ThreadStartupParms
  1894. )
  1895. /*++
  1896. Routine Description:
  1897. Normalizes the command line argument information that came across in
  1898. the pipe. The argument information is stored in a buffer that consists
  1899. of an array of string pointers followed by the strings themselves.
  1900. However, in the pipe, the pointers are replaced with offsets. This
  1901. routine transforms the offsets into real pointers.
  1902. This routine also puts the service name into the array of argument
  1903. vectors, and adds the service name string to the end of the
  1904. buffer (space has already been allocated for it).
  1905. Arguments:
  1906. Msg - This is a pointer to the Message. Useful information from this
  1907. includes the NumCmdArgs and the service name.
  1908. ThreadStartupParms - A pointer to the thread startup parameter structure.
  1909. Return Value:
  1910. none.
  1911. --*/
  1912. {
  1913. DWORD i;
  1914. LPWSTR *argv;
  1915. DWORD numCmdArgs;
  1916. LPWSTR *serviceNameVector;
  1917. LPWSTR serviceNamePtr;
  1918. #if defined(_X86_)
  1919. PULONG64 argv64 = NULL;
  1920. #endif
  1921. numCmdArgs = Msg->NumCmdArgs;
  1922. argv = &(ThreadStartupParms->VectorTable);
  1923. //
  1924. // Save the first argv for the service name.
  1925. //
  1926. serviceNameVector = argv;
  1927. argv++;
  1928. //
  1929. // Normalize the Command Line Argument information by replacing
  1930. // offsets in buffer with pointers.
  1931. //
  1932. // NOTE: The elaborate casting that takes place here is because we
  1933. // are taking some (pointer sized) offsets, and turning them back
  1934. // into pointers to strings. The offsets are in bytes, and are
  1935. // relative to the beginning of the vector table which contains
  1936. // pointers to the various command line arg strings.
  1937. //
  1938. #if defined(_X86_)
  1939. if (g_fWow64Process) {
  1940. //
  1941. // Pointers on the 64-bit land are 64-bit so make argv
  1942. // point to the 1st arg after the service name offset
  1943. //
  1944. argv64 = (PULONG64)argv;
  1945. }
  1946. #endif
  1947. for (i = 0; i < numCmdArgs; i++) {
  1948. #if defined(_X86_)
  1949. if (g_fWow64Process)
  1950. argv[i] = (LPWSTR)((LPBYTE)argv + PtrToUlong(argv64[i]));
  1951. else
  1952. #endif
  1953. argv[i] = (LPWSTR)((LPBYTE)argv + PtrToUlong(argv[i]));
  1954. }
  1955. //
  1956. // If we are starting a service, then we need to add the service name
  1957. // to the argument vectors.
  1958. //
  1959. if ((Msg->OpCode == SERVICE_CONTROL_START_SHARE) ||
  1960. (Msg->OpCode == SERVICE_CONTROL_START_OWN)) {
  1961. numCmdArgs++;
  1962. if (numCmdArgs > 1) {
  1963. //
  1964. // Find the location for the service name string by finding
  1965. // the pointer to the last argument adding its string length
  1966. // to it.
  1967. //
  1968. serviceNamePtr = argv[i-1];
  1969. serviceNamePtr += (wcslen(serviceNamePtr) + 1);
  1970. }
  1971. else {
  1972. serviceNamePtr = (LPWSTR)argv;
  1973. }
  1974. wcscpy(serviceNamePtr, (LPWSTR) ((LPBYTE)Msg + Msg->ServiceNameOffset));
  1975. *serviceNameVector = serviceNamePtr;
  1976. }
  1977. ThreadStartupParms->NumArgs = numCmdArgs;
  1978. }
  1979. VOID
  1980. ScSendResponse (
  1981. IN HANDLE PipeHandle,
  1982. IN DWORD Response,
  1983. IN DWORD dwHandlerRetVal
  1984. )
  1985. /*++
  1986. Routine Description:
  1987. This routine sends a status response to the Service Controller's pipe.
  1988. Arguments:
  1989. Response - This is the status message that is to be sent.
  1990. dwHandlerRetVal - This is the return value from the service's control
  1991. handler function (NO_ERROR for non-Ex handlers)
  1992. Return Value:
  1993. none.
  1994. --*/
  1995. {
  1996. DWORD numBytesWritten;
  1997. PIPE_RESPONSE_MSG prmResponse;
  1998. prmResponse.dwDispatcherStatus = Response;
  1999. prmResponse.dwHandlerRetVal = dwHandlerRetVal;
  2000. if (!WriteFile(PipeHandle,
  2001. &prmResponse,
  2002. sizeof(PIPE_RESPONSE_MSG),
  2003. &numBytesWritten,
  2004. NULL))
  2005. {
  2006. SCC_LOG1(ERROR,
  2007. "ScSendResponse: WriteFile failed, rc= %d\n",
  2008. GetLastError());
  2009. }
  2010. }
  2011. DWORD
  2012. ScSvcctrlThreadW(
  2013. IN LPTHREAD_STARTUP_PARMSW lpThreadStartupParms
  2014. )
  2015. /*++
  2016. Routine Description:
  2017. This is the thread for the newly started service. This code
  2018. calls the service's main thread with parameters from the
  2019. ThreadStartupParms structure.
  2020. NOTE: The first item in the argument vector table is the pointer to
  2021. the service registry path string.
  2022. Arguments:
  2023. lpThreadStartupParms - This is a pointer to the ThreadStartupParms
  2024. structure. (This is a unicode structure);
  2025. Return Value:
  2026. --*/
  2027. {
  2028. //
  2029. // Call the Service's Main Routine.
  2030. //
  2031. ((LPSERVICE_MAIN_FUNCTIONW)lpThreadStartupParms->ServiceStartRoutine) (
  2032. lpThreadStartupParms->NumArgs,
  2033. &lpThreadStartupParms->VectorTable);
  2034. LocalFree(lpThreadStartupParms);
  2035. return(0);
  2036. }
  2037. DWORD
  2038. ScSvcctrlThreadA(
  2039. IN LPTHREAD_STARTUP_PARMSA lpThreadStartupParms
  2040. )
  2041. /*++
  2042. Routine Description:
  2043. This is the thread for the newly started service. This code
  2044. calls the service's main thread with parameters from the
  2045. ThreadStartupParms structure.
  2046. NOTE: The first item in the argument vector table is the pointer to
  2047. the service registry path string.
  2048. Arguments:
  2049. lpThreadStartupParms - This is a pointer to the ThreadStartupParms
  2050. structure. (This is a unicode structure);
  2051. Return Value:
  2052. --*/
  2053. {
  2054. //
  2055. // Call the Service's Main Routine.
  2056. //
  2057. // NOTE: The first item in the argument vector table is the pointer to
  2058. // the service registry path string.
  2059. //
  2060. ((LPSERVICE_MAIN_FUNCTIONA)lpThreadStartupParms->ServiceStartRoutine) (
  2061. lpThreadStartupParms->NumArgs,
  2062. &lpThreadStartupParms->VectorTable);
  2063. LocalFree(lpThreadStartupParms);
  2064. return(0);
  2065. }