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.

489 lines
15 KiB

  1. /****************************************************************************\
  2. LOG.C / OPK Library (OPKLIB.LIB)
  3. Microsoft Confidential
  4. Copyright (c) Microsoft Corporation 1999
  5. All rights reserved
  6. Logging API source file for use in the OPK tools.
  7. 08/00 - Jason Cohen (JCOHEN)
  8. Added this new source file to Whistler for common logging
  9. functionality across all the OPK tools.
  10. \****************************************************************************/
  11. //
  12. // Include File(s):
  13. //
  14. #include <pch.h>
  15. #include <stdio.h>
  16. //
  17. // Defines
  18. //
  19. #ifdef CHR_NEWLINE
  20. #undef CHR_NEWLINE
  21. #endif // CHR_NEWLINE
  22. #define CHR_NEWLINE _T('\n')
  23. #ifdef CHR_CR
  24. #undef CHR_CR
  25. #endif // CHR_CR
  26. #define CHR_CR _T('\r')
  27. // Global logging info handle.
  28. //
  29. PLOG_INFO g_pLogInfo = NULL;
  30. //
  31. // Exported Function(s):
  32. //
  33. INT LogFileLst(LPCTSTR lpFileName, LPTSTR lpFormat, va_list lpArgs)
  34. {
  35. INT iChars = 0;
  36. HANDLE hFile;
  37. // Make sure we have the required params and can create the file.
  38. //
  39. if ( ( lpFileName && lpFileName[0] && lpFormat ) &&
  40. ( hFile = _tfopen(lpFileName, _T("a")) ) )
  41. {
  42. // Print the debug message to the end of the file.
  43. //
  44. iChars = _vftprintf(hFile, lpFormat, lpArgs);
  45. // Close the handle to the file.
  46. //
  47. fclose(hFile);
  48. }
  49. // Return the number of chars written from the printf call.
  50. //
  51. return iChars;
  52. }
  53. INT LogFileStr(LPCTSTR lpFileName, LPTSTR lpFormat, ...)
  54. {
  55. va_list lpArgs;
  56. // Initialize the lpArgs parameter with va_start().
  57. //
  58. va_start(lpArgs, lpFormat);
  59. // Return the return value of the MessageBox() call. If there was a memory
  60. // error, 0 will be returned. This is all
  61. //
  62. return LogFileLst(lpFileName, lpFormat, lpArgs);
  63. }
  64. INT LogFile(LPCTSTR lpFileName, UINT uFormat, ...)
  65. {
  66. va_list lpArgs;
  67. INT nReturn;
  68. LPTSTR lpFormat = NULL;
  69. // Initialize the lpArgs parameter with va_start().
  70. //
  71. va_start(lpArgs, uFormat);
  72. // Get the format and caption strings from the resource.
  73. //
  74. if ( uFormat )
  75. lpFormat = AllocateString(NULL, uFormat);
  76. // Return the return value of the MessageBox() call. If there was a memory
  77. // error, 0 will be returned.
  78. //
  79. nReturn = LogFileLst(lpFileName, lpFormat, lpArgs);
  80. // Free the format and caption strings.
  81. //
  82. FREE(lpFormat);
  83. // Return the value saved from the previous function call.
  84. //
  85. return nReturn;
  86. }
  87. //
  88. // Function Implementations
  89. //
  90. /*++
  91. Routine Description:
  92. This routine ckecks the specified ini file for settings for logging. Logging
  93. is enabled by default if nothing is specified in the ini file.
  94. Disables logging by setting pLogInfo->szLogFile = NULL.
  95. Arguments:
  96. None.
  97. Return Value:
  98. None.
  99. --*/
  100. BOOL OpkInitLogging(LPTSTR lpszIniPath, LPTSTR lpAppName)
  101. {
  102. TCHAR szScratch[MAX_PATH] = NULLSTR;
  103. LPTSTR lpszScratch;
  104. BOOL bWinbom = ( lpszIniPath && *lpszIniPath );
  105. PLOG_INFO pLogInfo = NULL;
  106. pLogInfo = MALLOC(sizeof(LOG_INFO));
  107. if ( NULL == pLogInfo )
  108. {
  109. return FALSE;
  110. }
  111. if ( lpAppName )
  112. {
  113. pLogInfo->lpAppName = MALLOC((lstrlen(lpAppName) + 1) * sizeof(TCHAR));
  114. lstrcpy(pLogInfo->lpAppName, lpAppName);
  115. }
  116. // First check if logging is disabled in the WinBOM.
  117. //
  118. if ( ( bWinbom ) &&
  119. ( GetPrivateProfileString(INI_SEC_LOGGING, INI_KEY_LOGGING, _T("YES"), szScratch, AS(szScratch), lpszIniPath) ) &&
  120. ( LSTRCMPI(szScratch, INI_VAL_NO) == 0 ) )
  121. {
  122. // FREE macro sets pLogInfo to NULL.
  123. FREE(pLogInfo->lpAppName);
  124. FREE(pLogInfo);
  125. }
  126. else
  127. {
  128. // All these checks can only be done if we have a winbom.
  129. //
  130. if ( bWinbom )
  131. {
  132. // Check for quiet mode. If we are in quiet mode don't display any MessageBoxes.
  133. //
  134. if ( ( GetPrivateProfileString(INI_SEC_LOGGING, INI_KEY_QUIET, NULLSTR, szScratch, AS(szScratch), lpszIniPath) ) &&
  135. ( 0 == LSTRCMPI(szScratch, INI_VAL_YES) )
  136. )
  137. {
  138. SET_FLAG(pLogInfo->dwLogFlags, LOG_FLAG_QUIET_MODE);
  139. }
  140. /* // See if they want to turn on perf logging.
  141. //
  142. szScratch[0] = NULLCHR;
  143. if ( ( GetPrivateProfileString(WBOM_FACTORY_SECTION, INI_KEY_WBOM_LOGPERF, NULLSTR, szScratch, AS(szScratch), lpszIniPath) ) &&
  144. ( 0 == lstrcmpi(szScratch, WBOM_YES) ) )
  145. {
  146. SET_FLAG(pLogInfo->dwLogFlags, FLAG_LOG_PERF);
  147. }
  148. */
  149. // Set the logging level.
  150. //
  151. pLogInfo->dwLogLevel = (DWORD) GetPrivateProfileInt(INI_SEC_LOGGING, INI_KEY_LOGLEVEL, (DWORD) pLogInfo->dwLogLevel, lpszIniPath);
  152. }
  153. #ifndef DBG
  154. if ( pLogInfo->dwLogLevel >= LOG_DEBUG )
  155. pLogInfo->dwLogLevel = LOG_DEBUG - 1;
  156. #endif
  157. // Check to see if they have a custom log file they want to use.
  158. //
  159. if ( ( bWinbom ) &&
  160. ( lpszScratch = IniGetExpand(lpszIniPath, INI_SEC_LOGGING, INI_KEY_LOGFILE, NULL) ) )
  161. {
  162. TCHAR szFullPath[MAX_PATH] = NULLSTR;
  163. LPTSTR lpFind = NULL;
  164. // Turn the ini key into a full path.
  165. //
  166. // NTRAID#NTBUG9-551266-2002/03/27-acosma,robertko - Buffer overrun possibility.
  167. //
  168. lstrcpy(pLogInfo->szLogFile, lpszScratch);
  169. if (GetFullPathName(pLogInfo->szLogFile, AS(szFullPath), szFullPath, &lpFind) && szFullPath[0] && lpFind)
  170. {
  171. // Copy the full path into the global.
  172. //
  173. lstrcpyn(pLogInfo->szLogFile, szFullPath, AS(pLogInfo->szLogFile));
  174. // Chop off the file part so we can create the
  175. // path if it doesn't exist.
  176. //
  177. *lpFind = NULLCHR;
  178. // If the directory cannot be created or doesn't exist turn off logging.
  179. //
  180. if (!CreatePath(szFullPath))
  181. pLogInfo->szLogFile[0] = NULLCHR;
  182. }
  183. // Free the original path buffer from the ini file.
  184. //
  185. FREE(lpszScratch);
  186. }
  187. else // default case
  188. {
  189. // Create it in the current directory.
  190. //
  191. GetCurrentDirectory(AS(pLogInfo->szLogFile), pLogInfo->szLogFile);
  192. // NTRAID#NTBUG9-551266-2002/03/27-acosma - Buffer overrun possibility.
  193. //
  194. AddPath(pLogInfo->szLogFile, _T("logfile.txt"));
  195. }
  196. // Check to see if we have write access to the logfile. If we don't turn off logging.
  197. // If we're running in WinPE we'll call this function again once the drive becomes
  198. // writable.
  199. //
  200. // Write an FFFE header to the file to identify this as a Unicode text file.
  201. //
  202. if ( pLogInfo->szLogFile[0] )
  203. {
  204. HANDLE hFile;
  205. DWORD dwWritten = 0;
  206. WCHAR cHeader = 0xFEFF;
  207. SetLastError(ERROR_SUCCESS);
  208. if ( INVALID_HANDLE_VALUE != (hFile = CreateFile(pLogInfo->szLogFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)))
  209. {
  210. if ( ERROR_ALREADY_EXISTS != GetLastError() )
  211. WriteFile(hFile, &cHeader, sizeof(cHeader), &dwWritten, NULL);
  212. CloseHandle(hFile);
  213. }
  214. else
  215. { // There was a problem opening the file. Most of the time this means that the media is not writable.
  216. // Disable logging in that case. Macro sets variable to NULL.
  217. //
  218. FREE(pLogInfo->lpAppName);
  219. FREE(pLogInfo);
  220. }
  221. }
  222. }
  223. g_pLogInfo = pLogInfo;
  224. return TRUE;
  225. }
  226. // NTRAID#NTBUG9-551266-2002/03/27-acosma,robertko - Buffer overrun possibilities in this function. Use strsafe functions.
  227. //
  228. DWORD OpkLogFileLst(PLOG_INFO pLogInfo, DWORD dwLogOpt, LPTSTR lpFormat, va_list lpArgs)
  229. {
  230. LPTSTR lpPreOut = NULL;
  231. LPTSTR lpOut = NULL;
  232. DWORD dwSize = 1024;
  233. TCHAR szPreLog[MAX_PATH] = NULLSTR;
  234. HANDLE hFile;
  235. DWORD dwWritten = 0;
  236. DWORD cbAppName = 0;
  237. DWORD dwLogLevel = (DWORD) (dwLogOpt & LOG_LEVEL_MASK);
  238. if ( ( dwLogLevel <= pLogInfo->dwLogLevel) && lpFormat )
  239. {
  240. // Build the output string.
  241. //
  242. if ( pLogInfo->lpAppName )
  243. {
  244. // Create the prefix string
  245. //
  246. lstrcpy(szPreLog, pLogInfo->lpAppName);
  247. lstrcat(szPreLog, _T("::"));
  248. }
  249. // This is for skipping the App Name prefix when printing to the log file
  250. //
  251. cbAppName = lstrlen(szPreLog);
  252. if ( GET_FLAG(dwLogOpt, LOG_ERR) )
  253. {
  254. if ( 0 == dwLogLevel )
  255. lstrcat(szPreLog, _T("ERROR: "));
  256. else
  257. swprintf(szPreLog + cbAppName, _T("WARN%d: "), dwLogLevel);
  258. }
  259. if ( GET_FLAG(dwLogOpt, LOG_TIME) )
  260. {
  261. TCHAR szTime[100] = NULLSTR;
  262. GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, NULL, _T("'['HH':'mm':'ss'] '"), szTime, AS(szTime));
  263. lstrcat(szPreLog, szTime);
  264. }
  265. // Replace all the parameters in the Error string. Allocate more memory if necessary.
  266. // In case something goes seriously wrong here, cap memory allocation at 1 megabyte.
  267. //
  268. for ( lpPreOut = (LPTSTR) MALLOC((dwSize) * sizeof(TCHAR));
  269. lpPreOut && ( -1 == _vsnwprintf(lpPreOut, dwSize, lpFormat, lpArgs)) && dwSize < (1024 * 1024);
  270. FREE(lpPreOut), lpPreOut = (LPTSTR) MALLOC((dwSize *= 2) * sizeof(TCHAR))
  271. );
  272. //
  273. // We now have the Error string and the prefix string. Copy this to the final
  274. // string that we need to output.
  275. //
  276. if ( lpPreOut )
  277. {
  278. // Allocate another string that will be the final output string.
  279. // We need 1 extra TCHAR for NULL terminator and 2 extra for
  280. // an optional NewLine + Linefeed TCHAR pair that may be added.
  281. //
  282. dwSize = lstrlen(szPreLog) + lstrlen(lpPreOut) + 3;
  283. lpOut = (LPTSTR) MALLOC( (dwSize) * sizeof(TCHAR) );
  284. if ( lpOut )
  285. {
  286. lstrcpy(lpOut, szPreLog);
  287. lstrcat(lpOut, lpPreOut);
  288. // Make sure that string is terminated by NewLine unless the caller doesn't want to.
  289. //
  290. if ( !GET_FLAG(dwLogOpt, LOG_NO_NL) )
  291. {
  292. LPTSTR lpNL = lpOut;
  293. TCHAR szCRLF[] = _T("\r\n");
  294. BOOL bStringOk = FALSE;
  295. // Find the end of the string.
  296. //
  297. lpNL = lpNL + lstrlen(lpNL);
  298. // Make sure the string is terminated by "\r\n".
  299. //
  300. // There are three cases here:
  301. // 1. The string is already terminated by \r\n. Leave it alone.
  302. // 2. String is terminated by \n. Replace \n with \r\n.
  303. // 3. String is not terminated by anything. Append string with \r\n.
  304. //
  305. if ( CHR_NEWLINE == *(lpNL = (CharPrev(lpOut, lpNL))) )
  306. {
  307. if ( CHR_CR != *(CharPrev(lpOut, lpNL)) )
  308. {
  309. *(lpNL) = NULLCHR;
  310. }
  311. else
  312. {
  313. bStringOk = TRUE;
  314. }
  315. }
  316. // If there is a need to, fix up the string
  317. //
  318. if ( !bStringOk )
  319. {
  320. lstrcat( lpOut, szCRLF );
  321. }
  322. }
  323. // Write the error to the file and close the file.
  324. // Skip the "AppName::" at the beginning of the string when printing to the file.
  325. //
  326. if ( pLogInfo->szLogFile[0] &&
  327. ( INVALID_HANDLE_VALUE != (hFile = CreateFile(pLogInfo->szLogFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)))
  328. )
  329. {
  330. if ( INVALID_SET_FILE_POINTER != SetFilePointer(hFile, 0, 0, FILE_END) )
  331. {
  332. WriteFile(hFile, (lpOut + cbAppName), lstrlen(lpOut + cbAppName) * sizeof(TCHAR), &dwWritten, NULL);
  333. }
  334. CloseHandle(hFile);
  335. }
  336. // Output the string to the debugger and free it.
  337. //
  338. OutputDebugString(lpOut);
  339. FREE(lpOut);
  340. }
  341. // Put up the MessageBox if specified. This only allows message boxes
  342. // to be log level 0.
  343. //
  344. if ( !GET_FLAG(pLogInfo->dwLogFlags, LOG_FLAG_QUIET_MODE) &&
  345. GET_FLAG(dwLogOpt, LOG_MSG_BOX) &&
  346. (0 == dwLogLevel)
  347. )
  348. MessageBox(NULL, lpPreOut, pLogInfo->lpAppName, MB_OK | MB_SYSTEMMODAL |
  349. (GET_FLAG(dwLogOpt, LOG_ERR) ? MB_ICONERROR : MB_ICONWARNING) );
  350. // Free the error string
  351. //
  352. FREE(lpPreOut);
  353. }
  354. }
  355. // Return the number of bytes written to the file.
  356. //
  357. return dwWritten;
  358. }
  359. DWORD OpkLogFile(DWORD dwLogOpt, UINT uFormat, ...)
  360. {
  361. va_list lpArgs;
  362. DWORD dwWritten = 0;
  363. LPTSTR lpFormat = NULL;
  364. if ( g_pLogInfo )
  365. {
  366. // Initialize the lpArgs parameter with va_start().
  367. //
  368. va_start(lpArgs, uFormat);
  369. if ( lpFormat = AllocateString(NULL, uFormat) )
  370. {
  371. dwWritten = OpkLogFileLst(g_pLogInfo, dwLogOpt, lpFormat, lpArgs);
  372. }
  373. // Free the format string.
  374. //
  375. FREE(lpFormat);
  376. }
  377. // Return the value saved from the previous function call.
  378. //
  379. return dwWritten;
  380. }
  381. DWORD OpkLogFileStr(DWORD dwLogOpt, LPTSTR lpFormat, ...)
  382. {
  383. va_list lpArgs;
  384. DWORD dwWritten = 0;
  385. if ( g_pLogInfo )
  386. {
  387. // Initialize the lpArgs parameter with va_start().
  388. //
  389. va_start(lpArgs, lpFormat);
  390. dwWritten = OpkLogFileLst(g_pLogInfo, dwLogOpt, lpFormat, lpArgs);
  391. }
  392. // Return the value saved from the previous function call.
  393. //
  394. return dwWritten;
  395. }