diff --git a/html/style.css b/html/style.css
index 76e5c0e..12049ce 100644
--- a/html/style.css
+++ b/html/style.css
@@ -212,6 +212,7 @@ td {
border: 1px solid var(--clr-primary-fg);
padding: var(--size-xs);
vertical-align: top;
+ white-space: pre-wrap;
}
th {
diff --git a/luabridge.cc b/luabridge.cc
index 2514f77..33a428b 100644
--- a/luabridge.cc
+++ b/luabridge.cc
@@ -119,6 +119,9 @@ void initLua()
g_checkers.emplace_back(make_unique(data));
});
+ g_lua.set_function("external", [&](sol::table data) {
+ g_checkers.emplace_back(make_unique(data));
+ });
g_lua.set_function("smtp", [&](sol::table data) {
g_checkers.emplace_back(make_unique(data));
diff --git a/manual.md b/manual.md
index d452361..dcc646b 100644
--- a/manual.md
+++ b/manual.md
@@ -92,6 +92,15 @@ dnssoa{domain="berthub.eu", servers= nameservers}
dnssoa{domain="hubertnet.nl", servers= nameservers}
```
+## external
+
+Execute some external program, and check whether the output matches a regex and/or a return code:
+
+```lua
+external{cmd="do-thing", regex="success"}
+external{cmd="do-other-thing", rc=0}
+```
+
## httpredir
Does the http redirect work? TBC. Example:
diff --git a/netmon.cc b/netmon.cc
index 76a6280..4204ef6 100644
--- a/netmon.cc
+++ b/netmon.cc
@@ -531,3 +531,54 @@ CheckResult PINGChecker::perform()
}
return ret;
}
+
+
+// Based on: https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po
+std::pair exec(const char* cmd) {
+ char buffer[128];
+ std::string result = "";
+ int rc;
+ FILE* pipe = popen(cmd, "r");
+ if (!pipe) return std::pair (255, "");
+ try {
+ while (fgets(buffer, sizeof buffer, pipe) != NULL) {
+ result += buffer;
+ }
+ } catch (...) {
+ pclose(pipe);
+ throw;
+ }
+ rc = pclose(pipe) / 256;
+ std::pair ret(rc, result);
+ return ret;
+}
+
+ExternalChecker::ExternalChecker(sol::table data) : Checker(data)
+{
+ checkLuaTable(data, {"cmd"}, {"regex", "rc"});
+
+ d_cmd = data.get("cmd");
+ d_exp = data.get_or("regex", "");
+ d_rc = data.get_or("rc", -1);
+}
+
+CheckResult ExternalChecker::perform()
+{
+ std::pair output = exec(d_cmd.c_str());
+
+ d_results.clear();
+ d_results[d_cmd]["rc"] = output.first;
+ d_results[d_cmd]["output"] = output.second;
+
+ if (d_exp != "") {
+ if (!std::regex_search(output.second, std::regex(d_exp))) {
+ return fmt::format("External check \"{}\" against \"{}\" failed, actual output: \"{}\"", d_cmd, d_exp, output.second);
+ }
+ }
+ if (d_rc != -1) {
+ if (output.first != d_rc) {
+ return fmt::format("External check \"{}\" expected rc \"{}\", received: \"{}\"", d_cmd, d_rc, output.first);
+ }
+ }
+ return "";
+}
diff --git a/simplomon.hh b/simplomon.hh
index d6dc6eb..91adf26 100644
--- a/simplomon.hh
+++ b/simplomon.hh
@@ -215,6 +215,24 @@ private:
};
+class ExternalChecker : public Checker
+{
+public:
+ ExternalChecker(sol::table data);
+ CheckResult perform() override;
+ std::string getCheckerName() override { return "external"; }
+ std::string getDescription() override
+ {
+ return fmt::format("External check {}", d_cmd);
+ }
+
+private:
+ std::string d_cmd;
+ std::string d_exp;
+ int d_rc;
+};
+
+
class HTTPSChecker : public Checker
{
public: