* progress in moving all the fileaccess over to a wrapper system that can do fd based...
[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 #include "unused.h"
67 #define MEMBLK 8192
68
69 /* open a database file, return its header and a open filehandle */
70 /* positioned to the first cdp in the first rra */
71
72 rrd_file_t*
73 rrd_open(const char * const file_name, rrd_t *rrd, unsigned rdwr)
74 {
75         int flags = 0;
76         mode_t mode = S_IRUSR;
77         int version, prot = PROT_READ;
78         off_t offset = 0;
79         char *data;
80         struct stat statb;
81         rrd_file_t *rrd_file = malloc(sizeof(rrd_file_t));
82         if (rrd_file == NULL) {
83                 rrd_set_error("allocating rrd_file descriptor for '%s'",
84                         file_name);
85                 return NULL;
86         }
87         memset(rrd_file, 0, sizeof(rrd_file_t));
88         rrd_init(rrd);
89         if (rdwr == RRD_READWRITE) {
90                 mode |= S_IWUSR;
91                 prot |= PROT_WRITE;
92         } else if (rdwr == RRD_CREAT) {
93                 mode |= S_IWUSR;
94                 prot |= PROT_WRITE;
95                 flags |= (O_CREAT|O_TRUNC);
96         }
97 #ifdef O_NONBLOCK
98         flags |= O_NONBLOCK;
99 #endif
100
101         if ((rrd_file->fd = open(file_name, flags, mode)) < 0 ){
102                 rrd_set_error("opening '%s': %s",file_name, rrd_strerror(errno));
103                 return NULL;
104         }
105
106         /* ???: length = lseek(rrd_file->fd, 0, SEEK_END); */
107         /* ??? locking the whole area of the file may overdo it a bit, does it? */
108         if ((fstat(rrd_file->fd, &statb)) < 0) {
109                 rrd_set_error("fstat '%s': %s",file_name, rrd_strerror(errno));
110                 goto out_close;
111         }
112         rrd_file->file_len = statb.st_size;
113
114 #ifdef HAVE_POSIX_FADVISE
115     /* In general we need no read-ahead when dealing with rrd_files.
116        When we stop reading, it is highly unlikely that we start up again.
117        In this manner we actually save time and diskaccess (and buffer cache).
118        Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
119     if (0 != posix_fadvise(rrd_file->fd, 0, 0, POSIX_FADV_RANDOM)) {
120         rrd_set_error("setting POSIX_FADV_RANDOM on '%s': %s",file_name, rrd_strerror(errno));
121         goto out_close;
122      }
123 #endif
124
125 /*
126         if (rdwr == RRD_READWRITE)
127         {
128            if (setvbuf((rrd_file->fd),NULL,_IONBF,2)) {
129                   rrd_set_error("failed to disable the stream buffer\n");
130                   return (-1);
131            }
132         }
133 */
134         data = mmap(0, rrd_file->file_len, prot, MAP_SHARED,
135                 rrd_file->fd, offset);
136
137         /* lets see if the first read worked */
138         if (data == MAP_FAILED) {
139                 rrd_set_error("error mmaping file '%s'",file_name);
140                 goto out_close;
141         }
142         rrd_file->file_start = data;
143 #ifdef USE_MADVISE
144         if (rrd == NULL) { /*XXX: currently not used! */
145                 /* We will read everything in a moment (copying) */
146                 madvise(data, rrd_file->file_len, MADV_WILLNEED|MADV_SEQUENTIAL);
147                 goto out_done;
148         }
149         /* We do not need to read anything in for the moment */
150         madvise(data, rrd_file->file_len, MADV_DONTNEED);
151 #endif
152
153 #ifdef USE_MADVISE
154         /* the stat_head will be needed soonish, so hint accordingly */
155         madvise(data+offset, sizeof(stat_head_t), MADV_WILLNEED);
156 #endif
157
158         rrd->stat_head = (stat_head_t*)(data + offset);
159         offset += sizeof(stat_head_t);
160
161         /* lets do some test if we are on track ... */
162         if (memcmp(rrd->stat_head->cookie,RRD_COOKIE,sizeof(RRD_COOKIE)) != 0) {
163                 rrd_set_error("'%s' is not an RRD file",file_name);
164                 goto out_nullify_head;
165         }
166
167         if (rrd->stat_head->float_cookie != FLOAT_COOKIE){
168                 rrd_set_error("This RRD was created on other architecture");
169                 goto out_nullify_head;
170         }
171
172         version = atoi(rrd->stat_head->version);
173
174         if (version > atoi(RRD_VERSION)) {
175             rrd_set_error("can't handle RRD file version %s",
176                         rrd->stat_head->version);
177             goto out_nullify_head;
178         }
179
180 #ifdef USE_MADVISE
181         /* the ds_def will be needed soonish, so hint accordingly */
182         madvise(data+offset, sizeof(ds_def_t)*rrd->stat_head->ds_cnt, MADV_WILLNEED);
183 #endif
184         rrd->ds_def = (ds_def_t*)(data + offset);
185         offset += sizeof(ds_def_t) * rrd->stat_head->ds_cnt;
186
187 #ifdef USE_MADVISE
188         /* the rra_def will be needed soonish, so hint accordingly */
189         madvise(data+offset, sizeof(rra_def_t)*rrd->stat_head->rra_cnt, MADV_WILLNEED);
190 #endif
191         rrd->rra_def = (rra_def_t*)(data + offset);
192         offset += sizeof(rra_def_t) * rrd->stat_head->rra_cnt;
193
194     /* handle different format for the live_head */
195         if (version < 3) {
196             rrd->live_head = (live_head_t *)malloc(sizeof(live_head_t));
197             if (rrd->live_head == NULL) {
198                 rrd_set_error("live_head_t malloc");
199                 goto out_close;
200             }
201                 memmove(&rrd->live_head->last_up, data+offset, sizeof(long));
202                 rrd->live_head->last_up_usec = 0;
203         } else {
204 #ifdef USE_MADVISE
205                 /* the live_head will be needed soonish, so hint accordingly */
206                 madvise(data+offset, sizeof(live_head_t), MADV_WILLNEED);
207 #endif
208                 rrd->live_head = (live_head_t*)(data + offset);
209                 offset += sizeof(live_head_t);
210         }
211 // This doesn't look like it needs madvise
212         rrd->pdp_prep = (pdp_prep_t*)(data + offset);
213         offset += sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt;
214
215 // This could benefit from madvise()ing
216         rrd->cdp_prep = (cdp_prep_t*)(data + offset);
217         offset += sizeof(cdp_prep_t) *
218                                 (rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
219
220 // This could benefit from madvise()ing
221         rrd->rra_ptr = (rra_ptr_t*)(data + offset);
222         offset += sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt;
223 #ifdef USE_MADVISE
224 out_done:
225 #endif
226         rrd_file->header_len = offset;
227         rrd_file->pos = offset;
228 /* we could close(rrd_file->fd); here, the mapping is still valid anyway */
229         return (rrd_file);
230 out_nullify_head:
231         rrd->stat_head = NULL;
232 out_close:
233         close(rrd_file->fd);
234         return NULL;
235 }
236
237 /* Close a reference to an rrd_file.  */
238 int rrd_close(rrd_file_t* rrd_file) {
239         int ret = 0;
240 #ifdef HAVE_MMAP
241         ret = munmap(rrd_file->file_start, rrd_file->file_len);
242 //      if (ret != 0)
243 //              rrd_set_error("munmap rrd_file");
244 #endif
245         free(rrd_file);
246         rrd_file = NULL;
247         return ret;
248 }
249
250 /* Set position of rrd_file.  */
251 off_t rrd_seek(rrd_file_t* rrd_file, off_t off, int whence) {
252         off_t ret = 0;
253 #ifdef HAVE_MMAP
254         if (whence == SEEK_SET)
255                 rrd_file->pos = off;
256         else if (whence == SEEK_CUR)
257                 rrd_file->pos += off;
258         else if (whence == SEEK_END)
259                 rrd_file->pos = rrd_file->file_len + off;
260 #else
261         ret = lseek(rrd_file->fd, off, whence);
262         if (ret < 0)
263                 rrd_set_error("lseek: %s", rrd_strerror(errno));
264         rrd_file->pos = ret;
265 #endif
266 //XXX: mimic fseek, which returns 0 upon success
267         return ret == -1; //XXX: or just ret to mimic lseek
268 }
269
270 /* Get current position in rrd_file.  */
271 off_t rrd_tell(rrd_file_t* rrd_file) {
272         return rrd_file->pos;
273 }
274
275 /* read count bytes into buffer buf, starting at rrd_file->pos.
276  * Returns the number of bytes read.  */
277 ssize_t rrd_read(rrd_file_t* rrd_file, void*buf, size_t count) {
278 #ifdef HAVE_MMAP
279         char* pos = rrd_file->file_start + rrd_file->pos;
280         buf = memmove(buf, pos, count);
281         return count;
282 #else
283         ssize_t ret;
284         ret = read(rrd_file->fd, buf, count);
285         //XXX: eventually add generic rrd_set_error(""); here
286         return ret;
287 #endif
288 }
289
290 /* write count bytes from buffer buf to the current position
291  * rrd_file->pos of rrd_file->fd.  */
292 ssize_t rrd_write(rrd_file_t * rrd_file, const void*buf, size_t count) {
293         ssize_t ret = count;
294 #ifdef HAVE_MMAP
295         char *off, *new_pos;
296         off = rrd_file->file_start + rrd_file->pos;
297         new_pos = memmove(rrd_file->file_start + rrd_file->pos, buf, count);
298         ret = new_pos - off;
299 #else
300         ret = write(rrd_file->fd, buf, count)
301 #endif
302         return ret;
303 }
304
305 /* flush all data pending to be written to FD.  */
306 void rrd_flush(rrd_file_t* rrd_file)
307 {
308         if (fdatasync(rrd_file->fd) != 0) {
309                 rrd_set_error("flushing fd %d: %s", rrd_file->fd,
310                         rrd_strerror(errno));
311         }
312 }
313
314 void rrd_init(rrd_t *rrd)
315 {
316     rrd->stat_head = NULL;
317     rrd->ds_def = NULL;
318     rrd->rra_def = NULL;
319     rrd->live_head = NULL;
320     rrd->rra_ptr = NULL;
321     rrd->pdp_prep = NULL;
322     rrd->cdp_prep = NULL;
323     rrd->rrd_value = NULL;
324 }
325
326 void rrd_free(rrd_t UNUSED(*rrd))
327 {
328 #ifndef HAVE_MMAP
329     if (atoi(rrd->stat_head->version) < 3)
330             free(rrd->live_head);
331     free(rrd->stat_head);
332     free(rrd->ds_def);
333     free(rrd->rra_def);
334     free(rrd->rra_ptr);
335     free(rrd->pdp_prep);
336     free(rrd->cdp_prep);
337     free(rrd->rrd_value);
338 #endif
339 }
340
341 /* routine used by external libraries to free memory allocated by
342  * rrd library */
343 void rrd_freemem(void *mem)
344 {
345         free(mem);
346 }
347
348 int readfile(const char *file_name, char **buffer, int skipfirst){
349     long writecnt=0,totalcnt = MEMBLK;
350      long offset = 0;
351     FILE *input=NULL;
352     char c ;
353     if ((strcmp("-",file_name) == 0)) { input = stdin; }
354     else {
355       if ((input = fopen(file_name,"rb")) == NULL ){
356         rrd_set_error("opening '%s': %s",file_name,rrd_strerror(errno));
357         return (-1);
358       }
359     }
360     if (skipfirst){
361       do { c = getc(input); offset++; } while (c != '\n' && ! feof(input));
362     }
363     if (strcmp("-",file_name)) {
364       fseek(input, 0, SEEK_END);
365       /* have extra space for detecting EOF without realloc */
366       totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
367       if (totalcnt < MEMBLK)
368         totalcnt = MEMBLK; /* sanitize */
369       fseek(input, offset * sizeof(char), SEEK_SET);
370     }
371     if (((*buffer) = (char *) malloc((totalcnt+4) * sizeof(char))) == NULL) {
372         perror("Allocate Buffer:");
373         exit(1);
374     };
375     do{
376       writecnt += fread((*buffer)+writecnt, 1, (totalcnt - writecnt) * sizeof(char),input);
377       if (writecnt >= totalcnt){
378         totalcnt += MEMBLK;
379         if (((*buffer)=rrd_realloc((*buffer), (totalcnt+4) * sizeof(char)))==NULL){
380             perror("Realloc Buffer:");
381             exit(1);
382         };
383       }
384     } while (! feof(input));
385     (*buffer)[writecnt] = '\0';
386     if (strcmp("-",file_name) != 0) {fclose(input);};
387     return writecnt;
388 }
389