Project

General

Profile

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

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

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

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

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

    
57
/********* Other conf *******/
58
$scale_type="up";               //Autoscale default setup : "up" = only increase scale; "follow" = increase and decrease scale according to current graphed datas
59
$nb_plot=120;                   //NB plot in graph
60
if ($_GET["timeint"])
61
	$time_interval = $_GET["timeint"];		//Refresh time Interval
62
else
63
	$time_interval = 3;
64

    
65
if ($_GET["initdelay"])
66
	$init_delay = $_GET["initdelay"];		//Initial Delay
67
else
68
	$init_delay = 3;
69

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

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

    
88
$height=100;            //SVG internal height : do not modify
89
$width=200;             //SVG internal width : do not modify
90

    
91
$fetch_link = "ifstats.php?if=" . htmlspecialchars($ifnum);
92

    
93
/* check for custom theme colors */
94
if(file_exists("/usr/local/www/themes/{$g['theme']}/graph.php")) {
95
	$themetxt = file_get_contents("/usr/local/www/themes/{$g['theme']}/graph.php");
96
	eval($themetxt);
97
} 
98

    
99
/********* Graph DATA **************/
100
print('<?xml version="1.0" encoding="iso-8859-1"?>' . "\n");?>
101
<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)">
102
  <g id="graph">
103
    <rect id="bg" x1="0" y1="0" width="100%" height="100%" fill="white"/>
104
    <line id="axis_x" x1="0" y1="0" x2="0" y2="100%" <?=$attribs['axis']?>/>
105
    <line id="axis_y" x1="0" y1="100%" x2="100%" y2="100%" <?=$attribs['axis']?>/>
106
    <path id="graph_out" d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_out']?>/>
107
    <path id="graph_in"  d="M0 <?=$height?> L 0 <?=$height?>" <?=$attribs['graph_in']?>/>
108
    <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']?>/>
109
    <text id="grid_txt1" x="<?=$width?>" y="<?=$height/4*1?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
110
    <text id="grid_txt2" x="<?=$width?>" y="<?=$height/4*2?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
111
    <text id="grid_txt3" x="<?=$width?>" y="<?=$height/4*3?>" <?=$attribs['grid_txt']?> text-anchor="end"> </text>
112
    <text id="graph_in_lbl" x="5" y="8" <?=$attribs['in']?>><?=gettext("In"); ?></text>
113
    <text id="graph_out_lbl" x="5" y="16" <?=$attribs['out']?>><?=gettext("Out"); ?></text>
114
    <text id="graph_in_txt" x="20" y="8" <?=$attribs['in']?>> </text>
115
    <text id="graph_out_txt" x="20" y="16" <?=$attribs['out']?>> </text>
116
    <text id="ifname" x="<?=$width?>" y="8" <?=$attribs['graphname']?> text-anchor="end"><?=htmlspecialchars($ifname)?></text>
117
    <text id="switch_unit" x="<?=$width*0.55?>" y="5" <?=$attribs['switch_unit']?>><?=gettext("Switch to bytes/s"); ?></text>
118
    <text id="switch_scale" x="<?=$width*0.55?>" y="11" <?=$attribs['switch_scale']?>><?=gettext("AutoScale"); ?> (<?=$scale_type?>)</text>
119
    <text id="datetime" x="<?=$width*0.33?>" y="5" <?=$attribs['legend']?>> </text>
120
    <text id="graphlast" x="<?=$width*0.55?>" y="17" <?=$attribs['legend']?>><?=gettext("Graph shows last"); ?> <?=$time_interval*$nb_plot?> <?=gettext("seconds"); ?></text>
121
    <polygon id="axis_arrow_x" <?=$attribs['axis']?> points="<?=($width) . "," . ($height)?> <?=($width-2) . "," . ($height-2)?> <?=($width-2) . "," . $height?>"/>
122
    <text id="error" x="<?=$width*0.5?>" y="<?=$height*0.5?>"  visibility="hidden" <?=$attribs['error']?> text-anchor="middle"><?=$error_text?></text>
123
    <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>
124
  </g>
125
  <script type="text/ecmascript">
