File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / iftop / ui.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 16:57:34 2012 UTC (12 years, 4 months ago) by misho
Branches: iftop, MAIN
CVS tags: v0_17p0, v0_17, HEAD
iftop

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>