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.
1793 lines
55 KiB
1793 lines
55 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997.
|
|
//
|
|
// File: class.cxx
|
|
//
|
|
// Contents: Server table entry implementation
|
|
//
|
|
// History: VinayKr Created
|
|
// 06-Nov-98 TarunA Launch and shutdown logic change
|
|
// 24-Feb-99 TarunA Fix custom surrogate launch
|
|
// 17-Jun-99 a-sergiv Support for filtering RPCSS event logs
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "act.hxx"
|
|
|
|
CServerTable * gpClassTable = NULL;
|
|
CServerTable * gpProcessTable = NULL;
|
|
|
|
CSharedLock * gpClassLock = 0;
|
|
CSharedLock * gpProcessLock = 0;
|
|
|
|
CInterlockedInteger gRegisterKey( 1 );
|
|
|
|
//
|
|
// Default number of milliseconds to wait for a server to register its
|
|
// class factory before giving up and forcefully terminating it.
|
|
//
|
|
DWORD gServerStartTimeout = 120000;
|
|
|
|
//
|
|
// Default number of milliseconds to wait for a class factory registration
|
|
// after we detect that the launched server has died (for supporting cases
|
|
// where the server we launch turns around and launches another, then dies
|
|
// immediately)
|
|
//
|
|
DWORD gServerStartSecondChanceTimeout = 30000;
|
|
|
|
//
|
|
// Default number of milliseconds to wait for an NT service server to register
|
|
// before we wake up and query its status. At most we wait for
|
|
// gNTServiceMaxTimeouts of these periods (see code in WaitForService below).
|
|
//
|
|
DWORD gNTServiceInterrogatePeriod = 30000;
|
|
DWORD gNTServiceMaxTimeouts = 4;
|
|
|
|
//
|
|
// Default number of milliseconds that we block while waiting for a dllhost
|
|
// server to register. There is a startup protocol here - we block for
|
|
// thirty seconds, then check the state of the launched process.
|
|
//
|
|
DWORD gDllhostServerStartTimeout = 30000;
|
|
|
|
//
|
|
// Maxiumum number of milliseconds we will wait while waiting for a dllhost
|
|
// server to register. There is a protocol yes, but this is the upper time
|
|
// bound no matter what.
|
|
//
|
|
DWORD gDllhostMaxServerStartTimeout = 90000;
|
|
|
|
//
|
|
// Default number of milliseconds to wait for a dllhost to finish
|
|
// long-running initialization.
|
|
//
|
|
DWORD gDllhostInitializationTimeout = 90000;
|
|
|
|
void GetSessionIDFromActParams(IActivationPropertiesIn* pActPropsIn, LONG* plSessionID)
|
|
{
|
|
*plSessionID = INVALID_SESSION_ID;
|
|
|
|
if (pActPropsIn != NULL)
|
|
{
|
|
ISpecialSystemProperties *pSp = NULL;
|
|
HRESULT hr;
|
|
|
|
hr = pActPropsIn->QueryInterface(IID_ISpecialSystemProperties, (void**) &pSp);
|
|
if (SUCCEEDED(hr) && pSp != NULL)
|
|
{
|
|
ULONG ulSessionID;
|
|
BOOL bUseConsole;
|
|
|
|
hr = pSp->GetSessionId(&ulSessionID, &bUseConsole);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!bUseConsole && (ulSessionID != INVALID_SESSION_ID))
|
|
{
|
|
// Just use whatever was there
|
|
*plSessionID = (LONG) ulSessionID;
|
|
}
|
|
else if (bUseConsole)
|
|
{
|
|
// Little bit of magic here regarding how to find the
|
|
// currently active NT console session ID.
|
|
*plSessionID = (USER_SHARED_DATA->ActiveConsoleId);
|
|
}
|
|
}
|
|
|
|
pSp->Release();
|
|
pSp = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTable::GetOrCreate
|
|
//
|
|
// A somewhat degenerate combination of Lookup and Create -- only creates
|
|
// empty entries -- in particular without hooking up process entry if the
|
|
// entry being created is a class entry -- used only to create process entries
|
|
// and to create class entries for unsolicited CoRegisterClassObject calls
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CServerTableEntry *
|
|
CServerTable::GetOrCreate(
|
|
IN GUID & ServerGuid
|
|
)
|
|
{
|
|
CServerTableEntry * pEntry;
|
|
|
|
pEntry = Lookup( ServerGuid );
|
|
|
|
if ( pEntry )
|
|
return pEntry;
|
|
else
|
|
return Create(ServerGuid);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTable::Create
|
|
//
|
|
// Creates a new CServerTableEntry, or returns an existing one.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CServerTableEntry *
|
|
CServerTable::Create(
|
|
IN GUID & ServerGuid
|
|
)
|
|
{
|
|
BOOL fEntryFoundInTable = FALSE;
|
|
|
|
_pServerTableLock->LockExclusive();
|
|
|
|
// Must use CGuidTable::Lookup method because it doesn't take
|
|
// a shared lock. Already have the exclusive lock.
|
|
CServerTableEntry *pNewEntry = (CServerTableEntry *) CGuidTable::Lookup( &ServerGuid );
|
|
|
|
if (pNewEntry)
|
|
{
|
|
pNewEntry->Reference(); // Found an entry, take a reference on it
|
|
fEntryFoundInTable = TRUE;
|
|
}
|
|
else
|
|
{
|
|
LONG Status = ERROR_SUCCESS;
|
|
pNewEntry = new CServerTableEntry( Status, &ServerGuid, _EntryType );
|
|
|
|
if ( ! pNewEntry )
|
|
{
|
|
_pServerTableLock->UnlockExclusive();
|
|
return NULL;
|
|
}
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
{
|
|
_pServerTableLock->UnlockExclusive();
|
|
pNewEntry->Release();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (!fEntryFoundInTable)
|
|
{
|
|
Add( pNewEntry );
|
|
}
|
|
|
|
_pServerTableLock->UnlockExclusive();
|
|
return pNewEntry;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTable::Lookup
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CServerTableEntry *
|
|
CServerTable::Lookup(
|
|
IN GUID & ServerGuid
|
|
)
|
|
{
|
|
CServerTableEntry * pServerTableEntry;
|
|
|
|
_pServerTableLock->LockShared();
|
|
pServerTableEntry = (CServerTableEntry *) CGuidTable::Lookup( &ServerGuid );
|
|
if ( pServerTableEntry )
|
|
pServerTableEntry->Reference();
|
|
_pServerTableLock->UnlockShared();
|
|
|
|
return pServerTableEntry;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::CServerTableEntry
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CServerTableEntry::CServerTableEntry(
|
|
OUT LONG& Status,
|
|
IN GUID * pServerGUID,
|
|
IN EnumEntryType EntryType
|
|
) :
|
|
CGuidTableEntry( pServerGUID ), _EntryType(EntryType)
|
|
,_ServerLock( Status ), _lThreadToken(0), _hProcess(0), _pProcess(NULL)
|
|
,_pvRunAsHandle(NULL), _bSuspendedClsid(FALSE), _bSuspendedApplication(FALSE),
|
|
_dwProcessId(0)
|
|
{
|
|
switch (_EntryType)
|
|
{
|
|
case ENTRY_TYPE_CLASS:
|
|
_pParentTable = gpClassTable;
|
|
break;
|
|
|
|
case ENTRY_TYPE_PROCESS:
|
|
_pParentTable = gpProcessTable;
|
|
break;
|
|
|
|
default:
|
|
if (Status == ERROR_SUCCESS)
|
|
Status = E_INVALIDARG;
|
|
}
|
|
|
|
_pParentTableLock = _pParentTable->_pServerTableLock;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::~CServerTableEntry
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CServerTableEntry::~CServerTableEntry()
|
|
{
|
|
ASSERT(_pParentTableLock->HeldExclusive());
|
|
|
|
// Remove ourselves from the table - this will be a no-op if we
|
|
// we were never actually added.
|
|
_pParentTable->Remove(this);
|
|
|
|
if(NULL != _pProcess)
|
|
{
|
|
ReleaseProcess(_pProcess);
|
|
}
|
|
|
|
ASSERT(_pvRunAsHandle == NULL);
|
|
ASSERT(_hProcess == NULL);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::Release
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
DWORD
|
|
CServerTableEntry::Release()
|
|
{
|
|
DWORD Refs;
|
|
CSharedLock* pParentTableLock = _pParentTableLock;
|
|
|
|
CairoleDebugOut((DEB_SCM, "Releasing ServerTableEntry at 0x%p\n", this));
|
|
|
|
pParentTableLock->LockExclusive();
|
|
|
|
Dereference();
|
|
if ( 0 == (Refs = References()) )
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
pParentTableLock->UnlockExclusive();
|
|
|
|
return Refs;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::RegisterServer
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT
|
|
CServerTableEntry::RegisterServer(
|
|
IN CProcess * pServerProcess,
|
|
IN IPID ipid,
|
|
IN CClsidData * pClsidData, OPTIONAL
|
|
IN CAppidData * pAppidData,
|
|
IN UCHAR ServerState,
|
|
OUT DWORD * pRegistrationKey )
|
|
{
|
|
CServerListEntry * pEntry;
|
|
CSurrogateListEntry * pSurrogateEntry;
|
|
|
|
UCHAR Context = 0;
|
|
UCHAR SubContext = 0;
|
|
|
|
if (pClsidData)
|
|
{
|
|
if ( (pClsidData->ServerType() == SERVERTYPE_SERVICE) ||
|
|
(pClsidData->ServerType() == SERVERTYPE_COMPLUS_SVC) )
|
|
{
|
|
Context = SERVER_SERVICE;
|
|
}
|
|
else if ( pClsidData->HasRunAs() )
|
|
{
|
|
Context = SERVER_RUNAS;
|
|
|
|
if ( pClsidData->IsInteractiveUser() )
|
|
SubContext = SUB_CONTEXT_RUNAS_INTERACTIVE;
|
|
else
|
|
SubContext = SUB_CONTEXT_RUNAS_THIS_USER;
|
|
}
|
|
// Special case of COM+ library class being loaded by VB for debugging
|
|
else if ( pClsidData->ServerType() == SERVERTYPE_COMPLUS && pClsidData->IsInprocClass() )
|
|
{
|
|
Context = SERVER_RUNAS;
|
|
}
|
|
}
|
|
|
|
if ( _EntryType == ENTRY_TYPE_PROCESS )
|
|
{
|
|
// This is the COM+/COM surrogate case
|
|
ASSERT(NULL == pClsidData && "Process Entry Given Clsid data");
|
|
ASSERT(NULL != pAppidData && "Process Entry Not Given Appid data");
|
|
if (pAppidData->Service())
|
|
{
|
|
Context = SERVER_SERVICE;
|
|
}
|
|
else if ((pAppidData->IsInteractiveUser()) || pAppidData->RunAsUser())
|
|
{
|
|
Context = SERVER_RUNAS;
|
|
}
|
|
}
|
|
|
|
// If none of the above applied, Context will be zero at this point -- this
|
|
// essentially means the server should be treated as an activate-as-activator
|
|
// server. However, if Context is zero SubContext had better also be zero,
|
|
// and so we assert that here.
|
|
if (Context == 0)
|
|
{
|
|
Context = SERVER_ACTIVATOR;
|
|
ASSERT(SubContext == 0);
|
|
}
|
|
|
|
pEntry = new CServerListEntry(
|
|
this,
|
|
pServerProcess,
|
|
ipid,
|
|
Context,
|
|
ServerState,
|
|
SubContext);
|
|
|
|
if ( ! pEntry )
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Add registration to the process object -- but only if we are a class entry;
|
|
// otherwise this is a registration for a surrogate server
|
|
if ( _EntryType == ENTRY_TYPE_CLASS )
|
|
{
|
|
pEntry->AddToProcess( Guid() );
|
|
}
|
|
|
|
//
|
|
SetSuspendedFlagOnNewServer(pServerProcess);
|
|
|
|
//
|
|
// NOTE: The entry should be inserted in the lists only after
|
|
// all the registration specific stuff has been done. This will
|
|
// ensure that anyone outside this procedure sees the complete
|
|
// entry and not some half-baked one
|
|
//
|
|
|
|
ServerLock()->LockExclusive();
|
|
// Check if process handle exists implying that a
|
|
// launch is in progress for this class. Try to
|
|
// register runas,process, and threadtoken with
|
|
// the newly created entry
|
|
// Note that it is essential for the server lock to
|
|
// be held since _hProcess can change otherwise.
|
|
if (_hProcess || _pProcess)
|
|
RegisterHandles(pEntry, pServerProcess);
|
|
_ServerList.Insert( pEntry );
|
|
ServerLock()->UnlockExclusive();
|
|
|
|
//
|
|
// It's possible that a process could register itself as a surrogate but
|
|
// there's no CLSID info in the registry. We might want to disallow
|
|
// this kind of register.
|
|
//
|
|
// Note: We allow class registration if appid is not present but don't
|
|
// allow surrogate registration since it'll never be found
|
|
// The right thing to do would be to reject the call completely when
|
|
// an appid is not present but we will take the path of least resistance
|
|
// and preserve legacy
|
|
if ( pClsidData && (ServerState & SERVERSTATE_SURROGATE) && pClsidData->AppidString() )
|
|
{
|
|
pSurrogateEntry = new CSurrogateListEntry( pClsidData->AppidString(), pEntry );
|
|
|
|
if ( ! pSurrogateEntry )
|
|
{
|
|
pEntry->CReferencedObject::Release();
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
gpSurrogateList->Insert( pSurrogateEntry );
|
|
}
|
|
|
|
*pRegistrationKey = pEntry->RegistrationKey();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::RevokeServer -- version 1 for class entries
|
|
//
|
|
// CODE WORK: Consider defining two separate classes for class and
|
|
// process entries so we can avoid this sort of thing
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CServerTableEntry::RevokeServer(
|
|
IN CProcess * pServerProcess,
|
|
IN DWORD RegistrationKey
|
|
)
|
|
{
|
|
ASSERT(_EntryType == ENTRY_TYPE_CLASS);
|
|
|
|
ServerLock()->LockExclusive();
|
|
|
|
CServerListEntry * pEntry = (CServerListEntry *) _ServerList.First();
|
|
|
|
for ( ; pEntry; pEntry = (CServerListEntry *) pEntry->Next() )
|
|
{
|
|
if ( (RegistrationKey == pEntry->_RegistrationKey)
|
|
&& (pServerProcess == pEntry->_pServerProcess)
|
|
)
|
|
break;
|
|
}
|
|
|
|
if ( pEntry )
|
|
_ServerList.Remove( pEntry );
|
|
|
|
ServerLock()->UnlockExclusive();
|
|
|
|
if ( pEntry )
|
|
{
|
|
pEntry->RemoveFromProcess();
|
|
pEntry->Release();
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::RevokeServer -- version 2 for process entries
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CServerTableEntry::RevokeServer(
|
|
IN ScmProcessReg * pScmProcessReg
|
|
)
|
|
{
|
|
ASSERT(_EntryType == ENTRY_TYPE_PROCESS);
|
|
|
|
ServerLock()->LockExclusive();
|
|
|
|
CServerListEntry * pEntry = (CServerListEntry *) _ServerList.First();
|
|
|
|
for ( ; pEntry; pEntry = (CServerListEntry *) pEntry->Next() )
|
|
{
|
|
if ( (pScmProcessReg->RegistrationToken == pEntry->_RegistrationKey)
|
|
)
|
|
break;
|
|
}
|
|
|
|
if ( pEntry )
|
|
_ServerList.Remove( pEntry );
|
|
|
|
ServerLock()->UnlockExclusive();
|
|
|
|
if ( pEntry )
|
|
{
|
|
pEntry->RemoveFromProcess();
|
|
pEntry->Release();
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::LookupServer
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
CServerTableEntry::LookupServer(
|
|
IN CToken * pClientToken,
|
|
IN BOOL bRemoteActivation,
|
|
IN BOOL bClientImpersonating,
|
|
IN WCHAR * pwszWinstaDesktop,
|
|
IN DWORD dwFlags,
|
|
IN LONG lThreadToken,
|
|
IN LONG lSessionID,
|
|
IN DWORD pid,
|
|
IN DWORD dwProcessReqType,
|
|
OUT CServerListEntry ** ppServerListEntry)
|
|
{
|
|
CServerListEntry * pEntry;
|
|
|
|
*ppServerListEntry = NULL;
|
|
|
|
// If we are suspended, no need to go any further
|
|
if (IsSuspended())
|
|
return FALSE;
|
|
|
|
ServerLock()->LockShared();
|
|
|
|
pEntry = (CServerListEntry *) _ServerList.First();
|
|
|
|
for ( ; pEntry; pEntry = (CServerListEntry *) pEntry->Next() )
|
|
{
|
|
if ( pEntry->Match( pClientToken,
|
|
bRemoteActivation,
|
|
bClientImpersonating,
|
|
pwszWinstaDesktop,
|
|
FALSE,
|
|
lThreadToken,
|
|
lSessionID,
|
|
pid,
|
|
dwProcessReqType,
|
|
dwFlags ) )
|
|
{
|
|
pEntry->Reference();
|
|
break;
|
|
}
|
|
}
|
|
|
|
ServerLock()->UnlockShared();
|
|
|
|
*ppServerListEntry = pEntry;
|
|
return (pEntry != 0);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::LookupServer
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
CServerTableEntry::LookupServer(
|
|
IN DWORD RegistrationKey,
|
|
CServerListEntry ** ppServerListEntry )
|
|
{
|
|
CServerListEntry * pEntry;
|
|
|
|
ServerLock()->LockShared();
|
|
|
|
pEntry = (CServerListEntry *) _ServerList.First();
|
|
|
|
for ( ; pEntry; pEntry = (CServerListEntry *) pEntry->Next() )
|
|
{
|
|
if ( RegistrationKey == pEntry->_RegistrationKey )
|
|
break;
|
|
}
|
|
|
|
if ( pEntry )
|
|
pEntry->Reference();
|
|
|
|
ServerLock()->UnlockShared();
|
|
|
|
*ppServerListEntry = pEntry;
|
|
return (pEntry != 0);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::UnsuspendServer
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
CServerTableEntry::UnsuspendServer(
|
|
IN DWORD RegistrationKey
|
|
)
|
|
{
|
|
CServerListEntry * pEntry;
|
|
|
|
LookupServer( RegistrationKey, &pEntry );
|
|
|
|
if ( pEntry )
|
|
{
|
|
pEntry->_State &= ~SERVERSTATE_SUSPENDED;
|
|
pEntry->Release();
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// CServerTableEntry::ServerExists
|
|
//+-------------------------------------------------------------------------
|
|
HRESULT
|
|
CServerTableEntry::ServerExists(
|
|
IN ACTIVATION_PARAMS * pActParams, BOOL *pfExist)
|
|
{
|
|
LONG lSessionID = INVALID_SESSION_ID;
|
|
ServerLock()->LockShared();
|
|
|
|
GetSessionIDFromActParams(pActParams->pActPropsIn, &lSessionID);
|
|
|
|
CServerListEntry *pEntry = (CServerListEntry *) _ServerList.First();
|
|
|
|
for ( ; pEntry; pEntry = (CServerListEntry *) pEntry->Next() )
|
|
{
|
|
if ( pEntry->Match(
|
|
pActParams->pToken,
|
|
pActParams->RemoteActivation,
|
|
pActParams->bClientImpersonating,
|
|
pActParams->pProcess ? pActParams->pProcess->WinstaDesktop() : NULL ,
|
|
FALSE,
|
|
0,
|
|
lSessionID ))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
ServerLock()->UnlockShared();
|
|
|
|
*pfExist = (pEntry != NULL);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::CallRunningServer
|
|
// Synopsis: The behavior of CallRunningServer is best described by this matrix
|
|
//
|
|
// If the call is made successfully then we return
|
|
// If CallServer() returns error then we take the following action
|
|
// ----------------------------------------------------------------------------
|
|
// | | RPC_E_SERVERFAULT | CO_E_SERVER_STOPPING | Other Errors |
|
|
// ----------------------------------------------------------------------------
|
|
// | COM | Kill the process | Remove the list entry |Remove the list entry|
|
|
// | | Remove the list entry | Try launching another |Try launching another|
|
|
// | | Try launching another | process |process |
|
|
// | | process | | |
|
|
// | | | | |
|
|
// -----------------------------------------------------------------------------
|
|
// | COM+| Kill the process | Set a timeout period |If process exists |
|
|
// | | Remove the list entry | If the server |then |
|
|
// | | Return the error | stops before timeout | return error |
|
|
// | | | then |else |
|
|
// | | | try launching | Remove the entry |
|
|
// | | | another process | try launching |
|
|
// | | | else | another process |
|
|
// | | | return error | |
|
|
// -----------------------------------------------------------------------------
|
|
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
CServerTableEntry::CallRunningServer(
|
|
IN ACTIVATION_PARAMS * pActParams,
|
|
IN DWORD dwFlags,
|
|
IN LONG lLaunchThreadToken,
|
|
IN CClsidData * pClsidData,
|
|
OUT HRESULT * phr
|
|
)
|
|
{
|
|
BOOL bStatus;
|
|
LONG lSessionID = INVALID_SESSION_ID;
|
|
CServerListEntry* pEntry;
|
|
|
|
GetSessionIDFromActParams(pActParams->pActPropsIn, &lSessionID);
|
|
|
|
for (;;)
|
|
{
|
|
BOOL bTerminatedServer;
|
|
|
|
bStatus = LookupServer(
|
|
pActParams->pToken,
|
|
pActParams->RemoteActivation,
|
|
pActParams->bClientImpersonating,
|
|
pActParams->pProcess ? pActParams->pProcess->WinstaDesktop() : NULL ,
|
|
dwFlags,
|
|
lLaunchThreadToken,
|
|
lSessionID,
|
|
pActParams->dwPID,
|
|
pActParams->dwProcessReqType,
|
|
&pEntry);
|
|
if (!bStatus)
|
|
{
|
|
ASSERT(!pEntry);
|
|
return FALSE;
|
|
}
|
|
|
|
*phr = S_OK;
|
|
|
|
//
|
|
// If the entry is "initializing", then it isn't really ready yet.
|
|
// Wait for it to become so.
|
|
//
|
|
if (pEntry->IsInitializing())
|
|
{
|
|
*phr = WaitForInitCompleted(pEntry, pClsidData);
|
|
if (FAILED(*phr))
|
|
{
|
|
if (*phr == CO_E_SERVER_STOPPING)
|
|
bStatus = FALSE;
|
|
else
|
|
bStatus = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Returns TRUE if we successfully make a call to this server, or
|
|
// if an unrecoverable non-server-related error is encountered
|
|
// (e.g, an out-of-mem failure while allocating data before or
|
|
// after the call).
|
|
//
|
|
// The final HRESULT is not necessarily S_OK.
|
|
//
|
|
if (SUCCEEDED(*phr))
|
|
bStatus = pEntry->CallServer(pActParams, phr);
|
|
if (!bStatus)
|
|
{
|
|
// We were unable to talk to the server, or got back
|
|
// CO_E_SERVER_STOPPING.
|
|
|
|
// Retrieve the process handle, if we have one
|
|
HANDLE hProcess = pEntry->GetProcess()->GetProcessHandle();
|
|
|
|
bTerminatedServer = FALSE;
|
|
|
|
// Try to kill the server if it returned RPC_E_SERVERFAULT (this
|
|
// means that we caught an unhandled exception somewhere in the
|
|
// server, which we don't really appreciate.)
|
|
if(*phr == RPC_E_SERVERFAULT)
|
|
{
|
|
if(hProcess)
|
|
{
|
|
bTerminatedServer = TerminateProcess(hProcess, 0);
|
|
}
|
|
|
|
// Remember that the server blew up on us. This is mostly
|
|
// for debugging purposes
|
|
pEntry->IncServerFaults();
|
|
}
|
|
|
|
//
|
|
// Determine if we need to remove the server entry. We will do this if
|
|
// a) we killed the server process up above; or b) the server returned
|
|
// CO_E_SERVER_STOPPING; or c) the server process is dead already.
|
|
//
|
|
if (bTerminatedServer ||
|
|
(*phr == CO_E_SERVER_STOPPING) ||
|
|
pEntry->ServerDied())
|
|
{
|
|
BOOL bRemoved;
|
|
|
|
//
|
|
// Remove this server entry if it's still in the list.
|
|
//
|
|
ServerLock()->LockExclusive();
|
|
|
|
bRemoved = _ServerList.InList(pEntry);
|
|
if (bRemoved)
|
|
_ServerList.Remove(pEntry);
|
|
|
|
ServerLock()->UnlockExclusive();
|
|
|
|
// Need to remove this entry from the surrogate list as
|
|
// well, if applicable
|
|
(void)gpSurrogateList->RemoveMatchingEntry(pEntry);
|
|
|
|
if (bRemoved)
|
|
{
|
|
pEntry->RemoveFromProcess();
|
|
pEntry->Release();
|
|
}
|
|
}
|
|
// 6ba => we should not retry calls into the server
|
|
else if (RPC_S_SERVER_UNAVAILABLE == HRESULT_CODE(*phr))
|
|
{
|
|
bStatus = TRUE;
|
|
}
|
|
}
|
|
|
|
// Now allow threads other than launching one to access
|
|
// this entry. Exit since we matched launched server
|
|
// regardless of result since we know that we will
|
|
// not get any more matches.
|
|
if (lLaunchThreadToken)
|
|
{
|
|
pEntry->SetThreadToken(0);
|
|
pEntry->Release();
|
|
return bStatus;
|
|
}
|
|
|
|
pEntry->Release();
|
|
|
|
if ( bStatus )
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::StartServerAndWait
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT
|
|
CServerTableEntry::StartServerAndWait(
|
|
IN ACTIVATION_PARAMS * pActParams,
|
|
IN CClsidData * pClsidData,
|
|
IN LONG &lLaunchThreadToken
|
|
)
|
|
{
|
|
SC_HANDLE hService;
|
|
HRESULT hr;
|
|
BOOL bStatus;
|
|
BOOL bServiceAlreadyRunning = FALSE;
|
|
DWORD dwActvFlags;
|
|
|
|
if ( ! pClsidData->LaunchAllowed( pActParams->pToken, pActParams->ClsContext ) )
|
|
return E_ACCESSDENIED;
|
|
|
|
// Check CreateProcessMemoryGate
|
|
// [a-sergiv (Sergei O. Ivanov) 11/1/99 Implemented Memory Gates in COM Base
|
|
if(pActParams->pActPropsIn)
|
|
{
|
|
BOOL bResult = TRUE;
|
|
IClassClassicInfo *pClassicInfo = NULL;
|
|
hr = pActParams->pActPropsIn->GetClassInfo(IID_IClassClassicInfo, (void**) &pClassicInfo);
|
|
|
|
if(SUCCEEDED(hr) && pClassicInfo)
|
|
{
|
|
IResourceGates *pResGates = NULL;
|
|
hr = pClassicInfo->GetProcess(IID_IResourceGates, (void**) &pResGates);
|
|
pClassicInfo->Release();
|
|
|
|
if(SUCCEEDED(hr) && pResGates)
|
|
{
|
|
hr = pResGates->Test(CreateProcessMemoryGate, &bResult);
|
|
pResGates->Release();
|
|
|
|
if(SUCCEEDED(hr) && !bResult)
|
|
{
|
|
// The gate said NO!
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hService = 0;
|
|
|
|
// NOTE: _hProcess, _dwProcessId, _pProcess and all other process specific
|
|
// variables that are part of CServerTableEntry are used only during the
|
|
// launch. Once the launch is completed either they are transferred to
|
|
// process specific entries or discarded. See CServerTableEntry::RegisterHandles
|
|
// for an example.
|
|
_dwProcessId = 0;
|
|
|
|
// _hProcess and _pProcess variables are released and set to
|
|
// null whether
|
|
// (1) This is the first launch
|
|
// OR
|
|
// (2) We succeeded in an earlier launch or failed
|
|
ASSERT((0 == _hProcess) && (NULL == _pProcess));
|
|
|
|
//
|
|
// Get the register event for the server we're about to launch. Note
|
|
// there exists a problem in that we may launch a server, but another
|
|
// server may register the desired clsid\appid at about the same time.
|
|
// I think this is just something we have to live with, especially since
|
|
// there are servers in the world that don't register with us, but instead
|
|
// turn around and launch another process to do the registration. So,
|
|
// even if we have a process handle it would be impossible to always
|
|
// correlate from register event back to the launching table entry.
|
|
//
|
|
CNamedObject* pRegisterEvent = pClsidData->ServerRegisterEvent();
|
|
if (!pRegisterEvent)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (pClsidData->ServerType() == SERVERTYPE_SURROGATE)
|
|
{
|
|
ASSERT(_EntryType == ENTRY_TYPE_CLASS);
|
|
|
|
LPWSTR pwszAppidString = pClsidData->AppidString();
|
|
CSurrogateListEntry * pSurrogateListEntry = NULL;
|
|
|
|
if(pwszAppidString)
|
|
{
|
|
pSurrogateListEntry = gpSurrogateList->Lookup(
|
|
pActParams->pToken,
|
|
pActParams->RemoteActivation,
|
|
pActParams->bClientImpersonating,
|
|
pActParams->pProcess ? pActParams->pProcess->WinstaDesktop() : NULL ,
|
|
pwszAppidString );
|
|
}
|
|
|
|
if ( pSurrogateListEntry )
|
|
{
|
|
// If we find a surrogate entry and we are inside a launch
|
|
// server then that implies that a surrogate was launched for
|
|
// a different class' activation. We have a process entry for
|
|
// this surrogate and we set it here. Later when RegisterServer
|
|
// gets called we use the process object to set the state.
|
|
// For all other cases, the state is set using the process handle.
|
|
_pProcess = pSurrogateListEntry->Process();
|
|
ASSERT(NULL !=_pProcess);
|
|
// We will release this on every path below
|
|
ReferenceProcess( _pProcess, TRUE );
|
|
|
|
// Remember the launching thread's token
|
|
_lThreadToken = lLaunchThreadToken;
|
|
|
|
// Load the dll that will service the activation request
|
|
bStatus = pSurrogateListEntry->LoadDll( pActParams, &hr );
|
|
pSurrogateListEntry->Release();
|
|
|
|
// A status of TRUE implies that we successfully talked to the
|
|
// server
|
|
if (bStatus)
|
|
{
|
|
// A successful hresult implies that the server was able to
|
|
// load the dll successfully and hence we expect it to
|
|
// register back
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
// Wait for the loaded object server to register back
|
|
bStatus = WaitForSingleObject( pRegisterEvent->Handle(), 30000 );
|
|
}
|
|
// If the server failed to load the dll or it registered back
|
|
// within the timeout then we return immediately
|
|
if ( FAILED(hr) || (bStatus == WAIT_OBJECT_0) )
|
|
{
|
|
ReleaseProcess(_pProcess);
|
|
_pProcess = NULL;
|
|
pRegisterEvent->Release();
|
|
return hr;
|
|
}
|
|
}
|
|
// If a launched server failed to register back in the given
|
|
// timeout or we were not able to talk to the server then we
|
|
// continue and try to launch a new process
|
|
ReleaseProcess(_pProcess);
|
|
_pProcess = NULL;
|
|
}
|
|
}
|
|
|
|
if ((pClsidData->ServerType() == SERVERTYPE_SERVICE) ||
|
|
(pClsidData->ServerType() == SERVERTYPE_COMPLUS_SVC))
|
|
{
|
|
// Set the token here as service does not register back
|
|
// with us and so it cannot be set at registration time
|
|
// unlike the activator server case below
|
|
|
|
hr = pClsidData->LaunchService( pActParams->pToken, pActParams->ClsContext, &hService );
|
|
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_SERVICE_ALREADY_RUNNING))
|
|
{
|
|
hr = S_OK;
|
|
bServiceAlreadyRunning = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//When a "RunAs Interactive User" server is launched from a Hydra session,
|
|
//the server runs in the Hydra session of the caller. In this case,
|
|
//we want to call LaunchActivator server instead of LaunchRunAsServer
|
|
//so that the server runs in the appropriate Hydra session.
|
|
|
|
_lThreadToken = lLaunchThreadToken;
|
|
|
|
if ( pClsidData->HasRunAs())
|
|
{
|
|
hr = pClsidData->LaunchRunAsServer(
|
|
pActParams->pToken,
|
|
pActParams->RemoteActivation,
|
|
pActParams->pActPropsIn,
|
|
pActParams->ClsContext,
|
|
&_hProcess,
|
|
&_dwProcessId,
|
|
&_pvRunAsHandle);
|
|
}
|
|
else
|
|
{
|
|
// Check to see if the client requested "protected" activation. If
|
|
// so, we refuse to launch a server using their token.
|
|
hr = pActParams->pActPropsIn->GetActivationFlags(&dwActvFlags);
|
|
if (FAILED(hr) || (dwActvFlags & ACTVFLAGS_DISABLE_AAA))
|
|
{
|
|
hr = E_ACCESSDENIED;
|
|
}
|
|
|
|
// If client came in unsecure, reject the activation
|
|
if (pActParams->UnsecureActivation)
|
|
{
|
|
hr = E_ACCESSDENIED;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pClsidData->LaunchActivatorServer(
|
|
pActParams->pToken,
|
|
pActParams->pEnvBlock,
|
|
pActParams->EnvBlockLength,
|
|
pActParams->RemoteActivation,
|
|
pActParams->bClientImpersonating,
|
|
pActParams->pProcess ? pActParams->pProcess->WinstaDesktop() : NULL,
|
|
pActParams->ClsContext,
|
|
&_hProcess,
|
|
&_dwProcessId);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(_pvRunAsHandle == NULL);
|
|
_lThreadToken = 0;
|
|
pRegisterEvent->Release();
|
|
return hr;
|
|
}
|
|
|
|
|
|
// we got this far so it is time to wait for the server/service we just
|
|
// launched to register itself with us appropriately and in a timely fashion
|
|
|
|
// Retry once if we fail to get desktop resources the first time
|
|
BOOL fSuccessful = FALSE;
|
|
for (int i=0; i<2; i++) {
|
|
|
|
ULONG winerr=0;
|
|
fSuccessful = FALSE;
|
|
|
|
switch (pClsidData->ServerType())
|
|
{
|
|
case SERVERTYPE_SERVICE:
|
|
case SERVERTYPE_COMPLUS_SVC:
|
|
ASSERT(hService && "Waiting for service with NULL handle");
|
|
fSuccessful = WaitForService(hService, pRegisterEvent->Handle(),
|
|
bServiceAlreadyRunning);
|
|
CloseServiceHandle( hService );
|
|
hService = 0;
|
|
break;
|
|
|
|
case SERVERTYPE_COMPLUS:
|
|
case SERVERTYPE_DLLHOST:
|
|
fSuccessful = WaitForDllhostServer(pRegisterEvent->Handle(),
|
|
pActParams,
|
|
winerr,
|
|
lLaunchThreadToken);
|
|
break;
|
|
|
|
default:
|
|
fSuccessful = WaitForLocalServer(pRegisterEvent->Handle(),
|
|
winerr);
|
|
break;
|
|
}
|
|
|
|
if ( (i==0) && _pvRunAsHandle && (!fSuccessful ) &&
|
|
(winerr == ERROR_WAIT_NO_CHILDREN))
|
|
{
|
|
RunAsInvalidateAndRelease(_pvRunAsHandle);
|
|
_pvRunAsHandle = NULL;
|
|
hr = pClsidData->LaunchRunAsServer(
|
|
pActParams->pToken,
|
|
pActParams->RemoteActivation,
|
|
pActParams->pActPropsIn,
|
|
pActParams->ClsContext,
|
|
&_hProcess,
|
|
&_dwProcessId,
|
|
&_pvRunAsHandle);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
ASSERT(_pvRunAsHandle == NULL);
|
|
pRegisterEvent->Release();
|
|
return hr;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if ( !fSuccessful )
|
|
{
|
|
GUID ServerGuid = Guid();
|
|
LogRegisterTimeout( &ServerGuid, pActParams->ClsContext, pActParams->pToken );
|
|
hr = CO_E_SERVER_EXEC_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
// Ensure that we are serializing around
|
|
// registration which takes an exclusive
|
|
// before modifying this and is the only
|
|
// one that needs to look at or modify it.
|
|
if (_hProcess)
|
|
{
|
|
|
|
ServerLock()->LockShared();
|
|
|
|
if (_hProcess)
|
|
{
|
|
CloseHandle(_hProcess);
|
|
_hProcess = 0;
|
|
}
|
|
|
|
ServerLock()->UnlockShared();
|
|
|
|
}
|
|
|
|
|
|
// If we still have the token set the callers also to
|
|
// NULL because we might have succeeded the launch but
|
|
// the server registering back was different from
|
|
// the one launched(this should be very rare).
|
|
if (_lThreadToken)
|
|
{
|
|
_lThreadToken = 0;
|
|
lLaunchThreadToken = 0;
|
|
}
|
|
|
|
if (_pvRunAsHandle)
|
|
{
|
|
ASSERT(pClsidData->HasRunAs());
|
|
RunAsRelease(_pvRunAsHandle);
|
|
_pvRunAsHandle = NULL;
|
|
}
|
|
|
|
pRegisterEvent->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::RegisterHandles
|
|
//
|
|
// Synopsis Register either the RunAs or the process handle with the
|
|
// CProcess object. Also register Single use Thread token.
|
|
//
|
|
// NOTE: this is called by RegisterServer
|
|
//--------------------------------------------------------------------------
|
|
BOOL CServerTableEntry::RegisterHandles(IN CServerListEntry * pEntry,
|
|
IN CProcess *pServerProcess)
|
|
{
|
|
BOOL bStatus = TRUE;
|
|
|
|
// Are we interested in registering the process handle
|
|
if(_hProcess || _pProcess)
|
|
{
|
|
ASSERT(pServerProcess);
|
|
// We can have either a process handle if this entry is the one
|
|
// which was the first to launch the server or we can have a process
|
|
// object if the server has already been launched. The two cases are
|
|
// mutually exclusive and we assert that here.
|
|
ASSERT((_hProcess && (NULL == _pProcess)) ||
|
|
(0 == _hProcess && (NULL != _pProcess)));
|
|
if(_hProcess)
|
|
bStatus = pServerProcess->SetProcessHandle(_hProcess,_dwProcessId);
|
|
|
|
// If a FALSE status is returned then the PID of the process launched
|
|
// by us did not match the PID of the process registered.
|
|
// The only cases where this will be FALSE are if a server is
|
|
// started by hand at the same time as a system launch or the
|
|
// process registering was launched by the one we launched.
|
|
// This should almost never happen
|
|
if (!bStatus)
|
|
goto ExitRegisterHandles;
|
|
|
|
// If the PID matched then we are sure that this register event
|
|
// matches the server we launched
|
|
|
|
|
|
// If we had a process match save thread token if
|
|
// it was passed since this thread launched the server
|
|
if (_lThreadToken)
|
|
{
|
|
pEntry->SetThreadToken(_lThreadToken);
|
|
_lThreadToken = NULL;
|
|
}
|
|
|
|
// Are we interested in registering the RunAs handle ?
|
|
if (_pvRunAsHandle)
|
|
{
|
|
// Pass our winsta reference over to process entry which will
|
|
// release it when it goes away so we can be rid of
|
|
// responsibilities for it.
|
|
RunAsSetWinstaDesktop(_pvRunAsHandle,
|
|
pServerProcess->WinstaDesktop());
|
|
pServerProcess->SetRunAsHandle(_pvRunAsHandle);
|
|
|
|
// Given Handle away so remove our reference
|
|
_pvRunAsHandle = NULL;
|
|
}
|
|
}
|
|
|
|
ExitRegisterHandles:
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::WaitForLocalServer
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
CServerTableEntry::WaitForLocalServer(
|
|
IN HANDLE hRegisterEvent,
|
|
IN ULONG &winerr
|
|
)
|
|
{
|
|
DWORD Status = WAIT_OBJECT_0+1;
|
|
BOOL bStatus = FALSE;
|
|
|
|
HANDLE Handles[2];
|
|
|
|
Handles[0] = hRegisterEvent;
|
|
Handles[1] = _hProcess;
|
|
|
|
// Wait for process and register events
|
|
Status = WaitForMultipleObjects( 2, Handles, FALSE, gServerStartTimeout );
|
|
|
|
if ( Status == (WAIT_OBJECT_0 + 1) )
|
|
{
|
|
// Launched Process went away but this could be the case
|
|
// where we have a launched server that starts another server
|
|
// which registers back(Lotus or something ??)
|
|
// So null the handle and wait for the register event for
|
|
// a much shorter time
|
|
GetExitCodeProcess(_hProcess, &winerr);
|
|
|
|
HANDLE hProcess;
|
|
|
|
CloseHandle( _hProcess );
|
|
_hProcess = 0;
|
|
|
|
Status = WaitForSingleObject( hRegisterEvent, gServerStartSecondChanceTimeout );
|
|
}
|
|
|
|
if ( Status != WAIT_OBJECT_0 && _hProcess )
|
|
TerminateProcess( _hProcess, 0 );
|
|
|
|
if ( Status == WAIT_OBJECT_0 )
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::WaitForDllhostServer
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CServerTableEntry::WaitForDllhostServer(
|
|
IN HANDLE hRegisterEvent,
|
|
IN ACTIVATION_PARAMS *pActParams,
|
|
IN ULONG &winerr,
|
|
IN LONG lThreadToken
|
|
)
|
|
{
|
|
DWORD Status = WAIT_OBJECT_0+1;
|
|
DWORD WaitedMilliSecondsToStart = 0;
|
|
|
|
CServerListEntry * pEntry = NULL;
|
|
BOOL bStatus;
|
|
CProcess *pProcess=NULL;
|
|
|
|
HANDLE Handles[2];
|
|
|
|
Handles[0] = hRegisterEvent;
|
|
Handles[1] = _hProcess;
|
|
|
|
LONG lSessionID = INVALID_SESSION_ID;
|
|
GetSessionIDFromActParams(pActParams->pActPropsIn, &lSessionID);
|
|
|
|
for (;;)
|
|
{
|
|
// Wait for process and register events
|
|
Status = WaitForMultipleObjects( 2, Handles,
|
|
FALSE, gDllhostServerStartTimeout );
|
|
|
|
if ( ( Status == WAIT_OBJECT_0 ) ||
|
|
( Status == (WAIT_OBJECT_0 + 1 )))
|
|
break;
|
|
|
|
if (pEntry == NULL)
|
|
{
|
|
bStatus = LookupServer(
|
|
pActParams->pToken,
|
|
pActParams->RemoteActivation,
|
|
pActParams->bClientImpersonating,
|
|
pActParams->pProcess ? pActParams->pProcess->WinstaDesktop() : NULL ,
|
|
MATCHFLAG_ALLOW_SUSPENDED, // Dllhost is suspended now, we still want to find it!
|
|
lThreadToken,
|
|
lSessionID,
|
|
0, // no need for a pid here
|
|
PRT_IGNORE, // no need for prt here
|
|
&pEntry);
|
|
|
|
if (!bStatus)
|
|
pEntry = NULL;
|
|
else
|
|
{
|
|
pProcess = pEntry->_pServerProcess;
|
|
ASSERT(pProcess != NULL);
|
|
}
|
|
}
|
|
|
|
ScmProcessReg *pScmProcessReg=NULL;
|
|
if (pProcess)
|
|
{
|
|
pScmProcessReg = pProcess->GetProcessReg();
|
|
}
|
|
|
|
// If the server died in the middle of an activationand context rundown ocured, then
|
|
// pScmProcessReg is NULL
|
|
if (! pScmProcessReg )
|
|
{
|
|
// ProcessActivatorStarted not called yet
|
|
WaitedMilliSecondsToStart += gDllhostServerStartTimeout;
|
|
if (WaitedMilliSecondsToStart >= gDllhostMaxServerStartTimeout)
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
// ProcessActivatorStarted has been called, how long since last call from
|
|
// initial process activator?
|
|
|
|
// handles wraparound
|
|
CTime CurrentTime;
|
|
DWORD WaitedMilliSecondsToReady = CurrentTime - pScmProcessReg->TimeOfLastPing;
|
|
|
|
if (WaitedMilliSecondsToReady >= gDllhostMaxServerStartTimeout)
|
|
break;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
if (pEntry)
|
|
pEntry->Release();
|
|
|
|
if ( Status == WAIT_OBJECT_0 )
|
|
return TRUE;
|
|
else
|
|
{
|
|
GetExitCodeProcess(_hProcess, &winerr);
|
|
TerminateProcess( _hProcess, 0 );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::WaitForService
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CServerTableEntry::WaitForService(
|
|
IN SC_HANDLE hService,
|
|
IN HANDLE hRegisterEvent,
|
|
IN BOOL bServiceAlreadyRunning
|
|
)
|
|
{
|
|
SERVICE_STATUS ServiceStatus;
|
|
DWORD Status = WAIT_FAILED;
|
|
BOOL bStatus;
|
|
BOOL bKeepLooping;
|
|
BOOL bStopService = FALSE;
|
|
|
|
for (DWORD i = 0; i < gNTServiceMaxTimeouts; i++)
|
|
{
|
|
Status = WaitForSingleObject( hRegisterEvent, gNTServiceInterrogatePeriod );
|
|
if (Status == WAIT_OBJECT_0)
|
|
{
|
|
// register event was signalled, just return
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Assume we are done
|
|
bKeepLooping = FALSE;
|
|
|
|
bStatus = ControlService( hService,
|
|
SERVICE_CONTROL_INTERROGATE,
|
|
&ServiceStatus );
|
|
if (bStatus)
|
|
{
|
|
switch ( ServiceStatus.dwCurrentState )
|
|
{
|
|
case SERVICE_STOPPED :
|
|
case SERVICE_STOP_PENDING :
|
|
// weirdness
|
|
break;
|
|
|
|
case SERVICE_RUNNING :
|
|
case SERVICE_START_PENDING :
|
|
case SERVICE_CONTINUE_PENDING :
|
|
// the service is taking its own sweet time. Keep
|
|
// looping to give it more time.
|
|
bKeepLooping = TRUE;
|
|
break;
|
|
|
|
case SERVICE_PAUSE_PENDING :
|
|
case SERVICE_PAUSED :
|
|
// more weirdness
|
|
if (!bServiceAlreadyRunning)
|
|
bStopService = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
//else quit
|
|
|
|
if (!bKeepLooping)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bStopService )
|
|
{
|
|
(void) ControlService( hService,
|
|
SERVICE_CONTROL_STOP,
|
|
&ServiceStatus );
|
|
}
|
|
|
|
if ( Status == WAIT_OBJECT_0 )
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::WaitForInitCompleted
|
|
//
|
|
// Wait for an initializing server (dllhost) to finish initialization.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT
|
|
CServerTableEntry::WaitForInitCompleted(
|
|
IN CServerListEntry *pEntry,
|
|
IN CClsidData *pClsidData
|
|
)
|
|
{
|
|
ASSERT(pClsidData); // Should NOT call this without registration info.
|
|
if (NULL == pClsidData)
|
|
{
|
|
// If no clsid data, then we'll just need to return the
|
|
// "I'm initializing still" error code, since we have no
|
|
// way of waiting. I don't think this should ever happen,
|
|
// though.
|
|
return CO_E_SERVER_INIT_TIMEOUT;
|
|
}
|
|
|
|
CNamedObject* pInitEvent = pClsidData->ServerInitializedEvent();
|
|
if (!pInitEvent)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
if (pEntry->IsInitializing())
|
|
{
|
|
HANDLE hProcess = pEntry->_pServerProcess->GetProcessHandle();
|
|
|
|
DWORD dwRet;
|
|
if (hProcess)
|
|
{
|
|
HANDLE handles[2] = {pInitEvent->Handle(), hProcess};
|
|
dwRet = WaitForMultipleObjects(2, handles, FALSE, gDllhostInitializationTimeout);
|
|
}
|
|
else
|
|
{
|
|
dwRet = WaitForSingleObject(pInitEvent->Handle(), gDllhostInitializationTimeout);
|
|
}
|
|
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
hr = CO_E_SERVER_INIT_TIMEOUT;
|
|
}
|
|
else if (dwRet == WAIT_OBJECT_0)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else if (dwRet == WAIT_OBJECT_0+1)
|
|
{
|
|
hr = CO_E_SERVER_STOPPING;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
pInitEvent->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::GetServerListWithLock
|
|
//
|
|
// Returns a pointer to this entry's server list. Takes shared lock before
|
|
// returning. Caller is expected to call ReleaseSharedListLock to release
|
|
// the lock
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
CServerList* CServerTableEntry::GetServerListWithSharedLock()
|
|
{
|
|
ServerLock()->LockShared();
|
|
return &_ServerList;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CServerTableEntry::ReleaseSharedListLock
|
|
//
|
|
// Releases a shared lock on this entry's list lock.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
void CServerTableEntry::ReleaseSharedListLock()
|
|
{
|
|
ServerLock()->UnlockShared();
|
|
}
|
|
|
|
|
|
//
|
|
// CServerTableEntry::SuspendClass
|
|
//
|
|
// Turns on the suspended flag for this entry.
|
|
//
|
|
void CServerTableEntry::SuspendClass()
|
|
{
|
|
ASSERT(!_bSuspendedClsid &&
|
|
!_bSuspendedApplication &&
|
|
_EntryType == ENTRY_TYPE_CLASS);
|
|
|
|
_bSuspendedClsid = TRUE;
|
|
|
|
SetSuspendOnAllServers(TRUE);
|
|
}
|
|
|
|
//
|
|
// CServerTableEntry::UnsuspendClass
|
|
//
|
|
// Turns off the suspended flag for this entry.
|
|
//
|
|
void CServerTableEntry::UnsuspendClass()
|
|
{
|
|
ASSERT(_bSuspendedClsid &&
|
|
!_bSuspendedApplication &&
|
|
_EntryType == ENTRY_TYPE_CLASS);
|
|
|
|
_bSuspendedClsid = FALSE;
|
|
|
|
SetSuspendOnAllServers(FALSE);
|
|
}
|
|
|
|
//
|
|
// CServerTableEntry::RetireClass
|
|
//
|
|
// Retires all currently registered servers for
|
|
// this clsid.
|
|
//
|
|
void CServerTableEntry::RetireClass()
|
|
{
|
|
ASSERT(_EntryType == ENTRY_TYPE_CLASS);
|
|
|
|
RetireAllServers();
|
|
}
|
|
|
|
//
|
|
// CServerTableEntry::SuspendApplication
|
|
//
|
|
// Turns on the suspended flag for this entry.
|
|
//
|
|
void CServerTableEntry::SuspendApplication()
|
|
{
|
|
ASSERT(!_bSuspendedClsid &&
|
|
!_bSuspendedApplication &&
|
|
_EntryType == ENTRY_TYPE_PROCESS);
|
|
|
|
_bSuspendedClsid = TRUE;
|
|
}
|
|
|
|
//
|
|
// CServerTableEntry::UnsuspendApplication
|
|
//
|
|
// Turns on the suspended flag for this entry.
|
|
//
|
|
void CServerTableEntry::UnsuspendApplication()
|
|
{
|
|
ASSERT(!_bSuspendedClsid &&
|
|
_bSuspendedApplication &&
|
|
_EntryType == ENTRY_TYPE_PROCESS);
|
|
|
|
_bSuspendedClsid = FALSE;
|
|
}
|
|
|
|
//
|
|
// CServerTableEntry::RetireApplication
|
|
//
|
|
// Marks as retired all servers registered with
|
|
// this entry.
|
|
//
|
|
void CServerTableEntry::RetireApplication()
|
|
{
|
|
ASSERT(_EntryType == ENTRY_TYPE_PROCESS);
|
|
|
|
RetireAllServers();
|
|
}
|
|
|
|
//
|
|
// CServerTableEntry::IsSuspended
|
|
//
|
|
// Returns TRUE if servers of this type are
|
|
// currently suspended, FALSE otherwise.
|
|
//
|
|
BOOL CServerTableEntry::IsSuspended()
|
|
{
|
|
ASSERT(_EntryType == ENTRY_TYPE_CLASS ||
|
|
_EntryType == ENTRY_TYPE_PROCESS);
|
|
|
|
if (_EntryType == ENTRY_TYPE_CLASS)
|
|
return _bSuspendedClsid;
|
|
else
|
|
return _bSuspendedApplication;
|
|
}
|
|
|
|
|
|
//
|
|
// CServerTableEntry::SetSuspendOnAllServers
|
|
//
|
|
// Sets the suspended bit on all currently registered servers.
|
|
//
|
|
void CServerTableEntry::SetSuspendOnAllServers(BOOL bSuspended)
|
|
{
|
|
CServerListEntry * pEntry;
|
|
|
|
ServerLock()->LockShared();
|
|
|
|
for (pEntry = (CServerListEntry *) _ServerList.First();
|
|
pEntry;
|
|
pEntry = (CServerListEntry *) pEntry->Next() )
|
|
{
|
|
if (bSuspended)
|
|
pEntry->Suspend();
|
|
else
|
|
pEntry->Unsuspend();
|
|
}
|
|
|
|
ServerLock()->UnlockShared();
|
|
}
|
|
|
|
//
|
|
// CServerTableEntry::RetireAllServers
|
|
//
|
|
// Marks as retired all currently registered servers.
|
|
//
|
|
void CServerTableEntry::RetireAllServers()
|
|
{
|
|
CServerListEntry * pEntry;
|
|
|
|
ServerLock()->LockShared();
|
|
|
|
for (pEntry = (CServerListEntry *) _ServerList.First();
|
|
pEntry;
|
|
pEntry = (CServerListEntry *) pEntry->Next() )
|
|
{
|
|
pEntry->Retire();
|
|
}
|
|
|
|
ServerLock()->UnlockShared();
|
|
}
|
|
|
|
//
|
|
// CServerTableEntry::SetSuspendedFlagOnNewServer
|
|
//
|
|
// Helper function for when a new class factory registration/app
|
|
// comes along. If we are currently suspended, then we need to
|
|
// pass that state on to the new server. If we're not suspended,
|
|
// then there's nothing to do.
|
|
//
|
|
void CServerTableEntry::SetSuspendedFlagOnNewServer(CProcess* pprocess)
|
|
{
|
|
if ( (_EntryType == ENTRY_TYPE_CLASS) && _bSuspendedClsid)
|
|
{
|
|
pprocess->Suspend();
|
|
}
|
|
else if ( (_EntryType == ENTRY_TYPE_PROCESS) && _bSuspendedApplication)
|
|
{
|
|
pprocess->Suspend();
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// SCMRemoveRegistration Version 1: for class entries representing traditional
|
|
// local servers including custom surrogate servers.
|
|
//
|
|
// Called from CProcess::RevokeClassRegs() in the resolver.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
void
|
|
SCMRemoveRegistration(
|
|
CProcess * pProcess,
|
|
GUID & Guid,
|
|
DWORD Reg
|
|
)
|
|
{
|
|
CServerTableEntry * pClassTableEntry = NULL;
|
|
|
|
pClassTableEntry = gpClassTable->Lookup( Guid );
|
|
|
|
if ( pClassTableEntry )
|
|
{
|
|
pClassTableEntry->RevokeServer( pProcess, Reg );
|
|
pClassTableEntry->Release();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// SCMRemoveRegistration Version 2: for process entries representing
|
|
// new unified surrogate servers.
|
|
//
|
|
// Called from CProcess::RevokeClassRegs() in the resolver.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void
|
|
SCMRemoveRegistration(
|
|
ScmProcessReg * pScmProcessReg
|
|
)
|
|
{
|
|
CServerTableEntry * pProcessTableEntry = NULL;
|
|
|
|
pProcessTableEntry = gpProcessTable->Lookup( pScmProcessReg->ProcessGUID );
|
|
|
|
if ( pProcessTableEntry )
|
|
{
|
|
pProcessTableEntry->RevokeServer( pScmProcessReg );
|
|
pProcessTableEntry->Release();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SCMProcessCleanup(
|
|
const CProcess *pProcess
|
|
)
|
|
{
|
|
CSurrogateListEntry *pSurrogateEntry = gpSurrogateList->Lookup(pProcess);
|
|
if (pSurrogateEntry)
|
|
{
|
|
gpSurrogateList->Remove(pSurrogateEntry);
|
|
pSurrogateEntry->Release();
|
|
}
|
|
}
|
|
|