[rrd-users] need help with C bindings - rrd_update() corrupts rrd file
Don Kossman
don.kossman at hear2talk.com
Sat May 31 18:21:23 CEST 2014
I've been struggling with this for a week now and am hoping someone can point out what I am doing wrong.
I'm sure its just a really silly bug in my code, but I can't seem to find it! I would really appreciate any help on this... I'm trying to create a utility library that I can call from other programs to manipulate rrd files.
Context:
- using Raspian on a Raspberry Pi
- Linux raspberryospi 3.12.20+ #686 PREEMPT Tue May 27 15:48:33 BST 2014 armv6l GNU/Linux
- installed rrdtool using apt-get
- RRDtool 1.4.7 Copyright 1997-2012 by Tobias Oetiker <tobi at oetiker.ch>
Compiled Sep 4 2012 23:43:06
- using rrdtool to graph cpu temperatures
- works fine using the command line tools from a shell script
- same calls cause rrd file to be corrupted after rrdupdate() command
Here's the working shell script, called every 5 minutes via cron:
-------------
#!/bin/bash
#
# update .rrd database with CPU temperature
#
# This sets up an RRD called temperature.rrd which accepts one temperature value every 300
# seconds. If no new data is supplied for more than 1200 seconds, the temperature becomes
# *UNKNOWN*. The minimum and maximum acceptable values are Undefined (U)
#
# A few archive areas are also defined. The first stores the temperatures supplied for 100
# hours (1'200 * 300 seconds = 100 hours). The second RRA stores the minimum temperature
# recorded over every hour (12 * 300 seconds = 1 hour), for 100 days (2'400 hours). The third
# and the fourth RRA's do the same for the maximum and average temperature, respectively.
#
INTERVAL=300
# This script is designed to be called from CRON every INTERVALseconds
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH
cd /home/pi/temperature
# create rrd database if not exists
[ -f cputemp.rrd ] || { rrdtool create cputemp.rrd --step $INTERVAL \
DS:cputemp:GAUGE:1200:U:U \
RRA:AVERAGE:0.5:1:1200 \
RRA:MIN:0.5:12:2400 \
RRA:MAX:0.5:12:2400 \
RRA:AVERAGE:0.5:12:2400
}
# read the temperature and convert “59234″ into “59.234″ (degrees celsius)
TEMPERATURE=`cat /sys/class/thermal/thermal_zone0/temp`
TEMPERATURE=`echo -n ${TEMPERATURE:0:2}; echo -n .; echo -n ${TEMPERATURE:2}`
rrdtool update cputemp.rrd `date +%s`:$TEMPERATURE
rrdtool graph cputemp.png DEF:temp=cputemp.rrd:cputemp:AVERAGE LINE2:temp#00FF00 --width 800 --start end-4d --end 00:00 >/dev/null 2>&1
-------------
the above works just fine.
Now, here's my C program, which successfully creates the rrd file. I changed the interval to 20 seconds so its easier to test.
The problem is that after the first rrd_upate(), which returns OK, the rrd file is corrupted, which causes the next rrd_update() to fail.
The rrd_create() is working OK: If i comment out the rrd_update(), and run "rrdtool info" on the rrd file, its fine.
What am i doing wrong??
-------------
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <rrd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "rrdutil.h"
int initRRD(char *rrdfile) {
char *createparams[] = {
"rrdcreate",
rrdfile,
"--step",
"20",
"DS:cputemp:GAUGE:1200:U:U",
"RRA:AVERAGE:0.5:1:1200",
"RRA:MIN:0.5:12:2400",
"RRA:MAX:0.5:12:2400",
"RRA:AVERAGE:0.5:12:2400",
NULL
};
struct stat sb;
/* if rrd database file does not exist, then create it */
if (stat(rrdfile, &sb) == -1) {
optind = opterr = 0; /* Because rrdtool uses getopt() */
rrd_clear_error();
rrd_create(9, createparams);
if (rrd_test_error() != 0) {
fprintf(stderr, "rrd_create() failed\n");
return(1);
}
printf("created rrd database: %s\n", rrdfile);
}
else {
fprintf(stderr, "rrd file %s already exists\n", rrdfile);
}
return(0);
};
int updateRRD(char *rrdfile, char *value) {
/* construct time string as secs since 1/1/1970 */
char timestr[30];
time_t t;
struct tm *ttm;
t = time(NULL);
ttm = localtime(&t);
strftime(timestr, sizeof(timestr), "%s", ttm);
char tbuf[30];
/* rrd needs "time-in-secs:value */
sprintf(tbuf, "%s:%s", timestr, value);
printf("updating %s with %s\n", rrdfile, tbuf);
char *updateparams[] = {
"rrdupdate",
rrdfile,
tbuf,
NULL
};
/* update the rrd database with the new reading */
optind = opterr = 0;
rrd_clear_error();
rrd_update(3, updateparams);
if (rrd_test_error() != 0) {
fprintf(stderr, "rrd_update(%s, %s) failed with %s\n", rrdfile, tbuf, rrd_get_error());
return(1);
}
/* graph the results */
char **calcpr = NULL;
int rrdargcount, xsize, ysize, result;
double ymin, ymax;
char def[100];
sprintf(def, "DEF:temp=%s:cputemp:AVERAGE", rrdfile);
char *rrdgraphargs[] = {
"rrdgraph",
rrdfile,
def,
"LINE2:temp#00FF00",
"--width",
"800",
"--start",
"end-4d",
"--end",
"00:00",
NULL
};
optind = opterr = 0;
rrd_clear_error();
result = rrd_graph(10, rrdgraphargs, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
/* Was it OK ? */
if (rrd_test_error() || (result != 0)) {
if (calcpr) {
int i;
for (i=0; (calcpr[i]); i++) free(calcpr[i]);
calcpr = NULL;
}
fprintf(stderr,"Graph error: %s\n", rrd_get_error());
return(2);
}
return(0);
};
#ifdef TEST
extern char *optarg;
extern int optind, opterr, optopt;
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s <rrd file path>\n", argv[0]);
return(1);
}
char rrdfile[100];
strncpy(rrdfile, argv[1], sizeof(rrdfile));
if (initRRD(rrdfile) != 0)
return(2);
/* temperature returned as 5 digits, centigrade, no decimal */
int fd = open("/sys/class/thermal/thermal_zone0/temp", O_RDONLY);
if (fd < 0) {
fprintf(stderr, "open() of cpu temperature file failed\n");
return(3);
}
/* optimistic */
char temp[10];
(void) read(fd, temp, sizeof(temp));
(void) close(fd);
char tempstr[10];
strncpy(tempstr, temp, 2);
tempstr[2]= '.';
strncpy(&tempstr[3], &temp[2], 3);
tempstr[6] = 0;
if (updateRRD(rrdfile, tempstr) != 0)
return(4);
return(0);
}
#endif
----------
Makefile:
rrdutil: rrdutil.cpp rrdutil.h
gcc -Wno-write-strings -o rrdutil -DTEST -l :librrd.so.4 rrdutil.cpp
clean:
rm -f rrdutil
----------------
here's the output:
pi at raspberryospi ~/temperature $ ./rrdutil fee.rrd
created rrd database: fee.rrd
updating fee.rrd with 1401552682:55.148
pi at raspberryospi ~/temperature $ ./rrdutil fee.rrd
rrd file fee.rrd already exists
updating fee.rrd with 1401552704:55.686
rrd_update(fee.rrd, 1401552704:55.686) failed with 'fee.rrd' is not an RRD file
---------
More information about the rrd-users
mailing list