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.
590 lines
15 KiB
590 lines
15 KiB
//******************************************************************************
|
|
//
|
|
// QSINK.CPP
|
|
//
|
|
// Copyright (C) 1996-1999 Microsoft Corporation
|
|
//
|
|
//******************************************************************************
|
|
|
|
#include "precomp.h"
|
|
#include <stdio.h>
|
|
#include <genutils.h>
|
|
#include <cominit.h>
|
|
#include "ess.h"
|
|
#include "evsink.h"
|
|
#include "delivrec.h"
|
|
|
|
#define IN_SPIN_LOCK CInCritSec
|
|
#define MAX_EVENT_DELIVERY_SIZE 10000000
|
|
#define SLOWDOWN_DROP_LIMIT 1000
|
|
#define DELIVER_SPIN_COUNT 1000
|
|
|
|
/*****************************************************************************
|
|
CQueueingEventSink
|
|
******************************************************************************/
|
|
|
|
CQueueingEventSink::CQueueingEventSink(CEssNamespace* pNamespace)
|
|
: m_pNamespace(pNamespace), m_bDelivering(FALSE), m_dwTotalSize(0),
|
|
m_dwMaxSize(0xFFFFFFFF), m_wszName(NULL), m_bRecovering(FALSE),
|
|
m_hRecoveryComplete(NULL), m_hrRecovery(S_OK)
|
|
{
|
|
m_pNamespace->AddRef();
|
|
m_pNamespace->AddCache();
|
|
}
|
|
|
|
CQueueingEventSink::~CQueueingEventSink()
|
|
{
|
|
if ( m_hRecoveryComplete != NULL )
|
|
{
|
|
CloseHandle( m_hRecoveryComplete );
|
|
}
|
|
delete m_wszName;
|
|
m_pNamespace->RemoveCache();
|
|
m_pNamespace->Release();
|
|
}
|
|
|
|
HRESULT CQueueingEventSink::SetName( LPCWSTR wszName )
|
|
{
|
|
if ( m_wszName != NULL )
|
|
{
|
|
return WBEM_E_CRITICAL_ERROR;
|
|
}
|
|
|
|
m_wszName = new WCHAR[wcslen(wszName)+1];
|
|
|
|
if ( m_wszName == NULL )
|
|
{
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
StringCchCopyW( m_wszName, wcslen(wszName)+1, wszName );
|
|
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
|
|
|
|
STDMETHODIMP CQueueingEventSink::SecureIndicate( long lNumEvents,
|
|
IWbemEvent** apEvents,
|
|
BOOL bMaintainSecurity,
|
|
BOOL bSlowDown,
|
|
DWORD dwQoS,
|
|
CEventContext* pContext)
|
|
{
|
|
// BUGBUG: context. levn: no security implications at this level --- we
|
|
// are past the filter
|
|
|
|
HRESULT hres;
|
|
DWORD dwSleep = 0;
|
|
|
|
// If security needs to be maintained, record the calling security
|
|
// context
|
|
// ===============================================================
|
|
|
|
IWbemCallSecurity* pSecurity = NULL;
|
|
|
|
if(bMaintainSecurity && IsNT())
|
|
{
|
|
pSecurity = CWbemCallSecurity::CreateInst();
|
|
if (pSecurity == 0)
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
|
|
hres = pSecurity->CloneThreadContext(FALSE);
|
|
if(FAILED(hres))
|
|
{
|
|
pSecurity->Release();
|
|
return hres;
|
|
}
|
|
}
|
|
|
|
CReleaseMe rmpSecurity( pSecurity );
|
|
|
|
HRESULT hr;
|
|
|
|
BOOL bSchedule = FALSE;
|
|
|
|
for(int i = 0; i < lNumEvents; i++)
|
|
{
|
|
CWbemPtr<CDeliveryRecord> pRecord;
|
|
|
|
//
|
|
// TODO: Fix this so that we put multiple events in the record.
|
|
//
|
|
|
|
hr = GetDeliveryRecord( 1,
|
|
&apEvents[i],
|
|
dwQoS,
|
|
pContext,
|
|
pSecurity,
|
|
&pRecord );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
ERRORTRACE((LOG_ESS, "Couldn't create delivery record for %S "
|
|
" sink. HR = 0x%x\n", m_wszName, hr ));
|
|
ReportQosFailure( apEvents[i], hr );
|
|
continue;
|
|
}
|
|
|
|
DWORD dwThisSleep;
|
|
BOOL bFirst;
|
|
|
|
if( !AddRecord( pRecord, bSlowDown, &dwThisSleep, &bFirst) )
|
|
{
|
|
//
|
|
// make sure that we give the record a chance to perform any post
|
|
// deliver actions before getting rid of it.
|
|
//
|
|
pRecord->PostDeliverAction( NULL, S_OK );
|
|
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
dwSleep += dwThisSleep;
|
|
if(bFirst)
|
|
bSchedule = TRUE;
|
|
}
|
|
|
|
if(bSchedule)
|
|
{
|
|
// DeliverAll();
|
|
// TRACE((LOG_ESS, "Scheduling delivery!!\n"));
|
|
hres = m_pNamespace->ScheduleDelivery(this);
|
|
}
|
|
else
|
|
{
|
|
// TRACE((LOG_ESS, "NOT Scheduling delivery!!\n"));
|
|
hres = WBEM_S_FALSE;
|
|
}
|
|
|
|
if(dwSleep && bSlowDown)
|
|
m_pNamespace->AddSleepCharge(dwSleep);
|
|
|
|
return hres;
|
|
}
|
|
|
|
BOOL CQueueingEventSink::AddRecord( CDeliveryRecord* pRecord,
|
|
BOOL bSlowDown,
|
|
DWORD* pdwSleep,
|
|
BOOL* pbFirst )
|
|
{
|
|
// Inform the system of the additional space in the queue
|
|
// ======================================================
|
|
|
|
DWORD dwRecordSize = pRecord->GetTotalBytes();
|
|
|
|
pRecord->AddToCache( m_pNamespace, m_dwTotalSize, pdwSleep );
|
|
|
|
BOOL bDrop = FALSE;
|
|
|
|
// Check if the sleep is such as to cause us to drop the event
|
|
// ===========================================================
|
|
|
|
if(!bSlowDown && *pdwSleep > SLOWDOWN_DROP_LIMIT)
|
|
{
|
|
bDrop = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Check if our queue size is so large as to cause us to drop
|
|
// ==============================================================
|
|
|
|
if(m_dwTotalSize + dwRecordSize > m_dwMaxSize)
|
|
bDrop = TRUE;
|
|
}
|
|
|
|
if( bDrop )
|
|
{
|
|
//
|
|
// Report that we're dropping the events. Call for each event.
|
|
//
|
|
|
|
IWbemClassObject** apEvents = pRecord->GetEvents();
|
|
|
|
for( ULONG i=0; i < pRecord->GetNumEvents(); i++ )
|
|
{
|
|
ReportQueueOverflow( apEvents[i], m_dwTotalSize + dwRecordSize );
|
|
}
|
|
|
|
*pdwSleep = 0;
|
|
*pbFirst = FALSE;
|
|
}
|
|
else
|
|
{
|
|
IN_SPIN_LOCK isl(&m_sl);
|
|
|
|
*pbFirst = (m_qpEvents.GetQueueSize() == 0) && !m_bDelivering;
|
|
m_dwTotalSize += dwRecordSize;
|
|
|
|
if(!m_qpEvents.Enqueue(pRecord))
|
|
{
|
|
*pdwSleep = 0;
|
|
return FALSE;
|
|
}
|
|
pRecord->AddRef();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT CQueueingEventSink::DeliverAll()
|
|
{
|
|
HRESULT hr = WBEM_S_NO_ERROR;
|
|
BOOL bSomeLeft = TRUE;
|
|
|
|
while( bSomeLeft )
|
|
{
|
|
try
|
|
{
|
|
{
|
|
IN_SPIN_LOCK ics(&m_sl);
|
|
m_bDelivering = TRUE;
|
|
}
|
|
|
|
hr = DeliverSome( );
|
|
}
|
|
catch( CX_MemoryException )
|
|
{
|
|
hr = WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
catch ( ... )
|
|
{
|
|
hr = WBEM_E_FAILED;
|
|
}
|
|
|
|
{
|
|
IN_SPIN_LOCK ics(&m_sl);
|
|
m_bDelivering = FALSE;
|
|
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
bSomeLeft = (m_qpEvents.GetQueueSize() != 0);
|
|
}
|
|
else
|
|
{
|
|
m_qpEvents.Clear();
|
|
bSomeLeft = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CQueueingEventSink::ClearAll()
|
|
{
|
|
IN_SPIN_LOCK isl(&m_sl);
|
|
m_qpEvents.Clear();
|
|
}
|
|
|
|
#pragma optimize("", off)
|
|
void CQueueingEventSink::WaitABit()
|
|
{
|
|
SwitchToThread();
|
|
/*
|
|
int nCount = 0;
|
|
while(m_qpEvents.GetQueueSize() == 0 && nCount++ < DELIVER_SPIN_COUNT);
|
|
*/
|
|
}
|
|
#pragma optimize("", on)
|
|
|
|
|
|
HRESULT CQueueingEventSink::DeliverSome( )
|
|
{
|
|
// Retrieve records until maximum size is reached and while the same
|
|
// security context is used for all
|
|
// ==================================================================
|
|
|
|
CTempArray<CDeliveryRecord*> apRecords;
|
|
|
|
m_sl.Enter(); // CANNOT USE SCOPE BECAUSE CTempArray uses _alloca
|
|
DWORD dwMaxRecords = m_qpEvents.GetQueueSize();
|
|
m_sl.Leave();
|
|
|
|
if(!INIT_TEMP_ARRAY(apRecords, dwMaxRecords))
|
|
{
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
CDeliveryRecord* pEventRec;
|
|
DWORD dwDeliverySize = 0;
|
|
DWORD dwTotalEvents = 0;
|
|
int cRecords = 0;
|
|
LUID luidBatch;
|
|
IWbemCallSecurity* pBatchSecurity = NULL;
|
|
|
|
m_sl.Enter();
|
|
|
|
while( dwDeliverySize < GetMaxDeliverySize() &&
|
|
cRecords < dwMaxRecords &&
|
|
(pEventRec = m_qpEvents.Dequeue()) != NULL )
|
|
{
|
|
// Compare it to the last context
|
|
// ==============================
|
|
|
|
m_sl.Leave();
|
|
if( dwDeliverySize > 0 )
|
|
{
|
|
if(!DoesRecordFitBatch(pEventRec, pBatchSecurity, luidBatch))
|
|
{
|
|
// Put it back and that's it for the batch
|
|
// =======================================
|
|
|
|
IN_SPIN_LOCK ics(&m_sl);
|
|
m_qpEvents.Requeue(pEventRec);
|
|
|
|
m_sl.Enter();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// First --- record luid
|
|
// =====================
|
|
|
|
pBatchSecurity = pEventRec->GetCallSecurity();
|
|
|
|
if( pBatchSecurity )
|
|
{
|
|
pBatchSecurity->AddRef();
|
|
pBatchSecurity->GetAuthenticationId( luidBatch );
|
|
}
|
|
}
|
|
|
|
apRecords[cRecords++] = pEventRec;
|
|
dwTotalEvents += pEventRec->GetNumEvents();
|
|
|
|
// Matched batch parameters --- add it to the batch
|
|
// ================================================
|
|
|
|
DWORD dwRecordSize = pEventRec->GetTotalBytes();
|
|
|
|
m_dwTotalSize -= dwRecordSize;
|
|
dwDeliverySize += dwRecordSize;
|
|
|
|
//
|
|
// Remove this size from the total of events held
|
|
//
|
|
|
|
m_sl.Enter();
|
|
}
|
|
|
|
m_sl.Leave();
|
|
|
|
//
|
|
// we've now got one or more delivery records to handle.
|
|
//
|
|
|
|
//
|
|
// we now need to initialize the event array that we're going to indicate
|
|
// to the client.
|
|
//
|
|
|
|
CTempArray<IWbemClassObject*> apEvents;
|
|
|
|
if( !INIT_TEMP_ARRAY( apEvents, dwTotalEvents ))
|
|
{
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
//
|
|
// go through the delivery records and add their events to the
|
|
// events to deliver. Also perform any PreDeliverAction on the
|
|
// record.
|
|
//
|
|
|
|
CWbemPtr<ITransaction> pTxn;
|
|
HRESULT hr;
|
|
int cEvents = 0;
|
|
int i;
|
|
|
|
for(i=0; i < cRecords; i++ )
|
|
{
|
|
//if ( apRecords[i]->RequiresTransaction() && pTxn == NULL )
|
|
//{
|
|
// TODO : XACT - aquire txn from DTC.
|
|
//}
|
|
|
|
hr = apRecords[i]->PreDeliverAction( pTxn );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
//
|
|
// TODO : handle error reporting here.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
IWbemEvent** apRecordEvents = apRecords[i]->GetEvents();
|
|
DWORD cRecordEvents = apRecords[i]->GetNumEvents();
|
|
|
|
for( DWORD j=0; j < cRecordEvents; j++ )
|
|
{
|
|
apEvents[cEvents++] = apRecordEvents[j];
|
|
}
|
|
}
|
|
|
|
// Actually Deliver
|
|
// =======
|
|
|
|
HRESULT hres = WBEM_S_NO_ERROR;
|
|
|
|
if( dwDeliverySize > 0 )
|
|
{
|
|
//
|
|
// Error returns are already logged in ActuallyDeliver
|
|
// we do not need to return return value of DeliverEvents
|
|
//
|
|
hres = DeliverEvents( pBatchSecurity, cEvents, apEvents );
|
|
}
|
|
|
|
//
|
|
// call postdeliveryaction on all the records. Then clean them up.
|
|
//
|
|
|
|
for(i=0; i < cRecords; i++ )
|
|
{
|
|
apRecords[i]->PostDeliverAction( pTxn, hres );
|
|
apRecords[i]->Release();
|
|
}
|
|
|
|
// Release all of the events.
|
|
// ================
|
|
|
|
if( pBatchSecurity )
|
|
{
|
|
pBatchSecurity->Release();
|
|
}
|
|
|
|
// Check if we need to continue
|
|
// ============================
|
|
|
|
WaitABit();
|
|
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
|
|
HRESULT CQueueingEventSink::DeliverEvents(IWbemCallSecurity* pBatchSecurity,
|
|
long lNumEvents, IWbemEvent** apEvents)
|
|
{
|
|
HRESULT hres = WBEM_S_NO_ERROR;
|
|
IUnknown* pOldSec = NULL;
|
|
if(pBatchSecurity)
|
|
{
|
|
hres = WbemCoSwitchCallContext(pBatchSecurity, &pOldSec);
|
|
if(FAILED(hres))
|
|
{
|
|
// Unable to set security --- cannot deliver
|
|
// =========================================
|
|
|
|
return hres;
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
// BUGBUG: propagate context. levn: no security implications at this
|
|
// point --- we are past the filter
|
|
hres = ActuallyDeliver(lNumEvents, apEvents, (pBatchSecurity != NULL),
|
|
NULL);
|
|
}
|
|
|
|
if(pBatchSecurity)
|
|
{
|
|
IUnknown* pTemp;
|
|
|
|
HRESULT hr = WbemCoSwitchCallContext(pOldSec, &pTemp);
|
|
|
|
if ( FAILED( hr ) && SUCCEEDED( hres ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
BOOL CQueueingEventSink::DoesRecordFitBatch( CDeliveryRecord* pEventRec,
|
|
IWbemCallSecurity* pBatchSecurity,
|
|
LUID luidBatch )
|
|
{
|
|
IWbemCallSecurity* pEventSec = pEventRec->GetCallSecurity();
|
|
|
|
if( pEventSec != NULL || pBatchSecurity != NULL )
|
|
{
|
|
if( pEventSec == NULL || pBatchSecurity == NULL )
|
|
{
|
|
// Definite mistatch --- one NULL, one not
|
|
// =======================================
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
LUID luidThis;
|
|
pEventSec->GetAuthenticationId(luidThis);
|
|
|
|
if( luidThis.LowPart != luidBatch.LowPart ||
|
|
luidThis.HighPart != luidBatch.HighPart )
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
DWORD CQueueingEventSink::GetMaxDeliverySize()
|
|
{
|
|
return MAX_EVENT_DELIVERY_SIZE;
|
|
}
|
|
|
|
HRESULT CQueueingEventSink::GetDeliveryRecord( ULONG cEvents,
|
|
IWbemEvent** apEvents,
|
|
DWORD dwQos,
|
|
CEventContext* pContext,
|
|
IWbemCallSecurity* pCallSec,
|
|
CDeliveryRecord** ppRecord )
|
|
{
|
|
*ppRecord = NULL;
|
|
|
|
CWbemPtr<CDeliveryRecord> pRecord;
|
|
|
|
if ( dwQos == WMIMSG_FLAG_QOS_EXPRESS )
|
|
{
|
|
pRecord = new CExpressDeliveryRecord;
|
|
if ( pRecord == NULL )
|
|
{
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
HRESULT hr = pRecord->Initialize( apEvents, cEvents, pCallSec );
|
|
if ( FAILED(hr) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
|
|
pRecord->AddRef();
|
|
*ppRecord = pRecord;
|
|
|
|
return WBEM_S_NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|