From 20e368d9dec2acca70539f701986d06efa550255 Mon Sep 17 00:00:00 2001
From: hahn <axel.hahn@iml.unibe.ch>
Date: Fri, 17 Dec 2021 12:51:27 +0100
Subject: [PATCH] update appmonitor client

---
 .../classes/appmonitor-checks.class.php       | 639 ++----------------
 .../classes/appmonitor-client.class.php       |   7 +-
 .../appmonitor/classes/client_all_in_one.php  |  23 +
 .../apacheprocesses.php}                      |   9 +
 .../appmonitor/plugins/checks/cert.php        | 115 ++++
 .../appmonitor/plugins/checks/diskfree.php    |  87 +++
 .../appmonitor/plugins/checks/file.php        |  99 +++
 .../{checkHello.php => checks/hello.php}      |   0
 .../appmonitor/plugins/checks/httpcontent.php | 236 +++++++
 .../loadmeter.php}                            |  10 +-
 .../plugins/checks/mysqlconnect.php           |  69 ++
 .../appmonitor/plugins/checks/pdoconnect.php  |  69 ++
 .../appmonitor/plugins/checks/porttcp.php     |  76 +++
 .../appmonitor/plugins/checks/simple.php      |  57 ++
 .../plugins/checks/sqliteconnect.php          |  69 ++
 15 files changed, 994 insertions(+), 571 deletions(-)
 create mode 100644 public_html/appmonitor/classes/client_all_in_one.php
 rename public_html/appmonitor/plugins/{checkApacheProcesses.php => checks/apacheprocesses.php} (96%)
 create mode 100644 public_html/appmonitor/plugins/checks/cert.php
 create mode 100644 public_html/appmonitor/plugins/checks/diskfree.php
 create mode 100644 public_html/appmonitor/plugins/checks/file.php
 rename public_html/appmonitor/plugins/{checkHello.php => checks/hello.php} (100%)
 create mode 100644 public_html/appmonitor/plugins/checks/httpcontent.php
 rename public_html/appmonitor/plugins/{checkLoadmeter.php => checks/loadmeter.php} (96%)
 create mode 100644 public_html/appmonitor/plugins/checks/mysqlconnect.php
 create mode 100644 public_html/appmonitor/plugins/checks/pdoconnect.php
 create mode 100644 public_html/appmonitor/plugins/checks/porttcp.php
 create mode 100644 public_html/appmonitor/plugins/checks/simple.php
 create mode 100644 public_html/appmonitor/plugins/checks/sqliteconnect.php

