Leaked source code of windows server 2003
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

  1. //***************************************************************************
  2. //
  3. // DOCUMENT.CPP
  4. //
  5. // Module: NLB Manager
  6. //
  7. // Purpose: Implements the document class for nlb manager.
  8. //
  9. // Copyright (c)2001-2002 Microsoft Corporation, All Rights Reserved
  10. //
  11. // History:
  12. //
  13. // 07/30/01 JosephJ Created based on MHakim's code
  14. //
  15. //***************************************************************************
  16. #include "precomp.h"
  17. #pragma hdrstop
  18. #include "private.h"
  19. #include "document.tmh"
  20. #include "wchar.h"
  21. #include "share.h"
  22. IMPLEMENT_DYNCREATE( Document, CDocument )
  23. CNlbEngine gEngine;
  24. CNlbMgrCommandLineInfo gCmdLineInfo;
  25. #define MAX_LOG_FILE_SIZE 10000000L // 10MB
  26. #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.
  27. Document::Document()
  28. :
  29. m_pLeftView(NULL),
  30. m_pDetailsView(NULL),
  31. m_pLogView(NULL),
  32. m_pNlbEngine(NULL),
  33. m_dwLoggingEnabled(NULL),
  34. m_hStatusLog(NULL),
  35. m_fPrepareToDeinitialize(FALSE)
  36. {
  37. TRACE_INFO("->%!FUNC!");
  38. *m_szLogFileName = 0;
  39. //
  40. // load the images which are used.
  41. //
  42. m_images48x48 = new CImageList;
  43. m_images48x48->Create( 16, // x
  44. 16, // y
  45. ILC_COLOR16, // 16 bit color
  46. 0, // initially image list is empty
  47. 10 ); // max images is 10. This value arbitrary.
  48. // Add the icons which we are going to use.
  49. // WARNING: these are added according to the order specified
  50. // in the Document::ICON_XXX enum
  51. //
  52. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTERS)); // 1
  53. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTER)); // 2
  54. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_STARTED)); // 3
  55. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_STOPPED)); // 4
  56. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_CONVERGING));
  57. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_SUSPENDED));
  58. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_DRAINING));
  59. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_DISCONNECTED));
  60. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_PORTRULE) ); // 5
  61. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_PENDING )); // 6
  62. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_MYINFORMATIONAL ));// 7
  63. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_MYWARNING )); // 8
  64. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_MYERROR )); // 9
  65. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTER_OK )); // 10
  66. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTER_PENDING ));// 11
  67. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_CLUSTER_BROKEN )); // 12
  68. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_OK )); // 13
  69. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_PENDING )); // 14
  70. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_MISCONFIGURED ));// 15
  71. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_UNREACHABLE ));// 16
  72. m_images48x48->Add( AfxGetApp()->LoadIcon( IDI_HOST_UNKNOWN )); // 17
  73. //
  74. // Initialize the NLB Engine.
  75. //
  76. // NOTE: "this", of type Document, inherits from NlbEngine::IUICallbacks,
  77. // so it is the IUICallbacks interface that gets passed into
  78. // gEngine.Initialize below.
  79. //
  80. NLBERROR NlbErr = gEngine.Initialize(
  81. REF *this,
  82. gCmdLineInfo.m_bDemo,
  83. gCmdLineInfo.m_bNoPing
  84. );
  85. if (NlbErr != NLBERR_OK)
  86. {
  87. TRACE_CRIT("%!FUNC!: gEngine.Initialize failed with error %08lx",
  88. NlbErr);
  89. //TODO: displayNlbError(ID_INITIALIZATION_FAILURE, NlbErr);
  90. }
  91. m_dwLoggingEnabled = 0;
  92. ZeroMemory(m_szLogFileName, MAXFILEPATHLEN*sizeof(WCHAR));
  93. m_hStatusLog = NULL;
  94. //
  95. // TODO: figure out what to do if we fail to initialize logging in the constructor!
  96. //
  97. // // 2/12/02 JosephJ SECURITY BUGBUG: need to inform user that they could not start logging.
  98. //
  99. initLogging();
  100. //
  101. // TODO: figure out how to DEinitialize!!!
  102. //
  103. TRACE_INFO("<-%!FUNC!");
  104. }
  105. Document::~Document()
  106. {
  107. // Don't check return value since we are exiting
  108. stopLogging();
  109. }
  110. void
  111. Document::registerLogView( LogView* logView )
  112. {
  113. m_pLogView = logView;
  114. }
  115. void
  116. Document::registerDetailsView( DetailsView* detailsView )
  117. {
  118. m_pDetailsView = detailsView;
  119. }
  120. DWORD WINAPI FinalInitialize(PVOID pvContext)
  121. //
  122. // This is typically called in the context of a work item.
  123. //
  124. {
  125. TRACE_INFO(L"-> %!FUNC!");
  126. // Check whether to connect to hosts specified in a host-list file
  127. if (gCmdLineInfo.m_bHostList)
  128. {
  129. ((Document *)(pvContext))->LoadHostsFromFile(gCmdLineInfo.m_bstrHostListFile);
  130. }
  131. TRACE_INFO(L"<- %!FUNC!");
  132. return 0;
  133. }
  134. void
  135. Document::registerLeftView(LeftView *pLeftView)
  136. {
  137. TRACE_INFO(L"-> %!FUNC!");
  138. m_pLeftView = pLeftView;
  139. // If there is a file containing the list of hosts to connect to,
  140. // read the file in a background thread. This is so that the UI
  141. // can show up while the communication with the hosts can go on
  142. // in the bakground. If this is NOT done, the UI will not show up
  143. // until we have heard from all of the hosts listed in the file.
  144. // -KarthicN
  145. if(!QueueUserWorkItem(FinalInitialize, this, WT_EXECUTEDEFAULT))
  146. {
  147. TRACE_CRIT(L"%!FUNC! QueueUserWorkItem failed with error : 0x%x", GetLastError());
  148. }
  149. TRACE_INFO(L"<- %!FUNC!");
  150. }
  151. //
  152. // Asks the user to update user-supplied info about a host.
  153. //
  154. BOOL
  155. Document::UpdateHostInformation(
  156. IN BOOL fNeedCredentials,
  157. IN BOOL fNeedConnectionString,
  158. IN OUT CHostSpec& host
  159. )
  160. {
  161. return FALSE;
  162. }
  163. //
  164. // Log a message in human-readable form.
  165. //
  166. void
  167. Document::Log(
  168. IN LogEntryType Type,
  169. IN const wchar_t *szCluster, OPTIONAL
  170. IN const wchar_t *szHost, OPTIONAL
  171. IN UINT ResourceID,
  172. ...
  173. )
  174. {
  175. WCHAR wszBuffer[1024];
  176. CString FormatString;
  177. if (m_fPrepareToDeinitialize)
  178. {
  179. goto end;
  180. }
  181. if (!FormatString.LoadString(ResourceID))
  182. {
  183. StringCbPrintf(wszBuffer, sizeof(wszBuffer), L"BAD/UNKNOWN resource ID %d", ResourceID);
  184. }
  185. else
  186. {
  187. DWORD dwRet;
  188. va_list arglist;
  189. va_start (arglist, ResourceID);
  190. dwRet = FormatMessage(
  191. FORMAT_MESSAGE_FROM_STRING,
  192. (LPCWSTR) FormatString,
  193. 0, // Message Identifier - Ignored
  194. 0, // Language Identifier
  195. wszBuffer,
  196. ASIZE(wszBuffer)-1,
  197. &arglist
  198. );
  199. if (dwRet == 0)
  200. {
  201. TRACE_CRIT("%!FUNC!: FormatMessage failed.");
  202. wszBuffer[0]=0;
  203. }
  204. va_end (arglist);
  205. }
  206. if (m_pLogView)
  207. {
  208. LogEntryHeader Header;
  209. Header.type = Type;
  210. Header.szCluster = szCluster;
  211. Header.szHost = szHost;
  212. if (!theApplication.IsMainThread())
  213. {
  214. //
  215. //
  216. // Let's allocate a UI work item and post the item to the mainform
  217. // thread so that the mainform thread will handle it.
  218. //
  219. //
  220. CUIWorkItem *pWorkItem = new CUIWorkItem(
  221. &Header,
  222. wszBuffer
  223. );
  224. if (pWorkItem != NULL)
  225. {
  226. if (!mfn_DeferUIOperation(pWorkItem))
  227. {
  228. delete pWorkItem;
  229. }
  230. }
  231. goto end;
  232. }
  233. m_pLogView->LogString(&Header, wszBuffer);
  234. }
  235. end:
  236. return;
  237. }
  238. //
  239. // Log a message in human-readable form.
  240. //
  241. void
  242. Document::LogEx(
  243. IN const LogEntryHeader *pHeader,
  244. IN UINT ResourceID,
  245. ...
  246. )
  247. {
  248. WCHAR wszBuffer[1024];
  249. CString FormatString;
  250. if (m_fPrepareToDeinitialize)
  251. {
  252. goto end;
  253. }
  254. if (!FormatString.LoadString(ResourceID))
  255. {
  256. StringCbPrintf(wszBuffer, sizeof(wszBuffer), L"BAD/UNKNOWN resource ID %d", ResourceID);
  257. }
  258. else
  259. {
  260. DWORD dwRet;
  261. va_list arglist;
  262. va_start (arglist, ResourceID);
  263. dwRet = FormatMessage(
  264. FORMAT_MESSAGE_FROM_STRING,
  265. (LPCWSTR) FormatString,
  266. 0, // Message Identifier - Ignored
  267. 0, // Language Identifier
  268. wszBuffer,
  269. ASIZE(wszBuffer)-1,
  270. &arglist
  271. );
  272. if (dwRet == 0)
  273. {
  274. TRACE_CRIT("%!FUNC!: FormatMessage failed.");
  275. wszBuffer[0]=0;
  276. }
  277. va_end (arglist);
  278. }
  279. if (m_pLogView)
  280. {
  281. if (!theApplication.IsMainThread())
  282. {
  283. //
  284. //
  285. // Let's allocate a UI work item and post the item to the mainform
  286. // thread so that the mainform thread will handle it.
  287. //
  288. //
  289. CUIWorkItem *pWorkItem = new CUIWorkItem(
  290. pHeader,
  291. wszBuffer
  292. );
  293. if (pWorkItem != NULL)
  294. {
  295. if (!mfn_DeferUIOperation(pWorkItem))
  296. {
  297. delete pWorkItem;
  298. }
  299. }
  300. goto end;
  301. }
  302. m_pLogView->LogString(pHeader, wszBuffer);
  303. }
  304. end:
  305. return;
  306. }
  307. //
  308. // Handle an event relating to a specific instance of a specific
  309. // object type.
  310. //
  311. void
  312. Document::HandleEngineEvent(
  313. IN ObjectType objtype,
  314. IN ENGINEHANDLE ehClusterId, // could be NULL
  315. IN ENGINEHANDLE ehObjId,
  316. IN EventCode evt
  317. )
  318. {
  319. TRACE_INFO(
  320. L"%!FUNC!: cid=%lx; id=%lx; obj=%lu, evt=%lu",
  321. (UINT) ehClusterId,
  322. (UINT) ehObjId,
  323. (UINT) objtype,
  324. (UINT) evt
  325. );
  326. if (m_fPrepareToDeinitialize)
  327. {
  328. goto end;
  329. }
  330. if (!theApplication.IsMainThread())
  331. {
  332. // DummyAction(L"HandleEngineEvent -- deferring UI");
  333. //
  334. //
  335. // Let's allocate a UI work item and post the item to the mainform
  336. // thread so that the mainform thread will handle it.
  337. //
  338. //
  339. CUIWorkItem *pWorkItem = new CUIWorkItem(
  340. objtype,
  341. ehClusterId,
  342. ehObjId,
  343. evt
  344. );
  345. if (pWorkItem != NULL)
  346. {
  347. if (!mfn_DeferUIOperation(pWorkItem))
  348. {
  349. delete pWorkItem;
  350. }
  351. }
  352. goto end;
  353. }
  354. //
  355. // TODO: consider locking and reference-counting below.
  356. //
  357. if (m_pLeftView != NULL)
  358. {
  359. m_pLeftView->HandleEngineEvent(objtype, ehClusterId, ehObjId, evt);
  360. }
  361. if (m_pDetailsView != NULL)
  362. {
  363. m_pDetailsView->HandleEngineEvent(objtype, ehClusterId, ehObjId, evt);
  364. }
  365. end:
  366. return;
  367. }
  368. //
  369. // Handle a selection change notification from the left (tree) view
  370. //
  371. void
  372. Document::HandleLeftViewSelChange(
  373. IN IUICallbacks::ObjectType objtype,
  374. IN ENGINEHANDLE ehObj
  375. )
  376. {
  377. if (m_fPrepareToDeinitialize)
  378. {
  379. goto end;
  380. }
  381. if (m_pDetailsView != NULL)
  382. {
  383. m_pDetailsView->HandleLeftViewSelChange(objtype, ehObj);
  384. }
  385. end:
  386. return;
  387. }
  388. //
  389. // Read registry settings. If there are none, create defaults according to what is set in constructor.
  390. //
  391. Document::LOG_RESULT Document::initLogging()
  392. {
  393. LOG_RESULT lrResult = REG_IO_ERROR;
  394. LONG status;
  395. HKEY key;
  396. WCHAR szKey[MAXSTRINGLEN];
  397. WCHAR szError[MAXSTRINGLEN];
  398. DWORD size;
  399. DWORD type;
  400. key = NlbMgrRegCreateKey(NULL);
  401. if (key == NULL)
  402. {
  403. TRACE_CRIT(L"%!FUNC! registry key doesn't exist");
  404. return REG_IO_ERROR;
  405. }
  406. size = sizeof (m_dwLoggingEnabled);
  407. type = REG_DWORD;
  408. status = RegQueryValueEx(key, L"LoggingEnabled", 0L, &type,
  409. (LPBYTE) &m_dwLoggingEnabled, &size);
  410. if (status == ERROR_FILE_NOT_FOUND)
  411. {
  412. //
  413. // Create the regkey and initialize to what is set in constructor
  414. //
  415. status = RegSetValueEx(key, L"LoggingEnabled", 0L, REG_DWORD, (LPBYTE) &m_dwLoggingEnabled, size);
  416. if (status != ERROR_SUCCESS)
  417. {
  418. lrResult = REG_IO_ERROR;
  419. TRACE_CRIT(L"%!FUNC! failed while creating the LoggingEnabled registry value");
  420. goto end;
  421. }
  422. }
  423. else if (status != ERROR_SUCCESS)
  424. {
  425. lrResult = REG_IO_ERROR;
  426. TRACE_CRIT(L"%!FUNC! failed while reading logging enabledLoggingEnabled registry value");
  427. goto end;
  428. }
  429. size = MAXFILEPATHLEN*sizeof(WCHAR);
  430. type = REG_SZ;
  431. status = RegQueryValueEx(key, L"LogFileName", 0L, &type,
  432. (LPBYTE) &m_szLogFileName, &size);
  433. if (status == ERROR_FILE_NOT_FOUND)
  434. {
  435. //
  436. // Create the regkey and initialize to empty string
  437. //
  438. status = RegSetValueEx(key, L"LogFileName", 0L, REG_SZ, (LPBYTE) &m_szLogFileName, size);
  439. if (status != ERROR_SUCCESS)
  440. {
  441. TRACE_CRIT(L"%!FUNC! failed while creating LogFileName registry value");
  442. lrResult = REG_IO_ERROR;
  443. goto end;
  444. }
  445. }
  446. else if (status == ERROR_MORE_DATA)
  447. {
  448. 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);
  449. goto end;
  450. }
  451. else if (status != ERROR_SUCCESS)
  452. {
  453. lrResult = REG_IO_ERROR;
  454. TRACE_CRIT(L"%!FUNC! failed while reading LogFileName registry value");
  455. goto end;
  456. }
  457. //
  458. // Validate the log file name
  459. //
  460. if (!isDirectoryValid(m_szLogFileName))
  461. {
  462. lrResult = FILE_PATH_INVALID;
  463. TRACE_CRIT(L"%!FUNC! LogFileName has an invalid file path \"%ws\"",
  464. m_szLogFileName);
  465. goto end;
  466. }
  467. if(m_dwLoggingEnabled != 0)
  468. {
  469. if (m_szLogFileName[0] == L'\0')
  470. {
  471. TRACE_CRIT(L"%!FUNC! Logging is enabled but a log file name has not been specified. Logging will not be started.");
  472. }
  473. else
  474. {
  475. if (NULL == m_hStatusLog)
  476. {
  477. lrResult = startLogging();
  478. }
  479. }
  480. }
  481. end:
  482. // Close handle to the registry
  483. RegCloseKey(key);
  484. return lrResult;
  485. }
  486. //
  487. // Change settings in memory and registry to allow logging.
  488. //
  489. LONG Document::enableLogging()
  490. {
  491. LONG status = ERROR_INTERNAL_ERROR;
  492. DWORD dwLoggingEnabled = 1;
  493. HKEY key;
  494. key = NlbMgrRegCreateKey(NULL);
  495. if (key == NULL)
  496. {
  497. TRACE_CRIT(L"%!FUNC! registry key doesn't exist");
  498. status = ERROR_CANTOPEN;
  499. goto end;
  500. }
  501. status = RegSetValueEx(key, L"LoggingEnabled", 0L, REG_DWORD, (LPBYTE) &dwLoggingEnabled, sizeof(DWORD));
  502. //
  503. // Ignore return value since we can't do anything if this fails.
  504. //
  505. RegCloseKey(key);
  506. end:
  507. if (ERROR_SUCCESS == status)
  508. {
  509. m_dwLoggingEnabled = dwLoggingEnabled;
  510. }
  511. TRACE_INFO(L"%!FUNC! returns status=%i", status);
  512. return status;
  513. }
  514. //
  515. // Change settings in memory and registry to prevent logging.
  516. //
  517. LONG Document::disableLogging()
  518. {
  519. LONG status = ERROR_INTERNAL_ERROR;
  520. DWORD dwLoggingEnabled = 0;
  521. HKEY key;
  522. key = NlbMgrRegCreateKey(NULL);
  523. if (key == NULL)
  524. {
  525. TRACE_CRIT(L"%!FUNC! registry key doesn't exist");
  526. status = ERROR_CANTOPEN;
  527. goto end;
  528. }
  529. status = RegSetValueEx(key, L"LoggingEnabled", 0L, REG_DWORD, (LPBYTE) &dwLoggingEnabled, sizeof(DWORD));
  530. //
  531. // Ignore return value since we can't do anything if this fails.
  532. //
  533. RegCloseKey(key);
  534. end:
  535. if (ERROR_SUCCESS == status)
  536. {
  537. m_dwLoggingEnabled = dwLoggingEnabled;
  538. }
  539. TRACE_INFO(L"%!FUNC! returns status=%i", status);
  540. return status;
  541. }
  542. //
  543. // Log information sent to LogView to a file
  544. //
  545. Document::LOG_RESULT Document::startLogging()
  546. {
  547. Document::LOG_RESULT lrResult = STARTED;
  548. if (m_fPrepareToDeinitialize)
  549. {
  550. goto end;
  551. }
  552. if(m_dwLoggingEnabled == 0)
  553. {
  554. lrResult = NOT_ENABLED;
  555. TRACE_INFO(L"%!FUNC! failed because logging is not enabled");
  556. goto end;
  557. }
  558. if (m_szLogFileName[0] == L'\0')
  559. {
  560. lrResult = NO_FILE_NAME;
  561. TRACE_INFO(L"%!FUNC! failed because there is no log file name");
  562. goto end;
  563. }
  564. if (NULL != m_hStatusLog)
  565. {
  566. //
  567. // If we have a file open, we assume it is the correct one and return true
  568. //
  569. lrResult = ALREADY;
  570. TRACE_INFO(L"%!FUNC! is already running");
  571. goto end;
  572. }
  573. {
  574. //
  575. // Determine whether the log file exists.
  576. //
  577. boolean fWriteBOM = false;
  578. {
  579. FILE *hTmpLog = _wfsopen(m_szLogFileName, L"r", _SH_DENYNO);
  580. if (NULL == hTmpLog)
  581. {
  582. DWORD dwError = GetLastError();
  583. if (dwError == ERROR_FILE_NOT_FOUND)
  584. {
  585. //
  586. // This is a new file. Set a flag so we can write the 2-byte BOM
  587. // inside it to indicate the Unicode encoding. The write will be done
  588. // when we open the file for appending below.
  589. //
  590. fWriteBOM = true;
  591. }
  592. else
  593. {
  594. TRACE_CRIT(L"%!FUNC! failure %u while opening log file for read", dwError);
  595. lrResult = IO_ERROR;
  596. goto end;
  597. }
  598. }
  599. else
  600. {
  601. //
  602. // The uninteresting case where the log file already exists. Close the file and move on.
  603. //
  604. fclose(hTmpLog);
  605. }
  606. }
  607. //
  608. // This is the "real" file-open for logging
  609. //
  610. if (NULL == (m_hStatusLog = _wfsopen(m_szLogFileName, L"a+b", _SH_DENYWR)))
  611. {
  612. TRACE_CRIT(L"%!FUNC! failed to open log file");
  613. lrResult = IO_ERROR;
  614. goto end;
  615. }
  616. //
  617. // Write the BOM to indicate that this file is Unicode encoded, but only for a new log file.
  618. //
  619. if (fWriteBOM)
  620. {
  621. //
  622. // According to MSDN, a file opened for append will always write to the end of
  623. // the file, regardless of fseek and fsetpos calls. We need the BOM at BOF but
  624. // we are OK since we know this is a new file.
  625. //
  626. USHORT usBOM = (USHORT) BOM;
  627. int i = fwrite(
  628. &usBOM, // Pointer to a buffer to write to file
  629. sizeof(usBOM), // The size of an item in bytes
  630. 1, // Max count (in units of 2nd arg) of items in buffer to write to the file
  631. m_hStatusLog); // Pointer to the file stream
  632. if (i != 1) // Number of units actually written to file
  633. {
  634. TRACE_CRIT(L"%!FUNC! failed while writing Unicode BOM to pFILE 0x%p",
  635. m_hStatusLog);
  636. lrResult = IO_ERROR;
  637. (void) stopLogging();
  638. goto end;
  639. }
  640. }
  641. }
  642. //
  643. // Now check if the file has exceeded the limit.
  644. //
  645. {
  646. //
  647. // seek to the end (sdk says fseek (or a write) needs to happen
  648. // before a file opened with append reports the correct offset via
  649. // ftell.)
  650. //
  651. int i = fseek(m_hStatusLog, 0, SEEK_END);
  652. if (i != 0)
  653. {
  654. TRACE_CRIT(L"%!FUNC! failure %lu attempting to seek to end of pFILE 0x%p",
  655. i, m_hStatusLog);
  656. lrResult = IO_ERROR;
  657. (void) stopLogging();
  658. goto end;
  659. }
  660. #if 0 // We won't fail now -- so that a subsequent write will create
  661. // an entry in the in-memory log.
  662. i = ftell(m_hStatusLog);
  663. if (i == -1L)
  664. {
  665. TRACE_CRIT(L"%!FUNC! failure %lu calling ftell(pFILE 0x%p)",
  666. i, m_hStatusLog);
  667. lrResult = IO_ERROR;
  668. (void) stopLogging();
  669. goto end;
  670. }
  671. if (i >= MAX_LOG_FILE_SIZE)
  672. {
  673. TRACE_CRIT(L"%!FUNC! File size exceeded: %lu (limit=%lu)",
  674. i, MAX_LOG_FILE_SIZE);
  675. lrResult = FILE_TOO_LARGE;
  676. (void) stopLogging();
  677. goto end;
  678. }
  679. #endif // 0
  680. }
  681. end:
  682. return lrResult;
  683. }
  684. //
  685. // Stop logging information sent to LogView to a file
  686. //
  687. bool Document::stopLogging()
  688. {
  689. bool ret = true;
  690. if (NULL != m_hStatusLog)
  691. {
  692. if (0 == fclose(m_hStatusLog))
  693. {
  694. TRACE_INFO(L"%!FUNC! logging stopped");
  695. m_hStatusLog = NULL;
  696. }
  697. else {
  698. TRACE_CRIT(L"%!FUNC! failed to close log file");
  699. ret = false;
  700. }
  701. }
  702. else
  703. {
  704. TRACE_INFO(L"%!FUNC! logging already stopped");
  705. }
  706. return ret;
  707. }
  708. //
  709. // Retrieve the cached log file name
  710. //
  711. void Document::getLogfileName(WCHAR* pszFileName, DWORD dwBufLen)
  712. {
  713. wcsncat(pszFileName, m_szLogFileName, dwBufLen);
  714. }
  715. //
  716. // Set the log file name in memory and registry. false = couldn't write file name to registry.
  717. //
  718. LONG Document::setLogfileName(WCHAR* pszFileName)
  719. {
  720. LONG status;
  721. HKEY key;
  722. ZeroMemory(m_szLogFileName, MAXFILEPATHLEN*sizeof(WCHAR));
  723. if (NULL != pszFileName && pszFileName != L'\0')
  724. {
  725. //
  726. // Truncate the file name if it is larger than what we can store.
  727. // Buffer is already initialized so that the last WCHAR is NULL.
  728. //
  729. wcsncat(m_szLogFileName, pszFileName, MAXFILEPATHLEN-1);
  730. }
  731. //
  732. // Write file name to the registry
  733. //
  734. key = NlbMgrRegCreateKey(NULL);
  735. if (key == NULL)
  736. {
  737. TRACE_CRIT(L"%!FUNC! registry key doesn't exist");
  738. goto end;
  739. }
  740. status = RegSetValueEx(key, L"LogFileName", 0L, REG_SZ, (LPBYTE) &m_szLogFileName, MAXFILEPATHLEN*sizeof(WCHAR));
  741. //
  742. // Ignore return value since we can't do anything if this fails.
  743. //
  744. RegCloseKey(key);
  745. end:
  746. TRACE_INFO(L"%!FUNC! returns status=%i", status);
  747. return status;
  748. }
  749. //
  750. // Log and entry from LogView into the log file. Flush it immediately.
  751. //
  752. void Document::logStatus(WCHAR* pszStatus)
  753. {
  754. if (m_fPrepareToDeinitialize)
  755. {
  756. goto end;
  757. }
  758. if (m_dwLoggingEnabled == 0 || NULL == m_hStatusLog || NULL == pszStatus)
  759. {
  760. goto end;
  761. }
  762. //
  763. // Check if the log is grown too large ...
  764. //
  765. {
  766. BOOL fStopLogging = FALSE;
  767. int i = ftell(m_hStatusLog);
  768. if (i == -1L)
  769. {
  770. TRACE_CRIT(L"%!FUNC! failure %lu calling ftell(pFILE 0x%p)",
  771. i, m_hStatusLog);
  772. (void) stopLogging();
  773. goto end;
  774. }
  775. if (i >= MAX_LOG_FILE_SIZE)
  776. {
  777. CLocalLogger logDetails;
  778. LogEntryHeader Header;
  779. TRACE_CRIT(L"%!FUNC! File size exceeded: %lu (limit=%lu)",
  780. i, MAX_LOG_FILE_SIZE);
  781. (void) stopLogging();
  782. //
  783. // WANING -- we're logging, so this will cause this function
  784. // (logStatus) to be reentered, however it will bail out early
  785. // because we've stopped logging.
  786. //
  787. logDetails.Log(
  788. IDS_LOGFILE_FILE_TOO_LARGE_DETAILS,
  789. m_szLogFileName,
  790. MAX_LOG_FILE_SIZE/1000
  791. );
  792. Header.type = LOG_ERROR;
  793. Header.szDetails = logDetails.GetStringSafe();
  794. this->LogEx(
  795. &Header,
  796. IDS_LOGFILE_FILE_TOO_LARGE
  797. );
  798. goto end;
  799. }
  800. }
  801. //
  802. // Now actually log.
  803. //
  804. {
  805. TRACE_INFO(L"%!FUNC! logging: %ls", pszStatus);
  806. PWCHAR pc = pszStatus;
  807. while (*pc != NULL)
  808. {
  809. if (*pc == '\n')
  810. {
  811. //
  812. // TODO: fputwc could fail with WEOF...
  813. //
  814. fputwc('\r', m_hStatusLog);
  815. }
  816. fputwc(*pc, m_hStatusLog);
  817. pc++;
  818. }
  819. fflush(m_hStatusLog);
  820. }
  821. end:
  822. return;
  823. }
  824. //
  825. // Check if the specified directory exists
  826. //
  827. // This function supports strings of the following format:
  828. // c:\myfile.log
  829. // c:myfile.log
  830. // c:\mydir1\mydir2\...\mydirN\myfile.log
  831. // The requirement is that the destination directory must exist and is not a file.
  832. // IOW, if c:\mydir1\mydir2 is a file, this function will fail the validity test
  833. // if the input file name is c:\mydir1\mydir2\myfile.log
  834. //
  835. bool Document::isDirectoryValid(WCHAR* pwszFileName)
  836. {
  837. bool fRet = false;
  838. WCHAR pwszFullPath[_MAX_PATH + 1];
  839. ASSERT(pwszFileName != NULL);
  840. TRACE_INFO(L"-> Path = '%ls'", pwszFileName);
  841. //
  842. // Convert the input file name into a full path name (in case we are given a relative path)
  843. //
  844. if (_wfullpath(pwszFullPath, pwszFileName, _MAX_PATH) == NULL)
  845. {
  846. TRACE_CRIT(L"_wfullpath failed converting '%ls' to a full path. Name could be too long or could specify an invalid drive letter", pwszFileName);
  847. goto end;
  848. }
  849. //
  850. // Check the attributes of this file. We'll get an error if the specified path doesn't exist
  851. //
  852. DWORD dwAttrib = GetFileAttributes(pwszFullPath);
  853. if (dwAttrib == INVALID_FILE_ATTRIBUTES)
  854. {
  855. //
  856. // The only error we will continue on is a "file doesn't exist" error
  857. //
  858. DWORD dwStatus = GetLastError();
  859. if (dwStatus != ERROR_FILE_NOT_FOUND)
  860. {
  861. TRACE_CRIT(L"Error %d retrieving file attributes for '%ls'", dwStatus, pwszFullPath);
  862. goto end;
  863. }
  864. }
  865. else
  866. {
  867. if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
  868. {
  869. TRACE_CRIT(L"'%ls' is a directory and can't be used as a log file", pwszFullPath);
  870. goto end;
  871. }
  872. else if (dwAttrib & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))
  873. {
  874. 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);
  875. goto end;
  876. }
  877. }
  878. fRet = true;
  879. end:
  880. TRACE_INFO(L"<- returns fRet=%u", fRet);
  881. return fRet;
  882. }
  883. /*
  884. bool Document::isDirectoryValid(WCHAR* pwszFileName)
  885. {
  886. bool fRet = false;
  887. CFile f;
  888. CFileException e;
  889. UINT uiOpenOptions = CFile::modeReadWrite | CFile::shareDenyWrite | CFile::modeCreate | CFile::modeNoTruncate;
  890. WCHAR pwszFullPath[_MAX_PATH + 1];
  891. ASSERT(pwszFileName != NULL);
  892. TRACE_INFO(L"-> Path = '%ls'", pwszFileName);
  893. //
  894. // Convert the input file name into a full path name (in case we are given a relative path)
  895. //
  896. if (_wfullpath(pwszFullPath, pwszFileName, _MAX_PATH) == NULL)
  897. {
  898. TRACE_CRIT(L"_wfullpath failed converting '%ls' to a full path. Name could be too long or could specify an invalid drive letter", pwszFileName);
  899. goto end;
  900. }
  901. if(!f.Open(pwszFullPath, uiOpenOptions, &e))
  902. {
  903. if (e.m_cause != CFileException::fileNotFound && e.m_cause != CFileException::none)
  904. {
  905. TRACE_CRIT(L"Test open failed for '%ls' with CFileException cause = %d. See ", pwszFullPath, e.m_cause);
  906. goto end;
  907. }
  908. }
  909. f.Close();
  910. fRet = true;
  911. end:
  912. TRACE_INFO(L"<- returns fRet=%u", fRet);
  913. return fRet;
  914. }
  915. */
  916. void Document::LoadHostsFromFile(_bstr_t &FileName)
  917. {
  918. CStdioFile HostListFile;
  919. CString HostName;
  920. WMI_CONNECTION_INFO ConnInfo;
  921. ZeroMemory(&ConnInfo, sizeof(ConnInfo));
  922. //
  923. // Take the credentials from the options field.
  924. //
  925. _bstr_t bstrUserName;
  926. _bstr_t bstrPassword;
  927. this->getDefaultCredentials(bstrUserName, bstrPassword);
  928. ConnInfo.szUserName = (LPCWSTR) bstrUserName;
  929. ConnInfo.szPassword = (LPCWSTR) bstrPassword;
  930. TRACE_INFO(L"-> %!FUNC! File name : %ls", (LPCWSTR)FileName);
  931. if (m_fPrepareToDeinitialize)
  932. {
  933. goto end;
  934. }
  935. Log(LOG_INFORMATIONAL, NULL, NULL, IDS_LOG_BEGIN_LOADING_FROM_FILE, (LPCWSTR) FileName);
  936. // Open file as a text file in read-only mode, allowing others to read when we have the file opened.
  937. if (!HostListFile.Open(FileName, CFile::modeRead | CFile::shareDenyWrite | CFile::typeText))
  938. {
  939. AfxMessageBox((LPCTSTR)(GETRESOURCEIDSTRING(IDS_FILE_OPEN_FAILED) + FileName));
  940. Log(LOG_ERROR, NULL, NULL, IDS_LOG_FILE_OPEN_FAILED, (LPCWSTR) FileName);
  941. TRACE_CRIT(L"%!FUNC! Could not open file: %ws", (LPCWSTR) FileName);
  942. goto end;
  943. }
  944. // Read host names from file in a loop
  945. // Call LoadHost for each host
  946. BeginWaitCursor();
  947. while(HostListFile.ReadString(REF HostName))
  948. {
  949. LPCWSTR szHostName = (LPCWSTR) HostName;
  950. //
  951. // We skip blank lines, and lines beginning with whitespace followed
  952. // by the ";" character, which we use as a comment char.
  953. //
  954. if (szHostName==NULL)
  955. {
  956. continue;
  957. }
  958. //
  959. // Skip initial whitespage
  960. //
  961. szHostName = _wcsspnp(szHostName, L" \t\n\r");
  962. if (szHostName==NULL)
  963. {
  964. continue;
  965. }
  966. //
  967. // Skip if string is empty (we don't expect this because the other
  968. // call didn't return NULL) OR the first char is a ';' character.
  969. //
  970. if (*szHostName == 0 || *szHostName == ';')
  971. {
  972. continue;
  973. }
  974. ConnInfo.szMachine = szHostName;
  975. gEngine.LoadHost(&ConnInfo, NULL);
  976. }
  977. EndWaitCursor();
  978. // Close file
  979. HostListFile.Close();
  980. end:
  981. TRACE_INFO(L"<- %!FUNC!");
  982. return;
  983. }
  984. Document::VIEWTYPE
  985. Document::GetViewType(CWnd* pWnd)
  986. {
  987. VIEWTYPE vt = NO_VIEW;
  988. if (pWnd == m_pLeftView)
  989. {
  990. vt = LEFTVIEW;
  991. }
  992. else if (pWnd == m_pDetailsView)
  993. {
  994. vt = DETAILSVIEW;
  995. }
  996. else if (pWnd == m_pLogView)
  997. {
  998. vt = LOGVIEW;
  999. }
  1000. return vt;
  1001. }
  1002. /* Method: SetFocusNextView
  1003. * Description: Given the input window, sets the focus on the next view
  1004. */
  1005. void
  1006. Document::SetFocusNextView(CWnd* pWnd, UINT nChar)
  1007. {
  1008. Document::VIEWTYPE vt = this->GetViewType(pWnd);
  1009. //
  1010. // 05/10/2002 JosephJ
  1011. // Note: we special case F6 and Details below because
  1012. // (a) We can't cycle through DetailsView for TAB, because we can't
  1013. // figure out how to capture TAB in DetailsView
  1014. // (b) We need to a special version of SetFocus for details view --
  1015. // check out DetailsView::SetFocus for details.
  1016. //
  1017. CWnd* pTmp = NULL;
  1018. switch(vt)
  1019. {
  1020. case LEFTVIEW:
  1021. if (nChar == VK_F6)
  1022. {
  1023. pTmp = m_pDetailsView;
  1024. }
  1025. else
  1026. {
  1027. pTmp = m_pLogView;
  1028. }
  1029. break;
  1030. case DETAILSVIEW:
  1031. pTmp = m_pLogView;
  1032. break;
  1033. case LOGVIEW:
  1034. pTmp = m_pLeftView;
  1035. break;
  1036. default:
  1037. pTmp = m_pLeftView;
  1038. break;
  1039. }
  1040. if (pTmp != NULL)
  1041. {
  1042. if (pTmp == m_pDetailsView)
  1043. {
  1044. m_pDetailsView->SetFocus();
  1045. }
  1046. else
  1047. {
  1048. pTmp->SetFocus();
  1049. }
  1050. }
  1051. }
  1052. /* Method: SetFocusPrevView
  1053. * Description: Given the input window, sets the focus on the prev view
  1054. */
  1055. void
  1056. Document::SetFocusPrevView(CWnd* pWnd, UINT nChar)
  1057. {
  1058. Document::VIEWTYPE vt = this->GetViewType(pWnd);
  1059. // 05/10/2002 JosephJ see note concerning VK_F6 and DetailsView in
  1060. // Document::SetFocusNextView
  1061. CWnd* pTmp = NULL;
  1062. switch(vt)
  1063. {
  1064. case LEFTVIEW:
  1065. pTmp = m_pLogView;
  1066. break;
  1067. case DETAILSVIEW:
  1068. pTmp = m_pLeftView;
  1069. break;
  1070. case LOGVIEW:
  1071. if (nChar == VK_F6)
  1072. {
  1073. pTmp = m_pDetailsView;
  1074. }
  1075. else
  1076. {
  1077. pTmp = m_pLeftView;
  1078. }
  1079. break;
  1080. default:
  1081. pTmp = m_pLeftView;
  1082. break;
  1083. }
  1084. if (pTmp != NULL)
  1085. {
  1086. if (pTmp == m_pDetailsView)
  1087. {
  1088. m_pDetailsView->SetFocus();
  1089. }
  1090. else
  1091. {
  1092. pTmp->SetFocus();
  1093. }
  1094. }
  1095. }
  1096. void
  1097. Document::OnCloseDocument()
  1098. {
  1099. ASSERT(m_fPrepareToDeinitialize);
  1100. //
  1101. // Deinitialize log view
  1102. //
  1103. if (m_pLogView != NULL)
  1104. {
  1105. m_pLogView->Deinitialize();
  1106. }
  1107. //
  1108. // Deinitialize left view
  1109. //
  1110. if (m_pLeftView != NULL)
  1111. {
  1112. m_pLeftView->Deinitialize();
  1113. }
  1114. //
  1115. // Deinitialize details view
  1116. //
  1117. if (m_pDetailsView != NULL)
  1118. {
  1119. m_pDetailsView->Deinitialize();
  1120. }
  1121. //
  1122. // Deinitialize engine
  1123. //
  1124. gEngine.Deinitialize();
  1125. CDocument::OnCloseDocument();
  1126. }
  1127. VOID
  1128. Document::PrepareToClose(BOOL fBlock)
  1129. {
  1130. //
  1131. // Cancel any pending operations in the engine, and prevent any
  1132. // new operations to be launched. During this time, we want the
  1133. // views and the log to be updated, so we don't PrepareToDeinitialize
  1134. // for ourselves or the views yet...
  1135. //
  1136. {
  1137. CWaitCursor wait;
  1138. gEngine.PrepareToDeinitialize();
  1139. gEngine.CancelAllPendingOperations(fBlock);
  1140. }
  1141. if (!fBlock)
  1142. {
  1143. goto end;
  1144. }
  1145. //
  1146. // At this time there should be no more pending activity. Block
  1147. // any further updates to the views...
  1148. //
  1149. m_fPrepareToDeinitialize = TRUE;
  1150. if (m_pLeftView != NULL)
  1151. {
  1152. m_pLeftView->PrepareToDeinitialize();
  1153. }
  1154. if (m_pDetailsView != NULL)
  1155. {
  1156. m_pDetailsView->PrepareToDeinitialize();
  1157. }
  1158. if (m_pLogView != NULL)
  1159. {
  1160. m_pLogView->PrepareToDeinitialize();
  1161. }
  1162. end:
  1163. return;
  1164. }
  1165. BOOL
  1166. Document::mfn_DeferUIOperation(CUIWorkItem *pWorkItem)
  1167. {
  1168. BOOL fRet = FALSE;
  1169. extern CWnd *g_pMainFormWnd;
  1170. if (g_pMainFormWnd != NULL)
  1171. {
  1172. fRet = g_pMainFormWnd->PostMessage(MYWM_DEFER_UI_MSG, 0, (LPARAM) pWorkItem);
  1173. #if 0
  1174. if (fRet)
  1175. {
  1176. DummyAction(L"PostMessage returns TRUE");
  1177. }
  1178. else
  1179. {
  1180. DummyAction(L"PostMessage returns FALSE");
  1181. }
  1182. #endif // 0
  1183. }
  1184. return fRet;
  1185. }
  1186. void
  1187. Document::HandleDeferedUIWorkItem(CUIWorkItem *pWorkItem)
  1188. {
  1189. if (m_fPrepareToDeinitialize)
  1190. {
  1191. goto end;
  1192. }
  1193. if (!theApplication.IsMainThread())
  1194. {
  1195. goto end;
  1196. }
  1197. switch(pWorkItem->workItemType)
  1198. {
  1199. case CUIWorkItem::ITEM_LOG:
  1200. if (m_pLogView)
  1201. {
  1202. LogEntryHeader Header;
  1203. Header.type = pWorkItem->type;
  1204. Header.szCluster = pWorkItem->bstrCluster;
  1205. Header.szHost = pWorkItem->bstrHost;
  1206. Header.szDetails = pWorkItem->bstrDetails;
  1207. Header.szInterface = pWorkItem->bstrInterface;
  1208. m_pLogView->LogString(&Header, (LPCWSTR) pWorkItem->bstrText);
  1209. }
  1210. break;
  1211. case CUIWorkItem::ITEM_ENGINE_EVENT:
  1212. this->HandleEngineEvent(
  1213. pWorkItem->objtype,
  1214. pWorkItem->ehClusterId,
  1215. pWorkItem->ehObjId,
  1216. pWorkItem->evt
  1217. );
  1218. break;
  1219. default:
  1220. break;
  1221. }
  1222. end:
  1223. return;
  1224. }