16 #define ARGSTR_LEN 32768
18 #define RRDTOOL_CMD "/usr/bin/rrdtool"
21 * Add new argument to a argument list
23 * args pointer list to arguments
24 * argcnt argument counter
25 * argstr array where the actual arguments are stored
26 * idx index in the argstr where the new argument will be appended
28 #define add_arg(args, argcnt, argstr, idx, fmt, arg...) \
29 args[argcnt] = argstr + idx; \
30 idx += sprintf(argstr + idx, fmt, ##arg); \
35 int rrdtool_draw_image(struct rrd_image *image)
37 char cmd[] = RRDTOOL_CMD;
38 // char cmd[] = "echo";
39 char *args[512], argstr[ARGSTR_LEN];
40 int idx = 0, argcnt = 0, i,j;
42 char tmp[sizeof(timestamp)];
45 const char *updatestr = "Last update %d.%m.%Y %T (%Z)";
47 pr_info("Drawing image %s\n", image->image_filename);
50 strncat(tmpfile, image->image_filename, sizeof(tmp) - strlen(tmp) - 1);
51 strncat(tmpfile, ".tmp", sizeof(tmp) - strlen(tmp) - 1);
54 updatestr = image->updatestr;
56 strftime(tmp, sizeof(tmp), updatestr, localtime(&t));
57 for (i = 0, j = 0; j < sizeof(tmp);) {
59 timestamp[j++] = '\\';
61 timestamp[j++] = tmp[i++];
68 add_arg(args, argcnt, argstr, idx, RRDTOOL_CMD);
69 add_arg(args, argcnt, argstr, idx, "graph");
70 add_arg(args, argcnt, argstr, idx, "%s", tmpfile);
72 add_arg(args, argcnt, argstr, idx, "--start");
73 add_arg(args, argcnt, argstr, idx, "%s", image->timestart);
74 add_arg(args, argcnt, argstr, idx, "--end");
75 add_arg(args, argcnt, argstr, idx, "%s", image->timeend);
76 add_arg(args, argcnt, argstr, idx, "--width");
77 add_arg(args, argcnt, argstr, idx, "%d", image->width);
78 add_arg(args, argcnt, argstr, idx, "--height");
79 add_arg(args, argcnt, argstr, idx, "%d", image->height);
80 add_arg(args, argcnt, argstr, idx, "--imgformat");
81 add_arg(args, argcnt, argstr, idx, "%s", image->imageformat);
83 for (i = 0; image->options[i]; i++) {
84 add_arg(args, argcnt, argstr, idx, "%s", image->options[i]);
87 for (i = 0; image->text[i]; i++) {
88 args[argcnt++] = (char *)image->text[i];
91 add_arg(args, argcnt, argstr, idx, "COMMENT: %s\\c", timestamp);
95 rename(tmpfile, image->image_filename);
100 int rrdtool_draw_images(struct rrd_image **image)
103 for (i = 0; image[i]; i++)
104 queue_work(WORK_PRIORITY_LOW, "rrdtool_draw_image",
105 (work_fn_t *)rrdtool_draw_image, image[i]);
110 static int sanitize_rrd_update_data(char *data)
112 char clean_data[RRD_DATA_MAX_LEN];
115 char *src, *end, *cln;
117 data[RRD_DATA_MAX_LEN - 1] = 0;
122 * Copy a legit floating point number to clean_data buffer
123 * starting from *src and ending to next ':'. If no legit
124 * number could be found, put a 'U' there instead to make
125 * rrdtool to understand this datapoint is undefined.
128 while (src < data + RRD_DATA_MAX_LEN && *src) {
131 /* skip any non_numbers but not ':' */
132 while (*src && !isdigit(*src) && *src != '-' && *src != ':')
140 /* Now find the end of the number */
141 end = skip_numbers(src);
143 /* Floating point numberrs may have a dot with more numbers */
146 end = skip_numbers(end);
150 * Now we have gone past the number, there should be a
151 * colon or zero byte. If src == end, there was no
152 * number and the entry is undefined instead.
154 if ((*end == ':' || !*end) && src != end) {
161 * Copy the legit number and start copying the
164 for (; src <= end; src++, cln++)
170 /* Skip over whatever junk there might be */
171 while (*end != ':' && *end)
174 /* Mark the entry as undefined */
186 * If last entry was undefined, we need to remove the extra
189 if (*(cln - 1) == ':')
193 strncpy(data, clean_data, RRD_DATA_MAX_LEN);
197 static int write_to_logfile(struct rrd_database *rrd, const char *data)
199 time_t t = time(NULL);
203 char logstr[RRD_DATA_MAX_LEN * 2] = { 0 };
204 const char *time_stamp_fmt = "%Y.%m.%d %H:%M ";
210 if (rrd->logfile_timestamp_fmt)
211 time_stamp_fmt = rrd->logfile_timestamp_fmt;
213 strftime(filename, sizeof(filename), rrd->logfile, localtime(&t));
215 fd = open(filename, O_RDWR | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
217 pr_err("Failed to open file %s for logging: %m\n", filename);
221 strftime(logstr, sizeof(logstr), time_stamp_fmt, localtime(&t));
223 str_ptr = logstr + strlen(logstr);
225 data += 2; /* Skip the "N: part */
228 while (*data && str_ptr - logstr < sizeof(logstr) - 1) {
231 for (i = 0; i < spacing; i++)
238 *str_ptr++ = *data++;
244 ret = write(fd, logstr, strlen(logstr));
246 pr_err("Failed to write to logfile %s: %m\n", filename);
250 return ret < 0 ? ret : 0;
253 static int run_post_draw_cmd(struct rrd_database *rrd)
255 pr_info("Running post draw command for %s\n", rrd->name);
257 if (rrd->post_draw_cmd && !strcmp(rrd->post_draw_cmd[0], "shell"))
258 run(rrd->post_draw_cmd[1], &rrd->post_draw_cmd[1]);
263 static int do_rrdtool_update_data(struct rrd_database *rrd)
265 char data[RRD_DATA_MAX_LEN + 3]; /* 3 == "N:" + NULL termination */
266 char cmd[] = RRDTOOL_CMD;
267 // char cmd[] = "echo";
268 char *const cmdline[] = {
271 (char *const)rrd->filename,
277 bzero(data, sizeof(data));
278 l = sprintf(data, "N:");
280 if (rrd->parser && rrd->parser->parse) {
281 rrd->parser->parse(data + l, rrd->parser_data,
283 data[RRD_DATA_MAX_LEN + 2] = '\0';
285 pr_info("Data: %s\n", data);
287 sanitize_rrd_update_data(data + l);
288 write_to_logfile(rrd, data);
293 if (rrd->pre_draw_cmd && !strcmp(rrd->pre_draw_cmd[0], "shell")) {
294 run(rrd->pre_draw_cmd[1], &rrd->pre_draw_cmd[1]);
298 rrdtool_draw_images(rrd->images);
301 * We rely on the fact that rrdtool_draw_images queues image
302 * drawings into low priority queue and the post draw queue is
303 * placed on the queue after images. This ensures post draw
304 * command is not started before images are started.
306 * There is nothing that guarantees post_draw_cmd is executed
307 * after all images are completed though, but it's close..
309 if (rrd->post_draw_cmd)
310 queue_work(WORK_PRIORITY_LOW, "rrdtool_post_draw_cmd",
311 (work_fn_t *)run_post_draw_cmd, rrd);
316 int rrdtool_update_data(struct rrd_database *rrd)
318 time_t now = time(0);
321 * This will put our last update slightly into past, but
322 * ensures our update interval will not drift over time.
324 rrd->last_update = now - now % rrd->interval;
326 return queue_work(WORK_PRIORITY_HIGH, "rrdtool_update_data",
327 (work_fn_t *)do_rrdtool_update_data, rrd);
331 * Walk through the database list and return the first database which
332 * last update is too far in past
334 struct rrd_database *get_outdated_db(struct rrd_database **dblist)
337 time_t now = time(0);
339 for (i = 0; dblist[i]; i++) {
340 if ((dblist[i]->last_update + dblist[i]->interval) - now <= 0)
344 /* Nothing to update this time, return null */
349 * See how long we may sleep until it is required to run an update
352 int get_next_update(struct rrd_database **dblist, const char **name)
354 int i, sleeptime = 0, diff;
355 time_t now = time(0);
357 for (i = 0; dblist[i]; i++) {
358 diff = dblist[i]->last_update + dblist[i]->interval - now;
361 *name = dblist[i]->name;
363 if (sleeptime > diff) {
365 *name = dblist[i]->name;
374 static int database_exists(struct rrd_database *db)
378 /* If the filename exists, stat will return zero */
380 return !stat(db->filename, &s);
385 static int create_database(struct rrd_database *db)
387 char cmd[] = RRDTOOL_CMD;
388 // char cmd[] = "echo";
389 char *args[512], argstr[ARGSTR_LEN];
390 int idx = 0, argcnt = 0;
394 pr_err("Database %s missing database filename\n", db->name);
398 if (!db->sources || !db->archives) {
399 pr_err("Cannot create db \"%s\", insufficient source data\n",
404 add_arg(args, argcnt, argstr, idx, RRDTOOL_CMD);
405 add_arg(args, argcnt, argstr, idx, "create");
406 add_arg(args, argcnt, argstr, idx, "%s", db->filename);
407 add_arg(args, argcnt, argstr, idx, "--step");
408 add_arg(args, argcnt, argstr, idx, "%d", db->interval);
410 for (i = 0; db->sources[i].type; i++) {
411 add_arg(args, argcnt, argstr, idx, "DS:%s:%s:%d:%f:%f",
414 db->sources[i].heartbeat,
419 for (i = 0; db->archives[i].type; i++) {
420 add_arg(args, argcnt, argstr, idx, "RRA:%s:%f:%d:%d",
421 db->archives[i].type,
423 db->archives[i].steps,
424 db->archives[i].rows);
432 int rrdtool_create_missing_databases(struct rrd_database *dbs[])
434 struct rrd_database *db;
437 for (i = 0, db = dbs[i]; db; i++, db = dbs[i]) {
438 if (database_exists(db)) {
439 pr_info("database %s found\n", db->filename);
442 pr_info("Database %s missing, creating\n", db->filename);
443 ret |= create_database(db);