Logo Search packages:      
Sourcecode: hobbit version File versions  Download package

hobbitgraph.c

/*----------------------------------------------------------------------------*/
/* Hobbit RRD graph generator.                                                */
/*                                                                            */
/* This is a CGI script for generating graphs from the data stored in the     */
/* RRD databases.                                                             */
/*                                                                            */
/* Copyright (C) 2004-2006 Henrik Storner <henrik@hswn.dk>                    */
/*                                                                            */
/* This program is released under the GNU General Public License (GPL),       */
/* version 2. See the file "COPYING" for details.                             */
/*                                                                            */
/*----------------------------------------------------------------------------*/

static char rcsid[] = "$Id: hobbitgraph.c,v 1.51 2006/07/20 16:06:41 henrik Rel $";

#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

#include <pcre.h>
#include <rrd.h>

#include "libbbgen.h"

#define HOUR_GRAPH  "e-48h"
#define DAY_GRAPH   "e-12d"
#define WEEK_GRAPH  "e-48d"
#define MONTH_GRAPH "e-576d"

/* RRDtool 1.0.x handles graphs with no DS definitions just fine. 1.2.x does not. */
#ifdef RRDTOOL12
#ifndef HIDE_EMPTYGRAPH
#define HIDE_EMPTYGRAPH 1
#endif
#endif

#ifdef HIDE_EMPTYGRAPH
unsigned char blankimg[] = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x12\x00\x00\x0b\x12\x01\xd2\xdd\x7e\xfc\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xd1\x01\x14\x12\x21\x14\x7e\x4a\x3a\xd2\x00\x00\x00\x0d\x49\x44\x41\x54\x78\xda\x63\x60\x60\x60\x60\x00\x00\x00\x05\x00\x01\x7a\xa8\x57\x50\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82";
#endif

char *hostname = NULL;
char **hostlist = NULL;
int hostlistsize = 0;
char *displayname = NULL;
char *service = NULL;
char *period = NULL;
time_t persecs = 0;
char *gtype = NULL;
char *glegend = NULL;
enum {ACT_MENU, ACT_SELZOOM, ACT_SHOWZOOM, ACT_VIEW} action = ACT_VIEW;
time_t graphstart = 0;
time_t graphend = 0;
int haveupper = 0;
double upperlimit = 0.0;
int havelower = 0;
double lowerlimit = 0.0;
int graphwidth = 0;
int graphheight = 0;
int ignorestalerrds = 0;

int coloridx = 0;
char *colorlist[] = { 
      "0000FF", "FF0000", "00CC00", "FF00FF", 
      "555555", "880000", "000088", "008800", 
      "008888", "888888", "880088", "FFFF00", 
      "888800", "00FFFF", "00FF00", "AA8800", 
      "AAAAAA", "DD8833", "DDCC33", "8888FF", 
      "5555AA", "B428D3", "FF5555", "DDDDDD", 
      "AAFFAA", "AAFFFF", "FFAAFF", "FFAA55", 
      "55AAFF", "AA55FF", 
      NULL
};

typedef struct gdef_t {
      char *name;
      char *fnpat;
      char *exfnpat;
      char *title;
      char *yaxis;
      char **defs;
      struct gdef_t *next;
} gdef_t;
gdef_t *gdefs = NULL;

typedef struct rrddb_t {
      char *key;
      char *rrdfn;
      char *rrdparam;
} rrddb_t;

rrddb_t *rrddbs = NULL;
int rrddbcount = 0;
int rrddbsize = 0;
int rrdidx = 0;
int paramlen = 0;
int firstidx = -1;
int idxcount = -1;
int lastidx = 0;

