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.
527 lines
19 KiB
527 lines
19 KiB
/*---------------------------------------------------------------------------
|
|
File: ScanLog.cpp
|
|
|
|
Comments: Routines to scan the dispatch log for the DCT agents
|
|
|
|
(c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved
|
|
Proprietary and confidential to Mission Critical Software, Inc.
|
|
|
|
REVISION LOG ENTRY
|
|
Revision By: Christy Boles
|
|
Revised on 03/15/99 13:29:18
|
|
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
#include "StdAfx.h"
|
|
#include "Common.hpp"
|
|
#include "UString.hpp"
|
|
#include "TNode.hpp"
|
|
#include "ServList.hpp"
|
|
#include "Globals.h"
|
|
#include "Monitor.h"
|
|
#include "FParse.hpp"
|
|
#include "afxdao.h"
|
|
#include "errDct.hpp"
|
|
#include "scanlog.h"
|
|
#include <Winnls.h>
|
|
|
|
|
|
#define AR_Status_Created (0x00000001)
|
|
#define AR_Status_Replaced (0x00000002)
|
|
#define AR_Status_AlreadyExisted (0x00000004)
|
|
#define AR_Status_RightsUpdated (0x00000008)
|
|
#define AR_Status_DomainChanged (0x00000010)
|
|
#define AR_Status_Rebooted (0x00000020)
|
|
#define AR_Status_Warning (0x40000000)
|
|
#define AR_Status_Error (0x80000000)
|
|
|
|
|
|
#define BYTE_ORDER_MARK (0xFEFF)
|
|
|
|
extern DWORD __stdcall MonitorRunningAgent(void *);
|
|
|
|
void ParseInputFile(const WCHAR * filename);
|
|
|
|
DWORD __stdcall LogReaderFn(void * arg)
|
|
{
|
|
WCHAR logfile[MAX_PATH];
|
|
BOOL bDone;
|
|
long nSeconds;
|
|
|
|
CoInitialize(NULL);
|
|
|
|
gData.GetLogPath(logfile);
|
|
gData.GetDone(&bDone);
|
|
gData.GetWaitInterval(&nSeconds);
|
|
|
|
while ( ! bDone )
|
|
{
|
|
ParseInputFile(logfile);
|
|
Sleep(nSeconds * 1000);
|
|
gData.GetDone(&bDone);
|
|
if (bDone)
|
|
{
|
|
// before we finish up this thread, set the LogDone to TRUE
|
|
gData.SetLogDone(TRUE);
|
|
break;
|
|
}
|
|
|
|
// if the dispatcher.csv has been processed, we should terminate this thread
|
|
gData.GetLogDone(&bDone);
|
|
gData.GetWaitInterval(&nSeconds);
|
|
}
|
|
|
|
CoUninitialize();
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool ConvertToLocalUserDefault(WCHAR* originalTimestamp, WCHAR* convertedTimestamp, size_t size)
|
|
{
|
|
SYSTEMTIME st;
|
|
bool bConverted = false;
|
|
|
|
int cFields = _stscanf(
|
|
originalTimestamp,
|
|
_T("%hu-%hu-%hu %hu:%hu:%hu"),
|
|
&st.wYear,
|
|
&st.wMonth,
|
|
&st.wDay,
|
|
&st.wHour,
|
|
&st.wMinute,
|
|
&st.wSecond
|
|
);
|
|
|
|
if (cFields == 6)
|
|
{
|
|
// format date and time using LOCALE_USER_DEFAULT
|
|
WCHAR formatedDate[100];
|
|
WCHAR formatedTime[100];
|
|
st.wMilliseconds = 0;
|
|
int formatedDateLen =
|
|
GetDateFormatW(LOCALE_USER_DEFAULT, NULL, &st, NULL, formatedDate, sizeof(formatedDate)/sizeof(formatedDate[0]));
|
|
int formatedTimeLen =
|
|
GetTimeFormatW(LOCALE_USER_DEFAULT, NULL, &st, NULL, formatedTime, sizeof(formatedTime)/sizeof(formatedTime[0]));
|
|
if (formatedDateLen != 0 && formatedTimeLen != 0 && formatedDateLen + formatedTimeLen + 1 < (int) size)
|
|
{
|
|
swprintf(convertedTimestamp, L"%s %s", formatedDate, formatedTime);
|
|
bConverted = true;
|
|
}
|
|
}
|
|
|
|
return bConverted;
|
|
}
|
|
|
|
BOOL TErrorLogParser::ScanFileEntry(
|
|
WCHAR * string, // in - line from TError log file
|
|
WCHAR * timestamp, // out- timestamp from this line
|
|
int * pSeverity, // out- severity level of this message
|
|
int * pSourceLine, // out- the source line for this message
|
|
WCHAR * msgtext // out- the textual part of the message
|
|
)
|
|
{
|
|
BOOL bScan = FALSE;
|
|
|
|
// skip byte order mark if present
|
|
|
|
if (string[0] == BYTE_ORDER_MARK)
|
|
{
|
|
++string;
|
|
}
|
|
|
|
// initialize return values
|
|
|
|
*timestamp = L'\0';
|
|
*pSeverity = 0;
|
|
*pSourceLine = 0;
|
|
*msgtext = L'\0';
|
|
|
|
// scan fields
|
|
|
|
//2001-09-11 20:27:32 ERR2:0080 Unable...
|
|
|
|
SYSTEMTIME st;
|
|
_TCHAR szError[4];
|
|
|
|
int cFields = _stscanf(
|
|
string,
|
|
_T("%hu-%hu-%hu %hu:%hu:%hu %3[^0-9]%d:%d %[^\r\n]"),
|
|
&st.wYear,
|
|
&st.wMonth,
|
|
&st.wDay,
|
|
&st.wHour,
|
|
&st.wMinute,
|
|
&st.wSecond,
|
|
szError,
|
|
pSeverity,
|
|
pSourceLine,
|
|
msgtext
|
|
);
|
|
|
|
// if warning or error message
|
|
// else re-scan message
|
|
|
|
if ((cFields >= 9) && ((_tcsicmp(szError, _T("WRN")) == 0) || (_tcsicmp(szError, _T("ERR")) == 0)))
|
|
{
|
|
bScan = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*pSeverity = 0;
|
|
*pSourceLine = 0;
|
|
|
|
cFields = _stscanf(
|
|
string,
|
|
_T("%hu-%hu-%hu %hu:%hu:%hu %[^\r\n]"),
|
|
&st.wYear,
|
|
&st.wMonth,
|
|
&st.wDay,
|
|
&st.wHour,
|
|
&st.wMinute,
|
|
&st.wSecond,
|
|
msgtext
|
|
);
|
|
|
|
if (cFields >= 6)
|
|
{
|
|
bScan = TRUE;
|
|
}
|
|
}
|
|
|
|
if (bScan)
|
|
{
|
|
_stprintf(
|
|
timestamp,
|
|
_T("%hu-%02hu-%02hu %02hu:%02hu:%02hu"),
|
|
st.wYear,
|
|
st.wMonth,
|
|
st.wDay,
|
|
st.wHour,
|
|
st.wMinute,
|
|
st.wSecond
|
|
);
|
|
}
|
|
|
|
return bScan;
|
|
}
|
|
|
|
BOOL GetServerFromMessage(WCHAR const * msg,WCHAR * server)
|
|
{
|
|
BOOL bSuccess = FALSE;
|
|
int ndx = 0;
|
|
|
|
for ( ndx = 0 ; msg[ndx] ; ndx++ )
|
|
{
|
|
if ( msg[ndx] == L'\\' && msg[ndx+1] == L'\\' )
|
|
{
|
|
bSuccess = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if ( bSuccess )
|
|
{
|
|
int i = 0;
|
|
ndx+=2; // strip of the backslashes
|
|
for ( i=0; msg[ndx] && msg[ndx] != L'\\' && msg[ndx]!= L' ' && msg[ndx] != L',' && msg[ndx] != L'\t' && msg[ndx] != L'\n' ; i++,ndx++)
|
|
{
|
|
server[i] = msg[ndx];
|
|
}
|
|
server[i] = 0;
|
|
}
|
|
else
|
|
{
|
|
server[0] = 0;
|
|
}
|
|
return bSuccess;
|
|
}
|
|
|
|
|
|
void ParseInputFile(WCHAR const * gLogFile)
|
|
{
|
|
FILE * pFile = 0;
|
|
WCHAR server[MAX_PATH];
|
|
|
|
int nRead = 0;
|
|
int count = 0;
|
|
HWND lWnd = NULL;
|
|
long totalRead;
|
|
BOOL bNeedToCheckResults = FALSE;
|
|
TErrorLogParser parser;
|
|
TErrorDct edct;
|
|
BOOL bTotalReadGlobal; // indicates whether we have read the total number of agents
|
|
BOOL bTotalReadLocal = FALSE; // indicates whether we will encounter total read in the file this time
|
|
|
|
parser.Open(gLogFile);
|
|
|
|
gData.GetLinesRead(&totalRead);
|
|
gData.GetTotalRead(&bTotalReadGlobal);
|
|
|
|
if ( parser.IsOpen() )
|
|
{
|
|
// scan the file
|
|
while ( ! parser.IsEof() )
|
|
{
|
|
if ( parser.ScanEntry() )
|
|
{
|
|
nRead++;
|
|
if ( nRead < totalRead )
|
|
continue;
|
|
// the first three lines each have their own specific format
|
|
if ( nRead == 1 )
|
|
{
|
|
// first comes the name of the human-readable log file
|
|
gData.SetReadableLogFile(parser.GetMessage());
|
|
}
|
|
else if ( nRead == 2 )
|
|
{
|
|
// next, the name result directory - this is needed to look for the result files
|
|
WCHAR const * dirName = parser.GetMessage();
|
|
gData.SetResultDir(dirName);
|
|
}
|
|
else if ( nRead == 3 )
|
|
{
|
|
// now the count of computers being dispatched to
|
|
count = _wtoi(parser.GetMessage());
|
|
ComputerStats cStat;
|
|
|
|
gData.GetComputerStats(&cStat);
|
|
cStat.total = count;
|
|
gData.SetComputerStats(&cStat);
|
|
bTotalReadLocal = TRUE;
|
|
continue;
|
|
}
|
|
else // all other message have the following format: COMPUTER<tab>Action<tab>RetCode
|
|
{
|
|
WCHAR action[50];
|
|
WCHAR const * pAction = wcschr(parser.GetMessage(),L'\t');
|
|
WCHAR const * retcode = wcsrchr(parser.GetMessage(),L'\t');
|
|
TServerNode * pServer = NULL;
|
|
|
|
if ( GetServerFromMessage(parser.GetMessage(),server)
|
|
&& pAction
|
|
&& retcode
|
|
&& pAction != retcode
|
|
)
|
|
{
|
|
|
|
// UStrCpy(action,pAction+1,retcode - pAction);
|
|
UStrCpy(action,pAction+1,(int)(retcode - pAction));
|
|
// add the server to the list, if it isn't already there
|
|
gData.Lock();
|
|
pServer = gData.GetUnsafeServerList()->FindServer(server);
|
|
if ( ! pServer )
|
|
pServer = gData.GetUnsafeServerList()->AddServer(server);
|
|
gData.Unlock();
|
|
|
|
retcode++;
|
|
|
|
DWORD rc = _wtoi(retcode);
|
|
|
|
if ( pServer )
|
|
{
|
|
if ( UStrICmp(pServer->GetTimeStamp(),parser.GetTimestamp()) < 0 )
|
|
{
|
|
pServer->SetTimeStamp(parser.GetTimestamp());
|
|
}
|
|
if ( !UStrICmp(action,L"WillInstall") )
|
|
{
|
|
pServer->SetIncluded(TRUE);
|
|
}
|
|
else if (! UStrICmp(action,L"JobFile") )
|
|
{
|
|
// this part is in the form of "%d,%d,job path"
|
|
// where the number is indicative of whether account reference
|
|
// result is expected
|
|
WCHAR acctRefResultFlag = L'0';
|
|
WCHAR joinRenameFlag = L'0';
|
|
const WCHAR* msg = retcode;
|
|
WCHAR* comma = wcschr(msg, L',');
|
|
if ( comma )
|
|
{
|
|
if ( comma != msg )
|
|
{
|
|
acctRefResultFlag = *msg;
|
|
WCHAR* msg1 = comma + 1;
|
|
comma = wcschr(msg1, L',');
|
|
if (comma != msg1)
|
|
joinRenameFlag = *msg1;
|
|
}
|
|
pServer->SetJobPath(comma+1);
|
|
}
|
|
else
|
|
{
|
|
pServer->SetJobPath(L"");
|
|
}
|
|
|
|
// set whether account reference result is expected or not
|
|
if (acctRefResultFlag == L'0')
|
|
pServer->SetAccountReferenceResultExpected(FALSE);
|
|
else
|
|
pServer->SetAccountReferenceResultExpected(TRUE);
|
|
|
|
// set whether join & rename is expected or not
|
|
if (joinRenameFlag == L'0')
|
|
pServer->SetJoinDomainWithRename(FALSE);
|
|
else
|
|
pServer->SetJoinDomainWithRename(TRUE);
|
|
}
|
|
else if (!UStrICmp(action,L"RemoteResultPath"))
|
|
{
|
|
pServer->SetRemoteResultPath(retcode);
|
|
}
|
|
else if (! UStrICmp(action,L"Install") )
|
|
{
|
|
if ( rc )
|
|
{
|
|
if ( ! *pServer->GetMessageText() )
|
|
{
|
|
TErrorDct errTemp;
|
|
WCHAR text[2000];
|
|
errTemp.ErrorCodeToText(rc,DIM(text),text);
|
|
|
|
pServer->SetMessageText(text);
|
|
}
|
|
pServer->SetSeverity(2);
|
|
pServer->SetFailed();
|
|
pServer->SetIncluded(TRUE);
|
|
gData.GetListWindow(&lWnd);
|
|
SendMessage(lWnd,DCT_ERROR_ENTRY,NULL,(LPARAM)pServer);
|
|
}
|
|
else
|
|
{
|
|
pServer->SetInstalled();
|
|
pServer->SetIncluded(TRUE);
|
|
gData.GetListWindow(&lWnd);
|
|
SendMessage(lWnd,DCT_UPDATE_ENTRY,NULL,(LPARAM)pServer);
|
|
}
|
|
}
|
|
else if ( ! UStrICmp(action,L"Start") )
|
|
{
|
|
if ( rc )
|
|
{
|
|
if ( ! *pServer->GetMessageText() )
|
|
{
|
|
TErrorDct errTemp;
|
|
WCHAR text[2000];
|
|
errTemp.ErrorCodeToText(rc,DIM(text),text);
|
|
|
|
pServer->SetMessageText(text);
|
|
}
|
|
pServer->SetSeverity(2);
|
|
pServer->SetFailed();
|
|
pServer->SetIncluded(TRUE);
|
|
gData.GetListWindow(&lWnd);
|
|
SendMessage(lWnd,DCT_ERROR_ENTRY,NULL,(LPARAM)pServer);
|
|
}
|
|
else
|
|
{
|
|
// extract the filename and GUID from the end of the message
|
|
WCHAR filename[MAX_PATH];
|
|
WCHAR guid[100];
|
|
WCHAR * comma1 = wcschr(parser.GetMessage(),L',');
|
|
WCHAR * comma2 = NULL;
|
|
if ( comma1 )
|
|
{
|
|
comma2 = wcschr(comma1 + 1,L',');
|
|
|
|
if ( comma2 )
|
|
{
|
|
|
|
// UStrCpy(filename,comma1+1,(comma2-comma1)); // skip the comma & space before the filename
|
|
UStrCpy(filename,comma1+1,(int)(comma2-comma1)); // skip the comma & space before the filename
|
|
safecopy(guid,comma2+1); // skip the comma & space before the guid
|
|
pServer->SetJobID(guid);
|
|
pServer->SetJobFile(filename);
|
|
pServer->SetStarted();
|
|
bNeedToCheckResults = TRUE;
|
|
|
|
// launch a worker thread to monitor the agent
|
|
|
|
// IsMonitoringTried is used to make sure that at most one
|
|
// thread is created to monitor a particular agent.
|
|
// The reason for adding this logic is that in case that lWnd
|
|
// is NULL the total number of read lines will not be set in
|
|
// gData so that the same line containing "Start" could be read
|
|
// more than once and thus more than one thread will be
|
|
// created to monitor an agent. This is problematic.
|
|
// lWnd will be NULL if server list dialog has not been initialized
|
|
// or it is command line case where there is no UI.
|
|
if (!pServer->IsMonitoringTried())
|
|
{
|
|
// mark that we have tried to monitor the agent
|
|
pServer->SetMonitoringTried(TRUE);
|
|
|
|
DWORD id;
|
|
HANDLE aThread = CreateThread(NULL,0,&MonitorRunningAgent,(void*)pServer,0,&id);
|
|
if (aThread == NULL)
|
|
{
|
|
// indicate so if we have run out of resource to monitor the agent
|
|
pServer->SetFailed();
|
|
pServer->SetOutOfResourceToMonitor(TRUE);
|
|
}
|
|
else
|
|
{
|
|
CloseHandle(aThread);
|
|
}
|
|
}
|
|
}
|
|
gData.GetListWindow(&lWnd);
|
|
SendMessage(lWnd,DCT_UPDATE_ENTRY,NULL,(LPARAM)pServer);
|
|
}
|
|
}
|
|
}
|
|
else if ( ! UStrICmp(action,L"Finished") )
|
|
{
|
|
SendMessage(lWnd,DCT_UPDATE_ENTRY,NULL,NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if dispatcher finished dispatching agents set log done
|
|
|
|
LPCWSTR psz = parser.GetMessage();
|
|
|
|
if (wcsstr(psz, L"All") && wcsstr(psz, L"Finished"))
|
|
{
|
|
gData.SetLogDone(TRUE);
|
|
ComputerStats cStat;
|
|
gData.GetComputerStats(&cStat);
|
|
if (cStat.total == 0 && bTotalReadGlobal)
|
|
{
|
|
gData.GetListWindow(&lWnd);
|
|
SendMessage(lWnd,DCT_UPDATE_ENTRY,NULL,NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// once we hit an invalid entry, we stop scanning
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we don't have the handle from the list window, we couldn't really send the messages
|
|
// in that case we must read the lines again next time, so that we can resend the messages.
|
|
if ( lWnd )
|
|
{
|
|
// if we have sent the messages, we don't need to send them again
|
|
gData.SetLinesRead(nRead);
|
|
}
|
|
|
|
// we signal the first pass done only after the total number of server has been read
|
|
// and we only need to set it once
|
|
if (!bTotalReadGlobal && bTotalReadLocal)
|
|
{
|
|
gData.SetFirstPassDone(TRUE);
|
|
gData.SetTotalRead(TRUE);
|
|
}
|
|
parser.Close();
|
|
}
|
|
}
|
|
|
|
|