*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgment:
* "This product includes software developed by the pfSense Project
* for use in the pfSense software distribution. (http://www.pfsense.org/).
*
* 4. The names "pfSense" and "pfSense Project" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* coreteam@pfsense.org.
*
* 5. Products derived from this software may not be called "pfSense"
* nor may "pfSense" appear in their names without prior written
* permission of the Electric Sheep Fencing, LLC.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
*
* "This product includes software developed by the pfSense Project
* for use in the pfSense software distribution (http://www.pfsense.org/).
*
* THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ====================================================================
*
*/
##|+PRIV
##|*IDENT=page-status-rrdgraphs
##|*NAME=Status: RRD Graphs
##|*DESCR=Allow access to the 'Status: RRD Graphs' page.
##|*MATCH=status_rrd_graph.php*
##|*MATCH=status_rrd_graph_img.php*
##|-PRIV
require("guiconfig.inc");
require_once("filter.inc");
require("shaper.inc");
require_once("rrd.inc");
global $rrd_graph_list, $rrd_period_list, $rrd_graph_length_list, $rrd_style_list;
unset($input_errors);
/* if the rrd graphs are not enabled redirect to settings page */
if (!isset($config['rrd']['enable'])) {
header("Location: status_rrd_graph_settings.php");
}
$home = getcwd();
$rrddbpath = "/var/db/rrd/";
chdir($rrddbpath);
$databases = glob("*.rrd");
chdir($home);
if ($_GET['cat']) {
$curcat = htmlspecialchars($_GET['cat']);
} else {
if (!empty($config['rrd']['category'])) {
$curcat = $config['rrd']['category'];
} else {
$curcat = "system";
}
}
if ($_POST['cat']) {
$curcat = htmlspecialchars($_POST['cat']);
}
if ($_GET['zone']) {
$curzone = $_GET['zone'];
} else {
$curzone = '';
}
if ($_POST['period']) {
$curperiod = $_POST['period'];
} else {
if (!empty($config['rrd']['period'])) {
$curperiod = $config['rrd']['period'];
} else {
$curperiod = "absolute";
}
}
if ($_POST['style']) {
$curstyle = $_POST['style'];
} else {
if (!empty($config['rrd']['style'])) {
$curstyle = $config['rrd']['style'];
} else {
$curstyle = "absolute";
}
}
if ($_POST['option']) {
$curoption = $_POST['option'];
} else {
switch ($curcat) {
case "system":
$curoption = "processor";
break;
case "queues":
$curoption = "queues";
break;
case "queuedrops":
$curoption = "queuedrops";
break;
case "quality":
foreach ($databases as $database) {
if (preg_match("/[-]quality\.rrd/i", $database)) {
/* pick off the 1st database we find that matches the quality graph */
$name = explode("-", $database);
$curoption = "$name[0]";
continue 2;
}
}
case "wireless":
foreach ($databases as $database) {
if (preg_match("/[-]wireless\.rrd/i", $database)) {
/* pick off the 1st database we find that matches the wireless graph */
$name = explode("-", $database);
$curoption = "$name[0]";
continue 2;
}
}
case "cellular":
foreach ($databases as $database) {
if (preg_match("/[-]cellular\.rrd/i", $database)) {
/* pick off the 1st database we find that matches the celullar graph */
$name = explode("-", $database);
$curoption = "$name[0]";
continue 2;
}
}
case "vpnusers":
foreach ($databases as $database) {
if (preg_match("/[-]vpnusers\.rrd/i", $database)) {
/* pick off the 1st database we find that matches the VPN graphs */
$name = explode("-", $database);
$curoption = "$name[0]";
continue 2;
}
}
case "dhcpd":
foreach ($databases as $database) {
if (preg_match("/[-]dhcpd\.rrd/i", $database)) {
/* pick off the 1st database we find that matches the dhcpd graph */
$name = explode("-", $database);
$curoption = "$name[0]";
continue 2;
}
}
case "captiveportal":
$curoption = "allgraphs";
break;
case "ntpd":
if (isset($config['ntpd']['statsgraph'])) {
$curoption = "allgraphs";
} else {
$curoption = "processor";
$curcat = "system";
}
break;
default:
$curoption = "wan";
break;
}
}
$now = time();
if ($curcat == "custom") {
if (is_numeric($_GET['start'])) {
if ($start < ($now - (3600 * 24 * 365 * 5))) {
$start = $now - (8 * 3600);
}
$start = $_POST['start'];
} else if ($_POST['start']) {
$start = strtotime($_POST['start']);
if ($start === FALSE || $start === -1) {
$input_errors[] = gettext("Invalid start date/time:") . " '{$_POST['start']}'";
$start = $now - (8 * 3600);
}
} else {
$start = $now - (8 * 3600);
}
}
if (is_numeric($_GET['end'])) {
$end = $_GET['end'];
} else if ($_GET['end']) {
$end = strtotime($_GET['end']);
if ($end === FALSE || $end === -1) {
$input_errors[] = gettext("Invalid end date/time:") . " '{$_POST['end']}'";
$end = $now;
}
} else {
$end = $now;
}
/* this should never happen */
if ($end < $start) {
log_error(sprintf(gettext("start %d is smaller than end %d"), $start, $end));
$end = $now;
}
$seconds = $end - $start;
/* sort names reverse so WAN comes first */
rsort($databases);
/* these boilerplate databases are required for the other menu choices */
$dbheader = array("allgraphs-traffic.rrd",
"allgraphs-quality.rrd",
"allgraphs-wireless.rrd",
"allgraphs-cellular.rrd",
"allgraphs-vpnusers.rrd",
"allgraphs-packets.rrd",
"system-allgraphs.rrd",
"system-throughput.rrd",
"outbound-quality.rrd",
"outbound-packets.rrd",
"outbound-traffic.rrd");
/* additional menu choices for the custom tab */
$dbheader_custom = array("system-throughput.rrd");
foreach ($databases as $database) {
if (stristr($database, "-wireless")) {
$wireless = true;
}
if (stristr($database, "-queues")) {
$queues = true;
}
if (stristr($database, "-cellular") && !empty($config['ppps'])) {
$cellular = true;
}
if (stristr($database, "-vpnusers")) {
$vpnusers = true;
}
if (stristr($database, "captiveportal-") && is_array($config['captiveportal'])) {
$captiveportal = true;
}
if (stristr($database, "ntpd") && isset($config['ntpd']['statsgraph'])) {
$ntpd = true;
}
if (stristr($database, "-dhcpd") && is_array($config['dhcpd'])) {
$dhcpd = true;
}
}
/* append the existing array to the header */
$ui_databases = array_merge($dbheader, $databases);
$custom_databases = array_merge($dbheader_custom, $databases);
switch ($curcat) {
case "vpnusers":
$curcattext = gettext("VPN Users");
break;
case "captiveportal":
$curcattext = gettext("Captive Portal");
break;
case "ntpd":
$curcattext = gettext("NTP");
break;
case "dhcpd":
$curcattext = gettext("DHCP Server");
break;
case "queuedrops":
$curcattext = gettext("QueueDrops");
break;
default:
$curcattext = ucfirst($curcat);
break;
}
$pgtitle = array(gettext("Status"), gettext("RRD Graphs"), gettext($curcattext));
/* Load all CP zones */
if ($captiveportal && is_array($config['captiveportal'])) {
$cp_zones_tab_array = array();
foreach ($config['captiveportal'] as $cpkey => $cp) {
if (!isset($cp['enable'])) {
continue;
}
if ($curzone == '') {
$tabactive = true;
$curzone = $cpkey;
} elseif ($curzone == $cpkey) {
$tabactive = true;
} else {
$tabactive = false;
}
$cp_zones_tab_array[] = array($cp['zone'], $tabactive, "status_rrd_graph.php?cat=captiveportal&zone=$cpkey");
}
}
function get_dates($curperiod, $graph) {
global $rrd_graph_length_list;
$now = time();
$end = $now;
if ($curperiod == "absolute") {
$start = $end - $rrd_graph_length_list[$graph];
} else {
$curyear = date('Y', $now);
$curmonth = date('m', $now);
$curweek = date('W', $now);
$curweekday = date('N', $now) - 1; // We want to start on monday
$curday = date('d', $now);
$curhour = date('G', $now);
switch ($curperiod) {
case "previous":
$offset = -1;
break;
default:
$offset = 0;
}
switch ($graph) {
case "eighthour":
if ($curhour < 24) {
$starthour = 16;
}
if ($curhour < 16) {
$starthour = 8;
}
if ($curhour < 8) {
$starthour = 0;
}
switch ($offset) {
case 0:
$houroffset = $starthour;
break;
default:
$houroffset = $starthour + ($offset * 8);
break;
}
$start = mktime($houroffset, 0, 0, $curmonth, $curday, $curyear);
if ($offset != 0) {
$end = mktime(($houroffset + 8), 0, 0, $curmonth, $curday, $curyear);
}
break;
case "day":
$start = mktime(0, 0, 0, $curmonth, ($curday + $offset), $curyear);
if ($offset != 0) {
$end = mktime(0, 0, 0, $curmonth, (($curday + $offset) + 1), $curyear);
}
break;
case "week":
switch ($offset) {
case 0:
$weekoffset = 0;
break;
default:
$weekoffset = ($offset * 7) - 7;
break;
}
$start = mktime(0, 0, 0, $curmonth, (($curday - $curweekday) + $weekoffset), $curyear);
if ($offset != 0) {
$end = mktime(0, 0, 0, $curmonth, (($curday - $curweekday) + $weekoffset + 7), $curyear);
}
break;
case "month":
$start = mktime(0, 0, 0, ($curmonth + $offset), 0, $curyear);
if ($offset != 0) {
$end = mktime(0, 0, 0, (($curmonth + $offset) + 1), 0, $curyear);
}
break;
case "quarter":
$start = mktime(0, 0, 0, (($curmonth - 2) + $offset), 0, $curyear);
if ($offset != 0) {
$end = mktime(0, 0, 0, (($curmonth + $offset) + 1), 0, $curyear);
}
break;
case "year":
$start = mktime(0, 0, 0, 1, 0, ($curyear + $offset));
if ($offset != 0) {
$end = mktime(0, 0, 0, 1, 0, (($curyear + $offset) +1));
}
break;
case "fouryear":
$start = mktime(0, 0, 0, 1, 0, (($curyear - 3) + $offset));
if ($offset != 0) {
$end = mktime(0, 0, 0, 1, 0, (($curyear + $offset) +1));
}
break;
}
}
// echo "start $start ". date('l jS \of F Y h:i:s A', $start) .", end $end ". date('l jS \of F Y h:i:s A', $end) ."
";
$dates = array();
$dates['start'] = $start;
$dates['end'] = $end;
return $dates;
}
function make_tabs() {
global $curcat, $queues, $wireless, $cellular, $vpnusers, $captiveportal, $dhcpd, $ntpd;
$tab_array = array();
$tab_array[] = array(gettext("System"), ($curcat == "system"), "status_rrd_graph.php?cat=system");
$tab_array[] = array(gettext("Traffic"), ($curcat == "traffic"), "status_rrd_graph.php?cat=traffic");
$tab_array[] = array(gettext("Packets"), ($curcat == "packets"), "status_rrd_graph.php?cat=packets");
$tab_array[] = array(gettext("Quality"), ($curcat == "quality"), "status_rrd_graph.php?cat=quality");
if ($queues) {
$tab_array[] = array(gettext("Queues"), ($curcat == "queues"), "status_rrd_graph.php?cat=queues");
$tab_array[] = array(gettext("QueueDrops"), ($curcat == "queuedrops"), "status_rrd_graph.php?cat=queuedrops");
}
if ($wireless) {
$tab_array[] = array(gettext("Wireless"), ($curcat == "wireless"), "status_rrd_graph.php?cat=wireless");
}
if ($cellular) {
$tab_array[] = array(gettext("Cellular"), ($curcat == "cellular"), "status_rrd_graph.php?cat=cellular");
}
if ($vpnusers) {
$tab_array[] = array(gettext("VPN Users"), ($curcat == "vpnusers"), "status_rrd_graph.php?cat=vpnusers");
}
if ($captiveportal) {
$tab_array[] = array(gettext("Captive Portal"), ($curcat == "captiveportal"), "status_rrd_graph.php?cat=captiveportal");
}
if ($ntpd) {
$tab_array[] = array("NTP", ($curcat == "ntpd"), "status_rrd_graph.php?cat=ntpd");
}
if ($dhcpd) {
$tab_array[] = array(gettext("DHCP Server"), ($curcat == "dhcpd"), "status_rrd_graph.php?cat=dhcpd");
}
$tab_array[] = array(gettext("Custom"), ($curcat == "custom"), "status_rrd_graph.php?cat=custom");
$tab_array[] = array(gettext("Settings"), ($curcat == "settings"), "status_rrd_graph_settings.php");
return($tab_array);
}
// Create the selectable list of graphs
function build_options() {
global $curcat, $custom_databases, $ui_databases;
$optionslist = array();
if ($curcat == "custom") {
foreach ($custom_databases as $db => $database) {
$optionc = explode("-", $database);
$friendly = convert_friendly_interface_to_friendly_descr(strtolower($optionc[0]));
if (empty($friendly)) {
$friendly = $optionc[0];
}
$search = array("-", ".rrd", $optionc[0]);
$replace = array(" :: ", "", $friendly);
$prettyprint = ucwords(str_replace($search, $replace, $database));
$optionslist[$database] = htmlspecialchars($prettyprint);
}
}
foreach ($ui_databases as $db => $database) {
if (!preg_match("/($curcat)/i", $database)) {
continue;
}
if (($curcat == "captiveportal") && !empty($curzone) && !preg_match("/captiveportal-{$curzone}/i", $database)) {
continue;
}
$optionc = explode("-", $database);
$search = array("-", ".rrd", $optionc);
$replace = array(" :: ", "", $friendly);
switch ($curcat) {
case "captiveportal":
$optionc = str_replace($search, $replace, $optionc[2]);
$prettyprint = ucwords(str_replace($search, $replace, $optionc));
$optionslist[$optionc] = htmlspecialchars($prettyprint);
break;
case "system":
$optionc = str_replace($search, $replace, $optionc[1]);
$prettyprint = ucwords(str_replace($search, $replace, $optionc));
$optionslist[$optionc] = htmlspecialchars($prettyprint);
break;
default:
/* Deduce an interface if possible and use the description */
$optionc = "$optionc[0]";
$friendly = convert_friendly_interface_to_friendly_descr(strtolower($optionc));
if (empty($friendly)) {
$friendly = $optionc;
}
$search = array("-", ".rrd", $optionc);
$replace = array(" :: ", "", $friendly);
$prettyprint = ucwords(str_replace($search, $replace, $friendly));
$optionslist[$optionc] = htmlspecialchars($prettyprint);
}
}
return($optionslist);
}
include("head.inc");
display_top_tabs(make_tabs());
if ($input_errors && count($input_errors)) {
print_input_errors($input_errors);
}
$form = new Form(false);
$section = new Form_Section('Graph Settings');
$group = new Form_Group('Options');
$group->add(new Form_Select(
'option',
'Graphs',
$curoption,
build_options()
))->setHelp('Graph');
$group->add(new Form_Select(
'style',
'Style',
$curstyle,
$rrd_style_list
))->setHelp('Style');
$group->add(new Form_Select(
'period',
'Period',
$curperiod,
$rrd_period_list
))->setHelp('Period');
if ($curcat == 'custom') {
$group->setHelp('Any changes to these options may not take affect until the next auto-refresh.');
}
$section->add($group);
if ($curcat == 'custom') {
$section->addInput(new Form_Input(
'cat',
null,
'hidden',
'custom'
));
$tz = date_default_timezone_get();
$tz_msg = gettext("Enter date and/or time. Current timezone:") . " $tz";
$start_fmt = strftime("%m/%d/%Y %H:%M:%S", $start);
$end_fmt = strftime("%m/%d/%Y %H:%M:%S", $end);
$group = new Form_Group('');
$group->add(new Form_Input(
'start',
'Start',
'datetime',
$start_fmt
))->setHelp('Start');
$group->add(new Form_Input(
'end',
'End',
'datetime',
$end_fmt
))->setHelp('End');
if ($curcat != 'custom') {
$group->setHelp('Any changes to these options may not take affect until the next auto-refresh');
}
$section->add($group);
$form->add($section);
print($form);
$curdatabase = $curoption;
$graph = "custom-$curdatabase";
if (in_array($curdatabase, $custom_databases)) {
$id = "{$graph}-{$curoption}-{$curdatabase}";
$id = preg_replace('/\./', '_', $id);
?>