126
    <![CDATA[
127

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

    
137
    try {
138
      if (typeof callback.operationComplete == 'function')
139
        callback = callback.operationComplete;
140
    } catch (e) {}
141
    if (typeof callback != 'function')
142
      throw '<?=gettext("No callback function for getURL"); ?>';
143

    
144
    var http_request = null;
145
    if (typeof XMLHttpRequest != 'undefined') {
146
      http_request = new XMLHttpRequest();
147
    }
148
    else if (typeof ActiveXObject != 'undefined') {
149
      try {
150
        http_request = new ActiveXObject('Msxml2.XMLHTTP');
151
      } catch (e) {
152
        try {
153
          http_request = new ActiveXObject('Microsoft.XMLHTTP');
154
        } catch (e) {}
155
      }
156
    }
157
    if (!http_request)
158
      throw '<?=gettext("Both getURL and XMLHttpRequest are undefined"); ?>';
159

    
160
    http_request.onreadystatechange = function() {
161
      if (http_request.readyState == 4) {
162
        callback( { success : true,
163
                    content : http_request.responseText,
164
                    contentType : http_request.getResponseHeader("Content-Type") } );
165
      }
166
    }
167
    http_request.open('GET', url, true);
168
    http_request.send(null);
169
  }
170
}
171

    
172
var SVGDoc = null;
173
var last_ifin = 0;
174
var last_ifout = 0;
175
var last_ugmt = 0;
176
var max = 0;
177
var plot_in = new Array();
178
var plot_out = new Array();
179

    
180
var max_num_points = <?=$nb_plot?>;  // maximum number of plot data points
181
var step = <?=$width?> / max_num_points ;
182
var unit = 'bits';
183
var scale_type = '<?=$scale_type?>';
184

    
185
function init(evt) {
186
  SVGDoc = evt.target.ownerDocument;
187
  SVGDoc.getElementById("switch_unit").addEventListener("mousedown", switch_unit, false);
188
  SVGDoc.getElementById("switch_scale").addEventListener("mousedown", switch_scale, false);
189

    
190
  fetch_data();
191
}
192

    
193
function switch_unit(event)
194
{
195
  SVGDoc.getElementById('switch_unit').firstChild.data = '<?=gettext("Switch to"); ?> ' + unit + '/s';
196
  unit = (unit == 'bits') ? 'bytes' : 'bits';
197
}
198

    
199
function switch_scale(event)
200
{
201
  scale_type = (scale_type == 'up') ? '<?=gettext("follow"); ?>' : '<?=gettext("up"); ?>';
202
  SVGDoc.getElementById('switch_scale').firstChild.data = 'AutoScale (' + scale_type + ')';
203
}
204

    
205
function fetch_data() {
206
  getURL('<?=$fetch_link?>', plot_data);
207
}
208

    
209
function plot_data(obj) {
210
  // Show datetimelegend
211
  var now = new Date();
212
  var datetime = (now.getMonth()+1) + "/" + now.getDate() + "/" + now.getFullYear() + ' ' + 
213
    LZ(now.getHours()) + ":" + LZ(now.getMinutes()) + ":" + LZ(now.getSeconds());
214
  SVGDoc.getElementById('datetime').firstChild.data = datetime;
215

    
216
  if (!obj.success)
217
    return handle_error();  // getURL failed to get data
218

    
219
  var t = obj.content.split("|");
220
  var ugmt = parseFloat(t[0]);  // ugmt is an unixtimestamp style
221
  var ifin = parseInt(t[1]);    // number of bytes received by the interface
222
  var ifout = parseInt(t[2]);   // number of bytes sent by the interface
223
  var scale;
224

    
225
  if (!isNumber(ifin) || !isNumber(ifout))
226
    return handle_error();
227

    
228
  var diff_ugmt  = ugmt - last_ugmt;
229
  var diff_ifin  = ifin - last_ifin;
230
  var diff_ifout = ifout - last_ifout;
231

    
232
  if (diff_ugmt == 0)
233
    diff_ugmt = 1;  /* avoid division by zero */
234

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

    
260
  plot_in[plot_in.length] = diff_ifin / diff_ugmt;
261
  plot_out[plot_out.length]= diff_ifout / diff_ugmt;
262
  var index_plot = plot_in.length - 1;
263

    
264
  SVGDoc.getElementById('graph_in_txt').firstChild.data = formatSpeed(plot_in[index_plot], unit);
265
  SVGDoc.getElementById('graph_out_txt').firstChild.data = formatSpeed(plot_out[index_plot], unit);
266

    
267
  /* determine peak for sensible scaling */
268
  if (scale_type == 'up') {
269
    if (plot_in[index_plot] > max)
270
      max = plot_in[index_plot];
271
    if (plot_out[index_plot] > max)
272
      max = plot_out[index_plot];
273
  }
274
  else if (scale_type == 'follow') {
275
    i = 0;
276
    max = 0;
277
    while (i < plot_in.length) {
278
      if (plot_in[i] > max)
279
        max = plot_in[i];
280
      if (plot_out[i] > max)
281
        max = plot_out[i];
282
      i++;
283
    }
284
  }
285

    
286
  var rmax;  // max, rounded up
287

    
288
  if (unit == 'bits') {
289
    /* round up max, such that
290
         100 kbps -> 200 kbps -> 400 kbps -> 800 kbps -> 1 Mbps -> 2 Mbps -> ... */
291
    rmax = 12500;
292
    i = 0;
293
    while (max > rmax) {
294
      i++;
295
      if (i && (i % 4 == 0))
296
        rmax *= 1.25;
297
      else
298
        rmax *= 2;
299
    }
300
  } else {
301
    /* round up max, such that
302
         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 ... */
303
    rmax = 10240;
304
    i = 0;
305
    while (max > rmax) {
306
      i++;
307
      if (i && (i % 4 == 0))
308
        rmax *= 1.25;
309
      else
310
        rmax *= 2;
311
      
312
      if (i == 8)
313
        rmax *= 1.024;
314
    }
315
  }
316

    
317
  scale = <?=$height?> / rmax;
318

    
319
  /* change labels accordingly */
320
  SVGDoc.getElementById('grid_txt1').firstChild.data = formatSpeed(3*rmax/4,unit);
321
  SVGDoc.getElementById('grid_txt2').firstChild.data = formatSpeed(2*rmax/4,unit);
322
  SVGDoc.getElementById('grid_txt3').firstChild.data = formatSpeed(rmax/4,unit);
323

    
324
  var path_in = "M 0 " + (<?=$height?> - (plot_in[0] * scale));
325
  var path_out = "M 0 " + (<?=$height?> - (plot_out[0] * scale));
326
  for (i = 1; i < plot_in.length; i++)
327
  {
328
    var x = step * i;
329
    var y_in = <?=$height?> - (plot_in[i] * scale);
330
    var y_out = <?=$height?> - (plot_out[i] * scale);
331
    path_in += " L" + x + " " + y_in;
332
    path_out += " L" + x + " " + y_out;
333
  }
334

    
335
  SVGDoc.getElementById('error').setAttributeNS(null, 'visibility', 'hidden');
336
  SVGDoc.getElementById('graph_in').setAttributeNS(null, 'd', path_in);
337
  SVGDoc.getElementById('graph_out').setAttributeNS(null, 'd', path_out);
338

    
339
  setTimeout('fetch_data()',<?=1000*$time_interval?>);
340
}
341

    
342
function handle_error() {
343
  SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'visible');
