
/*
 *  Host Resources MIB - partition device group implementation - hr_partition.c
 *
 */

#include <config.h>
#include <fcntl.h>
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>

#ifdef dynix
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#endif

#include "host_res.h"
#include "hr_partition.h"
#include "hr_filesys.h"
#include "hr_disk.h"

#include <sys/stat.h>

#define HRP_MONOTONICALLY_INCREASING

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

static int  HRP_savedDiskIndex;
static int  HRP_savedPartIndex;
static char HRP_savedName[100];

static int  HRP_DiskIndex;

static void  Save_HR_Partition (int, int);


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

static void  Init_HR_Partition (void);
static int   Get_Next_HR_Partition (void);
int header_hrpartition (struct variable *,oid *, size_t *, int, size_t *, WriteMethod **);


#define	HRPART_INDEX		1
#define	HRPART_LABEL		2
#define	HRPART_ID		3
#define	HRPART_SIZE		4
#define	HRPART_FSIDX		5

struct variable4 hrpartition_variables[] = {
    { HRPART_INDEX,     ASN_INTEGER, RONLY, var_hrpartition, 2, {1,1}},
    { HRPART_LABEL,   ASN_OCTET_STR, RONLY, var_hrpartition, 2, {1,2}},
    { HRPART_ID,      ASN_OCTET_STR, RONLY, var_hrpartition, 2, {1,3}},
    { HRPART_SIZE,      ASN_INTEGER, RONLY, var_hrpartition, 2, {1,4}},
    { HRPART_FSIDX,     ASN_INTEGER, RONLY, var_hrpartition, 2, {1,5}}
};
oid hrpartition_variables_oid[] = { 1,3,6,1,2,1,25,3,7 };


void init_hr_partition( void )
{
    REGISTER_MIB("host/hr_partition", hrpartition_variables, variable4, hrpartition_variables_oid);
}


/*
  header_hrpartition(...
  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_hrpartition(struct variable *vp,
		   oid *name,
		   size_t *length,
		   int exact,
		   size_t *var_len,
		   WriteMethod **write_method)
{
#define HRPART_DISK_NAME_LENGTH		11
#define HRPART_ENTRY_NAME_LENGTH	12
    oid newname[MAX_OID_LEN];
    int part_idx, LowDiskIndex=-1, LowPartIndex = -1;
    int result;

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

    memcpy( (char *)newname,(char *)vp->name, (int)vp->namelen * sizeof(oid));
	/* Find "next" partition entry */

    Init_HR_Disk();
    Init_HR_Partition();

		/*
	 	 *  Find the "next" disk and partition entries.
		 *  If we're in the middle of the table, then there's
		 *     no point in examining earlier disks, so set the
		 *     starting disk 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 > HRPART_DISK_NAME_LENGTH )) {
        LowDiskIndex = (name[HRPART_DISK_NAME_LENGTH] & ((1<<HRDEV_TYPE_SHIFT)-1));

	while ( HRP_DiskIndex < LowDiskIndex ) {
            Init_HR_Partition();	/* moves to next disk */
	    if ( HRP_DiskIndex == -1 )
		return(MATCH_FAILED);
	}
    }

    for ( ;; ) {
        part_idx = Get_Next_HR_Partition();
        if ( part_idx == 0 )
	    break;
	newname[HRPART_DISK_NAME_LENGTH] = (HRDEV_DISK << HRDEV_TYPE_SHIFT) + HRP_DiskIndex;
	newname[HRPART_ENTRY_NAME_LENGTH] = part_idx;
        result = snmp_oid_compare(name, *length, newname, vp->namelen + 2);
        if (exact && (result == 0)) {
	    Save_HR_Partition( HRP_DiskIndex, part_idx );
	    LowDiskIndex = HRP_DiskIndex;
	    LowPartIndex = part_idx;
            break;
	}
	if (!exact && (result < 0)) {
	    if ( LowPartIndex == -1 ) {
		Save_HR_Partition( HRP_DiskIndex, part_idx );
	        LowDiskIndex = HRP_DiskIndex;
	        LowPartIndex = part_idx;
	    }
	    else if ( LowDiskIndex < HRP_DiskIndex )
		break;
	    else if ( part_idx < LowPartIndex ) {
		Save_HR_Partition( HRP_DiskIndex, part_idx );
	        LowDiskIndex = HRP_DiskIndex;
	        LowPartIndex = part_idx;
	    }
#ifdef HRP_MONOTONICALLY_INCREASING
	    break;
#endif
	}
    }

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

    newname[HRPART_DISK_NAME_LENGTH] = (HRDEV_DISK << HRDEV_TYPE_SHIFT) + LowDiskIndex;
    newname[HRPART_ENTRY_NAME_LENGTH] = LowPartIndex;
    memcpy( (char *)name,(char *)newname, ((int)vp->namelen + 2) * sizeof(oid));
    *length = vp->namelen + 2;
    *write_method = 0;
    *var_len = sizeof(long);	/* default to 'long' results */

    DEBUGMSGTL(("host/hr_partition", "... get partition stats "));
    DEBUGMSGOID(("host/hr_partition", name, *length));
    DEBUGMSG(("host/hr_partition","\n"));
    return LowPartIndex;
}


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


