2 * PROJECT: ReactOS Service Host
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: base/services/svchost/svchost.c
5 * PURPOSE: Main Service Host Implementation Routines
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
18 CRITICAL_SECTION ListLock
;
21 PSVCHOST_SERVICE ServiceArray
;
23 /* FUNCTIONS *****************************************************************/
28 SvchostUnhandledExceptionFilter (
29 _In_
struct _EXCEPTION_POINTERS
* ExceptionInfo
32 /* Call the default OS handler */
33 return RtlUnhandledExceptionFilter(ExceptionInfo
);
38 DummySvchostCtrlHandler (
42 /* This is just a stub while we abort loading */
49 _In_ LPWSTR pszCmdLine
53 PSVCHOST_OPTIONS pOptions
;
54 LPWSTR pch
, pGroupNameStart
, lpServiceGroup
;
55 LPWSTR
*pGroupName
= NULL
;
57 /* Nothing to do without a command-line */
58 if (pszCmdLine
== NULL
) return ERROR_SUCCESS
;
60 /* Get the length if the command line*/
61 cbCmdLine
= sizeof(WCHAR
) * lstrlenW(pszCmdLine
) + sizeof(UNICODE_NULL
);
63 /* Allocate a buffer for the parsed options */
64 pOptions
= MemAlloc(HEAP_ZERO_MEMORY
, cbCmdLine
+ sizeof(*pOptions
));
65 if (pOptions
== NULL
) return ERROR_SUCCESS
;
67 /* Copy the buffer into the parsed options block */
68 pOptions
->CmdLineBuffer
= (PWCHAR
)(pOptions
+ 1);
69 memcpy(pOptions
->CmdLineBuffer
, pszCmdLine
, cbCmdLine
);
71 /* Set the command-line as being inside the block itself */
72 pch
= pOptions
->CmdLineBuffer
;
74 pOptions
->CmdLine
= pch
;
77 while (*pch
!= UNICODE_NULL
)
79 /* And look for the first space or TAB */
80 if ((*pch
== ' ') || (*pch
== '\t'))
82 /* Terminate the command-line there */
83 *pch
++ = UNICODE_NULL
;
91 /* Lowercase the entire command line, but not the options */
92 SvchostCharLowerW(pOptions
->CmdLine
);
94 /* Loop over the command-line */
95 while (*pch
!= UNICODE_NULL
)
97 /* Do an inner loop within it */
98 while (*pch
!= UNICODE_NULL
)
100 /* And keep going until we find a space or TAB */
101 if ((*pch
!= ' ') && (*pch
!= '\t')) break;
105 /* Did we reach the end? If so, bail out */
106 if (*pch
== UNICODE_NULL
) break;
108 /* We found the space -- is it followed by a dash or slash? */
109 if ((*pch
== '-' || *pch
== '/') && (*(pch
+ 1) != UNICODE_NULL
))
111 /* Yep, and there's at least one character. Is it k or K? */
113 if (*pch
== 'k' || *pch
== 'K')
115 /* Yep, we have a proper command line with a group! */
116 pOptions
->HasServiceGroup
= TRUE
;
117 pGroupName
= &pOptions
->ServiceGroupName
;
120 /* Keep going -- the name follows between spaces or quotes */
125 /* Nope, so this might be the group being entered */
126 pGroupNameStart
= pch
;
128 /* Check if there's a quote */
129 if ((*pch
== '"') && (*(pch
+ 1) != UNICODE_NULL
))
131 /* There is, keep going until the quote is over with */
132 pGroupNameStart
= ++pch
;
133 while (*pch
) if (*pch
++ == '"') break;
137 /* No quote, so keep going until the next space or TAB */
138 while ((*pch
) && ((*pch
!= ' ') && (*pch
!= '\t'))) pch
++;
141 /* Now we have a space or quote delimited name, terminate it */
142 if (*pch
) *pch
++ = UNICODE_NULL
;
144 /* Ok, so we have a string -- was it preceded by a -K or /K? */
147 /* Yes it was, remember this, and don't overwrite it later */
148 *pGroupName
= pGroupNameStart
;
154 /* Print out the service group for the debugger, and the command line */
155 DBG_TRACE("Command line : %ws\n", pszCmdLine
);
156 lpServiceGroup
= (pOptions
->HasServiceGroup
) ?
157 pOptions
->ServiceGroupName
: L
"No";
158 DBG_TRACE("Service Group : %ws\n", lpServiceGroup
);
160 /* Was a group specified? */
161 if (pOptions
->HasServiceGroup
== FALSE
)
163 /* This isn't valid. Print out help on the debugger and fail */
164 DBG_TRACE("Generic Service Host\n\n"
165 "%ws [-k <key>] | [-r] | <service>\n\n"
166 " -k <key> Host all services whose ImagePath matches\n"
167 " %ws -k <key>.\n\n",
173 /* Return the allocated option block, if valid*/
179 ReadPerInstanceRegistryParameters (
181 _In_ PSVCHOST_OPTIONS pOptions
184 DWORD dwError
, dwData
;
187 /* We should have a name for this service group... */
188 ASSERT(pOptions
->ServiceGroupName
);
190 /* Query the group to see what services are part of it */
191 dwError
= RegQueryString(hKey
,
192 pOptions
->ServiceGroupName
,
194 (PBYTE
*)&ServiceNames
);
195 if ((dwError
== ERROR_SUCCESS
) &&
196 ((ServiceNames
== NULL
) || (*ServiceNames
== UNICODE_NULL
)))
198 /* If the key exists but there's no data, fail */
199 return ERROR_INVALID_DATA
;
202 /* Open the key for this group */
203 if (RegOpenKeyExW(hKey
,
204 pOptions
->ServiceGroupName
,
207 &hSubKey
) != ERROR_SUCCESS
)
209 /* If we couldn't, bail out */
213 /* Check if we should initialize COM */
214 if (RegQueryDword(hSubKey
,
215 L
"CoInitializeSecurityParam",
216 &dwData
) == ERROR_SUCCESS
)
218 /* Yes, remember the parameter to be sent when we do so */
219 pOptions
->CoInitializeSecurityParam
= dwData
;
222 /* Also, if COM is requested, we must read a bunch more data... */
223 if (pOptions
->CoInitializeSecurityParam
)
225 /* First, read the authentication level, use a default if none is set */
226 if (RegQueryDword(hSubKey
, L
"AuthenticationLevel", &dwData
))
228 pOptions
->AuthenticationLevel
= RPC_C_AUTHN_LEVEL_PKT
;
232 pOptions
->AuthenticationLevel
= dwData
;
235 /* Do the same for the impersonation level */
236 if (RegQueryDword(hSubKey
, L
"ImpersonationLevel", &dwData
))
238 pOptions
->ImpersonationLevel
= RPC_C_IMP_LEVEL_IDENTIFY
;
242 pOptions
->ImpersonationLevel
= dwData
;
245 /* Do the same for the authentication capabilities */
246 if (RegQueryDword(hSubKey
, L
"AuthenticationCapabilities", &dwData
))
248 pOptions
->AuthenticationCapabilities
= EOAC_NO_CUSTOM_MARSHAL
|
253 pOptions
->AuthenticationCapabilities
= dwData
;
257 /* Check if we need a specific RPC stack size, if not, we'll set it later */
258 if (!RegQueryDword(hSubKey
, L
"DefaultRpcStackSize", &dwData
))
260 pOptions
->DefaultRpcStackSize
= dwData
;
263 /* Finally, check if the services should be marked critical later on */
264 if (!RegQueryDword(hSubKey
, L
"SystemCritical", &dwData
))
266 pOptions
->SystemCritical
= dwData
;
269 /* All done reading the settings */
270 RegCloseKey(hSubKey
);
277 _In_ PSVCHOST_OPTIONS lpOptions
281 PSVCHOST_SERVICE pService
;
282 LPCWSTR pszServiceName
;
285 /* Open the service host key */
286 dwError
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
287 L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost",
291 if (dwError
!= ERROR_SUCCESS
) return;
293 /* Read all the parameters for this service group */
294 dwError
= ReadPerInstanceRegistryParameters(hKey
, lpOptions
);
296 if (dwError
!= ERROR_SUCCESS
) return;
298 /* Acquire the database lock while we create the service group */
299 EnterCriticalSection(&ListLock
);
301 /* Loop the array of services for this group */
302 pszServiceName
= ServiceNames
;
303 while (*pszServiceName
!= UNICODE_NULL
)
305 /* For each one, increment the amount of services we'llhost */
307 pszServiceName
+= lstrlenW(pszServiceName
) + 1;
310 /* We should host at least one... */
311 ASSERT(ServiceCount
);
313 /* Now allocate an array to hold all their pointers */
314 ServiceArray
= MemAlloc(HEAP_ZERO_MEMORY
, sizeof(*pService
) * ServiceCount
);
317 /* Start at the beginning of the array */
318 pService
= ServiceArray
;
320 /* Loop through all the services */
321 pszServiceName
= ServiceNames
;
322 while (*pszServiceName
!= UNICODE_NULL
)
324 /* Save the service's name and move to the next entry */
325 pService
++->pszServiceName
= pszServiceName
;
326 pszServiceName
+= lstrlenW(pszServiceName
) + 1;
329 /* We should have gotten the same count as earlier! */
330 ASSERT(pService
== ServiceArray
+ ServiceCount
);
333 /* We're done, drop the lock */
334 LeaveCriticalSection(&ListLock
);
339 FDebugBreakForService (
340 _In_ LPCWSTR ServiceName
344 BOOL DebugBreakOn
= FALSE
;
347 /* Open the key for Svchost itself */
348 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
349 L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost",
352 &hKey
) == ERROR_SUCCESS
)
354 /* That worked, now open the key for this service, if it exists */
355 if (RegOpenKeyExW(hKey
, ServiceName
, 0, KEY_READ
, &hSubKey
) == ERROR_SUCCESS
)
357 /* It does -- is there a DebugBreak value set? */
358 if (RegQueryDword(hSubKey
, L
"DebugBreak", &Data
) == ERROR_SUCCESS
)
360 /* There is! Check if it's 0 or 1 */
361 DebugBreakOn
= Data
!= 0;
364 /* Done, close the service key */
365 RegCloseKey(hSubKey
);
368 /* Done, close the svchost key */
372 /* Return if the value was set or not */
378 OpenServiceParametersKey (
379 _In_ LPCWSTR lpSubKey
,
384 HKEY hSubKey
= NULL
, hKey
= NULL
;
387 /* Open the services key */
388 dwError
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
389 L
"System\\CurrentControlSet\\Services",
393 if (dwError
== ERROR_SUCCESS
)
395 /* Now open the service specific key, returning its handle */
396 dwError
= RegOpenKeyExW(hKey
, lpSubKey
, 0, KEY_READ
, &hSubKey
);
397 if (dwError
== ERROR_SUCCESS
)
399 /* And if that worked, return a handle to the parameters key */
400 dwError
= RegOpenKeyExW(hSubKey
,
408 /* Clean up any leftovers*/
409 if (hKey
!= NULL
) RegCloseKey(hKey
);
410 if (hSubKey
!= NULL
) RegCloseKey(hSubKey
);
417 _In_ PSVCHOST_SERVICE pService
428 /* Grab the lock while we touch the thread count and potentially unload */
429 EnterCriticalSection(&ListLock
);
431 /* There is one less active callout into the DLL, it may be unloadabl now */
432 ASSERT(pService
->cServiceActiveThreadCount
> 0);
433 pService
->cServiceActiveThreadCount
--;
435 /* Try to open the service registry key */
436 if (OpenServiceParametersKey(pService
->pszServiceName
, &hKey
) == ERROR_SUCCESS
)
438 /* It worked -- check if we should unload the service when stopped */
439 if (RegQueryValueExW(hKey
,
440 L
"ServiceDllUnloadOnStop",
444 &cbData
) == ERROR_SUCCESS
)
446 /* Make sure the data is correctly formatted */
447 if ((dwType
== REG_DWORD
) && (Data
== 1))
449 /* Does the service still have active threads? */
450 if (pService
->cServiceActiveThreadCount
!= 0)
452 /* Yep, so we can't kill it just yet */
453 DBG_TRACE("Skip Unload dll %ws for service %ws, "
454 "active threads %d\n",
455 pService
->pDll
->pszDllPath
,
456 pService
->pszServiceName
,
457 pService
->cServiceActiveThreadCount
);
461 /* Nope, we're good to go */
462 DBG_TRACE("Unload dll %ws for service %ws\n",
463 pService
->pDll
->pszDllPath
,
464 pService
->pszServiceName
);
466 /* Free the library and clear the module handle */
467 FreeLibrary(pService
->pDll
->hModule
);
468 pService
->pDll
->hModule
= NULL
;
474 /* Drop the lock, unload is complete */
475 LeaveCriticalSection(&ListLock
);
477 /* Close the parameters key if we had it */
478 if (hKey
) RegCloseKey(hKey
);
483 SvchostStopCallback (
485 _In_ BOOLEAN TimerOrWaitFired
488 PSVCHOST_CALLBACK_CONTEXT pSvcsStopCbContext
= Context
;
489 PSVCHOST_STOP_CALLBACK pfnStopCallback
;
491 /* Hold the lock while we grab the callback */
492 EnterCriticalSection(&ListLock
);
494 /* Grab the callback, then clear it */
495 ASSERT(pSvcsStopCbContext
->pService
!= NULL
);
496 pfnStopCallback
= pSvcsStopCbContext
->pService
->pfnStopCallback
;
497 ASSERT(pfnStopCallback
!= NULL
);
498 pSvcsStopCbContext
->pService
->pfnStopCallback
= NULL
;
500 /* Now release the lock, making sure the above was atomic */
501 LeaveCriticalSection(&ListLock
);
503 /* Now make the callout */
504 DBG_TRACE("Call stop callback for service %ws, active threads %d\n",
505 pSvcsStopCbContext
->pService
->pszServiceName
,
506 pSvcsStopCbContext
->pService
->cServiceActiveThreadCount
);
507 pfnStopCallback(pSvcsStopCbContext
->pContext
, TimerOrWaitFired
);
509 /* Decrement the active threads -- maybe the DLL can unload now */
510 UnloadServiceDll(pSvcsStopCbContext
->pService
);
512 /* We no longer need the context */
513 MemFree(pSvcsStopCbContext
);
518 SvcRegisterStopCallback (
519 _In_ PHANDLE phNewWaitObject
,
520 _In_ LPCWSTR ServiceName
,
522 _In_ PSVCHOST_STOP_CALLBACK pfnStopCallback
,
528 PSVCHOST_CALLBACK_CONTEXT pSvcsStopCbContext
= NULL
;
529 PSVCHOST_SERVICE pService
= NULL
;
530 DWORD dwError
= ERROR_SUCCESS
;
532 /* All parameters must be present */
533 if ((phNewWaitObject
== NULL
) ||
534 (ServiceName
== NULL
) ||
536 (pfnStopCallback
== NULL
))
539 return ERROR_INVALID_PARAMETER
;
542 /* Don't allow new DLLs while we send notifications */
543 EnterCriticalSection(&ListLock
);
545 /* Scan all registered services */
546 for (i
= 0; i
< ServiceCount
; i
++)
548 /* Search for the service entry matching this service name */
549 if (lstrcmpiW(ServiceName
, ServiceArray
[i
].pszServiceName
) == 0)
552 pService
= &ServiceArray
[i
];
557 /* Do we have a service? Does it already have a callback? */
558 if ((pService
== NULL
) || (pService
->pfnStopCallback
!= NULL
))
560 /* Affirmative, nothing for us to do */
561 dwError
= ERROR_INVALID_DATA
;
562 DBG_ERR("Service %ws missing or already registered for callback, "
569 /* Allocate our internal context */
570 pSvcsStopCbContext
= MemAlloc(HEAP_ZERO_MEMORY
, sizeof(*pSvcsStopCbContext
));
571 if (pSvcsStopCbContext
== NULL
)
573 /* We failed, bail out */
574 dwError
= ERROR_NOT_ENOUGH_MEMORY
;
575 DBG_ERR("MemAlloc for context block for service %ws failed, status "
582 /* Setup the context and register for the wait */
583 pSvcsStopCbContext
->pContext
= pContext
;
584 pSvcsStopCbContext
->pService
= pService
;
585 if (RegisterWaitForSingleObject(phNewWaitObject
,
592 /* We now have an active thread associated with this wait */
593 pService
->cServiceActiveThreadCount
++;
594 pService
->pfnStopCallback
= pfnStopCallback
;
595 DBG_TRACE("Registered stop callback for service %ws, active threads %d\n",
597 pService
->cServiceActiveThreadCount
);
601 /* Registration failed, bail out */
602 dwError
= GetLastError();
603 DBG_ERR("Registration for stop callback for service %ws failed, "
610 /* Drop the lock, and free the context if we failed */
611 LeaveCriticalSection(&ListLock
);
612 if (dwError
!= ERROR_SUCCESS
) MemFree(pSvcsStopCbContext
);
618 AbortSvchostService (
619 _In_ LPCWSTR lpServiceName
,
620 _In_ DWORD dwExitCode
623 SERVICE_STATUS_HANDLE scHandle
;
624 SERVICE_STATUS ServiceStatus
;
626 /* Make us stopped and accept only query commands */
627 ServiceStatus
.dwCheckPoint
= 0;
628 ServiceStatus
.dwWaitHint
= 0;
629 ServiceStatus
.dwServiceSpecificExitCode
= 0;
630 ServiceStatus
.dwCurrentState
= SERVICE_STOPPED
;
631 ServiceStatus
.dwControlsAccepted
= SERVICE_QUERY_CONFIG
;
632 ServiceStatus
.dwServiceType
= SERVICE_WIN32
;
633 ServiceStatus
.dwWin32ExitCode
= dwExitCode
;
635 /* Register a handler that will do nothing while we are being stopped */
636 scHandle
= RegisterServiceCtrlHandlerW(lpServiceName
,
637 DummySvchostCtrlHandler
);
641 if (!SetServiceStatus(scHandle
, &ServiceStatus
))
643 /* Tell the debugger if this didn't work */
644 DBG_ERR("AbortSvchostService: SetServiceStatus error %ld\n",
650 /* Tell the debugger if we couldn't register the handler */
651 DBG_ERR("AbortSvchostService: RegisterServiceCtrlHandler failed %d\n",
658 GetServiceDllFunction (
659 _In_ PSVCHOST_DLL pDll
,
660 _In_ LPCSTR lpProcName
,
661 _Out_ PDWORD lpdwError
665 PVOID lpAddress
= NULL
;
666 ULONG_PTR ulCookie
= 0;
668 /* Activate the context */
669 if (ActivateActCtx(pDll
->hActCtx
, &ulCookie
) == FALSE
)
671 /* We couldn't, bail out */
672 if (lpdwError
) *lpdwError
= GetLastError();
673 DBG_ERR("ActivateActCtx for %ws failed. Error %d.\n",
679 /* Check if we already have a loaded module for this service */
680 hModule
= pDll
->hModule
;
683 /* We don't -- load it */
684 hModule
= LoadLibraryExW(pDll
->pszDllPath
,
686 LOAD_WITH_ALTERED_SEARCH_PATH
);
689 /* We failed to load it, bail out */
690 if (lpdwError
) *lpdwError
= GetLastError();
691 DBG_ERR("LoadLibrary (%ws) failed. Error %d.\n",
694 DeactivateActCtx(0, ulCookie
);
699 pDll
->hModule
= hModule
;
702 /* Next, get the address being looked up*/
703 lpAddress
= GetProcAddress(hModule
, lpProcName
);
704 if (!lpAddress
&& lpdwError
)
706 /* We couldn't find it, write the error code and a debug statement */
707 *lpdwError
= GetLastError();
708 DBG_ERR("GetProcAddress (%s) failed on DLL %ws. Error %d.\n",
714 /* All done, get rid of the activation context */
715 DeactivateActCtx(0, ulCookie
);
722 _In_ LPCWSTR pszManifestPath
,
723 _In_ LPCWSTR pszDllPath
,
724 _In_ PSVCHOST_SERVICE pService
727 PSVCHOST_DLL pDll
, pFoundDll
= NULL
;
728 PLIST_ENTRY pNextEntry
;
731 /* Lock the DLL database */
732 EnterCriticalSection(&ListLock
);
734 /* Scan through the database */
735 pNextEntry
= DllList
.Flink
;
736 while (pNextEntry
!= &DllList
)
738 /* Search for an existing DLL with the same parameters */
739 pDll
= CONTAINING_RECORD(pNextEntry
, SVCHOST_DLL
, DllList
);
740 if ((lstrcmpW(pDll
->pszDllPath
, pszDllPath
) == 0) &&
741 (lstrcmpW(pDll
->pszManifestPath
, pszManifestPath
) == 0) &&
742 (lstrcmpW(pDll
->pService
->pszServiceName
, pService
->pszServiceName
) == 0))
750 pNextEntry
= pNextEntry
->Flink
;
753 /* All done, release the lock and return what we found, if anything */
754 LeaveCriticalSection(&ListLock
);
761 _In_ LPCWSTR pszManifestPath
,
762 _In_ LPCWSTR pszDllPath
,
763 _In_ PSVCHOST_SERVICE pService
,
764 _Out_ PDWORD lpdwError
768 ULONG nDllPathLength
, nManifestPathLength
;
772 /* Compute the length of the two paths */
773 nDllPathLength
= lstrlenW(pszDllPath
);
774 nManifestPathLength
= lstrlenW(pszManifestPath
);
776 /* Allocate the DLL entry for this service */
777 pDll
= MemAlloc(HEAP_ZERO_MEMORY
,
779 (nDllPathLength
+ nManifestPathLength
) * sizeof(WCHAR
) +
780 2 * sizeof(UNICODE_NULL
));
783 /* Bail out with failure */
784 *lpdwError
= ERROR_NOT_ENOUGH_MEMORY
;
788 /* Put the DLL path right after the DLL entry */
789 pDll
->pszDllPath
= (LPCWSTR
)(pDll
+ 1);
791 /* Put the manifest path right past the DLL path (note the pointer math) */
792 pDll
->pszManifestPath
= pDll
->pszDllPath
+ nDllPathLength
+ 1;
794 /* Now copy both paths */
795 CopyMemory((PVOID
)pDll
->pszDllPath
,
797 sizeof(WCHAR
) * nDllPathLength
);
798 CopyMemory((PVOID
)pDll
->pszManifestPath
,
800 sizeof(WCHAR
) * nManifestPathLength
);
802 /* Now set the service, and make sure both paths are NULL-terminated */
803 pDll
->pService
= pService
;
804 ASSERT(pDll
->hActCtx
== NULL
);
805 ASSERT(pDll
->pszDllPath
[nDllPathLength
] == UNICODE_NULL
);
806 ASSERT(pDll
->pszManifestPath
[nManifestPathLength
] == UNICODE_NULL
);
808 /* Finally, add the entry to the DLL database, while holding the lock */
809 EnterCriticalSection(&ListLock
);
810 InsertTailList(&DllList
, &pDll
->DllList
);
811 LeaveCriticalSection(&ListLock
);
819 GetServiceMainFunctions (
820 _In_ PSVCHOST_SERVICE pService
,
821 _Out_ PVOID
*pServiceMain
,
822 _Out_ PVOID
*pPushServiceGlobals
,
823 _Out_ PDWORD lpdwError
826 DWORD dwError
, cbDllLength
, cbData
, dwType
;
833 WCHAR szDllBuffer
[MAX_PATH
+ 2], szManifestBuffer
[MAX_PATH
+ 2];
835 /* Initialize the activation context we might need later */
836 RtlZeroMemory(&actCtx
, sizeof(actCtx
));
837 actCtx
.cbSize
= sizeof(actCtx
);
839 /* We clean these up in our failure path so initialize them to NULL here */
843 *lpdwError
= ERROR_SUCCESS
;
845 /* Null terminate the string buffers */
846 szDllBuffer
[0] = UNICODE_NULL
;
847 szManifestBuffer
[0] = UNICODE_NULL
;
849 /* Do we already have a DLL ready to go for this service? */
850 pDll
= pService
->pDll
;
851 if (pDll
!= NULL
) goto HaveDll
;
853 /* Nope, we're starting from scratch. Open a handle to parameters key */
854 dwError
= OpenServiceParametersKey(pService
->pszServiceName
, &hKey
);
857 *lpdwError
= dwError
;
858 ASSERT(*lpdwError
!= NO_ERROR
);
862 /* Allocate enough space to hold a unicode path (NULL-terminated) */
863 cbData
= MAX_PATH
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
864 lpData
= MemAlloc(0, cbData
);
867 /* No memory, bail out */
868 *lpdwError
= ERROR_NOT_ENOUGH_MEMORY
;
872 /* Query the DLL path */
873 lpData
[0] = UNICODE_NULL
;
874 dwError
= RegQueryValueExW(hKey
,
880 if (dwError
!= ERROR_SUCCESS
)
882 *lpdwError
= dwError
;
883 DBG_ERR("RegQueryValueEx for the ServiceDll parameter of the %ws "
884 "service returned %u\n",
885 pService
->pszServiceName
,
890 /* Is the registry data valid and present? */
891 if ((dwType
!= REG_EXPAND_SZ
) || (lpData
[0] == UNICODE_NULL
))
894 *lpdwError
= ERROR_FILE_NOT_FOUND
;
895 DBG_ERR("The ServiceDll parameter for the %ws service is not of type "
897 pService
->pszServiceName
);
901 /* Convert the expandable path into an absolute path */
902 ExpandEnvironmentStringsW(lpData
, szDllBuffer
, MAX_PATH
);
903 SvchostCharLowerW(szDllBuffer
);
905 /* Check if the service has a manifest file associated with it */
906 cbData
= MAX_PATH
* sizeof(WCHAR
) + sizeof(UNICODE_NULL
);
907 dwError
= RegQueryValueExW(hKey
,
913 if (dwError
!= ERROR_SUCCESS
)
915 /* Did we fail because one wasn't set? */
916 if ((dwError
!= ERROR_PATH_NOT_FOUND
) &&
917 (dwError
!= ERROR_FILE_NOT_FOUND
))
919 /* We failed for some other reason, bail out */
920 *lpdwError
= dwError
;
921 DBG_ERR("RegQueryValueEx for the ServiceManifest parameter of the "
922 "%ws service returned %u\n",
923 pService
->pszServiceName
,
928 /* We have no manifest, make sure the buffer is empty */
929 szManifestBuffer
[0] = UNICODE_NULL
;
931 /* We're done with this buffer */
935 /* Use the whole DLL path, since we don't have a manifest */
936 pszDllPath
= szDllBuffer
;
940 /* Do we have invalid registry data? */
941 if ((dwType
!= REG_EXPAND_SZ
) || (*lpData
== UNICODE_NULL
))
943 /* Yes, pretend there's no manifest and bail out */
944 *lpdwError
= ERROR_FILE_NOT_FOUND
;
945 DBG_ERR("The ServiceManifest parameter for the %ws service is not "
946 "of type REG_EXPAND_SZ\n",
947 pService
->pszServiceName
);
951 /* Expand the string into our stack buffer */
952 ExpandEnvironmentStringsW(lpData
, szManifestBuffer
, MAX_PATH
);
954 /* We no longer need the heap buffer*/
958 /* Lowercase the manifest path */
959 SvchostCharLowerW(szManifestBuffer
);
961 /* Now loop over the DLL path */
962 cbDllLength
= lstrlenW(szDllBuffer
);
965 /* From the end, until we find the first path separator */
966 if (szDllBuffer
[cbDllLength
] == '\\' || szDllBuffer
[cbDllLength
] == '/')
968 /* Use just a short name (cut out the rest of the path) */
969 pszDllPath
= &szDllBuffer
[cbDllLength
+ 1];
973 /* Try the next character */
978 /* See if we already have a matching DLL and manifest for this service */
979 pDll
= FindDll(szManifestBuffer
, pszDllPath
, pService
);
982 /* We don't have it yet -- does this DLL have a manifest? */
983 if (szManifestBuffer
[0] != UNICODE_NULL
)
985 /* Create an activation context for it */
986 actCtx
.lpSource
= szManifestBuffer
;
987 hActCtx
= CreateActCtxW(&actCtx
);
988 if (hActCtx
== INVALID_HANDLE_VALUE
)
990 /* We've failed to create one, bail out */
991 *lpdwError
= GetLastError();
992 DBG_ERR("CreateActCtxW(%ws) for the %ws service returned %u\n",
994 pService
->pszServiceName
,
1000 /* Add this new DLL into the database */
1001 pDll
= AddDll(szManifestBuffer
, pszDllPath
, pService
, lpdwError
);
1004 /* Save the activation context and zero it so we don't release later */
1005 pDll
->hActCtx
= hActCtx
;
1010 /* We either found, added, or failed to add, the DLL for this service */
1011 ASSERT(!pService
->pDll
);
1012 pService
->pDll
= pDll
;
1014 /* In all cases, we will query the ServiceMain function, however*/
1015 RegQueryStringA(hKey
,
1018 &pService
->pszServiceMain
);
1020 /* And now we'll check if we were able to create it earlier (or find it) */
1021 if (!pService
->pDll
)
1023 /* We were not, so there's no point in going on */
1024 ASSERT(*lpdwError
!= NO_ERROR
);
1028 /* We do have a valid DLL, so now get the service main routine out of it */
1030 *pServiceMain
= GetServiceDllFunction(pDll
,
1031 pService
->pszServiceMain
?
1032 pService
->pszServiceMain
:
1036 /* And now get the globals routine out of it (this one is optional) */
1037 *pPushServiceGlobals
= GetServiceDllFunction(pDll
,
1038 "SvchostPushServiceGlobals",
1042 /* We're done, cleanup any resources we left behind */
1043 if (hKey
!= NULL
) RegCloseKey(hKey
);
1044 if (lpData
!= NULL
) MemFree(lpData
);
1045 if ((hActCtx
) && (hActCtx
!= INVALID_HANDLE_VALUE
)) ReleaseActCtx(hActCtx
);
1051 _In_ DWORD dwNumServicesArgs
,
1052 _In_ LPWSTR
*lpServiceArgVectors
1055 DWORD i
, dwError
= ERROR_FILE_NOT_FOUND
;
1056 PSVCHOST_SERVICE ServiceEntry
;
1057 LPCWSTR lpServiceName
;
1058 LPSERVICE_MAIN_FUNCTIONW ServiceMain
= NULL
;
1059 PSVCHOST_INIT_GLOBALS ServiceInitGlobals
= NULL
;
1061 /* Hold the lock while we start a service */
1062 EnterCriticalSection(&ListLock
);
1064 /* Get this service's name, and loop the database */
1065 lpServiceName
= *lpServiceArgVectors
;
1066 for (i
= 0; i
< ServiceCount
; i
++)
1068 /* Try to find a match by name */
1069 if (!lstrcmpiW(lpServiceName
, ServiceArray
[i
].pszServiceName
)) break;
1072 /* Check if we didn't find it */
1073 if (i
== ServiceCount
)
1075 /* This looks like a bogus attempt, bail out */
1076 LeaveCriticalSection(&ListLock
);
1081 ServiceEntry
= &ServiceArray
[i
];
1083 /* Should we breakpoint before loading this service? */
1084 if (FDebugBreakForService(lpServiceName
) != FALSE
)
1087 DBG_TRACE("Attaching debugger before getting ServiceMain for %ws...\n",
1092 /* Get the callbacks for this service */
1093 GetServiceMainFunctions(&ServiceArray
[i
],
1094 (PVOID
*)&ServiceMain
,
1095 (PVOID
*)&ServiceInitGlobals
,
1098 /* If this service is valid and needs globals, but we don't have them... */
1099 if ((ServiceMain
!= NULL
) &&
1100 (ServiceInitGlobals
!= NULL
) &&
1101 (g_pSvchostSharedGlobals
== NULL
))
1103 /* Go and build them for the first time! */
1104 SvchostBuildSharedGlobals();
1107 /* We call a service if it has a main, or if wants globals and we have them */
1108 if (((ServiceInitGlobals
!= NULL
) && (g_pSvchostSharedGlobals
!= NULL
)) ||
1109 ((ServiceMain
!= NULL
) && (ServiceInitGlobals
== NULL
)))
1111 /* Therefore, make sure it won't be unloaded as we drop the lock */
1112 ServiceEntry
->cServiceActiveThreadCount
++;
1115 /* Drop the lock while we call into the service DLL */
1116 LeaveCriticalSection(&ListLock
);
1118 /* First: does this service want globals? */
1119 if (ServiceInitGlobals
!= NULL
)
1121 /* Do we have any to give? */
1122 if (g_pSvchostSharedGlobals
!= NULL
)
1124 /* Yes, push them to the service */
1125 ServiceInitGlobals(g_pSvchostSharedGlobals
);
1127 /* Does the service NOT have an entrypoint? */
1128 if (ServiceMain
== NULL
)
1130 /* We're going to abort loading, it serves no use */
1131 if (lpServiceName
!= NULL
)
1133 AbortSvchostService(lpServiceName
, dwError
);
1136 /* And drop a reference count since it's not active anymore */
1137 UnloadServiceDll(ServiceEntry
);
1140 else if (lpServiceName
!= NULL
)
1142 /* We have no globals to give, so don't even bother calling it */
1143 AbortSvchostService(lpServiceName
, dwError
);
1147 /* Next up, does it have an entrypoint? */
1148 if (ServiceMain
!= NULL
)
1150 /* It does, so call it */
1151 DBG_TRACE("Calling ServiceMain for %ws...\n", lpServiceName
);
1152 ServiceMain(dwNumServicesArgs
, lpServiceArgVectors
);
1154 /* We're out of the service now, so we can drop a reference */
1155 UnloadServiceDll(ServiceEntry
);
1157 else if (lpServiceName
!= NULL
)
1159 /* It has no entrypoint, so abort its launch */
1160 AbortSvchostService(lpServiceName
, dwError
);
1164 SERVICE_TABLE_ENTRYW
*
1170 SERVICE_TABLE_ENTRYW
*pServiceTable
;
1173 /* Acquire the database lock while we go over the services */
1174 EnterCriticalSection(&ListLock
);
1176 /* Allocate space for a NULL entry at the end as well, Windows needs this */
1177 pServiceTable
= MemAlloc(HEAP_ZERO_MEMORY
,
1178 (ServiceCount
+ 1) * sizeof(*pServiceTable
));
1181 /* Go over all our registered services */
1182 for (i
= 0; i
< ServiceCount
; i
++)
1184 /* And set their parameters in the service table */
1185 pServiceTable
[i
].lpServiceName
= (LPWSTR
)ServiceArray
[i
].pszServiceName
;
1186 pServiceTable
[i
].lpServiceProc
= ServiceStarter
;
1187 DBG_TRACE("Added service table entry for %ws\n",
1188 pServiceTable
[i
].lpServiceName
);
1192 /* All done, can release the lock now */
1193 LeaveCriticalSection(&ListLock
);
1194 return pServiceTable
;
1199 CallPerInstanceInitFunctions (
1200 _In_ PSVCHOST_OPTIONS pOptions
1203 PIMAGE_NT_HEADERS pNtHeaders
;
1206 /* Is COM required for this host? */
1207 if (pOptions
->CoInitializeSecurityParam
!= 0)
1209 /* Yes, initialize COM security and parameters */
1210 bResult
= InitializeSecurity(pOptions
->CoInitializeSecurityParam
,
1211 pOptions
->AuthenticationLevel
,
1212 pOptions
->ImpersonationLevel
,
1213 pOptions
->AuthenticationCapabilities
);
1214 if (bResult
== FALSE
) return FALSE
;
1217 /* Do we have a custom RPC stack size? */
1218 if (pOptions
->DefaultRpcStackSize
!= 0)
1220 /* Yes, go set it */
1221 RpcMgmtSetServerStackSize(pOptions
->DefaultRpcStackSize
<< 10);
1225 /* Nope, get the NT headers from the image */
1226 pNtHeaders
= RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress
);
1229 /* And just use whatever the default thread stack is */
1230 RpcMgmtSetServerStackSize(pNtHeaders
->OptionalHeader
.SizeOfStackCommit
);
1234 /* Is this host holding critical services? */
1235 if (pOptions
->SystemCritical
!= FALSE
)
1237 /* Mark the process as critical if so */
1238 RtlSetProcessIsCritical(TRUE
, NULL
, TRUE
);
1251 PSVCHOST_OPTIONS lpOptions
;
1252 SERVICE_TABLE_ENTRYW
*pServiceTable
;
1255 /* Set a generic SEH filter and hard fail all critical errors */
1256 SetUnhandledExceptionFilter(SvchostUnhandledExceptionFilter
);
1257 SetErrorMode(SEM_FAILCRITICALERRORS
);
1259 /* Initialize the heap allocator */
1260 MemInit(GetProcessHeap());
1262 /* Initialize the DLL database and lock */
1263 InitializeListHead(&DllList
);
1264 InitializeCriticalSection(&ListLock
);
1266 /* Get the command-line and parse it to get the service group */
1267 pszCmdLine
= GetCommandLineW();
1268 lpOptions
= BuildCommandOptions(pszCmdLine
);
1269 if (lpOptions
== NULL
)
1271 /* Without a valid command-line, there's nothing for us to do */
1272 DBG_TRACE("Calling ExitProcess for %ws\n", pszCmdLine
);
1276 /* Now use the service group information to lookup all the services */
1277 BuildServiceArray(lpOptions
);
1279 /* Convert the list of services in this group to the SCM format */
1280 pServiceTable
= BuildServiceTable();
1281 if (pServiceTable
== NULL
)
1283 /* This is critical, bail out without it */
1288 /* Initialize COM and RPC as needed for this service group */
1289 if (CallPerInstanceInitFunctions(lpOptions
) == FALSE
)
1291 /* Exit with a special code indicating COM/RPC setup has failed */
1292 DBG_ERR("%s", "CallPerInstanceInitFunctions failed -- exiting!\n");
1296 /* We're ready to go -- free the options buffer */
1299 /* And call into ADVAPI32 to get our services going */
1300 StartServiceCtrlDispatcherW(pServiceTable
);