refactor(network): Code cleanup
- Cleanup of network module and adapter - Check operstate instead of carrier when testing the the current connection state, as suggested by @patrick96 in #44 - Separate signal quality and signal strength - Log failed attempt to query device driver
This commit is contained in:
parent
334dc7731d
commit
82f311b695
@ -39,21 +39,32 @@ LEMONBUDDY_NS
|
||||
|
||||
namespace net {
|
||||
DEFINE_ERROR(network_error);
|
||||
DEFINE_ERROR(wired_network_error);
|
||||
DEFINE_ERROR(wireless_network_error);
|
||||
|
||||
// types {{{
|
||||
|
||||
struct bytes_t {
|
||||
uint32_t transmitted = 0;
|
||||
uint32_t received = 0;
|
||||
std::chrono::system_clock::time_point time;
|
||||
struct quality_range {
|
||||
int val = 0;
|
||||
int max = 0;
|
||||
|
||||
int percentage() const {
|
||||
if (max < 0)
|
||||
return 2 * (val + 100);
|
||||
return static_cast<float>(val) / max * 100.0f + 0.5f;
|
||||
}
|
||||
};
|
||||
|
||||
struct linkdata_t {
|
||||
string ip_address;
|
||||
bytes_t previous;
|
||||
bytes_t current;
|
||||
using bytes_t = unsigned int;
|
||||
|
||||
struct link_activity {
|
||||
bytes_t transmitted = 0;
|
||||
bytes_t received = 0;
|
||||
chrono::system_clock::time_point time;
|
||||
};
|
||||
|
||||
struct link_status {
|
||||
string ip;
|
||||
link_activity previous;
|
||||
link_activity current;
|
||||
};
|
||||
|
||||
// }}}
|
||||
@ -61,169 +72,145 @@ namespace net {
|
||||
|
||||
class network {
|
||||
public:
|
||||
/**
|
||||
* Construct network interface
|
||||
*/
|
||||
explicit network(string interface) : m_interface(interface) {
|
||||
if (if_nametoindex(m_interface.c_str()) == 0)
|
||||
throw network_error("Invalid network interface \"" + m_interface + "\"");
|
||||
if ((m_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
||||
if ((m_socketfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
||||
throw network_error("Failed to open socket");
|
||||
std::memset(&m_data, 0, sizeof(m_data));
|
||||
std::strncpy(m_data.ifr_name, m_interface.data(), IFNAMSIZ - 1);
|
||||
}
|
||||
|
||||
~network() {
|
||||
if (m_fd != -1)
|
||||
close(m_fd);
|
||||
/**
|
||||
* Destruct network interface
|
||||
*/
|
||||
virtual ~network() {
|
||||
if (m_socketfd != -1)
|
||||
close(m_socketfd);
|
||||
}
|
||||
|
||||
bool test_interface() {
|
||||
if ((ioctl(m_fd, SIOCGIFFLAGS, &m_data)) == -1)
|
||||
throw network_error("Failed to get flags");
|
||||
if ((m_data.ifr_flags & IFF_UP) == 0)
|
||||
return false;
|
||||
if ((m_data.ifr_flags & IFF_RUNNING) == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_connection() {
|
||||
int status = EXIT_FAILURE;
|
||||
|
||||
try {
|
||||
m_ping = command_util::make_command(
|
||||
"ping -c 2 -W 2 -I " + m_interface + " " + string(CONNECTION_TEST_IP));
|
||||
status = m_ping->exec(true);
|
||||
m_ping.reset();
|
||||
} catch (std::exception& e) {
|
||||
}
|
||||
|
||||
return (status == EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
bool test() {
|
||||
try {
|
||||
return test_interface() && test_connection();
|
||||
} catch (network_error& e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool connected() {
|
||||
try {
|
||||
if (!test_interface())
|
||||
return false;
|
||||
return file_util::get_contents("/sys/class/net/" + m_interface + "/carrier")[0] == '1';
|
||||
} catch (network_error& e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool query_interface() {
|
||||
auto now = chrono::system_clock::now();
|
||||
if ((now - m_last_query) < chrono::seconds(1))
|
||||
return true;
|
||||
m_last_query = now;
|
||||
|
||||
/**
|
||||
* Query device driver for information
|
||||
*/
|
||||
virtual bool query() {
|
||||
struct ifaddrs* ifaddr;
|
||||
getifaddrs(&ifaddr);
|
||||
bool match = false;
|
||||
|
||||
if (getifaddrs(&ifaddr) == -1)
|
||||
return false;
|
||||
|
||||
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
|
||||
if (m_interface.compare(0, m_interface.length(), ifa->ifa_name) != 0)
|
||||
continue;
|
||||
match = true;
|
||||
|
||||
switch (ifa->ifa_addr->sa_family) {
|
||||
case AF_INET:
|
||||
char ip_buffer[NI_MAXHOST];
|
||||
getnameinfo(ifa->ifa_addr, sizeof(sockaddr_in), ip_buffer, NI_MAXHOST, nullptr, 0,
|
||||
NI_NUMERICHOST);
|
||||
m_linkdata.ip_address = string(ip_buffer);
|
||||
m_status.ip = string{ip_buffer};
|
||||
break;
|
||||
|
||||
case AF_PACKET:
|
||||
if (ifa->ifa_data == nullptr)
|
||||
continue;
|
||||
struct rtnl_link_stats* link_state =
|
||||
reinterpret_cast<struct rtnl_link_stats*>(ifa->ifa_data);
|
||||
m_linkdata.previous = m_linkdata.current;
|
||||
m_linkdata.current.transmitted = link_state->tx_bytes;
|
||||
m_linkdata.current.received = link_state->rx_bytes;
|
||||
m_linkdata.current.time = chrono::system_clock::now();
|
||||
reinterpret_cast<decltype(link_state)>(ifa->ifa_data);
|
||||
m_status.previous = m_status.current;
|
||||
m_status.current.transmitted = link_state->tx_bytes;
|
||||
m_status.current.received = link_state->rx_bytes;
|
||||
m_status.current.time = chrono::system_clock::now();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
|
||||
return match;
|
||||
return true;
|
||||
}
|
||||
|
||||
string ip() {
|
||||
if (!test_interface())
|
||||
throw network_error("Interface is not up");
|
||||
if (!query_interface())
|
||||
throw network_error("Failed to query interface");
|
||||
return m_linkdata.ip_address;
|
||||
/**
|
||||
* Check current connection state
|
||||
*/
|
||||
virtual bool connected() const = 0;
|
||||
|
||||
/**
|
||||
* Run ping command to test internet connectivity
|
||||
*/
|
||||
virtual bool ping() const {
|
||||
try {
|
||||
auto exec = "ping -c 2 -W 2 -I " + m_interface + " " + string(CONNECTION_TEST_IP);
|
||||
auto ping = command_util::make_command(exec);
|
||||
return ping && ping->exec(true) == EXIT_SUCCESS;
|
||||
} catch (const std::exception& err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
string downspeed(int minwidth = 3) {
|
||||
if (!query_interface())
|
||||
throw network_error("Failed to query interface");
|
||||
|
||||
float bytes_diff = m_linkdata.current.received - m_linkdata.previous.received;
|
||||
float time_diff =
|
||||
chrono::duration_cast<chrono::seconds>(m_linkdata.current.time - m_linkdata.previous.time)
|
||||
.count();
|
||||
float speed = bytes_diff / time_diff;
|
||||
|
||||
speed /= 1000; // convert to KB
|
||||
int suffix_n = 0;
|
||||
vector<string> suffixes{"KB", "MB", "GB"};
|
||||
|
||||
while (speed >= 1000 && suffix_n < (int)suffixes.size() - 1) {
|
||||
suffix_n++;
|
||||
speed /= 1000;
|
||||
/**
|
||||
* Get interface ip address
|
||||
*/
|
||||
string ip() const {
|
||||
return m_status.ip;
|
||||
}
|
||||
|
||||
return string_util::from_stream(stringstream() << std::setw(minwidth) << std::setfill(' ')
|
||||
<< std::setprecision(0) << std::fixed << speed
|
||||
<< " " << suffixes[suffix_n] << "/s");
|
||||
/**
|
||||
* Get download speed rate
|
||||
*/
|
||||
string downspeed(int minwidth = 3) const {
|
||||
float bytes_diff = m_status.current.received - m_status.previous.received;
|
||||
return format_speedrate(bytes_diff, minwidth);
|
||||
}
|
||||
|
||||
string upspeed(int minwidth = 3) {
|
||||
if (!query_interface())
|
||||
throw network_error("Failed to query interface");
|
||||
|
||||
float bytes_diff = m_linkdata.current.transmitted - m_linkdata.previous.transmitted;
|
||||
float time_diff =
|
||||
chrono::duration_cast<chrono::seconds>(m_linkdata.current.time - m_linkdata.previous.time)
|
||||
.count();
|
||||
float speed = bytes_diff / time_diff;
|
||||
|
||||
speed /= 1000; // convert to KB
|
||||
int suffix_n = 0;
|
||||
vector<string> suffixes{"KB", "MB", "GB"};
|
||||
|
||||
while (speed >= 1000 && suffix_n < (int)suffixes.size() - 1) {
|
||||
suffix_n++;
|
||||
speed /= 1000;
|
||||
}
|
||||
|
||||
return string_util::from_stream(stringstream() << std::setw(minwidth) << std::setfill(' ')
|
||||
<< std::setprecision(0) << std::fixed << speed
|
||||
<< " " << suffixes[suffix_n] << "/s");
|
||||
/**
|
||||
* Get upload speed rate
|
||||
*/
|
||||
string upspeed(int minwidth = 3) const {
|
||||
float bytes_diff = m_status.current.transmitted - m_status.previous.transmitted;
|
||||
return format_speedrate(bytes_diff, minwidth);
|
||||
}
|
||||
|
||||
protected:
|
||||
unique_ptr<command_util::command> m_ping;
|
||||
/**
|
||||
* Test if the network interface is in a valid state
|
||||
*/
|
||||
bool test_interface(struct ifreq& request) const {
|
||||
if ((ioctl(m_socketfd, SIOCGIFFLAGS, &request)) == -1)
|
||||
return false;
|
||||
if ((request.ifr_flags & IFF_UP) == 0)
|
||||
return false;
|
||||
if ((request.ifr_flags & IFF_RUNNING) == 0)
|
||||
return false;
|
||||
|
||||
auto operstate = file_util::get_contents("/sys/class/net/" + m_interface + "/operstate");
|
||||
|
||||
return operstate.compare(0, 2, "up") == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format up- and download speed
|
||||
*/
|
||||
string format_speedrate(float bytes_diff, int minwidth) const {
|
||||
const auto duration = m_status.current.time - m_status.previous.time;
|
||||
float time_diff = chrono::duration_cast<chrono::seconds>(duration).count();
|
||||
float speedrate = bytes_diff / (time_diff ? time_diff : 1);
|
||||
|
||||
vector<string> suffixes{"GB", "MB"};
|
||||
string suffix{"KB"};
|
||||
|
||||
while ((speedrate /= 1000) > 999) {
|
||||
suffix = suffixes.back();
|
||||
suffixes.pop_back();
|
||||
}
|
||||
|
||||
return string_util::from_stream(stringstream() << std::setw(minwidth) << std::setfill(' ')
|
||||
<< std::setprecision(0) << std::fixed
|
||||
<< speedrate << " " << suffix << "/s");
|
||||
}
|
||||
|
||||
int m_socketfd = 0;
|
||||
link_status m_status;
|
||||
string m_interface;
|
||||
string m_ip;
|
||||
struct ifreq m_data;
|
||||
int m_fd = 0;
|
||||
|
||||
linkdata_t m_linkdata;
|
||||
|
||||
chrono::system_clock::time_point m_last_query;
|
||||
};
|
||||
|
||||
// }}}
|
||||
@ -231,18 +218,56 @@ namespace net {
|
||||
|
||||
class wired_network : public network {
|
||||
public:
|
||||
explicit wired_network(string interface) : network(interface) {
|
||||
struct ethtool_cmd e;
|
||||
e.cmd = ETHTOOL_GSET;
|
||||
explicit wired_network(string interface) : network(interface) {}
|
||||
|
||||
m_data.ifr_data = (caddr_t)&e;
|
||||
/**
|
||||
* Query device driver for information
|
||||
*/
|
||||
bool query() override {
|
||||
if (!network::query())
|
||||
return false;
|
||||
|
||||
if (ioctl(m_fd, SIOCETHTOOL, &m_data) == 0)
|
||||
m_linkspeed = (e.speed == USHRT_MAX ? 0 : e.speed);
|
||||
struct ethtool_cmd ethernet_data;
|
||||
ethernet_data.cmd = ETHTOOL_GSET;
|
||||
|
||||
struct ifreq request;
|
||||
strncpy(request.ifr_name, m_interface.c_str(), IFNAMSIZ - 1);
|
||||
request.ifr_data = reinterpret_cast<caddr_t>(ðernet_data);
|
||||
|
||||
if (ioctl(m_socketfd, SIOCETHTOOL, &request) == -1)
|
||||
return false;
|
||||
|
||||
m_linkspeed = ethernet_data.speed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string link_speed() {
|
||||
return string((m_linkspeed == 0 ? "???" : to_string(m_linkspeed)) + " Mbit/s");
|
||||
/**
|
||||
* Check current connection state
|
||||
*/
|
||||
bool connected() const override {
|
||||
struct ifreq request;
|
||||
struct ethtool_value ethernet_data;
|
||||
|
||||
strncpy(request.ifr_name, m_interface.c_str(), IFNAMSIZ - 1);
|
||||
ethernet_data.cmd = ETHTOOL_GLINK;
|
||||
request.ifr_data = reinterpret_cast<caddr_t>(ðernet_data);
|
||||
|
||||
if (!network::test_interface(request))
|
||||
return false;
|
||||
|
||||
if (ioctl(m_socketfd, SIOCETHTOOL, &request) != -1)
|
||||
return ethernet_data.data != 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* about the current connection
|
||||
*/
|
||||
string linkspeed() const {
|
||||
return (m_linkspeed == 0 ? "???" : to_string(m_linkspeed)) + " Mbit/s";
|
||||
}
|
||||
|
||||
private:
|
||||
@ -252,182 +277,153 @@ namespace net {
|
||||
// }}}
|
||||
// class: wireless_network {{{
|
||||
|
||||
struct wireless_info {
|
||||
std::bitset<5> flags;
|
||||
string essid{IW_ESSID_MAX_SIZE + 1};
|
||||
int quality = 0;
|
||||
int quality_max = 0;
|
||||
int quality_avg = 0;
|
||||
int signal = 0;
|
||||
int signal_max = 0;
|
||||
int noise = 0;
|
||||
int noise_max = 0;
|
||||
int bitrate = 0;
|
||||
double frequency = 0;
|
||||
};
|
||||
|
||||
enum wireless_flags {
|
||||
ESSID = 0,
|
||||
QUALITY = 1,
|
||||
SIGNAL = 2,
|
||||
NOISE = 3,
|
||||
FREQUENCY = 4,
|
||||
};
|
||||
|
||||
class wireless_network : public network {
|
||||
public:
|
||||
wireless_network(string interface) : network(interface) {
|
||||
std::strcpy((char*)&m_iw.ifr_ifrn.ifrn_name, m_interface.c_str());
|
||||
wireless_network(string interface) : network(interface) {}
|
||||
|
||||
if (!m_info)
|
||||
m_info.reset(new wireless_info());
|
||||
}
|
||||
/**
|
||||
* Query the wireless device for information
|
||||
* about the current connection
|
||||
*/
|
||||
bool query() override {
|
||||
if (!network::query())
|
||||
return false;
|
||||
|
||||
string essid() {
|
||||
if (!query_interface())
|
||||
return "";
|
||||
if (!m_info->flags.test(wireless_flags::ESSID))
|
||||
return "";
|
||||
return m_info->essid;
|
||||
}
|
||||
|
||||
float signal_quality() {
|
||||
if (!query_interface())
|
||||
return 0;
|
||||
if (m_info->flags.test(wireless_flags::QUALITY))
|
||||
return 2 * (signal_dbm() + 100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
float signal_dbm() {
|
||||
if (!query_interface())
|
||||
return 0;
|
||||
if (m_info->flags.test(wireless_flags::QUALITY))
|
||||
return m_info->quality + m_info->noise - 256;
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool query_interface() {
|
||||
if ((chrono::system_clock::now() - m_last_query) < chrono::seconds(1))
|
||||
return true;
|
||||
|
||||
network::query_interface();
|
||||
|
||||
auto ifname = m_interface.c_str();
|
||||
auto socket_fd = iw_sockets_open();
|
||||
|
||||
if (socket_fd == -1)
|
||||
return false;
|
||||
|
||||
auto on_exit = scope_util::make_exit_handler<>([&]() { iw_sockets_close(socket_fd); });
|
||||
{
|
||||
wireless_config wcfg;
|
||||
struct iwreq req;
|
||||
|
||||
if (iw_get_basic_config(socket_fd, ifname, &wcfg) == -1)
|
||||
if (iw_get_ext(socket_fd, m_interface.c_str(), SIOCGIWMODE, &req) == -1)
|
||||
return false;
|
||||
|
||||
// reset flags
|
||||
m_info->flags.none();
|
||||
// Ignore interfaces in ad-hoc mode
|
||||
if (req.u.mode == IW_MODE_ADHOC)
|
||||
return false;
|
||||
|
||||
if (wcfg.has_essid && wcfg.essid_on) {
|
||||
m_info->essid = {wcfg.essid, 0, IW_ESSID_MAX_SIZE};
|
||||
m_info->flags |= wireless_flags::ESSID;
|
||||
}
|
||||
query_essid(socket_fd);
|
||||
query_quality(socket_fd);
|
||||
|
||||
if (wcfg.has_freq) {
|
||||
m_info->frequency = wcfg.freq;
|
||||
m_info->flags |= wireless_flags::FREQUENCY;
|
||||
}
|
||||
iw_sockets_close(socket_fd);
|
||||
|
||||
if (wcfg.mode == IW_MODE_ADHOC)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check current connection state
|
||||
*/
|
||||
bool connected() const override {
|
||||
struct ifreq request;
|
||||
strncpy(request.ifr_name, m_interface.c_str(), IFNAMSIZ - 1);
|
||||
|
||||
if (!network::test_interface(request))
|
||||
return false;
|
||||
if (m_essid.empty())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ESSID reported by last query
|
||||
*/
|
||||
string essid() const {
|
||||
return m_essid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal strength percentage reported by last query
|
||||
*/
|
||||
int signal() const {
|
||||
return m_signalstrength.percentage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Link quality percentage reported by last query
|
||||
*/
|
||||
int quality() const {
|
||||
return m_linkquality.percentage();
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Query for ESSID
|
||||
*/
|
||||
void query_essid(const int& socket_fd) {
|
||||
char essid[IW_ESSID_MAX_SIZE + 1];
|
||||
|
||||
struct iwreq req;
|
||||
req.u.essid.pointer = &essid;
|
||||
req.u.essid.length = sizeof(essid);
|
||||
req.u.essid.flags = 0;
|
||||
|
||||
if (iw_get_ext(socket_fd, m_interface.c_str(), SIOCGIWESSID, &req) != -1) {
|
||||
m_essid = string{essid};
|
||||
} else {
|
||||
m_essid.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for device driver quality values
|
||||
*/
|
||||
void query_quality(const int& socket_fd) {
|
||||
iwrange range;
|
||||
if (iw_get_range_info(socket_fd, ifname, &range) == -1)
|
||||
return false;
|
||||
|
||||
iwstats stats;
|
||||
if (iw_get_stats(socket_fd, ifname, &stats, &range, 1) == -1)
|
||||
return false;
|
||||
|
||||
if (stats.qual.updated & IW_QUAL_RCPI) {
|
||||
if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
|
||||
m_info->quality = stats.qual.qual;
|
||||
m_info->quality_max = range.max_qual.qual;
|
||||
m_info->quality_avg = range.avg_qual.qual;
|
||||
m_info->flags |= wireless_flags::QUALITY;
|
||||
// Fill range
|
||||
if (iw_get_range_info(socket_fd, m_interface.c_str(), &range) == -1)
|
||||
return;
|
||||
// Fill stats
|
||||
if (iw_get_stats(socket_fd, m_interface.c_str(), &stats, &range, 1) == -1)
|
||||
return;
|
||||
|
||||
// Check if the driver supplies the quality value
|
||||
if (stats.qual.updated & IW_QUAL_QUAL_INVALID)
|
||||
return;
|
||||
// Check if the driver supplies the quality level value
|
||||
if (stats.qual.updated & IW_QUAL_LEVEL_INVALID)
|
||||
return;
|
||||
|
||||
// Check if the link quality has been uodated
|
||||
if (stats.qual.updated & IW_QUAL_QUAL_UPDATED) {
|
||||
m_linkquality.val = stats.qual.qual;
|
||||
m_linkquality.max = range.max_qual.qual;
|
||||
}
|
||||
|
||||
if (stats.qual.updated & IW_QUAL_RCPI) {
|
||||
if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
|
||||
m_info->signal = stats.qual.level / 2.0 - 110 + 0.5;
|
||||
m_info->flags |= wireless_flags::SIGNAL;
|
||||
}
|
||||
if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
|
||||
m_info->noise = stats.qual.noise / 2.0 - 110 + 0.5;
|
||||
m_info->flags |= wireless_flags::NOISE;
|
||||
}
|
||||
} else {
|
||||
if ((stats.qual.updated & IW_QUAL_DBM) || stats.qual.level > range.max_qual.level) {
|
||||
if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
|
||||
m_info->signal = stats.qual.level;
|
||||
if (m_info->signal > 63)
|
||||
m_info->signal -= 256;
|
||||
m_info->flags |= wireless_flags::SIGNAL;
|
||||
}
|
||||
if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
|
||||
m_info->noise = stats.qual.noise;
|
||||
if (m_info->noise > 63)
|
||||
m_info->noise -= 256;
|
||||
m_info->flags |= wireless_flags::NOISE;
|
||||
}
|
||||
} else {
|
||||
if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
|
||||
m_info->signal = stats.qual.level;
|
||||
m_info->signal_max = range.max_qual.level;
|
||||
m_info->flags |= wireless_flags::SIGNAL;
|
||||
}
|
||||
if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
|
||||
m_info->noise = stats.qual.noise;
|
||||
m_info->noise_max = range.max_qual.noise;
|
||||
m_info->flags |= wireless_flags::NOISE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
|
||||
m_info->quality = stats.qual.qual;
|
||||
m_info->flags |= wireless_flags::QUALITY;
|
||||
}
|
||||
if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
|
||||
m_info->quality = stats.qual.level;
|
||||
m_info->flags |= wireless_flags::SIGNAL;
|
||||
}
|
||||
if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
|
||||
m_info->quality = stats.qual.noise;
|
||||
m_info->flags |= wireless_flags::NOISE;
|
||||
}
|
||||
}
|
||||
// Check if the signal strength has been uodated
|
||||
if (stats.qual.updated & IW_QUAL_LEVEL_UPDATED) {
|
||||
m_signalstrength.val = stats.qual.level;
|
||||
m_signalstrength.max = range.max_qual.level;
|
||||
|
||||
// struct iwreq wrq;
|
||||
// if (iw_get_ext(socket_fd, ifname, SIOCGIWRATE, &wrq) != -1)
|
||||
// m_info->bitrate = wrq.u.bitrate.value;
|
||||
|
||||
return true;
|
||||
// Check if the values are defined in dBm
|
||||
if (stats.qual.level > range.max_qual.level) {
|
||||
m_signalstrength.val -= 0x100;
|
||||
m_signalstrength.max -= 0x100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct iwreq m_iw;
|
||||
shared_ptr<wireless_info> m_info;
|
||||
string m_essid;
|
||||
quality_range m_signalstrength;
|
||||
quality_range m_linkquality;
|
||||
};
|
||||
|
||||
// }}}
|
||||
|
||||
/**
|
||||
* Test if interface with given name is a wireless device
|
||||
*/
|
||||
inline bool is_wireless_interface(string ifname) {
|
||||
return file_util::exists("/sys/class/net/" + ifname + "/wireless");
|
||||
}
|
||||
|
||||
using wireless_t = unique_ptr<wireless_network>;
|
||||
using wired_t = unique_ptr<wired_network>;
|
||||
}
|
||||
|
||||
LEMONBUDDY_NS_END
|
||||
|
@ -31,8 +31,6 @@
|
||||
#define PIPE_READ 0
|
||||
#define PIPE_WRITE 1
|
||||
|
||||
#define LOG(m) std::cout << m << std::endl
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "debug.hpp"
|
||||
#endif
|
||||
|
@ -1,22 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "adapters/net.hpp"
|
||||
#include "components/config.hpp"
|
||||
#include "drawtypes/animation.hpp"
|
||||
#include "drawtypes/animation.hpp"
|
||||
#include "drawtypes/label.hpp"
|
||||
#include "drawtypes/ramp.hpp"
|
||||
#include "modules/meta.hpp"
|
||||
|
||||
#define DEFAULT_FORMAT_CONNECTED TAG_LABEL_CONNECTED
|
||||
#define DEFAULT_FORMAT_DISCONNECTED TAG_LABEL_DISCONNECTED
|
||||
#define DEFAULT_FORMAT_PACKETLOSS TAG_LABEL_CONNECTED
|
||||
|
||||
#define DEFAULT_LABEL_CONNECTED "%ifname% %local_ip%"
|
||||
#define DEFAULT_LABEL_DISCONNECTED ""
|
||||
#define DEFAULT_LABEL_PACKETLOSS ""
|
||||
|
||||
LEMONBUDDY_NS
|
||||
|
||||
namespace modules {
|
||||
enum class connection_state { NONE = 0, CONNECTED, DISCONNECTED, PACKETLOSS };
|
||||
|
||||
class network_module : public timer_module<network_module> {
|
||||
public:
|
||||
@ -24,91 +19,78 @@ namespace modules {
|
||||
|
||||
void setup() {
|
||||
// Load configuration values
|
||||
m_interface = m_conf.get<string>(name(), "interface");
|
||||
REQ_CONFIG_VALUE(name(), m_interface, "interface");
|
||||
GET_CONFIG_VALUE(name(), m_ping_nth_update, "ping-interval");
|
||||
GET_CONFIG_VALUE(name(), m_udspeed_minwidth, "udspeed-minwidth");
|
||||
|
||||
m_interval = chrono::duration<double>(m_conf.get<float>(name(), "interval", 1));
|
||||
m_ping_nth_update = m_conf.get<int>(name(), "ping-interval", 0);
|
||||
m_udspeed_minwidth = m_conf.get<int>(name(), "udspeed-minwidth", m_udspeed_minwidth);
|
||||
|
||||
// Add formats
|
||||
m_formatter->add(
|
||||
FORMAT_CONNECTED, DEFAULT_FORMAT_CONNECTED, {TAG_RAMP_SIGNAL, TAG_LABEL_CONNECTED});
|
||||
m_formatter->add(FORMAT_DISCONNECTED, DEFAULT_FORMAT_DISCONNECTED, {TAG_LABEL_DISCONNECTED});
|
||||
FORMAT_CONNECTED, TAG_LABEL_CONNECTED, {TAG_RAMP_SIGNAL, TAG_RAMP_QUALITY, TAG_LABEL_CONNECTED});
|
||||
m_formatter->add(FORMAT_DISCONNECTED, TAG_LABEL_DISCONNECTED, {TAG_LABEL_DISCONNECTED});
|
||||
|
||||
// Create elements for format-connected
|
||||
if (m_formatter->has(TAG_RAMP_SIGNAL, FORMAT_CONNECTED))
|
||||
m_ramp_signal = get_config_ramp(m_conf, name(), TAG_RAMP_SIGNAL);
|
||||
if (m_formatter->has(TAG_RAMP_QUALITY, FORMAT_CONNECTED))
|
||||
m_ramp_quality = get_config_ramp(m_conf, name(), TAG_RAMP_QUALITY);
|
||||
if (m_formatter->has(TAG_LABEL_CONNECTED, FORMAT_CONNECTED)) {
|
||||
m_label_connected =
|
||||
get_optional_config_label(m_conf, name(), TAG_LABEL_CONNECTED, DEFAULT_LABEL_CONNECTED);
|
||||
m_label_connected_tokenized = m_label_connected->clone();
|
||||
m_label[connection_state::CONNECTED] =
|
||||
get_optional_config_label(m_conf, name(), TAG_LABEL_CONNECTED, "%ifname% %local_ip%");
|
||||
m_tokenized[connection_state::CONNECTED] =
|
||||
m_label[connection_state::CONNECTED]->clone();
|
||||
}
|
||||
|
||||
// Create elements for format-disconnected
|
||||
if (m_formatter->has(TAG_LABEL_DISCONNECTED, FORMAT_DISCONNECTED)) {
|
||||
m_label_disconnected = get_optional_config_label(
|
||||
m_conf, name(), TAG_LABEL_DISCONNECTED, DEFAULT_LABEL_DISCONNECTED);
|
||||
m_label_disconnected->replace_token("%ifname%", m_interface);
|
||||
m_label[connection_state::DISCONNECTED] =
|
||||
get_optional_config_label(m_conf, name(), TAG_LABEL_DISCONNECTED, "");
|
||||
m_label[connection_state::DISCONNECTED]->replace_token("%ifname%", m_interface);
|
||||
}
|
||||
|
||||
// Create elements for format-packetloss if we are told to test connectivity
|
||||
if (m_ping_nth_update > 0) {
|
||||
m_formatter->add(FORMAT_PACKETLOSS, DEFAULT_FORMAT_PACKETLOSS,
|
||||
m_formatter->add(FORMAT_PACKETLOSS, TAG_LABEL_CONNECTED,
|
||||
{TAG_ANIMATION_PACKETLOSS, TAG_LABEL_PACKETLOSS, TAG_LABEL_CONNECTED});
|
||||
|
||||
if (m_formatter->has(TAG_LABEL_PACKETLOSS, FORMAT_PACKETLOSS)) {
|
||||
m_label_packetloss = get_optional_config_label(
|
||||
m_conf, name(), TAG_LABEL_PACKETLOSS, DEFAULT_LABEL_PACKETLOSS);
|
||||
m_label_packetloss_tokenized = m_label_packetloss->clone();
|
||||
m_label[connection_state::PACKETLOSS] =
|
||||
get_optional_config_label(m_conf, name(), TAG_LABEL_PACKETLOSS, "");
|
||||
m_tokenized[connection_state::PACKETLOSS] =
|
||||
m_label[connection_state::PACKETLOSS]->clone();
|
||||
}
|
||||
if (m_formatter->has(TAG_ANIMATION_PACKETLOSS, FORMAT_PACKETLOSS))
|
||||
m_animation_packetloss = get_config_animation(m_conf, name(), TAG_ANIMATION_PACKETLOSS);
|
||||
}
|
||||
|
||||
// Get an intstance of the network interface
|
||||
if (net::is_wireless_interface(m_interface)) {
|
||||
m_wireless_network = make_unique<net::wireless_network>(m_interface);
|
||||
} else {
|
||||
m_wired_network = make_unique<net::wired_network>(m_interface);
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
timer_module::start();
|
||||
if (net::is_wireless_interface(m_interface))
|
||||
m_wireless = net::wireless_t{new net::wireless_t::element_type(m_interface)};
|
||||
else
|
||||
m_wired = net::wired_t{new net::wired_t::element_type(m_interface)};
|
||||
|
||||
// We only need to start the subthread if the packetloss animation is used
|
||||
if (m_animation_packetloss)
|
||||
m_threads.emplace_back(thread(&network_module::subthread_routine, this));
|
||||
}
|
||||
|
||||
bool update() {
|
||||
string ip, essid, linkspeed;
|
||||
int signal_quality = 0;
|
||||
net::network* network = m_wireless ? dynamic_cast<net::network*>(m_wireless.get())
|
||||
: dynamic_cast<net::network*>(m_wired.get());
|
||||
|
||||
net::network* network = nullptr;
|
||||
|
||||
// Process data for wireless network interfaces
|
||||
if (m_wireless_network) {
|
||||
network = m_wireless_network.get();
|
||||
|
||||
try {
|
||||
essid = m_wireless_network->essid();
|
||||
signal_quality = m_wireless_network->signal_quality();
|
||||
} catch (net::wireless_network_error& e) {
|
||||
m_log.trace("%s: %s", name(), e.what());
|
||||
if (!network->query()) {
|
||||
m_log.warn("%s: Failed to query interface '%s'", name(), m_interface);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_signal_quality = signal_quality;
|
||||
|
||||
// Process data for wired network interfaces
|
||||
} else if (m_wired_network) {
|
||||
network = m_wired_network.get();
|
||||
linkspeed = m_wired_network->link_speed();
|
||||
}
|
||||
|
||||
if (network != nullptr) {
|
||||
try {
|
||||
ip = network->ip();
|
||||
} catch (net::network_error& e) {
|
||||
m_log.trace("%s: %s", name(), e.what());
|
||||
if (m_wireless) {
|
||||
m_signal = m_wireless->signal();
|
||||
m_quality = m_wireless->quality();
|
||||
}
|
||||
} catch (const net::network_error& err) {
|
||||
m_log.warn("%s: Error getting interface data (%s)", name(), err.what());
|
||||
}
|
||||
|
||||
m_connected = network->connected();
|
||||
@ -117,40 +99,39 @@ namespace modules {
|
||||
if (m_counter == -1) {
|
||||
m_counter = 0;
|
||||
} else if (m_ping_nth_update > 0 && m_connected && (++m_counter % m_ping_nth_update) == 0) {
|
||||
m_conseq_packetloss = !network->test();
|
||||
m_packetloss = !network->ping();
|
||||
m_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update label contents
|
||||
if (m_label_connected || m_label_packetloss) {
|
||||
auto replace_tokens = [&](label_t label) {
|
||||
label->replace_token("%ifname%", m_interface);
|
||||
label->replace_token("%local_ip%", !ip.empty() ? ip : "x.x.x.x");
|
||||
|
||||
if (m_wired_network) {
|
||||
label->replace_token("%linkspeed%", linkspeed);
|
||||
} else if (m_wireless_network) {
|
||||
label->replace_token("%essid%", !essid.empty() ? essid : "UNKNOWN");
|
||||
label->replace_token("%signal%", to_string(signal_quality) + "%");
|
||||
}
|
||||
|
||||
auto upspeed = network->upspeed(m_udspeed_minwidth);
|
||||
auto downspeed = network->downspeed(m_udspeed_minwidth);
|
||||
|
||||
// Update label contents
|
||||
auto replace_tokens = [&](label_t& label) {
|
||||
label->replace_token("%ifname%", m_interface);
|
||||
label->replace_token("%local_ip%", network->ip());
|
||||
label->replace_token("%upspeed%", upspeed);
|
||||
label->replace_token("%downspeed%", downspeed);
|
||||
|
||||
if (m_wired) {
|
||||
label->replace_token("%linkspeed%", m_wired->linkspeed());
|
||||
} else if (m_wireless) {
|
||||
label->replace_token("%essid%", m_wireless->essid());
|
||||
label->replace_token("%signal%", to_string(m_signal) + "%");
|
||||
label->replace_token("%quality%", to_string(m_quality) + "%");
|
||||
}
|
||||
};
|
||||
|
||||
if (m_label_connected) {
|
||||
m_label_connected_tokenized->m_text = m_label_connected->m_text;
|
||||
replace_tokens(m_label_connected_tokenized);
|
||||
if (m_label[connection_state::CONNECTED]) {
|
||||
m_tokenized[connection_state::CONNECTED]->m_text =
|
||||
m_label[connection_state::CONNECTED]->m_text;
|
||||
replace_tokens(m_tokenized[connection_state::CONNECTED]);
|
||||
}
|
||||
|
||||
if (m_label_packetloss) {
|
||||
m_label_packetloss_tokenized->m_text = m_label_packetloss->m_text;
|
||||
replace_tokens(m_label_packetloss_tokenized);
|
||||
}
|
||||
if (m_label[connection_state::PACKETLOSS]) {
|
||||
m_tokenized[connection_state::PACKETLOSS]->m_text =
|
||||
m_label[connection_state::PACKETLOSS]->m_text;
|
||||
replace_tokens(m_tokenized[connection_state::PACKETLOSS]);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -159,7 +140,7 @@ namespace modules {
|
||||
string get_format() {
|
||||
if (!m_connected)
|
||||
return FORMAT_DISCONNECTED;
|
||||
else if (m_conseq_packetloss && m_ping_nth_update > 0)
|
||||
else if (m_packetloss && m_ping_nth_update > 0)
|
||||
return FORMAT_PACKETLOSS;
|
||||
else
|
||||
return FORMAT_CONNECTED;
|
||||
@ -167,15 +148,17 @@ namespace modules {
|
||||
|
||||
bool build(builder* builder, string tag) {
|
||||
if (tag == TAG_LABEL_CONNECTED)
|
||||
builder->node(m_label_connected_tokenized);
|
||||
builder->node(m_tokenized[connection_state::CONNECTED]);
|
||||
else if (tag == TAG_LABEL_DISCONNECTED)
|
||||
builder->node(m_label_disconnected);
|
||||
builder->node(m_label[connection_state::DISCONNECTED]);
|
||||
else if (tag == TAG_LABEL_PACKETLOSS)
|
||||
builder->node(m_label_packetloss_tokenized);
|
||||
builder->node(m_tokenized[connection_state::PACKETLOSS]);
|
||||
else if (tag == TAG_ANIMATION_PACKETLOSS)
|
||||
builder->node(m_animation_packetloss->get());
|
||||
else if (tag == TAG_RAMP_SIGNAL)
|
||||
builder->node(m_ramp_signal->get_by_percentage(m_signal_quality));
|
||||
builder->node(m_ramp_signal->get_by_percentage(m_signal));
|
||||
else if (tag == TAG_RAMP_QUALITY)
|
||||
builder->node(m_ramp_quality->get_by_percentage(m_quality));
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
@ -185,13 +168,12 @@ namespace modules {
|
||||
void subthread_routine() {
|
||||
this_thread::yield();
|
||||
|
||||
const auto dur =
|
||||
chrono::duration<double>(float(m_animation_packetloss->framerate()) / 1000.0f);
|
||||
const chrono::milliseconds framerate{m_animation_packetloss->framerate()};
|
||||
const auto dur = chrono::duration<double>(framerate);
|
||||
|
||||
while (enabled()) {
|
||||
if (m_connected && m_conseq_packetloss)
|
||||
if (m_connected && m_packetloss)
|
||||
broadcast();
|
||||
|
||||
sleep(dur);
|
||||
}
|
||||
|
||||
@ -202,31 +184,31 @@ namespace modules {
|
||||
static constexpr auto FORMAT_CONNECTED = "format-connected";
|
||||
static constexpr auto FORMAT_PACKETLOSS = "format-packetloss";
|
||||
static constexpr auto FORMAT_DISCONNECTED = "format-disconnected";
|
||||
|
||||
static constexpr auto TAG_RAMP_SIGNAL = "<ramp-signal>";
|
||||
static constexpr auto TAG_RAMP_QUALITY = "<ramp-quality>";
|
||||
static constexpr auto TAG_LABEL_CONNECTED = "<label-connected>";
|
||||
static constexpr auto TAG_LABEL_DISCONNECTED = "<label-disconnected>";
|
||||
static constexpr auto TAG_LABEL_PACKETLOSS = "<label-packetloss>";
|
||||
static constexpr auto TAG_ANIMATION_PACKETLOSS = "<animation-packetloss>";
|
||||
|
||||
unique_ptr<net::wired_network> m_wired_network;
|
||||
unique_ptr<net::wireless_network> m_wireless_network;
|
||||
net::wired_t m_wired;
|
||||
net::wireless_t m_wireless;
|
||||
|
||||
ramp_t m_ramp_signal;
|
||||
ramp_t m_ramp_quality;
|
||||
animation_t m_animation_packetloss;
|
||||
label_t m_label_connected;
|
||||
label_t m_label_connected_tokenized;
|
||||
label_t m_label_disconnected;
|
||||
label_t m_label_packetloss;
|
||||
label_t m_label_packetloss_tokenized;
|
||||
map<connection_state, label_t> m_label;
|
||||
map<connection_state, label_t> m_tokenized;
|
||||
|
||||
stateflag m_connected{false};
|
||||
stateflag m_conseq_packetloss{false};
|
||||
stateflag m_packetloss{false};
|
||||
|
||||
int m_signal = 0;
|
||||
int m_quality = 0;
|
||||
int m_counter = -1; // -1 to ignore the first run
|
||||
|
||||
string m_interface;
|
||||
int m_signal_quality;
|
||||
int m_ping_nth_update;
|
||||
int m_counter = -1; // -1 to ignore the first run
|
||||
int m_ping_nth_update = 0;
|
||||
int m_udspeed_minwidth = 3;
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user