#include #include #include #include #include #include #include "network.h" #include "idw_dbg.h" #include "server.h" /*++ Filename : idw_dbg.cpp Description: Contains the idwlog1.dbg idwlog2.dbg error logging functions. Created by: Wally Ho History: Created on 31/01/2000. Modified to TCHAR from my implementation in the MPK test suite. 09.19.2001 Joe Holman fixes for idwlog bugs 409338, 399178, and 352810 10.03.2001 Joe Holman make the log file report a 'w' instead of a 'a' fopen call. 11.02.2001 Joe Holman Added code to make a connection to ntburnlab2 with particular user so we can authenticate when a machine is not in the same domain thus allowing the file copy of logs to succeed. 11.12.2001 Joe Holman Added language value to the output log. Contains these functions: 1. VOID OpenIdwlogDebugFile(DWORD dwPart); 2. VOID CloseIdwlogDebugFile(VOID); 3. VOID RemoveIdwlogDebugFile(DWORD dwPart); 4. VOID Idwlog (LPTSTR szMessage,...); --*/ // Global FILE* fpIdwlogDebugFile; CONST DWORD MAX_SIZE_OF_DEBUG_FILE = 4000000; static CONST LPTSTR IDWLOG_LOG = TEXT("Idwlog.log"); static CONST LPTSTR IDWLOGSERVICE_LOG = TEXT("IdwlogService.log"); TCHAR szSuiteMask[MAX_PATH*2]; TCHAR szProductType[MAX_PATH*2]; #define CLEAR_LOG TRUE #define APPEND_LOG FALSE VOID MyLogger ( TCHAR * String, DWORD dwBuild, BOOL bClearLog, DWORD dwDelta ) /*++ Routine Description: This function does the following: - get's the computer name - tries to create the log directory for the build - opens the log file in append mode or overwrites - writes out the specified string - closes the log file Arguments: String - this is the text that we want to copy over to the server dwBuild - the build # for the machine bClearLog - if TRUE, we fopen with "w" (zero out), if FALSE, we fopen with "a" (append) Return Value: NONE Notes: NONE Author: Joe Holman (joehol) 09.20.2001 --*/ { FILE * stream; TCHAR szName[MAX_PATH]; TCHAR szComputerName [ MAX_COMPUTERNAME_LENGTH + MAX_PATH] = "DefCompName"; DWORD dwSize; dwSize = sizeof ( szComputerName ); if ( GetComputerName ( szComputerName, &dwSize ) ) { // Try to make the directory name. This will only be succussful on the first instance for // for this build, but we need to do the operation always to make sure it gets created. // if ( dwBuild == 2600 ) { // For Service Pack builds we want to use minor and major build # also to differentiate. // We are going to use the format of: // // 2600.1001 (for internal use, we don't need to worry about having the major and minor data. // _stprintf ( szName, TEXT("\\\\ntburnlab2\\joehol\\logs\\%ld.%ld"), dwBuild, dwDelta ); CreateDirectory ( szName, NULL ); _stprintf ( szName, TEXT("\\\\ntburnlab2\\joehol\\logs\\%ld.%ld\\%s"), dwBuild, dwDelta, szComputerName ); } else { _stprintf ( szName, TEXT("\\\\ntburnlab2\\joehol\\logs\\%ld"), dwBuild ); CreateDirectory ( szName, NULL ); _stprintf ( szName, TEXT("\\\\ntburnlab2\\joehol\\logs\\%ld\\%s"), dwBuild, szComputerName ); } // Idwlog ( TEXT("MyLogger szName = %s.\n"), szName ); if ( (stream = _tfopen ( szName, (bClearLog?TEXT("w"):TEXT("a")) )) != NULL ) { TCHAR szBuf[2*MAX_PATH]; _stprintf ( szBuf, TEXT("%s"), String ); if ( fwrite ( szBuf, 1, _tcsclen(szBuf), stream ) < 1 ) { Idwlog ( TEXT("MyLogger ERROR fwrite had an error writing.\n") ); } fclose ( stream ); } else { Idwlog ( TEXT("MyLogger ERROR fopen had a problem on (%s).\n"), szName ); } } else { Idwlog ( TEXT("MyLogger ERROR GetComputerName gle = %ld\n"), GetLastError() ); } } TCHAR * ShowProductType ( DWORD dwProductType ) /*++ Routine Description: This function does the following: - determines what the product type is and returns it in string format Arguments: dwProductType - the bit mask to examine Return Value: Pointer to global string to display. Notes: NONE Author: Joe Holman (joehol) 09.20.2001 --*/ { switch ( dwProductType ) { case VER_NT_WORKSTATION : _tcscpy ( szProductType, TEXT("VER_NT_WORKSTATION") ); break; case VER_NT_DOMAIN_CONTROLLER : _tcscpy ( szProductType, TEXT("VER_NT_DOMAIN_CONTROLLER") ); break; case VER_NT_SERVER : _tcscpy ( szProductType, TEXT("VER_NT_SERVER") ); break; default : strcpy ( szProductType, "ERRORUNKNOWNPRODUCTTYPE" ); } return ( szProductType ); } TCHAR * ShowSuiteMask ( DWORD dwSuiteMask ) /*++ Routine Description: This function does the following: - examines the suite mask provided and appends each suite characteristic to the global string. Arguments: dwSuiteMask - suite mask to do bit compares against. Return Value: Pointer to global string to display. Notes: NONE Author: Joe Holman (joehol) 09.20.2001 --*/ { _tcscpy ( szSuiteMask, " " ); if ( dwSuiteMask & VER_SUITE_SMALLBUSINESS ) { _tcscat( szSuiteMask, TEXT("Small Business, ") ); } if ( dwSuiteMask & VER_SUITE_BACKOFFICE ) { _tcscat( szSuiteMask, TEXT("BackOffice, ") ); } if ( dwSuiteMask & VER_SUITE_COMMUNICATIONS ) { _tcscat( szSuiteMask, TEXT("Communications, ") ); } if ( dwSuiteMask & VER_SUITE_TERMINAL ) { _tcscat( szSuiteMask, TEXT("Terminal, ") ); } if ( dwSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED ) { _tcscat( szSuiteMask, TEXT("Small Business Restricted, ") ); } if ( dwSuiteMask & VER_SUITE_EMBEDDEDNT ) { _tcscat( szSuiteMask, TEXT("Embedded NT, ") ); } if ( dwSuiteMask & VER_SUITE_SINGLEUSERTS ) { _tcscat( szSuiteMask, TEXT("Supports Single User Terminal Service, ") ); } if ( dwSuiteMask & VER_SUITE_PERSONAL ) { _tcscat( szSuiteMask, TEXT("Home Edition, ") ); } if ( dwSuiteMask & VER_SUITE_DATACENTER ) { _tcscat( szSuiteMask, TEXT("Data Center, ") ); } if ( dwSuiteMask & VER_SUITE_ENTERPRISE ) { _tcscat( szSuiteMask, TEXT("Enterprise(old Advanced Server), ") ); } if ( dwSuiteMask & VER_SUITE_BLADE ) { _tcscat( szSuiteMask, TEXT("Blade, ") ); } return ( szSuiteMask ); } typedef struct _tagLANGINFO { LANGID LangID; INT Count; } LANGINFO,*PLANGINFO; BOOL CALLBACK EnumLangProc( HANDLE hModule, // resource-module handle LPCTSTR lpszType, // pointer to resource type LPCTSTR lpszName, // pointer to resource name WORD wIDLanguage, // resource language identifier LONG_PTR lParam // application-defined parameter ) /*++ Routine Description: Callback that counts versions stamps. Arguments: Details of version enumerated version stamp. (Ignore.) Return Value: Indirectly thru lParam: count, langID --*/ { PLANGINFO LangInfo; LangInfo = (PLANGINFO) lParam; LangInfo->Count++; // // for localized build contains multiple resource, // it usually contains 0409 as backup lang. // // if LangInfo->LangID != 0 means we already assigned an ID to it // // so when wIDLanguage == 0x409, we keep the one we got from last time // if ((wIDLanguage == 0x409) && (LangInfo->LangID != 0)) { return TRUE; } LangInfo->LangID = wIDLanguage; return TRUE; // continue enumeration } VOID CopySetupErrorLog ( LPINSTALL_DATA pID ) /*++ Routine Description: This function copies the machines SetupError.log file to one of our specified servers for later analysis. It will copy it to the following directory: \\ntburnlab2\joehol\logs\\ Arguments: NONE Return Value: NONE Notes: This function should silently fail gracefully if any error is encountered. Author: Joe Holman (joehol) 09.20.2001 --*/ { TCHAR szLog[2*MAX_PATH]; TCHAR szWindowsDirectory[MAX_PATH]; TCHAR Line[2*MAX_PATH]; UINT ui; FILE* fp; OSVERSIONINFOEX osv; BOOL b; DWORD dwBuild = 0; NETRESOURCE NetResource; TCHAR szRemoteName [MAX_PATH]; TCHAR szPassWord [ MAX_PATH ]; TCHAR szUserId [ MAX_PATH ]; DWORD dwError=0; LPCTSTR Type = (LPCTSTR) RT_VERSION; LPCTSTR Name = (LPCTSTR) 1; LANGINFO LangInfo; Idwlog ( TEXT("Entering CopySetupErrorLog.\n") ); Idwlog ( TEXT("CopySetupErrorLog pID.szSystemBuildSourceLocation = %s\n"), pID->szSystemBuildSourceLocation ); Idwlog ( TEXT("CopySetupErrorLog pID.szInstallingBuildSourceLocation = %s\n"), pID->szInstallingBuildSourceLocation ); Idwlog ( TEXT("CopySetupErrorLog g_InstallData.dwInstallingBuildDelta = %ld\n"), g_InstallData.dwInstallingBuildDelta ); // Get the windows directory for the machine. // ui = GetWindowsDirectory ( szWindowsDirectory, MAX_PATH ); if ( ui == 0 ) { Idwlog ( TEXT("CopySetupErrorLog ERROR - GetWindowsDirectory gle = %ld\n"), GetLastError()); return; } // Gain access to the machine via a user and password since a lot of machines are NOT // setup on the same domain and thus cannot authenticate unless we specify this. // We will log an error if this doesn't work, but we won't stop. // _stprintf(szRemoteName, TEXT("%s"), SETUPLOGS_MACH); _stprintf(szPassWord, TEXT("%s"), SETUPLOGS_PW); _stprintf(szUserId, TEXT("%s"), SETUPLOGS_USER); // Setup the memory for the connection. ZeroMemory( &NetResource, sizeof( NetResource ) ); NetResource.dwType = RESOURCETYPE_DISK ; NetResource.lpLocalName = NULL; NetResource.lpRemoteName = szRemoteName; NetResource.lpProvider = NULL; _stprintf(szUserId, TEXT("%s"), SETUPLOGS_USER ); dwError = WNetAddConnection2( &NetResource, szPassWord, szUserId, 0 ); //Idwlog(TEXT("WNetAddConnection2 %s [dwError=%ld] using: %s, %s, %s\n"), (dwError==NO_ERROR)?TEXT("OK"):TEXT("ERROR"), dwError, szRemoteName, szUserId, szPassWord ); Idwlog(TEXT("WNetAddConnection2 %s [dwError=%ld] using: %s\n"), (dwError==NO_ERROR)?TEXT("OK"):TEXT("ERROR"), dwError, szRemoteName ); // Write the header out to the log file. // // Note: the first writes clear the log file. // ZeroMemory (&osv, sizeof(osv) ); osv.dwOSVersionInfoSize = sizeof ( OSVERSIONINFOEX ); b = GetVersionEx ( (OSVERSIONINFO * ) &osv ); if ( !b ) { Idwlog ( TEXT("CopySetupErrorLog ERROR GetVersionEx FAILed, gle = %ld\n"), GetLastError()); dwBuild = 0; MyLogger ( TEXT("IdwLog Header (GetVersionEx ERROR.)\n"), dwBuild, CLEAR_LOG, g_InstallData.dwInstallingBuildDelta ); } else { dwBuild = osv.dwBuildNumber; Idwlog ( TEXT("CopySetupErrorLog retreived os version info. dwBuild = %ld\n"), dwBuild ); _stprintf ( szLog, TEXT ("IdwLog\nosv.dwOSVersionInfoSize = %x\nosv.dwMajorVersion = %d\nosv.dwMinorVersion = %d\nosv.dwBuildNumber = %d\nosv.dwPlatformId = %x\nosv.szCSDVersion = %s\nosv.wServicePackMajor = %x\nosv.wServicePackMinor = %x\nosv.wSuiteMask = %x (%s)\nosv.wProductType = %x (%s)\nszSystemBuildSource = %s\nszInstallingBuildSource = %s\n"), osv.dwOSVersionInfoSize, osv.dwMajorVersion, osv.dwMinorVersion, osv.dwBuildNumber, osv.dwPlatformId, osv.szCSDVersion, osv.wServicePackMajor, osv.wServicePackMinor, osv.wSuiteMask, ShowSuiteMask (osv.wSuiteMask), osv.wProductType, ShowProductType (osv.wProductType), pID->szSystemBuildSourceLocation, pID->szInstallingBuildSourceLocation ); MyLogger ( szLog, dwBuild, CLEAR_LOG, g_InstallData.dwInstallingBuildDelta) ; } // Write out the language information. // // ZeroMemory(&LangInfo,sizeof(LangInfo)); EnumResourceLanguages( GetModuleHandle(TEXT("ntdll.dll")), Type, Name, (ENUMRESLANGPROC) EnumLangProc, (LONG_PTR) &LangInfo ); _stprintf ( szLog, "LangInfo.LangID = %X\n\n", LangInfo.LangID ); MyLogger ( szLog, dwBuild, APPEND_LOG, g_InstallData.dwInstallingBuildDelta ); // Open the setup error log. // _stprintf ( szLog, TEXT("%s\\setuperr.log"), szWindowsDirectory ); fp = _tfopen ( szLog, TEXT("r") ); if ( fp == NULL ) { Idwlog ( TEXT("CopySetupErrorLog ERROR Couldn't open log file: %s\n"), szLog ); return; } Idwlog ( TEXT("CopySetupErrorLog opened local setuperr.log file.\n") ); while ( _fgetts ( Line, MAX_PATH, fp ) ) { // Prepend our tag text and write to the Server. // _stprintf ( szLog, TEXT("SetupErr.Log ERROR: %s"), Line ); MyLogger ( szLog, dwBuild, APPEND_LOG, g_InstallData.dwInstallingBuildDelta ); } fclose ( fp ); Idwlog ( TEXT("CopySetupErrorLog finished.\n") ); } VOID OpenIdwlogDebugFile(DWORD dwPart) /*++ Routine Description: This will open a logfile for the dbg output for the idwlog The parameter it takes will let it write a *.log file for either Part1 or Part two of the tools execution. Arguments: 1 for service otherwise its idwlog.log. Return Value: NONE Author: Wally Ho (wallyho) Jan 31st, 2000 --*/ { TCHAR sztimeClock[128]; TCHAR sztimeDate[128]; TCHAR szmsg[MAX_PATH]; TCHAR szIdwlogFile[30]; TCHAR szIdwlogFileAndPath[100]; TCHAR szSystemDrive[4]; BOOL bUseSysDrive; HANDLE hTestExistence; WIN32_FIND_DATA ffd; UINT i; TCHAR szLogDirectoryToCreate[100] = TEXT("c:\\idwlog"); fpIdwlogDebugFile = NULL; // Determine which part is the one we want. if (1 == dwPart){ _tcscpy(szIdwlogFile,IDWLOGSERVICE_LOG ); } else { _tcscpy(szIdwlogFile, IDWLOG_LOG ); } // Do a look for where we wrote the file first. // The case is this: we install with a system on C drive. // We install to d: drive. D drive boots up; we write the dbg // on system root d:. This splits it from the initial write. // This will find it if its on C. // if it doesn't find it then we default to system drive. bUseSysDrive = TRUE; for (i= TEXT('c'); i <= TEXT('z'); i++){ _stprintf ( szIdwlogFileAndPath, TEXT("%c:\\idwlog\\Idwlo*.dbg"), i); hTestExistence = FindFirstFile(szIdwlogFileAndPath, &ffd); if (INVALID_HANDLE_VALUE != hTestExistence){ FindClose(hTestExistence); // Delete Old DBG files. // _stprintf ( szIdwlogFileAndPath, TEXT("%c:\\idwlog\\%s"), i, IDWLOGSERVICE_LOG ); SetFileAttributes(szIdwlogFileAndPath,FILE_ATTRIBUTE_NORMAL); DeleteFile( szIdwlogFileAndPath); _stprintf ( szIdwlogFileAndPath, TEXT("%c:\\idwlog\\%s"), i, IDWLOG_LOG ); SetFileAttributes(szIdwlogFileAndPath,FILE_ATTRIBUTE_NORMAL); DeleteFile( szIdwlogFileAndPath); } } for (i= TEXT('c'); i <= TEXT('z'); i++){ _stprintf ( szIdwlogFileAndPath, TEXT("%c:\\idwlog\\%s"), i,szIdwlogFile); hTestExistence = FindFirstFile(szIdwlogFileAndPath, &ffd); if (INVALID_HANDLE_VALUE != hTestExistence){ // We found a log file in this case here. // bUseSysDrive = FALSE; FindClose(hTestExistence); _stprintf ( szIdwlogFileAndPath, TEXT("%c:\\idwlog\\%s"), i, szIdwlogFile); // Check for FileSize if Greater that 500,000 bytes then delete it. if (ffd.nFileSizeLow >= MAX_SIZE_OF_DEBUG_FILE ) { SetFileAttributes(szIdwlogFileAndPath,FILE_ATTRIBUTE_NORMAL); DeleteFile( szIdwlogFileAndPath); } break; } } if (TRUE == bUseSysDrive){ // Get the system Drive // if ( 0 == GetEnvironmentVariable(TEXT("SystemDrive"),szSystemDrive, 4)) { // // Default to C: (we're probably running on Win9x where there is // no SystemDrive envinronment variable) // _tcscpy(szSystemDrive, TEXT("C:")); } _stprintf(szIdwlogFileAndPath,TEXT("%s\\idwlog\\%s"), szSystemDrive,szIdwlogFile); _stprintf( szLogDirectoryToCreate, TEXT("%s\\idwlog"), szSystemDrive ); // new } else { _stprintf( szLogDirectoryToCreate, TEXT("%c\\idwlog"), szIdwlogFileAndPath[0] ); // new, szIdwlogFileAndPath filled out above. } // We want to store the logs in the our idwlog directory from the root, in order to fix bug #352810 - on logon, non-admin can't write log. CreateDirectory ( szLogDirectoryToCreate, NULL ); // don't check return code since it will fail in some cases if exist. fpIdwlogDebugFile = _tfopen(szIdwlogFileAndPath,TEXT("a")); if(NULL == fpIdwlogDebugFile) { // nothing we can do if the logfile is not formed? //_tprintf ( TEXT("ERROR - Could not open log file: %s\n"), szIdwlogFileAndPath ); ExitProcess(GetLastError()); } _tstrtime(sztimeClock); _tstrdate(sztimeDate); _stprintf(szmsg,TEXT("[Started on %s %s]\n"), sztimeDate, sztimeClock); _ftprintf( fpIdwlogDebugFile,TEXT("%s"), szmsg); /*** This is too annoying to have it hidden, so I'm removing it. JoeHol 09.17.2001 if(FALSE == SetFileAttributes(szIdwlogFileAndPath, FILE_ATTRIBUTE_HIDDEN)) { Idwlog(TEXT("OpenIdwlogDebugFile ERROR - Could not set the debug file to Hidden.\n")); } ***/ return; } VOID CloseIdwlogDebugFile(VOID) /*++ Routine Description: This will close the logfile for the dbg output for the idwlog Arguments: NONE Return Value: NONE Author: Wally Ho (wallyho) Jan 31st, 2000 --*/ { if ( NULL != fpIdwlogDebugFile){ fclose(fpIdwlogDebugFile); fpIdwlogDebugFile = NULL; } } VOID RemoveIdwlogDebugFile(DWORD dwPart) /*++ Routine Description: This will remove the logfile for the dbg output for the idwlog Arguments: NONE Return Value: NONE Author: Wally Ho (wallyho) Jan 31st, 2000 --*/ { TCHAR szIdwlogFile[50]; TCHAR szIdwlogFileAndPath[100]; HANDLE hTestExistence; WIN32_FIND_DATA ffd; UINT i; // Determine which part is the one we want. if (1 == dwPart){ _tcscpy(szIdwlogFile, IDWLOGSERVICE_LOG ); } else _tcscpy(szIdwlogFile, IDWLOG_LOG ); // Search all drives and kill the idwlog.dbg file for (i= TEXT('c'); i <= TEXT('z'); i++){ _stprintf ( szIdwlogFileAndPath, TEXT("%c:\\idwlog\\%s"), i,szIdwlogFile); hTestExistence = FindFirstFile(szIdwlogFileAndPath, &ffd); if (INVALID_HANDLE_VALUE != hTestExistence){ FindClose(hTestExistence); _stprintf ( szIdwlogFileAndPath, TEXT("%c:\\idwlog\\%s"), i,szIdwlogFile); // Make sure the file is removed so we have a fresh file everytime. _tremove(szIdwlogFile); break; } } } VOID Idwlog (LPTSTR szMessage,...) /*++ Routine Description: This is the logging function for the idwlog. It behaves much like printf. Arguments: same as printf. Return Value: NONE Author: Wally Ho (wallyho) Jan 31st, 2000 --*/ { va_list vaArgs; time_t t; TCHAR szTimeBuffer[30]; if ( NULL != fpIdwlogDebugFile) { // Write the time to the log. time(&t); _stprintf ( szTimeBuffer, TEXT("%s"), ctime(&t) ); // ctime addes a new line to the buffer. Erase it here. szTimeBuffer[_tcslen(szTimeBuffer) - 1] = TEXT('\0'); _ftprintf( fpIdwlogDebugFile, TEXT("[%s] "),szTimeBuffer); // Write the formatted string to the log. va_start( vaArgs, szMessage ); _vftprintf( fpIdwlogDebugFile, szMessage, vaArgs ); va_end ( vaArgs ); // Flush the stream fflush(fpIdwlogDebugFile); } }