rrd_file_t *rrd_open( const char *const file_name, rrd_t *rrd, unsigned rdwr) { unsigned long ui; int flags = 0; int version; #ifdef HAVE_MMAP char *data = MAP_FAILED; #endif off_t offset = 0; struct stat statb; rrd_file_t *rrd_file = NULL; rrd_simple_file_t *rrd_simple_file = NULL; size_t newfile_size = 0; size_t header_len, value_cnt, data_len; /* Are we creating a new file? */ if((rdwr & RRD_CREAT) && (rrd->stat_head != NULL)) { header_len = rrd_get_header_size(rrd); value_cnt = 0; for (ui = 0; ui < rrd->stat_head->rra_cnt; ui++) value_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[ui].row_cnt; data_len = sizeof(rrd_value_t) * value_cnt; newfile_size = header_len + data_len; } rrd_file = (rrd_file_t*)malloc(sizeof(rrd_file_t)); if (rrd_file == NULL) { rrd_set_error("allocating rrd_file descriptor for '%s'", file_name); return NULL; } memset(rrd_file, 0, sizeof(rrd_file_t)); rrd_file->rrd = rrd; rrd_file->pvt = malloc(sizeof(rrd_simple_file_t)); if(rrd_file->pvt == NULL) { rrd_set_error("allocating rrd_simple_file for '%s'", file_name); return NULL; } memset(rrd_file->pvt, 0, sizeof(rrd_simple_file_t)); rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt; #ifdef DEBUG if ((rdwr & (RRD_READONLY | RRD_READWRITE)) == (RRD_READONLY | RRD_READWRITE)) { /* Both READONLY and READWRITE were given, which is invalid. */ rrd_set_error("in read/write request mask"); exit(-1); } #endif #ifdef HAVE_MMAP rrd_simple_file->mm_prot = PROT_READ; rrd_simple_file->mm_flags = 0; #endif if (rdwr & RRD_READONLY) { flags |= O_RDONLY; #ifdef HAVE_MMAP # if !defined(AIX) rrd_simple_file->mm_flags = MAP_PRIVATE; # endif # ifdef MAP_NORESERVE rrd_simple_file->mm_flags |= MAP_NORESERVE; /* readonly, so no swap backing needed */ # endif #endif } else { if (rdwr & RRD_READWRITE) { flags |= O_RDWR; #ifdef HAVE_MMAP rrd_simple_file->mm_flags = MAP_SHARED; rrd_simple_file->mm_prot |= PROT_WRITE; #endif } if (rdwr & RRD_CREAT) { flags |= (O_CREAT | O_TRUNC); } if (rdwr & RRD_EXCL) { flags |= O_EXCL; } } if (rdwr & RRD_READAHEAD) { #ifdef MAP_POPULATE rrd_simple_file->mm_flags |= MAP_POPULATE; /* populate ptes and data */ #endif #if defined MAP_NONBLOCK rrd_simple_file->mm_flags |= MAP_NONBLOCK; /* just populate ptes */ #endif } #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) flags |= O_BINARY; #endif if ((rrd_simple_file->fd = open(file_name, flags, 0666)) < 0) { rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno)); goto out_free; } #ifdef HAVE_MMAP #ifdef HAVE_BROKEN_MS_ASYNC if (rdwr & RRD_READWRITE) { /* some unices, the files mtime does not get updated on memory mapped files, in order to help them, we update the the timestamp at this point. The thing happens pretty 'close' to the open call so the chances of a race should be minimal. Maybe ask your vendor to fix your OS ... */ utime(file_name,NULL); } #endif #endif /* Better try to avoid seeks as much as possible. stat may be heavy but * many concurrent seeks are even worse. */ if (newfile_size == 0 && ((fstat(rrd_simple_file->fd, &statb)) < 0)) { rrd_set_error("fstat '%s': %s", file_name, rrd_strerror(errno)); goto out_close; } if (newfile_size == 0) { rrd_file->file_len = statb.st_size; } else { rrd_file->file_len = newfile_size; #ifdef HAVE_POSIX_FALLOCATE if (posix_fallocate(rrd_simple_file->fd, 0, newfile_size) == 0){ /* if all is well we skip the seeking below */ goto no_lseek_necessary; } #endif lseek(rrd_simple_file->fd, newfile_size - 1, SEEK_SET); if ( write(rrd_simple_file->fd, "\0", 1) == -1){ /* poke */ rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno)); goto out_close; } lseek(rrd_simple_file->fd, 0, SEEK_SET); } no_lseek_necessary: #ifdef HAVE_POSIX_FADVISE /* In general we need no read-ahead when dealing with rrd_files. When we stop reading, it is highly unlikely that we start up again. In this manner we actually save time and diskaccess (and buffer cache). Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */ posix_fadvise(rrd_simple_file->fd, 0, 0, POSIX_FADV_RANDOM); #endif /* if (rdwr & RRD_READWRITE) { if (setvbuf((rrd_simple_file->fd),NULL,_IONBF,2)) { rrd_set_error("failed to disable the stream buffer\n"); return (-1); } } */ #ifdef HAVE_MMAP #ifndef HAVE_POSIX_FALLOCATE /* force allocating the file on the underlaying filesystem to prevent any * future bus error when the filesystem is full and attempting to write * trough the file mapping. Filling the file using memset on the file * mapping can also lead some bus error, so we use the old fashioned * write(). */ if (rdwr & RRD_CREAT) { char buf[4096]; unsigned i; memset(buf, DNAN, sizeof buf); lseek(rrd_simple_file->fd, offset, SEEK_SET); for (i = 0; i < (newfile_size - 1) / sizeof buf; ++i) { if (write(rrd_simple_file->fd, buf, sizeof buf) == -1) { rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno)); goto out_close; } } if (write(rrd_simple_file->fd, buf, (newfile_size - 1) % sizeof buf) == -1) { rrd_set_error("write '%s': %s", file_name, rrd_strerror(errno)); goto out_close; } lseek(rrd_simple_file->fd, 0, SEEK_SET); } #endif data = mmap(0, rrd_file->file_len, rrd_simple_file->mm_prot, rrd_simple_file->mm_flags, rrd_simple_file->fd, offset); /* lets see if the first read worked */ if (data == MAP_FAILED) { rrd_set_error("mmaping file '%s': %s", file_name, rrd_strerror(errno)); goto out_close; } rrd->__mmap_start = data; rrd->__mmap_size = rrd_file->file_len; rrd_simple_file->file_start = data; #endif if (rdwr & RRD_CREAT) goto out_done; #ifdef USE_MADVISE if (rdwr & RRD_COPY) { /* We will read everything in a moment (copying) */ madvise(data, rrd_file->file_len, MADV_SEQUENTIAL ); } else { /* We do not need to read anything in for the moment */ madvise(data, rrd_file->file_len, MADV_RANDOM); } #endif __rrd_read(rrd->stat_head, stat_head_t, 1); /* lets do some test if we are on track ... */ if (memcmp(rrd->stat_head->cookie, RRD_COOKIE, sizeof(RRD_COOKIE)) != 0) { rrd_set_error("'%s' is not an RRD file", file_name); goto out_nullify_head; } if (rrd->stat_head->float_cookie != FLOAT_COOKIE) { rrd_set_error("This RRD was created on another architecture"); goto out_nullify_head; } version = atoi(rrd->stat_head->version); if (version > atoi(RRD_VERSION)) { rrd_set_error("can't handle RRD file version %s", rrd->stat_head->version); goto out_nullify_head; } __rrd_read(rrd->ds_def, ds_def_t, rrd->stat_head->ds_cnt); __rrd_read(rrd->rra_def, rra_def_t, rrd->stat_head->rra_cnt); /* handle different format for the live_head */ if (version < 3) { rrd->live_head = (live_head_t *) malloc(sizeof(live_head_t)); if (rrd->live_head == NULL) { rrd_set_error("live_head_t malloc"); goto out_close; } __rrd_read(rrd->legacy_last_up, time_t, 1); rrd->live_head->last_up = *rrd->legacy_last_up; rrd->live_head->last_up_usec = 0; } else { __rrd_read(rrd->live_head, live_head_t, 1); } __rrd_read(rrd->pdp_prep, pdp_prep_t, rrd->stat_head->ds_cnt); __rrd_read(rrd->cdp_prep, cdp_prep_t, rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt); __rrd_read(rrd->rra_ptr, rra_ptr_t, rrd->stat_head->rra_cnt); rrd_file->header_len = offset; rrd_file->pos = offset; { unsigned long row_cnt = 0; for (ui=0; ui<rrd->stat_head->rra_cnt; ui++) row_cnt += rrd->rra_def[ui].row_cnt; size_t correct_len = rrd_file->header_len + sizeof(rrd_value_t) * row_cnt * rrd->stat_head->ds_cnt; if (correct_len > rrd_file->file_len) { rrd_set_error("'%s' is too small (should be %ld bytes)", file_name, (long long) correct_len); goto out_nullify_head; } if (rdwr & RRD_READVALUES) { long d_offset = offset; __rrd_read(rrd->rrd_value, rrd_value_t, row_cnt * rrd->stat_head->ds_cnt); rrd_file->header_len = d_offset; rrd_file->pos = d_offset; } } out_done: return (rrd_file); out_nullify_head: rrd->stat_head = NULL; out_close: #ifdef HAVE_MMAP if (data != MAP_FAILED) munmap(data, rrd_file->file_len); rrd->__mmap_start = NULL; rrd->__mmap_size = 0; #endif close(rrd_simple_file->fd); out_free: free(rrd_file->pvt); free(rrd_file); return NULL; }
rrd_info_t *rrd_info_r( const char *filename) { unsigned int i, ii = 0; rrd_t rrd; rrd_info_t *data = NULL, *cd; rrd_infoval_t info; rrd_file_t *rrd_file; enum cf_en current_cf; enum dst_en current_ds; rrd_init(&rrd); rrd_file = rrd_open(filename, &rrd, RRD_READONLY); if (rrd_file == NULL) goto err_free; info.u_str = filename; cd = rrd_info_push(NULL, sprintf_alloc("filename"), RD_I_STR, info); data = cd; info.u_str = rrd.stat_head->version; cd = rrd_info_push(cd, sprintf_alloc("rrd_version"), RD_I_STR, info); info.u_cnt = rrd.stat_head->pdp_step; cd = rrd_info_push(cd, sprintf_alloc("step"), RD_I_CNT, info); info.u_cnt = rrd.live_head->last_up; cd = rrd_info_push(cd, sprintf_alloc("last_update"), RD_I_CNT, info); info.u_cnt = rrd_get_header_size(&rrd); cd = rrd_info_push(cd, sprintf_alloc("header_size"), RD_I_CNT, info); for (i = 0; i < rrd.stat_head->ds_cnt; i++) { info.u_cnt=i; cd= rrd_info_push(cd,sprintf_alloc("ds[%s].index", rrd.ds_def[i].ds_nam), RD_I_CNT, info); info.u_str = rrd.ds_def[i].dst; cd = rrd_info_push(cd, sprintf_alloc("ds[%s].type", rrd.ds_def[i].ds_nam), RD_I_STR, info); current_ds = dst_conv(rrd.ds_def[i].dst); switch (current_ds) { case DST_CDEF: { char *buffer = NULL; rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]), rrd.ds_def, &buffer); info.u_str = buffer; cd = rrd_info_push(cd, sprintf_alloc("ds[%s].cdef", rrd.ds_def[i].ds_nam), RD_I_STR, info); free(buffer); } break; default: info.u_cnt = rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt; cd = rrd_info_push(cd, sprintf_alloc("ds[%s].minimal_heartbeat", rrd.ds_def[i].ds_nam), RD_I_CNT, info); info.u_val = rrd.ds_def[i].par[DS_min_val].u_val; cd = rrd_info_push(cd, sprintf_alloc("ds[%s].min", rrd.ds_def[i].ds_nam), RD_I_VAL, info); info.u_val = rrd.ds_def[i].par[DS_max_val].u_val; cd = rrd_info_push(cd, sprintf_alloc("ds[%s].max", rrd.ds_def[i].ds_nam), RD_I_VAL, info); break; } info.u_str = rrd.pdp_prep[i].last_ds; cd = rrd_info_push(cd, sprintf_alloc("ds[%s].last_ds", rrd.ds_def[i].ds_nam), RD_I_STR, info); info.u_val = rrd.pdp_prep[i].scratch[PDP_val].u_val; cd = rrd_info_push(cd, sprintf_alloc("ds[%s].value", rrd.ds_def[i].ds_nam), RD_I_VAL, info); info.u_cnt = rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt; cd = rrd_info_push(cd, sprintf_alloc("ds[%s].unknown_sec", rrd.ds_def[i].ds_nam), RD_I_CNT, info); } for (i = 0; i < rrd.stat_head->rra_cnt; i++) { info.u_str = rrd.rra_def[i].cf_nam; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].cf", i), RD_I_STR, info); current_cf = cf_conv(rrd.rra_def[i].cf_nam); info.u_cnt = rrd.rra_def[i].row_cnt; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].rows", i), RD_I_CNT, info); info.u_cnt = rrd.rra_ptr[i].cur_row; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].cur_row", i), RD_I_CNT, info); info.u_cnt = rrd.rra_def[i].pdp_cnt; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].pdp_per_row", i), RD_I_CNT, info); switch (current_cf) { case CF_HWPREDICT: case CF_MHWPREDICT: info.u_val = rrd.rra_def[i].par[RRA_hw_alpha].u_val; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].alpha", i), RD_I_VAL, info); info.u_val = rrd.rra_def[i].par[RRA_hw_beta].u_val; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].beta", i), RD_I_VAL, info); break; case CF_SEASONAL: case CF_DEVSEASONAL: info.u_val = rrd.rra_def[i].par[RRA_seasonal_gamma].u_val; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].gamma", i), RD_I_VAL, info); if (atoi(rrd.stat_head->version) >= 4) { info.u_val = rrd.rra_def[i].par[RRA_seasonal_smoothing_window].u_val; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].smoothing_window", i), RD_I_VAL, info); } break; case CF_FAILURES: info.u_val = rrd.rra_def[i].par[RRA_delta_pos].u_val; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].delta_pos", i), RD_I_VAL, info); info.u_val = rrd.rra_def[i].par[RRA_delta_neg].u_val; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].delta_neg", i), RD_I_VAL, info); info.u_cnt = rrd.rra_def[i].par[RRA_failure_threshold].u_cnt; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].failure_threshold", i), RD_I_CNT, info); info.u_cnt = rrd.rra_def[i].par[RRA_window_len].u_cnt; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].window_length", i), RD_I_CNT, info); break; case CF_DEVPREDICT: break; default: info.u_val = rrd.rra_def[i].par[RRA_cdp_xff_val].u_val; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].xff", i), RD_I_VAL, info); break; } for (ii = 0; ii < rrd.stat_head->ds_cnt; ii++) { switch (current_cf) { case CF_HWPREDICT: case CF_MHWPREDICT: info.u_val = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch[CDP_hw_intercept].u_val; cd = rrd_info_push(cd, sprintf_alloc ("rra[%d].cdp_prep[%d].intercept", i, ii), RD_I_VAL, info); info.u_val = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch[CDP_hw_slope].u_val; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].cdp_prep[%d].slope", i, ii), RD_I_VAL, info); info.u_cnt = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch[CDP_null_count].u_cnt; cd = rrd_info_push(cd, sprintf_alloc ("rra[%d].cdp_prep[%d].NaN_count", i, ii), RD_I_CNT, info); break; case CF_SEASONAL: info.u_val = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch[CDP_hw_seasonal].u_val; cd = rrd_info_push(cd, sprintf_alloc ("rra[%d].cdp_prep[%d].seasonal", i, ii), RD_I_VAL, info); break; case CF_DEVSEASONAL: info.u_val = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch[CDP_seasonal_deviation].u_val; cd = rrd_info_push(cd, sprintf_alloc ("rra[%d].cdp_prep[%d].deviation", i, ii), RD_I_VAL, info); break; case CF_DEVPREDICT: break; case CF_FAILURES: { unsigned short j; char *violations_array; char history[MAX_FAILURES_WINDOW_LEN + 1]; violations_array = (char *) rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch; for (j = 0; j < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++j) history[j] = (violations_array[j] == 1) ? '1' : '0'; history[j] = '\0'; info.u_str = history; cd = rrd_info_push(cd, sprintf_alloc ("rra[%d].cdp_prep[%d].history", i, ii), RD_I_STR, info); } break; default: info.u_val = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch[CDP_val].u_val; cd = rrd_info_push(cd, sprintf_alloc("rra[%d].cdp_prep[%d].value", i, ii), RD_I_VAL, info); info.u_cnt = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch[CDP_unkn_pdp_cnt].u_cnt; cd = rrd_info_push(cd, sprintf_alloc ("rra[%d].cdp_prep[%d].unknown_datapoints", i, ii), RD_I_CNT, info); break; } } } rrd_close(rrd_file); err_free: rrd_free(&rrd); return (data); }