/*
 *  Host Resources MIB - Device group implementation - hr_device.c
 *
 */
/* Portions of this file are subject to the following copyright(s).  See
 * the Net-SNMP's COPYING file for more details and other copyrights
 * that may apply:
 */
/*
 * Portions of this file are copyrighted by:
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms specified in the COPYING file
 * distributed with the Net-SNMP package.
 */

#include <net-snmp/net-snmp-config.h>
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif

#include "host_res.h"
#include "hr_device.h"

        /*********************
	 *
	 *  Kernel & interface information,
	 *   and internal forward declarations
	 *
	 *********************/

int             Get_Next_Device(void);

PFV             init_device[HRDEV_TYPE_MAX];
PFIV            next_device[HRDEV_TYPE_MAX];
PFV             save_device[HRDEV_TYPE_MAX];
int             dev_idx_inc[HRDEV_TYPE_MAX];

PFS             device_descr[HRDEV_TYPE_MAX];
PFO             device_prodid[HRDEV_TYPE_MAX];
PFI             device_status[HRDEV_TYPE_MAX];
PFI             device_errors[HRDEV_TYPE_MAX];

int             current_type;

void            Init_Device(void);
int             header_hrdevice(struct variable *, oid *, size_t *, int,
                                size_t *, WriteMethod **);


        /*********************
	 *
	 *  Initialisation & common implementation functions
	 *
	 *********************/

#define	HRDEV_INDEX		1
#define	HRDEV_TYPE		2
#define	HRDEV_DESCR		3
#define	HRDEV_ID		4
#define	HRDEV_STATUS		5
#define	HRDEV_ERRORS		6

struct variable4 hrdevice_variables[] = {
    {HRDEV_INDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
     var_hrdevice, 2, {1, 1}},
    {HRDEV_TYPE, ASN_OBJECT_ID, NETSNMP_OLDAPI_RONLY,
     var_hrdevice, 2, {1, 2}},
    {HRDEV_DESCR, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
     var_hrdevice, 2, {1, 3}},
    {HRDEV_ID, ASN_OBJECT_ID, NETSNMP_OLDAPI_RONLY,
     var_hrdevice, 2, {1, 4}},
    {HRDEV_STATUS, ASN_INTEGER, NETSNMP_OLDAPI_RONLY,
     var_hrdevice, 2, {1, 5}},
    {HRDEV_ERRORS, ASN_COUNTER, NETSNMP_OLDAPI_RONLY,
     var_hrdevice, 2, {1, 6}}
};
oid             hrdevice_variables_oid[] = { 1, 3, 6, 1, 2, 1, 25, 3, 2 };


void
init_hr_device(void)
{
    int             i;

    /*
     * Initially assume no devices
     *    Insert pointers to initialisation/get_next routines
     *    for particular device types as they are implemented
     *      (set up in the appropriate 'init_*()' routine )
     */

    for (i = 0; i < HRDEV_TYPE_MAX; ++i) {
        init_device[i] = NULL;
        next_device[i] = NULL;
        save_device[i] = NULL;
        dev_idx_inc[i] = 0;     /* Assume random indices */

        device_descr[i] = NULL;
        device_prodid[i] = NULL;
        device_status[i] = NULL;
        device_errors[i] = NULL;
    }

    REGISTER_MIB("host/hr_device", hrdevice_variables, variable4,
                 hrdevice_variables_oid);
}


/*
 * header_hrdevice(...
 * Arguments:
 * vp     IN      - pointer to variable entry that points here
 * name    IN/OUT  - IN/name requested, OUT/name found
 * length  IN/OUT  - length of IN/OUT oid's 
 * exact   IN      - TRUE if an exact match was requested
 * var_len OUT     - length of variable or 0 if function returned
 * write_method
 * 
 */

int
header_hrdevice(struct variable *vp,
                oid * name,
                size_t * length,
                int exact, size_t * var_len, WriteMethod ** write_method)
{
#define HRDEV_ENTRY_NAME_LENGTH	11
    oid             newname[MAX_OID_LEN];
    int             dev_idx, LowIndex = -1, LowType = -1;
    int             result;

    DEBUGMSGTL(("host/hr_device", "var_hrdevice: "));
    DEBUGMSGOID(("host/hr_device", name, *length));
    DEBUGMSG(("host/hr_device", " %d\n", exact));

    memcpy((char *) newname, (char *) vp->name,
           (int) vp->namelen * sizeof(oid));


    /*
     *  Find the "next" device entry.
     *  If we're in the middle of the table, then there's
     *     no point in examining earlier types of devices,
     *     so set the starting type to that of the variable
     *     being queried.
     *  If we've moved from one column of the table to another,
     *     then we need to start at the beginning again.
     *     (i.e. the 'compare' fails to match)
     *  Similarly if we're at the start of the table
     *     (i.e. *length is too short to be a full instance)
     */

    if ((snmp_oid_compare(vp->name, vp->namelen, name, vp->namelen) == 0)
        && (*length > HRDEV_ENTRY_NAME_LENGTH))
        current_type = (name[HRDEV_ENTRY_NAME_LENGTH] >> HRDEV_TYPE_SHIFT);
    else
        current_type = 0;

    Init_Device();
    for (;;) {
        dev_idx = Get_Next_Device();
        DEBUGMSG(("host/hr_device", "(index %d ....", dev_idx));
        if (dev_idx == -1)
            break;
        if (LowType != -1 && LowType < (dev_idx >> HRDEV_TYPE_SHIFT))
            break;
        newname[HRDEV_ENTRY_NAME_LENGTH] = dev_idx;
        DEBUGMSGOID(("host/hr_device", newname, *length));
        DEBUGMSG(("host/hr_device", "\n"));
        result = snmp_oid_compare(name, *length, newname, vp->namelen + 1);
        if (exact && (result == 0)) {
            if (save_device[current_type] != NULL)
                (*save_device[current_type]) ();
            LowIndex = dev_idx;
            break;
        }
        if ((!exact && (result < 0)) &&
            (LowIndex == -1 || dev_idx < LowIndex)) {
            if (save_device[current_type] != NULL)
                (*save_device[current_type]) ();
            LowIndex = dev_idx;
            LowType = (dev_idx >> HRDEV_TYPE_SHIFT);
            if (dev_idx_inc[LowType])   /* Increasing indices => now done */
                break;
        }

    }

    if (LowIndex == -1) {
        DEBUGMSGTL(("host/hr_device", "... index out of range\n"));
        return (MATCH_FAILED);
    }

    newname[HRDEV_ENTRY_NAME_LENGTH] = LowIndex;
    memcpy((char *) name, (char *) newname,
           ((int) vp->namelen + 1) * sizeof(oid));
    *length = vp->namelen + 1;
    *write_method = (WriteMethod*)0;
    *var_len = sizeof(long);    /* default to 'long' results */

    DEBUGMSGTL(("host/hr_device", "... get device stats "));
    DEBUGMSGOID(("host/hr_device", name, *length));
    DEBUGMSG(("host/hr_device", "\n"));

    return LowIndex;
}


