]> git.itanic.dy.fi Git - rrdd/blob - rrdtool.c
config: Fix copy paste errors in error messages
[rrdd] / rrdtool.c
1 #include <time.h>
2
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <string.h>
7
8 #include "rrdtool.h"
9 #include "process.h"
10 #include "parser.h"
11 #include "debug.h"
12 #include "string.h"
13
14 #define MAX_ARGS        512
15 #define ARGSTR_LEN      32768
16
17 #define RRDTOOL_CMD "/usr/bin/rrdtool"
18
19 /*
20  * Add new argument to a argument list
21  *
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
26  */
27 #define add_arg(args, argcnt, argstr, idx, fmt, arg...) \
28         args[argcnt] = argstr + idx;                    \
29         idx += sprintf(argstr + idx, fmt, ##arg);       \
30         argcnt++;                                       \
31         argstr[++idx] = 0
32
33 int rrdtool_draw_image(struct rrd_image *image)
34 {
35         int pid;
36         char cmd[] = RRDTOOL_CMD;
37 //      char cmd[] = "echo";
38         char *args[512], argstr[ARGSTR_LEN];
39         int idx = 0, argcnt = 0, i,j;
40         char timestamp[256];
41         char tmp[256];
42         char tmpfile[256];
43         time_t t = time(0);
44
45         pid = do_fork_limited();
46         if (pid)
47                 return pid;
48
49         pr_info("Drawing image %s\n", image->image_filename);
50
51         tmpfile[0] = 0;
52         strncat(tmpfile, image->image_filename, sizeof(tmp) - strlen(tmp) - 1);
53         strncat(tmpfile, ".tmp", sizeof(tmp) - strlen(tmp) - 1);
54
55         strftime(tmp, 256, "%d.%m.%Y %T (%Z) ", localtime(&t));
56         for (i = 0, j = 0; j < 256;) {
57                 if (tmp[i] == ':') {
58                         timestamp[j++] = '\\';
59                 }
60                 timestamp[j++] = tmp[i++];
61                 if (!tmp[i])
62                         break;
63         }
64         timestamp[j] = 0;
65
66
67         add_arg(args, argcnt, argstr, idx, RRDTOOL_CMD);
68         add_arg(args, argcnt, argstr, idx, "graph");
69         add_arg(args, argcnt, argstr, idx, "%s", tmpfile);
70
71         add_arg(args, argcnt, argstr, idx, "--start");
72         add_arg(args, argcnt, argstr, idx, "%s", image->timestart);
73         add_arg(args, argcnt, argstr, idx, "--end");
74         add_arg(args, argcnt, argstr, idx, "%s", image->timeend);
75         add_arg(args, argcnt, argstr, idx, "--width");
76         add_arg(args, argcnt, argstr, idx, "%d", image->width);
77         add_arg(args, argcnt, argstr, idx, "--height");
78         add_arg(args, argcnt, argstr, idx, "%d", image->height);
79         add_arg(args, argcnt, argstr, idx, "--imgformat");
80         add_arg(args, argcnt, argstr, idx, "%s", image->imageformat);
81
82         for (i = 0; image->options[i]; i++) {
83                 add_arg(args, argcnt, argstr, idx, "%s", image->options[i]);
84         }
85
86         for (i = 0; image->text[i]; i++) {
87                 args[argcnt++] = (char *)image->text[i];
88         }
89
90         add_arg(args, argcnt, argstr, idx, "COMMENT:Last update %s\\c", timestamp);
91
92         args[argcnt] = 0;
93
94         pid = run(cmd, args);
95         harvest_zombies(pid);
96
97         rename(tmpfile, image->image_filename);
98
99         exit(0);
100 }
101
102 int rrdtool_draw_images(struct rrd_image **image)
103 {
104         int i;
105         for (i = 0; image[i]; i++)
106                 rrdtool_draw_image(image[i]);
107
108         return 0;
109 }
110
111 static int sanitize_rrd_update_data(char *data)
112 {
113         char clean_data[RRD_DATA_MAX_LEN];
114         int entries = 0;
115         int minus;
116         char *src, *end, *cln;
117
118         data[RRD_DATA_MAX_LEN - 1] = 0;
119         src = data;
120         cln = clean_data;
121
122         /*
123          * Copy a legit floating point number to clean_data buffer
124          * starting from *src and ending to next ':'. If no legit
125          * number could be found, put a 'U' there instead to make
126          * rrdtool to understand this datapoint is undefined.
127          */
128
129         while (src < data + RRD_DATA_MAX_LEN && *src) {
130                 minus = 0;
131
132                 /* skip any non_numbers but not ':' */
133                 while (*src && !isdigit(*src) && *src != '-' && *src != ':')
134                         src++;
135
136                 if (*src == '-') {
137                         src++;
138                         minus = 1;
139                 }
140
141                 /* Now find the end of the number */
142                 end = skip_numbers(src);
143
144                 /* Floating point numberrs may have a dot with more numbers */
145                 if (*end == '.') {
146                         end++;
147                         end = skip_numbers(end);
148                 }
149
150                 /*
151                  * Now we have gone past the number, there should be a
152                  * colon or zero byte. If src == end, there was no
153                  * number and the entry is undefined instead.
154                  */
155                 if ((*end == ':' || !*end) && src != end) {
156                         if (minus) {
157                                 *cln = '-';
158                                 cln++;
159                         }
160
161                         /*
162                          * Copy the legit number and start copying the
163                          * next one
164                          */
165                         for (; src <= end; src++, cln++)
166                                 *cln = *src;
167
168                         goto next;
169                 }
170
171                 /* Skip over whatever junk there might be */
172                 while (*end != ':' && *end)
173                         end++;
174
175                 /* Mark the entry as undefined */
176                 *cln = 'U';
177                 cln++;
178                 *cln = ':';
179                 cln++;
180         next:
181                 end++;
182                 src = end;
183                 entries++;
184         }
185
186         /*
187          * If last entry was undefined, we need to remove the extra
188          * colon at the end
189          */
190         if (*(cln - 1) == ':')
191                 cln--;
192         *cln = '\0';
193
194         strncpy(data, clean_data, RRD_DATA_MAX_LEN);
195         return entries;
196 }
197
198 int rrdtool_update_data(struct rrd_database *rrd)
199 {
200         int pid;
201         char data[RRD_DATA_MAX_LEN + 3]; /* 3 == "N:" + NULL termination */
202         char cmd[] = RRDTOOL_CMD;
203 //      char cmd[] = "echo";
204         char *const cmdline[] = {
205                 RRDTOOL_CMD,
206                 "update",
207                 (char *const)rrd->filename,
208                 data,
209                 0
210         };
211         int l;
212
213         rrd->last_update = time(0);
214         if (do_fork())
215                 return 0;
216
217         l = sprintf(data, "N:");
218
219         if (rrd->parse) {
220                 rrd->parse(data + l, rrd->parser_data);
221                 data[RRD_DATA_MAX_LEN + 2] = '\0';
222
223                 pr_info("Data: %s\n", data);
224
225                 sanitize_rrd_update_data(data + l);
226                 pid = run(cmd, cmdline);
227                 harvest_zombies(pid);
228         }
229
230         if (rrd->images)
231                 rrdtool_draw_images(rrd->images);
232
233         while (harvest_zombies(0));
234         exit(0);
235 }
236
237 /*
238  * Walk through the database list and return the first database which
239  * last update is too far in past
240  */
241 struct rrd_database *get_outdated_db(struct rrd_database **dblist)
242 {
243         int i;
244         time_t now = time(0);
245
246         for (i = 0; dblist[i]; i++) {
247                 if ((dblist[i]->last_update + dblist[i]->interval) - now <= 0)
248                         return dblist[i];
249         }
250
251         /* Nothing to update this time, return null */
252         return NULL;
253 }
254
255 /*
256  * See how long we may sleep until it is required to run an update
257  * again
258  */
259 int get_next_update(struct rrd_database **dblist, const char **name)
260 {
261         int i, sleeptime = 0, diff;
262         time_t now = time(0);
263
264         for (i = 0; dblist[i]; i++) {
265                 diff = dblist[i]->last_update + dblist[i]->interval - now;
266                 if (!sleeptime) {
267                         sleeptime = diff;
268                         *name = dblist[i]->name;
269                 }
270                 if (sleeptime > diff) {
271                         sleeptime = diff;
272                         *name = dblist[i]->name;
273                 }
274                 if (sleeptime <= 0)
275                         return 0;
276         }
277
278         return sleeptime;
279 }
280
281 static int database_exists(struct rrd_database *db)
282 {
283         struct stat s;
284
285         /* If the filename exists, stat will return zero */
286         if (db->filename)
287                 return !stat(db->filename, &s);
288
289         return 0;
290 }
291
292 static int create_database(struct rrd_database *db)
293 {
294         char cmd[] = RRDTOOL_CMD;
295 //      char cmd[] = "echo";
296         char *args[512], argstr[ARGSTR_LEN];
297         int idx = 0, argcnt = 0;
298         int child, i;
299
300         if (!db->filename) {
301                 pr_err("Database %s missing database filename\n", db->name);
302                 return -1;
303         }
304
305         if (!db->sources || !db->archives) {
306                 pr_err("Cannot create db \"%s\", insufficient source data\n",
307                         db->filename);
308                 return -1;
309         }
310
311         add_arg(args, argcnt, argstr, idx, RRDTOOL_CMD);
312         add_arg(args, argcnt, argstr, idx, "create");
313         add_arg(args, argcnt, argstr, idx, "%s", db->filename);
314         add_arg(args, argcnt, argstr, idx, "--step");
315         add_arg(args, argcnt, argstr, idx, "%d", db->interval);
316
317         for (i = 0; db->sources[i].type; i++) {
318                 add_arg(args, argcnt, argstr, idx, "DS:%s:%s:%d:%f:%f",
319                         db->sources[i].name,
320                         db->sources[i].type,
321                         db->sources[i].heartbeat,
322                         db->sources[i].min,
323                         db->sources[i].max);
324         }
325
326         for (i = 0; db->archives[i].type; i++) {
327                 add_arg(args, argcnt, argstr, idx, "RRA:%s:%f:%d:%d",
328                         db->archives[i].type,
329                         db->archives[i].xff,
330                         db->archives[i].steps,
331                         db->archives[i].rows);
332         }
333
334         child = run(cmd, args);
335
336         harvest_zombies(child);
337
338         return 0;
339 }
340
341 int rrdtool_create_missing_databases(struct rrd_database *dbs[])
342 {
343         struct rrd_database *db;
344         int i, ret = 0;
345
346         for (i = 0, db = dbs[i]; db; i++, db = dbs[i]) {
347                 if (database_exists(db)) {
348                         pr_info("database %s found\n", db->filename);
349                         continue;
350                 }
351                 pr_info("Database %s missing, creating\n", db->filename);
352                 ret |= create_database(db);
353         }
354
355         return ret;
356 }