From 34eea7a235e00335d6f325b496ba5231b810b582 Mon Sep 17 00:00:00 2001 From: "Hahn Axel (hahn)" <axel.hahn@unibe.ch> Date: Thu, 25 Jul 2024 17:14:47 +0200 Subject: [PATCH] update appmonitor client v1.137 --- .../classes/appmonitor-checks.class.php | 299 +++++++------- .../classes/appmonitor-client.class.php | 376 +++++++++--------- .../appmonitor/classes/client_all_in_one.php | 33 +- .../appmonitor/git_update_appmonitor.sh | 29 +- public_html/appmonitor/local.php | 6 + .../plugins/apps/iml-appmonitor-server.php | 135 +++---- .../plugins/checks/apacheprocesses.php | 161 +++++--- .../appmonitor/plugins/checks/cert.php | 86 ++-- .../appmonitor/plugins/checks/diskfree.php | 60 +-- .../appmonitor/plugins/checks/exec.php | 92 +++-- .../appmonitor/plugins/checks/file.php | 46 ++- .../appmonitor/plugins/checks/hello.php | 17 +- .../appmonitor/plugins/checks/httpcontent.php | 188 ++++----- .../appmonitor/plugins/checks/loadmeter.php | 76 ++-- .../plugins/checks/mysqlconnect.php | 38 +- .../appmonitor/plugins/checks/pdoconnect.php | 38 +- .../appmonitor/plugins/checks/phpmodules.php | 67 ++-- .../appmonitor/plugins/checks/ping.php | 35 +- .../appmonitor/plugins/checks/porttcp.php | 31 +- .../appmonitor/plugins/checks/simple.php | 21 +- .../plugins/checks/sqliteconnect.php | 51 ++- 21 files changed, 1000 insertions(+), 885 deletions(-) create mode 100644 public_html/appmonitor/local.php diff --git a/public_html/appmonitor/classes/appmonitor-checks.class.php b/public_html/appmonitor/classes/appmonitor-checks.class.php index dfff0ea6..865a0e8e 100755 --- a/public_html/appmonitor/classes/appmonitor-checks.class.php +++ b/public_html/appmonitor/classes/appmonitor-checks.class.php @@ -1,6 +1,6 @@ <?php -if(!defined('RESULT_OK')){ +if (!defined('RESULT_OK')) { define("RESULT_OK", 0); define("RESULT_UNKNOWN", 1); define("RESULT_WARNING", 2); @@ -51,76 +51,79 @@ if(!defined('RESULT_OK')){ * 2021-10-28 0.93 axel.hahn@iml.unibe.ch add plugins<br> * 2021-12-14 0.93 axel.hahn@iml.unibe.ch split plugins into single files; added key group in a check<br> * 2023-06-02 0.125 axel.hahn@unibe.ch replace array_key_exists for better readability + * 2024-07-22 0.137 axel.hahn@unibe.ch php 8 only: use typed variables * --------------------------------------------------------------------------------<br> - * @version 0.125 + * @version 0.137 * @author Axel Hahn * @link TODO * @license GPL * @license http://www.gnu.org/licenses/gpl-3.0.html GPL 3.0 * @package IML-Appmonitor */ -class appmonitorcheck { +class appmonitorcheck +{ // ---------------------------------------------------------------------- // CONFIG // ---------------------------------------------------------------------- - + /** * starting time using microtime - * @var float|string + * @var float */ - protected $_iStart=0; + protected float $_iStart = 0; /** * config container * @var array */ - protected $_aConfig = []; + protected array $_aConfig = []; /** * data of all checks * @var array */ - protected $_aData = []; - + protected array $_aData = []; /** * flat array with units for sizes * @var array */ - protected $_units = [ 'B', 'KB', 'MB', 'GB', 'TB' ]; - + protected array $_units = ['B', 'KB', 'MB', 'GB', 'TB']; + /** * timeout in sec for tcp socket connections - * @var type + * @var integer */ - protected $_iTimeoutTcp=5; - + protected int $_iTimeoutTcp = 5; + /** * point to the plugin directory * @var string */ - protected $_sPluginDir=__DIR__.'/../plugins'; + protected string $_sPluginDir = __DIR__ . '/../plugins'; // ---------------------------------------------------------------------- // CONSTRUCTOR // ---------------------------------------------------------------------- /** - * constructor (nothing) + * Constructor (nothing here) */ - public function __construct() { - + public function __construct() + { + } - + // ---------------------------------------------------------------------- // PRIVATE FUNCTIONS // ---------------------------------------------------------------------- /** - * internal: create basic array values for metadata + * Internal: create basic array values for metadata * @return boolean */ - protected function _createDefaultMetadata() { + protected function _createDefaultMetadata(): bool + { $this->_aData = [ "name" => $this->_aConfig["name"], @@ -136,33 +139,38 @@ class appmonitorcheck { } /** - * set a result value of a check - * @param type $iResult - * @return type + * Set the result value of a check + * @param integer $iResult result code; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN + * @return bool */ - protected function _setResult($iResult) { - return $this->_aData["result"] = (int) $iResult; + protected function _setResult(int $iResult): bool + { + $this->_aData["result"] = (int) $iResult; + return true; } /** - * set a result value of a check - * @param type $iResult - * @return type + * Set a result value of a check + * @param string $s value; message text for this result + * @return bool */ - protected function _setOutput($s) { - return $this->_aData["value"] = (string) $s; + protected function _setOutput(string $s): bool + { + $this->_aData["value"] = $s; + return true; } - + /** - * put counter data to result set - * @param type $aParams + * Put counter data to result set + * @param array $aParams array with possible keys type, count, visual * @return boolean */ - protected function _setCounter($aParams){ - if(is_array($aParams) && count($aParams)){ - foreach([ 'type', 'count', 'visual' ] as $sMyKey){ - if(isset($aParams[$sMyKey])){ - $this->_aData[$sMyKey]=$aParams[$sMyKey]; + protected function _setCounter(array $aParams): bool + { + if (is_array($aParams) && count($aParams)) { + foreach (['type', 'count', 'visual'] as $sMyKey) { + if (isset($aParams[$sMyKey])) { + $this->_aData[$sMyKey] = $aParams[$sMyKey]; } } } @@ -170,41 +178,43 @@ class appmonitorcheck { } /** - * set result and output - * @param integer $iResult value; use a RESULT_XYZ constant + * Set result and output + * @param integer $iResult result code; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN * @param string $s message text * @param array $aCounter optional: counter with array keys type, count, visual * @return boolean */ - protected function _setReturn($iResult, $s, $aCounter=[]) { + protected function _setReturn(int $iResult, string $s, array $aCounter = []) + { $this->_setResult($iResult); $this->_setOutput($s); $this->_setCounter($aCounter); return true; } - + /** - * check a given array if it contains wanted keys + * Check a given array if it contains wanted keys * @param array $aConfig array to verify * @param string $sKeyList key or keys as comma seprated list * @return boolean */ - protected function _checkArrayKeys($aConfig, $sKeyList) { + protected function _checkArrayKeys($aConfig, $sKeyList) + { foreach (explode(",", $sKeyList) as $sKey) { if (!isset($aConfig[$sKey])) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' - . '<h2>Details</h2>' - .__METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array." - . "<pre>" . print_r($aConfig, true) .'</pre>' + . '<h2>Details</h2>' + . __METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array." + . "<pre>" . print_r($aConfig, true) . '</pre>' ); } if (is_null($aConfig[$sKey])) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' - . '<h2>Details</h2>' - .__METHOD__ . " - key <code>$sKey</code> is empty in config array" - . "<pre>" . print_r($aConfig, true) .'</pre>' + . '<h2>Details</h2>' + . __METHOD__ . " - key <code>$sKey</code> is empty in config array" + . "<pre>" . print_r($aConfig, true) . '</pre>' ); } } @@ -216,9 +226,9 @@ class appmonitorcheck { // ---------------------------------------------------------------------- /** - * perform a check - * @param type $aConfig - * Array + * Perform a check + * @param array $aConfig configuration array for a check, eg. + * <code> * [ * [name] => Dummy * [description] => Dummy Test @@ -228,10 +238,11 @@ class appmonitorcheck { * // its keys depend on the function * ] * ] - * + * </code> * @return array */ - public function makeCheck($aConfig) { + public function makeCheck(array $aConfig): array + { $this->_iStart = microtime(true); $this->_checkArrayKeys($aConfig, "name,description,check"); $this->_checkArrayKeys($aConfig["check"], "function"); @@ -241,53 +252,53 @@ class appmonitorcheck { $sCheck = preg_replace('/[^a-zA-Z0-9]/', '', $this->_aConfig["check"]["function"]); $aParams = $this->_aConfig["check"]["params"] ?? []; - + // try to load as plugin from a plugin file - $sPluginFile= strtolower($this->_sPluginDir.'/checks/'.$sCheck.'.php'); + $sPluginFile = strtolower($this->_sPluginDir . '/checks/' . $sCheck . '.php'); // echo "plugin file: $sPluginFile<br>\n"; - $sCheckClass = 'check'.$sCheck; - if (!class_exists($sCheckClass)){ - if (file_exists($sPluginFile)) { - require_once($sPluginFile); + $sCheckClass = 'check' . $sCheck; + if (!class_exists($sCheckClass)) { + if (file_exists($sPluginFile)) { + require_once ($sPluginFile); } } - if (!class_exists($sCheckClass)){ + if (!class_exists($sCheckClass)) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' - . '<h2>Details</h2>' - .__METHOD__ . " - check class not found: <code>$sCheckClass</code>" - . "<pre>" . print_r($aConfig, true) .'</pre>' - ."<h2>Known checks</h2>\n".print_r($this->listChecks(), 1) + . '<h2>Details</h2>' + . __METHOD__ . " - check class not found: <code>$sCheckClass</code>" + . "<pre>" . print_r($aConfig, true) . '</pre>' + . "<h2>Known checks</h2>\n" . print_r($this->listChecks(), 1) ); } - + $oPlugin = new $sCheckClass; - $aResponse=$oPlugin->run($aParams); - if(!is_array($aResponse)){ + $aResponse = $oPlugin->run($aParams); + if (!is_array($aResponse)) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' - . '<h2>Details</h2>' - .__METHOD__ . " - plugin : $sCheck does not responses an array" - . "<pre>INPUT " . print_r($aConfig, true) .'</pre>' - . "<pre>RESPONSE " . print_r($aResponse, true) .'</pre>' + . '<h2>Details</h2>' + . __METHOD__ . " - plugin : $sCheck does not responses an array" + . "<pre>INPUT " . print_r($aConfig, true) . '</pre>' + . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>' ); } - if(count($aResponse)<2){ + if (count($aResponse) < 2) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' - . '<h2>Details</h2>' - .__METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values" - . "<pre>INPUT " . print_r($aConfig, true) .'</pre>' - . "<pre>RESPONSE " . print_r($aResponse, true) .'</pre>' + . '<h2>Details</h2>' + . __METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values" + . "<pre>INPUT " . print_r($aConfig, true) . '</pre>' + . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>' ); } - if(!isset($aResponse[2])){ - $aResponse[2]=[]; + if (!isset($aResponse[2]) || !$aResponse[2]) { + $aResponse[2] = []; } $this->_setReturn($aResponse[0], $aResponse[1], $aResponse[2]); - if (!$this->_aData['group'] && method_exists($oPlugin, "getGroup")){ - $this->_aData['group']=$oPlugin->getGroup($aParams); + if (!$this->_aData['group'] && method_exists($oPlugin, "getGroup")) { + $this->_aData['group'] = $oPlugin->getGroup($aParams); } $this->_aData['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; @@ -296,22 +307,23 @@ class appmonitorcheck { } /** - * list all available check functions. This is a helper class you can call + * List all available checks. This is a helper class you can call * to get an overview over built in functions and plugins. * You get a flat array with all function names. * @return array */ - public function listChecks() { + public function listChecks(): array + { $aReturn = []; // return internal protected fuctions named "check[whatever]" $class = new ReflectionClass($this); foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) { if (strpos($oReflectionMethod->name, "check") === 0) { - $aReturn[(string) $oReflectionMethod->name]=1; + $aReturn[(string) $oReflectionMethod->name] = 1; } } // return checks from plugins subdir - foreach(glob($this->_sPluginDir.'/checks/*.php') as $sPluginFile){ + foreach (glob($this->_sPluginDir . '/checks/*.php') as $sPluginFile) { $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1; } ksort($aReturn); @@ -319,10 +331,11 @@ class appmonitorcheck { } /** - * final call of class: send response (data array) - * @return type + * Final call of class: send response (data array) + * @return array */ - public function respond() { + public function respond() + { return $this->_aData; } @@ -331,65 +344,70 @@ class appmonitorcheck { // ---------------------------------------------------------------------- /** - * helper function: read certificate data + * Helper function: read certificate data * called in checkCert() + * * @param string $sUrl url to connect * @param boolean $bVerifyCert flag: verify certificate; default: no check * @return array */ - protected function _certGetInfos($sUrl, $bVerifyCert) { - $iTimeout=10; - $aUrldata=parse_url($sUrl); + protected function _certGetInfos(string $sUrl, bool $bVerifyCert): array + { + $iTimeout = 10; + $aUrldata = parse_url($sUrl); $sHost = isset($aUrldata['host']) ? $aUrldata['host'] : false; $iPort = isset($aUrldata['port']) ? $aUrldata['port'] : ((isset($aUrldata['scheme']) && $aUrldata['scheme'] === 'https') ? 443 : false); - $aSsl=['capture_peer_cert' => true ]; - if($bVerifyCert){ - $aSsl['verify_peer']=false; - $aSsl['verify_peer_name']=false; - }; - $get = stream_context_create([ 'ssl' => $aSsl ]); - if(!$get){ - return [ '_error' => 'Error: Cannot create stream_context' ]; + $aSsl = ['capture_peer_cert' => true]; + if ($bVerifyCert) { + $aSsl['verify_peer'] = false; + $aSsl['verify_peer_name'] = false; } - $errno=-1; - $errstr="stream_socket_client failed."; + ; + $get = stream_context_create(['ssl' => $aSsl]); + if (!$get) { + return ['_error' => 'Error: Cannot create stream_context']; + } + $errno = -1; + $errstr = "stream_socket_client failed."; $read = stream_socket_client("ssl://$sHost:$iPort", $errno, $errstr, $iTimeout, STREAM_CLIENT_CONNECT, $get); - if(!$read){ - return [ '_error' => "Error $errno: $errstr; cannot create stream_socket_client with given stream_context to ssl://$sHost:$iPort; you can try to set the flag [verify] to false to check expiration date only." ]; + if (!$read) { + return ['_error' => "Error $errno: $errstr; cannot create stream_socket_client with given stream_context to ssl://$sHost:$iPort; you can try to set the flag [verify] to false to check expiration date only."]; } $cert = stream_context_get_params($read); - if(!$cert){ - return [ '_error' => "Error: socket was connected to ssl://$sHost:$iPort - but I cannot read certificate infos with stream_context_get_params " ]; + if (!$cert) { + return ['_error' => "Error: socket was connected to ssl://$sHost:$iPort - but I cannot read certificate infos with stream_context_get_params "]; } - $certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); - return $certinfo; + return openssl_x509_parse($cert['options']['ssl']['peer_certificate']); } /** - * get human readable space value + * Get human readable space value * @param integer $size * @return string */ - protected function _getHrSize($size){ + protected function _getHrSize(int $size): string + { $power = $size > 0 ? floor(log($size, 1024)) : 0; return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $this->_units[$power]; } + /** * get a space in a real value if an integer has added MB|GB|... * @param string $sValue * @return integer */ - protected function _getSize($sValue){ - if(is_int($sValue)){ + protected function _getSize(string $sValue): int + { + if (is_int($sValue)) { return $sValue; } - $power=0; - foreach($this->_units as $sUnit){ - if (preg_match('/^[0-9\.\ ]*'.$sUnit.'/', $sValue)){ - $i=preg_replace('/([0-9\.]*).*/', '$1', $sValue); - $iReal=$i*pow(1024, $power); + $power = 0; + foreach ($this->_units as $sUnit) { + if (preg_match('/^[0-9\.\ ]*' . $sUnit . '/', $sValue)) { + $i = preg_replace('/([0-9\.]*).*/', '$1', $sValue); + $iReal = $i * pow(1024, $power); // die("FOUND: $sValue with unit ${sUnit} - 1024^$power * $i = $iReal"); return $iReal; } @@ -397,48 +415,9 @@ class appmonitorcheck { } header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' - . '<h2>Details</h2>' - .__METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units) + . '<h2>Details</h2>' + . __METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units) ); } - - - /** - * compare function for 2 values - * @param string $verifyValue search value - * @param string $sCompare compare function; it is one of - * IS - - * GE - greater or equal - * GT - greater - * @param string $value existing value - * @return boolean - */ - protected function _compare($value, $sCompare, $verifyValue){ - switch ($sCompare){ - case "IS": - return $value===$verifyValue; - break; - case "GE": - return $value>=$verifyValue; - break; - case "GT": - return $value>$verifyValue; - break; - case "HAS": - return !!(strstr($value, $verifyValue)!==false); - break; - default: - header('HTTP/1.0 503 Service Unavailable'); - die('<h1>503 Service Unavailable</h1>' - . '<h2>Details</h2>' - .__METHOD__ . " - FATAL ERROR: a compare function [$sCompare] is not implemented (yet)." - ); - break; - } - return false; - } - - - } diff --git a/public_html/appmonitor/classes/appmonitor-client.class.php b/public_html/appmonitor/classes/appmonitor-client.class.php index 08a58424..bdf70db9 100755 --- a/public_html/appmonitor/classes/appmonitor-client.class.php +++ b/public_html/appmonitor/classes/appmonitor-client.class.php @@ -1,5 +1,5 @@ <?php -if (!class_exists('appmonitorcheck')){ +if (!class_exists('appmonitorcheck')) { require_once 'appmonitor-checks.class.php'; } @@ -41,60 +41,62 @@ if (!class_exists('appmonitorcheck')){ * 2019-05-31 0.87 axel.hahn@iml.unibe.ch add timeout as param in connective checks (http, tcp, databases)<br> * 2020-05-03 0.110 axel.hahn@iml.unibe.ch update renderHtmloutput<br> * 2023-07-06 0.128 axel.hahn@unibe.ch update httpcontent check<br> + * 2024-07-19 0.137 axel.hahn@unibe.ch php 8 only: use typed variables * --------------------------------------------------------------------------------<br> - * @version 0.128 + * @version 0.137 * @author Axel Hahn * @link TODO * @license GPL * @license http://www.gnu.org/licenses/gpl-3.0.html GPL 3.0 * @package IML-Appmonitor */ -class appmonitor { +class appmonitor +{ /** - * config: default ttl for server before requesting the client check again - * value is in seconds - * @var int + * Name and Version number + * @var string */ - protected $_sVersion = 'php-client-v0.128'; + protected string $_sVersion = 'php-client-v0.137'; /** * config: default ttl for server before requesting the client check again * value is in seconds * @var int */ - protected $_iDefaultTtl = 300; + protected int $_iDefaultTtl = 300; /** * internal counter: greatest return value of all checks - * @var type + * @var integer */ - protected $_iMaxResult = false; + protected int $_iMaxResult = -1; /** * responded metadata of a website * @see _createDefaultMetadata() * @var array */ - protected $_aMeta = []; + protected array $_aMeta = []; /** - * repended array of all checks + * Response array of all checks * @see addCheck() * @var array */ - protected $_aChecks = []; - + protected array $_aChecks = []; + /** * for time measurements: start time * @var float */ - protected $_iStart = false; - + protected float $_iStart = 0; + /** * constructor: init data */ - public function __construct() { + public function __construct() + { $this->_createDefaultMetadata(); } @@ -103,10 +105,11 @@ class appmonitor { // ---------------------------------------------------------------------- /** - * create basic array values for metadata + * Create basic array values for metadata * @return boolean */ - protected function _createDefaultMetadata() { + protected function _createDefaultMetadata(): bool + { $this->_iStart = microtime(true); $this->_aMeta = [ "host" => false, @@ -129,21 +132,26 @@ class appmonitor { // ---------------------------------------------------------------------- /** - * set the physical hostname for metadata; if no host is given then + * Set the physical hostname for metadata; if no host is given then * the php_uname("n") will be used to set one. * * @param string $s hostname * @return bool */ - public function setHost($s = false) { + public function setHost(string $s = ''): bool + { if (!$s) { $s = php_uname("n"); } - return $this->_aMeta["host"] = $s; + if (!$s) { + return false; + } + $this->_aMeta["host"] = $s; + return true; } /** - * set a name for this website or application and its environment + * Set a name for this website or application and its environment * (dev, test, prod); * * If you have several application in subdirectories, i.e. /blog, /shop... @@ -151,14 +159,19 @@ class appmonitor { * * if no argument is given the name of HTTP_HOST will be used * - * @param string $sNewHost hostname - * @return bool + * @param string $sWebsite Name of the website or web application + * @return boolean */ - public function setWebsite($s = false) { - if (!$s && isset($_SERVER["HTTP_HOST"])) { - $s = $_SERVER["HTTP_HOST"]; + public function setWebsite($sWebsite = ''): bool + { + if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) { + $sWebsite = $_SERVER["HTTP_HOST"]; + } + if (!$sWebsite) { + return false; } - return $this->_aMeta["website"] = $s; + $this->_aMeta["website"] = $sWebsite; + return true; } /** @@ -168,58 +181,67 @@ class appmonitor { * @param int $iTTl TTL value in sec * @return boolean */ - public function setTTL($iTTl = false) { - if (!$iTTl) { + public function setTTL($iTTl = 0) + { + if ($iTTl == 0) { $iTTl = $this->_iDefaultTtl; } return $this->_aMeta["ttl"] = $iTTl; } /** - * set final result in meta data; if no value was given then it + * Set final result in meta data; if no value was given then it * sets the biggest value of any check. - * @param integer $iResult set resultcode + * + * @param integer $iResult set resultcode; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN * @return boolean */ - public function setResult($iResult = false) { - if ($iResult === false) { + public function setResult(int $iResult = -1): bool + { + if ($iResult === -1) { $iResult = $this->_iMaxResult; // see addCheck() } - return $this->_aMeta["result"] = $iResult; + $this->_aMeta["result"] = $iResult; + return true; } /** - * add a check array; - * @param type $aJob - * @return type + * Add a check array + * @param array $aJob array with check data + * @return boolean */ - public function addCheck($aJob = []) { + public function addCheck($aJob = []): bool + { require_once 'appmonitor-checks.class.php'; $oCheck = new appmonitorcheck(); $aCheck = $oCheck->makecheck($aJob); - + // limit result code - $iMyResult=isset($aJob['worstresult']) - ? min($aCheck["result"], $aJob['worstresult']) - : $aCheck["result"] - ; + $iMyResult = isset($aJob['worstresult']) + ? min($aCheck["result"], $aJob['worstresult']) + : $aCheck["result"] + ; if (!$this->_iMaxResult || $iMyResult > $this->_iMaxResult) { $this->_iMaxResult = $iMyResult; } - return $this->_aChecks[] = $aCheck; + $this->_aChecks[] = $aCheck; + return true; } /** - * add an item to notifications meta data + * Add an item to notifications meta data + * @see addEmail() + * @see addSlack() * * @param string $sType type ... one of email|slack - * @param type $sValue value - * @param type $sKey optional key (for key->value instead of list of values) + * @param string $sValue value + * @param string $sKey optional key (for key->value instead of list of values) * @return boolean */ - protected function _addNotification($sType, $sValue, $sKey = false) { + protected function _addNotification(string $sType, string $sValue, string $sKey = ''): bool + { $sTypeCleaned = preg_replace('/[^a-z]/', '', strtolower($sType)); if (!isset($this->_aMeta['notifications'])) { $this->_aMeta['notifications'] = []; @@ -236,12 +258,13 @@ class appmonitor { } /** - * add an email to notifications list + * Add an email to notifications list * * @param string $sEmailAddress email address to add * @return boolean */ - public function addEmail($sEmailAddress) { + public function addEmail(string $sEmailAddress) + { return $this->_addNotification('email', $sEmailAddress); } @@ -249,36 +272,41 @@ class appmonitor { * Add slack channel for notification * @param string $sLabel * @param string $sSlackWebhookUrl - * @return type + * @return boolean */ - public function addSlackWebhook($sLabel, $sSlackWebhookUrl) { + public function addSlackWebhook(string $sLabel, string $sSlackWebhookUrl): bool + { return $this->_addNotification('slack', $sSlackWebhookUrl, $sLabel); } + /** - * add a tag for grouping in the server gui + * Add a tag for grouping in the server gui. + * Spaces will be replaced with underscore * - * @param string $sLabel - * @param string $sSlackWebhookUrl - * @return type + * @param string $sTag tag to add + * @return boolean */ - public function addTag($sTag) { - if(!isset($this->_aMeta['tags'])){ - $this->_aMeta['tags']=[]; + public function addTag(string $sTag): bool + { + if (!isset($this->_aMeta['tags'])) { + $this->_aMeta['tags'] = []; } - $this->_aMeta['tags'][]=$sTag; + $this->_aMeta['tags'][] = str_replace(' ', '_', $sTag); return true; } /** - * check referers IP address if it matches any entry in the list + * Check referers IP address if it matches any entry in the list * requires http request; CLI is always allowed * On deny this method exits with 403 response * * @param array $aAllowedIps array of allowed ip addresses / ranges * the ip must match from the beginning, i.e. * "127.0." will allow requests from 127.0.X.Y + * @return boolean */ - public function checkIp($aAllowedIps = []) { + public function checkIp(array $aAllowedIps = []): bool + { if (!isset($_SERVER['REMOTE_ADDR']) || !count($aAllowedIps)) { return true; } @@ -297,11 +325,12 @@ class appmonitor { * requires http request; CLI is always allowed * On deny this method exits with 403 response * - * @param type $sVarname - * @param type $sToken + * @param string $sVarname name of GET variable + * @param string $sToken value * @return boolean */ - public function checkToken($sVarname, $sToken) { + public function checkToken(string $sVarname, string $sToken): bool + { if (!isset($_GET)) { return true; } @@ -322,7 +351,8 @@ class appmonitor { * all function names. * @return array */ - public function listChecks() { + public function listChecks(): array + { require_once 'appmonitor-checks.class.php'; $oCheck = new appmonitorcheck(); return $oCheck->listChecks(); @@ -334,8 +364,10 @@ class appmonitor { /** * verify array values and in case of an error abort and show all found errors + * @return boolean */ - protected function _checkData() { + protected function _checkData(): bool + { $aErrors = []; if (!count($this->_aChecks)) { @@ -360,19 +392,22 @@ class appmonitor { // ---------------------------------------------------------------------- /** - * stop processing the client checks and abort with an error - * @param string $sMessage + * Stop processing the client checks and abort with an error + * @param string $sMessage text to show after a 503 headline + * @return void */ - public function abort($sMessage){ + public function abort(string $sMessage): void + { header('HTTP/1.0 503 Service Unavailable'); - die('<h1>503 Service Unavailable</h1>'.$sMessage); + die('<h1>503 Service Unavailable</h1>' . $sMessage); } - + /** - * get full array for response with metadata and Checks - * @return type + * Get full array for response with metadata and checks + * @return array */ - public function getResults() { + public function getResults(): array + { return [ "meta" => $this->_aMeta, "checks" => $this->_aChecks, @@ -380,74 +415,24 @@ class appmonitor { } /** - * output appmonitor values as JSON - * @param bool $bPretty turn on pretty print; default is false - * @param bool $bHighlight print syntax highlighted html code; $bPretty must be true to enable + * Send http response with header and appmonitor JSON data + * @return string */ - public function render($bPretty = false, $bHighlight = false) { + public function render(): string + { $this->_checkData(); $this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; + $sOut=json_encode($this->getResults()); - // JSON_PRETTY_PRINT reqires PHP 5.4 - if (!defined('JSON_PRETTY_PRINT')) { - $bPretty = false; - } - if (!$bPretty) { - $bHighlight = false; - $sOut = json_encode($this->getResults()); - } else { - $sOut = json_encode($this->getResults(), JSON_PRETTY_PRINT); - if ($bHighlight) { - $aMsg = [ - 0 => "OK", - 1 => "UNKNOWN", - 2 => "WARNING", - 3 => "ERROR" - ]; - foreach (array_keys($aMsg) as $iCode) { - $sOut = preg_replace('/(\"result\":\ ' . $iCode . ')/', '$1 <span class="result' . $iCode . '"> <--- ' . $aMsg[$iCode] . ' </span>', $sOut); - } - - $sOut = preg_replace('/:\ \"(.*)\"/U', ': "<span style="color:#66e;">$1</span>"', $sOut); - $sOut = preg_replace('/:\ ([0-9]*)/', ': <span style="color:#3a3; font-weight: bold;">$1</span>', $sOut); - $sOut = preg_replace('/\"(.*)\":/U', '"<span style="color:#840;">$1</span>":', $sOut); - - $sOut = preg_replace('/([{\[])/', '$1<blockquote>', $sOut); - $sOut = preg_replace('/([}\]])/', '</blockquote>$1', $sOut); - $sOut = str_replace(' ', '', $sOut); - // $sOut = preg_replace('/([{}])/', '<span style="color:#a00; ">$1</span>', $sOut); - // $sOut = preg_replace('/([\[\]])/', '<span style="color:#088; ">$1</span>', $sOut); - - $sOut = '<!DOCTYPE html><html><head>' - . '<style>' - . 'body{background:#e0e8f8; color:#235; font-family: verdana,arial;}' - . 'blockquote{background:rgba(0,0,0,0.03); border-left: 0px solid rgba(0,0,0,0.06); margin: 0 0 0 3em; padding: 0; border-radius: 1em; border-top-left-radius: 0;}' - . 'blockquote blockquote:hover{; }' - . 'blockquote blockquote blockquote:hover{border-color: #808;}' - . 'pre{background:rgba(0,0,0,0.05); padding: 1em; border-radius: 1em;}' - . '.result0{background:#aca; border-right: 0em solid #080;}' - . '.result1{background:#666; border-right: 0em solid #ccc;}' - . '.result2{background:#fc9; border-right: 0em solid #860;}' - . '.result3{background:#800; border-right: 0em solid #f00;}' - . '</style>' - . '<title>' . __CLASS__ . '</title>' - . '</head><body>' - . '<h1>' . __CLASS__ . ' :: debug</h1>' - . '<pre>' - . $sOut - . '</pre></body></html>'; - } - } - if (!$bHighlight) { - header('Content-type: application/json'); - header('Cache-Control: cache'); - header('max-age: ' . $this->_aMeta["ttl"]); - } + header('Content-type: application/json'); + header('Cache-Control: cache'); + header('max-age: ' . $this->_aMeta["ttl"]); echo $sOut; return $sOut; } + /** - * output appmonitor client status as single html page + * Output appmonitor client status as single html page * * @example <code> * ob_start();<br> @@ -458,9 +443,10 @@ class appmonitor { * </code> * * @param string $sJson JSON of client output + * @return string */ - public function renderHtmloutput($sJson) { - + public function renderHtmloutput(string $sJson): string + { header('Content-type: text/html'); header('Cache-Control: cache'); @@ -473,61 +459,79 @@ class appmonitor { ]; // $sOut = print_r($sJson, 1); - $aData= json_decode($sJson, 1); + $aData = json_decode($sJson, 1); // ----- Ausgabe human readable - $sOut=''; - $sOut.='<h2>Metadata</h2>' - . '<div class="meta'.(isset($aData['meta']['result']) ? ' result'.$aData['meta']['result'] : '' ) .'">' - . 'Status: ' . (isset($aData['meta']['result']) ? $aMsg[$aData['meta']['result']] : '?').'<br>' - . '</div>' - . 'Host: ' . (isset($aData['meta']['host']) ? '<span class="string">' . $aData['meta']['host'] .'</span>' : '?').'<br>' - . 'Website: ' . (isset($aData['meta']['website']) ? '<span class="string">' . $aData['meta']['website'].'</span>' : '?').'<br>' - . 'Execution time: ' . (isset($aData['meta']['time']) ? '<span class="float">' . $aData['meta']['time'] .'</span>' : '?').'<br>' - . 'Client: ' . (isset($aData['meta']['version']) ? '<span class="float">' . $aData['meta']['version'] .'</span>' : '?').'<br>' - - .'<h2>Checks</h2>' - ; - if (isset($aData['checks'][0]) && count($aData['checks'])){ - foreach($aData['checks'] as $aCheck){ - $sOut.= '' - . '<span class="result'.$aCheck['result'].'"> <strong>'.$aCheck['name'].'</strong></span> <br>' - . '<div class="check">' + $sOut = ''; + $sOut .= '' + . '<h2>Metadata</h2>' + . '<div class="meta' . (isset($aData['meta']['result']) ? ' result' . $aData['meta']['result'] : '') . '">' + . 'Status: ' . (isset($aData['meta']['result']) ? $aMsg[$aData['meta']['result']] : '?') . '<br>' + . '</div>' + . 'Host: ' . (isset($aData['meta']['host']) ? '<span class="string">' . $aData['meta']['host'] . '</span>' : '?') . '<br>' + . 'Website: ' . (isset($aData['meta']['website']) ? '<span class="string">' . $aData['meta']['website'] . '</span>' : '?') . '<br>' + . 'Execution time: ' . (isset($aData['meta']['time']) ? '<span class="float">' . $aData['meta']['time'] . '</span>' : '?') . '<br>' + . 'Client: ' . (isset($aData['meta']['version']) ? '<span class="string">' . $aData['meta']['version'] . '</span>' : '?') . '<br>' + + . '<h2>Checks</h2>' + ; + if (isset($aData['checks'][0]) && count($aData['checks'])) { + foreach ($aData['checks'] as $aCheck) { + $sOut .= '' + . '<span class="result' . $aCheck['result'] . '"> <strong>' . $aCheck['name'] . '</strong></span> <br>' + . '<div class="check">' . '<div class="description">' - . $aCheck['description'].'<br>' - . $aCheck['value'].'<br>' + . $aCheck['description'] . '<br>' + . $aCheck['value'] . '<br>' + . '</div>' + . 'Execution time: <span class="float">' . (isset($aCheck['time']) ? $aCheck['time'] : ' - ') . '</span><br>' + . 'Group: <span class="string">' . (isset($aCheck['group']) ? $aCheck['group'] : '-') . '</span><br>' + . 'parent: <span class="string">' . (isset($aCheck['parent']) ? $aCheck['parent'] : '-') . '</span><br>' + . 'Status: ' . $aMsg[$aCheck['result']] . '<br>' . '</div>' - . 'Execution time: ' . (isset($aCheck['time']) ? $aCheck['time'] : ' - ').'<br>' - . 'Group: ' . (isset($aCheck['group']) ? $aCheck['group'] : '-').'<br>' - . 'parent: ' . (isset($aCheck['parent']) ? $aCheck['parent']: '-').'<br>' - . 'Status: ' . $aMsg[$aCheck['result']].'<br>' - . '</div>' - ; + ; } } - $sOut.= '<h2>List of farbcodes</h2>'; - foreach ($aMsg as $i=>$sText){ - $sOut.= '<span class="result'.$i.'">'. $sText.'</span> '; + $sOut .= '<h2>List of farbcodes</h2>'; + foreach ($aMsg as $i => $sText) { + $sOut .= '<span class="result' . $i . '">' . $sText . '</span> '; } - $sOut.='<h2>Raw result data</h2><pre>'.json_encode($aData, JSON_PRETTY_PRINT).'</pre>'; + + $sRaw=json_encode($aData, JSON_PRETTY_PRINT); + $sRaw = preg_replace('/:\ \"(.*)\"/U', ': "<span class="string">$1</span>"', $sRaw); + $sRaw = preg_replace('/:\ ([0-9]*)/', ': <span class="int">$1</span>', $sRaw); + $sRaw = preg_replace('/\"(.*)\":/U', '"<span class="key">$1</span>":', $sRaw); + + $sOut .= '<h2>Raw result data</h2><pre id="raw">' . $sRaw . '</pre>'; + + $sOut = '<!DOCTYPE html><html><head>' - . '<style>' - . 'body{background:#fff; color:#444; font-family: verdana,arial; margin: 3em;}' - . 'h1{color:#346;}' - . 'h2{color:#569; margin-top: 1.5em;}' - . '.check{border: 1px solid; padding: 0.4em; margin-bottom: 2em;}' - . '.description{font-style: italic; padding: 0.4em 1em;}' - . '.result0{background:#aca; border-left: 1em solid #080; padding: 0.5em; }' - . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0.5em; }' - . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0.5em; }' - . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0.5em; }' - . '</style>' - . '<title>' . __CLASS__ . '</title>' - . '</head><body>' - . '<h1>' . __CLASS__ . ' :: client status</h1>' - . $sOut - . '</body></html>'; - echo $sOut; + . '<style>' + . 'body{background:#eee; color:#444; font-family: verdana,arial; margin: 0; }' + . 'body>div#content{background: #fff; border-radius: 2em; border: 4px solid #abc; box-shadow: 0.5em 0.5em 2em #aaa; margin: 2em 10%; padding: 2em;}' + . 'h1{color:#346; margin: 0;}' + . 'h2{color:#569; margin-top: 2em;}' + . 'pre{background:#f4f4f8; padding: 1em; overflow-x:auto; }' + . '#raw .key{color:#808;}' + . '#raw .int{color:#3a3; font-weight: bold;}' + . '#raw .string{color:#66e;}' + . '.check{border: 1px solid #ccc; padding: 0.4em; margin-bottom: 2em;}' + . '.description{font-style: italic; padding: 0.4em 1em;}' + . '.float{color:#080;}' + . '.meta{margin-bottom: 1em;}' + . '.result0{background:#aca; border-left: 1em solid #080; padding: 0.5em; }' + . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0.5em; }' + . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0.5em; }' + . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0.5em; }' + . '.string{color:#338;}' + . '</style>' + . '<title>' . __CLASS__ . '</title>' + . '</head><body>' + . '<div id="content">' + . '<h1>' . __CLASS__ . ' :: client status</h1>' + . $sOut + . '</div>' + . '</body></html>'; return $sOut; } diff --git a/public_html/appmonitor/classes/client_all_in_one.php b/public_html/appmonitor/classes/client_all_in_one.php index 874d1bd0..b17aa09c 100644 --- a/public_html/appmonitor/classes/client_all_in_one.php +++ b/public_html/appmonitor/classes/client_all_in_one.php @@ -3,21 +3,24 @@ MERGED APPMONITOR CLIENT :: WORK IN PROGRESS - generated Do 25 Nov 2021 15:45:27 CET + generated Tue Jul 23 16:47:01 CEST 2024 */ - class checkApacheProcesses extends appmonitorcheck{ protected $_sServerStatusUrl = 'http://localhost/server-status'; protected $_iWarn = 50; protected $_iError = 75; protected function _getApacheProcesses() { $sBody = file_get_contents($this->_sServerStatusUrl); if(!$sBody){ return false; } $sRegexScoreboard = '/<pre>(.*)\<\/pre\>/U'; $aScore=array(); $sStatusNobr = str_replace("\n", "", $sBody); if (preg_match_all($sRegexScoreboard, $sStatusNobr, $aTmpTable)) { $sScoreString=$aTmpTable[1][0]; $aScore['total']=strlen($sScoreString); $aScore['free']=substr_count($sScoreString, '.'); $aScore['waiting']=substr_count($sScoreString, '_'); $aScore['active']=$aScore['total']-$aScore['free']-$aScore['waiting']; } return $aScore; } public function run($aParams){ if(isset($aParams['url']) && $aParams['url']){ $this->_sServerStatusUrl=$aParams['url']; } if(isset($aParams['warning']) && (int)$aParams['warning']){ $this->_iWarn=(int)$aParams['warning']; } if(isset($aParams['error']) && (int)$aParams['error']){ $this->_iError=(int)$aParams['error']; } $aProcesses=$this->_getApacheProcesses(); $iActive=$aProcesses ? $aProcesses['active'] : false; if($iActive===false){ $iResult=RESULT_UNKNOWN; } else { $sComment=''; $iTotal=$aProcesses['total']; $iResult=RESULT_OK; if(($iActive/$iTotal*100)>$this->_iWarn){ $iResult=RESULT_WARNING; $sComment='more than warning level '.$this->_iWarn.'%'; } else { $sComment='less than warning level '.$this->_iWarn.'%'; } if(($iActive/$iTotal*100)>$this->_iError){ $iResult=RESULT_ERROR; $sComment='more than error level '.$this->_iError.'%'; } } return array( $iResult, ($iActive===false ? 'Apache httpd server status is not available' : 'apache processes: '.print_r($aProcesses, 1)).' '.$sComment, ($iActive===false ? array() : array( 'type'=>'counter', 'count'=>$iActive, 'visual'=>'line', ) ) ); } } - class checkCert extends appmonitorcheck{ public function run($aParams) { $sUrl = isset($aParams["url"]) ? $aParams["url"] : 'http'. ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] .':' . $_SERVER['SERVER_PORT'] ; $bVerify = isset($aParams["verify"]) ? !!$aParams["verify"] : true; $iWarn = isset($aParams["warning"]) ? (int)($aParams["warning"]) : 30; $sMessage="Checked url: $sUrl ... "; $certinfo=$this->_certGetInfos($sUrl, $bVerify); if(isset($certinfo['_error'])){ return [ RESULT_ERROR, $certinfo['_error'] . $sMessage ]; } $sDNS=isset($certinfo['extensions']['subjectAltName']) ? $certinfo['extensions']['subjectAltName'] : false; $sHost=parse_url($sUrl,PHP_URL_HOST); if(strstr($sDNS, 'DNS:'.$sHost)===false){ return [ RESULT_ERROR, 'Wrong certificate: '.$sHost.' is not listed as DNS alias in ['.$sDNS.'] ' . $sMessage ]; } $iDaysleft = round(($certinfo['validTo_time_t'] - date('U')) / 60 / 60 / 24); $sMessage.= 'Issuer: '. $sIssuer=$certinfo['issuer']['O'] . '; valid from: '. date("Y-m-d H:i", $certinfo['validFrom_time_t']) . ' to '.date("Y-m-d H:i", $certinfo['validTo_time_t']).' ' . ( $iDaysleft ? "($iDaysleft days left)" : "expired since ".(-$iDaysleft)." days.") ; if ($iDaysleft<0) { return [ RESULT_ERROR, 'Expired! ' . $sMessage ]; } if ($iDaysleft<=$iWarn) { return [ RESULT_WARNING, 'Expires soon. ' . $sMessage ]; } return [ RESULT_OK, 'OK. ' .($bVerify ? 'Certificate is valid. ' : '(Verification is disabled; Check for expiration only.) ' ) . $sMessage ]; } } - class checkDiskfree extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "directory", "critical"); $sDirectory = $aParams["directory"]; if(!is_dir($sDirectory)){ return [ RESULT_ERROR, 'directory [' . $sDirectory . '] does not exist. Maybe it is wrong or is not mounted.' ]; } $iWarn = isset($aParams["warning"]) ? $this->_getSize($aParams["warning"]) : false; $iCritical = $this->_getSize($aParams["critical"]); $iSpaceLeft=disk_free_space($sDirectory); $sMessage='[' . $sDirectory . '] has '.$this->_getHrSize($iSpaceLeft).' left.'; if($iWarn){ if($iWarn<=$iCritical){ header('HTTP/1.0 503 Service Unavailable'); die("ERROR in a Diskfree check - warning value must be larger than critical.<pre>" . print_r($aParams, true)); } if ($iWarn<$iSpaceLeft){ return [ RESULT_OK, $sMessage.' Warning level is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iWarn).' over warning limit).' ]; } if ($iWarn>$iSpaceLeft && $iCritical<$iSpaceLeft){ return [ RESULT_WARNING, $sMessage.' Warning level '.$this->_getHrSize($iWarn).' was reached (space is '.$this->_getHrSize($iWarn-$iSpaceLeft).' below warning limit; still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).' ]; } } if ($iCritical<$iSpaceLeft){ return [RESULT_OK, $sMessage .' Minimum is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).']; } else { return [RESULT_ERROR, $sMessage]; } } } - class checkFile extends appmonitorcheck{ public function run($aParams) { $aOK = array(); $aErrors = array(); $this->_checkArrayKeys($aParams, "filename"); $sFile = $aParams["filename"]; if (isset($aParams['exists'])) { $sMyflag = 'exists=' . ($aParams['exists'] ? 'yes' : 'no'); if (file_exists($sFile) && $aParams['exists']) { $aOK[] = $sMyflag; } else { $aErrors[] = $sMyflag; } } foreach (array('dir', 'executable', 'file', 'link', 'readable', 'writable') as $sFiletest) { if (isset($aParams[$sFiletest])) { $sTestCmd = 'return is_' . $sFiletest . '("' . $sFile . '");'; if (eval($sTestCmd) && $aParams[$sFiletest]) { $aOK[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } else { $aErrors[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } } } $sMessage = (count($aOK) ? ' flags OK: ' . implode('|', $aOK) : '') . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '') ; if (count($aErrors)) { return [ RESULT_ERROR, 'file test [' . $sFile . '] ' . $sMessage ]; } else { return[ RESULT_OK, 'file test [' . $sFile . '] ' . $sMessage ]; } } } - class checkHello extends appmonitorcheck{ public function run($aParams){ $this->_checkArrayKeys($aParams, "message"); return array( RESULT_OK, 'Hello world! My message is: ' .$aParams['message'] ); } } - class checkHttpContent extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "url"); if (!function_exists("curl_init")) { header('HTTP/1.0 503 Service Unavailable'); die("ERROR: PHP CURL module is not installed."); } $bShowContent = (isset($aParams["content"]) && $aParams["content"]) ? true : false; $ch = curl_init($aParams["url"]); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_NOBODY, isset($aParams["headeronly"]) && $aParams["headeronly"]); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, isset($aParams["follow"]) && $aParams["follow"]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp); $res = curl_exec($ch); if (!$res) { return [ RESULT_ERROR, 'ERROR: failed to fetch ' . $aParams["url"] . '.' ]; } $sOut=''; $bError=false; $aInfos = curl_getinfo($ch); curl_close($ch); $aTmp=explode("\r\n\r\n", $res, 2); $sHttpHeader=$aTmp[0]; $sHttpBody=isset($aTmp[1]) ? $aTmp[1] : false; $sOut.="Http status: ".$aInfos['http_code']." - "; if(isset($aParams["status"])){ if($aInfos['http_code'] === $aParams["status"]){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } else { if($aInfos['http_code'] >= 400){ $sOut.="Error page detected<br>"; $bError=true; } else { $sOut.="request successful<br>"; } } if(isset($aParams["headercontains"]) && $aParams["headercontains"]){ $sOut.="Http header contains "".$aParams["headercontains"]."" - "; if(!strstr($sHttpHeader, $aParams["headercontains"])===false){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } if(isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]){ $sOut.="Http header does not contain "".$aParams["headernotcontains"]."" - "; if(strstr($sHttpHeader, $aParams["headernotcontains"])===false){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } if(isset($aParams["headerregex"]) && $aParams["headerregex"]){ $sOut.="Http header regex test "".$aParams["headerregex"]."" - "; try{ $bRegex=preg_match($aParams["headerregex"], $sHttpHeader); if($bRegex){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } catch(Exception $e){ $sOut.="Wrong REGEX<br>" . print_r($e, 1).'<br>'; $bError=true; } } if(isset($aParams["bodycontains"]) && $aParams["bodycontains"]){ $sOut.="Http body contains "".$aParams["bodycontains"]."" - "; if(!strstr($sHttpBody, $aParams["bodycontains"])===false){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } if(isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]){ $sOut.="Http body does not contain "".$aParams["bodynotcontains"]."" - "; if(strstr($sHttpBody, $aParams["bodynotcontains"])===false){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } if(isset($aParams["bodyregex"]) && $aParams["bodyregex"]){ $sOut.="Http body regex test "".$aParams["bodyregex"]."" - "; try{ $bRegex=preg_match($aParams["bodyregex"], $sHttpBody); if($bRegex){ $sOut.="compare OK<br>"; } else { $sOut.="compare failed<br>"; $bError=true; } } catch(Exception $e){ $sOut.="Wrong REGEX<br>" . print_r($e, 1).'<br>'; $bError=true; } } if (!$bError) { return [ RESULT_OK, 'OK: http check "' . $aParams["url"] . '".<br>'.$sOut ]; } else { return [ RESULT_ERROR, 'ERROR: http check "' . $aParams["url"] . '".<br>'.$sOut ]; } } } - class checkLoadmeter extends appmonitorcheck{ protected function _getLoad() { if (function_exists('sys_getloadavg')){ $load = sys_getloadavg(); return $load[0]; } else { if(class_exists('COM')){ $wmi=new COM('WinMgmts:\\\\.'); $cpus=$wmi->InstancesOf('Win32_Processor'); $load=0; if(version_compare('4.50.0', PHP_VERSION) == 1){ while($cpu = $cpus->Next()){ $load += $cpu->LoadPercentage; } }else{ foreach($cpus as $cpu){ $load += $cpu->LoadPercentage; } } return $load; } return false; } } public function run($aParams){ $fLoad=$this->_getLoad(); if($fLoad===false){ $iResult=RESULT_UNKNOWN; } else { $iResult=RESULT_OK; if(isset($aParams['warning']) && $aParams['warning'] && $fLoad>$aParams['warning']){ $iResult=RESULT_WARNING; } if(isset($aParams['error']) && $aParams['error'] && $fLoad>$aParams['error']){ $iResult=RESULT_ERROR; } } return array( $iResult, ($fLoad===false ? 'load value is not available' : 'current load is: '.$fLoad), ($fLoad===false ? array() : array( 'type'=>'counter', 'count'=>$fLoad, 'visual'=>'line', ) ) ); } } - class checkMysqlConnect extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "server,user,password,db"); $mysqli=mysqli_init(); if(!$mysqli){ return [RESULT_ERROR, 'ERROR: mysqli_init failed.']; } if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp)) { return [RESULT_ERROR, 'ERROR: setting mysqli_options failed.']; } $db = (isset($aParams["port"]) && $aParams["port"]) ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"]) : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"]) ; if ($db) { $mysqli->close(); return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"]; return true; } else { return [ RESULT_ERROR, "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error ".mysqli_connect_errno() .": ". mysqli_connect_error() ]; } } } - class checkPdoConnect extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "connect,user,password"); try{ $db = new PDO( $aParams['connect'], $aParams['user'], $aParams['password'], array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, ) ); $db=null; return [RESULT_OK, "OK: Database was connected with PDO " . $aParams['connect']]; } catch(PDOException $e) { return [RESULT_ERROR, "ERROR: Database was not connected " . $aParams['connect'] . " was not connected. Error ".$e->getMessage()]; } } } - class checkPortTcp extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "port"); $sHost = array_key_exists('host', $aParams) ? $aParams['host'] : '127.0.0.1'; $iPort = (int) $aParams['port']; $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($socket === false) { return [RESULT_UNKNOWN, "ERROR: $sHost:$iPort was not checked. socket_create() failed: " . socket_strerror(socket_last_error())]; } socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, array( "sec"=>(isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, "usec"=>0 ) ); $result = socket_connect($socket, $sHost, $iPort); if ($result === false) { socket_close($socket); return [RESULT_ERROR, "ERROR: $sHost:$iPort failed. " . socket_strerror(socket_last_error($socket))]; } else { socket_close($socket); return [RESULT_OK, "OK: $sHost:$iPort was connected."]; } } } - class checkSimple extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "result,value"); $aData=[]; foreach(array('type', 'count', 'visual') as $sMyKey){ if(isset($aParams[$sMyKey])){ $aData[$sMyKey]=$aParams[$sMyKey]; } } return [ $aParams["result"], $aParams["value"], count($aData) ? $aData : false ]; } } - class checkSqliteConnect extends appmonitorcheck{ public function run($aParams) { $this->_checkArrayKeys($aParams, "db"); if (!file_exists($aParams["db"])) { return [RESULT_ERROR, "ERROR: Sqlite database file " . $aParams["db"] . " does not exist."]; } if(!isset($aParams['user'])){ $aParams['user']=''; } if(!isset($aParams['password'])){ $aParams['password']=''; } try { $o = new PDO("sqlite:" . $aParams["db"], $aParams['user'], $aParams['password'], array( PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, ) ); return [RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected"]; } catch (Exception $e) { return [RESULT_ERROR, "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . $e->getMessage()]; } } } - if(!defined('RESULT_OK')){ define("RESULT_OK", 0); define("RESULT_UNKNOWN", 1); define("RESULT_WARNING", 2); define("RESULT_ERROR", 3); } class appmonitorcheck { protected $_aConfig = array(); protected $_aData = array(); protected $_units = array( 'B', 'KB', 'MB', 'GB', 'TB'); protected $_iTimeoutTcp=5; protected $_sPluginDir=__DIR__.'/../plugins'; public function __construct() { } protected function _createDefaultMetadata() { $this->_aData = array( "name" => $this->_aConfig["name"], "description" => $this->_aConfig["description"], "result" => RESULT_UNKNOWN, "value" => false, "type" => false, "time" => false, ); return true; } protected function _setResult($iResult) { return $this->_aData["result"] = (int) $iResult; } protected function _setOutput($s) { return $this->_aData["value"] = (string) $s; } protected function _setCounter($aParams){ if(count($aParams)){ foreach(array('type', 'count', 'visual') as $sMyKey){ if(isset($aParams[$sMyKey])){ $this->_aData[$sMyKey]=$aParams[$sMyKey]; } } } return true; } protected function _setReturn($iResult, $s, $aCounter=array()) { $this->_setResult($iResult); $this->_setOutput($s); $this->_setCounter($aCounter); return true; } protected function _checkArrayKeys($aConfig, $sKeyList) { foreach (explode(",", $sKeyList) as $sKey) { if (!array_key_exists($sKey, $aConfig)) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array." . "<pre>" . print_r($aConfig, true) .'</pre>' ); } if (is_null($aConfig[$sKey])) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - key <code>$sKey</code> is empty in config array" . "<pre>" . print_r($aConfig, true) .'</pre>' ); } } return true; } public function makeCheck($aConfig) { $this->_iStart = microtime(true); $this->_checkArrayKeys($aConfig, "name,description,check"); $this->_checkArrayKeys($aConfig["check"], "function"); $this->_aConfig = $aConfig; $this->_createDefaultMetadata(); $sCheck = preg_replace('/[^a-zA-Z0-9]/', '', $this->_aConfig["check"]["function"]); $aParams = array_key_exists("params", $this->_aConfig["check"]) ? $this->_aConfig["check"]["params"] : array(); $sPluginFile=$this->_sPluginDir.'/checks/'.$sCheck.'.php'; $sCheckClass = 'check'.$sCheck; if (!class_exists($sCheckClass)){ if (file_exists($sPluginFile)) { } } if (!class_exists($sCheckClass)){ header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - check class not found: <code>$sCheckClass</code>" . "<pre>" . print_r($aConfig, true) .'</pre>' ."<h2>Known checks</h2>\n".print_r($this->listChecks(), 1) ); } $oPlogin = new $sCheckClass; $aResponse=$oPlogin->run($aParams); if(!is_array($aResponse)){ header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - plugin : $sCheck does not responses an array" . "<pre>INPUT " . print_r($aConfig, true) .'</pre>' . "<pre>RESPONSE " . print_r($aResponse, true) .'</pre>' ); } if(count($aResponse)<2){ header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values" . "<pre>INPUT " . print_r($aConfig, true) .'</pre>' . "<pre>RESPONSE " . print_r($aResponse, true) .'</pre>' ); } if(!isset($aResponse[2])){ $aResponse[2]=array(); } $this->_setReturn($aResponse[0], $aResponse[1], $aResponse[2]); $this->_aData['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; return $this->respond(); } public function listChecks() { $aReturn = array(); $class = new ReflectionClass($this); foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) { if (strpos($oReflectionMethod->name, "check") === 0) { $aReturn[(string) $oReflectionMethod->name]=1; } } foreach(glob($this->_sPluginDir.'/checks/*.php') as $sPluginFile){ $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1; } ksort($aReturn); return array_keys($aReturn); } public function respond() { return $this->_aData; } protected function _certGetInfos($sUrl, $bVerifyCert) { $iTimeout=10; $aUrldata=parse_url($sUrl); $sHost = isset($aUrldata['host']) ? $aUrldata['host'] : false; $iPort = isset($aUrldata['port']) ? $aUrldata['port'] : ((isset($aUrldata['scheme']) && $aUrldata['scheme'] === 'https') ? 443 : false); $aSsl=array('capture_peer_cert' => true); if($bVerifyCert){ $aSsl['verify_peer']=false; $aSsl['verify_peer_name']=false; }; $get = stream_context_create(array('ssl' => $aSsl)); if(!$get){ return array('_error' => 'Error: Cannot create stream_context'); } $errno=-1; $errstr="stream_socket_client failed."; $read = stream_socket_client("ssl://$sHost:$iPort", $errno, $errstr, $iTimeout, STREAM_CLIENT_CONNECT, $get); if(!$read){ return array('_error' => "Error $errno: $errstr; cannot create stream_socket_client with given stream_context to ssl://$sHost:$iPort; you can try to set the flag [verify] to false to check expiration date only."); } $cert = stream_context_get_params($read); if(!$cert){ return array('_error' => "Error: socket was connected to ssl://$sHost:$iPort - but I cannot read certificate infos with stream_context_get_params "); } $certinfo = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); return $certinfo; } protected function _getHrSize($size){ $power = $size > 0 ? floor(log($size, 1024)) : 0; return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $this->_units[$power]; } protected function _getSize($sValue){ if(is_int($sValue)){ return $sValue; } $power=0; foreach($this->_units as $sUnit){ if (preg_match('/^[0-9\.\ ]*'.$sUnit.'/', $sValue)){ $i=preg_replace('/([0-9\.]*).*/', '$1', $sValue); $iReal=$i*pow(1024, $power); return $iReal; } $power++; } header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units) ); } protected function _compare($value, $sCompare, $verifyValue){ switch ($sCompare){ case "IS": return $value===$verifyValue; break; case "GE": return $value>=$verifyValue; break; case "GT": return $value>$verifyValue; break; case "HAS": return !!(strstr($value, $verifyValue)!==false); break; default: header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' .__METHOD__ . " - FATAL ERROR: a compare function [$sCompare] is not implemented (yet)." ); break; } return false; } } -if (!class_exists('appmonitorcheck')){ } class appmonitor { protected $_sVersion = 'php-client-v0.93'; protected $_iDefaultTtl = 300; protected $_iMaxResult = false; protected $_aMeta = array(); protected $_aChecks = array(); protected $_iStart = false; public function __construct() { $this->_createDefaultMetadata(); } protected function _createDefaultMetadata() { $this->_iStart = microtime(true); $this->_aMeta = array( "host" => false, "website" => false, "ttl" => false, "result" => false, "time" => false, "version" => $this->_sVersion, ); $this->setHost(); $this->setWebsite(); $this->setTTL(); return true; } public function setHost($s = false) { if (!$s) { $s = php_uname("n"); } return $this->_aMeta["host"] = $s; } public function setWebsite($s = false) { if (!$s && isset($_SERVER["HTTP_HOST"])) { $s = $_SERVER["HTTP_HOST"]; } return $this->_aMeta["website"] = $s; } public function setTTL($iTTl = false) { if (!$iTTl) { $iTTl = $this->_iDefaultTtl; } return $this->_aMeta["ttl"] = $iTTl; } public function setResult($iResult = false) { if ($iResult === false) { $iResult = $this->_iMaxResult; } return $this->_aMeta["result"] = $iResult; } public function addCheck($aJob = array()) { $oCheck = new appmonitorcheck(); $aCheck = $oCheck->makecheck($aJob); $iMyResult=isset($aJob['worstresult']) ? min($aCheck["result"], $aJob['worstresult']) : $aCheck["result"] ; if (!$this->_iMaxResult || $iMyResult > $this->_iMaxResult) { $this->_iMaxResult = $iMyResult; } return $this->_aChecks[] = $aCheck; } protected function _addNotification($sType, $sValue, $sKey = false) { $sTypeCleaned = preg_replace('/[^a-z]/', '', strtolower($sType)); if (!isset($this->_aMeta['notifications'])) { $this->_aMeta['notifications'] = array(); } if (!isset($this->_aMeta['notifications'][$sTypeCleaned])) { $this->_aMeta['notifications'][$sTypeCleaned] = array(); } if ($sKey) { $this->_aMeta['notifications'][$sTypeCleaned][$sKey] = $sValue; } else { $this->_aMeta['notifications'][$sTypeCleaned][] = $sValue; } return true; } public function addEmail($sEmailAddress) { return $this->_addNotification('email', $sEmailAddress); } public function addSlackWebhook($sLabel, $sSlackWebhookUrl) { return $this->_addNotification('slack', $sSlackWebhookUrl, $sLabel); } public function addTag($sTag) { if(!isset($this->_aMeta['tags'])){ $this->_aMeta['tags']=array(); } $this->_aMeta['tags'][]=$sTag; return true; } public function checkIp($aAllowedIps = array()) { if (!isset($_SERVER['REMOTE_ADDR']) || !count($aAllowedIps)) { return true; } $sIP = $_SERVER['REMOTE_ADDR']; foreach ($aAllowedIps as $sIp2Check) { if (strpos($sIP, $sIp2Check) === 0) { return true; } } header('HTTP/1.0 403 Forbidden'); die('ERROR: Your ip address [' . $sIP . '] has no access.'); } public function checkToken($sVarname, $sToken) { if (!isset($_GET)) { return true; } if (isset($_GET[$sVarname]) && $_GET[$sVarname] === $sToken) { return true; } header('HTTP/1.0 403 Forbidden'); die('ERROR: A token is required.'); } public function listChecks() { $oCheck = new appmonitorcheck(); return $oCheck->listChecks(); } protected function _checkData() { $aErrors = array(); if (!count($this->_aChecks)) { $aErrors[] = "No checks have been defined."; } if ($this->_aMeta["result"] === false) { $aErrors[] = "method setResult was not used to set a final result for all checks."; } if (count($aErrors)) { $this->abort( '<h2>Error: client check is not complete</h2><p>Found errors:</p><ol><li>' . implode('<li>', $aErrors) . '</ol><br><br>' ); } return true; } public function abort($sMessage){ header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>'.$sMessage); } public function getResults() { return array( "meta" => $this->_aMeta, "checks" => $this->_aChecks, ); } public function render($bPretty = false, $bHighlight = false) { $this->_checkData(); $this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; if (!defined('JSON_PRETTY_PRINT')) { $bPretty = false; } if (!$bPretty) { $bHighlight = false; $sOut = json_encode($this->getResults()); } else { $sOut = json_encode($this->getResults(), JSON_PRETTY_PRINT); if ($bHighlight) { $aMsg = array( 0 => "OK", 1 => "UNKNOWN", 2 => "WARNING", 3 => "ERROR" ); foreach (array_keys($aMsg) as $iCode) { $sOut = preg_replace('/(\"result\":\ ' . $iCode . ')/', '$1 <span class="result' . $iCode . '"> <--- ' . $aMsg[$iCode] . ' </span>', $sOut); } $sOut = preg_replace('/:\ \"(.*)\"/U', ': "<span style="color:#66e;">$1</span>"', $sOut); $sOut = preg_replace('/:\ ([0-9]*)/', ': <span style="color:#3a3; font-weight: bold;">$1</span>', $sOut); $sOut = preg_replace('/\"(.*)\":/U', '"<span style="color:#840;">$1</span>":', $sOut); $sOut = preg_replace('/([{\[])/', '$1<blockquote>', $sOut); $sOut = preg_replace('/([}\]])/', '</blockquote>$1', $sOut); $sOut = str_replace(' ', '', $sOut); $sOut = '<!DOCTYPE html><html><head>' . '<style>' . 'body{background:#e0e8f8; color:#235; font-family: verdana,arial;}' . 'blockquote{background:rgba(0,0,0,0.03); border-left: 0px solid rgba(0,0,0,0.06); margin: 0 0 0 3em; padding: 0; border-radius: 1em; border-top-left-radius: 0;}' . 'blockquote blockquote:hover{; }' . 'blockquote blockquote blockquote:hover{border-color: #808;}' . 'pre{background:rgba(0,0,0,0.05); padding: 1em; border-radius: 1em;}' . '.result0{background:#aca; border-right: 0em solid #080;}' . '.result1{background:#666; border-right: 0em solid #ccc;}' . '.result2{background:#fc9; border-right: 0em solid #860;}' . '.result3{background:#800; border-right: 0em solid #f00;}' . '</style>' . '<title>' . __CLASS__ . '</title>' . '</head><body>' . '<h1>' . __CLASS__ . ' :: debug</h1>' . '<pre>' . $sOut . '</pre></body></html>'; } } if (!$bHighlight) { header('Content-type: application/json'); header('Cache-Control: cache'); header('max-age: ' . $this->_aMeta["ttl"]); } echo $sOut; return $sOut; } public function renderHtmloutput($sJson) { header('Content-type: text/html'); header('Cache-Control: cache'); header('max-age: ' . $this->_aMeta["ttl"]); $aMsg = array( 0 => "OK", 1 => "UNKNOWN", 2 => "WARNING", 3 => "ERROR" ); $aData= json_decode($sJson, 1); $sOut=''; $sOut.='<h2>Metadata</h2>' . '<div class="meta'.(isset($aData['meta']['result']) ? ' result'.$aData['meta']['result'] : '' ) .'">' . 'Status: ' . (isset($aData['meta']['result']) ? $aMsg[$aData['meta']['result']] : '?').'<br>' . '</div>' . 'Host: ' . (isset($aData['meta']['host']) ? '<span class="string">' . $aData['meta']['host'] .'</span>' : '?').'<br>' . 'Website: ' . (isset($aData['meta']['website']) ? '<span class="string">' . $aData['meta']['website'].'</span>' : '?').'<br>' . 'Execution time: ' . (isset($aData['meta']['time']) ? '<span class="float">' . $aData['meta']['time'] .'</span>' : '?').'<br>' .'<h2>Checks</h2>' ; if (isset($aData['checks'][0]) && count($aData['checks'])){ foreach($aData['checks'] as $aCheck){ $sOut.= '<span class="result'.$aCheck['result'].'"> <strong>'.$aCheck['name'].'</strong></span> <br>' . $aCheck['description'].'<br>' . $aCheck['value'].'<br>' . 'Execution time: ' . $aCheck['time'].'<br>' . 'Status: ' . $aMsg[$aCheck['result']].'<br>' . '<br>' ; } } $sOut.= '<hr>List of farbcodes: '; foreach ($aMsg as $i=>$sText){ $sOut.= '<span class="result'.$i.'">'. $sText.'</span> '; } $sOut = '<!DOCTYPE html><html><head>' . '<style>' . 'body{background:#fff; color:#444; font-family: verdana,arial; margin: 3em;}' . '.result0{background:#aca; border-left: 1em solid #080; padding: 0 0.5em; }' . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0 0.5em; }' . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0 0.5em; }' . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0 0.5em; }' . '</style>' . '<title>' . __CLASS__ . '</title>' . '</head><body>' . '<h1>' . __CLASS__ . ' :: client status</h1>' . $sOut . '</body></html>'; echo $sOut; return $sOut; } } \ No newline at end of file + class checkApacheProcesses extends appmonitorcheck { protected string $_sServerStatusUrl = 'http://localhost/server-status'; protected float $_iWarn = 50; protected float $_iError = 75; public function explain(): array { return [ 'name' => 'Plugin ApacheProcesses', 'descriptionm' => 'Check count running Apache processes', 'parameters' => [ 'url' => [ 'type' => 'string', 'required' => false, 'decsription' => 'Override https server-status page; default is http://localhost/server-status; Use it if the protocol to localhost is not http, but https or if it requires an authentication', 'default' => $this->_sServerStatusUrl, 'example' => '', ], 'warning' => [ 'type' => 'float', 'required' => false, 'decsription' => 'Limit to switch to warning (in percent)', 'default' => $this->_iWarn, 'example' => 30, ], 'error' => [ 'type' => 'float', 'required' => false, 'decsription' => 'Limit to switch to critical (in percent)', 'default' => $this->_iError, 'example' => 50, ], ], ]; } protected function _getApacheProcesses(): bool|array { $sBody = file_get_contents($this->_sServerStatusUrl); if (!$sBody) { return false; } $sRegexScoreboard = '/<pre>(.*)\<\/pre\>/U'; $aScore = []; $sStatusNobr = str_replace("\n", "", $sBody); if (preg_match_all($sRegexScoreboard, $sStatusNobr, $aTmpTable)) { $sScoreString = $aTmpTable[1][0]; $aScore['total'] = strlen($sScoreString); $aScore['free'] = substr_count($sScoreString, '.'); $aScore['waiting'] = substr_count($sScoreString, '_'); $aScore['active'] = $aScore['total'] - $aScore['free'] - $aScore['waiting']; } return $aScore; } public function getGroup(): string { return 'monitor'; } public function run(array $aParams): array { if (isset($aParams['url']) && $aParams['url']) { $this->_sServerStatusUrl = $aParams['url']; } if (isset($aParams['warning']) && (int) $aParams['warning']) { $this->_iWarn = (int) $aParams['warning']; } if (isset($aParams['error']) && (int) $aParams['error']) { $this->_iError = (int) $aParams['error']; } $aProcesses = $this->_getApacheProcesses(); $iActive = $aProcesses ? $aProcesses['active'] : false; if ($iActive === false) { $iResult = RESULT_UNKNOWN; } else { $sComment = ''; $iTotal = $aProcesses['total']; $iResult = RESULT_OK; if (($iActive / $iTotal * 100) > $this->_iWarn) { $iResult = RESULT_WARNING; $sComment = "more than warning level $this->_iWarn %"; } else { $sComment = "less than warning level $this->_iWarn %"; } if (($iActive / $iTotal * 100) > $this->_iError) { $iResult = RESULT_ERROR; $sComment = "more than error level $this->_iError %"; } } return [ $iResult, ($iActive === false ? 'Apache httpd server status is not available' : 'apache processes: ' . print_r($aProcesses, 1)) . ' ' . $sComment, ($iActive === false ? [] : [ 'type' => 'counter', 'count' => $iActive, 'visual' => 'line', ] ) ]; } } + class checkCert extends appmonitorcheck { public function getGroup(): string { return 'security'; } public function run(array $aParams): array { $sUrl = $aParams["url"] ?? 'http' . ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']; $bVerify = isset($aParams["verify"]) ? !!$aParams["verify"] : true; $iWarn = isset($aParams["warning"]) ? (int) ($aParams["warning"]) : 21; $iCrtitcal = isset($aParams["critical"]) ? (int) ($aParams["critical"]) : 5; $sMessage = "Checked url: $sUrl ... "; $certinfo = $this->_certGetInfos($sUrl, $bVerify); if (isset($certinfo['_error'])) { return [ RESULT_ERROR, $certinfo['_error'] . $sMessage ]; } $sDNS = $certinfo['extensions']['subjectAltName'] ?? false; $sHost = parse_url($sUrl, PHP_URL_HOST); if (strstr($sDNS, "DNS:$sHost") === false) { return [ RESULT_ERROR, "Wrong certificate: $sHost is not listed as DNS alias in [$sDNS]. $sMessage" ]; } $iDaysleft = round(($certinfo['validTo_time_t'] - date('U')) / 60 / 60 / 24); $sMessage .= 'Issuer: ' . $certinfo['issuer']['O'] . '; valid from: ' . date("Y-m-d H:i", $certinfo['validFrom_time_t']) . ' to ' . date("Y-m-d H:i", $certinfo['validTo_time_t']) . ' ' . ($iDaysleft ? "($iDaysleft days left)" : "expired since " . (-$iDaysleft) . " days.") ; if ($iDaysleft <= 0) { return [ RESULT_ERROR, 'Expired! ' . $sMessage ]; } if ($iDaysleft <= $iWarn) { return [ RESULT_WARNING, ($iDaysleft <= $iCrtitcal ? 'Expires very soon! ' : 'Expires soon. ' ) . $sMessage ]; } return [ RESULT_OK, 'OK. ' . ($bVerify ? 'Certificate is valid. ' : '(Verification is disabled; Check for expiration only.) ') . $sMessage ]; } } + class checkDiskfree extends appmonitorcheck { public function getGroup(): string { return 'disk'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "directory,critical"); $sDirectory = $aParams["directory"]; if (!is_dir($sDirectory)) { return [ RESULT_ERROR, "directory [$sDirectory] does not exist. Maybe it is wrong or is not mounted." ]; } $iWarn = isset($aParams["warning"]) ? $this->_getSize($aParams["warning"]) : false; $iCritical = $this->_getSize($aParams["critical"]); $iSpaceLeft = disk_free_space($sDirectory); $sMessage = '[' . $sDirectory . '] has ' . $this->_getHrSize($iSpaceLeft) . ' left.'; if ($iWarn) { if ($iWarn <= $iCritical) { header('HTTP/1.0 503 Service Unavailable'); die("ERROR in a Diskfree check - warning value must be larger than critical.<pre>" . print_r($aParams, true)); } if ($iWarn < $iSpaceLeft) { return [ RESULT_OK, "$sMessage Warning level is not reached yet (still " . $this->_getHrSize($iSpaceLeft - $iWarn) . "over warning limit)." ]; } if ($iWarn > $iSpaceLeft && $iCritical < $iSpaceLeft) { return [ RESULT_WARNING, $sMessage . ' Warning level ' . $this->_getHrSize($iWarn) . ' was reached (space is ' . $this->_getHrSize($iWarn - $iSpaceLeft) . ' below warning limit; still ' . $this->_getHrSize($iSpaceLeft - $iCritical) . ' over critical limit).' ]; } } if ($iCritical < $iSpaceLeft) { return [RESULT_OK, $sMessage . ' Minimum is not reached yet (still ' . $this->_getHrSize($iSpaceLeft - $iCritical) . ' over critical limit).']; } else { return [RESULT_ERROR, $sMessage]; } } } + class checkExec extends appmonitorcheck { public function getGroup() { return 'service'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "command"); $_sCmd = $aParams['command']; $_bShowOutput = isset($aParams['output']) ? !!$aParams['output'] : true; $_aRcOK = isset($aParams['exitOK']) ? $aParams['exitOK'] : []; $_aRcWarning = isset($aParams['exitWarn']) ? $aParams['exitWarn'] : []; $_aRcCritical = isset($aParams['exitCritical']) ? $aParams['exitCritical'] : []; $_sMode = 'default'; if (count($_aRcOK) + count($_aRcWarning) + count($_aRcCritical)) { $_sMode = 'exitcode'; } exec($_sCmd, $aOutput, $iRc); $_sOut = $_bShowOutput ? '<br>' . implode("<br>", $aOutput) : ''; switch ($_sMode) { case "default": if ($iRc) { return [ RESULT_ERROR, 'command failed with exitcode ' . $iRc . ': [' . $_sCmd . ']' . $_sOut ]; } else { return [ RESULT_OK, "OK [$_sCmd] $_sOut" ]; } ; ; case "exitcode": if (in_array($iRc, $_aRcCritical)) { return [ RESULT_ERROR, "Critical exitcode $iRc detected: [$_sCmd] $_sOut" ]; } if (in_array($iRc, $_aRcWarning)) { return [ RESULT_WARNING, "Warning exitcode $iRc detected: [$_sCmd] $_sOut" ]; } if ($iRc == 0 || in_array($iRc, $_aRcOK)) { return [ RESULT_OK, "OK exitcode $iRc detected: [$_sCmd] $_sOut" ]; } return [ RESULT_UNKNOWN, "UNKNOWN - unhandled exitcode $iRc detected: [$_sCmd] $_sOut" ]; case "search": return [ RESULT_UNKNOWN, "UNKNOWN method [$_sMode] - is not implemented yet." ]; ; default: return [ RESULT_UNKNOWN, 'UNKNOWN mode [' . htmlentities($_sMode) . '].' ]; } } } + class checkFile extends appmonitorcheck { public function getGroup(array $aParams = []): string { $sReturn = 'file'; if (isset($aParams['dir'])) { $sReturn = 'folder'; } foreach (['exists', 'executable', 'readable', 'writable'] as $sFlag) { if (isset($aParams[$sFlag]) && !$aParams[$sFlag]) { $sReturn = 'deny'; } } return $sReturn; } public function run(array $aParams): array { $aOK = []; $aErrors = []; $this->_checkArrayKeys($aParams, "filename"); $sFile = $aParams["filename"]; if (isset($aParams['exists'])) { $sMyflag = 'exists=' . ($aParams['exists'] ? 'yes' : 'no'); if (file_exists($sFile) && $aParams['exists']) { $aOK[] = $sMyflag; } else { $aErrors[] = $sMyflag; } } foreach (['dir', 'executable', 'file', 'link', 'readable', 'writable'] as $sFiletest) { if (isset($aParams[$sFiletest])) { $sTestCmd = 'return is_' . $sFiletest . '("' . $sFile . '");'; if (eval ($sTestCmd) && $aParams[$sFiletest]) { $aOK[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } else { $aErrors[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } } } $sMessage = (count($aOK) ? ' flags OK: ' . implode('|', $aOK) : '') . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '') ; if (count($aErrors)) { return [ RESULT_ERROR, "file test [$sFile] $sMessage" ]; } else { return [ RESULT_OK, "file test [$sFile] $sMessage" ]; } } } + class checkHello extends appmonitorcheck { public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "message"); return [ RESULT_OK, 'Hello world! My message is: ' . $aParams['message'] ]; } } + class checkHttpContent extends appmonitorcheck { public function getGroup(array $aParams=[]): string { $sReturn = 'service'; if (isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500) { $sReturn = 'deny'; } return $sReturn; } public function run(array $aParams) { $this->_checkArrayKeys($aParams, "url"); if (!function_exists("curl_init")) { header('HTTP/1.0 503 Service Unavailable'); die("ERROR: PHP CURL module is not installed."); } $bShowContent = (isset($aParams["content"]) && $aParams["content"]) ? true : false; $ch = curl_init($aParams["url"]); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_NOBODY, isset($aParams["headeronly"]) && $aParams["headeronly"]); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, isset($aParams["follow"]) && $aParams["follow"]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, isset($aParams["sslverify"]) ? !!$aParams["sslverify"] : 1); curl_setopt($ch, CURLOPT_TIMEOUT, (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp); if (isset($aParams["userpwd"])) { curl_setopt($ch, CURLOPT_USERPWD, $aParams["userpwd"]); } $res = curl_exec($ch); if (!$res) { $iErrorCode = curl_errno($ch); $sErrorMsg = curl_error($ch); curl_close($ch); return [ RESULT_ERROR, 'ERROR: failed to fetch ' . $aParams["url"] . ' - curl error #' . $iErrorCode . ': ' . $sErrorMsg ]; } $sOut = ''; $bError = false; $aInfos = curl_getinfo($ch); curl_close($ch); $aTmp = explode("\r\n\r\n", $res, 2); $sHttpHeader = $aTmp[0]; $sHttpBody = $aTmp[1] ?? false; $sOut .= "Http status: " . $aInfos['http_code'] . " - "; if (isset($aParams["status"])) { if ($aInfos['http_code'] === $aParams["status"]) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } else { if ($aInfos['http_code'] >= 400) { $sOut .= "Error page detected<br>"; $bError = true; } else { $sOut .= "request successful<br>"; } } if (isset($aParams["headercontains"]) && $aParams["headercontains"]) { $sOut .= "Http header contains "" . $aParams["headercontains"] . "" - "; if (!strstr($sHttpHeader, $aParams["headercontains"]) === false) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } if (isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]) { $sOut .= "Http header does not contain "" . $aParams["headernotcontains"] . "" - "; if (strstr($sHttpHeader, $aParams["headernotcontains"]) === false) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } if (isset($aParams["headerregex"]) && $aParams["headerregex"]) { $sOut .= "Http header regex test "" . $aParams["headerregex"] . "" - "; try { $bRegex = preg_match($aParams["headerregex"], $sHttpHeader); if ($bRegex) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } catch (Exception $e) { $sOut .= "Wrong REGEX<br>" . print_r($e, 1) . '<br>'; $bError = true; } } if (isset($aParams["bodycontains"]) && $aParams["bodycontains"]) { $sOut .= "Http body contains "" . $aParams["bodycontains"] . "" - "; if (!strstr($sHttpBody, $aParams["bodycontains"]) === false) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } if (isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]) { $sOut .= "Http body does not contain "" . $aParams["bodynotcontains"] . "" - "; if (strstr($sHttpBody, $aParams["bodynotcontains"]) === false) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } if (isset($aParams["bodyregex"]) && $aParams["bodyregex"]) { $sOut .= "Http body regex test "" . $aParams["bodyregex"] . "" - "; try { $bRegex = preg_match($aParams["bodyregex"], $sHttpBody); if ($bRegex) { $sOut .= "compare OK<br>"; } else { $sOut .= "compare failed<br>"; $bError = true; } } catch (Exception $e) { $sOut .= "Wrong REGEX<br>" . print_r($e, 1) . '<br>'; $bError = true; } } if (!$bError) { return [ RESULT_OK, 'OK: http check "' . $aParams["url"] . '".<br>' . $sOut ]; } else { return [ RESULT_ERROR, 'ERROR: http check "' . $aParams["url"] . '".<br>' . $sOut ]; } } } + class checkLoadmeter extends appmonitorcheck { public function getGroup(): string { return 'monitor'; } protected function _getLoad(): float { if (function_exists('sys_getloadavg')) { $load = sys_getloadavg(); return $load[0]; } else { if (class_exists('COM')) { $wmi = new COM('WinMgmts:\\\\.'); $cpus = $wmi->InstancesOf('Win32_Processor'); $load = 0; if (version_compare('4.50.0', PHP_VERSION) == 1) { while ($cpu = $cpus->Next()) { $load += $cpu->LoadPercentage; } } else { foreach ($cpus as $cpu) { $load += $cpu->LoadPercentage; } } return $load; } return false; } } public function run(array $aParams): array { $fLoad = $this->_getLoad(); if ($fLoad === false) { $iResult = RESULT_UNKNOWN; } else { $iResult = RESULT_OK; if (isset($aParams['warning']) && $aParams['warning'] && $fLoad > $aParams['warning']) { $iResult = RESULT_WARNING; } if (isset($aParams['error']) && $aParams['error'] && $fLoad > $aParams['error']) { $iResult = RESULT_ERROR; } } return [ $iResult, ($fLoad === false ? 'load value is not available' : 'current load is: ' . $fLoad), ($fLoad === false ? [] : [ 'type' => 'counter', 'count' => $fLoad, 'visual' => 'line', ] ) ] ; } } + class checkMysqlConnect extends appmonitorcheck { public function getGroup(): string { return 'database'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "server,user,password,db"); $mysqli = mysqli_init(); if (!$mysqli) { return [RESULT_ERROR, 'ERROR: mysqli_init failed.']; } if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp)) { return [RESULT_ERROR, 'ERROR: setting mysqli_options failed.']; } $db = (isset($aParams["port"]) && $aParams["port"]) ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"]) : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"]) ; if ($db) { $mysqli->close(); return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"]; } else { return [ RESULT_ERROR, "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error " . mysqli_connect_errno() . ": " . mysqli_connect_error() ]; } } } + class checkPdoConnect extends appmonitorcheck { public function getGroup(): string { return 'database'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "connect,user,password"); try { $db = new PDO( $aParams['connect'], $aParams['user'], $aParams['password'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, ] ); $db = null; return [RESULT_OK, "OK: Database was connected with PDO " . $aParams['connect']]; } catch (PDOException $e) { return [RESULT_ERROR, "ERROR: Database was not connected " . $aParams['connect'] . " was not connected. Error " . $e->getMessage()]; } } } + class checkPhpmodules extends appmonitorcheck { public function getGroup(): string { return 'service'; } public function run(array $aParams): array { $sOut = ''; $bHasError = false; $bHasWarning = false; $aAllMods = get_loaded_extensions(false); if (isset($aParams['required']) && count($aParams['required'])) { $sOut .= 'Required: '; foreach ($aParams['required'] as $sMod) { $sOut .= $sMod . '='; if (!array_search($sMod, $aAllMods) === false) { $sOut .= 'OK;'; } else { $bHasError = true; $sOut .= 'MISS;'; } } } if (isset($aParams['optional']) && count($aParams['optional'])) { $sOut .= ($sOut ? '|' : '') . 'Optional: '; foreach ($aParams['optional'] as $sMod) { $sOut .= $sMod . '='; if (!array_search($sMod, $aAllMods) === false) { $sOut .= 'OK;'; } else { $bHasWarning = true; $sOut .= 'MISS;'; } } } if ($bHasError) { return [RESULT_ERROR, "ERROR: " . $sOut]; } if ($bHasWarning) { return [RESULT_WARNING, "WARNING: " . $sOut]; } return [RESULT_OK, "OK: " . $sOut]; } } + class checkPing extends appmonitorcheck { public function getGroup(): string { return 'network'; } public function run(array $aParams): array { $sHost = $aParams['host'] ?? '127.0.0.1'; $sParamCount = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? "n" : "c"; $iRepeat = 1; $sCommand = "ping -$sParamCount $iRepeat $sHost 2>&1"; exec($sCommand, $aOut, $iRc); $sOut = implode("\n", $aOut); if ($iRc > 0) { return [RESULT_ERROR, "ERROR: ping to $sHost failed.\n" . $sOut]; } return [RESULT_OK, "OK: ping to $sHost\n" . $sOut]; } } + class checkPortTcp extends appmonitorcheck { public function getGroup(): string { return 'network'; } public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "port"); $sHost = $aParams['host'] ?? '127.0.0.1'; $iPort = (int) $aParams['port']; if (!function_exists('socket_create')) { return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform tcp test. The socket module is not enabled in the php installation."]; } $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if ($socket === false) { return [RESULT_UNKNOWN, "ERROR: $sHost:$iPort was not checked. socket_create() failed: " . socket_strerror(socket_last_error())]; } socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, [ "sec" => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, "usec" => 0 ] ); $result = @socket_connect($socket, $sHost, $iPort); if ($result === false) { $aResult = [RESULT_ERROR, "ERROR: $sHost:$iPort failed. " . socket_strerror(socket_last_error($socket))]; socket_close($socket); return $aResult; } else { socket_close($socket); return [RESULT_OK, "OK: $sHost:$iPort was connected."]; } } } + class checkSimple extends appmonitorcheck { public function run(array $aParams): array { $this->_checkArrayKeys($aParams, "result,value"); $aData = []; foreach (['type', 'count', 'visual'] as $sMyKey) { if (isset($aParams[$sMyKey])) { $aData[$sMyKey] = $aParams[$sMyKey]; } } return [ $aParams["result"], $aParams["value"], count($aData) ? $aData : false ]; } } + class checkSqliteConnect extends appmonitorcheck { public function getGroup() { return 'database'; } public function run($aParams): array { $this->_checkArrayKeys($aParams, "db"); if (!file_exists($aParams["db"])) { return [ RESULT_ERROR, "ERROR: Sqlite database file " . $aParams["db"] . " does not exist." ]; } if (!isset($aParams['user'])) { $aParams['user'] = ''; } if (!isset($aParams['password'])) { $aParams['password'] = ''; } try { $o = new PDO( "sqlite:" . $aParams["db"], $aParams['user'], $aParams['password'], [ PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, ] ); return [ RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected" ]; } catch (Exception $e) { return [ RESULT_ERROR, "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . $e->getMessage() ]; } } } + if (!defined('RESULT_OK')) { define("RESULT_OK", 0); define("RESULT_UNKNOWN", 1); define("RESULT_WARNING", 2); define("RESULT_ERROR", 3); } class appmonitorcheck { protected float $_iStart = 0; protected array $_aConfig = []; protected array $_aData = []; protected array $_units = ['B', 'KB', 'MB', 'GB', 'TB']; protected int $_iTimeoutTcp = 5; protected string $_sPluginDir = __DIR__ . '/../plugins'; public function __construct() { } protected function _createDefaultMetadata(): bool { $this->_aData = [ "name" => $this->_aConfig["name"], "description" => $this->_aConfig["description"], "group" => isset($this->_aConfig["group"]) ? $this->_aConfig["group"] : false, "parent" => isset($this->_aConfig["parent"]) ? $this->_aConfig["parent"] : false, "result" => RESULT_UNKNOWN, "value" => false, "type" => false, "time" => false, ]; return true; } protected function _setResult(int $iResult): true { $this->_aData["result"] = (int) $iResult; return true; } protected function _setOutput(string $s): bool { $this->_aData["value"] = $s; return true; } protected function _setCounter(array $aParams): bool { if (is_array($aParams) && count($aParams)) { foreach (['type', 'count', 'visual'] as $sMyKey) { if (isset($aParams[$sMyKey])) { $this->_aData[$sMyKey] = $aParams[$sMyKey]; } } } return true; } protected function _setReturn(int $iResult, string $s, array $aCounter = []) { $this->_setResult($iResult); $this->_setOutput($s); $this->_setCounter($aCounter); return true; } protected function _checkArrayKeys($aConfig, $sKeyList) { foreach (explode(",", $sKeyList) as $sKey) { if (!isset($aConfig[$sKey])) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array." . "<pre>" . print_r($aConfig, true) . '</pre>' ); } if (is_null($aConfig[$sKey])) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - key <code>$sKey</code> is empty in config array" . "<pre>" . print_r($aConfig, true) . '</pre>' ); } } return true; } public function makeCheck(array $aConfig): array { $this->_iStart = microtime(true); $this->_checkArrayKeys($aConfig, "name,description,check"); $this->_checkArrayKeys($aConfig["check"], "function"); $this->_aConfig = $aConfig; $this->_createDefaultMetadata(); $sCheck = preg_replace('/[^a-zA-Z0-9]/', '', $this->_aConfig["check"]["function"]); $aParams = $this->_aConfig["check"]["params"] ?? []; $sPluginFile = strtolower($this->_sPluginDir . '/checks/' . $sCheck . '.php'); $sCheckClass = 'check' . $sCheck; if (!class_exists($sCheckClass)) { if (file_exists($sPluginFile)) { } } if (!class_exists($sCheckClass)) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - check class not found: <code>$sCheckClass</code>" . "<pre>" . print_r($aConfig, true) . '</pre>' . "<h2>Known checks</h2>\n" . print_r($this->listChecks(), 1) ); } $oPlugin = new $sCheckClass; $aResponse = $oPlugin->run($aParams); if (!is_array($aResponse)) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - plugin : $sCheck does not responses an array" . "<pre>INPUT " . print_r($aConfig, true) . '</pre>' . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>' ); } if (count($aResponse) < 2) { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values" . "<pre>INPUT " . print_r($aConfig, true) . '</pre>' . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>' ); } if (!isset($aResponse[2]) || !$aResponse[2]) { $aResponse[2] = []; } $this->_setReturn($aResponse[0], $aResponse[1], $aResponse[2]); if (!$this->_aData['group'] && method_exists($oPlugin, "getGroup")) { $this->_aData['group'] = $oPlugin->getGroup($aParams); } $this->_aData['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; return $this->respond(); } public function listChecks(): array { $aReturn = []; $class = new ReflectionClass($this); foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) { if (strpos($oReflectionMethod->name, "check") === 0) { $aReturn[(string) $oReflectionMethod->name] = 1; } } foreach (glob($this->_sPluginDir . '/checks/*.php') as $sPluginFile) { $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1; } ksort($aReturn); return array_keys($aReturn); } public function respond() { return $this->_aData; } protected function _certGetInfos(string $sUrl, bool $bVerifyCert): array { $iTimeout = 10; $aUrldata = parse_url($sUrl); $sHost = isset($aUrldata['host']) ? $aUrldata['host'] : false; $iPort = isset($aUrldata['port']) ? $aUrldata['port'] : ((isset($aUrldata['scheme']) && $aUrldata['scheme'] === 'https') ? 443 : false); $aSsl = ['capture_peer_cert' => true]; if ($bVerifyCert) { $aSsl['verify_peer'] = false; $aSsl['verify_peer_name'] = false; } ; $get = stream_context_create(['ssl' => $aSsl]); if (!$get) { return ['_error' => 'Error: Cannot create stream_context']; } $errno = -1; $errstr = "stream_socket_client failed."; $read = stream_socket_client("ssl://$sHost:$iPort", $errno, $errstr, $iTimeout, STREAM_CLIENT_CONNECT, $get); if (!$read) { return ['_error' => "Error $errno: $errstr; cannot create stream_socket_client with given stream_context to ssl://$sHost:$iPort; you can try to set the flag [verify] to false to check expiration date only."]; } $cert = stream_context_get_params($read); if (!$cert) { return ['_error' => "Error: socket was connected to ssl://$sHost:$iPort - but I cannot read certificate infos with stream_context_get_params "]; } return openssl_x509_parse($cert['options']['ssl']['peer_certificate']); } protected function _getHrSize(int $size): string { $power = $size > 0 ? floor(log($size, 1024)) : 0; return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $this->_units[$power]; } protected function _getSize(string $sValue): int { if (is_int($sValue)) { return $sValue; } $power = 0; foreach ($this->_units as $sUnit) { if (preg_match('/^[0-9\.\ ]*' . $sUnit . '/', $sValue)) { $i = preg_replace('/([0-9\.]*).*/', '$1', $sValue); $iReal = $i * pow(1024, $power); return $iReal; } $power++; } header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . '<h2>Details</h2>' . __METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units) ); } } +if (!class_exists('appmonitorcheck')) { } class appmonitor { protected string $_sVersion = 'php-client-v0.137'; protected int $_iDefaultTtl = 300; protected int $_iMaxResult = -1; protected array $_aMeta = []; protected array $_aChecks = []; protected float $_iStart = 0; public function __construct() { $this->_createDefaultMetadata(); } protected function _createDefaultMetadata(): bool { $this->_iStart = microtime(true); $this->_aMeta = [ "host" => false, "website" => false, "ttl" => false, "result" => false, "time" => false, "version" => $this->_sVersion, ]; $this->setHost(); $this->setWebsite(); $this->setTTL(); return true; } public function setHost(string $s = ''): bool { if (!$s) { $s = php_uname("n"); } if (!$s) { return false; } $this->_aMeta["host"] = $s; return true; } public function setWebsite($sWebsite = ''): bool { if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) { $sWebsite = $_SERVER["HTTP_HOST"]; } if (!$sWebsite) { return false; } $this->_aMeta["website"] = $sWebsite; return true; } public function setTTL($iTTl = 0) { if ($iTTl == 0) { $iTTl = $this->_iDefaultTtl; } return $this->_aMeta["ttl"] = $iTTl; } public function setResult(int $iResult = -1): bool { if ($iResult === -1) { $iResult = $this->_iMaxResult; } $this->_aMeta["result"] = $iResult; return true; } public function addCheck($aJob = []): bool { $oCheck = new appmonitorcheck(); $aCheck = $oCheck->makecheck($aJob); $iMyResult = isset($aJob['worstresult']) ? min($aCheck["result"], $aJob['worstresult']) : $aCheck["result"] ; if (!$this->_iMaxResult || $iMyResult > $this->_iMaxResult) { $this->_iMaxResult = $iMyResult; } $this->_aChecks[] = $aCheck; return true; } protected function _addNotification(string $sType, string $sValue, string $sKey = ''): bool { $sTypeCleaned = preg_replace('/[^a-z]/', '', strtolower($sType)); if (!isset($this->_aMeta['notifications'])) { $this->_aMeta['notifications'] = []; } if (!isset($this->_aMeta['notifications'][$sTypeCleaned])) { $this->_aMeta['notifications'][$sTypeCleaned] = []; } if ($sKey) { $this->_aMeta['notifications'][$sTypeCleaned][$sKey] = $sValue; } else { $this->_aMeta['notifications'][$sTypeCleaned][] = $sValue; } return true; } public function addEmail(string $sEmailAddress) { return $this->_addNotification('email', $sEmailAddress); } public function addSlackWebhook(string $sLabel, string $sSlackWebhookUrl): bool { return $this->_addNotification('slack', $sSlackWebhookUrl, $sLabel); } public function addTag(string $sTag): bool { if (!isset($this->_aMeta['tags'])) { $this->_aMeta['tags'] = []; } $this->_aMeta['tags'][] = str_replace(' ', '_', $sTag); return true; } public function checkIp(array $aAllowedIps = []): bool { if (!isset($_SERVER['REMOTE_ADDR']) || !count($aAllowedIps)) { return true; } $sIP = $_SERVER['REMOTE_ADDR']; foreach ($aAllowedIps as $sIp2Check) { if (strpos($sIP, $sIp2Check) === 0) { return true; } } header('HTTP/1.0 403 Forbidden'); die('ERROR: Your ip address [' . $sIP . '] has no access.'); } public function checkToken(string $sVarname, string $sToken): bool { if (!isset($_GET)) { return true; } if (isset($_GET[$sVarname]) && $_GET[$sVarname] === $sToken) { return true; } header('HTTP/1.0 403 Forbidden'); die('ERROR: A token is required.'); } public function listChecks(): array { $oCheck = new appmonitorcheck(); return $oCheck->listChecks(); } protected function _checkData(): bool { $aErrors = []; if (!count($this->_aChecks)) { $aErrors[] = "No checks have been defined."; } if ($this->_aMeta["result"] === false) { $aErrors[] = "method setResult was not used to set a final result for all checks."; } if (count($aErrors)) { $this->abort( '<h2>Error: client check is not complete</h2><p>Found errors:</p><ol><li>' . implode('<li>', $aErrors) . '</ol><br><br>' ); } return true; } public function abort(string $sMessage): void { header('HTTP/1.0 503 Service Unavailable'); die('<h1>503 Service Unavailable</h1>' . $sMessage); } public function getResults(): array { return [ "meta" => $this->_aMeta, "checks" => $this->_aChecks, ]; } public function render(): string { $this->_checkData(); $this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; $sOut=json_encode($this->getResults()); header('Content-type: application/json'); header('Cache-Control: cache'); header('max-age: ' . $this->_aMeta["ttl"]); echo $sOut; return $sOut; } public function renderHtmloutput(string $sJson): string { header('Content-type: text/html'); header('Cache-Control: cache'); header('max-age: ' . $this->_aMeta["ttl"]); $aMsg = [ 0 => "OK", 1 => "UNKNOWN", 2 => "WARNING", 3 => "ERROR" ]; $aData = json_decode($sJson, 1); $sOut = ''; $sOut .= '' . '<h2>Metadata</h2>' . '<div class="meta' . (isset($aData['meta']['result']) ? ' result' . $aData['meta']['result'] : '') . '">' . 'Status: ' . (isset($aData['meta']['result']) ? $aMsg[$aData['meta']['result']] : '?') . '<br>' . '</div>' . 'Host: ' . (isset($aData['meta']['host']) ? '<span class="string">' . $aData['meta']['host'] . '</span>' : '?') . '<br>' . 'Website: ' . (isset($aData['meta']['website']) ? '<span class="string">' . $aData['meta']['website'] . '</span>' : '?') . '<br>' . 'Execution time: ' . (isset($aData['meta']['time']) ? '<span class="float">' . $aData['meta']['time'] . '</span>' : '?') . '<br>' . 'Client: ' . (isset($aData['meta']['version']) ? '<span class="string">' . $aData['meta']['version'] . '</span>' : '?') . '<br>' . '<h2>Checks</h2>' ; if (isset($aData['checks'][0]) && count($aData['checks'])) { foreach ($aData['checks'] as $aCheck) { $sOut .= '' . '<span class="result' . $aCheck['result'] . '"> <strong>' . $aCheck['name'] . '</strong></span> <br>' . '<div class="check">' . '<div class="description">' . $aCheck['description'] . '<br>' . $aCheck['value'] . '<br>' . '</div>' . 'Execution time: <span class="float">' . (isset($aCheck['time']) ? $aCheck['time'] : ' - ') . '</span><br>' . 'Group: <span class="string">' . (isset($aCheck['group']) ? $aCheck['group'] : '-') . '</span><br>' . 'parent: <span class="string">' . (isset($aCheck['parent']) ? $aCheck['parent'] : '-') . '</span><br>' . 'Status: ' . $aMsg[$aCheck['result']] . '<br>' . '</div>' ; } } $sOut .= '<h2>List of farbcodes</h2>'; foreach ($aMsg as $i => $sText) { $sOut .= '<span class="result' . $i . '">' . $sText . '</span> '; } $sRaw=json_encode($aData, JSON_PRETTY_PRINT); $sRaw = preg_replace('/:\ \"(.*)\"/U', ': "<span class="string">$1</span>"', $sRaw); $sRaw = preg_replace('/:\ ([0-9]*)/', ': <span class="int">$1</span>', $sRaw); $sRaw = preg_replace('/\"(.*)\":/U', '"<span class="key">$1</span>":', $sRaw); $sOut .= '<h2>Raw result data</h2><pre id="raw">' . $sRaw . '</pre>'; $sOut = '<!DOCTYPE html><html><head>' . '<style>' . 'body{background:#eee; color:#444; font-family: verdana,arial; margin: 0; }' . 'body>div#content{background: #fff; border-radius: 2em; border: 4px solid #abc; box-shadow: 0.5em 0.5em 2em #aaa; margin: 2em 10%; padding: 2em;}' . 'h1{color:#346; margin: 0;}' . 'h2{color:#569; margin-top: 2em;}' . 'pre{background:#f4f4f8; padding: 1em; overflow-x:auto; }' . '#raw .key{color:#808;}' . '#raw .int{color:#3a3; font-weight: bold;}' . '#raw .string{color:#66e;}' . '.check{border: 1px solid #ccc; padding: 0.4em; margin-bottom: 2em;}' . '.description{font-style: italic; padding: 0.4em 1em;}' . '.float{color:#080;}' . '.meta{margin-bottom: 1em;}' . '.result0{background:#aca; border-left: 1em solid #080; padding: 0.5em; }' . '.result1{background:#ccc; border-left: 1em solid #aaa; padding: 0.5em; }' . '.result2{background:#fc9; border-left: 1em solid #860; padding: 0.5em; }' . '.result3{background:#f88; border-left: 1em solid #f00; padding: 0.5em; }' . '.string{color:#338;}' . '</style>' . '<title>' . __CLASS__ . '</title>' . '</head><body>' . '<div id="content">' . '<h1>' . __CLASS__ . ' :: client status</h1>' . $sOut . '</div>' . '</body></html>'; return $sOut; } } \ No newline at end of file diff --git a/public_html/appmonitor/git_update_appmonitor.sh b/public_html/appmonitor/git_update_appmonitor.sh index fdc069e9..32f5b785 100755 --- a/public_html/appmonitor/git_update_appmonitor.sh +++ b/public_html/appmonitor/git_update_appmonitor.sh @@ -1,14 +1,21 @@ #!/bin/bash # ====================================================================== # -# UPDATE APPMONITOR CLIENT +# A P P M O N I T O R :: CLIENT - UPDATE # -# requires git, rsync +# This script will install or update the appmonitor client only. +# +# Below the document root of a website create a new directory, +# i.e. [webroot]/appmonitor/ and copy this script there. +# Change the directory "cd [webroot]/appmonitor/" and execute it. # # ---------------------------------------------------------------------- -# 2022-04-11 <axel.hahn@iml.unibe.ch> first lines -# 2022-04-12 <axel.hahn@iml.unibe.ch> add help; exclude unneeded files -# 2022-05-03 <axel.hahn@iml.unibe.ch> create general_include.php +# requires git, rsync +# ---------------------------------------------------------------------- +# 2022-04-11 0.1 <axel.hahn@iml.unibe.ch> first lines +# 2022-04-12 0.2 <axel.hahn@iml.unibe.ch> add help; exclude unneeded files +# 2022-05-03 0.3 <axel.hahn@iml.unibe.ch> create general_include.php +# 2024-07-25 0.4 <axel.hahn@iml.unibe.ch> update quoting and comments # ====================================================================== # ---------------------------------------------------------------------- @@ -16,14 +23,14 @@ # ---------------------------------------------------------------------- readonly git_repo_url="https://github.com/iml-it/appmonitor.git" -readonly line="____________________________________________________________" -readonly version="0.3" +readonly line="______________________________________________________________________________" +readonly version="0.4" git_target=/tmp/git_data__appmonitor client_from="${git_target}/public_html/client" client_to="." -cd $( dirname "$0" ) || exit 1 +cd "$( dirname "$0" )" || exit 1 # ---------------------------------------------------------------------- # FUNCTIONS @@ -99,6 +106,9 @@ case "$1" in This is a helper script to get the files of the IML Appmonitor client part only. + Below the document root of a website create a new directory, + i.e. [webroot]/appmonitor/ and copy this script there. + This script clones and updates the repository in the /tmp directory and syncs the client files of it to a given directory. @@ -157,6 +167,7 @@ rsync -rav \ --exclude "*.sample.*" \ --exclude "example.json" \ --exclude "check-appmonitor-server.php" \ + --exclude "local.php" \ $client_from/* "$client_to" echo @@ -165,7 +176,7 @@ _fileupdate general_include.sample.php echo $line echo ">>> #3 of 3 >>> Diff" echo -diff -r "$client_from" "$client_to" +diff --color -r "$client_from" "$client_to" echo diff --git a/public_html/appmonitor/local.php b/public_html/appmonitor/local.php new file mode 100644 index 00000000..8fcc94e1 --- /dev/null +++ b/public_html/appmonitor/local.php @@ -0,0 +1,6 @@ +<?php + +require __DIR__ . '/check-appmonitor-server.php'; +$sJson=ob_get_contents(); +ob_end_clean(); +echo $oMonitor->renderHtmloutput($sJson); diff --git a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php index d85938a9..0c468221 100755 --- a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php +++ b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php @@ -12,6 +12,7 @@ * 2019-05-17 aded check http to config- and tmp dir * 2021-11-nn removed all checks ... created as single files * 2022-03-28 move checks into plugins/apps/ + * 2024-07-23 php 8: short array syntax */ // ---------------------------------------------------------------------- @@ -19,65 +20,65 @@ // ---------------------------------------------------------------------- $oMonitor->addCheck( - array( + [ "name" => "write to ./tmp/", "description" => "Check cache storage", // "group" => "folder", - "check" => array( + "check" => [ "function" => "File", - "params" => array( - "filename" => $sApproot . "/server/tmp", + "params" => [ + "filename" => "$sApproot/server/tmp", "dir" => true, "writable" => true, - ), - ), - ) + ], + ], + ] ); $oMonitor->addCheck( - array( + [ "name" => "write to ./config/", "description" => "Check config target directory", // "group" => "folder", - "check" => array( + "check" => [ "function" => "File", - "params" => array( - "filename" => $sApproot . "/server/config", + "params" => [ + "filename" => "$sApproot/server/config", "dir" => true, "writable" => true, - ), - ), - ) + ], + ], + ] ); $oMonitor->addCheck( - array( + [ "name" => "check config file", "description" => "The config file must be writable", "parent" => "write to ./config/", // "group" => "file", - "check" => array( + "check" => [ "function" => "File", - "params" => array( - "filename" => $sApproot . "/server/config/appmonitor-server-config.json", + "params" => [ + "filename" => "$sApproot/server/config/appmonitor-server-config.json", "file" => true, "writable" => true, - ), - ), - ) + ], + ], + ] ); $oMonitor->addCheck( - array( + [ "name" => "PHP modules", "description" => "Check needed PHP modules", // "group" => "folder", - "check" => array( + "check" => [ "function" => "Phpmodules", - "params" => array( + "params" => [ "required" => ["curl"], "optional" => [], - ), - ), - ) + ], + ], + ] ); // ---------------------------------------------------------------------- @@ -89,21 +90,21 @@ $sBaseUrl = 'http'.(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? 's' : '') .'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'] .dirname(dirname($_SERVER['REQUEST_URI'])); -foreach(array('server/config', 'server/tmp') as $sMyDir){ +foreach(['server/config', 'server/tmp'] as $sMyDir){ $oMonitor->addCheck( - array( + [ "name" => "http to $sMyDir", "description" => "Check if the $sMyDir directory is not accessible (counts as warning on fail)", "group" => "deny", - "check" => array( + "check" => [ "function" => "HttpContent", - "params" => array( - "url" => $sBaseUrl . "/$sMyDir/readme.md", + "params" => [ + "url" => "$sBaseUrl/$sMyDir/readme.md", "status" => 403, - ), - ), + ], + ], "worstresult" => RESULT_WARNING - ) + ] ); } @@ -114,48 +115,48 @@ require_once($sApproot.'/server/classes/appmonitor-server.class.php'); $oServer=new appmonitorserver(); $iCount=count($oServer->getAppIds()); $oMonitor->addCheck( - array( + [ "name" => "appcounter", "description" => "Monitored apps", "group" => "monitor", "parent" => "check config file", - "check" => array( + "check" => [ "function" => "Simple", - "params" => array( + "params" => [ "result" => RESULT_OK, "value" => "Found monitored web apps: $iCount", "count" => $iCount, "visual" => "simple", - ), - ), - ) + ], + ], + ] ); // ---------------------------------------------------------------------- // check running service // ---------------------------------------------------------------------- require_once($sApproot.'/server/classes/tinyservice.class.php'); ob_start(); -$oService = new tinyservice($sApproot.'/server/service.php', 15, $sApproot.'/server/tmp'); +$oService = new tinyservice("$sApproot/server/service.php", 15, "$sApproot/server/tmp"); $sIsStopped=$oService->canStart(); $out=ob_get_contents(); ob_clean(); $oMonitor->addCheck( - array( + [ "name" => "running service", "description" => "Check if the service is running", "group" => "service", - "check" => array( + "check" => [ "function" => "Simple", - "params" => array( + "params" => [ "result" => ($sIsStopped ? RESULT_WARNING : RESULT_OK), "value" => ($sIsStopped ? "Info: Service is NOT running. Apps are checked interactively only (if the appmonitor web ui is running). | Output: $out" : "OK, service is running. | Output: $out" - ), - ), - ), + ) + ], + ], "worstresult" => RESULT_OK - ) + ] ); // ---------------------------------------------------------------------- // check certificate if https is used @@ -170,42 +171,42 @@ include 'shared_check_ssl.php'; * AS A DEMO: using a custom plugin: * $oMonitor->addCheck( - array( + [ "name" => "plugin test", "description" => "minimal test of the plugin plugins/checkHello.php", - "check" => array( + "check" => [ "function" => "Hello", - "params" => array( + "params" => [] "message" => "Here I am", - ), - ), - ) + ], + ], + ] ); $oMonitor->addCheck( - array( + [ "name" => "plugin Load", "description" => "check current load", - "check" => array( + "check" => [ "function" => "Loadmeter", - "params" => array( + "params" => [ "warning" => 1.0, "error" => 3, - ), - ), + ], + ], "worstresult" => RESULT_OK - ) + ] ); $oMonitor->addCheck( - array( + [ "name" => "plugin ApacheProcesses", "description" => "check count running Apache processes", - "check" => array( + "check" => [ "function" => "ApacheProcesses", - "params" => array( - ), - ), + "params" => [ + ], + ], "worstresult" => RESULT_OK - ) + ] ); */ diff --git a/public_html/appmonitor/plugins/checks/apacheprocesses.php b/public_html/appmonitor/plugins/checks/apacheprocesses.php index 2b6809be..400c880b 100755 --- a/public_html/appmonitor/plugins/checks/apacheprocesses.php +++ b/public_html/appmonitor/plugins/checks/apacheprocesses.php @@ -44,94 +44,145 @@ * * 2019-06-07 <axel.hahn@iml.unibe.ch> * 2022-07-06 <axel.hahn@iml.unibe.ch> set group "monitor" + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkApacheProcesses extends appmonitorcheck{ - - protected $_sServerStatusUrl = 'http://localhost/server-status'; - protected $_iWarn = 50; - protected $_iError = 75; - +class checkApacheProcesses extends appmonitorcheck +{ + /** - * fetch http server status and return slots, active and waiting processes - * as array i.e. [total] => 256 \n [free] => 247\n [waiting] => 7\n [active] => 2 - * @return boolean + * url of server status + * @var string + */ + protected string $_sServerStatusUrl = 'http://localhost/server-status'; + + /** + * Warning level in percent + * @var float + */ + protected float $_iWarn = 50; + + /** + * Critical level in percent + * @var float + */ + protected float $_iError = 75; + + /** + * Self documentation (as idea) + * @return array */ - protected function _getApacheProcesses() { + public function explain(): array + { + return [ + 'name' => 'Plugin ApacheProcesses', + 'descriptionm' => 'Check count running Apache processes', + 'parameters' => [ + 'url' => [ + 'type' => 'string', + 'required' => false, + 'decsription' => 'Override https server-status page; default is http://localhost/server-status; Use it if the protocol to localhost is not http, but https or if it requires an authentication', + 'default' => $this->_sServerStatusUrl, + 'example' => '', + ], + 'warning' => [ + 'type' => 'float', + 'required' => false, + 'decsription' => 'Limit to switch to warning (in percent)', + 'default' => $this->_iWarn, + 'example' => 30, + ], + 'error' => [ + 'type' => 'float', + 'required' => false, + 'decsription' => 'Limit to switch to critical (in percent)', + 'default' => $this->_iError, + 'example' => 50, + ], + ], + ]; + } + /** + * Fetch http server status and return slots, active and waiting processes + * as array i.e. [total] => 256 \n [free] => 247\n [waiting] => 7\n [active] => 2 + * It returns false if the url is not reachable + * It returns an empty array if the server status could not be parsed from http response + * @return array + */ + protected function _getApacheProcesses(): bool|array + { $sBody = file_get_contents($this->_sServerStatusUrl); - if(!$sBody){ + if (!$sBody) { return false; } $sRegexScoreboard = '/<pre>(.*)\<\/pre\>/U'; - $aScore=[]; + $aScore = []; $sStatusNobr = str_replace("\n", "", $sBody); if (preg_match_all($sRegexScoreboard, $sStatusNobr, $aTmpTable)) { - $sScoreString=$aTmpTable[1][0]; + $sScoreString = $aTmpTable[1][0]; // $aScore['scoreboard']=$sScoreString; - $aScore['total']=strlen($sScoreString); - $aScore['free']=substr_count($sScoreString, '.'); - $aScore['waiting']=substr_count($sScoreString, '_'); - $aScore['active']=$aScore['total']-$aScore['free']-$aScore['waiting']; + $aScore['total'] = strlen($sScoreString); + $aScore['free'] = substr_count($sScoreString, '.'); + $aScore['waiting'] = substr_count($sScoreString, '_'); + $aScore['active'] = $aScore['total'] - $aScore['free'] - $aScore['waiting']; } return $aScore; } - /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup(): string + { return 'monitor'; } /** - * - * @param array $aParams + * Implemented method: run the check + * @param array $aParams parameters * @return array */ - public function run($aParams){ - + public function run(array $aParams): array + { + // --- (1) verify if array key(s) exist: // $this->_checkArrayKeys($aParams, "..."); - if(isset($aParams['url']) && $aParams['url']){ - $this->_sServerStatusUrl=$aParams['url']; + if (isset($aParams['url']) && $aParams['url']) { + $this->_sServerStatusUrl = $aParams['url']; } - if(isset($aParams['warning']) && (int)$aParams['warning']){ - $this->_iWarn=(int)$aParams['warning']; + if (isset($aParams['warning']) && (int) $aParams['warning']) { + $this->_iWarn = (int) $aParams['warning']; } - if(isset($aParams['error']) && (int)$aParams['error']){ - $this->_iError=(int)$aParams['error']; + if (isset($aParams['error']) && (int) $aParams['error']) { + $this->_iError = (int) $aParams['error']; } - // --- (2) do something magic - $aProcesses=$this->_getApacheProcesses(); - $iActive=$aProcesses ? $aProcesses['active'] : false; - + $aProcesses = $this->_getApacheProcesses(); + $iActive = $aProcesses ? $aProcesses['active'] : false; + // set result code - if($iActive===false){ - $iResult=RESULT_UNKNOWN; + if ($iActive === false) { + $iResult = RESULT_UNKNOWN; } else { - $sComment=''; - $iTotal=$aProcesses['total']; - $iResult=RESULT_OK; - if(($iActive/$iTotal*100)>$this->_iWarn){ - $iResult=RESULT_WARNING; - $sComment='more than warning level '.$this->_iWarn.'%'; + $sComment = ''; + $iTotal = $aProcesses['total']; + $iResult = RESULT_OK; + if (($iActive / $iTotal * 100) > $this->_iWarn) { + $iResult = RESULT_WARNING; + $sComment = "more than warning level $this->_iWarn %"; } else { - $sComment='less than warning level '.$this->_iWarn.'%'; + $sComment = "less than warning level $this->_iWarn %"; } - if(($iActive/$iTotal*100)>$this->_iError){ - $iResult=RESULT_ERROR; - $sComment='more than error level '.$this->_iError.'%'; + if (($iActive / $iTotal * 100) > $this->_iError) { + $iResult = RESULT_ERROR; + $sComment = "more than error level $this->_iError %"; } } - // --- (3) response // see method appmonitorcheck->_setReturn() // @@ -144,14 +195,14 @@ class checkApacheProcesses extends appmonitorcheck{ // visual => {string} one of bar|line|simple (+params) // return [ - $iResult, - ($iActive===false ? 'Apache httpd server status is not available' : 'apache processes: '.print_r($aProcesses, 1)).' '.$sComment, - ($iActive===false + $iResult, + ($iActive === false ? 'Apache httpd server status is not available' : 'apache processes: ' . print_r($aProcesses, 1)) . ' ' . $sComment, + ($iActive === false ? [] : [ - 'type'=>'counter', - 'count'=>$iActive, - 'visual'=>'line', + 'type' => 'counter', + 'count' => $iActive, + 'visual' => 'line', ] ) ]; diff --git a/public_html/appmonitor/plugins/checks/cert.php b/public_html/appmonitor/plugins/checks/cert.php index e83fa9e3..a6705b23 100755 --- a/public_html/appmonitor/plugins/checks/cert.php +++ b/public_html/appmonitor/plugins/checks/cert.php @@ -39,20 +39,22 @@ * 2021-10-26 <axel.hahn@iml.unibe.ch> * 2022-05-02 <axel.hahn@iml.unibe.ch> set warning to 21 days (old value was 30); add "critical" param * 2022-05-03 <axel.hahn@iml.unibe.ch> critical limit is a warning only (because app is still functional) + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkCert extends appmonitorcheck{ +class checkCert extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup(): string + { return 'security'; } /** - * check SSL certificate + * Check SSL certificate * @param array $aParams * [ * "url" optional: url to connect check; default: own protocol + server @@ -60,62 +62,60 @@ class checkCert extends appmonitorcheck{ * "warning" optional: count of days to warn; default=21 (=3 weeks) * "critical" optional: count of days to raise critical; default=5 * ] - * @return boolean + * @return array */ - public function run($aParams) { - $sUrl = isset($aParams["url"]) - ? $aParams["url"] - : 'http'. ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] .':' . $_SERVER['SERVER_PORT'] - ; - $bVerify = isset($aParams["verify"]) ? !!$aParams["verify"] : true; - $iWarn = isset($aParams["warning"]) ? (int)($aParams["warning"]) : 21; - $iCrtitcal = isset($aParams["critical"]) ? (int)($aParams["critical"]) : 5; + public function run(array $aParams): array + { + $sUrl = $aParams["url"] ?? 'http' . ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']; + $bVerify = isset($aParams["verify"]) ? !!$aParams["verify"] : true; + $iWarn = isset($aParams["warning"]) ? (int) ($aParams["warning"]) : 21; + $iCrtitcal = isset($aParams["critical"]) ? (int) ($aParams["critical"]) : 5; - $sMessage="Checked url: $sUrl ... "; - $certinfo=$this->_certGetInfos($sUrl, $bVerify); - if(isset($certinfo['_error'])){ + $sMessage = "Checked url: $sUrl ... "; + $certinfo = $this->_certGetInfos($sUrl, $bVerify); + if (isset($certinfo['_error'])) { return [ - RESULT_ERROR, + RESULT_ERROR, $certinfo['_error'] . $sMessage ]; } - - $sDNS=isset($certinfo['extensions']['subjectAltName']) ? $certinfo['extensions']['subjectAltName'] : false; - $sHost=parse_url($sUrl,PHP_URL_HOST); - if(strstr($sDNS, 'DNS:'.$sHost)===false){ + + $sDNS = $certinfo['extensions']['subjectAltName'] ?? false; + $sHost = parse_url($sUrl, PHP_URL_HOST); + if (strstr($sDNS, "DNS:$sHost") === false) { return [ - RESULT_ERROR, - 'Wrong certificate: '.$sHost.' is not listed as DNS alias in ['.$sDNS.'] ' . $sMessage + RESULT_ERROR, + "Wrong certificate: $sHost is not listed as DNS alias in [$sDNS]. $sMessage" ]; } - + $iDaysleft = round(($certinfo['validTo_time_t'] - date('U')) / 60 / 60 / 24); - $sMessage.= 'Issuer: '. $sIssuer=$certinfo['issuer']['O'] - . '; valid from: '. date("Y-m-d H:i", $certinfo['validFrom_time_t']) - . ' to '.date("Y-m-d H:i", $certinfo['validTo_time_t']).' ' - . ( $iDaysleft ? "($iDaysleft days left)" : "expired since ".(-$iDaysleft)." days.") - ; - if ($iDaysleft<=0) { + $sMessage .= 'Issuer: ' . $certinfo['issuer']['O'] + . '; valid from: ' . date("Y-m-d H:i", $certinfo['validFrom_time_t']) + . ' to ' . date("Y-m-d H:i", $certinfo['validTo_time_t']) . ' ' + . ($iDaysleft ? "($iDaysleft days left)" : "expired since " . (-$iDaysleft) . " days.") + ; + if ($iDaysleft <= 0) { return [ - RESULT_ERROR, + RESULT_ERROR, 'Expired! ' . $sMessage ]; } - if ($iDaysleft<=$iWarn) { + if ($iDaysleft <= $iWarn) { return [ - RESULT_WARNING, - ($iDaysleft<=$iCrtitcal - ? 'Expires very soon! ' - : 'Expires soon. ' - ). $sMessage + RESULT_WARNING, + ($iDaysleft <= $iCrtitcal + ? 'Expires very soon! ' + : 'Expires soon. ' + ) . $sMessage ]; } // echo '<pre>'; return [ - RESULT_OK, - 'OK. ' - .($bVerify ? 'Certificate is valid. ' : '(Verification is disabled; Check for expiration only.) ' ) - . $sMessage + RESULT_OK, + 'OK. ' + . ($bVerify ? 'Certificate is valid. ' : '(Verification is disabled; Check for expiration only.) ') + . $sMessage ]; } diff --git a/public_html/appmonitor/plugins/checks/diskfree.php b/public_html/appmonitor/plugins/checks/diskfree.php index 403c94e5..f1d94284 100755 --- a/public_html/appmonitor/plugins/checks/diskfree.php +++ b/public_html/appmonitor/plugins/checks/diskfree.php @@ -18,67 +18,69 @@ * ____________________________________________________________________________ * * 2021-10-26 <axel.hahn@iml.unibe.ch> + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkDiskfree extends appmonitorcheck{ +class checkDiskfree extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup(): string + { return 'disk'; } /** - * check free disk space on a given directory + * Check free disk space on a given directory * @param array $aParams * [ * "directory" directory that must exist * "warning" space for warning (optional) * "critical" minimal space * ] - * @return boolean + * @return array */ - public function run($aParams) { - $this->_checkArrayKeys($aParams, "directory", "critical"); - + public function run(array $aParams): array + { + $this->_checkArrayKeys($aParams, "directory,critical"); + $sDirectory = $aParams["directory"]; - if(!is_dir($sDirectory)){ + if (!is_dir($sDirectory)) { return [ - RESULT_ERROR, - 'directory [' . $sDirectory . '] does not exist. Maybe it is wrong or is not mounted.' + RESULT_ERROR, + "directory [$sDirectory] does not exist. Maybe it is wrong or is not mounted." ]; } - + $iWarn = isset($aParams["warning"]) ? $this->_getSize($aParams["warning"]) : false; $iCritical = $this->_getSize($aParams["critical"]); - $iSpaceLeft=disk_free_space($sDirectory); - - - $sMessage='[' . $sDirectory . '] has '.$this->_getHrSize($iSpaceLeft).' left.'; - - if($iWarn){ - if($iWarn<=$iCritical){ + $iSpaceLeft = disk_free_space($sDirectory); + + $sMessage = '[' . $sDirectory . '] has ' . $this->_getHrSize($iSpaceLeft) . ' left.'; + + if ($iWarn) { + if ($iWarn <= $iCritical) { header('HTTP/1.0 503 Service Unavailable'); die("ERROR in a Diskfree check - warning value must be larger than critical.<pre>" . print_r($aParams, true)); } - if ($iWarn<$iSpaceLeft){ + if ($iWarn < $iSpaceLeft) { return [ - RESULT_OK, - $sMessage.' Warning level is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iWarn).' over warning limit).' + RESULT_OK, + "$sMessage Warning level is not reached yet (still " . $this->_getHrSize($iSpaceLeft - $iWarn) . "over warning limit)." ]; } - if ($iWarn>$iSpaceLeft && $iCritical<$iSpaceLeft){ + if ($iWarn > $iSpaceLeft && $iCritical < $iSpaceLeft) { return [ - RESULT_WARNING, - $sMessage.' Warning level '.$this->_getHrSize($iWarn).' was reached (space is '.$this->_getHrSize($iWarn-$iSpaceLeft).' below warning limit; still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).' + RESULT_WARNING, + $sMessage . ' Warning level ' . $this->_getHrSize($iWarn) . ' was reached (space is ' . $this->_getHrSize($iWarn - $iSpaceLeft) . ' below warning limit; still ' . $this->_getHrSize($iSpaceLeft - $iCritical) . ' over critical limit).' ]; } } // check space - if ($iCritical<$iSpaceLeft){ - return [RESULT_OK, $sMessage .' Minimum is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).']; + if ($iCritical < $iSpaceLeft) { + return [RESULT_OK, $sMessage . ' Minimum is not reached yet (still ' . $this->_getHrSize($iSpaceLeft - $iCritical) . ' over critical limit).']; } else { return [RESULT_ERROR, $sMessage]; } diff --git a/public_html/appmonitor/plugins/checks/exec.php b/public_html/appmonitor/plugins/checks/exec.php index 05ff55da..6221ca6f 100644 --- a/public_html/appmonitor/plugins/checks/exec.php +++ b/public_html/appmonitor/plugins/checks/exec.php @@ -20,20 +20,22 @@ * ____________________________________________________________________________ * * 2022-09-19 <axel.hahn@iml.unibe.ch> + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkExec extends appmonitorcheck{ +class checkExec extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - see run() method - * @return array + * Get default group of this check + * @return string */ - public function getGroup($aParams){ + public function getGroup() + { return 'service'; } /** - * check execution of a command + * Check execution of a command * @param array $aParams * [ * "command" {string} command to execute @@ -48,74 +50,78 @@ class checkExec extends appmonitorcheck{ * "searchWarn" {string} if search string is found check returns with warning * "searchCritical" {string} if search string is found check returns with critical * ] - * @return boolean + * @return array */ - public function run($aParams) { + public function run(array $aParams): array + { $this->_checkArrayKeys($aParams, "command"); - $_sCmd=$aParams['command']; - $_bShowOutput=isset($aParams['output']) ? !!$aParams['output'] : true; + $_sCmd = $aParams['command']; + $_bShowOutput = isset($aParams['output']) ? !!$aParams['output'] : true; - $_aRcOK=isset($aParams['exitOK']) ? $aParams['exitOK'] : []; - $_aRcWarning=isset($aParams['exitWarn']) ? $aParams['exitWarn'] : []; - $_aRcCritical=isset($aParams['exitCritical']) ? $aParams['exitCritical'] : []; + $_aRcOK = isset($aParams['exitOK']) ? $aParams['exitOK'] : []; + $_aRcWarning = isset($aParams['exitWarn']) ? $aParams['exitWarn'] : []; + $_aRcCritical = isset($aParams['exitCritical']) ? $aParams['exitCritical'] : []; - $_sMode='default'; - if(count($_aRcOK) + count($_aRcWarning) + count($_aRcCritical)){ - $_sMode='exitcode'; + $_sMode = 'default'; + if (count($_aRcOK) + count($_aRcWarning) + count($_aRcCritical)) { + $_sMode = 'exitcode'; } - exec($_sCmd,$aOutput, $iRc); - $_sOut=$_bShowOutput ? '<br>'.implode("<br>", $aOutput) : ''; + exec($_sCmd, $aOutput, $iRc); + $_sOut = $_bShowOutput ? '<br>' . implode("<br>", $aOutput) : ''; - switch($_sMode){ + switch ($_sMode) { // non-zero exitcode is an error case "default": if ($iRc) { return [ - RESULT_ERROR, - 'command failed with exitcode '.$iRc.': [' . $_sCmd . ']'.$_sOut + RESULT_ERROR, + 'command failed with exitcode ' . $iRc . ': [' . $_sCmd . ']' . $_sOut ]; } else { - return[ - RESULT_OK, - 'OK [' . $_sCmd . '] ' .$_sOut + return [ + RESULT_OK, + "OK [$_sCmd] $_sOut" ]; - }; - break;; + } + ; + // break; + ; // handle given custom exitcodes case "exitcode": - if (in_array($iRc, $_aRcCritical)){ + if (in_array($iRc, $_aRcCritical)) { return [ - RESULT_ERROR, - 'Critical exitcode '.$iRc.' detected: [' . $_sCmd . ']'.$_sOut + RESULT_ERROR, + "Critical exitcode $iRc detected: [$_sCmd] $_sOut" ]; } - if (in_array($iRc, $_aRcWarning)){ + if (in_array($iRc, $_aRcWarning)) { return [ - RESULT_WARNING, - 'Warning exitcode '.$iRc.' detected: [' . $_sCmd . ']'.$_sOut + RESULT_WARNING, + "Warning exitcode $iRc detected: [$_sCmd] $_sOut" ]; } - if ($iRc == 0 || in_array($iRc, $_aRcOK)){ + if ($iRc == 0 || in_array($iRc, $_aRcOK)) { return [ - RESULT_OK, - 'OK exitcode '.$iRc.' detected: [' . $_sCmd . ']'.$_sOut + RESULT_OK, + "OK exitcode $iRc detected: [$_sCmd] $_sOut" ]; } return [ - RESULT_UNKNOWN, - 'UNKNOWN - unhandled exitcode '.$iRc.' detected: [' . $_sCmd . ']'.$_sOut + RESULT_UNKNOWN, + "UNKNOWN - unhandled exitcode $iRc detected: [$_sCmd] $_sOut" ]; case "search": - return[ - RESULT_UNKNOWN, - 'UNKNOWN method [' . $_sMode . '] - is not implemented yet.' + return [ + RESULT_UNKNOWN, + "UNKNOWN method [$_sMode] - is not implemented yet." ]; - break;; + // break; + ; default: - return[ - RESULT_UNKNOWN, + return [ + RESULT_UNKNOWN, 'UNKNOWN mode [' . htmlentities($_sMode) . '].' ]; } // switch($_sMode) diff --git a/public_html/appmonitor/plugins/checks/file.php b/public_html/appmonitor/plugins/checks/file.php index 590ac778..f0aafde1 100755 --- a/public_html/appmonitor/plugins/checks/file.php +++ b/public_html/appmonitor/plugins/checks/file.php @@ -21,29 +21,31 @@ * ____________________________________________________________________________ * * 2021-10-26 <axel.hahn@iml.unibe.ch> - * + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables */ -class checkFile extends appmonitorcheck{ +class checkFile extends appmonitorcheck +{ /** - * get default group of this check + * Get default group of this check * @param array $aParams - see run() method - * @return array + * @return string */ - public function getGroup($aParams){ - $sReturn='file'; - if(isset($aParams['dir'])){ - $sReturn='folder'; + public function getGroup(array $aParams = []): string + { + $sReturn = 'file'; + if (isset($aParams['dir'])) { + $sReturn = 'folder'; } - foreach(['exists', 'executable', 'readable', 'writable'] as $sFlag){ - if (isset($aParams[$sFlag]) && !$aParams[$sFlag]){ - $sReturn='deny'; + foreach (['exists', 'executable', 'readable', 'writable'] as $sFlag) { + if (isset($aParams[$sFlag]) && !$aParams[$sFlag]) { + $sReturn = 'deny'; } } return $sReturn; } /** - * check a file + * Check a file * @param array $aParams * [ * "filename" directory that must exist @@ -55,9 +57,10 @@ class checkFile extends appmonitorcheck{ * "readable" flag is readable * "writable" flag is writable * ] - * @return boolean + * @return array */ - public function run($aParams) { + public function run(array $aParams): array + { $aOK = []; $aErrors = []; $this->_checkArrayKeys($aParams, "filename"); @@ -71,10 +74,10 @@ class checkFile extends appmonitorcheck{ $aErrors[] = $sMyflag; } } - foreach ([ 'dir', 'executable', 'file', 'link', 'readable', 'writable' ] as $sFiletest) { + foreach (['dir', 'executable', 'file', 'link', 'readable', 'writable'] as $sFiletest) { if (isset($aParams[$sFiletest])) { $sTestCmd = 'return is_' . $sFiletest . '("' . $sFile . '");'; - if (eval($sTestCmd) && $aParams[$sFiletest]) { + if (eval ($sTestCmd) && $aParams[$sFiletest]) { $aOK[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } else { $aErrors[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); @@ -82,16 +85,17 @@ class checkFile extends appmonitorcheck{ } } $sMessage = (count($aOK) ? ' flags OK: ' . implode('|', $aOK) : '') - . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '') + . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '') ; if (count($aErrors)) { return [ - RESULT_ERROR, - 'file test [' . $sFile . '] ' . $sMessage + RESULT_ERROR, + "file test [$sFile] $sMessage" ]; } else { - return[ - RESULT_OK, 'file test [' . $sFile . '] ' . $sMessage + return [ + RESULT_OK, + "file test [$sFile] $sMessage" ]; } } diff --git a/public_html/appmonitor/plugins/checks/hello.php b/public_html/appmonitor/plugins/checks/hello.php index ebeb9818..e2ba5e60 100755 --- a/public_html/appmonitor/plugins/checks/hello.php +++ b/public_html/appmonitor/plugins/checks/hello.php @@ -40,17 +40,20 @@ * ____________________________________________________________________________ * * 2019-06-05 <axel.hahn@iml.unibe.ch> + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkHello extends appmonitorcheck{ - +class checkHello extends appmonitorcheck +{ + /** - * + * Run the check * @param array $aParams * @return array */ - public function run($aParams){ - + public function run(array $aParams): array + { + // --- (1) verify if array key(s) exist: $this->_checkArrayKeys($aParams, "message"); @@ -70,8 +73,8 @@ class checkHello extends appmonitorcheck{ // visual => {string} one of bar|line|simple (+params) // return [ - RESULT_OK, - 'Hello world! My message is: ' .$aParams['message'] + RESULT_OK, + 'Hello world! My message is: ' . $aParams['message'] ]; } } diff --git a/public_html/appmonitor/plugins/checks/httpcontent.php b/public_html/appmonitor/plugins/checks/httpcontent.php index 037adf6c..8655a067 100755 --- a/public_html/appmonitor/plugins/checks/httpcontent.php +++ b/public_html/appmonitor/plugins/checks/httpcontent.php @@ -20,24 +20,29 @@ * 2021-10-26 <axel.hahn@iml.unibe.ch> * 2022-12-21 <axel.hahn@unibe.ch> add flag sslverify * 2023-07-06 <axel.hahn@unibe.ch> add flag userpwd + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkHttpContent extends appmonitorcheck{ +class checkHttpContent extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * It is a "service" icon or "deny" for expected failures + * + * @param array $aParams with optional 'status' containing http response code + * @return string */ - public function getGroup($aParams){ - $sReturn='service'; - if(isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500 ){ - $sReturn='deny'; + public function getGroup(array $aParams=[]): string + { + $sReturn = 'service'; + if (isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500) { + $sReturn = 'deny'; } return $sReturn; } /** - * make http request and test response body + * Make http request and test response header + body * @param array $aParams * [ * url string url to fetch @@ -46,16 +51,20 @@ class checkHttpContent extends appmonitorcheck{ * headeronly boolean optional flag to fetch http response herader only; default: false = returns header and body * follow boolean optional flag to follow a location; default: false = do not follow * sslverify boolean flag: enable/ disable verification of ssl certificate; default: true (verification is on) + * * status integer test for an expected http status code; if none is given then test fails on status 400 and greater + * * headercontains string test for a string in the http response header; it returns OK if the text was found * headernotcontains string test for a string in the http response header; it returns OK if the text was not found * headerregex string test for a regex in the http response header; it returns OK if the regex matches; example: "headerregex"=>"/lowercasematch/i" + * * bodycontains string test for a string in the http response body; it returns OK if the text was found * bodynotcontains string test for a string in the http response body; it returns OK if the text was not found * bodyregex string test for a regex in the http response body; it returns OK if the regex matches; example: "headerregex"=>"/lowercasematch/i" * ] */ - public function run($aParams) { + public function run(array $aParams) + { $this->_checkArrayKeys($aParams, "url"); if (!function_exists("curl_init")) { header('HTTP/1.0 503 Service Unavailable'); @@ -63,30 +72,31 @@ class checkHttpContent extends appmonitorcheck{ } $bShowContent = (isset($aParams["content"]) && $aParams["content"]) ? true : false; $ch = curl_init($aParams["url"]); - + curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_NOBODY, isset($aParams["headeronly"]) && $aParams["headeronly"]); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, isset($aParams["follow"]) && $aParams["follow"]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, isset($aParams["sslverify"]) ? !!$aParams["sslverify"] : 1); - curl_setopt($ch, CURLOPT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp); - if (isset($aParams["userpwd"])){ + curl_setopt($ch, CURLOPT_TIMEOUT, (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp); + if (isset($aParams["userpwd"])) { curl_setopt($ch, CURLOPT_USERPWD, $aParams["userpwd"]); } $res = curl_exec($ch); if (!$res) { - $iErrorCode=curl_errno($ch); - $sErrorMsg=curl_error($ch); + $iErrorCode = curl_errno($ch); + $sErrorMsg = curl_error($ch); curl_close($ch); return [ - RESULT_ERROR, 'ERROR: failed to fetch ' . $aParams["url"] . ' - curl error #'.$iErrorCode.': '.$sErrorMsg + RESULT_ERROR, + 'ERROR: failed to fetch ' . $aParams["url"] . ' - curl error #' . $iErrorCode . ': ' . $sErrorMsg ]; - } - $sOut=''; - $bError=false; - + } + $sOut = ''; + $bError = false; + $aInfos = curl_getinfo($ch); /* Array @@ -122,112 +132,110 @@ class checkHttpContent extends appmonitorcheck{ [local_port] => 63597 ) */ - + curl_close($ch); - - $aTmp=explode("\r\n\r\n", $res, 2); - $sHttpHeader=$aTmp[0]; - $sHttpBody=isset($aTmp[1]) ? $aTmp[1] : false; - + + $aTmp = explode("\r\n\r\n", $res, 2); + $sHttpHeader = $aTmp[0]; + $sHttpBody = $aTmp[1] ?? false; + // ---------- check functions - + // --- http status code - $sOut.="Http status: ".$aInfos['http_code']." - "; - if(isset($aParams["status"])){ - if($aInfos['http_code'] === $aParams["status"]){ - $sOut.="compare OK<br>"; + $sOut .= "Http status: " . $aInfos['http_code'] . " - "; + if (isset($aParams["status"])) { + if ($aInfos['http_code'] === $aParams["status"]) { + $sOut .= "compare OK<br>"; } else { - $sOut.="compare failed<br>"; - $bError=true; + $sOut .= "compare failed<br>"; + $bError = true; } } else { - if($aInfos['http_code'] >= 400){ - $sOut.="Error page detected<br>"; - $bError=true; + if ($aInfos['http_code'] >= 400) { + $sOut .= "Error page detected<br>"; + $bError = true; } else { - $sOut.="request successful<br>"; + $sOut .= "request successful<br>"; } } // --- http header - if(isset($aParams["headercontains"]) && $aParams["headercontains"]){ - $sOut.="Http header contains "".$aParams["headercontains"]."" - "; - if(!strstr($sHttpHeader, $aParams["headercontains"])===false){ - $sOut.="compare OK<br>"; + if (isset($aParams["headercontains"]) && $aParams["headercontains"]) { + $sOut .= "Http header contains "" . $aParams["headercontains"] . "" - "; + if (!strstr($sHttpHeader, $aParams["headercontains"]) === false) { + $sOut .= "compare OK<br>"; } else { - $sOut.="compare failed<br>"; - $bError=true; + $sOut .= "compare failed<br>"; + $bError = true; } } - if(isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]){ - $sOut.="Http header does not contain "".$aParams["headernotcontains"]."" - "; - if(strstr($sHttpHeader, $aParams["headernotcontains"])===false){ - $sOut.="compare OK<br>"; + if (isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]) { + $sOut .= "Http header does not contain "" . $aParams["headernotcontains"] . "" - "; + if (strstr($sHttpHeader, $aParams["headernotcontains"]) === false) { + $sOut .= "compare OK<br>"; } else { - $sOut.="compare failed<br>"; - $bError=true; + $sOut .= "compare failed<br>"; + $bError = true; } } - if(isset($aParams["headerregex"]) && $aParams["headerregex"]){ - $sOut.="Http header regex test "".$aParams["headerregex"]."" - "; - try{ - $bRegex=preg_match($aParams["headerregex"], $sHttpHeader); - if($bRegex){ - $sOut.="compare OK<br>"; + if (isset($aParams["headerregex"]) && $aParams["headerregex"]) { + $sOut .= "Http header regex test "" . $aParams["headerregex"] . "" - "; + try { + $bRegex = preg_match($aParams["headerregex"], $sHttpHeader); + if ($bRegex) { + $sOut .= "compare OK<br>"; } else { - $sOut.="compare failed<br>"; - $bError=true; + $sOut .= "compare failed<br>"; + $bError = true; } - } - catch(Exception $e){ - $sOut.="Wrong REGEX<br>" . print_r($e, 1).'<br>'; - $bError=true; + } catch (Exception $e) { + $sOut .= "Wrong REGEX<br>" . print_r($e, 1) . '<br>'; + $bError = true; } } // --- http body - if(isset($aParams["bodycontains"]) && $aParams["bodycontains"]){ - $sOut.="Http body contains "".$aParams["bodycontains"]."" - "; - if(!strstr($sHttpBody, $aParams["bodycontains"])===false){ - $sOut.="compare OK<br>"; + if (isset($aParams["bodycontains"]) && $aParams["bodycontains"]) { + $sOut .= "Http body contains "" . $aParams["bodycontains"] . "" - "; + if (!strstr($sHttpBody, $aParams["bodycontains"]) === false) { + $sOut .= "compare OK<br>"; } else { - $sOut.="compare failed<br>"; - $bError=true; + $sOut .= "compare failed<br>"; + $bError = true; } } - if(isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]){ - $sOut.="Http body does not contain "".$aParams["bodynotcontains"]."" - "; - if(strstr($sHttpBody, $aParams["bodynotcontains"])===false){ - $sOut.="compare OK<br>"; + if (isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]) { + $sOut .= "Http body does not contain "" . $aParams["bodynotcontains"] . "" - "; + if (strstr($sHttpBody, $aParams["bodynotcontains"]) === false) { + $sOut .= "compare OK<br>"; } else { - $sOut.="compare failed<br>"; - $bError=true; + $sOut .= "compare failed<br>"; + $bError = true; } } - if(isset($aParams["bodyregex"]) && $aParams["bodyregex"]){ - $sOut.="Http body regex test "".$aParams["bodyregex"]."" - "; - try{ - $bRegex=preg_match($aParams["bodyregex"], $sHttpBody); - if($bRegex){ - $sOut.="compare OK<br>"; + if (isset($aParams["bodyregex"]) && $aParams["bodyregex"]) { + $sOut .= "Http body regex test "" . $aParams["bodyregex"] . "" - "; + try { + $bRegex = preg_match($aParams["bodyregex"], $sHttpBody); + if ($bRegex) { + $sOut .= "compare OK<br>"; } else { - $sOut.="compare failed<br>"; - $bError=true; + $sOut .= "compare failed<br>"; + $bError = true; } - } - catch(Exception $e){ - $sOut.="Wrong REGEX<br>" . print_r($e, 1).'<br>'; - $bError=true; + } catch (Exception $e) { + $sOut .= "Wrong REGEX<br>" . print_r($e, 1) . '<br>'; + $bError = true; } } - + if (!$bError) { return [ - RESULT_OK, - 'OK: http check "' . $aParams["url"] . '".<br>'.$sOut + RESULT_OK, + 'OK: http check "' . $aParams["url"] . '".<br>' . $sOut ]; } else { return [ - RESULT_ERROR, - 'ERROR: http check "' . $aParams["url"] . '".<br>'.$sOut + RESULT_ERROR, + 'ERROR: http check "' . $aParams["url"] . '".<br>' . $sOut ]; } diff --git a/public_html/appmonitor/plugins/checks/loadmeter.php b/public_html/appmonitor/plugins/checks/loadmeter.php index 8bc5daf1..13911af1 100755 --- a/public_html/appmonitor/plugins/checks/loadmeter.php +++ b/public_html/appmonitor/plugins/checks/loadmeter.php @@ -43,40 +43,44 @@ * ____________________________________________________________________________ * * 2019-06-06 <axel.hahn@iml.unibe.ch> + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables + * 2024-07-25 <axel.hahn@unibe.ch> float return with 2 digits behind comma * */ -class checkLoadmeter extends appmonitorcheck{ +class checkLoadmeter extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup(): string + { return 'monitor'; } /** - * detect load of a machine and return a float value + * Detect load of a machine and return a float value * windows part was taken from https://stackoverflow.com/questions/5588616/how-do-you-calculate-server-load-in-php * @return float */ - protected function _getLoad() { - if (function_exists('sys_getloadavg')){ + protected function _getLoad(): float + { + if (function_exists('sys_getloadavg')) { $load = sys_getloadavg(); return $load[0]; } else { // Only MS Windows has not implemented sys_getloadavg // try something else - if(class_exists('COM')){ - $wmi=new COM('WinMgmts:\\\\.'); - $cpus=$wmi->InstancesOf('Win32_Processor'); - $load=0; - if(version_compare('4.50.0', PHP_VERSION) == 1){ - while($cpu = $cpus->Next()){ + if (class_exists('COM')) { + $wmi = new COM('WinMgmts:\\\\.'); + $cpus = $wmi->InstancesOf('Win32_Processor'); + $load = 0; + if (version_compare('4.50.0', PHP_VERSION) == 1) { + while ($cpu = $cpus->Next()) { $load += $cpu->LoadPercentage; } - }else{ - foreach($cpus as $cpu){ + } else { + foreach ($cpus as $cpu) { $load += $cpu->LoadPercentage; } } @@ -87,31 +91,31 @@ class checkLoadmeter extends appmonitorcheck{ } /** - * - * @param array $aParams + * Run the check and get load + * @param array $aParams optional array with keys warning,error * @return array */ - public function run($aParams){ - + public function run(array $aParams): array + { + // --- (1) verify if array key(s) exist: // $this->_checkArrayKeys($aParams, "..."); - // --- (2) do something magic // $fLoad=rand(0, 1.3); // $fLoad=$this->_getServerLoad(); - $fLoad=$this->_getLoad(); - + $fLoad = $this->_getLoad(); + // set result code - if($fLoad===false){ - $iResult=RESULT_UNKNOWN; + if ($fLoad === false) { + $iResult = RESULT_UNKNOWN; } else { - $iResult=RESULT_OK; - if(isset($aParams['warning']) && $aParams['warning'] && $fLoad>$aParams['warning']){ - $iResult=RESULT_WARNING; + $iResult = RESULT_OK; + if (isset($aParams['warning']) && $aParams['warning'] && $fLoad > $aParams['warning']) { + $iResult = RESULT_WARNING; } - if(isset($aParams['error']) && $aParams['error'] && $fLoad>$aParams['error']){ - $iResult=RESULT_ERROR; + if (isset($aParams['error']) && $aParams['error'] && $fLoad > $aParams['error']) { + $iResult = RESULT_ERROR; } } @@ -128,14 +132,14 @@ class checkLoadmeter extends appmonitorcheck{ // visual => {string} one of bar|line|simple (+params) // return [ - $iResult, - ($fLoad===false ? 'load value is not available' : 'current load is: '.$fLoad), - ($fLoad===false + $iResult, + ($fLoad === false ? 'load value is not available' : 'current load is: ' . round($fLoad, 2)), + ($fLoad === false ? [] : [ - 'type'=>'counter', - 'count'=>$fLoad, - 'visual'=>'line', + 'type' => 'counter', + 'count' => round($fLoad, 2), + 'visual' => 'line', ] ) ] diff --git a/public_html/appmonitor/plugins/checks/mysqlconnect.php b/public_html/appmonitor/plugins/checks/mysqlconnect.php index 71342456..d259ec00 100755 --- a/public_html/appmonitor/plugins/checks/mysqlconnect.php +++ b/public_html/appmonitor/plugins/checks/mysqlconnect.php @@ -18,19 +18,22 @@ * ____________________________________________________________________________ * * 2021-10-27 <axel.hahn@iml.unibe.ch> + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkMysqlConnect extends appmonitorcheck{ +class checkMysqlConnect extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup(): string + { return 'database'; } + /** - * check mysql connection to a database using mysqli realconnect + * Check mysql connection to a database using mysqli realconnect * @param array $aParams * [ * server string database hostname / ip address @@ -40,29 +43,30 @@ class checkMysqlConnect extends appmonitorcheck{ * port integer optional: port * timeout integer optional timeout in sec; default: 5 * ] + * @return array */ - public function run($aParams) { + public function run(array $aParams): array + { $this->_checkArrayKeys($aParams, "server,user,password,db"); - $mysqli=mysqli_init(); - if(!$mysqli){ + $mysqli = mysqli_init(); + if (!$mysqli) { return [RESULT_ERROR, 'ERROR: mysqli_init failed.']; } - if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp)) { + if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp)) { return [RESULT_ERROR, 'ERROR: setting mysqli_options failed.']; } - $db = (isset($aParams["port"]) && $aParams["port"]) - ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"]) - : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"]) - ; + $db = (isset($aParams["port"]) && $aParams["port"]) + ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"]) + : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"]) + ; if ($db) { $mysqli->close(); return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"]; - return true; } else { return [ - RESULT_ERROR, - "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error ".mysqli_connect_errno() .": ". mysqli_connect_error() + RESULT_ERROR, + "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error " . mysqli_connect_errno() . ": " . mysqli_connect_error() ]; } } diff --git a/public_html/appmonitor/plugins/checks/pdoconnect.php b/public_html/appmonitor/plugins/checks/pdoconnect.php index 7f8016b8..d95b3d55 100755 --- a/public_html/appmonitor/plugins/checks/pdoconnect.php +++ b/public_html/appmonitor/plugins/checks/pdoconnect.php @@ -18,19 +18,21 @@ * ____________________________________________________________________________ * * 2021-10-27 <axel.hahn@iml.unibe.ch> + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkPdoConnect extends appmonitorcheck{ +class checkPdoConnect extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup(): string + { return 'database'; } /** - * check connection to a database using pdo + * Check connection to a database using pdo * see http://php.net/manual/en/pdo.drivers.php * * @param array $aParams @@ -40,30 +42,32 @@ class checkPdoConnect extends appmonitorcheck{ * password string password for db user * timeout integer optional timeout in sec; default: 5 * ] + * @return array */ - public function run($aParams) { + public function run(array $aParams): array + { $this->_checkArrayKeys($aParams, "connect,user,password"); - try{ + try { $db = new PDO( - $aParams['connect'], - $aParams['user'], - $aParams['password'], + $aParams['connect'], + $aParams['user'], + $aParams['password'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - + // timeout // Not all drivers support this option; mysqli does - PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, + PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, // mssql // PDO::SQLSRV_ATTR_QUERY_TIMEOUT => $this->_iTimeoutTcp, ] ); - $db=null; + $db = null; return [RESULT_OK, "OK: Database was connected with PDO " . $aParams['connect']]; - } catch(PDOException $e) { - return [RESULT_ERROR, "ERROR: Database was not connected " . $aParams['connect'] . " was not connected. Error ".$e->getMessage()]; + } catch (PDOException $e) { + return [RESULT_ERROR, "ERROR: Database was not connected " . $aParams['connect'] . " was not connected. Error " . $e->getMessage()]; } } - + } diff --git a/public_html/appmonitor/plugins/checks/phpmodules.php b/public_html/appmonitor/plugins/checks/phpmodules.php index 29e2874b..b4b8170a 100644 --- a/public_html/appmonitor/plugins/checks/phpmodules.php +++ b/public_html/appmonitor/plugins/checks/phpmodules.php @@ -18,71 +18,74 @@ * ____________________________________________________________________________ * * 2022-05-06 <axel.hahn@iml.unibe.ch> first lines + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkPhpmodules extends appmonitorcheck{ +class checkPhpmodules extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup(): string + { return 'service'; } /** - * check if system is listening to a given port + * Check if system is listening to a given port * @param array $aParams * [ * required array list of required php modules * optional array optional: list of optional php modules * ] - * @return boolean + * @return array */ - public function run($aParams) { - $sOut=''; - $bHasError=false; - $bHasWarning=false; + public function run(array $aParams): array + { + $sOut = ''; + $bHasError = false; + $bHasWarning = false; // $this->_checkArrayKeys($aParams, "required"); // --- get all modules - $aAllMods=get_loaded_extensions(false); - + $aAllMods = get_loaded_extensions(false); + // --- check required modules - if(isset($aParams['required']) && count($aParams['required'])){ - $sOut.='Required: '; - foreach($aParams['required'] as $sMod){ - $sOut.=$sMod.'='; - if(!array_search($sMod, $aAllMods)===false){ - $sOut.='OK;'; + if (isset($aParams['required']) && count($aParams['required'])) { + $sOut .= 'Required: '; + foreach ($aParams['required'] as $sMod) { + $sOut .= $sMod . '='; + if (!array_search($sMod, $aAllMods) === false) { + $sOut .= 'OK;'; } else { - $bHasError=true; - $sOut.='MISS;'; + $bHasError = true; + $sOut .= 'MISS;'; } } } // --- check optional modules - if(isset($aParams['optional']) && count($aParams['optional'])){ - $sOut.=($sOut ? '|' : '') . 'Optional: '; - foreach($aParams['optional'] as $sMod){ - $sOut.=$sMod.'='; - if(!array_search($sMod, $aAllMods)===false){ - $sOut.='OK;'; + if (isset($aParams['optional']) && count($aParams['optional'])) { + $sOut .= ($sOut ? '|' : '') . 'Optional: '; + foreach ($aParams['optional'] as $sMod) { + $sOut .= $sMod . '='; + if (!array_search($sMod, $aAllMods) === false) { + $sOut .= 'OK;'; } else { - $bHasWarning=true; - $sOut.='MISS;'; + $bHasWarning = true; + $sOut .= 'MISS;'; } } } // --- return result - if($bHasError){ + if ($bHasError) { return [RESULT_ERROR, "ERROR: " . $sOut]; } - if($bHasWarning){ + if ($bHasWarning) { return [RESULT_WARNING, "WARNING: " . $sOut]; } return [RESULT_OK, "OK: " . $sOut]; } - + } diff --git a/public_html/appmonitor/plugins/checks/ping.php b/public_html/appmonitor/plugins/checks/ping.php index fdb5910f..3056314c 100644 --- a/public_html/appmonitor/plugins/checks/ping.php +++ b/public_html/appmonitor/plugins/checks/ping.php @@ -20,40 +20,43 @@ * 2022-07-05 <axel.hahn@iml.unibe.ch> * 2022-09-16 <axel.hahn@iml.unibe.ch> read error before closing socket. * 2022-11-22 <axel.hahn@iml.unibe.ch> Use exec with detecting MS Win for the ping parameter for count of pings + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables */ -class checkPing extends appmonitorcheck{ +class checkPing extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup(): string + { return 'network'; } /** - * check ping to a target + * Check ping to a target * @param array $aParams * [ * host string optional hostname to connect; default: 127.0.0.1 * timeout integer OBSOLET (because using exec): optional timeout in sec; default: 5 * ] - * @return boolean + * @return array */ - public function run($aParams) { + public function run(array $aParams): array + { $sHost = $aParams['host'] ?? '127.0.0.1'; - $sParamCount=strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? "n" : "c"; - $iRepeat=1; - - $sCommand="ping -$sParamCount $iRepeat $sHost 2>&1"; + $sParamCount = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? "n" : "c"; + $iRepeat = 1; + + $sCommand = "ping -$sParamCount $iRepeat $sHost 2>&1"; exec($sCommand, $aOut, $iRc); - $sOut=implode("\n", $aOut); + $sOut = implode("\n", $aOut); - if ($iRc>0){ - return [RESULT_ERROR, "ERROR: ping to $sHost failed.\n".$sOut]; + if ($iRc > 0) { + return [RESULT_ERROR, "ERROR: ping to $sHost failed.\n" . $sOut]; } - return [RESULT_OK, "OK: ping to $sHost\n".$sOut]; + return [RESULT_OK, "OK: ping to $sHost\n" . $sOut]; /* Socket functions require root :-/ diff --git a/public_html/appmonitor/plugins/checks/porttcp.php b/public_html/appmonitor/plugins/checks/porttcp.php index b9cee468..bd47ce5f 100755 --- a/public_html/appmonitor/plugins/checks/porttcp.php +++ b/public_html/appmonitor/plugins/checks/porttcp.php @@ -21,36 +21,39 @@ * 2022-07-05 <axel.hahn@iml.unibe.ch> send unknown if socket module is not activated. * 2022-09-16 <axel.hahn@iml.unibe.ch> read error before closing socket. * 2022-12-05 <axel.hahn@unibe.ch> add @ sign at socket functions to prevent warning + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkPortTcp extends appmonitorcheck{ +class checkPortTcp extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup(): string + { return 'network'; } /** - * check if system is listening to a given port + * Check if system is listening to a given port * @param array $aParams * [ * port integer port * host string optional hostname to connect; default: 127.0.0.1 * timeout integer optional timeout in sec; default: 5 * ] - * @return boolean + * @return array */ - public function run($aParams) { + public function run(array $aParams): array + { $this->_checkArrayKeys($aParams, "port"); $sHost = $aParams['host'] ?? '127.0.0.1'; $iPort = (int) $aParams['port']; - if (!function_exists('socket_create')){ - return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform tcp test. The socket module is not enabled in the php installation."]; + if (!function_exists('socket_create')) { + return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform tcp test. The php-sockets module is not enabled in the php installation."]; } // from http://php.net/manual/de/sockets.examples.php @@ -65,14 +68,14 @@ class checkPortTcp extends appmonitorcheck{ SOL_SOCKET, // socket level SO_SNDTIMEO, // timeout option [ - "sec"=>(isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, // timeout in seconds - "usec"=>0 + "sec" => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, // timeout in seconds + "usec" => 0 ] ); $result = @socket_connect($socket, $sHost, $iPort); if ($result === false) { - $aResult=[RESULT_ERROR, "ERROR: $sHost:$iPort failed. " . socket_strerror(socket_last_error($socket))]; + $aResult = [RESULT_ERROR, "ERROR: $sHost:$iPort failed. " . socket_strerror(socket_last_error($socket))]; socket_close($socket); return $aResult; } else { @@ -80,5 +83,5 @@ class checkPortTcp extends appmonitorcheck{ return [RESULT_OK, "OK: $sHost:$iPort was connected."]; } } - + } diff --git a/public_html/appmonitor/plugins/checks/simple.php b/public_html/appmonitor/plugins/checks/simple.php index daacf0a3..3af69b69 100755 --- a/public_html/appmonitor/plugins/checks/simple.php +++ b/public_html/appmonitor/plugins/checks/simple.php @@ -18,12 +18,14 @@ * ____________________________________________________________________________ * * 2021-10-27 <axel.hahn@iml.unibe.ch> + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkSimple extends appmonitorcheck{ - +class checkSimple extends appmonitorcheck +{ + /** - * most simple check: set given values + * Most simple check: set given values * Use this function to add a counter * * @param array $aParams @@ -37,15 +39,16 @@ class checkSimple extends appmonitorcheck{ * - label string a label * - value float a number * - type string one of simple | bar | line - * + * @return array */ - public function run($aParams) { + public function run(array $aParams): array + { $this->_checkArrayKeys($aParams, "result,value"); // $this->_setReturn((int) $aParams["result"], $aParams["value"]); - $aData=[]; - foreach([ 'type', 'count', 'visual' ] as $sMyKey){ - if(isset($aParams[$sMyKey])){ - $aData[$sMyKey]=$aParams[$sMyKey]; + $aData = []; + foreach (['type', 'count', 'visual'] as $sMyKey) { + if (isset($aParams[$sMyKey])) { + $aData[$sMyKey] = $aParams[$sMyKey]; } } return [ diff --git a/public_html/appmonitor/plugins/checks/sqliteconnect.php b/public_html/appmonitor/plugins/checks/sqliteconnect.php index 1fc264c5..703db60d 100755 --- a/public_html/appmonitor/plugins/checks/sqliteconnect.php +++ b/public_html/appmonitor/plugins/checks/sqliteconnect.php @@ -18,15 +18,17 @@ * ____________________________________________________________________________ * * 2021-10-27 <axel.hahn@iml.unibe.ch> + * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * */ -class checkSqliteConnect extends appmonitorcheck{ +class checkSqliteConnect extends appmonitorcheck +{ /** - * get default group of this check - * @param array $aParams - * @return array + * Get default group of this check + * @return string */ - public function getGroup(){ + public function getGroup() + { return 'database'; } @@ -37,33 +39,44 @@ class checkSqliteConnect extends appmonitorcheck{ * db string full path of sqlite file * timeout integer optional timeout in sec; default: 5 * ] - * @return boolean + * @return array */ - public function run($aParams) { + public function run($aParams): array + { $this->_checkArrayKeys($aParams, "db"); if (!file_exists($aParams["db"])) { - return [RESULT_ERROR, "ERROR: Sqlite database file " . $aParams["db"] . " does not exist."]; + return [ + RESULT_ERROR, + "ERROR: Sqlite database file " . $aParams["db"] . " does not exist." + ]; } - if(!isset($aParams['user'])){ - $aParams['user']=''; + if (!isset($aParams['user'])) { + $aParams['user'] = ''; } - if(!isset($aParams['password'])){ - $aParams['password']=''; + if (!isset($aParams['password'])) { + $aParams['password'] = ''; } try { // $db = new SQLite3($sqliteDB); // $db = new PDO("sqlite:".$sqliteDB); - $o = new PDO("sqlite:" . $aParams["db"], - $aParams['user'], - $aParams['password'], + $o = new PDO( + "sqlite:" . $aParams["db"], + $aParams['user'], + $aParams['password'], [ - PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp, + PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int) $aParams["timeout"]) ? (int) $aParams["timeout"] : $this->_iTimeoutTcp, ] ); - return [RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected"]; + return [ + RESULT_OK, + "OK: Sqlite database " . $aParams["db"] . " was connected" + ]; } catch (Exception $e) { - return [RESULT_ERROR, "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . $e->getMessage()]; + return [ + RESULT_ERROR, + "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . $e->getMessage() + ]; } } - + } -- GitLab