]> git.itanic.dy.fi Git - rrdd/blobdiff - rrdtool.c
onewire_parser.c: Fix compiler warnings about string lengths
[rrdd] / rrdtool.c
index 8f6bc4a43f3568a385665760f827d204519950db..f570f5c981d77cbfafac03acdda50eec5c44a206 100644 (file)
--- a/rrdtool.c
+++ b/rrdtool.c
@@ -2,6 +2,7 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <string.h>
 
@@ -10,6 +11,7 @@
 #include "parser.h"
 #include "debug.h"
 #include "string.h"
+#include "utils.h"
 
 #define MAX_ARGS       512
 #define ARGSTR_LEN     32768
        args[argcnt] = argstr + idx;                    \
        idx += sprintf(argstr + idx, fmt, ##arg);       \
        argcnt++;                                       \
+       args[argcnt] = 0;                               \
        argstr[++idx] = 0
 
 int rrdtool_draw_image(struct rrd_image *image)
 {
-       int pid;
        char cmd[] = RRDTOOL_CMD;
 //     char cmd[] = "echo";
        char *args[512], argstr[ARGSTR_LEN];
        int idx = 0, argcnt = 0, i,j;
        char timestamp[256];
-       char tmp[256];
+       char tmp[sizeof(timestamp)];
        char tmpfile[256];
        time_t t = time(0);
-
-       pid = do_fork_limited();
-       if (pid)
-               return pid;
+       const char *updatestr = "Last update %d.%m.%Y %T (%Z)";
 
        pr_info("Drawing image %s\n", image->image_filename);
 
        tmpfile[0] = 0;
-       strncat(tmpfile, image->image_filename, sizeof(tmp) - strlen(tmp) - 1);
-       strncat(tmpfile, ".tmp", sizeof(tmp) - strlen(tmp) - 1);
+       tmp[0] = 0;
+       strncpy(tmpfile, image->image_filename, sizeof(tmpfile) - 1);
+       _strlcat(tmpfile, ".tmp", sizeof(tmpfile));
+
+       if (image->updatestr)
+               updatestr = image->updatestr;
 
-       strftime(tmp, 256, "%d.%m.%Y %T (%Z) ", localtime(&t));
-       for (i = 0, j = 0; j < 256;) {
+       strftime(tmp, sizeof(tmp), updatestr, localtime(&t));
+       for (i = 0, j = 0; j < sizeof(tmp);) {
                if (tmp[i] == ':') {
                        timestamp[j++] = '\\';
                }
@@ -87,23 +90,21 @@ int rrdtool_draw_image(struct rrd_image *image)
                args[argcnt++] = (char *)image->text[i];
        }
 
-       add_arg(args, argcnt, argstr, idx, "COMMENT:Last update %s\\c", timestamp);
+       add_arg(args, argcnt, argstr, idx, "COMMENT: %s\\c", timestamp);
 
-       args[argcnt] = 0;
-
-       pid = run(cmd, args);
-       harvest_zombies(pid);
+       run(cmd, args);
 
        rename(tmpfile, image->image_filename);
 
-       exit(0);
+       return 0;
 }
 
 int rrdtool_draw_images(struct rrd_image **image)
 {
        int i;
        for (i = 0; image[i]; i++)
-               rrdtool_draw_image(image[i]);
+               queue_work(WORK_PRIORITY_LOW, "rrdtool_draw_image",
+                       (work_fn_t *)rrdtool_draw_image, image[i]);
 
        return 0;
 }
@@ -195,10 +196,147 @@ static int sanitize_rrd_update_data(char *data)
        return entries;
 }
 
-int rrdtool_update_data(struct rrd_database *rrd)
+static int write_to_logfile(struct rrd_database *rrd, const char *data, time_t now)
+{
+       int fd, ret;
+       int spacing, i;
+       char filename[1024];
+       char logstr[RRD_DATA_MAX_LEN * 2] = { 0 };
+       const char *time_stamp_fmt = "%Y.%m.%d %H:%M ";
+       char *str_ptr;
+
+       if (!rrd->logfile)
+               return 0;
+
+       if (rrd->logfile_timestamp_fmt)
+               time_stamp_fmt = rrd->logfile_timestamp_fmt;
+
+       strftime(filename, sizeof(filename), rrd->logfile, localtime(&now));
+
+       fd = open(filename, O_RDWR | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
+       if (fd < 0) {
+               pr_err("Failed to open file %s for logging: %m\n", filename);
+               return -1;
+       }
+
+       strftime(logstr, sizeof(logstr), time_stamp_fmt, localtime(&now));
+
+       str_ptr = logstr + strlen(logstr);
+
+       /* Skip the "N: part */
+       while (*data != ':')
+               data++;
+       data++;
+
+       spacing = 12;
+
+       while (*data && str_ptr - logstr < sizeof(logstr) - 1) {
+               if (*data == ':') {
+                       *str_ptr++ = ' ';
+                       for (i = 0; i < spacing; i++)
+                               *str_ptr++ = ' ';
+                       spacing = 12;
+                       data++;
+                       continue;
+               }
+
+               *str_ptr++ = *data++;
+               spacing--;
+       }
+       *str_ptr++ = '\n';
+       *str_ptr++ = 0;
+
+       ret = write(fd, logstr, strlen(logstr));
+       if (ret < 0)
+               pr_err("Failed to write to logfile %s: %m\n", filename);
+
+       close(fd);
+
+       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;
+       time_t now = time(NULL);
+
+       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; d++) {
+               time_t then;
+
+               if (!data[d])
+                       break;
+
+               sanitize_rrd_update_data(data[d]);
+
+               then = atoi(data[d]);
+               if (then > now) {
+                       pr_err("Skipping bad data with timestamp in future: %ld > %ld\n",
+                               then, now);
+                       continue;
+               }
+               write_to_logfile(rrd, data[d], then);
+               cmdline[i] = data[d];
+               pr_info("Data: %s\n", data[d]);
+
+               rrd->last_update = then;
+               i++;
+       }
+
+       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 + 2];
+       char data[RRD_DATA_MAX_LEN + 12]; /* 12 == "%s:" + NULL termination */
        char cmd[] = RRDTOOL_CMD;
 //     char cmd[] = "echo";
        char *const cmdline[] = {
@@ -209,25 +347,103 @@ int rrdtool_update_data(struct rrd_database *rrd)
                0
        };
        int l;
+       time_t now = time(NULL);
 
-       rrd->last_update = time(0);
-       if (do_fork())
-               return 0;
+       bzero(data, sizeof(data));
+       l = sprintf(data, "%zd:", now);
+
+       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';
 
-       l = sprintf(data, "N:");
+               pr_info("Data: %s\n", data);
 
-       if (rrd->parse) {
-               rrd->parse(data + l, rrd->parser_data);
                sanitize_rrd_update_data(data + l);
-               pid = run(cmd, cmdline);
-               harvest_zombies(pid);
+               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);
 
-       while (harvest_zombies(0));
-       exit(0);
+       /*
+        * 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->update_active = 1;
+
+       return queue_work(WORK_PRIORITY_HIGH, "rrdtool_update_data",
+                       (work_fn_t *)do_rrdtool_update_data, rrd);
+}
+
+/*
+ * Walk through the database list and return the first database which
+ * last update is too far in past
+ */
+struct rrd_database *get_outdated_db(struct rrd_database **dblist)
+{
+       int i;
+       time_t now = time(0), last;
+
+       for (i = 0; dblist[i]; i++) {
+               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];
+       }
+
+       /* Nothing to update this time, return null */
+       return NULL;
+}
+
+/*
+ * See how long we may sleep until next update interval window begins
+ */
+int get_next_update(struct rrd_database **dblist, const char **name)
+{
+       int i, sleeptime = -1, diff;
+       time_t now = time(0);
+
+       for (i = 0; dblist[i]; i++) {
+               if (dblist[i]->update_active)
+                       continue;
+
+               diff = ROUND_UP(dblist[i]->last_update, dblist[i]->interval) - now;
+               diff = max(diff, dblist[i]->update_backoff - now);
+
+               if (sleeptime == -1 || sleeptime > diff) {
+                       sleeptime = diff;
+                       *name = dblist[i]->name;
+               }
+       }
+
+       return sleeptime;
 }
 
 static int database_exists(struct rrd_database *db)
@@ -241,13 +457,45 @@ static int database_exists(struct rrd_database *db)
        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, 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, NULL);
+       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);
+
+       close(ofd);
+       clear_zombie(child);
+
+       return 0;
+}
+
 static int create_database(struct rrd_database *db)
 {
        char cmd[] = RRDTOOL_CMD;
 //     char cmd[] = "echo";
        char *args[512], argstr[ARGSTR_LEN];
        int idx = 0, argcnt = 0;
-       int child, i;
+       int i;
 
        if (!db->filename) {
                pr_err("Database %s missing database filename\n", db->name);
@@ -283,9 +531,7 @@ static int create_database(struct rrd_database *db)
                        db->archives[i].rows);
        }
 
-       child = run(cmd, args);
-
-       harvest_zombies(child);
+       run(cmd, args);
 
        return 0;
 }
@@ -298,6 +544,7 @@ int rrdtool_create_missing_databases(struct rrd_database *dbs[])
        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);