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.

5686 lines
159 KiB

  1. /*++
  2. Copyright (c) 2000-2002 Microsoft Corporation
  3. Module Name:
  4. ullog.c (UL IIS6 HIT Logging)
  5. Abstract:
  6. This module implements the logging facilities
  7. for IIS6 including the NCSA, IIS and W3CE types
  8. of logging.
  9. Author:
  10. Ali E. Turkoglu (aliTu) 10-May-2000
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #include "ullogp.h"
  15. //
  16. // Generic Private globals.
  17. //
  18. LIST_ENTRY g_LogListHead = {NULL,NULL};
  19. LONG g_LogListEntryCount = 0;
  20. BOOLEAN g_InitLogsCalled = FALSE;
  21. BOOLEAN g_InitLogTimersCalled = FALSE;
  22. CHAR g_GMTOffset[SIZE_OF_GMT_OFFSET + 1];
  23. //
  24. // The global parameter keeps track of the changes to the
  25. // utf8 logging which applies to the all sites.
  26. //
  27. BOOLEAN g_UTF8Logging = FALSE;
  28. //
  29. // For Log Buffering and periodic flush of the buffers.
  30. //
  31. UL_LOG_TIMER g_BufferTimer;
  32. //
  33. // For Log File ReCycling based on Local and/or GMT time.
  34. //
  35. UL_LOG_TIMER g_LogTimer;
  36. #ifdef ALLOC_PRAGMA
  37. #pragma alloc_text( INIT, UlInitializeLogs )
  38. #pragma alloc_text( PAGE, UlTerminateLogs )
  39. #pragma alloc_text( PAGE, UlpGetGMTOffset )
  40. #pragma alloc_text( PAGE, UlpRecycleLogFile )
  41. #pragma alloc_text( PAGE, UlCreateLogEntry )
  42. #pragma alloc_text( PAGE, UlpCreateLogFile )
  43. #pragma alloc_text( PAGE, UlRemoveLogEntry )
  44. #pragma alloc_text( PAGE, UlpConstructLogEntry )
  45. #pragma alloc_text( PAGE, UlpAllocateLogDataBuffer )
  46. #pragma alloc_text( PAGE, UlReConfigureLogEntry )
  47. #pragma alloc_text( PAGE, UlBufferTimerHandler )
  48. #pragma alloc_text( PAGE, UlpAppendW3CLogTitle )
  49. #pragma alloc_text( PAGE, UlpWriteToLogFile )
  50. #pragma alloc_text( PAGE, UlSetUTF8Logging )
  51. #pragma alloc_text( PAGE, UlCaptureLogFieldsW3C )
  52. #pragma alloc_text( PAGE, UlCaptureLogFieldsNCSA )
  53. #pragma alloc_text( PAGE, UlCaptureLogFieldsIIS )
  54. #pragma alloc_text( PAGE, UlLogHttpCacheHit )
  55. #pragma alloc_text( PAGE, UlLogHttpHit )
  56. #pragma alloc_text( PAGE, UlpGenerateDateAndTimeFields )
  57. #pragma alloc_text( PAGE, UlpMakeEntryInactive )
  58. #pragma alloc_text( PAGE, UlDisableLogEntry )
  59. #pragma alloc_text( PAGE, UlpEventLogWriteFailure )
  60. #endif // ALLOC_PRAGMA
  61. #if 0
  62. NOT PAGEABLE -- UlLogTimerDpcRoutine
  63. NOT PAGEABLE -- UlpTerminateLogTimer
  64. NOT PAGEABLE -- UlpInsertLogEntry
  65. NOT PAGEABLE -- UlLogTimerHandler
  66. NOT PAGEABLE -- UlBufferTimerDpcRoutine
  67. NOT PAGEABLE -- UlpTerminateTimers
  68. NOT PAGEABLE -- UlpInitializeTimers
  69. NOT PAGEABLE -- UlpBufferFlushAPC
  70. NOT PAGEABLE -- UlDestroyLogDataBuffer
  71. NOT PAGEABLE -- UlDestroyLogDataBufferWorker
  72. #endif
  73. //
  74. // Public functions.
  75. //
  76. /***************************************************************************++
  77. Routine Description:
  78. UlInitializeLogs :
  79. Initialize the resource for log list synchronization
  80. --***************************************************************************/
  81. NTSTATUS
  82. UlInitializeLogs (
  83. VOID
  84. )
  85. {
  86. NTSTATUS Status = STATUS_SUCCESS;
  87. PAGED_CODE();
  88. ASSERT(!g_InitLogsCalled);
  89. if (!g_InitLogsCalled)
  90. {
  91. InitializeListHead(&g_LogListHead);
  92. UlInitializePushLock(
  93. &g_pUlNonpagedData->LogListPushLock,
  94. "LogListPushLock",
  95. 0,
  96. UL_LOG_LIST_PUSHLOCK_TAG
  97. );
  98. g_InitLogsCalled = TRUE;
  99. UlpInitializeTimers();
  100. UlpInitializeLogCache();
  101. UlpGetGMTOffset();
  102. }
  103. return Status;
  104. }
  105. /***************************************************************************++
  106. Routine Description:
  107. UlTerminateLogs :
  108. Deletes the resource for log list synchronization
  109. --***************************************************************************/
  110. VOID
  111. UlTerminateLogs(
  112. VOID
  113. )
  114. {
  115. PAGED_CODE();
  116. if (g_InitLogsCalled)
  117. {
  118. ASSERT( IsListEmpty( &g_LogListHead )) ;
  119. //
  120. // Make sure terminate the log timer before
  121. // deleting the log list resource
  122. //
  123. UlpTerminateTimers();
  124. UlDeletePushLock(
  125. &g_pUlNonpagedData->LogListPushLock
  126. );
  127. g_InitLogsCalled = FALSE;
  128. }
  129. }
  130. /***************************************************************************++
  131. Routine Description:
  132. UlSetUTF8Logging :
  133. Sets the UTF8Logging on or off. Only once. Initially Utf8Logging is
  134. FALSE and it may only be set during the init once. Following possible
  135. changes won't be taken.
  136. ReConfiguration code is explicitly missing as WAS will anly call this
  137. only once (init) during the lifetime of the control channel.
  138. --***************************************************************************/
  139. NTSTATUS
  140. UlSetUTF8Logging (
  141. IN BOOLEAN UTF8Logging
  142. )
  143. {
  144. PLIST_ENTRY pLink;
  145. PUL_LOG_FILE_ENTRY pEntry;
  146. NTSTATUS Status;
  147. PAGED_CODE();
  148. Status = STATUS_SUCCESS;
  149. //
  150. // Update & Reycle. Need to acquire the logging resource to prevent
  151. // further log hits to be written to file before we finish our
  152. // business. recycle is necessary because files will be renamed to
  153. // have prefix "u_" once we enabled the UTF8.
  154. //
  155. UlTrace(LOGGING,("Http!UlSetUTF8Logging: UTF8Logging Old %d -> New %d\n",
  156. g_UTF8Logging,UTF8Logging
  157. ));
  158. UlAcquirePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  159. //
  160. // Drop the change if the setting is not changing.
  161. //
  162. if ( g_UTF8Logging == UTF8Logging )
  163. {
  164. goto end;
  165. }
  166. g_UTF8Logging = UTF8Logging;
  167. for (pLink = g_LogListHead.Flink;
  168. pLink != &g_LogListHead;
  169. pLink = pLink->Flink
  170. )
  171. {
  172. pEntry = CONTAINING_RECORD(
  173. pLink,
  174. UL_LOG_FILE_ENTRY,
  175. LogFileListEntry
  176. );
  177. UlAcquirePushLockExclusive(&pEntry->EntryPushLock);
  178. if (pEntry->Flags.Active && !pEntry->Flags.RecyclePending)
  179. {
  180. pEntry->Flags.StaleSequenceNumber = 1;
  181. Status = UlpRecycleLogFile(pEntry);
  182. }
  183. UlReleasePushLockExclusive(&pEntry->EntryPushLock);
  184. }
  185. end:
  186. UlReleasePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  187. return Status;
  188. }
  189. /***************************************************************************++
  190. Routine Description:
  191. UlpWriteToLogFile :
  192. Writes a record to a log file
  193. Arguments:
  194. pFile - Handle to a log file entry
  195. RecordSize - Length of the record to be written.
  196. pRecord - The log record to be written to the log buffer
  197. --***************************************************************************/
  198. NTSTATUS
  199. UlpWriteToLogFile(
  200. IN PUL_LOG_FILE_ENTRY pFile,
  201. IN ULONG RecordSize,
  202. IN PCHAR pRecord,
  203. IN ULONG UsedOffset1,
  204. IN ULONG UsedOffset2
  205. )
  206. {
  207. NTSTATUS Status;
  208. PAGED_CODE();
  209. ASSERT(pRecord!=NULL);
  210. ASSERT(RecordSize!=0);
  211. ASSERT(IS_VALID_LOG_FILE_ENTRY(pFile));
  212. UlTrace(LOGGING, ("Http!UlpWriteToLogFile: pEntry %p\n", pFile));
  213. if ( pFile==NULL ||
  214. pRecord==NULL ||
  215. RecordSize==0 ||
  216. RecordSize>g_UlLogBufferSize
  217. )
  218. {
  219. return STATUS_INVALID_PARAMETER;
  220. }
  221. //
  222. // We are safe here by dealing only with entry eresource since the
  223. // time based recycling, reconfiguration and periodic buffer flushing
  224. // always acquires the global list eresource exclusively and we are
  225. // already holding it shared. But we should still be carefull about
  226. // file size based recyling and we should only do it while we are
  227. // holding the entries eresource exclusive.I.e. look at the exclusive
  228. // writer down below.
  229. //
  230. if (g_UlDisableLogBuffering)
  231. {
  232. //
  233. // Above global variable is safe to look, it doesn't get changed
  234. // during the life-time of the driver. It's get initialized from
  235. // the registry and disables the log buffering.
  236. //
  237. UlAcquirePushLockExclusive(&pFile->EntryPushLock);
  238. Status = UlpWriteToLogFileDebug(
  239. pFile,
  240. RecordSize,
  241. pRecord,
  242. UsedOffset1,
  243. UsedOffset2
  244. );
  245. UlReleasePushLockExclusive(&pFile->EntryPushLock);
  246. return Status;
  247. }
  248. //
  249. // Try UlpWriteToLogFileShared first which merely moves the
  250. // BufferUsed forward and copy the record to LogBuffer->Buffer.
  251. //
  252. UlAcquirePushLockShared(&pFile->EntryPushLock);
  253. Status = UlpWriteToLogFileShared(
  254. pFile,
  255. RecordSize,
  256. pRecord,
  257. UsedOffset1,
  258. UsedOffset2
  259. );
  260. UlReleasePushLockShared(&pFile->EntryPushLock);
  261. if (Status == STATUS_MORE_PROCESSING_REQUIRED)
  262. {
  263. //
  264. // UlpWriteToLogFileShared returns STATUS_MORE_PROCESSING_REQUIRED,
  265. // we need to flush the buffer and try to log again. This time, we
  266. // need to take the entry eresource exclusive.
  267. //
  268. UlAcquirePushLockExclusive(&pFile->EntryPushLock);
  269. Status = UlpWriteToLogFileExclusive(
  270. pFile,
  271. RecordSize,
  272. pRecord,
  273. UsedOffset1,
  274. UsedOffset2
  275. );
  276. UlReleasePushLockExclusive(&pFile->EntryPushLock);
  277. }
  278. return Status;
  279. }
  280. /***************************************************************************++
  281. Routine Description:
  282. UlpAppendToLogBuffer :
  283. Append a record to a log file
  284. REQUIRES you to hold the loglist resource shared and entry mutex
  285. shared or exclusive
  286. Arguments:
  287. pFile - Handle to a log file entry
  288. RecordSize - Length of the record to be written.
  289. pRecord - The log record to be written to the log buffer
  290. --***************************************************************************/
  291. __inline
  292. VOID
  293. UlpAppendToLogBuffer(
  294. IN PUL_LOG_FILE_ENTRY pFile,
  295. IN ULONG BufferUsed,
  296. IN ULONG RecordSize,
  297. IN PCHAR pRecord,
  298. IN ULONG UsedOffset1,
  299. IN ULONG UsedOffset2
  300. )
  301. {
  302. PUL_LOG_FILE_BUFFER pLogBuffer = pFile->LogBuffer;
  303. UlTrace(LOGGING,
  304. ("Http!UlpAppendToLogBuffer: pEntry %p TW:%I64d FileBuffer %p (%d + %d)\n",
  305. pFile,
  306. pFile->TotalWritten.QuadPart,
  307. pLogBuffer->Buffer,
  308. BufferUsed,
  309. RecordSize
  310. ));
  311. //
  312. // IIS format log line may be fragmented (identified by looking at the
  313. // UsedOffset2), handle it wisely.
  314. //
  315. if (UsedOffset2)
  316. {
  317. RtlCopyMemory(
  318. pLogBuffer->Buffer + BufferUsed,
  319. &pRecord[0],
  320. UsedOffset1
  321. );
  322. RtlCopyMemory(
  323. pLogBuffer->Buffer + BufferUsed + UsedOffset1,
  324. &pRecord[512],
  325. UsedOffset2
  326. );
  327. RtlCopyMemory(
  328. pLogBuffer->Buffer + BufferUsed + UsedOffset1 + UsedOffset2,
  329. &pRecord[1024],
  330. RecordSize - (UsedOffset1 + UsedOffset2)
  331. );
  332. }
  333. else
  334. {
  335. RtlCopyMemory(
  336. pLogBuffer->Buffer + BufferUsed,
  337. pRecord,
  338. RecordSize
  339. );
  340. }
  341. }
  342. /***************************************************************************++
  343. Routine Description:
  344. REQUIRES LogListResource Shared & Entry eresource exclusive.
  345. Appends the W3C log file title to the existing buffer.
  346. Arguments:
  347. pFile - Pointer to the logfile entry
  348. pCurrentTimeFields - Current time fields
  349. --***************************************************************************/
  350. NTSTATUS
  351. UlpAppendW3CLogTitle(
  352. IN PUL_LOG_FILE_ENTRY pEntry,
  353. OUT PCHAR pDestBuffer,
  354. IN OUT PULONG pBytesCopied
  355. )
  356. {
  357. PCHAR TitleBuffer;
  358. LONG BytesCopied;
  359. ULONG LogExtFileFlags;
  360. TIME_FIELDS CurrentTimeFields;
  361. LARGE_INTEGER CurrentTimeStamp;
  362. PUL_LOG_FILE_BUFFER pLogBuffer;
  363. PAGED_CODE();
  364. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  365. ASSERT(pEntry->Format == HttpLoggingTypeW3C);
  366. pLogBuffer = pEntry->LogBuffer;
  367. LogExtFileFlags = pEntry->LogExtFileFlags;
  368. KeQuerySystemTime(&CurrentTimeStamp);
  369. RtlTimeToTimeFields(&CurrentTimeStamp, &CurrentTimeFields);
  370. if (pDestBuffer)
  371. {
  372. // Append to the provided buffer
  373. ASSERT(pBytesCopied);
  374. ASSERT(*pBytesCopied >= UL_MAX_TITLE_BUFFER_SIZE);
  375. UlTrace(LOGGING,("Http!UlpAppendW3CLogTitle: Copying to Provided Buffer %p\n",
  376. pDestBuffer));
  377. TitleBuffer = pDestBuffer;
  378. }
  379. else
  380. {
  381. // Append to the entry buffer
  382. ASSERT(pLogBuffer);
  383. ASSERT(pLogBuffer->Buffer);
  384. UlTrace(LOGGING,("Http!UlpAppendW3CLogTitle: Copying to Entry Buffer %p\n",
  385. pLogBuffer));
  386. TitleBuffer = (PCHAR) pLogBuffer->Buffer + pLogBuffer->BufferUsed;
  387. }
  388. BytesCopied = _snprintf(
  389. TitleBuffer,
  390. UL_MAX_TITLE_BUFFER_SIZE,
  391. // TODO: Make this maintainance friendly
  392. "#Software: Microsoft Internet Information Services 6.0\r\n"
  393. "#Version: 1.0\r\n"
  394. "#Date: %4d-%02d-%02d %02d:%02d:%02d\r\n"
  395. "#Fields:%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls \r\n",
  396. CurrentTimeFields.Year,
  397. CurrentTimeFields.Month,
  398. CurrentTimeFields.Day,
  399. CurrentTimeFields.Hour,
  400. CurrentTimeFields.Minute,
  401. CurrentTimeFields.Second,
  402. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldDate,LogExtFileFlags,MD_EXTLOG_DATE),
  403. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldTime,LogExtFileFlags,MD_EXTLOG_TIME),
  404. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldSiteName,LogExtFileFlags,MD_EXTLOG_SITE_NAME),
  405. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldServerName,LogExtFileFlags,MD_EXTLOG_COMPUTER_NAME),
  406. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldServerIp,LogExtFileFlags,MD_EXTLOG_SERVER_IP),
  407. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldMethod,LogExtFileFlags,MD_EXTLOG_METHOD),
  408. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldUriStem,LogExtFileFlags,MD_EXTLOG_URI_STEM),
  409. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldUriQuery,LogExtFileFlags,MD_EXTLOG_URI_QUERY),
  410. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldServerPort,LogExtFileFlags,MD_EXTLOG_SERVER_PORT),
  411. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldUserName,LogExtFileFlags,MD_EXTLOG_USERNAME),
  412. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldClientIp,LogExtFileFlags,MD_EXTLOG_CLIENT_IP),
  413. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldProtocolVersion,LogExtFileFlags,MD_EXTLOG_PROTOCOL_VERSION),
  414. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldUserAgent,LogExtFileFlags,MD_EXTLOG_USER_AGENT),
  415. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldCookie,LogExtFileFlags,MD_EXTLOG_COOKIE),
  416. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldReferrer,LogExtFileFlags,MD_EXTLOG_REFERER),
  417. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldHost,LogExtFileFlags,MD_EXTLOG_HOST),
  418. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldProtocolStatus,LogExtFileFlags,MD_EXTLOG_HTTP_STATUS),
  419. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldSubStatus,LogExtFileFlags,MD_EXTLOG_HTTP_SUB_STATUS),
  420. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldWin32Status,LogExtFileFlags,MD_EXTLOG_WIN32_STATUS),
  421. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldBytesSent,LogExtFileFlags,MD_EXTLOG_BYTES_SENT),
  422. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldBytesReceived,LogExtFileFlags,MD_EXTLOG_BYTES_RECV),
  423. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldTimeTaken,LogExtFileFlags,MD_EXTLOG_TIME_TAKEN)
  424. );
  425. if (BytesCopied < 0)
  426. {
  427. ASSERT(!"Default title buffer size is too small !");
  428. BytesCopied = UL_MAX_TITLE_BUFFER_SIZE;
  429. }
  430. if (pDestBuffer)
  431. {
  432. *pBytesCopied = BytesCopied;
  433. }
  434. else
  435. {
  436. pLogBuffer->BufferUsed += BytesCopied;
  437. }
  438. return STATUS_SUCCESS;
  439. }
  440. /***************************************************************************++
  441. Routine Description:
  442. Writes a record to the log buffer and flushes.
  443. This func only get called when debug parameter
  444. g_UlDisableLogBuffering is set.
  445. REQUIRES you to hold the entry eresource EXCLUSIVE.
  446. Arguments:
  447. pFile - Handle to a log file entry
  448. RecordSize - Length of the record to be written.
  449. --***************************************************************************/
  450. NTSTATUS
  451. UlpWriteToLogFileDebug(
  452. IN PUL_LOG_FILE_ENTRY pFile,
  453. IN ULONG RecordSize,
  454. IN PCHAR pRecord,
  455. IN ULONG UsedOffset1,
  456. IN ULONG UsedOffset2
  457. )
  458. {
  459. NTSTATUS Status = STATUS_SUCCESS;
  460. PUL_LOG_FILE_BUFFER pLogBuffer;
  461. ULONG RecordSizePlusTitle = RecordSize;
  462. CHAR TitleBuffer[UL_MAX_TITLE_BUFFER_SIZE];
  463. ULONG TitleBufferSize = UL_MAX_TITLE_BUFFER_SIZE;
  464. PAGED_CODE();
  465. ASSERT(IS_VALID_LOG_FILE_ENTRY(pFile));
  466. ASSERT(UlDbgPushLockOwnedExclusive(&pFile->EntryPushLock));
  467. ASSERT(g_UlDisableLogBuffering!=0);
  468. UlTrace(LOGGING,("Http!UlpWriteToLogFileDebug: pEntry %p\n", pFile ));
  469. if (!pFile->Flags.LogTitleWritten)
  470. {
  471. //
  472. // First append to the temp buffer to calculate the size.
  473. //
  474. UlpAppendW3CLogTitle(pFile, TitleBuffer, &TitleBufferSize);
  475. RecordSizePlusTitle += TitleBufferSize;
  476. }
  477. if (UlpIsLogFileOverFlow(pFile,RecordSizePlusTitle))
  478. {
  479. Status = UlpRecycleLogFile(pFile);
  480. }
  481. if (pFile->pLogFile==NULL || !NT_SUCCESS(Status))
  482. {
  483. //
  484. // If we were unable to acquire a new file handle that means logging
  485. // is temporarly ceased because of either STATUS_DISK_FULL or the
  486. // drive went down for some reason. We just bail out.
  487. //
  488. return Status;
  489. }
  490. if (!pFile->LogBuffer)
  491. {
  492. //
  493. // The buffer will be null for each log hit when log buffering
  494. // is disabled.
  495. //
  496. pFile->LogBuffer = UlPplAllocateLogFileBuffer();
  497. if (!pFile->LogBuffer)
  498. {
  499. return STATUS_NO_MEMORY;
  500. }
  501. }
  502. pLogBuffer = pFile->LogBuffer;
  503. ASSERT(pLogBuffer->BufferUsed == 0);
  504. if (!pFile->Flags.LogTitleWritten)
  505. {
  506. ASSERT(pFile->Format == HttpLoggingTypeW3C);
  507. UlpAppendW3CLogTitle(pFile, NULL, NULL);
  508. pFile->Flags.LogTitleWritten = 1;
  509. pFile->Flags.TitleFlushPending = 1;
  510. }
  511. ASSERT(RecordSize + pLogBuffer->BufferUsed <= g_UlLogBufferSize);
  512. UlpAppendToLogBuffer(
  513. pFile,
  514. pLogBuffer->BufferUsed,
  515. RecordSize,
  516. pRecord,
  517. UsedOffset1,
  518. UsedOffset2
  519. );
  520. pLogBuffer->BufferUsed += RecordSize;
  521. Status = UlpFlushLogFile(pFile);
  522. if (!NT_SUCCESS(Status))
  523. {
  524. return Status;
  525. }
  526. return STATUS_SUCCESS;
  527. }
  528. /***************************************************************************++
  529. Routine Description:
  530. Writes an event log to system log for log file write failure.
  531. Entry pushlock should be acquired exclusive prior to calling this function.
  532. Arguments:
  533. pEntry - Log file entry
  534. Status - Result of last write
  535. --***************************************************************************/
  536. VOID
  537. UlpEventLogWriteFailure(
  538. IN PUL_LOG_FILE_ENTRY pEntry,
  539. IN NTSTATUS Status
  540. )
  541. {
  542. NTSTATUS TempStatus = STATUS_SUCCESS;
  543. PWSTR StringList[2];
  544. WCHAR SiteName[MAX_ULONG_STR + 1];
  545. //
  546. // Sanity Check.
  547. //
  548. PAGED_CODE();
  549. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  550. //
  551. // There should better be a failure.
  552. //
  553. ASSERT(!NT_SUCCESS(Status));
  554. //
  555. // Bail out if we have already logged the event failure.
  556. //
  557. if (pEntry->Flags.WriteFailureLogged)
  558. {
  559. return;
  560. }
  561. //
  562. // Report the log file name and the site name.
  563. //
  564. ASSERT(pEntry->pShortName);
  565. ASSERT(pEntry->pShortName[0] == L'\\');
  566. StringList[0] = (PWSTR) (pEntry->pShortName + 1); // Skip the L'\'
  567. UlStrPrintUlongW(SiteName, pEntry->SiteId, 0, L'\0');
  568. StringList[1] = (PWSTR) SiteName;
  569. TempStatus = UlWriteEventLogEntry(
  570. (NTSTATUS)EVENT_HTTP_LOGGING_FILE_WRITE_FAILED,
  571. 0,
  572. 2,
  573. StringList,
  574. sizeof(NTSTATUS),
  575. (PVOID) &Status
  576. );
  577. ASSERT(TempStatus != STATUS_BUFFER_OVERFLOW);
  578. if (TempStatus == STATUS_SUCCESS)
  579. {
  580. pEntry->Flags.WriteFailureLogged = 1;
  581. }
  582. UlTrace(LOGGING,(
  583. "Http!UlpEventLogWriteFailure: Event Logging Status %08lx\n",
  584. TempStatus
  585. ));
  586. }
  587. /***************************************************************************++
  588. Routine Description:
  589. Simple wrapper function around global buffer flush.
  590. Arguments:
  591. pEntry - Log file entry
  592. --***************************************************************************/
  593. NTSTATUS
  594. UlpFlushLogFile(
  595. IN PUL_LOG_FILE_ENTRY pEntry
  596. )
  597. {
  598. NTSTATUS Status = STATUS_SUCCESS;
  599. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  600. if (NULL != pEntry->LogBuffer && 0 != pEntry->LogBuffer->BufferUsed)
  601. {
  602. Status = UlFlushLogFileBuffer(
  603. &pEntry->LogBuffer,
  604. pEntry->pLogFile,
  605. (BOOLEAN)pEntry->Flags.TitleFlushPending,
  606. &pEntry->TotalWritten.QuadPart
  607. );
  608. if (!NT_SUCCESS(Status))
  609. {
  610. UlpEventLogWriteFailure(pEntry, Status);
  611. }
  612. else
  613. {
  614. //
  615. // If we have successfully flushed some data.
  616. // Reset the event log indication.
  617. //
  618. pEntry->Flags.WriteFailureLogged = 0;
  619. }
  620. if (pEntry->Flags.TitleFlushPending)
  621. {
  622. pEntry->Flags.TitleFlushPending = 0;
  623. if (!NT_SUCCESS(Status))
  624. {
  625. //
  626. // We need to recopy the header, it couldn't make it
  627. // to the log file yet.
  628. //
  629. pEntry->Flags.LogTitleWritten = 0;
  630. }
  631. }
  632. //
  633. // Buffer flush means activity reset the TimeToClose to its max.
  634. //
  635. pEntry->TimeToClose = DEFAULT_MAX_FILE_IDLE_TIME;
  636. }
  637. return Status;
  638. }
  639. /***************************************************************************++
  640. Routine Description:
  641. UlpWriteToLogFileShared :
  642. Writes a record to a log file
  643. REQUIRES you to hold the loglist resource shared
  644. Arguments:
  645. pFile - Handle to a log file entry
  646. RecordSize - Length of the record to be written.
  647. pRecord - The log record to be written to the log buffer
  648. --***************************************************************************/
  649. NTSTATUS
  650. UlpWriteToLogFileShared(
  651. IN PUL_LOG_FILE_ENTRY pFile,
  652. IN ULONG RecordSize,
  653. IN PCHAR pRecord,
  654. IN ULONG UsedOffset1,
  655. IN ULONG UsedOffset2
  656. )
  657. {
  658. PUL_LOG_FILE_BUFFER pLogBuffer;
  659. LONG BufferUsed;
  660. PAGED_CODE();
  661. ASSERT(IS_VALID_LOG_FILE_ENTRY(pFile));
  662. ASSERT(g_UlDisableLogBuffering == 0);
  663. pLogBuffer = pFile->LogBuffer;
  664. UlTrace(LOGGING,("Http!UlpWriteToLogFileShared: pEntry %p\n", pFile));
  665. //
  666. // Bail out and try the exclusive writer for cases;
  667. //
  668. // 1. No log buffer available.
  669. // 2. Logging ceased. (NULL handle)
  670. // 3. Title needs to be written.
  671. // 4. The actual log file itself has to be recycled.
  672. //
  673. // Otherwise proceed with appending to the current buffer
  674. // if there is enough space avialable for us. If not;
  675. //
  676. // 5. Bail out to get a new buffer
  677. //
  678. if ( pLogBuffer==NULL ||
  679. pFile->pLogFile==NULL ||
  680. !pFile->Flags.LogTitleWritten ||
  681. UlpIsLogFileOverFlow(pFile,RecordSize)
  682. )
  683. {
  684. return STATUS_MORE_PROCESSING_REQUIRED;
  685. }
  686. //
  687. // Reserve space in pLogBuffer by InterlockedCompareExchange add
  688. // RecordSize. If we exceed the limit, bail out and take the
  689. // exclusive lock to flush the buffer.
  690. //
  691. do
  692. {
  693. BufferUsed = *((volatile LONG *) &pLogBuffer->BufferUsed);
  694. if ( RecordSize + BufferUsed > g_UlLogBufferSize )
  695. {
  696. return STATUS_MORE_PROCESSING_REQUIRED;
  697. }
  698. PAUSE_PROCESSOR;
  699. } while (BufferUsed != InterlockedCompareExchange(
  700. &pLogBuffer->BufferUsed,
  701. RecordSize + BufferUsed,
  702. BufferUsed
  703. ));
  704. //
  705. // Keep buffering until our buffer is full.
  706. //
  707. UlpAppendToLogBuffer(
  708. pFile,
  709. BufferUsed,
  710. RecordSize,
  711. pRecord,
  712. UsedOffset1,
  713. UsedOffset2
  714. );
  715. return STATUS_SUCCESS;
  716. }
  717. /***************************************************************************++
  718. Routine Description:
  719. By assuming that it's holding the entrie's eresource exclusively
  720. this function does various functions;
  721. - It Writes a record to a log file
  722. REQUIRES you to hold the loglist resource shared
  723. Arguments:
  724. pFile - Handle to a log file entry
  725. RecordSize - Length of the record to be written.
  726. --***************************************************************************/
  727. NTSTATUS
  728. UlpWriteToLogFileExclusive(
  729. IN PUL_LOG_FILE_ENTRY pFile,
  730. IN ULONG RecordSize,
  731. IN PCHAR pRecord,
  732. IN ULONG UsedOffset1,
  733. IN ULONG UsedOffset2
  734. )
  735. {
  736. PUL_LOG_FILE_BUFFER pLogBuffer;
  737. NTSTATUS Status = STATUS_SUCCESS;
  738. ULONG RecordSizePlusTitle = RecordSize;
  739. CHAR TitleBuffer[UL_MAX_TITLE_BUFFER_SIZE];
  740. ULONG TitleBufferSize = UL_MAX_TITLE_BUFFER_SIZE;
  741. PAGED_CODE();
  742. ASSERT(IS_VALID_LOG_FILE_ENTRY(pFile));
  743. ASSERT(g_UlDisableLogBuffering == 0);
  744. ASSERT(UlDbgPushLockOwnedExclusive(&pFile->EntryPushLock));
  745. UlTrace(LOGGING,("Http!UlpWriteToLogFileExclusive: pEntry %p\n", pFile));
  746. //
  747. // First append title to the temp buffer to calculate the size of
  748. // the title if we need to write the title as well.
  749. //
  750. if (!pFile->Flags.LogTitleWritten)
  751. {
  752. UlpAppendW3CLogTitle(pFile, TitleBuffer, &TitleBufferSize);
  753. RecordSizePlusTitle += TitleBufferSize;
  754. }
  755. //
  756. // Now check log file overflow.
  757. //
  758. if (UlpIsLogFileOverFlow(pFile,RecordSizePlusTitle))
  759. {
  760. //
  761. // We already acquired the LogListResource Shared and the
  762. // entry eresource exclusive. Therefore ReCycle is fine. Look
  763. // at the comment in UlpWriteToLogFile.
  764. //
  765. Status = UlpRecycleLogFile(pFile);
  766. }
  767. if (pFile->pLogFile==NULL || !NT_SUCCESS(Status))
  768. {
  769. //
  770. // If somehow the logging ceased and handle released,it happens
  771. // when recycle isn't able to write to the log drive.
  772. //
  773. return Status;
  774. }
  775. pLogBuffer = pFile->LogBuffer;
  776. if (pLogBuffer)
  777. {
  778. //
  779. // There are two conditions we execute the following if block
  780. // 1. We were blocked on eresource exclusive and before us some
  781. // other thread already take care of the buffer flush or recycling.
  782. // 2. Reconfiguration happened and log attempt needs to write the
  783. // title again.
  784. //
  785. if (RecordSizePlusTitle + pLogBuffer->BufferUsed <= g_UlLogBufferSize)
  786. {
  787. //
  788. // If this is the first log attempt after a reconfig, then we have
  789. // to write the title here. Reconfig doesn't immediately write the
  790. // title but rather depend on us by setting the LogTitleWritten flag
  791. // to false.
  792. //
  793. if (!pFile->Flags.LogTitleWritten)
  794. {
  795. ASSERT(RecordSizePlusTitle > RecordSize);
  796. ASSERT(pFile->Format == HttpLoggingTypeW3C);
  797. UlpAppendW3CLogTitle(pFile, NULL, NULL);
  798. pFile->Flags.LogTitleWritten = 1;
  799. pFile->Flags.TitleFlushPending = 1;
  800. }
  801. UlpAppendToLogBuffer(
  802. pFile,
  803. pLogBuffer->BufferUsed,
  804. RecordSize,
  805. pRecord,
  806. UsedOffset1,
  807. UsedOffset2
  808. );
  809. pLogBuffer->BufferUsed += RecordSize;
  810. return STATUS_SUCCESS;
  811. }
  812. //
  813. // Flush out the buffer first then proceed with allocating a new one.
  814. //
  815. Status = UlpFlushLogFile(pFile);
  816. if (!NT_SUCCESS(Status))
  817. {
  818. return Status;
  819. }
  820. }
  821. ASSERT(pFile->LogBuffer == NULL);
  822. //
  823. // This can be the very first log attempt or the previous allocation
  824. // of LogBuffer failed, or the previous hit flushed and deallocated
  825. // the old buffer. In either case, we allocate a new one,append the
  826. // (title plus) new record and return for more/shared processing.
  827. //
  828. pLogBuffer = pFile->LogBuffer = UlPplAllocateLogFileBuffer();
  829. if (pLogBuffer == NULL)
  830. {
  831. return STATUS_NO_MEMORY;
  832. }
  833. //
  834. // Very first attempt needs to write the title, as well as the attempt
  835. // which causes the log file recycling. Both cases comes down here
  836. //
  837. if (!pFile->Flags.LogTitleWritten)
  838. {
  839. ASSERT(pFile->Format == HttpLoggingTypeW3C);
  840. UlpAppendW3CLogTitle(pFile, NULL, NULL);
  841. pFile->Flags.LogTitleWritten = 1;
  842. pFile->Flags.TitleFlushPending = 1;
  843. }
  844. UlpAppendToLogBuffer(
  845. pFile,
  846. pLogBuffer->BufferUsed,
  847. RecordSize,
  848. pRecord,
  849. UsedOffset1,
  850. UsedOffset2
  851. );
  852. pLogBuffer->BufferUsed += RecordSize;
  853. return STATUS_SUCCESS;
  854. }
  855. /***************************************************************************++
  856. Routine Description:
  857. Create or open a new file from the existing fully qualifed file name on
  858. the entry.
  859. Arguments:
  860. pEntry : Corresponding entry that we are closing and opening
  861. the log files for.
  862. pConfigGroup : Current configuration for the entry.
  863. --***************************************************************************/
  864. NTSTATUS
  865. UlpCreateLogFile(
  866. IN OUT PUL_LOG_FILE_ENTRY pEntry,
  867. IN PUL_CONFIG_GROUP_OBJECT pConfigGroup
  868. )
  869. {
  870. NTSTATUS Status;
  871. PUNICODE_STRING pDirectory;
  872. //
  873. // Sanity check.
  874. //
  875. PAGED_CODE();
  876. Status = STATUS_SUCCESS;
  877. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  878. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  879. pDirectory = &pConfigGroup->LoggingConfig.LogFileDir;
  880. UlTrace(LOGGING,("Http!UlpCreateLogFile: pEntry %p\n", pEntry));
  881. //
  882. // It's possible that LogFileDir.Buffer could be NULL,
  883. // if the allocation failed during the Set cgroup ioctl.
  884. //
  885. if (pDirectory == NULL || pDirectory->Buffer == NULL)
  886. {
  887. return STATUS_INVALID_PARAMETER;
  888. }
  889. //
  890. // Build the fully qualified file name.
  891. //
  892. Status = UlRefreshFileName(pDirectory,
  893. &pEntry->FileName,
  894. &pEntry->pShortName
  895. );
  896. if (!NT_SUCCESS(Status))
  897. {
  898. return Status;
  899. }
  900. //
  901. // Set the sequence number stale so that the recylcler below can
  902. // obtain the proper number by scanning the directory.
  903. //
  904. pEntry->Flags.StaleSequenceNumber = 1;
  905. //
  906. // This is the first time we are creating this log file,
  907. // set the time to expire stale so that recycle will
  908. // calculate it for us.
  909. //
  910. pEntry->Flags.StaleTimeToExpire = 1;
  911. //
  912. // After this, recycle does the whole job for us.
  913. //
  914. Status = UlpRecycleLogFile(pEntry);
  915. if (!NT_SUCCESS(Status))
  916. {
  917. UlTrace(LOGGING,(
  918. "Http!UlpCreateLogFile: Filename: %S Status %08lx\n",
  919. pEntry->FileName.Buffer,
  920. Status
  921. ));
  922. }
  923. return Status;
  924. }
  925. /***************************************************************************++
  926. Routine Description:
  927. When logging configuration happens we create the entry but not the log
  928. file itself yet. Log file itself will be created when the first request
  929. comes in. Please look at UlpCreateLogFile.
  930. Arguments:
  931. pConfigGroup - Supplies the necessary information for constructing the
  932. log file entry.
  933. pUserConfig - Logging config from the user.
  934. --***************************************************************************/
  935. NTSTATUS
  936. UlCreateLogEntry(
  937. IN OUT PUL_CONFIG_GROUP_OBJECT pConfigGroup,
  938. IN PHTTP_CONFIG_GROUP_LOGGING pUserConfig
  939. )
  940. {
  941. NTSTATUS Status;
  942. PUL_LOG_FILE_ENTRY pNewEntry;
  943. PHTTP_CONFIG_GROUP_LOGGING pConfig;
  944. //
  945. // Sanity check.
  946. //
  947. PAGED_CODE();
  948. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  949. Status = STATUS_SUCCESS;
  950. pNewEntry = NULL;
  951. //
  952. // We have to acquire the LogListresource exclusively, prior to
  953. // the operations Create/Remove/ReConfig and anything touches to
  954. // the cgroup log parameters.
  955. //
  956. UlAcquirePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  957. ASSERT(pConfigGroup->pLogFileEntry == NULL);
  958. //
  959. // Save the user logging info to the config group.
  960. //
  961. pConfigGroup->LoggingConfig = *pUserConfig;
  962. pConfig = &pConfigGroup->LoggingConfig;
  963. pConfig->LogFileDir.Buffer =
  964. (PWSTR) UL_ALLOCATE_ARRAY(
  965. PagedPool,
  966. UCHAR,
  967. pConfig->LogFileDir.MaximumLength,
  968. UL_CG_LOGDIR_POOL_TAG
  969. );
  970. if (pConfig->LogFileDir.Buffer == NULL)
  971. {
  972. Status = STATUS_NO_MEMORY;
  973. goto end;
  974. }
  975. RtlCopyMemory(
  976. pConfig->LogFileDir.Buffer,
  977. pUserConfig->LogFileDir.Buffer,
  978. pUserConfig->LogFileDir.MaximumLength
  979. );
  980. pConfig->Flags.Present = 1;
  981. pConfig->LoggingEnabled = TRUE;
  982. //
  983. // Now add a new entry to the global list of log entries.
  984. //
  985. Status = UlpConstructLogEntry(pConfig,&pNewEntry);
  986. if (!NT_SUCCESS(Status))
  987. goto end;
  988. //
  989. // Get the site id from the cgroup. Site id doesn't change
  990. // during the lifetime of the cgroup.
  991. //
  992. pNewEntry->SiteId = pConfigGroup->SiteId;
  993. UlpInsertLogEntry(pNewEntry);
  994. pConfigGroup->pLogFileEntry = pNewEntry;
  995. UlTrace(LOGGING,
  996. ("Http!UlCreateLogEntry: pEntry %p created for %S pConfig %p Rollover %d\n",
  997. pNewEntry,
  998. pConfig->LogFileDir.Buffer,
  999. pConfigGroup,
  1000. pNewEntry->Flags.LocaltimeRollover
  1001. ));
  1002. end:
  1003. if (!NT_SUCCESS(Status))
  1004. {
  1005. UlTrace(LOGGING,("Http!UlCreateLogEntry: dir %S failure %08lx\n",
  1006. pConfig->LogFileDir.Buffer,
  1007. Status
  1008. ));
  1009. //
  1010. // Restore the logging disabled state on the cgroup, free the
  1011. // memory for the dir.
  1012. //
  1013. if (pConfig->LogFileDir.Buffer)
  1014. {
  1015. UL_FREE_POOL(pConfig->LogFileDir.Buffer,
  1016. UL_CG_LOGDIR_POOL_TAG
  1017. );
  1018. }
  1019. pConfig->LogFileDir.Buffer = NULL;
  1020. ASSERT(pConfigGroup->pLogFileEntry == NULL);
  1021. pConfig->Flags.Present = 0;
  1022. pConfig->LoggingEnabled = FALSE;
  1023. }
  1024. UlReleasePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1025. return Status;
  1026. }
  1027. /***************************************************************************++
  1028. Routine Description:
  1029. Inserts a log file entry to our global log entry list.
  1030. REQUIRES caller to have LogListresource EXCLUSIVELY.
  1031. Arguments:
  1032. pEntry - The log file entry to be added to the global list
  1033. pTimeFields - The current time fields.
  1034. --***************************************************************************/
  1035. VOID
  1036. UlpInsertLogEntry(
  1037. IN PUL_LOG_FILE_ENTRY pEntry
  1038. )
  1039. {
  1040. LONG listSize;
  1041. HTTP_LOGGING_PERIOD Period;
  1042. KIRQL oldIrql;
  1043. //
  1044. // Sanity check.
  1045. //
  1046. PAGED_CODE();
  1047. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1048. //
  1049. // add to the list
  1050. //
  1051. InsertHeadList(&g_LogListHead, &pEntry->LogFileListEntry);
  1052. Period = pEntry->Period;
  1053. listSize = InterlockedIncrement(&g_LogListEntryCount);
  1054. ASSERT(listSize >= 1);
  1055. //
  1056. // Time to start the Log Timer if we haven't done it yet.
  1057. // Once we start this timer it keeps working until the
  1058. // termination of the driver. Start the timer only if the
  1059. // entry is running on a time dependent log format.
  1060. //
  1061. if (Period != HttpLoggingPeriodMaxSize)
  1062. {
  1063. UlAcquireSpinLock(&g_LogTimer.SpinLock, &oldIrql);
  1064. if (g_LogTimer.Started == FALSE)
  1065. {
  1066. UlSetLogTimer(&g_LogTimer);
  1067. g_LogTimer.Started = TRUE;
  1068. }
  1069. UlReleaseSpinLock(&g_LogTimer.SpinLock, oldIrql);
  1070. }
  1071. //
  1072. // Go ahead and start the buffer timer as soon as we have
  1073. // a log entry.
  1074. //
  1075. UlAcquireSpinLock(&g_BufferTimer.SpinLock, &oldIrql);
  1076. if (g_BufferTimer.Started == FALSE)
  1077. {
  1078. UlSetBufferTimer(&g_BufferTimer);
  1079. g_BufferTimer.Started = TRUE;
  1080. }
  1081. UlReleaseSpinLock(&g_BufferTimer.SpinLock, oldIrql);
  1082. }
  1083. /***************************************************************************++
  1084. Routine Description:
  1085. Removes a log file entry from our global log entry list. Also cleans up
  1086. the config group's logging settings ( only directory string )
  1087. Arguments:
  1088. pEntry - The log file entry to be removed from the global list
  1089. --***************************************************************************/
  1090. VOID
  1091. UlRemoveLogEntry(
  1092. IN PUL_CONFIG_GROUP_OBJECT pConfigGroup
  1093. )
  1094. {
  1095. LONG listSize;
  1096. PUL_LOG_FILE_ENTRY pEntry;
  1097. //
  1098. // Sanity check.
  1099. //
  1100. PAGED_CODE();
  1101. UlAcquirePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1102. //
  1103. // Clean up config group's directory string.
  1104. //
  1105. if (pConfigGroup->LoggingConfig.LogFileDir.Buffer)
  1106. {
  1107. UL_FREE_POOL(
  1108. pConfigGroup->LoggingConfig.LogFileDir.Buffer,
  1109. UL_CG_LOGDIR_POOL_TAG );
  1110. }
  1111. pEntry = pConfigGroup->pLogFileEntry;
  1112. if (pEntry == NULL)
  1113. {
  1114. UlReleasePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1115. return;
  1116. }
  1117. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1118. RemoveEntryList(&pEntry->LogFileListEntry);
  1119. pEntry->LogFileListEntry.Flink =
  1120. pEntry->LogFileListEntry.Blink = NULL;
  1121. if (pEntry->pLogFile != NULL)
  1122. {
  1123. //
  1124. // Flush the buffer, close the file and mark the entry
  1125. // inactive.
  1126. //
  1127. UlpMakeEntryInactive(pEntry);
  1128. }
  1129. //
  1130. // Free up the FileName (allocated when the entry becomes active
  1131. // otherwise it's empty)
  1132. //
  1133. if (pEntry->FileName.Buffer)
  1134. {
  1135. UL_FREE_POOL(pEntry->FileName.Buffer,UL_CG_LOGDIR_POOL_TAG);
  1136. pEntry->FileName.Buffer = NULL;
  1137. }
  1138. //
  1139. // Delete the entry eresource
  1140. //
  1141. UlDeletePushLock(&pEntry->EntryPushLock);
  1142. listSize = InterlockedDecrement(&g_LogListEntryCount);
  1143. ASSERT(listSize >= 0);
  1144. UlTrace(LOGGING,
  1145. ("Http!UlRemoveLogFileEntry: pEntry %p removed\n",
  1146. pEntry
  1147. ));
  1148. if (pEntry->LogBuffer)
  1149. {
  1150. UlPplFreeLogFileBuffer(pEntry->LogBuffer);
  1151. }
  1152. UL_FREE_POOL_WITH_SIG(pEntry,UL_LOG_FILE_ENTRY_POOL_TAG);
  1153. UlReleasePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1154. }
  1155. /***************************************************************************++
  1156. Routine Description:
  1157. Initializes the Log recycling and the buffering timers.
  1158. --***************************************************************************/
  1159. VOID
  1160. UlpInitializeTimers(
  1161. VOID
  1162. )
  1163. {
  1164. // Guard against multiple inits
  1165. if (g_InitLogTimersCalled) return;
  1166. g_InitLogTimersCalled = TRUE;
  1167. // Log timer
  1168. g_LogTimer.Initialized = TRUE;
  1169. g_LogTimer.Started = FALSE;
  1170. g_LogTimer.Period = -1;
  1171. g_LogTimer.PeriodType = UlLogTimerPeriodNone;
  1172. UlInitializeSpinLock(&g_LogTimer.SpinLock, "g_LogTimersSpinLock");
  1173. KeInitializeDpc(
  1174. &g_LogTimer.DpcObject, // DPC object
  1175. &UlLogTimerDpcRoutine, // DPC routine
  1176. NULL // context
  1177. );
  1178. KeInitializeTimer(&g_LogTimer.Timer);
  1179. // Buffer timer
  1180. g_BufferTimer.Initialized = TRUE;
  1181. g_BufferTimer.Started = FALSE;
  1182. g_BufferTimer.Period = -1; // Not used
  1183. g_BufferTimer.PeriodType = UlLogTimerPeriodNone; // Not used
  1184. UlInitializeSpinLock(&g_BufferTimer.SpinLock, "g_BufferTimersSpinLock");
  1185. KeInitializeDpc(
  1186. &g_BufferTimer.DpcObject, // DPC object
  1187. &UlBufferTimerDpcRoutine, // DPC routine
  1188. NULL // context
  1189. );
  1190. KeInitializeTimer(&g_BufferTimer.Timer);
  1191. }
  1192. /***************************************************************************++
  1193. Routine Description:
  1194. Terminates the Log & buffering Timers
  1195. --***************************************************************************/
  1196. VOID
  1197. UlpTerminateTimers(
  1198. VOID
  1199. )
  1200. {
  1201. KIRQL oldIrql;
  1202. // Guard against multiple terminates
  1203. if (!g_InitLogTimersCalled) return;
  1204. g_InitLogTimersCalled = FALSE;
  1205. // Log timer
  1206. UlAcquireSpinLock(&g_LogTimer.SpinLock, &oldIrql);
  1207. g_LogTimer.Initialized = FALSE;
  1208. KeCancelTimer(&g_LogTimer.Timer);
  1209. UlReleaseSpinLock(&g_LogTimer.SpinLock, oldIrql);
  1210. // Buffer timer
  1211. UlAcquireSpinLock(&g_BufferTimer.SpinLock, &oldIrql);
  1212. g_BufferTimer.Initialized = FALSE;
  1213. KeCancelTimer(&g_BufferTimer.Timer);
  1214. UlReleaseSpinLock(&g_BufferTimer.SpinLock, oldIrql);
  1215. }
  1216. /***************************************************************************++
  1217. Routine Description:
  1218. Work item for the threadpool that goes thru the log list and
  1219. cycle the necessary logs.
  1220. Arguments:
  1221. PUL_WORK_ITEM - Ignored but freed up once we are done.
  1222. --***************************************************************************/
  1223. VOID
  1224. UlLogTimerHandler(
  1225. IN PUL_WORK_ITEM pWorkItem
  1226. )
  1227. {
  1228. NTSTATUS Status;
  1229. PLIST_ENTRY pLink;
  1230. PUL_LOG_FILE_ENTRY pEntry;
  1231. BOOLEAN Picked;
  1232. KIRQL OldIrql;
  1233. PAGED_CODE();
  1234. UlTrace(LOGGING,("Http!UlLogTimerHandler: Scanning the log entries ...\n"));
  1235. UlAcquirePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1236. // Attempt to reinit the GMT offset every hour, to pickup the changes
  1237. // because of the day light changes. Synced by the logging eresource.
  1238. UlpGetGMTOffset();
  1239. for (pLink = g_LogListHead.Flink;
  1240. pLink != &g_LogListHead;
  1241. pLink = pLink->Flink
  1242. )
  1243. {
  1244. pEntry = CONTAINING_RECORD(
  1245. pLink,
  1246. UL_LOG_FILE_ENTRY,
  1247. LogFileListEntry
  1248. );
  1249. //
  1250. // We should not recycle this entry if it's period
  1251. // is not time based but size based.
  1252. //
  1253. UlAcquirePushLockExclusive(&pEntry->EntryPushLock);
  1254. switch(g_LogTimer.PeriodType)
  1255. {
  1256. //
  1257. // Rollover table:
  1258. //
  1259. // LocaltimeRollover
  1260. // TRUE FALSE (Default)
  1261. // Format
  1262. // ------------------------------
  1263. // W3C | Local | GMT |
  1264. // -------------------------
  1265. // NCSA | Local | Local |
  1266. // -------------------------
  1267. // IIS | Local | Local |
  1268. // -------------------------
  1269. //
  1270. // If the timer waked up at the beginning of an hour
  1271. // for GMT, LocalTime or Both. E.g.
  1272. //
  1273. // 1) For Pacific Time Zone: (-8:00)
  1274. // PeriodType will always be UlLogTimerPeriodBoth
  1275. // and all of the entries will rollover regardless
  1276. // of their format.
  1277. //
  1278. // 2) For Adelaide (Australia) (+9:30)
  1279. // Timer will wake up seperately for GMT & Local.
  1280. // NCSA & IIS entries will always rollover at
  1281. // UlLogTimerPeriodLocal, W3C will rollover at
  1282. // UlLogTimerPeriodLocal only if LocaltimeRollover
  1283. // is set otherwise it will rollover at
  1284. // UlLogTimerPeriodGMT.
  1285. //
  1286. case UlLogTimerPeriodGMT:
  1287. //
  1288. // Only entries with W3C format type may rollover
  1289. // at GMT only time interval.
  1290. //
  1291. Picked = (BOOLEAN) ((pEntry->Flags.LocaltimeRollover == 0)
  1292. && (pEntry->Format == HttpLoggingTypeW3C));
  1293. break;
  1294. case UlLogTimerPeriodLocal:
  1295. //
  1296. // Entries with NCSA or IIS format type always rollover
  1297. // at Local time interval. W3C may also rollover if
  1298. // LocaltimeRollover is set.
  1299. //
  1300. Picked = (BOOLEAN) ((pEntry->Flags.LocaltimeRollover == 1)
  1301. || (pEntry->Format != HttpLoggingTypeW3C));
  1302. break;
  1303. case UlLogTimerPeriodBoth:
  1304. //
  1305. // We really don't care what format the entry has,
  1306. // since the local time and GMT hourly beginnings are
  1307. // aligned.
  1308. //
  1309. Picked = TRUE;
  1310. break;
  1311. default:
  1312. ASSERT(!"Unexpected timer period type !\n");
  1313. Picked = FALSE;
  1314. break;
  1315. }
  1316. if (Picked &&
  1317. pEntry->Flags.Active &&
  1318. pEntry->Period != HttpLoggingPeriodMaxSize
  1319. )
  1320. {
  1321. if (pEntry->TimeToExpire == 1)
  1322. {
  1323. pEntry->Flags.StaleTimeToExpire = 1;
  1324. //
  1325. // Mark the entry inactive and postpone the recycle
  1326. // until the next request arrives.
  1327. //
  1328. Status = UlpMakeEntryInactive(pEntry);
  1329. }
  1330. else
  1331. {
  1332. //
  1333. // Just decrement the hourly counter for this time.
  1334. //
  1335. pEntry->TimeToExpire -= 1;
  1336. }
  1337. }
  1338. UlReleasePushLockExclusive(&pEntry->EntryPushLock);
  1339. }
  1340. UlReleasePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1341. //
  1342. // Free the memory allocated (ByDpcRoutine below) for
  1343. // this work item.
  1344. //
  1345. UL_FREE_POOL( pWorkItem, UL_WORK_ITEM_POOL_TAG );
  1346. //
  1347. // Now reset the timer for the next hour.
  1348. //
  1349. UlAcquireSpinLock(&g_LogTimer.SpinLock, &OldIrql);
  1350. if (g_LogTimer.Initialized == TRUE)
  1351. {
  1352. UlSetLogTimer(&g_LogTimer);
  1353. }
  1354. UlReleaseSpinLock(&g_LogTimer.SpinLock, OldIrql);
  1355. }
  1356. /***************************************************************************++
  1357. Routine Description:
  1358. Allocates and queues a work item to do the the actual work at lowered
  1359. irql.
  1360. Arguments:
  1361. Ignored
  1362. --***************************************************************************/
  1363. VOID
  1364. UlLogTimerDpcRoutine(
  1365. PKDPC Dpc,
  1366. PVOID DeferredContext,
  1367. PVOID SystemArgument1,
  1368. PVOID SystemArgument2
  1369. )
  1370. {
  1371. PUL_WORK_ITEM pWorkItem;
  1372. //
  1373. // Parameters are ignored.
  1374. //
  1375. UNREFERENCED_PARAMETER(Dpc);
  1376. UNREFERENCED_PARAMETER(DeferredContext);
  1377. UNREFERENCED_PARAMETER(SystemArgument1);
  1378. UNREFERENCED_PARAMETER(SystemArgument2);
  1379. UlAcquireSpinLockAtDpcLevel(&g_LogTimer.SpinLock);
  1380. if (g_LogTimer.Initialized == TRUE)
  1381. {
  1382. //
  1383. // It's not possible to acquire the resource which protects
  1384. // the log list at DISPATCH_LEVEL therefore we will queue a
  1385. // work item for this.
  1386. //
  1387. pWorkItem = (PUL_WORK_ITEM) UL_ALLOCATE_POOL(
  1388. NonPagedPool,
  1389. sizeof(*pWorkItem),
  1390. UL_WORK_ITEM_POOL_TAG
  1391. );
  1392. if (pWorkItem)
  1393. {
  1394. UlInitializeWorkItem(pWorkItem);
  1395. UL_QUEUE_WORK_ITEM(pWorkItem, &UlLogTimerHandler);
  1396. }
  1397. else
  1398. {
  1399. UlTrace(LOGGING,("Http!UlLogTimerDpcRoutine: Not enough memory!\n"));
  1400. }
  1401. }
  1402. UlReleaseSpinLockFromDpcLevel(&g_LogTimer.SpinLock);
  1403. }
  1404. /***************************************************************************++
  1405. Routine Description:
  1406. Queues a passive worker for the lowered irql.
  1407. Arguments:
  1408. Ignored
  1409. --***************************************************************************/
  1410. VOID
  1411. UlBufferTimerDpcRoutine(
  1412. PKDPC Dpc,
  1413. PVOID DeferredContext,
  1414. PVOID SystemArgument1,
  1415. PVOID SystemArgument2
  1416. )
  1417. {
  1418. PUL_WORK_ITEM pWorkItem;
  1419. //
  1420. // Parameters are ignored.
  1421. //
  1422. UNREFERENCED_PARAMETER(Dpc);
  1423. UNREFERENCED_PARAMETER(DeferredContext);
  1424. UNREFERENCED_PARAMETER(SystemArgument1);
  1425. UNREFERENCED_PARAMETER(SystemArgument2);
  1426. UlAcquireSpinLockAtDpcLevel(&g_BufferTimer.SpinLock);
  1427. if (g_BufferTimer.Initialized == TRUE)
  1428. {
  1429. //
  1430. // It's not possible to acquire the resource which protects
  1431. // the log list at DISPATCH_LEVEL therefore we will queue a
  1432. // work item for this.
  1433. //
  1434. pWorkItem = (PUL_WORK_ITEM) UL_ALLOCATE_POOL(
  1435. NonPagedPool,
  1436. sizeof(*pWorkItem),
  1437. UL_WORK_ITEM_POOL_TAG
  1438. );
  1439. if (pWorkItem)
  1440. {
  1441. UlInitializeWorkItem(pWorkItem);
  1442. UL_QUEUE_WORK_ITEM(pWorkItem, &UlBufferTimerHandler);
  1443. }
  1444. else
  1445. {
  1446. UlTrace(LOGGING,("Http!UlBufferTimerDpcRoutine: Not enough memory.\n"));
  1447. }
  1448. }
  1449. UlReleaseSpinLockFromDpcLevel(&g_BufferTimer.SpinLock);
  1450. }
  1451. /***************************************************************************++
  1452. Routine Description:
  1453. UlLogBufferTimerHandler :
  1454. Work item for the threadpool that goes thru the log list and
  1455. flush the log's file buffers.
  1456. Arguments:
  1457. PUL_WORK_ITEM - Ignored but cleaned up at the end
  1458. --***************************************************************************/
  1459. VOID
  1460. UlBufferTimerHandler(
  1461. IN PUL_WORK_ITEM pWorkItem
  1462. )
  1463. {
  1464. NTSTATUS Status;
  1465. PLIST_ENTRY pLink;
  1466. PUL_LOG_FILE_ENTRY pEntry;
  1467. PAGED_CODE();
  1468. UlTrace(LOGGING,("Http!UlBufferTimerHandler: Scanning the log entries ...\n"));
  1469. UlAcquirePushLockShared(&g_pUlNonpagedData->LogListPushLock);
  1470. for (pLink = g_LogListHead.Flink;
  1471. pLink != &g_LogListHead;
  1472. pLink = pLink->Flink
  1473. )
  1474. {
  1475. pEntry = CONTAINING_RECORD(
  1476. pLink,
  1477. UL_LOG_FILE_ENTRY,
  1478. LogFileListEntry
  1479. );
  1480. UlAcquirePushLockExclusive(&pEntry->EntryPushLock);
  1481. //
  1482. // Entry may be staying inactive since no request came in yet.
  1483. //
  1484. if (pEntry->Flags.Active)
  1485. {
  1486. if (pEntry->Flags.RecyclePending)
  1487. {
  1488. //
  1489. // Try to resurrect it back.
  1490. //
  1491. Status = UlpRecycleLogFile(pEntry);
  1492. }
  1493. else
  1494. {
  1495. //
  1496. // Everything is fine simply flush.
  1497. //
  1498. if (NULL != pEntry->LogBuffer && 0 != pEntry->LogBuffer->BufferUsed)
  1499. {
  1500. Status = UlpFlushLogFile(pEntry);
  1501. }
  1502. else
  1503. {
  1504. //
  1505. // Decrement the idle counter and close the file if necessary.
  1506. //
  1507. ASSERT( pEntry->TimeToClose > 0 );
  1508. if (pEntry->TimeToClose == 1)
  1509. {
  1510. //
  1511. // Entry was staying inactive for too long, disable it.
  1512. // But next recycle should recalculate the timeToExpire
  1513. // or determine the proper sequence number according to
  1514. // the current period type.
  1515. //
  1516. if (pEntry->Period == HttpLoggingPeriodMaxSize)
  1517. {
  1518. pEntry->Flags.StaleSequenceNumber = 1;
  1519. }
  1520. else
  1521. {
  1522. pEntry->Flags.StaleTimeToExpire = 1;
  1523. }
  1524. Status = UlpMakeEntryInactive(pEntry);
  1525. }
  1526. else
  1527. {
  1528. pEntry->TimeToClose -= 1;
  1529. }
  1530. }
  1531. }
  1532. }
  1533. UlReleasePushLockExclusive(&pEntry->EntryPushLock);
  1534. }
  1535. UlReleasePushLockShared(&g_pUlNonpagedData->LogListPushLock);
  1536. //
  1537. // Free the memory allocated (ByDpcRoutine below) to
  1538. // this work item.
  1539. //
  1540. UL_FREE_POOL( pWorkItem, UL_WORK_ITEM_POOL_TAG );
  1541. }
  1542. /***************************************************************************++
  1543. Routine Description:
  1544. UlReconfigureLogEntry :
  1545. This function implements the logging reconfiguration per attribute.
  1546. Everytime config changes happens we try to update the existing logging
  1547. parameters here.
  1548. Arguments:
  1549. pConfig - corresponding cgroup object
  1550. pCfgCurrent - Current logging config on the cgroup object
  1551. pCfgNew - New logging config passed down by the user.
  1552. --***************************************************************************/
  1553. NTSTATUS
  1554. UlReConfigureLogEntry(
  1555. IN PUL_CONFIG_GROUP_OBJECT pConfigGroup,
  1556. IN PHTTP_CONFIG_GROUP_LOGGING pCfgCurrent,
  1557. IN PHTTP_CONFIG_GROUP_LOGGING pCfgNew
  1558. )
  1559. {
  1560. NTSTATUS Status ;
  1561. PUL_LOG_FILE_ENTRY pEntry;
  1562. BOOLEAN HaveToReCycle;
  1563. //
  1564. // Sanity check first
  1565. //
  1566. PAGED_CODE();
  1567. Status = STATUS_SUCCESS;
  1568. HaveToReCycle = FALSE;
  1569. UlTrace(LOGGING,("Http!UlReConfigureLogEntry: entry %p\n",
  1570. pConfigGroup->pLogFileEntry));
  1571. if (pCfgCurrent->LoggingEnabled==FALSE && pCfgNew->LoggingEnabled==FALSE)
  1572. {
  1573. //
  1574. // Do nothing. Not even update the fields. As soon as we get enable,
  1575. // field update will take place anyway.
  1576. //
  1577. return Status;
  1578. }
  1579. //
  1580. // No matter what ReConfiguration should acquire the LogListResource
  1581. // exclusively.
  1582. //
  1583. UlAcquirePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1584. pEntry = pConfigGroup->pLogFileEntry;
  1585. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1586. if (pCfgCurrent->LoggingEnabled==TRUE && pCfgNew->LoggingEnabled==FALSE)
  1587. {
  1588. //
  1589. // Disable the entry if necessary.
  1590. //
  1591. if (pEntry->Flags.Active == 1)
  1592. {
  1593. Status = UlpMakeEntryInactive(pEntry);
  1594. }
  1595. pCfgCurrent->LoggingEnabled = FALSE;
  1596. goto end;
  1597. }
  1598. else
  1599. {
  1600. pCfgCurrent->LoggingEnabled = TRUE;
  1601. }
  1602. //
  1603. // If LogEntry is Inactive (means no request served for this site yet and
  1604. // the LogFile itself hasn't been created yet), all we have to do is flush
  1605. // the settings on the LogEntry, the cgroup and then return.
  1606. //
  1607. if (!pEntry->Flags.Active)
  1608. {
  1609. ASSERT(pEntry->pLogFile == NULL);
  1610. if (RtlCompareUnicodeString(&pCfgNew->LogFileDir,
  1611. &pCfgCurrent->LogFileDir, TRUE)
  1612. != 0)
  1613. {
  1614. //
  1615. // Store the new directory in the cgroup even if the entry is
  1616. // inactive. Discard the return value, if failure happens we
  1617. // keep the old directory.
  1618. //
  1619. UlCopyLogFileDir(
  1620. &pCfgCurrent->LogFileDir,
  1621. &pCfgNew->LogFileDir
  1622. );
  1623. //
  1624. // If creation fails later, we should event log.
  1625. //
  1626. pEntry->Flags.CreateFileFailureLogged = 0;
  1627. }
  1628. pEntry->Format = pCfgNew->LogFormat;
  1629. pCfgCurrent->LogFormat = pCfgNew->LogFormat;
  1630. pEntry->Period = (HTTP_LOGGING_PERIOD) pCfgNew->LogPeriod;
  1631. pCfgCurrent->LogPeriod = pCfgNew->LogPeriod;
  1632. pEntry->TruncateSize = pCfgNew->LogFileTruncateSize;
  1633. pCfgCurrent->LogFileTruncateSize = pCfgNew->LogFileTruncateSize;
  1634. pEntry->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  1635. pCfgCurrent->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  1636. pCfgCurrent->LocaltimeRollover = pCfgNew->LocaltimeRollover;
  1637. pEntry->Flags.LocaltimeRollover = (pCfgNew->LocaltimeRollover ? 1 : 0);
  1638. pCfgCurrent->SelectiveLogging = pCfgNew->SelectiveLogging;
  1639. if (pEntry->Format != HttpLoggingTypeW3C)
  1640. {
  1641. pEntry->Flags.LogTitleWritten = 1;
  1642. }
  1643. goto end;
  1644. }
  1645. //
  1646. // if the entry was active then proceed down to do proper reconfiguration
  1647. // and recyle immediately if it's necessary.
  1648. //
  1649. Status = UlCheckLogDirectory(&pCfgNew->LogFileDir);
  1650. if (!NT_SUCCESS(Status))
  1651. {
  1652. // Otherwise keep the old settings
  1653. goto end;
  1654. }
  1655. if (RtlCompareUnicodeString(
  1656. &pCfgNew->LogFileDir, &pCfgCurrent->LogFileDir, TRUE) != 0)
  1657. {
  1658. //
  1659. // Store the new directory in the config group.
  1660. //
  1661. Status = UlCopyLogFileDir(&pCfgCurrent->LogFileDir,
  1662. &pCfgNew->LogFileDir);
  1663. if (!NT_SUCCESS(Status))
  1664. {
  1665. goto end;
  1666. }
  1667. //
  1668. // Rebuild the fully qualified file name.
  1669. //
  1670. Status = UlRefreshFileName(&pCfgCurrent->LogFileDir,
  1671. &pEntry->FileName,
  1672. &pEntry->pShortName
  1673. );
  1674. if (!NT_SUCCESS(Status))
  1675. {
  1676. goto end;
  1677. }
  1678. //
  1679. // Set the sequence number stale so that the recylcler below can
  1680. // obtain the proper number by scanning the directory.
  1681. //
  1682. pEntry->Flags.StaleSequenceNumber = 1;
  1683. HaveToReCycle = TRUE;
  1684. }
  1685. if (pCfgNew->LogFormat != pCfgCurrent->LogFormat)
  1686. {
  1687. pCfgCurrent->LogFormat = pCfgNew->LogFormat;
  1688. pEntry->Format = pCfgNew->LogFormat;
  1689. pEntry->Flags.StaleTimeToExpire = 1;
  1690. pEntry->Flags.StaleSequenceNumber = 1;
  1691. HaveToReCycle = TRUE;
  1692. }
  1693. if (pCfgNew->LogPeriod != pCfgCurrent->LogPeriod)
  1694. {
  1695. pCfgCurrent->LogPeriod = pCfgNew->LogPeriod;
  1696. pEntry->Period = (HTTP_LOGGING_PERIOD) pCfgNew->LogPeriod;
  1697. pEntry->Flags.StaleTimeToExpire = 1;
  1698. pEntry->Flags.StaleSequenceNumber = 1;
  1699. HaveToReCycle = TRUE;
  1700. }
  1701. if (pCfgNew->LogFileTruncateSize != pCfgCurrent->LogFileTruncateSize)
  1702. {
  1703. if (TRUE == UlUpdateLogTruncateSize(
  1704. pCfgNew->LogFileTruncateSize,
  1705. &pCfgCurrent->LogFileTruncateSize,
  1706. &pEntry->TruncateSize,
  1707. pEntry->TotalWritten
  1708. ))
  1709. {
  1710. HaveToReCycle = TRUE;
  1711. }
  1712. }
  1713. if (pCfgNew->LogExtFileFlags != pCfgCurrent->LogExtFileFlags)
  1714. {
  1715. //
  1716. // Just a change in the flags should not cause us to recyle.
  1717. // Unless ofcourse something else is also changed.
  1718. //
  1719. pCfgCurrent->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  1720. pEntry->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  1721. if (pEntry->Format == HttpLoggingTypeW3C)
  1722. {
  1723. pEntry->Flags.LogTitleWritten = 0;
  1724. }
  1725. }
  1726. if (pCfgNew->LocaltimeRollover != pCfgCurrent->LocaltimeRollover)
  1727. {
  1728. //
  1729. // Need to reclycle if the format is W3C.
  1730. //
  1731. pCfgCurrent->LocaltimeRollover = pCfgNew->LocaltimeRollover;
  1732. pEntry->Flags.LocaltimeRollover = (pCfgNew->LocaltimeRollover ? 1 : 0);
  1733. HaveToReCycle = TRUE;
  1734. }
  1735. //
  1736. // Copy over the new selective logging criteria. No change is required
  1737. // at this time.
  1738. //
  1739. pCfgCurrent->SelectiveLogging = pCfgNew->SelectiveLogging;
  1740. if (HaveToReCycle)
  1741. {
  1742. //
  1743. // Mark the entry inactive and postpone the recycle until the next
  1744. // request arrives.
  1745. //
  1746. Status = UlpMakeEntryInactive(pEntry);
  1747. }
  1748. end:
  1749. if (!NT_SUCCESS(Status))
  1750. {
  1751. UlTrace(LOGGING,("Http!UlReConfigureLogEntry: entry %p, failure %08lx\n",
  1752. pEntry,
  1753. Status
  1754. ));
  1755. }
  1756. UlReleasePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1757. return Status;
  1758. } // UlReConfigureLogEntry
  1759. /***************************************************************************++
  1760. Routine Description:
  1761. Marks the entry inactive, closes the existing file.
  1762. Caller should hold the log list eresource exclusive.
  1763. Arguments:
  1764. pEntry - The log file entry which we will mark inactive.
  1765. --***************************************************************************/
  1766. NTSTATUS
  1767. UlpMakeEntryInactive(
  1768. IN OUT PUL_LOG_FILE_ENTRY pEntry
  1769. )
  1770. {
  1771. //
  1772. // Sanity checks
  1773. //
  1774. PAGED_CODE();
  1775. UlTrace(LOGGING,("Http!UlpMakeEntryInactive: entry %p disabled.\n",
  1776. pEntry
  1777. ));
  1778. //
  1779. // Flush and close the old file until the next recycle.
  1780. //
  1781. if (pEntry->pLogFile != NULL)
  1782. {
  1783. UlpFlushLogFile(pEntry);
  1784. UlCloseLogFile(&pEntry->pLogFile);
  1785. }
  1786. //
  1787. // Mark this inactive so that the next http hit awakens the entry.
  1788. //
  1789. pEntry->Flags.Active = 0;
  1790. return STATUS_SUCCESS;
  1791. }
  1792. /***************************************************************************++
  1793. Routine Description:
  1794. When the config group, the one that owns the log entry is disabled or lost
  1795. all of its URLs then we temporarly disable the entry by marking it inactive
  1796. until the config group get enabled and/or receives a URL.
  1797. Arguments:
  1798. pEntry - The log file entry which we will disable.
  1799. --***************************************************************************/
  1800. NTSTATUS
  1801. UlDisableLogEntry(
  1802. IN OUT PUL_LOG_FILE_ENTRY pEntry
  1803. )
  1804. {
  1805. NTSTATUS Status = STATUS_SUCCESS;
  1806. PAGED_CODE();
  1807. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1808. UlAcquirePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1809. //
  1810. // If the entry is already disabled. Perhaps because of a recent reconfig,
  1811. // then just bail out.
  1812. //
  1813. if (pEntry->Flags.Active == 1)
  1814. {
  1815. //
  1816. // Once the entry is disabled, it will be awaken when the next hit
  1817. // happens.And that obviously cannot happen before cgroup receives
  1818. // a new URL.
  1819. //
  1820. Status = UlpMakeEntryInactive(pEntry);
  1821. }
  1822. UlReleasePushLockExclusive(&g_pUlNonpagedData->LogListPushLock);
  1823. return Status;
  1824. }
  1825. /***************************************************************************++
  1826. Routine Description:
  1827. Allocates the necessary file entry from non-paged pool. This entry
  1828. get removed from the list when the corresponding config group object
  1829. has been destroyed. At that time RemoveLogFile entry called and
  1830. it frees this memory.
  1831. Arguments:
  1832. pConfig - corresponding cgroup object
  1833. ppEntry - will point to newly created entry.
  1834. --***************************************************************************/
  1835. NTSTATUS
  1836. UlpConstructLogEntry(
  1837. IN PHTTP_CONFIG_GROUP_LOGGING pConfig,
  1838. OUT PUL_LOG_FILE_ENTRY * ppEntry
  1839. )
  1840. {
  1841. NTSTATUS Status;
  1842. PUL_LOG_FILE_ENTRY pEntry;
  1843. //
  1844. // Sanity check and init.
  1845. //
  1846. PAGED_CODE();
  1847. ASSERT(pConfig);
  1848. ASSERT(ppEntry);
  1849. Status = STATUS_SUCCESS;
  1850. pEntry = NULL;
  1851. //
  1852. // Allocate a memory for our new logfile entry in the list. To avoid the
  1853. // frequent reallocs for the log entry.E.g. we receive a timer update
  1854. // and the filename changes according to new time.We will try to allocate
  1855. // a fixed amount here for all the possible file_names ( But this doesn't
  1856. // include the log_dir changes may happen through cgroup. In that case we
  1857. // will reallocate a new one) It should be nonpaged because it holds an
  1858. // eresource.
  1859. //
  1860. pEntry = UL_ALLOCATE_STRUCT(
  1861. NonPagedPool,
  1862. UL_LOG_FILE_ENTRY,
  1863. UL_LOG_FILE_ENTRY_POOL_TAG
  1864. );
  1865. if (pEntry == NULL)
  1866. {
  1867. return STATUS_NO_MEMORY;
  1868. }
  1869. pEntry->Signature = UL_LOG_FILE_ENTRY_POOL_TAG;
  1870. //
  1871. // No filename yet, it will generated when the first hit happens,
  1872. // and before we actually create the log file.
  1873. //
  1874. pEntry->FileName.Buffer = NULL;
  1875. pEntry->FileName.Length = 0;
  1876. pEntry->FileName.MaximumLength = 0;
  1877. //
  1878. // Init the entry eresource
  1879. //
  1880. UlInitializePushLock(
  1881. &pEntry->EntryPushLock,
  1882. "EntryPushLock",
  1883. 0,
  1884. UL_LOG_FILE_ENTRY_POOL_TAG
  1885. );
  1886. //
  1887. // No file handle or file until a request comes in.
  1888. //
  1889. pEntry->pLogFile = NULL;
  1890. //
  1891. // Set the private logging information from config group.
  1892. //
  1893. pEntry->Format = pConfig->LogFormat;
  1894. pEntry->Period = (HTTP_LOGGING_PERIOD) pConfig->LogPeriod;
  1895. pEntry->TruncateSize = pConfig->LogFileTruncateSize;
  1896. pEntry->LogExtFileFlags = pConfig->LogExtFileFlags;
  1897. pEntry->SiteId = 0;
  1898. //
  1899. // Time to initialize our Log Cycling parameter
  1900. //
  1901. pEntry->TimeToExpire = 0;
  1902. pEntry->TimeToClose = 0;
  1903. pEntry->SequenceNumber = 1;
  1904. pEntry->TotalWritten.QuadPart = (ULONGLONG) 0;
  1905. //
  1906. // The entry state bits
  1907. //
  1908. pEntry->Flags.Value = 0;
  1909. if (pEntry->Format != HttpLoggingTypeW3C)
  1910. {
  1911. pEntry->Flags.LogTitleWritten = 1;
  1912. }
  1913. if (pConfig->LocaltimeRollover)
  1914. {
  1915. pEntry->Flags.LocaltimeRollover = 1;
  1916. }
  1917. //
  1918. // LogBuffer get allocated with the first incoming request
  1919. //
  1920. pEntry->LogBuffer = NULL;
  1921. //
  1922. // Lets happily return our entry
  1923. //
  1924. *ppEntry = pEntry;
  1925. return STATUS_SUCCESS;
  1926. }
  1927. /***************************************************************************++
  1928. Routine Description:
  1929. Small wrapper around handle recycle to ensure it happens under the system
  1930. process context.
  1931. Arguments:
  1932. pEntry - Points to the existing entry.
  1933. --***************************************************************************/
  1934. NTSTATUS
  1935. UlpRecycleLogFile(
  1936. IN OUT PUL_LOG_FILE_ENTRY pEntry
  1937. )
  1938. {
  1939. NTSTATUS Status;
  1940. PAGED_CODE();
  1941. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1942. Status = UlQueueLoggingRoutine(
  1943. (PVOID) pEntry,
  1944. &UlpHandleRecycle
  1945. );
  1946. return Status;
  1947. }
  1948. /***************************************************************************++
  1949. Routine Description:
  1950. This function requires to have the loglist resource shared,as well as
  1951. the logfile entry mutex to be acquired.
  1952. We do not want anybody to Create/Remove/ReConfig to the entry while
  1953. we are working on it, therefore shared access to the loglist.
  1954. We do not want anybody to Hit/Flush to the entry, therefore
  1955. entry's mutex should be acquired.
  1956. Or otherwise caller might have the loglist resource exclusively and
  1957. this will automatically ensure the safety as well. As it is not
  1958. possible for anybody else to acquire entry mutex first w/o having
  1959. the loglist resource shared at least, according to the current
  1960. design.
  1961. Sometimes it may be necessary to scan the new directory to figure out
  1962. the correct sequence numbe rand the file name. Especially after dir
  1963. name reconfig and/or the period becomes MaskPeriod.
  1964. * Always open/close the log files when running under system process. *
  1965. Arguments:
  1966. pEntry - Points to the existing entry.
  1967. --***************************************************************************/
  1968. NTSTATUS
  1969. UlpHandleRecycle(
  1970. IN OUT PVOID pContext
  1971. )
  1972. {
  1973. NTSTATUS Status;
  1974. PUL_LOG_FILE_ENTRY pEntry;
  1975. TIME_FIELDS TimeFields;
  1976. LARGE_INTEGER TimeStamp;
  1977. TIME_FIELDS TimeFieldsLocal;
  1978. LARGE_INTEGER TimeStampLocal;
  1979. PUL_LOG_FILE_HANDLE pLogFile;
  1980. WCHAR _FileName[UL_MAX_FILE_NAME_SUFFIX_LENGTH + 1];
  1981. UNICODE_STRING FileName;
  1982. BOOLEAN UncShare;
  1983. BOOLEAN ACLSupport;
  1984. //
  1985. // Sanity check.
  1986. //
  1987. PAGED_CODE();
  1988. pEntry = (PUL_LOG_FILE_ENTRY) pContext;
  1989. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1990. // Always create the log files when running under system process.
  1991. ASSERT(g_pUlSystemProcess == (PKPROCESS)IoGetCurrentProcess());
  1992. Status = STATUS_SUCCESS;
  1993. pLogFile = NULL;
  1994. FileName.Buffer = _FileName;
  1995. FileName.Length = 0;
  1996. FileName.MaximumLength = sizeof(_FileName);
  1997. //
  1998. // We have two criterions for the log file name
  1999. // its LogFormat and its LogPeriod
  2000. //
  2001. ASSERT(IS_VALID_ANSI_LOGGING_TYPE(pEntry->Format));
  2002. ASSERT(pEntry->Period < HttpLoggingPeriodMaximum);
  2003. ASSERT(pEntry->FileName.Length!=0);
  2004. UlTrace( LOGGING, ("Http!UlpHandleRecycle: pEntry %p \n", pEntry ));
  2005. //
  2006. // This value is computed for the GMT time zone.
  2007. //
  2008. KeQuerySystemTime(&TimeStamp);
  2009. RtlTimeToTimeFields(&TimeStamp, &TimeFields);
  2010. ExSystemTimeToLocalTime(&TimeStamp, &TimeStampLocal);
  2011. RtlTimeToTimeFields(&TimeStampLocal, &TimeFieldsLocal);
  2012. // If we need to scan the directory. Sequence number should start
  2013. // from 1 again. Set this before constructing the log file name.
  2014. if (pEntry->Flags.StaleSequenceNumber &&
  2015. pEntry->Period==HttpLoggingPeriodMaxSize)
  2016. {
  2017. // Init otherwise if QueryDirectory doesn't find any it
  2018. // will not update this value
  2019. pEntry->SequenceNumber = 1;
  2020. }
  2021. //
  2022. // Now construct the filename using the lookup table
  2023. // And the current time
  2024. //
  2025. UlConstructFileName(
  2026. pEntry->Period,
  2027. UL_GET_LOG_FILE_NAME_PREFIX(pEntry->Format),
  2028. DEFAULT_LOG_FILE_EXTENSION,
  2029. &FileName,
  2030. UL_PICK_TIME_FIELD(pEntry, &TimeFieldsLocal, &TimeFields),
  2031. UTF8_LOGGING_ENABLED(),
  2032. &pEntry->SequenceNumber
  2033. );
  2034. if ( pEntry->FileName.MaximumLength <= FileName.Length )
  2035. {
  2036. ASSERT(!"FileName buffer is not sufficient.");
  2037. Status = STATUS_INVALID_PARAMETER;
  2038. goto end;
  2039. }
  2040. //
  2041. // Do the magic and renew the filename. Replace the old file
  2042. // name with the new one.
  2043. //
  2044. ASSERT( pEntry->pShortName != NULL );
  2045. //
  2046. // Get rid of the old filename before flushing the
  2047. // directories and reconcataneting the new file name
  2048. // to the end again.
  2049. //
  2050. *((PWCHAR)pEntry->pShortName) = UNICODE_NULL;
  2051. pEntry->FileName.Length =
  2052. (USHORT) wcslen( pEntry->FileName.Buffer ) * sizeof(WCHAR);
  2053. //
  2054. // Create/Open the director(ies) first. This might be
  2055. // necessary if we get called after an entry reconfiguration
  2056. // and directory name change.
  2057. //
  2058. Status = UlCreateSafeDirectory(&pEntry->FileName,
  2059. &UncShare,
  2060. &ACLSupport
  2061. );
  2062. if (!NT_SUCCESS(Status))
  2063. goto eventlog;
  2064. //
  2065. // Now Restore the short file name pointer back
  2066. //
  2067. pEntry->pShortName = (PWSTR)
  2068. &(pEntry->FileName.Buffer[pEntry->FileName.Length/sizeof(WCHAR)]);
  2069. //
  2070. // Append the new file name ( based on updated current time )
  2071. // to the end.
  2072. //
  2073. Status = RtlAppendUnicodeStringToString( &pEntry->FileName, &FileName );
  2074. if (!NT_SUCCESS(Status))
  2075. goto end;
  2076. //
  2077. // Time to close the old file and reopen a new one
  2078. //
  2079. if (pEntry->pLogFile != NULL)
  2080. {
  2081. //
  2082. // Flush the buffer, close the file and mark the entry
  2083. // inactive.
  2084. //
  2085. UlpMakeEntryInactive(pEntry);
  2086. }
  2087. ASSERT(pEntry->pLogFile == NULL);
  2088. //
  2089. // If the sequence is stale because of the nature of the recycle.
  2090. // And if our period is size based then rescan the new directory
  2091. // to figure out the proper file to open.
  2092. //
  2093. pEntry->TotalWritten.QuadPart = (ULONGLONG) 0;
  2094. if (pEntry->Flags.StaleSequenceNumber &&
  2095. pEntry->Period==HttpLoggingPeriodMaxSize)
  2096. {
  2097. // This call may update the filename, the file size and the
  2098. // sequence number if there is an old file in the new dir.
  2099. Status = UlQueryDirectory(
  2100. &pEntry->FileName,
  2101. pEntry->pShortName,
  2102. UL_GET_LOG_FILE_NAME_PREFIX(pEntry->Format),
  2103. DEFAULT_LOG_FILE_EXTENSION_PLUS_DOT,
  2104. &pEntry->SequenceNumber,
  2105. &pEntry->TotalWritten.QuadPart
  2106. );
  2107. if (!NT_SUCCESS(Status))
  2108. {
  2109. if (Status == STATUS_ACCESS_DENIED)
  2110. {
  2111. Status = STATUS_INVALID_OWNER;
  2112. goto eventlog;
  2113. }
  2114. else
  2115. {
  2116. goto end;
  2117. }
  2118. }
  2119. }
  2120. //
  2121. // Allocate a new log file structure for the new log file we are about
  2122. // to open or create.
  2123. //
  2124. pLogFile = pEntry->pLogFile =
  2125. UL_ALLOCATE_STRUCT(
  2126. NonPagedPool,
  2127. UL_LOG_FILE_HANDLE,
  2128. UL_LOG_FILE_HANDLE_POOL_TAG
  2129. );
  2130. if (pLogFile == NULL)
  2131. {
  2132. Status = STATUS_NO_MEMORY;
  2133. goto end;
  2134. }
  2135. pLogFile->Signature = UL_LOG_FILE_HANDLE_POOL_TAG;
  2136. pLogFile->hFile = NULL;
  2137. UlInitializeWorkItem(&pLogFile->WorkItem);
  2138. //
  2139. // Create the new log file.
  2140. //
  2141. Status = UlCreateLogFile(&pEntry->FileName,
  2142. UncShare,
  2143. ACLSupport,
  2144. &pLogFile->hFile
  2145. );
  2146. if (!NT_SUCCESS(Status))
  2147. {
  2148. goto eventlog;
  2149. }
  2150. ASSERT(pLogFile->hFile);
  2151. pEntry->TotalWritten.QuadPart = UlGetLogFileLength(pLogFile->hFile);
  2152. //
  2153. // Recalculate the time to expire.
  2154. //
  2155. if (pEntry->Flags.StaleTimeToExpire &&
  2156. pEntry->Period != HttpLoggingPeriodMaxSize)
  2157. {
  2158. UlCalculateTimeToExpire(
  2159. UL_PICK_TIME_FIELD(pEntry, &TimeFieldsLocal, &TimeFields),
  2160. pEntry->Period,
  2161. &pEntry->TimeToExpire
  2162. );
  2163. }
  2164. //
  2165. // Set the time to close to default for a new file. The value is in
  2166. // buffer flushup periods.
  2167. //
  2168. pEntry->TimeToClose = DEFAULT_MAX_FILE_IDLE_TIME;
  2169. //
  2170. // By setting the flag to zero, we mark that we need to write title
  2171. // with the next incoming request.But this only applies to W3C format.
  2172. // Otherwise the flag stays as set all the time, and the LogWriter
  2173. // does not attempt to write the title for NCSA and IIS log formats
  2174. // with the next incoming request.
  2175. //
  2176. if (pEntry->Format == HttpLoggingTypeW3C)
  2177. {
  2178. pEntry->Flags.LogTitleWritten = 0;
  2179. }
  2180. else
  2181. {
  2182. pEntry->Flags.LogTitleWritten = 1;
  2183. }
  2184. //
  2185. // File is successfully opened and the entry is no longer inactive.
  2186. // Update our state flags accordingly.
  2187. //
  2188. pEntry->Flags.Active = 1;
  2189. pEntry->Flags.RecyclePending = 0;
  2190. pEntry->Flags.StaleSequenceNumber = 0;
  2191. pEntry->Flags.StaleTimeToExpire = 0;
  2192. pEntry->Flags.CreateFileFailureLogged = 0;
  2193. pEntry->Flags.WriteFailureLogged = 0;
  2194. pEntry->Flags.TitleFlushPending = 0;
  2195. UlTrace(LOGGING, ("Http!UlpHandleRecycle: entry %p, file %S, handle %lx\n",
  2196. pEntry,
  2197. pEntry->FileName.Buffer,
  2198. pLogFile->hFile
  2199. ));
  2200. eventlog:
  2201. if (!NT_SUCCESS(Status))
  2202. {
  2203. if (!pEntry->Flags.CreateFileFailureLogged)
  2204. {
  2205. NTSTATUS TempStatus;
  2206. TempStatus = UlEventLogCreateFailure(
  2207. Status,
  2208. UlEventLogNormal,
  2209. &pEntry->FileName,
  2210. pEntry->SiteId
  2211. );
  2212. if (TempStatus == STATUS_SUCCESS)
  2213. {
  2214. //
  2215. // Avoid filling up the event log with error entries. This code
  2216. // path might get hit every time a request arrives.
  2217. //
  2218. pEntry->Flags.CreateFileFailureLogged = 1;
  2219. }
  2220. UlTrace(LOGGING,(
  2221. "Http!UlpHandleRecycle: Event Logging Status %08lx\n",
  2222. TempStatus
  2223. ));
  2224. }
  2225. }
  2226. end:
  2227. if (!NT_SUCCESS(Status))
  2228. {
  2229. UlTrace(LOGGING,("Http!UlpHandleRecycle: entry %p, failure %08lx\n",
  2230. pEntry,
  2231. Status
  2232. ));
  2233. if (pLogFile != NULL)
  2234. {
  2235. //
  2236. // This means we have already closed the old file but failed
  2237. // when we try to create or open the new one.
  2238. //
  2239. ASSERT(pLogFile->hFile == NULL);
  2240. UL_FREE_POOL_WITH_SIG(pLogFile,UL_LOG_FILE_HANDLE_POOL_TAG);
  2241. pEntry->pLogFile = NULL;
  2242. pEntry->Flags.Active = 0;
  2243. }
  2244. else
  2245. {
  2246. //
  2247. // We were about to recyle the old one but something failed
  2248. // lets try to flush and close the existing file if it's still
  2249. // around.
  2250. //
  2251. if (pEntry->pLogFile)
  2252. {
  2253. UlpMakeEntryInactive(pEntry);
  2254. }
  2255. }
  2256. //
  2257. // Mark this entry RecyclePending so that buffer timer can try to
  2258. // resurrect this back every minute.
  2259. //
  2260. pEntry->Flags.RecyclePending = 1;
  2261. }
  2262. return Status;
  2263. }
  2264. /***************************************************************************++
  2265. Routine Description:
  2266. When the log file is on size based recycling and if we write this new
  2267. record to the file, we have to recycle. This function returns TRUE.
  2268. Otherwise it returns FALSE.
  2269. Arguments:
  2270. pEntry: The log file entry.
  2271. NewRecordSize: The size of the new record going to the buffer. (Bytes)
  2272. --***************************************************************************/
  2273. __inline
  2274. BOOLEAN
  2275. UlpIsLogFileOverFlow(
  2276. IN PUL_LOG_FILE_ENTRY pEntry,
  2277. IN ULONG NewRecordSize
  2278. )
  2279. {
  2280. if (pEntry->Period != HttpLoggingPeriodMaxSize ||
  2281. pEntry->TruncateSize == HTTP_LIMIT_INFINITE)
  2282. {
  2283. return FALSE;
  2284. }
  2285. else
  2286. {
  2287. //
  2288. // BufferUsed: Amount of log buffer we are >currently< using.
  2289. //
  2290. ULONG BufferUsed = 0;
  2291. if (pEntry->LogBuffer)
  2292. {
  2293. BufferUsed = pEntry->LogBuffer->BufferUsed;
  2294. }
  2295. //
  2296. // TotalWritten get updated >only< with buffer flush. Therefore
  2297. // we have to pay attention to the buffer used.
  2298. //
  2299. if ((pEntry->TotalWritten.QuadPart
  2300. + (ULONGLONG) BufferUsed
  2301. + (ULONGLONG) NewRecordSize
  2302. ) >= (ULONGLONG) pEntry->TruncateSize)
  2303. {
  2304. UlTrace(LOGGING,
  2305. ("Http!UlpIsLogFileOverFlow: pEntry %p FileBuffer %p "
  2306. "TW:%I64d B:%d R:%d T:%d\n",
  2307. pEntry,
  2308. pEntry->LogBuffer,
  2309. pEntry->TotalWritten.QuadPart,
  2310. BufferUsed,
  2311. NewRecordSize,
  2312. pEntry->TruncateSize
  2313. ));
  2314. return TRUE;
  2315. }
  2316. else
  2317. {
  2318. return FALSE;
  2319. }
  2320. }
  2321. }
  2322. /***************************************************************************++
  2323. Routine Description:
  2324. UlpInitializeGMTOffset :
  2325. Calculates and builds the time difference string.
  2326. Get called during the initialization.
  2327. And every hour after that.
  2328. --***************************************************************************/
  2329. VOID
  2330. UlpGetGMTOffset()
  2331. {
  2332. RTL_TIME_ZONE_INFORMATION Tzi;
  2333. NTSTATUS Status;
  2334. CHAR Sign;
  2335. LONG Bias;
  2336. ULONG Hour;
  2337. ULONG Minute;
  2338. ULONG DT = UL_TIME_ZONE_ID_UNKNOWN;
  2339. LONG BiasN = 0;
  2340. PAGED_CODE();
  2341. //
  2342. // get the timezone data from the system
  2343. //
  2344. Status = NtQuerySystemInformation(
  2345. SystemCurrentTimeZoneInformation,
  2346. (PVOID)&Tzi,
  2347. sizeof(Tzi),
  2348. NULL
  2349. );
  2350. if (!NT_SUCCESS(Status))
  2351. {
  2352. UlTrace(LOGGING,("Http!UlpGetGMTOffset: failure %08lx\n", Status));
  2353. }
  2354. else
  2355. {
  2356. DT = UlCalcTimeZoneIdAndBias(&Tzi, &BiasN);
  2357. }
  2358. if ( BiasN > 0 )
  2359. {
  2360. //
  2361. // UTC = local time + bias
  2362. //
  2363. Bias = BiasN;
  2364. Sign = '-';
  2365. }
  2366. else
  2367. {
  2368. Bias = -1 * BiasN;
  2369. Sign = '+';
  2370. }
  2371. Minute = Bias % 60;
  2372. Hour = (Bias - Minute) / 60;
  2373. UlTrace( LOGGING,
  2374. ("Http!UlpGetGMTOffset: %c%02d:%02d (h:m) D/S %d BiasN %d\n",
  2375. Sign,
  2376. Hour,
  2377. Minute,
  2378. DT,
  2379. BiasN
  2380. ) );
  2381. _snprintf( g_GMTOffset,
  2382. SIZE_OF_GMT_OFFSET,
  2383. "%c%02d%02d",
  2384. Sign,
  2385. Hour,
  2386. Minute
  2387. );
  2388. }
  2389. /***************************************************************************++
  2390. Routine Description:
  2391. Allocates a log data buffer from pool.
  2392. Arguments:
  2393. pLogData - The internal buffer to hold logging info. We will keep this
  2394. around until we are done with logging.
  2395. pRequest - Pointer to the currently logged request.
  2396. pConfigGroup - CG from cache or from request
  2397. --***************************************************************************/
  2398. PUL_LOG_DATA_BUFFER
  2399. UlpAllocateLogDataBuffer(
  2400. IN ULONG Size,
  2401. IN PUL_INTERNAL_REQUEST pRequest,
  2402. IN PUL_CONFIG_GROUP_OBJECT pConfigGroup
  2403. )
  2404. {
  2405. PUL_LOG_DATA_BUFFER pLogData = NULL;
  2406. //
  2407. // Sanity check.
  2408. //
  2409. PAGED_CODE();
  2410. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  2411. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  2412. if (Size > UL_ANSI_LOG_LINE_BUFFER_SIZE)
  2413. {
  2414. //
  2415. // Provided buffer is not big enough to hold the user data.
  2416. //
  2417. pLogData = UlReallocLogDataBuffer(Size, FALSE);
  2418. }
  2419. else
  2420. {
  2421. //
  2422. // Default is enough, try to pop it from the lookaside list.
  2423. //
  2424. pLogData = UlPplAllocateLogDataBuffer(FALSE);
  2425. }
  2426. //
  2427. // If failed to allocate then bail out. We won't be logging this request.
  2428. //
  2429. if (pLogData)
  2430. {
  2431. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  2432. ASSERT(pLogData->Flags.Binary == 0);
  2433. ASSERT(pLogData->Size > 0);
  2434. //
  2435. // Initialize Log Fields in the Log Buffer
  2436. //
  2437. UL_REFERENCE_INTERNAL_REQUEST(pRequest);
  2438. pLogData->pRequest = pRequest;
  2439. pLogData->Flags.CacheAndSendResponse = FALSE;
  2440. pLogData->BytesTransferred = 0;
  2441. pLogData->Used = 0;
  2442. pLogData->Data.Str.Format = pConfigGroup->LoggingConfig.LogFormat;
  2443. pLogData->Data.Str.Flags =
  2444. UL_GET_LOG_TYPE_MASK(
  2445. pConfigGroup->LoggingConfig.LogFormat,
  2446. pConfigGroup->LoggingConfig.LogExtFileFlags
  2447. );
  2448. pLogData->Data.Str.Offset1 = 0;
  2449. pLogData->Data.Str.Offset2 = 0;
  2450. pLogData->Data.Str.Offset3 = 0;
  2451. UlInitializeWorkItem(&pLogData->WorkItem);
  2452. pRequest->pLogDataCopy = pLogData;
  2453. }
  2454. UlTrace(LOGGING,("Http!UlAllocateLogDataBuffer: pLogData %p \n",pLogData));
  2455. return pLogData;
  2456. }
  2457. /***************************************************************************++
  2458. Routine Description:
  2459. Copy over the user log field by enforcing a limit.
  2460. Post filter out the control characters according to the CharMask.
  2461. Adds a separator character at the end.
  2462. Arguments:
  2463. psz: Pointer to log data buffer. Enough space is assumed to be allocated.
  2464. pField: Field to be copied over.
  2465. FieldLength: It's length.
  2466. FieldLimit: Copy will not exceed this limit.
  2467. chSeparator: Will be written after the converted field.
  2468. bReplace : If field exceeds the limit should we truncate or replace with
  2469. LOG_FIELD_TOO_BIG.
  2470. RestrictiveMask : Mask for filtering out control chars.
  2471. Returns:
  2472. the pointer to the log data buffer after the last written separator.
  2473. --***************************************************************************/
  2474. __inline
  2475. PCHAR
  2476. UlpCopyField(
  2477. IN PCHAR psz,
  2478. IN PCSTR pField,
  2479. IN ULONG FieldLength,
  2480. IN ULONG FieldLimit,
  2481. IN CHAR chSeparator,
  2482. IN BOOLEAN bReplace,
  2483. IN ULONG RestrictiveMask
  2484. )
  2485. {
  2486. if (FieldLength)
  2487. {
  2488. if ((FieldLength > FieldLimit) && bReplace)
  2489. {
  2490. psz = UlStrPrintStr(psz, LOG_FIELD_TOO_BIG, chSeparator);
  2491. }
  2492. else
  2493. {
  2494. ULONG i = 0;
  2495. FieldLength = MIN(FieldLength, FieldLimit);
  2496. while (i < FieldLength)
  2497. {
  2498. if (IS_CHAR_TYPE((*pField),RestrictiveMask))
  2499. {
  2500. *psz++ = '+';
  2501. }
  2502. else
  2503. {
  2504. *psz++ = *pField;
  2505. }
  2506. pField++;
  2507. i++;
  2508. }
  2509. *psz++ = chSeparator;
  2510. }
  2511. }
  2512. else
  2513. {
  2514. *psz++ = '-'; *psz++ = chSeparator;
  2515. }
  2516. return psz;
  2517. }
  2518. /***************************************************************************++
  2519. Routine Description:
  2520. Either does Utf8 conversion or Local Code Page conversion.
  2521. Post filter out the control characters according to the CharMask.
  2522. Adds a separator character at the end.
  2523. Arguments:
  2524. psz: Pointer to log data buffer. Enough space is assumed to be allocated.
  2525. pwField: Unicode field to be converted.
  2526. FieldLength: It's length.
  2527. FieldLimit: Conversion will not exceed this limit.
  2528. chSeparator: Will be written after the converted field.
  2529. bUtf8Enabled: If FALSE Local Code Page conversion otherwise Utf8
  2530. RestrictiveMask : Mask for filtering out control chars.
  2531. Returns:
  2532. the pointer to the log data buffer after the last written separator.
  2533. --***************************************************************************/
  2534. __inline
  2535. PCHAR
  2536. UlpCopyUnicodeField(
  2537. IN PCHAR psz,
  2538. IN PCWSTR pwField,
  2539. IN ULONG FieldLength, // In Bytes
  2540. IN ULONG FieldLimit, // In Bytes
  2541. IN CHAR chSeparator,
  2542. IN BOOLEAN bUtf8Enabled,
  2543. IN ULONG RestrictiveMask
  2544. )
  2545. {
  2546. ASSERT(FieldLimit > (2 * sizeof(WCHAR)));
  2547. if (FieldLength)
  2548. {
  2549. ULONG BytesConverted = 0;
  2550. PCHAR pszT = psz;
  2551. if (bUtf8Enabled)
  2552. {
  2553. LONG DstSize; // In Bytes
  2554. LONG SrcSize; // In Bytes
  2555. // Utf8 Conversion may require up to two times of the source
  2556. // buffer because of a possible 2 byte (a wchar) to 4 byte
  2557. // conversion.
  2558. // TODO: This calculation is slightly pessimistic because the worst case
  2559. // TODO: conversion is from 1 wchar to 3 byte sequence.
  2560. if ((FieldLength * 2) > FieldLimit)
  2561. {
  2562. // Conversion may exceed the dest buffer in the worst
  2563. // case (where every wchar converted to 4 byte sequence),
  2564. // need to truncate the source to avoid overflow.
  2565. SrcSize = FieldLimit / 2;
  2566. DstSize = FieldLimit;
  2567. }
  2568. else
  2569. {
  2570. SrcSize = FieldLength;
  2571. DstSize = FieldLength * 2;
  2572. }
  2573. //
  2574. // HttpUnicodeToUTF8 does not truncate and convert. We actually
  2575. // use shorter url when utf8 conversion is set, to be able to
  2576. // prevent a possible overflow.
  2577. //
  2578. BytesConverted =
  2579. HttpUnicodeToUTF8(
  2580. pwField,
  2581. SrcSize / sizeof(WCHAR), // In WChars
  2582. psz,
  2583. DstSize // In Bytes
  2584. );
  2585. ASSERT(BytesConverted);
  2586. }
  2587. else
  2588. {
  2589. NTSTATUS Status;
  2590. // Local codepage is normally closer to the half the length,
  2591. // but due to the possibility of pre-composed characters,
  2592. // the upperbound of the ANSI length is the UNICODE length
  2593. // in bytes
  2594. Status =
  2595. RtlUnicodeToMultiByteN(
  2596. psz,
  2597. FieldLimit, // Dest in Bytes
  2598. &BytesConverted,
  2599. (PWSTR) pwField,
  2600. FieldLength // Src in Bytes
  2601. );
  2602. ASSERT(NT_SUCCESS(Status));
  2603. }
  2604. psz += BytesConverted;
  2605. // Post convert control chars.
  2606. while (pszT != psz)
  2607. {
  2608. if (IS_CHAR_TYPE((*pszT),RestrictiveMask))
  2609. {
  2610. *pszT = '+';
  2611. }
  2612. pszT++;
  2613. }
  2614. *psz++ = chSeparator;
  2615. }
  2616. else
  2617. {
  2618. *psz++ = '-'; *psz++ = chSeparator;
  2619. }
  2620. return psz;
  2621. }
  2622. /***************************************************************************++
  2623. Routine Description:
  2624. Extended check happens for w3c field against the total buffer size.
  2625. Post filter out the control characters according to the CharMask.
  2626. Adds a separator character at the end.
  2627. Arguments:
  2628. psz: Pointer to log data buffer. Enough space is assumed to be allocated.
  2629. Mask: Picked flags by the user config.
  2630. Flag: Bitmask of the field passed in.
  2631. pField: Field to be copied over.
  2632. FieldLength: It's length.
  2633. FieldLimit: Copy will not exceed this limit.
  2634. BufferUsed: Additional limit comparison happens against toatal buffer used
  2635. Returns:
  2636. the pointer to the log data buffer after the last written separator.
  2637. --***************************************************************************/
  2638. __inline
  2639. PCHAR
  2640. UlpCopyW3CFieldEx(
  2641. IN PCHAR psz,
  2642. IN ULONG Mask,
  2643. IN ULONG Flag,
  2644. IN PCSTR pField,
  2645. IN ULONG FieldLength,
  2646. IN ULONG FieldLimit,
  2647. IN ULONG BufferUsed,
  2648. IN ULONG BufferSize
  2649. )
  2650. {
  2651. if (Mask & Flag)
  2652. {
  2653. if (FieldLength)
  2654. {
  2655. if ((FieldLength > FieldLimit) ||
  2656. ((BufferUsed + FieldLength) > BufferSize))
  2657. {
  2658. psz = UlStrPrintStr(psz, LOG_FIELD_TOO_BIG, ' ');
  2659. }
  2660. else
  2661. {
  2662. ULONG i = 0;
  2663. ASSERT(FieldLength <= FieldLimit);
  2664. while (i < FieldLength)
  2665. {
  2666. if (IS_HTTP_WHITE((*pField)))
  2667. {
  2668. *psz++ = '+';
  2669. }
  2670. else
  2671. {
  2672. *psz++ = *pField;
  2673. }
  2674. pField++;
  2675. i++;
  2676. }
  2677. *psz++ = ' ';
  2678. }
  2679. }
  2680. else
  2681. {
  2682. *psz++ = '-'; *psz++ = ' ';
  2683. }
  2684. }
  2685. return psz;
  2686. }
  2687. /***************************************************************************++
  2688. Thin wrapper macros for log field copies. See above inline functions.
  2689. --***************************************************************************/
  2690. #define COPY_W3C_FIELD(psz, \
  2691. Mask, \
  2692. Flag, \
  2693. pField, \
  2694. FieldLength, \
  2695. FieldLimit, \
  2696. bReplace) \
  2697. if (Mask & Flag) \
  2698. { \
  2699. psz = UlpCopyField( \
  2700. psz, \
  2701. pField, \
  2702. FieldLength, \
  2703. FieldLimit, \
  2704. ' ', \
  2705. bReplace, \
  2706. HTTP_ISWHITE \
  2707. ); \
  2708. }
  2709. #define COPY_W3C_UNICODE_FIELD( \
  2710. psz, \
  2711. Mask, \
  2712. Flag, \
  2713. pwField, \
  2714. FieldLength, \
  2715. FieldLimit, \
  2716. bUtf8Enabled) \
  2717. if (Mask & Flag) \
  2718. { \
  2719. psz = UlpCopyUnicodeField( \
  2720. psz, \
  2721. pwField, \
  2722. FieldLength, \
  2723. FieldLimit, \
  2724. ' ', \
  2725. bUtf8Enabled, \
  2726. HTTP_ISWHITE \
  2727. ); \
  2728. }
  2729. /***************************************************************************++
  2730. Routine Description:
  2731. For cache-hits extended w3c fields are generated from request headers.
  2732. Post filter out the control characters according to the CharMask.
  2733. Adds a separator character at the end.
  2734. Arguments:
  2735. psz: Pointer to log data buffer. Enough space is assumed to be allocated.
  2736. Mask: Picked flags by the user config.
  2737. Flag: Bitmask of the field passed in.
  2738. pRequest: Internal request
  2739. HeaderId: Identifies the extended field.
  2740. BufferUsed: Additional limit comparison happens against toatal buffer used
  2741. Returns:
  2742. the pointer to the log data buffer after the last written separator.
  2743. --***************************************************************************/
  2744. __inline
  2745. PCHAR
  2746. UlpCopyRequestHeader(
  2747. IN PCHAR psz,
  2748. IN ULONG Mask,
  2749. IN ULONG Flag,
  2750. IN PUL_INTERNAL_REQUEST pRequest,
  2751. IN HTTP_HEADER_ID HeaderId,
  2752. IN ULONG BufferUsed
  2753. )
  2754. {
  2755. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  2756. ASSERT(HeaderId < HttpHeaderRequestMaximum);
  2757. psz = UlpCopyW3CFieldEx(
  2758. psz,
  2759. Mask,
  2760. Flag,
  2761. (PCSTR)pRequest->Headers[HeaderId].pHeader,
  2762. pRequest->HeaderValid[HeaderId] ?
  2763. pRequest->Headers[HeaderId].HeaderLength :
  2764. 0,
  2765. MAX_LOG_EXTEND_FIELD_LEN,
  2766. BufferUsed,
  2767. MAX_LOG_RECORD_LEN
  2768. );
  2769. return psz;
  2770. }
  2771. /***************************************************************************++
  2772. Routine Description:
  2773. Generic function which will generate the TimeStamp field by calculating
  2774. the difference between the time request first get started to be parsed
  2775. and the current time.
  2776. Arguments:
  2777. psz: Pointer to log data buffer. Enough space is assumed to be allocated.
  2778. pRequest: Pointer to internal request structure. For "TimeStamp"
  2779. chSeparator : Once the LONGLONG lifetime converted, separator will be
  2780. copied as well.
  2781. Returns:
  2782. the pointer to the log data buffer after the last written separator.
  2783. --***************************************************************************/
  2784. __inline
  2785. PCHAR
  2786. UlpCopyTimeStamp(
  2787. IN PCHAR psz,
  2788. IN PUL_INTERNAL_REQUEST pRequest,
  2789. IN CHAR chSeparator
  2790. )
  2791. {
  2792. LARGE_INTEGER CurrentTimeStamp;
  2793. LONGLONG LifeTime;
  2794. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  2795. KeQuerySystemTime(&CurrentTimeStamp);
  2796. LifeTime = CurrentTimeStamp.QuadPart
  2797. - pRequest->TimeStamp.QuadPart;
  2798. LifeTime = MAX(LifeTime,0);
  2799. LifeTime /= C_NS_TICKS_PER_MSEC;
  2800. psz = UlStrPrintUlonglong(
  2801. psz,
  2802. (ULONGLONG)LifeTime,
  2803. chSeparator
  2804. );
  2805. return psz;
  2806. }
  2807. /***************************************************************************++
  2808. Routine Description:
  2809. Small utility which will increment the total used for w3c fields.
  2810. Arguments:
  2811. pTotal: Will be incremented.
  2812. Mask: Picked flags by the user config.
  2813. Flag: Bitmask of the field passed in.
  2814. FieldLength: It's length.
  2815. FieldLimit: Copy will not exceed this limit.
  2816. bUtf8Enabled
  2817. --***************************************************************************/
  2818. __inline
  2819. VOID
  2820. UlpIncForW3CField(
  2821. IN PULONG pTotal,
  2822. IN ULONG Mask,
  2823. IN ULONG Flag,
  2824. IN ULONG FieldLength,
  2825. IN ULONG FieldLimit,
  2826. IN BOOLEAN bUtf8Enabled
  2827. )
  2828. {
  2829. if (Mask & Flag)
  2830. {
  2831. if (FieldLength)
  2832. {
  2833. if (bUtf8Enabled)
  2834. {
  2835. *pTotal += MIN((FieldLength * 2),FieldLimit) + 1;
  2836. }
  2837. else
  2838. {
  2839. *pTotal += MIN(FieldLength,FieldLimit) + 1;
  2840. }
  2841. }
  2842. else
  2843. {
  2844. *pTotal += 2; // For "- "
  2845. }
  2846. }
  2847. }
  2848. /***************************************************************************++
  2849. Routine Description:
  2850. This is a worst case estimate for the maximum possible buffer required to
  2851. generate the W3C Log record for a given set of user fields. All the fields
  2852. are assumed to be picked.
  2853. Arguments:
  2854. pLogData : Captured copy of the user log field data.
  2855. --***************************************************************************/
  2856. __inline
  2857. ULONG
  2858. UlpMaxLogLineSizeForW3C(
  2859. IN PHTTP_LOG_FIELDS_DATA pLogData,
  2860. IN BOOLEAN Utf8Enabled
  2861. )
  2862. {
  2863. ULONG FastLength;
  2864. //
  2865. // For each field
  2866. //
  2867. // 1 for separator space.
  2868. // + 1 for '-', in case field length is zero.
  2869. // + pLogData->FieldLength for field, assuming it's always picked.
  2870. // enforce the field limitation to prevent overflow.
  2871. //
  2872. FastLength =
  2873. 2 + MIN(pLogData->ClientIpLength, MAX_LOG_DEFAULT_FIELD_LEN)
  2874. + 2 + MIN(pLogData->ServiceNameLength,MAX_LOG_DEFAULT_FIELD_LEN)
  2875. + 2 + MIN(pLogData->ServerNameLength, MAX_LOG_DEFAULT_FIELD_LEN)
  2876. + 2 + MIN(pLogData->ServerIpLength, MAX_LOG_DEFAULT_FIELD_LEN)
  2877. + 2 + MIN(pLogData->MethodLength, MAX_LOG_METHOD_FIELD_LEN)
  2878. + 2 + MIN(pLogData->UriQueryLength, MAX_LOG_EXTEND_FIELD_LEN)
  2879. + 2 + MIN(pLogData->UserAgentLength, MAX_LOG_EXTEND_FIELD_LEN)
  2880. + 2 + MIN(pLogData->CookieLength, MAX_LOG_EXTEND_FIELD_LEN)
  2881. + 2 + MIN(pLogData->ReferrerLength, MAX_LOG_EXTEND_FIELD_LEN)
  2882. + 2 + MIN(pLogData->HostLength, MAX_LOG_EXTEND_FIELD_LEN)
  2883. + MAX_W3C_FIX_FIELD_OVERHEAD
  2884. ;
  2885. //
  2886. // If Utf8 logging is enabled, unicode fields require more space.
  2887. //
  2888. if (Utf8Enabled)
  2889. {
  2890. //
  2891. // Allow only half of the normal limit, so that conversion doesn't
  2892. // overflow even in the worst case ( 1 wchar to 4 byte conversion)
  2893. //
  2894. FastLength +=
  2895. 2 + MIN((pLogData->UserNameLength * 2),MAX_LOG_USERNAME_FIELD_LEN)
  2896. + 2 + MIN((pLogData->UriStemLength * 2), MAX_LOG_EXTEND_FIELD_LEN)
  2897. ;
  2898. }
  2899. else
  2900. {
  2901. //
  2902. // RtlUnicodeToMultiByteN requires no more than the original unicode
  2903. // buffer size.
  2904. //
  2905. FastLength +=
  2906. 2 + MIN(pLogData->UserNameLength, MAX_LOG_USERNAME_FIELD_LEN)
  2907. + 2 + MIN(pLogData->UriStemLength, MAX_LOG_EXTEND_FIELD_LEN)
  2908. ;
  2909. }
  2910. return FastLength;
  2911. }
  2912. /***************************************************************************++
  2913. Routine Description:
  2914. Now if the fast length calculation exceeds the default log buffer size.
  2915. This function tries to calculate the max required log record length by
  2916. paying attention to whether the field is picked or not. This is to avoid
  2917. oversize-allocation. And we are in the slow path anyway.
  2918. Arguments:
  2919. pLogData : Captured copy of the user log field data.
  2920. --***************************************************************************/
  2921. ULONG
  2922. UlpGetLogLineSizeForW3C(
  2923. IN PHTTP_LOG_FIELDS_DATA pLogData,
  2924. IN ULONG Mask,
  2925. IN BOOLEAN bUtf8Enabled
  2926. )
  2927. {
  2928. ULONG TotalLength = 0;
  2929. //
  2930. // Increment the total length for each picked field.
  2931. //
  2932. UlpIncForW3CField( &TotalLength,
  2933. Mask,
  2934. MD_EXTLOG_DATE,
  2935. W3C_DATE_FIELD_LEN,
  2936. W3C_DATE_FIELD_LEN,
  2937. FALSE);
  2938. UlpIncForW3CField( &TotalLength,
  2939. Mask,
  2940. MD_EXTLOG_TIME,
  2941. W3C_TIME_FIELD_LEN,
  2942. W3C_TIME_FIELD_LEN,
  2943. FALSE);
  2944. UlpIncForW3CField( &TotalLength,
  2945. Mask,
  2946. MD_EXTLOG_CLIENT_IP,
  2947. pLogData->ClientIpLength,
  2948. MAX_LOG_DEFAULT_FIELD_LEN,
  2949. FALSE);
  2950. UlpIncForW3CField( &TotalLength,
  2951. Mask,
  2952. MD_EXTLOG_USERNAME,
  2953. pLogData->UserNameLength,
  2954. MAX_LOG_USERNAME_FIELD_LEN,
  2955. bUtf8Enabled);
  2956. UlpIncForW3CField( &TotalLength,
  2957. Mask,
  2958. MD_EXTLOG_SITE_NAME,
  2959. pLogData->ServiceNameLength,
  2960. MAX_LOG_DEFAULT_FIELD_LEN,
  2961. FALSE);
  2962. UlpIncForW3CField( &TotalLength,
  2963. Mask,
  2964. MD_EXTLOG_COMPUTER_NAME,
  2965. pLogData->ServerNameLength,
  2966. MAX_LOG_DEFAULT_FIELD_LEN,
  2967. FALSE);
  2968. UlpIncForW3CField( &TotalLength,
  2969. Mask,
  2970. MD_EXTLOG_SERVER_IP,
  2971. pLogData->ServerIpLength,
  2972. MAX_LOG_DEFAULT_FIELD_LEN,
  2973. FALSE);
  2974. UlpIncForW3CField( &TotalLength,
  2975. Mask,
  2976. MD_EXTLOG_METHOD,
  2977. pLogData->MethodLength,
  2978. MAX_LOG_METHOD_FIELD_LEN,
  2979. FALSE);
  2980. UlpIncForW3CField( &TotalLength,
  2981. Mask,
  2982. MD_EXTLOG_URI_STEM,
  2983. pLogData->UriStemLength,
  2984. MAX_LOG_EXTEND_FIELD_LEN,
  2985. bUtf8Enabled);
  2986. UlpIncForW3CField( &TotalLength,
  2987. Mask,
  2988. MD_EXTLOG_URI_QUERY,
  2989. pLogData->UriQueryLength,
  2990. MAX_LOG_EXTEND_FIELD_LEN,
  2991. FALSE);
  2992. UlpIncForW3CField( &TotalLength,
  2993. Mask,
  2994. MD_EXTLOG_HTTP_STATUS,
  2995. UL_MAX_HTTP_STATUS_CODE_LENGTH,
  2996. UL_MAX_HTTP_STATUS_CODE_LENGTH,
  2997. FALSE);
  2998. UlpIncForW3CField( &TotalLength,
  2999. Mask,
  3000. MD_EXTLOG_HTTP_SUB_STATUS,
  3001. MAX_USHORT_STR,
  3002. MAX_USHORT_STR,
  3003. FALSE);
  3004. UlpIncForW3CField( &TotalLength,
  3005. Mask,
  3006. MD_EXTLOG_WIN32_STATUS,
  3007. MAX_ULONG_STR,
  3008. MAX_ULONG_STR,
  3009. FALSE);
  3010. UlpIncForW3CField( &TotalLength,
  3011. Mask,
  3012. MD_EXTLOG_SERVER_PORT,
  3013. MAX_USHORT_STR,
  3014. MAX_USHORT_STR,
  3015. FALSE);
  3016. UlpIncForW3CField( &TotalLength,
  3017. Mask,
  3018. MD_EXTLOG_PROTOCOL_VERSION,
  3019. UL_HTTP_VERSION_LENGTH,
  3020. UL_HTTP_VERSION_LENGTH,
  3021. FALSE);
  3022. UlpIncForW3CField( &TotalLength,
  3023. Mask,
  3024. MD_EXTLOG_USER_AGENT,
  3025. pLogData->UserAgentLength,
  3026. MAX_LOG_EXTEND_FIELD_LEN,
  3027. FALSE);
  3028. UlpIncForW3CField( &TotalLength,
  3029. Mask,
  3030. MD_EXTLOG_COOKIE,
  3031. pLogData->CookieLength,
  3032. MAX_LOG_EXTEND_FIELD_LEN,
  3033. FALSE);
  3034. UlpIncForW3CField( &TotalLength,
  3035. Mask,
  3036. MD_EXTLOG_REFERER,
  3037. pLogData->ReferrerLength,
  3038. MAX_LOG_EXTEND_FIELD_LEN,
  3039. FALSE);
  3040. UlpIncForW3CField( &TotalLength,
  3041. Mask,
  3042. MD_EXTLOG_HOST,
  3043. pLogData->HostLength,
  3044. MAX_LOG_EXTEND_FIELD_LEN,
  3045. FALSE);
  3046. UlpIncForW3CField( &TotalLength,
  3047. Mask,
  3048. MD_EXTLOG_BYTES_SENT,
  3049. MAX_ULONGLONG_STR,
  3050. MAX_ULONGLONG_STR,
  3051. FALSE);
  3052. UlpIncForW3CField( &TotalLength,
  3053. Mask,
  3054. MD_EXTLOG_BYTES_RECV,
  3055. MAX_ULONGLONG_STR,
  3056. MAX_ULONGLONG_STR,
  3057. FALSE);
  3058. UlpIncForW3CField( &TotalLength,
  3059. Mask,
  3060. MD_EXTLOG_TIME_TAKEN,
  3061. MAX_ULONGLONG_STR,
  3062. MAX_ULONGLONG_STR,
  3063. FALSE);
  3064. //
  3065. // Finally increment the length for CRLF and terminating null.
  3066. //
  3067. TotalLength += 3; // \r\n\0
  3068. return TotalLength;
  3069. }
  3070. __inline
  3071. ULONG
  3072. UlpGetCacheHitLogLineSizeForW3C(
  3073. IN ULONG Flags,
  3074. IN PUL_INTERNAL_REQUEST pRequest,
  3075. IN ULONG SizeOfFieldsFrmCache
  3076. )
  3077. {
  3078. ULONG NewSize;
  3079. #define INC_FOR_REQUEST_HEADER(Flags,FieldMask,pRequest,Id,Size) \
  3080. if ((Flags & FieldMask) && \
  3081. pRequest->HeaderValid[Id]) \
  3082. { \
  3083. ASSERT( pRequest->Headers[Id].HeaderLength == \
  3084. strlen((const CHAR *)pRequest->Headers[Id].pHeader)); \
  3085. \
  3086. Size += 2 + pRequest->Headers[Id].HeaderLength; \
  3087. }
  3088. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  3089. NewSize = SizeOfFieldsFrmCache + MAX_W3C_CACHE_FIELD_OVERHEAD;
  3090. //
  3091. // And now add extended field's lengths.
  3092. //
  3093. INC_FOR_REQUEST_HEADER(Flags,
  3094. MD_EXTLOG_USER_AGENT,
  3095. pRequest,
  3096. HttpHeaderUserAgent,
  3097. NewSize);
  3098. INC_FOR_REQUEST_HEADER(Flags,
  3099. MD_EXTLOG_COOKIE,
  3100. pRequest,
  3101. HttpHeaderCookie,
  3102. NewSize);
  3103. INC_FOR_REQUEST_HEADER(Flags,
  3104. MD_EXTLOG_REFERER,
  3105. pRequest,
  3106. HttpHeaderReferer,
  3107. NewSize);
  3108. INC_FOR_REQUEST_HEADER(Flags,
  3109. MD_EXTLOG_HOST,
  3110. pRequest,
  3111. HttpHeaderHost,
  3112. NewSize);
  3113. return NewSize;
  3114. }
  3115. __inline
  3116. ULONG
  3117. UlpGetLogLineSizeForNCSA(
  3118. IN PHTTP_LOG_FIELDS_DATA pLogData,
  3119. IN BOOLEAN bUtf8Enabled
  3120. )
  3121. {
  3122. ULONG Size;
  3123. #define NCSA_FIELD_SIZE(length,limit) (1 + MAX(MIN((length),(limit)),1))
  3124. //
  3125. // For each field
  3126. //
  3127. // 1 for separator ' '
  3128. // + pLogData->FieldLength for field, limited by a upper bound.
  3129. // max(length, 1) if length is zero we still need to log a dash.
  3130. //
  3131. //
  3132. // cIP - UserN [07/Jan/2000:00:02:23 -0800] "MTHD URI?QUERY VER" Status bSent
  3133. //
  3134. Size = NCSA_FIELD_SIZE(pLogData->ClientIpLength, MAX_LOG_DEFAULT_FIELD_LEN)
  3135. +
  3136. 2 // "- " for remote user name
  3137. +
  3138. NCSA_FIX_DATE_AND_TIME_FIELD_SIZE // Including the separator
  3139. +
  3140. 1 // '"' : starting double quote
  3141. +
  3142. NCSA_FIELD_SIZE(pLogData->MethodLength,MAX_LOG_METHOD_FIELD_LEN)
  3143. +
  3144. 1 // '?'
  3145. +
  3146. NCSA_FIELD_SIZE(pLogData->UriQueryLength,MAX_LOG_EXTEND_FIELD_LEN)
  3147. +
  3148. UL_HTTP_VERSION_LENGTH + 1
  3149. +
  3150. 1 // "' : ending double quote
  3151. +
  3152. UL_MAX_HTTP_STATUS_CODE_LENGTH + 1 // Status
  3153. +
  3154. MAX_ULONGLONG_STR // BytesSent
  3155. +
  3156. 3 // \r\n\0
  3157. ;
  3158. if (bUtf8Enabled)
  3159. {
  3160. Size +=
  3161. NCSA_FIELD_SIZE((pLogData->UserNameLength * 2),MAX_LOG_USERNAME_FIELD_LEN)
  3162. +
  3163. NCSA_FIELD_SIZE((pLogData->UriStemLength * 2),MAX_LOG_EXTEND_FIELD_LEN)
  3164. ;
  3165. }
  3166. else
  3167. {
  3168. Size +=
  3169. NCSA_FIELD_SIZE(pLogData->UserNameLength,MAX_LOG_USERNAME_FIELD_LEN)
  3170. +
  3171. NCSA_FIELD_SIZE(pLogData->UriStemLength,MAX_LOG_EXTEND_FIELD_LEN)
  3172. ;
  3173. }
  3174. return Size;
  3175. }
  3176. __inline
  3177. ULONG
  3178. UlpGetLogLineSizeForIIS(
  3179. IN PHTTP_LOG_FIELDS_DATA pLogData,
  3180. IN BOOLEAN bUtf8Enabled
  3181. )
  3182. {
  3183. ULONG MaxSize,Frag1size,Frag2size,Frag3size;
  3184. #define IIS_FIELD_SIZE(length,limit) (2 + MAX(MIN((length),(limit)),1))
  3185. Frag1size =
  3186. IIS_FIELD_SIZE(pLogData->ClientIpLength,MAX_LOG_DEFAULT_FIELD_LEN)
  3187. +
  3188. IIS_MAX_DATE_AND_TIME_FIELD_SIZE // Including the separators
  3189. ;
  3190. Frag2size =
  3191. IIS_FIELD_SIZE(pLogData->ServiceNameLength,MAX_LOG_DEFAULT_FIELD_LEN)
  3192. +
  3193. IIS_FIELD_SIZE(pLogData->ServerNameLength,MAX_LOG_DEFAULT_FIELD_LEN)
  3194. +
  3195. IIS_FIELD_SIZE(pLogData->ServerIpLength,MAX_LOG_DEFAULT_FIELD_LEN)
  3196. +
  3197. 2 + MAX_ULONGLONG_STR // TimeTaken
  3198. +
  3199. 2 + MAX_ULONGLONG_STR // BytesReceived
  3200. +
  3201. 2 + MAX_ULONGLONG_STR // BytesSend
  3202. +
  3203. 2 + UL_MAX_HTTP_STATUS_CODE_LENGTH
  3204. +
  3205. 2 + MAX_ULONG_STR // Win32 Status
  3206. ;
  3207. Frag3size =
  3208. IIS_FIELD_SIZE(pLogData->MethodLength,MAX_LOG_METHOD_FIELD_LEN)
  3209. +
  3210. IIS_FIELD_SIZE(pLogData->UriQueryLength,MAX_LOG_EXTEND_FIELD_LEN)
  3211. +
  3212. 3 // \r\n\0
  3213. ;
  3214. if (bUtf8Enabled)
  3215. {
  3216. Frag3size +=
  3217. IIS_FIELD_SIZE((pLogData->UriStemLength * 2),MAX_LOG_EXTEND_FIELD_LEN);
  3218. Frag1size +=
  3219. IIS_FIELD_SIZE((pLogData->UserNameLength * 2),MAX_LOG_USERNAME_FIELD_LEN);
  3220. }
  3221. else
  3222. {
  3223. Frag3size +=
  3224. IIS_FIELD_SIZE(pLogData->UriStemLength,MAX_LOG_EXTEND_FIELD_LEN);
  3225. Frag1size +=
  3226. IIS_FIELD_SIZE(pLogData->UserNameLength,MAX_LOG_USERNAME_FIELD_LEN);
  3227. }
  3228. //
  3229. // First two fragments must always fit to the default buffer.
  3230. //
  3231. ASSERT(Frag1size < IIS_LOG_LINE_DEFAULT_FIRST_FRAGMENT_ALLOCATION_SIZE);
  3232. ASSERT(Frag2size < IIS_LOG_LINE_DEFAULT_SECOND_FRAGMENT_ALLOCATION_SIZE);
  3233. //
  3234. // The required third fragment size may be too big for the default
  3235. // buffer size.
  3236. //
  3237. MaxSize = IIS_LOG_LINE_DEFAULT_FIRST_FRAGMENT_ALLOCATION_SIZE +
  3238. IIS_LOG_LINE_DEFAULT_SECOND_FRAGMENT_ALLOCATION_SIZE +
  3239. Frag3size;
  3240. return MaxSize;
  3241. }
  3242. /***************************************************************************++
  3243. Routine Description:
  3244. Captures and writes the log fields from user buffer to the log line.
  3245. pLogData itself must have been captured by the caller however embedded
  3246. pointers inside of the structure are not, therefore we need to be carefull
  3247. here and cleanup if an exception raises.
  3248. Captures only those necessary fields according to the picked Flags.
  3249. Does UTF8 and LocalCode Page conversion for UserName and URI Stem.
  3250. Leaves enough space for Date & Time fields for late generation.
  3251. WARNING:
  3252. Even though the pUserData is already captured to the kernel buffer, it
  3253. still holds pointers to user-mode memory for individual log fields,
  3254. therefore this function should be called inside a try/except block and
  3255. If this function raises an exception caller should cleanup the allocated
  3256. pLogBuffer.
  3257. Arguments:
  3258. pLogData : Captured user data in a kernel buffer.
  3259. pRequest : Request.
  3260. ppLogBuffer : Returning pLogBuffer.
  3261. --***************************************************************************/
  3262. NTSTATUS
  3263. UlCaptureLogFieldsW3C(
  3264. IN PHTTP_LOG_FIELDS_DATA pLogData,
  3265. IN PUL_INTERNAL_REQUEST pRequest,
  3266. OUT PUL_LOG_DATA_BUFFER *ppLogBuffer
  3267. )
  3268. {
  3269. PUL_LOG_DATA_BUFFER pLogBuffer;
  3270. PUL_CONFIG_GROUP_OBJECT pConfigGroup;
  3271. ULONG Flags;
  3272. PCHAR psz;
  3273. PCHAR pBuffer;
  3274. ULONG FastLength;
  3275. BOOLEAN bUtf8Enabled;
  3276. //
  3277. // Sanity check and init.
  3278. //
  3279. PAGED_CODE();
  3280. ASSERT(pLogData);
  3281. *ppLogBuffer = pLogBuffer = NULL;
  3282. pConfigGroup = pRequest->ConfigInfo.pLoggingConfig;
  3283. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  3284. Flags = pConfigGroup->LoggingConfig.LogExtFileFlags;
  3285. bUtf8Enabled = UTF8_LOGGING_ENABLED();
  3286. //
  3287. // Try the fast length calculation first. If this fails then
  3288. // we need to re-calculate the required length.
  3289. //
  3290. FastLength = UlpMaxLogLineSizeForW3C(pLogData, bUtf8Enabled);
  3291. if (FastLength > UL_ANSI_LOG_LINE_BUFFER_SIZE)
  3292. {
  3293. FastLength = UlpGetLogLineSizeForW3C(
  3294. pLogData,
  3295. Flags,
  3296. bUtf8Enabled
  3297. );
  3298. if (FastLength > MAX_LOG_RECORD_LEN)
  3299. {
  3300. //
  3301. // While we are enforcing 10k upper limit for the log record
  3302. // length. We still allocate slightly larger space to include
  3303. // the overhead for the post-generated log fields. As well as
  3304. // for the replacement strings for "too long" fields.
  3305. //
  3306. // TODO: (PAGE_SIZE - ALIGN_UP(FastLength,PVOID))
  3307. FastLength = MAX_LOG_RECORD_ALLOCATION_LENGTH;
  3308. }
  3309. }
  3310. *ppLogBuffer = pLogBuffer = UlpAllocateLogDataBuffer(
  3311. FastLength,
  3312. pRequest,
  3313. pConfigGroup
  3314. );
  3315. if (!pLogBuffer)
  3316. {
  3317. return STATUS_INSUFFICIENT_RESOURCES;
  3318. }
  3319. ASSERT(pLogBuffer->Data.Str.Format == HttpLoggingTypeW3C);
  3320. ASSERT(pLogBuffer->Data.Str.Flags == Flags);
  3321. //
  3322. // Remember the beginning of the buffer.
  3323. //
  3324. psz = pBuffer = (PCHAR) pLogBuffer->Line;
  3325. //
  3326. // Skip the date and time fields, but reserve the space.
  3327. //
  3328. if ( Flags & MD_EXTLOG_DATE ) psz += W3C_DATE_FIELD_LEN + 1;
  3329. if ( Flags & MD_EXTLOG_TIME ) psz += W3C_TIME_FIELD_LEN + 1;
  3330. //
  3331. // Remember if we have reserved any space for Date and Time or not.
  3332. //
  3333. pLogBuffer->Data.Str.Offset1 = DIFF_USHORT(psz - pBuffer);
  3334. COPY_W3C_FIELD(psz,
  3335. Flags,
  3336. MD_EXTLOG_SITE_NAME,
  3337. pLogData->ServiceName,
  3338. pLogData->ServiceNameLength,
  3339. MAX_LOG_DEFAULT_FIELD_LEN,
  3340. TRUE);
  3341. COPY_W3C_FIELD(psz,
  3342. Flags,
  3343. MD_EXTLOG_COMPUTER_NAME,
  3344. pLogData->ServerName,
  3345. pLogData->ServerNameLength,
  3346. MAX_LOG_DEFAULT_FIELD_LEN,
  3347. TRUE);
  3348. COPY_W3C_FIELD(psz,
  3349. Flags,
  3350. MD_EXTLOG_SERVER_IP,
  3351. pLogData->ServerIp,
  3352. pLogData->ServerIpLength,
  3353. MAX_LOG_DEFAULT_FIELD_LEN,
  3354. TRUE);
  3355. COPY_W3C_FIELD(psz,
  3356. Flags,
  3357. MD_EXTLOG_METHOD,
  3358. pLogData->Method,
  3359. pLogData->MethodLength,
  3360. MAX_LOG_METHOD_FIELD_LEN,
  3361. FALSE);
  3362. COPY_W3C_UNICODE_FIELD(
  3363. psz,
  3364. Flags,
  3365. MD_EXTLOG_URI_STEM,
  3366. pLogData->UriStem,
  3367. pLogData->UriStemLength,
  3368. MAX_LOG_EXTEND_FIELD_LEN,
  3369. bUtf8Enabled);
  3370. COPY_W3C_FIELD(psz,
  3371. Flags,
  3372. MD_EXTLOG_URI_QUERY,
  3373. pLogData->UriQuery,
  3374. pLogData->UriQueryLength,
  3375. MAX_LOG_EXTEND_FIELD_LEN,
  3376. TRUE);
  3377. if ( Flags & MD_EXTLOG_SERVER_PORT )
  3378. {
  3379. psz = UlStrPrintUlong(psz, pLogData->ServerPort,' ');
  3380. }
  3381. //
  3382. // Fields after this point won't be stored in the uri cache entry.
  3383. //
  3384. pLogBuffer->Data.Str.Offset2 = DIFF_USHORT(psz - pBuffer);
  3385. COPY_W3C_UNICODE_FIELD(
  3386. psz,
  3387. Flags,
  3388. MD_EXTLOG_USERNAME,
  3389. pLogData->UserName,
  3390. pLogData->UserNameLength,
  3391. MAX_LOG_USERNAME_FIELD_LEN,
  3392. bUtf8Enabled);
  3393. COPY_W3C_FIELD(psz,
  3394. Flags,
  3395. MD_EXTLOG_CLIENT_IP,
  3396. pLogData->ClientIp,
  3397. pLogData->ClientIpLength,
  3398. MAX_LOG_DEFAULT_FIELD_LEN,
  3399. TRUE);
  3400. if ( Flags & MD_EXTLOG_PROTOCOL_VERSION )
  3401. {
  3402. psz = UlCopyHttpVersion(psz, pRequest->Version, ' ');
  3403. }
  3404. ASSERT(DIFF(psz - pBuffer) <= MAX_LOG_RECORD_LEN);
  3405. //
  3406. // Following fields may still overflow the allocated buffer
  3407. // even though they are not exceeding their length limits.
  3408. // Ex function does the extra check.
  3409. //
  3410. psz = UlpCopyW3CFieldEx(
  3411. psz,
  3412. Flags,
  3413. MD_EXTLOG_USER_AGENT,
  3414. pLogData->UserAgent,
  3415. pLogData->UserAgentLength,
  3416. MAX_LOG_EXTEND_FIELD_LEN,
  3417. DIFF(psz - pBuffer),
  3418. MAX_LOG_RECORD_LEN);
  3419. psz = UlpCopyW3CFieldEx(
  3420. psz,
  3421. Flags,
  3422. MD_EXTLOG_COOKIE,
  3423. pLogData->Cookie,
  3424. pLogData->CookieLength,
  3425. MAX_LOG_EXTEND_FIELD_LEN,
  3426. DIFF(psz - pBuffer),
  3427. MAX_LOG_RECORD_LEN);
  3428. psz = UlpCopyW3CFieldEx(
  3429. psz,
  3430. Flags,
  3431. MD_EXTLOG_REFERER,
  3432. pLogData->Referrer,
  3433. pLogData->ReferrerLength,
  3434. MAX_LOG_EXTEND_FIELD_LEN,
  3435. DIFF(psz - pBuffer),
  3436. MAX_LOG_RECORD_LEN);
  3437. psz = UlpCopyW3CFieldEx(
  3438. psz,
  3439. Flags,
  3440. MD_EXTLOG_HOST,
  3441. pLogData->Host,
  3442. pLogData->HostLength,
  3443. MAX_LOG_EXTEND_FIELD_LEN,
  3444. DIFF(psz - pBuffer),
  3445. MAX_LOG_RECORD_LEN);
  3446. //
  3447. // Status fields may be updated upon send completion.
  3448. //
  3449. pLogBuffer->ProtocolStatus =
  3450. (USHORT) MIN(pLogData->ProtocolStatus,UL_MAX_HTTP_STATUS_CODE);
  3451. pLogBuffer->SubStatus = pLogData->SubStatus;
  3452. pLogBuffer->Win32Status = pLogData->Win32Status;
  3453. //
  3454. // Finally calculate the used space and verify that we did not overflow.
  3455. //
  3456. pLogBuffer->Used = DIFF_USHORT(psz - pBuffer);
  3457. ASSERT( pLogBuffer->Used <= MAX_LOG_RECORD_LEN );
  3458. UlTrace(LOGGING,
  3459. ("Http!UlCaptureLogFields: user %p kernel %p\n",
  3460. pLogData,pLogBuffer
  3461. ));
  3462. return STATUS_SUCCESS;
  3463. }
  3464. NTSTATUS
  3465. UlCaptureLogFieldsNCSA(
  3466. IN PHTTP_LOG_FIELDS_DATA pLogData,
  3467. IN OUT PUL_INTERNAL_REQUEST pRequest,
  3468. OUT PUL_LOG_DATA_BUFFER *ppLogBuffer
  3469. )
  3470. {
  3471. PCHAR psz;
  3472. PCHAR pBuffer;
  3473. ULONG MaxLength;
  3474. BOOLEAN bUtf8Enabled;
  3475. PUL_LOG_DATA_BUFFER pLogBuffer;
  3476. PUL_CONFIG_GROUP_OBJECT pConfigGroup;
  3477. //
  3478. // Sanity check.
  3479. //
  3480. PAGED_CODE();
  3481. *ppLogBuffer = pLogBuffer = NULL;
  3482. pConfigGroup = pRequest->ConfigInfo.pLoggingConfig;
  3483. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  3484. bUtf8Enabled = UTF8_LOGGING_ENABLED();
  3485. //
  3486. // Estimate the maximum possible length (worst case) and
  3487. // allocate a bigger log data buffer line if necessary.
  3488. //
  3489. MaxLength = UlpGetLogLineSizeForNCSA(pLogData, bUtf8Enabled);
  3490. MaxLength = MIN(MaxLength, MAX_LOG_RECORD_LEN);
  3491. *ppLogBuffer = pLogBuffer = UlpAllocateLogDataBuffer(
  3492. MaxLength,
  3493. pRequest,
  3494. pConfigGroup
  3495. );
  3496. if (!pLogBuffer)
  3497. {
  3498. return STATUS_INSUFFICIENT_RESOURCES;
  3499. }
  3500. ASSERT(pLogBuffer->Data.Str.Format == HttpLoggingTypeNCSA);
  3501. ASSERT(pLogBuffer->Data.Str.Flags == UL_DEFAULT_NCSA_FIELDS);
  3502. //
  3503. // cIP - UserN [07/Jan/2000:00:02:23 -0800] "MTHD URI?QUERY VER" Status bSent
  3504. //
  3505. psz = pBuffer = (PCHAR) pLogBuffer->Line;
  3506. psz = UlpCopyField(psz,
  3507. pLogData->ClientIp,
  3508. pLogData->ClientIpLength,
  3509. MAX_LOG_DEFAULT_FIELD_LEN,
  3510. ' ',
  3511. TRUE,
  3512. HTTP_ISWHITE);
  3513. *psz++ = '-'; *psz++ = ' '; // Fixed dash
  3514. psz = UlpCopyUnicodeField(
  3515. psz,
  3516. pLogData->UserName,
  3517. pLogData->UserNameLength,
  3518. MAX_LOG_USERNAME_FIELD_LEN,
  3519. ' ',
  3520. bUtf8Enabled,
  3521. HTTP_ISWHITE);
  3522. //
  3523. // Reserve a space for the date and time fields.
  3524. //
  3525. pLogBuffer->Data.Str.Offset1 = DIFF_USHORT(psz - pBuffer);
  3526. psz += NCSA_FIX_DATE_AND_TIME_FIELD_SIZE;
  3527. //
  3528. // "MTHD U-STEM?U-QUERY P-VER"
  3529. //
  3530. *psz++ = '\"';
  3531. psz = UlpCopyField(psz,
  3532. pLogData->Method,
  3533. pLogData->MethodLength,
  3534. MAX_LOG_METHOD_FIELD_LEN,
  3535. ' ',
  3536. FALSE,
  3537. HTTP_CTL);
  3538. psz = UlpCopyUnicodeField(
  3539. psz,
  3540. pLogData->UriStem,
  3541. pLogData->UriStemLength,
  3542. MAX_LOG_EXTEND_FIELD_LEN,
  3543. '?',
  3544. bUtf8Enabled,
  3545. HTTP_CTL);
  3546. if (pLogData->UriQueryLength)
  3547. {
  3548. psz = UlpCopyField(psz,
  3549. pLogData->UriQuery,
  3550. pLogData->UriQueryLength,
  3551. MAX_LOG_EXTEND_FIELD_LEN,
  3552. ' ',
  3553. TRUE,
  3554. HTTP_CTL);
  3555. }
  3556. else
  3557. {
  3558. psz--;
  3559. if ((*psz) == '?') *psz = ' '; // Eat the question mark
  3560. psz++;
  3561. }
  3562. pLogBuffer->Data.Str.Offset2 = DIFF_USHORT(psz - pBuffer);
  3563. psz = UlCopyHttpVersion(psz, pRequest->Version, '\"');
  3564. *psz++ = ' ';
  3565. //
  3566. // Set the log record length
  3567. //
  3568. ASSERT(pLogBuffer->Used == 0);
  3569. pLogBuffer->Used = DIFF_USHORT(psz - pBuffer);
  3570. //
  3571. // Store the status to the kernel buffer.
  3572. //
  3573. pLogBuffer->ProtocolStatus =
  3574. (USHORT) MIN(pLogData->ProtocolStatus,UL_MAX_HTTP_STATUS_CODE);
  3575. return STATUS_SUCCESS;
  3576. }
  3577. NTSTATUS
  3578. UlCaptureLogFieldsIIS(
  3579. IN PHTTP_LOG_FIELDS_DATA pLogData,
  3580. IN PUL_INTERNAL_REQUEST pRequest,
  3581. OUT PUL_LOG_DATA_BUFFER *ppLogBuffer
  3582. )
  3583. {
  3584. PUL_LOG_DATA_BUFFER pLogBuffer;
  3585. PUL_CONFIG_GROUP_OBJECT pConfigGroup;
  3586. PCHAR psz;
  3587. PCHAR pBuffer;
  3588. ULONG MaxLength;
  3589. BOOLEAN bUtf8Enabled;
  3590. //
  3591. // Sanity check.
  3592. //
  3593. PAGED_CODE();
  3594. *ppLogBuffer = pLogBuffer = NULL;
  3595. pConfigGroup = pRequest->ConfigInfo.pLoggingConfig;
  3596. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  3597. bUtf8Enabled = UTF8_LOGGING_ENABLED();
  3598. //
  3599. // Try worst case allocation.
  3600. //
  3601. MaxLength = UlpGetLogLineSizeForIIS(pLogData,bUtf8Enabled);
  3602. ASSERT(MaxLength <= MAX_LOG_RECORD_LEN);
  3603. *ppLogBuffer = pLogBuffer = UlpAllocateLogDataBuffer(
  3604. MaxLength,
  3605. pRequest,
  3606. pConfigGroup
  3607. );
  3608. if (!pLogBuffer)
  3609. {
  3610. return STATUS_INSUFFICIENT_RESOURCES;
  3611. }
  3612. ASSERT(pLogBuffer->Data.Str.Format == HttpLoggingTypeIIS);
  3613. ASSERT(pLogBuffer->Data.Str.Flags == UL_DEFAULT_IIS_FIELDS);
  3614. // IIS log line is fragmented to three pieces as follows;
  3615. //
  3616. // [UIP, user, D, T, ][site, Server, SIP, Ttaken, BR, BS, PS, WS, ][M, URI, URIQUERY,]
  3617. // 0 511 512 1023 1024 4096
  3618. psz = pBuffer = (PCHAR) pLogBuffer->Line;
  3619. psz = UlpCopyField(psz,
  3620. pLogData->ClientIp,
  3621. pLogData->ClientIpLength,
  3622. MAX_LOG_DEFAULT_FIELD_LEN,
  3623. ',',
  3624. TRUE,
  3625. HTTP_CTL);
  3626. *psz++ = ' ';
  3627. psz = UlpCopyUnicodeField(
  3628. psz,
  3629. pLogData->UserName,
  3630. pLogData->UserNameLength,
  3631. MAX_LOG_USERNAME_FIELD_LEN,
  3632. ',',
  3633. bUtf8Enabled,
  3634. HTTP_CTL);
  3635. *psz++ = ' ';
  3636. // Store the current size of the 1st frag to Offset1
  3637. pLogBuffer->Data.Str.Offset1 = DIFF_USHORT(psz - pBuffer);
  3638. // Move pointer to the beginning of 2nd frag.
  3639. pBuffer = psz = (PCHAR) &pLogBuffer->Line[IIS_LOG_LINE_SECOND_FRAGMENT_OFFSET];
  3640. psz = UlpCopyField(psz,
  3641. pLogData->ServiceName,
  3642. pLogData->ServiceNameLength,
  3643. MAX_LOG_DEFAULT_FIELD_LEN,
  3644. ',',
  3645. TRUE,
  3646. HTTP_CTL);
  3647. *psz++ = ' ';
  3648. psz = UlpCopyField(psz,
  3649. pLogData->ServerName,
  3650. pLogData->ServerNameLength,
  3651. MAX_LOG_DEFAULT_FIELD_LEN,
  3652. ',',
  3653. TRUE,
  3654. HTTP_CTL);
  3655. *psz++ = ' ';
  3656. psz = UlpCopyField(psz,
  3657. pLogData->ServerIp,
  3658. pLogData->ServerIpLength,
  3659. MAX_LOG_DEFAULT_FIELD_LEN,
  3660. ',',
  3661. TRUE,
  3662. HTTP_CTL);
  3663. *psz++ = ' ';
  3664. // Store the current size of the 2nd frag to Offset2
  3665. pLogBuffer->Data.Str.Offset2 = DIFF_USHORT(psz - pBuffer);
  3666. // Following fields might be updated upon send completion
  3667. // do not copy them yet, just store their values.
  3668. pLogBuffer->ProtocolStatus =
  3669. (USHORT) MIN(pLogData->ProtocolStatus,UL_MAX_HTTP_STATUS_CODE);
  3670. pLogBuffer->Win32Status = pLogData->Win32Status;
  3671. // Move pointer to the beginning of 3rd frag.
  3672. pBuffer = psz = (PCHAR) &pLogBuffer->Line[IIS_LOG_LINE_THIRD_FRAGMENT_OFFSET];
  3673. psz = UlpCopyField(psz,
  3674. pLogData->Method,
  3675. pLogData->MethodLength,
  3676. MAX_LOG_METHOD_FIELD_LEN,
  3677. ',',
  3678. FALSE,
  3679. HTTP_CTL);
  3680. *psz++ = ' ';
  3681. psz = UlpCopyUnicodeField(
  3682. psz,
  3683. pLogData->UriStem,
  3684. pLogData->UriStemLength,
  3685. MAX_LOG_EXTEND_FIELD_LEN,
  3686. ',',
  3687. bUtf8Enabled,
  3688. HTTP_CTL);
  3689. *psz++ = ' ';
  3690. psz = UlpCopyField(psz,
  3691. pLogData->UriQuery,
  3692. pLogData->UriQueryLength,
  3693. MAX_LOG_EXTEND_FIELD_LEN,
  3694. ',',
  3695. TRUE,
  3696. HTTP_CTL);
  3697. // Terminate the 3rd fragment. It is complete.
  3698. *psz++ = '\r'; *psz++ = '\n';
  3699. ASSERT(pLogBuffer->Used == 0);
  3700. pLogBuffer->Used = DIFF_USHORT(psz - pBuffer);
  3701. *psz++ = ANSI_NULL;
  3702. return STATUS_SUCCESS;
  3703. }
  3704. USHORT
  3705. UlpCompleteLogRecordW3C(
  3706. IN OUT PUL_LOG_DATA_BUFFER pLogData,
  3707. IN PUL_URI_CACHE_ENTRY pUriEntry
  3708. )
  3709. {
  3710. PUL_INTERNAL_REQUEST pRequest;
  3711. PCHAR psz;
  3712. PCHAR pBuffer;
  3713. PCHAR pLine;
  3714. ULONG BytesWritten;
  3715. ULONG Flags;
  3716. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  3717. ASSERT(pLogData->Data.Str.Format == HttpLoggingTypeW3C);
  3718. BytesWritten = 0;
  3719. Flags = pLogData->Data.Str.Flags;
  3720. pRequest = pLogData->pRequest;
  3721. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  3722. psz = pLine = (PCHAR) pLogData->Line;
  3723. // For cache-miss and cache-and-send hits the space for
  3724. // date and time is reserved at the beginning and their
  3725. // sizes are already counted for to the "Used". For pure
  3726. // cache hits, the buffer is freshly allocated. It's all
  3727. // right to copy over.
  3728. if (Flags & MD_EXTLOG_DATE)
  3729. {
  3730. UlGetDateTimeFields(
  3731. HttpLoggingTypeW3C,
  3732. psz,
  3733. &BytesWritten,
  3734. NULL,
  3735. NULL
  3736. );
  3737. psz += BytesWritten; *psz++ = ' ';
  3738. ASSERT(BytesWritten == W3C_DATE_FIELD_LEN);
  3739. }
  3740. if (Flags & MD_EXTLOG_TIME)
  3741. {
  3742. UlGetDateTimeFields(
  3743. HttpLoggingTypeW3C,
  3744. NULL,
  3745. NULL,
  3746. psz,
  3747. &BytesWritten
  3748. );
  3749. psz += BytesWritten; *psz++ = ' ';
  3750. ASSERT(BytesWritten == W3C_TIME_FIELD_LEN);
  3751. }
  3752. // If this is a cache hit restore the logging data in
  3753. // to the newly allocated log data buffer.
  3754. if (IS_PURE_CACHE_HIT(pUriEntry,pLogData))
  3755. {
  3756. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriEntry));
  3757. ASSERT(pLogData->Used == 0);
  3758. // The picked flags should not change during the
  3759. // lifetime of a cache entry.
  3760. ASSERT(DIFF(psz - pLine) == pUriEntry->UsedOffset1);
  3761. if (pUriEntry->LogDataLength)
  3762. {
  3763. RtlCopyMemory(psz,
  3764. pUriEntry->pLogData,
  3765. pUriEntry->LogDataLength
  3766. );
  3767. psz += pUriEntry->LogDataLength;
  3768. }
  3769. // Some fields need to be generated for each cache hit. These
  3770. // are not stored in the cache entry.
  3771. if ( Flags & MD_EXTLOG_USERNAME )
  3772. {
  3773. *psz++ = '-'; *psz++ = ' ';
  3774. }
  3775. if ( Flags & MD_EXTLOG_CLIENT_IP )
  3776. {
  3777. psz = UlStrPrintIP(
  3778. psz,
  3779. pRequest->pHttpConn->pConnection->RemoteAddress,
  3780. pRequest->pHttpConn->pConnection->AddressType,
  3781. ' '
  3782. );
  3783. }
  3784. if ( Flags & MD_EXTLOG_PROTOCOL_VERSION )
  3785. {
  3786. psz = UlCopyHttpVersion(psz, pRequest->Version, ' ');
  3787. }
  3788. psz = UlpCopyRequestHeader(
  3789. psz,
  3790. Flags,
  3791. MD_EXTLOG_USER_AGENT,
  3792. pRequest,
  3793. HttpHeaderUserAgent,
  3794. DIFF(psz - pLine)
  3795. );
  3796. psz = UlpCopyRequestHeader(
  3797. psz,
  3798. Flags,
  3799. MD_EXTLOG_COOKIE,
  3800. pRequest,
  3801. HttpHeaderCookie,
  3802. DIFF(psz - pLine)
  3803. );
  3804. psz = UlpCopyRequestHeader(
  3805. psz,
  3806. Flags,
  3807. MD_EXTLOG_REFERER,
  3808. pRequest,
  3809. HttpHeaderReferer,
  3810. DIFF(psz - pLine)
  3811. );
  3812. psz = UlpCopyRequestHeader(
  3813. psz,
  3814. Flags,
  3815. MD_EXTLOG_HOST,
  3816. pRequest,
  3817. HttpHeaderHost,
  3818. DIFF(psz - pLine)
  3819. );
  3820. // This was a newly allocated buffer, init the "Used" field
  3821. // accordingly.
  3822. pLogData->Used = DIFF_USHORT(psz - pLine);
  3823. }
  3824. // Now complete the half baked log record by copying the remaining
  3825. // fields to the end.
  3826. pBuffer = psz = &pLine[pLogData->Used];
  3827. if ( Flags & MD_EXTLOG_HTTP_STATUS )
  3828. {
  3829. psz = UlStrPrintProtocolStatus(psz,pLogData->ProtocolStatus,' ');
  3830. }
  3831. if ( Flags & MD_EXTLOG_HTTP_SUB_STATUS )
  3832. {
  3833. psz = UlStrPrintUlong(psz, pLogData->SubStatus, ' ');
  3834. }
  3835. if ( Flags & MD_EXTLOG_WIN32_STATUS )
  3836. {
  3837. psz = UlStrPrintUlong(psz, pLogData->Win32Status,' ');
  3838. }
  3839. if ( Flags & MD_EXTLOG_BYTES_SENT )
  3840. {
  3841. psz = UlStrPrintUlonglong(psz, pRequest->BytesSent,' ');
  3842. }
  3843. if ( Flags & MD_EXTLOG_BYTES_RECV )
  3844. {
  3845. psz = UlStrPrintUlonglong(psz, pRequest->BytesReceived,' ');
  3846. }
  3847. if ( Flags & MD_EXTLOG_TIME_TAKEN )
  3848. {
  3849. psz = UlpCopyTimeStamp(psz, pRequest, ' ');
  3850. }
  3851. // Now calculate the space we have used
  3852. pLogData->Used =
  3853. (USHORT) (pLogData->Used + DIFF_USHORT(psz - pBuffer));
  3854. // Eat the last space and write the \r\n to the end.
  3855. // Only if we have any fields picked and written.
  3856. if (pLogData->Used)
  3857. {
  3858. psz = &pLine[pLogData->Used-1]; // Eat the last space
  3859. *psz++ = '\r'; *psz++ = '\n'; *psz++ = ANSI_NULL;
  3860. pLogData->Used += 1;
  3861. }
  3862. // Cleanup the UsedOffsets otherwise it will be interpreted by the
  3863. // caller as fragmented.
  3864. pLogData->Data.Str.Offset1 = pLogData->Data.Str.Offset2 = 0;
  3865. ASSERT(pLogData->Size > pLogData->Used);
  3866. return pLogData->Used;
  3867. }
  3868. USHORT
  3869. UlpCompleteLogRecordNCSA(
  3870. IN OUT PUL_LOG_DATA_BUFFER pLogData,
  3871. IN PUL_URI_CACHE_ENTRY pUriEntry
  3872. )
  3873. {
  3874. PUL_INTERNAL_REQUEST pRequest;
  3875. PCHAR psz;
  3876. PCHAR pBuffer;
  3877. PCHAR pLine;
  3878. ULONG BytesWritten;
  3879. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  3880. ASSERT(pLogData->Data.Str.Format == HttpLoggingTypeNCSA);
  3881. BytesWritten = 0;
  3882. pRequest = pLogData->pRequest;
  3883. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  3884. psz = pLine = (PCHAR) pLogData->Line;
  3885. // If this is a cache hit restore the logging data in
  3886. // to the newly allocated log data buffer.
  3887. if (IS_PURE_CACHE_HIT(pUriEntry,pLogData))
  3888. {
  3889. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriEntry));
  3890. ASSERT(pLogData->Used == 0);
  3891. ASSERT(pLogData->Data.Str.Offset1 == 0);
  3892. // Client IP
  3893. psz = UlStrPrintIP(
  3894. psz,
  3895. pRequest->pHttpConn->pConnection->RemoteAddress,
  3896. pRequest->pHttpConn->pConnection->AddressType,
  3897. ' '
  3898. );
  3899. // Fixed dash
  3900. *psz++ = '-'; *psz++ = ' ';
  3901. // Authenticated users cannot be served from cache.
  3902. *psz++ = '-'; *psz++ = ' ';
  3903. // Mark the beginning of the date & time fields
  3904. pLogData->Data.Str.Offset1 = DIFF_USHORT(psz - pLine);
  3905. }
  3906. // [date:time GmtOffset] -> "[07/Jan/2000:00:02:23 -0800] "
  3907. // Restore the pointer to the reserved space first.
  3908. psz = &pLine[pLogData->Data.Str.Offset1];
  3909. *psz++ = '[';
  3910. UlGetDateTimeFields(
  3911. HttpLoggingTypeNCSA,
  3912. psz,
  3913. &BytesWritten,
  3914. NULL,
  3915. NULL
  3916. );
  3917. psz += BytesWritten; *psz++ = ':';
  3918. ASSERT(BytesWritten == 11);
  3919. UlGetDateTimeFields(
  3920. HttpLoggingTypeNCSA,
  3921. NULL,
  3922. NULL,
  3923. psz,
  3924. &BytesWritten
  3925. );
  3926. psz += BytesWritten; *psz++ = ' ';
  3927. ASSERT(BytesWritten == 8);
  3928. UlAcquirePushLockShared(&g_pUlNonpagedData->LogListPushLock);
  3929. psz = UlStrPrintStr(psz, g_GMTOffset,']');
  3930. UlReleasePushLockShared(&g_pUlNonpagedData->LogListPushLock);
  3931. *psz++ = ' ';
  3932. ASSERT(DIFF(psz - &pLine[pLogData->Data.Str.Offset1])
  3933. == NCSA_FIX_DATE_AND_TIME_FIELD_SIZE);
  3934. if (IS_PURE_CACHE_HIT(pUriEntry,pLogData))
  3935. {
  3936. ASSERT(pUriEntry->LogDataLength);
  3937. ASSERT(pUriEntry->pLogData);
  3938. RtlCopyMemory( psz,
  3939. pUriEntry->pLogData,
  3940. pUriEntry->LogDataLength
  3941. );
  3942. psz += pUriEntry->LogDataLength;
  3943. // Protocol Version
  3944. psz = UlCopyHttpVersion(psz, pRequest->Version, '\"');
  3945. *psz++ = ' ';
  3946. // Init the "Used" according to the cached data and date &
  3947. // time fields we have generated.
  3948. pLogData->Used = DIFF_USHORT(psz - pLine);
  3949. }
  3950. // Forward to the end.
  3951. pBuffer = psz = &pLine[pLogData->Used];
  3952. psz = UlStrPrintProtocolStatus(psz, pLogData->ProtocolStatus,' ');
  3953. psz = UlStrPrintUlonglong(psz, pRequest->BytesSent,'\r');
  3954. pLogData->Used =
  3955. (USHORT) (pLogData->Used + DIFF_USHORT(psz - pBuffer));
  3956. // \n\0
  3957. *psz++ = '\n'; *psz++ = ANSI_NULL;
  3958. pLogData->Used += 1;
  3959. // Cleanup the UsedOffsets otherwise length calculation will
  3960. // fail down below.
  3961. pLogData->Data.Str.Offset1 = pLogData->Data.Str.Offset2 = 0;
  3962. ASSERT(pLogData->Size > pLogData->Used);
  3963. return pLogData->Used;
  3964. }
  3965. USHORT
  3966. UlpCompleteLogRecordIIS(
  3967. IN OUT PUL_LOG_DATA_BUFFER pLogData,
  3968. IN PUL_URI_CACHE_ENTRY pUriEntry
  3969. )
  3970. {
  3971. PUL_INTERNAL_REQUEST pRequest;
  3972. PCHAR psz;
  3973. PCHAR pLine;
  3974. PCHAR pTemp;
  3975. ULONG BytesWritten;
  3976. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  3977. ASSERT(pLogData->Data.Str.Format == HttpLoggingTypeIIS);
  3978. BytesWritten = 0;
  3979. pRequest = pLogData->pRequest;
  3980. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  3981. psz = pLine = (PCHAR) pLogData->Line;
  3982. //
  3983. // Now we need to handle two different ways of completing this
  3984. // IIS log record; 1) Cache-miss, Build and Send Cache hit case,
  3985. // where the buffer interpreted as three different fragments.
  3986. // 2) Pure Cache-hit case where the buffer is used contiguously.
  3987. //
  3988. //
  3989. // Complete the first fragment
  3990. //
  3991. if (IS_PURE_CACHE_HIT(pUriEntry,pLogData))
  3992. {
  3993. ASSERT(pLogData->Used == 0);
  3994. ASSERT(pLogData->Data.Str.Offset1 == 0);
  3995. ASSERT(pLogData->Data.Str.Offset2 == 0);
  3996. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriEntry));
  3997. // Client IP
  3998. psz = UlStrPrintIP(
  3999. pLine,
  4000. pRequest->pHttpConn->pConnection->RemoteAddress,
  4001. pRequest->pHttpConn->pConnection->AddressType,
  4002. ','
  4003. );
  4004. *psz++ = ' ';
  4005. // Authenticated users cannot be served from cache.
  4006. *psz++ = '-'; *psz++ = ','; *psz++ = ' ';
  4007. }
  4008. else
  4009. {
  4010. ASSERT(pLogData->Data.Str.Offset1);
  4011. psz = pLine + pLogData->Data.Str.Offset1;
  4012. }
  4013. pTemp = psz;
  4014. UlGetDateTimeFields(
  4015. HttpLoggingTypeIIS,
  4016. psz,
  4017. &BytesWritten,
  4018. NULL,
  4019. NULL
  4020. );
  4021. psz += BytesWritten; *psz++ = ','; *psz++ = ' ';
  4022. UlGetDateTimeFields(
  4023. HttpLoggingTypeIIS,
  4024. NULL,
  4025. NULL,
  4026. psz,
  4027. &BytesWritten
  4028. );
  4029. psz += BytesWritten; *psz++ = ','; *psz++ = ' ';
  4030. ASSERT(DIFF(psz - pTemp) <= IIS_MAX_DATE_AND_TIME_FIELD_SIZE);
  4031. pLogData->Data.Str.Offset1 = DIFF_USHORT(psz - pLine);
  4032. //
  4033. // Complete the second fragment.
  4034. //
  4035. if (IS_PURE_CACHE_HIT(pUriEntry,pLogData))
  4036. {
  4037. ASSERT(pUriEntry->pLogData);
  4038. ASSERT(pUriEntry->LogDataLength);
  4039. ASSERT(pUriEntry->LogDataLength ==
  4040. (ULONG) (pUriEntry->UsedOffset1 +
  4041. pUriEntry->UsedOffset2)
  4042. );
  4043. // Remember the fragment start.
  4044. pTemp = psz;
  4045. // Restore it from the cache
  4046. RtlCopyMemory( psz,
  4047. pUriEntry->pLogData,
  4048. pUriEntry->UsedOffset1
  4049. );
  4050. psz += pUriEntry->UsedOffset1;
  4051. }
  4052. else
  4053. {
  4054. // Fragmented. And 2nd frag cannot be empty.
  4055. ASSERT(pLogData->Data.Str.Offset2);
  4056. // Remember the fragment start.
  4057. pTemp = pLine
  4058. + IIS_LOG_LINE_SECOND_FRAGMENT_OFFSET;
  4059. // Jump to the end of the 2nd frag.
  4060. psz = pTemp
  4061. + pLogData->Data.Str.Offset2;
  4062. }
  4063. psz = UlpCopyTimeStamp(psz, pRequest, ',');
  4064. *psz++ = ' ';
  4065. psz = UlStrPrintUlonglong(psz, pRequest->BytesReceived,',');
  4066. *psz++ = ' ';
  4067. psz = UlStrPrintUlonglong(psz, pRequest->BytesSent,',');
  4068. *psz++ = ' ';
  4069. psz = UlStrPrintProtocolStatus(psz,pLogData->ProtocolStatus,',');
  4070. *psz++ = ' ';
  4071. psz = UlStrPrintUlong(psz, pLogData->Win32Status,',');
  4072. *psz++ = ' ';
  4073. pLogData->Data.Str.Offset2 = DIFF_USHORT(psz - pTemp);
  4074. //
  4075. // Complete the third fragment.
  4076. //
  4077. if (IS_PURE_CACHE_HIT(pUriEntry,pLogData))
  4078. {
  4079. RtlCopyMemory( psz,
  4080. &pUriEntry->pLogData[pUriEntry->UsedOffset1],
  4081. pUriEntry->UsedOffset2
  4082. );
  4083. // Total record size is whatever we have copied before this
  4084. // last copy plus the size of the last copy.
  4085. pLogData->Used = (USHORT)
  4086. (DIFF_USHORT(psz - pLine) + pUriEntry->UsedOffset2);
  4087. // Reset the UsedOffset1 & 2 to zero to tell that the log line
  4088. // is not fragmented anymore but a complete line.
  4089. pLogData->Data.Str.Offset1 = pLogData->Data.Str.Offset2 = 0;
  4090. }
  4091. else
  4092. {
  4093. // It is already there and its size is stored in "Used".
  4094. ASSERT(pLogData->Used);
  4095. }
  4096. ASSERT(pLogData->Size > (pLogData->Data.Str.Offset1 +
  4097. pLogData->Data.Str.Offset2 +
  4098. pLogData->Used));
  4099. //
  4100. // Return the complete size of the IIS log record.
  4101. //
  4102. return (pLogData->Data.Str.Offset1 +
  4103. pLogData->Data.Str.Offset2 +
  4104. pLogData->Used);
  4105. }
  4106. /***************************************************************************++
  4107. Routine Description:
  4108. UlLogHttpHit :
  4109. This function ( or its cache pair ) gets called everytime a log hit
  4110. happens. Just before completing the SendResponse request to the user.
  4111. The most likely places for calling this API or its pair for cache
  4112. is just before the send completion when we were about the destroy
  4113. send trackers.
  4114. Means:
  4115. 1. UlpCompleteSendRequestWorker for ORDINARY hits; before destroying
  4116. the PUL_CHUNK_TRACKER for send operation.
  4117. 2. UlpCompleteSendCacheEntryWorker for both types of CACHE hits
  4118. (cache build&send or just pure cache hit) before destroying the
  4119. the PUL_FULL_TRACKER for cache send operation.
  4120. 3. Fast I/O path.
  4121. This function requires Request & Response structures ( whereas its
  4122. cache pair only requires the Request ) to successfully generate the
  4123. the log fields and even for referencing to the right log configuration
  4124. settings for this site ( thru pRequest's pConfigInfo pointer ).
  4125. Arguments:
  4126. pLogBuffer - Half baked log data allocated at the capture time.
  4127. >MUST< be cleaned up by the caller.
  4128. --***************************************************************************/
  4129. NTSTATUS
  4130. UlLogHttpHit(
  4131. IN PUL_LOG_DATA_BUFFER pLogData
  4132. )
  4133. {
  4134. NTSTATUS Status;
  4135. PUL_CONFIG_GROUP_OBJECT pConfigGroup;
  4136. PUL_INTERNAL_REQUEST pRequest;
  4137. PUL_LOG_FILE_ENTRY pEntry;
  4138. USHORT LogSize;
  4139. //
  4140. // A LOT of sanity checks.
  4141. //
  4142. PAGED_CODE();
  4143. Status = STATUS_SUCCESS;
  4144. UlTrace(LOGGING, ("Http!UlLogHttpHit: pLogData %p\n", pLogData));
  4145. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  4146. pRequest = pLogData->pRequest;
  4147. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4148. //
  4149. // If logging is disabled or log settings don't
  4150. // exist then do not proceed. Just exit out.
  4151. //
  4152. if (pRequest->ConfigInfo.pLoggingConfig == NULL ||
  4153. IS_LOGGING_DISABLED(pRequest->ConfigInfo.pLoggingConfig)
  4154. )
  4155. {
  4156. return STATUS_SUCCESS;
  4157. }
  4158. pConfigGroup = pRequest->ConfigInfo.pLoggingConfig;
  4159. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  4160. #ifdef IMPLEMENT_SELECTIVE_LOGGING
  4161. //
  4162. // See if the selective logging is turned on. If it is on and
  4163. // if the request's response code does not match, do not log
  4164. // this request.
  4165. //
  4166. if (!UlpIsRequestSelected(pConfigGroup,pLogData->ProtocolStatus))
  4167. {
  4168. return STATUS_SUCCESS;
  4169. }
  4170. #endif
  4171. //
  4172. // Generate the remaining log fields.
  4173. //
  4174. switch(pLogData->Data.Str.Format)
  4175. {
  4176. case HttpLoggingTypeW3C:
  4177. {
  4178. LogSize = UlpCompleteLogRecordW3C(pLogData, NULL);
  4179. if (LogSize == 0)
  4180. {
  4181. return STATUS_SUCCESS; // No log fields, nothing to log
  4182. }
  4183. }
  4184. break;
  4185. case HttpLoggingTypeNCSA:
  4186. {
  4187. LogSize = UlpCompleteLogRecordNCSA(pLogData, NULL);
  4188. ASSERT(LogSize > 0);
  4189. }
  4190. break;
  4191. case HttpLoggingTypeIIS:
  4192. {
  4193. LogSize = UlpCompleteLogRecordIIS(pLogData, NULL);
  4194. ASSERT(LogSize > 0);
  4195. }
  4196. break;
  4197. default:
  4198. {
  4199. ASSERT(!"Unknown Log Format Type\n");
  4200. return STATUS_INVALID_PARAMETER;
  4201. }
  4202. }
  4203. //
  4204. // Finally this log line is ready to rock. Lets write it out.
  4205. //
  4206. UlAcquirePushLockShared(&g_pUlNonpagedData->LogListPushLock);
  4207. //
  4208. // We might have null pEntry pointer if the allocation failed
  4209. // because of a lack of resources. This case should be handled
  4210. // by minute timer.
  4211. //
  4212. pEntry = pConfigGroup->pLogFileEntry;
  4213. if (pEntry == NULL)
  4214. {
  4215. UlTrace(LOGGING,("Http!UlLogHttpHit: Null logfile entry !\n"));
  4216. UlReleasePushLockShared(&g_pUlNonpagedData->LogListPushLock);
  4217. return STATUS_INVALID_PARAMETER;
  4218. }
  4219. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  4220. Status = UlpCheckAndWrite(pEntry, pConfigGroup, pLogData);
  4221. if (!NT_SUCCESS(Status))
  4222. {
  4223. UlTrace(LOGGING, ("Http!UlLogHttpHit: entry %p, failure %08lx \n",
  4224. pEntry,
  4225. Status
  4226. ));
  4227. }
  4228. UlReleasePushLockShared(&g_pUlNonpagedData->LogListPushLock);
  4229. return Status;
  4230. }
  4231. /***************************************************************************++
  4232. Routine Description:
  4233. If the tracker doesn't supply a pLogData, it pre-calculates the max size
  4234. and then completes the log record. Finally it logs the "complete" record
  4235. out to the log file buffer.
  4236. It also assumes the responsibility of cleaning up the pLogData,regardless
  4237. of the fact that it is provided by pTracker or not.
  4238. Arguments:
  4239. pTracker - Supplies the tracker to complete.
  4240. --***************************************************************************/
  4241. NTSTATUS
  4242. UlLogHttpCacheHit(
  4243. IN OUT PUL_FULL_TRACKER pTracker
  4244. )
  4245. {
  4246. NTSTATUS Status;
  4247. PUL_LOG_DATA_BUFFER pLogData;
  4248. PUL_LOG_FILE_ENTRY pEntry;
  4249. ULONG NewLength;
  4250. PUL_INTERNAL_REQUEST pRequest;
  4251. PUL_URI_CACHE_ENTRY pUriEntry;
  4252. PUL_CONFIG_GROUP_OBJECT pConfigGroup;
  4253. USHORT LogSize;
  4254. //
  4255. // A Lot of sanity checks.
  4256. //
  4257. PAGED_CODE();
  4258. ASSERT(pTracker);
  4259. ASSERT(IS_VALID_FULL_TRACKER(pTracker));
  4260. Status = STATUS_SUCCESS;
  4261. pRequest = pTracker->pRequest;
  4262. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4263. pUriEntry = pTracker->pUriEntry;
  4264. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriEntry));
  4265. //
  4266. // If the tracker has already a log buffer allocated , carry
  4267. // over the ownership of that pLogData. This would happen
  4268. // for build and send type of cache hits.
  4269. //
  4270. pLogData = pTracker->pLogData;
  4271. pTracker->pLogData = NULL;
  4272. //
  4273. // If logging is disabled or log settings don't exist, then
  4274. // just exit out. However goto cleanup path just in case we
  4275. // have acquired a pLogData from the tracker above.
  4276. //
  4277. pConfigGroup = pUriEntry->ConfigInfo.pLoggingConfig;
  4278. if (pConfigGroup == NULL || IS_LOGGING_DISABLED(pConfigGroup))
  4279. {
  4280. goto end;
  4281. }
  4282. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  4283. #ifdef IMPLEMENT_SELECTIVE_LOGGING
  4284. //
  4285. // See if the selective logging is turned on. If it is on and
  4286. // if the request's response code does not match, do not log
  4287. // this request.
  4288. //
  4289. if (!UlpIsRequestSelected(pConfigGroup,pUriEntry->StatusCode))
  4290. {
  4291. goto end;
  4292. }
  4293. #endif
  4294. //
  4295. // If this was a pure cache hit, we will need to allocate a new
  4296. // log data buffer here.
  4297. //
  4298. if (pLogData)
  4299. {
  4300. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  4301. ASSERT(pLogData->Flags.CacheAndSendResponse == 1);
  4302. }
  4303. else
  4304. {
  4305. switch(pConfigGroup->LoggingConfig.LogFormat)
  4306. {
  4307. case HttpLoggingTypeW3C:
  4308. NewLength = UlpGetCacheHitLogLineSizeForW3C(
  4309. pConfigGroup->LoggingConfig.LogExtFileFlags,
  4310. pRequest,
  4311. pUriEntry->LogDataLength
  4312. );
  4313. ASSERT(NewLength < MAX_LOG_RECORD_ALLOCATION_LENGTH);
  4314. break;
  4315. case HttpLoggingTypeNCSA:
  4316. NewLength = MAX_NCSA_CACHE_FIELD_OVERHEAD
  4317. + pUriEntry->LogDataLength;
  4318. break;
  4319. case HttpLoggingTypeIIS:
  4320. NewLength = MAX_IIS_CACHE_FIELD_OVERHEAD
  4321. + pUriEntry->LogDataLength;
  4322. break;
  4323. default:
  4324. ASSERT(!"Unknown Log Format.\n");
  4325. Status = STATUS_INVALID_DEVICE_STATE;
  4326. goto end;
  4327. }
  4328. pLogData = UlpAllocateLogDataBuffer(
  4329. NewLength,
  4330. pRequest,
  4331. pConfigGroup
  4332. );
  4333. if (!pLogData)
  4334. {
  4335. Status = STATUS_INSUFFICIENT_RESOURCES;
  4336. goto end;
  4337. }
  4338. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  4339. }
  4340. UlTrace(LOGGING,("Http!UlLogHttpCacheHit: pLogData %p\n",pLogData));
  4341. pLogData->ProtocolStatus = pUriEntry->StatusCode;
  4342. pLogData->SubStatus = 0;
  4343. LOG_SET_WIN32STATUS(
  4344. pLogData,
  4345. pTracker->IoStatus.Status
  4346. );
  4347. // TODO: For cache hits send bytes info is coming from the tracker.
  4348. // TODO: Need to update pRequest->BytesSent for cache hits as well.
  4349. pRequest->BytesSent = pTracker->IoStatus.Information;
  4350. switch(pLogData->Data.Str.Format)
  4351. {
  4352. case HttpLoggingTypeW3C:
  4353. {
  4354. LogSize = UlpCompleteLogRecordW3C(pLogData, pUriEntry);
  4355. if (LogSize == 0)
  4356. {
  4357. goto end; // No log fields, nothing to log
  4358. }
  4359. }
  4360. break;
  4361. case HttpLoggingTypeNCSA:
  4362. {
  4363. LogSize = UlpCompleteLogRecordNCSA(pLogData, pUriEntry);
  4364. ASSERT(LogSize);
  4365. }
  4366. break;
  4367. case HttpLoggingTypeIIS:
  4368. {
  4369. LogSize = UlpCompleteLogRecordIIS(pLogData, pUriEntry);
  4370. ASSERT(LogSize);
  4371. }
  4372. break;
  4373. default:
  4374. ASSERT(!"Unknown Log Format !");
  4375. goto end;
  4376. }
  4377. //
  4378. // Finally this log line is ready to rock. Let's log it out.
  4379. //
  4380. UlAcquirePushLockShared(&g_pUlNonpagedData->LogListPushLock);
  4381. pEntry = pConfigGroup->pLogFileEntry;
  4382. if (pEntry == NULL)
  4383. {
  4384. UlTrace(LOGGING,
  4385. ("Http!UlpLogHttpcacheHit: pEntry is NULL !"));
  4386. UlReleasePushLockShared(
  4387. &g_pUlNonpagedData->LogListPushLock);
  4388. goto end;
  4389. }
  4390. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  4391. Status = UlpCheckAndWrite(pEntry, pConfigGroup, pLogData);
  4392. if (!NT_SUCCESS(Status))
  4393. {
  4394. UlTrace(LOGGING,
  4395. ("Http!UlpLogHttpcacheHit: pEntry %p, Failure %08lx \n",
  4396. pEntry,
  4397. Status
  4398. ));
  4399. }
  4400. UlReleasePushLockShared(&g_pUlNonpagedData->LogListPushLock);
  4401. end:
  4402. if (pLogData)
  4403. {
  4404. UlDestroyLogDataBuffer(pLogData);
  4405. }
  4406. return Status;
  4407. }