u_char *
var_hrpartition(struct variable *vp,
		oid *name,
		size_t *length,
		int exact,
		size_t *var_len,
		WriteMethod **write_method)
{
    int  part_idx;
    static char string[100];
    struct stat stat_buf;

    part_idx = header_hrpartition(vp, name, length, exact, var_len, write_method);
    if ( part_idx == MATCH_FAILED )
	return NULL;
        
    if (stat( HRP_savedName, &stat_buf ) == -1 )
	return NULL;

    switch (vp->magic){
	case HRPART_INDEX:
	    long_return = part_idx;
	    return (u_char *)&long_return;
	case HRPART_LABEL:
	    *var_len = strlen(HRP_savedName);
	    return (u_char *)HRP_savedName;
	case HRPART_ID:			/* Use the device number */
	    sprintf(string, "0x%x", (int)stat_buf.st_rdev) ;
	    *var_len = strlen(string);
	    return (u_char *) string;
	case HRPART_SIZE:
				/* XXX - based on single partition assumption */
	    long_return = Get_FSSize( HRP_savedName );
	    return (u_char *)&long_return;
	case HRPART_FSIDX:
	    long_return = Get_FSIndex( HRP_savedName );
	    return (u_char *)&long_return;
	default:
	    DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_hrpartition\n", vp->magic));
    }
    return NULL;
}


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

static int HRP_index;

static void
Init_HR_Partition (void)
{
   HRP_DiskIndex = Get_Next_HR_Disk();
   if (HRP_DiskIndex != -1)
       HRP_DiskIndex &= ((1 << HRDEV_TYPE_SHIFT) - 1);

   HRP_index = -1;
}

static int
Get_Next_HR_Partition (void)
{
    char string[100];
    int fd;

    if ( HRP_DiskIndex == -1 ) {
	return 0;
    }

    HRP_index++;
    while ( Get_Next_HR_Disk_Partition( string, HRP_index ) != -1 ) {
	DEBUGMSGTL(("host/hr_partition",
                    "Get_Next_HR_Partition: %s (:%d)\n",
                    string, HRP_index ));

	fd=open( string, O_RDONLY  );
	if (fd != -1 ) {
            close(fd);
            return HRP_index+1;
	}
	else if (errno == EBUSY) {
	    return HRP_index+1;
	}
	HRP_index++;
    }

	/*
	 * Finished with this disk, try the next
	 */
    Init_HR_Partition();
    return( Get_Next_HR_Partition() );
}

static void
Save_HR_Partition(int disk_idx, 
		  int part_idx)
{
   HRP_savedDiskIndex = disk_idx;
   HRP_savedPartIndex = part_idx;
   (void) Get_Next_HR_Disk_Partition ( HRP_savedName, HRP_index );
}
