aboutsummaryrefslogtreecommitdiffstats
path: root/scan_scr.c
diff options
context:
space:
mode:
Diffstat (limited to 'scan_scr.c')
-rw-r--r--scan_scr.c241
1 files changed, 108 insertions, 133 deletions
diff --git a/scan_scr.c b/scan_scr.c
index 02cb8db..611a32f 100644
--- a/scan_scr.c
+++ b/scan_scr.c
@@ -20,44 +20,42 @@
#include "iw_if.h"
#define START_LINE 2 /* where to begin the screen */
-#define NUMTOP 5 /* maximum number of 'top' statistics entries */
/* GLOBALS */
+static struct scan_result sr;
+static pthread_t scan_thread;
static WINDOW *w_aplst;
-static pid_t pid;
-static void (*sig_tstp)(int);
-static char *fmt_scan_result(struct scan_result *cur, struct iw_range *iw_range,
- char buf[], size_t buflen)
+/**
+ * Sanitize and format single scan entry as a string.
+ * @cur: entry to format
+ * @buf: buffer to put results into
+ * @buflen: length of @buf
+ */
+static void fmt_scan_entry(struct scan_entry *cur, char buf[], size_t buflen)
{
- struct iw_levelstat dbm;
size_t len = 0;
- int channel = freq_to_channel(cur->freq, iw_range);
-
- iw_sanitize(iw_range, &cur->qual, &dbm);
if (!(cur->qual.updated & (IW_QUAL_QUAL_INVALID|IW_QUAL_LEVEL_INVALID)))
len += snprintf(buf + len, buflen - len, "%3.0f%%, %.0f dBm",
- 1E2 * cur->qual.qual / iw_range->max_qual.qual,
- dbm.signal);
+ 1E2 * cur->qual.qual / sr.range.max_qual.qual,
+ cur->dbm.signal);
else if (!(cur->qual.updated & IW_QUAL_QUAL_INVALID))
len += snprintf(buf + len, buflen - len, "%2d/%d",
- cur->qual.qual, iw_range->max_qual.qual);
+ cur->qual.qual, sr.range.max_qual.qual);
else if (!(cur->qual.updated & IW_QUAL_LEVEL_INVALID))
len += snprintf(buf + len, buflen - len, "%.0f dBm",
- dbm.signal);
+ cur->dbm.signal);
else
len += snprintf(buf + len, buflen - len, "? dBm");
if (cur->freq < 1e3)
len += snprintf(buf + len, buflen - len, ", Chan %2.0f",
cur->freq);
- else if (channel >= 0 && cur->freq < 5e9)
- len += snprintf(buf + len, buflen - len, ", ch %2d, %g MHz",
- channel, cur->freq / 1e6);
- else if (channel >= 0)
- len += snprintf(buf + len, buflen - len, ", CH %3d, %g MHz",
- channel, cur->freq / 1e6);
+ else if (cur->chan >= 0)
+ len += snprintf(buf + len, buflen - len, ", %s %3d, %g MHz",
+ cur->freq < 5e9 ? "ch" : "CH",
+ cur->chan, cur->freq / 1e6);
else
len += snprintf(buf + len, buflen - len, ", %g GHz",
cur->freq / 1e9);
@@ -69,97 +67,53 @@ static char *fmt_scan_result(struct scan_result *cur, struct iw_range *iw_range,
if (cur->flags)
len += snprintf(buf + len, buflen - len, ", %s",
format_enc_capab(cur->flags, "/"));
- return buf;
}
static void display_aplist(WINDOW *w_aplst)
{
char s[IW_ESSID_MAX_SIZE << 3];
- int max_essid_len = 0;
- int i, line = START_LINE;
- int total = 0, open = 0, tg = 0, fg = 0;
- struct iw_range range;
- struct scan_result *head, *cur;
- struct cnt *stats;
- int max_cnt = NUMTOP;
- int skfd = socket(AF_INET, SOCK_DGRAM, 0);
-
- if (skfd < 0)
- err_sys("%s: can not open socket", __func__);
-
- iw_getinf_range(conf_ifname(), &range);
-
- head = get_scan_list(skfd, conf_ifname(),
- range.we_version_compiled);
- if (head) {
- ;
- } else if (errno == EPERM || !has_net_admin_capability()) {
- /*
- * Don't try to read leftover results, it does not work reliably
- */
- sprintf(s, "This screen requires CAP_NET_ADMIN permissions");
- } else if (errno == EINTR || errno == EAGAIN || errno == EBUSY) {
- /* Ignore temporary errors */
- goto done;
- } else if (!if_is_up(skfd, conf_ifname())) {
- sprintf(s, "Interface '%s' is down ", conf_ifname());
- if (!has_net_admin_capability())
- strcat(s, "- can not scan");
- else if (if_set_up(skfd, conf_ifname()) < 0)
- sprintf(s, "Can not bring up '%s' for scanning: %s",
- conf_ifname(), strerror(errno));
- else
- strcat(s, "- setting it up ...");
- } else if (errno == EFAULT) {
- /*
- * EFAULT can occur after a window resizing event and is temporary.
- * It may also occur when the interface is down, hence we need to
- * test the interface status first.
- */
- goto done;
- } else if (errno) {
- sprintf(s, "No scan on %s: %s", conf_ifname(), strerror(errno));
- } else {
- sprintf(s, "No scan results on %s", conf_ifname());
- }
-
- for (i = 1; i <= MAXYLEN; i++)
- mvwclrtoborder(w_aplst, i, 1);
-
- if (!head)
- waddstr_center(w_aplst, WAV_HEIGHT/2 - 1, s);
-
- for (cur = head; cur; cur = cur->next, total++) {
- if (str_is_ascii(cur->essid))
- max_essid_len = clamp(strlen(cur->essid),
- max_essid_len, IW_ESSID_MAX_SIZE);
- open += ! cur->has_key;
- if (cur->freq < 1e3)
- ; /* cur->freq is channel number */
- else if (cur->freq < 5e9)
- tg++;
- else
- fg++;
- }
-
- /* Truncate overly long access point lists to match screen height */
- for (cur = head; cur && line < MAXYLEN; line++, cur = cur->next) {
- int col = CP_SCAN_NON_AP;
+ const char *sort_type[] = {
+ [SO_CHAN] = "Chan",
+ [SO_SIGNAL] = "Sig",
+ [SO_ESSID] = "Essid",
+ [SO_OPEN] = "Open",
+ [SO_CHAN_SIG] = "Ch/Sg",
+ [SO_OPEN_SIG] = "Op/Sg"
+ };
+ int i, col, line = START_LINE;
+ struct scan_entry *cur;
+
+ /* Scanning can take several seconds - do not refresh if locked. */
+ if (pthread_mutex_trylock(&sr.mutex))
+ return;
+
+ if (sr.head || *sr.msg)
+ for (i = 1; i <= MAXYLEN; i++)
+ mvwclrtoborder(w_aplst, i, 1);
+
+ if (!sr.head)
+ waddstr_center(w_aplst, WAV_HEIGHT/2 - 1, sr.msg);
+
+ sort_scan_list(&sr.head);
+
+ /* Truncate overly long access point lists to match screen height. */
+ for (cur = sr.head; cur && line < MAXYLEN; line++, cur = cur->next) {
+ col = CP_SCAN_NON_AP;
if (cur->mode == IW_MODE_MASTER)
col = cur->has_key ? CP_SCAN_CRYPT : CP_SCAN_UNENC;
wmove(w_aplst, line, 1);
if (!*cur->essid) {
- sprintf(s, "%-*s ", max_essid_len, "<hidden ESSID>");
+ sprintf(s, "%-*s ", sr.max_essid_len, "<hidden ESSID>");
wattron(w_aplst, COLOR_PAIR(col));
waddstr(w_aplst, s);
} else if (str_is_ascii(cur->essid)) {
- sprintf(s, "%-*s ", max_essid_len, cur->essid);
+ sprintf(s, "%-*s ", sr.max_essid_len, cur->essid);
waddstr_b(w_aplst, s);
wattron(w_aplst, COLOR_PAIR(col));
} else {
- sprintf(s, "%-*s ", max_essid_len, "<cryptic ESSID>");
+ sprintf(s, "%-*s ", sr.max_essid_len, "<cryptic ESSID>");
wattron(w_aplst, COLOR_PAIR(col));
waddstr(w_aplst, s);
}
@@ -167,91 +121,112 @@ static void display_aplist(WINDOW *w_aplst)
wattroff(w_aplst, COLOR_PAIR(col));
- fmt_scan_result(cur, &range, s, sizeof(s));
+ fmt_scan_entry(cur, s, sizeof(s));
waddstr(w_aplst, " ");
waddstr(w_aplst, s);
}
- /* Summary statistics at the bottom. */
- if (total < NUMTOP)
+ if (sr.num.entries < MAX_CH_STATS)
goto done;
wmove(w_aplst, MAXYLEN, 1);
wadd_attr_str(w_aplst, A_REVERSE, "total:");
- sprintf(s, " %d", total);
+ sprintf(s, " %d ", sr.num.entries);
waddstr(w_aplst, s);
- if (total + START_LINE > line) {
- sprintf(s, " (%d not shown)", total + START_LINE - line);
+ sprintf(s, "%s %ssc", sort_type[conf.scan_sort_order], conf.scan_sort_asc ? "a" : "de");
+ wadd_attr_str(w_aplst, A_REVERSE, s);
+
+ if (sr.num.entries + START_LINE > line) {
+ sprintf(s, ", %d not shown", sr.num.entries + START_LINE - line);
waddstr(w_aplst, s);
}
- if (open) {
- sprintf(s, ", %d open", open);
+ if (sr.num.open) {
+ sprintf(s, ", %d open", sr.num.open);
waddstr(w_aplst, s);
}
- if (tg && fg) {
+
+ if (sr.num.two_gig && sr.num.five_gig) {
waddch(w_aplst, ' ');
wadd_attr_str(w_aplst, A_REVERSE, "5/2GHz:");
- sprintf(s, " %d/%d", fg, tg);
+ sprintf(s, " %d/%d", sr.num.five_gig, sr.num.two_gig);
waddstr(w_aplst, s);
}
- stats = channel_stats(head, &range, &max_cnt);
- if (stats) {
+ if (sr.channel_stats) {
waddch(w_aplst, ' ');
- if (conf.scan_sort_order == SO_CHAN_REV)
- sprintf(s, "bottom-%d:", max_cnt);
+ if (conf.scan_sort_order == SO_CHAN && !conf.scan_sort_asc)
+ sprintf(s, "bottom-%d:", (int)sr.num.ch_stats);
else
- sprintf(s, "top-%d:", max_cnt);
+ sprintf(s, "top-%d:", (int)sr.num.ch_stats);
wadd_attr_str(w_aplst, A_REVERSE, s);
- for (i = 0; i < max_cnt; i++) {
- sprintf(s, "%s CH-%d(%d)", i ? "," : "",
- stats[i].val, stats[i].count);
+ for (i = 0; i < sr.num.ch_stats; i++) {
+ waddstr(w_aplst, i ? ", " : " ");
+ sprintf(s, "ch#%d", sr.channel_stats[i].val);
+ wadd_attr_str(w_aplst, A_BOLD, s);
+ sprintf(s, " (%d)", sr.channel_stats[i].count);
waddstr(w_aplst, s);
}
}
- free(stats);
done:
- free_scan_result(head);
- close(skfd);
+ pthread_mutex_unlock(&sr.mutex);
wrefresh(w_aplst);
}
void scr_aplst_init(void)
{
w_aplst = newwin_title(0, WAV_HEIGHT, "Scan window", false);
- /*
- * Both parent and child process write to the terminal, updating
- * different areas of the screen. Suspending wavemon brings the
- * terminal state out of order, messing up the screen. The choice
- * is between a more complicated (sophisticated) handling of
- * signals, and to keep it simple by not allowing to suspend.
- */
- sig_tstp = xsignal(SIGTSTP, SIG_IGN);
/* Gathering scan data can take seconds. Inform user. */
mvwaddstr(w_aplst, START_LINE, 1, "Waiting for scan data ...");
wrefresh(w_aplst);
- pid = fork();
- if (pid < 0) {
- err_sys("could not fork scan process");
- } else if (pid == 0) {
- do display_aplist(w_aplst);
- while (usleep(conf.stat_iv * 1000) == 0);
- exit(EXIT_SUCCESS);
- }
+ scan_result_init(&sr);
+ pthread_create(&scan_thread, NULL, do_scan, &sr);
}
int scr_aplst_loop(WINDOW *w_menu)
{
- return wgetch(w_menu);
+ int key;
+
+ display_aplist(w_aplst);
+
+ key = wgetch(w_menu);
+ switch (key) {
+ case 'a': /* ascending */
+ conf.scan_sort_asc = true;
+ return -1;
+ case 'c': /* channel */
+ conf.scan_sort_order = SO_CHAN;
+ return -1;
+ case 'C': /* channel and signal */
+ conf.scan_sort_order = SO_CHAN_SIG;
+ return -1;
+ case 'd': /* descending */
+ conf.scan_sort_asc = false;
+ return -1;
+ case 'e': /* ESSID */
+ conf.scan_sort_order = SO_ESSID;
+ return -1;
+ case 'o': /* open (descending is default) */
+ conf.scan_sort_order = SO_OPEN;
+ conf.scan_sort_asc = false;
+ return -1;
+ case 'O': /* open and signal (descending) */
+ conf.scan_sort_order = SO_OPEN_SIG;
+ conf.scan_sort_asc = false;
+ return -1;
+ case 's': /* signal */
+ conf.scan_sort_order = SO_SIGNAL;
+ return -1;
+ }
+ return key;
}
void scr_aplst_fini(void)
{
- kill(pid, SIGTERM);
+ pthread_cancel(scan_thread);
+ scan_result_fini(&sr);
delwin(w_aplst);
- xsignal(SIGTSTP, sig_tstp);
}