diff options
Diffstat (limited to '')
-rw-r--r-- | Makefile.in | 12 | ||||
-rw-r--r-- | NEWS | 26 | ||||
-rw-r--r-- | README | 7 | ||||
-rw-r--r-- | THANKS | 9 | ||||
-rw-r--r-- | conf.c | 19 | ||||
-rw-r--r-- | conf_scr.c | 15 | ||||
-rwxr-xr-x | configure | 387 | ||||
-rw-r--r-- | configure.ac | 20 | ||||
-rwxr-xr-x | contrib/ipkg-build.sh | 68 | ||||
-rwxr-xr-x | contrib/wavemon.SlackBuild | 87 | ||||
-rw-r--r-- | info_scr.c | 16 | ||||
-rw-r--r-- | iw_if.c | 34 | ||||
-rw-r--r-- | iw_if.h | 66 | ||||
-rw-r--r-- | iw_scan.c | 264 | ||||
-rw-r--r-- | lhist_scr.c | 2 | ||||
-rw-r--r-- | llist.c | 49 | ||||
-rw-r--r-- | llist.h | 1 | ||||
-rw-r--r-- | scan_scr.c | 241 | ||||
-rw-r--r-- | timer.c | 18 | ||||
-rw-r--r-- | wavemon.1 | 18 | ||||
-rw-r--r-- | wavemon.c | 19 | ||||
-rw-r--r-- | wavemon.h | 14 | ||||
-rw-r--r-- | wavemonrc.5 | 22 |
23 files changed, 743 insertions, 671 deletions
diff --git a/Makefile.in b/Makefile.in index b7d8ece..0b27ad7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -8,10 +8,12 @@ datadir = $(datarootdir)/@PACKAGE_NAME@ exec_perms = 0755 install-suid-root: exec_perms = 4755 -CC = @CC@ -CFLAGS = @CFLAGS@ -DEFS = @DEFS@ -LDLIBS = @LIBS@ +CC ?= @CC@ +CFLAGS ?= @CFLAGS@ +CPPFLAGS ?= @CPPFLAGS@ +LDFLAGS ?= @LDFLAGS@ +DEFS ?= @DEFS@ +LDLIBS ?= @LIBS@ INSTALL = @INSTALL@ RM = rm -vf @@ -23,7 +25,7 @@ OBJS = $(PURESRC:.c=.o) DOCS = README NEWS THANKS AUTHORS COPYING ChangeLog %.o: %.c $(HEADERS) - $(CC) $(CFLAGS) $(DEFS) -c -o $@ $< + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(DEFS) -c -o $@ $< all: @PACKAGE_NAME@ @PACKAGE_NAME@: $(MAIN) $(OBJS) @@ -2,6 +2,32 @@ NEWS ==== -------------------- +0.7.6 (2014-01-18) +-------------------- + * Scan window: + - complete redesign based on pthreads to fix terminal bug, as a result of better + design the scan window now also can be suspended (details of fix are below), + - add message for 'too much data' (E2BIG) error case, + - use same formatting (3 digits) for 2/5 GHz, + - support for changing sort order via keyboard shortcuts (see manpage, several + ascending/descending sort orders are now available directly on the screen), + - currently active sort order is now displayed on status line at the bottom, + - stabilized by-channel sort to always use the same order for identical channels, + - added 'sort by Essid' to selection of sort menus; + * Info window: + - add percentage for easier reading of link quality, + - deprecate use of locale settings for 'discard' information (fixes display); + * Configuration screen: + - add support for Page Up/Down, Home, and End buttons to ease navigation, + - added more information to '-v' version information (ncurses/WE versions); + * Bug fixes: + - fix bug in ranking top/bottom 3 channels in scan window, + - complete redesign of scan process based on pthreads to fix bug that became + visible on ncurses >= 5.9.20130504 (slackware 14.1 and debian jessie), caused + by child/parent both updating the same screen (bug had been hidden by the + internal buffering of ncurses which was then removed), reported by Nick Warne. + +-------------------- 0.7.5 (2012-05-04) -------------------- * Scan window: @@ -3,7 +3,7 @@ wavemon is a wireless device monitoring application that allows you to watch signal and noise levels, packet statistics, device configuration and network -parameters of your wireless network hardware. It *should* work (though with +parameters of your wireless network hardware. It should work (though with varying features) with all devices supported by the Linux wireless kernel extensions by Jean Tourrilhes. @@ -12,9 +12,10 @@ See the man page for an in-depth description of operation and configuration. 2) Where to obtain ------------------ -You can always find the latest version at: +Apart from debian/ubuntu packages (apt-cache search wavemon) and slackbuild +scripts for wavemon on slackbuilds.org, up-to-date sources are available at - http://eden-feed.erg.abdn.ac.uk/wavemon + http://www.erg.abdn.ac.uk/ergcms/wavemon/ Please check this page for updates and for further information. wavemon is distributed under the GPLv3, refer to the file COPYING. @@ -3,14 +3,13 @@ improvements, or submitting actual code. Here is a list of these people. Help me (gerrit@erg.abdn.ac.uk) keep it complete and accurate. Christoph J. Thompson (cjsthompson@gmail.com) provided DESTDIR support for -the installation process and contributed a Slackware build script, found in -the contrib folder (2009-03-03). +the installation process and contributed a Slackware build script (2009-03-03). Tom Gordon to help fix a build error occurring under Puppy Linux, in wavemon version 0.5 (2009-02-06). Dave W. Capella (dave.capella@cornell.edu) provided modifications to build -ipkg packages for mobile devices in 2002 (now in contrib/). +ipkg packages for mobile devices in 2002. Nelson A. de Oliveira provided helpful tips for choosing a sensible screen-size default, and helped much to reduce annoyances in keeping the configuration-file @@ -32,3 +31,7 @@ Robert M. Stockmann and Koniu helped with bug fixes in 0.7.1; thanks to Koniu support for window resizing got started. Jonathan McCrohan fixed manpage compilation errors in 0.7.4. + +Mohammad Etemaddar suggested useful keyboard highlighting in 0.7.6. Nick Warne helped +uncover a bug in the scan window which led to a complete overhaul of based on threads; +he also provided useful suggestions to improve sorting of scan results (0.7.6). @@ -36,12 +36,11 @@ static char *action_items[] = { static char *sort_order[] = { [SO_CHAN] = "Channel", - [SO_CHAN_REV] = "Rev Channel", [SO_SIGNAL] = "Signal", + [SO_ESSID] = "Essid", [SO_OPEN] = "Open", [SO_CHAN_SIG] = "Chan/Sig", [SO_OPEN_SIG] = "Open/Sig", - [SO_OPEN_CH_SI] = "Open/Chan/Sig", NULL }; @@ -70,7 +69,8 @@ struct wavemon_conf conf = { .noise_min = -102, .noise_max = 10, - .scan_sort_order = SO_CHAN, + .scan_sort_order = SO_CHAN_SIG, + .scan_sort_asc = false, .lthreshold_action = TA_DISABLED, .lthreshold = -80, .hthreshold_action = TA_DISABLED, @@ -326,7 +326,7 @@ static void init_conf_items(void) ll_push(conf_items, "*", item); item = calloc(1, sizeof(*item)); - item->name = strdup("Scan sort order"); + item->name = strdup("Scan sort type"); item->cfname = strdup("sort_order"); item->type = t_list; item->v.i = &conf.scan_sort_order; @@ -334,6 +334,14 @@ static void init_conf_items(void) ll_push(conf_items, "*", item); item = calloc(1, sizeof(*item)); + item->name = strdup("Scan sort in ascending order"); + item->cfname = strdup("sort_ascending"); + item->type = t_list; + item->v.i = &conf.scan_sort_asc; + item->list = on_off_names; + ll_push(conf_items, "*", item); + + item = calloc(1, sizeof(*item)); item->name = strdup("Statistics updates"); item->cfname = strdup("stat_updates"); item->type = t_int; @@ -561,7 +569,8 @@ void getconf(int argc, char *argv[]) } if (version) { - printf("wavemon wireless monitor %s\n", PACKAGE_VERSION); + printf("wavemon %s", PACKAGE_VERSION); + printf(" with %s and %s.\n", we_version(), curses_version()); printf("Distributed under the terms of the GPLv3.\n%s", help ? "\n" : ""); } if (help) { @@ -33,7 +33,6 @@ extern int conf_items; /* index into array storing menu items */ static WINDOW *w_conf, *w_confpad; static int first_item, active_item; static int num_items, list_offset; -static struct conf_item *item; static void waddstr_item(WINDOW *w, int y, struct conf_item *item, char hilight) { @@ -133,13 +132,13 @@ static int select_item(int rv, int incr) /* Perform selection, return offset value to ensure pad fits inside window */ static int m_pref(WINDOW *w_conf, int list_offset, int active_item, int num_items) { - struct conf_item *item; int active_line, i, j; werase(w_conf); for (active_line = i = j = 0; i < num_items; i++) { - item = ll_get(conf_items, i); + struct conf_item *item = ll_get(conf_items, i); + if (!item->dep || *item->dep) { if (i != active_item) waddstr_item(w_conf, j++, item, 0); @@ -159,6 +158,7 @@ static int m_pref(WINDOW *w_conf, int list_offset, int active_item, int num_item void scr_conf_init(void) { + struct conf_item *item; conf_get_interface_list(false); /* may have changed in the meantime */ num_items = ll_size(conf_items); @@ -174,6 +174,7 @@ void scr_conf_init(void) int scr_conf_loop(WINDOW *w_menu) { + struct conf_item *item; int key; list_offset = m_pref(w_confpad, list_offset, active_item, num_items); @@ -185,7 +186,14 @@ int scr_conf_loop(WINDOW *w_menu) key = wgetch(w_menu); switch (key) { + case KEY_HOME: + active_item = first_item; + break; + case KEY_END: + active_item = num_items - 1; + break; case KEY_DOWN: + case KEY_NPAGE: active_item = select_item(active_item, 1); if (active_item >= num_items) { active_item = first_item; @@ -193,6 +201,7 @@ int scr_conf_loop(WINDOW *w_menu) } break; case KEY_UP: + case KEY_PPAGE: active_item = select_item(active_item, -1); if (active_item < first_item) active_item = num_items - 1; @@ -1,13 +1,11 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.68 for wavemon 0.7.5. +# Generated by GNU Autoconf 2.69 for wavemon 0.7.6. # # Report bugs to <gerrit@erg.abdn.ac.uk>. # # -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software -# Foundation, Inc. +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation @@ -136,6 +134,31 @@ export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh @@ -169,7 +192,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi -test x\$exitcode = x0 || exit 1" +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && @@ -214,21 +238,25 @@ IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : - # We cannot yet assume a decent shell, so we have to provide a - # neutralization value for shells without unset; and this also - # works around shells that cannot unset nonexistent variables. - # Preserve -v and -x to the replacement shell. - BASH_ENV=/dev/null - ENV=/dev/null - (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV - export CONFIG_SHELL - case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; - esac - exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 fi if test x$as_have_required = xno; then : @@ -331,6 +359,14 @@ $as_echo X"$as_dir" | } # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take @@ -452,6 +488,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). @@ -486,16 +526,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -507,28 +547,8 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -560,10 +580,10 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='wavemon' PACKAGE_TARNAME='wavemon-current' -PACKAGE_VERSION='0.7.5' -PACKAGE_STRING='wavemon 0.7.5' +PACKAGE_VERSION='0.7.6' +PACKAGE_STRING='wavemon 0.7.6' PACKAGE_BUGREPORT='gerrit@erg.abdn.ac.uk' -PACKAGE_URL='http://eden-feed.erg.abdn.ac.uk/wavemon' +PACKAGE_URL='http://www.erg.abdn.ac.uk/ergcms/wavemon/' # Factoring default headers for most tests. ac_includes_default="\ @@ -1130,8 +1150,6 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. - If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1217,7 +1235,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures wavemon 0.7.5 to adapt to many kinds of systems. +\`configure' configures wavemon 0.7.6 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1282,7 +1300,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of wavemon 0.7.5:";; + short | recursive ) echo "Configuration of wavemon 0.7.6:";; esac cat <<\_ACEOF @@ -1300,7 +1318,7 @@ Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to <gerrit@erg.abdn.ac.uk>. -wavemon home page: <http://eden-feed.erg.abdn.ac.uk/wavemon>. +wavemon home page: <http://www.erg.abdn.ac.uk/ergcms/wavemon/>. _ACEOF ac_status=$? fi @@ -1363,10 +1381,10 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -wavemon configure 0.7.5 -generated by GNU Autoconf 2.68 +wavemon configure 0.7.6 +generated by GNU Autoconf 2.69 -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF @@ -1697,7 +1715,7 @@ $as_echo "$ac_try_echo"; } >&5 test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || - $as_test_x conftest$ac_exeext + test -x conftest$ac_exeext }; then : ac_retval=0 else @@ -1786,8 +1804,8 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by wavemon $as_me 0.7.5, which was -generated by GNU Autoconf 2.68. Invocation command line was +It was created by wavemon $as_me 0.7.6, which was +generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2276,7 +2294,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2316,7 +2334,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2369,7 +2387,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2410,7 +2428,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue @@ -2468,7 +2486,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2512,7 +2530,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -2958,8 +2976,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <stdarg.h> #include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> +struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -3218,7 +3235,7 @@ case $as_dir/ in #(( # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. @@ -3294,7 +3311,7 @@ do for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue + as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in @@ -3360,7 +3377,7 @@ do for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue + as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in @@ -3573,7 +3590,7 @@ done for ac_header in ncurses.h fcntl.h netdb.h sys/ioctl.h sys/time.h unistd.h\ - net/if_arp.h netinet/ether.h net/ethernet.h + net/if_arp.h netinet/ether.h net/ethernet.h pthread.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -3646,60 +3663,60 @@ else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include <stdbool.h> -#ifndef bool - "error: bool is not defined" -#endif -#ifndef false - "error: false is not defined" -#endif -#if false - "error: false is not 0" -#endif -#ifndef true - "error: true is not defined" -#endif -#if true != 1 - "error: true is not 1" -#endif -#ifndef __bool_true_false_are_defined - "error: __bool_true_false_are_defined is not defined" -#endif - - struct s { _Bool s: 1; _Bool t; } s; - - char a[true == 1 ? 1 : -1]; - char b[false == 0 ? 1 : -1]; - char c[__bool_true_false_are_defined == 1 ? 1 : -1]; - char d[(bool) 0.5 == true ? 1 : -1]; - /* See body of main program for 'e'. */ - char f[(_Bool) 0.0 == false ? 1 : -1]; - char g[true]; - char h[sizeof (_Bool)]; - char i[sizeof s.t]; - enum { j = false, k = true, l = false * true, m = true * 256 }; - /* The following fails for - HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ - _Bool n[m]; - char o[sizeof n == m * sizeof n[0] ? 1 : -1]; - char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; - /* Catch a bug in an HP-UX C compiler. See - http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html - http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html - */ - _Bool q = true; - _Bool *pq = &q; + #include <stdbool.h> + #ifndef bool + "error: bool is not defined" + #endif + #ifndef false + "error: false is not defined" + #endif + #if false + "error: false is not 0" + #endif + #ifndef true + "error: true is not defined" + #endif + #if true != 1 + "error: true is not 1" + #endif + #ifndef __bool_true_false_are_defined + "error: __bool_true_false_are_defined is not defined" + #endif + + struct s { _Bool s: 1; _Bool t; } s; + + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) 0.5 == true ? 1 : -1]; + /* See body of main program for 'e'. */ + char f[(_Bool) 0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + enum { j = false, k = true, l = false * true, m = true * 256 }; + /* The following fails for + HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ + _Bool n[m]; + char o[sizeof n == m * sizeof n[0] ? 1 : -1]; + char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; + /* Catch a bug in an HP-UX C compiler. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + */ + _Bool q = true; + _Bool *pq = &q; int main () { - bool e = &s; - *pq |= q; - *pq |= ! q; - /* Refer to every declared value, to avoid compiler optimizations. */ - return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l - + !m + !n + !o + !p + !q + !pq); + bool e = &s; + *pq |= q; + *pq |= ! q; + /* Refer to every declared value, to avoid compiler optimizations. */ + return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l + + !m + !n + !o + !p + !q + !pq); ; return 0; @@ -3714,7 +3731,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 $as_echo "$ac_cv_header_stdbool_h" >&6; } -ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" + ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" if test "x$ac_cv_type__Bool" = xyes; then : cat >>confdefs.h <<_ACEOF @@ -3724,6 +3741,7 @@ _ACEOF fi + if test $ac_cv_header_stdbool_h = yes; then $as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h @@ -3775,6 +3793,8 @@ _ACEOF LIBS="-lm $LIBS" +else + as_fn_error $? "math library not found" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for waddstr in -lncurses" >&5 @@ -3820,6 +3840,50 @@ _ACEOF LIBS="-lncurses $LIBS" +else + as_fn_error $? "ncurses library not found" "$LINENO" 5 +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 +$as_echo_n "checking for pthread_create in -lpthread... " >&6; } +if ${ac_cv_lib_pthread_pthread_create+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_create (); +int +main () +{ +return pthread_create (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pthread_pthread_create=yes +else + ac_cv_lib_pthread_pthread_create=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 +$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } +if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : + CFLAGS="$CFLAGS -pthread" +else + as_fn_error $? "pthread library not found" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap_get_flag in -lcap" >&5 @@ -3868,14 +3932,8 @@ _ACEOF fi - # Testing library functions -for ac_func in modf log10 gettimeofday \ - memmove memset strdup \ - strchr strspn strcspn \ - strcasecmp strncasecmp \ - strtol \ - ether_ntohost +for ac_func in gettimeofday ether_ntohost do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -3944,11 +4002,11 @@ else int main () { -/* FIXME: Include the comments suggested by Paul. */ + #ifndef __cplusplus - /* Ultrix mips cc rejects this. */ + /* Ultrix mips cc rejects this sort of thing. */ typedef int charset[2]; - const charset cs; + const charset cs = { 0, 0 }; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; @@ -3965,8 +4023,9 @@ main () ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; - { /* SCO 3.2v4 cc rejects this. */ - char *t; + { /* SCO 3.2v4 cc rejects this sort of thing. */ + char tx; + char *t = &tx; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; @@ -3982,10 +4041,10 @@ main () iptr p = 0; ++p; } - { /* AIX XL C 1.02.0.0 rejects this saying + { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; }; - struct s *b; b->j = 5; + struct s { int j; const int *ap[3]; } bx; + struct s *b = &bx; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; @@ -4499,16 +4558,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. + # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' + as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi else - as_ln_s='cp -p' + as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -4568,28 +4627,16 @@ else as_mkdir_p=false fi -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in #( - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -4610,8 +4657,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by wavemon $as_me 0.7.5, which was -generated by GNU Autoconf 2.68. Invocation command line was +This file was extended by wavemon $as_me 0.7.6, which was +generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -4658,17 +4705,17 @@ Configuration files: $config_files Report bugs to <gerrit@erg.abdn.ac.uk>. -wavemon home page: <http://eden-feed.erg.abdn.ac.uk/wavemon>." +wavemon home page: <http://www.erg.abdn.ac.uk/ergcms/wavemon/>." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -wavemon config.status 0.7.5 -configured by $0, generated by GNU Autoconf 2.68, +wavemon config.status 0.7.6 +configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -4746,7 +4793,7 @@ fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then - set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' diff --git a/configure.ac b/configure.ac index 8d6cbce..e50c986 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.64) -AC_INIT([wavemon], [0.7.5], [gerrit@erg.abdn.ac.uk], [wavemon-current], - [http://eden-feed.erg.abdn.ac.uk/wavemon]) +AC_INIT([wavemon], [0.7.6], [gerrit@erg.abdn.ac.uk], [wavemon-current], + [http://www.erg.abdn.ac.uk/ergcms/wavemon/]) # Variables @@ -28,7 +28,7 @@ AC_PROG_INSTALL AC_HEADER_STDC AC_HEADER_TIME AC_CHECK_HEADERS([ncurses.h fcntl.h netdb.h sys/ioctl.h sys/time.h unistd.h\ - net/if_arp.h netinet/ether.h net/ethernet.h], + net/if_arp.h netinet/ether.h net/ethernet.h pthread.h], [], [AC_MSG_ERROR($ac_header not found)]) # linux/if.h needs 'struct sockaddr' @@ -53,18 +53,14 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM( AC_HEADER_STDBOOL # Tests involving libraries -AC_CHECK_LIB([m], [pow]) -AC_CHECK_LIB([ncurses], [waddstr]) +AC_CHECK_LIB([m], [pow], [], [AC_MSG_ERROR(math library not found)]) +AC_CHECK_LIB([ncurses], [waddstr], [], [AC_MSG_ERROR(ncurses library not found)]) +AC_CHECK_LIB([pthread], [pthread_create], [CFLAGS="$CFLAGS -pthread"], + [AC_MSG_ERROR(pthread library not found)]) AC_CHECK_LIB([cap], [cap_get_flag]) - # Testing library functions -AC_CHECK_FUNCS([modf log10 gettimeofday \ - memmove memset strdup \ - strchr strspn strcspn \ - strcasecmp strncasecmp \ - strtol \ - ether_ntohost], [], +AC_CHECK_FUNCS([gettimeofday ether_ntohost], [], [AC_MSG_ERROR(function '$ac_func' not supported)]) # Checks for typedefs, structures, and compiler characteristics. diff --git a/contrib/ipkg-build.sh b/contrib/ipkg-build.sh deleted file mode 100755 index 731c42d..0000000 --- a/contrib/ipkg-build.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/bin/sh -# -# ipkg build script for wavemon -# -# Extracted from a former Makefile target by Dave W. Capella -# -# However I could not reach that person, hence $maintainer is commented out. -# Send any updates / suggestions to gerrit@erg.abdn.ac.uk. - -# CONFIGURATION -#maintainer="dave w capella <dave.capella@cornell.edu>" # email address outdated -version="0.5" - -build_dir="./ipkg" -ctl_dir="${build_dir}/CONTROL" -bin_dir="${build_dir}/usr/local/bin" -doc_dir="${build_dir}/usr/doc/wavemon" -men_dir="${build_dir}/usr/lib/menu" - -set -e - -# 'clean' option -case "$1" in - clean|CLEAN) rm -vfr $build_dir; exit 0; -esac - - -# Build directories -for dir in $ctl_dir $bin_dir $doc_dir $men_dir -do - test -d $dir || mkdir -vp $dir -done - - -# Binary -test -f ../wavemon || exec echo "Run 'make' first to build wavemon" -cp -vp ../wavemon ${bin_dir} - - -# Documentation -for ipaq_doc in ../AUTHORS ../COPYING ../Makefile ../README -do - cp -vp ${ipaq_doc} ${doc_dir} -done - -groff -Tascii -man ../wavemon.1 > ${doc_dir}/wavemon.doc -groff -Tascii -man ../wavemonrc.5 > ${doc_dir}/wavemonrc.doc - - -# Menu -cat > ${men_dir}/wavemon <<EO_MENU -?package(wavemon): needs="text" section="Utilities" title="wavemon" command="wavemon" -EO_MENU - - -# Control -cat > ${ctl_dir}/control <<EO_CONTROL -Package: wavemon -Version: $version -Architecture: arm -Maintainer: ${maintainer:-unknown} -Description: Wireless network monitoring tool -Priority: optional -Section: extras -EO_CONTROL - -# Build -ipkg-build ${build_dir} diff --git a/contrib/wavemon.SlackBuild b/contrib/wavemon.SlackBuild deleted file mode 100755 index f955d49..0000000 --- a/contrib/wavemon.SlackBuild +++ /dev/null @@ -1,87 +0,0 @@ -#!/bin/sh -# -# Slackware build script for wavemon -# -# Contributed by Christoph J. Thompson <cjsthompson@gmail.com> -# -CWD=`pwd` -NAME=wavemon -VERSION=0.5 -ARCH=i586 -BUILD=1 -PKGNAME=$NAME -PKGVER=$VERSION -BASEDIR=$PKGNAME-$PKGVER -ARCHIVE=$PKGNAME-current.tar.bz2 -REPOSITORY=http://eden-feed.erg.abdn.ac.uk/wavemon -FLAGS="-O2 -march=pentium -mtune=pentium -fno-strength-reduce \ - -fomit-frame-pointer -ffast-math" -PKG=/tmp/package-$NAME - -rm -rf $PKG -mkdir -p $PKG - -# Obtain sources -if [ ! -e $ARCHIVE ]; then - if `wget "$REPOSITORY/$ARCHIVE"`; then - true - else - exit 1 - fi -fi - -# Compile -cd /tmp -tar jxvf $CWD/$ARCHIVE -cd $BASEDIR -CFLAGS="${FLAGS}" CXXFLAGS="${FLAGS}" ./configure \ - --prefix=/usr \ - --mandir=/usr/man \ - --sysconfdir=/etc \ - --localstatedir=/var -make CFLAGS="${FLAGS}" - -# Install -make install DESTDIR=$PKG -mkdir -p $PKG/install $PKG/usr/doc/$NAME-$VERSION -cat > $PKG/install/slack-desc <<EOF -# HOW TO EDIT THIS FILE: -# The "handy ruler" below makes it easier to edit a package description. Line -# up the first '|' above the ':' following the base package name, and the '|' on -# the right side marks the last column you can put a character in. You must make -# exactly 11 lines for the formatting to be correct. It's also customary to -# leave one space after the ':'. - - |-----handy-ruler------------------------------------------------------| -wavemon: wavemon (wireless monitoring utility) -wavemon: -wavemon: Wavemon is a text-mode monitoring application for wireless network -wavemon: devices originally developed by Jan Morgenstern. -wavemon: -wavemon: Homepage: http://eden-feed.erg.abdn.ac.uk/wavemon -wavemon: -wavemon: -wavemon: -wavemon: -wavemon: -EOF - -cat > $PKG/install/slack-required <<EOF -ncurses >= 5.6-i486-3 -EOF - -install -m644 -g0 -o0 AUTHORS README \ - $PKG/usr/doc/$NAME-$VERSION - -chmod 444 $PKG/usr/man/man?/*.? -gzip -9nf $PKG/usr/man/man?/*.? - -strip \ - $PKG/usr/bin/* || : - -chown -R root.root $PKG - -# Make package -cd $PKG -cat install/slack-desc | grep "$NAME:" > /tmp/$NAME-$VERSION-$ARCH-$BUILD.txt -makepkg -l y -c n /tmp/$NAME-$VERSION-$ARCH-$BUILD.tgz @@ -75,8 +75,10 @@ static void display_levels(void) qual = ewma(qual, cur.stat.qual.qual, conf.meter_decay / 100.0); mvwaddstr(w_levels, line++, 1, "link quality: "); - sprintf(tmp, "%0.f/%d ", qual, cur.range.max_qual.qual); + sprintf(tmp, "%0.f%% ", (1e2 * qual)/cur.range.max_qual.qual); waddstr_b(w_levels, tmp); + sprintf(tmp, "(%0.f/%d) ", qual, cur.range.max_qual.qual); + waddstr(w_levels, tmp); waddbar(w_levels, line++, qual, 0, cur.range.max_qual.qual, lvlscale, true); @@ -151,20 +153,20 @@ static void display_stats(void) waddstr_b(w_stats, tmp); waddstr(w_stats, ", invalid: "); - sprintf(tmp, "%'u", cur.stat.discard.nwid); + sprintf(tmp, "%u", cur.stat.discard.nwid); waddstr_b(w_stats, tmp); waddstr(w_stats, " nwid, "); - sprintf(tmp, "%'u", cur.stat.discard.code); + sprintf(tmp, "%u", cur.stat.discard.code); waddstr_b(w_stats, tmp); waddstr(w_stats, " crypt, "); - sprintf(tmp, "%'u", cur.stat.discard.fragment); + sprintf(tmp, "%u", cur.stat.discard.fragment); waddstr_b(w_stats, tmp); waddstr(w_stats, " frag, "); - sprintf(tmp, "%'u", cur.stat.discard.misc); + sprintf(tmp, "%u", cur.stat.discard.misc); waddstr_b(w_stats, tmp); waddstr(w_stats, " misc"); @@ -180,11 +182,11 @@ static void display_stats(void) waddstr_b(w_stats, tmp); waddstr(w_stats, ", mac retries: "); - sprintf(tmp, "%'u", cur.stat.discard.retries); + sprintf(tmp, "%u", cur.stat.discard.retries); waddstr_b(w_stats, tmp); waddstr(w_stats, ", missed beacons: "); - sprintf(tmp, "%'u", cur.stat.miss.beacon); + sprintf(tmp, "%u", cur.stat.miss.beacon); waddstr_b(w_stats, tmp); wclrtoborder(w_stats); @@ -39,16 +39,20 @@ static int if_get_flags(int skfd, const char *ifname) return ifr.ifr_flags; } -/* Return true if @ifname is known to be up */ +/* Return true if @ifname is known to be up. */ bool if_is_up(int skfd, const char *ifname) { return if_get_flags(skfd, ifname) & IFF_UP; } /** Bring @ifname up if not already up. Return 0 if ok, < 0 on error. */ -int if_set_up(int skfd, const char *ifname) +int if_set_up(const char *ifname) { struct ifreq ifr; + int ret, skfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (skfd < 0) + err_sys("%s: can not open socket", __func__); memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); @@ -58,7 +62,9 @@ int if_set_up(int skfd, const char *ifname) return 0; ifr.ifr_flags |= IFF_UP; - return ioctl(skfd, SIOCSIFFLAGS, &ifr); + ret = ioctl(skfd, SIOCSIFFLAGS, &ifr); + close(skfd); + return ret; } /* Interface information */ @@ -128,6 +134,8 @@ char **iw_get_interface_list(void) continue; if_list = realloc(if_list, sizeof(char *) * (nifs + 1)); + if (if_list == NULL) + err_sys("can not reallocate interface list"); if_list[nifs-1] = strdup(p); if_list[nifs++] = NULL; } @@ -157,7 +165,7 @@ void if_getstat(const char *ifname, struct if_stat *stat) lp += strlen(ifname) + 1; lp += strspn(lp, " "); - sscanf(lp, "%llu %llu %lu %lu %lu %lu %lu %lu %llu %llu", + sscanf(lp, "%Lu %Lu %lu %lu %lu %lu %lu %lu %Lu %Lu", &stat->rx_bytes, &stat->rx_packets, &d, &d, &d, &d, &d, &d, &stat->tx_bytes, &stat->tx_packets); } @@ -304,7 +312,9 @@ void dyn_info_cleanup(struct iw_dyn_info *info) /* - * get range information + * Request range information for a given wireless interface. + * @ifname: name of the wireless argument + * @range: storage location to populate with range information. */ void iw_getinf_range(const char *ifname, struct iw_range *range) { @@ -468,6 +478,17 @@ void iw_getstat(struct iw_stat *iw) iw_sanitize(&iw->range, &iw->stat.qual, &iw->dbm); } +const char *we_version(void) +{ + static char buf[BUFSIZ]; + struct iw_stat iw; + + iw_getinf_range(conf_ifname(), &iw.range); + sprintf(buf, "wireless extensions v%d (source v%d)", + iw.range.we_version_compiled, iw.range.we_version_source); + return buf; +} + void dump_parameters(void) { struct iw_dyn_info info; @@ -501,8 +522,7 @@ void dump_parameters(void) } printf("\n"); } - printf(" WE version: %d (source version %d)\n\n", - iw.range.we_version_compiled, iw.range.we_version_source); + printf("\n"); if (info.cap_essid) { if (info.essid_ct > 1) @@ -19,6 +19,7 @@ */ #include "wavemon.h" #include <netdb.h> +#include <pthread.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ether.h> @@ -66,11 +67,10 @@ struct if_info { netmask, bcast; uint16_t mtu; - short txqlen; - short flags; + uint16_t txqlen; + uint16_t flags; }; -extern bool if_is_up(int skfd, const char *ifname); -extern int if_set_up(int skfd, const char *ifname); +extern int if_set_up(const char *ifname); extern void if_getinf(const char *ifname, struct if_info *info); /** @@ -232,30 +232,32 @@ static inline void sampling_stop(void) { alarm(0); } * Organization of scan results */ /** - * struct scan_result - Ranked list of scan results + * struct scan_entry - Representation of a single scan result. * @ap_addr: MAC address * @essid: station SSID (may be empty) * @mode: operation mode (type of station) * @freq: frequency/channel information + * @chan: channel corresponding to @freq (where applicable) * @qual: signal quality information * @has_key: whether using encryption or not * @flags: properties gathered from Information Elements - * @next: next, lower-ranking entry + * @next: next entry in list */ -struct scan_result { +struct scan_entry { struct ether_addr ap_addr; char essid[IW_ESSID_MAX_SIZE + 2]; int mode; double freq; - struct iw_quality qual; + int chan; + struct iw_quality qual; + struct iw_levelstat dbm; int has_key:1; uint32_t flags; - struct scan_result *next; + struct scan_entry *next; }; -extern struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version); -extern void free_scan_result(struct scan_result *head); +extern void sort_scan_list(struct scan_entry **headp); /** * struct cnt - count frequency of integer numbers @@ -266,8 +268,42 @@ struct cnt { int val; int count; }; -extern struct cnt *channel_stats(struct scan_result *head, - struct iw_range *iw_range, int *max_cnt); + +/** + * struct scan_result - Structure to aggregate all collected scan data. + * @head: begin of scan_entry list (may be NULL) + * @msg: error message, if any + * @max_essid_len: maximum ESSID-string length (for formatting) + * @channel_stats: array of channel statistics entries + * @num.total: number of entries in list starting at @head + * @num.open: number of open entries among @num.total + * @num.two_gig: number of 2.4GHz stations among @num.total + * @num.five_gig: number of 5 GHz stations among @num.total + * @num.ch_stats: length of @channel_stats array + * @range: range data associated with scan interface + * @mutex: protects against concurrent consumer/producer access + */ +struct scan_result { + struct scan_entry *head; + char msg[128]; + uint16_t max_essid_len; + struct cnt *channel_stats; + struct assorted_numbers { + uint16_t entries, + open, + two_gig, + five_gig; +/* Maximum number of 'top' statistics entries. */ +#define MAX_CH_STATS 3 + size_t ch_stats; + } num; + struct iw_range range; + pthread_mutex_t mutex; +}; + +extern void scan_result_init(struct scan_result *sr); +extern void scan_result_fini(struct scan_result *sr); +extern void *do_scan(void *sr_ptr); /* * General helper routines @@ -484,8 +520,8 @@ static inline int freq_to_channel(double freq, const struct iw_range *range) { int i; - if (freq < 1.0e3) - return -1; + if (freq < 1e3) /* Convention: freq is channel number if < 1e3 */ + return freq; for (i = 0; i < range->num_frequency; i++) if (freq_to_hz(&range->freq[i]) == freq) @@ -519,7 +519,7 @@ static int iw_extract_event_stream(struct stream_descr *stream, return 1; } -static void iw_extract_ie(struct iw_event *iwe, struct scan_result *sr) +static void iw_extract_ie(struct iw_event *iwe, struct scan_entry *sr) { const uint8_t wpa1_oui[3] = { 0x00, 0x50, 0xf2 }; uint8_t *buffer = iwe->u.data.pointer; @@ -549,64 +549,77 @@ static void iw_extract_ie(struct iw_event *iwe, struct scan_result *sr) /*----------------- End of code copied from iwlib -----------------------*/ /* - * Ordering functions for scan results + * Ordering functions for scan results: all return true for a < b. */ -/* Order by ascending frequency. */ -static int cmp_chan(const struct scan_result *a, const struct scan_result *b) + +/* Order by frequency. */ +static bool cmp_freq(const struct scan_entry *a, const struct scan_entry *b) { return a->freq < b->freq; } -static int cmp_chan_rev(const struct scan_result *a, const struct scan_result *b) +/* Order by signal strength. */ +static bool cmp_sig(const struct scan_entry *a, const struct scan_entry *b) { - return cmp_chan(b, a); + return a->qual.level < b->qual.level; } -/* Order by descending signal strength. */ -static int cmp_sig(const struct scan_result *a, const struct scan_result *b) +/* Order by ESSID, organize entries with same ESSID by frequency and signal. */ +static bool cmp_essid(const struct scan_entry *a, const struct scan_entry *b) { - return a->qual.level - b->qual.level; + int res = strncmp(a->essid, b->essid, IW_ESSID_MAX_SIZE); + + return res == 0 ? (a->freq == b->freq ? cmp_sig(a, b) : cmp_freq(a, b)) + : res < 0; } -/* Order by ascending frequency first, then by descending signal strength. */ -static int cmp_chan_sig(const struct scan_result *a, const struct scan_result *b) +/* Order by frequency, grouping channels by ESSID. */ +static bool cmp_chan(const struct scan_entry *a, const struct scan_entry *b) { - return a->freq == b->freq ? cmp_sig(a, b) : cmp_chan(a, b); + return a->freq == b->freq ? cmp_essid(a, b) : cmp_freq(a, b); } -/* Show open access points first, ordered by mode. */ -static int cmp_open(const struct scan_result *a, const struct scan_result *b) +/* Order by frequency first, then by signal strength. */ +static bool cmp_chan_sig(const struct scan_entry *a, const struct scan_entry *b) { - return a->has_key > b->has_key; + return a->freq == b->freq ? cmp_sig(a, b) : cmp_chan(a, b); } -/* Sort (open) access points by descending signal strength. */ -static int cmp_open_sig(const struct scan_result *a, const struct scan_result *b) +/* Order by openness (open access points frist). */ +static bool cmp_open(const struct scan_entry *a, const struct scan_entry *b) { - return a->has_key == b->has_key ? cmp_sig(a, b) : cmp_open(a, b); + return a->has_key < b->has_key; } -/* Sort (open) access points by ascending frequency, then by descending signal. */ -static int cmp_open_chan_sig(const struct scan_result *a, const struct scan_result *b) +/* Sort (open) access points by signal strength. */ +static bool cmp_open_sig(const struct scan_entry *a, const struct scan_entry *b) { - return a->has_key == b->has_key ? cmp_chan_sig(a, b) : cmp_open(a, b); + return a->has_key == b->has_key ? cmp_sig(a, b) : cmp_open(a, b); } -static int (*scan_cmp[])(const struct scan_result *, const struct scan_result *) = { +static bool (*scan_cmp[])(const struct scan_entry *, const struct scan_entry *) = { [SO_CHAN] = cmp_chan, - [SO_CHAN_REV] = cmp_chan_rev, [SO_SIGNAL] = cmp_sig, + [SO_ESSID] = cmp_essid, [SO_OPEN] = cmp_open, [SO_CHAN_SIG] = cmp_chan_sig, - [SO_OPEN_SIG] = cmp_open_sig, - [SO_OPEN_CH_SI] = cmp_open_chan_sig + [SO_OPEN_SIG] = cmp_open_sig }; -struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version) +/** + * Produce ranked list of scan results. + * @ifname: interface name to run scan on + * @we_version: version of the WE extensions (needed internally) + */ +static struct scan_entry *get_scan_list(const char *ifname, int we_version) { - struct scan_result *head = NULL; + struct scan_entry *head = NULL, **tailp = &head; struct iwreq wrq; int wait, waited = 0; + int skfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (skfd < 0) + err_sys("%s: can not open socket", __func__); /* * Some drivers may return very large scan results, either because there * are many cells, or there are many large elements. Do not bother to @@ -620,7 +633,7 @@ struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version) memset(&wrq, 0, sizeof(wrq)); strncpy(wrq.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ); if (ioctl(skfd, SIOCSIWSCAN, &wrq) < 0) - return NULL; + goto done; /* Larger initial timeout of 250ms between set and first get */ for (wait = 250; (waited += wait) < MAX_SCAN_WAIT; wait = 100) { @@ -641,7 +654,7 @@ struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version) if (wrq.u.data.length) { struct iw_event iwe; struct stream_descr stream; - struct scan_result *new = NULL; + struct scan_entry *new = NULL; int f = 0; /* Idea taken from waproamd */ memset(&stream, 0, sizeof(stream)); @@ -686,37 +699,53 @@ struct scan_result *get_scan_list(int skfd, const char *ifname, int we_version) break; } if (f == 127) { - struct scan_result *cur = head, **prev = &head; - - while (cur && scan_cmp[conf.scan_sort_order](cur, new) > 0) - prev = &cur->next, cur = cur->next; - - *prev = new; - new->next = cur; - new = NULL; - f = 0; + f = 0; + *tailp = new; + tailp = &new->next; + new = NULL; } } - free(new); /* may have been allocated but not filled in */ + free(new); /* may have been allocated, but not filled in */ } +done: + close(skfd); return head; } -void free_scan_result(struct scan_result *head) +/* + * Simple sort routine. + * FIXME: use hash or tree to store entries, a list to display them. + */ +void sort_scan_list(struct scan_entry **headp) +{ + struct scan_entry *head = NULL, *cur, *new = *headp, **prev; + + while (new) { + for (cur = head, prev = &head; cur && + conf.scan_sort_asc == scan_cmp[conf.scan_sort_order](cur, new); + prev = &cur->next, cur = cur->next) + ; + *prev = new; + new = new->next; + (*prev)->next = cur; + } + *headp = head; +} + +static void free_scan_list(struct scan_entry *head) { if (head) { - free_scan_result(head->next); + free_scan_list(head->next); free(head); } } /* - * Channel statistics + * Channel statistics shown at the bottom of scan screen. */ - /* - * For lfind, it compares key value with array member, needs to + * For lsearch, it compares key value with array member, needs to * return 0 if they are the same, non-0 otherwise. */ static int cmp_key(const void *a, const void *b) @@ -727,41 +756,140 @@ static int cmp_key(const void *a, const void *b) /* For quick-sorting the array in descending order of counts */ static int cmp_cnt(const void *a, const void *b) { - if (conf.scan_sort_order == SO_CHAN_REV) + if (conf.scan_sort_order == SO_CHAN && !conf.scan_sort_asc) return ((struct cnt *)a)->count - ((struct cnt *)b)->count; return ((struct cnt *)b)->count - ((struct cnt *)a)->count; } -struct cnt *channel_stats(struct scan_result *head, - struct iw_range *iw_range, int *max_cnt) +/** + * Fill in sr->channel_stats (must not have been allocated yet). + */ +static void compute_channel_stats(struct scan_result *sr) { - struct scan_result *cur; - struct cnt *arr = NULL, *bin, key = {0, 0}; - size_t cnt = 0, n = 0; - - for (cur = head; cur; cur = cur->next) - cnt++; - if (!cnt) - return NULL; - - arr = calloc(cnt, sizeof(key)); - for (cur = head; cur && n < *max_cnt; cur = cur->next) { - key.val = freq_to_channel(cur->freq, iw_range); - - if (key.val >= 0) { - bin = lsearch(&key, arr, &n, sizeof(key), cmp_key); + struct scan_entry *cur; + struct cnt *bin, key = {0, 0}; + size_t n = 0; + + if (!sr->num.entries) + return; + + sr->channel_stats = calloc(sr->num.entries, sizeof(key)); + for (cur = sr->head; cur; cur = cur->next) { + if (cur->chan >= 0) { + key.val = cur->chan; + bin = lsearch(&key, sr->channel_stats, &n, sizeof(key), cmp_key); if (bin) bin->count++; } } - if (n < *max_cnt) - *max_cnt = n; if (n > 0) { - qsort(arr, n, sizeof(key), cmp_cnt); + qsort(sr->channel_stats, n, sizeof(key), cmp_cnt); } else { - free(arr); - return NULL; + free(sr->channel_stats); + sr->channel_stats = NULL; } - return arr; + sr->num.ch_stats = n < MAX_CH_STATS ? n : MAX_CH_STATS; +} + +/* + * Scan results. + */ +void scan_result_init(struct scan_result *sr) +{ + memset(sr, 0, sizeof(*sr)); + iw_getinf_range(conf_ifname(), &sr->range); + pthread_mutex_init(&sr->mutex, NULL); +} + +void scan_result_fini(struct scan_result *sr) +{ + free_scan_list(sr->head); + free(sr->channel_stats); + pthread_mutex_destroy(&sr->mutex); +} + +/** The actual scan thread. */ +void *do_scan(void *sr_ptr) +{ + struct scan_result *sr = (struct scan_result *)sr_ptr; + struct scan_entry *cur; + + pthread_detach(pthread_self()); + + do { + pthread_mutex_lock(&sr->mutex); + + free_scan_list(sr->head); + free(sr->channel_stats); + + sr->head = NULL; + sr->channel_stats = NULL; + sr->msg[0] = '\0'; + sr->max_essid_len = MAX_ESSID_LEN; + memset(&(sr->num), 0, sizeof(sr->num)); + + sr->head = get_scan_list(conf_ifname(), sr->range.we_version_compiled); + if (!sr->head) { + switch(errno) { + case EPERM: + /* Don't try to read leftover results, it does not work reliably. */ + if (!has_net_admin_capability()) + snprintf(sr->msg, sizeof(sr->msg), + "This screen requires CAP_NET_ADMIN permissions"); + break; + case EFAULT: + /* + * EFAULT can occur after a window resizing event and is temporary. + * It may also occur when the interface is down, hence defer handling. + */ + break; + case EINTR: + case EBUSY: + case EAGAIN: + /* Temporary errors. */ + snprintf(sr->msg, sizeof(sr->msg), "Waiting for scan data on %s ...", conf_ifname()); + break; + case ENETDOWN: + snprintf(sr->msg, sizeof(sr->msg), "Interface %s is down - setting it up ...", conf_ifname()); + if (if_set_up(conf_ifname()) < 0) + err_sys("Can not bring up interface '%s'", conf_ifname()); + break; + case E2BIG: + /* + * This is a driver issue, since already using the largest possible + * scan buffer. See comments in iwlist.c of wireless tools. + */ + snprintf(sr->msg, sizeof(sr->msg), + "No scan on %s: Driver returned too much data", conf_ifname()); + break; + case 0: + snprintf(sr->msg, sizeof(sr->msg), "Empty scan results on %s", conf_ifname()); + break; + default: + snprintf(sr->msg, sizeof(sr->msg), + "Scan failed on %s: %s", conf_ifname(), strerror(errno)); + } + } + + for (cur = sr->head; cur; cur = cur->next) { + if (str_is_ascii(cur->essid)) + sr->max_essid_len = clamp(strlen(cur->essid), + sr->max_essid_len, + IW_ESSID_MAX_SIZE); + iw_sanitize(&sr->range, &cur->qual, &cur->dbm); + cur->chan = freq_to_channel(cur->freq, &sr->range); + if (cur->freq >= 5e9) + sr->num.five_gig++; + else if (cur->freq >= 2e9) + sr->num.two_gig++; + sr->num.entries += 1; + sr->num.open += !cur->has_key; + } + compute_channel_stats(sr); + + pthread_mutex_unlock(&sr->mutex); + } while (usleep(conf.stat_iv * 1000) == 0); + + return NULL; } diff --git a/lhist_scr.c b/lhist_scr.c index e93359f..84b6925 100644 --- a/lhist_scr.c +++ b/lhist_scr.c @@ -222,7 +222,7 @@ static void display_lhist(void) /* Clear screen and set up horizontal grid lines */ wattrset(w_lhist, COLOR_PAIR(CP_STATBKG)); for (y = 1; y <= HIST_MAXYLEN; y++) - mvwaddch(w_lhist, hist_y(y), hist_x(x), y % 5 ? ' ' : '-'); + mvwaddch(w_lhist, hist_y(y), hist_x(x), (y % 5) ? ' ' : '-'); /* * SNR comes first, as it determines the background. If either @@ -33,7 +33,7 @@ * c = char * f = float * s = char * - * S = char *, case-insensitive and fuzzy (for ll_scan) + * S = char *, case-insensitive and fuzzy * * = void * */ @@ -75,7 +75,6 @@ static llist *arg2element(char type, va_list * ap, llist * next) *((double *)l->e) = va_arg(*ap, double); break; case '*': - l->e = (void *)malloc(sizeof(void *)); l->e = va_arg(*ap, void *); } l->next = next; @@ -199,52 +198,6 @@ void ll_replace(int ld, unsigned long n, const char *format, ...) } /* - * return the position of a given element in list (or -1) - */ -signed long ll_scan(int ld, const char *format, ...) -{ - llist *l = lists[ld]; - va_list ap; - int len, i, rv = -1, int_v; - double double_v; - char *string_v; - - va_start(ap, format); - switch (*format) { - case 'd': - int_v = va_arg(ap, int); - for (i = 0; (l = l->next); i++) - if (*(int *)l->e == int_v) - rv = i; - break; - case 'f': - double_v = va_arg(ap, double); - for (i = 0; (l = l->next); i++) - if (*(double *)l->e == double_v) - rv = i; - break; - case 's': - string_v = strdup(va_arg(ap, char *)); - for (i = 0; (l = l->next); i++) - if (!strcmp(l->e, string_v)) - rv = i; - free(string_v); - break; - case 'S': - string_v = strdup(va_arg(ap, char *)); - len = strlen(string_v); - for (i = 0; (l = l->next); i++) - if (strncasecmp(l->e, string_v, len) == 0) { - rv = i; - break; - } - free(string_v); - } - va_end(ap); - return rv; -} - -/* * return the number of elements in a given list */ unsigned long ll_size(int ld) @@ -25,6 +25,5 @@ void *ll_getall(int ld); void ll_reset(int ld); void ll_push(int ld, const char *format, ...); void ll_replace(int ld, unsigned long n, const char *format, ...); -signed long ll_scan(int ld, const char *format, ...); unsigned long ll_size(int ld); void ll_destroy(int ld); @@ -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); } @@ -19,23 +19,23 @@ */ #include "wavemon.h" -void start_timer(struct timer *t, unsigned long duration) +static unsigned long get_usecs(void) { struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); - t->stime = tv.tv_sec * 1000000 + tv.tv_usec; - t->duration = duration; + return tv.tv_sec * 1000000 + tv.tv_usec; } -int end_timer(struct timer *t) +void start_timer(struct timer *t, unsigned long duration) { - struct timeval tv; - struct timezone tz; - - gettimeofday(&tv, &tz); + t->stime = get_usecs(); + t->duration = duration; +} - return (tv.tv_sec * 1000000 + tv.tv_usec >= t->stime + t->duration); +bool end_timer(struct timer *t) +{ + return get_usecs() >= t->stime + t->duration; } @@ -73,21 +73,23 @@ SNR graph appear. .TP .B Scan window (F3 or 's') A periodically updated network scan, showing access points and other -wireless clients, ordered by frequency and then descending order of signal -quality. Each entry starts with the ESSID, followed by the colour-coded MAC +wireless clients. It is sorted depending on \fIsort_order\fR and \fIsort_ascending\fR, see \fBwavemonrc\fR(5). +Each entry starts with the ESSID, followed by the colour-coded MAC address and the signal/channel information. A green/red MAC address indicates an (un-)encrypted access point, the colour changes to yellow for non-access points (in this case the mode is shown at the end of the line). The uncoloured information following the MAC address lists relative and absolute signal strengths, channel, frequency, and the mode if the node is not an access point. -A status line at the bottom informs about the most (least) crowded channels, -depending on how \fIsort_order\fR is set (reverse channel or other); see -\fBwavemonrc\fR(5). +A status line at the bottom informs about the current sort order and a few +statistics, such as most (least) crowded channels (least crowded channels +are listed when sorting by descending channel). -Please note that gathering meaningful scan data can take several seconds. Partly -for this reason, the Scan window is the only screen that can not be suspended -(CTRL-Z). +The \fIsort_order\fR can also directly be changed via these keyboard shortcuts: +\fIa\fRscending, \fId\fRescending; by \fIe\fRssid, \fIs\fRignal, \fIc\fRhannel (\fIC\fR also with signal), +or by \fIo\fRpen access (\fIO\fR also with signal). + +Please note that gathering meaningful scan data can take several seconds. .TP .B Preferences (F7 or 'p') This screen allows you to change all program options such as interface and @@ -114,7 +114,16 @@ static WINDOW *init_menubar(const enum wavemon_screen active) wattrset(menu, cur != active ? COLOR_PAIR(CP_INACTIVE) : COLOR_PAIR(CP_ACTIVE) | A_BOLD); - wprintw(menu, "%-6s", screens[cur].key_name); + if (*screens[cur].key_name) { + + wattron(menu, A_UNDERLINE); + waddch(menu, screens[cur].key_name[0]); + wattroff(menu, A_UNDERLINE); + + wprintw(menu, "%-5s", screens[cur].key_name + 1); + } else { + wprintw(menu, "%-6s", ""); + } } wrefresh(menu); @@ -131,7 +140,6 @@ static void check_geometry(void) int main(int argc, char *argv[]) { - WINDOW *w_menu; enum wavemon_screen cur, next; sigset_t blockmask, oldmask; @@ -145,12 +153,12 @@ int main(int argc, char *argv[]) /* initialize the ncurses interface */ initscr(); - check_geometry(); - cbreak(); noecho(); nonl(); - clear(); + cbreak(); curs_set(0); + clear(); + check_geometry(); start_color(); init_pair(CP_STANDARD, COLOR_WHITE, COLOR_BLACK); @@ -183,6 +191,7 @@ int main(int argc, char *argv[]) sigaddset(&blockmask, SIGWINCH); for (cur = conf.startup_scr; cur != SCR_QUIT; cur = next) { + WINDOW *w_menu; int escape = 0; if (sigprocmask(SIG_BLOCK, &blockmask, &oldmask) < 0) @@ -80,6 +80,9 @@ enum info_screen_geometry { /* Number of seconds to display a warning message outside ncurses mode */ #define WARN_DISPLAY_DELAY 3 +/* Minimum SSID length */ +#define MAX_ESSID_LEN 16 + /* * Symbolic names of actions to take when crossing thresholds. * These actions invoke the corresponding ncurses functions. @@ -104,12 +107,11 @@ static inline void threshold_action(enum threshold_action action) */ enum scan_sort_order { SO_CHAN, - SO_CHAN_REV, SO_SIGNAL, + SO_ESSID, SO_OPEN, SO_CHAN_SIG, - SO_OPEN_SIG, - SO_OPEN_CH_SI + SO_OPEN_SIG }; /* @@ -134,7 +136,8 @@ extern struct wavemon_conf { int check_geometry, /* ensure window is large enough */ cisco_mac, /* Cisco-style MAC addresses */ random, /* random signals */ - override_bounds; /* override autodetection */ + override_bounds, /* override autodetection */ + scan_sort_asc; /* direction of @scan_sort_order */ /* Enumerated values */ int scan_sort_order, /* channel|signal|open|chan/sig ... */ @@ -276,6 +279,7 @@ static inline int cp_from_scale(float value, const char *cscale, bool reverse) /* * Wireless interfaces */ +extern const char *we_version(void); extern const char *conf_ifname(void); extern void conf_get_interface_list(bool init); extern char **iw_get_interface_list(void); @@ -290,7 +294,7 @@ struct timer { }; extern void start_timer(struct timer *t, unsigned long d); -extern int end_timer(struct timer *t); +extern bool end_timer(struct timer *t); /* * Error handling diff --git a/wavemonrc.5 b/wavemonrc.5 index f860b89..0353194 100644 --- a/wavemonrc.5 +++ b/wavemonrc.5 @@ -24,18 +24,24 @@ If enabled, display MAC addresses using lower-case hex digits separated by dots rather than colons. .P .RE -.B sort_order = (channel|rev channel|signal|open|chan/sig|open/sig|open/chan/sig) +.B sort_order = (channel|essid|signal|open|chan/sig|open/sig) .RS .RE -(Scan Sort Order) +(Scan sort type) .RS -Determines the ordering used in the scan window: (\fIrev\fR) \fIchannel\fR sorts in ascending -(descending) order of channel number, \fIsignal\fR in descending order of signal strength, and -\fIopen\fR in descending order of openness. The combined variants \fIchan/sig\fR and -\fIopen/sig\fR sort first by channel/openness and then by signal strength; their combined -functionality is provided by \fIopen/chan/sig\fR. +Determines the ordering used in the scan window: \fIchannel\fR sorts by channel number, \fIessid\fR by +access point name, \fIsignal\fR by signal strength, and \fIopen\fR by openness. The combined variants +\fIchan/sig\fR and \fIopen/sig\fR sort first by channel/openness and then by signal strength. It also affects the status line at the bottom: normally the most crowded channels are listed, -use of \fIrev\ channel\fR switches to least crowded ones instead. +sorting by \fIchannel\fR in descending order switches to least crowded ones instead. +.P +.RE +.B sort_ascending = (on|off) +.RS +.RE +(Scan sort direction) +.RS +Sets the direction of the \fIsort_order\fR: ascending (on) or descending (off). .P .RE .B stat_updates = <n> |