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.
2255 lines
58 KiB
2255 lines
58 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Process.cxx
|
|
|
|
Abstract:
|
|
|
|
Process objects represent local clients and servers. These
|
|
objects live as context handles.
|
|
|
|
There are relatively few of these objects in the universe.
|
|
|
|
Author:
|
|
|
|
Mario Goertzel [MarioGo]
|
|
|
|
Revision History:
|
|
|
|
MarioGo 02-20-95 Bits 'n pieces
|
|
Ronans 20-02-97 UseProtseqIfNeeded modified for custom endpoints
|
|
Ronans 20-02-97 Added custom endpoints stuff to process object
|
|
TarunA 09-Nov-98 Added process handle
|
|
|
|
--*/
|
|
|
|
#include <or.hxx>
|
|
|
|
CRITICAL_SECTION gcsFastProcessLock;
|
|
extern HRESULT FreeSPIFromCProcess(void** ppSCMProcessInfo);
|
|
|
|
const DWORD BINDINGUPDATESTUFF_SIG = 0xFEDCBA01;
|
|
|
|
typedef struct _BINDINGS_UPDATE_CALLBACK_STUFF
|
|
{
|
|
DWORD dwSig; // see BINDINGUPDATESTUFF_SIG above
|
|
|
|
// housekeeping stuff
|
|
CProcess* pProcess; // has reference while call is in-flight
|
|
RPC_BINDING_HANDLE hBinding;
|
|
RPC_ASYNC_STATE async;
|
|
|
|
// out and in-out params
|
|
DWORD64 dwBindingsID;
|
|
DUALSTRINGARRAY* pdsaNewBindings;
|
|
DUALSTRINGARRAY* pdsaNewSecurity;
|
|
} BINDINGS_UPDATE_CALLBACK_STUFF;
|
|
|
|
|
|
const DWORD ASYNCRUNDOWNOID_SIG = 0xFEDCBA02;
|
|
|
|
typedef struct _ASYNCRUNDOWNOID_STUFF
|
|
{
|
|
DWORD dwSig; // see ASYNCRUNDOWNOID_SIG above
|
|
|
|
// housekeeping stuff
|
|
CProcess* pProcess; // has reference while call is in-flight
|
|
CServerOxid* pOxid; // has reference while call is in-flight
|
|
RPC_BINDING_HANDLE hBinding;
|
|
RPC_ASYNC_STATE async;
|
|
|
|
// We keep these for when we process the return
|
|
ULONG cOids;
|
|
CServerOid* aOids[MAX_OID_RUNDOWNS_PER_CALL];
|
|
|
|
// The ORPC params are reference pointers, so they must
|
|
// stay alive for the life of the call
|
|
ORPCTHIS orpcthis;
|
|
LOCALTHIS localthis;
|
|
ORPCTHAT orpcthat;
|
|
|
|
INT callIDHint; // need to free this on return
|
|
|
|
// in-out or out params.
|
|
BYTE aRundownStatus[MAX_OID_RUNDOWNS_PER_CALL];
|
|
} ASYNCRUNDOWNOID_STUFF;
|
|
|
|
|
|
void
|
|
CProcess::Rundown()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The client process has rundown. This means there are no more
|
|
client refernces which means we are free to clean things up
|
|
as long as server OXIDs still holding references won't get
|
|
upset. They all use the server lock when accessing the process.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ORSTATUS status;
|
|
|
|
gpServerLock->LockExclusive();
|
|
|
|
ASSERT(_cClientReferences == 0);
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"OR: Rundown of %p: %d oxids, %d oids and %d roxids left\n",
|
|
this,
|
|
_blistOxids.Size(),
|
|
_blistOids.Size()));
|
|
|
|
// Release any OXIDs owned by this process. This may destroy the OXID.
|
|
// This will release this CProcess, but won't release the last reference as
|
|
// the client process still owns one.
|
|
|
|
|
|
if (_blistOxids.Size())
|
|
{
|
|
CServerOxid *poxid;
|
|
|
|
CBListIterator oxids(&_blistOxids);
|
|
|
|
while (poxid = (CServerOxid *)oxids.Next())
|
|
{
|
|
gpServerOxidTable->Remove(poxid);
|
|
poxid->ProcessRelease();
|
|
}
|
|
}
|
|
|
|
// Release any OIDs is use by this processes.
|
|
|
|
// Do this now, rather then waiting for the last server oid
|
|
// owned by this process to get invalidated and rundown.
|
|
|
|
gpClientLock->LockExclusive();
|
|
|
|
// *** Both client and server lock held here. ***
|
|
|
|
if (_blistOids.Size())
|
|
{
|
|
CClientOid *poid;
|
|
|
|
CBListIterator oids(&_blistOids);
|
|
|
|
while (poid = (CClientOid *)oids.Next())
|
|
{
|
|
ASSERT(NULL != poid->GetClientOxid());
|
|
|
|
poid->GetClientOxid()->Release();
|
|
|
|
poid->ClientRelease();
|
|
}
|
|
}
|
|
|
|
// Cleanup other process state. Note: it is important that the
|
|
// release of the process and token handle never happen any later
|
|
// than rundown time. If the process handle is released any later, you
|
|
// will see bugs like "can't recompile my com server" after they run
|
|
// it once. If the token handle is released any later, you will get
|
|
// security bugs from the NT security folks since we will hold onto
|
|
// the logged-on user's token until many minutes after logoff.
|
|
//
|
|
// Ask me how I know this...
|
|
//
|
|
if (_hProcHandle)
|
|
{
|
|
CloseHandle(_hProcHandle);
|
|
_hProcHandle = NULL;
|
|
}
|
|
|
|
if (_pToken)
|
|
{
|
|
_pToken->Release();
|
|
_pToken = 0;
|
|
}
|
|
|
|
// Free the cached SCMProcessInfo if we have one
|
|
FreeSPIFromCProcess(&_pSCMProcessInfo);
|
|
|
|
// Flip the dirty bit
|
|
FlagsOn(PROCESS_SPI_DIRTY);
|
|
|
|
// Cleanup the ROT entries associated with this process outside the lock.
|
|
// NULL the list first, though.
|
|
void *pvFirstROT = _pvFirstROTEntry;
|
|
_pvFirstROTEntry = NULL;
|
|
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
if (_cDropTargets != 0) // check if we have any outstanding droptargets to clean up
|
|
DragDropRunDown(this);
|
|
|
|
// Done, release the clients' reference, this may actually delete this
|
|
// process. (If an OXID still exists and has OIDs it will not be deleted
|
|
// until the OIDs all rundown).
|
|
|
|
this->Release();
|
|
|
|
// The this pointer maybe invalid now.
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
|
|
// Now actually cleanup the ROT entries.
|
|
// Do this outside of the server lock, to avoid deadlocks.
|
|
SCMCleanupROTEntries(pvFirstROT);
|
|
}
|
|
|
|
|
|
CProcess::CProcess(
|
|
IN CToken*& pToken,
|
|
IN WCHAR *pwszWinstaDesktop,
|
|
IN DWORD procID,
|
|
OUT ORSTATUS &status
|
|
) :
|
|
_blistOxids(4),
|
|
_blistOids(16),
|
|
_listClasses()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initalized a process object, members and add it to the
|
|
process list.
|
|
|
|
Arguments:
|
|
|
|
pToken - The clients token. We assume we have a reference.
|
|
|
|
pwszWinstaDesktop - The client's windowstation/desktop string.
|
|
|
|
procID - The client's process ID.
|
|
|
|
status - Sometimes the C'tor can fail with OR_NOMEM.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
_cClientReferences = 1;
|
|
_hProcess = NULL;
|
|
_fCacheFree = FALSE;
|
|
_pdsaLocalBindings = NULL;
|
|
_pdsaRemoteBindings = NULL;
|
|
_pToken = NULL;
|
|
_pwszWinstaDesktop = NULL;
|
|
_pScmProcessReg = NULL;
|
|
_pvRunAsHandle = NULL;
|
|
_procID = 0;
|
|
_hProcHandle = NULL;
|
|
_dwFlags = PROCESS_SPI_DIRTY; // always start out dirty
|
|
_pSCMProcessInfo = NULL;
|
|
_ulClasses = 0;
|
|
_dwCurrentBindingsID = 0;
|
|
_dwAsyncUpdatesOutstanding = 0;
|
|
_pvFirstROTEntry = NULL;
|
|
_cDropTargets = 0;
|
|
|
|
// Store time of object creation. Note that this is in UTC time.
|
|
GetSystemTimeAsFileTime(&_ftCreated);
|
|
|
|
// Generate a unique guid to represent this process
|
|
UuidCreate(&_guidProcessIdentifier);
|
|
|
|
// ronans - entries for custom protseqs from server
|
|
// not used for clients
|
|
_fReadCustomProtseqs = FALSE;
|
|
_pdsaCustomProtseqs = NULL;
|
|
|
|
status = OR_OK;
|
|
if ( pwszWinstaDesktop == NULL )
|
|
{
|
|
status = OR_BADPARAM;
|
|
}
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
_pwszWinstaDesktop = new WCHAR[OrStringLen(pwszWinstaDesktop)+1];
|
|
if (! _pwszWinstaDesktop)
|
|
status = OR_NOMEM;
|
|
else
|
|
OrStringCopy(_pwszWinstaDesktop, pwszWinstaDesktop);
|
|
}
|
|
|
|
if (status == OR_OK)
|
|
{
|
|
_pToken = pToken;
|
|
pToken = NULL; // We've taken the token
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
{
|
|
// Need to check that this is truly a new registration
|
|
// before we add it to the list
|
|
gpProcessListLock->LockExclusive();
|
|
if (VerifyNewProcess(procID))
|
|
{
|
|
status = gpProcessList->Insert(this);
|
|
}
|
|
else
|
|
{
|
|
// bad baby bad
|
|
status = OR_NOACCESS;
|
|
ASSERT(!"Possible bogus caller");
|
|
}
|
|
gpProcessListLock->UnlockExclusive();
|
|
}
|
|
|
|
_procID = procID;
|
|
|
|
#if DBG
|
|
_cRundowns = 0;
|
|
#endif
|
|
}
|
|
|
|
CProcess::~CProcess(void)
|
|
// You probably should be looking in the ::Rundown method.
|
|
// This process object stays alive until the last server oxid dies.
|
|
{
|
|
ASSERT(gpServerLock->HeldExclusive());
|
|
ASSERT(_hProcHandle == 0);
|
|
ASSERT(_pSCMProcessInfo == 0);
|
|
|
|
delete _pdsaLocalBindings;
|
|
delete _pdsaRemoteBindings;
|
|
MIDL_user_free( _pdsaCustomProtseqs );
|
|
|
|
delete _pwszWinstaDesktop;
|
|
|
|
if (_hProcess)
|
|
{
|
|
RPC_STATUS status = RpcBindingFree(&_hProcess);
|
|
ASSERT(status == RPC_S_OK);
|
|
ASSERT(_hProcess == 0);
|
|
}
|
|
|
|
extern void RunAsRelease(void*);
|
|
RunAsRelease(_pvRunAsHandle);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// SetSCMProcessInfo
|
|
//
|
|
// Swaps our old cached SCMProcessInfo* for a new one.
|
|
//
|
|
HRESULT CProcess::SetSCMProcessInfo(void* pSPI)
|
|
{
|
|
ASSERT(gpServerLock->HeldExclusive());
|
|
ASSERT(!(_dwFlags & PROCESS_RUNDOWN));
|
|
|
|
if (_dwFlags & PROCESS_RUNDOWN)
|
|
return E_UNEXPECTED;
|
|
|
|
FreeSPIFromCProcess(&_pSCMProcessInfo);
|
|
|
|
// set the new one
|
|
_pSCMProcessInfo = pSPI;
|
|
|
|
// this means we're no longer dirty
|
|
FlagsOff(PROCESS_SPI_DIRTY);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void CProcess::SetProcessReadyState(DWORD dwState)
|
|
{
|
|
ASSERT(_pScmProcessReg);
|
|
|
|
gpServerLock->LockExclusive();
|
|
|
|
_pScmProcessReg->ReadinessStatus = dwState;
|
|
|
|
// we're now dirty
|
|
FlagsOn(PROCESS_SPI_DIRTY);
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
}
|
|
|
|
|
|
void CProcess::Retire()
|
|
{
|
|
// A process can (or should be) only retired once
|
|
ASSERT(!IsRetired());
|
|
|
|
// Mark ourselves as retired
|
|
FlagsOn(PROCESS_RETIRED | PROCESS_SPI_DIRTY);
|
|
}
|
|
|
|
|
|
void CProcess::SetRunAsHandle(void *pvRunAsHandle)
|
|
{
|
|
ASSERT(!_pvRunAsHandle);
|
|
_pvRunAsHandle = pvRunAsHandle;
|
|
}
|
|
|
|
BOOL CProcess::SetProcessHandle(HANDLE hProcHandle, DWORD dwLaunchedPID)
|
|
{
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Store the handle of the process only if the process launched by
|
|
us and the process registering back are the same. Otherwise
|
|
we might kill a process not launched by us on receiving certain
|
|
error conditions (notably RPC_E_SERVERFAULT)
|
|
|
|
Arguments:
|
|
|
|
hProcHandle - Handle of the process launched
|
|
|
|
dwLaunchedPID - PID of the process launched
|
|
|
|
Return Value:
|
|
|
|
TRUE - If handle is set
|
|
FALSE - otherwise
|
|
|
|
--*/
|
|
|
|
BOOL fRet = FALSE;
|
|
if (dwLaunchedPID == _procID)
|
|
{
|
|
|
|
// if the server registers the same class twice, this handle will be non-zero if the
|
|
// the waiting activation thread has not NULL'd the CServerTableEntry's _hProcess yet.
|
|
if (_hProcHandle)
|
|
{
|
|
CloseHandle(_hProcHandle);
|
|
_hProcHandle = 0;
|
|
}
|
|
// This should fail only we are out of pool.
|
|
// If this fails, we cannot kill off servers we started
|
|
// nor can we test them for deadness.
|
|
if ( (fRet = DuplicateHandle(GetCurrentProcess(), hProcHandle,
|
|
GetCurrentProcess(), &_hProcHandle,
|
|
0, FALSE, DUPLICATE_SAME_ACCESS)) == 0)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: DuplicateHandle failed 0x%x\n", GetLastError()));
|
|
}
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
RPC_STATUS
|
|
CProcess::ProcessBindings(
|
|
IN DUALSTRINGARRAY *pdsaStringBindings,
|
|
IN DUALSTRINGARRAY *pdsaSecurityBindings
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates the string and optionally the security
|
|
bindings associated with this process.
|
|
|
|
Arguments:
|
|
|
|
psaStringBindings - The expanded string bindings of the process
|
|
|
|
psaSecurityBindings - compressed security bindings of the process.
|
|
If NULL, the current security bindings are reused.
|
|
|
|
Environment:
|
|
|
|
Server lock held during call or called from an OXID with an extra
|
|
reference owned by the process and keeping this process alive.
|
|
|
|
Return Value:
|
|
|
|
OR_NOMEM - unable to allocate storage for the new string arrays.
|
|
|
|
OR_OK - normally.
|
|
|
|
--*/
|
|
{
|
|
CMutexLock lock(&gcsFastProcessLock);
|
|
USHORT wSecSize;
|
|
PWSTR pwstrSecPointer;
|
|
|
|
// NULL security bindings means we should use the existing bindings.
|
|
if (0 == pdsaSecurityBindings)
|
|
{
|
|
ASSERT(_pdsaLocalBindings);
|
|
wSecSize = _pdsaLocalBindings->wNumEntries - _pdsaLocalBindings->wSecurityOffset;
|
|
pwstrSecPointer = _pdsaLocalBindings->aStringArray
|
|
+ _pdsaLocalBindings->wSecurityOffset;
|
|
}
|
|
else
|
|
{
|
|
wSecSize = pdsaSecurityBindings->wNumEntries - pdsaSecurityBindings->wSecurityOffset;
|
|
pwstrSecPointer = &pdsaSecurityBindings->aStringArray[pdsaSecurityBindings->wSecurityOffset];
|
|
}
|
|
|
|
DUALSTRINGARRAY *pdsaT = CompressStringArrayAndAddIPAddrs(pdsaStringBindings);
|
|
if (!pdsaT)
|
|
{
|
|
return(OR_NOMEM);
|
|
}
|
|
|
|
// ignore security on string binding parameter
|
|
pdsaT->wNumEntries = pdsaT->wSecurityOffset;
|
|
|
|
DUALSTRINGARRAY *pdsaResult = new((pdsaT->wNumEntries + wSecSize) * sizeof(WCHAR)) DUALSTRINGARRAY;
|
|
|
|
if (0 == pdsaResult)
|
|
{
|
|
delete pdsaT;
|
|
return(OR_NOMEM);
|
|
}
|
|
|
|
pdsaResult->wNumEntries = pdsaT->wNumEntries + wSecSize;
|
|
pdsaResult->wSecurityOffset = pdsaT->wSecurityOffset;
|
|
|
|
OrMemoryCopy(pdsaResult->aStringArray,
|
|
pdsaT->aStringArray,
|
|
pdsaT->wSecurityOffset*sizeof(WCHAR));
|
|
|
|
OrMemoryCopy(pdsaResult->aStringArray + pdsaResult->wSecurityOffset,
|
|
pwstrSecPointer,
|
|
wSecSize*sizeof(WCHAR));
|
|
|
|
ASSERT(dsaValid(pdsaResult));
|
|
|
|
delete pdsaT;
|
|
|
|
delete _pdsaLocalBindings;
|
|
_pdsaLocalBindings = pdsaResult;
|
|
|
|
delete _pdsaRemoteBindings;
|
|
_pdsaRemoteBindings = 0;
|
|
|
|
return(RPC_S_OK);
|
|
}
|
|
|
|
DUALSTRINGARRAY *
|
|
CProcess::GetLocalBindings(void)
|
|
// Server lock held or called within an
|
|
// OXID with an extra reference.
|
|
{
|
|
CMutexLock lock(&gcsFastProcessLock);
|
|
|
|
if (0 == _pdsaLocalBindings)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
DUALSTRINGARRAY *T = (DUALSTRINGARRAY *)MIDL_user_allocate(sizeof(DUALSTRINGARRAY)
|
|
+ sizeof(USHORT) * _pdsaLocalBindings->wNumEntries);
|
|
|
|
if (0 != T)
|
|
{
|
|
dsaCopy(T, _pdsaLocalBindings);
|
|
}
|
|
|
|
return(T);
|
|
}
|
|
|
|
DUALSTRINGARRAY *
|
|
CProcess::GetRemoteBindings(void)
|
|
// Server lock held.
|
|
{
|
|
CMutexLock lock(&gcsFastProcessLock);
|
|
|
|
ORSTATUS Status;
|
|
|
|
if (0 == _pdsaRemoteBindings)
|
|
{
|
|
if (0 == _pdsaLocalBindings)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
Status = ConvertToRemote(_pdsaLocalBindings, &_pdsaRemoteBindings);
|
|
|
|
if (Status != OR_OK)
|
|
{
|
|
ASSERT(Status == OR_NOMEM);
|
|
return(0);
|
|
}
|
|
ASSERT(dsaValid(_pdsaRemoteBindings));
|
|
}
|
|
|
|
DUALSTRINGARRAY *T = (DUALSTRINGARRAY *)MIDL_user_allocate(sizeof(DUALSTRINGARRAY)
|
|
+ sizeof(USHORT) * _pdsaRemoteBindings->wNumEntries);
|
|
|
|
if (0 != T)
|
|
{
|
|
dsaCopy(T, _pdsaRemoteBindings);
|
|
}
|
|
|
|
ASSERT(dsaValid(T));
|
|
return(T);
|
|
}
|
|
|
|
|
|
ORSTATUS
|
|
CProcess::AddOxid(CServerOxid *pOxid)
|
|
{
|
|
ASSERT(gpServerLock->HeldExclusive());
|
|
|
|
pOxid->Reference();
|
|
|
|
ASSERT(_blistOxids.Member(pOxid) == FALSE);
|
|
|
|
ORSTATUS status = _blistOxids.Insert(pOxid);
|
|
|
|
if (status != OR_OK)
|
|
{
|
|
pOxid->ProcessRelease();
|
|
return(status);
|
|
}
|
|
|
|
gpServerOxidTable->Add(pOxid);
|
|
|
|
return(OR_OK);
|
|
}
|
|
|
|
BOOL
|
|
CProcess::RemoveOxid(CServerOxid *poxid)
|
|
{
|
|
ASSERT(gpServerLock->HeldExclusive());
|
|
|
|
CServerOxid *pit = (CServerOxid *)_blistOxids.Remove(poxid);
|
|
|
|
if (pit)
|
|
{
|
|
ASSERT(pit == poxid);
|
|
gpServerOxidTable->Remove(poxid);
|
|
poxid->ProcessRelease();
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
CProcess::IsOwner(CServerOxid *poxid)
|
|
{
|
|
ASSERT(gpServerLock->HeldExclusive());
|
|
|
|
return(_blistOxids.Member(poxid));
|
|
}
|
|
|
|
ORSTATUS
|
|
CProcess::AddOid(CClientOid *poid)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a new oid to the list of OIDs owned by this process and
|
|
increments the reference count of the associated OXID
|
|
|
|
Arguments:
|
|
|
|
poid - the oid to add. It's reference is transferred to this
|
|
function. If this function fails, it must dereference the oid.
|
|
The caller passed a client reference to this process. The
|
|
process must eventually call ClientRelease() on the parameter.
|
|
|
|
Return Value:
|
|
|
|
OR_OK - normally
|
|
|
|
OR_NOMEM - out of memory.
|
|
|
|
--*/
|
|
|
|
{
|
|
ORSTATUS status;
|
|
|
|
ASSERT(gpClientLock->HeldExclusive());
|
|
|
|
status = _blistOids.Insert(poid);
|
|
|
|
if (status != OR_OK)
|
|
{
|
|
ASSERT(status == OR_NOMEM);
|
|
poid->ClientRelease();
|
|
}
|
|
else
|
|
{
|
|
ASSERT(NULL != poid->GetClientOxid());
|
|
poid->GetClientOxid()->Reference();
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
|
|
CClientOid *
|
|
CProcess::RemoveOid(CClientOid *poid)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes an OID from this list of OID in use by this process.
|
|
|
|
Arguments:
|
|
|
|
poid - The OID to remove.
|
|
|
|
Return Value:
|
|
|
|
non-zero - the pointer actually remove. (ASSERT(retval == poid))
|
|
It will be released by the process before return,
|
|
so you should not use the pointer unless you know you
|
|
have another reference.
|
|
|
|
0 - not in the list
|
|
|
|
--*/
|
|
|
|
{
|
|
ASSERT(gpClientLock->HeldExclusive());
|
|
|
|
CClientOid *pit = (CClientOid *)_blistOids.Remove(poid);
|
|
|
|
if (pit)
|
|
{
|
|
ASSERT(pit == poid);
|
|
|
|
pit->ClientRelease();
|
|
|
|
ASSERT(NULL != poid->GetClientOxid());
|
|
|
|
poid->GetClientOxid()->Release();
|
|
|
|
return(pit);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
void
|
|
CProcess::AddClassReg(GUID & Guid, DWORD Reg)
|
|
{
|
|
CClassReg * pReg;
|
|
|
|
pReg = new CClassReg( Guid, Reg );
|
|
|
|
if (pReg)
|
|
{
|
|
gpServerLock->LockExclusive();
|
|
|
|
_listClasses.Insert( pReg );
|
|
|
|
// flip the dirty bit
|
|
FlagsOn(PROCESS_SPI_DIRTY);
|
|
|
|
_ulClasses++;
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
}
|
|
}
|
|
|
|
void
|
|
CProcess::RemoveClassReg(DWORD Reg)
|
|
{
|
|
CClassReg * pReg;
|
|
|
|
gpServerLock->LockExclusive();
|
|
|
|
pReg = (CClassReg *)_listClasses.First();
|
|
|
|
while ( (pReg != NULL) && (pReg->_Reg != Reg) )
|
|
pReg = (CClassReg *)pReg->Next();
|
|
|
|
if (pReg)
|
|
{
|
|
(void)_listClasses.Remove( pReg );
|
|
delete pReg;
|
|
|
|
// flip the dirty bit
|
|
FlagsOn(PROCESS_SPI_DIRTY);
|
|
|
|
_ulClasses--;
|
|
}
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
}
|
|
|
|
void
|
|
CProcess::Cleanup()
|
|
{
|
|
SCMProcessCleanup(this);
|
|
}
|
|
|
|
void
|
|
CProcess::RevokeClassRegs()
|
|
{
|
|
|
|
if (_pScmProcessReg)
|
|
{
|
|
// This is a unified surrogate (COM+) server
|
|
SCMRemoveRegistration(_pScmProcessReg);
|
|
_pScmProcessReg = NULL;
|
|
}
|
|
|
|
// This is for legacy local or custom surrogate servers -- however,
|
|
// nothing prevents someone from calling CoRegisterClassObject in
|
|
// user code even in a COM+ (surrogate) server
|
|
|
|
CClassReg * pReg;
|
|
|
|
// This is only called during rundown so we don't have to take a lock.
|
|
|
|
while ( (pReg = (CClassReg *)_listClasses.First()) != 0 )
|
|
{
|
|
(void)_listClasses.Remove((CListElement *)pReg);
|
|
SCMRemoveRegistration( this,
|
|
pReg->_Guid,
|
|
pReg->_Reg );
|
|
delete pReg;
|
|
}
|
|
}
|
|
|
|
|
|
void CProcess::SetProcessReg(ScmProcessReg *pProcessReg)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by SCM to set COM+ process registration.
|
|
This is also used as a cache during the startup protocol
|
|
to query and set the readiness state of the server process.
|
|
There is exactly one such registration per COM+ server process.
|
|
|
|
CODEWORK: these should be inlined
|
|
|
|
Arguments:
|
|
|
|
registration struct
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
gpServerLock->LockExclusive();
|
|
|
|
_pScmProcessReg = pProcessReg;
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
}
|
|
|
|
ScmProcessReg* CProcess::GetProcessReg()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by SCM to lookup COM+ process registration.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
registration struct, if any.
|
|
|
|
--*/
|
|
{
|
|
gpServerLock->LockShared();
|
|
|
|
ScmProcessReg *pResult = _pScmProcessReg;
|
|
|
|
gpServerLock->UnlockShared();
|
|
|
|
return pResult;
|
|
}
|
|
|
|
ScmProcessReg*
|
|
CProcess::RemoveProcessReg()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called by SCM when COM+ process revokes its activator registration.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
gpServerLock->LockExclusive();
|
|
|
|
ScmProcessReg *pResult = _pScmProcessReg;
|
|
|
|
// Even if this process was a new-style surrogate process, _pScmProcessReg
|
|
// may already be NULL here. See bug 26676. So we don't assert anymore
|
|
// that _pScmProcessReg is non-NULL.
|
|
// ASSERT(_pScmProcessReg);
|
|
|
|
_pScmProcessReg = NULL;
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
|
|
return pResult;
|
|
}
|
|
|
|
void CProcess::RevokeProcessReg()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called during rundown to let SCM know that the COM+ process has died.
|
|
|
|
CODEWORK: This needs to be defined.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
}
|
|
|
|
RPC_BINDING_HANDLE
|
|
CProcess::CreateBindingHandle(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new binding handle back to the process.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Binding Handle, NULL if no valid handle.
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status;
|
|
|
|
CMutexLock lock(&gcsFastProcessLock);
|
|
|
|
// Find ncalrpc binding.
|
|
PWSTR pwstr = _pdsaLocalBindings->aStringArray;
|
|
while (*pwstr)
|
|
{
|
|
if (*pwstr == ID_LPC)
|
|
{
|
|
break;
|
|
}
|
|
pwstr = OrStringSearch(pwstr, 0) + 1;
|
|
}
|
|
|
|
if (*pwstr == 0)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Unable to find ncalrpc binding to server: %p %p\n",
|
|
_pdsaLocalBindings,
|
|
this));
|
|
|
|
ASSERT(0);
|
|
return NULL;
|
|
}
|
|
|
|
HANDLE hRpc = GetBinding(pwstr);
|
|
if (!hRpc)
|
|
return NULL;
|
|
|
|
// Set mutual auth on the binding handle
|
|
RPC_SECURITY_QOS_V3 qos;
|
|
ZeroMemory(&qos, sizeof(RPC_SECURITY_QOS_V3));
|
|
|
|
qos.Version = RPC_C_SECURITY_QOS_VERSION_3;
|
|
qos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH |
|
|
RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT;
|
|
qos.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
|
|
qos.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY;
|
|
qos.AdditionalSecurityInfoType = 0;
|
|
qos.Sid = _pToken->GetSid();
|
|
|
|
status = RpcBindingSetAuthInfoEx(hRpc,
|
|
NULL, // pass sid in QOS instead
|
|
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
|
|
RPC_C_AUTHN_WINNT,
|
|
NULL,
|
|
RPC_C_AUTHZ_NONE,
|
|
(RPC_SECURITY_QOS*)&qos);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
status = RpcBindingFree(&hRpc);
|
|
ASSERT(status == RPC_S_OK);
|
|
hRpc = NULL;
|
|
}
|
|
|
|
return hRpc;
|
|
}
|
|
|
|
BOOL
|
|
CProcess::EnsureBindingHandle()
|
|
{
|
|
if (_hProcess)
|
|
return TRUE;
|
|
|
|
CMutexLock lock(&gcsFastProcessLock);
|
|
|
|
// Fault in a handle back to the process
|
|
// if we haven't already
|
|
if (0 == _hProcess)
|
|
{
|
|
ASSERT(!_fCacheFree);
|
|
|
|
_hProcess = CreateBindingHandle();
|
|
if (!_hProcess)
|
|
return FALSE;
|
|
|
|
_fCacheFree = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
RPC_BINDING_HANDLE
|
|
CProcess::AllocateBinding(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a unique binding handle for a call back
|
|
to the process. This binding handle will not be
|
|
used by another thread until it is freed.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
0 - failure
|
|
|
|
non-zero - a binding handle to use.
|
|
|
|
--*/
|
|
{
|
|
if (!EnsureBindingHandle())
|
|
return NULL;
|
|
|
|
CMutexLock lock(&gcsFastProcessLock);
|
|
|
|
// If the master copy is available, let
|
|
// the caller use it.
|
|
if (_fCacheFree)
|
|
{
|
|
_fCacheFree = FALSE;
|
|
return _hProcess;
|
|
}
|
|
|
|
// Otherwise, create a copy
|
|
RPC_BINDING_HANDLE hCopy;
|
|
RPC_STATUS status;
|
|
status = RpcBindingCopy(_hProcess, &hCopy);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return hCopy;
|
|
}
|
|
|
|
|
|
void
|
|
CProcess::FreeBinding(
|
|
IN RPC_BINDING_HANDLE hBinding
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees a binding back to the process.
|
|
|
|
Arguments:
|
|
|
|
hBinding - A binding back to the process previously
|
|
allocated with AllocateBinding().
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if (hBinding == _hProcess)
|
|
{
|
|
_fCacheFree = TRUE;
|
|
}
|
|
else
|
|
{
|
|
RPC_STATUS status = RpcBindingFree(&hBinding);
|
|
ASSERT(status == RPC_S_OK);
|
|
}
|
|
}
|
|
|
|
RPC_BINDING_HANDLE CProcess::GetBindingHandle()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates and returns a binding handle that can be
|
|
used to call back to the process. Caller owns the
|
|
binding handle and should clean it up by calling
|
|
RpcBindingFree.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
0 - failure
|
|
|
|
non-zero - a binding handle to use.
|
|
|
|
--*/
|
|
{
|
|
if (!EnsureBindingHandle())
|
|
return NULL;
|
|
|
|
RPC_BINDING_HANDLE hCopy;
|
|
RPC_STATUS status;
|
|
status = RpcBindingCopy(_hProcess, &hCopy);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return hCopy;
|
|
}
|
|
|
|
|
|
RPC_STATUS
|
|
CProcess::RundownOids(
|
|
IN RPC_BINDING_HANDLE hRpc,
|
|
IN CServerOxid* pOwningOxid,
|
|
IN ULONG cOids,
|
|
IN CServerOid* aOids[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Issues an async call to the process which will rundown the OIDs.
|
|
This is called from an OXID which will be kept alive during the
|
|
whole call. Multiple calls maybe made to this function by
|
|
one or more OXIDs at the same time. The callback itself is
|
|
an ORPC call, ie is must have THIS and THAT pointers.
|
|
|
|
Arguments:
|
|
|
|
hRpc -- rpc binding handle with which to make the call. Caller owns.
|
|
|
|
pOwningOxid - oxid that owns the specified oids. This oxid should
|
|
be registered from this process.
|
|
|
|
cOids - The number of entries in aOids and afRundownOk
|
|
|
|
aOids - An array of CServerOid's to rundown. The OIDs must
|
|
all be owned by pOwningOxid. The caller will have already
|
|
called SetRundown(TRUE) on each one.
|
|
|
|
Return Value:
|
|
|
|
RPC_S_OK - the async call was issued successfully
|
|
|
|
other -- error occurred. call was not issued.
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
error_status_t status = OR_OK;
|
|
RPC_BINDING_HANDLE hBinding;
|
|
OID aScalarOids[MAX_OID_RUNDOWNS_PER_CALL];
|
|
|
|
ASSERT(hRpc);
|
|
ASSERT(cOids > 0 && aOids);
|
|
ASSERT(cOids <= MAX_OID_RUNDOWNS_PER_CALL);
|
|
ASSERT(IsOwner(pOwningOxid));
|
|
|
|
// Callers must be aware that the lock will be released upon return
|
|
ASSERT(gpServerLock->HeldExclusive());
|
|
gpServerLock->UnlockExclusive();
|
|
|
|
// This process will be held alive by the OXID calling
|
|
// us since it has an extra reference.
|
|
|
|
//
|
|
// Allocate async structure and zero it out
|
|
//
|
|
ASYNCRUNDOWNOID_STUFF* pArgs = new ASYNCRUNDOWNOID_STUFF;
|
|
if (!pArgs)
|
|
return OR_NOMEM;
|
|
|
|
ZeroMemory(pArgs, sizeof(ASYNCRUNDOWNOID_STUFF));
|
|
|
|
//
|
|
// Initialize RPC async state
|
|
//
|
|
status = RpcAsyncInitializeHandle(&(pArgs->async), sizeof(pArgs->async));
|
|
if (status != RPC_S_OK)
|
|
{
|
|
delete pArgs;
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Initialize async structure
|
|
//
|
|
pArgs->dwSig = ASYNCRUNDOWNOID_SIG;
|
|
pArgs->pProcess = this; // we take a reference further below
|
|
pArgs->pOxid = pOwningOxid; // we take a reference further below
|
|
pArgs->cOids = cOids;
|
|
CopyMemory(pArgs->aOids, aOids, cOids * sizeof(CServerOid*));
|
|
|
|
//
|
|
// Fill in the numeric oid values
|
|
//
|
|
ZeroMemory(aScalarOids, sizeof(OID) * MAX_OID_RUNDOWNS_PER_CALL);
|
|
for (i = 0; i < cOids; i++)
|
|
{
|
|
ASSERT(pArgs->aOids[i]);
|
|
ASSERT(pArgs->aOids[i]->IsRunningDown());
|
|
ASSERT(pArgs->aOids[i]->GetOxid() == pOwningOxid);
|
|
aScalarOids[i] = pArgs->aOids[i]->Id();
|
|
}
|
|
|
|
//
|
|
// Save the binding handle. This is not strictly necessary now that
|
|
// the soxid supplies the handle and owns its lifetime, but might be
|
|
// useful in a debugging situation.
|
|
//
|
|
pArgs->hBinding = hRpc;
|
|
|
|
//
|
|
// Init parts of the RPC_ASYNC_STATE struct that we care about
|
|
//
|
|
pArgs->async.Flags = 0; // the absence of RPC_C_NOTIFY_ON_SEND_COMPLETE flag means
|
|
// notify us on call completion, and no sooner.
|
|
pArgs->async.UserInfo = pArgs;
|
|
pArgs->async.NotificationType = RpcNotificationTypeCallback;
|
|
pArgs->async.u.NotificationRoutine = CProcess::AsyncRundownReturnNotification;
|
|
|
|
//
|
|
// Initialize other params
|
|
//
|
|
pArgs->orpcthis.version.MajorVersion = COM_MAJOR_VERSION;
|
|
pArgs->orpcthis.version.MinorVersion = COM_MINOR_VERSION;
|
|
pArgs->orpcthis.flags = ORPCF_LOCAL;
|
|
pArgs->orpcthis.reserved1 = 0;
|
|
pArgs->orpcthis.extensions = NULL;
|
|
pArgs->callIDHint = AllocateCallId(pArgs->orpcthis.cid);
|
|
pArgs->localthis.dwClientThread = 0;
|
|
pArgs->localthis.dwFlags = LOCALF_NONE;
|
|
pArgs->orpcthat.flags = 0;
|
|
pArgs->orpcthat.extensions = 0;
|
|
|
|
//
|
|
// Take an extra reference on the owning oxid and ourself.
|
|
// These references will be released either on the call
|
|
// return notification, or on the failure path below.
|
|
//
|
|
pOwningOxid->Reference();
|
|
this->Reference(); // non-client ref, will not stop rundown
|
|
|
|
//
|
|
// Finally, issue the call. Note that this is an async call
|
|
// from our perspective, but is a synchronous ORPC call from
|
|
// the server's perspective.
|
|
//
|
|
|
|
status = RawRundownOid(
|
|
&(pArgs->async),
|
|
pArgs->hBinding,
|
|
&(pArgs->orpcthis),
|
|
&(pArgs->localthis),
|
|
&(pArgs->orpcthat),
|
|
pArgs->cOids,
|
|
aScalarOids,
|
|
pArgs->aRundownStatus
|
|
);
|
|
if (status != RPC_S_OK)
|
|
{
|
|
// If we get anything other than RPC_S_OK back, that
|
|
// means we will not receive a call completion notif-
|
|
// ication. So, we need to cleanup everything up
|
|
// right here in that case.
|
|
|
|
// Call failed, so cleanup before returning. Caller will
|
|
// handle the failure semantics for the oids.
|
|
FreeCallId(pArgs->callIDHint);
|
|
delete pArgs;
|
|
|
|
//
|
|
// Must hold gpServerLock in order to call Release.
|
|
//
|
|
ASSERT(!gpServerLock->HeldExclusive());
|
|
gpServerLock->LockExclusive();
|
|
|
|
pOwningOxid->Release();
|
|
this->Release();
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
ASSERT(!gpServerLock->HeldExclusive());
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
CProcess::RundownOidNotify(CServerOxid* pOwningOxid,
|
|
ULONG cOids,
|
|
CServerOid* aOids[],
|
|
BYTE aRundownStatus[],
|
|
HRESULT hrReturn)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback notification function that is invoked when
|
|
async oid rundown calls are completed.
|
|
|
|
Arguments:
|
|
|
|
pOwningOxid -- owning oxid of the oids we tried to rundown.
|
|
|
|
cOids -- count of oids
|
|
|
|
aOids -- array of oids
|
|
|
|
aRundownStatus -- array of individual status rundowns for each oid
|
|
|
|
hrReturn -- return value from the function
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
error_status_t status = OR_OK;
|
|
|
|
ASSERT(pOwningOxid);
|
|
ASSERT((cOids > 0) && aOids && aRundownStatus);
|
|
|
|
//
|
|
// If destination oxid\apartment was not found, mark all
|
|
// oids for rundown.
|
|
//
|
|
if (hrReturn == RPC_E_DISCONNECTED)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Rundown returned disconnected\n"));
|
|
|
|
for (i = 0; i < cOids; i++)
|
|
{
|
|
aRundownStatus[i] = ORS_OK_TO_RUNDOWN;
|
|
}
|
|
hrReturn = RPC_S_OK;
|
|
}
|
|
|
|
//
|
|
// In case of any other error, don't rundown the oids
|
|
//
|
|
if (hrReturn != S_OK)
|
|
{
|
|
for (i = 0; i < cOids; i++)
|
|
{
|
|
aRundownStatus[i] = ORS_DONTRUNDOWN;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Notify the server oxid of the results
|
|
//
|
|
|
|
gpServerLock->LockExclusive();
|
|
|
|
pOwningOxid->ProcessRundownResults(
|
|
cOids,
|
|
aOids,
|
|
aRundownStatus
|
|
);
|
|
|
|
ASSERT(!gpServerLock->HeldExclusive());
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
CProcess::ExamineRpcAsyncCompleteCallReturnCode(RPC_STATUS status)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function adds some diagnostic asserts for the various return
|
|
values we might get back from RpcAsyncCompleteCall. It is still
|
|
the caller's responsibility to appropriately handle any errors.
|
|
|
|
++*/
|
|
{
|
|
switch (status)
|
|
{
|
|
case RPC_S_OK:
|
|
// Normal case
|
|
break;
|
|
|
|
case RPC_S_OUT_OF_MEMORY:
|
|
// This means that an internal RPC operation failed. Kamenm
|
|
// says in this case we can't depend on the state of the call's
|
|
// out-params, but all rpc-runtime allocated resources should
|
|
// be cleaned up, so no leaks.
|
|
break;
|
|
|
|
case RPC_S_ASYNC_CALL_PENDING:
|
|
case RPC_S_INVALID_ASYNC_HANDLE:
|
|
case RPC_S_INVALID_ASYNC_CALL:
|
|
case RPC_S_INVALID_ARG:
|
|
// Any of these error codes usually signify a caller coding error. So
|
|
// we assert. Unfortunately there is not much else we can do; also RPC
|
|
// will likely leak resources.
|
|
ASSERT(!"Got error back from RpcAsyncCompleteCall, possible COM bug");
|
|
break;
|
|
|
|
default:
|
|
// No clue. Most often this is because the server process died (0x6be)
|
|
break;
|
|
};
|
|
return;
|
|
}
|
|
|
|
void RPC_ENTRY
|
|
CProcess::AsyncRundownReturnNotification(
|
|
IN RPC_ASYNC_STATE* pAsync,
|
|
IN void* pContext,
|
|
IN RPC_ASYNC_EVENT Event
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback notification function that is invoked when
|
|
async rundown calls are completed. It unpacks the necessary
|
|
stuff from the async args struct, forwards them on to
|
|
RundownOidNotify, and does other necessary cleanup.
|
|
|
|
Arguments:
|
|
|
|
pAsync -- pointer to the async rpc state struct
|
|
|
|
pContext -- rpc thingy, we ignore it
|
|
|
|
Event -- should always be RpcCallComplete
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status;
|
|
HRESULT hrRetVal;
|
|
ASYNCRUNDOWNOID_STUFF* pArgs;
|
|
|
|
pArgs = (ASYNCRUNDOWNOID_STUFF*)
|
|
(((char*)pAsync) - offsetof(ASYNCRUNDOWNOID_STUFF, async));
|
|
|
|
ASSERT(pArgs->async.UserInfo = pArgs);
|
|
ASSERT(pArgs->dwSig == ASYNCRUNDOWNOID_SIG);
|
|
ASSERT(pArgs->hBinding);
|
|
ASSERT(pArgs->pProcess);
|
|
ASSERT(pArgs->pOxid);
|
|
ASSERT(pArgs->cOids > 0);
|
|
ASSERT(pAsync->Event == RpcCallComplete);
|
|
ASSERT(Event == RpcCallComplete);
|
|
|
|
//
|
|
// Free the call id
|
|
//
|
|
FreeCallId(pArgs->callIDHint);
|
|
|
|
//
|
|
// Complete the call. Even on successful call completion,
|
|
// hrRetVal is not necessarily S_OK.
|
|
//
|
|
status = RpcAsyncCompleteCall(&(pArgs->async), &hrRetVal);
|
|
ExamineRpcAsyncCompleteCallReturnCode(status);
|
|
|
|
//
|
|
// Notify the process object that the call has returned
|
|
//
|
|
pArgs->pProcess->RundownOidNotify(
|
|
pArgs->pOxid,
|
|
pArgs->cOids,
|
|
pArgs->aOids,
|
|
pArgs->aRundownStatus,
|
|
(status == RPC_S_OK) ? hrRetVal : E_FAIL
|
|
);
|
|
|
|
//
|
|
// Cleanup other stuff from the call
|
|
//
|
|
if (status == RPC_S_OK && pArgs->orpcthat.extensions)
|
|
{
|
|
for (ULONG i = 0; i < pArgs->orpcthat.extensions->size; i++)
|
|
{
|
|
MIDL_user_free(pArgs->orpcthat.extensions->extent[i]);
|
|
}
|
|
MIDL_user_free(pArgs->orpcthat.extensions->extent);
|
|
MIDL_user_free(pArgs->orpcthat.extensions);
|
|
}
|
|
|
|
//
|
|
// Release the references on ourselves and the server
|
|
// oxid. Must hold gpServerLock while calling Release.
|
|
//
|
|
gpServerLock->LockExclusive();
|
|
|
|
pArgs->pProcess->Release();
|
|
pArgs->pOxid->Release();
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
|
|
delete pArgs;
|
|
|
|
return;
|
|
}
|
|
|
|
ORSTATUS
|
|
CProcess::UseProtseqIfNeeded(
|
|
IN USHORT cClientProtseqs,
|
|
IN USHORT aClientProtseqs[]
|
|
)
|
|
{
|
|
ORSTATUS status;
|
|
RPC_BINDING_HANDLE hBinding;
|
|
UUID NullUuid = {0};
|
|
USHORT wProtseqTowerId;
|
|
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: UseProtseqIfNeeded ==> %d\n",
|
|
cClientProtseqs));
|
|
|
|
// This process will be held alive by the OXID calling
|
|
// us since it has an extra reference.
|
|
|
|
CMutexLock process(&gcsFastProcessLock);
|
|
|
|
// Another thread may have used the protseq in the mean time.
|
|
|
|
ASSERT(_pdsaLocalBindings);
|
|
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: FindMatchingProtSeq from local bindings\n"));
|
|
|
|
// ronans - initially _pdsaLocalBindings will hold bindings which have been set
|
|
// by when the server called ServerAllocateOxidAndOids .. usually only local
|
|
// protseqs LRPC or WMSG at that point.
|
|
wProtseqTowerId = FindMatchingProtseq(cClientProtseqs,
|
|
aClientProtseqs,
|
|
_pdsaLocalBindings->aStringArray
|
|
);
|
|
|
|
if (0 != wProtseqTowerId)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Found protseq in local bindings\n"));
|
|
|
|
return(OR_OK);
|
|
}
|
|
|
|
// No protseq shared between the client and the OXIDs' server.
|
|
// Find a matching protseq.
|
|
|
|
// check if its a solitary local protocol sequence LRPC or WMSG
|
|
if (cClientProtseqs == 1 && IsLocal(aClientProtseqs[0]))
|
|
{
|
|
// if so - get it
|
|
wProtseqTowerId = aClientProtseqs[0];
|
|
ASSERT(wProtseqTowerId);
|
|
}
|
|
else
|
|
// we have multiple protseqs - presumed to be nonlocal
|
|
{
|
|
// ensure we have custom protseq information
|
|
if (!_fReadCustomProtseqs)
|
|
{
|
|
// use local temporary to avoid holding process lock
|
|
DUALSTRINGARRAY *pdsaCustomProtseqs = NULL;
|
|
|
|
// Release the lock before we call the server
|
|
process.Unlock();
|
|
|
|
hBinding = AllocateBinding();
|
|
if (0 == hBinding)
|
|
return(OR_NOMEM);
|
|
|
|
status = RpcBindingSetObject(hBinding, &NullUuid);
|
|
// get the information from the Object server
|
|
if (status == RPC_S_OK)
|
|
{
|
|
status = ::GetCustomProtseqInfo(hBinding,cMyProtseqs, aMyProtseqs, &pdsaCustomProtseqs);
|
|
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"GetCustomProtseqInfo - status : %ld\n",
|
|
status));
|
|
}
|
|
|
|
FreeBinding(hBinding);
|
|
|
|
if (status == RPC_S_OK)
|
|
{
|
|
// relock the process object
|
|
process.Lock();
|
|
|
|
// Make sure another thread didn't beat us. Note that we're
|
|
// assuming that the server (ole32) will give us back the same
|
|
// answer each time. If they don't, then shame on them.
|
|
if (!_fReadCustomProtseqs)
|
|
{
|
|
// Don't make any more calls to the server
|
|
_fReadCustomProtseqs = TRUE;
|
|
|
|
if (pdsaCustomProtseqs)
|
|
{
|
|
ASSERT(dsaValid(pdsaCustomProtseqs));
|
|
}
|
|
_pdsaCustomProtseqs = pdsaCustomProtseqs;
|
|
}
|
|
else
|
|
{
|
|
// We got beat. Free our copy
|
|
MIDL_user_free( pdsaCustomProtseqs );
|
|
}
|
|
}
|
|
// else keep going.
|
|
// CODEWORK: this error path should be tightened up in future --
|
|
// ie, return an error at this point, and then re-try the
|
|
// GetCustomProtseqInfo call until it succeeds.
|
|
}
|
|
USHORT i,j;
|
|
|
|
// if there is custom protseq information - scan it for a match
|
|
if (_pdsaCustomProtseqs)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Using custom protseq information\n"));
|
|
|
|
wProtseqTowerId = FindMatchingProtseq(cClientProtseqs,
|
|
aClientProtseqs,
|
|
_pdsaCustomProtseqs->aStringArray);
|
|
|
|
if (wProtseqTowerId)
|
|
{
|
|
ASSERT(FALSE == IsLocal(wProtseqTowerId));
|
|
}
|
|
}
|
|
else
|
|
// we don't have custom protseqs so use
|
|
// the standard ones
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Using standard protseq information\n"));
|
|
|
|
for (i = 0; i < cClientProtseqs && wProtseqTowerId == 0; i++)
|
|
{
|
|
for (j = 0; j < cMyProtseqs; j++)
|
|
{
|
|
if (aMyProtseqs[j] == aClientProtseqs[i])
|
|
{
|
|
ASSERT(FALSE == IsLocal(aMyProtseqs[j]));
|
|
|
|
wProtseqTowerId = aMyProtseqs[j];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (0 == wProtseqTowerId)
|
|
{
|
|
// No shared protseq, must be a bug since the client managed to call us.
|
|
ASSERT(0 && "No shared protseq, must be a bug since the client managed to call us");
|
|
#if DBG
|
|
if (cClientProtseqs == 0)
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Client OR not configured to use remote protseqs\n"));
|
|
else
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Client called on an unsupported protocol:"
|
|
"%d %p %p \n",
|
|
cClientProtseqs,
|
|
aClientProtseqs,
|
|
aMyProtseqs));
|
|
#endif
|
|
|
|
return(OR_NOSERVER);
|
|
}
|
|
|
|
process.Unlock();
|
|
|
|
DUALSTRINGARRAY *pdsaBinding = 0;
|
|
DUALSTRINGARRAY *pdsaSecurity = 0;
|
|
|
|
hBinding = AllocateBinding();
|
|
if (0 == hBinding)
|
|
{
|
|
return(OR_NOMEM);
|
|
}
|
|
|
|
status = RpcBindingSetObject(hBinding, &NullUuid);
|
|
if (status == RPC_S_OK)
|
|
{
|
|
status = ::UseProtseq(hBinding,
|
|
wProtseqTowerId,
|
|
&pdsaBinding,
|
|
&pdsaSecurity);
|
|
|
|
}
|
|
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Lazy use protseq: %S (from towerid) in process %p - %d\n",
|
|
GetProtseq(wProtseqTowerId),
|
|
this,
|
|
status));
|
|
|
|
// Update this process' state to include the new bindings.
|
|
if (!dsaValid(pdsaBinding))
|
|
{
|
|
if (pdsaBinding)
|
|
{
|
|
KdPrintEx((DPFLTR_DCOMSS_ID,
|
|
DPFLTR_WARNING_LEVEL,
|
|
"OR: Use protseq returned an invalid dsa: %p\n",
|
|
pdsaBinding));
|
|
}
|
|
|
|
status = OR_NOMEM;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(_pdsaLocalBindings);
|
|
ASSERT(status == RPC_S_OK);
|
|
status = ProcessBindings(pdsaBinding, pdsaSecurity);
|
|
}
|
|
|
|
if (pdsaBinding != NULL)
|
|
MIDL_user_free(pdsaBinding);
|
|
if (pdsaSecurity != NULL)
|
|
MIDL_user_free(pdsaSecurity);
|
|
|
|
FreeBinding(hBinding);
|
|
return(status);
|
|
}
|
|
|
|
|
|
ORSTATUS CProcess::UpdateResolverBindings(DWORD64 dwBindingsID, DUALSTRINGARRAY* pdsaResolverBindings)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function does the work of calling back into the server process
|
|
to tell it to update its local OR bindings. It also updates our
|
|
cached local\remote bindings for this process object.
|
|
|
|
Arguments:
|
|
|
|
dwBindingsID -- unique id of pdsaResolverBindings. We use this
|
|
to resolve what to do when async calls arrive\return in an
|
|
out-of-order fashion.
|
|
|
|
pdsaResolverBindings -- ptr to the new resolver bindings
|
|
|
|
Return Value:
|
|
|
|
OR_OK -- success
|
|
OR_NOMEM -- out of memory
|
|
other -- unexpected error
|
|
|
|
--*/
|
|
{
|
|
ORSTATUS status;
|
|
RPC_BINDING_HANDLE hBinding;
|
|
UUID NullUuid = {0};
|
|
DUALSTRINGARRAY* pdsaBinding = NULL;
|
|
DUALSTRINGARRAY* pdsaSecurity = NULL;
|
|
BINDINGS_UPDATE_CALLBACK_STUFF* pArgs = NULL;
|
|
|
|
CMutexLock process(&gcsFastProcessLock);
|
|
|
|
if (dwBindingsID <= _dwCurrentBindingsID)
|
|
{
|
|
// The supplied bindings are the same or older than
|
|
// what we already have, so ignore them.
|
|
return OR_OK;
|
|
}
|
|
|
|
// if the server has not yet called _ServerAllocateOxidAndOids
|
|
// then we will not yet have cached binding info for them, and
|
|
// hence will be unable to construct a binding handle. In
|
|
// this case we simply do not update that process. If this
|
|
// happens we will give them the new bindings later, if and
|
|
// when they do call _SAOAO.
|
|
if (!_pdsaLocalBindings)
|
|
{
|
|
FlagsOn(PROCESS_NEEDSBINDINGS);
|
|
return OR_OK;
|
|
}
|
|
|
|
// Done with the lock.
|
|
process.Unlock();
|
|
|
|
pArgs = new BINDINGS_UPDATE_CALLBACK_STUFF;
|
|
if (!pArgs)
|
|
return OR_NOMEM;
|
|
|
|
ZeroMemory(pArgs, sizeof(BINDINGS_UPDATE_CALLBACK_STUFF));
|
|
|
|
// Allocate binding. We must hold onto the binding handle
|
|
// until the async call has completed.
|
|
status = OR_NOMEM;
|
|
hBinding = AllocateBinding();
|
|
if (hBinding)
|
|
{
|
|
status = RpcBindingSetObject(hBinding, &NullUuid);
|
|
if (status == RPC_S_OK)
|
|
{
|
|
status = RpcAsyncInitializeHandle(&(pArgs->async), sizeof(pArgs->async));
|
|
}
|
|
}
|
|
|
|
// Check for errors
|
|
if (status != RPC_S_OK)
|
|
{
|
|
if (hBinding) FreeBinding(hBinding);
|
|
delete pArgs;
|
|
return status;
|
|
}
|
|
|
|
// Init parts of the RPC_ASYNC_STATE struct that we care about
|
|
pArgs->async.Flags = 0; // the absence of RPC_C_NOTIFY_ON_SEND_COMPLETE flag means
|
|
// notify us on call completion, and no sooner.
|
|
pArgs->async.UserInfo = pArgs;
|
|
pArgs->async.NotificationType = RpcNotificationTypeCallback;
|
|
pArgs->async.u.NotificationRoutine = CProcess::AsyncRpcNotification;
|
|
|
|
// Init other stuff
|
|
pArgs->dwSig = BINDINGUPDATESTUFF_SIG;
|
|
pArgs->pProcess = this;
|
|
pArgs->dwBindingsID = dwBindingsID;
|
|
pArgs->hBinding = hBinding;
|
|
|
|
// Take a non-client reference (will not stop rundown) on
|
|
// ourselves, to be owned implicitly by the async call
|
|
this->Reference();
|
|
|
|
// Issue async call
|
|
|
|
status = ::UpdateResolverBindings(
|
|
&(pArgs->async),
|
|
_hProcess,
|
|
pdsaResolverBindings,
|
|
&(pArgs->dwBindingsID),
|
|
&(pArgs->pdsaNewBindings),
|
|
&(pArgs->pdsaNewSecurity));
|
|
|
|
if (status != RPC_S_OK)
|
|
{
|
|
// If we get anything other than RPC_S_OK back, that
|
|
// means we will not receive a call completion notif-
|
|
// ication. So, we need to cleanup everything up
|
|
// right here in that case.
|
|
FreeBinding(hBinding);
|
|
this->Release();
|
|
delete pArgs;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwAUO = (DWORD)InterlockedIncrement((PLONG)&_dwAsyncUpdatesOutstanding);
|
|
|
|
// This assert is somewhat arbitrary, if it fires there are
|
|
// one of two things wrong: 1) the machine is so totally
|
|
// overstressed that the async calls are piling up and are not
|
|
// getting enough cpu time to complete; or 2) more likely, the
|
|
// process in question is deadlocked somehow.
|
|
ASSERT(dwAUO < 5000);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
CProcess::BindingsUpdateNotify(RPC_BINDING_HANDLE hBinding,
|
|
DWORD64 dwBindingsID,
|
|
DUALSTRINGARRAY* pdsaNewBindings,
|
|
DUALSTRINGARRAY* pdsaSecBindings,
|
|
HRESULT hrReturn)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Private helper function. This function is used to process a
|
|
successful return from an async call to the process to update
|
|
the bindings.
|
|
|
|
Arguments:
|
|
|
|
hBinding -- binding handle used to make the call. We now own it,
|
|
either to keep or to cleanup.
|
|
|
|
dwBindingsID -- unique id of the updated bindings. We use this
|
|
to resolve what to do when async calls arrive\return in an
|
|
out-of-order fashion.
|
|
|
|
pdsaNewBindings -- new bindings in use by the process. We now own
|
|
it, either to keep or to cleanup
|
|
|
|
pdsaSecBindings -- new security bindings in use by the process. We
|
|
now own it, either to keep or to cleanup.
|
|
|
|
hrReturn -- the result of the operation.
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status;
|
|
|
|
// Always free the binding
|
|
FreeBinding(hBinding);
|
|
|
|
// Only process the out-params if the operation was a success; otherwise
|
|
// ignore everthing.
|
|
if (hrReturn == S_OK)
|
|
{
|
|
// Take lock while looking at _dwCurrentBindingsID
|
|
CMutexLock process(&gcsFastProcessLock);
|
|
|
|
// Only process the out-params if they contain newer bindings
|
|
// than what we currently have cached.
|
|
if ((dwBindingsID > _dwCurrentBindingsID) &&
|
|
pdsaNewBindings &&
|
|
pdsaSecBindings)
|
|
{
|
|
// The process has the right bindings, so update our counter
|
|
// no matter what happens in ProcessBindings.
|
|
_dwCurrentBindingsID = dwBindingsID;
|
|
|
|
status = ProcessBindings(pdsaNewBindings, pdsaSecBindings);
|
|
}
|
|
|
|
// No lock needed from here on out
|
|
process.Unlock();
|
|
|
|
// Cleanup allocated out-params
|
|
if (pdsaNewBindings != NULL)
|
|
MIDL_user_free(pdsaNewBindings);
|
|
if (pdsaSecBindings != NULL)
|
|
MIDL_user_free(pdsaSecBindings);
|
|
}
|
|
else
|
|
{
|
|
// Assert that on an error path we are not leaking memory.
|
|
ASSERT(NULL == pdsaNewBindings);
|
|
ASSERT(NULL == pdsaSecBindings);
|
|
}
|
|
|
|
InterlockedDecrement((PLONG)&_dwAsyncUpdatesOutstanding);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void RPC_ENTRY
|
|
CProcess::AsyncRpcNotification(RPC_ASYNC_STATE* pAsync,
|
|
void* pContext,
|
|
RPC_ASYNC_EVENT Event)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
RPC calls this static function when an async call to a server
|
|
process returns.
|
|
|
|
Arguments:
|
|
|
|
pAsync -- pointer to the async rpc state struct
|
|
|
|
pContext -- rpc thingy, we ignore it
|
|
|
|
Event -- should always be RpcCallComplete
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
{
|
|
RPC_STATUS status;
|
|
HRESULT hrRetVal;
|
|
BINDINGS_UPDATE_CALLBACK_STUFF* pArgs;
|
|
|
|
pArgs = (BINDINGS_UPDATE_CALLBACK_STUFF*)
|
|
(((char*)pAsync) - offsetof(BINDINGS_UPDATE_CALLBACK_STUFF, async));
|
|
|
|
ASSERT(pArgs->async.UserInfo = pArgs);
|
|
ASSERT(pArgs->dwSig == BINDINGUPDATESTUFF_SIG);
|
|
ASSERT(pArgs->hBinding);
|
|
ASSERT(pArgs->pProcess);
|
|
ASSERT(pArgs->dwBindingsID > 0);
|
|
ASSERT(pAsync->Event == RpcCallComplete);
|
|
ASSERT(Event == RpcCallComplete);
|
|
|
|
// Complete the call. Since we've asked for a direct callback upon
|
|
// completion, we should never get back RPC_S_ASYNC_CALL_PENDING, so
|
|
// we assert on this. Otherwise, both RPC and the server need to
|
|
// return success before we do further processing.
|
|
status = RpcAsyncCompleteCall(&(pArgs->async), &hrRetVal);
|
|
ExamineRpcAsyncCompleteCallReturnCode(status);
|
|
|
|
// Deliver notification to process object. Process may have
|
|
// already been rundown, that's okay. BindingsUpdateNotify will
|
|
// own/cleanup the params, see code.
|
|
pArgs->pProcess->BindingsUpdateNotify(pArgs->hBinding,
|
|
pArgs->dwBindingsID,
|
|
pArgs->pdsaNewBindings,
|
|
pArgs->pdsaNewSecurity,
|
|
(status == RPC_S_OK) ? hrRetVal : E_FAIL);
|
|
|
|
gpServerLock->LockExclusive();
|
|
|
|
// This may be the last release on the process object
|
|
pArgs->pProcess->Release();
|
|
|
|
gpServerLock->UnlockExclusive();
|
|
|
|
delete pArgs;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
CProcess::FlagsOn(DWORD dwOn)
|
|
{
|
|
for (;;)
|
|
{
|
|
DWORD dwCurrent = _dwFlags;
|
|
DWORD dwNew = dwCurrent | dwOn;
|
|
if (dwCurrent == (ULONG)InterlockedCompareExchange(
|
|
(LONG*)&_dwFlags,
|
|
dwNew,
|
|
dwCurrent))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CProcess::FlagsOff(DWORD dwOff)
|
|
{
|
|
for (;;)
|
|
{
|
|
DWORD dwCurrent = _dwFlags;
|
|
DWORD dwNew = dwCurrent & ~dwOff;
|
|
if (dwCurrent == (ULONG)InterlockedCompareExchange(
|
|
(LONG*)&_dwFlags,
|
|
dwNew,
|
|
dwCurrent))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
CBList *gpProcessList = 0;
|
|
|
|
CProcess *
|
|
ReferenceProcess(
|
|
IN PVOID key,
|
|
IN BOOL fNotContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Used to find a CProcess and get a reference on it
|
|
|
|
Arguments:
|
|
|
|
key - The dword key of the process allocated in _Connect.
|
|
fNotContext - Normally the key is stored as a context handle
|
|
which means locking is unnecessary. There is an extra
|
|
refernce which is released buring context rundown which
|
|
means managers using the key as a context handle
|
|
a) Don't need to lock the process and
|
|
b) Don't need to call ReleaseProcess()
|
|
|
|
Return Value:
|
|
|
|
0 - invalid key
|
|
non-zero - The process.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(gpProcessList != 0);
|
|
CProcess *pProcess;
|
|
|
|
if (!key)
|
|
return NULL;
|
|
|
|
gpProcessListLock->LockShared();
|
|
|
|
if (gpProcessList->Member(key) == FALSE)
|
|
{
|
|
gpProcessListLock->UnlockShared();
|
|
return(0);
|
|
}
|
|
|
|
pProcess = (CProcess *)key;
|
|
|
|
if (fNotContext)
|
|
{
|
|
pProcess->ClientReference();
|
|
}
|
|
|
|
gpProcessListLock->UnlockShared();
|
|
return(pProcess);
|
|
}
|
|
|
|
void ReleaseProcess(CProcess *pProcess)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Releases a pProcess object. This should only be called when
|
|
a process object has been referenced with the fNotContext == TRUE.
|
|
|
|
Arguments:
|
|
|
|
pProcess - the process to release. May actually be deleted.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
gpProcessListLock->LockExclusive();
|
|
|
|
if (pProcess->ClientRelease() == 0)
|
|
{
|
|
// Process has been completely released,
|
|
// we'll remove it from the list now since we
|
|
// already have the lock. It may not have been
|
|
// added, so this may fail.
|
|
|
|
PVOID t = gpProcessList->Remove(pProcess);
|
|
ASSERT(t == pProcess || t == 0);
|
|
|
|
gpProcessListLock->UnlockExclusive();
|
|
|
|
// The client process owns one real reference which will be
|
|
// released in Rundown().
|
|
|
|
pProcess->Rundown();
|
|
}
|
|
else
|
|
{
|
|
gpProcessListLock->UnlockExclusive();
|
|
}
|
|
}
|
|
|
|
BOOL VerifyNewProcess(DWORD dwPID)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks to see if we already have a registered active
|
|
process with the specified pid.
|
|
|
|
Arguments:
|
|
|
|
dwPID - pid of the new client
|
|
|
|
Return Value:
|
|
|
|
TRUE - the process is not known to us
|
|
FALSE - an existing entry already exists for this pid
|
|
|
|
Notes: there is a tiny race between the time a process dies
|
|
an abnormal death and the time we receive the rundown. To
|
|
try to account for this, we allow up to MAX_ENTRIES_PER_PID
|
|
registrations for a single pid.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(gpProcessListLock->HeldExclusive());
|
|
|
|
CProcess* pProcess;
|
|
CBListIterator itor(gpProcessList);
|
|
DWORD dwCurrentEntries = 0;
|
|
const DWORD MAX_ENTRIES_PER_PID = 10;
|
|
|
|
while (pProcess = (CProcess*)itor.Next())
|
|
{
|
|
if ((pProcess->GetPID() == dwPID) &&
|
|
(!pProcess->HasBeenRundown()))
|
|
{
|
|
dwCurrentEntries++;
|
|
}
|
|
}
|
|
|
|
return (dwCurrentEntries <= MAX_ENTRIES_PER_PID);
|
|
}
|