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