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.

1172 lines
28 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000.
  5. //
  6. // File: svchost.c
  7. //
  8. // Contents: Generic Host Process for Win32 Services
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 3-30-98 RichardW Created
  15. // 3-31-98 ShaunCo Took ownership.
  16. // Finished off basic implementation.
  17. // 1-24-00 JSchwart Took ownership.
  18. // Adapted to run NT intrinsic services.
  19. //----------------------------------------------------------------------------
  20. #include "pch.h"
  21. #pragma hdrstop
  22. #include "globals.h"
  23. #include "registry.h"
  24. #include "security.h"
  25. //
  26. // Generic Service Process:
  27. //
  28. // This process will grovel the service portion of the registry, looking
  29. // for instances of itself (details below), and constructing a list of services
  30. // to submit to the service controller. As an individual service is started,
  31. // the DLL is loaded and the entry point called. Services in these DLLs are
  32. // expected to play nicely with others, that is, use the common thread pool,
  33. // not stomp memory, etc.
  34. //
  35. //
  36. // Loading.
  37. //
  38. // Each service that will be resident in this process must have svchost.exe as
  39. // the ImagePath, with the same parameters. Additionally, the service must
  40. // have under its Parameters key, these values:
  41. //
  42. // ServiceDll = REG_EXPAND_SZ <path to DLL>
  43. // ServiceMain = REG_SZ <pszFunctionName> OPTIONAL
  44. //
  45. // If ServiceMain is not present, then it defaults to "ServiceMain".
  46. //
  47. //
  48. // Multiple Service Groups
  49. //
  50. // Multiple service groups can be accomplished by supplying parameters to the
  51. // svchost.exe on the ImagePath.
  52. //
  53. // svchost.exe -k "Key"
  54. //
  55. // will grovel the services and only load those with matching ImagePath.
  56. //
  57. #define REGSTR_PATH_SVCHOST TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost")
  58. typedef struct _COMMAND_OPTIONS
  59. {
  60. PTSTR CommandLineBuffer;
  61. PTSTR ImageName;
  62. BOOL fServiceGroup;
  63. PTSTR ServiceGroupName;
  64. //
  65. // dwCoInitializeSecurityParam is a DWORD read from the registry for the
  66. // service group we were instantiated for. If non-zero, we will
  67. // call CoInitializeSecurity in a way based on the value.
  68. //
  69. DWORD dwCoInitializeSecurityParam;
  70. DWORD dwAuthLevel;
  71. DWORD dwImpersonationLevel;
  72. DWORD dwAuthCapabilities;
  73. //
  74. // Default stack size for RPC threads (to prevent stack overflow)
  75. //
  76. DWORD dwDefaultRpcStackSize;
  77. }
  78. COMMAND_OPTIONS, * PCOMMAND_OPTIONS;
  79. typedef struct _SERVICE_DLL
  80. {
  81. LIST_ENTRY List;
  82. HMODULE hmod;
  83. PTSTR pszDllPath;
  84. } SERVICE_DLL, * PSERVICE_DLL;
  85. typedef struct _SERVICE
  86. {
  87. PTSTR pszName;
  88. PSERVICE_DLL pDll;
  89. PSTR pszEntryPoint;
  90. } SERVICE, * PSERVICE;
  91. //+---------------------------------------------------------------------------
  92. //
  93. // Global variables.
  94. //
  95. // ListLock protects access to the Dll list and Service array.
  96. //
  97. CRITICAL_SECTION ListLock;
  98. // DllList is a list of SERVICE_DLL structures representing the DLL's
  99. // which host entry points for the services hosted by this process.
  100. //
  101. LIST_ENTRY DllList;
  102. // ServiceArray is an array of SERVICE structures representing the services
  103. // hosted by this process.
  104. //
  105. PSERVICE ServiceArray;
  106. // ServiceCount is the count of SERVICE entries in ServiceList.
  107. //
  108. UINT ServiceCount;
  109. // ServiceNames is the multi-sz read from the registry for the
  110. // service group we were instantiated for.
  111. //
  112. PTSTR ServiceNames;
  113. //+---------------------------------------------------------------------------
  114. //
  115. // Local function prototypes
  116. //
  117. VOID
  118. SvchostCharLowerW(
  119. LPWSTR pszString
  120. );
  121. //+---------------------------------------------------------------------------
  122. //
  123. VOID
  124. DummySvchostCtrlHandler(
  125. DWORD Opcode
  126. )
  127. {
  128. return;
  129. }
  130. VOID
  131. AbortSvchostService( // used if cant find Service DLL or entrypoint
  132. LPWSTR ServiceName,
  133. DWORD Error
  134. )
  135. {
  136. SERVICE_STATUS_HANDLE GenericServiceStatusHandle;
  137. SERVICE_STATUS GenericServiceStatus;
  138. GenericServiceStatus.dwServiceType = SERVICE_WIN32;
  139. GenericServiceStatus.dwCurrentState = SERVICE_STOPPED;
  140. GenericServiceStatus.dwControlsAccepted = SERVICE_CONTROL_STOP;
  141. GenericServiceStatus.dwCheckPoint = 0;
  142. GenericServiceStatus.dwWaitHint = 0;
  143. GenericServiceStatus.dwWin32ExitCode = Error;
  144. GenericServiceStatus.dwServiceSpecificExitCode = 0;
  145. GenericServiceStatusHandle = RegisterServiceCtrlHandler(ServiceName,
  146. DummySvchostCtrlHandler);
  147. if (GenericServiceStatusHandle == NULL)
  148. {
  149. SVCHOST_LOG1(ERROR,
  150. "AbortSvchostService: RegisterServiceCtrlHandler failed %d\n",
  151. GetLastError());
  152. }
  153. else if (!SetServiceStatus (GenericServiceStatusHandle,
  154. &GenericServiceStatus))
  155. {
  156. SVCHOST_LOG1(ERROR,
  157. "AbortSvchostService: SetServiceStatus error %ld\n",
  158. GetLastError());
  159. }
  160. return;
  161. }
  162. FARPROC
  163. GetServiceDllFunction (
  164. PSERVICE_DLL pDll,
  165. PCSTR pszFunctionName,
  166. LPDWORD lpdwError OPTIONAL
  167. )
  168. {
  169. FARPROC pfn;
  170. HMODULE hmod;
  171. // Load the module if neccessary.
  172. //
  173. hmod = pDll->hmod;
  174. if (!hmod)
  175. {
  176. hmod = LoadLibraryEx (
  177. pDll->pszDllPath,
  178. NULL,
  179. LOAD_WITH_ALTERED_SEARCH_PATH);
  180. if (hmod)
  181. {
  182. pDll->hmod = hmod;
  183. }
  184. else
  185. {
  186. if (lpdwError)
  187. {
  188. *lpdwError = GetLastError();
  189. }
  190. SVCHOST_LOG2(ERROR,
  191. "LoadLibrary (%ws) failed. Error %d.\n",
  192. pDll->pszDllPath,
  193. GetLastError());
  194. return NULL;
  195. }
  196. }
  197. ASSERT (hmod);
  198. pfn = GetProcAddress(hmod, pszFunctionName);
  199. if (!pfn)
  200. {
  201. if (lpdwError)
  202. {
  203. *lpdwError = GetLastError();
  204. }
  205. SVCHOST_LOG3(TRACE,
  206. "GetProcAddress (%s) failed on DLL %ws. Error = %d.\n",
  207. pszFunctionName,
  208. pDll->pszDllPath,
  209. GetLastError());
  210. }
  211. return pfn;
  212. }
  213. PSERVICE_DLL
  214. FindDll(
  215. IN LPCTSTR pszDllPath
  216. )
  217. {
  218. PLIST_ENTRY pNode;
  219. PSERVICE_DLL pDll = NULL;
  220. ASSERT (pszDllPath);
  221. EnterCriticalSection (&ListLock);
  222. pNode = DllList.Flink;
  223. while (pNode != &DllList)
  224. {
  225. pDll = CONTAINING_RECORD (pNode, SERVICE_DLL, List);
  226. if (0 == lstrcmp (pDll->pszDllPath, pszDllPath))
  227. {
  228. break;
  229. }
  230. pDll = NULL;
  231. pNode = pNode->Flink;
  232. }
  233. LeaveCriticalSection (&ListLock);
  234. return pDll;
  235. }
  236. PSERVICE_DLL
  237. AddDll(
  238. IN LPCTSTR pszDllPath,
  239. OUT LPDWORD lpdwError
  240. )
  241. {
  242. PSERVICE_DLL pDll;
  243. ASSERT (pszDllPath);
  244. ASSERT (*pszDllPath);
  245. pDll = MemAlloc (HEAP_ZERO_MEMORY,
  246. sizeof (SERVICE_DLL) +
  247. (lstrlen (pszDllPath) + 1) * sizeof (TCHAR));
  248. if (pDll)
  249. {
  250. // Set the structure members.
  251. //
  252. pDll->pszDllPath = (PTSTR) (pDll + 1);
  253. lstrcpy (pDll->pszDllPath, pszDllPath);
  254. // Add the entry to the list.
  255. //
  256. EnterCriticalSection (&ListLock);
  257. InsertTailList (&DllList, &pDll->List);
  258. LeaveCriticalSection (&ListLock);
  259. }
  260. else
  261. {
  262. *lpdwError = GetLastError();
  263. }
  264. return pDll;
  265. }
  266. LONG
  267. OpenServiceParametersKey (
  268. LPCTSTR pszServiceName,
  269. HKEY* phkey
  270. )
  271. {
  272. LONG lr;
  273. HKEY hkeyServices;
  274. ASSERT (phkey);
  275. // Open the Services key.
  276. //
  277. lr = RegOpenKeyEx (
  278. HKEY_LOCAL_MACHINE,
  279. REGSTR_PATH_SERVICES,
  280. 0,
  281. KEY_READ,
  282. &hkeyServices);
  283. if (!lr)
  284. {
  285. HKEY hkeySvc;
  286. // Open the service key.
  287. //
  288. lr = RegOpenKeyEx (
  289. hkeyServices,
  290. pszServiceName,
  291. 0,
  292. KEY_READ,
  293. &hkeySvc);
  294. if (!lr)
  295. {
  296. // Open the Parameters key.
  297. //
  298. lr = RegOpenKeyEx (
  299. hkeySvc,
  300. TEXT("Parameters"),
  301. 0,
  302. KEY_READ,
  303. phkey);
  304. RegCloseKey (hkeySvc);
  305. }
  306. RegCloseKey (hkeyServices);
  307. }
  308. return lr;
  309. }
  310. #if DBG
  311. BOOL
  312. FDebugBreakForService (
  313. LPCWSTR pszwService
  314. )
  315. {
  316. BOOL fAttach = FALSE;
  317. LONG lr;
  318. HKEY hkeySvchost;
  319. // Open the Svchost key.
  320. //
  321. lr = RegOpenKeyEx (
  322. HKEY_LOCAL_MACHINE,
  323. REGSTR_PATH_SVCHOST,
  324. 0,
  325. KEY_READ,
  326. &hkeySvchost);
  327. if (!lr)
  328. {
  329. HKEY hkeyServiceOptions;
  330. // Look for the key with the same name as the service.
  331. //
  332. lr = RegOpenKeyExW (
  333. hkeySvchost,
  334. pszwService,
  335. 0,
  336. KEY_READ,
  337. &hkeyServiceOptions);
  338. if (!lr)
  339. {
  340. DWORD dwValue;
  341. lr = RegQueryDword (
  342. hkeyServiceOptions,
  343. TEXT("DebugBreak"),
  344. &dwValue);
  345. if (!lr)
  346. {
  347. fAttach = !!dwValue;
  348. }
  349. RegCloseKey (hkeyServiceOptions);
  350. }
  351. RegCloseKey (hkeySvchost);
  352. }
  353. return fAttach;
  354. }
  355. #endif
  356. VOID
  357. GetServiceMainFunctions (
  358. PSERVICE pService,
  359. LPSERVICE_MAIN_FUNCTION *ppfnServiceMain,
  360. LPSVCHOST_PUSH_GLOBAL_FUNCTION *ppfnPushGlobals,
  361. LPDWORD lpdwError
  362. )
  363. {
  364. LPCSTR pszEntryPoint;
  365. *lpdwError = NO_ERROR;
  366. // Get the dll and entrypoint for this service if we don't have it yet.
  367. //
  368. if (!pService->pDll)
  369. {
  370. LONG lr;
  371. HKEY hkeyParams;
  372. lr = OpenServiceParametersKey (pService->pszName, &hkeyParams);
  373. if (!lr)
  374. {
  375. DWORD dwType;
  376. DWORD dwSize;
  377. WCHAR pszDllName [MAX_PATH + 1];
  378. WCHAR pszExpandedDllName [MAX_PATH + 1];
  379. // Look for the service dll path and expand it.
  380. //
  381. dwSize = sizeof(pszDllName);
  382. lr = RegQueryValueEx (
  383. hkeyParams,
  384. TEXT("ServiceDll"),
  385. NULL,
  386. &dwType,
  387. (LPBYTE)pszDllName,
  388. &dwSize);
  389. if (!lr && (REG_EXPAND_SZ == dwType) && *pszDllName)
  390. {
  391. PSERVICE_DLL pDll;
  392. // Expand the dll name and lower case it for comparison
  393. // when we try to find an existing dll record.
  394. //
  395. ExpandEnvironmentStrings (
  396. pszDllName,
  397. pszExpandedDllName,
  398. MAX_PATH);
  399. SvchostCharLowerW (pszExpandedDllName);
  400. // Try to find an existing dll record that we might have and
  401. // if we don't, add this as a new record.
  402. //
  403. pDll = FindDll (pszExpandedDllName);
  404. if (!pDll)
  405. {
  406. pDll = AddDll (pszExpandedDllName, lpdwError);
  407. }
  408. // Remember this dll for this service for next time.
  409. //
  410. ASSERT (!pService->pDll);
  411. pService->pDll = pDll;
  412. // Look for an explicit entrypoint name for this service.
  413. // (Optional)
  414. //
  415. RegQueryStringA (
  416. hkeyParams,
  417. TEXT("ServiceMain"),
  418. REG_SZ,
  419. &pService->pszEntryPoint);
  420. }
  421. else if (NOERROR != lr)
  422. {
  423. *lpdwError = lr;
  424. SVCHOST_LOG2(ERROR,
  425. "RegQueryValueEx for the ServiceDll parameter of the "
  426. "%ws service returned %u\n",
  427. pService->pszName,
  428. lr);
  429. }
  430. else if (REG_EXPAND_SZ != dwType)
  431. {
  432. *lpdwError = ERROR_FILE_NOT_FOUND;
  433. SVCHOST_LOG1(ERROR,
  434. "The ServiceDll parameter for the %ws service is not "
  435. "of type REG_EXPAND_SZ\n",
  436. pService->pszName);
  437. }
  438. else
  439. {
  440. *lpdwError = ERROR_FILE_NOT_FOUND;
  441. }
  442. RegCloseKey (hkeyParams);
  443. }
  444. else
  445. {
  446. *lpdwError = lr;
  447. }
  448. // If we don't have the service dll record by now, we're through.
  449. //
  450. if (!pService->pDll)
  451. {
  452. ASSERT(*lpdwError != NO_ERROR);
  453. return;
  454. }
  455. }
  456. // We should have it the dll by now, so proceed to load the entry point.
  457. //
  458. ASSERT (pService->pDll);
  459. // Default the entry point if we don't have one specified.
  460. //
  461. if (pService->pszEntryPoint)
  462. {
  463. pszEntryPoint = pService->pszEntryPoint;
  464. }
  465. else
  466. {
  467. pszEntryPoint = "ServiceMain";
  468. }
  469. // Get the address for the service's ServiceMain
  470. //
  471. *ppfnServiceMain = (LPSERVICE_MAIN_FUNCTION) GetServiceDllFunction(
  472. pService->pDll,
  473. pszEntryPoint,
  474. lpdwError);
  475. // Get the address for the "push the globals" function (optional)
  476. //
  477. *ppfnPushGlobals = (LPSVCHOST_PUSH_GLOBAL_FUNCTION) GetServiceDllFunction(
  478. pService->pDll,
  479. "SvchostPushServiceGlobals",
  480. NULL);
  481. }
  482. LONG
  483. ReadPerInstanceRegistryParameters(
  484. IN HKEY hkeySvchost,
  485. IN OUT PCOMMAND_OPTIONS pOptions
  486. )
  487. {
  488. HKEY hkeySvchostGroup;
  489. LONG lr;
  490. // Read the value corresponding to this service group.
  491. //
  492. ASSERT (pOptions->ServiceGroupName);
  493. lr = RegQueryString (
  494. hkeySvchost,
  495. pOptions->ServiceGroupName,
  496. REG_MULTI_SZ,
  497. &ServiceNames);
  498. if (!lr && (!ServiceNames || !*ServiceNames))
  499. {
  500. lr = ERROR_INVALID_DATA;
  501. }
  502. // Read any per-instance parameters from the service group subkey
  503. // if it exists.
  504. //
  505. if (!RegOpenKeyEx (
  506. hkeySvchost,
  507. pOptions->ServiceGroupName,
  508. 0, KEY_READ,
  509. &hkeySvchostGroup))
  510. {
  511. DWORD dwValue;
  512. if (!RegQueryDword (
  513. hkeySvchostGroup,
  514. TEXT("CoInitializeSecurityParam"),
  515. &dwValue))
  516. {
  517. pOptions->dwCoInitializeSecurityParam = dwValue;
  518. }
  519. if (pOptions->dwCoInitializeSecurityParam)
  520. {
  521. if (!RegQueryDword (
  522. hkeySvchostGroup,
  523. TEXT("AuthenticationLevel"),
  524. &dwValue))
  525. {
  526. pOptions->dwAuthLevel = dwValue;
  527. }
  528. else
  529. {
  530. pOptions->dwAuthLevel = RPC_C_AUTHN_LEVEL_PKT;
  531. }
  532. if (!RegQueryDword (
  533. hkeySvchostGroup,
  534. TEXT("ImpersonationLevel"),
  535. &dwValue))
  536. {
  537. pOptions->dwImpersonationLevel = dwValue;
  538. }
  539. else
  540. {
  541. pOptions->dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
  542. }
  543. if (!RegQueryDword (
  544. hkeySvchostGroup,
  545. TEXT("AuthenticationCapabilities"),
  546. &dwValue))
  547. {
  548. pOptions->dwAuthCapabilities = dwValue;
  549. }
  550. else
  551. {
  552. pOptions->dwAuthCapabilities = EOAC_NO_CUSTOM_MARSHAL |
  553. EOAC_DISABLE_AAA;
  554. }
  555. }
  556. if (!RegQueryDword (
  557. hkeySvchostGroup,
  558. TEXT("DefaultRpcStackSize"),
  559. &dwValue))
  560. {
  561. pOptions->dwDefaultRpcStackSize = dwValue;
  562. }
  563. RegCloseKey (hkeySvchostGroup);
  564. }
  565. return lr;
  566. }
  567. VOID
  568. CallPerInstanceInitFunctions(
  569. IN OUT PCOMMAND_OPTIONS pOptions
  570. )
  571. {
  572. if (pOptions->dwCoInitializeSecurityParam)
  573. {
  574. InitializeSecurity (
  575. pOptions->dwCoInitializeSecurityParam,
  576. pOptions->dwAuthLevel,
  577. pOptions->dwImpersonationLevel,
  578. pOptions->dwAuthCapabilities);
  579. }
  580. if (pOptions->dwDefaultRpcStackSize)
  581. {
  582. RpcMgmtSetServerStackSize(pOptions->dwDefaultRpcStackSize * 1024);
  583. }
  584. else
  585. {
  586. //
  587. // Make sure the default RPC stack size will be at least as
  588. // large as the default thread stack size for the process so
  589. // a random service calling RpcMgmtSetServerStackSize can't
  590. // set it to a value that's too low, causing overflows.
  591. //
  592. PIMAGE_NT_HEADERS NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
  593. if (NtHeaders != NULL)
  594. {
  595. RpcMgmtSetServerStackSize((ULONG) NtHeaders->OptionalHeader.SizeOfStackCommit);
  596. }
  597. }
  598. }
  599. VOID
  600. BuildServiceArray (
  601. IN OUT PCOMMAND_OPTIONS pOptions
  602. )
  603. {
  604. LONG lr;
  605. HKEY hkeySvchost;
  606. // Open the Svchost key.
  607. //
  608. lr = RegOpenKeyEx (
  609. HKEY_LOCAL_MACHINE,
  610. REGSTR_PATH_SVCHOST,
  611. 0, KEY_READ,
  612. &hkeySvchost);
  613. if (!lr)
  614. {
  615. lr = ReadPerInstanceRegistryParameters(hkeySvchost, pOptions);
  616. RegCloseKey (hkeySvchost);
  617. }
  618. if (!lr)
  619. {
  620. PTSTR pszServiceName;
  621. EnterCriticalSection (&ListLock);
  622. // Count the number of service names read.
  623. //
  624. ServiceCount = 0;
  625. for (pszServiceName = ServiceNames;
  626. *pszServiceName;
  627. pszServiceName += lstrlen(pszServiceName) + 1)
  628. {
  629. ServiceCount++;
  630. }
  631. ASSERT (ServiceCount);
  632. // Allocate memory for the service array.
  633. //
  634. ServiceArray = MemAlloc (HEAP_ZERO_MEMORY,
  635. sizeof (SERVICE) * ServiceCount);
  636. if (ServiceArray)
  637. {
  638. PSERVICE pService;
  639. // Initialize the service array.
  640. //
  641. pService = ServiceArray;
  642. for (pszServiceName = ServiceNames;
  643. *pszServiceName;
  644. pszServiceName += lstrlen(pszServiceName) + 1)
  645. {
  646. pService->pszName = pszServiceName;
  647. pService++;
  648. }
  649. ASSERT (pService == ServiceArray + ServiceCount);
  650. }
  651. LeaveCriticalSection (&ListLock);
  652. }
  653. }
  654. // type of LPSERVICE_MAIN_FUNCTIONW
  655. //
  656. VOID
  657. WINAPI
  658. ServiceStarter(
  659. DWORD argc,
  660. PWSTR argv[]
  661. )
  662. {
  663. LPSERVICE_MAIN_FUNCTION pfnServiceMain = NULL;
  664. LPSVCHOST_PUSH_GLOBAL_FUNCTION pfnPushGlobals = NULL;
  665. LPCWSTR pszwService = argv[0];
  666. LPWSTR pszwAbort = NULL;
  667. DWORD dwError = ERROR_FILE_NOT_FOUND;
  668. EnterCriticalSection (&ListLock);
  669. {
  670. UINT i;
  671. for (i = 0; i < ServiceCount; i++)
  672. {
  673. if (0 == lstrcmpi (pszwService, ServiceArray[i].pszName))
  674. {
  675. #if DBG
  676. if (FDebugBreakForService (pszwService))
  677. {
  678. SVCHOST_LOG1(TRACE,
  679. "Attaching debugger before getting ServiceMain for %ws...",
  680. pszwService);
  681. DebugBreak ();
  682. }
  683. #endif
  684. GetServiceMainFunctions(&ServiceArray[i],
  685. &pfnServiceMain,
  686. &pfnPushGlobals,
  687. &dwError);
  688. if (pfnServiceMain && pfnPushGlobals && !g_pSvchostSharedGlobals)
  689. {
  690. SvchostBuildSharedGlobals();
  691. }
  692. pszwAbort = argv[0];
  693. break;
  694. }
  695. }
  696. }
  697. LeaveCriticalSection (&ListLock);
  698. if (pfnPushGlobals && g_pSvchostSharedGlobals)
  699. {
  700. pfnPushGlobals (g_pSvchostSharedGlobals);
  701. if (pfnServiceMain)
  702. {
  703. SVCHOST_LOG1(TRACE,
  704. "Calling ServiceMain for %ws...\n",
  705. pszwService);
  706. pfnServiceMain (argc, argv);
  707. }
  708. else if (pszwAbort)
  709. {
  710. AbortSvchostService(pszwAbort,
  711. dwError);
  712. }
  713. }
  714. else if (pfnServiceMain && !pfnPushGlobals)
  715. {
  716. SVCHOST_LOG1(TRACE,
  717. "Calling ServiceMain for %ws...\n",
  718. pszwService);
  719. pfnServiceMain (argc, argv);
  720. }
  721. else if (pszwAbort)
  722. {
  723. AbortSvchostService(pszwAbort,
  724. dwError);
  725. }
  726. }
  727. LPSERVICE_TABLE_ENTRY
  728. BuildServiceTable(
  729. VOID
  730. )
  731. {
  732. LPSERVICE_TABLE_ENTRY pServiceTable;
  733. EnterCriticalSection (&ListLock);
  734. // Allocate one extra entry and zero the entire range. The extra entry
  735. // is the table terminator required by StartServiceCtrlDispatcher.
  736. //
  737. pServiceTable = MemAlloc (HEAP_ZERO_MEMORY,
  738. sizeof (SERVICE_TABLE_ENTRY) * (ServiceCount + 1));
  739. if (pServiceTable)
  740. {
  741. UINT i;
  742. for (i = 0; i < ServiceCount; i++)
  743. {
  744. pServiceTable[i].lpServiceName = ServiceArray[i].pszName;
  745. pServiceTable[i].lpServiceProc = ServiceStarter;
  746. SVCHOST_LOG1(TRACE,
  747. "Added service table entry for %ws\n",
  748. pServiceTable[i].lpServiceName);
  749. }
  750. }
  751. LeaveCriticalSection (&ListLock);
  752. return pServiceTable;
  753. }
  754. PCOMMAND_OPTIONS
  755. BuildCommandOptions (
  756. LPCTSTR pszCommandLine
  757. )
  758. {
  759. PCOMMAND_OPTIONS pOptions;
  760. ULONG cbCommandLine;
  761. if (pszCommandLine == NULL)
  762. {
  763. return NULL;
  764. }
  765. cbCommandLine = (lstrlen(pszCommandLine) + 1) * sizeof (TCHAR);
  766. pOptions = MemAlloc (HEAP_ZERO_MEMORY,
  767. sizeof (COMMAND_OPTIONS) + cbCommandLine);
  768. if (pOptions)
  769. {
  770. TCHAR* pch;
  771. TCHAR* pArgumentStart;
  772. PTSTR* ppNextArgument = NULL;
  773. pOptions->CommandLineBuffer = (PTSTR) (pOptions + 1);
  774. RtlCopyMemory (
  775. pOptions->CommandLineBuffer,
  776. pszCommandLine,
  777. cbCommandLine);
  778. pch = pOptions->CommandLineBuffer;
  779. ASSERT (pch);
  780. // Skip the name of the executable.
  781. //
  782. pOptions->ImageName = pch;
  783. while (*pch && (L' ' != *pch) && (L'\t' != *pch))
  784. {
  785. pch++;
  786. }
  787. if (*pch)
  788. {
  789. *pch++ = 0;
  790. }
  791. SvchostCharLowerW (pOptions->ImageName);
  792. while (1)
  793. {
  794. // Skip whitespace.
  795. //
  796. while (*pch && ((L' ' == *pch) || (L'\t' == *pch)))
  797. {
  798. pch++;
  799. }
  800. // End of string?
  801. //
  802. if (!*pch)
  803. {
  804. break;
  805. }
  806. // Is it a '-' or '/' argument?
  807. //
  808. if (((L'-' == *pch) || (L'/' == *pch)) && *(++pch))
  809. {
  810. if ((L'k' == *pch) || (L'K' == *pch))
  811. {
  812. pOptions->fServiceGroup = TRUE;
  813. ppNextArgument = &pOptions->ServiceGroupName;
  814. }
  815. pch++;
  816. continue;
  817. }
  818. // This is the start of an argument.
  819. //
  820. pArgumentStart = pch;
  821. // If the argument starts with a quote, skip it and scan to the
  822. // next quote to terminate it.
  823. //
  824. if ((L'\"' == *pch) && *(++pch))
  825. {
  826. pArgumentStart = pch;
  827. while (*pch && (L'\"' != *pch))
  828. {
  829. pch++;
  830. }
  831. }
  832. // otherwise, skip to the next whitespace and this will be
  833. // our argument.
  834. //
  835. else
  836. {
  837. while (*pch && (L' ' != *pch) && (L'\t' != *pch))
  838. {
  839. pch++;
  840. }
  841. }
  842. if (*pch)
  843. {
  844. // terminate the newly found argument string.
  845. //
  846. *pch++ = 0;
  847. }
  848. if (ppNextArgument)
  849. {
  850. *ppNextArgument = pArgumentStart;
  851. ppNextArgument = NULL;
  852. }
  853. }
  854. pOptions->fServiceGroup = !!pOptions->ServiceGroupName;
  855. SVCHOST_LOG1(TRACE,
  856. "Command line : %ws\n",
  857. pszCommandLine);
  858. SVCHOST_LOG1(TRACE,
  859. "Service Group : %ws\n",
  860. (pOptions->fServiceGroup) ? pOptions->ServiceGroupName : L"No");
  861. // Validate the options.
  862. //
  863. if (!pOptions->fServiceGroup)
  864. {
  865. SVCHOST_LOG2(TRACE,
  866. "Generic Service Host\n\n"
  867. "%ws [-k <key>] | [-r] | <service>\n\n"
  868. " -k <key> Host all services whose ImagePath matches\n"
  869. " %ws -k <key>.\n\n",
  870. pOptions->CommandLineBuffer,
  871. pOptions->CommandLineBuffer);
  872. MemFree (pOptions);
  873. pOptions = NULL;
  874. }
  875. }
  876. return pOptions;
  877. }
  878. VOID
  879. SvchostCharLowerW(
  880. LPWSTR pszString
  881. )
  882. {
  883. //
  884. // LocalVersion of CharLower to avoid pulling in user32.dll
  885. //
  886. int cwchT;
  887. DWORD cwch;
  888. if (pszString == NULL)
  889. {
  890. return;
  891. }
  892. cwch = (DWORD) wcslen(pszString) + 1;
  893. cwchT = LCMapStringW(LOCALE_USER_DEFAULT,
  894. LCMAP_LOWERCASE,
  895. pszString,
  896. cwch,
  897. pszString,
  898. cwch);
  899. if (cwchT == 0)
  900. {
  901. SVCHOST_LOG1(ERROR,
  902. "SvchostCharLowerW failed for %ws\n",
  903. pszString);
  904. }
  905. return;
  906. }
  907. LONG
  908. WINAPI
  909. SvchostUnhandledExceptionFilter(
  910. struct _EXCEPTION_POINTERS *ExceptionInfo
  911. )
  912. {
  913. return RtlUnhandledExceptionFilter(ExceptionInfo);
  914. }
  915. VOID
  916. wmainCRTStartup (
  917. VOID
  918. )
  919. {
  920. LPSERVICE_TABLE_ENTRY pServiceTable = NULL;
  921. PCOMMAND_OPTIONS pOptions;
  922. PCWSTR pszwCommandLine;
  923. SetUnhandledExceptionFilter(&SvchostUnhandledExceptionFilter);
  924. // Prevent critical errors from raising hard error popups and
  925. // halting svchost.exe. The flag below will have the system send
  926. // the errors to the process instead.
  927. //
  928. SetErrorMode(SEM_FAILCRITICALERRORS);
  929. // Initialize our HeapAlloc wrapper to use the process heap.
  930. //
  931. MemInit (GetProcessHeap());
  932. // Initialize our global DLL list, Service array, and the critical
  933. // section that protects them. InitializeCriticalSection can throw a
  934. // STATUS_NO_MEMORY exception. We want the process to exit if that
  935. // happens, so the default exception handler is fine.
  936. //
  937. InitializeListHead (&DllList);
  938. InitializeCriticalSection (&ListLock);
  939. // Build a COMMAND_OPTIONS structure and use it to grovel the registry
  940. // and create the service entry table.
  941. //
  942. pszwCommandLine = GetCommandLine ();
  943. pOptions = BuildCommandOptions (pszwCommandLine);
  944. if (pOptions)
  945. {
  946. BuildServiceArray (pOptions);
  947. pServiceTable = BuildServiceTable ();
  948. if (pServiceTable)
  949. {
  950. CallPerInstanceInitFunctions(pOptions);
  951. }
  952. MemFree (pOptions);
  953. }
  954. // If we have a valid service entry table, use it to transfer control
  955. // to the service controller. StartServiceCtrlDispatcher won't return
  956. // until all services are stopped.
  957. //
  958. if (pServiceTable)
  959. {
  960. StartServiceCtrlDispatcher (pServiceTable);
  961. }
  962. SVCHOST_LOG1(TRACE,
  963. "Calling ExitProcess for %ws\n",
  964. pszwCommandLine);
  965. ExitProcess (0);
  966. }