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) 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" ?>' . "\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="date" x="<?=$width*0.33?>" y="5" <?=$attribs['legend']?>> </text>
120
    <text id="time" x="<?=$width*0.33?>" y="11" <?=$attribs['legend']?>> </text>
121
    <text id="graphlast" x="<?=$width*0.55?>" y="17" <?=$attribs['legend']?>><?=gettext("Graph shows last"); ?> <?=$time_interval*$nb_plot?> <?=gettext("seconds"); ?></text>
122
    <polygon id="axis_arrow_x" <?=$attribs['axis']?> points="<?=($width) . "," . ($height)?> <?=($width-2) . "," . ($height-2)?> <?=($width-2) . "," . $height?>"/>
123
    <text id="error" x="<?=$width*0.5?>" y="<?=$height*0.5?>"  visibility="hidden" <?=$attribs['error']?> text-anchor="middle"><?=$error_text?></text>
124
    <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>
125
  </g>
126
  <script type="text/ecmascript">
127
    <![CDATA[
128

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

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

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

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

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

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

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

    
191
  fetch_data();
192
}
193

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

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

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

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

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

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

    
227
  if (!isNumber(ifin) || !isNumber(ifout))
228
    return handle_error();
229

    
230
  var diff_ugmt  = ugmt - last_ugmt;
231
  var diff_ifin  = ifin - last_ifin;
232
  var diff_ifout = ifout - last_ifout;
233

    
234
  if (diff_ugmt == 0)
235
    diff_ugmt = 1;  /* avoid division by zero */
236

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

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

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

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

    
288
  var rmax;  // max, rounded up
289

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

    
319
  scale = <?=$height?> / rmax;
320

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

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

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

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

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

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

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

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

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

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

    
384
    ]]>
385
  </script>
386
</svg>
(82-82/246)