Project

General

Profile

Download (14.7 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-cache, no-store, must-revalidate"); // HTTP/1.1
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
if (isset($config["widgets"]["trafficgraphs"]["scale_type"])) {
59
	$scale_type = $config["widgets"]["trafficgraphs"]["scale_type"];
60
} else {
61
	$scale_type = "up";
62
}
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

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

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

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

    
95
$height=100;            //SVG internal height : do not modify
96
$width=200;             //SVG internal width : do not modify
97

    
98
$fetch_link = "ifstats.php?if=" . htmlspecialchars($ifnum);
99

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

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

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

    
146
		try {
147
			if (typeof callback.operationComplete == 'function') {
148
				callback = callback.operationComplete;
149
			}
150
		} catch (e) {}
151
		if (typeof callback != 'function') {
152
			throw '<?=gettext("No callback function for getURL"); ?>';
153
		}
154

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

    
171
		http_request.onreadystatechange = function() {
172
			if (http_request.readyState == 4) {
173
				callback( { success : true,
174
					content : http_request.responseText,
175
					contentType : http_request.getResponseHeader("Content-Type") } );
176
			}
177
		}
178
		http_request.open('GET', url, true);
179
		http_request.send(null);
180
	}
181
}
182

    
183
var SVGDoc = null;
184
var last_ifin = 0;
185
var last_ifout = 0;
186
var last_ugmt = 0;
187
var max = 0;
188
var plot_in = new Array();
189
var plot_out = new Array();
190

    
191
var max_num_points = <?=$nb_plot?>;  // maximum number of plot data points
192
var step = <?=$width?> / max_num_points ;
193
var unit = 'bits';
194
var scale_type = '<?=$scale_type?>';
195

    
196
function init(evt) {
197
	SVGDoc = evt.target.ownerDocument;
198
	SVGDoc.getElementById("switch_unit").addEventListener("mousedown", switch_unit, false);
199
	SVGDoc.getElementById("switch_scale").addEventListener("mousedown", switch_scale, false);
200

    
201
	fetch_data();
202
}
203

    
204
function switch_unit(event) {
205
	SVGDoc.getElementById('switch_unit').firstChild.data = '<?=gettext("Switch to"); ?> ' + unit + '/s';
206
	unit = (unit == 'bits') ? 'bytes' : 'bits';
207
}
208

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

    
214
function fetch_data() {
215
	getURL('<?=$fetch_link?>', plot_data);
216
}
217

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

    
226
	if (!obj.success) {
227
		return handle_error();  // getURL failed to get data
228
	}
229

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

    
236
	if (!isNumber(ifin) || !isNumber(ifout)) {
237
		return handle_error();
238
	}
239

    
240
	var diff_ugmt  = ugmt - last_ugmt;
241
	var diff_ifin  = ifin - last_ifin;
242
	var diff_ifout = ifout - last_ifout;
243

    
244
	if (diff_ugmt == 0) {
245
		diff_ugmt = 1;  /* avoid division by zero */
246
	}
247

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

    
273
	plot_in[plot_in.length] = diff_ifin / diff_ugmt;
274
	plot_out[plot_out.length]= diff_ifout / diff_ugmt;
275
	var index_plot = plot_in.length - 1;
276

    
277
	SVGDoc.getElementById('graph_in_txt').firstChild.data = formatSpeed(plot_in[index_plot], unit);
278
	SVGDoc.getElementById('graph_out_txt').firstChild.data = formatSpeed(plot_out[index_plot], unit);
279

    
280
	/* determine peak for sensible scaling */
281
	if (scale_type == 'up') {
282
		if (plot_in[index_plot] > max) {
283
			max = plot_in[index_plot];
284
		}
285
		if (plot_out[index_plot] > max) {
286
			max = plot_out[index_plot];
287
		}
288
	} else if (scale_type == 'follow') {
289
		i = 0;
290
		max = 0;
291
		while (i < plot_in.length) {
292
			if (plot_in[i] > max) {
293
				max = plot_in[i];
294
			}
295
			if (plot_out[i] > max) {
296
				max = plot_out[i];
297
			}
298
			i++;
299
		}
300
	}
301

    
302
	var rmax;  // max, rounded up
