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:
Michael Carlberg 2016-10-18 09:34:35 +02:00
parent 334dc7731d
commit 82f311b695
3 changed files with 381 additions and 405 deletions

View File

@ -39,21 +39,32 @@ LEMONBUDDY_NS
namespace net { namespace net {
DEFINE_ERROR(network_error); DEFINE_ERROR(network_error);
DEFINE_ERROR(wired_network_error);
DEFINE_ERROR(wireless_network_error);
// types {{{ // types {{{
struct bytes_t { struct quality_range {
uint32_t transmitted = 0; int val = 0;
uint32_t received = 0; int max = 0;
std::chrono::system_clock::time_point time;
int percentage() const {
if (max < 0)
return 2 * (val + 100);
return static_cast<float>(val) / max * 100.0f + 0.5f;
}
}; };
struct linkdata_t { using bytes_t = unsigned int;
string ip_address;
bytes_t previous; struct link_activity {
bytes_t current; 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 { class network {
public: public:
/**
* Construct network interface
*/
explicit network(string interface) : m_interface(interface) { explicit network(string interface) : m_interface(interface) {
if (if_nametoindex(m_interface.c_str()) == 0) if (if_nametoindex(m_interface.c_str()) == 0)
throw network_error("Invalid network interface \"" + m_interface + "\""); 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"); 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) * Destruct network interface
close(m_fd); */
virtual ~network() {
if (m_socketfd != -1)
close(m_socketfd);
} }
bool test_interface() { /**
if ((ioctl(m_fd, SIOCGIFFLAGS, &m_data)) == -1) * Query device driver for information
throw network_error("Failed to get flags"); */
if ((m_data.ifr_flags & IFF_UP) == 0) virtual bool query() {
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;
struct ifaddrs* ifaddr; struct ifaddrs* ifaddr;
getifaddrs(&ifaddr);
bool match = false; if (getifaddrs(&ifaddr) == -1)
return false;
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (m_interface.compare(0, m_interface.length(), ifa->ifa_name) != 0) if (m_interface.compare(0, m_interface.length(), ifa->ifa_name) != 0)
continue; continue;
match = true;
switch (ifa->ifa_addr->sa_family) { switch (ifa->ifa_addr->sa_family) {
case AF_INET: case AF_INET:
char ip_buffer[NI_MAXHOST]; char ip_buffer[NI_MAXHOST];
getnameinfo(ifa->ifa_addr, sizeof(sockaddr_in), ip_buffer, NI_MAXHOST, nullptr, 0, getnameinfo(ifa->ifa_addr, sizeof(sockaddr_in), ip_buffer, NI_MAXHOST, nullptr, 0,
NI_NUMERICHOST); NI_NUMERICHOST);
m_linkdata.ip_address = string(ip_buffer); m_status.ip = string{ip_buffer};
break; break;
case AF_PACKET: case AF_PACKET:
if (ifa->ifa_data == nullptr) if (ifa->ifa_data == nullptr)
continue; continue;
struct rtnl_link_stats* link_state = struct rtnl_link_stats* link_state =
reinterpret_cast<struct rtnl_link_stats*>(ifa->ifa_data); reinterpret_cast<decltype(link_state)>(ifa->ifa_data);
m_linkdata.previous = m_linkdata.current; m_status.previous = m_status.current;
m_linkdata.current.transmitted = link_state->tx_bytes; m_status.current.transmitted = link_state->tx_bytes;
m_linkdata.current.received = link_state->rx_bytes; m_status.current.received = link_state->rx_bytes;
m_linkdata.current.time = chrono::system_clock::now(); m_status.current.time = chrono::system_clock::now();
break; break;
} }
} }
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
return match; return true;
} }
string ip() { /**
if (!test_interface()) * Check current connection state
throw network_error("Interface is not up"); */
if (!query_interface()) virtual bool connected() const = 0;
throw network_error("Failed to query interface");
return m_linkdata.ip_address;
}
string downspeed(int minwidth = 3) { /**
if (!query_interface()) * Run ping command to test internet connectivity
throw network_error("Failed to query interface"); */
virtual bool ping() const {
float bytes_diff = m_linkdata.current.received - m_linkdata.previous.received; try {
float time_diff = auto exec = "ping -c 2 -W 2 -I " + m_interface + " " + string(CONNECTION_TEST_IP);
chrono::duration_cast<chrono::seconds>(m_linkdata.current.time - m_linkdata.previous.time) auto ping = command_util::make_command(exec);
.count(); return ping && ping->exec(true) == EXIT_SUCCESS;
float speed = bytes_diff / time_diff; } catch (const std::exception& err) {
return false;
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");
} }
string upspeed(int minwidth = 3) { /**
if (!query_interface()) * Get interface ip address
throw network_error("Failed to query interface"); */
string ip() const {
return m_status.ip;
}
float bytes_diff = m_linkdata.current.transmitted - m_linkdata.previous.transmitted; /**
float time_diff = * Get download speed rate
chrono::duration_cast<chrono::seconds>(m_linkdata.current.time - m_linkdata.previous.time) */
.count(); string downspeed(int minwidth = 3) const {
float speed = bytes_diff / time_diff; float bytes_diff = m_status.current.received - m_status.previous.received;
return format_speedrate(bytes_diff, minwidth);
}
speed /= 1000; // convert to KB /**
int suffix_n = 0; * Get upload speed rate
vector<string> suffixes{"KB", "MB", "GB"}; */
string upspeed(int minwidth = 3) const {
while (speed >= 1000 && suffix_n < (int)suffixes.size() - 1) { float bytes_diff = m_status.current.transmitted - m_status.previous.transmitted;
suffix_n++; return format_speedrate(bytes_diff, minwidth);
speed /= 1000;
}
return string_util::from_stream(stringstream() << std::setw(minwidth) << std::setfill(' ')
<< std::setprecision(0) << std::fixed << speed
<< " " << suffixes[suffix_n] << "/s");
} }
protected: 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_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 { class wired_network : public network {
public: public:
explicit wired_network(string interface) : network(interface) { explicit wired_network(string interface) : network(interface) {}
struct ethtool_cmd e;
e.cmd = ETHTOOL_GSET;
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) struct ethtool_cmd ethernet_data;
m_linkspeed = (e.speed == USHRT_MAX ? 0 : e.speed); 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>(&ethernet_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>(&ethernet_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: private:
@ -252,182 +277,153 @@ namespace net {
// }}} // }}}
// class: wireless_network {{{ // 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 { class wireless_network : public network {
public: public:
wireless_network(string interface) : network(interface) { wireless_network(string interface) : network(interface) {}
std::strcpy((char*)&m_iw.ifr_ifrn.ifrn_name, m_interface.c_str());
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(); auto socket_fd = iw_sockets_open();
if (socket_fd == -1) if (socket_fd == -1)
return false; return false;
auto on_exit = scope_util::make_exit_handler<>([&]() { iw_sockets_close(socket_fd); }); struct iwreq req;
{
wireless_config wcfg;
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; return false;
// reset flags // Ignore interfaces in ad-hoc mode
m_info->flags.none(); if (req.u.mode == IW_MODE_ADHOC)
return false;
if (wcfg.has_essid && wcfg.essid_on) { query_essid(socket_fd);
m_info->essid = {wcfg.essid, 0, IW_ESSID_MAX_SIZE}; query_quality(socket_fd);
m_info->flags |= wireless_flags::ESSID;
iw_sockets_close(socket_fd);
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;
iwstats stats;
// 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;
}
// 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;
// Check if the values are defined in dBm
if (stats.qual.level > range.max_qual.level) {
m_signalstrength.val -= 0x100;
m_signalstrength.max -= 0x100;
} }
if (wcfg.has_freq) {
m_info->frequency = wcfg.freq;
m_info->flags |= wireless_flags::FREQUENCY;
}
if (wcfg.mode == IW_MODE_ADHOC)
return true;
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;
}
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;
}
}
// struct iwreq wrq;
// if (iw_get_ext(socket_fd, ifname, SIOCGIWRATE, &wrq) != -1)
// m_info->bitrate = wrq.u.bitrate.value;
return true;
} }
} }
private: private:
struct iwreq m_iw;
shared_ptr<wireless_info> m_info; 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) { inline bool is_wireless_interface(string ifname) {
return file_util::exists("/sys/class/net/" + ifname + "/wireless"); 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 LEMONBUDDY_NS_END

View File

@ -31,8 +31,6 @@
#define PIPE_READ 0 #define PIPE_READ 0
#define PIPE_WRITE 1 #define PIPE_WRITE 1
#define LOG(m) std::cout << m << std::endl
#ifdef DEBUG #ifdef DEBUG
#include "debug.hpp" #include "debug.hpp"
#endif #endif

View File

@ -1,22 +1,17 @@
#pragma once #pragma once
#include "adapters/net.hpp" #include "adapters/net.hpp"
#include "components/config.hpp"
#include "drawtypes/animation.hpp"
#include "drawtypes/animation.hpp" #include "drawtypes/animation.hpp"
#include "drawtypes/label.hpp" #include "drawtypes/label.hpp"
#include "drawtypes/ramp.hpp" #include "drawtypes/ramp.hpp"
#include "modules/meta.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 LEMONBUDDY_NS
namespace modules { namespace modules {
enum class connection_state { NONE = 0, CONNECTED, DISCONNECTED, PACKETLOSS };
class network_module : public timer_module<network_module> { class network_module : public timer_module<network_module> {
public: public:
@ -24,133 +19,119 @@ namespace modules {
void setup() { void setup() {
// Load configuration values // 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_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 // Add formats
m_formatter->add( m_formatter->add(
FORMAT_CONNECTED, DEFAULT_FORMAT_CONNECTED, {TAG_RAMP_SIGNAL, TAG_LABEL_CONNECTED}); FORMAT_CONNECTED, TAG_LABEL_CONNECTED, {TAG_RAMP_SIGNAL, TAG_RAMP_QUALITY, TAG_LABEL_CONNECTED});
m_formatter->add(FORMAT_DISCONNECTED, DEFAULT_FORMAT_DISCONNECTED, {TAG_LABEL_DISCONNECTED}); m_formatter->add(FORMAT_DISCONNECTED, TAG_LABEL_DISCONNECTED, {TAG_LABEL_DISCONNECTED});
// Create elements for format-connected // Create elements for format-connected
if (m_formatter->has(TAG_RAMP_SIGNAL, FORMAT_CONNECTED)) if (m_formatter->has(TAG_RAMP_SIGNAL, FORMAT_CONNECTED))
m_ramp_signal = get_config_ramp(m_conf, name(), TAG_RAMP_SIGNAL); 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)) { if (m_formatter->has(TAG_LABEL_CONNECTED, FORMAT_CONNECTED)) {
m_label_connected = m_label[connection_state::CONNECTED] =
get_optional_config_label(m_conf, name(), TAG_LABEL_CONNECTED, DEFAULT_LABEL_CONNECTED); get_optional_config_label(m_conf, name(), TAG_LABEL_CONNECTED, "%ifname% %local_ip%");
m_label_connected_tokenized = m_label_connected->clone(); m_tokenized[connection_state::CONNECTED] =
m_label[connection_state::CONNECTED]->clone();
} }
// Create elements for format-disconnected // Create elements for format-disconnected
if (m_formatter->has(TAG_LABEL_DISCONNECTED, FORMAT_DISCONNECTED)) { if (m_formatter->has(TAG_LABEL_DISCONNECTED, FORMAT_DISCONNECTED)) {
m_label_disconnected = get_optional_config_label( m_label[connection_state::DISCONNECTED] =
m_conf, name(), TAG_LABEL_DISCONNECTED, DEFAULT_LABEL_DISCONNECTED); get_optional_config_label(m_conf, name(), TAG_LABEL_DISCONNECTED, "");
m_label_disconnected->replace_token("%ifname%", m_interface); m_label[connection_state::DISCONNECTED]->replace_token("%ifname%", m_interface);
} }
// Create elements for format-packetloss if we are told to test connectivity // Create elements for format-packetloss if we are told to test connectivity
if (m_ping_nth_update > 0) { 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}); {TAG_ANIMATION_PACKETLOSS, TAG_LABEL_PACKETLOSS, TAG_LABEL_CONNECTED});
if (m_formatter->has(TAG_LABEL_PACKETLOSS, FORMAT_PACKETLOSS)) { if (m_formatter->has(TAG_LABEL_PACKETLOSS, FORMAT_PACKETLOSS)) {
m_label_packetloss = get_optional_config_label( m_label[connection_state::PACKETLOSS] =
m_conf, name(), TAG_LABEL_PACKETLOSS, DEFAULT_LABEL_PACKETLOSS); get_optional_config_label(m_conf, name(), TAG_LABEL_PACKETLOSS, "");
m_label_packetloss_tokenized = m_label_packetloss->clone(); m_tokenized[connection_state::PACKETLOSS] =
m_label[connection_state::PACKETLOSS]->clone();
} }
if (m_formatter->has(TAG_ANIMATION_PACKETLOSS, FORMAT_PACKETLOSS)) if (m_formatter->has(TAG_ANIMATION_PACKETLOSS, FORMAT_PACKETLOSS))
m_animation_packetloss = get_config_animation(m_conf, name(), TAG_ANIMATION_PACKETLOSS); m_animation_packetloss = get_config_animation(m_conf, name(), TAG_ANIMATION_PACKETLOSS);
} }
// Get an intstance of the network interface // Get an intstance of the network interface
if (net::is_wireless_interface(m_interface)) { if (net::is_wireless_interface(m_interface))
m_wireless_network = make_unique<net::wireless_network>(m_interface); m_wireless = net::wireless_t{new net::wireless_t::element_type(m_interface)};
} else { else
m_wired_network = make_unique<net::wired_network>(m_interface); m_wired = net::wired_t{new net::wired_t::element_type(m_interface)};
}
}
void start() {
timer_module::start();
// We only need to start the subthread if the packetloss animation is used // We only need to start the subthread if the packetloss animation is used
if (m_animation_packetloss) if (m_animation_packetloss)
m_threads.emplace_back(thread(&network_module::subthread_routine, this)); m_threads.emplace_back(thread(&network_module::subthread_routine, this));
} }
bool update() { bool update() {
string ip, essid, linkspeed; net::network* network = m_wireless ? dynamic_cast<net::network*>(m_wireless.get())
int signal_quality = 0; : dynamic_cast<net::network*>(m_wired.get());
net::network* network = nullptr; if (!network->query()) {
m_log.warn("%s: Failed to query interface '%s'", name(), m_interface);
// Process data for wireless network interfaces return false;
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());
}
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 {
try { if (m_wireless) {
ip = network->ip(); m_signal = m_wireless->signal();
} catch (net::network_error& e) { m_quality = m_wireless->quality();
m_log.trace("%s: %s", name(), e.what());
}
m_connected = network->connected();
// Ignore the first run
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_counter = 0;
} }
} catch (const net::network_error& err) {
m_log.warn("%s: Error getting interface data (%s)", name(), err.what());
} }
m_connected = network->connected();
// Ignore the first run
if (m_counter == -1) {
m_counter = 0;
} else if (m_ping_nth_update > 0 && m_connected && (++m_counter % m_ping_nth_update) == 0) {
m_packetloss = !network->ping();
m_counter = 0;
}
auto upspeed = network->upspeed(m_udspeed_minwidth);
auto downspeed = network->downspeed(m_udspeed_minwidth);
// Update label contents // Update label contents
if (m_label_connected || m_label_packetloss) { auto replace_tokens = [&](label_t& label) {
auto replace_tokens = [&](label_t label) { label->replace_token("%ifname%", m_interface);
label->replace_token("%ifname%", m_interface); label->replace_token("%local_ip%", network->ip());
label->replace_token("%local_ip%", !ip.empty() ? ip : "x.x.x.x"); label->replace_token("%upspeed%", upspeed);
label->replace_token("%downspeed%", downspeed);
if (m_wired_network) { if (m_wired) {
label->replace_token("%linkspeed%", linkspeed); label->replace_token("%linkspeed%", m_wired->linkspeed());
} else if (m_wireless_network) { } else if (m_wireless) {
label->replace_token("%essid%", !essid.empty() ? essid : "UNKNOWN"); label->replace_token("%essid%", m_wireless->essid());
label->replace_token("%signal%", to_string(signal_quality) + "%"); label->replace_token("%signal%", to_string(m_signal) + "%");
} label->replace_token("%quality%", to_string(m_quality) + "%");
auto upspeed = network->upspeed(m_udspeed_minwidth);
auto downspeed = network->downspeed(m_udspeed_minwidth);
label->replace_token("%upspeed%", upspeed);
label->replace_token("%downspeed%", downspeed);
};
if (m_label_connected) {
m_label_connected_tokenized->m_text = m_label_connected->m_text;
replace_tokens(m_label_connected_tokenized);
} }
};
if (m_label_packetloss) { if (m_label[connection_state::CONNECTED]) {
m_label_packetloss_tokenized->m_text = m_label_packetloss->m_text; m_tokenized[connection_state::CONNECTED]->m_text =
replace_tokens(m_label_packetloss_tokenized); m_label[connection_state::CONNECTED]->m_text;
} replace_tokens(m_tokenized[connection_state::CONNECTED]);
}
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; return true;
@ -159,7 +140,7 @@ namespace modules {
string get_format() { string get_format() {
if (!m_connected) if (!m_connected)
return FORMAT_DISCONNECTED; 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; return FORMAT_PACKETLOSS;
else else
return FORMAT_CONNECTED; return FORMAT_CONNECTED;
@ -167,15 +148,17 @@ namespace modules {
bool build(builder* builder, string tag) { bool build(builder* builder, string tag) {
if (tag == TAG_LABEL_CONNECTED) if (tag == TAG_LABEL_CONNECTED)
builder->node(m_label_connected_tokenized); builder->node(m_tokenized[connection_state::CONNECTED]);
else if (tag == TAG_LABEL_DISCONNECTED) else if (tag == TAG_LABEL_DISCONNECTED)
builder->node(m_label_disconnected); builder->node(m_label[connection_state::DISCONNECTED]);
else if (tag == TAG_LABEL_PACKETLOSS) 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) else if (tag == TAG_ANIMATION_PACKETLOSS)
builder->node(m_animation_packetloss->get()); builder->node(m_animation_packetloss->get());
else if (tag == TAG_RAMP_SIGNAL) 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 else
return false; return false;
return true; return true;
@ -185,13 +168,12 @@ namespace modules {
void subthread_routine() { void subthread_routine() {
this_thread::yield(); this_thread::yield();
const auto dur = const chrono::milliseconds framerate{m_animation_packetloss->framerate()};
chrono::duration<double>(float(m_animation_packetloss->framerate()) / 1000.0f); const auto dur = chrono::duration<double>(framerate);
while (enabled()) { while (enabled()) {
if (m_connected && m_conseq_packetloss) if (m_connected && m_packetloss)
broadcast(); broadcast();
sleep(dur); sleep(dur);
} }
@ -202,31 +184,31 @@ namespace modules {
static constexpr auto FORMAT_CONNECTED = "format-connected"; static constexpr auto FORMAT_CONNECTED = "format-connected";
static constexpr auto FORMAT_PACKETLOSS = "format-packetloss"; static constexpr auto FORMAT_PACKETLOSS = "format-packetloss";
static constexpr auto FORMAT_DISCONNECTED = "format-disconnected"; static constexpr auto FORMAT_DISCONNECTED = "format-disconnected";
static constexpr auto TAG_RAMP_SIGNAL = "<ramp-signal>"; 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_CONNECTED = "<label-connected>";
static constexpr auto TAG_LABEL_DISCONNECTED = "<label-disconnected>"; static constexpr auto TAG_LABEL_DISCONNECTED = "<label-disconnected>";
static constexpr auto TAG_LABEL_PACKETLOSS = "<label-packetloss>"; static constexpr auto TAG_LABEL_PACKETLOSS = "<label-packetloss>";
static constexpr auto TAG_ANIMATION_PACKETLOSS = "<animation-packetloss>"; static constexpr auto TAG_ANIMATION_PACKETLOSS = "<animation-packetloss>";
unique_ptr<net::wired_network> m_wired_network; net::wired_t m_wired;
unique_ptr<net::wireless_network> m_wireless_network; net::wireless_t m_wireless;
ramp_t m_ramp_signal; ramp_t m_ramp_signal;
ramp_t m_ramp_quality;
animation_t m_animation_packetloss; animation_t m_animation_packetloss;
label_t m_label_connected; map<connection_state, label_t> m_label;
label_t m_label_connected_tokenized; map<connection_state, label_t> m_tokenized;
label_t m_label_disconnected;
label_t m_label_packetloss;
label_t m_label_packetloss_tokenized;
stateflag m_connected{false}; 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; string m_interface;
int m_signal_quality; int m_ping_nth_update = 0;
int m_ping_nth_update;
int m_counter = -1; // -1 to ignore the first run
int m_udspeed_minwidth = 3; int m_udspeed_minwidth = 3;
}; };
} }