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