/*
 *  HrStorageEntry.c v0.10
 *  Generated in conjunction with Management Factory scripts:
 *      script version: SNMPv1, 0.16, Apr 25, 1996
 *      project:        D:\TEMP\EXAMPLE\HOSTMIB
 ****************************************************************************
 *                                                                          *
 *      (C) Copyright 1995 DIGITAL EQUIPMENT CORPORATION                    *
 *                                                                          *
 *      This  software  is  an  unpublished work protected under the        *
 *      the copyright laws of the  United  States  of  America,  all        *
 *      rights reserved.                                                    *
 *                                                                          *
 *      In the event this software is licensed for use by the United        *
 *      States Government, all use, duplication or disclosure by the        *
 *      United States Government is subject to restrictions  as  set        *
 *      forth in either subparagraph  (c)(1)(ii)  of the  Rights  in        *
 *      Technical  Data  And  Computer  Software  Clause  at   DFARS        *
 *      252.227-7013, or the Commercial Computer Software Restricted        *
 *      Rights Clause at FAR 52.221-19, whichever is applicable.            *
 *                                                                          *
 ****************************************************************************
 *
 *  Facility:
 *
 *    Windows NT SNMP Extension Agent
 *
 *  Abstract:
 *
 *    This module contains the code for dealing with the get, set, and
 *    instance name routines for the HrStorageEntry.
 *    Actual instrumentation code is supplied by the developer.
 *
 *  Functions:
 *
 *    A get and set routine for each attribute in the class.
 *
 *    The routines for instances within the class, plus the cache
 *    initialization function "Gen_Hrstorage_Cache()".
 *
 *  Author:
 *
 *	D. D. Burns @ Webenable Inc
 *
 *  Revision History:
 *
 *    V1.00 - 04/17/97  D. D. Burns     Genned: Thu Nov 07 16:40:22 1996
 *    V1.01 - 05/15/97  D. D. Burns     Move Disk Label/Size acquisitions
 *                                       to cache from real-time
 *    V1.02 - 06/18/97  D. D. Burns     Add spt to scan event log for
 *                                       allocation failures
 *
 */

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <malloc.h>

#include <snmp.h>

#include "mib.h"
#include "smint.h"
#include "hostmsmi.h"
#include "user.h"         /* Developer supplied include file */
#include "HMCACHE.H"      /* Cache-related definitions */
#include "string.h"       /* For string manipulation in "Gen_Hrstorage_Cache"*/
#include "stdio.h"        /* For sprintf */


/*
|==============================================================================
| Function prototypes for this module.
|
*/
/* ScanLog_Failures - Scan Event Log for Storage Allocation Failures */
static UINT
ScanLog_Failures(
                 CHAR   *device
                 );


#if defined(CACHE_DUMP)

/* debug_print_hrstorage - Prints a Row from Hrstorage sub-table */
static void
debug_print_hrstorage(
                      CACHEROW     *row  /* Row in hrstorage table */
                      );
#endif

/*
|==============================================================================
| Cache Refresh Time
|
| The cache for the hrStorage and hrFSTable tables are refreshed automatically
| when a request arrives at the hrStorage --AND-- the cache is older than 
| CACHE_MAX_AGE in seconds.
|
*/
static
LARGE_INTEGER   cache_time;   // 100ns Timestamp of cache (when last refreshed)

#define CACHE_MAX_AGE 120     // Maximum age in seconds

/*
|==============================================================================
| Create the list-head for the HrStorage table cache.
|
| This list-head is globally accessible so the logic that loads hrFSTable
| can scan this cache for matches (among other reasons).
|
| (This macro is defined in "HMCACHE.H").
*/
CACHEHEAD_INSTANCE(hrStorage_cache, debug_print_hrstorage);


/*
|==============================================================================
| Local string for this kind of "storage".
*/
#define VM            "Virtual Memory"


/*
 * ============================================================================
 *  GetHrStorageIndex
 *    A unique value for each logical storage are contained by the host.
 *
 *    Gets the value for HrStorageIndex.
 *
 *  Arguments:
 *
 *    outvalue                   address to return variable value
 *    accesss                    Reserved for future security use
 *    instance                   address of instance name as ordered native
 *                               data type(s)
 *
 *  Return Codes:
 *
 *    Standard PDU error codes.
 *
 *    SNMP_ERRORSTATUS_NOERROR    Successful get
 *    SNMP_ERRORSTATUS_GENERR     Catch-all failure code
 * mibtget.c v0.10
 *
 | =============== From WebEnable Design Spec Rev 3 04/11/97==================
 | hrStorageIndex
 |
 |  ACCESS         SYNTAX
 |  read-only      INTEGER (1..2147483647)
 |
 | "A unique value for each logical storage area contained by the host."
 |
 | DISCUSSION:
 |
 | The value of this attribute is always the number of drives reported by
 | "GetLogicalDrives" (excepting network drives) plus one (for reporting on
 | "Virtual Memory").
 |
 |============================================================================
 | 1.3.6.1.2.1.25.2.3.1.1.<instance>
 |                | | | |
 |                | | | *hrStorageIndex
 |                | | *hrStorageEntry
 |                | *-hrStorageTable
 |                *-hrStorage
 */

UINT
GetHrStorageIndex(
        OUT Integer *outvalue ,
        IN Access_Credential *access ,
        IN InstanceName *instance )

{
ULONG           index;          /* As fetched from instance structure */
CACHEROW        *row;           /* Row entry fetched from cache       */

/*
| Grab the instance information
*/
index = GET_INSTANCE(0);

/*
| Use it to find the right entry in the cache
*/
if ((row = FindTableRow(index, &hrStorage_cache)) == NULL) {
    return SNMP_ERRORSTATUS_GENERR;
    }

/*
| Return the "hrStorageIndex" value from this entry
*/
*outvalue = row->attrib_list[HRST_INDEX].u.unumber_value;

return SNMP_ERRORSTATUS_NOERROR ;

} /* end of GetHrStorageIndex() */


/*
 * ============================================================================
 *  GetHrStorageType
 *    The type of strage represented by this entry.
 *
 *    Gets the value for HrStorageType.
 *
 *  Arguments:
 *
 *    outvalue                   address to return variable value
 *    accesss                    Reserved for future security use
 *    instance                   address of instance name as ordered native
 *                               data type(s)
 *
 *  Return Codes:
 *
 *    Standard PDU error codes.
 *
 *    SNMP_ERRORSTATUS_NOERROR    Successful get
 *    SNMP_ERRORSTATUS_GENERR     Catch-all failure code
 * mibtget.c v0.10
 *
 | =============== From WebEnable Design Spec Rev 3 04/11/97==================
 | hrStorageType
 |
 |  ACCESS         SYNTAX
 |  read-only      OBJECT IDENTIFIER
 |
 | "The type of storage represented by this entry."
 |
 |    -- Registration for some storage types, for use with hrStorageType
 |
 |    hrStorageOther          OBJECT IDENTIFIER ::= { hrStorageTypes 1 }
 |    hrStorageRam            OBJECT IDENTIFIER ::= { hrStorageTypes 2 }
 |    -- hrStorageVirtualMemory is temporary storage of swapped
 |    -- or paged memory
 |    hrStorageVirtualMemory  OBJECT IDENTIFIER ::= { hrStorageTypes 3 }
 |    hrStorageFixedDisk      OBJECT IDENTIFIER ::= { hrStorageTypes 4 }
 |    hrStorageRemovableDisk  OBJECT IDENTIFIER ::= { hrStorageTypes 5 }
 |    hrStorageFloppyDisk     OBJECT IDENTIFIER ::= { hrStorageTypes 6 }
 |    hrStorageCompactDisc    OBJECT IDENTIFIER ::= { hrStorageTypes 7 }
 |    hrStorageRamDisk        OBJECT IDENTIFIER ::= { hrStorageTypes 8 }
 |
 | DISCUSSION:
 |
 | The value returned for this attribute is determined by indications from
 | "GetDriveType" for disks.  For the "Virtual Memory" entry, the OID for
 | "hrStorageVirtualMemory" is returned.
 |
 |============================================================================
 | 1.3.6.1.2.1.25.2.3.1.2.<instance>
 |                | | | |
 |                | | | *hrStorageType
 |                | | *hrStorageEntry
 |                | *-hrStorageTable
 |                *-hrStorage
 */

