/*++ Copyright (c) 1999 Microsoft Corporation Module Name: log.cpp Abstract: This module contains routines to log errors, warnings and info in the asr log file at %systemroot%\repair\asr.log Author: Guhan Suriyanarayanan (guhans) 10-Jul-2000 Environment: User-mode only. Revision History: 10-Jul-2000 guhans Initial creation --*/ #include #include #include #include "log.h" #define ASRSFGEN_ASR_ERROR_FILE_PATH L"%SystemRoot%\\repair\\asr.err" #define ASRSFGEN_ASR_LOG_FILE_PATH L"%SystemRoot%\\repair\\asr.log" // // ---- // Data global to this module // ---- // BOOL Gbl_IsAsrEnabled = FALSE; PWSTR Gbl_AsrErrorFilePath = NULL; HANDLE Gbl_AsrLogFileHandle = NULL; // // ---- // Function implementations // ---- // VOID AsrpLogMessage( IN CONST _MesgLevel Level, IN CONST PCSTR Message ) /*++ Routine Description: Logs the message to the asr log file, and the asr error file if needed. Note that AsrpInitialiseLogFile must have been called once before this routine is used. Arguments: Level - An enum specifying the level of the message being logged. If Level is set to s_Warning or s_Error, the Message is logged to the asr error file in addition to the asr log file. Message - The Message being logged. This routine will add in the time- stamp at the beginning of each message. Return Values: None. If the log file couldn't be found, the message isn't logged. --*/ { SYSTEMTIME Time; DWORD bytesWritten = 0; char buffer[4196]; GetLocalTime(&Time); // // This needs to be fixed by the year 2100. // sprintf(buffer, "[%02hu%02hu%02hu %02hu%02hu%02hu sfgen] %s%s\r\n", Time.wYear % 2000, Time.wMonth, Time.wDay, Time.wHour, Time.wMinute, Time.wSecond, ((s_Error == Level) ? "(ERROR) " : (s_Warning == Level ? "(warning) " : "")), Message ); OutputDebugStringA(buffer); if (Gbl_AsrLogFileHandle) { WriteFile(Gbl_AsrLogFileHandle, buffer, (strlen(buffer) * sizeof(char)), &bytesWritten, NULL ); } // // If this is a fatal error, we need to add to the error log. // if (((s_Error == Level) || (s_Warning == Level)) && (Gbl_AsrErrorFilePath) ) { WCHAR buffer2[4196]; HANDLE hFile = NULL; // // Open the error log // hFile = CreateFileW( Gbl_AsrErrorFilePath, // lpFileName GENERIC_WRITE | GENERIC_READ, // dwDesiredAccess FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode NULL, // lpSecurityAttributes OPEN_ALWAYS, // dwCreationFlags FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes NULL // hTemplateFile ); if ((!hFile) || (INVALID_HANDLE_VALUE == hFile)) { return; } wsprintf(buffer2, L"\r\n[%04hu/%02hu/%02hu %02hu:%02hu:%02hu AsrSFGen] %ws%S\r\n", Time.wYear, Time.wMonth, Time.wDay, Time.wHour, Time.wMinute, Time.wSecond, ((s_Error == Level) ? L"(ERROR) " : (s_Warning == Level ? L"(warning) " : L"")), Message ); // // Move to the end of file // SetFilePointer(hFile, 0L, NULL, FILE_END); // // Add our error string // WriteFile(hFile, buffer2, (wcslen(buffer2) * sizeof(WCHAR)), &bytesWritten, NULL ); // // And we're done // CloseHandle(hFile); } } VOID AsrpPrintDbgMsg( IN CONST _MesgLevel Level, IN CONST PCSTR FormatString, ... ) /*++ Description: This prints a debug message AND makes the appropriate entries in the log and error files. Arguments: Level - Message Level (info, warning or error) FormatString - Formatted Message String to be printed. The expanded string should fit in a buffer of 4096 characters (including the terminating null character). Return Values: None --*/ { char str[4096]; // the message better fit in this va_list arglist; va_start(arglist, FormatString); wvsprintfA(str, FormatString, arglist); va_end(arglist); AsrpLogMessage(Level, str); } PWSTR // must be freed by caller AsrpExpandEnvStrings( IN CONST PCWSTR OriginalString ) /*++ Routine Description: Expands any environment variables in the original string, replacing them with their defined values, and returns a pointer to a buffer containing the result. Arguments: OriginalString - Pointer to a null-terminated string that contains environment variables of the form %variableName%. For each such reference, the %variableName% portion is replaced with the current value of that environment variable. The replacement rules are the same as those used by the command interpreter. Case is ignored when looking up the environment- variable name. If the name is not found, the %variableName% portion is left undisturbed. Return Values: If this routine succeeds, the return value is a pointer to a buffer containing a copy of OriginalString after all environment-variable name substitutions have been performed. The caller is responsible for de-allocating this memory using HeapFree(GetProcessHeap(),...) when it is no longer needed. If the function fails, the return value is NULL. To get extended error information, call GetLastError. --*/ { BOOL result = FALSE; UINT cchRequiredSize = 0, cchSize = MAX_PATH + 1; // start with a reasonable default PWSTR expandedString = NULL; DWORD status = ERROR_SUCCESS; HANDLE hHeap = GetProcessHeap(); // // Allocate some memory for the destination string // expandedString = (PWSTR) HeapAlloc( hHeap, HEAP_ZERO_MEMORY, (cchSize * sizeof(WCHAR)) ); ErrExitCode(!expandedString, status, ERROR_NOT_ENOUGH_MEMORY); // // Try expanding. If the buffer isn't big enough, we'll re-allocate. // cchRequiredSize = ExpandEnvironmentStringsW(OriginalString, expandedString, cchSize ); if (cchRequiredSize > cchSize) { // // Buffer wasn't big enough; free and re-allocate as needed // HeapFree(hHeap, 0L, expandedString); cchSize = cchRequiredSize + 1; expandedString = (PWSTR) HeapAlloc( hHeap, HEAP_ZERO_MEMORY, (cchSize * sizeof(WCHAR)) ); ErrExitCode(!expandedString, status, ERROR_NOT_ENOUGH_MEMORY); cchRequiredSize = ExpandEnvironmentStringsW(OriginalString, expandedString, cchSize ); if (cchRequiredSize > cchSize) { SetLastError(ERROR_BAD_ENVIRONMENT); } } if ((0 == cchRequiredSize) || (cchRequiredSize > cchSize)) { // // Either the function failed, or the buffer wasn't big enough // even on the second try. // if (expandedString) { HeapFree(hHeap, 0L, expandedString); expandedString = NULL; } } EXIT: return expandedString; } VOID AsrpInitialiseErrorFile( VOID ) /*++ Description: Creates an empty ASR error file at %systemroot%\repair\asr.err, and initialises Gbl_AsrErrorFilePath with the full path to asr.err. This routine must be called once before AsrPrintDbgMsg is used. Arguments: None Return Values: None --*/ { HANDLE errorFileHandle = NULL; // // Get full path to the error file. // Gbl_AsrErrorFilePath = AsrpExpandEnvStrings(ASRSFGEN_ASR_ERROR_FILE_PATH); if (!Gbl_AsrErrorFilePath) { return; } // // Create an empty file (append to it if it already exists), and close it // immediately // errorFileHandle = CreateFileW( Gbl_AsrErrorFilePath, // lpFileName GENERIC_WRITE, // dwDesiredAccess FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode NULL, // lpSecurityAttributes OPEN_ALWAYS, // dwCreationFlags FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes NULL // hTemplateFile ); if ((errorFileHandle) && (INVALID_HANDLE_VALUE != errorFileHandle)) { CloseHandle(errorFileHandle); } } VOID AsrpInitialiseLogFiles( VOID ) /*++ Description: This creates an ASR log file at %systemroot%\repair\asr.log, and initialises Gbl_AsrLogFileHandle. It also initialises the ASR error file by calling AsrInitialiseErrorFile(). This routine must be called once before AsrPrintDbgMsg is used. Arguments: None Return Values: None --*/ { PWSTR asrLogFilePath = NULL; HANDLE hHeap = GetProcessHeap(); DWORD bytesWritten; AsrpInitialiseErrorFile(); Gbl_AsrLogFileHandle = NULL; // // Get full path to the error file. // asrLogFilePath = AsrpExpandEnvStrings(ASRSFGEN_ASR_LOG_FILE_PATH); if (!asrLogFilePath) { return; } // // Create an empty file (over-write it if it already exists). // Gbl_AsrLogFileHandle = CreateFileW( asrLogFilePath, // lpFileName GENERIC_WRITE | GENERIC_READ, // dwDesiredAccess FILE_SHARE_READ, // dwShareMode: nobody else should write to the log file while we are NULL, // lpSecurityAttributes OPEN_ALWAYS, // dwCreationFlags FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes: write through so we flush NULL // hTemplateFile ); if ((Gbl_AsrLogFileHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrLogFileHandle)) { // // Move to the end of file // SetFilePointer(Gbl_AsrLogFileHandle, 0L, NULL, FILE_END); WriteFile(Gbl_AsrLogFileHandle, "\r\n", (strlen("\r\n") * sizeof(char)), &bytesWritten,NULL); AsrpPrintDbgMsg(s_Info, "****** Entering asrsfgen.exe. ASR log at %ws", asrLogFilePath); } else { AsrpPrintDbgMsg(s_Error, "******* Unable to create/open ASR log file at %ws (0x%x)", asrLogFilePath, GetLastError() ); } if (asrLogFilePath) { HeapFree(hHeap, 0L, asrLogFilePath); asrLogFilePath = NULL; } } VOID AsrpCloseLogFiles( VOID ) /*++ Description: This closes the ASR error and log file at %systemroot%\repair\, and frees the global variables associated with them. This must be called during clean-up. AsrpPrintDbgMesg() will have no effect after this routine is called. Arguments: None Return Values: None --*/ { AsrpPrintDbgMsg(s_Info, "****** Exiting asrsfgen.exe."); // // Clean up global values // if (Gbl_AsrErrorFilePath) { HeapFree(GetProcessHeap(), 0L, Gbl_AsrErrorFilePath); Gbl_AsrErrorFilePath = NULL; } if ((Gbl_AsrLogFileHandle) && (INVALID_HANDLE_VALUE != Gbl_AsrLogFileHandle)) { CloseHandle(Gbl_AsrLogFileHandle); Gbl_AsrLogFileHandle = NULL; } }