[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.


- 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:

# 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.
# This script is designed to be called from CRON every INTERVALseconds

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 \

# 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[] = {

    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_create(9, createparams);
	if (rrd_test_error() != 0) {
	    fprintf(stderr, "rrd_create() failed\n");
	printf("created rrd database: %s\n", rrdfile);
    else {
	fprintf(stderr, "rrd file %s already exists\n", rrdfile);



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[] = {

    /* update the rrd database with the new reading */
    optind = opterr = 0;
    rrd_update(3, updateparams);
    if (rrd_test_error() != 0) {
	fprintf(stderr, "rrd_update(%s, %s) failed with %s\n", rrdfile, tbuf, rrd_get_error());

    /* 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[] = {

    optind = opterr = 0;
    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());



#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]);

    char rrdfile[100];
    strncpy(rrdfile, argv[1], sizeof(rrdfile));

    if (initRRD(rrdfile) != 0)

    /* 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");

    /* 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)




rrdutil:	rrdutil.cpp rrdutil.h
	gcc -Wno-write-strings -o rrdutil -DTEST -l :librrd.so.4 rrdutil.cpp

	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