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