From e9890596a05ec8a2235ec8e77d80ebfaa786e775 Mon Sep 17 00:00:00 2001
From: "Hahn Axel (hahn)" <axel.hahn@unibe.ch>
Date: Fri, 28 Feb 2025 16:57:25 +0100
Subject: [PATCH] update base files

---
 .gitignore        |   1 +
 TODO.md           |  42 +++++++++++
 build.php         |  32 ++++++---
 inc_functions.php | 172 ++++++++++++++++++++++++++++++++++++++++++++--
 installer.php     |  54 +++++++++++----
 src/amcli.php     |  29 +++++---
 src/simple.ini    |   5 +-
 7 files changed, 293 insertions(+), 42 deletions(-)
 create mode 100644 TODO.md

diff --git a/.gitignore b/.gitignore
index c81286c..ce6d634 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ built_packages/
 external/
 build
 src/include_checks.php
+src/amcli.php.tmp
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..ccd91b2
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,42 @@
+# TODOs
+
+was ich so sehe ...
+
+✅ Slack-Notifikation: statt Parsing-mechanismus eines Strings --> JSON in der INI schreiben
+✅ `--ini=<FILE>` bei relativem Pfad ist relativ zum Binary --> nicht ins eigene Verzeichnis wechseln
+⬜ Dokumentation
+⬜ Code aufräumen
+
+## Tests schreiben
+
+✅ Test auf gesetzte Metadaten
+⬜ für alle Checks einen Test schreiben
+
+    ⬜ ApacheProcesses
+    ✅ Cert
+    ✅ Diskfree
+    ✅ Exec
+    ⬜ File --> "exists": false meldet bei Test einer nicht existierenden Datei fälschlicherweise einen Fehler
+    ✅ Hello
+    ⬜ HttpContent
+    ⬜ Loadmeter
+    ⬜ MysqlConnect
+    ⬜ PdoConnect
+    ⬜ Phpmodules
+    ⬜ Ping
+    ⬜ PortTcp
+    ⬜ Simple
+    ⬜ SqliteConnect
+
+## Done
+
+✅ PHP compiliert zum Binary
+✅ Binary startet in einem fremden Verzichnis
+✅ Binary startet auf einen Nicht-PHP-System
+
+---
+Legende:
+
+⬜
+✅
+❌
diff --git a/build.php b/build.php
index 51d5aae..4b993e5 100755
--- a/build.php
+++ b/build.php
@@ -7,6 +7,8 @@ require("inc_functions.php");
 $INFILE="$selfdir/src/amcli.php";
 $INCFILE="$selfdir/src/include_checks.php";
 $TMPFILE="$selfdir/src/amcli.php.tmp";
+$OUTFILE="$selfdir/$dirPackages/".str_replace('.php', '', basename($INFILE));
+
 
 echo "
 
@@ -16,12 +18,10 @@ echo "
 
 ";
 
-
 _chdir($selfdir);
 _mkdir($dirPackages);
 
-
-$OUTFILE="$selfdir/$dirPackages/".str_replace('.php', '', basename($INFILE));
+// ----------------------------------------------------------------------
 
 _h1("Check target file");
 if(file_exists($OUTFILE)){
@@ -35,17 +35,20 @@ if(file_exists($OUTFILE)){
     }
 }
 
+// ----------------------------------------------------------------------
+
 _h1("Prepare source");
 $in=file_get_contents($INFILE);
 $inccode=file_get_contents($INCFILE);
 $inccode=str_replace("<?php", "", $inccode);
 
-$tempcode=preg_replace("#\/\/ ---MARK---INCLUDE-CHECKS---START---(.*)END#s", "$inccode", $in);
+$tempcode=preg_replace("#\/\/ ---MARK---INCLUDE-CHECKS---START---.*---MARK---INCLUDE-CHECKS---END#s", "$inccode", $in);
 
 if(file_put_contents($TMPFILE, $tempcode)){
-    echo "✅ OK $TMPFILE was written\n";
+    _ok("$TMPFILE was written");
 }
 
+// ----------------------------------------------------------------------
 
 _h1("Compile");
 _chdir("$selfdir/$dirBuild");
