/*
* Copyright (c) 1998,2004 Rinet Corp., Novosibirsk, Russia
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* THIS SOURCE CODE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_SLCURSES
#include <slcurses.h>
#elif HAVE_NCURSES
#include <ncurses.h>
#else
#include <curses.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "getkey.h"
#include "screen.h"
#include "session.h"
#include "trafshow.h"
#include "selector.h"
#include "show_if.h"
#include "show_stat.h"
#include "show_dump.h"
#include "events.h"
#include "netstat.h"
#include "help_page.h"
static void read_key(SESSION *sd, const unsigned char *data, int len);
static int scan_key(const unsigned char *buf, int len);
static void parse_key(int key, PCAP_HANDLER *ph);
static void init_edit_string(const char *prompter, const char *charset, int size);
static int edit_string(int ch);
/* edit string stuff */
static const char *numbers = "1234567890";
static const char *spaces = " ,.;@/\\";
static char prompt_buf[MAX_PARAM_LEN], cut_buf[MAX_PARAM_LEN];
static const char *char_set;
static int buf_size, cur, nb, win, scr, bartop, barlen, touch, show_win;
void
getkey_init(ph)
PCAP_HANDLER *ph;
{
SESSION *sd;
if ((sd = session_open(0, 0, PlainFile)) == 0) {
perror("session_open 0"); /* should not happen */
exit(1);
}
session_setcallback(sd, 0, 0, read_key);
session_setcookie(sd, ph);
prompt_mode = 0;
}
static void
read_key(sd, data, len)
SESSION *sd;
const unsigned char *data;
int len;
{
/* sanity check */
if (sd && data && len > 0) {
int key = scan_key(data, len);
if (key != -1)
parse_key(key, (PCAP_HANDLER *)session_cookie(sd));
}
}
static SELECTOR *
get_selector(ph_list)
PCAP_HANDLER *ph_list;
{
/* return current selector */
switch (show_mode) {
case Interfaces:
return show_if_selector();
case NetStat:
return show_stat_selector(pcap_get_selected(ph_list));
case FlowDump:
/* nope */
break;
case HelpPage:
return help_page_selector();
}
return 0;
}
static void
parse_key(key, ph_list)
int key;
PCAP_HANDLER *ph_list;
{
int ch = key;
PCAP_HANDLER *ph = 0;
SELECTOR *sp = 0;
struct timeval now;
if (prompt_mode) {
const char *txt = 0;
int redraw = 1;
if ((ch = edit_string(ch)) == 0) /* still edit */
return;
if (ch > 0) {
switch (prompt_mode) {
case 'r': /* end of getting refresh time */
case 'R':
ch = atoi(prompt_buf);
if (ch > 0 && ch != refresh_time) {
if (ch < purge_time)
refresh_time = ch;
else txt = "Refresh Time must be less than Purge Time";
}
break;
case 'p': /* end of getting purge time */
case 'P':
ch = atoi(prompt_buf);
if (ch > 0 && ch != purge_time) {
if (ch > refresh_time) {
purge_time = ch;
add_event(0, pcap_purge, ph_list);
redraw = 0;
} else txt = "Purge Time must be bigger than Refresh Time";
}
break;
case 'f': /* end of getting filter expression */
case 'F':
if (!expression || strcmp(prompt_buf, expression)) {
if (expression) free(expression);
expression = strdup(prompt_buf);
if ((txt = pcap_setexpr(ph_list, expression)) == 0) {
if (prompt_mode == 'F') {
add_event(0, pcap_clear, ph_list);
redraw = 0;
}
}
}
break;
case '/': /* end of getting search string */
if (prompt_buf[0] == '\0') {
if (search) {
free(search);
search = 0;
txt = "Search mode turned Off";
}
} else if (!search || strcmp(prompt_buf, search)) {
if (search) free(search);
search = strdup(prompt_buf);
}
break;
case 'a': /* end of getting aggregation masklen */
case 'A':
if (prompt_buf[0]) {
ch = atoi(prompt_buf);
if (ch < 0 || ch > ADDRBITLEN) {
txt = "Wrong netmask length";
break;
}
} else ch = -1;
if (show_mode == NetStat &&
(ph = pcap_get_selected(ph_list)) != 0) {
if (ph->masklen != ch) {
ph->masklen = ch;
if (prompt_mode == 'A')
netstat_purge(ph, 0);
}
} else {
aggregate = ch;
for (ph = ph_list; ph; ph = ph->next) {
if (ph->masklen != aggregate) {
ph->masklen = aggregate;
if (prompt_mode == 'A')
netstat_purge(ph, 0);
}
}
}
break;
}
}
prompt_mode = 0;
if (redraw)
add_event(0, pcap_show, ph_list);
if (txt)
screen_status(txt);
else screen_update();
return;
}
/* try global operation keys */
switch (ch) {
case K_ESC: /* get back show mode */
case 'q':
case 'Q':
switch (show_mode) {
case Interfaces:
exit(0);
case NetStat:
if ((ph = pcap_get_selected(ph_list)) != 0 && ph->top)
pcaph_close(ph);
else show_mode = Interfaces;
pcap_show(ph_list);
return;
case FlowDump:
show_dump_close();
show_mode = NetStat;
pcap_show(ph_list);
return;
case HelpPage:
show_mode = help_page_mode();
pcap_show(ph_list);
return;
}
break;
case K_CTRL('L'): /* refresh screen */
clear();
refresh();
pcap_show(ph_list);
return;
case 'h': /* help page if any */
case 'H':
case '?':
case K_F1:
if (help_page_list(show_mode)) {
show_mode = HelpPage;
pcap_show(ph_list);
return;
}
break;
case 'r': /* start to get refresh time */
case 'R':
if (show_mode != FlowDump) {
prompt_mode = ch;
snprintf(prompt_buf, sizeof(prompt_buf), "%d", refresh_time);
init_edit_string("Refresh seconds: ", numbers, 5);
selector_withdraw(get_selector(ph_list));
screen_update();
return;
}
break;
case 'p': /* start to get purge time */
case 'P':
if (show_mode != FlowDump) {
prompt_mode = ch;
snprintf(prompt_buf, sizeof(prompt_buf), "%d", purge_time);
init_edit_string("Purge seconds: ", numbers, 5);
selector_withdraw(get_selector(ph_list));
screen_update();
return;
}
break;
case 'f': /* start to get filter expression */
case 'F':
if (show_mode != FlowDump) {
prompt_mode = ch;
prompt_buf[0] = '\0';
if (expression) {
(void)strncpy(prompt_buf, expression, sizeof(prompt_buf));
prompt_buf[sizeof(prompt_buf)-1] = '\0';
}
init_edit_string("Filter expression: ", 0, sizeof(prompt_buf));
selector_withdraw(get_selector(ph_list));
screen_update();
return;
}
break;
case '/': /* start to get search string */
if (show_mode != FlowDump) {
prompt_mode = ch;
prompt_buf[0] = '\0';
if (search) {
(void)strncpy(prompt_buf, search, sizeof(prompt_buf));
prompt_buf[sizeof(prompt_buf)-1] = '\0';
}
init_edit_string("Search string: ", 0, sizeof(prompt_buf));
selector_withdraw(get_selector(ph_list));
screen_update();
return;
}
break;
case K_CTRL('_'): /* turn off search mode */
if (show_mode != FlowDump) {
if (search) {
free(search);
search = 0;
screen_status("Search mode turned Off");
}
return;
}
break;
case 'a': /* start to get aggregation masklen */
case 'A':
if (show_mode != FlowDump) {
char buf[100];
prompt_mode = ch;
prompt_buf[0] = '\0';
if (show_mode == NetStat &&
(ph = pcap_get_selected(ph_list)) != 0) {
if (ph->masklen >= 0)
snprintf(prompt_buf, sizeof(prompt_buf), "%d", ph->masklen);
snprintf(buf, sizeof(buf), "%s aggregation netmask length: ", ph->name);
} else {
if (aggregate >= 0)
snprintf(prompt_buf, sizeof(prompt_buf), "%d", aggregate);
(void)strcpy(buf, "Aggregation netmask length: ");
}
init_edit_string(buf, numbers, 5);
selector_withdraw(get_selector(ph_list));
screen_update();
return;
}
break;
case K_CTRL('R'): /* reset all netstat hash */
if (show_mode == Interfaces) {
add_event(0, pcap_clear, ph_list);
screen_status("Resetting all flows");
return;
}
break;
case 'n': /* toggle numeric values to names conversion */
case 'N':
if (show_mode != FlowDump) {
nflag ^= 1;
if (ch == 'N') {
add_event(0, pcap_show, ph_list);
} else {
screen_status("Numeric values turned %s",
nflag ? "On" : "Off");
}
return;
}
break;
}
/* prevent screen refresh overhead */
gettimeofday(&now, 0);
now.tv_sec += refresh_time;
add_event(&now, pcap_show, ph_list);
/* get current selector */
switch (show_mode) {
case Interfaces:
sp = show_if_selector();
break;
case NetStat:
if ((ph = pcap_get_selected(ph_list)) == 0)
return;
sp = show_stat_selector(ph);
/* try special input for the show mode */
if (show_stat_input(ph, ch)) {
selector_redraw(sp);
return;
}
break;
case FlowDump:
/* special input only for the show mode */
show_dump_input(ch);
return;
case HelpPage:
sp = help_page_selector();
break;
}
/* try special input for the selecting */
ch = selector_move(ch, sp);
if (ch < 0) {
selector_redraw(sp);
return;
}
/* something selected */
switch (show_mode) {
case Interfaces:
if ((ph = pcap_set_selected(ph_list, ch)) == 0)
return; /* should not happen */
/*selector_withdraw(sp);*/
show_mode = NetStat;
pcap_show(ph_list);
return;
case NetStat:
/*selector_withdraw(sp);*/
if (ph->masklen == -1) {
if (show_dump_open(ph, show_stat_get(ph, ch)) == 0)
show_mode = FlowDump;
} else if (pcaph_create(ph, (struct netstat_header *)show_stat_get(ph, ch))) {
pcap_show(ph_list);
}
return;
case FlowDump:
/* not reached; just to avoid compiler warning */
return;
case HelpPage:
key = help_page_key(ch);
if (key != -1 && key != 0) {
show_mode = help_page_mode(); /* get back show mode */
pcap_show(ph_list);
parse_key(key, ph_list);
}
return;
}
}
static int
scan_key(buf, len)
const unsigned char *buf;
int len;
{
int i;
if (buf[0] != ESCAPE) return buf[0];
if (len == 1) return K_ESC;
i = 1;
if (buf[i] == '[' || buf[i] == 'O')
if (++i >= len) return -1;
switch (buf[i]) {
case '\0': /* xterm */
return K_HOME;
case 'A':
case 'i':
return K_UP;
case 'B':
return K_DOWN;
case 'D':
return K_LEFT;
case 'C':
return K_RIGHT;
case 'I': /* ansi PgUp */
case 'V': /* at386 PgUp */
case 'S': /* 97801 PgUp */
case 'v': /* emacs style */
return K_PAGEUP;
case 'G': /* ansi PgDn */
case 'U': /* at386 PgDn */
case 'T': /* 97801 PgDn */
return K_PAGEDOWN;
case 'H': /* at386 Home */
return K_HOME;
case 'F': /* ansi End */
case 'Y': /* at386 End */
return K_END;
case '5': /* vt200 PgUp */
return K_PAGEUP;
case '6': /* vt200 PgUp */
return K_PAGEDOWN;
case '1': /* vt200 PgUp */
if (++i >= len) return -1;
switch(buf[i]) { /* xterm */
case '1':
return K_F1;
case '2':
return K_F2;
case '3':
return K_F3;
case '4':
return K_F4;
case '5': /* RS/6000 PgUp is 150g, PgDn is 154g */
if (++i >= len) return -1;
if (buf[i] == '0')
return K_PAGEUP;
if (buf[i] == '4')
return K_PAGEDOWN;
}
return K_HOME;
case '4': /* vt200 PgUp */
return K_END;
case '2': /* xterm */
case 'L':
return K_INS;
case 'M':
return K_F1;
case 'N':
return K_F2;
case 'O':
return K_F3;
case 'P':
return K_F4;
}
return -1;
}
static void
init_edit_string(prompter, charset, size)
const char *prompter, *charset;
int size;
{
int i;
char_set = charset;
touch = 0;
show_win = 0;
*cut_buf = '\0';
bartop = strlen(prompter);
i = COLS - (bartop + 3);
barlen = buf_size = size;
if (barlen < 1 || barlen > i) {
barlen = i;
show_win = 1;
}
attrset(A_NORMAL);
move(LINES-1, 0);
clrtoeol();
addstr(prompter);
nb = strlen(prompt_buf);
if (nb >= buf_size) nb = buf_size - 1;
prompt_buf[nb] = '\0';
cur = nb;
win = cur / barlen; /* window number */
scr = cur % barlen; /* screen position */
if (show_win) mvprintw(LINES-1, COLS-2, "%-2d", win+1);
attrset(A_STANDOUT);
mvprintw(LINES-1, bartop, "%-*.*s", barlen, barlen, &prompt_buf[win * barlen]);
screen_dock_cursor(LINES-1, bartop + scr);
}
static int
edit_string(ch)
int ch;
{
int i;
switch (ch) {
case K_ESC:
case K_CR:
case K_NL:
prompt_buf[nb] = '\0';
attrset(A_NORMAL);
move(LINES-1, 0);
clrtoeol();
screen_dock_cursor(0, 0);
return (ch == K_ESC ? -1 : 1);
case K_PAGEUP: /* move to begin of window */
cur -= cur % barlen;
break;
case K_PAGEDOWN:/* move to end of window */
if (strlen(&prompt_buf[cur]) < barlen)
cur = nb;
else cur += barlen - cur % barlen - 1;
break;
case K_UP: /* skip to previous word */
case K_CTRL('P'):
ch = 0;
for (i = cur; i > 0; i--) {
if (!ch) {
if (strchr(spaces, prompt_buf[i-1]))
ch++;
} else if (!strchr(spaces, prompt_buf[i-1]))
break;
}
cur = i;
break;
case K_DOWN: /* skip to next word */
case K_CTRL('N'):
ch = 0;
for (i = cur; i < nb; i++) {
if (!ch) {
if (strchr(spaces, prompt_buf[i]))
ch++;
} else if (!strchr(spaces, prompt_buf[i]))
break;
}
cur = i;
break;
case K_HOME: /* move to begin of line */
case K_CTRL('A'):
cur = 0;
break;
case K_END: /* move to end of line */
case K_CTRL('E'):
cur = nb;
break;
case K_LEFT: /* move cursor left */
case K_CTRL('B'):
if (cur > 0) cur--;
break;
case K_RIGHT: /* move cursor right */
case K_CTRL('F'):
if (cur < nb) cur++;
break;
case K_BS: /* backspace */
if (nb && cur) {
memmove(&prompt_buf[cur-1], &prompt_buf[cur], nb - cur);
cur--;
nb--;
}
break;
case K_DEL: /* delete */
case K_CTRL('D'):
if (nb && cur < nb) {
memmove(&prompt_buf[cur], &prompt_buf[cur+1], nb - cur);
nb--;
}
break;
case K_CTRL('U'): /* erase entire line */
(void)strcpy(cut_buf, prompt_buf);
nb = 0;
cur = 0;
break;
case K_CTRL('W'): /* erase last word */
ch = 0;
for (i = cur; i > 0; i--) {
if (!ch) {
if (strchr(spaces, prompt_buf[i-1]))
ch++;
} else if (!strchr(spaces, prompt_buf[i-1]))
break;
}
if (cur > i) {
memcpy(cut_buf, &prompt_buf[i], cur - i);
cut_buf[cur - i] = '\0';
memmove(&prompt_buf[i], &prompt_buf[cur], cur - i);
nb -= cur - i;
cur = i;
}
break;
case K_CTRL('K'): /* erase end of line */
if (prompt_buf[cur] != '\0')
(void)strcpy(cut_buf, &prompt_buf[cur]);
nb = cur;
break;
case K_TAB: /* insert cut_buf */
i = strlen(cut_buf);
if (i && (buf_size - 1) - strlen(prompt_buf) >= i) {
memmove(&prompt_buf[cur+i], &prompt_buf[cur], nb - cur);
memmove(&prompt_buf[cur], cut_buf, i);
nb += i;
cur += i;
}
break;
default:
if (ch < 32 || ch > 126)
return 0; /* skip garbage chars */
if (char_set && !strchr(char_set, ch)) {
beep();
return 0;
}
if (!touch) {
nb = 0;
cur = 0;
}
if (nb >= buf_size - 1) { /* no more space available */
beep();
return 0;
}
if (nb > cur)
memmove(&prompt_buf[cur+1], &prompt_buf[cur], nb - cur);
prompt_buf[cur++] = ch;
nb++;
}
touch = 1;
prompt_buf[nb] = '\0';
win = cur / barlen; /* window number */
scr = cur % barlen; /* screen position */
attrset(A_STANDOUT);
mvprintw(LINES-1, bartop, "%-*.*s", barlen, barlen, &prompt_buf[win * barlen]);
if (show_win) {
attrset(A_NORMAL);
mvprintw(LINES-1, COLS-2, "%-2d", win+1);
}
screen_dock_cursor(LINES-1, bartop + scr);
screen_update();
return 0;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>