UINT
GetHrStorageType(
        OUT ObjectIdentifier *outvalue ,
        IN Access_Credential *access ,
        IN InstanceName *instance )

{
ULONG           index;          /* As fetched from instance structure */
CACHEROW        *row;           /* Row entry fetched from cache       */


/*
| Grab the instance information
*/
index = GET_INSTANCE(0);

/*
| Use it to find the right entry in the cache
*/
if ((row = FindTableRow(index, &hrStorage_cache)) == NULL) {
    return SNMP_ERRORSTATUS_GENERR;
    }

/*
| By convention with the cache-building function "Gen_Hrstorage_Cache()",
| the cached value is the right-most arc we must return as the value.
|
| Hence whatever cache entry we retrieve, we tack the number retrieved
| from the cache for this attribute onto { hrStorageTypes ... }.
*/
if ( (outvalue->ids = SNMP_malloc(10 * sizeof( UINT ))) == NULL) {
    return SNMP_ERRORSTATUS_GENERR;
    }
outvalue->idLength = 10;


/*
| Load in the full hrStorageType OID:
|
| 1.3.6.1.2.1.25.2.1.n
|                | | |
|                | | *-Type indicator
|                | *-hrStorageTypes (OIDs specifying storage types)
|                *-hrStorage
|
*/
outvalue->ids[0] = 1;
outvalue->ids[1] = 3;
outvalue->ids[2] = 6;
outvalue->ids[3] = 1;
outvalue->ids[4] = 2;
outvalue->ids[5] = 1;
outvalue->ids[6] = 25;
outvalue->ids[7] = 2;
outvalue->ids[8] = 1;

/* Cached Type indicator */
outvalue->ids[9] = row->attrib_list[HRST_TYPE].u.unumber_value;

return SNMP_ERRORSTATUS_NOERROR ;

} /* end of GetHrStorageType() */


/*
 *  GetHrStorageDesc
 *    A description of the type and instance of the storage described by this
 *    entry.
 *
 *    Gets the value for HrStorageDesc.
 *
 *  Arguments:
 *
 *    outvalue                   address to return variable value
 *    accesss                    Reserved for future security use
 *    instance                   address of instance name as ordered native
 *                               data type(s)
 *
 *  Return Codes:
 *
 *    Standard PDU error codes.
 *
 *    SNMP_ERRORSTATUS_NOERROR    Successful get
 *    SNMP_ERRORSTATUS_GENERR     Catch-all failure code
 * mibtget.c v0.10
 *
 | =============== From WebEnable Design Spec Rev 3 04/11/97==================
 | hrStorageDescr
 |
 |  ACCESS         SYNTAX
 |  read-only      DisplayString
 |
 | "A description of the type and instance of the storage described by this
 | entry."
 |
 | DISCUSSION:
 |
 | For the Virtual Memory entry, the string "Virtual Memory" is returned.
 |
 | For disks, a string composed of:
 |         + the logical drive letter followed by
 |         + the Volume Identification (for drives containing a volume)
 |           in double quotes
 |         + the Volume Serial Number
 |
 | For instance, the value of this variable for drive C might be
 |                          C: Label="Main Disk"  Serial #=0030-34FE
 |
 | For speed, the label acquisition is done at cache-build time, and
 | as a consequence the removable drives are sampled only once.
 | ===========================================================================
 | 1.3.6.1.2.1.25.2.3.1.3.<instance>
 |                | | | |
 |                | | | *hrStorageDescr
 |                | | *hrStorageEntry
 |                | *-hrStorageTable
 |                *-hrStorage
 */

UINT
GetHrStorageDesc(
        OUT Simple_DisplayString *outvalue ,
        IN Access_Credential *access ,
        IN InstanceName *instance )

{
ULONG           index;          /* As fetched from instance structure     */
CACHEROW       *row;            /* Row entry fetched from cache           */


/*
| Grab the instance information
*/
index = GET_INSTANCE(0);

/*
| Use it to find the right entry in the cache
*/
if ((row = FindTableRow(index, &hrStorage_cache)) == NULL) {
    return SNMP_ERRORSTATUS_GENERR;
    }

/* Return the description that was computed at cache-build time */
outvalue->length = strlen(row->attrib_list[HRST_DESCR].u.string_value);
outvalue->string = row->attrib_list[HRST_DESCR].u.string_value;

return SNMP_ERRORSTATUS_NOERROR ;

} /* end of GetHrStorageDesc() */


/*
 *  GetHrStorageAllocationUnits
 *    The size, in bytes, of the data objects allocated from this pool.  If
 *    this entry is monitoring sectors, blocks, buffers, or pack
 *
 *    Gets the value for HrStorageAllocationUnits.
 *
 *  Arguments:
 *
 *    outvalue                   address to return variable value
 *    accesss                    Reserved for future security use
 *    instance                   address of instance name as ordered native
 *                               data type(s)
 *
 *  Return Codes:
 *
 *    Standard PDU error codes.
 *
 *    SNMP_ERRORSTATUS_NOERROR    Successful get
 *    SNMP_ERRORSTATUS_GENERR     Catch-all failure code
 * mibtget.c v0.10
 *
 | =============== From WebEnable Design Spec Rev 3 04/11/97==================
 | hrStorageAllocationUnits
 |
 |  ACCESS         SYNTAX
 |  read-only      INTEGER (1..2147483647)
 |
 | "The size, in bytes, of the data objects allocated from this pool.  If this
 | entry is monitoring sectors, blocks, buffers, or packets, for example, this
 | number will commonly be greater than one.  Otherwise this number will
 | typically be one."
 |
 | DISCUSSION:
 |
 | For Virtual Memory, the value returned is that provided as
 | "AllocationGranularity" after a call to "GetSystemInfo".
 |
 | For disks, the size of the "hrStorageAllocationUnits" value is computed as
 | the quantity "BytesPerSector * SectorsPerCluster" as returned by Win32 API
 | function "GetDiskFreeSpace".
 |
 | =============================================================================
 | 1.3.6.1.2.1.25.2.3.1.4.<instance>
 |                | | | |
 |                | | | *hrStorageAllocationUnits
 |                | | *hrStorageEntry
 |                | *-hrStorageTable
 |                *-hrStorage
 */

