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