#!/usr/bin/php
<? // ( -*- php -*- this makes emacs users happy too? )
/*
 Network Interface Statistics Collection Agent v2.5
 by Brett Baugh

 Here we have the Nisca SNMP Collector. Set the two variables below before using.

 */
//---------------------------------------------------------------------------------//
// If you want to run this from crontab instead of as a daemon, change the
// $runasdaemon variable to anything but "y", and then add a crontab job for it.
$runasdaemon="y";
// Where to put your temporary SQL queries cache file.
// Don't use the same file as the one the "collect" script is using!
// BEWARE if you have one of those demon-possessed systems that nukes /tmp on every reboot.
$cache_file="/tmp/nisca-snmp_sql_cache.txt";
//---------------------------------------------------------------------------------//

set_time_limit(0);
$skip_load=1; // tells functions.php to not automatically load all the config
              // variables and interfaces; this collector has its own way.
$inhibit_html=1; // tells functions.php to just DIE if this is being run from a browser.
require_once("/etc/nisca/nisca.conf");
require_once($location["functions"]);
require_once($location["cfunctions"]);

function load_config() {
  global $idnum, $dsources, $delay;
  static $idnum_bak, $dsources_bak, $delay_bak;
  $idnum=$dsources=array();

// Get the default "delay" value.
  $sq="select delay from config";
  $res=dosql2($sq);
  if (mysql_num_rows($res) > 0) {
    $delay=mysql_result($res,0,0);

// Populate $dsources array of arrays of arrays of arrays of naming methods.
    $sq="select hostname, community, if_name, dynamic, snmpv2, method from interfaces " .
        "order by hostname, community, if_name";
    $res=dosql2($sq);
    if (mysql_num_rows($res) > 0) {
      while ($row=mysql_fetch_array($res)) {
        $hn=$row["hostname"];
        $comm=$row["community"];
        $if=$row["if_name"];
        $dsources[ss($row["hostname"])][ss($row["community"])][ss($row["if_name"])]=array(
          "m" => ss($row["method"]),
          "v" => $row["snmpv2"],
          "d" => $row["dynamic"]
        );
      }
    }

// Populate $idnum array for DB "id" number fields for each interface.
    $sq="select * from stats_ifs";
    $res=dosql2($sq);
    if (mysql_num_rows($res) > 0) {
      while ($row=mysql_fetch_array($res)) {
        $idnum[ss($row["hostname"])][ss($row["community"])][ss($row["if_name"])]=$row["id"];
      }
    }
    if ($debug) { echo "Done:\n  Hosts: " . count($dsources) . "\n"; flush(); }
    $idnum_bak=$idnum;
    $dsources_bak=$dsources;
    $delay_bak=$delay;
    return;
  } else {
// Our call did not go through.
    if (!is_array($idnum_bak) || !is_array($dsources_bak) || !$delay_bak) {
// This means there'll be no sql_cache or messages to worry about.
      echo "\nUrp. No database link is available, and I don't have enough\n" .
           "backup configuration to work with. I must now die. Try later...\n\n";
      die;
    }
    $delay=300;
  }
// If all else failed, but not too badly, restore the config from the backups and keep going.
  if ($debug) { echo "Database link has failed. Restoring backup config settings.\n"; }
  $idnum=$idnum_bak;
  $dsources=$dsources_bak;
  $delay=$delay_bak;
  if ($debug) { echo "Done:\n  Hosts: " . count($dsources) . "\n"; flush(); }
}

//---------------------------------------------------------------------------------//

if ($argc > 1) {
  $debug="y";
} else {
  unset($debug);
  write_pid($location["snmp_pidfile"]);
}

