1
/*****************************************************************************
2
* RRDtool 1.0.49 Copyright Tobias Oetiker, 1997 - 2000
3
*****************************************************************************
4
* rrd_update.c RRD Update Function
5
*****************************************************************************
6
* $Id: rrd_update.c,v 1.4 2004/09/06 10:20:05 deri Exp $
7
* $Log: rrd_update.c,v $
8
* Revision 1.4 2004/09/06 10:20:05 deri
9
* Updated to RRDtool 1.0.49
11
* Revision 1.1.1.1 2002/02/26 10:21:38 oetiker
14
*****************************************************************************/
17
#include <sys/types.h>
21
#include <sys/locking.h>
28
int LockRRD(FILE *rrd_file);
35
main(int argc, char **argv){
36
rrd_update(argc,argv);
37
if (rrd_test_error()) {
38
printf("RRDtool 1.0.49 Copyright 1997-2000 by Tobias Oetiker <tobi@oetiker.ch>\n\n"
39
"Usage: rrdupdate filename\n"
40
"\t\t\t[--template|-t ds-name:ds-name:...]\n"
41
"\t\t\ttime|N:value[:value...]\n\n"
42
"\t\t\t[ time:value[:value...] ..]\n\n");
44
printf("ERROR: %s\n",rrd_get_error());
53
rrd_update(int argc, char **argv)
59
unsigned long rra_begin; /* byte pointer to the rra
60
* area in the rrd file. this
61
* pointer never changes value */
62
unsigned long rra_start; /* byte pointer to the rra
63
* area in the rrd file. this
64
* pointer changes as each rrd is
66
unsigned long rra_current; /* byte pointer to the current write
67
* spot in the rrd file. */
68
unsigned long rra_pos_tmp; /* temporary byte pointer. */
69
unsigned long interval,
70
pre_int,post_int; /* interval between this and
72
unsigned long proc_pdp_st; /* which pdp_st was the last
74
unsigned long occu_pdp_st; /* when was the pdp_st
75
* before the last update
77
unsigned long proc_pdp_age; /* how old was the data in
78
* the pdp prep area when it
80
unsigned long occu_pdp_age; /* how long ago was the last
82
unsigned long pdp_st; /* helper for cdp_prep
84
rrd_value_t *pdp_new; /* prepare the incoming data
87
rrd_value_t *pdp_temp; /* prepare the pdp values
91
long *tmpl_idx; /* index representing the settings
92
transported by the template index */
93
long tmpl_cnt = 2; /* time and data */
97
time_t current_time = time(NULL);
99
int wrote_to_file = 0;
100
char *template = NULL;
101
char *endptr; /* used in the conversion */
105
static struct option long_options[] =
107
{"template", required_argument, 0, 't'},
110
int option_index = 0;
112
opt = getopt_long(argc, argv, "t:",
113
long_options, &option_index);
124
rrd_set_error("unknown option '%s'",argv[optind-1]);
129
/* need at least 2 arguments: filename, data. */
130
if (argc-optind < 2) {
131
rrd_set_error("Not enough arguments");
135
if(rrd_open(argv[optind],&rrd_file,&rrd, RRD_READWRITE)==-1){
138
rra_current = rra_start = rra_begin = ftell(rrd_file);
139
/* This is defined in the ANSI C standard, section 7.9.5.3:
141
When a file is opened with udpate mode ('+' as the second
142
or third character in the ... list of mode argument
143
variables), both input and output may be performed on the
144
associated stream. However, ... input may not be directly
145
followed by output without an intervening call to a file
146
positioning function, unless the input operation encounters
148
fseek(rrd_file, 0, SEEK_CUR);
151
/* get exclusive lock to whole file.
152
* lock gets removed when we close the file.
154
if (LockRRD(rrd_file) != 0) {
155
rrd_set_error("could not lock RRD");
161
if((updvals = malloc( sizeof(char*) * (rrd.stat_head->ds_cnt+1)))==NULL){
162
rrd_set_error("allocating updvals pointer array");
168
if ((pdp_temp = malloc(sizeof(rrd_value_t)
169
*rrd.stat_head->ds_cnt))==NULL){
170
rrd_set_error("allocating pdp_temp ...");
177
if ((tmpl_idx = malloc(sizeof(unsigned long)
178
*(rrd.stat_head->ds_cnt+1)))==NULL){
179
rrd_set_error("allocating tmpl_idx ...");
186
/* initialize template redirector */
188
tmpl_idx[0] -> 0; (time)
189
tmpl_idx[1] -> 1; (DS 0)
190
tmpl_idx[2] -> 2; (DS 1)
191
tmpl_idx[3] -> 3; (DS 2)
193
for (i=0;i<=rrd.stat_head->ds_cnt;i++) tmpl_idx[i]=i;
194
tmpl_cnt=rrd.stat_head->ds_cnt+1;
199
tmpl_cnt = 1; /* the first entry is the time */
200
tmpl_len = strlen(template);
201
for(i=0;i<=tmpl_len ;i++) {
202
if (template[i] == ':' || template[i] == '\0') {
204
if (tmpl_cnt>rrd.stat_head->ds_cnt){
205
rrd_set_error("Template contains more DS definitions than RRD");
206
free(updvals); free(pdp_temp);
207
free(tmpl_idx); rrd_free(&rrd);
208
fclose(rrd_file); return(-1);
210
if ((tmpl_idx[tmpl_cnt++] = ds_match(&rrd,dsname)) == -1){
211
rrd_set_error("unknown DS name '%s'",dsname);
212
free(updvals); free(pdp_temp);
213
free(tmpl_idx); rrd_free(&rrd);
214
fclose(rrd_file); return(-1);
216
/* the first element is always the time */
217
tmpl_idx[tmpl_cnt-1]++;
218
/* go to the next entry on the template */
219
dsname = &template[i+1];
220
/* fix the damage we did before */
229
if ((pdp_new = malloc(sizeof(rrd_value_t)
230
*rrd.stat_head->ds_cnt))==NULL){
231
rrd_set_error("allocating pdp_new ...");
240
/* loop through the arguments. */
241
for(arg_i=optind+1; arg_i<argc;arg_i++) {
242
char *stepper = malloc((strlen(argv[arg_i])+1)*sizeof(char));
243
char *step_start = stepper;
244
if (stepper == NULL){
245
rrd_set_error("failed duplication argv entry");
253
/* initialize all ds input to unknown except the first one
254
which has always got to be set */
255
for(ii=1;ii<=rrd.stat_head->ds_cnt;ii++) updvals[ii] = "U";
257
strcpy(stepper,argv[arg_i]);
260
if (*stepper == ':') {
264
updvals[tmpl_idx[ii]] = stepper+1;
270
if (ii != tmpl_cnt-1) {
271
rrd_set_error("expected %lu data source readings (got %lu) from %s:...",
272
tmpl_cnt-1, ii, argv[arg_i]);
277
/* get the time from the reading ... handle N */
278
if (strcmp(updvals[0],"N")==0){
279
current_time = time(NULL);
282
current_time = strtol(updvals[0],&endptr,10);
284
rrd_set_error("converting '%s' to long: %s",updvals[0],strerror(errno));
288
if (endptr[0] != '\0'){
289
rrd_set_error("conversion of '%s' to long not complete: tail '%s'",updvals[0],endptr);
295
if(current_time <= rrd.live_head->last_up){
296
rrd_set_error("illegal attempt to update using time %ld when "
297
"last update time is %ld (minimum one second step)",
298
current_time, rrd.live_head->last_up);
304
/* seek to the beginning of the rrd's */
305
if (rra_current != rra_begin) {
306
if(fseek(rrd_file, rra_begin, SEEK_SET) != 0) {
307
rrd_set_error("seek error in rrd");
311
rra_current = rra_begin;
313
rra_start = rra_begin;
315
/* when was the current pdp started */
316
proc_pdp_age = rrd.live_head->last_up % rrd.stat_head->pdp_step;
317
proc_pdp_st = rrd.live_head->last_up - proc_pdp_age;
319
/* when did the last pdp_st occur */
320
occu_pdp_age = current_time % rrd.stat_head->pdp_step;
321
occu_pdp_st = current_time - occu_pdp_age;
322
interval = current_time - rrd.live_head->last_up;
324
if (occu_pdp_st > proc_pdp_st){
325
/* OK we passed the pdp_st moment*/
326
pre_int = occu_pdp_st - rrd.live_head->last_up; /* how much of the input data
327
* occurred before the latest
329
post_int = occu_pdp_age; /* how much after it */
343
"post_int %lu\n", proc_pdp_age, proc_pdp_st,
344
occu_pdp_age, occu_pdp_st,
345
interval, pre_int, post_int);
348
/* process the data sources and update the pdp_prep
349
* area accordingly */
350
for(i=0;i<rrd.stat_head->ds_cnt;i++){
352
dst_idx= dst_conv(rrd.ds_def[i].dst);
353
if((updvals[i+1][0] != 'U') &&
354
rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt >= interval) {
356
/* the data source type defines how to process the data */
357
/* pdp_temp contains rate * time ... e.g. the bytes
358
* transferred during the interval. Doing it this way saves
359
* a lot of math operations */
365
if(rrd.pdp_prep[i].last_ds[0] != 'U'){
366
for(ii=0;updvals[i+1][ii] != '\0';ii++){
367
if(updvals[i+1][ii] < '0' || updvals[i+1][ii] > '9' || (ii==0 && updvals[i+1][ii] == '-')){
368
rrd_set_error("not a simple integer: '%s'",updvals[i+1]);
372
if (rrd_test_error()){
375
pdp_new[i]= rrd_diff(updvals[i+1],rrd.pdp_prep[i].last_ds);
376
if(dst_idx == DST_COUNTER) {
377
/* simple overflow catcher suggested by andres Kroonmaa */
378
/* this will fail terribly for non 32 or 64 bit counters ... */
379
/* are there any others in SNMP land ? */
380
if (pdp_new[i] < (double)0.0 )
381
pdp_new[i] += (double)4294967296.0 ; /* 2^32 */
382
if (pdp_new[i] < (double)0.0 )
383
pdp_new[i] += (double)18446744069414584320.0; /* 2^64-2^32 */;
385
rate = pdp_new[i] / interval;
393
pdp_new[i] = strtod(updvals[i+1],&endptr);
395
rrd_set_error("converting '%s' to float: %s",updvals[i+1],strerror(errno));
398
if (endptr[0] != '\0'){
399
rrd_set_error("conversion of '%s' to float not complete: tail '%s'",updvals[i+1],endptr);
402
rate = pdp_new[i] / interval;
406
pdp_new[i] = strtod(updvals[i+1],&endptr) * interval;
408
rrd_set_error("converting '%s' to float: %s",updvals[i+1],strerror(errno));
411
if (endptr[0] != '\0'){
412
rrd_set_error("conversion of '%s' to float not complete: tail '%s'",updvals[i+1],endptr);
415
rate = pdp_new[i] / interval;
418
rrd_set_error("rrd contains unknown DS type : '%s'",
422
/* break out of this for loop if the error string is set */
423
if (rrd_test_error()){
426
/* make sure pdp_temp is neither too large or too small
427
* if any of these occur it becomes unknown ...
429
if ( ! isnan(rate) &&
430
(( ! isnan(rrd.ds_def[i].par[DS_max_val].u_val) &&
431
rate > rrd.ds_def[i].par[DS_max_val].u_val ) ||
432
( ! isnan(rrd.ds_def[i].par[DS_min_val].u_val) &&
433
rate < rrd.ds_def[i].par[DS_min_val].u_val ))){
437
/* no news is news all the same */
441
/* make a copy of the command line argument for the next run */
449
rrd.pdp_prep[i].last_ds,
450
updvals[i+1], pdp_new[i]);
452
if(dst_idx == DST_COUNTER || dst_idx == DST_DERIVE){
453
strncpy(rrd.pdp_prep[i].last_ds,
454
updvals[i+1],LAST_DS_LEN-1);
455
rrd.pdp_prep[i].last_ds[LAST_DS_LEN-1]='\0';
458
/* break out of the argument parsing loop if the error_string is set */
459
if (rrd_test_error()){
463
/* has a pdp_st moment occurred since the last run ? */
465
if (proc_pdp_st == occu_pdp_st){
466
/* no we have not passed a pdp_st moment. therefore update is simple */
468
for(i=0;i<rrd.stat_head->ds_cnt;i++){
469
if(isnan(pdp_new[i]))
470
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += interval;
472
rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i];
479
rrd.pdp_prep[i].scratch[PDP_val].u_val,
480
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
484
/* an pdp_st has occurred. */
486
/* in pdp_prep[].scratch[PDP_val].u_val we have collected rate*seconds which
487
* occurred up to the last run.
488
pdp_new[] contains rate*seconds from the latest run.
489
pdp_temp[] will contain the rate for cdp */
492
for(i=0;i<rrd.stat_head->ds_cnt;i++){
493
/* update pdp_prep to the current pdp_st */
494
if(isnan(pdp_new[i]))
495
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += pre_int;
497
rrd.pdp_prep[i].scratch[PDP_val].u_val +=
498
pdp_new[i]/(double)interval*(double)pre_int;
500
/* if too much of the pdp_prep is unknown we dump it */
501
if ((rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt
502
> rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt) ||
503
(occu_pdp_st-proc_pdp_st <=
504
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt)) {
507
pdp_temp[i] = rrd.pdp_prep[i].scratch[PDP_val].u_val
508
/ (double)( occu_pdp_st
510
- rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
512
/* make pdp_prep ready for the next run */
513
if(isnan(pdp_new[i])){
514
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = post_int;
515
rrd.pdp_prep[i].scratch[PDP_val].u_val = 0.0;
517
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = 0;
518
rrd.pdp_prep[i].scratch[PDP_val].u_val =
519
pdp_new[i]/(double)interval*(double)post_int;
527
"new_unkn_sec %5lu\n",
529
rrd.pdp_prep[i].scratch[PDP_val].u_val,
530
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
535
/* now we have to integrate this data into the cdp_prep areas */
536
/* going through the round robin archives */
538
i < rrd.stat_head->rra_cnt;
540
enum cf_en current_cf = cf_conv(rrd.rra_def[i].cf_nam);
541
/* going through all pdp_st moments which have occurred
542
* since the last run */
543
for(pdp_st = proc_pdp_st+rrd.stat_head->pdp_step;
544
pdp_st <= occu_pdp_st;
545
pdp_st += rrd.stat_head->pdp_step){
548
fprintf(stderr,"RRA %lu STEP %lu\n",i,pdp_st);
552
(rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step)) == 0){
554
/* later on the cdp_prep values will be transferred to
555
* the rra. we want to be in the right place. */
556
rrd.rra_ptr[i].cur_row++;
557
if (rrd.rra_ptr[i].cur_row >= rrd.rra_def[i].row_cnt)
558
/* oops ... we have to wrap the beast ... */
559
rrd.rra_ptr[i].cur_row=0;
561
fprintf(stderr," -- RRA Preseek %ld\n",ftell(rrd_file));
563
/* determine if a seek is even needed. */
564
rra_pos_tmp = rra_start +
565
rrd.stat_head->ds_cnt*rrd.rra_ptr[i].cur_row*sizeof(rrd_value_t);
566
if(rra_pos_tmp != rra_current) {
567
if(fseek(rrd_file, rra_pos_tmp, SEEK_SET) != 0){
568
rrd_set_error("seek error in rrd");
571
rra_current = rra_pos_tmp;
574
fprintf(stderr," -- RRA Postseek %ld\n",ftell(rrd_file));
579
ii < rrd.stat_head->ds_cnt;
581
iii=i*rrd.stat_head->ds_cnt+ii;
583
/* the contents of cdp_prep[].scratch[CDP_val].u_val depends
584
* on the consolidation function ! */
586
if (isnan(pdp_temp[ii])){ /* pdp is unknown */
587
rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt++;
589
fprintf(stderr," ** UNKNOWN ADD %lu\n",
590
rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
593
if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val)){
594
/* cdp_prep is unknown when it does not
595
* yet contain data. It can not be zero for
596
* things like mim and max consolidation
599
fprintf(stderr," ** INIT CDP %e\n", pdp_temp[ii]);
601
rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
606
rrd.cdp_prep[iii].scratch[CDP_val].u_val+=pdp_temp[ii];
608
fprintf(stderr," ** AVERAGE %e\n",
609
rrd.cdp_prep[iii].scratch[CDP_val].u_val);
613
if (pdp_temp[ii] < rrd.cdp_prep[iii].scratch[CDP_val].u_val)
614
rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
616
fprintf(stderr," ** MINIMUM %e\n",
617
rrd.cdp_prep[iii].scratch[CDP_val].u_val);
621
if (pdp_temp[ii] > rrd.cdp_prep[iii].scratch[CDP_val].u_val)
622
rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
624
fprintf(stderr," ** MAXIMUM %e\n",
625
rrd.cdp_prep[iii].scratch[CDP_val].u_val);
629
rrd.cdp_prep[iii].scratch[CDP_val].u_val=pdp_temp[ii];
631
fprintf(stderr," ** LAST %e\n",
632
rrd.cdp_prep[iii].scratch[CDP_val].u_val);
636
rrd_set_error("Unknown cf %s",
637
rrd.rra_def[i].cf_nam);
644
/* is the data in the cdp_prep ready to go into
647
(rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step)) == 0){
649
/* prepare cdp_pref for its transition to the rra. */
650
if (rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt
651
> rrd.rra_def[i].pdp_cnt*
652
rrd.rra_def[i].par[RRA_cdp_xff_val].u_val)
653
/* to much of the cdp_prep is unknown ... */
654
rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
655
else if (current_cf == CF_AVERAGE){
656
/* for a real average we have to divide
657
* the sum we built earlier on. While ignoring
658
* the unknown pdps */
659
rrd.cdp_prep[iii].scratch[CDP_val].u_val
660
/= (rrd.rra_def[i].pdp_cnt
661
-rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
663
/* we can write straight away, because we are
664
* already in the right place ... */
667
fprintf(stderr," -- RRA WRITE VALUE %e, at %ld\n",
668
rrd.cdp_prep[iii].scratch[CDP_val].u_val,ftell(rrd_file));
671
if(fwrite(&(rrd.cdp_prep[iii].scratch[CDP_val].u_val),
672
sizeof(rrd_value_t),1,rrd_file) != 1){
673
rrd_set_error("writing rrd");
676
rra_current += sizeof(rrd_value_t);
680
fprintf(stderr," -- RRA WROTE new at %ld\n",ftell(rrd_file));
683
/* make cdp_prep ready for the next run */
684
rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
685
rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 0;
688
/* break out of this loop if error_string has been set */
689
if (rrd_test_error())
692
/* break out of this loop if error_string has been set */
693
if (rrd_test_error())
695
/* to be able to position correctly in the next rra w move
696
* the rra_start pointer on to the next rra */
697
rra_start += rrd.rra_def[i].row_cnt
698
*rrd.stat_head->ds_cnt*sizeof(rrd_value_t);
701
/* break out of the argument parsing loop if error_string is set */
702
if (rrd_test_error()){
707
rrd.live_head->last_up = current_time;
712
/* if we got here and if there is an error and if the file has not been
713
* written to, then close things up and return. */
714
if (rrd_test_error()) {
724
/* aargh ... that was tough ... so many loops ... anyway, its done.
725
* we just need to write back the live header portion now*/
727
if (fseek(rrd_file, (sizeof(stat_head_t)
728
+ sizeof(ds_def_t)*rrd.stat_head->ds_cnt
729
+ sizeof(rra_def_t)*rrd.stat_head->rra_cnt),
731
rrd_set_error("seek rrd for live header writeback");
741
if(fwrite( rrd.live_head,
742
sizeof(live_head_t), 1, rrd_file) != 1){
743
rrd_set_error("fwrite live_head to rrd");
753
if(fwrite( rrd.pdp_prep,
755
rrd.stat_head->ds_cnt, rrd_file) != rrd.stat_head->ds_cnt){
756
rrd_set_error("ftwrite pdp_prep to rrd");
766
if(fwrite( rrd.cdp_prep,
768
rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt, rrd_file)
769
!= rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt){
771
rrd_set_error("ftwrite cdp_prep to rrd");
781
if(fwrite( rrd.rra_ptr,
783
rrd.stat_head->rra_cnt,rrd_file) != rrd.stat_head->rra_cnt){
784
rrd_set_error("fwrite rra_ptr to rrd");
794
/* OK now close the files and free the memory */
795
if(fclose(rrd_file) != 0){
796
rrd_set_error("closing rrd");
814
* get exclusive lock to whole file.
815
* lock gets removed when we close the file
817
* returns 0 on success
820
LockRRD(FILE *rrdfile)
822
int rrd_fd; /* File descriptor for RRD */
825
rrd_fd = fileno(rrdfile);
830
lock.l_type = F_WRLCK; /* exclusive write lock */
831
lock.l_len = 0; /* whole file */
832
lock.l_start = 0; /* start of file */
833
lock.l_whence = SEEK_SET; /* end of file */
835
stat = fcntl(rrd_fd, F_SETLK, &lock);
839
if ( _fstat( rrd_fd, &st ) == 0 ) {
840
stat = _locking ( rrd_fd, _LK_NBLCK, st.st_size );