@@ -54,14 +57,25 @@ _exec("$SPC \
     '$TMPFILE' \
     -O '$OUTFILE'");
 
-if(unlink($TMPFILE)){
-    echo "Cleanup: $TMPFILE was deleted\n";
-}
+// if(unlink($TMPFILE)){
+//     echo "Cleanup: $TMPFILE was deleted\n";
+// }
+
+// ----------------------------------------------------------------------
 
 _h1("Test generated binary");
 
 _exec("file '$OUTFILE'");
-_exec("'$OUTFILE' --verbose --ini=../src/simple.ini");
+_exec("'$OUTFILE' --ini=../src/simple.ini");
 
+_h1("Test binary in other location");
+$testfile="/tmp/".basename($OUTFILE);
+_exec("cp '$OUTFILE' '$testfile'");
+_exec("'$testfile' --ini=$selfdir/src/simple.ini");
+_exec("rm '$testfile'");
+
+// ----------------------------------------------------------------------
 
 _h1("Done");
+
+// ----------------------------------------------------------------------
diff --git a/inc_functions.php b/inc_functions.php
index c5a1a4d..075db46 100644
--- a/inc_functions.php
+++ b/inc_functions.php
@@ -6,6 +6,13 @@
 function _h1($s): void
 {
     echo "\n>>>>>>>>>> $s\n";
+    
+    /*
+    echo "\n   __".str_repeat("_", strlen($s))."\n";
+    echo "__/ ".str_repeat(" ", strlen($s))." \___".str_repeat("_", 70-strlen($s))."\n";
+    echo "    $s\n";
+    echo str_repeat(".", 79)."\n\n";
+    */
 }
 function _abort($sMessage, $iExitcode = 1): never
 {
@@ -13,6 +20,15 @@ function _abort($sMessage, $iExitcode = 1): never
     exit($iExitcode);
 }
 
