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

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // SYNOPSIS
  6. //
  7. // Defines the class Database.
  8. //
  9. ///////////////////////////////////////////////////////////////////////////////
  10. #include "ias.h"
  11. #include "database.h"
  12. #include "dbconfig.h"
  13. #include "xmlwriter.h"
  14. const ULONGLONG Database::blackoutInterval = 2 * 10000000ui64;
  15. Database::Database() throw ()
  16. : state(AVAILABLE),
  17. blackoutExpiry(0)
  18. {
  19. }
  20. Database::~Database() throw ()
  21. {
  22. }
  23. HRESULT Database::FinalConstruct() throw ()
  24. {
  25. return pool.FinalConstruct();
  26. }
  27. STDMETHODIMP Database::Initialize()
  28. {
  29. DWORD len = sizeof(computerName) / sizeof(wchar_t);
  30. if (!GetComputerNameW(computerName, &len))
  31. {
  32. computerName[0] = L'\0';
  33. }
  34. return Accountant::Initialize();
  35. }
  36. STDMETHODIMP Database::Shutdown()
  37. {
  38. ResetConnection();
  39. return Accountant::Shutdown();
  40. }
  41. STDMETHODIMP Database::PutProperty(LONG Id, VARIANT *pValue)
  42. {
  43. // We only process one property. Everything else is proxied to our base
  44. // class.
  45. if (Id != PROPERTY_ACCOUNTING_SQL_MAX_SESSIONS)
  46. {
  47. return Accountant::PutProperty(Id, pValue);
  48. }
  49. // Check the arguments.
  50. if (pValue == 0)
  51. {
  52. return E_POINTER;
  53. }
  54. if (V_VT(pValue) != VT_I4)
  55. {
  56. return E_INVALIDARG;
  57. }
  58. // This is a good time to reread the LSA config as well.
  59. CComBSTR newInitString;
  60. CComBSTR dataSourceName;
  61. HRESULT hr = IASLoadDatabaseConfig(
  62. 0,
  63. &newInitString,
  64. &dataSourceName
  65. );
  66. if (FAILED(hr))
  67. {
  68. return hr;
  69. }
  70. // Did the config change?
  71. if (!initString ||
  72. !newInitString ||
  73. (wcscmp(initString, newInitString) != 0))
  74. {
  75. OnConfigChange();
  76. initString.Attach(newInitString.Detach());
  77. }
  78. pool.SetMaxCommands(V_I4(pValue));
  79. return S_OK;
  80. }
  81. void Database::Process(IASTL::IASRequest& request)
  82. {
  83. // Quick precheck so we don't waste time if the database isn't configured.
  84. if (initString)
  85. {
  86. RecordEvent(0, request);
  87. }
  88. }
  89. void Database::InsertRecord(
  90. void* context,
  91. IASTL::IASRequest& request,
  92. const SYSTEMTIME& localTime,
  93. PATTRIBUTEPOSITION first,
  94. PATTRIBUTEPOSITION last
  95. )
  96. {
  97. XmlWriter doc;
  98. doc.StartDocument();
  99. doc.InsertElement(L"Computer-Name", computerName, XmlWriter::DataType::string);
  100. static const wchar_t eventSourceName[] = L"Event-Source";
  101. switch (request.get_Protocol())
  102. {
  103. case IAS_PROTOCOL_RADIUS:
  104. {
  105. doc.InsertElement(eventSourceName, L"IAS", XmlWriter::DataType::string);
  106. break;
  107. }
  108. case IAS_PROTOCOL_RAS:
  109. {
  110. doc.InsertElement(eventSourceName, L"RAS", XmlWriter::DataType::string);
  111. break;
  112. }
  113. default:
  114. {
  115. break;
  116. }
  117. }
  118. for (PATTRIBUTEPOSITION i = first; i != last; ++i)
  119. {
  120. const LogField* field = schema.find(i->pAttribute->dwId);
  121. if ((field != 0) && !field->excludeFromDatabase)
  122. {
  123. doc.InsertAttribute(field->name, *(i->pAttribute));
  124. }
  125. }
  126. doc.EndDocument();
  127. HRESULT hr;
  128. ReportEventCommand* command = pool.Alloc();
  129. if (command != 0)
  130. {
  131. hr = ExecuteCommand(*command, doc.GetDocument(), true);
  132. pool.Free(command);
  133. }
  134. else
  135. {
  136. hr = E_OUTOFMEMORY;
  137. }
  138. if (FAILED(hr))
  139. {
  140. IASTL::issue_error(hr);
  141. }
  142. }
  143. void Database::Flush(
  144. void* context,
  145. IASTL::IASRequest& request,
  146. const SYSTEMTIME& localTime
  147. )
  148. {
  149. }
  150. HRESULT Database::ExecuteCommand(
  151. ReportEventCommand& command,
  152. const wchar_t* doc,
  153. bool retry
  154. ) throw ()
  155. {
  156. HRESULT hr = PrepareCommand(command);
  157. if (hr == S_OK)
  158. {
  159. hr = command.Execute(doc);
  160. if (SUCCEEDED(hr))
  161. {
  162. OnExecuteSuccess(command);
  163. }
  164. else
  165. {
  166. OnExecuteError(command);
  167. if (retry)
  168. {
  169. ExecuteCommand(command, doc, false);
  170. }
  171. }
  172. }
  173. return hr;
  174. }
  175. HRESULT Database::PrepareCommand(ReportEventCommand& command) throw ()
  176. {
  177. HRESULT hr = S_OK;
  178. Lock();
  179. if (!initString)
  180. {
  181. // If we don't have an initialization string, it's not an error. It just
  182. // means the admin never configured it.
  183. hr = S_FALSE;
  184. }
  185. else if (IsBlackedOut())
  186. {
  187. // Don't even try to prepare if we're blacked out.
  188. hr = E_FAIL;
  189. }
  190. else
  191. {
  192. // Create the connection if necessary.
  193. if (!dataSource)
  194. {
  195. hr = ReportEventCommand::CreateDataSource(
  196. initString,
  197. &dataSource
  198. );
  199. if (FAILED(hr))
  200. {
  201. OnConnectError();
  202. }
  203. }
  204. // If we have a good connection, prepare the command if necessary.
  205. if (SUCCEEDED(hr) && !command.IsPrepared())
  206. {
  207. hr = command.Prepare(dataSource);
  208. if (FAILED(hr))
  209. {
  210. OnConnectError();
  211. }
  212. }
  213. }
  214. Unlock();
  215. return hr;
  216. }
  217. void Database::ResetConnection() throw ()
  218. {
  219. dataSource.Release();
  220. pool.UnprepareAll();
  221. }
  222. inline void Database::OnConfigChange() throw ()
  223. {
  224. ResetConnection();
  225. state = AVAILABLE;
  226. }
  227. void Database::OnConnectError() throw ()
  228. {
  229. ResetConnection();
  230. SetBlackOut();
  231. }
  232. inline void Database::OnExecuteSuccess(ReportEventCommand& command) throw ()
  233. {
  234. // Suppress events from old connections.
  235. if (command.Version() == pool.Version())
  236. {
  237. state = AVAILABLE;
  238. }
  239. }
  240. inline void Database::OnExecuteError(ReportEventCommand& command) throw ()
  241. {
  242. Lock();
  243. // Suppress events from old connections.
  244. if (command.Version() == pool.Version())
  245. {
  246. ResetConnection();
  247. if (state == AVAILABLE)
  248. {
  249. state = QUESTIONABLE;
  250. }
  251. else if (state == QUESTIONABLE)
  252. {
  253. SetBlackOut();
  254. }
  255. }
  256. command.Unprepare();
  257. Unlock();
  258. }
  259. inline bool Database::IsBlackedOut() throw ()
  260. {
  261. if (state == BLACKED_OUT)
  262. {
  263. ULONGLONG now;
  264. GetSystemTimeAsFileTime(reinterpret_cast<FILETIME*>(&now));
  265. if (now >= blackoutExpiry)
  266. {
  267. state = AVAILABLE;
  268. }
  269. }
  270. return state == BLACKED_OUT;
  271. }
  272. void Database::SetBlackOut() throw ()
  273. {
  274. state = BLACKED_OUT;
  275. GetSystemTimeAsFileTime(reinterpret_cast<FILETIME*>(&blackoutExpiry));
  276. blackoutExpiry += blackoutInterval;
  277. }