invitation.c: fix socket error checking on Windows
[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 = diff.tv_sec + diff.tv_usec * 1e-6;
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 = (in_packets - found->in_packets) / interval;
135                 found->in_bytes_rate = (in_bytes - found->in_bytes) / interval;
136                 found->out_packets_rate = (out_packets - found->out_packets) / interval;
137                 found->out_bytes_rate = (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, node->in_packets * pscale, node->in_bytes * bscale, node->out_packets * pscale, node->out_bytes * bscale);
284                 else
285                         mvprintw(row, 0, "%-16s %10.0f %10.0f %10.0f %10.0f",
286                                  node->name, node->in_packets_rate * pscale, node->in_bytes_rate * bscale, node->out_packets_rate * pscale, node->out_bytes_rate * bscale);
287         }
288
289         attrset(A_NORMAL);
290         move(1, 0);
291
292         refresh();
293 }
294
295 void top(int fd) {
296         initscr();
297         timeout(delay);
298         bool running = true;
299
300         while(running) {
301                 if(!update(fd)) {
302                         break;
303                 }
304
305                 redraw();
306
307                 switch(getch()) {
308                 case 's': {
309                         timeout(-1);
310                         float input = delay * 1e-3;
311                         mvprintw(1, 0, "Change delay from %.1fs to: ", input);
312                         scanw("%f", &input);
313
314                         if(input < 0.1) {
315                                 input = 0.1;
316                         }
317
318                         delay = input * 1e3;
319                         timeout(delay);
320                         break;
321                 }
322
323                 case 'c':
324                         cumulative = !cumulative;
325                         break;
326
327                 case 'n':
328                         sortmode = 0;
329                         break;
330
331                 case 'i':
332                         sortmode = 2;
333                         break;
334
335                 case 'I':
336                         sortmode = 1;
337                         break;
338
339                 case 'o':
340                         sortmode = 4;
341                         break;
342
343                 case 'O':
344                         sortmode = 3;
345                         break;
346
347                 case 't':
348                         sortmode = 6;
349                         break;
350
351                 case 'T':
352                         sortmode = 5;
353                         break;
354
355                 case 'b':
356                         bunit = "bytes";
357                         bscale = 1;
358                         punit = "pkts";
359                         pscale = 1;
360                         break;
361
362                 case 'k':
363                         bunit = "kbyte";
364                         bscale = 1e-3;
365                         punit = "pkts";
366                         pscale = 1;
367                         break;
368
369                 case 'M':
370                         bunit = "Mbyte";
371                         bscale = 1e-6;
372                         punit = "kpkt";
373                         pscale = 1e-3;
374                         break;
375
376                 case 'G':
377                         bunit = "Gbyte";
378                         bscale = 1e-9;
379                         punit = "Mpkt";
380                         pscale = 1e-6;
381                         break;
382
383                 case 'q':
384                 case KEY_BREAK:
385                         running = false;
386                         break;
387
388                 default:
389                         break;
390                 }
391         }
392
393         endwin();
394 }
395
396 #endif