[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