~ubuntu-branches/ubuntu/natty/ntop/natty

« back to all changes in this revision

Viewing changes to plugins/rrdPlugin.c

  • Committer: Bazaar Package Importer
  • Author(s): Ola Lundqvist
  • Date: 2005-01-30 21:59:13 UTC
  • mfrom: (2.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20050130215913-xc3ke963bw49b3k4
Tags: 2:3.0-5
* Updated README.Debian file so users will understand what to do at
  install, closes: #291794, #287802.
* Updated ntop init script to give better output.
* Also changed log directory from /var/lib/ntop to /var/log/ntop,
  closes: #252352.
* Quoted the interface list to allow whitespace, closes: #267248.
* Added a couple of logcheck ignores, closes: #269321, #269319.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright (C) 2002-04 Luca Deri <deri@ntop.org>
 
3
 *
 
4
 *                     http://www.ntop.org/
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
19
 */
 
20
 
 
21
/* This plugin works only with threads */
 
22
 
 
23
/* #define RRD_DEBUG  8 */
 
24
 
 
25
/*
 
26
 
 
27
Plugin History
 
28
 
 
29
1.0     Initial release
 
30
1.0.1   Added Flows
 
31
1.0.2   Added Matrix
 
32
1.0.3
 
33
2.0     Rolled major version due to new interface parameter.
 
34
2.1     Added tests/creates for rrd and subdirectories, fixed timer,
 
35
        --reuse-rrd-graphics etc.
 
36
2.1.1   Fixed hosts / interface bug (Luca)
 
37
2.1.2   Added status message
 
38
2.2     Version roll (preparatory) for ntop 2.2
 
39
2.2a    Multiple RRAs
 
40
2.2b    Large rrd population option
 
41
2.3     Updates, fixes, etc ... for ntop 2.3
 
42
 
 
43
Remember, there are TWO paths into this - one is through the main loop,
 
44
if the plugin is active, the other is through the http function if the
 
45
plugin is NOT active.  So initialize stuff in BOTH places!
 
46
 
 
47
 
 
48
Aberrant RRD Behavior (http://cricket.sourceforge.net/aberrant/) 
 
49
patch courtesy of Dominique Karg <dk@ipsoluciones.com>
 
50
 
 
51
2.4     General cleanup
 
52
*/
 
53
 
 
54
static const char *rrd_subdirs[] =
 
55
  { "graphics",    /* graphics sub directory - must be first */
 
56
    "flows",
 
57
    "interfaces",
 
58
  };
 
59
 
 
60
#include "ntop.h"
 
61
#include "globals-report.h"
 
62
 
 
63
#include <rrd.h>
 
64
 
 
65
static void setPluginStatus(char * status);
 
66
#ifdef CFG_MULTITHREADED
 
67
static PthreadMutex rrdMutex;
 
68
#endif
 
69
 
 
70
#ifdef WIN32
 
71
int optind, opterr;
 
72
#endif
 
73
 
 
74
static unsigned short initialized = 0, active = 0, dumpInterval, dumpDetail;
 
75
static unsigned short dumpDays, dumpHours, dumpMonths;
 
76
static char *hostsFilter = NULL;
 
77
static Counter numTotalRRDs = 0;
 
78
static unsigned long numRuns = 0, numRRDerrors = 0;
 
79
static time_t start_tm, end_tm, rrdTime;
 
80
 
 
81
#ifdef CFG_MULTITHREADED
 
82
pthread_t rrdThread;
 
83
#endif
 
84
 
 
85
static u_short dumpDomains, dumpFlows, dumpHosts, dumpInterfaces, dumpMatrix, shownCreate=0;
 
86
#ifndef WIN32
 
87
static u_short dumpPermissions;
 
88
#endif
 
89
 
 
90
static Counter rrdGraphicRequests=0;
 
91
 
 
92
static DIR * workDir;
 
93
static struct dirent *workDirent;
 
94
 
 
95
#ifdef RRD_DEBUG
 
96
char startTimeBuf[32], endTimeBuf[32], fileTimeBuf[32];
 
97
#endif
 
98
 
 
99
/* forward */
 
100
int sumCounter(char *rrdPath, char *rrdFilePath,
 
101
               char *startTime, char* endTime, Counter *total, float *average);
 
102
void graphCounter(char *rrdPath, char *rrdName, char *rrdTitle,
 
103
                  char *startTime, char* endTime, char* rrdPrefix);
 
104
void updateCounter(char *hostPath, char *key, Counter value);
 
105
void updateGauge(char *hostPath, char *key, Counter value);
 
106
void updateTrafficCounter(char *hostPath, char *key, TrafficCounter *counter);
 
107
char x2c(char *what);
 
108
void unescape_url(char *url);
 
109
void mkdir_p(char *path);
 
110
static int initRRDfunct(void);
 
111
static void termRRDfunct(void);
 
112
static void handleRRDHTTPrequest(char* url);
 
113
 
 
114
/* ************************************* */
 
115
 
 
116
static PluginInfo rrdPluginInfo[] = {
 
117
  {
 
118
    VERSION, /* current ntop version */
 
119
    "rrdPlugin",
 
120
    "This plugin is used to setup, activate and deactivate ntop's rrd support.<br>"
 
121
    "This plugin also produces the graphs of rrd data, available via a "
 
122
    "link from the various 'Info about host xxxxx' reports.",
 
123
    "2.4", /* version */
 
124
    "<a HREF=\"http://luca.ntop.org/\" alt=\"Luca's home page\">L.Deri</A>",
 
125
    "rrdPlugin", /* http://<host>:<port>/plugins/rrdPlugin */
 
126
    1, /* Active by default */
 
127
    1, /* Inactive setup */
 
128
    initRRDfunct, /* TermFunc   */
 
129
    termRRDfunct, /* TermFunc   */
 
130
    NULL, /* PluginFunc */
 
131
    handleRRDHTTPrequest,
 
132
    NULL, /* no capture */
 
133
    NULL /* no status */
 
134
  }
 
135
};
 
136
 
 
137
/* ****************************************************** */
 
138
 
 
139
static char **calcpr=NULL;
 
140
 
 
141
static void calfree (void) {
 
142
  if(calcpr) {
 
143
    long i;
 
144
    for(i=0;calcpr[i];i++){
 
145
      if(calcpr[i]){
 
146
        free(calcpr[i]);
 
147
      }
 
148
    }
 
149
    if(calcpr) {
 
150
      free(calcpr);
 
151
    }
 
152
  }
 
153
}
 
154
 
 
155
/* ******************************************* */
 
156
 
 
157
#ifdef WIN32
 
158
void revertSlash(char *str, int mode) {
 
159
  int i;
 
160
 
 
161
  for(i=0; str[i] != '\0'; i++)
 
162
    switch(mode) {
 
163
    case 0:
 
164
      if(str[i] == '/') str[i] = '\\';
 
165
      break;
 
166
    case 1:
 
167
      if(str[i] == '\\') str[i] = '/';
 
168
      break;
 
169
    }
 
170
}
 
171
 
 
172
void revertDoubleColumn(char *str) {
 
173
  int i, j;
 
174
  char str1[512];
 
175
 
 
176
  for(i=0, j=0; str[i] != '\0'; i++) {
 
177
    if(str[i] == ':') {
 
178
      str1[j++] = '\\';
 
179
      str1[j++] = str[i];
 
180
    } else {
 
181
      str1[j++] = str[i];
 
182
    }
 
183
  }
 
184
 
 
185
  str1[j] = '\0';
 
186
  strcpy(str, str1);
 
187
}
 
188
 
 
189
#endif
 
190
 
 
191
/* ******************************************* */
 
192
 
 
193
#ifdef WIN32
 
194
#define _mkdir(a) mkdir(a)
 
195
#else
 
196
#define _mkdir(a) mkdir(a, myGlobals.rrdDirectoryPermissions)
 
197
#endif
 
198
 
 
199
void mkdir_p(char *path) {
 
200
  int i, rc;
 
201
 
 
202
  if(path == NULL) {
 
203
    traceEvent(CONST_TRACE_NOISY, "RRD: mkdir(null) skipped");
 
204
    return;
 
205
  }
 
206
 
 
207
#ifdef WIN32
 
208
  revertSlash(path, 0);
 
209
#endif
 
210
 
 
211
  /* Start at 1 to skip the root */
 
212
  for(i=1; path[i] != '\0'; i++)
 
213
    if(path[i] == CONST_PATH_SEP) {
 
214
      path[i] = '\0';
 
215
#if RRD_DEBUG >= 3
 
216
      traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: calling mkdir(%s)", path);
 
217
#endif
 
218
      rc = _mkdir(path);
 
219
      if((rc != 0) && (errno != EEXIST) )
 
220
        traceEvent(CONST_TRACE_WARNING, "RRD: %s, error %d %s",
 
221
                   path,
 
222
                   errno,
 
223
                   strerror(errno));
 
224
      path[i] = CONST_PATH_SEP;
 
225
    }
 
226
 
 
227
#if RRD_DEBUG >= 2
 
228
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: calling mkdir(%s)", path);
 
229
#endif
 
230
  _mkdir(path);
 
231
  if((rc != 0) && (errno != EEXIST) )
 
232
    traceEvent(CONST_TRACE_WARNING, "RRD: %s, error %d %s",
 
233
               path,
 
234
               errno,
 
235
               strerror(errno));
 
236
}
 
237
 
 
238
/* ******************************************* */
 
239
 
 
240
static void fillupArgv(int argc, int maxArgc, char *argv[]) {
 
241
  int i;
 
242
 
 
243
  for(i=argc; i<maxArgc; i++)
 
244
    argv[i] = "";
 
245
 
 
246
  optind = 1;
 
247
}
 
248
 
 
249
/* ******************************************* */
 
250
 
 
251
int sumCounter(char *rrdPath, char *rrdFilePath,
 
252
               char *startTime, char* endTime, Counter *total, float *average) {
 
253
  char *argv[32], path[512];
 
254
  int argc = 0, rc;
 
255
  time_t        start,end;
 
256
  unsigned long step, ds_cnt,i;
 
257
  rrd_value_t   *data,*datai, _total, _val;
 
258
  char          **ds_namv;
 
259
 
 
260
  if(snprintf(path, sizeof(path), "%s/%s/%s",
 
261
           myGlobals.rrdPath, rrdPath, rrdFilePath) < 0)
 
262
    BufferTooShort();
 
263
 
 
264
#ifdef WIN32
 
265
  revertSlash(path, 0);
 
266
#endif
 
267
 
 
268
  argv[argc++] = "rrd_fetch";
 
269
  argv[argc++] = path;
 
270
  argv[argc++] = "AVERAGE";
 
271
  argv[argc++] = "--start";
 
272
  argv[argc++] = startTime;
 
273
  argv[argc++] = "--end";
 
274
  argv[argc++] = endTime;
 
275
 
 
276
#ifdef CFG_MULTITHREADED
 
277
  accessMutex(&rrdMutex, "rrd_fetch");
 
278
#endif
 
279
  optind=0; /* reset gnu getopt */
 
280
  opterr=0; /* no error messages */
 
281
 
 
282
  fillupArgv(argc, sizeof(argv)/sizeof(char*), argv);
 
283
 
 
284
  rrd_clear_error();
 
285
  rc = rrd_fetch(argc, argv, &start, &end, &step, &ds_cnt, &ds_namv, &data);
 
286
 
 
287
#ifdef CFG_MULTITHREADED
 
288
  releaseMutex(&rrdMutex);
 
289
#endif
 
290
 
 
291
  if(rc == -1) {
 
292
#if RRD_DEBUG >= 3
 
293
    int x;
 
294
    
 
295
    for (x = 0; x < argc; x++)
 
296
      traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: argv[%d] = %s", x, argv[x]);
 
297
#endif
 
298
 
 
299
    return(-1);
 
300
  }
 
301
 
 
302
  datai  = data, _total = 0;
 
303
 
 
304
  for(i = start; i <= end; i += step) {
 
305
    _val = *(datai++);
 
306
 
 
307
    if(_val > 0)
 
308
      _total += _val;
 
309
  }
 
310
 
 
311
  for(i=0;i<ds_cnt;i++) free(ds_namv[i]);
 
312
  free(ds_namv);
 
313
  free(data);
 
314
 
 
315
  (*total)   = _total*step;
 
316
  (*average) = (float)(*total)/(float)(end-start);
 
317
  return(0);
 
318
}
 
319
 
 
320
 
 
321
/* ******************************************* */
 
322
 
 
323
static void listResource(char *rrdPath, char *rrdTitle,
 
324
                         char *startTime, char* endTime) {
 
325
  char path[512], url[256], formatBuf[32];
 
326
  DIR* directoryPointer=NULL;
 
327
  struct dirent* dp;
 
328
  int numEntries = 0;
 
329
 
 
330
  sendHTTPHeader(FLAG_HTTP_TYPE_HTML, 0, 1);
 
331
 
 
332
  if(snprintf(path, sizeof(path), "%s/%s", myGlobals.rrdPath, rrdPath) < 0)
 
333
    BufferTooShort();
 
334
 
 
335
#ifdef WIN32
 
336
  revertSlash(path, 0);
 
337
#endif
 
338
 
 
339
  directoryPointer = opendir(path);
 
340
 
 
341
  if(directoryPointer == NULL) {
 
342
    char buf[256];
 
343
    if(snprintf(buf, sizeof(buf), "<I>Unable to read directory %s</I>", path) < 0)
 
344
      BufferTooShort();
 
345
    printFlagedWarning(buf);
 
346
    printHTMLtrailer();
 
347
    return;
 
348
  }
 
349
 
 
350
  if(snprintf(path, sizeof(path), "Info about %s", rrdTitle) < 0)
 
351
    BufferTooShort();
 
352
 
 
353
  printHTMLheader(path, NULL, 0);
 
354
  sendString("<CENTER>\n<p ALIGN=right>\n");
 
355
 
 
356
  if(snprintf(url, sizeof(url),
 
357
              "/plugins/rrdPlugin?action=list&key=%s&title=%s&end=now",
 
358
              rrdPath, rrdTitle) < 0)
 
359
      BufferTooShort();
 
360
 
 
361
  if(snprintf(path, sizeof(path), "<b>View:</b> [ <A HREF=\"%s&start=now-1y\">year</A> ]", url) < 0)
 
362
      BufferTooShort();
 
363
    sendString(path);
 
364
  if(snprintf(path, sizeof(path), "[ <A HREF=\"%s&start=now-1m\">month</A> ]", url) < 0)
 
365
      BufferTooShort();
 
366
    sendString(path);
 
367
  if(snprintf(path, sizeof(path), "[ <A HREF=\"%s&start=now-1w\">week</A> ]", url) < 0)
 
368
      BufferTooShort();
 
369
    sendString(path);
 
370
  if(snprintf(path, sizeof(path), "[ <A HREF=\"%s&start=now-1d\">day</A> ]", url) < 0)
 
371
      BufferTooShort();
 
372
    sendString(path);
 
373
  if(snprintf(path, sizeof(path), "[ <A HREF=\"%s&start=now-12h\">last 12h</A> ]\n", url) < 0)
 
374
      BufferTooShort();
 
375
    sendString(path);
 
376
  if(snprintf(path, sizeof(path), "[ <A HREF=\"%s&start=now-6h\">last 6h</A> ]\n", url) < 0)
 
377
      BufferTooShort();
 
378
    sendString(path);
 
379
  if(snprintf(path, sizeof(path), "[ <A HREF=\"%s&start=now-1h\">last hour</A> ]&nbsp;\n", url) < 0)
 
380
      BufferTooShort();
 
381
    sendString(path);
 
382
 
 
383
  sendString("</p>\n<p>\n<TABLE BORDER=1 "TABLE_DEFAULTS">\n");
 
384
 
 
385
  sendString("<TR><TH "DARK_BG">Graph</TH><TH "DARK_BG">Total</TH></TR>\n");
 
386
 
 
387
  while((dp = readdir(directoryPointer)) != NULL) {
 
388
    char *rsrcName;
 
389
    Counter total;
 
390
    float  average;
 
391
    int rc, isGauge;
 
392
 
 
393
    if(dp->d_name[0] == '.')
 
394
      continue;
 
395
    else if(strlen(dp->d_name) < strlen(CONST_RRD_EXTENSION)+3)
 
396
      continue;
 
397
 
 
398
    rsrcName = &dp->d_name[strlen(dp->d_name)-strlen(CONST_RRD_EXTENSION)-3];
 
399
    if(strcmp(rsrcName, "Num"CONST_RRD_EXTENSION) == 0)
 
400
      isGauge = 1;
 
401
    else
 
402
      isGauge = 0;
 
403
 
 
404
    rsrcName = &dp->d_name[strlen(dp->d_name)-strlen(CONST_RRD_EXTENSION)];
 
405
    if(strcmp(rsrcName, CONST_RRD_EXTENSION))
 
406
      continue;
 
407
 
 
408
    rc = sumCounter(rrdPath, dp->d_name, startTime, endTime, &total, &average);
 
409
 
 
410
    if(isGauge
 
411
       || ((rc >= 0) && (total > 0))) {
 
412
      rsrcName[0] = '\0';
 
413
      rsrcName = dp->d_name;
 
414
 
 
415
      sendString("<TR><TD>\n");
 
416
 
 
417
      if(snprintf(path, sizeof(path), "<IMG SRC=\"/plugins/rrdPlugin?action=graph&key=%s/&name=%s&title=%s&start=%s&end=%s\"><P>\n",
 
418
               rrdPath, rsrcName, rsrcName, startTime, endTime) < 0)
 
419
        BufferTooShort();
 
420
      sendString(path);
 
421
 
 
422
      sendString("</TD><TD ALIGN=RIGHT>\n");
 
423
 
 
424
      /* printf("rsrcName: %s\n", rsrcName); */
 
425
 
 
426
      if(isGauge) {
 
427
        sendString("&nbsp;");
 
428
      } else {
 
429
        if((strncmp(rsrcName, "pkt", 3) == 0)
 
430
           || ((strlen(rsrcName) > 4) && (strcmp(&rsrcName[strlen(rsrcName)-4], "Pkts") == 0))) {
 
431
          if(snprintf(path, sizeof(path), "%s Pkt</TD>",
 
432
                      formatPkts(total, formatBuf, sizeof(formatBuf))) < 0)
 
433
            BufferTooShort();
 
434
        } else {
 
435
          if(snprintf(path, sizeof(path), "%s",
 
436
                      formatBytes(total, 1, formatBuf, sizeof(formatBuf))) < 0)
 
437
            BufferTooShort();
 
438
        }
 
439
        sendString(path);
 
440
      }
 
441
 
 
442
      sendString("</TD></TR>\n");
 
443
      numEntries++;
 
444
    }
 
445
  } /* while */
 
446
 
 
447
  closedir(directoryPointer);
 
448
 
 
449
  /* if(numEntries > 0) */ {
 
450
    sendString("</TABLE>\n");
 
451
  }
 
452
 
 
453
  sendString("</CENTER>");
 
454
  sendString("<br><b>NOTE: total and average values are NOT absolute but calculated on the specified time interval.</b>\n");
 
455
 
 
456
  printHTMLtrailer();
 
457
}
 
458
 
 
459
/* ******************************************* */
 
460
 
 
461
static int endsWith(char* label, char* pattern) {
 
462
  int lenLabel, lenPattern;
 
463
 
 
464
  lenLabel   = strlen(label);
 
465
  lenPattern = strlen(pattern);
 
466
 
 
467
  if(lenPattern >= lenLabel)
 
468
    return(0);
 
469
  else
 
470
    return(!strcmp(&label[lenLabel-lenPattern], pattern));
 
471
}
 
472
 
 
473
/* ******************************************* */
 
474
 
 
475
void graphCounter(char *rrdPath, char *rrdName, char *rrdTitle,
 
476
                  char *startTime, char* endTime, char *rrdPrefix) {
 
477
  char path[512], *argv[32], buf[384], buf1[384], fname[384], *label;
 
478
#ifdef HAVE_RRD_ABERRANT_BEHAVIOR
 
479
  char buf2[384], buf3[384], buf4[384];
 
480
#endif
 
481
  struct stat statbuf;
 
482
  int argc = 0, rc, x, y;
 
483
 
 
484
  if(snprintf(path, sizeof(path), "%s/%s%s.rrd", myGlobals.rrdPath, rrdPath, rrdName) < 0)
 
485
    BufferTooShort();
 
486
 
 
487
  /* startTime[4] skips the 'now-' */
 
488
  if(snprintf(fname, sizeof(fname), "%s/%s/%s-%s%s%s",
 
489
           myGlobals.rrdPath, rrd_subdirs[0], startTime, rrdPrefix, rrdName,
 
490
           CHART_FORMAT) < 0)
 
491
    BufferTooShort();
 
492
 
 
493
#ifdef WIN32
 
494
  revertSlash(path, 0);
 
495
  revertSlash(fname, 0);
 
496
#endif
 
497
 
 
498
  if(endsWith(rrdName, "Bytes")) label = "Bytes/sec";
 
499
  else if(endsWith(rrdName, "Pkts")) label = "Packets/sec";
 
500
  else label = rrdName;
 
501
 
 
502
  rrdGraphicRequests++;
 
503
 
 
504
  if(stat(path, &statbuf) == 0) {
 
505
    argv[argc++] = "rrd_graph";
 
506
    argv[argc++] = fname;
 
507
    argv[argc++] = "--lazy";
 
508
    argv[argc++] = "--imgformat";
 
509
    argv[argc++] = "PNG";
 
510
    argv[argc++] = "--vertical-label";
 
511
    argv[argc++] = label;
 
512
    argv[argc++] = "--start";
 
513
    argv[argc++] = startTime;
 
514
    argv[argc++] = "--end";
 
515
    argv[argc++] = endTime;
 
516
#ifdef CONST_RRD_DEFAULT_FONT_NAME
 
517
    argv[argc++] = "--font";
 
518
 #ifdef CONST_RRD_DEFAULT_FONT_PATH
 
519
    argv[argc++] = "DEFAULT:" CONST_RRD_DEFAULT_FONT_SIZE ":" \
 
520
                   CONST_RRD_DEFAULT_FONT_PATH CONST_RRD_DEFAULT_FONT_NAME; 
 
521
 #else
 
522
    argv[argc++] = "DEFAULT:" CONST_RRD_DEFAULT_FONT_SIZE ":" CONST_RRD_DEFAULT_FONT_NAME; 
 
523
 #endif
 
524
#endif
 
525
#ifdef WIN32
 
526
    revertDoubleColumn(path);
 
527
#endif
 
528
    if(snprintf(buf, sizeof(buf), "DEF:ctr=%s:counter:AVERAGE", path) < 0)
 
529
      BufferTooShort();
 
530
    argv[argc++] = buf;
 
531
    if(snprintf(buf1, sizeof(buf1), "AREA:ctr#00a000:%s", rrdTitle) < 0)
 
532
      BufferTooShort();
 
533
    argv[argc++] = buf1;
 
534
    argv[argc++] = "GPRINT:ctr:MIN:Min\\: %3.1lf%s";
 
535
    argv[argc++] = "GPRINT:ctr:MAX:Max\\: %3.1lf%s";
 
536
    argv[argc++] = "GPRINT:ctr:AVERAGE:Avg\\: %3.1lf%s";
 
537
    argv[argc++] = "GPRINT:ctr:LAST:Current\\: %3.1lf%s";
 
538
#ifdef HAVE_RRD_ABERRANT_BEHAVIOR
 
539
    if(snprintf(buf2, sizeof(buf2), "DEF:pred=%s:counter:HWPREDICT", path) < 0)
 
540
      BufferTooShort();
 
541
    argv[argc++] = buf2;
 
542
    if(snprintf(buf3, sizeof(buf3), "DEF:dev=%s:counter:DEVPREDICT", path) < 0)
 
543
      BufferTooShort();
 
544
    argv[argc++] = buf3;
 
545
    if(snprintf(buf4, sizeof(buf4), "DEF:fail=%s:counter:FAILURES", path) < 0)
 
546
      BufferTooShort();
 
547
    argv[argc++] = buf4;
 
548
    argv[argc++] = "TICK:fail#ffffa0:1.0:Anomalia";
 
549
    argv[argc++] = "CDEF:upper=pred,dev,2,*,+";
 
550
    argv[argc++] = "CDEF:lower=pred,dev,2,*,-";
 
551
    argv[argc++] = "LINE1:upper#ff0000:Upper";
 
552
    argv[argc++] = "LINE2:lower#ff0000:Lower";
 
553
#endif
 
554
 
 
555
#ifdef CFG_MULTITHREADED
 
556
    accessMutex(&rrdMutex, "rrd_graph");
 
557
#endif
 
558
    optind=0; /* reset gnu getopt */
 
559
    opterr=0; /* no error messages */
 
560
 
 
561
    fillupArgv(argc, sizeof(argv)/sizeof(char*), argv);
 
562
    rrd_clear_error();
 
563
    rc = rrd_graph(argc, argv, &calcpr, &x, &y);
 
564
 
 
565
    calfree();
 
566
 
 
567
    if(rc == 0) {
 
568
      sendHTTPHeader(FLAG_HTTP_TYPE_PNG, 0, 1);
 
569
      sendGraphFile(fname, 0);
 
570
      unlink(fname);
 
571
    } else {
 
572
#if RRD_DEBUG >= 3
 
573
      for (x = 0; x < argc; x++)
 
574
        traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: argv[%d] = %s", x, argv[x]);
 
575
#endif
 
576
      
 
577
      sendHTTPHeader(FLAG_HTTP_TYPE_HTML, 0, 1);
 
578
      printHTMLheader("RRD Graph", NULL, 0);
 
579
      if(snprintf(path, sizeof(path),
 
580
                  "<I>Error while building graph of the requested file. %s</I>",
 
581
                  rrd_get_error()) < 0)
 
582
        BufferTooShort();
 
583
      printFlagedWarning(path);
 
584
      rrd_clear_error();
 
585
    }
 
586
 
 
587
#ifdef CFG_MULTITHREADED
 
588
    releaseMutex(&rrdMutex);
 
589
#endif    
 
590
  } else {
 
591
    sendHTTPHeader(FLAG_HTTP_TYPE_HTML, 0, 1);
 
592
    printHTMLheader("RRD Graph", NULL, 0);
 
593
    printFlagedWarning("<I>Error while building graph of the requested file "
 
594
                       "(unknown RRD file)</I>");
 
595
  }
 
596
}
 
597
 
 
598
/* ******************************* */
 
599
 
 
600
static void updateRRD(char *hostPath, char *key, Counter value, int isCounter) {
 
601
  char path[512], *argv[32], cmd[64];
 
602
  struct stat statbuf;
 
603
  int argc = 0, rc, createdCounter = 0, i;
 
604
 
 
605
  if(value == 0) return;
 
606
 
 
607
  if(snprintf(path, sizeof(path), "%s%s.rrd", hostPath, key) < 0)
 
608
    BufferTooShort();
 
609
 
 
610
  /* Avoid path problems */
 
611
  for(i=strlen(hostPath); i<strlen(path); i++)
 
612
    if(path[i] == '/') path[i]='_';
 
613
 
 
614
#ifdef WIN32
 
615
  revertSlash(path, 0);
 
616
#endif
 
617
 
 
618
  if(stat(path, &statbuf) != 0) {
 
619
    char startStr[32], stepStr[32], counterStr[64], intervalStr[32];
 
620
    char minStr[32], maxStr[32], daysStr[32], monthsStr[32];
 
621
#ifdef HAVE_RRD_ABERRANT_BEHAVIOR
 
622
    char tempStr[64];
 
623
#endif
 
624
    int step = dumpInterval;
 
625
    int value1, value2;
 
626
    unsigned long topValue;
 
627
 
 
628
    topValue = 1000000000 /* 1 Gbit/s */;
 
629
 
 
630
    if(strncmp(key, "pkt", 3) == 0) {
 
631
      topValue /= 8*64 /* 64 bytes is the shortest packet we care of */;
 
632
    } else {
 
633
      topValue /= 8 /* 8 bytes */;
 
634
    }
 
635
 
 
636
    argv[argc++] = "rrd_create";
 
637
    argv[argc++] = path;
 
638
    argv[argc++] = "--start";
 
639
    if(snprintf(startStr, sizeof(startStr), "%u",
 
640
             rrdTime-1 /* -1 avoids subsequent rrd_update call problems */) < 0)
 
641
      BufferTooShort();
 
642
    argv[argc++] = startStr;
 
643
 
 
644
    argv[argc++] = "--step";
 
645
    if(snprintf(stepStr, sizeof(stepStr), "%u", dumpInterval) < 0)
 
646
      BufferTooShort();
 
647
    argv[argc++] = stepStr;
 
648
 
 
649
    if(isCounter) {
 
650
      if(snprintf(counterStr, sizeof(counterStr), "DS:counter:COUNTER:%d:0:%u", step, topValue) < 0)
 
651
        BufferTooShort();
 
652
    } else {
 
653
      /* Unlimited */
 
654
      if(snprintf(counterStr, sizeof(counterStr), "DS:counter:GAUGE:%d:0:U", step) < 0)
 
655
        BufferTooShort();
 
656
    }
 
657
    argv[argc++] = counterStr;
 
658
 
 
659
    /* dumpInterval is in seconds.  There are 60m*60s = 3600s in an hour.
 
660
     * value1 is the # of dumpIntervals per hour
 
661
     */
 
662
    value1 = (60*60 + dumpInterval - 1) / dumpInterval;
 
663
    /* value2 is the # of value1 (hours) for dumpHours hours */
 
664
    value2 = value1 * dumpHours;
 
665
    if(snprintf(intervalStr, sizeof(intervalStr), "RRA:AVERAGE:%.1f:1:%d", 0.5, value2) < 0)
 
666
      BufferTooShort();
 
667
    argv[argc++] = intervalStr;
 
668
 
 
669
    /* Store the MIN/MAX 5m value for a # of hours */
 
670
    if(snprintf(minStr, sizeof(minStr), "RRA:MIN:%.1f:1:%d",
 
671
                0.5, dumpHours > 0 ? dumpHours : DEFAULT_RRD_HOURS) < 0)
 
672
      BufferTooShort();
 
673
    argv[argc++] = minStr;
 
674
    if(snprintf(maxStr, sizeof(maxStr), "RRA:MAX:%.1f:1:%d",
 
675
               0.5, dumpHours > 0 ? dumpHours : DEFAULT_RRD_HOURS) < 0)
 
676
      BufferTooShort();
 
677
    argv[argc++] = maxStr;
 
678
 
 
679
    if(dumpDays > 0) {
 
680
      if(snprintf(daysStr, sizeof(daysStr), "RRA:AVERAGE:%.1f:%d:%d",
 
681
                  0.5, value1, dumpDays * 24) < 0)
 
682
        BufferTooShort();
 
683
      argv[argc++] = daysStr;
 
684
    }
 
685
 
 
686
    /* Compute the rollup - how many dumpInterval seconds interval are in a day */
 
687
    value1 = (24*60*60 + dumpInterval - 1) / dumpInterval;
 
688
    if(dumpMonths > 0) {
 
689
      if(snprintf(monthsStr, sizeof(monthsStr), "RRA:AVERAGE:%.1f:%d:%d",
 
690
                  0.5, value1, dumpMonths * 30) < 0)
 
691
        BufferTooShort();
 
692
      argv[argc++] = monthsStr;
 
693
    }
 
694
 
 
695
#ifdef HAVE_RRD_ABERRANT_BEHAVIOR
 
696
    if(snprintf(tempStr, sizeof(tempStr), "RRA:HWPREDICT:1440:0.1:0.0035:20") < 0)
 
697
      BufferTooShort();
 
698
    argv[argc++] = tempStr;
 
699
#endif
 
700
 
 
701
#if DEBUG
 
702
    if(shownCreate == 0) {
 
703
      char buf[LEN_GENERAL_WORK_BUFFER];
 
704
      int i;
 
705
 
 
706
      shownCreate=1;
 
707
 
 
708
      memset(buf, 0, sizeof(buf));
 
709
 
 
710
      if(snprintf(buf, sizeof(buf), "%s", argv[4]) < 0)
 
711
        BufferTooShort();
 
712
 
 
713
      for (i=5; i<argc; i++) {
 
714
        strncat(buf, " ", (sizeof(buf) - strlen(buf) - 1));
 
715
        strncat(buf, argv[i], (sizeof(buf) - strlen(buf) - 1));
 
716
      }
 
717
 
 
718
      traceEvent(CONST_TRACE_INFO, "RRD: rrdtool create --start now-1 file %s", buf);
 
719
    }
 
720
#endif
 
721
 
 
722
#ifdef CFG_MULTITHREADED
 
723
    accessMutex(&rrdMutex, "rrd_create");
 
724
#endif
 
725
    optind=0; /* reset gnu getopt */
 
726
    opterr=0; /* no error messages */
 
727
 
 
728
    fillupArgv(argc, sizeof(argv)/sizeof(char*), argv);
 
729
    rrd_clear_error();
 
730
    rc = rrd_create(argc, argv);
 
731
 
 
732
    if(rrd_test_error()) {
 
733
#if RRD_DEBUG >= 3
 
734
      int x;
 
735
      
 
736
      for (x = 0; x < argc; x++)
 
737
        traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: argv[%d] = %s", x, argv[x]);
 
738
#endif
 
739
      
 
740
      traceEvent(CONST_TRACE_WARNING, "RRD: rrd_create(%s) error: %s", path, rrd_get_error());
 
741
      rrd_clear_error();
 
742
      numRRDerrors++;
 
743
    }
 
744
 
 
745
#ifdef CFG_MULTITHREADED
 
746
    releaseMutex(&rrdMutex);
 
747
#endif
 
748
 
 
749
#if RRD_DEBUG > 0
 
750
    traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: rrd_create(%s, %s, %u)=%d",
 
751
               hostPath, key, (unsigned long)value, rc);
 
752
#endif
 
753
    createdCounter = 1;
 
754
  }
 
755
 
 
756
#if RRD_DEBUG > 0
 
757
  argc = 0;
 
758
  argv[argc++] = "rrd_last";
 
759
  argv[argc++] = path;
 
760
 
 
761
#ifdef CFG_MULTITHREADED
 
762
  accessMutex(&rrdMutex, "rrd_last");
 
763
#endif
 
764
  optind=0; /* reset gnu getopt */
 
765
  opterr=0; /* no error messages */
 
766
 
 
767
  fillupArgv(argc, sizeof(argv)/sizeof(char*), argv);
 
768
  rrd_clear_error();
 
769
  if(rrd_last(argc, argv) >= rrdTime) {
 
770
    traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: WARNING rrd_update not performed (RRD already updated)");
 
771
  }
 
772
 
 
773
#ifdef CFG_MULTITHREADED
 
774
  releaseMutex(&rrdMutex);
 
775
#endif
 
776
#endif
 
777
 
 
778
  argc = 0;
 
779
  argv[argc++] = "rrd_update";
 
780
  argv[argc++] = path;
 
781
 
 
782
  if((!createdCounter) && (numRuns == 1)) {
 
783
    /* This is the first rrd update hence in order to avoid
 
784
       wrong traffic peaks we set the value for the counter on the previous
 
785
       interval to unknown
 
786
 
 
787
       # From: Alex van den Bogaerdt <alex@ergens.op.HET.NET>
 
788
       # Date: Fri, 12 Jul 2002 01:32:45 +0200 (CEST)
 
789
       # Subject: Re: [rrd-users] Re: To DERIVE or not to DERIVE
 
790
 
 
791
       [...]
 
792
 
 
793
       Oops.  OK, so the counter is unknown.  Indeed one needs to discard
 
794
       the first interval between reboot time and poll time in that case.
 
795
 
 
796
       [...]
 
797
 
 
798
       But this would also make the next interval unknown.  My suggestion:
 
799
       insert an unknown at that time minus one second, enter the fetched
 
800
       value at that time.
 
801
 
 
802
       cheers,
 
803
       --
 
804
       __________________________________________________________________
 
805
       / alex@slot.hollandcasino.nl                  alex@ergens.op.het.net \
 
806
 
 
807
    */
 
808
 
 
809
    if(snprintf(cmd, sizeof(cmd), "%u:u", rrdTime-10) < 0) /* u = undefined */
 
810
      BufferTooShort();
 
811
  } else {
 
812
    if(snprintf(cmd, sizeof(cmd), "%u:%u", rrdTime, (unsigned long)value) < 0)
 
813
      BufferTooShort();
 
814
  }
 
815
 
 
816
  argv[argc++] = cmd;
 
817
 
 
818
#ifdef CFG_MULTITHREADED
 
819
  accessMutex(&rrdMutex, "rrd_update");
 
820
#endif
 
821
  optind=0; /* reset gnu getopt */
 
822
  opterr=0; /* no error messages */
 
823
 
 
824
  fillupArgv(argc, sizeof(argv)/sizeof(char*), argv);
 
825
  rrd_clear_error();
 
826
  rc = rrd_update(argc, argv);
 
827
 
 
828
  numTotalRRDs++;
 
829
 
 
830
  if(rrd_test_error()) {
 
831
    int x;
 
832
    char *rrdError;
 
833
 
 
834
#if RRD_DEBUG >= 3
 
835
    for (x = 0; x < argc; x++)
 
836
      traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: argv[%d] = %s", x, argv[x]);
 
837
#endif
 
838
 
 
839
    numRRDerrors++;
 
840
    rrdError = rrd_get_error();
 
841
    if(rrdError != NULL) {
 
842
      traceEvent(CONST_TRACE_WARNING, "RRD: rrd_update(%s) error: %s", path, rrdError);
 
843
      traceEvent(CONST_TRACE_NOISY, "RRD: call stack (counter created: %d):", createdCounter);
 
844
      for (x = 0; x < argc; x++)
 
845
        traceEvent(CONST_TRACE_NOISY, "RRD:   argv[%d]: %s", x, argv[x]);
 
846
 
 
847
      if(!strcmp(rrdError, "error: illegal attempt to update using time")) {
 
848
        char errTimeBuf1[32], errTimeBuf2[32], errTimeBuf3[32];
 
849
        time_t rrdLast;
 
850
        struct tm workT;
 
851
 
 
852
        strftime(errTimeBuf1, sizeof(errTimeBuf1), CONST_LOCALE_TIMESPEC, localtime_r(&myGlobals.actTime, &workT));
 
853
        strftime(errTimeBuf2, sizeof(errTimeBuf2), CONST_LOCALE_TIMESPEC, localtime_r(&rrdTime, &workT));
 
854
        argc = 0;
 
855
        argv[argc++] = "rrd_last";
 
856
        argv[argc++] = path;
 
857
 
 
858
        rrdLast = rrd_last(argc, argv);
 
859
        strftime(errTimeBuf3, sizeof(errTimeBuf3), CONST_LOCALE_TIMESPEC, localtime_r(&rrdLast, &workT));
 
860
        traceEvent(CONST_TRACE_WARNING,
 
861
                   "RRD: actTime = %d(%s), rrdTime %d(%s), lastUpd %d(%s)",
 
862
                   myGlobals.actTime,
 
863
                   errTimeBuf1,
 
864
                   rrdTime,
 
865
                   errTimeBuf2,
 
866
                   rrdLast,
 
867
                   rrdLast == -1 ? "rrdlast ERROR" : errTimeBuf3);
 
868
      } else if(strstr(rrdError, "is not an RRD file")) {
 
869
        unlink(path);
 
870
      }
 
871
 
 
872
      rrd_clear_error();
 
873
    }
 
874
  }
 
875
 
 
876
#ifdef CFG_MULTITHREADED
 
877
  releaseMutex(&rrdMutex);
 
878
#endif
 
879
}
 
