Hi,

I would like to propose an extension to the librrd API with a new function similar to rrd_graph(), but instead of writing the generated image data to stdout it will return it in a memory buffer back to the caller.

My reason for this is two-fold:


1) I am using librrd to generate graphs via a web CGI program. This really has to be done like:

   - Output the HTTP headers, including a "Content-type: image/png"
   - call rrd_graph() to generate the image

If the rrd_graph() invocation fails for some reason, then it is not possible to provide an error message to the user since the content type is already fixed as a PNG image. So unless I can compose an error message in a PNG image (a bit difficult), then the webpage just shows up with a broken image.


2) I would like to use the FastCGI interface for speeding up web image generation, but this requires that all output goes through the FastCGI output routines. Since rrd_graph() simply does an fwrite() to stdout, it cannot be combined with FastCGI.


Looking at the rrd_graph() and rrd_graph_v() code, I can see that the generated image data is already contained in an "unsigned char" buffer. So the amount of change needed to just return this buffer back to the caller is minimal.

To avoid an API change that breaks existing code, I think the best way is to provide a new function - e.g. "rrd_graph_buffer()" - that returns the graph data in a buffer, and keep the current rrd_graph() API unchanged.

I have attached a patch against version 1.4.8 implementing this. The patch implements the rrd_graph_buffer() via the C API; I am not sure of how to do the various other language bindings, so those are not included.

I hope this patch - or something with similar functionality - can be included in a future rrdtool release.


Best regards,
Henrik
diff -ur rrdtool-1.4.8.orig/src/librrd.sym.in.in rrdtool-1.4.8/src/librrd.sym.in.in
--- rrdtool-1.4.8.orig/src/librrd.sym.in.in	2013-05-23 09:55:07.000000000 +0200
+++ rrdtool-1.4.8/src/librrd.sym.in.in	2014-02-13 08:58:36.222520350 +0100
@@ -21,6 +21,7 @@
 rrd_get_context
 rrd_get_error
 rrd_graph
+rrd_graph_buffer
 rrd_graph_v
 rrd_info
 rrd_info_free
diff -ur rrdtool-1.4.8.orig/src/rrd_graph.c rrdtool-1.4.8/src/rrd_graph.c
--- rrdtool-1.4.8.orig/src/rrd_graph.c	2013-05-23 09:55:07.000000000 +0200
+++ rrdtool-1.4.8/src/rrd_graph.c	2014-02-13 14:38:07.288224178 +0100
@@ -3871,7 +3871,7 @@
     return inp;
 }
 
-/* Now just a wrapper around rrd_graph_v */
+/* Now just a wrapper around rrd_graph_buffer, which wraps around rrd_graph_v */
 int rrd_graph(
     int argc,
     char **argv,
@@ -3882,10 +3882,41 @@
     double *ymin,
     double *ymax)
 {
+    unsigned char *imgbuffer = NULL;
+    unsigned long imgsz;
+    int result;
+ 
+    result = rrd_graph_buffer(argc,argv,prdata,xsize,ysize,ymin,ymax,&imgbuffer,&imgsz);
+    if ((result != 0) || (imgbuffer == NULL))
+        return result;
+
+    if ( fwrite(imgbuffer, imgsz, 1,
+           (stream ? stream : stdout)) == 0 && ferror(stream ? stream : stdout)){
+        rrd_set_error("writing image");
+    }
+    free(imgbuffer);
+
+    return 0;
+}
+
+int rrd_graph_buffer(
+    int argc,
+    char **argv,
+    char ***prdata,
+    int *xsize,
+    int *ysize,
+    double *ymin,
+    double *ymax,
+    unsigned char **imgbuffer,
+    unsigned long *imgsz)
+{
     int       prlines = 0;
     rrd_info_t *grinfo = NULL;
     rrd_info_t *walker;
 
+    if ((imgbuffer == NULL) || (imgsz == NULL))
+        return -1;
+
     grinfo = rrd_graph_v(argc, argv);
     if (grinfo == NULL)
         return -1;
@@ -3936,11 +3967,10 @@
             (*prdata)[prlines] = NULL;
             strcpy((*prdata)[prlines - 1], walker->value.u_str);
         } else if (strcmp(walker->key, "image") == 0) {
-            if ( fwrite(walker->value.u_blo.ptr, walker->value.u_blo.size, 1,
-                   (stream ? stream : stdout)) == 0 && ferror(stream ? stream : stdout)){
-                rrd_set_error("writing image");
-                return 0;
-            }
+            *imgbuffer = walker->value.u_blo.ptr;
+            *imgsz = walker->value.u_blo.size;
+            walker->value.u_blo.ptr = malloc(1); /* So rrd_info_free() won't free the buffer we just returned to userspace */
+            walker->value.u_blo.size = 1;
         }
         /* skip anything else */
         walker = walker->next;
diff -ur rrdtool-1.4.8.orig/src/rrd.h rrdtool-1.4.8/src/rrd.h
--- rrdtool-1.4.8.orig/src/rrd.h	2013-05-23 09:55:07.000000000 +0200
+++ rrdtool-1.4.8/src/rrd.h	2014-02-13 09:17:32.552942880 +0100
@@ -172,6 +172,16 @@
     FILE *,
     double *,
     double *);
+    int       rrd_graph_buffer(
+    int,
+    char **,
+    char ***,
+    int *,
+    int *,
+    double *,
+    double *,
+    unsigned char **,
+    unsigned long *);
     rrd_info_t *rrd_graph_v(
     int,
     char **);
_______________________________________________
rrd-users mailing list
rrd-users@lists.oetiker.ch
https://lists.oetiker.ch/cgi-bin/listinfo/rrd-users

Reply via email to