diff --git a/public_html/appmonitor/classes/appmonitor-checks.class.php b/public_html/appmonitor/classes/appmonitor-checks.class.php index 9301efe3a8b0d4012544623bd48abd821a633726..c7825de60a00db42367dabd51482777850761203 100644 --- a/public_html/appmonitor/classes/appmonitor-checks.class.php +++ b/public_html/appmonitor/classes/appmonitor-checks.class.php @@ -23,6 +23,7 @@ define("RESULT_ERROR", 3); * 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> * --------------------------------------------------------------------------------<br> * @version 0.9 * @author Axel Hahn @@ -47,6 +48,10 @@ class appmonitorcheck { * @var array */ private $_aData = array(); + + + // protected $_units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'); + protected $_units = array( 'B', 'KB', 'MB', 'GB', 'TB'); // ---------------------------------------------------------------------- // CONSTRUCTOR @@ -195,46 +200,216 @@ class appmonitorcheck { // CHECK FUNCTIONS (private) // ---------------------------------------------------------------------- + /** + * helper function: read certificate data + * called in checkCert() + * @param string $sUrl url to connect + * @return array + */ + protected function _certGetInfos($sUrl) { + $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); + + + $get = stream_context_create(array("ssl" => array("capture_peer_cert" => TRUE))); + 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_context to ssl://$sHost:$iPort"); + } + $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; + } + + + + /** + * check SSL certificate + * @param array $aParams + * array( + * "url" optional: url to connect check; default: own protocol + server + * "warning" optional: count of days to warn; default=30 + * ) + * @return boolean + */ + public function checkCert($aParams) { + $sUrl = isset($aParams["url"]) + ? $aParams["url"] + : 'http'. ($_SERVER['HTTPS'] ? 's' : '') . '://' . $_SERVER['SERVER_NAME'] .':' . $_SERVER['SERVER_PORT'] + ; + $iWarn = isset($aParams["warning"]) ? (int)($aParams["warning"]) : 30; + + $sMessage="url $sUrl ... "; + $certinfo=$this->_certGetInfos($sUrl); + if(isset($certinfo['_error'])){ + $this->_setReturn(RESULT_ERROR, $certinfo['_error'] . $sMessage); + return true; + } + + $sDNS=isset($certinfo['extensions']['subjectAltName']) ? $certinfo['extensions']['subjectAltName'] : false; + $sHost=parse_url($url,PHP_URL_HOST); + if(strstr($sDNS, 'DNS:'.$sHost)===false){ + $aReturn['errors'][]="Domainname $sHost ist nicht als DNS ALias im Zertifikat enthalten."; + $this->_setReturn(RESULT_ERROR, 'Wrong certificate: '.$sHost.' is not listed as DNS alias in ['.$sDNS.'] ' . $sMessage); + return true; + } + + $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) { + $this->_setReturn(RESULT_ERROR, 'Expired ' . $sMessage); + return true; + } + if ($iDaysleft<=$iWarn) { + $this->_setReturn(RESULT_WARNING, 'Expires soon ' . $sMessage); + return true; + } + // echo '<pre>'; + $this->_setReturn(RESULT_OK, 'OK, is valid ' . $sMessage); + return true; + } + + /** + * get human readable space value + * @param type $size + * @return string + */ + protected function _getHrSize($size){ + $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 type $sValue + * @return integer + */ + 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); + // die("FOUND: $sValue with unit ${sUnit} - 1024^$power * $i = $iReal"); + return $iReal; + } + $power++; + } + header('HTTP/1.0 503 Service Unavailable'); + die("ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units)); + return false; + } + + /** + * check free disk space on a given directory + * @param array $aParams + * array( + * "directory" directory that must exist + * "warning" space for warning (optional) + * "critical" minimal space + * ) + * @return boolean + */ + public function checkDiskfree($aParams) { + $this->_checkArrayKeys($aParams, "directory", "critical"); + + $sDirectory = $aParams["directory"]; + if(!is_dir($sDirectory)){ + $this->_setReturn(RESULT_ERROR, 'directory [' . $sDirectory . '] does not exist. Maybe it is wrong or is not mounted.'); + return true; + } + + $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){ + $this->_setReturn(RESULT_OK, $sMessage.' Warning level is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iWarn).' over warning limit).'); + return true; + } + if ($iWarn>$iSpaceLeft && $iCritical<$iSpaceLeft){ + $this->_setReturn(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).'); + return true; + } + } + // check space + if ($iCritical<$iSpaceLeft){ + $this->_setReturn(RESULT_OK, $sMessage .' Minimum is not reached yet (still '.$this->_getHrSize($iSpaceLeft-$iCritical).' over critical limit).'); + } else { + $this->_setReturn(RESULT_ERROR, $sMessage); + } + return true; + } /** * check a file * @param array $aParams * array( - * "filename" directory that must exist - * "writable" flag to check that it must be writable too + * "filename" directory that must exist + * "exists" "filename" must exist/ must be absent + * "dir" filetype directory + * "file" filetype file + * "link" filetype symbolic link + * "executable" flag executable + * "readable" flag is readable + * "writable" flag is writable * ) * @return boolean */ public function checkFile($aParams) { - $aOK=array(); - $aErrors=array(); + $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; + $sFile = $aParams["filename"]; + + if (isset($aParams['exists'])) { + $sMyflag = 'exists=' . ($aParams['exists'] ? 'yes' : 'no'); + if (file_exists($sFile) && $aParams['exists']) { + $aOK[] = $sMyflag; } else { - $aErrors[]=$sMyflag; + $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'); + 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'); + $aErrors[] = $sFiletest . '=' . ($aParams[$sFiletest] ? 'yes' : 'no'); } } } - $sMessage=(count($aOK) ? ' flags OK: ' .implode('|', $aOK) : '') - .' '. (count($aErrors) ? ' flags FAILED: '.implode('|', $aErrors) : '') - ; - if(count($aErrors)){ - $this->_setReturn(RESULT_ERROR, 'file test ['. $sFile . '] '.$sMessage); + $sMessage = (count($aOK) ? ' flags OK: ' . implode('|', $aOK) : '') + . ' ' . (count($aErrors) ? ' flags FAILED: ' . implode('|', $aErrors) : '') + ; + if (count($aErrors)) { + $this->_setReturn(RESULT_ERROR, 'file test [' . $sFile . '] ' . $sMessage); } else { - $this->_setReturn(RESULT_OK, 'file test ['. $sFile . '] '.$sMessage); + $this->_setReturn(RESULT_OK, 'file test [' . $sFile . '] ' . $sMessage); } return true; } @@ -281,15 +456,20 @@ class appmonitorcheck { * "user" * "password" * "db" + * "port" <<< optional * ) */ private function checkMysqlConnect($aParams) { $this->_checkArrayKeys($aParams, "server,user,password,db"); + if (!isset($aParams["port"])) { + $aParams["port"] = false; + } $db = mysqli_connect( - $aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"] + $aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"] ); if ($db) { $this->_setReturn(RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"); + mysqli_close($db); return true; } else { $this->_setReturn(RESULT_ERROR, "ERROR: Mysql database " . $aParams["db"] . " was not connected. " . mysqli_connect_error()); @@ -334,7 +514,11 @@ class appmonitorcheck { /** * most simple check: set values - * @return type + * @param array $aParams + * array( + * "result" integer; RESUL_nn + * "value" description text + * ) */ private function checkSimple($aParams) { $this->_checkArrayKeys($aParams, "result,value"); @@ -345,7 +529,7 @@ class appmonitorcheck { * check sqlite connection * @param array $aParams * array( - * "db" + * "db" full path of sqlite file * ) * @return boolean */ diff --git a/public_html/appmonitor/index.php b/public_html/appmonitor/index.php index 9fb40bbaa132c50f782d48a1e027a13b7c14affb..d867a90f1114adfa83f8618ccb8bc4f72971cacd 100644 --- a/public_html/appmonitor/index.php +++ b/public_html/appmonitor/index.php @@ -74,6 +74,32 @@ foreach(array('dataDir', 'buildDir', 'packageDir', 'archiveDir') as $sDirKey){ ); } +foreach(array('dataDir', 'buildDir', 'packageDir', 'archiveDir') as $sDirKey){ + $oMonitor->addCheck( + array( + "name" => "Disk space in dir [$sDirKey]", + "description" => "Check if workdir $sDirKey has enough space", + "check" => array( + "function" => "Diskfree", + "params" => array( + "directory" => $aConfig[$sDirKey], + "warning" => "500MB", + "critical" => "100MB", + ), + ), + ) + ); + +} + $oMonitor->addCheck( + array( + "name" => "Certificate check", + "description" => "Check if SSL cert is valid and does not expire soon", + "check" => array( + "function" => "Cert", + ), + ) + ); // ---------------------------------------------------------------------- // databases