880
 
 
881
/* ******************************* */
 
882
 
 
883
void updateCounter(char *hostPath, char *key, Counter value) {
 
884
  updateRRD(hostPath, key, value, 1);
 
885
}
 
886
 
 
887
/* ******************************* */
 
888
 
 
889
void updateGauge(char *hostPath, char *key, Counter value) {
 
890
  updateRRD(hostPath, key, value, 0);
 
891
}
 
892
 
 
893
/* ******************************* */
 
894
 
 
895
void updateTrafficCounter(char *hostPath, char *key, TrafficCounter *counter) {
 
896
  if(counter->modified) {
 
897
    updateCounter(hostPath, key, counter->value);
 
898
    counter->modified = 0;
 
899
  }
 
900
}
 
901
 
 
902
/* ******************************* */
 
903
 
 
904
char x2c(char *what) {
 
905
  char digit;
 
906
 
 
907
  digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
 
908
  digit *= 16;
 
909
  digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
 
910
  return(digit);
 
911
}
 
912
 
 
913
/* ******************************* */
 
914
 
 
915
void unescape_url(char *url) {
 
916
  register int x,y;
 
917
 
 
918
  for(x=0,y=0;url[y];++x,++y) {
 
919
    if((url[x] = url[y]) == '%') {
 
920
      url[x] = x2c(&url[y+1]);
 
921
      y+=2;
 
922
    }
 
923
  }
 
924
  url[x] = '\0';
 
925
}
 
