Leaked source code of windows server 2003
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

//******************************************************************************
//
// 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;
}