1 /*****************************************************************************
2 * RRDtool 1.4.2 Copyright by Tobi Oetiker, 1997-2009
3 *****************************************************************************
4 * rrd_open.c Open an RRD File
5 *****************************************************************************
6 * $Id: rrd_open.c 1970 2009-11-15 11:54:23Z oetiker $
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 if ( write(rrd_simple_file->fd, "\0", 1) == -1){ /* poke */
220 rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno));
221 goto out_close;
222 }
223 lseek(rrd_simple_file->fd, 0, SEEK_SET);
224 }
225 #ifdef HAVE_POSIX_FADVISE
226 /* In general we need no read-ahead when dealing with rrd_files.
227 When we stop reading, it is highly unlikely that we start up again.
228 In this manner we actually save time and diskaccess (and buffer cache).
229 Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
230 posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM);
231 #endif
233 /*
234 if (rdwr & RRD_READWRITE)
235 {
236 if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) {
237 rrd_set_error("failed to disable the stream buffer\n");
238 return (-1);
239 }
240 }
241 */
243 #ifdef HAVE_MMAP
244 data = mmap(0, rrd_file->file_len,
245 rrd_simple_file->mm_prot, rrd_simple_file->mm_flags,
246 rrd_simple_file->fd, offset);
248 /* lets see if the first read worked */
249 if (data == MAP_FAILED) {
250 rrd_set_error("mmaping file '%s': %s", file_name,
251 rrd_strerror(errno));
252 goto out_close;
253 }
254 rrd_simple_file->file_start = data;
255 if (rdwr & RRD_CREAT) {
256 memset(data, DNAN, newfile_size - 1);
257 goto out_done;
258 }
259 #endif
260 if (rdwr & RRD_CREAT)
261 goto out_done;
262 #ifdef USE_MADVISE
263 if (rdwr & RRD_COPY) {
264 /* We will read everything in a moment (copying) */
265 madvise(data, rrd_file->file_len, MADV_WILLNEED );
266 madvise(data, rrd_file->file_len, MADV_SEQUENTIAL );
267 } else {
268 /* We do not need to read anything in for the moment */
269 madvise(data, rrd_file->file_len, MADV_RANDOM);
270 /* the stat_head will be needed soonish, so hint accordingly */
271 madvise(data, sizeof(stat_head_t), MADV_WILLNEED);
272 madvise(data, sizeof(stat_head_t), MADV_RANDOM);
273 }
274 #endif
276 __rrd_read(rrd->stat_head, stat_head_t,
277 1);
279 /* lets do some test if we are on track ... */
280 if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) {
281 rrd_set_error("'%s' is not an RRD file", file_name);
282 goto out_nullify_head;
283 }
285 if (rrd->stat_head->float_cookie != FLOAT_COOKIE) {
286 rrd_set_error("This RRD was created on another architecture");
287 goto out_nullify_head;
288 }
290 version = atoi(rrd->stat_head->version);
292 if (version > atoi(RRD_VERSION)) {
293 rrd_set_error("can't handle RRD file version %s",
294 rrd->stat_head->version);
295 goto out_nullify_head;
296 }
297 #if defined USE_MADVISE
298 /* the ds_def will be needed soonish, so hint accordingly */
299 madvise(data + PAGE_START(offset),
300 sizeof(ds_def_t) * rrd->stat_head->ds_cnt, MADV_WILLNEED);
301 #endif
302 __rrd_read(rrd->ds_def, ds_def_t,
303 rrd->stat_head->ds_cnt);
305 #if defined USE_MADVISE
306 /* the rra_def will be needed soonish, so hint accordingly */
307 madvise(data + PAGE_START(offset),
308 sizeof(rra_def_t) * rrd->stat_head->rra_cnt, MADV_WILLNEED);
309 #endif
310 __rrd_read(rrd->rra_def, rra_def_t,
311 rrd->stat_head->rra_cnt);
313 /* handle different format for the live_head */
314 if (version < 3) {
315 rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t));
316 if (rrd->live_head == NULL) {
317 rrd_set_error("live_head_t malloc");
318 goto out_close;
319 }
320 #if defined USE_MADVISE
321 /* the live_head will be needed soonish, so hint accordingly */
322 madvise(data + PAGE_START(offset), sizeof(time_t), MADV_WILLNEED);
323 #endif
324 __rrd_read(rrd->legacy_last_up, time_t,
325 1);
327 rrd->live_head->last_up = *rrd->legacy_last_up;
328 rrd->live_head->last_up_usec = 0;
329 } else {
330 #if defined USE_MADVISE
331 /* the live_head will be needed soonish, so hint accordingly */
332 madvise(data + PAGE_START(offset),
333 sizeof(live_head_t), MADV_WILLNEED);
334 #endif
335 __rrd_read(rrd->live_head, live_head_t,
336 1);
337 }
338 __rrd_read(rrd->pdp_prep, pdp_prep_t,
339 rrd->stat_head->ds_cnt);
340 __rrd_read(rrd->cdp_prep, cdp_prep_t,
341 rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt);
342 __rrd_read(rrd->rra_ptr, rra_ptr_t,
343 rrd->stat_head->rra_cnt);
345 rrd_file->header_len = offset;
346 rrd_file->pos = offset;
348 {
349 unsigned long row_cnt = 0;
351 for (ui=0; ui<rrd->stat_head->rra_cnt; ui++)
352 row_cnt += rrd->rra_def[ui].row_cnt;
354 size_t correct_len = rrd_file->header_len +
355 sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt;
357 if (correct_len > rrd_file->file_len)
358 {
359 rrd_set_error("'%s' is too small (should be %ld bytes)",
360 file_name, (long long) correct_len);
361 goto out_nullify_head;
362 }
363 }
365 out_done:
366 return (rrd_file);
367 out_nullify_head:
368 rrd->stat_head = NULL;
369 out_close:
370 #ifdef HAVE_MMAP
371 if (data != MAP_FAILED)
372 munmap(data, rrd_file->file_len);
373 #endif
375 close(rrd_simple_file->fd);
376 out_free:
377 free(rrd_file->pvt);
378 free(rrd_file);
379 return NULL;
380 }
383 #if defined DEBUG && DEBUG > 1
384 /* Print list of in-core pages of a the current rrd_file. */
385 static
386 void mincore_print(
387 rrd_file_t *rrd_file,
388 char *mark)
389 {
390 rrd_simple_file_t *rrd_simple_file;
391 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
392 #ifdef HAVE_MMAP
393 /* pretty print blocks in core */
394 size_t off;
395 unsigned char *vec;
396 ssize_t _page_size = sysconf(_SC_PAGESIZE);
398 off = rrd_file->file_len +
399 ((rrd_file->file_len + _page_size - 1) / _page_size);
400 vec = malloc(off);
401 if (vec != NULL) {
402 memset(vec, 0, off);
403 if (mincore(rrd_simple_file->file_start, rrd_file->file_len, vec) == 0) {
404 int prev;
405 unsigned is_in = 0, was_in = 0;
407 for (off = 0, prev = 0; off < rrd_file->file_len; ++off) {
408 is_in = vec[off] & 1; /* if lsb set then is core resident */
409 if (off == 0)
410 was_in = is_in;
411 if (was_in != is_in) {
412 fprintf(stderr, "%s: %sin core: %p len %ld\n", mark,
413 was_in ? "" : "not ", vec + prev, off - prev);
414 was_in = is_in;
415 prev = off;
416 }
417 }
418 fprintf(stderr,
419 "%s: %sin core: %p len %ld\n", mark,
420 was_in ? "" : "not ", vec + prev, off - prev);
421 } else
422 fprintf(stderr, "mincore: %s", rrd_strerror(errno));
423 }
424 #else
425 fprintf(stderr, "sorry mincore only works with mmap");
426 #endif
427 }
428 #endif /* defined DEBUG && DEBUG > 1 */
430 /*
431 * get exclusive lock to whole file.
432 * lock gets removed when we close the file
433 *
434 * returns 0 on success
435 */
436 int rrd_lock(
437 rrd_file_t *rrd_file)
438 {
439 int rcstat;
440 rrd_simple_file_t *rrd_simple_file;
441 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
443 {
444 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
445 struct _stat st;
447 if (_fstat(rrd_simple_file->fd, &st) == 0) {
448 rcstat = _locking(rrd_simple_file->fd, _LK_NBLCK, st.st_size);
449 } else {
450 rcstat = -1;
451 }
452 #else
453 struct flock lock;
455 lock.l_type = F_WRLCK; /* exclusive write lock */
456 lock.l_len = 0; /* whole file */
457 lock.l_start = 0; /* start of file */
458 lock.l_whence = SEEK_SET; /* end of file */
460 rcstat = fcntl(rrd_simple_file->fd, F_SETLK, &lock);
461 #endif
462 }
464 return (rcstat);
465 }
468 /* drop cache except for the header and the active pages */
469 void rrd_dontneed(
470 rrd_file_t *rrd_file,
471 rrd_t *rrd)
472 {
473 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
474 #if defined USE_MADVISE || defined HAVE_POSIX_FADVISE
475 size_t dontneed_start;
476 size_t rra_start;
477 size_t active_block;
478 size_t i;
479 ssize_t _page_size = sysconf(_SC_PAGESIZE);
481 if (rrd_file == NULL) {
482 #if defined DEBUG && DEBUG
483 fprintf (stderr, "rrd_dontneed: Argument 'rrd_file' is NULL.\n");
484 #endif
485 return;
486 }
488 #if defined DEBUG && DEBUG > 1
489 mincore_print(rrd_file, "before");
490 #endif
492 /* ignoring errors from RRDs that are smaller then the file_len+rounding */
493 rra_start = rrd_file->header_len;
494 dontneed_start = PAGE_START(rra_start) + _page_size;
495 for (i = 0; i < rrd->stat_head->rra_cnt; ++i) {
496 active_block =
497 PAGE_START(rra_start
498 + rrd->rra_ptr[i].cur_row
499 * rrd->stat_head->ds_cnt * sizeof(rrd_value_t));
500 if (active_block > dontneed_start) {
501 #ifdef USE_MADVISE
502 madvise(rrd_simple_file->file_start + dontneed_start,
503 active_block - dontneed_start - 1, MADV_DONTNEED);
504 #endif
505 /* in linux at least only fadvise DONTNEED seems to purge pages from cache */
506 #ifdef HAVE_POSIX_FADVISE
507 posix_fadvise(rrd_simple_file->fd, dontneed_start,
508 active_block - dontneed_start - 1,
509 POSIX_FADV_DONTNEED);
510 #endif
511 }
512 dontneed_start = active_block;
513 /* do not release 'hot' block if update for this RAA will occur
514 * within 10 minutes */
515 if (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt -
516 rrd->live_head->last_up % (rrd->stat_head->pdp_step *
517 rrd->rra_def[i].pdp_cnt) < 10 * 60) {
518 dontneed_start += _page_size;
519 }
520 rra_start +=
521 rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt *
522 sizeof(rrd_value_t);
523 }
525 if (dontneed_start < rrd_file->file_len) {
526 #ifdef USE_MADVISE
527 madvise(rrd_simple_file->file_start + dontneed_start,
528 rrd_file->file_len - dontneed_start, MADV_DONTNEED);
529 #endif
530 #ifdef HAVE_POSIX_FADVISE
531 posix_fadvise(rrd_simple_file->fd, dontneed_start,
532 rrd_file->file_len - dontneed_start,
533 POSIX_FADV_DONTNEED);
534 #endif
535 }
537 #if defined DEBUG && DEBUG > 1
538 mincore_print(rrd_file, "after");
539 #endif
540 #endif /* without madvise and posix_fadvise it does not make much sense todo anything */
541 }
547 int rrd_close(
548 rrd_file_t *rrd_file)
549 {
550 rrd_simple_file_t *rrd_simple_file;
551 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
552 int ret;
554 #ifdef HAVE_MMAP
555 ret = msync(rrd_simple_file->file_start, rrd_file->file_len, MS_ASYNC);
556 if (ret != 0)
557 rrd_set_error("msync rrd_file: %s", rrd_strerror(errno));
558 ret = munmap(rrd_simple_file->file_start, rrd_file->file_len);
559 if (ret != 0)
560 rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno));
561 #endif
562 ret = close(rrd_simple_file->fd);
563 if (ret != 0)
564 rrd_set_error("closing file: %s", rrd_strerror(errno));
565 free(rrd_file->pvt);
566 free(rrd_file);
567 rrd_file = NULL;
568 return ret;
569 }
572 /* Set position of rrd_file. */
574 off_t rrd_seek(
575 rrd_file_t *rrd_file,
576 off_t off,
577 int whence)
578 {
579 off_t ret = 0;
580 rrd_simple_file_t *rrd_simple_file;
581 rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
583 #ifdef HAVE_MMAP
584 if (whence == SEEK_SET)
585 rrd_file->pos = off;
586 else if (whence == SEEK_CUR)
587 rrd_file->pos += off;
588 else if (whence == SEEK_END)
589 rrd_file->pos = rrd_file->file_len + off;
590 #else
591 ret = lseek(rrd_simple_file->fd, off, whence);
592 if (ret < 0)
593 rrd_set_error("lseek: %s", rrd_strerror(errno));
594 rrd_file->pos = ret;
595 #endif
596 /* mimic fseek, which returns 0 upon success */
597 return ret < 0; /*XXX: or just ret to mimic lseek */
598 }
601 /* Get current position in rrd_file. */
603 off_t rrd_tell(
604 rrd_file_t *rrd_file)
605 {
606 return rrd_file->pos;
607 }
610 /* Read count bytes into buffer buf, starting at rrd_file->pos.
611 * Returns the number of bytes read or <0 on error. */
613 ssize_t rrd_read(
614 rrd_file_t *rrd_file,
615 void *buf,
616 size_t count)
617 {
618 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
619 #ifdef HAVE_MMAP
620 size_t _cnt = count;
621 ssize_t _surplus;
623 if (rrd_file->pos > rrd_file->file_len || _cnt == 0) /* EOF */
624 return 0;
625 if (buf == NULL)
626 return -1; /* EINVAL */
627 _surplus = rrd_file->pos + _cnt - rrd_file->file_len;
628 if (_surplus > 0) { /* short read */
629 _cnt -= _surplus;
630 }
631 if (_cnt == 0)
632 return 0; /* EOF */
633 buf = memcpy(buf, rrd_simple_file->file_start + rrd_file->pos, _cnt);
635 rrd_file->pos += _cnt; /* mimmic read() semantics */
636 return _cnt;
637 #else
638 ssize_t ret;
640 ret = read(rrd_simple_file->fd, buf, count);
641 if (ret > 0)
642 rrd_file->pos += ret; /* mimmic read() semantics */
643 return ret;
644 #endif
645 }
648 /* Write count bytes from buffer buf to the current position
649 * rrd_file->pos of rrd_simple_file->fd.
650 * Returns the number of bytes written or <0 on error. */
652 ssize_t rrd_write(
653 rrd_file_t *rrd_file,
654 const void *buf,
655 size_t count)
656 {
657 rrd_simple_file_t *rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt;
658 #ifdef HAVE_MMAP
659 size_t old_size = rrd_file->file_len;
660 if (count == 0)
661 return 0;
662 if (buf == NULL)
663 return -1; /* EINVAL */
665 if((rrd_file->pos + count) > old_size)
666 {
667 rrd_set_error("attempting to write beyond end of file");
668 return -1;
669 }
670 memcpy(rrd_simple_file->file_start + rrd_file->pos, buf, count);
671 rrd_file->pos += count;
672 return count; /* mimmic write() semantics */
673 #else
674 ssize_t _sz = write(rrd_simple_file->fd, buf, count);
676 if (_sz > 0)
677 rrd_file->pos += _sz;
678 return _sz;
679 #endif
680 }
683 /* this is a leftover from the old days, it serves no purpose
684 and is therefore turned into a no-op */
685 void rrd_flush(
686 rrd_file_t *rrd_file __attribute__((unused)))
687 {
688 }
690 /* Initialize RRD header. */
692 void rrd_init(
693 rrd_t *rrd)
694 {
695 rrd->stat_head = NULL;
696 rrd->ds_def = NULL;
697 rrd->rra_def = NULL;
698 rrd->live_head = NULL;
699 rrd->legacy_last_up = NULL;
700 rrd->rra_ptr = NULL;
701 rrd->pdp_prep = NULL;
702 rrd->cdp_prep = NULL;
703 rrd->rrd_value = NULL;
704 }
707 /* free RRD header data. */
709 #ifdef HAVE_MMAP
710 void rrd_free(
711 rrd_t *rrd)
712 {
713 if (rrd->legacy_last_up) { /* this gets set for version < 3 only */
714 free(rrd->live_head);
715 }
716 }
717 #else
718 void rrd_free(
719 rrd_t *rrd)
720 {
721 free(rrd->live_head);
722 free(rrd->stat_head);
723 free(rrd->ds_def);
724 free(rrd->rra_def);
725 free(rrd->rra_ptr);
726 free(rrd->pdp_prep);
727 free(rrd->cdp_prep);
728 free(rrd->rrd_value);
729 }
730 #endif
733 /* routine used by external libraries to free memory allocated by
734 * rrd library */
736 void rrd_freemem(
737 void *mem)
738 {
739 free(mem);
740 }
742 /*
743 * rra_update informs us about the RRAs being updated
744 * The low level storage API may use this information for
745 * aligning RRAs within stripes, or other performance enhancements
746 */
747 void rrd_notify_row(
748 rrd_file_t *rrd_file __attribute__((unused)),
749 int rra_idx __attribute__((unused)),
750 unsigned long rra_row __attribute__((unused)),
751 time_t rra_time __attribute__((unused)))
752 {
753 }
755 /*
756 * This function is called when creating a new RRD
757 * The storage implementation can use this opportunity to select
758 * a sensible starting row within the file.
759 * The default implementation is random, to ensure that all RRAs
760 * don't change to a new disk block at the same time
761 */
762 unsigned long rrd_select_initial_row(
763 rrd_file_t *rrd_file __attribute__((unused)),
764 int rra_idx __attribute__((unused)),
765 rra_def_t *rra
766 )
767 {
768 return rrd_random() % rra->row_cnt;
769 }