#include "minicurl.hh" #include #include "navmon.hh" #include "fmt/format.h" #include "fmt/printf.h" #include "ext/powerblog/h2o-pp.hh" #include #include "githash.h" using namespace std; /* Monitoring the satellites for sensible alerts. A satellite transitions state if: * galmon is up * galmon is up to date * We are seeing recent messages for the satellite * For more than 60 seconds this state is different than it was A satellite becomes 'unobserved' if: * galmon is up * galmon is up to date * We haven't seen a message since an hour A satellite becomes observed again if we have seen it for > 60 seconds. At startup, every satellite assumes, without alert, the status we find for it, which is not a transition. */ class StateKeeper { public: typedef std::variant var_t; void setBoolNames(string_view name, string_view offName, string_view onName); std::optional reportState(string_view thing, string_view name, var_t state, time_t now=0); std::optional getState(string_view thing, string_view name); std::optional getPrevState(string_view thing, string_view name); private: struct Names { string offName, onName; }; map names; struct State { var_t state; time_t since; }; struct ThingState { std::optional prev, live, provisional; }; map > states; private: std::string getName(string_view name, var_t state) { if(auto ptr = std::get_if(&state)) { if(*ptr) { return names[(string)name].onName; } else return names[(string)name].offName; } if(auto ptr = std::get_if(&state)) { return *ptr; } if(auto ptr = std::get_if(&state)) { return to_string(*ptr); } return "?"; } }; void StateKeeper::setBoolNames(string_view name, string_view offName, string_view onName) { names[(string)name].offName = offName; names[(string)name].onName = onName; } std::optional StateKeeper::getState(string_view thing, string_view name) { if(states.count((string)thing) && states[(string)thing].count((string) name) && states[(string)thing][(string)name].live) { return getName(name, states[(string)thing][(string)name].live->state); } return std::optional(); } std::optional StateKeeper::getPrevState(string_view thing, string_view name) { if(states.count((string)thing) && states[(string)thing].count((string) name) && states[(string)thing][(string)name].prev) { return getName(name, states[(string)thing][(string)name].prev->state); } return std::optional(); } std::optional StateKeeper::reportState(string_view thing, string_view name, var_t newstate, time_t now) { auto& state = states[(string)thing][(string)name]; std::optional ret; if(!now) now = time(0); if(!state.live) { // we had no state yet state.live = State{newstate, now}; state.provisional.reset(); // for good measure return ret; } else if(state.live->state == newstate) { // confirmation of current state state.provisional.reset(); return ret; } else if(!state.provisional) { // new provisional state state.provisional = State{newstate, now}; return ret; } else { if(now - state.provisional->since > 60) { // provisional state has been confirmed state.prev = state.live; state.live = state.provisional; state.provisional.reset(); return getName(name, state.live->state); } } return ret; } StateKeeper g_sk; static std::string string_replace(const std::string& str, const std::string& match, const std::string& replacement, unsigned int max_replacements = UINT_MAX) { size_t pos = 0; std::string newstr = str; unsigned int replacements = 0; while ((pos = newstr.find(match, pos)) != std::string::npos && replacements < max_replacements) { newstr.replace(pos, match.length(), replacement); pos += replacement.length(); replacements++; } return newstr; } void sendTweet(const string& tweet) { string etweet = string_replace(tweet, "+", "%2b"); system((string("twurl -X POST \"/1.1/statuses/update.json?status=")+etweet+"\" >> twitter.log").c_str()); } int main(int argc, char **argv) { MiniCurl mc; MiniCurl::MiniCurlHeaders mch; // string url="https://galmon.eu/svs.json"; string url="http://[::1]:10000/"; g_sk.setBoolNames("health", "healthy", "unhealthy"); g_sk.setBoolNames("eph-too-old", "ephemeris fresh", "ephemeris too old"); g_sk.setBoolNames("silent", "observed", "not observed"); std::variant tst; extern const char* g_gitHash; auto observers = nlohmann::json::parse(mc.getURL(url+"observers.json")); cout<<("Galmonmon " +string(g_gitHash)+ " started, " + std::to_string(observers.size()) +" observers seen")< 30) { cout<<"Galmon at "< tooOldChange; if(gnssid == 2) tooOldChange = g_sk.reportState(fullName, "eph-too-old", sv["eph-age-m"] > 120); else tooOldChange = g_sk.reportState(fullName, "eph-too-old", sv["eph-age-m"] > 140); auto seenChange = g_sk.reportState(fullName, "silent", notseen); auto sisaChange = g_sk.reportState(fullName, "sisa", (string)sv["sisa"]); /* cout<