oid             device_type_id[] = { 1, 3, 6, 1, 2, 1, 25, 3, 1, 99 };  /* hrDeviceType99 */
int             device_type_len =
    sizeof(device_type_id) / sizeof(device_type_id[0]);


        /*********************
	 *
	 *  System specific implementation functions
	 *
	 *********************/


u_char         *
var_hrdevice(struct variable *vp,
             oid * name,
             size_t * length,
             int exact, size_t * var_len, WriteMethod ** write_method)
{
    int             dev_idx, type;
    oid            *oid_p;
    const char     *tmp_str;
    static char     string[1024];

really_try_next:
    dev_idx =
        header_hrdevice(vp, name, length, exact, var_len, write_method);
    if (dev_idx == MATCH_FAILED)
        return NULL;

    type = (dev_idx >> HRDEV_TYPE_SHIFT);

    switch (vp->magic) {
    case HRDEV_INDEX:
        long_return = dev_idx;
        return (u_char *) & long_return;
    case HRDEV_TYPE:
        device_type_id[device_type_len - 1] = type;
        *var_len = sizeof(device_type_id);
        return (u_char *) device_type_id;
    case HRDEV_DESCR:
        if ((device_descr[type] != NULL) &&
            (NULL!=(tmp_str=((*device_descr[type])(dev_idx))))) {
            strncpy(string, tmp_str, sizeof(string)-1);
            string[ sizeof(string)-1] = 0;
        } else
#if NETSNMP_NO_DUMMY_VALUES
            goto try_next;
#else
            sprintf(string, "a black box of some sort");
#endif
        *var_len = strlen(string);
        return (u_char *) string;
    case HRDEV_ID:
        if (device_prodid[type] != NULL)
            oid_p = ((*device_prodid[type]) (dev_idx, var_len));
        else {
            oid_p = nullOid;
            *var_len = nullOidLen;
        }
        return (u_char *) oid_p;
    case HRDEV_STATUS:
        if (device_status[type] != NULL)
            long_return = ((*device_status[type]) (dev_idx));
        else
#if NETSNMP_NO_DUMMY_VALUES
            goto try_next;
#else
            long_return = 2;    /* Assume running */
#endif
        if ( !long_return )
            goto try_next;
        return (u_char *) & long_return;
    case HRDEV_ERRORS:
        if (device_errors[type] != NULL)
            long_return = (*device_errors[type]) (dev_idx);
        else
#if NETSNMP_NO_DUMMY_VALUES
            goto try_next;
#else
            long_return = 0;    /* Assume OK */
#endif
        return (u_char *) & long_return;
    default:
        DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_hrdevice\n",
                    vp->magic));
    }

  try_next:
    if (!exact)
        goto really_try_next;

    return NULL;
}


        /*********************
	 *
	 *  Internal implementation functions
	 *
	 *********************/


void
Init_Device(void)
{
    /*
     *  Find the first non-NULL initialisation function
     *    and call it
     */
    while (current_type < HRDEV_TYPE_MAX &&
           init_device[current_type] == NULL)
        if (++current_type >= HRDEV_TYPE_MAX)
            return;
    /* Check current_type, if >= MAX first time into loop, would fail below */
    if (current_type < HRDEV_TYPE_MAX)
        (*init_device[current_type]) ();
}

int
Get_Next_Device(void)
{
    int             result = -1;

    /*
     *  Call the 'next device' function for the current
     *    type of device
     *
     *  TODO:  save the necessary information about that device
     */
    if (current_type < HRDEV_TYPE_MAX && next_device[current_type] != NULL)
        result = (*next_device[current_type]) ();

    /*
     *  No more devices of the current type.
     *  Try the next type (if any)
     */
    if (result == -1) {
        if (++current_type >= HRDEV_TYPE_MAX) {
            current_type = 0;
            return -1;
        }
        Init_Device();
        return Get_Next_Device();
    }
    return result;
}
