<?php
/***** File Info *****\
== Version: 2.0.0 ==
== Description: Multi-Output Server Monitoring Console (MOSMC) ==
== Author: Alesandro Ortiz - The_PHP_Jedi <thephpjedi@thephpjedi.com> ==
== Created: Wednesday, July 2nd 2008 ==
== Modified: Sunday, January 11th 2009 ==
== License: Creative Commons Attribution-Noncommercial 3.0 United States ==
== License URL: http://creativecommons.org/licenses/by-nc/3.0/us/ ==
\***** File Info *****/
/*** Changelog ***\
== 1.1.0 ==
= Public release
=
== 1.1.1 & 1.1.2 ==
= Display fix: Fixed minor bugs regarding display of information.
=
== 1.1.3 ==
= New Feature: Different e-mail subject, sender, and recipient for Reports and Alerts.
= Bug fix: Site monitor would show incorrect information in most cases where more than one site was monitored.
= Display fix: E-mail report would show 'MB' in RAM information in a new line.
=
== 1.1.4 ==
= New Feature: Added User Agent "MOSMC-PHP" when checking sites.
= Bux fix: Minor. Related to services checking. Did not affect functionality.
=
== 1.1.5 (07/22/2008) ==
= Bug fix: PHP error. Might be major bug if running through cron.
=
== 2.0.0 (01/11/2009) ==
= Update: Re-wrote MOSMC in OOP.
= Update: Re-worded some of the comments to make clearer what MOSMC is doing.
= Update: Documentation is now more specific, and has been re-worded to make it clearer.
= New Feature: Added configuration option: hard drive partition to use for hard drive usage stats.
= New Feature: Integrated HTTP authentication.
= New Feature: Notification when new MOSMC release is available (optional; See configuration options).
= New Feature: Test variables simulating various scenarios have been included. Search for "Testing Variables" in the source. Note: Email and Browser output testing variables are separated.
= Bug fix: PHP mail() would not set the Return-Path header properly, often required for SPF. Now fixed, using sender email as return path address.
= Bug fix: Service listening check now returns proper results. In limited cases, it would show a service as listening when it wasn't.
= Bug fix: Only first word of HTTP status description would be shown.
= Bug fix: Now handles DNS errors and cURL errors properly, and when the server hangs connections (an alert is triggered).
= Bug fix: When testing sites, a 30 second timeout is used. Previously, MOSMC would seem to hang if a site was not responding.
= Security fix: Vulnerability when register_globals is enabled in PHP configuration.
= Display fix: Alert SMS output has updated formatting for improved readability.
= Misc fix: Cleaned up HTML output formatting.
= Misc fix: Cleaned up PHP source code formatting.
\*** Changelog ***/
/*** Comments ***\
= MOSMC is pronounced as "moss-em-see".
= Feel free to improve upon it, but please let me know what you've done with it. :)
\*** Comments ***/
/*** Notes & Usage ***\
== How To Configure Initially ==
= Go to the area delimited as "Configuration" (the Config class), and insert/modify values as necessary.
= For configuration options with arrays (multiple values), such as services and sites, increase the array key (inside the square brackets) by one for each new service/site configured.
= Then proceed to use MOSMC via a Web browser, by placing it in a PHP-enabled Web server directory.
= For full utilization of MOSMC, it is recommended you set up cron jobs, as detailed later in this documentation. This enables alerts on component failure, as well as periodic reports.
=
== General ==
= Status codes are in ascending order of severity. Higher number, higher severity.
= Site monitor links to URL only in case of failure. Otherwise, it doesn't link to the URL being tested.
= 'Component' refers to any item that is monitored by MOSMC (such as a specific website or service).
=
== Cron Jobs // Overview ==
= Cron jobs can be run to either notify you on failure of a component or to send reports periodically.
= There are two types of cron jobs: Those that trigger alerts only on failure (type = failure), and those who send a full report of all monitored components, regardless of their status (type = report)
= Additionally, you can set up a cron job to check for updates periodically (optional, of course).
=
== Setting Up Local Cron Jobs ==
= Use command 'php /path/to/monitor.php type password'
= Example: 'php /path/to/monitor.php failure yourpass' if you wanted to send emails only on component failure
=
== Setting Up Remote Cron Jobs or On-Demand Reporting ==
= Use monitor.php?cron=1&type=failure&pass=yourpass if you want to send an email formatted for SMS on component failure with the details of the failing component
= Use monitor.php?cron=1&type=report&pass=yourpass if you want to send email with full report regardless of status (Not optimized for SMS)
= To send an e-mail, you must to append the password you've set in the configuration to the URL. This prevents others from triggering e-mails. Example: monitor.php?cron=1&type=failure&pass=yourpass
= Note: HTTP authentication (username and password) is not required when using web cron jobs.
=
== Checking for Updates Automatically ==
= This is optional, but recommended. There are two methods to check for updates:
= Add a cron job, either locally ('php /path/to/monitor.php update password'), or remotely (monitor.php?cron=1&type=update&pass=yourpass)
= It is recommended that you set your cron job to check once a week, preferably Monday or Tuesday (updates will probably be published on these days)
= Note: An e-mail will be sent if an update is available each time you run the cron job.
= Note: The Web console may check for updates each time you access it, depending on your configuration.
= Note: MOSMC checks updates using cURL from the terminal, when using cron jobs. It checks using an AJAX request when using the Web console.
= Note: No information is deliberately sent or stored, other than your current version number. Standard information recorded by Apache logs is saved, but will *NEVER* be used or analyzed whatsoever.
=
== How MOSMC-PHP Tests Components ==
= Services: Checks first if services are listening through netstat, then checks if it accepts internal connections, and then checks if it accepts external connections (last two using fsockopen() ).
= Sites: Checks HTTP header response using cURL (from the terminal, not the PHP library).
=
== Disclaimers ==
=
****====IMPORTANT====****
= MOSMC outputs certain information that can make it easier to target your system for vulnerabilities, such as ports where services are running, and the types of connections accepted by services.
= It is highly recommended that your MOSMC script is *ALWAYS* in a location you only know, and that is not easily guessable.
= It is highly recommended that your MOSMC script *ALWAYS* be protected by a strong username and password combination.
= It is highly recommended that you have register_globals *DISABLED* in your PHP configuration (although MOSMC makes sure only required input is accepted).
****====IMPORTANT====****
=
= MOSMC has only been tested using PHP 5.2 on Debian 4.0 (Etch). No guarantee of functionality or reliability is given (although I've tested it extensively, and it seems very reliable).
= MOSMC will probably *not* work in PHP 4, as it uses PHP 5 OOP and functions only supported by PHP 5.
\*** Notes & Usage ***/
class Config {
protected $server, $email, $web, $service, $load, $ram, $fs, $site;
public function __construct()
{
/*** Configuration ***/
//External IP of server to test
$this->server->ip = '1234.56.78.90';
//If using MOSMC in a server with a dynamic IP linked to a DNS record, comment the line above and uncomment line below
//$this->server->ip = gethostbyname('sub.domain.tld');
//Name of your server
$this->server->name = 'Server Name';
//E-mail to send Reports to
$this->email->recipient->report = 'reports@yourdomain.tld';
//E-mail to send Alerts to
$this->email->recipient->alert = 'alerts@yourdomain.tld';
//E-mail to send Update notifications to (optional)
$this->email->recipient->update = 'update@yourdomain.tld';
//E-mail to send Reports from
$this->email->sender->report = 'mosmc-report@yourdomain.tld';
//E-mail to send Alerts from
$this->email->sender->alert = 'mosmc-alert@yourdomain.tld';
//E-mail to send Update notifcations from (optional)
$this->email->sender->update = 'mosmc-update@yourdomain.tld';
//Subject of Report e-mail (Recommended: Default)
$this->email->subject->report = $this->server->name.' Report';
//Subject of Alert e-mail (Recommended: Default)
$this->email->subject->alert = $this->server->name.' Alert';
//Subject of Update notification e-mail (Recommended: Default)
$this->email->subject->update = 'MOSMC Update needed at '.$this->server->name;
//E-mail headers for Reports (Recommended: Default)
$this->email->header->report = 'From: "MOSMC" <'.$this->email->sender->report.">\r\n";
//E-mail headers for Alerts (Recommended: Default)
$this->email->header->alert = 'From: "MOSMC" <'.$this->email->sender->alert.">\r\n";
//E-mail headers for Update notifications (Recommended: Default)
$this->email->header->update = 'From: "MOSMC" <'.$this->email->sender->update.">\r\n";
//Set minimum threshold level to send email. Values: 0 = Send on Warning and Max/Alert (recommended); 1 = Send only on Max/Alert
$this->email->threshold = 0;
//Username used for viewing the Web console.
$this->web->username = 'yourUsername';
//Password for viewing the Web console, and sending e-mails.
$this->web->password = 'yourVerySecurePassword';
//Set if you want to check for updates automatically when visiting the Web console. 1 = yes (default), 0 = no
$this->web->update = 1;
//Human readable service name
$this->service[0]->name = 'httpd';
$this->service[1]->name = 'MySQL';
//Type of connection accepted by service. Type 0 = 'Local service' (only internal connections), type 1 = 'Public service' (internal + external connections)
$this->service[0]->type = 1;
$this->service[1]->type = 0;
//Port service is running on
$this->service[0]->port = 80;
$this->service[1]->port = 3306;
//Statistics Warning and Maximum/Alert thresholds
//Load average
$this->load->warn = '4.00';
$this->load->max = '8.00';
//RAM
$this->ram->warn = 460;
$this->ram->max = 512;
//Storage (File System)
$this->fs->partition = 'sda1'; //As listed in 'df' after /dev/
$this->fs->warn = 8*1024; //8GB (2.0 GB free)
$this->fs->max = 10*1024; //10GB (0.0 GB free)
//Human readable website name
$this->site[0]->name = 'Google';
$this->site[1]->name = 'Slashdot';
//URL of websites to check (You may use alternate ports. Ex: http://domain.tld:8080)
$this->site[0]->url = 'http://www.google.com';
$this->site[1]->url = 'http://slashdot.org';
/*** Configuration ***/
}
}
class Monitor extends Config {
protected $version, $get, $services, $sites;
private $global, $key;
public function __construct()
{
$this->sanitizeGlobals();
$this->getArguments();
parent::__construct();
$this->version = '2.0.0';
$this->populateData();
}
private function sanitizeGlobals()
{
//We don't need any of these
foreach(array($_POST, $_COOKIE, $_FILES, $_ENV, (isset($_SESSION) && is_array($_SESSION)) ?
$_SESSION : array()) as $global)
{
}
//Lets only grab the input we need, and get rid of everything else
foreach(array($_GET, $_SERVER) as $global)
{
foreach($global as $key => $value)
{
if($key == 'argv')
{
$this->get->$key = $value;
}
elseif($key == 'PHP_AUTH_USER')
{
$this->get->http->user = $value;
}
elseif($key == 'PHP_AUTH_PW')
{
$this->get->http->pass = $value;
}
elseif($key == 'cron' || $key == 'type' || $key == 'pass')
{
$_GET[$key] = $value;
}
else
{
unset($_GET[$key], $_SERVER[$key]);
}
}
}
}
private function getArguments()
{
//Check if running cron job. If so, get configuration options from CLI.
if(isset($this->get->argv))
{
$this->get->cron = 1;
$this->get->type = strtolower($this->get->argv[1
]);
$this->get->pass = $this->get->argv[2];
}
else
{
//Already sanitized. Contains only necessary information.
foreach($_GET as $key => $value)
{
$this->get->$key = $value;
}
}
}
private function populateData()
{
/*** Populate Data Variables ***/
$this->services->count = count($this->service);
$this->sites->count = count($this->site);
$this->checkServices();
$this->grabStats();
$this->checkSites();
/*** Populate Data Variables ***/
}
private function checkServices()
{
//Check if ports are open for each service
for($i=0; $i < $this->services->count; $i++)
{
$this->service[$i]->status->listen = -1;
$this->service[$i]->status->internal = -1;
$this->service[$i]->status->external = -1;
//Check if services are listening
$listenCheck = exec('netstat -l -n | grep ":'.$this->service[$i]->port.' " | wc -l');
//If listening, win
if($listenCheck > 0)
{
$this->service[$i]->status->listen = 0;
}
//If not listening, fail
else
{
$this->service[$i]->status->listen = 1;
}
//Check internal connection if service is listening for connections
if($this->service[$i]->status->listen == 0)
{
$fp = @fsockopen('localhost', $this->service[$i]->port, $errno, $errstr, 1
);
//If internal connection is accepted, win
if($fp)
{
$this->service[$i]->status->internal = 0;
}
//If internal connection is rejected, fail
else
{
$this->service[$i]->status->internal = 1;
}
}
//Check external connection if internal connection is available
if($this->service[$i]->status->internal == 0 && $this->service[$i]->status->listen == 0)
{
$fp = @fsockopen($this->server->ip, $this->service[$i]->port, $errno, $errstr, 1
);
//If external connection is accepted, and is supposed to be accepted, win
if($fp && $this->service[$i]->type == 1)
{
$this->service[$i]->status->external = 0;
}
//If external connection is rejected, and not supposed to be accepted, win
elseif(!$fp && $this->service[$i]->type == 0)
{
$this->service[$i]->status->external = 1;
}
//If external connection is rejected, and is supposed to be accepted, fail
elseif(!$fp && $this->service[$i]->type == 1)
{
$this->service[$i]->status->external = 2;
}
//If external connection is accepted, but not supposed to be, fail + alert
elseif($fp && $this->service[$i]->type == 0)
{
$this->service[$i]->status->external = 3;
}
}
}
}
private function grabStats()
{
//Grab Uptime + Load Averages
$this->uptime = @exec('uptime');
//Extract load averages
preg_match("/averages?: ([0-9\.]+),[\s]+([0-9\.]+),[\s]+([0-9\.]+)/", $this->uptime, $this->loads);
$this->load->status = -1;
if($this->load->warn > $this->loads[1] && $this->loads[1] < $this->load->max)
{
$this->load->status = 0;
}
elseif($this->load->warn <= $this->loads[1] && $this->loads[1] < $this->load->max)
{
$this->load->status = 1;
}
else
{
$this->load->status = 2;
}
//Extract uptime
$this->uptime = explode(' up ', $this->uptime);
$this->uptime = explode(',', $this->uptime[1
]);
$this->uptime = $this->uptime[0].', '.trim($this->uptime[1
]);
//Grab number of users logged in
//Grab RAM usage
$this->ram->used = trim(shell_exec('free -m | grep "buffers/cache" | awk \'{print $3}\''));
$this->ram->total = shell_exec('free -m | grep Mem | awk \'{print $2}\'');
$this->ram->status = -1;
if($this->ram->warn > $this->ram->used && $this->ram->used < $this->ram->max)
{
$this->ram->status = 0;
}
elseif($this->ram->warn <= $this->ram->used && $this->ram->used < $this->ram->max)
{
$this->ram->status = 1;
}
else
{
$this->ram->status = 2;
}
//Grab HDD File System usage
$this->fs->free = round(shell_exec('df | grep '.$this->fs->partition.' | awk \'{print $4}\'')/1024
, 2
);
$this->fs->used = round(shell_exec('df | grep '.$this->fs->partition.' | awk \'{print $3}\'')/1024
, 2
);
$this->fs->total = round(shell_exec('df | grep '.$this->fs->partition.' | awk \'{print $2}\'')/1024
, 2
);
$this->fs->status = -1;
if($this->fs->warn > $this->fs->used && $this->fs->used < $this->fs->max)
{
$this->fs->status = 0;
}
elseif($this->fs->warn <= $this->fs->used && $this->fs->used < $this->fs->max)
{
$this->fs->status = 1;
}
else
{
$this->fs->status = 2;
}
//Grab number of processes
$this->processes = $this->processes--;
//Grab number of connections
$this->conns->tcp = trim(shell_exec('netstat -t | grep tcp | wc -l'));
$this->conns->udp = trim(shell_exec('netstat -u | grep udp | wc -l'));
}
private function checkSites()
{
//Check if sites are returning HTTP status 200 (OK) or HTTP status 302 (Found)
for($i=0; $i < $this->sites->count; $i++)
{
$this->site[$i]->status = -1;
//Grab HTTP Status. The whitespace in the awk command is there as a workaround to a small bug when parsing data w/ awk.
$this->site[$i]->http->response = trim(shell_exec('curl '.$this->site[$i]->url." -I -s -m 30 -A 'MOSMC-PHP' | grep HTTP | ".'awk \'{print " "$2" "$3" "$4" "$5" "$6}\''));
$this->site[$i]->http->status = substr($this->site[$i]->http->response, 0
, 3
);
$this->site[$i]->http->desc = trim(substr($this->site[$i]->http->response, 3
));
//If headers return HTTP status 200 (OK) or HTTP status 302 (Found), win
if($this->site[$i]->http->status == 200 || $this->site[$i]->http->status == 302)
{
$this->site[$i]->status = 0;
}
//If DNS cannot be resolved, cURL isn't installed, or server hangs connections, fail
elseif($this->site[$i]->http->response == '')
{
$this->site[$i]->status = 1;
$this->site[$i]->http->desc = 'DNS/cURL error or server hangs conn.';
}
//If headers return HTTP status other than 200 (OK) or HTTP status 302 (Found), fail
else
{
$this->site[$i]->status = 1;
}
}
}
}
class BrowserOutput extends Monitor {
public function __construct()
{
parent::__construct();
if($this->get->http->user == $this->web->username && $this->get->http->pass == $this->web->password)
{
//Do nothing
}
else
{
header('WWW-Authenticate: Basic realm="MOSMC"');
}
/*** Testing Variables ***/
//These testing variables only affect browser output, not email notifications.
//Various service status scenarios
//1-Service is not listening, probably because it's down
/*$this->service[0]->status->listen = 1;
$this->service[0]->status->internal = -1;
$this->service[0]->status->external = -1;*/
//2-Service is listening, but rejecting all connections
/*$this->service[0]->status->listen = 0;
$this->service[0]->status->internal = 1;
$this->service[0]->status->external = -1;*/
//3-Service is listening, and rejecting external connections, but supposed to accept them
/*$this->service[0]->status->listen = 0;
$this->service[0]->status->internal = 0;
$this->service[0]->status->external = 2;*/
//4-Service is listening, and accepting external connections, but supposed to reject them
/*$this->service[0]->status->listen = 0;
$this->service[0]->status->internal = 0;
$this->service[0]->status->external = 3;*/
//Various system stats failure scenarios
//Trigger warning
/*$this->load->status = 1;
$this->ram->status = 1;
$this->fs->status = 1;*/
//Trigger alert
/*$this->load->status = 2;
$this->ram->status = 2;
$this->fs->status = 2;*/
//Site failure scenario
/*$this->site[0]->status = 1;
$this->site[0]->http->status = 500;
$this->site[0]->http->desc = 'Internal Server Error';*/
}
public function update()
{
//Send a request to my update checking script
$return = shell_exec('curl http://update.thephpjedi.com/mosmc/?version='.$this->version.' -s -m 30 -A "MOSMC Updater"');
//If we get a response, display it
if($return != '')
{
echo $return;
}
else
{
echo 'updates=>Error while checking for newer version.|';
}
}
public function render()
{
?>
<html>
<head>
<title><?php echo $this->server->name; ?> Monitoring Console</title>
<style type="text/css">
body {
background-color:#f4ead3;
color:#746f63;
text-align:center;
}
h1 {
color:#838078;
}
a:link, a:active, a:visited {
color:#595751;
text-decoration:none;
}
a:hover {
color:#6e6c65;
}
table{
border: 1.5pt dotted #dacfb6;
font-size:0.8em;
}
.up {
background-color:#99cc66;
}
.down {
background-color:#cc3333;
color:#eee;
}
.warn {
background-color:#fcc15a;
}
#footer {
background-color:#e9ddc2;
border: 1pt dotted #dacfb6;
font-size:0.7em;
margin:auto;
margin-top:20px;
padding:5px;
width:350px;
}
#updates {
background-color:#dacfb6;
color:#746f63;
font-size:0.8em;
margin:auto;
margin-bottom:20px;
padding:5px;
width:350px;
}
#updates input {
background-color:#e9ddc2;
border:1px solid #eee;
color:#746f63;
}
</style>
<script type="text/javascript">
//Very lightweight AJAX library. I love it! -TPJ
//Created by Sean Kane (http://celtickane.com/programming/code/ajax.php)
//Feather Ajax v1.0.1
function AjaxObject101() {
this.createRequestObject = function() {
try {
var ro = new XMLHttpRequest();
}
catch (e) {
var ro = new ActiveXObject("Microsoft.XMLHTTP");
}
return ro;
}
this.sndReq = function(action, url, data) {
if (action.toUpperCase() == "POST") {
this.http.open(action,url,true);
this.http.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
this.http.onreadystatechange = this.handleResponse;
this.http.send(data);
}
else {
this.http.open(action,url + '?' + data,true);
this.http.onreadystatechange = this.handleResponse;
this.http.send(null);
}
}
this.handleResponse = function() {
if ( me.http.readyState == 4) {
if (typeof me.funcDone == 'function') { me.funcDone();}
var rawdata = me.http.responseText.split("|");
for ( var i = 0; i < rawdata.length; i++ ) {
var item = (rawdata[i]).split("=>");
if (item[0] != "") {
if (item[1].substr(0,3) == "%V%" ) {
document.getElementById(item[0]).value = item[1].substring(3);
}
else {
document.getElementById(item[0]).innerHTML = item[1];
}
}
}
}
if ((me.http.readyState == 1) && (typeof me.funcWait == 'function')) { me.funcWait(); }
}
var me = this;
this.http = this.createRequestObject();
var funcWait = null;
var funcDone = null;
}
</script>
</head>
<body>
<h1><?php echo $this->server->name; ?> Monitoring Console</h1>
<h2><?php echo $this->server->ip; ?></h2>
<div id="updates">
<input type="button" onclick="checkUpdates();" value="Check for newer version" />
</div>
<?
$this->services();
$this->stats();
$this->sites();
?>
<div id="footer">
MOSMC v<?php echo $this->version; ?> | Alesandro Ortiz<br />
The source of this script is available at <a href="http://thephpjedi.com">ThePHPJedi.com</a>
<div style="width:80%;margin:3px auto 3px auto;border-top:1px solid #746f63;"></div>
Source of MOSMC is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc/3.0/us/">Creative Commons Attribution-Noncommercial 3.0 United States License</a>.
</div>
<script type="text/javascript">
function waiting()
{
document.getElementById('updates').innerHTML = 'Checking for newer version...';
}
function checkUpdates()
{
var ao = new AjaxObject101();
ao.funcWait = waiting;
ao.sndReq('get','','update=1');
}
<?php
if($this->web->update)
{
?>
checkUpdates();
<?
}
?>
</script>
</body>
</html>
<?
}
private function services()
{
?>
<table width="600px" border="0" cellspacing="1" cellpadding="5" align="center">
<tr style="background-color:#e9ddc2">
<td style="min-width:75px;background-color:#dacfb6">Service</td>
<td>Port</td>
<td>Local Connection</td>
<td>External Connection</td>
</tr>
<?php
for($i=0; $i < $this->services->count; $i++)
{
?>
<tr style="background-color:#dacfb6">
<td style="background-color:#e9ddc2"><?php echo $this->service[$i]->name; ?></td>
<?php
switch ($this->service[$i]->status->listen)
{
case 0:
echo '<td class="up">';
echo 'Listening ('.$this->service[$i]->port.')';
break;
case 1:
echo '<td class="down">';
echo '<b>Not listening</b> ('.$this->service[$i]->port.')';
break;
default:
echo '<td class="warn">';
echo 'N/A';
break;
}
?></td>
<?php
switch ($this->service[$i]->status->internal)
{
case 0:
echo '<td class="up">';
echo 'Accepting';
break;
case 1:
echo '<td class="down">';
echo '<b>Rejecting</b>';
break;
default:
echo '<td class="warn">';
echo 'N/A';
break;
}
?></td>
<?php
switch ($this->service[$i]->status->external)
{
case 0:
echo '<td class="up">';
echo 'Accepting as configured';
break;
case 1:
echo '<td class="up">';
echo 'Rejecting as configured';
break;
case 2:
echo '<td class="down">';
echo '<b>Rejecting; Misconfigured</b>';
break;
case 3:
echo '<td class="down">';
echo '<b>Accepting; Misconfigured</b>';
break;
default:
echo '<td class="warn">';
echo 'N/A';
break;
}
?></td>
</tr>
<?php
}
?>
</table>
<br />
<?
}
private function stats()
{
?>
<table width="600px" border="0" cellspacing="1" cellpadding="5" bordercolor="#333333" align="center">
<tr style="background-color:#e9ddc2">
<td style="width:125px;background-color:#dacfb6">Stat Type</td>
<td style="width:150px;">Current Value</td>
<td>Warn Threshold</td>
<td>Alert Threshold</td>
</tr>
<tr style="background-color:#dacfb6">
<td style="background-color:#e9ddc2">Load Averages</td>
<?php
switch ($this->load->status)
{
case 0:
echo '<td class="up">';
echo $this->loads[1].' '.$this->loads[2].' '.$this->loads[3];
break;
case 1:
echo '<td class="warn">';
echo '<b>'.$this->loads[1].' '.$this->loads[2].' '.$this->loads[3].'</b>';
break;
case 2:
echo '<td class="down">';
echo '<b>'.$this->loads[1].' '.$this->loads[2].' '.$this->loads[3].'</b>';
break;
default:
echo '<td class="warn">';
echo 'Error';
break;
}
?></td>
<td><?php echo $this->load->warn; ?> (1 min avg)</td>
<td><?php echo $this->load->max; ?> (1 min avg)</td>
</tr>
<tr style="background-color:#dacfb6">
<td style="background-color:#e9ddc2">Used RAM</td>
<?php
switch ($this->ram->status)
{
case 0:
echo '<td class="up">';
echo $this->ram->used;
break;
case 1:
echo '<td class="warn">';
echo '<b>'.$this->ram->used.'</b>';
break;
case 2:
echo '<td class="down">';
echo '<b>'.$this->ram->used.'</b>';
break;
default:
echo '<td class="warn">';
echo 'Error';
break;
}
?> MB</td>
<td><?php echo $this->ram->warn; ?> MB</td>
<td><?php echo ($this->ram->max >= 1024) ? ($this->ram->max/1024).' GB' : $this->ram->max.' MB'; ?></td>
</tr>
<tr style="background-color:#dacfb6">
<td style="background-color:#e9ddc2">Used Storage</td>
<?php
switch ($this->fs->status)
{
case 0:
echo '<td class="up">';
echo ($this->fs->used >= 1024
) ?
round($this->fs->used/1024, 2).' GB' : $this->fs->used.' MB';
break;
case 1:
echo '<td class="warn"><b>';
echo ($this->fs->used >= 1024
) ?
round($this->fs->used/1024, 2).' GB' : $this->fs->used.' MB';
echo '</b>';
break;
case 2:
echo '<td class="down"><b>';
echo ($this->fs->used >= 1024
) ?
round($this->fs->used/1024, 2).' GB' : $this->fs->used.' MB';
echo '</b>';
break;
default:
echo '<td class="warn">';
echo 'Error';
break;
}
?></td>
<td>
<?php echo ($this->fs->warn >= 1024
) ?
round($this->fs->warn/1024, 2).' GB' : $this->fs->warn.' MB'; ?></td>
<td>
<?php echo ($this->fs->max >= 1024
) ?
round($this->fs->max/1024, 2).' GB' : $this->fs->max.' MB'; ?></td>
</tr>
</table>
<br />
<table width="600px" border="0" cellspacing="1" cellpadding="5" bordercolor="#333333" align="center">
<tr style="background-color:#e9ddc2">
<td style="width:150px;background-color:#dacfb6">Stat Type</td>
<td style="width:150px;">Current Value</td>
<td style="width:150px;background-color:#dacfb6">Stat Type</td>
<td style="width:150px;">Current Value</td>
</tr>
<tr style="background-color:#dacfb6">
<td style="background-color:#e9ddc2">Processes</td>
<td><?php echo $this->processes; ?></td>
<td style="background-color:#e9ddc2">Users Logged In</td>
<td><?php echo $this->users; ?></td>
</tr>
<tr style="background-color:#dacfb6">
<td style="background-color:#e9ddc2">Uptime</td>
<td><?php echo $this->uptime; ?></td>
<td style="background-color:#e9ddc2">TCP/UDP Conns</td>
<td><?php echo $this->conns->tcp.'/'.$this->conns->udp; ?></td>
</tr>
</table>
<br />
<?
}
private function sites()
{
?>
<table width="600px" border="0" cellspacing="1" cellpadding="5" bordercolor="#333333" align="center">
<tr style="background-color:#e9ddc2">
<td style="width:125px;background-color:#dacfb6">Site</td>
<td style="width:175px;">HTTP Status</td>
<td style="width:300px;">URL</td>
</tr>
<?php
for($i=0; $i < $this->sites->count; $i++)
{
?>
<tr style="background-color:#dacfb6">
<td style="background-color:#e9ddc2"><?php echo $this->site[$i]->name; ?></td>
<?php
switch ($this->site[$i]->status)
{
case 0:
echo '<td class="up">';
echo $this->site[$i]->http->status.' ('.$this->site[$i]->http->desc.')';
break;
case 1:
echo '<td class="down">';
echo $this->site[$i]->http->status.' ('.$this->site[$i]->http->desc.')';
break;
default:
echo '<td class="warn">';
echo 'N/A';
break;
}
?></td>
<td><?php
//If site is not returning HTTP 200 (OK) or HTTP status 302 (Found), link. Otherwise, just show URL
echo ($this->site[$i]->status) ? '<a href="'.$this->site[$i]->url.'">'.$this->site[$i]->url.'</a>' : $this->site[$i]->url;
?></td>
</tr>
<?php
}
?>
</table>
<?
}
}
class EmailOutput extends Monitor {
public function __construct()
{
parent::__construct();
/*** Testing Variables ***/
//These testing variables only affect email notifications, not browser output.
//Various service status scenarios
//1-Service is not listening, probably because it's down
/*$this->service[0]->status->listen = 1;
$this->service[0]->status->internal = -1;
$this->service[0]->status->external = -1;*/
//2-Service is listening, but rejecting all connections
/*$this->service[0]->status->listen = 0;
$this->service[0]->status->internal = 1;
$this->service[0]->status->external = -1;*/
//3-Service is listening, and rejecting external connections, but supposed to accept them
/*$this->service[1]->status->listen = 0;
$this->service[1]->status->internal = 0;
$this->service[1]->status->external = 2;*/
//4-Service is listening, and accepting external connections, but supposed to reject them
/*$this->service[0]->status->listen = 0;
$this->service[0]->status->internal = 0;
$this->service[0]->status->external = 3;*/
//Various system stats failure scenarios
//Trigger warning
/*$this->load->status = 1;
$this->ram->status = 1;
$this->fs->status = 1;*/
//Trigger alert
/*$this->load->status = 2;
$this->ram->status = 2;
$this->fs->status = 2;*/
//Site failure scenario
/*$this->site[0]->status = 1;
$this->site[0]->http->status = 500;
$this->site[0]->http->desc = 'Internal Server Error';*/
/*** Testing Variables ***/
}
public function update()
{
//Send a request to my update checking script
$return = shell_exec('curl "http://update.thephpjedi.com/mosmc/?version='.$this->version.'&method=cron" -s -m 30 -A "MOSMC Updater"');
//If we get a response, send it
if($return != '')
{
mail($this->email->recipient->update, $this->email->subject->update, $return, $this->email->header->update, '-f'.$this->email->sender->update);
}
}
public function send()
{
if($this->web->password == $this->get->pass)
{
if($this->get->type == 'report')
{
$this->generateReport();
mail($this->email->recipient->report, $this->email->subject->report, $this->email->output, $this->email->header->report, '-f'.$this->email->sender->report);
echo 'Cron job run successfully.';
}
elseif($this->get->type == 'failure')
{
$this->generateAlert();
//Send SMS containing alerts only if threshold includes alerts.
if(isset($this->email->alert->trigger) && $this->email->alert->trigger)
{
mail($this->email->recipient->alert, $this->email->subject->alert, $this->email->output, $this->email->header->alert, '-f'.$this->email->sender->alert);
}
//Send SMS containing warnings only if threshold includes warnings.
elseif((isset($this->email->warning->trigger) && $this->email->warning->trigger) && $this->email->threshold == 0
)
{
mail($this->email->recipient->alert, $this->email->subject->alert, $this->email->output, $this->email->header->alert, '-f'.$this->email->sender->alert);
}
echo 'Cron job run successfully.';
}
}
}
private function generateReport()
{
echo '=Services='."\n";
for($i=0; $i < $this->services->count; $i++)
{
echo $this->service[$i]->name;
echo ' | Port: ';
switch ($this->service[$i]->status->listen)
{
case 0:
echo 'Up, ';
echo 'Listening ('.$this->service[$i]->port.')';
break;
case 1:
echo 'Down, ';
echo 'Not listening ('.$this->service[$i]->port.')';
break;
default:
echo 'Warning, ';
echo 'N/A';
break;
}
echo "\n".'Local Connection: ';
switch ($this->service[$i]->status->internal)
{
case 0:
echo 'Up, ';
echo 'Accepting';
break;
case 1:
echo 'Down, ';
echo 'Rejecting';
break;
default:
echo 'Warning, ';
echo 'N/A';
break;
}
echo "\n".'External Connection: ';
switch ($this->service[$i]->status->external)
{
case 0:
echo 'Up, ';
echo 'Accepting as configured';
break;
case 1:
echo 'Up, ';
echo 'Rejecting as configured';
break;
case 2:
echo 'Down, ';
echo 'Rejecting; Misconfigured';
break;
case 3:
echo 'Down, ';
echo 'Accepting; Misconfigured';
break;
default:
echo 'Warning, ';
echo 'N/A';
break;
}
echo "\n\n";
}
echo '=System Stats=';
echo "\n".'Loads: ';
switch ($this->load->status)
{
case 0:
echo 'Up, ';
echo $this->loads[1].' '.$this->loads[2].' '.$this->loads[3];
break;
case 1:
echo 'Warning, ';
echo $this->loads[1].' '.$this->loads[2].' '.$this->loads[3];
break;
case 2:
echo 'Down, ';
echo $this->loads[1].' '.$this->loads[2].' '.$this->loads[3];
break;
default:
echo 'Warning, ';
echo 'Error';
break;
}
echo "\n".'RAM: ';
switch ($this->ram->status)
{
case 0:
echo 'Up, ';
echo $this->ram->used.' MB';
break;
case 1:
echo 'Warning, ';
echo $this->ram->used.' MB';
break;
case 2:
echo 'Down, ';
echo $this->ram->used.' MB';
break;
default:
echo 'Warning, ';
echo 'Error';
break;
}
echo "\n".'Storage: ';
switch ($this->fs->status)
{
case 0:
echo 'Up, ';
echo ($this->fs->used >= 1024
) ?
round($this->fs->used/1024, 2).' GB' : $this->fs->used.' MB';
break;
case 1:
echo 'Warning, ';
echo ($this->fs->used >= 1024
) ?
round($this->fs->used/1024, 2).' GB' : $this->fs->used.' MB';
break;
case 2:
echo 'Down, ';
echo ($this->fs->used >= 1024
) ?
round($this->fs->used/1024, 2).' GB' : $this->fs->used.' MB';
break;
default:
echo 'Warning, ';
echo 'Error';
break;
}
echo "\n".'Processes: '.$this->processes;
echo "\n".'Uptime: '.$this->uptime;
echo "\n".'Users Logged In: '.$this->users;
echo "\n".'TCP Connections: '.$this->conns->tcp;
echo "\n".'UDP Connections: '.$this->conns->udp;
echo "\n\n".'=Sites=';
for($i=0; $i < $this->sites->count; $i++)
{
echo "\n".$this->site[$i]->name.': ';
switch ($this->site[$i]->status)
{
case 0:
echo 'Up, ';
echo $this->site[$i]->http->status.' ('.$this->site[$i]->http->desc.')';
break;
case 1:
echo 'Down, ';
echo $this->site[$i]->http->status.' ('.$this->site[$i]->http->desc.')';
break;
default:
echo 'Warning, ';
echo 'Error';
break;
}
}
}
private function generateAlert()
{
for($i=0; $i < $this->services->count; $i++)
{
echo ($this->service[$i]->status->listen >= 1 || $this->service[$i]->status->internal >= 1 || $this->service[$i]->status->external >= 2 || $this->service[$i]->status->listen == -1 || $this->service[$i]->status->internal == -1 || $this->service[$i]->status->external == -1) ? '>'.$this->service[$i]->name : null;
echo ($this->service[$i]->status->listen >= 1 || $this->service[$i]->status->listen == -1) ? '|Port:' : null;
switch ($this->service[$i]->status->listen)
{
case 0:
break;
case 1:
echo 'Down:';
echo 'NotListening ('.$this->service[$i]->port.');';
$this->email->alert->trigger = 1;
break;
default:
echo 'Warning:';
echo 'N/A;';
$this->email->warning->trigger = 1;
break;
}
echo ($this->service[$i]->status->internal >= 1 || $this->service[$i]->status->internal == -1) ? '|LocalConn:' : null;
switch ($this->service[$i]->status->internal)
{
case 0:
break;
case 1:
echo 'Down:';
echo 'Rejecting;';
$this->email->alert->trigger = 1;
break;
default:
echo 'Warning:';
echo 'N/A;';
$this->email->warning->trigger = 1;
break;
}
echo ($this->service[$i]->status->external >= 2 || $this->service[$i]->status->external == -1) ? '|ExtConn:' : null;
switch ($this->service[$i]->status->external)
{
case 0:
break;
case 1:
break;
case 2:
echo 'Down:';
echo 'Rejecting,Misconfigured;';
$this->email->alert->trigger = 1;
break;
case 3:
echo 'Down:';
echo 'Accepting,Misconfigured;';
$this->email->alert->trigger = 1;
break;
default:
echo 'Warning:';
echo 'N/A;';
$this->email->warning->trigger = 1;
break;
}
}
echo ($this->load->status >= 1 | $this->load->status == -1) ? '|Loads:' : null;
switch ($this->load->status)
{
case 0:
break;
case 1:
echo 'Warning:';
echo $this->loads[1].' '.$this->loads[2].' '.$this->loads[3].';';
$this->email->warning->trigger = 1;
break;
case 2:
echo 'Down:';
echo $this->loads[1].' '.$this->loads[2].' '.$this->loads[3].';';
$this->email->alert->trigger = 1;
break;
default:
echo 'Warning:';
echo 'Error;';
$this->email->warning->trigger = 1;
break;
}
echo ($this->ram->status >= 1 || $this->ram->status == -1) ? '|RAM:' : null;
switch ($this->ram->status)
{
case 0:
break;
case 1:
echo 'Warning:';
echo $this->ram->used;
$this->email->warning->trigger = 1;
break;
case 2:
echo 'Down:';
echo $this->ram->used;
$this->email->alert->trigger = 1;
break;
default:
echo 'Warning:';
echo 'Error;';
$this->email->warning->trigger = 1;
break;
}
echo ($this->ram->status >= 1 || $this->ram->status == -1) ? 'MB;' : null;
echo ($this->fs->status >= 1 || $this->fs->status == -1) ? '|FS:' : null;
switch ($this->fs->status)
{
case 0:
break;
case 1:
echo 'Warning:';
echo ($this->fs->used >= 1024
) ?
round($this->fs->used/1024, 2).'GB;' : $this->fs->used.'MB;';
$this->email->warning->trigger = 1;
break;
case 2:
echo 'Down:';
echo ($this->fs->used >= 1024
) ?
round($this->fs->used/1024, 2).'GB;' : $this->fs->used.'MB;';
$this->email->alert->trigger = 1;
break;
default:
echo 'Warning:';
echo 'Error;';
$this->email->warning->trigger = 1;
break;
}
for($i=0; $i < $this->sites->count; $i++)
{
echo ($this->site[$i]->status >= 1 || $this->site[$i]->status == -1) ? '|'.$this->site[$i]->name.':' : null;
switch ($this->site[$i]->status)
{
case 0:
break;
case 1:
echo 'Down:';
echo $this->site[$i]->http->status.'('.$this->site[$i]->http->desc.');';
$this->email->alert->trigger = 1;
break;
default:
echo 'Warning:';
echo 'Error;';
$this->email->warning->trigger = 1;
break;
}
}
}
}
//If running cron job (either through CLI or Web), and type isn't update, send email if needed
if((isset($argv) && $argv[1] != 'update') || (isset($_GET['cron']) && $_GET['cron']))
{
$email = new EmailOutput();
$email->send();
}
//If running cron job (through CLI), and type is update, check for updates, and send email if needed
elseif(isset($argv) && $argv[1] == 'update')
{
$email = new EmailOutput();
$email->update();
}
//If checking for updates through AJAX
elseif(isset($_GET['update']) && $_GET['update'])
{
$browser = new BrowserOutput();
$browser->update();
}
//If accessing MOSMC directly from browser
else
{
$browser = new BrowserOutput();
$browser->render();
}
?>