//-------------------------------------------------------------------------- // LogFile.cpp // Copyright (C) Microsoft Corporation, 1997 - Rocket Database //-------------------------------------------------------------------------- #include "pch.hxx" #include #include #include #include "clogfile.h" #include #include #include //-------------------------------------------------------------------------- // LogFileTypes - RX = Receive, TX = Transmit, DB = Debug //-------------------------------------------------------------------------- static LPSTR s_rgszPrefix[LOGFILE_MAX] = { "rx", "tx", "db" }; //-------------------------------------------------------------------------- // These are strings that we shouldn't log in plaintext //-------------------------------------------------------------------------- static LPSTR s_rgszPassPrefix[] = { "AUTHINFO PASS ", "PASS ", NULL }; //-------------------------------------------------------------------------- // CreateSystemHandleName //-------------------------------------------------------------------------- HRESULT CreateSystemHandleName(LPCSTR pszBase, LPCSTR pszSpecific, LPSTR *ppszName) { // Locals HRESULT hr=S_OK; DWORD cchName; // Trace TraceCall("CreateSystemHandleName"); // Invalid Args Assert(pszBase && pszSpecific && ppszName); // Init *ppszName = NULL; // Compute Length cchName = lstrlen(pszBase) + lstrlen(pszSpecific) + 15; // Allocate IF_NULLEXIT(*ppszName = PszAllocA(cchName)); // Format the name wnsprintf(*ppszName, cchName, "%s%s", pszBase, pszSpecific); // Remove backslashes from this string ReplaceChars(*ppszName, '\\', '_'); // Lower Case CharLower(*ppszName); exit: // Done return hr; } //-------------------------------------------------------------------------- // CreateLogFile //-------------------------------------------------------------------------- OESTDAPI_(HRESULT) CreateLogFile(HINSTANCE hInst, LPCSTR pszLogFile, LPCSTR pszPrefix, DWORD cbTruncate, ILogFile **ppLogFile, DWORD dwShareMode) { // Locals HRESULT hr=S_OK; CLogFile *pNew=NULL; // Trace TraceCall("CreateLogFile"); // Invalid Args Assert(ppLogFile && pszLogFile); // Initialize *ppLogFile = NULL; // Create me pNew = new CLogFile; if (NULL == pNew) { hr = TraceResult(E_OUTOFMEMORY); goto exit; } // Open It IF_FAILEXIT(hr = pNew->Open(hInst, pszLogFile, pszPrefix, cbTruncate, dwShareMode)); // Open It *ppLogFile = (ILogFile *)pNew; // Don't release it pNew = NULL; exit: // Cleanup SafeRelease(pNew); // Done return hr; } //-------------------------------------------------------------------------- // CLogFile::CLogFile //-------------------------------------------------------------------------- CLogFile::CLogFile(void) { TraceCall("CLogFile::CLogFile"); m_cRef = 1; m_hMutex = NULL; m_hFile = INVALID_HANDLE_VALUE; InitializeCriticalSection(&m_cs); } //-------------------------------------------------------------------------- // CLogFile::~CLogFile //-------------------------------------------------------------------------- CLogFile::~CLogFile(void) { TraceCall("CLogFile::~CLogFile"); if (m_hFile != INVALID_HANDLE_VALUE) CloseHandle_F16(m_hFile); SafeCloseHandle(m_hMutex); DeleteCriticalSection(&m_cs); } //-------------------------------------------------------------------------- // CLogFile::AddRef //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CLogFile::AddRef(void) { TraceCall("CLogFile::AddRef"); return InterlockedIncrement(&m_cRef); } //-------------------------------------------------------------------------- // CLogFile::Release //-------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CLogFile::Release(void) { TraceCall("CLogFile::Release"); LONG cRef = InterlockedDecrement(&m_cRef); if (0 == cRef) delete this; return (ULONG)cRef; } //-------------------------------------------------------------------------- // CLogFile::QueryInterface //-------------------------------------------------------------------------- STDMETHODIMP CLogFile::QueryInterface(REFIID riid, LPVOID *ppv) { // Locals HRESULT hr=S_OK; // Stack TraceCall("CLogFile::QueryInterface"); // Find IID if (IID_IUnknown == riid) *ppv = (IUnknown *)this; else { *ppv = NULL; hr = TraceResult(E_NOINTERFACE); goto exit; } // AddRef It ((IUnknown *)*ppv)->AddRef(); exit: // Done return hr; } //-------------------------------------------------------------------------- // CLogFile::Open //-------------------------------------------------------------------------- STDMETHODIMP CLogFile::Open(HINSTANCE hInst, LPCSTR pszFile, LPCSTR pszPrefix, DWORD cbTruncate, DWORD dwShareMode) { // Locals HRESULT hr=S_OK; CHAR szVersion[MAX_PATH]; CHAR szPath[MAX_PATH]; LPSTR pszInfo=NULL; DWORD dwVerHnd; DWORD dwVerInfoSize; DWORD cbFile; CHAR szGet[MAX_PATH]; UINT uLen; LPWORD pdwTrans; LPSTR pszT; SYSTEMTIME st; LPSTR pszVersion; CHAR szPathMinusExt[MAX_PATH + 2]; CHAR szExt[4]; DWORD dwBytesWritten; LPSTR pszMutex=NULL; BOOL fReleaseMutex=FALSE; int iCurrentLogNum; // For unique logfile generation // Tracing TraceCall("CLogFile::Open"); // Save the Prefix StrCpyN(m_szPrefix, pszPrefix ? pszPrefix : "", ARRAYSIZE(m_szPrefix)); // Create a Mutex Name IF_FAILEXIT(hr = CreateSystemHandleName(pszFile, "logfile", &pszMutex)); // Create the Mutex m_hMutex = CreateMutex(NULL, FALSE, pszMutex); if (m_hMutex == NULL) { hr = TraceResult(E_FAIL); goto exit; } // If we have a mutex if (WAIT_OBJECT_0 != WaitForSingleObject(m_hMutex, INFINITE)) { hr = TraceResult(E_FAIL); goto exit; } // Release the Mutex fReleaseMutex = TRUE; // Split the logfile into path+filename and extension iCurrentLogNum = 0; StrCpyN(szPathMinusExt, pszFile, ARRAYSIZE(szPathMinusExt)); pszT = PathFindExtension(szPathMinusExt); if (pszT && '.' == *pszT) { StrCpyN(szExt, pszT + 1, ARRAYSIZE(szExt)); *pszT = '\0'; // Remove extension from path and filename } else { // Use default extension of "log" StrCpyN(szExt, "log", ARRAYSIZE(szExt)); } // Generate first logfile name wnsprintf(szPath, ARRAYSIZE(szPath), "%s.%s", szPathMinusExt, szExt); // Open|Create the log file do { m_hFile = CreateFile(szPath, GENERIC_WRITE, dwShareMode, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == m_hFile) { DWORD dwLastErr; dwLastErr = GetLastError(); // If file is already in use, then try to create a separate logfile. Otherwise bail. if (ERROR_SHARING_VIOLATION == dwLastErr) { // Generate next unique file name iCurrentLogNum += 1; wnsprintf(szPath, ARRAYSIZE(szPath), "%s (%d).%s", szPathMinusExt, iCurrentLogNum, szExt); } else { hr = TraceResultSz(E_FAIL, _MSG("Can't open logfile %s, GetLastError() = %d", szPath, dwLastErr)); goto exit; } } } while (INVALID_HANDLE_VALUE == m_hFile); // Get the File Size cbFile = GetFileSize(m_hFile, NULL); // Get the size of the file if (0xFFFFFFFF != cbFile) { // Truncate It if (cbFile >= cbTruncate) { // Set the file pointer to the end of the file if (0xFFFFFFFF == SetFilePointer(m_hFile, 0, NULL, FILE_BEGIN)) { Assert(FALSE); hr = TraceResult(E_FAIL); goto exit; } // Set the End of file if (0 == SetEndOfFile(m_hFile)) { Assert(FALSE); hr = TraceResult(E_FAIL); goto exit; } // File is zero-length cbFile = 0; } // Set the file pointer to the end of the file if (0xFFFFFFFF == SetFilePointer(m_hFile, 0, NULL, FILE_END)) { Assert(FALSE); hr = TraceResultSz(E_FAIL, _MSG("Can't seek to the end of the logfile %s, GetLastError() = %d", szPath, GetLastError())); goto exit; } } // Get Module FileName GetModuleFileName(hInst, szPath, sizeof(szPath)); // Initialize szVersion szVersion[0] = '\0'; // Get version information from athena dwVerInfoSize = GetFileVersionInfoSize(szPath, &dwVerHnd); if (dwVerInfoSize) { // Allocate IF_NULLEXIT(pszInfo = (LPSTR)g_pMalloc->Alloc(dwVerInfoSize)); // Get version info if (GetFileVersionInfo(szPath, dwVerHnd, dwVerInfoSize, pszInfo)) { // VerQueryValue if (VerQueryValue(pszInfo, "\\VarFileInfo\\Translation", (LPVOID *)&pdwTrans, &uLen) && uLen >= (2 * sizeof(WORD))) { // set up buffer for calls to VerQueryValue() wnsprintf(szGet, ARRAYSIZE(szGet), "\\StringFileInfo\\%04X%04X\\", pdwTrans[0], pdwTrans[1]); // What is this doing DWORD cchLen = lstrlen(szGet); pszT = szGet + cchLen; // Setup file description StrCatBuff(szGet, "FileDescription", ARRAYSIZE(szGet)); // Get the file description if (VerQueryValue(pszInfo, szGet, (LPVOID *)&pszVersion, &uLen) && uLen) { StrCpyN(szVersion, pszVersion, ARRAYSIZE(szVersion)); StrCatBuff(szVersion, " ", ARRAYSIZE(szVersion)); } // Setup Version String StrCpyN(pszT, "FileVersion", ARRAYSIZE(szGet) - cchLen); // Get the file version if (VerQueryValue(pszInfo, szGet, (LPVOID *)&pszVersion, &uLen) && uLen) StrCatBuff(szVersion, pszVersion, ARRAYSIZE(szVersion)); } } } // Write the Date GetLocalTime(&st); wnsprintf(szPath, ARRAYSIZE(szPath), "\r\n%s\r\n%s Log started at %.2d/%.2d/%.4d %.2d:%.2d:%.2d\r\n", szVersion, pszPrefix, st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond); // add a new line WriteFile(m_hFile, szPath, lstrlen(szPath), &dwBytesWritten, NULL); exit: // Failure AssertSz(SUCCEEDED(hr), "Log file could not be opened."); // Cleanup if (fReleaseMutex) ReleaseMutex(m_hMutex); SafeMemFree(pszInfo); SafeMemFree(pszMutex); // Done return hr; } //-------------------------------------------------------------------------- // WriteLogMsg //-------------------------------------------------------------------------- STDMETHODIMP WriteLogMsg(CLogFile *pLogFile, LOGFILETYPE lft, LPTSTR pszFormat, ...) { static TCHAR szBuffer[2048]; va_list arglist; va_start(arglist, pszFormat); wvnsprintf(szBuffer, ARRAYSIZE(szBuffer), pszFormat, arglist); va_end(arglist); return pLogFile->WriteLog(lft, szBuffer); } //-------------------------------------------------------------------------- // CLogFile::TraceLog //-------------------------------------------------------------------------- STDMETHODIMP CLogFile::TraceLog(SHOWTRACEMASK dwMask, TRACEMACROTYPE tracetype, ULONG ulLine, HRESULT hrResult, LPCSTR pszMessage) { // Write the message if (TRACE_INFO == tracetype && pszMessage) { if (ISFLAGSET(dwMask, SHOW_TRACE_INFO)) WriteLogMsg(this, LOGFILE_DB, "0x%08X: L(%d), Info: %s", GetCurrentThreadId(), ulLine, pszMessage); } else if (TRACE_RESULT == tracetype && pszMessage) WriteLogMsg(this, LOGFILE_DB, "0x%08X: L(%d), Result: HRESULT(0x%08X) - GetLastError() = %d - %s", GetCurrentThreadId(), ulLine, hrResult, GetLastError(), pszMessage); else if (TRACE_RESULT == tracetype && NULL == pszMessage) WriteLogMsg(this, LOGFILE_DB, "0x%08X: L(%d), Result: HRESULT(0x%08X) - GetLastError() = %d", GetCurrentThreadId(), ulLine, hrResult, GetLastError()); else Assert(FALSE); // Done return hrResult; } //-------------------------------------------------------------------------- // CLogFile::WriteLog //-------------------------------------------------------------------------- STDMETHODIMP CLogFile::WriteLog(LOGFILETYPE lft, LPCSTR pszData) { // Locals HRESULT hr=S_OK; DWORD bWrite; DWORD cBytesWritten; SYSTEMTIME st; CHAR szLogPrefx[30]; INT cb; LPSTR *ppszPrefix; LPSTR pszFree=NULL; BOOL fReleaseMutex=FALSE; // Trace TraceCall("CLogFile::WriteLog"); // File is not open if (m_hFile == INVALID_HANDLE_VALUE) return TraceResult(E_UNEXPECTED); // Invalid Args Assert(pszData && lft < LOGFILE_MAX); // Thread Safety EnterCriticalSection(&m_cs); // Initialized Assert(m_hMutex); // If we have a mutex if (WAIT_OBJECT_0 != WaitForSingleObject(m_hMutex, INFINITE)) { hr = TraceResult(E_FAIL); goto exit; } // Release the Mutex fReleaseMutex = TRUE; // Get the time GetLocalTime(&st); // Write the log prefix and time stamp wnsprintf(szLogPrefx, ARRAYSIZE(szLogPrefx), "%s: %.2d:%.2d:%.2d [%s] ", m_szPrefix, st.wHour, st.wMinute, st.wSecond, s_rgszPrefix[lft]); // Set the file pointer to the end of the file (otherwise multiple writers overwrite each other) if (0xFFFFFFFF == SetFilePointer(m_hFile, 0, NULL, FILE_END)) { hr = TraceResult(E_FAIL); goto exit; } // Write the time and prefix if (0 == WriteFile(m_hFile, szLogPrefx, lstrlen(szLogPrefx), &cBytesWritten, NULL)) { hr = TraceResultSz(E_FAIL, _MSG("Can't write to logfile. GetLastError() = %d", GetLastError())); goto exit; } // Loop through prefixes for (ppszPrefix = s_rgszPassPrefix; *ppszPrefix; ppszPrefix++) { // Does the data start with one of these prefixes if (0 == StrCmpNI(pszData, *ppszPrefix, lstrlen(*ppszPrefix))) { // Dup the buffer that was passed in IF_NULLEXIT(pszFree = PszDupA(pszData)); // Reset pszData pszData = pszFree; // Fixup the buffer for (LPSTR pszTmp = (LPSTR)pszData + lstrlen(*ppszPrefix); *pszTmp && *pszTmp != '\r'; pszTmp++) *pszTmp = '*'; // Done break; } } // Get the length of pszData cb = lstrlen(pszData); // write the log data if (0 == WriteFile(m_hFile, pszData, cb, &cBytesWritten, NULL)) { hr = TraceResultSz(E_FAIL, _MSG("Can't write to logfile. GetLastError() = %d", GetLastError())); goto exit; } // Add a CRLF if not there already if (cb < 2 || pszData[cb-1] != '\n' || pszData[cb-2] != '\r') WriteFile(m_hFile, "\r\n", 2, &cBytesWritten, NULL); exit: // Cleanup if (fReleaseMutex) ReleaseMutex(m_hMutex); // Thread Safety LeaveCriticalSection(&m_cs); // Cleanup SafeMemFree(pszFree); // Done return hr; } //-------------------------------------------------------------------------- // CLogFile::DebugLog //-------------------------------------------------------------------------- STDMETHODIMP CLogFile::DebugLog(LPCSTR pszData) { return WriteLog(LOGFILE_DB, pszData); } //-------------------------------------------------------------------------- // CLogFile::DebugLogs //-------------------------------------------------------------------------- STDMETHODIMP CLogFile::DebugLogs(LPCSTR pszFormat, LPCSTR pszString) { // Locals CHAR szBuffer[1024]; // Build the String wnsprintf(szBuffer, ARRAYSIZE(szBuffer), pszFormat, pszString); // Call Debug Log return DebugLog(szBuffer); } //-------------------------------------------------------------------------- // CLogFile::DebugLogd //-------------------------------------------------------------------------- STDMETHODIMP CLogFile::DebugLogd(LPCSTR pszFormat, INT d) { // Locals CHAR szBuffer[1024]; // Build the String wnsprintf(szBuffer, ARRAYSIZE(szBuffer), pszFormat, d); // Call Debug Log return DebugLog(szBuffer); }