UINT
GetHrStorageAllocationUnits(
        OUT Integer *outvalue ,
        IN Access_Credential *access ,
        IN InstanceName *instance )

{
ULONG           index;          /* As fetched from instance structure     */
CACHEROW       *row;            /* Row entry fetched from cache           */


/*
| Grab the instance information
*/
index = GET_INSTANCE(0);

/*
| Use it to find the right entry in the cache
*/
if ((row = FindTableRow(index, &hrStorage_cache)) == NULL) {
    return SNMP_ERRORSTATUS_GENERR;
    }

*outvalue = row->attrib_list[HRST_ALLOC].u.number_value;
return ( SNMP_ERRORSTATUS_NOERROR );

} /* end of GetHrStorageAllocationUnits() */


/*
 *  GetHrStorageSize
 *    The size of the storage represented by this entry, in units of
 *    hrStorageAllocationUnits.
 *
 *    Gets the value for HrStorageSize.
 *
 *  Arguments:
 *
 *    outvalue                   address to return variable value
 *    accesss                    Reserved for future security use
 *    instance                   address of instance name as ordered native
 *                               data type(s)
 *
 *  Return Codes:
 *
 *    Standard PDU error codes.
 *
 *    SNMP_ERRORSTATUS_NOERROR    Successful get
 *    SNMP_ERRORSTATUS_GENERR     Catch-all failure code
 * mibtget.c v0.10
 *
 | =============== From WebEnable Design Spec Rev 3 04/11/97==================
 | hrStorageSize
 |
 |  ACCESS         SYNTAX
 |  read-write     INTEGER (0..2147483647)
 |
 | "The size of the storage represented by this entry, in units of
 | hrStorageAllocationUnits."
 |
 | DISCUSSION:
 |
 | For Virtual Memory, the value returned is computed as "TotalPageFile" (as
 | returned by "GlobalMemoryStatus") divided by "AllocationGranularity" from
 | "GetSystemInfo".
 |
 | For disks, the "hrStorageSize" value is the value of "TotalNumberOfClusters"
 | as returned by Win32 API function "GetDiskFreeSpace".
 |
 | <POA-4> This variable is marked as ACCESS="read-write".  It is unclear to me
 | what effect can be expected from a SET operation on this variable.  I propose
 | making a SET operation have no effect.
 |
 | RESOLVED >>>>>>>
 | <POA-4> Leaving this read-only is fine.
 | RESOLVED >>>>>>>
 |
 | =============================================================================
 | 1.3.6.1.2.1.25.2.3.1.5.<instance>
 |                | | | |
 |                | | | *hrStorageSize
 |                | | *hrStorageEntry
 |                | *-hrStorageTable
 |                *-hrStorage
 */

UINT
GetHrStorageSize(
        OUT Integer *outvalue ,
        IN Access_Credential *access ,
        IN InstanceName *instance )

{
ULONG           index;          /* As fetched from instance structure     */
CACHEROW       *row;            /* Row entry fetched from cache           */


/*
| Grab the instance information
*/
index = GET_INSTANCE(0);

/*
| Use it to find the right entry in the cache
*/
if ((row = FindTableRow(index, &hrStorage_cache)) == NULL) {
    return SNMP_ERRORSTATUS_GENERR;
    }

*outvalue = row->attrib_list[HRST_SIZE].u.number_value;
return ( SNMP_ERRORSTATUS_NOERROR )  ;

} /* end of GetHrStorageSize() */


/*
 *  SetHrStorageSize
 *    The size of the storage represented by this entry, in units of
 *    hrStorageAllocationUnits.
 *
 *    Sets the HrStorageSize value.
 *
 *  Arguments:
 *
 *    invalue                    address of value to set the variable
 *    outvalue                   address to return the set variable value
 *    access                     Reserved for future security use
 *    instance                   address of instance name as ordered native
 *                               data type(s)
 *
 *  Return Codes:
 *
 *    Standard PDU error codes.
 *
 *    SNMP_ERRORSTATUS_NOERROR    Successful get
 *    SNMP_ERRORSTATUS_BADVALUE   Set value not in range
 *    SNMP_ERRORSTATUS_GENERR     Catch-all failure code
 * mibtset.ntc v0.10
 *
 | =============================================================================
 | 1.3.6.1.2.1.25.2.3.1.5.<instance>
 |                | | | |
 |                | | | *hrStorageSize
 |                | | *hrStorageEntry
 |                | *-hrStorageTable
 |                *-hrStorage
 */

UINT
SetHrStorageSize(
        IN Integer *invalue ,
        OUT Integer *outvalue ,
        IN Access_Credential *access ,
        IN InstanceName *instance )

{

#if 0
//debug debug debug debug debug
static int x=0;
if (x==0) {
    /*
    | If it is invoked here, the invocation
    | of it in mib.c must be removed.
    */
    Gen_HrDevice_Cache();
    x =1;
    }
//debug debug debug debug debug
#endif

    return SNMP_ERRORSTATUS_NOSUCHNAME ;

} /* end of SetHrStorageSize() */


/*
 *  GetHrStorageUsed
 *    The amount of the storage represented by this entry that is allocated,
 *    in units of hrStorageAllocationUnits.
 *
 *    Gets the value for HrStorageUsed.
 *
 *  Arguments:
 *
 *    outvalue                   address to return variable value
 *    accesss                    Reserved for future security use
 *    instance                   address of instance name as ordered native
 *                               data type(s)
 *
 *  Return Codes:
 *
 *    Standard PDU error codes.
 *
 *    SNMP_ERRORSTATUS_NOERROR    Successful get
 *    SNMP_ERRORSTATUS_GENERR     Catch-all failure code
 * mibtget.c v0.10
 *
 | =============== From WebEnable Design Spec Rev 3 04/11/97==================
 | hrStorageUsed
 |
 |  ACCESS         SYNTAX
 |  read-only      INTEGER (0..2147483647)
 |
 | "The amount of the storage represented by this entry that is allocated, in
 | units of hrStorageAllocationUnits."
 |
 | DISCUSSION:
 |
 | For Virtual Memory, the value returned is computed as the quantity
 | "TotalPageFile" less "AvailPageFile" (as returned by "GlobalMemoryStatus")
 | divided by "AllocationGranularity" (as returned by "GetSystemInfo".
 |
 | For disks, the "hrStorageUsed" value is computed as the quantity
 | "TotalNumberOfClusters - NumberOfFreeClusters" as returned by Win32 API
 | function "GetDiskFreeSpace".
 |
 | ===========================================================================
 | 1.3.6.1.2.1.25.2.3.1.6.<instance>
 |                | | | |
 |                | | | *hrStorageUsed
 |                | | *hrStorageEntry
 |                | *-hrStorageTable
 |                *-hrStorage
 */

UINT
GetHrStorageUsed(
        OUT Integer *outvalue ,
        IN Access_Credential *access ,
        IN InstanceName *instance )

