415 lines
14 KiB
C
415 lines
14 KiB
C
/****************************************************************************
|
|
|
|
PROGRAM: NWPerf.c
|
|
|
|
PURPOSE: Contains library routines for providing perfmon with data
|
|
|
|
FUNCTIONS:
|
|
*******************************************************************************/
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <winperf.h>
|
|
#include <ntddnwfs.h>
|
|
#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;
|
|
|
|
}
|
|
|