windows-nt/Source/XPSP1/NT/admin/pchealth/client/common/pfcommon/weblog.cpp
2020-09-26 16:20:57 +08:00

594 lines
17 KiB
C++

/********************************************************************
Copyright (c) 1996-2000 Microsoft Corporation
Module Name:
weblog.cpp
Abstract:
Defines a generic class that can be used to log
info from ISAPIs. This class allows its user to
create application specific logfiles and
automatically uses an intermediate file to log info and
creates permanent log files at predefined intervals
or after predefined number of records have been
written to the intermediate file.
Revision History:
rsraghav created 03/25/96
DerekM modified 04/06/99
DerekM modifued 02/24/00
********************************************************************/
#include "stdafx.h"
#include "weblog.h"
#include "util.h"
#include "wchar.h"
/////////////////////////////////////////////////////////////////////////////
// CWeblog- initialization stuff
// **************************************************************************
CWeblog::CWeblog(void)
{
InitializeCriticalSection(&m_cs);
m_fInit = FALSE;
m_cMaxRecords = c_dwMaxRecordsDefault;
m_dwDumpInterval = c_dwDumpIntervalDefault;
m_liDumpIntervalAsFT = c_dwDumpIntervalDefault;
m_liDumpIntervalAsFT *= c_dwMinToMS;
m_liDumpIntervalAsFT *= c_dwFTtoMS;
m_szAppName[0] = L'\0';
m_szFileName[0] = L'\0';
m_szFilePath[0] = L'\0';
ZeroMemory(&m_ftLastDump, sizeof(m_ftLastDump));
m_cRecords = 0;
m_hFile = INVALID_HANDLE_VALUE;
}
// **************************************************************************
CWeblog::~CWeblog()
{
if (m_hFile != INVALID_HANDLE_VALUE)
CloseHandle(m_hFile);
DeleteCriticalSection(&m_cs);
}
/////////////////////////////////////////////////////////////////////////////
// CWeblog- internal stuff
// **************************************************************************
HRESULT CWeblog::InitFromRegistry()
{
USE_TRACING("CWeblog::InitFromRegistry");
SAppLogInfoExtra alie;
SAppLogInfo ali;
HRESULT hr;
// read the ALI & ALIE structures
TESTHR(hr, ReadALI(m_szAppName, &ali, &alie));
if (FAILED(hr))
goto done;
CopyMemory(&m_ftLastDump, &alie.ftLastDump, sizeof(m_ftLastDump));
lstrcpyW(m_szFileName, ali.wszLogPath);
lstrcpyW(m_szFilePath, ali.wszLogPath);
m_cMaxRecords = ali.cMaxTempRecs;
m_cRecords = alie.cCurTempRecs;
m_dwDumpInterval = ali.cDumpMins;
m_liDumpIntervalAsFT = m_dwDumpInterval;
m_liDumpIntervalAsFT *= c_dwMinToMS;
m_liDumpIntervalAsFT *= c_dwFTtoMS;
done:
return hr;
}
// **************************************************************************
BOOL CWeblog::IsDumpRequired()
{
USE_TRACING("CWeblog::IsDumpRequired");
SYSTEMTIME stNow;
FILETIME ftNow;
// check if we've gone over our allotted amount of records
if ((m_cMaxRecords > 0) && (m_cRecords >= m_cMaxRecords))
return TRUE;
// ok, so now check if we've gone over our allotted time range.
// if we fail to convert the system time, do a dump to be on the safe side...
GetLocalTime(&stNow);
if (SystemTimeToFileTime(&stNow, &ftNow) == FALSE)
return TRUE;
if (((*(unsigned __int64 *)&ftNow) -
(*(unsigned __int64 *)&m_ftLastDump)) > m_liDumpIntervalAsFT)
return TRUE;
return FALSE;
}
// **************************************************************************
HRESULT CWeblog::DumpLog()
{
USE_TRACING("CWeblog::DumpLog");
SYSTEMTIME st;
HRESULT hr = NOERROR;
DWORD dwRet;
WCHAR *pszStart = NULL;
WCHAR szNewName[MAX_PATH + 1];
int nUsed;
this->Lock();
// Close handle to the intermediate file
CloseHandle(m_hFile);
// intialize new internal stuff...
m_hFile = INVALID_HANDLE_VALUE;
m_cRecords = 0;
// Rename the intermediate file to be permanent logfile with
// appropriate name
nUsed = wsprintfW(szNewName, L"%ls%ls", m_szFilePath, m_szAppName);
if ((nUsed + c_cMaxTimeSuffixLen) > MAX_PATH)
{
// the file name is too long - truncate it
szNewName[MAX_PATH - c_cMaxTimeSuffixLen - 1] = L'\0';
nUsed = lstrlenW(szNewName);
}
// update last dump time stamp
GetLocalTime(&st);
SystemTimeToFileTime(&st, &m_ftLastDump);
// create the rest of the filename...
pszStart = &szNewName[nUsed];
wsprintfW(pszStart, L"%04d%02d%02d%02d%02d%02d%ls", st.wYear, st.wMonth,
st.wDay, st.wHour, st.wMinute, st.wSecond,
c_szPermLogfileSuffix);
// move the file (duh.)
MoveFileExW(m_szFileName, szNewName,
MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
// recreate the intermediate file and get a handle to it
m_hFile = CreateFileW(m_szFileName, GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (m_hFile == INVALID_HANDLE_VALUE)
hr = Err2HR(GetLastError());
else
SetFilePointer(m_hFile, 0, NULL, FILE_END);
this->Unlock();
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// CWeblog- exposed methods
// **************************************************************************
HRESULT CWeblog::InitLogging(LPCWSTR szAppName)
{
USE_TRACING("CWeblog::InitLogging");
HRESULT hr = NOERROR;
int nUsed;
// only allow one init per lifetime of the object...
VALIDATEEXPR(hr, (m_fInit), E_FAIL);
if (FAILED(hr))
goto done;
// validate params
VALIDATEPARM(hr, (szAppName == NULL));
if (FAILED(hr))
goto done;
// copy in the app name, zero terminating in case app name is longer than we allow...
wcsncpy(m_szAppName, szAppName, c_cMaxAppNameLen);
m_szAppName[c_cMaxAppNameLen] = L'\0';
// Read in settings from Registry
TESTHR(hr, InitFromRegistry());
if (FAILED(hr))
goto done;
// m_szFileName contains the file path: append appname and suffix
nUsed = lstrlenW(m_szFileName);
VALIDATEEXPR(hr, ((nUsed + lstrlenW(m_szAppName) + 6) > MAX_PATH), E_FAIL);
if (FAILED(hr))
goto done;
// Open the intermediate file and keep it ready for appending
wsprintfW(m_szFileName + nUsed, L"%ls%ls", m_szAppName, c_szTempLogfileSuffix);
m_hFile = CreateFileW(m_szFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL,
OPEN_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
TESTBOOL(hr, (m_hFile != INVALID_HANDLE_VALUE))
if (FAILED(hr))
goto done;
SetFilePointer(m_hFile, 0, NULL, FILE_END);
m_fInit = TRUE;
done:
return hr;
}
// **************************************************************************
HRESULT CWeblog::TerminateLogging(void)
{
USE_TRACING("CWeblog::TerminateLogging");
SAppLogInfoExtra alie;
HRESULT hr = NOERROR;
// bug out if the initialization didn't work smoothly...
if (m_fInit == FALSE)
goto done;
lstrcpyW(alie.wszName, m_szAppName);
CopyMemory(&alie.ftLastDump, &m_ftLastDump, sizeof(alie.ftLastDump));
alie.cCurTempRecs = m_cRecords;
TESTHR(hr, WriteALI(NULL, &alie));
if (FAILED(hr))
goto done;
m_fInit = FALSE;
m_cMaxRecords = c_dwMaxRecordsDefault;
m_dwDumpInterval = c_dwDumpIntervalDefault;
m_liDumpIntervalAsFT = c_dwDumpIntervalDefault;
m_liDumpIntervalAsFT *= c_dwMinToMS;
m_liDumpIntervalAsFT *= c_dwFTtoMS;
m_szAppName[0] = L'\0';
m_szFileName[0] = L'\0';
m_szFilePath[0] = L'\0';
ZeroMemory(&m_ftLastDump, sizeof(m_ftLastDump));
m_cRecords = 0;
done:
return hr;
}
// **************************************************************************
HRESULT CWeblog::LogRecord(LPCWSTR szFormat, ... )
{
USE_TRACING("CWeblog::LogRecord");
SYSTEMTIME st;
va_list arglist;
HRESULT hr = NOERROR;
DWORD dwUsed, nWritten, dwUsedA;
WCHAR szLogRecordW[c_cMaxRecLen + 1];
CHAR szLogRecordA[(2 * c_cMaxRecLen) + 1];
int nAdded;
// only allow 'em in if they've called init...
VALIDATEEXPR(hr, (m_fInit == FALSE), E_FAIL);
if (FAILED(hr))
goto done;
// validate params
VALIDATEPARM(hr, (szFormat == NULL));
if (FAILED(hr))
goto done;
// prepend the app name and current time
GetLocalTime(&st);
dwUsed = (ULONG)wsprintfW(szLogRecordW, L"%ls,%04u/%02u/%02u %02u:%02u:%02u\n",
m_szAppName, st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
va_start(arglist, szFormat);
nAdded = _vsnwprintf(&szLogRecordW[dwUsed], c_cMaxRecLen - dwUsed,
szFormat, arglist);
// is the arglist too big for us?
if (nAdded < 0)
{
// if so, just insert a dummy value
lstrcpyW(&szLogRecordW[dwUsed],
L"Logging Error: Record given for logging is too big!\r\n");
dwUsed = lstrlenW(szLogRecordW);
}
else
{
// otherwise, add a CRLF to the end...
dwUsed += (ULONG) nAdded;
szLogRecordW[dwUsed++] = L'\r';
szLogRecordW[dwUsed++] = L'\n';
szLogRecordW[dwUsed] = L'\0';
}
va_end(arglist);
// translate the record down into ASCII so the lab team can use grep &
// other such utilities on these files (which don't work with unicode
// text files)
dwUsedA = WideCharToMultiByte(CP_ACP, 0, szLogRecordW, -1, szLogRecordA,
2 * c_cMaxRecLen, NULL, NULL);
if (dwUsedA == 0)
{
dwUsedA = (ULONG)wsprintfA(szLogRecordA, "%ls,%04u/%02u/%02u %02u:%02u:%02u\n",
m_szAppName, st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
lstrcpyA(&szLogRecordA[dwUsedA], "Logging Error: unable to translate log record to ASCII\r\n");
dwUsedA = lstrlen(szLogRecordA);
}
else
{
// dwUsedA contains a character for the NULL terminator at the end of
// the ASCII string. Since we don't want to write this to the log
// file, reduce the size of the log entry by 1 byte...
dwUsedA--;
}
this->Lock();
TESTBOOL(hr, (WriteFile(m_hFile, szLogRecordA, dwUsedA, &nWritten,
NULL) == FALSE));
if (FAILED(hr))
{
this->Unlock();
goto done;
}
m_cRecords++;
if (IsDumpRequired())
TESTHR(hr, DumpLog());
this->Unlock();
done:
return hr;
}
//////////////////////////////////////////////////////////////////////
// CWeblogConfig initialization
// **************************************************************************
HRESULT ReadALI(LPCWSTR wszName, SAppLogInfo *pali, SAppLogInfoExtra *palie)
{
USE_TRACING("CWeblog::ReadALI");
HRESULT hr = NOERROR;
WCHAR wszDef[MAX_PATH];
DWORD dw = 0, cb;
HKEY hKeyLog = NULL, hKeyRoot = NULL;
BOOL fWrite;
VALIDATEPARM(hr, (wszName == NULL || pali == NULL));
if (FAILED(hr))
goto done;
GetWindowsDirectoryW(wszDef, sizeof(wszDef) / sizeof(WCHAR));
wszDef[3] = L'\0';
TESTHR(hr, OpenRegKey(HKEY_LOCAL_MACHINE, c_szRPWeblogRootKey, FALSE,
&hKeyRoot));
if (FAILED(hr))
goto done;
TESTHR(hr, OpenRegKey(hKeyRoot, wszName, FALSE, &hKeyLog));
if (FAILED(hr))
goto done;
// max allowed temp records
cb = sizeof(pali->cMaxTempRecs);
TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVMaxRecords, NULL,
(PBYTE)&pali->cMaxTempRecs, &cb,
(PBYTE)&c_dwMaxRecordsDefault, sizeof(DWORD)));
if (FAILED(hr))
goto done;
// dump interval
cb = sizeof(pali->cDumpMins);
TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVDumpInterval, NULL,
(PBYTE)&pali->cDumpMins, &cb,
(PBYTE)&c_dwDumpIntervalDefault, sizeof(DWORD)));
if (FAILED(hr))
goto done;
// weblog path
cb = sizeof(pali->wszLogPath) * sizeof(WCHAR);
TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVLogFilePath, NULL,
(PBYTE)&pali->wszLogPath, &cb, (PBYTE)&wszDef,
(lstrlenW(wszDef) + 1) * sizeof(WCHAR)));
if (FAILED(hr))
goto done;
lstrcpyW(pali->wszName, wszName);
// make sure we have a '\' at the end of the file path...
dw = lstrlenW(pali->wszLogPath);
if (pali->wszLogPath[dw - 1] != L'\\')
{
pali->wszLogPath[dw] = L'\\';
pali->wszLogPath[dw + 1] = L'\0';
}
// make sure we have valid values here...
if (pali->cDumpMins == 0)
pali->cDumpMins = c_dwDumpIntervalDefault;
if (pali->cMaxTempRecs == 0)
pali->cMaxTempRecs = c_dwMaxRecordsDefault;
// if this is NULL, we don't have to do anything more
if (palie != NULL)
{
FILETIME ftDef;
// last dump time
ZeroMemory(&ftDef, sizeof(FILETIME));
cb = sizeof(palie->ftLastDump);
TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVLastDumpTime, NULL,
(PBYTE)&palie->ftLastDump, &cb,
(PBYTE)&ftDef, sizeof(FILETIME)));
if (FAILED(hr))
goto done;
// current temp records
dw = 0;
cb = sizeof(palie->cCurTempRecs);
TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVCurrentRecs, NULL,
(PBYTE)&palie->cCurTempRecs, &cb,
(PBYTE)&dw, sizeof(DWORD)));
if (FAILED(hr))
goto done;
lstrcpyW(palie->wszName, wszName);
}
done:
if (hKeyLog != NULL)
RegCloseKey(hKeyLog);
if (hKeyRoot != NULL)
RegCloseKey(hKeyRoot);
return hr;
}
// **************************************************************************
HRESULT WriteALI(SAppLogInfo *pali, SAppLogInfoExtra *palie)
{
USE_TRACING("CWeblog::WriteALI");
HRESULT hr = NOERROR;
LPWSTR pwsz = NULL;
DWORD dwErr;
HKEY hKeyLog = NULL, hKeyRoot = NULL;
BOOL fWrite;
if (pali != NULL)
pwsz = pali->wszName;
else if (palie != NULL)
pwsz = palie->wszName;
else
goto done;
TESTHR(hr, OpenRegKey(HKEY_LOCAL_MACHINE, c_szRPWeblogRootKey, TRUE,
&hKeyRoot));
if (FAILED(hr))
goto done;
TESTHR(hr, OpenRegKey(hKeyRoot, pwsz, TRUE, &hKeyLog));
if (FAILED(hr))
goto done;
if (pali != NULL)
{
DWORD dw;
// make sure we have valid values here...
if (pali->cDumpMins == 0)
pali->cDumpMins = c_dwDumpIntervalDefault;
if (pali->cMaxTempRecs == 0)
pali->cMaxTempRecs = c_dwMaxRecordsDefault;
// make sure we have a '\' at the end of the file path...
dw = lstrlenW(pali->wszLogPath);
if (pali->wszLogPath[dw - 1] != L'\\')
{
pali->wszLogPath[dw] = L'\\';
pali->wszLogPath[dw + 1] = L'\0';
}
// max temp records
TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVMaxRecords, 0, REG_DWORD,
(PBYTE)&pali->cMaxTempRecs, sizeof(DWORD)));
if (FAILED(hr))
goto done;
// dump interval
TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVDumpInterval, 0, REG_DWORD,
(PBYTE)&pali->cDumpMins, sizeof(DWORD)));
if (FAILED(hr))
goto done;
// weblog path
TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVLogFilePath, 0, REG_SZ,
(PBYTE)&pali->wszLogPath,
(lstrlenW(pali->wszLogPath) + 1) * sizeof(WCHAR)));
if (FAILED(hr))
goto done;
}
// if this is NULL, we don't have to do anything more
if (palie != NULL)
{
// last dump time
TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVLastDumpTime, 0, REG_BINARY,
(PBYTE)&palie->ftLastDump, sizeof(FILETIME)));
if (FAILED(hr))
goto done;
// current temp records
TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVCurrentRecs, 0, REG_DWORD,
(PBYTE)&palie->cCurTempRecs, sizeof(DWORD)));
if (FAILED(hr))
goto done;
}
done:
if (hKeyLog != NULL)
RegCloseKey(hKeyLog);
if (hKeyRoot != NULL)
RegCloseKey(hKeyRoot);
return hr;
}
// **************************************************************************
HRESULT DeleteALI(LPCWSTR wszName)
{
USE_TRACING("CWeblog::DeleteALI");
HRESULT hr = NOERROR;
DWORD dwErr;
HKEY hKeyRoot = NULL;
BOOL fWrite;
TESTHR(hr, OpenRegKey(HKEY_LOCAL_MACHINE, c_szRPWeblogRootKey, TRUE,
&hKeyRoot));
if (FAILED(hr))
goto done;
dwErr = RegDeleteKeyW(hKeyRoot, wszName);
if (dwErr != ERROR_SUCCESS && dwErr != ERROR_PATH_NOT_FOUND &&
dwErr != ERROR_FILE_NOT_FOUND)
{
hr = Err2HR(GetLastError());
goto done;
}
done:
if (hKeyRoot != NULL)
RegCloseKey(hKeyRoot);
return hr;
}