303

    
304
	if (unit == 'bits') {
305
		/* round up max, such that
306
		   100 kbps -> 200 kbps -> 400 kbps -> 800 kbps -> 1 Mbps -> 2 Mbps -> ... */
307
		rmax = 12500;
308
		i = 0;
309
		while (max > rmax) {
310
			i++;
311
			if (i && (i % 4 == 0)) {
312
				rmax *= 1.25;
313
			} else {
314
				rmax *= 2;
315
			}
316
		}
317
	} else {
318
		/* round up max, such that
319
		   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 ... */
320
		rmax = 10240;
321
		i = 0;
322
		while (max > rmax) {
323
			i++;
324
			if (i && (i % 4 == 0)) {
325
				rmax *= 1.25;
326
			} else {
327
				rmax *= 2;
328
			}
329

    
330
			if (i == 8) {
331
				rmax *= 1.024;
332
			}
333
		}
334
	}
335

    
336
	scale = <?=$height?> / rmax;
337

    
338
	/* change labels accordingly */
339
	SVGDoc.getElementById('grid_txt1').firstChild.data = formatSpeed(3*rmax/4, unit);
340
	SVGDoc.getElementById('grid_txt2').firstChild.data = formatSpeed(2*rmax/4, unit);
341
	SVGDoc.getElementById('grid_txt3').firstChild.data = formatSpeed(rmax/4, unit);
342

    
343
	var path_in = "M 0 " + (<?=$height?> - (plot_in[0] * scale));
344
	var path_out = "M 0 " + (<?=$height?> - (plot_out[0] * scale));
345
	for (i = 1; i < plot_in.length; i++) {
346
		var x = step * i;
347
		var y_in = <?=$height?> - (plot_in[i] * scale);
348
		var y_out = <?=$height?> - (plot_out[i] * scale);
349
		path_in += " L" + x + " " + y_in;
350
		path_out += " L" + x + " " + y_out;
351
	}
352

    
353
	SVGDoc.getElementById('error').setAttributeNS(null, 'visibility', 'hidden');
354
	SVGDoc.getElementById('graph_in').setAttributeNS(null, 'd', path_in);
355
	SVGDoc.getElementById('graph_out').setAttributeNS(null, 'd', path_out);
356

    
357
	setTimeout('fetch_data()', <?=1000*$time_interval?>);
358
}
359

    
360
function handle_error() {
361
	SVGDoc.getElementById("error").setAttributeNS(null, 'visibility', 'visible');
362
	setTimeout('fetch_data()', <?=1000*$time_interval?>);
363
}
364

    
365
function isNumber(a) {
366
	return typeof a == 'number' && isFinite(a);
367
}
368

    
369
function formatSpeed(speed, unit) {
370
	if (unit == 'bits') {
371
		return formatSpeedBits(speed);
372
	}
373
	if (unit == 'bytes') {
374
		return formatSpeedBytes(speed);
375
	}
376
}
377

    
378
function formatSpeedBits(speed) {
379
	// format speed in bits/sec, input: bytes/sec
380
	if (speed < 125000) {
381
		return Math.round(speed / 125) + " <?=gettext("Kbps"); ?>";
382
	}
383
	if (speed < 125000000) {
384
		return Math.round(speed / 1250)/100 + " <?=gettext("Mbps"); ?>";
385
	}
386
	// else
387
	return Math.round(speed / 1250000)/100 + " <?=gettext("Gbps"); ?>";  /* wow! */
388
}
389

    
390
function formatSpeedBytes(speed) {
391
	// format speed in bytes/sec, input:  bytes/sec
392
	if (speed < 1048576) {
393
		return Math.round(speed / 10.24)/100 + " <?=gettext("KB/s"); ?>";
394
	}
395
	if (speed < 1073741824) {
396
		return Math.round(speed / 10485.76)/100 + " <?=gettext("MB/s"); ?>";
397
	}
398
	// else
399
	return Math.round(speed / 10737418.24)/100 + " <?=gettext("GB/s"); ?>";  /* wow! */
400
}
401

    
402
function LZ(x) {
403
	return (x < 0 || x > 9 ? "" : "0") + x;
404
}
405

    
406
    ]]>
407
	</script>
408
</svg>
(84-84/252)