Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

873 lines
23 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name :
ilogaux.cxx
Abstract:
This module supplies the auxiliary functions required for
Internet services common Logging module.
Author:
Murali R. Krishnan ( MuraliK ) 06-Oct-1995
Environment:
Win32 -- User Mode
Project:
Internet Services Common DLL
Functions Exported:
Revision History:
--*/
/************************************************************
* Include Headers
************************************************************/
# include "tcpdllp.hxx"
# include "inetlog.h"
# include "ilogcls.hxx"
# define MAX_ODBC_ERROR_LEN (200)
static VOID
SendOdbcOpenFailedToEventLog(IN DWORD dwError,
IN EVENT_LOG * pEventLog,
IN LPCWSTR rgchDataSource,
IN LPCWSTR rgchTableName,
IN LPCWSTR rgchUserName,
IN LPCWSTR pszError);
/************************************************************
* Functions
************************************************************/
INETLOG_CONTEXT::INETLOG_CONTEXT( IN LPCWSTR pszServiceName,
IN EVENT_LOG * pEventLog,
IN PINET_BASIC_LOG pilNew)
/*++
This function creates a new context object for inetlog.
It uses the pilNew as the starting log object and creates an
entry for the ILREF_LOG object for the same.
--*/
: m_currentSerialNumber ( FIRST_SERIAL_NUMBER),
m_refLog1 ( FIRST_SERIAL_NUMBER, pilNew),
m_refLog2 ( INVALID_SERIAL_NUMBER, NULL),
m_pEventLog ( pEventLog)
{
CopyUnicodeStringToBuffer( m_rgchServiceName, MAX_SERVICE_NAME_LEN,
pszServiceName);
m_fValid = ( pilNew != NULL);
m_pilRefCurrent = &m_refLog1; // use refLog1 as starting reflog object.
m_refLog1.cRefs = 1;
return;
} // INETLOG_CONTEXT::INETLOG_CONTEXT()
INETLOG_CONTEXT::~INETLOG_CONTEXT()
/*++
This function cleans up the INETLOG_CONTEXT object freeing up
the log object embedded after checking for the validity of
new log object.
This function should be called after all the users of TsLogInformation()
using this context quit.
This function is not multithread safe.
--*/
{
PILREF_LOG piRef = m_pilRefCurrent;
m_pilRefCurrent = NULL; // fine, since no body is expected to use it
DBG_ASSERT( piRef->cRefs == 1); // the ref count for valid object.
InterlockedDecrement(&piRef->cRefs);
if ( piRef->piLog != NULL) {
delete piRef->piLog;
piRef->piLog = NULL;
}
return;
} // INETLOG_CONTEXT::~INETLOG_CONTEXT()
PILREF_LOG
INETLOG_CONTEXT::AcquireCurrentPinetForRead( VOID)
{
PILREF_LOG pilRef;
for(;;) {
pilRef = m_pilRefCurrent;
DBG_REQUIRE( InterlockedIncrement( &pilRef->cRefs) > 0);
DBG_ASSERT( pilRef->cRefs > 1);
if ( pilRef->serialNumber == m_currentSerialNumber) {
break;
}
// we failed to get valid pilRef. give up and try again.
DBG_REQUIRE( InterlockedDecrement( &pilRef->cRefs) > 0);
IF_DEBUG( INETLOG) {
DBGPRINTF(( DBG_CONTEXT,
"Unable to get ptr to log object. Will sleep\n"));
}
Sleep( GET_CURRENT_PINET_SLEEP_INTERVAL); // sleep for some time.
} // for
DBG_ASSERT( pilRef != NULL);
return (pilRef);
} // INETLOG_CONTEXT::AcquireCurrentPinetForRead()
VOID
INETLOG_CONTEXT::ReleasePinet( IN OUT PILREF_LOG pilRef)
/*++
This function releases the acquired log object for future release or
for reuse.
The caller should not use the pointer pil after calling this function.
--*/
{
DBG_ASSERT( pilRef == &m_refLog1 || pilRef == &m_refLog2);
DBG_REQUIRE( InterlockedDecrement( &pilRef->cRefs) > 0);
return;
} // INETLOG_CONTEXT::ReleasePinet()
BOOL
INETLOG_CONTEXT::IsInUse(VOID)
/*++
This function checks to see if this context is in use by some thread.
It is required to check to see if this object is free before calling
cleanup for this object.
Logic: Initially when the object is created, we assign a seed ref of 1.
Now we acquire the object and check to see if the value is 2.
( 2 since acquire bumps up the ref count).
If it is 2, then we are guaranteed that this object is free. Release it and
==> return FALSE.
Otherwise this object is in use by some thread. Release the object
==> return TRUE.
Arguments:
None
Returns:
TRUE if object is in use and FALSE if it not in use.
--*/
{
PILREF_LOG pilRef = AcquireCurrentPinetForRead();
BOOL fInUse;
// Verify all the threads are out and not using this object.
fInUse = (pilRef->cRefs > 2);
ReleasePinet(pilRef);
return ( fInUse);
} // INETLOG_CONTEXT::IsInUse()
BOOL
INETLOG_CONTEXT::SetNewPinetLog( IN PINET_BASIC_LOG pilNew)
/*++
This function replaces the old pointer to log object with a new one(given).
This update is made with care so that we do not run into deadlock or race.
Since we have a guarantee that there will be one writer,
update the values freely.
--*/
{
PILREF_LOG pilRefOld;
PILREF_LOG pilRefNew;
DWORD tmpSerialNumber;
IF_DEBUG( INETLOG) {
DBGPRINTF(( DBG_CONTEXT,
"New Basic Log object = %08x\n",
pilNew));
}
// Set the new and old Ref object pointers.
pilRefOld = m_pilRefCurrent;
pilRefNew = (pilRefOld == &m_refLog1) ? &m_refLog2 : &m_refLog1;
// set values in pilRefNew
pilRefNew->serialNumber = INVALID_SERIAL_NUMBER;
pilRefNew->cRefs = 1; // since newly created.
pilRefNew->piLog = pilNew;
//
// invalidate the old log object's serial number
// This will cause all the new entrants of AcquireCurrentPinetForRead()
// to block and loop till they get the proper log object.
//
m_pilRefCurrent->serialNumber = INVALID_SERIAL_NUMBER;
//
// Reset the current log entry pointer to point to new one
//
m_pilRefCurrent = pilRefNew;
m_fValid = (pilNew != NULL);
//
// Now both pilNew and pilOld should be having invalid serial numbers
// ==> No AcquireCurrentPinetLog() can succeed.
// That is good. Now we can set up things such that these succeed.
//
// Compute a new serial number to be used for this INETLOG_CONTEXT object.
tmpSerialNumber = m_currentSerialNumber;
tmpSerialNumber = ( (tmpSerialNumber + 1 == INVALID_SERIAL_NUMBER)
? FIRST_SERIAL_NUMBER : tmpSerialNumber + 1);
DBG_ASSERT( tmpSerialNumber != INVALID_SERIAL_NUMBER);
// Set the new serial number in both current as well as New RefLog object
m_currentSerialNumber = tmpSerialNumber;
pilRefNew->serialNumber= tmpSerialNumber;
//
// Now the new and waiting threads should be able to pick up the new log
// object and use the same.
// We will loop and wait for the old log object to become unused and
// then delete it.
//
while ( pilRefOld->cRefs > 1) {
// There is still some caller who is using this object.
Sleep( GET_CURRENT_PINET_SLEEP_INTERVAL);
} // while
DBG_REQUIRE( InterlockedDecrement( &pilRefOld->cRefs) == 0);
PINET_BASIC_LOG pilOld = pilRefOld->piLog;
pilRefOld->piLog = NULL;
DBG_ASSERT( pilRefOld->cRefs == 0 &&
pilRefOld->serialNumber == INVALID_SERIAL_NUMBER);
// delete if necessary
if ( pilOld != NULL) {
delete pilOld;
}
IF_DEBUG( INETLOG) {
DBGPRINTF(( DBG_CONTEXT,
"Pointer to LogObject switched for INETLOG_CONTEXT(%08x)"
" SerialNumber = %u\n"
" from RefOld:%08x(Log=%08x) to RefNew:%08x(Log=%08x)\n",
this, m_currentSerialNumber,
pilRefOld, pilOld,
pilRefNew, pilRefNew->piLog));
}
return (TRUE);
} // INETLOG_CONTEXT::SetNewPinetLog()
# if DBG
VOID
INETLOG_CONTEXT::Print(VOID) const
{
DBGPRINTF(( DBG_CONTEXT, "INETLOG_CONTEXT(%08x). Valid = %u; "
" Service = %ws;"
" CurrentSerialNumber = %u; CurrentRefLog = %08x\n"
" RefLog1(%08x): SerialNum(%u), cRefs(%u), PilBasicLog(%08x)\n"
" RefLog2(%08x): SerialNum(%u), cRefs(%u), PilBasicLog(%08x)\n",
this, m_fValid, m_rgchServiceName,
m_currentSerialNumber, m_pilRefCurrent,
&m_refLog1, m_refLog1.serialNumber,
m_refLog1.cRefs, m_refLog1.piLog,
&m_refLog2, m_refLog2.serialNumber,
m_refLog2.cRefs, m_refLog2.piLog
));
return;
} // INETLOG_CONTEXT::Print()
# endif // DBG
static VOID
SetServerName( IN PINET_BASIC_LOG piLog)
{
//
// Get and set the computer name as ServerName
//
WCHAR rgchServer[ MAX_SERVER_NAME_LEN] = {L'\0'};
DWORD cbServerName = MAX_SERVER_NAME_LEN;
DBG_ASSERT( piLog != NULL);
#ifndef CHICAGO
DBG_REQUIRE( GetComputerNameW( rgchServer, &cbServerName));
#else
CHAR rgchServerA[ MAX_SERVER_NAME_LEN];
DWORD cch;
DBG_REQUIRE( GetComputerNameA( (LPSTR)rgchServerA, &cbServerName));
cch = MultiByteToWideChar( CP_ACP,
MB_PRECOMPOSED,
rgchServerA,
-1,
rgchServer,
MAX_SERVER_NAME_LEN
);
#endif
piLog->SetServerName( rgchServer);
return;
} // SetServerName()
PINET_BASIC_LOG
TsCreateInetBasicLog(IN LPCWSTR pszServiceName,
IN EVENT_LOG * pEventLog,
IN const INETLOG_CONFIGURATIONW * pilConfig)
{
DWORD dwError = NO_ERROR;
PINET_BASIC_LOG piLog = NULL;
//
// Valid Configuration has been read. Construct the InetLog object.
//
switch ( pilConfig->inetLogType) {
case InetNoLog:
{
piLog = new INET_BASIC_LOG( pszServiceName, pEventLog);
if ( piLog != NULL) {
SetServerName( piLog);
} else {
dwError = ERROR_NOT_ENOUGH_MEMORY;
}
break;
} // case InetNoLog:
case InetLogToFile:
{
PINET_FILE_LOG pinetFileLog;
if ( (dwError = CheckIfPathIsDirectory(pilConfig->u.logFile.
rgchLogFileDirectory,
pEventLog))
!= NO_ERROR) {
break;
}
pinetFileLog = new INET_FILE_LOG(
pszServiceName, pEventLog,
pilConfig->u.logFile.rgchLogFileDirectory,
pilConfig->u.logFile.ilPeriod,
pilConfig->u.logFile.ilFormat );
if ( pinetFileLog != NULL && pinetFileLog->IsValid()) {
pinetFileLog->SetSizeForTruncation(
pilConfig->u.logFile.cbSizeForTruncation);
pinetFileLog->
SetLogRecordFormat(pilConfig->rgchLogRecordFormat);
SetServerName( pinetFileLog);
} else {
dwError = ERROR_NOT_ENOUGH_MEMORY;
}
piLog = (PINET_BASIC_LOG ) pinetFileLog;
break;
} // case InetLogToPeriodicFile:
case InetLogToSql:
{
PINET_SQL_LOG pinetSqlLog;
STR strError;
if ( (dwError = CheckAndLoadOdbc(pEventLog)) != NO_ERROR) {
break;
}
pinetSqlLog = new INET_SQL_LOG(
pszServiceName, pEventLog,
pilConfig->u.logSql.rgchDataSource,
pilConfig->u.logSql.rgchTableName);
if ( pinetSqlLog != NULL) {
//
// Format and computer name should be set before calling
// INET_SQL_LOG::Open().
//
pinetSqlLog->
SetLogRecordFormat(pilConfig->rgchLogRecordFormat);
SetServerName( pinetSqlLog);
dwError = pinetSqlLog->Open(pilConfig->u.logSql.rgchUserName,
pilConfig->u.logSql.rgchPassword);
if ( dwError != NO_ERROR) {
//
// Failure to open an ODBC connection.
//
LPCSTR pszError = NULL;
WCHAR pwszOdbcError[ MAX_ODBC_ERROR_LEN];
if ( pinetSqlLog->GetLastErrorText( &strError)) {
pszError = strError.QueryStr();
if ( pszError != NULL &&
strlen( pszError) < MAX_ODBC_ERROR_LEN) {
#ifdef JAPAN // BUGBUG ntbug #35293
WCHAR pwszError[ MAX_ODBC_ERROR_LEN ];
MultiByteToWideChar( CP_ACP, 0, pszError, -1, pwszError, MAX_ODBC_ERROR_LEN );
wsprintfW( pwszOdbcError, L"%s", pwszError);
#else
wsprintfW( pwszOdbcError, L"%s", pszError);
#endif
} else {
pszError = NULL;
}
}
if ( pszError == NULL) {
wsprintfW(pwszOdbcError,
L" <Unknown/Long ODBC Error Message>");
}
SendOdbcOpenFailedToEventLog(dwError,
pEventLog,
pilConfig->u.logSql.rgchDataSource,
pilConfig->u.logSql.rgchTableName,
pilConfig->u.logSql.rgchUserName,
pwszOdbcError);
DBGPRINTF( ( DBG_CONTEXT,
" Failure(%ws) to open ODBC connection.\n",
pwszOdbcError));
DBG_REQUIRE( pinetSqlLog->Close() == NO_ERROR);
delete pinetSqlLog;
pinetSqlLog = NULL;
}
} else {
dwError = ERROR_NOT_ENOUGH_MEMORY;
}
piLog = (PINET_BASIC_LOG ) pinetSqlLog;
break;
} // case InetLogToSql:
break;
default:
IF_DEBUG( INETLOG) {
DBGPRINTF( ( DBG_CONTEXT,
" Invalid LogFileType specified ( %d). \n",
pilConfig->inetLogType));
}
DBG_ASSERT( piLog == NULL);
dwError = ERROR_INVALID_PARAMETER;
break;
} // switch
if ( piLog != NULL && ! piLog->IsValid()) {
//
// Free the invalid Log handle
//
IF_DEBUG( INETLOG) {
DBGPRINTF( ( DBG_CONTEXT,
" Log Handle ( %08x) is invalid. Deleting..\n",
piLog));
DBG_CODE(piLog->Print());
}
delete piLog;
piLog = NULL;
dwError = ERROR_INVALID_PARAMETER;
}
if ( dwError != NO_ERROR) {
// Log an event telling about the failure to create log object
pEventLog->LogEvent(INET_SVC_LOG_CREATION_FAILED,
0,
(const CHAR **) NULL,
dwError);
SetLastError( dwError);
}
return (piLog);
} // TsCreateInetBasicLog()
DWORD
CheckAndLoadOdbc(IN EVENT_LOG * pEventLog)
/*++
This function checks to see if ODBC module exists and loadable.
It attempts to load ODBC modules. If there is any error, it
reports an error in the event log and returns back.
Return:
TRUE on success and FALSE if there is any failure.
--*/
{
DWORD dwError = NO_ERROR;
//
// Load odbc32.dll since we're doing SQL logging
//
if ( !LoadODBC() ) {
STR str;
const CHAR * apsz[1];
dwError = GetLastError();
str.LoadString( dwError );
apsz[0] = str.QueryStr();
pEventLog->LogEvent( INET_SVC_ODBC_DLL_LOAD_FAILED,
1,
apsz,
dwError );
DBGPRINTF( ( DBG_CONTEXT,
" Unable to load ODBC32.DLL!\n"));
}
return (dwError);
} // CheckAndLoadOdbc()
static VOID
SendOdbcOpenFailedToEventLog(IN DWORD dwError,
IN EVENT_LOG * pEventLog,
IN LPCWSTR rgchDataSource,
IN LPCWSTR rgchTableName,
IN LPCWSTR rgchUserName,
IN LPCWSTR pszOdbcError)
/*++
Forms an error message for sending to event log.
--*/
{
WCHAR * apsz[4];
apsz[0] = (WCHAR *) rgchDataSource;
apsz[1] = (WCHAR *) rgchTableName;
apsz[2] = (WCHAR *) rgchUserName;
apsz[3] = (WCHAR *) pszOdbcError;
pEventLog->LogEvent( INET_SVC_ODBC_OPEN_FAILED,
4,
apsz,
dwError );
return;
} // SendOdbcOpenFailedToEventLog()
#ifndef CHICAGO
//
// Windows NT version
//
DWORD
CheckIfPathIsDirectory(IN LPCWSTR pszPath, IN EVENT_LOG * pEventLog)
/*++
This function checks to see if given path is referring to a directory.
The path may have environment strings. Hence, it has to be expanded to check
for the full path.
Return:
TRUE on success and FALSE if there is any failure.
--*/
{
DWORD dwError = NO_ERROR;
DWORD cchBuffer;
LPWSTR pszFullPath;
// Find size of buffer required for expansion
cchBuffer = ExpandEnvironmentStringsW( pszPath, NULL, 0);
pszFullPath = (WCHAR *) LocalAlloc( LPTR, ( cchBuffer + 1) * sizeof(WCHAR));
if ( pszFullPath == NULL) {
dwError = ERROR_NOT_ENOUGH_MEMORY;
} else {
// do actual expansion now using scratch buffer pszFullPath
if ( ExpandEnvironmentStringsW( pszPath, pszFullPath, cchBuffer)
> cchBuffer) {
dwError = ERROR_INSUFFICIENT_BUFFER;
} else {
DWORD dwAttribs;
dwAttribs = GetFileAttributesW( pszFullPath);
// check if the attribute means this is a directory.
dwError = ((dwAttribs != (DWORD) -1)
? ((dwAttribs & FILE_ATTRIBUTE_DIRECTORY)
? NO_ERROR: ERROR_PATH_NOT_FOUND)
: GetLastError()
);
if ( dwError != NO_ERROR) {
WCHAR * apsz[1];
apsz[0] = (WCHAR *) pszPath;
pEventLog->LogEvent( INET_SVC_INVALID_LOGFILE_DIRECTORY,
1,
apsz,
dwError );
}
}
// Free the space
LocalFree( pszFullPath);
}
return (dwError);
} // CheckIfPathIsDirectory()
#else // CHICAGO
//
// Windows 95 version
//
DWORD
CheckIfPathIsDirectory(IN LPCWSTR pszPath, IN EVENT_LOG * pEventLog)
/*++
This function checks to see if given path is referring to a directory.
The path may have environment strings. Hence, it has to be expanded to check
for the full path.
Return:
TRUE on success and FALSE if there is any failure.
--*/
{
DWORD dwError = NO_ERROR;
CHAR szFullPathA[MAX_PATH];
DWORD cch;
DWORD dwAttribs;
*szFullPathA = '0';
cch = WideCharToMultiByte(CP_ACP,
0,
pszPath,
-1,
szFullPathA,
sizeof(szFullPathA)/sizeof(CHAR),
NULL,NULL
);
dwAttribs = GetFileAttributes( szFullPathA);
// check if the attribute means this is a directory.
dwError = ((dwAttribs != (DWORD) -1)
? ((dwAttribs & FILE_ATTRIBUTE_DIRECTORY)
? NO_ERROR: ERROR_PATH_NOT_FOUND)
: GetLastError()
);
return (dwError);
} // CheckIfPathIsDirectory()
#endif
#ifdef CHICAGO
//
// Windows95 replacement for missing functionality
//
DWORD
W95RegOpenKeyExW(HKEY hKeyParent,
LPCWSTR pwszSubKey,
DWORD dwReserved,
REGSAM dwMask,
HKEY* phKey
)
{
CHAR szSubKeyA[MAX_PATH];
DWORD cch;
*szSubKeyA = '0';
cch = WideCharToMultiByte(CP_ACP,
0,
pwszSubKey,
-1,
szSubKeyA,
sizeof(szSubKeyA)/sizeof(CHAR),
NULL,NULL
);
return RegOpenKeyEx(hKeyParent,
szSubKeyA,
dwReserved,
dwMask,
phKey
);
}
DWORD
W95RegQueryValueExW(HKEY hKeyParent,
LPCWSTR pwszValue,
LPDWORD lpvReserved,
LPDWORD lpdwType,
LPBYTE lpvData,
LPDWORD lpdwSize
)
{
CHAR szValueA[MAX_PATH];
DWORD cch;
*szValueA = '0';
cch = WideCharToMultiByte(CP_ACP,
0,
pwszValue,
-1,
szValueA,
sizeof(szValueA)/sizeof(CHAR),
NULL,NULL
);
return RegQueryValueEx(hKeyParent,
szValueA,
lpvReserved,
lpdwType,
lpvData,
lpdwSize
);
}
LPWSTR WINAPI
W95lstrcpyW(
LPWSTR lpString1,
LPCWSTR lpString2
)
{
LPWSTR cp = lpString1;
while (*lpString2) {
*cp++= *lpString2++;
}
*cp++ = L'\0';
return lpString1;
}
LPWSTR WINAPI
W95lstrcpynW(
LPWSTR lpString1,
LPCWSTR lpString2,
int iMax
)
{
LPWSTR cp = lpString1;
if (iMax) {
if (iMax > 1 ) {
iMax--;
while (*lpString2 && iMax--) {
*cp++= *lpString2++;
}
}
*cp++ = L'\0';
}
return lpString1;
}
#endif
/************************ End of File ***********************/