15 #define ARGSTR_LEN 32768
17 #define RRDTOOL_CMD "/usr/bin/rrdtool"
20 * Add new argument to a argument list
22 * args pointer list to arguments
23 * argcnt argument counter
24 * argstr array where the actual arguments are stored
25 * idx index in the argstr where the new argument will be appended
27 #define add_arg(args, argcnt, argstr, idx, fmt, arg...) \
28 args[argcnt] = argstr + idx; \
29 idx += sprintf(argstr + idx, fmt, ##arg); \
33 int rrdtool_draw_image(struct rrd_image *image)
36 char cmd[] = RRDTOOL_CMD;
37 // char cmd[] = "echo";
38 char *args[512], argstr[ARGSTR_LEN];
39 int idx = 0, argcnt = 0, i,j;
41 char tmp[sizeof(timestamp)];
44 const char *updatestr = "Last update %d.%m.%Y %T (%Z)";
46 pid = do_fork_limited();
50 pr_info("Drawing image %s\n", image->image_filename);
53 strncat(tmpfile, image->image_filename, sizeof(tmp) - strlen(tmp) - 1);
54 strncat(tmpfile, ".tmp", sizeof(tmp) - strlen(tmp) - 1);
57 updatestr = image->updatestr;
59 strftime(tmp, sizeof(tmp), updatestr, localtime(&t));
60 for (i = 0, j = 0; j < sizeof(tmp);) {
62 timestamp[j++] = '\\';
64 timestamp[j++] = tmp[i++];
71 add_arg(args, argcnt, argstr, idx, RRDTOOL_CMD);
72 add_arg(args, argcnt, argstr, idx, "graph");
73 add_arg(args, argcnt, argstr, idx, "%s", tmpfile);
75 add_arg(args, argcnt, argstr, idx, "--start");
76 add_arg(args, argcnt, argstr, idx, "%s", image->timestart);
77 add_arg(args, argcnt, argstr, idx, "--end");
78 add_arg(args, argcnt, argstr, idx, "%s", image->timeend);
79 add_arg(args, argcnt, argstr, idx, "--width");
80 add_arg(args, argcnt, argstr, idx, "%d", image->width);
81 add_arg(args, argcnt, argstr, idx, "--height");
82 add_arg(args, argcnt, argstr, idx, "%d", image->height);
83 add_arg(args, argcnt, argstr, idx, "--imgformat");
84 add_arg(args, argcnt, argstr, idx, "%s", image->imageformat);
86 for (i = 0; image->options[i]; i++) {
87 add_arg(args, argcnt, argstr, idx, "%s", image->options[i]);
90 for (i = 0; image->text[i]; i++) {
91 args[argcnt++] = (char *)image->text[i];
94 add_arg(args, argcnt, argstr, idx, "COMMENT: %s\\c", timestamp);
101 rename(tmpfile, image->image_filename);
106 int rrdtool_draw_images(struct rrd_image **image)
109 for (i = 0; image[i]; i++)
110 rrdtool_draw_image(image[i]);
115 static int sanitize_rrd_update_data(char *data)
117 char clean_data[RRD_DATA_MAX_LEN];
120 char *src, *end, *cln;
122 data[RRD_DATA_MAX_LEN - 1] = 0;
127 * Copy a legit floating point number to clean_data buffer
128 * starting from *src and ending to next ':'. If no legit
129 * number could be found, put a 'U' there instead to make
130 * rrdtool to understand this datapoint is undefined.
133 while (src < data + RRD_DATA_MAX_LEN && *src) {
136 /* skip any non_numbers but not ':' */
137 while (*src && !isdigit(*src) && *src != '-' && *src != ':')
145 /* Now find the end of the number */
146 end = skip_numbers(src);
148 /* Floating point numberrs may have a dot with more numbers */
151 end = skip_numbers(end);
155 * Now we have gone past the number, there should be a
156 * colon or zero byte. If src == end, there was no
157 * number and the entry is undefined instead.
159 if ((*end == ':' || !*end) && src != end) {
166 * Copy the legit number and start copying the
169 for (; src <= end; src++, cln++)
175 /* Skip over whatever junk there might be */
176 while (*end != ':' && *end)
179 /* Mark the entry as undefined */
191 * If last entry was undefined, we need to remove the extra
194 if (*(cln - 1) == ':')
198 strncpy(data, clean_data, RRD_DATA_MAX_LEN);
202 int rrdtool_update_data(struct rrd_database *rrd)
205 char data[RRD_DATA_MAX_LEN + 3]; /* 3 == "N:" + NULL termination */
206 char cmd[] = RRDTOOL_CMD;
207 // char cmd[] = "echo";
208 char *const cmdline[] = {
211 (char *const)rrd->filename,
217 rrd->last_update = time(0);
221 l = sprintf(data, "N:");
224 rrd->parse(data + l, rrd->parser_data);
225 data[RRD_DATA_MAX_LEN + 2] = '\0';
227 pr_info("Data: %s\n", data);
229 sanitize_rrd_update_data(data + l);
230 pid = run(cmd, cmdline);
231 harvest_zombies(pid);
234 if (rrd->pre_draw_cmd && !strcmp(rrd->pre_draw_cmd[0], "shell")) {
235 pid = run(rrd->pre_draw_cmd[1], &rrd->pre_draw_cmd[1]);
236 harvest_zombies(pid);
240 rrdtool_draw_images(rrd->images);
242 while (harvest_zombies(0));
247 * Walk through the database list and return the first database which
248 * last update is too far in past
250 struct rrd_database *get_outdated_db(struct rrd_database **dblist)
253 time_t now = time(0);
255 for (i = 0; dblist[i]; i++) {
256 if ((dblist[i]->last_update + dblist[i]->interval) - now <= 0)
260 /* Nothing to update this time, return null */
265 * See how long we may sleep until it is required to run an update
268 int get_next_update(struct rrd_database **dblist, const char **name)
270 int i, sleeptime = 0, diff;
271 time_t now = time(0);
273 for (i = 0; dblist[i]; i++) {
274 diff = dblist[i]->last_update + dblist[i]->interval - now;
277 *name = dblist[i]->name;
279 if (sleeptime > diff) {
281 *name = dblist[i]->name;
290 static int database_exists(struct rrd_database *db)
294 /* If the filename exists, stat will return zero */
296 return !stat(db->filename, &s);
301 static int create_database(struct rrd_database *db)
303 char cmd[] = RRDTOOL_CMD;
304 // char cmd[] = "echo";
305 char *args[512], argstr[ARGSTR_LEN];
306 int idx = 0, argcnt = 0;
310 pr_err("Database %s missing database filename\n", db->name);
314 if (!db->sources || !db->archives) {
315 pr_err("Cannot create db \"%s\", insufficient source data\n",
320 add_arg(args, argcnt, argstr, idx, RRDTOOL_CMD);
321 add_arg(args, argcnt, argstr, idx, "create");
322 add_arg(args, argcnt, argstr, idx, "%s", db->filename);
323 add_arg(args, argcnt, argstr, idx, "--step");
324 add_arg(args, argcnt, argstr, idx, "%d", db->interval);
326 for (i = 0; db->sources[i].type; i++) {
327 add_arg(args, argcnt, argstr, idx, "DS:%s:%s:%d:%f:%f",
330 db->sources[i].heartbeat,
335 for (i = 0; db->archives[i].type; i++) {
336 add_arg(args, argcnt, argstr, idx, "RRA:%s:%f:%d:%d",
337 db->archives[i].type,
339 db->archives[i].steps,
340 db->archives[i].rows);
343 child = run(cmd, args);
345 harvest_zombies(child);
350 int rrdtool_create_missing_databases(struct rrd_database *dbs[])
352 struct rrd_database *db;
355 for (i = 0, db = dbs[i]; db; i++, db = dbs[i]) {
356 if (database_exists(db)) {
357 pr_info("database %s found\n", db->filename);
360 pr_info("Database %s missing, creating\n", db->filename);
361 ret |= create_database(db);