aboutsummaryrefslogtreecommitdiffstats
path: root/scan_scr.c
blob: ab6b433c2fe4793d8151a3985aa6616a1d3eb663 (plain)
generated by cgit v1.2.3 (git 2.25.1) at 2024-11-15 17:33:52 +0000
 


='#n175'>175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*
 * 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 "iw_if.h"

#define START_LINE	2	/* where to begin the screen */

/* GLOBALS */
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)
{
	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);
	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);
	else if (!(cur->qual.updated & IW_QUAL_LEVEL_INVALID))
		len += snprintf(buf + len, buflen - len, "%.0f dBm",
				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)
		len += snprintf(buf + len, buflen - len, ", Ch %2d, %g MHz",
				channel, cur->freq / 1e6);
	else
		len += snprintf(buf + len, buflen - len, ", %g GHz",
				cur->freq / 1e9);

	/* Access Points are marked by CP_SCAN_CRYPT/CP_SCAN_UNENC already */
	if (cur->mode != IW_MODE_MASTER)
		len += snprintf(buf + len, buflen - len, " %s",
				iw_opmode(cur->mode));
	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;
	struct iw_range range;
	struct scan_result *head, *cur;
	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, cmp_freq_sig);
	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) {
		if (str_is_ascii(cur->essid))
			max_essid_len = clamp(strlen(cur->essid),
					      max_essid_len, IW_ESSID_MAX_SIZE);
	}

	/* 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;

		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>");
			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);
			waddstr_b(w_aplst, s);
			wattron(w_aplst, COLOR_PAIR(col));
		} else {
			sprintf(s, "%-*s ", max_essid_len, "<cryptic ESSID>");
			wattron(w_aplst, COLOR_PAIR(col));
			waddstr(w_aplst, s);
		}
		waddstr(w_aplst, ether_addr(&cur->ap_addr));

		wattroff(w_aplst, COLOR_PAIR(col));

		fmt_scan_result(cur, &range, s, sizeof(s));
		waddstr(w_aplst, " ");
		waddstr(w_aplst, s);
	}
	free_scan_result(head);
done:
	close(skfd);
	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);
	}
}

int scr_aplst_loop(WINDOW *w_menu)
{
	return wgetch(w_menu);
}

void scr_aplst_fini(void)
{
	kill(pid, SIGTERM);
	delwin(w_aplst);
	xsignal(SIGTSTP, sig_tstp);
}