+function _ok(string $sMessage=""): void
+{
+    echo "✅ OK $sMessage\n";
+}
+function _skip(string $sMessage=""): void
+{
+    echo "🔹 SKIP: $sMessage\n";
+}
+
 function _chdir($sDir): void
 {
     if (!is_dir($sDir)) {
@@ -32,16 +48,16 @@ function _exec($cmd): void
     echo "cmd > $cmd\n";
     exec("$cmd 2>&1", $aOut, $rc);
 
-    if(!count($aOut)) {
-        $aOut=["-- no output --"];
+    if (!count($aOut)) {
+        $aOut = ["-- no output --"];
     }
-    echo implode("\n", $aOut)."\n";
+    echo implode("\n", $aOut) . "\n";
 
     if ($rc != 0) {
         echo "rc=$rc ";
         _abort("Error: Command failed. Aborting.", $rc);
     }
-    echo "✅ OK\n";
+    _ok();
 }
 
 function _mkdir(string $sMyDir): void
@@ -51,9 +67,153 @@ function _mkdir(string $sMyDir): void
         if (!mkdir($sMyDir, 0755, true)) {
             _abort("ERROR: mkdir failed.");
         }
-        echo "✅ OK\n\n";
+        _ok();
+        echo "\n";
     } else {
-        echo "⏩ SKIP mkdir: already exists: '$sMyDir'\n";
+        _skip("mkdir: already exists: '$sMyDir");
+    }
+
+}
+
+
+/**
+ * Compress PHP code
+ * from https://www.php.net/manual/de/function.php-strip-whitespace.php
+ * with removed lowercase function
+ * 
+ * @param string $src  source code or filename
+ * @return bool|string
+ */
+function compress_php_src(string $src): bool|string
+{
+    // Whitespaces left and right from this signs can be ignored
+    static $IW = array(
+    T_CONCAT_EQUAL, // .=
+    T_DOUBLE_ARROW, // =>
+    T_BOOLEAN_AND, // &&
+    T_BOOLEAN_OR, // ||
+    T_IS_EQUAL, // ==
+    T_IS_NOT_EQUAL, // != or <>
+    T_IS_SMALLER_OR_EQUAL, // <=
+    T_IS_GREATER_OR_EQUAL, // >=
+    T_INC, // ++
+    T_DEC, // --
+    T_PLUS_EQUAL, // +=
+    T_MINUS_EQUAL, // -=
+    T_MUL_EQUAL, // *=
+    T_DIV_EQUAL, // /=
+    T_IS_IDENTICAL, // ===
+    T_IS_NOT_IDENTICAL, // !==
+    T_DOUBLE_COLON, // ::
+    T_PAAMAYIM_NEKUDOTAYIM, // ::
+    T_OBJECT_OPERATOR, // ->
+    T_DOLLAR_OPEN_CURLY_BRACES, // ${
+    T_AND_EQUAL, // &=
+    T_MOD_EQUAL, // %=
+    T_XOR_EQUAL, // ^=
+    T_OR_EQUAL, // |=
+    T_SL, // <<
+    T_SR, // >>
+    T_SL_EQUAL, // <<=
+    T_SR_EQUAL, // >>=
+    );
+    if (is_file($src)) {
+        if (!$src = file_get_contents($src)) {
+            return false;
+        }
     }
+    $tokens = token_get_all($src);
 
+    $new = "";
+    $c = sizeof($tokens);
+    $iw = false; // ignore whitespace
+    $ih = false; // in HEREDOC
+    $ls = "";    // last sign
+    $ot = null;  // open tag
+    for ($i = 0; $i < $c; $i++) {
+        $token = $tokens[$i];
+        if (is_array($token)) {
+            list($tn, $ts) = $token; // tokens: number, string, line
+            $tname = token_name($tn);
+            if ($tn == T_INLINE_HTML) {
+                $new .= $ts;
+                $iw = false;
+            } else {
+                if ($tn == T_OPEN_TAG) {
+                    if (strpos($ts, " ") || strpos($ts, "\n") || strpos($ts, "\t") || strpos($ts, "\r")) {
+                        $ts = rtrim($ts);
+                    }
+                    $ts .= " ";
+                    $new .= $ts;
+                    $ot = T_OPEN_TAG;
+                    $iw = true;
+                } elseif ($tn == T_OPEN_TAG_WITH_ECHO) {
+                    $new .= $ts;
+                    $ot = T_OPEN_TAG_WITH_ECHO;
+                    $iw = true;
+                } elseif ($tn == T_CLOSE_TAG) {
+                    if ($ot == T_OPEN_TAG_WITH_ECHO) {
+                        $new = rtrim($new, "; ");
+                    } else {
+                        $ts = " " . $ts;
+                    }
+                    $new .= $ts;
+                    $ot = null;
+                    $iw = false;
+                } elseif (in_array($tn, $IW)) {
+                    $new .= $ts;
+                    $iw = true;
+                } elseif ($tn == T_CONSTANT_ENCAPSED_STRING || $tn == T_ENCAPSED_AND_WHITESPACE) {
+                    if ($ts[0] == '"') {
+                        $ts = addcslashes($ts, "\n\t\r");
+                    }
+                    $new .= $ts;
+                    $iw = true;
+                } elseif ($tn == T_WHITESPACE) {
+                    $nt = @$tokens[$i + 1];
+                    if (!$iw && (!is_string($nt) || $nt == '$') && !in_array($nt[0], $IW)) {
+                        $new .= " ";
+                    }
+                    $iw = false;
+                } elseif ($tn == T_START_HEREDOC) {
+                    $new .= "<<<S\n";
+                    $iw = false;
+                    $ih = true; // in HEREDOC
+                } elseif ($tn == T_END_HEREDOC) {
+                    $new .= "S;";
+                    $iw = true;
+                    $ih = false; // in HEREDOC
+                    for ($j = $i + 1; $j < $c; $j++) {
+                        if (is_string($tokens[$j]) && $tokens[$j] == ";") {
+                            $i = $j;
+                            break;
+                        } else if ($tokens[$j][0] == T_CLOSE_TAG) {
+                            break;
+                        }
+                    }
+                } elseif ($tn == T_COMMENT || $tn == T_DOC_COMMENT) {
+                    $iw = true;
+                } else {
+                    /*
+                     * Axel: DISABLE lowercase - it has bad impact on constants
+                     * 
+                      if (!$ih) {
+                      $ts = strtolower($ts);
+                      }
+                     * 
+                     */
+                    $new .= $ts;
+                    $iw = false;
+                }
+            }
+            $ls = "";
+        } else {
+            if (($token != ";" && $token != ":") || $ls != $token) {
+                $new .= $token;
+                $ls = $token;
+            }
+            $iw = true;
+        }
+    }
+    return $new;
 }
