Annotation of embedaddon/iftop/ui.c, revision 1.1.1.1
1.1 misho 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>