926
 
 
927
/* ******************************* */
 
928
 
 
929
#ifndef WIN32
 
930
static void setGlobalPermissions(int permissionsFlag) {
 
931
  switch (permissionsFlag) {
 
932
    case CONST_RRD_PERMISSIONS_GROUP:
 
933
      myGlobals.rrdDirectoryPermissions = CONST_RRD_D_PERMISSIONS_GROUP;
 
934
      myGlobals.rrdUmask = CONST_RRD_UMASK_GROUP;
 
935
      break;
 
936
    case CONST_RRD_PERMISSIONS_EVERYONE:
 
937
      myGlobals.rrdDirectoryPermissions = CONST_RRD_D_PERMISSIONS_EVERYONE;
 
938
      myGlobals.rrdUmask = CONST_RRD_UMASK_EVERYONE;
 
939
      break;
 
940
    default:
 
941
      myGlobals.rrdDirectoryPermissions = CONST_RRD_D_PERMISSIONS_PRIVATE;
 
942
      myGlobals.rrdUmask = CONST_RRD_UMASK_PRIVATE;
 
943
      break;
 
944
  }
 
945
}
 
946
#endif
 
947
 
 
948
/* ******************************* */
 
949
 
 
950
static void commonRRDinit(void) {
 
951
  char value[1024];
 
952
 
 
953
  shownCreate=0;
 
954
 
 
955
  if(fetchPrefsValue("rrd.dataDumpInterval", value, sizeof(value)) == -1) {
 
956
    if(snprintf(value, sizeof(value), "%d", DEFAULT_RRD_INTERVAL) < 0)
 
957
      BufferTooShort();
 
958
    storePrefsValue("rrd.dataDumpInterval", value);
 
959
    dumpInterval = DEFAULT_RRD_INTERVAL;
 
960
  } else {
 
961
    dumpInterval = atoi(value);
 
962
  }
 
963
 
 
964
  if(fetchPrefsValue("rrd.dataDumpHours", value, sizeof(value)) == -1) {
 
965
    if(snprintf(value, sizeof(value), "%d", DEFAULT_RRD_HOURS) < 0)
 
966
      BufferTooShort();
 
967
    storePrefsValue("rrd.dataDumpHours", value);
 
968
    dumpHours = DEFAULT_RRD_HOURS;
 
969
  } else {
 
970
    dumpHours = atoi(value);
 
971
  }
 
972
 
 
973
  if(fetchPrefsValue("rrd.dataDumpDays", value, sizeof(value)) == -1) {
 
974
    if(snprintf(value, sizeof(value), "%d", DEFAULT_RRD_DAYS) < 0)
 
975
      BufferTooShort();
 
976
    storePrefsValue("rrd.dataDumpDays", value);
 
977
    dumpDays = DEFAULT_RRD_DAYS;
 
978
  } else {
 
979
    dumpDays = atoi(value);
 
980
  }
 
981
 
 
982
  if(fetchPrefsValue("rrd.dataDumpMonths", value, sizeof(value)) == -1) {
 
983
    if(snprintf(value, sizeof(value), "%d", DEFAULT_RRD_MONTHS) < 0)
 
984
      BufferTooShort();
 
985
    storePrefsValue("rrd.dataDumpMonths", value);
 
986
    dumpMonths = DEFAULT_RRD_MONTHS;
 
987
  } else {
 
988
    dumpMonths = atoi(value);
 
989
  }
 
990
 
 
991
  if(fetchPrefsValue("rrd.dataDumpDomains", value, sizeof(value)) == -1) {
 
992
    storePrefsValue("rrd.dataDumpDomains", "0");
 
993
    dumpDomains = 0;
 
994
  } else {
 
995
    dumpDomains = atoi(value);
 
996
  }
 
997
 
 
998
  if(fetchPrefsValue("rrd.dataDumpFlows", value, sizeof(value)) == -1) {
 
999
    storePrefsValue("rrd.dataDumpFlows", "0");
 
1000
    dumpFlows = 0;
 
1001
  } else {
 
1002
    dumpFlows = atoi(value);
 
1003
  }
 
1004
 
 
1005
  if(fetchPrefsValue("rrd.dataDumpHosts", value, sizeof(value)) == -1) {
 
1006
    storePrefsValue("rrd.dataDumpHosts", "0");
 
1007
    dumpHosts = 0;
 
1008
  } else {
 
1009
    dumpHosts = atoi(value);
 
1010
  }
 
1011
 
 
1012
  if(fetchPrefsValue("rrd.dataDumpInterfaces", value, sizeof(value)) == -1) {
 
1013
    storePrefsValue("rrd.dataDumpInterfaces", "1");
 
1014
    dumpInterfaces = 1;
 
1015
  } else {
 
1016
    dumpInterfaces = atoi(value);
 
1017
  }
 
1018
 
 
1019
  if(fetchPrefsValue("rrd.dataDumpMatrix", value, sizeof(value)) == -1) {
 
1020
    storePrefsValue("rrd.dataDumpMatrix", "0");
 
1021
    dumpMatrix = 0;
 
1022
  } else {
 
1023
    dumpMatrix = atoi(value);
 
1024
  }
 
1025
 
 
1026
  if(hostsFilter != NULL) free(hostsFilter);
 
1027
  if(fetchPrefsValue("rrd.hostsFilter", value, sizeof(value)) == -1) {
 
1028
    storePrefsValue("rrd.hostsFilter", "");
 
1029
    hostsFilter  = strdup("");
 
1030
  } else {
 
1031
    hostsFilter  = strdup(value);
 
1032
  }
 
1033
 
 
1034
  if(fetchPrefsValue("rrd.dataDumpDetail", value, sizeof(value)) == -1) {
 
1035
    if(snprintf(value, sizeof(value), "%d", CONST_RRD_DETAIL_DEFAULT) < 0)
 
1036
      BufferTooShort();
 
1037
    storePrefsValue("rrd.dataDumpDetail", value);
 
1038
    dumpDetail = CONST_RRD_DETAIL_DEFAULT;
 
1039
  } else {
 
1040
    dumpDetail  = atoi(value);
 
1041
  }
 
1042
 
 
1043
  if(fetchPrefsValue("rrd.rrdPath", value, sizeof(value)) == -1) {
 
1044
    char *thePath = "/rrd";
 
1045
    int len = strlen(myGlobals.dbPath)+strlen(thePath)+1;
 
1046
 
 
1047
    if(myGlobals.rrdPath) free(myGlobals.rrdPath);
 
1048
    myGlobals.rrdPath = (char*)malloc(len);
 
1049
    if(snprintf(myGlobals.rrdPath, len, "%s%s", myGlobals.dbPath, thePath) < 0)
 
1050
      BufferTooShort();
 
1051
    storePrefsValue("rrd.rrdPath", myGlobals.rrdPath);
 
1052
  } else {
 
1053
    int vlen = strlen(value)+1;
 
1054
 
 
1055
    myGlobals.rrdPath  = (char*)malloc(vlen);
 
1056
    unescape(myGlobals.rrdPath, vlen, value);
 
1057
  }
 
1058
 
 
1059
#ifndef WIN32
 
1060
  if(fetchPrefsValue("rrd.permissions", value, sizeof(value)) == -1) {
 
1061
    if(snprintf(value, sizeof(value), "%d", DEFAULT_RRD_PERMISSIONS) < 0)
 
1062
      BufferTooShort();
 
1063
    storePrefsValue("rrd.permissions", value);
 
1064
    dumpPermissions = DEFAULT_RRD_PERMISSIONS;
 
1065
  } else {
 
1066
    dumpPermissions = atoi(value);
 
1067
  }
 
1068
  setGlobalPermissions(dumpPermissions);
 
1069
  traceEvent(CONST_TRACE_INFO, "RRD: Mask for new directories is %04o",
 
1070
             myGlobals.rrdDirectoryPermissions);
 
1071
  umask(myGlobals.rrdUmask);
 
1072
  traceEvent(CONST_TRACE_INFO, "RRD: Mask for new files is %04o",
 
1073
             myGlobals.rrdUmask);
 
1074
#endif
 
1075
 
 
1076
#ifdef RRD_DEBUG
 
1077
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: Parameters:");
 
1078
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpInterval %d seconds", dumpInterval);
 
1079
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpHours %d hours by %d seconds", dumpHours, dumpInterval);
 
1080
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpDays %d days by hour", dumpDays);
 
1081
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpMonths %d months by day", dumpMonths);
 
1082
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpDomains %s", dumpDomains == 0 ? "no" : "yes");
 
1083
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpFlows %s", dumpFlows == 0 ? "no" : "yes");
 
1084
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpHosts %s", dumpHosts == 0 ? "no" : "yes");
 
1085
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpInterfaces %s", dumpInterfaces == 0 ? "no" : "yes");
 
1086
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpMatrix %s", dumpMatrix == 0 ? "no" : "yes");
 
1087
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     dumpDetail %s",
 
1088
             dumpDetail == FLAG_RRD_DETAIL_HIGH ? "high" :
 
1089
             (dumpDetail == FLAG_RRD_DETAIL_MEDIUM ? "medium" : "low"));
 
