1 /*****************************************************************************
2  * RRDtool 1.7.2 Copyright by Tobi Oetiker
3  *****************************************************************************
4  * rrd_modify  Structurally modify an RRD file
5  *      (c) 2014 by Peter Stamfest and Tobi Oetiker
6  *****************************************************************************
7  * Initially based on rrd_dump.c
8  *****************************************************************************/
9 #include "rrd_tool.h"
10 #include "rrd_rpncalc.h"
11 #include "rrd_client.h"
12 #include "rrd_restore.h"   /* write_file */
13 #include "rrd_create.h"    /* parseDS */
14 #include "rrd_update.h"    /* update_cdp */
15 #include "rrd_modify.h"
16 #include "unused.h"
17 
18 #include "fnv.h"
19 
20 #include <locale.h>
21 #include "rrd_config.h"
22 #ifdef _WIN32
23 #include <stdlib.h>
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #endif
27 
28 
29 
30 // prototypes
31 static int add_rras(const rrd_t *in, rrd_t *out, const int *ds_map,
32 		    const rra_mod_op_t *rra_mod_ops, int rra_mod_ops_cnt, unsigned long hash);
33 
34 /* a convenience realloc/memcpy combo  */
copy_over_realloc(void * dest,int dest_index,const void * src,int src_index,ssize_t size)35 static void * copy_over_realloc(void *dest, int dest_index,
36 				const void *src, int src_index,
37 				ssize_t size) {
38     void *r = realloc(dest, size * (dest_index + 1));
39     if (r == NULL) {
40         rrd_set_error("copy_over_realloc: realloc failed.");
41 	return r;
42     }
43 
44     memcpy(((char*)r) + size * dest_index, ((char*)src) + size * src_index, size);
45     return r;
46 }
47 
48 
49 /*
50    Try to populate rows (presumably for added rows) in new_rra from
51    available data in rrd. This only works for some CF types and
52    generally is wildly inaccurate - eg. it does not take the xff factor
53    into account. Do not think of it as producing correct data but
54    rather as a way to produce nice pictures for subsequent rrdgraph
55    invocations...
56 
57    NOTE: rrd and new_rra may point to entirely different RRAs.
58 */
59 
60 
sort_candidates(const void * va,const void * vb)61 static int sort_candidates(const void *va, const void *vb) {
62     const candidate_t *a = (candidate_t *) va;
63     const candidate_t *b = (candidate_t *) vb;
64 
65     if (a == b) return 0;
66 
67     if (a->rrd == b->rrd && a->rra_index == b->rra_index) return 0;
68 
69     rra_def_t *a_def = a->rrd->rra_def + a->rra_index;
70     rra_def_t *b_def = b->rrd->rra_def + b->rra_index;
71 
72     if (a_def->pdp_cnt == b_def->pdp_cnt) {
73 	return b_def->row_cnt - a_def->row_cnt;  // prefer the RRA with more rows
74     }
75 
76     // ultimately, prefer the RRA with fewer PDPs per CDP
77     return a_def->pdp_cnt - b_def->pdp_cnt;
78 }
79 
select_for_modify(const rra_def_t * tofill,const rra_def_t * maybe)80 static int select_for_modify(const rra_def_t *tofill, const rra_def_t *maybe) {
81     enum cf_en cf = rrd_cf_conv(tofill->cf_nam);
82     enum cf_en other_cf = rrd_cf_conv(maybe->cf_nam);
83     return (other_cf == cf ||
84 	    (other_cf == CF_AVERAGE /*&& other_rra->pdp_cnt == 1*/));
85 }
86 
find_candidate_rras(const rrd_t * rrd,const rra_def_t * rra,int * cnt,candidate_extra_t extra,int (* selectfunc)(const rra_def_t * tofill,const rra_def_t * maybe))87 candidate_t *find_candidate_rras(const rrd_t *rrd, const rra_def_t *rra, int *cnt,
88                                  candidate_extra_t extra,
89                                  int (*selectfunc)(const rra_def_t *tofill, const rra_def_t *maybe))
90 {
91     int total_rows = 0;
92     candidate_t *candidates = NULL;
93     *cnt = 0;
94 
95     int i;
96 
97     /* find other RRAs with the same CF or an RRA with CF_AVERAGE and
98        a stepping of 1 as possible candidates for filling */
99     for (i = 0 ; i < (int) rrd->stat_head->rra_cnt ; i++) {
100 	rra_def_t *other_rra = rrd->rra_def + i;
101 
102 	// can't use our own data
103 	if (other_rra == rra) {
104 	    continue;
105 	}
106 
107 	if (selectfunc(rra, other_rra)) {
108 #ifdef _WINDOWS
109             candidate_t c;
110             c.rrd = rrd;
111             c.rra_index = i;
112             c.values = rrd->rrd_value + rrd->stat_head->ds_cnt * total_rows;
113             c.rra = rrd->rra_def + i;
114             c.ptr = rrd->rra_ptr + i;
115             c.cdp = rrd->cdp_prep + rrd->stat_head->ds_cnt * i;
116             memcpy(&c.extra, &extra, sizeof(extra));
117 #else
118             const candidate_t c = {
119 		.rrd = (rrd_t*) rrd,    /* cast effectively removes const-ness, but we won't
120                                          * mess around with it. promised */
121 		.rra_index = i,
122 		.values = rrd->rrd_value + rrd->stat_head->ds_cnt * total_rows,
123 		.rra = rrd->rra_def + i,
124 		.rra_cf = rrd_cf_conv(rrd->rra_def[i].cf_nam),
125 		.ptr = rrd->rra_ptr + i,
126 		.cdp = rrd->cdp_prep + rrd->stat_head->ds_cnt * i,
127 		.extra = extra
128 	    };
129 #endif
130             candidates = (candidate_t *) copy_over_realloc(candidates, *cnt,
131 					   &c, 0, sizeof(c));
132 	    if (candidates == NULL) {
133 		rrd_set_error("out of memory");
134 		*cnt = 0;
135 		return NULL;
136 	    }
137 	    (*cnt)++;
138 #ifdef MODIFY_DEBUG
139 	    fprintf(stderr, "candidate: index=%d pdp=%d\n", i, in_rrd->rra_def[i].pdp_cnt);
140 #endif
141 	}
142 	total_rows += other_rra->row_cnt;
143     }
144 
145     if (*cnt == 0) {
146 	return NULL;
147     }
148 
149     // now sort candidates by granularity
150     qsort(candidates, *cnt, sizeof(candidate_t), sort_candidates);
151 
152     return candidates;
153 }
154 
155 
156 /* copy over existing DS definitions (and related data
157    structures), check on the way (and skip) if they should be
158    deleted
159    */
copy_or_delete_DSs(const rrd_t * in,rrd_t * out,char * ops)160 static int copy_or_delete_DSs(const rrd_t *in, rrd_t *out, char *ops) {
161     int rc = -1;
162 
163     for (unsigned int in_ds = 0 ; in_ds < in->stat_head->ds_cnt ; in_ds++) {
164 	switch (ops[in_ds]) {
165 	case 'c': {
166 	    out->ds_def = (ds_def_t *) copy_over_realloc(out->ds_def, out->stat_head->ds_cnt,
167 					   in->ds_def, in_ds,
168 					   sizeof(ds_def_t));
169 	    if (out->ds_def == NULL) goto done;
170 
171 		out->pdp_prep = (pdp_prep_t *) copy_over_realloc(out->pdp_prep, out->stat_head->ds_cnt,
172 					     in->pdp_prep, in_ds,
173 					     sizeof(pdp_prep_t));
174 	    if (out->pdp_prep == NULL) goto done;
175 
176 	    out->stat_head->ds_cnt++;
177 	    break;
178 	}
179 	case 'd':
180 	    break;
181 	case 'a':
182 	default:
183 	    rrd_set_error("internal error: invalid ops");
184 	    goto done;
185 	}
186     }
187     // only if we did all iterations without any problems will we arrive here
188     rc = 0;
189 done:
190     return rc;
191 }
192 
193 /*
194   Handle all RRAs definitions (and associated CDP data structures), taking care
195   of RRA removals, and RRA row additions and removals. NOTE: data copying is
196   NOT done by this function, but it DOES calculate overall total_row
197   information needed for sizing the data area.
198 
199   returns the total number out RRA rows for both the in and out RRDs in the
200   variables pointed to by total_in_rra_rows and total_out_rra_rows respectively
201   */
handle_rra_defs(const rrd_t * in,rrd_t * out,rra_mod_op_t * rra_mod_ops,unsigned int rra_mod_ops_cnt,const char * ds_ops,unsigned int ds_ops_cnt,int * total_in_rra_rows,int * total_out_rra_rows)202 static int handle_rra_defs(const rrd_t *in, rrd_t *out,
203 			   rra_mod_op_t *rra_mod_ops, unsigned int rra_mod_ops_cnt,
204 			   const char *ds_ops, unsigned int ds_ops_cnt,
205 			   int *total_in_rra_rows, int *total_out_rra_rows)
206 {
207     int rc = -1;
208     unsigned int j, r;
209 	rra_ptr_t rra_0_ptr; rra_0_ptr.cur_row = 0;
210     cdp_prep_t empty_cdp_prep;
211     memset(&empty_cdp_prep, 0, sizeof(empty_cdp_prep));
212 
213     for (j = 0 ; j < in->stat_head->rra_cnt ; j++) {
214 	if (total_in_rra_rows)
215 	    *total_in_rra_rows +=  in->rra_def[j].row_cnt;
216 
217 	rra_mod_op_t *rra_op = NULL;
218 	for (r = 0 ; r < rra_mod_ops_cnt ; r++) {
219 	    if (rra_mod_ops[r].index == (int) j) {
220 		rra_op = rra_mod_ops + r;
221 		break;
222 	    }
223 	}
224 
225 	int final_row_count = in->rra_def[j].row_cnt;
226 	if (rra_op) {
227 	    switch (rra_op->op) {
228 	    case '=':
229 		final_row_count = rra_op->row_count;
230 		break;
231 	    case '-':
232 		final_row_count -= rra_op->row_count;
233 		break;
234 	    case '+':
235 		final_row_count += rra_op->row_count;
236 		break;
237 	    }
238 	    if (final_row_count < 0) final_row_count = 0;
239 	    /* record the final row_count. I don't like this, because
240 	       it changes the data passed to us via an argument: */
241 
242 	    rra_op->final_row_count = final_row_count;
243 	}
244 
245 	// do we have to keep the RRA at all??
246 	if (final_row_count == 0) {
247 	    // delete the RRA! - just skip processing this RRA....
248 	    continue;
249 	}
250 
251 	out->cdp_prep = (cdp_prep_t *) realloc(out->cdp_prep,
252 			       sizeof(cdp_prep_t) * out->stat_head->ds_cnt
253 			       * (out->stat_head->rra_cnt + 1));
254 
255 	if (out->cdp_prep == NULL) {
256 	    rrd_set_error("Cannot allocate memory");
257 	    goto done;
258 	}
259 
260 	/* for every RRA copy only those CDPs in the prep area where we keep
261 	   the DS! */
262 
263 	int start_index_in  = in->stat_head->ds_cnt * j;
264 	int start_index_out = out->stat_head->ds_cnt * out->stat_head->rra_cnt;
265 
266 	out->rra_def = (rra_def_t *) copy_over_realloc(out->rra_def, out->stat_head->rra_cnt,
267 					 in->rra_def, j,
268 					 sizeof(rra_def_t));
269 	if (out->rra_def == NULL) goto done;
270 
271 	// adapt row count:
272 	out->rra_def[out->stat_head->rra_cnt].row_cnt = final_row_count;
273 
274 	out->rra_ptr = (rra_ptr_t *) copy_over_realloc(out->rra_ptr, out->stat_head->rra_cnt,
275 					&rra_0_ptr, 0,
276 					sizeof(rra_ptr_t));
277 	if (out->rra_ptr == NULL) goto done;
278 
279 	out->rra_ptr[out->stat_head->rra_cnt].cur_row = final_row_count - 1;
280 
281 	unsigned int i, ii;
282 	for (i = ii = 0 ; i < ds_ops_cnt ; i++) {
283 	    switch (ds_ops[i]) {
284 	    case 'c': {
285 		memcpy(out->cdp_prep + start_index_out + ii,
286 		       in->cdp_prep + start_index_in + i,
287 		       sizeof(cdp_prep_t));
288 		ii++;
289 		break;
290 	    }
291 	    case 'a': {
292 		cdp_prep_t *cdp_prep = out->cdp_prep + start_index_out + ii;
293 		memcpy(cdp_prep,
294 		       &empty_cdp_prep, sizeof(cdp_prep_t));
295 
296 		init_cdp(out,
297 			 out->rra_def + out->stat_head->rra_cnt,
298                          out->pdp_prep + ii,
299 			 cdp_prep);
300 		ii++;
301 		break;
302 	    }
303 	    case 'd':
304 		break;
305 	    default:
306 		rrd_set_error("internal error: invalid ops");
307 		goto done;
308 	    }
309 	}
310 
311 	if (total_out_rra_rows)
312 	    *total_out_rra_rows += out->rra_def[out->stat_head->rra_cnt].row_cnt;
313 
314 	out->stat_head->rra_cnt++;
315     }
316     rc = 0;
317 done:
318     return rc;
319 }
320 
321 
322 /* add datasources as specified in the addDS array of DS specs as documented
323     in rrdcreate(1)
324 
325     Returns the number of DSs added or -1 on error
326 */
327 
add_dss(const rrd_t UNUSED (* in),rrd_t * out,const char ** addDS)328 static int add_dss(const rrd_t UNUSED(*in), rrd_t *out,
329 		   const char **addDS)
330 {
331     if (addDS == NULL) return 0;
332 
333     int added_count = 0;
334     int rc = -1;
335     int j;
336     const char *c;
337     const char *require_version = NULL;
338 
339     for (j = 0, c = addDS[j] ; c ; j++, c = addDS[j]) {
340 	ds_def_t added;
341 
342 	// parse DS
343 	parseDS(c + 3,
344 		&added, // out.ds_def + out.stat_head->ds_cnt,
345 		out, lookup_DS, NULL, &require_version);
346 
347 	// check if there is a name clash with an existing DS
348 	if (lookup_DS(out, added.ds_nam) >= 0) {
349 	    rrd_set_error("Duplicate DS name: %s", added.ds_nam);
350 	    goto done;
351 	}
352 
353 	// copy parse result to output RRD
354 	out->ds_def = (ds_def_t *) copy_over_realloc(out->ds_def, out->stat_head->ds_cnt,
355 					&added, 0,
356 					sizeof(ds_def_t));
357 	if (out->ds_def ==  NULL) {
358 	    goto done;
359 	}
360 
361 	// also add a pdp_prep_t
362 	pdp_prep_t added_pdp_prep;
363 	memset(&added_pdp_prep, 0, sizeof(added_pdp_prep));
364 	strcpy(added_pdp_prep.last_ds, "U");
365 
366 	added_pdp_prep.scratch[PDP_val].u_val = 0.0;
367 	added_pdp_prep.scratch[PDP_unkn_sec_cnt].u_cnt =
368 	    out->live_head->last_up % out->stat_head->pdp_step;
369 
370 	out->pdp_prep = (pdp_prep_t *) copy_over_realloc(out->pdp_prep,
371 					  out->stat_head->ds_cnt,
372 					  &added_pdp_prep, 0,
373 					  sizeof(pdp_prep_t));
374 	if (out->pdp_prep == NULL) {
375 	    goto done;
376 	}
377 	out->stat_head->ds_cnt++;
378 
379 	added_count++;
380     }
381     rc = added_count;
382 done:
383     return rc;
384 }
385 
386 
387 /*
388   Populate (presumably just added) rows of an RRA from available
389   data. Currently only basic CF types are supported.
390 
391   in_rrd .. the RRD to use for the search of other RRAs to populate the new RRA
392   out_rrd .. the RRD new_rra is part of
393   ds_map .. maps the DS indices from the ones used in new_rra to the ones used in
394             rrd. If NULL, an identity mapping is used. This is needed to support
395             DS addition/removal from the rrd to new_rra.
396   new_rra .. the RRA to populate. It is assumed, that this RRA will
397              become part of rrd. This means that all meta settings (step size,
398 	     last update time, etc.) not part of the RRA definition can be taken
399 	     from rrd.
400   cur_row .. The cur_row value for new_rra. This is not kept with the def, so it
401              has to be passed separately
402   values .. Pointer to the 0-index row of the RRA
403   populate_start .. the first row to populate in new_rra
404   populate_cnt .. the number of rows to populate in new_rra, starting at
405                   populate_start
406  */
populate_row(const rrd_t * in_rrd,const rrd_t * out_rrd,const int * ds_map,rra_def_t * new_rra,int cur_row,rrd_value_t * values,int populate_start,int populate_cnt)407 static int populate_row(const rrd_t *in_rrd,
408 			const rrd_t *out_rrd,
409 			const int *ds_map,
410 			rra_def_t *new_rra,
411 			int cur_row,
412 			rrd_value_t *values,
413 			int populate_start,
414 			int populate_cnt)
415 {
416     int rc = -1;
417 
418     if (in_rrd->stat_head->rra_cnt < 1) return 0;
419 
420     enum cf_en cf = rrd_cf_conv(new_rra->cf_nam);
421     switch (cf) {
422     case CF_AVERAGE:
423     case CF_MINIMUM:
424     case CF_MAXIMUM:
425     case CF_LAST:
426 	break;
427     default: // unsupported CF for extension
428 	return 0;
429     }
430 
431     int ds_cnt = in_rrd->stat_head->ds_cnt;
432 
433     candidate_t *candidates = NULL;
434     int candidates_cnt = 0;
435 
436     int i, ri;
437     candidate_extra_t junk;
438     junk.l = 0; /* Initialize junk.l to avoid warning C4700 in MSVC */
439 
440     candidates = find_candidate_rras(in_rrd, new_rra, &candidates_cnt, junk, select_for_modify);
441     if (candidates == NULL) {
442 	goto done;
443     }
444 
445     /* some of the code below is based on
446        https://github.com/ssinyagin/perl-rrd-tweak/blob/master/lib/RRD/Tweak.pm#L1455
447     */
448 
449     for (ri = 0 ; ri < populate_cnt ; ri++) {
450 	int row = populate_start + ri;
451 
452 	time_t new_timeslot = new_rra->pdp_cnt * out_rrd->stat_head->pdp_step;
453 
454 	time_t row_end_time = end_time_for_row(out_rrd, new_rra, cur_row, row);
455 	time_t row_start_time   = row_end_time - new_timeslot + 1;
456 
457 	/* now walk all candidates */
458 
459 	for (i = 0 ; i < candidates_cnt ; i++) {
460 	    candidate_t *c = candidates + i;
461 	    rra_def_t *r = c->rrd->rra_def + c->rra_index;
462 	    int cand_cur_row = c->rrd->rra_ptr[c->rra_index].cur_row;
463 
464 	    /* find a matching range of rows */
465 	    int cand_row_start = row_for_time(in_rrd, r, cand_cur_row, row_start_time);
466 	    int cand_row_end   = row_for_time(in_rrd, r, cand_cur_row, row_end_time);
467 
468 	    if (cand_row_start == -1 && cand_row_end != -1) {
469 		// start time is beyond last_up */
470 		cand_row_start = cand_cur_row;
471 	    } else if (cand_row_start != -1 && cand_row_end == -1) {
472 		// maybe the candidate has fewer rows than the pdp_step ....
473 		cand_row_end = (cand_cur_row - 1) % r->row_cnt;
474 	    } else if (cand_row_start == -1 && cand_row_end == -1) {
475 		// neither start nor end in range. Can't use this candidate RRA...
476 		continue;
477 	    }
478 
479 	    /* note: cand_row_end is usually after cand_row_start,
480 	       unless we have a wrap over.... so we turn the
481 	       iteration over the rows into one based on the number
482 	       of rows starting at cand_row_end. All this dance should
483 	       be in preparation for unusual cases where we have
484 	       candidates and new RRAs that have pdp counts that are
485 	       not directly divisible by each other (like populating a
486 	       2-pdp RRA from a 3-pdp RRA) */
487 
488 	    int cand_rows = (cand_row_end - cand_row_start + 1);
489 	    if (cand_rows < 0) cand_rows += r->row_cnt;
490 
491 #ifdef MODIFY_DEBUG
492 	    fprintf(stderr, "cand: start=%d end=%d rows=%d\n",
493 		    cand_row_start, cand_row_end, cand_rows);
494 #endif
495 
496 	    int cand_timeslot = r->pdp_cnt * c->rrd->stat_head->pdp_step;
497 
498 	    int out_ds_cnt = out_rrd->stat_head->ds_cnt;
499 	    for (int k = 0 ; k < out_ds_cnt ; k++) {
500 		/* check if we already have a value (maybe from a
501 		   prior (=better!) candidate....)  if we have: skip this DS */
502 		if (! isnan(values[row * out_ds_cnt + k])) {
503 		    continue;
504 		}
505 
506 		int cand_row, ci ;
507 		rrd_value_t tmp = DNAN, final = DNAN;
508 		int covered = 0;
509 
510 		int in_k = ds_map ? ds_map[k] : k;
511 
512 		// if the DS was just added we have no pre-existing data anyway, so skip
513 		if (in_k < 0) continue;
514 
515 		/* Go: Use the range of candidate rows to populate this DS in this row */
516 		for (cand_row = cand_row_start, ci = 0 ;
517 		     ci < cand_rows ;
518 		     ci++, cand_row = (cand_row + 1) % r->row_cnt)
519 		    {
520 		    rrd_value_t v = c->values[cand_row * ds_cnt + in_k];
521 
522 		    /* skipping NAN values. Note that if all candidate
523 		       rows are filled with NAN values, a later
524 		       candidate RRA might be used instead. This works
525 		       in combination with the isnan check above */
526 		    if (isnan(v)) continue;
527 
528 		    switch (cf) {
529 		    case CF_AVERAGE:
530 			tmp = isnan(tmp) ? v * cand_timeslot : (tmp + v * cand_timeslot);
531 			covered += cand_timeslot;
532 			final = tmp / covered;
533 			break;
534 		    case CF_MINIMUM:
535 			final = tmp = isnan(tmp) ? v : (tmp < v ? tmp : v);
536 			break;
537 		    case CF_MAXIMUM:
538 			final = tmp = isnan(tmp) ? v : (tmp > v ? tmp : v);
539 			break;
540 		    case CF_LAST:
541 			final = tmp = v;
542 			break;
543 		    default: // unsupported CF for extension
544 			return 0;
545 		    }
546 		}
547 
548 		values[row * out_ds_cnt + k] = final;
549 	    }
550 	}
551     }
552 
553     rc = 0;
554 
555  done:
556     if (candidates) {
557 	free(candidates);
558     }
559 
560     return rc;
561 }
562 
mod_rras(const rrd_t * in,rrd_t * out,const int * ds_map,const rra_mod_op_t * rra_mod_ops,unsigned int rra_mod_ops_cnt,const char * ds_ops,unsigned int ds_ops_cnt)563 static int mod_rras(const rrd_t *in, rrd_t *out, const int *ds_map,
564 		    const rra_mod_op_t *rra_mod_ops, unsigned int rra_mod_ops_cnt,
565 		    const char *ds_ops, unsigned int ds_ops_cnt)
566 {
567     int rc = -1;
568     unsigned int rra_index, j;
569     int total_cnt = 0, total_cnt_out = 0;
570     int out_rra = 0;	    // index of currently copied RRA
571 
572     for (rra_index = 0; rra_index < in->stat_head->rra_cnt; rra_index++) {
573 	const rra_mod_op_t *rra_op = NULL;
574 	for (unsigned int op_index = 0 ; op_index < rra_mod_ops_cnt ; op_index++) {
575 	    if (rra_mod_ops[op_index].index == (int)rra_index) {
576 		rra_op = rra_mod_ops + op_index;
577 		break;
578 	    }
579 	}
580 
581 	if (rra_op && rra_op->final_row_count == 0) {
582 	    // RRA deleted - skip !
583 	    continue;
584 	}
585 
586 	/* number and sizes of all the data in an RRA */
587 	int rra_values     = in->stat_head->ds_cnt  * in->rra_def[rra_index].row_cnt;
588 	int rra_values_out = out->stat_head->ds_cnt * out->rra_def[out_rra].row_cnt;
589 
590 	/* we now have all the data for the current RRA available, now
591 	   start to transfer it to the output RRD: For every row copy
592 	   the data corresponding to copied DSes, add NaN values for newly
593 	   added DSes. */
594 
595 	unsigned int ii = 0, jj, oi = 0;
596 
597 	/* we have to decide beforehand about row addition and
598 	   deletion, because this takes place in the front of the
599 	   rrd_value array....
600 	 */
601 
602 	if (rra_op) {
603 	    char op = rra_op->op;
604 	    unsigned int row_count = rra_op->row_count;
605 
606 	    // rewrite '=' ops into '-' or '+' for better code-reuse...
607 	    if (op == '=') {
608 		if (row_count < in->rra_def[rra_index].row_cnt) {
609 		    row_count = in->rra_def[rra_index].row_cnt - row_count;
610 		    op = '-';
611 		} else if (row_count > in->rra_def[rra_index].row_cnt) {
612 		    row_count = row_count - in->rra_def[rra_index].row_cnt;
613 		    op = '+';
614 		} else {
615 		    // equal - special case: nothing to do...
616 		}
617 	    }
618 
619 	    switch (op) {
620 	    case '=':
621 		// no op
622 		break;
623 	    case '-':
624 		// remove rows: just skip the first couple of rows!
625 		ii = row_count;
626 		break;
627 	    case '+':
628 		// add rows: insert the requested number of rows!
629 		// currently, just add the all as NaN values...
630 
631 		for ( ; oi < row_count ; oi++) {
632 		    for (j = 0 ; j < out->stat_head->ds_cnt ; j++) {
633 			out->rrd_value[total_cnt_out +
634 				       oi * out->stat_head->ds_cnt +
635 				       j] = DNAN;
636 		    }
637 		}
638 
639 		// now try to populate the newly added rows
640 		populate_row(in, out, ds_map,
641 			     out->rra_def + out_rra,
642 			     out->rra_ptr[rra_index].cur_row,
643 			     out->rrd_value + total_cnt_out,
644 			     0, row_count);
645 
646 		break;
647 	    default:
648 		rrd_set_error("RRA modification operation '%c' "
649 			      "not (yet) supported", rra_op->op);
650 		goto done;
651 	    }
652 	}
653 
654 	/* now do the actual copying of data */
655 
656 	for ( ; ii < in->rra_def[rra_index].row_cnt
657 		  && oi < out->rra_def[out_rra].row_cnt ; ii++, oi++) {
658 	    int real_ii = (ii + in->rra_ptr[rra_index].cur_row + 1) % in->rra_def[rra_index].row_cnt;
659 	    for (j = jj = 0 ; j < ds_ops_cnt ; j++) {
660 		switch (ds_ops[j]) {
661 		case 'c': {
662 		    out->rrd_value[total_cnt_out + oi * out->stat_head->ds_cnt + jj] =
663 			in->rrd_value[total_cnt + real_ii * in->stat_head->ds_cnt + j];
664 
665 		    /* it might be better to use memcpy, actually (to
666 		       treat them opaquely)... so keep the code for
667 		       the time being */
668 		    /*
669 		    memcpy((void*) (out.rrd_value + total_cnt_out + oi * out.stat_head->ds_cnt + jj),
670 			   (void*) (in.rrd_value + total_cnt + real_ii * in.stat_head->ds_cnt + j), sizeof(rrd_value_t));
671 		    */
672 		    jj++;
673 		    break;
674 		}
675 		case 'a': {
676 		    out->rrd_value[total_cnt_out + oi * out->stat_head->ds_cnt + jj] = DNAN;
677 		    jj++;
678 		    break;
679 		}
680 		case 'd':
681 		    break;
682 		default:
683 		    rrd_set_error("internal error: invalid ops");
684 		    goto done;
685 		}
686 	    }
687 	}
688 
689 	total_cnt     += rra_values;
690 	total_cnt_out += rra_values_out;
691 
692 	out_rra++;
693     }
694     rc = 0;
695 done:
696     return rc;
697 }
698 
stretch_rras(rrd_t * out,int stretch)699 static int stretch_rras(rrd_t *out, int stretch) {
700     int rc = -1;
701     if (stretch < 2) {
702 	rrd_set_error("invalid stretch count. Must be > 1");
703 	goto done;
704     }
705 
706     unsigned int ds_cnt = out->stat_head->ds_cnt;
707     unsigned int rra_index, ds_index;
708     for (rra_index = 0 ; rra_index < out->stat_head->rra_cnt ; rra_index++) {
709 	rra_def_t *rra = out->rra_def + rra_index;
710 	enum cf_en cf = rrd_cf_conv(rra->cf_nam);
711 
712 	cdp_prep_t *cdp_prep_row = out->cdp_prep + rra_index * ds_cnt;
713 	for (ds_index = 0 ; ds_index < ds_cnt ; ds_index++) {
714 	    switch (cf) {
715 	    case CF_AVERAGE:
716 	    case CF_MINIMUM:
717 	    case CF_MAXIMUM:
718 	    case CF_LAST:
719 		(cdp_prep_row + ds_index)->scratch[CDP_unkn_pdp_cnt].u_val *= stretch;
720 		break;
721 	    default:
722 		break;
723 	    }
724 	}
725 
726 	rra->pdp_cnt *= stretch;
727     }
728 
729     out->stat_head->pdp_step /= stretch;
730 
731     rc =0;
732 done:
733     return rc;
734 }
735 
rrd_memory_free(rrd_t * rrd)736 static void rrd_memory_free(rrd_t *rrd)
737 {
738     if (rrd == NULL) return;
739     if (rrd->live_head) free(rrd->live_head);
740     if (rrd->stat_head) free(rrd->stat_head);
741     if (rrd->ds_def) free(rrd->ds_def);
742     if (rrd->rra_def) free(rrd->rra_def);
743     if (rrd->rra_ptr) free(rrd->rra_ptr);
744     if (rrd->pdp_prep) free(rrd->pdp_prep);
745     if (rrd->cdp_prep) free(rrd->cdp_prep);
746     if (rrd->rrd_value) free(rrd->rrd_value);
747 }
748 
rrd_modify_structure(const rrd_t * in,const char ** removeDS,const char ** addDS,rra_mod_op_t * rra_mod_ops,int rra_mod_ops_cnt,unsigned long hash)749 static rrd_t *rrd_modify_structure(const rrd_t *in,
750 				   const char **removeDS,
751 				   const char **addDS,
752 				   rra_mod_op_t *rra_mod_ops, int rra_mod_ops_cnt,
753 				   unsigned long hash)
754 {
755     rrd_t *out;
756     int rc = -1;
757     unsigned int i, j;
758     char       *ds_ops = NULL;
759     unsigned int ds_ops_cnt = 0;
760     int *ds_map = NULL;
761 
762 	out = (rrd_t *) malloc(sizeof(rrd_t));
763     if (out == NULL) {
764 	rrd_set_error("Out of memory");
765 	goto done;
766     }
767     rrd_init(out);
768 
769     /* currently we only allow to modify version 3 RRDs. If other
770        files should be modified, a dump/restore cycle should be
771        done.... */
772 
773     if (atoi(in->stat_head->version) < atoi(RRD_VERSION3) || atoi(in->stat_head->version) > atoi(RRD_VERSION5)) {
774 	rrd_set_error("direct modification is only supported for version 3, 4 or 5 of RRD files. Consider to dump/restore before retrying a modification");
775 	goto done;
776     }
777 
778 
779     /* copy over structure to out RRD */
780 
781     out->stat_head = (stat_head_t *) malloc(sizeof(stat_head_t));
782     if (out->stat_head == NULL) {
783 	rrd_set_error("rrd_modify_r: malloc failed.");
784 	goto done;
785     }
786 
787     memset(out->stat_head, 0, (sizeof(stat_head_t)));
788 
789     strncpy(out->stat_head->cookie, "RRD", sizeof(out->stat_head->cookie));
790     strcpy(out->stat_head->version, in->stat_head->version);
791     out->stat_head->float_cookie = FLOAT_COOKIE;
792     out->stat_head->pdp_step = in->stat_head->pdp_step;
793 
794     out->stat_head->ds_cnt = 0;
795     out->stat_head->rra_cnt = 0;
796 
797 	out->live_head = (live_head_t *) copy_over_realloc(out->live_head, 0, in->live_head, 0,
798 				       sizeof(live_head_t));
799 
800     if (out->live_head == NULL) goto done;
801 
802     /* use the ops array as a scratchpad to remember what we are about
803     to do to each DS. There is one entry for every DS in the
804     original RRD and one additional entry for every added DS.
805 
806     Entries marked as
807     - 'c' will be copied to the out RRD,
808     - 'd' will not be copied (= will effectively be deleted)
809     - 'a' will be added.
810     */
811     ds_ops_cnt = in->stat_head->ds_cnt;
812     ds_ops = (char *) malloc(ds_ops_cnt);
813 
814     if (ds_ops == NULL) {
815 	rrd_set_error("parse_tag_rrd: malloc failed.");
816 	goto done;
817     }
818 
819     memset(ds_ops, 'c', in->stat_head->ds_cnt);
820 
821     // record DSs to be deleted in ds_ops
822     if (removeDS != NULL) {
823 	for (unsigned int in_ds = 0 ; in_ds < in->stat_head->ds_cnt ; in_ds++) {
824 	    const char *c;
825 	    for (j = 0, c = removeDS[j] ; c ; j++, c = removeDS[j]) {
826 		if (strcmp(in->ds_def[in_ds].ds_nam, c) == 0) {
827 		    ds_ops[in_ds] = 'd';
828 		    break;
829 		}
830 	    }
831 	}
832     }
833 
834     if (copy_or_delete_DSs(in, out, ds_ops) != 0) {
835 	// error
836 	goto done;
837     }
838 
839     /* now add any DS definitions to be added */
840     int added_cnt = add_dss(in, out, addDS);
841     if (added_cnt < 0) {
842 	// error
843 	goto done;
844     }
845     if (added_cnt > 0) {
846 	// and extend the ds_ops array as well
847 		ds_ops = (char *) realloc(ds_ops, ds_ops_cnt + added_cnt);
848 	for(; added_cnt > 0 ; added_cnt--) {
849 	    ds_ops[ds_ops_cnt++] = 'a';
850 	}
851     }
852 
853     /* prepare explicit data source index to map from output index to
854        input index */
855 
856     ds_map = (int *) malloc(sizeof(int) * out->stat_head->ds_cnt);
857 
858     j = 0;
859     for (i = 0 ; i < ds_ops_cnt ; i++) {
860 	switch (ds_ops[i]) {
861 	case 'c':
862 	    ds_map[j++] = i;
863 	    break;
864 	case 'd':
865 	    break;
866 	case 'a':
867 	    ds_map[j++] = -1;
868 	    break;
869 	}
870     }
871 
872     /* now take care to copy all RRAs, removing and adding columns for
873        every row as needed for the requested DS changes */
874 
875     /* we also reorder all rows, adding/removing rows as needed */
876 
877     /* later on, we'll need to know the total number of rows for both RRDs in
878        order to allocate memory. Luckily, handle_rra_defs will give that to us. */
879     int total_out_rra_rows = 0, total_in_rra_rows = 0;
880 
881     rc = handle_rra_defs(in, out, rra_mod_ops, rra_mod_ops_cnt, ds_ops, ds_ops_cnt, &total_in_rra_rows, &total_out_rra_rows);
882     if (rc != 0) goto done;
883 
884     /* read and process all data ... */
885 
886     /* there seem to be two function in the current rrdtool codebase dealing
887        with writing a new rrd file to disk: write_file and rrd_create_fn. The
888        latter has the major problem, that it tries to free data passed to it
889        (WTF?), but write_file assumes chronologically ordered data in RRAs (that
890        is, in the data space starting at rrd.rrd_value....
891 
892        This is the reason why:
893         - we use write_file and
894         - why we reset cur_row in RRAs and reorder data to be chronological
895     */
896 
897     /* prepare space for output data */
898 	out->rrd_value = (rrd_value_t *) realloc(out->rrd_value,
899 			     total_out_rra_rows * out->stat_head->ds_cnt
900 			     * sizeof(rrd_value_t));
901 
902     if (out->rrd_value == NULL) {
903 	rrd_set_error("out of memory");
904 	goto done;
905     }
906 
907     rc = mod_rras(in, out, ds_map, rra_mod_ops, rra_mod_ops_cnt, ds_ops, ds_ops_cnt);
908     if (rc != 0) goto done;
909 
910     rc = add_rras(in, out, ds_map, rra_mod_ops, rra_mod_ops_cnt, hash);
911     if (rc != 0) goto done;
912 
913 
914 done:
915     if (ds_ops != NULL) free(ds_ops);
916     if (ds_map != NULL) free(ds_map);
917 
918     if (rc != 0 && out != NULL) {
919 	rrd_memory_free(out);
920 	free(out);
921 	out = NULL;
922     }
923     return out;
924 }
925 
926 /* copies the RRD in to a new RRD and return it
927 
928    In that process, data sources may be removed or added.
929 
930    removeDS points to an array of strings, each naming a DS to be
931    removed. The list itself is NULL terminated. addDS points to a
932    similar list holding rrdcreate-style data source definitions to be
933    added.
934 */
935 
rrd_modify_r2(const rrd_t * in,const char ** removeDS,const char ** addDS,rra_mod_op_t * rra_mod_ops,int rra_mod_ops_cnt,int newstep,unsigned long hash)936 static rrd_t *rrd_modify_r2(const rrd_t *in,
937 			    const char **removeDS,
938 			    const char **addDS,
939 			    rra_mod_op_t *rra_mod_ops, int rra_mod_ops_cnt,
940 			    int newstep,
941 			    unsigned long hash)
942 {
943     int rc = -1;
944     /* basic check: do we have a new step size: if we do: is it a smaller than
945        the original and is the old one a whole-number multiple of the new one? */
946 
947     int stretch = 0;
948     rrd_t *out = NULL;
949     rrd_t *finalout = NULL;
950 
951     if (newstep > 0) {
952 	if (in->stat_head->pdp_step % newstep == 0
953 		&& in->stat_head->pdp_step / newstep > 1) {
954 	    /* we will "stretch" the RRD: existing rows will correspond to the same
955 	       time period, but the CF will consolidate 'stretch' times as many PDPs.
956 	     */
957 
958 	    stretch = in->stat_head->pdp_step / newstep;
959 	} else {
960 	    rrd_set_error("invalid 'newstep' parameter. The newsize must "
961 			  "divide the old step parameter without a remainder.");
962 	    goto done;
963 	}
964 
965 	// create temporary RRD structure for in-place resizing...
966 
967 	out = rrd_modify_structure(in, NULL, NULL, NULL, 0, hash);
968 	if (out == NULL) {
969 	    goto done;
970 	}
971 
972 	if (stretch > 1) {
973 	    rc = stretch_rras(out, stretch);
974 	    if (rc != 0) goto done;
975 /*	} else if (shrink > 1) {
976 	    rc = shrink_rras(out, shrink);
977 	    if (rc != 0) goto done;*/
978 	}
979 
980 
981 	finalout = rrd_modify_structure(out, removeDS, addDS, rra_mod_ops, rra_mod_ops_cnt, hash);
982 	if (finalout == NULL) {
983 	    goto done;
984 	}
985     } else {
986 	// shortcut: do changes in one step
987 	finalout = rrd_modify_structure(in, removeDS, addDS, rra_mod_ops, rra_mod_ops_cnt, hash);
988 	if (finalout == NULL) {
989 	    goto done;
990 	}
991     }
992     rc = 0;
993 done:
994     if (out) {
995 	rrd_memory_free(out);
996 	free(out);
997 	out = NULL;
998     }
999     if (rc != 0) {
1000 	out = NULL;
1001 	finalout = NULL;
1002     }
1003 
1004     return finalout;
1005 }
1006 
1007 
1008 
1009 
1010 // prepare CDPs + values for new RRA
1011 
prepare_CDPs(const rrd_t * in,rrd_t * out,int curr_rra,int start_values_index_out,const int * ds_map)1012 static void prepare_CDPs(const rrd_t *in, rrd_t *out,
1013 			 int curr_rra,
1014 			 int start_values_index_out,
1015 			 const int *ds_map)
1016 {
1017     cdp_prep_t empty_cdp_prep;
1018     memset(&empty_cdp_prep, 0, sizeof(cdp_prep_t));
1019 
1020     rra_def_t *rra_def = out->rra_def + curr_rra;
1021 
1022     enum cf_en cf = rrd_cf_conv(rra_def->cf_nam);
1023     int candidates_cnt = 0;
1024     candidate_t *candidates = NULL;
1025     candidate_t *chosen_candidate = NULL;
1026     candidate_extra_t junk;
1027     junk.l = 0;
1028 
1029     candidates = find_candidate_rras(in, rra_def, &candidates_cnt, junk, select_for_modify);
1030 
1031     if (candidates != NULL) {
1032 	int ci;
1033 	for (ci = 0 ; ci < candidates_cnt ; ci++) {
1034 	    candidate_t *c = candidates + ci;
1035 	    rra_def_t *cand_rra = c->rrd->rra_def + c->rra_index;
1036 
1037 	    // we only accept AVERAGE RRAs or RRAs with pdp_cnt == 1
1038 	    if (cand_rra->pdp_cnt == 1 || rrd_cf_conv(cand_rra->cf_nam) == CF_AVERAGE) {
1039 		chosen_candidate = c;
1040 		break;
1041 	    }
1042 	}
1043     }
1044 #ifdef MODIFY_DEBUG
1045     fprintf(stderr, "chosen candidate index=%d row_cnt=%ld\n", chosen_candidate->rra_index, chosen_candidate->rra->row_cnt);
1046 #endif
1047     int start_cdp_index_out = out->stat_head->ds_cnt * curr_rra;
1048 
1049     for (int i = 0 ; i < (int) out->stat_head->ds_cnt ; i++) {
1050 	int mapped_i = ds_map[i];
1051 
1052 	cdp_prep_t *cdp_prep = out->cdp_prep + start_cdp_index_out + i;
1053 	memcpy(cdp_prep, &empty_cdp_prep, sizeof(cdp_prep_t));
1054 
1055 	init_cdp(out, rra_def, out->pdp_prep + i, cdp_prep);
1056 
1057 	if (chosen_candidate && mapped_i != -1) {
1058 	    int ds_cnt = chosen_candidate->rrd->stat_head->ds_cnt;
1059 
1060 	    /* we have a chosen candidate. Find out what the */
1061 
1062 	    time_t last_up = in->live_head->last_up;
1063 
1064 	    int timeslot = rra_def->pdp_cnt * in->stat_head->pdp_step;
1065 
1066 	    int delta = last_up % timeslot;
1067 	    time_t end_time = last_up, start_time;
1068 	    if (delta != 0) {
1069 		end_time = last_up - delta + timeslot;
1070 	    }
1071 	    start_time = end_time - timeslot + 1;
1072 
1073 	    int start_row = row_for_time(chosen_candidate->rrd,
1074 					 chosen_candidate->rra,
1075 					 chosen_candidate->ptr->cur_row,
1076 					 start_time);
1077 	    int end_row = row_for_time(chosen_candidate->rrd,
1078 				       chosen_candidate->rra,
1079 				       chosen_candidate->ptr->cur_row,
1080 				       end_time);
1081 
1082 #ifdef MODIFY_DEBUG
1083 	    fprintf(stderr, "need PDP data for %ld to %ld\n", start_time, end_time);
1084 	    fprintf(stderr, "last_up %ld\n", chosen_candidate->rrd->live_head->last_up);
1085 	    fprintf(stderr, "RAW fill CDP using rows %d to %d\n", start_row, end_row);
1086 #endif
1087 	    if (end_time == last_up) {
1088 		end_row = chosen_candidate->ptr->cur_row;
1089 	    }
1090 	    if (end_time > last_up) {
1091 		end_row = chosen_candidate->ptr->cur_row;
1092 	    }
1093 
1094 	    int cnt = end_row - start_row + 1;
1095 	    if (end_row < start_row) {
1096 		cnt += chosen_candidate->rra->row_cnt;
1097 	    }
1098 
1099 	    int row_cnt = chosen_candidate->rra->row_cnt;
1100 
1101 	    // the chosen candidate CDP for the DS...
1102 	    cdp_prep_t *ccdp = chosen_candidate->cdp + mapped_i;
1103 
1104 #ifdef MODIFY_DEBUG
1105 	    fprintf(stderr, "fill CDP using rows %d to %d (=%d)\n", start_row, end_row, cnt);
1106 #endif
1107 	    /*
1108 	      if (start_row == -1) we are just at the start of a
1109 	      new CDP interval and we can just reconstruct the CDP
1110 	      from various information:
1111 
1112 	      if (start_row != -1, we assume that we are a couple
1113 	      of steps behind (namely at a time that would CAUSE
1114 	      start_row to be -1, fill out the CDP and then we
1115 	      update the CDP with data points from the chosen
1116 	      candidate RRA.
1117 	    */
1118 
1119 	    // null out the CDP....
1120 
1121 	    for (int z = 0 ; z < MAX_CDP_PAR_EN ; z++) {
1122 		cdp_prep->scratch[z].u_val = 0;
1123 	    }
1124 
1125 	    rrd_value_t curr = out->rrd_value[start_values_index_out +
1126 					      out->stat_head->ds_cnt  * (out->rra_ptr[curr_rra].cur_row) +
1127 					      i];
1128 
1129 	    cdp_prep->scratch[CDP_primary_val].u_val = curr;
1130 	    cdp_prep->scratch[CDP_val].u_val = 0;
1131 
1132 	    if (start_row == -1) {
1133 		// the primary value of the chosen_candidate cdp becomes the secondary value for the new CDP
1134 
1135 		cdp_prep->scratch[CDP_secondary_val].u_val =
1136 		    ccdp->scratch[CDP_primary_val].u_val;
1137 	    } else {
1138 		int pre_start = start_row - 1;
1139 		if (pre_start < 0) pre_start = chosen_candidate->rra->row_cnt - 1;
1140 
1141 		cdp_prep->scratch[CDP_secondary_val].u_val =
1142 		    chosen_candidate->values[ds_cnt * pre_start + mapped_i];
1143 
1144 		int start_pdp_offset = rra_def->pdp_cnt;
1145 
1146 		for (int j =  0 ; j < cnt ; j++) {
1147 		    int row = (start_row + j) % row_cnt;
1148 		    rrd_value_t v = chosen_candidate->values[ds_cnt * row + mapped_i];
1149 
1150 		    update_cdp(
1151 			       cdp_prep->scratch,    //    unival *scratch,
1152 			       cf,   //    int current_cf,
1153 			       v,    //    rrd_value_t pdp_temp_val,
1154 			       0,    //    unsigned long rra_step_cnt,
1155 			       1,    //    unsigned long elapsed_pdp_st,
1156 			       start_pdp_offset--,    //    unsigned long start_pdp_offset,
1157 			       rra_def->pdp_cnt,    //    unsigned long pdp_cnt,
1158 			       chosen_candidate->rra->par[RRA_cdp_xff_val].u_val,    //    rrd_value_t xff,
1159 			       0,    //    int i,
1160 			       0     //    int ii)
1161 			       );
1162 		}
1163 	    }
1164 	}
1165     }
1166 
1167     if (candidates) free(candidates);
1168 }
1169 
1170 
add_rras(const rrd_t * in,rrd_t * out,const int * ds_map,const rra_mod_op_t * rra_mod_ops,int rra_mod_ops_cnt,unsigned long hash)1171 static int add_rras(const rrd_t *in, rrd_t *out, const int *ds_map,
1172 		    const rra_mod_op_t *rra_mod_ops, int rra_mod_ops_cnt, unsigned long hash)
1173 {
1174     int rc = -1;
1175 
1176     /* now add any new RRAs: */
1177     cdp_prep_t empty_cdp_prep;
1178     int i, r;
1179     unsigned int last_rra_cnt = out->stat_head->rra_cnt;
1180     int total_out_rra_rows = 0;
1181     int total_cnt_out = 0;
1182     const char *require_version = NULL;
1183 
1184     memset(&empty_cdp_prep, 0, sizeof(cdp_prep_t));
1185 
1186     // first, calculate total number of rows already in rrd_value...
1187 
1188     for (i = 0 ; i < (int) last_rra_cnt ; i++) {
1189 	total_out_rra_rows += out->rra_def[i].row_cnt;
1190     }
1191     total_cnt_out = out->stat_head->ds_cnt * total_out_rra_rows;
1192 
1193     for (r = 0 ; r < rra_mod_ops_cnt ; r++) {
1194 	if (rra_mod_ops[r].op == 'a') {
1195 	    rra_def_t rra_def;
1196 
1197 	    // the hash doesn't really matter...
1198 	    parseRRA(rra_mod_ops[r].def, &rra_def, out, hash, &require_version);
1199 
1200 	    if (rrd_test_error()) {
1201 		// failed!!!
1202 		goto done;
1203             }
1204 
1205 		out->rra_def = (rra_def_t *) copy_over_realloc(out->rra_def, out->stat_head->rra_cnt,
1206 					    &rra_def, 0,
1207 					    sizeof(rra_def_t));
1208 	    if (out->rra_def == NULL) goto done;
1209 	    out->stat_head->rra_cnt++;
1210 
1211 	    out->rra_def = handle_dependent_rras(out->rra_def, &(out->stat_head->rra_cnt),
1212 						hash);
1213 	    if (out->rra_def == NULL) {
1214 		goto done;
1215 	    }
1216 	}
1217     }
1218 
1219     if (require_version != NULL && atoi(require_version) < atoi(out->stat_head->version)) {
1220         strncpy(out->stat_head->version, require_version, 4);
1221         out->stat_head->version[4] = '\0';
1222     }
1223 
1224     if (last_rra_cnt < out->stat_head->rra_cnt) {
1225 	// extend cdp_prep and rra_ptr arrays
1226 		out->cdp_prep = (cdp_prep_t *) realloc(out->cdp_prep,
1227 				sizeof(cdp_prep_t) * out->stat_head->ds_cnt
1228 				* (out->stat_head->rra_cnt));
1229 
1230 	if (out->cdp_prep == NULL) {
1231 	    rrd_set_error("out of memory");
1232 	    goto done;
1233 	}
1234 
1235 	out->rra_ptr = (rra_ptr_t *) realloc(out->rra_ptr,
1236 			       sizeof(rra_ptr_t) * out->stat_head->rra_cnt);
1237 
1238 	if (out->rra_ptr == NULL) {
1239 	    rrd_set_error("out of memory");
1240 	    goto done;
1241 	}
1242     }
1243 
1244     int curr_rra;
1245     for (curr_rra = last_rra_cnt ; curr_rra < (int) out->stat_head->rra_cnt ; curr_rra++ ) {
1246 	// RRA added!!!
1247 	rra_def_t *rra_def = out->rra_def + curr_rra;
1248 
1249 	// null out CDPs
1250 	int start_cdp_index_out = out->stat_head->ds_cnt * curr_rra;
1251 	for (i = 0 ; i < (int) out->stat_head->ds_cnt ; i++) {
1252 	    cdp_prep_t *cdp_prep = out->cdp_prep + start_cdp_index_out + i;
1253 	    memcpy(cdp_prep,
1254 		   &empty_cdp_prep, sizeof(cdp_prep_t));
1255 	}
1256 
1257 	out->rra_ptr[curr_rra].cur_row = rra_def->row_cnt - 1;
1258 
1259 	// extend and fill rrd_value array...
1260 	int start_values_index_out = total_out_rra_rows;
1261 
1262 	total_out_rra_rows += rra_def->row_cnt;
1263 
1264 	/* prepare space for output data */
1265 	out->rrd_value = (rrd_value_t *) realloc(out->rrd_value,
1266 				(total_out_rra_rows) * out->stat_head->ds_cnt
1267 				* sizeof(rrd_value_t));
1268 
1269 	if (out->rrd_value == NULL) {
1270 	    rrd_set_error("out of memory");
1271 	    goto done;
1272 	}
1273 
1274 	unsigned int oi, jj;
1275 	for (oi = 0 ; oi < rra_def->row_cnt ; oi++) {
1276 	    for (jj = 0 ; jj < out->stat_head->ds_cnt ; jj++) {
1277 		out->rrd_value[total_cnt_out + oi * out->stat_head->ds_cnt + jj] = DNAN;
1278 	    }
1279 	}
1280 
1281 	int rra_values_out = out->stat_head->ds_cnt * rra_def->row_cnt;
1282 
1283 	// now try to populate the newly added rows
1284 	populate_row(in, out, ds_map,
1285 		     rra_def,
1286 		     out->rra_ptr[curr_rra].cur_row,
1287 		     out->rrd_value + total_cnt_out,
1288 		     0, rra_def->row_cnt);
1289 
1290 	prepare_CDPs(in, out, curr_rra, start_values_index_out, ds_map);
1291 
1292 	total_cnt_out += rra_values_out;
1293     }
1294     rc = 0;
1295 done:
1296     return rc;
1297 }
1298 
handle_modify(const rrd_t * in,const char * outfilename,int argc,char ** argv,int optidx,int newstep)1299 int handle_modify(const rrd_t *in, const char *outfilename,
1300 		  int argc, char **argv, int optidx,
1301 		  int newstep) {
1302     // parse add/remove options
1303     int rc = -1;
1304     int i;
1305 
1306     const char **del = NULL, **add = NULL;
1307     rra_mod_op_t *rra_ops = NULL;
1308     int rcnt = 0, acnt = 0, rraopcnt = 0;
1309 
1310     for (i = optidx ; i < argc ; i++) {
1311 	if (strncmp("DEL:", argv[i], 4) == 0 && strlen(argv[i]) > 4) {
1312 		del = (const char **) realloc((char **) del, (rcnt + 2) * sizeof(char*));   /* Cast 'del' from 'const char **' to 'char **' to avoid MSVC warning C4090 */
1313 	    if (del == NULL) {
1314 		rrd_set_error("out of memory");
1315 		rc = -1;
1316 		goto done;
1317 	    }
1318 
1319 	    del[rcnt] = strdup(argv[i] + 4);
1320 	    if (del[rcnt] == NULL) {
1321 		rrd_set_error("out of memory");
1322 		rc = -1;
1323 		goto done;
1324 	    }
1325 
1326 	    rcnt++;
1327 	    del[rcnt] = NULL;
1328 	} else if (strncmp("DS:", argv[i], 3) == 0 && strlen(argv[i]) > 3) {
1329 		add = (const char **) realloc((char **) add, (acnt + 2) * sizeof(char*));   /* Cast 'add' from 'const char **' to 'char **' to avoid MSVC warning C4090 */
1330 	    if (add == NULL) {
1331 		rrd_set_error("out of memory");
1332 		rc = -1;
1333 		goto done;
1334 	    }
1335 
1336 	    add[acnt] = strdup(argv[i]);
1337 	    if (add[acnt] == NULL) {
1338 		rrd_set_error("out of memory");
1339 		rc = -1;
1340 		goto done;
1341 	    }
1342 
1343 	    acnt++;
1344 	    add[acnt] = NULL;
1345 	} else if (strncmp("RRA#", argv[i], 4) == 0 && strlen(argv[i]) > 4) {
1346 		rra_mod_op_t rra_mod; // = { .def = NULL };  // not in VS2013
1347 		rra_mod.def = NULL;
1348 	    char sign;
1349 	    unsigned int number;
1350 	    unsigned int idx;
1351 
1352 	    if (sscanf(argv[i] + 4, "%u:%c%u", &idx, &sign, &number) != 3) {
1353 		rrd_set_error("Failed to parse RRA# command");
1354 		rc = -1;
1355 		goto done;
1356 	    }
1357 
1358 	    rra_mod.index = idx;
1359 	    switch (sign) {
1360 	    case '=':
1361 	    case '-':
1362 	    case '+':
1363 		rra_mod.index = idx;
1364 		rra_mod.op = sign;
1365 		rra_mod.row_count = number;
1366 		rra_mod.final_row_count = 0;
1367 		break;
1368 	    default:
1369 		rrd_set_error("Failed to parse RRA# command: invalid operation: %c", sign);
1370 		rc = -1;
1371 		goto done;
1372 	    }
1373 
1374 	    rra_ops = (rra_mod_op_t *) copy_over_realloc(rra_ops, rraopcnt,
1375 					&rra_mod, 0, sizeof(rra_mod));
1376 	    if (rra_ops == NULL) {
1377 		rrd_set_error("out of memory");
1378 		rc = -1;
1379 		goto done;
1380 	    }
1381 	    rraopcnt++;
1382 	} else if (strncmp("RRA:", argv[i], 4) == 0 && strlen(argv[i]) > 4) {
1383 	    rra_mod_op_t rra_mod;
1384 	    rra_mod.op = 'a';
1385 	    rra_mod.index = -1;
1386 	    rra_mod.def = strdup(argv[i]);
1387 
1388 	    if (rra_mod.def == NULL) {
1389 		rrd_set_error("out of memory");
1390 		rc = -1;
1391 		goto done;
1392 	    }
1393 
1394 		rra_ops = (rra_mod_op_t *) copy_over_realloc(rra_ops, rraopcnt,
1395 					&rra_mod, 0, sizeof(rra_mod));
1396 	    if (rra_ops == NULL) {
1397 		rrd_set_error("out of memory");
1398 		rc = -1;
1399 		goto done;
1400 	    }
1401 	    rraopcnt++;
1402 	} else if (strncmp("DELRRA:", argv[i], 7) == 0 && strlen(argv[i]) > 7) {
1403 		rra_mod_op_t rra_mod;
1404 		/* NOT in VS2013 C++ allowed
1405 		= { .def = NULL,
1406 				     .op = '=',
1407 				     .row_count = 0 // eg. deletion
1408 	    };*/
1409 		rra_mod.def = NULL;
1410 		rra_mod.op = '=';
1411 		rra_mod.row_count = 0; // eg. deletion
1412 
1413 	    rra_mod.index = atoi(argv[i] + 7);
1414 	    if (rra_mod.index < 0 ) {
1415 		rrd_set_error("DELRRA requires a non-negative, integer argument");
1416 		rc = -1;
1417 		goto done;
1418 	    }
1419 
1420 		rra_ops = (rra_mod_op_t *) copy_over_realloc(rra_ops, rraopcnt,
1421 					&rra_mod, 0, sizeof(rra_mod));
1422 	    if (rra_ops == NULL) {
1423 		rrd_set_error("out of memory");
1424 		rc = -1;
1425 		goto done;
1426 	    }
1427 	    rraopcnt++;
1428 	} else {
1429 	    rrd_set_error("unparseable argument: %s", argv[i]);
1430 	    rc = -1;
1431 	    goto done;
1432 	}
1433     }
1434 
1435     if (rcnt > 0 || acnt > 0 || rraopcnt > 0) {
1436 	unsigned long hashed_name = FnvHash(outfilename);
1437 	rrd_t *out = rrd_modify_r2(in, del, add, rra_ops, rraopcnt, newstep, hashed_name);
1438 
1439 	if (out == NULL) {
1440 	    goto done;
1441 	}
1442 
1443 	rc = write_rrd(outfilename, out);
1444 	rrd_free(out);
1445 	free(out);
1446 
1447 	if (rc < 0) goto done;
1448     }
1449 
1450     rc = argc;
1451 
1452 done:
1453     if (del) {
1454 	for (const char **c = del ; *c ; c++) {
1455 	    free((void*) *c);
1456 	}
1457 	free((char **) del);    /* Cast 'del' from 'const char **' to 'char **' to avoid MSVC warning C4090 */
1458     }
1459     if (add) {
1460 	for (const char **c = add ; *c ; c++) {
1461 	    free((void*) *c);
1462 	}
1463 	free((char **) add);    /* Cast 'add' from 'const char **' to 'char **' to avoid MSVC warning C4090 */
1464     }
1465     if (rra_ops) {
1466 	for (i = 0 ; i < rraopcnt ; i++) {
1467 	    if (rra_ops[i].def) free(rra_ops[i].def);
1468 	}
1469 	free(rra_ops);
1470     }
1471 
1472     return rc;
1473 }
1474