Source code of Windows XP (NT5)
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.

593 lines
17 KiB

  1. /********************************************************************
  2. Copyright (c) 1996-2000 Microsoft Corporation
  3. Module Name:
  4. weblog.cpp
  5. Abstract:
  6. Defines a generic class that can be used to log
  7. info from ISAPIs. This class allows its user to
  8. create application specific logfiles and
  9. automatically uses an intermediate file to log info and
  10. creates permanent log files at predefined intervals
  11. or after predefined number of records have been
  12. written to the intermediate file.
  13. Revision History:
  14. rsraghav created 03/25/96
  15. DerekM modified 04/06/99
  16. DerekM modifued 02/24/00
  17. ********************************************************************/
  18. #include "stdafx.h"
  19. #include "weblog.h"
  20. #include "util.h"
  21. #include "wchar.h"
  22. /////////////////////////////////////////////////////////////////////////////
  23. // CWeblog- initialization stuff
  24. // **************************************************************************
  25. CWeblog::CWeblog(void)
  26. {
  27. InitializeCriticalSection(&m_cs);
  28. m_fInit = FALSE;
  29. m_cMaxRecords = c_dwMaxRecordsDefault;
  30. m_dwDumpInterval = c_dwDumpIntervalDefault;
  31. m_liDumpIntervalAsFT = c_dwDumpIntervalDefault;
  32. m_liDumpIntervalAsFT *= c_dwMinToMS;
  33. m_liDumpIntervalAsFT *= c_dwFTtoMS;
  34. m_szAppName[0] = L'\0';
  35. m_szFileName[0] = L'\0';
  36. m_szFilePath[0] = L'\0';
  37. ZeroMemory(&m_ftLastDump, sizeof(m_ftLastDump));
  38. m_cRecords = 0;
  39. m_hFile = INVALID_HANDLE_VALUE;
  40. }
  41. // **************************************************************************
  42. CWeblog::~CWeblog()
  43. {
  44. if (m_hFile != INVALID_HANDLE_VALUE)
  45. CloseHandle(m_hFile);
  46. DeleteCriticalSection(&m_cs);
  47. }
  48. /////////////////////////////////////////////////////////////////////////////
  49. // CWeblog- internal stuff
  50. // **************************************************************************
  51. HRESULT CWeblog::InitFromRegistry()
  52. {
  53. USE_TRACING("CWeblog::InitFromRegistry");
  54. SAppLogInfoExtra alie;
  55. SAppLogInfo ali;
  56. HRESULT hr;
  57. // read the ALI & ALIE structures
  58. TESTHR(hr, ReadALI(m_szAppName, &ali, &alie));
  59. if (FAILED(hr))
  60. goto done;
  61. CopyMemory(&m_ftLastDump, &alie.ftLastDump, sizeof(m_ftLastDump));
  62. lstrcpyW(m_szFileName, ali.wszLogPath);
  63. lstrcpyW(m_szFilePath, ali.wszLogPath);
  64. m_cMaxRecords = ali.cMaxTempRecs;
  65. m_cRecords = alie.cCurTempRecs;
  66. m_dwDumpInterval = ali.cDumpMins;
  67. m_liDumpIntervalAsFT = m_dwDumpInterval;
  68. m_liDumpIntervalAsFT *= c_dwMinToMS;
  69. m_liDumpIntervalAsFT *= c_dwFTtoMS;
  70. done:
  71. return hr;
  72. }
  73. // **************************************************************************
  74. BOOL CWeblog::IsDumpRequired()
  75. {
  76. USE_TRACING("CWeblog::IsDumpRequired");
  77. SYSTEMTIME stNow;
  78. FILETIME ftNow;
  79. // check if we've gone over our allotted amount of records
  80. if ((m_cMaxRecords > 0) && (m_cRecords >= m_cMaxRecords))
  81. return TRUE;
  82. // ok, so now check if we've gone over our allotted time range.
  83. // if we fail to convert the system time, do a dump to be on the safe side...
  84. GetLocalTime(&stNow);
  85. if (SystemTimeToFileTime(&stNow, &ftNow) == FALSE)
  86. return TRUE;
  87. if (((*(unsigned __int64 *)&ftNow) -
  88. (*(unsigned __int64 *)&m_ftLastDump)) > m_liDumpIntervalAsFT)
  89. return TRUE;
  90. return FALSE;
  91. }
  92. // **************************************************************************
  93. HRESULT CWeblog::DumpLog()
  94. {
  95. USE_TRACING("CWeblog::DumpLog");
  96. SYSTEMTIME st;
  97. HRESULT hr = NOERROR;
  98. DWORD dwRet;
  99. WCHAR *pszStart = NULL;
  100. WCHAR szNewName[MAX_PATH + 1];
  101. int nUsed;
  102. this->Lock();
  103. // Close handle to the intermediate file
  104. CloseHandle(m_hFile);
  105. // intialize new internal stuff...
  106. m_hFile = INVALID_HANDLE_VALUE;
  107. m_cRecords = 0;
  108. // Rename the intermediate file to be permanent logfile with
  109. // appropriate name
  110. nUsed = wsprintfW(szNewName, L"%ls%ls", m_szFilePath, m_szAppName);
  111. if ((nUsed + c_cMaxTimeSuffixLen) > MAX_PATH)
  112. {
  113. // the file name is too long - truncate it
  114. szNewName[MAX_PATH - c_cMaxTimeSuffixLen - 1] = L'\0';
  115. nUsed = lstrlenW(szNewName);
  116. }
  117. // update last dump time stamp
  118. GetLocalTime(&st);
  119. SystemTimeToFileTime(&st, &m_ftLastDump);
  120. // create the rest of the filename...
  121. pszStart = &szNewName[nUsed];
  122. wsprintfW(pszStart, L"%04d%02d%02d%02d%02d%02d%ls", st.wYear, st.wMonth,
  123. st.wDay, st.wHour, st.wMinute, st.wSecond,
  124. c_szPermLogfileSuffix);
  125. // move the file (duh.)
  126. MoveFileExW(m_szFileName, szNewName,
  127. MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH);
  128. // recreate the intermediate file and get a handle to it
  129. m_hFile = CreateFileW(m_szFileName, GENERIC_WRITE, FILE_SHARE_READ,
  130. NULL, OPEN_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN,
  131. NULL);
  132. if (m_hFile == INVALID_HANDLE_VALUE)
  133. hr = Err2HR(GetLastError());
  134. else
  135. SetFilePointer(m_hFile, 0, NULL, FILE_END);
  136. this->Unlock();
  137. return hr;
  138. }
  139. /////////////////////////////////////////////////////////////////////////////
  140. // CWeblog- exposed methods
  141. // **************************************************************************
  142. HRESULT CWeblog::InitLogging(LPCWSTR szAppName)
  143. {
  144. USE_TRACING("CWeblog::InitLogging");
  145. HRESULT hr = NOERROR;
  146. int nUsed;
  147. // only allow one init per lifetime of the object...
  148. VALIDATEEXPR(hr, (m_fInit), E_FAIL);
  149. if (FAILED(hr))
  150. goto done;
  151. // validate params
  152. VALIDATEPARM(hr, (szAppName == NULL));
  153. if (FAILED(hr))
  154. goto done;
  155. // copy in the app name, zero terminating in case app name is longer than we allow...
  156. wcsncpy(m_szAppName, szAppName, c_cMaxAppNameLen);
  157. m_szAppName[c_cMaxAppNameLen] = L'\0';
  158. // Read in settings from Registry
  159. TESTHR(hr, InitFromRegistry());
  160. if (FAILED(hr))
  161. goto done;
  162. // m_szFileName contains the file path: append appname and suffix
  163. nUsed = lstrlenW(m_szFileName);
  164. VALIDATEEXPR(hr, ((nUsed + lstrlenW(m_szAppName) + 6) > MAX_PATH), E_FAIL);
  165. if (FAILED(hr))
  166. goto done;
  167. // Open the intermediate file and keep it ready for appending
  168. wsprintfW(m_szFileName + nUsed, L"%ls%ls", m_szAppName, c_szTempLogfileSuffix);
  169. m_hFile = CreateFileW(m_szFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL,
  170. OPEN_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  171. TESTBOOL(hr, (m_hFile != INVALID_HANDLE_VALUE))
  172. if (FAILED(hr))
  173. goto done;
  174. SetFilePointer(m_hFile, 0, NULL, FILE_END);
  175. m_fInit = TRUE;
  176. done:
  177. return hr;
  178. }
  179. // **************************************************************************
  180. HRESULT CWeblog::TerminateLogging(void)
  181. {
  182. USE_TRACING("CWeblog::TerminateLogging");
  183. SAppLogInfoExtra alie;
  184. HRESULT hr = NOERROR;
  185. // bug out if the initialization didn't work smoothly...
  186. if (m_fInit == FALSE)
  187. goto done;
  188. lstrcpyW(alie.wszName, m_szAppName);
  189. CopyMemory(&alie.ftLastDump, &m_ftLastDump, sizeof(alie.ftLastDump));
  190. alie.cCurTempRecs = m_cRecords;
  191. TESTHR(hr, WriteALI(NULL, &alie));
  192. if (FAILED(hr))
  193. goto done;
  194. m_fInit = FALSE;
  195. m_cMaxRecords = c_dwMaxRecordsDefault;
  196. m_dwDumpInterval = c_dwDumpIntervalDefault;
  197. m_liDumpIntervalAsFT = c_dwDumpIntervalDefault;
  198. m_liDumpIntervalAsFT *= c_dwMinToMS;
  199. m_liDumpIntervalAsFT *= c_dwFTtoMS;
  200. m_szAppName[0] = L'\0';
  201. m_szFileName[0] = L'\0';
  202. m_szFilePath[0] = L'\0';
  203. ZeroMemory(&m_ftLastDump, sizeof(m_ftLastDump));
  204. m_cRecords = 0;
  205. done:
  206. return hr;
  207. }
  208. // **************************************************************************
  209. HRESULT CWeblog::LogRecord(LPCWSTR szFormat, ... )
  210. {
  211. USE_TRACING("CWeblog::LogRecord");
  212. SYSTEMTIME st;
  213. va_list arglist;
  214. HRESULT hr = NOERROR;
  215. DWORD dwUsed, nWritten, dwUsedA;
  216. WCHAR szLogRecordW[c_cMaxRecLen + 1];
  217. CHAR szLogRecordA[(2 * c_cMaxRecLen) + 1];
  218. int nAdded;
  219. // only allow 'em in if they've called init...
  220. VALIDATEEXPR(hr, (m_fInit == FALSE), E_FAIL);
  221. if (FAILED(hr))
  222. goto done;
  223. // validate params
  224. VALIDATEPARM(hr, (szFormat == NULL));
  225. if (FAILED(hr))
  226. goto done;
  227. // prepend the app name and current time
  228. GetLocalTime(&st);
  229. dwUsed = (ULONG)wsprintfW(szLogRecordW, L"%ls,%04u/%02u/%02u %02u:%02u:%02u\n",
  230. m_szAppName, st.wYear, st.wMonth, st.wDay,
  231. st.wHour, st.wMinute, st.wSecond);
  232. va_start(arglist, szFormat);
  233. nAdded = _vsnwprintf(&szLogRecordW[dwUsed], c_cMaxRecLen - dwUsed,
  234. szFormat, arglist);
  235. // is the arglist too big for us?
  236. if (nAdded < 0)
  237. {
  238. // if so, just insert a dummy value
  239. lstrcpyW(&szLogRecordW[dwUsed],
  240. L"Logging Error: Record given for logging is too big!\r\n");
  241. dwUsed = lstrlenW(szLogRecordW);
  242. }
  243. else
  244. {
  245. // otherwise, add a CRLF to the end...
  246. dwUsed += (ULONG) nAdded;
  247. szLogRecordW[dwUsed++] = L'\r';
  248. szLogRecordW[dwUsed++] = L'\n';
  249. szLogRecordW[dwUsed] = L'\0';
  250. }
  251. va_end(arglist);
  252. // translate the record down into ASCII so the lab team can use grep &
  253. // other such utilities on these files (which don't work with unicode
  254. // text files)
  255. dwUsedA = WideCharToMultiByte(CP_ACP, 0, szLogRecordW, -1, szLogRecordA,
  256. 2 * c_cMaxRecLen, NULL, NULL);
  257. if (dwUsedA == 0)
  258. {
  259. dwUsedA = (ULONG)wsprintfA(szLogRecordA, "%ls,%04u/%02u/%02u %02u:%02u:%02u\n",
  260. m_szAppName, st.wYear, st.wMonth, st.wDay,
  261. st.wHour, st.wMinute, st.wSecond);
  262. lstrcpyA(&szLogRecordA[dwUsedA], "Logging Error: unable to translate log record to ASCII\r\n");
  263. dwUsedA = lstrlen(szLogRecordA);
  264. }
  265. else
  266. {
  267. // dwUsedA contains a character for the NULL terminator at the end of
  268. // the ASCII string. Since we don't want to write this to the log
  269. // file, reduce the size of the log entry by 1 byte...
  270. dwUsedA--;
  271. }
  272. this->Lock();
  273. TESTBOOL(hr, (WriteFile(m_hFile, szLogRecordA, dwUsedA, &nWritten,
  274. NULL) == FALSE));
  275. if (FAILED(hr))
  276. {
  277. this->Unlock();
  278. goto done;
  279. }
  280. m_cRecords++;
  281. if (IsDumpRequired())
  282. TESTHR(hr, DumpLog());
  283. this->Unlock();
  284. done:
  285. return hr;
  286. }
  287. //////////////////////////////////////////////////////////////////////
  288. // CWeblogConfig initialization
  289. // **************************************************************************
  290. HRESULT ReadALI(LPCWSTR wszName, SAppLogInfo *pali, SAppLogInfoExtra *palie)
  291. {
  292. USE_TRACING("CWeblog::ReadALI");
  293. HRESULT hr = NOERROR;
  294. WCHAR wszDef[MAX_PATH];
  295. DWORD dw = 0, cb;
  296. HKEY hKeyLog = NULL, hKeyRoot = NULL;
  297. BOOL fWrite;
  298. VALIDATEPARM(hr, (wszName == NULL || pali == NULL));
  299. if (FAILED(hr))
  300. goto done;
  301. GetWindowsDirectoryW(wszDef, sizeof(wszDef) / sizeof(WCHAR));
  302. wszDef[3] = L'\0';
  303. TESTHR(hr, OpenRegKey(HKEY_LOCAL_MACHINE, c_szRPWeblogRootKey, FALSE,
  304. &hKeyRoot));
  305. if (FAILED(hr))
  306. goto done;
  307. TESTHR(hr, OpenRegKey(hKeyRoot, wszName, FALSE, &hKeyLog));
  308. if (FAILED(hr))
  309. goto done;
  310. // max allowed temp records
  311. cb = sizeof(pali->cMaxTempRecs);
  312. TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVMaxRecords, NULL,
  313. (PBYTE)&pali->cMaxTempRecs, &cb,
  314. (PBYTE)&c_dwMaxRecordsDefault, sizeof(DWORD)));
  315. if (FAILED(hr))
  316. goto done;
  317. // dump interval
  318. cb = sizeof(pali->cDumpMins);
  319. TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVDumpInterval, NULL,
  320. (PBYTE)&pali->cDumpMins, &cb,
  321. (PBYTE)&c_dwDumpIntervalDefault, sizeof(DWORD)));
  322. if (FAILED(hr))
  323. goto done;
  324. // weblog path
  325. cb = sizeof(pali->wszLogPath) * sizeof(WCHAR);
  326. TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVLogFilePath, NULL,
  327. (PBYTE)&pali->wszLogPath, &cb, (PBYTE)&wszDef,
  328. (lstrlenW(wszDef) + 1) * sizeof(WCHAR)));
  329. if (FAILED(hr))
  330. goto done;
  331. lstrcpyW(pali->wszName, wszName);
  332. // make sure we have a '\' at the end of the file path...
  333. dw = lstrlenW(pali->wszLogPath);
  334. if (pali->wszLogPath[dw - 1] != L'\\')
  335. {
  336. pali->wszLogPath[dw] = L'\\';
  337. pali->wszLogPath[dw + 1] = L'\0';
  338. }
  339. // make sure we have valid values here...
  340. if (pali->cDumpMins == 0)
  341. pali->cDumpMins = c_dwDumpIntervalDefault;
  342. if (pali->cMaxTempRecs == 0)
  343. pali->cMaxTempRecs = c_dwMaxRecordsDefault;
  344. // if this is NULL, we don't have to do anything more
  345. if (palie != NULL)
  346. {
  347. FILETIME ftDef;
  348. // last dump time
  349. ZeroMemory(&ftDef, sizeof(FILETIME));
  350. cb = sizeof(palie->ftLastDump);
  351. TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVLastDumpTime, NULL,
  352. (PBYTE)&palie->ftLastDump, &cb,
  353. (PBYTE)&ftDef, sizeof(FILETIME)));
  354. if (FAILED(hr))
  355. goto done;
  356. // current temp records
  357. dw = 0;
  358. cb = sizeof(palie->cCurTempRecs);
  359. TESTHR(hr, ReadRegEntry(hKeyLog, c_szRVCurrentRecs, NULL,
  360. (PBYTE)&palie->cCurTempRecs, &cb,
  361. (PBYTE)&dw, sizeof(DWORD)));
  362. if (FAILED(hr))
  363. goto done;
  364. lstrcpyW(palie->wszName, wszName);
  365. }
  366. done:
  367. if (hKeyLog != NULL)
  368. RegCloseKey(hKeyLog);
  369. if (hKeyRoot != NULL)
  370. RegCloseKey(hKeyRoot);
  371. return hr;
  372. }
  373. // **************************************************************************
  374. HRESULT WriteALI(SAppLogInfo *pali, SAppLogInfoExtra *palie)
  375. {
  376. USE_TRACING("CWeblog::WriteALI");
  377. HRESULT hr = NOERROR;
  378. LPWSTR pwsz = NULL;
  379. DWORD dwErr;
  380. HKEY hKeyLog = NULL, hKeyRoot = NULL;
  381. BOOL fWrite;
  382. if (pali != NULL)
  383. pwsz = pali->wszName;
  384. else if (palie != NULL)
  385. pwsz = palie->wszName;
  386. else
  387. goto done;
  388. TESTHR(hr, OpenRegKey(HKEY_LOCAL_MACHINE, c_szRPWeblogRootKey, TRUE,
  389. &hKeyRoot));
  390. if (FAILED(hr))
  391. goto done;
  392. TESTHR(hr, OpenRegKey(hKeyRoot, pwsz, TRUE, &hKeyLog));
  393. if (FAILED(hr))
  394. goto done;
  395. if (pali != NULL)
  396. {
  397. DWORD dw;
  398. // make sure we have valid values here...
  399. if (pali->cDumpMins == 0)
  400. pali->cDumpMins = c_dwDumpIntervalDefault;
  401. if (pali->cMaxTempRecs == 0)
  402. pali->cMaxTempRecs = c_dwMaxRecordsDefault;
  403. // make sure we have a '\' at the end of the file path...
  404. dw = lstrlenW(pali->wszLogPath);
  405. if (pali->wszLogPath[dw - 1] != L'\\')
  406. {
  407. pali->wszLogPath[dw] = L'\\';
  408. pali->wszLogPath[dw + 1] = L'\0';
  409. }
  410. // max temp records
  411. TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVMaxRecords, 0, REG_DWORD,
  412. (PBYTE)&pali->cMaxTempRecs, sizeof(DWORD)));
  413. if (FAILED(hr))
  414. goto done;
  415. // dump interval
  416. TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVDumpInterval, 0, REG_DWORD,
  417. (PBYTE)&pali->cDumpMins, sizeof(DWORD)));
  418. if (FAILED(hr))
  419. goto done;
  420. // weblog path
  421. TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVLogFilePath, 0, REG_SZ,
  422. (PBYTE)&pali->wszLogPath,
  423. (lstrlenW(pali->wszLogPath) + 1) * sizeof(WCHAR)));
  424. if (FAILED(hr))
  425. goto done;
  426. }
  427. // if this is NULL, we don't have to do anything more
  428. if (palie != NULL)
  429. {
  430. // last dump time
  431. TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVLastDumpTime, 0, REG_BINARY,
  432. (PBYTE)&palie->ftLastDump, sizeof(FILETIME)));
  433. if (FAILED(hr))
  434. goto done;
  435. // current temp records
  436. TESTERR(hr, RegSetValueExW(hKeyLog, c_szRVCurrentRecs, 0, REG_DWORD,
  437. (PBYTE)&palie->cCurTempRecs, sizeof(DWORD)));
  438. if (FAILED(hr))
  439. goto done;
  440. }
  441. done:
  442. if (hKeyLog != NULL)
  443. RegCloseKey(hKeyLog);
  444. if (hKeyRoot != NULL)
  445. RegCloseKey(hKeyRoot);
  446. return hr;
  447. }
  448. // **************************************************************************
  449. HRESULT DeleteALI(LPCWSTR wszName)
  450. {
  451. USE_TRACING("CWeblog::DeleteALI");
  452. HRESULT hr = NOERROR;
  453. DWORD dwErr;
  454. HKEY hKeyRoot = NULL;
  455. BOOL fWrite;
  456. TESTHR(hr, OpenRegKey(HKEY_LOCAL_MACHINE, c_szRPWeblogRootKey, TRUE,
  457. &hKeyRoot));
  458. if (FAILED(hr))
  459. goto done;
  460. dwErr = RegDeleteKeyW(hKeyRoot, wszName);
  461. if (dwErr != ERROR_SUCCESS && dwErr != ERROR_PATH_NOT_FOUND &&
  462. dwErr != ERROR_FILE_NOT_FOUND)
  463. {
  464. hr = Err2HR(GetLastError());
  465. goto done;
  466. }
  467. done:
  468. if (hKeyRoot != NULL)
  469. RegCloseKey(hKeyRoot);
  470. return hr;
  471. }