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.

1413 lines
39 KiB

  1. //=======================================================================
  2. //
  3. // Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
  4. //
  5. // File: IULogger.cpp: implementation of the CIULogger class.
  6. //
  7. // Description:
  8. //
  9. // See IULogger.h
  10. //
  11. //=======================================================================
  12. #if defined(DBG)
  13. #include <windows.h>
  14. #include <ole2.h>
  15. #include <tchar.h>
  16. #include <MemUtil.h>
  17. #include <fileutil.h>
  18. #include <Logging.h>
  19. #include <strsafe.h>
  20. //
  21. // declare constants used to control log exclusions
  22. //
  23. const DWORD LOG_BLOCK = 0x00000001; // log function/block in/out
  24. const DWORD LOG_XML_DETAIL = 0x00000002; // log detailed XML operation
  25. const DWORD LOG_INTERNET = 0x00000004; // log action related to Internet activities, e.g., downld
  26. const DWORD LOG_SOFTWARE = 0x00000008; // log details about software detection/installation
  27. const DWORD LOG_DRIVER = 0x00000010; // log actions related to driver detection/installation
  28. const DWORD LOG_TRUST = 0x00000020; // log actions related to wintrust checking
  29. const DWORD LOG_DOWNLOAD = 0x00000040; // log actions related to download
  30. const DWORD LOG_XML_BSTR_DETAIL = 0x00000080; // log XML BSTRs
  31. const DWORD LOG_ERROR = 0x00008000; // you can not exclude this type of logs from output
  32. const DWORD LOG_ALL = 0xFFFFFFFF; // default, all above
  33. //
  34. // const for longest line of XML we will output
  35. //
  36. const DWORD LOG_XML_BUFF_LEN = 128;
  37. //
  38. // const for specifying the intent array size increament.
  39. // each element in array holds indent data for one thread
  40. //
  41. const int c_IndentArrayChunk = 16;
  42. //
  43. // define the log header format
  44. //
  45. // It is constructed as: <date> <time> <thread id>
  46. //
  47. const TCHAR szLogHeaderFmt[] = _T("yyyy/mm/dd hh:nn:ss:sss xxxxxxxx ");
  48. //
  49. // initialization of static members
  50. //
  51. int CIULogger::m_Size = 0;
  52. int CIULogger::m_siIndentStep = 0; // init to use tab char
  53. CIULogger::_THREAD_INDENT* CIULogger::m_psIndent = NULL;
  54. DWORD CIULogger::m_sdwLogMask = LOG_ALL;
  55. HANDLE CIULogger::m_shFile = INVALID_HANDLE_VALUE;
  56. bool CIULogger::m_fLogDebugMsg = false;
  57. bool CIULogger::m_fLogFile = false;
  58. bool CIULogger::m_fLogUsable = false;
  59. HANDLE CIULogger::m_hMutex = NULL;
  60. int CIULogger::m_cFailedWaits = 0;
  61. int CIULogger::m_fFlushEveryTime = FALSE;
  62. //
  63. // Defines for Mutex (borrowed from freelog)
  64. //
  65. // NOTE: globals and statics are per-module (e.g. iuctl, iuengine), but Mutex is per-processes
  66. // due to the name being constructed from the log file name (contains process ID).
  67. //
  68. #define MUTEX_TIMEOUT 1000 // Don't wait more than 1 second to write to logfile
  69. #define MAX_MUTEX_WAITS 4 // Don't keep trying after this many failures
  70. //
  71. // global variable
  72. //
  73. //
  74. // reference count to control log file open/close
  75. //
  76. LONG g_RefCount = 0;
  77. //
  78. // critical sectoin handling multi-threading
  79. // access of indent array case
  80. //
  81. //
  82. CRITICAL_SECTION g_LogCs;
  83. //
  84. // we need to declare a global object so refcount wont't
  85. // be zero, otherwise in multi-threading mode ref count
  86. // can be fooled and AV when one object thinks m_psIdent not NULL
  87. // but another object in another (parent) thread freed m_psIndent
  88. // in destructor (only if parent thread quits)
  89. //
  90. CIULogger g_DummyLogObj(NULL);
  91. //////////////////////////////////////////////////////////////////////
  92. // Construction/Destruction
  93. //////////////////////////////////////////////////////////////////////
  94. CIULogger::CIULogger(char* szBlockName)
  95. : m_Index(-1), m_LineNum(0)
  96. {
  97. if (0 == g_RefCount)
  98. {
  99. //
  100. // This must be the g_DummyLogObj (or another global
  101. // instance) during initialization of globals. Since
  102. // this is the first instance created, we must init CS
  103. //
  104. InitializeCriticalSection(&g_LogCs);
  105. }
  106. //
  107. // Protect the statics while in the ctor
  108. //
  109. EnterCriticalSection(&g_LogCs);
  110. m_dwTickBegin = GetTickCount();
  111. m_dwThreadId = GetCurrentThreadId();
  112. m_fProcessLog = (NULL == szBlockName);
  113. ZeroMemory(m_szBlockName, sizeof(m_szBlockName));
  114. g_RefCount++;
  115. if (1 == g_RefCount)
  116. {
  117. //
  118. // this is the first time to call this class, we need to
  119. // find out whether we should log and where to log to
  120. //
  121. ReadRegistrySettings();
  122. //
  123. // allocate memory for thread indent array initially
  124. //
  125. SetIndent(0);
  126. //
  127. // if the indent level is not negative, then it's okay to log
  128. //
  129. m_fLogFile = (INVALID_HANDLE_VALUE != m_shFile);
  130. m_fLogUsable = (m_fLogFile || m_fLogDebugMsg) && (NULL != m_psIndent);
  131. }
  132. if (m_fLogUsable)
  133. {
  134. if (!m_fProcessLog)
  135. {
  136. //
  137. // this is probably a new thread, so we need to find the index
  138. // for this thread.
  139. //
  140. SetIndent(0);
  141. //
  142. // do block logging, if permitted
  143. //
  144. if (0x0 != (m_sdwLogMask & LOG_BLOCK) && szBlockName && _T('\0') != szBlockName[0])
  145. {
  146. StringCchCopyA(m_szBlockName, ARRAYSIZE(m_szBlockName), szBlockName);
  147. USES_IU_CONVERSION;
  148. char szOut[sizeof(m_szBlockName) + 10];
  149. //
  150. // Implicit "Enter " before block name to save log space
  151. //
  152. if (SUCCEEDED(StringCchPrintfA(szOut, ARRAYSIZE(szOut), "%hs\r\n", szBlockName)))
  153. {
  154. _LogOut(A2T(szOut));
  155. }
  156. }
  157. SetIndent(+1);
  158. }
  159. }
  160. LeaveCriticalSection(&g_LogCs);
  161. }
  162. CIULogger::~CIULogger()
  163. {
  164. EnterCriticalSection(&g_LogCs);
  165. if (m_fLogUsable)
  166. {
  167. //
  168. // decrease the indent level by 1 if we increased indent
  169. //
  170. if (!m_fProcessLog)
  171. {
  172. SetIndent(-1);
  173. }
  174. //
  175. // write log file for exiting block, if allowed and block name exists
  176. //
  177. if (0x0 != (m_sdwLogMask & LOG_BLOCK) && _T('\0') != m_szBlockName[0])
  178. {
  179. USES_IU_CONVERSION;
  180. char szOut[1024];
  181. //
  182. // "Exit " shortened to "~" to save log space
  183. //
  184. if (SUCCEEDED(StringCchPrintfA(szOut, ARRAYSIZE(szOut), "~%hs, %d msec\r\n", m_szBlockName, GetTickCount() - m_dwTickBegin)))
  185. {
  186. _LogOut(A2T(szOut));
  187. }
  188. }
  189. }
  190. //
  191. // reduce reference cnt
  192. //
  193. g_RefCount--;
  194. //
  195. // g_RefCount will go to zero before leaving dtor if this is the last global instance
  196. // in this module
  197. //
  198. if (0 == g_RefCount)
  199. {
  200. //
  201. // close file if the file is open
  202. //
  203. if (m_fLogFile && INVALID_HANDLE_VALUE != m_shFile) // redundent?
  204. {
  205. CloseHandle(m_shFile);
  206. m_shFile = INVALID_HANDLE_VALUE;
  207. }
  208. if(NULL != m_hMutex)
  209. {
  210. CloseHandle(m_hMutex);
  211. }
  212. //
  213. // free memory of indent array
  214. //
  215. if (NULL != m_psIndent)
  216. {
  217. HeapFree(GetProcessHeap(), 0, m_psIndent);
  218. m_psIndent = NULL;
  219. }
  220. }
  221. LeaveCriticalSection(&g_LogCs);
  222. //
  223. // This is the last global instance (probably g_DummyLogObj) and is
  224. // being destructed before the DLL unloads
  225. //
  226. if (0 == g_RefCount)
  227. {
  228. DeleteCriticalSection(&g_LogCs);
  229. }
  230. }
  231. //
  232. // Mutex stuff borrowed from freelog
  233. // fixcode: This should not be required here since chk logging is per process only
  234. BOOL CIULogger::AcquireMutex()
  235. {
  236. // In rare case where mutex not created, we allow file operations
  237. // with no synchronization
  238. if (m_hMutex == NULL)
  239. return TRUE;
  240. // Don't keep waiting if we've been blocked in the past
  241. if (m_cFailedWaits >= MAX_MUTEX_WAITS)
  242. return FALSE;
  243. BOOL fResult = TRUE;
  244. if (WaitForSingleObject(m_hMutex, MUTEX_TIMEOUT) != WAIT_OBJECT_0)
  245. {
  246. fResult = FALSE;
  247. m_cFailedWaits++;
  248. }
  249. return fResult;
  250. }
  251. void CIULogger::ReleaseMutex()
  252. {
  253. if (m_hMutex != NULL) // Note: AcquireMutex succeeds even if m_hMutex is NULL
  254. {
  255. ::ReleaseMutex(m_hMutex);
  256. }
  257. }
  258. ////////////////////////////////////////////////////////////////////////
  259. //
  260. // log with no flag, so can not be removed by excluding directives
  261. //
  262. ////////////////////////////////////////////////////////////////////////
  263. void CIULogger::Log(LPCTSTR szLogFormat, ...)
  264. {
  265. if (m_fLogUsable)
  266. {
  267. USES_IU_CONVERSION;
  268. va_list va;
  269. va_start (va, szLogFormat);
  270. _Log(LOG_ALL, szLogFormat, va);
  271. va_end (va);
  272. }
  273. }
  274. ////////////////////////////////////////////////////////////////////////
  275. //
  276. // log error, so can not be removed by excluding directives
  277. //
  278. ////////////////////////////////////////////////////////////////////////
  279. void CIULogger::LogError(LPCTSTR szLogFormat, ...)
  280. {
  281. if (m_fLogUsable)
  282. {
  283. va_list va;
  284. va_start (va, szLogFormat);
  285. _Log(LOG_ERROR, szLogFormat, va);
  286. va_end (va);
  287. }
  288. }
  289. ////////////////////////////////////////////////////////////////////////
  290. //
  291. // Helper for LogErrorMsg and LogInfoMsg (which supply message to prepend)
  292. //
  293. ////////////////////////////////////////////////////////////////////////
  294. void CIULogger::_LogFormattedMsg(DWORD dwErrCode, LPCTSTR pszErrorInfo)
  295. {
  296. if (m_fLogUsable)
  297. {
  298. //
  299. // try to retrive system msg
  300. //
  301. LPTSTR lpszBuffer = NULL, lpszLogMsg = NULL;
  302. LPVOID lpMsg = NULL;
  303. FormatMessage(
  304. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  305. FORMAT_MESSAGE_FROM_SYSTEM,
  306. NULL, // no source, use system msg
  307. dwErrCode,
  308. MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
  309. (LPTSTR)&lpMsg,
  310. 0,
  311. NULL);
  312. if (NULL == lpMsg)
  313. {
  314. //
  315. // if we failed to get the msg, then output generic
  316. // error/info log
  317. //
  318. LogError(_T("Unknown %s Line %d: 0x%08x\n"), pszErrorInfo, m_LineNum, dwErrCode);
  319. }
  320. else
  321. {
  322. lpszBuffer = (LPTSTR) lpMsg;
  323. int nLen = lstrlen(lpszBuffer);
  324. lpszLogMsg = (LPTSTR) LocalAlloc(0, (nLen + 128) * sizeof(TCHAR));
  325. if (NULL != lpszLogMsg)
  326. {
  327. //
  328. // insert Error/Info keyword
  329. //
  330. if (FAILED(StringCchPrintf(lpszLogMsg, ARRAYSIZE(lpszLogMsg), _T("%s Line %d: 0x%08x: %s"), pszErrorInfo, m_LineNum, dwErrCode, lpszBuffer)))
  331. {
  332. // Couldn't build the right string, so just output the system msg
  333. LocalFree(lpszLogMsg);
  334. lpszLogMsg = lpszBuffer;
  335. }
  336. }
  337. else
  338. {
  339. //
  340. // failed to get buffer? unlikely, anyway,
  341. // we have no option but just output the system msg
  342. //
  343. lpszLogMsg = lpszBuffer;
  344. }
  345. //
  346. // write log out
  347. //
  348. _LogOut(lpszLogMsg);
  349. //
  350. // clean up buffer
  351. //
  352. if (lpszLogMsg != lpszBuffer)
  353. {
  354. LocalFree(lpszLogMsg);
  355. }
  356. LocalFree(lpszBuffer);
  357. }
  358. }
  359. }
  360. ////////////////////////////////////////////////////////////////////////
  361. //
  362. // similar to LogError, but try to log the system msg based
  363. // on the error code. If the sysmsg not avail, log
  364. // "Unknown error with error code 0x%08x"
  365. //
  366. ////////////////////////////////////////////////////////////////////////
  367. void CIULogger::LogErrorMsg(DWORD dwErrCode)
  368. {
  369. _LogFormattedMsg(dwErrCode, _T("Error"));
  370. }
  371. ////////////////////////////////////////////////////////////////////////
  372. //
  373. // similar to LogErrorMsg but prepends with "Info" rather than "Error"
  374. //
  375. ////////////////////////////////////////////////////////////////////////
  376. void CIULogger::LogInfoMsg(DWORD dwErrCode)
  377. {
  378. _LogFormattedMsg(dwErrCode, _T("Info"));
  379. }
  380. ////////////////////////////////////////////////////////////////////////
  381. //
  382. // log with type INTERNET, this function will do nothing
  383. // if the Internet exclusion directive is detected from reg
  384. //
  385. ////////////////////////////////////////////////////////////////////////
  386. void CIULogger::LogInternet(LPCTSTR szLogFormat, ...)
  387. {
  388. if (m_fLogUsable)
  389. {
  390. va_list va;
  391. va_start (va, szLogFormat);
  392. _Log(LOG_INTERNET, szLogFormat, va);
  393. va_end (va);
  394. }
  395. }
  396. ////////////////////////////////////////////////////////////////////////
  397. //
  398. // log with type XML, this function will do nothing
  399. // if the XML exclusion directive is detected from reg
  400. //
  401. ////////////////////////////////////////////////////////////////////////
  402. void CIULogger::LogXML(LPCTSTR szLogFormat, ...)
  403. {
  404. if (m_fLogUsable)
  405. {
  406. va_list va;
  407. va_start (va, szLogFormat);
  408. _Log(LOG_XML_DETAIL, szLogFormat, va);
  409. va_end (va);
  410. }
  411. }
  412. void CIULogger::_NukeCrLf(LPTSTR pszBuffer)
  413. {
  414. while (*pszBuffer)
  415. {
  416. if (_T('\r') == *pszBuffer || _T('\n') == *pszBuffer)
  417. {
  418. //
  419. // Overwrite <CR> and <LF> with space
  420. //
  421. *pszBuffer = _T(' ');
  422. }
  423. pszBuffer++;
  424. }
  425. }
  426. ////////////////////////////////////////////////////////////////////////
  427. //
  428. // log BSTR containing valid XML. This gets around length limitations
  429. // of LogOutput and attempts to break lines following ">". This
  430. // output is sent for both fre and chk builds unless excluded from reg.
  431. //
  432. ////////////////////////////////////////////////////////////////////////
  433. void CIULogger::LogXmlBSTR(BSTR bstrXML)
  434. {
  435. USES_IU_CONVERSION;
  436. LPTSTR pszLine;
  437. LPTSTR pszTemp;
  438. LPTSTR pszStop;
  439. LPTSTR pszLastGT;
  440. TCHAR szXmlBuff[LOG_XML_BUFF_LEN];
  441. HRESULT hr;
  442. if (NULL == bstrXML)
  443. {
  444. return;
  445. }
  446. if (m_fLogUsable && (m_sdwLogMask & LOG_XML_BSTR_DETAIL) )
  447. {
  448. #if !(defined(UNICODE) || defined(_UNICODE))
  449. DWORD dwANSIBuffLen = SysStringLen(bstrXML) + 1;
  450. LPSTR pszANSIBuff = (LPSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwANSIBuffLen);
  451. if (NULL == pszANSIBuff)
  452. {
  453. //
  454. // We're toast - just return without logging
  455. //
  456. return;
  457. }
  458. LPTSTR pTempTchar = OLE2T(bstrXML);
  459. if (NULL != pTempTchar)
  460. {
  461. if (FAILED(StringCchCopyA(pszANSIBuff, dwANSIBuffLen, pTempTchar)))
  462. {
  463. goto done;
  464. }
  465. }
  466. pszLine = pszANSIBuff;
  467. #else
  468. pszLine = bstrXML;
  469. #endif
  470. while (*pszLine)
  471. {
  472. //
  473. // Skip <CR> & <LF> chars
  474. //
  475. while (_T('\r') == *pszLine || _T('\n') == *pszLine)
  476. {
  477. pszLine++;
  478. if (NULL == *pszLine)
  479. {
  480. break;
  481. }
  482. }
  483. if (NULL == *pszLine)
  484. {
  485. break;
  486. }
  487. pszTemp = pszLine;
  488. pszStop = pszLine + LOG_XML_BUFF_LEN - 1;
  489. pszLastGT = NULL;
  490. //
  491. // Try to find the last '>' char that will fit in buffer
  492. //
  493. while (*pszTemp && pszTemp < pszStop)
  494. {
  495. if (_T('>') == *pszTemp)
  496. {
  497. pszLastGT = pszTemp;
  498. }
  499. pszTemp++;
  500. }
  501. if (pszLastGT)
  502. {
  503. //
  504. // Break the line at the last '>' that fits into LOG_XML_BUFF_LEN
  505. //
  506. hr = StringCchCopy(szXmlBuff, (int) (pszLastGT - pszLine) + 2, pszLine);
  507. //
  508. // STRSAFE_E_INSUFFICIENT_BUFFER is returned if the string is truncated.
  509. // This is normal since we are just copying a portion of the XML at
  510. // a time so it won't be too long to log.
  511. //
  512. if (SUCCEEDED(hr) || STRSAFE_E_INSUFFICIENT_BUFFER == hr)
  513. {
  514. _NukeCrLf(szXmlBuff);
  515. _LogOut(szXmlBuff);
  516. pszLine = pszLastGT + 1;
  517. }
  518. else
  519. {
  520. break;
  521. }
  522. }
  523. else if (*pszTemp)
  524. {
  525. //
  526. // We're forced to break the line at LOG_XML_BUFF_LEN with no '>' in range
  527. //
  528. hr = StringCchCopy(szXmlBuff, LOG_XML_BUFF_LEN, pszLine);
  529. if (SUCCEEDED(hr) || STRSAFE_E_INSUFFICIENT_BUFFER == hr)
  530. {
  531. _NukeCrLf(szXmlBuff);
  532. _LogOut(szXmlBuff);
  533. pszLine += LOG_XML_BUFF_LEN -1;
  534. }
  535. else
  536. {
  537. break;
  538. }
  539. }
  540. else
  541. {
  542. //
  543. // Output any leftover XML to end of BSTR
  544. //
  545. _NukeCrLf(pszLine);
  546. _LogOut(pszLine);
  547. //
  548. // Set to end of BSTR so we bust out of outer while
  549. //
  550. pszLine += lstrlen(pszLine);
  551. }
  552. }
  553. #if !(defined(UNICODE) || defined(_UNICODE))
  554. done:
  555. if (pszANSIBuff)
  556. {
  557. HeapFree(GetProcessHeap(), 0, pszANSIBuff);
  558. pszANSIBuff = NULL;
  559. }
  560. #endif
  561. }
  562. }
  563. ////////////////////////////////////////////////////////////////////////
  564. //
  565. // log with type SOFTWARE, this function will do nothing
  566. // if the SOFTWARE exclusion directive is detected from reg
  567. //
  568. ////////////////////////////////////////////////////////////////////////
  569. void CIULogger::LogSoftware(LPCTSTR szLogFormat, ...)
  570. {
  571. USES_IU_CONVERSION;
  572. if (m_fLogUsable)
  573. {
  574. va_list va;
  575. va_start (va, szLogFormat);
  576. _Log(LOG_SOFTWARE, szLogFormat, va);
  577. va_end (va);
  578. }
  579. }
  580. ////////////////////////////////////////////////////////////////////////
  581. //
  582. // log with type DOWNLOAD, this function will do nothing
  583. // if the LogDownload exclusion directive is detected from reg
  584. //
  585. ////////////////////////////////////////////////////////////////////////
  586. void CIULogger::LogDownload(LPCTSTR szLogFormat, ...)
  587. {
  588. USES_IU_CONVERSION;
  589. if (m_fLogUsable)
  590. {
  591. va_list va;
  592. va_start (va, szLogFormat);
  593. _Log(LOG_DOWNLOAD, szLogFormat, va);
  594. va_end (va);
  595. }
  596. }
  597. ////////////////////////////////////////////////////////////////////////
  598. //
  599. // log with type DRIVER, this function will do nothing
  600. // if the DRIVER exclusion directive is detected from reg
  601. //
  602. ////////////////////////////////////////////////////////////////////////
  603. void CIULogger::LogDriver(LPCTSTR szLogFormat, ...)
  604. {
  605. USES_IU_CONVERSION;
  606. if (m_fLogUsable)
  607. {
  608. va_list va;
  609. va_start (va, szLogFormat);
  610. _Log(LOG_DRIVER, szLogFormat, va);
  611. va_end (va);
  612. }
  613. }
  614. ////////////////////////////////////////////////////////////////////////
  615. //
  616. // log with type CHECKTRUST, this function will do nothing
  617. // if the CHECKTRUST exclusion directive is detected from reg
  618. //
  619. ////////////////////////////////////////////////////////////////////////
  620. void CIULogger::LogTrust(LPCTSTR szLogFormat, ...)
  621. {
  622. USES_IU_CONVERSION;
  623. if (m_fLogUsable)
  624. {
  625. va_list va;
  626. va_start (va, szLogFormat);
  627. _Log(LOG_TRUST, szLogFormat, va);
  628. va_end (va);
  629. }
  630. }
  631. ////////////////////////////////////////////////////////////////////////
  632. //
  633. // actual base logging function
  634. // if it actually logged, or just returned
  635. // because directives say don't make this kind of log
  636. //
  637. ////////////////////////////////////////////////////////////////////////
  638. void CIULogger::_Log(DWORD LogType, LPCTSTR pszLogFormat, va_list va)
  639. {
  640. USES_IU_CONVERSION;
  641. TCHAR szOut[5 * 1024];
  642. LPTSTR pszFormat;
  643. DWORD dwFormatLen;
  644. if (!m_fLogUsable || (0x0 == (m_sdwLogMask & LogType)) || NULL == pszLogFormat)
  645. {
  646. return;
  647. }
  648. if (LOG_ERROR == LogType)
  649. {
  650. //
  651. // for error case, we try to add "Error Line %d: " in front of the log
  652. //
  653. dwFormatLen = lstrlen(pszLogFormat) + 128;
  654. pszFormat = (TCHAR*) MemAlloc(dwFormatLen * sizeof(TCHAR));
  655. if (NULL != pszFormat)
  656. {
  657. if (FAILED(StringCchPrintf(pszFormat, dwFormatLen, _T("Error Line %d: %s"), m_LineNum, pszLogFormat)))
  658. {
  659. pszFormat = (LPTSTR)pszLogFormat;
  660. }
  661. }
  662. else
  663. {
  664. pszFormat = (LPTSTR)pszLogFormat;
  665. }
  666. }
  667. else
  668. {
  669. pszFormat = (LPTSTR)pszLogFormat;
  670. }
  671. if (SUCCEEDED(StringCchVPrintf(szOut, ARRAYSIZE(szOut), pszFormat, va)))
  672. {
  673. _LogOut(szOut);
  674. }
  675. return;
  676. }
  677. //
  678. // function to write the log to log file
  679. // also taking care of indentation
  680. //
  681. void CIULogger::_LogOut(LPTSTR pszLog)
  682. {
  683. if (NULL == pszLog)
  684. return;
  685. //
  686. // Protect static variables and indent values
  687. //
  688. EnterCriticalSection(&g_LogCs);
  689. int n = GetIndent();
  690. int i,
  691. nLogLen, // length of log string passed in
  692. nTotalLen; // length of constructed
  693. HANDLE hHeap = GetProcessHeap();
  694. LPTSTR pszWholeLog;
  695. LPTSTR pszCurrentPos;
  696. DWORD dwCurrentLen;
  697. DWORD dwWritten;
  698. TCHAR szTab = (m_siIndentStep < 1) ? szTab = _T('\t') : szTab = _T(' ');
  699. //
  700. // find out length for log header
  701. //
  702. if (m_siIndentStep > 0)
  703. {
  704. //
  705. // if positive number, it means the number of
  706. // space chars to use for each indent, rather
  707. // than using a tab
  708. //
  709. n *= m_siIndentStep;
  710. }
  711. nLogLen = lstrlen(pszLog);
  712. //
  713. // verify this log is \r\n ended
  714. //
  715. if (nLogLen > 1 && _T('\n') == pszLog[nLogLen-1])
  716. {
  717. //
  718. // if there is no catriege return, just a \n,
  719. // then remove \n
  720. //
  721. if (_T('\r') != pszLog[nLogLen-2])
  722. {
  723. nLogLen--;
  724. pszLog[nLogLen] = _T('\0');
  725. }
  726. }
  727. nTotalLen = n + sizeof(szLogHeaderFmt)/sizeof(TCHAR) + nLogLen + 3;
  728. //
  729. // allocate memory to construct the log
  730. //
  731. pszWholeLog = (LPTSTR) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, nTotalLen * sizeof(TCHAR));
  732. if (NULL == pszWholeLog)
  733. {
  734. //
  735. // nothing we can do in this case, bail.
  736. //
  737. LeaveCriticalSection(&g_LogCs);
  738. return;
  739. }
  740. //
  741. // get log header
  742. //
  743. GetLogHeader(pszWholeLog, nTotalLen);
  744. //
  745. // construct indent
  746. //
  747. pszCurrentPos = pszWholeLog + lstrlen(pszWholeLog);
  748. dwCurrentLen = nTotalLen - lstrlen(pszWholeLog);
  749. for (i = 0; i < n; i++)
  750. {
  751. pszCurrentPos[i] = szTab;
  752. }
  753. pszCurrentPos[i] = _T('\0');
  754. //
  755. // add log to whilelog buffer
  756. //
  757. if (FAILED(StringCchCat(pszCurrentPos, dwCurrentLen, pszLog)))
  758. {
  759. goto done;
  760. }
  761. //
  762. // Always terminate lines with <CR> <LF>
  763. //
  764. if (_T('\n') != pszLog[nLogLen-1])
  765. {
  766. if (FAILED(StringCchCat(pszCurrentPos, dwCurrentLen, _T("\r\n"))))
  767. {
  768. goto done;
  769. }
  770. }
  771. //
  772. // write log
  773. //
  774. nTotalLen = lstrlen(pszWholeLog);
  775. if (m_fLogFile)
  776. {
  777. if (TRUE == AcquireMutex())
  778. {
  779. //
  780. // Another module (e.g. if we are iuengine, maybe iuctl) may have written
  781. // to the iu_xxx.log file, so we need to seek to the end before writing
  782. //
  783. SetFilePointer(m_shFile, 0, NULL, FILE_END);
  784. WriteFile(m_shFile, pszWholeLog, nTotalLen * sizeof(TCHAR), &dwWritten, NULL);
  785. if (m_fFlushEveryTime)
  786. {
  787. FlushFileBuffers(m_shFile);
  788. }
  789. ReleaseMutex();
  790. }
  791. }
  792. if (m_fLogDebugMsg)
  793. {
  794. OutputDebugString(pszWholeLog);
  795. }
  796. done:
  797. HeapFree(hHeap, 0, pszWholeLog);
  798. LeaveCriticalSection(&g_LogCs);
  799. return;
  800. }
  801. //////////////////////////////////////////////////////////////////////
  802. //
  803. // Timestamp Helper
  804. //
  805. //////////////////////////////////////////////////////////////////////
  806. void CIULogger::GetLogHeader(LPTSTR pszBuffer, DWORD cchBufferLen)
  807. {
  808. SYSTEMTIME st = {0};
  809. if (pszBuffer == NULL)
  810. {
  811. return;
  812. }
  813. GetLocalTime(&st);
  814. //
  815. // print out as the pre-defined format:
  816. // szTimeStampFmt[]
  817. //
  818. if (FAILED(StringCchPrintf(pszBuffer, cchBufferLen,
  819. _T("%4d/%02d/%02d|%02d:%02d:%02d:%03d|%08x| "),
  820. st.wYear,
  821. st.wMonth,
  822. st.wDay,
  823. st.wHour,
  824. st.wMinute,
  825. st.wSecond,
  826. st.wMilliseconds,
  827. m_dwThreadId)))
  828. {
  829. // It wont fit, just set it to an empty string
  830. pszBuffer[0] = 0;
  831. }
  832. }
  833. //
  834. // function to retrieve the indent of current thread
  835. //
  836. int CIULogger::GetIndent(void)
  837. {
  838. if (m_Index < 0 || !m_fLogUsable)
  839. {
  840. return 0;
  841. }
  842. else
  843. {
  844. return m_psIndent[m_Index].iIndent;
  845. }
  846. }
  847. //
  848. // function to change indention of current thread
  849. //
  850. void CIULogger::SetIndent(int IndentDelta)
  851. {
  852. int i;
  853. bool fQuit = false;
  854. EnterCriticalSection(&g_LogCs);
  855. if (m_Index < 0)
  856. {
  857. //
  858. // try to find the index
  859. //
  860. if (NULL == m_psIndent)
  861. {
  862. //
  863. // if no indent array created yet
  864. //
  865. m_psIndent = (_THREAD_INDENT*)
  866. HeapAlloc(
  867. GetProcessHeap(),
  868. HEAP_ZERO_MEMORY,
  869. c_IndentArrayChunk * sizeof(_THREAD_INDENT)
  870. );
  871. if (NULL != m_psIndent)
  872. {
  873. m_Size = c_IndentArrayChunk;
  874. }
  875. else
  876. {
  877. LeaveCriticalSection(&g_LogCs);
  878. return;
  879. }
  880. }
  881. for (i = 0; i < m_Size && m_psIndent[i].dwThreadId != 0; i++)
  882. {
  883. if (m_psIndent[i].dwThreadId == m_dwThreadId)
  884. {
  885. m_Index = i;
  886. break;
  887. }
  888. }
  889. if (m_Index < 0)
  890. {
  891. //
  892. // this thread is not in the array yet
  893. //
  894. for (i = 0; i < m_Size; i++)
  895. {
  896. if (0 == m_psIndent[i].dwThreadId)
  897. {
  898. break;
  899. }
  900. }
  901. if (i < m_Size)
  902. {
  903. //
  904. // fill the next empty slot in array
  905. //
  906. m_psIndent[i].dwThreadId = m_dwThreadId;
  907. m_psIndent[i].iIndent = 0;
  908. m_Index = i;
  909. }
  910. else
  911. {
  912. //
  913. // array is full, no empty slot anymore
  914. // need to increase the indent array size
  915. //
  916. int iSize = m_Size + c_IndentArrayChunk;
  917. _THREAD_INDENT* pNewArray = (_THREAD_INDENT*)
  918. HeapReAlloc(
  919. GetProcessHeap(),
  920. HEAP_ZERO_MEMORY,
  921. m_psIndent,
  922. iSize * sizeof(_THREAD_INDENT)
  923. );
  924. if (NULL != pNewArray)
  925. {
  926. m_psIndent = pNewArray;
  927. m_Size = iSize;
  928. m_psIndent[i].dwThreadId = m_dwThreadId;
  929. m_psIndent[i].iIndent = 0;
  930. m_Index = i;
  931. }
  932. }
  933. }
  934. }
  935. if (m_Index >= 0)
  936. {
  937. m_psIndent[m_Index].iIndent += IndentDelta;
  938. }
  939. LeaveCriticalSection(&g_LogCs);
  940. }
  941. //
  942. // read registry value helper -- protected by g_LogCs in ctor
  943. //
  944. void CIULogger::ReadRegistrySettings(void)
  945. {
  946. //
  947. // declare constants used to retrive logging settings
  948. //
  949. const TCHAR REGKEY_IUTCTL[] = _T("Software\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\IUControlLogging");
  950. const TCHAR REGVAL_LOGFILE[] = _T("Logging File");
  951. const TCHAR REGVAL_LOGDEBUGMSG[] = _T("Logging DebugMsg");
  952. const TCHAR REGVAL_LOGINDENT[] = _T("LogIndentStep");
  953. const TCHAR REGVAL_LOGNOBLOCK[] = _T("LogExcludeBlock");
  954. const TCHAR REGVAL_LOGNOXML[] = _T("LogExcludeXML");
  955. const TCHAR REGVAL_LOGNOXMLBSTR[] = _T("LogExcludeXmlBSTR");
  956. const TCHAR REGVAL_LOGNOINET[] = _T("LogExcludeInternet");
  957. const TCHAR REGVAL_LOGNODRIVER[] = _T("LogExcludeDriver");
  958. const TCHAR REGVAL_LOGNOSW[] = _T("LogExcludeSoftware");
  959. const TCHAR REGVAL_LOGNOTRUST[] = _T("LogExcludeTrust");
  960. const TCHAR REGVAL_LOGDOWNLOAD[] = _T("LogExcludeDownload");
  961. const TCHAR REGVAL_LOGFLUSH[] = _T("FlushLogEveryTime"); // added by charlma 11/27/01 to improve logging performance
  962. // only flush everytime if this flag is set to 1
  963. HKEY hKey = NULL;
  964. TCHAR szFilePath[MAX_PATH] = {0};
  965. DWORD dwSize = sizeof(szFilePath);
  966. DWORD dwData;
  967. if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_IUTCTL, 0, KEY_READ, &hKey))
  968. {
  969. //
  970. // there is no reg key setting available, so we will not
  971. // output any log to anywhere - this is the released mode
  972. //
  973. return;
  974. }
  975. //
  976. // try to read out the file path for log file.
  977. //
  978. if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGVAL_LOGFILE, 0, 0, (LPBYTE)&szFilePath, &dwSize) && dwSize > 0 && szFilePath[0] != _T('\0'))
  979. {
  980. TCHAR szLogFile[MAX_PATH];
  981. TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFName[_MAX_FNAME], szExt[_MAX_EXT];
  982. //
  983. // TODO: changed to use private version splitpath()
  984. //
  985. //_tsplitpath(szFilePath, szDrive, szDir, szFName, szExt);
  986. MySplitPath(szFilePath, szDrive, szDir, szFName, szExt);
  987. //
  988. // construct the log file name with process id embedded
  989. //
  990. if (FAILED(StringCchPrintf(szLogFile, ARRAYSIZE(szLogFile),
  991. _T("%s%s%s_%d%s"),
  992. szDrive,
  993. szDir,
  994. szFName,
  995. GetCurrentProcessId(),
  996. szExt)))
  997. {
  998. // Can't construct log filename, so nothing we can do.
  999. RegCloseKey(hKey);
  1000. return;
  1001. }
  1002. m_shFile = CreateFile(
  1003. szLogFile,
  1004. GENERIC_WRITE,
  1005. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1006. NULL,
  1007. OPEN_ALWAYS,
  1008. 0,
  1009. NULL);
  1010. if (INVALID_HANDLE_VALUE != m_shFile)
  1011. {
  1012. if (INVALID_SET_FILE_POINTER == SetFilePointer(m_shFile, 0, NULL, FILE_END))
  1013. {
  1014. CloseHandle(m_shFile);
  1015. m_shFile = INVALID_HANDLE_VALUE;
  1016. }
  1017. else
  1018. {
  1019. //
  1020. // we have successfully opened the log file
  1021. // so increase the indent level to 0 for
  1022. // top level logging. this will cause
  1023. // the indent array created
  1024. //
  1025. SetIndent(0);
  1026. //
  1027. // Unicode files need a 0xFEFF header
  1028. //
  1029. #if defined(UNICODE) || defined(_UNICODE)
  1030. const WORD wUnicodeHeader = 0xFEFF;
  1031. //
  1032. // if the file is zero length, then this is a new file
  1033. // we need to add unicode header
  1034. //
  1035. DWORD dwFileSize;
  1036. if ( -1 != (dwFileSize = GetFileSize(m_shFile, NULL)))
  1037. {
  1038. if (0 == dwFileSize)
  1039. {
  1040. WriteFile(m_shFile, &wUnicodeHeader, sizeof(WORD), &dwFileSize, NULL);
  1041. }
  1042. }
  1043. #endif
  1044. }
  1045. //
  1046. // Now create the Mutex we will use to protect future writes (we are in global ctor now...)
  1047. //
  1048. // construct the log file name with process id embedded, but no drive or '\' in path
  1049. // so we can use it to name our mutex (file will be per-process).
  1050. //
  1051. if (FAILED(StringCchPrintf(szLogFile, ARRAYSIZE(szLogFile),
  1052. _T("%s_%d%s"),
  1053. szFName,
  1054. GetCurrentProcessId(),
  1055. szExt)))
  1056. {
  1057. // If that doesn't work, just use a simple named mutex
  1058. m_hMutex = ::CreateMutex(NULL, FALSE, szFName);
  1059. }
  1060. else
  1061. {
  1062. m_hMutex = ::CreateMutex(NULL, FALSE, szLogFile);
  1063. }
  1064. }
  1065. }
  1066. //
  1067. // try to find out if we should output debug msg to debugger
  1068. //
  1069. dwData = 0x0;
  1070. dwSize = sizeof(dwData);
  1071. if (ERROR_SUCCESS ==
  1072. RegQueryValueEx(hKey, REGVAL_LOGDEBUGMSG, 0, 0, (LPBYTE)&dwData, &dwSize)
  1073. && (0x1 == dwData))
  1074. {
  1075. m_fLogDebugMsg = true;
  1076. }
  1077. //
  1078. // keep reading other *optional* log directives
  1079. //
  1080. //
  1081. // read whether we should exlude block data
  1082. //
  1083. dwData = 0x0;
  1084. dwSize = sizeof(dwData);
  1085. if (ERROR_SUCCESS ==
  1086. RegQueryValueEx(hKey, REGVAL_LOGNOBLOCK, 0, 0, (LPBYTE)&dwData, &dwSize)
  1087. && (0x1 == dwData))
  1088. {
  1089. //
  1090. // remove block logging bit
  1091. //
  1092. m_sdwLogMask &= (~LOG_BLOCK);
  1093. }
  1094. //
  1095. // read whether we should exlude XML related logging
  1096. //
  1097. dwData = 0x0;
  1098. dwSize = sizeof(dwData);
  1099. if (ERROR_SUCCESS ==
  1100. RegQueryValueEx(hKey, REGVAL_LOGNOXML, 0, 0, (LPBYTE)&dwData, &dwSize)
  1101. && (0x1 == dwData))
  1102. {
  1103. //
  1104. // remove block logging bit
  1105. //
  1106. m_sdwLogMask &= (~LOG_XML_DETAIL);
  1107. }
  1108. //
  1109. // read whether we should exlude XML BSTR related logging
  1110. //
  1111. dwData = 0x0;
  1112. dwSize = sizeof(dwData);
  1113. if (ERROR_SUCCESS ==
  1114. RegQueryValueEx(hKey, REGVAL_LOGNOXMLBSTR, 0, 0, (LPBYTE)&dwData, &dwSize)
  1115. && (0x1 == dwData))
  1116. {
  1117. //
  1118. // remove block logging bit
  1119. //
  1120. m_sdwLogMask &= (~LOG_XML_BSTR_DETAIL);
  1121. }
  1122. //
  1123. // read whether we should exlude internet related logging
  1124. //
  1125. dwData = 0x0;
  1126. dwSize = sizeof(dwData);
  1127. if (ERROR_SUCCESS ==
  1128. RegQueryValueEx(hKey, REGVAL_LOGNOINET, 0, 0, (LPBYTE)&dwData, &dwSize)
  1129. && (0x1 == dwData))
  1130. {
  1131. //
  1132. // remove block logging bit
  1133. //
  1134. m_sdwLogMask &= (~LOG_INTERNET);
  1135. }
  1136. //
  1137. // read whether we should exlude driver related logging
  1138. //
  1139. dwData = 0x0;
  1140. dwSize = sizeof(dwData);
  1141. if (ERROR_SUCCESS ==
  1142. RegQueryValueEx(hKey, REGVAL_LOGNODRIVER, 0, 0, (LPBYTE)&dwData, &dwSize)
  1143. && (0x1 == dwData))
  1144. {
  1145. //
  1146. // remove block logging bit
  1147. //
  1148. m_sdwLogMask &= (~LOG_DRIVER);
  1149. }
  1150. //
  1151. // read whether we should exlude driver related logging
  1152. //
  1153. dwData = 0x0;
  1154. dwSize = sizeof(dwData);
  1155. if (ERROR_SUCCESS ==
  1156. RegQueryValueEx(hKey, REGVAL_LOGNOSW, 0, 0, (LPBYTE)&dwData, &dwSize)
  1157. && (0x1 == dwData))
  1158. {
  1159. //
  1160. // remove block logging bit
  1161. //
  1162. m_sdwLogMask &= (~LOG_SOFTWARE);
  1163. }
  1164. //
  1165. // read whether we should exlude wintrust checking related logging
  1166. //
  1167. dwData = 0x0;
  1168. dwSize = sizeof(dwData);
  1169. if (ERROR_SUCCESS ==
  1170. RegQueryValueEx(hKey, REGVAL_LOGNOTRUST, 0, 0, (LPBYTE)&dwData, &dwSize)
  1171. && (0x1 == dwData))
  1172. {
  1173. //
  1174. // remove block logging bit
  1175. //
  1176. m_sdwLogMask &= (~LOG_TRUST);
  1177. }
  1178. //
  1179. // read whether we should exlude wintrust checking related logging
  1180. //
  1181. dwData = 0x0;
  1182. dwSize = sizeof(dwData);
  1183. if (ERROR_SUCCESS ==
  1184. RegQueryValueEx(hKey, REGVAL_LOGDOWNLOAD, 0, 0, (LPBYTE)&dwData, &dwSize)
  1185. && (0x1 == dwData))
  1186. {
  1187. //
  1188. // remove block logging bit
  1189. //
  1190. m_sdwLogMask &= (~LOG_DOWNLOAD);
  1191. }
  1192. //
  1193. // read whether we should use tab or space(s) for each indent step
  1194. //
  1195. dwData = 0x0;
  1196. dwSize = sizeof(dwData);
  1197. if (ERROR_SUCCESS ==
  1198. RegQueryValueEx(hKey, REGVAL_LOGINDENT, 0, 0, (LPBYTE)&dwData, &dwSize)
  1199. && ((int)dwData > 0))
  1200. {
  1201. //
  1202. // use space char(s) (_T(' ')). If nagetive or 0, _Logout will use tab char
  1203. //
  1204. m_siIndentStep = (int) dwData;
  1205. }
  1206. //
  1207. // read whether we should flush everytime we do file logging
  1208. //
  1209. dwData = 0x0;
  1210. dwSize = sizeof(dwData);
  1211. if (m_shFile != INVALID_HANDLE_VALUE &&
  1212. (ERROR_SUCCESS ==
  1213. RegQueryValueEx(hKey, REGVAL_LOGFLUSH, 0, 0, (LPBYTE)&dwData, &dwSize)))
  1214. {
  1215. m_fFlushEveryTime = (0x1 == dwData);
  1216. }
  1217. //
  1218. // finished registry checking
  1219. //
  1220. RegCloseKey(hKey);
  1221. }
  1222. #endif // defined(DBG)