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.
1283 lines
29 KiB
1283 lines
29 KiB
/*++
|
|
|
|
Copyright (c) 1995-1996 Microsoft Corporation
|
|
|
|
Module Name :
|
|
filectl.cxx
|
|
|
|
Abstract:
|
|
OLE control to handle file logging object
|
|
|
|
Author:
|
|
|
|
Terence Kwan ( terryk ) 18-Sep-1996
|
|
|
|
Project:
|
|
|
|
IIS Logging 3.0
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#include "initguid.h"
|
|
#include <ilogobj.hxx>
|
|
#include "filectl.hxx"
|
|
#include <issched.hxx>
|
|
|
|
#include <atlimpl.cpp>
|
|
|
|
#define LOG_FILE_SLOP 512
|
|
|
|
//
|
|
// tick minute.
|
|
//
|
|
|
|
#define TICK_MINUTE (60 * 1000)
|
|
|
|
//************************************************************************************
|
|
|
|
|
|
VOID
|
|
LogWriteEvent(
|
|
IN LPCSTR InstanceName,
|
|
IN BOOL fResume
|
|
);
|
|
|
|
//
|
|
// globals
|
|
//
|
|
|
|
LPEVENT_LOG g_eventLog = NULL;
|
|
|
|
|
|
CLogFileCtrl::CLogFileCtrl(
|
|
VOID
|
|
)
|
|
:
|
|
m_fFirstLog ( TRUE),
|
|
m_pLogFile ( NULL),
|
|
m_fDiskFullShutdown ( FALSE),
|
|
m_fUsingCustomHeaders ( FALSE),
|
|
m_sequence ( 1),
|
|
m_TickResumeOpen ( 0),
|
|
m_strLogFileName ( ),
|
|
m_dwSchedulerCookie ( 0),
|
|
m_fInTerminate ( FALSE)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Contructor for the log file control
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// initialize all the internal variable
|
|
//
|
|
|
|
ZeroMemory( &m_stCurrentFile, sizeof( m_stCurrentFile));
|
|
INITIALIZE_CRITICAL_SECTION( &m_csLock );
|
|
}
|
|
|
|
|
|
//************************************************************************************
|
|
// CLogFileCtrl::~CLogFileCtrl - Destructor
|
|
|
|
CLogFileCtrl::~CLogFileCtrl()
|
|
/*++
|
|
|
|
Routine Description:
|
|
destructor for the log file control
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
TerminateLog();
|
|
|
|
DeleteCriticalSection( &m_csLock );
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
STDMETHODIMP
|
|
CLogFileCtrl::InitializeLog(
|
|
LPCSTR szInstanceName,
|
|
LPCSTR pszMetabasePath,
|
|
CHAR* pvIMDCOM )
|
|
/*++
|
|
|
|
Routine Description:
|
|
Initialize log
|
|
|
|
Arguments:
|
|
cbSize - size of the service name
|
|
RegKey - service name
|
|
dwInstanceOf - instance number
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// get the default parameters
|
|
//
|
|
|
|
m_strInstanceName.Copy(szInstanceName);
|
|
m_strMetabasePath.Copy(pszMetabasePath);
|
|
m_pvIMDCOM = (LPVOID)pvIMDCOM;
|
|
|
|
//
|
|
// get the registry value
|
|
//
|
|
|
|
(VOID)GetRegParameters(
|
|
pszMetabasePath,
|
|
pvIMDCOM );
|
|
|
|
return 0;
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
STDMETHODIMP
|
|
CLogFileCtrl::TerminateLog(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
clean up the log
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
Lock( );
|
|
|
|
m_fInTerminate = TRUE;
|
|
|
|
if ( m_pLogFile!=NULL) {
|
|
m_pLogFile->CloseFile( );
|
|
delete m_pLogFile;
|
|
m_pLogFile = NULL;
|
|
}
|
|
|
|
if (m_dwSchedulerCookie)
|
|
{
|
|
RemoveWorkItem(m_dwSchedulerCookie);
|
|
}
|
|
|
|
m_dwSchedulerCookie = 0;
|
|
|
|
m_fInTerminate = FALSE;
|
|
|
|
Unlock( );
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
STDMETHODIMP
|
|
CLogFileCtrl::LogInformation(
|
|
IInetLogInformation * ppvDataObj
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
log information
|
|
|
|
Arguments:
|
|
ppvDataObj - COM Logging object
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
SYSTEMTIME stNow;
|
|
|
|
CHAR tmpBuf[512];
|
|
DWORD dwSize = sizeof(tmpBuf);
|
|
PCHAR pBuf = tmpBuf;
|
|
DWORD err;
|
|
|
|
retry:
|
|
|
|
err = NO_ERROR;
|
|
|
|
if ( FormatLogBuffer(ppvDataObj,
|
|
pBuf,
|
|
&dwSize,
|
|
&stNow // time is returned
|
|
)
|
|
)
|
|
{
|
|
WriteLogInformation(stNow, pBuf, dwSize, FALSE, FALSE);
|
|
}
|
|
else
|
|
{
|
|
|
|
err = GetLastError();
|
|
|
|
IIS_PRINTF((buff,"FormatLogBuffer failed with %d\n",GetLastError()));
|
|
|
|
if ( (err == ERROR_INSUFFICIENT_BUFFER) &&
|
|
( pBuf == tmpBuf ) &&
|
|
(dwSize <= MAX_LOG_RECORD_LEN) )
|
|
{
|
|
|
|
pBuf = (PCHAR)LocalAlloc( 0, dwSize );
|
|
|
|
if ( pBuf != NULL )
|
|
{
|
|
goto retry;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (pBuf != tmpBuf) && (pBuf != NULL) )
|
|
{
|
|
LocalFree( pBuf );
|
|
}
|
|
|
|
return(0);
|
|
|
|
} // LogInformation
|
|
|
|
//************************************************************************************
|
|
|
|
STDMETHODIMP
|
|
CLogFileCtrl::GetConfig( DWORD, BYTE * log)
|
|
/*++
|
|
|
|
Routine Description:
|
|
get configuration information
|
|
|
|
Arguments:
|
|
cbSize - size of the data structure
|
|
log - log configuration data structure
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
InternalGetConfig( (PINETLOG_CONFIGURATIONA)log );
|
|
return(0L);
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
STDMETHODIMP
|
|
CLogFileCtrl::QueryExtraLoggingFields(
|
|
IN PDWORD pcbSize,
|
|
PCHAR pszFieldsList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
get configuration information
|
|
|
|
Arguments:
|
|
cbSize - size of the data structure
|
|
log - log configuration data structure
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
InternalGetExtraLoggingFields( pcbSize, pszFieldsList );
|
|
return(0L);
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
STDMETHODIMP
|
|
CLogFileCtrl::LogCustomInformation(
|
|
IN DWORD,
|
|
IN PCUSTOM_LOG_DATA,
|
|
IN LPSTR
|
|
)
|
|
{
|
|
return(0L);
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
void
|
|
CLogFileCtrl::InternalGetExtraLoggingFields(
|
|
PDWORD pcbSize,
|
|
TCHAR *pszFieldsList
|
|
)
|
|
{
|
|
pszFieldsList[0]=_T('\0');
|
|
pszFieldsList[1]=_T('\0');
|
|
*pcbSize = 2;
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
VOID
|
|
CLogFileCtrl::InternalGetConfig(
|
|
IN PINETLOG_CONFIGURATIONA pLogConfig
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
internal; get configuration information function.
|
|
|
|
Arguments:
|
|
log - log configuration data structure
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
pLogConfig->inetLogType = INET_LOG_TO_FILE;
|
|
strcpy(
|
|
pLogConfig->u.logFile.rgchLogFileDirectory,
|
|
QueryLogFileDirectory()
|
|
);
|
|
|
|
pLogConfig->u.logFile.cbSizeForTruncation = QuerySizeForTruncation();
|
|
pLogConfig->u.logFile.ilPeriod = QueryPeriod();
|
|
pLogConfig->u.logFile.ilFormat = QueryLogFormat();
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
STDMETHODIMP
|
|
CLogFileCtrl::SetConfig(
|
|
DWORD,
|
|
BYTE * log
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
set the log configuration information
|
|
|
|
Arguments:
|
|
cbSize - size of the configuration data structure
|
|
log - log information
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// write the configuration information to the registry
|
|
//
|
|
|
|
PINETLOG_CONFIGURATIONA pLogConfig = (PINETLOG_CONFIGURATIONA)log;
|
|
SetSizeForTruncation( pLogConfig->u.logFile.cbSizeForTruncation );
|
|
SetPeriod( pLogConfig->u.logFile.ilPeriod );
|
|
SetLogFileDirectory( pLogConfig->u.logFile.rgchLogFileDirectory );
|
|
return(0L);
|
|
} // CLogFileCtrl::SetConfig
|
|
|
|
//************************************************************************************
|
|
|
|
DWORD
|
|
CLogFileCtrl::GetRegParameters(
|
|
IN LPCSTR pszRegKey,
|
|
IN LPVOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
get the registry value
|
|
|
|
Arguments:
|
|
strRegKey - registry key
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
|
|
DWORD err = NO_ERROR;
|
|
MB mb( (IMDCOM*) m_pvIMDCOM );
|
|
DWORD dwSize;
|
|
CHAR szTmp[MAX_PATH+1];
|
|
DWORD cbTmp = sizeof(szTmp);
|
|
CHAR buf[MAX_PATH+1];
|
|
DWORD dwPeriod;
|
|
|
|
if ( !mb.Open("") ) {
|
|
err = GetLastError();
|
|
return(err);
|
|
}
|
|
|
|
//
|
|
// Get log file period
|
|
//
|
|
|
|
if ( mb.GetDword(
|
|
pszRegKey,
|
|
MD_LOGFILE_PERIOD,
|
|
IIS_MD_UT_SERVER,
|
|
&dwPeriod ) )
|
|
{
|
|
//
|
|
// Make sure it is within bounds
|
|
//
|
|
|
|
if ( dwPeriod > INET_LOG_PERIOD_HOURLY )
|
|
{
|
|
IIS_PRINTF((buff,"Invalid log period %d, set to %d\n",
|
|
dwPeriod, DEFAULT_LOG_FILE_PERIOD));
|
|
|
|
dwPeriod = DEFAULT_LOG_FILE_PERIOD;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
dwPeriod = DEFAULT_LOG_FILE_PERIOD;
|
|
}
|
|
|
|
SetPeriod( dwPeriod );
|
|
|
|
//
|
|
// Get truncate size
|
|
//
|
|
|
|
if ( dwPeriod == INET_LOG_PERIOD_NONE )
|
|
{
|
|
|
|
SetSizeForTruncation ( DEFAULT_LOG_FILE_TRUNCATE_SIZE );
|
|
|
|
if ( mb.GetDword( pszRegKey,
|
|
MD_LOGFILE_TRUNCATE_SIZE,
|
|
IIS_MD_UT_SERVER,
|
|
&dwSize ) )
|
|
{
|
|
|
|
if ( dwSize < MIN_FILE_TRUNCATION_SIZE )
|
|
{
|
|
dwSize = MIN_FILE_TRUNCATION_SIZE;
|
|
IIS_PRINTF((buff,
|
|
"Setting truncation size to %d\n", dwSize));
|
|
}
|
|
|
|
SetSizeForTruncation( dwSize );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetSizeForTruncation( NO_FILE_TRUNCATION );
|
|
}
|
|
|
|
//
|
|
// Get directory
|
|
//
|
|
|
|
if ( !mb.GetExpandString(
|
|
pszRegKey,
|
|
MD_LOGFILE_DIRECTORY,
|
|
IIS_MD_UT_SERVER,
|
|
szTmp,
|
|
&cbTmp ) )
|
|
{
|
|
lstrcpy(szTmp,
|
|
DEFAULT_LOG_FILE_DIRECTORY_NT );
|
|
}
|
|
|
|
mb.Close();
|
|
|
|
ExpandEnvironmentStrings( szTmp, buf, MAX_PATH+1 );
|
|
SetLogFileDirectory( buf );
|
|
|
|
return(err);
|
|
|
|
} // CLogFileCtrl::GetRegParameters
|
|
|
|
//************************************************************************************
|
|
|
|
|
|
BOOL
|
|
CLogFileCtrl::OpenLogFile(
|
|
IN PSYSTEMTIME pst
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
internal routine to open file.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
BOOL fReturn = TRUE;
|
|
BOOL bRet = FALSE;
|
|
HANDLE hToken = NULL;
|
|
DWORD dwError = NO_ERROR;
|
|
CHAR rgchPath[ MAX_PATH + 1 + 32];
|
|
|
|
if ( m_pLogFile != NULL) {
|
|
|
|
//
|
|
// already a log file is open. return silently
|
|
//
|
|
|
|
IIS_PRINTF( ( buff,
|
|
" Log File %s is already open ( %p)\n",
|
|
m_strLogFileName.QueryStr(), m_pLogFile));
|
|
|
|
} else {
|
|
|
|
//
|
|
// If this the first time we opened, get the file name
|
|
//
|
|
|
|
if ( m_fFirstLog || (QueryPeriod() != INET_LOG_PERIOD_NONE) ) {
|
|
m_fFirstLog = FALSE;
|
|
FormNewLogFileName( pst );
|
|
}
|
|
|
|
//
|
|
// Append log file name to path to form the path of file to be opened.
|
|
//
|
|
|
|
if ( (m_strLogFileName.QueryCCH() +
|
|
m_strLogFileDirectory.QueryCCH() >= MAX_PATH) ||
|
|
(m_strLogFileDirectory.QueryCCH() < 3) ) {
|
|
|
|
fReturn = FALSE;
|
|
|
|
if ( (g_eventLog != NULL) && !m_fDiskFullShutdown) {
|
|
|
|
const CHAR* tmpString[1];
|
|
tmpString[0] = rgchPath;
|
|
g_eventLog->LogEvent(
|
|
LOG_EVENT_CREATE_DIR_ERROR,
|
|
1,
|
|
tmpString,
|
|
ERROR_BAD_PATHNAME );
|
|
}
|
|
SetLastError( ERROR_BAD_PATHNAME );
|
|
goto exit;
|
|
}
|
|
|
|
lstrcpy( rgchPath, QueryLogFileDirectory());
|
|
// if ( rgchPath[strlen(rgchPath)-1] != '\\' ) {
|
|
|
|
if ( *CharPrev(rgchPath, rgchPath + strlen(rgchPath)) != '\\' ) {
|
|
lstrcat( rgchPath, "\\");
|
|
}
|
|
lstrcat( rgchPath, QueryInstanceName() );
|
|
|
|
//
|
|
// There is a small chance that this function could be called (indirectly)
|
|
// from an INPROC ISAPI completion thread (HSE_REQ_DONE). In this case
|
|
// the thread token is the impersonated user and may not have permissions
|
|
// to open the log file (especially if the user is the IUSR_ account).
|
|
// To be paranoid, let's revert to LOCAL_SYSTEM anyways before opening.
|
|
//
|
|
|
|
if ( OpenThreadToken( GetCurrentThread(),
|
|
TOKEN_ALL_ACCESS,
|
|
FALSE,
|
|
&hToken ) )
|
|
{
|
|
DBG_ASSERT( hToken != NULL );
|
|
RevertToSelf();
|
|
}
|
|
|
|
// Allow logging to mapped drives
|
|
|
|
bRet = IISCreateDirectory( rgchPath, TRUE );
|
|
dwError = GetLastError();
|
|
|
|
if ( hToken != NULL )
|
|
{
|
|
SetThreadToken( NULL, hToken );
|
|
SetLastError( dwError );
|
|
}
|
|
|
|
if ( !bRet ) {
|
|
|
|
if ( (g_eventLog != NULL) && !m_fDiskFullShutdown) {
|
|
|
|
const CHAR* tmpString[1];
|
|
tmpString[0] = rgchPath;
|
|
g_eventLog->LogEvent(
|
|
LOG_EVENT_CREATE_DIR_ERROR,
|
|
1,
|
|
tmpString,
|
|
GetLastError()
|
|
);
|
|
}
|
|
|
|
IIS_PRINTF((buff,"IISCreateDir[%s] error %d\n",
|
|
rgchPath, GetLastError()));
|
|
fReturn = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
lstrcat( rgchPath, "\\");
|
|
lstrcat( rgchPath, m_strLogFileName.QueryStr());
|
|
|
|
m_pLogFile = new ILOG_FILE( );
|
|
|
|
if (m_pLogFile != NULL) {
|
|
|
|
if ( m_pLogFile->Open(
|
|
rgchPath,
|
|
QuerySizeForTruncation(),
|
|
!m_fDiskFullShutdown
|
|
) ) {
|
|
|
|
m_pLogFile->QueryFileSize(&m_cbTotalWritten);
|
|
} else {
|
|
|
|
delete m_pLogFile;
|
|
m_pLogFile = NULL;
|
|
fReturn = FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
IIS_PRINTF((buff,"Unable to allocate ILOG_FILE[err %d]\n",
|
|
GetLastError()));
|
|
|
|
fReturn = FALSE;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
return ( fReturn);
|
|
|
|
} // CLogFileCtrl::OpenLogFile
|
|
|
|
//************************************************************************************
|
|
|
|
|
|
BOOL
|
|
CLogFileCtrl::WriteLogDirectives(
|
|
IN DWORD Sludge
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
virtual function for the sub class to log directives to the file.
|
|
|
|
Arguments:
|
|
|
|
Sludge - number of additional bytes that needs to be written
|
|
together with the directives
|
|
|
|
Return Value:
|
|
|
|
TRUE, ok
|
|
FALSE, not enough space to write.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// if we will overflow, open another file
|
|
//
|
|
|
|
if ( IsFileOverFlowForCB( Sludge ) ) {
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Unable to write directive\n"));
|
|
return(FALSE);
|
|
}
|
|
|
|
return TRUE;
|
|
} // CLogFileCtrl::WriteLogDirectives
|
|
|
|
//************************************************************************************
|
|
|
|
BOOL
|
|
CLogFileCtrl::WriteCustomLogDirectives(
|
|
IN DWORD
|
|
)
|
|
{
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
VOID
|
|
CLogFileCtrl::I_FormNewLogFileName(
|
|
IN LPSYSTEMTIME pstNow,
|
|
IN LPCSTR LogNamePrefix
|
|
)
|
|
{
|
|
|
|
CHAR tmpBuf[MAX_PATH+1];
|
|
|
|
WORD wYear = ( pstNow->wYear % 100); // retain just last 2 digits.
|
|
|
|
switch ( QueryPeriod( ) ) {
|
|
|
|
case INET_LOG_PERIOD_HOURLY:
|
|
|
|
wsprintf( tmpBuf, "%.2s%02.2u%02u%02u%02u.%s",
|
|
LogNamePrefix,
|
|
wYear,
|
|
pstNow->wMonth,
|
|
pstNow->wDay,
|
|
pstNow->wHour,
|
|
DEFAULT_LOG_FILE_EXTENSION);
|
|
break;
|
|
|
|
case INET_LOG_PERIOD_DAILY:
|
|
|
|
wsprintf( tmpBuf, "%.2s%02.2u%02u%02u.%s",
|
|
LogNamePrefix,
|
|
wYear,
|
|
pstNow->wMonth,
|
|
pstNow->wDay,
|
|
DEFAULT_LOG_FILE_EXTENSION);
|
|
break;
|
|
|
|
case INET_LOG_PERIOD_WEEKLY:
|
|
|
|
wsprintf( tmpBuf, "%.2s%02.2u%02u%02u.%s",
|
|
LogNamePrefix,
|
|
wYear,
|
|
pstNow->wMonth,
|
|
WeekOfMonth(pstNow),
|
|
DEFAULT_LOG_FILE_EXTENSION);
|
|
break;
|
|
|
|
case INET_LOG_PERIOD_MONTHLY:
|
|
|
|
wsprintf( tmpBuf, "%.2s%02u%02u.%s",
|
|
LogNamePrefix,
|
|
wYear,
|
|
pstNow->wMonth,
|
|
DEFAULT_LOG_FILE_EXTENSION);
|
|
break;
|
|
|
|
case INET_LOG_PERIOD_NONE:
|
|
default:
|
|
|
|
wsprintf(tmpBuf, "%.6s%u.%s",
|
|
LogNamePrefix,
|
|
m_sequence,
|
|
DEFAULT_LOG_FILE_EXTENSION);
|
|
|
|
m_sequence++;
|
|
break;
|
|
|
|
} // switch()
|
|
|
|
m_strLogFileName.Copy(tmpBuf);
|
|
|
|
return;
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
VOID
|
|
CLogFileCtrl::SetLogFileDirectory(
|
|
IN LPCSTR pszDir
|
|
)
|
|
{
|
|
|
|
STR tmpStr;
|
|
HANDLE hFile;
|
|
WIN32_FIND_DATA findData;
|
|
DWORD maxFileSize = 0;
|
|
|
|
m_strLogFileDirectory.Copy(pszDir);
|
|
|
|
//
|
|
// if period is not none, then return
|
|
//
|
|
|
|
if ( QueryPeriod() != INET_LOG_PERIOD_NONE ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the starting sequence number
|
|
//
|
|
|
|
m_sequence = 1;
|
|
|
|
//
|
|
// Append instance name and the pattern.
|
|
// should look like c:\winnt\system32\logfiles\w3svc1\inetsv*.log
|
|
//
|
|
|
|
tmpStr.Copy(pszDir);
|
|
// if ( pszDir[tmpStr.QueryCCH()-1] != '\\' ) {
|
|
if ( *CharPrev(pszDir, pszDir + tmpStr.QueryCCH()) != '\\' ) {
|
|
tmpStr.Append("\\");
|
|
}
|
|
tmpStr.Append( QueryInstanceName() );
|
|
tmpStr.Append( "\\" );
|
|
tmpStr.Append( QueryNoPeriodPattern() );
|
|
|
|
hFile = FindFirstFile( tmpStr.QueryStr(), &findData );
|
|
if ( hFile == INVALID_HANDLE_VALUE ) {
|
|
return;
|
|
}
|
|
|
|
do {
|
|
|
|
PCHAR ptr;
|
|
DWORD sequence = 1;
|
|
|
|
ptr = strchr(findData.cFileName, '.');
|
|
if (ptr != NULL ) {
|
|
*ptr = '\0';
|
|
ptr = findData.cFileName;
|
|
|
|
while ( *ptr != '\0' ) {
|
|
|
|
if ( isdigit((UCHAR)(*ptr)) ) {
|
|
sequence = atoi( ptr );
|
|
break;
|
|
}
|
|
ptr++;
|
|
}
|
|
|
|
if ( sequence > m_sequence ) {
|
|
maxFileSize = findData.nFileSizeLow;
|
|
m_sequence = sequence;
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Sequence start is %d[%d]\n", sequence, maxFileSize));
|
|
}
|
|
}
|
|
|
|
} while ( FindNextFile( hFile, &findData ) );
|
|
|
|
FindClose(hFile);
|
|
|
|
if ( (maxFileSize+LOG_FILE_SLOP) > QuerySizeForTruncation() ) {
|
|
m_sequence++;
|
|
}
|
|
|
|
return;
|
|
|
|
} // SetLogFileDirectory
|
|
|
|
//************************************************************************************
|
|
|
|
VOID
|
|
CLogFileCtrl::WriteLogInformation(
|
|
IN SYSTEMTIME& stNow,
|
|
IN PCHAR pBuf,
|
|
IN DWORD dwSize,
|
|
IN BOOL fCustom,
|
|
IN BOOL fResetHeaders
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
write log line to file
|
|
|
|
Arguments:
|
|
stNow Present Time
|
|
fResetHeaders TRUE -> Reset headers, FALSE -> Don't reset headers
|
|
pBuf Pointer to Log Line
|
|
dwSize Number of characters in pBuf
|
|
fCustom TRUE -> Using custom logging, FALSE -> normal logging
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
|
|
BOOL fOpenNewFile;
|
|
DWORD err = NO_ERROR;
|
|
DWORD tickCount = 0;
|
|
|
|
|
|
Lock ( );
|
|
|
|
if ( m_pLogFile != NULL )
|
|
{
|
|
if ( QueryPeriod() == INET_LOG_PERIOD_DAILY )
|
|
{
|
|
fOpenNewFile = (m_stCurrentFile.wDay != stNow.wDay) ||
|
|
(m_stCurrentFile.wMonth != stNow.wMonth);
|
|
}
|
|
else
|
|
{
|
|
fOpenNewFile = IsBeginningOfNewPeriod( QueryPeriod(),
|
|
&m_stCurrentFile,
|
|
&stNow) ||
|
|
IsFileOverFlowForCB( dwSize);
|
|
|
|
//
|
|
// Reset headers if day is over. Used for weekly or unlimited files.
|
|
//
|
|
|
|
if ( !fOpenNewFile && !fResetHeaders)
|
|
{
|
|
fResetHeaders = (m_stCurrentFile.wDay != stNow.wDay) ||
|
|
(m_stCurrentFile.wMonth != stNow.wMonth);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fOpenNewFile = TRUE;
|
|
}
|
|
|
|
if (fOpenNewFile )
|
|
{
|
|
|
|
//
|
|
// open a file only after every minute when we hit disk full
|
|
//
|
|
|
|
if ( m_TickResumeOpen != 0 )
|
|
{
|
|
tickCount = GetTickCount( );
|
|
|
|
if ( (tickCount < m_TickResumeOpen) ||
|
|
((tickCount + TICK_MINUTE) < tickCount ) ) // The Tick counter is about to wrap.
|
|
{
|
|
goto exit_tick;
|
|
}
|
|
}
|
|
|
|
retry_open:
|
|
|
|
//
|
|
// Close existing log
|
|
//
|
|
|
|
TerminateLog();
|
|
|
|
//
|
|
// Open new log file
|
|
//
|
|
|
|
if ( OpenLogFile( &stNow ) )
|
|
{
|
|
//
|
|
// Schedule Callback for closing log file and set flag for writing directives.
|
|
//
|
|
|
|
ScheduleCallback(stNow);
|
|
|
|
fResetHeaders = TRUE;
|
|
}
|
|
else
|
|
{
|
|
err = GetLastError();
|
|
|
|
//
|
|
// The file is already bigger than the truncate size
|
|
// try another one.
|
|
//
|
|
|
|
if ( err == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
FormNewLogFileName( &stNow );
|
|
err = NO_ERROR;
|
|
goto retry_open;
|
|
}
|
|
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset Headers if needed
|
|
//
|
|
|
|
if ((fResetHeaders) || (fCustom != m_fUsingCustomHeaders))
|
|
{
|
|
BOOL fSucceeded;
|
|
|
|
if (fCustom)
|
|
{
|
|
m_fUsingCustomHeaders = TRUE;
|
|
fSucceeded = WriteCustomLogDirectives(dwSize);
|
|
}
|
|
else
|
|
{
|
|
m_fUsingCustomHeaders = FALSE;
|
|
fSucceeded = WriteLogDirectives(dwSize);
|
|
}
|
|
|
|
if (!fSucceeded)
|
|
{
|
|
err = GetLastError( );
|
|
|
|
if ( err == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
FormNewLogFileName( &stNow );
|
|
err = NO_ERROR;
|
|
goto retry_open;
|
|
}
|
|
|
|
TerminateLog();
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// record the time of opening of this new file
|
|
//
|
|
|
|
m_stCurrentFile = stNow;
|
|
}
|
|
|
|
//
|
|
// write it to the buffer
|
|
//
|
|
|
|
if ( m_pLogFile->Write(pBuf, dwSize) )
|
|
{
|
|
IncrementBytesWritten(dwSize);
|
|
|
|
//
|
|
// If this had been shutdown, log event for reactivation
|
|
//
|
|
|
|
if ( m_fDiskFullShutdown )
|
|
{
|
|
m_fDiskFullShutdown = FALSE;
|
|
m_TickResumeOpen = 0;
|
|
|
|
LogWriteEvent( QueryInstanceName(), TRUE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
err = GetLastError();
|
|
TerminateLog( );
|
|
}
|
|
|
|
exit:
|
|
|
|
if ( err == ERROR_DISK_FULL )
|
|
{
|
|
if ( !m_fDiskFullShutdown )
|
|
{
|
|
m_fDiskFullShutdown = TRUE;
|
|
LogWriteEvent( QueryInstanceName(), FALSE );
|
|
}
|
|
|
|
m_TickResumeOpen = GetTickCount();
|
|
m_TickResumeOpen += TICK_MINUTE;
|
|
}
|
|
|
|
exit_tick:
|
|
|
|
Unlock( );
|
|
|
|
} // LogInformation
|
|
|
|
|
|
//************************************************************************************
|
|
|
|
DWORD
|
|
CLogFileCtrl::ScheduleCallback(SYSTEMTIME& stNow)
|
|
{
|
|
DWORD dwTimeRemaining = 0;
|
|
|
|
switch (m_dwPeriod)
|
|
{
|
|
case INET_LOG_PERIOD_HOURLY:
|
|
dwTimeRemaining = 60*60 -
|
|
(stNow.wMinute*60 +
|
|
stNow.wSecond);
|
|
break;
|
|
|
|
case INET_LOG_PERIOD_DAILY:
|
|
dwTimeRemaining = 24*60*60 -
|
|
(stNow.wHour*60*60 +
|
|
stNow.wMinute*60 +
|
|
stNow.wSecond);
|
|
break;
|
|
|
|
case INET_LOG_PERIOD_WEEKLY:
|
|
dwTimeRemaining = 7*24*60*60 -
|
|
(stNow.wDayOfWeek*24*60*60 +
|
|
stNow.wHour*60*60 +
|
|
stNow.wMinute*60 +
|
|
stNow.wSecond);
|
|
break;
|
|
|
|
case INET_LOG_PERIOD_MONTHLY:
|
|
|
|
DWORD dwNumDays = 31;
|
|
|
|
if ( (4 == stNow.wMonth) || // April
|
|
(6 == stNow.wMonth) || // June
|
|
(9 == stNow.wMonth) || // September
|
|
(11 == stNow.wMonth) // November
|
|
)
|
|
{
|
|
dwNumDays = 30;
|
|
}
|
|
|
|
if (2 == stNow.wMonth) // February
|
|
{
|
|
if ((stNow.wYear % 4 == 0 && stNow.wYear % 100 != 0) || stNow.wYear % 400 == 0)
|
|
{
|
|
//
|
|
// leap year.
|
|
//
|
|
|
|
dwNumDays = 29;
|
|
}
|
|
else
|
|
{
|
|
dwNumDays = 28;
|
|
}
|
|
}
|
|
|
|
dwTimeRemaining = dwNumDays*24*60*60 -
|
|
(stNow.wDay*24*60*60 +
|
|
stNow.wHour*60*60 +
|
|
stNow.wMinute*60 +
|
|
stNow.wSecond);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Convert remaining time to millisecs
|
|
//
|
|
|
|
dwTimeRemaining = dwTimeRemaining*1000 - stNow.wMilliseconds;
|
|
|
|
if (dwTimeRemaining)
|
|
{
|
|
m_dwSchedulerCookie = ScheduleWorkItem(
|
|
LoggingSchedulerCallback,
|
|
this,
|
|
dwTimeRemaining,
|
|
FALSE);
|
|
}
|
|
|
|
return(m_dwSchedulerCookie);
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
CHAR * SkipWhite( CHAR * pch )
|
|
{
|
|
while ( ISWHITEA( *pch ) )
|
|
{
|
|
pch++;
|
|
}
|
|
|
|
return pch;
|
|
}
|
|
|
|
//************************************************************************************
|
|
|
|
DWORD
|
|
FastDwToA(
|
|
CHAR* pBuf,
|
|
DWORD dwV
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Convert DWORD to ascii (decimal )
|
|
returns length ( w/o trailing '\0' )
|
|
|
|
Arguments:
|
|
pBuf - buffer where to store converted value
|
|
dwV - value to convert
|
|
|
|
Return Value:
|
|
length of ascii string
|
|
|
|
--*/
|
|
{
|
|
DWORD v;
|
|
|
|
if ( dwV < 10 ) {
|
|
pBuf[0] = (CHAR)('0'+dwV);
|
|
pBuf[1] = '\0';
|
|
return 1;
|
|
} else if ( dwV < 100 ) {
|
|
pBuf[0] = (CHAR)((dwV/10) + '0');
|
|
pBuf[1] = (CHAR)((dwV%10) + '0');
|
|
pBuf[2] = '\0';
|
|
return 2;
|
|
} else if ( dwV < 1000 ) {
|
|
pBuf[0] = (CHAR)((v=dwV/100) + '0');
|
|
dwV -= v * 100;
|
|
pBuf[1] = (CHAR)((dwV/10) + '0');
|
|
pBuf[2] = (CHAR)((dwV%10) + '0');
|
|
pBuf[3] = '\0';
|
|
return 3;
|
|
} else if ( dwV < 10000 ) {
|
|
|
|
pBuf[0] = (CHAR)((v=dwV/1000) + '0');
|
|
dwV -= v * 1000;
|
|
pBuf[1] = (CHAR)((v=dwV/100) + '0');
|
|
dwV -= v * 100;
|
|
pBuf[2] = (CHAR)((dwV/10) + '0');
|
|
pBuf[3] = (CHAR)((dwV%10) + '0');
|
|
pBuf[4] = '\0';
|
|
return 4;
|
|
}
|
|
|
|
_ultoa(dwV, pBuf, 10);
|
|
return (DWORD)strlen(pBuf);
|
|
|
|
} // FastDwToA
|
|
|
|
//************************************************************************************
|
|
|
|
VOID
|
|
LogWriteEvent(
|
|
IN LPCSTR InstanceName,
|
|
IN BOOL fResume
|
|
)
|
|
{
|
|
if ( g_eventLog != NULL ) {
|
|
|
|
const CHAR* tmpString[1];
|
|
tmpString[0] = InstanceName;
|
|
|
|
g_eventLog->LogEvent(
|
|
fResume ?
|
|
LOG_EVENT_RESUME_LOGGING :
|
|
LOG_EVENT_DISK_FULL_SHUTDOWN,
|
|
1,
|
|
tmpString,
|
|
0);
|
|
}
|
|
return;
|
|
} // LogWriteEvent
|
|
|
|
//************************************************************************************
|
|
|
|
VOID WINAPI LoggingSchedulerCallback( PVOID pContext)
|
|
{
|
|
CLogFileCtrl *pLog = (CLogFileCtrl *) pContext;
|
|
|
|
//
|
|
// There is a possibility of deadlock if another thread is inside TerminateLog
|
|
// stuck in RemoveWorkItem, waiting for this callback thread to complete. To
|
|
// prevent that we use the synchronization flag - m_fInTerminate.
|
|
//
|
|
|
|
pLog->m_dwSchedulerCookie = 0;
|
|
|
|
if (!pLog->m_fInTerminate)
|
|
{
|
|
pLog->TerminateLog();
|
|
}
|
|
}
|
|
|
|
//************************************************************************************
|
|
|