#!/usr/bin/perl -w

#
# Description: 
#
# This program, given an OID reference as an argument, creates some
# template mib module files to be used with the ucd-snmp agent.  It is
# far from perfect and will not generate working modules, but it
# significantly shortens development time by outlining the basic
# structure.
#
# Its up to you to verify what it does and change the default values
# it returns.
#
# You *must* correct the beginning of the var_XXX() function to
# correctly determine mib ownership of the incoming request.
#

use SNMP;
#use strict 'vars';
$SNMP::save_descriptions=1;
$SNMP::use_long_names=1;
$SNMP::use_enums=1;
SNMP::initMib();

#
# internal conversion tables
#
%typeToVar = qw(OctetStr string Objid objid Integer long_ret NetAddr objid 
		Ipaddr string Counter long_ret Gauge long_ret Timeticks long_ret
		UInteger long_ret Opaque string Counter64 c64);

%typeToPtr = qw(OctetStr string Objid objid Integer &long_ret NetAddr objid 
		Ipaddr string Counter &long_ret Gauge &long_ret 
		Timeticks &long_ret UInteger &long_ret Opaque string 
		Counter64 &c64);

%typeToParser = qw(OctetStr string Objid objid Integer int NetAddr objid 
		Ipaddr string Counter int Gauge int Timeticks int
		UInteger unsigned_int Opaque string Counter64 unsigned_int64);

$varInits =
"  /* variables we may use later */
  static long long_ret;
  static unsigned char string[1500];
  static oid objid[30];
  static struct counter64 c64;";