{

ULONG           index;          /* As fetched from instance structure     */
CACHEROW       *row;            /* Row entry fetched from cache           */



/*
| Grab the instance information
*/
index = GET_INSTANCE(0);

/*
| Use it to find the right entry in the cache
*/
if ((row = FindTableRow(index, &hrStorage_cache)) == NULL) {
    return SNMP_ERRORSTATUS_GENERR;
    }

*outvalue = row->attrib_list[HRST_USED].u.number_value;
return ( SNMP_ERRORSTATUS_NOERROR )  ;

} /* end of GetHrStorageUsed() */


/*
 *  GetHrStorageAllocationFailures
 *    The number of requests for storage represented by this entry that could
 *    not be honored due to not enough storage.  It should be
 *
 *    Gets the value for HrStorageAllocationFailures.
 *
 *  Arguments:
 *
 *    outvalue                   address to return variable value
 *    accesss                    Reserved for future security use
 *    instance                   address of instance name as ordered native
 *                               data type(s)
 *
 *  Return Codes:
 *
 *    Standard PDU error codes.
 *
 *    SNMP_ERRORSTATUS_NOERROR    Successful get
 *    SNMP_ERRORSTATUS_GENERR     Catch-all failure code
 * mibtget.c v0.10
 *
 |=============================================================================
 |hrStorageAllocationFailures
 |
 | ACCESS         SYNTAX
 | read-only      Counter
 |
 |"The number of requests for storage represented by this entry that could not
 |be honored due to not enough storage.  It should be noted that as this object
 |has a SYNTAX of Counter, that it does not have a defined initial value.
 |However, it is recommended that this object be initialized to zero."
 |
 |DISCUSSION:
 |
 |<POA-5> This value as very problematical for both Virtual Memory and Disk
 |storage.  There appear to be no Win32 APIs that report allocation failures for
 |either virtual memory or disk storage.  I presume there may be performance
 |monitoring counters stored in the registry, however I'm not able to find the
 |documentation that describes where such information might be stored.  For
 |disks, we need to be able to map whatever counters are stored into logical
 |drives (as that is how this table is organized).
 |
 |RESOLVED >>>>>>>
 |<POA-5> You would have to scan to event log looking for the out of virtual
 |memory and out of disk space events and count them.
 |RESOLVED >>>>>>>
 |=============================================================================
 | 1.3.6.1.2.1.25.2.3.1.7.<instance>
 |                | | | |
 |                | | | *hrStorageAllocationFailures
 |                | | *hrStorageEntry
 |                | *-hrStorageTable
 |                *-hrStorage
 */

UINT
GetHrStorageAllocationFailures(
        OUT Counter *outvalue ,
        IN Access_Credential *access ,
        IN InstanceName *instance )

{
CHAR            device[3];      /* Device Name build-buffer             */
ULONG           index;          /* As fetched from instance structure   */
CACHEROW       *row;            /* Row entry fetched from cache         */


/*
| Grab the instance information
*/
index = GET_INSTANCE(0);

/*
| Use it to find the right entry in the cache
*/
if ((row = FindTableRow(index, &hrStorage_cache)) == NULL) {
    return SNMP_ERRORSTATUS_GENERR;
    }

/*
| Load the "device[]" array with two characters, either something like "C:"
| (indicating we're interested in allocation failures for "C:") or "VM"
| if we want VM storage allocation failures.... from the HRST_DESCR string.
*/
if (strcmp(row->attrib_list[HRST_DESCR].u.string_value, VM) == 0) {
    /* Storage is really "Virtual Memory" */
    device[0] = 'V';
    device[1] = 'M';
    }
else {
    device[0] = row->attrib_list[HRST_DESCR].u.string_value[0];
    device[1] = row->attrib_list[HRST_DESCR].u.string_value[1];
    }
device[2] = '\0';       /* Null-terminate */

/* Riffle thru the Event Log looking for this device's failures */
*outvalue = ScanLog_Failures( device );

return SNMP_ERRORSTATUS_NOERROR ;

} /* end of GetHrStorageAllocationFailures() */


/*
 *  HrStorageEntryFindInstance
 *
 *     This routine is used to verify that the specified instance is
 *     valid.
 *
 *  Arguments:
 *
 *     FullOid                 Address for the full oid - group, variable,
 *                             and instance information
 *     instance                Address for instance specification as an oid
 *
 *  Return Codes:
 *
 *     SNMP_ERRORSTATUS_NOERROR     Instance found and valid
 *     SNMP_ERRORSTATUS_NOSUCHNAME  Invalid instance
 *
 */

UINT
HrStorageEntryFindInstance( IN ObjectIdentifier *FullOid ,
                       IN OUT ObjectIdentifier *instance )
{
    UINT tmp_instance ;

    //
    //  Developer instrumentation code to find appropriate instance goes here.
    //  For non-tables, it is not necessary to modify this routine.  However, if
    //  there is any context that needs to be set, it can be done here.
    //

    if ( FullOid->idLength <= HRSTORAGEENTRY_VAR_INDEX )
	// No instance was specified
	return SNMP_ERRORSTATUS_NOSUCHNAME ;
    else  if ( FullOid->idLength != HRSTORAGEENTRY_VAR_INDEX + 1 )
	// Instance length is more than 1
	return SNMP_ERRORSTATUS_NOSUCHNAME ;
    else
	// The only valid instance for a non-table are instance 0.  If this
	// is a non-table, the following code validates the instances.  If this
	// is a table, developer modification is necessary below.

	tmp_instance = FullOid->ids[ HRSTORAGEENTRY_VAR_INDEX ] ;

        /*
        | Check for age-out and possibly refresh the entire cache for the
        | hrStorage table and hrFSTable before we check to see if the 
        | instance is there.
        */
        if (hrStorageCache_hrFSTableCache_Refresh() == FALSE) {
            return SNMP_ERRORSTATUS_GENERR;
            }

        /*
        | For hrStorage, the instance arc(s) is a single arc, and it must
        | correctly select an entry in the hrStorageTable cache.
        | Check that here.
        */
	if ( FindTableRow(tmp_instance, &hrStorage_cache) == NULL ) {
	    return SNMP_ERRORSTATUS_NOSUCHNAME ;
            }
	else
	{
	    // the instance is valid.  Create the instance portion of the OID
	    // to be returned from this call.
	    instance->ids[ 0 ] = tmp_instance ;
	    instance->idLength = 1 ;
	}

    return SNMP_ERRORSTATUS_NOERROR ;

} /* end of HrStorageEntryFindInstance() */



/*
 *  HrStorageEntryFindNextInstance
 *
 *     This routine is called to get the next instance.  If no instance
 *     was passed than return the first instance (1).
 *
 *  Arguments:
 *
 *     FullOid                 Address for the full oid - group, variable,
 *                             and instance information
 *     instance                Address for instance specification as an oid
 *
 *  Return Codes:
 *
 *     SNMP_ERRORSTATUS_NOERROR     Instance found and valid
 *     SNMP_ERRORSTATUS_NOSUCHNAME  Invalid instance
 *
 */

