Merge pull request #4326 from notpeelz/feat-network-match-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,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;
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user