17 #define ARGSTR_LEN 32768
19 #define RRDTOOL_CMD "/usr/bin/rrdtool"
22 * Add new argument to a argument list
24 * args pointer list to arguments
25 * argcnt argument counter
26 * argstr array where the actual arguments are stored
27 * idx index in the argstr where the new argument will be appended
29 #define add_arg(args, argcnt, argstr, idx, fmt, arg...) \
30 args[argcnt] = argstr + idx; \
31 idx += sprintf(argstr + idx, fmt, ##arg); \
36 int rrdtool_draw_image(struct rrd_image *image)
38 char cmd[] = RRDTOOL_CMD;
39 // char cmd[] = "echo";
40 char *args[512], argstr[ARGSTR_LEN];
41 int idx = 0, argcnt = 0, i,j;
43 char tmp[sizeof(timestamp)];
46 const char *updatestr = "Last update %d.%m.%Y %T (%Z)";
48 pr_info("Drawing image %s\n", image->image_filename);
52 strncpy(tmpfile, image->image_filename, sizeof(tmpfile) - 1);
53 strncat(tmpfile, ".tmp",
54 sizeof(tmpfile) - strlen(image->image_filename) - 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);
98 rename(tmpfile, image->image_filename);
103 int rrdtool_draw_images(struct rrd_image **image)
106 for (i = 0; image[i]; i++)
107 queue_work(WORK_PRIORITY_LOW, "rrdtool_draw_image",
108 (work_fn_t *)rrdtool_draw_image, image[i]);
113 static int sanitize_rrd_update_data(char *data)
115 char clean_data[RRD_DATA_MAX_LEN];
118 char *src, *end, *cln;
120 data[RRD_DATA_MAX_LEN - 1] = 0;
125 * Copy a legit floating point number to clean_data buffer
126 * starting from *src and ending to next ':'. If no legit
127 * number could be found, put a 'U' there instead to make
128 * rrdtool to understand this datapoint is undefined.
131 while (src < data + RRD_DATA_MAX_LEN && *src) {
134 /* skip any non_numbers but not ':' */
135 while (*src && !isdigit(*src) && *src != '-' && *src != ':')
143 /* Now find the end of the number */
144 end = skip_numbers(src);
146 /* Floating point numberrs may have a dot with more numbers */
149 end = skip_numbers(end);
153 * Now we have gone past the number, there should be a
154 * colon or zero byte. If src == end, there was no
155 * number and the entry is undefined instead.
157 if ((*end == ':' || !*end) && src != end) {
164 * Copy the legit number and start copying the
167 for (; src <= end; src++, cln++)
173 /* Skip over whatever junk there might be */
174 while (*end != ':' && *end)
177 /* Mark the entry as undefined */
189 * If last entry was undefined, we need to remove the extra
192 if (*(cln - 1) == ':')
196 strncpy(data, clean_data, RRD_DATA_MAX_LEN);
200 static int write_to_logfile(struct rrd_database *rrd, const char *data, time_t now)
205 char logstr[RRD_DATA_MAX_LEN * 2] = { 0 };
206 const char *time_stamp_fmt = "%Y.%m.%d %H:%M ";
212 if (rrd->logfile_timestamp_fmt)
213 time_stamp_fmt = rrd->logfile_timestamp_fmt;
215 strftime(filename, sizeof(filename), rrd->logfile, localtime(&now));
217 fd = open(filename, O_RDWR | O_APPEND | O_CREAT | O_CLOEXEC, 0644);
219 pr_err("Failed to open file %s for logging: %m\n", filename);
223 strftime(logstr, sizeof(logstr), time_stamp_fmt, localtime(&now));
225 str_ptr = logstr + strlen(logstr);
227 /* Skip the "N: part */
234 while (*data && str_ptr - logstr < sizeof(logstr) - 1) {
237 for (i = 0; i < spacing; i++)
244 *str_ptr++ = *data++;
250 ret = write(fd, logstr, strlen(logstr));
252 pr_err("Failed to write to logfile %s: %m\n", filename);
256 return ret < 0 ? ret : 0;
259 static int run_post_draw_cmd(struct rrd_database *rrd)
261 pr_info("Running post draw command for %s\n", rrd->name);
263 if (rrd->post_draw_cmd && !strcmp(rrd->post_draw_cmd[0], "shell"))
264 run(rrd->post_draw_cmd[1], &rrd->post_draw_cmd[1]);
269 static int rrdtool_update_data_multi(struct rrd_database *rrd)
273 char cmd[] = RRDTOOL_CMD;
274 char *cmdline[512] = {
277 (char *const)rrd->filename,
279 int old_last_update = rrd->last_update;
281 ret = rrd->parser->parse_multi(&data, rrd->parser_data,
282 &rrd->parser_state, rrd->last_update);
284 pr_err("Parser failure: %d\n", ret);
288 for (i = 3, d = 0; i < ARRAY_SIZE(cmdline) - 1; i++, d++) {
294 sanitize_rrd_update_data(data[d]);
296 then = atoi(data[d]);
297 write_to_logfile(rrd, data[d], then);
298 cmdline[i] = data[d];
299 pr_info("Data: %s\n", data[d]);
301 rrd->last_update = then;
311 for (d = 0; data[d]; d++)
315 if (old_last_update == rrd->last_update) {
316 rrd->update_backoff = time(NULL) + 10;
317 pr_info("Setting backoff\n");
319 rrd->update_backoff = 0;
322 * Re-schedule job processing in case we are too far behind
323 * with updates on this database and can start parsing more
326 notify_job_request();
331 static int do_rrdtool_update_data(struct rrd_database *rrd)
333 char data[RRD_DATA_MAX_LEN + 12]; /* 12 == "%s:" + NULL termination */
334 char cmd[] = RRDTOOL_CMD;
335 // char cmd[] = "echo";
336 char *const cmdline[] = {
339 (char *const)rrd->filename,
344 time_t now = time(NULL);
346 bzero(data, sizeof(data));
347 l = sprintf(data, "%zd:", now);
349 if (rrd->parser && rrd->parser->parse_multi) {
350 rrdtool_update_data_multi(rrd);
351 } else if (rrd->parser && rrd->parser->parse) {
352 rrd->parser->parse(data + l, rrd->parser_data,
354 data[RRD_DATA_MAX_LEN + l] = '\0';
356 pr_info("Data: %s\n", data);
358 sanitize_rrd_update_data(data + l);
359 write_to_logfile(rrd, data, now);
362 rrd->last_update = now;
364 rrd->last_update = now;
366 if (rrd->pre_draw_cmd && !strcmp(rrd->pre_draw_cmd[0], "shell")) {
367 run(rrd->pre_draw_cmd[1], &rrd->pre_draw_cmd[1]);
371 rrdtool_draw_images(rrd->images);
374 * We rely on the fact that rrdtool_draw_images queues image
375 * drawings into low priority queue and the post draw queue is
376 * placed on the queue after images. This ensures post draw
377 * command is not started before images are started.
379 * There is nothing that guarantees post_draw_cmd is executed
380 * after all images are completed though, but it's close..
382 if (rrd->post_draw_cmd)
383 queue_work(WORK_PRIORITY_LOW, "rrdtool_post_draw_cmd",
384 (work_fn_t *)run_post_draw_cmd, rrd);
386 rrd->update_active = 0;
391 int rrdtool_update_data(struct rrd_database *rrd)
393 rrd->update_active = 1;
395 return queue_work(WORK_PRIORITY_HIGH, "rrdtool_update_data",
396 (work_fn_t *)do_rrdtool_update_data, rrd);
400 * Walk through the database list and return the first database which
401 * last update is too far in past
403 struct rrd_database *get_outdated_db(struct rrd_database **dblist)
406 time_t now = time(0), last;
408 for (i = 0; dblist[i]; i++) {
409 last = max(ROUND_UP(dblist[i]->last_update, dblist[i]->interval),
410 dblist[i]->update_backoff);
411 if (!dblist[i]->update_active && last - now <= 0)
415 /* Nothing to update this time, return null */
420 * See how long we may sleep until next update interval window begins
422 int get_next_update(struct rrd_database **dblist, const char **name)
424 int i, sleeptime = 0, diff;
425 time_t now = time(0);
427 for (i = 0; dblist[i]; i++) {
428 diff = ROUND_UP(dblist[i]->last_update, dblist[i]->interval) - now;
429 diff = max(diff, dblist[i]->update_backoff - now);
431 if (dblist[i]->update_active)
432 diff = (now + dblist[i]->interval) % dblist[i]->interval;
436 *name = dblist[i]->name;
438 if (sleeptime > diff) {
440 *name = dblist[i]->name;
450 static int database_exists(struct rrd_database *db)
454 /* If the filename exists, stat will return zero */
456 return !stat(db->filename, &s);
461 static int get_last_update(struct rrd_database *db)
463 char cmd[] = RRDTOOL_CMD;
464 char *args[10], argstr[ARGSTR_LEN];
466 int idx = 0, argcnt = 0;
470 add_arg(args, argcnt, argstr, idx, RRDTOOL_CMD);
471 add_arg(args, argcnt, argstr, idx, "last");
472 add_arg(args, argcnt, argstr, idx, db->filename);
474 child = run_piped(cmd, args, NULL, &ofd, &efd);
475 ret = read(ofd, buf, sizeof(buf) - 1);
477 pr_err("Error reading: %m\n");
483 db->last_update = atoi(buf);
484 pr_info("Last update for %s is: %ld, %ld sec ago\n", db->name, db->last_update,
485 time(NULL) - db->last_update);
492 static int create_database(struct rrd_database *db)
494 char cmd[] = RRDTOOL_CMD;
495 // char cmd[] = "echo";
496 char *args[512], argstr[ARGSTR_LEN];
497 int idx = 0, argcnt = 0;
501 pr_err("Database %s missing database filename\n", db->name);
505 if (!db->sources || !db->archives) {
506 pr_err("Cannot create db \"%s\", insufficient source data\n",
511 add_arg(args, argcnt, argstr, idx, RRDTOOL_CMD);
512 add_arg(args, argcnt, argstr, idx, "create");
513 add_arg(args, argcnt, argstr, idx, "%s", db->filename);
514 add_arg(args, argcnt, argstr, idx, "--step");
515 add_arg(args, argcnt, argstr, idx, "%d", db->interval);
517 for (i = 0; db->sources[i].type; i++) {
518 add_arg(args, argcnt, argstr, idx, "DS:%s:%s:%d:%f:%f",
521 db->sources[i].heartbeat,
526 for (i = 0; db->archives[i].type; i++) {
527 add_arg(args, argcnt, argstr, idx, "RRA:%s:%f:%d:%d",
528 db->archives[i].type,
530 db->archives[i].steps,
531 db->archives[i].rows);
539 int rrdtool_create_missing_databases(struct rrd_database *dbs[])
541 struct rrd_database *db;
544 for (i = 0, db = dbs[i]; db; i++, db = dbs[i]) {
545 if (database_exists(db)) {
546 pr_info("database %s found\n", db->filename);
550 pr_info("Database %s missing, creating\n", db->filename);
551 ret |= create_database(db);