1 /*****************************************************************************
2 * RRDtool 1.4.8 Copyright by Tobi Oetiker, 1997-2013
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 #ifdef HAVE_POSIX_FALLOCATE
225 if (posix_fallocate(rrd_simple_file->fd, 0, newfile_size) == -1) {
226 rrd_set_error("posix_fallocate '%s': %s", file_name,
227 rrd_strerror(errno));
228 goto out_close;
229 }
230 #else
231 lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET);
232 if ( write(rrd_simple_file->fd, "\0", 1) == -1){ /* poke */
233 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
234 goto out_close;
235 }
236 lseek(rrd_simple_file->fd, 0, SEEK_SET);
237 #endif
238 }
239 #ifdef HAVE_POSIX_FADVISE
240 /* In general we need no read-ahead when dealing with rrd_files.
241 When we stop reading, it is highly unlikely that we start up again.
242 In this manner we actually save time and diskaccess (and buffer cache).
243 Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
244 posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
245 #endif
247 /*
248 if (rdwr & RRD_READWRITE)
249 {
250 if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
251 rrd_set_error("failed to disable the stream buffer\n");
252 return (-1);
253 }
254 }
255 */
257 #ifdef HAVE_MMAP
258 #ifndef HAVE_POSIX_FALLOCATE
259 /* force allocating the file on the underlaying filesystem to prevent any
260 * future bus error when the filesystem is full and attempting to write
261 * trough the file mapping. Filling the file using memset on the file
262 * mapping can also lead some bus error, so we use the old fashioned
263 * write().
264 */
265 if (rdwr & RRD_CREAT) {
266 char buf[4096];
267 unsigned i;
269 memset(buf, DNAN, sizeof buf);
270 lseek(rrd_simple_file->fd, offset, SEEK_SET);
272 for (i = 0; i < (newfile_size - 1) / sizeof buf; ++i)
273 {
274 if (write(rrd_simple_file->fd, buf, sizeof buf) == -1)
275 {
276 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
277 goto out_close;
278 }
279 }
281 if (write(rrd_simple_file->fd, buf,
282 (newfile_size - 1) % sizeof buf) == -1)
283 {
284 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
285 goto out_close;
286 }
288 lseek(rrd_simple_file->fd, 0, SEEK_SET);
289 }
290 #endif
292 data = mmap(0, rrd_file->file_len,
293 rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
294 rrd_simple_file->fd, offset);
296 /* lets see if the first read worked */
297 if (data == MAP_FAILED) {
298 rrd_set_error("mmaping file '%s': %s", file_name,
299 rrd_strerror(errno));
300 goto out_close;
301 }
302 rrd_simple_file->file_start = data;
303 #endif
304 if (rdwr & RRD_CREAT)
305 goto out_done;
306 #ifdef USE_MADVISE
307 if (rdwr & RRD_COPY) {
308 /* We will read everything in a moment (copying) */
309 madvise(data, rrd_file->file_len, MADV_WILLNEED );
310 madvise(data, rrd_file->file_len, MADV_SEQUENTIAL );
311 } else {
312 /* We do not need to read anything in for the moment */
313 madvise(data, rrd_file->file_len, MADV_RANDOM);
314 /* the stat_head will be needed soonish, so hint accordingly */
315 madvise(data, sizeof(stat_head_t), MADV_WILLNEED);
316 madvise(data, sizeof(stat_head_t), MADV_RANDOM);
317 }
318 #endif
320 __rrd_read(rrd->stat_head, stat_head_t,
321 1);
323 /* lets do some test if we are on track ... */
324 if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
325 rrd_set_error("'%s' is not an RRD file", file_name);
326 goto out_nullify_head;
327 }
329 if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
330 rrd_set_error("This RRD was created on another architecture");
331 goto out_nullify_head;
332 }
334 version = atoi(rrd->stat_head->version);
336 if (version > atoi(RRD_VERSION)) {
337 rrd_set_error("can't handle RRD file version %s",
338 rrd->stat_head->version);
339 goto out_nullify_head;
340 }
341 #if defined USE_MADVISE
342 /* the ds_def will be needed soonish, so hint accordingly */
343 madvise(data + PAGE_START(offset),
344 sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
345 #endif
346 __rrd_read(rrd->ds_def, ds_def_t,
347 rrd->stat_head->ds_cnt);
349 #if defined USE_MADVISE
350 /* the rra_def will be needed soonish, so hint accordingly */
351 madvise(data + PAGE_START(offset),
352 sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
353 #endif
354 __rrd_read(rrd->rra_def, rra_def_t,
355 rrd->stat_head->rra_cnt);
357 /* handle different format for the live_head */
358 if (version < 3) {
359 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
360 if (rrd->live_head == NULL) {
361 rrd_set_error("live_head_t malloc");
362 goto out_close;
363 }
364 #if defined USE_MADVISE
365 /* the live_head will be needed soonish, so hint accordingly */
366 madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
367 #endif
368 __rrd_read(rrd->legacy_last_up, time_t,
369 1);
371 rrd->live_head->last_up = *rrd->legacy_last_up;
372 rrd->live_head->last_up_usec = 0;
373 } else {
374 #if defined USE_MADVISE
375 /* the live_head will be needed soonish, so hint accordingly */
376 madvise(data + PAGE_START(offset),
377 sizeof(live_head_t), MADV_WILLNEED);
378 #endif
379 __rrd_read(rrd->live_head, live_head_t,
380 1);
381 }
382 __rrd_read(rrd->pdp_prep, pdp_prep_t,
383 rrd->stat_head->ds_cnt);
384 __rrd_read(rrd->cdp_prep, cdp_prep_t,
385 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
386 __rrd_read(rrd->rra_ptr, rra_ptr_t,
387 rrd->stat_head->rra_cnt);
389 rrd_file->header_len = offset;
390 rrd_file->pos = offset;
392 {
393 unsigned long row_cnt = 0;
395 for (ui=0; ui<rrd->stat_head->rra_cnt; ui++)
396 row_cnt += rrd->rra_def[ui].row_cnt;
398 size_t correct_len = rrd_file->header_len +
399 sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
401 if (correct_len > rrd_file->file_len)
402 {
403 rrd_set_error("'%s' is too small (should be %ld bytes)",
404 file_name, (long long) correct_len);
405 goto out_nullify_head;
406 }
407 }
409 out_done:
410 return (rrd_file);
411 out_nullify_head:
412 rrd->stat_head = NULL;
413 out_close:
414 #ifdef HAVE_MMAP
415 if (data != MAP_FAILED)
416 munmap(data, rrd_file->file_len);
417 #endif
419 close(rrd_simple_file->fd);
420 out_free:
421 free(rrd_file->pvt);
422 free(rrd_file);
423 return NULL;
424 }
427 #if defined DEBUG && DEBUG > 1
428 /* Print list of in-core pages of a the current rrd_file. */
429 static
430 void mincore_print(
431 rrd_file_t *rrd_file,
432 char *mark)
433 {
434 rrd_simple_file_t *rrd_simple_file;
435 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
436 #ifdef HAVE_MMAP
437 /* pretty print blocks in core */
438 size_t off;
439 unsigned char *vec;
440 ssize_t _page_size = sysconf(_SC_PAGESIZE);
442 off = rrd_file->file_len +
443 ((rrd_file->file_len + _page_size - 1) / _page_size);
444 vec = malloc(off);
445 if (vec != NULL) {
446 memset(vec, 0, off);
447 if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
448 int prev;
449 unsigned is_in = 0, was_in = 0;
451 for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
452 is_in = vec[off] & 1; /* if lsb set then is core resident */
453 if (off == 0)
454 was_in = is_in;
455 if (was_in != is_in) {
456 fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
457 was_in ? "" : "not ", vec + prev, off - prev);
458 was_in = is_in;
459 prev = off;
460 }
461 }
462 fprintf(stderr,
463 "%s: %sin core: %p len %ld\n", mark,
464 was_in ? "" : "not ", vec + prev, off - prev);
465 } else
466 fprintf(stderr, "mincore: %s", rrd_strerror(errno));
467 }
468 #else
469 fprintf(stderr, "sorry mincore only works with mmap");
470 #endif
471 }
472 #endif /* defined DEBUG && DEBUG > 1 */
474 /*
475 * get exclusive lock to whole file.
476 * lock gets removed when we close the file
477 *
478 * returns 0 on success
479 */
480 int rrd_lock(
481 rrd_file_t *rrd_file)
482 {
483 int rcstat;
484 rrd_simple_file_t *rrd_simple_file;
485 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
487 {
488 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
489 struct _stat st;
491 if (_fstat(rrd_simple_file->fd, &st) == 0) {
492 rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
493 } else {
494 rcstat = -1;
495 }
496 #else
497 struct flock lock;
499 lock.l_type = F_WRLCK; /* exclusive write lock */
500 lock.l_len = 0; /* whole file */
501 lock.l_start = 0; /* start of file */
502 lock.l_whence = SEEK_SET; /* end of file */
504 rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
505 #endif
506 }
508 return (rcstat);
509 }
512 /* drop cache except for the header and the active pages */
513 void rrd_dontneed(
514 rrd_file_t *rrd_file,
515 rrd_t *rrd)
516 {
517 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
518 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
519 size_t dontneed_start;
520 size_t rra_start;
521 size_t active_block;
522 size_t i;
523 ssize_t _page_size = sysconf(_SC_PAGESIZE);
525 if (rrd_file == NULL) {
526 #if defined DEBUG && DEBUG
527 fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
528 #endif
529 return;
530 }
532 #if defined DEBUG && DEBUG > 1
533 mincore_print(rrd_file, "before");
534 #endif
536 /* ignoring errors from RRDs that are smaller then the file_len+rounding */
537 rra_start = rrd_file->header_len;
538 dontneed_start = PAGE_START(rra_start) + _page_size;
539 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
540 active_block =
541 PAGE_START(rra_start
542 + rrd->rra_ptr[i].cur_row
543 * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
544 if (active_block > dontneed_start) {
545 #ifdef USE_MADVISE
546 madvise(rrd_simple_file->file_start + dontneed_start,
547 active_block - dontneed_start - 1, MADV_DONTNEED);
548 #endif
549 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
550 #ifdef HAVE_POSIX_FADVISE
551 posix_fadvise(rrd_simple_file->fd, dontneed_start,
552 active_block - dontneed_start - 1,
553 POSIX_FADV_DONTNEED);
554 #endif
555 }
556 dontneed_start = active_block;
557 /* do not release 'hot' block if update for this RAA will occur
558 * within 10 minutes */
559 if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
560 rrd->live_head->last_up % (rrd->stat_head->pdp_step *
561 rrd->rra_def[i].pdp_cnt) < 10 * 60) {
562 dontneed_start += _page_size;
563 }
564 rra_start +=
565 rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
566 sizeof(rrd_value_t);
567 }
569 if (dontneed_start < rrd_file->file_len) {
570 #ifdef USE_MADVISE
571 madvise(rrd_simple_file->file_start + dontneed_start,
572 rrd_file->file_len - dontneed_start, MADV_DONTNEED);
573 #endif
574 #ifdef HAVE_POSIX_FADVISE
575 posix_fadvise(rrd_simple_file->fd, dontneed_start,
576 rrd_file->file_len - dontneed_start,
577 POSIX_FADV_DONTNEED);
578 #endif
579 }
581 #if defined DEBUG && DEBUG > 1
582 mincore_print(rrd_file, "after");
583 #endif
584 #endif /* without madvise and posix_fadvise it does not make much sense todo anything */
585 }
591 int rrd_close(
592 rrd_file_t *rrd_file)
593 {
594 rrd_simple_file_t *rrd_simple_file;
595 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
596 int ret;
598 #ifdef HAVE_MMAP
599 ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
600 if (ret != 0)
601 rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
602 ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
603 if (ret != 0)
604 rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
605 #endif
606 ret = close(rrd_simple_file->fd);
607 if (ret != 0)
608 rrd_set_error("closing file: %s", rrd_strerror(errno));
609 free(rrd_file->pvt);
610 free(rrd_file);
611 rrd_file = NULL;
612 return ret;
613 }
616 /* Set position of rrd_file. */
618 off_t rrd_seek(
619 rrd_file_t *rrd_file,
620 off_t off,
621 int whence)
622 {
623 off_t ret = 0;
624 #ifndef HAVE_MMAP
625 rrd_simple_file_t *rrd_simple_file;
626 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
627 #endif
629 #ifdef HAVE_MMAP
630 if (whence == SEEK_SET)
631 rrd_file->pos = off;
632 else if (whence == SEEK_CUR)
633 rrd_file->pos += off;
634 else if (whence == SEEK_END)
635 rrd_file->pos = rrd_file->file_len + off;
636 #else
637 ret = lseek(rrd_simple_file->fd, off, whence);
638 if (ret < 0)
639 rrd_set_error("lseek: %s", rrd_strerror(errno));
640 rrd_file->pos = ret;
641 #endif
642 /* mimic fseek, which returns 0 upon success */
643 return ret < 0; /*XXX: or just ret to mimic lseek */
644 }
647 /* Get current position in rrd_file. */
649 off_t rrd_tell(
650 rrd_file_t *rrd_file)
651 {
652 return rrd_file->pos;
653 }
656 /* Read count bytes into buffer buf, starting at rrd_file->pos.
657 * Returns the number of bytes read or <0 on error. */
659 ssize_t rrd_read(
660 rrd_file_t *rrd_file,
661 void *buf,
662 size_t count)
663 {
664 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
665 #ifdef HAVE_MMAP
666 size_t _cnt = count;
667 ssize_t _surplus;
669 if (rrd_file->pos > rrd_file->file_len || _cnt == 0) /* EOF */
670 return 0;
671 if (buf == NULL)
672 return -1; /* EINVAL */
673 _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
674 if (_surplus > 0) { /* short read */
675 _cnt -= _surplus;
676 }
677 if (_cnt == 0)
678 return 0; /* EOF */
679 buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
681 rrd_file->pos += _cnt; /* mimmic read() semantics */
682 return _cnt;
683 #else
684 ssize_t ret;
686 ret = read(rrd_simple_file->fd, buf, count);
687 if (ret > 0)
688 rrd_file->pos += ret; /* mimmic read() semantics */
689 return ret;
690 #endif
691 }
694 /* Write count bytes from buffer buf to the current position
695 * rrd_file->pos of rrd_simple_file->fd.
696 * Returns the number of bytes written or <0 on error. */
698 ssize_t rrd_write(
699 rrd_file_t *rrd_file,
700 const void *buf,
701 size_t count)
702 {
703 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
704 #ifdef HAVE_MMAP
705 size_t old_size = rrd_file->file_len;
706 if (count == 0)
707 return 0;
708 if (buf == NULL)
709 return -1; /* EINVAL */
711 if((rrd_file->pos + count) > old_size)
712 {
713 rrd_set_error("attempting to write beyond end of file");
714 return -1;
715 }
716 memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
717 rrd_file->pos += count;
718 return count; /* mimmic write() semantics */
719 #else
720 ssize_t _sz = write(rrd_simple_file->fd, buf, count);
722 if (_sz > 0)
723 rrd_file->pos += _sz;
724 return _sz;
725 #endif
726 }
729 /* this is a leftover from the old days, it serves no purpose
730 and is therefore turned into a no-op */
731 void rrd_flush(
732 rrd_file_t UNUSED(*rrd_file))
733 {
734 }
736 /* Initialize RRD header. */
738 void rrd_init(
739 rrd_t *rrd)
740 {
741 rrd->stat_head = NULL;
742 rrd->ds_def = NULL;
743 rrd->rra_def = NULL;
744 rrd->live_head = NULL;
745 rrd->legacy_last_up = NULL;
746 rrd->rra_ptr = NULL;
747 rrd->pdp_prep = NULL;
748 rrd->cdp_prep = NULL;
749 rrd->rrd_value = NULL;
750 }
753 /* free RRD header data. */
755 #ifdef HAVE_MMAP
756 void rrd_free(
757 rrd_t *rrd)
758 {
759 if (rrd->legacy_last_up) { /* this gets set for version < 3 only */
760 free(rrd->live_head);
761 }
762 }
763 #else
764 void rrd_free(
765 rrd_t *rrd)
766 {
767 free(rrd->live_head);
768 free(rrd->stat_head);
769 free(rrd->ds_def);
770 free(rrd->rra_def);
771 free(rrd->rra_ptr);
772 free(rrd->pdp_prep);
773 free(rrd->cdp_prep);
774 free(rrd->rrd_value);
775 }
776 #endif
779 /* routine used by external libraries to free memory allocated by
780 * rrd library */
782 void rrd_freemem(
783 void *mem)
784 {
785 free(mem);
786 }
788 /*
789 * rra_update informs us about the RRAs being updated
790 * The low level storage API may use this information for
791 * aligning RRAs within stripes, or other performance enhancements
792 */
793 void rrd_notify_row(
794 rrd_file_t UNUSED(*rrd_file),
795 int UNUSED(rra_idx),
796 unsigned long UNUSED(rra_row),
797 time_t UNUSED(rra_time))
798 {
799 }
801 /*
802 * This function is called when creating a new RRD
803 * The storage implementation can use this opportunity to select
804 * a sensible starting row within the file.
805 * The default implementation is random, to ensure that all RRAs
806 * don't change to a new disk block at the same time
807 */
808 unsigned long rrd_select_initial_row(
809 rrd_file_t UNUSED(*rrd_file),
810 int UNUSED(rra_idx),
811 rra_def_t *rra
812 )
813 {
814 return rrd_random() % rra->row_cnt;
815 }