1090
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     hostsFilter %s", hostsFilter);
 
1091
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     rrdPath %s", myGlobals.rrdPath);
 
1092
#ifndef WIN32
 
1093
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     umask %04o", myGlobals.rrdUmask);
 
1094
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG:     DirPerms %04o", myGlobals.rrdDirectoryPermissions);
 
1095
#endif
 
1096
#endif /* RRD_DEBUG */
 
1097
 
 
1098
  initialized = 1;
 
1099
}
 
1100
 
 
1101
/* ****************************** */
 
1102
 
 
1103
static void handleRRDHTTPrequest(char* url) {
 
1104
  char buf[1024], *strtokState, *mainState, *urlPiece,
 
1105
    rrdKey[64], rrdName[64], rrdTitle[64], startTime[32], endTime[32], rrdPrefix[32];
 
1106
  u_char action = FLAG_RRD_ACTION_NONE;
 
1107
  int _dumpDomains, _dumpFlows, _dumpHosts, _dumpInterfaces, _dumpMatrix, _dumpDetail, _dumpInterval, _dumpHours, _dumpDays, _dumpMonths;
 
1108
  char * _hostsFilter;
 
1109
#ifndef WIN32
 
1110
  int _dumpPermissions;
 
1111
#endif
 
1112
 
 
1113
  if(initialized == 0)
 
1114
    commonRRDinit();
 
1115
 
 
1116
  /* Initial values - remember, for checkboxes these need to be OFF (there's no html UNCHECKED option) */
 
1117
  _dumpDomains=0;
 
1118
  _dumpFlows=0;
 
1119
  _dumpHosts=0;
 
1120
  _dumpInterfaces=0;
 
1121
  _dumpMatrix=0;
 
1122
  _dumpDetail=CONST_RRD_DETAIL_DEFAULT;
 
1123
  _dumpInterval=DEFAULT_RRD_INTERVAL;
 
1124
  _dumpHours=DEFAULT_RRD_HOURS;
 
1125
  _dumpDays=DEFAULT_RRD_DAYS;
 
1126
  _dumpMonths=DEFAULT_RRD_MONTHS;
 
1127
  _hostsFilter = NULL;
 
1128
#ifndef WIN32
 
1129
  _dumpPermissions = DEFAULT_RRD_PERMISSIONS;
 
1130
#endif
 
1131
 
 
1132
  if((url != NULL) && (url[0] != '\0')) {
 
1133
    unescape_url(url);
 
1134
 
 
1135
    /* traceEvent(CONST_TRACE_INFO, "RRD: URL=%s", url); */
 
1136
 
 
1137
    urlPiece = strtok_r(url, "&", &mainState);
 
1138
    strcpy(startTime, "now-12h");
 
1139
    strcpy(endTime, "now");
 
1140
 
 
1141
    while(urlPiece != NULL) {
 
1142
      char *key, *value;
 
1143
 
 
1144
      key = strtok_r(urlPiece, "=", &strtokState);
 
1145
      if(key != NULL) value = strtok_r(NULL, "=", &strtokState); else value = NULL;
 
1146
 
 
1147
      /* traceEvent(CONST_TRACE_INFO, "RRD: key(%s)=%s", key, value);  */
 
1148
 
 
1149
      if(value && key) {
 
1150
 
 
1151
        if(strcmp(key, "action") == 0) {
 
1152
          if(strcmp(value, "graph") == 0)     action = FLAG_RRD_ACTION_GRAPH;
 
1153
          else if(strcmp(value, "list") == 0) action = FLAG_RRD_ACTION_LIST;
 
1154
        } else if(strcmp(key, "key") == 0) {
 
1155
          int len = strlen(value), i;
 
1156
 
 
1157
          if(len >= sizeof(rrdKey)) len = sizeof(rrdKey)-1;
 
1158
          strncpy(rrdKey, value, len);
 
1159
          rrdKey[len] = '\0';
 
1160
          for(i=0; i<len; i++) if(rrdKey[i] == '+') rrdKey[i] = ' ';
 
1161
 
 
1162
          if(strncmp(value, "hosts/", strlen("hosts/")) == 0) {
 
1163
            int plen, ii;
 
1164
            if(snprintf(rrdPrefix, sizeof(rrdPrefix), "ip_%s_", &value[6]) < 0)
 
1165
              BufferTooShort();
 
1166
            plen=strlen(rrdPrefix);
 
1167
            for (ii=0; ii<plen; ii++)
 
1168
              if( (rrdPrefix[ii] == '.') || (rrdPrefix[ii] == '/') )
 
1169
                rrdPrefix[ii]='_';
 
1170
          } else {
 
1171
            rrdPrefix[0] = '\0';
 
1172
          }
 
1173
        } else if(strcmp(key, "name") == 0) {
 
1174
          int len = strlen(value), i;
 
1175
 
 
1176
          if(len >= sizeof(rrdName)) len = sizeof(rrdName)-1;
 
1177
          strncpy(rrdName, value, len);
 
1178
          for(i=0; i<len; i++) if(rrdName[i] == '+') rrdName[i] = ' ';
 
1179
 
 
1180
          rrdName[len] = '\0';
 
1181
        } else if(strcmp(key, "title") == 0) {
 
1182
          int len = strlen(value), i;
 
1183
 
 
1184
          if(len >= sizeof(rrdTitle)) len = sizeof(rrdTitle)-1;
 
1185
          strncpy(rrdTitle, value, len);
 
1186
          for(i=0; i<len; i++) if(rrdTitle[i] == '+') rrdTitle[i] = ' ';
 
1187
 
 
1188
          rrdTitle[len] = '\0';
 
1189
        } else if(strcmp(key, "start") == 0) {
 
1190
          int len = strlen(value);
 
1191
 
 
1192
          if(len >= sizeof(startTime)) len = sizeof(startTime)-1;
 
1193
          strncpy(startTime, value, len); startTime[len] = '\0';
 
1194
        } else if(strcmp(key, "end") == 0) {
 
1195
          int len = strlen(value);
 
1196
 
 
1197
          if(len >= sizeof(endTime)) len = sizeof(endTime)-1;
 
1198
          strncpy(endTime, value, len); endTime[len] = '\0';
 
1199
        } else if(strcmp(key, "interval") == 0) {
 
1200
          _dumpInterval = atoi(value);
 
1201
          if(_dumpInterval < 1) _dumpInterval = 1 /* Min 1 second */;
 
1202
        } else if(strcmp(key, "days") == 0) {
 
1203
          _dumpDays = atoi(value);
 
1204
          if(_dumpDays < 0) _dumpDays = 0 /* Min none */;
 
1205
        } else if(strcmp(key, "hours") == 0) {
 
1206
          _dumpHours = atoi(value);
 
1207
          if(_dumpHours < 0) _dumpHours = 0 /* Min none */;
 
1208
        } else if(strcmp(key, "months") == 0) {
 
1209
          _dumpMonths = atoi(value);
 
1210
          if(_dumpMonths < 0) _dumpMonths = 0 /* Min none */;
 
1211
        } else if(strcmp(key, "hostsFilter") == 0) {
 
1212
          _hostsFilter = strdup(value);
 
1213
        } else if(strcmp(key, "rrdPath") == 0) {
 
1214
          int vlen = strlen(value)+1;
 
1215
 
 
1216
          if(myGlobals.rrdPath != NULL) free(myGlobals.rrdPath);
 
1217
          myGlobals.rrdPath  = (char*)malloc(vlen);
 
1218
          unescape(myGlobals.rrdPath, vlen, value);
 
1219
          storePrefsValue("rrd.rrdPath", myGlobals.rrdPath);
 
1220
        } else if(strcmp(key, "dumpDomains") == 0) {
 
1221
          _dumpDomains = 1;
 
1222
        } else if(strcmp(key, "dumpFlows") == 0) {
 
1223
          _dumpFlows = 1;
 
1224
        } else if(strcmp(key, "dumpDetail") == 0) {
 
1225
          _dumpDetail = atoi(value);
 
1226
          if(_dumpDetail > FLAG_RRD_DETAIL_HIGH) _dumpDetail = FLAG_RRD_DETAIL_HIGH;
 
1227
          if(_dumpDetail < FLAG_RRD_DETAIL_LOW)  _dumpDetail = FLAG_RRD_DETAIL_LOW;
 
1228
        } else if(strcmp(key, "dumpHosts") == 0) {
 
1229
          _dumpHosts = 1;
 
1230
        } else if(strcmp(key, "dumpInterfaces") == 0) {
 
1231
          _dumpInterfaces = 1;
 
1232
        } else if(strcmp(key, "dumpMatrix") == 0) {
 
1233
          _dumpMatrix = 1;
 
1234
#ifndef WIN32
 
1235
        } else if(strcmp(key, "permissions") == 0) {
 
1236
          _dumpPermissions = atoi(value);
 
1237
          if((_dumpPermissions != CONST_RRD_PERMISSIONS_PRIVATE) &&
 
1238
             (_dumpPermissions != CONST_RRD_PERMISSIONS_GROUP) &&
 
1239
             (_dumpPermissions != CONST_RRD_PERMISSIONS_EVERYONE)) {
 
1240
            _dumpPermissions = DEFAULT_RRD_PERMISSIONS;
 
1241
          }
 
1242
#endif
 
1243
        }
 
1244
      }
 
1245
 
 
1246
      urlPiece = strtok_r(NULL, "&", &mainState);
 
1247
    }
 
1248
 
 
1249
    if(action == FLAG_RRD_ACTION_NONE) {
 
1250
      dumpInterval = _dumpInterval;
 
1251
      dumpHours = _dumpHours;
 
1252
      dumpDays = _dumpDays;
 
1253
      dumpMonths = _dumpMonths;
 
1254
      /* traceEvent(CONST_TRACE_INFO, "RRD: dumpFlows=%d", dumpFlows); */
 
1255
      dumpDomains=_dumpDomains;
 
1256
      dumpFlows=_dumpFlows;
 
1257
      dumpHosts=_dumpHosts;
 
1258
      dumpInterfaces=_dumpInterfaces;
 
1259
      dumpMatrix=_dumpMatrix;
 
1260
      dumpDetail = _dumpDetail;
 
1261
#ifndef WIN32
 
1262
      dumpPermissions = _dumpPermissions;
 
1263
      setGlobalPermissions(_dumpPermissions);
 
1264
#endif
 
1265
      if(snprintf(buf, sizeof(buf), "%d", dumpInterval) < 0)
 
1266
        BufferTooShort();
 
1267
      storePrefsValue("rrd.dataDumpInterval", buf);
 
1268
      if(snprintf(buf, sizeof(buf), "%d", dumpHours) < 0)
 
1269
        BufferTooShort();
 
1270
      storePrefsValue("rrd.dataDumpHours", buf);
 
1271
      if(snprintf(buf, sizeof(buf), "%d", dumpDays) < 0)
 
1272
        BufferTooShort();
 
1273
      storePrefsValue("rrd.dataDumpDays", buf);
 
1274
      if(snprintf(buf, sizeof(buf), "%d", dumpMonths) < 0)
 
1275
        BufferTooShort();
 
1276
      storePrefsValue("rrd.dataDumpMonths", buf);
 
1277
      if(snprintf(buf, sizeof(buf), "%d", dumpDomains) < 0)
 
1278
        BufferTooShort();
 
1279
      storePrefsValue("rrd.dataDumpDomains", buf);
 
1280
      if(snprintf(buf, sizeof(buf), "%d", dumpFlows) < 0)
 
1281
        BufferTooShort();
 
1282
      storePrefsValue("rrd.dataDumpFlows", buf);
 
1283
      if(snprintf(buf, sizeof(buf), "%d", dumpHosts) < 0)
 
1284
        BufferTooShort();
 
1285
      storePrefsValue("rrd.dataDumpHosts", buf);
 
1286
      if(snprintf(buf, sizeof(buf), "%d", dumpInterfaces) < 0)
 
1287
        BufferTooShort();
 
1288
      storePrefsValue("rrd.dataDumpInterfaces", buf);
 
1289
      if(snprintf(buf, sizeof(buf), "%d", dumpMatrix) < 0)
 
1290
        BufferTooShort();
 
1291
      storePrefsValue("rrd.dataDumpMatrix", buf);
 
1292
      if(snprintf(buf, sizeof(buf), "%d", dumpDetail) < 0)
 
1293
        BufferTooShort();
 
1294
      storePrefsValue("rrd.dataDumpDetail", buf);
 
1295
 
 
1296
      if(hostsFilter != NULL) free(hostsFilter);
 
1297
      if(_hostsFilter == NULL) {
 
1298
        hostsFilter  = strdup("");
 
1299
      } else {
 
1300
        hostsFilter = _hostsFilter;
 
1301
        _hostsFilter = NULL;
 
1302
      }
 
1303
      storePrefsValue("rrd.hostsFilter", hostsFilter);
 
1304
#ifndef WIN32
 
1305
      if(snprintf(buf, sizeof(buf), "%d", dumpPermissions) < 0)
 
1306
        BufferTooShort();
 
1307
      storePrefsValue("rrd.permissions", buf);
 
1308
      umask(myGlobals.rrdUmask);
 
1309
#ifdef RRD_DEBUG
 
1310
      traceEvent(CONST_TRACE_INFO, "RRD: Mask for new directories set to %04o",
 
1311
                 myGlobals.rrdDirectoryPermissions);
 
1312
      traceEvent(CONST_TRACE_INFO, "RRD: Mask for new files set to %04o",
 
1313
                 myGlobals.rrdUmask);
 
1314
#endif
 
1315
#endif
 
1316
      shownCreate=0;
 
1317
    }
 
1318
  }
 
1319
 
 
1320
  if(_hostsFilter != NULL) free(_hostsFilter);
 
1321
 
 
1322
  /* traceEvent(CONST_TRACE_INFO, "RRD: action=%d", action); */
 
1323
 
 
1324
  if(action == FLAG_RRD_ACTION_GRAPH) {
 
1325
    graphCounter(rrdKey, rrdName, rrdTitle, startTime, endTime, rrdPrefix);
 
1326
    return;
 
1327
  } else if(action == FLAG_RRD_ACTION_LIST) {
 
1328
    listResource(rrdKey, rrdTitle, startTime, endTime);
 
1329
    return;
 
1330
  }
 
1331
 
 
1332
  sendHTTPHeader(FLAG_HTTP_TYPE_HTML, 0, 1);
 
1333
  printHTMLheader("RRD Preferences", NULL, 0);
 
1334
 
 
1335
  if(active == 1)
 
1336
    sendString("<p>You must restart the rrd plugin for changes here to take affect.</p>\n");
 
1337
  else 
 
1338
    sendString("<p>Changes here will take effect when the plugin is started.</p>\n");
 
1339
 
 
1340
  sendString("<center><form action=\"/plugins/rrdPlugin\" method=GET>\n"
 
1341
             "<TABLE BORDER=1 "TABLE_DEFAULTS">\n"
 
1342
             "<TR><TH ALIGN=CENTER "DARK_BG">Item</TH>"
 
1343
                 "<TH ALIGN=CENTER "DARK_BG">Description and notes</TH></TR>\n"
 
1344
             "<TR><TH ALIGN=LEFT "DARK_BG">Dump Interval</TH><TD>"
 
1345
             "<INPUT NAME=interval SIZE=5 VALUE=");
 
1346
  if(snprintf(buf, sizeof(buf), "%d", (int)dumpInterval) < 0)
 
1347
    BufferTooShort();
 
1348
  sendString(buf);
 
1349
  sendString("> seconds<br>Specifies how often data is stored permanently.</TD></tr>\n");
 
1350
 
 
1351
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">Dump Hours</TH><TD>"
 
1352
             "<INPUT NAME=hours SIZE=5 VALUE=");
 
