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