UINT
HrStorageEntryFindNextInstance( IN ObjectIdentifier *FullOid ,
                           IN OUT ObjectIdentifier *instance )
{
    //  Developer supplied code to find the next instance of class goes here.
    //
    //  The purpose of this function is to indicate to the rest of the system
    //  what the exact OID of the "next instance" is GIVEN THAT:
    //
    //          a) The "FullOid" passed in will have enough arcs to specify
    //             both the TABLE and the ATTRIBUTE in the table
    //
    //          b) The "instance" OID array is always big enough to have
    //             as many arcs as needed loaded into it by this function
    //             to specify (exactly) the "next instance"
    //
    //
    //  If this is a class with cardinality 1, no modification of this routine
    //  is necessary unless additional context needs to be set.
    //
    //  If the FullOid is so short that it does not specify an instance,
    //  then the only instance of the class should be returned.  If this is a
    //  table, the first row of the table is returned.  To do these things,
    //  set the "instance" OID to just the right arcs such that when
    //  concatenated onto the FullOid, the concatenation exactly specifies
    //  the first instance of this attribute in the table.
    //
    //  If an instance is specified and this is a non-table class, then
    //  NOSUCHNAME is returned so that correct MIB rollover processing occurs.
    //
    //  If this is a table, then the next instance is the one following the
    //  current instance.
    //
    //  If there are no more instances in the table, return NOSUCHNAME.
    //

    CACHEROW        *row;
    ULONG           tmp_instance;


    if ( FullOid->idLength <= HRSTORAGEENTRY_VAR_INDEX )
    {
        /*
        | Too short: must return the instance arc that selects the first
        |            entry in the table if there is one.
        */
        tmp_instance = 0;
    }
    else {
        /*
        | There is at least one instance arc.  Even if it is the only arc
        | we use it as the "index" in a request for the "NEXT" one.
        */
        tmp_instance = FullOid->ids[ HRSTORAGEENTRY_VAR_INDEX ] ;
        }

    /*
    | Check for age-out and possibly refresh the entire cache for the
    | hrStorage table and hrFSTable before we check to see if the 
    | instance is there.
    */
    if (hrStorageCache_hrFSTableCache_Refresh() == FALSE) {
        return SNMP_ERRORSTATUS_GENERR;
        }

    /* Now go off and try to find the next instance in the table */
    if ((row = FindNextTableRow(tmp_instance, &hrStorage_cache)) == NULL) {
        return SNMP_ERRORSTATUS_NOSUCHNAME ;
        }

    instance->ids[ 0 ] = row->index ;
    instance->idLength = 1 ;

    return SNMP_ERRORSTATUS_NOERROR ;

} /* end of HrStorageEntryFindNextInstance() */



/*
 *  HrStorageEntryConvertInstance
 *
 *     This routine is used to convert the object id specification of an
 *     instance into an ordered native representation.  The object id format
 *     is that object identifier that is returned from the Find Instance
 *     or Find Next Instance routines.  It is NOT the full object identifier
 *     that contains the group and variable object ids as well.  The native
 *     representation is an argc/argv-like structure that contains the
 *     ordered variables that define the instance.  This is specified by
 *     the MIB's INDEX clause.  See RFC 1212 for information about the INDEX
 *     clause.
 *
 *
 *  Arguments:
 *
 *     oid_spec                Address of the object id instance specification
 *     native_spec             Address to return the ordered native instance
 *                             specification
 *
 *  Return Codes:
 *
 *     SUCCESS                 Conversion complete successfully
 *     FAILURE                 Unable to convert object id into native format
 *
 */

UINT
HrStorageEntryConvertInstance( IN ObjectIdentifier *oid_spec ,
                          IN OUT InstanceName *native_spec )
{
static char    *array;  /* The address of this (char *) is passed back     */
                        /* as though it were an array of length 1 of these */
                        /* types.                                          */

static ULONG    inst;   /* The address of this ULONG is passed back  */
                        /* (Obviously, no "free()" action is needed) */

    /* We only expect the one arc in "oid_spec" */
    inst = oid_spec->ids[0];
    array = (char *) &inst;

    native_spec->count = 1;
    native_spec->array = &array;
    return SUCCESS ;

} /* end of HrStorageEntryConvertInstance() */




/*
 *  HrStorageEntryFreeInstance
 *
 *     This routine is used to free an ordered native representation of an
 *     instance name.
 *
 *  Arguments:
 *
 *     instance                Address to return the ordered native instance
 *                             specification
 *
 *  Return Codes:
 *
 *
 */

void
HrStorageEntryFreeInstance( IN OUT InstanceName *instance )
{

  /* No action needed for hrStorageTable */
} /* end of HrStorageEntryFreeInstance() */

/*
| End of Generated Code
*/


/* hrStorageCache_hrFSTableCache_Refresh Cache Refresh-Check Routine */
/* hrStorageCache_hrFSTableCache_Refresh Cache Refresh-Check Routine */
/* hrStorageCache_hrFSTableCache_Refresh Cache Refresh-Check Routine */

BOOL
hrStorageCache_hrFSTableCache_Refresh(
                     void
                     )

/*
|  EXPLICIT INPUTS:
|
|       None.
|
|  IMPLICIT INPUTS: 
|
|       The "hrStorage_cache" CACHEHEAD structure and the time when
|       it was last refreshed in module-local cell "cache_time".
|       The "hrFSTable_cache" CACHEHEAD structure which depends on
|       the hrStorage_cache CACHEHEAD structure.
|
|  OUTPUTS:
|
|     On Success/Failure:
|       The function returns TRUE.  Only if the cache-time has aged-out
|       is the cache actually rebuilt.
|
|     On any Failure:
|       If during a rebuild there is an error, this function returns FALSE.
|       The state of the cache is indeterminate.
|
|  THE BIG PICTURE:
|
|       This function is invoked before any reference is made to any SNMP
|       variable in the hrStorage table.  It checks to see
|       if the cache needs to be rebuilt based on the last time it was built.
|       Since the hrFSTable_cache depends on the hrStorage table,
|       hrFSTable_cache will be rebuilt whenever hrStorage table is rebuilt.
|
|       The calls to this function are strategically located in the
|       "FindInstance" and "FindNextInstance" functions in "HRSTOENT.C"
|       (this module).
|
|  OTHER THINGS TO KNOW:
|
*/
{
LARGE_INTEGER   now_time;       /* Current System time in 100 ns ticks */


/* Get the current time in 100 ns ticks*/
NtQuerySystemTime (&now_time);

/* If the cache is older than the maximum allowed time (in ticks) . . .  */
if ( (now_time.QuadPart - cache_time.QuadPart) > (CACHE_MAX_AGE * 10000000) )
{
    if (Gen_Hrstorage_Cache())
    {
        // hrFSStorageIndex attribute of hrFSTable depends on the 
        // the index of the hrStorageEntry 
        return (Gen_HrFSTable_Cache());
    }
    else
        return FALSE;
}

return ( TRUE );        /* No Error (because no refresh) */
}

/* Gen_Hrstorage_Cache - Generate a initial cache for HrStorage Table */
/* Gen_Hrstorage_Cache - Generate a initial cache for HrStorage Table */
/* Gen_Hrstorage_Cache - Generate a initial cache for HrStorage Table */

