]> gitweb.fperrin.net Git - iftop.git/blob - ui_common.c
Import iftop-1.0pre4
[iftop.git] / ui_common.c
1 /*
2  * ui_common.c
3  *
4  *
5  */
6
7 #include <string.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include "addr_hash.h"
12 #include "serv_hash.h"
13 #include "iftop.h"
14 #include "resolver.h"
15 #include "sorted_list.h"
16 #include "options.h"
17
18 #include "ui_common.h"
19
20 /* 2, 10 and 40 seconds */
21 int history_divs[HISTORY_DIVISIONS] = {1, 5, 20};
22
23 #define UNIT_DIVISIONS 4
24 char* unit_bits[UNIT_DIVISIONS] =  { "b", "Kb", "Mb", "Gb"};
25 char* unit_bytes[UNIT_DIVISIONS] =  { "B", "KB", "MB", "GB"};
26
27 extern hash_type* history;
28 extern int history_pos;
29 extern int history_len;
30
31 /*
32  * Compare two screen lines based on bandwidth.  Start comparing from the 
33  * specified column
34  */
35 int screen_line_bandwidth_compare(host_pair_line* aa, host_pair_line* bb, int start_div) {
36     int i;
37     switch(options.linedisplay) {
38       case OPTION_LINEDISPLAY_ONE_LINE_SENT:
39         for(i = start_div; i < HISTORY_DIVISIONS; i++) {
40             if(aa->sent[i] != bb->sent[i]) {
41                 return(aa->sent[i] < bb->sent[i]);
42             }
43         }
44         break;
45       case OPTION_LINEDISPLAY_ONE_LINE_RECV:
46         for(i = start_div; i < HISTORY_DIVISIONS; i++) {
47             if(aa->recv[i] != bb->recv[i]) {
48                 return(aa->recv[i] < bb->recv[i]);
49             }
50         }
51         break;
52       case OPTION_LINEDISPLAY_TWO_LINE:
53       case OPTION_LINEDISPLAY_ONE_LINE_BOTH:
54         /* fallback to the combined sent+recv that also act as fallback for sent/recv */
55         break;
56     }
57     for(i = start_div; i < HISTORY_DIVISIONS; i++) {
58         if(aa->recv[i] + aa->sent[i] != bb->recv[i] + bb->sent[i]) {
59             return(aa->recv[i] + aa->sent[i] < bb->recv[i] + bb->sent[i]);
60         }
61     }
62     return 1;
63 }
64
65 /*
66  * Compare two screen lines based on hostname / IP.  Fall over to compare by
67  * bandwidth.
68  */
69 int screen_line_host_compare(void* a, void* b, host_pair_line* aa, host_pair_line* bb) {
70     char hosta[HOSTNAME_LENGTH], hostb[HOSTNAME_LENGTH];
71     int r;
72
73     /* This isn't overly efficient because we resolve again before 
74        display. */
75     if (options.dnsresolution) {
76         resolve(aa->ap.af, a, hosta, HOSTNAME_LENGTH);
77         resolve(bb->ap.af, b, hostb, HOSTNAME_LENGTH);
78     }
79     else {
80         inet_ntop(aa->ap.af, a, hosta, sizeof(hosta));
81         inet_ntop(bb->ap.af, b, hostb, sizeof(hostb));
82     }
83
84     r = strcmp(hosta, hostb);
85
86     if(r == 0) {
87         return screen_line_bandwidth_compare(aa, bb, 2);
88     }
89     else {
90         return (r > 0);
91     }
92
93
94 }
95
96 /*
97  * Compare two screen lines based on the sorting options selected.
98  */
99 int screen_line_compare(void* a, void* b) {
100     host_pair_line* aa = (host_pair_line*)a;
101     host_pair_line* bb = (host_pair_line*)b;
102     if(options.sort == OPTION_SORT_DIV1) {
103       return screen_line_bandwidth_compare(aa, bb, 0);
104     }
105     else if(options.sort == OPTION_SORT_DIV2) {
106       return screen_line_bandwidth_compare(aa, bb, 1);
107     }
108     else if(options.sort == OPTION_SORT_DIV3) {
109       return screen_line_bandwidth_compare(aa, bb, 2);
110     }
111     else if(options.sort == OPTION_SORT_SRC) {
112       return screen_line_host_compare(&(aa->ap.src6), &(bb->ap.src6), aa, bb);
113     }
114     else if(options.sort == OPTION_SORT_DEST) {
115       return screen_line_host_compare(&(aa->ap.dst6), &(bb->ap.dst6), aa, bb);
116     }
117
118     return 1;
119 }
120
121 /*
122  * Format a data size in human-readable format
123  */
124 void readable_size(float n, char* buf, int bsize, int ksize, int bytes) {
125
126     int i = 0;
127     float size = 1;
128
129     /* Convert to bits? */
130     if(bytes == 0) { 
131       n *= 8;
132     }
133
134     while(1) {
135       if(n < size * 1000 || i >= UNIT_DIVISIONS - 1) {
136         snprintf(buf, bsize, " %4.0f%s", n / size, bytes ? unit_bytes[i] : unit_bits[i]); 
137         break;
138       }
139       i++;
140       size *= ksize;
141       if(n < size * 10) {
142         snprintf(buf, bsize, " %4.2f%s", n / size, bytes ? unit_bytes[i] : unit_bits[i]); 
143         break;
144       }
145       else if(n < size * 100) {
146         snprintf(buf, bsize, " %4.1f%s", n / size, bytes ? unit_bytes[i] : unit_bits[i]); 
147         break;
148       }
149   }
150 }
151
152 int history_length(const int d) {
153     if (history_len < history_divs[d])
154         return history_len * RESOLUTION;
155     else
156         return history_divs[d] * RESOLUTION;
157 }
158
159 void screen_list_init() {
160     screen_list.compare = &screen_line_compare;
161     sorted_list_initialise(&screen_list);
162 }
163
164 void screen_list_clear() {
165     sorted_list_node* nn = NULL;
166     peaksent = peakrecv = peaktotal = 0;
167     while((nn = sorted_list_next_item(&screen_list, nn)) != NULL) {
168         free(nn->data);
169     }
170     sorted_list_destroy(&screen_list);
171 }
172
173 /*
174  * Calculate peaks and totals
175  */
176 void calculate_totals() {
177     int i;
178
179     for(i = 0; i < HISTORY_LENGTH; i++) {
180         int j;
181         int ii = (HISTORY_LENGTH + history_pos - i) % HISTORY_LENGTH;
182
183         for(j = 0; j < HISTORY_DIVISIONS; j++) {
184             if(i < history_divs[j]) {
185                 totals.recv[j] += history_totals.recv[ii];
186                 totals.sent[j] += history_totals.sent[ii];
187             }
188         }
189
190         if(history_totals.recv[i] > peakrecv) {
191             peakrecv = history_totals.recv[i];
192         }
193         if(history_totals.sent[i] > peaksent) {
194             peaksent = history_totals.sent[i];
195         }
196         if(history_totals.recv[i] + history_totals.sent[i] > peaktotal) {
197             peaktotal = history_totals.recv[i] + history_totals.sent[i];        
198         }
199     }
200     for(i = 0; i < HISTORY_DIVISIONS; i++) {
201       int t = history_length(i);
202       totals.recv[i] /= t;
203       totals.sent[i] /= t;
204     }
205 }
206
207 void make_screen_list() {
208     hash_node_type* n = NULL;
209     while(hash_next_item(screen_hash, &n) == HASH_STATUS_OK) {
210         host_pair_line* line = (host_pair_line*)n->rec;
211         int i;
212         for(i = 0; i < HISTORY_DIVISIONS; i++) {
213           line->recv[i] /= history_length(i);
214           line->sent[i] /= history_length(i);
215         }
216
217         /* Don't make a new, sorted screen list if order is frozen
218          */
219         if(!options.freezeorder) {
220             sorted_list_insert(&screen_list, line);
221         } 
222          
223     }
224 }
225
226 /*
227  * Zeros all data in the screen hash, but does not remove items.
228  */
229 void screen_hash_clear() {
230     hash_node_type* n = NULL;
231     while(hash_next_item(screen_hash, &n) == HASH_STATUS_OK) {
232         host_pair_line* hpl = (host_pair_line*)n->rec;
233         hpl->total_recv = hpl->total_sent = 0;
234         memset(hpl->recv, 0, sizeof(hpl->recv));
235         memset(hpl->sent, 0, sizeof(hpl->sent));
236     }
237 }
238
239 void analyse_data() {
240     hash_node_type* n = NULL;
241
242     if(options.paused == 1) {
243       return;
244     }
245
246     // Zero totals
247     memset(&totals, 0, sizeof totals);
248
249     if(options.freezeorder) {
250       screen_hash_clear();
251     }
252     else {
253       screen_list_clear();
254       hash_delete_all(screen_hash);
255     }
256
257     while(hash_next_item(history, &n) == HASH_STATUS_OK) {
258         history_type* d = (history_type*)n->rec;
259         host_pair_line* screen_line;
260         union {
261             host_pair_line **h_p_l_pp;
262             void **void_pp;
263         } u_screen_line = { &screen_line };
264         addr_pair ap;
265         int i;
266         int tsent, trecv;
267         tsent = trecv = 0;
268
269
270         ap = *(addr_pair*)n->key;
271
272         /* Aggregate hosts, if required */
273         if(options.aggregate_src) {
274             memset(&ap.src6, '\0', sizeof(ap.src6));
275         }
276         if(options.aggregate_dest) {
277             memset(&ap.dst6, '\0', sizeof(ap.dst6));
278         }
279
280         /* Aggregate ports, if required */
281         if(options.showports == OPTION_PORTS_DEST || options.showports == OPTION_PORTS_OFF) {
282             ap.src_port = 0;
283         }
284         if(options.showports == OPTION_PORTS_SRC || options.showports == OPTION_PORTS_OFF) {
285             ap.dst_port = 0;
286         }
287         if(options.showports == OPTION_PORTS_OFF) {
288             ap.protocol = 0;
289         }
290
291         
292         if(hash_find(screen_hash, &ap, u_screen_line.void_pp) == HASH_STATUS_KEY_NOT_FOUND) {
293             screen_line = xcalloc(1, sizeof *screen_line);
294             hash_insert(screen_hash, &ap, screen_line);
295             screen_line->ap = ap;
296         }
297         
298         screen_line->total_sent += d->total_sent;
299         screen_line->total_recv += d->total_recv;
300
301         for(i = 0; i < HISTORY_LENGTH; i++) {
302             int j;
303             int ii = (HISTORY_LENGTH + history_pos - i) % HISTORY_LENGTH;
304
305             for(j = 0; j < HISTORY_DIVISIONS; j++) {
306                 if(i < history_divs[j]) {
307                     screen_line->recv[j] += d->recv[ii];
308                     screen_line->sent[j] += d->sent[ii];
309                 }
310             }
311         }
312
313     }
314
315     make_screen_list();
316
317     
318     calculate_totals();
319
320 }
321
322 void sprint_host(char * line, int af, struct in6_addr* addr, unsigned int port, unsigned int protocol, int L, int unspecified_as_star) {
323     char hostname[HOSTNAME_LENGTH];
324     char service[HOSTNAME_LENGTH];
325     char* s_name;
326     union {
327         char **ch_pp;
328         void **void_pp;
329     } u_s_name = { &s_name };
330
331     ip_service skey;
332     int left;
333
334     if(IN6_IS_ADDR_UNSPECIFIED(addr) && unspecified_as_star) {
335         sprintf(hostname, " * ");
336     }
337     else {
338         if (options.dnsresolution)
339             resolve(af, addr, hostname, L);
340         else
341             inet_ntop(af, addr, hostname, sizeof(hostname));
342     }
343     left = strlen(hostname);
344
345     if(port != 0) {
346       skey.port = port;
347       skey.protocol = protocol;
348       if(options.portresolution && hash_find(service_hash, &skey, u_s_name.void_pp) == HASH_STATUS_OK) {
349         snprintf(service, HOSTNAME_LENGTH, ":%s", s_name);
350       }
351       else {
352         snprintf(service, HOSTNAME_LENGTH, ":%d", port);
353       }
354     }
355     else {
356       service[0] = '\0';
357     }
358     
359     /* If we're showing IPv6 addresses with a port number, put them in square
360      * brackets. */
361     if(port == 0 || af == AF_INET || L < 2) {
362       sprintf(line, "%-*s", L, hostname);
363     }
364     else {
365       sprintf(line, "[%-.*s]", L-2, hostname);
366       left += 2;
367     }
368     if(left > (L - strlen(service))) {
369         left = L - strlen(service);
370         if(left < 0) {
371            left = 0;
372         }
373     }
374     sprintf(line + left, "%-*s", L-left, service);
375 }
376