/**************************************************************************** PROGRAM: mttf.c AUTHOR: Lars Opstad (LarsOp) 3/16/93 PURPOSE: NT Mean-time-to-failure reporting tool. FUNCTIONS: WinMain() - check for local file, read ini file and display dialogs SignonDlgProc() - processes messages for signon dialog EventDlgProc() - processes messages for event (other problem) dialog COMMENTS: This program displays 2 dialog boxes to prompt the user for what type of problem occurred. Every polling period, the time in the mttf data file is updated (as either busy or idle based on percent of cpu usage). If a machine is idle for more than 4 hrs, the "gone" field is increased by the time gone. Whenever the program starts, the user is prompted for why they rebooted or logged off. Other problems should be logged as they happen. If any machine can not access the server file for some reason, the data is stored in c:\mttf.dat until the next time the server file is opened. At such a time, the server file is updated and the local file is deleted. ****************************************************************************/ #include #include #include #include "mttf.h" /* specific to this program */ // // For internal use, include header for NetMessageBufferSend and set up // alert name and unicode buffers for NetMessageBufferSend. // #ifndef CUSTOMER #include #define AlertName "DavidAn" WCHAR UniAlertName[16]; WCHAR UnicodeBuffer[1024]; #endif #define IniFileName "Mttf.ini" #define LocalFileName "c:\\mttf.dat" #define DEFAULT_IDLE_LIMIT 10 #define DEFAULT_POLLING_PERIOD 30 #define CONSEC_IDLE_LIMIT 4*60 #define POLLING_PRODUCT 60000 #define HUNDREDNS_TO_MS 10000 #define MAX_RETRIES 10 HANDLE hInst; // current instance HWND hCopying; // handle to copying dialog box DWORD PollingPeriod = DEFAULT_POLLING_PERIOD; DWORD IdlePercentage = DEFAULT_IDLE_LIMIT; char ResultsFile[MAX_DIR], NameFile[MAX_DIR]; BOOL Enabled=TRUE; BOOL LocalExists=FALSE; DWORD Version; DWORD ConsecIdle=0; SYSTEM_PERFORMANCE_INFORMATION PerfInfo; SYSTEM_PERFORMANCE_INFORMATION PreviousPerfInfo; /**************************************************************************** FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int) PURPOSE: Check for local file, read ini file and display dialogs. COMMENTS: Check to see if local data file exists and set flag appropriately. Initialize the performance information. Read the IniFile. Display signon dialog. Display event dialog (minimized). ****************************************************************************/ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { OFSTRUCT ofstruct; hInst = hInstance; // // Check for local file (if exists, write it to server at next opportunity) // LocalExists=(HFILE_ERROR!=OpenFile(LocalFileName, &ofstruct, OF_EXIST)); // // Initialize performance information // Version=GetVersion(); NtQuerySystemInformation( SystemPerformanceInformation, &PerfInfo, sizeof(PerfInfo), NULL ); // // Read ini file // ReadIniFile(); // // For Internal use, convert alert name to unicode for NetMessageBufferSend. // #ifndef CUSTOMER MultiByteToWideChar(CP_ACP, MB_COMPOSITE, AlertName, sizeof(UniAlertName)/2, UniAlertName, sizeof(UniAlertName)/2); #endif // // Display signon dlg // DialogBox(hInstance, (LPCSTR)IDD_SIGNON, NULL, SignonDlgProc); // // Display event dialog // DialogBox(hInstance, (LPCSTR) IDD_EVENT, NULL, EventDlgProc); return(0); } // WinMain() /***************************************************************************** FUNCTION: ReadIniFile ( ) PURPOSE: Read values from INI file using GetPrivateProfileString/Int COMMENTS: This reads entries from the mttf.ini file (in windows nt directory) for NameFile, ResultsFile, PollingPeriod and IdlePercent. Any non- existent entry returns a default. *****************************************************************************/ VOID ReadIniFile ( ) { GetPrivateProfileString("Mttf", "NameFile", "", NameFile, MAX_DIR, IniFileName); GetPrivateProfileString("Mttf", "ResultsFile", "", ResultsFile, MAX_DIR, IniFileName); PollingPeriod = GetPrivateProfileInt("Mttf", "PollingPeriod", DEFAULT_POLLING_PERIOD, IniFileName); IdlePercentage = GetPrivateProfileInt("Mttf", "IdlePercent", DEFAULT_IDLE_LIMIT, IniFileName); } /**************************************************************************** FUNCTION: DWORD CpuUsage ( ) PURPOSE: Returns percentage of cpu usage for last polling period. COMMENTS: Computes time spent in the idle thread. Divides this number by the number of 100nanoseconds in a millisecond. (This division is to prevent over-flows in later computations.) Returns 100-PercentIdle to get percent busy. ****************************************************************************/ DWORD CpuUsage( ) { LARGE_INTEGER EndTime, BeginTime, ElapsedTime; DWORD PercentIdle, Remainder; PreviousPerfInfo = PerfInfo; // // Get current perf info // NtQuerySystemInformation( SystemPerformanceInformation, &PerfInfo, sizeof(PerfInfo), NULL ); // // Get times from PerfInfo and PreviousPerfInfo // EndTime = *(PLARGE_INTEGER)&PerfInfo.IdleProcessTime; BeginTime = *(PLARGE_INTEGER)&PreviousPerfInfo.IdleProcessTime; // // Convert from 100 NS to Milliseconds // EndTime = RtlExtendedLargeIntegerDivide(EndTime, HUNDREDNS_TO_MS, &Remainder); BeginTime = RtlExtendedLargeIntegerDivide(BeginTime, HUNDREDNS_TO_MS, &Remainder); // // Compute elapsed time and percent idle // ElapsedTime = RtlLargeIntegerSubtract(EndTime,BeginTime); PercentIdle = (ElapsedTime.LowPart) / ((POLLING_PRODUCT/100)*PollingPeriod); // // Sometimes it takes significantly longer than PollingPeriod // to make a round trip. // if ( PercentIdle > 100 ) { PercentIdle = 100; } // // return cpuusage (100-PercentIdle) // return 100-PercentIdle; } /**************************************************************************** FUNCTION: IncrementStats (StatType) PURPOSE: Increments the specified stat and writes new data to mttf.dat. COMMENTS: Increment the specified stat. For MTTF_TIME, check if it is busy or idle by comparing the CpuUsage to IdlePercentage. If a machine is idle for 4 hrs consecutively, first time this is true set the IdleConsec stat to the total ConsecIdle value, otherwise set to polling period. (IdleConsec should never exceed Idle.) Open mttf.dat. If opening the server fails (10 times), open the local data file. If server opened and local file also exists, open local file also for transfer. Search through data file for matching build number, add all values and write new record. (Continue until local file is gone if transferring.) ****************************************************************************/ VOID IncrementStats( StatType stattype ) { HANDLE fhandle,localHandle; StatFileRecord newRec, curRec; DWORD numBytes; BOOL localToServer=FALSE; CHAR buffer[1024]; int i; // // Initialize all values to zero // memset(&newRec, 0, sizeof(newRec)); newRec.Version=Version; // // Increment the appropriate stat. // switch (stattype) { // // MTTF_TIME stat: Get cpu usage and set idle/busy and percent fields. // // If idle for more than CONSEC_IDLE_LIMIT, Set IdleConsec. // If difference is less than polling period (first time), set to total // ConsecIdle; otherwise, just PollingPeriod. // case MTTF_TIME: // // Has the CPU been "busy"? // if ((newRec.PercentTotal=CpuUsage()) > IdlePercentage) { // // Yes, set busy to polling period and clear ConsecIdle. // newRec.Busy = PollingPeriod; ConsecIdle=0; } else { // // No, not busy, increment consec idle by PollingPeriod. // (Set value of idle to polling period.) // // If ConsecIdle is greater than IDLE_LIMIT, set IdleConsec. // If first time, set IdleConsec to ConsecIdle, else PollingPer. // ConsecIdle+=(newRec.Idle = PollingPeriod); if (ConsecIdle>=CONSEC_IDLE_LIMIT) { if (ConsecIdle < CONSEC_IDLE_LIMIT + PollingPeriod) { newRec.IdleConsec=ConsecIdle; } else { newRec.IdleConsec=PollingPeriod; } } } // // Weight PercentTotal by number of minutes in polling period. // newRec.PercentTotal *= PollingPeriod; break; // // MTTF_COLD: Set cold boot count to 1 // case MTTF_COLD: newRec.Cold = 1; break; // // MTTF_WARM: Set warm boot count to 1 // case MTTF_WARM: newRec.Warm = 1; break; // // MTTF_OTHER: Set other problem count to 1 // case MTTF_OTHER: newRec.Other = 1; break; default: ; } // // If there is a ResultsFile name entered in the INI file, // if (ResultsFile[0]) { // // Try to open the server file (MAX_RETRIES times). // for (i=0;i>16); memstat.dwLength=sizeof(memstat); GlobalMemoryStatus(&memstat); // Get memory info sprintf(newRec.Mem, "%5ldMB",memstat.dwTotalPhys/(1024*1024)); ulLength=MAX_NAME; GetUserName(newRec.UserName, &ulLength); GetLocalTime(&sysTime); sprintf(newRec.DateAndTime, "%2d/%2d/%4d %2d:%02ld", sysTime.wMonth, sysTime.wDay, sysTime.wYear, sysTime.wHour, sysTime.wMinute); newRec.Tab1=newRec.Tab2=newRec.Tab3=newRec.Tab4=newRec.Tab5=9; newRec.CRLF[0]=13; newRec.CRLF[1]=10; // // Try to open NameFile, for shared read access. // if (INVALID_HANDLE_VALUE==(fhandle = CreateFile(NameFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL))) { return; } // // Read each record until a match or end-of-file is encountered. // while (ReadFile(fhandle, &curRec, sizeof(curRec), &numBytes, NULL)) { // // At end of file, break out and write new record. // if (numBytes==0) { break; } // // If there is a match, close the file and return. // if (0==strcmp(curRec.Build, newRec.Build) && 0==strcmp(curRec.MachineName, newRec.MachineName)) { CloseHandle(fhandle); return; } } // // Close the name file and try to open it for ExclusiveWrite // CloseHandle(fhandle); for (i=0;i