1414 lines
37 KiB
C++
1414 lines
37 KiB
C++
//=======================================================================
|
|
//
|
|
// Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// File: IULogger.cpp: implementation of the CIULogger class.
|
|
//
|
|
// Description:
|
|
//
|
|
// See IULogger.h
|
|
//
|
|
//=======================================================================
|
|
|
|
#if defined(DBG)
|
|
|
|
#include <windows.h>
|
|
#include <ole2.h>
|
|
#include <tchar.h>
|
|
#include <MemUtil.h>
|
|
|
|
#include <fileutil.h>
|
|
#include <Logging.h>
|
|
#include <strsafe.h>
|
|
|
|
|
|
//
|
|
// declare constants used to control log exclusions
|
|
//
|
|
const DWORD LOG_BLOCK = 0x00000001; // log function/block in/out
|
|
const DWORD LOG_XML_DETAIL = 0x00000002; // log detailed XML operation
|
|
const DWORD LOG_INTERNET = 0x00000004; // log action related to Internet activities, e.g., downld
|
|
const DWORD LOG_SOFTWARE = 0x00000008; // log details about software detection/installation
|
|
const DWORD LOG_DRIVER = 0x00000010; // log actions related to driver detection/installation
|
|
const DWORD LOG_TRUST = 0x00000020; // log actions related to wintrust checking
|
|
const DWORD LOG_DOWNLOAD = 0x00000040; // log actions related to download
|
|
const DWORD LOG_XML_BSTR_DETAIL = 0x00000080; // log XML BSTRs
|
|
const DWORD LOG_ERROR = 0x00008000; // you can not exclude this type of logs from output
|
|
const DWORD LOG_ALL = 0xFFFFFFFF; // default, all above
|
|
|
|
//
|
|
// const for longest line of XML we will output
|
|
//
|
|
const DWORD LOG_XML_BUFF_LEN = 128;
|
|
|
|
//
|
|
// const for specifying the intent array size increament.
|
|
// each element in array holds indent data for one thread
|
|
//
|
|
const int c_IndentArrayChunk = 16;
|
|
|
|
//
|
|
// define the log header format
|
|
//
|
|
// It is constructed as: <date> <time> <thread id>
|
|
//
|
|
const TCHAR szLogHeaderFmt[] = _T("yyyy/mm/dd hh:nn:ss:sss xxxxxxxx ");
|
|
|
|
//
|
|
// initialization of static members
|
|
//
|
|
|
|
int CIULogger::m_Size = 0;
|
|
int CIULogger::m_siIndentStep = 0; // init to use tab char
|
|
CIULogger::_THREAD_INDENT* CIULogger::m_psIndent = NULL;
|
|
|
|
DWORD CIULogger::m_sdwLogMask = LOG_ALL;
|
|
HANDLE CIULogger::m_shFile = INVALID_HANDLE_VALUE;
|
|
bool CIULogger::m_fLogDebugMsg = false;
|
|
bool CIULogger::m_fLogFile = false;
|
|
bool CIULogger::m_fLogUsable = false;
|
|
HANDLE CIULogger::m_hMutex = NULL;
|
|
int CIULogger::m_cFailedWaits = 0;
|
|
int CIULogger::m_fFlushEveryTime = FALSE;
|
|
|
|
//
|
|
// Defines for Mutex (borrowed from freelog)
|
|
//
|
|
// NOTE: globals and statics are per-module (e.g. iuctl, iuengine), but Mutex is per-processes
|
|
// due to the name being constructed from the log file name (contains process ID).
|
|
//
|
|
#define MUTEX_TIMEOUT 1000 // Don't wait more than 1 second to write to logfile
|
|
#define MAX_MUTEX_WAITS 4 // Don't keep trying after this many failures
|
|
|
|
//
|
|
// global variable
|
|
//
|
|
|
|
//
|
|
// reference count to control log file open/close
|
|
//
|
|
LONG g_RefCount = 0;
|
|
|
|
//
|
|
// critical sectoin handling multi-threading
|
|
// access of indent array case
|
|
//
|
|
//
|
|
CRITICAL_SECTION g_LogCs;
|
|
|
|
|
|
|
|
//
|
|
// we need to declare a global object so refcount wont't
|
|
// be zero, otherwise in multi-threading mode ref count
|
|
// can be fooled and AV when one object thinks m_psIdent not NULL
|
|
// but another object in another (parent) thread freed m_psIndent
|
|
// in destructor (only if parent thread quits)
|
|
//
|
|
CIULogger g_DummyLogObj(NULL);
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
CIULogger::CIULogger(char* szBlockName)
|
|
: m_Index(-1), m_LineNum(0)
|
|
{
|
|
if (0 == g_RefCount)
|
|
{
|
|
//
|
|
// This must be the g_DummyLogObj (or another global
|
|
// instance) during initialization of globals. Since
|
|
// this is the first instance created, we must init CS
|
|
//
|
|
InitializeCriticalSection(&g_LogCs);
|
|
}
|
|
|
|
//
|
|
// Protect the statics while in the ctor
|
|
//
|
|
EnterCriticalSection(&g_LogCs);
|
|
|
|
m_dwTickBegin = GetTickCount();
|
|
m_dwThreadId = GetCurrentThreadId();
|
|
m_fProcessLog = (NULL == szBlockName);
|
|
ZeroMemory(m_szBlockName, sizeof(m_szBlockName));
|
|
|
|
g_RefCount++;
|
|
|
|
if (1 == g_RefCount)
|
|
{
|
|
//
|
|
// this is the first time to call this class, we need to
|
|
// find out whether we should log and where to log to
|
|
//
|
|
|
|
ReadRegistrySettings();
|
|
|
|
//
|
|
// allocate memory for thread indent array initially
|
|
//
|
|
SetIndent(0);
|
|
|
|
//
|
|
// if the indent level is not negative, then it's okay to log
|
|
//
|
|
m_fLogFile = (INVALID_HANDLE_VALUE != m_shFile);
|
|
m_fLogUsable = (m_fLogFile || m_fLogDebugMsg) && (NULL != m_psIndent);
|
|
}
|
|
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
if (!m_fProcessLog)
|
|
{
|
|
//
|
|
// this is probably a new thread, so we need to find the index
|
|
// for this thread.
|
|
//
|
|
SetIndent(0);
|
|
|
|
//
|
|
// do block logging, if permitted
|
|
//
|
|
if (0x0 != (m_sdwLogMask & LOG_BLOCK) && szBlockName && _T('\0') != szBlockName[0])
|
|
{
|
|
StringCchCopyA(m_szBlockName, ARRAYSIZE(m_szBlockName), szBlockName);
|
|
USES_IU_CONVERSION;
|
|
|
|
char szOut[sizeof(m_szBlockName) + 10];
|
|
//
|
|
// Implicit "Enter " before block name to save log space
|
|
//
|
|
if (SUCCEEDED(StringCchPrintfA(szOut, ARRAYSIZE(szOut), "%hs\r\n", szBlockName)))
|
|
{
|
|
_LogOut(A2T(szOut));
|
|
}
|
|
}
|
|
SetIndent(+1);
|
|
}
|
|
|
|
}
|
|
LeaveCriticalSection(&g_LogCs);
|
|
}
|
|
|
|
|
|
|
|
CIULogger::~CIULogger()
|
|
{
|
|
EnterCriticalSection(&g_LogCs);
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
//
|
|
// decrease the indent level by 1 if we increased indent
|
|
//
|
|
if (!m_fProcessLog)
|
|
{
|
|
SetIndent(-1);
|
|
}
|
|
|
|
//
|
|
// write log file for exiting block, if allowed and block name exists
|
|
//
|
|
if (0x0 != (m_sdwLogMask & LOG_BLOCK) && _T('\0') != m_szBlockName[0])
|
|
{
|
|
USES_IU_CONVERSION;
|
|
char szOut[1024];
|
|
//
|
|
// "Exit " shortened to "~" to save log space
|
|
//
|
|
if (SUCCEEDED(StringCchPrintfA(szOut, ARRAYSIZE(szOut), "~%hs, %d msec\r\n", m_szBlockName, GetTickCount() - m_dwTickBegin)))
|
|
{
|
|
_LogOut(A2T(szOut));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// reduce reference cnt
|
|
//
|
|
g_RefCount--;
|
|
|
|
//
|
|
// g_RefCount will go to zero before leaving dtor if this is the last global instance
|
|
// in this module
|
|
//
|
|
if (0 == g_RefCount)
|
|
{
|
|
//
|
|
// close file if the file is open
|
|
//
|
|
if (m_fLogFile && INVALID_HANDLE_VALUE != m_shFile) // redundent?
|
|
{
|
|
CloseHandle(m_shFile);
|
|
m_shFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
if(NULL != m_hMutex)
|
|
{
|
|
CloseHandle(m_hMutex);
|
|
}
|
|
//
|
|
// free memory of indent array
|
|
//
|
|
if (NULL != m_psIndent)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, m_psIndent);
|
|
m_psIndent = NULL;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&g_LogCs);
|
|
|
|
//
|
|
// This is the last global instance (probably g_DummyLogObj) and is
|
|
// being destructed before the DLL unloads
|
|
//
|
|
if (0 == g_RefCount)
|
|
{
|
|
DeleteCriticalSection(&g_LogCs);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Mutex stuff borrowed from freelog
|
|
// fixcode: This should not be required here since chk logging is per process only
|
|
BOOL CIULogger::AcquireMutex()
|
|
{
|
|
// In rare case where mutex not created, we allow file operations
|
|
// with no synchronization
|
|
if (m_hMutex == NULL)
|
|
return TRUE;
|
|
|
|
// Don't keep waiting if we've been blocked in the past
|
|
if (m_cFailedWaits >= MAX_MUTEX_WAITS)
|
|
return FALSE;
|
|
|
|
BOOL fResult = TRUE;
|
|
if (WaitForSingleObject(m_hMutex, MUTEX_TIMEOUT) != WAIT_OBJECT_0)
|
|
{
|
|
fResult = FALSE;
|
|
m_cFailedWaits++;
|
|
}
|
|
|
|
return fResult;
|
|
}
|
|
|
|
void CIULogger::ReleaseMutex()
|
|
{
|
|
if (m_hMutex != NULL) // Note: AcquireMutex succeeds even if m_hMutex is NULL
|
|
{
|
|
::ReleaseMutex(m_hMutex);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// log with no flag, so can not be removed by excluding directives
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::Log(LPCTSTR szLogFormat, ...)
|
|
{
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
USES_IU_CONVERSION;
|
|
va_list va;
|
|
va_start (va, szLogFormat);
|
|
_Log(LOG_ALL, szLogFormat, va);
|
|
va_end (va);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// log error, so can not be removed by excluding directives
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogError(LPCTSTR szLogFormat, ...)
|
|
{
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
va_list va;
|
|
va_start (va, szLogFormat);
|
|
_Log(LOG_ERROR, szLogFormat, va);
|
|
va_end (va);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Helper for LogErrorMsg and LogInfoMsg (which supply message to prepend)
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::_LogFormattedMsg(DWORD dwErrCode, LPCTSTR pszErrorInfo)
|
|
{
|
|
if (m_fLogUsable)
|
|
{
|
|
//
|
|
// try to retrive system msg
|
|
//
|
|
LPTSTR lpszBuffer = NULL, lpszLogMsg = NULL;
|
|
LPVOID lpMsg = NULL;
|
|
FormatMessage(
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, // no source, use system msg
|
|
dwErrCode,
|
|
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
|
|
(LPTSTR)&lpMsg,
|
|
0,
|
|
NULL);
|
|
if (NULL == lpMsg)
|
|
{
|
|
//
|
|
// if we failed to get the msg, then output generic
|
|
// error/info log
|
|
//
|
|
LogError(_T("Unknown %s Line %d: 0x%08x\n"), pszErrorInfo, m_LineNum, dwErrCode);
|
|
}
|
|
else
|
|
{
|
|
lpszBuffer = (LPTSTR) lpMsg;
|
|
int nLen = lstrlen(lpszBuffer);
|
|
lpszLogMsg = (LPTSTR) LocalAlloc(0, (nLen + 128) * sizeof(TCHAR));
|
|
if (NULL != lpszLogMsg)
|
|
{
|
|
//
|
|
// insert Error/Info keyword
|
|
//
|
|
if (FAILED(StringCchPrintf(lpszLogMsg, ARRAYSIZE(lpszLogMsg), _T("%s Line %d: 0x%08x: %s"), pszErrorInfo, m_LineNum, dwErrCode, lpszBuffer)))
|
|
{
|
|
// Couldn't build the right string, so just output the system msg
|
|
LocalFree(lpszLogMsg);
|
|
lpszLogMsg = lpszBuffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// failed to get buffer? unlikely, anyway,
|
|
// we have no option but just output the system msg
|
|
//
|
|
lpszLogMsg = lpszBuffer;
|
|
}
|
|
|
|
//
|
|
// write log out
|
|
//
|
|
_LogOut(lpszLogMsg);
|
|
|
|
//
|
|
// clean up buffer
|
|
//
|
|
if (lpszLogMsg != lpszBuffer)
|
|
{
|
|
LocalFree(lpszLogMsg);
|
|
}
|
|
LocalFree(lpszBuffer);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// similar to LogError, but try to log the system msg based
|
|
// on the error code. If the sysmsg not avail, log
|
|
// "Unknown error with error code 0x%08x"
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogErrorMsg(DWORD dwErrCode)
|
|
{
|
|
_LogFormattedMsg(dwErrCode, _T("Error"));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// similar to LogErrorMsg but prepends with "Info" rather than "Error"
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogInfoMsg(DWORD dwErrCode)
|
|
{
|
|
_LogFormattedMsg(dwErrCode, _T("Info"));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// log with type INTERNET, this function will do nothing
|
|
// if the Internet exclusion directive is detected from reg
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogInternet(LPCTSTR szLogFormat, ...)
|
|
{
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
va_list va;
|
|
va_start (va, szLogFormat);
|
|
_Log(LOG_INTERNET, szLogFormat, va);
|
|
va_end (va);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// log with type XML, this function will do nothing
|
|
// if the XML exclusion directive is detected from reg
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogXML(LPCTSTR szLogFormat, ...)
|
|
{
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
va_list va;
|
|
va_start (va, szLogFormat);
|
|
_Log(LOG_XML_DETAIL, szLogFormat, va);
|
|
va_end (va);
|
|
}
|
|
}
|
|
|
|
void CIULogger::_NukeCrLf(LPTSTR pszBuffer)
|
|
{
|
|
while (*pszBuffer)
|
|
{
|
|
if (_T('\r') == *pszBuffer || _T('\n') == *pszBuffer)
|
|
{
|
|
//
|
|
// Overwrite <CR> and <LF> with space
|
|
//
|
|
*pszBuffer = _T(' ');
|
|
}
|
|
pszBuffer++;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// log BSTR containing valid XML. This gets around length limitations
|
|
// of LogOutput and attempts to break lines following ">". This
|
|
// output is sent for both fre and chk builds unless excluded from reg.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogXmlBSTR(BSTR bstrXML)
|
|
{
|
|
USES_IU_CONVERSION;
|
|
|
|
LPTSTR pszLine;
|
|
LPTSTR pszTemp;
|
|
LPTSTR pszStop;
|
|
LPTSTR pszLastGT;
|
|
TCHAR szXmlBuff[LOG_XML_BUFF_LEN];
|
|
HRESULT hr;
|
|
|
|
if (NULL == bstrXML)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (m_fLogUsable && (m_sdwLogMask & LOG_XML_BSTR_DETAIL) )
|
|
{
|
|
#if !(defined(UNICODE) || defined(_UNICODE))
|
|
DWORD dwANSIBuffLen = SysStringLen(bstrXML) + 1;
|
|
LPSTR pszANSIBuff = (LPSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwANSIBuffLen);
|
|
|
|
if (NULL == pszANSIBuff)
|
|
{
|
|
//
|
|
// We're toast - just return without logging
|
|
//
|
|
return;
|
|
}
|
|
LPTSTR pTempTchar = OLE2T(bstrXML);
|
|
if (NULL != pTempTchar)
|
|
{
|
|
if (FAILED(StringCchCopyA(pszANSIBuff, dwANSIBuffLen, pTempTchar)))
|
|
{
|
|
goto done;
|
|
}
|
|
}
|
|
pszLine = pszANSIBuff;
|
|
#else
|
|
pszLine = bstrXML;
|
|
#endif
|
|
|
|
while (*pszLine)
|
|
{
|
|
//
|
|
// Skip <CR> & <LF> chars
|
|
//
|
|
while (_T('\r') == *pszLine || _T('\n') == *pszLine)
|
|
{
|
|
pszLine++;
|
|
if (NULL == *pszLine)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (NULL == *pszLine)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pszTemp = pszLine;
|
|
pszStop = pszLine + LOG_XML_BUFF_LEN - 1;
|
|
pszLastGT = NULL;
|
|
|
|
//
|
|
// Try to find the last '>' char that will fit in buffer
|
|
//
|
|
while (*pszTemp && pszTemp < pszStop)
|
|
{
|
|
if (_T('>') == *pszTemp)
|
|
{
|
|
pszLastGT = pszTemp;
|
|
}
|
|
pszTemp++;
|
|
}
|
|
|
|
if (pszLastGT)
|
|
{
|
|
//
|
|
// Break the line at the last '>' that fits into LOG_XML_BUFF_LEN
|
|
//
|
|
hr = StringCchCopy(szXmlBuff, (int) (pszLastGT - pszLine) + 2, pszLine);
|
|
//
|
|
// STRSAFE_E_INSUFFICIENT_BUFFER is returned if the string is truncated.
|
|
// This is normal since we are just copying a portion of the XML at
|
|
// a time so it won't be too long to log.
|
|
//
|
|
if (SUCCEEDED(hr) || STRSAFE_E_INSUFFICIENT_BUFFER == hr)
|
|
{
|
|
_NukeCrLf(szXmlBuff);
|
|
_LogOut(szXmlBuff);
|
|
pszLine = pszLastGT + 1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (*pszTemp)
|
|
{
|
|
//
|
|
// We're forced to break the line at LOG_XML_BUFF_LEN with no '>' in range
|
|
//
|
|
hr = StringCchCopy(szXmlBuff, LOG_XML_BUFF_LEN, pszLine);
|
|
if (SUCCEEDED(hr) || STRSAFE_E_INSUFFICIENT_BUFFER == hr)
|
|
{
|
|
_NukeCrLf(szXmlBuff);
|
|
_LogOut(szXmlBuff);
|
|
pszLine += LOG_XML_BUFF_LEN -1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Output any leftover XML to end of BSTR
|
|
//
|
|
_NukeCrLf(pszLine);
|
|
_LogOut(pszLine);
|
|
//
|
|
// Set to end of BSTR so we bust out of outer while
|
|
//
|
|
pszLine += lstrlen(pszLine);
|
|
}
|
|
}
|
|
|
|
#if !(defined(UNICODE) || defined(_UNICODE))
|
|
done:
|
|
if (pszANSIBuff)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, pszANSIBuff);
|
|
pszANSIBuff = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// log with type SOFTWARE, this function will do nothing
|
|
// if the SOFTWARE exclusion directive is detected from reg
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogSoftware(LPCTSTR szLogFormat, ...)
|
|
{
|
|
USES_IU_CONVERSION;
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
va_list va;
|
|
va_start (va, szLogFormat);
|
|
_Log(LOG_SOFTWARE, szLogFormat, va);
|
|
va_end (va);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// log with type DOWNLOAD, this function will do nothing
|
|
// if the LogDownload exclusion directive is detected from reg
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogDownload(LPCTSTR szLogFormat, ...)
|
|
{
|
|
USES_IU_CONVERSION;
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
va_list va;
|
|
va_start (va, szLogFormat);
|
|
_Log(LOG_DOWNLOAD, szLogFormat, va);
|
|
va_end (va);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// log with type DRIVER, this function will do nothing
|
|
// if the DRIVER exclusion directive is detected from reg
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogDriver(LPCTSTR szLogFormat, ...)
|
|
{
|
|
USES_IU_CONVERSION;
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
va_list va;
|
|
va_start (va, szLogFormat);
|
|
_Log(LOG_DRIVER, szLogFormat, va);
|
|
va_end (va);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// log with type CHECKTRUST, this function will do nothing
|
|
// if the CHECKTRUST exclusion directive is detected from reg
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::LogTrust(LPCTSTR szLogFormat, ...)
|
|
{
|
|
|
|
USES_IU_CONVERSION;
|
|
|
|
if (m_fLogUsable)
|
|
{
|
|
va_list va;
|
|
va_start (va, szLogFormat);
|
|
_Log(LOG_TRUST, szLogFormat, va);
|
|
va_end (va);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// actual base logging function
|
|
// if it actually logged, or just returned
|
|
// because directives say don't make this kind of log
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
void CIULogger::_Log(DWORD LogType, LPCTSTR pszLogFormat, va_list va)
|
|
{
|
|
|
|
USES_IU_CONVERSION;
|
|
TCHAR szOut[5 * 1024];
|
|
LPTSTR pszFormat;
|
|
DWORD dwFormatLen;
|
|
|
|
if (!m_fLogUsable || (0x0 == (m_sdwLogMask & LogType)) || NULL == pszLogFormat)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (LOG_ERROR == LogType)
|
|
{
|
|
//
|
|
// for error case, we try to add "Error Line %d: " in front of the log
|
|
//
|
|
dwFormatLen = lstrlen(pszLogFormat) + 128;
|
|
pszFormat = (TCHAR*) MemAlloc(dwFormatLen * sizeof(TCHAR));
|
|
if (NULL != pszFormat)
|
|
{
|
|
if (FAILED(StringCchPrintf(pszFormat, dwFormatLen, _T("Error Line %d: %s"), m_LineNum, pszLogFormat)))
|
|
{
|
|
pszFormat = (LPTSTR)pszLogFormat;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszFormat = (LPTSTR)pszLogFormat;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszFormat = (LPTSTR)pszLogFormat;
|
|
}
|
|
|
|
if (SUCCEEDED(StringCchVPrintf(szOut, ARRAYSIZE(szOut), pszFormat, va)))
|
|
{
|
|
_LogOut(szOut);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// function to write the log to log file
|
|
// also taking care of indentation
|
|
//
|
|
void CIULogger::_LogOut(LPTSTR pszLog)
|
|
{
|
|
|
|
if (NULL == pszLog)
|
|
return;
|
|
|
|
//
|
|
// Protect static variables and indent values
|
|
//
|
|
EnterCriticalSection(&g_LogCs);
|
|
|
|
int n = GetIndent();
|
|
int i,
|
|
nLogLen, // length of log string passed in
|
|
nTotalLen; // length of constructed
|
|
|
|
HANDLE hHeap = GetProcessHeap();
|
|
LPTSTR pszWholeLog;
|
|
LPTSTR pszCurrentPos;
|
|
DWORD dwCurrentLen;
|
|
DWORD dwWritten;
|
|
TCHAR szTab = (m_siIndentStep < 1) ? szTab = _T('\t') : szTab = _T(' ');
|
|
|
|
//
|
|
// find out length for log header
|
|
//
|
|
if (m_siIndentStep > 0)
|
|
{
|
|
//
|
|
// if positive number, it means the number of
|
|
// space chars to use for each indent, rather
|
|
// than using a tab
|
|
//
|
|
n *= m_siIndentStep;
|
|
}
|
|
|
|
nLogLen = lstrlen(pszLog);
|
|
//
|
|
// verify this log is \r\n ended
|
|
//
|
|
if (nLogLen > 1 && _T('\n') == pszLog[nLogLen-1])
|
|
{
|
|
//
|
|
// if there is no catriege return, just a \n,
|
|
// then remove \n
|
|
//
|
|
if (_T('\r') != pszLog[nLogLen-2])
|
|
{
|
|
nLogLen--;
|
|
pszLog[nLogLen] = _T('\0');
|
|
}
|
|
}
|
|
|
|
nTotalLen = n + sizeof(szLogHeaderFmt)/sizeof(TCHAR) + nLogLen + 3;
|
|
|
|
//
|
|
// allocate memory to construct the log
|
|
//
|
|
pszWholeLog = (LPTSTR) HeapAlloc(hHeap, HEAP_ZERO_MEMORY, nTotalLen * sizeof(TCHAR));
|
|
|
|
if (NULL == pszWholeLog)
|
|
{
|
|
//
|
|
// nothing we can do in this case, bail.
|
|
//
|
|
LeaveCriticalSection(&g_LogCs);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// get log header
|
|
//
|
|
GetLogHeader(pszWholeLog, nTotalLen);
|
|
|
|
//
|
|
// construct indent
|
|
//
|
|
pszCurrentPos = pszWholeLog + lstrlen(pszWholeLog);
|
|
dwCurrentLen = nTotalLen - lstrlen(pszWholeLog);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
pszCurrentPos[i] = szTab;
|
|
}
|
|
pszCurrentPos[i] = _T('\0');
|
|
|
|
//
|
|
// add log to whilelog buffer
|
|
//
|
|
if (FAILED(StringCchCat(pszCurrentPos, dwCurrentLen, pszLog)))
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Always terminate lines with <CR> <LF>
|
|
//
|
|
if (_T('\n') != pszLog[nLogLen-1])
|
|
{
|
|
if (FAILED(StringCchCat(pszCurrentPos, dwCurrentLen, _T("\r\n"))))
|
|
{
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// write log
|
|
//
|
|
nTotalLen = lstrlen(pszWholeLog);
|
|
|
|
if (m_fLogFile)
|
|
{
|
|
if (TRUE == AcquireMutex())
|
|
{
|
|
//
|
|
// Another module (e.g. if we are iuengine, maybe iuctl) may have written
|
|
// to the iu_xxx.log file, so we need to seek to the end before writing
|
|
//
|
|
SetFilePointer(m_shFile, 0, NULL, FILE_END);
|
|
WriteFile(m_shFile, pszWholeLog, nTotalLen * sizeof(TCHAR), &dwWritten, NULL);
|
|
if (m_fFlushEveryTime)
|
|
{
|
|
FlushFileBuffers(m_shFile);
|
|
}
|
|
|
|
ReleaseMutex();
|
|
}
|
|
}
|
|
|
|
if (m_fLogDebugMsg)
|
|
{
|
|
OutputDebugString(pszWholeLog);
|
|
}
|
|
|
|
done:
|
|
|
|
HeapFree(hHeap, 0, pszWholeLog);
|
|
|
|
LeaveCriticalSection(&g_LogCs);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Timestamp Helper
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void CIULogger::GetLogHeader(LPTSTR pszBuffer, DWORD cchBufferLen)
|
|
{
|
|
SYSTEMTIME st = {0};
|
|
|
|
if (pszBuffer == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetLocalTime(&st);
|
|
|
|
//
|
|
// print out as the pre-defined format:
|
|
// szTimeStampFmt[]
|
|
//
|
|
if (FAILED(StringCchPrintf(pszBuffer, cchBufferLen,
|
|
_T("%4d/%02d/%02d|%02d:%02d:%02d:%03d|%08x| "),
|
|
st.wYear,
|
|
st.wMonth,
|
|
st.wDay,
|
|
st.wHour,
|
|
st.wMinute,
|
|
st.wSecond,
|
|
st.wMilliseconds,
|
|
m_dwThreadId)))
|
|
{
|
|
// It wont fit, just set it to an empty string
|
|
pszBuffer[0] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// function to retrieve the indent of current thread
|
|
//
|
|
int CIULogger::GetIndent(void)
|
|
{
|
|
|
|
if (m_Index < 0 || !m_fLogUsable)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return m_psIndent[m_Index].iIndent;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// function to change indention of current thread
|
|
//
|
|
void CIULogger::SetIndent(int IndentDelta)
|
|
{
|
|
int i;
|
|
bool fQuit = false;
|
|
|
|
EnterCriticalSection(&g_LogCs);
|
|
|
|
if (m_Index < 0)
|
|
{
|
|
//
|
|
// try to find the index
|
|
//
|
|
|
|
if (NULL == m_psIndent)
|
|
{
|
|
//
|
|
// if no indent array created yet
|
|
//
|
|
m_psIndent = (_THREAD_INDENT*)
|
|
HeapAlloc(
|
|
GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
c_IndentArrayChunk * sizeof(_THREAD_INDENT)
|
|
);
|
|
if (NULL != m_psIndent)
|
|
{
|
|
m_Size = c_IndentArrayChunk;
|
|
}
|
|
else
|
|
{
|
|
LeaveCriticalSection(&g_LogCs);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
for (i = 0; i < m_Size && m_psIndent[i].dwThreadId != 0; i++)
|
|
{
|
|
if (m_psIndent[i].dwThreadId == m_dwThreadId)
|
|
{
|
|
m_Index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (m_Index < 0)
|
|
{
|
|
//
|
|
// this thread is not in the array yet
|
|
//
|
|
for (i = 0; i < m_Size; i++)
|
|
{
|
|
if (0 == m_psIndent[i].dwThreadId)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i < m_Size)
|
|
{
|
|
//
|
|
// fill the next empty slot in array
|
|
//
|
|
m_psIndent[i].dwThreadId = m_dwThreadId;
|
|
m_psIndent[i].iIndent = 0;
|
|
m_Index = i;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// array is full, no empty slot anymore
|
|
// need to increase the indent array size
|
|
//
|
|
int iSize = m_Size + c_IndentArrayChunk;
|
|
|
|
_THREAD_INDENT* pNewArray = (_THREAD_INDENT*)
|
|
HeapReAlloc(
|
|
GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
m_psIndent,
|
|
iSize * sizeof(_THREAD_INDENT)
|
|
);
|
|
if (NULL != pNewArray)
|
|
{
|
|
m_psIndent = pNewArray;
|
|
m_Size = iSize;
|
|
|
|
m_psIndent[i].dwThreadId = m_dwThreadId;
|
|
m_psIndent[i].iIndent = 0;
|
|
m_Index = i;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if (m_Index >= 0)
|
|
{
|
|
m_psIndent[m_Index].iIndent += IndentDelta;
|
|
}
|
|
|
|
LeaveCriticalSection(&g_LogCs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// read registry value helper -- protected by g_LogCs in ctor
|
|
//
|
|
void CIULogger::ReadRegistrySettings(void)
|
|
{
|
|
|
|
//
|
|
// declare constants used to retrive logging settings
|
|
//
|
|
const TCHAR REGKEY_IUTCTL[] = _T("Software\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\IUControlLogging");
|
|
const TCHAR REGVAL_LOGFILE[] = _T("Logging File");
|
|
const TCHAR REGVAL_LOGDEBUGMSG[] = _T("Logging DebugMsg");
|
|
const TCHAR REGVAL_LOGINDENT[] = _T("LogIndentStep");
|
|
const TCHAR REGVAL_LOGNOBLOCK[] = _T("LogExcludeBlock");
|
|
const TCHAR REGVAL_LOGNOXML[] = _T("LogExcludeXML");
|
|
const TCHAR REGVAL_LOGNOXMLBSTR[] = _T("LogExcludeXmlBSTR");
|
|
const TCHAR REGVAL_LOGNOINET[] = _T("LogExcludeInternet");
|
|
const TCHAR REGVAL_LOGNODRIVER[] = _T("LogExcludeDriver");
|
|
const TCHAR REGVAL_LOGNOSW[] = _T("LogExcludeSoftware");
|
|
const TCHAR REGVAL_LOGNOTRUST[] = _T("LogExcludeTrust");
|
|
const TCHAR REGVAL_LOGDOWNLOAD[] = _T("LogExcludeDownload");
|
|
const TCHAR REGVAL_LOGFLUSH[] = _T("FlushLogEveryTime"); // added by charlma 11/27/01 to improve logging performance
|
|
// only flush everytime if this flag is set to 1
|
|
|
|
|
|
HKEY hKey = NULL;
|
|
TCHAR szFilePath[MAX_PATH] = {0};
|
|
DWORD dwSize = sizeof(szFilePath);
|
|
DWORD dwData;
|
|
|
|
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGKEY_IUTCTL, 0, KEY_READ, &hKey))
|
|
{
|
|
//
|
|
// there is no reg key setting available, so we will not
|
|
// output any log to anywhere - this is the released mode
|
|
//
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// try to read out the file path for log file.
|
|
//
|
|
if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGVAL_LOGFILE, 0, 0, (LPBYTE)&szFilePath, &dwSize) && dwSize > 0 && szFilePath[0] != _T('\0'))
|
|
{
|
|
TCHAR szLogFile[MAX_PATH];
|
|
TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFName[_MAX_FNAME], szExt[_MAX_EXT];
|
|
//
|
|
// TODO: changed to use private version splitpath()
|
|
//
|
|
//_tsplitpath(szFilePath, szDrive, szDir, szFName, szExt);
|
|
MySplitPath(szFilePath, szDrive, szDir, szFName, szExt);
|
|
|
|
//
|
|
// construct the log file name with process id embedded
|
|
//
|
|
if (FAILED(StringCchPrintf(szLogFile, ARRAYSIZE(szLogFile),
|
|
_T("%s%s%s_%d%s"),
|
|
szDrive,
|
|
szDir,
|
|
szFName,
|
|
GetCurrentProcessId(),
|
|
szExt)))
|
|
{
|
|
// Can't construct log filename, so nothing we can do.
|
|
RegCloseKey(hKey);
|
|
return;
|
|
}
|
|
|
|
m_shFile = CreateFile(
|
|
szLogFile,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
0,
|
|
NULL);
|
|
if (INVALID_HANDLE_VALUE != m_shFile)
|
|
{
|
|
if (INVALID_SET_FILE_POINTER == SetFilePointer(m_shFile, 0, NULL, FILE_END))
|
|
{
|
|
CloseHandle(m_shFile);
|
|
m_shFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// we have successfully opened the log file
|
|
// so increase the indent level to 0 for
|
|
// top level logging. this will cause
|
|
// the indent array created
|
|
//
|
|
SetIndent(0);
|
|
|
|
//
|
|
// Unicode files need a 0xFEFF header
|
|
//
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
const WORD wUnicodeHeader = 0xFEFF;
|
|
|
|
//
|
|
// if the file is zero length, then this is a new file
|
|
// we need to add unicode header
|
|
//
|
|
DWORD dwFileSize;
|
|
|
|
if ( -1 != (dwFileSize = GetFileSize(m_shFile, NULL)))
|
|
{
|
|
if (0 == dwFileSize)
|
|
{
|
|
WriteFile(m_shFile, &wUnicodeHeader, sizeof(WORD), &dwFileSize, NULL);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
//
|
|
// Now create the Mutex we will use to protect future writes (we are in global ctor now...)
|
|
//
|
|
// construct the log file name with process id embedded, but no drive or '\' in path
|
|
// so we can use it to name our mutex (file will be per-process).
|
|
//
|
|
if (FAILED(StringCchPrintf(szLogFile, ARRAYSIZE(szLogFile),
|
|
_T("%s_%d%s"),
|
|
szFName,
|
|
GetCurrentProcessId(),
|
|
szExt)))
|
|
{
|
|
// If that doesn't work, just use a simple named mutex
|
|
m_hMutex = ::CreateMutex(NULL, FALSE, szFName);
|
|
}
|
|
else
|
|
{
|
|
m_hMutex = ::CreateMutex(NULL, FALSE, szLogFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// try to find out if we should output debug msg to debugger
|
|
//
|
|
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGDEBUGMSG, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& (0x1 == dwData))
|
|
{
|
|
m_fLogDebugMsg = true;
|
|
}
|
|
|
|
|
|
//
|
|
// keep reading other *optional* log directives
|
|
//
|
|
|
|
//
|
|
// read whether we should exlude block data
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGNOBLOCK, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& (0x1 == dwData))
|
|
{
|
|
//
|
|
// remove block logging bit
|
|
//
|
|
m_sdwLogMask &= (~LOG_BLOCK);
|
|
}
|
|
|
|
//
|
|
// read whether we should exlude XML related logging
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGNOXML, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& (0x1 == dwData))
|
|
{
|
|
//
|
|
// remove block logging bit
|
|
//
|
|
m_sdwLogMask &= (~LOG_XML_DETAIL);
|
|
}
|
|
|
|
//
|
|
// read whether we should exlude XML BSTR related logging
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGNOXMLBSTR, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& (0x1 == dwData))
|
|
{
|
|
//
|
|
// remove block logging bit
|
|
//
|
|
m_sdwLogMask &= (~LOG_XML_BSTR_DETAIL);
|
|
}
|
|
|
|
//
|
|
// read whether we should exlude internet related logging
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGNOINET, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& (0x1 == dwData))
|
|
{
|
|
//
|
|
// remove block logging bit
|
|
//
|
|
m_sdwLogMask &= (~LOG_INTERNET);
|
|
}
|
|
|
|
//
|
|
// read whether we should exlude driver related logging
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGNODRIVER, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& (0x1 == dwData))
|
|
{
|
|
//
|
|
// remove block logging bit
|
|
//
|
|
m_sdwLogMask &= (~LOG_DRIVER);
|
|
}
|
|
|
|
//
|
|
// read whether we should exlude driver related logging
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGNOSW, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& (0x1 == dwData))
|
|
{
|
|
//
|
|
// remove block logging bit
|
|
//
|
|
m_sdwLogMask &= (~LOG_SOFTWARE);
|
|
}
|
|
|
|
//
|
|
// read whether we should exlude wintrust checking related logging
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGNOTRUST, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& (0x1 == dwData))
|
|
{
|
|
//
|
|
// remove block logging bit
|
|
//
|
|
m_sdwLogMask &= (~LOG_TRUST);
|
|
}
|
|
|
|
//
|
|
// read whether we should exlude wintrust checking related logging
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGDOWNLOAD, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& (0x1 == dwData))
|
|
{
|
|
//
|
|
// remove block logging bit
|
|
//
|
|
m_sdwLogMask &= (~LOG_DOWNLOAD);
|
|
}
|
|
|
|
|
|
//
|
|
// read whether we should use tab or space(s) for each indent step
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGINDENT, 0, 0, (LPBYTE)&dwData, &dwSize)
|
|
&& ((int)dwData > 0))
|
|
{
|
|
//
|
|
// use space char(s) (_T(' ')). If nagetive or 0, _Logout will use tab char
|
|
//
|
|
m_siIndentStep = (int) dwData;
|
|
}
|
|
|
|
|
|
//
|
|
// read whether we should flush everytime we do file logging
|
|
//
|
|
dwData = 0x0;
|
|
dwSize = sizeof(dwData);
|
|
if (m_shFile != INVALID_HANDLE_VALUE &&
|
|
(ERROR_SUCCESS ==
|
|
RegQueryValueEx(hKey, REGVAL_LOGFLUSH, 0, 0, (LPBYTE)&dwData, &dwSize)))
|
|
{
|
|
|
|
m_fFlushEveryTime = (0x1 == dwData);
|
|
}
|
|
|
|
//
|
|
// finished registry checking
|
|
//
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
#endif // defined(DBG)
|
|
|
|
|
|
|