Merge pull request #4326 from notpeelz/feat-network-match-altnames

This commit is contained in:
Alexis Rouillard
2025-08-08 08:21:33 +02:00
committed by GitHub
2 changed files with 111 additions and 73 deletions

View File

@ -9,6 +9,7 @@
#include <sys/epoll.h> #include <sys/epoll.h>
#include <optional> #include <optional>
#include <vector>
#include "ALabel.hpp" #include "ALabel.hpp"
#include "util/sleeper_thread.hpp" #include "util/sleeper_thread.hpp"
@ -44,11 +45,11 @@ class Network : public ALabel {
void parseFreq(struct nlattr**); void parseFreq(struct nlattr**);
void parseBssid(struct nlattr**); void parseBssid(struct nlattr**);
bool associatedOrJoined(struct nlattr**); bool associatedOrJoined(struct nlattr**);
bool checkInterface(std::string name); bool matchInterface(const std::string& ifname, const std::vector<std::string>& altnames,
std::string& matched) const;
auto getInfo() -> void; auto getInfo() -> void;
const std::string getNetworkState() const; const std::string getNetworkState() const;
void clearIface(); void clearIface();
bool wildcardMatch(const std::string& pattern, const std::string& text) const;
std::optional<std::pair<unsigned long long, unsigned long long>> readBandwidthUsage(); std::optional<std::pair<unsigned long long, unsigned long long>> readBandwidthUsage();
int ifid_{-1}; int ifid_{-1};

View File

@ -1,13 +1,18 @@
#include "modules/network.hpp" #include "modules/network.hpp"
#include <linux/if.h> #include <linux/if.h>
#include <linux/if_link.h>
#include <netlink/netlink.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <sys/eventfd.h> #include <sys/eventfd.h>
#include <cassert> #include <cassert>
#include <cstring>
#include <fstream> #include <fstream>
#include <optional> #include <optional>
#include <sstream> #include <sstream>
#include <string>
#include <vector>
#include "util/format.hpp" #include "util/format.hpp"
#ifdef WANT_RFKILL #ifdef WANT_RFKILL
@ -386,11 +391,65 @@ auto waybar::modules::Network::update() -> void {
ALabel::update(); ALabel::update();
} }
bool waybar::modules::Network::checkInterface(std::string name) { // https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495
if (config_["interface"].isString()) { static bool wildcardMatch(const std::string &pattern, const std::string &text) {
return config_["interface"].asString() == name || auto P = int(pattern.size());
wildcardMatch(config_["interface"].asString(), name); auto T = int(text.size());
auto p = 0, fallback_p = -1;
auto t = 0, fallback_t = -1;
while (t < T) {
// Wildcard match:
if (p < P && pattern[p] == '*') {
fallback_p = p++; // starting point after failures
fallback_t = t; // starting point after failures
}
// Simple match:
else if (p < P && (pattern[p] == '?' || pattern[p] == text[t])) {
p++;
t++;
}
// Failure, fall back just after last matched '*':
else if (fallback_p >= 0) {
p = fallback_p + 1; // position just after last matched '*"
t = ++fallback_t; // re-try to match text from here
}
// There were no '*' before, so we fail here:
else {
return false;
}
} }
// Consume all '*' at the end of pattern:
while (p < P && pattern[p] == '*') p++;
return p == P;
}
bool waybar::modules::Network::matchInterface(const std::string &ifname,
const std::vector<std::string> &altnames,
std::string &matched) const {
if (!config_["interface"].isString()) {
return false;
}
auto config_ifname = config_["interface"].asString();
if (config_ifname == ifname || wildcardMatch(config_ifname, ifname)) {
matched = ifname;
return true;
}
for (const auto &altname : altnames) {
if (config_ifname == altname || wildcardMatch(config_ifname, altname)) {
matched = altname;
return true;
}
}
return false; return false;
} }
@ -425,12 +484,16 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
is_del_event = true; is_del_event = true;
case RTM_NEWLINK: { case RTM_NEWLINK: {
struct ifinfomsg *ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh)); struct ifinfomsg *ifi = static_cast<struct ifinfomsg *>(NLMSG_DATA(nh));
ssize_t attrlen = IFLA_PAYLOAD(nh); struct nlattr *attrs[IFLA_MAX + 1];
struct rtattr *ifla = IFLA_RTA(ifi); std::string ifname;
const char *ifname = NULL; std::vector<std::string> altnames;
size_t ifname_len = 0;
std::optional<bool> carrier; std::optional<bool> carrier;
if (nlmsg_parse(nh, sizeof(*ifi), attrs, IFLA_MAX, nullptr) < 0) {
spdlog::error("network: failed to parse netlink attributes");
return NL_SKIP;
}
if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) { if (net->ifid_ != -1 && ifi->ifi_index != net->ifid_) {
return NL_OK; return NL_OK;
} }
@ -448,26 +511,33 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
return NL_OK; return NL_OK;
} }
for (; RTA_OK(ifla, attrlen); ifla = RTA_NEXT(ifla, attrlen)) { if (attrs[IFLA_IFNAME] != nullptr) {
switch (ifla->rta_type) { const char *ifname_ptr = nla_get_string(attrs[IFLA_IFNAME]);
case IFLA_IFNAME: size_t ifname_len = nla_len(attrs[IFLA_IFNAME]) - 1; // minus \0
ifname = static_cast<const char *>(RTA_DATA(ifla)); ifname = std::string(ifname_ptr, ifname_len);
ifname_len = RTA_PAYLOAD(ifla) - 1; // minus \0 }
if (ifi->ifi_flags & IFF_POINTOPOINT && net->checkInterface(ifname))
net->is_p2p_ = true; if (attrs[IFLA_CARRIER] != nullptr) {
break; carrier = nla_get_u8(attrs[IFLA_CARRIER]) == 1;
case IFLA_CARRIER: { }
carrier = *(char *)RTA_DATA(ifla) == 1;
break; if (attrs[IFLA_PROP_LIST] != nullptr) {
struct nlattr *prop;
int rem;
nla_for_each_nested(prop, attrs[IFLA_PROP_LIST], rem) {
if (nla_type(prop) == IFLA_ALT_IFNAME) {
const char *altname_ptr = nla_get_string(prop);
size_t altname_len = nla_len(prop) - 1; // minus \0
altnames.emplace_back(altname_ptr, altname_len);
} }
} }
} }
if (!is_del_event && ifi->ifi_index == net->ifid_) { if (!is_del_event && ifi->ifi_index == net->ifid_) {
// Update interface information // Update interface information
if (net->ifname_.empty() && ifname != NULL) { if (net->ifname_.empty() && !ifname.empty()) {
std::string new_ifname(ifname, ifname_len); net->ifname_ = ifname;
net->ifname_ = new_ifname;
} }
if (carrier.has_value()) { if (carrier.has_value()) {
if (net->carrier_ != *carrier) { if (net->carrier_ != *carrier) {
@ -488,13 +558,20 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
} }
} else if (!is_del_event && net->ifid_ == -1) { } else if (!is_del_event && net->ifid_ == -1) {
// Checking if it's an interface we care about. // Checking if it's an interface we care about.
std::string new_ifname(ifname, ifname_len); std::string matched;
if (net->checkInterface(new_ifname)) { if (net->matchInterface(ifname, altnames, matched)) {
spdlog::debug("network: selecting new interface {}/{}", new_ifname, ifi->ifi_index); if (ifname == matched) {
spdlog::debug("network: selecting new interface {}/{}", ifname, ifi->ifi_index);
} else {
spdlog::debug("network: selecting new interface {}/{} (matched altname {})", ifname,
ifi->ifi_index, matched);
}
net->ifname_ = new_ifname; net->ifname_ = ifname;
net->ifid_ = ifi->ifi_index; net->ifid_ = ifi->ifi_index;
if (ifi->ifi_flags & IFF_POINTOPOINT) net->is_p2p_ = true; if ((ifi->ifi_flags & IFF_POINTOPOINT) != 0) {
net->is_p2p_ = true;
}
if ((ifi->ifi_flags & IFF_UP) == 0) { if ((ifi->ifi_flags & IFF_UP) == 0) {
// With some network drivers (e.g. mt7921e), the interface may // With some network drivers (e.g. mt7921e), the interface may
// report having a carrier even though interface is down. // report having a carrier even though interface is down.
@ -593,7 +670,6 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
break; break;
} }
char temp_gw_addr[INET6_ADDRSTRLEN];
case RTM_DELROUTE: case RTM_DELROUTE:
is_del_event = true; is_del_event = true;
case RTM_NEWROUTE: { case RTM_NEWROUTE: {
@ -604,6 +680,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
int family = rtm->rtm_family; int family = rtm->rtm_family;
ssize_t attrlen = RTM_PAYLOAD(nh); ssize_t attrlen = RTM_PAYLOAD(nh);
struct rtattr *attr = RTM_RTA(rtm); struct rtattr *attr = RTM_RTA(rtm);
char gateway_addr[INET6_ADDRSTRLEN];
bool has_gateway = false; bool has_gateway = false;
bool has_destination = false; bool has_destination = false;
int temp_idx = -1; int temp_idx = -1;
@ -629,7 +706,7 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
* If someone ever needs to figure out the gateway address as well, * If someone ever needs to figure out the gateway address as well,
* it's here as the attribute payload. * it's here as the attribute payload.
*/ */
inet_ntop(family, RTA_DATA(attr), temp_gw_addr, sizeof(temp_gw_addr)); inet_ntop(family, RTA_DATA(attr), gateway_addr, sizeof(gateway_addr));
has_gateway = true; has_gateway = true;
break; break;
case RTA_DST: { case RTA_DST: {
@ -674,8 +751,8 @@ int waybar::modules::Network::handleEvents(struct nl_msg *msg, void *data) {
net->clearIface(); net->clearIface();
net->ifid_ = temp_idx; net->ifid_ = temp_idx;
net->route_priority = priority; net->route_priority = priority;
net->gwaddr_ = temp_gw_addr; net->gwaddr_ = gateway_addr;
spdlog::debug("network: new default route via {} on if{} metric {}", temp_gw_addr, spdlog::debug("network: new default route via {} on if{} metric {}", gateway_addr,
temp_idx, priority); temp_idx, priority);
/* Ask ifname associated with temp_idx as well as carrier status */ /* Ask ifname associated with temp_idx as well as carrier status */
@ -888,43 +965,3 @@ auto waybar::modules::Network::getInfo() -> void {
} }
nl_send_sync(sock_, nl_msg); nl_send_sync(sock_, nl_msg);
} }
// https://gist.github.com/rressi/92af77630faf055934c723ce93ae2495
bool waybar::modules::Network::wildcardMatch(const std::string &pattern,
const std::string &text) const {
auto P = int(pattern.size());
auto T = int(text.size());
auto p = 0, fallback_p = -1;
auto t = 0, fallback_t = -1;
while (t < T) {
// Wildcard match:
if (p < P && pattern[p] == '*') {
fallback_p = p++; // starting point after failures
fallback_t = t; // starting point after failures
}
// Simple match:
else if (p < P && (pattern[p] == '?' || pattern[p] == text[t])) {
p++;
t++;
}
// Failure, fall back just after last matched '*':
else if (fallback_p >= 0) {
p = fallback_p + 1; // position just after last matched '*"
t = ++fallback_t; // re-try to match text from here
}
// There were no '*' before, so we fail here:
else {
return false;
}
}
// Consume all '*' at the end of pattern:
while (p < P && pattern[p] == '*') p++;
return p == P;
}