Commit a9663192 by pdw

Patch for text output mode by originally by Patrik Bless, updated by Roman Hoog

Antink <rha@open.ch>
1 parent 59cf3606
......@@ -15,7 +15,7 @@ sbin_PROGRAMS = iftop
iftop_SOURCES = addr_hash.c edline.c hash.c iftop.c ns_hash.c \
options.c resolver.c screenfilter.c serv_hash.c \
sorted_list.c threadprof.c ui.c util.c \
sorted_list.c threadprof.c ui_common.c ui.c tui.c util.c \
addrs_ioctl.c addrs_dlpi.c dlcommon.c \
stringmap.c cfgfile.c vector.c
......@@ -30,7 +30,7 @@ iftop_SOURCES = addr_hash.c edline.c hash.c iftop.c ns_hash.c \
noinst_HEADERS = addr_hash.h ether.h ethertype.h extract.h hash.h iftop.h \
integers.h ip.h llc.h ns_hash.h options.h resolver.h \
screenfilter.h serv_hash.h sll.h sorted_list.h tcp.h \
threadprof.h token.h ui.h dlcommon.h stringmap.h \
threadprof.h token.h ui_common.h ui.h tui.h dlcommon.h stringmap.h \
vector.h cfgfile.h ppp.h
man_MANS = iftop.8
......
......@@ -39,6 +39,9 @@ char * config_directives[] = {
"net-filter6",
"link-local",
"port-display",
"timed-output",
"no-curses",
"num-lines",
NULL
};
......
......@@ -76,6 +76,11 @@ hash_status_enum hash_find(hash_type* hash_table, void* key, void **rec) {
hash_status_enum hash_next_item(hash_type* hash_table, hash_node_type** ppnode) {
int i;
if (hash_table == 0) {
return HASH_STATUS_KEY_NOT_FOUND;
}
if(*ppnode != NULL) {
if((*ppnode)->next != NULL) {
*ppnode = (*ppnode)->next;
......@@ -101,6 +106,11 @@ hash_status_enum hash_next_item(hash_type* hash_table, hash_node_type** ppnode)
void hash_delete_all(hash_type* hash_table) {
int i;
hash_node_type *n, *nn;
if(hash_table == 0) {
return;
}
for(i = 0; i < hash_table->size; i++) {
n = hash_table->table[i];
while(n != NULL) {
......
......@@ -32,7 +32,9 @@
#include "iftop.h"
#include "addr_hash.h"
#include "resolver.h"
#include "ui_common.h"
#include "ui.h"
#include "tui.h"
#include "options.h"
#ifdef DLT_LINUX_SLL
#include "sll.h"
......@@ -65,6 +67,7 @@ extern options_t options;
hash_type* history;
history_type history_totals;
time_t last_timestamp;
time_t first_timestamp;
int history_pos = 0;
int history_len = 1;
pthread_mutex_t tick_mutex;
......@@ -141,12 +144,27 @@ void tick(int print) {
t = time(NULL);
if(t - last_timestamp >= RESOLUTION) {
analyse_data();
ui_print();
if (options.no_curses) {
if (!options.timed_output || options.timed_output && t - first_timestamp >= options.timed_output) {
tui_print();
if (options.timed_output) {
finish(SIGINT);
}
}
}
else {
ui_print();
}
history_rotate();
last_timestamp = t;
}
else {
ui_tick(print);
if (options.no_curses) {
tui_tick(print);
}
else {
ui_tick(print);
}
}
pthread_mutex_unlock(&tick_mutex);
......@@ -247,6 +265,8 @@ static void handle_ip_packet(struct ip* iptr, int hw_dir)
memset(&ap, '\0', sizeof(ap));
tick(0);
if( (IP_V(iptr) ==4 && options.netfilter == 0)
|| (IP_V(iptr) == 6 && options.netfilter6 == 0) ) {
/*
......@@ -570,8 +590,6 @@ static void handle_eth_packet(unsigned char* args, const struct pcap_pkthdr* pkt
ether_type = ntohs(eptr->ether_type);
payload = packet + sizeof(struct ether_header);
tick(0);
if(ether_type == ETHERTYPE_8021Q) {
struct vlan_8021q_header* vptr;
vptr = (struct vlan_8021q_header*)payload;
......@@ -784,11 +802,31 @@ int main(int argc, char **argv) {
init_history();
ui_init();
if (options.no_curses) {
tui_init();
}
else {
ui_init();
}
pthread_create(&thread, NULL, (void*)&packet_loop, NULL);
ui_loop();
/* Keep the starting time (used for timed termination) */
first_timestamp = time(NULL);
if (options.no_curses) {
if (options.timed_output) {
while(!foad) {
sleep(1);
}
}
else {
tui_loop();
}
}
else {
ui_loop();
}
pthread_cancel(thread);
......
......@@ -30,7 +30,7 @@
options_t options;
char optstr[] = "+i:f:nNF:G:lhpbBPm:c:";
char optstr[] = "+i:f:nNF:G:lhpbBPm:c:s:tL:o:";
/* Global options. */
......@@ -55,7 +55,7 @@ config_enumeration_type sort_enumeration[] = {
{ "10s", OPTION_SORT_DIV2 },
{ "40s", OPTION_SORT_DIV3 },
{ "source", OPTION_SORT_SRC },
{ "destination", OPTION_SORT_SRC },
{ "destination", OPTION_SORT_DEST },
{ NULL, -1 }
};
......@@ -155,6 +155,9 @@ void options_set_defaults() {
options.max_bandwidth = 0; /* auto */
options.log_scale = 0;
options.bar_interval = 1;
options.timed_output = 0;
options.no_curses = 0;
options.num_lines = 10;
/* Figure out the name for the config file */
s = getenv("HOME");
......@@ -268,6 +271,18 @@ static void usage(FILE *fp) {
" -P show ports as well as hosts\n"
" -m limit sets the upper limit for the bandwidth scale\n"
" -c config file specifies an alternative configuration file\n"
" -t use text interface without ncurses\n"
"\n"
" Sorting orders:\n"
" -o 2s Sort by first column (2s traffic average)\n"
" -o 10s Sort by second column (10s traffic average) [default]\n"
" -o 40s Sort by third column (40s traffic average)\n"
" -o source Sort by source address\n"
" -o destination Sort by destination address\n"
"\n"
" The following options are only available in combination with -t\n"
" -s num print one single text output afer num seconds, then quit\n"
" -L num number of lines to print\n"
"\n"
"iftop, version " IFTOP_VERSION "\n"
"copyright (c) 2002 Paul Warren <pdw@ex-parrot.com> and contributors\n"
......@@ -332,6 +347,22 @@ void options_read_args(int argc, char **argv) {
config_set_string("use-bytes", "true");
break;
case 's':
config_set_string("timed-output", optarg);
break;
case 't':
config_set_string("no-curses", "true");
break;
case 'L':
config_set_string("num-lines", optarg);
break;
case 'o':
config_set_string("sort", optarg);
break;
case 'c':
xfree(options.config_file);
options.config_file = xstrdup(optarg);
......@@ -595,6 +626,9 @@ void options_make() {
options_config_get_enum("port-display", showports_enumeration, (int*)&options.showports);
options_config_get_string("screen-filter", &options.screenfilter);
options_config_get_bool("link-local", &options.link_local);
options_config_get_int("timed-output", &options.timed_output);
options_config_get_bool("no-curses", &options.no_curses);
options_config_get_int("num-lines", &options.num_lines);
options_config_get_net_filter();
options_config_get_net_filter6();
};
......@@ -56,6 +56,9 @@ typedef struct {
int aggregate_dest;
int paused;
int showhelp;
int timed_output;
int no_curses;
int num_lines;
int bandwidth_in_bytes;
option_sort_t sort;
......
/*
* ui_common.c
*
*
*/
#include <string.h>
#include <stdio.h>
#include "addr_hash.h"
#include "serv_hash.h"
#include "iftop.h"
#include "resolver.h"
#include "sorted_list.h"
#include "options.h"
#include "ui_common.h"
/* 2, 10 and 40 seconds */
int history_divs[HISTORY_DIVISIONS] = {1, 5, 20};
#define UNIT_DIVISIONS 4
char* unit_bits[UNIT_DIVISIONS] = { "b", "Kb", "Mb", "Gb"};
char* unit_bytes[UNIT_DIVISIONS] = { "B", "KB", "MB", "GB"};
extern hash_type* history;
extern int history_pos;
extern int history_len;
/*
* Compare two screen lines based on bandwidth. Start comparing from the
* specified column
*/
int screen_line_bandwidth_compare(host_pair_line* aa, host_pair_line* bb, int start_div) {
int i;
switch(options.linedisplay) {
case OPTION_LINEDISPLAY_ONE_LINE_SENT:
for(i = start_div; i < HISTORY_DIVISIONS; i++) {
if(aa->sent[i] != bb->sent[i]) {
return(aa->sent[i] < bb->sent[i]);
}
}
break;
case OPTION_LINEDISPLAY_ONE_LINE_RECV:
for(i = start_div; i < HISTORY_DIVISIONS; i++) {
if(aa->recv[i] != bb->recv[i]) {
return(aa->recv[i] < bb->recv[i]);
}
}
break;
case OPTION_LINEDISPLAY_TWO_LINE:
case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
/* fallback to the combined sent+recv that also act as fallback for sent/recv */
break;
}
for(i = start_div; i < HISTORY_DIVISIONS; i++) {
if(aa->recv[i] + aa->sent[i] != bb->recv[i] + bb->sent[i]) {
return(aa->recv[i] + aa->sent[i] < bb->recv[i] + bb->sent[i]);
}
}
return 1;
}
/*
* Compare two screen lines based on hostname / IP. Fall over to compare by
* bandwidth.
*/
int screen_line_host_compare(void* a, void* b, host_pair_line* aa, host_pair_line* bb) {
char hosta[HOSTNAME_LENGTH], hostb[HOSTNAME_LENGTH];
int r;
/* This isn't overly efficient because we resolve again before
display. */
if (options.dnsresolution) {
resolve(aa->ap.af, a, hosta, HOSTNAME_LENGTH);
resolve(bb->ap.af, b, hostb, HOSTNAME_LENGTH);
}
else {
inet_ntop(aa->ap.af, a, hosta, sizeof(hosta));
inet_ntop(bb->ap.af, b, hostb, sizeof(hostb));
}
r = strcmp(hosta, hostb);
if(r == 0) {
return screen_line_bandwidth_compare(aa, bb, 2);
}
else {
return (r > 0);
}
}
/*
* Compare two screen lines based on the sorting options selected.
*/
int screen_line_compare(void* a, void* b) {
host_pair_line* aa = (host_pair_line*)a;
host_pair_line* bb = (host_pair_line*)b;
if(options.sort == OPTION_SORT_DIV1) {
return screen_line_bandwidth_compare(aa, bb, 0);
}
else if(options.sort == OPTION_SORT_DIV2) {
return screen_line_bandwidth_compare(aa, bb, 1);
}
else if(options.sort == OPTION_SORT_DIV3) {
return screen_line_bandwidth_compare(aa, bb, 2);
}
else if(options.sort == OPTION_SORT_SRC) {
return screen_line_host_compare(&(aa->ap.src6), &(bb->ap.src6), aa, bb);
}
else if(options.sort == OPTION_SORT_DEST) {
return screen_line_host_compare(&(aa->ap.dst6), &(bb->ap.dst6), aa, bb);
}
return 1;
}
/*
* Format a data size in human-readable format
*/
void readable_size(float n, char* buf, int bsize, int ksize, int bytes) {
int i = 0;
float size = 1;
/* Convert to bits? */
if(bytes == 0) {
n *= 8;
}
while(1) {
if(n < size * 1000 || i >= UNIT_DIVISIONS - 1) {
snprintf(buf, bsize, " %4.0f%s", n / size, bytes ? unit_bytes[i] : unit_bits[i]);
break;
}
i++;
size *= ksize;
if(n < size * 10) {
snprintf(buf, bsize, " %4.2f%s", n / size, bytes ? unit_bytes[i] : unit_bits[i]);
break;
}
else if(n < size * 100) {
snprintf(buf, bsize, " %4.1f%s", n / size, bytes ? unit_bytes[i] : unit_bits[i]);
break;
}
}
}
int history_length(const int d) {
if (history_len < history_divs[d])
return history_len * RESOLUTION;
else
return history_divs[d] * RESOLUTION;
}
void screen_list_init() {
screen_list.compare = &screen_line_compare;
sorted_list_initialise(&screen_list);
}
void screen_list_clear() {
sorted_list_node* nn = NULL;
peaksent = peakrecv = peaktotal = 0;
while((nn = sorted_list_next_item(&screen_list, nn)) != NULL) {
free(nn->data);
}
sorted_list_destroy(&screen_list);
}
/*
* Calculate peaks and totals
*/
void calculate_totals() {
int i;
for(i = 0; i < HISTORY_LENGTH; i++) {
int j;
int ii = (HISTORY_LENGTH + history_pos - i) % HISTORY_LENGTH;
for(j = 0; j < HISTORY_DIVISIONS; j++) {
if(i < history_divs[j]) {
totals.recv[j] += history_totals.recv[ii];
totals.sent[j] += history_totals.sent[ii];
}
}
if(history_totals.recv[i] > peakrecv) {
peakrecv = history_totals.recv[i];
}
if(history_totals.sent[i] > peaksent) {
peaksent = history_totals.sent[i];
}
if(history_totals.recv[i] + history_totals.sent[i] > peaktotal) {
peaktotal = history_totals.recv[i] + history_totals.sent[i];
}
}
for(i = 0; i < HISTORY_DIVISIONS; i++) {
int t = history_length(i);
totals.recv[i] /= t;
totals.sent[i] /= t;
}
}
void make_screen_list() {
hash_node_type* n = NULL;
while(hash_next_item(screen_hash, &n) == HASH_STATUS_OK) {
host_pair_line* line = (host_pair_line*)n->rec;
int i;
for(i = 0; i < HISTORY_DIVISIONS; i++) {
line->recv[i] /= history_length(i);
line->sent[i] /= history_length(i);
}
/* Don't make a new, sorted screen list if order is frozen
*/
if(!options.freezeorder) {
sorted_list_insert(&screen_list, line);
}
}
}
/*
* Zeros all data in the screen hash, but does not remove items.
*/
void screen_hash_clear() {
hash_node_type* n = NULL;
while(hash_next_item(screen_hash, &n) == HASH_STATUS_OK) {
host_pair_line* hpl = (host_pair_line*)n->rec;
hpl->total_recv = hpl->total_sent = 0;
memset(hpl->recv, 0, sizeof(hpl->recv));
memset(hpl->sent, 0, sizeof(hpl->sent));
}
}
void analyse_data() {
hash_node_type* n = NULL;
if(options.paused == 1) {
return;
}
// Zero totals
memset(&totals, 0, sizeof totals);
if(options.freezeorder) {
screen_hash_clear();
}
else {
screen_list_clear();
hash_delete_all(screen_hash);
}
while(hash_next_item(history, &n) == HASH_STATUS_OK) {
history_type* d = (history_type*)n->rec;
host_pair_line* screen_line;
union {
host_pair_line **h_p_l_pp;
void **void_pp;
} u_screen_line = { &screen_line };
addr_pair ap;
int i;
int tsent, trecv;
tsent = trecv = 0;
ap = *(addr_pair*)n->key;
/* Aggregate hosts, if required */
if(options.aggregate_src) {
memset(&ap.src6, '\0', sizeof(ap.src6));
}
if(options.aggregate_dest) {
memset(&ap.dst6, '\0', sizeof(ap.dst6));
}
/* Aggregate ports, if required */
if(options.showports == OPTION_PORTS_DEST || options.showports == OPTION_PORTS_OFF) {
ap.src_port = 0;
}
if(options.showports == OPTION_PORTS_SRC || options.showports == OPTION_PORTS_OFF) {
ap.dst_port = 0;
}
if(options.showports == OPTION_PORTS_OFF) {
ap.protocol = 0;
}
if(hash_find(screen_hash, &ap, u_screen_line.void_pp) == HASH_STATUS_KEY_NOT_FOUND) {
screen_line = xcalloc(1, sizeof *screen_line);
hash_insert(screen_hash, &ap, screen_line);
screen_line->ap = ap;
}
screen_line->total_sent += d->total_sent;
screen_line->total_recv += d->total_recv;
for(i = 0; i < HISTORY_LENGTH; i++) {
int j;
int ii = (HISTORY_LENGTH + history_pos - i) % HISTORY_LENGTH;
for(j = 0; j < HISTORY_DIVISIONS; j++) {
if(i < history_divs[j]) {
screen_line->recv[j] += d->recv[ii];
screen_line->sent[j] += d->sent[ii];
}
}
}
}
make_screen_list();
calculate_totals();
}
void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, unsigned int protocol, int L, int unspecified_as_star) {
char hostname[HOSTNAME_LENGTH];
char service[HOSTNAME_LENGTH];
char* s_name;
union {
char **ch_pp;
void **void_pp;
} u_s_name = { &s_name };
ip_service skey;
int left;
if(IN6_IS_ADDR_UNSPECIFIED(addr) && unspecified_as_star) {
sprintf(hostname, " * ");
}
else {
if (options.dnsresolution)
resolve(af, addr, hostname, L);
else
inet_ntop(af, addr, hostname, sizeof(hostname));
}
left = strlen(hostname);
if(port != 0) {
skey.port = port;
skey.protocol = protocol;
if(options.portresolution && hash_find(service_hash, &skey, u_s_name.void_pp) == HASH_STATUS_OK) {
snprintf(service, HOSTNAME_LENGTH, ":%s", s_name);
}
else {
snprintf(service, HOSTNAME_LENGTH, ":%d", port);
}
}
else {
service[0] = '\0';
}
/* If we're showing IPv6 addresses with a port number, put them in square
* brackets. */
if(port == 0 || af == AF_INET || L < 2) {
sprintf(line, "%-*s", L, hostname);
}
else {
sprintf(line, "[%-.*s]", L-2, hostname);
left += 2;
}
if(left > (L - strlen(service))) {
left = L - strlen(service);
if(left < 0) {
left = 0;
}
}
sprintf(line + left, "%-*s", L-left, service);
}
/*
* ui_common.h
*
*
*/
#ifndef __UI_COMMON_H_ /* include guard */
#define __UI_COMMON_H_
#include <string.h>
#include <stdio.h>
#include "addr_hash.h"
#include "serv_hash.h"
#include "iftop.h"
#include "resolver.h"
#include "sorted_list.h"
#include "options.h"
#define HISTORY_DIVISIONS 3
#define UNIT_DIVISIONS 4
#define HOSTNAME_LENGTH 256
typedef struct host_pair_line_tag {
addr_pair ap;
double long total_recv;
double long total_sent;
double long recv[HISTORY_DIVISIONS];
double long sent[HISTORY_DIVISIONS];
} host_pair_line;
extern options_t options;
sorted_list_type screen_list;
host_pair_line totals;
int peaksent, peakrecv, peaktotal;
extern history_type history_totals;
hash_type* screen_hash;
hash_type* service_hash;
void analyse_data(void);
void screen_list_init(void);
void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, unsigned int protocol, int L, int unspecified_as_star);
void readable_size(float, char*, int, int, int);
#endif /* __UI_COMMON_H_ */
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!