diff --git a/public_html/appmonitor/classes/appmonitor-checks.class.php b/public_html/appmonitor/classes/appmonitor-checks.class.php
index a4d6914a..b571cfaa 100644
--- a/public_html/appmonitor/classes/appmonitor-checks.class.php
+++ b/public_html/appmonitor/classes/appmonitor-checks.class.php
@@ -48,8 +48,10 @@ if(!defined('RESULT_OK')){
  * 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>
  * --------------------------------------------------------------------------------<br>
- * @version 0.91
+ * @version 0.99
  * @author Axel Hahn
  * @link TODO
  * @license GPL
@@ -116,6 +118,7 @@ class appmonitorcheck {
         $this->_aData = array(
             "name" => $this->_aConfig["name"],
             "description" => $this->_aConfig["description"],
+            "group" => isset($this->_aConfig["group"]) ? $this->_aConfig["group"] : false,
             "result" => RESULT_UNKNOWN,
             "value" => false,
             "type" => false,
@@ -148,7 +151,7 @@ class appmonitorcheck {
      * @return boolean
      */
     protected function _setCounter($aParams){
-        if(count($aParams)){
+        if(is_array($aParams) && count($aParams)){
             foreach(array('type', 'count', 'visual') as $sMyKey){
                 if(isset($aParams[$sMyKey])){
                     $this->_aData[$sMyKey]=$aParams[$sMyKey];
@@ -182,11 +185,19 @@ class appmonitorcheck {
         foreach (explode(",", $sKeyList) as $sKey) {
             if (!array_key_exists($sKey, $aConfig)) {
                 header('HTTP/1.0 503 Service Unavailable');
-                die('ERROR in ' . __CLASS__ . "<br>array requires the keys [$sKeyList] - but key '$sKey' was not found in config array <pre>" . print_r($aConfig, true));
+                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('ERROR in ' . __CLASS__ . "<br> key '$sKey' is empty in config array <pre>" . print_r($aConfig, true));
+                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;
@@ -220,38 +231,57 @@ class appmonitorcheck {
         $this->_aConfig = $aConfig;
         $this->_createDefaultMetadata();
 
-        $sCheck = "check" . preg_replace('/[^a-zA-Z0-9]/', '', $this->_aConfig["check"]["function"]);
+        $sCheck = preg_replace('/[^a-zA-Z0-9]/', '', $this->_aConfig["check"]["function"]);
         $aParams = array_key_exists("params", $this->_aConfig["check"]) ? $this->_aConfig["check"]["params"] : array();
-        if (method_exists($this, $sCheck)) {
-            // load as internal function
-            call_user_func(array($this, $sCheck), $aParams);
-        } else {
-            // load as plugin
-            $sPluginFile=$this->_sPluginDir.'/'.$sCheck.'.php';
-            if (file_exists($sPluginFile)) {
-                
+        
+        // try to load as plugin from a plugin file
+        $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);
-                $oPlogin = new $sCheck;
-                $aResponse=$oPlogin->run($aParams); 
-                if(!is_array($aResponse)){
-                    header('HTTP/1.0 503 Service Unavailable');
-                    die(__CLASS__ . " plugin : $sCheck does not responses an array<pre>" . print_r($aResponse, true));
-                }
-                if(count($aResponse)<2){
-                    header('HTTP/1.0 503 Service Unavailable');
-                    die(__CLASS__ . " plugin : $sCheck does not responses the minimum of 2 array values<pre>" . print_r($aResponse, true));
-                }
-                if(!isset($aResponse[2])){
-                    $aResponse[2]=array();
-                }
-                $this->_setReturn($aResponse[0], $aResponse[1], $aResponse[2]);
-                
-            } else {
-                header('HTTP/1.0 503 Service Unavailable');
-                die(__CLASS__ . " check not found: $sCheck <pre>" . print_r($aConfig, true));
             }
         }
 
+        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]=array();
+        }
+        $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';
         // ... and send response 
         return $this->respond();
@@ -273,7 +303,7 @@ class appmonitorcheck {
             }
         }
         // return checks from plugins subdir
-        foreach(glob($this->_sPluginDir.'/check*.php') as $sPluginFile){
+        foreach(glob($this->_sPluginDir.'/checks/*.php') as $sPluginFile){
             $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1;
         }
         ksort($aReturn);
@@ -328,60 +358,6 @@ class appmonitorcheck {
         return $certinfo;
     }
 
-    
-    
-    /**
-     * check SSL certificate 
-     * @param array $aParams
-     * array(
-     *     "url"       optional: url to connect check; default: own protocol + server
-     *     "verify"    optional: flag for verification of certificate or check for expiration only; default=true (=verification is on)
-     *     "warning"   optional: count of days to warn; default=30
-     * )
-     * @return boolean
-     */
-    protected function checkCert($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'])){
-            $this->_setReturn(RESULT_ERROR, $certinfo['_error'] . $sMessage);
-            return true;
-        }
-        
-        $sDNS=isset($certinfo['extensions']['subjectAltName']) ? $certinfo['extensions']['subjectAltName'] : false;
-        $sHost=parse_url($sUrl,PHP_URL_HOST);
-        if(strstr($sDNS, 'DNS:'.$sHost)===false){
-            $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. ' 
-                .($bVerify ? 'Certificate is valid. ' : '(Verification is disabled; Check for expiration only.) ' )
-                . $sMessage);
-        return true;
-    }
 
     /**
      * get human readable space value
@@ -412,107 +388,12 @@ class appmonitorcheck {
             $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
-     */
-    protected 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;
+        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)
+        );
     }
-    /**
-     * check a file
-     * @param array $aParams
-     * array(
-     *     "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
-     */
-    protected function checkFile($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)) {
-            $this->_setReturn(RESULT_ERROR, 'file test [' . $sFile . '] ' . $sMessage);
-        } else {
-            $this->_setReturn(RESULT_OK, 'file test [' . $sFile . '] ' . $sMessage);
-        }
-        return true;
-    }
 
     
     /**
@@ -540,394 +421,16 @@ class appmonitorcheck {
                 return !!(strstr($value, $verifyValue)!==false);
                 break;
             default:
-                die("FATAL ERROR: a compare function [$sCompare] is not implemented (yet).");
+                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;
     }
 
 
-    /**
-     * make http request and test response body
-     * @param array $aParams
-     * array(
-     *     url                 string   url to fetch
-     *     timeout             integer  optional timeout in sec; default: 5
-     *     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
-     *     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"
-     * )
-     */
-    protected function checkHttpContent($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) {
-            $this->_setReturn(RESULT_ERROR, 'ERROR: failed to fetch ' . $aParams["url"] . '.');
-            return false;
-        } 
-        $sOut='';
-        $bError=false;
-        
-        $aInfos = curl_getinfo($ch);
-        /*
-            Array
-            (
-                [url] => https://www.iml.unibe.ch/
-                [content_type] => text/html; charset=utf-8
-                [http_code] => 200
-                [header_size] => 926
-                [request_size] => 55
-                [filetime] => -1
-                [ssl_verify_result] => 20
-                [redirect_count] => 0
-                [total_time] => 1.812
-                [namelookup_time] => 0
-                [connect_time] => 0
-                [pretransfer_time] => 0.015
-                [size_upload] => 0
-                [size_download] => 94654
-                [speed_download] => 52237
-                [speed_upload] => 0
-                [download_content_length] => -1
-                [upload_content_length] => -1
-                [starttransfer_time] => 1.812
-                [redirect_time] => 0
-                [redirect_url] => 
-                [primary_ip] => 130.92.30.80
-                [certinfo] => Array
-                    (
-                    )
-
-                [primary_port] => 443
-                [local_ip] => 10.1.30.49
-                [local_port] => 63597
-            )
-         */
-        
-        curl_close($ch);
-        
-        $aTmp=explode("\r\n\r\n", $res, 2);
-        $sHttpHeader=$aTmp[0];
-        $sHttpBody=isset($aTmp[1]) ? $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>";
-            } 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>";
-            }
-        }
-        // --- http header
-        if(isset($aParams["headercontains"]) && $aParams["headercontains"]){
-            $sOut.="Http header contains &quot;".$aParams["headercontains"]."&quot; - ";
-            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 &quot;".$aParams["headernotcontains"]."&quot; - ";
-            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 &quot;".$aParams["headerregex"]."&quot; - ";
-            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;
-            }
-        }
-        // --- http body
-        if(isset($aParams["bodycontains"]) && $aParams["bodycontains"]){
-            $sOut.="Http body contains &quot;".$aParams["bodycontains"]."&quot; - ";
-            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 &quot;".$aParams["bodynotcontains"]."&quot; - ";
-            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 &quot;".$aParams["bodyregex"]."&quot; - ";
-            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) {
-            $this->_setReturn(RESULT_OK, 
-                    'OK: http check "' . $aParams["url"] . '".<br>'.$sOut
-                    );
-        } else {
-            $this->_setReturn(RESULT_ERROR, 
-                    'ERROR: http check "' . $aParams["url"] . '".<br>'.$sOut
-                    );
-        }
-
-        /*
-        echo '<pre>'; 
-        echo $sOut."<hr>";
-        echo "<hr>HEADER: ".htmlentities($sHttpHeader)."<hr>";
-        print_r($aParams); print_r($aInfos); 
-        // echo htmlentities($sHttpBody);
-        die();
-         */
-        return true;
-    }
-
-    /**
-     * check mysql connection to a database using mysqli realconnect
-     * @param array $aParams
-     * array(
-     *     server              string   database hostname / ip address
-     *     user                string   db user
-     *     password            string   password for db user
-     *     db                  string   schema / database name
-     *     port                integer  optional: port
-     *     timeout             integer  optional timeout in sec; default: 5
-     * )
-     */
-    protected function checkMysqlConnect($aParams) {
-        $this->_checkArrayKeys($aParams, "server,user,password,db");
-        $mysqli=mysqli_init();
-        if(!$mysqli){
-            $this->_setReturn(RESULT_ERROR, 'ERROR: mysqli_init failed.');
-            return false;
-        }
-        if (!$mysqli->options(MYSQLI_OPT_CONNECT_TIMEOUT, (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp)) {
-            $this->_setReturn(RESULT_ERROR, 'ERROR: setting mysqli_init failed.');
-            return false;
-        }
-
-        $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) {
-            $this->_setReturn(RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected");
-            $mysqli->close();
-            return true;
-        } else {
-            $this->_setReturn(RESULT_ERROR, "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error ".mysqli_connect_errno() .": ". mysqli_connect_error());
-            return false;
-        }
-    }
-    /**
-     * check connection to a database using pdo
-     * see http://php.net/manual/en/pdo.drivers.php
-     * 
-     * @param array $aParams
-     * array(
-     *     connect             string   connect string
-     *     user                string   db user
-     *     password            string   password for db user
-     *     timeout             integer  optional timeout in sec; default: 5
-     * )
-     */
-    protected function checkPdoConnect($aParams) {
-        $this->_checkArrayKeys($aParams, "connect,user,password");
-
-        try{
-            $db = new PDO(
-                $aParams['connect'], 
-                $aParams['user'], 
-                $aParams['password'], 
-                array(
-                    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,                  
-                    // mssql
-                    // PDO::SQLSRV_ATTR_QUERY_TIMEOUT => $this->_iTimeoutTcp,  
-                )
-            );
-            $this->_setReturn(RESULT_OK, "OK: Database was connected with PDO " . $aParams['connect']);
-            $db=null;
-            return true;
-        } catch(PDOException $e) {
-            $this->_setReturn(RESULT_ERROR, "ERROR: Database was not connected " . $aParams['connect'] . " was not connected. Error ".$e->getMessage());
-            return false;
-        }
-    }
-
-    /**
-     * check if system is listening to a given port
-     * @param array $aParams
-     * array(
-     *     port                integer  port
-     *     host                string   optional hostname to connect; default: 127.0.0.1
-     *     timeout             integer  optional timeout in sec; default: 5
-     * )
-     * @return boolean
-     */
-    protected function checkPortTcp($aParams) {
-        $this->_checkArrayKeys($aParams, "port");
-
-        $sHost = array_key_exists('host', $aParams) ? $aParams['host'] : '127.0.0.1';
-        $iPort = (int) $aParams['port'];
-
-        // from http://php.net/manual/de/sockets.examples.php
-
-        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
-        if ($socket === false) {
-            $this->_setReturn(RESULT_UNKNOWN, "ERROR: $sHost:$iPort was not checked. socket_create() failed: " . socket_strerror(socket_last_error()));
-            return false;
-        }
-        // set socket timeout
-        socket_set_option(
-            $socket,
-            SOL_SOCKET,  // socket level
-            SO_SNDTIMEO, // timeout option
-            array(
-              "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) {
-            $this->_setReturn(RESULT_ERROR, "ERROR: $sHost:$iPort failed. " . socket_strerror(socket_last_error($socket)));
-            socket_close($socket);
-            return false;
-        } else {
-            $this->_setReturn(RESULT_OK, "OK: $sHost:$iPort was connected.");
-            socket_close($socket);
-            return true;
-        }
-    }
-
-    /**
-     * most simple check: set given values
-     * Use this function to add a counter
-     * 
-     * @param array $aParams
-     * array keys:
-     *     value               string   description text
-     *     result              integer  RESULT_nn
-     * 
-     *     brainstorming for a future release
-     * 
-     *     "counter"  optioal: array of counter values
-     *         - label         string   a label
-     *         - value         float    a number
-     *         - type          string   one of simple | bar | line
-     * 
-     */
-    protected function checkSimple($aParams) {
-        $this->_checkArrayKeys($aParams, "result,value");
-        $this->_setReturn((int) $aParams["result"], $aParams["value"]);
-        foreach(array('type', 'count', 'visual') as $sMyKey){
-            if(isset($aParams[$sMyKey])){
-                $this->_aData[$sMyKey]=$aParams[$sMyKey];
-            }
-        }
-        return true;
-    }
-
-    /**
-     * check sqlite connection
-     * @param array $aParams
-     * array(
-     *     db                  string   full path of sqlite file 
-     *     timeout             integer  optional timeout in sec; default: 5
-     * )
-     * @return boolean
-     */
-    protected function checkSqliteConnect($aParams) {
-        $this->_checkArrayKeys($aParams, "db");
-        if (!file_exists($aParams["db"])) {
-            $this->_setReturn(RESULT_ERROR, "ERROR: Sqlite database file " . $aParams["db"] . " does not exist.");
-            return false;
-        }
-        if(!isset($aParams['user'])){
-            $aParams['user']='';
-        }
-        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'], 
-                array(
-                    PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp,                  
-                )
-            );
-            $this->_setReturn(RESULT_OK, "OK: Sqlite database " . $aParams["db"] . " was connected");
-            return true;
-        } catch (Exception $e) {
-            $this->_setReturn(RESULT_ERROR, "ERROR: Sqlite database " . $aParams["db"] . " was not connected. " . $e->getMessage());
-            return false;
-        }
-    }
 
 }