void errormsg(char *msg)
{
      printf("Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
      printf("<html><head><title>Invalid request</title></head>\n");
      printf("<body>%s</body></html>\n", msg);
      exit(1);
}

void parse_query(void)
{
      cgidata_t *cgidata = NULL, *cwalk;
      char *stp;

      cgidata = cgi_request();

      cwalk = cgidata;
      while (cwalk) {
            if (strcmp(cwalk->name, "host") == 0) {
                  char *hnames = strdup(cwalk->value);

                  hostname = strtok_r(cwalk->value, ",", &stp);
                  while (hostname) {
                        if (hostlist == NULL) {
                              hostlistsize = 1;
                              hostlist = (char **)malloc(sizeof(char *));
                              hostlist[0] = strdup(hostname);
                        }
                        else {
                              hostlistsize++;
                              hostlist = (char **)realloc(hostlist, (hostlistsize * sizeof(char *)));
                              hostlist[hostlistsize-1] = strdup(hostname);
                        }

                        hostname = strtok_r(NULL, ",", &stp);
                  }

                  xfree(hnames);
                  if (hostlist) hostname = hostlist[0];
            }
            else if (strcmp(cwalk->name, "service") == 0) {
                  service = strdup(cwalk->value);
            }
            else if (strcmp(cwalk->name, "disp") == 0) {
                  displayname = strdup(cwalk->value);
            }
            else if (strcmp(cwalk->name, "graph") == 0) {
                  if (strcmp(cwalk->value, "hourly") == 0) {
                        period = HOUR_GRAPH;
                        persecs = 48*60*60;
                        gtype = strdup(cwalk->value);
                        glegend = "Last 48 Hours";
                  }
                  else if (strcmp(cwalk->value, "daily") == 0) {
                        period = DAY_GRAPH;
                        persecs = 12*24*60*60;
                        gtype = strdup(cwalk->value);
                        glegend = "Last 12 Days";
                  }
                  else if (strcmp(cwalk->value, "weekly") == 0) {
                        period = WEEK_GRAPH;
                        persecs = 48*24*60*60;
                        gtype = strdup(cwalk->value);
                        glegend = "Last 48 Days";
                  }
                  else if (strcmp(cwalk->value, "monthly") == 0) {
                        period = MONTH_GRAPH;
                        persecs = 576*24*60*60;
                        gtype = strdup(cwalk->value);
                        glegend = "Last 576 Days";
                  }
                  else if (strcmp(cwalk->value, "custom") == 0) {
                        period = NULL;
                        persecs = 0;
                        gtype = strdup(cwalk->value);
                        glegend = "";
                  }
            }
            else if (strcmp(cwalk->name, "first") == 0) {
                  firstidx = atoi(cwalk->value) - 1;
            }
            else if (strcmp(cwalk->name, "count") == 0) {
                  idxcount = atoi(cwalk->value);
                  lastidx = firstidx + idxcount - 1;
            }
            else if (strcmp(cwalk->name, "action") == 0) {
                  if (cwalk->value) {
                        if      (strcmp(cwalk->value, "menu") == 0) action = ACT_MENU;
                        else if (strcmp(cwalk->value, "selzoom") == 0) action = ACT_SELZOOM;
                        else if (strcmp(cwalk->value, "showzoom") == 0) action = ACT_SHOWZOOM;
                        else if (strcmp(cwalk->value, "view") == 0) action = ACT_VIEW;
                  }
            }
            else if (strcmp(cwalk->name, "graph_start") == 0) {
                  if (cwalk->value) graphstart = atoi(cwalk->value);
            }
            else if (strcmp(cwalk->name, "graph_end") == 0) {
                  if (cwalk->value) graphend = atoi(cwalk->value);
            }
            else if (strcmp(cwalk->name, "upper") == 0) {
                  if (cwalk->value) { upperlimit = atof(cwalk->value); haveupper = 1; }
            }
            else if (strcmp(cwalk->name, "lower") == 0) {
                  if (cwalk->value) { lowerlimit = atof(cwalk->value); havelower = 1; }
            }
            else if (strcmp(cwalk->name, "graph_width") == 0) {
                  if (cwalk->value) graphwidth = atoi(cwalk->value);
            }
            else if (strcmp(cwalk->name, "graph_height") == 0) {
                  if (cwalk->value) graphheight = atoi(cwalk->value);
            }
            else if (strcmp(cwalk->name, "nostale") == 0) {
                  ignorestalerrds = 1;
            }

            cwalk = cwalk->next;
      }

      if (hostlistsize == 1) {
            xfree(hostlist); hostlist = NULL;
      }
      else {
            displayname = hostname = strdup("");
      }

      if ((hostname == NULL) || (service == NULL)) errormsg("Invalid request - no host or service");
      if (displayname == NULL) displayname = hostname;
      if (graphstart && graphend) {
            char t1[15], t2[15];

            persecs = (graphend - graphstart);
            
            strftime(t1, sizeof(t1), "%d/%b/%Y", localtime(&graphstart));
            strftime(t2, sizeof(t2), "%d/%b/%Y", localtime(&graphend));
            glegend = (char *)malloc(40);
            sprintf(glegend, "%s - %s", t1, t2);
      }
}


void load_gdefs(char *fn)
{
      FILE *fd;
      strbuffer_t *inbuf;
      char *p;
      gdef_t *newitem = NULL;
      char **alldefs = NULL;
      int alldefcount = 0, alldefidx = 0;

      inbuf = newstrbuffer(0);
      fd = stackfopen(fn, "r", NULL);
      if (fd == NULL) errormsg("Cannot load graph definitions");
      while (stackfgets(inbuf, NULL)) {
            p = strchr(STRBUF(inbuf), '\n'); if (p) *p = '\0';
            p = STRBUF(inbuf); p += strspn(p, " \t");
            if ((strlen(p) == 0) || (*p == '#')) continue;

            if (*p == '[') {
                  char *delim;
                  
                  if (newitem) {
                        /* Save the current one, and start on the next item */
                        alldefs[alldefidx] = NULL;
                        newitem->defs = alldefs;
                        newitem->next = gdefs;
                        gdefs = newitem;
                  }
                  newitem = calloc(1, sizeof(gdef_t));
                  delim = strchr(p, ']'); if (delim) *delim = '\0';
                  newitem->name = strdup(p+1);
                  alldefcount = 10;
                  alldefs = (char **)malloc((alldefcount+1) * sizeof(char *));
                  alldefidx = 0;
            }
            else if (strncmp(p, "FNPATTERN", 9) == 0) {
                  p += 9; p += strspn(p, " \t");
                  newitem->fnpat = strdup(p);
            }
            else if (strncmp(p, "EXFNPATTERN", 11) == 0) {
                  p += 11; p += strspn(p, " \t");
                  newitem->exfnpat = strdup(p);
            }
            else if (strncmp(p, "TITLE", 5) == 0) {
                  p += 5; p += strspn(p, " \t");
                  newitem->title = strdup(p);
            }
            else if (strncmp(p, "YAXIS", 5) == 0) {
                  p += 5; p += strspn(p, " \t");
                  newitem->yaxis = strdup(p);
            }
            else {
                  if (alldefidx == alldefcount) {
                        /* Must expand alldefs */
                        alldefcount += 5;
                        alldefs = (char **)realloc(alldefs, (alldefcount+1) * sizeof(char *));
                  }
                  alldefs[alldefidx++] = strdup(p);
            }
      }

      /* Pick up the last item */
      if (newitem) {
            /* Save the current one, and start on the next item */
            alldefs[alldefidx] = NULL;
            newitem->defs = alldefs;
            newitem->next = gdefs;
            gdefs = newitem;
      }

      stackfclose(fd);
      freestrbuffer(inbuf);
}

char *colon_escape(char *buf)
{
      static char *result = NULL;
      int count = 0;
      char *p, *inp, *outp;

      p = buf; while ((p = strchr(p, ':')) != NULL) { count++; p++; }
      if (count == 0) return buf;

      if (result) xfree(result);
      result = (char *) malloc(strlen(buf) + count + 1);
      *result = '\0';

      inp = buf; outp = result;
      while (*inp) {
            p = strchr(inp, ':');
            if (p == NULL) {
                  strcat(outp, inp);
                  inp += strlen(inp);
                  outp += strlen(outp);
            }
            else {
                  *p = '\0';
                  strcat(outp, inp); strcat(outp, "\\:");
                  *p = ':';
                  inp = p+1;
                  outp = outp + strlen(outp);
            }
      }

      *outp = '\0';
      return result;
}

char *expand_tokens(char *tpl)
{
      static char result[4096];
      char *inp, *outp, *p;

      if (strchr(tpl, '@') == NULL) return tpl;

      *result = '\0'; inp = tpl; outp = result;
      while (*inp) {
            p = strchr(inp, '@');
            if (p == NULL) {
                  strcpy(outp, inp);
                  inp += strlen(inp);
                  outp += strlen(outp);
                  continue;
            }

            *p = '\0';
            if (strlen(inp)) {
                  strcpy(outp, inp);
                  inp = p;
                  outp += strlen(outp);
            }
            *p = '@';

            if (strncmp(inp, "@RRDFN@", 7) == 0) {
                  strcpy(outp, colon_escape(rrddbs[rrdidx].rrdfn));
                  inp += 7;
                  outp += strlen(outp);
            }
            else if (strncmp(inp, "@RRDPARAM@", 10) == 0) {
                  /* 
                   * We do a colon-escape first, then change all commas to slashes as
                   * this is a common mangling used by multiple backends (disk, http, iostat...)
                   */
                  if (rrddbs[rrdidx].rrdparam) {
                        char *p;
                        sprintf(outp, "%-*s", paramlen, colon_escape(rrddbs[rrdidx].rrdparam));
                        p = outp; while ((p = strchr(p, ',')) != NULL) *p = '/';
                        outp += strlen(outp);
                  }
                  inp += 10;
            }
            else if (strncmp(inp, "@RRDIDX@", 8) == 0) {
                  char numstr[10];

                  sprintf(numstr, "%d", rrdidx);
                  strcpy(outp, numstr);
                  inp += 8;
                  outp += strlen(outp);
            }
            else if (strncmp(inp, "@SERVICE@", 9) == 0) {
                  strcpy(outp, service);
                  inp += 9;
                  outp += strlen(outp);
            }
            else if (strncmp(inp, "@COLOR@", 7) == 0) {
                  strcpy(outp, colorlist[coloridx]);
                  inp += 7;
                  outp += strlen(outp);
                  coloridx++; if (colorlist[coloridx] == NULL) coloridx = 0;
            }
            else {
                  strcpy(outp, "@");
                  inp += 1;
                  outp += 1;
            }
      }
      *outp = '\0';

      return result;
}

int rrd_name_compare(const void *v1, const void *v2)
{
      rrddb_t *r1 = (rrddb_t *)v1;
      rrddb_t *r2 = (rrddb_t *)v2;

      return strcmp(r1->key, r2->key);
}

void graph_link(FILE *output, char *uri, char *grtype, time_t seconds)
{
      time_t gstart, gend;

      fprintf(output, "<tr>\n");

      switch (action) {
        case ACT_MENU:
            fprintf(output, "  <td align=\"left\"><img src=\"%s&amp;action=view&amp;graph=%s\" alt=\"%s graph\"></td>\n",
                  uri, grtype, grtype);
            fprintf(output, "  <td align=\"left\" valign=\"top\"> <a href=\"%s&amp;graph=%s&amp;action=selzoom\"> <img src=\"%s/zoom.gif\" border=0 alt=\"Zoom graph\" style='padding: 3px'> </a> </td>\n",
                  uri, grtype, getenv("BBSKIN"));
            break;

        case ACT_SELZOOM:
        case ACT_SHOWZOOM:
            if (graphend == 0) gend = time(NULL); else gend = graphend;
            if (graphstart == 0) gstart = gend - persecs; else gstart = graphstart;

            fprintf(output, "  <td align=\"left\"><img id='zoomGraphImage' src=\"%s&amp;graph=%s&amp;action=view&amp;graph_start=%u&amp;graph_end=%u&amp;graph_height=%d&amp;graph_width=%d\" alt=\"Zoom source image\"></td>\n",
                  uri, grtype, (int) gstart, (int) gend, graphheight, graphwidth);
            break;

        case ACT_VIEW:
            break;
      }

      fprintf(output, "</tr>\n");
}

int main(int argc, char *argv[])
{
      char *rrddir = NULL;
      char *gdeffn = NULL;
      char *graphfn = "-";
      char *envarea = NULL;

      gdef_t *gdef = NULL, *gdefuser = NULL;
      int wantsingle = 0;
      char **rrdargs = NULL;

      DIR *dir;
      char **calcpr  = NULL;
      int argi, pcount, argcount, rrdargcount, xsize, ysize, result;
      double ymin, ymax;
      time_t now;
      char timestamp[100];
      char graphtitle[1024];
      char graphopt[30];
      char heightopt[30];
      char widthopt[30];

      char *okuri, *p;
      int urilen;

      graphwidth = atoi(xgetenv("RRDWIDTH"));
      graphheight = atoi(xgetenv("RRDHEIGHT"));

      /* See what we want to do - i.e. get hostname, service and graph-type */
      parse_query();
      now = time(NULL);

      /* Handle any commandline args */
      for (argi=1; (argi < argc); argi++) {
            if (strcmp(argv[argi], "--debug") == 0) {
                  debug = 1;
            }
            else if (argnmatch(argv[argi], "--env=")) {
                  char *p = strchr(argv[argi], '=');
                  loadenv(p+1, envarea);
            }
            else if (argnmatch(argv[argi], "--area=")) {
                  char *p = strchr(argv[argi], '=');
                  envarea = strdup(p+1);
            }
            else if (argnmatch(argv[argi], "--rrddir=")) {
                  char *p = strchr(argv[argi], '=');
                  rrddir = strdup(p+1);
            }
            else if (argnmatch(argv[argi], "--config=")) {
                  char *p = strchr(argv[argi], '=');
                  gdeffn = strdup(p+1);
            }
            else if (strcmp(argv[argi], "--save=") == 0) {
                  char *p = strchr(argv[argi], '=');
                  graphfn = strdup(p+1);
            }
      }

      redirect_cgilog("hobbitgraph");

      p = xgetenv("SCRIPT_NAME");
      urilen = strlen(p);
      if (hostlist) { int i; for (i = 0; (i < hostlistsize); i++) urilen += (strlen(hostlist[i]) + 10); }
      okuri = (char *)malloc(urilen + 2048);

      strcpy(okuri, p);
      p = strchr(okuri, '?'); if (p) *p = '\0'; else p = okuri + strlen(okuri);

      if (hostlist) {
            int i;

            p += sprintf(p, "?host=%s", hostlist[0]);
            for (i = 1; (i < hostlistsize); i++) p += sprintf(p, ",%s", hostlist[i]);
      }
      else
            p += sprintf(p, "?host=%s", hostname);

      p += sprintf(p, "&amp;service=%s&amp;graph_height=%d&amp;graph_width=%d", 
                 service, graphheight, graphwidth);
      if (displayname != hostname) p += sprintf(p, "&amp;disp=%s", displayname);
      if (firstidx != -1) p += sprintf(p, "&amp;first=%d", firstidx+1);
      if (idxcount != -1) p += sprintf(p, "&amp;count=%d", idxcount);
      if (ignorestalerrds) p += sprintf(p, "&amp;nostale");

      switch (action) {
        case ACT_MENU:
            fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
            sethostenv(displayname, "", service, colorname(COL_GREEN), hostname);
            headfoot(stdout, "graphs", "", "header", COL_GREEN);

            fprintf(stdout, "<table align=\"center\" summary=\"Graphs\">\n");

            graph_link(stdout, okuri, "hourly",      48*60*60);
            graph_link(stdout, okuri, "daily",    12*24*60*60);
            graph_link(stdout, okuri, "weekly",   48*24*60*60);
            graph_link(stdout, okuri, "monthly", 576*24*60*60);

            fprintf(stdout, "</table>\n");

            headfoot(stdout, "graphs", "", "footer", COL_GREEN);
            return 0;

        case ACT_SELZOOM:
            fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
            sethostenv(displayname, "", service, colorname(COL_GREEN), hostname);
            headfoot(stdout, "graphs", "", "header", COL_GREEN);


            fprintf(stdout, "  <div id='zoomBox' style='position:absolute; overflow:none; left:0px; top:0px; width:0px; height:0px; visibility:visible; background:red; filter:alpha(opacity=50); -moz-opacity:0.5; -khtml-opacity:0.5'></div>\n");
            fprintf(stdout, "  <div id='zoomSensitiveZone' style='position:absolute; overflow:none; left:0px; top:0px; width:0px; height:0px; visibility:visible; cursor:crosshair; background:blue; filter:alpha(opacity=0); -moz-opacity:0; -khtml-opacity:0'></div>\n");

            fprintf(stdout, "<table align=\"center\" summary=\"Graphs\">\n");
            graph_link(stdout, okuri, gtype, 0);
            fprintf(stdout, "</table>\n");

            {
                  char zoomjsfn[PATH_MAX];
                  struct stat st;

                  sprintf(zoomjsfn, "%s/web/zoom.js", xgetenv("BBHOME"));
                  if (stat(zoomjsfn, &st) == 0) {
                        FILE *fd;
                        char *buf;
                        size_t n;
                        char *zoomrightoffsetmarker = "var cZoomBoxRightOffset = -";
                        char *zoomrightoffsetp;

                        fd = fopen(zoomjsfn, "r");
                        if (fd) {
                              buf = (char *)malloc(st.st_size+1);
                              n = fread(buf, 1, st.st_size, fd);
                              fclose(fd);

#ifdef RRDTOOL12
                              zoomrightoffsetp = strstr(buf, zoomrightoffsetmarker);
                              if (zoomrightoffsetp) {
                                    zoomrightoffsetp += strlen(zoomrightoffsetmarker);
                                    memcpy(zoomrightoffsetp, "30", 2);
                              }
#endif

                              fwrite(buf, 1, n, stdout);
                        }
                  }
            }


            headfoot(stdout, "graphs", "", "footer", COL_GREEN);
            return 0;

        case ACT_SHOWZOOM:
            fprintf(stdout, "Content-type: %s\n\n", xgetenv("HTMLCONTENTTYPE"));
            sethostenv(displayname, "", service, colorname(COL_GREEN), hostname);
            headfoot(stdout, "graphs", "", "header", COL_GREEN);

            fprintf(stdout, "<table align=\"center\" summary=\"Graphs\">\n");

            graph_link(stdout, okuri, gtype, 0);

            fprintf(stdout, "</table>\n");

            headfoot(stdout, "graphs", "", "footer", COL_GREEN);
            return 0;

        case ACT_VIEW:
            break;
      }

      /* Find the config-file and load it */
      if (gdeffn == NULL) {
            char fnam[PATH_MAX];
            sprintf(fnam, "%s/etc/hobbitgraph.cfg", xgetenv("BBHOME"));
            gdeffn = strdup(fnam);
      }
      load_gdefs(gdeffn);

      /* Determine the directory with the host RRD files, and go there. */
      if (rrddir == NULL) {
            char dnam[PATH_MAX];

            if (hostlist) sprintf(dnam, "%s", xgetenv("BBRRDS"));
            else sprintf(dnam, "%s/%s", xgetenv("BBRRDS"), hostname);

            rrddir = strdup(dnam);
      }
      if (chdir(rrddir)) errormsg("Cannot access RRD directory");


      if (strchr(service, ':') || strchr(service, '.')) {
            /*
             * service is "tcp:foo" - so use the "tcp" graph definition, but for a
             * single service (as if service was set to just "foo").
             */
            char *delim = service + strcspn(service, ":.");
            char *realservice;

            *delim = '\0';
            realservice = strdup(delim+1);

            /* The requested gdef only acts as a fall-back solution so dont set gdef here. */
            for (gdefuser = gdefs; (gdefuser && strcmp(service, gdefuser->name)); gdefuser = gdefuser->next) ;
            strcpy(service, realservice);
            wantsingle = 1;

            xfree(realservice);
      }

      /*
       * Lookup which RRD file corresponds to the service-name, and how we handle this graph.
       * We first lookup the service name in the graph definition list.
       * If that fails, then we try mapping it via the BB service -> RRD map.
       */
      for (gdef = gdefs; (gdef && strcmp(service, gdef->name)); gdef = gdef->next) ;
      if (gdef == NULL) {
            if (gdefuser) {
                  gdef = gdefuser;
            }
            else {
                  hobbitrrd_t *ldef = find_hobbit_rrd(service, NULL);
                  if (ldef) {
                        for (gdef = gdefs; (gdef && strcmp(ldef->hobbitrrdname, gdef->name)); gdef = gdef->next) ;
                        wantsingle = 1;
                  }
            }
      }
      if (gdef == NULL) errormsg("Unknown graph requested");

      if (hostlist && (gdef->fnpat == NULL)) {
            char *multiname = (char *)malloc(strlen(gdef->name) + 7);
            sprintf(multiname, "%s-multi", gdef->name);
            for (gdef = gdefs; (gdef && strcmp(multiname, gdef->name)); gdef = gdef->next) ;
            if (gdef == NULL) errormsg("Unknown multi-graph requested");
            xfree(multiname);
      }

      /* What RRD files do we have matching this request? */
      if (hostlist || (gdef->fnpat == NULL)) {
            /*
             * No pattern, just a single file. It doesnt matter if it exists, because
             * these types of graphs usually have a hard-coded value for the RRD filename
             * in the graph definition.
             */
            rrddbcount = rrddbsize = (hostlist ? hostlistsize : 1);
            rrddbs = (rrddb_t *)malloc((rrddbsize + 1) * sizeof(rrddb_t));

            if (!hostlist) {
                  rrddbs[0].key = strdup(service);
                  rrddbs[0].rrdfn = (char *)malloc(strlen(gdef->name) + strlen(".rrd") + 1);
                  sprintf(rrddbs[0].rrdfn, "%s.rrd", gdef->name);
                  rrddbs[0].rrdparam = NULL;
            }
            else {
                  int i, maxlen;
                  char paramfmt[10];

                  for (i=0, maxlen=0; (i < hostlistsize); i++) {
                        if (strlen(hostlist[i]) > maxlen) maxlen = strlen(hostlist[i]);
                  }
                  sprintf(paramfmt, "%%-%ds", maxlen+1);

                  for (i=0; (i < hostlistsize); i++) {
                        rrddbs[i].key = strdup(service);
                        rrddbs[i].rrdfn = (char *)malloc(strlen(hostlist[i]) + strlen(gdef->fnpat) + 2);
                        sprintf(rrddbs[i].rrdfn, "%s/%s", hostlist[i], gdef->fnpat);

                        rrddbs[i].rrdparam = (char *)malloc(maxlen + 2);
                        sprintf(rrddbs[i].rrdparam, paramfmt, hostlist[i]);
                  }
            }
      }
      else {
            struct dirent *d;
            pcre *pat, *expat = NULL;
            const char *errmsg;
            int errofs, result;
            int ovector[30];
            struct stat st;
            time_t now = time(NULL);

            /* Scan the directory to see what RRD files are there that match */
            dir = opendir("."); if (dir == NULL) errormsg("Unexpected error while accessing RRD directory");

            /* Setup the pattern to match filenames against */
            pat = pcre_compile(gdef->fnpat, PCRE_CASELESS, &errmsg, &errofs, NULL);
            if (!pat) {
                  char msg[8192];

                  snprintf(msg, sizeof(msg), "hobbitgraph.cfg error, PCRE pattern %s invalid: %s, offset %d\n",
                         gdef->fnpat, errmsg, errofs);
                  errormsg(msg);
            }
            if (gdef->exfnpat) {
                  expat = pcre_compile(gdef->exfnpat, PCRE_CASELESS, &errmsg, &errofs, NULL);
                  if (!expat) {
                        char msg[8192];

                        snprintf(msg, sizeof(msg), 
                               "hobbitgraph.cfg error, PCRE pattern %s invalid: %s, offset %d\n",
                               gdef->exfnpat, errmsg, errofs);
                        errormsg(msg);
                  }
            }

            /* Allocate an initial filename table */
            rrddbsize = 5;
            rrddbs = (rrddb_t *) malloc((rrddbsize+1) * sizeof(rrddb_t));

            while ((d = readdir(dir)) != NULL) {
                  char *ext;
                  char param[PATH_MAX];

                  /* Ignore dot-files and files with names shorter than ".rrd" */
                  if (*(d->d_name) == '.') continue;
                  ext = d->d_name + strlen(d->d_name) - strlen(".rrd");
                  if ((ext <= d->d_name) || (strcmp(ext, ".rrd") != 0)) continue;

                  /* First check the exclude pattern. */
                  if (expat) {
                        result = pcre_exec(expat, NULL, d->d_name, strlen(d->d_name), 0, 0, 
                                       ovector, (sizeof(ovector)/sizeof(int)));
                        if (result >= 0) continue;
                  }

                  /* Then see if the include pattern matches. */
                  result = pcre_exec(pat, NULL, d->d_name, strlen(d->d_name), 0, 0, 
                                 ovector, (sizeof(ovector)/sizeof(int)));
                  if (result < 0) continue;

                  if (wantsingle) {
                        /* "Single" graph, i.e. a graph for a service normally included in a bundle (tcp) */
                        if (strstr(d->d_name, service) == NULL) continue;
                  }

                  /* 
                   * Has it been updated recently (within the past 24 hours) ? 
                   * We dont want old graphs to mess up multi-displays.
                   */
                  if (ignorestalerrds && (stat(d->d_name, &st) == 0) && ((now - st.st_mtime) > 86400)) {
                        continue;
                  }

                  /* We have a matching file! */
                  rrddbs[rrddbcount].rrdfn = strdup(d->d_name);
                  if (pcre_copy_substring(d->d_name, ovector, result, 1, param, sizeof(param)) > 0) {
                        /*
                         * This is ugly, but I cannot find a pretty way of un-mangling
                         * the disk- and http-data that has been molested by the back-end.
                         */
                        if ((strcmp(param, ",root") == 0) &&
                            ((strncmp(gdef->name, "disk", 4) == 0) || (strncmp(gdef->name, "inode", 5) == 0)) ) {
                              rrddbs[rrddbcount].rrdparam = strdup(",");
                        }
                        else if ((strcmp(gdef->name, "http") == 0) && (strncmp(param, "http", 4) != 0)) {
                              rrddbs[rrddbcount].rrdparam = (char *)malloc(strlen("http://")+strlen(param)+1);
                              sprintf(rrddbs[rrddbcount].rrdparam, "http://%s", param);
                        }
                        else {
                              rrddbs[rrddbcount].rrdparam = strdup(param);
                        }

                        if (strlen(rrddbs[rrddbcount].rrdparam) > paramlen) {
                              paramlen = strlen(rrddbs[rrddbcount].rrdparam);
                        }

                        rrddbs[rrddbcount].key = strdup(rrddbs[rrddbcount].rrdparam);
                  }
                  else {
                        rrddbs[rrddbcount].key = strdup(d->d_name);
                        rrddbs[rrddbcount].rrdparam = NULL;
                  }

                  rrddbcount++;
                  if (rrddbcount == rrddbsize) {
                        rrddbsize += 5;
                        rrddbs = (rrddb_t *)realloc(rrddbs, (rrddbsize+1) * sizeof(rrddb_t));
                  }
            }
            pcre_free(pat);
            if (expat) pcre_free(expat);
            closedir(dir);
      }
      rrddbs[rrddbcount].key = rrddbs[rrddbcount].rrdfn = rrddbs[rrddbcount].rrdparam = NULL;

      /* Sort them so the display looks prettier */
      qsort(&rrddbs[0], rrddbcount, sizeof(rrddb_t), rrd_name_compare);

      /* Setup the title */
      if (!gdef->title) gdef->title = strdup("");
      if (strncmp(gdef->title, "exec:", 5) == 0) {
            char *pcmd;
            int i, pcmdlen = 0;
            FILE *pfd;
            char *p;

            pcmdlen = strlen(gdef->title+5) + strlen(displayname) + strlen(service) + strlen(glegend) + 5;
            for (i=0; (i<rrddbcount); i++) pcmdlen += (strlen(rrddbs[i].rrdfn) + 3);

            p = pcmd = (char *)malloc(pcmdlen+1);
            p += sprintf(p, "%s %s %s \"%s\"", gdef->title+5, displayname, service, glegend);
            for (i=0; (i<rrddbcount); i++) {
                  if ((firstidx == -1) || ((i >= firstidx) && (i <= lastidx))) {
                        p += sprintf(p, " \"%s\"", rrddbs[i].rrdfn);
                  }
            }
            pfd = popen(pcmd, "r");
            if (pfd) {
                  fgets(graphtitle, sizeof(graphtitle), pfd);
                  pclose(pfd);
            }

            /* Drop any newline at end of the title */
            p = strchr(graphtitle, '\n'); if (p) *p = '\0';
      }
      else {
            sprintf(graphtitle, "%s %s %s", displayname, gdef->title, glegend);
      }

      /*
       * Setup the arguments for calling rrd_graph. 
       * There's 11 standard arguments, plus the 
       * graph-specific ones (which may be repeated if
       * there are multiple RRD-files to handle).
       */
      sprintf(heightopt, "-h%d", graphheight);
      sprintf(widthopt, "-w%d", graphwidth);
      for (pcount = 0; (gdef->defs[pcount]); pcount++) ;
      argcount = (15 + pcount*rrddbcount + 1); argi = 0;
      rrdargs = (char **) calloc(argcount+1, sizeof(char *));
      rrdargs[argi++]  = "rrdgraph";
      rrdargs[argi++]  = graphfn;
      rrdargs[argi++]  = "--title";
      rrdargs[argi++]  = graphtitle;
      rrdargs[argi++]  = widthopt;
      rrdargs[argi++]  = heightopt;
      rrdargs[argi++]  = "-v";
      rrdargs[argi++]  = gdef->yaxis;
      rrdargs[argi++]  = "-a";
      rrdargs[argi++] = "PNG";

      if (haveupper) {
            sprintf(graphopt, "-u %f", upperlimit);
            rrdargs[argi++] = strdup(graphopt);
      }
      if (havelower) {
            sprintf(graphopt, "-l %f", lowerlimit);
            rrdargs[argi++] = strdup(graphopt);
      }
      if (haveupper || havelower) rrdargs[argi++] = "--rigid";

      if (graphstart) sprintf(graphopt, "-s %u", (unsigned int) graphstart);
      else sprintf(graphopt, "-s %s", period);
      rrdargs[argi++] = strdup(graphopt);

      if (graphend) {
            sprintf(graphopt, "-e %u", (unsigned int) graphend);
            rrdargs[argi++] = strdup(graphopt);
      }

      for (rrdidx=0; (rrdidx < rrddbcount); rrdidx++) {
            if ((firstidx == -1) || ((rrdidx >= firstidx) && (rrdidx <= lastidx))) {
                  int i;
                  for (i=0; (gdef->defs[i]); i++) rrdargs[argi++] = strdup(expand_tokens(gdef->defs[i]));
            }
      }

#ifdef RRDTOOL12
      strftime(timestamp, sizeof(timestamp), "COMMENT:Updated\\: %d-%b-%Y %H\\:%M\\:%S", localtime(&now));
#else
      strftime(timestamp, sizeof(timestamp), "COMMENT:Updated: %d-%b-%Y %H:%M:%S", localtime(&now));
#endif
      rrdargs[argi++] = strdup(timestamp);


      rrdargcount = argi; rrdargs[argi++] = NULL;


      if (debug) { for (argi=0; (argi < rrdargcount); argi++) dbgprintf("%s\n", rrdargs[argi]); }

      /* If sending to stdout, print the HTTP header first. */
      if (strcmp(graphfn, "-") == 0) {
            time_t expiretime = now + 300;
            char expirehdr[100];

            printf("Content-type: image/png\n");
            strftime(expirehdr, sizeof(expirehdr), "Expires: %a, %d %b %Y %H:%M:%S GMT", gmtime(&expiretime));
            printf("%s\n", expirehdr);
            printf("\n");

#ifdef HIDE_EMPTYGRAPH
            /* It works, but we still get the "zoom" magnifying glass which looks odd */
            if (rrddbcount == 0) {
                  /* No graph */
                  fwrite(blankimg, 1, sizeof(blankimg), stdout);
                  return 0;
            }
#endif
      }

      /* All set - generate the graph */
      rrd_clear_error();
#ifdef RRDTOOL12
      result = rrd_graph(rrdargcount, rrdargs, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
#else
      result = rrd_graph(rrdargcount, rrdargs, &calcpr, &xsize, &ysize);
#endif

      /* Was it OK ? */
      if (rrd_test_error() || (result != 0)) {
            if (calcpr) { 
                  int i;
                  for (i=0; (calcpr[i]); i++) xfree(calcpr[i]);
                  calcpr = NULL;
            }

            errormsg(rrd_get_error());
      }

      if (displayname != hostname) { xfree(displayname); displayname = NULL; }
      if (hostname) { xfree(hostname); hostname = NULL; }
      if (service) { xfree(service); service = NULL; }
      if (gtype) { xfree(gtype); gtype = NULL; }
      glegend = period = NULL; /* Dont free these - they are static strings in the program. */
      for (argi=0; (argi < rrddbcount); argi++) {
            if (rrddbs[argi].key) xfree(rrddbs[argi].key);
            if (rrddbs[argi].rrdfn) xfree(rrddbs[argi].rrdfn);
            if (rrddbs[argi].rrdparam) xfree(rrddbs[argi].rrdparam);
      }
      xfree(rrddbs);
      rrddbs = NULL;
      coloridx = rrddbcount = rrddbsize = rrdidx = paramlen = 0;

      gdef = gdefuser = NULL;
      wantsingle = 0;

      return 0;
}


Generated by  Doxygen 1.6.0   Back to index