/*--------------------------------------------------------------------------- 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 #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: COMPUTERActionRetCode { 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(); } }