#include "parser.h"
#include "debug.h"
#include "string.h"
+#include "utils.h"
#define MAX_ARGS 512
#define ARGSTR_LEN 32768
int rrdtool_draw_image(struct rrd_image *image)
{
- int pid;
char cmd[] = RRDTOOL_CMD;
// char cmd[] = "echo";
char *args[512], argstr[ARGSTR_LEN];
pr_info("Drawing image %s\n", image->image_filename);
tmpfile[0] = 0;
- strncat(tmpfile, image->image_filename, sizeof(tmp) - 1);
- strncat(tmpfile, ".tmp", sizeof(tmp) - 1);
+ tmp[0] = 0;
+ strncpy(tmpfile, image->image_filename, sizeof(tmpfile) - 1);
+ _strlcat(tmpfile, ".tmp", sizeof(tmpfile));
if (image->updatestr)
updatestr = image->updatestr;
int i;
for (i = 0; image[i]; i++)
queue_work(WORK_PRIORITY_LOW, "rrdtool_draw_image",
- rrdtool_draw_image, image[i]);
+ (work_fn_t *)rrdtool_draw_image, image[i]);
return 0;
}
return entries;
}
-static int write_to_logfile(struct rrd_database *rrd, const char *data)
+static int write_to_logfile(struct rrd_database *rrd, const char *data, time_t now)
{
- time_t t = time(NULL);
int fd, ret;
int spacing, i;
char filename[1024];
if (rrd->logfile_timestamp_fmt)
time_stamp_fmt = rrd->logfile_timestamp_fmt;
- strftime(filename, sizeof(filename), rrd->logfile, localtime(&t));
+ strftime(filename, sizeof(filename), rrd->logfile, localtime(&now));
fd = open(filename, O_RDWR | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
if (fd < 0) {
return -1;
}
- strftime(logstr, sizeof(logstr), time_stamp_fmt, localtime(&t));
+ strftime(logstr, sizeof(logstr), time_stamp_fmt, localtime(&now));
str_ptr = logstr + strlen(logstr);
- data += 2; /* Skip the "N: part */
+ /* Skip the "N: part */
+ while (*data != ':')
+ data++;
+ data++;
+
spacing = 12;
while (*data && str_ptr - logstr < sizeof(logstr) - 1) {
return ret < 0 ? ret : 0;
}
+static int run_post_draw_cmd(struct rrd_database *rrd)
+{
+ pr_info("Running post draw command for %s\n", rrd->name);
+
+ if (rrd->post_draw_cmd && !strcmp(rrd->post_draw_cmd[0], "shell"))
+ run(rrd->post_draw_cmd[1], &rrd->post_draw_cmd[1]);
+
+ return 0;
+}
+
+static int rrdtool_update_data_multi(struct rrd_database *rrd)
+{
+ char **data = NULL;
+ int ret, i, d;
+ char cmd[] = RRDTOOL_CMD;
+ char *cmdline[512] = {
+ RRDTOOL_CMD,
+ "update",
+ (char *const)rrd->filename,
+ };
+ int old_last_update = rrd->last_update;
+
+ ret = rrd->parser->parse_multi(&data, rrd->parser_data,
+ &rrd->parser_state, rrd->last_update);
+ if (ret < 0) {
+ pr_err("Parser failure: %d\n", ret);
+ goto out;
+ }
+
+ for (i = 3, d = 0; i < ARRAY_SIZE(cmdline) - 1; i++, d++) {
+ time_t then;
+
+ if (!data[d])
+ break;
+
+ sanitize_rrd_update_data(data[d]);
+
+ then = atoi(data[d]);
+ write_to_logfile(rrd, data[d], then);
+ cmdline[i] = data[d];
+ pr_info("Data: %s\n", data[d]);
+
+ rrd->last_update = then;
+ }
+
+ cmdline[i] = 0;
+
+ if (ret)
+ run(cmd, cmdline);
+
+out:
+ if (data)
+ for (d = 0; data[d]; d++)
+ free(data[d]);
+ free(data);
+
+ if (old_last_update == rrd->last_update) {
+ rrd->update_backoff = time(NULL) + 10;
+ pr_info("Setting backoff\n");
+ } else
+ rrd->update_backoff = 0;
+
+ /*
+ * Re-schedule job processing in case we are too far behind
+ * with updates on this database and can start parsing more
+ * data immediately.
+ */
+ notify_job_request();
+
+ return 0;
+}
+
static int do_rrdtool_update_data(struct rrd_database *rrd)
{
- int pid;
- char data[RRD_DATA_MAX_LEN + 3]; /* 3 == "N:" + NULL termination */
+ char data[RRD_DATA_MAX_LEN + 12]; /* 12 == "%s:" + NULL termination */
char cmd[] = RRDTOOL_CMD;
// char cmd[] = "echo";
char *const cmdline[] = {
0
};
int l;
+ time_t now = time(NULL);
- l = sprintf(data, "N:");
+ bzero(data, sizeof(data));
+ l = sprintf(data, "%zd:", now);
- if (rrd->parser && rrd->parser->parse) {
- rrd->parser->parse(data + l, rrd->parser_data);
- data[RRD_DATA_MAX_LEN + 2] = '\0';
+ if (rrd->parser && rrd->parser->parse_multi) {
+ rrdtool_update_data_multi(rrd);
+ } else if (rrd->parser && rrd->parser->parse) {
+ rrd->parser->parse(data + l, rrd->parser_data,
+ &rrd->parser_state);
+ data[RRD_DATA_MAX_LEN + l] = '\0';
pr_info("Data: %s\n", data);
sanitize_rrd_update_data(data + l);
- write_to_logfile(rrd, data);
+ write_to_logfile(rrd, data, now);
run(cmd, cmdline);
- }
+ rrd->last_update = now;
+ } else
+ rrd->last_update = now;
if (rrd->pre_draw_cmd && !strcmp(rrd->pre_draw_cmd[0], "shell")) {
run(rrd->pre_draw_cmd[1], &rrd->pre_draw_cmd[1]);
if (rrd->images)
rrdtool_draw_images(rrd->images);
- if (rrd->post_draw_cmd && !strcmp(rrd->post_draw_cmd[0], "shell"))
- run(rrd->post_draw_cmd[1], &rrd->post_draw_cmd[1]);
+ /*
+ * We rely on the fact that rrdtool_draw_images queues image
+ * drawings into low priority queue and the post draw queue is
+ * placed on the queue after images. This ensures post draw
+ * command is not started before images are started.
+ *
+ * There is nothing that guarantees post_draw_cmd is executed
+ * after all images are completed though, but it's close..
+ */
+ if (rrd->post_draw_cmd)
+ queue_work(WORK_PRIORITY_LOW, "rrdtool_post_draw_cmd",
+ (work_fn_t *)run_post_draw_cmd, rrd);
+
+ rrd->update_active = 0;
return 0;
}
int rrdtool_update_data(struct rrd_database *rrd)
{
- rrd->last_update = time(0);
+ rrd->update_active = 1;
return queue_work(WORK_PRIORITY_HIGH, "rrdtool_update_data",
- do_rrdtool_update_data, rrd);
+ (work_fn_t *)do_rrdtool_update_data, rrd);
}
/*
struct rrd_database *get_outdated_db(struct rrd_database **dblist)
{
int i;
- time_t now = time(0);
+ time_t now = time(0), last;
for (i = 0; dblist[i]; i++) {
- if ((dblist[i]->last_update + dblist[i]->interval) - now <= 0)
+ last = max(ROUND_UP(dblist[i]->last_update, dblist[i]->interval),
+ dblist[i]->update_backoff);
+ if (!dblist[i]->update_active && last - now <= 0)
return dblist[i];
}
}
/*
- * See how long we may sleep until it is required to run an update
- * again
+ * See how long we may sleep until next update interval window begins
*/
int get_next_update(struct rrd_database **dblist, const char **name)
{
time_t now = time(0);
for (i = 0; dblist[i]; i++) {
- diff = dblist[i]->last_update + dblist[i]->interval - now;
+ diff = ROUND_UP(dblist[i]->last_update, dblist[i]->interval) - now;
+ diff = max(diff, dblist[i]->update_backoff - now);
+
+ if (dblist[i]->update_active)
+ diff = (now + dblist[i]->interval) % dblist[i]->interval;
+
if (!sleeptime) {
sleeptime = diff;
*name = dblist[i]->name;
sleeptime = diff;
*name = dblist[i]->name;
}
- if (sleeptime <= 0)
- return 0;
}
+ if (sleeptime == 0)
+ sleeptime = -1;
+
return sleeptime;
}
return 0;
}
+static int get_last_update(struct rrd_database *db)
+{
+ char cmd[] = RRDTOOL_CMD;
+ char *args[10], argstr[ARGSTR_LEN];
+ char buf[16];
+ int idx = 0, argcnt = 0;
+ int ofd, efd, child;
+ int ret;
+
+ add_arg(args, argcnt, argstr, idx, RRDTOOL_CMD);
+ add_arg(args, argcnt, argstr, idx, "last");
+ add_arg(args, argcnt, argstr, idx, db->filename);
+
+ child = run_piped(cmd, args, NULL, &ofd, &efd);
+ ret = read(ofd, buf, sizeof(buf) - 1);
+ if (ret < 0) {
+ pr_err("Error reading: %m\n");
+ buf[0] = 0;
+ } else {
+ buf[ret] = 0;
+ }
+
+ db->last_update = atoi(buf);
+ pr_info("Last update for %s is: %ld, %ld sec ago\n", db->name, db->last_update,
+ time(NULL) - db->last_update);
+
+ clear_zombie(child);
+
+ return 0;
+}
+
static int create_database(struct rrd_database *db)
{
char cmd[] = RRDTOOL_CMD;
for (i = 0, db = dbs[i]; db; i++, db = dbs[i]) {
if (database_exists(db)) {
pr_info("database %s found\n", db->filename);
+ get_last_update(db);
continue;
}
pr_info("Database %s missing, creating\n", db->filename);