Bruce Campbell <bruce.campbell@apnic.net>
Sean Summers <sean@Fenstermaker.com> (RPM .spec)
Christophe Van Ginneken <Christophe.VanGinneken@ubizen.com> (--no-legend)
-
+ Wolfgang Schrimm <wschrimm@uni-hd.de> xport function
Documentation
/* convert the data array into perl format */
datai=data;
retar=newAV();
- for (i = start; i <= end; i += step){
+ for (i = start+step; i <= end; i += step){
line = newAV();
for (ii = 0; ii < ds_cnt; ii++){
av_push(line,(isnan(*datai) ? &PL_sv_undef : newSVnv(*datai)));
}
free(data);
EXTEND(sp,5);
- PUSHs(sv_2mortal(newSViv(start)));
+ PUSHs(sv_2mortal(newSViv(start+step)));
PUSHs(sv_2mortal(newSViv(step)));
PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+int
+rrd_xport(...)
+ PROTOTYPE: @
+ PREINIT:
+ time_t start,end;
+ int xsize;
+ unsigned long step, col_cnt,row_cnt,i,ii;
+ rrd_value_t *data,*ptr;
+ char **argv,**legend_v;
+ AV *retar,*line,*names;
+ PPCODE:
+ argv = (char **) malloc((items+1)*sizeof(char *));
+ argv[0] = "dummy";
+ for (i = 0; i < items; i++) {
+ STRLEN len;
+ char *handle = SvPV(ST(i),len);
+ /* actually copy the data to make sure possible modifications
+ on the argv data does not backfire into perl */
+ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
+ strcpy(argv[i+1],handle);
+ }
+ optind=0; opterr=0;
+ rrd_clear_error();
+ rrd_xport(items+1,argv,&xsize,&start,&end,&step,&col_cnt,&legend_v,&data);
+ for (i=0; i < items; i++) {
+ free(argv[i+1]);
+ }
+ free(argv);
+ if (rrd_test_error()) XSRETURN_UNDEF;
+
+ /* convert the legend_v into perl format */
+ names=newAV();
+ for (ii = 0; ii < col_cnt; ii++){
+ av_push(names,newSVpv(legend_v[ii],0));
+ free(legend_v[ii]);
+ }
+ free(legend_v);
+
+ /* convert the data array into perl format */
+ ptr=data;
+ retar=newAV();
+ for (i = start+step; i <= end; i += step){
+ line = newAV();
+ for (ii = 0; ii < col_cnt; ii++){
+ av_push(line,(isnan(*ptr) ? &PL_sv_undef : newSVnv(*ptr)));
+ ptr++;
+ }
+ av_push(retar,newRV_noinc((SV*)line));
+ }
+ free(data);
+
+ EXTEND(sp,7);
+ PUSHs(sv_2mortal(newSViv(start+step)));
+ PUSHs(sv_2mortal(newSViv(end)));
+ PUSHs(sv_2mortal(newSViv(step)));
+ PUSHs(sv_2mortal(newSViv(col_cnt)));
+ PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
+ PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+
SV*
rrd_info(...)
PROTOTYPE: @
POD = rrdtool.pod rrdlast.pod rrdcreate.pod rrdupdate.pod rrdtutorial.es.pod \
cdeftutorial.pod rpntutorial.pod rrdgraph-old.pod bin_dec_hex.pod \
rrdfetch.pod rrdrestore.pod rrddump.pod rrdtune.pod rrdresize.pod \
- rrdcgi.pod rrdtutorial.pod rrdinfo.pod $(SRC:.src=.pod)
+ rrdxport.pod rrdcgi.pod rrdtutorial.pod rrdinfo.pod $(SRC:.src=.pod)
PMP = RRDs.pm RRDp.pm
Change the size of individual RRAs ... Dangerous! Check L<rrdresize>.
+=item B<xport>
+
+Export data retrieved from one or several RRD. Check L<rrdxport>
+
=item B<rrdcgi>
This is a standalone tool for producing rrd graphs on the fly. Check
=head1 SEE ALSO
-rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast
+rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast, rrdxport
=head1 BUGS
--- /dev/null
+=head1 NAME
+
+rrdtool xport - Export data in XML format based on data from one or several RRD
+
+=for html <div align="right"><a href="rrdxport.pdf">PDF</a> version.</div>
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<xport>
+S<[B<-s>|B<--start> I<seconds>]>
+S<[B<-e>|B<--end> I<seconds>]>
+S<[B<-m>|B<--maxrows> I<rows>]>
+S<[B<--step> I<value>]>
+S<[B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>]>
+S<[B<CDEF:>I<vname>B<=>I<rpn-expression>]>
+S<[B<XPORT>B<:>I<vname>[B<:>I<legend>]]>
+
+=head1 DESCRIPTION
+
+The B<xport> functions main purpose is to write XML formatted
+representation of the data stored in one or several B<RRD>s. It
+can also extract numerical reports.
+
+If no I<XPORT> statements are found, there will be no output.
+
+=over
+
+=item B<-s>|B<--start> I<seconds> (default end-1day)
+
+The time when the exported range should begin. Time in seconds since
+epoch (1970-01-01) is required. Negative numbers are relative to the
+current time. By default one day worth of data will be printed.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation on how to specify time.
+
+=item B<-e>|B<--end> I<seconds> (default now)
+
+The time when the exported range should end. Time in seconds since epoch.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation of ways to specify time.
+
+=item B<-m>|B<--maxrows> I<rows> (default 400 rows)
+
+This works like the B<-w>|B<--width> parameter of I<rrdgraph>.
+In fact it is exactly the same, but the parameter was renamed to
+describe its purpose in this module. See I<rrdgraph> documentation
+for details.
+
+=item B<--step> I<value> (default automatic)
+
+See I<rrdgraph> documentation.
+
+=item B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>
+
+See I<rrdgraph> documentation.
+
+=item B<CDEF:>I<vname>B<=>I<rpn-expression>
+
+See I<rrdgraph> documentation.
+
+=item B<XPORT:>I<vname>B<:>B<:>I<legend>
+
+At least one I<XPORT> statement should be present. The values
+referenced by I<vname> are printed. Optionally add a legend.
+
+=over
+
+=head1 Output format
+
+The output is enclosed in a B<xport> element and contains two
+blocks. The first block is enclosed by a B<meta> element and
+contains some meta data. The second block is enclosed by a
+B<data> element and contains the data rows.
+
+Let's assume that the I<xport> command looks like this:
+
+ rrdtool xport \
+ --start now-1h --end now \
+ DEF:xx=host-inout.lo.rrd:output:AVERAGE \
+ DEF:yy=host-inout.lo.rrd:input:AVERAGE \
+ CDEF:aa=xx,yy,+,8,* \
+ XPORT:xx:"out bytes" \
+ XPORT:aa:"in and out bits"
+
+The resulting meta data section (the values will depend on the
+RRD characteristics):
+
+ <meta>
+ <start>1020611700</start>
+ <step>300</step>
+ <end>1020615600</end>
+ <rows>14</rows>
+ <columns>2</columns>
+ <legend>
+ <entry>out bytes</entry>
+ <entry>in and out bits</entry>
+ </legend>
+ </meta>
+
+The resulting data section:
+
+ <data>
+ <row><t>1020611700</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020612000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020612300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020612600</t><v>3.4113333333e+00</v><v>5.4581333333e+01</v></row>
+ <row><t>1020612900</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020613200</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020613500</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020613800</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020614100</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020614400</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020614700</t><v>3.7333333333e+00</v><v>5.9733333333e+01</v></row>
+ <row><t>1020615000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020615300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020615600</t><v>NaN</v><v>NaN</v></row>
+ </data>
+
+
+=head1 EXAMPLE 1
+
+ rrdtool xport \
+ DEF:out=if1-inouts.rrd:outoctets:AVERAGE \
+ XPORT:out:"out bytes"
+
+=head1 EXAMPLE 2
+
+ rrdtool xport \
+ DEF:out1=if1-inouts.rrd:outoctets:AVERAGE \
+ DEF:out2=if2-inouts.rrd:outoctets:AVERAGE \
+ CDEF:sum=out1,out2,+ \
+ XPORT:out1:"if1 out bytes" \
+ XPORT:out2:"if2 out bytes" \
+ XPORT:sum:"output sum"
+
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>oetiker@ee.ethz.chE<gt>
+
+my ($start,$end,$step,$col_cnt,$legend,$data) =
+ RRDs::xport ("-m", 400,
+ "--start", "now-1day",
+ "--end", "now",
+ "DEF:alpha=$RRD1:a:AVERAGE",
+ "DEF:beta=$RRD1:d:AVERAGE",
+ "CDEF:calc=alpha,beta,+,2,/,100,*,102,/",
+ "XPORT:alpha:original ds",
+ "XPORT:calc:calculated values",
+ );
+
+my $ERROR = RRDs::error;
+die "$0: unable to xport: $ERROR\n" if $ERROR;
+
+print "\nrrdxport test:\n\n";
+print "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\n";
+print "<xport>\n";
+print " <meta>\n";
+print " <start>$start</start>\n";
+print " <step>$step</step>\n";
+print " <end>$end</end>\n";
+print " <rows>", $#$data + 1, "</rows>\n";
+print " <columns>$col_cnt</columns>\n";
+print " <legend>\n";
+foreach my $entry (@$legend) {
+ print " <entry>$entry</entry>\n";
+}
+print " </legend>\n";
+print " </meta>\n";
+print " <data>\n";
+my $row_counter = 0;
+foreach my $row (@$data) {
+ $row_counter++;
+ print " <row id=\"$row_counter\"><t is=\"", scalar localtime($start), "\">$start</t>";
+ $start += $step;
+ foreach my $val (@$row) {
+ printf ("<v>%1.10e</v>",$val) if $val ne '';
+ print "<v>NaN</v>" if $val eq '';
+ }
+ print "</row>\n";
+}
+print " </data>\n";
+print "</xport>\n";
rrd_rpncalc.c \
rrd_tune.c \
rrd_update.c \
+ rrd_xport.c \
rrd_gfx.c rrd_gfx.h \
rrd_afm.c rrd_afm_data.c \
- getopt.h ntconfig.h parsetime.h rrd_format.h rrd_tool.h rrd.h rrd_hw.h rrd_rpncalc.h
+ getopt.h ntconfig.h parsetime.h \
+ rrd_format.h rrd_tool.h rrd_xport.h rrd.h rrd_hw.h rrd_rpncalc.h
# Build two libraries. One is a public one that gets installed in
# $prefix/lib. Libtool does not create an archive of the PIC compiled
*****************************************************************************
* $Id$
* $Log$
- * Revision 1.1 2001/02/25 22:25:05 oetiker
- * Initial revision
+ * Revision 1.2 2002/05/07 21:58:32 oetiker
+ * new command rrdtool xport integrated
+ * -- Wolfgang Schrimm <Wolfgang.Schrimm@urz.uni-heidelberg.de>
+ *
+ * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
+ * checkin
*
*****************************************************************************/
#ifdef __cplusplus
int rrd_tune(int, char **);
time_t rrd_last(int, char **);
int rrd_resize(int, char **);
+int rrd_xport(int, char **, int *, time_t *, time_t *,
+ unsigned long *, unsigned long *,
+ char ***, rrd_value_t **);
/* Transplanted from parsetime.h */
typedef enum {
conv_if(CDEF,GF_CDEF)
conv_if(VDEF,GF_VDEF)
conv_if(PART,GF_PART)
+ conv_if(XPORT,GF_XPORT)
return (-1);
}
* so CDEFs can use VDEFs and vice versa
*/
switch (im->gdes[gdi].gf) {
+ case GF_XPORT:
+ break;
case GF_VDEF:
/* A VDEF has no DS. This also signals other parts
* of rrdtool that this is a VDEF value, not a CDEF.
case GF_CDEF:
case GF_VDEF:
case GF_PART:
+ case GF_XPORT:
break;
}
}
case GF_CDEF:
case GF_VDEF:
case GF_PART:
+ case GF_XPORT:
break;
}
}
case GF_COMMENT:
case GF_HRULE:
case GF_VRULE:
+ case GF_XPORT:
break;
case GF_TICK:
for (ii = 0; ii < im->xsize; ii++)
/* If the error string is set, we exit at the end of the switch */
switch (gdp->gf) {
+ case GF_XPORT:
+ break;
case GF_COMMENT:
if (rrd_graph_legend(gdp,&line[argstart])==0)
rrd_set_error("Cannot parse comment in line: %s",line);
enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE,
GF_AREA,GF_STACK,GF_TICK,
GF_DEF, GF_CDEF, GF_VDEF,
- GF_PART};
+ GF_PART, GF_XPORT};
enum vdef_op_en {
VDEF_MAXIMUM /* like the MAX in (G)PRINT */
*****************************************************************************/
#include "rrd_tool.h"
+#include "rrd_xport.h"
void PrintUsage(char *cmd);
int CountArgs(char *aLine);
char help_list[] =
"Valid commands: create, update, graph, dump, restore,\n"
- "\t\tlast, info, fetch, tune, resize\n\n";
+ "\t\tlast, info, fetch, tune, resize, xport\n\n";
char help_create[] =
"* create - create a new RRD\n\n"
" * resize - alter the lenght of one of the RRAs in an RRD\n\n"
"\trrdtool resize filename rranum GROW|SHRINK rows\n\n";
+ char help_xport[] =
+ "* xport - generate XML dump from one or several RRD\n\n"
+ "\trrdtool xport [-s|--start seconds] [-e|--end seconds]\n"
+ "\t\t[-m|--maxrows rows]\n"
+ "\t\t[--step seconds]\n"
+ "\t\t[DEF:vname=rrd:ds-name:CF]\n"
+ "\t\t[CDEF:vname=rpn-expression]\n"
+ "\t\t[XPORT:vname:legend]\n\n";
+
char help_lic[] =
"RRDtool is distributed under the Terms of the GNU General\n"
"Public License Version 2. (www.gnu.org/copyleft/gpl.html)\n\n"
"For more information read the RRD manpages\n\n";
enum { C_NONE, C_CREATE, C_DUMP, C_INFO, C_RESTORE, C_LAST,
- C_UPDATE, C_FETCH, C_GRAPH, C_TUNE, C_RESIZE };
+ C_UPDATE, C_FETCH, C_GRAPH, C_TUNE, C_RESIZE, C_XPORT };
int help_cmd = C_NONE;
help_cmd = C_TUNE;
else if (!strcmp(cmd,"resize"))
help_cmd = C_RESIZE;
+ else if (!strcmp(cmd,"xport"))
+ help_cmd = C_XPORT;
}
fputs(help_main, stdout);
switch (help_cmd)
case C_RESIZE:
fputs(help_resize, stdout);
break;
+ case C_XPORT:
+ fputs(help_xport, stdout);
+ break;
}
fputs(help_lic, stdout);
}
free(ds_namv);
free (data);
}
+ } else if (strcmp("xport", argv[1]) == 0) {
+ int xxsize;
+ int i = 0, j = 0;
+ time_t start,end;
+ unsigned long step, col_cnt,row_cnt;
+ rrd_value_t *data,*ptr;
+ char **legend_v;
+ if(rrd_xport(argc-1, &argv[1], &xxsize,&start,&end,&step,&col_cnt,&legend_v,&data) != -1) {
+ row_cnt = (end-start)/step;
+ ptr = data;
+ printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n\n", XML_ENCODING);
+ printf("<%s>\n", ROOT_TAG);
+ printf(" <%s>\n", META_TAG);
+ printf(" <%s>%lu</%s>\n", META_START_TAG, start+step, META_START_TAG);
+ printf(" <%s>%lu</%s>\n", META_STEP_TAG, step, META_STEP_TAG);
+ printf(" <%s>%lu</%s>\n", META_END_TAG, end, META_END_TAG);
+ printf(" <%s>%lu</%s>\n", META_ROWS_TAG, row_cnt, META_ROWS_TAG);
+ printf(" <%s>%lu</%s>\n", META_COLS_TAG, col_cnt, META_COLS_TAG);
+ printf(" <%s>\n", LEGEND_TAG);
+ for (j = 0; j < col_cnt; j++) {
+ char *entry = NULL;
+ entry = legend_v[j];
+ printf(" <%s>%s</%s>\n", LEGEND_ENTRY_TAG, entry, LEGEND_ENTRY_TAG);
+ free(entry);
+ }
+ free(legend_v);
+ printf(" </%s>\n", LEGEND_TAG);
+ printf(" </%s>\n", META_TAG);
+ printf(" <%s>\n", DATA_TAG);
+ for (i = start+step; i <= end; i += step) {
+ printf (" <%s>", DATA_ROW_TAG);
+ printf ("<%s>%lu</%s>", COL_TIME_TAG, i, COL_TIME_TAG);
+ for (j = 0; j < col_cnt; j++) {
+ rrd_value_t newval = DNAN;
+ newval = *ptr;
+ if(isnan(newval)){
+ printf("<%s>NaN</%s>", COL_DATA_TAG, COL_DATA_TAG);
+ } else {
+ printf("<%s>%0.10e</%s>", COL_DATA_TAG, newval, COL_DATA_TAG);
+ };
+ ptr++;
+ }
+ printf("</%s>\n", DATA_ROW_TAG);
+ }
+ free(data);
+ printf(" </%s>\n", DATA_TAG);
+ printf("</%s>\n", ROOT_TAG);
+ }
}
else if (strcmp("graph", argv[1]) == 0) {
char **calcpr;
--- /dev/null
+/****************************************************************************
+ * RRDtool 1.0.37 Copyright Tobias Oetiker, 1997 - 2000
+ ****************************************************************************
+ * rrd_xport.c export RRD data
+ ****************************************************************************/
+
+#include <sys/stat.h>
+
+#include "rrd_tool.h"
+#include "rrd_graph.h"
+#include "rrd_xport.h"
+
+#ifdef WIN32
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+
+int rrd_xport(int, char **, int *,
+ time_t *, time_t *,
+ unsigned long *, unsigned long *,
+ char ***, rrd_value_t **);
+
+int rrd_xport_fn(image_desc_t *,
+ time_t *, time_t *,
+ unsigned long *, unsigned long *,
+ char ***, rrd_value_t **);
+
+
+
+
+int
+rrd_xport(int argc, char **argv, int *xsize,
+ time_t *start,
+ time_t *end, /* which time frame do you want ?
+ * will be changed to represent reality */
+ unsigned long *step, /* which stepsize do you want?
+ * will be changed to represent reality */
+ unsigned long *col_cnt, /* number of data columns in the result */
+ char ***legend_v, /* legend entries */
+ rrd_value_t **data) /* two dimensional array containing the data */
+
+{
+
+ image_desc_t im;
+ int i;
+ long long_tmp;
+ time_t start_tmp=0,end_tmp=0;
+ char symname[100];
+ long scancount;
+ struct time_value start_tv, end_tv;
+ char *parsetime_error = NULL;
+
+ rrd_graph_init(&im);
+
+ parsetime("end-24h", &start_tv);
+ parsetime("now", &end_tv);
+
+ while (1){
+ static struct option long_options[] =
+ {
+ {"start", required_argument, 0, 's'},
+ {"end", required_argument, 0, 'e'},
+ {"maxrows", required_argument, 0, 'm'},
+ {"step", required_argument, 0, 261},
+ {0,0,0,0}
+ };
+ int option_index = 0;
+ int opt;
+
+ opt = getopt_long(argc, argv, "s:e:m:",
+ long_options, &option_index);
+
+ if (opt == EOF)
+ break;
+
+ switch(opt) {
+ case 261:
+ im.step = atoi(optarg);
+ break;
+ case 's':
+ if ((parsetime_error = parsetime(optarg, &start_tv))) {
+ rrd_set_error( "start time: %s", parsetime_error );
+ return -1;
+ }
+ break;
+ case 'e':
+ if ((parsetime_error = parsetime(optarg, &end_tv))) {
+ rrd_set_error( "end time: %s", parsetime_error );
+ return -1;
+ }
+ break;
+ case 'm':
+ long_tmp = atol(optarg);
+ if (long_tmp < 10) {
+ rrd_set_error("maxrows below 10 rows");
+ return -1;
+ }
+ im.xsize = long_tmp;
+ break;
+ case '?':
+ rrd_set_error("unknown option '%c'", optopt);
+ return -1;
+ }
+ }
+
+ if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
+ return -1;
+ }
+
+ if (start_tmp < 3600*24*365*10){
+ rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
+ return -1;
+ }
+
+ if (end_tmp < start_tmp) {
+ rrd_set_error("start (%ld) should be less than end (%ld)",
+ start_tmp, end_tmp);
+ return -1;
+ }
+
+ im.start = start_tmp;
+ im.end = end_tmp;
+
+ for(i=optind;i<argc;i++){
+ int argstart=0;
+ int strstart=0;
+ char varname[30],*rpnex;
+ gdes_alloc(&im);
+ if(sscanf(argv[i],"%10[A-Z0-9]:%n",symname,&argstart)==1){
+ if((im.gdes[im.gdes_c-1].gf=gf_conv(symname))==-1){
+ im_free(&im);
+ rrd_set_error("unknown function '%s'",symname);
+ return -1;
+ }
+ } else {
+ rrd_set_error("can't parse '%s'",argv[i]);
+ im_free(&im);
+ return -1;
+ }
+
+ switch(im.gdes[im.gdes_c-1].gf){
+ case GF_CDEF:
+ if((rpnex = malloc(strlen(&argv[i][argstart])*sizeof(char)))==NULL){
+ rrd_set_error("malloc for CDEF");
+ return -1;
+ }
+ if(sscanf(
+ &argv[i][argstart],
+ DEF_NAM_FMT "=%[^: ]",
+ im.gdes[im.gdes_c-1].vname,
+ rpnex) != 2){
+ im_free(&im);
+ free(rpnex);
+ rrd_set_error("can't parse CDEF '%s'",&argv[i][argstart]);
+ return -1;
+ }
+ /* checking for duplicate DEF CDEFS */
+ if(find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
+ im_free(&im);
+ rrd_set_error("duplicate variable '%s'",
+ im.gdes[im.gdes_c-1].vname);
+ return -1;
+ }
+ if((im.gdes[im.gdes_c-1].rpnp = rpn_parse(&im,rpnex,&find_var_wrapper))== NULL){
+ rrd_set_error("invalid rpn expression '%s'", rpnex);
+ im_free(&im);
+ return -1;
+ }
+ free(rpnex);
+ break;
+ case GF_DEF:
+ if (sscanf(
+ &argv[i][argstart],
+ DEF_NAM_FMT "=%n",
+ im.gdes[im.gdes_c-1].vname,
+ &strstart)== 1 && strstart){ /* is the = did not match %n returns 0 */
+ if(sscanf(&argv[i][argstart
+ +strstart
+ +scan_for_col(&argv[i][argstart+strstart],
+ MAXPATH,im.gdes[im.gdes_c-1].rrd)],
+ ":" DS_NAM_FMT ":" CF_NAM_FMT,
+ im.gdes[im.gdes_c-1].ds_nam,
+ symname) != 2){
+ im_free(&im);
+ rrd_set_error("can't parse DEF '%s' -2",&argv[i][argstart]);
+ return -1;
+ }
+ } else {
+ im_free(&im);
+ rrd_set_error("can't parse DEF '%s'",&argv[i][argstart]);
+ return -1;
+ }
+
+ /* checking for duplicate DEF CDEFS */
+ if (find_var(&im,im.gdes[im.gdes_c-1].vname) != -1){
+ im_free(&im);
+ rrd_set_error("duplicate variable '%s'",
+ im.gdes[im.gdes_c-1].vname);
+ return -1;
+ }
+ if((im.gdes[im.gdes_c-1].cf=cf_conv(symname))==-1){
+ im_free(&im);
+ rrd_set_error("unknown cf '%s'",symname);
+ return -1;
+ }
+ break;
+ case GF_XPORT:
+ if((scancount=sscanf(
+ &argv[i][argstart],
+ "%29[^:]:%n",
+ varname,
+ &strstart))>=1){
+ if(strstart <= 0){
+ im.gdes[im.gdes_c-1].legend[0] = '\0';
+ } else {
+ scan_for_col(&argv[i][argstart+strstart],FMT_LEG_LEN,im.gdes[im.gdes_c-1].legend);
+ }
+ if((im.gdes[im.gdes_c-1].vidx=find_var(&im,varname))==-1){
+ im_free(&im);
+ rrd_set_error("unknown variable '%s'",varname);
+ return -1;
+ }
+ } else {
+ im_free(&im);
+ rrd_set_error("can't parse '%s'",&argv[i][argstart]);
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ if (im.gdes_c == 0){
+ rrd_set_error("can't make a graph without contents");
+ im_free(&im);
+ return(-1);
+ }
+
+ if (rrd_xport_fn(&im, start, end, step, col_cnt, legend_v, data) == -1){
+ im_free(&im);
+ return -1;
+ }
+
+ im_free(&im);
+ return 0;
+}
+
+
+
+int
+rrd_xport_fn(image_desc_t *im,
+ time_t *start,
+ time_t *end, /* which time frame do you want ?
+ * will be changed to represent reality */
+ unsigned long *step, /* which stepsize do you want?
+ * will be changed to represent reality */
+ unsigned long *col_cnt, /* number of data columns in the result */
+ char ***legend_v, /* legend entries */
+ rrd_value_t **data) /* two dimensional array containing the data */
+{
+
+ int i = 0, j = 0;
+ unsigned long *ds_cnt; /* number of data sources in file */
+ unsigned long col, dst_row, row_cnt;
+ rrd_value_t *srcptr, *dstptr;
+
+ unsigned long nof_xports = 0;
+ unsigned long xport_counter = 0;
+ unsigned long *ref_list;
+ rrd_value_t **srcptr_list;
+ char **legend_list;
+ int ii = 0;
+
+ time_t start_tmp = 0;
+ time_t end_tmp = 0;
+ unsigned long step_tmp = 1;
+
+ /* pull the data from the rrd files ... */
+ if(data_fetch(im)==-1)
+ return -1;
+
+ /* evaluate CDEF operations ... */
+ if(data_calc(im)==-1)
+ return -1;
+
+ /* how many xports? */
+ for(i = 0; i < im->gdes_c; i++) {
+ switch(im->gdes[i].gf) {
+ case GF_XPORT:
+ nof_xports++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(nof_xports == 0) {
+ rrd_set_error("no XPORT found, nothing to do");
+ return -1;
+ }
+
+ /* a list of referenced gdes */
+ ref_list = malloc(sizeof(int) * nof_xports);
+ if(ref_list == NULL)
+ return -1;
+
+ /* a list to save pointers into each gdes data */
+ srcptr_list = malloc(sizeof(srcptr) * nof_xports);
+ if(srcptr_list == NULL) {
+ free(ref_list);
+ return -1;
+ }
+
+ /* a list to save pointers to the column's legend entry */
+ /* this is a return value! */
+ legend_list = malloc(sizeof(char *) * nof_xports);
+ if(legend_list == NULL) {
+ free(srcptr_list);
+ free(ref_list);
+ return -1;
+ }
+
+ /* find referenced gdes and save their index and */
+ /* a pointer into their data */
+ for(i = 0; i < im->gdes_c; i++) {
+ switch(im->gdes[i].gf) {
+ case GF_XPORT:
+ ii = im->gdes[i].vidx;
+ if(xport_counter > nof_xports) {
+ rrd_set_error( "too many xports: should not happen. Hmmm");
+ free(srcptr_list);
+ free(ref_list);
+ free(legend_list);
+ return -1;
+ }
+ srcptr_list[xport_counter] = im->gdes[ii].data;
+ ref_list[xport_counter++] = i;
+ break;
+ default:
+ break;
+ }
+ }
+
+ start_tmp = im->gdes[0].start;
+ end_tmp = im->gdes[0].end;
+ step_tmp = im->gdes[0].step;
+
+ /* fill some return values */
+ *col_cnt = nof_xports;
+ *start = start_tmp;
+ *end = end_tmp;
+ *step = step_tmp;
+
+ row_cnt = ((*end)-(*start))/(*step);
+
+ /* room for rearranged data */
+ /* this is a return value! */
+ if (((*data) = malloc((*col_cnt) * row_cnt * sizeof(rrd_value_t)))==NULL){
+ free(srcptr_list);
+ free(ref_list);
+ free(legend_list);
+ rrd_set_error("malloc xport data area");
+ return(-1);
+ }
+ dstptr = (*data);
+
+ j = 0;
+ for(i = 0; i < im->gdes_c; i++) {
+ switch(im->gdes[i].gf) {
+ case GF_XPORT:
+ /* reserve room for one legend entry */
+ /* is FMT_LEG_LEN + 5 the correct size? */
+ if ((legend_list[j] = malloc(sizeof(char) * (FMT_LEG_LEN+5)))==NULL) {
+ free(srcptr_list);
+ free(ref_list);
+ free(legend_list);
+ rrd_set_error("malloc xprint legend entry");
+ return(-1);
+ }
+
+ if (im->gdes[i].legend)
+ /* omit bounds check, should have the same size */
+ strcpy (legend_list[j++], im->gdes[i].legend);
+ else
+ legend_list[j++][0] = '\0';
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* fill data structure */
+ for(dst_row = 0; dst_row < row_cnt; dst_row++) {
+ for(i = 0; i < nof_xports; i++) {
+ j = ref_list[i];
+ ii = im->gdes[j].vidx;
+ ds_cnt = &im->gdes[ii].ds_cnt;
+
+ srcptr = srcptr_list[i];
+ for(col = 0; col < (*ds_cnt); col++) {
+ rrd_value_t newval = DNAN;
+ newval = srcptr[col];
+
+ if (im->gdes[ii].ds_namv && im->gdes[ii].ds_nam) {
+ if(strcmp(im->gdes[ii].ds_namv[col],im->gdes[ii].ds_nam) == 0)
+ (*dstptr++) = newval;
+ } else {
+ (*dstptr++) = newval;
+ }
+
+ }
+ srcptr_list[i] += (*ds_cnt);
+ }
+ }
+
+ *legend_v = legend_list;
+ free(srcptr_list);
+ free(ref_list);
+ return 0;
+
+}
--- /dev/null
+/****************************************************************************
+ * RRDtool 1.0.37 Copyright Tobias Oetiker, 1997 - 2000
+ ****************************************************************************
+ * rrd_xport.h contains XML related constants
+ ****************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _RRD_XPORT_H
+#define _RRD_XPORT_H
+
+#define XML_ENCODING "ISO-8859-1"
+#define ROOT_TAG "xport"
+#define META_TAG "meta"
+#define META_START_TAG "start"
+#define META_STEP_TAG "step"
+#define META_END_TAG "end"
+#define META_ROWS_TAG "rows"
+#define META_COLS_TAG "columns"
+#define LEGEND_TAG "legend"
+#define LEGEND_ENTRY_TAG "entry"
+#define DATA_TAG "data"
+#define DATA_ROW_TAG "row"
+#define COL_TIME_TAG "t"
+#define COL_DATA_TAG "v"
+
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif