CI: add libgcrypt to sanitizer run
[tinc] / src / top.c
1 /*
2     top.c -- Show real-time statistics from a running tincd
3     Copyright (C) 2011-2013 Guus Sliepen <guus@tinc-vpn.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21
22 #ifdef HAVE_CURSES
23
24 #undef KEY_EVENT  /* There are conflicting declarations for KEY_EVENT in Windows wincon.h and curses.h. */
25 #include <curses.h>
26
27 #include "control_common.h"
28 #include "list.h"
29 #include "names.h"
30 #include "tincctl.h"
31 #include "top.h"
32 #include "xalloc.h"
33
34 typedef struct nodestats_t {
35         char *name;
36         int i;
37         uint64_t in_packets;
38         uint64_t in_bytes;
39         uint64_t out_packets;
40         uint64_t out_bytes;
41         float in_packets_rate;
42         float in_bytes_rate;
43         float out_packets_rate;
44         float out_bytes_rate;
45         bool known;
46 } nodestats_t;
47
48 static const char *const sortname[] = {
49         "name",
50         "in pkts",
51         "in bytes",
52         "out pkts",
53         "out bytes",
54         "tot pkts",
55         "tot bytes",
56 };
57
58 static int sortmode = 0;
59 static bool cumulative = false;
60
61 static list_t node_list;
62 static struct timeval cur, prev, diff;
63 static int delay = 1000;
64 static bool changed = true;
65 static const char *bunit = "bytes";
66 static float bscale = 1;
67 static const char *punit = "pkts";
68 static float pscale = 1;
69
70 static bool update(int fd) {
71         if(!sendline(fd, "%d %d", CONTROL, REQ_DUMP_TRAFFIC)) {
72                 return false;
73         }
74
75         gettimeofday(&cur, NULL);
76
77         timersub(&cur, &prev, &diff);
78         prev = cur;
79         float interval = (float) diff.tv_sec + (float) diff.tv_usec * 1e-6f;
80
81         char line[4096];
82         char name[4096];
83         int code;
84         int req;
85         uint64_t in_packets;
86         uint64_t in_bytes;
87         uint64_t out_packets;
88         uint64_t out_bytes;
89
90         for list_each(nodestats_t, ns, &node_list) {
91                 ns->known = false;
92         }
93
94         while(recvline(fd, line, sizeof(line))) {
95                 int n = sscanf(line, "%d %d %4095s %"PRIu64" %"PRIu64" %"PRIu64" %"PRIu64, &code, &req, name, &in_packets, &in_bytes, &out_packets, &out_bytes);
96
97                 if(n == 2) {
98                         return true;
99                 }
100
101                 if(n != 7) {
102                         return false;
103                 }
104
105                 nodestats_t *found = NULL;
106
107                 for list_each(nodestats_t, ns, &node_list) {
108                         int result = strcmp(name, ns->name);
109
110                         if(result > 0) {
111                                 continue;
112                         }
113
114                         if(result == 0) {
115                                 found = ns;
116                                 break;
117                         } else {
118                                 found = xzalloc(sizeof(*found));
119                                 found->name = xstrdup(name);
120                                 list_insert_before(&node_list, node, found);
121                                 changed = true;
122                                 break;
123                         }
124                 }
125
126                 if(!found) {
127                         found = xzalloc(sizeof(*found));
128                         found->name = xstrdup(name);
129                         list_insert_tail(&node_list, found);
130                         changed = true;
131                 }
132
133                 found->known = true;
134                 found->in_packets_rate = (float)(in_packets - found->in_packets) / interval;
135                 found->in_bytes_rate = (float)(in_bytes - found->in_bytes) / interval;
136                 found->out_packets_rate = (float)(out_packets - found->out_packets) / interval;
137                 found->out_bytes_rate = (float)(out_bytes - found->out_bytes) / interval;
138                 found->in_packets = in_packets;
139                 found->in_bytes = in_bytes;
140                 found->out_packets = out_packets;
141                 found->out_bytes = out_bytes;
142         }
143
144         return false;
145 }
146
147 static int cmpfloat(float a, float b) {
148         if(a < b) {
149                 return -1;
150         } else if(a > b) {
151                 return 1;
152         } else {
153                 return 0;
154         }
155 }
156
157 static int cmpu64(uint64_t a, uint64_t b) {
158         if(a < b) {
159                 return -1;
160         } else if(a > b) {
161                 return 1;
162         } else {
163                 return 0;
164         }
165 }
166
167 static int sortfunc(const void *a, const void *b) {
168         const nodestats_t *na = *(const nodestats_t **)a;
169         const nodestats_t *nb = *(const nodestats_t **)b;
170         int result;
171
172         switch(sortmode) {
173         case 1:
174                 if(cumulative) {
175                         result = -cmpu64(na->in_packets, nb->in_packets);
176                 } else {
177                         result = -cmpfloat(na->in_packets_rate, nb->in_packets_rate);
178                 }
179
180                 break;
181
182         case 2:
183                 if(cumulative) {
184                         result = -cmpu64(na->in_bytes, nb->in_bytes);
185                 } else {
186                         result = -cmpfloat(na->in_bytes_rate, nb->in_bytes_rate);
187                 }
188
189                 break;
190
191         case 3:
192                 if(cumulative) {
193                         result = -cmpu64(na->out_packets, nb->out_packets);
194                 } else {
195                         result = -cmpfloat(na->out_packets_rate, nb->out_packets_rate);
196                 }
197
198                 break;
199
200         case 4:
201                 if(cumulative) {
202                         result = -cmpu64(na->out_bytes, nb->out_bytes);
203                 } else {
204                         result = -cmpfloat(na->out_bytes_rate, nb->out_bytes_rate);
205                 }
206
207                 break;
208
209         case 5:
210                 if(cumulative) {
211                         result = -cmpu64(na->in_packets + na->out_packets, nb->in_packets + nb->out_packets);
212                 } else {
213                         result = -cmpfloat(na->in_packets_rate + na->out_packets_rate, nb->in_packets_rate + nb->out_packets_rate);
214                 }
215
216                 break;
217
218         case 6:
219                 if(cumulative) {
220                         result = -cmpu64(na->in_bytes + na->out_bytes, nb->in_bytes + nb->out_bytes);
221                 } else {
222                         result = -cmpfloat(na->in_bytes_rate + na->out_bytes_rate, nb->in_bytes_rate + nb->out_bytes_rate);
223                 }
224
225                 break;
226
227         default:
228                 result = strcmp(na->name, nb->name);
229                 break;
230         }
231
232         if(result) {
233                 return result;
234         } else {
235                 return na->i - nb->i;
236         }
237 }
238
239 static void redraw(void) {
240         erase();
241
242         mvprintw(0, 0, "Tinc %-16s  Nodes: %4d  Sort: %-10s  %s", netname ? netname : "", node_list.count, sortname[sortmode], cumulative ? "Cumulative" : "Current");
243         attrset(A_REVERSE);
244         mvprintw(2, 0, "Node                IN %s   IN %s   OUT %s  OUT %s", punit, bunit, punit, bunit);
245         chgat(-1, A_REVERSE, 0, NULL);
246
247         static nodestats_t **sorted = 0;
248         static int n = 0;
249
250         if(changed) {
251                 n = 0;
252                 sorted = xrealloc(sorted, node_list.count * sizeof(*sorted));
253
254                 for list_each(nodestats_t, ns, &node_list) {
255                         sorted[n++] = ns;
256                 }
257
258                 changed = false;
259         }
260
261         for(int i = 0; i < n; i++) {
262                 sorted[i]->i = i;
263         }
264
265         if(sorted) {
266                 qsort(sorted, n, sizeof(*sorted), sortfunc);
267         }
268
269         for(int i = 0, row = 3; i < n; i++, row++) {
270                 nodestats_t *node = sorted[i];
271
272                 if(node->known)
273                         if(node->in_packets_rate || node->out_packets_rate) {
274                                 attrset(A_BOLD);
275                         } else {
276                                 attrset(A_NORMAL);
277                         } else {
278                         attrset(A_DIM);
279                 }
280
281                 if(cumulative)
282                         mvprintw(row, 0, "%-16s %10.0f %10.0f %10.0f %10.0f",
283                                  node->name, (float) node->in_packets * pscale, (float) node->in_bytes * bscale,
284                                  (float) node->out_packets * pscale, (float) node->out_bytes * bscale);
285                 else
286                         mvprintw(row, 0, "%-16s %10.0f %10.0f %10.0f %10.0f",
287                                  node->name, node->in_packets_rate * pscale, node->in_bytes_rate * bscale, node->out_packets_rate * pscale, node->out_bytes_rate * bscale);
288         }
289
290         attrset(A_NORMAL);
291         move(1, 0);
292
293         refresh();
294 }
295
296 void top(int fd) {
297         initscr();
298         timeout(delay);
299         bool running = true;
300
301         while(running) {
302                 if(!update(fd)) {
303                         break;
304                 }
305
306                 redraw();
307
308                 switch(getch()) {
309                 case 's': {
310                         timeout(-1);
311                         float input = (float)delay * 1e-3f;
312                         mvprintw(1, 0, "Change delay from %.1fs to: ", input);
313                         scanw("%f", &input);
314
315                         if(input < 0.1) {
316                                 input = 0.1f;
317                         }
318
319                         delay = (int)(input * 1e3f);
320                         timeout(delay);
321                         break;
322                 }
323
324                 case 'c':
325                         cumulative = !cumulative;
326                         break;
327
328                 case 'n':
329                         sortmode = 0;
330                         break;
331
332                 case 'i':
333                         sortmode = 2;
334                         break;
335
336                 case 'I':
337                         sortmode = 1;
338                         break;
339
340                 case 'o':
341                         sortmode = 4;
342                         break;
343
344                 case 'O':
345                         sortmode = 3;
346                         break;
347
348                 case 't':
349                         sortmode = 6;
350                         break;
351
352                 case 'T':
353                         sortmode = 5;
354                         break;
355
356                 case 'b':
357                         bunit = "bytes";
358                         bscale = 1;
359                         punit = "pkts";
360                         pscale = 1;
361                         break;
362
363                 case 'k':
364                         bunit = "kbyte";
365                         bscale = 1e-3f;
366                         punit = "pkts";
367                         pscale = 1;
368                         break;
369
370                 case 'M':
371                         bunit = "Mbyte";
372                         bscale = 1e-6f;
373                         punit = "kpkt";
374                         pscale = 1e-3f;
375                         break;
376
377                 case 'G':
378                         bunit = "Gbyte";
379                         bscale = 1e-9f;
380                         punit = "Mpkt";
381                         pscale = 1e-6f;
382                         break;
383
384                 case 'q':
385                 case KEY_BREAK:
386                         running = false;
387                         break;
388
389                 default:
390                         break;
391                 }
392         }
393
394         endwin();
395 }
396
397 #endif