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.
1433 lines
36 KiB
1433 lines
36 KiB
//***************************************************************************
|
|
//
|
|
// DOCUMENT.CPP
|
|
//
|
|
// Module: NLB Manager
|
|
//
|
|
// Purpose: Implements the document class for nlb manager.
|
|
//
|
|
// Copyright (c)2001-2002 Microsoft Corporation, All Rights Reserved
|
|
//
|
|
// History:
|
|
//
|
|
// 07/30/01 JosephJ Created based on MHakim's code
|
|
//
|
|
//***************************************************************************
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "private.h"
|
|
#include "document.tmh"
|
|
#include "wchar.h"
|
|
#include "share.h"
|
|
|
|
IMPLEMENT_DYNCREATE( Document, CDocument )
|
|
|
|
CNlbEngine gEngine;
|
|
CNlbMgrCommandLineInfo gCmdLineInfo;
|
|
|
|
#define MAX_LOG_FILE_SIZE 10000000L // 10MB
|
|
#define BOM 0xFEFF // The first two bytes of a Unicode file must be this BOM. It is a hint to applications that the file is Unicode-enabled in little endian format.
|
|
|
|
Document::Document()
|
|
:
|
|
m_pLeftView(NULL),
|
|
m_pDetailsView(NULL),
|
|
m_pLogView(NULL),
|
|
m_pNlbEngine(NULL),
|
|
m_dwLoggingEnabled(NULL),
|
|
m_hStatusLog(NULL),
|
|
m_fPrepareToDeinitialize(FALSE)
|
|
{
|
|
TRACE_INFO("->%!FUNC!");
|
|
|
|
*m_szLogFileName = 0;
|
|
|
|
//
|
|
// load the images which are used.
|
|
//
|
|
|
|
m_images48x48 = new CImageList;
|
|
|
|
m_images48x48->Create( 16, // x
|
|
16, // y
|
|
ILC_COLOR16, // 16 bit color
|
|
0, // initially image list is empty
|
|
10 ); // max images is 10. This value arbitrary.
|
|
|
|
// Add the icons which we are going to use.
|
|
// WARNING: these are added according to the order specified
|
|
// in the Document::ICON_XXX enum
|
|
//
|
|
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTERS)); // 1
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTER)); // 2
|
|
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_STARTED)); // 3
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_STOPPED)); // 4
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_CONVERGING));
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_SUSPENDED));
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_DRAINING));
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_DISCONNECTED));
|
|
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_PORTRULE) ); // 5
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_PENDING )); // 6
|
|
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_MYINFORMATIONAL ));// 7
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_MYWARNING )); // 8
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_MYERROR )); // 9
|
|
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTER_OK )); // 10
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTER_PENDING ));// 11
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTER_BROKEN )); // 12
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_OK )); // 13
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_PENDING )); // 14
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_MISCONFIGURED ));// 15
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_UNREACHABLE ));// 16
|
|
m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_UNKNOWN )); // 17
|
|
|
|
|
|
//
|
|
// Initialize the NLB Engine.
|
|
//
|
|
// NOTE: "this", of type Document, inherits from NlbEngine::IUICallbacks,
|
|
// so it is the IUICallbacks interface that gets passed into
|
|
// gEngine.Initialize below.
|
|
//
|
|
NLBERROR NlbErr = gEngine.Initialize(
|
|
REF *this,
|
|
gCmdLineInfo.m_bDemo,
|
|
gCmdLineInfo.m_bNoPing
|
|
);
|
|
if (NlbErr != NLBERR_OK)
|
|
{
|
|
TRACE_CRIT("%!FUNC!: gEngine.Initialize failed with error %08lx",
|
|
NlbErr);
|
|
//TODO: displayNlbError(ID_INITIALIZATION_FAILURE, NlbErr);
|
|
}
|
|
|
|
m_dwLoggingEnabled = 0;
|
|
ZeroMemory(m_szLogFileName, MAXFILEPATHLEN*sizeof(WCHAR));
|
|
m_hStatusLog = NULL;
|
|
|
|
//
|
|
// TODO: figure out what to do if we fail to initialize logging in the constructor!
|
|
//
|
|
// // 2/12/02 JosephJ SECURITY BUGBUG: need to inform user that they could not start logging.
|
|
//
|
|
initLogging();
|
|
|
|
//
|
|
// TODO: figure out how to DEinitialize!!!
|
|
//
|
|
|
|
TRACE_INFO("<-%!FUNC!");
|
|
}
|
|
|
|
Document::~Document()
|
|
{
|
|
// Don't check return value since we are exiting
|
|
stopLogging();
|
|
}
|
|
|
|
void
|
|
Document::registerLogView( LogView* logView )
|
|
{
|
|
m_pLogView = logView;
|
|
}
|
|
|
|
void
|
|
Document::registerDetailsView( DetailsView* detailsView )
|
|
{
|
|
m_pDetailsView = detailsView;
|
|
}
|
|
|
|
|
|
DWORD WINAPI FinalInitialize(PVOID pvContext)
|
|
//
|
|
// This is typically called in the context of a work item.
|
|
//
|
|
{
|
|
TRACE_INFO(L"-> %!FUNC!");
|
|
// Check whether to connect to hosts specified in a host-list file
|
|
if (gCmdLineInfo.m_bHostList)
|
|
{
|
|
((Document *)(pvContext))->LoadHostsFromFile(gCmdLineInfo.m_bstrHostListFile);
|
|
}
|
|
TRACE_INFO(L"<- %!FUNC!");
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
Document::registerLeftView(LeftView *pLeftView)
|
|
{
|
|
TRACE_INFO(L"-> %!FUNC!");
|
|
m_pLeftView = pLeftView;
|
|
|
|
// If there is a file containing the list of hosts to connect to,
|
|
// read the file in a background thread. This is so that the UI
|
|
// can show up while the communication with the hosts can go on
|
|
// in the bakground. If this is NOT done, the UI will not show up
|
|
// until we have heard from all of the hosts listed in the file.
|
|
// -KarthicN
|
|
if(!QueueUserWorkItem(FinalInitialize, this, WT_EXECUTEDEFAULT))
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! QueueUserWorkItem failed with error : 0x%x", GetLastError());
|
|
}
|
|
TRACE_INFO(L"<- %!FUNC!");
|
|
}
|
|
|
|
//
|
|
// Asks the user to update user-supplied info about a host.
|
|
//
|
|
BOOL
|
|
Document::UpdateHostInformation(
|
|
IN BOOL fNeedCredentials,
|
|
IN BOOL fNeedConnectionString,
|
|
IN OUT CHostSpec& host
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Log a message in human-readable form.
|
|
//
|
|
void
|
|
Document::Log(
|
|
IN LogEntryType Type,
|
|
IN const wchar_t *szCluster, OPTIONAL
|
|
IN const wchar_t *szHost, OPTIONAL
|
|
IN UINT ResourceID,
|
|
...
|
|
)
|
|
{
|
|
WCHAR wszBuffer[1024];
|
|
CString FormatString;
|
|
|
|
if (m_fPrepareToDeinitialize)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
if (!FormatString.LoadString(ResourceID))
|
|
{
|
|
StringCbPrintf(wszBuffer, sizeof(wszBuffer), L"BAD/UNKNOWN resource ID %d", ResourceID);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwRet;
|
|
va_list arglist;
|
|
va_start (arglist, ResourceID);
|
|
|
|
dwRet = FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING,
|
|
(LPCWSTR) FormatString,
|
|
0, // Message Identifier - Ignored
|
|
0, // Language Identifier
|
|
wszBuffer,
|
|
ASIZE(wszBuffer)-1,
|
|
&arglist
|
|
);
|
|
if (dwRet == 0)
|
|
{
|
|
TRACE_CRIT("%!FUNC!: FormatMessage failed.");
|
|
wszBuffer[0]=0;
|
|
}
|
|
|
|
va_end (arglist);
|
|
}
|
|
|
|
if (m_pLogView)
|
|
{
|
|
LogEntryHeader Header;
|
|
Header.type = Type;
|
|
Header.szCluster = szCluster;
|
|
Header.szHost = szHost;
|
|
|
|
if (!theApplication.IsMainThread())
|
|
{
|
|
//
|
|
//
|
|
// Let's allocate a UI work item and post the item to the mainform
|
|
// thread so that the mainform thread will handle it.
|
|
//
|
|
//
|
|
CUIWorkItem *pWorkItem = new CUIWorkItem(
|
|
&Header,
|
|
wszBuffer
|
|
);
|
|
if (pWorkItem != NULL)
|
|
{
|
|
if (!mfn_DeferUIOperation(pWorkItem))
|
|
{
|
|
delete pWorkItem;
|
|
}
|
|
}
|
|
goto end;
|
|
}
|
|
|
|
m_pLogView->LogString(&Header, wszBuffer);
|
|
}
|
|
|
|
end:
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Log a message in human-readable form.
|
|
//
|
|
void
|
|
Document::LogEx(
|
|
IN const LogEntryHeader *pHeader,
|
|
IN UINT ResourceID,
|
|
...
|
|
)
|
|
{
|
|
WCHAR wszBuffer[1024];
|
|
CString FormatString;
|
|
|
|
if (m_fPrepareToDeinitialize)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
if (!FormatString.LoadString(ResourceID))
|
|
{
|
|
StringCbPrintf(wszBuffer, sizeof(wszBuffer), L"BAD/UNKNOWN resource ID %d", ResourceID);
|
|
}
|
|
else
|
|
{
|
|
DWORD dwRet;
|
|
va_list arglist;
|
|
va_start (arglist, ResourceID);
|
|
|
|
dwRet = FormatMessage(
|
|
FORMAT_MESSAGE_FROM_STRING,
|
|
(LPCWSTR) FormatString,
|
|
0, // Message Identifier - Ignored
|
|
0, // Language Identifier
|
|
wszBuffer,
|
|
ASIZE(wszBuffer)-1,
|
|
&arglist
|
|
);
|
|
if (dwRet == 0)
|
|
{
|
|
TRACE_CRIT("%!FUNC!: FormatMessage failed.");
|
|
wszBuffer[0]=0;
|
|
}
|
|
|
|
va_end (arglist);
|
|
}
|
|
|
|
if (m_pLogView)
|
|
{
|
|
if (!theApplication.IsMainThread())
|
|
{
|
|
//
|
|
//
|
|
// Let's allocate a UI work item and post the item to the mainform
|
|
// thread so that the mainform thread will handle it.
|
|
//
|
|
//
|
|
CUIWorkItem *pWorkItem = new CUIWorkItem(
|
|
pHeader,
|
|
wszBuffer
|
|
);
|
|
if (pWorkItem != NULL)
|
|
{
|
|
if (!mfn_DeferUIOperation(pWorkItem))
|
|
{
|
|
delete pWorkItem;
|
|
}
|
|
}
|
|
goto end;
|
|
}
|
|
|
|
m_pLogView->LogString(pHeader, wszBuffer);
|
|
}
|
|
|
|
end:
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Handle an event relating to a specific instance of a specific
|
|
// object type.
|
|
//
|
|
void
|
|
Document::HandleEngineEvent(
|
|
IN ObjectType objtype,
|
|
IN ENGINEHANDLE ehClusterId, // could be NULL
|
|
IN ENGINEHANDLE ehObjId,
|
|
IN EventCode evt
|
|
)
|
|
{
|
|
TRACE_INFO(
|
|
L"%!FUNC!: cid=%lx; id=%lx; obj=%lu, evt=%lu",
|
|
(UINT) ehClusterId,
|
|
(UINT) ehObjId,
|
|
(UINT) objtype,
|
|
(UINT) evt
|
|
);
|
|
|
|
if (m_fPrepareToDeinitialize)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
if (!theApplication.IsMainThread())
|
|
{
|
|
// DummyAction(L"HandleEngineEvent -- deferring UI");
|
|
//
|
|
//
|
|
// Let's allocate a UI work item and post the item to the mainform
|
|
// thread so that the mainform thread will handle it.
|
|
//
|
|
//
|
|
CUIWorkItem *pWorkItem = new CUIWorkItem(
|
|
objtype,
|
|
ehClusterId,
|
|
ehObjId,
|
|
evt
|
|
);
|
|
if (pWorkItem != NULL)
|
|
{
|
|
if (!mfn_DeferUIOperation(pWorkItem))
|
|
{
|
|
delete pWorkItem;
|
|
}
|
|
}
|
|
|
|
goto end;
|
|
}
|
|
|
|
|
|
//
|
|
// TODO: consider locking and reference-counting below.
|
|
//
|
|
|
|
if (m_pLeftView != NULL)
|
|
{
|
|
m_pLeftView->HandleEngineEvent(objtype, ehClusterId, ehObjId, evt);
|
|
}
|
|
if (m_pDetailsView != NULL)
|
|
{
|
|
m_pDetailsView->HandleEngineEvent(objtype, ehClusterId, ehObjId, evt);
|
|
}
|
|
|
|
end:
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Handle a selection change notification from the left (tree) view
|
|
//
|
|
void
|
|
Document::HandleLeftViewSelChange(
|
|
IN IUICallbacks::ObjectType objtype,
|
|
IN ENGINEHANDLE ehObj
|
|
)
|
|
{
|
|
if (m_fPrepareToDeinitialize)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
if (m_pDetailsView != NULL)
|
|
{
|
|
m_pDetailsView->HandleLeftViewSelChange(objtype, ehObj);
|
|
}
|
|
|
|
end:
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Read registry settings. If there are none, create defaults according to what is set in constructor.
|
|
//
|
|
Document::LOG_RESULT Document::initLogging()
|
|
{
|
|
|
|
LOG_RESULT lrResult = REG_IO_ERROR;
|
|
LONG status;
|
|
HKEY key;
|
|
WCHAR szKey[MAXSTRINGLEN];
|
|
WCHAR szError[MAXSTRINGLEN];
|
|
DWORD size;
|
|
DWORD type;
|
|
|
|
key = NlbMgrRegCreateKey(NULL);
|
|
|
|
if (key == NULL)
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! registry key doesn't exist");
|
|
return REG_IO_ERROR;
|
|
}
|
|
|
|
size = sizeof (m_dwLoggingEnabled);
|
|
type = REG_DWORD;
|
|
status = RegQueryValueEx(key, L"LoggingEnabled", 0L, &type,
|
|
(LPBYTE) &m_dwLoggingEnabled, &size);
|
|
|
|
if (status == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
//
|
|
// Create the regkey and initialize to what is set in constructor
|
|
//
|
|
status = RegSetValueEx(key, L"LoggingEnabled", 0L, REG_DWORD, (LPBYTE) &m_dwLoggingEnabled, size);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
lrResult = REG_IO_ERROR;
|
|
TRACE_CRIT(L"%!FUNC! failed while creating the LoggingEnabled registry value");
|
|
goto end;
|
|
}
|
|
|
|
}
|
|
else if (status != ERROR_SUCCESS)
|
|
{
|
|
lrResult = REG_IO_ERROR;
|
|
TRACE_CRIT(L"%!FUNC! failed while reading logging enabledLoggingEnabled registry value");
|
|
goto end;
|
|
}
|
|
|
|
size = MAXFILEPATHLEN*sizeof(WCHAR);
|
|
type = REG_SZ;
|
|
status = RegQueryValueEx(key, L"LogFileName", 0L, &type,
|
|
(LPBYTE) &m_szLogFileName, &size);
|
|
|
|
if (status == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
//
|
|
// Create the regkey and initialize to empty string
|
|
//
|
|
status = RegSetValueEx(key, L"LogFileName", 0L, REG_SZ, (LPBYTE) &m_szLogFileName, size);
|
|
|
|
if (status != ERROR_SUCCESS)
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! failed while creating LogFileName registry value");
|
|
lrResult = REG_IO_ERROR;
|
|
goto end;
|
|
}
|
|
}
|
|
else if (status == ERROR_MORE_DATA)
|
|
{
|
|
|
|
TRACE_CRIT(L"%!FUNC! the log file name in the registry is longer than the maximum number of characters supported: %d. Logging will not be started.", MAXSTRINGLEN-1);
|
|
goto end;
|
|
}
|
|
else if (status != ERROR_SUCCESS)
|
|
{
|
|
lrResult = REG_IO_ERROR;
|
|
TRACE_CRIT(L"%!FUNC! failed while reading LogFileName registry value");
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// Validate the log file name
|
|
//
|
|
if (!isDirectoryValid(m_szLogFileName))
|
|
{
|
|
lrResult = FILE_PATH_INVALID;
|
|
TRACE_CRIT(L"%!FUNC! LogFileName has an invalid file path \"%ws\"",
|
|
m_szLogFileName);
|
|
goto end;
|
|
}
|
|
|
|
if(m_dwLoggingEnabled != 0)
|
|
{
|
|
if (m_szLogFileName[0] == L'\0')
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! Logging is enabled but a log file name has not been specified. Logging will not be started.");
|
|
}
|
|
else
|
|
{
|
|
if (NULL == m_hStatusLog)
|
|
{
|
|
lrResult = startLogging();
|
|
}
|
|
}
|
|
}
|
|
|
|
end:
|
|
// Close handle to the registry
|
|
RegCloseKey(key);
|
|
|
|
return lrResult;
|
|
}
|
|
|
|
//
|
|
// Change settings in memory and registry to allow logging.
|
|
//
|
|
LONG Document::enableLogging()
|
|
{
|
|
LONG status = ERROR_INTERNAL_ERROR;
|
|
DWORD dwLoggingEnabled = 1;
|
|
HKEY key;
|
|
|
|
key = NlbMgrRegCreateKey(NULL);
|
|
|
|
if (key == NULL)
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! registry key doesn't exist");
|
|
status = ERROR_CANTOPEN;
|
|
goto end;
|
|
}
|
|
|
|
status = RegSetValueEx(key, L"LoggingEnabled", 0L, REG_DWORD, (LPBYTE) &dwLoggingEnabled, sizeof(DWORD));
|
|
|
|
//
|
|
// Ignore return value since we can't do anything if this fails.
|
|
//
|
|
RegCloseKey(key);
|
|
|
|
end:
|
|
|
|
if (ERROR_SUCCESS == status)
|
|
{
|
|
m_dwLoggingEnabled = dwLoggingEnabled;
|
|
}
|
|
|
|
TRACE_INFO(L"%!FUNC! returns status=%i", status);
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Change settings in memory and registry to prevent logging.
|
|
//
|
|
LONG Document::disableLogging()
|
|
{
|
|
LONG status = ERROR_INTERNAL_ERROR;
|
|
DWORD dwLoggingEnabled = 0;
|
|
HKEY key;
|
|
|
|
key = NlbMgrRegCreateKey(NULL);
|
|
|
|
if (key == NULL)
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! registry key doesn't exist");
|
|
status = ERROR_CANTOPEN;
|
|
goto end;
|
|
}
|
|
|
|
status = RegSetValueEx(key, L"LoggingEnabled", 0L, REG_DWORD, (LPBYTE) &dwLoggingEnabled, sizeof(DWORD));
|
|
|
|
//
|
|
// Ignore return value since we can't do anything if this fails.
|
|
//
|
|
RegCloseKey(key);
|
|
|
|
end:
|
|
if (ERROR_SUCCESS == status)
|
|
{
|
|
m_dwLoggingEnabled = dwLoggingEnabled;
|
|
}
|
|
|
|
TRACE_INFO(L"%!FUNC! returns status=%i", status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Log information sent to LogView to a file
|
|
//
|
|
Document::LOG_RESULT Document::startLogging()
|
|
{
|
|
Document::LOG_RESULT lrResult = STARTED;
|
|
|
|
if (m_fPrepareToDeinitialize)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
if(m_dwLoggingEnabled == 0)
|
|
{
|
|
lrResult = NOT_ENABLED;
|
|
TRACE_INFO(L"%!FUNC! failed because logging is not enabled");
|
|
goto end;
|
|
}
|
|
|
|
if (m_szLogFileName[0] == L'\0')
|
|
{
|
|
lrResult = NO_FILE_NAME;
|
|
TRACE_INFO(L"%!FUNC! failed because there is no log file name");
|
|
goto end;
|
|
}
|
|
|
|
if (NULL != m_hStatusLog)
|
|
{
|
|
//
|
|
// If we have a file open, we assume it is the correct one and return true
|
|
//
|
|
lrResult = ALREADY;
|
|
TRACE_INFO(L"%!FUNC! is already running");
|
|
goto end;
|
|
}
|
|
|
|
{
|
|
//
|
|
// Determine whether the log file exists.
|
|
//
|
|
boolean fWriteBOM = false;
|
|
{
|
|
FILE *hTmpLog = _wfsopen(m_szLogFileName, L"r", _SH_DENYNO);
|
|
|
|
if (NULL == hTmpLog)
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
if (dwError == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
//
|
|
// This is a new file. Set a flag so we can write the 2-byte BOM
|
|
// inside it to indicate the Unicode encoding. The write will be done
|
|
// when we open the file for appending below.
|
|
//
|
|
fWriteBOM = true;
|
|
}
|
|
else
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! failure %u while opening log file for read", dwError);
|
|
lrResult = IO_ERROR;
|
|
goto end;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The uninteresting case where the log file already exists. Close the file and move on.
|
|
//
|
|
fclose(hTmpLog);
|
|
}
|
|
}
|
|
|
|
//
|
|
// This is the "real" file-open for logging
|
|
//
|
|
if (NULL == (m_hStatusLog = _wfsopen(m_szLogFileName, L"a+b", _SH_DENYWR)))
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! failed to open log file");
|
|
lrResult = IO_ERROR;
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// Write the BOM to indicate that this file is Unicode encoded, but only for a new log file.
|
|
//
|
|
if (fWriteBOM)
|
|
{
|
|
//
|
|
// According to MSDN, a file opened for append will always write to the end of
|
|
// the file, regardless of fseek and fsetpos calls. We need the BOM at BOF but
|
|
// we are OK since we know this is a new file.
|
|
//
|
|
USHORT usBOM = (USHORT) BOM;
|
|
int i = fwrite(
|
|
&usBOM, // Pointer to a buffer to write to file
|
|
sizeof(usBOM), // The size of an item in bytes
|
|
1, // Max count (in units of 2nd arg) of items in buffer to write to the file
|
|
m_hStatusLog); // Pointer to the file stream
|
|
|
|
if (i != 1) // Number of units actually written to file
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! failed while writing Unicode BOM to pFILE 0x%p",
|
|
m_hStatusLog);
|
|
lrResult = IO_ERROR;
|
|
(void) stopLogging();
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now check if the file has exceeded the limit.
|
|
//
|
|
{
|
|
//
|
|
// seek to the end (sdk says fseek (or a write) needs to happen
|
|
// before a file opened with append reports the correct offset via
|
|
// ftell.)
|
|
//
|
|
int i = fseek(m_hStatusLog, 0, SEEK_END);
|
|
if (i != 0)
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! failure %lu attempting to seek to end of pFILE 0x%p",
|
|
i, m_hStatusLog);
|
|
lrResult = IO_ERROR;
|
|
(void) stopLogging();
|
|
goto end;
|
|
}
|
|
|
|
#if 0 // We won't fail now -- so that a subsequent write will create
|
|
// an entry in the in-memory log.
|
|
i = ftell(m_hStatusLog);
|
|
if (i == -1L)
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! failure %lu calling ftell(pFILE 0x%p)",
|
|
i, m_hStatusLog);
|
|
lrResult = IO_ERROR;
|
|
(void) stopLogging();
|
|
goto end;
|
|
}
|
|
|
|
if (i >= MAX_LOG_FILE_SIZE)
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! File size exceeded: %lu (limit=%lu)",
|
|
i, MAX_LOG_FILE_SIZE);
|
|
lrResult = FILE_TOO_LARGE;
|
|
(void) stopLogging();
|
|
goto end;
|
|
}
|
|
#endif // 0
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
return lrResult;
|
|
}
|
|
|
|
//
|
|
// Stop logging information sent to LogView to a file
|
|
//
|
|
bool Document::stopLogging()
|
|
{
|
|
bool ret = true;
|
|
|
|
if (NULL != m_hStatusLog)
|
|
{
|
|
if (0 == fclose(m_hStatusLog))
|
|
{
|
|
TRACE_INFO(L"%!FUNC! logging stopped");
|
|
m_hStatusLog = NULL;
|
|
}
|
|
else {
|
|
TRACE_CRIT(L"%!FUNC! failed to close log file");
|
|
ret = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE_INFO(L"%!FUNC! logging already stopped");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Retrieve the cached log file name
|
|
//
|
|
void Document::getLogfileName(WCHAR* pszFileName, DWORD dwBufLen)
|
|
{
|
|
wcsncat(pszFileName, m_szLogFileName, dwBufLen);
|
|
}
|
|
|
|
//
|
|
// Set the log file name in memory and registry. false = couldn't write file name to registry.
|
|
//
|
|
LONG Document::setLogfileName(WCHAR* pszFileName)
|
|
{
|
|
LONG status;
|
|
HKEY key;
|
|
|
|
ZeroMemory(m_szLogFileName, MAXFILEPATHLEN*sizeof(WCHAR));
|
|
|
|
if (NULL != pszFileName && pszFileName != L'\0')
|
|
{
|
|
//
|
|
// Truncate the file name if it is larger than what we can store.
|
|
// Buffer is already initialized so that the last WCHAR is NULL.
|
|
//
|
|
wcsncat(m_szLogFileName, pszFileName, MAXFILEPATHLEN-1);
|
|
}
|
|
|
|
//
|
|
// Write file name to the registry
|
|
//
|
|
key = NlbMgrRegCreateKey(NULL);
|
|
|
|
if (key == NULL)
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! registry key doesn't exist");
|
|
goto end;
|
|
}
|
|
|
|
status = RegSetValueEx(key, L"LogFileName", 0L, REG_SZ, (LPBYTE) &m_szLogFileName, MAXFILEPATHLEN*sizeof(WCHAR));
|
|
|
|
//
|
|
// Ignore return value since we can't do anything if this fails.
|
|
//
|
|
RegCloseKey(key);
|
|
|
|
end:
|
|
|
|
TRACE_INFO(L"%!FUNC! returns status=%i", status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Log and entry from LogView into the log file. Flush it immediately.
|
|
//
|
|
void Document::logStatus(WCHAR* pszStatus)
|
|
{
|
|
if (m_fPrepareToDeinitialize)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
|
|
if (m_dwLoggingEnabled == 0 || NULL == m_hStatusLog || NULL == pszStatus)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// Check if the log is grown too large ...
|
|
//
|
|
{
|
|
BOOL fStopLogging = FALSE;
|
|
|
|
int i = ftell(m_hStatusLog);
|
|
if (i == -1L)
|
|
{
|
|
TRACE_CRIT(L"%!FUNC! failure %lu calling ftell(pFILE 0x%p)",
|
|
i, m_hStatusLog);
|
|
(void) stopLogging();
|
|
goto end;
|
|
}
|
|
|
|
if (i >= MAX_LOG_FILE_SIZE)
|
|
{
|
|
CLocalLogger logDetails;
|
|
LogEntryHeader Header;
|
|
TRACE_CRIT(L"%!FUNC! File size exceeded: %lu (limit=%lu)",
|
|
i, MAX_LOG_FILE_SIZE);
|
|
(void) stopLogging();
|
|
|
|
//
|
|
// WANING -- we're logging, so this will cause this function
|
|
// (logStatus) to be reentered, however it will bail out early
|
|
// because we've stopped logging.
|
|
//
|
|
logDetails.Log(
|
|
IDS_LOGFILE_FILE_TOO_LARGE_DETAILS,
|
|
m_szLogFileName,
|
|
MAX_LOG_FILE_SIZE/1000
|
|
);
|
|
Header.type = LOG_ERROR;
|
|
Header.szDetails = logDetails.GetStringSafe();
|
|
this->LogEx(
|
|
&Header,
|
|
IDS_LOGFILE_FILE_TOO_LARGE
|
|
);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now actually log.
|
|
//
|
|
{
|
|
TRACE_INFO(L"%!FUNC! logging: %ls", pszStatus);
|
|
|
|
PWCHAR pc = pszStatus;
|
|
while (*pc != NULL)
|
|
{
|
|
if (*pc == '\n')
|
|
{
|
|
//
|
|
// TODO: fputwc could fail with WEOF...
|
|
//
|
|
fputwc('\r', m_hStatusLog);
|
|
}
|
|
fputwc(*pc, m_hStatusLog);
|
|
|
|
pc++;
|
|
}
|
|
|
|
fflush(m_hStatusLog);
|
|
}
|
|
|
|
end:
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check if the specified directory exists
|
|
//
|
|
// This function supports strings of the following format:
|
|
// c:\myfile.log
|
|
// c:myfile.log
|
|
// c:\mydir1\mydir2\...\mydirN\myfile.log
|
|
// The requirement is that the destination directory must exist and is not a file.
|
|
// IOW, if c:\mydir1\mydir2 is a file, this function will fail the validity test
|
|
// if the input file name is c:\mydir1\mydir2\myfile.log
|
|
//
|
|
bool Document::isDirectoryValid(WCHAR* pwszFileName)
|
|
{
|
|
bool fRet = false;
|
|
|
|
WCHAR pwszFullPath[_MAX_PATH + 1];
|
|
|
|
ASSERT(pwszFileName != NULL);
|
|
|
|
TRACE_INFO(L"-> Path = '%ls'", pwszFileName);
|
|
|
|
//
|
|
// Convert the input file name into a full path name (in case we are given a relative path)
|
|
//
|
|
if (_wfullpath(pwszFullPath, pwszFileName, _MAX_PATH) == NULL)
|
|
{
|
|
TRACE_CRIT(L"_wfullpath failed converting '%ls' to a full path. Name could be too long or could specify an invalid drive letter", pwszFileName);
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// Check the attributes of this file. We'll get an error if the specified path doesn't exist
|
|
//
|
|
DWORD dwAttrib = GetFileAttributes(pwszFullPath);
|
|
if (dwAttrib == INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
//
|
|
// The only error we will continue on is a "file doesn't exist" error
|
|
//
|
|
DWORD dwStatus = GetLastError();
|
|
if (dwStatus != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
TRACE_CRIT(L"Error %d retrieving file attributes for '%ls'", dwStatus, pwszFullPath);
|
|
goto end;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
TRACE_CRIT(L"'%ls' is a directory and can't be used as a log file", pwszFullPath);
|
|
goto end;
|
|
}
|
|
else if (dwAttrib & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
|
|
{
|
|
TRACE_CRIT(L"'%ls' can't be used as a log file because it is either an offline file, system file or a readonly file", pwszFullPath);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
fRet = true;
|
|
|
|
end:
|
|
|
|
TRACE_INFO(L"<- returns fRet=%u", fRet);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
/*
|
|
bool Document::isDirectoryValid(WCHAR* pwszFileName)
|
|
{
|
|
bool fRet = false;
|
|
|
|
CFile f;
|
|
CFileException e;
|
|
UINT uiOpenOptions = CFile::modeReadWrite | CFile::shareDenyWrite | CFile::modeCreate | CFile::modeNoTruncate;
|
|
|
|
WCHAR pwszFullPath[_MAX_PATH + 1];
|
|
|
|
ASSERT(pwszFileName != NULL);
|
|
|
|
TRACE_INFO(L"-> Path = '%ls'", pwszFileName);
|
|
|
|
//
|
|
// Convert the input file name into a full path name (in case we are given a relative path)
|
|
//
|
|
if (_wfullpath(pwszFullPath, pwszFileName, _MAX_PATH) == NULL)
|
|
{
|
|
TRACE_CRIT(L"_wfullpath failed converting '%ls' to a full path. Name could be too long or could specify an invalid drive letter", pwszFileName);
|
|
goto end;
|
|
}
|
|
|
|
if(!f.Open(pwszFullPath, uiOpenOptions, &e))
|
|
{
|
|
if (e.m_cause != CFileException::fileNotFound && e.m_cause != CFileException::none)
|
|
{
|
|
TRACE_CRIT(L"Test open failed for '%ls' with CFileException cause = %d. See ", pwszFullPath, e.m_cause);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
f.Close();
|
|
fRet = true;
|
|
|
|
end:
|
|
|
|
TRACE_INFO(L"<- returns fRet=%u", fRet);
|
|
|
|
return fRet;
|
|
}
|
|
*/
|
|
|
|
void Document::LoadHostsFromFile(_bstr_t &FileName)
|
|
{
|
|
CStdioFile HostListFile;
|
|
CString HostName;
|
|
WMI_CONNECTION_INFO ConnInfo;
|
|
|
|
|
|
ZeroMemory(&ConnInfo, sizeof(ConnInfo));
|
|
|
|
//
|
|
// Take the credentials from the options field.
|
|
//
|
|
_bstr_t bstrUserName;
|
|
_bstr_t bstrPassword;
|
|
this->getDefaultCredentials(bstrUserName, bstrPassword);
|
|
ConnInfo.szUserName = (LPCWSTR) bstrUserName;
|
|
ConnInfo.szPassword = (LPCWSTR) bstrPassword;
|
|
|
|
|
|
|
|
TRACE_INFO(L"-> %!FUNC! File name : %ls", (LPCWSTR)FileName);
|
|
|
|
if (m_fPrepareToDeinitialize)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
Log(LOG_INFORMATIONAL, NULL, NULL, IDS_LOG_BEGIN_LOADING_FROM_FILE, (LPCWSTR) FileName);
|
|
|
|
// Open file as a text file in read-only mode, allowing others to read when we have the file opened.
|
|
if (!HostListFile.Open(FileName, CFile::modeRead | CFile::shareDenyWrite | CFile::typeText))
|
|
{
|
|
AfxMessageBox((LPCTSTR)(GETRESOURCEIDSTRING(IDS_FILE_OPEN_FAILED) + FileName));
|
|
Log(LOG_ERROR, NULL, NULL, IDS_LOG_FILE_OPEN_FAILED, (LPCWSTR) FileName);
|
|
TRACE_CRIT(L"%!FUNC! Could not open file: %ws", (LPCWSTR) FileName);
|
|
goto end;
|
|
}
|
|
|
|
|
|
// Read host names from file in a loop
|
|
// Call LoadHost for each host
|
|
BeginWaitCursor();
|
|
while(HostListFile.ReadString(REF HostName))
|
|
{
|
|
LPCWSTR szHostName = (LPCWSTR) HostName;
|
|
|
|
//
|
|
// We skip blank lines, and lines beginning with whitespace followed
|
|
// by the ";" character, which we use as a comment char.
|
|
//
|
|
if (szHostName==NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Skip initial whitespage
|
|
//
|
|
szHostName = _wcsspnp(szHostName, L" \t\n\r");
|
|
if (szHostName==NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Skip if string is empty (we don't expect this because the other
|
|
// call didn't return NULL) OR the first char is a ';' character.
|
|
//
|
|
if (*szHostName == 0 || *szHostName == ';')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ConnInfo.szMachine = szHostName;
|
|
gEngine.LoadHost(&ConnInfo, NULL);
|
|
}
|
|
EndWaitCursor();
|
|
|
|
// Close file
|
|
HostListFile.Close();
|
|
|
|
end:
|
|
|
|
TRACE_INFO(L"<- %!FUNC!");
|
|
return;
|
|
}
|
|
|
|
Document::VIEWTYPE
|
|
Document::GetViewType(CWnd* pWnd)
|
|
{
|
|
VIEWTYPE vt = NO_VIEW;
|
|
if (pWnd == m_pLeftView)
|
|
{
|
|
vt = LEFTVIEW;
|
|
}
|
|
else if (pWnd == m_pDetailsView)
|
|
{
|
|
vt = DETAILSVIEW;
|
|
}
|
|
else if (pWnd == m_pLogView)
|
|
{
|
|
vt = LOGVIEW;
|
|
}
|
|
|
|
return vt;
|
|
}
|
|
|
|
/* Method: SetFocusNextView
|
|
* Description: Given the input window, sets the focus on the next view
|
|
*/
|
|
void
|
|
Document::SetFocusNextView(CWnd* pWnd, UINT nChar)
|
|
{
|
|
Document::VIEWTYPE vt = this->GetViewType(pWnd);
|
|
|
|
//
|
|
// 05/10/2002 JosephJ
|
|
// Note: we special case F6 and Details below because
|
|
// (a) We can't cycle through DetailsView for TAB, because we can't
|
|
// figure out how to capture TAB in DetailsView
|
|
// (b) We need to a special version of SetFocus for details view --
|
|
// check out DetailsView::SetFocus for details.
|
|
//
|
|
|
|
CWnd* pTmp = NULL;
|
|
switch(vt)
|
|
{
|
|
case LEFTVIEW:
|
|
if (nChar == VK_F6)
|
|
{
|
|
pTmp = m_pDetailsView;
|
|
}
|
|
else
|
|
{
|
|
pTmp = m_pLogView;
|
|
}
|
|
break;
|
|
case DETAILSVIEW:
|
|
pTmp = m_pLogView;
|
|
break;
|
|
case LOGVIEW:
|
|
pTmp = m_pLeftView;
|
|
break;
|
|
default:
|
|
pTmp = m_pLeftView;
|
|
break;
|
|
}
|
|
|
|
if (pTmp != NULL)
|
|
{
|
|
if (pTmp == m_pDetailsView)
|
|
{
|
|
m_pDetailsView->SetFocus();
|
|
}
|
|
else
|
|
{
|
|
pTmp->SetFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Method: SetFocusPrevView
|
|
* Description: Given the input window, sets the focus on the prev view
|
|
*/
|
|
void
|
|
Document::SetFocusPrevView(CWnd* pWnd, UINT nChar)
|
|
{
|
|
Document::VIEWTYPE vt = this->GetViewType(pWnd);
|
|
|
|
// 05/10/2002 JosephJ see note concerning VK_F6 and DetailsView in
|
|
// Document::SetFocusNextView
|
|
|
|
CWnd* pTmp = NULL;
|
|
switch(vt)
|
|
{
|
|
case LEFTVIEW:
|
|
pTmp = m_pLogView;
|
|
break;
|
|
case DETAILSVIEW:
|
|
pTmp = m_pLeftView;
|
|
break;
|
|
case LOGVIEW:
|
|
if (nChar == VK_F6)
|
|
{
|
|
pTmp = m_pDetailsView;
|
|
}
|
|
else
|
|
{
|
|
pTmp = m_pLeftView;
|
|
}
|
|
break;
|
|
default:
|
|
pTmp = m_pLeftView;
|
|
break;
|
|
}
|
|
|
|
if (pTmp != NULL)
|
|
{
|
|
if (pTmp == m_pDetailsView)
|
|
{
|
|
m_pDetailsView->SetFocus();
|
|
}
|
|
else
|
|
{
|
|
pTmp->SetFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Document::OnCloseDocument()
|
|
{
|
|
ASSERT(m_fPrepareToDeinitialize);
|
|
|
|
//
|
|
// Deinitialize log view
|
|
//
|
|
if (m_pLogView != NULL)
|
|
{
|
|
m_pLogView->Deinitialize();
|
|
}
|
|
|
|
//
|
|
// Deinitialize left view
|
|
//
|
|
if (m_pLeftView != NULL)
|
|
{
|
|
m_pLeftView->Deinitialize();
|
|
}
|
|
|
|
//
|
|
// Deinitialize details view
|
|
//
|
|
if (m_pDetailsView != NULL)
|
|
{
|
|
m_pDetailsView->Deinitialize();
|
|
}
|
|
|
|
//
|
|
// Deinitialize engine
|
|
//
|
|
gEngine.Deinitialize();
|
|
|
|
CDocument::OnCloseDocument();
|
|
}
|
|
|
|
VOID
|
|
Document::PrepareToClose(BOOL fBlock)
|
|
{
|
|
//
|
|
// Cancel any pending operations in the engine, and prevent any
|
|
// new operations to be launched. During this time, we want the
|
|
// views and the log to be updated, so we don't PrepareToDeinitialize
|
|
// for ourselves or the views yet...
|
|
//
|
|
{
|
|
CWaitCursor wait;
|
|
gEngine.PrepareToDeinitialize();
|
|
gEngine.CancelAllPendingOperations(fBlock);
|
|
}
|
|
|
|
if (!fBlock)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
//
|
|
// At this time there should be no more pending activity. Block
|
|
// any further updates to the views...
|
|
//
|
|
|
|
m_fPrepareToDeinitialize = TRUE;
|
|
|
|
if (m_pLeftView != NULL)
|
|
{
|
|
m_pLeftView->PrepareToDeinitialize();
|
|
}
|
|
if (m_pDetailsView != NULL)
|
|
{
|
|
m_pDetailsView->PrepareToDeinitialize();
|
|
}
|
|
if (m_pLogView != NULL)
|
|
{
|
|
m_pLogView->PrepareToDeinitialize();
|
|
}
|
|
|
|
end:
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
Document::mfn_DeferUIOperation(CUIWorkItem *pWorkItem)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
extern CWnd *g_pMainFormWnd;
|
|
|
|
if (g_pMainFormWnd != NULL)
|
|
{
|
|
fRet = g_pMainFormWnd->PostMessage(MYWM_DEFER_UI_MSG, 0, (LPARAM) pWorkItem);
|
|
#if 0
|
|
if (fRet)
|
|
{
|
|
DummyAction(L"PostMessage returns TRUE");
|
|
}
|
|
else
|
|
{
|
|
DummyAction(L"PostMessage returns FALSE");
|
|
}
|
|
#endif // 0
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
void
|
|
Document::HandleDeferedUIWorkItem(CUIWorkItem *pWorkItem)
|
|
{
|
|
if (m_fPrepareToDeinitialize)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
if (!theApplication.IsMainThread())
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
switch(pWorkItem->workItemType)
|
|
{
|
|
case CUIWorkItem::ITEM_LOG:
|
|
if (m_pLogView)
|
|
{
|
|
LogEntryHeader Header;
|
|
Header.type = pWorkItem->type;
|
|
Header.szCluster = pWorkItem->bstrCluster;
|
|
Header.szHost = pWorkItem->bstrHost;
|
|
Header.szDetails = pWorkItem->bstrDetails;
|
|
Header.szInterface = pWorkItem->bstrInterface;
|
|
m_pLogView->LogString(&Header, (LPCWSTR) pWorkItem->bstrText);
|
|
}
|
|
break;
|
|
|
|
case CUIWorkItem::ITEM_ENGINE_EVENT:
|
|
this->HandleEngineEvent(
|
|
pWorkItem->objtype,
|
|
pWorkItem->ehClusterId,
|
|
pWorkItem->ehObjId,
|
|
pWorkItem->evt
|
|
);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
end:
|
|
return;
|
|
}
|