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