diff --git a/src/NetworkSource.cpp b/src/NetworkSource.cpp index a89e140..d0feb3c 100644 --- a/src/NetworkSource.cpp +++ b/src/NetworkSource.cpp @@ -30,7 +30,7 @@ static char *get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen) { return s; } -std::shared_ptr NetworkSource::Create(MainLoop &mainLoop) { +std::shared_ptr NetworkSource::Create(std::shared_ptr mainloop) { auto sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) { return nullptr; @@ -47,12 +47,15 @@ std::shared_ptr NetworkSource::Create(MainLoop &mainLoop) { spdlog::error("Failed to set timer: {}", strerror(errno)); return nullptr; } - auto source = std::shared_ptr(new NetworkSource(sock, fd)); - mainLoop.RegisterIoHandler(fd, "NetworkSource", source); + auto source = std::shared_ptr(new NetworkSource(mainloop, sock, fd)); + mainloop->RegisterIoHandler(fd, "NetworkSource", source); return source; } -void NetworkSource::Initialize() { ReadState(); } +void NetworkSource::Initialize() { + ReadState(); + m_drawn = m_published = false; +} void NetworkSource::ReadState() { char buf[8192] = {0}; @@ -65,7 +68,15 @@ void NetworkSource::ReadState() { } auto ifr = ifc.ifc_req; auto nInterfaces = ifc.ifc_len / sizeof(struct ifreq); - Networks networks; + bool alerted = false; + bool changed = false; + // Note that only interfaces that are up is iterated here + // so to detect interfaces that were up but now is down, clear + // all interfaces first + for (auto &keyValue : m_networks) { + auto &network = keyValue.second; + network.isUp = false; + } for (unsigned long i = 0; i < nInterfaces; i++) { auto item = &ifr[i]; auto addr = &item->ifr_addr; @@ -79,19 +90,44 @@ void NetworkSource::ReadState() { } // extended flags SIOCGIFPFLAGS // SIOCGIWSTATS - NetworkState network = {}; + NetworkState network = {.isAlerted = false}; network.isUp = (item->ifr_flags & IFF_UP) != 0; // Get interface address if (ioctl(m_socket, SIOCGIFADDR, item) < 0) { continue; } - network.interface = item->ifr_name; network.address = get_ip_str(addr, ip, sizeof(ip)); - networks.push_back(std::move(network)); + if (m_networks.contains(item->ifr_name)) { + // Existing network. Might have changed from down -> up + // or changed address. + auto &existing = m_networks[item->ifr_name]; + if (existing != network) { + changed = true; + m_networks[item->ifr_name] = std::move(network); + } else { + existing.isUp = true; + existing.isAlerted = false; + } + } else { + // New network + m_networks[item->ifr_name] = std::move(network); + changed = true; + // Is this an alert? A positive one perhaps. + } + } + // Check if there is any networks that has gone down + for (auto &keyValue : m_networks) { + auto &network = keyValue.second; + if (!network.isUp && !network.isAlerted) { + network.isAlerted = true; + alerted = true; + changed = true; + } } - if (m_networks != networks) { - m_drawn = m_published = false; - m_networks = networks; + m_drawn = m_published = !changed; + if (alerted) { + spdlog::info("Network source is triggering alert"); + m_mainloop->AlertAndWakeup(); } } diff --git a/src/NetworkSource.h b/src/NetworkSource.h index 91acca7..dbce79c 100644 --- a/src/NetworkSource.h +++ b/src/NetworkSource.h @@ -8,16 +8,18 @@ class NetworkSource : public Source, public IoHandler { public: - static std::shared_ptr Create(MainLoop& mainLoop); + static std::shared_ptr Create(std::shared_ptr mainloop); void Initialize(); - void ReadState(); virtual bool OnRead() override; void Publish(const std::string_view sourceName, ScriptContext& scriptContext) override; virtual ~NetworkSource() { close(m_timerfd); } private: - NetworkSource(int socket, int timerfd) : Source(), m_socket(socket), m_timerfd(timerfd) {} + NetworkSource(std::shared_ptr mainloop, int socket, int timerfd) + : Source(), m_mainloop(mainloop), m_socket(socket), m_timerfd(timerfd) {} + void ReadState(); + std::shared_ptr m_mainloop; int m_socket; int m_timerfd; std::shared_ptr m_scriptContext; diff --git a/src/ScriptContext.cpp b/src/ScriptContext.cpp index 7d2cb29..ac23c31 100644 --- a/src/ScriptContext.cpp +++ b/src/ScriptContext.cpp @@ -379,10 +379,12 @@ void ScriptContextImpl::Publish(const std::string_view name, const KeyboardState } void ScriptContextImpl::Publish(const std::string_view name, const Networks& networks) { auto networksTable = m_lua.create_table(); - for (const auto& network : networks) { + for (const auto& keyValue : networks) { + const auto& interface = keyValue.first; + const auto& network = keyValue.second; auto networkTable = m_lua.create_table(); networkTable["up"] = network.isUp; - networkTable["interface"] = network.interface; + networkTable["interface"] = interface; networkTable["address"] = network.address; networksTable.add(networkTable); } diff --git a/src/ScriptContext.h b/src/ScriptContext.h index 7928dda..c2e6dc2 100644 --- a/src/ScriptContext.h +++ b/src/ScriptContext.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "src/Configuration.h" @@ -61,15 +62,15 @@ struct KeyboardState { }; struct NetworkState { + bool isAlerted; bool isUp; - std::string interface; std::string address; // type (eth, wifi) // wifi strength auto operator<=>(const NetworkState& other) const = default; }; -using Networks = std::vector; +using Networks = std::map; class ScriptContext { public: diff --git a/src/main.cpp b/src/main.cpp index 4c24c53..43ff44f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -57,7 +57,7 @@ static void InitializeSource(const std::string& source, Sources& sources, return; } if (source == "networks") { - auto networkSource = NetworkSource::Create(*mainLoop); + auto networkSource = NetworkSource::Create(mainLoop); if (!networkSource) { spdlog::error("Failed to initialize network source"); return;