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