Big bunch of improvements for the caching daemon.
[rrdtool.git] / src / rrd_info.c
1 /*****************************************************************************
2  * RRDtool 1.3.2  Copyright by Tobi Oetiker, 1997-2008
3  *****************************************************************************
4  * rrd_info  Get Information about the configuration of an RRD
5  *****************************************************************************/
6
7 #include "rrd_tool.h"
8 #include "rrd_rpncalc.h"
9 #include "rrd_client.h"
10 #include <stdarg.h>
11
12 /* proto */
13 rrd_info_t *rrd_info(
14     int,
15     char **);
16 rrd_info_t *rrd_info_r(
17     char *filename);
18
19 /* allocate memory for string */
20 char     *sprintf_alloc(
21     char *fmt,
22     ...)
23 {
24     int       maxlen = 1024 + strlen(fmt);
25     char     *str = NULL;
26     va_list   argp;
27     str = malloc(sizeof(char) * (maxlen + 1));
28     if (str != NULL) {
29         va_start(argp, fmt);
30 #ifdef HAVE_VSNPRINTF
31         vsnprintf(str, maxlen, fmt, argp);
32 #else
33         vsprintf(str, fmt, argp);
34 #endif
35     }
36     va_end(argp);
37     return str;
38 }
39
40 /* the function formerly known as push was renamed to info_push and later
41  * rrd_info_push because it is now used outside the scope of this file */
42 rrd_info_t
43     * rrd_info_push(rrd_info_t * info,
44                     char *key, rrd_info_type_t type, rrd_infoval_t value)
45 {
46     rrd_info_t *next;
47
48     next = malloc(sizeof(*next));
49     next->next = (rrd_info_t *) 0;
50     if (info)
51         info->next = next;
52     next->type = type;
53     next->key = key;
54     switch (type) {
55     case RD_I_VAL:
56         next->value.u_val = value.u_val;
57         break;
58     case RD_I_CNT:
59         next->value.u_cnt = value.u_cnt;
60         break;
61     case RD_I_INT:
62         next->value.u_int = value.u_int;
63         break;
64     case RD_I_STR:
65         next->value.u_str = malloc(sizeof(char) * (strlen(value.u_str) + 1));
66         strcpy(next->value.u_str, value.u_str);
67         break;
68     case RD_I_BLO:
69         next->value.u_blo.size = value.u_blo.size;
70         next->value.u_blo.ptr =
71             malloc(sizeof(unsigned char) * value.u_blo.size);
72         memcpy(next->value.u_blo.ptr, value.u_blo.ptr, value.u_blo.size);
73         break;
74     }
75     return (next);
76 }
77
78
79 rrd_info_t *rrd_info(
80     int argc,
81     char **argv)
82 {
83     rrd_info_t *info;
84     char *opt_daemon = NULL;
85     int status;
86
87     optind = 0;
88     opterr = 0;         /* initialize getopt */
89
90     while (42) {
91         int       opt;
92         int       option_index = 0;
93         static struct option long_options[] = {
94             {"daemon", required_argument, 0, 'd'},
95             {0, 0, 0, 0}
96         };
97
98         opt = getopt_long(argc, argv, "d:", long_options, &option_index);
99
100         if (opt == EOF)
101             break;
102
103         switch (opt) {
104         case 'd':
105             if (opt_daemon != NULL)
106                     free (opt_daemon);
107             opt_daemon = strdup (optarg);
108             if (opt_daemon == NULL)
109             {
110                 rrd_set_error ("strdup failed.");
111                 return (NULL);
112             }
113             break;
114
115         default:
116             rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
117                     argv[0]);
118             return (NULL);
119             break;
120         }
121     }                   /* while (42) */
122
123     if ((argc - optind) != 1) {
124         rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
125                 argv[0]);
126         return (NULL);
127     }
128
129     status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
130     if (opt_daemon) free (opt_daemon);
131     if (status) return (NULL);
132
133     info = rrd_info_r(argv[optind]);
134
135     return (info);
136 } /* rrd_info_t *rrd_info */
137
138 rrd_info_t *rrd_info_r(
139     char *filename)
140 {
141     unsigned int i, ii = 0;
142     rrd_t     rrd;
143     rrd_info_t *data = NULL, *cd;
144     rrd_infoval_t info;
145     rrd_file_t *rrd_file;
146     enum cf_en current_cf;
147     enum dst_en current_ds;
148
149     rrd_file = rrd_open(filename, &rrd, RRD_READONLY);
150     if (rrd_file == NULL)
151         goto err_free;
152
153     info.u_str = filename;
154     cd = rrd_info_push(NULL, sprintf_alloc("filename"), RD_I_STR, info);
155     data = cd;
156
157     info.u_str = rrd.stat_head->version;
158     cd = rrd_info_push(cd, sprintf_alloc("rrd_version"), RD_I_STR, info);
159
160     info.u_cnt = rrd.stat_head->pdp_step;
161     cd = rrd_info_push(cd, sprintf_alloc("step"), RD_I_CNT, info);
162
163     info.u_cnt = rrd.live_head->last_up;
164     cd = rrd_info_push(cd, sprintf_alloc("last_update"), RD_I_CNT, info);
165
166     for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
167
168         info.u_str = rrd.ds_def[i].dst;
169         cd = rrd_info_push(cd, sprintf_alloc("ds[%s].type",
170                                              rrd.ds_def[i].ds_nam),
171                            RD_I_STR, info);
172
173         current_ds = dst_conv(rrd.ds_def[i].dst);
174         switch (current_ds) {
175         case DST_CDEF:
176         {
177             char     *buffer = NULL;
178
179             rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),
180                             rrd.ds_def, &buffer);
181             info.u_str = buffer;
182             cd = rrd_info_push(cd,
183                                sprintf_alloc("ds[%s].cdef",
184                                              rrd.ds_def[i].ds_nam), RD_I_STR,
185                                info);
186             free(buffer);
187         }
188             break;
189         default:
190             info.u_cnt = rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt;
191             cd = rrd_info_push(cd,
192                                sprintf_alloc("ds[%s].minimal_heartbeat",
193                                              rrd.ds_def[i].ds_nam), RD_I_CNT,
194                                info);
195
196             info.u_val = rrd.ds_def[i].par[DS_min_val].u_val;
197             cd = rrd_info_push(cd,
198                                sprintf_alloc("ds[%s].min",
199                                              rrd.ds_def[i].ds_nam), RD_I_VAL,
200                                info);
201
202             info.u_val = rrd.ds_def[i].par[DS_max_val].u_val;
203             cd = rrd_info_push(cd,
204                                sprintf_alloc("ds[%s].max",
205                                              rrd.ds_def[i].ds_nam), RD_I_VAL,
206                                info);
207             break;
208         }
209
210         info.u_str = rrd.pdp_prep[i].last_ds;
211         cd = rrd_info_push(cd,
212                            sprintf_alloc("ds[%s].last_ds",
213                                          rrd.ds_def[i].ds_nam), RD_I_STR,
214                            info);
215
216         info.u_val = rrd.pdp_prep[i].scratch[PDP_val].u_val;
217         cd = rrd_info_push(cd,
218                            sprintf_alloc("ds[%s].value",
219                                          rrd.ds_def[i].ds_nam), RD_I_VAL,
220                            info);
221
222         info.u_cnt = rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt;
223         cd = rrd_info_push(cd,
224                            sprintf_alloc("ds[%s].unknown_sec",
225                                          rrd.ds_def[i].ds_nam), RD_I_CNT,
226                            info);
227     }
228
229     for (i = 0; i < rrd.stat_head->rra_cnt; i++) {
230         info.u_str = rrd.rra_def[i].cf_nam;
231         cd = rrd_info_push(cd, sprintf_alloc("rra[%d].cf", i), RD_I_STR,
232                            info);
233         current_cf = cf_conv(rrd.rra_def[i].cf_nam);
234
235         info.u_cnt = rrd.rra_def[i].row_cnt;
236         cd = rrd_info_push(cd, sprintf_alloc("rra[%d].rows", i), RD_I_CNT,
237                            info);
238
239         info.u_cnt = rrd.rra_ptr[i].cur_row;
240         cd = rrd_info_push(cd, sprintf_alloc("rra[%d].cur_row", i), RD_I_CNT,
241                            info);
242
243         info.u_cnt = rrd.rra_def[i].pdp_cnt;
244         cd = rrd_info_push(cd, sprintf_alloc("rra[%d].pdp_per_row", i),
245                            RD_I_CNT, info);
246
247         switch (current_cf) {
248         case CF_HWPREDICT:
249         case CF_MHWPREDICT:
250             info.u_val = rrd.rra_def[i].par[RRA_hw_alpha].u_val;
251             cd = rrd_info_push(cd, sprintf_alloc("rra[%d].alpha", i),
252                                RD_I_VAL, info);
253             info.u_val = rrd.rra_def[i].par[RRA_hw_beta].u_val;
254             cd = rrd_info_push(cd, sprintf_alloc("rra[%d].beta", i), RD_I_VAL,
255                                info);
256             break;
257         case CF_SEASONAL:
258         case CF_DEVSEASONAL:
259             info.u_val = rrd.rra_def[i].par[RRA_seasonal_gamma].u_val;
260             cd = rrd_info_push(cd, sprintf_alloc("rra[%d].gamma", i),
261                                RD_I_VAL, info);
262             if (atoi(rrd.stat_head->version) >= 4) {
263                 info.u_val =
264                     rrd.rra_def[i].par[RRA_seasonal_smoothing_window].u_val;
265                 cd = rrd_info_push(cd,
266                                    sprintf_alloc("rra[%d].smoothing_window",
267                                                  i), RD_I_VAL, info);
268             }
269             break;
270         case CF_FAILURES:
271             info.u_val = rrd.rra_def[i].par[RRA_delta_pos].u_val;
272             cd = rrd_info_push(cd, sprintf_alloc("rra[%d].delta_pos", i),
273                                RD_I_VAL, info);
274             info.u_val = rrd.rra_def[i].par[RRA_delta_neg].u_val;
275             cd = rrd_info_push(cd, sprintf_alloc("rra[%d].delta_neg", i),
276                                RD_I_VAL, info);
277             info.u_cnt = rrd.rra_def[i].par[RRA_failure_threshold].u_cnt;
278             cd = rrd_info_push(cd,
279                                sprintf_alloc("rra[%d].failure_threshold", i),
280                                RD_I_CNT, info);
281             info.u_cnt = rrd.rra_def[i].par[RRA_window_len].u_cnt;
282             cd = rrd_info_push(cd, sprintf_alloc("rra[%d].window_length", i),
283                                RD_I_CNT, info);
284             break;
285         case CF_DEVPREDICT:
286             break;
287         default:
288             info.u_val = rrd.rra_def[i].par[RRA_cdp_xff_val].u_val;
289             cd = rrd_info_push(cd, sprintf_alloc("rra[%d].xff", i), RD_I_VAL,
290                                info);
291             break;
292         }
293
294         for (ii = 0; ii < rrd.stat_head->ds_cnt; ii++) {
295             switch (current_cf) {
296             case CF_HWPREDICT:
297             case CF_MHWPREDICT:
298                 info.u_val =
299                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
300                                  ii].scratch[CDP_hw_intercept].u_val;
301                 cd = rrd_info_push(cd,
302                                    sprintf_alloc
303                                    ("rra[%d].cdp_prep[%d].intercept", i, ii),
304                                    RD_I_VAL, info);
305                 info.u_val =
306                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
307                                  ii].scratch[CDP_hw_slope].u_val;
308                 cd = rrd_info_push(cd,
309                                    sprintf_alloc("rra[%d].cdp_prep[%d].slope",
310                                                  i, ii), RD_I_VAL, info);
311                 info.u_cnt =
312                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
313                                  ii].scratch[CDP_null_count].u_cnt;
314                 cd = rrd_info_push(cd,
315                                    sprintf_alloc
316                                    ("rra[%d].cdp_prep[%d].NaN_count", i, ii),
317                                    RD_I_CNT, info);
318                 break;
319             case CF_SEASONAL:
320                 info.u_val =
321                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
322                                  ii].scratch[CDP_hw_seasonal].u_val;
323                 cd = rrd_info_push(cd,
324                                    sprintf_alloc
325                                    ("rra[%d].cdp_prep[%d].seasonal", i, ii),
326                                    RD_I_VAL, info);
327                 break;
328             case CF_DEVSEASONAL:
329                 info.u_val =
330                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
331                                  ii].scratch[CDP_seasonal_deviation].u_val;
332                 cd = rrd_info_push(cd,
333                                    sprintf_alloc
334                                    ("rra[%d].cdp_prep[%d].deviation", i, ii),
335                                    RD_I_VAL, info);
336                 break;
337             case CF_DEVPREDICT:
338                 break;
339             case CF_FAILURES:
340             {
341                 unsigned short j;
342                 char     *violations_array;
343                 char      history[MAX_FAILURES_WINDOW_LEN + 1];
344
345                 violations_array =
346                     (char *) rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
347                                           ii].scratch;
348                 for (j = 0; j < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++j)
349                     history[j] = (violations_array[j] == 1) ? '1' : '0';
350                 history[j] = '\0';
351                 info.u_str = history;
352                 cd = rrd_info_push(cd,
353                                    sprintf_alloc
354                                    ("rra[%d].cdp_prep[%d].history", i, ii),
355                                    RD_I_STR, info);
356             }
357                 break;
358             default:
359                 info.u_val =
360                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
361                                  ii].scratch[CDP_val].u_val;
362                 cd = rrd_info_push(cd,
363                                    sprintf_alloc("rra[%d].cdp_prep[%d].value",
364                                                  i, ii), RD_I_VAL, info);
365                 info.u_cnt =
366                     rrd.cdp_prep[i * rrd.stat_head->ds_cnt +
367                                  ii].scratch[CDP_unkn_pdp_cnt].u_cnt;
368                 cd = rrd_info_push(cd,
369                                    sprintf_alloc
370                                    ("rra[%d].cdp_prep[%d].unknown_datapoints",
371                                     i, ii), RD_I_CNT, info);
372                 break;
373             }
374         }
375     }
376
377     rrd_close(rrd_file);
378   err_free:
379     rrd_free(&rrd);
380     return (data);
381 }
382
383
384 void rrd_info_print(
385     rrd_info_t * data)
386 {
387     while (data) {
388         printf("%s = ", data->key);
389
390         switch (data->type) {
391         case RD_I_VAL:
392             if (isnan(data->value.u_val))
393                 printf("NaN\n");
394             else
395                 printf("%0.10e\n", data->value.u_val);
396             break;
397         case RD_I_CNT:
398             printf("%lu\n", data->value.u_cnt);
399             break;
400         case RD_I_INT:
401             printf("%d\n", data->value.u_int);
402             break;
403         case RD_I_STR:
404             printf("\"%s\"\n", data->value.u_str);
405             break;
406         case RD_I_BLO:
407             printf("BLOB_SIZE:%lu\n", data->value.u_blo.size);
408             fwrite(data->value.u_blo.ptr, data->value.u_blo.size, 1, stdout);
409             break;
410         }
411         data = data->next;
412     }
413 }
414
415 void rrd_info_free(
416     rrd_info_t * data)
417 {
418     rrd_info_t *save;
419
420     while (data) {
421         save = data;
422         if (data->key) {
423             if (data->type == RD_I_STR) {
424                 free(data->value.u_str);
425             }
426             if (data->type == RD_I_BLO) {
427                 free(data->value.u_blo.ptr);
428             }
429             free(data->key);
430         }
431         data = data->next;
432         free(save);
433     }
434 }