2037 lines
49 KiB
C++
2037 lines
49 KiB
C++
/*++
|
||
|
||
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 rundown and dirty bit
|
||
_dwFlags |= (PROCESS_RUNDOWN & PROCESS_SPI_DIRTY);
|
||
|
||
gpClientLock->UnlockExclusive();
|
||
|
||
// 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();
|
||
}
|
||
|
||
|
||
CProcess::CProcess(
|
||
IN CToken*& pToken,
|
||
IN WCHAR *pwszWinstaDesktop,
|
||
IN DWORD procID,
|
||
IN DWORD dwFlags,
|
||
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;
|
||
_fLockValid = FALSE;
|
||
_hProcHandle = NULL;
|
||
_dwFlags = PROCESS_SPI_DIRTY; // always start out dirty
|
||
_pSCMProcessInfo = NULL;
|
||
_ulClasses = 0;
|
||
_dwCurrentBindingsID = 0;
|
||
_dwAsyncUpdatesOutstanding = 0;
|
||
|
||
// Set 64 bit flag if so indicated
|
||
if (dwFlags & CONNECT_FLAGS_64BIT)
|
||
_dwFlags |= PROCESS_64BIT;
|
||
|
||
// 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)
|
||
{
|
||
status = RtlInitializeCriticalSection(&_csCallbackLock);
|
||
_fLockValid = (status == STATUS_SUCCESS);
|
||
}
|
||
|
||
if (status == STATUS_SUCCESS)
|
||
{
|
||
gpProcessListLock->LockExclusive();
|
||
status = gpProcessList->Insert(this);
|
||
gpProcessListLock->UnlockExclusive();
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
|
||
_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 (_fLockValid)
|
||
DeleteCriticalSection(&_csCallbackLock);
|
||
|
||
if (_hProcess)
|
||
{
|
||
RPC_STATUS status = RpcBindingFree(&_hProcess);
|
||
ASSERT(status == RPC_S_OK);
|
||
ASSERT(_hProcess == 0);
|
||
}
|
||
|
||
#ifndef _CHICAGO_
|
||
extern void RunAsRelease(void*);
|
||
RunAsRelease(_pvRunAsHandle);
|
||
#endif // _CHICAGO_
|
||
|
||
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
|
||
_dwFlags &= ~PROCESS_SPI_DIRTY;
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
void CProcess::SetProcessReadyState(DWORD dwState)
|
||
{
|
||
ASSERT(_pScmProcessReg);
|
||
|
||
gpServerLock->LockExclusive();
|
||
|
||
_pScmProcessReg->ReadinessStatus = dwState;
|
||
|
||
// we're now dirty
|
||
_dwFlags |= PROCESS_SPI_DIRTY;
|
||
|
||
gpServerLock->UnlockExclusive();
|
||
}
|
||
|
||
|
||
void CProcess::Retire()
|
||
{
|
||
// A process can (or should be) only retired once
|
||
ASSERT(!IsRetired());
|
||
|
||
// Mark ourselves as retired
|
||
_dwFlags |= (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
|
||
|
||
--*/
|
||
|
||
if (dwLaunchedPID == _procID)
|
||
{
|
||
_hProcHandle = hProcHandle;
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
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
|
||
_dwFlags |= 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
|
||
_dwFlags |= 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::GetBindingHandle(
|
||
void
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If necessary, this function allocates a binding handle
|
||
back to process. It used either mswmsg or ncalrpc depending
|
||
on the apartmentness of 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;
|
||
}
|
||
|
||
return GetBinding(pwstr);
|
||
}
|
||
|
||
void
|
||
CProcess::EnsureRealBinding(
|
||
void
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If necessary, this function allocates a binding handle
|
||
back to process.
|
||
|
||
Note: Called with the server lock held -OR- from an OXID
|
||
with and extra reference which keeps this process alive.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
CMutexLock lock(&gcsFastProcessLock);
|
||
|
||
if (0 == _hProcess)
|
||
{
|
||
_hProcess = GetBindingHandle();
|
||
_fCacheFree = 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.
|
||
|
||
--*/
|
||
{
|
||
|
||
EnsureRealBinding();
|
||
|
||
if (_hProcess == 0)
|
||
{
|
||
return(0);
|
||
}
|
||
|
||
CMutexLock lock(&gcsFastProcessLock);
|
||
|
||
ASSERT(_hProcess);
|
||
|
||
if (_fCacheFree)
|
||
{
|
||
_fCacheFree = FALSE;
|
||
return(_hProcess);
|
||
}
|
||
|
||
RPC_BINDING_HANDLE h;
|
||
RPC_STATUS status;
|
||
|
||
status = RpcBindingCopy(_hProcess, &h);
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
return(0);
|
||
}
|
||
|
||
return(h);
|
||
}
|
||
|
||
|
||
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_STATUS
|
||
CProcess::RundownOids(
|
||
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:
|
||
|
||
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(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 async structure
|
||
//
|
||
pArgs->dwSig = ASYNCRUNDOWNOID_SIG;
|
||
pArgs->pProcess = this; // will take ref below after issuing call
|
||
pArgs->pOxid = pOwningOxid; // will take ref below after issuing call
|
||
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();
|
||
}
|
||
|
||
//
|
||
// Allocate binding handle
|
||
//
|
||
status = OR_NOMEM; // assume no mem
|
||
hBinding = AllocateBinding();
|
||
if (hBinding)
|
||
{
|
||
IPID ipidUnk = pOwningOxid->GetIPID();
|
||
status = RpcBindingSetObject(hBinding, &ipidUnk);
|
||
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;
|
||
}
|
||
|
||
//
|
||
// Save the binding handle
|
||
//
|
||
pArgs->hBinding = hBinding;
|
||
|
||
//
|
||
// Init parts of the RPC_ASYNC_STATE struct that we care about
|
||
//
|
||
pArgs->async.UserInfo = pArgs;
|
||
pArgs->async.Event = RpcCallComplete;
|
||
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)
|
||
{
|
||
// Call failed, so cleanup before returning. Caller will
|
||
// handle the failure semantics for the oids.
|
||
FreeBinding(pArgs->hBinding);
|
||
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(RPC_BINDING_HANDLE hBinding,
|
||
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:
|
||
|
||
hBinding -- binding handle used to make the call
|
||
|
||
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(hBinding);
|
||
ASSERT(pOwningOxid);
|
||
ASSERT((cOids > 0) && aOids && aRundownStatus);
|
||
|
||
//
|
||
// Free the binding
|
||
//
|
||
FreeBinding(hBinding);
|
||
|
||
//
|
||
// 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 != RPC_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
|
||
);
|
||
|
||
gpServerLock->UnlockExclusive();
|
||
|
||
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 -- rpc thingy, we ignore it
|
||
|
||
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);
|
||
|
||
//
|
||
// Free the call id
|
||
//
|
||
FreeCallId(pArgs->callIDHint);
|
||
|
||
// 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. hrRetVal is not necessarily S_OK.
|
||
status = RpcAsyncCompleteCall(&(pArgs->async), &hrRetVal);
|
||
ASSERT(status != RPC_S_ASYNC_CALL_PENDING);
|
||
|
||
//
|
||
// Notify the process object that the call has returned
|
||
//
|
||
pArgs->pProcess->RundownOidNotify(
|
||
pArgs->hBinding, // process frees the binding
|
||
pArgs->pOxid,
|
||
pArgs->cOids,
|
||
pArgs->aOids,
|
||
pArgs->aRundownStatus,
|
||
(status == RPC_S_OK) ? hrRetVal : status
|
||
);
|
||
|
||
//
|
||
// 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 callback(&_csCallbackLock);
|
||
|
||
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;
|
||
|
||
// we'll only try this once - so set the flag now to avoid
|
||
// race conditions
|
||
_fReadCustomProtseqs = TRUE;
|
||
|
||
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));
|
||
}
|
||
|
||
// relock the process object
|
||
process.Lock();
|
||
|
||
if (status == RPC_S_OK)
|
||
{
|
||
if (pdsaCustomProtseqs)
|
||
{
|
||
ASSERT(dsaValid(pdsaCustomProtseqs));
|
||
}
|
||
_pdsaCustomProtseqs = pdsaCustomProtseqs;
|
||
}
|
||
FreeBinding(hBinding);
|
||
}
|
||
|
||
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;
|
||
|
||
// undone figure out why we need this callback lock thingy
|
||
CMutexLock callback(&_csCallbackLock);
|
||
|
||
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)
|
||
{
|
||
_dwFlags |= PROCESS_NEEDSBINDINGS;
|
||
return OR_OK;
|
||
}
|
||
|
||
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.UserInfo = pArgs;
|
||
pArgs->async.Event = RpcCallComplete;
|
||
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)
|
||
/*++
|
||
|
||
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.
|
||
|
||
Return Value:
|
||
|
||
void
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS status;
|
||
CMutexLock callback(&_csCallbackLock);
|
||
|
||
// Always free the binding
|
||
FreeBinding(hBinding);
|
||
|
||
// 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);
|
||
}
|
||
|
||
// Cleanup allocated out-params
|
||
if (pdsaNewBindings != NULL)
|
||
MIDL_user_free(pdsaNewBindings);
|
||
if (pdsaSecBindings != NULL)
|
||
MIDL_user_free(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
|
||
|
||
|
||
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);
|
||
|
||
// 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);
|
||
ASSERT(status != RPC_S_ASYNC_CALL_PENDING);
|
||
if ((status == RPC_S_OK) && (hrRetVal == S_OK))
|
||
{
|
||
// 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);
|
||
}
|
||
|
||
gpServerLock->LockExclusive();
|
||
|
||
// This may be the last release on the process object
|
||
pArgs->pProcess->Release();
|
||
|
||
gpServerLock->UnlockExclusive();
|
||
|
||
delete pArgs;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
|
||
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;
|
||
|
||
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 completly released the process,
|
||
// 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();
|
||
}
|
||
}
|
||
|