new trunk based on current 1.2
[rrdtool.git] / src / rrd_open.c
1 /*****************************************************************************
2  * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
3  *****************************************************************************
4  * rrd_open.c  Open an RRD File
5  *****************************************************************************
6  * $Id$
7  * $Log$
8  * Revision 1.10  2004/05/26 22:11:12  oetiker
9  * reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
10  *
11  * Revision 1.9  2003/04/29 21:56:49  oetiker
12  * readline in rrd_open.c reads the file in 8 KB blocks, and calls realloc for
13  * each block. realloc is very slow in Mac OS X for huge blocks, e.g. when
14  * restoring databases from huge xml files. This patch finds the size of the
15  * file, and starts out with malloc'ing the full size.
16  * -- Peter Speck <speck@ruc.dk>
17  *
18  * Revision 1.8  2003/04/11 19:43:44  oetiker
19  * New special value COUNT which allows calculations based on the position of a
20  * value within a data set. Bug fix in rrd_rpncalc.c. PREV returned erroneus
21  * value for the second value. Bug fix in rrd_restore.c. Bug causing seek error
22  * when accesing an RRD restored from an xml that holds an RRD version <3.
23  * --  Ruben Justo <ruben@ainek.com>
24  *
25  * Revision 1.7  2003/03/31 21:22:12  oetiker
26  * enables RRDtool updates with microsecond or in case of windows millisecond
27  * precision. This is needed to reduce time measurement error when archive step
28  * is small. (<30s) --  Sasha Mikheev <sasha@avalon-net.co.il>
29  *
30  * Revision 1.6  2003/02/13 07:05:27  oetiker
31  * Find attached the patch I promised to send to you. Please note that there
32  * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
33  * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
34  * library is identical to librrd, but it contains support code for per-thread
35  * global variables currently used for error information only. This is similar
36  * to how errno per-thread variables are implemented.  librrd_th must be linked
37  * alongside of libpthred
38  *
39  * There is also a new file "THREADS", holding some documentation.
40  *
41  * -- Peter Stamfest <peter@stamfest.at>
42  *
43  * Revision 1.5  2002/06/20 00:21:03  jake
44  * More Win32 build changes; thanks to Kerry Calvert.
45  *
46  * Revision 1.4  2002/02/01 20:34:49  oetiker
47  * fixed version number and date/time
48  *
49  * Revision 1.3  2001/03/04 13:01:55  oetiker
50  * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
51  * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
52  * This is backwards compatible! But new files using the Aberrant stuff are not readable
53  * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
54  * -- Jake Brutlag <jakeb@corp.webtv.net>
55  *
56  * Revision 1.2  2001/03/04 10:29:20  oetiker
57  * fixed filedescriptor leak
58  * -- Mike Franusich <mike@franusich.com>
59  *
60  * Revision 1.1.1.1  2001/02/25 22:25:05  oetiker
61  * checkin
62  *
63  *****************************************************************************/
64
65 #include "rrd_tool.h"
66 #define MEMBLK 8192
67
68 /* open a database file, return its header and a open filehandle */
69 /* positioned to the first cdp in the first rra */
70
71 int
72 rrd_open(const char *file_name, FILE **in_file, rrd_t *rrd, int rdwr)    
73 {
74
75     
76     char *mode = NULL;
77     int version;
78     
79     rrd_init(rrd);
80     if (rdwr == RRD_READONLY) {
81         mode = "rb";
82     } else {
83         mode = "rb+";
84     }
85     
86     if (((*in_file) = fopen(file_name,mode)) == NULL ){
87         rrd_set_error("opening '%s': %s",file_name, rrd_strerror(errno));
88         return (-1);
89     }
90
91 #ifdef HAVE_POSIX_FADVISE
92     /* In general we need no read-ahead when dealing with rrd_files.
93        When we stop reading, it is highly unlikely that we start up again.
94        In this manner we actually save time and diskaccess (and buffer cache).
95        Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */       
96     if (0 != posix_fadvise(fileno(*in_file), 0, 0, POSIX_FADV_RANDOM)) {
97         rrd_set_error("setting POSIX_FADV_RANDOM on '%s': %s",file_name, rrd_strerror(errno));
98         fclose(*in_file);
99         return(-1);
100      }    
101 #endif
102
103 /*
104         if (rdwr == RRD_READWRITE)
105         {
106            if (setvbuf((*in_file),NULL,_IONBF,2)) {
107                   rrd_set_error("failed to disable the stream buffer\n");
108                   return (-1);
109            }
110         }
111 */
112     
113 #define MYFREAD(MYVAR,MYVART,MYCNT) \
114     if ((MYVAR = malloc(sizeof(MYVART) * MYCNT)) == NULL) {\
115         rrd_set_error("" #MYVAR " malloc"); \
116         fclose(*in_file); \
117         return (-1); } \
118     fread(MYVAR,sizeof(MYVART),MYCNT, *in_file); 
119
120
121     MYFREAD(rrd->stat_head, stat_head_t,  1)
122     /* lets see if the first read worked */
123     if (ferror( *in_file ) || feof(*in_file)) {
124         rrd_set_error("reading the cookie off %s faild",file_name);
125         fclose(*in_file);
126         return(-1);
127     }        
128
129         /* lets do some test if we are on track ... */
130         if (strncmp(rrd->stat_head->cookie,RRD_COOKIE,4) != 0){
131             rrd_set_error("'%s' is not an RRD file",file_name);
132             free(rrd->stat_head);
133             rrd->stat_head = NULL; 
134             fclose(*in_file);
135             return(-1);}
136
137         if (rrd->stat_head->float_cookie != FLOAT_COOKIE){
138             rrd_set_error("This RRD was created on other architecture");
139             free(rrd->stat_head);
140             rrd->stat_head = NULL; 
141             fclose(*in_file);
142             return(-1);}
143
144     version = atoi(rrd->stat_head->version);
145
146         if (version > atoi(RRD_VERSION)){
147             rrd_set_error("can't handle RRD file version %s",
148                         rrd->stat_head->version);
149             free(rrd->stat_head);
150             rrd->stat_head = NULL; 
151             fclose(*in_file);
152             return(-1);}
153
154
155     MYFREAD(rrd->ds_def,    ds_def_t,     rrd->stat_head->ds_cnt)
156     MYFREAD(rrd->rra_def,   rra_def_t,    rrd->stat_head->rra_cnt)
157     /* handle different format for the live_head */
158     if(version < 3) {
159             rrd->live_head = (live_head_t *)malloc(sizeof(live_head_t));
160             if(rrd->live_head == NULL) {
161                 rrd_set_error("live_head_t malloc");
162                 fclose(*in_file); 
163                 return (-1);
164             }
165                 fread(&rrd->live_head->last_up, sizeof(long), 1, *in_file); 
166                 rrd->live_head->last_up_usec = 0;
167     }
168     else {
169             MYFREAD(rrd->live_head, live_head_t, 1)
170     }
171     MYFREAD(rrd->pdp_prep,  pdp_prep_t,   rrd->stat_head->ds_cnt)
172     MYFREAD(rrd->cdp_prep,  cdp_prep_t,   (rrd->stat_head->rra_cnt
173                                              * rrd->stat_head->ds_cnt))
174     MYFREAD(rrd->rra_ptr,   rra_ptr_t,    rrd->stat_head->rra_cnt)
175 #undef MYFREAD
176
177     return(0);
178 }
179
180 void rrd_init(rrd_t *rrd)
181 {
182     rrd->stat_head = NULL;
183     rrd->ds_def = NULL;
184     rrd->rra_def = NULL;
185     rrd->live_head = NULL;
186     rrd->rra_ptr = NULL;
187     rrd->pdp_prep = NULL;
188     rrd->cdp_prep = NULL;
189     rrd->rrd_value = NULL;
190 }
191
192 void rrd_free(rrd_t *rrd)
193 {
194     if (rrd->stat_head) free(rrd->stat_head);
195     if (rrd->ds_def) free(rrd->ds_def);
196     if (rrd->rra_def) free(rrd->rra_def);
197     if (rrd->live_head) free(rrd->live_head);
198     if (rrd->rra_ptr) free(rrd->rra_ptr);
199     if (rrd->pdp_prep) free(rrd->pdp_prep);
200     if (rrd->cdp_prep) free(rrd->cdp_prep);
201     if (rrd->rrd_value) free(rrd->rrd_value);
202 }
203
204 /* routine used by external libraries to free memory allocated by
205  * rrd library */
206 void rrd_freemem(void *mem)
207 {
208
209     if (mem) free(mem);
210 }
211
212 int readfile(const char *file_name, char **buffer, int skipfirst){
213     long writecnt=0,totalcnt = MEMBLK;
214      long offset = 0;
215     FILE *input=NULL;
216     char c ;
217     if ((strcmp("-",file_name) == 0)) { input = stdin; }
218     else {
219       if ((input = fopen(file_name,"rb")) == NULL ){
220         rrd_set_error("opening '%s': %s",file_name,rrd_strerror(errno));
221         return (-1);
222       }
223     }
224     if (skipfirst){
225       do { c = getc(input); offset++; } while (c != '\n' && ! feof(input));
226     }
227     if (strcmp("-",file_name)) {
228       fseek(input, 0, SEEK_END);
229       /* have extra space for detecting EOF without realloc */
230       totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
231       if (totalcnt < MEMBLK)
232         totalcnt = MEMBLK; /* sanitize */
233       fseek(input, offset * sizeof(char), SEEK_SET);
234     }
235     if (((*buffer) = (char *) malloc((totalcnt+4) * sizeof(char))) == NULL) {
236         perror("Allocate Buffer:");
237         exit(1);
238     };
239     do{
240       writecnt += fread((*buffer)+writecnt, 1, (totalcnt - writecnt) * sizeof(char),input);
241       if (writecnt >= totalcnt){
242         totalcnt += MEMBLK;
243         if (((*buffer)=rrd_realloc((*buffer), (totalcnt+4) * sizeof(char)))==NULL){
244             perror("Realloc Buffer:");
245             exit(1);
246         };
247       }
248     } while (! feof(input));
249     (*buffer)[writecnt] = '\0';
250     if (strcmp("-",file_name) != 0) {fclose(input);};
251     return writecnt;
252 }
253
254