madvise hints may not be ORed since they are enumerated ... #224
[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         write(rrd_simple_file->fd, "\0", 1);   /* poke */
220         lseek(rrd_simple_file->fd, 0, SEEK_SET);
221     }
222 #ifdef HAVE_POSIX_FADVISE
223     /* In general we need no read-ahead when dealing with rrd_files.
224        When we stop reading, it is highly unlikely that we start up again.
225        In this manner we actually save time and diskaccess (and buffer cache).
226        Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
227     posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
228 #endif
229
230 /*
231         if (rdwr & RRD_READWRITE)
232         {
233            if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
234                   rrd_set_error("failed to disable the stream buffer\n");
235                   return (-1);
236            }
237         }
238 */
239
240 #ifdef HAVE_MMAP
241     data = mmap(0, rrd_file->file_len, 
242         rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
243         rrd_simple_file->fd, offset);
244
245     /* lets see if the first read worked */
246     if (data == MAP_FAILED) {
247         rrd_set_error("mmaping file '%s': %s", file_name,
248                       rrd_strerror(errno));
249         goto out_close;
250     }
251     rrd_simple_file->file_start = data;
252     if (rdwr & RRD_CREAT) {
253         memset(data, DNAN, newfile_size - 1);
254         goto out_done;
255     }
256 #endif
257     if (rdwr & RRD_CREAT)
258         goto out_done;
259 #ifdef USE_MADVISE
260     if (rdwr & RRD_COPY) {
261         /* We will read everything in a moment (copying) */
262         madvise(data, rrd_file->file_len, MADV_WILLNEED );
263         madvise(data, rrd_file->file_len, MADV_SEQUENTIAL );
264     } else {
265         /* We do not need to read anything in for the moment */
266         madvise(data, rrd_file->file_len, MADV_RANDOM);
267         /* the stat_head will be needed soonish, so hint accordingly */
268         madvise(data, sizeof(stat_head_t), MADV_WILLNEED);
269         madvise(data, sizeof(stat_head_t), 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     size_t 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 /* this is a leftover from the old days, it serves no purpose
681    and is therefore turned into a no-op */
682 void rrd_flush(
683     rrd_file_t *rrd_file  __attribute__((unused)))
684 {
685 }
686
687 /* Initialize RRD header.  */
688
689 void rrd_init(
690     rrd_t *rrd)
691 {
692     rrd->stat_head = NULL;
693     rrd->ds_def = NULL;
694     rrd->rra_def = NULL;
695     rrd->live_head = NULL;
696     rrd->legacy_last_up = NULL;
697     rrd->rra_ptr = NULL;
698     rrd->pdp_prep = NULL;
699     rrd->cdp_prep = NULL;
700     rrd->rrd_value = NULL;
701 }
702
703
704 /* free RRD header data.  */
705
706 #ifdef HAVE_MMAP
707 void rrd_free(
708     rrd_t *rrd)
709 {
710     if (rrd->legacy_last_up) {  /* this gets set for version < 3 only */
711         free(rrd->live_head);
712     }
713 }
714 #else
715 void rrd_free(
716     rrd_t *rrd)
717 {
718     free(rrd->live_head);
719     free(rrd->stat_head);
720     free(rrd->ds_def);
721     free(rrd->rra_def);
722     free(rrd->rra_ptr);
723     free(rrd->pdp_prep);
724     free(rrd->cdp_prep);
725     free(rrd->rrd_value);
726 }
727 #endif
728
729
730 /* routine used by external libraries to free memory allocated by
731  * rrd library */
732
733 void rrd_freemem(
734     void *mem)
735 {
736     free(mem);
737 }
738
739 /*
740  * rra_update informs us about the RRAs being updated
741  * The low level storage API may use this information for
742  * aligning RRAs within stripes, or other performance enhancements
743  */
744 void rrd_notify_row(
745     rrd_file_t *rrd_file  __attribute__((unused)),
746     int rra_idx  __attribute__((unused)),
747     unsigned long rra_row  __attribute__((unused)),
748     time_t rra_time  __attribute__((unused)))
749 {
750 }
751
752 /*
753  * This function is called when creating a new RRD
754  * The storage implementation can use this opportunity to select
755  * a sensible starting row within the file.
756  * The default implementation is random, to ensure that all RRAs
757  * don't change to a new disk block at the same time
758  */
759 unsigned long rrd_select_initial_row(
760     rrd_file_t *rrd_file  __attribute__((unused)),
761     int rra_idx  __attribute__((unused)),
762     rra_def_t *rra
763     )
764 {
765     return rrd_random() % rra->row_cnt;
766 }