1 /*****************************************************************************
2 * RRDtool 1.0.33 Copyright Tobias Oetiker, 1997 - 2000
3 *****************************************************************************
4 * rrd_update.c RRD Update Function
5 *****************************************************************************
8 * Revision 1.1 2001/02/25 22:25:06 oetiker
11 *****************************************************************************/
14 #include <sys/types.h>
18 #include <sys/locking.h>
25 int LockRRD(FILE *rrd_file);
32 main(int argc, char **argv){
33 rrd_update(argc,argv);
34 if (rrd_test_error()) {
35 printf("RRDtool 1.0.33 Copyright 1997-2000 by Tobias Oetiker <tobi@oetiker.ch>\n\n"
36 "Usage: rrdupdate filename\n"
37 "\t\t\t[--template|-t ds-name:ds-name:...]\n"
38 "\t\t\ttime|N:value[:value...]\n\n"
39 "\t\t\t[ time:value[:value...] ..]\n\n");
41 printf("ERROR: %s\n",rrd_get_error());
50 rrd_update(int argc, char **argv)
56 unsigned long rra_begin; /* byte pointer to the rra
57 * area in the rrd file. this
58 * pointer never changes value */
59 unsigned long rra_start; /* byte pointer to the rra
60 * area in the rrd file. this
61 * pointer changes as each rrd is
63 unsigned long rra_current; /* byte pointer to the current write
64 * spot in the rrd file. */
65 unsigned long rra_pos_tmp; /* temporary byte pointer. */
66 unsigned long interval,
67 pre_int,post_int; /* interval between this and
69 unsigned long proc_pdp_st; /* which pdp_st was the last
71 unsigned long occu_pdp_st; /* when was the pdp_st
72 * before the last update
74 unsigned long proc_pdp_age; /* how old was the data in
75 * the pdp prep area when it
77 unsigned long occu_pdp_age; /* how long ago was the last
79 unsigned long pdp_st; /* helper for cdp_prep
81 rrd_value_t *pdp_new; /* prepare the incoming data
84 rrd_value_t *pdp_temp; /* prepare the pdp values
88 long *tmpl_idx; /* index representing the settings
89 transported by the template index */
90 long tmpl_cnt = 2; /* time and data */
94 time_t current_time = time(NULL);
96 int wrote_to_file = 0;
97 char *template = NULL;
101 static struct option long_options[] =
103 {"template", required_argument, 0, 't'},
106 int option_index = 0;
108 opt = getopt_long(argc, argv, "t:",
109 long_options, &option_index);
120 rrd_set_error("unknown option '%s'",argv[optind-1]);
126 /* need at least 2 arguments: filename, data. */
127 if (argc-optind < 2) {
128 rrd_set_error("Not enough arguments");
132 if(rrd_open(argv[optind],&rrd_file,&rrd, RRD_READWRITE)==-1){
135 rra_current = rra_start = rra_begin = ftell(rrd_file);
136 /* This is defined in the ANSI C standard, section 7.9.5.3:
138 When a file is opened with udpate mode ('+' as the second
139 or third character in the ... list of mode argument
140 variables), both input and ouptut may be performed on the
141 associated stream. However, ... input may not be directly
142 followed by output without an intervening call to a file
143 positioning function, unless the input oepration encounters
145 fseek(rrd_file, 0, SEEK_CUR);
148 /* get exclusive lock to whole file.
149 * lock gets removed when we close the file.
151 if (LockRRD(rrd_file) != 0) {
152 rrd_set_error("could not lock RRD");
158 if((updvals = malloc( sizeof(char*) * (rrd.stat_head->ds_cnt+1)))==NULL){
159 rrd_set_error("allocating updvals pointer array");
165 if ((pdp_temp = malloc(sizeof(rrd_value_t)
166 *rrd.stat_head->ds_cnt))==NULL){
167 rrd_set_error("allocating pdp_temp ...");
174 if ((tmpl_idx = malloc(sizeof(unsigned long)
175 *(rrd.stat_head->ds_cnt+1)))==NULL){
176 rrd_set_error("allocating tmpl_idx ...");
183 /* initialize template redirector */
185 tmpl_idx[0] -> 0; (time)
186 tmpl_idx[1] -> 1; (DS 0)
187 tmpl_idx[2] -> 2; (DS 1)
188 tmpl_idx[3] -> 3; (DS 2)
190 for (i=0;i<=rrd.stat_head->ds_cnt;i++) tmpl_idx[i]=i;
191 tmpl_cnt=rrd.stat_head->ds_cnt+1;
196 tmpl_cnt = 1; /* the first entry is the time */
197 tmpl_len = strlen(template);
198 for(i=0;i<=tmpl_len ;i++) {
199 if (template[i] == ':' || template[i] == '\0') {
201 if (tmpl_cnt>rrd.stat_head->ds_cnt){
202 rrd_set_error("Template contains more DS definitions than RRD");
203 free(updvals); free(pdp_temp);
204 free(tmpl_idx); rrd_free(&rrd);
205 fclose(rrd_file); return(-1);
207 if ((tmpl_idx[tmpl_cnt++] = ds_match(&rrd,dsname)) == -1){
208 rrd_set_error("unknown DS name '%s'",dsname);
209 free(updvals); free(pdp_temp);
210 free(tmpl_idx); rrd_free(&rrd);
211 fclose(rrd_file); return(-1);
213 /* the first element is always the time */
214 tmpl_idx[tmpl_cnt-1]++;
215 /* go to the next entry on the template */
216 dsname = &template[i+1];
217 /* fix the damage we did before */
226 if ((pdp_new = malloc(sizeof(rrd_value_t)
227 *rrd.stat_head->ds_cnt))==NULL){
228 rrd_set_error("allocating pdp_new ...");
237 /* loop through the arguments. */
238 for(arg_i=optind+1; arg_i<argc;arg_i++) {
239 char *stepper = malloc((strlen(argv[arg_i])+1)*sizeof(char));
240 char *step_start = stepper;
241 if (stepper == NULL){
242 rrd_set_error("faild duplication argv entry");
250 /* initialize all ds input to unknown except the first one
251 which has always got to be set */
252 for(ii=1;ii<=rrd.stat_head->ds_cnt;ii++) updvals[ii] = "U";
254 strcpy(stepper,argv[arg_i]);
257 if (*stepper == ':') {
261 updvals[tmpl_idx[ii]] = stepper+1;
267 if (ii != tmpl_cnt-1) {
268 rrd_set_error("expected %lu data source readings (got %lu) from %s:...",
269 tmpl_cnt-1, ii, argv[arg_i]);
274 /* get the time from the reading ... handle N */
275 if (strcmp(updvals[0],"N")==0){
276 current_time = time(NULL);
278 current_time = atol(updvals[0]);
281 if(current_time <= rrd.live_head->last_up){
282 rrd_set_error("illegal attempt to update using time %ld when "
283 "last update time is %ld (minimum one second step)",
284 current_time, rrd.live_head->last_up);
290 /* seek to the beginning of the rrd's */
291 if (rra_current != rra_begin) {
292 if(fseek(rrd_file, rra_begin, SEEK_SET) != 0) {
293 rrd_set_error("seek error in rrd");
297 rra_current = rra_begin;
299 rra_start = rra_begin;
301 /* when was the current pdp started */
302 proc_pdp_age = rrd.live_head->last_up % rrd.stat_head->pdp_step;
303 proc_pdp_st = rrd.live_head->last_up - proc_pdp_age;
305 /* when did the last pdp_st occur */
306 occu_pdp_age = current_time % rrd.stat_head->pdp_step;
307 occu_pdp_st = current_time - occu_pdp_age;
308 interval = current_time - rrd.live_head->last_up;
310 if (occu_pdp_st > proc_pdp_st){
311 /* OK we passed the pdp_st moment*/
312 pre_int = occu_pdp_st - rrd.live_head->last_up; /* how much of the input data
313 * occurred before the latest
315 post_int = occu_pdp_age; /* how much after it */
329 "post_int %lu\n", proc_pdp_age, proc_pdp_st,
330 occu_pdp_age, occu_pdp_st,
331 interval, pre_int, post_int);
334 /* process the data sources and update the pdp_prep
335 * area accordingly */
336 for(i=0;i<rrd.stat_head->ds_cnt;i++){
338 dst_idx= dst_conv(rrd.ds_def[i].dst);
339 if((updvals[i+1][0] != 'U') &&
340 rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt >= interval) {
342 /* the data source type defines how to process the data */
343 /* pdp_temp contains rate * time ... eg the bytes
344 * transferred during the interval. Doing it this way saves
345 * a lot of math operations */
351 if(rrd.pdp_prep[i].last_ds[0] != 'U'){
352 pdp_new[i]= rrd_diff(updvals[i+1],rrd.pdp_prep[i].last_ds);
353 if(dst_idx == DST_COUNTER) {
354 /* simple overflow catcher sugestet by andres kroonmaa */
355 /* this will fail terribly for non 32 or 64 bit counters ... */
356 /* are there any others in SNMP land ? */
357 if (pdp_new[i] < (double)0.0 )
358 pdp_new[i] += (double)4294967296.0 ; /* 2^32 */
359 if (pdp_new[i] < (double)0.0 )
360 pdp_new[i] += (double)18446744069414584320.0; /* 2^64-2^32 */;
362 rate = pdp_new[i] / interval;
369 pdp_new[i]= atof(updvals[i+1]);
370 rate = pdp_new[i] / interval;
373 pdp_new[i] = atof(updvals[i+1]) * interval;
374 rate = pdp_new[i] / interval;
377 rrd_set_error("rrd contains unknown DS type : '%s'",
381 /* break out of this for loop if the error string is set */
382 if (rrd_test_error()){
385 /* make sure pdp_temp is neither too large or too small
386 * if any of these occur it becomes unknown ...
388 if ( ! isnan(rate) &&
389 (( ! isnan(rrd.ds_def[i].par[DS_max_val].u_val) &&
390 rate > rrd.ds_def[i].par[DS_max_val].u_val ) ||
391 ( ! isnan(rrd.ds_def[i].par[DS_min_val].u_val) &&
392 rate < rrd.ds_def[i].par[DS_min_val].u_val ))){
396 /* no news is news all the same */
400 /* make a copy of the command line argument for the next run */
408 rrd.pdp_prep[i].last_ds,
409 updvals[i+1], pdp_new[i]);
411 if(dst_idx == DST_COUNTER || dst_idx == DST_DERIVE){
412 strncpy(rrd.pdp_prep[i].last_ds,
413 updvals[i+1],LAST_DS_LEN-1);
414 rrd.pdp_prep[i].last_ds[LAST_DS_LEN-1]='\0';
417 /* break out of the argument parsing loop if the error_string is set */
418 if (rrd_test_error()){
422 /* has a pdp_st moment occurred since the last run ? */
424 if (proc_pdp_st == occu_pdp_st){
425 /* no we have not passed a pdp_st moment. therefore update is simple */
427 for(i=0;i<rrd.stat_head->ds_cnt;i++){
428 if(isnan(pdp_new[i]))
429 rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += interval;
431 rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i];
438 rrd.pdp_prep[i].scratch[PDP_val].u_val,
439 rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
443 /* an pdp_st has occurred. */
445 /* in pdp_prep[].scratch[PDP_val].u_val we have collected rate*seconds which
446 * occurred up to the last run.
447 pdp_new[] contains rate*seconds from the latest run.
448 pdp_temp[] will contain the rate for cdp */
451 for(i=0;i<rrd.stat_head->ds_cnt;i++){
452 /* update pdp_prep to the current pdp_st */
453 if(isnan(pdp_new[i]))
454 rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += pre_int;
456 rrd.pdp_prep[i].scratch[PDP_val].u_val +=
457 pdp_new[i]/(double)interval*(double)pre_int;
459 /* if too much of the pdp_prep is unknown we dump it */
460 if ((rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt
461 > rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt) ||
462 (occu_pdp_st-proc_pdp_st <=
463 rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt)) {
466 pdp_temp[i] = rrd.pdp_prep[i].scratch[PDP_val].u_val
467 / (double)( occu_pdp_st
469 - rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
471 /* make pdp_prep ready for the next run */
472 if(isnan(pdp_new[i])){
473 rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = post_int;
474 rrd.pdp_prep[i].scratch[PDP_val].u_val = 0.0;
476 rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = 0;
477 rrd.pdp_prep[i].scratch[PDP_val].u_val =
478 pdp_new[i]/(double)interval*(double)post_int;
486 "new_unkn_sec %5lu\n",
488 rrd.pdp_prep[i].scratch[PDP_val].u_val,
489 rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
494 /* now we have to integrate this data into the cdp_prep areas */
495 /* going through the round robin archives */
497 i < rrd.stat_head->rra_cnt;
499 enum cf_en current_cf = cf_conv(rrd.rra_def[i].cf_nam);
500 /* going through all pdp_st moments which have occurred
501 * since the last run */
502 for(pdp_st = proc_pdp_st+rrd.stat_head->pdp_step;
503 pdp_st <= occu_pdp_st;
504 pdp_st += rrd.stat_head->pdp_step){
507 fprintf(stderr,"RRA %lu STEP %lu\n",i,pdp_st);
511 (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step)) == 0){
513 /* later on the cdp_prep values will be transferred to
514 * the rra. we want to be in the right place. */
515 rrd.rra_ptr[i].cur_row++;
516 if (rrd.rra_ptr[i].cur_row >= rrd.rra_def[i].row_cnt)
517 /* oops ... we have to wrap the beast ... */
518 rrd.rra_ptr[i].cur_row=0;
520 fprintf(stderr," -- RRA Preseek %ld\n",ftell(rrd_file));
522 /* determine if a seek is even needed. */
523 rra_pos_tmp = rra_start +
524 rrd.stat_head->ds_cnt*rrd.rra_ptr[i].cur_row*sizeof(rrd_value_t);
525 if(rra_pos_tmp != rra_current) {
526 if(fseek(rrd_file, rra_pos_tmp, SEEK_SET) != 0){
527 rrd_set_error("seek error in rrd");
530 rra_current = rra_pos_tmp;
533 fprintf(stderr," -- RRA Postseek %ld\n",ftell(rrd_file));
538 ii < rrd.stat_head->ds_cnt;
540 iii=i*rrd.stat_head->ds_cnt+ii;
542 /* the contents of cdp_prep[].scratch[CDP_val].u_val depends
543 * on the consolidation function ! */
545 if (isnan(pdp_temp[ii])){ /* pdp is unknown */
546 rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt++;
548 fprintf(stderr," ** UNKNOWN ADD %lu\n",
549 rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
552 if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val)){
553 /* cdp_prep is unknown when it does not
554 * yet contain data. It can not be zero for
555 * things like mim and max consolidation
558 fprintf(stderr," ** INIT CDP %e\n", pdp_temp[ii]);
560 rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
565 rrd.cdp_prep[iii].scratch[CDP_val].u_val+=pdp_temp[ii];
567 fprintf(stderr," ** AVERAGE %e\n",
568 rrd.cdp_prep[iii].scratch[CDP_val].u_val);
572 if (pdp_temp[ii] < rrd.cdp_prep[iii].scratch[CDP_val].u_val)
573 rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
575 fprintf(stderr," ** MINIMUM %e\n",
576 rrd.cdp_prep[iii].scratch[CDP_val].u_val);
580 if (pdp_temp[ii] > rrd.cdp_prep[iii].scratch[CDP_val].u_val)
581 rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
583 fprintf(stderr," ** MAXIMUM %e\n",
584 rrd.cdp_prep[iii].scratch[CDP_val].u_val);
588 rrd.cdp_prep[iii].scratch[CDP_val].u_val=pdp_temp[ii];
590 fprintf(stderr," ** LAST %e\n",
591 rrd.cdp_prep[iii].scratch[CDP_val].u_val);
595 rrd_set_error("Unknown cf %s",
596 rrd.rra_def[i].cf_nam);
603 /* is the data in the cdp_prep ready to go into
606 (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step)) == 0){
608 /* prepare cdp_pref for its transition to the rra. */
609 if (rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt
610 > rrd.rra_def[i].pdp_cnt*
611 rrd.rra_def[i].par[RRA_cdp_xff_val].u_val)
612 /* to much of the cdp_prep is unknown ... */
613 rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
614 else if (current_cf == CF_AVERAGE){
615 /* for a real average we have to divide
616 * the sum we built earlier on. While ignoring
617 * the unknown pdps */
618 rrd.cdp_prep[iii].scratch[CDP_val].u_val
619 /= (rrd.rra_def[i].pdp_cnt
620 -rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
622 /* we can write straight away, because we are
623 * already in the right place ... */
626 fprintf(stderr," -- RRA WRITE VALUE %e, at %ld\n",
627 rrd.cdp_prep[iii].scratch[CDP_val].u_val,ftell(rrd_file));
630 if(fwrite(&(rrd.cdp_prep[iii].scratch[CDP_val].u_val),
631 sizeof(rrd_value_t),1,rrd_file) != 1){
632 rrd_set_error("writing rrd");
635 rra_current += sizeof(rrd_value_t);
639 fprintf(stderr," -- RRA WROTE new at %ld\n",ftell(rrd_file));
642 /* make cdp_prep ready for the next run */
643 rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
644 rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 0;
647 /* break out of this loop if error_string has been set */
648 if (rrd_test_error())
651 /* break out of this loop if error_string has been set */
652 if (rrd_test_error())
654 /* to be able to position correctly in the next rra w move
655 * the rra_start pointer on to the next rra */
656 rra_start += rrd.rra_def[i].row_cnt
657 *rrd.stat_head->ds_cnt*sizeof(rrd_value_t);
660 /* break out of the argument parsing loop if error_string is set */
661 if (rrd_test_error()){
666 rrd.live_head->last_up = current_time;
671 /* if we got here and if there is an error and if the file has not been
672 * written to, then close things up and return. */
673 if (rrd_test_error()) {
683 /* aargh ... that was tough ... so many loops ... anyway, its done.
684 * we just need to write back the live header portion now*/
686 if (fseek(rrd_file, (sizeof(stat_head_t)
687 + sizeof(ds_def_t)*rrd.stat_head->ds_cnt
688 + sizeof(rra_def_t)*rrd.stat_head->rra_cnt),
690 rrd_set_error("seek rrd for live header writeback");
700 if(fwrite( rrd.live_head,
701 sizeof(live_head_t), 1, rrd_file) != 1){
702 rrd_set_error("fwrite live_head to rrd");
712 if(fwrite( rrd.pdp_prep,
714 rrd.stat_head->ds_cnt, rrd_file) != rrd.stat_head->ds_cnt){
715 rrd_set_error("ftwrite pdp_prep to rrd");
725 if(fwrite( rrd.cdp_prep,
727 rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt, rrd_file)
728 != rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt){
730 rrd_set_error("ftwrite cdp_prep to rrd");
740 if(fwrite( rrd.rra_ptr,
742 rrd.stat_head->rra_cnt,rrd_file) != rrd.stat_head->rra_cnt){
743 rrd_set_error("fwrite rra_ptr to rrd");
753 /* OK now close the files and free the memory */
754 if(fclose(rrd_file) != 0){
755 rrd_set_error("closing rrd");
773 * get exclusive lock to whole file.
774 * lock gets removed when we close the file
776 * returns 0 on success
779 LockRRD(FILE *rrdfile)
781 int rrd_fd; /* File descriptor for RRD */
784 rrd_fd = fileno(rrdfile);
789 lock.l_type = F_WRLCK; /* exclusive write lock */
790 lock.l_len = 0; /* whole file */
791 lock.l_start = 0; /* start of file */
792 lock.l_whence = SEEK_SET; /* end of file */
794 stat = fcntl(rrd_fd, F_SETLK, &lock);
798 if ( _fstat( rrd_fd, &st ) == 0 ) {
799 stat = _locking ( rrd_fd, _LK_NBLCK, st.st_size );