1 /*
2 * jnettop, network online traffic visualiser
3 * Copyright (C) 2002-2006 Jakub Skopal
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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * $Header: /cvsroot/jnettop/jnettop/jcursesdisplay.c,v 1.2 2006/04/11 15:21:05 merunka Exp $
20 *
21 */
22
23 #include "jbase.h"
24 #include "jdevice.h"
25 #include "jprocessor.h"
26 #include "jconfig.h"
27 #include "jutil.h"
28 #include "jcursesdisplay.h"
29
30 #ifdef SUPPORT_NCURSES
31
32 gboolean recycleJnettop;
33
34 GMutex *statusMutex;
35 char *statusMessage;
36 GTimeVal statusTimeout;
37
38 GMutex *displayStreamsMutex;
39 jbase_stream **displayStreams;
40 int displayStreamsCount;
41 gchar line0FormatString[512], line1FormatString[512], line2FormatString[512];
42
43 gboolean onoffBitValues;
44 gboolean onoffPackets;
45
46 #define DISPLAYMODE_NORMAL 0
47 #define DISPLAYMODE_BPFFILTERS 1
48 #define DISPLAYMODE_HELP 2
49 #define DISPLAYMODE_SORTING 3
50
51 int displayMode = DISPLAYMODE_NORMAL;
52
53 WINDOW *listWindow;
54
55 int activeLines=1, activeColumns=1;
56
57 GCompareFunc currentByBytesCompareFunc = (GCompareFunc) jprocessor_compare_ByBytesStat;
58 GCompareFunc currentByPacketsCompareFunc = (GCompareFunc) jprocessor_compare_ByPacketsStat;
59
drawStatus(const gchar * msg)60 static void drawStatus(const gchar *msg) {
61 g_mutex_lock(statusMutex);
62 statusMessage = g_strdup(msg);
63 g_get_current_time(&statusTimeout);
64 g_time_val_add(&statusTimeout, 1000000);
65 g_mutex_unlock(statusMutex);
66 attron(A_BOLD);
67 mvprintw(2, 0, "%s", statusMessage);
68 clrtoeol();
69 attroff(A_BOLD);
70 refresh();
71 }
72
drawScreen()73 static void drawScreen() {
74 if (LINES != activeLines || COLS != activeColumns || !activeLines || !activeColumns) {
75 activeLines = LINES;
76 activeColumns = COLS;
77
78 if (activeLines < 20 || activeColumns < 80) {
79 endwin();
80 fprintf(stderr, "Too small terminal (detected size: %dx%d), minimum required size: 80x20\n", activeColumns, activeLines);
81 exit(255);
82 }
83
84 attrset(A_NORMAL);
85
86 mvprintw(0, 0, "run XXX:XX:XX device XXXXXXXXXX pkt[f]ilter: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
87 mvprintw(1, 0, "[c]ntfilter: XXX [b]ps=XXXXXXX [l]ocal aggr.: XXXX [r]emote aggr.: XXXX ");
88 mvprintw(0, activeColumns-1, ".");
89
90 {
91 int addrColumns = (activeColumns - 48) / 2;
92 sprintf(line0FormatString, "%%-%d.%ds %%7.7s %%7.7s %%8.8s", activeColumns-25, activeColumns-25);
93 sprintf(line1FormatString, " %%-%d.%ds %%5.5s %%6.6s %%-%d.%ds %%5.5s %%7.7s %%7.7s %%8.8s", addrColumns, addrColumns, addrColumns, addrColumns);
94 sprintf(line2FormatString, " %%-%d.%ds", activeColumns-3, activeColumns-3);
95 }
96
97 if (listWindow) {
98 delwin(listWindow);
99 }
100 listWindow = newwin(activeLines-8, activeColumns, 5, 0);
101 }
102 g_mutex_lock(statusMutex);
103 if (statusMessage == NULL) {
104 mvprintw(2, 0, "[q]uit [h]elp [s]orting [p]ackets [.] pause ");
105 if (jdevice_DevicesCount>1) {
106 mvprintw(2, 44, "[0]-[9] switch device");
107 }
108 } else {
109 GTimeVal tv;
110 attron(A_BOLD);
111 mvprintw(2, 0, statusMessage);
112 attroff(A_BOLD);
113 g_get_current_time(&tv);
114 if (tv.tv_sec >= statusTimeout.tv_sec) {
115 g_free(statusMessage);
116 statusMessage = NULL;
117 }
118 }
119 g_mutex_unlock(statusMutex);
120 clrtoeol();
121 }
122
drawHeader()123 static void drawHeader() {
124 GTimeVal currentTime;
125 gchar timeBuffer[32];
126 gchar srcbps[10], dstbps[10], bps[10], total[10], totalsrc[10], totaldst[10];
127 int i;
128 struct tm tm;
129
130 attron(A_BOLD);
131
132 g_get_current_time(¤tTime);
133 localtime_r(¤tTime.tv_sec, &tm);
134 sprintf(timeBuffer, "%3d:%02d:%02d", (int)((currentTime.tv_sec-jprocessor_Stats.startTime.tv_sec)/3600), (int)((currentTime.tv_sec-jprocessor_Stats.startTime.tv_sec)%3600/60), (int)((currentTime.tv_sec-jprocessor_Stats.startTime.tv_sec)%60));
135 mvprintw(0, 4, "%s", timeBuffer);
136 if (jcapture_ActiveDevice)
137 mvprintw(0, 21, "%-10s", jcapture_ActiveDevice->name);
138 mvprintw(0, 45, "%-29.29s", jconfig_GetSelectedBpfFilterName());
139 mvprintw(1, 13, "%s", jprocessor_ContentFiltering?"on ":"off");
140 mvprintw(1, 23, "%s", onoffPackets ? "pckts/s" : (onoffBitValues?"bits/s ":"bytes/s"));
141 mvprintw(1, 46, "%s", JBASE_AGGREGATION[jprocessor_LocalAggregation]);
142 mvprintw(1, 67, "%s", JBASE_AGGREGATION[jprocessor_RemoteAggregation]);
143
144 attroff(A_BOLD);
145
146 jutil_formatNumber(onoffPackets?jprocessor_Stats.totalPPS:(onoffBitValues?8:1)*jprocessor_Stats.totalBPS, onoffPackets, bps, 6);
147 g_strlcat(bps, "/s", sizeof(bps));
148 jutil_formatNumber(onoffPackets?jprocessor_Stats.totalSrcPPS:(onoffBitValues?8:1)*jprocessor_Stats.totalSrcBPS, onoffPackets, srcbps, 6);
149 g_strlcat(srcbps, "/s", sizeof(srcbps));
150 jutil_formatNumber(onoffPackets?jprocessor_Stats.totalDstPPS:(onoffBitValues?8:1)*jprocessor_Stats.totalDstBPS, onoffPackets, dstbps, 6);
151 g_strlcat(dstbps, "/s", sizeof(dstbps));
152 mvprintw(activeLines-2, 0, line0FormatString, "TOTAL", srcbps, dstbps, bps);
153
154 jutil_formatNumber(onoffPackets?jprocessor_Stats.totalPackets:(onoffBitValues?8:1)*jprocessor_Stats.totalBytes, onoffPackets, total, 6);
155 jutil_formatNumber(onoffPackets?jprocessor_Stats.totalSrcPackets:(onoffBitValues?8:1)*jprocessor_Stats.totalSrcBytes, onoffPackets, totalsrc, 6);
156 jutil_formatNumber(onoffPackets?jprocessor_Stats.totalDstPackets:(onoffBitValues?8:1)*jprocessor_Stats.totalDstBytes, onoffPackets, totaldst, 6);
157 mvprintw(activeLines-1, 0, line1FormatString, "", "", "", "", "", totalsrc, totaldst, total);
158
159 mvchgat(activeLines-2, 0, activeColumns-25, A_BOLD, 0, NULL);
160
161 for (i=0; i<activeColumns; i++)
162 mvaddch(activeLines-3, i, ACS_HLINE);
163
164 attron(A_REVERSE);
165
166 mvprintw(3, 0, line0FormatString, "LOCAL <-> REMOTE", onoffPackets ? "TXPPS" : "TXBPS",
167 onoffPackets ? "RXPPS" : "RXBPS", onoffPackets ? "TOTALPPS" : "TOTALBPS");
168 mvprintw(4, 0, line1FormatString, "(IP)", "PORT", "PROTO", "(IP)", "PORT", "TX", "RX", "TOTAL");
169
170 attroff(A_REVERSE);
171 }
172
processStreamsFunc(GPtrArray * streamArray)173 static void processStreamsFunc(GPtrArray * streamArray) {
174 guint i, j;
175 int lines,oldLines;
176 jbase_stream **streams,**oldStreams;
177
178 lines = (activeLines - 8) / 3;
179 streams = g_new0(jbase_stream *, lines);
180
181 for (i=0,j=0; i<streamArray->len && j<lines; i++) {
182 jbase_stream *s = (jbase_stream *)g_ptr_array_index(streamArray, i);
183 if (s->dead > 5) {
184 continue;
185 }
186 s->displayed ++;
187 streams[j++] = s;
188 }
189 lines = j;
190
191 g_mutex_lock(displayStreamsMutex);
192 oldStreams = displayStreams;
193 oldLines = displayStreamsCount;
194 displayStreams = streams;
195 displayStreamsCount = lines;
196 g_mutex_unlock(displayStreamsMutex);
197
198 for (i=0; i<oldLines; i++) {
199 oldStreams[i]->displayed --;
200 }
201
202 if (oldStreams)
203 g_free(oldStreams);
204 }
205
doDisplayStreams()206 static void doDisplayStreams() {
207 int i;
208 for (i=0; i<displayStreamsCount; i++) {
209 gchar srcaddr[INET6_ADDRSTRLEN + 1], dstaddr[INET6_ADDRSTRLEN + 1];
210 gchar srcport[10], dstport[10], srcbps[10], dstbps[10], bps[10];
211 gchar total[10], totalsrc[10], totaldst[10];
212 uint tmp;
213 gchar linebuffer[1024];
214 const gchar *psrcaddr, *pdstaddr;
215 jbase_stream *s = displayStreams[i];
216 tmp = onoffPackets ? s->totalpps : (onoffBitValues?8:1)*s->totalbps;
217 jutil_formatNumber(tmp, onoffPackets, bps, 6);
218 g_strlcat(bps, "/s", sizeof(bps));
219 tmp = onoffPackets ? s->srcpps : (onoffBitValues?8:1)*s->srcbps;
220 jutil_formatNumber(tmp, onoffPackets, srcbps, 6);
221 g_strlcat(srcbps, "/s", sizeof(srcbps));
222 tmp = onoffPackets ? s->dstpps : (onoffBitValues?8:1)*s->dstbps;
223 jutil_formatNumber(tmp, onoffPackets, dstbps, 6);
224 g_strlcat(dstbps, "/s", sizeof(dstbps));
225 jutil_formatNumber(onoffPackets ? s->totalpackets : s->totalbytes, onoffPackets, total, 6);
226 jutil_formatNumber(onoffPackets ? s->srcpackets : s->srcbytes, onoffPackets, totalsrc, 6);
227 jutil_formatNumber(onoffPackets ? s->dstpackets : s->dstbytes, onoffPackets, totaldst, 6);
228 jutil_Address2String(JBASE_AF(s->proto), &s->src, srcaddr, INET6_ADDRSTRLEN);
229 if (s->srcresolv == NULL || s->srcresolv->name == NULL) {
230 psrcaddr = srcaddr;
231 } else {
232 psrcaddr = s->srcresolv->name;
233 }
234 jutil_Address2String(JBASE_AF(s->proto), &s->dst, dstaddr, INET6_ADDRSTRLEN);
235 if (s->dstresolv == NULL || s->dstresolv->name == NULL) {
236 pdstaddr = dstaddr;
237 } else {
238 pdstaddr = s->dstresolv->name;
239 }
240 if (s->srcport == -1)
241 strcpy(srcport, "AGGR.");
242 else
243 sprintf(srcport, "%d", s->srcport);
244 if (s->dstport == -1)
245 strcpy(dstport, "AGGR.");
246 else
247 sprintf(dstport, "%d", s->dstport);
248 sprintf(linebuffer, "%s <-> %s", psrcaddr, pdstaddr);
249 mvwprintw(listWindow, i*3, 0, line0FormatString, linebuffer, srcbps, dstbps, bps);
250 mvwchgat(listWindow, i*3, 0, activeColumns-25, A_BOLD, 0, NULL);
251 mvwprintw(listWindow, i*3+1, 0, line1FormatString, srcaddr, srcport, JBASE_PROTOCOLS[s->proto], dstaddr, dstport, totalsrc, totaldst, total);
252 mvwprintw(listWindow, i*3+2, 0, line2FormatString, s->filterDataString);
253 }
254 }
255
doDisplayWholeScreen()256 static void doDisplayWholeScreen() {
257 drawScreen();
258 drawHeader();
259 werase(listWindow);
260 }
261
displayLoop()262 static void displayLoop() {
263 g_usleep(500000);
264
265 while (jcapture_IsRunning) {
266 int i;
267
268 g_mutex_lock(displayStreamsMutex);
269 doDisplayWholeScreen();
270
271 switch (displayMode) {
272 case DISPLAYMODE_NORMAL:
273 doDisplayStreams();
274 break;
275 case DISPLAYMODE_BPFFILTERS:
276 wattron(listWindow, A_BOLD);
277 mvwprintw(listWindow, 1, 0, "Select rule you want to apply:");
278 wattroff(listWindow, A_BOLD);
279 mvwprintw(listWindow, 3, 5, "[.] None");
280 for (i=0; i<JCONFIG_BPFFILTERS_LEN; i++) {
281 mvwprintw(listWindow, i+5, 5, "[%c] %s", 'a'+i, JCONFIG_BPFFILTERS_GETNAME(i));
282 }
283 if (JCONFIG_BPFFILTERS_LEN == 0) {
284 mvwprintw(listWindow, 6, 5, "You have no predefined filter rules. See README file for explanation");
285 mvwprintw(listWindow, 7, 5, "on how to predefine filter rules");
286 }
287 break;
288 case DISPLAYMODE_HELP:
289 mvwprintw(listWindow, 2, 0, "I must write something here... :)");
290 mvwprintw(listWindow, 4, 0, "Press any key to return.");
291 break;
292 case DISPLAYMODE_SORTING:
293 mvwprintw(listWindow, 1, 0, "Select sorting column");
294 mvwprintw(listWindow, 3, 0, " [.] on/off");
295 mvwprintw(listWindow, 5, 0, " [t]xbps/txpps");
296 mvwprintw(listWindow, 6, 0, " [r]xbps/rxpps");
297 mvwprintw(listWindow, 7, 0, " total [b]ps/total pps");
298 }
299
300 g_mutex_unlock(displayStreamsMutex);
301
302 wnoutrefresh(listWindow);
303 refresh();
304
305 i = getch();
306 if (i==ERR) {
307 g_usleep(1000000);
308 } else {
309 switch (displayMode) {
310 case DISPLAYMODE_NORMAL:
311 switch (i) {
312 case '.':
313 drawStatus("Paused. Press any key to resume.");
314 while (getch() == ERR) {
315 g_usleep(100000);
316 }
317 break;
318 case 'q':
319 case 'Q':
320 drawStatus("Please wait, shutting down...");
321 jcapture_Kill();
322 break;
323 case 'c':
324 jprocessor_SetContentFiltering( !jprocessor_ContentFiltering );
325 break;
326 case 'b':
327 onoffBitValues = !onoffBitValues;
328 break;
329 case 'p':
330 onoffPackets = !onoffPackets;
331 jprocessor_SetSorting( jprocessor_Sorting, onoffPackets ? currentByPacketsCompareFunc : currentByBytesCompareFunc );
332 break;
333 case 's':
334 displayMode = DISPLAYMODE_SORTING;
335 break;
336 case 'f':
337 displayMode = DISPLAYMODE_BPFFILTERS;
338 break;
339 case 'h':
340 displayMode = DISPLAYMODE_HELP;
341 break;
342 case 'l':
343 jprocessor_SetLocalAggregation((jprocessor_LocalAggregation + 1) % 3);
344 break;
345 case 'r':
346 jprocessor_SetRemoteAggregation((jprocessor_RemoteAggregation + 1) % 3);
347 break;
348 case '0':
349 case '1':
350 case '2':
351 case '3':
352 case '4':
353 case '5':
354 case '6':
355 case '7':
356 case '8':
357 case '9':
358 i -= '0';
359 if (jdevice_DevicesCount>1 && jdevice_DevicesCount>i) {
360 drawStatus("Please wait, cleaning up...");
361 jconfig_Settings.device = jdevice_Devices + i;
362 jconfig_Settings.deviceName = jconfig_Settings.device->name;
363 recycleJnettop = TRUE;
364 jcapture_Kill();
365 }
366 break;
367 }
368 break;
369 case DISPLAYMODE_BPFFILTERS:
370 if ((i == '.') || ((i >= 'a') && (i < 'a' + (JCONFIG_BPFFILTERS_LEN)))) {
371 drawStatus("Please wait, cleaning up...");
372 switch (i) {
373 case '.':
374 JCONFIG_BPFFILTERS_SETNONE;
375 break;
376 default:
377 JCONFIG_BPFFILTERS_SETSELECTEDFILTER(i-'a');
378 break;
379 }
380 recycleJnettop = TRUE;
381 jcapture_Kill();
382 displayMode = DISPLAYMODE_NORMAL;
383 break;
384 }
385 break;
386 case DISPLAYMODE_SORTING:
387 switch (i) {
388 case '.':
389 jprocessor_SetSorting(!jprocessor_Sorting, NULL);
390 if (!jprocessor_Sorting)
391 drawStatus("Streams sorting suspended.");
392 else
393 drawStatus("Streams sorting resumed.");
394 displayMode = DISPLAYMODE_NORMAL;
395 break;
396 case 't':
397 currentByBytesCompareFunc = (GCompareFunc) jprocessor_compare_ByTxBytesStat;
398 currentByPacketsCompareFunc = (GCompareFunc) jprocessor_compare_ByTxPacketsStat;
399 jprocessor_SetSorting(-1, onoffPackets ? currentByPacketsCompareFunc : currentByBytesCompareFunc );
400 displayMode = DISPLAYMODE_NORMAL;
401 break;
402 case 'r':
403 currentByBytesCompareFunc = (GCompareFunc) jprocessor_compare_ByRxBytesStat;
404 currentByPacketsCompareFunc = (GCompareFunc) jprocessor_compare_ByRxPacketsStat;
405 jprocessor_SetSorting(-1, onoffPackets ? currentByPacketsCompareFunc : currentByBytesCompareFunc );
406 displayMode = DISPLAYMODE_NORMAL;
407 break;
408 case 'b':
409 currentByBytesCompareFunc = (GCompareFunc) jprocessor_compare_ByBytesStat;
410 currentByPacketsCompareFunc = (GCompareFunc) jprocessor_compare_ByPacketsStat;
411 jprocessor_SetSorting(-1, onoffPackets ? currentByPacketsCompareFunc : currentByBytesCompareFunc );
412 displayMode = DISPLAYMODE_NORMAL;
413 break;
414 default:
415 drawStatus("Invalid key.");
416 break;
417 }
418 break;
419 case DISPLAYMODE_HELP:
420 displayMode = DISPLAYMODE_NORMAL;
421 break;
422 }
423 }
424 }
425 }
426
jcursesdisplay_PreSetup()427 static gboolean jcursesdisplay_PreSetup() {
428 return TRUE;
429 }
430
jcursesdisplay_Setup()431 static void jcursesdisplay_Setup() {
432 displayStreamsMutex = g_mutex_new();
433 statusMutex = g_mutex_new();
434
435 jprocessor_SetProcessStreamsFunc((ProcessStreamsFunc) processStreamsFunc);
436
437 initscr();
438 cbreak();
439 noecho();
440 nonl();
441 intrflush(stdscr, FALSE);
442 keypad(stdscr, TRUE);
443 nodelay(stdscr, TRUE);
444
445 onoffBitValues = FALSE;
446 }
447
jcursesdisplay_PreRunSetup()448 static gboolean jcursesdisplay_PreRunSetup() {
449 return TRUE;
450 }
451
jcursesdisplay_PreRun()452 static void jcursesdisplay_PreRun() {
453 displayStreams = NULL;
454 displayStreamsCount = 0;
455
456 activeLines = 0;
457 activeColumns = 0;
458
459 if (statusMessage) {
460 g_free(statusMessage);
461 statusMessage = NULL;
462 }
463
464 clear();
465 drawScreen();
466
467 recycleJnettop = FALSE;
468 }
469
jcursesdisplay_Run()470 static gboolean jcursesdisplay_Run() {
471 displayLoop();
472 return recycleJnettop;
473 }
474
jcursesdisplay_Shutdown()475 static void jcursesdisplay_Shutdown() {
476 endwin();
477 }
478
jcursesdisplay_DrawStatus(const gchar * msg)479 static void jcursesdisplay_DrawStatus(const gchar *msg) {
480 drawStatus(msg);
481 }
482
jcursesdisplay_ProcessArgument(const gchar ** arg,int argc)483 static int jcursesdisplay_ProcessArgument(const gchar **arg, int argc) {
484 if (!strcmp(*arg, "-b") || !strcmp(*arg, "--bit-units")) {
485 onoffBitValues = TRUE;
486 return 1;
487 }
488 return 0;
489 }
490
491 jbase_display jcursesdisplay_Functions = {
492 TRUE,
493 jcursesdisplay_PreSetup,
494 jcursesdisplay_Setup,
495 jcursesdisplay_PreRunSetup,
496 jcursesdisplay_PreRun,
497 jcursesdisplay_Run,
498 jcursesdisplay_Shutdown,
499 jcursesdisplay_DrawStatus,
500 jcursesdisplay_ProcessArgument
501 };
502
503 #else
504
505 jbase_display jcursesdisplay_Functions = { FALSE };
506
507 #endif
508