BOOL
Gen_Hrstorage_Cache(
                    void
                    )

/*
|  EXPLICIT INPUTS:
|
|       None.
|
|  IMPLICIT INPUTS:
|
|       The module-local head of the cache for the HrStorage table,
|       "hrStorage_cache".
|
|  OUTPUTS:
|
|     On Success:
|       Function returns TRUE indicating that the cache has been fully
|       populated with all "static" cache-able values.
|
|     On any Failure:
|       Function returns FALSE (indicating "not enough storage").
|
|  THE BIG PICTURE:
|
|       At subagent startup time, the cache for each table in the MIB is
|       populated with rows for each row in the table.  This function is
|       invoked by the start-up code in "UserMibInit()" ("MIB.C") to
|       populate the cache for the HrStorage table.
|
|  OTHER THINGS TO KNOW:
|
|       There is one of these function for every table that has a cache.
|       Each is found in the respective source file.
|
|=============== From WebEnable Design Spec Rev 3 04/11/97==================
|  DISCUSSION:
|
|  Since this table is meant for diagnosing "out-of-storage" situations and
|  given that the information is meant to be regarded from the point of view
|  of an application, we simply report every instance of what would appear
|  on the "Drive Bar" of the File Manager (excepting network drives) plus
|  one more table entry to reflect information on "Virtual Memory".
|
|  To this end, a combination of Win32 API functions "GetLogicalDrives",
|  "GetVolumeInformation", "GetDriveType" and "GetDiskFreeSpace" are used to
|  acquire the information for the SNMP attributes in this table.
|
|  For reporting on "Virtual Memory", functions "GlobalMemoryStatus" and
|  "GetSystemInfo" are invoked.
|============================================================================
| 1.3.6.1.2.1.25.2.1....
|                | |
|                | *-hrStorageTypes (OIDs specifying storage types)
|                *-hrStorage
|
| 1.3.6.1.2.1.25.2.2....
|                | |
|                | *-hrMemorySize   (standalone attribute)
|                *-hrStorage
|
| 1.3.6.1.2.1.25.2.3....
|                | |
|                | *-hrStorageTable (the table)
|                *-hrStorage
*/

#define VOL_NAME_SIZE 256
#define DESCR_SIZE    384

