/**************************************************************************** PROGRAM: NWPerf.c PURPOSE: Contains library routines for providing perfmon with data FUNCTIONS: *******************************************************************************/ #include #include #include #include #include #include #include "NWPerf.h" #include "prfutil.h" #ifndef QFE_BUILD #include "ntprfctr.h" #endif BOOL gbInitOK = FALSE; HANDLE hNetWareRdr ; extern NW_DATA_DEFINITION NWDataDefinition; #ifdef QFE_BUILD TCHAR PerformanceKeyName [] = TEXT("SYSTEM\\CurrentControlSet\\Services\\NWrdr\\Performance"); TCHAR FirstCounterKeyName [] = TEXT("First Counter"); TCHAR FirstHelpKeyName [] = TEXT("First Help"); #endif /**************************************************************************** FUNCTION: OpenNetWarePerformanceData Purpose: This routine also initializes the data structures used to pass data back to the registry Return: None. r****************************************************************************/ DWORD APIENTRY OpenNetWarePerformanceData( LPWSTR pInstances ) { LONG status; #ifdef QFE_BUILD HKEY hKeyPerf = 0; DWORD size; DWORD type; DWORD dwFirstCounter; DWORD dwFirstHelp; #else NT_PRODUCT_TYPE ProductType; DWORD dwFirstCounter = NWCS_CLIENT_COUNTER_INDEX ; DWORD dwFirstHelp = NWCS_CLIENT_HELP_INDEX ; #endif IO_STATUS_BLOCK IoStatusBlock; RTL_RELATIVE_NAME RelativeName; UNICODE_STRING DeviceNameU; OBJECT_ATTRIBUTES ObjectAttributes; #ifdef QFE_BUILD status = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, PerformanceKeyName, 0L, KEY_ALL_ACCESS, &hKeyPerf ); if (status != ERROR_SUCCESS) { goto OpenExitPoint; } size = sizeof (DWORD); status = RegQueryValueEx( hKeyPerf, FirstCounterKeyName, 0L, &type, (LPBYTE)&dwFirstCounter, &size); if (status != ERROR_SUCCESS) { goto OpenExitPoint; } size = sizeof (DWORD); status = RegQueryValueEx( hKeyPerf, FirstHelpKeyName, 0L, &type, (LPBYTE)&dwFirstHelp, &size ); if (status != ERROR_SUCCESS) { goto OpenExitPoint; } #endif // // NOTE: the initialization program could also retrieve // LastCounter and LastHelp if they wanted to do // bounds checking on the new number. e.g. // // counter->CounterNameTitleIndex += dwFirstCounter; // if (counter->CounterNameTitleIndex > dwLastCounter) { // LogErrorToEventLog (INDEX_OUT_OF_BOUNDS); // } NWDataDefinition.NWObjectType.ObjectNameTitleIndex += dwFirstCounter; NWDataDefinition.NWObjectType.ObjectHelpTitleIndex += dwFirstHelp; // Counters not defined in Redirector, setup the correct IDs NWDataDefinition.PacketBurstRead.CounterNameTitleIndex += dwFirstCounter; NWDataDefinition.PacketBurstRead.CounterHelpTitleIndex += dwFirstHelp; NWDataDefinition.PacketBurstReadTimeouts.CounterNameTitleIndex += dwFirstCounter; NWDataDefinition.PacketBurstReadTimeouts.CounterHelpTitleIndex += dwFirstHelp; NWDataDefinition.PacketBurstWrite.CounterNameTitleIndex += dwFirstCounter; NWDataDefinition.PacketBurstWrite.CounterHelpTitleIndex += dwFirstHelp; NWDataDefinition.PacketBurstWriteTimeouts.CounterNameTitleIndex += dwFirstCounter; NWDataDefinition.PacketBurstWriteTimeouts.CounterHelpTitleIndex += dwFirstHelp; NWDataDefinition.PacketBurstIO.CounterNameTitleIndex += dwFirstCounter; NWDataDefinition.PacketBurstIO.CounterHelpTitleIndex += dwFirstHelp; NWDataDefinition.NetWare2XConnects.CounterNameTitleIndex += dwFirstCounter; NWDataDefinition.NetWare2XConnects.CounterHelpTitleIndex += dwFirstHelp; NWDataDefinition.NetWare3XConnects.CounterNameTitleIndex += dwFirstCounter; NWDataDefinition.NetWare3XConnects.CounterHelpTitleIndex += dwFirstHelp; NWDataDefinition.NetWare4XConnects.CounterNameTitleIndex += dwFirstCounter; NWDataDefinition.NetWare4XConnects.CounterHelpTitleIndex += dwFirstHelp; #ifndef QFE_BUILD // Check for WorkStation or Server and use the gateway indexes if // currently running on Server. // If RtlGetNtProductType is not successful or ProductType is // WinNt machine, ObjectNameTitleIndex and ObjectHelpTitleIndex are set // to the correct values already. #ifdef GATEWAY_ENABLED if ( RtlGetNtProductType( &ProductType)) { if ( ProductType != NtProductWinNt ) { NWDataDefinition.NWObjectType.ObjectNameTitleIndex = NWCS_GATEWAY_COUNTER_INDEX; NWDataDefinition.NWObjectType.ObjectHelpTitleIndex = NWCS_GATEWAY_HELP_INDEX; } } #endif #endif hNetWareRdr = NULL; RtlInitUnicodeString(&DeviceNameU, DD_NWFS_DEVICE_NAME_U); RelativeName.ContainingDirectory = NULL; InitializeObjectAttributes(&ObjectAttributes, &DeviceNameU, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, NULL ); status = NtCreateFile(&hNetWareRdr, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); gbInitOK = TRUE; // ok to use this function status = ERROR_SUCCESS; // for successful exit #ifdef QFE_BUILD OpenExitPoint: if (hKeyPerf) RegCloseKey (hKeyPerf); // close key to registry #endif return ((DWORD) status); } /**************************************************************************** FUNCTION: CollectNetWarePerformanceData Purpose: This routine will return the data for the NetWare counters. Arguments:IN LPWSTR lpValueName pointer to a wide character string passed by registry. IN OUT LPVOID *lppData IN: pointer to the address of the buffer to receive the completed PerfDataBlock and subordinate structures. This routine will append its data to the buffer starting at the point referenced by *lppData. OUT: points to the first byte after the data structure added by this routine. This routine updated the value at lppdata after appending its data. IN OUT LPDWORD lpcbTotalBytes IN: the address of the DWORD that tells the size in bytes of the buffer referenced by the lppData argument OUT: the number of bytes added by this routine is written to the DWORD pointed to by this argument IN OUT LPDWORD NumObjectTypes IN: the address of the DWORD to receive the number of objects added by this routine OUT: the number of objects added by this routine is written to the DWORD pointed to by this argument Return: ERROR_MORE_DATA if buffer passed is too small to hold data any error conditions encountered are reported to the event log if event logging is enabled. ERROR_SUCCESS if success or any other error. Errors, however are also reported to the event log. ****************************************************************************/ DWORD APIENTRY CollectNetWarePerformanceData( IN LPWSTR lpValueName, IN OUT LPVOID *lppData, IN OUT LPDWORD lpcbTotalBytes, IN OUT LPDWORD lpNumObjectTypes) { ULONG SpaceNeeded; PDWORD pdwCounter; DWORD dwQueryType; PERF_COUNTER_BLOCK *pPerfCounterBlock; NW_DATA_DEFINITION *pNWDataDefinition; LONG status; NW_REDIR_STATISTICS NWRdrStatistics; LARGE_INTEGER UNALIGNED *pliCounter; IO_STATUS_BLOCK IoStatusBlock; // // before doing anything else, see if Open went OK // if (!gbInitOK) { *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_SUCCESS; // yes, this is a successful exit } // see if this is a foreign (i.e. non-NT) computer data request // dwQueryType = GetQueryType (lpValueName); if (dwQueryType == QUERY_FOREIGN) { // this routine does not service requests for data from // Non-NT computers *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_SUCCESS; } // If the caller only wanted some counter, check if we have 'em if (dwQueryType == QUERY_ITEMS){ if ( !(IsNumberInUnicodeList ( NWDataDefinition.NWObjectType.ObjectNameTitleIndex, lpValueName))) { // request received for data object not provided by this routine *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_SUCCESS; } } pNWDataDefinition = (NW_DATA_DEFINITION *) *lppData; SpaceNeeded = sizeof(NW_DATA_DEFINITION) + SIZE_OF_COUNTER_BLOCK; if ( *lpcbTotalBytes < SpaceNeeded ) { *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ((DWORD) ERROR_MORE_DATA); } // // Copy the (constant, initialized) Object Type and counter definitions // to the caller's data buffer // memmove( pNWDataDefinition, &NWDataDefinition, sizeof(NW_DATA_DEFINITION) ); // Point at the byte right after all the definitions pPerfCounterBlock = (PERF_COUNTER_BLOCK *) &pNWDataDefinition[1]; // The first DWORD should specify the size of actual data block pPerfCounterBlock->ByteLength = SIZE_OF_COUNTER_BLOCK; // Move the pointer up pdwCounter = (PDWORD) (&pPerfCounterBlock[1]); // Open the NetWare data if ( hNetWareRdr != NULL) { status = NtFsControlFile(hNetWareRdr, NULL, NULL, NULL, &IoStatusBlock, FSCTL_NWR_GET_STATISTICS, NULL, 0, &NWRdrStatistics, sizeof(NWRdrStatistics) ); } if ( hNetWareRdr != NULL && NT_SUCCESS(status) ) { pliCounter = (LARGE_INTEGER UNALIGNED * ) (&pPerfCounterBlock[1]); pliCounter->QuadPart = NWRdrStatistics.BytesReceived.QuadPart + NWRdrStatistics.BytesTransmitted.QuadPart; pdwCounter = (PDWORD) ++pliCounter; *pdwCounter = NWRdrStatistics.ReadOperations + NWRdrStatistics.WriteOperations; pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter; pliCounter->QuadPart = NWRdrStatistics.NcpsReceived.QuadPart + NWRdrStatistics.NcpsTransmitted.QuadPart; *++pliCounter = NWRdrStatistics.BytesReceived; *++pliCounter = NWRdrStatistics.NcpsReceived; *++pliCounter = NWRdrStatistics.BytesTransmitted; *++pliCounter = NWRdrStatistics.NcpsTransmitted; pdwCounter = (PDWORD) ++pliCounter; *pdwCounter = NWRdrStatistics.ReadOperations; *++pdwCounter = NWRdrStatistics.RandomReadOperations; *++pdwCounter = NWRdrStatistics.ReadNcps; *++pdwCounter = NWRdrStatistics.WriteOperations; *++pdwCounter = NWRdrStatistics.RandomWriteOperations; *++pdwCounter = NWRdrStatistics.WriteNcps; *++pdwCounter = NWRdrStatistics.Sessions; *++pdwCounter = NWRdrStatistics.Reconnects; *++pdwCounter = NWRdrStatistics.NW2xConnects; *++pdwCounter = NWRdrStatistics.NW3xConnects; *++pdwCounter = NWRdrStatistics.NW4xConnects; *++pdwCounter = NWRdrStatistics.ServerDisconnects; *++pdwCounter = NWRdrStatistics.PacketBurstReadNcps; *++pdwCounter = NWRdrStatistics.PacketBurstReadTimeouts; *++pdwCounter = NWRdrStatistics.PacketBurstWriteNcps; *++pdwCounter = NWRdrStatistics.PacketBurstWriteTimeouts; *++pdwCounter = NWRdrStatistics.PacketBurstReadNcps + NWRdrStatistics.PacketBurstWriteNcps; // // Add an extra empty DWORD to pad the buffer to an 8-byte boundary // *++pdwCounter = 0; *lppData = (LPVOID) ++pdwCounter; } else { // // Failure to access Redirector: clear counters to 0 // memset(&pPerfCounterBlock[1], 0, SIZE_OF_COUNTER_BLOCK - sizeof(pPerfCounterBlock)); pdwCounter = (PDWORD) ((PBYTE) pPerfCounterBlock + SIZE_OF_COUNTER_BLOCK); *lppData = (LPVOID) pdwCounter; } // We sent data for only one Object. (Remember not to confuse this // with counters. Even if more counters are added, the number of object // is still only one. However, this does not mean more objects cannot // be added *lpNumObjectTypes = 1; // Fill in the number of bytes we copied - incl. the definitions and the // counter data. *lpcbTotalBytes = (DWORD) ((PBYTE) pdwCounter - (PBYTE) pNWDataDefinition); // // Make sure the output buffer is 8-byte aligned // ASSERT((*lpcbTotalBytes & 0x7) == 0); return ERROR_SUCCESS; } /**************************************************************************** FUNCTION: CloseNetWarePerformanceData Purpose: This routine closes the open handles to NetWare performance counters Return: ERROR_SUCCESS ****************************************************************************/ DWORD APIENTRY CloseNetWarePerformanceData( ) { if ( hNetWareRdr ) { NtClose( hNetWareRdr ); hNetWareRdr = NULL; } return ERROR_SUCCESS; }