]> gitweb.fperrin.net Git - iftop.git/blob - ui.c
9de8525ea5e51dd10e3eb52e085626bb737b74be
[iftop.git] / ui.c
1 /*
2  * ui.c:
3  *
4  */
5
6 #include <sys/types.h>
7
8 #include <ctype.h>
9 #include <curses.h>
10 #include <errno.h>
11 #include <string.h>
12 #include <math.h>
13 #include <pthread.h>
14 #include <signal.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <netdb.h>
18
19 #include <sys/wait.h>
20
21 #include "addr_hash.h"
22 #include "serv_hash.h"
23 #include "iftop.h"
24 #include "resolver.h"
25 #include "sorted_list.h"
26 #include "options.h"
27 #include "screenfilter.h"
28
29 #define HOSTNAME_LENGTH 256
30
31 #define HISTORY_DIVISIONS   3
32
33 #define HELP_TIME 2
34
35 #define HELP_MESSAGE \
36 "Host display:                          General:\n"\
37 " n - toggle DNS host resolution         P - pause display\n"\
38 " s - toggle show source host            h - toggle this help display\n"\
39 " d - toggle show destination host       b - toggle bar graph display\n"\
40 " t - cycle line display mode            B - cycle bar graph average\n"\
41 "                                        T - toggle cumulative line totals\n"\
42 "Port display:                           j/k - scroll display\n"\
43 " N - toggle service resolution          f - edit filter code\n"\
44 " S - toggle show source port            l - set screen filter\n"\
45 " D - toggle show destination port       L - lin/log scales\n"\
46 " p - toggle port display                ! - shell command\n"\
47 "                                        q - quit\n"\
48 "Sorting:\n"\
49 " 1/2/3 - sort by 1st/2nd/3rd column\n"\
50 " < - sort by source name\n"\
51 " > - sort by dest name\n"\
52 " o - freeze current order\n"\
53 "\n"\
54 "iftop, version " IFTOP_VERSION 
55
56
57 /* 2, 10 and 40 seconds */
58 int history_divs[HISTORY_DIVISIONS] = {1, 5, 20};
59
60 #define UNIT_DIVISIONS 4
61 char* unit_bits[UNIT_DIVISIONS] =  { "b", "kb", "Mb", "Gb"};
62 char* unit_bytes[UNIT_DIVISIONS] =  { "B", "kB", "MB", "GB"};
63
64 typedef struct host_pair_line_tag {
65     addr_pair ap;
66     double long total_recv;
67     double long total_sent;
68     double long recv[HISTORY_DIVISIONS];
69     double long sent[HISTORY_DIVISIONS];
70 } host_pair_line;
71
72
73 extern hash_type* history;
74 extern int history_pos;
75 extern int history_len;
76
77 extern options_t options ;
78
79 void ui_finish();
80
81 hash_type* screen_hash;
82 hash_type* service_hash;
83 sorted_list_type screen_list;
84 host_pair_line totals;
85 int peaksent, peakrecv, peaktotal;
86
87 #define HELP_MSG_SIZE 80
88 int showhelphint = 0;
89 int persistenthelp = 0;
90 time_t helptimer = 0;
91 char helpmsg[HELP_MSG_SIZE];
92 int dontshowdisplay = 0;
93
94 /*
95  * Compare two screen lines based on bandwidth.  Start comparing from the 
96  * specified column
97  */
98 int screen_line_bandwidth_compare(host_pair_line* aa, host_pair_line* bb, int start_div) {
99     int i;
100     switch(options.linedisplay) {
101       case OPTION_LINEDISPLAY_ONE_LINE_SENT:
102         for(i = start_div; i < HISTORY_DIVISIONS; i++) {
103             if(aa->sent[i] != bb->sent[i]) {
104                 return(aa->sent[i] < bb->sent[i]);
105             }
106         }
107         break;
108       case OPTION_LINEDISPLAY_ONE_LINE_RECV:
109         for(i = start_div; i < HISTORY_DIVISIONS; i++) {
110             if(aa->recv[i] != bb->recv[i]) {
111                 return(aa->recv[i] < bb->recv[i]);
112             }
113         }
114         break;
115       case OPTION_LINEDISPLAY_TWO_LINE:
116       case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
117         /* fallback to the combined sent+recv that also act as fallback for sent/recv */
118         break;
119     }
120     for(i = start_div; i < HISTORY_DIVISIONS; i++) {
121         if(aa->recv[i] + aa->sent[i] != bb->recv[i] + bb->sent[i]) {
122             return(aa->recv[i] + aa->sent[i] < bb->recv[i] + bb->sent[i]);
123         }
124     }
125     return 1;
126 }
127
128 /*
129  * Compare two screen lines based on hostname / IP.  Fall over to compare by
130  * bandwidth.
131  */
132 int screen_line_host_compare(void* a, void* b, host_pair_line* aa, host_pair_line* bb) {
133     char hosta[HOSTNAME_LENGTH], hostb[HOSTNAME_LENGTH];
134     int r;
135
136     /* This isn't overly efficient because we resolve again before 
137        display. */
138     if (options.dnsresolution) {
139         resolve(aa->ap.af, a, hosta, HOSTNAME_LENGTH);
140         resolve(bb->ap.af, b, hostb, HOSTNAME_LENGTH);
141     }
142     else {
143         inet_ntop(aa->ap.af, a, hosta, sizeof(hosta));
144         inet_ntop(bb->ap.af, b, hostb, sizeof(hostb));
145     }
146
147     r = strcmp(hosta, hostb);
148
149     if(r == 0) {
150         return screen_line_bandwidth_compare(aa, bb, 2);
151     }
152     else {
153         return (r > 0);
154     }
155
156
157 }
158
159 int screen_line_compare(void* a, void* b) {
160     host_pair_line* aa = (host_pair_line*)a;
161     host_pair_line* bb = (host_pair_line*)b;
162     if(options.sort == OPTION_SORT_DIV1) {
163       return screen_line_bandwidth_compare(aa, bb, 0);
164     }
165     else if(options.sort == OPTION_SORT_DIV2) {
166       return screen_line_bandwidth_compare(aa, bb, 1);
167     }
168     else if(options.sort == OPTION_SORT_DIV3) {
169       return screen_line_bandwidth_compare(aa, bb, 2);
170     }
171     else if(options.sort == OPTION_SORT_SRC) {
172       return screen_line_host_compare(&(aa->ap.src6), &(bb->ap.src6), aa, bb);
173     }
174     else if(options.sort == OPTION_SORT_DEST) {
175       return screen_line_host_compare(&(aa->ap.dst6), &(bb->ap.dst6), aa, bb);
176     }
177
178     return 1;
179 }
180
181 void readable_size(float n, char* buf, int bsize, int ksize, int bytes) {
182
183     int i = 0;
184     float size = 1;
185
186     /* Convert to bits? */
187     if(bytes == 0) { 
188       n *= 8;
189     }
190
191     while(1) {
192       if(n < size * 1000 || i >= UNIT_DIVISIONS - 1) {
193         snprintf(buf, bsize, " %4.0f%s", n / size, bytes ? unit_bytes[i] : unit_bits[i]); 
194         break;
195       }
196       i++;
197       size *= ksize;
198       if(n < size * 10) {
199         snprintf(buf, bsize, " %4.2f%s", n / size, bytes ? unit_bytes[i] : unit_bits[i]); 
200         break;
201       }
202       else if(n < size * 100) {
203         snprintf(buf, bsize, " %4.1f%s", n / size, bytes ? unit_bytes[i] : unit_bits[i]); 
204         break;
205       }
206   }
207 }
208
209
210 /* Barchart scales. */
211 static struct {
212     int max, interval;
213 } scale[] = {
214         {      64000,     10 },     /* 64 kbit/s */
215         {     128000,     10 },
216         {     256000,     10 },
217         {    1000000,     10 },     /* 1 Mbit/s */
218         {   10000000,     10 },     
219         {  100000000,    100 },
220         { 1000000000,    100 }      /* 1 Gbit/s */
221     };
222 static int rateidx = 0, wantbiggerrate;
223
224 static int get_bar_interval(float bandwidth) {
225     int i = 10;
226     if(bandwidth > 100000000) {
227         i = 100;
228     }
229     return i;
230 }
231
232 static float get_max_bandwidth() {
233     float max;
234     if(options.max_bandwidth > 0) {
235         max = options.max_bandwidth;
236     }
237     else {
238         max = scale[rateidx].max;
239     }
240     return max;
241 }
242
243 /* rate in bits */
244 static int get_bar_length(const int rate) {
245     float l;
246     if (rate <= 0)
247         return 0;
248     if (rate > scale[rateidx].max)
249         wantbiggerrate = 1;
250     if(options.log_scale) {
251         l = log(rate) / log(get_max_bandwidth());
252     }
253     else {
254         l = rate / get_max_bandwidth();
255     }
256     return (l * COLS);
257 }
258
259 static void draw_bar_scale(int* y) {
260     float i;
261     float max,interval;
262     max = get_max_bandwidth();
263     interval = get_bar_interval(max);
264     if(options.showbars) {
265         float stop;
266         /* Draw bar graph scale on top of the window. */
267         move(*y, 0);
268         clrtoeol();
269         mvhline(*y + 1, 0, 0, COLS);
270         /* i in bytes */
271
272         if(options.log_scale) {
273             i = 1.25;
274             stop = max / 8;
275         }
276         else {
277             i = max / (5 * 8);
278             stop = max / 8;
279         }
280
281         /* for (i = 1.25; i * 8 <= max; i *= interval) { */
282         while(i <= stop) {
283             char s[40], *p;
284             int x;
285             /* This 1024 vs 1000 stuff is just plain evil */
286             readable_size(i, s, sizeof s, options.log_scale ? 1000 : 1024, options.bandwidth_in_bytes);
287             p = s + strspn(s, " ");
288             x = get_bar_length(i * 8);
289             mvaddch(*y + 1, x, ACS_BTEE);
290             if (x + strlen(p) >= COLS)
291                 x = COLS - strlen(p);
292             mvaddstr(*y, x, p);
293
294             if(options.log_scale) {
295                 i *= interval;
296             }
297             else {
298                 i += max / (5 * 8);
299             }
300         }
301         mvaddch(*y + 1, 0, ACS_LLCORNER);
302         *y += 2;
303     }
304     else {
305         mvhline(*y, 0, 0, COLS);
306         *y += 1;
307     }
308 }
309
310 int history_length(const int d) {
311     if (history_len < history_divs[d])
312         return history_len * RESOLUTION;
313     else
314         return history_divs[d] * RESOLUTION;
315 }
316
317 void draw_line_total(float sent, float recv, int y, int x, option_linedisplay_t linedisplay, int bytes) {
318     char buf[10];
319     float n;
320     switch(linedisplay) {
321         case OPTION_LINEDISPLAY_TWO_LINE:
322           draw_line_total(sent, recv, y, x, OPTION_LINEDISPLAY_ONE_LINE_SENT, bytes);
323           draw_line_total(sent, recv, y+1, x, OPTION_LINEDISPLAY_ONE_LINE_RECV, bytes);
324           break;
325         case OPTION_LINEDISPLAY_ONE_LINE_SENT:
326           n = sent;
327           break;
328         case OPTION_LINEDISPLAY_ONE_LINE_RECV:
329           n = recv;
330           break;
331         case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
332           n = recv + sent;
333           break;
334     }
335     if(linedisplay != OPTION_LINEDISPLAY_TWO_LINE) {
336         readable_size(n, buf, 10, 1024, bytes);
337         mvaddstr(y, x, buf);
338     }
339 }
340
341 void draw_bar(float n, int y) {
342     int L;
343     mvchgat(y, 0, -1, A_NORMAL, 0, NULL);
344     L = get_bar_length(8 * n);
345     if (L > 0)
346         mvchgat(y, 0, L + 1, A_REVERSE, 0, NULL);
347 }
348
349 void draw_line_totals(int y, host_pair_line* line, option_linedisplay_t linedisplay) {
350     int j;
351     int x = (COLS - 8 * HISTORY_DIVISIONS);
352
353     for(j = 0; j < HISTORY_DIVISIONS; j++) {
354         draw_line_total(line->sent[j], line->recv[j], y, x, linedisplay, options.bandwidth_in_bytes);
355         x += 8;
356     }
357     
358     if(options.showbars) {
359       switch(linedisplay) {
360         case OPTION_LINEDISPLAY_TWO_LINE:
361           draw_bar(line->sent[options.bar_interval],y);
362           draw_bar(line->recv[options.bar_interval],y+1);
363           break;
364         case OPTION_LINEDISPLAY_ONE_LINE_SENT:
365           draw_bar(line->sent[options.bar_interval],y);
366           break;
367         case OPTION_LINEDISPLAY_ONE_LINE_RECV:
368           draw_bar(line->recv[options.bar_interval],y);
369           break;
370         case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
371           draw_bar(line->recv[options.bar_interval] + line->sent[options.bar_interval],y);
372           break;
373       }
374     }
375 }
376
377 void draw_totals(host_pair_line* totals) {
378     /* Draw rule */
379     int y = LINES - 4;
380     int j;
381     char buf[10];
382     int x = (COLS - 8 * HISTORY_DIVISIONS);
383     y++;
384     draw_line_totals(y, totals, OPTION_LINEDISPLAY_TWO_LINE);
385     y += 2;
386     for(j = 0; j < HISTORY_DIVISIONS; j++) {
387         readable_size((totals->sent[j] + totals->recv[j]) , buf, 10, 1024, options.bandwidth_in_bytes);
388         mvaddstr(y, x, buf);
389         x += 8;
390     }
391 }
392
393 extern history_type history_totals;
394
395 void screen_list_init() {
396     screen_list.compare = &screen_line_compare;
397     sorted_list_initialise(&screen_list);
398 }
399
400 void screen_list_clear() {
401     sorted_list_node* nn = NULL;
402     peaksent = peakrecv = peaktotal = 0;
403     while((nn = sorted_list_next_item(&screen_list, nn)) != NULL) {
404         free(nn->data);
405     }
406     sorted_list_destroy(&screen_list);
407 }
408
409 void calculate_totals() {
410     int i;
411
412     /**
413      * Calculate peaks and totals
414      */
415     for(i = 0; i < HISTORY_LENGTH; i++) {
416         int j;
417         int ii = (HISTORY_LENGTH + history_pos - i) % HISTORY_LENGTH;
418
419         for(j = 0; j < HISTORY_DIVISIONS; j++) {
420             if(i < history_divs[j]) {
421                 totals.recv[j] += history_totals.recv[ii];
422                 totals.sent[j] += history_totals.sent[ii];
423             }
424         }
425
426         if(history_totals.recv[i] > peakrecv) {
427             peakrecv = history_totals.recv[i];
428         }
429         if(history_totals.sent[i] > peaksent) {
430             peaksent = history_totals.sent[i];
431         }
432         if(history_totals.recv[i] + history_totals.sent[i] > peaktotal) {
433             peaktotal = history_totals.recv[i] + history_totals.sent[i];        
434         }
435     }
436     for(i = 0; i < HISTORY_DIVISIONS; i++) {
437       int t = history_length(i);
438       totals.recv[i] /= t;
439       totals.sent[i] /= t;
440     }
441 }
442
443 void make_screen_list() {
444     hash_node_type* n = NULL;
445     while(hash_next_item(screen_hash, &n) == HASH_STATUS_OK) {
446         host_pair_line* line = (host_pair_line*)n->rec;
447         int i;
448         for(i = 0; i < HISTORY_DIVISIONS; i++) {
449           line->recv[i] /= history_length(i);
450           line->sent[i] /= history_length(i);
451         }
452
453         /* Don't make a new, sorted screen list if order is frozen
454          */
455         if(!options.freezeorder) {
456             sorted_list_insert(&screen_list, line);
457         } 
458          
459     }
460 }
461
462 /*
463  * Zeros all data in the screen hash, but does not remove items.
464  */
465 void screen_hash_clear() {
466     hash_node_type* n = NULL;
467     while(hash_next_item(screen_hash, &n) == HASH_STATUS_OK) {
468         host_pair_line* hpl = (host_pair_line*)n->rec;
469         hpl->total_recv = hpl->total_sent = 0;
470         memset(hpl->recv, 0, sizeof(hpl->recv));
471         memset(hpl->sent, 0, sizeof(hpl->sent));
472     }
473 }
474
475 void analyse_data() {
476     hash_node_type* n = NULL;
477
478     if(options.paused == 1) {
479       return;
480     }
481
482     // Zero totals
483     memset(&totals, 0, sizeof totals);
484
485     if(options.freezeorder) {
486       screen_hash_clear();
487     }
488     else {
489       screen_list_clear();
490       hash_delete_all(screen_hash);
491     }
492
493     while(hash_next_item(history, &n) == HASH_STATUS_OK) {
494         history_type* d = (history_type*)n->rec;
495         host_pair_line* screen_line;
496         union {
497             host_pair_line **h_p_l_pp;
498             void **void_pp;
499         } u_screen_line = { &screen_line };
500         addr_pair ap;
501         int i;
502         int tsent, trecv;
503         tsent = trecv = 0;
504
505
506         ap = *(addr_pair*)n->key;
507
508         /* Aggregate hosts, if required */
509         if(options.aggregate_src) {
510             memset(&ap.src6, '\0', sizeof(ap.src6));
511         }
512         if(options.aggregate_dest) {
513             memset(&ap.dst6, '\0', sizeof(ap.dst6));
514         }
515
516         /* Aggregate ports, if required */
517         if(options.showports == OPTION_PORTS_DEST || options.showports == OPTION_PORTS_OFF) {
518             ap.src_port = 0;
519         }
520         if(options.showports == OPTION_PORTS_SRC || options.showports == OPTION_PORTS_OFF) {
521             ap.dst_port = 0;
522         }
523         if(options.showports == OPTION_PORTS_OFF) {
524             ap.protocol = 0;
525         }
526
527         
528         if(hash_find(screen_hash, &ap, u_screen_line.void_pp) == HASH_STATUS_KEY_NOT_FOUND) {
529             screen_line = xcalloc(1, sizeof *screen_line);
530             hash_insert(screen_hash, &ap, screen_line);
531             screen_line->ap = ap;
532         }
533         
534         screen_line->total_sent += d->total_sent;
535         screen_line->total_recv += d->total_recv;
536
537         for(i = 0; i < HISTORY_LENGTH; i++) {
538             int j;
539             int ii = (HISTORY_LENGTH + history_pos - i) % HISTORY_LENGTH;
540
541             for(j = 0; j < HISTORY_DIVISIONS; j++) {
542                 if(i < history_divs[j]) {
543                     screen_line->recv[j] += d->recv[ii];
544                     screen_line->sent[j] += d->sent[ii];
545                 }
546             }
547         }
548
549     }
550
551     make_screen_list();
552
553     
554     calculate_totals();
555
556 }
557
558 void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, unsigned int protocol, int L) {
559     char hostname[HOSTNAME_LENGTH];
560     char service[HOSTNAME_LENGTH];
561     char* s_name;
562     union {
563         char **ch_pp;
564         void **void_pp;
565     } u_s_name = { &s_name };
566
567     ip_service skey;
568     int left;
569
570     if(IN6_IS_ADDR_UNSPECIFIED(addr)) {
571         sprintf(hostname, " * ");
572     }
573     else {
574         if (options.dnsresolution)
575             resolve(af, addr, hostname, L);
576         else
577             inet_ntop(af, addr, hostname, sizeof(hostname));
578     }
579     left = strlen(hostname);
580
581     if(port != 0) {
582       skey.port = port;
583       skey.protocol = protocol;
584       if(options.portresolution && hash_find(service_hash, &skey, u_s_name.void_pp) == HASH_STATUS_OK) {
585         snprintf(service, HOSTNAME_LENGTH, ":%s", s_name);
586       }
587       else {
588         snprintf(service, HOSTNAME_LENGTH, ":%d", port);
589       }
590     }
591     else {
592       service[0] = '\0';
593     }
594
595
596     sprintf(line, "%-*s", L, hostname);
597     if(left > (L - strlen(service))) {
598         left = L - strlen(service);
599         if(left < 0) {
600            left = 0;
601         }
602     }
603     sprintf(line + left, "%-*s", L-left, service);
604 }
605
606
607
608 void ui_print() {
609     sorted_list_node* nn = NULL;
610     char host1[HOSTNAME_LENGTH], host2[HOSTNAME_LENGTH];
611     static char *line;
612     static int lcols;
613     int y = 0;
614
615     if (dontshowdisplay)
616         return;
617
618     if (!line || lcols != COLS) {
619         xfree(line);
620         line = calloc(COLS + 1, 1);
621     }
622
623     /* 
624      * erase() is faster than clear().  Dunno why we switched to 
625      * clear() -pdw 24/10/02
626      */
627     erase();
628
629     draw_bar_scale(&y);
630
631     if(options.showhelp) {
632       mvaddstr(y,0,HELP_MESSAGE);
633     }
634     else {
635       int i = 0;
636
637       while(i < options.screen_offset && ((nn = sorted_list_next_item(&screen_list, nn)) != NULL)) {
638         i++;
639       }
640
641       /* Screen layout: we have 2 * HISTORY_DIVISIONS 6-character wide history
642        * items, and so can use COLS - 12 * HISTORY_DIVISIONS to print the two
643        * host names. */
644
645       if(i == 0 || nn != NULL) {
646         while((y < LINES - 5) && ((nn = sorted_list_next_item(&screen_list, nn)) != NULL)) {
647             int x = 0, L;
648
649
650             host_pair_line* screen_line = (host_pair_line*)nn->data;
651
652             if(y < LINES - 5) {
653                 L = (COLS - 8 * HISTORY_DIVISIONS - 4) / 2;
654                 if(options.show_totals) {
655                     L -= 4;    
656                 }
657                 if(L > HOSTNAME_LENGTH) {
658                     L = HOSTNAME_LENGTH;
659                 }
660
661                 sprint_host(host1, screen_line->ap.af,
662                             &(screen_line->ap.src6),
663                             screen_line->ap.src_port,
664                             screen_line->ap.protocol, L);
665                 sprint_host(host2, screen_line->ap.af,
666                             &(screen_line->ap.dst6),
667                             screen_line->ap.dst_port,
668                             screen_line->ap.protocol, L);
669
670                 if(!screen_filter_match(host1) && !screen_filter_match(host2)) {
671                   continue;
672                 }
673
674                 mvaddstr(y, x, host1);
675                 x += L;
676
677                 switch(options.linedisplay) {
678                   case OPTION_LINEDISPLAY_TWO_LINE:
679                     mvaddstr(y, x, " => ");
680                     mvaddstr(y+1, x, " <= ");
681                     break;
682                   case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
683                     mvaddstr(y, x, "<=> ");
684                     break;
685                   case OPTION_LINEDISPLAY_ONE_LINE_SENT:
686                     mvaddstr(y, x, " => ");
687                     break;
688                   case OPTION_LINEDISPLAY_ONE_LINE_RECV:
689                     mvaddstr(y, x, " <= ");
690                     break;
691                 }
692
693                 x += 4;
694
695
696                 mvaddstr(y, x, host2);
697                 
698                 if(options.show_totals) {
699                     draw_line_total(screen_line->total_sent, screen_line->total_recv, y, COLS - 8 * (HISTORY_DIVISIONS + 1), options.linedisplay, 1);
700                 }
701
702                 draw_line_totals(y, screen_line, options.linedisplay);
703
704             }
705             if(options.linedisplay == OPTION_LINEDISPLAY_TWO_LINE) {
706               y += 2;
707             }
708             else {
709               y += 1;
710             }
711         }
712       }
713     }
714
715
716     y = LINES - 3;
717     
718     mvhline(y-1, 0, 0, COLS);
719
720     mvaddstr(y, 0, "TX: ");
721     mvaddstr(y+1, 0, "RX: ");
722     mvaddstr(y+2, 0, "TOTAL: ");
723
724     /* Cummulative totals */
725     mvaddstr(y, 16, "cum: ");
726
727     readable_size(history_totals.total_sent, line, 10, 1024, 1);
728     mvaddstr(y, 22, line);
729
730     readable_size(history_totals.total_recv, line, 10, 1024, 1);
731     mvaddstr(y+1, 22, line);
732
733     readable_size(history_totals.total_recv + history_totals.total_sent, line, 10, 1024, 1);
734     mvaddstr(y+2, 22, line);
735
736     /* peak traffic */
737     mvaddstr(y, 32, "peak: ");
738
739     readable_size(peaksent / RESOLUTION, line, 10, 1024, options.bandwidth_in_bytes);
740     mvaddstr(y, 39, line);
741
742     readable_size(peakrecv / RESOLUTION, line, 10, 1024, options.bandwidth_in_bytes);
743     mvaddstr(y+1, 39, line);
744
745     readable_size(peaktotal / RESOLUTION, line, 10, 1024, options.bandwidth_in_bytes);
746     mvaddstr(y+2, 39, line);
747
748     mvaddstr(y, COLS - 8 * HISTORY_DIVISIONS - 8, "rates:");
749
750     draw_totals(&totals);
751
752
753     if(showhelphint) {
754       mvaddstr(0, 0, " ");
755       mvaddstr(0, 1, helpmsg);
756       mvaddstr(0, 1 + strlen(helpmsg), " ");
757       mvchgat(0, 0, strlen(helpmsg) + 2, A_REVERSE, 0, NULL);
758     }
759     move(LINES - 1, COLS - 1);
760     
761     refresh();
762
763     /* Bar chart auto scale */
764     if (wantbiggerrate && options.max_bandwidth == 0) {
765         ++rateidx;
766         wantbiggerrate = 0;
767     }
768 }
769
770 void ui_tick(int print) {
771   if(print) {
772     ui_print();
773   }
774   else if(showhelphint && (time(NULL) - helptimer > HELP_TIME) && !persistenthelp) {
775     showhelphint = 0;
776     ui_print();
777   }
778 }
779
780 void ui_curses_init() {
781     (void) initscr();      /* initialize the curses library */
782     keypad(stdscr, TRUE);  /* enable keyboard mapping */
783     (void) nonl();         /* tell curses not to do NL->CR/NL on output */
784     (void) cbreak();       /* take input chars one at a time, no wait for \n */
785     (void) noecho();       /* don't echo input */
786     halfdelay(2);
787 }
788
789 void showhelp(const char * s) {
790   strncpy(helpmsg, s, HELP_MSG_SIZE);
791   showhelphint = 1;
792   helptimer = time(NULL);
793   persistenthelp = 0;
794   tick(1);
795 }
796
797 void ui_init() {
798     char msg[20];
799     ui_curses_init();
800     
801     erase();
802
803     screen_list_init();
804     screen_hash = addr_hash_create();
805
806     service_hash = serv_hash_create();
807     serv_hash_initialise(service_hash);
808
809     snprintf(msg,20,"Listening on %s",options.interface);
810     showhelp(msg);
811
812
813 }
814
815
816 void showportstatus() {
817   if(options.showports == OPTION_PORTS_ON) {
818     showhelp("Port display ON");
819   }
820   else if(options.showports == OPTION_PORTS_OFF) {
821     showhelp("Port display OFF");
822   }
823   else if(options.showports == OPTION_PORTS_DEST) {
824     showhelp("Port display DEST");
825   }
826   else if(options.showports == OPTION_PORTS_SRC) {
827     showhelp("Port display SOURCE");
828   }
829 }
830
831
832 void ui_loop() {
833     /* in edline.c */
834     char *edline(int linenum, const char *prompt, const char *initial);
835     /* in iftop.c */
836     char *set_filter_code(const char *filter);
837
838     extern sig_atomic_t foad;
839
840     while(foad == 0) {
841         int i;
842         i = getch();
843         switch (i) {
844             case 'q':
845                 foad = 1;
846                 break;
847
848             case 'n':
849                 if(options.dnsresolution) {
850                     options.dnsresolution = 0;
851                     showhelp("DNS resolution off");
852                 }
853                 else {
854                     options.dnsresolution = 1;
855                     showhelp("DNS resolution on");
856                 }
857                 tick(1);
858                 break;
859
860             case 'N':
861                 if(options.portresolution) {
862                     options.portresolution = 0;
863                     showhelp("Port resolution off");
864                 }
865                 else {
866                     options.portresolution = 1;
867                     showhelp("Port resolution on");
868                 }
869                 tick(1);
870                 break;
871
872             case 'h':
873             case '?':
874                 options.showhelp = !options.showhelp;
875                 tick(1);
876                 break;
877
878             case 'b':
879                 if(options.showbars) {
880                     options.showbars = 0;
881                     showhelp("Bars off");
882                 }
883                 else {
884                     options.showbars = 1;
885                     showhelp("Bars on");
886                 }
887                 tick(1);
888                 break;
889
890             case 'B':
891                 options.bar_interval = (options.bar_interval + 1) % 3;
892                 if(options.bar_interval == 0) {
893                     showhelp("Bars show 2s average");
894                 }
895                 else if(options.bar_interval == 1) { 
896                     showhelp("Bars show 10s average");
897                 }
898                 else {
899                     showhelp("Bars show 40s average");
900                 }
901                 ui_print();
902                 break;
903             case 's':
904                 if(options.aggregate_src) {
905                     options.aggregate_src = 0;
906                     showhelp("Show source host");
907                 }
908                 else {
909                     options.aggregate_src = 1;
910                     showhelp("Hide source host");
911                 }
912                 break;
913             case 'd':
914                 if(options.aggregate_dest) {
915                     options.aggregate_dest = 0;
916                     showhelp("Show dest host");
917                 }
918                 else {
919                     options.aggregate_dest = 1;
920                     showhelp("Hide dest host");
921                 }
922                 break;
923             case 'S':
924                 /* Show source ports */
925                 if(options.showports == OPTION_PORTS_OFF) {
926                   options.showports = OPTION_PORTS_SRC;
927                 }
928                 else if(options.showports == OPTION_PORTS_DEST) {
929                   options.showports = OPTION_PORTS_ON;
930                 }
931                 else if(options.showports == OPTION_PORTS_ON) {
932                   options.showports = OPTION_PORTS_DEST;
933                 }
934                 else {
935                   options.showports = OPTION_PORTS_OFF;
936                 }
937                 showportstatus();
938                 break;
939             case 'D':
940                 /* Show dest ports */
941                 if(options.showports == OPTION_PORTS_OFF) {
942                   options.showports = OPTION_PORTS_DEST;
943                 }
944                 else if(options.showports == OPTION_PORTS_SRC) {
945                   options.showports = OPTION_PORTS_ON;
946                 }
947                 else if(options.showports == OPTION_PORTS_ON) {
948                   options.showports = OPTION_PORTS_SRC;
949                 }
950                 else {
951                   options.showports = OPTION_PORTS_OFF;
952                 }
953                 showportstatus();
954                 break;
955             case 'p':
956                 options.showports = 
957                   (options.showports == OPTION_PORTS_OFF)
958                   ? OPTION_PORTS_ON
959                   : OPTION_PORTS_OFF;
960                 showportstatus();
961                 // Don't tick here, otherwise we get a bogus display
962                 break;
963             case 'P':
964                 if(options.paused) {
965                     options.paused = 0;
966                     showhelp("Display unpaused");
967                 }
968                 else {
969                     options.paused = 1;
970                     showhelp("Display paused");
971                     persistenthelp = 1;
972                 }
973                 break;
974             case 'o':
975                 if(options.freezeorder) {
976                     options.freezeorder = 0;
977                     showhelp("Order unfrozen");
978                 }
979                 else {
980                     options.freezeorder = 1;
981                     showhelp("Order frozen");
982                     persistenthelp = 1;
983                 }
984                 break;
985             case '1':
986                 options.sort = OPTION_SORT_DIV1;
987                 showhelp("Sort by col 1");
988                 break;
989             case '2':
990                 options.sort = OPTION_SORT_DIV2;
991                 showhelp("Sort by col 2");
992                 break;
993             case '3':
994                 options.sort = OPTION_SORT_DIV3;
995                 showhelp("Sort by col 3");
996                 break;
997             case '<':
998                 options.sort = OPTION_SORT_SRC;
999                 showhelp("Sort by source");
1000                 break;
1001             case '>':
1002                 options.sort = OPTION_SORT_DEST;
1003                 showhelp("Sort by dest");
1004                 break;
1005             case 'j':
1006                 options.screen_offset++;
1007                 ui_print();
1008                 break;
1009             case 'k':
1010                 if(options.screen_offset > 0) {
1011                   options.screen_offset--;
1012                   ui_print();
1013                 }
1014                 break;
1015             case 't':
1016                 options.linedisplay = (options.linedisplay + 1) % 4;
1017                 switch(options.linedisplay) {
1018                   case OPTION_LINEDISPLAY_TWO_LINE:
1019                     showhelp("Two lines per host");
1020                     break;
1021                   case OPTION_LINEDISPLAY_ONE_LINE_SENT:
1022                     showhelp("Sent traffic only");
1023                     break;
1024                   case OPTION_LINEDISPLAY_ONE_LINE_RECV:
1025                     showhelp("Received traffic only");
1026                     break;
1027                   case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
1028                     showhelp("One line per host");
1029                     break;
1030                 }
1031                 ui_print();
1032                 break;
1033             case 'f': {
1034                 char *s;
1035                 dontshowdisplay = 1;
1036                 if ((s = edline(0, "Net filter", options.filtercode))) {
1037                     char *m;
1038                     if (s[strspn(s, " \t")] == 0) {
1039                         /* Empty filter; set to NULL. */
1040                         xfree(s);
1041                         s = NULL;
1042                     }
1043                     if (!(m = set_filter_code(s))) {
1044                         xfree(options.filtercode);
1045                         options.filtercode = s;
1046                         /* -lpcap will write junk to stderr; we do our best to
1047                          * erase it.... */
1048                         move(COLS - 1, LINES - 1);
1049                         wrefresh(curscr);
1050                         showhelp("Installed new filter");
1051                     } else {
1052                         showhelp(m);
1053                         xfree(s);
1054                     }
1055                 }
1056                 dontshowdisplay = 0;
1057                 ui_print();
1058                 break;
1059             }
1060             case 'l': {
1061 #ifdef HAVE_REGCOMP
1062                 char *s;
1063                 dontshowdisplay = 1;
1064                 if ((s = edline(0, "Screen filter", options.screenfilter))) {
1065                     if(!screen_filter_set(s)) {
1066                         showhelp("Invalid regexp");
1067                     }
1068                 }
1069                 dontshowdisplay = 0;
1070                 ui_print();
1071 #else
1072                 showhelp("Sorry, screen filters not supported on this platform")
1073 #endif
1074                 break;
1075             }
1076             case '!': {
1077 #ifdef ALLOW_SUBSHELL
1078                 char *s;
1079                 dontshowdisplay = 1;
1080                 if ((s = edline(0, "Command", "")) && s[strspn(s, " \t")]) {
1081                     int i, dowait = 0;
1082                     erase();
1083                     refresh();
1084                     endwin();
1085                     errno = 0;
1086                     i = system(s);
1087                     if (i == -1 || (i == 127 && errno != 0)) {
1088                         fprintf(stderr, "system: %s: %s\n", s, strerror(errno));
1089                         dowait = 1;
1090                     } else if (i != 0) {
1091                         if (WIFEXITED(i))
1092                             fprintf(stderr, "%s: exited with code %d\n", s, WEXITSTATUS(i));
1093                         else if (WIFSIGNALED(i))
1094                             fprintf(stderr, "%s: killed by signal %d\n", s, WTERMSIG(i));
1095                         dowait = 1;
1096                     }
1097                     ui_curses_init();
1098                     if (dowait) {
1099                         fprintf(stderr, "Press any key....");
1100                         while (getch() == ERR);
1101                     }
1102                     erase();
1103                     xfree(s);
1104                 }
1105                 dontshowdisplay = 0;
1106 #else
1107                 showhelp("Sorry, subshells have been disabled.");
1108 #endif
1109                 break;
1110             }
1111             case 'T':
1112                 options.show_totals = !options.show_totals;
1113                 if(options.show_totals) {
1114                     showhelp("Show cumulative totals");
1115                 }
1116                 else {
1117                     showhelp("Hide cumulative totals");
1118                 }
1119                 ui_print();
1120                 break;
1121             case 'L':
1122                 options.log_scale = !options.log_scale;
1123                 showhelp(options.log_scale ? "Logarithmic scale" : "Linear scale");
1124                 ui_print();
1125                 break;
1126             case KEY_CLEAR:
1127             case 12:    /* ^L */
1128                 wrefresh(curscr);
1129                 break;
1130             case ERR:
1131                 break;
1132             default:
1133                 showhelp("Press H or ? for help");
1134                 break;
1135         }
1136         tick(0);
1137     }
1138 }
1139
1140 void ui_finish() {
1141     endwin();
1142 }