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