system_queries: Migrate system queries into a single place
This provides better separation of the DBus interface and the underlying
system interactions.
Change-Id: I8bf7a44c679ab065f959af19a00c29897c1d5c48
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/system_queries.cpp b/src/system_queries.cpp
new file mode 100644
index 0000000..f02d568
--- /dev/null
+++ b/src/system_queries.cpp
@@ -0,0 +1,156 @@
+#include "system_queries.hpp"
+
+#include "util.hpp"
+
+#include <fmt/format.h>
+#include <ifaddrs.h>
+#include <linux/ethtool.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+
+#include <algorithm>
+#include <optional>
+#include <phosphor-logging/log.hpp>
+#include <stdexcept>
+#include <stdplus/fd/create.hpp>
+#include <stdplus/raw.hpp>
+#include <stdplus/util/cexec.hpp>
+#include <string_view>
+#include <system_error>
+
+namespace phosphor::network::system
+{
+
+using std::literals::string_view_literals::operator""sv;
+using phosphor::logging::entry;
+using phosphor::logging::level;
+using phosphor::logging::log;
+
+static stdplus::Fd& getIFSock()
+{
+ using namespace stdplus::fd;
+ static auto fd =
+ socket(SocketDomain::INet, SocketType::Datagram, SocketProto::IP);
+ return fd;
+}
+
+static ifreq makeIFReq(std::string_view ifname)
+{
+ ifreq ifr = {};
+ const auto copied = std::min<std::size_t>(ifname.size(), IFNAMSIZ - 1);
+ std::copy_n(ifname.begin(), copied, ifr.ifr_name);
+ return ifr;
+}
+
+static ifreq executeIFReq(std::string_view ifname, unsigned long cmd,
+ void* data = nullptr)
+{
+ ifreq ifr = makeIFReq(ifname);
+ ifr.ifr_data = reinterpret_cast<char*>(data);
+ getIFSock().ioctl(cmd, &ifr);
+ return ifr;
+}
+
+inline auto optionalIFReq(stdplus::zstring_view ifname, unsigned long long cmd,
+ std::string_view cmdname, auto&& complete,
+ void* data = nullptr)
+{
+ ifreq ifr;
+ std::optional<decltype(complete(ifr))> ret;
+ try
+ {
+ ifr = executeIFReq(ifname, cmd, data);
+ }
+ catch (const std::system_error& e)
+ {
+ if (e.code() == std::errc::operation_not_supported)
+ {
+ auto msg = fmt::format("{} not supported on {}", cmdname, ifname);
+ log<level::INFO>(msg.c_str(),
+ entry("INTERFACE=%s", ifname.c_str()));
+ return ret;
+ }
+ throw;
+ }
+ ret.emplace(complete(ifr));
+ return ret;
+}
+
+EthInfo getEthInfo(stdplus::zstring_view ifname)
+{
+ ethtool_cmd edata = {};
+ edata.cmd = ETHTOOL_GSET;
+ return optionalIFReq(
+ ifname, SIOCETHTOOL, "ETHTOOL"sv,
+ [&](const ifreq&) {
+ return EthInfo{.autoneg = edata.autoneg != 0,
+ .speed = edata.speed};
+ },
+ &edata)
+ .value_or(EthInfo{});
+}
+
+bool intfIsRunning(std::string_view ifname)
+{
+ return executeIFReq(ifname, SIOCGIFFLAGS).ifr_flags & IFF_RUNNING;
+}
+
+unsigned intfIndex(stdplus::const_zstring ifname)
+{
+ unsigned idx = if_nametoindex(ifname.c_str());
+ if (idx == 0)
+ {
+ auto msg = fmt::format("if_nametoindex({})", ifname);
+ throw std::system_error(errno, std::generic_category(), msg);
+ }
+ return idx;
+}
+
+std::optional<ether_addr> getMAC(stdplus::zstring_view ifname)
+{
+ return optionalIFReq(
+ ifname, SIOCGIFHWADDR, "IFHWADDR", [](const ifreq& ifr) {
+ return stdplus::raw::refFrom<ether_addr>(ifr.ifr_hwaddr.sa_data);
+ });
+}
+
+std::optional<unsigned> getMTU(stdplus::zstring_view ifname)
+{
+ return optionalIFReq(ifname, SIOCGIFMTU, "GMTU",
+ [](const ifreq& ifr) { return ifr.ifr_mtu; });
+}
+
+void setMTU(std::string_view ifname, unsigned mtu)
+{
+ auto ifr = makeIFReq(ifname);
+ ifr.ifr_mtu = mtu;
+ getIFSock().ioctl(SIOCSIFMTU, &ifr);
+}
+
+void setNICUp(std::string_view ifname, bool up)
+{
+ ifreq ifr = executeIFReq(ifname, SIOCGIFFLAGS);
+ ifr.ifr_flags &= ~IFF_UP;
+ ifr.ifr_flags |= up ? IFF_UP : 0;
+ getIFSock().ioctl(SIOCSIFFLAGS, &ifr);
+}
+
+string_uset getInterfaces()
+{
+ string_uset ret;
+ struct ifaddrs* root;
+ CHECK_ERRNO(getifaddrs(&root), "getifaddrs");
+ const auto& ignored = internal::getIgnoredInterfaces();
+ for (auto it = root; it != nullptr; it = it->ifa_next)
+ {
+ if (!(it->ifa_flags & IFF_LOOPBACK) &&
+ ignored.find(it->ifa_name) == ignored.end())
+ {
+ ret.emplace(it->ifa_name);
+ }
+ }
+ freeifaddrs(root);
+ return ret;
+}
+
+} // namespace phosphor::network::system