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