1 /*****************************************************************************
2 * RRDtool 1.3.2 Copyright by Tobi Oetiker, 1997-2008
3 *****************************************************************************
4 * rrd_open.c Open an RRD File
5 *****************************************************************************
6 * $Id$
7 *****************************************************************************/
9 #include "rrd_tool.h"
10 #include "unused.h"
11 #define MEMBLK 8192
13 /* DEBUG 2 prints information obtained via mincore(2) */
14 #define DEBUG 1
15 /* do not calculate exact madvise hints but assume 1 page for headers and
16 * set DONTNEED for the rest, which is assumed to be data */
17 /* Avoid calling madvise on areas that were already hinted. May be benefical if
18 * your syscalls are very slow */
20 #ifdef HAVE_MMAP
21 /* the cast to void* is there to avoid this warning seen on ia64 with certain
22 versions of gcc: 'cast increases required alignment of target type'
23 */
24 #define __rrd_read(dst, dst_t, cnt) { \
25 size_t wanted = sizeof(dst_t)*(cnt); \
26 if (offset + wanted > rrd_file->file_len) { \
27 rrd_set_error("reached EOF while loading header " #dst); \
28 goto out_nullify_head; \
29 } \
30 (dst) = (dst_t*)(void*) (data + offset); \
31 offset += wanted; \
32 }
33 #else
34 #define __rrd_read(dst, dst_t, cnt) { \
35 size_t wanted = sizeof(dst_t)*(cnt); \
36 size_t got; \
37 if ((dst = malloc(wanted)) == NULL) { \
38 rrd_set_error(#dst " malloc"); \
39 goto out_nullify_head; \
40 } \
41 got = read (rrd_simple_file->fd, dst, wanted); \
42 if (got != wanted) { \
43 rrd_set_error("short read while reading header " #dst); \
44 goto out_nullify_head; \
45 } \
46 offset += got; \
47 }
48 #endif
50 /* get the address of the start of this page */
51 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
52 #ifndef PAGE_START
53 #define PAGE_START(addr) ((addr)&(~(_page_size-1)))
54 #endif
55 #endif
57 long int rra_random_row(
58 rra_def_t *);
61 /* Open a database file, return its header and an open filehandle,
62 * positioned to the first cdp in the first rra.
63 * In the error path of rrd_open, only rrd_free(&rrd) has to be called
64 * before returning an error. Do not call rrd_close upon failure of rrd_open.
65 * If creating a new file, the parameter rrd must be initialised with
66 * details of the file content.
67 * If opening an existing file, then use rrd must be initialised by
68 * rrd_init(rrd) prior to invoking rrd_open
69 */
71 rrd_file_t *rrd_open(
72 const char *const file_name,
73 rrd_t *rrd,
74 unsigned rdwr)
75 {
76 int i;
77 int flags = 0;
78 int version;
80 #ifdef HAVE_MMAP
81 ssize_t _page_size = sysconf(_SC_PAGESIZE);
82 char *data = MAP_FAILED;
83 #endif
84 off_t offset = 0;
85 struct stat statb;
86 rrd_file_t *rrd_file = NULL;
87 rrd_simple_file_t *rrd_simple_file = NULL;
88 off_t newfile_size = 0;
89 off_t header_len, value_cnt, data_len;
91 /* Are we creating a new file? */
92 if((rdwr & RRD_CREAT) && (rrd->stat_head != NULL))
93 {
94 header_len = \
95 sizeof(stat_head_t) + \
96 sizeof(ds_def_t) * rrd->stat_head->ds_cnt + \
97 sizeof(rra_def_t) * rrd->stat_head->rra_cnt + \
98 sizeof(time_t) + \
99 sizeof(live_head_t) + \
100 sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt + \
101 sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt + \
102 sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt;
104 value_cnt = 0;
105 for (i = 0; i < rrd->stat_head->rra_cnt; i++)
106 value_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[i].row_cnt;
108 data_len = sizeof(rrd_value_t) * value_cnt;
110 newfile_size = header_len + data_len;
111 }
113 rrd_file = malloc(sizeof(rrd_file_t));
114 if (rrd_file == NULL) {
115 rrd_set_error("allocating rrd_file descriptor for '%s'", file_name);
116 return NULL;
117 }
118 memset(rrd_file, 0, sizeof(rrd_file_t));
120 rrd_file->pvt = malloc(sizeof(rrd_simple_file_t));
121 if(rrd_file->pvt == NULL) {
122 rrd_set_error("allocating rrd_simple_file for '%s'", file_name);
123 return NULL;
124 }
125 memset(rrd_file->pvt, 0, sizeof(rrd_simple_file_t));
126 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
128 #ifdef DEBUG
129 if ((rdwr & (RRD_READONLY | RRD_READWRITE)) ==
130 (RRD_READONLY | RRD_READWRITE)) {
131 /* Both READONLY and READWRITE were given, which is invalid. */
132 rrd_set_error("in read/write request mask");
133 exit(-1);
134 }
135 #endif
137 #ifdef HAVE_MMAP
138 rrd_simple_file->mm_prot = PROT_READ;
139 rrd_simple_file->mm_flags = 0;
140 #endif
142 if (rdwr & RRD_READONLY) {
143 flags |= O_RDONLY;
144 #ifdef HAVE_MMAP
145 rrd_simple_file->mm_flags = MAP_PRIVATE;
146 # ifdef MAP_NORESERVE
147 rrd_simple_file->mm_flags |= MAP_NORESERVE; /* readonly, so no swap backing needed */
148 # endif
149 #endif
150 } else {
151 if (rdwr & RRD_READWRITE) {
152 flags |= O_RDWR;
153 #ifdef HAVE_MMAP
154 rrd_simple_file->mm_flags = MAP_SHARED;
155 rrd_simple_file->mm_prot |= PROT_WRITE;
156 #endif
157 }
158 if (rdwr & RRD_CREAT) {
159 flags |= (O_CREAT | O_TRUNC);
160 }
161 }
162 if (rdwr & RRD_READAHEAD) {
163 #ifdef MAP_POPULATE
164 rrd_simple_file->mm_flags |= MAP_POPULATE; /* populate ptes and data */
165 #endif
166 #if defined MAP_NONBLOCK
167 rrd_simple_file->mm_flags |= MAP_NONBLOCK; /* just populate ptes */
168 #endif
169 }
170 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
171 flags |= O_BINARY;
172 #endif
174 if ((rrd_simple_file->fd = open(file_name, flags, 0666)) < 0) {
175 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
176 goto out_free;
177 }
179 /* Better try to avoid seeks as much as possible. stat may be heavy but
180 * many concurrent seeks are even worse. */
181 if (newfile_size == 0 && ((fstat(rrd_simple_file->fd, &statb)) < 0)) {
182 rrd_set_error("fstat '%s': %s", file_name, rrd_strerror(errno));
183 goto out_close;
184 }
185 if (newfile_size == 0) {
186 rrd_file->file_len = statb.st_size;
187 } else {
188 rrd_file->file_len = newfile_size;
189 lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET);
190 write(rrd_simple_file->fd, "\0", 1); /* poke */
191 lseek(rrd_simple_file->fd, 0, SEEK_SET);
192 }
193 #ifdef HAVE_POSIX_FADVISE
194 /* In general we need no read-ahead when dealing with rrd_files.
195 When we stop reading, it is highly unlikely that we start up again.
196 In this manner we actually save time and diskaccess (and buffer cache).
197 Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
198 posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
199 #endif
201 /*
202 if (rdwr & RRD_READWRITE)
203 {
204 if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
205 rrd_set_error("failed to disable the stream buffer\n");
206 return (-1);
207 }
208 }
209 */
211 #ifdef HAVE_MMAP
212 data = mmap(0, rrd_file->file_len,
213 rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
214 rrd_simple_file->fd, offset);
216 /* lets see if the first read worked */
217 if (data == MAP_FAILED) {
218 rrd_set_error("mmaping file '%s': %s", file_name,
219 rrd_strerror(errno));
220 goto out_close;
221 }
222 rrd_simple_file->file_start = data;
223 if (rdwr & RRD_CREAT) {
224 memset(data, DNAN, newfile_size - 1);
225 goto out_done;
226 }
227 #endif
228 if (rdwr & RRD_CREAT)
229 goto out_done;
230 #ifdef USE_MADVISE
231 if (rdwr & RRD_COPY) {
232 /* We will read everything in a moment (copying) */
233 madvise(data, rrd_file->file_len, MADV_WILLNEED | MADV_SEQUENTIAL);
234 } else {
235 /* We do not need to read anything in for the moment */
236 madvise(data, rrd_file->file_len, MADV_RANDOM);
237 /* the stat_head will be needed soonish, so hint accordingly */
238 madvise(data, sizeof(stat_head_t), MADV_WILLNEED | MADV_RANDOM);
239 }
240 #endif
242 __rrd_read(rrd->stat_head, stat_head_t,
243 1);
245 /* lets do some test if we are on track ... */
246 if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
247 rrd_set_error("'%s' is not an RRD file", file_name);
248 goto out_nullify_head;
249 }
251 if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
252 rrd_set_error("This RRD was created on another architecture");
253 goto out_nullify_head;
254 }
256 version = atoi(rrd->stat_head->version);
258 if (version > atoi(RRD_VERSION)) {
259 rrd_set_error("can't handle RRD file version %s",
260 rrd->stat_head->version);
261 goto out_nullify_head;
262 }
263 #if defined USE_MADVISE
264 /* the ds_def will be needed soonish, so hint accordingly */
265 madvise(data + PAGE_START(offset),
266 sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
267 #endif
268 __rrd_read(rrd->ds_def, ds_def_t,
269 rrd->stat_head->ds_cnt);
271 #if defined USE_MADVISE
272 /* the rra_def will be needed soonish, so hint accordingly */
273 madvise(data + PAGE_START(offset),
274 sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
275 #endif
276 __rrd_read(rrd->rra_def, rra_def_t,
277 rrd->stat_head->rra_cnt);
279 /* handle different format for the live_head */
280 if (version < 3) {
281 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
282 if (rrd->live_head == NULL) {
283 rrd_set_error("live_head_t malloc");
284 goto out_close;
285 }
286 #if defined USE_MADVISE
287 /* the live_head will be needed soonish, so hint accordingly */
288 madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
289 #endif
290 __rrd_read(rrd->legacy_last_up, time_t,
291 1);
293 rrd->live_head->last_up = *rrd->legacy_last_up;
294 rrd->live_head->last_up_usec = 0;
295 } else {
296 #if defined USE_MADVISE
297 /* the live_head will be needed soonish, so hint accordingly */
298 madvise(data + PAGE_START(offset),
299 sizeof(live_head_t), MADV_WILLNEED);
300 #endif
301 __rrd_read(rrd->live_head, live_head_t,
302 1);
303 }
304 __rrd_read(rrd->pdp_prep, pdp_prep_t,
305 rrd->stat_head->ds_cnt);
306 __rrd_read(rrd->cdp_prep, cdp_prep_t,
307 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
308 __rrd_read(rrd->rra_ptr, rra_ptr_t,
309 rrd->stat_head->rra_cnt);
311 rrd_file->header_len = offset;
312 rrd_file->pos = offset;
314 {
315 unsigned long row_cnt = 0;
316 unsigned long i;
318 for (i=0; i<rrd->stat_head->rra_cnt; i++)
319 row_cnt += rrd->rra_def[i].row_cnt;
321 off_t correct_len = rrd_file->header_len +
322 sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
324 if (correct_len > rrd_file->file_len)
325 {
326 rrd_set_error("'%s' is too small (should be %ld bytes)",
327 file_name, (long long) correct_len);
328 goto out_nullify_head;
329 }
330 }
332 out_done:
333 return (rrd_file);
334 out_nullify_head:
335 rrd->stat_head = NULL;
336 out_close:
337 #ifdef HAVE_MMAP
338 if (data != MAP_FAILED)
339 munmap(data, rrd_file->file_len);
340 #endif
341 close(rrd_simple_file->fd);
342 out_free:
343 free(rrd_file->pvt);
344 free(rrd_file);
345 return NULL;
346 }
349 #if defined DEBUG && DEBUG > 1
350 /* Print list of in-core pages of a the current rrd_file. */
351 static
352 void mincore_print(
353 rrd_file_t *rrd_file,
354 char *mark)
355 {
356 rrd_simple_file_t *rrd_simple_file;
357 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
358 #ifdef HAVE_MMAP
359 /* pretty print blocks in core */
360 off_t off;
361 unsigned char *vec;
362 ssize_t _page_size = sysconf(_SC_PAGESIZE);
364 off = rrd_file->file_len +
365 ((rrd_file->file_len + _page_size - 1) / _page_size);
366 vec = malloc(off);
367 if (vec != NULL) {
368 memset(vec, 0, off);
369 if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
370 int prev;
371 unsigned is_in = 0, was_in = 0;
373 for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
374 is_in = vec[off] & 1; /* if lsb set then is core resident */
375 if (off == 0)
376 was_in = is_in;
377 if (was_in != is_in) {
378 fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
379 was_in ? "" : "not ", vec + prev, off - prev);
380 was_in = is_in;
381 prev = off;
382 }
383 }
384 fprintf(stderr,
385 "%s: %sin core: %p len %ld\n", mark,
386 was_in ? "" : "not ", vec + prev, off - prev);
387 } else
388 fprintf(stderr, "mincore: %s", rrd_strerror(errno));
389 }
390 #else
391 fprintf(stderr, "sorry mincore only works with mmap");
392 #endif
393 }
394 #endif /* defined DEBUG && DEBUG > 1 */
396 /*
397 * get exclusive lock to whole file.
398 * lock gets removed when we close the file
399 *
400 * returns 0 on success
401 */
402 int rrd_lock(
403 rrd_file_t *rrd_file)
404 {
405 int rcstat;
406 rrd_simple_file_t *rrd_simple_file;
407 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
409 {
410 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
411 struct _stat st;
413 if (_fstat(rrd_simple_file->fd, &st) == 0) {
414 rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
415 } else {
416 rcstat = -1;
417 }
418 #else
419 struct flock lock;
421 lock.l_type = F_WRLCK; /* exclusive write lock */
422 lock.l_len = 0; /* whole file */
423 lock.l_start = 0; /* start of file */
424 lock.l_whence = SEEK_SET; /* end of file */
426 rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
427 #endif
428 }
430 return (rcstat);
431 }
434 /* drop cache except for the header and the active pages */
435 void rrd_dontneed(
436 rrd_file_t *rrd_file,
437 rrd_t *rrd)
438 {
439 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
440 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
441 off_t dontneed_start;
442 off_t rra_start;
443 off_t active_block;
444 unsigned long i;
445 ssize_t _page_size = sysconf(_SC_PAGESIZE);
447 if (rrd_file == NULL) {
448 #if defined DEBUG && DEBUG
449 fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
450 #endif
451 return;
452 }
454 #if defined DEBUG && DEBUG > 1
455 mincore_print(rrd_file, "before");
456 #endif
458 /* ignoring errors from RRDs that are smaller then the file_len+rounding */
459 rra_start = rrd_file->header_len;
460 dontneed_start = PAGE_START(rra_start) + _page_size;
461 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
462 active_block =
463 PAGE_START(rra_start
464 + rrd->rra_ptr[i].cur_row
465 * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
466 if (active_block > dontneed_start) {
467 #ifdef USE_MADVISE
468 madvise(rrd_simple_file->file_start + dontneed_start,
469 active_block - dontneed_start - 1, MADV_DONTNEED);
470 #endif
471 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
472 #ifdef HAVE_POSIX_FADVISE
473 posix_fadvise(rrd_simple_file->fd, dontneed_start,
474 active_block - dontneed_start - 1,
475 POSIX_FADV_DONTNEED);
476 #endif
477 }
478 dontneed_start = active_block;
479 /* do not release 'hot' block if update for this RAA will occur
480 * within 10 minutes */
481 if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
482 rrd->live_head->last_up % (rrd->stat_head->pdp_step *
483 rrd->rra_def[i].pdp_cnt) < 10 * 60) {
484 dontneed_start += _page_size;
485 }
486 rra_start +=
487 rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
488 sizeof(rrd_value_t);
489 }
491 if (dontneed_start < rrd_file->file_len) {
492 #ifdef USE_MADVISE
493 madvise(rrd_simple_file->file_start + dontneed_start,
494 rrd_file->file_len - dontneed_start, MADV_DONTNEED);
495 #endif
496 #ifdef HAVE_POSIX_FADVISE
497 posix_fadvise(rrd_simple_file->fd, dontneed_start,
498 rrd_file->file_len - dontneed_start,
499 POSIX_FADV_DONTNEED);
500 #endif
501 }
503 #if defined DEBUG && DEBUG > 1
504 mincore_print(rrd_file, "after");
505 #endif
506 #endif /* without madvise and posix_fadvise ist does not make much sense todo anything */
507 }
513 int rrd_close(
514 rrd_file_t *rrd_file)
515 {
516 rrd_simple_file_t *rrd_simple_file;
517 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
518 int ret;
520 #ifdef HAVE_MMAP
521 ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
522 if (ret != 0)
523 rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
524 ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
525 if (ret != 0)
526 rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
527 #endif
528 ret = close(rrd_simple_file->fd);
529 if (ret != 0)
530 rrd_set_error("closing file: %s", rrd_strerror(errno));
531 free(rrd_file->pvt);
532 free(rrd_file);
533 rrd_file = NULL;
534 return ret;
535 }
538 /* Set position of rrd_file. */
540 off_t rrd_seek(
541 rrd_file_t *rrd_file,
542 off_t off,
543 int whence)
544 {
545 off_t ret = 0;
546 rrd_simple_file_t *rrd_simple_file;
547 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
549 #ifdef HAVE_MMAP
550 if (whence == SEEK_SET)
551 rrd_file->pos = off;
552 else if (whence == SEEK_CUR)
553 rrd_file->pos += off;
554 else if (whence == SEEK_END)
555 rrd_file->pos = rrd_file->file_len + off;
556 #else
557 ret = lseek(rrd_simple_file->fd, off, whence);
558 if (ret < 0)
559 rrd_set_error("lseek: %s", rrd_strerror(errno));
560 rrd_file->pos = ret;
561 #endif
562 /* mimic fseek, which returns 0 upon success */
563 return ret < 0; /*XXX: or just ret to mimic lseek */
564 }
567 /* Get current position in rrd_file. */
569 off_t rrd_tell(
570 rrd_file_t *rrd_file)
571 {
572 return rrd_file->pos;
573 }
576 /* Read count bytes into buffer buf, starting at rrd_file->pos.
577 * Returns the number of bytes read or <0 on error. */
579 ssize_t rrd_read(
580 rrd_file_t *rrd_file,
581 void *buf,
582 size_t count)
583 {
584 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
585 #ifdef HAVE_MMAP
586 size_t _cnt = count;
587 ssize_t _surplus;
589 if (rrd_file->pos > rrd_file->file_len || _cnt == 0) /* EOF */
590 return 0;
591 if (buf == NULL)
592 return -1; /* EINVAL */
593 _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
594 if (_surplus > 0) { /* short read */
595 _cnt -= _surplus;
596 }
597 if (_cnt == 0)
598 return 0; /* EOF */
599 buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
601 rrd_file->pos += _cnt; /* mimmic read() semantics */
602 return _cnt;
603 #else
604 ssize_t ret;
606 ret = read(rrd_simple_file->fd, buf, count);
607 if (ret > 0)
608 rrd_file->pos += ret; /* mimmic read() semantics */
609 return ret;
610 #endif
611 }
614 /* Write count bytes from buffer buf to the current position
615 * rrd_file->pos of rrd_simple_file->fd.
616 * Returns the number of bytes written or <0 on error. */
618 ssize_t rrd_write(
619 rrd_file_t *rrd_file,
620 const void *buf,
621 size_t count)
622 {
623 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
624 #ifdef HAVE_MMAP
625 int old_size = rrd_file->file_len;
626 if (count == 0)
627 return 0;
628 if (buf == NULL)
629 return -1; /* EINVAL */
631 if((rrd_file->pos + count) > old_size)
632 {
633 rrd_set_error("attempting to write beyond end of file");
634 return -1;
635 }
636 memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
637 rrd_file->pos += count;
638 return count; /* mimmic write() semantics */
639 #else
640 ssize_t _sz = write(rrd_simple_file->fd, buf, count);
642 if (_sz > 0)
643 rrd_file->pos += _sz;
644 return _sz;
645 #endif
646 }
649 /* flush all data pending to be written to FD. */
651 void rrd_flush(
652 rrd_file_t *rrd_file)
653 {
654 rrd_simple_file_t *rrd_simple_file;
655 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
656 if (fdatasync(rrd_simple_file->fd) != 0) {
657 rrd_set_error("flushing fd %d: %s", rrd_simple_file->fd,
658 rrd_strerror(errno));
659 }
660 }
663 /* Initialize RRD header. */
665 void rrd_init(
666 rrd_t *rrd)
667 {
668 rrd->stat_head = NULL;
669 rrd->ds_def = NULL;
670 rrd->rra_def = NULL;
671 rrd->live_head = NULL;
672 rrd->legacy_last_up = NULL;
673 rrd->rra_ptr = NULL;
674 rrd->pdp_prep = NULL;
675 rrd->cdp_prep = NULL;
676 rrd->rrd_value = NULL;
677 }
680 /* free RRD header data. */
682 #ifdef HAVE_MMAP
683 void rrd_free(
684 rrd_t *rrd)
685 {
686 if (rrd->legacy_last_up) { /* this gets set for version < 3 only */
687 free(rrd->live_head);
688 }
689 }
690 #else
691 void rrd_free(
692 rrd_t *rrd)
693 {
694 free(rrd->live_head);
695 free(rrd->stat_head);
696 free(rrd->ds_def);
697 free(rrd->rra_def);
698 free(rrd->rra_ptr);
699 free(rrd->pdp_prep);
700 free(rrd->cdp_prep);
701 free(rrd->rrd_value);
702 }
703 #endif
706 /* routine used by external libraries to free memory allocated by
707 * rrd library */
709 void rrd_freemem(
710 void *mem)
711 {
712 free(mem);
713 }
715 /*
716 * rra_update informs us about the RRAs being updated
717 * The low level storage API may use this information for
718 * aligning RRAs within stripes, or other performance enhancements
719 */
720 void rrd_notify_row(
721 rrd_file_t *rrd_file,
722 int rra_idx,
723 unsigned long rra_row,
724 time_t rra_time)
725 {
726 }
728 /*
729 * This function is called when creating a new RRD
730 * The storage implementation can use this opportunity to select
731 * a sensible starting row within the file.
732 * The default implementation is random, to ensure that all RRAs
733 * don't change to a new disk block at the same time
734 */
735 unsigned long rrd_select_initial_row(
736 rrd_file_t *rrd_file,
737 int rra_idx,
738 rra_def_t *rra
739 )
740 {
741 return rra_random_row(rra);
742 }
744 static int rand_init = 0;
746 long int rra_random_row(
747 rra_def_t *rra)
748 {
749 if (!rand_init) {
750 srandom((unsigned int) time(NULL) + (unsigned int) getpid());
751 rand_init++;
752 }
754 return random() % rra->row_cnt;
755 }