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.

634 lines
20 KiB

  1. //#pragma title( "Err.cpp - Basic error handling/message/logging" )
  2. /*
  3. Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
  4. ===============================================================================
  5. Module - Err.cpp
  6. System - Common
  7. Author - Tom Bernhardt, Rich Denham
  8. Created - 1994-08-22
  9. Description - Implements the TError class that handles basic exception
  10. handling, message generation, and logging functions.
  11. Updates - 1997-09-12 RED replace TTime class
  12. ===============================================================================
  13. */
  14. #ifdef USE_STDAFX
  15. # include "stdafx.h"
  16. #else
  17. # include <windows.h>
  18. #endif
  19. #ifndef WIN16_VERSION
  20. #include <lm.h>
  21. #endif
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <fcntl.h>
  25. #include <io.h>
  26. #include <string.h>
  27. #include <stdarg.h>
  28. #include <share.h>
  29. #include <time.h>
  30. #include <rpc.h>
  31. #include <rpcdce.h>
  32. #include <sys\types.h>
  33. #include <sys\stat.h>
  34. #include "Common.hpp"
  35. #include "Err.hpp"
  36. #include "UString.hpp"
  37. #include <ResStr.h>
  38. #include "TReg.hpp"
  39. #define TERR_MAX_MSG_LEN (2000)
  40. #define BYTE_ORDER_MARK (0xFEFF)
  41. TCriticalSection csLogError;
  42. TError::TError(
  43. int displevel ,// in -mimimum severity level to display
  44. int loglevel ,// in -mimimum severity level to log
  45. WCHAR const * filename ,// in -file name of log (NULL if none)
  46. int logmode ,// in -0=replace, 1=append
  47. int beeplevel // in -min error level for beeping
  48. )
  49. {
  50. lastError = 0;
  51. maxError = 0;
  52. logLevel = loglevel;
  53. dispLevel = displevel;
  54. logFile = INVALID_HANDLE_VALUE;
  55. beepLevel = beeplevel;
  56. bWriteOnCurPos = FALSE;
  57. LogOpen(filename, logmode, loglevel);
  58. }
  59. TError::~TError()
  60. {
  61. LogClose();
  62. }
  63. // Closes any existing open logFile and opens a new log file if the fileName is
  64. // not null. If it is a null string, then a default fileName of "Temp.log" is
  65. // used.
  66. BOOL
  67. TError::LogOpen(
  68. WCHAR const * fileName ,// in -name of file including any path
  69. int mode ,// in -0=overwrite, 1=append
  70. int level ,// in -minimum level to log
  71. bool bBeginNew // in -begin a new log file
  72. )
  73. {
  74. BOOL retval=TRUE;
  75. if ( logFile != INVALID_HANDLE_VALUE )
  76. {
  77. CloseHandle(logFile);
  78. logFile = INVALID_HANDLE_VALUE;
  79. }
  80. if ( fileName && fileName[0] )
  81. {
  82. // Check to see if the file already exists
  83. WIN32_FIND_DATA fDat;
  84. HANDLE hFind;
  85. BOOL bExisted = FALSE;
  86. hFind = FindFirstFile(fileName,&fDat);
  87. if ( hFind != INVALID_HANDLE_VALUE )
  88. {
  89. FindClose(hFind);
  90. if (bBeginNew)
  91. {
  92. // rename existing log file
  93. // get next sequence number from registry
  94. DWORD dwSequence = 1;
  95. static WCHAR c_szValueName[] = L"LogSeqNum";
  96. TRegKey key(GET_STRING(IDS_HKLM_DomainAdmin_Key));
  97. key.ValueGetDWORD(c_szValueName, &dwSequence);
  98. // split path components
  99. WCHAR szPath[_MAX_PATH];
  100. WCHAR szDrive[_MAX_DRIVE];
  101. WCHAR szDir[_MAX_DIR];
  102. WCHAR szFName[_MAX_FNAME];
  103. WCHAR szExt[_MAX_EXT];
  104. _wsplitpath(fileName, szDrive, szDir, szFName, szExt);
  105. // find name for backup that isn't already used...
  106. for (bool bFoundName = false; bFoundName == false; dwSequence++)
  107. {
  108. // generate backup name using the sequence number
  109. WCHAR szFNameSeq[_MAX_FNAME];
  110. wsprintf(szFNameSeq, L"%s %04lu", szFName, dwSequence);
  111. // make path from path components
  112. _wmakepath(szPath, szDrive, szDir, szFNameSeq, szExt);
  113. // check if file exists
  114. WIN32_FIND_DATA fd;
  115. HANDLE hFind = FindFirstFile(szPath, &fd);
  116. if (hFind == INVALID_HANDLE_VALUE)
  117. {
  118. DWORD dwError = GetLastError();
  119. if (dwError == ERROR_FILE_NOT_FOUND)
  120. {
  121. bFoundName = true;
  122. }
  123. }
  124. else
  125. {
  126. FindClose(hFind);
  127. }
  128. }
  129. if (bFoundName)
  130. {
  131. // attempt to rename file
  132. if (MoveFile(fileName, szPath))
  133. {
  134. // save next sequence number in registry
  135. key.ValueSetDWORD(c_szValueName, dwSequence);
  136. }
  137. else
  138. {
  139. bExisted = TRUE;
  140. }
  141. }
  142. if (!bExisted)
  143. {
  144. // get log history value from registry
  145. TRegKey keyHistory(GET_STRING(IDS_HKLM_DomainAdmin_Key));
  146. DWORD dwHistory = 20;
  147. static WCHAR c_szValueName[] = L"LogHistory";
  148. if (keyHistory.ValueGetDWORD(c_szValueName, &dwHistory) == ERROR_FILE_NOT_FOUND)
  149. {
  150. keyHistory.ValueSetDWORD(c_szValueName, dwHistory);
  151. }
  152. keyHistory.Close();
  153. if (dwSequence > dwHistory)
  154. {
  155. DWORD dwMinimum = dwSequence - dwHistory;
  156. // generate migration log path specification
  157. WCHAR szFNameSpec[_MAX_FNAME];
  158. wsprintf(szFNameSpec, L"%s *", szFName);
  159. _wmakepath(szPath, szDrive, szDir, szFNameSpec, szExt);
  160. // for each migration older than minimum
  161. WIN32_FIND_DATA fd;
  162. HANDLE hFind = FindFirstFile(szPath, &fd);
  163. if (hFind != INVALID_HANDLE_VALUE)
  164. {
  165. do
  166. {
  167. DWORD dwFileSequence;
  168. if (swscanf(fd.cFileName, L"%*s %lu", &dwFileSequence) == 1)
  169. {
  170. // if file sequence less than minimum to keep...
  171. if (dwFileSequence < dwMinimum)
  172. {
  173. // delete file
  174. WCHAR szDeleteName[_MAX_FNAME];
  175. _wsplitpath(fd.cFileName, 0, 0, szDeleteName, 0);
  176. WCHAR szDeletePath[_MAX_PATH];
  177. _wmakepath(szDeletePath, szDrive, szDir, szDeleteName, szExt);
  178. DeleteFile(szDeletePath);
  179. }
  180. }
  181. }
  182. while (FindNextFile(hFind, &fd));
  183. FindClose(hFind);
  184. }
  185. }
  186. }
  187. key.Close();
  188. }
  189. else
  190. {
  191. // overwrite or append to existing log file
  192. bExisted = TRUE;
  193. }
  194. }
  195. logFile = CreateFile(fileName,
  196. GENERIC_READ | GENERIC_WRITE,
  197. FILE_SHARE_READ | FILE_SHARE_WRITE,
  198. NULL,
  199. bExisted ? OPEN_EXISTING : CREATE_NEW,
  200. FILE_ATTRIBUTE_NORMAL,
  201. NULL);
  202. if ( logFile == INVALID_HANDLE_VALUE )
  203. {
  204. retval = FALSE;
  205. }
  206. else
  207. {
  208. // the append case
  209. if (mode == 1)
  210. {
  211. if (SetFilePointer(logFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
  212. retval = FALSE;
  213. }
  214. // if it is a new file or in the overwrite mode, we write the
  215. // byte order mark into it
  216. if (retval && (!bExisted || mode == 0) )
  217. {
  218. // this is a new file we've just created
  219. // we need to write the byte order mark to the beginning of the file
  220. WCHAR x = BYTE_ORDER_MARK;
  221. DWORD nWritten;
  222. if (!WriteFile(logFile, &x, sizeof(x), &nWritten, NULL))
  223. retval = FALSE;
  224. }
  225. }
  226. }
  227. logLevel = level;
  228. return retval;
  229. }
  230. DWORD TError::ExtendSize(DWORD dwNumOfBytes)
  231. {
  232. DWORD rc = ERROR_SUCCESS;
  233. const int size = 4096; // we write in 4K chunk
  234. BYTE* buffer; // the buffer used to initialize the stream
  235. DWORD orig; // the starting file pointer
  236. SetLastError(ERROR_SUCCESS);
  237. // to extend by 0 byte, we don't need to do anything
  238. if (dwNumOfBytes > 0)
  239. {
  240. // get the current file pointer
  241. if ((orig = SetFilePointer(logFile, 0, NULL, FILE_CURRENT)) != INVALID_SET_FILE_POINTER)
  242. {
  243. buffer = new BYTE[size];
  244. if (buffer != NULL)
  245. {
  246. memset((void*)buffer, 0, size);
  247. // seek forward dwNumOfBytes bytes, set the end of file and
  248. // then come back to the current file pointer
  249. if (SetFilePointer(logFile, dwNumOfBytes, NULL, FILE_CURRENT) != INVALID_SET_FILE_POINTER
  250. && SetEndOfFile(logFile)
  251. && SetFilePointer(logFile, orig, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER)
  252. {
  253. // initialize the buffer
  254. DWORD nWritten = size;
  255. while (nWritten > 0 && dwNumOfBytes > 0)
  256. {
  257. if (!WriteFile(logFile,
  258. (void*)buffer,
  259. (dwNumOfBytes > size) ? size : dwNumOfBytes,
  260. &nWritten,
  261. NULL))
  262. {
  263. // if WriteFile failed, stop writing
  264. break;
  265. }
  266. dwNumOfBytes -= nWritten;
  267. }
  268. // if dwNumOfBytes of bytes are written, we flush the file buffer
  269. if (dwNumOfBytes == 0)
  270. {
  271. FlushFileBuffers(logFile);
  272. }
  273. }
  274. rc = GetLastError(); // we catch any error code here
  275. }
  276. else
  277. rc = ERROR_OUTOFMEMORY;
  278. // clean up
  279. if (buffer)
  280. delete[] buffer;
  281. // we always try to restore the file pointer
  282. if (SetFilePointer(logFile, orig, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER
  283. && rc == ERROR_SUCCESS)
  284. {
  285. // we only need to get the error if we didn't fail before
  286. rc = GetLastError();
  287. }
  288. }
  289. else
  290. rc = GetLastError();
  291. }
  292. return rc;
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Writes formatted message to log file and flushes buffers
  296. //-----------------------------------------------------------------------------
  297. void TError::LogWrite(WCHAR const * msg)
  298. {
  299. csLogError.Enter();
  300. WCHAR sTime[32];
  301. static WCHAR sTemp[TERR_MAX_MSG_LEN];
  302. DWORD size = sizeof(sTemp) / sizeof(sTemp[0]);
  303. gTTime.FormatIsoLcl( gTTime.Now( NULL ), sTime );
  304. _snwprintf(sTemp, size - 2, L"%s %s", sTime, msg); // leave room for "\r\n"
  305. sTemp[size - 3] = L'\0'; // make sure the string terminates
  306. DWORD dwLen = wcslen(sTemp);
  307. // Get rid of the <CR> from the end of the message because it causes things
  308. // to run together in the logs
  309. if ( sTemp[dwLen-1] == 0x0d )
  310. sTemp[dwLen-1] = 0x00;
  311. dwLen = wcslen(sTemp);
  312. wcscpy(&sTemp[dwLen], L"\r\n");
  313. dwLen = wcslen(sTemp);
  314. if ( logFile != INVALID_HANDLE_VALUE )
  315. {
  316. // make sure only one write at a time in the process
  317. criticalSection.Enter();
  318. BOOL bCanWrite = TRUE;
  319. if (!bWriteOnCurPos)
  320. {
  321. if (SetFilePointer(logFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
  322. bCanWrite = FALSE;
  323. }
  324. if (bCanWrite)
  325. {
  326. if (WriteFile(logFile, sTemp, dwLen * sizeof(WCHAR), &size, NULL))
  327. FlushFileBuffers(logFile);
  328. }
  329. criticalSection.Leave();
  330. }
  331. csLogError.Leave();
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Error message with format and arguments
  335. //-----------------------------------------------------------------------------
  336. void __cdecl
  337. TError::MsgWrite(
  338. int num ,// in -error number/level code
  339. WCHAR const msg[] ,// in -error message to display
  340. ... // in -printf args to msg pattern
  341. )
  342. {
  343. csLogError.Enter();
  344. static WCHAR suffix[TERR_MAX_MSG_LEN];
  345. va_list argPtr;
  346. va_start(argPtr, msg);
  347. _vsnwprintf(suffix, DIM(suffix) - 1, msg, argPtr);
  348. suffix[DIM(suffix) - 1] = L'\0';
  349. va_end(argPtr);
  350. MsgProcess(num, suffix);
  351. csLogError.Leave();
  352. }
  353. #ifndef WIN16_VERSION
  354. //-----------------------------------------------------------------------------
  355. // System Error message with format and arguments
  356. //-----------------------------------------------------------------------------
  357. void __cdecl
  358. TError::SysMsgWrite(
  359. int num ,// in -error number/level code
  360. DWORD lastRc ,// in -error return code
  361. WCHAR const msg[] ,// in -error message/pattern to display
  362. ... // in -printf args to msg pattern
  363. )
  364. {
  365. csLogError.Enter();
  366. static WCHAR suffix[TERR_MAX_MSG_LEN];
  367. va_list argPtr;
  368. int len;
  369. // When an error occurs while in a constructor for a global object,
  370. // the TError object may not yet exist. In this case, "this" is zero
  371. // and we gotta get out of here before we generate a protection exception.
  372. if ( !this )
  373. return;
  374. va_start(argPtr, msg);
  375. len = _vsnwprintf(suffix, DIM(suffix) - 1, msg, argPtr);
  376. // append the system message for the lastRc at the end.
  377. if ( len < DIM(suffix) - 1 )
  378. {
  379. ErrorCodeToText(lastRc, DIM(suffix) - len - 1, suffix + len);
  380. }
  381. suffix[DIM(suffix) - 1] = L'\0';
  382. va_end(argPtr);
  383. MsgProcess(num, suffix);
  384. csLogError.Leave();
  385. }
  386. //-----------------------------------------------------------------------------
  387. // System Error message with format and arguments
  388. //-----------------------------------------------------------------------------
  389. void __cdecl
  390. TError::SysMsgWrite(
  391. int num ,// in -error number/level code
  392. WCHAR const msg[] ,// in -error message/pattern to display
  393. ... // in -printf args to msg pattern
  394. )
  395. {
  396. csLogError.Enter();
  397. static WCHAR suffix[TERR_MAX_MSG_LEN];
  398. va_list argPtr;
  399. int len;
  400. DWORD lastRc = GetLastError();
  401. // When an error occurs while in a constructor for a global object,
  402. // the TError object may not yet exist. In this case, "this" is zero
  403. // and we gotta get out of here before we generate a protection exception.
  404. if ( !this )
  405. return;
  406. va_start( argPtr, msg );
  407. len = _vsnwprintf( suffix, DIM(suffix) - 1, msg, argPtr );
  408. // append the system message for the lastRc at the end.
  409. if ( len < DIM(suffix) - 1 )
  410. {
  411. ErrorCodeToText(lastRc, DIM(suffix) - len - 1, suffix + len);
  412. }
  413. suffix[DIM(suffix) - 1] = L'\0';
  414. va_end(argPtr);
  415. MsgProcess(num, suffix);
  416. csLogError.Leave();
  417. }
  418. #endif
  419. //-----------------------------------------------------------------------------
  420. // Error message format, display and exception processing function
  421. //-----------------------------------------------------------------------------
  422. void __stdcall
  423. TError::MsgProcess(
  424. int num ,// in -error number/level code
  425. WCHAR const * str // in -error string to display
  426. )
  427. {
  428. csLogError.Enter();
  429. static WCHAR fullmsg[TERR_MAX_MSG_LEN];
  430. struct
  431. {
  432. USHORT frequency; // audio frequency
  433. USHORT duration; // duration in mSec
  434. } audio[] = {{ 300, 20},{ 500, 50},{ 700, 100},
  435. { 800, 200},{1000, 300},{1500, 400},
  436. {2500, 750},{2500,1000},{2500,1000}};
  437. if ( num >= 0 )
  438. level = num / 10000; // 10000's position of error number
  439. else
  440. level = -1;
  441. if ( level <= 0 )
  442. {
  443. wcsncpy(fullmsg, str, DIM(fullmsg));
  444. fullmsg[DIM(fullmsg) - 1] = L'\0'; // ensure null termination
  445. }
  446. else
  447. {
  448. if ( num > maxError )
  449. maxError = num;
  450. _snwprintf(fullmsg, DIM(fullmsg), L"%s%1d:%04d %-s", (level <= 1) ? L"WRN" : L"ERR", level, num % 10000, str);
  451. fullmsg[DIM(fullmsg) - 1] = L'\0'; // ensure null termination
  452. }
  453. lastError = num;
  454. if ( level >= beepLevel )
  455. Beep(audio[level].frequency, audio[level].duration);
  456. if ( level >= logLevel )
  457. LogWrite(fullmsg);
  458. if ( level > 4 )
  459. {
  460. exit(level);
  461. }
  462. csLogError.Leave();
  463. }
  464. //-----------------------------------------------------------------------------
  465. // Return text for error code
  466. //-----------------------------------------------------------------------------
  467. WCHAR *
  468. TError::ErrorCodeToText(
  469. DWORD code ,// in -message code
  470. DWORD lenMsg ,// in -length of message text area
  471. WCHAR * msg // out-returned message text
  472. )
  473. {
  474. static HMODULE hNetMsg = NULL;
  475. DWORD rc;
  476. WCHAR * pMsg;
  477. msg[0] = '\0'; // force to null
  478. if ( code >= NERR_BASE && code < MAX_NERR )
  479. {
  480. if ( !hNetMsg )
  481. hNetMsg = LoadLibrary(L"netmsg.dll");
  482. rc = 1;
  483. }
  484. else
  485. {
  486. rc = DceErrorInqText( code, msg );
  487. // Change any imbedded CR or LF to blank.
  488. for ( pMsg = msg;
  489. *pMsg;
  490. pMsg++ )
  491. {
  492. if ( (*pMsg == L'\x0D') || (*pMsg == L'\x0A') )
  493. *pMsg = L' ';
  494. }
  495. // Remove trailing blanks
  496. for ( pMsg--;
  497. pMsg >= msg;
  498. pMsg-- )
  499. {
  500. if ( *pMsg == L' ' )
  501. *pMsg = L'\0';
  502. else
  503. break;
  504. }
  505. }
  506. if ( rc )
  507. {
  508. if ( code >= NERR_BASE && code < MAX_NERR && hNetMsg )
  509. {
  510. FormatMessage(FORMAT_MESSAGE_FROM_HMODULE
  511. | FORMAT_MESSAGE_MAX_WIDTH_MASK
  512. | FORMAT_MESSAGE_IGNORE_INSERTS
  513. | 80,
  514. hNetMsg,
  515. code,
  516. 0,
  517. msg,
  518. lenMsg,
  519. NULL );
  520. }
  521. else
  522. {
  523. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
  524. | FORMAT_MESSAGE_MAX_WIDTH_MASK
  525. | FORMAT_MESSAGE_IGNORE_INSERTS
  526. | 80,
  527. NULL,
  528. code,
  529. 0,
  530. msg,
  531. lenMsg,
  532. NULL );
  533. }
  534. }
  535. return msg;
  536. }
  537. // Err.cpp - end of file