Code

Imported upstream version 1.3.5.
[pkg-rrdtool.git] / src / rrd_resize.c
1 /*****************************************************************************
2  * RRDtool 1.3.5  Copyright by Tobi Oetiker, 1997-2008
3  *****************************************************************************
4  * rrd_resize.c Alters size of an RRA
5  *****************************************************************************
6  * Initial version by Alex van den Bogaerdt
7  *****************************************************************************/
9 #include "rrd_tool.h"
11 #ifdef WIN32
12 #include <stdlib.h>
13 #endif
15 int rrd_resize(
16     int argc,
17     char **argv)
18 {
19     char     *infilename, outfilename[11] = "resize.rrd";
20     rrd_t     rrdold, rrdnew;
21     rrd_value_t buffer;
22     int       version;
23     unsigned long l, rra;
24     long      modify;
25     unsigned long target_rra;
26     int       grow = 0, shrink = 0;
27     char     *endptr;
28     rrd_file_t *rrd_file, *rrd_out_file;
30     infilename = argv[1];
31     if (!strcmp(infilename, "resize.rrd")) {
32         rrd_set_error("resize.rrd is a reserved name");
33         return (-1);
34     }
35     if (argc != 5) {
36         rrd_set_error("wrong number of parameters");
37         return (-1);
38     }
40     target_rra = strtol(argv[2], &endptr, 0);
42     if (!strcmp(argv[3], "GROW"))
43         grow = 1;
44     else if (!strcmp(argv[3], "SHRINK"))
45         shrink = 1;
46     else {
47         rrd_set_error("I can only GROW or SHRINK");
48         return (-1);
49     }
51     modify = strtol(argv[4], &endptr, 0);
53     if ((modify < 1)) {
54         rrd_set_error("Please grow or shrink with at least 1 row");
55         return (-1);
56     }
58     if (shrink)
59         modify = -modify;
62     rrd_file = rrd_open(infilename, &rrdold, RRD_READWRITE | RRD_COPY);
63     if (rrd_file == NULL) {
64         rrd_free(&rrdold);
65         return (-1);
66     }
68     if (rrd_lock(rrd_file) != 0) {
69         rrd_set_error("could not lock original RRD");
70         rrd_free(&rrdold);
71         rrd_close(rrd_file);
72         return (-1);
73     }
76     if (target_rra >= rrdold.stat_head->rra_cnt) {
77         rrd_set_error("no such RRA in this RRD");
78         rrd_free(&rrdold);
79         rrd_close(rrd_file);
80         return (-1);
81     }
83     if (modify < 0)
84         if ((long) rrdold.rra_def[target_rra].row_cnt <= -modify) {
85             rrd_set_error("This RRA is not that big");
86             rrd_free(&rrdold);
87             rrd_close(rrd_file);
88             return (-1);
89         }
90     /* the size of the new file */
91     /* yes we are abusing the float cookie for this, aargh */
92     if ((rrdnew.stat_head = (stat_head_t*)calloc(1, sizeof(stat_head_t))) == NULL) {
93         rrd_set_error("allocating stat_head for new RRD");
94         rrd_free(&rrdold);
95         rrd_close(rrd_file);
96         return (-1);
97     }
98     rrdnew.stat_head->float_cookie = rrd_file->file_len +
99         (rrdold.stat_head->ds_cnt * sizeof(rrd_value_t) * modify);
100     rrd_out_file = rrd_open(outfilename, &rrdnew, RRD_READWRITE | RRD_CREAT);
101     if (rrd_out_file == NULL) {
102         rrd_set_error("Can't create '%s': %s", outfilename,
103                       rrd_strerror(errno));
104         rrd_free(&rrdnew);
105         return (-1);
106     }
107     if (rrd_lock(rrd_out_file) != 0) {
108         rrd_set_error("could not lock new RRD");
109         rrd_free(&rrdold);
110         rrd_close(rrd_file);
111         rrd_close(rrd_out_file);
112         return (-1);
113     }
114 /*XXX: do one write for those parts of header that are unchanged */
115     if ((rrdnew.stat_head = (stat_head_t*)malloc(sizeof(stat_head_t))) == NULL) {
116         rrd_set_error("allocating stat_head for new RRD");
117         rrd_free(&rrdnew);
118         rrd_free(&rrdold);
119         rrd_close(rrd_file);
120         rrd_close(rrd_out_file);
121         return (-1);
122     }
124     if ((rrdnew.rra_ptr = (rra_ptr_t*)malloc(sizeof(rra_ptr_t) * rrdold.stat_head->rra_cnt)) == NULL) {
125         rrd_set_error("allocating rra_ptr for new RRD");
126         rrd_free(&rrdnew);
127         rrd_free(&rrdold);
128         rrd_close(rrd_file);
129         rrd_close(rrd_out_file);
130         return (-1);
131     }
133     if ((rrdnew.rra_def = (rra_def_t*)malloc(sizeof(rra_def_t) * rrdold.stat_head->rra_cnt)) == NULL) {
134         rrd_set_error("allocating rra_def for new RRD");
135         rrd_free(&rrdnew);
136         rrd_free(&rrdold);
137         rrd_close(rrd_file);
138         rrd_close(rrd_out_file);
139         return (-1);
140     }
141      
142 #ifndef WIN32
143     memcpy(rrdnew.stat_head,rrdold.stat_head,sizeof(stat_head_t));
144     rrdnew.ds_def = rrdold.ds_def;
145     memcpy(rrdnew.rra_def,rrdold.rra_def,sizeof(rra_def_t) * rrdold.stat_head->rra_cnt);    
146     rrdnew.live_head = rrdold.live_head;
147     rrdnew.pdp_prep = rrdold.pdp_prep;
148     rrdnew.cdp_prep = rrdold.cdp_prep;
149     memcpy(rrdnew.rra_ptr,rrdold.rra_ptr,sizeof(rra_ptr_t) * rrdold.stat_head->rra_cnt);
150 #else // WIN32
151         /*
152          * For windows Other fields also should be allocated. Crashes otherwise
153          */
155     if ((rrdnew.ds_def = (ds_def_t*)malloc(sizeof(ds_def_t) * rrdold.stat_head->ds_cnt)) == NULL) {
156         rrd_set_error("allocating ds_def for new RRD");
157         rrd_free(&rrdnew);
158         rrd_free(&rrdold);
159         rrd_close(rrd_file);
160         rrd_close(rrd_out_file);
161         return (-1);
162     }
164     if ((rrdnew.live_head = (live_head_t*)malloc(sizeof(live_head_t))) == NULL) {
165         rrd_set_error("allocating live_head for new RRD");
166         rrd_free(&rrdnew);
167         rrd_free(&rrdold);
168         rrd_close(rrd_file);
169         rrd_close(rrd_out_file);
170         return (-1);
171     }
173     if ((rrdnew.pdp_prep = (pdp_prep_t*)malloc(sizeof(pdp_prep_t))) == NULL) {
174         rrd_set_error("allocating pdp_prep for new RRD");
175         rrd_free(&rrdnew);
176         rrd_free(&rrdold);
177         rrd_close(rrd_file);
178         rrd_close(rrd_out_file);
179         return (-1);
180     }
182     if ((rrdnew.cdp_prep = (cdp_prep_t*)malloc(sizeof(cdp_prep_t))) == NULL) {
183         rrd_set_error("allocating cdp_prep for new RRD");
184         rrd_free(&rrdnew);
185         rrd_free(&rrdold);
186         rrd_close(rrd_file);
187         rrd_close(rrd_out_file);
188         return (-1);
189     }
190     memcpy(rrdnew.stat_head,rrdold.stat_head,sizeof(stat_head_t));
191     memcpy(rrdnew.ds_def,rrdold.ds_def,sizeof(ds_def_t) * rrdold.stat_head->ds_cnt);
192     memcpy(rrdnew.rra_def,rrdold.rra_def,sizeof(rra_def_t) * rrdold.stat_head->rra_cnt);    
193     memcpy(rrdnew.live_head,rrdold.live_head,sizeof(live_head_t));
194     memcpy(rrdnew.pdp_prep,rrdold.pdp_prep,sizeof(pdp_prep_t));
195     memcpy(rrdnew.cdp_prep,rrdold.cdp_prep,sizeof(cdp_prep_t));
196     memcpy(rrdnew.rra_ptr,rrdold.rra_ptr,sizeof(rra_ptr_t) * rrdold.stat_head->rra_cnt);
197 #endif // WIN32
199     version = atoi(rrdold.stat_head->version);
200     switch (version) {
201     case 4:
202         break;        
203     case 3:
204         break;
205     case 1:
206         rrdold.stat_head->version[3] = '3';
207         break;
208     default:
209         rrd_set_error("Do not know how to handle RRD version %s",
210                       rrdold.stat_head->version);
211         rrd_free(&rrdnew);
212         rrd_free(&rrdold);
213         rrd_close(rrd_file);
214         rrd_close(rrd_out_file);
215         return (-1);
216         break;
217     }
219 /* XXX: Error checking? */
220     rrd_write(rrd_out_file, rrdnew.stat_head, sizeof(stat_head_t) * 1);
221     rrd_write(rrd_out_file, rrdnew.ds_def,
222               sizeof(ds_def_t) * rrdnew.stat_head->ds_cnt);
223     rrd_write(rrd_out_file, rrdnew.rra_def,
224               sizeof(rra_def_t) * rrdnew.stat_head->rra_cnt);
225     rrd_write(rrd_out_file, rrdnew.live_head, sizeof(live_head_t) * 1);
226     rrd_write(rrd_out_file, rrdnew.pdp_prep,
227               sizeof(pdp_prep_t) * rrdnew.stat_head->ds_cnt);
228     rrd_write(rrd_out_file, rrdnew.cdp_prep,
229               sizeof(cdp_prep_t) * rrdnew.stat_head->ds_cnt *
230               rrdnew.stat_head->rra_cnt);
231     rrd_write(rrd_out_file, rrdnew.rra_ptr,
232               sizeof(rra_ptr_t) * rrdnew.stat_head->rra_cnt);
234     /* Move the CDPs from the old to the new database.
235      ** This can be made (much) faster but isn't worth the effort. Clarity
236      ** is much more important.
237      */
239     /* Move data in unmodified RRAs
240      */
241     l = 0;
242     for (rra = 0; rra < target_rra; rra++) {
243         l += rrdnew.stat_head->ds_cnt * rrdnew.rra_def[rra].row_cnt;
244     }
245     while (l > 0) {
246         rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1);
247         rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
248         l--;
249     }
250     /* Move data in this RRA, either removing or adding some rows
251      */
252     if (modify > 0) {
253         /* Adding extra rows; insert unknown values just after the
254          ** current row number.
255          */
256         l = rrdnew.stat_head->ds_cnt *
257             (rrdnew.rra_ptr[target_rra].cur_row + 1);
258         while (l > 0) {
259             rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1);
260             rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
261             l--;
262         }
263 #ifndef HAVE_MMAP
264         buffer = DNAN;
265         l = rrdnew.stat_head->ds_cnt * modify;
266         while (l > 0) {
267             rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
268             l--;
269         }
270 #else
271         /* for the mmap case, we did already fill the whole new file with DNAN
272          * before we copied the old values, so nothing to do here.  */
273 #endif
274     } else {
275         /* Removing rows. Normally this would be just after the cursor
276          ** however this may also mean that we wrap to the beginning of
277          ** the array.
278          */
279         signed long int remove_end = 0;
281         remove_end =
282             (rrdnew.rra_ptr[target_rra].cur_row -
283              modify) % rrdnew.rra_def[target_rra].row_cnt;
284         if (remove_end <=
285             (signed long int) rrdnew.rra_ptr[target_rra].cur_row) {
286             while (remove_end >= 0) {
287                 rrd_seek(rrd_file,
288                          sizeof(rrd_value_t) * rrdnew.stat_head->ds_cnt,
289                          SEEK_CUR);
290                 rrdnew.rra_ptr[target_rra].cur_row--;
291                 rrdnew.rra_def[target_rra].row_cnt--;
292                 remove_end--;
293                 modify++;
294             }
295             remove_end = rrdnew.rra_def[target_rra].row_cnt - 1;
296         }
297         for (l = 0; l <= rrdnew.rra_ptr[target_rra].cur_row; l++) {
298             unsigned int tmp;
300             for (tmp = 0; tmp < rrdnew.stat_head->ds_cnt; tmp++) {
301                 rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1);
302                 rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
303             }
304         }
305         while (modify < 0) {
306             rrd_seek(rrd_file,
307                      sizeof(rrd_value_t) * rrdnew.stat_head->ds_cnt,
308                      SEEK_CUR);
309             rrdnew.rra_def[target_rra].row_cnt--;
310             modify++;
311         }
312     }
313     /* Move the rest of the CDPs
314      */
315     while (1) {
316         if (rrd_read(rrd_file, &buffer, sizeof(rrd_value_t) * 1) <= 0)
317             break;
318         rrd_write(rrd_out_file, &buffer, sizeof(rrd_value_t) * 1);
319     }
320     rrdnew.rra_def[target_rra].row_cnt += modify;
321     rrd_seek(rrd_out_file,
322              sizeof(stat_head_t) +
323              sizeof(ds_def_t) * rrdnew.stat_head->ds_cnt, SEEK_SET);
324     rrd_write(rrd_out_file, rrdnew.rra_def,
325               sizeof(rra_def_t) * rrdnew.stat_head->rra_cnt);
326     rrd_seek(rrd_out_file, sizeof(live_head_t), SEEK_CUR);
327     rrd_seek(rrd_out_file, sizeof(pdp_prep_t) * rrdnew.stat_head->ds_cnt,
328              SEEK_CUR);
329     rrd_seek(rrd_out_file,
330              sizeof(cdp_prep_t) * rrdnew.stat_head->ds_cnt *
331              rrdnew.stat_head->rra_cnt, SEEK_CUR);
332     rrd_write(rrd_out_file, rrdnew.rra_ptr,
333               sizeof(rra_ptr_t) * rrdnew.stat_head->rra_cnt);
334     rrd_close(rrd_file);    
335     rrd_close(rrd_out_file);    
336     rrd_free(&rrdold);
337     rrd_free(&rrdnew);
338     return (0);