/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1997, Microsoft Corp. All rights reserved. // // FILE // // iasperf.cpp // // SYNOPSIS // // This file implements the PerfMon DLL for the Everest server. // // MODIFICATION HISTORY // // 09/15/1997 Original version. // 03/17/1998 Unmap the shared memory after each collection interval. // 06/18/1998 Unmap view of file after each collection interval. // 08/10/1998 Clean-up registration code. // 09/08/1997 Conform to latest rev. of ietf draft. // 09/11/1997 Use stats API. Add SNMP registration code. // 10/19/1998 Throw LONG's on error. // 01/18/1999 Do not register extension agent. // 02/11/1999 Ref. count open & close. // 05/13/1999 Unload previous counters before loading new ones. // 09/23/1999 Don't create files from resource. // 01/25/2000 Check if DLL is opend during Collect. // 02/18/2000 Added support for proxy counters. // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include ////////// // Schema for the performance objects supported by this DLL. ////////// extern PerfCollectorDef PERF_SCHEMA; ////////// // The performance collector. ////////// PerfCollector theCollector; ////////// // Last start time of the server -- used to detect restarts. ////////// LARGE_INTEGER theLastStart; ////////// // Computes the server time counters. ////////// PDWORD WINAPI ComputeServerTimes( PDWORD dst ) throw () { if (theStats->seServer.liStartTime.QuadPart) { LARGE_INTEGER now, elapsed; GetSystemTimeAsFileTime((LPFILETIME)&now); elapsed.QuadPart = now.QuadPart - theStats->seServer.liStartTime.QuadPart; elapsed.QuadPart /= 10000000; *dst++ = elapsed.LowPart; elapsed.QuadPart = now.QuadPart - theStats->seServer.liResetTime.QuadPart; elapsed.QuadPart /= 10000000; *dst++ = elapsed.LowPart; } else { // If the start time is zero, then the server's not running. *dst++ = 0; *dst++ = 0; } return dst; } ////////// // Creates instances for any newly added clients. ////////// VOID WINAPI PopulateInstances( PerfObjectType& sink ) throw () { for (DWORD i = sink.size(); i < theStats->dwNumClients; ++i) { WCHAR buf[16]; sink.addInstance(ias_inet_htow(theStats->ceClients[i].dwAddress, buf)); } } ////////// // Computes derived authentication counters from raw counters. ////////// VOID WINAPI DeriveAuthCounters( PDWORD dst ) throw () { // Compute packets received. DWORD rcvd = 0; for (DWORD i = 0; i < 6; ++i) rcvd += dst[i]; dst[9] = rcvd; // Compute packets sent. DWORD sent = 0; for (DWORD j = 6; j < 9; ++j) sent += dst[j]; dst[10] = sent; // Copy raw counters into rate counters. memcpy(dst + 11, dst, sizeof(DWORD) * 11); } ////////// // Computes derived accounting counters from raw counters. ////////// VOID WINAPI DeriveAcctCounters( PDWORD dst ) throw () { // Compute packets received. DWORD rcvd = 0; for (DWORD i = 0; i < 7; ++i) rcvd += dst[i]; dst[8] = rcvd; // Compute packets sent. DWORD sent = 0; for (DWORD j = 7; j < 8; ++j) sent += dst[j]; dst[9] = sent; // Copy raw counters into rate counters. memcpy(dst + 10, dst, sizeof(DWORD) * 10); } ////////// // Callback for the authentication server object. ////////// VOID WINAPI AuthServerDataSource(PerfObjectType& sink) { PDWORD p = ComputeServerTimes(sink[0].getCounters()); *p++ = theStats->seServer.dwCounters[radiusAuthServTotalInvalidRequests]; *p++ = theStats->seServer.dwCounters[radiusAuthServTotalInvalidRequests]; memset(p, 0, sizeof(DWORD) * 9); for (DWORD i = 0; i < theStats->dwNumClients; ++i) { for (DWORD j = 0; j < 9; ++j) { p[j] += theStats->ceClients[i].dwCounters[j]; } } DeriveAuthCounters(p); } ////////// // Callback for the authentication clients object. ////////// VOID WINAPI AuthClientDataSource(PerfObjectType& sink) { PopulateInstances(sink); for (DWORD i = 0; i < theStats->dwNumClients; ++i) { PDWORD dst = sink[i].getCounters(); memcpy(dst, theStats->ceClients[i].dwCounters, sizeof(DWORD) * 9); DeriveAuthCounters(dst); } } ////////// // Callback for the accounting server object. ////////// VOID WINAPI AcctServerDataSource(PerfObjectType& sink) { PDWORD p = ComputeServerTimes(sink[0].getCounters()); *p++ = theStats->seServer.dwCounters[radiusAccServTotalInvalidRequests]; *p++ = theStats->seServer.dwCounters[radiusAccServTotalInvalidRequests]; memset(p, 0, sizeof(DWORD) * 8); for (DWORD i = 0; i < theStats->dwNumClients; ++i) { for (DWORD j = 0; j < 8; ++j) { p[j] += theStats->ceClients[i].dwCounters[j + 9]; } } DeriveAcctCounters(p); } ////////// // Callback for the accounting clients object. ////////// VOID WINAPI AcctClientDataSource(PerfObjectType& sink) { PopulateInstances(sink); for (DWORD i = 0; i < theStats->dwNumClients; ++i) { PDWORD dst = sink[i].getCounters(); memcpy(dst, theStats->ceClients[i].dwCounters + 9, sizeof(DWORD) * 8); DeriveAcctCounters(dst); } } ////////// // Creates instances for any newly added remote servers. ////////// VOID WINAPI PopulateServers( PerfObjectType& sink ) throw () { for (DWORD i = sink.size(); i < theProxy->dwNumRemoteServers; ++i) { WCHAR buf[16]; sink.addInstance( ias_inet_htow(theProxy->rseRemoteServers[i].dwAddress, buf) ); } } VOID WINAPI DeriveProxyAuthCounters( PDWORD dst ) throw () { // Compute packets received. dst[12] = + dst[radiusAuthClientAccessAccepts] + dst[radiusAuthClientAccessRejects] + dst[radiusAuthClientAccessChallenges] + dst[radiusAuthClientUnknownTypes]; // Compute requests pending. dst[13] = + dst[radiusAuthClientAccessRequests] - dst[radiusAuthClientAccessAccepts] - dst[radiusAuthClientAccessRejects] - dst[radiusAuthClientAccessChallenges] + dst[radiusAuthClientMalformedAccessResponses] + dst[radiusAuthClientBadAuthenticators] + dst[radiusAuthClientPacketsDropped] - dst[radiusAuthClientTimeouts]; // Copy raw counters into rate counters. memcpy(dst + 14, dst + 2, sizeof(DWORD) * 10); } VOID WINAPI DeriveProxyAcctCounters( PDWORD dst ) throw () { // Compute packets received. dst[10] = + dst[radiusAccClientResponses - 12] + dst[radiusAccClientUnknownTypes - 12]; // Compute requests pending. dst[11] = + dst[radiusAccClientRequests - 12] - dst[radiusAccClientResponses - 12] + dst[radiusAccClientMalformedResponses - 12] + dst[radiusAccClientBadAuthenticators - 12] + dst[radiusAccClientPacketsDropped - 12] - dst[radiusAccClientTimeouts - 12]; // Copy raw counters into rate counters. memcpy(dst + 12, dst + 2, sizeof(DWORD) * 8); } ////////// // Callback for the authentication proxy object. ////////// VOID WINAPI AuthProxyDataSource(PerfObjectType& sink) { PDWORD p = sink[0].getCounters(); p[0] = theProxy->peProxy.dwCounters[radiusAuthClientInvalidAddresses]; p[1] = theProxy->peProxy.dwCounters[radiusAuthClientInvalidAddresses]; memset(p + 2, 0, sizeof(DWORD) * 10); for (DWORD i = 0; i < theProxy->dwNumRemoteServers; ++i) { for (DWORD j = 2; j < 12; ++j) { p[j] += theProxy->rseRemoteServers[i].dwCounters[j]; } } DeriveProxyAuthCounters(p); } ////////// // Callback for the accounting proxy object. ////////// VOID WINAPI AcctProxyDataSource(PerfObjectType& sink) { PDWORD p = sink[0].getCounters(); p[0] = theProxy->peProxy.dwCounters[radiusAccClientInvalidAddresses]; p[1] = theProxy->peProxy.dwCounters[radiusAccClientInvalidAddresses]; memset(p + 2, 0, sizeof(DWORD) * 8); for (DWORD i = 0; i < theProxy->dwNumRemoteServers; ++i) { for (DWORD j = 2; j < 10; ++j) { p[j] += theProxy->rseRemoteServers[i].dwCounters[j + 12]; } } DeriveProxyAcctCounters(p); } ////////// // Callback for the remote authentication servers. ////////// VOID WINAPI AuthRemoteServerDataSource(PerfObjectType& sink) { PopulateServers(sink); for (DWORD i = 0; i < theProxy->dwNumRemoteServers; ++i) { PDWORD dst = sink[i].getCounters(); memcpy( dst, theProxy->rseRemoteServers[i].dwCounters, sizeof(DWORD) * 12 ); DeriveProxyAuthCounters(dst); } } ////////// // Callback for the remote accounting servers. ////////// VOID WINAPI AcctRemoteServerDataSource(PerfObjectType& sink) { PopulateServers(sink); for (DWORD i = 0; i < theProxy->dwNumRemoteServers; ++i) { PDWORD dst = sink[i].getCounters(); memcpy( dst, theProxy->rseRemoteServers[i].dwCounters + 12, sizeof(DWORD) * 10 ); DeriveProxyAcctCounters(dst); } } ////////// // Reference count for API initialization. ////////// LONG theRefCount; ////////// // Serialize access to PerfMon. ////////// CRITICAL_SECTION thePerfLock; /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // OpenPerformanceData // /////////////////////////////////////////////////////////////////////////////// extern "C" DWORD WINAPI OpenPerformanceData( LPWSTR lpDeviceNames ) { EnterCriticalSection(&thePerfLock); DWORD error = NO_ERROR; // Are we already initialized? if (theRefCount == 0) { if (StatsOpen()) { try { theCollector.open(PERF_SCHEMA); // Everything succeeded, so update theRefCount. theRefCount = 1; } catch (LONG lErr) { StatsClose(); error = (DWORD)lErr; } } else { error = GetLastError(); } } else { // Already initialized, so just bump the ref. count. ++theRefCount; } LeaveCriticalSection(&thePerfLock); return error; } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // CollectPerformanceData // /////////////////////////////////////////////////////////////////////////////// extern "C" DWORD WINAPI CollectPerformanceData( LPWSTR lpwszValue, LPVOID* lppData, LPDWORD lpcbBytes, LPDWORD lpcObjectTypes ) { DWORD error = NO_ERROR; EnterCriticalSection(&thePerfLock); if (theRefCount) { StatsLock(); // If the server has restarted, then if (theStats->seServer.liStartTime.QuadPart != theLastStart.QuadPart) { // ... clear out any old instances. theCollector.clear(); theLastStart = theStats->seServer.liStartTime; } try { theCollector.collect( lpwszValue, *lppData, *lpcbBytes, *lpcObjectTypes ); } catch (LONG lErr) { error = (DWORD)lErr; } StatsUnlock(); } else { error = ERROR_NOT_READY; } LeaveCriticalSection(&thePerfLock); return error; } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // ClosePerformanceData // /////////////////////////////////////////////////////////////////////////////// extern "C" DWORD WINAPI ClosePerformanceData() { EnterCriticalSection(&thePerfLock); DWORD error = NO_ERROR; if (--theRefCount == 0) { // We're the last man out, so clean-up. StatsClose(); try { theCollector.close(); } catch (LONG lErr) { error = (DWORD)lErr; } } LeaveCriticalSection(&thePerfLock); return error; } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // CreateKey // // DESCRIPTION // // Creates a registry key. // /////////////////////////////////////////////////////////////////////////////// LONG WINAPI CreateKey( PCWSTR lpSubKey, PHKEY phkResult ) { DWORD disposition; return RegCreateKeyExW( HKEY_LOCAL_MACHINE, lpSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, phkResult, &disposition ); } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // SetStringValue // // DESCRIPTION // // Sets a string value on a registry key. // /////////////////////////////////////////////////////////////////////////////// LONG WINAPI SetStringValue( HKEY hKey, PCWSTR lpValueName, DWORD dwType, PCWSTR lpData ) { return RegSetValueEx( hKey, lpValueName, 0, dwType, (CONST BYTE*)lpData, sizeof(WCHAR) * (wcslen(lpData) + 1) ); } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // DllRegisterServer // // DESCRIPTION // // Adds entries to the system registry. // /////////////////////////////////////////////////////////////////////////////// const WCHAR MODULE[] = L"%SystemRoot%\\System32\\iasperf.dll"; const WCHAR PERF_KEY[] = L"SYSTEM\\CurrentControlSet\\Services\\IAS\\Performance"; extern "C" STDAPI DllRegisterServer(void) { LONG error; HKEY hKey; DWORD disposition; ////////// // Blow away the existing counters ... ////////// UnloadPerfCounterTextStringsW(L"LODCTR " IASServiceName, TRUE); ////////// // Update the PerfMon registry entries. ////////// error = CreateKey(PERF_KEY, &hKey); if (error) { return HRESULT_FROM_WIN32(error); } SetStringValue(hKey, L"Library", REG_EXPAND_SZ, MODULE); SetStringValue(hKey, L"Open", REG_SZ, L"OpenPerformanceData"); SetStringValue(hKey, L"Close", REG_SZ, L"ClosePerformanceData"); SetStringValue(hKey, L"Collect", REG_SZ, L"CollectPerformanceData"); RegCloseKey(hKey); ////////// // Install the counters. ////////// LONG ErrorCode = LoadPerfCounterTextStringsW(L"LODCTR IASPERF.INI", TRUE); if (ErrorCode == ERROR_ALREADY_EXISTS) { ErrorCode = NO_ERROR; } return HRESULT_FROM_WIN32(ErrorCode); } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // DllUnregisterServer // // DESCRIPTION // // Removes entries from the system registry. // /////////////////////////////////////////////////////////////////////////////// extern "C" STDAPI DllUnregisterServer(void) { LONG error; HKEY hKey; ///////// // Unload the text strings. ///////// UnloadPerfCounterTextStringsW(L"LODCTR " IASServiceName, TRUE); ////////// // Delete the PerfMon registry key. ////////// error = RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\IAS", 0, KEY_CREATE_SUB_KEY, &hKey ); if (error == NO_ERROR) { RegDeleteKey(hKey, L"Performance"); RegCloseKey(hKey); } return S_OK; } /////////////////////////////////////////////////////////////////////////////// // // FUNCTION // // DllMain // /////////////////////////////////////////////////////////////////////////////// extern "C" BOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/ ) { if (dwReason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hInstance); InitializeCriticalSectionAndSpinCount(&thePerfLock, 0x80001000); } else if (dwReason == DLL_PROCESS_DETACH) { DeleteCriticalSection(&thePerfLock); } return TRUE; }