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 = munmap(rrd_simple_file->file_start, rrd_file->file_len); if (ret != 0) rrd_set_error("munmap rrd_file: %s", rrd_strerror(errno)); #endif #ifdef HAVE_LIBRADOS if (rrd_file->rados) ret = rrd_rados_close(rrd_file->rados); else #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; }
int write_file( const char *file_name, rrd_t *rrd) { FILE *fh; #ifdef HAVE_LIBRADOS if (strncmp("ceph//", file_name, 6) == 0) { return rrd_rados_create(file_name + 6, rrd); } #endif if (strcmp("-", file_name) == 0) fh = stdout; else { int fd_flags = O_WRONLY | O_CREAT; int fd; #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) fd_flags |= O_BINARY; #endif if (opt_force_overwrite == 0) fd_flags |= O_EXCL; fd = open(file_name, fd_flags, 0666); if (fd == -1) { rrd_set_error("creating '%s': %s", file_name, rrd_strerror(errno)); return (-1); } fh = fdopen(fd, "wb"); if (fh == NULL) { rrd_set_error("fdopen failed: %s", rrd_strerror(errno)); close(fd); return (-1); } } int rc = write_fh(fh, rrd); /* lets see if we had an error */ if (ferror(fh)) { rrd_set_error("a file error occurred while creating '%s': %s", file_name, rrd_strerror(errno)); fclose(fh); if (strcmp("-", file_name) != 0) unlink(file_name); return (-1); } fclose(fh); return rc; }
int getDouble(const char* v, double *val,char**extra) { /* try to execute the parser */ /* NOTE that this may be a bit different from the original parser */ char *old_locale; *extra=NULL; old_locale = setlocale(LC_NUMERIC, "C"); errno = 0; *val = strtod(v,extra); if (errno > 0) { rrd_set_error("converting '%s' to float: %s", v, rrd_strerror(errno)); setlocale(LC_NUMERIC, old_locale); return -1; }; setlocale(LC_NUMERIC, old_locale); *val=strtod(v,extra); /* and error handling */ if (extra==NULL) { return 0; } else { if (*extra==v) { return -1; /* failed miserably */ } else { if ((*extra)[0]==0) { return 0; } return 1; /* got extra bytes */ } } /* not found, so return error */ return -2; }
off_t rrd_seek( rrd_file_t *rrd_file, off_t off, int whence) { off_t ret = 0; #ifndef HAVE_MMAP rrd_simple_file_t *rrd_simple_file; rrd_simple_file = (rrd_simple_file_t *)rrd_file->pvt; #endif #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 */ }
int rrd_dump_opt_r( const char *filename, char *outname, int opt_noheader) { FILE *out_file; int res; out_file = NULL; if (outname) { if (!(out_file = fopen(outname, "w"))) { return (-1); } } else { out_file = stdout; } res = rrd_dump_cb_r(filename, opt_noheader, rrd_dump_opt_cb_fileout, (void *)out_file); if (fflush(out_file) != 0) { rrd_set_error("error flushing output: %s", rrd_strerror(errno)); res = -1; } if (out_file != stdout) { fclose(out_file); if (res != 0) unlink(outname); } return res; }
/* get_path: Return a path name appropriate to be sent to the daemon. * * When talking to a local daemon (thru a UNIX socket), relative path names * are resolved to absolute path names to allow for transparent integration * into existing solutions (as requested by Tobi). Else, absolute path names * are not allowed, since path name translation is done by the server. * * One must hold `lock' when calling this function. */ static const char *get_path (const char *path, char *resolved_path) /* {{{ */ { const char *ret = path; int is_unix = 0; if ((path == NULL) || (resolved_path == NULL) || (sd_path == NULL)) return (NULL); if ((*sd_path == '/') || (strncmp ("unix:", sd_path, strlen ("unix:")) == 0)) is_unix = 1; if (is_unix) { ret = realpath(path, resolved_path); if (ret == NULL) rrd_set_error("realpath(%s): %s", path, rrd_strerror(errno)); return ret; } else { if (*path == '/') /* not absolute path */ { rrd_set_error ("absolute path names not allowed when talking " "to a remote daemon"); return NULL; } } return path; } /* }}} char *get_path */
/* convenience function; if there is a daemon specified, or if we can * detect one from the environment, then flush the file. Otherwise, no-op */ int rrdc_flush_if_daemon (const char *opt_daemon, const char *filename) /* {{{ */ { int status = 0; rrdc_connect(opt_daemon); if (rrdc_is_connected(opt_daemon)) { rrd_clear_error(); status = rrdc_flush (filename); if (status != 0 && !rrd_test_error()) { if (status > 0) { rrd_set_error("rrdc_flush (%s) failed: %s", filename, rrd_strerror(status)); } else if (status < 0) { rrd_set_error("rrdc_flush (%s) failed with status %i.", filename, status); } } } /* if (rrdc_is_connected(..)) */ return status; } /* }}} int rrdc_flush_if_daemon */
static int get_xml_time_t( xmlTextReaderPtr reader, time_t *value) { xmlChar *text; time_t temp; if ((text = get_xml_text(reader)) != NULL){ errno = 0; #if SIZEOF_TIME_T == 4 temp = strtol((char *)text,NULL, 0); #elif SIZEOF_TIME_T == 8 temp = strtoll((char *)text,NULL, 0); #else #error "Don't know how to deal with TIME_T other than 4 or 8 bytes" #endif if (errno>0){ rrd_set_error("ling %d: get_xml_time_t from '%s' %s", xmlTextReaderGetParserLineNumber(reader), text,rrd_strerror(errno)); xmlFree(text); return -1; } xmlFree(text); *value = temp; return 0; } return -1; } /* get_xml_time_t */
static int readfile( const char *file_name, char **buffer, int skipfirst) { long writecnt = 0, totalcnt = MEMBLK; long offset = 0; FILE *input = NULL; char c; if ((strcmp("-", file_name) == 0)) { input = stdin; } else { if ((input = fopen(file_name, "rb")) == NULL) { rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno)); return (-1); } } if (skipfirst) { do { c = getc(input); offset++; } while (c != '\n' && !feof(input)); } if (strcmp("-", file_name)) { fseek(input, 0, SEEK_END); /* have extra space for detecting EOF without realloc */ totalcnt = (ftell(input) + 1) / sizeof(char) - offset; if (totalcnt < MEMBLK) totalcnt = MEMBLK; /* sanitize */ fseek(input, offset * sizeof(char), SEEK_SET); } if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) { perror("Allocate Buffer:"); exit(1); }; do { writecnt += fread((*buffer) + writecnt, 1, (totalcnt - writecnt) * sizeof(char), input); if (writecnt >= totalcnt) { totalcnt += MEMBLK; if (((*buffer) = rrd_realloc((*buffer), (totalcnt + 4) * sizeof(char))) == NULL) { perror("Realloc Buffer:"); exit(1); }; } } while (!feof(input)); (*buffer)[writecnt] = '\0'; if (strcmp("-", file_name) != 0) { fclose(input); }; return writecnt; }
int rrdc_connect (const char *addr) /* {{{ */ { int status = 0; if (addr == NULL) { addr = getenv (ENV_RRDCACHED_ADDRESS); } if (addr == NULL || strcmp(addr,"") == 0 ) { addr = NULL; return 0; } pthread_mutex_lock(&lock); if (sd >= 0 && sd_path != NULL && strcmp(addr, sd_path) == 0) { /* connection to the same daemon; use cached connection */ pthread_mutex_unlock (&lock); return (0); } else { close_connection(); } rrd_clear_error (); if (strncmp ("unix:", addr, strlen ("unix:")) == 0) status = rrdc_connect_unix (addr + strlen ("unix:")); else if (addr[0] == '/') status = rrdc_connect_unix (addr); else status = rrdc_connect_network(addr); if (status == 0 && sd >= 0) sd_path = strdup(addr); else { char *err = rrd_test_error () ? rrd_get_error () : "Internal error"; /* err points the string that gets written to by rrd_set_error(), thus we * cannot pass it to that function */ err = strdup (err); rrd_set_error("Unable to connect to rrdcached: %s", (status < 0) ? (err ? err : "Internal error") : rrd_strerror (status)); if (err != NULL) free (err); } pthread_mutex_unlock (&lock); return (status); } /* }}} int rrdc_connect */
/* 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 }
static int get_xml_ulong( xmlTextReaderPtr reader, unsigned long *value) { xmlChar *text; unsigned long temp; if ((text = get_xml_text(reader)) != NULL){ errno = 0; temp = strtoul((char *)text,NULL, 0); if (errno>0){ rrd_set_error("ling %d: get_xml_ulong from '%s' %s", xmlTextReaderGetParserLineNumber(reader), text,rrd_strerror(errno)); xmlFree(text); return -1; } xmlFree(text); *value = temp; return 0; } return -1; } /* get_xml_ulong */
static int get_xml_double( xmlTextReaderPtr reader, double *value) { xmlChar *text; double temp; if ((text = get_xml_text(reader))!= NULL){ if (xmlStrcasestr(text,(xmlChar *)"nan")){ *value = DNAN; xmlFree(text); return 0; } else if (xmlStrcasestr(text,(xmlChar *)"-inf")){ *value = -DINF; xmlFree(text); return 0; } else if (xmlStrcasestr(text,(xmlChar *)"+inf") || xmlStrcasestr(text,(xmlChar *)"inf")){ *value = DINF; xmlFree(text); return 0; } if ( rrd_strtodbl((char *)text,NULL, &temp, NULL) != 2 ){ rrd_set_error("ling %d: get_xml_double from '%s' %s", xmlTextReaderGetParserLineNumber(reader), text,rrd_strerror(errno)); xmlFree(text); return -1; } xmlFree(text); *value = temp; return 0; } return -1; } /* get_xml_double */
/* HandleInputLine is NOT thread safe - due to readdir issues, resolving them portably is not really simple. */ static int HandleInputLine( int argc, char **argv, FILE * out) { #if defined(HAVE_OPENDIR) && defined (HAVE_READDIR) DIR *curdir; /* to read current dir with ls */ struct dirent *dent; #endif /* Reset errno to 0 before we start. */ if (RemoteMode) { if (argc > 1 && strcmp("quit", argv[1]) == 0) { if (argc != 2) { printf("ERROR: invalid parameter count for quit\n"); return (1); } exit(0); } #if defined(HAVE_OPENDIR) && defined(HAVE_READDIR) && defined(HAVE_CHDIR) && defined(HAVE_SYS_STAT_H) if (argc > 1 && strcmp("cd", argv[1]) == 0) { if (argc != 3) { printf("ERROR: invalid parameter count for cd\n"); return (1); } #if ! defined(HAVE_CHROOT) && defined(HAVE_GETUID) if (getuid() == 0 && !ChangeRoot) { printf ("ERROR: chdir security problem - rrdtool is running as " "root but not chroot!\n"); return (1); } #endif if (chdir(argv[2]) != 0){ printf("ERROR: chdir %s %s\n", argv[2], rrd_strerror(errno)); return (1); } return (0); } if (argc > 1 && strcmp("pwd", argv[1]) == 0) { char *cwd; /* To hold current working dir on call to pwd */ if (argc != 2) { printf("ERROR: invalid parameter count for pwd\n"); return (1); } #ifdef MAXPATH cwd = getcwd(NULL, MAXPATH); #elif defined(HAVE_GET_CURRENT_DIR_NAME) cwd = get_current_dir_name(); #else #error "You must have either MAXPATH or get_current_dir_name()" #endif if (cwd == NULL) { printf("ERROR: getcwd %s\n", rrd_strerror(errno)); return (1); } printf("%s\n", cwd); free(cwd); return (0); } if (argc > 1 && strcmp("mkdir", argv[1]) == 0) { if (argc != 3) { printf("ERROR: invalid parameter count for mkdir\n"); return (1); } #if ! defined(HAVE_CHROOT) && defined(HAVE_GETUID) if (getuid() == 0 && !ChangeRoot) { printf ("ERROR: mkdir security problem - rrdtool is running as " "root but not chroot!\n"); return (1); } #endif if(mkdir(argv[2], 0777)!=0){ printf("ERROR: mkdir %s: %s\n", argv[2],rrd_strerror(errno)); return (1); } return (0); } if (argc > 1 && strcmp("ls", argv[1]) == 0) { if (argc != 2) { printf("ERROR: invalid parameter count for ls\n"); return (1); } if ((curdir = opendir(".")) != NULL) { struct stat st; while ((dent = readdir(curdir)) != NULL) { if (!stat(dent->d_name, &st)) { if (S_ISDIR(st.st_mode)) { printf("d %s\n", dent->d_name); } if (strlen(dent->d_name) > 4 && S_ISREG(st.st_mode)) { if (!strcmp (dent->d_name + NAMLEN(dent) - 4, ".rrd") || !strcmp(dent->d_name + NAMLEN(dent) - 4, ".RRD")) { printf("- %s\n", dent->d_name); } } } } closedir(curdir); } else { printf("ERROR: opendir .: %s\n", rrd_strerror(errno)); return (errno); } return (0); } #endif /* opendir and readdir */ } if (argc < 3 || strcmp("help", argv[1]) == 0 || strcmp("--help", argv[1]) == 0 || strcmp("-help", argv[1]) == 0 || strcmp("-?", argv[1]) == 0 || strcmp("-h", argv[1]) == 0) { PrintUsage(""); return 0; } if (strcmp("create", argv[1]) == 0) rrd_create(argc - 1, &argv[1]); else if (strcmp("dump", argv[1]) == 0) rrd_dump(argc - 1, &argv[1]); else if (strcmp("info", argv[1]) == 0 || strcmp("updatev", argv[1]) == 0) { rrd_info_t *data; if (strcmp("info", argv[1]) == 0) data = rrd_info(argc - 1, &argv[1]); else data = rrd_update_v(argc - 1, &argv[1]); rrd_info_print(data); rrd_info_free(data); } else if (strcmp("list", argv[1]) == 0) { char *list; list = rrd_list(argc - 1, &argv[1]); if (list) { printf("%s", list); free(list); } } else if (strcmp("--version", argv[1]) == 0 || strcmp("version", argv[1]) == 0 || strcmp("v", argv[1]) == 0 || strcmp("-v", argv[1]) == 0 || strcmp("-version", argv[1]) == 0) printf("RRDtool " PACKAGE_VERSION " Copyright by Tobi Oetiker (%f)\n", rrd_version()); else if (strcmp("restore", argv[1]) == 0) #ifdef HAVE_RRD_RESTORE rrd_restore(argc - 1, &argv[1]); #else rrd_set_error("the instance of rrdtool has been compiled without XML import functions"); #endif else if (strcmp("resize", argv[1]) == 0)
int rrd_dump_cb_r( const char *filename, int opt_header, rrd_output_callback_t cb, void *user) { unsigned int i, ii, ix, iii = 0; time_t now; char somestring[255]; rrd_value_t my_cdp; off_t rra_base, rra_start, rra_next; rrd_file_t *rrd_file; rrd_t rrd; rrd_value_t value; struct tm tm; //These two macros are local defines to clean up visible code from its redndancy //and make it easier to read. #define CB_PUTS(str) \ do { \ size_t len = strlen(str); \ \ if (cb((str), len, user) != len) \ goto err_out; \ } while (0); #define CB_FMTS(...) do { \ char buffer[256]; \ rrd_snprintf (buffer, sizeof(buffer), __VA_ARGS__); \ CB_PUTS (buffer); \ } while (0) //These macros are to be undefined at the end of this function //Check if we got a (valid) callback method if (!cb) { return (-1); } rrd_init(&rrd); rrd_file = rrd_open(filename, &rrd, RRD_READONLY | RRD_READAHEAD); if (rrd_file == NULL) { rrd_free(&rrd); return (-1); } if (opt_header == 1) { CB_PUTS("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); CB_PUTS("<!DOCTYPE rrd SYSTEM \"http://oss.oetiker.ch/rrdtool/rrdtool.dtd\">\n"); CB_PUTS("<!-- Round Robin Database Dump -->\n"); CB_PUTS("<rrd>\n"); } else if (opt_header == 2) { CB_PUTS("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); CB_PUTS("<!-- Round Robin Database Dump -->\n"); CB_PUTS("<rrd xmlns=\"http://oss.oetiker.ch/rrdtool/rrdtool-dump.xml\" " "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"); CB_PUTS("\txsi:schemaLocation=\"http://oss.oetiker.ch/rrdtool/rrdtool-dump.xml " "http://oss.oetiker.ch/rrdtool/rrdtool-dump.xsd\">\n"); } else { CB_PUTS("<!-- Round Robin Database Dump -->\n"); CB_PUTS("<rrd>\n"); } if (atoi(rrd.stat_head->version) <= 3) { CB_FMTS("\t<version>%s</version>\n", RRD_VERSION3); } else { CB_FMTS("\t<version>%s</version>\n", rrd.stat_head->version); } CB_FMTS("\t<step>%lu</step> <!-- Seconds -->\n", rrd.stat_head->pdp_step); #ifdef HAVE_STRFTIME localtime_r(&rrd.live_head->last_up, &tm); strftime(somestring, 255, "%Y-%m-%d %H:%M:%S %Z", &tm); #else # error "Need strftime" #endif CB_FMTS("\t<lastupdate>%lld</lastupdate> <!-- %s -->\n\n", (long long int) rrd.live_head->last_up, somestring); for (i = 0; i < rrd.stat_head->ds_cnt; i++) { CB_PUTS("\t<ds>\n"); CB_FMTS("\t\t<name> %s </name>\n", rrd.ds_def[i].ds_nam); CB_FMTS("\t\t<type> %s </type>\n", rrd.ds_def[i].dst); if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) { CB_FMTS("\t\t<minimal_heartbeat>%lu</minimal_heartbeat>\n", rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt); if (isnan(rrd.ds_def[i].par[DS_min_val].u_val)) { CB_PUTS("\t\t<min>NaN</min>\n"); } else { CB_FMTS("\t\t<min>%0.10e</min>\n", rrd.ds_def[i].par[DS_min_val].u_val); } if (isnan(rrd.ds_def[i].par[DS_max_val].u_val)) { CB_PUTS("\t\t<max>NaN</max>\n"); } else { CB_FMTS("\t\t<max>%0.10e</max>\n", rrd.ds_def[i].par[DS_max_val].u_val); } } else { /* DST_CDEF */ char *str = NULL; rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]), rrd.ds_def, &str); //Splitting into 3 writes to avoid allocating memory //This is better compared to snprintf as str may be of arbitrary size CB_PUTS("\t\t<cdef> "); CB_PUTS(str); CB_PUTS(" </cdef>\n"); free(str); } CB_PUTS("\n\t\t<!-- PDP Status -->\n"); CB_FMTS("\t\t<last_ds>%s</last_ds>\n", rrd.pdp_prep[i].last_ds); if (isnan(rrd.pdp_prep[i].scratch[PDP_val].u_val)) { CB_PUTS("\t\t<value>NaN</value>\n"); } else { CB_FMTS("\t\t<value>%0.10e</value>\n", rrd.pdp_prep[i].scratch[PDP_val].u_val); } CB_FMTS("\t\t<unknown_sec> %lu </unknown_sec>\n", rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt); CB_PUTS("\t</ds>\n\n"); } CB_PUTS("\t<!-- Round Robin Archives -->\n"); rra_base = rrd_file->header_len; rra_next = rra_base; for (i = 0; i < rrd.stat_head->rra_cnt; i++) { long timer = 0; rra_start = rra_next; rra_next += (rrd.stat_head->ds_cnt * rrd.rra_def[i].row_cnt * sizeof(rrd_value_t)); CB_PUTS("\t<rra>\n"); CB_FMTS("\t\t<cf>%s</cf>\n", rrd.rra_def[i].cf_nam); CB_FMTS("\t\t<pdp_per_row>%lu</pdp_per_row> <!-- %lu seconds -->\n\n", rrd.rra_def[i].pdp_cnt, rrd.rra_def[i].pdp_cnt * rrd.stat_head->pdp_step); /* support for RRA parameters */ CB_PUTS("\t\t<params>\n"); switch (cf_conv(rrd.rra_def[i].cf_nam)) { case CF_HWPREDICT: case CF_MHWPREDICT: CB_FMTS("\t\t<hw_alpha>%0.10e</hw_alpha>\n", rrd.rra_def[i].par[RRA_hw_alpha].u_val); CB_FMTS("\t\t<hw_beta>%0.10e</hw_beta>\n", rrd.rra_def[i].par[RRA_hw_beta].u_val); CB_FMTS("\t\t<dependent_rra_idx>%lu</dependent_rra_idx>\n", rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt); break; case CF_SEASONAL: case CF_DEVSEASONAL: CB_FMTS("\t\t<seasonal_gamma>%0.10e</seasonal_gamma>\n", rrd.rra_def[i].par[RRA_seasonal_gamma].u_val); CB_FMTS("\t\t<seasonal_smooth_idx>%lu</seasonal_smooth_idx>\n", rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt); if (atoi(rrd.stat_head->version) >= 4) { CB_FMTS("\t\t<smoothing_window>%0.10e</smoothing_window>\n", rrd.rra_def[i].par[RRA_seasonal_smoothing_window].u_val); } CB_FMTS("\t\t<dependent_rra_idx>%lu</dependent_rra_idx>\n", rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt); break; case CF_FAILURES: CB_FMTS("\t\t<delta_pos>%0.10e</delta_pos>\n", rrd.rra_def[i].par[RRA_delta_pos].u_val); CB_FMTS("\t\t<delta_neg>%0.10e</delta_neg>\n", rrd.rra_def[i].par[RRA_delta_neg].u_val); CB_FMTS("\t\t<window_len>%lu</window_len>\n", rrd.rra_def[i].par[RRA_window_len].u_cnt); CB_FMTS("\t\t<failure_threshold>%lu</failure_threshold>\n", rrd.rra_def[i].par[RRA_failure_threshold].u_cnt); /* fall thru */ case CF_DEVPREDICT: CB_FMTS("\t\t<dependent_rra_idx>%lu</dependent_rra_idx>\n", rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt); break; case CF_AVERAGE: case CF_MAXIMUM: case CF_MINIMUM: case CF_LAST: default: CB_FMTS("\t\t<xff>%0.10e</xff>\n", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val); break; } CB_PUTS("\t\t</params>\n"); CB_PUTS("\t\t<cdp_prep>\n"); for (ii = 0; ii < rrd.stat_head->ds_cnt; ii++) { unsigned long ivalue; CB_PUTS("\t\t\t<ds>\n"); /* support for exporting all CDP parameters */ /* parameters common to all CFs */ /* primary_val and secondary_val do not need to be saved between updates * so strictly speaking they could be omitted. * However, they can be useful for diagnostic purposes, so are included here. */ value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_primary_val].u_val; if (isnan(value)) { CB_PUTS("\t\t\t<primary_value>NaN</primary_value>\n"); } else { CB_FMTS("\t\t\t<primary_value>%0.10e</primary_value>\n", value); } value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_secondary_val].u_val; if (isnan(value)) { CB_PUTS("\t\t\t<secondary_value>NaN</secondary_value>\n"); } else { CB_FMTS("\t\t\t<secondary_value>%0.10e</secondary_value>\n", value); } switch (cf_conv(rrd.rra_def[i].cf_nam)) { case CF_HWPREDICT: case CF_MHWPREDICT: value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_hw_intercept].u_val; if (isnan(value)) { CB_PUTS("\t\t\t<intercept>NaN</intercept>\n"); } else { CB_FMTS("\t\t\t<intercept>%0.10e</intercept>\n", value); } value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_hw_last_intercept].u_val; if (isnan(value)) { CB_PUTS("\t\t\t<last_intercept>NaN</last_intercept>\n"); } else { CB_FMTS("\t\t\t<last_intercept>%0.10e</last_intercept>\n", value); } value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_hw_slope].u_val; if (isnan(value)) { CB_PUTS("\t\t\t<slope>NaN</slope>\n"); } else { CB_FMTS("\t\t\t<slope>%0.10e</slope>\n", value); } value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_hw_last_slope].u_val; if (isnan(value)) { CB_PUTS("\t\t\t<last_slope>NaN</last_slope>\n"); } else { CB_FMTS("\t\t\t<last_slope>%0.10e</last_slope>\n", value); } ivalue = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_null_count].u_cnt; CB_FMTS("\t\t\t<nan_count>%lu</nan_count>\n", ivalue); ivalue = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_last_null_count].u_cnt; CB_FMTS("\t\t\t<last_nan_count>%lu</last_nan_count>\n", ivalue); break; case CF_SEASONAL: case CF_DEVSEASONAL: value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_hw_seasonal].u_val; if (isnan(value)) { CB_PUTS("\t\t\t<seasonal>NaN</seasonal>\n"); } else { CB_FMTS("\t\t\t<seasonal>%0.10e</seasonal>\n", value); } value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_hw_last_seasonal].u_val; if (isnan(value)) { CB_PUTS("\t\t\t<last_seasonal>NaN</last_seasonal>\n"); } else { CB_FMTS("\t\t\t<last_seasonal>%0.10e</last_seasonal>\n", value); } ivalue = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_init_seasonal].u_cnt; CB_FMTS("\t\t\t<init_flag>%lu</init_flag>\n", ivalue); break; case CF_DEVPREDICT: break; case CF_FAILURES: { unsigned short vidx; char *violations_array = (char *) ((void *) rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch); CB_PUTS("\t\t\t<history>"); for (vidx = 0; vidx < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++vidx) { CB_FMTS("%d", violations_array[vidx]); } CB_PUTS("</history>\n"); } break; case CF_AVERAGE: case CF_MAXIMUM: case CF_MINIMUM: case CF_LAST: default: value = rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii].scratch[CDP_val].u_val; if (isnan(value)) { CB_PUTS("\t\t\t<value>NaN</value>\n"); } else { CB_FMTS("\t\t\t<value>%0.10e</value>\n", value); } CB_FMTS("\t\t\t<unknown_datapoints>%lu</unknown_datapoints>\n", rrd.cdp_prep[i * rrd.stat_head->ds_cnt + ii]. scratch[CDP_unkn_pdp_cnt].u_cnt); break; } CB_PUTS("\t\t\t</ds>\n"); } CB_PUTS("\t\t</cdp_prep>\n"); CB_PUTS("\t\t<database>\n"); rrd_seek(rrd_file, (rra_start + (rrd.rra_ptr[i].cur_row + 1) * rrd.stat_head->ds_cnt * sizeof(rrd_value_t)), SEEK_SET); timer = -(long)(rrd.rra_def[i].row_cnt - 1); ii = rrd.rra_ptr[i].cur_row; for (ix = 0; ix < rrd.rra_def[i].row_cnt; ix++) { ii++; if (ii >= rrd.rra_def[i].row_cnt) { rrd_seek(rrd_file, rra_start, SEEK_SET); ii = 0; /* wrap if max row cnt is reached */ } now = (rrd.live_head->last_up - rrd.live_head->last_up % (rrd.rra_def[i].pdp_cnt * rrd.stat_head->pdp_step)) + (timer * rrd.rra_def[i].pdp_cnt * rrd.stat_head->pdp_step); timer++; #if HAVE_STRFTIME localtime_r(&now, &tm); strftime(somestring, 255, "%Y-%m-%d %H:%M:%S %Z", &tm); #else # error "Need strftime" #endif CB_FMTS("\t\t\t<!-- %s / %lld --> <row>", somestring, (long long int) now); for (iii = 0; iii < rrd.stat_head->ds_cnt; iii++) { rrd_read(rrd_file, &my_cdp, sizeof(rrd_value_t) * 1); if (isnan(my_cdp)) { CB_PUTS("<v>NaN</v>"); } else { CB_FMTS("<v>%0.10e</v>", my_cdp); } } CB_PUTS("</row>\n"); } CB_PUTS("\t\t</database>\n\t</rra>\n"); } CB_PUTS("</rrd>\n"); rrd_free(&rrd); return rrd_close(rrd_file); err_out: rrd_set_error("error writing output file: %s", rrd_strerror(errno)); rrd_free(&rrd); rrd_close(rrd_file); return (-1); //Undefining the previously defined shortcuts //See start of this function #undef CB_PUTS #undef CB_FMTS //End of macro undefining }
int rrd_open(const char *file_name, FILE **in_file, rrd_t *rrd, int rdwr) { char *mode = NULL; int version; rrd_init(rrd); if (rdwr == RRD_READONLY) { mode = "rb"; } else { mode = "rb+"; } if (((*in_file) = fopen(file_name,mode)) == NULL ){ rrd_set_error("opening '%s': %s",file_name, rrd_strerror(errno)); return (-1); } #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. */ /* if (0 != posix_fadvise(fileno(*in_file), 0, 0, POSIX_FADV_RANDOM)) { rrd_set_error("setting POSIX_FADV_RANDOM on '%s': %s",file_name, rrd_strerror(errno)); fclose(*in_file); return(-1); */ /* if it does not work, then this is sad, but we should not quit */ posix_fadvise(fileno(*in_file), 0, 0, POSIX_FADV_RANDOM); /* } */ #endif /* if (rdwr == RRD_READWRITE) { if (setvbuf((*in_file),NULL,_IONBF,2)) { rrd_set_error("failed to disable the stream buffer\n"); return (-1); } } */ #define MYFREAD(MYVAR,MYVART,MYCNT) \ if ((MYVAR = malloc(sizeof(MYVART) * MYCNT)) == NULL) {\ rrd_set_error("" #MYVAR " malloc"); \ fclose(*in_file); \ return (-1); } \ fread(MYVAR,sizeof(MYVART),MYCNT, *in_file); MYFREAD(rrd->stat_head, stat_head_t, 1) /* lets see if the first read worked */ if (ferror( *in_file ) || feof(*in_file)) { rrd_set_error("reading the cookie off %s faild",file_name); fclose(*in_file); return(-1); } /* lets do some test if we are on track ... */ if (strncmp(rrd->stat_head->cookie,RRD_COOKIE,4) != 0){ rrd_set_error("'%s' is not an RRD file",file_name); free(rrd->stat_head); rrd->stat_head = NULL; fclose(*in_file); return(-1);} if (rrd->stat_head->float_cookie != FLOAT_COOKIE){ rrd_set_error("This RRD was created on other architecture"); free(rrd->stat_head); rrd->stat_head = NULL; fclose(*in_file); return(-1);} 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); free(rrd->stat_head); rrd->stat_head = NULL; fclose(*in_file); return(-1);} MYFREAD(rrd->ds_def, ds_def_t, rrd->stat_head->ds_cnt) MYFREAD(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"); fclose(*in_file); return (-1); } fread(&rrd->live_head->last_up, sizeof(long), 1, *in_file); rrd->live_head->last_up_usec = 0; } else { MYFREAD(rrd->live_head, live_head_t, 1) } MYFREAD(rrd->pdp_prep, pdp_prep_t, rrd->stat_head->ds_cnt) MYFREAD(rrd->cdp_prep, cdp_prep_t, (rrd->stat_head->rra_cnt * rrd->stat_head->ds_cnt)) MYFREAD(rrd->rra_ptr, rra_ptr_t, rrd->stat_head->rra_cnt) #undef MYFREAD return(0); }
int rrd_resize( int argc, char **argv) { char *infilename, outfilename[11] = "resize.rrd"; rrd_t rrdold, rrdnew; rrd_value_t buffer; int version; unsigned long l, rra; long modify; unsigned long target_rra; int grow = 0, shrink = 0; char *endptr; rrd_file_t *rrd_file, *rrd_out_file; infilename = argv[1]; if (!strcmp(infilename, "resize.rrd")) { rrd_set_error("resize.rrd is a reserved name"); return (-1); } if (argc != 5) { rrd_set_error("wrong number of parameters"); return (-1); } target_rra = strtol(argv[2], &endptr, 0); if (!strcmp(argv[3], "GROW")) grow = 1; else if (!strcmp(argv[3], "SHRINK")) shrink = 1; else { rrd_set_error("I can only GROW or SHRINK"); return (-1); } modify = strtol(argv[4], &endptr, 0); if ((modify < 1)) { rrd_set_error("Please grow or shrink with at least 1 row"); return (-1); } if (shrink) modify = -modify; rrd_init(&rrdold); rrd_file = rrd_open(infilename, &rrdold, RRD_READWRITE | RRD_COPY); if (rrd_file == NULL) { rrd_free(&rrdold); return (-1); } if (rrd_lock(rrd_file) != 0) { rrd_set_error("could not lock original RRD"); rrd_free(&rrdold); rrd_close(rrd_file); return (-1); } if (target_rra >= rrdold.stat_head->rra_cnt) { rrd_set_error("no such RRA in this RRD"); rrd_free(&rrdold); rrd_close(rrd_file); return (-1); } if (modify < 0) if ((long) rrdold.rra_def[target_rra].row_cnt <= -modify) { rrd_set_error("This RRA is not that big"); rrd_free(&rrdold); rrd_close(rrd_file); return (-1); } rrd_init(&rrdnew); /* These need to be initialised before calling rrd_open() with the RRD_CREATE flag */ if ((rrdnew.stat_head = (stat_head_t*)calloc(1, sizeof(stat_head_t))) == NULL) { rrd_set_error("allocating stat_head for new RRD"); rrd_free(&rrdold); rrd_close(rrd_file); return (-1); } memcpy(rrdnew.stat_head,rrdold.stat_head,sizeof(stat_head_t)); if ((rrdnew.rra_def = (rra_def_t *)malloc(sizeof(rra_def_t) * rrdold.stat_head->rra_cnt)) == NULL) { rrd_set_error("allocating rra_def for new RRD"); rrd_free(&rrdnew); rrd_free(&rrdold); rrd_close(rrd_file); return (-1); } memcpy(rrdnew.rra_def,rrdold.rra_def,sizeof(rra_def_t) * rrdold.stat_head->rra_cnt); /* Set this so that the file will be created with the correct size */ rrdnew.rra_def[target_rra].row_cnt += modify; rrd_out_file = rrd_open(outfilename, &rrdnew, RRD_READWRITE | RRD_CREAT); if (rrd_out_file == NULL) { rrd_set_error("Can't create '%s': %s", outfilename, rrd_strerror(errno)); rrd_free(&rrdnew); rrd_free(&rrdold); rrd_close(rrd_file); return (-1); } if (rrd_lock(rrd_out_file) != 0) { rrd_set_error("could not lock new RRD"); rrd_free(&rrdnew); rrd_free(&rrdold); rrd_close(rrd_file); rrd_close(rrd_out_file); return (-1); } /*XXX: do one write for those parts of header that are unchanged */ if ((rrdnew.rra_ptr = (rra_ptr_t *)malloc(sizeof(rra_ptr_t) * rrdold.stat_head->rra_cnt)) == NULL) { rrd_set_error("allocating rra_ptr for new RRD"); rrd_free(&rrdnew); rrd_free(&rrdold); rrd_close(rrd_file); rrd_close(rrd_out_file); return (-1); } /* Put this back the way it was so that the rest of the algorithm below remains unchanged, it will be corrected later */ rrdnew.rra_def[target_rra].row_cnt -= modify; rrdnew.ds_def = rrdold.ds_def; rrdnew.live_head = rrdold.live_head; rrdnew.pdp_prep = rrdold.pdp_prep; rrdnew.cdp_prep = rrdold.cdp_prep; memcpy(rrdnew.rra_ptr,rrdold.rra_ptr,sizeof(rra_ptr_t) * rrdold.stat_head->rra_cnt); version = atoi(rrdold.stat_head->version); switch (version) { case 4: break; case 3: break; case 1: rrdnew.stat_head->version[3] = '3'; break; default: rrd_set_error("Do not know how to handle RRD version %s", rrdold.stat_head->version); rrdnew.ds_def = NULL; rrdnew.live_head = NULL; rrdnew.pdp_prep = NULL; rrdnew.cdp_prep = NULL; rrd_free(&rrdnew); rrd_free(&rrdold); rrd_close(rrd_file); rrd_close(rrd_out_file); return (-1); break; } /* XXX: Error checking? */ rrd_write(rrd_out_file, rrdnew.stat_head, sizeof(stat_head_t) * 1); rrd_write(rrd_out_file, rrdnew.ds_def, sizeof(ds_def_t) * rrdnew.stat_head->ds_cnt); rrd_write(rrd_out_file, rrdnew.rra_def, sizeof(rra_def_t) * rrdnew.stat_head->rra_cnt); rrd_write(rrd_out_file, rrdnew.live_head, sizeof(live_head_t) * 1); rrd_write(rrd_out_file, rrdnew.pdp_prep, sizeof(pdp_prep_t) * rrdnew.stat_head->ds_cnt); rrd_write(rrd_out_file, rrdnew.cdp_prep, sizeof(cdp_prep_t) * rrdnew.stat_head->ds_cnt * rrdnew.stat_head->rra_cnt); rrd_write(rrd_out_file, rrdnew.rra_ptr, sizeof(rra_ptr_t) * rrdnew.stat_head->rra_cnt); /* Move the CDPs from the old to the new database. ** This can be made (much) faster but isn't worth the effort. Clarity ** is much more important. */ /* Move data in unmodified RRAs */ l = 0; for (rra = 0; rra < target_rra; rra++) { l += rrdnew.stat_head->ds_cnt * rrdnew.rra_def[rra].row_cnt; } while (l > 0) { rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1); rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1); l--; } /* Move data in this RRA, either removing or adding some rows */ if (modify > 0) { /* Adding extra rows; insert unknown values just after the ** current row number. */ l = rrdnew.stat_head->ds_cnt * (rrdnew.rra_ptr[target_rra].cur_row + 1); while (l > 0) { rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1); rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1); l--; } buffer = DNAN; l = rrdnew.stat_head->ds_cnt * modify; while (l > 0) { rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1); l--; } } else { /* Removing rows. Normally this would be just after the cursor ** however this may also mean that we wrap to the beginning of ** the array. */ signed long int remove_end = 0; remove_end = (rrdnew.rra_ptr[target_rra].cur_row - modify) % rrdnew.rra_def[target_rra].row_cnt; if (remove_end <= (signed long int) rrdnew.rra_ptr[target_rra].cur_row) { while (remove_end >= 0) { rrd_seek(rrd_file, sizeof(rrd_value_t) * rrdnew.stat_head->ds_cnt, SEEK_CUR); rrdnew.rra_ptr[target_rra].cur_row--; rrdnew.rra_def[target_rra].row_cnt--; remove_end--; modify++; } remove_end = rrdnew.rra_def[target_rra].row_cnt - 1; } for (l = 0; l <= rrdnew.rra_ptr[target_rra].cur_row; l++) { unsigned int tmp; for (tmp = 0; tmp < rrdnew.stat_head->ds_cnt; tmp++) { rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1); rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1); } } while (modify < 0) { rrd_seek(rrd_file, sizeof(rrd_value_t) * rrdnew.stat_head->ds_cnt, SEEK_CUR); rrdnew.rra_def[target_rra].row_cnt--; modify++; } } /* Move the rest of the CDPs */ while (1) { ssize_t b_read; if ((b_read=rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1)) <= 0) break; if(rrd_out_file->pos+b_read > rrd_out_file->file_len) { fprintf(stderr,"WARNING: ignoring last %zu bytes\nWARNING: if you see this message multiple times for a single file you're in trouble\n", b_read); continue; } rrd_write(rrd_out_file, &buffer, b_read); } rrdnew.rra_def[target_rra].row_cnt += modify; rrd_seek(rrd_out_file, sizeof(stat_head_t) + sizeof(ds_def_t) * rrdnew.stat_head->ds_cnt, SEEK_SET); rrd_write(rrd_out_file, rrdnew.rra_def, sizeof(rra_def_t) * rrdnew.stat_head->rra_cnt); rrd_seek(rrd_out_file, sizeof(live_head_t), SEEK_CUR); rrd_seek(rrd_out_file, sizeof(pdp_prep_t) * rrdnew.stat_head->ds_cnt, SEEK_CUR); rrd_seek(rrd_out_file, sizeof(cdp_prep_t) * rrdnew.stat_head->ds_cnt * rrdnew.stat_head->rra_cnt, SEEK_CUR); rrd_write(rrd_out_file, rrdnew.rra_ptr, sizeof(rra_ptr_t) * rrdnew.stat_head->rra_cnt); rrd_close(rrd_file); rrd_close(rrd_out_file); rrd_free(&rrdold); rrdnew.ds_def = NULL; rrdnew.live_head = NULL; rrdnew.pdp_prep = NULL; rrdnew.cdp_prep = NULL; rrd_free(&rrdnew); return (0); }
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; }
int rrd_write(char *file_name, rrd_t *rrd, char force_overwrite) { unsigned long i,ii,val_cnt; FILE *rrd_file=NULL; int fdflags; int fd; if (strcmp("-",file_name)==0){ rrd_file= stdout; } else { #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) fdflags = O_RDWR|O_BINARY|O_CREAT; #else fdflags = O_WRONLY|O_CREAT; #endif if (force_overwrite == 0) { fdflags |= O_EXCL; } fd = open(file_name,fdflags,0666); if (fd == -1 || (rrd_file = fdopen(fd,"wb")) == NULL) { rrd_set_error("creating '%s': %s",file_name,rrd_strerror(errno)); if (fd != -1) close(fd); return(-1); } } fwrite(rrd->stat_head, sizeof(stat_head_t), 1, rrd_file); fwrite(rrd->ds_def, sizeof(ds_def_t), rrd->stat_head->ds_cnt, rrd_file); fwrite(rrd->rra_def, sizeof(rra_def_t), rrd->stat_head->rra_cnt, rrd_file); /* maybe the xml hold an old formatted rrd */ if (atoi(rrd->stat_head->version) < 3) fwrite(&(rrd->live_head->last_up), sizeof(long),1, rrd_file); else fwrite(rrd->live_head, sizeof(live_head_t),1, rrd_file); fwrite( rrd->pdp_prep, sizeof(pdp_prep_t),rrd->stat_head->ds_cnt,rrd_file); fwrite( rrd->cdp_prep, sizeof(cdp_prep_t),rrd->stat_head->rra_cnt* rrd->stat_head->ds_cnt,rrd_file); fwrite( rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt,rrd_file); /* calculate the number of rrd_values to dump */ val_cnt=0; for(i=0; i < rrd->stat_head->rra_cnt; i++) for(ii=0; ii < rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt;ii++) val_cnt++; fwrite( rrd->rrd_value, sizeof(rrd_value_t),val_cnt,rrd_file); /* lets see if we had an error */ if(ferror(rrd_file)){ rrd_set_error("a file error occurred while creating '%s'",file_name); fclose(rrd_file); return(-1); } fclose(rrd_file); return 0; }
/* HandleInputLine is NOT thread safe - due to readdir issues, resolving them portably is not really simple. */ int HandleInputLine( int argc, char **argv, FILE * out) { #if defined(HAVE_OPENDIR) && defined (HAVE_READDIR) DIR *curdir; /* to read current dir with ls */ struct dirent *dent; #endif #if defined(HAVE_SYS_STAT_H) struct stat st; #endif /* Reset errno to 0 before we start. */ if (RemoteMode) { if (argc > 1 && strcmp("quit", argv[1]) == 0) { if (argc > 2) { printf("ERROR: invalid parameter count for quit\n"); return (1); } exit(0); } #if defined(HAVE_OPENDIR) && defined(HAVE_READDIR) && defined(HAVE_CHDIR) if (argc > 1 && strcmp("cd", argv[1]) == 0) { if (argc > 3) { printf("ERROR: invalid parameter count for cd\n"); return (1); } #if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID) if (getuid() == 0 && !ChangeRoot) { printf ("ERROR: chdir security problem - rrdtool is running as " "root but not chroot!\n"); return (1); } #endif if (chdir(argv[2]) != 0) { printf("ERROR: chdir %s %s\n", argv[2], rrd_strerror(errno)); return (1); } return (0); } if (argc > 1 && strcmp("pwd", argv[1]) == 0) { char *cwd; /* To hold current working dir on call to pwd */ if (argc > 2) { printf("ERROR: invalid parameter count for pwd\n"); return (1); } cwd = getcwd(NULL, MAXPATH); if (cwd == NULL) { printf("ERROR: getcwd %s\n", rrd_strerror(errno)); return (1); } printf("%s\n", cwd); free(cwd); return (0); } if (argc > 1 && strcmp("mkdir", argv[1]) == 0) { if (argc > 3) { printf("ERROR: invalid parameter count for mkdir\n"); return (1); } #if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID) if (getuid() == 0 && !ChangeRoot) { printf ("ERROR: mkdir security problem - rrdtool is running as " "root but not chroot!\n"); return (1); } #endif if(mkdir(argv[2], 0777)!=0) { printf("ERROR: mkdir %s: %s\n", argv[2],rrd_strerror(errno)); return (1); } return (0); } if (argc > 1 && strcmp("ls", argv[1]) == 0) { if (argc > 2) { printf("ERROR: invalid parameter count for ls\n"); return (1); } if ((curdir = opendir(".")) != NULL) { while ((dent = readdir(curdir)) != NULL) { if (!stat(dent->d_name, &st)) { if (S_ISDIR(st.st_mode)) { printf("d %s\n", dent->d_name); } if (strlen(dent->d_name) > 4 && S_ISREG(st.st_mode)) { if (!strcmp (dent->d_name + NAMLEN(dent) - 4, ".rrd") || !strcmp(dent->d_name + NAMLEN(dent) - 4, ".RRD")) { printf("- %s\n", dent->d_name); } } } } closedir(curdir); } else { printf("ERROR: opendir .: %s\n", rrd_strerror(errno)); return (errno); } return (0); } #endif /* opendir and readdir */ } if (argc < 3 || strcmp("help", argv[1]) == 0 || strcmp("--help", argv[1]) == 0 || strcmp("-help", argv[1]) == 0 || strcmp("-?", argv[1]) == 0 || strcmp("-h", argv[1]) == 0) { PrintUsage(""); return 0; } if (strcmp("create", argv[1]) == 0) rrd_create(argc - 1, &argv[1]); else if (strcmp("dump", argv[1]) == 0) rrd_dump(argc - 1, &argv[1]); else if (strcmp("info", argv[1]) == 0 || strcmp("updatev", argv[1]) == 0) { rrd_info_t *data; if (strcmp("info", argv[1]) == 0) data = rrd_info(argc - 1, &argv[1]); else data = rrd_update_v(argc - 1, &argv[1]); rrd_info_print(data); rrd_info_free(data); } else if (strcmp("--version", argv[1]) == 0 || strcmp("version", argv[1]) == 0 || strcmp("v", argv[1]) == 0 || strcmp("-v", argv[1]) == 0 || strcmp("-version", argv[1]) == 0) printf("RRDtool " PACKAGE_VERSION " Copyright by Tobi Oetiker, 1997-2008 (%f)\n", rrd_version()); else if (strcmp("restore", argv[1]) == 0) rrd_restore(argc - 1, &argv[1]); else if (strcmp("resize", argv[1]) == 0) rrd_resize(argc - 1, &argv[1]); else if (strcmp("last", argv[1]) == 0) printf("%ld\n", rrd_last(argc - 1, &argv[1])); else if (strcmp("lastupdate", argv[1]) == 0) { rrd_lastupdate(argc - 1, &argv[1]); } else if (strcmp("first", argv[1]) == 0) printf("%ld\n", rrd_first(argc - 1, &argv[1])); else if (strcmp("update", argv[1]) == 0) rrd_update(argc - 1, &argv[1]); else if (strcmp("fetch", argv[1]) == 0) { time_t start, end, ti; unsigned long step, ds_cnt, i, ii; rrd_value_t *data, *datai; char **ds_namv; if (rrd_fetch (argc - 1, &argv[1], &start, &end, &step, &ds_cnt, &ds_namv, &data) != -1) { datai = data; printf(" "); for (i = 0; i < ds_cnt; i++) printf("%20s", ds_namv[i]); printf("\n\n"); for (ti = start + step; ti <= end; ti += step) { printf("%10lu:", ti); for (ii = 0; ii < ds_cnt; ii++) printf(" %0.10e", *(datai++)); printf("\n"); } for (i = 0; i < ds_cnt; i++) free(ds_namv[i]); free(ds_namv); free(data); } } else if (strcmp("xport", argv[1]) == 0) { int xxsize; unsigned long int j = 0; time_t start, end, ti; unsigned long step, col_cnt, row_cnt; rrd_value_t *data, *ptr; char **legend_v; int enumds = 0; int i; size_t vtag_s = strlen(COL_DATA_TAG) + 10; char *vtag = malloc(vtag_s); for (i = 2; i < argc; i++) { if (strcmp("--enumds", argv[i]) == 0) enumds = 1; } if (rrd_xport (argc - 1, &argv[1], &xxsize, &start, &end, &step, &col_cnt, &legend_v, &data) != -1) { char *old_locale = setlocale(LC_NUMERIC, "C"); row_cnt = (end - start) / step; ptr = data; printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n\n", XML_ENCODING); printf("<%s>\n", ROOT_TAG); printf(" <%s>\n", META_TAG); printf(" <%s>%lld</%s>\n", META_START_TAG, (long long int) start + step, META_START_TAG); printf(" <%s>%lu</%s>\n", META_STEP_TAG, step, META_STEP_TAG); printf(" <%s>%lld</%s>\n", META_END_TAG, (long long int) end, META_END_TAG); printf(" <%s>%lu</%s>\n", META_ROWS_TAG, row_cnt, META_ROWS_TAG); printf(" <%s>%lu</%s>\n", META_COLS_TAG, col_cnt, META_COLS_TAG); printf(" <%s>\n", LEGEND_TAG); for (j = 0; j < col_cnt; j++) { char *entry = NULL; entry = legend_v[j]; printf(" <%s>%s</%s>\n", LEGEND_ENTRY_TAG, entry, LEGEND_ENTRY_TAG); free(entry); } free(legend_v); printf(" </%s>\n", LEGEND_TAG); printf(" </%s>\n", META_TAG); printf(" <%s>\n", DATA_TAG); for (ti = start + step; ti <= end; ti += step) { printf(" <%s>", DATA_ROW_TAG); printf("<%s>%lld</%s>", COL_TIME_TAG, (long long int)ti, COL_TIME_TAG); for (j = 0; j < col_cnt; j++) { rrd_value_t newval = DNAN; if (enumds == 1) snprintf(vtag, vtag_s, "%s%lu", COL_DATA_TAG, j); else snprintf(vtag, vtag_s, "%s", COL_DATA_TAG); newval = *ptr; if (isnan(newval)) { printf("<%s>NaN</%s>", vtag, vtag); } else { printf("<%s>%0.10e</%s>", vtag, newval, vtag); }; ptr++; } printf("</%s>\n", DATA_ROW_TAG); } free(data); printf(" </%s>\n", DATA_TAG); printf("</%s>\n", ROOT_TAG); setlocale(LC_NUMERIC, old_locale); } free(vtag); } else if (strcmp("graph", argv[1]) == 0) { char **calcpr; #ifdef notused /*XXX*/ const char *imgfile = argv[2]; /* rrd_graph changes argv pointer */ #endif int xsize, ysize; double ymin, ymax; int i; int tostdout = (strcmp(argv[2], "-") == 0); int imginfo = 0; for (i = 2; i < argc; i++) { if (strcmp(argv[i], "--imginfo") == 0 || strcmp(argv[i], "-f") == 0) { imginfo = 1; break; } } if (rrd_graph (argc - 1, &argv[1], &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) != -1) { if (!tostdout && !imginfo) printf("%dx%d\n", xsize, ysize); if (calcpr) { for (i = 0; calcpr[i]; i++) { if (!tostdout) printf("%s\n", calcpr[i]); free(calcpr[i]); } free(calcpr); } } } else if (strcmp("graphv", argv[1]) == 0) { rrd_info_t *grinfo = NULL; /* 1 to distinguish it from the NULL that rrd_graph sends in */ grinfo = rrd_graph_v(argc - 1, &argv[1]); if (grinfo) { rrd_info_print(grinfo); rrd_info_free(grinfo); } } else if (strcmp("tune", argv[1]) == 0) rrd_tune(argc - 1, &argv[1]); else if (strcmp("flushcached", argv[1]) == 0) rrd_flushcached(argc - 1, &argv[1]); else { rrd_set_error("unknown function '%s'", argv[1]); } if (rrd_test_error()) { fprintf(out, "ERROR: %s\n", rrd_get_error()); rrd_clear_error(); return 1; } return (0); }
int main( int argc, char *argv[]) { char **myargv; char *aLine; char *firstdir = ""; #ifdef MUST_DISABLE_SIGFPE signal(SIGFPE, SIG_IGN); #endif #ifdef MUST_DISABLE_FPMASK fpsetmask(0); #endif /* initialize locale settings according to localeconv(3) */ setlocale(LC_ALL, ""); #if defined(WIN32) && !defined(__CYGWIN__) setmode(fileno(stdout), O_BINARY); setmode(fileno(stdin), O_BINARY); #endif #if defined(HAVE_LIBINTL_H) && defined(BUILD_LIBINTL) bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); textdomain(GETTEXT_PACKAGE); #endif if (argc == 1) { PrintUsage(""); return 0; } if (((argc == 2) || (argc == 3)) && !strcmp("-", argv[1])) { #if HAVE_GETRUSAGE struct rusage myusage; struct timeval starttime; struct timeval currenttime; gettimeofday(&starttime, NULL); #endif RemoteMode = 1; if ((argc == 3) && strcmp("", argv[2])) { if ( #ifdef HAVE_GETUID getuid() #else 1 #endif == 0) { #ifdef HAVE_CHROOT if (chroot(argv[2]) != 0){ fprintf(stderr, "ERROR: chroot %s: %s\n", argv[2],rrd_strerror(errno)); exit(errno); } ChangeRoot = 1; firstdir = "/"; #else fprintf(stderr, "ERROR: change root is not supported by your OS " "or at least by this copy of rrdtool\n"); exit(1); #endif } else { firstdir = argv[2]; } } if (strcmp(firstdir, "")) { if (chdir(firstdir) != 0){ fprintf(stderr, "ERROR: chdir %s %s\n", firstdir,rrd_strerror(errno)); exit(errno); } } while (fgetslong(&aLine, stdin)) { char *aLineOrig = aLine; if ((argc = CountArgs(aLine)) == 0) { free(aLine); printf("ERROR: not enough arguments\n"); continue; } if ((myargv = (char **) malloc((argc + 1) * sizeof(char *))) == NULL) { perror("malloc"); exit(1); } if ((argc = CreateArgs(argv[0], aLine, myargv)) < 0) { printf("ERROR: creating arguments\n"); } else { if ( HandleInputLine(argc, myargv, stdout) == 0 ){ #if HAVE_GETRUSAGE getrusage(RUSAGE_SELF, &myusage); gettimeofday(¤ttime, NULL); printf("OK u:%1.2f s:%1.2f r:%1.2f\n", (double) myusage.ru_utime.tv_sec + (double) myusage.ru_utime.tv_usec / 1000000.0, (double) myusage.ru_stime.tv_sec + (double) myusage.ru_stime.tv_usec / 1000000.0, (double) (currenttime.tv_sec - starttime.tv_sec) + (double) (currenttime.tv_usec - starttime.tv_usec) / 1000000.0); #else printf("OK\n"); #endif } } fflush(stdout); /* this is important for pipes to work */ free(myargv); free(aLineOrig); } } else if (argc == 2) { PrintUsage(argv[1]); exit(0); } else if (argc == 3 && !strcmp(argv[1], "help")) { PrintUsage(argv[2]); exit(0); } else { exit(HandleInputLine(argc, argv, stderr)); } return 0; }
int rrd_create_fn( const char *file_name, rrd_t *rrd) { unsigned long i, ii; rrd_value_t *unknown; int unkn_cnt; rrd_file_t *rrd_file_dn; rrd_t rrd_dn; unsigned rrd_flags = RRD_READWRITE | RRD_CREAT; if (opt_no_overwrite) { rrd_flags |= RRD_EXCL ; } unkn_cnt = 0; for (i = 0; i < rrd->stat_head->rra_cnt; i++) unkn_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[i].row_cnt; if ((rrd_file_dn = rrd_open(file_name, rrd, rrd_flags)) == NULL) { rrd_set_error("creating '%s': %s", file_name, rrd_strerror(errno)); rrd_free2(rrd); return (-1); } rrd_write(rrd_file_dn, rrd->stat_head, sizeof(stat_head_t)); rrd_write(rrd_file_dn, rrd->ds_def, sizeof(ds_def_t) * rrd->stat_head->ds_cnt); rrd_write(rrd_file_dn, rrd->rra_def, sizeof(rra_def_t) * rrd->stat_head->rra_cnt); rrd_write(rrd_file_dn, rrd->live_head, sizeof(live_head_t)); if ((rrd->pdp_prep = (pdp_prep_t*)calloc(1, sizeof(pdp_prep_t))) == NULL) { rrd_set_error("allocating pdp_prep"); rrd_free2(rrd); rrd_close(rrd_file_dn); return (-1); } strcpy(rrd->pdp_prep->last_ds, "U"); rrd->pdp_prep->scratch[PDP_val].u_val = 0.0; rrd->pdp_prep->scratch[PDP_unkn_sec_cnt].u_cnt = rrd->live_head->last_up % rrd->stat_head->pdp_step; for (i = 0; i < rrd->stat_head->ds_cnt; i++) rrd_write(rrd_file_dn, rrd->pdp_prep, sizeof(pdp_prep_t)); if ((rrd->cdp_prep = (cdp_prep_t*)calloc(1, sizeof(cdp_prep_t))) == NULL) { rrd_set_error("allocating cdp_prep"); rrd_free2(rrd); rrd_close(rrd_file_dn); return (-1); } for (i = 0; i < rrd->stat_head->rra_cnt; i++) { switch (cf_conv(rrd->rra_def[i].cf_nam)) { case CF_HWPREDICT: case CF_MHWPREDICT: init_hwpredict_cdp(rrd->cdp_prep); break; case CF_SEASONAL: case CF_DEVSEASONAL: init_seasonal_cdp(rrd->cdp_prep); break; case CF_FAILURES: /* initialize violation history to 0 */ for (ii = 0; ii < MAX_CDP_PAR_EN; ii++) { /* We can zero everything out, by setting u_val to the * NULL address. Each array entry in scratch is 8 bytes * (a double), but u_cnt only accessed 4 bytes (long) */ rrd->cdp_prep->scratch[ii].u_val = 0.0; } break; default: /* can not be zero because we don't know anything ... */ rrd->cdp_prep->scratch[CDP_val].u_val = DNAN; /* startup missing pdp count */ rrd->cdp_prep->scratch[CDP_unkn_pdp_cnt].u_cnt = ((rrd->live_head->last_up - rrd->pdp_prep->scratch[PDP_unkn_sec_cnt].u_cnt) % (rrd->stat_head->pdp_step * rrd->rra_def[i].pdp_cnt)) / rrd->stat_head->pdp_step; break; } for (ii = 0; ii < rrd->stat_head->ds_cnt; ii++) { rrd_write(rrd_file_dn, rrd->cdp_prep, sizeof(cdp_prep_t)); } } /* now, we must make sure that the rest of the rrd struct is properly initialized */ if ((rrd->rra_ptr = (rra_ptr_t*)calloc(1, sizeof(rra_ptr_t))) == NULL) { rrd_set_error("allocating rra_ptr"); rrd_free2(rrd); rrd_close(rrd_file_dn); return (-1); } /* changed this initialization to be consistent with * rrd_restore. With the old value (0), the first update * would occur for cur_row = 1 because rrd_update increments * the pointer a priori. */ for (i = 0; i < rrd->stat_head->rra_cnt; i++) { rrd->rra_ptr->cur_row = rrd_select_initial_row(rrd_file_dn, i, &rrd->rra_def[i]); rrd_write(rrd_file_dn, rrd->rra_ptr, sizeof(rra_ptr_t)); } /* write the empty data area */ if ((unknown = (rrd_value_t *) malloc(512 * sizeof(rrd_value_t))) == NULL) { rrd_set_error("allocating unknown"); rrd_free2(rrd); rrd_close(rrd_file_dn); return (-1); } for (i = 0; i < 512; ++i) unknown[i] = DNAN; while (unkn_cnt > 0) { if(rrd_write(rrd_file_dn, unknown, sizeof(rrd_value_t) * min(unkn_cnt, 512)) < 0) { rrd_set_error("creating rrd: %s", rrd_strerror(errno)); return -1; } unkn_cnt -= 512; } free(unknown); rrd_free2(rrd); if (rrd_close(rrd_file_dn) == -1) { rrd_set_error("creating rrd: %s", rrd_strerror(errno)); return -1; } /* flush all we don't need out of the cache */ rrd_init(&rrd_dn); if((rrd_file_dn = rrd_open(file_name, &rrd_dn, RRD_READONLY)) != NULL) { rrd_dontneed(rrd_file_dn, &rrd_dn); /* rrd_free(&rrd_dn); */ rrd_close(rrd_file_dn); } return (0); }
/* Smooth a periodic array with a moving average: equal weights and * length = 5% of the period. */ int apply_smoother( rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start, rrd_file_t *rrd_file) { unsigned long i, j, k; unsigned long totalbytes; rrd_value_t *rrd_values; unsigned long row_length = rrd->stat_head->ds_cnt; unsigned long row_count = rrd->rra_def[rra_idx].row_cnt; unsigned long offset; FIFOqueue **buffers; rrd_value_t *working_average; rrd_value_t *rrd_values_cpy; rrd_value_t *baseline; if (atoi(rrd->stat_head->version) >= 4) { offset = floor(rrd->rra_def[rra_idx]. par[RRA_seasonal_smoothing_window]. u_val / 2 * row_count); } else { offset = floor(0.05 / 2 * row_count); } if (offset == 0) return 0; /* no smoothing */ /* allocate memory */ totalbytes = sizeof(rrd_value_t) * row_length * row_count; rrd_values = (rrd_value_t *) malloc(totalbytes); if (rrd_values == NULL) { rrd_set_error("apply smoother: memory allocation failure"); return -1; } /* rra_start is at the beginning of this rra */ if (rrd_seek(rrd_file, rra_start, SEEK_SET)) { rrd_set_error("seek to rra %d failed", rra_start); free(rrd_values); return -1; } /* could read all data in a single block, but we need to * check for NA values */ for (i = 0; i < row_count; ++i) { for (j = 0; j < row_length; ++j) { if (rrd_read (rrd_file, &(rrd_values[i * row_length + j]), sizeof(rrd_value_t) * 1) != (ssize_t) (sizeof(rrd_value_t) * 1)) { rrd_set_error("reading value failed: %s", rrd_strerror(errno)); } if (isnan(rrd_values[i * row_length + j])) { /* can't apply smoothing, still uninitialized values */ #ifdef DEBUG fprintf(stderr, "apply_smoother: NA detected in seasonal array: %ld %ld\n", i, j); #endif free(rrd_values); return 0; } } } /* allocate queues, one for each data source */ buffers = (FIFOqueue **) malloc(sizeof(FIFOqueue *) * row_length); for (i = 0; i < row_length; ++i) { queue_alloc(&(buffers[i]), 2 * offset + 1); } /* need working average initialized to 0 */ working_average = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t)); baseline = (rrd_value_t *) calloc(row_length, sizeof(rrd_value_t)); /* compute sums of the first 2*offset terms */ for (i = 0; i < 2 * offset; ++i) { k = MyMod(i - offset, row_count); for (j = 0; j < row_length; ++j) { queue_push(buffers[j], rrd_values[k * row_length + j]); working_average[j] += rrd_values[k * row_length + j]; } } /* as we are working through the value, we have to make sure to not double apply the smoothing after wrapping around. so best is to copy the rrd_values first */ rrd_values_cpy = (rrd_value_t *) calloc(row_length*row_count, sizeof(rrd_value_t)); memcpy(rrd_values_cpy,rrd_values,sizeof(rrd_value_t)*row_length*row_count); /* compute moving averages */ for (i = offset; i < row_count + offset; ++i) { for (j = 0; j < row_length; ++j) { k = MyMod(i, row_count); /* add a term to the sum */ working_average[j] += rrd_values_cpy[k * row_length + j]; queue_push(buffers[j], rrd_values_cpy[k * row_length + j]); /* reset k to be the center of the window */ k = MyMod(i - offset, row_count); /* overwrite rdd_values entry, the old value is already * saved in buffers */ rrd_values[k * row_length + j] = working_average[j] / (2 * offset + 1); baseline[j] += rrd_values[k * row_length + j]; /* remove a term from the sum */ working_average[j] -= queue_pop(buffers[j]); } } for (i = 0; i < row_length; ++i) { queue_dealloc(buffers[i]); baseline[i] /= row_count; } free(rrd_values_cpy); free(buffers); free(working_average); if (cf_conv(rrd->rra_def[rra_idx].cf_nam) == CF_SEASONAL) { rrd_value_t ( *init_seasonality) ( rrd_value_t seasonal_coef, rrd_value_t intercept); switch (cf_conv(rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam)) { case CF_HWPREDICT: init_seasonality = hw_additive_init_seasonality; break; case CF_MHWPREDICT: init_seasonality = hw_multiplicative_init_seasonality; break; default: rrd_set_error("apply smoother: SEASONAL rra doesn't have " "valid dependency: %s", rrd->rra_def[hw_dep_idx(rrd, rra_idx)].cf_nam); return -1; } for (j = 0; j < row_length; ++j) { for (i = 0; i < row_count; ++i) { rrd_values[i * row_length + j] = init_seasonality(rrd_values[i * row_length + j], baseline[j]); } /* update the baseline coefficient, * first, compute the cdp_index. */ offset = hw_dep_idx(rrd, rra_idx) * row_length + j; (rrd->cdp_prep[offset]).scratch[CDP_hw_intercept].u_val += baseline[j]; } /* if we are not running on mmap, lets write stuff to disk now */ #ifndef HAVE_MMAP /* flush cdp to disk */ if (rrd_seek(rrd_file, sizeof(stat_head_t) + rrd->stat_head->ds_cnt * sizeof(ds_def_t) + rrd->stat_head->rra_cnt * sizeof(rra_def_t) + sizeof(live_head_t) + rrd->stat_head->ds_cnt * sizeof(pdp_prep_t), SEEK_SET)) { rrd_set_error("apply_smoother: seek to cdp_prep failed"); free(rrd_values); return -1; } if (rrd_write(rrd_file, rrd->cdp_prep, sizeof(cdp_prep_t) * (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt) != (ssize_t) (sizeof(cdp_prep_t) * (rrd->stat_head->rra_cnt) * (rrd->stat_head->ds_cnt))) { rrd_set_error("apply_smoother: cdp_prep write failed"); free(rrd_values); return -1; } #endif } /* endif CF_SEASONAL */ /* flush updated values to disk */ if (rrd_seek(rrd_file, rra_start, SEEK_SET)) { rrd_set_error("apply_smoother: seek to pos %d failed", rra_start); free(rrd_values); return -1; } /* write as a single block */ if (rrd_write (rrd_file, rrd_values, sizeof(rrd_value_t) * row_length * row_count) != (ssize_t) (sizeof(rrd_value_t) * row_length * row_count)) { rrd_set_error("apply_smoother: write failed to %lu", rra_start); free(rrd_values); return -1; } free(rrd_values); free(baseline); return 0; }
/* HandleInputLine is NOT thread safe - due to readdir issues, resolving them portably is not really simple. */ int HandleInputLine( int argc, char **argv, FILE * out) { #if defined(HAVE_OPENDIR) && defined (HAVE_READDIR) DIR *curdir; /* to read current dir with ls */ struct dirent *dent; #endif /* Reset errno to 0 before we start. */ if (RemoteMode) { if (argc > 1 && strcmp("quit", argv[1]) == 0) { if (argc != 2) { printf("ERROR: invalid parameter count for quit\n"); return (1); } exit(0); } #if defined(HAVE_OPENDIR) && defined(HAVE_READDIR) && defined(HAVE_CHDIR) && defined(HAVE_SYS_STAT_H) if (argc > 1 && strcmp("cd", argv[1]) == 0) { if (argc != 3) { printf("ERROR: invalid parameter count for cd\n"); return (1); } #if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID) if (getuid() == 0 && !ChangeRoot) { printf ("ERROR: chdir security problem - rrdtool is running as " "root but not chroot!\n"); return (1); } #endif if (chdir(argv[2]) != 0){ printf("ERROR: chdir %s %s\n", argv[2], rrd_strerror(errno)); return (1); } return (0); } if (argc > 1 && strcmp("pwd", argv[1]) == 0) { char *cwd; /* To hold current working dir on call to pwd */ if (argc != 2) { printf("ERROR: invalid parameter count for pwd\n"); return (1); } cwd = getcwd(NULL, MAXPATH); if (cwd == NULL) { printf("ERROR: getcwd %s\n", rrd_strerror(errno)); return (1); } printf("%s\n", cwd); free(cwd); return (0); } if (argc > 1 && strcmp("mkdir", argv[1]) == 0) { if (argc != 3) { printf("ERROR: invalid parameter count for mkdir\n"); return (1); } #if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID) if (getuid() == 0 && !ChangeRoot) { printf ("ERROR: mkdir security problem - rrdtool is running as " "root but not chroot!\n"); return (1); } #endif if(mkdir(argv[2], 0777)!=0){ printf("ERROR: mkdir %s: %s\n", argv[2],rrd_strerror(errno)); return (1); } return (0); } if (argc > 1 && strcmp("ls", argv[1]) == 0) { if (argc != 2) { printf("ERROR: invalid parameter count for ls\n"); return (1); } if ((curdir = opendir(".")) != NULL) { struct stat st; while ((dent = readdir(curdir)) != NULL) { if (!stat(dent->d_name, &st)) { if (S_ISDIR(st.st_mode)) { printf("d %s\n", dent->d_name); } if (strlen(dent->d_name) > 4 && S_ISREG(st.st_mode)) { if (!strcmp (dent->d_name + NAMLEN(dent) - 4, ".rrd") || !strcmp(dent->d_name + NAMLEN(dent) - 4, ".RRD")) { printf("- %s\n", dent->d_name); } } } } closedir(curdir); } else { printf("ERROR: opendir .: %s\n", rrd_strerror(errno)); return (errno); } return (0); } #endif /* opendir and readdir */ } if (argc < 3 || strcmp("help", argv[1]) == 0 || strcmp("--help", argv[1]) == 0 || strcmp("-help", argv[1]) == 0 || strcmp("-?", argv[1]) == 0 || strcmp("-h", argv[1]) == 0) { PrintUsage(""); return 0; } if (strcmp("create", argv[1]) == 0) rrd_create(argc - 1, &argv[1]); else if (strcmp("dump", argv[1]) == 0) rrd_dump(argc - 1, &argv[1]); else if (strcmp("info", argv[1]) == 0 || strcmp("updatev", argv[1]) == 0) { rrd_info_t *data; if (strcmp("info", argv[1]) == 0) data = rrd_info(argc - 1, &argv[1]); else data = rrd_update_v(argc - 1, &argv[1]); rrd_info_print(data); rrd_info_free(data); } else if (strcmp("--version", argv[1]) == 0 || strcmp("version", argv[1]) == 0 || strcmp("v", argv[1]) == 0 || strcmp("-v", argv[1]) == 0 || strcmp("-version", argv[1]) == 0) printf("RRDtool " PACKAGE_VERSION " Copyright by Tobi Oetiker, 1997-2008 (%f)\n", rrd_version()); else if (strcmp("restore", argv[1]) == 0) rrd_restore(argc - 1, &argv[1]); else if (strcmp("resize", argv[1]) == 0) rrd_resize(argc - 1, &argv[1]); else if (strcmp("last", argv[1]) == 0) printf("%ld\n", rrd_last(argc - 1, &argv[1])); else if (strcmp("lastupdate", argv[1]) == 0) { rrd_lastupdate(argc - 1, &argv[1]); } else if (strcmp("first", argv[1]) == 0) printf("%ld\n", rrd_first(argc - 1, &argv[1])); else if (strcmp("update", argv[1]) == 0) rrd_update(argc - 1, &argv[1]); else if (strcmp("fetch", argv[1]) == 0) { time_t start, end, ti; unsigned long step, ds_cnt, i, ii; rrd_value_t *data, *datai; char **ds_namv; if (rrd_fetch (argc - 1, &argv[1], &start, &end, &step, &ds_cnt, &ds_namv, &data) == 0) { datai = data; printf(" "); for (i = 0; i < ds_cnt; i++) printf("%20s", ds_namv[i]); printf("\n\n"); for (ti = start + step; ti <= end; ti += step) { printf("%10lu:", ti); for (ii = 0; ii < ds_cnt; ii++) printf(" %0.10e", *(datai++)); printf("\n"); } for (i = 0; i < ds_cnt; i++) free(ds_namv[i]); free(ds_namv); free(data); } } else if (strcmp("xport", argv[1]) == 0) { #ifdef HAVE_RRD_GRAPH time_t start, end; unsigned long step, col_cnt; rrd_value_t *data; char **legend_v; rrd_xport (argc - 1, &argv[1], NULL, &start, &end, &step, &col_cnt, &legend_v, &data); #else rrd_set_error("the instance of rrdtool has been compiled without graphics"); #endif } else if (strcmp("graph", argv[1]) == 0) { #ifdef HAVE_RRD_GRAPH char **calcpr; #ifdef notused /*XXX*/ const char *imgfile = argv[2]; /* rrd_graph changes argv pointer */ #endif int xsize, ysize; double ymin, ymax; int i; int tostdout = (strcmp(argv[2], "-") == 0); int imginfo = 0; for (i = 2; i < argc; i++) { if (strcmp(argv[i], "--imginfo") == 0 || strcmp(argv[i], "-f") == 0) { imginfo = 1; break; } } if (rrd_graph (argc - 1, &argv[1], &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) == 0) { if (!tostdout && !imginfo) printf("%dx%d\n", xsize, ysize); if (calcpr) { for (i = 0; calcpr[i]; i++) { if (!tostdout) printf("%s\n", calcpr[i]); free(calcpr[i]); } free(calcpr); } } #else rrd_set_error("the instance of rrdtool has been compiled without graphics"); #endif } else if (strcmp("graphv", argv[1]) == 0) { #ifdef HAVE_RRD_GRAPH rrd_info_t *grinfo = NULL; /* 1 to distinguish it from the NULL that rrd_graph sends in */ grinfo = rrd_graph_v(argc - 1, &argv[1]); if (grinfo) { rrd_info_print(grinfo); rrd_info_free(grinfo); } #else rrd_set_error("the instance of rrdtool has been compiled without graphics"); #endif } else if (strcmp("tune", argv[1]) == 0) rrd_tune(argc - 1, &argv[1]); else if (strcmp("flushcached", argv[1]) == 0) rrd_flushcached(argc - 1, &argv[1]); else if (strcmp("modify", argv[1]) == 0) rrd_modify(argc - 1, &argv[1]); else { rrd_set_error("unknown function '%s'", argv[1]); } if (rrd_test_error()) { fprintf(out, "ERROR: %s\n", rrd_get_error()); rrd_clear_error(); return 1; } return (0); }
const char *rrdCreate(const char *filename, unsigned long step, time_t start, int argc, const char **argv) { int ret; ret = rrd_create_r(filename, step, start, argc, argv); return rrd_strerror(ret); }