Project

General

Profile

Download (15 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	graph.php
4
	part of m0n0wall (http://m0n0.ch/wall)
5
	
6
	Copyright (C) 2013-2015 Electric Sheep Fencing, LP
7
	Copyright (C) 2004-2006 T. Lechat <dev@lechat.org>, Manuel Kasper <mk@neon1.net>
8
	and Jonathan Watt <jwatt@jwatt.org>.
9
	All rights reserved.
10
	
11
	Redistribution and use in source and binary forms, with or without
12
	modification, are permitted provided that the following conditions are met:
13
	
14
	1. Redistributions of source code must retain the above copyright notice,
15
	   this list of conditions and the following disclaimer.
16
	
17
	2. Redistributions in binary form must reproduce the above copyright
18
	   notice, this list of conditions and the following disclaimer in the
19
	   documentation and/or other materials provided with the distribution.
20
	
21
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
22
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
23
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
25
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
	POSSIBILITY OF SUCH DAMAGE.
31
*/
32
/*
33
	pfSense_MODULE:	graph
34
*/
35

    
36
##|+PRIV
37
##|*IDENT=page-diagnostics-interfacetraffic
38
##|*NAME=Diagnostics: Interface Traffic page
39
##|*DESCR=Allow access to the 'Diagnostics: Interface Traffic' page.
40
##|*MATCH=graph.php*
41
##|-PRIV
42

    
43
require("globals.inc");
44
require("guiconfig.inc");
45

    
46
header("Last-Modified: " . gmdate( "D, j M Y H:i:s" ) . " GMT" );
47
header("Expires: " . gmdate( "D, j M Y H:i:s", time() ) . " GMT" );
48
header("Cache-Control: no-store, no-cache, must-revalidate" ); // HTTP/1.1
49
header("Cache-Control: post-check=0, pre-check=0", FALSE );
50
header("Pragma: no-cache"); // HTTP/1.0
51
header("Content-type: image/svg+xml");
52

    
53
/********** HTTP GET Based Conf ***********/
54
$ifnum=@$_GET["ifnum"];  // BSD / SNMP interface name / number
55
$ifnum = get_real_interface($ifnum);
56
$ifname=@$_GET["ifname"]?$_GET["ifname"]:"Interface $ifnum";  //Interface name that will be showed on top right of graph
57

    
58
/********* Other conf *******/
59
if (isset($config["widgets"]["trafficgraphs"]["scale_type"]))
60
	$scale_type = $config["widgets"]["trafficgraphs"]["scale_type"];
61
else
62
	$scale_type = "up";
63

    
64
$nb_plot=120;                   //NB plot in graph
65
if ($_GET["timeint"])
66
	$time_interval = $_GET["timeint"];		//Refresh time Interval
67
else
68
	$time_interval = 3;
69

    
70
if ($_GET["initdelay"])
71
	$init_delay = $_GET["initdelay"];		//Initial Delay
72
else
73
	$init_delay = 3;
74

    
75
//SVG attributes
76
$attribs['axis']='fill="black" stroke="black"';
77
$attribs['in']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"';
78
$attribs['out']='fill="#000000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="7"';
79
$attribs['graph_in']='fill="none" stroke="#FF0000" stroke-opacity="0.8"';
80
$attribs['graph_out']='fill="none" stroke="#000000" stroke-opacity="0.8"';
81
$attribs['legend']='fill="black" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"';
82
$attribs['graphname']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="8"';
83
$attribs['grid_txt']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="6"';
84
$attribs['grid']='stroke="gray" stroke-opacity="0.5"';
85
$attribs['switch_unit']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"';
86
$attribs['switch_scale']='fill="#FF0000" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4" text-decoration="underline"';
87
$attribs['error']='fill="blue" font-family="Arial" font-size="4"';
88
$attribs['collect_initial']='fill="gray" font-family="Tahoma, Verdana, Arial, Helvetica, sans-serif" font-size="4"';
89

    
90
//Error text if we cannot fetch data : depends on which method is used
91
$error_text = "Cannot get data about interface " . htmlspecialchars($ifnum);
92

    
93
$height=100;            //SVG internal height : do not modify
94
$width=200;             //SVG internal width : do not modify
95

    
96
$fetch_link = "ifstats.php?if=" . htmlspecialchars($ifnum);
97

    
98
/* check for custom theme colors */
99
if(file_exists("/usr/local/www/themes/{$g['theme']}/graph.php")) {
100
	$themetxt = file_get_contents("/usr/local/www/themes/{$g['theme']}/graph.php");
101
	eval($themetxt);
102
} 
103

    
104
/********* Graph DATA **************/
105
print('<?xml version="1.0" ?>' . "\n");?>
106
<svg width="100%" height="100%" viewBox="0 0 <?=$width?> <?=$height?>" preserveAspectRatio="none" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" onload="init(evt)">
107
  <g id="graph">
108
    <rect id="bg" x1="0" y1="0" width="100%" height="100%" fill="white"/>
109
    <line id="axis_x" x1="0" y1="0" x2="0" y2="100%" <?=$attribs['axis']?>/>
110
    <line id="axis_y" x1="0" y1="100%" x2="100%" y2="100%" <?=$attribs['axis']?>/>
111
    <path id="graph_out" d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_out']?>/>
112
    <path id="graph_in"  d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_in']?>/>
113
    <path id="grid"  d="M0 <?=$height/4*1?> L <?=$width?> <?=$height/4*1?> M0 <?=$height/4*2?> L <?=$width?> <?=$height/4*2?> M0 <?=$height/4*3?> L <?=$width?> <?=$height/4*3?>" <?=$attribs['grid']?>/>
114
    <text id="grid_txt1" x="<?=$width?>" y="<?=$height/4*1?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
115
    <text id="grid_txt2" x="<?=$width?>" y="<?=$height/4*2?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
116
    <text id="grid_txt3" x="<?=$width?>" y="<?=$height/4*3?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
117
    <text id="graph_in_lbl" x="5" y="8" <?=$attribs['in']?>><?=gettext("In"); ?></text>
118
    <text id="graph_out_lbl" x="5" y="16" <?=$attribs['out']?>><?=gettext("Out"); ?></text>
119
    <text id="graph_in_txt" x="20" y="8" <?=$attribs['in']?>> </text>
120
    <text id="graph_out_txt" x="20" y="16" <?=$attribs['out']?>> </text>
121
    <text id="ifname" x="<?=$width?>" y="8" <?=$attribs['graphname']?> text-anchor="end"><?=htmlspecialchars($ifname)?></text>
122
    <text id="switch_unit" x="<?=$width*0.55?>" y="5" <?=$attribs['switch_unit']?>><?=gettext("Switch to bytes/s"); ?></text>
123
    <text id="switch_scale" x="<?=$width*0.55?>" y="11" <?=$attribs['switch_scale']?>><?=gettext("AutoScale"); ?> (<?=$scale_type?>)</text>
124
    <text id="date" x="<?=$width*0.33?>" y="5" <?=$attribs['legend']?>> </text>
125
    <text id="time" x="<?=$width*0.33?>" y="11" <?=$attribs['legend']?>> </text>
126
    <text id="graphlast" x="<?=$width*0.55?>" y="17" <?=$attribs['legend']?>><?=gettext("Graph shows last"); ?> <?=$time_interval*$nb_plot?> <?=gettext("seconds"); ?></text>
127
    <polygon id="axis_arrow_x" <?=$attribs['axis']?> points="<?=($width) . "," . ($height)?> <?=($width-2) . "," . ($height-2)?> <?=($width-2) . "," . $height?>"/>
128
    <text id="error" x="<?=$width*0.5?>" y="<?=$height*0.5?>"  visibility="hidden" <?=$attribs['error']?> text-anchor="middle"><?=$error_text?></text>
129
    <text id="collect_initial" x="<?=$width*0.5?>" y="<?=$height*0.5?>"  visibility="hidden" <?=$attribs['collect_initial']?> text-anchor="middle"><?=gettext("Collecting initial data, please wait"); ?>...</text>
130
  </g>
131
  <script type="text/ecmascript">
132
    <![CDATA[
133

    
134
/**
135
 * getURL is a proprietary Adobe function, but it's simplicity has made it very
136
 * popular. If getURL is undefined we spin our own by wrapping XMLHttpRequest.
137
 */
138
if (typeof getURL == 'undefined') {
139
  getURL = function(url, callback) {
140
    if (!url)
141
      throw '<?=gettext("No URL for getURL"); ?>';
142

    
143
    try {
144
      if (typeof callback.operationComplete == 'function')
145
        callback = callback.operationComplete;
146
    } catch (e) {}
147
    if (typeof callback != 'function')
148
      throw '<?=gettext("No callback function for getURL"); ?>';
149

    
150
    var http_request = null;
151
    if (typeof XMLHttpRequest != 'undefined') {
152
      http_request = new XMLHttpRequest();
153
    }
154
    else if (typeof ActiveXObject != 'undefined') {
155
      try {
156
        http_request = new ActiveXObject('Msxml2.XMLHTTP');
157
      } catch (e) {
158
        try {
159
          http_request = new ActiveXObject('Microsoft.XMLHTTP');
160
        } catch (e) {}
161
      }
162
    }
163
    if (!http_request)
164
      throw '<?=gettext("Both getURL and XMLHttpRequest are undefined"); ?>';
165

    
166
    http_request.onreadystatechange = function() {
167
      if (http_request.readyState == 4) {
168
        callback( { success : true,
169
                    content : http_request.responseText,
170
                    contentType : http_request.getResponseHeader("Content-Type") } );
171
      }
172
    }
173
    http_request.open('GET', url, true);
174
    http_request.send(null);
175
  }
176
}
177

    
178
var SVGDoc = null;
179
var last_ifin = 0;
180
var last_ifout = 0;
181
var last_ugmt = 0;
182
var max = 0;
183
var plot_in = new Array();
184
var plot_out = new Array();
185

    
186
var max_num_points = <?=$nb_plot?>;  // maximum number of plot data points
187
var step = <?=$width?> / max_num_points ;
188
var unit = 'bits';
189
var scale_type = '<?=$scale_type?>';
190

    
191
function init(evt) {
192
  SVGDoc = evt.target.ownerDocument;
193
  SVGDoc.getElementById("switch_unit").addEventListener("mousedown", switch_unit, false);
194
  SVGDoc.getElementById("switch_scale").addEventListener("mousedown", switch_scale, false);
195

    
196
  fetch_data();
197
}
198

    
199
function switch_unit(event)
200
{
201
  SVGDoc.getElementById('switch_unit').firstChild.data = '<?=gettext("Switch to"); ?> ' + unit + '/s';
202
  unit = (unit == 'bits') ? 'bytes' : 'bits';
203
}
204

    
205
function switch_scale(event)
206
{
207
  scale_type = (scale_type == 'up') ? '<?=gettext("follow"); ?>' : '<?=gettext("up"); ?>';
208
  SVGDoc.getElementById('switch_scale').firstChild.data = 'AutoScale (' + scale_type + ')';
209
}
210

    
211
function fetch_data() {
212
  getURL('<?=$fetch_link?>', plot_data);
213
}
214

    
215
function plot_data(obj) {
216
  // Show datetimelegend
217
  var now = new Date();
218
  var time = LZ(now.getHours()) + ":" + LZ(now.getMinutes()) + ":" + LZ(now.getSeconds());
219
  SVGDoc.getElementById('time').firstChild.data = time;
220
  var date = (now.getMonth()+1) + "/" + now.getDate() + "/" + now.getFullYear();
221
  SVGDoc.getElementById('date').firstChild.data = date;
222

    
223
  if (!obj.success)
224
    return handle_error();  // getURL failed to get data
225

    
226
  var t = obj.content.split("|");
227
  var ugmt = parseFloat(t[0]);  // ugmt is an unixtimestamp style
228
  var ifin = parseInt(t[1], 10);    // number of bytes received by the interface
229
  var ifout = parseInt(t[2], 10);   // number of bytes sent by the interface
230
  var scale;
231

    
232
  if (!isNumber(ifin) || !isNumber(ifout))
233
    return handle_error();
234

    
235
  var diff_ugmt  = ugmt - last_ugmt;
236
  var diff_ifin  = ifin - last_ifin;
237
  var diff_ifout = ifout - last_ifout;
238

    
239
  if (diff_ugmt == 0)
240
    diff_ugmt = 1;  /* avoid division by zero */
241

    
242
  last_ugmt = ugmt;
243
  last_ifin = ifin;
244
  last_ifout = ifout;
245
  var graphTimerId = 0;
246
  switch (plot_in.length) {
247
  	case 0:
248
  		SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'visible');
249
		plot_in[0] = diff_ifin / diff_ugmt;
250
		plot_out[0] = diff_ifout / diff_ugmt;
251
		setTimeout('fetch_data()',<?=1000*($time_interval + $init_delay)?>);
252
		return;
253
	case 1:
254
    	SVGDoc.getElementById("collect_initial").setAttributeNS(null, 'visibility', 'hidden');
255
    	break;
256
    case max_num_points:
257
		// shift plot to left if the maximum number of plot points has been reached
258
		var i = 0;
259
		while (i < max_num_points) {
260
		  plot_in[i] = plot_in[i+1];
261
		  plot_out[i] = plot_out[++i];
262
		}
263
		plot_in.length--;
264
		plot_out.length--;
265
  }
266

    
267
  plot_in[plot_in.length] = diff_ifin / diff_ugmt;
268
  plot_out[plot_out.length]= diff_ifout / diff_ugmt;
269
  var index_plot = plot_in.length - 1;
270

    
271
  SVGDoc.getElementById('graph_in_txt').firstChild.data = formatSpeed(plot_in[index_plot], unit);
272
  SVGDoc.getElementById('graph_out_txt').firstChild.data = formatSpeed(plot_out[index_plot], unit);
273

    
274
  /* determine peak for sensible scaling */
275
  if (scale_type == 'up') {
276
    if (plot_in[index_plot] > max)
277
      max = plot_in[index_plot];
278
    if (plot_out[index_plot] > max)
279
      max = plot_out[index_plot];
280
  }
281
  else if (scale_type == 'follow') {
282
    i = 0;
283
    max = 0;
284
    while (i < plot_in.length) {
285
      if (plot_in[i] > max)
286
        max = plot_in[i];
287
      if (plot_out[i] > max)
288
        max = plot_out[i];
289
      i++;
290
    }
291
  }
292

    
293
  var rmax;  // max, rounded up
294

    
295
  if (unit == 'bits') {
296
    /* round up max, such that
297
         100 kbps -> 200 kbps -> 400 kbps -> 800 kbps -> 1 Mbps -> 2 Mbps -> ... */
298
    rmax = 12500;
299
    i = 0;
300
    while (max > rmax) {
301
      i++;
302
      if (i && (i % 4 == 0))
303
        rmax *= 1.25;
304
      else
305
        rmax *= 2;
306
    }
307
  } else {
308
    /* round up max, such that
309
         10 KB/s -> 20 KB/s -> 40 KB/s -> 80 KB/s -> 100 KB/s -> 200 KB/s -> 400 KB/s -> 800 KB/s -> 1 MB/s ... */
310
    rmax = 10240;
311
    i = 0;
312
    while (max > rmax) {
313
      i++;
314
      if (i && (i % 4 == 0))
315
        rmax *= 1.25;
316
      else
317
        rmax *= 2;
318
      
319
      if (i == 8)
320
        rmax *= 1.024;
321
    }
322
  }
323

    
324
  scale = <?=$height?> / rmax;
325

    
326
  /* change labels accordingly */
327
  SVGDoc.getElementById('grid_txt1').firstChild.data = formatSpeed(3*rmax/4,unit);
328
  SVGDoc.getElementById('grid_txt2').firstChild.data = formatSpeed(2*rmax/4,unit);
329
  SVGDoc.getElementById('grid_txt3').firstChild.data = formatSpeed(rmax/4,unit);
330

    
331
  var path_in = "M 0 " + (<?=$height?> - (plot_in[0] * scale));
332
  var path_out = "M 0 " + (<?=$height?> - (plot_out[0] * scale));
333
  for (i = 1; i < plot_in.length; i++)
334
  {
335
    var x = step * i;
336
    var y_in = <?=$height?> - (plot_in[i] * scale);
337
    var y_out = <?=$height?> - (plot_out[i] * scale);
338
    path_in += " L" + x + " " + y_in;
339
    path_out += " L" + x + " " + y_out;
340
  }
341

    
342
  SVGDoc.getElementById('error').setAttributeNS(null, 'visibility', 'hidden');
343
  SVGDoc.getElementById('graph_in').setAttributeNS(null, 'd', path_in);
344
  SVGDoc.getElementById('graph_out').setAttributeNS(null, 'd', path_out);
345

    
346
  setTimeout('fetch_data()',<?=1000*$time_interval?>);
347
}
348

    
349
function handle_error() {
350
  SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'visible');
351
  setTimeout('fetch_data()',<?=1000*$time_interval?>);
352
}
353

    
354
function isNumber(a) {
355
  return typeof a == 'number' && isFinite(a);
356
}
357

    
358
function formatSpeed(speed, unit) {
359
  if (unit == 'bits')
360
    return formatSpeedBits(speed);
361
  if (unit == 'bytes')
362
    return formatSpeedBytes(speed);
363
}
364

    
365
function formatSpeedBits(speed) {
366
  // format speed in bits/sec, input: bytes/sec
367
  if (speed < 125000)
368
    return Math.round(speed / 125) + " <?=gettext("Kbps"); ?>";
369
  if (speed < 125000000)
370
    return Math.round(speed / 1250)/100 + " <?=gettext("Mbps"); ?>";
371
  // else
372
  return Math.round(speed / 1250000)/100 + " <?=gettext("Gbps"); ?>";  /* wow! */
373
}
374

    
375
function formatSpeedBytes(speed) {
376
  // format speed in bytes/sec, input:  bytes/sec
377
  if (speed < 1048576)
378
    return Math.round(speed / 10.24)/100 + " <?=gettext("KB/s"); ?>";
379
  if (speed < 1073741824)
380
    return Math.round(speed / 10485.76)/100 + " <?=gettext("MB/s"); ?>";
381
  // else
382
  return Math.round(speed / 10737418.24)/100 + " <?=gettext("GB/s"); ?>";  /* wow! */
383
}
384

    
385
function LZ(x) {
386
  return (x < 0 || x > 9 ? "" : "0") + x;
387
}
388

    
389
    ]]>
390
  </script>
391
</svg>
(84-84/256)