diff --git a/public_html/appmonitor/classes/appmonitor-checks.class.php b/public_html/appmonitor/classes/appmonitor-checks.class.php index 865a0e8e041ca1cc0f46d13c2db5aa8d4a8f2d37..4aa9deead4e675692bad5b0cf571197e798d7094 100755 --- a/public_html/appmonitor/classes/appmonitor-checks.class.php +++ b/public_html/appmonitor/classes/appmonitor-checks.class.php @@ -1,4 +1,5 @@ <?php +require_once 'validateparam.class.php'; if (!defined('RESULT_OK')) { define("RESULT_OK", 0); @@ -36,24 +37,28 @@ if (!defined('RESULT_OK')) { * --------------------------------------------------------------------------------<br> * <br> * --- HISTORY:<br> - * 2014-10-24 0.5 axel.hahn@iml.unibe.ch<br> - * 2015-04-08 0.9 axel.hahn@iml.unibe.ch added sochket test: checkPortTcp<br> - * 2018-06-29 0.24 axel.hahn@iml.unibe.ch add file and directory checks<br> - * 2018-07-17 0.42 axel.hahn@iml.unibe.ch add port on mysqli check<br> - * 2018-07-26 0.46 axel.hahn@iml.unibe.ch fix mysql connection check with empty port param<br> - * 2018-08-14 0.47 axel.hahn@iml.unibe.ch appmonitor client: use timeout of 5 sec for tcp socket connections<br> - * 2018-08-15 0.49 axel.hahn@iml.unibe.ch cert check: added flag to skip verification<br> - * 2018-08-23 0.50 axel.hahn@iml.unibe.ch replace mysqli connect with mysqli real connect (to use a timeout)<br> - * 2018-08-27 0.52 axel.hahn@iml.unibe.ch add pdo connect (starting with mysql)<br> - * 2018-11-05 0.58 axel.hahn@iml.unibe.ch additional flag in http check to show content<br> - * 2019-05-31 0.87 axel.hahn@iml.unibe.ch add timeout as param in connective checks (http, tcp, databases)<br> - * 2019-06-05 0.88 axel.hahn@iml.unibe.ch add plugins<br> - * 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 + * 2014-10-24 0.5 axel.hahn@iml.unibe.ch<br> + * 2015-04-08 0.9 axel.hahn@iml.unibe.ch added sochket test: checkPortTcp<br> + * 2018-06-29 0.24 axel.hahn@iml.unibe.ch add file and directory checks<br> + * 2018-07-17 0.42 axel.hahn@iml.unibe.ch add port on mysqli check<br> + * 2018-07-26 0.46 axel.hahn@iml.unibe.ch fix mysql connection check with empty port param<br> + * 2018-08-14 0.47 axel.hahn@iml.unibe.ch appmonitor client: use timeout of 5 sec for tcp socket connections<br> + * 2018-08-15 0.49 axel.hahn@iml.unibe.ch cert check: added flag to skip verification<br> + * 2018-08-23 0.50 axel.hahn@iml.unibe.ch replace mysqli connect with mysqli real connect (to use a timeout)<br> + * 2018-08-27 0.52 axel.hahn@iml.unibe.ch add pdo connect (starting with mysql)<br> + * 2018-11-05 0.58 axel.hahn@iml.unibe.ch additional flag in http check to show content<br> + * 2019-05-31 0.87 axel.hahn@iml.unibe.ch add timeout as param in connective checks (http, tcp, databases)<br> + * 2019-06-05 0.88 axel.hahn@iml.unibe.ch add plugins<br> + * 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 + * 2025-02-28 0.152 axel.hahn@unibe.ch listChecks: add loop over currently loaded classes + * 2025-03-03 0.153 axel.hahn@unibe.ch getSize() preg_replace did not work in compiled binary + * 2025-03-04 0.154 axel.hahn@unibe.ch finish with existcode instead of die() + * 2025-03-18 0.156 axel.hahn@unibe.ch add validation class * --------------------------------------------------------------------------------<br> - * @version 0.137 + * @version 0.156-dev * @author Axel Hahn * @link TODO * @license GPL @@ -66,6 +71,47 @@ class appmonitorcheck // CONFIG // ---------------------------------------------------------------------- + protected array $_aCheckDocs = [ + 'name' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'Name of a check', + 'regex'=>'/./', + 'example' => 'configfile', + ], + 'description' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'A short description to describe what is tested', + 'regex'=>'/./', + 'example' => 'Check if config file inc_config.php exists, is readable and writable', + ], + 'check' => [ + 'type' => 'array', + 'required' => true, + 'description' => 'Array of the check', + ], + 'parent' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Reference a \'name\' of another check to generate a dependency tree', + 'regex'=>'/./', + ], + 'group' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Name of a group to be nedsted in', + 'regex'=>'/./', + ], + 'worstresult' => [ + 'type' => 'int', + 'required' => false, + 'description' => 'A failed check is max counted as given result. Use it on not required but optional checks', + 'min'=>RESULT_OK, + 'max'=>RESULT_ERROR, + ], + ]; + /** * starting time using microtime * @var float @@ -118,6 +164,25 @@ class appmonitorcheck // PRIVATE FUNCTIONS // ---------------------------------------------------------------------- + /** + * Exit execution with error message and given exicode. + * @param int $iHttpcode http statuscode + * @param string $sMessage detailed message + * @param int $iExitcode exitcode + * @return never + */ + protected function _exit($iHttpcode, $sMessage, $iExitcode): never + { + $aStatus=[ + 503 => 'Service Unavailable', + ]; + header("HTTP/1.1 $iHttpcode $aStatus[$iHttpcode]"); + echo "<h1>$iHttpcode $aStatus[$iHttpcode]</h1> + <h2>Details</h2> + $sMessage\n"; + exit($iExitcode); + } + /** * Internal: create basic array values for metadata * @return boolean @@ -195,36 +260,52 @@ class appmonitorcheck /** * 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 + * @param string $sMustKeys key or keys as comma seprated list * @return boolean */ - protected function _checkArrayKeys($aConfig, $sKeyList) + protected function _checkArrayKeys($aConfig, $sMustKeys, $sOptionalKeys = '') { - foreach (explode(",", $sKeyList) as $sKey) { + $aTmp=$aConfig; + foreach (explode(",", $sMustKeys) 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>' + $this->_exit( + 503, + __METHOD__ . " - array of check parameters requires the keys [$sMustKeys] - but key <code>$sKey</code> was not found in config array." + . "<pre>" . print_r($aConfig, true) . '</pre>', + 20 ); } 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>' + $this->_exit( + 503, + __METHOD__ . " - key <code>$sKey</code> is empty in config array" + . "<pre>" . print_r($aConfig, true) . '</pre>', + 21 ); } + unset($aTmp[$sKey]); } + return true; } + + // ---------------------------------------------------------------------- // PUBLIC FUNCTIONS // ---------------------------------------------------------------------- + /** + * Self documentation of a check. The array is defined in + * plugins/checks/*.php files + * + * @return array + */ + public function explain(): array + { + return $this->_aDoc??[]; + } + /** * Perform a check * @param array $aConfig configuration array for a check, eg. @@ -237,6 +318,9 @@ class appmonitorcheck * [params] => [array] // optional; arguments for Check function * // its keys depend on the function * ] + * [group] => Group A + * [parent] => field "name" of another check + * [worstresult] => RESULT_WARNING * ] * </code> * @return array @@ -244,8 +328,24 @@ class appmonitorcheck public function makeCheck(array $aConfig): array { $this->_iStart = microtime(true); - $this->_checkArrayKeys($aConfig, "name,description,check"); - $this->_checkArrayKeys($aConfig["check"], "function"); + + $oVal=new validateparam(); + $aErrors=$oVal->validateArray($this->_aCheckDocs, $aConfig); + if(count($aErrors)){ + $this->_exit( + 503, + __METHOD__ . " - validation of params failed\n" + . "<pre>Errors: " + . print_r($aErrors, true) + . "Input array was: " + . print_r($aConfig, true) + . '</pre>', + 22 + ); + } + + $this->_checkArrayKeys($aConfig, "name,description,check", "group,parent,worstresult", true); + $this->_checkArrayKeys($aConfig["check"], "function", "params", true); $this->_aConfig = $aConfig; $this->_createDefaultMetadata(); @@ -264,33 +364,49 @@ class appmonitorcheck } 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) + $this->_exit( + 503, + __METHOD__ . " - [$aConfig[name]] - check class not found: <code>$sCheckClass</code>" + . "<pre>" . print_r($aConfig, true) . '</pre>', + 22 ); } $oPlugin = new $sCheckClass; + $aCheckDoc=$oPlugin->explain(); + + $aErrors=$oVal->validateArray($oPlugin->explain()['parameters']??[], $aParams); + if(count($aErrors)){ + $this->_exit( + 503, + __METHOD__ . " - [$aConfig[name]] - validation of check -> params failed\n" + . "<pre>Errors: " + . print_r($aErrors, true) + . "Input array: " + . print_r($aConfig, true) + . '</pre>', + 22 + ); + } + // die(__FILE__.":".__LINE__); + $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" + $this->_exit( + 503, + __METHOD__ . " - plugin : $sCheck does not responses an array" . "<pre>INPUT " . print_r($aConfig, true) . '</pre>' - . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>' + . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>', + 23 ); } 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>' + $this->_exit( + 503, + __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>', + 24 ); } if (!isset($aResponse[2]) || !$aResponse[2]) { @@ -315,6 +431,8 @@ class appmonitorcheck public function listChecks(): array { $aReturn = []; + + // **DEPRECATED** // return internal protected fuctions named "check[whatever]" $class = new ReflectionClass($this); foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) { @@ -326,6 +444,15 @@ class appmonitorcheck foreach (glob($this->_sPluginDir . '/checks/*.php') as $sPluginFile) { $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1; } + + // from currently loaded classes + foreach(get_declared_classes() as $sClass){ + if (strpos($sClass, "check") === 0) { + $aReturn[str_replace('check','',$sClass)] = 1; + } + } + + array_unique($aReturn); ksort($aReturn); return array_keys($aReturn); } @@ -406,17 +533,18 @@ class appmonitorcheck $power = 0; foreach ($this->_units as $sUnit) { if (preg_match('/^[0-9\.\ ]*' . $sUnit . '/', $sValue)) { - $i = preg_replace('/([0-9\.]*).*/', '$1', $sValue); + // $i = preg_replace('/([0-9\.]*).*/', '$1', $sValue); + $i = str_replace($sUnit, '', $sValue); $iReal = $i * pow(1024, $power); // die("FOUND: $sValue with unit ${sUnit} - 1024^$power * $i = $iReal"); 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) + $this->_exit( + 503, + __METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units), + 25 ); } diff --git a/public_html/appmonitor/classes/appmonitor-client.class.php b/public_html/appmonitor/classes/appmonitor-client.class.php index 66def5a5d02705512751a93982d325823d7a35f3..ae2d3003900b206f50cf2187c23dee97bd6b90fa 100755 --- a/public_html/appmonitor/classes/appmonitor-client.class.php +++ b/public_html/appmonitor/classes/appmonitor-client.class.php @@ -44,8 +44,12 @@ if (!class_exists('appmonitorcheck')) { * 2024-07-19 0.137 axel.hahn@unibe.ch php 8 only: use typed variables * 2024-11-22 0.141 axel.hahn@unibe.ch Set client version to server version after updating http, mysqli and app checks * 2025-01-02 0.149 axel.hahn@unibe.ch add getChecks method + * 2025-03-03 0.153 axel.hahn@unibe.ch fix client checks during development of a compiled binary + * 2025-03-04 0.154 axel.hahn@unibe.ch finish with exitcode instead of die() + * 2025-03-17 0.155 axel.hahn@unibe.ch added: getVersion() and setVersion() + * 2025-03-19 0.156 axel.hahn@unibe.ch added: validation rules for parameters in all checks * --------------------------------------------------------------------------------<br> - * @version 0.149 + * @version 0.155 * @author Axel Hahn * @link TODO * @license GPL @@ -59,7 +63,7 @@ class appmonitor * Name and Version number * @var string */ - protected string $_sVersion = 'php-client-v0.149'; + protected string $_sVersion = '0.156'; /** * config: default ttl for server before requesting the client check again @@ -119,13 +123,14 @@ class appmonitor "ttl" => false, "result" => false, "time" => false, - "version" => $this->_sVersion, + "version" => false, ]; // fill with default values $this->setHost(); $this->setWebsite(); $this->setTTL(); + $this->setVersion(); return true; } @@ -151,28 +156,19 @@ class appmonitor $this->_aMeta["host"] = $s; return true; } - /** - * 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... - * then you should the path or any description to identify them too - * - * if no argument is given the name of HTTP_HOST will be used + * Set final result in meta data; if no value was given then it + * sets the biggest value of any check. * - * @param string $sWebsite Name of the website or web application + * @param integer $iResult set resultcode; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN * @return boolean */ - public function setWebsite($sWebsite = ''): bool + public function setResult(int $iResult = -1): bool { - if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) { - $sWebsite = $_SERVER["HTTP_HOST"]; - } - if (!$sWebsite) { - return false; + if ($iResult === -1) { + $iResult = $this->_iMaxResult; // see addCheck() } - $this->_aMeta["website"] = $sWebsite; + $this->_aMeta["result"] = $iResult; return true; } @@ -192,18 +188,38 @@ class appmonitor } /** - * Set final result in meta data; if no value was given then it - * sets the biggest value of any check. + * Set a prefix for meta -> version ... before "-php-client-v<VERSION>" + * @param string $sVersionPrefix new prefix + * @return bool + */ + public function setVersion($sVersionPrefix = ""): bool + { + $this->_aMeta["version"] = ($sVersionPrefix ? "$sVersionPrefix-" : "") + . "php-client-v$this->_sVersion" + ; + return true; + } + /** + * Set a name for this website or application and its environment + * (dev, test, prod); * - * @param integer $iResult set resultcode; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN + * If you have several application in subdirectories, i.e. /blog, /shop... + * then you should the path or any description to identify them too + * + * if no argument is given the name of HTTP_HOST will be used + * + * @param string $sWebsite Name of the website or web application * @return boolean */ - public function setResult(int $iResult = -1): bool + public function setWebsite($sWebsite = ''): bool { - if ($iResult === -1) { - $iResult = $this->_iMaxResult; // see addCheck() + if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) { + $sWebsite = $_SERVER["HTTP_HOST"]; } - $this->_aMeta["result"] = $iResult; + if (!$sWebsite) { + return false; + } + $this->_aMeta["website"] = $sWebsite; return true; } @@ -318,8 +334,11 @@ class appmonitor return true; } } - header('HTTP/1.0 403 Forbidden'); - die('ERROR: Your ip address [' . $sIP . '] has no access.'); + $this->_exit( + 403, + 'ERROR: Your ip address [' . $sIP . '] has no access.', + 11 + ); } /** @@ -339,14 +358,26 @@ class appmonitor if (isset($_GET[$sVarname]) && $_GET[$sVarname] === $sToken) { return true; } - header('HTTP/1.0 403 Forbidden'); - die('ERROR: A token is required.'); + $this->_exit( + 403, + 'ERROR: A token is required.', + 12 + ); } // ---------------------------------------------------------------------- // getter // ---------------------------------------------------------------------- + /** + * Get version of the appmoinitor php client + * @return string + */ + public function getVersion(): string + { + return $this->_sVersion; + } + /** * list all available check functions. This is a helper class you cann call * to get an overview over built in functions. You get a flat array with @@ -381,9 +412,11 @@ class appmonitor } 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>' - // .'Dump of your data so far:<pre>' . json_encode($this->getResults(), JSON_PRETTY_PRINT) . '</pre><hr>' + $this->_exit( + 503, + 'Client check is not complete<br>Found errors:<br>' + . '<ol><li>' . implode('<li>', $aErrors) . '</ol><br><br>', + 10 ); } return true; @@ -394,14 +427,24 @@ class appmonitor // ---------------------------------------------------------------------- /** - * Stop processing the client checks and abort with an error - * @param string $sMessage text to show after a 503 headline - * @return void + * Exit execution with error message and given exicode. + * @param int $iHttpcode http statuscode + * @param string $sMessage detailed message + * @param int $iExitcode exitcode + * @return never */ - public function abort(string $sMessage): void + protected function _exit($iHttpcode, $sMessage, $iExitcode): never { - header('HTTP/1.0 503 Service Unavailable'); - die('<h1>503 Service Unavailable</h1>' . $sMessage); + + $aStatus = [ + 403 => 'Forbidden', + 503 => 'Service Unavailable', + ]; + header("HTTP/1.1 $iHttpcode $aStatus[$iHttpcode]"); + echo "<h1>$iHttpcode $aStatus[$iHttpcode]</h1> + <h2>Details</h2> + $sMessage\n"; + exit($iExitcode); } /** @@ -433,7 +476,7 @@ class appmonitor { $this->_checkData(); $this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms'; - $sOut=json_encode($this->getResults()); + $sOut = json_encode($this->getResults()); header('Content-type: application/json'); header('Cache-Control: cache'); @@ -508,7 +551,7 @@ class appmonitor $sOut .= '<span class="result' . $i . '">' . $sText . '</span> '; } - $sRaw=json_encode($aData, JSON_PRETTY_PRINT); + $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); diff --git a/public_html/appmonitor/classes/validateparam.class.php b/public_html/appmonitor/classes/validateparam.class.php new file mode 100644 index 0000000000000000000000000000000000000000..dfdf41d4667b8aa2698d5ba07fa0620f9a84c0af --- /dev/null +++ b/public_html/appmonitor/classes/validateparam.class.php @@ -0,0 +1,203 @@ +<?php +class validateparam +{ + + /** + * Summary of _aValidationDefs + * @var array + */ + protected array $_aValidationDefs = []; + + protected bool $_flag_debug = false; + + + /** + * Write debug line if _flag_debug is set to true + * @param string $s message to show + * @return void + */ + protected function _wd(string $s) + { + if ($this->_flag_debug) { + echo "DEBUG: $s<br>\n"; + } + } + + /** + * Include the default validation rules defined in validateparam.settings.php + */ + public function __construct(){ + // IMPORTANT: + // This style is not so nice like <variable> = include <file>; + // keep the include line unchanged - it is detected by installer + // CLI client project (amcli) + include 'validateparam.settings.php'; + } + + /** + * shared validaten checks for floor and integer + * + * @param array $aOpt Validation rules + * @param mixed $value value to verify + * @return string with found error + */ + protected function _checkCount(array $aOpt, mixed $value) + { + $sError = ""; + + if ($aOpt['min'] ?? false) { + if ($value < $aOpt['min']) { + $sError .= "Value is too small; minimum is $aOpt[min]. "; + } + } + if ($aOpt['max'] ?? false) { + if ($value > $aOpt['max']) { + $sError .= "Value is too big; maximum is $aOpt[max]. "; + } + } + if (isset($aOpt['oneof']) && is_array($aOpt['oneof'])) { + if (array_search($value, $aOpt['oneof']) == false) { + $sError .= "Value is invalid. Value doesn't match one of these values " . print_r($aOpt['oneof']); + } + } + + return $sError; + } + + /** + * Validate a single value. It returns a string with an errormessage. + * No error means ok. + * + * @param array $aOpt Validation rules + * @param mixed $value value to verify + * @return string with found error + */ + public function validateValue(array $aOpt, mixed $value): string + { + $sError = ''; + + if (isset($aOpt['validate'])){ + + if ($this->_aValidationDefs[$aOpt['validate']] ?? false) { + $this->_wd("adding options ".print_r($this->_aValidationDefs[$aOpt['validate']],true)); + $aOpt = array_merge($aOpt, $this->_aValidationDefs[$aOpt['validate']]); + } else { + $sError .= "Unknown value in 'validate'='$aOpt[validate]'"; + } + } + // check type + if (isset($aOpt['type'])) { + $this->_wd("check type $aOpt[type]"); + switch ($aOpt['type']) { + case 'array': + if (!is_array($value)) { + $sError .= "Value isn't an array"; + } + break; + case 'bool': + if (!is_bool($value)) { + $sError .= "Value '$value' isn't a bool"; + } + break; + + case 'float': + if (!is_float($value) && !is_int($value)) { + $sError .= "Value isn't a float"; + } else { + $sError .= $this->_checkCount($aOpt, $value); + } + break; + + case 'int': + case 'integer': + if (!is_int($value)) { + $sError .= "Value '$value' isn't an integer"; + } else { + $sError .= $this->_checkCount($aOpt, $value); + } + break; + + case 'string': + if (!is_string($value)) { + $sError .= "Value '$value' isn't a string"; + } else { + if ($aOpt['regex'] ?? false) { + if (!preg_match($aOpt['regex'], $value)) { + $sError .= "Value is invalid. Regex doesn't match: $aOpt[regex]"; + } + } + if (isset($aOpt['oneof']) && is_array($aOpt['oneof'])) { + if (array_search($value, $aOpt['oneof']) == false) { + $sError .= "Value is invalid. Value doesn't match one of these values " . print_r($aOpt['oneof']); + } + } + } + break; + + default: + $sError .= "ERROR Cannot validate unknown type: '$aOpt[type]'<br>\n"; + } + } + // if string: verify regex + + return $sError; + } + + /** + * Validate an array of parameter definitions. It returns an array of all error messages + * No error / an empty array means ok. + * + * @param mixed $aDefs array with definitions + * @param mixed $aParams array of given values + * @param mixed $bStrict flag: strict checking to detect wrong keys. + * @return array with errors + */ + public function validateArray(array $aDefs, array $aParams, bool $bStrict = true) + { + $aErrors = []; + + // echo "<pre>"; + // echo "aDefs = "; print_r($aDefs); + // echo "aParams = "; print_r($aParams); + // echo "<hr>"; + if (!count($aDefs)) { + return ['Defs' => 'No validation rules given.']; + } + + $aTmp = $aParams; + foreach ($aDefs as $sKey => $aOpt) { + unset($aTmp[$sKey]); + if ($aOpt['required'] ?? false) { + + $this->_wd("Check MUST $sKey"); + // verify MUST field + if (!isset($aParams[$sKey])) { + $aErrors[$sKey] = "Missing required key '$sKey'"; + } else { + $this->_wd("MUST field exists: $sKey"); + $sError = $this->validateValue($aOpt, $aParams[$sKey]); + if ($sError) { + $aErrors[$sKey] = $sError; + } else { + $this->_wd("$sKey was successfully validated.<hr>"); + } + + } + } + if (isset($aParams[$sKey])) { + $this->_wd("Check OPTIONAL $sKey"); + $sError = $this->validateValue($aOpt, $aParams[$sKey]); + if ($sError) { + $aErrors[$sKey] = $sError; + } + } + } + if ($bStrict && isset($aTmp) && count($aTmp)) { + foreach (array_keys($aTmp) as $sKey) { + $aErrors[$sKey] = "Invalid key was found: '$sKey'; allowed keys are '" . implode("', '", array_keys($aDefs ?? [])) . "'"; + } + } + $this->_wd("found errors: " . print_r($aErrors, 1)); + return $aErrors; + } +} \ No newline at end of file diff --git a/public_html/appmonitor/classes/validateparam.class.php.md b/public_html/appmonitor/classes/validateparam.class.php.md new file mode 100644 index 0000000000000000000000000000000000000000..75e98b55ead259b33f96dff816c125dd34494d41 --- /dev/null +++ b/public_html/appmonitor/classes/validateparam.class.php.md @@ -0,0 +1,132 @@ +# validateparam.class.php + +## Description + +This class can validate values and arrays. +You define type and validateion rules. A validation method checks the given value against your rules. It returns the error message(s). No error means, the value is ok. + +## Usage + +```php + +$oVal=new validateparam(); + +// check an array +$aErrors=$oVal->validateArray($aRules, $aGivenValues, $bStrict); +if(count($aErrors)){ + echo "Something is wrong: <pre>".print_r($aErrors, 1)."</pre>"; + exit(1); +} +``` + +## Validate Arrays + +First something visual - an example: + +```txt +Array +( + [name] => Array + ( + [type] => string + [required] => true + [regex] => /./ + ) + + [description] => Array + ( + [type] => string + [required] => true + [regex] => /./ + ) + : + [worstresult] => Array + ( + 'type' => 'int', + 'required' => false, + 'description' => 'A failed check is max counted as given result. Use it on not required but optional checks', + 'min'=>RESULT_OK, + 'max'=>RESULT_ERROR, + ) +) +``` + + +### Type check + +Each key can be marked to be a value of a given type. + +It can be a mandantory value or optional. + +| Name | Type | Description +|-- |-- |-- +| 'type' | {string} | variable type that must match; one of "array", "bool", "count", "float", "int", "integer", "string" +| 'required' | {bool} | define value as required + +Next to these keys per type you can define validation rules in dependency of the type. + +### Validate numbers + +This section describes values of the `type` + +* int|integer - integer values +* float - float values (or integer) + +Values you can verify if it is in a wanted range. + +| Name | Type | Description +|-- |-- |-- +| 'min' | {float\|int} | allowed minimum value +| 'max' | {float\|int} | allowed maximum value +| 'oneof' | {array} | value must match one of the given values + +### Validate string + +Values of type "string" can be verified + +* against a given regex +* with a set of allowed values + +| Name | Type | Description +|-- |-- |-- +| 'regex' | {string} | value must match given regex +| 'oneof' | {array} | value must match one of the given values + +### Validate with presets + +In the file *validateparam.settings.php* you can put presets and their validation rules for elements that repeat. + +```php +<?php +/* + + validation rules + if a param has the key 'validate' that matches a key then its values will + be added for validation. + + SYNTAX + + KEY - value for'validate' key + VALUE - array with these possible keys + - type - set a type + - regex - define a regex for type sting + - min, max - range for types float and int + - oneof +*/ + + +return [ + 'hostname' => [ 'type' => 'string', 'regex' => '/^[a-z0-9\_\-\.]/i'], + 'portnumber' => [ 'type' => 'int', 'min' => 0, 'max' => 65535], + 'website' => [ 'type' => 'string', 'regex' => '/https?:\/\//'], +]; +``` + +```txt +[port] => Array + ( + [type] => int + [required] => true + [validate] => portnumber + ) +``` diff --git a/public_html/appmonitor/classes/validateparam.settings.php b/public_html/appmonitor/classes/validateparam.settings.php new file mode 100644 index 0000000000000000000000000000000000000000..fdc84648519e84e85dd6cc12d87272a734d72b59 --- /dev/null +++ b/public_html/appmonitor/classes/validateparam.settings.php @@ -0,0 +1,25 @@ +<?php +/* + + validation rules + if a param has the key 'validate' that matches a key then its values will + be added for validation. + + This file is included in the constructor of the validateparam class + + SYNTAX: + + KEY - value for'validate' key + VALUE - array with these possible keys + - type - set a type + - regex - define a regex for type sting + - min, max - range for types float and int + - oneof +*/ + + +$this->_aValidationDefs = [ + 'hostname' => [ 'type' => 'string', 'regex' => '/^[a-z0-9\_\-\.]/i'], + 'portnumber' => [ 'type' => 'int', 'min' => 0, 'max' => 65535], + 'website' => [ 'type' => 'string', 'regex' => '/https?:\/\//'], +]; diff --git a/public_html/appmonitor/classes/validateparam.settings.php.dist b/public_html/appmonitor/classes/validateparam.settings.php.dist new file mode 100644 index 0000000000000000000000000000000000000000..980f6bf9c400985b2e8b046946e8b3a2c4409e9c --- /dev/null +++ b/public_html/appmonitor/classes/validateparam.settings.php.dist @@ -0,0 +1,23 @@ +<?php +/* + + validation rules + if a param has the key 'validate' that matches a key then its values will + be added for validation. + + SYNTAX + + KEY - value for'validate' key + VALUE - array with these possible keys + - type - set a type + - regex - define a regex for type sting + - min, max - range for types float and int + - oneof +*/ + + +$this->_aValidationDefs = [ + 'hostname' => [ 'type' => 'string', 'regex' => '/^[a-z0-9\_\-\.]/i'], + 'portnumber' => [ 'type' => 'int', 'min' => 0, 'max' => 65535], + 'website' => [ 'type' => 'string', 'regex' => '/https?:\/\//'], +]; diff --git a/public_html/appmonitor/index.php b/public_html/appmonitor/index.php index f109b8a4e0a52d22ae127180fe99ef494b8225cd..1afda2a2fc8e0d40cb271de9fad785ffd4b9b754 100644 --- a/public_html/appmonitor/index.php +++ b/public_html/appmonitor/index.php @@ -362,7 +362,7 @@ $oMonitor->addCheck( [ "name" => "plugin Load", "description" => "current load", - "parent" => false, + "parent" => null, "check" => [ "function" => "Loadmeter", "params" => [ @@ -377,7 +377,7 @@ $oMonitor->addCheck( [ "name" => "plugin ApacheProcesses", "description" => "Apache processes", - "parent" => false, + "parent" => null, "check" => [ "function" => "ApacheProcesses", "params" => [ diff --git a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php index 87473021d5cbe8b47d9ac9de7b512f732e593636..83618b00f69273937a5b29214526d34c62457fe2 100755 --- a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php +++ b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php @@ -14,6 +14,7 @@ * 2022-03-28 move checks into plugins/apps/ * 2024-07-23 php 8: short array syntax * 2024-12-28 added check for custom config and url file (if they exist) + * 2025-02-24 add checks for sqlite and data dir */ // ---------------------------------------------------------------------- @@ -28,7 +29,7 @@ $oMonitor->addCheck( "check" => [ "function" => "Phpmodules", "params" => [ - "required" => ["curl"], + "required" => ["curl", "json", "pdo_sqlite"], "optional" => [], ], ], @@ -123,6 +124,22 @@ if (is_file("$sApproot/server/config/appmonitor-server-urls.json")) { ); } +$oMonitor->addCheck( + [ + "name" => "write to ./data/", + "description" => "Check sqlite data directory", + // "group" => "folder", + "check" => [ + "function" => "File", + "params" => [ + "filename" => "$sApproot/server/data", + "dir" => true, + "writable" => true, + ], + ], + ] +); + // ---------------------------------------------------------------------- // protect dirs against web access // specialty: if the test results in an error, the total result switches @@ -132,7 +149,7 @@ $sBaseUrl = 'http' . (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . dirname(dirname($_SERVER['REQUEST_URI'])); -foreach (['server/config', 'server/tmp'] as $sMyDir) { +foreach (['server/config', 'server/data', 'server/tmp'] as $sMyDir) { $oMonitor->addCheck( [ "name" => "http to $sMyDir", @@ -154,14 +171,58 @@ foreach (['server/config', 'server/tmp'] as $sMyDir) { // count of current projects // ---------------------------------------------------------------------- require_once($sApproot . '/server/classes/appmonitor-server.class.php'); -$oServer = new appmonitorserver(); +$oServer = new appmonitorserver(false); + +$aCfg=$oServer->getConfigVars(); + +$oMonitor->addCheck( + [ + "name" => "db-config", + "description" => "Check if the service is running", + // "group" => "service", + "parent" => "check custom config file", + "check" => [ + "function" => "Simple", + "params" => [ + "result" => ($aCfg['db']['dsn'] ? RESULT_OK : RESULT_ERROR), + "value" => ($aCfg['db']['dsn'] + ? "OK: dsn was set" + :"Error: database dsn was not found in config." + ) + ], + ] + ] +); + +if($aCfg['db']['dsn']){ + + $aDB=[ + 'connect'=>$aCfg['db']['dsn'] ?? '', + 'user'=>$aCfg['db']['user'] ?? '', + 'password'=> $aCfg['db']['password'] ?? '' + ]; + // print_r($aDB); + $oMonitor->addCheck( + [ + "name" => "connect db", + "description" => "Connnect PDO database", + "group" => "database", + "parent" => "db-config", + "check" => [ + "function" => "PdoConnect", + "params" => $aDB + ], + ] + ); +} + $iCount = count($oServer->getAppIds()); $oMonitor->addCheck( [ "name" => "appcounter", "description" => "Monitored apps", "group" => "monitor", - "parent" => false, + "parent" => null, "check" => [ "function" => "Simple", "params" => [ @@ -173,6 +234,7 @@ $oMonitor->addCheck( ], ] ); + // ---------------------------------------------------------------------- // check running service // ---------------------------------------------------------------------- @@ -211,7 +273,7 @@ $oMonitor->addCheck( "name" => "plugin Load", "description" => "current load", "group" => 'monitor', - "parent" => false, + "parent" => null, "check" => [ "function" => "Loadmeter", "params" => [ diff --git a/public_html/appmonitor/plugins/apps/wordpress.php b/public_html/appmonitor/plugins/apps/wordpress.php index 1ac052034d32a09d01b14fc6dae2e730f79c2160..49f3d00552c646ade912815bab0be896ff4968ca 100644 --- a/public_html/appmonitor/plugins/apps/wordpress.php +++ b/public_html/appmonitor/plugins/apps/wordpress.php @@ -64,8 +64,10 @@ $aDb = [ // checks // ---------------------------------------------------------------------- -// required php modules -// see https://ertano.com/required-php-modules-for-wordpress/ +// required php modules * WIP +// see https://wordpress.org/about/requirements/ << doesn't say anything about php modules +// see https://ertano.com/rired-php-modules-for-wordpress/ << too many modules +// see https://zeropointdevelopment.com/required-php-extensions-for-wordpress-wpquickies/ $oMonitor->addCheck( [ "name" => "PHP modules", @@ -76,22 +78,24 @@ $oMonitor->addCheck( "params" => [ "required" => [ // "cmath", - "cli", + // "cli", "curl", "date", "dom", "fileinfo", - "filter", - "gd", + // "filter", + // "gd", "gettext", "hash", - "iconv", + // "iconv", "imagick", "json", // "libsodium", - "mysql", + "mbstring", + "mysqli", "openssl", "pcre", + "sodium", // "opcache", // "readline", "xml", diff --git a/public_html/appmonitor/plugins/checks/apacheprocesses.php b/public_html/appmonitor/plugins/checks/apacheprocesses.php index 400c880bf9d2724e6adda5caa3df45d156aa12e7..262ffdb227422125bc151c12b40e0e9a472d9b53 100755 --- a/public_html/appmonitor/plugins/checks/apacheprocesses.php +++ b/public_html/appmonitor/plugins/checks/apacheprocesses.php @@ -45,7 +45,8 @@ * 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 - * + * 2025-03-04 <axel.hahn@unibe.ch> fix sComment error on failed apache status + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkApacheProcesses extends appmonitorcheck { @@ -69,39 +70,37 @@ class checkApacheProcesses extends appmonitorcheck protected float $_iError = 75; /** - * Self documentation (as idea) - * @return array + * Self documentation and validation rules + * @var array */ - 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 array $_aDoc = [ + 'name' => 'Plugin ApacheProcesses', + 'description' => 'Check count running Apache processes', + 'parameters' => [ + 'url' => [ + 'type' => 'string', + 'required' => false, + 'description' => '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' => "http://localhost/server-status", + 'regex'=>'/^https?:\/\/[^\s]+/', + 'example' => '', ], - ]; - } + 'warning' => [ + 'type' => 'float', + 'required' => false, + 'description' => 'Limit to switch to warning (in percent)', + 'default' => "50", + 'example' => 30, + ], + 'error' => [ + 'type' => 'float', + 'required' => false, + 'description' => 'Limit to switch to critical (in percent)', + 'default' => "75", + 'example' => 50, + ], + ], + ]; /** * Fetch http server status and return slots, active and waiting processes @@ -112,7 +111,7 @@ class checkApacheProcesses extends appmonitorcheck */ protected function _getApacheProcesses(): bool|array { - $sBody = file_get_contents($this->_sServerStatusUrl); + $sBody = @file_get_contents($this->_sServerStatusUrl); if (!$sBody) { return false; } @@ -163,12 +162,12 @@ class checkApacheProcesses extends appmonitorcheck // --- (2) do something magic $aProcesses = $this->_getApacheProcesses(); $iActive = $aProcesses ? $aProcesses['active'] : false; + $sComment = ''; // set result code if ($iActive === false) { $iResult = RESULT_UNKNOWN; } else { - $sComment = ''; $iTotal = $aProcesses['total']; $iResult = RESULT_OK; if (($iActive / $iTotal * 100) > $this->_iWarn) { diff --git a/public_html/appmonitor/plugins/checks/cert.php b/public_html/appmonitor/plugins/checks/cert.php index a6705b23df2429cbeca41d00d63add35432eb81a..311e6d58c965a66fa4552ee365f9602e119bb6f7 100755 --- a/public_html/appmonitor/plugins/checks/cert.php +++ b/public_html/appmonitor/plugins/checks/cert.php @@ -40,10 +40,52 @@ * 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 - * + * 2025-03-03 <axel.hahn@unibe.ch> comment block for host check in DND names + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkCert extends appmonitorcheck { + + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin Cert', + 'description' => 'Check if a SSL certificate is still valid … and does not expire soon.', + 'parameters' => [ + 'url' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'Url to check https://[server}[:{port}] or ssl://[server}[:{port}]', + 'default' => null, + 'regex'=>'/^(https|ssl):\/\/[^\s]+/', + 'example' => '', + ], + 'verify' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'optional: flag verify certificate; default = true', + 'default' => true, + 'example' => "false", + ], + 'warning' => [ + 'type' => 'int', + 'required' => false, + 'description' => 'optional: count of days to warn; default=21', + 'default' => 21, + 'example' => 30, + ], + 'critical' => [ + 'type' => 'int', + 'required' => false, + 'description' => 'optional: count of days to raise critical; default=5', + 'default' => 5, + 'example' => "7", + ], + ], + ]; + /** * Get default group of this check * @return string @@ -80,6 +122,11 @@ class checkCert extends appmonitorcheck ]; } + /* + + unneeded: + when verify is true (=default) then it cannot connect with wrong certificate + $sDNS = $certinfo['extensions']['subjectAltName'] ?? false; $sHost = parse_url($sUrl, PHP_URL_HOST); if (strstr($sDNS, "DNS:$sHost") === false) { @@ -88,6 +135,7 @@ class checkCert extends appmonitorcheck "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'] diff --git a/public_html/appmonitor/plugins/checks/diskfree.php b/public_html/appmonitor/plugins/checks/diskfree.php index 86e8efc8d4f74547b9e1647074167e59cb3af206..78c14fe093d9bd578abb7bb333daef7751674f35 100755 --- a/public_html/appmonitor/plugins/checks/diskfree.php +++ b/public_html/appmonitor/plugins/checks/diskfree.php @@ -20,9 +20,44 @@ * 2021-10-26 <axel.hahn@iml.unibe.ch> * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * 2025-01-02 <www.axel-hahn.de> update output + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkDiskfree extends appmonitorcheck { + + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin Diskfree', + 'description' => 'Check if a given filesystem / directory that it has enough space.', + 'parameters' => [ + 'directory' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'directory to check', + 'default' => null, + 'regex'=>'/./', + 'example' => '', + ], + 'warning' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'size for warning level', + 'default' => 21, + 'example' => "1.25GB", + ], + 'critical' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'size for critical level', + 'default' => 5, + 'example' => "500.7MB", + ], + ], + ]; + /** * Get default group of this check * @return string diff --git a/public_html/appmonitor/plugins/checks/exec.php b/public_html/appmonitor/plugins/checks/exec.php index 6221ca6fed432c724e4339e6b030d7cc73fd3e14..6731d2e9a220a30df51384836618d1761e58445d 100644 --- a/public_html/appmonitor/plugins/checks/exec.php +++ b/public_html/appmonitor/plugins/checks/exec.php @@ -21,10 +21,58 @@ * * 2022-09-19 <axel.hahn@iml.unibe.ch> * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables - * + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkExec extends appmonitorcheck { + + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin Exec', + 'description' => 'Execute a shell command.', + 'parameters' => [ + 'command' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'Command line', + 'default' => null, + 'regex'=>'/./', + 'example' => '[command] [parameters] 2>&1', + ], + 'output' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'size for warning level', + 'default' => 21, + 'example' => "1.25GB", + ], + 'exitOk' => [ + 'type' => 'array', + 'required' => false, + 'description' => 'array of integers for ok exitcodes', + 'default' => null, + 'example' => "", + ], + 'exitWarn' => [ + 'type' => 'array', + 'required' => false, + 'description' => 'array of integers for warning exitcodes', + 'default' => null, + 'example' => "", + ], + 'exitCritical' => [ + 'type' => 'array', + 'required' => false, + 'description' => 'array of integers for critical exitcodes', + 'default' => null, + 'example' => "", + ], + ], + ]; + /** * Get default group of this check * @return string diff --git a/public_html/appmonitor/plugins/checks/file.php b/public_html/appmonitor/plugins/checks/file.php index f0aafde19c2330ced2a1d19762913db36e625b74..c4bc8713176053322c50cf0dc342a16fe2d08797 100755 --- a/public_html/appmonitor/plugins/checks/file.php +++ b/public_html/appmonitor/plugins/checks/file.php @@ -22,9 +22,79 @@ * * 2021-10-26 <axel.hahn@iml.unibe.ch> * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables + * 2025-03-01 <axel.hahn@unibe.ch> fix check for files that must be absent + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkFile extends appmonitorcheck { + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin File', + 'description' => 'Check existing or absent file objects: files, directories, softlinks', + 'parameters' => [ + 'filename' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'filename or directory to check', + 'default' => "", + 'regex' => '/./', + 'example' => '/var/www/myfile.txt', + ], + 'exists' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'true = file object must exist (default); false = file object must not exist', + 'default' => true, + 'example' => "true", + ], + 'dir' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'If true then check if fileobject is a directory', + 'default' => null, + 'example' => "false", + ], + 'file' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'If true then check if fileobject is a file', + 'default' => null, + 'example' => "true", + ], + 'link' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'If true then check if fileobject is a file', + 'default' => null, + 'example' => "true", + ], + 'executable' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'If true then check if fileobject has executable permissions; if false it must be not executable', + 'default' => null, + 'example' => "false", + ], + 'readable' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'If true then check if fileobject is readable; if false it must be not readable', + 'default' => null, + 'example' => "true", + ], + 'writable' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'If true then check if fileobject is writable; ; if false it must be not writable', + 'default' => null, + 'example' => "true", + ], + ], + ]; + /** * Get default group of this check * @param array $aParams - see run() method @@ -68,7 +138,10 @@ class checkFile extends appmonitorcheck if (isset($aParams['exists'])) { $sMyflag = 'exists=' . ($aParams['exists'] ? 'yes' : 'no'); - if (file_exists($sFile) && $aParams['exists']) { + if ( + file_exists($sFile) && $aParams['exists'] + || !file_exists($sFile) && !$aParams['exists'] + ) { $aOK[] = $sMyflag; } else { $aErrors[] = $sMyflag; diff --git a/public_html/appmonitor/plugins/checks/hello.php b/public_html/appmonitor/plugins/checks/hello.php index e2ba5e60f28924b57535c5f5da9c68ee8fed8354..3f5c516ac1112ecbfea2bf2b3402ae9dabf1b7fa 100755 --- a/public_html/appmonitor/plugins/checks/hello.php +++ b/public_html/appmonitor/plugins/checks/hello.php @@ -41,10 +41,28 @@ * * 2019-06-05 <axel.hahn@iml.unibe.ch> * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables - * + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkHello extends appmonitorcheck { + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin Hello', + 'description' => 'Show a simple message', + 'parameters' => [ + 'message' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'Message to show', + 'default' => "", + 'regex' => '/./', + 'example' => 'Here I am', + ], + ], + ]; /** * Run the check diff --git a/public_html/appmonitor/plugins/checks/httpcontent.php b/public_html/appmonitor/plugins/checks/httpcontent.php index 3d981c54e7a34dcf29f9473681d7e2a2be8e950c..c05e47f8785d29246eb4b32d8759fac19ef9be0a 100755 --- a/public_html/appmonitor/plugins/checks/httpcontent.php +++ b/public_html/appmonitor/plugins/checks/httpcontent.php @@ -22,9 +22,115 @@ * 2023-07-06 <axel.hahn@unibe.ch> add flag userpwd * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * 2024-11-22 <axel.hahn@unibe.ch> Return unknown if curl module is not active + * 2025-03-17 <axel.hahn@unibe.ch> Fix check for http status code + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkHttpContent extends appmonitorcheck { + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin HttpContent', + 'description' => 'This check verifies if a given url can be requested. Optionally you can test if it follows wanted rules.', + 'parameters' => [ + 'url' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'Url to fetch', + 'default' => "", + 'regex' => '/https?:\/\//', + 'example' => 'https://www.example.com/', + ], + 'userpwd' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'User and password; syntax: “[username]:[password]”', + 'regex' => '/.*:.*/', + 'default' => "", + 'example' => "myuser:aVerySecretPassword", + ], + 'timeout' => [ + 'type' => 'int', + 'required' => false, + 'description' => 'Timeout in sec', + 'default' => 5, + 'example' => "10", + ], + 'headeronly' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'flag to fetch http response herader only (HEAD request); default: false = returns header and body;', + 'default' => false, + 'example' => "true", + ], + 'follow' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'flag to follow a location; default: false = do not follow; If you set it to true it ries to follow (but this is not a safe method)', + 'default' => false, + 'example' => "true", + ], + 'sslverify' => [ + 'type' => 'bool', + 'required' => false, + 'description' => 'Enable/ disable verification of ssl certificate; default: true (verification is on)', + 'default' => true, + 'example' => "false", + ], + 'status' => [ + 'type' => 'int', + 'required' => false, + 'description' => 'Test for an expected http status code; if none is given then test fails on status 400 and greater.', + 'default' => null, + 'example' => "401", + ], + 'headercontains' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Test for a string in the http response header; it returns OK if the text was found', + 'default' => null, + 'example' => "Content-Type: text/css", + ], + 'headernotcontains' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Test for a string in the http response header; it returns OK if the text was not found', + 'default' => null, + 'example' => "Server:", + ], + 'headerregex' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Test for a regex in the http response header; it returns OK if the regex matches', + 'default' => null, + 'example' => "", + ], + 'bodycontains' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Test for a string in the http response body; it returns OK if the text was found', + 'default' => null, + 'example' => "Content-Type: text/css", + ], + 'bodynotcontains' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Test for a string in the http response body; it returns OK if the text was not found', + 'default' => null, + 'example' => "Server:", + ], + 'bodyregex' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Test for a regex in the http response body; it returns OK if the regex matches', + 'default' => null, + 'example' => "", + ], + ], + ]; + /** * Get default group of this check * It is a "service" icon or "deny" for expected failures @@ -32,7 +138,7 @@ class checkHttpContent extends appmonitorcheck * @param array $aParams with optional 'status' containing http response code * @return string */ - public function getGroup(array $aParams=[]): string + public function getGroup(array $aParams = []): string { $sReturn = 'service'; if (isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500) { @@ -67,7 +173,7 @@ class checkHttpContent extends appmonitorcheck { $this->_checkArrayKeys($aParams, "url"); if (!function_exists("curl_init")) { - return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform mysqli test. The php-curl module is not active."]; + return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform http test. The php-curl module is not active."]; } $bShowContent = (isset($aParams["content"]) && $aParams["content"]) ? true : false; $ch = curl_init($aParams["url"]); @@ -141,12 +247,12 @@ class checkHttpContent extends appmonitorcheck // ---------- check functions // --- http status code - $sOut .= "Http status: " . $aInfos['http_code'] . " - "; + $sOut .= "Http status: $aInfos[http_code] - "; if (isset($aParams["status"])) { - if ($aInfos['http_code'] === $aParams["status"]) { - $sOut .= "compare OK<br>"; + if ($aInfos['http_code'] == $aParams["status"]) { + $sOut .= "as expected - OK<br>"; } else { - $sOut .= "compare failed<br>"; + $sOut .= "compare failed - not eaqual $aParams[status]<br>"; $bError = true; } } else { @@ -159,7 +265,7 @@ class checkHttpContent extends appmonitorcheck } // --- http header if (isset($aParams["headercontains"]) && $aParams["headercontains"]) { - $sOut .= "Http header contains "" . $aParams["headercontains"] . "" - "; + $sOut .= "Http header contains '$aParams[headercontains]' - "; if (!strstr($sHttpHeader, $aParams["headercontains"]) === false) { $sOut .= "compare OK<br>"; } else { @@ -168,7 +274,7 @@ class checkHttpContent extends appmonitorcheck } } if (isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]) { - $sOut .= "Http header does not contain "" . $aParams["headernotcontains"] . "" - "; + $sOut .= "Http header does not contain '$aParams[headernotcontains]' - "; if (strstr($sHttpHeader, $aParams["headernotcontains"]) === false) { $sOut .= "compare OK<br>"; } else { @@ -177,7 +283,7 @@ class checkHttpContent extends appmonitorcheck } } if (isset($aParams["headerregex"]) && $aParams["headerregex"]) { - $sOut .= "Http header regex test "" . $aParams["headerregex"] . "" - "; + $sOut .= "Http header regex test '$aParams[headerregex]' - "; try { $bRegex = preg_match($aParams["headerregex"], $sHttpHeader); if ($bRegex) { @@ -193,7 +299,7 @@ class checkHttpContent extends appmonitorcheck } // --- http body if (isset($aParams["bodycontains"]) && $aParams["bodycontains"]) { - $sOut .= "Http body contains "" . $aParams["bodycontains"] . "" - "; + $sOut .= "Http body contains '$aParams[bodycontains]' - "; if (!strstr($sHttpBody, $aParams["bodycontains"]) === false) { $sOut .= "compare OK<br>"; } else { @@ -202,7 +308,7 @@ class checkHttpContent extends appmonitorcheck } } if (isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]) { - $sOut .= "Http body does not contain "" . $aParams["bodynotcontains"] . "" - "; + $sOut .= "Http body does not contain '$aParams[bodynotcontains]' - "; if (strstr($sHttpBody, $aParams["bodynotcontains"]) === false) { $sOut .= "compare OK<br>"; } else { @@ -211,7 +317,7 @@ class checkHttpContent extends appmonitorcheck } } if (isset($aParams["bodyregex"]) && $aParams["bodyregex"]) { - $sOut .= "Http body regex test "" . $aParams["bodyregex"] . "" - "; + $sOut .= "Http body regex test '$aParams[bodyregex]' - "; try { $bRegex = preg_match($aParams["bodyregex"], $sHttpBody); if ($bRegex) { @@ -229,12 +335,12 @@ class checkHttpContent extends appmonitorcheck if (!$bError) { return [ RESULT_OK, - 'OK: http check "' . $aParams["url"] . '".<br>' . $sOut + "OK: http check '$aParams[url]'<br>$sOut" ]; } else { return [ RESULT_ERROR, - 'ERROR: http check "' . $aParams["url"] . '".<br>' . $sOut + "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 13911af15f71926e01f23cce97154217fe076ccd..00eaacfa4312a19c05c0a90cd9c04bf417eb2630 100755 --- a/public_html/appmonitor/plugins/checks/loadmeter.php +++ b/public_html/appmonitor/plugins/checks/loadmeter.php @@ -45,10 +45,37 @@ * 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 - * + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkLoadmeter extends appmonitorcheck { + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin Loadmeter', + 'description' => 'Get system load and render it as a tile.', + 'parameters' => [ + 'warning' => [ + 'type' => 'float', + 'required' => false, + 'description' => 'Warning level', + + // doc + 'default' => null, + 'example' => '1.5', + ], + 'error' => [ + 'type' => 'float', + 'required' => false, + 'description' => 'Critical level when to raise an error', + 'default' => null, + 'example' => '2.5', + ], + ], + ]; + /** * Get default group of this check * @return string diff --git a/public_html/appmonitor/plugins/checks/mysqlconnect.php b/public_html/appmonitor/plugins/checks/mysqlconnect.php index 5fe8a611cccb570bdd57475a915b11a9f16c96eb..48940afd92acd10c92a1143bb45a2457501d6cf5 100755 --- a/public_html/appmonitor/plugins/checks/mysqlconnect.php +++ b/public_html/appmonitor/plugins/checks/mysqlconnect.php @@ -20,9 +20,70 @@ * 2021-10-27 <axel.hahn@iml.unibe.ch> * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables * 2024-11-22 <axel.hahn@unibe.ch> detect installed mysqli function + * 2025-03-01 <axel.hahn@unibe.ch> add try catch + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkMysqlConnect extends appmonitorcheck { + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin Mysqlconnect', + 'description' => 'Verify a database connection with mysqli real connect function.', + 'parameters' => [ + 'server' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'Hostname/ ip of mysql server', + 'validate' => 'hostname', + + // doc + 'default' => null, + 'example' => 'localhost', + ], + 'port' => [ + 'type' => 'int', + 'required' => false, + 'description' => 'port of mysql server', + 'validate' => 'portnumber', + + 'default' => 5, + 'example' => '3', + ], + 'user' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Database user to connect with', + 'default' => null, + 'example' => 'dbuser', + ], + 'password' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Password of the database user to authenticate', + 'default' => null, + 'example' => 'mySecretDatabasePassword', + ], + 'db' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Database name to connect', + 'default' => null, + 'example' => 'wordpress', + ], + 'timeout' => [ + 'type' => 'float', + 'required' => false, + 'description' => 'Timeout in sec', + + 'default' => 5, + 'example' => '3', + ], + ], + ]; + /** * Get default group of this check * @return string @@ -59,14 +120,22 @@ class checkMysqlConnect extends appmonitorcheck 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 { + try{ + + $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() + ]; + } + } catch (Exception $e) { return [ 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 d95b3d55074553181d307f68e59bdcfcaba9ab1f..a3915015e63bc308f696bcdc4270863051d2eb64 100755 --- a/public_html/appmonitor/plugins/checks/pdoconnect.php +++ b/public_html/appmonitor/plugins/checks/pdoconnect.php @@ -19,10 +19,52 @@ * * 2021-10-27 <axel.hahn@iml.unibe.ch> * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables - * + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkPdoConnect extends appmonitorcheck { + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin PdoConnect', + 'description' => 'Verify a database connection with PDO connect', + 'parameters' => [ + 'connect' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'PDO conect string. See http://php.net/manual/en/pdo.drivers.php', + + // doc + 'default' => null, + 'example' => 'mysql:host=$aDb[server];port=3306;dbname=$aDb[database]', + ], + 'user' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Database user to connect with', + 'default' => null, + 'example' => 'dbuser', + ], + 'password' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Password of the database user to authenticate', + 'default' => null, + 'example' => 'mySecretDatabasePassword', + ], + 'timeout' => [ + 'type' => 'float', + 'required' => false, + 'description' => 'Timeout in sec', + + 'default' => 5, + 'example' => '3', + ], + ], + ]; + /** * Get default group of this check * @return string @@ -31,6 +73,7 @@ class checkPdoConnect extends appmonitorcheck { return 'database'; } + /** * Check connection to a database using pdo * see http://php.net/manual/en/pdo.drivers.php diff --git a/public_html/appmonitor/plugins/checks/phpmodules.php b/public_html/appmonitor/plugins/checks/phpmodules.php index b4b8170aa02b816070867397ddc884bfa3dc1557..547ec2d64f07d110df3aa11b68fc480571a3f311 100644 --- a/public_html/appmonitor/plugins/checks/phpmodules.php +++ b/public_html/appmonitor/plugins/checks/phpmodules.php @@ -19,10 +19,37 @@ * * 2022-05-06 <axel.hahn@iml.unibe.ch> first lines * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables - * + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkPhpmodules extends appmonitorcheck { + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin Phpmodules', + 'description' => 'Check loaded php modules', + 'parameters' => [ + 'required' => [ + 'type' => 'array', + 'required' => true, + 'description' => 'List of php modules that are required', + + // doc + 'default' => [], + 'example' => '["curl", "PDO"]', + ], + 'optional' => [ + 'type' => 'array', + 'required' => false, + 'description' => 'List of php modules that are optional. If one is missing, the status is set to warning.', + 'default' => [], + 'example' => '["gd"]', + ], + ], + ]; + /** * Get default group of this check * @return string diff --git a/public_html/appmonitor/plugins/checks/ping.php b/public_html/appmonitor/plugins/checks/ping.php index 3056314ccbedca581d82c8ae204305046aa37130..f1acd56eaad15e2308781784f7d8f1a5f7dde45d 100644 --- a/public_html/appmonitor/plugins/checks/ping.php +++ b/public_html/appmonitor/plugins/checks/ping.php @@ -21,9 +21,32 @@ * 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 + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkPing extends appmonitorcheck { + + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin Ping', + 'description' => 'Check if a given host can be pinged.', + 'parameters' => [ + 'host' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Hostname to ping; default: 127.0.0.1', + 'regex' => '/^[a-z0-9\_\-\.]/i', + + // doc + 'default' => "127.0.0.1", + 'example' => 'www.example.com', + ], + ], + ]; + /** * Get default group of this check * @return string diff --git a/public_html/appmonitor/plugins/checks/porttcp.php b/public_html/appmonitor/plugins/checks/porttcp.php index bd47ce5fbdf5919891c239debe68887a6b6b87da..879c3469a0bafcd23db981e2d0c70695040aea9b 100755 --- a/public_html/appmonitor/plugins/checks/porttcp.php +++ b/public_html/appmonitor/plugins/checks/porttcp.php @@ -22,10 +22,46 @@ * 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 - * + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkPortTcp extends appmonitorcheck { + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin PortTcp', + 'description' => 'Check if the local server or another host is listening to a given port number.', + 'parameters' => [ + 'host' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'optional: hostname to connect to; if unavailable 127.0.0.1 will be tested', + 'regex' => '/./', + + // doc + 'default' => null, + 'example' => 'mysql:host=$aDb[server];port=3306;dbname=$aDb[database]', + ], + 'port' => [ + 'type' => 'int', + 'required' => true, + 'description' => 'port number to check', + 'default' => null, + 'validate' => 'portnumber', + 'example' => '22', + ], + 'timeout' => [ + 'type' => 'int', + 'required' => false, + 'description' => 'optional timeout in sec; default: 5', + 'default' => 5, + 'example' => '3', + ], + ], + ]; + /** * Get default group of this check * @return string diff --git a/public_html/appmonitor/plugins/checks/simple.php b/public_html/appmonitor/plugins/checks/simple.php index 3af69b69008682e7a91ed9cfe2be93ebc27b30ed..f488ccaae5a053c500a4dd41a3b4e7119b5d4310 100755 --- a/public_html/appmonitor/plugins/checks/simple.php +++ b/public_html/appmonitor/plugins/checks/simple.php @@ -19,10 +19,60 @@ * * 2021-10-27 <axel.hahn@iml.unibe.ch> * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables - * + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkSimple extends appmonitorcheck { + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin Simple', + 'description' => 'Check loaded php modules', + 'parameters' => [ + 'value' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'Ouput text to describe the result', + 'default' => "", + 'example' => 'My simple example', + ], + 'result' => [ + 'type' => 'int', + 'required' => true, + 'description' => 'Result value to return', + 'min' => RESULT_OK, + 'max' => RESULT_ERROR, + + // doc + 'default' => RESULT_OK, + 'example' => '0 (for "ok")', + ], + 'type' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Type of the rendetred tile', + 'oneof' => ["counter"], + 'default' => null, + 'example' => 'counter', + ], + 'count' => [ + 'type' => 'float', + 'required' => false, + 'description' => 'If a count exists in a check then a tile will be rendered as a tile', + 'default' => null, + 'example' => '3.14', + ], + 'visual' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Visualize count value with "simple", "line", "bar,<width>,<count>"', + 'default' => null, + 'example' => 'bar,3,100', + ], + ], + ]; /** * Most simple check: set given values @@ -36,8 +86,8 @@ class checkSimple extends appmonitorcheck * brainstorming for a future release * * "counter" optioal: array of counter values - * - label string a label - * - value float a number + * - type string a label + * - count float a number * - type string one of simple | bar | line * @return array */ diff --git a/public_html/appmonitor/plugins/checks/sqliteconnect.php b/public_html/appmonitor/plugins/checks/sqliteconnect.php index 703db60d7467d1ff6968b659e0d2c3ca8c2d5c22..fcbda105782872bbeb1933100ae25c24458fb8cc 100755 --- a/public_html/appmonitor/plugins/checks/sqliteconnect.php +++ b/public_html/appmonitor/plugins/checks/sqliteconnect.php @@ -19,10 +19,54 @@ * * 2021-10-27 <axel.hahn@iml.unibe.ch> * 2024-07-23 <axel.hahn@unibe.ch> php 8 only: use typed variables - * + * 2025-03-19 <axel.hahn@unibe.ch> add validation rules and parameter description */ class checkSqliteConnect extends appmonitorcheck { + + /** + * Self documentation and validation rules + * @var array + */ + protected array $_aDoc = [ + 'name' => 'Plugin SqliteConnect', + 'description' => 'Verify a database connection with PDO connect', + 'parameters' => [ + 'db' => [ + 'type' => 'string', + 'required' => true, + 'description' => 'full path of the sqlite database file', + 'regex' => '/./', + + // doc + 'default' => null, + 'example' => 'mysql:host=$aDb[server];port=3306;dbname=$aDb[database]', + ], + 'user' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Database user to connect with', + 'default' => null, + 'example' => 'dbuser', + ], + 'password' => [ + 'type' => 'string', + 'required' => false, + 'description' => 'Password of the database user to authenticate', + 'default' => null, + 'example' => 'mySecretDatabasePassword', + ], + 'timeout' => [ + 'type' => 'float', + 'required' => false, + 'description' => 'Timeout in sec', + + 'default' => 5, + 'example' => '3', + ], + ], + ]; + /** * Get default group of this check * @return string