fixed version checks to only complain if xml version is > than current RRD version
[rrdtool.git] / src / rrd_update.c
index 08ecb49..6ba9fdb 100644 (file)
@@ -1,10 +1,45 @@
 /*****************************************************************************
- * RRDtool 1.0.33  Copyright Tobias Oetiker, 1997 - 2000
+ * RRDtool 1.1.x  Copyright Tobias Oetiker, 1997 - 2002
  *****************************************************************************
  * rrd_update.c  RRD Update Function
  *****************************************************************************
  * $Id$
  * $Log$
+ * Revision 1.8  2003/03/31 21:22:12  oetiker
+ * enables RRDtool updates with microsecond or in case of windows millisecond
+ * precision. This is needed to reduce time measurement error when archive step
+ * is small. (<30s) --  Sasha Mikheev <sasha@avalon-net.co.il>
+ *
+ * Revision 1.7  2003/02/13 07:05:27  oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented.  librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.6  2002/02/01 20:34:49  oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.5  2001/05/09 05:31:01  oetiker
+ * Bug fix: when update of multiple PDP/CDP RRAs coincided
+ * with interpolation of multiple PDPs an incorrect value was
+ * stored as the CDP. Especially evident for GAUGE data sources.
+ * Minor changes to rrdcreate.pod. -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.4  2001/03/10 23:54:41  oetiker
+ * Support for COMPUTE data sources (CDEF data sources). Removes the RPN
+ * parser and calculator from rrd_graph and puts then in a new file,
+ * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some
+ * clean-up of aberrant behavior stuff, including a bug fix.
+ * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format.
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
  * Revision 1.3  2001/03/04 13:01:55  oetiker
  * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
  * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
  #include <io.h>
 #endif
 
-/* Prototypes */
+#include "rrd_hw.h"
+#include "rrd_rpncalc.h"
+
+#include "rrd_is_thread_safe.h"
+
+#ifdef WIN32
+/*
+ * WIN32 does not have gettimeofday    and struct timeval. This is a quick and dirty
+ * replacement.
+ */
+#include <sys/timeb.h>
+
+struct timeval {
+       time_t tv_sec; /* seconds */
+       long tv_usec;  /* microseconds */
+};
+
+struct __timezone {
+       int  tz_minuteswest; /* minutes W of Greenwich */
+       int  tz_dsttime;     /* type of dst correction */
+};
+
+static gettimeofday(struct timeval *t, struct __timezone *tz) {
+       
+       struct timeb current_time;
+
+       _ftime(&current_time);
+       
+       t->tv_sec  = current_time.time;
+       t->tv_usec = current_time.millitm * 1000;
+}
+
+#endif
+/*
+ * normilize time as returned by gettimeofday. usec part must
+ * be always >= 0
+ */
+static void normalize_time(struct timeval *t)
+{
+       if(t->tv_usec < 0) {
+               t->tv_sec--;
+               t->tv_usec += 1000000L;
+       }
+}
+
+/* Local prototypes */
 int LockRRD(FILE *rrd_file);
-void write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
-    unsigned short CDP_scratch_idx, FILE *rrd_file);
-/*#define DEBUG */
+void write_RRA_row (rrd_t *rrd, unsigned long rra_idx, 
+                                       unsigned long *rra_current,
+                                       unsigned short CDP_scratch_idx, FILE *rrd_file);
+int rrd_update_r(char *filename, char *template, int argc, char **argv);
 
 #define IFDNAN(X,Y) (isnan(X) ? (Y) : (X));
 
