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