{
CHAR    temp[8];                /* Temporary buffer for first call         */
LPSTR   pDrvStrings;            /* --> allocated storage for drive strings */
LPSTR   pOriginal_DrvStrings;   /* (Needed for final deallocation          */
DWORD   DS_request_len;         /* Storage actually needed                 */
DWORD   DS_current_len;         /* Storage used on 2nd call                */
ULONG   table_index=0;          /* hrStorageTable index counter            */
CACHEROW *row;                  /* --> Cache structure for row-being built */

SYSTEM_INFO     sys_info;       /* Filled in by GetSystemInfo for VM       */

LPSTR   str_descr;              /* String for disk label/serial description*/
TCHAR   volname[VOL_NAME_SIZE+2];            /* Volume Name returned here  */
DWORD   volnamesize=VOL_NAME_SIZE;           /* Size of volname buffer     */
DWORD   serial_number;          /* Volume Serial Number                    */
DWORD   max_comp_len;           /* File system file-name component length  */
DWORD   filesys_flags;          /* File System flags (GetVolInformation)   */
CHAR    descr[DESCR_SIZE];      /* Full description possibly built here    */

DWORD   SectorsPerCluster;      /* GetDiskFreeSpace() cells */
DWORD   BytesPerSector;
DWORD   NumberOfFreeClusters;
DWORD   TotalNumberOfClusters;

MEMORYSTATUS    mem_status;     /* Filled in by GlobalMemoryStatus        */
NTSTATUS        ntstatus;       /* Generic return status                  */


/*
| Blow away any old copy of the cache
*/
DestroyTable(&hrStorage_cache);

/*
| We're going to call GetLogicalDriveStrings() twice, once to get the proper
| buffer size, and the second time to actually get the drive strings.
|
| Bogus:
*/
if ((DS_request_len = GetLogicalDriveStrings(2, temp)) == 0) {

    /* Request failed altogether, can't initialize */
    return ( FALSE );
    }

/*
| Grab enough storage for the drive strings plus one null byte at the end
*/

if ( (pOriginal_DrvStrings = pDrvStrings = malloc( (DS_request_len + 2) ) )
    == NULL) {

    /* Storage Request failed altogether, can't initialize */
    return ( FALSE );
    }

/* Go for all of the strings */
if ((DS_current_len = GetLogicalDriveStrings(DS_request_len, pDrvStrings))
    == 0) {

    /* Request failed altogether, can't initialize */
    free( pOriginal_DrvStrings );
    return ( FALSE );
    }

/*
| Freshen the time on the cache
|
| Get the current system-time in 100ns intervals . . . */
ntstatus = NtQuerySystemTime (&cache_time);


/*
|==============================================================================
| As long as we've got an unprocessed drive-string. . .
*/
while ( strlen(pDrvStrings) > 0 ) {

    UINT        drivetype;      /* Type of the drive from "GetDriveType()" */


    /*
    | Get the drive-type so we can decide whether it should participate in
    | this table.
    */
    drivetype = GetDriveType(pDrvStrings);

    if (   drivetype == DRIVE_UNKNOWN
        || drivetype == DRIVE_NO_ROOT_DIR
        || drivetype == DRIVE_REMOTE            /* No Remotes in HrStorage */
        ) {

        /* Step to next string, if any */
        pDrvStrings += strlen(pDrvStrings) + 1;

        continue;
        }

    /*
    | OK, we want this one in the table, get a row-entry created.
    */
    if ((row = CreateTableRow( HRST_ATTRIB_COUNT ) ) == NULL) {
        return ( FALSE );       // Out of memory
        }

    /* =========== hrStorageIndex ==========*/
    row->attrib_list[HRST_INDEX].attrib_type = CA_NUMBER;
    row->attrib_list[HRST_INDEX].u.unumber_value = ++table_index;


    /* =========== hrStorageType ==========*/
    row->attrib_list[HRST_TYPE].attrib_type = CA_NUMBER;

    /*
    | Based on the drive-type returned, we store a single number as
    | the cached value of the hrStorageType attribute.  When this attribute
    | is fetched, the cached number forms the last arc in the OBJECT IDENTIFIER
    | that actually specifies the type: { hrStorageTypes x }, where "x" is
    | what gets stored.
    */
    switch (drivetype) {

        case DRIVE_REMOVABLE:
            row->attrib_list[HRST_TYPE].u.unumber_value = 5;
            break;

        case DRIVE_FIXED:
            row->attrib_list[HRST_TYPE].u.unumber_value = 4;
            break;

        case DRIVE_CDROM:
            row->attrib_list[HRST_TYPE].u.unumber_value = 7;
            break;

        case DRIVE_RAMDISK:
            row->attrib_list[HRST_TYPE].u.unumber_value = 8;
            break;

        default:
            row->attrib_list[HRST_TYPE].u.unumber_value = 1;  // "Other"
            break;
        }


    /* =========== hrStorageDescr ==========
    |
    |  We try and fetch the volume label here, to get a string
    |  that may look like:
    |           C: Label="Main Disk"  Serial #=0030-34FE
    |
    | Handle all kinds of disk storage here:
    |
    |   Try to get volume label and serial number.  If we fail, we just give
    |   'em the root-path name.
    |
    |   Presume that we'll fail, and just return the root-path string.
    */
    str_descr = pDrvStrings;

    /*
    | Suppress any attempt by the system to make the user put a volume in a
    | removable drive.
    */
    SetErrorMode(SEM_FAILCRITICALERRORS);

    if (GetVolumeInformation(pDrvStrings,       /* drive name           */
                             volname,           /* Volume Name Buffer   */
                             volnamesize,       /* Size of buffer       */
                             &serial_number,    /* Vol. # Returned here */
                             &max_comp_len,     /* Max filename length  */
                             &filesys_flags,    /* File System flags    */
                             NULL,              /* Name of Filesystem   */
                             0                  /* Length of name       */
                             )) {
        /*
        | We got something back.
        |
        | If we have room given string lengths, build a description:
        |
        |    <root path> Label:<volume name>  Serial Number: <#>
        */
        #define SPRINTF_FORMAT "%s Label:%s  Serial Number %x"

        if ((strlen(SPRINTF_FORMAT) + strlen(volname) + strlen(str_descr))
            < DESCR_SIZE) {
            sprintf(descr,
                    SPRINTF_FORMAT,
                    str_descr,          // root-path
                    volname,            // volume name
                    serial_number);     // volume serial #
            str_descr = descr;
            }
        }


    row->attrib_list[HRST_DESCR].attrib_type = CA_STRING;

    /*
    | Note:
    |       The convention is established that the first characters of
    |       this description string is always the device-string (e.g. "C:")
    |       or the value of local symbol "VM" ("Virtual Memory").
    |
    |       Code in "GetHrStorageAllocationFailures()" attempts to extract
    |       the drive letter (or "Virtual Memory") from the beginning of this
    |       string in order to determine allocation failures from the
    |       Event Log(!).
    */
    if ( (row->attrib_list[HRST_DESCR].u.string_value
          = ( LPSTR ) malloc(strlen(str_descr) + 1)) == NULL) {
        return ( FALSE );       /* out of memory */
        }
    /* Copy the Value into the space */
    strcpy(row->attrib_list[HRST_DESCR].u.string_value, str_descr);

    row->attrib_list[HRST_ALLOC].attrib_type = CA_NUMBER;
    row->attrib_list[HRST_SIZE].attrib_type = CA_NUMBER;
    row->attrib_list[HRST_USED].attrib_type = CA_NUMBER;

    /*
    | Handle all kinds of disk storage info here:
    |
    |   Try to get volume statistics via GetDiskFreeSpace().
    */
    if (GetDiskFreeSpace(pDrvStrings,           // drive
                         &SectorsPerCluster,
                         &BytesPerSector,
                         &NumberOfFreeClusters,
                         &TotalNumberOfClusters
                         )) {
        /* Success */

        /* =========== hrStorageAllocationUnits ==========*/
        row->attrib_list[HRST_ALLOC].u.unumber_value =
                          BytesPerSector * SectorsPerCluster;

        /* =========== hrStorageSize ==========*/
        row->attrib_list[HRST_SIZE].u.unumber_value = TotalNumberOfClusters;

        /* =========== hrStorageUsed ==========*/
        row->attrib_list[HRST_USED].u.unumber_value =
            TotalNumberOfClusters - NumberOfFreeClusters;
        }
    else {
        /* Failure */

        /* =========== hrStorageAllocationUnits ==========*/
        row->attrib_list[HRST_ALLOC].u.unumber_value = 0;

        /* =========== hrStorageSize ==========*/
        row->attrib_list[HRST_SIZE].u.unumber_value = 0;

        /* =========== hrStorageUsed ==========*/
        row->attrib_list[HRST_USED].u.unumber_value = 0;
        }

    /* =========== hrStorageAllocationFailures ==========*/
    row->attrib_list[HRST_FAILS].attrib_type = CA_COMPUTED;

    SetErrorMode(0);        /* Turn error suppression mode off */

    /*
    | Now insert the filled-in CACHEROW structure into the
    | cache-list for the hrStorageTable.
    */
    if (AddTableRow(row->attrib_list[HRST_INDEX].u.unumber_value,  /* Index */
                    row,                                           /* Row   */
                    &hrStorage_cache                               /* Cache */
                    ) == FALSE) {
        return ( FALSE );       /* Internal Logic Error! */
        }

    /* Step to next string, if any */
    pDrvStrings += strlen(pDrvStrings) + 1;
    }


free( pOriginal_DrvStrings );

/*
|==============================================================================
| Now handle Virtual Memory as a special case.
|==============================================================================
*/
if ((row = CreateTableRow( HRST_ATTRIB_COUNT ) ) == NULL) {
    return ( FALSE );       // Out of memory
    }

/* =========== hrStorageIndex ==========*/
row->attrib_list[HRST_INDEX].attrib_type = CA_NUMBER;
row->attrib_list[HRST_INDEX].u.unumber_value = ++table_index;

/* =========== hrStorageType ==========*/
row->attrib_list[HRST_TYPE].attrib_type = CA_NUMBER;
row->attrib_list[HRST_TYPE].u.unumber_value = 3;        /* Virtual Memory */

/* =========== hrStorageDescr ==========*/
row->attrib_list[HRST_DESCR].attrib_type = CA_STRING;
if ( (row->attrib_list[HRST_DESCR].u.string_value
      = ( LPSTR ) malloc(strlen(VM) + 1)) == NULL) {
    return ( FALSE );       /* out of memory */
    }
strcpy(row->attrib_list[HRST_DESCR].u.string_value, VM);


/* =========== hrStorageAllocationUnits ==========*/
GetSystemInfo(&sys_info);
row->attrib_list[HRST_ALLOC].attrib_type = CA_NUMBER;
row->attrib_list[HRST_ALLOC].u.unumber_value =
                                             sys_info.dwAllocationGranularity;

/* =========== hrStorageSize ==========*/
/* Acquire current memory statistics */
GlobalMemoryStatus(&mem_status);

row->attrib_list[HRST_SIZE].attrib_type = CA_NUMBER;
row->attrib_list[HRST_SIZE].u.unumber_value =
    (DWORD)(mem_status.dwTotalPageFile / sys_info.dwAllocationGranularity);


/* =========== hrStorageUsed ==========*/
row->attrib_list[HRST_USED].attrib_type = CA_NUMBER;
row->attrib_list[HRST_USED].attrib_type =
    (int)((mem_status.dwTotalPageFile - mem_status.dwAvailPageFile)
                           / sys_info.dwAllocationGranularity);

/* =========== hrStorageAllocationFailures ==========*/
row->attrib_list[HRST_FAILS].attrib_type = CA_COMPUTED;

/*
| Now insert the filled-in CACHEROW structure into the
| cache-list for the hrStorageTable.
*/
if (AddTableRow(row->attrib_list[HRST_INDEX].u.unumber_value,  /* Index */
                row,                                           /* Row   */
                &hrStorage_cache                               /* Cache */
                ) == FALSE) {
    return ( FALSE );       /* Internal Logic Error! */
    }
/*
|==============================================================================
| End of Virtual Memory
|==============================================================================
*/


#if defined(CACHE_DUMP)
PrintCache(&hrStorage_cache);
#endif

/*
| Initialization of this table's cache succeeded
*/
return (TRUE);
}

