Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1372 lines
37 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2000.
//
// File: svchost.c
//
// Contents: Generic Host Process for Win32 Services
//
// Classes:
//
// Functions:
//
// History: 3-30-98 RichardW Created
// 3-31-98 ShaunCo Took ownership.
// Finished off basic implementation.
// 1-24-00 JSchwart Took ownership.
// Adapted to run NT intrinsic services.
// April 2002 JayKrell added simple support for ServiceManifest registry setting
// did minimal cleanup..much room for improvement..
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include "globals.h"
#include "registry.h"
#include "security.h"
//
// Generic Service Process:
//
// This process will grovel the service portion of the registry, looking
// for instances of itself (details below), and constructing a list of services
// to submit to the service controller. As an individual service is started,
// the DLL is loaded and the entry point called. Services in these DLLs are
// expected to play nicely with others, that is, use the common thread pool,
// not stomp memory, etc.
//
//
// Loading.
//
// Each service that will be resident in this process must have svchost.exe as
// the ImagePath, with the same parameters. Additionally, the service must
// have under its Parameters key, these values:
//
// ServiceDll = REG_EXPAND_SZ <path to DLL>
// ServiceMain = REG_SZ <pszFunctionName> OPTIONAL
//
// If ServiceMain is not present, then it defaults to "ServiceMain".
//
//
// Multiple Service Groups
//
// Multiple service groups can be accomplished by supplying parameters to the
// svchost.exe on the ImagePath.
//
// svchost.exe -k "Key"
//
// will grovel the services and only load those with matching ImagePath.
//
#define REGSTR_PATH_SVCHOST TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost")
typedef struct _COMMAND_OPTIONS
{
PTSTR CommandLineBuffer;
PTSTR ImageName;
BOOL fServiceGroup;
PTSTR ServiceGroupName;
//
// dwCoInitializeSecurityParam is a DWORD read from the registry for the
// service group we were instantiated for. If non-zero, we will
// call CoInitializeSecurity in a way based on the value.
//
DWORD dwCoInitializeSecurityParam;
DWORD dwAuthLevel;
DWORD dwImpersonationLevel;
DWORD dwAuthCapabilities;
//
// Default stack size for RPC threads (to prevent stack overflow)
//
DWORD dwDefaultRpcStackSize;
//
// Should this svchost instance mark itself as system-critical?
//
BOOL fSystemCritical;
}
COMMAND_OPTIONS, * PCOMMAND_OPTIONS;
typedef struct _SERVICE_DLL
{
LIST_ENTRY List;
HMODULE hmod;
PTSTR pszDllPath;
PTSTR pszManifestPath;
HANDLE hActCtx;
} SERVICE_DLL, * PSERVICE_DLL;
typedef struct _SERVICE
{
PTSTR pszName;
PSERVICE_DLL pDll;
PSTR pszEntryPoint;
} SERVICE, * PSERVICE;
//+---------------------------------------------------------------------------
//
// Global variables.
//
// ListLock protects access to the Dll list and Service array.
//
CRITICAL_SECTION ListLock;
// DllList is a list of SERVICE_DLL structures representing the DLL's
// which host entry points for the services hosted by this process.
//
LIST_ENTRY DllList;
// ServiceArray is an array of SERVICE structures representing the services
// hosted by this process.
//
PSERVICE ServiceArray;
// ServiceCount is the count of SERVICE entries in ServiceList.
//
UINT ServiceCount;
// ServiceNames is the multi-sz read from the registry for the
// service group we were instantiated for.
//
PTSTR ServiceNames;
//+---------------------------------------------------------------------------
//
// Local function prototypes
//
VOID
SvchostCharLowerW(
LPWSTR pszString
);
//+---------------------------------------------------------------------------
//
VOID
DummySvchostCtrlHandler(
DWORD Opcode
)
{
return;
}
VOID
AbortSvchostService( // used if cant find Service DLL or entrypoint
LPWSTR ServiceName,
DWORD Error
)
{
SERVICE_STATUS_HANDLE GenericServiceStatusHandle;
SERVICE_STATUS GenericServiceStatus;
GenericServiceStatus.dwServiceType = SERVICE_WIN32;
GenericServiceStatus.dwCurrentState = SERVICE_STOPPED;
GenericServiceStatus.dwControlsAccepted = SERVICE_CONTROL_STOP;
GenericServiceStatus.dwCheckPoint = 0;
GenericServiceStatus.dwWaitHint = 0;
GenericServiceStatus.dwWin32ExitCode = Error;
GenericServiceStatus.dwServiceSpecificExitCode = 0;
GenericServiceStatusHandle = RegisterServiceCtrlHandler(ServiceName,
DummySvchostCtrlHandler);
if (GenericServiceStatusHandle == NULL)
{
SVCHOST_LOG1(ERROR,
"AbortSvchostService: RegisterServiceCtrlHandler failed %d\n",
GetLastError());
}
else if (!SetServiceStatus (GenericServiceStatusHandle,
&GenericServiceStatus))
{
SVCHOST_LOG1(ERROR,
"AbortSvchostService: SetServiceStatus error %ld\n",
GetLastError());
}
return;
}
FARPROC
GetServiceDllFunction (
PSERVICE_DLL pDll,
PCSTR pszFunctionName,
LPDWORD lpdwError OPTIONAL
)
{
FARPROC pfn = NULL;
HMODULE hmod;
ULONG_PTR ulpActCtxStackCookie = 0;
BOOL fActivateSuccess = FALSE;
//
// GetProcAddress can lead to .dlls being loaded, at least
// in the presence of forwarders. So we are sure to activate the activationcontext
// even if we are not doing a LoadLibrary here.
//
fActivateSuccess = ActivateActCtx(pDll->hActCtx, &ulpActCtxStackCookie);
if (!fActivateSuccess)
{
if (lpdwError)
{
*lpdwError = GetLastError();
}
SVCHOST_LOG2(ERROR,
"ActivateActCtx for %ws failed. Error %d.\n",
pDll->pszDllPath,
GetLastError());
goto Exit;
}
// Load the module if neccessary.
//
hmod = pDll->hmod;
if (!hmod)
{
hmod = LoadLibraryEx (
pDll->pszDllPath,
NULL,
LOAD_WITH_ALTERED_SEARCH_PATH);
if (hmod)
{
pDll->hmod = hmod;
}
else
{
if (lpdwError)
{
*lpdwError = GetLastError();
}
SVCHOST_LOG2(ERROR,
"LoadLibrary (%ws) failed. Error %d.\n",
pDll->pszDllPath,
GetLastError());
goto Exit;
}
}
ASSERT (hmod);
pfn = GetProcAddress(hmod, pszFunctionName);
if (!pfn)
{
if (lpdwError)
{
*lpdwError = GetLastError();
}
SVCHOST_LOG3(TRACE,
"GetProcAddress (%s) failed on DLL %ws. Error = %d.\n",
pszFunctionName,
pDll->pszDllPath,
GetLastError());
}
Exit:
if (fActivateSuccess)
DeactivateActCtx(0, ulpActCtxStackCookie);
return pfn;
}
PSERVICE_DLL
FindDll(
IN LPCTSTR pszManifestPath,
IN LPCTSTR pszDllPath
)
{
PLIST_ENTRY pNode;
PSERVICE_DLL pDll = NULL;
ASSERT (pszDllPath);
EnterCriticalSection (&ListLock);
pNode = DllList.Flink;
while (pNode != &DllList)
{
pDll = CONTAINING_RECORD (pNode, SERVICE_DLL, List);
if (0 == lstrcmp (pDll->pszDllPath, pszDllPath)
&& 0 == lstrcmp (pDll->pszManifestPath, pszManifestPath)
)
{
break;
}
pDll = NULL;
pNode = pNode->Flink;
}
LeaveCriticalSection (&ListLock);
return pDll;
}
PSERVICE_DLL
AddDll(
IN LPCTSTR pszManifestPath,
IN LPCTSTR pszDllPath,
OUT LPDWORD lpdwError
)
{
PSERVICE_DLL pDll;
SIZE_T nDllPathLength;
SIZE_T nManifestPathLength;
ASSERT (pszDllPath);
ASSERT (*pszDllPath);
nDllPathLength = lstrlenW (pszDllPath);
nManifestPathLength = lstrlenW (pszManifestPath);
pDll = (PSERVICE_DLL)MemAlloc (HEAP_ZERO_MEMORY,
sizeof (SERVICE_DLL)
+ ((nDllPathLength + 1) * sizeof(WCHAR))
+ ((nManifestPathLength + 1) * sizeof(WCHAR))
);
if (pDll)
{
// Set the structure members.
//
pDll->pszDllPath = (PTSTR) (pDll + 1);
pDll->pszManifestPath = pDll->pszDllPath + nDllPathLength + 1;
CopyMemory(pDll->pszDllPath, pszDllPath, nDllPathLength * sizeof(WCHAR));
CopyMemory(pDll->pszManifestPath, pszManifestPath, nManifestPathLength * sizeof(WCHAR));
ASSERT(pDll->hActCtx == NULL);
ASSERT(pDll->pszDllPath[nDllPathLength] == 0);
ASSERT(pDll->pszManifestPath[nManifestPathLength] == 0);
// Add the entry to the list.
//
EnterCriticalSection (&ListLock);
InsertTailList (&DllList, &pDll->List);
LeaveCriticalSection (&ListLock);
}
else
{
*lpdwError = ERROR_NOT_ENOUGH_MEMORY;
}
return pDll;
}
LONG
OpenServiceParametersKey (
LPCTSTR pszServiceName,
HKEY* phkey
)
{
LONG lr;
HKEY hkeyServices = NULL;
HKEY hkeySvc = NULL;
ASSERT (phkey);
// Open the Services key.
//
lr = RegOpenKeyEx (
HKEY_LOCAL_MACHINE,
REGSTR_PATH_SERVICES,
0,
KEY_READ,
&hkeyServices);
if (lr != ERROR_SUCCESS)
goto Exit;
// Open the service key.
//
lr = RegOpenKeyEx (
hkeyServices,
pszServiceName,
0,
KEY_READ,
&hkeySvc);
if (lr != ERROR_SUCCESS)
goto Exit;
// Open the Parameters key.
//
lr = RegOpenKeyEx (
hkeySvc,
TEXT("Parameters"),
0,
KEY_READ,
phkey);
Exit:
if (hkeyServices != NULL)
RegCloseKey(hkeyServices);
if (hkeySvc != NULL)
RegCloseKey(hkeySvc);
return lr;
}
#if DBG
BOOL
FDebugBreakForService (
LPCWSTR pszwService
)
{
BOOL fAttach = FALSE;
LONG lr;
HKEY hkeySvchost;
// Open the Svchost key.
//
lr = RegOpenKeyEx (
HKEY_LOCAL_MACHINE,
REGSTR_PATH_SVCHOST,
0,
KEY_READ,
&hkeySvchost);
if (!lr)
{
HKEY hkeyServiceOptions;
// Look for the key with the same name as the service.
//
lr = RegOpenKeyExW (
hkeySvchost,
pszwService,
0,
KEY_READ,
&hkeyServiceOptions);
if (!lr)
{
DWORD dwValue;
lr = RegQueryDword (
hkeyServiceOptions,
TEXT("DebugBreak"),
&dwValue);
if (!lr)
{
fAttach = !!dwValue;
}
RegCloseKey (hkeyServiceOptions);
}
RegCloseKey (hkeySvchost);
}
return fAttach;
}
#endif
VOID
GetServiceMainFunctions (
PSERVICE pService,
LPSERVICE_MAIN_FUNCTION *ppfnServiceMain,
LPSVCHOST_PUSH_GLOBAL_FUNCTION *ppfnPushGlobals,
LPDWORD lpdwError
)
{
LPCSTR pszEntryPoint;
ACTCTXW ActCtxW = { sizeof(ActCtxW) };
HANDLE hActCtx = NULL;
HKEY hkeyParams = NULL;
PSERVICE_DLL pDll = NULL;
WCHAR pszExpandedDllName [MAX_PATH + 1];
PWSTR pszDllName = pszExpandedDllName; // This sometimes get bumped forward to be the leaf name.
WCHAR pszExpandedManifestName [MAX_PATH + 1];
WCHAR * Temp = NULL;
const DWORD TempSize = sizeof(pszExpandedDllName);
*lpdwError = NO_ERROR;
pszExpandedDllName[0] = 0;
pszExpandedManifestName[0] = 0;
// Get the dll and entrypoint for this service if we don't have it yet.
//
if (!pService->pDll)
{
LONG lr;
lr = OpenServiceParametersKey (pService->pszName, &hkeyParams);
if (!lr)
{
DWORD dwType;
DWORD dwSize;
Temp = (WCHAR*)MemAlloc(0, TempSize);
if (Temp == NULL)
{
*lpdwError = ERROR_NOT_ENOUGH_MEMORY;
goto Exit;
}
Temp[0] = 0;
// Look for the service dll path and expand it.
//
dwSize = TempSize;
lr = RegQueryValueEx (
hkeyParams,
TEXT("ServiceDll"),
NULL,
&dwType,
(LPBYTE)Temp,
&dwSize);
if (lr != ERROR_SUCCESS)
{
*lpdwError = lr;
SVCHOST_LOG2(ERROR,
"RegQueryValueEx for the ServiceDll parameter of the "
"%ws service returned %u\n",
pService->pszName,
lr);
goto Exit;
}
if (dwType != REG_EXPAND_SZ)
{
*lpdwError = ERROR_FILE_NOT_FOUND;
SVCHOST_LOG1(ERROR,
"The ServiceDll parameter for the %ws service is not "
"of type REG_EXPAND_SZ\n",
pService->pszName);
goto Exit;
}
if (Temp[0] == 0)
{
*lpdwError = ERROR_FILE_NOT_FOUND;
goto Exit;
}
// Expand the dll name and lower case it for comparison
// when we try to find an existing dll record.
//
ExpandEnvironmentStrings (
Temp,
pszDllName,
MAX_PATH);
SvchostCharLowerW (pszDllName);
dwSize = TempSize;
lr = RegQueryValueExW (
hkeyParams,
L"ServiceManifest",
NULL,
&dwType,
(LPBYTE)Temp,
&dwSize);
switch (lr)
{
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
// ok
pszExpandedManifestName[0] = 0;
MemFree(Temp);
Temp = NULL;
goto NoManifest;
default:
*lpdwError = lr;
SVCHOST_LOG2(ERROR,
"RegQueryValueEx for the ServiceManifest parameter of the "
"%ws service returned %u\n",
pService->pszName,
lr);
goto Exit;
case ERROR_SUCCESS:
if (REG_EXPAND_SZ != dwType)
{
// invalid parameter is probably better here, but just do
// as it does for ServiceDll
*lpdwError = ERROR_FILE_NOT_FOUND;
SVCHOST_LOG1(ERROR,
"The ServiceManifest parameter for the %ws service is not "
"of type REG_EXPAND_SZ\n",
pService->pszName);
goto Exit;
}
if (Temp[0] == 0)
{
// invalid parameter is probably better here, but just do
// as it does for ServiceDll
*lpdwError = ERROR_FILE_NOT_FOUND;
SVCHOST_LOG1(ERROR,
"The ServiceManifest parameter for the %ws service is not "
"of type REG_EXPAND_SZ\n",
pService->pszName);
goto Exit;
}
}
// Expand the manifest name and lower case it for comparison
// when we try to find an existing dll record.
//
ExpandEnvironmentStringsW (
Temp,
pszExpandedManifestName,
MAX_PATH);
MemFree(Temp);
Temp = NULL;
SvchostCharLowerW (pszExpandedManifestName);
//
// Now only use the leaf dll name.
// This way people can setup/register the same for downlevel and sidebyside.
//
{
SIZE_T i = lstrlenW(pszDllName);
while (i != 0)
{
i -= 1;
if (pszDllName[i] == L'\\' || pszDllName[i] == L'/')
{
pszDllName = pszDllName + i + 1;
break;
}
}
}
NoManifest:
// Try to find an existing dll record that we might have and
// if we don't, add this as a new record.
//
pDll = FindDll (pszExpandedManifestName, pszDllName);
if (!pDll)
{
if (pszExpandedManifestName[0] != 0)
{
ActCtxW.lpSource = pszExpandedManifestName;
hActCtx = CreateActCtxW(&ActCtxW);
if (hActCtx == INVALID_HANDLE_VALUE)
{
*lpdwError = GetLastError();
SVCHOST_LOG3(ERROR,
"CreateActCtxW(%ws) for the "
"%ws service returned %u\n",
pszExpandedManifestName,
pService->pszName,
*lpdwError);
goto Exit;
}
}
// Remember this dll for this service for next time.
//
pDll = AddDll (pszExpandedManifestName, pszDllName, lpdwError);
if (pDll == NULL)
{
//
// Don't set *lpdwError here as AddDll already set it
//
goto Exit;
}
pDll->hActCtx = hActCtx;
hActCtx = NULL;
}
ASSERT (!pService->pDll);
pService->pDll = pDll;
pDll = NULL;
// Look for an explicit entrypoint name for this service.
// (Optional)
//
RegQueryStringA (
hkeyParams,
TEXT("ServiceMain"),
REG_SZ,
&pService->pszEntryPoint);
}
else
{
*lpdwError = lr;
}
// If we don't have the service dll record by now, we're through.
//
if (!pService->pDll)
{
ASSERT(*lpdwError != NO_ERROR);
goto Exit;
}
}
// We should have it the dll by now, so proceed to load the entry point.
//
ASSERT (pService->pDll);
// Default the entry point if we don't have one specified.
//
if (pService->pszEntryPoint)
{
pszEntryPoint = pService->pszEntryPoint;
}
else
{
pszEntryPoint = "ServiceMain";
}
// Get the address for the service's ServiceMain
//
*ppfnServiceMain = (LPSERVICE_MAIN_FUNCTION) GetServiceDllFunction(
pService->pDll,
pszEntryPoint,
lpdwError);
// Get the address for the "push the globals" function (optional)
//
*ppfnPushGlobals = (LPSVCHOST_PUSH_GLOBAL_FUNCTION) GetServiceDllFunction(
pService->pDll,
"SvchostPushServiceGlobals",
NULL);
Exit:
if (hkeyParams != NULL)
RegCloseKey (hkeyParams);
if (Temp != NULL)
MemFree(Temp);
if (hActCtx != NULL && hActCtx != INVALID_HANDLE_VALUE)
ReleaseActCtx(hActCtx);
}
LONG
ReadPerInstanceRegistryParameters(
IN HKEY hkeySvchost,
IN OUT PCOMMAND_OPTIONS pOptions
)
{
HKEY hkeySvchostGroup;
LONG lr;
// Read the value corresponding to this service group.
//
ASSERT (pOptions->ServiceGroupName);
lr = RegQueryString (
hkeySvchost,
pOptions->ServiceGroupName,
REG_MULTI_SZ,
&ServiceNames);
if (!lr && (!ServiceNames || !*ServiceNames))
{
lr = ERROR_INVALID_DATA;
}
// Read any per-instance parameters from the service group subkey
// if it exists.
//
if (!RegOpenKeyEx (
hkeySvchost,
pOptions->ServiceGroupName,
0, KEY_READ,
&hkeySvchostGroup))
{
DWORD dwValue;
if (!RegQueryDword (
hkeySvchostGroup,
TEXT("CoInitializeSecurityParam"),
&dwValue))
{
pOptions->dwCoInitializeSecurityParam = dwValue;
}
if (pOptions->dwCoInitializeSecurityParam)
{
if (!RegQueryDword (
hkeySvchostGroup,
TEXT("AuthenticationLevel"),
&dwValue))
{
pOptions->dwAuthLevel = dwValue;
}
else
{
pOptions->dwAuthLevel = RPC_C_AUTHN_LEVEL_PKT;
}
if (!RegQueryDword (
hkeySvchostGroup,
TEXT("ImpersonationLevel"),
&dwValue))
{
pOptions->dwImpersonationLevel = dwValue;
}
else
{
pOptions->dwImpersonationLevel = RPC_C_IMP_LEVEL_IDENTIFY;
}
if (!RegQueryDword (
hkeySvchostGroup,
TEXT("AuthenticationCapabilities"),
&dwValue))
{
pOptions->dwAuthCapabilities = dwValue;
}
else
{
pOptions->dwAuthCapabilities = EOAC_NO_CUSTOM_MARSHAL |
EOAC_DISABLE_AAA;
}
}
if (!RegQueryDword (
hkeySvchostGroup,
TEXT("DefaultRpcStackSize"),
&dwValue))
{
pOptions->dwDefaultRpcStackSize = dwValue;
}
if (!RegQueryDword (
hkeySvchostGroup,
TEXT("SystemCritical"),
&dwValue))
{
pOptions->fSystemCritical = dwValue;
}
RegCloseKey (hkeySvchostGroup);
}
return lr;
}
BOOL
CallPerInstanceInitFunctions(
IN OUT PCOMMAND_OPTIONS pOptions
)
{
if (pOptions->dwCoInitializeSecurityParam)
{
if (!InitializeSecurity(pOptions->dwCoInitializeSecurityParam,
pOptions->dwAuthLevel,
pOptions->dwImpersonationLevel,
pOptions->dwAuthCapabilities))
{
return FALSE;
}
}
if (pOptions->dwDefaultRpcStackSize)
{
RpcMgmtSetServerStackSize(pOptions->dwDefaultRpcStackSize * 1024);
}
else
{
//
// Make sure the default RPC stack size will be at least as
// large as the default thread stack size for the process so
// a random service calling RpcMgmtSetServerStackSize can't
// set it to a value that's too low, causing overflows.
//
PIMAGE_NT_HEADERS NtHeaders = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
if (NtHeaders != NULL)
{
RpcMgmtSetServerStackSize((ULONG) NtHeaders->OptionalHeader.SizeOfStackCommit);
}
}
if (pOptions->fSystemCritical)
{
//
// Ignore the return value
//
RtlSetProcessIsCritical(TRUE, NULL, TRUE);
}
return TRUE;
}
VOID
BuildServiceArray (
IN OUT PCOMMAND_OPTIONS pOptions
)
{
LONG lr;
HKEY hkeySvchost;
// Open the Svchost key.
//
lr = RegOpenKeyEx (
HKEY_LOCAL_MACHINE,
REGSTR_PATH_SVCHOST,
0, KEY_READ,
&hkeySvchost);
if (!lr)
{
lr = ReadPerInstanceRegistryParameters(hkeySvchost, pOptions);
RegCloseKey (hkeySvchost);
}
if (!lr)
{
PTSTR pszServiceName;
EnterCriticalSection (&ListLock);
// Count the number of service names read.
//
ServiceCount = 0;
for (pszServiceName = ServiceNames;
*pszServiceName;
pszServiceName += lstrlen(pszServiceName) + 1)
{
ServiceCount++;
}
ASSERT (ServiceCount);
// Allocate memory for the service array.
//
ServiceArray = MemAlloc (HEAP_ZERO_MEMORY,
sizeof (SERVICE) * ServiceCount);
if (ServiceArray)
{
PSERVICE pService;
// Initialize the service array.
//
pService = ServiceArray;
for (pszServiceName = ServiceNames;
*pszServiceName;
pszServiceName += lstrlen(pszServiceName) + 1)
{
pService->pszName = pszServiceName;
pService++;
}
ASSERT (pService == ServiceArray + ServiceCount);
}
LeaveCriticalSection (&ListLock);
}
}
// type of LPSERVICE_MAIN_FUNCTIONW
//
VOID
WINAPI
ServiceStarter(
DWORD argc,
PWSTR argv[]
)
{
LPSERVICE_MAIN_FUNCTION pfnServiceMain = NULL;
LPSVCHOST_PUSH_GLOBAL_FUNCTION pfnPushGlobals = NULL;
LPCWSTR pszwService = argv[0];
LPWSTR pszwAbort = NULL;
DWORD dwError = ERROR_FILE_NOT_FOUND;
EnterCriticalSection (&ListLock);
{
UINT i;
for (i = 0; i < ServiceCount; i++)
{
if (0 == lstrcmpi (pszwService, ServiceArray[i].pszName))
{
#if DBG
if (FDebugBreakForService (pszwService))
{
SVCHOST_LOG1(TRACE,
"Attaching debugger before getting ServiceMain for %ws...",
pszwService);
DebugBreak ();
}
#endif
GetServiceMainFunctions(&ServiceArray[i],
&pfnServiceMain,
&pfnPushGlobals,
&dwError);
if (pfnServiceMain && pfnPushGlobals && !g_pSvchostSharedGlobals)
{
SvchostBuildSharedGlobals();
}
pszwAbort = argv[0];
break;
}
}
}
LeaveCriticalSection (&ListLock);
if (pfnPushGlobals && g_pSvchostSharedGlobals)
{
pfnPushGlobals (g_pSvchostSharedGlobals);
if (pfnServiceMain)
{
SVCHOST_LOG1(TRACE,
"Calling ServiceMain for %ws...\n",
pszwService);
pfnServiceMain (argc, argv);
}
else if (pszwAbort)
{
AbortSvchostService(pszwAbort,
dwError);
}
}
else if (pfnServiceMain && !pfnPushGlobals)
{
SVCHOST_LOG1(TRACE,
"Calling ServiceMain for %ws...\n",
pszwService);
pfnServiceMain (argc, argv);
}
else if (pszwAbort)
{
AbortSvchostService(pszwAbort,
dwError);
}
}
LPSERVICE_TABLE_ENTRY
BuildServiceTable(
VOID
)
{
LPSERVICE_TABLE_ENTRY pServiceTable;
EnterCriticalSection (&ListLock);
// Allocate one extra entry and zero the entire range. The extra entry
// is the table terminator required by StartServiceCtrlDispatcher.
//
pServiceTable = MemAlloc (HEAP_ZERO_MEMORY,
sizeof (SERVICE_TABLE_ENTRY) * (ServiceCount + 1));
if (pServiceTable)
{
UINT i;
for (i = 0; i < ServiceCount; i++)
{
pServiceTable[i].lpServiceName = ServiceArray[i].pszName;
pServiceTable[i].lpServiceProc = ServiceStarter;
SVCHOST_LOG1(TRACE,
"Added service table entry for %ws\n",
pServiceTable[i].lpServiceName);
}
}
LeaveCriticalSection (&ListLock);
return pServiceTable;
}
PCOMMAND_OPTIONS
BuildCommandOptions (
LPCTSTR pszCommandLine
)
{
PCOMMAND_OPTIONS pOptions;
ULONG cbCommandLine;
if (pszCommandLine == NULL)
{
return NULL;
}
cbCommandLine = (lstrlen(pszCommandLine) + 1) * sizeof (TCHAR);
pOptions = MemAlloc (HEAP_ZERO_MEMORY,
sizeof (COMMAND_OPTIONS) + cbCommandLine);
if (pOptions)
{
TCHAR* pch;
TCHAR* pArgumentStart;
PTSTR* ppNextArgument = NULL;
pOptions->CommandLineBuffer = (PTSTR) (pOptions + 1);
RtlCopyMemory (
pOptions->CommandLineBuffer,
pszCommandLine,
cbCommandLine);
pch = pOptions->CommandLineBuffer;
ASSERT (pch);
// Skip the name of the executable.
//
pOptions->ImageName = pch;
while (*pch && (L' ' != *pch) && (L'\t' != *pch))
{
pch++;
}
if (*pch)
{
*pch++ = 0;
}
SvchostCharLowerW (pOptions->ImageName);
while (1)
{
// Skip whitespace.
//
while (*pch && ((L' ' == *pch) || (L'\t' == *pch)))
{
pch++;
}
// End of string?
//
if (!*pch)
{
break;
}
// Is it a '-' or '/' argument?
//
if (((L'-' == *pch) || (L'/' == *pch)) && *(++pch))
{
if ((L'k' == *pch) || (L'K' == *pch))
{
pOptions->fServiceGroup = TRUE;
ppNextArgument = &pOptions->ServiceGroupName;
}
pch++;
continue;
}
// This is the start of an argument.
//
pArgumentStart = pch;
// If the argument starts with a quote, skip it and scan to the
// next quote to terminate it.
//
if ((L'\"' == *pch) && *(++pch))
{
pArgumentStart = pch;
while (*pch && (L'\"' != *pch))
{
pch++;
}
}
// otherwise, skip to the next whitespace and this will be
// our argument.
//
else
{
while (*pch && (L' ' != *pch) && (L'\t' != *pch))
{
pch++;
}
}
if (*pch)
{
// terminate the newly found argument string.
//
*pch++ = 0;
}
if (ppNextArgument)
{
*ppNextArgument = pArgumentStart;
ppNextArgument = NULL;
}
}
pOptions->fServiceGroup = !!pOptions->ServiceGroupName;
SVCHOST_LOG1(TRACE,
"Command line : %ws\n",
pszCommandLine);
SVCHOST_LOG1(TRACE,
"Service Group : %ws\n",
(pOptions->fServiceGroup) ? pOptions->ServiceGroupName : L"No");
// Validate the options.
//
if (!pOptions->fServiceGroup)
{
SVCHOST_LOG2(TRACE,
"Generic Service Host\n\n"
"%ws [-k <key>] | [-r] | <service>\n\n"
" -k <key> Host all services whose ImagePath matches\n"
" %ws -k <key>.\n\n",
pOptions->CommandLineBuffer,
pOptions->CommandLineBuffer);
MemFree (pOptions);
pOptions = NULL;
}
}
return pOptions;
}
VOID
SvchostCharLowerW(
LPWSTR pszString
)
{
//
// LocalVersion of CharLower to avoid pulling in user32.dll
//
int cwchT;
DWORD cwch;
if (pszString == NULL)
{
return;
}
cwch = (DWORD) wcslen(pszString) + 1;
cwchT = LCMapStringW(LOCALE_USER_DEFAULT,
LCMAP_LOWERCASE,
pszString,
cwch,
pszString,
cwch);
if (cwchT == 0)
{
SVCHOST_LOG1(ERROR,
"SvchostCharLowerW failed for %ws\n",
pszString);
}
return;
}
LONG
WINAPI
SvchostUnhandledExceptionFilter(
struct _EXCEPTION_POINTERS *ExceptionInfo
)
{
return RtlUnhandledExceptionFilter(ExceptionInfo);
}
VOID
wmainCRTStartup (
VOID
)
{
LPSERVICE_TABLE_ENTRY pServiceTable = NULL;
PCOMMAND_OPTIONS pOptions;
PCWSTR pszwCommandLine;
SetUnhandledExceptionFilter(&SvchostUnhandledExceptionFilter);
// Prevent critical errors from raising hard error popups and
// halting svchost.exe. The flag below will have the system send
// the errors to the process instead.
//
SetErrorMode(SEM_FAILCRITICALERRORS);
// Initialize our HeapAlloc wrapper to use the process heap.
//
MemInit (GetProcessHeap());
// Initialize our global DLL list, Service array, and the critical
// section that protects them. InitializeCriticalSection can throw a
// STATUS_NO_MEMORY exception. We want the process to exit if that
// happens, so the default exception handler is fine.
//
InitializeListHead (&DllList);
InitializeCriticalSection (&ListLock);
// Build a COMMAND_OPTIONS structure and use it to grovel the registry
// and create the service entry table.
//
pszwCommandLine = GetCommandLine ();
pOptions = BuildCommandOptions (pszwCommandLine);
if (pOptions)
{
BuildServiceArray (pOptions);
pServiceTable = BuildServiceTable ();
if (pServiceTable)
{
if (!CallPerInstanceInitFunctions(pOptions))
{
SVCHOST_LOG0(ERROR,
"CallPerInstanceInitFunctions failed -- exiting!\n");
ExitProcess(1);
}
}
MemFree (pOptions);
}
// If we have a valid service entry table, use it to transfer control
// to the service controller. StartServiceCtrlDispatcher won't return
// until all services are stopped.
//
if (pServiceTable)
{
StartServiceCtrlDispatcher (pServiceTable);
}
SVCHOST_LOG1(TRACE,
"Calling ExitProcess for %ws\n",
pszwCommandLine);
ExitProcess (0);
}