1353
  if(snprintf(buf, sizeof(buf), "%d", (int)dumpHours) < 0)
 
1354
    BufferTooShort();
 
1355
  sendString(buf);
 
1356
  sendString("><br>Specifies how many hours of 'interval' data is stored permanently.</TD></tr>\n");
 
1357
 
 
1358
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">Dump Days</TH><TD>"
 
1359
             "<INPUT NAME=days SIZE=5 VALUE=");
 
1360
  if(snprintf(buf, sizeof(buf), "%d", (int)dumpDays) < 0)
 
1361
    BufferTooShort();
 
1362
  sendString(buf);
 
1363
  sendString("><br>Specifies how many days of hourly data is stored permanently.</TD></tr>\n");
 
1364
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">Dump Months</TH><TD>"
 
1365
             "<INPUT NAME=months SIZE=5 VALUE=");
 
1366
  if(snprintf(buf, sizeof(buf), "%d", (int)dumpMonths) < 0)
 
1367
    BufferTooShort();
 
1368
  sendString(buf);
 
1369
  sendString("><br>Specifies how many months of daily data is stored permanently.</TD></tr>\n");
 
1370
 
 
1371
  sendString("<TR><TD ALIGN=CENTER COLSPAN=2><B>WARNING:</B>&nbsp;"
 
1372
             "Changes to the above values will ONLY affect NEW rrds</TD></TR>");
 
1373
 
 
1374
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">Data to Dump</TH><TD>");
 
1375
 
 
1376
  if(snprintf(buf, sizeof(buf), "<INPUT TYPE=checkbox NAME=dumpDomains VALUE=1 %s> Domains<br>\n",
 
1377
              dumpDomains ? "CHECKED" : "" ) < 0)
 
1378
    BufferTooShort();
 
1379
  sendString(buf);
 
1380
 
 
1381
  if(snprintf(buf, sizeof(buf), "<INPUT TYPE=checkbox NAME=dumpFlows VALUE=1 %s> Flows<br>\n",
 
1382
              dumpFlows ? "CHECKED" : "" ) < 0)
 
1383
    BufferTooShort();
 
1384
  sendString(buf);
 
1385
 
 
1386
  if(snprintf(buf, sizeof(buf), "<INPUT TYPE=checkbox NAME=dumpHosts VALUE=1 %s> Hosts<br>\n",
 
1387
              dumpHosts ? "CHECKED" : "") < 0)
 
1388
    BufferTooShort();
 
1389
  sendString(buf);
 
1390
 
 
1391
  if(snprintf(buf, sizeof(buf), "<INPUT TYPE=checkbox NAME=dumpInterfaces VALUE=1 %s> Interfaces<br>\n",
 
1392
              dumpInterfaces ? "CHECKED" : "") < 0)
 
1393
    BufferTooShort();
 
1394
  sendString(buf);
 
1395
 
 
1396
  if(snprintf(buf, sizeof(buf), "<INPUT TYPE=checkbox NAME=dumpMatrix VALUE=1 %s> Matrix<br>\n",
 
1397
              dumpMatrix ? "CHECKED" : "") < 0)
 
1398
    BufferTooShort();
 
1399
  sendString(buf);
 
1400
 
 
1401
  sendString("</TD></tr>\n");
 
1402
 
 
1403
  if(dumpHosts) {
 
1404
    sendString("<TR><TH ALIGN=LEFT "DARK_BG">Hosts Filter</TH><TD>"
 
1405
               "<INPUT NAME=hostsFilter VALUE=\"");
 
1406
 
 
1407
    sendString(hostsFilter);
 
1408
 
 
1409
    sendString("\" SIZE=80><br>A list of networks [e.g. 172.22.0.0/255.255.0.0,192.168.5.0/255.255.255.0]<br>"
 
1410
               "separated by commas to which hosts that will be<br>"
 
1411
               "saved must belong to. An empty list means that all the hosts will "
 
1412
               "be stored on disk</TD></tr>\n");
 
1413
  }
 
1414
 
 
1415
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">RRD Detail</TH><TD>");
 
1416
  if(snprintf(buf, sizeof(buf), "<INPUT TYPE=radio NAME=dumpDetail VALUE=%d %s>Low\n",
 
1417
              FLAG_RRD_DETAIL_LOW, (dumpDetail == FLAG_RRD_DETAIL_LOW) ? "CHECKED" : "") < 0)
 
1418
    BufferTooShort();
 
1419
  sendString(buf);
 
1420
 
 
1421
  if(snprintf(buf, sizeof(buf), "<INPUT TYPE=radio NAME=dumpDetail VALUE=%d %s>Medium\n",
 
1422
              FLAG_RRD_DETAIL_MEDIUM, (dumpDetail == FLAG_RRD_DETAIL_MEDIUM) ? "CHECKED" : "") < 0)
 
1423
    BufferTooShort();
 
1424
  sendString(buf);
 
1425
 
 
1426
  if(snprintf(buf, sizeof(buf), "<INPUT TYPE=radio NAME=dumpDetail VALUE=%d %s>Full\n",
 
1427
              FLAG_RRD_DETAIL_HIGH, (dumpDetail == FLAG_RRD_DETAIL_HIGH) ? "CHECKED" : "") < 0)
 
1428
    BufferTooShort();
 
1429
  sendString(buf);
 
1430
  sendString("</TD></TR>\n");
 
1431
 
 
1432
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">RRD Files Path</TH><TD>"
 
1433
             "<INPUT NAME=rrdPath SIZE=50 VALUE=\"");
 
1434
  sendString(myGlobals.rrdPath);
 
1435
  sendString("\">");
 
1436
  sendString("<br>NOTE: The rrd files will be in a subdirectory structure, e.g.\n");
 
1437
  if(snprintf(buf, sizeof(buf),
 
1438
#ifdef WIN32
 
1439
               "%s\\interfaces\\interface-name\\12\\239\\98\\199\\xxxxx.rrd ",
 
1440
#else
 
1441
               "%s/interfaces/interface-name/12/239/98/199/xxxxx.rrd ",
 
1442
#endif
 
1443
               myGlobals.rrdPath) < 0)
 
1444
    BufferTooShort();
 
1445
  sendString(buf);
 
1446
  sendString("to limit the number of files per subdirectory.");
 
1447
  sendString("</TD></tr>\n");
 
1448
 
 
1449
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">RRD Updates</TH><TD>");
 
1450
  if(snprintf(buf, sizeof(buf), "%lu RRD files updated</TD></TR>\n", (unsigned long)numTotalRRDs) < 0)
 
1451
    BufferTooShort();
 
1452
  sendString(buf);
 
1453
 
 
1454
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">RRD Update Errors</TH><TD>");
 
1455
  if(snprintf(buf, sizeof(buf), "%lu RRD update errors</TD></TR>\n", (unsigned long)numRRDerrors) < 0)
 
1456
    BufferTooShort();
 
1457
  sendString(buf);
 
1458
 
 
1459
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">RRD Graphic Requests</TH><TD>");
 
1460
  if(snprintf(buf, sizeof(buf), "%lu RRD graphics requested</TD></TR>\n",
 
1461
              (unsigned long)rrdGraphicRequests) < 0)
 
1462
    BufferTooShort();
 
1463
  sendString(buf);
 
1464
 
 
1465
#ifndef WIN32
 
1466
  sendString("<TR><TH ALIGN=LEFT "DARK_BG">File/Directory Permissions</TH><TD>");
 
1467
  sendString("<ul>\n");
 
1468
  if(snprintf(buf, sizeof(buf), "<li><INPUT TYPE=radio NAME=permissions VALUE=%d %s>Private - ",
 
1469
              CONST_RRD_PERMISSIONS_PRIVATE,
 
1470
              (dumpPermissions == CONST_RRD_PERMISSIONS_PRIVATE) ? "CHECKED" : "") < 0)
 
1471
    BufferTooShort();
 
1472
  sendString(buf);
 
1473
  sendString("means that ONLY the ntop userid will be able to view the files</li>\n");
 
1474
 
 
1475
  if(snprintf(buf, sizeof(buf), "<li><INPUT TYPE=radio NAME=permissions VALUE=%d %s>Group - ",
 
1476
              CONST_RRD_PERMISSIONS_GROUP,
 
1477
              (dumpPermissions == CONST_RRD_PERMISSIONS_GROUP) ? "CHECKED" : "") < 0)
 
1478
    BufferTooShort();
 
1479
  sendString(buf);
 
1480
  sendString("means that all users in the same group as the ntop userid will be able to view the rrd files.\n");
 
1481
  sendString("<br><i>(this is a bad choice if ntop's group is 'nobody' along with many other service ids)</i></li>\n");
 
1482
 
 
1483
  if(snprintf(buf, sizeof(buf), "<li><INPUT TYPE=radio NAME=permissions VALUE=%d %s>Everyone - ",
 
1484
              CONST_RRD_PERMISSIONS_EVERYONE,
 
1485
              (dumpPermissions == CONST_RRD_PERMISSIONS_EVERYONE) ? "CHECKED" : "") < 0)
 
1486
    BufferTooShort();
 
1487
  sendString(buf);
 
1488
  sendString("means that everyone on the ntop host system will be able to view the rrd files.</li>\n");
 
1489
 
 
1490
  sendString("</ul><br>\n<B>WARNING</B>:&nbsp;Changing this setting affects only new files "
 
1491
             "and directories! "
 
1492
             "<i>Unless you go back and fixup existing file and directory permissions:</i><br>\n"
 
1493
             "<ul><li>Users will retain access to any rrd file or directory they currently have "
 
1494
             "access to even if you change to a more restrictive setting.</li>\n"
 
1495
             "<li>Users will not gain access to any rrd file or directory they currently do not "
 
1496
             "have access to even if you change to a less restrictive setting. Further, existing "
 
1497
             "directory permissions may prevent them from reading new files created in existing "
 
1498
             "directories.</li>\n"
 
1499
             "</ul>\n</TD></TR>\n");
 
1500
#endif
 
