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