Index: html/pages/device/apps/bind.inc.php
===================================================================
--- html/pages/device/apps/bind.inc.php	(revision 0)
+++ html/pages/device/apps/bind.inc.php	(revision 0)
@@ -0,0 +1,57 @@
+<?php
+
+global $config;
+
+$app_sections = array('stats' => "Server statistics",
+                      'auth' => "Authoritative",
+                      'resolv' => "Resolving",
+                      'queries' => "Queries");
+
+print_optionbar_start();
+echo('<span style="font-weight: bold;">'.$app["app_type"].'</span> &#187; ');
+unset($sep);
+foreach ($app_sections as $app_section => $app_section_text)
+{
+    echo($sep);
+    if (!$vars['app_section']) { $vars['app_section'] = $app_section; }
+    if ($vars['app_section'] == $app_section)
+    {
+        echo("<span class='pagemenu-selected'>");
+    }
+    echo(generate_link($app_section_text,$vars,array('app_section'=>$app_section)));
+    if ($vars['app_section'] == $app_section) { echo("</span>"); }
+    $sep = " | ";
+}
+print_optionbar_end();
+
+$graphs['stats'] = array('bind_req_in'  => "Incoming requests",
+                         'bind_answers' => "Answers Given",
+                         'bind_updates' => "Dynamic Updates",
+                         'bind_req_proto' => "Request protocol details");
+
+$graphs['auth'] = array('bind_zone_maint' => "Zone maintenance");
+
+$graphs['resolv'] = array('bind_resolv_queries' => "Queries",
+                          'bind_resolv_errors' => "Errors",
+                          'bind_resolv_rtt' => "Query RTT",
+                          'bind_resolv_dnssec' => "DNSSEC validation");
+                          
+
+$graphs['queries'] = array('bind_query_rejected' => "Rejected queries",
+                           'bind_query_in' => "Incoming queries",
+                           'bind_query_out' => "Outgoing queries");
+
+foreach ($graphs[$vars['app_section']] as $key => $text) {
+    $graph_type            = $key;
+    $graph_array['to']     = $config['time']['now'];
+    $graph_array['id']     = $app['app_id'];
+    $graph_array['type']   = "application_".$key;
+    echo("<h4>".$text."</h3>");
+    echo("<tr bgcolor='$row_colour'><td colspan=5>");
+
+    include("includes/print-graphrow.inc.php");
+
+    echo("</td></tr>");
+}
+
+?>
Index: html/includes/graphs/application/bind_resolv_rtt.inc.php
===================================================================
--- html/includes/graphs/application/bind_resolv_rtt.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_resolv_rtt.inc.php	(revision 0)
@@ -0,0 +1,36 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Count";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-resolver-default.rrd";
+
+$array = array(
+  'QryRTT10' => array('descr' => "< 10ms", 'colour' => '00d200'),
+  'QryRTT100' => array('descr' => "10-100ms", 'colour' => '26ac00'),
+  'QryRTT500' => array('descr' => "100-500ms", 'colour' => '498900'),
+  'QryRTT800' => array('descr' => "500-800ms", 'colour' => '894900'),
+  'QryRTT1600' => array('descr' => "800-1600ms", 'colour' => 'ac2600'),
+  'QryRTT1600plus' => array('descr' => "> 1600ms", 'colour' => 'd20000'),
+);
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $rrd_list[$i]['colour']   = $data['colour'];
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_answers.inc.php
===================================================================
--- html/includes/graphs/application/bind_answers.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_answers.inc.php	(revision 0)
@@ -0,0 +1,42 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Count";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-ns-stats.rrd";
+
+$array = array(
+  'Response' => array('descr' => "Responses sent", 'colour' => '999999'),
+  'QrySuccess' => array('descr' => "Successful answers", 'colour' => '33cc33'),
+  'QryAuthAns' => array('descr' => "Authoritative answer", 'colour' => '009900'),
+  'QryNoauthAns' => array('descr' => "Non-authoritative answer", 'colour' => '336633'),
+  'QryReferral' => array('descr' => "Referral answer", 'colour' => '996633'),
+  'QryNxrrset' => array('descr' => "Empty answers", 'colour' => '36393d'),
+  'QrySERVFAIL' => array('descr' => "SERVFAIL answer", 'colour' => 'ff3333'),
+  'QryFORMERR' => array('descr' => "FORMERR answer", 'colour' => 'ffcccc'),
+  'QryNXDOMAIN' => array('descr' => "NXDOMAIN answers", 'colour' => 'ff33ff'),
+  'QryDropped' => array('descr' => "Dropped queries", 'colour' => '666666'),
+  'QryFailure' => array('descr' => "Failed queries", 'colour' => 'ff0000'),
+  'XfrReqDone' => array('descr' => "Transfers completed", 'colour' => '6666ff'),
+);
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $rrd_list[$i]['colour']   = $data['colour'];
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi_line.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_zone_maint.inc.php
===================================================================
--- html/includes/graphs/application/bind_zone_maint.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_zone_maint.inc.php	(revision 0)
@@ -0,0 +1,43 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Count";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-zone-maint.rrd";
+
+$array = array(
+  'NotifyOutv4' => array('descr' => "Notifies sent IPv4", 'colour' => '87cefa'),
+  'NotifyOutv6' => array('descr' => "Notifies sent IPv6", 'colour' => '00bfff'),
+  'NotifyInv4' => array('descr' => "Notifies received IPv4", 'colour' => '3cb371'),
+  'NotifyInv6' => array('descr' => "Notifies received IPv6", 'colour' => '2e8b57'),
+  'NotifyRej' => array('descr' => "Notifies rejected", 'colour' => 'ff8c00'),
+  'SOAOutv4' => array('descr' => "SOA queries sent IPv4", 'colour' => 'daa520'),
+  'SOAOutv6' => array('descr' => "SOA queries sent IPv6", 'colour' => 'b8860b'),
+  'AXFRReqv4' => array('descr' => "AXFR requested IPv4", 'colour' => 'da70d6'),
+  'AXFRReqv6' => array('descr' => "AXFR requested IPv6", 'colour' => '9932cc'),
+  'IXFRReqv4' => array('descr' => "IXFR requested IPv4", 'colour' => 'ff69b4'),
+  'IXFRReqv6' => array('descr' => "IXFR requested IPv6", 'colour' => 'ff1493'),
+  'XfrSuccess' => array('descr' => "Successful transfer", 'colour' => '32cd32'),
+  'XfrFail' => array('descr' => "Failed transfer", 'colour' => 'ff0000'),
+);
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $rrd_list[$i]['colour']   = $data['colour'];
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi_line.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_updates.inc.php
===================================================================
--- html/includes/graphs/application/bind_updates.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_updates.inc.php	(revision 0)
@@ -0,0 +1,37 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Count";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-ns-stats.rrd";
+
+$array = array(
+  'UpdateDone' => array('descr' => "Completed", 'colour' => '228b22'),
+  'UpdateFail' => array('descr' => "Failed", 'colour' => 'ff0000'),
+  'UpdateRej' => array('descr' => "Rejected", 'colour' => 'cd853f'),
+  'UpdateBadPrereq' => array('descr' => "Rejected due to prereq fail", 'colour' => 'ff8c00'),
+  'UpdateReqFwd' => array('descr' => "Fwd request", 'colour' => '6495ed'),
+  'UpdateRespFwd' => array('descr' => "Fwd response", 'colour' => '40e0d0'),
+  'UpdateFwdFail' => array('descr' => "Fwd failed", 'colour' => 'ffd700'),
+);
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $rrd_list[$i]['colour']   = $data['colour'];
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_resolv_queries.inc.php
===================================================================
--- html/includes/graphs/application/bind_resolv_queries.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_resolv_queries.inc.php	(revision 0)
@@ -0,0 +1,39 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Count";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-resolver-default.rrd";
+
+$array = array(
+  'Queryv4' => array('descr' => "Queries sent IPv4", 'colour' => '87cefa'),
+  'Responsev4' => array('descr' => "Responses received IPv4", 'colour' => '00bfff'),
+  'Queryv6' => array('descr' => "Queries sent IPv6", 'colour' => 'ff69b4'),
+  'Responsev6' => array('descr' => "Responses received IPv6", 'colour' => 'ff1493'),
+  'NXDOMAIN' => array('descr' => "NXDOMAIN received", 'colour' => 'ffa07a', 'invert' => True),
+  'SERVFAIL' => array('descr' => "SERVFAIL received", 'colour' => 'ff6533', 'invert' => True),
+  'FORMERR' => array('descr' => "FORMERR received", 'colour' => 'ff8c00', 'invert' => True),
+  'OtherError' => array('descr' => "Other error received", 'colour' => 'ff0000', 'invert' => True),
+);
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $rrd_list[$i]['colour']   = $data['colour'];
+        $rrd_list[$i]['invert']   = $data['invert'];
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi_line.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_query_out.inc.php
===================================================================
--- html/includes/graphs/application/bind_query_out.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_query_out.inc.php	(revision 0)
@@ -0,0 +1,37 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Requests";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-query-out-default.rrd";
+
+#$rrtypes = array('A', 'AAAA', 'PTR', 'ANY', 'IXFR', 'AXFR');
+$rrtypes = array('SOA', 'A', 'AAAA', 'NS', 'MX', 'CNAME', 'DNAME', 'TXT', 'SPF', 'SRV', 'SSHFP', 'TLSA', 'IPSECKEY', 'PTR', 'DNSKEY', 'RRSIG', 'NSEC', 'NSEC3', 'NSEC3PARAM', 'DS', 'DLV', 'ANY', 'IXFR', 'AXFR');
+$inverted = array('ANY', 'IXFR', 'AXFR');
+$array = array();
+foreach ($rrtypes as $rrtype)
+{
+ $array[$rrtype] = array('descr' => $rrtype);
+}
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+  foreach ($array as $ds => $data)
+  {
+    $rrd_list[$i]['filename'] = $rrd_filename;
+    $rrd_list[$i]['descr']    = $data['descr'];
+    $rrd_list[$i]['ds']       = $ds;
+    $rrd_list[$i]['invert']   = in_array($data['descr'], $inverted);
+    $i++;
+  }
+} else {
+  echo("file missing: $file");
+}
+
+#include("includes/graphs/generic_multi_line.inc.php");
+include("includes/graphs/generic_multi.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_req_proto.inc.php
===================================================================
--- html/includes/graphs/application/bind_req_proto.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_req_proto.inc.php	(revision 0)
@@ -0,0 +1,41 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Count";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-ns-stats.rrd";
+
+$array = array(
+  'Requestv4' => array('descr' => "IPv4 requests", 'colour' => '006600'),
+  'Requestv6' => array('descr' => "IPv6 requests", 'colour' => '66cc66'),
+  'ReqEdns0' => array('descr' => "EDNS(0) requests", 'colour' => '9999ff'),
+  'RespEDNS0' => array('descr' => "EDNS(0) responses", 'colour' => '6666ff'),
+  'ReqTSIG' => array('descr' => "TSIG requests", 'colour' => 'ff9999'),
+  'RespTSIG' => array('descr' => "TSIG responses", 'colour' => 'ff6666'),
+  'ReqSIG0' => array('descr' => "SIG(0) requests", 'colour' => 'da70d6'),
+  'RespSIG0' => array('descr' => "responses with SIG(0) sent", 'colour' => '9932cc'),
+  'ReqTCP' => array('descr' => "TCP requests", 'colour' => 'ffd700'),
+  'Response' => array('descr' => "Responses sent", 'colour' => '999999'),
+  'TruncatedResp' => array('descr' => "Truncated Responses", 'colour' => 'ff0000'),
+);
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $rrd_list[$i]['colour']   = $data['colour'];
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi_line.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_query_in.inc.php
===================================================================
--- html/includes/graphs/application/bind_query_in.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_query_in.inc.php	(revision 0)
@@ -0,0 +1,37 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Requests";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-query-in.rrd";
+
+#$rrtypes = array('A', 'AAAA', 'PTR', 'ANY', 'IXFR', 'AXFR');
+$rrtypes = array('SOA', 'A', 'AAAA', 'NS', 'MX', 'CNAME', 'DNAME', 'TXT', 'SPF', 'SRV', 'SSHFP', 'TLSA', 'IPSECKEY', 'PTR', 'DNSKEY', 'RRSIG', 'NSEC', 'NSEC3', 'NSEC3PARAM', 'DS', 'DLV', 'ANY', 'IXFR', 'AXFR');
+$inverted = array('ANY', 'IXFR', 'AXFR');
+$array = array();
+foreach ($rrtypes as $rrtype)
+{
+ $array[$rrtype] = array('descr' => $rrtype);
+}
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+  foreach ($array as $ds => $data)
+  {
+    $rrd_list[$i]['filename'] = $rrd_filename;
+    $rrd_list[$i]['descr']    = $data['descr'];
+    $rrd_list[$i]['ds']       = $ds;
+    $rrd_list[$i]['invert']   = in_array($data['descr'], $inverted);
+    $i++;
+  }
+} else {
+  echo("file missing: $file");
+}
+
+#include("includes/graphs/generic_multi_line.inc.php");
+include("includes/graphs/generic_multi.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_resolv_errors.inc.php
===================================================================
--- html/includes/graphs/application/bind_resolv_errors.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_resolv_errors.inc.php	(revision 0)
@@ -0,0 +1,38 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Count";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-resolver-default.rrd";
+
+$array = array(
+  'EDNS0Fail' => array('descr' => "EDNS(0) query failures", 'colour' => '87cefa'),
+  'Mismatch' => array('descr' => "Mismatch responses received", 'colour' => '00bfff'),
+  'Truncated' => array('descr' => "Truncated responses received", 'colour' => 'ff69b4'),
+  'Lame' => array('descr' => "Lame delegations received", 'colour' => 'ff1493'),
+  'Retry' => array('descr' => "Retried queries", 'colour' => 'ffa07a'),
+  'QueryAbort' => array('descr' => "Aborted due to quota", 'colour' => 'ff6533'),
+  'QuerySockFail' => array('descr' => "Socket errors", 'colour' => 'ff8c00'),
+  'QueryTimeout' => array('descr' => "Timeouts", 'colour' => 'ff0000'),
+);
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $rrd_list[$i]['colour']   = $data['colour'];
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi_line.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_resolv_dnssec.inc.php
===================================================================
--- html/includes/graphs/application/bind_resolv_dnssec.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_resolv_dnssec.inc.php	(revision 0)
@@ -0,0 +1,35 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Count";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-resolver-default.rrd";
+
+$array = array(
+  'ValAttempt' => array('descr' => "Attempted validation", 'colour' => '4242CC', 'invert' => True),
+  'ValOk' => array('descr' => "Succeeded validation", 'colour' => '33A533'),
+  'ValNegOk' => array('descr' => "NX Succeeded validation", 'colour' => 'FFA500'),
+  'ValFail' => array('descr' => "Failed validation", 'colour' => 'ff0000'),
+);
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $rrd_list[$i]['colour']   = $data['colour'];
+        $rrd_list[$i]['invert']   = $data['invert'];
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_query_rejected.inc.php
===================================================================
--- html/includes/graphs/application/bind_query_rejected.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_query_rejected.inc.php	(revision 0)
@@ -0,0 +1,35 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Count";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-ns-stats.rrd";
+
+$array = array(
+  'AuthQryRej' => array('descr' => "Auth queries rejected", 'colour' => '6495ed'),
+  'RecQryRej' => array('descr' => "Recursive queries rejected", 'colour' => '40e0d0'),
+  'XfrRej' => array('descr' => "Transfer requests rejected", 'colour' => 'ffd700'),
+  'UpdateRej' => array('descr' => "Update requests rejected", 'colour' => 'cd853f'),
+  'UpdateBadPrereq' => array('descr' => "Updates rejected due to prereq fail", 'colour' => 'ff8c00'),
+);
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $rrd_list[$i]['colour']   = $data['colour'];
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi.inc.php");
+
+?>
Index: html/includes/graphs/application/bind_req_in.inc.php
===================================================================
--- html/includes/graphs/application/bind_req_in.inc.php	(revision 0)
+++ html/includes/graphs/application/bind_req_in.inc.php	(revision 0)
@@ -0,0 +1,33 @@
+<?php
+
+include("includes/graphs/common.inc.php");
+
+$colours      = "mixed";
+$nototal      = (($width<224) ? 1 : 0);
+$unit_text    = "Requests";
+$rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-req-in.rrd";
+
+$array = array(
+               'query' => array('descr' => 'Query'),
+               'status' => array('descr' => 'Status'),
+               'notify' => array('descr' => 'Notify'),
+               'update' => array('descr' => 'Update'),
+               );
+$i = 0;
+
+if (is_file($rrd_filename))
+{
+    foreach ($array as $ds => $data)
+    {
+        $rrd_list[$i]['filename'] = $rrd_filename;
+        $rrd_list[$i]['descr']    = $data['descr'];
+        $rrd_list[$i]['ds']       = $ds;
+        $i++;
+    }
+} else {
+    echo("file missing: $file");
+}
+
+include("includes/graphs/generic_multi_line.inc.php");
+
+?>
Index: includes/polling/applications/bind.inc.php
===================================================================
--- includes/polling/applications/bind.inc.php	(revision 0)
+++ includes/polling/applications/bind.inc.php	(revision 0)
@@ -0,0 +1,359 @@
+<?php
+
+if (!empty($agent_data['app']['bind']['global']))
+{
+    $app_id = dbFetchCell("SELECT app_id FROM `applications` WHERE `device_id` = ? AND `app_instance` = ?", array($device['device_id'], $memcached_host));
+
+  # Prepare data arrays
+  # -------------------
+  $rrtypes = array('SOA', 'ANY', 'A', 'AAAA', 'NS', 'MX', 'CNAME', 'DNAME', 'TXT', 'SPF', 'SRV', 'SSHFP', 'TLSA', 'IPSECKEY', 'PTR', 'DNSKEY', 'RRSIG', 'NSEC', 'NSEC3', 'NSEC3PARAM', 'DS', 'DLV', 'IXFR', 'AXFR');
+
+  # Requests incoming
+  $req_in = array(
+    'QUERY' => 0,
+    'STATUS' => 0,
+    'NOTIFY' => 0,
+    'UPDATE' => 0,
+  );
+
+  # Query incoming
+  $query_in = array();
+  foreach ($rrtypes as $rrtype)
+  {
+    $query_in[$rrtype] = 0;
+  }
+
+  # Query outgoing
+  $query_out = array();
+
+  # Name server statistics
+  $ns_stats_field_mapping = array(
+    "IPv4 requests received" => 'Requestv4',
+    "IPv6 requests received" => 'Requestv6',
+    "requests with EDNS(0) received" => 'ReqEdns0',
+    "requests with unsupported EDNS version received" => 'ReqBadEDNSVer',
+    "requests with TSIG received" => 'ReqTSIG',
+    "requests with SIG(0) received" => 'ReqSIG0',
+    "requests with invalid signature" => 'ReqBadSIG',
+    "TCP requests received" => 'ReqTCP',
+    "auth queries rejected" => 'AuthQryRej',
+    "recursive queries rejected" => 'RecQryRej',
+    "transfer requests rejected" => 'XfrRej',
+    "update requests rejected" => 'UpdateRej',
+    "responses sent" => 'Response',
+    "truncated responses sent" => 'TruncatedResp',
+    "responses with EDNS(0) sent" => 'RespEDNS0',
+    "responses with TSIG sent" => 'RespTSIG',
+    "responses with SIG(0) sent" => 'RespSIG0',
+    "queries resulted in successful answer" => 'QrySuccess',
+    "queries resulted in authoritative answer" => 'QryAuthAns',
+    "queries resulted in non authoritative answer" => 'QryNoauthAns',
+    "queries resulted in referral answer" => 'QryReferral',
+    "queries resulted in nxrrset" => 'QryNxrrset',
+    "queries resulted in SERVFAIL" => 'QrySERVFAIL',
+    "queries resulted in FORMERR" => 'QryFORMERR',
+    "queries resulted in NXDOMAIN" => 'QryNXDOMAIN',
+    "queries caused recursion" => 'QryRecursion',
+    "duplicate queries received" => 'QryDuplicate',
+    "queries dropped" => 'QryDropped',
+    "other query failures" => 'QryFailure',
+    "requested transfers completed" => 'XfrReqDone',
+    "update requests forwarded" => 'UpdateReqFwd',
+    "update responses forwarded" => 'UpdateRespFwd',
+    "update forward failed" => 'UpdateFwdFail',
+    "updates completed" => 'UpdateDone',
+    "updates failed" => 'UpdateFail',
+    "updates rejected due to prerequisite failure" => 'UpdateBadPrereq',
+    "response policy zone rewrites" => 'RPZRewrites',
+  );
+
+  $ns_stats_fields = array_values($ns_stats_field_mapping);
+  array_sort($ns_stats_fields);
+
+  $ns_stats = array();
+  foreach ($ns_stats_fields as $field)
+  {
+    $ns_stats[$field] = 0;
+  }
+
+  # Zone maintenance
+  $zone_maint_field_mapping = array(
+    "IPv4 notifies sent" => 'NotifyOutv4',
+    "IPv6 notifies sent" => 'NotifyOutv6',
+    "IPv4 notifies received" => 'NotifyInv4',
+    "IPv6 notifies received" => 'NotifyInv6',
+    "notifies rejected" => 'NotifyRej',
+    "IPv4 SOA queries sent" => 'SOAOutv4',
+    "IPv6 SOA queries sent" => 'SOAOutv6',
+    "IPv4 AXFR requested" => 'AXFRReqv4',
+    "IPv6 AXFR requested" => 'AXFRReqv6',
+    "IPv4 IXFR requested" => 'IXFRReqv4',
+    "IPv6 IXFR requested" => 'IXFRReqv6',
+    "transfer requests succeeded" => 'XfrSuccess',
+    "transfer requests failed" => 'XfrFail',
+  );
+
+  $zone_maint_fields = array_values($zone_maint_field_mapping);
+  array_sort($zone_maint_fields);
+
+  $zone_maint = array();
+  foreach ($zone_maint_fields as $field)
+  {
+    $zone_maint[$field] = 0;
+  }
+
+  # Resolver
+  $resolver_field_mapping = array(
+    "IPv4 queries sent" => 'Queryv4',
+    "IPv6 queries sent" => 'Queryv6',
+    "IPv4 responses received" => 'Responsev4',
+    "IPv6 responses received" => 'Responsev6',
+    "NXDOMAIN received" => 'NXDOMAIN',
+    "SERVFAIL received" => 'SERVFAIL',
+    "FORMERR received" => 'FORMERR',
+    "other errors received" => 'OtherError',
+    "EDNS(0) query failures" => 'EDNS0Fail',
+    "mismatch responses received" => 'Mismatch',
+    "truncated responses received" => 'Truncated',
+    "lame delegations received" => 'Lame',
+    "query retries" => 'Retry',
+    "queries aborted due to quota" => 'QueryAbort',
+    "failures in opening query sockets" => 'QuerySockFail',
+    "query timeouts" => 'QueryTimeout',
+    "IPv4 NS address fetches" => 'GlueFetchv4',
+    "IPv6 NS address fetches" => 'GlueFetchv6',
+    "IPv4 NS address fetch failed" => 'GlueFetchv4Fail',
+    "IPv6 NS address fetch failed" => 'GlueFetchv6Fail',
+    "DNSSEC validation attempted" => 'ValAttempt',
+    "DNSSEC validation succeeded" => 'ValOk',
+    "DNSSEC NX validation succeeded" => 'ValNegOk',
+    "DNSSEC validation failed" => 'ValFail',
+    "queries with RTT < 10ms" => 'QryRTT10',
+    "queries with RTT 10-100ms" => 'QryRTT100',
+    "queries with RTT 100-500ms" => 'QryRTT500',
+    "queries with RTT 500-800ms" => 'QryRTT800',
+    "queries with RTT 800-1600ms" => 'QryRTT1600',
+    "queries with RTT > 1600ms" => 'QryRTT1600plus',
+  );
+
+  $resolver_fields = array_values($resolver_field_mapping);
+  array_sort($resolver_fields);
+
+  $resolver = array();
+
+  # Store the data in arrays
+  # ------------------------
+  $lines = explode("\n", $agent_data['app']['bind']['global']);
+  foreach ($lines as $line) {
+    # Line format is "key:value"
+    list ($key, $value) = explode(':', $line);
+
+    # Keys consist of "section,subkey"
+    list ($section, $subkey) = explode(',', $key, 2);
+
+    # The subkey depends on the section
+    if ($section == 'req-in')
+    {
+      # Subkey is the opcode
+      $req_in[$subkey] = (int) $value;
+    }
+    elseif ($section == 'query-in')
+    {
+      # Subkey is the RRType
+      $query_in[$subkey] = (int) $value;
+    }
+    elseif ($section == 'ns-stats')
+    {
+      # Subkey is description
+      if (isset($ns_stats_field_mapping[$subkey]))
+      {
+        $subkey = $ns_stats_field_mapping[$subkey];
+      }
+      $ns_stats[$subkey] = (int) $value;
+    }
+    elseif ($section == 'zone-maint')
+    {
+      # Subkey is description
+      if (isset($zone_maint_field_mapping[$subkey]))
+      {
+        $subkey = $zone_maint_field_mapping[$subkey];
+      }
+      $zone_maint[$subkey] = (int) $value;
+    }
+  }
+
+  # Done with the global stuff
+  unset($agent_data['app']['bind']['global']);
+
+  # The rest is views
+  foreach ($agent_data['app']['bind'] as $view => $view_data)
+  {
+    $lines = explode("\n", $agent_data['app']['bind'][$view]);
+    foreach ($lines as $line) {
+      # Line format is "key:value"
+      list ($key, $value) = explode(':', $line);
+
+      # Keys consist of "section,subkey"
+      list ($section, $subkey) = explode(',', $key, 2);
+
+      # The subkey depends on the section
+      if ($section == 'query-out')
+      {
+        # Create the view if it doesn't exist yet
+        if (!isset($query_out[$view]))
+        {
+          foreach ($rrtypes as $rrtype)
+          {
+            $query_out[$view][$rrtype] = 0;
+          }
+        }
+
+        # Subkey is the RRType
+        $query_out[$view][$subkey] = (int) $value;
+      }
+      elseif ($section == 'resolver')
+      {
+        # Create the view if it doesn't exist yet
+        if (!isset($resolver[$view]))
+        {
+          foreach ($resolver_fields as $field)
+          {
+            $resolver[$view][$field] = 0;
+          }
+        }
+        
+        # Subkey is the description
+        if (isset($resolver_field_mapping[$subkey]))
+        {
+          $subkey = $resolver_field_mapping[$subkey];
+        }
+        $resolver[$view][$subkey] = (int) $value;
+      }
+    }
+  }
+
+  # Use the data from the arrays to build RRDs
+  # ------------------------------------------
+
+  # rrdcreate list of rrtypes
+  $rrdcreate_rrtypes = "";
+  foreach ($rrtypes as $rrtype)
+  {
+    $rrdcreate_rrtypes .= " DS:$rrtype:DERIVE:600:0:7500000";
+  }
+
+  # req-in
+  $rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-req-in.rrd";
+
+  if (!is_file($rrd_filename))
+  {
+    rrdtool_create($rrd_filename, "--step 300 \
+        DS:query:DERIVE:600:0:7500000 \
+        DS:status:DERIVE:600:0:7500000 \
+        DS:notify:DERIVE:600:0:7500000 \
+        DS:update:DERIVE:600:0:7500000 ".$config['rrd_rra']);
+  }
+
+  rrdtool_update($rrd_filename,  "N:$req_in[QUERY]:$req_in[STATUS]:$req_in[NOTIFY]:$req_in[UPDATE]");
+
+  # query-in
+  $rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-query-in.rrd";
+
+  if (!is_file($rrd_filename))
+  {
+    rrdtool_create($rrd_filename, "--step 300 $rrdcreate_rrtypes ".$config['rrd_rra']);
+  }
+
+  $rrd_data = "";
+  foreach ($rrtypes as $rrtype)
+  {
+    $rrd_data .= ":".$query_in[$rrtype];
+  }
+  rrdtool_update($rrd_filename,  "N".$rrd_data);
+
+  # ns-stats
+  $rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-ns-stats.rrd";
+
+  if (!is_file($rrd_filename))
+  {
+    # rrdcreate list of fields
+    $rrdcreate_ns_stats = "";
+    foreach ($ns_stats_fields as $field)
+    {
+      $rrdcreate_ns_stats .= " DS:$field:DERIVE:600:0:7500000";
+    }
+    rrdtool_create($rrd_filename, "--step 300 $rrdcreate_ns_stats ".$config['rrd_rra']);
+  }
+
+  $rrd_data = "";
+  foreach ($ns_stats_fields as $field)
+  {
+    $rrd_data .= ":".$ns_stats[$field];
+  }
+  rrdtool_update($rrd_filename,  "N".$rrd_data);
+
+  # zone-maint
+  $rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-zone-maint.rrd";
+
+  if (!is_file($rrd_filename))
+  {
+    # rrdcreate list of fields
+    $rrdcreate_zone_maint = "";
+    foreach ($zone_maint_fields as $field)
+    {
+      $rrdcreate_zone_maint .= " DS:$field:DERIVE:600:0:7500000";
+    }
+    rrdtool_create($rrd_filename, "--step 300 $rrdcreate_zone_maint ".$config['rrd_rra']);
+  }
+
+  $rrd_data = "";
+  foreach ($zone_maint_fields as $field)
+  {
+    $rrd_data .= ":".$zone_maint[$field];
+  }
+  rrdtool_update($rrd_filename,  "N".$rrd_data);
+
+  # query-out
+  foreach ($query_out as $view => $view_data)
+  {
+    $rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-query-out-".$view.".rrd";
+
+    if (!is_file($rrd_filename))
+    {
+      rrdtool_create($rrd_filename, "--step 300 $rrdcreate_rrtypes ".$config['rrd_rra']);
+    }
+
+    $rrd_data = "";
+    foreach ($rrtypes as $rrtype)
+    {
+      $rrd_data .= ":".$view_data[$rrtype];
+    }
+    rrdtool_update($rrd_filename,  "N".$rrd_data);
+  }
+
+  # resolver
+  foreach ($resolver as $view => $view_data)
+  {
+    $rrd_filename = $config['rrd_dir'] . "/" . $device['hostname'] . "/app-bind-".$app['app_id']."-resolver-".$view.".rrd";
+
+    if (!is_file($rrd_filename))
+    {
+      # rrdcreate list of fields
+      $rrdcreate_resolver = "";
+      foreach ($resolver_fields as $field)
+      {
+        $rrdcreate_resolver .= " DS:$field:DERIVE:600:0:7500000";
+      }
+      rrdtool_create($rrd_filename, "--step 300 $rrdcreate_resolver ".$config['rrd_rra']);
+    }
+
+    $rrd_data = "";
+    foreach ($resolver_fields as $field)
+    {
+      $rrd_data .= ":".$view_data[$field];
+    }
+    rrdtool_update($rrd_filename,  "N".$rrd_data);
+  }
+}
+
+?>
Index: includes/definitions.inc.php
===================================================================
--- includes/definitions.inc.php	(revision 4326)
+++ includes/definitions.inc.php	(working copy)
@@ -1529,6 +1529,19 @@
 $config['graph_descr']['application_unbound_rcode']   = "Answers sorted by return value. RRSets bogus is the number of RRSets marked bogus per second by the validator.";
 $config['graph_descr']['application_unbound_flags']   = "This graphs plots the flags inside incoming queries. For example, if QR, AA, TC, RA, Z flags are set, the query can be rejected. RD, AD, CD and DO are legitimately set by some software.";
 
+$config['graph_types']['application']['bind_answers']['descr'] = 'BIND Received Answers';
+$config['graph_types']['application']['bind_query_in']['descr'] = 'BIND Incoming Queries';
+$config['graph_types']['application']['bind_query_out']['descr'] = 'BIND Outgoing Queries';
+$config['graph_types']['application']['bind_query_rejected']['descr'] = 'BIND Rejected Queries';
+$config['graph_types']['application']['bind_req_in']['descr'] = 'BIND Incoming Requests';
+$config['graph_types']['application']['bind_req_proto']['descr'] = 'BIND Request Protocol Details';
+$config['graph_types']['application']['bind_resolv_dnssec']['descr'] = 'BIND DNSSEC Validation';
+$config['graph_types']['application']['bind_resolv_errors']['descr'] = 'BIND Errors while Resolving';
+$config['graph_types']['application']['bind_resolv_queries']['descr'] = 'BIND Resolving Queries';
+$config['graph_types']['application']['bind_resolv_rtt']['descr'] = 'BIND Resolving RTT';
+$config['graph_types']['application']['bind_updates']['descr'] = 'BIND Dynamic Updates';
+$config['graph_types']['application']['bind_zone_maint']['descr'] = 'BIND Zone Maintenance';
+
 // Device Types
 
 $i = 0;
Index: scripts/agent-local/bind
===================================================================
--- scripts/agent-local/bind	(revision 0)
+++ scripts/agent-local/bind	(revision 0)
@@ -0,0 +1,173 @@
+#!/bin/bash
+
+STATS_FILE=/var/cache/bind/named.stats
+
+# Remove old stats file
+rm -f $STATS_FILE
+
+# Generate new stats
+/usr/sbin/rndc stats
+
+# Delay until the stats file is written
+i=0
+while [ ! -f $STATS_FILE ]; do
+	# We don't wait forever
+	if [ $i -ge 10 ]; then
+		exit 1
+	fi
+	i=$[ $i + 1 ]
+	
+	# Wait a little
+	sleep 0.2
+done
+
+# Save view specific stuff
+declare -A VIEWS
+VIEWS[global]=""
+
+# Read the stats file and parse it
+MODE="unknown"
+while read LINE; do
+	if [[ "$LINE" =~ ^-- ]]; then
+		# Statistics, skip this line
+		continue
+	elif [[ "$LINE" =~ ^\+\+ ]]; then
+		# This is a section start, switch mode
+		if [ "$LINE" == "++ Incoming Requests ++" ]; then
+			MODE="req-in"
+		elif [ "$LINE" == "++ Incoming Queries ++" ]; then
+			MODE="query-in"
+		elif [ "$LINE" == "++ Outgoing Queries ++" ]; then
+			MODE="query-out"
+			VIEW="default"
+		elif [ "$LINE" == "++ Name Server Statistics ++" ]; then
+			MODE="ns-stats"
+		elif [ "$LINE" == "++ Zone Maintenance Statistics ++" ]; then
+			MODE="zone-maint"
+		elif [ "$LINE" == "++ Resolver Statistics ++" ]; then
+			MODE="resolver"
+			VIEW="default"
+		elif [ "$LINE" == "++ Cache DB RRsets ++" ]; then
+			MODE="cache"
+			VIEW="default"
+		elif [ "$LINE" == "++ Per Zone Query Statistics ++" ]; then
+			MODE="zone"
+			VIEW="default"
+			ZONE="unknown"
+		else
+			MODE="unknown"
+		fi
+		
+		continue
+	fi
+	
+	# Parse based on current mode
+	if [ "$MODE" == "req-in" ]; then
+		# Line format is: <count> <opcode>
+		read REQ_COUNT REQ_OPCODE <<< $( echo $LINE )
+		VIEWS[global]+="req-in,$REQ_OPCODE:$REQ_COUNT"$'\n'
+	elif [ "$MODE" == "query-in" ]; then
+		# Line format is: <count> <rrtype>
+		read QUERY_COUNT QUERY_RRTYPE <<< $( echo $LINE )
+		VIEWS[global]+="query-in,$QUERY_RRTYPE:$QUERY_COUNT"$'\n'
+	elif [ "$MODE" == "query-out" ]; then
+		# Is this a section?
+		if [[ "$LINE" =~ ^\[ ]]; then
+			# Is this a view start?
+			NEW_VIEW=$( echo "$LINE" | sed -n 's/^\[View: \(.*\)\]$/\1/p' )
+			if [ -n "$NEW_VIEW" ]; then
+				VIEW="$NEW_VIEW"
+			fi
+			
+			continue
+		fi
+		
+		# Ignore the _bind view
+		if [ "$VIEW" == "_bind" ]; then
+			continue
+		fi
+
+		# Line format is: <count> <rrtype>
+		read QUERY_COUNT QUERY_RRTYPE <<< $( echo $LINE )
+		VIEWS[$VIEW]+="query-out,$QUERY_RRTYPE:$QUERY_COUNT"$'\n'
+	elif [ "$MODE" == "ns-stats" ]; then
+		# Line format is: <count> <action>
+		read COUNT ACTION <<< $( echo $LINE )
+		VIEWS[global]+="ns-stats,$ACTION:$COUNT"$'\n'
+	elif [ "$MODE" == "zone-maint" ]; then
+		# Line format is: <count> <action>
+		read COUNT ACTION <<< $( echo $LINE )
+		VIEWS[global]+="zone-maint,$ACTION:$COUNT"$'\n'
+	elif [ "$MODE" == "resolver" ]; then
+		# Is this a section?
+		if [[ "$LINE" =~ ^\[ ]]; then
+			# Is this a view start?
+			NEW_VIEW=$( echo "$LINE" | sed -n 's/^\[View: \(.*\)\]$/\1/p' )
+			if [ -n "$NEW_VIEW" ]; then
+				VIEW="$NEW_VIEW"
+			fi
+
+			continue
+		fi
+		
+		# Ignore the _bind view
+		if [ "$VIEW" == "_bind" ]; then
+			continue
+		fi
+
+		# Line format is: <count> <action>
+		read COUNT ACTION <<< $( echo $LINE )
+		VIEWS[$VIEW]+="resolver,$ACTION:$COUNT"$'\n'
+	elif [ "$MODE" == "cache" ]; then
+		# Is this a section?
+		if [[ "$LINE" =~ ^\[ ]]; then
+			# Is this a view start?
+			NEW_VIEW=$( echo "$LINE" | sed -n 's/^\[View: \(.*\)\]$/\1/p' )
+			if [ -n "$NEW_VIEW" ]; then
+				VIEW="$NEW_VIEW"
+			fi
+
+			continue
+		fi
+		
+		# Ignore the _bind view
+		if [ "$VIEW" == "_bind" ]; then
+			continue
+		fi
+
+		# Line format is: <count> <action>
+		read COUNT ACTION <<< $( echo $LINE )
+		VIEWS[$VIEW]+="cache,$ACTION:$COUNT"$'\n'
+	elif [ "$MODE" == "zone" ]; then
+		# Is this a section?
+		if [[ "$LINE" =~ ^\[ ]]; then
+			# Does this line define a view?
+			NEW_VIEW=$( echo "$LINE" | sed -n 's/^.* (view: \(.*\))\]$/\1/p' )
+			if [ -n "$NEW_VIEW" ]; then
+				VIEW="$NEW_VIEW"
+			fi
+
+			# Is this a zone start?
+			NEW_ZONE=$( echo "$LINE" | sed -n 's/^\[\([^] ]*\).*/\1/p' )
+			if [ -n "$NEW_ZONE" ]; then
+				ZONE="$NEW_ZONE"
+			fi
+			
+			continue
+		fi
+		
+		# Ignore the _bind view
+		if [ "$VIEW" == "_bind" ]; then
+			continue
+		fi
+
+		# Line format is: <count> <action>
+		read COUNT ACTION <<< $( echo $LINE )
+		VIEWS[$VIEW]+="$ZONE,$ACTION:$COUNT"$'\n'
+	fi
+done < $STATS_FILE
+
+for VIEW in "${!VIEWS[@]}"; do
+	echo "<<<app-bind-$VIEW>>>"
+	echo -n "${VIEWS[$VIEW]}"
+done

Property changes on: scripts/agent-local/bind
___________________________________________________________________
Added: svn:executable
   + *