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.

728 lines
15 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. logapi.c
  5. Abstract:
  6. Public exposure of an error logging API, based on windows\setup\setuplog.
  7. Author:
  8. Jim Schmidt (jimschm) 28-Apr-1997
  9. Revision History:
  10. jimschm 16-Dec-1998 Added UseCountCs (duh!!)
  11. --*/
  12. #include "precomp.h"
  13. #include <setuplog.h>
  14. SETUPLOG_CONTEXT LogContext;
  15. INT UseCount;
  16. #define MAX_STRING_RESOURCE 0x08000
  17. //
  18. // NOTE: Watch the case. We expose an API named SetupLogError, which is different than
  19. // the lib-based SetuplogError function.
  20. //
  21. LPSTR
  22. pUnicodeToAnsiForDisplay (
  23. PCWSTR UnicodeStr
  24. )
  25. {
  26. INT Len;
  27. LPSTR AnsiBuffer;
  28. CHAR CodePage[32];
  29. DWORD rc;
  30. //
  31. // Allocate buffer to be freed by caller
  32. //
  33. Len = (lstrlenW (UnicodeStr) + 1) * sizeof (WCHAR);
  34. AnsiBuffer = (LPSTR) MyMalloc (Len);
  35. if (!AnsiBuffer) {
  36. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  37. return NULL;
  38. }
  39. //
  40. // Convert to UNICODE based on thread's Locale; convert assuming string
  41. // is for display purposes
  42. //
  43. if (!GetLocaleInfoA (GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE, CodePage, 32)) {
  44. MyFree (AnsiBuffer);
  45. return NULL;
  46. }
  47. rc = WideCharToMultiByte (
  48. atoi (CodePage),
  49. WC_COMPOSITECHECK|WC_DISCARDNS,
  50. UnicodeStr,
  51. -1,
  52. AnsiBuffer,
  53. Len,
  54. NULL,
  55. NULL
  56. );
  57. if (rc == 0) {
  58. MyFree (AnsiBuffer);
  59. return NULL;
  60. }
  61. return AnsiBuffer;
  62. }
  63. PWSTR
  64. pAnsiToUnicodeForDisplay (
  65. LPCSTR AnsiStr
  66. )
  67. {
  68. INT Len;
  69. LPWSTR UnicodeBuffer;
  70. CHAR CodePage[32];
  71. DWORD rc;
  72. //
  73. // Allocate buffer to be freed by caller
  74. //
  75. Len = (lstrlenA (AnsiStr) + 1) * sizeof (WCHAR);
  76. UnicodeBuffer = (LPWSTR) MyMalloc (Len);
  77. if (!UnicodeBuffer) {
  78. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  79. return NULL;
  80. }
  81. //
  82. // Convert to UNICODE based on thread's Locale
  83. //
  84. if (!GetLocaleInfoA (GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE, CodePage, 32)) {
  85. MyFree (UnicodeBuffer);
  86. return NULL;
  87. }
  88. rc = MultiByteToWideChar (
  89. atoi (CodePage),
  90. MB_USEGLYPHCHARS,
  91. AnsiStr,
  92. -1,
  93. UnicodeBuffer,
  94. Len
  95. );
  96. if (rc == 0) {
  97. MyFree (UnicodeBuffer);
  98. return NULL;
  99. }
  100. return UnicodeBuffer;
  101. }
  102. PVOID
  103. pOpenFileCallback (
  104. IN LPCTSTR Filename,
  105. IN BOOL WipeLogFile
  106. )
  107. /*++
  108. Routine Description:
  109. Opens the log and optionally overwrites an existing copy.
  110. Arguments:
  111. FileName - Specifies the name of the file to open or create
  112. WipeLogFile - TRUE if an existing log should be overwritten, FALSE if
  113. it should be appended
  114. Return Value:
  115. Pointer to the file handle.
  116. --*/
  117. {
  118. TCHAR CompleteFilename[MAX_PATH];
  119. HANDLE hFile;
  120. //
  121. // Form the pathname of the logfile. (uses real Windows directory)
  122. //
  123. lstrcpyn(CompleteFilename,WindowsDirectory,SIZECHARS(CompleteFilename));
  124. if (!pSetupConcatenatePaths (CompleteFilename, Filename, SIZECHARS(CompleteFilename), NULL)) {
  125. return NULL;
  126. }
  127. //
  128. // If we're wiping the logfile clean, attempt to delete
  129. // what's there.
  130. //
  131. if(WipeLogFile) {
  132. SetFileAttributes (CompleteFilename, FILE_ATTRIBUTE_NORMAL);
  133. DeleteFile (CompleteFilename);
  134. }
  135. //
  136. // Open existing file or create a new one.
  137. //
  138. hFile = CreateFile (
  139. CompleteFilename,
  140. GENERIC_READ | GENERIC_WRITE,
  141. FILE_SHARE_READ | FILE_SHARE_WRITE,
  142. NULL,
  143. OPEN_ALWAYS,
  144. FILE_ATTRIBUTE_NORMAL,
  145. NULL
  146. );
  147. return (PVOID)hFile;
  148. }
  149. static
  150. BOOL
  151. pWriteFile (
  152. IN PVOID LogFile,
  153. IN LPCTSTR Buffer
  154. )
  155. /*++
  156. Routine Description:
  157. Writes an entry to the Setup Error Log by converting string to ANSI and
  158. calling WriteFile. The message is appended to the log.
  159. Arguments:
  160. LogFile - The handle to an open log file
  161. Buffer - The UNICODE message to write
  162. Return Value:
  163. Boolean indicating whether the operation was successful. Error code is set
  164. to a Win32 error code if the return value is FALSE.
  165. --*/
  166. {
  167. PCSTR AnsiBuffer;
  168. BOOL Status;
  169. DWORD DontCare;
  170. if (0xffffffff == SetFilePointer (LogFile, 0, NULL, FILE_END)) {
  171. return FALSE;
  172. }
  173. #ifdef UNICODE
  174. //
  175. // Convert to ANSI for file output
  176. //
  177. if (AnsiBuffer = pUnicodeToAnsiForDisplay (Buffer)) {
  178. Status = WriteFile (
  179. LogFile,
  180. AnsiBuffer,
  181. lstrlenA (AnsiBuffer),
  182. &DontCare,
  183. NULL
  184. );
  185. MyFree (AnsiBuffer);
  186. } else {
  187. Status = FALSE;
  188. }
  189. #else
  190. Status = WriteFile (
  191. LogFile,
  192. Buffer,
  193. lstrlen (Buffer),
  194. &DontCare,
  195. NULL
  196. );
  197. #endif
  198. if (Status) {
  199. FlushFileBuffers (LogFile);
  200. }
  201. return Status;
  202. }
  203. static
  204. LPTSTR
  205. pFormatLogMessage (
  206. IN LPCTSTR MessageString,
  207. IN UINT MessageId, OPTIONAL
  208. IN va_list * ArgumentList
  209. )
  210. /*++
  211. Routine Description:
  212. Format a message string using a message string and caller-supplied
  213. arguments.
  214. This routine supports only MessageIds that are Win32 error codes. It
  215. does not support messages for string resources.
  216. Arguments:
  217. MessageString - Supplies the message text. For logapi.c, this should
  218. always be non-NULL.
  219. MessageId - Supplies a Win32 error code, or 0 if MessageString is to be
  220. used.
  221. ArgumentList - supplies arguments to be inserted in the message text.
  222. Return Value:
  223. Pointer to buffer containing formatted message. If the message was not found
  224. or some error occurred retrieving it, this buffer will bne empty.
  225. Caller can free the buffer with MyFree().
  226. If NULL is returned, out of memory.
  227. --*/
  228. {
  229. DWORD d;
  230. LPTSTR Buffer;
  231. LPTSTR Message;
  232. TCHAR ModuleName[MAX_PATH];
  233. TCHAR ErrorNumber[24];
  234. LPTSTR Args[2];
  235. if (MessageString > (LPCTSTR) SETUPLOG_USE_MESSAGEID) {
  236. d = FormatMessage (
  237. FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_STRING,
  238. MessageString,
  239. 0,
  240. 0,
  241. (LPTSTR) &Buffer,
  242. 0,
  243. ArgumentList
  244. );
  245. } else {
  246. d = FormatMessage (
  247. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  248. ((MessageId < MSG_FIRST) ? FORMAT_MESSAGE_FROM_SYSTEM : FORMAT_MESSAGE_FROM_HMODULE),
  249. (PVOID) GetModuleHandle (NULL),
  250. MessageId,
  251. MAKELANGID (LANG_NEUTRAL,SUBLANG_NEUTRAL),
  252. (LPTSTR) &Buffer,
  253. 0,
  254. ArgumentList
  255. );
  256. }
  257. if(!d) {
  258. //
  259. // Give up.
  260. //
  261. return NULL;
  262. }
  263. //
  264. // Make duplicate using our memory system so user can free with MyFree().
  265. //
  266. Message = DuplicateString (Buffer);
  267. LocalFree ((HLOCAL) Buffer);
  268. return Message;
  269. }
  270. static
  271. BOOL
  272. pAcquireMutex (
  273. IN PVOID Mutex
  274. )
  275. /*++
  276. Routine Description:
  277. Waits on the log mutex for a max of 1 second, and returns TRUE if the mutex
  278. was claimed, or FALSE if the claim timed out.
  279. Arguments:
  280. Mutex - specifies which mutex to acquire.
  281. Return Value:
  282. TRUE if the mutex was claimed, or FALSE if the claim timed out.
  283. --*/
  284. {
  285. DWORD rc;
  286. if (!Mutex) {
  287. SetLastError (ERROR_INVALID_HANDLE);
  288. return FALSE;
  289. }
  290. // Wait a max of 1 second for the mutex
  291. rc = WaitForSingleObject (Mutex, 1000);
  292. if (rc != WAIT_OBJECT_0) {
  293. SetLastError (ERROR_EXCL_SEM_ALREADY_OWNED);
  294. return FALSE;
  295. }
  296. return TRUE;
  297. }
  298. BOOL
  299. WINAPI
  300. SetupOpenLog (
  301. BOOL Erase
  302. )
  303. /*++
  304. Routine Description:
  305. Opens the log for processing. Must be called before SetupLogError is called.
  306. A use count is maintained so a single process can call SetupOpenLog and
  307. SetupCloseLog from multiple threads.
  308. Arguments:
  309. Erase - TRUE to erase an existing log, or FALSE to append to an existing log
  310. Return Value:
  311. Boolean indicating whether the operation was successful. Error code is set
  312. to a Win32 error code if the return value is FALSE.
  313. --*/
  314. {
  315. BOOL b = TRUE;
  316. INT i;
  317. DWORD rc;
  318. BOOL locked = FALSE;
  319. __try {
  320. EnterCriticalSection (&LogUseCountCs);
  321. locked = TRUE;
  322. //
  323. // Perform initialization of log APIs
  324. //
  325. if (!UseCount) {
  326. LogContext.OpenFile = (PSPLOG_OPENFILE_ROUTINE) pOpenFileCallback;
  327. LogContext.CloseFile = CloseHandle;
  328. LogContext.AllocMem = pSetupMalloc;
  329. LogContext.FreeMem = pSetupFree;
  330. LogContext.Format = (PSPLOG_FORMAT_ROUTINE) pFormatLogMessage;
  331. LogContext.Write = (PSPLOG_WRITE_ROUTINE) pWriteFile;
  332. LogContext.Lock = pAcquireMutex;
  333. LogContext.Unlock = ReleaseMutex;
  334. LogContext.Mutex = CreateMutexW(NULL,FALSE,L"SetuplogMutex");
  335. for (i = 0 ; i < LogSevMaximum ; i++) {
  336. LogContext.SeverityDescriptions[i] = MyLoadString (IDS_LOGSEVINFORMATION + i);
  337. }
  338. //
  339. // We don't want to allow anyone to erase the existing log, so we just
  340. // ignore the value of Erase and always append to the log.
  341. //
  342. b = SetuplogInitialize (&LogContext, FALSE);
  343. rc = GetLastError();
  344. } else {
  345. rc = ERROR_ALREADY_INITIALIZED;
  346. }
  347. UseCount++;
  348. }
  349. __finally {
  350. //
  351. // Clean up and exit
  352. //
  353. if (!b) {
  354. SetupCloseLog();
  355. }
  356. SetLastError (rc);
  357. if(locked) {
  358. LeaveCriticalSection (&LogUseCountCs);
  359. }
  360. }
  361. return b;
  362. }
  363. VOID
  364. WINAPI
  365. SetupCloseLog (
  366. VOID
  367. )
  368. /*++
  369. Routine Description:
  370. Cleans up all resources associated with the log
  371. Arguments:
  372. none
  373. Return Value:
  374. none
  375. --*/
  376. {
  377. INT i;
  378. BOOL locked=FALSE;
  379. __try {
  380. EnterCriticalSection (&LogUseCountCs);
  381. locked = TRUE;
  382. if (!UseCount) {
  383. __leave;
  384. }
  385. UseCount--;
  386. if (!UseCount) {
  387. if(LogContext.Mutex) {
  388. CloseHandle(LogContext.Mutex);
  389. LogContext.Mutex = NULL;
  390. }
  391. for (i=0; i<LogSevMaximum; i++) {
  392. if (LogContext.SeverityDescriptions[i]) {
  393. MyFree (LogContext.SeverityDescriptions[i]);
  394. }
  395. }
  396. SetuplogTerminate();
  397. }
  398. }
  399. __finally {
  400. if(locked) {
  401. LeaveCriticalSection (&LogUseCountCs);
  402. }
  403. }
  404. }
  405. BOOL
  406. WINAPI
  407. SetupLogErrorA (
  408. IN PCSTR MessageString,
  409. IN LogSeverity Severity
  410. )
  411. /*++
  412. Routine Description:
  413. Writes an entry to the Setup Error Log. If we're being compiled UNICODE,
  414. we convert the MessageString to UNICODE and call SetupLogErrorW. If we're
  415. being compiled ANSI, we call the log API directly.
  416. Arguments:
  417. MessageString - Pointer to a buffer containing unformatted message text
  418. Severity - Severity of the error:
  419. LogSevInformation
  420. LogSevWarning
  421. LogSevError
  422. LogSevFatalError
  423. Return Value:
  424. Boolean indicating whether the operation was successful. Error code is set
  425. to a Win32 error code if the return value is FALSE.
  426. --*/
  427. {
  428. INT Len;
  429. PWSTR UnicodeBuffer;
  430. BOOL b = FALSE;
  431. CHAR CodePage[32];
  432. DWORD rc;
  433. __try {
  434. if (!UseCount) {
  435. rc = ERROR_FILE_INVALID;
  436. } else {
  437. #ifdef UNICODE
  438. UnicodeBuffer = pAnsiToUnicodeForDisplay (MessageString);
  439. //
  440. // Call UNICODE version of the log API, preserve error code
  441. //
  442. if (UnicodeBuffer) {
  443. b = SetupLogErrorW (UnicodeBuffer, Severity);
  444. rc = GetLastError();
  445. MyFree (UnicodeBuffer);
  446. } else {
  447. rc = GetLastError();
  448. }
  449. #else
  450. //
  451. // ANSI version -- call SetuplogError directly
  452. //
  453. b = SetuplogError (Severity, "%1", 0, MessageString, 0, 0);
  454. rc = GetLastError();
  455. #endif
  456. }
  457. }
  458. __except (TRUE) {
  459. //
  460. // If caller passes in bogus pointer, fail with invalid parameter error
  461. //
  462. rc = ERROR_INVALID_PARAMETER;
  463. b = FALSE;
  464. }
  465. SetLastError(rc);
  466. return b;
  467. }
  468. BOOL
  469. WINAPI
  470. SetupLogErrorW (
  471. IN PCWSTR MessageString,
  472. IN LogSeverity Severity
  473. )
  474. /*++
  475. Routine Description:
  476. Writes an entry to the Setup Error Log. If compiled with UNICODE, we call the
  477. SetuplogError function directly. If compiled with ANSI, we convert to ANSI
  478. and call SetupLogErrorA.
  479. Arguments:
  480. MessageString - Pointer to a buffer containing unformatted message text
  481. Severity - Severity of the error:
  482. LogSevInformation
  483. LogSevWarning
  484. LogSevError
  485. LogSevFatalError
  486. Return Value:
  487. Boolean indicating whether the operation was successful. Error code is set
  488. to a Win32 error code if the return value is FALSE.
  489. --*/
  490. {
  491. BOOL b = FALSE;
  492. PCSTR AnsiBuffer;
  493. DWORD rc;
  494. __try {
  495. if (!UseCount) {
  496. rc = ERROR_FILE_INVALID;
  497. } else {
  498. #ifdef UNICODE
  499. //
  500. // UNICODE version: Call SetuplogError directly
  501. //
  502. // Log the error -- we always link to a UNICODE SetuplogError, despite the TCHAR header file
  503. b = SetuplogError (Severity, L"%1", 0, MessageString, NULL, NULL);
  504. rc = GetLastError();
  505. #else
  506. //
  507. // ANSI version: Convert down to ANSI, then call SetupLogErrorA
  508. //
  509. AnsiBuffer = pUnicodeToAnsiForDisplay (MessageString);
  510. if (AnsiBuffer) {
  511. b = SetupLogErrorA (AnsiBuffer, Severity);
  512. rc = GetLastError();
  513. MyFree (AnsiBuffer);
  514. } else {
  515. rc = GetLastError();
  516. }
  517. #endif
  518. }
  519. }
  520. __except (TRUE) {
  521. rc = ERROR_INVALID_PARAMETER;
  522. b = FALSE;
  523. }
  524. SetLastError(rc);
  525. return b;
  526. }