$oids=set_oids();
while (1==1) {
  load_config();
  if ($debug) { $start_overall=getmicrotime(); }
  $master_sql="insert ignore into stats values ";
  $master_sql_len=strlen($master_sql);
  foreach (array_keys($dsources) as $host_id) {
    $v2capable="y";
    $get32=$get64=FALSE;
    if ($debug) {
      $start_host=getmicrotime();
      echo "Now doing the host \"$host_id\"\n"; flush();
    }

// First we make the list of things to ask for from this host.
// This way, we only have to query them for stuff *once each*.
    $tmp_idx=$snmp_todo=$snmp_results=array();
    foreach (array_keys($dsources[$host_id]) as $comm) {
      foreach (array_keys($dsources[$host_id][$comm]) as $ds_id) {
        $tmp=$dsources[$host_id][$comm][$ds_id];
        $snmp_todo[$comm][$oids[$tmp["m"]]]=$tmp["v"];
        if ($tmp["v"]=="y") { $get64=TRUE; }
        else { $get32=TRUE; }
      }
    }

// Now we ask for them.
    $aout=array();
    $that_time=time();
    foreach(array_keys($snmp_todo) as $c) {
      foreach(array_keys($snmp_todo[$c]) as $method) {
        $vtmp=$snmp_todo[$c][$method];
        $oidarr=array();
        if ($get32 || $method==".1.3.6.1.2.1.2.2.1.1") { $oidarr[]=$oids["ifEntry"]; }
        if ($get64) { $oidarr[]=$oids["ifXEntry"]; }
        foreach($oidarr as $thisoid) {
          if (! $a=mysnmpwalkoid($host_id, $c, $thisoid, $v2capable)) {
            $v2capable="";
            $a=mysnmpwalkoid($host_id, $c, $thisoid, "");
          }
          if ($a) {
            foreach($a as $line) {
              $aout[]=$line;
            }
          } else {
            if ($debug) {
              echo "  Couldn't get the OID $thisoid from this host with v1 OR v2c...\n";
              $tmp=formattime(getmicrotime()-$start_host);
              echo "Finished host ($tmp).\n\n"; flush();
            }
// No need to keep bothering this host; it ain't talking to us. Just a waste of time.
            continue 4;
          }
        }
      }
    }
    if ($debug) {
      echo "  This host is ";
      if ($v2capable != "y") { echo "NOT "; }
      echo "capable of using snmpv2c.\n";
    }
    foreach($aout as $line) {
      unset($regs);
      preg_match('/^([\d\.]+)\.(\d+)\s+(.+)$/', $line, $regs);
      if ($field_name[$regs[1]]=="") { continue; }
      if (strpos($regs[1], $oids["ifEntry"]) === FALSE) {
        $snmp_results["v64"][$field_name[$regs[1]]][$regs[2]]=result_fix($regs[3]);
      } else {
        $snmp_results["v32"][$field_name[$regs[1]]][$regs[2]]=result_fix($regs[3]);
      }
    }
    unset($a, $aout, $snmp_todo);
// Now parse the result arrays for the specific data we need.
    foreach (array_keys($dsources[$host_id]) as $comm) {
      foreach (array_keys($dsources[$host_id][$comm]) as $ds_id) {
        $dsm=$dsources[$host_id][$comm][$ds_id];
        if ($dsm["v"]=="y") { $which="v64"; } else { $which="v32"; }
        if ($dsm["m"] != "ifIndex" && ($dsm["index"] == "" || $dsm["d"]=="y")) {
          $m=$oids[$dsm["m"]];
          if (!is_array($tmp_idx[$m])) {
            if ($debug) {
              echo "  Reading {$dsm["m"]}-to-ifIndex map...\n"; flush();
            }
            if ($a=mysnmpwalkoid($host_id, $comm, $m, $v2capable)) {
              foreach($a as $line) {
                if (preg_match('/^[\d\.]+\.(\d+)\s+(.+)/', $line, $regs)) {
                  $tmp_idx[$m][result_fix($regs[2])]=$regs[1];
                }
              }
            }
          }
          $if_index=$tmp_idx[$m][$ds_id];
          $dsources[$host_id][$comm][$ds_id]["index"]=$if_index;
        } elseif ($dsm["m"]=="ifIndex") {
          $if_index=$ds_id;
          $dsources[$host_id][$comm][$ds_id]["index"]=$if_index;
        } else {
          $if_index=$dsources[$host_id][$comm][$ds_id]["index"];
        }

// If this is an interface that has never been put into the stats table,
// add a new entry in stats_ifs for it and define it in the $if_id array.
// We'll do this even if debug mode is on.
        $idchk=$idnum[$host_id][$comm][$ds_id];
        if ($idchk=="") {
          if ($debug) {
            echo "  New interface detected... "; flush();
          }
          $sq="select id from stats_ifs where (hostname='$host_id' AND " .
              "community='$comm' AND if_name='$ds_id')";
          $res=dosql2($sq);
          if (mysql_num_rows($res) > 0) {
            $idchk=mysql_result($res,0,"id");
            if ($debug) {
              echo "entry ($idchk) already in stats_ifs table,\n  but not in \$idnum array.\n";
              flush();
            }
          } else {
            $sq="insert into stats_ifs values(0, '$host_id', '$comm', '$ds_id')";
            $res=dosql2($sq);
            $idchk=mysql_insert_id();
            if ($debug) {
              echo "entry ($idchk) added to stats_ifs table and \$idnum array.\n";
              flush();
            }
          }
          $idnum[$host_id][$comm][$ds_id]=$idchk;
        }
        $tmpsql="({$idnum[$host_id][$comm][$ds_id]},";
        $fnames=array("r_bytes", "r_packets", "r_errors", "r_dropped",
                      "t_bytes", "t_packets", "t_errors", "t_dropped");
        foreach($fnames as $t) {
          $data=$snmp_results[$which][$t][$if_index];
          if (preg_match('/[^\d\.-]/', $data) || trim($data)=="") { $data=0; }
          $tmpsql .= "$data,";
        }
        $tmpsql=substr($tmpsql, 0, strlen($tmpsql)-1) . ",$that_time),";
        if (strlen($master_sql)+strlen($tmpsql)>$maxpkt) {
          $master_sql=substr($master_sql, 0, strlen($master_sql)-1);
          if ($debug) {
            echo "I would have sent this combined-insert SQL query now:\n\n$master_sql\n\n";
          } else {
            if (dosql2($master_sql)==FALSE) {
              $sql_cache[]="$master_sql\n";
            } else {
              send_cache();
            }
          }
          $master_sql="insert ignore into stats values ";
          $master_sql_len=strlen($master_sql);
        }
        $master_sql .= $tmpsql;
        unset($dsm);
      }
    }
    if ($debug) {
      $tmp=formattime(getmicrotime()-$start_host);
      echo "Finished host ($tmp).\n\n"; flush();
    }
    unset($tmp_idx);
  }
  if ($master_sql_len != strlen($master_sql)) {
    $master_sql=substr($master_sql, 0, strlen($master_sql)-1);
    if ($debug) {
      echo "I would have sent this combined-insert SQL query now:\n\n$master_sql\n\n";
    } else {
      if (dosql2($master_sql)==FALSE) {
        $sql_cache[]="$master_sql\n";
      } else {
        send_cache();
      }
    }
  }
  if ($debug) {
    $tot=formattime(getmicrotime()-$start_overall);
    echo "Entire collection cycle took $tot.\n";
    unset($tot);
  }
  save_cachefile();
  if ($runasdaemon != "y") {
    break;
  }
  if ($debug) {
    echo "Sleeping for $delay seconds...\n" .
         "-----------------------------------------------------------------------\n\n";
    flush();
  }
  sleep($delay);
}
unlink($location["snmp_pidfile"]);
?>
