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.
 
 
 
 
 
 

787 lines
19 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
Start.c
Abstract:
Process init and service controller interaction for dcomss.exe
Author:
Mario Goertzel [MarioGo]
Revision History:
MarioGo 06-14-95 Cloned from the old endpoint mapper.
MazharM 10-12.98 Add pnp stuff
TarunA 12-11-98 Removed pnpmngr.h
a-sergiv 25-08-99 Defined gC2Security for process-wide use
--*/
//#define NCADG_MQ_ON
#if !defined(_M_IA64)
#define SPX_ON
#endif
//#define IPX_ON
#if !defined(SPX_ON) && !defined(IPX_ON)
#define SPX_IPX_OFF
#endif
#include <dcomss.h>
#include <debnot.h>
#include <olesem.hxx>
#include <wtypes.h>
#include <objbase.h>
#include <winioctl.h>
#include <ntddndis.h>
#include <ndispnp.h>
#include <dbt.h>
#include <initguid.h>
#include <ndisguid.h>
#include <ndispnp.h>
#ifndef SPX_IPX_OFF
#include "sap.h"
#endif
#include "../../com/inc/secdes.hxx"
extern LONG g_bInSCM; // from catalog
C2Security gC2Security;
// Array of service status blocks and pointers to service control
// functions for each component service.
#define SERVICE_NAME L"RPCSS"
#define DEVICE_PREFIX L"\\\\.\\"
VOID WINAPI ServiceMain(DWORD, PWSTR[]);
extern BOOL CatalogDllMain (
HINSTANCE hInst,
DWORD dwReason,
LPVOID lpReserved
);
void NotifyCOMOnSuspend();
void NotifyCOMOnResume();
extern DWORD gLockTlsIdx;
SERVICE_TABLE_ENTRY gaServiceEntryTable[] = {
{ SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
{ NULL, NULL }
};
static SERVICE_STATUS gServiceStatus;
static SERVICE_STATUS_HANDLE ghServiceHandle;
#define OFFSET_TO_PTR(val, start) \
(val) = ((val) == NULL) ? NULL : (PWCHAR) ( (PCHAR)(val) + (ULONG_PTR)(start))
EXTERN_C const IID IID_IAssertConfig; // make the linker happy
void
CookupNodeId(PUCHAR NodeId)
/*++
Routine Description:
This routine is called when all else fails. Here we mix a bunch of
system parameters together for a 47bit node ID.
Arguments:
NodeId - Will be set to a value unlikly to be duplicated on another
machine. It is not guaranteed to be unique even on this machine.
But since UUIDs are (time + sequence) this is okay for
a local UUID.
It will be composed of:
The computer name.
The value of the performance counter.
The system memory status.
The total bytes and free bytes on C:
The stack pointer (value).
An LUID (locally unique ID)
Plus whatever random stuff was in the NodeId to begin with.
The NodeId returned is explicity made into a Multicast IEEE 802
address so that it will not conflict with a 'real' IEEE 802
based UUID.
--*/
{
unsigned char LocalNodeId[6]; // NOT initialized.
unsigned char ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
BOOL BoolResult;
ULONG i = MAX_COMPUTERNAME_LENGTH+1;
LARGE_INTEGER largeInt;
LUID luid;
ULONG UNALIGNED *NodeIdPart1 = (ULONG *)&LocalNodeId[0]; // Bytes 0 - 3
ULONG UNALIGNED *NodeIdPart2 = (ULONG *)&LocalNodeId[2]; // Bytes 2 - 5
MEMORYSTATUS memStatus;
ULONG SectorsPerCluster;
ULONG BytesPerSector;
ULONG TotalClusters;
ULONG FreeClusters;
// Initialize the LocalNodeId. Seventeen is the most random, random number.
memset(LocalNodeId, 17, sizeof(LocalNodeId));
// The computer name is xor'ed in until it runs out.
BoolResult =
GetComputerNameA((CHAR *)ComputerName, &i);
if (BoolResult)
{
unsigned char *p = ComputerName;
i = 0;
while(*p)
{
*( ((unsigned char *)LocalNodeId) + i) ^= *p++;
if (++i > 6)
{
i = 0;
}
}
}
else
{
#if DBG
DbgPrint ("GetComputerName failed - %d\n", GetLastError());
#endif
}
// The performance counter is xor'ed into the LocalNodeId.
BoolResult =
QueryPerformanceCounter(&largeInt);
if (BoolResult)
{
*NodeIdPart2 ^= largeInt.HighPart ^ largeInt.LowPart;
*NodeIdPart1 ^= largeInt.HighPart ^ largeInt.LowPart;
}
else
{
#if DBG
DbgPrint ("QueryPreformanceCount failed - %d\n", GetLastError());
#endif
}
// The current SP is xor'ed into both parts of the LocalNodeId.
*NodeIdPart1 ^= (ULONG_PTR)&LocalNodeId;
*NodeIdPart2 ^= (ULONG_PTR)&LocalNodeId;
// The memory status is Xor's into the LocalNodeId.
memStatus.dwLength = sizeof(MEMORYSTATUS);
GlobalMemoryStatus(&memStatus);
*NodeIdPart1 ^= memStatus.dwMemoryLoad;
*NodeIdPart2 ^= memStatus.dwTotalPhys;
*NodeIdPart1 ^= memStatus.dwAvailPhys;
*NodeIdPart1 ^= memStatus.dwTotalPageFile;
*NodeIdPart2 ^= memStatus.dwAvailPageFile;
*NodeIdPart2 ^= memStatus.dwTotalVirtual;
*NodeIdPart1 ^= memStatus.dwAvailVirtual;
// LUID's are good on this machine during this boot only.
BoolResult =
AllocateLocallyUniqueId(&luid);
if (BoolResult)
{
*NodeIdPart1 ^= luid.LowPart;
*NodeIdPart2 ^= luid.HighPart;
}
else
{
#if DBG
DbgPrint ("Status %d\n", GetLastError());
#endif
}
// Disk parameters and free space
BoolResult =
GetDiskFreeSpaceA("c:\\",
&SectorsPerCluster,
&BytesPerSector,
&FreeClusters,
&TotalClusters
);
if (BoolResult)
{
*NodeIdPart2 ^= TotalClusters * SectorsPerCluster * BytesPerSector;
*NodeIdPart1 ^= FreeClusters * SectorsPerCluster * BytesPerSector;
}
else
{
#if DBG
DbgPrint ("GetDiskFreeSpace failed - %d\n", GetLastError());
#endif
}
// Or in the 'multicast' bit to distinguish this NodeId
// from all other possible IEEE 802 addresses.
LocalNodeId[0] |= 0x80;
memcpy(NodeId, LocalNodeId, 6);
}
BOOLEAN
getMacAddress (
PUCHAR pMacAddress
)
/*++
Function Name:getMacAddress
Parameters:
Description:
Returns:
--*/
{
int i;
UINT fStatus;
int Size = 1024*5;
PNDIS_ENUM_INTF Interfaces;
UCHAR OidVendData[16];
Interfaces = (PNDIS_ENUM_INTF) I_RpcAllocate (Size);
if (Interfaces == 0)
{
return FALSE;
}
if (NdisEnumerateInterfaces(Interfaces, Size))
{
UINT i;
for (i = 0; i < Interfaces->TotalInterfaces; i++)
{
PUNICODE_STRING pDeviceName= &(Interfaces->Interface[i].DeviceName);
UCHAR PermMacAddr[6];
fStatus = NdisQueryHwAddress(pDeviceName, pMacAddress, PermMacAddr, &OidVendData[0]);
if (fStatus && (OidVendData[0] != 0xFF
|| OidVendData[1] != 0xFF
|| OidVendData[2] != 0xFF))
{
I_RpcFree (Interfaces);
return TRUE;
}
}
}
I_RpcFree (Interfaces);
return FALSE;
}
extern "C" void
DealWithDeviceEvent()
/*++
Function Name: DealWithDeviceEvent
Parameters:
Description:
Returns:
--*/
{
UCHAR MacAddress[8];
NTSTATUS NtStatus;
if (getMacAddress(&MacAddress[0]))
{
NtStatus = NtSetUuidSeed((PCHAR) &MacAddress[0]);
}
else
{
CookupNodeId(&MacAddress[0]);
ASSERT(MacAddress[0] & 0x80);
NtStatus = NtSetUuidSeed((PCHAR) &MacAddress[0]);
}
if (!NT_SUCCESS(NtStatus))
{
#if DBG
DbgPrint("NtSetUuidSeed failed\n", NtStatus);
#endif
}
#if !defined(SPX_IPX_OFF)
UpdateSap( SAP_CTRL_UPDATE_ADDRESS );
#endif
}
void
DealWithPowerStatusEvent(DWORD dwEvent)
{
switch (dwEvent)
{
//
// First the events we care about
//
case PBT_APMSUSPEND: // System is suspending operation.
NotifyCOMOnSuspend();
break;
case PBT_APMRESUMESUSPEND: // Operation resuming after suspension.
// This is the normal, user-initiated resume after a suspend.
NotifyCOMOnResume();
break;
case PBT_APMRESUMEAUTOMATIC: // Operation resuming automatically after event.
// For our purposes this is a regular resume, since we don't have any
// direct dialogue with the user. Eg, wake-on-lan might cause this.
NotifyCOMOnResume();
break;
case PBT_APMRESUMECRITICAL: // Operation resuming after critical suspension.
// This means we're resuming w/o previously having had a suspend
// notification. May have lost state (ie, ping set timers may have
// rundown). Let's process the resume anyway, so that the ping set
// timers start from scratch (might save somebody from a transient app
// error).
NotifyCOMOnResume();
break;
//
// And then the ones we don't care about.
//
case PBT_APMBATTERYLOW: // Battery power is low.
case PBT_APMOEMEVENT: // OEM-defined event occurred.
case PBT_APMPOWERSTATUSCHANGE: // Power status has changed.
case PBT_APMQUERYSUSPEND: // Request for permission to suspend.
case PBT_APMQUERYSUSPENDFAILED: // Suspension request denied.
break;
default:
ASSERT(!"Unexpected power event. Check to see if we should be dealing with it somehow.");
break;
}
return;
}
ULONG WINAPI
ServiceHandler(
DWORD dwCode,
DWORD dwEventType,
PVOID EventData,
PVOID pData
)
/*++
Routine Description:
Lowest level callback from the service controller to
cause this service to change our status. (stop, start, pause, etc).
Arguments:
opCode - One of the service "Controls" value.
SERVICE_CONTROL_{STOP, PAUSE, CONTINUE, INTERROGATE, SHUTDOWN}.
Return Value:
None
--*/
{
switch(dwCode) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
default:
#if DBG
DbgPrint("%S: Unexpected service control message %d.\n", SERVICE_NAME, dwCode);
#endif
ASSERT(0);
break;
case SERVICE_CONTROL_INTERROGATE:
// Service controller wants us to call SetServiceStatus.
UpdateState(gServiceStatus.dwCurrentState);
break ;
case SERVICE_CONTROL_SHUTDOWN:
// The machine is shutting down. We'll be killed once we return.
// Note, currently we don't register for these messages.
break;
case SERVICE_CONTROL_POWEREVENT:
DealWithPowerStatusEvent(dwEventType);
break;
}
return NO_ERROR;
}
VOID
UpdateState(
DWORD dwNewState
)
/*++
Routine Description:
Updates this services state with the service controller.
Arguments:
dwNewState - The next start for this service. One of
SERVICE_START_PENDING
SERVICE_RUNNING
Return Value:
None
--*/
{
DWORD status = ERROR_SUCCESS;
ASSERT( (dwNewState == SERVICE_RUNNING) ||
(gServiceStatus.dwCurrentState != SERVICE_RUNNING) );
switch (dwNewState)
{
case SERVICE_RUNNING:
case SERVICE_STOPPED:
gServiceStatus.dwCheckPoint = 0;
gServiceStatus.dwWaitHint = 0;
break;
case SERVICE_START_PENDING:
case SERVICE_STOP_PENDING:
++gServiceStatus.dwCheckPoint;
gServiceStatus.dwWaitHint = 30000L;
break;
default:
ASSERT(0);
status = ERROR_INVALID_SERVICE_CONTROL;
break;
}
if (status == ERROR_SUCCESS)
{
gServiceStatus.dwCurrentState = dwNewState;
if (!SetServiceStatus(ghServiceHandle, &gServiceStatus))
{
status = GetLastError();
}
}
#if DBG
if (status != ERROR_SUCCESS)
{
DbgPrint("%S: Failed to update service state: %d\n", SERVICE_NAME, status);
}
#endif
// We could return a status but how would we recover? Ignore it, the
// worst thing is that services will kill us and there's nothing
// we can about it if this call fails.
return;
}
VOID WINAPI
ServiceMain(
DWORD argc,
PWSTR argv[]
)
/*++
Routine Description:
Callback by the service controller when starting this service.
Arguments:
argc - number of arguments, usually 1
argv - argv[0] is the name of the service.
argv[>0] are arguments passed to the service.
Return Value:
None
--*/
{
DWORD status = ERROR_SUCCESS;
// COM needs power standby\resume events
const DWORD RPCSS_CONTROLS = SERVICE_ACCEPT_POWEREVENT;
// set the initial stack to 12K. This ensures enough commit
// so that server threads don't need to extend their stacks
RpcMgmtSetServerStackSize(3 * 4096);
DealWithDeviceEvent();
ASSERT( (argc >= 1 && lstrcmpiW(argv[0], SERVICE_NAME) == 0)
|| (argc == 0 && argv == 0));
#if DBG==1
// Note that we've completed running the static constructors
ASSERT(g_fDllState == DLL_STATE_STATIC_CONSTRUCTING);
g_fDllState = DLL_STATE_NORMAL;
#endif
// Initialize the mutex package
status = RtlInitializeCriticalSection(&g_OleMutexCreationSem);
if (!NT_SUCCESS(status))
return;
// Initialize TLS
gLockTlsIdx = TlsAlloc();
if (gLockTlsIdx == -1)
{
return;
}
// Initialize catalog
CatalogDllMain (NULL, DLL_PROCESS_ATTACH, NULL);
{ // Create a named event for Vista to toggle event logging
// Check to see if the event logging is installed and enabled
//
long cb = 128;
char szValue[128];
if (RegQueryValueA(HKEY_CLASSES_ROOT,
"CLSID\\{6C736DB0-BD94-11D0-8A23-00AA00B58E10}\\EnableEvents",
szValue, &cb) == ERROR_SUCCESS && !strcmp(szValue, "1"))
{
// [Sergei O. Ivanov (a-sergiv), 28-Jun-99]
// This code here used to grant access to the whole world. But the world
// tends to try and mess you up. So we'll only grant them limited access.
SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_WORLD_SID_AUTHORITY;
PSID pSidEveryone = NULL;
PACL pAcl = NULL;
DWORD cbAcl = 0;
ACCESS_ALLOWED_ACE *pAce = NULL;
AllocateAndInitializeSid(&SidAuthority, 1, SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0, &pSidEveryone);
if(pSidEveryone)
{
cbAcl = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + GetLengthSid(pSidEveryone);
pAcl = (PACL) LocalAlloc(LMEM_FIXED, cbAcl);
if(pAcl)
{
InitializeAcl(pAcl, cbAcl, ACL_REVISION);
AddAccessAllowedAce(pAcl, ACL_REVISION, EVENT_QUERY_STATE|EVENT_MODIFY_STATE|SYNCHRONIZE|READ_CONTROL, pSidEveryone);
}
}
// Create security descriptor and attach the DACL to it
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = &sd;
// The toggle event, which is signaled by LEC and checked by all.
// The handle never closes during the process
HANDLE hLeak1 = CreateEventA(
&sa, /* Security */
TRUE, /* Manual reset */
FALSE, /* InitialState is non-signaled */
"MSFT.VSA.IEC.STATUS.6c736db0" /* Name */
);
if(pSidEveryone) FreeSid(pSidEveryone);
if(pAcl) LocalFree(pAcl);
}
}
// Tell catalog it's running in SCM
g_bInSCM = TRUE;
gServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
gServiceStatus.dwCurrentState = SERVICE_START_PENDING;
gServiceStatus.dwControlsAccepted = RPCSS_CONTROLS;
gServiceStatus.dwWin32ExitCode = 0;
gServiceStatus.dwServiceSpecificExitCode = 0;
gServiceStatus.dwCheckPoint = 0;
gServiceStatus.dwWaitHint = 3000L;
ghServiceHandle = RegisterServiceCtrlHandlerEx(SERVICE_NAME,
ServiceHandler,
UIntToPtr(0xCAFECAFE) );
if (0 == ghServiceHandle)
{
status = GetLastError();
ASSERT(status != ERROR_SUCCESS);
}
if (status == ERROR_SUCCESS)
{
UpdateState(SERVICE_START_PENDING);
}
if (status == ERROR_SUCCESS)
{
// epts.c
status = InitializeEndpointManager();
}
#ifndef _CHICAGO_
// Start Ep Mapper.
if (status == ERROR_SUCCESS)
{
// ..\epmap\server.c
UpdateState(SERVICE_START_PENDING);
status = StartEndpointMapper();
}
#ifdef NCADG_MQ_ON
// Start MQ Manager Interface
if (status == ERROR_SUCCESS)
{
UpdateState(SERVICE_START_PENDING);
status = StartMqManagement();
}
#endif // NCADG_MQ_ON
#endif
// Do pre-listen olescm initialization
if (status == ERROR_SUCCESS)
{
UpdateState(SERVICE_START_PENDING);
status = InitializeSCMBeforeListen();
}
// Start object resolver
if (status == ERROR_SUCCESS)
{
// ..\objex\objex.cxx
UpdateState(SERVICE_START_PENDING);
status = StartObjectExporter();
}
// Start OLESCM
if (status == ERROR_SUCCESS)
{
UpdateState(SERVICE_START_PENDING);
status = InitializeSCM();
}
// Start listening for RPC requests
if (status == ERROR_SUCCESS)
{
status = RpcServerListen(1, 1234, TRUE);
if (status == RPC_S_OK)
{
while (RpcMgmtIsServerListening(0) == RPC_S_NOT_LISTENING)
{
Sleep(100);
}
}
}
//
// There is some initialization that must be done after we
// have done the RpcServerListen.
//
if (status == ERROR_SUCCESS)
{
// ..\olescm\scmsvc.cxx
UpdateState(SERVICE_START_PENDING);
InitializeSCMAfterListen();
}
// Trim our working set - free space now at the cost of time later.
if (status == ERROR_SUCCESS)
{
UpdateState(SERVICE_RUNNING);
}
#ifdef DEBUGRPC
if (status != ERROR_SUCCESS)
{
DbgPrint("RPCSS ServiceMain failed %d (%08x)\n", status, status);
}
#endif
if (status == ERROR_SUCCESS)
{
ObjectExporterWorkerThread(0);
ASSERT(0);
}
return;
}
extern HRESULT PrivGetRPCSSInfo(REFCLSID rclsid, REFIID riid, void** pIntf);
extern "C"
{
STDAPI GetRPCSSInfo(REFCLSID rclsid, REFIID riid, void** ppv)
{
return PrivGetRPCSSInfo(rclsid, riid, ppv);
};
STDAPI GetCatalogHelper(REFIID riid, void** ppv);
STDAPI CoGetComCatalog(REFIID riid, void** ppv)
{
return GetCatalogHelper (riid, ppv);
}
}