@@ -46,7 +126,7 @@ int
 main(int argc, char **argv){
         rrd_update(argc,argv);
         if (rrd_test_error()) {
-                printf("RRDtool 1.0.33  Copyright 1997-2000 by Tobias Oetiker <tobi@oetiker.ch>\n\n"
+                printf("RRDtool 1.1.x  Copyright 1997-2000 by Tobias Oetiker <tobi@oetiker.ch>\n\n"
                         "Usage: rrdupdate filename\n"
                         "\t\t\t[--template|-t ds-name:ds-name:...]\n"
                         "\t\t\ttime|N:value[:value...]\n\n"
@@ -64,10 +144,53 @@ main(int argc, char **argv){
 int
 rrd_update(int argc, char **argv)
 {
+    char             *template = NULL;          
+    int rc;
+
+    while (1) {
+               static struct option long_options[] =
+                       {
+                               {"template",      required_argument, 0, 't'},
+                               {0,0,0,0}
+                       };
+               int option_index = 0;
+               int opt;
+               opt = getopt_long(argc, argv, "t:", 
+                                                 long_options, &option_index);
+               
+               if (opt == EOF)
+                       break;
+               
+               switch(opt) {
+               case 't':
+                       template = optarg;
+                       break;
+                       
+               case '?':
+                       rrd_set_error("unknown option '%s'",argv[optind-1]);
+                       return(-1);
+               }
+    }
+
+    /* need at least 2 arguments: filename, data. */
+    if (argc-optind < 2) {
+               rrd_set_error("Not enough arguments");
+
+               return -1;
+    }
+
+    rc = rrd_update_r(argv[optind], template,
+                     argc - optind - 1, argv + optind + 1);
+    return rc;
+}
+
+int
+rrd_update_r(char *filename, char *template, int argc, char **argv)
+{
 
     int              arg_i = 2;
     short            j;
-    long             i,ii,iii=1;
+    unsigned long    i,ii,iii=1;
 
     unsigned long    rra_begin;          /* byte pointer to the rra
                                          * area in the rrd file.  this
@@ -79,7 +202,7 @@ rrd_update(int argc, char **argv)
     unsigned long    rra_current;        /* byte pointer to the current write
                                          * spot in the rrd file. */
     unsigned long    rra_pos_tmp;        /* temporary byte pointer. */
-    unsigned long    interval,
+    double           interval,
        pre_int,post_int;                /* interval between this and
                                          * the last run */
     unsigned long    proc_pdp_st;        /* which pdp_st was the last
@@ -101,14 +224,16 @@ rrd_update(int argc, char **argv)
 
     long             *tmpl_idx;          /* index representing the settings
                                            transported by the template index */
-    long             tmpl_cnt = 2;       /* time and data */
+    unsigned long    tmpl_cnt = 2;       /* time and data */
 
     FILE             *rrd_file;
     rrd_t            rrd;
-    time_t           current_time = time(NULL);
+    time_t           current_time;
+    unsigned long    current_time_usec;  /* microseconds part of current time */
+    struct timeval   tmp_time;           /* used for time conversion */
+
     char             **updvals;
     int              schedule_smooth = 0;
-    char             *template = NULL;   
        rrd_value_t      *seasonal_coef = NULL, *last_seasonal_coef = NULL;
                                         /* a vector of future Holt-Winters seasonal coefs */
     unsigned long    elapsed_pdp_st;
@@ -124,42 +249,34 @@ rrd_update(int argc, char **argv)
                                         /* index into the CDP scratch array */
     enum cf_en       current_cf;
                                         /* numeric id of the current consolidation function */
+    rpnstack_t       rpnstack; /* used for COMPUTE DS */
+    int                     version;  /* rrd version */
 
-    while (1) {
-       static struct option long_options[] =
-       {
-           {"template",      required_argument, 0, 't'},
-           {0,0,0,0}
-       };
-       int option_index = 0;
-       int opt;
-       opt = getopt_long(argc, argv, "t:", 
-                         long_options, &option_index);
-       
-       if (opt == EOF)
-         break;
-       
-       switch(opt) {
-       case 't':
-           template = optarg;
-           break;
-
-       case '?':
-           rrd_set_error("unknown option '%s'",argv[optind-1]);
-            rrd_free(&rrd);            
-           return(-1);
-       }
-    }
+    rpnstack_init(&rpnstack);
 
-    /* need at least 2 arguments: filename, data. */
-    if (argc-optind < 2) {
+    /* need at least 1 arguments: data. */
+    if (argc < 1) {
        rrd_set_error("Not enough arguments");
        return -1;
     }
+    
+    
 
-    if(rrd_open(argv[optind],&rrd_file,&rrd, RRD_READWRITE)==-1){
+    if(rrd_open(filename,&rrd_file,&rrd, RRD_READWRITE)==-1){
        return -1;
     }
+    /* initialize time */
+    version = atoi(rrd.stat_head->version);
+    gettimeofday(&tmp_time, 0);
+    normalize_time(&tmp_time);
+    current_time = tmp_time.tv_sec;
+    if(version >= 3) {
+        current_time_usec = tmp_time.tv_usec;
+    }
+    else {
+       current_time_usec = 0;
+    }
+
     rra_current = rra_start = rra_begin = ftell(rrd_file);
     /* This is defined in the ANSI C standard, section 7.9.5.3:
 
@@ -209,17 +326,22 @@ rrd_update(int argc, char **argv)
        return(-1);
     }
     /* initialize template redirector */
-    /* default config
+    /* default config example (assume DS 1 is a CDEF DS)
        tmpl_idx[0] -> 0; (time)
        tmpl_idx[1] -> 1; (DS 0)
-       tmpl_idx[2] -> 2; (DS 1)
-       tmpl_idx[3] -> 3; (DS 2)
-       ... */
-    for (i=0;i<=rrd.stat_head->ds_cnt;i++) tmpl_idx[i]=i;
-    tmpl_cnt=rrd.stat_head->ds_cnt+1;
+       tmpl_idx[2] -> 3; (DS 2)
+       tmpl_idx[3] -> 4; (DS 3) */
+    tmpl_idx[0] = 0; /* time */
+    for (i = 1, ii = 1 ; i <= rrd.stat_head->ds_cnt ; i++) 
+       {
+          if (dst_conv(rrd.ds_def[i-1].dst) != DST_CDEF)
+             tmpl_idx[ii++]=i;
+       }
+    tmpl_cnt= ii;
+
     if (template) {
        char *dsname;
-       int tmpl_len;
+       unsigned int tmpl_len;
        dsname = template;
        tmpl_cnt = 1; /* the first entry is the time */
        tmpl_len = strlen(template);
@@ -263,7 +385,7 @@ rrd_update(int argc, char **argv)
     }
 
     /* loop through the arguments. */
-    for(arg_i=optind+1; arg_i<argc;arg_i++) {
+    for(arg_i=0; arg_i<argc;arg_i++) {
        char *stepper = malloc((strlen(argv[arg_i])+1)*sizeof(char));
         char *step_start = stepper;
        char *p;
@@ -330,18 +452,29 @@ rrd_update(int argc, char **argv)
            if (ds_tv.type == RELATIVE_TO_END_TIME ||
                ds_tv.type == RELATIVE_TO_START_TIME) {
                rrd_set_error("specifying time relative to the 'start' "
-                              "or 'end' makes no sense here: %s",
+                             "or 'end' makes no sense here: %s",
                              updvals[0]);
                free(step_start);
                break;
            }
 
            current_time = mktime(&ds_tv.tm) + ds_tv.offset;
+           current_time_usec = 0; /* FIXME: how to handle usecs here ? */
+           
        } else if (strcmp(updvals[0],"N")==0){
-           current_time = time(NULL);
+           gettimeofday(&tmp_time, 0);
+           normalize_time(&tmp_time);
+           current_time = tmp_time.tv_sec;
+           current_time_usec = tmp_time.tv_usec;
        } else {
-           current_time = atol(updvals[0]);
+           double tmp;
+           tmp = strtod(updvals[0], 0);
+           current_time = floor(tmp);
+           current_time_usec = (long)((tmp - current_time) * 1000000L);
        }
+       /* dont do any correction for old version RRDs */
+       if(version < 3) 
+           current_time_usec = 0;
        
        if(current_time <= rrd.live_head->last_up){
            rrd_set_error("illegal attempt to update using time %ld when "
@@ -370,14 +503,17 @@ rrd_update(int argc, char **argv)
        /* when did the last pdp_st occur */
        occu_pdp_age = current_time % rrd.stat_head->pdp_step;
        occu_pdp_st = current_time - occu_pdp_age;
-       interval = current_time - rrd.live_head->last_up;
+       /* interval = current_time - rrd.live_head->last_up; */
+       interval    = current_time + ((double)current_time_usec - (double)rrd.live_head->last_up_usec)/1000000.0 - rrd.live_head->last_up;
     
        if (occu_pdp_st > proc_pdp_st){
            /* OK we passed the pdp_st moment*/
            pre_int =  occu_pdp_st - rrd.live_head->last_up; /* how much of the input data
                                                              * occurred before the latest
                                                              * pdp_st moment*/
+           pre_int -= ((double)rrd.live_head->last_up_usec)/1000000.0; /* adjust usecs */
            post_int = occu_pdp_age;                         /* how much after it */
+           post_int += ((double)current_time_usec)/1000000.0;  /* adjust usecs */
        } else {
            pre_int = interval;
            post_int = 0;
@@ -389,9 +525,9 @@ rrd_update(int argc, char **argv)
               "proc_pdp_st %lu\t" 
               "occu_pfp_age %lu\t" 
               "occu_pdp_st %lu\t"
-              "int %lu\t"
-              "pre_int %lu\t"
-              "post_int %lu\n", proc_pdp_age, proc_pdp_st, 
+              "int %lf\t"
+              "pre_int %lf\t"
+              "post_int %lf\n", proc_pdp_age, proc_pdp_st, 
                occu_pdp_age, occu_pdp_st,
               interval, pre_int, post_int);
 #endif
@@ -401,11 +537,16 @@ rrd_update(int argc, char **argv)
        for(i=0;i<rrd.stat_head->ds_cnt;i++){
            enum dst_en dst_idx;
            dst_idx= dst_conv(rrd.ds_def[i].dst);
+               /* NOTE: DST_CDEF should never enter this if block, because
+                * updvals[i+1][0] is initialized to 'U'; unless the caller
+                * accidently specified a value for the DST_CDEF. To handle 
+                * this case, an extra check is required. */
            if((updvals[i+1][0] != 'U') &&
+                  (dst_idx != DST_CDEF) &&
               rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt >= interval) {
               double rate = DNAN;
               /* the data source type defines how to process the data */
-               /* pdp_temp contains rate * time ... eg the bytes
+               /* pdp_new contains rate * time ... eg the bytes
                 * transferred during the interval. Doing it this way saves
                 * a lot of math operations */
                
@@ -512,7 +653,6 @@ rrd_update(int argc, char **argv)
            pdp_new[] contains rate*seconds from the latest run.
            pdp_temp[] will contain the rate for cdp */
 
-
            for(i=0;i<rrd.stat_head->ds_cnt;i++){
                /* update pdp_prep to the current pdp_st */
                if(isnan(pdp_new[i]))
@@ -533,6 +673,27 @@ rrd_update(int argc, char **argv)
                                   - proc_pdp_st
                                   - rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
                }
+
+               /* process CDEF data sources; remember each CDEF DS can
+                * only reference other DS with a lower index number */
+           if (dst_conv(rrd.ds_def[i].dst) == DST_CDEF) {
+                  rpnp_t *rpnp;
+                  rpnp = rpn_expand((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]));
+                  /* substitue data values for OP_VARIABLE nodes */
+                  for (ii = 0; rpnp[ii].op != OP_END; ii++)
+                  {
+                         if (rpnp[ii].op == OP_VARIABLE) {
+                                rpnp[ii].op = OP_NUMBER;
+                                rpnp[ii].val =  pdp_temp[rpnp[ii].ptr];
+                         }
+                  }
+                  /* run the rpn calculator */
+                  if (rpn_calc(rpnp,&rpnstack,0,pdp_temp,i) == -1) {
+                         free(rpnp);
+                         break; /* exits the data sources pdp_temp loop */
+                  }
+               }
+        
                /* make pdp_prep ready for the next run */
                if(isnan(pdp_new[i])){
                    rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = post_int;
@@ -555,6 +716,12 @@ rrd_update(int argc, char **argv)
 #endif
            }
 
+               /* if there were errors during the last loop, bail out here */
+           if (rrd_test_error()){
+              free(step_start);
+              break;
+           }
+
                /* compute the number of elapsed pdp_st moments */
                elapsed_pdp_st = (occu_pdp_st - proc_pdp_st) / rrd.stat_head -> pdp_step;
 #ifdef DEBUG
@@ -713,7 +880,7 @@ rrd_update(int argc, char **argv)
                                                  cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, 0.0);
                                                  cur_val = IFDNAN(pdp_temp[ii],0.0);
                           rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val =
-                                              (cum_val + cur_val) /
+                                              (cum_val + cur_val * start_pdp_offset) /
                                           (rrd.rra_def[i].pdp_cnt
                                               -rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
                                                   /* initialize carry over value */
@@ -891,6 +1058,7 @@ rrd_update(int argc, char **argv)
                         lookup_seasonal(&rrd,i,rra_start,rrd_file,
                                    elapsed_pdp_st + (scratch_idx == CDP_primary_val ? 1 : 2),
                                &seasonal_coef);
+                 rra_current = ftell(rrd_file);
                          }
                          if (rrd_test_error()) break;
                      /* loop over data soures within each RRA */
@@ -936,6 +1104,7 @@ rrd_update(int argc, char **argv)
                   }
                   rra_current = rra_pos_tmp;
                }
+
 #ifdef DEBUG
            fprintf(stderr,"  -- RRA Postseek %ld\n",ftell(rrd_file));
 #endif
@@ -982,12 +1151,14 @@ rrd_update(int argc, char **argv)
            
        } /* endif a pdp_st has occurred */ 
        rrd.live_head->last_up = current_time;
+       rrd.live_head->last_up_usec = current_time_usec; 
        free(step_start);
     } /* function argument loop */
 
     if (seasonal_coef != NULL) free(seasonal_coef);
     if (last_seasonal_coef != NULL) free(last_seasonal_coef);
        if (rra_step_cnt != NULL) free(rra_step_cnt);
+    rpnstack_free(&rpnstack);
 
     /* if we got here and if there is an error and if the file has not been
      * written to, then close things up and return. */
@@ -997,7 +1168,7 @@ rrd_update(int argc, char **argv)
        rrd_free(&rrd);
        free(pdp_temp);
        free(pdp_new);
-        fclose(rrd_file);
+       fclose(rrd_file);
        return(-1);
     }
 
@@ -1014,21 +1185,37 @@ rrd_update(int argc, char **argv)
        rrd_free(&rrd);
        free(pdp_temp);
        free(pdp_new);
-        fclose(rrd_file);
+       fclose(rrd_file);
        return(-1);
     }
 
-    if(fwrite( rrd.live_head,
-              sizeof(live_head_t), 1, rrd_file) != 1){
-       rrd_set_error("fwrite live_head to rrd");
-       free(updvals);
-       rrd_free(&rrd);
-       free(tmpl_idx);
-       free(pdp_temp);
-       free(pdp_new);
-        fclose(rrd_file);
-       return(-1);
+    if(version >= 3) {
+           if(fwrite( rrd.live_head,
+                      sizeof(live_head_t), 1, rrd_file) != 1){
+               rrd_set_error("fwrite live_head to rrd");
+               free(updvals);
+               rrd_free(&rrd);
+               free(tmpl_idx);
+               free(pdp_temp);
+               free(pdp_new);
+               fclose(rrd_file);
+               return(-1);
+           }
+    }
+    else {
+           if(fwrite( &rrd.live_head->last_up,
+                      sizeof(time_t), 1, rrd_file) != 1){
+               rrd_set_error("fwrite live_head to rrd");
+               free(updvals);
+               rrd_free(&rrd);
+               free(tmpl_idx);
+               free(pdp_temp);
+               free(pdp_new);
+               fclose(rrd_file);
+               return(-1);
+           }
     }
+           
 
     if(fwrite( rrd.pdp_prep,
               sizeof(pdp_prep_t),
@@ -1039,7 +1226,7 @@ rrd_update(int argc, char **argv)
        free(tmpl_idx);
        free(pdp_temp);
        free(pdp_new);
-        fclose(rrd_file);
+       fclose(rrd_file);
        return(-1);
     }
 
@@ -1054,7 +1241,7 @@ rrd_update(int argc, char **argv)
        rrd_free(&rrd);
        free(pdp_temp);
        free(pdp_new);
-        fclose(rrd_file);
+       fclose(rrd_file);
        return(-1);
     }
 
@@ -1067,7 +1254,7 @@ rrd_update(int argc, char **argv)
        rrd_free(&rrd);
        free(pdp_temp);
        free(pdp_new);
-        fclose(rrd_file);
+       fclose(rrd_file);
        return(-1);
     }
 
@@ -1090,9 +1277,9 @@ rrd_update(int argc, char **argv)
        if (schedule_smooth)
        {
 #ifndef WIN32
-         rrd_file = fopen(argv[optind],"r+");
+         rrd_file = fopen(filename,"r+");
 #else
-         rrd_file = fopen(argv[optind],"rb+");
+         rrd_file = fopen(filename,"rb+");
 #endif
          rra_start = rra_begin;
          for (i = 0; i < rrd.stat_head -> rra_cnt; ++i)
@@ -1160,7 +1347,7 @@ LockRRD(FILE *rrdfile)
 
 void
 write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
-   unsigned short CDP_scratch_idx, FILE *rrd_file)
+              unsigned short CDP_scratch_idx, FILE *rrd_file)
 {
    unsigned long ds_idx, cdp_idx;
 
@@ -1172,7 +1359,7 @@ write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
          fprintf(stderr,"  -- RRA WRITE VALUE %e, at %ld CF:%s\n",
             rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,ftell(rrd_file),
             rrd -> rra_def[rra_idx].cf_nam);
-#endif
+#endif 
 
          if(fwrite(&(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val),
                 sizeof(rrd_value_t),1,rrd_file) != 1)