* progress in moving all the fileaccess over to a wrapper system that can do fd based...
[rrdtool.git] / src / rrd_dump.c
1 /*****************************************************************************
2  * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
3  *****************************************************************************
4  * rrd_dump  Display a RRD
5  *****************************************************************************
6  * $Id$
7  * $Log$
8  * Revision 1.7  2004/05/25 20:53:21  oetiker
9  * prevent small leak when resources are exhausted -- Mike Slifcak
10  *
11  * Revision 1.6  2004/05/25 20:51:49  oetiker
12  * Update displayed copyright messages to be consistent. -- Mike Slifcak
13  *
14  * Revision 1.5  2003/02/13 07:05:27  oetiker
15  * Find attached the patch I promised to send to you. Please note that there
16  * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
17  * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
18  * library is identical to librrd, but it contains support code for per-thread
19  * global variables currently used for error information only. This is similar
20  * to how errno per-thread variables are implemented.  librrd_th must be linked
21  * alongside of libpthred
22  *
23  * There is also a new file "THREADS", holding some documentation.
24  *
25  * -- Peter Stamfest <peter@stamfest.at>
26  *
27  * Revision 1.4  2002/02/01 20:34:49  oetiker
28  * fixed version number and date/time
29  *
30  * Revision 1.3  2001/03/10 23:54:39  oetiker
31  * Support for COMPUTE data sources (CDEF data sources). Removes the RPN
32  * parser and calculator from rrd_graph and puts then in a new file,
33  * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some
34  * clean-up of aberrant behavior stuff, including a bug fix.
35  * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format.
36  * -- Jake Brutlag <jakeb@corp.webtv.net>
37  *
38  * Revision 1.2  2001/03/04 13:01:55  oetiker
39  *
40  * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
41  * checkin
42  *
43  *****************************************************************************/
44 #include "rrd_tool.h"
45 #include "rrd_rpncalc.h"
46
47 #if !(defined(NETWARE) || defined(WIN32))
48 extern char *tzname[2];
49 #endif
50
51 int
52 rrd_dump(int argc, char **argv) 
53 {
54     int                 rc;
55
56     if (argc < 2) {
57         rrd_set_error("Not enough arguments");
58         return -1;
59     }
60
61     if (argc == 3)
62     {
63       rc = rrd_dump_r(argv[1], argv[2]);
64     }
65     else
66     {
67       rc = rrd_dump_r(argv[1], NULL);                   
68     }
69
70     return rc;
71 }
72
73 int
74 rrd_dump_r(const char *filename, char *outname)    
75 {   
76     unsigned int i,ii,ix,iii=0;
77     time_t       now;
78     char         somestring[255];
79     rrd_value_t  my_cdp;
80     off_t         rra_base, rra_start, rra_next;
81     rrd_file_t   *rrd_file;
82                 FILE                            *out_file;
83     rrd_t        rrd;
84     rrd_value_t  value;
85     struct tm    tm;
86     rrd_file = rrd_open(filename, &rrd, RRD_READONLY);
87     if (rrd_file == NULL) {
88         rrd_free(&rrd);
89         return(-1);
90     }
91
92     out_file = NULL;
93     if (outname)
94     {
95       if (!(out_file = fopen(outname, "w")))
96       {
97         return (-1);      
98       }
99     }
100     else 
101     {
102       out_file = stdout;
103     }
104                 
105     fputs("<!-- Round Robin Database Dump -->", out_file);
106     fputs("<rrd>", out_file);
107     fprintf(out_file, "\t<version> %s </version>\n",RRD_VERSION);
108     fprintf(out_file, "\t<step> %lu </step> <!-- Seconds -->\n",rrd.stat_head->pdp_step);
109 #if HAVE_STRFTIME
110     localtime_r(&rrd.live_head->last_up, &tm);
111     strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z",
112              &tm);
113 #else
114 # error "Need strftime"
115 #endif
116     fprintf(out_file, "\t<lastupdate> %ld </lastupdate> <!-- %s -->\n\n",
117            rrd.live_head->last_up,somestring);
118     for(i=0;i<rrd.stat_head->ds_cnt;i++){
119            fprintf(out_file, "\t<ds>\n");
120            fprintf(out_file, "\t\t<name> %s </name>\n",rrd.ds_def[i].ds_nam);
121            fprintf(out_file, "\t\t<type> %s </type>\n",rrd.ds_def[i].dst);
122        if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
123           fprintf(out_file, "\t\t<minimal_heartbeat> %lu </minimal_heartbeat>\n",rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
124               if (isnan(rrd.ds_def[i].par[DS_min_val].u_val)){
125                   fprintf(out_file, "\t\t<min> NaN </min>\n");
126               } else {
127                   fprintf(out_file, "\t\t<min> %0.10e </min>\n",rrd.ds_def[i].par[DS_min_val].u_val);
128               }
129               if (isnan(rrd.ds_def[i].par[DS_max_val].u_val)){
130                   fprintf(out_file, "\t\t<max> NaN </max>\n");
131               } else {
132                   fprintf(out_file, "\t\t<max> %0.10e </max>\n",rrd.ds_def[i].par[DS_max_val].u_val);
133               }
134        } else { /* DST_CDEF */
135               char *str=NULL;
136               rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),rrd.ds_def,&str);
137               fprintf(out_file, "\t\t<cdef> %s </cdef>\n", str);
138               free(str);
139            }
140            fprintf(out_file, "\n\t\t<!-- PDP Status -->\n");
141            fprintf(out_file, "\t\t<last_ds> %s </last_ds>\n",rrd.pdp_prep[i].last_ds);
142            if (isnan(rrd.pdp_prep[i].scratch[PDP_val].u_val)){
143               fprintf(out_file, "\t\t<value> NaN </value>\n");
144            } else {
145               fprintf(out_file, "\t\t<value> %0.10e </value>\n",rrd.pdp_prep[i].scratch[PDP_val].u_val);
146            }
147               fprintf(out_file, "\t\t<unknown_sec> %lu </unknown_sec>\n",
148                       rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
149         
150               fprintf(out_file, "\t</ds>\n\n");
151        }
152
153     fputs("<!-- Round Robin Archives -->", out_file);
154
155     rra_base = rrd_file->header_len;
156     rra_next = rra_base;
157
158     for(i=0;i<rrd.stat_head->rra_cnt;i++){
159         
160         long timer=0;
161         rra_start= rra_next;
162         rra_next +=  ( rrd.stat_head->ds_cnt
163                       * rrd.rra_def[i].row_cnt
164                       * sizeof(rrd_value_t));
165         fprintf(out_file, "\t<rra>\n");
166         fprintf(out_file, "\t\t<cf> %s </cf>\n",rrd.rra_def[i].cf_nam);
167         fprintf(out_file, "\t\t<pdp_per_row> %lu </pdp_per_row> <!-- %lu seconds -->\n\n",
168                rrd.rra_def[i].pdp_cnt, rrd.rra_def[i].pdp_cnt
169                *rrd.stat_head->pdp_step);
170         /* support for RRA parameters */
171         fprintf(out_file, "\t\t<params>\n");
172         switch(cf_conv(rrd.rra_def[i].cf_nam)) {
173         case CF_HWPREDICT:
174            fprintf(out_file, "\t\t<hw_alpha> %0.10e </hw_alpha>\n", 
175                   rrd.rra_def[i].par[RRA_hw_alpha].u_val);
176            fprintf(out_file, "\t\t<hw_beta> %0.10e </hw_beta>\n", 
177                   rrd.rra_def[i].par[RRA_hw_beta].u_val);
178            fprintf(out_file, "\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
179                   rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
180            break;
181         case CF_SEASONAL:
182         case CF_DEVSEASONAL:
183            fprintf(out_file, "\t\t<seasonal_gamma> %0.10e </seasonal_gamma>\n", 
184                   rrd.rra_def[i].par[RRA_seasonal_gamma].u_val);
185            fprintf(out_file, "\t\t<seasonal_smooth_idx> %lu </seasonal_smooth_idx>\n",
186                   rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
187            fprintf(out_file, "\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
188                   rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
189            break;
190         case CF_FAILURES:
191            fprintf(out_file, "\t\t<delta_pos> %0.10e </delta_pos>\n", 
192                   rrd.rra_def[i].par[RRA_delta_pos].u_val);
193            fprintf(out_file, "\t\t<delta_neg> %0.10e </delta_neg>\n", 
194                   rrd.rra_def[i].par[RRA_delta_neg].u_val);
195            fprintf(out_file, "\t\t<window_len> %lu </window_len>\n",
196                   rrd.rra_def[i].par[RRA_window_len].u_cnt);
197            fprintf(out_file, "\t\t<failure_threshold> %lu </failure_threshold>\n",
198                   rrd.rra_def[i].par[RRA_failure_threshold].u_cnt);
199                   /* fall thru */
200         case CF_DEVPREDICT:
201            fprintf(out_file, "\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
202                   rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
203            break;
204         case CF_AVERAGE:
205         case CF_MAXIMUM:
206         case CF_MINIMUM:
207         case CF_LAST:
208         default:
209            fprintf(out_file, "\t\t<xff> %0.10e </xff>\n", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
210            break;
211         }
212         fprintf(out_file, "\t\t</params>\n");
213         fprintf(out_file, "\t\t<cdp_prep>\n");
214         for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
215                 unsigned long ivalue;
216                 fprintf(out_file, "\t\t\t<ds>\n");
217                 /* support for exporting all CDP parameters */
218                 /* parameters common to all CFs */
219                     /* primary_val and secondary_val do not need to be saved between updates
220                          * so strictly speaking they could be omitted.
221                          * However, they can be useful for diagnostic purposes, so are included here. */
222                 value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt
223                            +ii].scratch[CDP_primary_val].u_val;
224                         if (isnan(value)) {
225                            fprintf(out_file, "\t\t\t<primary_value> NaN </primary_value>\n");
226                         } else {
227                            fprintf(out_file, "\t\t\t<primary_value> %0.10e </primary_value>\n", value);
228                         }
229                 value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_secondary_val].u_val;
230                         if (isnan(value)) {
231                            fprintf(out_file, "\t\t\t<secondary_value> NaN </secondary_value>\n");
232                         } else {
233                            fprintf(out_file, "\t\t\t<secondary_value> %0.10e </secondary_value>\n", value);
234                         }
235                 switch(cf_conv(rrd.rra_def[i].cf_nam)) {
236                 case CF_HWPREDICT:
237                 value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_intercept].u_val;
238                         if (isnan(value)) {
239                            fprintf(out_file, "\t\t\t<intercept> NaN </intercept>\n");
240                         } else {
241                            fprintf(out_file, "\t\t\t<intercept> %0.10e </intercept>\n", value);
242                         }
243                 value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_last_intercept].u_val;
244                         if (isnan(value)) {
245                            fprintf(out_file, "\t\t\t<last_intercept> NaN </last_intercept>\n");
246                         } else {
247                            fprintf(out_file, "\t\t\t<last_intercept> %0.10e </last_intercept>\n", value);
248                         }
249                 value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_slope].u_val;
250                         if (isnan(value)) {
251                            fprintf(out_file, "\t\t\t<slope> NaN </slope>\n");
252                         } else {
253                            fprintf(out_file, "\t\t\t<slope> %0.10e </slope>\n", value);
254                         }
255                 value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_last_slope].u_val;
256                         if (isnan(value)) {
257                            fprintf(out_file, "\t\t\t<last_slope> NaN </last_slope>\n");
258                         } else {
259                            fprintf(out_file, "\t\t\t<last_slope> %0.10e </last_slope>\n", value);
260                         }
261                         ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_null_count].u_cnt;
262                         fprintf(out_file, "\t\t\t<nan_count> %lu </nan_count>\n", ivalue);
263                         ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_last_null_count].u_cnt;
264                         fprintf(out_file, "\t\t\t<last_nan_count> %lu </last_nan_count>\n", ivalue);
265                         break;
266                 case CF_SEASONAL:
267                 case CF_DEVSEASONAL:
268                 value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_seasonal].u_val;
269                         if (isnan(value)) {
270                            fprintf(out_file, "\t\t\t<seasonal> NaN </seasonal>\n");
271                         } else {
272                            fprintf(out_file, "\t\t\t<seasonal> %0.10e </seasonal>\n", value);
273                         }
274                 value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_last_seasonal].u_val;
275                         if (isnan(value)) {
276                            fprintf(out_file, "\t\t\t<last_seasonal> NaN </last_seasonal>\n");
277                         } else {
278                            fprintf(out_file, "\t\t\t<last_seasonal> %0.10e </last_seasonal>\n", value);
279                         }
280                 ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_init_seasonal].u_cnt;
281                         fprintf(out_file, "\t\t\t<init_flag> %lu </init_flag>\n", ivalue);
282                         break;
283                 case CF_DEVPREDICT:
284                         break;
285                 case CF_FAILURES:
286                     {
287             unsigned short vidx;
288                         char *violations_array = (char *) ((void*) 
289                            rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch);
290                         fprintf(out_file, "\t\t\t<history> ");
291                         for (vidx = 0; vidx < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++vidx)
292                         {
293                                 fprintf(out_file, "%d",violations_array[vidx]);
294                         }
295                         fprintf(out_file, " </history>\n");
296                         }
297                         break;
298                 case CF_AVERAGE:
299                 case CF_MAXIMUM:
300                 case CF_MINIMUM:
301                 case CF_LAST:
302                 default:
303                 value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
304                         if (isnan(value)) {
305                            fprintf(out_file, "\t\t\t<value> NaN </value>\n");
306                         } else {
307                            fprintf(out_file, "\t\t\t<value> %0.10e </value>\n", value);
308                         }
309                     fprintf(out_file, "\t\t\t<unknown_datapoints> %lu </unknown_datapoints>\n",
310                        rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt);
311                         break;
312                 }
313         fprintf(out_file, "\t\t\t</ds>\n");      
314     }
315         fprintf(out_file, "\t\t</cdp_prep>\n");
316
317         fprintf(out_file, "\t\t<database>\n");
318         rrd_seek(rrd_file,(rra_start
319                        +(rrd.rra_ptr[i].cur_row+1)
320                        * rrd.stat_head->ds_cnt
321                        * sizeof(rrd_value_t)),SEEK_SET);
322         timer = - (rrd.rra_def[i].row_cnt-1);
323         ii=rrd.rra_ptr[i].cur_row;
324         for(ix=0;ix<rrd.rra_def[i].row_cnt;ix++){           
325             ii++;
326             if (ii>=rrd.rra_def[i].row_cnt) {
327                 rrd_seek(rrd_file,rra_start,SEEK_SET);
328                 ii=0; /* wrap if max row cnt is reached */
329             }
330             now = (rrd.live_head->last_up 
331                    - rrd.live_head->last_up 
332                    % (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step)) 
333                 + (timer*rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step);
334
335             timer++;
336 #if HAVE_STRFTIME
337             localtime_r(&now, &tm);
338             strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", &tm);
339 #else
340 # error "Need strftime"
341 #endif
342             fprintf(out_file, "\t\t\t<!-- %s / %d --> <row>",somestring,(int)now);
343             for(iii=0;iii<rrd.stat_head->ds_cnt;iii++){
344                 rrd_read(rrd_file, &my_cdp,sizeof(rrd_value_t)*1);
345                 if (isnan(my_cdp)){
346                   fprintf(out_file, "<v> NaN </v>");
347                 } else {
348                   fprintf(out_file, "<v> %0.10e </v>",my_cdp);
349                 };
350             }
351             fprintf(out_file, "</row>\n");
352         }
353         fprintf(out_file, "\t\t</database>\n\t</rra>\n");
354
355     }
356     fprintf(out_file, "</rrd>\n");
357     rrd_free(&rrd);
358     close(rrd_file->fd);
359     if (out_file != stdout)
360     {
361       fclose(out_file);
362     }
363     return(0);
364 }
365