setting svn:exports
[jnettop.git] / jnettop / juiadisplay.c
blob816c73b405f8ed18f578bac08221caea79d2c6d7
1 /*
2 * jnettop, network online traffic visualiser
3 * Copyright (C) 2002-2006 Jakub Skopal
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.
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.
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
19 * $Header$
23 #include "jbase.h"
24 #include "jdevice.h"
25 #include "jprocessor.h"
26 #include "jconfig.h"
27 #include "jutil.h"
28 #include "juiadisplay.h"
30 #ifdef ENABLE_UIA
32 #define LISTEN_ERROR_ANSWER "listen:ASCII:NAK:Error compiling rule: syntax error\n\n"
33 #define ERROR_MAXIMUM_TIMEOUT_EXPIRED "\n\n"
34 #define GET_REQUEST_END_BOUNDARY "\n"
36 #define MAXRECV 32769
37 #define MAX_COMMAND_TIMEOUT_MINUTES 5
38 #define SMALL_WAIT 100
39 #define DEFAULT_LINE_COUNT 15
41 GMutex *displayStreamsMutex;
42 jbase_stream **displayStreams = NULL;
43 int displayStreamsCount = 0;
44 gboolean bHaveData = FALSE;
45 int nLineCount = DEFAULT_LINE_COUNT;
47 gboolean onoffPackets;
48 gboolean onoffBitValues;
50 static void processStreamsFunc(GPtrArray * streamArray) {
51 guint i,j;
52 guint lines, oldLines;
53 jbase_stream **streams,**oldStreams;
55 streams = g_new0(jbase_stream *, nLineCount);
57 for (i=0,j=0; i<streamArray->len && j<nLineCount; i++) {
58 jbase_stream *s = (jbase_stream *)g_ptr_array_index(streamArray, i);
59 if (s->dead > 5) {
60 continue;
62 s->displayed ++;
63 streams[j++] = s;
66 lines = j;
68 g_mutex_lock(displayStreamsMutex);
69 if(lines > 0)
70 bHaveData = TRUE;
71 oldStreams = displayStreams;
72 oldLines = displayStreamsCount;
73 displayStreams = streams;
74 displayStreamsCount = lines;
75 g_mutex_unlock(displayStreamsMutex);
77 for (i=0; i<oldLines; i++) {
78 oldStreams[i]->displayed --;
81 if (oldStreams)
82 g_free(oldStreams);
85 static gchar * get_next_token_colon_delim(gchar ** text){
86 gchar * tmp = NULL;
87 if(!text)
88 return NULL;
90 tmp = strsep(text, ":");
91 if(tmp && *tmp != '\0'){
92 return strdup(tmp);
93 }else{
94 return NULL;
98 void doWriteFormatedNetworkStreams(pid_t nSessionID, gulong lUSecsWaited) {
99 int i;
100 gchar buffer[32768];
101 gchar srcport[10], dstport[10], srcbps[10], dstbps[10], bps[10];
103 debug(LOG_DEBUG, "streams count %d", displayStreamsCount);
105 // dump out the totals line...
106 jutil_formatNumber(onoffPackets?jprocessor_Stats.totalPPS:(onoffBitValues?8:1)*jprocessor_Stats.totalBPS, onoffPackets, bps, 6);
107 g_strlcat(bps, "/s", sizeof(bps));
108 jutil_formatNumber(onoffPackets?jprocessor_Stats.totalSrcPPS:(onoffBitValues?8:1)*jprocessor_Stats.totalSrcBPS, onoffPackets, srcbps, 6);
109 g_strlcat(srcbps, "/s", sizeof(srcbps));
110 jutil_formatNumber(onoffPackets?jprocessor_Stats.totalDstPPS:(onoffBitValues?8:1)*jprocessor_Stats.totalDstBPS, onoffPackets, dstbps, 6);
111 g_strlcat(dstbps, "/s", sizeof(dstbps));
113 sprintf(buffer, "get:ASCII:%d:%d:ACK:TOTAL:::::%s:%s:%s\n", nSessionID, (int)lUSecsWaited, srcbps, dstbps, bps);
114 //debug(LOG_DEBUG, "sending %d characters '%s'", strlen(buffer), buffer);
115 printf("%s", buffer);
117 for (i=0; i< displayStreamsCount; i++) {
118 gchar srcaddr[INET6_ADDRSTRLEN + 1], dstaddr[INET6_ADDRSTRLEN + 1];
119 gchar total[10], totalsrc[10], totaldst[10];
120 uint tmp;
121 gchar linebuffer[1024];
122 const gchar *psrcaddr, *pdstaddr;
123 jbase_stream *s = displayStreams[i];
124 tmp = onoffPackets ? s->totalpps : (onoffBitValues?8:1)*s->totalbps;
125 jutil_formatNumber(tmp, onoffPackets, bps, 6);
126 g_strlcat(bps, "/s", sizeof(bps));
127 tmp = onoffPackets ? s->srcpps : (onoffBitValues?8:1)*s->srcbps;
128 jutil_formatNumber(tmp, onoffPackets, srcbps, 6);
129 g_strlcat(srcbps, "/s", sizeof(srcbps));
130 tmp = onoffPackets ? s->dstpps : (onoffBitValues?8:1)*s->dstbps;
131 jutil_formatNumber(tmp, onoffPackets, dstbps, 6);
132 g_strlcat(dstbps, "/s", sizeof(dstbps));
133 jutil_formatNumber(onoffPackets ? s->totalpackets : s->totalbytes, onoffPackets, total, 6);
134 jutil_formatNumber(onoffPackets ? s->srcpackets : s->srcbytes, onoffPackets, totalsrc, 6);
135 jutil_formatNumber(onoffPackets ? s->dstpackets : s->dstbytes, onoffPackets, totaldst, 6);
136 jutil_Address2String(JBASE_AF(s->proto), &s->src, srcaddr, INET6_ADDRSTRLEN);
137 if (s->srcresolv == NULL || s->srcresolv->name == NULL) {
138 psrcaddr = srcaddr;
139 } else {
140 psrcaddr = s->srcresolv->name;
142 jutil_Address2String(JBASE_AF(s->proto), &s->dst, dstaddr, INET6_ADDRSTRLEN);
143 if (s->dstresolv == NULL || s->dstresolv->name == NULL) {
144 pdstaddr = dstaddr;
145 } else {
146 pdstaddr = s->dstresolv->name;
148 if (s->srcport == -1)
149 strcpy(srcport, "AGGR.");
150 else
151 sprintf(srcport, "%d", s->srcport);
152 if (s->dstport == -1)
153 strcpy(dstport, "AGGR.");
154 else
155 sprintf(dstport, "%d", s->dstport);
156 sprintf(linebuffer, "%s:%s", psrcaddr, pdstaddr);
158 sprintf(buffer, "get:ASCII:%d:%d:ACK:%s:%s:%s:%s:%s:%s:%s:%s\n", nSessionID, (int)lUSecsWaited, srcaddr, srcport, JBASE_PROTOCOLS[s->proto], dstaddr, dstport, srcbps, dstbps, bps);
159 //debug(LOG_DEBUG, "sending %d characters '%s'", strlen(buffer), buffer);
160 printf("%s", buffer);
162 printf(GET_REQUEST_END_BOUNDARY);
165 static GTimeVal timeNow(){
166 GTimeVal timeNow;
167 g_get_current_time(&timeNow);
168 return timeNow;
171 static void networkConnectionLoop(){
172 // get our pid...
173 pid_t ourpid = getpid();
174 gboolean bExit = FALSE;
176 // setup timer here...
177 GTimeVal commandTimeout;
178 g_get_current_time(&commandTimeout);
180 while(!bExit && (timeNow().tv_sec - commandTimeout.tv_sec < MAX_COMMAND_TIMEOUT_MINUTES * 60)){
181 // make sure we don't block forever...
182 int nSelectReturn = 0;
183 fd_set listenSet;
184 struct timeval tm;
185 tm.tv_sec = 10; // wait ten seconds...
186 tm.tv_usec = 0;
187 FD_ZERO(&listenSet);
188 FD_SET(fileno(stdin), &listenSet);
190 nSelectReturn = select(fileno(stdin)+1, &listenSet, NULL, NULL, &tm);
192 if(nSelectReturn > 0){
193 if(FD_ISSET(fileno(stdin), &listenSet)){
194 int nDataRecievedCount = 0;
195 gchar data[ MAXRECV + 1];
196 bzero(data, MAXRECV);
197 nDataRecievedCount = read(fileno(stdin), data, MAXRECV - 2);
199 if(nDataRecievedCount > 0){
200 gchar * strPid = NULL;
201 gchar * strType = NULL;
202 gchar * strMethod = NULL;
203 gchar * strMaxWaitUSecs = NULL;
204 gchar * cpdata = data;
205 int nIndex;
207 // make sure we end the string...
208 data[nDataRecievedCount] = ':';
209 data[nDataRecievedCount + 1] = '\0';
211 // remove any control charaters...
212 for(nIndex = 0; nIndex<nDataRecievedCount; nIndex++){
213 if(iscntrl(data[nIndex])){
214 // this is a control character - change it to a :
215 data[nIndex] = ':';
219 strMethod = get_next_token_colon_delim(&cpdata);
220 strType = get_next_token_colon_delim(&cpdata);
221 strPid = get_next_token_colon_delim(&cpdata);
222 strMaxWaitUSecs = get_next_token_colon_delim(&cpdata);
224 if(strPid && ourpid != atoi(strPid)){
225 // error - key id not correct
226 debug(LOG_DEBUG, "Invalid session - %s given, %d expected", strPid, ourpid);
227 printf(LISTEN_ERROR_ANSWER);
228 }else if(strncmp(strMethod, "end", strlen("end")) == 0){
229 gchar endAnswer[16384];
230 g_get_current_time(&commandTimeout);
231 // recived an end command
232 sprintf(endAnswer, "end:ASCII:%d:ACK\n\n", ourpid);
233 debug(LOG_DEBUG, "Sending '%s'", endAnswer);
234 printf("%s", endAnswer);
235 bExit = TRUE;
236 }else if(strncmp(data, "get", strlen("get")) == 0){
237 gulong lMicroSeconds = 0;
238 gulong lWaitedSeconds = 0;
239 g_get_current_time(&commandTimeout);
240 // see how long we should wait...
242 if(strMaxWaitUSecs)
243 lMicroSeconds = atol(strMaxWaitUSecs);
246 debug(LOG_DEBUG, "waiting for data - will wait %d useconds", lMicroSeconds);
247 while(!bHaveData && lWaitedSeconds < lMicroSeconds){
248 lWaitedSeconds += SMALL_WAIT;
249 g_usleep(SMALL_WAIT);
252 debug(LOG_DEBUG, "waited %d useconds - bHaveData - %d", lWaitedSeconds, bHaveData);
254 if(bHaveData){
255 // recieved get request
256 // lock the mutex...
257 g_mutex_lock(displayStreamsMutex);
258 doWriteFormatedNetworkStreams(ourpid, lWaitedSeconds);
259 g_mutex_unlock(displayStreamsMutex);
260 }else{
261 debug(LOG_DEBUG, "Timed out waiting for data - sending '%s'", ERROR_MAXIMUM_TIMEOUT_EXPIRED);
262 printf(ERROR_MAXIMUM_TIMEOUT_EXPIRED);
265 fflush(NULL);
266 } // nDataRecievedCount
267 } // if(FD_ISSET)
268 } // select != -1
269 } // while (!bExit)
270 if(!(timeNow().tv_sec - commandTimeout.tv_sec < MAX_COMMAND_TIMEOUT_MINUTES * 60)){
271 // send timeout error
272 debug(LOG_WARNING, "Timed out while waiting for get/end command - waited %d seconds", timeNow().tv_sec - commandTimeout.tv_sec);
273 printf(ERROR_MAXIMUM_TIMEOUT_EXPIRED);
277 gboolean parseListenLineAndConfig(){
278 // wait here for a listen command with parameters...
279 gboolean bInitialized = FALSE;
281 static gchar data[ MAXRECV + 1];
283 pid_t ourpid = getpid();
284 gboolean bBitValuesBackup = onoffBitValues;
285 GTimeVal commandTimeout;
287 // backup the device name
288 gchar strDeviceBackup[30];
289 strDeviceBackup[0] = '\0';
291 if(jconfig_Settings.deviceName)
292 strcpy(strDeviceBackup, jconfig_Settings.deviceName);
295 // setup timer here...
296 g_get_current_time(&commandTimeout);
299 // keep going while we have not initialized, aren't shutting down, and haven't timed out
300 while(!bInitialized && (timeNow().tv_sec - commandTimeout.tv_sec < MAX_COMMAND_TIMEOUT_MINUTES * 60)){
301 // make sure we don't block forever...
302 fd_set listenSet;
303 struct timeval tm;
304 int nSelectReturn = 0;
305 FD_ZERO(&listenSet);
306 FD_SET(fileno(stdin), &listenSet);
307 tm.tv_sec = 10; // wait ten seconds...
308 tm.tv_usec = 0;
309 nSelectReturn = select(fileno(stdin)+1, &listenSet, NULL, NULL, &tm);
311 if(nSelectReturn != -1){
312 // try to read stdout...
313 if(FD_ISSET(fileno(stdin), &listenSet)){
314 int nDataRecievedCount = read(fileno(stdin), data, MAXRECV - 2);
316 if(nDataRecievedCount > 0){
317 int nIndex;
318 gchar * strMethod = NULL;
319 gchar * strType = NULL;
320 gchar * strDevice = NULL;
321 gchar * strBits = NULL;
322 gchar * strFilter = NULL;
323 gchar * strMaxLines = NULL;
324 gchar * cpdata = data; //(gchar *) strdup(data);
326 // make sure we end the string...
327 data[nDataRecievedCount] = ':';
328 data[nDataRecievedCount+1] = '\0';
330 // clear the bpf filter...
331 JCONFIG_BPFFILTERS_SETNONE;
332 // reset the device name
333 if(strlen(strDeviceBackup) > 0){
334 strcpy(jconfig_Settings.deviceName, strDeviceBackup);
336 // reset the bit values
337 onoffBitValues = bBitValuesBackup;
339 // remove any control charaters...
340 for(nIndex = 0; nIndex<nDataRecievedCount; nIndex++){
341 if(iscntrl(data[nIndex])){
342 // this is a control character - change it to a :
343 data[nIndex] = ':';
347 strMethod = get_next_token_colon_delim(&cpdata);
348 strType = get_next_token_colon_delim(&cpdata);
349 strDevice = get_next_token_colon_delim(&cpdata);
350 strBits = get_next_token_colon_delim(&cpdata);
351 strFilter = get_next_token_colon_delim(&cpdata);
352 strMaxLines = get_next_token_colon_delim(&cpdata);
354 if(strncmp(strMethod, "listen", strlen("listen")) == 0){
355 gchar firstAnswer[16384];
356 int nStrMaxLines = 0;
357 gboolean bIsRequestGood = TRUE;
359 if(strMaxLines)
360 nStrMaxLines = atoi(strMaxLines);
362 debug(LOG_DEBUG, "Got listen request");
363 // got a listen request
366 if(strFilter){
367 // set the filter...
368 const char * strFilterResult = jutil_ValidateBPFFilter(strFilter);
369 if(!strFilterResult){
370 // good - set filter
371 JCONFIG_BPFFILTERS_SETSELECTEDFILTER(JCONFIG_BPFFILTERS_LEN);
372 jconfig_AddBpfFilter("<fromlisten>", strFilter);
373 }else{
374 debug(LOG_WARNING, "strFilter is BAD - %s", strFilterResult);
375 bIsRequestGood = FALSE;
376 printf(LISTEN_ERROR_ANSWER);
380 if(strDevice){
381 debug(LOG_DEBUG, "Setting device name '%s'", strDevice);
382 // set the device...
383 jconfig_Settings.deviceName = strDevice;
386 if(strBits && strcmp(strBits, "bits") == 0){
387 debug(LOG_DEBUG, "Setting bits");
388 // set bits...
389 onoffBitValues = TRUE;
393 if(bIsRequestGood){
394 if(nStrMaxLines != 0)
395 nLineCount = nStrMaxLines;
397 sprintf(firstAnswer, "listen:ASCII:%d:ACK:%s:%s:%s:%s\n\n", ourpid, strDevice, strBits, strFilter, strMaxLines);
398 debug(LOG_DEBUG,"sending '%s'", firstAnswer);
399 printf(firstAnswer);
400 bInitialized = TRUE;
401 } else {
402 printf(LISTEN_ERROR_ANSWER);
404 fflush(NULL);
405 // reset the timeout timer..
406 g_get_current_time(&commandTimeout);
408 } // if recv
409 } // if FD_ISSET
410 } // select
411 } // while
412 if(!(timeNow().tv_sec - commandTimeout.tv_sec < MAX_COMMAND_TIMEOUT_MINUTES * 60)){
413 // send timeout error
414 debug(LOG_NOTICE, "Timed out while waiting for listen command - waited %d seconds", timeNow().tv_sec - commandTimeout.tv_sec);
415 printf(ERROR_MAXIMUM_TIMEOUT_EXPIRED);fflush(NULL);
417 return bInitialized;
420 static gboolean juiadisplay_PreSetup() {
421 setvbuf(stdin, NULL, _IOLBF, 0);
422 setvbuf(stdout, NULL, _IOLBF, 0);
423 return parseListenLineAndConfig();
426 static void juiadisplay_Setup() {
427 displayStreamsMutex = g_mutex_new();
429 jprocessor_SetProcessStreamsFunc((ProcessStreamsFunc) processStreamsFunc);
430 onoffBitValues = FALSE;
431 onoffPackets = FALSE;
434 static gboolean juiadisplay_PreRunSetup() {
435 return TRUE;
438 static void juiadisplay_PreRun() {
441 static gboolean juiadisplay_Run() {
442 networkConnectionLoop();
443 return FALSE;
446 static void juiadisplay_Shutdown() {
449 static void juiadisplay_DrawStatus(const gchar *msg) {
452 static int juiadisplay_ProcessArgument(const gchar **arg, int argc) {
453 if (!strcmp(*arg, "-b") || !strcmp(*arg, "--bit-units")) {
454 onoffBitValues = TRUE;
455 return 1;
457 return 0;
460 jbase_display juiadisplay_Functions = {
461 TRUE,
462 juiadisplay_PreSetup,
463 juiadisplay_Setup,
464 juiadisplay_PreRunSetup,
465 juiadisplay_PreRun,
466 juiadisplay_Run,
467 juiadisplay_Shutdown,
468 juiadisplay_DrawStatus,
469 juiadisplay_ProcessArgument
472 #else
474 jbase_display juiadisplay_Functions = { FALSE };
476 #endif