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.

2179 lines
56 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. errlogp.c (HTTP.SYS Generic Error Logging)
  5. Abstract:
  6. This module implements the generic error logging
  7. This functionality is driver wide.
  8. Author:
  9. Ali E. Turkoglu (aliTu) 24-Jan-2002
  10. Revision History:
  11. ---
  12. --*/
  13. #include "precomp.h"
  14. #include "iiscnfg.h"
  15. #include "errlogp.h"
  16. //
  17. // Generic Private globals.
  18. //
  19. UL_ERROR_LOG_FILE_ENTRY g_ErrorLogEntry;
  20. BOOLEAN g_InitErrorLogCalled = FALSE;
  21. LONG g_ErrorLoggingEnabled = 0;
  22. #ifdef ALLOC_PRAGMA
  23. #pragma alloc_text( INIT, UlInitializeErrorLog )
  24. #pragma alloc_text( PAGE, UlpErrorLogBufferTimerHandler )
  25. #pragma alloc_text( PAGE, UlpCreateErrorLogFile )
  26. #pragma alloc_text( PAGE, UlpFlushErrorLogFile )
  27. #pragma alloc_text( PAGE, UlpDisableErrorLogEntry )
  28. #pragma alloc_text( PAGE, UlpRecycleErrorLogFile )
  29. #pragma alloc_text( PAGE, UlCloseErrorLogEntry )
  30. #pragma alloc_text( PAGE, UlLogHttpError )
  31. #pragma alloc_text( PAGE, UlpAllocErrorLogBuffer )
  32. #pragma alloc_text( PAGE, UlErrorLoggingEnabled )
  33. #pragma alloc_text( PAGE, UlpBuildErrorLogRecord )
  34. #pragma alloc_text( PAGE, UlpWriteToErrorLogFileDebug )
  35. #pragma alloc_text( PAGE, UlpWriteToErrorLogFileShared )
  36. #pragma alloc_text( PAGE, UlpWriteToErrorLogFileExclusive )
  37. #pragma alloc_text( PAGE, UlpWriteToErrorLogFile )
  38. #endif // ALLOC_PRAGMA
  39. #if 0
  40. NOT PAGEABLE -- UlpErrorLogBufferTimerDpcRoutine
  41. NOT PAGEABLE -- UlTerminateErrorLog
  42. NOT PAGEABLE -- UlConfigErrorLogEntry
  43. NOT PAGEABLE --
  44. #endif
  45. /***************************************************************************++
  46. Init the generic error logging entry and its fields.
  47. --***************************************************************************/
  48. NTSTATUS
  49. UlInitializeErrorLog (
  50. VOID
  51. )
  52. {
  53. PAGED_CODE();
  54. ASSERT(!g_InitErrorLogCalled);
  55. if (!g_InitErrorLogCalled)
  56. {
  57. //
  58. // Init the generic log entry.
  59. //
  60. RtlZeroMemory(
  61. (PCHAR)&g_ErrorLogEntry, sizeof(UL_ERROR_LOG_FILE_ENTRY));
  62. g_ErrorLogEntry.Signature = UL_ERROR_LOG_FILE_ENTRY_POOL_TAG;
  63. UlInitializePushLock(
  64. &g_ErrorLogEntry.PushLock,
  65. "ErrorLogEntryPushLock",
  66. 0,
  67. UL_ERROR_LOG_FILE_ENTRY_POOL_TAG
  68. );
  69. //
  70. // Initialize the buffer flush timer.
  71. //
  72. UlInitializeSpinLock(
  73. &g_ErrorLogEntry.BufferTimer.SpinLock,
  74. "ErrorLogEntryBufferTimerSpinLock" );
  75. KeInitializeDpc(
  76. &g_ErrorLogEntry.BufferTimer.DpcObject, // DPC object
  77. &UlpErrorLogBufferTimerDpcRoutine, // DPC routine
  78. NULL // context
  79. );
  80. KeInitializeTimer(&g_ErrorLogEntry.BufferTimer.Timer);
  81. g_ErrorLogEntry.BufferTimer.Initialized = TRUE;
  82. g_ErrorLogEntry.BufferTimer.Started = FALSE;
  83. g_ErrorLogEntry.BufferTimer.Period = -1;
  84. g_ErrorLogEntry.BufferTimer.PeriodType = UlLogTimerPeriodNone;
  85. UlInitializeWorkItem(&g_ErrorLogEntry.WorkItem);
  86. g_ErrorLogEntry.WorkItemScheduled = FALSE;
  87. g_InitErrorLogCalled = TRUE;
  88. UlTrace(ERROR_LOGGING,("Http!UlInitializeErrorLog:"
  89. " g_ErrorLogEntry @ (%p) Initialized.\n",
  90. &g_ErrorLogEntry
  91. ));
  92. //
  93. // Since the default config is already built from registry,
  94. // time to configure the global error log entry.
  95. //
  96. if (g_UlErrLoggingConfig.Enabled)
  97. {
  98. NTSTATUS Status =
  99. UlConfigErrorLogEntry(&g_UlErrLoggingConfig);
  100. UlTrace(ERROR_LOGGING,("Http!UlInitializeErrorLog:"
  101. " g_ErrorLogEntry @ (%p) Configured Status %08lx\n",
  102. &g_ErrorLogEntry,
  103. Status
  104. ));
  105. if (!NT_SUCCESS(Status))
  106. {
  107. g_UlErrLoggingConfig.Enabled = FALSE;
  108. UlWriteEventLogEntry(
  109. EVENT_HTTP_LOGGING_ERROR_FILE_CONFIG_FAILED,
  110. 0,
  111. 0,
  112. NULL,
  113. sizeof(NTSTATUS),
  114. (PVOID) &Status
  115. );
  116. }
  117. }
  118. }
  119. return STATUS_SUCCESS;
  120. }
  121. /***************************************************************************++
  122. Terminates the error logging entry and its timer.
  123. --***************************************************************************/
  124. VOID
  125. UlTerminateErrorLog(
  126. VOID
  127. )
  128. {
  129. KIRQL OldIrql;
  130. if (g_InitErrorLogCalled)
  131. {
  132. PUL_LOG_TIMER pBufferTimer = &g_ErrorLogEntry.BufferTimer;
  133. //
  134. // Terminate the buffer timer
  135. //
  136. UlAcquireSpinLock(&pBufferTimer->SpinLock, &OldIrql);
  137. pBufferTimer->Initialized = FALSE;
  138. KeCancelTimer(&pBufferTimer->Timer);
  139. UlReleaseSpinLock(&pBufferTimer->SpinLock, OldIrql);
  140. //
  141. // Try to cleanup the error log entry in case it has been configured
  142. // before. Even if not, following call is not dangerous.
  143. //
  144. UlCloseErrorLogEntry();
  145. //
  146. // Delete the push lock
  147. //
  148. UlDeletePushLock(&g_ErrorLogEntry.PushLock);
  149. g_InitErrorLogCalled = FALSE;
  150. }
  151. }
  152. /***************************************************************************++
  153. Routine Description:
  154. Queues a passive worker for the lowered irql.
  155. Arguments:
  156. Ignored
  157. --***************************************************************************/
  158. VOID
  159. UlpErrorLogBufferTimerDpcRoutine(
  160. PKDPC Dpc,
  161. PVOID DeferredContext,
  162. PVOID SystemArgument1,
  163. PVOID SystemArgument2
  164. )
  165. {
  166. PUL_LOG_TIMER pTimer = &g_ErrorLogEntry.BufferTimer;
  167. PUL_WORK_ITEM pWorkItem = &g_ErrorLogEntry.WorkItem;;
  168. UNREFERENCED_PARAMETER(Dpc);
  169. UNREFERENCED_PARAMETER(DeferredContext);
  170. UNREFERENCED_PARAMETER(SystemArgument1);
  171. UNREFERENCED_PARAMETER(SystemArgument2);
  172. UlAcquireSpinLockAtDpcLevel(&pTimer->SpinLock);
  173. if (pTimer->Initialized == TRUE)
  174. {
  175. //
  176. // Protect against multiple queueing with the same item.
  177. // If threadpool is busy this could happen under stress.
  178. // In this case drop this flush.
  179. //
  180. if (FALSE == InterlockedExchange(
  181. &g_ErrorLogEntry.WorkItemScheduled,
  182. TRUE
  183. ))
  184. {
  185. UL_QUEUE_WORK_ITEM(pWorkItem, &UlpErrorLogBufferTimerHandler);
  186. }
  187. }
  188. UlReleaseSpinLockFromDpcLevel(&pTimer->SpinLock);
  189. }
  190. /***************************************************************************++
  191. Routine Description:
  192. Flushes or recycles the error log file.
  193. Arguments:
  194. PUL_WORK_ITEM - Ignored but cleaned up at the end
  195. --***************************************************************************/
  196. VOID
  197. UlpErrorLogBufferTimerHandler(
  198. IN PUL_WORK_ITEM pWorkItem
  199. )
  200. {
  201. NTSTATUS Status;
  202. PUL_ERROR_LOG_FILE_ENTRY pEntry = &g_ErrorLogEntry;
  203. UNREFERENCED_PARAMETER(pWorkItem);
  204. PAGED_CODE();
  205. UlTrace(ERROR_LOGGING,
  206. ("Http!UlpErrorLogBufferTimerHandler: Checking the ErrorLogEntry. \n"));
  207. InterlockedExchange(
  208. &g_ErrorLogEntry.WorkItemScheduled,
  209. FALSE
  210. );
  211. UlAcquirePushLockExclusive(&pEntry->PushLock);
  212. if (pEntry->Flags.Active)
  213. {
  214. if (pEntry->Flags.RecyclePending)
  215. {
  216. //
  217. // Try to resurrect it back.
  218. //
  219. Status = UlpRecycleErrorLogFile(pEntry);
  220. }
  221. else
  222. {
  223. //
  224. // Everything is fine simply flush.
  225. //
  226. Status = UlpFlushErrorLogFile(pEntry);
  227. }
  228. }
  229. UlReleasePushLockExclusive(&pEntry->PushLock);
  230. }
  231. /***************************************************************************++
  232. Routine Description:
  233. Small utility to check whether error logging is disabled or not.
  234. Returns
  235. TRUE if enabled FALSE otherwise.
  236. --***************************************************************************/
  237. BOOLEAN
  238. UlErrorLoggingEnabled(
  239. VOID
  240. )
  241. {
  242. if (g_ErrorLoggingEnabled && g_InitErrorLogCalled)
  243. {
  244. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(&g_ErrorLogEntry));
  245. return TRUE;
  246. }
  247. else
  248. {
  249. return FALSE;
  250. }
  251. }
  252. /***************************************************************************++
  253. Routine Description:
  254. Builds the error logging directory.
  255. Arguments:
  256. pSrc - Source string copied first.
  257. pDir - Source string + SubDir + UnicodeNull
  258. --***************************************************************************/
  259. NTSTATUS
  260. UlBuildErrorLoggingDirStr(
  261. IN PCWSTR pSrc,
  262. OUT PUNICODE_STRING pDir
  263. )
  264. {
  265. NTSTATUS Status;
  266. UNICODE_STRING DirStr,SubDirStr;
  267. //
  268. // Lets make sure the unicode string's buffer is sufficient.
  269. //
  270. ASSERT(pDir->MaximumLength
  271. >= (( wcslen(pSrc)
  272. + UL_ERROR_LOG_SUB_DIR_LENGTH // SubDir
  273. + 1 // UnicodeNull
  274. ) * sizeof(WCHAR))
  275. );
  276. //
  277. // Copy the beginning portion from the source string.
  278. //
  279. Status = UlInitUnicodeStringEx(&DirStr, pSrc);
  280. if (!NT_SUCCESS(Status))
  281. {
  282. return Status;
  283. }
  284. RtlCopyUnicodeString(pDir, &DirStr);
  285. //
  286. // Append the sub directory. AppendUnicodeString will null terminate.
  287. //
  288. Status = UlInitUnicodeStringEx(&SubDirStr, UL_ERROR_LOG_SUB_DIR);
  289. ASSERT(NT_SUCCESS(Status));
  290. Status = RtlAppendUnicodeStringToString(pDir, &SubDirStr);
  291. if (!NT_SUCCESS(Status))
  292. {
  293. return Status;
  294. }
  295. ASSERT(IS_WELL_FORMED_UNICODE_STRING(pDir));
  296. return Status;
  297. }
  298. /***************************************************************************++
  299. Routine Description:
  300. When Error Log File Configuration is provided by registry
  301. this function provides the basic sanity check on the values.
  302. Arguments:
  303. pUserConfig - Error Logging config from the registry.
  304. --***************************************************************************/
  305. NTSTATUS
  306. UlCheckErrorLogConfig(
  307. IN PHTTP_ERROR_LOGGING_CONFIG pUserConfig
  308. )
  309. {
  310. NTSTATUS Status;
  311. //
  312. // Sanity check.
  313. //
  314. PAGED_CODE();
  315. ASSERT(pUserConfig);
  316. Status = STATUS_SUCCESS;
  317. //
  318. // Cook the directory if it is enabled.
  319. //
  320. if (pUserConfig->Enabled)
  321. {
  322. ASSERT(pUserConfig->Dir.Buffer);
  323. ASSERT(pUserConfig->Dir.Length);
  324. //
  325. // Following check must have already been done by the registry code
  326. // in init.c.
  327. //
  328. ASSERT(pUserConfig->TruncateSize
  329. >= DEFAULT_MIN_ERROR_FILE_TRUNCATION_SIZE
  330. );
  331. //
  332. // Directory should be fully qualified.
  333. //
  334. if (!UlIsValidLogDirectory(
  335. &pUserConfig->Dir,
  336. FALSE, // UncSupport
  337. TRUE // SystemRootSupport
  338. ))
  339. {
  340. Status = STATUS_NOT_SUPPORTED;
  341. }
  342. }
  343. if (!NT_SUCCESS(Status))
  344. {
  345. UlWriteEventLogEntry(
  346. EVENT_HTTP_LOGGING_ERROR_FILE_CONFIG_FAILED,
  347. 0,
  348. 0,
  349. NULL,
  350. sizeof(NTSTATUS),
  351. (PVOID) &Status
  352. );
  353. UlTrace(ERROR_LOGGING,
  354. ("Http!UlCheckErrorLogDir: failed for : (%S)\n",
  355. pUserConfig->Dir.Buffer
  356. ));
  357. }
  358. return Status;
  359. }
  360. /***************************************************************************++
  361. Routine Description:
  362. When logging configuration happens we init the entry but do not create the
  363. error log file itself yet. That will be created when the first request
  364. comes in.
  365. Arguments:
  366. pUserConfig - Error Logging config from the registry.
  367. --***************************************************************************/
  368. NTSTATUS
  369. UlConfigErrorLogEntry(
  370. IN PHTTP_ERROR_LOGGING_CONFIG pUserConfig
  371. )
  372. {
  373. KIRQL OldIrql;
  374. PUL_ERROR_LOG_FILE_ENTRY pEntry = &g_ErrorLogEntry;
  375. //
  376. // Sanity check.
  377. //
  378. PAGED_CODE();
  379. //
  380. // If disabled do not proceed.
  381. //
  382. if (pUserConfig->Enabled == FALSE)
  383. {
  384. InterlockedExchange(&g_ErrorLoggingEnabled, 0);
  385. UlTrace(ERROR_LOGGING,
  386. ("Http!UlConfigErrorLogEntry: Error Logging Disabled !\n",
  387. pEntry
  388. ));
  389. return STATUS_SUCCESS;
  390. }
  391. //
  392. // Registry reader shouldn't accept an improper config in the
  393. // first place.
  394. //
  395. ASSERT(NT_SUCCESS(UlCheckErrorLogConfig(pUserConfig)));
  396. //
  397. // Acquire the entry lock and resurrect the entry.
  398. //
  399. UlAcquirePushLockExclusive(&pEntry->PushLock);
  400. //
  401. // Remember the logging directory in the entry for the time
  402. // being. Also allocate sufficient space to hold the max
  403. // possible file name plus the existing directory string.
  404. // So that logutil doesn't need to realloc this buffer again.
  405. //
  406. pEntry->FileName.Buffer =
  407. (PWSTR) UL_ALLOCATE_ARRAY(
  408. PagedPool,
  409. UCHAR,
  410. pUserConfig->Dir.MaximumLength
  411. + ERROR_LOG_MAX_FULL_FILE_NAME_SIZE,
  412. UL_CG_LOGDIR_POOL_TAG
  413. );
  414. if (pEntry->FileName.Buffer == NULL)
  415. {
  416. UlReleasePushLockExclusive(&pEntry->PushLock);
  417. return STATUS_NO_MEMORY;
  418. }
  419. pEntry->FileName.Length =
  420. pUserConfig->Dir.Length;
  421. pEntry->FileName.MaximumLength =
  422. pUserConfig->Dir.MaximumLength
  423. + ERROR_LOG_MAX_FULL_FILE_NAME_SIZE;
  424. RtlCopyMemory(
  425. pEntry->FileName.Buffer ,
  426. pUserConfig->Dir.Buffer,
  427. pUserConfig->Dir.MaximumLength
  428. );
  429. //
  430. // Now set the fields on the binary log entry accordingly.
  431. //
  432. pEntry->TruncateSize = pUserConfig->TruncateSize;
  433. pEntry->SequenceNumber = 1;
  434. pEntry->TotalWritten.QuadPart = (ULONGLONG) 0;
  435. //
  436. // Start the buffer flush timer as soon as the configuration
  437. // happens.
  438. //
  439. UlAcquireSpinLock(&pEntry->BufferTimer.SpinLock, &OldIrql);
  440. if (pEntry->BufferTimer.Started == FALSE)
  441. {
  442. UlSetBufferTimer(&pEntry->BufferTimer);
  443. pEntry->BufferTimer.Started = TRUE;
  444. }
  445. UlReleaseSpinLock(&pEntry->BufferTimer.SpinLock, OldIrql);
  446. UlTrace(ERROR_LOGGING,
  447. ("Http!UlConfigErrorLogEntry: pEntry %p for %S\n",
  448. pEntry,
  449. pUserConfig->Dir.Buffer
  450. ));
  451. UlReleasePushLockExclusive(&pEntry->PushLock);
  452. //
  453. // Mark it as enabled.
  454. //
  455. InterlockedExchange(&g_ErrorLoggingEnabled, 1);
  456. return STATUS_SUCCESS;
  457. }
  458. /***************************************************************************++
  459. Routine Description:
  460. Create a new error log file or open an existing one. The fully qualified
  461. file name should be in the error log entry.
  462. Arguments:
  463. pEntry : Corresponding entry that we are closing and opening
  464. the error log files for.
  465. --***************************************************************************/
  466. NTSTATUS
  467. UlpCreateErrorLogFile(
  468. IN OUT PUL_ERROR_LOG_FILE_ENTRY pEntry
  469. )
  470. {
  471. NTSTATUS Status;
  472. PUNICODE_STRING pDir;
  473. //
  474. // Sanity check.
  475. //
  476. PAGED_CODE();
  477. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  478. pDir = &g_UlErrLoggingConfig.Dir;
  479. //
  480. // Build the fully qualified error log file name.
  481. //
  482. Status = UlRefreshFileName(pDir,
  483. &pEntry->FileName,
  484. &pEntry->pShortName
  485. );
  486. if (!NT_SUCCESS(Status))
  487. {
  488. return Status;
  489. }
  490. //
  491. // SequenceNumber is stale because we have to scan the existing
  492. // directory the first time we open a file.
  493. //
  494. pEntry->Flags.StaleSequenceNumber = 1;
  495. //
  496. // After that Recycle does the whole job for us.
  497. //
  498. Status = UlpRecycleErrorLogFile(pEntry);
  499. if (!NT_SUCCESS(Status))
  500. {
  501. UlTrace(ERROR_LOGGING,
  502. ("Http!UlpCreateErrorLogFile: Filename: %S Failure %08lx\n",
  503. pEntry->FileName.Buffer,
  504. Status
  505. ));
  506. }
  507. UlTrace(ERROR_LOGGING,
  508. ("Http!UlpCreateErrorLogFile: pEntry %p for %S to %S\n",
  509. pEntry,
  510. pDir->Buffer,
  511. pEntry->FileName.Buffer
  512. ));
  513. return Status;
  514. }
  515. /***************************************************************************++
  516. Routine Description:
  517. Simple wrapper function around the global buffer flush routine.
  518. Arguments:
  519. pEntry - Error Log file entry
  520. --***************************************************************************/
  521. NTSTATUS
  522. UlpFlushErrorLogFile(
  523. IN PUL_ERROR_LOG_FILE_ENTRY pEntry
  524. )
  525. {
  526. NTSTATUS Status = STATUS_SUCCESS;
  527. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  528. if (NULL != pEntry->LogBuffer && 0 != pEntry->LogBuffer->BufferUsed)
  529. {
  530. Status = UlFlushLogFileBuffer(
  531. &pEntry->LogBuffer,
  532. pEntry->pLogFile,
  533. FALSE,
  534. &pEntry->TotalWritten.QuadPart
  535. );
  536. if (!NT_SUCCESS(Status))
  537. {
  538. if (!pEntry->Flags.WriteFailureLogged)
  539. {
  540. NTSTATUS TempStatus;
  541. TempStatus =
  542. UlWriteEventLogEntry(
  543. (NTSTATUS)EVENT_HTTP_LOGGING_ERROR_FILE_WRITE_FAILED,
  544. 0,
  545. 0,
  546. NULL,
  547. sizeof(NTSTATUS),
  548. (PVOID) &Status
  549. );
  550. ASSERT(TempStatus != STATUS_BUFFER_OVERFLOW);
  551. if (TempStatus == STATUS_SUCCESS)
  552. {
  553. pEntry->Flags.WriteFailureLogged = 1;
  554. }
  555. }
  556. }
  557. else
  558. {
  559. //
  560. // If we have successfully flushed some data.
  561. // Reset the event log indication.
  562. //
  563. pEntry->Flags.WriteFailureLogged = 0;
  564. }
  565. }
  566. return Status;
  567. }
  568. /***************************************************************************++
  569. Routine Description:
  570. Marks the entry inactive, closes the existing file.
  571. Caller should hold the error log entry pushlock exclusive.
  572. Arguments:
  573. pEntry - The log file entry which we will mark inactive.
  574. --***************************************************************************/
  575. NTSTATUS
  576. UlpDisableErrorLogEntry(
  577. IN OUT PUL_ERROR_LOG_FILE_ENTRY pEntry
  578. )
  579. {
  580. //
  581. // Sanity checks
  582. //
  583. PAGED_CODE();
  584. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  585. UlTrace(ERROR_LOGGING,
  586. ("Http!UlpDisableErrorLogEntry: pEntry %p disabled.\n",
  587. pEntry
  588. ));
  589. //
  590. // Flush and close the old file until the next recycle.
  591. //
  592. if (pEntry->pLogFile)
  593. {
  594. UlpFlushErrorLogFile(pEntry);
  595. UlCloseLogFile(
  596. &pEntry->pLogFile
  597. );
  598. }
  599. //
  600. // Mark this inactive so that the next http hit awakens the entry.
  601. //
  602. pEntry->Flags.Active = 0;
  603. return STATUS_SUCCESS;
  604. }
  605. /***************************************************************************++
  606. Routine Description:
  607. Small wrapper around handle recycle to ensure it happens under the system
  608. process context.
  609. Arguments:
  610. pEntry - Points to error log file entry to be recycled.
  611. --***************************************************************************/
  612. NTSTATUS
  613. UlpRecycleErrorLogFile(
  614. IN OUT PUL_ERROR_LOG_FILE_ENTRY pEntry
  615. )
  616. {
  617. NTSTATUS Status;
  618. PAGED_CODE();
  619. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  620. Status = UlQueueLoggingRoutine(
  621. (PVOID) pEntry,
  622. &UlpHandleErrorLogFileRecycle
  623. );
  624. return Status;
  625. }
  626. /***************************************************************************++
  627. Routine Description:
  628. This function requires to have the entry resource to be acquired.
  629. Sometimes it may be necessary to scan the new directory to figure out
  630. the correct sequence number and the file name. Especially after a dir
  631. name reconfig.
  632. Arguments:
  633. pEntry - Points to the error log file entry
  634. --***************************************************************************/
  635. NTSTATUS
  636. UlpHandleErrorLogFileRecycle(
  637. IN OUT PVOID pContext
  638. )
  639. {
  640. NTSTATUS Status;
  641. PUL_ERROR_LOG_FILE_ENTRY pEntry;
  642. TIME_FIELDS TimeFields;
  643. LARGE_INTEGER TimeStamp;
  644. PUL_LOG_FILE_HANDLE pLogFile;
  645. WCHAR _FileName[UL_MAX_FILE_NAME_SUFFIX_LENGTH + 1];
  646. UNICODE_STRING FileName;
  647. BOOLEAN UncShare;
  648. BOOLEAN ACLSupport;
  649. //
  650. // Sanity check.
  651. //
  652. PAGED_CODE();
  653. pEntry = (PUL_ERROR_LOG_FILE_ENTRY) pContext;
  654. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  655. Status = STATUS_SUCCESS;
  656. pLogFile = NULL;
  657. FileName.Buffer = _FileName;
  658. FileName.Length = 0;
  659. FileName.MaximumLength = sizeof(_FileName);
  660. ASSERT(pEntry->FileName.Length !=0 );
  661. UlTrace(ERROR_LOGGING,
  662. ("Http!UlpHandleErrorLogFileRecycle: pEntry %p \n", pEntry ));
  663. //
  664. // This value is computed for the GMT time zone.
  665. //
  666. KeQuerySystemTime(&TimeStamp);
  667. RtlTimeToTimeFields(&TimeStamp, &TimeFields);
  668. //
  669. // If we need to scan the directory. Sequence number should start
  670. // from 1 again. Set this before constructing the log file name.
  671. //
  672. if (pEntry->Flags.StaleSequenceNumber)
  673. {
  674. //
  675. // Init otherwise if QueryDirectory doesn't find any files
  676. // in the provided directory, this will not get properly
  677. // initialized.
  678. //
  679. pEntry->SequenceNumber = 1;
  680. }
  681. //
  682. // Use binary logging settings when constructing the filename.
  683. //
  684. UlConstructFileName(
  685. HttpLoggingPeriodMaxSize,
  686. ERROR_LOG_FILE_NAME_PREFIX,
  687. ERROR_LOG_FILE_NAME_EXTENSION,
  688. &FileName,
  689. &TimeFields,
  690. FALSE,
  691. &pEntry->SequenceNumber
  692. );
  693. if (pEntry->FileName.MaximumLength <= FileName.Length)
  694. {
  695. ASSERT(!"FileName buffer is not sufficient.");
  696. Status = STATUS_INVALID_PARAMETER;
  697. goto end;
  698. }
  699. //
  700. // Do the magic and renew the filename. Replace the old file
  701. // name with the new one.
  702. //
  703. ASSERT(pEntry->pShortName != NULL);
  704. //
  705. // Get rid of the old filename before scanning the
  706. // directories.
  707. //
  708. *((PWCHAR)pEntry->pShortName) = UNICODE_NULL;
  709. pEntry->FileName.Length =
  710. (USHORT) wcslen( pEntry->FileName.Buffer ) * sizeof(WCHAR);
  711. //
  712. // Create/Open the director(ies) first. This might be
  713. // necessary if we get called after an entry reconfiguration
  714. // and directory name change or for the first time we
  715. // try to create/open the log file.
  716. //
  717. Status = UlCreateSafeDirectory(&pEntry->FileName,
  718. &UncShare,
  719. &ACLSupport
  720. );
  721. if (!NT_SUCCESS(Status))
  722. goto eventlog;
  723. ASSERT(FALSE == UncShare);
  724. //
  725. // Now Restore the short file name pointer back
  726. //
  727. pEntry->pShortName = (PWSTR)
  728. &(pEntry->FileName.Buffer[pEntry->FileName.Length/sizeof(WCHAR)]);
  729. //
  730. // Append the new file name ( based on the updated current time )
  731. // to the end.
  732. //
  733. Status = RtlAppendUnicodeStringToString(&pEntry->FileName, &FileName);
  734. if (!NT_SUCCESS(Status))
  735. goto end;
  736. //
  737. // Time to close the old file and reopen a new one
  738. //
  739. if (pEntry->pLogFile != NULL)
  740. {
  741. //
  742. // Flush,close and mark the entry inactive.
  743. //
  744. UlpDisableErrorLogEntry(pEntry);
  745. }
  746. ASSERT(pEntry->pLogFile == NULL);
  747. //
  748. // If the sequence is stale because of the nature of the recycle.
  749. // And if our period is size based then rescan the new directory
  750. // to figure out the proper file to open.
  751. //
  752. pEntry->TotalWritten.QuadPart = (ULONGLONG) 0;
  753. if (pEntry->Flags.StaleSequenceNumber)
  754. {
  755. // This call may update the filename, the file size and the
  756. // sequence number if there is an old file in the new dir.
  757. Status = UlQueryDirectory(
  758. &pEntry->FileName,
  759. pEntry->pShortName,
  760. ERROR_LOG_FILE_NAME_PREFIX,
  761. ERROR_LOG_FILE_NAME_EXTENSION_PLUS_DOT,
  762. &pEntry->SequenceNumber,
  763. &pEntry->TotalWritten.QuadPart
  764. );
  765. if (!NT_SUCCESS(Status))
  766. {
  767. if (Status == STATUS_ACCESS_DENIED)
  768. {
  769. Status = STATUS_INVALID_OWNER;
  770. goto eventlog;
  771. }
  772. else
  773. {
  774. goto end;
  775. }
  776. }
  777. }
  778. //
  779. // Allocate a new log file structure for the new log file we are
  780. // about to open or create.
  781. //
  782. pLogFile = pEntry->pLogFile =
  783. UL_ALLOCATE_STRUCT(
  784. NonPagedPool,
  785. UL_LOG_FILE_HANDLE,
  786. UL_LOG_FILE_HANDLE_POOL_TAG
  787. );
  788. if (pLogFile == NULL)
  789. {
  790. Status = STATUS_NO_MEMORY;
  791. goto end;
  792. }
  793. pLogFile->Signature = UL_LOG_FILE_HANDLE_POOL_TAG;
  794. pLogFile->hFile = NULL;
  795. UlInitializeWorkItem(&pLogFile->WorkItem);
  796. //
  797. // Create the new log file.
  798. //
  799. Status = UlCreateLogFile(&pEntry->FileName,
  800. UncShare,
  801. ACLSupport,
  802. &pLogFile->hFile
  803. );
  804. if (!NT_SUCCESS(Status))
  805. {
  806. goto eventlog;
  807. }
  808. ASSERT(pLogFile->hFile);
  809. pEntry->TotalWritten.QuadPart = UlGetLogFileLength(pLogFile->hFile);
  810. //
  811. // File is successfully opened and the entry is no longer inactive.
  812. // Update our state flags accordingly.
  813. //
  814. pEntry->Flags.Active = 1;
  815. pEntry->Flags.RecyclePending = 0;
  816. pEntry->Flags.StaleSequenceNumber = 0;
  817. pEntry->Flags.CreateFileFailureLogged = 0;
  818. UlTrace(ERROR_LOGGING,
  819. ("Http!UlpHandleErrorLogFileRecycle: entry %p, file %S, handle %lx\n",
  820. pEntry,
  821. pEntry->FileName.Buffer,
  822. pLogFile->hFile
  823. ));
  824. eventlog:
  825. if (!NT_SUCCESS(Status))
  826. {
  827. if (!pEntry->Flags.CreateFileFailureLogged)
  828. {
  829. NTSTATUS TempStatus;
  830. TempStatus = UlEventLogCreateFailure(
  831. Status,
  832. UlEventLogError,
  833. &pEntry->FileName,
  834. 0
  835. );
  836. if (TempStatus == STATUS_SUCCESS)
  837. {
  838. //
  839. // Avoid filling up the event log with error entries.
  840. // This code path might get hit every time a request
  841. // arrives.
  842. //
  843. pEntry->Flags.CreateFileFailureLogged = 1;
  844. }
  845. UlTrace(ERROR_LOGGING,(
  846. "Http!UlpHandleErrorLogFileRecycle: Event Logging Status %08lx\n",
  847. TempStatus
  848. ));
  849. }
  850. }
  851. end:
  852. if (!NT_SUCCESS(Status))
  853. {
  854. UlTrace(ERROR_LOGGING,
  855. ("Http!UlpHandleErrorLogFileRecycle: entry %p, failure %08lx\n",
  856. pEntry,
  857. Status
  858. ));
  859. if (pLogFile != NULL)
  860. {
  861. //
  862. // This means we have alread closed the old file but failed
  863. // when we try to create or open the new one.
  864. //
  865. ASSERT(pLogFile->hFile == NULL);
  866. UL_FREE_POOL_WITH_SIG(pLogFile,UL_LOG_FILE_HANDLE_POOL_TAG);
  867. pEntry->pLogFile = NULL;
  868. pEntry->Flags.Active = 0;
  869. }
  870. else
  871. {
  872. //
  873. // We were about to recyle the old one but something failed
  874. // lets try to flush and close the existing file if it's still
  875. // around.
  876. //
  877. if (pEntry->pLogFile)
  878. {
  879. UlpDisableErrorLogEntry(pEntry);
  880. }
  881. }
  882. //
  883. // Mark this entry RecyclePending so that buffer timer can try to
  884. // resurrect this back every minute.
  885. //
  886. pEntry->Flags.RecyclePending = 1;
  887. }
  888. return Status;
  889. }
  890. /***************************************************************************++
  891. Routine Description:
  892. Closes the error log file entry.
  893. Arguments:
  894. - none -
  895. --***************************************************************************/
  896. VOID
  897. UlCloseErrorLogEntry(
  898. VOID
  899. )
  900. {
  901. PUL_ERROR_LOG_FILE_ENTRY pEntry = &g_ErrorLogEntry;
  902. //
  903. // No more error logging !
  904. //
  905. PAGED_CODE();
  906. InterlockedExchange(&g_ErrorLoggingEnabled, 0);
  907. UlAcquirePushLockExclusive(&pEntry->PushLock);
  908. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  909. if (pEntry->pLogFile)
  910. {
  911. //
  912. // Flush the buffer, close the file and mark the entry
  913. // inactive.
  914. //
  915. UlpDisableErrorLogEntry(pEntry);
  916. }
  917. //
  918. // Free up the FileName (allocated when the entry becomes active
  919. // otherwise it's empty)
  920. //
  921. if (pEntry->FileName.Buffer)
  922. {
  923. UL_FREE_POOL(pEntry->FileName.Buffer,UL_CG_LOGDIR_POOL_TAG);
  924. pEntry->FileName.Buffer = NULL;
  925. }
  926. if (pEntry->LogBuffer)
  927. {
  928. // TODO: Is this really necessary here ?
  929. ASSERT(FALSE);
  930. UlPplFreeLogFileBuffer(pEntry->LogBuffer);
  931. }
  932. UlReleasePushLockExclusive(&pEntry->PushLock);
  933. UlTrace(ERROR_LOGGING,
  934. ("Http!UlCloseErrorLogEntry: pEntry %p closed.\n",
  935. pEntry
  936. ));
  937. }
  938. /***************************************************************************++
  939. Routine Description:
  940. Private allocator if lookaside list entries are not big enough.
  941. Arguments:
  942. pLogInfo - Log info structure also holds the default allocated buffer.
  943. Return Status
  944. STATUS_INSUFFICIENT_RESOURCES - if buffer allocation fails.
  945. STATUS_SUCCESS - Otherwise
  946. --***************************************************************************/
  947. PUL_ERROR_LOG_BUFFER
  948. UlpAllocErrorLogBuffer(
  949. IN ULONG BufferSize
  950. )
  951. {
  952. PUL_ERROR_LOG_BUFFER pErrorLogBuffer = NULL;
  953. USHORT BytesNeeded = (USHORT) ALIGN_UP(BufferSize, PVOID);
  954. //
  955. // It should be bigger than the default size
  956. //
  957. ASSERT(BufferSize > UL_ERROR_LOG_BUFFER_SIZE);
  958. pErrorLogBuffer =
  959. UL_ALLOCATE_STRUCT_WITH_SPACE(
  960. PagedPool,
  961. UL_ERROR_LOG_BUFFER,
  962. BytesNeeded,
  963. UL_ERROR_LOG_BUFFER_POOL_TAG
  964. );
  965. if (pErrorLogBuffer)
  966. {
  967. pErrorLogBuffer->Signature = UL_ERROR_LOG_BUFFER_POOL_TAG;
  968. pErrorLogBuffer->Used = 0;
  969. pErrorLogBuffer->pBuffer = (PUCHAR) (pErrorLogBuffer + 1);
  970. pErrorLogBuffer->IsFromLookaside = FALSE;
  971. }
  972. return pErrorLogBuffer;
  973. }
  974. /***************************************************************************++
  975. Routine Description:
  976. After we are done with writing this record we have to clean up
  977. the internal error log buffer structure here.
  978. Arguments:
  979. pErrorLogBuffer - Will be freed up.
  980. --***************************************************************************/
  981. VOID
  982. UlpFreeErrorLogBuffer(
  983. IN OUT PUL_ERROR_LOG_BUFFER pErrorLogBuffer
  984. )
  985. {
  986. if (pErrorLogBuffer->IsFromLookaside)
  987. {
  988. UlPplFreeErrorLogBuffer(pErrorLogBuffer);
  989. }
  990. else
  991. {
  992. //
  993. // Large log line get allocated from paged pool we better
  994. // be running on lowered IRQL if that's the case.
  995. //
  996. PAGED_CODE();
  997. UL_FREE_POOL_WITH_SIG(
  998. pErrorLogBuffer,
  999. UL_ERROR_LOG_BUFFER_POOL_TAG
  1000. );
  1001. }
  1002. }
  1003. /***************************************************************************++
  1004. Routine Description:
  1005. This function will build the error log record in a temp buffer
  1006. The provided log info is used to build the individual log fields.
  1007. Arguments:
  1008. pLogInfo - Log info structure also holds the default allocated buffer.
  1009. Return Status
  1010. STATUS_INSUFFICIENT_RESOURCES - if buffer allocation fails.
  1011. STATUS_SUCCESS - Otherwise
  1012. --***************************************************************************/
  1013. NTSTATUS
  1014. UlpBuildErrorLogRecord(
  1015. IN PUL_ERROR_LOG_INFO pLogInfo
  1016. )
  1017. {
  1018. #define ERROR_LOG_BUILD_SEPERATOR(psz) \
  1019. { \
  1020. *(psz)++ = ERROR_LOG_FIELD_SEPERATOR_CHAR; \
  1021. }
  1022. #define ERROR_LOG_BUILD_EMPTY_FIELD(psz) \
  1023. { \
  1024. *(psz)++ = ERROR_LOG_FIELD_NOT_EXISTS_CHAR; \
  1025. ERROR_LOG_BUILD_SEPERATOR( psz ) \
  1026. }
  1027. #define ERROR_LOG_SANITIZE_UNICODE_FIELD(pszT,psz) \
  1028. while ((pszT) != (psz)) \
  1029. { \
  1030. if (IS_CHAR_TYPE((*(pszT)),HTTP_ISWHITE)) \
  1031. { \
  1032. *(pszT) = ERROR_LOG_FIELD_BAD_CHAR; \
  1033. } \
  1034. (pszT)++; \
  1035. }
  1036. ULONG BytesRequired = MAX_ERROR_LOG_FIX_FIELD_OVERHEAD;
  1037. ULONG BytesConverted = 0;
  1038. ULONG BytesAllocated = UL_ERROR_LOG_BUFFER_SIZE;
  1039. PUL_INTERNAL_REQUEST pRequest = NULL;
  1040. PUL_HTTP_CONNECTION pHttpConn = NULL;
  1041. ULONG UrlSize = 0;
  1042. BOOLEAN bRawUrl = FALSE;
  1043. PCHAR psz = NULL;
  1044. UNREFERENCED_PARAMETER(BytesAllocated);
  1045. //
  1046. // Sanity checks.
  1047. //
  1048. PAGED_CODE();
  1049. //
  1050. // Get the pointers to understand what we need to log.
  1051. //
  1052. ASSERT(IS_VALID_ERROR_LOG_INFO(pLogInfo));
  1053. if (pLogInfo->pRequest)
  1054. {
  1055. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pLogInfo->pRequest));
  1056. pRequest = pLogInfo->pRequest;
  1057. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));
  1058. pHttpConn = pRequest->pHttpConn;
  1059. }
  1060. if (pLogInfo->pHttpConn)
  1061. {
  1062. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pLogInfo->pHttpConn));
  1063. pHttpConn = pLogInfo->pHttpConn;
  1064. }
  1065. //
  1066. // Precalculate the max required bytes to check against
  1067. // the default buffer size.
  1068. //
  1069. if (pRequest)
  1070. {
  1071. UrlSize = UlpCalculateUrlSize(pRequest, &bRawUrl);
  1072. BytesRequired += UrlSize;
  1073. }
  1074. if (pLogInfo->pInfo)
  1075. {
  1076. ASSERT(pLogInfo->InfoSize);
  1077. BytesRequired += pLogInfo->InfoSize;
  1078. }
  1079. UlTrace(ERROR_LOGGING,
  1080. ("Http!UlPplAllocateErrorLogBuffer: Rb:(%d) Os:(%d) Ls:(%d)\n",
  1081. BytesRequired, MAX_ERROR_LOG_FIX_FIELD_OVERHEAD,
  1082. UL_ERROR_LOG_BUFFER_SIZE
  1083. ));
  1084. if (BytesRequired > UL_ERROR_LOG_BUFFER_SIZE)
  1085. {
  1086. //
  1087. // Lookaside buffer is not big enough to hold the logging data.
  1088. //
  1089. pLogInfo->pErrorLogBuffer = UlpAllocErrorLogBuffer(BytesRequired);
  1090. BytesAllocated = BytesRequired;
  1091. }
  1092. else
  1093. {
  1094. //
  1095. // Default buffer is big enough, try to pop it from the lookaside list.
  1096. //
  1097. pLogInfo->pErrorLogBuffer = UlPplAllocateErrorLogBuffer();
  1098. }
  1099. if (pLogInfo->pErrorLogBuffer == NULL)
  1100. {
  1101. return STATUS_INSUFFICIENT_RESOURCES;
  1102. }
  1103. psz = (PCHAR) pLogInfo->pErrorLogBuffer->pBuffer;
  1104. //
  1105. // Copy all the fields.
  1106. //
  1107. BytesConverted = 0;
  1108. UlGetDateTimeFields(
  1109. HttpLoggingTypeW3C, // Date
  1110. psz,
  1111. &BytesConverted,
  1112. NULL,
  1113. NULL
  1114. );
  1115. psz += BytesConverted;
  1116. ASSERT(BytesConverted == ERR_DATE_FIELD_LEN);
  1117. ERROR_LOG_BUILD_SEPERATOR(psz);
  1118. BytesConverted = 0;
  1119. UlGetDateTimeFields(
  1120. HttpLoggingTypeW3C, // Time
  1121. NULL,
  1122. NULL,
  1123. psz,
  1124. &BytesConverted
  1125. );
  1126. psz += BytesConverted;
  1127. ASSERT(BytesConverted == ERR_TIME_FIELD_LEN);
  1128. ERROR_LOG_BUILD_SEPERATOR(psz);
  1129. if (pHttpConn)
  1130. {
  1131. // Client IP & Port
  1132. psz = UlStrPrintIPAndPort(
  1133. psz,
  1134. pHttpConn->pConnection->RemoteAddress,
  1135. pHttpConn->pConnection->AddressType,
  1136. ERROR_LOG_FIELD_SEPERATOR_CHAR
  1137. );
  1138. // Server IP & Port
  1139. psz = UlStrPrintIPAndPort(
  1140. psz,
  1141. pHttpConn->pConnection->LocalAddress,
  1142. pHttpConn->pConnection->AddressType,
  1143. ERROR_LOG_FIELD_SEPERATOR_CHAR
  1144. );
  1145. }
  1146. else
  1147. {
  1148. ERROR_LOG_BUILD_EMPTY_FIELD(psz);
  1149. ERROR_LOG_BUILD_EMPTY_FIELD(psz);
  1150. }
  1151. if (pRequest)
  1152. {
  1153. // Version
  1154. if (pRequest->ParseState > ParseVersionState)
  1155. {
  1156. psz = UlCopyHttpVersion(
  1157. psz,
  1158. pRequest->Version,
  1159. ERROR_LOG_FIELD_SEPERATOR_CHAR
  1160. );
  1161. }
  1162. else
  1163. {
  1164. ERROR_LOG_BUILD_EMPTY_FIELD(psz);
  1165. }
  1166. // Verb
  1167. if (pRequest->ParseState > ParseVerbState)
  1168. {
  1169. psz = UlCopyHttpVerb(
  1170. psz,
  1171. pRequest,
  1172. ERROR_LOG_FIELD_SEPERATOR_CHAR
  1173. );
  1174. }
  1175. else
  1176. {
  1177. ERROR_LOG_BUILD_EMPTY_FIELD(psz);
  1178. }
  1179. }
  1180. else
  1181. {
  1182. ERROR_LOG_BUILD_EMPTY_FIELD(psz);
  1183. ERROR_LOG_BUILD_EMPTY_FIELD(psz);
  1184. }
  1185. //
  1186. // Do LocalCodePage conversion for a cooked Uri.
  1187. // If query exists, it will be at the end of the Uri.
  1188. //
  1189. if (UrlSize)
  1190. {
  1191. PCHAR pszT = psz;
  1192. if (bRawUrl)
  1193. {
  1194. ASSERT(pRequest->RawUrl.pAbsPath);
  1195. RtlCopyMemory( psz,
  1196. pRequest->RawUrl.pAbsPath,
  1197. UrlSize
  1198. );
  1199. psz += UrlSize;
  1200. }
  1201. else
  1202. {
  1203. ASSERT(pRequest->CookedUrl.pAbsPath);
  1204. BytesConverted = 0;
  1205. RtlUnicodeToMultiByteN(
  1206. psz,
  1207. MAX_LOG_EXTEND_FIELD_LEN,
  1208. &BytesConverted,
  1209. (PWSTR) pRequest->CookedUrl.pAbsPath,
  1210. UrlSize
  1211. );
  1212. psz += BytesConverted;
  1213. }
  1214. ERROR_LOG_SANITIZE_UNICODE_FIELD(pszT, psz);
  1215. ERROR_LOG_BUILD_SEPERATOR(psz);
  1216. }
  1217. else
  1218. {
  1219. ERROR_LOG_BUILD_EMPTY_FIELD(psz);
  1220. }
  1221. // Protocol Status
  1222. if (pLogInfo->ProtocolStatus != UL_PROTOCOL_STATUS_NA)
  1223. {
  1224. psz = UlStrPrintProtocolStatus(
  1225. psz,
  1226. pLogInfo->ProtocolStatus,
  1227. ERROR_LOG_FIELD_SEPERATOR_CHAR
  1228. );
  1229. }
  1230. else
  1231. {
  1232. ERROR_LOG_BUILD_EMPTY_FIELD(psz);
  1233. }
  1234. // Site Id field. Log this field only if the Site Id
  1235. // is set. (non-zero)
  1236. if (pRequest && pRequest->ConfigInfo.SiteId)
  1237. {
  1238. psz = UlStrPrintUlong(
  1239. psz,
  1240. pRequest->ConfigInfo.SiteId,
  1241. ERROR_LOG_FIELD_SEPERATOR_CHAR
  1242. );
  1243. }
  1244. else
  1245. {
  1246. ERROR_LOG_BUILD_EMPTY_FIELD(psz);
  1247. }
  1248. // No seperator after the Informational field
  1249. // because it is the last one.
  1250. if (pLogInfo->pInfo)
  1251. {
  1252. ASSERT(ANSI_NULL !=
  1253. pLogInfo->pInfo[pLogInfo->InfoSize - 1]);
  1254. RtlCopyMemory( psz,
  1255. pLogInfo->pInfo,
  1256. pLogInfo->InfoSize
  1257. );
  1258. psz += pLogInfo->InfoSize;
  1259. }
  1260. else
  1261. {
  1262. *psz++ = ERROR_LOG_FIELD_NOT_EXISTS_CHAR;
  1263. }
  1264. // Terminate the line with "\r\n"
  1265. *psz++ = '\r'; *psz++ = '\n';
  1266. //
  1267. // Done make sure that we didn't buffer overflow
  1268. //
  1269. pLogInfo->pErrorLogBuffer->Used =
  1270. DIFF(psz - (PCHAR)pLogInfo->pErrorLogBuffer->pBuffer);
  1271. ASSERT(pLogInfo->pErrorLogBuffer->Used <= BytesRequired);
  1272. ASSERT(pLogInfo->pErrorLogBuffer->Used <= BytesAllocated);
  1273. return STATUS_SUCCESS;
  1274. }
  1275. /***************************************************************************++
  1276. Routine Description:
  1277. Error logging for those requests/connections that are not routed up
  1278. to the worker process. Basically requests/connections refused by
  1279. the driver are logged here. As well as appool process crashes. This is
  1280. a driver wide error logging functionality.
  1281. Arguments:
  1282. pLogInfo - This should contains the necessary info and the pointers
  1283. for the error log to be created.
  1284. --***************************************************************************/
  1285. NTSTATUS
  1286. UlLogHttpError(
  1287. IN PUL_ERROR_LOG_INFO pLogInfo
  1288. )
  1289. {
  1290. NTSTATUS Status = STATUS_SUCCESS;
  1291. PUL_ERROR_LOG_FILE_ENTRY pEntry = &g_ErrorLogEntry;
  1292. //
  1293. // Sanity checks.
  1294. //
  1295. PAGED_CODE();
  1296. ASSERT(IS_VALID_ERROR_LOG_INFO(pLogInfo));
  1297. //ASSERT(UlErrorLoggingEnabled());
  1298. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  1299. UlTrace(ERROR_LOGGING,("Http!UlLogHttpError: pLogInfo %p\n", pLogInfo ));
  1300. //
  1301. // Bail out if disabled.
  1302. //
  1303. if (!UlErrorLoggingEnabled())
  1304. {
  1305. return STATUS_SUCCESS;
  1306. }
  1307. //
  1308. // Proceed with building the record from the passed-in info.
  1309. //
  1310. Status = UlpBuildErrorLogRecord(pLogInfo);
  1311. if (!NT_SUCCESS(Status))
  1312. {
  1313. return Status;
  1314. }
  1315. ASSERT(IS_VALID_ERROR_LOG_BUFFER(pLogInfo->pErrorLogBuffer));
  1316. ASSERT(pLogInfo->pErrorLogBuffer->Used);
  1317. ASSERT(pLogInfo->pErrorLogBuffer->pBuffer);
  1318. //
  1319. // Open the error log file if necessary.
  1320. //
  1321. if (!pEntry->Flags.Active)
  1322. {
  1323. UlAcquirePushLockExclusive(&pEntry->PushLock);
  1324. //
  1325. // Ping again to see if we have been blocked on the lock, and
  1326. // somebody else already took care of the creation.
  1327. //
  1328. if (!pEntry->Flags.Active)
  1329. {
  1330. Status = UlpCreateErrorLogFile(pEntry);
  1331. }
  1332. UlReleasePushLockExclusive(&pEntry->PushLock);
  1333. }
  1334. if (NT_SUCCESS(Status))
  1335. {
  1336. Status =
  1337. UlpWriteToErrorLogFile (
  1338. pEntry,
  1339. pLogInfo->pErrorLogBuffer->Used,
  1340. pLogInfo->pErrorLogBuffer->pBuffer
  1341. );
  1342. }
  1343. //
  1344. // Free up the error log record before returning.
  1345. //
  1346. UlpFreeErrorLogBuffer(pLogInfo->pErrorLogBuffer);
  1347. pLogInfo->pErrorLogBuffer = NULL;
  1348. return Status;
  1349. }
  1350. /***************************************************************************++
  1351. Routine Description:
  1352. Exclusive (Debug) writer function. Basically it flushes the buffer
  1353. everytime we write a record to the file buffer.
  1354. REQUIRES you to hold the error log entry lock EXCLUSIVE.
  1355. Arguments:
  1356. pEntry - The binary log file entry we are working on.
  1357. RecordSize - The amount (in bytes) of data will be copied.
  1358. pUserRecord - The actual log record to go to file buffer.
  1359. --***************************************************************************/
  1360. NTSTATUS
  1361. UlpWriteToErrorLogFileDebug(
  1362. IN PUL_ERROR_LOG_FILE_ENTRY pEntry,
  1363. IN ULONG RecordSize,
  1364. IN PUCHAR pUserRecord
  1365. )
  1366. {
  1367. NTSTATUS Status;
  1368. PUL_LOG_FILE_BUFFER pLogBuffer;
  1369. PAGED_CODE();
  1370. ASSERT(RecordSize);
  1371. ASSERT(pUserRecord);
  1372. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  1373. UlTrace(ERROR_LOGGING,
  1374. ("Http!UlpWriteToErrorLogFileDebug: pEntry %p\n", pEntry));
  1375. ASSERT(UlDbgPushLockOwnedExclusive(&pEntry->PushLock));
  1376. ASSERT(g_UlDisableLogBuffering != 0);
  1377. Status = STATUS_SUCCESS;
  1378. //
  1379. // Check the log file for overflow.
  1380. //
  1381. if (UlpIsErrorLogFileOverFlow(pEntry, RecordSize))
  1382. {
  1383. Status = UlpRecycleErrorLogFile(pEntry);
  1384. }
  1385. if (pEntry->pLogFile == NULL || !NT_SUCCESS(Status))
  1386. {
  1387. return Status;
  1388. }
  1389. //
  1390. // Prevent against abnormally big record sizes.
  1391. //
  1392. if (pEntry->LogBuffer &&
  1393. RecordSize + pEntry->LogBuffer->BufferUsed > g_UlLogBufferSize)
  1394. {
  1395. ASSERT( !"Abnormally big log record !" );
  1396. return STATUS_INVALID_PARAMETER;
  1397. }
  1398. //
  1399. // Grab a new file buffer if we need.
  1400. //
  1401. pLogBuffer = pEntry->LogBuffer;
  1402. if (pLogBuffer == NULL)
  1403. {
  1404. pLogBuffer = pEntry->LogBuffer = UlPplAllocateLogFileBuffer();
  1405. if (!pLogBuffer)
  1406. {
  1407. return STATUS_NO_MEMORY;
  1408. }
  1409. }
  1410. //
  1411. // Finally copy over the log record to file buffer.
  1412. //
  1413. RtlCopyMemory(
  1414. pLogBuffer->Buffer + pLogBuffer->BufferUsed,
  1415. pUserRecord,
  1416. RecordSize
  1417. );
  1418. pLogBuffer->BufferUsed += RecordSize;
  1419. //
  1420. // Now flush what we have.
  1421. //
  1422. Status = UlpFlushErrorLogFile(pEntry);
  1423. if (!NT_SUCCESS(Status))
  1424. {
  1425. return Status;
  1426. }
  1427. return STATUS_SUCCESS;
  1428. }
  1429. /***************************************************************************++
  1430. Routine Description:
  1431. It tries to write to the file buffer with a shared lock.
  1432. Exits and returns STATUS_MORE_PROCESSING_REQUIRED for exclusive access
  1433. for the following conditions;
  1434. 1. No log buffer available.
  1435. 2. Logging ceased. (NULL file handle)
  1436. 3. Recycle is necessary because of a size overflow.
  1437. 4. No available space left in the current buffer.
  1438. Need to allocate a new one.
  1439. Otherwise reserves a space in the current buffer, copies the data.
  1440. Arguments:
  1441. pEntry - The binary log file entry we are working on.
  1442. RecordSize - The amount (in bytes) of data will be copied.
  1443. pUserRecord - The actual log record to go to file buffer.
  1444. --***************************************************************************/
  1445. NTSTATUS
  1446. UlpWriteToErrorLogFileShared(
  1447. IN PUL_ERROR_LOG_FILE_ENTRY pEntry,
  1448. IN ULONG RecordSize,
  1449. IN PUCHAR pUserRecord
  1450. )
  1451. {
  1452. PUL_LOG_FILE_BUFFER pLogBuffer;
  1453. LONG BufferUsed;
  1454. PAGED_CODE();
  1455. ASSERT(RecordSize);
  1456. ASSERT(pUserRecord);
  1457. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  1458. pLogBuffer = pEntry->LogBuffer;
  1459. UlTrace(ERROR_LOGGING,
  1460. ("Http!UlpWriteToErrorLogFileShared: pEntry %p\n", pEntry));
  1461. //
  1462. // Bail out and try the exclusive writer for conditions;
  1463. //
  1464. if ( pLogBuffer == NULL ||
  1465. pEntry->pLogFile == NULL ||
  1466. UlpIsErrorLogFileOverFlow(pEntry,RecordSize)
  1467. )
  1468. {
  1469. return STATUS_MORE_PROCESSING_REQUIRED;
  1470. }
  1471. //
  1472. // Reserve space in pLogBuffer by InterlockedCompareExchange add
  1473. // RecordSize. If we exceed the limit, bail out and take the
  1474. // exclusive lock to flush the buffer.
  1475. //
  1476. do
  1477. {
  1478. BufferUsed = *((volatile LONG *) &pLogBuffer->BufferUsed);
  1479. if ( RecordSize + BufferUsed > g_UlLogBufferSize )
  1480. {
  1481. return STATUS_MORE_PROCESSING_REQUIRED;
  1482. }
  1483. PAUSE_PROCESSOR;
  1484. } while (BufferUsed != InterlockedCompareExchange(
  1485. &pLogBuffer->BufferUsed,
  1486. RecordSize + BufferUsed,
  1487. BufferUsed
  1488. ));
  1489. //
  1490. // Now we have a reserved space lets proceed with the copying.
  1491. //
  1492. RtlCopyMemory(
  1493. pLogBuffer->Buffer + BufferUsed,
  1494. pUserRecord,
  1495. RecordSize
  1496. );
  1497. return STATUS_SUCCESS;
  1498. }
  1499. /***************************************************************************++
  1500. Routine Description:
  1501. Exclusive writer counterpart of the above function..
  1502. Arguments:
  1503. pEntry - The binary log file entry we are working on.
  1504. RecordSize - The amount (in bytes) of data will be copied.
  1505. pUserRecord - The actual log record to go to file buffer.
  1506. --***************************************************************************/
  1507. NTSTATUS
  1508. UlpWriteToErrorLogFileExclusive(
  1509. IN PUL_ERROR_LOG_FILE_ENTRY pEntry,
  1510. IN ULONG RecordSize,
  1511. IN PUCHAR pUserRecord
  1512. )
  1513. {
  1514. PUL_LOG_FILE_BUFFER pLogBuffer;
  1515. NTSTATUS Status;
  1516. PAGED_CODE();
  1517. ASSERT(RecordSize);
  1518. ASSERT(pUserRecord);
  1519. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  1520. UlTrace(ERROR_LOGGING,
  1521. ("Http!UlpWriteToErrorLogFileExclusive: pEntry %p\n", pEntry));
  1522. ASSERT(UlDbgPushLockOwnedExclusive(&pEntry->PushLock));
  1523. //
  1524. // Check the log file for overflow.
  1525. //
  1526. Status = STATUS_SUCCESS;
  1527. if (UlpIsErrorLogFileOverFlow(pEntry,RecordSize))
  1528. {
  1529. Status = UlpRecycleErrorLogFile(pEntry);
  1530. }
  1531. if (pEntry->pLogFile==NULL || !NT_SUCCESS(Status))
  1532. {
  1533. return Status;
  1534. }
  1535. pLogBuffer = pEntry->LogBuffer;
  1536. if (pLogBuffer)
  1537. {
  1538. //
  1539. // There is only one condition for which we execute the following if block
  1540. // - We were blocked on eresource exclusive and before us some other
  1541. // thread already took care of the buffer flush or the recycling.
  1542. //
  1543. if (RecordSize + pLogBuffer->BufferUsed <= g_UlLogBufferSize)
  1544. {
  1545. RtlCopyMemory(
  1546. pLogBuffer->Buffer + pLogBuffer->BufferUsed,
  1547. pUserRecord,
  1548. RecordSize
  1549. );
  1550. pLogBuffer->BufferUsed += RecordSize;
  1551. return STATUS_SUCCESS;
  1552. }
  1553. //
  1554. // Need to flush the existing buffer before allocating a new one.
  1555. //
  1556. Status = UlpFlushErrorLogFile(pEntry);
  1557. if (!NT_SUCCESS(Status))
  1558. {
  1559. return Status;
  1560. }
  1561. }
  1562. ASSERT(pEntry->LogBuffer == NULL);
  1563. //
  1564. // Now allocate a new buffer for this log record to be copied over.
  1565. //
  1566. pLogBuffer = pEntry->LogBuffer = UlPplAllocateLogFileBuffer();
  1567. if (pLogBuffer == NULL)
  1568. {
  1569. return STATUS_NO_MEMORY;
  1570. }
  1571. RtlCopyMemory(
  1572. pLogBuffer->Buffer + pLogBuffer->BufferUsed,
  1573. pUserRecord,
  1574. RecordSize
  1575. );
  1576. pLogBuffer->BufferUsed += RecordSize;
  1577. return STATUS_SUCCESS;
  1578. }
  1579. /***************************************************************************++
  1580. Routine Description:
  1581. Tries shared write first, if fails then it goes for exclusice lock and
  1582. flushes and/or recycles the file.
  1583. Arguments:
  1584. pEntry - The binary log file entry we are working on.
  1585. RecordSize - The amount (in bytes) of data will be copied.
  1586. pUserRecord - The actual log record to go to file buffer.
  1587. --***************************************************************************/
  1588. NTSTATUS
  1589. UlpWriteToErrorLogFile(
  1590. IN PUL_ERROR_LOG_FILE_ENTRY pEntry,
  1591. IN ULONG RecordSize,
  1592. IN PUCHAR pUserRecord
  1593. )
  1594. {
  1595. NTSTATUS Status;
  1596. PAGED_CODE();
  1597. ASSERT(RecordSize);
  1598. ASSERT(pUserRecord);
  1599. ASSERT(RecordSize <= g_UlLogBufferSize);
  1600. ASSERT(IS_VALID_ERROR_LOG_FILE_ENTRY(pEntry));
  1601. UlTrace(ERROR_LOGGING,
  1602. ("Http!UlpWriteToErrorLogFile: pEntry %p\n", pEntry));
  1603. if ( pEntry == NULL ||
  1604. pUserRecord == NULL ||
  1605. RecordSize == 0 ||
  1606. RecordSize > g_UlLogBufferSize
  1607. )
  1608. {
  1609. return STATUS_INVALID_PARAMETER;
  1610. }
  1611. if (g_UlDisableLogBuffering)
  1612. {
  1613. UlAcquirePushLockExclusive(&pEntry->PushLock);
  1614. Status = UlpWriteToErrorLogFileDebug(
  1615. pEntry,
  1616. RecordSize,
  1617. pUserRecord
  1618. );
  1619. UlReleasePushLockExclusive(&pEntry->PushLock);
  1620. return Status;
  1621. }
  1622. //
  1623. // Try Shared write first which merely moves the BufferUsed forward
  1624. // and copy the error record to the file buffer.
  1625. //
  1626. UlAcquirePushLockShared(&pEntry->PushLock);
  1627. Status = UlpWriteToErrorLogFileShared(
  1628. pEntry,
  1629. RecordSize,
  1630. pUserRecord
  1631. );
  1632. UlReleasePushLockShared(&pEntry->PushLock);
  1633. if (Status == STATUS_MORE_PROCESSING_REQUIRED)
  1634. {
  1635. //
  1636. // If shared write returns STATUS_MORE_PROCESSING_REQUIRED,
  1637. // we need to flush/recycle the buffer and try to log again.
  1638. // This time, we need to take the entry eresource exclusive.
  1639. //
  1640. UlAcquirePushLockExclusive(&pEntry->PushLock);
  1641. Status = UlpWriteToErrorLogFileExclusive(
  1642. pEntry,
  1643. RecordSize,
  1644. pUserRecord
  1645. );
  1646. UlReleasePushLockExclusive(&pEntry->PushLock);
  1647. }
  1648. return Status;
  1649. }