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.

534 lines
18 KiB

  1. // ===========================================================================
  2. // File: DBGLOG.CXX
  3. // CDLDebugLog: Class for Debug Logs
  4. // DebugLogElement: Node class for debug log messages
  5. //
  6. #include <cdlpch.h>
  7. #include <stdio.h>
  8. extern HINSTANCE g_hInst;
  9. // Initialize static variables
  10. CList<CDLDebugLog *, CDLDebugLog *> CDLDebugLog::s_dlogList;
  11. TCHAR CDLDebugLog::s_szMessage[MAX_DEBUG_STRING_LENGTH];
  12. CMutexSem CDLDebugLog::s_mxsDLogList;
  13. BOOL CDLDebugLog::s_bMessage(FALSE);
  14. CMutexSem CDLDebugLog::s_mxsMessage;
  15. CDLDebugLog::CDLDebugLog()
  16. :m_DebugLogList(),
  17. m_fAddedDebugLogHead(FALSE),
  18. m_iRefCount(0)
  19. {
  20. m_szFileName[0] = '\0';
  21. m_szUrlName[0] = '\0';
  22. m_szMainClsid[0] = '\0';
  23. m_szMainType[0] = '\0';
  24. m_szMainExt[0] = '\0';
  25. m_szMainUrl[0] = '\0';
  26. }
  27. CDLDebugLog::~CDLDebugLog()
  28. {
  29. Clear();
  30. }
  31. int CDLDebugLog::AddRef()
  32. {
  33. return ++m_iRefCount;
  34. }
  35. int CDLDebugLog::Release()
  36. {
  37. ASSERT(m_iRefCount > 0);
  38. m_iRefCount--;
  39. if(m_iRefCount <= 0)
  40. {
  41. delete this;
  42. return 0;
  43. }
  44. else
  45. return m_iRefCount;
  46. }
  47. CDLDebugLog * CDLDebugLog::MakeDebugLog()
  48. {
  49. return new CDLDebugLog();
  50. }
  51. // Delete all new'd data and start a new log
  52. void CDLDebugLog::Clear()
  53. {
  54. DebugLogElement *pMsg = NULL;
  55. LISTPOSITION pos;
  56. int iNumMessages;
  57. int i;
  58. // delete new'd messages
  59. iNumMessages = m_DebugLogList.GetCount();
  60. pos = m_DebugLogList.GetHeadPosition();
  61. for (i = 0; i < iNumMessages; i++) {
  62. pMsg = m_DebugLogList.GetNext(pos);
  63. delete pMsg;
  64. }
  65. m_DebugLogList.RemoveAll();
  66. m_fAddedDebugLogHead = FALSE;
  67. // commit any lingering CacheEntry
  68. if(m_szUrlName[0])
  69. {
  70. FILETIME ftExpireTime;
  71. FILETIME ftTime;
  72. GetSystemTimeAsFileTime(&ftTime);
  73. ftExpireTime.dwLowDateTime = (DWORD)0;
  74. ftExpireTime.dwHighDateTime = (DWORD)0;
  75. CommitUrlCacheEntry(m_szUrlName, m_szFileName, ftExpireTime,
  76. ftTime, NORMAL_CACHE_ENTRY,
  77. NULL, 0, NULL, 0);
  78. }
  79. m_szUrlName[0] = 0;
  80. m_szFileName[0] = 0;
  81. }
  82. // Initializes the main clsid, type, ext, and codebase of the debuglog from the
  83. // corresponding values in the CCodeDownload
  84. // If any of the CCodeDownload's parameters are NULL, they are ignored
  85. // (An assertion is thrown if they are all NULL)
  86. // for Retail: returns false if they are all NULL
  87. BOOL CDLDebugLog::Init(CCodeDownload * pcdl)
  88. {
  89. int iRes = 0;
  90. if(pcdl->GetMainDistUnit())
  91. {
  92. iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainDistUnit(), -1, m_szMainClsid,
  93. MAX_DEBUG_STRING_LENGTH, NULL, NULL);
  94. ASSERT(iRes != 0);
  95. }
  96. if(pcdl->GetMainType())
  97. {
  98. iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainType(), -1, m_szMainType,
  99. MAX_DEBUG_STRING_LENGTH, NULL, NULL);
  100. ASSERT(iRes != 0);
  101. }
  102. if(pcdl->GetMainExt())
  103. {
  104. iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainExt(), -1, m_szMainExt,
  105. MAX_DEBUG_STRING_LENGTH, NULL, NULL);
  106. ASSERT(iRes != 0);
  107. }
  108. if(pcdl->GetMainURL())
  109. {
  110. iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainURL(), -1, m_szMainUrl,
  111. INTERNET_MAX_URL_LENGTH, NULL, NULL);
  112. ASSERT(iRes != 0);
  113. }
  114. ASSERT(iRes != 0);
  115. return(iRes != 0);
  116. }
  117. // Initializes the main clsid, type, ext, and codebase of the debuglog
  118. // If any of the parameters are NULL, they are ignored
  119. // (An assertion is thrown if they are all NULL)
  120. // for Retail: returns false if they are all NULL
  121. BOOL CDLDebugLog::Init(LPCWSTR wszMainClsid, LPCWSTR wszMainType, LPCWSTR wszMainExt, LPCWSTR wszMainUrl)
  122. {
  123. int iRes = 0;
  124. if(wszMainClsid)
  125. {
  126. iRes = WideCharToMultiByte(CP_ACP, 0, wszMainClsid, -1, m_szMainClsid,
  127. MAX_DEBUG_STRING_LENGTH, NULL, NULL);
  128. ASSERT(iRes != 0);
  129. }
  130. if(wszMainType)
  131. {
  132. iRes = WideCharToMultiByte(CP_ACP, 0, wszMainType, -1, m_szMainType,
  133. MAX_DEBUG_STRING_LENGTH, NULL, NULL);
  134. ASSERT(iRes != 0);
  135. }
  136. if(wszMainExt)
  137. {
  138. WideCharToMultiByte(CP_ACP, 0, wszMainExt, -1, m_szMainExt,
  139. MAX_DEBUG_STRING_LENGTH, NULL, NULL);
  140. ASSERT(iRes != 0);
  141. }
  142. if(wszMainUrl)
  143. {
  144. WideCharToMultiByte(CP_ACP, 0, wszMainUrl, -1, m_szMainUrl,
  145. INTERNET_MAX_URL_LENGTH, NULL, NULL);
  146. ASSERT(iRes != 0);
  147. }
  148. ASSERT(iRes != 0);
  149. return(iRes != 0);
  150. }
  151. // Make the cache file for the debug log using the current main clsid, type, ext,
  152. // and url data. The previous values in m_szUrlName and m_szFileName are cleaned
  153. // up and written over.
  154. void CDLDebugLog::MakeFile()
  155. {
  156. TCHAR szExtension[] = TEXT("HTM");
  157. if(m_szFileName[0])
  158. {
  159. return; // Only make the file if we don't already have one
  160. }
  161. m_szUrlName[0] = 0;
  162. m_szFileName[0] = 0;
  163. if(m_szMainClsid[0])
  164. {
  165. wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!name=%s", m_szMainClsid);
  166. }
  167. else if(m_szMainType[0])
  168. {
  169. wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!type=%s", m_szMainType);
  170. }
  171. else if(m_szMainExt[0])
  172. {
  173. wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!ext=%s", m_szMainExt);
  174. }
  175. else
  176. {
  177. wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!");
  178. }
  179. CreateUrlCacheEntry(m_szUrlName, 0, szExtension, m_szFileName, 0);
  180. }
  181. // ---------------------------------------------------------------------------
  182. // %%Function: CDLDebugLog::DebugOut(int iOption, const char *pscFormat, ...)
  183. // Replacement for UrlMkDebugOut() and CCodeDownload::CodeDownloadDebugOut
  184. // calls to log code download debug/error messages
  185. // ---------------------------------------------------------------------------
  186. void CDLDebugLog::DebugOut(int iOption, BOOL fOperationFailed,
  187. UINT iResId, ...)
  188. {
  189. // Temp solution to prevent buffer overruns in debug logging code.
  190. // Long term, the printfs should be constrained. It will be a must
  191. // if URLs become fully dynamic.
  192. static char szDebugString[MAX_DEBUG_STRING_LENGTH*5];
  193. static char szFormatString[MAX_DEBUG_FORMAT_STRING_LENGTH];
  194. va_list args;
  195. LoadString(g_hInst, iResId, szFormatString, MAX_DEBUG_FORMAT_STRING_LENGTH);
  196. va_start(args, iResId);
  197. vsprintf(szDebugString, szFormatString, args);
  198. va_end(args);
  199. DebugOutPreFormatted(iOption, fOperationFailed, szDebugString);
  200. }
  201. // Debug out taken a preformatted string instead of a resid format address and
  202. // an arbitrary list of arguments. szDebugString is the string which will be outputted
  203. // as the debug statement
  204. void CDLDebugLog::DebugOutPreFormatted(int iOption, BOOL fOperationFailed,
  205. LPTSTR szDebugString)
  206. {
  207. static TCHAR szUrlMkDebugOutString[MAX_DEBUG_STRING_LENGTH];
  208. DebugLogElement *pdbglog = NULL;
  209. DebugLogElement *pdbglogHead = NULL;
  210. pdbglog = new DebugLogElement(szDebugString);
  211. if(! pdbglog)
  212. return;
  213. wnsprintf(szUrlMkDebugOutString, MAX_DEBUG_STRING_LENGTH-1, "CODE DL:%s", szDebugString);
  214. UrlMkDebugOut((iOption,szUrlMkDebugOutString));
  215. if (pdbglog != NULL) {
  216. m_DebugLogList.AddTail(pdbglog);
  217. if (fOperationFailed && !m_fAddedDebugLogHead) {
  218. m_fAddedDebugLogHead = TRUE;
  219. pdbglogHead = new DebugLogElement("--- Detailed Error Log Follows ---\n");
  220. if(! pdbglogHead)
  221. return;
  222. m_DebugLogList.AddHead(pdbglogHead);
  223. pdbglogHead = new DebugLogElement(*pdbglog);
  224. if(! pdbglogHead)
  225. return;
  226. m_DebugLogList.AddHead(pdbglogHead);
  227. }
  228. }
  229. }
  230. #define DEBUG_LOG_HTML_START TEXT("<html><pre>\n")
  231. #define DEBUG_LOG_HTML_END TEXT("\n</pre></html>")
  232. #define PAD_DIGITS_FOR_STRING(x) (((x) > 9) ? TEXT("") : TEXT("0"))
  233. // ---------------------------------------------------------------------------
  234. // %%Function: CDLDebugLog::DumpDebugLog()
  235. // Output the debug error log. This log is written as a cache entry.
  236. // pszCacheFileName is an outparam for the name of the file in the cache,
  237. // cbBufLen is the length of the buffer
  238. // szErrorMsg is the error message causing the dump, and hrError the hr causing
  239. // the dump
  240. // ---------------------------------------------------------------------------
  241. void CDLDebugLog::DumpDebugLog(LPTSTR pszCacheFileName, int cbBufLen, LPTSTR szErrorMsg,
  242. HRESULT hrError)
  243. {
  244. DebugLogElement *pMsg = NULL;
  245. LPCSTR pszStr = NULL;
  246. LISTPOSITION pos = NULL;
  247. int iNumMessages = 0;
  248. int i = 0;
  249. HANDLE hFile = INVALID_HANDLE_VALUE;
  250. FILETIME ftTime;
  251. FILETIME ftExpireTime;
  252. DWORD dwBytes = 0;
  253. SYSTEMTIME systime;
  254. static TCHAR pszHeader[MAX_DEBUG_STRING_LENGTH];
  255. static const TCHAR *ppszMonths[] = {TEXT("Jan"), TEXT("Feb"), TEXT("Mar"),
  256. TEXT("Apr"), TEXT("May"), TEXT("Jun"),
  257. TEXT("Jul"), TEXT("Aug"), TEXT("Sep"),
  258. TEXT("Oct"), TEXT("Nov"), TEXT("Dec")};
  259. // Get the filename and put it in the out LPTSTR
  260. if(!m_szFileName[0])
  261. MakeFile();
  262. if(pszCacheFileName)
  263. lstrcpyn(pszCacheFileName,m_szFileName, cbBufLen);
  264. iNumMessages = m_DebugLogList.GetCount();
  265. pos = m_DebugLogList.GetHeadPosition();
  266. if (pos) {
  267. pMsg = m_DebugLogList.GetAt(pos);
  268. if (pMsg != NULL) {
  269. hFile = CreateFile(m_szFileName, GENERIC_READ | GENERIC_WRITE,
  270. 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
  271. NULL);
  272. if (hFile != INVALID_HANDLE_VALUE) {
  273. // Write the header
  274. WriteFile(hFile, DEBUG_LOG_HTML_START, strlen(DEBUG_LOG_HTML_START),
  275. &dwBytes, NULL);
  276. GetLocalTime(&systime);
  277. wnsprintf(pszHeader, ARRAY_ELEMENTS(pszHeader)-1, "*** Code Download Log entry (%s%d %s %d @ %s%d:%s%d:%s%d) ***\n",
  278. PAD_DIGITS_FOR_STRING(systime.wDay), systime.wDay,
  279. ppszMonths[systime.wMonth - 1],
  280. systime.wYear,
  281. PAD_DIGITS_FOR_STRING(systime.wHour), systime.wHour,
  282. PAD_DIGITS_FOR_STRING(systime.wMinute), systime.wMinute,
  283. PAD_DIGITS_FOR_STRING(systime.wSecond), systime.wSecond);
  284. WriteFile(hFile, pszHeader, strlen(pszHeader), &dwBytes, NULL);
  285. // Write what error caused this dump
  286. wnsprintf(pszHeader, ARRAY_ELEMENTS(pszHeader)-1, "Code Download Error: (hr = %lx) %s\n",
  287. hrError,
  288. (szErrorMsg == NULL) ? ("(null)") : (szErrorMsg));
  289. WriteFile(hFile, pszHeader, strlen(pszHeader), &dwBytes, NULL);
  290. wnsprintf(pszHeader, ARRAY_ELEMENTS(pszHeader)-1, "Operation failed. Detailed Information:\n"
  291. " CodeBase: %s\n"
  292. " CLSID: %s\n"
  293. " Extension: %s\n"
  294. " Type: %s\n\n",
  295. m_szMainUrl,
  296. m_szMainClsid,
  297. m_szMainExt,
  298. m_szMainType);
  299. WriteFile(hFile, pszHeader, strlen(pszHeader), &dwBytes, NULL);
  300. // Write the Debug Log
  301. pos = m_DebugLogList.GetHeadPosition();
  302. iNumMessages = m_DebugLogList.GetCount();
  303. for (i = 0; i < iNumMessages; i++) {
  304. pMsg = m_DebugLogList.GetNext(pos);
  305. pszStr = pMsg->GetLogMessage();
  306. WriteFile(hFile, pszStr, strlen(pszStr), &dwBytes, NULL);
  307. }
  308. // Close and clean
  309. WriteFile(hFile, DEBUG_LOG_HTML_END, strlen(DEBUG_LOG_HTML_END),
  310. &dwBytes, NULL);
  311. CloseHandle(hFile);
  312. GetSystemTimeAsFileTime(&ftTime);
  313. ftExpireTime.dwLowDateTime = (DWORD)0;
  314. ftExpireTime.dwHighDateTime = (DWORD)0;
  315. CommitUrlCacheEntry(m_szUrlName, m_szFileName, ftExpireTime,
  316. ftTime, NORMAL_CACHE_ENTRY,
  317. NULL, 0, NULL, 0);
  318. m_fAddedDebugLogHead = FALSE;
  319. m_szUrlName[0] = NULL;
  320. m_szFileName[0] = NULL;
  321. }
  322. }
  323. }
  324. }
  325. // returns TRUE if there is already a message saved
  326. // (If there is a message saved already, it will not be written over
  327. // unless bOverwrite is true)
  328. BOOL CDLDebugLog::SetSavedMessage(LPCTSTR szMessage, BOOL bOverwrite)
  329. {
  330. CLock lck(s_mxsMessage);
  331. BOOL bRet = FALSE;
  332. if(s_bMessage)
  333. bRet = TRUE;
  334. if((!s_bMessage) || bOverwrite)
  335. {
  336. lstrcpyn(s_szMessage, szMessage, MAX_DEBUG_STRING_LENGTH);
  337. s_bMessage=TRUE;
  338. }
  339. return bRet;
  340. }
  341. LPCTSTR CDLDebugLog::GetSavedMessage()
  342. {
  343. CLock lck(s_mxsMessage);
  344. if(!s_bMessage) // Make sure to return an empty string if one has not been set
  345. {
  346. s_szMessage[0] = '\0';
  347. }
  348. return s_szMessage;
  349. }
  350. // add a debug log to the global list
  351. void CDLDebugLog::AddDebugLog(CDLDebugLog * dlog)
  352. {
  353. CLock lck(s_mxsDLogList);
  354. if(dlog)
  355. s_dlogList.AddTail(dlog);
  356. }
  357. // Remove a given debug log from the global list
  358. void CDLDebugLog::RemoveDebugLog(CDLDebugLog * dlog)
  359. {
  360. CLock lck(s_mxsDLogList);
  361. if(dlog)
  362. {
  363. POSITION pos = s_dlogList.Find(dlog);
  364. if(pos != NULL)
  365. s_dlogList.RemoveAt(pos);
  366. }
  367. }
  368. // Gets the debug log with the given clsid, mime type, file extension or url code base.
  369. // Naming priority is the same as at debug file urlname creation: clsid, then type, then ext, then url
  370. // The names are checked in this order (so two logs with the same extension but different classids will never
  371. // be confused, since clsids are checked before extensions)
  372. // If an earlier name is NULL or doesn't produce a match, the next name is checked.
  373. // If all names fails to produce a match, the return value is NULL
  374. CDLDebugLog * CDLDebugLog::GetDebugLog(LPCWSTR wszMainClsid, LPCWSTR wszMainType, LPCWSTR wszMainExt, LPCWSTR wszMainUrl)
  375. {
  376. TCHAR szComparer[MAX_DEBUG_STRING_LENGTH];
  377. POSITION pos = NULL;
  378. szComparer[0] = NULL;
  379. CDLDebugLog * dlogCur = NULL;
  380. const int iSwitchMax = 4;
  381. LPCWSTR ppwszMains[] = {wszMainClsid, wszMainType, wszMainExt, wszMainUrl};
  382. int iSwitch = iSwitchMax;
  383. int iRes = 0;
  384. // Find the first non-empty name
  385. for(iSwitch = 0; iSwitch < iSwitchMax; iSwitch++)
  386. {
  387. if((ppwszMains[iSwitch]) && (ppwszMains[iSwitch])[0])
  388. break;
  389. }
  390. if(iSwitch >= iSwitchMax)
  391. return NULL;
  392. // Grab mutex for accesing the list
  393. CLock lck(s_mxsDLogList);
  394. // Loop over the names; a higher array value means a lower priority name;
  395. // don't check a lower priority name unless all higher priority names have
  396. // failed
  397. while(iSwitch < iSwitchMax)
  398. {
  399. iRes = WideCharToMultiByte(CP_ACP, 0, ppwszMains[iSwitch], -1, szComparer,
  400. MAX_DEBUG_STRING_LENGTH, NULL, NULL);
  401. if(iRes == 0)
  402. return NULL;
  403. // Look through the entire list for a debuglog whose name (clsid, type, ext, or url)
  404. // matches the given one
  405. for(pos = s_dlogList.GetHeadPosition(); pos != NULL; )
  406. {
  407. dlogCur = s_dlogList.GetNext(pos);
  408. switch(iSwitch)
  409. {
  410. case 0:
  411. if(!lstrcmp(szComparer, dlogCur->GetMainClsid()))
  412. return dlogCur;
  413. break;
  414. case 1:
  415. if(!lstrcmp(szComparer, dlogCur->GetMainType()))
  416. return dlogCur;
  417. break;
  418. case 2:
  419. if(!lstrcmp(szComparer, dlogCur->GetMainExt()))
  420. return dlogCur;
  421. break;
  422. case 3:
  423. if(!lstrcmp(szComparer, dlogCur->GetMainUrl()))
  424. return dlogCur;
  425. break;
  426. default:
  427. break;
  428. }
  429. }
  430. iSwitch++;
  431. }
  432. // No match of any of the non-NULL passed in names was found
  433. return NULL;
  434. }
  435. DebugLogElement::DebugLogElement(const DebugLogElement &ref)
  436. {
  437. SetLogMessage(ref.m_szMessage);
  438. }
  439. DebugLogElement::DebugLogElement(LPSTR szMessage)
  440. : m_szMessage(NULL)
  441. {
  442. SetLogMessage(szMessage);
  443. }
  444. DebugLogElement::~DebugLogElement()
  445. {
  446. if (m_szMessage != NULL)
  447. {
  448. delete [] m_szMessage;
  449. }
  450. }
  451. HRESULT DebugLogElement::SetLogMessage(LPSTR szMessage)
  452. {
  453. HRESULT hr = S_OK;
  454. if (m_szMessage != NULL)
  455. {
  456. delete [] m_szMessage;
  457. m_szMessage = NULL;
  458. }
  459. m_szMessage = new char[strlen(szMessage) + 1];
  460. if (m_szMessage != NULL)
  461. {
  462. strcpy(m_szMessage, szMessage);
  463. }
  464. else
  465. {
  466. hr = E_OUTOFMEMORY;
  467. }
  468. return hr;
  469. }