]> git.itanic.dy.fi Git - rrdd/commitdiff
Initial comit
authorTimo Kokkonen <kaapeli@ee.oulu.fi>
Sun, 30 Mar 2008 10:16:43 +0000 (13:16 +0300)
committerTimo Kokkonen <kaapeli@ee.oulu.fi>
Sun, 30 Mar 2008 10:16:43 +0000 (13:16 +0300)
Makefile [new file with mode: 0644]
draw_graphs.c [new file with mode: 0644]
draw_graphs.h [new file with mode: 0644]
main.c [new file with mode: 0644]
parser.c [new file with mode: 0644]
parser.h [new file with mode: 0644]
process.c [new file with mode: 0644]
process.h [new file with mode: 0644]
scheduler.c [new file with mode: 0644]
scheduler.h [new file with mode: 0644]
testdata.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..a683889
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+CC=gcc
+LD=ld
+CFLAGS=-Wall -O2 -g
+
+RRDD_OBJS= main.o process.o draw_graphs.o parser.o scheduler.o
+
+rrdd: $(RRDD_OBJS)
+       $(CC) -o rrdd $(RRDD_OBJS)
+
+all: rrdd
+
+clean:
+       rm -vf rrdd *~ *.o
+
+.cpp.o:
+       $(CC) $(CFLAGS) -c $< -o $@
+
+TAGS:
+       etags *.[ch]
+
+.PHONY: all clean TAGS
+
+main.o: *.h
+process.o: *.h
+draw_garphs.o: *.h
+parser.o: *.h
+scheduler.o: *.h
diff --git a/draw_graphs.c b/draw_graphs.c
new file mode 100644 (file)
index 0000000..39549a2
--- /dev/null
@@ -0,0 +1,76 @@
+#include <time.h>
+
+#include "draw_graphs.h"
+#include "process.h"
+
+#define MAX_ARGS       512
+#define ARGSTR_LEN     32768
+
+#define print(fmt, arg...) \
+       args[argcnt] = argstr + idx;                    \
+       idx += sprintf(argstr + idx, fmt, ##arg);       \
+       argcnt++;                                       \
+       argstr[++idx] = 0
+
+int draw_image(struct rrd_image *image)
+{
+       char cmd[] = "/usr/bin/rrdtool";
+//     char cmd[] = "echo";
+       char *args[512], argstr[ARGSTR_LEN];
+       int idx = 0, argcnt = 0, i,j;
+       char timestamp[256];
+       char tmp[256];
+       time_t t = time(0);
+
+       strftime(tmp, 256, "%d.%m.%Y %T (%Z) ", localtime(&t));
+       for (i = 0, j = 0; j < 256;) {
+               if (tmp[i] == ':') {
+                       timestamp[j++] = '\\';
+               }
+               timestamp[j++] = tmp[i++];
+               if (!tmp[i])
+                       break;
+       }
+       timestamp[j] = 0;
+
+
+       print("");
+       print("graph");
+       print(image->image_filename);
+
+       print("--start");
+       print("%s", image->timestart);
+       print("--end");
+       print("%s", image->timeend);
+       print("--width");
+       print("%d", image->width);
+       print("--height");
+       print("%d", image->height);
+       print("--imgformat");
+       print("%s", image->imageformat);
+
+       for (i = 0; image->options[i]; i++) {
+               print("%s", image->options[i]);
+       }
+
+       for (i = 0; image->text[i]; i++) {
+               args[argcnt++] = image->text[i];
+       }
+
+       print("COMMENT:Last update %s\\c", timestamp);
+
+       args[argcnt] = 0;
+
+       run(cmd, args);
+
+       return 0;
+}
+
+int draw_images(struct rrd_image **image)
+{
+       int i;
+       for (i = 0; image[i]; i++)
+               draw_image(image[i]);
+
+       return 0;
+}
diff --git a/draw_graphs.h b/draw_graphs.h
new file mode 100644 (file)
index 0000000..783305a
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _DRAW_GRAPHS
+#define _DRAW_GRAPHS
+
+#define MAX_STRLEN 16
+
+struct rrd_image {
+       char    *rrd_database;  /* Database file path */
+       char    *image_filename; /* Output image */
+       int     width;          /* Image dimensions */
+       int     height;
+       char    timestart[MAX_STRLEN];
+       char    timeend[MAX_STRLEN];
+       char    imageformat[MAX_STRLEN];
+       char    **options;      /* Null terminated list of rrdgraph options */
+       int     text_lead;      /* Number of spaces at the beginning of line */
+       char    **text;         /* Null terminated list of text lines */
+};
+
+int draw_image(struct rrd_image *image);
+int draw_images(struct rrd_image **image);
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..373d113
--- /dev/null
+++ b/main.c
@@ -0,0 +1,43 @@
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "process.h"
+#include "draw_graphs.h"
+#include "parser.h"
+#include "scheduler.h"
+
+#include "testdata.h"
+
+
+int main(int argc, char *argv[])
+{
+//     draw_image(&cpudaily);
+
+//     draw_images(&all_images);
+
+//     update_data(&cpumem_rrd);
+
+       struct rrd_database *db;
+       int sleeptime;
+
+       while (1) {
+               printf("%d: loop start\n", getpid());
+               /*
+                * Update all databases parallel in one shot
+                */
+               while ((db = check_update_need(&all_rrds)))
+                       update_data(db);
+
+               /*
+                * Let the updates finish
+                */
+               if (harvest_zombies())
+                       continue;
+
+               sleeptime = get_next_update(&all_rrds);
+               printf("Time to sleep %d seconds\n", sleeptime);
+               sleep(sleeptime);
+
+       }
+       return 0;
+}
diff --git a/parser.c b/parser.c
new file mode 100644 (file)
index 0000000..1a635ff
--- /dev/null
+++ b/parser.c
@@ -0,0 +1,143 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <error.h>
+
+#include "parser.h"
+
+static int str_to_dec(char *src, char **dst)
+{
+       int ret;
+
+       while(((*src < '0') || (*src > '9')) && *src)
+               src++;
+
+       ret = atoi(src);
+
+       if (dst) {
+               while(((*src >= '0') && (*src <= '9')) && *src)
+                       src++;
+               *dst = src;
+       }
+
+       return ret;
+}
+
+static int get_word(char *src, char **dst, char *word)
+{
+       int ret = 0;
+       while(((*src == ' ') || (*src == '\t')) && *src)
+               src++;
+
+       while(((*src != ' ') && (*src != '\t')) && *src) {
+               *word = *src;
+               word++;
+               src++;
+               ret++;
+       }
+       *word = 0;
+
+       if (dst)
+               *dst = src;
+
+       return ret;
+}
+
+#define STATFILE "/proc/stat"
+
+int cpu_parser(char *data)
+{
+       char buf[1024];
+       char *str = buf;
+       FILE *file = fopen(STATFILE, "r");
+       int user, nice, sys, idle, wait, irq, softirq;
+
+       if (file == NULL) {
+               fprintf(stderr, "Failed to open file %s\n", STATFILE);
+               return -1;
+       }
+
+       if (!fgets(buf, 1024, file)) {
+               fprintf(stderr, "Failed to read file %s\n", STATFILE);
+               fclose(file);
+               return -1;
+       }
+
+       user    = str_to_dec(str, &str);
+       nice    = str_to_dec(str, &str);
+       sys     = str_to_dec(str, &str);
+       idle    = str_to_dec(str, &str);
+       wait    = str_to_dec(str, &str);
+       irq     = str_to_dec(str, &str);
+       softirq = str_to_dec(str, &str);
+
+       sprintf(data, "%d:%d:%d:%d:%d:%d:%d",
+               user, nice, sys, idle, wait, irq, softirq);
+
+       fclose(file);
+       return 0;
+}
+
+#define MEMFILE "/proc/meminfo"
+
+int mem_parser(char *data)
+{
+       char buf[1024], word[1024];
+       int free = 0, buffered = 0, cache = 0, active = 0, inactive = 0,
+               swapfree = 0, anon = 0, slab = 0, tables = 0;
+       FILE *file = fopen(MEMFILE, "r");
+
+       if (file == NULL) {
+               fprintf(stderr, "Failed to open file %s\n", MEMFILE);
+               return -1;
+       }
+
+       while (fgets(buf, 1024, file)) {
+               get_word(buf, 0, word);
+               
+               if (!strcmp(word, "MemFree:")) {
+                       free = str_to_dec(buf, 0);
+               } else if (!strcmp(word, "Buffers:")) {
+                       buffered = str_to_dec(buf, 0);
+               } else if (!strcmp(word, "Cached:")) {
+                       cache = str_to_dec(buf, 0);
+               } else if (!strcmp(word, "Active:")) {
+                       active = str_to_dec(buf, 0);
+               } else if (!strcmp(word, "Inactive:")) {
+                       inactive = str_to_dec(buf, 0);
+               } else if (!strcmp(word, "SwapFree:")) {
+                       swapfree = str_to_dec(buf, 0);
+               } else if (!strcmp(word, "AnonPages:")) {
+                               anon = str_to_dec(buf, 0);
+               } else if (!strcmp(word, "Slab:")) {
+                       slab = str_to_dec(buf, 0);
+               } else if (!strcmp(word, "PageTables:")) {
+                       tables = str_to_dec(buf, 0);
+               }
+       }
+       fclose(file);
+
+       sprintf(data, "%f:%f:%f:%f:%f:%f:%f:%f:%f",
+               free / 1024.0,
+               buffered / 1024.0,
+               cache / 1024.0,
+               active / 1024.0,
+               inactive / 1024.0,
+               swapfree / 1024.0,
+               anon / 1024.0,
+               slab / 1024.0,
+               tables / 1024.0);
+
+       return 0;
+}
+
+int cpu_mem_parser(char *data)
+{
+       char cpu[1024], mem[1024];
+
+       cpu_parser(cpu);
+       mem_parser(mem);
+       sprintf(data, "%s:%s", cpu, mem);
+
+       return 0;
+}
diff --git a/parser.h b/parser.h
new file mode 100644 (file)
index 0000000..9702a99
--- /dev/null
+++ b/parser.h
@@ -0,0 +1,8 @@
+#ifndef _PARSER_H
+#define _PARSER_H
+
+int cpu_parser(char *data);
+int mem_parser(char *data);
+int cpu_mem_parser(char *data);
+
+#endif
diff --git a/process.c b/process.c
new file mode 100644 (file)
index 0000000..a04e495
--- /dev/null
+++ b/process.c
@@ -0,0 +1,43 @@
+#include "process.h"
+
+int child_count = 0;
+
+int run(const char *p, char *const argv[])
+{
+       int child, error;
+       child = fork();
+       if (child < 0) {
+               error = errno;
+               fprintf(stderr, "fork() failed: %s\n", strerror(error));
+               return -1;
+       }
+
+       if (child) {
+               printf("Forked child %d\n", child);
+               child_count++;
+               return child;
+       }
+
+       execvp(p, argv);
+       error = errno;
+       printf("Failed to execv: %s\n", strerror(error));
+       exit(1);
+       return 0;
+}
+
+int harvest_zombies()
+{
+       int status;
+       pid_t pid;
+
+       if (child_count == 0)
+               return 0;
+
+       pid = wait(&status);
+       if (status == 0)
+               printf("pid %d: terminated with exit code %d\n", pid, status);
+
+       child_count--;
+       return 1;
+}
+
diff --git a/process.h b/process.h
new file mode 100644 (file)
index 0000000..0497d74
--- /dev/null
+++ b/process.h
@@ -0,0 +1,17 @@
+#ifndef _PROCESS_H
+#define _PROCESS_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <error.h>
+#include <errno.h>
+
+int run(const char *p, char *const argv[]);
+int harvest_zombies();
+
+extern int child_count;
+
+#endif
diff --git a/scheduler.c b/scheduler.c
new file mode 100644 (file)
index 0000000..cd16762
--- /dev/null
@@ -0,0 +1,78 @@
+#include "scheduler.h"
+#include "process.h"
+
+int update_data(struct rrd_database *rrd)
+{
+       char data[1024];
+       char cmd[] = "/usr/bin/rrdtool";
+//     char cmd[] = "echo";
+       char *cmdline[] = {
+               "",
+               "update",
+               rrd->filename,
+               data,
+               0
+       };
+       int l;
+
+       rrd->last_update = time(0);
+       if (fork()) {
+               child_count++;
+               return 0;
+       }
+
+       l = sprintf(data, "N:");
+
+       if (rrd->parse) {
+               rrd->parse(data + l);
+               run(cmd, cmdline);
+       }
+
+       if (rrd->images)
+               draw_images(rrd->images);
+
+       while(harvest_zombies());
+       exit(0);
+}
+
+/*
+ * Walk through the database list and return the first database which
+ * last update is too far in past
+ */
+
+struct rrd_database *check_update_need(struct rrd_database **dblist)
+{
+       int i;
+       time_t now = time(0);
+
+       for (i = 0; dblist[i]; i++) {
+               if ((dblist[i]->last_update + dblist[i]->interval) - now <= 0)
+                       return dblist[i];
+       }
+
+       /* Nothing to update this time, return null */
+       return NULL;
+}
+
+/*
+ * See how long we may sleep until it is required to run an update
+ * again
+ */
+
+int get_next_update(struct rrd_database **dblist)
+{
+       int i, sleeptime = 0, diff;
+       time_t now = time(0);
+
+       for (i = 0; dblist[i]; i++) {
+               diff = dblist[i]->last_update + dblist[i]->interval - now;
+               if (!sleeptime)
+                       sleeptime = diff;
+               if (sleeptime > diff)
+                       sleeptime = diff;
+               if (sleeptime <= 0)
+                       return 0;
+       }
+
+       return sleeptime;
+}
diff --git a/scheduler.h b/scheduler.h
new file mode 100644 (file)
index 0000000..a23f848
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef _SCHEDULER_H
+#define _SCHEDULER_H
+
+#include <time.h>
+
+#include "draw_graphs.h"
+
+struct rrd_database {
+       char    *filename;      /* rrd database location */
+       int     interval;       /* Update interval */
+       int (*parse)(char *data); /* Parser to aquire data */
+       struct  rrd_image **images; /* Images to draw */
+       int     last_update;    /* When was the data last updated */
+       char    *name;          /* Name of the database */
+};
+
+int update_data(struct rrd_database *rrd);
+struct rrd_database *check_update_need(struct rrd_database **dblist);
+int get_next_update(struct rrd_database **dblist);
+
+#endif
diff --git a/testdata.h b/testdata.h
new file mode 100644 (file)
index 0000000..929a611
--- /dev/null
@@ -0,0 +1,342 @@
+#ifndef _TESTDATA_H
+#define _TESTDATA_H
+
+#include "draw_graphs.h"
+#include "scheduler.h"
+#include "parser.h"
+
+const char blank[] = "COMMENT:               ";
+#define numfmt " % 8.2lf "
+
+const char *cputext[] = {
+       "DEF:us=/home/kaapeli/sysinfo/sysinfo.rrd:User:AVERAGE",
+       "DEF:ni=/home/kaapeli/sysinfo/sysinfo.rrd:Nice:AVERAGE",
+       "DEF:sy=/home/kaapeli/sysinfo/sysinfo.rrd:Sys:AVERAGE",
+       "DEF:id=/home/kaapeli/sysinfo/sysinfo.rrd:Idle:AVERAGE",
+       "DEF:wa=/home/kaapeli/sysinfo/sysinfo.rrd:Wait:AVERAGE",
+       "DEF:in=/home/kaapeli/sysinfo/sysinfo.rrd:IRQ:AVERAGE",
+       "DEF:so=/home/kaapeli/sysinfo/sysinfo.rrd:SoftIRQ:AVERAGE",
+       "COMMENT:\\n",
+       blank,
+       "COMMENT:                   "
+       "MIN          MAX         AVG        Last\\n",
+       blank,
+       "AREA:sy#ff0000:System   ",
+       "GPRINT:sy:MIN:" numfmt,
+       "GPRINT:sy:MAX:" numfmt,
+       "GPRINT:sy:AVERAGE:" numfmt,
+       "GPRINT:sy:LAST:" numfmt "\\n",
+       blank,
+       "STACK:us#0000ff:User     ",
+       "GPRINT:us:MIN:" numfmt,
+       "GPRINT:us:MAX:" numfmt,
+       "GPRINT:us:AVERAGE:" numfmt,
+       "GPRINT:us:LAST:" numfmt "\\n",
+       blank,
+       "STACK:ni#00ff00:Nice     ",
+       "GPRINT:ni:MIN:" numfmt,
+       "GPRINT:ni:MAX:" numfmt,
+       "GPRINT:ni:AVERAGE:" numfmt,
+       "GPRINT:ni:LAST:" numfmt "\\n",
+       blank,
+       "STACK:id#ffffff:Idle     ",
+       "GPRINT:id:MIN:" numfmt,
+       "GPRINT:id:MAX:" numfmt,
+       "GPRINT:id:AVERAGE:" numfmt,
+       "GPRINT:id:LAST:" numfmt "\\n",
+       blank,
+       "STACK:wa#0000ff:IO wait  ",
+       "GPRINT:wa:MIN:" numfmt,
+       "GPRINT:wa:MAX:" numfmt,
+       "GPRINT:wa:AVERAGE:" numfmt,
+       "GPRINT:wa:LAST:" numfmt "\\n",
+       blank,
+       "STACK:in#ffff00:IRQ      ",
+       "GPRINT:in:MIN:" numfmt,
+       "GPRINT:in:MAX:" numfmt,
+       "GPRINT:in:AVERAGE:" numfmt,
+       "GPRINT:in:LAST:" numfmt "\\n",
+       blank,
+       "STACK:so#00ffff:Soft IRQ ",
+       "GPRINT:so:MIN:" numfmt,
+       "GPRINT:so:MAX:" numfmt,
+       "GPRINT:so:AVERAGE:" numfmt,
+       "GPRINT:so:LAST:" numfmt "\\n",
+       "COMMENT: \\n",
+       0
+};
+
+const char *memtext[] = {
+       "DEF:fr=/home/kaapeli/sysinfo/sysinfo.rrd:Free:AVERAGE",
+       "DEF:bu=/home/kaapeli/sysinfo/sysinfo.rrd:Buffers:AVERAGE",
+       "DEF:ca=/home/kaapeli/sysinfo/sysinfo.rrd:Cached:AVERAGE",
+       "DEF:an=/home/kaapeli/sysinfo/sysinfo.rrd:Anon:AVERAGE",
+       "DEF:sl=/home/kaapeli/sysinfo/sysinfo.rrd:Slab:AVERAGE",
+       "DEF:ta=/home/kaapeli/sysinfo/sysinfo.rrd:Tables:AVERAGE",
+       "COMMENT:\\n",
+       blank,
+       "COMMENT:                     "
+       "MIN          MAX         AVG        Last\\n",
+       blank,
+       "AREA:bu#ff0000:Buffers    ",
+       "GPRINT:bu:MIN:" numfmt,
+       "GPRINT:bu:MAX:" numfmt,
+       "GPRINT:bu:AVERAGE:" numfmt,
+       "GPRINT:bu:LAST:" numfmt "\\n",
+       blank,
+       "STACK:an#000000:AnonPages  ",
+       "GPRINT:an:MIN:" numfmt,
+       "GPRINT:an:MAX:" numfmt,
+       "GPRINT:an:AVERAGE:" numfmt,
+       "GPRINT:an:LAST:" numfmt "\\n",
+       blank,
+       "STACK:ca#00ff00:Cached     ",
+       "GPRINT:ca:MIN:" numfmt,
+       "GPRINT:ca:MAX:" numfmt,
+       "GPRINT:ca:AVERAGE:" numfmt,
+       "GPRINT:ca:LAST:" numfmt "\\n",
+       blank,
+       "STACK:fr#ffffff:Free       ",
+       "GPRINT:fr:MIN:" numfmt,
+       "GPRINT:fr:MAX:" numfmt,
+       "GPRINT:fr:AVERAGE:" numfmt,
+       "GPRINT:fr:LAST:" numfmt "\\n",
+       blank,
+       "STACK:sl#00ffff:Slab       ",
+       "GPRINT:sl:MIN:" numfmt,
+       "GPRINT:sl:MAX:" numfmt,
+       "GPRINT:sl:AVERAGE:" numfmt,
+       "GPRINT:sl:LAST:" numfmt "\\n",
+       blank,
+       "STACK:ta#0000ff:PageTables ",
+       "GPRINT:ta:MIN:" numfmt,
+       "GPRINT:ta:MAX:" numfmt,
+       "GPRINT:ta:AVERAGE:" numfmt,
+       "GPRINT:ta:LAST:" numfmt "\\n",
+       "COMMENT: \\n",
+       0
+};
+
+const char *systemptext[] = {
+       "DEF:sda_c=/root/systemp/systemp.rrd:sda:AVERAGE",
+       "DEF:SYS_c=/root/systemp/systemp.rrd:SYS:AVERAGE",
+       "DEF:CPU_c=/root/systemp/systemp.rrd:CPU:AVERAGE",
+       "COMMENT:\\n",
+       blank,
+       "COMMENT:                          "
+       "MIN            MAX             AVG             Last\\n",
+       blank,
+       "LINE1:sda_c#0000FF:SAMSUNG HD501LJ      ",
+       "GPRINT:sda_c:MIN:  %5.2lf °C",
+       "GPRINT:sda_c:MAX:     %5.2lf °C",
+       "GPRINT:sda_c:AVERAGE:      %5.2lf °C",
+       "GPRINT:sda_c:LAST:      %5.2lf °C\\n",
+       blank,
+       "LINE1:SYS_c#00FF00:Sys Temp              ",
+       "GPRINT:SYS_c:MIN: %5.2lf °C",
+       "GPRINT:SYS_c:MAX:     %5.2lf °C",
+       "GPRINT:SYS_c:AVERAGE:      %5.2lf °C",
+       "GPRINT:SYS_c:LAST:      %5.2lf °C\\n",
+       blank,
+       "LINE1:CPU_c#FF0000:Intel Core2 Quad     ",
+       "GPRINT:CPU_c:MIN:  %5.2lf °C",    
+       "GPRINT:CPU_c:MAX:     %5.2lf °C",    
+       "GPRINT:CPU_c:AVERAGE:      %5.2lf °C",    
+       "GPRINT:CPU_c:LAST:      %5.2lf °C\\n", 
+       "COMMENT: \\n",
+       0
+};
+
+const char *cpuoptions[] = {
+       "--alt-autoscale-max",
+       "--vertical-label",
+       "CPU Tics",
+       0
+};
+
+const char *memoptions[] = {
+       "--alt-autoscale-max",
+       "--vertical-label",
+       "Mem usage (MB)",
+       "--units-exponent", "0",
+       0
+};
+
+const char *systempoptions[] = {
+       "--alt-autoscale",
+       "--vertical-label", "Temp °C",
+       0
+};
+
+static struct rrd_image cpudaily = {
+       .image_filename = "/home/kaapeli/sysinfo/images/cpu_daily.png",
+       .width = 720,
+       .height = 480,
+       .timestart = "end-1d",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&cpuoptions,
+       .text = (char **)&cputext
+};
+
+static struct rrd_image cpuweekly = {
+       .image_filename = "/home/kaapeli/sysinfo/images/cpu_weekly.png",
+       .width = 720,
+       .height = 480,
+       .timestart = "end-1w",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&cpuoptions,
+       .text = (char **)&cputext
+};
+
+static struct rrd_image cpumonthly = {
+       .image_filename = "/home/kaapeli/sysinfo/images/cpu_monthly.png",
+       .width = 720,
+       .height = 480,
+       .timestart = "end-1m",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&cpuoptions,
+       .text = (char **)&cputext
+};
+
+static struct rrd_image cpuyearly = {
+       .image_filename = "/home/kaapeli/sysinfo/images/cpu_yearly.png",
+       .width = 720,
+       .height = 480,
+       .timestart = "end-1y",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&cpuoptions,
+       .text = (char **)&cputext
+};
+
+static struct rrd_image memdaily = {
+       .image_filename = "/home/kaapeli/sysinfo/images/mem_daily.png",
+       .width = 720,
+       .height = 480,
+       .timestart = "end-1d",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&memoptions,
+       .text = (char **)&memtext
+};
+
+static struct rrd_image memweekly = {
+       .image_filename = "/home/kaapeli/sysinfo/images/mem_weekly.png",
+       .width = 720,
+       .height = 480,
+       .timestart = "end-1w",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&memoptions,
+       .text = (char **)&memtext
+};
+static struct rrd_image memmonthly = {
+       .image_filename = "/home/kaapeli/sysinfo/images/mem_monthly.png",
+       .width = 720,
+       .height = 480,
+       .timestart = "end-1m",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&memoptions,
+       .text = (char **)&memtext
+};
+static struct rrd_image memyearly = {
+       .image_filename = "/home/kaapeli/sysinfo/images/mem_yearly.png",
+       .width = 720,
+       .height = 480,
+       .timestart = "end-1y",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&memoptions,
+       .text = (char **)&memtext
+};
+
+static struct rrd_image systempdaily = {
+       .image_filename = "/home/kaapeli/sysinfo/images/systemp_daily.png",
+       .width = 600,
+       .height = 200,
+       .timestart = "end-1d",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&systempoptions,
+       .text = (char **)&systemptext
+};
+
+static struct rrd_image systempweekly = {
+       .image_filename = "/home/kaapeli/sysinfo/images/systemp_weekly.png",
+       .width = 600,
+       .height = 200,
+       .timestart = "end-1w",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&systempoptions,
+       .text = (char **)&systemptext
+};
+
+static struct rrd_image systempmonthly = {
+       .image_filename = "/home/kaapeli/sysinfo/images/systemp_monthly.png",
+       .width = 600,
+       .height = 200,
+       .timestart = "end-1m",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&systempoptions,
+       .text = (char **)&systemptext
+};
+
+static struct rrd_image systempyearly = {
+       .image_filename = "/home/kaapeli/sysinfo/images/systemp_yearly.png",
+       .width = 600,
+       .height = 200,
+       .timestart = "end-1y",
+       .timeend = "now",
+       .imageformat = "PNG",
+       .options = (char **)&systempoptions,
+       .text = (char **)&systemptext
+};
+
+static struct rrd_image *cpu_mem_images[] = {
+       &cpudaily,
+       &cpuweekly,
+       &cpumonthly,
+       &cpuyearly,
+       &memdaily,
+       &memweekly,
+       &memmonthly,
+       &memyearly,
+       0
+};
+
+static struct rrd_image *systemp_images[] = {
+       &systempdaily,
+       &systempweekly,
+       &systempmonthly,
+       &systempyearly,
+       0
+};
+
+static struct rrd_database cpumem_rrd = {
+       .filename       = "/home/kaapeli/sysinfo/sysinfo.rrd",
+       .interval       = 60,
+       .parse          = cpu_mem_parser,
+       .images         = (struct rrd_image **)&cpu_mem_images,
+       .name           = "cpumem"
+};
+
+static struct rrd_database systemp_rrd = {
+       .interval       = 300,
+       .parse          = NULL,
+       .images         = (struct rrd_image **)&systemp_images,
+       .name           = "systemp"
+};
+
+static struct rrd_database *all_rrds[] = {
+       &cpumem_rrd,
+       &systemp_rrd,
+       0
+};
+
+#endif