aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md30
-rw-r--r--conf.c32
-rw-r--r--info_scr.c227
-rw-r--r--iw_if.c31
-rw-r--r--iw_if.h44
-rw-r--r--lhist_scr.c21
-rw-r--r--timer.c41
-rw-r--r--wavemon.110
-rw-r--r--wavemon.h12
9 files changed, 178 insertions, 270 deletions
diff --git a/README.md b/README.md
index 637f639..e0167f1 100644
--- a/README.md
+++ b/README.md
@@ -12,13 +12,26 @@ See the man page for an in-depth description of operation and configuration.
### Where to obtain
-Apart from debian/ubuntu packages (apt-cache search wavemon) and slackbuild
-scripts for wavemon on slackbuilds.org, this repository contains the full
-source code.
+Apart from debian/ubuntu packages (`apt-cache search wavemon`) and [slackbuild scripts for wavemon](https://slackbuilds.org/result/?search=wavemon&sv=), this repository contains the full source code.
+
+Please check [this page](#) for updates and for further information.
-Please check this page for updates and for further information.
wavemon is distributed under the [GPLv3](http://www.gnu.org/licenses/gpl-3.0.en.html), refer to the file `COPYING`.
+### Dependencies
+
+wavemon requires a Linux Kernel with wireless extensions enabled. If your Kernel setup uses `CONFIG_CFG80211`, make sure that the config option `CONFIG_CFG80211_WEXT` is set.
+
+In addition, minimally the following are required:
+* Netlink `libnl` at least version 3.2,
+* including the Generic Netlink support (`libnl-genl`),
+* ncurses development files (`libncurses5-dev`),
+* the `pkg-config` package.
+
+On Debian/Ubuntu, this can be done using
+```bash
+ apt-get -y install pkg-config libncurses5-dev libnl-3-dev libnl-genl-3-dev
+```
## How to build
@@ -31,12 +44,6 @@ wavemon uses `autoconf`, so that in most cases you can simply run
to build and install the package. Type 'make uninstall' if not happy.
Refer to the file `INSTALL` for generic installation instructions.
-**Dependencies**: at least version 3.2 of `libnl`, including the Generic Netlink support (`libnl-genl`).
-On Debian/Ubuntu, this can be done using
-```bash
- apt-get -y install libnl-3-dev libnl-genl-3-dev
-```
-
To grant users access to restricted networking operations (scan operations), use additionally
```
sudo make install-suid-root
@@ -45,8 +52,7 @@ If you have changed some of the autoconf files or use a git version, run
```
./config/bootstrap
```
-(requires a recent installation of `autotools`).
-
+(This requires a recent installation of `autotools`.)
## Bugs?
diff --git a/conf.c b/conf.c
index 27c11dc..294564b 100644
--- a/conf.c
+++ b/conf.c
@@ -20,6 +20,7 @@
#include "wavemon.h"
#include <pwd.h>
#include <sys/types.h>
+#include <netlink/version.h>
/* GLOBALS */
#define MAX_IFLIST_ENTRIES 64
@@ -94,7 +95,7 @@ void conf_get_interface_list(void)
}
iw_get_interface_list(if_list, MAX_IFLIST_ENTRIES);
if (!if_list[0])
- err_quit("no supported wireless interfaces found!");
+ err_quit("no supported wireless interfaces found! Check manpage for help.");
conf.if_idx = 0;
if (old_if) {
@@ -529,10 +530,7 @@ static void init_conf_items(void)
void getconf(int argc, char *argv[])
{
int arg, help = 0, version = 0;
-
- conf_get_interface_list();
- init_conf_items();
- read_cf();
+ const char *iface = NULL;
while ((arg = getopt(argc, argv, "ghi:v")) >= 0) {
switch (arg) {
@@ -543,10 +541,7 @@ void getconf(int argc, char *argv[])
help++;
break;
case 'i':
- conf.if_idx = argv_find(if_list, optarg);
- if (conf.if_idx < 0)
- err_quit("no wireless extensions found on '%s'",
- optarg);
+ iface = optarg;
break;
case 'v':
version++;
@@ -557,8 +552,7 @@ void getconf(int argc, char *argv[])
}
if (version) {
- printf("wavemon %s", PACKAGE_VERSION);
- printf(" with %s.\n", curses_version());
+ printf("wavemon %s\n", PACKAGE_VERSION);
printf("Distributed under the terms of the GPLv3.\n%s", help ? "\n" : "");
}
if (help) {
@@ -566,9 +560,21 @@ void getconf(int argc, char *argv[])
printf(" -g Ensure screen is sufficiently dimensioned\n");
printf(" -h This help screen\n");
printf(" -i <ifname> Use specified network interface (default: auto)\n");
- printf(" -v Print version number\n");
+ printf(" -v Print version details\n");
}
- if (version || help)
+ if (version || help) {
exit(EXIT_SUCCESS);
+ }
+
+ /* Actual initialization. */
+ conf_get_interface_list();
+ init_conf_items();
+ read_cf();
+
+ if (iface) {
+ conf.if_idx = argv_find(if_list, iface);
+ if (conf.if_idx < 0)
+ err_quit("%s is not a usable wireless interface", iface);
+ }
}
diff --git a/info_scr.c b/info_scr.c
index 6a50b43..55a3bbc 100644
--- a/info_scr.c
+++ b/info_scr.c
@@ -22,29 +22,45 @@
/* GLOBALS */
static WINDOW *w_levels, *w_stats, *w_if, *w_info, *w_net;
-static struct timer dyn_updates;
-struct iw_range range;
-static struct iw_nl80211_linkstat ls;
-
-void sampling_init(void (*sampling_handler)(int))
+static pthread_t sampling_thread;
+static time_t last_update;
+// Global linkstat data, populated by sampling thread.
+static struct {
+ bool run; // enable/disable sampling
+ pthread_mutex_t mutex; // producer/consumer lock for @data
+ struct iw_nl80211_linkstat data;
+} linkstat;
+
+/** Sampling pthread shared by info and histogram screen. */
+static void *sampling_loop(void *arg)
{
- struct itimerval i;
- div_t d = div(conf.stat_iv, 1000); /* conf.stat_iv in msec */
+ sigset_t blockmask;
- xsignal(SIGALRM, SIG_IGN);
- iw_getinf_range(conf_ifname(), &range);
- i.it_interval.tv_sec = i.it_value.tv_sec = d.quot;
- i.it_interval.tv_usec = i.it_value.tv_usec = d.rem * 1000;
- xsignal(SIGALRM, sampling_handler);
+ /* See comment in scan_scr.c for rationale. */
+ sigemptyset(&blockmask);
+ sigaddset(&blockmask, SIGWINCH);
+ pthread_sigmask(SIG_BLOCK, &blockmask, NULL);
+
+ do {
+ pthread_mutex_lock(&linkstat.mutex);
+ iw_nl80211_get_linkstat(&linkstat.data);
+ pthread_mutex_unlock(&linkstat.mutex);
- (*sampling_handler)(0);
- setitimer(ITIMER_REAL, &i, NULL);
+ iw_cache_update(&linkstat.data);
+ } while (linkstat.run && usleep(conf.stat_iv * 1000) == 0);
+ return NULL;
}
-void sampling_do_poll(void)
+void sampling_init(void)
{
- iw_nl80211_get_linkstat(&ls);
- iw_cache_update(&ls);
+ linkstat.run = true;
+ pthread_create(&sampling_thread, NULL, sampling_loop, NULL);
+}
+
+void sampling_stop(void)
+{
+ linkstat.run = false;
+ pthread_join(sampling_thread, NULL);
}
static void display_levels(void)
@@ -59,21 +75,23 @@ static void display_levels(void)
lvlscale[2] = { -40, -20};
char tmp[0x100];
int line;
- bool noise_data_valid = iw_nl80211_have_survey_data(&ls);
- int sig_qual = -1;
- int sig_qual_max;
- int sig_level = ls.signal_avg ?: ls.signal;
+ bool noise_data_valid;
+ int sig_qual = -1, sig_qual_max, sig_level;
+
+
+ noise_data_valid = iw_nl80211_have_survey_data(&linkstat.data);
+ sig_level = linkstat.data.signal_avg ?: linkstat.data.signal;
/* See comments in iw_cache_update */
if (sig_level == 0)
- sig_level = ls.bss_signal;
+ sig_level = linkstat.data.bss_signal;
for (line = 1; line <= WH_LEVEL; line++)
mvwclrtoborder(w_levels, line, 1);
- if (ls.bss_signal_qual) {
+ if (linkstat.data.bss_signal_qual) {
/* BSS_SIGNAL_UNSPEC is scaled 0..100 */
- sig_qual = ls.bss_signal_qual;
+ sig_qual = linkstat.data.bss_signal_qual;
sig_qual_max = 100;
} else if (sig_level) {
if (sig_level < -110)
@@ -136,7 +154,7 @@ static void display_levels(void)
line++;
if (noise_data_valid) {
- noise = ewma(noise, ls.survey.noise, conf.meter_decay / 100.0);
+ noise = ewma(noise, linkstat.data.survey.noise, conf.meter_decay / 100.0);
mvwaddstr(w_levels, line++, 1, "noise level: ");
sprintf(tmp, "%.0f dBm (%s)", noise, dbm2units(noise));
@@ -147,7 +165,7 @@ static void display_levels(void)
}
if (noise_data_valid && sig_level) {
- ssnr = ewma(ssnr, sig_level - ls.survey.noise,
+ ssnr = ewma(ssnr, sig_level - linkstat.data.survey.noise,
conf.meter_decay / 100.0);
mvwaddstr(w_levels, line++, 1, "SNR: ");
@@ -161,42 +179,39 @@ done_levels:
static void display_stats(void)
{
- struct if_stat nstat;
char tmp[0x100];
- if_getstat(conf_ifname(), &nstat);
-
/*
* Interface RX stats
*/
mvwaddstr(w_stats, 1, 1, "RX: ");
- if (ls.rx_packets) {
- sprintf(tmp, "%'u (%s)", ls.rx_packets,
- byte_units(ls.rx_bytes));
+ if (linkstat.data.rx_packets) {
+ sprintf(tmp, "%'u (%s)", linkstat.data.rx_packets,
+ byte_units(linkstat.data.rx_bytes));
waddstr_b(w_stats, tmp);
} else {
waddstr(w_stats, "n/a");
}
- if (iw_nl80211_have_survey_data(&ls)) {
- if (ls.rx_bitrate[0]) {
+ if (iw_nl80211_have_survey_data(&linkstat.data)) {
+ if (linkstat.data.rx_bitrate[0]) {
waddstr(w_stats, ", rate: ");
- waddstr_b(w_stats, ls.rx_bitrate);
+ waddstr_b(w_stats, linkstat.data.rx_bitrate);
}
- if (ls.expected_thru) {
- if (ls.expected_thru >= 1024)
- sprintf(tmp, " (exp: %.1f MB/s)", ls.expected_thru/1024.0);
+ if (linkstat.data.expected_thru) {
+ if (linkstat.data.expected_thru >= 1024)
+ sprintf(tmp, " (expected: %.1f MB/s)", linkstat.data.expected_thru/1024.0);
else
- sprintf(tmp, " (exp: %u kB/s)", ls.expected_thru);
+ sprintf(tmp, " (expected: %u kB/s)", linkstat.data.expected_thru);
waddstr(w_stats, tmp);
}
}
- if (ls.rx_drop_misc) {
+ if (linkstat.data.rx_drop_misc) {
waddstr(w_stats, ", drop: ");
- sprintf(tmp, "%'llu", (unsigned long long)ls.rx_drop_misc);
+ sprintf(tmp, "%'llu", (unsigned long long)linkstat.data.rx_drop_misc);
waddstr_b(w_stats, tmp);
}
@@ -207,28 +222,28 @@ static void display_stats(void)
*/
mvwaddstr(w_stats, 2, 1, "TX: ");
- if (ls.tx_packets) {
- sprintf(tmp, "%'u (%s)", ls.tx_packets,
- byte_units(ls.tx_bytes));
+ if (linkstat.data.tx_packets) {
+ sprintf(tmp, "%'u (%s)", linkstat.data.tx_packets,
+ byte_units(linkstat.data.tx_bytes));
waddstr_b(w_stats, tmp);
} else {
waddstr(w_stats, "n/a");
}
- if (iw_nl80211_have_survey_data(&ls) && ls.tx_bitrate[0]) {
+ if (iw_nl80211_have_survey_data(&linkstat.data) && linkstat.data.tx_bitrate[0]) {
waddstr(w_stats, ", rate: ");
- waddstr_b(w_stats, ls.tx_bitrate);
+ waddstr_b(w_stats, linkstat.data.tx_bitrate);
}
- if (ls.tx_retries) {
+ if (linkstat.data.tx_retries) {
waddstr(w_stats, ", retries: ");
- sprintf(tmp, "%'u", ls.tx_retries);
+ sprintf(tmp, "%'u", linkstat.data.tx_retries);
waddstr_b(w_stats, tmp);
}
- if (ls.tx_failed) {
+ if (linkstat.data.tx_failed) {
waddstr(w_stats, ", failed: ");
- sprintf(tmp, "%'u", ls.tx_failed);
+ sprintf(tmp, "%'u", linkstat.data.tx_failed);
waddstr_b(w_stats, tmp);
}
wclrtoborder(w_stats);
@@ -238,11 +253,13 @@ static void display_stats(void)
static void display_info(WINDOW *w_if, WINDOW *w_info)
{
struct iw_dyn_info info;
+ struct iw_range range;
struct iw_nl80211_ifstat ifs;
struct iw_nl80211_reg ir;
char tmp[0x100];
int i;
+ iw_getinf_range(conf_ifname(), &range);
dyn_info_get(&info, conf_ifname(), &range);
iw_nl80211_getifstat(&ifs);
iw_nl80211_getreg(&ir);
@@ -252,10 +269,7 @@ static void display_info(WINDOW *w_if, WINDOW *w_info)
*/
wmove(w_if, 1, 1);
waddstr_b(w_if, conf_ifname());
- if (range.enc_capa & IW_WPA_MASK)
- sprintf(tmp, " (%s, %s)", info.name, format_wpa(&range));
- else
- sprintf(tmp, " (%s)", info.name);
+ sprintf(tmp, " (%s)", info.name);
waddstr(w_if, tmp);
/* PHY */
@@ -288,10 +302,10 @@ static void display_info(WINDOW *w_if, WINDOW *w_info)
waddstr(w_info, "mode: ");
waddstr_b(w_info, iftype_name(ifs.iftype));
- if (!ether_addr_is_zero(&ls.bssid)) {
+ if (!ether_addr_is_zero(&linkstat.data.bssid)) {
waddstr_b(w_info, ", ");
- switch (ls.status) {
+ switch (linkstat.data.status) {
case NL80211_BSS_STATUS_ASSOCIATED:
waddstr(w_info, "connected to: ");
break;
@@ -304,15 +318,15 @@ static void display_info(WINDOW *w_if, WINDOW *w_info)
default:
waddstr(w_info, "station: ");
}
- waddstr_b(w_info, ether_lookup(&ls.bssid));
+ waddstr_b(w_info, ether_lookup(&linkstat.data.bssid));
- if (ls.status == NL80211_BSS_STATUS_ASSOCIATED) {
+ if (linkstat.data.status == NL80211_BSS_STATUS_ASSOCIATED) {
waddstr_b(w_info, ",");
waddstr(w_info, " time: ");
- waddstr_b(w_info, pretty_time(ls.connected_time));
+ waddstr_b(w_info, pretty_time(linkstat.data.connected_time));
waddstr(w_info, ", inactive: ");
- sprintf(tmp, "%.1fs", (float)ls.inactive_time/1e3);
+ sprintf(tmp, "%.1fs", (float)linkstat.data.inactive_time/1e3);
waddstr_b(w_info, tmp);
}
}
@@ -326,8 +340,8 @@ static void display_info(WINDOW *w_if, WINDOW *w_info)
waddstr_b(w_info, tmp);
/* The following condition should in theory never happen */
- if (ls.survey.freq && ls.survey.freq != ifs.freq) {
- sprintf(tmp, " [survey freq: %d MHz]", ls.survey.freq);
+ if (linkstat.data.survey.freq && linkstat.data.survey.freq != ifs.freq) {
+ sprintf(tmp, " [survey freq: %d MHz]", linkstat.data.survey.freq);
waddstr(w_info, tmp);
}
@@ -353,9 +367,9 @@ static void display_info(WINDOW *w_if, WINDOW *w_info)
sprintf(tmp, " (%s)", channel_type_name(ifs.chan_type));
waddstr(w_info, tmp);
}
- } else if (iw_nl80211_have_survey_data(&ls)) {
+ } else if (iw_nl80211_have_survey_data(&linkstat.data)) {
waddstr(w_info, "freq: ");
- sprintf(tmp, "%d MHz", ls.survey.freq);
+ sprintf(tmp, "%d MHz", linkstat.data.survey.freq);
waddstr_b(w_info, tmp);
} else {
waddstr(w_info, "frequency/channel: n/a");
@@ -364,87 +378,87 @@ static void display_info(WINDOW *w_if, WINDOW *w_info)
/* Channel data */
wmove(w_info, 3, 1);
- if (iw_nl80211_have_survey_data(&ls)) {
+ if (iw_nl80211_have_survey_data(&linkstat.data)) {
waddstr(w_info, "channel ");
waddstr(w_info, "active: ");
- waddstr_b(w_info, pretty_time_ms(ls.survey.time.active));
+ waddstr_b(w_info, pretty_time_ms(linkstat.data.survey.time.active));
waddstr(w_info, ", busy: ");
- waddstr_b(w_info, pretty_time_ms(ls.survey.time.busy));
+ waddstr_b(w_info, pretty_time_ms(linkstat.data.survey.time.busy));
- if (ls.survey.time.ext_busy) {
+ if (linkstat.data.survey.time.ext_busy) {
waddstr(w_info, ", ext-busy: ");
- waddstr_b(w_info, pretty_time_ms(ls.survey.time.ext_busy));
+ waddstr_b(w_info, pretty_time_ms(linkstat.data.survey.time.ext_busy));
}
waddstr(w_info, ", rx: ");
- waddstr_b(w_info, pretty_time_ms(ls.survey.time.rx));
+ waddstr_b(w_info, pretty_time_ms(linkstat.data.survey.time.rx));
waddstr(w_info, ", tx: ");
- waddstr_b(w_info, pretty_time_ms(ls.survey.time.tx));
+ waddstr_b(w_info, pretty_time_ms(linkstat.data.survey.time.tx));
- if (ls.survey.time.scan) {
+ if (linkstat.data.survey.time.scan) {
waddstr(w_info, ", scan: ");
- waddstr_b(w_info, pretty_time_ms(ls.survey.time.scan));
+ waddstr_b(w_info, pretty_time_ms(linkstat.data.survey.time.scan));
}
- } else if (ls.tx_bitrate[0] && ls.rx_bitrate[0]) {
+ } else if (linkstat.data.tx_bitrate[0] && linkstat.data.rx_bitrate[0]) {
waddstr(w_info, "rx rate: ");
- waddstr_b(w_info, ls.rx_bitrate);
+ waddstr_b(w_info, linkstat.data.rx_bitrate);
- if (ls.expected_thru) {
- if (ls.expected_thru >= 1024)
- sprintf(tmp, " (exp: %.1f MB/s)", ls.expected_thru/1024.0);
+ if (linkstat.data.expected_thru) {
+ if (linkstat.data.expected_thru >= 1024)
+ sprintf(tmp, " (exp: %.1f MB/s)", linkstat.data.expected_thru/1024.0);
else
- sprintf(tmp, " (exp: %u kB/s)", ls.expected_thru);
+ sprintf(tmp, " (exp: %u kB/s)", linkstat.data.expected_thru);
waddstr(w_info, tmp);
}
waddstr(w_info, ", tx rate: ");
- waddstr_b(w_info, ls.tx_bitrate);
+ waddstr_b(w_info, linkstat.data.tx_bitrate);
}
/* Beacons */
wmove(w_info, 4, 1);
- if (ls.beacons) {
+ if (linkstat.data.beacons) {
waddstr(w_info, "beacons: ");
- sprintf(tmp, "%'llu", (unsigned long long)ls.beacons);
+ sprintf(tmp, "%'llu", (unsigned long long)linkstat.data.beacons);
waddstr_b(w_info, tmp);
- if (ls.beacon_loss) {
+ if (linkstat.data.beacon_loss) {
waddstr(w_info, ", lost: ");
- sprintf(tmp, "%'u", ls.beacon_loss);
+ sprintf(tmp, "%'u", linkstat.data.beacon_loss);
waddstr_b(w_info, tmp);
}
waddstr(w_info, ", avg sig: ");
- sprintf(tmp, "%d dBm", (int8_t)ls.beacon_avg_sig);
+ sprintf(tmp, "%d dBm", (int8_t)linkstat.data.beacon_avg_sig);
waddstr_b(w_info, tmp);
waddstr(w_info, ", interval: ");
- sprintf(tmp, "%.1fs", (ls.beacon_int * 1024.0)/1e6);
+ sprintf(tmp, "%.1fs", (linkstat.data.beacon_int * 1024.0)/1e6);
waddstr_b(w_info, tmp);
waddstr(w_info, ", DTIM: ");
- sprintf(tmp, "%u", ls.dtim_period);
+ sprintf(tmp, "%u", linkstat.data.dtim_period);
waddstr_b(w_info, tmp);
} else {
waddstr(w_info, "station flags:");
- if (ls.cts_protection)
+ if (linkstat.data.cts_protection)
waddstr_b(w_info, " CTS");
- if (ls.wme)
+ if (linkstat.data.wme)
waddstr_b(w_info, " WME");
- if (ls.tdls)
+ if (linkstat.data.tdls)
waddstr_b(w_info, " TDLS");
- if (ls.mfp)
+ if (linkstat.data.mfp)
waddstr_b(w_info, " MFP");
- if (!(ls.cts_protection | ls.wme | ls.tdls | ls.mfp))
+ if (!(linkstat.data.cts_protection | linkstat.data.wme | linkstat.data.tdls | linkstat.data.mfp))
waddstr_b(w_info, " (none)");
waddstr(w_info, ", preamble:");
- if (ls.long_preamble)
+ if (linkstat.data.long_preamble)
waddstr_b(w_info, " long");
else
waddstr_b(w_info, " short");
waddstr(w_info, ", slot:");
- if (ls.short_slot_time)
+ if (linkstat.data.short_slot_time)
waddstr_b(w_info, " short");
else
waddstr_b(w_info, " long");
@@ -657,6 +671,7 @@ static void display_netinfo(WINDOW *w_net)
waddstr_b(w_net, inet_ntoa(info.bcast));
}
}
+ wclrtoborder(w_net);
/* 802.11 MTU may be greater than Ethernet MTU (1500) */
if (info.mtu && info.mtu != ETH_DATA_LEN) {
@@ -668,13 +683,6 @@ static void display_netinfo(WINDOW *w_net)
wrefresh(w_net);
}
-static void redraw_stat_levels(int signum)
-{
- sampling_do_poll();
- display_levels();
- display_stats();
-}
-
void scr_info_init(void)
{
int line = 0;
@@ -692,18 +700,23 @@ void scr_info_init(void)
else
w_net = newwin_title(line, WH_NET_MAX, "Network", false);
- display_info(w_if, w_info);
- display_netinfo(w_net);
- start_timer(&dyn_updates, conf.info_iv * 1000000);
- sampling_init(redraw_stat_levels);
+ sampling_init();
}
int scr_info_loop(WINDOW *w_menu)
{
- if (end_timer(&dyn_updates)) {
+ time_t now = time(NULL);
+
+ if (!pthread_mutex_trylock(&linkstat.mutex)) {
+ display_levels();
+ display_stats();
+ pthread_mutex_unlock(&linkstat.mutex);
+ }
+
+ if (now - last_update >= conf.info_iv) {
+ last_update = now;
display_info(w_if, w_info);
display_netinfo(w_net);
- start_timer(&dyn_updates, conf.info_iv * 1000000);
}
return wgetch(w_menu);
}
diff --git a/iw_if.c b/iw_if.c
index 77fc5c2..ca7d3c5 100644
--- a/iw_if.c
+++ b/iw_if.c
@@ -148,35 +148,6 @@ void iw_get_interface_list(char** if_list, size_t max_entries)
fclose(fp);
}
-void if_getstat(const char *ifname, struct if_stat *stat)
-{
- char line[0x100];
- unsigned long long d;
- char *lp;
- size_t l = strlen(ifname);
- const char path[] = "/proc/net/dev";
- FILE *fp = fopen(path, "r");
-
- if (fp == NULL)
- err_sys("can not open %s", path);
- /*
- * Inter-| Receive | Transmit
- * face |bytes packets errs drop fifo frame compressed multicast|bytes packets
- */
- while (fgets(line, sizeof(line), fp)) {
- lp = line + strspn(line, " ");
- if (!strncmp(lp, ifname, l) && lp[l] == ':') {
- lp += l + 1;
- lp += strspn(lp, " ");
-
- sscanf(lp, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
- &stat->rx_bytes, &stat->rx_packets, &d, &d, &d, &d, &d, &d,
- &stat->tx_bytes, &stat->tx_packets);
- }
- }
- fclose(fp);
-}
-
/**
* iw_dyn_info_get - populate dynamic information
* @info: information to populate
@@ -192,7 +163,7 @@ void dyn_info_get(struct iw_dyn_info *info,
if (skfd < 0)
err_sys("%s: can not open socket", __func__);
- memset(info, 0, sizeof(struct iw_dyn_info));
+ memset(info, 0, sizeof(*info));
strncpy(iwr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(skfd, SIOCGIWNAME, &iwr) < 0)
diff --git a/iw_if.h b/iw_if.h
index cc9e886..846e9f9 100644
--- a/iw_if.h
+++ b/iw_if.h
@@ -181,18 +181,6 @@ extern void dyn_info_get(struct iw_dyn_info *info,
extern void dyn_info_cleanup(struct iw_dyn_info *info);
-/**
- * struct if_stat - Packet/byte counts for interfaces
- */
-struct if_stat {
- unsigned long long rx_packets,
- tx_packets;
- unsigned long long rx_bytes,
- tx_bytes;
-};
-
-extern void if_getstat(const char *ifname, struct if_stat *stat);
-
/*
* Structs to communicate WiFi statistics
*/
@@ -206,11 +194,10 @@ struct iw_levelstat {
extern void iw_getinf_range(const char *ifname, struct iw_range *range);
/*
- * Periodic sampling of wireless statistics via timer alarm
+ * Periodic sampling of wireless statistics
*/
-extern void sampling_init(void (*sampling_handler)(int));
-extern void sampling_do_poll(void);
-static inline void sampling_stop(void) { alarm(0); }
+extern void sampling_init(void);
+extern void sampling_stop(void);
/*
* Organization of scan results
@@ -457,31 +444,6 @@ static inline char *format_key(const struct iw_key *const iwk)
return buf;
}
-/* Human-readable representation of IW_ENC_CAPA_ types */
-static inline const char *format_enc_capab(const uint32_t capa, const char *sep)
-{
- static char buf[32];
- size_t len = 0, max = sizeof(buf);
-
- if (capa & IW_ENC_CAPA_WPA)
- len = snprintf(buf, max, "WPA");
- if (capa & IW_ENC_CAPA_WPA2)
- len += snprintf(buf + len, max - len, "%sWPA2", len ? sep : "");
- if (capa & IW_ENC_CAPA_CIPHER_TKIP)
- len += snprintf(buf + len, max - len, "%sTKIP", len ? sep : "");
- if (capa & IW_ENC_CAPA_CIPHER_CCMP)
- len += snprintf(buf + len, max - len, "%sCCMP", len ? sep : "");
- buf[len] = '\0';
- return buf;
-}
-
-/* Display only the supported WPA type */
-#define IW_WPA_MASK (IW_ENC_CAPA_WPA|IW_ENC_CAPA_WPA2)
-static inline const char *format_wpa(struct iw_range *ir)
-{
- return format_enc_capab(ir->enc_capa & IW_WPA_MASK, "/");
-}
-
static inline char *format_retry(const struct iw_param *retry,
const struct iw_range *range)
{
diff --git a/lhist_scr.c b/lhist_scr.c
index 2595b45..55d5d6b 100644
--- a/lhist_scr.c
+++ b/lhist_scr.c
@@ -317,18 +317,6 @@ static void display_key(WINDOW *w_key)
wrefresh(w_key);
}
-static void redraw_lhist(int signum)
-{
- static int vcount = 1;
-
- sampling_do_poll();
- if (!--vcount) {
- vcount = conf.slotsize;
- display_lhist();
- display_key(w_key);
- }
-}
-
void scr_lhist_init(void)
{
w_lhist = newwin_title(0, HIST_WIN_HEIGHT, "Level histogram", true);
@@ -337,13 +325,20 @@ void scr_lhist_init(void)
init_extrema(&e_signal);
init_extrema(&e_noise);
init_extrema(&e_snr);
- sampling_init(redraw_lhist);
+ sampling_init();
display_key(w_key);
}
int scr_lhist_loop(WINDOW *w_menu)
{
+ static int vcount = 1;
+
+ if (!--vcount) {
+ vcount = conf.slotsize;
+ display_lhist();
+ display_key(w_key);
+ }
return wgetch(w_menu);
}
diff --git a/timer.c b/timer.c
deleted file mode 100644
index cb6500c..0000000
--- a/timer.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * wavemon - a wireless network monitoring aplication
- *
- * Copyright (c) 2001-2002 Jan Morgenstern <jan@jm-music.de>
- *
- * wavemon is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2, or (at your option) any later
- * version.
- *
- * wavemon is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along
- * with wavemon; see the file COPYING. If not, write to the Free Software
- * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-#include "wavemon.h"
-
-static unsigned long get_usecs(void)
-{
- struct timeval tv;
- struct timezone tz;
-
- gettimeofday(&tv, &tz);
-
- return tv.tv_sec * 1000000 + tv.tv_usec;
-}
-
-void start_timer(struct timer *t, unsigned long duration)
-{
- t->stime = get_usecs();
- t->duration = duration;
-}
-
-bool end_timer(struct timer *t)
-{
- return get_usecs() >= t->stime + t->duration;
-}
diff --git a/wavemon.1 b/wavemon.1
index ccfba7d..b5a9e58 100644
--- a/wavemon.1
+++ b/wavemon.1
@@ -1,4 +1,4 @@
-.TH wavemon 1 "January 2015" Linux "User Manuals"
+.TH wavemon 1 "September 2016" Linux "User Manuals"
.SH NAME
wavemon \- a wireless network monitor
.SH SYNOPSIS
@@ -121,6 +121,12 @@ properly.
print help and exit.
.IP "\fB\-v\fR"
print version information and exit.
+.SH Troubleshooting
+wavemon will exit with \fB'no supported wireless interfaces found'\fR if no usable wireless interfaces
+were detected. Check if your wireless interfaces is otherwise usable, using e.g. \fIiw\fR, \fIiwconfig\fR,
+or similar tools. The interface should appear in /proc/net/dev and, if wireless extensions are supported,
+also in /proc/net/wireless. If the interface does not appear, causes can be a missing (or not loaded)
+kernel module, or missing firmware, which some cards need to operate.
.SH "ENVIRONMENT VARIABLES"
.IP "LC_NUMERIC"
@@ -131,7 +137,7 @@ The local per-user configuration file.
.SH "AUTHOR"
Written by Jan Morgenstern <jan@jm-music.de>.
.SH "REPORTING BUGS"
-Send bug reports and/or suggestions to Gerrit Renker <gerrit@erg.abdn.ac.uk>.
+Open an issue on \fIhttps://github.com/uoaerg/wavemon/issues\fR.
.SH "COPYRIGHT"
This is free software with ABSOLUTELY NO WARRANTY. See file COPYING for details.
.SH "SEE ALSO"
diff --git a/wavemon.h b/wavemon.h
index e7584f7..ee0cff4 100644
--- a/wavemon.h
+++ b/wavemon.h
@@ -18,6 +18,7 @@
* with wavemon; see the file COPYING. If not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -284,17 +285,6 @@ extern void conf_get_interface_list(void);
extern void iw_get_interface_list(char** if_list, size_t max_entries);
/*
- * Timers
- */
-struct timer {
- unsigned long long stime;
- unsigned long duration;
-};
-
-extern void start_timer(struct timer *t, unsigned long d);
-extern bool end_timer(struct timer *t);
-
-/*
* Error handling
*/
extern bool has_net_admin_capability(void);
ass="w"> return result; } static RESULT* DupResult (RESULT *result) { RESULT *result2; if ((result2=NewResult())==NULL) return NULL; result2->type=result->type; result2->number=result->number; if (result->string!=NULL) result2->string=strdup(result->string); else result2->string=NULL; return result2; } RESULT* SetResult (RESULT **result, int type, void *value) { if (*result) { DelResult(*result); } else { if ((*result=NewResult())==NULL) return NULL; } if (type==R_NUMBER) { (*result)->type=R_NUMBER; (*result)->number=*(double*)value; (*result)->string=NULL; } else if (type==R_STRING) { (*result)->type=R_STRING; (*result)->string=strdup(value); } else { error ("internal error: invalid result type %d", type); return NULL; } return *result; } double R2N (RESULT *result) { if (result==NULL) { error ("internal error: NULL result"); return 0.0; } if (result->type & R_NUMBER) { return result->number; } if (result->type & R_STRING) { result->type |= R_NUMBER; result->number=atof(result->string); return result->number; } error ("internal error: invalid result type %d", result->type); return 0.0; } char* R2S (RESULT *result) { char buffer[16]; if (result==NULL) { error ("internal error: NULL result"); return NULL; } if (result->type & R_STRING) { return result->string; } if (result->type & R_NUMBER) { sprintf(buffer, "%g", result->number); result->type |= R_STRING; result->string=strdup(buffer); return result->string; } error ("internal error: invalid result type %d", result->type); return NULL; } // bsearch compare function for variables static int v_lookup (const void *a, const void *b) { char *name=(char*)a; VARIABLE *v=(VARIABLE*)b; return strcmp(name, v->name); } // qsort compare function for variables static int v_sort (const void *a, const void *b) { VARIABLE *va=(VARIABLE*)a; VARIABLE *vb=(VARIABLE*)b; return strcmp(va->name, vb->name); } static int DelVariable (char *name) { VARIABLE *V; V=bsearch(name, Variable, nVariable, sizeof(VARIABLE), v_lookup); if (V!=NULL) { FreeResult (V->value); memmove (V, V+1, (nVariable-1)*sizeof(VARIABLE)-(V-Variable)); nVariable--; Variable=realloc(Variable, nVariable*sizeof(VARIABLE)); return 1; } return 0; } static int GetVariable (char *name, RESULT *value) { VARIABLE *V; V=bsearch(name, Variable, nVariable, sizeof(VARIABLE), v_lookup); if (V!=NULL) { value->type=V->value->type; value->number=V->value->number; if (V->value->string!=NULL) value->string=strdup(V->value->string); else value->string=NULL; return 1; } DelResult (value); return 0; } int SetVariable (char *name, RESULT *value) { VARIABLE *V; V=bsearch(name, Variable, nVariable, sizeof(VARIABLE), v_lookup); if (V!=NULL) { FreeResult (V->value); V->value=DupResult(value); return 1; } // we append the var at the end and re-sort // the whole array. As every SetVariable call // implies a bsearch(), this is ok and cannot // be optimized. nVariable++; Variable=realloc(Variable, nVariable*sizeof(VARIABLE)); Variable[nVariable-1].name=strdup(name); Variable[nVariable-1].value=DupResult(value); qsort(Variable, nVariable, sizeof(VARIABLE), v_sort); return 0; } int AddNumericVariable (char *name, double value) { RESULT result; result.type=R_NUMBER; result.number=value; result.string=NULL; return SetVariable (name, &result); } int AddStringVariable (char *name, char *value) { RESULT result; result.type=R_STRING; result.number=0.0; result.string=strdup(value); return SetVariable (name, &result); } // bsearch compare function for functions static int f_lookup (const void *a, const void *b) { char *name=(char*)a; FUNCTION *f=(FUNCTION*)b; return strcmp(name, f->name); } // qsort compare function for functions static int f_sort (const void *a, const void *b) { FUNCTION *va=(FUNCTION*)a; FUNCTION *vb=(FUNCTION*)b; return strcmp(va->name, vb->name); } static FUNCTION* GetFunction (char *name) { FUNCTION *F; if (!FunctionSorted) { FunctionSorted=1; qsort(Function, nFunction, sizeof(FUNCTION), f_sort); } F=bsearch(name, Function, nFunction, sizeof(FUNCTION), f_lookup); return F; } int AddFunction (char *name, int args, void (*func)()) { // we append the func at the end, and flag // the function table as unsorted. It will be // sorted again on the next GetFunction call. FunctionSorted=0; nFunction++; Function=realloc(Function, nFunction*sizeof(FUNCTION)); Function[nFunction-1].name=strdup(name); Function[nFunction-1].args=args; Function[nFunction-1].func=func; return 0; } // Prototypes static void Level01 (RESULT *result); static void Level02 (RESULT *result); static void Level03 (RESULT *result); static void Level04 (RESULT *result); static void Level05 (RESULT *result); static void Level06 (RESULT *result); static void Level07 (RESULT *result); static void Level08 (RESULT *result); static void Level09 (RESULT *result); static void Level10 (RESULT *result); static void Level11 (RESULT *result); static void Level12 (RESULT *result); static void Parse (void) { char *start; Type=0; if (Token) { free (Token); Token=NULL; } while (is_blank(*Expression)) Expression++; if (is_delim(*Expression)) { Type=T_DELIMITER; // special case for <=, >=, ==, != if (strchr("<>=!", *Expression)!=NULL && *(Expression+1)=='=') { Token=strndup(Expression, 2); Expression+=2; } else { Token=strndup(Expression, 1); Expression++; } } else if (isdigit(*Expression)) { Type=T_NUMBER; start=Expression; while (is_number(*Expression)) Expression++; Token=strndup(start, Expression-start); } else if (is_name(*Expression)) { Type=T_NAME; start=Expression; while (is_name(*Expression)) Expression++; Token=strndup(start, Expression-start); } else if (*Expression=='\'') { Type=T_STRING; start=++Expression; while (*Expression && *Expression!='\'') Expression++; Token=strndup(start, Expression-start); if (*Expression=='\'') Expression++; } else if (*Expression) { ERROR(E_SYNTAX); } while(is_blank(*Expression)) Expression++; // empty token if (Token==NULL) Token=strdup(""); } // expression lists static void Level01 (RESULT *result) { do { while (Type==T_DELIMITER && *Token==';') Parse(); Level02(result); } while (Type==T_DELIMITER && *Token==';'); } // variable assignments static void Level02 (RESULT *result) { char *name; if (Type==T_NAME) { if (Expression[0]=='=' && Expression[1]!='=') { name=strdup(Token); Parse(); Parse(); if (*Token && (Type!=T_DELIMITER || *Token!=';')) { Level03(result); SetVariable(name, result); } else { DelVariable(name); } free (name); return; } } Level03(result); } // conditional expression a?b:c static void Level03 (RESULT *result) { RESULT r_then = {0, 0.0, NULL}; RESULT r_else = {0, 0.0, NULL}; Level04(result); while(Type==T_DELIMITER && *Token=='?') { Parse(); Level01 (&r_then); if (Type==T_DELIMITER && *Token==':') { Parse(); Level01 (&r_else); } else { ERROR(E_SYNTAX); } if (R2N(result)!=0.0) { DelResult(result); DelResult(&r_else); *result=r_then; } else { DelResult(result); DelResult(&r_then); *result=r_else; } } } // logical 'or' static void Level04 (RESULT *result) { RESULT operand = {0, 0.0, NULL}; double value; Level05(result); while(Type==T_DELIMITER && *Token=='|') { Parse(); Level05 (&operand); value = (R2N(result)!=0.0) || (R2N(&operand)!=0.0); SetResult(&result, R_NUMBER, &value); } } // logical 'and' static void Level05 (RESULT *result) { RESULT operand; double value; Level06(result); while(Type==T_DELIMITER && *Token=='&') { Parse(); Level06 (&operand); value = (R2N(result)!=0.0) && (R2N(&operand)!=0.0); SetResult(&result, R_NUMBER, &value); } } // equal, not equal static void Level06 (RESULT *result) { char operator; RESULT operand = {0, 0.0, NULL}; double value; Level07 (result); if (Type==T_DELIMITER && ((operator=Token[0])=='=' || operator=='!') && Token[1]=='=') { Parse(); Level07 (&operand); if (operator=='=') value = (R2N(result) == R2N(&operand)); else value = (R2N(result) != R2N(&operand)); SetResult(&result, R_NUMBER, &value); } } // relational operators static void Level07 (RESULT *result) { char operator[2]; RESULT operand = {0, 0.0, NULL}; double value; Level08 (result); if (Type==T_DELIMITER && (*Token=='<' || *Token=='>')) { operator[0]=Token[0]; operator[1]=Token[1]; Parse(); Level08 (&operand); if (operator[0]=='<') if (operator[1]=='=') value = (R2N(result) <= R2N(&operand)); else value = (R2N(result) < R2N(&operand)); else if (operator[1]=='=') value = (R2N(result) >= R2N(&operand)); else value = (R2N(result) > R2N(&operand)); SetResult(&result, R_NUMBER, &value); } } // addition, subtraction, concatenation static void Level08 (RESULT *result) { char operator; RESULT operand = {0, 0.0, NULL}; double value; Level09(result); while(Type==T_DELIMITER && ((operator=*Token)=='+' || operator=='-' || operator=='.')) { Parse(); Level09 (&operand); if (operator=='+') { value = (R2N(result) + R2N(&operand)); SetResult(&result, R_NUMBER, &value); } else if (operator=='-') { value = (R2N(result) - R2N(&operand)); SetResult(&result, R_NUMBER, &value); } else { char *s1=R2S(result); char *s2=R2S(&operand); char *s3=malloc(strlen(s1)+strlen(s2)+1); strcpy (s3, s1); strcat (s3, s2); SetResult (&result, R_STRING, s3); free (s3); } } } // multiplication, division, modulo static void Level09 (RESULT *result) { char operator; RESULT operand = {0, 0.0, NULL}; double value; Level10 (result); while(Type==T_DELIMITER && ((operator=*Token)=='*' || operator=='/' || operator=='%')) { Parse(); Level10(&operand); if (operator == '*') { value = (R2N(result) * R2N(&operand)); } else if (operator == '/') { if (R2N(&operand) == 0.0) ERROR (E_DIVZERO); value = (R2N(result) / R2N(&operand)); } else { if (R2N(&operand) == 0.0) ERROR (E_DIVZERO); value = fmod(R2N(result), R2N(&operand)); } SetResult(&result, R_NUMBER, &value); } } // x^y static void Level10 (RESULT *result) { RESULT exponent = {0, 0.0, NULL}; double value; Level11 (result); if (Type==T_DELIMITER && *Token == '^') { Parse(); Level11 (&exponent); value = pow(R2N(result), R2N(&exponent)); SetResult(&result, R_NUMBER, &value); } } // unary + or - signs or logical 'not' static void Level11 (RESULT *result) { char sign=0; double value; if (Type==T_DELIMITER && (*Token=='+' || *Token=='-' || *Token=='!')) { sign=*Token; Parse(); } Level12 (result); if (sign == '-') { value = -R2N(result); SetResult(&result, R_NUMBER, &value); } else if (sign == '!') { value = (R2N(result)==0.0); SetResult(&result, R_NUMBER, &value); } } // literal numbers, variables, functions static void Level12 (RESULT *result) { RESULT *param[10]; if (*Token == '(') { Parse(); if (*Token == ')') ERROR (E_NOARG); Level01(result); if (*Token != ')') ERROR (E_UNBALAN); Parse(); } else { if (Type == T_NUMBER) { double value=atof(Token); SetResult(&result, R_NUMBER, &value); Parse(); } else if (Type == T_STRING) { SetResult(&result, R_STRING, Token); Parse(); } else if (Type == T_NAME) { if (*Expression == '(') { FUNCTION *F=GetFunction(Token); if (F!=NULL) { int n=0; Parse(); // read opening brace do { Parse(); // read argument if (*Token == ',') ERROR (E_NOARG); if (*Token == ')') { // immediately closed when no args if (F->args>0 || n>0) ERROR (E_NOARG); } else { param[n]=NewResult(); Level01(param[n]); n++; } } while (n < 10 && *Token == ','); Parse(); // read closing brace if (F->args<0) { // Function with variable argument list: // pass number of arguments as first parameter F->func(result, n, &param); } else { if (n != F->args) ERROR (E_NUMARGS); F->func(result, param[0], param[1], param[2], param[3], param[4], param[5], param[6], param[7], param[8], param[9]); } // free parameter list while(n-->0) { FreeResult(param[n]); } return; } else { ERROR(E_BADFUNC); } } else { if (!GetVariable(Token, result)) ERROR(E_UNKNOWN); } Parse(); } else { ERROR(E_SYNTAX); } } } int Eval (char* expression, RESULT *result) { int i, err; if ((err=setjmp(jb))) { error ("Evaluator: %s in expression <%s>", ErrMsg[err], expression); return -1; } // maybe sort function table if (!FunctionSorted) { FunctionSorted=1; qsort(Function, nFunction, sizeof(FUNCTION), f_sort); // sanity check: two functions with the same name? for (i=1; i<nFunction; i++) { if (strcmp(Function[i].name, Function[i-1].name)==0) { error ("Evaluator: internal error: Function '%s' defined twice!", Function[i].name); } } } Expression=expression; DelResult (result); Parse(); if (*Token=='\0') ERROR (E_EMPTY); Level01(result); if (*Token!='\0') ERROR (E_SYNTAX); return 0; }