diff --git a/public_html/appmonitor/classes/appmonitor-client.class.php b/public_html/appmonitor/classes/appmonitor-client.class.php
index 93ac0762..fc98da62 100644
--- a/public_html/appmonitor/classes/appmonitor-client.class.php
+++ b/public_html/appmonitor/classes/appmonitor-client.class.php
@@ -1,4 +1,7 @@
 <?php
+if (!class_exists('appmonitorcheck')){
+    require_once 'appmonitor-checks.class.php';
+}
 
 /**
  * ____________________________________________________________________________
@@ -37,7 +40,7 @@
  * 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>
  * --------------------------------------------------------------------------------<br>
- * @version 0.92
+ * @version 0.99
  * @author Axel Hahn
  * @link TODO
  * @license GPL
@@ -51,7 +54,7 @@ class appmonitor {
      * value is in seconds
      * @var int
      */
-    protected $_sVersion = 'php-client-v0.92';
+    protected $_sVersion = 'php-client-v0.99';
 
     /**
      * config: default ttl for server before requesting the client check again
diff --git a/public_html/appmonitor/classes/client_all_in_one.php b/public_html/appmonitor/classes/client_all_in_one.php
new file mode 100644
index 00000000..874d1bd0
--- /dev/null
+++ b/public_html/appmonitor/classes/client_all_in_one.php
@@ -0,0 +1,23 @@
+<?php
+/*
+
+    MERGED APPMONITOR CLIENT :: WORK IN PROGRESS
+
+    generated Do 25 Nov 2021 15:45:27 CET
+
+*/
+
+ 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 &quot;".$aParams["headercontains"]."&quot; - "; 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 &quot;".$aParams["headernotcontains"]."&quot; - "; 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 &quot;".$aParams["headerregex"]."&quot; - "; 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 &quot;".$aParams["bodycontains"]."&quot; - "; 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 &quot;".$aParams["bodynotcontains"]."&quot; - "; 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 &quot;".$aParams["bodyregex"]."&quot; - "; 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 . '"> &lt;--- ' . $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
diff --git a/public_html/appmonitor/plugins/checkApacheProcesses.php b/public_html/appmonitor/plugins/checks/apacheprocesses.php
similarity index 96%
rename from public_html/appmonitor/plugins/checkApacheProcesses.php
rename to public_html/appmonitor/plugins/checks/apacheprocesses.php
index 484f10a5..f2175375 100644
--- a/public_html/appmonitor/plugins/checkApacheProcesses.php
+++ b/public_html/appmonitor/plugins/checks/apacheprocesses.php
@@ -78,6 +78,15 @@ class checkApacheProcesses extends appmonitorcheck{
     }
 
 
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup(){
+        return 'services';
+    }
+
     /**
      * 
      * @param array   $aParams
diff --git a/public_html/appmonitor/plugins/checks/cert.php b/public_html/appmonitor/plugins/checks/cert.php
new file mode 100644
index 00000000..f75e60b8
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/cert.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * CUSTOM CHECK
+ * 
+ * Check expiration of ssl certificate
+ * ____________________________________________________________________________
+ * 
+ * USAGE:
+ * 
+ * $oMonitor->addCheck(
+ *     array(
+ *         "name" => "SSL cert",
+ *         "description" => "Check SSL certificate of my domain",
+ *         "check" => array(
+ *             "function" => "Cert",
+ *             "params" => array(
+ *                 "url" => "https://www.example.com",
+ *                 "warning" => "21",
+ *             ),
+ *         ),
+ *     )
+ * );
+ * ____________________________________________________________________________
+ * 
+ * 2021-10-26  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkCert extends appmonitorcheck{
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup(){
+        return 'security';
+    }
+
+    /**
+     * check SSL certificate 
+     * @param array $aParams
+     * array(
+     *     "url"       optional: url to connect check; default: own protocol + server
+     *     "verify"    optional: flag for verification of certificate or check for expiration only; default=true (=verification is on)
+     *     "warning"   optional: count of days to warn; default=30
+     * )
+     * @return boolean
+     */
+    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
+            ];
+        }
+        // echo '<pre>';
+        return [
+            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
new file mode 100644
index 00000000..667dfeb0
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/diskfree.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * CHECK FOR FREE DISKSPACE
+ * ____________________________________________________________________________
+ * 
+ * 2021-10-26  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkDiskfree extends appmonitorcheck{
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup(){
+        return 'disk';
+    }
+
+    /**
+     * 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 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).'
+                ];
+            }
+        }
+        // check space
+        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/file.php b/public_html/appmonitor/plugins/checks/file.php
new file mode 100644
index 00000000..c19b85dd
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/file.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * CUSTOM CHECK FOR FILE OBJECTS
+ * 
+ * Check files, directories, links if the exist or not, if they are accessible 
+ * or not.
+ * ____________________________________________________________________________
+ * 
+ * 2021-10-26  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkFile extends appmonitorcheck{
+    /**
+     * get default group of this check
+     * @param array   $aParams - see run() method
+     * @return array
+     */
+    public function getGroup($aParams){
+        $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;
+    }
+
+    /**
+     * check a file
+     * @param array $aParams
+     * array(
+     *     "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 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
+            ];
+        }
+    }
+
+}
diff --git a/public_html/appmonitor/plugins/checkHello.php b/public_html/appmonitor/plugins/checks/hello.php
similarity index 100%
rename from public_html/appmonitor/plugins/checkHello.php
rename to public_html/appmonitor/plugins/checks/hello.php
diff --git a/public_html/appmonitor/plugins/checks/httpcontent.php b/public_html/appmonitor/plugins/checks/httpcontent.php
new file mode 100644
index 00000000..d924c59a
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/httpcontent.php
@@ -0,0 +1,236 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * CHECK RESPONSE OF AN HTTP REQUEST
+ * ____________________________________________________________________________
+ * 
+ * 2021-10-26  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkHttpContent extends appmonitorcheck{
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup($aParams){
+        $sReturn='service';
+        if(isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500 ){
+            $sReturn='deny';
+        }
+        return $sReturn;
+    }
+
+    /**
+     * make http request and test response body
+     * @param array $aParams
+     * array(
+     *     url                 string   url to fetch
+     *     timeout             integer  optional timeout in sec; default: 5
+     *     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
+     *     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) {
+        $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) {
+            $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);
+        /*
+            Array
+            (
+                [url] => https://www.iml.unibe.ch/
+                [content_type] => text/html; charset=utf-8
+                [http_code] => 200
+                [header_size] => 926
+                [request_size] => 55
+                [filetime] => -1
+                [ssl_verify_result] => 20
+                [redirect_count] => 0
+                [total_time] => 1.812
+                [namelookup_time] => 0
+                [connect_time] => 0
+                [pretransfer_time] => 0.015
+                [size_upload] => 0
+                [size_download] => 94654
+                [speed_download] => 52237
+                [speed_upload] => 0
+                [download_content_length] => -1
+                [upload_content_length] => -1
+                [starttransfer_time] => 1.812
+                [redirect_time] => 0
+                [redirect_url] => 
+                [primary_ip] => 130.92.30.80
+                [certinfo] => Array
+                    (
+                    )
+
+                [primary_port] => 443
+                [local_ip] => 10.1.30.49
+                [local_port] => 63597
+            )
+         */
+        
+        curl_close($ch);
+        
+        $aTmp=explode("\r\n\r\n", $res, 2);
+        $sHttpHeader=$aTmp[0];
+        $sHttpBody=isset($aTmp[1]) ? $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>";
+            } 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>";
+            }
+        }
+        // --- http header
+        if(isset($aParams["headercontains"]) && $aParams["headercontains"]){
+            $sOut.="Http header contains &quot;".$aParams["headercontains"]."&quot; - ";
+            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 &quot;".$aParams["headernotcontains"]."&quot; - ";
+            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 &quot;".$aParams["headerregex"]."&quot; - ";
+            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;
+            }
+        }
+        // --- http body
+        if(isset($aParams["bodycontains"]) && $aParams["bodycontains"]){
+            $sOut.="Http body contains &quot;".$aParams["bodycontains"]."&quot; - ";
+            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 &quot;".$aParams["bodynotcontains"]."&quot; - ";
+            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 &quot;".$aParams["bodyregex"]."&quot; - ";
+            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
+            ];
+        }
+
+        /*
+        echo '<pre>'; 
+        echo $sOut."<hr>";
+        echo "<hr>HEADER: ".htmlentities($sHttpHeader)."<hr>";
+        print_r($aParams); print_r($aInfos); 
+        // echo htmlentities($sHttpBody);
+        die();
+         */
+    }
+
+}
diff --git a/public_html/appmonitor/plugins/checkLoadmeter.php b/public_html/appmonitor/plugins/checks/loadmeter.php
similarity index 96%
rename from public_html/appmonitor/plugins/checkLoadmeter.php
rename to public_html/appmonitor/plugins/checks/loadmeter.php
index 956dc5b1..d1a8c16c 100644
--- a/public_html/appmonitor/plugins/checkLoadmeter.php
+++ b/public_html/appmonitor/plugins/checks/loadmeter.php
@@ -46,7 +46,15 @@
  * 
  */
 class checkLoadmeter extends appmonitorcheck{
-    
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup(){
+        return 'monitor';
+    }
+
     /**
      * 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
diff --git a/public_html/appmonitor/plugins/checks/mysqlconnect.php b/public_html/appmonitor/plugins/checks/mysqlconnect.php
new file mode 100644
index 00000000..d386146e
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/mysqlconnect.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * CHECK DATABASE CONNECTION WITH MYSQLI
+ * ____________________________________________________________________________
+ * 
+ * 2021-10-27  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkMysqlConnect extends appmonitorcheck{
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup(){
+        return 'database';
+    }
+    /**
+     * check mysql connection to a database using mysqli realconnect
+     * @param array $aParams
+     * array(
+     *     server              string   database hostname / ip address
+     *     user                string   db user
+     *     password            string   password for db user
+     *     db                  string   schema / database name
+     *     port                integer  optional: port
+     *     timeout             integer  optional timeout in sec; default: 5
+     * )
+     */
+    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()
+            ];
+        }
+    }
+}
diff --git a/public_html/appmonitor/plugins/checks/pdoconnect.php b/public_html/appmonitor/plugins/checks/pdoconnect.php
new file mode 100644
index 00000000..03f1d221
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/pdoconnect.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * CHECK DATABASE CONNECTION USING PDO
+ * ____________________________________________________________________________
+ * 
+ * 2021-10-27  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkPdoConnect extends appmonitorcheck{
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup(){
+        return 'database';
+    }
+    /**
+     * check connection to a database using pdo
+     * see http://php.net/manual/en/pdo.drivers.php
+     * 
+     * @param array $aParams
+     * array(
+     *     connect             string   connect string
+     *     user                string   db user
+     *     password            string   password for db user
+     *     timeout             integer  optional timeout in sec; default: 5
+     * )
+     */
+    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,
+                    
+                    // timeout
+                    // Not all drivers support this option; mysqli does
+                    PDO::ATTR_TIMEOUT => (isset($aParams["timeout"]) && (int)$aParams["timeout"]) ? (int)$aParams["timeout"] : $this->_iTimeoutTcp,                  
+                    // mssql
+                    // PDO::SQLSRV_ATTR_QUERY_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()];
+        }
+    }
+    
+}
diff --git a/public_html/appmonitor/plugins/checks/porttcp.php b/public_html/appmonitor/plugins/checks/porttcp.php
new file mode 100644
index 00000000..79afcd87
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/porttcp.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * CHECK SWLITE CONNECTION USING PDO
+ * ____________________________________________________________________________
+ * 
+ * 2021-10-27  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkPortTcp extends appmonitorcheck{
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup(){
+        return 'network';
+    }
+
+    /**
+     * check if system is listening to a given port
+     * @param array $aParams
+     * array(
+     *     port                integer  port
+     *     host                string   optional hostname to connect; default: 127.0.0.1
+     *     timeout             integer  optional timeout in sec; default: 5
+     * )
+     * @return boolean
+     */
+    public function run($aParams) {
+        $this->_checkArrayKeys($aParams, "port");
+
+        $sHost = array_key_exists('host', $aParams) ? $aParams['host'] : '127.0.0.1';
+        $iPort = (int) $aParams['port'];
+
+        // from http://php.net/manual/de/sockets.examples.php
+
+        $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())];
+        }
+        // set socket timeout
+        socket_set_option(
+            $socket,
+            SOL_SOCKET,  // socket level
+            SO_SNDTIMEO, // timeout option
+            array(
+              "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) {
+            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."];
+        }
+    }
+    
+}
diff --git a/public_html/appmonitor/plugins/checks/simple.php b/public_html/appmonitor/plugins/checks/simple.php
new file mode 100644
index 00000000..15e6139a
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/simple.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * SIMPLE CHECK
+ * ____________________________________________________________________________
+ * 
+ * 2021-10-27  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkSimple extends appmonitorcheck{
+    
+    /**
+     * most simple check: set given values
+     * Use this function to add a counter
+     * 
+     * @param array $aParams
+     * array keys:
+     *     value               string   description text
+     *     result              integer  RESULT_nn
+     * 
+     *     brainstorming for a future release
+     * 
+     *     "counter"  optioal: array of counter values
+     *         - label         string   a label
+     *         - value         float    a number
+     *         - type          string   one of simple | bar | line
+     * 
+     */
+    public function run($aParams) {
+        $this->_checkArrayKeys($aParams, "result,value");
+        // $this->_setReturn((int) $aParams["result"], $aParams["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
+        ];
+    }
+}
diff --git a/public_html/appmonitor/plugins/checks/sqliteconnect.php b/public_html/appmonitor/plugins/checks/sqliteconnect.php
new file mode 100644
index 00000000..65b965cc
--- /dev/null
+++ b/public_html/appmonitor/plugins/checks/sqliteconnect.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * ____________________________________________________________________________
+ * 
+ *  _____ _____ __                   _____         _ _           
+ * |     |     |  |      ___ ___ ___|     |___ ___|_| |_ ___ ___ 
+ * |-   -| | | |  |__   | .'| . | . | | | | . |   | |  _| . |  _|
+ * |_____|_|_|_|_____|  |__,|  _|  _|_|_|_|___|_|_|_|_| |___|_|  
+ *                          |_| |_|                              
+ *                           _ _         _                                            
+ *                       ___| |_|___ ___| |_                                          
+ *                      |  _| | | -_|   |  _|                                         
+ *                      |___|_|_|___|_|_|_|   
+ *                                                               
+ * ____________________________________________________________________________
+ * 
+ * CHECK SWLITE CONNECTION USING PDO
+ * ____________________________________________________________________________
+ * 
+ * 2021-10-27  <axel.hahn@iml.unibe.ch>
+ * 
+ */
+class checkSqliteConnect extends appmonitorcheck{
+    /**
+     * get default group of this check
+     * @param array   $aParams
+     * @return array
+     */
+    public function getGroup(){
+        return 'database';
+    }
+
+    /**
+     * check sqlite connection
+     * @param array $aParams
+     * array(
+     *     db                  string   full path of sqlite file 
+     *     timeout             integer  optional timeout in sec; default: 5
+     * )
+     * @return boolean
+     */
+    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 {
+            // $db = new SQLite3($sqliteDB);
+            // $db = new PDO("sqlite:".$sqliteDB);
+            $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()];
+        }
+    }
+    
+}
-- 
GitLab