From: Peter Stamfest Date: Sun, 23 Feb 2014 22:15:39 +0000 (+0100) Subject: add rrd_modify (but do not yet use it) X-Git-Tag: v1.5.0-rc1~132^2~1^2~9 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b4b135b81fac0832b27227901efcb760512dc1dd;p=thirdparty%2Frrdtool-1.x.git add rrd_modify (but do not yet use it) --- diff --git a/src/Makefile.am b/src/Makefile.am index d4674a31..79e3e131 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -45,7 +45,8 @@ RRD_C_FILES = \ rrd_flushcached.c \ rrd_fetch.c \ rrd_resize.c \ - rrd_tune.c + rrd_tune.c \ + rrd_modify.c if BUILD_RRDGRAPH RRD_C_FILES += rrd_graph.c \ diff --git a/src/librrd.sym.in.in b/src/librrd.sym.in.in index dd31b627..ebc605b2 100644 --- a/src/librrd.sym.in.in +++ b/src/librrd.sym.in.in @@ -36,6 +36,7 @@ rrd_last_r rrd_lastupdate rrd_lastupdate_r rrd_lock +rrd_modify rrd_mkdir_p rrd_new_context rrd_open diff --git a/src/rrd.h b/src/rrd.h index 40b80cfe..0a1319bd 100644 --- a/src/rrd.h +++ b/src/rrd.h @@ -194,6 +194,9 @@ extern "C" { int rrd_tune( int, char **); + int rrd_modify( + int, + char **); time_t rrd_last( int, char **); diff --git a/src/rrd_modify.c b/src/rrd_modify.c new file mode 100644 index 00000000..61ce4643 --- /dev/null +++ b/src/rrd_modify.c @@ -0,0 +1,438 @@ +/***************************************************************************** + * RRDtool 1.4.8 Copyright by Tobi Oetiker, 1997-2013 + ***************************************************************************** + * rrd_modify Structurally modify an RRD file + ***************************************************************************** + * Initially based on rrd_dump.c + *****************************************************************************/ +#include "rrd_tool.h" +#include "rrd_rpncalc.h" +#include "rrd_client.h" +#include "rrd_restore.h" /* write_file */ + +#include + +static void * copy_over_realloc(void *dest, int dest_index, + const void *src, int index, + ssize_t size) { + void *r = realloc(dest, size * (dest_index + 1)); + if (r == NULL) { + rrd_set_error("copy_over_realloc: realloc failed."); + return r; + } + + memcpy(((char*)r) + size * dest_index, ((char*)src) + size * index, size); + return r; +} + +static int rrd_modify_r(const char *infilename, + const char *outfilename, + const char **removeDS, + const char **addDS) { + rrd_t in, out; + int rc = -1; + unsigned int i, j; + rrd_file_t *rrd_file; + char *old_locale = ""; + char *ops = NULL; + + rrd_init(&in); + rrd_init(&out); + + rrd_file = rrd_open(infilename, &in, RRD_READONLY | RRD_READAHEAD); + if (rrd_file == NULL) { + goto done; + } + + /* copy over structure to out RRD */ + + out.stat_head = (stat_head_t *) malloc(sizeof(stat_head_t)); + if (out.stat_head == NULL) { + rrd_set_error("rrd_modify_r: malloc failed."); + goto done; + } + + memset(out.stat_head, 0, (sizeof(stat_head_t))); + + strncpy(out.stat_head->cookie, "RRD", sizeof(out.stat_head->cookie)); + strcpy(out.stat_head->version, "0003"); + out.stat_head->float_cookie = FLOAT_COOKIE; + out.stat_head->pdp_step = in.stat_head->pdp_step; + + out.stat_head->ds_cnt = 0; + out.stat_head->rra_cnt = 0; + + out.live_head = (live_head_t *) malloc(sizeof(live_head_t)); + if (out.live_head == NULL) { + rrd_set_error("parse_tag_rrd: malloc failed."); + goto done; + } + + out.live_head->last_up = in.live_head->last_up; + out.live_head->last_up_usec = in.live_head->last_up_usec; + + + ops = malloc(in.stat_head->ds_cnt); + if (ops == NULL) { + rrd_set_error("parse_tag_rrd: malloc failed."); + goto done; + } + + memset(ops, 'c', in.stat_head->ds_cnt); + + // check all DSs for deletion + for (i = 0 ; i < in.stat_head->ds_cnt ; i++) { + const char *c; + if (removeDS != NULL) { + for (j = 0, c = removeDS[j] ; c ; j++, c = removeDS[j]) { + if (strcmp(in.ds_def[i].ds_nam, c) == 0) { + ops[i] = 'd'; + break; + } + } + } + + if (ops[i] == 'c') { + j = out.stat_head->ds_cnt; + out.stat_head->ds_cnt++; + + out.ds_def = copy_over_realloc(out.ds_def, j, + in.ds_def, i, + sizeof(ds_def_t)); + if (out.ds_def == NULL) goto done; + + out.pdp_prep = copy_over_realloc(out.pdp_prep, j, + in.pdp_prep, i, + sizeof(pdp_prep_t)); + if (out.pdp_prep == NULL) goto done; + + } + } + + rra_ptr_t rra_0_ptr = { .cur_row = 0 }; + for (j = 0 ; j < in.stat_head->rra_cnt ; j++) { + + /* for every RRA copy only those CDPs in the prep area where we keep + the DS! */ + + out.cdp_prep = realloc(out.cdp_prep, + sizeof(cdp_prep_t) * out.stat_head->ds_cnt + * (j+1)); + int start_index_in = in.stat_head->ds_cnt * j; + int start_index_out = out.stat_head->ds_cnt * j; + + int ii; + for (i = ii = 0 ; i < in.stat_head->ds_cnt ; i++) { + if (ops[i] == 'c') { + memcpy(out.cdp_prep + start_index_out + ii, + in.cdp_prep + start_index_in + i, sizeof(cdp_prep_t)); + ii++; + } + } + + + + out.rra_def = copy_over_realloc(out.rra_def, j, + in.rra_def, j, + sizeof(rra_def_t)); + if (out.rra_def == NULL) goto done; + + out.rra_ptr = copy_over_realloc(out.rra_ptr, j, + &rra_0_ptr, 0, + sizeof(rra_ptr_t)); + if (out.rra_ptr == NULL) goto done; + + // out.rra_ptr[j].cur_row = out.rra_def[j].row_cnt - 1; + + out.stat_head->rra_cnt++; + } + + old_locale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + + /* read all data ... */ + + /* there seem to be two function in the current rrdtool codebase + dealing with writing a new rrd file to disk: write_file and + rrd_create_fn. The latter has the major problem, that it tries + to free data passed to it (WTF?), but write_file assumes + chronologically ordered data in RRAs (that is, in the data + space starting at rrd.rrd_value.... + + This is the reason why: + - we use write_file and + - why we reset cur_row in RRAs and reorder data to be cronological + */ + + + + char *all_data = NULL; + ssize_t total_size = 0, total_size_out = 0; + ssize_t rra_start = 0, rra_start_out = 0; + + int total_cnt = 0, total_cnt_out = 0; + + for (i = 0; i < in.stat_head->rra_cnt; i++) { + int rra_values = in.stat_head->ds_cnt * in.rra_def[i].row_cnt; + int rra_values_out = out.stat_head->ds_cnt * out.rra_def[i].row_cnt; + + ssize_t rra_size = sizeof(rrd_value_t) * rra_values; + ssize_t rra_size_out = sizeof(rrd_value_t) * rra_values_out; + /* + fprintf(stderr, "A %08x\n", &(in.rra_ptr[i])); + fprintf(stderr, "A %08x\n", in.rra_ptr[i].cur_row); + */ + total_size += rra_size; + all_data = realloc(all_data, total_size); + in.rrd_value = (void *) all_data; + + if (all_data == NULL) { + rrd_set_error("out of memory"); + goto done; + } + + total_size_out += rra_size_out; + out.rrd_value = realloc(out.rrd_value, total_size_out); + + if (out.rrd_value == NULL) { + rrd_set_error("out of memory"); + goto done; + } + + + + /* reorder data by cleaverly reading it into the right place */ + + /* + Data in the RRD file: + + <-------------RRA--------------> + BBBBBBBBBBBBBBBBBBBBBAAAAAAAAAAA + ^ ^ ^ + 0 cur_row row_cnt + + Data in memory should become + + <-------------RRA--------------> + AAAAAAAAAAABBBBBBBBBBBBBBBBBBBBB + ^ ^ ^ + cur_row=0 n row_cnt + + using: n = row_cnt - cur_row + + we first read cur_row values to position n and then n values to + position 0. + + */ + + /* instead of the actual cur_row, we have to add 1, because + cur_row does not point to the next "free", but the + currently in use position */ + int last = (in.rra_ptr[i].cur_row + 1) % in.rra_def[i].row_cnt; + int n = in.rra_def[i].row_cnt - last; + + + size_t to_read = last * sizeof(rrd_value_t) * in.stat_head->ds_cnt; + size_t read_bytes; + + if (to_read > 0) { + read_bytes = + rrd_read(rrd_file, + all_data + rra_start + + n * sizeof(rrd_value_t) * in.stat_head->ds_cnt, + to_read); + + if (read_bytes != to_read) { + rrd_set_error("short read 1"); + goto done; + } + } + to_read = n * sizeof(rrd_value_t) * in.stat_head->ds_cnt ; + if (to_read > 0) { + read_bytes = + rrd_read(rrd_file, + all_data + rra_start, + to_read); + + if (read_bytes != to_read) { + rrd_set_error("short read 2"); + goto done; + } + } + + int ii, jj; + for (ii = 0 ; ii < in.rra_def[i].row_cnt ; ii++) { + for (j = jj = 0 ; j < in.stat_head->ds_cnt ; j++) { + if (ops[j] == 'c') { + out.rrd_value[total_cnt_out +ii*out.stat_head->ds_cnt +jj] = + in.rrd_value[total_cnt + ii * in.stat_head->ds_cnt + j]; + + /* + memcpy((void*) (out.rrd_value + total_cnt_out + ii * out.stat_head->ds_cnt + jj), + (void*) (in.rrd_value + total_cnt + ii * in.stat_head->ds_cnt + j), sizeof(rrd_value_t)); + */ + /* + fprintf(stderr, "%d->%d\n", + total_cnt + ii * in.stat_head->ds_cnt + j, + total_cnt_out + ii * out.stat_head->ds_cnt + jj); + */ + jj++; + } + } + } + + rra_start += rra_size; + rra_start_out += rra_size_out; + + total_cnt += rra_values; + total_cnt_out += rra_values_out; + } +/* + for (int z = 0 ; z < 160 ; z++) { + fprintf(stderr, "%02x ", (unsigned) ((char*)all_data)[z] & 0x0ff); + if (z % 16 == 15) { + fprintf(stderr, "\n"); + } + } + + + fprintf(stderr, "\n"); + fprintf(stderr, "cnt %ld\n", (long) total_size); +*/ + + + write_file(outfilename, &out); + + rc = 0; +done: + if (rrd_file != NULL) { + rrd_close(rrd_file); + } + rrd_free(&in); + rrd_free(&out); + + if (ops != NULL) free(ops); + + return rc; +} + +int rrd_modify ( + int argc, + char **argv) +{ + int rc; + int i; + /** + * 0 = no header + * 1 = dtd header + * 2 = xsd header + */ + int opt_header = 1; + char *opt_daemon = NULL; + + /* init rrd clean */ + + optind = 0; + opterr = 0; /* initialize getopt */ + + while (42) {/* ha ha */ + int opt; + int option_index = 0; + static struct option long_options[] = { + {"daemon", required_argument, 0, 'd'}, + {0, 0, 0, 0} + }; + + opt = getopt_long(argc, argv, "d:", long_options, &option_index); + + if (opt == EOF) + break; + + switch (opt) { + case 'd': + if (opt_daemon != NULL) + free (opt_daemon); + opt_daemon = strdup (optarg); + if (opt_daemon == NULL) + { + rrd_set_error ("strdup failed."); + return (-1); + } + break; + + default: + rrd_set_error("usage rrdtool %s" + "in.rrd out.rrd", argv[0]); + return (-1); + break; + } + } /* while (42) */ + + if ((argc - optind) < 2) { + rrd_set_error("usage rrdtool %s" + "in.rrd out.rrd", argv[0]); + return (-1); + } + + rc = rrdc_flush_if_daemon(opt_daemon, argv[optind]); + if (opt_daemon) free(opt_daemon); + if (rc) return (rc); + + // parse add/remove options + char **remove = NULL, **add = NULL; + int rcnt = 0, acnt = 0; + + for (i = optind + 2 ; i < argc ; i++) { + if (strncmp("DEL:", argv[i], 4) == 0 && strlen(argv[i]) > 4) { + remove = realloc(remove, (rcnt + 2) * sizeof(char*)); + if (remove == NULL) { + rrd_set_error("out of memory"); + return -1; + } + + remove[rcnt] = strdup(argv[i] + 4); + if (remove[rcnt] == NULL) { + rrd_set_error("out of memory"); + return -1; + } + + rcnt++; + remove[rcnt] = NULL; + } + if (strncmp("DS:", argv[i], 3) == 0 && strlen(argv[i]) > 3) { + add = realloc(add, (acnt + 2) * sizeof(char*)); + if (add == NULL) { + rrd_set_error("out of memory"); + return -1; + } + + add[acnt] = strdup(argv[i]); + if (add[acnt] == NULL) { + rrd_set_error("out of memory"); + return -1; + } + + acnt++; + add[acnt] = NULL; + } + } + + if ((argc - optind) >= 2) { + rc = rrd_modify_r(argv[optind], argv[optind + 1], + remove, add); + } + + if (remove) { + for (char **c = remove ; *c ; c++) { + free(*c); + } + free(remove); + } + if (add) { + for (char **c = add ; *c ; c++) { + free(*c); + } + free(add); + } + return rc; +}