+
+#if defined DEBUG && DEBUG > 1
+/* Print list of in-core pages of a the current rrd_file. */
+static
+void mincore_print(
+ rrd_file_t *rrd_file,
+ char *mark)
+{
+ rrd_simple_file_t *rrd_simple_file;
+ rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+#ifdef HAVE_MMAP
+ /* pretty print blocks in core */
+ size_t off;
+ unsigned char *vec;
+ ssize_t _page_size = sysconf(_SC_PAGESIZE);
+
+ off = rrd_file->file_len +
+ ((rrd_file->file_len + _page_size - 1) / _page_size);
+ vec = malloc(off);
+ if (vec != NULL) {
+ memset(vec, 0, off);
+ if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
+ int prev;
+ unsigned is_in = 0, was_in = 0;
+
+ for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
+ is_in = vec[off] & 1; /* if lsb set then is core resident */
+ if (off == 0)
+ was_in = is_in;
+ if (was_in != is_in) {
+ fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
+ was_in ? "" : "not ", vec + prev, off - prev);
+ was_in = is_in;
+ prev = off;
+ }
+ }
+ fprintf(stderr,
+ "%s: %sin core: %p len %ld\n", mark,
+ was_in ? "" : "not ", vec + prev, off - prev);
+ } else
+ fprintf(stderr, "mincore: %s", rrd_strerror(errno));
+ }
+#else
+ fprintf(stderr, "sorry mincore only works with mmap");
+#endif
+}
+#endif /* defined DEBUG && DEBUG > 1 */
+
+/*
+ * get exclusive lock to whole file.
+ * lock gets removed when we close the file
+ *
+ * returns 0 on success
+ */
+int rrd_lock(
+ rrd_file_t *rrd_file)
+{
+ int rcstat;
+ rrd_simple_file_t *rrd_simple_file;
+ rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+
+ {
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+ struct _stat st;
+
+ if (_fstat(rrd_simple_file->fd, &st) == 0) {
+ rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
+ } else {
+ rcstat = -1;
+ }
+#else
+ struct flock lock;
+
+ lock.l_type = F_WRLCK; /* exclusive write lock */
+ lock.l_len = 0; /* whole file */
+ lock.l_start = 0; /* start of file */
+ lock.l_whence = SEEK_SET; /* end of file */
+
+ rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
+#endif
+ }
+
+ return (rcstat);
+}
+
+
+/* drop cache except for the header and the active pages */
+void rrd_dontneed(
+ rrd_file_t *rrd_file,
+ rrd_t *rrd)
+{
+ rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+#if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
+ size_t dontneed_start;
+ size_t rra_start;
+ size_t active_block;
+ unsigned long i;
+ ssize_t _page_size = sysconf(_SC_PAGESIZE);
+
+ if (rrd_file == NULL) {
+#if defined DEBUG && DEBUG
+ fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
+#endif
+ return;
+ }
+
+#if defined DEBUG && DEBUG > 1
+ mincore_print(rrd_file, "before");
+#endif
+
+ /* ignoring errors from RRDs that are smaller then the file_len+rounding */
+ rra_start = rrd_file->header_len;
+ dontneed_start = PAGE_START(rra_start) + _page_size;
+ for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
+ active_block =
+ PAGE_START(rra_start
+ + rrd->rra_ptr[i].cur_row
+ * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
+ if (active_block > dontneed_start) {
+#ifdef USE_MADVISE
+ madvise(rrd_simple_file->file_start + dontneed_start,
+ active_block - dontneed_start - 1, MADV_DONTNEED);
+#endif
+/* in linux at least only fadvise DONTNEED seems to purge pages from cache */
+#ifdef HAVE_POSIX_FADVISE
+ posix_fadvise(rrd_simple_file->fd, dontneed_start,
+ active_block - dontneed_start - 1,
+ POSIX_FADV_DONTNEED);
+#endif
+ }
+ dontneed_start = active_block;
+ /* do not release 'hot' block if update for this RAA will occur
+ * within 10 minutes */
+ if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
+ rrd->live_head->last_up % (rrd->stat_head->pdp_step *
+ rrd->rra_def[i].pdp_cnt) < 10 * 60) {
+ dontneed_start += _page_size;
+ }
+ rra_start +=
+ rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
+ sizeof(rrd_value_t);
+ }
+
+ if (dontneed_start < rrd_file->file_len) {
+#ifdef USE_MADVISE
+ madvise(rrd_simple_file->file_start + dontneed_start,
+ rrd_file->file_len - dontneed_start, MADV_DONTNEED);
+#endif
+#ifdef HAVE_POSIX_FADVISE
+ posix_fadvise(rrd_simple_file->fd, dontneed_start,
+ rrd_file->file_len - dontneed_start,
+ POSIX_FADV_DONTNEED);
+#endif
+ }
+
+#if defined DEBUG && DEBUG > 1
+ mincore_print(rrd_file, "after");
+#endif
+#endif /* without madvise and posix_fadvise ist does not make much sense todo anything */
+}
+
+
+
+
+
+int rrd_close(
+ rrd_file_t *rrd_file)
+{
+ rrd_simple_file_t *rrd_simple_file;
+ rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+ int ret;
+
+#ifdef HAVE_MMAP
+ ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
+ if (ret != 0)
+ rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
+ ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
+ if (ret != 0)
+ rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
+#endif
+ ret = close(rrd_simple_file->fd);
+ if (ret != 0)
+ rrd_set_error("closing file: %s", rrd_strerror(errno));
+ free(rrd_file->pvt);
+ free(rrd_file);
+ rrd_file = NULL;
+ return ret;
+}
+
+
+/* Set position of rrd_file. */
+
+off_t rrd_seek(
+ rrd_file_t *rrd_file,
+ off_t off,
+ int whence)
+{
+ off_t ret = 0;
+ rrd_simple_file_t *rrd_simple_file;
+ rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+
+#ifdef HAVE_MMAP
+ if (whence == SEEK_SET)
+ rrd_file->pos = off;
+ else if (whence == SEEK_CUR)
+ rrd_file->pos += off;
+ else if (whence == SEEK_END)
+ rrd_file->pos = rrd_file->file_len + off;
+#else
+ ret = lseek(rrd_simple_file->fd, off, whence);
+ if (ret < 0)
+ rrd_set_error("lseek: %s", rrd_strerror(errno));
+ rrd_file->pos = ret;
+#endif
+ /* mimic fseek, which returns 0 upon success */
+ return ret < 0; /*XXX: or just ret to mimic lseek */
+}
+
+
+/* Get current position in rrd_file. */
+
+off_t rrd_tell(
+ rrd_file_t *rrd_file)
+{
+ return rrd_file->pos;
+}
+
+
+/* Read count bytes into buffer buf, starting at rrd_file->pos.
+ * Returns the number of bytes read or <0 on error. */
+
+ssize_t rrd_read(
+ rrd_file_t *rrd_file,
+ void *buf,
+ size_t count)
+{
+ rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+#ifdef HAVE_MMAP
+ size_t _cnt = count;
+ ssize_t _surplus;
+
+ if (rrd_file->pos > rrd_file->file_len || _cnt == 0) /* EOF */
+ return 0;
+ if (buf == NULL)
+ return -1; /* EINVAL */
+ _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
+ if (_surplus > 0) { /* short read */
+ _cnt -= _surplus;
+ }
+ if (_cnt == 0)
+ return 0; /* EOF */
+ buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
+
+ rrd_file->pos += _cnt; /* mimmic read() semantics */
+ return _cnt;
+#else
+ ssize_t ret;
+
+ ret = read(rrd_simple_file->fd, buf, count);
+ if (ret > 0)
+ rrd_file->pos += ret; /* mimmic read() semantics */
+ return ret;
+#endif
+}
+
+
+/* Write count bytes from buffer buf to the current position
+ * rrd_file->pos of rrd_simple_file->fd.
+ * Returns the number of bytes written or <0 on error. */
+
+ssize_t rrd_write(
+ rrd_file_t *rrd_file,
+ const void *buf,
+ size_t count)
+{
+ rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+#ifdef HAVE_MMAP
+ size_t old_size = rrd_file->file_len;
+ if (count == 0)
+ return 0;
+ if (buf == NULL)
+ return -1; /* EINVAL */
+
+ if((rrd_file->pos + count) > old_size)
+ {
+ rrd_set_error("attempting to write beyond end of file");
+ return -1;
+ }
+ memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
+ rrd_file->pos += count;
+ return count; /* mimmic write() semantics */
+#else
+ ssize_t _sz = write(rrd_simple_file->fd, buf, count);
+
+ if (_sz > 0)
+ rrd_file->pos += _sz;
+ return _sz;
+#endif
+}
+
+
+/* flush all data pending to be written to FD. */
+
+void rrd_flush(
+ rrd_file_t *rrd_file)
+{
+#ifndef WIN32
+ rrd_simple_file_t *rrd_simple_file;
+ rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
+ if (fdatasync(rrd_simple_file->fd) != 0) {
+ rrd_set_error("flushing fd %d: %s", rrd_simple_file->fd,
+ rrd_strerror(errno));
+ }
+#endif
+}
+
+
+/* Initialize RRD header. */
+
+void rrd_init(
+ rrd_t *rrd)