1501
 
 
1502
  sendString("<tr><td colspan=\"2\" align=\"center\">"
 
1503
             "<input type=submit value=\"Save Preferences\"></td></tr></table>\n"
 
1504
             "</form>\n<p></center>\n");
 
1505
 
 
1506
 
 
1507
  printPluginTrailer(NULL,
 
1508
                     "<a href=\"http://www.rrdtool.org/\" title=\"rrd home page\">RRDtool</a> "
 
1509
                     "was created by "
 
1510
                     "<a href=\"http://ee-staff.ethz.ch/~oetiker/\" title=\"Tobi's home page\">"
 
1511
                     "Tobi Oetiker</a>");
 
1512
 
 
1513
  printHTMLtrailer();
 
1514
}
 
1515
 
 
1516
/* ****************************** */
 
1517
#ifdef MAKE_WITH_RRDSIGTRAP
 
1518
RETSIGTYPE rrdcleanup(int signo) {
 
1519
  static int msgSent = 0;
 
1520
  int i;
 
1521
  void *array[20];
 
1522
  size_t size;
 
1523
  char **strings;
 
1524
 
 
1525
  if(msgSent<10) {
 
1526
    traceEvent(CONST_TRACE_FATALERROR, "RRD: caught signal %d %s", signo,
 
1527
               signo == SIGHUP ? "SIGHUP" :
 
1528
               signo == SIGINT ? "SIGINT" :
 
1529
               signo == SIGQUIT ? "SIGQUIT" :
 
1530
               signo == SIGILL ? "SIGILL" :
 
1531
               signo == SIGABRT ? "SIGABRT" :
 
1532
               signo == SIGFPE ? "SIGFPE" :
 
1533
               signo == SIGKILL ? "SIGKILL" :
 
1534
               signo == SIGSEGV ? "SIGSEGV" :
 
1535
               signo == SIGPIPE ? "SIGPIPE" :
 
1536
               signo == SIGALRM ? "SIGALRM" :
 
1537
               signo == SIGTERM ? "SIGTERM" :
 
1538
               signo == SIGUSR1 ? "SIGUSR1" :
 
1539
               signo == SIGUSR2 ? "SIGUSR2" :
 
1540
               signo == SIGCHLD ? "SIGCHLD" :
 
1541
#ifdef SIGCONT
 
1542
               signo == SIGCONT ? "SIGCONT" :
 
1543
#endif
 
1544
#ifdef SIGSTOP
 
1545
               signo == SIGSTOP ? "SIGSTOP" :
 
1546
#endif
 
1547
#ifdef SIGBUS
 
1548
               signo == SIGBUS ? "SIGBUS" :
 
1549
#endif
 
1550
#ifdef SIGSYS
 
1551
               signo == SIGSYS ? "SIGSYS"
 
1552
#endif
 
1553
               : "other");
 
1554
    msgSent++;
 
1555
  }
 
1556
 
 
1557
#ifdef HAVE_BACKTRACE
 
1558
  /* Don't double fault... */
 
1559
  /* signal(signo, SIG_DFL); */
 
1560
 
 
1561
  /* Grab the backtrace before we do much else... */
 
1562
  size = backtrace(array, 20);
 
1563
  strings = (char**)backtrace_symbols(array, size);
 
1564
 
 
1565
  traceEvent(CONST_TRACE_FATALERROR, "RRD: BACKTRACE:     backtrace is:");
 
1566
  if(size < 2) {
 
1567
    traceEvent(CONST_TRACE_FATALERROR, "RRD: BACKTRACE:         **unavailable!");
 
1568
  } else {
 
1569
    /* Ignore the 0th entry, that's our cleanup() */
 
1570
    for (i=1; i<size; i++) {
 
1571
      traceEvent(CONST_TRACE_FATALERROR, "RRD: BACKTRACE:          %2d. %s", i, strings[i]);
 
1572
    }
 
1573
  }
 
1574
#endif /* HAVE_BACKTRACE */
 
1575
 
 
1576
  exit(0);
 
1577
}
 
1578
#endif /* MAKE_WITH_RRDSIGTRAP */
 
1579
 
 
1580
/* ****************************** */
 
