646 lines
14 KiB
C++
646 lines
14 KiB
C++
//=============================================================================
|
|
//
|
|
// Copyright (c) 2000, Microsoft Corporation, All rights reserved
|
|
//
|
|
// Quota.CPP
|
|
//
|
|
// Implements the class that keeps track of quotas within ESS.
|
|
//
|
|
//=============================================================================
|
|
|
|
#include "precomp.h"
|
|
#include <stdio.h>
|
|
#include "ess.h"
|
|
#include "essutils.h"
|
|
#include "nsrep.h"
|
|
#include "Quota.h"
|
|
#include <cominit.h>
|
|
#include <callsec.h>
|
|
|
|
// Global instance.
|
|
CQuota g_quotas;
|
|
|
|
#define WMICOREQUOTAS_NAMESPACE L"root"
|
|
#define WMICOREQUOTAS_OBJPATH L"__ArbitratorConfiguration=@"
|
|
#define WMICOREQUOTAS_CLASS L"__ArbitratorConfiguration"
|
|
|
|
|
|
CUserInfo::CUserInfo() :
|
|
m_pData(NULL),
|
|
m_dwSize(0),
|
|
m_bAlloced(FALSE)
|
|
{
|
|
ZeroMemory(m_dwUserCount, sizeof(m_dwUserCount));
|
|
}
|
|
|
|
CUserInfo::CUserInfo(LPBYTE pData, DWORD dwSize) :
|
|
m_pData(pData),
|
|
m_dwSize(dwSize),
|
|
m_bAlloced(FALSE)
|
|
{
|
|
ZeroMemory(m_dwUserCount, sizeof(m_dwUserCount));
|
|
}
|
|
|
|
CUserInfo::~CUserInfo()
|
|
{
|
|
if (m_pData && m_bAlloced)
|
|
delete m_pData;
|
|
}
|
|
|
|
BOOL CUserInfo::CopyData(LPBYTE pData, DWORD dwSize)
|
|
{
|
|
BOOL bRet;
|
|
|
|
m_pData = new BYTE[dwSize];
|
|
if (m_pData)
|
|
{
|
|
memcpy(m_pData, pData, dwSize);
|
|
m_dwSize = dwSize;
|
|
m_bAlloced = TRUE;
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
bRet = FALSE;
|
|
|
|
return bRet;
|
|
}
|
|
|
|
const CUserInfo& CUserInfo::operator = (const CUserInfo& other)
|
|
{
|
|
if (other.m_bAlloced)
|
|
{
|
|
m_pData = other.m_pData;
|
|
m_dwSize = other.m_dwSize;
|
|
((CUserInfo&)other).m_bAlloced = FALSE;
|
|
m_bAlloced = TRUE;
|
|
}
|
|
else
|
|
CopyData(other.m_pData, other.m_dwSize);
|
|
|
|
memcpy(m_dwUserCount, other.m_dwUserCount, sizeof(m_dwUserCount));
|
|
|
|
return *this;
|
|
}
|
|
|
|
BOOL CUserInfo::Init(LPBYTE pData, DWORD dwSize)
|
|
{
|
|
BOOL bRet;
|
|
|
|
// See if we need to get the data out of the token.
|
|
if (!pData)
|
|
{
|
|
IWbemCallSecurity *pSecurity = NULL;
|
|
|
|
bRet = FALSE;
|
|
|
|
WbemCoGetCallContext(IID_IWbemCallSecurity, (void**) &pSecurity);
|
|
|
|
if (pSecurity)
|
|
{
|
|
// Get the client's SID.
|
|
TOKEN_USER tu;
|
|
DWORD dwLen = 0;
|
|
HANDLE hToken = pSecurity->GetToken();
|
|
|
|
GetTokenInformation(
|
|
hToken,
|
|
TokenUser,
|
|
&tu,
|
|
sizeof(tu),
|
|
&dwLen);
|
|
|
|
if (dwLen != 0)
|
|
{
|
|
BYTE *pTemp = new BYTE[dwLen];
|
|
DWORD dwRealLen = dwLen;
|
|
|
|
if (pTemp)
|
|
{
|
|
if (GetTokenInformation(
|
|
hToken,
|
|
TokenUser,
|
|
pTemp,
|
|
dwRealLen,
|
|
&dwLen))
|
|
{
|
|
// Make a copy of the SID
|
|
PSID pSid = ((TOKEN_USER*)pTemp)->User.Sid;
|
|
DWORD dwSidLen = GetLengthSid(pSid);
|
|
|
|
m_pData = new BYTE[dwSidLen];
|
|
|
|
if (m_pData)
|
|
{
|
|
CopySid(dwSidLen, m_pData, pSid);
|
|
m_dwSize = dwSidLen;
|
|
m_bAlloced = TRUE;
|
|
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
|
|
delete [] pTemp;
|
|
}
|
|
}
|
|
|
|
pSecurity->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pData = pData;
|
|
m_dwSize = dwSize;
|
|
m_bAlloced = FALSE;
|
|
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
#define DEF_GLOBAL_LIMIT 100
|
|
#define DEF_USER_LIMIT 20
|
|
|
|
CQuota::CQuota() :
|
|
m_pEss(NULL)
|
|
{
|
|
// Zero this out.
|
|
ZeroMemory(m_dwGlobalCount, sizeof(m_dwGlobalCount));
|
|
|
|
// Setup some defaults. These will eventually get overridden once
|
|
// Init() is called.
|
|
for (int i = 0; i < ESSQ_INDEX_COUNT; i++)
|
|
{
|
|
m_dwGlobalLimits[i] = DEF_GLOBAL_LIMIT;
|
|
m_dwUserLimits[i] = DEF_USER_LIMIT;
|
|
}
|
|
|
|
m_dwGlobalLimits[ESSQ_POLLING_MEMORY] = 10000000;
|
|
m_dwUserLimits[ESSQ_POLLING_MEMORY] = 5000000;
|
|
|
|
try
|
|
{
|
|
InitializeCriticalSection(&m_cs);
|
|
}
|
|
catch(...)
|
|
{
|
|
throw CX_MemoryException();
|
|
}
|
|
}
|
|
|
|
HRESULT CQuota::Init(CEss *pEss)
|
|
{
|
|
HRESULT hr;
|
|
|
|
m_pEss = pEss;
|
|
|
|
CEssNamespace *pNamespace = NULL;
|
|
|
|
hr =
|
|
pEss->GetNamespaceObject(
|
|
WMICOREQUOTAS_NAMESPACE,
|
|
TRUE,
|
|
&pNamespace );
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_IWmiObject *pObj = NULL;
|
|
|
|
hr =
|
|
pNamespace->GetInstance(
|
|
WMICOREQUOTAS_OBJPATH,
|
|
&pObj);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
UpdateQuotaSettings(pObj);
|
|
pObj->Release();
|
|
|
|
{
|
|
CInUpdate iu( pNamespace );
|
|
|
|
hr = pNamespace->InternalRegisterNotificationSink(
|
|
L"WQL",
|
|
L"select * from __InstanceModificationEvent "
|
|
L"where targetinstance isa '" WMICOREQUOTAS_CLASS L"'",
|
|
0, WMIMSG_FLAG_QOS_SYNCHRONOUS,
|
|
GetCurrentEssContext(),
|
|
this,
|
|
true,
|
|
NULL );
|
|
}
|
|
pNamespace->FirePostponedOperations();
|
|
}
|
|
|
|
pNamespace->Release();
|
|
}
|
|
|
|
// Always return S_OK in case WMICOREQUOTAS_OBJPATH doesn't exist yet.
|
|
// Hopefully at some point this instance will always be there.
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CQuota::Shutdown()
|
|
{
|
|
if (m_pEss)
|
|
m_pEss->RemoveNotificationSink(this);
|
|
m_pEss = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
CQuota::~CQuota()
|
|
{
|
|
DeleteCriticalSection(&m_cs);
|
|
}
|
|
|
|
bool CUserInfo::operator == (const CUserInfo& other) const
|
|
{
|
|
return m_dwSize == other.m_dwSize &&
|
|
!memcmp(m_pData, other.m_pData, m_dwSize);
|
|
}
|
|
|
|
bool CUserInfo::operator < (const CUserInfo& other) const
|
|
{
|
|
if (m_dwSize < other.m_dwSize)
|
|
return TRUE;
|
|
else if (m_dwSize > other.m_dwSize)
|
|
return FALSE;
|
|
else
|
|
return memcmp(m_pData, other.m_pData, m_dwSize) < 0;
|
|
}
|
|
|
|
void GetSidInfo(CEventFilter *pFilter, LPVOID *ppSid, DWORD *pdwSize)
|
|
{
|
|
if (pFilter)
|
|
{
|
|
*ppSid = pFilter->GetOwner();
|
|
|
|
*pdwSize = *ppSid ? GetLengthSid(*ppSid) : 0;
|
|
}
|
|
else
|
|
{
|
|
*ppSid = NULL;
|
|
*pdwSize = 0;
|
|
}
|
|
}
|
|
|
|
HRESULT CQuota::FindUser(CEventFilter* pFilter, void** pUser)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPVOID pSid = NULL;
|
|
DWORD dwSize;
|
|
|
|
GetSidInfo(pFilter, &pSid, &dwSize);
|
|
|
|
// We'll save the context if there's not a Sid.
|
|
BOOL bDoSwitchContext = pSid == NULL;
|
|
CSaveCallContext save(bDoSwitchContext);
|
|
|
|
if (bDoSwitchContext)
|
|
{
|
|
hr = pFilter->SetThreadSecurity();
|
|
if(FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
CInCritSec ics(&m_cs);
|
|
|
|
CUserInfo user;
|
|
if (user.Init((LPBYTE) pSid, dwSize))
|
|
{
|
|
CUserMapIterator it;
|
|
|
|
it= m_mapUserInfo.find(user);
|
|
if (it!= m_mapUserInfo.end())
|
|
{
|
|
*pUser = new CUserMapIterator(it);
|
|
if(*pUser == NULL)
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
// Add it to the map.
|
|
|
|
try
|
|
{
|
|
m_mapUserInfo[user] = 0;
|
|
}
|
|
catch(CX_MemoryException)
|
|
{
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
*pUser = new CUserMapIterator(m_mapUserInfo.find(user));
|
|
if(*pUser == NULL)
|
|
return WBEM_E_OUT_OF_MEMORY;
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Special case for calls Winmgmt makes: doesn't count against any
|
|
// user.
|
|
|
|
*pUser = NULL;
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
HRESULT CQuota::FreeUser(void* pUser)
|
|
{
|
|
delete (CUserMapIterator*)pUser;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CQuota::IncrementQuotaIndex(
|
|
ESS_QUOTA_INDEX dwIndex,
|
|
CEventFilter *pFilter,
|
|
DWORD dwToAdd)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPVOID pSid = NULL;
|
|
DWORD dwSize;
|
|
|
|
GetSidInfo(pFilter, &pSid, &dwSize);
|
|
|
|
// We'll save the context if there's not a Sid.
|
|
BOOL bDoSwitchContext = pSid == NULL;
|
|
CSaveCallContext save(bDoSwitchContext);
|
|
|
|
if (bDoSwitchContext)
|
|
{
|
|
hr = pFilter->SetThreadSecurity();
|
|
if(FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
Lock();
|
|
|
|
if (m_dwGlobalCount[dwIndex] + dwToAdd <= m_dwGlobalLimits[dwIndex])
|
|
{
|
|
CUserInfo user;
|
|
|
|
if (user.Init((LPBYTE) pSid, dwSize))
|
|
{
|
|
CUserMapIterator item;
|
|
|
|
item = m_mapUserInfo.find(user);
|
|
|
|
if (item != m_mapUserInfo.end())
|
|
{
|
|
CUserInfo &itemRef = (CUserInfo&) (*item).first;
|
|
|
|
if (itemRef.m_dwUserCount[dwIndex] + dwToAdd <= m_dwUserLimits[dwIndex])
|
|
{
|
|
itemRef.m_dwUserCount[dwIndex] += dwToAdd;
|
|
m_dwGlobalCount[dwIndex] += dwToAdd;
|
|
}
|
|
else
|
|
hr = WBEM_E_QUOTA_VIOLATION;
|
|
}
|
|
else
|
|
{
|
|
// Set the number of items to dwToAdd.
|
|
|
|
user.m_dwUserCount[dwIndex] = dwToAdd;
|
|
|
|
// Add it to the map.
|
|
try
|
|
{
|
|
m_mapUserInfo[user] = 0;
|
|
}
|
|
catch(CX_MemoryException)
|
|
{
|
|
hr = WBEM_E_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
m_dwGlobalCount[dwIndex] += dwToAdd;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Special case for calls Winmgmt makes: doesn't count against any
|
|
// user.
|
|
// Should this event count against our global counts?
|
|
m_dwGlobalCount[dwIndex] += dwToAdd;
|
|
}
|
|
}
|
|
else
|
|
hr = WBEM_E_QUOTA_VIOLATION;
|
|
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CQuota::DecrementQuotaIndex(
|
|
ESS_QUOTA_INDEX dwIndex,
|
|
CEventFilter *pFilter,
|
|
DWORD dwToRemove)
|
|
{
|
|
CUserInfo user;
|
|
BOOL bRet = FALSE;
|
|
LPVOID pSid;
|
|
DWORD dwSize;
|
|
HRESULT hr;
|
|
|
|
GetSidInfo(pFilter, &pSid, &dwSize);
|
|
|
|
// We'll save the context if there's not a Sid.
|
|
BOOL bDoSwitchContext = pSid == NULL;
|
|
CSaveCallContext save(bDoSwitchContext);
|
|
|
|
if (bDoSwitchContext)
|
|
{
|
|
hr = pFilter->SetThreadSecurity();
|
|
if(FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
Lock();
|
|
|
|
m_dwGlobalCount[dwIndex] -= dwToRemove;
|
|
|
|
if (user.Init((LPBYTE) pSid, dwSize))
|
|
{
|
|
CUserMapIterator item;
|
|
|
|
item = m_mapUserInfo.find(user);
|
|
|
|
if (item != m_mapUserInfo.end())
|
|
{
|
|
CUserInfo &itemRef = (CUserInfo&) (*item).first;
|
|
|
|
itemRef.m_dwUserCount[dwIndex] -= dwToRemove;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
HRESULT CQuota::IncrementQuotaIndexByUser(
|
|
ESS_QUOTA_INDEX dwIndex,
|
|
void *pUser,
|
|
DWORD dwToAdd)
|
|
{
|
|
CUserMapIterator* pIt = (CUserMapIterator*)pUser;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
Lock();
|
|
|
|
if (m_dwGlobalCount[dwIndex] + dwToAdd <= m_dwGlobalLimits[dwIndex])
|
|
{
|
|
if(pIt)
|
|
{
|
|
CUserInfo &itemRef = (CUserInfo&) (*pIt)->first;
|
|
|
|
if (itemRef.m_dwUserCount[dwIndex] + dwToAdd <=
|
|
m_dwUserLimits[dwIndex])
|
|
{
|
|
itemRef.m_dwUserCount[dwIndex] += dwToAdd;
|
|
m_dwGlobalCount[dwIndex] += dwToAdd;
|
|
}
|
|
else
|
|
{
|
|
hr = WBEM_E_QUOTA_VIOLATION;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Special case for calls Winmgmt makes: doesn't count against any
|
|
// user.
|
|
// Should this event count against our global counts?
|
|
m_dwGlobalCount[dwIndex] += dwToAdd;
|
|
}
|
|
}
|
|
else
|
|
hr = WBEM_E_QUOTA_VIOLATION;
|
|
|
|
Unlock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CQuota::DecrementQuotaIndexByUser(
|
|
ESS_QUOTA_INDEX dwIndex,
|
|
void *pUser,
|
|
DWORD dwToRemove)
|
|
{
|
|
CUserMapIterator* pIt = (CUserMapIterator*)pUser;
|
|
|
|
Lock();
|
|
|
|
m_dwGlobalCount[dwIndex] -= dwToRemove;
|
|
|
|
if (pIt)
|
|
{
|
|
CUserInfo &itemRef = (CUserInfo&) (*pIt)->first;
|
|
|
|
_ASSERT(itemRef.m_dwUserCount[dwIndex] >= dwToRemove,
|
|
L"Negative quotas!");
|
|
|
|
itemRef.m_dwUserCount[dwIndex] -= dwToRemove;
|
|
}
|
|
|
|
Unlock();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
const LPCWSTR szUserProps[] =
|
|
{
|
|
L"TemporarySubscriptionsPerUser",
|
|
L"PermanentSubscriptionsPerUser",
|
|
L"PollingInstructionsPerUser",
|
|
L"PollingMemoryPerUser",
|
|
};
|
|
|
|
const LPCWSTR szGlobalProps[] =
|
|
{
|
|
L"TemporarySubscriptionsTotal",
|
|
L"PermanentSubscriptionsTotal",
|
|
L"PollingInstructionsTotal",
|
|
L"PollingMemoryTotal",
|
|
};
|
|
|
|
void CQuota::UpdateQuotaSettings(IWbemClassObject *pObj)
|
|
{
|
|
VARIANT vTemp;
|
|
|
|
VariantInit(&vTemp);
|
|
|
|
Lock();
|
|
|
|
for (int i = 0; i < ESSQ_INVALID_INDEX; i++)
|
|
{
|
|
if (SUCCEEDED(pObj->Get(szUserProps[i], 0, &vTemp, NULL, NULL)))
|
|
m_dwUserLimits[i] = V_I4(&vTemp);
|
|
|
|
if (SUCCEEDED(pObj->Get(szGlobalProps[i], 0, &vTemp, NULL, NULL)))
|
|
m_dwGlobalLimits[i] = V_I4(&vTemp);
|
|
}
|
|
|
|
Unlock();
|
|
}
|
|
|
|
HRESULT WINAPI CQuota::Indicate(long lNumEvents, IWbemClassObject **ppEvents)
|
|
{
|
|
VARIANT vTemp;
|
|
|
|
VariantInit(&vTemp);
|
|
|
|
if (SUCCEEDED(ppEvents[lNumEvents - 1]->Get(
|
|
L"TARGETINSTANCE", 0, &vTemp, NULL, NULL)))
|
|
{
|
|
IWbemClassObject *pObj = NULL;
|
|
|
|
vTemp.punkVal->QueryInterface(IID_IWbemClassObject, (LPVOID*) &pObj);
|
|
|
|
if (pObj)
|
|
{
|
|
UpdateQuotaSettings(pObj);
|
|
|
|
pObj->Release();
|
|
}
|
|
}
|
|
|
|
VariantClear(&vTemp);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
CSaveCallContext::CSaveCallContext(BOOL bSave) :
|
|
m_pSecurity(NULL)
|
|
{
|
|
m_bSaved = bSave;
|
|
|
|
if (bSave)
|
|
WbemCoGetCallContext(IID_IWbemCallSecurity, (LPVOID*) &m_pSecurity);
|
|
}
|
|
|
|
CSaveCallContext::~CSaveCallContext()
|
|
{
|
|
if (m_bSaved)
|
|
{
|
|
IUnknown *pPrev = NULL;
|
|
|
|
CoSwitchCallContext(m_pSecurity, &pPrev);
|
|
|
|
if (m_pSecurity)
|
|
m_pSecurity->Release();
|
|
}
|
|
}
|
|
|