/* ScanLog_Failures - Scan Event Log for Storage Allocation Failures */
/* ScanLog_Failures - Scan Event Log for Storage Allocation Failures */
/* ScanLog_Failures - Scan Event Log for Storage Allocation Failures */

static UINT
ScanLog_Failures(
                 CHAR   *device
                 )

/*
|  EXPLICIT INPUTS:
|
|       "device" is either the string "VM" (for "Virtual Memory") or
|       the logical device for which we're looking for failures (e.g. "C:").
|
|  IMPLICIT INPUTS:
|
|       The System Event log file.
|
|  OUTPUTS:
|
|     On Success/Failure:
|       Function returns the number of storage allocation failures
|       found for the specified device.
|
|  THE BIG PICTURE:
|
|       This is a "helper" function for routine "GetHrStorageAllocationFailures"
|       within this module.
|
|  OTHER THINGS TO KNOW:
|
|       We scan backwards (in time) thru the Event Log until we hit the
|       "Event Logging Started" event record (because, presumably, we don't
|       care about failures that happened before the system last came up).
*/

/*
| These symbols select the "Event Log Started" informational message.
*/
#define EVENTLOG_START_ID   0x80001775
#define EVENTLOG_START_TYPE 4
#define EVENTLOG_START_SRC  "EventLog"

{
#define EVL_BUFFER_SIZE 2048
EVENTLOGRECORD *pevlr;
BYTE            bBuffer[EVL_BUFFER_SIZE];
DWORD           dwRead, dwNeeded, cRecords;
HANDLE          h;
BOOL            keep_scanning = TRUE;
UINT            alloc_failures = 0;

/*
| Open the System event log
*/
h = OpenEventLog(NULL,      /* local computer */
                 "System"   /* source name    */
                 );

if (h == NULL) {
    return ( alloc_failures );
    }

pevlr = (EVENTLOGRECORD *) &bBuffer;

/*
| Read records sequentially "Backward in Time" until there
| are no more, or we hit the "Event Logging Started" event.
|
| Read a "Slug 'o Records":
*/
while (ReadEventLog(h,                      // event log handle
                    EVENTLOG_BACKWARDS_READ | // reads backward
                    EVENTLOG_SEQUENTIAL_READ, // sequential read
                    0,                      // ignored for sequential reads
                    pevlr,                  // address of buffer
                    EVL_BUFFER_SIZE,        // size of buffer
                    &dwRead,                // count of bytes read
                    &dwNeeded)              // bytes in next record
       && keep_scanning == TRUE) {


    /* Wind down thru this "slug" . . . */
    while (dwRead > 0) {

        /*
        | Check for "Event Logging Started"
        |
        | (The source name is just past the end of the formal structure).
        */
        if (   pevlr->EventID == EVENTLOG_START_ID
            && pevlr->EventType == EVENTLOG_START_TYPE
            && strcmp( ((LPSTR) ((LPBYTE) pevlr + sizeof(EVENTLOGRECORD))),
                      EVENTLOG_START_SRC) == 0
            ) {
            keep_scanning = FALSE;
            break;
            }

//============================================================================
// INSERT RECORD CHECKING LOGIC OF THIS SORT HERE:
//
//      IF (    <eventrecordID>           == pevlr->EventID
//           && <eventrecordtype>         == pevlr->EventType
//           && <eventrecordsourcestring> == (is the same as..)
//                     ( (LPSTR) ((LPBYTE) pevlr + sizeof(EVENTLOGRECORD)) )
//         ) {
//              // It's an allocation-failure record, if it is for device
//              // "device", then count it.
//              IF (strcmp(device, <eventrecordinstance-data>) {
//                  alloc_failures += 1;
//                  }
//           }
//============================================================================

        dwRead -= pevlr->Length;
        pevlr = (EVENTLOGRECORD *)
            ((LPBYTE) pevlr + pevlr->Length);
        }

    pevlr = (EVENTLOGRECORD *) &bBuffer;
    }

CloseEventLog(h);

/* Give 'em the count */
return (alloc_failures);
}

#if defined(CACHE_DUMP)

/* debug_print_hrstorage - Prints a Row from Hrstorage sub-table */
/* debug_print_hrstorage - Prints a Row from Hrstorage sub-table */
/* debug_print_hrstorage - Prints a Row from Hrstorage sub-table */

static void
debug_print_hrstorage(
                      CACHEROW     *row  /* Row in hrstorage table */
                      )
/*
|  EXPLICIT INPUTS:
|
|       "row" - points to the row to be dumped, if NULL, the function
|       merely prints a suitable title.
|
|  IMPLICIT INPUTS:
|
|       - Symbols used to reference the attributes in the row entry.
|       - File handle defined by OFILE, presumed to be open.
|
|  OUTPUTS:
|
|     On Success:
|       Function prints a dump of the row in ASCII for debugging purposes
|       on file handle OFILE.
|
|  THE BIG PICTURE:
|
|     Debugging only.
|
|  OTHER THINGS TO KNOW:
*/
{

if (row == NULL) {
    fprintf(OFILE, "=====================\n");
    fprintf(OFILE, "hrStorage Table Cache\n");
    fprintf(OFILE, "=====================\n");
    return;
    }


fprintf(OFILE, "hrStorageIndex . . . . . %d\n",
        row->attrib_list[HRST_INDEX].u.unumber_value);

fprintf(OFILE, "hrStorageType. . . . . . %d ",
        row->attrib_list[HRST_TYPE].u.unumber_value);

switch (row->attrib_list[HRST_TYPE].u.unumber_value) {
    case 1: fprintf(OFILE, "(Other)\n");        break;
    case 2: fprintf(OFILE, "(RAM)\n");        break;
    case 3: fprintf(OFILE, "(Virtual Memory)\n");        break;
    case 4: fprintf(OFILE, "(Fixed Disk)\n");        break;
    case 5: fprintf(OFILE, "(Removable Disk)\n");        break;
    case 6: fprintf(OFILE, "(Floppy Disk)\n");        break;
    case 7: fprintf(OFILE, "(Compact Disk)\n");        break;
    case 8: fprintf(OFILE, "(RAM Disk)\n");        break;
    default:
            fprintf(OFILE, "(Unknown)\n");
    }


fprintf(OFILE, "hrStorageDescr . . . . . \"%s\"\n",
        row->attrib_list[HRST_DESCR].u.string_value);

fprintf(OFILE, "hrStorageAllocationUnits %d\n",
        row->attrib_list[HRST_ALLOC].u.number_value);

fprintf(OFILE, "hrStorageSize. . . . . . %d\n",
        row->attrib_list[HRST_SIZE].u.number_value);

fprintf(OFILE, "hrStorageUsed. . . . . . %d\n",
        row->attrib_list[HRST_USED].u.number_value);

fprintf(OFILE, "hrStorageAllocationFails (Computed)\n");
}
#endif
