· 4 years ago · Oct 27, 2020, 11:08 PM
1<?php
2
3/**
4 * netstat.php - Show online status of hosts and services
5 *
6 * This script is intended to provide a simplified, easily comprehensible
7 * and aesthetically pleasing overview of the online status of hosts and
8 * services. Checks are done in real-time but they only check whether
9 * a port is open (which might be sufficient if your hosts are monitored
10 * by full-blown monitoring tools anyway and all you want is a simple
11 * interface for e.g. users or clients).
12 *
13 * This netstat.php has been rewritten and extended by Todd E. Johnson.
14 * It is based on the original netstat 0.x by Andreas Schamanek, still
15 * available at http://www.fam.tuwien.ac.at/~schamane/sysadmin/netstat/
16 *
17 * Requirements: fsockopen(), for ICMP pings also exec()
18 *
19 * (License + History: see also end of file)
20 *
21 * @author Todd E. Johnson <http://www.toddejohnson.net>
22 * @author Andreas Schamanek <http://andreas.schamanek.net>
23 * @license GPL <http://www.gnu.org/licenses/gpl.html>
24 * @copyright (c) 2012 Todd E. Johnson, Andreas Schamanek
25 *
26 */
27
28
29
30// Use php netstat.php genconfig to create netstat.conf.php then
31// edit netstat.conf.php with your configuration.
32
33
34// ------------------------------------------------- functions part of script
35/**
36 * Catch Exceptions
37 * @param Exception $err
38 */
39function catchExceptions($err){
40 global $config;
41 echo "Error with your request! Please try again later. " .
42 "If the problem persists contact <a href=\"".$config['contact']."\">".$config['contact']."</a>.";
43 error_log($err->__toString(),0);
44 exit(1);
45}
46
47// Report no PHP errors (to be safe we include this very early)
48error_reporting(0);
49set_exception_handler('catchExceptions');
50
51/**
52 * Defaults for generic config
53 * @return Default configuration
54 */
55function defaultConfig(){
56 $config=array();
57 $config['version'] = "~ git master ~<br/>" . gethostname() . "<br/>" . date("F d Y H:i:s e", getlastmod());
58 $config['description'] = "Online status of hosts and services provided by netstat.php";
59
60
61 // below we set up some silly defaults; it is recommended to save your
62 // own settings in $configfile; if readable it will override our defaults;
63 // for a list of all configuration variables see http://wox.at/as/_/netstat_php
64 $config['configfile'] = 'netstat.conf.php';
65
66 // my network, title and headline of the page
67 $config['title'] = "Network Status Summary";
68 $config['headline'] = $config['title'];
69
70 // if $alertfile exists the contents will be included()/shown (use html!)
71 $config['alertfile'] = 'netstat.txt';
72
73 // checks (use pipes (|) with care ;)
74 // syntax: host or ip to check | port | description
75 // host/ip
76 // ipv6 addresses must be wrapped in brackets eg [2001:db8::1]. if you
77 // need to check a ssl service like https, smtps, or imaps you can use ssl://[2001:db8::1]
78 // port
79 // if $port = 'ping' an icmp ping will be executed
80 // if $port = 'ping6' an icmpv6 ping will be executed
81 // if $port = 'headline' $host is printed as a headline
82
83 $LOGO_CANON = '<img src="./img/logos/logos-theme-transparency-canon-100x25.png" class="logo-hardware"/>';
84 $LOGO_BROTHER = '<img src="./img/logos/logos-theme-transparency-brother-100x25.png" class="logo-hardware"/>';
85 $LOGO_OPENWRT = '<img src="./img/logos/logos-theme-transparency-OpenWrt-100x25.png" class="logo-hardware"/>';
86 $LOGO_RASPBERRYPI = '<img src="./img/logos/logos-theme-transparency-raspberry-pi-logo-100x130.png" class="logo-hardware"/>';
87
88 $config['checks'] = array(
89 '---------------------------------------------------------------------------------------',
90 '<div class="grid-container"><div class="grid-item item-1">canon-wifi.home.lan</div><div class="grid-item item-2 subheadline">Canon MX922</div><div class="grid-item item-3">' . $LOGO_CANON . '</div></div>|headline',
91 '---------------------------------------------------------------------------------------',
92 '192.168.1.100 | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping 192.168.1.100</code></div></div>',
93 '192.168.1.100 | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://192.168.1.100">http://192.168.1.100</a></code></div></div> ',
94 '---------------------------------------------------------------------------------------',
95 '<div class="grid-container"><div class="grid-item item-1">brother-wifi.home.lan</div><div class="grid-item item-2 subheadline">Brother HL-2170W</div><div class="grid-item item-3">' . $LOGO_BROTHER . '</div></div>|headline',
96 '---------------------------------------------------------------------------------------',
97 '192.168.1.102 | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping 192.168.1.102</code></div></div>',
98 '192.168.1.102 | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://192.168.1.102">http://192.168.1.102</a></code></div></div>',
99 '---------------------------------------------------------------',
100 '<div class="grid-container"><div class="grid-item item-1">callisto.home.lan</div><div class="grid-item item-2 subheadline">TP-Link WL-WR802N</div><div class="grid-item item-3">' . $LOGO_OPENWRT. '</div></div>|headline',
101 '---------------------------------------------------------------',
102 '192.168.1.10 | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping 192.168.1.10</code></div></div>',
103 '192.168.1.10 | 22 | <div class="grid-statuses"><div class="grid-item service-name">SSH</div><div class="grid-item"><code>ssh 192.168.1.10</code></div></div>',
104 '192.168.1.10 | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://192.168.1.10:80">http://192.168.1.10:80</a></code></div></div>',
105 '192.168.1.10 | 443 | <div class="grid-statuses"><div class="grid-item service-name">HTTPS</div><div class="grid-item"><code><a href="https://192.168.1.10:443">https://192.168.1.10:443</a></code></div></div> ',
106 'callisto.home.lan | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping callisto.home.lan</code></div></div>',
107 'callisto.home.lan | 22 | <div class="grid-statuses"><div class="grid-item service-name">SSH</div><div class="grid-item"><code>ssh callisto.home.lan (port 22)</code></div></div>',
108 'callisto.home.lan | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://callisto.home.lan:80">http://callisto.home.lan:80</a></code></div></div>',
109 'callisto.home.lan | 443 | <div class="grid-statuses"><div class="grid-item service-name">HTTPS</div><div class="grid-item"><code><a href="https://callisto.home.lan:443">https://callisto.home.lan:443</a></code></div></div>',
110 '---------------------------------------------------------------',
111 '<div class="grid-container"><div class="grid-item item-1">clearwater.home.lan</div><div class="grid-item item-2 subheadline">Raspberry Pi Zero W</div><div class="grid-item item-3">' . $LOGO_RASPBERRYPI. '</div></div>|headline',
112 '---------------------------------------------------------------',
113 '192.168.1.12 | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping 192.168.1.12</code></div></div> ',
114 '192.168.1.12 | 22 | <div class="grid-statuses"><div class="grid-item service-name">SSH</div><div class="grid-item"><code>ssh 192.168.1.12</code></div></div>',
115 '192.168.1.12 | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://192.168.1.12:80">http://192.168.1.12:80</a></code></div></div> ',
116 '192.168.1.12 | 443 | <div class="grid-statuses"><div class="grid-item service-name">HTTPS</div><div class="grid-item"><code><a href="https://192.168.1.12:443">https://192.168.1.12:443</a></code></div></div> ',
117 'clearwater.home.lan | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping clearwater.home.lan</code></div></div>',
118 'clearwater.home.lan | 22 | <div class="grid-statuses"><div class="grid-item service-name">SSH</div><div class="grid-item"><code>ssh clearwater.home.lan (port 22)</code></div></div>',
119 'clearwater.home.lan | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://clearwater.home.lan:80">http://clearwater.home.lan:80</a></code></div></div>',
120 'clearwater.home.lan | 443 | <div class="grid-statuses"><div class="grid-item service-name">HTTPS</div><div class="grid-item"><code><a href="https://clearwater.home.lan:443">https://clearwater.home.lan:443</a></code></div></div>',
121 '---------------------------------------------------------------',
122 '<div class="grid-container"><div class="grid-item item-1">jupiter.home.lan</div><div class="grid-item item-2 subheadline">Linksys EA4500 v1</div><div class="grid-item item-3">' . $LOGO_OPENWRT. '</div></div>|headline',
123 '---------------------------------------------------------------',
124 '192.168.1.3 | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping 192.168.1.3</code></div></div> ',
125 '192.168.1.3 | 22 | <div class="grid-statuses"><div class="grid-item service-name">SSH</div><div class="grid-item"><code>ssh 192.168.1.3</code></div></div>',
126 '192.168.1.3 | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://192.168.1.3:80">http://192.168.1.3:80</a></code></div></div> ',
127 '192.168.1.3 | 443 | <div class="grid-statuses"><div class="grid-item service-name">HTTPS</div><div class="grid-item"><code><a href="https://192.168.1.3:443">https://192.168.1.3:443</a></code></div></div> ',
128 'jupiter.home.lan | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping jupiter.home.lan</code></div></div>',
129 'jupiter.home.lan | 22 | <div class="grid-statuses"><div class="grid-item service-name">SSH</div><div class="grid-item"><code>ssh jupiter.home.lan (port 22)</code></div></div>',
130 'jupiter.home.lan | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://jupiter.home.lan:80">http://jupiter.home.lan:80</a></code></div></div>',
131 'jupiter.home.lan | 443 | <div class="grid-statuses"><div class="grid-item service-name">HTTPS</div><div class="grid-item"><code><a href="https://jupiter.home.lan:443">https://jupiter.home.lan:443</a></code></div></div>',
132 '---------------------------------------------------------------',
133 '<div class="grid-container"><div class="grid-item item-1">venus.home.lan</div><div class="grid-item item-2 subheadline">FriendlyElec NanoPi R2s</div><div class="grid-item item-3">' . $LOGO_OPENWRT. '</div></div>|headline',
134 '---------------------------------------------------------------',
135 '192.168.1.6 | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping 192.168.1.6</code></div></div> ',
136 '192.168.1.6 | 22 | <div class="grid-statuses"><div class="grid-item service-name">SSH</div><div class="grid-item"><code>ssh 192.168.1.6</code></div></div>',
137 '192.168.1.6 | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://192.168.1.6:80">http://192.168.1.6:80</a></code></div></div> ',
138 '192.168.1.6 | 443 | <div class="grid-statuses"><div class="grid-item service-name">HTTPS</div><div class="grid-item"><code><a href="https://192.168.1.6:443">https://192.168.1.6:443</a></code></div></div> ',
139 'venus.home.lan | ping| <div class="grid-statuses"><div class="grid-item service-name">ICMP</div><div class="grid-item"><code>ping venus.home.lan</code></div></div>',
140 'venus.home.lan | 22 | <div class="grid-statuses"><div class="grid-item service-name">SSH</div><div class="grid-item"><code>ssh venus.home.lan (port 22)</code></div></div>',
141 'venus.home.lan | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://venus.home.lan:80">http://venus.home.lan:80</a></code></div></div>',
142 'venus.home.lan | 443 | <div class="grid-statuses"><div class="grid-item service-name">HTTPS</div><div class="grid-item"><code><a href="https://venus.home.lan:443">https://venus.home.lan:443</a></code></div></div>',
143 '---------------------------------------------------------------',
144 'External Servers|headline ',
145 '---------------------------------------------------------------',
146 'zotero.willitreplicate.com | 80 | <div class="grid-statuses"><div class="grid-item service-name">HTTP</div><div class="grid-item"><code><a href="http://venus.home.lan:80">http://zotero.willitereplicate.com:80</a></code></div></div>',
147 'zotero.willitreplicate.com | 443 | <div class="grid-statuses"><div class="grid-item service-name">HTTPS</div><div class="grid-item"><code><a href="https://venus.home.lan:443">https://zotero.willitereplicate.com:443</a></code></div></div>',
148 'www.example.com | -80 | WWW server @ www.example.com' // no colon here!
149 );
150
151 // exec command for ping: -l3 (preload) is recommended but
152 //defaults($ping_command, 'ping -l3 -c3 -w1 -q'); // might not work everywhere
153 $config['ping_command'] = 'ping -c3 -w1 -q';
154 $config['ping6_command'] = 'ping6 -c3 -w1 -q';
155
156 // fsockopen timeout; might need adjustment depending on network
157 $config['timeout'] = 4;
158
159 // show a very simple progress indicator (requires Javascript)
160 // may be disabled also by adding '?noprogress' to the script's URL
161 $config['progressindicator'] = TRUE;
162
163 // strings for online and offline (by default these are used for CSS, too)
164 $config['online'] = 'Online';
165 $config['offline'] = 'OFFLINE';
166
167 // print date and/or time (leave empty to show no timestamp)
168 $config['datetime'] = 'l, F j, Y, H:i:s T';
169
170 // RSS alert feed
171 $config['rssfeed'] = TRUE; // use to enable or disable RSS feeds
172 // URL of RSS feed
173 $config['rssfeedurl'] = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'].'?rss';
174 // RSS feed title
175 $config['rsstitle'] = "RSS alert feed of {$config['title']}";
176 // RSS header e.g. to include in $htmlheader; set to '' to offer no RSS
177 $config['rssheader'] = '<link rel="alternate" type="application/rss+xml" '."title=\"{$config['rsstitle']}\" href=\"{$config['rssfeedurl']}\" />";
178 // RSS alert link (might point e.g. to your network status homepage)
179 $config['rsslink'] = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'].'?noprogress';
180 // RSS date and/or time format (here we use a ISO 8601 format)
181 $config['rssdatetime'] = 'o-m-d H:i:s T';
182
183 // HTML Header
184 $config['htmlheader'] = <<<EOH
185<!doctype html>
186<html>
187<head>
188<title>{$config['title']}</title>
189<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-15" />
190<meta name="description" content="{$config['description']}" />
191{$config['rssheader']}
192<link href="https://fonts.googleapis.com/css?family=Quicksand:300,500" rel="stylesheet">
193<style type="text/css">
194 body {
195 font-family: Quicksand, Geneva, Calibri, Moderno, "Lucida Sans", Verdana, Arial, Helvetica, sans-serif;
196 font-weight: 500;
197 background: black;
198 color: white;
199 }
200
201 h1, h2, h3, h4, h5, h6 {
202 /*
203 border-bottom: 1px solid #999999;
204 color: #500000;
205 */
206 font-weight: 800;
207 margin-bottom: 1em;
208 margin-top: 2em;
209 text-align: center;
210 }
211
212 a {
213 margin: 0;
214 padding: 0;
215 border-radius: 1em;
216 }
217
218 a:link {
219 text-decoration: none;
220 }
221
222 a:visited {
223 color: blueviolet;
224 }
225
226 a:hover {
227 font-size: 1.5em;
228 font-weight: bold;
229 box-shadow: 3px 3px 5px black;
230 border-radius: 1em;
231 padding: 0.5em;
232 }
233
234 a:active {
235 color: red;
236 box-shadow: inset 0 0 15px red;
237 }
238
239 div#alert {
240 border: 1px solid red;
241 padding: 0.2em 1.5em;
242 margin: 1em 0;
243 }
244
245 div#container {
246 width: 37em;
247 margin: 0 auto;
248 position: relative;
249 }
250
251 div#progress {
252 position: fixed;
253 top: 0;
254 left: 0;
255 background: darkblue;
256 color: white;
257 padding: 0.2em 1em 0.2em 1em;
258 }
259
260 .datetime {
261 font-size: 87%;
262 font-weight: bolder;
263 text-align: center;
264 margin-bottom: 2em;
265 }
266
267 .hidden {
268 display: none !important;
269 }
270
271 .status_table {
272 border: 1px solid #333333;
273 border-collapse: collapse;
274 margin: auto;
275 }
276
277 .status_table td {
278 border: 1px solid white;
279 padding: 0.25em;
280 }
281
282 .status_table td.headline {
283 font-weight: bolder;
284 background-color: gray;
285 color: white;
286 padding: 0.25em 0.25em 0 0.25em;
287 margin: 0 0 0 0;
288 font-size: 1.5em;
289 }
290
291 .subheadline {
292 font-family: impact;
293 font-size: 0.75em;
294 color: white;
295 font-weight: bold;
296 text-shadow: 2px 2px 4px black;
297 text-transform: none;
298 -webkit-text-stroke: black 1px;
299 }
300
301 .status_table td.description_field {
302 background-color: lightgray;
303 color: black;
304 }
305
306 .version a {
307 font-weight: bolder;
308 color: auto;
309 text-decoration: none;
310 }
311
312 .version a:hover {
313 text-decoration: underline;
314 }
315
316 .version {
317 font-size: 73%;
318 text-align: center;
319 color: white;
320 background: gray;
321 }
322
323 .logo-hardware {
324 height: 25px;
325 margin: 0 0 0 0;
326 }
327
328 .grid-container {
329 display: grid;
330 grid-template-columns: 300px 250px 100px;
331 grid-template-rows: auto;
332 grid-column-gap: 0;
333 grid-row-gap: 0;
334 border-bottom: solid medium black;
335 }
336
337 .grid-container .grid-item {
338 border: none;
339 }
340
341 .grid-container > .grid-item:first-child {
342 justify-self: start;
343 align-self: end;
344 }
345
346 .grid-container > .grid-item:nth-child(2) {
347 justify-self: start;
348 align-self: center;
349 }
350
351 .grid-container > .grid-item:nth-child(3) {
352 justify-self: end;
353 align-self: end;
354 }
355
356 .grid-statuses {
357 display: grid;
358 grid-template-columns: 65px 400px;
359 grid-template-rows: 1.5em;
360 grid-gap: 0;
361 }
362
363 .grid-statuses > .grid-item {
364 align-self: center;
365 padding: 3px;
366 }
367
368 .grid-statuses > .grid-item:first-child {
369 background: orange;
370 border-radius: 5px;
371 border: solid 1px black;
372 box-shadow: 1px 1px 2px black;
373 justify-self: end;
374 text-align: right;
375 width: 3.25em;
376 }
377
378 .grid-statuses > .grid-item:nth-child(2) {
379 justify-self: start;
380 padding-left: 0.5em;
381 }
382
383 .{$config['offline']} {
384 color: red;
385 background-color: #FFB6B6;
386 font-weight: bold;
387 text-align: center;
388 font-family: impact;
389 -webkit-text-stroke: black 1px;
390/* padding-left: 0.8em !important; */
391 }
392
393 .{$config['online']} {
394 color: black;
395 background-color: #D9FFB3;
396 text-align: center;
397/* padding-left: 0.8em !important; */
398}
399</style>
400</head>
401
402<body>
403<div id="container">
404EOH;
405
406 // HTML/page footer
407 $config['htmlfooter'] = "</div>\n</body>\n</html>";
408
409 // Amount of time to cache the script. 0 to disable.
410 $config['cachetime'] = 5*60;
411
412 // path to writable directory we can cache in. Null or false will disable caching.
413 $config['cachepath'] = getcwd() .'/files';
414
415 // Your support/admin contact address
416 $config['contact'] = 'N/A';
417
418 return $config;
419}
420
421/**
422 * Parse user supplied config
423 * @param string Configuration file
424 * @param array Configuration defaults
425 * @return array Configuration array
426 */
427function parseConfig($file, $config){
428 // including $configfile if available
429 if (file_exists($file) && is_readable($file)){
430 @include($file);
431 foreach($config as $key=>$var){
432 if(isset($$key)){
433 $config[$key]=$$key;
434 }
435 }
436 }
437
438 return $config;
439}
440
441/**
442 * Generate the default config to simplify instalation.
443 * @param string Filename for config
444 */
445function genConfig(){
446 global $config;
447 if(file_exists($config['configfile'])){
448 die("Config file alread exists!\n");
449 }
450 $fi=fopen($_SERVER['PHP_SELF'],'r');
451 if($fi===false) die("Error opening ".$_SERVER['PHP_SELF']);
452
453 $fo=fopen($config['configfile'],'w');
454 if($fo===false) die("Error opening ".$config['configfile']);
455 fwrite($fo, "<?php\n");
456 fwrite($fo, "/*\n");
457 fwrite($fo, " * This file contains the commented defaults. Feel free to delete\n");
458 fwrite($fo, " * the unused options. \n");
459 fwrite($fo, " */\n");
460
461 while(($l=fgets($fi,400))!== false){
462 if (trim($l)=='$config=array();'){
463 break;
464 }
465 }
466 while(($l=fgets($fi))!== false){
467 if(trim($l)=='}' || trim($l)=='return $config;'){
468 break;
469 }
470 fwrite($fo,preg_replace('/\$config\[[\'"]([^\'"]+)[\'"]\]/','\$$1',"//".trim($l))."\n");
471 }
472 fclose($fi);
473 fclose($fo);
474}
475
476/**
477 * Quick way to create status update.
478 * You shouldn't have to think about what todo when you have problems
479 * Run from command line only!
480 */
481function genStatusUpdate(){
482 global $config;
483
484 $date=date('Y-m-d H:i O');
485 $message=readline("Enter the message you would like to use:");
486 $statusstr=<<<EOT
487<p><b>Status as of $date</b><br />
488$message
489</p>
490EOT;
491
492 if((file_exists($config['alertfile']) && is_writable($config['alertfile'])) || is_writable(dirname($config['alertfile']))){
493 $fp=fopen($config['alertfile'],'w');
494 if($fp===false) die("Error writing alert file!");
495 fwrite($fp, $statusstr);
496 fclose($fp);
497 }else{
498 die("File Exists/Not Writable");
499 }
500
501}
502
503/**
504 * RSS code
505 */
506function rss(){
507 global $config;
508 if(!$config['rssfeed']){
509 // RSS requested even though it was disabled
510 exit;
511 }
512 header("Content-Type: application/rss+xml");
513 echo "<?xml version=\"1.0\"?><rss version=\"2.0\">\n<channel>\n";
514 echo "<title>{$config['rsstitle']}</title>\n<link>{$config['rsslink']}</link>\n";
515 echo "<description>{$config['rsstitle']}</description>\n";
516 echo "<language>en</language>\n";
517 if (file_exists($config['alertfile']) && is_readable($config['alertfile']))
518 {
519 echo "<item>\n<title>Alert ".date($config['rssdatetime'], filemtime($config['alertfile']))
520 . " for {$config['rsstitle']}</title>\n";
521 echo '<pubDate>'.date("r", filemtime($config['alertfile']))."</pubDate>\n";
522 echo "<link>{$config['rsslink']}</link>\n";
523 echo '<description><![CDATA[';
524 @include($config['alertfile']);
525 echo "]]></description>\n</item>\n";
526 }
527 echo "</channel>\n</rss>\n";
528}
529
530/**
531 * HTML Code
532 * @param cache Cache object
533 */
534function html($cache){
535 global $config;
536 // output HTML/page header
537 echo $config['htmlheader'];
538
539 // headline, date and time and start of table
540 echo "<h1>{$config['headline']}</h1>\n";
541 if ($config['datetime']) echo '<p class="datetime">as of ' . date($config['datetime']) . "</p>\n";
542
543 // show the contents of $alertfile if it is readable and larger than 2 bytes
544 if (file_exists($config['alertfile']) && is_readable($config['alertfile']))
545 {
546 clearstatcache();
547 if (filesize($config['alertfile']) > 2)
548 {
549 echo "<div id=\"alert\">\n";
550 @include($config['alertfile']);
551 echo "</div>\n";
552 }
553 }
554
555 // show a simple progress indicator
556 if (($_REQUEST['noprogress'] !== NULL) || ($argv[1] == 'noprogress'))
557 {
558 $config['progressindicator'] = $FALSE;
559 }
560 if ($config['progressindicator'])
561 {
562 echo '<script type="text/javascript">
563 document.write("<div id=\"progress\">Checks in progress ...</div>");'
564 ."</script>\n";
565 }
566
567 echo "<table class=\"status_table\">\n";
568
569 // main loop of checks
570 foreach ($config['checks'] as $check)
571 {
572 $status = $config['offline']; // default state
573 $diagnostics = ''; // mouse-over for tooltips
574 $output = TRUE; // print a line or print no line
575 list($host,$port,$description) = explode('|',"$check||"); // the 2 extra '|'s are to avoid notices about undefined offsets
576 $host = trim($host);
577 $port = trim($port);
578
579 switch ($port)
580 {
581 case '': // ignore lines with empty port
582 $output = FALSE; break;
583 case (substr($port,0,1)=='-'): // negative ports, '-ping', and
584 // any "port" starting with '-' is considered a disabled check
585 $output = FALSE; break;
586 case 'headline': // print a headline within the status table
587 // we enclose it with invisible <br>== == for nicer text output
588 echo '<tr><td class="headline" colspan="2">'
589 . '<span class="hidden"> <br />== </span>'
590 . $host
591 . '<span class="hidden"> ==</span>'
592 . "</td></tr>\n";
593 $output = FALSE; break;
594 case 'ping': // do an ICMP ping
595 $ping=exec("{$config['ping_command']} $host",$pingoutput,$pingreturn);
596 // This should continue on into ping6 as they share everything but the command.
597 case 'ping6': // do an ICMP ping
598 if(!isset($ping)){
599 $ping=exec("{$config['ping6_command']} $host",$pingoutput,$pingreturn);
600 }
601 if(strlen($ping)>10)
602 {
603 // strlen($ping)>10 works around a bug in Debian ping (", pipe 3")
604 // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=456192
605 $status = $config['online']; $diagnostics = "$ping :: $pingreturn";
606 }
607 else $diagnostics = "$ping :: $pingreturn";
608 // uncomment this if you want the full output as HTML comment
609 //echo "\n<!-- "; print_r($pingoutput); echo "-->\n";
610 // *nix ping command's return value meanings:
611 // 0: all OK; 1: an error occured; 2: host unknown
612 unset($pingoutput);
613 unset($ping);
614 break;
615 default: // look if a TCP connection to port can be opened
616 $time_start = microtime(true);
617 $fp = @fsockopen($host, $port, $errno, $errstr, $config['timeout']);
618 $time_end = microtime(true);
619 $time = number_format(($time_end - $time_start)*1000,1);
620
621 if ($fp)
622 {
623 // fsockopen worked, service is online
624 $status = $config['online'];
625 $diagnostics = "$time ms";
626 fclose($fp);
627 }
628 else if ($errno<0) { $diagnostics = "errno=$errno; Host unknown?"; }
629 else { $diagnostics = $errstr; }
630 }
631
632 // output results
633 if ($output)
634 echo "<tr><td class=\"description_field\">$description</td><td class=\"$status\" title=\"$diagnostics\">$status</td></tr>\n";
635 $cache->flush();
636 }
637
638 echo "</table>\n";
639
640 // make progress indicator disappear by means of Javascript
641 if ($config['progressindicator']) {
642 echo <<<EOT
643<script>
644progressindicator = document.getElementById("progress");
645progressindicator.innerHTML = "asdf";
646progressindicator.style.visibility = 'hidden';
647</script>
648EOT;
649 }
650
651 // output $version and HTML/page footer
652 if (!empty($config['version'])) echo "<p class=\"version\">{$config['version']}</p>\n";
653 echo "{$config['htmlfooter']}\n";
654
655}
656
657/**
658 * Class to wrap caching and simplifiy reuse
659 */
660class cache{
661
662 /**
663 * @var string
664 * File to cache in
665 */
666 private $file;
667
668 /**
669 * @var string
670 * Time to keep cache
671 */
672 private $time;
673
674 /**
675 * @var int
676 * Timestamp of cache file.
677 */
678 private $filetime;
679
680 /**
681 * @var string
682 * Buffer to store the output in for later caching so the page can be flushed.
683 */
684 private $buffer='';
685
686 /**
687 * @var bool
688 * If caching is enabled
689 */
690 private $enabled=false;
691
692 /**
693 * Constructor
694 * On new cache() run this
695 * @param string File to cache in
696 * @param array The config array
697 */
698 public function __construct($file, $config){
699 if($config['cachepath']!=null && $config['cachepath']!=false && $config['cachetime']>0){
700 // Caching isn't disabled explicitly. Continue
701 $this->file=$config['cachepath'].'/'.$file;
702 $this->time=$config['cachetime'];
703
704 // If output_buffering isn't small the progress indicator won't function properly
705 $output_buffering = ini_get("output_buffering");
706 if($config['progressindicator']==TRUE && $output_buffering > 0){
707 error_log("warning: progressindicator incompatible with php.ini output_buffering > 0. Caching disabled! Create a .htaccess with the contents: php_value output_buffering \"0\"");
708 return;
709 }
710 if((file_exists($this->file) && is_writable($this->file)) || is_writable(dirname($this->file))){
711 // We can wrte to the cache dirs enable caching.
712 // No sense caching if we can't write!
713 $this->enabled=true;
714 }
715 }
716 }
717
718 /**
719 * Check if the file is cached and still valid
720 * @return bool
721 */
722 private function isCached(){
723 $this->filetime=@filemtime($this->file);
724 if(file_exists($this->file) && (time() - $this->time <$this->filetime)){
725 return true;
726 }
727 return false;
728 }
729
730 /**
731 * Caching Start function.
732 * If it finds the file is already cached it will return the cached file and
733 * exit. Otherwise will ob_start()
734 */
735 public function start(){
736 if(!$this->enabled)return;
737 if($this->isCached()){
738 // Cache is good. Return the file and exit!
739 @include $this->file;
740 echo "<!-- Cached ".date('jS F Y H:i', $this->filetime)."-->";
741 exit;
742 }
743 // Need to create cache buffer output.
744 ob_start();
745 }
746
747 /**
748 * Store the page to our internal buffer and send the current page contents out.
749 */
750 public function flush(){
751 if($this->enabled){
752 // Store the contents
753 $this->buffer.=ob_get_contents();
754 // Flush contents of the buffer
755 ob_flush();
756 }
757 // Send to browser
758 flush();
759 }
760
761 /**
762 * Caching end function
763 * Writes the output to a file so it is cached for the next requests.
764 */
765 public function end(){
766 if(!$this->enabled)return;
767 if((file_exists($this->file) && is_writable($this->file)) || is_writable(dirname($this->file))){
768 // We can write the cache lets get the buffer contents and write it.
769 $fp=fopen($this->file,'w');
770 fwrite($fp, $this->buffer . ob_get_contents());
771 fclose($fp);
772 // Close the buffer and send the remaining contents to the browser
773 ob_end_flush();
774 }
775 }
776}
777
778
779
780
781// ------------------------------------------------- controler part of script
782
783// Get the default config
784$config = defaultConfig();
785
786// Check first for genconfig before we try to parseConfig
787if($argv[1] == 'genconfig'){
788 genConfig();
789 exit;
790}
791
792// Parse the user's config
793$config = parseConfig($config['configfile'], $config);
794
795
796// Main Controler
797if($_REQUEST['rss'] !== NULL or $argv[1] == 'rss'){
798 // Run rss()
799 // Create instance of cache class.
800 $cache=new cache('rss.xml',$config);
801 // Start the caching engine
802 $cache->start();
803 // Do the actuall rss function
804 rss();
805 // End the rss engine
806 $cache->end();
807}else if($argv[1] == 'setstatus'){
808 genStatusUpdate();
809}else if($argv[1] == '-h' || $argv[1] == '--help'){
810 echo <<<EOT
811{$config['version']} Help
812 rss - Output RSS
813 setstatus - netstat.txt generation helper
814 genconfig - generate sample netstat.conf.php
815
816EOT;
817} else {
818 // Create instance of cache class.
819 $cache=new cache('index.html',$config);
820 // Start the caching engine
821 $cache->start();
822 // Do the actuall html function
823 html($cache);
824 // End the rss engine
825 $cache->end();
826}
827
828
829/*
830 * License
831 *
832 * This script is free software; you can redistribute it and/or modify it
833 * under the terms of the GNU General Public License as published by
834 * the Free Software Foundation; either version 2 of the License, or
835 * (at your option) any later version.
836 *
837 * This script is distributed in the hope that it will be useful,
838 * but WITHOUT ANY WARRANTY; without even the implied warranty of
839 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
840 * GNU General Public License for more details.
841 *
842 * You should have received a copy of the GNU General Public License
843 * along with this script; if not, write to the
844 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
845 * Boston, MA 02111-1307 USA
846
847 * History
848 *
849 * 0.09 * 2009-10-26 first buggy "punish me" alpha release candidate
850 * 0.10 * 2009-11-07 added stopwatch (time diagnostics) for online services
851 * 0.11 * 2009-11-09 added RSS feed option for alerts
852 * 0.12 * 2009-11-12 cleaned up and simplified settings mechanism
853 * 0.13 * 2009-11-18 $alertfile is only included if larger than 2 bytes
854 * 0.14 * 2009-12-05 Default CSS code change to improve font size scaling
855 * 0.15 * 2012-08-04 added ping6 (suggested by Todd Johnson; thanks!)
856 * 1.x * 2012-..-.. New version by Todd Johnson w/ caching, setstatus, ...
857 *
858 */
859
860?>
861