344
  setTimeout('fetch_data()',<?=1000*$time_interval?>);
345
}
346

    
347
function isNumber(a) {
348
  return typeof a == 'number' && isFinite(a);
349
}
350

    
351
function formatSpeed(speed, unit) {
352
  if (unit == 'bits')
353
    return formatSpeedBits(speed);
354
  if (unit == 'bytes')
355
    return formatSpeedBytes(speed);
356
}
357

    
358
function formatSpeedBits(speed) {
359
  // format speed in bits/sec, input: bytes/sec
360
  if (speed < 125000)
361
    return Math.round(speed / 125) + " <?=gettext("Kbps"); ?>";
362
  if (speed < 125000000)
363
    return Math.round(speed / 1250)/100 + " <?=gettext("Mbps"); ?>";
364
  // else
365
  return Math.round(speed / 1250000)/100 + " <?=gettext("Gbps"); ?>";  /* wow! */
366
}
367

    
368
function formatSpeedBytes(speed) {
369
  // format speed in bytes/sec, input:  bytes/sec
370
  if (speed < 1048576)
371
    return Math.round(speed / 10.24)/100 + " <?=gettext("KB/s"); ?>";
372
  if (speed < 1073741824)
373
    return Math.round(speed / 10485.76)/100 + " <?=gettext("MB/s"); ?>";
374
  // else
375
  return Math.round(speed / 10737418.24)/100 + " <?=gettext("GB/s"); ?>";  /* wow! */
376
}
377

    
378
function LZ(x) {
379
  return (x < 0 || x > 9 ? "" : "0") + x;
380
}
381

    
382
    ]]>
383
  </script>
384
</svg>
(73-73/225)