1 /*****************************************************************************
2 * RRDtool 1.4.6 Copyright by Tobi Oetiker, 1997-2012
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 }
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 # if !defined(AIX)
161 rrd_simple_file->mm_flags = MAP_PRIVATE;
162 # endif
163 # ifdef MAP_NORESERVE
164 rrd_simple_file->mm_flags |= MAP_NORESERVE; /* readonly, so no swap backing needed */
165 # endif
166 #endif
167 } else {
168 if (rdwr & RRD_READWRITE) {
169 flags |= O_RDWR;
170 #ifdef HAVE_MMAP
171 rrd_simple_file->mm_flags = MAP_SHARED;
172 rrd_simple_file->mm_prot |= PROT_WRITE;
173 #endif
174 }
175 if (rdwr & RRD_CREAT) {
176 flags |= (O_CREAT | O_TRUNC);
177 }
178 if (rdwr & RRD_EXCL) {
179 flags |= O_EXCL;
180 }
181 }
182 if (rdwr & RRD_READAHEAD) {
183 #ifdef MAP_POPULATE
184 rrd_simple_file->mm_flags |= MAP_POPULATE; /* populate ptes and data */
185 #endif
186 #if defined MAP_NONBLOCK
187 rrd_simple_file->mm_flags |= MAP_NONBLOCK; /* just populate ptes */
188 #endif
189 }
190 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
191 flags |= O_BINARY;
192 #endif
194 if ((rrd_simple_file->fd = open(file_name, flags, 0666)) < 0) {
195 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
196 goto out_free;
197 }
199 #ifdef HAVE_MMAP
200 #ifdef HAVE_BROKEN_MS_ASYNC
201 if (rdwr & RRD_READWRITE) {
202 /* some unices, the files mtime does not get update
203 on msync MS_ASYNC, in order to help them,
204 we update the the timestamp at this point.
205 The thing happens pretty 'close' to the open
206 call so the chances of a race should be minimal.
208 Maybe ask your vendor to fix your OS ... */
209 utime(file_name,NULL);
210 }
211 #endif
212 #endif
214 /* Better try to avoid seeks as much as possible. stat may be heavy but
215 * many concurrent seeks are even worse. */
216 if (newfile_size == 0 && ((fstat(rrd_simple_file->fd, &statb)) < 0)) {
217 rrd_set_error("fstat '%s': %s", file_name, rrd_strerror(errno));
218 goto out_close;
219 }
220 if (newfile_size == 0) {
221 rrd_file->file_len = statb.st_size;
222 } else {
223 rrd_file->file_len = newfile_size;
224 lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET);
225 if ( write(rrd_simple_file->fd, "\0", 1) == -1){ /* poke */
226 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
227 goto out_close;
228 }
229 lseek(rrd_simple_file->fd, 0, SEEK_SET);
230 }
231 #ifdef HAVE_POSIX_FADVISE
232 /* In general we need no read-ahead when dealing with rrd_files.
233 When we stop reading, it is highly unlikely that we start up again.
234 In this manner we actually save time and diskaccess (and buffer cache).
235 Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
236 posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
237 #endif
239 /*
240 if (rdwr & RRD_READWRITE)
241 {
242 if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
243 rrd_set_error("failed to disable the stream buffer\n");
244 return (-1);
245 }
246 }
247 */
249 #ifdef HAVE_MMAP
250 /* force allocating the file on the underlaying filesystem to prevent any
251 * future bus error when the filesystem is full and attempting to write
252 * trough the file mapping. Filling the file using memset on the file
253 * mapping can also lead some bus error, so we use the old fashioned
254 * write().
255 */
256 if (rdwr & RRD_CREAT) {
257 char buf[4096];
258 unsigned i;
260 memset(buf, DNAN, sizeof buf);
261 lseek(rrd_simple_file->fd, offset, SEEK_SET);
263 for (i = 0; i < (newfile_size - 1) / sizeof buf; ++i)
264 {
265 if (write(rrd_simple_file->fd, buf, sizeof buf) == -1)
266 {
267 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
268 goto out_close;
269 }
270 }
272 if (write(rrd_simple_file->fd, buf,
273 (newfile_size - 1) % sizeof buf) == -1)
274 {
275 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
276 goto out_close;
277 }
279 lseek(rrd_simple_file->fd, 0, SEEK_SET);
280 }
282 data = mmap(0, rrd_file->file_len,
283 rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
284 rrd_simple_file->fd, offset);
286 /* lets see if the first read worked */
287 if (data == MAP_FAILED) {
288 rrd_set_error("mmaping file '%s': %s", file_name,
289 rrd_strerror(errno));
290 goto out_close;
291 }
292 rrd_simple_file->file_start = data;
293 if (rdwr & RRD_CREAT) {
294 goto out_done;
295 }
296 #endif
297 if (rdwr & RRD_CREAT)
298 goto out_done;
299 #ifdef USE_MADVISE
300 if (rdwr & RRD_COPY) {
301 /* We will read everything in a moment (copying) */
302 madvise(data, rrd_file->file_len, MADV_WILLNEED );
303 madvise(data, rrd_file->file_len, MADV_SEQUENTIAL );
304 } else {
305 /* We do not need to read anything in for the moment */
306 madvise(data, rrd_file->file_len, MADV_RANDOM);
307 /* the stat_head will be needed soonish, so hint accordingly */
308 madvise(data, sizeof(stat_head_t), MADV_WILLNEED);
309 madvise(data, sizeof(stat_head_t), MADV_RANDOM);
310 }
311 #endif
313 __rrd_read(rrd->stat_head, stat_head_t,
314 1);
316 /* lets do some test if we are on track ... */
317 if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
318 rrd_set_error("'%s' is not an RRD file", file_name);
319 goto out_nullify_head;
320 }
322 if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
323 rrd_set_error("This RRD was created on another architecture");
324 goto out_nullify_head;
325 }
327 version = atoi(rrd->stat_head->version);
329 if (version > atoi(RRD_VERSION)) {
330 rrd_set_error("can't handle RRD file version %s",
331 rrd->stat_head->version);
332 goto out_nullify_head;
333 }
334 #if defined USE_MADVISE
335 /* the ds_def will be needed soonish, so hint accordingly */
336 madvise(data + PAGE_START(offset),
337 sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
338 #endif
339 __rrd_read(rrd->ds_def, ds_def_t,
340 rrd->stat_head->ds_cnt);
342 #if defined USE_MADVISE
343 /* the rra_def will be needed soonish, so hint accordingly */
344 madvise(data + PAGE_START(offset),
345 sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
346 #endif
347 __rrd_read(rrd->rra_def, rra_def_t,
348 rrd->stat_head->rra_cnt);
350 /* handle different format for the live_head */
351 if (version < 3) {
352 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
353 if (rrd->live_head == NULL) {
354 rrd_set_error("live_head_t malloc");
355 goto out_close;
356 }
357 #if defined USE_MADVISE
358 /* the live_head will be needed soonish, so hint accordingly */
359 madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
360 #endif
361 __rrd_read(rrd->legacy_last_up, time_t,
362 1);
364 rrd->live_head->last_up = *rrd->legacy_last_up;
365 rrd->live_head->last_up_usec = 0;
366 } else {
367 #if defined USE_MADVISE
368 /* the live_head will be needed soonish, so hint accordingly */
369 madvise(data + PAGE_START(offset),
370 sizeof(live_head_t), MADV_WILLNEED);
371 #endif
372 __rrd_read(rrd->live_head, live_head_t,
373 1);
374 }
375 __rrd_read(rrd->pdp_prep, pdp_prep_t,
376 rrd->stat_head->ds_cnt);
377 __rrd_read(rrd->cdp_prep, cdp_prep_t,
378 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
379 __rrd_read(rrd->rra_ptr, rra_ptr_t,
380 rrd->stat_head->rra_cnt);
382 rrd_file->header_len = offset;
383 rrd_file->pos = offset;
385 {
386 unsigned long row_cnt = 0;
388 for (ui=0; ui<rrd->stat_head->rra_cnt; ui++)
389 row_cnt += rrd->rra_def[ui].row_cnt;
391 size_t correct_len = rrd_file->header_len +
392 sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
394 if (correct_len > rrd_file->file_len)
395 {
396 rrd_set_error("'%s' is too small (should be %ld bytes)",
397 file_name, (long long) correct_len);
398 goto out_nullify_head;
399 }
400 }
402 out_done:
403 return (rrd_file);
404 out_nullify_head:
405 rrd->stat_head = NULL;
406 out_close:
407 #ifdef HAVE_MMAP
408 if (data != MAP_FAILED)
409 munmap(data, rrd_file->file_len);
410 #endif
412 close(rrd_simple_file->fd);
413 out_free:
414 free(rrd_file->pvt);
415 free(rrd_file);
416 return NULL;
417 }
420 #if defined DEBUG && DEBUG > 1
421 /* Print list of in-core pages of a the current rrd_file. */
422 static
423 void mincore_print(
424 rrd_file_t *rrd_file,
425 char *mark)
426 {
427 rrd_simple_file_t *rrd_simple_file;
428 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
429 #ifdef HAVE_MMAP
430 /* pretty print blocks in core */
431 size_t off;
432 unsigned char *vec;
433 ssize_t _page_size = sysconf(_SC_PAGESIZE);
435 off = rrd_file->file_len +
436 ((rrd_file->file_len + _page_size - 1) / _page_size);
437 vec = malloc(off);
438 if (vec != NULL) {
439 memset(vec, 0, off);
440 if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
441 int prev;
442 unsigned is_in = 0, was_in = 0;
444 for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
445 is_in = vec[off] & 1; /* if lsb set then is core resident */
446 if (off == 0)
447 was_in = is_in;
448 if (was_in != is_in) {
449 fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
450 was_in ? "" : "not ", vec + prev, off - prev);
451 was_in = is_in;
452 prev = off;
453 }
454 }
455 fprintf(stderr,
456 "%s: %sin core: %p len %ld\n", mark,
457 was_in ? "" : "not ", vec + prev, off - prev);
458 } else
459 fprintf(stderr, "mincore: %s", rrd_strerror(errno));
460 }
461 #else
462 fprintf(stderr, "sorry mincore only works with mmap");
463 #endif
464 }
465 #endif /* defined DEBUG && DEBUG > 1 */
467 /*
468 * get exclusive lock to whole file.
469 * lock gets removed when we close the file
470 *
471 * returns 0 on success
472 */
473 int rrd_lock(
474 rrd_file_t *rrd_file)
475 {
476 int rcstat;
477 rrd_simple_file_t *rrd_simple_file;
478 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
480 {
481 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
482 struct _stat st;
484 if (_fstat(rrd_simple_file->fd, &st) == 0) {
485 rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
486 } else {
487 rcstat = -1;
488 }
489 #else
490 struct flock lock;
492 lock.l_type = F_WRLCK; /* exclusive write lock */
493 lock.l_len = 0; /* whole file */
494 lock.l_start = 0; /* start of file */
495 lock.l_whence = SEEK_SET; /* end of file */
497 rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
498 #endif
499 }
501 return (rcstat);
502 }
505 /* drop cache except for the header and the active pages */
506 void rrd_dontneed(
507 rrd_file_t *rrd_file,
508 rrd_t *rrd)
509 {
510 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
511 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
512 size_t dontneed_start;
513 size_t rra_start;
514 size_t active_block;
515 size_t i;
516 ssize_t _page_size = sysconf(_SC_PAGESIZE);
518 if (rrd_file == NULL) {
519 #if defined DEBUG && DEBUG
520 fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
521 #endif
522 return;
523 }
525 #if defined DEBUG && DEBUG > 1
526 mincore_print(rrd_file, "before");
527 #endif
529 /* ignoring errors from RRDs that are smaller then the file_len+rounding */
530 rra_start = rrd_file->header_len;
531 dontneed_start = PAGE_START(rra_start) + _page_size;
532 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
533 active_block =
534 PAGE_START(rra_start
535 + rrd->rra_ptr[i].cur_row
536 * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
537 if (active_block > dontneed_start) {
538 #ifdef USE_MADVISE
539 madvise(rrd_simple_file->file_start + dontneed_start,
540 active_block - dontneed_start - 1, MADV_DONTNEED);
541 #endif
542 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
543 #ifdef HAVE_POSIX_FADVISE
544 posix_fadvise(rrd_simple_file->fd, dontneed_start,
545 active_block - dontneed_start - 1,
546 POSIX_FADV_DONTNEED);
547 #endif
548 }
549 dontneed_start = active_block;
550 /* do not release 'hot' block if update for this RAA will occur
551 * within 10 minutes */
552 if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
553 rrd->live_head->last_up % (rrd->stat_head->pdp_step *
554 rrd->rra_def[i].pdp_cnt) < 10 * 60) {
555 dontneed_start += _page_size;
556 }
557 rra_start +=
558 rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
559 sizeof(rrd_value_t);
560 }
562 if (dontneed_start < rrd_file->file_len) {
563 #ifdef USE_MADVISE
564 madvise(rrd_simple_file->file_start + dontneed_start,
565 rrd_file->file_len - dontneed_start, MADV_DONTNEED);
566 #endif
567 #ifdef HAVE_POSIX_FADVISE
568 posix_fadvise(rrd_simple_file->fd, dontneed_start,
569 rrd_file->file_len - dontneed_start,
570 POSIX_FADV_DONTNEED);
571 #endif
572 }
574 #if defined DEBUG && DEBUG > 1
575 mincore_print(rrd_file, "after");
576 #endif
577 #endif /* without madvise and posix_fadvise it does not make much sense todo anything */
578 }
584 int rrd_close(
585 rrd_file_t *rrd_file)
586 {
587 rrd_simple_file_t *rrd_simple_file;
588 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
589 int ret;
591 #ifdef HAVE_MMAP
592 ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
593 if (ret != 0)
594 rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
595 ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
596 if (ret != 0)
597 rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
598 #endif
599 ret = close(rrd_simple_file->fd);
600 if (ret != 0)
601 rrd_set_error("closing file: %s", rrd_strerror(errno));
602 free(rrd_file->pvt);
603 free(rrd_file);
604 rrd_file = NULL;
605 return ret;
606 }
609 /* Set position of rrd_file. */
611 off_t rrd_seek(
612 rrd_file_t *rrd_file,
613 off_t off,
614 int whence)
615 {
616 off_t ret = 0;
617 rrd_simple_file_t *rrd_simple_file;
618 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
620 #ifdef HAVE_MMAP
621 if (whence == SEEK_SET)
622 rrd_file->pos = off;
623 else if (whence == SEEK_CUR)
624 rrd_file->pos += off;
625 else if (whence == SEEK_END)
626 rrd_file->pos = rrd_file->file_len + off;
627 #else
628 ret = lseek(rrd_simple_file->fd, off, whence);
629 if (ret < 0)
630 rrd_set_error("lseek: %s", rrd_strerror(errno));
631 rrd_file->pos = ret;
632 #endif
633 /* mimic fseek, which returns 0 upon success */
634 return ret < 0; /*XXX: or just ret to mimic lseek */
635 }
638 /* Get current position in rrd_file. */
640 off_t rrd_tell(
641 rrd_file_t *rrd_file)
642 {
643 return rrd_file->pos;
644 }
647 /* Read count bytes into buffer buf, starting at rrd_file->pos.
648 * Returns the number of bytes read or <0 on error. */
650 ssize_t rrd_read(
651 rrd_file_t *rrd_file,
652 void *buf,
653 size_t count)
654 {
655 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
656 #ifdef HAVE_MMAP
657 size_t _cnt = count;
658 ssize_t _surplus;
660 if (rrd_file->pos > rrd_file->file_len || _cnt == 0) /* EOF */
661 return 0;
662 if (buf == NULL)
663 return -1; /* EINVAL */
664 _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
665 if (_surplus > 0) { /* short read */
666 _cnt -= _surplus;
667 }
668 if (_cnt == 0)
669 return 0; /* EOF */
670 buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
672 rrd_file->pos += _cnt; /* mimmic read() semantics */
673 return _cnt;
674 #else
675 ssize_t ret;
677 ret = read(rrd_simple_file->fd, buf, count);
678 if (ret > 0)
679 rrd_file->pos += ret; /* mimmic read() semantics */
680 return ret;
681 #endif
682 }
685 /* Write count bytes from buffer buf to the current position
686 * rrd_file->pos of rrd_simple_file->fd.
687 * Returns the number of bytes written or <0 on error. */
689 ssize_t rrd_write(
690 rrd_file_t *rrd_file,
691 const void *buf,
692 size_t count)
693 {
694 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
695 #ifdef HAVE_MMAP
696 size_t old_size = rrd_file->file_len;
697 if (count == 0)
698 return 0;
699 if (buf == NULL)
700 return -1; /* EINVAL */
702 if((rrd_file->pos + count) > old_size)
703 {
704 rrd_set_error("attempting to write beyond end of file");
705 return -1;
706 }
707 memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
708 rrd_file->pos += count;
709 return count; /* mimmic write() semantics */
710 #else
711 ssize_t _sz = write(rrd_simple_file->fd, buf, count);
713 if (_sz > 0)
714 rrd_file->pos += _sz;
715 return _sz;
716 #endif
717 }
720 /* this is a leftover from the old days, it serves no purpose
721 and is therefore turned into a no-op */
722 void rrd_flush(
723 rrd_file_t UNUSED(*rrd_file))
724 {
725 }
727 /* Initialize RRD header. */
729 void rrd_init(
730 rrd_t *rrd)
731 {
732 rrd->stat_head = NULL;
733 rrd->ds_def = NULL;
734 rrd->rra_def = NULL;
735 rrd->live_head = NULL;
736 rrd->legacy_last_up = NULL;
737 rrd->rra_ptr = NULL;
738 rrd->pdp_prep = NULL;
739 rrd->cdp_prep = NULL;
740 rrd->rrd_value = NULL;
741 }
744 /* free RRD header data. */
746 #ifdef HAVE_MMAP
747 void rrd_free(
748 rrd_t *rrd)
749 {
750 if (rrd->legacy_last_up) { /* this gets set for version < 3 only */
751 free(rrd->live_head);
752 }
753 }
754 #else
755 void rrd_free(
756 rrd_t *rrd)
757 {
758 free(rrd->live_head);
759 free(rrd->stat_head);
760 free(rrd->ds_def);
761 free(rrd->rra_def);
762 free(rrd->rra_ptr);
763 free(rrd->pdp_prep);
764 free(rrd->cdp_prep);
765 free(rrd->rrd_value);
766 }
767 #endif
770 /* routine used by external libraries to free memory allocated by
771 * rrd library */
773 void rrd_freemem(
774 void *mem)
775 {
776 free(mem);
777 }
779 /*
780 * rra_update informs us about the RRAs being updated
781 * The low level storage API may use this information for
782 * aligning RRAs within stripes, or other performance enhancements
783 */
784 void rrd_notify_row(
785 rrd_file_t UNUSED(*rrd_file),
786 int UNUSED(rra_idx),
787 unsigned long UNUSED(rra_row),
788 time_t UNUSED(rra_time))
789 {
790 }
792 /*
793 * This function is called when creating a new RRD
794 * The storage implementation can use this opportunity to select
795 * a sensible starting row within the file.
796 * The default implementation is random, to ensure that all RRAs
797 * don't change to a new disk block at the same time
798 */
799 unsigned long rrd_select_initial_row(
800 rrd_file_t UNUSED(*rrd_file),
801 int UNUSED(rra_idx),
802 rra_def_t *rra
803 )
804 {
805 return rrd_random() % rra->row_cnt;
806 }