a83031a99cf40694129157fcee55400594e74202
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 /* Open a database file, return its header and an open filehandle,
58 * positioned to the first cdp in the first rra.
59 * In the error path of rrd_open, only rrd_free(&rrd) has to be called
60 * before returning an error. Do not call rrd_close upon failure of rrd_open.
61 * If creating a new file, the parameter rrd must be initialised with
62 * details of the file content.
63 * If opening an existing file, then use rrd must be initialised by
64 * rrd_init(rrd) prior to invoking rrd_open
65 */
67 rrd_file_t *rrd_open(
68 const char *const file_name,
69 rrd_t *rrd,
70 unsigned rdwr)
71 {
72 int i;
73 int flags = 0;
74 int version;
76 #ifdef HAVE_MMAP
77 ssize_t _page_size = sysconf(_SC_PAGESIZE);
78 char *data = MAP_FAILED;
79 #endif
80 off_t offset = 0;
81 struct stat statb;
82 rrd_file_t *rrd_file = NULL;
83 rrd_simple_file_t *rrd_simple_file = NULL;
84 off_t newfile_size = 0;
85 off_t header_len, value_cnt, data_len;
87 /* Are we creating a new file? */
88 if((rdwr & RRD_CREAT) && (rrd->stat_head != NULL))
89 {
90 header_len = \
91 sizeof(stat_head_t) + \
92 sizeof(ds_def_t) * rrd->stat_head->ds_cnt + \
93 sizeof(rra_def_t) * rrd->stat_head->rra_cnt + \
94 sizeof(time_t) + \
95 sizeof(live_head_t) + \
96 sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt + \
97 sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt + \
98 sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt;
100 value_cnt = 0;
101 for (i = 0; i < rrd->stat_head->rra_cnt; i++)
102 value_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[i].row_cnt;
104 data_len = sizeof(rrd_value_t) * value_cnt;
106 newfile_size = header_len + data_len;
107 }
109 rrd_file = malloc(sizeof(rrd_file_t));
110 if (rrd_file == NULL) {
111 rrd_set_error("allocating rrd_file descriptor for '%s'", file_name);
112 return NULL;
113 }
114 memset(rrd_file, 0, sizeof(rrd_file_t));
116 rrd_file->pvt = malloc(sizeof(rrd_simple_file_t));
117 if(rrd_file->pvt == NULL) {
118 rrd_set_error("allocating rrd_simple_file for '%s'", file_name);
119 return NULL;
120 }
121 memset(rrd_file->pvt, 0, sizeof(rrd_simple_file_t));
122 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
124 #ifdef DEBUG
125 if ((rdwr & (RRD_READONLY | RRD_READWRITE)) ==
126 (RRD_READONLY | RRD_READWRITE)) {
127 /* Both READONLY and READWRITE were given, which is invalid. */
128 rrd_set_error("in read/write request mask");
129 exit(-1);
130 }
131 #endif
133 #ifdef HAVE_MMAP
134 rrd_simple_file->mm_prot = PROT_READ;
135 rrd_simple_file->mm_flags = 0;
136 #endif
138 if (rdwr & RRD_READONLY) {
139 flags |= O_RDONLY;
140 #ifdef HAVE_MMAP
141 rrd_simple_file->mm_flags = MAP_PRIVATE;
142 # ifdef MAP_NORESERVE
143 rrd_simple_file->mm_flags |= MAP_NORESERVE; /* readonly, so no swap backing needed */
144 # endif
145 #endif
146 } else {
147 if (rdwr & RRD_READWRITE) {
148 flags |= O_RDWR;
149 #ifdef HAVE_MMAP
150 rrd_simple_file->mm_flags = MAP_SHARED;
151 rrd_simple_file->mm_prot |= PROT_WRITE;
152 #endif
153 }
154 if (rdwr & RRD_CREAT) {
155 flags |= (O_CREAT | O_TRUNC);
156 }
157 }
158 if (rdwr & RRD_READAHEAD) {
159 #ifdef MAP_POPULATE
160 rrd_simple_file->mm_flags |= MAP_POPULATE; /* populate ptes and data */
161 #endif
162 #if defined MAP_NONBLOCK
163 rrd_simple_file->mm_flags |= MAP_NONBLOCK; /* just populate ptes */
164 #endif
165 }
166 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
167 flags |= O_BINARY;
168 #endif
170 if ((rrd_simple_file->fd = open(file_name, flags, 0666)) < 0) {
171 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
172 goto out_free;
173 }
175 /* Better try to avoid seeks as much as possible. stat may be heavy but
176 * many concurrent seeks are even worse. */
177 if (newfile_size == 0 && ((fstat(rrd_simple_file->fd, &statb)) < 0)) {
178 rrd_set_error("fstat '%s': %s", file_name, rrd_strerror(errno));
179 goto out_close;
180 }
181 if (newfile_size == 0) {
182 rrd_file->file_len = statb.st_size;
183 } else {
184 rrd_file->file_len = newfile_size;
185 lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET);
186 write(rrd_simple_file->fd, "\0", 1); /* poke */
187 lseek(rrd_simple_file->fd, 0, SEEK_SET);
188 }
189 #ifdef HAVE_POSIX_FADVISE
190 /* In general we need no read-ahead when dealing with rrd_files.
191 When we stop reading, it is highly unlikely that we start up again.
192 In this manner we actually save time and diskaccess (and buffer cache).
193 Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
194 posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
195 #endif
197 /*
198 if (rdwr & RRD_READWRITE)
199 {
200 if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
201 rrd_set_error("failed to disable the stream buffer\n");
202 return (-1);
203 }
204 }
205 */
207 #ifdef HAVE_MMAP
208 data = mmap(0, rrd_file->file_len,
209 rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
210 rrd_simple_file->fd, offset);
212 /* lets see if the first read worked */
213 if (data == MAP_FAILED) {
214 rrd_set_error("mmaping file '%s': %s", file_name,
215 rrd_strerror(errno));
216 goto out_close;
217 }
218 rrd_simple_file->file_start = data;
219 if (rdwr & RRD_CREAT) {
220 memset(data, DNAN, newfile_size - 1);
221 goto out_done;
222 }
223 #endif
224 if (rdwr & RRD_CREAT)
225 goto out_done;
226 #ifdef USE_MADVISE
227 if (rdwr & RRD_COPY) {
228 /* We will read everything in a moment (copying) */
229 madvise(data, rrd_file->file_len, MADV_WILLNEED | MADV_SEQUENTIAL);
230 } else {
231 /* We do not need to read anything in for the moment */
232 madvise(data, rrd_file->file_len, MADV_RANDOM);
233 /* the stat_head will be needed soonish, so hint accordingly */
234 madvise(data, sizeof(stat_head_t), MADV_WILLNEED | MADV_RANDOM);
235 }
236 #endif
238 __rrd_read(rrd->stat_head, stat_head_t,
239 1);
241 /* lets do some test if we are on track ... */
242 if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
243 rrd_set_error("'%s' is not an RRD file", file_name);
244 goto out_nullify_head;
245 }
247 if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
248 rrd_set_error("This RRD was created on another architecture");
249 goto out_nullify_head;
250 }
252 version = atoi(rrd->stat_head->version);
254 if (version > atoi(RRD_VERSION)) {
255 rrd_set_error("can't handle RRD file version %s",
256 rrd->stat_head->version);
257 goto out_nullify_head;
258 }
259 #if defined USE_MADVISE
260 /* the ds_def will be needed soonish, so hint accordingly */
261 madvise(data + PAGE_START(offset),
262 sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
263 #endif
264 __rrd_read(rrd->ds_def, ds_def_t,
265 rrd->stat_head->ds_cnt);
267 #if defined USE_MADVISE
268 /* the rra_def will be needed soonish, so hint accordingly */
269 madvise(data + PAGE_START(offset),
270 sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
271 #endif
272 __rrd_read(rrd->rra_def, rra_def_t,
273 rrd->stat_head->rra_cnt);
275 /* handle different format for the live_head */
276 if (version < 3) {
277 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
278 if (rrd->live_head == NULL) {
279 rrd_set_error("live_head_t malloc");
280 goto out_close;
281 }
282 #if defined USE_MADVISE
283 /* the live_head will be needed soonish, so hint accordingly */
284 madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
285 #endif
286 __rrd_read(rrd->legacy_last_up, time_t,
287 1);
289 rrd->live_head->last_up = *rrd->legacy_last_up;
290 rrd->live_head->last_up_usec = 0;
291 } else {
292 #if defined USE_MADVISE
293 /* the live_head will be needed soonish, so hint accordingly */
294 madvise(data + PAGE_START(offset),
295 sizeof(live_head_t), MADV_WILLNEED);
296 #endif
297 __rrd_read(rrd->live_head, live_head_t,
298 1);
299 }
300 __rrd_read(rrd->pdp_prep, pdp_prep_t,
301 rrd->stat_head->ds_cnt);
302 __rrd_read(rrd->cdp_prep, cdp_prep_t,
303 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
304 __rrd_read(rrd->rra_ptr, rra_ptr_t,
305 rrd->stat_head->rra_cnt);
307 rrd_file->header_len = offset;
308 rrd_file->pos = offset;
310 {
311 unsigned long row_cnt = 0;
312 unsigned long i;
314 for (i=0; i<rrd->stat_head->rra_cnt; i++)
315 row_cnt += rrd->rra_def[i].row_cnt;
317 off_t correct_len = rrd_file->header_len +
318 sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
320 if (correct_len > rrd_file->file_len)
321 {
322 rrd_set_error("'%s' is too small (should be %ld bytes)",
323 file_name, (long long) correct_len);
324 goto out_nullify_head;
325 }
326 }
328 out_done:
329 return (rrd_file);
330 out_nullify_head:
331 rrd->stat_head = NULL;
332 out_close:
333 #ifdef HAVE_MMAP
334 if (data != MAP_FAILED)
335 munmap(data, rrd_file->file_len);
336 #endif
337 close(rrd_simple_file->fd);
338 out_free:
339 free(rrd_file->pvt);
340 free(rrd_file);
341 return NULL;
342 }
345 #if defined DEBUG && DEBUG > 1
346 /* Print list of in-core pages of a the current rrd_file. */
347 static
348 void mincore_print(
349 rrd_file_t *rrd_file,
350 char *mark)
351 {
352 rrd_simple_file_t *rrd_simple_file;
353 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
354 #ifdef HAVE_MMAP
355 /* pretty print blocks in core */
356 off_t off;
357 unsigned char *vec;
358 ssize_t _page_size = sysconf(_SC_PAGESIZE);
360 off = rrd_file->file_len +
361 ((rrd_file->file_len + _page_size - 1) / _page_size);
362 vec = malloc(off);
363 if (vec != NULL) {
364 memset(vec, 0, off);
365 if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
366 int prev;
367 unsigned is_in = 0, was_in = 0;
369 for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
370 is_in = vec[off] & 1; /* if lsb set then is core resident */
371 if (off == 0)
372 was_in = is_in;
373 if (was_in != is_in) {
374 fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
375 was_in ? "" : "not ", vec + prev, off - prev);
376 was_in = is_in;
377 prev = off;
378 }
379 }
380 fprintf(stderr,
381 "%s: %sin core: %p len %ld\n", mark,
382 was_in ? "" : "not ", vec + prev, off - prev);
383 } else
384 fprintf(stderr, "mincore: %s", rrd_strerror(errno));
385 }
386 #else
387 fprintf(stderr, "sorry mincore only works with mmap");
388 #endif
389 }
390 #endif /* defined DEBUG && DEBUG > 1 */
392 /*
393 * get exclusive lock to whole file.
394 * lock gets removed when we close the file
395 *
396 * returns 0 on success
397 */
398 int rrd_lock(
399 rrd_file_t *rrd_file)
400 {
401 int rcstat;
402 rrd_simple_file_t *rrd_simple_file;
403 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
405 {
406 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
407 struct _stat st;
409 if (_fstat(rrd_simple_file->fd, &st) == 0) {
410 rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
411 } else {
412 rcstat = -1;
413 }
414 #else
415 struct flock lock;
417 lock.l_type = F_WRLCK; /* exclusive write lock */
418 lock.l_len = 0; /* whole file */
419 lock.l_start = 0; /* start of file */
420 lock.l_whence = SEEK_SET; /* end of file */
422 rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
423 #endif
424 }
426 return (rcstat);
427 }
430 /* drop cache except for the header and the active pages */
431 void rrd_dontneed(
432 rrd_file_t *rrd_file,
433 rrd_t *rrd)
434 {
435 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
436 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
437 off_t dontneed_start;
438 off_t rra_start;
439 off_t active_block;
440 unsigned long i;
441 ssize_t _page_size = sysconf(_SC_PAGESIZE);
443 if (rrd_file == NULL) {
444 #if defined DEBUG && DEBUG
445 fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
446 #endif
447 return;
448 }
450 #if defined DEBUG && DEBUG > 1
451 mincore_print(rrd_file, "before");
452 #endif
454 /* ignoring errors from RRDs that are smaller then the file_len+rounding */
455 rra_start = rrd_file->header_len;
456 dontneed_start = PAGE_START(rra_start) + _page_size;
457 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
458 active_block =
459 PAGE_START(rra_start
460 + rrd->rra_ptr[i].cur_row
461 * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
462 if (active_block > dontneed_start) {
463 #ifdef USE_MADVISE
464 madvise(rrd_simple_file->file_start + dontneed_start,
465 active_block - dontneed_start - 1, MADV_DONTNEED);
466 #endif
467 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
468 #ifdef HAVE_POSIX_FADVISE
469 posix_fadvise(rrd_simple_file->fd, dontneed_start,
470 active_block - dontneed_start - 1,
471 POSIX_FADV_DONTNEED);
472 #endif
473 }
474 dontneed_start = active_block;
475 /* do not release 'hot' block if update for this RAA will occur
476 * within 10 minutes */
477 if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
478 rrd->live_head->last_up % (rrd->stat_head->pdp_step *
479 rrd->rra_def[i].pdp_cnt) < 10 * 60) {
480 dontneed_start += _page_size;
481 }
482 rra_start +=
483 rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
484 sizeof(rrd_value_t);
485 }
487 if (dontneed_start < rrd_file->file_len) {
488 #ifdef USE_MADVISE
489 madvise(rrd_simple_file->file_start + dontneed_start,
490 rrd_file->file_len - dontneed_start, MADV_DONTNEED);
491 #endif
492 #ifdef HAVE_POSIX_FADVISE
493 posix_fadvise(rrd_simple_file->fd, dontneed_start,
494 rrd_file->file_len - dontneed_start,
495 POSIX_FADV_DONTNEED);
496 #endif
497 }
499 #if defined DEBUG && DEBUG > 1
500 mincore_print(rrd_file, "after");
501 #endif
502 #endif /* without madvise and posix_fadvise ist does not make much sense todo anything */
503 }
509 int rrd_close(
510 rrd_file_t *rrd_file)
511 {
512 rrd_simple_file_t *rrd_simple_file;
513 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
514 int ret;
516 #ifdef HAVE_MMAP
517 ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
518 if (ret != 0)
519 rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
520 ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
521 if (ret != 0)
522 rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
523 #endif
524 ret = close(rrd_simple_file->fd);
525 if (ret != 0)
526 rrd_set_error("closing file: %s", rrd_strerror(errno));
527 free(rrd_file->pvt);
528 free(rrd_file);
529 rrd_file = NULL;
530 return ret;
531 }
534 /* Set position of rrd_file. */
536 off_t rrd_seek(
537 rrd_file_t *rrd_file,
538 off_t off,
539 int whence)
540 {
541 off_t ret = 0;
542 rrd_simple_file_t *rrd_simple_file;
543 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
545 #ifdef HAVE_MMAP
546 if (whence == SEEK_SET)
547 rrd_file->pos = off;
548 else if (whence == SEEK_CUR)
549 rrd_file->pos += off;
550 else if (whence == SEEK_END)
551 rrd_file->pos = rrd_file->file_len + off;
552 #else
553 ret = lseek(rrd_simple_file->fd, off, whence);
554 if (ret < 0)
555 rrd_set_error("lseek: %s", rrd_strerror(errno));
556 rrd_file->pos = ret;
557 #endif
558 /* mimic fseek, which returns 0 upon success */
559 return ret < 0; /*XXX: or just ret to mimic lseek */
560 }
563 /* Get current position in rrd_file. */
565 off_t rrd_tell(
566 rrd_file_t *rrd_file)
567 {
568 return rrd_file->pos;
569 }
572 /* Read count bytes into buffer buf, starting at rrd_file->pos.
573 * Returns the number of bytes read or <0 on error. */
575 ssize_t rrd_read(
576 rrd_file_t *rrd_file,
577 void *buf,
578 size_t count)
579 {
580 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
581 #ifdef HAVE_MMAP
582 size_t _cnt = count;
583 ssize_t _surplus;
585 if (rrd_file->pos > rrd_file->file_len || _cnt == 0) /* EOF */
586 return 0;
587 if (buf == NULL)
588 return -1; /* EINVAL */
589 _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
590 if (_surplus > 0) { /* short read */
591 _cnt -= _surplus;
592 }
593 if (_cnt == 0)
594 return 0; /* EOF */
595 buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
597 rrd_file->pos += _cnt; /* mimmic read() semantics */
598 return _cnt;
599 #else
600 ssize_t ret;
602 ret = read(rrd_simple_file->fd, buf, count);
603 if (ret > 0)
604 rrd_file->pos += ret; /* mimmic read() semantics */
605 return ret;
606 #endif
607 }
610 /* Write count bytes from buffer buf to the current position
611 * rrd_file->pos of rrd_simple_file->fd.
612 * Returns the number of bytes written or <0 on error. */
614 ssize_t rrd_write(
615 rrd_file_t *rrd_file,
616 const void *buf,
617 size_t count)
618 {
619 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
620 #ifdef HAVE_MMAP
621 int old_size = rrd_file->file_len;
622 if (count == 0)
623 return 0;
624 if (buf == NULL)
625 return -1; /* EINVAL */
627 if((rrd_file->pos + count) > old_size)
628 {
629 rrd_set_error("attempting to write beyond end of file");
630 return -1;
631 }
632 memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
633 rrd_file->pos += count;
634 return count; /* mimmic write() semantics */
635 #else
636 ssize_t _sz = write(rrd_simple_file->fd, buf, count);
638 if (_sz > 0)
639 rrd_file->pos += _sz;
640 return _sz;
641 #endif
642 }
645 /* flush all data pending to be written to FD. */
647 void rrd_flush(
648 rrd_file_t *rrd_file)
649 {
650 rrd_simple_file_t *rrd_simple_file;
651 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
652 if (fdatasync(rrd_simple_file->fd) != 0) {
653 rrd_set_error("flushing fd %d: %s", rrd_simple_file->fd,
654 rrd_strerror(errno));
655 }
656 }
659 /* Initialize RRD header. */
661 void rrd_init(
662 rrd_t *rrd)
663 {
664 rrd->stat_head = NULL;
665 rrd->ds_def = NULL;
666 rrd->rra_def = NULL;
667 rrd->live_head = NULL;
668 rrd->legacy_last_up = NULL;
669 rrd->rra_ptr = NULL;
670 rrd->pdp_prep = NULL;
671 rrd->cdp_prep = NULL;
672 rrd->rrd_value = NULL;
673 }
676 /* free RRD header data. */
678 #ifdef HAVE_MMAP
679 void rrd_free(
680 rrd_t *rrd)
681 {
682 if (rrd->legacy_last_up) { /* this gets set for version < 3 only */
683 free(rrd->live_head);
684 }
685 }
686 #else
687 void rrd_free(
688 rrd_t *rrd)
689 {
690 free(rrd->live_head);
691 free(rrd->stat_head);
692 free(rrd->ds_def);
693 free(rrd->rra_def);
694 free(rrd->rra_ptr);
695 free(rrd->pdp_prep);
696 free(rrd->cdp_prep);
697 free(rrd->rrd_value);
698 }
699 #endif
702 /* routine used by external libraries to free memory allocated by
703 * rrd library */
705 void rrd_freemem(
706 void *mem)
707 {
708 free(mem);
709 }