//-------------------------------------------------------------------------
// Desc: Check a database via HTTP monitoring.
// Tabs: 3
//
// Copyright (c) 2002-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id: imonchk.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------
#include "flaimsys.h"
#define CHECK_FORM_NAME "CheckForm"
#define DATABASE_NAME_FIELD "databasename"
#define DATA_DIR_FIELD "datadir"
#define RFL_DIR_FIELD "rfldir"
#define LOG_FILE_NAME_FIELD "logfilename"
#define CHECK_INDEXES_FIELD "checkindexes"
#define REPAIR_INDEXES_FIELD "repairindexes"
#define DETAILED_STATS_FIELD "detailedstats"
FSTATIC void format64Num(
FLMUINT64 ui64Num,
char * pszNum);
FSTATIC RCODE copyStr(
char ** ppszDestStr,
const char * pszSrcStr);
FSTATIC void copyNames(
CHECK_STATUS * pDestCheckStatus,
CHECK_STATUS * pSrcCheckStatus);
FSTATIC void freeCheckStatus(
CHECK_STATUS * pCheckStatus,
FLMBOOL bFreeStruct);
FSTATIC void imonLogField(
IF_FileHdl * pLogFile,
F_NameTable * pNameTable,
FlmRecord * pRecord,
void * pvField,
FLMUINT uiStartCol,
FLMUINT uiLevelOffset);
FSTATIC void imonLogKeyError(
IF_FileHdl * pLogFile,
F_NameTable * pNameTable,
CORRUPT_INFO * pCorrupt);
FSTATIC void imonLogCorruptError(
IF_FileHdl * pLogFile,
F_NameTable * pNameTable,
CORRUPT_INFO * pCorrupt);
FSTATIC RCODE CheckStatusCB(
eStatusType eStatus,
void * pvParm1,
void * pvParm2,
void * pvAppData);
FSTATIC RCODE imonDoCheck(
IF_Thread * pThread);
FSTATIC void imonLogStr(
IF_FileHdl * pLogFile,
FLMUINT uiIndent,
const char * pszStr);
/****************************************************************************
Desc: Prints the web page for checking a database.
****************************************************************************/
RCODE F_CheckDbPage::display(
FLMUINT uiNumParams,
const char ** ppszParams)
{
RCODE rc = FERR_OK;
const char * pszErrType = NULL;
RCODE runRc = FERR_OK;
F_Session * pFlmSession = m_pFlmSession;
HFDB hDb = HFDB_NULL;
F_NameTable * pNameTable = NULL;
char szTmp[ 32];
char * pszTmp;
char * pszOperation = NULL;
char * pszDbName = NULL;
char * pszDataDir = NULL;
char * pszRflDir = NULL;
FLMBOOL bCheckingIndexes = FALSE;
FLMBOOL bRepairingIndexes = FALSE;
FLMBOOL bDetailedStatistics = FALSE;
char * pszLogFileName = NULL;
FLMBOOL bPerformCheck = FALSE;
FLMBOOL bStopCheck = FALSE;
FLMUINT uiCheckThreadId;
CHECK_STATUS CheckStatus;
char szDbKey[ F_SESSION_DB_KEY_LEN];
f_memset( &CheckStatus, 0, sizeof( CHECK_STATUS));
// Acquire a FLAIM session
if (!pFlmSession)
{
rc = RC_SET( m_uiSessionRC);
goto ReportErrorExit;
}
// Get the database handle, if any
if( RC_BAD( rc = getDatabaseHandleParam( uiNumParams,
ppszParams, pFlmSession, &hDb, szDbKey)))
{
hDb = HFDB_NULL;
}
else
{
if( IsInCSMode( hDb))
{
rc = RC_SET( FERR_NOT_IMPLEMENTED);
goto ReportErrorExit;
}
if (RC_BAD( rc = pFlmSession->getNameTable( hDb, &pNameTable)))
{
goto ReportErrorExit;
}
}
// Get the value of the Operation field, if present.
getFormValueByName( "Operation", &pszOperation, 0, NULL);
if (pszOperation)
{
if (f_stricmp( pszOperation, OPERATION_CHECK) == 0)
{
bPerformCheck = TRUE;
}
else if (f_stricmp( pszOperation, OPERATION_STOP) == 0)
{
bStopCheck = TRUE;
}
}
// Get the database name, if any
if (getFormValueByName( DATABASE_NAME_FIELD, &pszDbName, 0, NULL) == 0)
{
if (pszDbName && *pszDbName)
{
fcsDecodeHttpString( pszDbName);
}
}
// Get the database directory, if any
if (getFormValueByName( DATA_DIR_FIELD, &pszDataDir, 0, NULL) == 0)
{
if (pszDataDir && *pszDataDir)
{
fcsDecodeHttpString( pszDataDir);
}
}
// Get the RFL directory, if any
if (getFormValueByName( RFL_DIR_FIELD, &pszRflDir, 0, NULL) == 0)
{
if (pszRflDir && *pszRflDir)
{
fcsDecodeHttpString( pszRflDir);
}
}
// Get the log file name, if any
if (getFormValueByName( LOG_FILE_NAME_FIELD, &pszLogFileName, 0, NULL) == 0)
{
if (pszLogFileName && *pszLogFileName)
{
fcsDecodeHttpString( pszLogFileName);
}
}
// Get the flag for whether or not to check indexes.
szTmp [0] = 0;
pszTmp = &szTmp [0];
if( RC_BAD( getFormValueByName( CHECK_INDEXES_FIELD,
&pszTmp, sizeof( szTmp), NULL)))
{
if( RC_BAD( ExtractParameter( uiNumParams, ppszParams,
CHECK_INDEXES_FIELD, sizeof( szTmp), szTmp)))
{
szTmp [0] = 0;
}
}
if (f_strcmp( szTmp, "yes") == 0)
{
bCheckingIndexes = TRUE;
}
// Get the flag for whether or not to repair indexes
szTmp [0] = 0;
pszTmp = &szTmp [0];
if (RC_BAD( getFormValueByName( REPAIR_INDEXES_FIELD,
&pszTmp, sizeof( szTmp), NULL)))
{
if( RC_BAD( ExtractParameter( uiNumParams, ppszParams,
REPAIR_INDEXES_FIELD, sizeof( szTmp), szTmp)))
{
szTmp [0] = 0;
}
}
if (f_strcmp( szTmp, "yes") == 0)
{
bRepairingIndexes = TRUE;
}
// Get the flag for whether or not to collect detailed statistics.
szTmp [0] = 0;
pszTmp = &szTmp [0];
if (RC_BAD( getFormValueByName( DETAILED_STATS_FIELD,
&pszTmp, sizeof( szTmp), NULL)))
{
if (RC_BAD( ExtractParameter( uiNumParams, ppszParams,
DETAILED_STATS_FIELD, sizeof( szTmp), szTmp)))
{
szTmp [0] = 0;
}
}
if (f_strcmp( szTmp, "yes") == 0)
{
bDetailedStatistics = TRUE;
}
// See if we had a check running. Get the check thread ID
// if any.
szTmp [0] = '\0';
uiCheckThreadId = 0;
if (RC_OK( ExtractParameter( uiNumParams, ppszParams,
"Running", sizeof( szTmp), szTmp)))
{
if (szTmp [0])
{
uiCheckThreadId = f_atoud( szTmp);
CheckStatus.bCheckRunning = TRUE;
}
}
if (bPerformCheck)
{
// Better not have both bCheckRunning and bPerformCheck set!
flmAssert( !CheckStatus.bCheckRunning);
if (RC_BAD( runRc = runCheck( pFlmSession,
&hDb, szDbKey, pszDbName, pszDataDir,
pszRflDir, pszLogFileName,
bCheckingIndexes, bRepairingIndexes,
bDetailedStatistics, &uiCheckThreadId)))
{
pszErrType = "RUNNING CHECK";
}
else
{
CheckStatus.bCheckRunning = TRUE;
}
}
// Stop the check, if requested, or get the check data.
if (CheckStatus.bCheckRunning)
{
// getCheckStatus could change CheckStatus.bCheckRunning
// to FALSE.
getCheckStatus( uiCheckThreadId, bStopCheck, &CheckStatus);
}
// Output the web page.
if (!CheckStatus.bCheckRunning && CheckStatus.bHaveCheckStatus)
{
// If we have check results, output a page for viewing/editing them.
printDocStart( "Check Results");
}
else if (!CheckStatus.bCheckRunning)
{
printDocStart( "Run Check");
if (pszErrType)
{
fnPrintf( m_pHRequest,
"
ERROR %04X (%s) %s
\n",
(unsigned)runRc, FlmErrorString( runRc), pszErrType);
}
}
else
{
stdHdr();
fnPrintf( m_pHRequest, HTML_DOCTYPE);
fnPrintf( m_pHRequest, "\n"
"
------LOG FILE CONTENTS------\n");
// Open the log file
if (RC_OK( gv_FlmSysData.pFileSystem->openFile(
pCheckStatus->pszLogFileName, FLM_IO_RDWR | FLM_IO_SH_DENYNONE,
&pFileHdl)))
{
RCODE rc;
FLMUINT uiBytesRead;
// Read and output until we run out of data
for (;;)
{
if (RC_BAD( rc = pFileHdl->read( FLM_IO_CURRENT_POS,
sizeof( szTmp) - 1, szTmp, &uiBytesRead)))
{
if (rc != FERR_IO_END_OF_FILE || !uiBytesRead)
{
break;
}
}
if (uiBytesRead)
{
szTmp [uiBytesRead] = 0;
fnPrintf( m_pHRequest, "%s", szTmp);
}
if (uiBytesRead < sizeof( szTmp) - 1)
{
break;
}
}
pFileHdl->Release();
pFileHdl = NULL;
}
fnPrintf( m_pHRequest, "\n------END OF LOG FILE------\n");
fnPrintf( m_pHRequest, "\n");
}
if( pFileHdl)
{
pFileHdl->Release();
}
}
/****************************************************************************
Desc: Copy one string into another - allocating memory if needed.
****************************************************************************/
FSTATIC RCODE copyStr(
char ** ppszDestStr,
const char * pszSrcStr)
{
RCODE rc = FERR_OK;
FLMUINT uiLen;
if (pszSrcStr && *pszSrcStr)
{
uiLen = f_strlen( pszSrcStr) + 1;
if (RC_BAD( rc = f_alloc( uiLen, ppszDestStr)))
{
goto Exit;
}
f_memcpy( *ppszDestStr, pszSrcStr, uiLen);
}
else
{
*ppszDestStr = NULL;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Copy the database names, etc. from one check status to another.
****************************************************************************/
FSTATIC void copyNames(
CHECK_STATUS * pDestCheckStatus,
CHECK_STATUS * pSrcCheckStatus)
{
(void)copyStr( &pDestCheckStatus->pszDbName,
pSrcCheckStatus->pszDbName);
(void)copyStr( &pDestCheckStatus->pszDataDir,
pSrcCheckStatus->pszDataDir);
(void)copyStr( &pDestCheckStatus->pszRflDir,
pSrcCheckStatus->pszRflDir);
(void)copyStr( &pDestCheckStatus->pszLogFileName,
pSrcCheckStatus->pszLogFileName);
}
/****************************************************************************
Desc: Free a CHECK_STATUS structure and all associated memory.
****************************************************************************/
FSTATIC void freeCheckStatus(
CHECK_STATUS * pCheckStatus,
FLMBOOL bFreeStruct
)
{
f_free( &pCheckStatus->pszDbName);
f_free( &pCheckStatus->pszDataDir);
f_free( &pCheckStatus->pszRflDir);
f_free( &pCheckStatus->pszLogFileName);
if (bFreeStruct)
{
if (pCheckStatus->hDb != HFDB_NULL)
{
FlmDbClose( &pCheckStatus->hDb);
}
if (pCheckStatus->pLogFile)
{
pCheckStatus->pLogFile->Release();
pCheckStatus->pLogFile = NULL;
}
if (pCheckStatus->pNameTable)
{
pCheckStatus->pNameTable->Release();
pCheckStatus->pNameTable = NULL;
}
f_free( &pCheckStatus);
}
}
/****************************************************************************
Desc: Run a database check.
****************************************************************************/
RCODE F_CheckDbPage::runCheck(
F_Session * pFlmSession,
HFDB * phDb,
char * pszDbKey,
const char * pszDbName,
const char * pszDataDir,
const char * pszRflDir,
const char * pszLogFileName,
FLMBOOL bCheckingIndexes,
FLMBOOL bRepairingIndexes,
FLMBOOL bDetailedStatistics,
FLMUINT * puiCheckThreadId)
{
RCODE rc = FERR_OK;
CHECK_STATUS * pCheckStatus = NULL;
IF_Thread * pThread;
HFDB hDb = HFDB_NULL;
FDB * pDb;
if (*phDb == HFDB_NULL)
{
// Open the database
if (RC_BAD( rc = FlmDbOpen( pszDbName, pszDataDir, pszRflDir,
0, NULL, phDb)))
{
goto Exit;
}
else
{
// Insert the handle into the session
if (RC_BAD( rc = pFlmSession->addDbHandle( *phDb, pszDbKey)))
{
FlmDbClose( phDb);
goto Exit;
}
}
}
else
{
pDb = (FDB *)(*phDb);
pszDbName = pDb->pFile->pszDbPath;
if ((pszDataDir = pDb->pFile->pszDataDir) != NULL)
{
if (!(*pszDataDir))
{
pszDataDir = NULL;
}
}
pszRflDir = NULL;
}
// Open the database for the thread - so it doesn't have
// to worry about the handle going away. The thread will close the
// new handle when it exits.
if (RC_BAD( rc = flmOpenFile( ((FDB *)(*phDb))->pFile, NULL, NULL, NULL,
0, TRUE, NULL, NULL,
(((FDB *)(*phDb))->pFile)->pszDbPassword, &pDb)))
{
goto Exit;
}
hDb = (HFDB)pDb;
// Create an object to track the check.
if (RC_BAD( rc = f_calloc( sizeof( CHECK_STATUS), &pCheckStatus)))
{
goto Exit;
}
pCheckStatus->hDb = hDb;
// Set hDb to HFDB_NULL so it won't be closed below.
hDb = HFDB_NULL;
// Copy database names.
if (RC_BAD( rc = copyStr( &pCheckStatus->pszDbName,
pszDbName)))
{
goto Exit;
}
if (RC_BAD( rc = copyStr( &pCheckStatus->pszDataDir,
pszDataDir)))
{
goto Exit;
}
if (RC_BAD( rc = copyStr( &pCheckStatus->pszRflDir,
pszRflDir)))
{
goto Exit;
}
if (RC_BAD( rc = copyStr( &pCheckStatus->pszLogFileName,
pszLogFileName)))
{
goto Exit;
}
// Create the log file, if one was specified.
if (pCheckStatus->pszLogFileName)
{
gv_FlmSysData.pFileSystem->deleteFile( pCheckStatus->pszLogFileName);
if (RC_BAD( gv_FlmSysData.pFileSystem->createFile(
pCheckStatus->pszLogFileName, FLM_IO_RDWR | FLM_IO_SH_DENYNONE,
&pCheckStatus->pLogFile)))
{
f_free( &pCheckStatus->pszLogFileName);
}
}
// Get a name table for the database - if we can.
if ((pCheckStatus->pNameTable = f_new F_NameTable) != NULL)
{
if (RC_BAD( pCheckStatus->pNameTable->setupFromDb( hDb)))
{
pCheckStatus->pNameTable->Release();
pCheckStatus->pNameTable = NULL;
}
}
pCheckStatus->bCheckingIndexes = bCheckingIndexes;
pCheckStatus->bRepairingIndexes = bRepairingIndexes;
pCheckStatus->bDetailedStatistics = bDetailedStatistics;
pCheckStatus->bCheckRunning = TRUE;
pCheckStatus->uiLastTimeBrowserChecked = FLM_GET_TIMER();
// If browser does not check status at least every 15 seconds, we will
// assume it has gone away and the thread will terminate itself.
pCheckStatus->uiCheckTimeout = FLM_SECS_TO_TIMER_UNITS( 15);
// Start a thread to do the check.
if (RC_BAD( rc = f_threadCreate( &pThread, imonDoCheck,
"WEB DB CHECK", gv_uiDbThrdGrp, 1,
(void *)pCheckStatus, (void *)hDb)))
{
goto Exit;
}
*puiCheckThreadId = pThread->getThreadId();
// Set pCheckStatus to NULL so it won't be freed below. The thread
// will free it when it stops.
pCheckStatus = NULL;
Exit:
if (pThread)
{
pThread->Release();
}
if (pCheckStatus)
{
freeCheckStatus( pCheckStatus, TRUE);
}
if (hDb != HFDB_NULL)
{
FlmDbClose( &hDb);
}
return( rc);
}
/****************************************************************************
Desc: Output the current thread status to the web page.
****************************************************************************/
void F_CheckDbPage::getCheckStatus(
FLMUINT uiCheckThreadId,
FLMBOOL bStopCheck,
CHECK_STATUS * pCheckStatus
)
{
FLMUINT uiThreadId;
IF_Thread * pThread = NULL;
CHECK_STATUS * pThreadCheckStatus;
FLMBOOL bMutexLocked = FALSE;
// pCheckStatus->bHaveCheckStatus should be set to FALSE by the caller.
flmAssert( !pCheckStatus->bHaveCheckStatus);
// See if the thread is still running.
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
uiThreadId = 0;
for (;;)
{
if (RC_BAD( gv_FlmSysData.pThreadMgr->getNextGroupThread( &pThread,
gv_uiDbThrdGrp, &uiThreadId)))
{
pCheckStatus->bCheckRunning = FALSE;
goto Exit;
}
if (uiThreadId == uiCheckThreadId)
{
// If the app ID is zero, the thread is on its way out or already
// out. Can no longer get thread status.
if (!pThread->getThreadAppId())
{
pCheckStatus->bCheckRunning = FALSE;
goto Exit;
}
// Found thread, get its check status data
pThreadCheckStatus = (CHECK_STATUS *)pThread->getParm1();
pThreadCheckStatus->uiLastTimeBrowserChecked = FLM_GET_TIMER();
// Tell the thread to stop the check before telling it
// to stop. This is so we can get partial results.
if (bStopCheck)
{
pThreadCheckStatus->bStopCheck = TRUE;
// Go into a while loop, waiting for the thread
// to finish its check.
while (pThreadCheckStatus->bCheckRunning)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
f_sleep( 200);
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
// If the thread app ID goes to zero, it has been
// told to shut down, and has either already gone
// away or is in the process of doing so, in which
// case pThreadCheckStatus has either already been
// deleted, or will be - so it is not safe to access
// it any more!
if (!pThread->getThreadAppId())
{
pCheckStatus->bCheckRunning = FALSE;
goto Exit;
}
}
}
break;
}
pThread->Release();
pThread = NULL;
}
// Mutex better still be locked at this point.
flmAssert( bMutexLocked);
// Note that we test pThreadCheckStatus->bCheckRunning BEFORE
// doing the memcpy. This is because puiDrnList is not guaranteed
// to be set until bCheckRunning is FALSE. If bCheckRunning is TRUE,
// we will NULL out whatever got copied into puiDrnList.
if (!pThreadCheckStatus->bCheckRunning)
{
f_memcpy( pCheckStatus, pThreadCheckStatus, sizeof( CHECK_STATUS));
copyNames( pCheckStatus, pThreadCheckStatus);
// Need to unlock the mutex so that the thread can stop.
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
pThread->stopThread();
}
else
{
f_memcpy( pCheckStatus, pThreadCheckStatus, sizeof( CHECK_STATUS));
copyNames( pCheckStatus, pThreadCheckStatus);
// Set bCheckRunning to TRUE. This takes care of a race
// race condition of pThreadCheckStatus->bCheckRunning getting
// set to FALSE by the check thread after we test it above.
// we make the test on pThreadCheckStatus->bCheckRunning. We will
// simply get that fact next time we get status.
pCheckStatus->bCheckRunning = TRUE;
}
// NULL out certain members so we won't attempt to use them. They
// may go away if the background thread has gone away.
pCheckStatus->hDb = HFDB_NULL;
pCheckStatus->pLogFile = NULL;
pCheckStatus->pNameTable = NULL;
pCheckStatus->bHaveCheckStatus = TRUE;
Exit:
if (bMutexLocked)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
}
if (pThread)
{
pThread->Release();
}
}
/********************************************************************
Desc: Log a string to the log file.
*********************************************************************/
FSTATIC void imonLogStr(
IF_FileHdl * pLogFile,
FLMUINT uiIndent,
const char * pszStr)
{
char szBuffer [100];
FLMUINT uiLoop;
FLMUINT uiBytesWritten;
if ((uiLoop = uiIndent) != 0)
{
f_memset( szBuffer, ' ', uiIndent);
uiLoop = uiIndent;
}
if (pszStr)
{
while (*pszStr)
{
if (uiLoop == sizeof( szBuffer))
{
pLogFile->write( FLM_IO_CURRENT_POS, uiLoop,
szBuffer, &uiBytesWritten);
uiLoop = 0;
}
szBuffer [uiLoop++] = *pszStr;
pszStr++;
}
}
if (uiLoop >= sizeof( szBuffer) - 2)
{
pLogFile->write( FLM_IO_CURRENT_POS,
uiLoop, szBuffer, &uiBytesWritten);
uiLoop = 0;
}
szBuffer [uiLoop++] = '\r';
szBuffer [uiLoop++] = '\n';
pLogFile->write( FLM_IO_CURRENT_POS,
uiLoop, szBuffer, &uiBytesWritten);
}
/***************************************************************************
Desc: Log a field's data.
*****************************************************************************/
FSTATIC void imonLogField(
IF_FileHdl * pLogFile,
F_NameTable * pNameTable,
FlmRecord * pRecord,
void * pvField,
FLMUINT uiStartCol,
FLMUINT uiLevelOffset)
{
char szTmpBuf [200];
char * pszTmp;
FLMUINT uiFieldNum;
FLMUINT uiLen;
FLMUINT uiBinLen;
FLMUINT uiTmpLen;
FLMBYTE * pucTmp;
FLMBYTE ucTmpBin [80];
FLMUINT uiNum;
FLMUINT uiLevel = pRecord->getLevel( pvField) + uiLevelOffset;
FLMUINT uiIndent = (uiLevel * 2) + uiStartCol;
// Insert leading spaces to indent for level
if (uiIndent)
{
f_memset(szTmpBuf, ' ', uiIndent);
}
// Output level and tag
f_sprintf( &szTmpBuf [uiIndent], "%u ", (unsigned)uiLevel);
pszTmp = &szTmpBuf [f_strlen( szTmpBuf)];
uiFieldNum = pRecord->getFieldID( pvField);
if (!pNameTable ||
!pNameTable->getFromTagNum( uiFieldNum,
NULL, pszTmp,
sizeof( szTmpBuf) - (pszTmp - &szTmpBuf [0])))
{
f_sprintf( pszTmp, "#%u", (unsigned)uiFieldNum);
}
// Output what will fit of the value on the rest of the line
uiLen = f_strlen( szTmpBuf);
szTmpBuf [uiLen++] = ' ';
szTmpBuf [uiLen] = 0;
if (!pRecord->getDataLength( pvField))
{
goto Exit;
}
switch (pRecord->getDataType( pvField))
{
case FLM_TEXT_TYPE:
pszTmp = &szTmpBuf [uiLen];
uiLen = 80 - uiLen;
pRecord->getNative( pvField, pszTmp, &uiLen);
break;
case FLM_NUMBER_TYPE:
pRecord->getUINT( pvField, &uiNum);
f_sprintf( &szTmpBuf [uiLen], "%u", (unsigned)uiNum);
break;
case FLM_BINARY_TYPE:
pRecord->getBinaryLength( pvField, &uiBinLen);
uiTmpLen = sizeof( ucTmpBin);
pRecord->getBinary( pvField, ucTmpBin, &uiTmpLen);
pucTmp = &ucTmpBin [0];
while (uiBinLen && uiLen < 77)
{
f_sprintf( &szTmpBuf [uiLen], "%02X ", (unsigned)*pucTmp);
uiBinLen--;
pucTmp++;
uiLen += 3;
}
szTmpBuf [uiLen - 1] = 0;
break;
case FLM_CONTEXT_TYPE:
pRecord->getUINT( pvField, &uiNum);
f_sprintf( &szTmpBuf[ uiLen], "@%u@", (unsigned)uiNum);
break;
}
Exit:
imonLogStr( pLogFile, 0, szTmpBuf);
}
/********************************************************************
Desc: Log an index key corruption error.
*********************************************************************/
FSTATIC void imonLogKeyError(
IF_FileHdl * pLogFile,
F_NameTable * pNameTable,
CORRUPT_INFO * pCorrupt)
{
FLMUINT uiLogItem;
FlmRecord * pRecord = NULL;
void * pvField;
REC_KEY * pTempKeyList = NULL;
FLMUINT uiIndent;
FLMUINT uiLevelOffset;
char szNameBuf [128];
char szTmpBuf [128];
if (!pNameTable ||
!pNameTable->getFromTagNum( pCorrupt->uiErrLfNumber,
NULL, szNameBuf, sizeof( szNameBuf)))
{
f_sprintf( (char *)szNameBuf, "#%u", (unsigned)pCorrupt->uiErrLfNumber);
}
imonLogStr( pLogFile, 0, NULL);
imonLogStr( pLogFile, 0, NULL);
f_sprintf( szTmpBuf, "ERROR IN INDEX: %s", szNameBuf);
imonLogStr( pLogFile, 0, szTmpBuf);
uiLogItem = 'R';
uiLevelOffset = 0;
for (;;)
{
uiIndent = 2;
if (uiLogItem == 'K')
{
if ((pRecord = pCorrupt->pErrIxKey) == NULL)
{
uiLogItem = 'L';
continue;
}
imonLogStr( pLogFile, 0, NULL);
imonLogStr( pLogFile, 0, " PROBLEM KEY");
}
else if (uiLogItem == 'R')
{
if ((pRecord = pCorrupt->pErrRecord) == NULL)
{
uiLogItem = 'K';
continue;
}
imonLogStr( pLogFile, 0, NULL);
imonLogStr( pLogFile, 0, " RECORD");
}
else if (uiLogItem == 'L')
{
if ((pTempKeyList =
pCorrupt->pErrRecordKeyList) == NULL)
{
break;
}
pRecord = pTempKeyList->pKey;
imonLogStr( pLogFile, 0, NULL);
imonLogStr( pLogFile, 0, " RECORD KEYS");
imonLogStr( pLogFile, 0, " 0 Key");
uiLevelOffset = 1;
}
for (pvField = pRecord->root();;)
{
if (!pvField)
{
if (uiLogItem != 'L')
{
break;
}
if ((pTempKeyList = pTempKeyList->pNextKey) == NULL)
{
break;
}
pRecord = pTempKeyList->pKey;
pvField = pRecord->root();
imonLogStr( pLogFile, 0, " 0 Key");
continue;
}
else
{
imonLogField( pLogFile, pNameTable, pRecord, pvField,
uiIndent, uiLevelOffset);
}
pvField = pRecord->next( pvField);
}
if (uiLogItem == 'L')
{
break;
}
else if (uiLogItem == 'R')
{
uiLogItem = 'K';
}
else
{
uiLogItem = 'L';
}
}
}
/********************************************************************
Desc: Log corruptions to log file.
*********************************************************************/
FSTATIC void imonLogCorruptError(
IF_FileHdl * pLogFile,
F_NameTable * pNameTable,
CORRUPT_INFO * pCorrupt)
{
char szWhat [20];
char szTmpBuf [100];
switch (pCorrupt->eErrLocale)
{
case LOCALE_LFH_LIST:
imonLogStr( pLogFile, 0, "ERROR IN LFH LINKED LIST:");
break;
case LOCALE_AVAIL_LIST:
imonLogStr( pLogFile, 0, "ERROR IN AVAIL LINKED LIST:");
break;
case LOCALE_B_TREE:
if (pCorrupt->eCorruption == FLM_OLD_VIEW)
{
imonLogStr( pLogFile, 0, "OLD VIEW");
}
else
{
if (pCorrupt->uiErrFieldNum)
{
f_strcpy( szWhat, "FIELD");
}
else if (pCorrupt->uiErrElmOffset)
{
f_strcpy( szWhat, "ELEMENT");
}
else if (pCorrupt->uiErrBlkAddress)
{
f_strcpy( szWhat, "BLOCK");
}
else
{
f_strcpy( szWhat, "LAST BLOCK");
}
f_sprintf( szTmpBuf, "BAD %s", szWhat);
imonLogStr( pLogFile, 0, szTmpBuf);
}
// Log the logical file number, name, and type
f_sprintf( szTmpBuf, "Logical File Number: %u",
(unsigned)pCorrupt->uiErrLfNumber);
imonLogStr( pLogFile, 2, szTmpBuf);
switch( pCorrupt->uiErrLfType)
{
case LF_CONTAINER:
f_strcpy( szWhat, "Container");
break;
case LF_INDEX:
f_strcpy( szWhat, "Index");
break;
default:
f_sprintf( (char *)szWhat, "?%u",
(unsigned)pCorrupt->uiErrLfType);
break;
}
f_sprintf( szTmpBuf, "Logical File Type: %s", szWhat);
imonLogStr( pLogFile, 2, szTmpBuf);
// Log the level in the B-Tree, if known
if (pCorrupt->uiErrBTreeLevel != 0xFF)
{
f_sprintf( szTmpBuf, "Level in B-Tree: %u",
(unsigned)pCorrupt->uiErrBTreeLevel);
imonLogStr( pLogFile, 2, szTmpBuf);
}
break;
case LOCALE_IXD_TBL:
f_sprintf( szTmpBuf, "ERROR IN IXD TABLE, Index Number: %u",
(unsigned)pCorrupt->uiErrLfNumber);
imonLogStr( pLogFile, 0, szTmpBuf);
break;
case LOCALE_INDEX:
f_strcpy( szWhat, "Index");
imonLogKeyError( pLogFile, pNameTable, pCorrupt);
break;
default:
pCorrupt->eErrLocale = LOCALE_NONE;
break;
}
// Log the block address, if known
if (pCorrupt->uiErrBlkAddress)
{
f_sprintf( szTmpBuf, "Block Address: 0x%08X (%u)",
(unsigned)pCorrupt->uiErrBlkAddress,
(unsigned)pCorrupt->uiErrBlkAddress);
imonLogStr( pLogFile, 2, szTmpBuf);
}
// Log the parent block address, if known
if (pCorrupt->uiErrParentBlkAddress)
{
if (pCorrupt->uiErrParentBlkAddress != 0xFFFFFFFF)
{
f_sprintf( szTmpBuf, "Parent Block Address: 0x%08X (%u)",
(unsigned)pCorrupt->uiErrParentBlkAddress,
(unsigned)pCorrupt->uiErrParentBlkAddress);
}
else
{
f_sprintf( szTmpBuf,
"Parent Block Address: NONE, Root Block");
}
imonLogStr( pLogFile, 2, szTmpBuf);
}
// Log the element offset, if known
if (pCorrupt->uiErrElmOffset)
{
f_sprintf( szTmpBuf, "Element Offset: %u",
(unsigned)pCorrupt->uiErrElmOffset);
imonLogStr( pLogFile, 2, szTmpBuf);
}
// Log the record number, if known
if (pCorrupt->uiErrDrn)
{
f_sprintf( szTmpBuf,
"Record Number: %u", (unsigned)pCorrupt->uiErrDrn);
imonLogStr( pLogFile, 2, szTmpBuf);
}
// Log the offset within the element record, if known
if (pCorrupt->uiErrElmRecOffset != 0xFFFF)
{
f_sprintf( szTmpBuf, "Offset Within Element: %u",
(unsigned)pCorrupt->uiErrElmRecOffset);
imonLogStr( pLogFile, 2, szTmpBuf);
}
// Log the field number, if known
if (pCorrupt->uiErrFieldNum)
{
f_sprintf( szTmpBuf,
"Field Number: %u", (unsigned)pCorrupt->uiErrFieldNum);
imonLogStr( pLogFile, 2, szTmpBuf);
}
f_strcpy( szTmpBuf, FlmVerifyErrToStr( pCorrupt->eCorruption));
f_sprintf( &szTmpBuf[ f_strlen( szTmpBuf)], " (%d)",
(int)pCorrupt->eCorruption);
imonLogStr( pLogFile, 2, szTmpBuf);
imonLogStr( pLogFile, 0, NULL);
pLogFile->flush();
}
/***************************************************************************
Desc: Check status callback.
***************************************************************************/
FSTATIC RCODE CheckStatusCB(
eStatusType eStatus,
void * pvParm1,
void * pvParm2,
void * pvAppData)
{
RCODE rc = FERR_OK;
FLMUINT uiCurrTime;
CORRUPT_INFO * pCorrupt;
CHECK_STATUS * pCheckStatus = (CHECK_STATUS *)pvAppData;
uiCurrTime = FLM_GET_TIMER();
if (pCheckStatus->bStopCheck)
{
rc = RC_SET( FERR_USER_ABORT);
goto Exit;
}
else if (FLM_ELAPSED_TIME( uiCurrTime,
pCheckStatus->uiLastTimeBrowserChecked) >=
pCheckStatus->uiCheckTimeout)
{
rc = RC_SET( FERR_TIMEOUT);
goto Exit;
}
// Handle each of the status types
if (eStatus == FLM_PROBLEM_STATUS)
{
FLMBOOL * pbFixCorruptions = (FLMBOOL *)pvParm2;
pCorrupt = (CORRUPT_INFO *)pvParm1;
if (pCheckStatus->pLogFile &&
pCorrupt->eCorruption != FLM_OLD_VIEW)
{
imonLogCorruptError( pCheckStatus->pLogFile,
pCheckStatus->pNameTable, pCorrupt);
}
if (pCorrupt->eCorruption == FLM_OLD_VIEW)
{
pCheckStatus->uiOldViewCount++;
}
else
{
pCheckStatus->uiCorruptCount++;
}
if (pbFixCorruptions)
{
*pbFixCorruptions = pCheckStatus->bRepairingIndexes;
}
}
else if (eStatus == FLM_CHECK_STATUS)
{
// Capture the progress information.
f_memcpy( &pCheckStatus->Progress, pvParm1,
sizeof( DB_CHECK_PROGRESS));
// Update thread status
if (FLM_ELAPSED_TIME( uiCurrTime, pCheckStatus->uiLastTimeSetStatus) >=
pCheckStatus->uiUpdateStatusInterval)
{
if (pCheckStatus->Progress.iCheckPhase == CHECK_RS_SORT)
{
FLMUINT uiPercent = 0;
if (pCheckStatus->Progress.ui64NumRSUnits > (FLMUINT64)0)
{
uiPercent =
(FLMUINT)((pCheckStatus->Progress.ui64NumRSUnitsDone *
(FLMUINT64)100) /
pCheckStatus->Progress.ui64NumRSUnits);
}
pCheckStatus->pThread->setThreadStatus( "Sorting, %u percent done",
(unsigned)uiPercent);
}
else
{
char szFileSize [60];
char szBytesDone [60];
format64Num( pCheckStatus->Progress.ui64DatabaseSize, szFileSize);
format64Num( pCheckStatus->Progress.ui64BytesExamined, szBytesDone);
pCheckStatus->pThread->setThreadStatus( "%s of %s bytes checked",
szBytesDone, szFileSize);
}
pCheckStatus->uiLastTimeSetStatus = uiCurrTime;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Thread to perform a database check for a web page.
****************************************************************************/
FSTATIC RCODE imonDoCheck(
IF_Thread * pThread)
{
RCODE rc;
CHECK_STATUS * pCheckStatus = (CHECK_STATUS *)pThread->getParm1();
FLMUINT uiFlags;
F_Pool pool;
DB_CHECK_PROGRESS CheckProgress;
FLMUINT uiCurrTime;
pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING);
pCheckStatus->pThread = pThread;
pCheckStatus->uiUpdateStatusInterval = FLM_SECS_TO_TIMER_UNITS( 5);
uiFlags = FLM_CHK_FIELDS;
if (pCheckStatus->bCheckingIndexes)
{
uiFlags |= FLM_CHK_INDEX_REFERENCING;
}
pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING);
pool.poolInit( 512);
rc = FlmDbCheck( pCheckStatus->hDb, NULL, NULL, NULL, uiFlags,
&pool, &CheckProgress, CheckStatusCB, pCheckStatus);
pool.poolFree();
// Close the database and log file before doing anything else.
FlmDbClose( &pCheckStatus->hDb);
if (pCheckStatus->pLogFile)
{
pCheckStatus->pLogFile->Release();
pCheckStatus->pLogFile = NULL;
}
pCheckStatus->CheckRc = rc;
pCheckStatus->bCheckRunning = FALSE;
if (RC_BAD( rc))
{
if (rc == FERR_USER_ABORT)
{
// Callback forced us to quit.
pThread->setThreadStatus( "User halted");
}
else if (rc == FERR_TIMEOUT)
{
pThread->setThreadStatus( "Timed out");
goto Exit;
}
else
{
pThread->setThreadStatus( "Check Error %04X,", (unsigned)rc);
}
}
// Wait for the user to tell us to quit.
for (;;)
{
// See if we should shut down.
if (pThread->getShutdownFlag())
{
// Transaction will be aborted below
pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING);
goto Exit;
}
// See if we timed out
uiCurrTime = FLM_GET_TIMER();
if (FLM_ELAPSED_TIME( uiCurrTime,
pCheckStatus->uiLastTimeBrowserChecked) >=
pCheckStatus->uiCheckTimeout)
{
goto Exit;
}
// Pause one second
f_sleep( 1000);
}
Exit:
// Set the thread's app ID to 0, so that it will not
// be found now that the thread is terminating (we don't
// want getCheckStatus() to find the thread).
pThread->setThreadAppId( 0);
// Free the check status. Must do inside mutex lock so
// that it doesn't go away after getCheckStatus finds the
// thread.
f_mutexLock( gv_FlmSysData.hShareMutex);
freeCheckStatus( pCheckStatus, TRUE);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
return( FERR_OK);
}