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