feat: match network interfaces by their altnames
This commit is contained in:
@ -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};
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#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>
|
||||||
|
|
||||||
@ -8,6 +10,8 @@
|
|||||||
#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 +390,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 +483,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 +510,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 +557,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.
|
||||||
@ -888,43 +964,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;
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user