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.
 
 
 
 
 
 

344 lines
6.9 KiB

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corporation
//
// SYNOPSIS
//
// Defines the class Database.
//
///////////////////////////////////////////////////////////////////////////////
#include "ias.h"
#include "database.h"
#include "dbconfig.h"
#include "xmlwriter.h"
const ULONGLONG Database::blackoutInterval = 2 * 10000000ui64;
Database::Database() throw ()
: state(AVAILABLE),
blackoutExpiry(0)
{
}
Database::~Database() throw ()
{
}
HRESULT Database::FinalConstruct() throw ()
{
return pool.FinalConstruct();
}
STDMETHODIMP Database::Initialize()
{
DWORD len = sizeof(computerName) / sizeof(wchar_t);
if (!GetComputerNameW(computerName, &len))
{
computerName[0] = L'\0';
}
return Accountant::Initialize();
}
STDMETHODIMP Database::Shutdown()
{
ResetConnection();
return Accountant::Shutdown();
}
STDMETHODIMP Database::PutProperty(LONG Id, VARIANT *pValue)
{
// We only process one property. Everything else is proxied to our base
// class.
if (Id != PROPERTY_ACCOUNTING_SQL_MAX_SESSIONS)
{
return Accountant::PutProperty(Id, pValue);
}
// Check the arguments.
if (pValue == 0)
{
return E_POINTER;
}
if (V_VT(pValue) != VT_I4)
{
return E_INVALIDARG;
}
// This is a good time to reread the LSA config as well.
CComBSTR newInitString;
CComBSTR dataSourceName;
HRESULT hr = IASLoadDatabaseConfig(
0,
&newInitString,
&dataSourceName
);
if (FAILED(hr))
{
return hr;
}
// Did the config change?
if (!initString ||
!newInitString ||
(wcscmp(initString, newInitString) != 0))
{
OnConfigChange();
initString.Attach(newInitString.Detach());
}
pool.SetMaxCommands(V_I4(pValue));
return S_OK;
}
void Database::Process(IASTL::IASRequest& request)
{
// Quick precheck so we don't waste time if the database isn't configured.
if (initString)
{
RecordEvent(0, request);
}
}
void Database::InsertRecord(
void* context,
IASTL::IASRequest& request,
const SYSTEMTIME& localTime,
PATTRIBUTEPOSITION first,
PATTRIBUTEPOSITION last
)
{
XmlWriter doc;
doc.StartDocument();
doc.InsertElement(L"Computer-Name", computerName, XmlWriter::DataType::string);
static const wchar_t eventSourceName[] = L"Event-Source";
switch (request.get_Protocol())
{
case IAS_PROTOCOL_RADIUS:
{
doc.InsertElement(eventSourceName, L"IAS", XmlWriter::DataType::string);
break;
}
case IAS_PROTOCOL_RAS:
{
doc.InsertElement(eventSourceName, L"RAS", XmlWriter::DataType::string);
break;
}
default:
{
break;
}
}
for (PATTRIBUTEPOSITION i = first; i != last; ++i)
{
const LogField* field = schema.find(i->pAttribute->dwId);
if ((field != 0) && !field->excludeFromDatabase)
{
doc.InsertAttribute(field->name, *(i->pAttribute));
}
}
doc.EndDocument();
HRESULT hr;
ReportEventCommand* command = pool.Alloc();
if (command != 0)
{
hr = ExecuteCommand(*command, doc.GetDocument(), true);
pool.Free(command);
}
else
{
hr = E_OUTOFMEMORY;
}
if (FAILED(hr))
{
IASTL::issue_error(hr);
}
}
void Database::Flush(
void* context,
IASTL::IASRequest& request,
const SYSTEMTIME& localTime
)
{
}
HRESULT Database::ExecuteCommand(
ReportEventCommand& command,
const wchar_t* doc,
bool retry
) throw ()
{
HRESULT hr = PrepareCommand(command);
if (hr == S_OK)
{
hr = command.Execute(doc);
if (SUCCEEDED(hr))
{
OnExecuteSuccess(command);
}
else
{
OnExecuteError(command);
if (retry)
{
ExecuteCommand(command, doc, false);
}
}
}
return hr;
}
HRESULT Database::PrepareCommand(ReportEventCommand& command) throw ()
{
HRESULT hr = S_OK;
Lock();
if (!initString)
{
// If we don't have an initialization string, it's not an error. It just
// means the admin never configured it.
hr = S_FALSE;
}
else if (IsBlackedOut())
{
// Don't even try to prepare if we're blacked out.
hr = E_FAIL;
}
else
{
// Create the connection if necessary.
if (!dataSource)
{
hr = ReportEventCommand::CreateDataSource(
initString,
&dataSource
);
if (FAILED(hr))
{
OnConnectError();
}
}
// If we have a good connection, prepare the command if necessary.
if (SUCCEEDED(hr) && !command.IsPrepared())
{
hr = command.Prepare(dataSource);
if (FAILED(hr))
{
OnConnectError();
}
}
}
Unlock();
return hr;
}
void Database::ResetConnection() throw ()
{
dataSource.Release();
pool.UnprepareAll();
}
inline void Database::OnConfigChange() throw ()
{
ResetConnection();
state = AVAILABLE;
}
void Database::OnConnectError() throw ()
{
ResetConnection();
SetBlackOut();
}
inline void Database::OnExecuteSuccess(ReportEventCommand& command) throw ()
{
// Suppress events from old connections.
if (command.Version() == pool.Version())
{
state = AVAILABLE;
}
}
inline void Database::OnExecuteError(ReportEventCommand& command) throw ()
{
Lock();
// Suppress events from old connections.
if (command.Version() == pool.Version())
{
ResetConnection();
if (state == AVAILABLE)
{
state = QUESTIONABLE;
}
else if (state == QUESTIONABLE)
{
SetBlackOut();
}
}
command.Unprepare();
Unlock();
}
inline bool Database::IsBlackedOut() throw ()
{
if (state == BLACKED_OUT)
{
ULONGLONG now;
GetSystemTimeAsFileTime(reinterpret_cast<FILETIME*>(&now));
if (now >= blackoutExpiry)
{
state = AVAILABLE;
}
}
return state == BLACKED_OUT;
}
void Database::SetBlackOut() throw ()
{
state = BLACKED_OUT;
GetSystemTimeAsFileTime(reinterpret_cast<FILETIME*>(&blackoutExpiry));
blackoutExpiry += blackoutInterval;
}