mirror of
https://github.com/aristocratos/btop.git
synced 2024-09-27 22:01:29 +02:00
network
This commit is contained in:
parent
899be68a78
commit
4eb812d52c
@ -26,10 +26,14 @@ tab-size = 4
|
|||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <netinet/tcp_fsm.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
|
||||||
#include <btop_config.hpp>
|
#include <btop_config.hpp>
|
||||||
#include <btop_shared.hpp>
|
#include <btop_shared.hpp>
|
||||||
@ -241,7 +245,7 @@ namespace Mem {
|
|||||||
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
|
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
|
||||||
return current_mem;
|
return current_mem;
|
||||||
|
|
||||||
auto& show_swap = Config::getB("show_swap");
|
auto &show_swap = Config::getB("show_swap");
|
||||||
auto &show_disks = Config::getB("show_disks");
|
auto &show_disks = Config::getB("show_disks");
|
||||||
auto &swap_disk = Config::getB("swap_disk");
|
auto &swap_disk = Config::getB("swap_disk");
|
||||||
auto &mem = current_mem;
|
auto &mem = current_mem;
|
||||||
@ -253,7 +257,7 @@ namespace Mem {
|
|||||||
mem.stats.at("available") = p.free_count * Shared::pageSize;
|
mem.stats.at("available") = p.free_count * Shared::pageSize;
|
||||||
mem.stats.at("free") = p.free_count * Shared::pageSize;
|
mem.stats.at("free") = p.free_count * Shared::pageSize;
|
||||||
mem.stats.at("cached") = p.external_page_count * Shared::pageSize;
|
mem.stats.at("cached") = p.external_page_count * Shared::pageSize;
|
||||||
mem.stats.at("used") = ((int64_t)p.active_count + (int64_t)p.inactive_count + (int64_t)p.wire_count) * (int64_t)Shared::pageSize;
|
mem.stats.at("used") = (p.active_count + p.inactive_count + p.wire_count) * Shared::pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mib[2] = {CTL_VM, VM_SWAPUSAGE};
|
int mib[2] = {CTL_VM, VM_SWAPUSAGE};
|
||||||
@ -267,18 +271,19 @@ namespace Mem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (show_swap and mem.stats.at("swap_total") > 0) {
|
if (show_swap and mem.stats.at("swap_total") > 0) {
|
||||||
for (const auto& name : swap_names) {
|
for (const auto &name : swap_names) {
|
||||||
mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / mem.stats.at("swap_total")));
|
mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / mem.stats.at("swap_total")));
|
||||||
while (cmp_greater(mem.percent.at(name).size(), width * 2)) mem.percent.at(name).pop_front();
|
while (cmp_greater(mem.percent.at(name).size(), width * 2))
|
||||||
|
mem.percent.at(name).pop_front();
|
||||||
}
|
}
|
||||||
has_swap = true;
|
has_swap = true;
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
has_swap = false;
|
has_swap = false;
|
||||||
//? Calculate percentages
|
//? Calculate percentages
|
||||||
for (const auto &name : mem_names) {
|
for (const auto &name : mem_names) {
|
||||||
mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / Shared::totalMem));
|
mem.percent.at(name).push_back(round((double)mem.stats.at(name) * 100 / Shared::totalMem));
|
||||||
while (cmp_greater(mem.percent.at(name).size(), width * 2)) mem.percent.at(name).pop_front();
|
while (cmp_greater(mem.percent.at(name).size(), width * 2))
|
||||||
|
mem.percent.at(name).pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (show_disks) {
|
if (show_disks) {
|
||||||
@ -314,18 +319,22 @@ namespace Mem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
found.push_back(mountpoint);
|
found.push_back(mountpoint);
|
||||||
if (not v_contains(last_found, mountpoint)) redraw = true;
|
if (not v_contains(last_found, mountpoint))
|
||||||
|
redraw = true;
|
||||||
last_found = std::move(found);
|
last_found = std::move(found);
|
||||||
|
|
||||||
if (disks.at(mountpoint).dev.empty()) disks.at(mountpoint).dev = dev;
|
if (disks.at(mountpoint).dev.empty())
|
||||||
if (disks.at(mountpoint).name.empty()) disks.at(mountpoint).name = (mountpoint == "/" ? "root" : mountpoint);
|
disks.at(mountpoint).dev = dev;
|
||||||
|
if (disks.at(mountpoint).name.empty())
|
||||||
|
disks.at(mountpoint).name = (mountpoint == "/" ? "root" : mountpoint);
|
||||||
disks.at(mountpoint).free = stfs[i].f_bfree;
|
disks.at(mountpoint).free = stfs[i].f_bfree;
|
||||||
disks.at(mountpoint).total = stfs[i].f_iosize;
|
disks.at(mountpoint).total = stfs[i].f_iosize;
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Get disk/partition stats
|
//? Get disk/partition stats
|
||||||
for (auto &[mountpoint, disk] : disks) {
|
for (auto &[mountpoint, disk] : disks) {
|
||||||
if (std::error_code ec; not fs::exists(mountpoint, ec)) continue;
|
if (std::error_code ec; not fs::exists(mountpoint, ec))
|
||||||
|
continue;
|
||||||
struct statvfs vfs;
|
struct statvfs vfs;
|
||||||
if (statvfs(mountpoint.c_str(), &vfs) < 0) {
|
if (statvfs(mountpoint.c_str(), &vfs) < 0) {
|
||||||
Logger::warning("Failed to get disk/partition stats with statvfs() for: " + mountpoint);
|
Logger::warning("Failed to get disk/partition stats with statvfs() for: " + mountpoint);
|
||||||
@ -346,7 +355,8 @@ namespace Mem {
|
|||||||
mem.disks_order.push_back("/");
|
mem.disks_order.push_back("/");
|
||||||
if (swap_disk and has_swap) {
|
if (swap_disk and has_swap) {
|
||||||
mem.disks_order.push_back("swap");
|
mem.disks_order.push_back("swap");
|
||||||
if (not disks.contains("swap")) disks["swap"] = {"", "swap"};
|
if (not disks.contains("swap"))
|
||||||
|
disks["swap"] = {"", "swap"};
|
||||||
disks.at("swap").total = mem.stats.at("swap_total");
|
disks.at("swap").total = mem.stats.at("swap_total");
|
||||||
disks.at("swap").used = mem.stats.at("swap_used");
|
disks.at("swap").used = mem.stats.at("swap_used");
|
||||||
disks.at("swap").free = mem.stats.at("swap_free");
|
disks.at("swap").free = mem.stats.at("swap_free");
|
||||||
@ -354,7 +364,8 @@ namespace Mem {
|
|||||||
disks.at("swap").free_percent = mem.percent.at("swap_free").back();
|
disks.at("swap").free_percent = mem.percent.at("swap_free").back();
|
||||||
}
|
}
|
||||||
for (const auto &name : last_found)
|
for (const auto &name : last_found)
|
||||||
if (not is_in(name, "/", "swap")) mem.disks_order.push_back(name);
|
if (not is_in(name, "/", "swap"))
|
||||||
|
mem.disks_order.push_back(name);
|
||||||
}
|
}
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
@ -384,7 +395,188 @@ namespace Net {
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto collect(const bool no_update) -> net_info & {
|
auto collect(const bool no_update) -> net_info & {
|
||||||
return empty_net;
|
auto &net = current_net;
|
||||||
|
auto &config_iface = Config::getS("net_iface");
|
||||||
|
auto &net_sync = Config::getB("net_sync");
|
||||||
|
auto &net_auto = Config::getB("net_auto");
|
||||||
|
auto new_timestamp = time_ms();
|
||||||
|
|
||||||
|
if (not no_update and errors < 3) {
|
||||||
|
//? Get interface list using getifaddrs() wrapper
|
||||||
|
getifaddr_wrapper if_wrap{};
|
||||||
|
if (if_wrap.status != 0) {
|
||||||
|
errors++;
|
||||||
|
Logger::error("Net::collect() -> getifaddrs() failed with id " + to_string(if_wrap.status));
|
||||||
|
redraw = true;
|
||||||
|
return empty_net;
|
||||||
|
}
|
||||||
|
int family = 0;
|
||||||
|
char ip[NI_MAXHOST];
|
||||||
|
interfaces.clear();
|
||||||
|
string ipv4, ipv6;
|
||||||
|
|
||||||
|
//? Iteration over all items in getifaddrs() list
|
||||||
|
for (auto* ifa = if_wrap(); ifa != NULL; ifa = ifa->ifa_next) {
|
||||||
|
if (ifa->ifa_addr == NULL) continue;
|
||||||
|
family = ifa->ifa_addr->sa_family;
|
||||||
|
const auto& iface = ifa->ifa_name;
|
||||||
|
//? Get IPv4 address
|
||||||
|
if (family == AF_INET) {
|
||||||
|
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)
|
||||||
|
net[iface].ipv4 = ip;
|
||||||
|
}
|
||||||
|
//? Get IPv6 address
|
||||||
|
else if (family == AF_INET6) {
|
||||||
|
if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0)
|
||||||
|
net[iface].ipv6 = ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
//? Update available interfaces vector and get status of interface
|
||||||
|
if (not v_contains(interfaces, iface)) {
|
||||||
|
interfaces.push_back(iface);
|
||||||
|
net[iface].connected = (ifa->ifa_flags & IFF_RUNNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
|
||||||
|
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0};
|
||||||
|
size_t len;
|
||||||
|
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
|
||||||
|
Logger::error("failed getting network interfaces");
|
||||||
|
}
|
||||||
|
char *buf = (char *)malloc(len);
|
||||||
|
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
|
||||||
|
Logger::error("failed getting network interfaces");
|
||||||
|
}
|
||||||
|
char *lim = buf + len;
|
||||||
|
char *next = NULL;
|
||||||
|
for (next = buf; next < lim;) {
|
||||||
|
struct if_msghdr *ifm = (struct if_msghdr *)next;
|
||||||
|
next += ifm->ifm_msglen;
|
||||||
|
if (ifm->ifm_type == RTM_IFINFO2) {
|
||||||
|
struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
|
||||||
|
struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);
|
||||||
|
char iface[32];
|
||||||
|
strncpy(iface, sdl->sdl_data, sdl->sdl_nlen);
|
||||||
|
iface[sdl->sdl_nlen] = 0;
|
||||||
|
ifstats[iface] = std::tuple(if2m->ifm_data.ifi_ibytes, if2m->ifm_data.ifi_obytes);
|
||||||
|
Logger::debug(iface);
|
||||||
|
Logger::debug(std::to_string(if2m->ifm_data.ifi_ibytes));
|
||||||
|
Logger::debug(std::to_string(if2m->ifm_data.ifi_obytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//? Get total recieved and transmitted bytes + device address if no ip was found
|
||||||
|
for (const auto& iface : interfaces) {
|
||||||
|
|
||||||
|
for (const string dir : {"download", "upload"}) {
|
||||||
|
auto& saved_stat = net.at(iface).stat.at(dir);
|
||||||
|
auto& bandwidth = net.at(iface).bandwidth.at(dir);
|
||||||
|
auto dirval = dir == "download" ? std::get<0>(ifstats[iface]) : std::get<1>(ifstats[iface]);
|
||||||
|
uint64_t val = saved_stat.last;
|
||||||
|
try { val = max(dirval, val); }
|
||||||
|
catch (const std::invalid_argument&) {}
|
||||||
|
catch (const std::out_of_range&) {}
|
||||||
|
|
||||||
|
//? Update speed, total and top values
|
||||||
|
saved_stat.speed = round((double)(val - saved_stat.last) / ((double)(new_timestamp - timestamp) / 1000));
|
||||||
|
if (saved_stat.speed > saved_stat.top) saved_stat.top = saved_stat.speed;
|
||||||
|
if (saved_stat.offset > val) saved_stat.offset = 0;
|
||||||
|
saved_stat.total = val - saved_stat.offset;
|
||||||
|
saved_stat.last = val;
|
||||||
|
|
||||||
|
//? Add values to graph
|
||||||
|
bandwidth.push_back(saved_stat.speed);
|
||||||
|
while (cmp_greater(bandwidth.size(), width * 2)) bandwidth.pop_front();
|
||||||
|
|
||||||
|
//? Set counters for auto scaling
|
||||||
|
if (net_auto and selected_iface == iface) {
|
||||||
|
if (saved_stat.speed > graph_max[dir]) {
|
||||||
|
++max_count[dir][0];
|
||||||
|
if (max_count[dir][1] > 0) --max_count[dir][1];
|
||||||
|
}
|
||||||
|
else if (graph_max[dir] > 10 << 10 and saved_stat.speed < graph_max[dir] / 10) {
|
||||||
|
++max_count[dir][1];
|
||||||
|
if (max_count[dir][0] > 0) --max_count[dir][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//? Clean up net map if needed
|
||||||
|
if (net.size() > interfaces.size()) {
|
||||||
|
for (auto it = net.begin(); it != net.end();) {
|
||||||
|
if (not v_contains(interfaces, it->first))
|
||||||
|
it = net.erase(it);
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
net.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp = new_timestamp;
|
||||||
|
}
|
||||||
|
//? Return empty net_info struct if no interfaces was found
|
||||||
|
if (net.empty())
|
||||||
|
return empty_net;
|
||||||
|
|
||||||
|
//? Find an interface to display if selected isn't set or valid
|
||||||
|
if (selected_iface.empty() or not v_contains(interfaces, selected_iface)) {
|
||||||
|
max_count["download"][0] = max_count["download"][1] = max_count["upload"][0] = max_count["upload"][1] = 0;
|
||||||
|
redraw = true;
|
||||||
|
if (net_auto) rescale = true;
|
||||||
|
if (not config_iface.empty() and v_contains(interfaces, config_iface))
|
||||||
|
selected_iface = config_iface;
|
||||||
|
else {
|
||||||
|
//? Sort interfaces by total upload + download bytes
|
||||||
|
auto sorted_interfaces = interfaces;
|
||||||
|
rng::sort(sorted_interfaces, [&](const auto &a, const auto &b) {
|
||||||
|
return cmp_greater(net.at(a).stat["download"].total + net.at(a).stat["upload"].total,
|
||||||
|
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
|
||||||
|
});
|
||||||
|
selected_iface.clear();
|
||||||
|
//? Try to set to a connected interface
|
||||||
|
for (const auto &iface : sorted_interfaces) {
|
||||||
|
if (net.at(iface).connected) selected_iface = iface;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//? If no interface is connected set to first available
|
||||||
|
if (selected_iface.empty() and not sorted_interfaces.empty())
|
||||||
|
selected_iface = sorted_interfaces.at(0);
|
||||||
|
else if (sorted_interfaces.empty())
|
||||||
|
return empty_net;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//? Calculate max scale for graphs if needed
|
||||||
|
if (net_auto) {
|
||||||
|
bool sync = false;
|
||||||
|
for (const auto &dir : {"download", "upload"}) {
|
||||||
|
for (const auto &sel : {0, 1}) {
|
||||||
|
if (rescale or max_count[dir][sel] >= 5) {
|
||||||
|
const uint64_t avg_speed = (net[selected_iface].bandwidth[dir].size() > 5
|
||||||
|
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0) / 5
|
||||||
|
: net[selected_iface].stat[dir].speed);
|
||||||
|
graph_max[dir] = max(uint64_t(avg_speed * (sel == 0 ? 1.3 : 3.0)), (uint64_t)10 << 10);
|
||||||
|
max_count[dir][0] = max_count[dir][1] = 0;
|
||||||
|
redraw = true;
|
||||||
|
if (net_sync) sync = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//? Sync download/upload graphs if enabled
|
||||||
|
if (sync) {
|
||||||
|
const auto other = (string(dir) == "upload" ? "download" : "upload");
|
||||||
|
graph_max[other] = graph_max[dir];
|
||||||
|
max_count[other][0] = max_count[other][1] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rescale = false;
|
||||||
|
return net.at(selected_iface);
|
||||||
}
|
}
|
||||||
} // namespace Net
|
} // namespace Net
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user