a83031a99cf40694129157fcee55400594e74202
[rrdtool.git] / src / rrd_open.c
1 /*****************************************************************************
2  * RRDtool 1.3.2  Copyright by Tobi Oetiker, 1997-2008
3  *****************************************************************************
4  * rrd_open.c  Open an RRD File
5  *****************************************************************************
6  * $Id$
7  *****************************************************************************/
8
9 #include "rrd_tool.h"
10 #include "unused.h"
11 #define MEMBLK 8192
12
13 /* DEBUG 2 prints information obtained via mincore(2) */
14 #define DEBUG 1
15 /* do not calculate exact madvise hints but assume 1 page for headers and
16  * set DONTNEED for the rest, which is assumed to be data */
17 /* Avoid calling madvise on areas that were already hinted. May be benefical if
18  * your syscalls are very slow */
19
20 #ifdef HAVE_MMAP
21 /* the cast to void* is there to avoid this warning seen on ia64 with certain
22    versions of gcc: 'cast increases required alignment of target type'
23 */
24 #define __rrd_read(dst, dst_t, cnt) { \
25         size_t wanted = sizeof(dst_t)*(cnt); \
26         if (offset + wanted > rrd_file->file_len) { \
27                 rrd_set_error("reached EOF while loading header " #dst); \
28                 goto out_nullify_head; \
29         } \
30         (dst) = (dst_t*)(void*) (data + offset); \
31         offset += wanted; \
32     }
33 #else
34 #define __rrd_read(dst, dst_t, cnt) { \
35         size_t wanted = sizeof(dst_t)*(cnt); \
36         size_t got; \
37         if ((dst = malloc(wanted)) == NULL) { \
38                 rrd_set_error(#dst " malloc"); \
39                 goto out_nullify_head; \
40         } \
41         got = read (rrd_simple_file->fd, dst, wanted); \
42         if (got != wanted) { \
43                 rrd_set_error("short read while reading header " #dst); \
44                 goto out_nullify_head; \
45         } \
46         offset += got; \
47     }
48 #endif
49
50 /* get the address of the start of this page */
51 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
52 #ifndef PAGE_START
53 #define PAGE_START(addr) ((addr)&(~(_page_size-1)))
54 #endif
55 #endif
56
57 /* Open a database file, return its header and an open filehandle,
58  * positioned to the first cdp in the first rra.
59  * In the error path of rrd_open, only rrd_free(&rrd) has to be called
60  * before returning an error. Do not call rrd_close upon failure of rrd_open.
61  * If creating a new file, the parameter rrd must be initialised with
62  * details of the file content.
63  * If opening an existing file, then use rrd must be initialised by
64  * rrd_init(rrd) prior to invoking rrd_open
65  */
66
67 rrd_file_t *rrd_open(
68     const char *const file_name,
69     rrd_t *rrd,
70     unsigned rdwr)
71 {
72     int i;
73     int       flags = 0;
74     int       version;
75
76 #ifdef HAVE_MMAP
77     ssize_t   _page_size = sysconf(_SC_PAGESIZE);
78     char     *data = MAP_FAILED;
79 #endif
80     off_t     offset = 0;
81     struct stat statb;
82     rrd_file_t *rrd_file = NULL;
83     rrd_simple_file_t *rrd_simple_file = NULL;
84     off_t     newfile_size = 0;
85     off_t header_len, value_cnt, data_len;
86
87     /* Are we creating a new file? */
88     if((rdwr & RRD_CREAT) && (rrd->stat_head != NULL))
89     {
90         header_len = \
91           sizeof(stat_head_t) + \
92           sizeof(ds_def_t) * rrd->stat_head->ds_cnt + \
93           sizeof(rra_def_t) * rrd->stat_head->rra_cnt + \
94           sizeof(time_t) + \
95           sizeof(live_head_t) + \
96           sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt + \
97           sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt + \
98           sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt;
99
100         value_cnt = 0;
101         for (i = 0; i < rrd->stat_head->rra_cnt; i++)
102             value_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[i].row_cnt;
103
104         data_len = sizeof(rrd_value_t) * value_cnt;
105
106         newfile_size = header_len + data_len;
107     }
108     
109     rrd_file = malloc(sizeof(rrd_file_t));
110     if (rrd_file == NULL) {
111         rrd_set_error("allocating rrd_file descriptor for '%s'", file_name);
112         return NULL;
113     }
114     memset(rrd_file, 0, sizeof(rrd_file_t));
115
116     rrd_file->pvt = malloc(sizeof(rrd_simple_file_t));
117     if(rrd_file->pvt == NULL) {
118         rrd_set_error("allocating rrd_simple_file for '%s'", file_name);
119         return NULL;
120     }
121     memset(rrd_file->pvt, 0, sizeof(rrd_simple_file_t));
122     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
123
124 #ifdef DEBUG
125     if ((rdwr & (RRD_READONLY | RRD_READWRITE)) ==
126         (RRD_READONLY | RRD_READWRITE)) {
127         /* Both READONLY and READWRITE were given, which is invalid.  */
128         rrd_set_error("in read/write request mask");
129         exit(-1);
130     }
131 #endif
132
133 #ifdef HAVE_MMAP
134     rrd_simple_file->mm_prot = PROT_READ;
135     rrd_simple_file->mm_flags = 0;
136 #endif
137
138     if (rdwr & RRD_READONLY) {
139         flags |= O_RDONLY;
140 #ifdef HAVE_MMAP
141         rrd_simple_file->mm_flags = MAP_PRIVATE;
142 # ifdef MAP_NORESERVE
143         rrd_simple_file->mm_flags |= MAP_NORESERVE;  /* readonly, so no swap backing needed */
144 # endif
145 #endif
146     } else {
147         if (rdwr & RRD_READWRITE) {
148             flags |= O_RDWR;
149 #ifdef HAVE_MMAP
150             rrd_simple_file->mm_flags = MAP_SHARED;
151             rrd_simple_file->mm_prot |= PROT_WRITE;
152 #endif
153         }
154         if (rdwr & RRD_CREAT) {
155             flags |= (O_CREAT | O_TRUNC);
156         }
157     }
158     if (rdwr & RRD_READAHEAD) {
159 #ifdef MAP_POPULATE
160         rrd_simple_file->mm_flags |= MAP_POPULATE;   /* populate ptes and data */
161 #endif
162 #if defined MAP_NONBLOCK
163         rrd_simple_file->mm_flags |= MAP_NONBLOCK;   /* just populate ptes */
164 #endif
165     }
166 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
167     flags |= O_BINARY;
168 #endif
169
170     if ((rrd_simple_file->fd = open(file_name, flags, 0666)) < 0) {
171         rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
172         goto out_free;
173     }
174
175     /* Better try to avoid seeks as much as possible. stat may be heavy but
176      * many concurrent seeks are even worse.  */
177     if (newfile_size == 0 && ((fstat(rrd_simple_file->fd, &statb)) < 0)) {
178         rrd_set_error("fstat '%s': %s", file_name, rrd_strerror(errno));
179         goto out_close;
180     }
181     if (newfile_size == 0) {
182         rrd_file->file_len = statb.st_size;
183     } else {
184         rrd_file->file_len = newfile_size;
185         lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET);
186         write(rrd_simple_file->fd, "\0", 1);   /* poke */
187         lseek(rrd_simple_file->fd, 0, SEEK_SET);
188     }
189 #ifdef HAVE_POSIX_FADVISE
190     /* In general we need no read-ahead when dealing with rrd_files.
191        When we stop reading, it is highly unlikely that we start up again.
192        In this manner we actually save time and diskaccess (and buffer cache).
193        Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
194     posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
195 #endif
196
197 /*
198         if (rdwr & RRD_READWRITE)
199         {
200            if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
201                   rrd_set_error("failed to disable the stream buffer\n");
202                   return (-1);
203            }
204         }
205 */
206
207 #ifdef HAVE_MMAP
208     data = mmap(0, rrd_file->file_len, 
209         rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
210         rrd_simple_file->fd, offset);
211
212     /* lets see if the first read worked */
213     if (data == MAP_FAILED) {
214         rrd_set_error("mmaping file '%s': %s", file_name,
215                       rrd_strerror(errno));
216         goto out_close;
217     }
218     rrd_simple_file->file_start = data;
219     if (rdwr & RRD_CREAT) {
220         memset(data, DNAN, newfile_size - 1);
221         goto out_done;
222     }
223 #endif
224     if (rdwr & RRD_CREAT)
225         goto out_done;
226 #ifdef USE_MADVISE
227     if (rdwr & RRD_COPY) {
228         /* We will read everything in a moment (copying) */
229         madvise(data, rrd_file->file_len, MADV_WILLNEED | MADV_SEQUENTIAL);
230     } else {
231         /* We do not need to read anything in for the moment */
232         madvise(data, rrd_file->file_len, MADV_RANDOM);
233         /* the stat_head will be needed soonish, so hint accordingly */
234         madvise(data, sizeof(stat_head_t), MADV_WILLNEED | MADV_RANDOM);
235     }
236 #endif
237
238     __rrd_read(rrd->stat_head, stat_head_t,
239                1);
240
241     /* lets do some test if we are on track ... */
242     if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
243         rrd_set_error("'%s' is not an RRD file", file_name);
244         goto out_nullify_head;
245     }
246
247     if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
248         rrd_set_error("This RRD was created on another architecture");
249         goto out_nullify_head;
250     }
251
252     version = atoi(rrd->stat_head->version);
253
254     if (version > atoi(RRD_VERSION)) {
255         rrd_set_error("can't handle RRD file version %s",
256                       rrd->stat_head->version);
257         goto out_nullify_head;
258     }
259 #if defined USE_MADVISE
260     /* the ds_def will be needed soonish, so hint accordingly */
261     madvise(data + PAGE_START(offset),
262             sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
263 #endif
264     __rrd_read(rrd->ds_def, ds_def_t,
265                rrd->stat_head->ds_cnt);
266
267 #if defined USE_MADVISE
268     /* the rra_def will be needed soonish, so hint accordingly */
269     madvise(data + PAGE_START(offset),
270             sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
271 #endif
272     __rrd_read(rrd->rra_def, rra_def_t,
273                rrd->stat_head->rra_cnt);
274
275     /* handle different format for the live_head */
276     if (version < 3) {
277         rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
278         if (rrd->live_head == NULL) {
279             rrd_set_error("live_head_t malloc");
280             goto out_close;
281         }
282 #if defined USE_MADVISE
283         /* the live_head will be needed soonish, so hint accordingly */
284         madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
285 #endif
286         __rrd_read(rrd->legacy_last_up, time_t,
287                    1);
288
289         rrd->live_head->last_up = *rrd->legacy_last_up;
290         rrd->live_head->last_up_usec = 0;
291     } else {
292 #if defined USE_MADVISE
293         /* the live_head will be needed soonish, so hint accordingly */
294         madvise(data + PAGE_START(offset),
295                 sizeof(live_head_t), MADV_WILLNEED);
296 #endif
297         __rrd_read(rrd->live_head, live_head_t,
298                    1);
299     }
300     __rrd_read(rrd->pdp_prep, pdp_prep_t,
301                rrd->stat_head->ds_cnt);
302     __rrd_read(rrd->cdp_prep, cdp_prep_t,
303                rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
304     __rrd_read(rrd->rra_ptr, rra_ptr_t,
305                rrd->stat_head->rra_cnt);
306
307     rrd_file->header_len = offset;
308     rrd_file->pos = offset;
309
310     {
311       unsigned long row_cnt = 0;
312       unsigned long i;
313
314       for (i=0; i<rrd->stat_head->rra_cnt; i++)
315         row_cnt += rrd->rra_def[i].row_cnt;
316
317       off_t correct_len = rrd_file->header_len +
318         sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
319
320       if (correct_len > rrd_file->file_len)
321       {
322         rrd_set_error("'%s' is too small (should be %ld bytes)",
323                       file_name, (long long) correct_len);
324         goto out_nullify_head;
325       }
326     }
327
328   out_done:
329     return (rrd_file);
330   out_nullify_head:
331     rrd->stat_head = NULL;
332   out_close:
333 #ifdef HAVE_MMAP
334     if (data != MAP_FAILED)
335       munmap(data, rrd_file->file_len);
336 #endif
337     close(rrd_simple_file->fd);
338   out_free:
339     free(rrd_file->pvt);
340     free(rrd_file);
341     return NULL;
342 }
343
344
345 #if defined DEBUG && DEBUG > 1
346 /* Print list of in-core pages of a the current rrd_file.  */
347 static
348 void mincore_print(
349     rrd_file_t *rrd_file,
350     char *mark)
351 {
352     rrd_simple_file_t *rrd_simple_file;
353     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
354 #ifdef HAVE_MMAP
355     /* pretty print blocks in core */
356     off_t     off;
357     unsigned char *vec;
358     ssize_t   _page_size = sysconf(_SC_PAGESIZE);
359
360     off = rrd_file->file_len +
361         ((rrd_file->file_len + _page_size - 1) / _page_size);
362     vec = malloc(off);
363     if (vec != NULL) {
364         memset(vec, 0, off);
365         if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
366             int       prev;
367             unsigned  is_in = 0, was_in = 0;
368
369             for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
370                 is_in = vec[off] & 1;   /* if lsb set then is core resident */
371                 if (off == 0)
372                     was_in = is_in;
373                 if (was_in != is_in) {
374                     fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
375                             was_in ? "" : "not ", vec + prev, off - prev);
376                     was_in = is_in;
377                     prev = off;
378                 }
379             }
380             fprintf(stderr,
381                     "%s: %sin core: %p len %ld\n", mark,
382                     was_in ? "" : "not ", vec + prev, off - prev);
383         } else
384             fprintf(stderr, "mincore: %s", rrd_strerror(errno));
385     }
386 #else
387     fprintf(stderr, "sorry mincore only works with mmap");
388 #endif
389 }
390 #endif                          /* defined DEBUG && DEBUG > 1 */
391
392 /*
393  * get exclusive lock to whole file.
394  * lock gets removed when we close the file
395  *
396  * returns 0 on success
397  */
398 int rrd_lock(
399     rrd_file_t *rrd_file)
400 {
401     int       rcstat;
402     rrd_simple_file_t *rrd_simple_file;
403     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
404
405     {
406 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
407         struct _stat st;
408
409         if (_fstat(rrd_simple_file->fd, &st) == 0) {
410             rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
411         } else {
412             rcstat = -1;
413         }
414 #else
415         struct flock lock;
416
417         lock.l_type = F_WRLCK;  /* exclusive write lock */
418         lock.l_len = 0; /* whole file */
419         lock.l_start = 0;   /* start of file */
420         lock.l_whence = SEEK_SET;   /* end of file */
421
422         rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
423 #endif
424     }
425
426     return (rcstat);
427 }
428
429
430 /* drop cache except for the header and the active pages */
431 void rrd_dontneed(
432     rrd_file_t *rrd_file,
433     rrd_t *rrd)
434 {
435     rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
436 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
437     off_t dontneed_start;
438     off_t rra_start;
439     off_t active_block;
440     unsigned long i;
441     ssize_t   _page_size = sysconf(_SC_PAGESIZE);
442
443     if (rrd_file == NULL) {
444 #if defined DEBUG && DEBUG
445             fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
446 #endif
447             return;
448     }
449
450 #if defined DEBUG && DEBUG > 1
451     mincore_print(rrd_file, "before");
452 #endif
453
454     /* ignoring errors from RRDs that are smaller then the file_len+rounding */
455     rra_start = rrd_file->header_len;
456     dontneed_start = PAGE_START(rra_start) + _page_size;
457     for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
458         active_block =
459             PAGE_START(rra_start
460                        + rrd->rra_ptr[i].cur_row
461                        * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
462         if (active_block > dontneed_start) {
463 #ifdef USE_MADVISE
464             madvise(rrd_simple_file->file_start + dontneed_start,
465                     active_block - dontneed_start - 1, MADV_DONTNEED);
466 #endif
467 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
468 #ifdef HAVE_POSIX_FADVISE
469             posix_fadvise(rrd_simple_file->fd, dontneed_start,
470                           active_block - dontneed_start - 1,
471                           POSIX_FADV_DONTNEED);
472 #endif
473         }
474         dontneed_start = active_block;
475         /* do not release 'hot' block if update for this RAA will occur
476          * within 10 minutes */
477         if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
478             rrd->live_head->last_up % (rrd->stat_head->pdp_step *
479                                        rrd->rra_def[i].pdp_cnt) < 10 * 60) {
480             dontneed_start += _page_size;
481         }
482         rra_start +=
483             rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
484             sizeof(rrd_value_t);
485     }
486
487     if (dontneed_start < rrd_file->file_len) {
488 #ifdef USE_MADVISE
489             madvise(rrd_simple_file->file_start + dontneed_start,
490                     rrd_file->file_len - dontneed_start, MADV_DONTNEED);
491 #endif
492 #ifdef HAVE_POSIX_FADVISE
493             posix_fadvise(rrd_simple_file->fd, dontneed_start,
494                           rrd_file->file_len - dontneed_start,
495                           POSIX_FADV_DONTNEED);
496 #endif
497     }
498
499 #if defined DEBUG && DEBUG > 1
500     mincore_print(rrd_file, "after");
501 #endif
502 #endif                          /* without madvise and posix_fadvise ist does not make much sense todo anything */
503 }
504
505
506
507
508
509 int rrd_close(
510     rrd_file_t *rrd_file)
511 {
512     rrd_simple_file_t *rrd_simple_file;
513     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
514     int       ret;
515
516 #ifdef HAVE_MMAP
517     ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
518     if (ret != 0)
519         rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
520     ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
521     if (ret != 0)
522         rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
523 #endif
524     ret = close(rrd_simple_file->fd);
525     if (ret != 0)
526         rrd_set_error("closing file: %s", rrd_strerror(errno));
527     free(rrd_file->pvt);
528     free(rrd_file);
529     rrd_file = NULL;
530     return ret;
531 }
532
533
534 /* Set position of rrd_file.  */
535
536 off_t rrd_seek(
537     rrd_file_t *rrd_file,
538     off_t off,
539     int whence)
540 {
541     off_t     ret = 0;
542     rrd_simple_file_t *rrd_simple_file;
543     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
544
545 #ifdef HAVE_MMAP
546     if (whence == SEEK_SET)
547         rrd_file->pos = off;
548     else if (whence == SEEK_CUR)
549         rrd_file->pos += off;
550     else if (whence == SEEK_END)
551         rrd_file->pos = rrd_file->file_len + off;
552 #else
553     ret = lseek(rrd_simple_file->fd, off, whence);
554     if (ret < 0)
555         rrd_set_error("lseek: %s", rrd_strerror(errno));
556     rrd_file->pos = ret;
557 #endif
558     /* mimic fseek, which returns 0 upon success */
559     return ret < 0;     /*XXX: or just ret to mimic lseek */
560 }
561
562
563 /* Get current position in rrd_file.  */
564
565 off_t rrd_tell(
566     rrd_file_t *rrd_file)
567 {
568     return rrd_file->pos;
569 }
570
571
572 /* Read count bytes into buffer buf, starting at rrd_file->pos.
573  * Returns the number of bytes read or <0 on error.  */
574
575 ssize_t rrd_read(
576     rrd_file_t *rrd_file,
577     void *buf,
578     size_t count)
579 {
580     rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
581 #ifdef HAVE_MMAP
582     size_t    _cnt = count;
583     ssize_t   _surplus;
584
585     if (rrd_file->pos > rrd_file->file_len || _cnt == 0)    /* EOF */
586         return 0;
587     if (buf == NULL)
588         return -1;      /* EINVAL */
589     _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
590     if (_surplus > 0) { /* short read */
591         _cnt -= _surplus;
592     }
593     if (_cnt == 0)
594         return 0;       /* EOF */
595     buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
596
597     rrd_file->pos += _cnt;  /* mimmic read() semantics */
598     return _cnt;
599 #else
600     ssize_t   ret;
601
602     ret = read(rrd_simple_file->fd, buf, count);
603     if (ret > 0)
604         rrd_file->pos += ret;   /* mimmic read() semantics */
605     return ret;
606 #endif
607 }
608
609
610 /* Write count bytes from buffer buf to the current position
611  * rrd_file->pos of rrd_simple_file->fd.
612  * Returns the number of bytes written or <0 on error.  */
613
614 ssize_t rrd_write(
615     rrd_file_t *rrd_file,
616     const void *buf,
617     size_t count)
618 {
619     rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
620 #ifdef HAVE_MMAP
621     int old_size = rrd_file->file_len;
622     if (count == 0)
623         return 0;
624     if (buf == NULL)
625         return -1;      /* EINVAL */
626     
627     if((rrd_file->pos + count) > old_size)
628     {
629         rrd_set_error("attempting to write beyond end of file");
630         return -1;
631     }
632     memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
633     rrd_file->pos += count;
634     return count;       /* mimmic write() semantics */
635 #else
636     ssize_t   _sz = write(rrd_simple_file->fd, buf, count);
637
638     if (_sz > 0)
639         rrd_file->pos += _sz;
640     return _sz;
641 #endif
642 }
643
644
645 /* flush all data pending to be written to FD.  */
646
647 void rrd_flush(
648     rrd_file_t *rrd_file)
649 {
650     rrd_simple_file_t *rrd_simple_file;
651     rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
652     if (fdatasync(rrd_simple_file->fd) != 0) {
653         rrd_set_error("flushing fd %d: %s", rrd_simple_file->fd,
654                       rrd_strerror(errno));
655     }
656 }
657
658
659 /* Initialize RRD header.  */
660
661 void rrd_init(
662     rrd_t *rrd)
663 {
664     rrd->stat_head = NULL;
665     rrd->ds_def = NULL;
666     rrd->rra_def = NULL;
667     rrd->live_head = NULL;
668     rrd->legacy_last_up = NULL;
669     rrd->rra_ptr = NULL;
670     rrd->pdp_prep = NULL;
671     rrd->cdp_prep = NULL;
672     rrd->rrd_value = NULL;
673 }
674
675
676 /* free RRD header data.  */
677
678 #ifdef HAVE_MMAP
679 void rrd_free(
680     rrd_t *rrd)
681 {
682     if (rrd->legacy_last_up) {  /* this gets set for version < 3 only */
683         free(rrd->live_head);
684     }
685 }
686 #else
687 void rrd_free(
688     rrd_t *rrd)
689 {
690     free(rrd->live_head);
691     free(rrd->stat_head);
692     free(rrd->ds_def);
693     free(rrd->rra_def);
694     free(rrd->rra_ptr);
695     free(rrd->pdp_prep);
696     free(rrd->cdp_prep);
697     free(rrd->rrd_value);
698 }
699 #endif
700
701
702 /* routine used by external libraries to free memory allocated by
703  * rrd library */
704
705 void rrd_freemem(
706     void *mem)
707 {
708     free(mem);
709 }