1581
 
 
1582
static void* rrdMainLoop(void* notUsed _UNUSED_) {
 
1583
  char value[512 /* leave it big for hosts filter */];
 
1584
  u_int32_t networks[32][3];
 
1585
  u_short numLocalNets;
 
1586
  int sleep_tm, devIdx, idx;
 
1587
  char rrdPath[512];
 
1588
  int cycleCount=0;
 
1589
  char *adjHostName;
 
1590
  ProtocolsList *protoList;
 
1591
 
 
1592
#ifdef CFG_MULTITHREADED
 
1593
  traceEvent(CONST_TRACE_INFO, "THREADMGMT: rrd thread (%ld) started", rrdThread);
 
1594
#else
 
1595
#ifdef RRD_DEBUG
 
1596
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: rrdMainLoop()");
 
1597
#endif
 
1598
#endif
 
1599
 
 
1600
#ifdef MAKE_WITH_RRDSIGTRAP
 
1601
  signal(SIGSEGV, rrdcleanup);
 
1602
  signal(SIGHUP,  rrdcleanup);
 
1603
  signal(SIGINT,  rrdcleanup);
 
1604
  signal(SIGQUIT, rrdcleanup);
 
1605
  signal(SIGILL,  rrdcleanup);
 
1606
  signal(SIGABRT, rrdcleanup);
 
1607
  signal(SIGFPE,  rrdcleanup);
 
1608
  signal(SIGKILL, rrdcleanup);
 
1609
  signal(SIGPIPE, rrdcleanup);
 
1610
  signal(SIGALRM, rrdcleanup);
 
1611
  signal(SIGTERM, rrdcleanup);
 
1612
  signal(SIGUSR1, rrdcleanup);
 
1613
  signal(SIGUSR2, rrdcleanup);
 
1614
  /* signal(SIGCHLD, rrdcleanup); */
 
1615
#ifdef SIGCONT
 
1616
  signal(SIGCONT, rrdcleanup);
 
1617
#endif
 
1618
#ifdef SIGSTOP
 
1619
  signal(SIGSTOP, rrdcleanup);
 
1620
#endif
 
1621
#ifdef SIGBUS
 
1622
  signal(SIGBUS,  rrdcleanup);
 
1623
#endif
 
1624
#ifdef SIGSYS
 
1625
  signal(SIGSYS,  rrdcleanup);
 
1626
#endif
 
1627
#endif /* MAKE_WITH_RRDSIGTRAP */
 
1628
 
 
1629
  if(initialized == 0)
 
1630
    commonRRDinit();
 
1631
 
 
1632
  /* Initialize the "end" of the dummy interval just far enough back in time
 
1633
     so that it expires once everything is up and running. */
 
1634
  end_tm = myGlobals.actTime - dumpInterval + 15;
 
1635
 
 
1636
  /* Show we're running */
 
1637
  active = 1;
 
1638
 
 
1639
  for(;myGlobals.capturePackets != FLAG_NTOPSTATE_TERM;) {
 
1640
    char *hostKey;
 
1641
    int i, j;
 
1642
    Counter numRRDs = numTotalRRDs;
 
1643
 
 
1644
#if RRD_DEBUG >= 1
 
1645
    char endTime[32];
 
1646
#endif
 
1647
 
 
1648
    cycleCount++;
 
1649
 
 
1650
    do {
 
1651
      end_tm += dumpInterval;
 
1652
      sleep_tm = end_tm - (start_tm = time(NULL));
 
1653
    } while (sleep_tm < 0);
 
1654
 
 
1655
#if RRD_DEBUG >= 1
 
1656
    {
 
1657
      struct tm workT;
 
1658
      strftime(endTime, sizeof(endTime), CONST_LOCALE_TIMESPEC, localtime_r(&end_tm, &workT));
 
1659
      traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: Sleeping for %d seconds (interval %d, end at %s)",
 
1660
                 sleep_tm,
 
1661
                 dumpInterval,
 
1662
                 endTime);
 
1663
    }
 
1664
#endif
 
1665
 
 
1666
    HEARTBEAT(0, "rrdMainLoop(), sleep(%d)...", sleep_tm);
 
1667
    sleep(sleep_tm);
 
1668
    HEARTBEAT(0, "rrdMainLoop(), sleep(%d)...woke", sleep_tm);
 
1669
    if(myGlobals.capturePackets != FLAG_NTOPSTATE_RUN) return(NULL);
 
1670
 
 
1671
    numRuns++;
 
1672
    rrdTime =  time(NULL);
 
1673
 
 
1674
    /* ****************************************************** */
 
1675
 
 
1676
    numLocalNets = 0;
 
1677
    /* Avoids strtok to blanks into hostsFilter */
 
1678
    if(snprintf(rrdPath, sizeof(rrdPath), "%s", hostsFilter) < 0)
 
1679
      BufferTooShort();
 
1680
    handleAddressLists(rrdPath, networks, &numLocalNets, value, sizeof(value), CONST_HANDLEADDRESSLISTS_RRD);
 
1681
 
 
1682
    /* ****************************************************** */
 
1683
 
 
1684
    if(dumpDomains) {
 
1685
 
 
1686
      DomainStats **stats, *tmpStats, *statsEntry;
 
1687
      u_int maxHosts, len = 0;
 
1688
      Counter totBytesSent = 0;
 
1689
      Counter totBytesRcvd = 0;
 
1690
      HostTraffic *el;
 
1691
      u_short keyValue=0;
 
1692
 
 
1693
      for(devIdx=0; devIdx<myGlobals.numDevices; devIdx++) {
 
1694
        u_int numEntries = 0;
 
1695
 
 
1696
        // save this as it may change
 
1697
        maxHosts = myGlobals.device[devIdx].hostsno;
 
1698
        len = sizeof(DomainStats)*maxHosts;
 
1699
        tmpStats = (DomainStats*)malloc(len);
 
1700
        memset(tmpStats, 0, len);
 
1701
 
 
1702
        len = sizeof(DomainStats**)*maxHosts;
 
1703
        stats = (DomainStats**)malloc(len);
 
1704
        memset(stats, 0, len);
 
1705
 
 
1706
        // walk through all hosts, getting their domain names and counting stats
 
1707
        for (el = getFirstHost(devIdx);
 
1708
          el != NULL; el = getNextHost(devIdx, el)) {
 
1709
 
 
1710
            fillDomainName(el);
 
1711
 
 
1712
            // if we didn't get a domain name, bail out
 
1713
            if ((el->dnsDomainValue == NULL)
 
1714
               || (el->dnsDomainValue[0] == '\0')
 
1715
               || (el->ip2ccValue == NULL)
 
1716
               || (el->hostResolvedName[0] == '\0')
 
1717
               || broadcastHost(el)
 
1718
               ) {
 
1719
              continue;
 
1720
            }
 
1721
 
 
1722
            for(keyValue=0, idx=0; el->dnsDomainValue[idx] != '\0'; idx++)
 
1723
              keyValue += (idx+1)*(u_short)el->dnsDomainValue[idx];
 
1724
 
 
1725
            keyValue %= maxHosts;
 
1726
 
 
1727
            while((stats[keyValue] != NULL)
 
1728
              && (strcasecmp(stats[keyValue]->domainHost->dnsDomainValue,
 
1729
              el->dnsDomainValue) != 0))
 
1730
                keyValue = (keyValue+1) % maxHosts;
 
1731
 
 
1732
            // if we just start counting for this domain...
 
1733
            if(stats[keyValue] != NULL)
 
1734
              statsEntry = stats[keyValue];
 
1735
            else {
 
1736
              statsEntry = &tmpStats[numEntries++];
 
1737
              memset(statsEntry, 0, sizeof(DomainStats));
 
1738
              statsEntry->domainHost = el;
 
1739
              stats[keyValue] = statsEntry;
 
1740
#if RRD_DEBUG >= 2
 
1741
              traceEvent(CONST_TRACE_INFO, "RRD_DEBUG [%d] %s/%s", numEntries, el->dnsDomainValue, el->ip2ccValue);
 
1742
#endif
 
1743
            }
 
1744
 
 
1745
            // count this host's stats in the domain stats
 
1746
            totBytesSent += el->bytesSent.value;
 
1747
            statsEntry->bytesSent.value += el->bytesSent.value;
 
1748
            statsEntry->bytesRcvd.value += el->bytesRcvd.value;
 
1749
            totBytesRcvd          += el->bytesRcvd.value;
 
1750
            statsEntry->tcpSent.value   += el->tcpSentLoc.value + el->tcpSentRem.value;
 
1751
            statsEntry->udpSent.value   += el->udpSentLoc.value + el->udpSentRem.value;
 
1752
            statsEntry->icmpSent.value  += el->icmpSent.value;
 
1753
            statsEntry->icmp6Sent.value  += el->icmp6Sent.value;
 
1754
            statsEntry->tcpRcvd.value   += el->tcpRcvdLoc.value + el->tcpRcvdFromRem.value;
 
1755
            statsEntry->udpRcvd.value   += el->udpRcvdLoc.value + el->udpRcvdFromRem.value;
 
1756
            statsEntry->icmpRcvd.value  += el->icmpRcvd.value;
 
1757
            statsEntry->icmp6Rcvd.value  += el->icmp6Rcvd.value;
 
1758
 
 
1759
            if(numEntries >= maxHosts) break;
 
1760
        }
 
1761
 
 
1762
        // if we didn't find a single domain, continue with the next interface
 
1763
        if (numEntries == 0) {
 
1764
            free(tmpStats); free(stats);
 
1765
            continue;
 
1766
        }
 
1767
 
 
1768
        // insert all domain data for this interface into the RRDs
 
1769
        for (idx=0; idx < numEntries; idx++) {
 
1770
            statsEntry = &tmpStats[idx];
 
1771
 
 
1772
            if(snprintf(rrdPath, sizeof(rrdPath), "%s/interfaces/%s/domains/%s/",
 
1773
                myGlobals.rrdPath, myGlobals.device[devIdx].humanFriendlyName,
 
1774
                statsEntry->domainHost->dnsDomainValue) < 0)
 
1775
              BufferTooShort();
 
1776
            mkdir_p(rrdPath);
 
1777
 
 
1778
#if RRD_DEBUG >= 2
 
1779
            traceEvent(CONST_TRACE_INFO, "RRD: Updating %s", rrdPath);
 
1780
#endif
 
1781
            updateCounter(rrdPath, "bytesSent", statsEntry->bytesSent.value);
 
1782
            updateCounter(rrdPath, "bytesRcvd", statsEntry->bytesRcvd.value);
 
1783
 
 
1784
            updateCounter(rrdPath, "tcpSent", statsEntry->tcpSent.value);
 
1785
            updateCounter(rrdPath, "udpSent", statsEntry->udpSent.value);
 
1786
            updateCounter(rrdPath, "icmpSent", statsEntry->icmpSent.value);
 
1787
            updateCounter(rrdPath, "icmp6Sent", statsEntry->icmp6Sent.value);
 
1788
 
 
1789
            updateCounter(rrdPath, "tcpRcvd", statsEntry->tcpRcvd.value);
 
1790
            updateCounter(rrdPath, "udpRcvd", statsEntry->udpRcvd.value);
 
1791
            updateCounter(rrdPath, "icmpRcvd", statsEntry->icmpRcvd.value);
 
1792
            updateCounter(rrdPath, "icmp6Rcvd", statsEntry->icmp6Rcvd.value);
 
1793
        }
 
1794
 
 
1795
        free(tmpStats); free(stats);
 
1796
      }
 
1797
    }
 
1798
 
 
1799
    /* ****************************************************** */
 
1800
 
 
1801
    if(dumpHosts) {
 
1802
      for(devIdx=0; devIdx<myGlobals.numDevices; devIdx++) {
 
1803
        for(i=1; i<myGlobals.device[devIdx].actualHashSize; i++) {
 
1804
          HostTraffic *el = myGlobals.device[devIdx].hash_hostTraffic[i];
 
1805
 
 
1806
          while(el != NULL) {
 
1807
            if((el == myGlobals.otherHostEntry) || (el == myGlobals.broadcastEntry)
 
1808
               || broadcastHost(el) || (myGlobals.trackOnlyLocalHosts && (!subnetPseudoLocalHost(el)))) {
 
1809
              el = el->next;
 
1810
              continue;
 
1811
            }
 
1812
 
 
1813
#ifdef CFG_MULTITHREADED
 
1814
            accessMutex(&myGlobals.hostsHashMutex, "rrdDumpHosts");
 
1815
#endif
 
1816
 
 
1817
            if((el->bytesSent.value > 0) || (el->bytesRcvd.value > 0)) {
 
1818
              if(el->hostNumIpAddress[0] != '\0') {
 
1819
                hostKey = el->hostNumIpAddress;
 
1820
 
 
1821
                if((numLocalNets > 0)
 
1822
                   && (el->hostIpAddress.hostFamily == AF_INET) /* IPv4 ONLY <-- FIX */
 
1823
                   && (!__pseudoLocalAddress(&el->hostIpAddress.Ip4Address, networks, numLocalNets))) {
 
1824
                  el = el->next;
 
1825
#ifdef CFG_MULTITHREADED
 
1826
                  releaseMutex(&myGlobals.hostsHashMutex);
 
1827
#endif
 
1828
                  continue;
 
1829
                } 
 
1830
 
 
1831
                if((!myGlobals.dontTrustMACaddr) 
 
1832
                   && subnetPseudoLocalHost(el)
 
1833
                   && (el->ethAddressString[0] != '\0')) /*
 
1834
                                                           NOTE:
 
1835
                                                           MAC address is empty even
 
1836
                                                           for local hosts if this host has
 
1837
                                                           been learnt on a virtual interface
 
1838
                                                           such as the NetFlow interface
 
1839
                                                         */
 
1840
                    hostKey = el->ethAddressString;
 
1841
              } else {
 
1842
                /* For the time being do not save IP-less hosts */
 
1843
                el = el->next;
 
1844
 
 
1845
#ifdef CFG_MULTITHREADED
 
1846
                releaseMutex(&myGlobals.hostsHashMutex);
 
1847
#endif
 
1848
                continue;
 
1849
              }
 
1850
 
 
1851
              adjHostName = dotToSlash(hostKey);
 
1852
 
 
1853
              if(snprintf(rrdPath, sizeof(rrdPath), "%s/interfaces/%s/hosts/%s/",
 
1854
                       myGlobals.rrdPath, myGlobals.device[devIdx].humanFriendlyName,
 
1855
                       adjHostName) < 0)
 
1856
                BufferTooShort();
 
1857
              mkdir_p(rrdPath);
 
1858
              
 
1859
#if RRD_DEBUG >= 2
 
1860
              traceEvent(CONST_TRACE_INFO, "RRD: Updating %s [%s/%s]", 
 
1861
                         hostKey, el->hostNumIpAddress, el->ethAddressString);
 
1862
#endif
 
1863
 
 
1864
              updateTrafficCounter(rrdPath, "pktSent", &el->pktSent);
 
1865
              updateTrafficCounter(rrdPath, "pktRcvd", &el->pktRcvd);
 
1866
              updateTrafficCounter(rrdPath, "bytesSent", &el->bytesSent);
 
1867
              updateTrafficCounter(rrdPath, "bytesRcvd", &el->bytesRcvd);
 
1868
 
 
1869
              if(dumpDetail >= FLAG_RRD_DETAIL_MEDIUM) {
 
1870
                updateTrafficCounter(rrdPath, "pktDuplicatedAckSent", &el->pktDuplicatedAckSent);
 
1871
                updateTrafficCounter(rrdPath, "pktDuplicatedAckRcvd", &el->pktDuplicatedAckRcvd);
 
1872
                updateTrafficCounter(rrdPath, "pktBroadcastSent", &el->pktBroadcastSent);
 
1873
                updateTrafficCounter(rrdPath, "bytesBroadcastSent", &el->bytesBroadcastSent);
 
1874
                updateTrafficCounter(rrdPath, "pktMulticastSent", &el->pktMulticastSent);
 
1875
                updateTrafficCounter(rrdPath, "bytesMulticastSent", &el->bytesMulticastSent);
 
1876
                updateTrafficCounter(rrdPath, "pktMulticastRcvd", &el->pktMulticastRcvd);
 
1877
                updateTrafficCounter(rrdPath, "bytesMulticastRcvd", &el->bytesMulticastRcvd);
 
1878
 
 
1879
                updateTrafficCounter(rrdPath, "bytesSentLoc", &el->bytesSentLoc);
 
1880
                updateTrafficCounter(rrdPath, "bytesSentRem", &el->bytesSentRem);
 
1881
                updateTrafficCounter(rrdPath, "bytesRcvdLoc", &el->bytesRcvdLoc);
 
1882
                updateTrafficCounter(rrdPath, "bytesRcvdFromRem", &el->bytesRcvdFromRem);
 
1883
                updateTrafficCounter(rrdPath, "ipBytesSent", &el->ipBytesSent);
 
1884
                updateTrafficCounter(rrdPath, "ipBytesRcvd", &el->ipBytesRcvd);
 
1885
                updateTrafficCounter(rrdPath, "tcpSentLoc", &el->tcpSentLoc);
 
1886
                updateTrafficCounter(rrdPath, "tcpSentRem", &el->tcpSentRem);
 
1887
                updateTrafficCounter(rrdPath, "udpSentLoc", &el->udpSentLoc);
 
1888
                updateTrafficCounter(rrdPath, "udpSentRem", &el->udpSentRem);
 
1889
                updateTrafficCounter(rrdPath, "icmpSent", &el->icmpSent);
 
1890
                updateTrafficCounter(rrdPath, "tcpRcvdLoc", &el->tcpRcvdLoc);
 
1891
                updateTrafficCounter(rrdPath, "tcpRcvdFromRem", &el->tcpRcvdFromRem);
 
1892
                updateTrafficCounter(rrdPath, "udpRcvdLoc", &el->udpRcvdLoc);
 
1893
                updateTrafficCounter(rrdPath, "udpRcvdFromRem", &el->udpRcvdFromRem);
 
1894
                updateTrafficCounter(rrdPath, "icmpRcvd", &el->icmpRcvd);
 
1895
                updateTrafficCounter(rrdPath, "tcpFragmentsSent", &el->tcpFragmentsSent);
 
1896
                updateTrafficCounter(rrdPath, "tcpFragmentsRcvd", &el->tcpFragmentsRcvd);
 
1897
                updateTrafficCounter(rrdPath, "udpFragmentsSent", &el->udpFragmentsSent);
 
1898
                updateTrafficCounter(rrdPath, "udpFragmentsRcvd", &el->udpFragmentsRcvd);
 
1899
                updateTrafficCounter(rrdPath, "icmpFragmentsSent", &el->icmpFragmentsSent);
 
1900
                updateTrafficCounter(rrdPath, "icmpFragmentsRcvd", &el->icmpFragmentsRcvd);
 
1901
                updateTrafficCounter(rrdPath, "stpSent", &el->stpSent);
 
1902
                updateTrafficCounter(rrdPath, "stpRcvd", &el->stpRcvd);
 
1903
                updateTrafficCounter(rrdPath, "ipxSent", &el->ipxSent);
 
1904
                updateTrafficCounter(rrdPath, "ipxRcvd", &el->ipxRcvd);
 
1905
                updateTrafficCounter(rrdPath, "osiSent", &el->osiSent);
 
1906
                updateTrafficCounter(rrdPath, "osiRcvd", &el->osiRcvd);
 
1907
                updateTrafficCounter(rrdPath, "dlcSent", &el->dlcSent);
 
1908
                updateTrafficCounter(rrdPath, "dlcRcvd", &el->dlcRcvd);
 
1909
                updateTrafficCounter(rrdPath, "arp_rarpSent", &el->arp_rarpSent);
 
1910
                updateTrafficCounter(rrdPath, "arp_rarpRcvd", &el->arp_rarpRcvd);
 
1911
                updateTrafficCounter(rrdPath, "arpReqPktsSent", &el->arpReqPktsSent);
 
1912
                updateTrafficCounter(rrdPath, "arpReplyPktsSent", &el->arpReplyPktsSent);
 
1913
                updateTrafficCounter(rrdPath, "arpReplyPktsRcvd", &el->arpReplyPktsRcvd);
 
1914
                updateTrafficCounter(rrdPath, "decnetSent", &el->decnetSent);
 
1915
                updateTrafficCounter(rrdPath, "decnetRcvd", &el->decnetRcvd);
 
1916
                updateTrafficCounter(rrdPath, "appletalkSent", &el->appletalkSent);
 
1917
                updateTrafficCounter(rrdPath, "appletalkRcvd", &el->appletalkRcvd);
 
1918
                updateTrafficCounter(rrdPath, "netbiosSent", &el->netbiosSent);
 
1919
                updateTrafficCounter(rrdPath, "netbiosRcvd", &el->netbiosRcvd);
 
1920
                updateTrafficCounter(rrdPath, "ipv6Sent", &el->ipv6Sent);
 
1921
                updateTrafficCounter(rrdPath, "ipv6Rcvd", &el->ipv6Rcvd);
 
1922
                updateTrafficCounter(rrdPath, "otherSent", &el->otherSent);
 
1923
                updateTrafficCounter(rrdPath, "otherRcvd", &el->otherRcvd);
 
1924
 
 
1925
                protoList = myGlobals.ipProtosList, idx=0;
 
1926
                while(protoList != NULL) {
 
1927
                  char buf[64];
 
1928
 
 
1929
                  if(snprintf(buf, sizeof(buf), "%sSent", protoList->protocolName) < 0) BufferTooShort();
 
1930
                  updateTrafficCounter(rrdPath, buf, &el->ipProtosList[idx].sent);
 
1931
                  if(snprintf(buf, sizeof(buf), "%sRcvd", protoList->protocolName) < 0) BufferTooShort();
 
1932
                  updateTrafficCounter(rrdPath, buf, &el->ipProtosList[idx].rcvd);
 
1933
                  idx++, protoList = protoList->next;
 
1934
                }               
 
1935
              }
 
1936
 
 
1937
              if(dumpDetail == FLAG_RRD_DETAIL_HIGH) {
 
1938
                updateCounter(rrdPath, "totContactedSentPeers", el->totContactedSentPeers);
 
1939
                updateCounter(rrdPath, "totContactedRcvdPeers", el->totContactedRcvdPeers);
 
1940
 
 
1941
                if((hostKey == el->hostNumIpAddress) && el->protoIPTrafficInfos) {
 
1942
#ifdef RRD_DEBUG
 
1943
                  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: Updating host %s", hostKey);
 
1944
#endif
 
1945
 
 
1946
                  if(snprintf(rrdPath, sizeof(rrdPath), "%s/interfaces/%s/hosts/%s/IP_",
 
1947
                           myGlobals.rrdPath,
 
1948
                           myGlobals.device[devIdx].humanFriendlyName,
 
1949
                           adjHostName
 
1950
                           ) < 0)
 
1951
                    BufferTooShort();
 
1952
 
 
1953
                  for(j=0; j<myGlobals.numIpProtosToMonitor; j++) {
 
1954
                    char key[128];
 
1955
                    if(snprintf(key, sizeof(key), "%sSentBytes",
 
1956
                                myGlobals.protoIPTrafficInfos[j]) < 0)
 
1957
                      BufferTooShort();
 
1958
                    updateCounter(rrdPath, key, el->protoIPTrafficInfos[j].sentLoc.value+
 
1959
                                  el->protoIPTrafficInfos[j].sentRem.value);
 
1960
 
 
1961
                    if(snprintf(key, sizeof(key), "%sRcvdBytes",
 
1962
                                myGlobals.protoIPTrafficInfos[j]) < 0)
 
1963
                      BufferTooShort();
 
1964
                    updateCounter(rrdPath, key, el->protoIPTrafficInfos[j].rcvdLoc.value+
 
1965
                                  el->protoIPTrafficInfos[j].rcvdFromRem.value);
 
1966
                  }
 
1967
                }
 
1968
              }
 
1969
 
 
1970
              if(adjHostName != NULL)
 
1971
                free(adjHostName);
 
1972
            }
 
1973
 
 
1974
#ifdef CFG_MULTITHREADED
 
1975
            releaseMutex(&myGlobals.hostsHashMutex);
 
1976
#endif
 
1977
#ifdef MAKE_WITH_SCHED_YIELD
 
1978
            sched_yield(); /* Allow other threads to run */
 
1979
#endif
 
1980
            el = el->next;
 
1981
          }
 
1982
        }
 
1983
      } /* for(devIdx...) */
 
1984
    }
 
1985
 
 
1986
    /* ************************** */
 
1987
 
 
1988
    if(dumpFlows) {
 
1989
      FlowFilterList *list = myGlobals.flowsList;
 
1990
 
 
1991
      while(list != NULL) {
 
1992
        if(list->pluginStatus.activePlugin) {
 
1993
          if(snprintf(rrdPath, sizeof(rrdPath), "%s/flows/%s/",
 
1994
                      myGlobals.rrdPath, list->flowName) < 0)
 
1995
            BufferTooShort();
 
1996
          mkdir_p(rrdPath);
 
1997
 
 
1998
          updateCounter(rrdPath, "packets", list->packets.value);
 
1999
          updateCounter(rrdPath, "bytes",   list->bytes.value);
 
2000
        }
 
2001
 
 
2002
        list = list->next;
 
2003
      }
 
2004
    }
 
2005
 
 
2006
    /* ************************** */
 
2007
 
 
2008
    if(dumpInterfaces) {
 
2009
      for(devIdx=0; devIdx<myGlobals.numDevices; devIdx++) {
 
2010
 
 
2011
        if(myGlobals.device[devIdx].virtualDevice) continue;
 
2012
 
 
2013
        if(snprintf(rrdPath, sizeof(rrdPath), "%s/interfaces/%s/", myGlobals.rrdPath,
 
2014
                    myGlobals.device[devIdx].humanFriendlyName) < 0)
 
2015
            BufferTooShort();
 
2016
        mkdir_p(rrdPath);
 
2017
 
 
2018
        updateCounter(rrdPath, "ethernetPkts",  myGlobals.device[devIdx].ethernetPkts.value);
 
2019
        updateCounter(rrdPath, "broadcastPkts", myGlobals.device[devIdx].broadcastPkts.value);
 
2020
        updateCounter(rrdPath, "multicastPkts", myGlobals.device[devIdx].multicastPkts.value);
 
2021
        updateCounter(rrdPath, "ethernetBytes", myGlobals.device[devIdx].ethernetBytes.value);
 
2022
        updateGauge(rrdPath,   "knownHostsNum", myGlobals.device[devIdx].hostsno);
 
2023
        updateGauge(rrdPath,   "activeHostSendersNum",  numActiveSenders(devIdx));
 
2024
        updateCounter(rrdPath, "ipBytes",       myGlobals.device[devIdx].ipBytes.value);
 
2025
 
 
2026
        if(dumpDetail >= FLAG_RRD_DETAIL_MEDIUM) {
 
2027
          updateCounter(rrdPath, "droppedPkts", myGlobals.device[devIdx].droppedPkts.value);
 
2028
          updateCounter(rrdPath, "fragmentedIpBytes", myGlobals.device[devIdx].fragmentedIpBytes.value);
 
2029
          updateCounter(rrdPath, "tcpBytes", myGlobals.device[devIdx].tcpBytes.value);
 
2030
          updateCounter(rrdPath, "udpBytes", myGlobals.device[devIdx].udpBytes.value);
 
2031
          updateCounter(rrdPath, "otherIpBytes", myGlobals.device[devIdx].otherIpBytes.value);
 
2032
          updateCounter(rrdPath, "icmpBytes", myGlobals.device[devIdx].icmpBytes.value);
 
2033
          updateCounter(rrdPath, "dlcBytes", myGlobals.device[devIdx].dlcBytes.value);
 
2034
          updateCounter(rrdPath, "ipxBytes", myGlobals.device[devIdx].ipxBytes.value);
 
2035
          updateCounter(rrdPath, "stpBytes", myGlobals.device[devIdx].stpBytes.value);
 
2036
          updateCounter(rrdPath, "decnetBytes", myGlobals.device[devIdx].decnetBytes.value);
 
2037
          updateCounter(rrdPath, "netbiosBytes", myGlobals.device[devIdx].netbiosBytes.value);
 
2038
          updateCounter(rrdPath, "arpRarpBytes", myGlobals.device[devIdx].arpRarpBytes.value);
 
2039
          updateCounter(rrdPath, "atalkBytes", myGlobals.device[devIdx].atalkBytes.value);
 
2040
          updateCounter(rrdPath, "egpBytes", myGlobals.device[devIdx].egpBytes.value);
 
2041
          updateCounter(rrdPath, "osiBytes", myGlobals.device[devIdx].osiBytes.value);
 
2042
          updateCounter(rrdPath, "ipv6Bytes", myGlobals.device[devIdx].ipv6Bytes.value);
 
2043
          updateCounter(rrdPath, "otherBytes", myGlobals.device[devIdx].otherBytes.value);
 
2044
          updateCounter(rrdPath, "upTo64Pkts", myGlobals.device[devIdx].rcvdPktStats.upTo64.value);
 
2045
          updateCounter(rrdPath, "upTo128Pkts", myGlobals.device[devIdx].rcvdPktStats.upTo128.value);
 
2046
          updateCounter(rrdPath, "upTo256Pkts", myGlobals.device[devIdx].rcvdPktStats.upTo256.value);
 
2047
          updateCounter(rrdPath, "upTo512Pkts", myGlobals.device[devIdx].rcvdPktStats.upTo512.value);
 
2048
          updateCounter(rrdPath, "upTo1024Pkts", myGlobals.device[devIdx].rcvdPktStats.upTo1024.value);
 
2049
          updateCounter(rrdPath, "upTo1518Pkts", myGlobals.device[devIdx].rcvdPktStats.upTo1518.value);
 
2050
          updateCounter(rrdPath, "badChecksumPkts", myGlobals.device[devIdx].rcvdPktStats.badChecksum.value);
 
2051
          updateCounter(rrdPath, "tooLongPkts", myGlobals.device[devIdx].rcvdPktStats.tooLong.value);
 
2052
 
 
2053
          protoList = myGlobals.ipProtosList, idx=0;
 
2054
          while(protoList != NULL) {
 
2055
            updateCounter(rrdPath, protoList->protocolName, myGlobals.device[devIdx].ipProtosList[idx].value);      
 
2056
            idx++, protoList = protoList->next;
 
2057
          }
 
2058
        }
 
2059
 
 
2060
        if(dumpDetail == FLAG_RRD_DETAIL_HIGH) {
 
2061
          if(myGlobals.device[devIdx].ipProtoStats != NULL) {
 
2062
            if(snprintf(rrdPath, sizeof(rrdPath), "%s/interfaces/%s/IP_",
 
2063
                       myGlobals.rrdPath,  myGlobals.device[devIdx].humanFriendlyName) < 0)
 
2064
              BufferTooShort();
 
2065
 
 
2066
            for(j=0; j<myGlobals.numIpProtosToMonitor; j++) {
 
2067
              TrafficCounter ctr;
 
2068
              char tmpStr[128];
 
2069
 
 
2070
              ctr.value =
 
2071
                myGlobals.device[devIdx].ipProtoStats[j].local.value+
 
2072
                myGlobals.device[devIdx].ipProtoStats[j].local2remote.value+
 
2073
                myGlobals.device[devIdx].ipProtoStats[j].remote2local.value+
 
2074
                myGlobals.device[devIdx].ipProtoStats[j].remote.value;
 
2075
 
 
2076
              if(snprintf(tmpStr, sizeof(tmpStr), "%sBytes", myGlobals.protoIPTrafficInfos[j]) < 0)
 
2077
                BufferTooShort();
 
2078
              updateCounter(rrdPath, tmpStr, ctr.value);
 
2079
            }
 
2080
          }
 
2081
        }
 
2082
      }
 
2083
    }
 
2084
 
 
2085
    /* ************************** */
 
2086
 
 
2087
    if(dumpMatrix) {
 
2088
      int k;
 
2089
 
 
2090
      for(k=0; k<myGlobals.numDevices; k++)
 
2091
        for(i=1; i<myGlobals.device[k].numHosts; i++)
 
2092
          for(j=1; j<myGlobals.device[k].numHosts; j++) {
 
2093
            if(i != j) {
 
2094
              idx = i*myGlobals.device[k].numHosts+j;
 
2095
 
 
2096
              if(myGlobals.device[k].ipTrafficMatrix == NULL)
 
2097
                continue;
 
2098
              if(myGlobals.device[k].ipTrafficMatrix[idx] == NULL)
 
2099
                continue;
 
2100
 
 
2101
              if(myGlobals.device[k].ipTrafficMatrix[idx]->bytesSent.value > 0) {
 
2102
 
 
2103
                if(snprintf(rrdPath, sizeof(rrdPath), "%s/interfaces/%s/matrix/%s/%s/",
 
2104
                         myGlobals.rrdPath,
 
2105
                         myGlobals.device[k].humanFriendlyName,
 
2106
                         myGlobals.device[k].ipTrafficMatrixHosts[i]->hostNumIpAddress,
 
2107
                         myGlobals.device[k].ipTrafficMatrixHosts[j]->hostNumIpAddress) < 0)
 
2108
                  BufferTooShort();
 
2109
                mkdir_p(rrdPath);
 
2110
 
 
2111
                updateCounter(rrdPath, "pkts",
 
2112
                              myGlobals.device[k].ipTrafficMatrix[idx]->pktsSent.value);
 
2113
 
 
2114
                updateCounter(rrdPath, "bytes",
 
2115
                              myGlobals.device[k].ipTrafficMatrix[idx]->bytesSent.value);
 
2116
              }
 
2117
            }
 
2118
          }
 
2119
    }
 
2120
 
 
2121
#ifdef RRD_DEBUG
 
2122
    traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: %lu RRDs updated (%lu total updates)",
 
2123
               (unsigned long)(numTotalRRDs-numRRDs), (unsigned long)numTotalRRDs);
 