\ No newline at end of file
diff --git a/installer.php b/installer.php
index 42dd1d1..00c4e6b 100755
--- a/installer.php
+++ b/installer.php
@@ -66,36 +66,62 @@ if(!is_dir("appmonitor")){
 // ----------------------------------------------------------------------
 
 _h1("Generate include file with all available checks...");
+// _exec("ln -s $selfdir/$dirExternal/appmonitor/public_html/client $selfdir/src");
+
 $incfile="$selfdir/src/include_checks.php";
-_chdir("$selfdir/$dirExternal");
-$out="<?php
+_chdir("$selfdir");
 
-// This file was generated by 
-// ".__FILE__." 
-// at ".(date("Y-m-d H:i:s"))."
+$aFiles2Merge=array_merge(
+    [
+        "$dirExternal/appmonitor/public_html/client/classes/appmonitor-checks.class.php",
+        "$dirExternal/appmonitor/public_html/client/classes/appmonitor-client.class.php",
+    ],
+    glob("$dirExternal/appmonitor/public_html/client/plugins/checks/*php")
+);
+
+$out="<?php
 
+/*
+    This file was generated by 
+    ".__FILE__." 
+    at ".(date("Y-m-d H:i:s"))."
 
-// ----- appmonitor client classes
-require_once('../$dirExternal/appmonitor/public_html/client/classes/appmonitor-checks.class.php');
-require_once('../$dirExternal/appmonitor/public_html/client/classes/appmonitor-client.class.php');
+    merged files:
+    ".implode("\n    ", $aFiles2Merge)."
 
-// ----- appmonitor checks
+*/
 ";
-foreach(glob("appmonitor/public_html/client/plugins/checks/*php") as $sFile){
-    $out.="require_once('../$dirExternal/$sFile');\n";
+
+foreach($aFiles2Merge as $sMyFile){
+    $sSource=file_get_contents($sMyFile);
+    $sSource=preg_replace(
+        [
+            "/(require[\ \_].*;)/",
+            "/(\<\?php)/"
+        ], 
+        [
+            "// REMOVED-BY-MERGER: $1",
+            "",
+        ], 
+        $sSource
+    );
+    $out.="$sSource\n";
 }
 
+
 if(file_put_contents($incfile, $out)){
-    echo "✅ OK $incfile was written\n";
+    _ok("$incfile was written");
 } 
 
+_exec("php -l '$incfile'");
+
 // ----------------------------------------------------------------------
 _h1("Get / update spc");
 _chdir("$selfdir/$dirExternal/bin");
 if(!file_exists($SPC)){
     _exec("wget -O $SPC '$spcUrl'");
 } else {
-    echo "⏩ SKIP download of spc.\n";
+    _skip("download of spc.");
 }
 
 if (PHP_OS == "Linux") {
@@ -114,7 +140,7 @@ if(!file_exists("$doneBuild")){
     _exec("$SPC build --no-interaction --build-micro '$php_libs'");
     touch("$doneBuild");
 } else {
-    echo "⏩ SKIP: Micro already built - php $php_version - extensions '$php_libs'\n";
+    _skip("Micro already built - php $php_version - extensions '$php_libs'");
 }
 
 
diff --git a/src/amcli.php b/src/amcli.php
index feeef54..1d1d8d8 100755
--- a/src/amcli.php
+++ b/src/amcli.php
@@ -1,18 +1,18 @@
 #!/usr/bin/env php
 <?php
 
-chdir(__DIR__);
+# chdir(__DIR__);
 $FLAG_DEBUG = 0;
 $VERSION = "0.0.1";
 
 // ---MARK---INCLUDE-CHECKS---START---
-if (!file_exists("include_checks.php")) {
+if (!file_exists(__DIR__."/include_checks.php")) {
     echo "ERROR: File 'include_checks.php' does not exist yet..\n";
     echo "Run the ../installer.php first!\n";
     exit(1);
 }
 
-if (!include "include_checks.php") {
+if (!include __DIR__."/include_checks.php") {
     echo "ERROR: Include of generated 'include_checks.php' failed.\n";
     echo "Check its generation by installer or run the installer again.\n";
     exit(2);
@@ -84,7 +84,7 @@ You can use the compiled binary on non PHP systems.
     !!! This tool is in alpha stadium !!!
 
 
-SYNTAX: $_self [OPTIONS]
+SYNTAX: $_self [OPTIONS] -i <INIFILE>
 
 OPTIONS:
     -h, --help      Print this help and exit
@@ -136,12 +136,17 @@ $oMonitor = new appmonitor();
 
 // show builtin checks
 if (isset($ARGS['-l']) || isset($ARGS['--list'])) {
+    _wd("Showing checks");
     echo implode("\n", $oMonitor->listChecks());
     exit(0);
 }
 
+$inifile = $ARGS["--ini"] ?? ($ARGS["-i"] ?? "");
+if (!$inifile) {
+    echo "ERROR: Set an INI File using -i=<FILE> (or --ini=<FILE>).\n";
+    exit(1);
+}
 
-$inifile = $ARGS["--ini"] ?? ($ARGS["-i"] ?? "simple.ini");
 _wd("Using ini file '$inifile'.");
 
 if (!file_exists($inifile)) {
@@ -165,12 +170,14 @@ _set("setHost", $aIni['meta']['host'] ?? null);
 _set("setWebsite", $aIni['meta']['website'] ?? null);
 _set("setTtl", $aIni['meta']['ttl'] ?? null);
 
-foreach ($aIni['notification']['email'] ?? [] as $sValue) {
+foreach ($aIni['notifications']['email'] ?? [] as $sValue) {
     _set("addEmail", $sValue);
 }
-foreach ($aIni['notification']['slack'] ?? [] as $sValue) {
-    $sChannel = preg_filter('/,.*/', '', $sValue);
-    $sWebhook = preg_filter('/^.*,/', '', $sValue);
+foreach ($aIni['notifications']['slack'] ?? [] as $sValue) {
+
+    $aArray = json_decode($sValue, 1);
+    $sChannel = array_keys($aArray)[0];
+    $sWebhook = array_values($aArray)[0];
 
     _set("addSlackWebhook", $sChannel, $sWebhook);
 }
@@ -178,7 +185,7 @@ foreach ($aIni['notification']['slack'] ?? [] as $sValue) {
 // loop over checks
 $aChecks = $aIni;
 unset($aChecks["meta"]);
-unset($aChecks["notification"]);
+unset($aChecks["notifications"]);
 
 foreach ($aChecks as $sKey => $aCheck) {
     $aChecks[$sKey]['name'] = $aCheck['name'] ?? $sKey;
@@ -191,7 +198,7 @@ foreach ($aChecks as $sKey => $aCheck) {
         }
     }
 
-    _wd("Execute Check '$sKey'");
+    _wd("Execute Check '$sKey' with params: " . print_r($aArray ?? [], 1));
     $oMonitor->addCheck([
         "name" => $aCheck['name'] ?? $sKey,
         "description" => $aCheck['description'],
diff --git a/src/simple.ini b/src/simple.ini
index 61897d1..56e1efa 100644
--- a/src/simple.ini
+++ b/src/simple.ini
@@ -22,13 +22,14 @@ ttl = 300
 tags[]="monitoring"
 
 
-[notification]
+[notifications]
 email[]="support@example.com"
 email[]="webmaster@example.com"
 
 ; for slack use the following format
 ; <channelname> + comma + <webhook url>
-slack[]="#support-channel,https://hooks.slack.com/services/XXXXXX/YYYYYY/ZZZZZ"
+; slack[]="#support-channel,https://hooks.slack.com/services/XXXXXX/YYYYYY/ZZZZZ"
+slack[]='{ "#support-channel": "https://hooks.slack.com/services/XXXXXX/YYYYYY/ZZZZZ" }'
 
 
 ; -----------------------------------------------------------------------
-- 
GitLab