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>
13 #include "rrd_tool.h"
14 #include "unused.h"
15 #define MEMBLK 8192
17 #ifdef WIN32
18 # define random() rand()
19 # define srandom(x) srand(x)
20 # define getpid() 0
22 #define _LK_UNLCK 0 /* Unlock */
23 #define _LK_LOCK 1 /* Lock */
24 #define _LK_NBLCK 2 /* Non-blocking lock */
25 #define _LK_RLCK 3 /* Lock for read only */
26 #define _LK_NBRLCK 4 /* Non-blocking lock for read only */
29 #define LK_UNLCK _LK_UNLCK
30 #define LK_LOCK _LK_LOCK
31 #define LK_NBLCK _LK_NBLCK
32 #define LK_RLCK _LK_RLCK
33 #define LK_NBRLCK _LK_NBRLCK
34 #endif
36 /* DEBUG 2 prints information obtained via mincore(2) */
37 #define DEBUG 1
38 /* do not calculate exact madvise hints but assume 1 page for headers and
39 * set DONTNEED for the rest, which is assumed to be data */
40 /* Avoid calling madvise on areas that were already hinted. May be benefical if
41 * your syscalls are very slow */
43 #ifdef HAVE_MMAP
44 /* the cast to void* is there to avoid this warning seen on ia64 with certain
45 versions of gcc: 'cast increases required alignment of target type'
46 */
47 #define __rrd_read(dst, dst_t, cnt) { \
48 size_t wanted = sizeof(dst_t)*(cnt); \
49 if (offset + wanted > rrd_file->file_len) { \
50 rrd_set_error("reached EOF while loading header " #dst); \
51 goto out_nullify_head; \
52 } \
53 (dst) = (dst_t*)(void*) (data + offset); \
54 offset += wanted; \
55 }
56 #else
57 #define __rrd_read(dst, dst_t, cnt) { \
58 size_t wanted = sizeof(dst_t)*(cnt); \
59 size_t got; \
60 if ((dst = (dst_t*)malloc(wanted)) == NULL) { \
61 rrd_set_error(#dst " malloc"); \
62 goto out_nullify_head; \
63 } \
64 got = read (rrd_simple_file->fd, dst, wanted); \
65 if (got != wanted) { \
66 rrd_set_error("short read while reading header " #dst); \
67 goto out_nullify_head; \
68 } \
69 offset += got; \
70 }
71 #endif
73 /* get the address of the start of this page */
74 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
75 #ifndef PAGE_START
76 #define PAGE_START(addr) ((addr)&(~(_page_size-1)))
77 #endif
78 #endif
80 long int rra_random_row(
81 rra_def_t *);
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 = \
118 sizeof(stat_head_t) + \
119 sizeof(ds_def_t) * rrd->stat_head->ds_cnt + \
120 sizeof(rra_def_t) * rrd->stat_head->rra_cnt + \
121 sizeof(time_t) + \
122 sizeof(live_head_t) + \
123 sizeof(pdp_prep_t) * rrd->stat_head->ds_cnt + \
124 sizeof(cdp_prep_t) * rrd->stat_head->ds_cnt * rrd->stat_head->rra_cnt + \
125 sizeof(rra_ptr_t) * rrd->stat_head->rra_cnt;
127 value_cnt = 0;
128 for (ui = 0; ui < rrd->stat_head->rra_cnt; ui++)
129 value_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[ui].row_cnt;
131 data_len = sizeof(rrd_value_t) * value_cnt;
133 newfile_size = header_len + data_len;
134 }
136 rrd_file = (rrd_file_t*)malloc(sizeof(rrd_file_t));
137 if (rrd_file == NULL) {
138 rrd_set_error("allocating rrd_file descriptor for '%s'", file_name);
139 return NULL;
140 }
141 memset(rrd_file, 0, sizeof(rrd_file_t));
143 rrd_file->pvt = malloc(sizeof(rrd_simple_file_t));
144 if(rrd_file->pvt == NULL) {
145 rrd_set_error("allocating rrd_simple_file for '%s'", file_name);
146 return NULL;
147 }
148 memset(rrd_file->pvt, 0, sizeof(rrd_simple_file_t));
149 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
151 #ifdef DEBUG
152 if ((rdwr & (RRD_READONLY | RRD_READWRITE)) ==
153 (RRD_READONLY | RRD_READWRITE)) {
154 /* Both READONLY and READWRITE were given, which is invalid. */
155 rrd_set_error("in read/write request mask");
156 exit(-1);
157 }
158 #endif
160 #ifdef HAVE_MMAP
161 rrd_simple_file->mm_prot = PROT_READ;
162 rrd_simple_file->mm_flags = 0;
163 #endif
165 if (rdwr & RRD_READONLY) {
166 flags |= O_RDONLY;
167 #ifdef HAVE_MMAP
168 rrd_simple_file->mm_flags = MAP_PRIVATE;
169 # ifdef MAP_NORESERVE
170 rrd_simple_file->mm_flags |= MAP_NORESERVE; /* readonly, so no swap backing needed */
171 # endif
172 #endif
173 } else {
174 if (rdwr & RRD_READWRITE) {
175 flags |= O_RDWR;
176 #ifdef HAVE_MMAP
177 rrd_simple_file->mm_flags = MAP_SHARED;
178 rrd_simple_file->mm_prot |= PROT_WRITE;
179 #endif
180 }
181 if (rdwr & RRD_CREAT) {
182 flags |= (O_CREAT | O_TRUNC);
183 }
184 }
185 if (rdwr & RRD_READAHEAD) {
186 #ifdef MAP_POPULATE
187 rrd_simple_file->mm_flags |= MAP_POPULATE; /* populate ptes and data */
188 #endif
189 #if defined MAP_NONBLOCK
190 rrd_simple_file->mm_flags |= MAP_NONBLOCK; /* just populate ptes */
191 #endif
192 }
193 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
194 flags |= O_BINARY;
195 #endif
197 if ((rrd_simple_file->fd = open(file_name, flags, 0666)) < 0) {
198 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
199 goto out_free;
200 }
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
363 close(rrd_simple_file->fd);
364 out_free:
365 free(rrd_file->pvt);
366 free(rrd_file);
367 return NULL;
368 }
371 #if defined DEBUG && DEBUG > 1
372 /* Print list of in-core pages of a the current rrd_file. */
373 static
374 void mincore_print(
375 rrd_file_t *rrd_file,
376 char *mark)
377 {
378 rrd_simple_file_t *rrd_simple_file;
379 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
380 #ifdef HAVE_MMAP
381 /* pretty print blocks in core */
382 size_t off;
383 unsigned char *vec;
384 ssize_t _page_size = sysconf(_SC_PAGESIZE);
386 off = rrd_file->file_len +
387 ((rrd_file->file_len + _page_size - 1) / _page_size);
388 vec = malloc(off);
389 if (vec != NULL) {
390 memset(vec, 0, off);
391 if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
392 int prev;
393 unsigned is_in = 0, was_in = 0;
395 for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
396 is_in = vec[off] & 1; /* if lsb set then is core resident */
397 if (off == 0)
398 was_in = is_in;
399 if (was_in != is_in) {
400 fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
401 was_in ? "" : "not ", vec + prev, off - prev);
402 was_in = is_in;
403 prev = off;
404 }
405 }
406 fprintf(stderr,
407 "%s: %sin core: %p len %ld\n", mark,
408 was_in ? "" : "not ", vec + prev, off - prev);
409 } else
410 fprintf(stderr, "mincore: %s", rrd_strerror(errno));
411 }
412 #else
413 fprintf(stderr, "sorry mincore only works with mmap");
414 #endif
415 }
416 #endif /* defined DEBUG && DEBUG > 1 */
418 /*
419 * get exclusive lock to whole file.
420 * lock gets removed when we close the file
421 *
422 * returns 0 on success
423 */
424 int rrd_lock(
425 rrd_file_t *rrd_file)
426 {
427 int rcstat;
428 rrd_simple_file_t *rrd_simple_file;
429 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
431 {
432 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
433 struct _stat st;
435 if (_fstat(rrd_simple_file->fd, &st) == 0) {
436 rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
437 } else {
438 rcstat = -1;
439 }
440 #else
441 struct flock lock;
443 lock.l_type = F_WRLCK; /* exclusive write lock */
444 lock.l_len = 0; /* whole file */
445 lock.l_start = 0; /* start of file */
446 lock.l_whence = SEEK_SET; /* end of file */
448 rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
449 #endif
450 }
452 return (rcstat);
453 }
456 /* drop cache except for the header and the active pages */
457 void rrd_dontneed(
458 rrd_file_t *rrd_file,
459 rrd_t *rrd)
460 {
461 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
462 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
463 size_t dontneed_start;
464 size_t rra_start;
465 size_t active_block;
466 unsigned long i;
467 ssize_t _page_size = sysconf(_SC_PAGESIZE);
469 if (rrd_file == NULL) {
470 #if defined DEBUG && DEBUG
471 fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
472 #endif
473 return;
474 }
476 #if defined DEBUG && DEBUG > 1
477 mincore_print(rrd_file, "before");
478 #endif
480 /* ignoring errors from RRDs that are smaller then the file_len+rounding */
481 rra_start = rrd_file->header_len;
482 dontneed_start = PAGE_START(rra_start) + _page_size;
483 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
484 active_block =
485 PAGE_START(rra_start
486 + rrd->rra_ptr[i].cur_row
487 * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
488 if (active_block > dontneed_start) {
489 #ifdef USE_MADVISE
490 madvise(rrd_simple_file->file_start + dontneed_start,
491 active_block - dontneed_start - 1, MADV_DONTNEED);
492 #endif
493 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
494 #ifdef HAVE_POSIX_FADVISE
495 posix_fadvise(rrd_simple_file->fd, dontneed_start,
496 active_block - dontneed_start - 1,
497 POSIX_FADV_DONTNEED);
498 #endif
499 }
500 dontneed_start = active_block;
501 /* do not release 'hot' block if update for this RAA will occur
502 * within 10 minutes */
503 if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
504 rrd->live_head->last_up % (rrd->stat_head->pdp_step *
505 rrd->rra_def[i].pdp_cnt) < 10 * 60) {
506 dontneed_start += _page_size;
507 }
508 rra_start +=
509 rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
510 sizeof(rrd_value_t);
511 }
513 if (dontneed_start < rrd_file->file_len) {
514 #ifdef USE_MADVISE
515 madvise(rrd_simple_file->file_start + dontneed_start,
516 rrd_file->file_len - dontneed_start, MADV_DONTNEED);
517 #endif
518 #ifdef HAVE_POSIX_FADVISE
519 posix_fadvise(rrd_simple_file->fd, dontneed_start,
520 rrd_file->file_len - dontneed_start,
521 POSIX_FADV_DONTNEED);
522 #endif
523 }
525 #if defined DEBUG && DEBUG > 1
526 mincore_print(rrd_file, "after");
527 #endif
528 #endif /* without madvise and posix_fadvise ist does not make much sense todo anything */
529 }
535 int rrd_close(
536 rrd_file_t *rrd_file)
537 {
538 rrd_simple_file_t *rrd_simple_file;
539 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
540 int ret;
542 #ifdef HAVE_MMAP
543 ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
544 if (ret != 0)
545 rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
546 ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
547 if (ret != 0)
548 rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
549 #endif
550 ret = close(rrd_simple_file->fd);
551 if (ret != 0)
552 rrd_set_error("closing file: %s", rrd_strerror(errno));
553 free(rrd_file->pvt);
554 free(rrd_file);
555 rrd_file = NULL;
556 return ret;
557 }
560 /* Set position of rrd_file. */
562 off_t rrd_seek(
563 rrd_file_t *rrd_file,
564 off_t off,
565 int whence)
566 {
567 off_t ret = 0;
568 rrd_simple_file_t *rrd_simple_file;
569 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
571 #ifdef HAVE_MMAP
572 if (whence == SEEK_SET)
573 rrd_file->pos = off;
574 else if (whence == SEEK_CUR)
575 rrd_file->pos += off;
576 else if (whence == SEEK_END)
577 rrd_file->pos = rrd_file->file_len + off;
578 #else
579 ret = lseek(rrd_simple_file->fd, off, whence);
580 if (ret < 0)
581 rrd_set_error("lseek: %s", rrd_strerror(errno));
582 rrd_file->pos = ret;
583 #endif
584 /* mimic fseek, which returns 0 upon success */
585 return ret < 0; /*XXX: or just ret to mimic lseek */
586 }
589 /* Get current position in rrd_file. */
591 off_t rrd_tell(
592 rrd_file_t *rrd_file)
593 {
594 return rrd_file->pos;
595 }
598 /* Read count bytes into buffer buf, starting at rrd_file->pos.
599 * Returns the number of bytes read or <0 on error. */
601 ssize_t rrd_read(
602 rrd_file_t *rrd_file,
603 void *buf,
604 size_t count)
605 {
606 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
607 #ifdef HAVE_MMAP
608 size_t _cnt = count;
609 ssize_t _surplus;
611 if (rrd_file->pos > rrd_file->file_len || _cnt == 0) /* EOF */
612 return 0;
613 if (buf == NULL)
614 return -1; /* EINVAL */
615 _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
616 if (_surplus > 0) { /* short read */
617 _cnt -= _surplus;
618 }
619 if (_cnt == 0)
620 return 0; /* EOF */
621 buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
623 rrd_file->pos += _cnt; /* mimmic read() semantics */
624 return _cnt;
625 #else
626 ssize_t ret;
628 ret = read(rrd_simple_file->fd, buf, count);
629 if (ret > 0)
630 rrd_file->pos += ret; /* mimmic read() semantics */
631 return ret;
632 #endif
633 }
636 /* Write count bytes from buffer buf to the current position
637 * rrd_file->pos of rrd_simple_file->fd.
638 * Returns the number of bytes written or <0 on error. */
640 ssize_t rrd_write(
641 rrd_file_t *rrd_file,
642 const void *buf,
643 size_t count)
644 {
645 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
646 #ifdef HAVE_MMAP
647 size_t old_size = rrd_file->file_len;
648 if (count == 0)
649 return 0;
650 if (buf == NULL)
651 return -1; /* EINVAL */
653 if((rrd_file->pos + count) > old_size)
654 {
655 rrd_set_error("attempting to write beyond end of file");
656 return -1;
657 }
658 memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
659 rrd_file->pos += count;
660 return count; /* mimmic write() semantics */
661 #else
662 ssize_t _sz = write(rrd_simple_file->fd, buf, count);
664 if (_sz > 0)
665 rrd_file->pos += _sz;
666 return _sz;
667 #endif
668 }
671 /* flush all data pending to be written to FD. */
673 void rrd_flush(
674 rrd_file_t *rrd_file)
675 {
676 #ifndef WIN32
677 rrd_simple_file_t *rrd_simple_file;
678 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
679 if (fdatasync(rrd_simple_file->fd) != 0) {
680 rrd_set_error("flushing fd %d: %s", rrd_simple_file->fd,
681 rrd_strerror(errno));
682 }
683 #endif
684 }
687 /* Initialize RRD header. */
689 void rrd_init(
690 rrd_t *rrd)
691 {
692 rrd->stat_head = NULL;
693 rrd->ds_def = NULL;
694 rrd->rra_def = NULL;
695 rrd->live_head = NULL;
696 rrd->legacy_last_up = NULL;
697 rrd->rra_ptr = NULL;
698 rrd->pdp_prep = NULL;
699 rrd->cdp_prep = NULL;
700 rrd->rrd_value = NULL;
701 }
704 /* free RRD header data. */
706 #ifdef HAVE_MMAP
707 void rrd_free(
708 rrd_t *rrd)
709 {
710 if (rrd->legacy_last_up) { /* this gets set for version < 3 only */
711 free(rrd->live_head);
712 }
713 }
714 #else
715 void rrd_free(
716 rrd_t *rrd)
717 {
718 free(rrd->live_head);
719 free(rrd->stat_head);
720 free(rrd->ds_def);
721 free(rrd->rra_def);
722 free(rrd->rra_ptr);
723 free(rrd->pdp_prep);
724 free(rrd->cdp_prep);
725 free(rrd->rrd_value);
726 }
727 #endif
730 /* routine used by external libraries to free memory allocated by
731 * rrd library */
733 void rrd_freemem(
734 void *mem)
735 {
736 free(mem);
737 }
739 /*
740 * rra_update informs us about the RRAs being updated
741 * The low level storage API may use this information for
742 * aligning RRAs within stripes, or other performance enhancements
743 */
744 void rrd_notify_row(
745 rrd_file_t *rrd_file __attribute__((unused)),
746 int rra_idx __attribute__((unused)),
747 unsigned long rra_row __attribute__((unused)),
748 time_t rra_time __attribute__((unused)))
749 {
750 }
752 /*
753 * This function is called when creating a new RRD
754 * The storage implementation can use this opportunity to select
755 * a sensible starting row within the file.
756 * The default implementation is random, to ensure that all RRAs
757 * don't change to a new disk block at the same time
758 */
759 unsigned long rrd_select_initial_row(
760 rrd_file_t *rrd_file __attribute__((unused)),
761 int rra_idx __attribute__((unused)),
762 rra_def_t *rra
763 )
764 {
765 return rra_random_row(rra);
766 }
768 static int rand_init = 0;
770 long int rra_random_row(
771 rra_def_t *rra)
772 {
773 if (!rand_init) {
774 srandom((unsigned int) time(NULL) + (unsigned int) getpid());
775 rand_init++;
776 }
778 return random() % rra->row_cnt;
779 }