1
/*****************************************************************************
2
* RRDtool 1.0.42 Copyright Tobias Oetiker, 1997 - 2000
3
*****************************************************************************
4
* rrd_update.c RRD Update Function
5
*****************************************************************************
6
* $Id: rrd_update.c,v 1.3 2003/06/19 10:06:04 deri Exp $
7
* $Log: rrd_update.c,v $
8
* Revision 1.3 2003/06/19 10:06:04 deri
11
* Revision 1.2 2003/06/18 16:55:24 deri
12
* Added fix until an official patch is released. This fix prevents ntop from
13
* crashing while calling rrd_update(). For more info see my post to the RRD
17
* I have found a problem on rrd_diff(). Basically the function is
18
* accessing invalid memory. This is because the two function parameters
19
* (*a and *b) can be of different length (e.g. a = "1234", b = "13") so
22
* for (x=0; x<m; x++) { ... }
24
* moves the pointers (a1, b1, r1) for m locations (a1--;b1--;r1--;) where
25
* m = max(strlen(a),strlen(b));
27
* In the above example m=4, but b1 cannot be shifted for more than 2
28
* positions (strlen("13")=2), therefore the b1-- statement causes a crash
29
* when b1 is moved for the third time.
31
* I have enclosed a quick hack (see below): fell free to accept it or to
32
* fix it the way you want. Anything is fine as long as the problem is fixed.
36
* Revision 1.1 2003/04/22 17:02:29 deri
39
* Revision 1.1.1.1 2002/02/26 10:21:38 oetiker
42
*****************************************************************************/
45
#include <sys/types.h>
49
#include <sys/locking.h>
56
int LockRRD(FILE *rrd_file);
63
main(int argc, char **argv){
64
rrd_update(argc,argv);
65
if (rrd_test_error()) {
66
printf("RRDtool 1.0.42 Copyright 1997-2000 by Tobias Oetiker <tobi@oetiker.ch>\n\n"
67
"Usage: rrdupdate filename\n"
68
"\t\t\t[--template|-t ds-name:ds-name:...]\n"
69
"\t\t\ttime|N:value[:value...]\n\n"
70
"\t\t\t[ time:value[:value...] ..]\n\n");
72
printf("ERROR: %s\n",rrd_get_error());
81
rrd_update(int argc, char **argv)
87
unsigned long rra_begin; /* byte pointer to the rra
88
* area in the rrd file. this
89
* pointer never changes value */
90
unsigned long rra_start; /* byte pointer to the rra
91
* area in the rrd file. this
92
* pointer changes as each rrd is
94
unsigned long rra_current; /* byte pointer to the current write
95
* spot in the rrd file. */
96
unsigned long rra_pos_tmp; /* temporary byte pointer. */
97
unsigned long interval,
98
pre_int,post_int; /* interval between this and
100
unsigned long proc_pdp_st; /* which pdp_st was the last
102
unsigned long occu_pdp_st; /* when was the pdp_st
103
* before the last update
105
unsigned long proc_pdp_age; /* how old was the data in
106
* the pdp prep area when it
107
* was last updated */
108
unsigned long occu_pdp_age; /* how long ago was the last
110
unsigned long pdp_st; /* helper for cdp_prep
112
rrd_value_t *pdp_new; /* prepare the incoming data
113
* to be added the the
115
rrd_value_t *pdp_temp; /* prepare the pdp values
116
* to be added the the
119
long *tmpl_idx; /* index representing the settings
120
transported by the template index */
121
long tmpl_cnt = 2; /* time and data */
125
time_t current_time = time(NULL);
127
int wrote_to_file = 0;
128
char *template = NULL;
132
static struct option long_options[] =
134
{"template", required_argument, 0, 't'},
137
int option_index = 0;
139
opt = getopt_long(argc, argv, "t:",
140
long_options, &option_index);
151
rrd_set_error("unknown option '%s'",argv[optind-1]);
156
/* need at least 2 arguments: filename, data. */
157
if (argc-optind < 2) {
158
rrd_set_error("Not enough arguments");
162
if(rrd_open(argv[optind],&rrd_file,&rrd, RRD_READWRITE)==-1){
165
rra_current = rra_start = rra_begin = ftell(rrd_file);
166
/* This is defined in the ANSI C standard, section 7.9.5.3:
168
When a file is opened with udpate mode ('+' as the second
169
or third character in the ... list of mode argument
170
variables), both input and ouptut may be performed on the
171
associated stream. However, ... input may not be directly
172
followed by output without an intervening call to a file
173
positioning function, unless the input oepration encounters
175
fseek(rrd_file, 0, SEEK_CUR);
178
/* get exclusive lock to whole file.
179
* lock gets removed when we close the file.
181
if (LockRRD(rrd_file) != 0) {
182
rrd_set_error("could not lock RRD");
188
if((updvals = malloc( sizeof(char*) * (rrd.stat_head->ds_cnt+1)))==NULL){
189
rrd_set_error("allocating updvals pointer array");
195
if ((pdp_temp = malloc(sizeof(rrd_value_t)
196
*rrd.stat_head->ds_cnt))==NULL){
197
rrd_set_error("allocating pdp_temp ...");
204
if ((tmpl_idx = malloc(sizeof(unsigned long)
205
*(rrd.stat_head->ds_cnt+1)))==NULL){
206
rrd_set_error("allocating tmpl_idx ...");
213
/* initialize template redirector */
215
tmpl_idx[0] -> 0; (time)
216
tmpl_idx[1] -> 1; (DS 0)
217
tmpl_idx[2] -> 2; (DS 1)
218
tmpl_idx[3] -> 3; (DS 2)
220
for (i=0;i<=rrd.stat_head->ds_cnt;i++) tmpl_idx[i]=i;
221
tmpl_cnt=rrd.stat_head->ds_cnt+1;
226
tmpl_cnt = 1; /* the first entry is the time */
227
tmpl_len = strlen(template);
228
for(i=0;i<=tmpl_len ;i++) {
229
if (template[i] == ':' || template[i] == '\0') {
231
if (tmpl_cnt>rrd.stat_head->ds_cnt){
232
rrd_set_error("Template contains more DS definitions than RRD");
233
free(updvals); free(pdp_temp);
234
free(tmpl_idx); rrd_free(&rrd);
235
fclose(rrd_file); return(-1);
237
if ((tmpl_idx[tmpl_cnt++] = ds_match(&rrd,dsname)) == -1){
238
rrd_set_error("unknown DS name '%s'",dsname);
239
free(updvals); free(pdp_temp);
240
free(tmpl_idx); rrd_free(&rrd);
241
fclose(rrd_file); return(-1);
243
/* the first element is always the time */
244
tmpl_idx[tmpl_cnt-1]++;
245
/* go to the next entry on the template */
246
dsname = &template[i+1];
247
/* fix the damage we did before */
256
if ((pdp_new = malloc(sizeof(rrd_value_t)
257
*rrd.stat_head->ds_cnt))==NULL){
258
rrd_set_error("allocating pdp_new ...");
267
/* loop through the arguments. */
268
for(arg_i=optind+1; arg_i<argc;arg_i++) {
269
char *stepper = malloc((strlen(argv[arg_i])+1)*sizeof(char));
270
char *step_start = stepper;
271
if (stepper == NULL){
272
rrd_set_error("faild duplication argv entry");
280
/* initialize all ds input to unknown except the first one
281
which has always got to be set */
282
for(ii=1;ii<=rrd.stat_head->ds_cnt;ii++) updvals[ii] = "U";
284
strcpy(stepper,argv[arg_i]);
287
if (*stepper == ':') {
291
updvals[tmpl_idx[ii]] = stepper+1;
297
if (ii != tmpl_cnt-1) {
298
rrd_set_error("expected %lu data source readings (got %lu) from %s:...",
299
tmpl_cnt-1, ii, argv[arg_i]);
304
/* get the time from the reading ... handle N */
305
if (strcmp(updvals[0],"N")==0){
306
current_time = time(NULL);
308
current_time = atol(updvals[0]);
311
if(current_time <= rrd.live_head->last_up){
312
rrd_set_error("illegal attempt to update using time %ld when "
313
"last update time is %ld (minimum one second step)",
314
current_time, rrd.live_head->last_up);
320
/* seek to the beginning of the rrd's */
321
if (rra_current != rra_begin) {
322
if(fseek(rrd_file, rra_begin, SEEK_SET) != 0) {
323
rrd_set_error("seek error in rrd");
327
rra_current = rra_begin;
329
rra_start = rra_begin;
331
/* when was the current pdp started */
332
proc_pdp_age = rrd.live_head->last_up % rrd.stat_head->pdp_step;
333
proc_pdp_st = rrd.live_head->last_up - proc_pdp_age;
335
/* when did the last pdp_st occur */
336
occu_pdp_age = current_time % rrd.stat_head->pdp_step;
337
occu_pdp_st = current_time - occu_pdp_age;
338
interval = current_time - rrd.live_head->last_up;
340
if (occu_pdp_st > proc_pdp_st){
341
/* OK we passed the pdp_st moment*/
342
pre_int = occu_pdp_st - rrd.live_head->last_up; /* how much of the input data
343
* occurred before the latest
345
post_int = occu_pdp_age; /* how much after it */
359
"post_int %lu\n", proc_pdp_age, proc_pdp_st,
360
occu_pdp_age, occu_pdp_st,
361
interval, pre_int, post_int);
364
/* process the data sources and update the pdp_prep
365
* area accordingly */
366
for(i=0;i<rrd.stat_head->ds_cnt;i++){
368
dst_idx= dst_conv(rrd.ds_def[i].dst);
369
if((updvals[i+1][0] != 'U') &&
370
rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt >= interval) {
372
/* the data source type defines how to process the data */
373
/* pdp_temp contains rate * time ... eg the bytes
374
* transferred during the interval. Doing it this way saves
375
* a lot of math operations */
381
if(rrd.pdp_prep[i].last_ds[0] != 'U'){
382
pdp_new[i]= rrd_diff(updvals[i+1],rrd.pdp_prep[i].last_ds);
383
if(dst_idx == DST_COUNTER) {
384
/* simple overflow catcher sugestet by andres kroonmaa */
385
/* this will fail terribly for non 32 or 64 bit counters ... */
386
/* are there any others in SNMP land ? */
387
if (pdp_new[i] < (double)0.0 )
388
pdp_new[i] += (double)4294967296.0 ; /* 2^32 */
389
if (pdp_new[i] < (double)0.0 )
390
pdp_new[i] += (double)18446744069414584320.0; /* 2^64-2^32 */;
392
rate = pdp_new[i] / interval;
399
pdp_new[i]= atof(updvals[i+1]);
400
rate = pdp_new[i] / interval;
403
pdp_new[i] = atof(updvals[i+1]) * interval;
404
rate = pdp_new[i] / interval;
407
rrd_set_error("rrd contains unknown DS type : '%s'",
411
/* break out of this for loop if the error string is set */
412
if (rrd_test_error()){
415
/* make sure pdp_temp is neither too large or too small
416
* if any of these occur it becomes unknown ...
418
if ( ! isnan(rate) &&
419
(( ! isnan(rrd.ds_def[i].par[DS_max_val].u_val) &&
420
rate > rrd.ds_def[i].par[DS_max_val].u_val ) ||
421
( ! isnan(rrd.ds_def[i].par[DS_min_val].u_val) &&
422
rate < rrd.ds_def[i].par[DS_min_val].u_val ))){
426
/* no news is news all the same */
430
/* make a copy of the command line argument for the next run */
438
rrd.pdp_prep[i].last_ds,
439
updvals[i+1], pdp_new[i]);
441
if(dst_idx == DST_COUNTER || dst_idx == DST_DERIVE){
442
strncpy(rrd.pdp_prep[i].last_ds,
443
updvals[i+1],LAST_DS_LEN-1);
444
rrd.pdp_prep[i].last_ds[LAST_DS_LEN-1]='\0';
447
/* break out of the argument parsing loop if the error_string is set */
448
if (rrd_test_error()){
452
/* has a pdp_st moment occurred since the last run ? */
454
if (proc_pdp_st == occu_pdp_st){
455
/* no we have not passed a pdp_st moment. therefore update is simple */
457
for(i=0;i<rrd.stat_head->ds_cnt;i++){
458
if(isnan(pdp_new[i]))
459
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += interval;
461
rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i];
468
rrd.pdp_prep[i].scratch[PDP_val].u_val,
469
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
473
/* an pdp_st has occurred. */
475
/* in pdp_prep[].scratch[PDP_val].u_val we have collected rate*seconds which
476
* occurred up to the last run.
477
pdp_new[] contains rate*seconds from the latest run.
478
pdp_temp[] will contain the rate for cdp */
481
for(i=0;i<rrd.stat_head->ds_cnt;i++){
482
/* update pdp_prep to the current pdp_st */
483
if(isnan(pdp_new[i]))
484
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += pre_int;
486
rrd.pdp_prep[i].scratch[PDP_val].u_val +=
487
pdp_new[i]/(double)interval*(double)pre_int;
489
/* if too much of the pdp_prep is unknown we dump it */
490
if ((rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt
491
> rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt) ||
492
(occu_pdp_st-proc_pdp_st <=
493
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt)) {
496
pdp_temp[i] = rrd.pdp_prep[i].scratch[PDP_val].u_val
497
/ (double)( occu_pdp_st
499
- rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
501
/* make pdp_prep ready for the next run */
502
if(isnan(pdp_new[i])){
503
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = post_int;
504
rrd.pdp_prep[i].scratch[PDP_val].u_val = 0.0;
506
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = 0;
507
rrd.pdp_prep[i].scratch[PDP_val].u_val =
508
pdp_new[i]/(double)interval*(double)post_int;
516
"new_unkn_sec %5lu\n",
518
rrd.pdp_prep[i].scratch[PDP_val].u_val,
519
rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
524
/* now we have to integrate this data into the cdp_prep areas */
525
/* going through the round robin archives */
527
i < rrd.stat_head->rra_cnt;
529
enum cf_en current_cf = cf_conv(rrd.rra_def[i].cf_nam);
530
/* going through all pdp_st moments which have occurred
531
* since the last run */
532
for(pdp_st = proc_pdp_st+rrd.stat_head->pdp_step;
533
pdp_st <= occu_pdp_st;
534
pdp_st += rrd.stat_head->pdp_step){
537
fprintf(stderr,"RRA %lu STEP %lu\n",i,pdp_st);
541
(rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step)) == 0){
543
/* later on the cdp_prep values will be transferred to
544
* the rra. we want to be in the right place. */
545
rrd.rra_ptr[i].cur_row++;
546
if (rrd.rra_ptr[i].cur_row >= rrd.rra_def[i].row_cnt)
547
/* oops ... we have to wrap the beast ... */
548
rrd.rra_ptr[i].cur_row=0;
550
fprintf(stderr," -- RRA Preseek %ld\n",ftell(rrd_file));
552
/* determine if a seek is even needed. */
553
rra_pos_tmp = rra_start +
554
rrd.stat_head->ds_cnt*rrd.rra_ptr[i].cur_row*sizeof(rrd_value_t);
555
if(rra_pos_tmp != rra_current) {
556
if(fseek(rrd_file, rra_pos_tmp, SEEK_SET) != 0){
557
rrd_set_error("seek error in rrd");
560
rra_current = rra_pos_tmp;
563
fprintf(stderr," -- RRA Postseek %ld\n",ftell(rrd_file));
568
ii < rrd.stat_head->ds_cnt;
570
iii=i*rrd.stat_head->ds_cnt+ii;
572
/* the contents of cdp_prep[].scratch[CDP_val].u_val depends
573
* on the consolidation function ! */
575
if (isnan(pdp_temp[ii])){ /* pdp is unknown */
576
rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt++;
578
fprintf(stderr," ** UNKNOWN ADD %lu\n",
579
rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
582
if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val)){
583
/* cdp_prep is unknown when it does not
584
* yet contain data. It can not be zero for
585
* things like mim and max consolidation
588
fprintf(stderr," ** INIT CDP %e\n", pdp_temp[ii]);
590
rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
595
rrd.cdp_prep[iii].scratch[CDP_val].u_val+=pdp_temp[ii];
597
fprintf(stderr," ** AVERAGE %e\n",
598
rrd.cdp_prep[iii].scratch[CDP_val].u_val);
602
if (pdp_temp[ii] < rrd.cdp_prep[iii].scratch[CDP_val].u_val)
603
rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
605
fprintf(stderr," ** MINIMUM %e\n",
606
rrd.cdp_prep[iii].scratch[CDP_val].u_val);
610
if (pdp_temp[ii] > rrd.cdp_prep[iii].scratch[CDP_val].u_val)
611
rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
613
fprintf(stderr," ** MAXIMUM %e\n",
614
rrd.cdp_prep[iii].scratch[CDP_val].u_val);
618
rrd.cdp_prep[iii].scratch[CDP_val].u_val=pdp_temp[ii];
620
fprintf(stderr," ** LAST %e\n",
621
rrd.cdp_prep[iii].scratch[CDP_val].u_val);
625
rrd_set_error("Unknown cf %s",
626
rrd.rra_def[i].cf_nam);
633
/* is the data in the cdp_prep ready to go into
636
(rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step)) == 0){
638
/* prepare cdp_pref for its transition to the rra. */
639
if (rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt
640
> rrd.rra_def[i].pdp_cnt*
641
rrd.rra_def[i].par[RRA_cdp_xff_val].u_val)
642
/* to much of the cdp_prep is unknown ... */
643
rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
644
else if (current_cf == CF_AVERAGE){
645
/* for a real average we have to divide
646
* the sum we built earlier on. While ignoring
647
* the unknown pdps */
648
rrd.cdp_prep[iii].scratch[CDP_val].u_val
649
/= (rrd.rra_def[i].pdp_cnt
650
-rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
652
/* we can write straight away, because we are
653
* already in the right place ... */
656
fprintf(stderr," -- RRA WRITE VALUE %e, at %ld\n",
657
rrd.cdp_prep[iii].scratch[CDP_val].u_val,ftell(rrd_file));
660
if(fwrite(&(rrd.cdp_prep[iii].scratch[CDP_val].u_val),
661
sizeof(rrd_value_t),1,rrd_file) != 1){
662
rrd_set_error("writing rrd");
665
rra_current += sizeof(rrd_value_t);
669
fprintf(stderr," -- RRA WROTE new at %ld\n",ftell(rrd_file));
672
/* make cdp_prep ready for the next run */
673
rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
674
rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 0;
677
/* break out of this loop if error_string has been set */
678
if (rrd_test_error())
681
/* break out of this loop if error_string has been set */
682
if (rrd_test_error())
684
/* to be able to position correctly in the next rra w move
685
* the rra_start pointer on to the next rra */
686
rra_start += rrd.rra_def[i].row_cnt
687
*rrd.stat_head->ds_cnt*sizeof(rrd_value_t);
690
/* break out of the argument parsing loop if error_string is set */
691
if (rrd_test_error()){
696
rrd.live_head->last_up = current_time;
701
/* if we got here and if there is an error and if the file has not been
702
* written to, then close things up and return. */
703
if (rrd_test_error()) {
713
/* aargh ... that was tough ... so many loops ... anyway, its done.
714
* we just need to write back the live header portion now*/
716
if (fseek(rrd_file, (sizeof(stat_head_t)
717
+ sizeof(ds_def_t)*rrd.stat_head->ds_cnt
718
+ sizeof(rra_def_t)*rrd.stat_head->rra_cnt),
720
rrd_set_error("seek rrd for live header writeback");
730
if(fwrite( rrd.live_head,
731
sizeof(live_head_t), 1, rrd_file) != 1){
732
rrd_set_error("fwrite live_head to rrd");
742
if(fwrite( rrd.pdp_prep,
744
rrd.stat_head->ds_cnt, rrd_file) != rrd.stat_head->ds_cnt){
745
rrd_set_error("ftwrite pdp_prep to rrd");
755
if(fwrite( rrd.cdp_prep,
757
rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt, rrd_file)
758
!= rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt){
760
rrd_set_error("ftwrite cdp_prep to rrd");
770
if(fwrite( rrd.rra_ptr,
772
rrd.stat_head->rra_cnt,rrd_file) != rrd.stat_head->rra_cnt){
773
rrd_set_error("fwrite rra_ptr to rrd");
783
/* OK now close the files and free the memory */
784
if(fclose(rrd_file) != 0){
785
rrd_set_error("closing rrd");
803
* get exclusive lock to whole file.
804
* lock gets removed when we close the file
806
* returns 0 on success
809
LockRRD(FILE *rrdfile)
811
int rrd_fd; /* File descriptor for RRD */
814
rrd_fd = fileno(rrdfile);
819
lock.l_type = F_WRLCK; /* exclusive write lock */
820
lock.l_len = 0; /* whole file */
821
lock.l_start = 0; /* start of file */
822
lock.l_whence = SEEK_SET; /* end of file */
824
stat = fcntl(rrd_fd, F_SETLK, &lock);
828
if ( _fstat( rrd_fd, &st ) == 0 ) {
829
stat = _locking ( rrd_fd, _LK_NBLCK, st.st_size );