%varDefaults = (
		'long_ret' => 
"      long_ret = 0;
      return (unsigned char *) &long_ret;\n", 

		'objid' =>
"      objid[0] = 0;
      objid[1] = 0;
      *var_len = 2*sizeof(oid);
      return (unsigned char *) objid;\n",

		'c64' =>
"      c64.high = 0;
      c64.low = 0;
      *var_len = sizeof(c64);
      return (unsigned char *) &c64;\n",

		'string' => 
"      *string = 0;
      *var_len = strlen(string);
      return (unsigned char *) string;\n");

%typeToASN = qw(OctetStr ASN_OCTET_STR Objid ASN_OBJECT_ID Integer ASN_INTEGER
		NetAddr ASN_OBJECT_ID Ipaddr ASN_IPADDRESS Counter ASN_COUNTER
		Gauge ASN_GAUGE Timeticks ASN_TIMETICKS
		UInteger ASN_UINTEGER Opaque ASN_OPAQUE 
		Counter64 ASN_COUNTER64);

%accessToUCD = qw(ReadOnly RONLY ReadWrite RWRITE 
		  WriteOnly RWRITE Create RWRITE);

@varLengths = (2,4,7,8,13);

$oid = shift;

if (!defined($oid)) {
    print STDERR "You didn\'t specify a mib oid to convert!\n";
    exit(1);
}

$mib = $SNMP::MIB{$oid};
$_ = $fulloid = $mib->{'objectID'};
if (!defined ($fulloid)) {
    print STDERR "Couldn\'t find mib reference: $oid\n";
    exit(1);
}

s/[^.]//g;
$fulllen = length($_);
$outputName = shift;
$outputName = $mib->{'label'} if (!defined($outputName));
$OUTPUTNAME = uc($outputName);
print "outputing to $outputName.c and $outputName.h ...\n";

open(DOTH,">$outputName.h");
open(DOTC,">$outputName.c");
print DOTH "
/* This file was generated by mib2c and is intended for use as a mib module
   for the ucd-snmp snmpd agent. */

#ifndef _MIBGROUP_${OUTPUTNAME}_H
#define _MIBGROUP_${OUTPUTNAME}_H

/* we use header_generic and checkmib from the util_funcs module */

config_require(util_funcs)

/* Magic number definitions: */\n\n";

print DOTC "
/* This file was generated by mib2c and is intended for use as a mib module
   for the ucd-snmp snmpd agent. */

#include <config.h>

#include \"mibincl.h\"
#include \"$outputName.h\"

void init_$outputName __P((void)) {
/* place any initialization routines needed here */
}

unsigned char *
var_$outputName(vp, name, length, exact, var_len, write_method)
    struct variable *vp;
    oid     *name;
    int     *length;
    int     exact;
    int     *var_len;
    int     (**write_method) __P((int, unsigned char *,unsigned char, int, unsigned char *,oid*, int));
{

$varInits

  *write_method = 0;           /* assume it isnt writable for the time being */
  *var_len = sizeof(long_ret); /* assume an integer and change later if not */

  /* XXX: this following line is almost gaurunteed to be wrong for
  your mib, please fix this */
  if (header_generic(vp,name,length,exact,var_len,write_method))
      return 0;

  /* this is where we do the value assignments for the mib results. */
  switch(vp->magic) {\n\n";

#
# Collect mib information
#
$count = 0;
$depth = loadMib($mib,0,0)-1;
for($varlen = 0; $varlen <= $#varLengths; $varlen++) {
  last if ($depth <= $varLengths[$varlen]);
}
$varlen = $varLengths[$varlen];

print DOTC "    default:
      ERROR_MSG(\"\");
  }
  return 0;
}

$writeInfo";

print DOTH "
/* function definitions */

extern void   init_$outputName __P((void));
extern unsigned char *var_$outputName __P((struct variable *, oid *, int *, int, int *, int (**write) __P((int, unsigned char *, unsigned char, int, unsigned char *, oid *, int)) ));
$functionInfo

/* Only load this structure when this .h file is called in the snmp_vars.c 
   file in tha agent subdirectory of the source tree */

#ifdef IN_SNMP_VARS_C

/* this variable defines function callbacks and type return information 
   for the $outputName mib */

struct variable$varlen ${outputName}_variables[] = {
$structinfo
};

/* now load this mib into the agents mib table */
config_load_mib(" . substr($fulloid,1) . ", $fulllen, ${outputName}_variables)

#endif /* IN_SNMP_VARS_C */
#endif /* _MIBGROUP_${OUTPUTNAME}_H */\n";

#
# Writable variable code
#
close(DOTH);
close(DOTC);
print "  depth: $depth\n";
print "  Number of Lines Created:\n";
system("wc -l $outputName.c $outputName.h");
print "Done.\n";

sub loadMib {
    my $mib = shift;
#    my $oid = shift;
    my $depth = shift;
    $depth = $depth + 1;
#    my $mib = $SNMP::MIB{$oid};
#    print "doing $mib->{label} : $mib->{objectID}\n";
    if (defined($mib->{'access'}) && 
	$mib->{'access'} =~ /ReadOnly|ReadWrite|WriteOnly|Create/) {
	my $cname = uc($mib->{'label'});
	print DOTC "    case $cname:\n";
	$count = $count + 1;
	$subid = $mib->{'objectID'};
	$subid =~ s/$fulloid\.//;
	$subid =~ s/\./,/g;
	if (!defined($typeToVar{$mib->{'type'}})) {
	    print DOTC "/* unknown type: $mib->{type}.  mib2c can not set up a default value for this mib value */.";
	    print DOTH "/* unknown type: $mib->{type}.  mib2c can not handle this mib value: */.";
	    $count = $count + 1;
	    print STDERR "unknown type:  $mib->{type} for $mib->{label}\n";
	    $structinfo .= 
		sprintf("/* unknown type: $mib->{type}.  mib2c can not handle this mib value: */\n{ %-20s, %-14s, %-6.6s, %s, %d, { %s } },\n",
			$cname, "UNKNOWN_TYPE_$mib->{type}",
			$accessToUCD{$mib->{'access'}},
			"var_$outputName",
			$depth-1, $subid);
	} else {
	    if ($mib->{'access'} =~ /ReadWrite|WriteOnly|Create/) {
		createWriteFunction($mib->{'label'}, $mib->{'type'});
		print DOTC "      *write_method = write_$mib->{label};\n";
	    }
	    print DOTC $varDefaults{$typeToVar{$mib->{'type'}}}, "\n";
	    $structinfo .= 
		sprintf("  { %-20s, %-14s, %-6.6s, %s, %d, { %s } },\n",
			$cname, $typeToASN{$mib->{'type'}},
			$accessToUCD{$mib->{'access'}},
			"var_$outputName",
			$depth-1, $subid);
	}
	printf DOTH ("#define   %-20s  $count\n", $cname);
    }
    my $children = $$mib{'children'}; 
    my $i;
    my $newdepth = $depth;
    foreach $i (@{$children}) {
	$newdepth = max(loadMib($i, $depth), $newdepth);
    }
    return $newdepth;
}

sub max {
    my $x = shift;
    my $y = shift;
    return ($x > $y) ? $x : $y;
}

sub createWriteFunction {
    my $name = shift;
    my $type = shift;

    $writeInfo .= "int
write_$name(action, var_val, var_val_type, var_val_len, statP, name, name_len)
   int      action;
   u_char   *var_val;
   u_char   var_val_type;
   int      var_val_len;
   u_char   *statP;
   oid      *name;
   int      name_len;
{
$varInits
  int size, bigsize=1000;

  if (var_val_type != $typeToASN{$type}){
      fprintf(stderr, \"write to $name not $typeToASN{$type}\\n\");
      return SNMP_ERR_WRONGTYPE;
  }
  if (var_val_len > sizeof($typeToVar{$type})){
      fprintf(stderr,\"write to $name: bad length\\n\");
      return SNMP_ERR_WRONGLENGTH;
  }
  if (action == COMMIT){
      size = sizeof($typeToVar{$type});
      asn_parse_$typeToParser{$type}(var_val, &bigsize, &var_val_type, $typeToPtr{$type}, size);
      /* Here, the variable has been stored in $typeToVar{$type} for
      you to use, and you have just been asked to do something with
      it... Your code goes here. */
  }
  return SNMP_ERR_NOERROR;
}\n\n";
    $functionInfo .= "int write_$name __P((int, u_char *,u_char, int, u_char *,oid*, int));\n";
}
