Code

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