2124
#endif
 
2125
 
 
2126
    /*
 
2127
     * If it's FLAG_NTOPSTATE_STOPCAP, and we're still running, then this
 
2128
     * is the 1st pass.  We just updated our data to save the counts, now
 
2129
     * we kill the thread...
 
2130
     */
 
2131
    if(myGlobals.capturePackets == FLAG_NTOPSTATE_STOPCAP) {
 
2132
      traceEvent(CONST_TRACE_WARNING, "RRD: STOPCAP, ending rrd thread");
 
2133
      break;
 
2134
    }
 
2135
  }
 
2136
 
 
2137
#ifdef CFG_MULTITHREADED
 
2138
  traceEvent(CONST_TRACE_WARNING, "THREADMGMT: rrd thread (%ld) terminated", rrdThread);
 
2139
#else
 
2140
#ifdef RRD_DEBUG
 
2141
  traceEvent(CONST_TRACE_INFO, "RRD_DEBUG: rrdMainLoop() terminated.");
 
2142
#endif
 
2143
#endif
 
2144
 
 
2145
  return(0);
 
2146
}
 
2147
 
 
2148
/* ****************************** */
 
2149
 
 
2150
static int initRRDfunct(void) {
 
2151
  char dname[256];
 
2152
  int i;
 
2153
 
 
2154
  traceEvent(CONST_TRACE_INFO, "RRD: Welcome to the RRD plugin");
 
2155
 
 
2156
#ifdef CFG_MULTITHREADED
 
2157
  createMutex(&rrdMutex);
 
2158
#endif
 
2159
 
 
2160
  setPluginStatus(NULL);
 
2161
 
 
2162
  if(myGlobals.rrdPath == NULL)
 
2163
    commonRRDinit();
 
2164
 
 
2165
#ifndef CFG_MULTITHREADED
 
2166
  /* This plugin works only with threads */
 
2167
  setPluginStatus("Disabled - requires POSIX thread support.");
 
2168
  return(-1);
 
2169
#endif
 
2170
 
 
2171
  if(snprintf(dname, sizeof(dname), "%s", myGlobals.rrdPath) < 0)
 
2172
    BufferTooShort();
 
2173
  if(_mkdir(dname) == -1) {
 
2174
    if(errno != EEXIST) {
 
2175
      traceEvent(CONST_TRACE_ERROR, "RRD: Disabled - unable to create base directory (err %d, %s)", 
 
2176
                 errno, dname);
 
2177
      setPluginStatus("Disabled - unable to create rrd base directory.");
 
2178
      /* Return w/o creating the rrd thread ... disabled */
 
2179
      return(-1);
 
2180
    }
 
2181
  } else {
 
2182
    traceEvent(CONST_TRACE_INFO, "RRD: Created base directory (%s)", dname);
 
2183
  }
 
2184
 
 
2185
  for (i=0; i<sizeof(rrd_subdirs)/sizeof(rrd_subdirs[0]); i++) {
 
2186
 
 
2187
    if(snprintf(dname, sizeof(dname), "%s/%s", myGlobals.rrdPath, rrd_subdirs[i]) < 0)
 
2188
      BufferTooShort();
 
2189
    if(_mkdir(dname) == -1) {
 
2190
      if(errno != EEXIST) {
 
2191
        traceEvent(CONST_TRACE_ERROR, "RRD: Disabled - unable to create directory (err %d, %s)", errno, dname);
 
2192
        setPluginStatus("Disabled - unable to create rrd subdirectory.");
 
2193
        /* Return w/o creating the rrd thread ... disabled */
 
2194
        return(-1);
 
2195
      }
 
2196
    } else {
 
2197
      traceEvent(CONST_TRACE_INFO, "RRD: Created directory (%s)", dname);
 
2198
    }
 
2199
  }
 
2200
 
 
2201
#ifdef CFG_MULTITHREADED
 
2202
  createThread(&rrdThread, rrdMainLoop, NULL);
 
2203
  traceEvent(CONST_TRACE_INFO, "RRD: Started thread (%ld) for data collection.", rrdThread);
 
2204
#endif
 
2205
 
 
2206
  fflush(stdout);
 
2207
  numTotalRRDs = 0;
 
2208
  return(0);
 
2209
}
 
2210
 
 
2211
/* ****************************** */
 
2212
 
 
2213
static void termRRDfunct(void) {
 
2214
#ifdef CFG_MULTITHREADED
 
2215
  int count=0, rc;
 
2216
 
 
2217
  /* Hold until rrd is finished or 15s elapsed... */
 
2218
  traceEvent(CONST_TRACE_ALWAYSDISPLAY, "RRD: Locking mutex (may block for a little while)");
 
2219
 
 
2220
  while ((count++ < 5) && (tryLockMutex(&rrdMutex, "Termination") != 0)) {
 
2221
      sleep(3);
 
2222
  }
 
2223
  if(rrdMutex.isLocked) {
 
2224
    traceEvent(CONST_TRACE_ALWAYSDISPLAY, "RRD: Locked mutex, continuing shutdown");
 
2225
  } else {
 
2226
    traceEvent(CONST_TRACE_ALWAYSDISPLAY, "RRD: Unable to lock mutex, continuing shutdown anyway");
 
2227
  }
 
2228
 
 
2229
  if(active) {
 
2230
    rc = killThread(&rrdThread);
 
2231
    if (rc == 0)
 
2232
      traceEvent(CONST_TRACE_INFO, "RRD: killThread() succeeded");
 
2233
    else 
 
2234
      traceEvent(CONST_TRACE_ERROR, "RRD: killThread() failed, rc %s(%d)", strerror(rc), rc);
 
2235
  }
 
2236
#endif
 
2237
 
 
2238
  if(hostsFilter != NULL) free(hostsFilter);
 
2239
  if(myGlobals.rrdPath != NULL) free(myGlobals.rrdPath);
 
2240
 
 
2241
#ifdef CFG_MULTITHREADED
 
2242
  deleteMutex(&rrdMutex);
 
2243
#endif
 
2244
 
 
2245
  traceEvent(CONST_TRACE_INFO, "RRD: Thanks for using the rrdPlugin");
 
2246
  traceEvent(CONST_TRACE_ALWAYSDISPLAY, "RRD: Done");
 
2247
  fflush(stdout);
 
2248
 
 
2249
  initialized = 0; /* Reinit on restart */
 
2250
}
 
2251
 
 
2252
/* ****************************** */
 
2253
 
 
2254
/* Plugin entry fctn */
 
2255
#ifdef MAKE_STATIC_PLUGIN
 
2256
PluginInfo* rrdPluginEntryFctn(void)
 
2257
#else
 
2258
     PluginInfo* PluginEntryFctn(void)
 
2259
#endif
 
2260
{
 
2261
  traceEvent(CONST_TRACE_ALWAYSDISPLAY, "RRD: Welcome to %s. (C) 2002-04 by Luca Deri.",
 
2262
             rrdPluginInfo->pluginName);
 
2263
 
 
2264
  return(rrdPluginInfo);
 
2265
}
 
2266
 
 
2267
/* This must be here so it can access the struct PluginInfo, above */
 
2268
static void setPluginStatus(char * status) {
 
2269
  if(rrdPluginInfo->pluginStatusMessage != NULL)
 
2270
    free(rrdPluginInfo->pluginStatusMessage);
 
2271
  if(status == NULL) {
 
2272
    rrdPluginInfo->pluginStatusMessage = NULL;
 
2273
  } else {
 
2274
    rrdPluginInfo->pluginStatusMessage = strdup(status);
 
2275
  }
 
2276
}