Loading CMakeLists.txt +1 −1 Changes for CMakeLists.txt: 1 added line, 1 removed line. Original line number Diff line number Diff line cmake_minimum_required(VERSION 3.18) project(syslite VERSION 0.1.0 LANGUAGES CXX) project(syslite VERSION 1.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) Loading include/sysbus/client.h +51 −0 Changes for include/sysbus/client.h: 51 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -62,6 +62,15 @@ constexpr const char *StopInterface = "StopInterface"; constexpr const char *RestartInterface = "RestartInterface"; constexpr const char *ReloadInterface = "ReloadInterface"; // no interface: all constexpr const char *ForceReloadInterface = "ForceReloadInterface"; // no interface: all // Bonds and bridges: .netdev files in /etc/syslite/network, their members // get member .network files (Bond=/Bridge=). Create/Edit change only the // files (ReloadInterface without an interface creates the device and adds // its members); Delete also removes the device. Root only. constexpr const char *ListNetDevs = "ListNetDevs"; // -> netdev, ... (repeated) constexpr const char *CreateNetDev = "CreateNetDev"; // netdev, kind, ... constexpr const char *EditNetDev = "EditNetDev"; // netdev, ... constexpr const char *DeleteNetDev = "DeleteNetDev"; // netdev } // namespace method namespace signal { Loading Loading @@ -127,6 +136,40 @@ struct InterfaceInfo { std::string configAddress; // [Network] Address= std::string gateway; std::vector<std::string> dns; std::string kind; // "bond", "bridge" or "" (what the device is) std::string master; // the bond/bridge it is a member of now std::string memberOf; // configured: [Network] Bond= or Bridge= }; // A bond or bridge (.netdev). For a bond: mode (balance-rr, active-backup, // balance-xor, broadcast, 802.3ad, balance-tlb, balance-alb), MII // monitoring interval, LACP rate (802.3ad: slow/fast), transmit hash policy // (layer2, layer3+4, layer2+3, ...). For a bridge: STP. struct NetDevInfo { std::string name; // the interface, e.g. bond0 std::string kind; // bond or bridge std::string mode; int miimonMs = 0; std::string lacpRate; std::string hashPolicy; bool stp = false; std::string config; // file name without ".netdev" std::string configPath; bool exists = false; // the device is there std::vector<std::string> members; // configured members }; // Create: netdev and kind required; Edit: only what is set changes, kind // can't. members, if set, becomes the complete member list. struct NetDevSettings { std::string netdev; std::string kind; std::optional<std::string> mode; std::optional<int> miimonMs; std::optional<std::string> lacpRate; std::optional<std::string> hashPolicy; std::optional<bool> stp; std::optional<std::vector<std::string>> members; }; // Create: interface is required; the configuration is named Loading Loading @@ -206,6 +249,12 @@ public: bool reloadInterface(const std::string &name = ""); // empty: all bool forceReloadInterface(const std::string &name = ""); // empty: all // Bonds and bridges: true on success, otherwise see lastError(). std::vector<NetDevInfo> listNetDevs(); bool createNetDev(const NetDevSettings &settings); bool editNetDev(const NetDevSettings &settings); bool deleteNetDev(const std::string &netdev); // Signals: subscribe once, then waitSignal() returns the next one // (false on timeout or a closed connection; timeoutMs < 0 waits forever). bool subscribe(); Loading @@ -232,5 +281,7 @@ private: UnitInfo unitFromFields(const std::vector<std::pair<std::string, std::string>> &fields); // Reads an InterfaceInfo from a GetInterface reply / ListInterfaces entry. InterfaceInfo interfaceFromFields(const std::vector<std::pair<std::string, std::string>> &fields); // Reads a NetDevInfo from a ListNetDevs entry. NetDevInfo netDevFromFields(const std::vector<std::pair<std::string, std::string>> &fields); } // namespace sysbus src/network.cpp +455 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes src/network.h +56 −0 Changes for src/network.h: 56 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -20,6 +20,11 @@ namespace syslite { std::string gateway; // Gateway=192.168.1.1 std::vector<std::string> dns; // DNS=8.8.8.8 // [Network] Bond=/Bridge=: the interface is a member of that bond or // bridge (and gets no addresses of its own) std::string bond; std::string bridge; // [Link] bool linkUp = true; Loading @@ -28,6 +33,29 @@ namespace syslite { std::string name; }; // Parsed from systemd-networkd .netdev files: a bond or a bridge that // syslite creates; its members are the interfaces whose .network says // Bond=<name> / Bridge=<name> struct NetDevConfig { // [NetDev] std::string ifname; // Name=bond0 std::string kind; // Kind=bond | bridge // [Bond] std::string mode = "balance-rr"; // Mode=balance-rr, active-backup, balance-xor, // broadcast, 802.3ad, balance-tlb, balance-alb int miimonMs = 100; // MIIMonitorSec= (link monitoring) std::string lacpRate; // LACPTransmitRate=slow | fast (802.3ad) std::string hashPolicy; // TransmitHashPolicy=layer2 | layer3+4 | layer2+3 | ... // [Bridge] bool stp = false; // STP=yes // Metadata std::string filePath; std::string name; // file name without ".netdev" }; // Runtime state of one network interface (from /sys/class/net and netlink) struct InterfaceStatus { std::string name; Loading @@ -36,6 +64,8 @@ namespace syslite { bool adminUp = false; // IFF_UP bool carrier = false; bool stopped = false; // stopped on request (stays down) std::string kind; // "bond", "bridge" or "" (/sys/class/net/<if>/bonding, /bridge) std::string master; // the bond or bridge it is a member of std::vector<std::string> addresses; // "a.b.c.d/len", IPv6 too const NetworkConfig *config = nullptr; // first matching configuration }; Loading Loading @@ -104,6 +134,20 @@ namespace syslite { // Re-read the .network files only (nothing is applied) void reloadConfigs(); // Bonds and bridges (.netdev files in ConfigDir). saveNetDev writes // ConfigDir/<nd.name>.netdev and makes exactly `members` its members: // each gets a member configuration ConfigDir/10-<member>.network // (Bond=/Bridge=), former members lose theirs. deleteNetDev removes // the file, the member configurations and the device itself. Both // re-read the files; applyNetDevs() (also part of applyAll/reload) // creates what is missing. const std::vector<NetDevConfig> &netDevs() const { return _netdevs; } const NetDevConfig *findNetDev(const std::string &ifname) const; std::vector<std::string> membersOf(const std::string &ifname) const; bool saveNetDev(const NetDevConfig &nd, const std::vector<std::string> &members, std::string *err); bool deleteNetDev(const std::string &ifname, std::string *err); void applyNetDevs(); // Start listening for new network devices (netlink RTNLGRP_LINK) // Returns fd to poll in the main loop (-1 on failure) int startDeviceMonitor(); Loading @@ -117,6 +161,17 @@ namespace syslite { private: // Parse a single .network file bool parseNetworkFile(const std::string &path, NetworkConfig &cfg); // ...and .netdev file bool parseNetDevFile(const std::string &path, NetDevConfig &nd); bool writeNetworkFile(const NetworkConfig &cfg, std::string *err); // Netlink: create a bond/bridge (RTM_NEWLINK with IFLA_LINKINFO), // delete a link, make ifname a member of master ("": none) bool createLink(const NetDevConfig &nd); bool deleteLink(const std::string &ifname); bool setMaster(const std::string &ifname, const std::string &master); // does the existing device match nd (kind, bond mode, STP)? bool linkMatches(const NetDevConfig &nd); // Match an interface name against a pattern (supports trailing *) bool matchInterface(const std::string &pattern, const std::string &ifname); Loading Loading @@ -161,6 +216,7 @@ namespace syslite { static std::string formatIpv4(uint32_t ip); std::vector<NetworkConfig> _configs; std::vector<NetDevConfig> _netdevs; std::map<std::string, std::vector<std::string>> _dnsByIf; // DNS servers per interface std::set<std::string> _configured; // interfaces configureInterface() set up std::set<std::string> _stopped; // interfaces stopped on request Loading src/sysbus/client.cpp +58 −0 Changes for src/sysbus/client.cpp: 58 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -72,10 +72,32 @@ InterfaceInfo interfaceFromFields(const std::vector<std::pair<std::string, std:: else if (k == "config_address") i.configAddress = v; else if (k == "gateway") i.gateway = v; else if (k == "dns") i.dns.push_back(v); else if (k == "kind") i.kind = v; else if (k == "master") i.master = v; else if (k == "member_of") i.memberOf = v; } return i; } NetDevInfo netDevFromFields(const std::vector<std::pair<std::string, std::string>> &fields) { NetDevInfo n; for (const auto &f : fields) { const std::string &k = f.first, &v = f.second; if (k == "netdev") n.name = v; else if (k == "kind") n.kind = v; else if (k == "mode") n.mode = v; else if (k == "miimon_ms") n.miimonMs = std::atoi(v.c_str()); else if (k == "lacp_rate") n.lacpRate = v; else if (k == "hash_policy") n.hashPolicy = v; else if (k == "stp") n.stp = v == "yes"; else if (k == "config") n.config = v; else if (k == "config_path") n.configPath = v; else if (k == "exists") n.exists = v == "yes"; else if (k == "member") n.members.push_back(v); } return n; } Client::~Client() { close(); } bool Client::connect(const std::string &path) { Loading Loading @@ -304,6 +326,42 @@ bool Client::getInterface(const std::string &name, InterfaceInfo &out) { } bool Client::createInterface(const InterfaceSettings &s) { return checkedCall(method::CreateInterface, settingsArgs(s)); } static std::vector<std::pair<std::string, std::string>> netDevArgs(const NetDevSettings &s) { std::vector<std::pair<std::string, std::string>> a; a.emplace_back("netdev", s.netdev); if (!s.kind.empty()) a.emplace_back("kind", s.kind); if (s.mode) a.emplace_back("mode", *s.mode); if (s.miimonMs) a.emplace_back("miimon_ms", std::to_string(*s.miimonMs)); if (s.lacpRate) a.emplace_back("lacp_rate", *s.lacpRate); if (s.hashPolicy) a.emplace_back("hash_policy", *s.hashPolicy); if (s.stp) a.emplace_back("stp", *s.stp ? "yes" : "no"); if (s.members) { a.emplace_back("set_members", "yes"); // also when empty: no members for (auto &m : *s.members) a.emplace_back("member", m); } return a; } std::vector<NetDevInfo> Client::listNetDevs() { std::vector<NetDevInfo> out; Message r; if (!checkedCall(method::ListNetDevs, {}, &r)) return out; std::vector<std::pair<std::string, std::string>> cur; for (const auto &f : r.fields) { if (f.first == "netdev" && !cur.empty()) { out.push_back(netDevFromFields(cur)); cur.clear(); } cur.push_back(f); } if (!cur.empty()) out.push_back(netDevFromFields(cur)); return out; } bool Client::createNetDev(const NetDevSettings &s) { return checkedCall(method::CreateNetDev, netDevArgs(s)); } bool Client::editNetDev(const NetDevSettings &s) { return checkedCall(method::EditNetDev, netDevArgs(s)); } bool Client::deleteNetDev(const std::string &n) { return checkedCall(method::DeleteNetDev, {{"netdev", n}}); } bool Client::editInterface(const InterfaceSettings &s) { return checkedCall(method::EditInterface, settingsArgs(s)); } bool Client::deleteInterface(const std::string &interface, const std::string &config) { Loading Loading
CMakeLists.txt +1 −1 Changes for CMakeLists.txt: 1 added line, 1 removed line. Original line number Diff line number Diff line cmake_minimum_required(VERSION 3.18) project(syslite VERSION 0.1.0 LANGUAGES CXX) project(syslite VERSION 1.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) Loading
include/sysbus/client.h +51 −0 Changes for include/sysbus/client.h: 51 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -62,6 +62,15 @@ constexpr const char *StopInterface = "StopInterface"; constexpr const char *RestartInterface = "RestartInterface"; constexpr const char *ReloadInterface = "ReloadInterface"; // no interface: all constexpr const char *ForceReloadInterface = "ForceReloadInterface"; // no interface: all // Bonds and bridges: .netdev files in /etc/syslite/network, their members // get member .network files (Bond=/Bridge=). Create/Edit change only the // files (ReloadInterface without an interface creates the device and adds // its members); Delete also removes the device. Root only. constexpr const char *ListNetDevs = "ListNetDevs"; // -> netdev, ... (repeated) constexpr const char *CreateNetDev = "CreateNetDev"; // netdev, kind, ... constexpr const char *EditNetDev = "EditNetDev"; // netdev, ... constexpr const char *DeleteNetDev = "DeleteNetDev"; // netdev } // namespace method namespace signal { Loading Loading @@ -127,6 +136,40 @@ struct InterfaceInfo { std::string configAddress; // [Network] Address= std::string gateway; std::vector<std::string> dns; std::string kind; // "bond", "bridge" or "" (what the device is) std::string master; // the bond/bridge it is a member of now std::string memberOf; // configured: [Network] Bond= or Bridge= }; // A bond or bridge (.netdev). For a bond: mode (balance-rr, active-backup, // balance-xor, broadcast, 802.3ad, balance-tlb, balance-alb), MII // monitoring interval, LACP rate (802.3ad: slow/fast), transmit hash policy // (layer2, layer3+4, layer2+3, ...). For a bridge: STP. struct NetDevInfo { std::string name; // the interface, e.g. bond0 std::string kind; // bond or bridge std::string mode; int miimonMs = 0; std::string lacpRate; std::string hashPolicy; bool stp = false; std::string config; // file name without ".netdev" std::string configPath; bool exists = false; // the device is there std::vector<std::string> members; // configured members }; // Create: netdev and kind required; Edit: only what is set changes, kind // can't. members, if set, becomes the complete member list. struct NetDevSettings { std::string netdev; std::string kind; std::optional<std::string> mode; std::optional<int> miimonMs; std::optional<std::string> lacpRate; std::optional<std::string> hashPolicy; std::optional<bool> stp; std::optional<std::vector<std::string>> members; }; // Create: interface is required; the configuration is named Loading Loading @@ -206,6 +249,12 @@ public: bool reloadInterface(const std::string &name = ""); // empty: all bool forceReloadInterface(const std::string &name = ""); // empty: all // Bonds and bridges: true on success, otherwise see lastError(). std::vector<NetDevInfo> listNetDevs(); bool createNetDev(const NetDevSettings &settings); bool editNetDev(const NetDevSettings &settings); bool deleteNetDev(const std::string &netdev); // Signals: subscribe once, then waitSignal() returns the next one // (false on timeout or a closed connection; timeoutMs < 0 waits forever). bool subscribe(); Loading @@ -232,5 +281,7 @@ private: UnitInfo unitFromFields(const std::vector<std::pair<std::string, std::string>> &fields); // Reads an InterfaceInfo from a GetInterface reply / ListInterfaces entry. InterfaceInfo interfaceFromFields(const std::vector<std::pair<std::string, std::string>> &fields); // Reads a NetDevInfo from a ListNetDevs entry. NetDevInfo netDevFromFields(const std::vector<std::pair<std::string, std::string>> &fields); } // namespace sysbus
src/network.h +56 −0 Changes for src/network.h: 56 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -20,6 +20,11 @@ namespace syslite { std::string gateway; // Gateway=192.168.1.1 std::vector<std::string> dns; // DNS=8.8.8.8 // [Network] Bond=/Bridge=: the interface is a member of that bond or // bridge (and gets no addresses of its own) std::string bond; std::string bridge; // [Link] bool linkUp = true; Loading @@ -28,6 +33,29 @@ namespace syslite { std::string name; }; // Parsed from systemd-networkd .netdev files: a bond or a bridge that // syslite creates; its members are the interfaces whose .network says // Bond=<name> / Bridge=<name> struct NetDevConfig { // [NetDev] std::string ifname; // Name=bond0 std::string kind; // Kind=bond | bridge // [Bond] std::string mode = "balance-rr"; // Mode=balance-rr, active-backup, balance-xor, // broadcast, 802.3ad, balance-tlb, balance-alb int miimonMs = 100; // MIIMonitorSec= (link monitoring) std::string lacpRate; // LACPTransmitRate=slow | fast (802.3ad) std::string hashPolicy; // TransmitHashPolicy=layer2 | layer3+4 | layer2+3 | ... // [Bridge] bool stp = false; // STP=yes // Metadata std::string filePath; std::string name; // file name without ".netdev" }; // Runtime state of one network interface (from /sys/class/net and netlink) struct InterfaceStatus { std::string name; Loading @@ -36,6 +64,8 @@ namespace syslite { bool adminUp = false; // IFF_UP bool carrier = false; bool stopped = false; // stopped on request (stays down) std::string kind; // "bond", "bridge" or "" (/sys/class/net/<if>/bonding, /bridge) std::string master; // the bond or bridge it is a member of std::vector<std::string> addresses; // "a.b.c.d/len", IPv6 too const NetworkConfig *config = nullptr; // first matching configuration }; Loading Loading @@ -104,6 +134,20 @@ namespace syslite { // Re-read the .network files only (nothing is applied) void reloadConfigs(); // Bonds and bridges (.netdev files in ConfigDir). saveNetDev writes // ConfigDir/<nd.name>.netdev and makes exactly `members` its members: // each gets a member configuration ConfigDir/10-<member>.network // (Bond=/Bridge=), former members lose theirs. deleteNetDev removes // the file, the member configurations and the device itself. Both // re-read the files; applyNetDevs() (also part of applyAll/reload) // creates what is missing. const std::vector<NetDevConfig> &netDevs() const { return _netdevs; } const NetDevConfig *findNetDev(const std::string &ifname) const; std::vector<std::string> membersOf(const std::string &ifname) const; bool saveNetDev(const NetDevConfig &nd, const std::vector<std::string> &members, std::string *err); bool deleteNetDev(const std::string &ifname, std::string *err); void applyNetDevs(); // Start listening for new network devices (netlink RTNLGRP_LINK) // Returns fd to poll in the main loop (-1 on failure) int startDeviceMonitor(); Loading @@ -117,6 +161,17 @@ namespace syslite { private: // Parse a single .network file bool parseNetworkFile(const std::string &path, NetworkConfig &cfg); // ...and .netdev file bool parseNetDevFile(const std::string &path, NetDevConfig &nd); bool writeNetworkFile(const NetworkConfig &cfg, std::string *err); // Netlink: create a bond/bridge (RTM_NEWLINK with IFLA_LINKINFO), // delete a link, make ifname a member of master ("": none) bool createLink(const NetDevConfig &nd); bool deleteLink(const std::string &ifname); bool setMaster(const std::string &ifname, const std::string &master); // does the existing device match nd (kind, bond mode, STP)? bool linkMatches(const NetDevConfig &nd); // Match an interface name against a pattern (supports trailing *) bool matchInterface(const std::string &pattern, const std::string &ifname); Loading Loading @@ -161,6 +216,7 @@ namespace syslite { static std::string formatIpv4(uint32_t ip); std::vector<NetworkConfig> _configs; std::vector<NetDevConfig> _netdevs; std::map<std::string, std::vector<std::string>> _dnsByIf; // DNS servers per interface std::set<std::string> _configured; // interfaces configureInterface() set up std::set<std::string> _stopped; // interfaces stopped on request Loading
src/sysbus/client.cpp +58 −0 Changes for src/sysbus/client.cpp: 58 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -72,10 +72,32 @@ InterfaceInfo interfaceFromFields(const std::vector<std::pair<std::string, std:: else if (k == "config_address") i.configAddress = v; else if (k == "gateway") i.gateway = v; else if (k == "dns") i.dns.push_back(v); else if (k == "kind") i.kind = v; else if (k == "master") i.master = v; else if (k == "member_of") i.memberOf = v; } return i; } NetDevInfo netDevFromFields(const std::vector<std::pair<std::string, std::string>> &fields) { NetDevInfo n; for (const auto &f : fields) { const std::string &k = f.first, &v = f.second; if (k == "netdev") n.name = v; else if (k == "kind") n.kind = v; else if (k == "mode") n.mode = v; else if (k == "miimon_ms") n.miimonMs = std::atoi(v.c_str()); else if (k == "lacp_rate") n.lacpRate = v; else if (k == "hash_policy") n.hashPolicy = v; else if (k == "stp") n.stp = v == "yes"; else if (k == "config") n.config = v; else if (k == "config_path") n.configPath = v; else if (k == "exists") n.exists = v == "yes"; else if (k == "member") n.members.push_back(v); } return n; } Client::~Client() { close(); } bool Client::connect(const std::string &path) { Loading Loading @@ -304,6 +326,42 @@ bool Client::getInterface(const std::string &name, InterfaceInfo &out) { } bool Client::createInterface(const InterfaceSettings &s) { return checkedCall(method::CreateInterface, settingsArgs(s)); } static std::vector<std::pair<std::string, std::string>> netDevArgs(const NetDevSettings &s) { std::vector<std::pair<std::string, std::string>> a; a.emplace_back("netdev", s.netdev); if (!s.kind.empty()) a.emplace_back("kind", s.kind); if (s.mode) a.emplace_back("mode", *s.mode); if (s.miimonMs) a.emplace_back("miimon_ms", std::to_string(*s.miimonMs)); if (s.lacpRate) a.emplace_back("lacp_rate", *s.lacpRate); if (s.hashPolicy) a.emplace_back("hash_policy", *s.hashPolicy); if (s.stp) a.emplace_back("stp", *s.stp ? "yes" : "no"); if (s.members) { a.emplace_back("set_members", "yes"); // also when empty: no members for (auto &m : *s.members) a.emplace_back("member", m); } return a; } std::vector<NetDevInfo> Client::listNetDevs() { std::vector<NetDevInfo> out; Message r; if (!checkedCall(method::ListNetDevs, {}, &r)) return out; std::vector<std::pair<std::string, std::string>> cur; for (const auto &f : r.fields) { if (f.first == "netdev" && !cur.empty()) { out.push_back(netDevFromFields(cur)); cur.clear(); } cur.push_back(f); } if (!cur.empty()) out.push_back(netDevFromFields(cur)); return out; } bool Client::createNetDev(const NetDevSettings &s) { return checkedCall(method::CreateNetDev, netDevArgs(s)); } bool Client::editNetDev(const NetDevSettings &s) { return checkedCall(method::EditNetDev, netDevArgs(s)); } bool Client::deleteNetDev(const std::string &n) { return checkedCall(method::DeleteNetDev, {{"netdev", n}}); } bool Client::editInterface(const InterfaceSettings &s) { return checkedCall(method::EditInterface, settingsArgs(s)); } bool Client::deleteInterface(const std::string &interface, const std::string &config) { Loading