follow the normal code path for exiting rrd_update if there is a problem with rrdc...
[rrdtool.git] / src / rrd_update.c
index d7ee4ac..dbf74df 100644 (file)
@@ -1,6 +1,6 @@
-
 /*****************************************************************************
- * RRDtool 1.3.2  Copyright by Tobi Oetiker, 1997-2008
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
+ *                Copyright by Florian Forster, 2008
  *****************************************************************************
  * rrd_update.c  RRD Update Function
  *****************************************************************************
@@ -23,6 +23,8 @@
 #include "rrd_is_thread_safe.h"
 #include "unused.h"
 
+#include "rrd_client.h"
+
 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
 /*
  * WIN32 does not have gettimeofday    and struct timeval. This is a quick and dirty
@@ -210,7 +212,6 @@ static void initialize_cdp_val(
     unival *scratch,
     int current_cf,
     rrd_value_t pdp_temp_val,
-    unsigned long elapsed_pdp_st,
     unsigned long start_pdp_offset,
     unsigned long pdp_cnt);
 
@@ -225,8 +226,9 @@ static void reset_cdp(
     int cdp_idx,
     enum cf_en current_cf);
 
-static rrd_value_t initialize_average_carry_over(
+static rrd_value_t initialize_carry_over(
     rrd_value_t pdp_temp_val,
+    int         current_cf,
     unsigned long elapsed_pdp_st,
     unsigned long start_pdp_offset,
     unsigned long pdp_cnt);
@@ -280,7 +282,7 @@ static int write_changes_to_disk(
  * normalize time as returned by gettimeofday. usec part must
  * be always >= 0
  */
-static inline void normalize_time(
+static void normalize_time(
     struct timeval *t)
 {
     if (t->tv_usec < 0) {
@@ -293,7 +295,7 @@ static inline void normalize_time(
  * Sets current_time and current_time_usec based on the current time.
  * current_time_usec is set to 0 if the version number is 1 or 2.
  */
-static inline void initialize_time(
+static void initialize_time(
     time_t *current_time,
     unsigned long *current_time_usec,
     int version)
@@ -319,6 +321,7 @@ rrd_info_t *rrd_update_v(
     char     *tmplt = NULL;
     rrd_info_t *result = NULL;
     rrd_infoval_t rc;
+    char *opt_daemon = NULL;
     struct option long_options[] = {
         {"template", required_argument, 0, 't'},
         {0, 0, 0, 0}
@@ -348,6 +351,15 @@ rrd_info_t *rrd_update_v(
         }
     }
 
+    opt_daemon = getenv (ENV_RRDCACHED_ADDRESS);
+    if (opt_daemon != NULL) {
+        rrd_set_error ("The \"%s\" environment variable is defined, "
+                "but \"%s\" cannot work with rrdcached. Either unset "
+                "the environment variable or use \"update\" instead.",
+                ENV_RRDCACHED_ADDRESS, argv[0]);
+        goto end_tag;
+    }
+
     /* need at least 2 arguments: filename, data. */
     if (argc - optind < 2) {
         rrd_set_error("Not enough arguments");
@@ -369,18 +381,20 @@ int rrd_update(
 {
     struct option long_options[] = {
         {"template", required_argument, 0, 't'},
+        {"daemon",   required_argument, 0, 'd'},
         {0, 0, 0, 0}
     };
     int       option_index = 0;
     int       opt;
     char     *tmplt = NULL;
     int       rc = -1;
+    char     *opt_daemon = NULL;
 
     optind = 0;
     opterr = 0;         /* initialize getopt */
 
     while (1) {
-        opt = getopt_long(argc, argv, "t:", long_options, &option_index);
+        opt = getopt_long(argc, argv, "t:d:", long_options, &option_index);
 
         if (opt == EOF)
             break;
@@ -390,6 +404,17 @@ int rrd_update(
             tmplt = strdup(optarg);
             break;
 
+        case 'd':
+            if (opt_daemon != NULL)
+                free (opt_daemon);
+            opt_daemon = strdup (optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error("strdup failed.");
+                goto out;
+            }
+            break;
+
         case '?':
             rrd_set_error("unknown option '%s'", argv[optind - 1]);
             goto out;
@@ -402,10 +427,47 @@ int rrd_update(
         goto out;
     }
 
-    rc = rrd_update_r(argv[optind], tmplt,
-                      argc - optind - 1, (const char **) (argv + optind + 1));
+    {   /* try to connect to rrdcached */
+        int status = rrdc_connect(opt_daemon);
+        if (status != 0) {        
+             rc = status;           
+             goto out;           
+        }        
+    }
+
+    if ((tmplt != NULL) && rrdc_is_connected(opt_daemon))
+    {
+        rrd_set_error("The caching daemon cannot be used together with "
+                "templates yet.");
+        goto out;
+    }
+
+    if (! rrdc_is_connected(opt_daemon))
+    {
+      rc = rrd_update_r(argv[optind], tmplt,
+                        argc - optind - 1, (const char **) (argv + optind + 1));
+    }
+    else /* we are connected */
+    {
+        rc = rrdc_update (argv[optind], /* file */
+                          argc - optind - 1, /* values_num */
+                          (const char *const *) (argv + optind + 1)); /* values */
+        if (rc > 0)
+            rrd_set_error("Failed sending the values to rrdcached: %s",
+                          rrd_strerror (rc));
+    }
+
   out:
-    free(tmplt);
+    if (tmplt != NULL)
+    {
+        free(tmplt);
+        tmplt = NULL;
+    }
+    if (opt_daemon != NULL)
+    {
+        free (opt_daemon);
+        opt_daemon = NULL;
+    }
     return rc;
 }
 
@@ -418,6 +480,16 @@ int rrd_update_r(
     return _rrd_update(filename, tmplt, argc, argv, NULL);
 }
 
+int rrd_update_v_r(
+    const char *filename,
+    const char *tmplt,
+    int argc,
+    const char **argv,
+    rrd_info_t * pcdp_summary)
+{
+    return _rrd_update(filename, tmplt, argc, argv, pcdp_summary);
+}
+
 int _rrd_update(
     const char *filename,
     const char *tmplt,
@@ -459,6 +531,7 @@ int _rrd_update(
         goto err_out;
     }
 
+    rrd_init(&rrd);
     if ((rrd_file = rrd_open(filename, &rrd, RRD_READWRITE)) == NULL) {
         goto err_free;
     }
@@ -557,41 +630,6 @@ int _rrd_update(
 }
 
 /*
- * get exclusive lock to whole file.
- * lock gets removed when we close the file
- *
- * returns 0 on success
- */
-int rrd_lock(
-    rrd_file_t *file)
-{
-    int       rcstat;
-
-    {
-#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
-        struct _stat st;
-
-        if (_fstat(file->fd, &st) == 0) {
-            rcstat = _locking(file->fd, _LK_NBLCK, st.st_size);
-        } else {
-            rcstat = -1;
-        }
-#else
-        struct flock lock;
-
-        lock.l_type = F_WRLCK;  /* exclusive write lock */
-        lock.l_len = 0; /* whole file */
-        lock.l_start = 0;   /* start of file */
-        lock.l_whence = SEEK_SET;   /* end of file */
-
-        rcstat = fcntl(file->fd, F_SETLK, &lock);
-#endif
-    }
-
-    return (rcstat);
-}
-
-/*
  * Allocate some important arrays used, and initialize the template.
  *
  * When it returns, either all of the structures are allocated
@@ -880,12 +918,16 @@ static int parse_ds(
             if (i < tmpl_cnt) {
                 updvals[tmpl_idx[i++]] = p + 1;
             }
+            else {
+                rrd_set_error("found extra data on update argument: %s",p+1);
+                return -1;
+            }                
         }
     }
 
     if (i != tmpl_cnt) {
         rrd_set_error("expected %lu data source readings (got %lu) from %s",
-                      tmpl_cnt - 1, i, input);
+                      tmpl_cnt - 1, i - 1, input);
         return -1;
     }
 
@@ -938,9 +980,21 @@ static int get_time_from_reading(
         *current_time = tmp_time.tv_sec;
         *current_time_usec = tmp_time.tv_usec;
     } else {
-        old_locale = setlocale(LC_NUMERIC, "C");
+        old_locale = setlocale(LC_NUMERIC, NULL);
+        setlocale(LC_NUMERIC, "C");
+        errno = 0;
         tmp = strtod(updvals[0], 0);
+        if (errno > 0) {
+            rrd_set_error("converting '%s' to float: %s",
+                updvals[0], rrd_strerror(errno));
+            return -1;
+        };
         setlocale(LC_NUMERIC, old_locale);
+        if (tmp < 0.0){
+            gettimeofday(&tmp_time, 0);
+            tmp = (double)tmp_time.tv_sec + (double)tmp_time.tv_usec * 1e-6f + tmp;
+        }
+
         *current_time = floor(tmp);
         *current_time_usec = (long) ((tmp - (double) *current_time) * 1e6f);
     }
@@ -1003,15 +1057,22 @@ static int update_pdp_prep(
             switch (dst_idx) {
             case DST_COUNTER:
             case DST_DERIVE:
-                for (ii = 0; updvals[ds_idx + 1][ii] != '\0'; ii++) {
-                    if ((updvals[ds_idx + 1][ii] < '0'
-                         || updvals[ds_idx + 1][ii] > '9')
-                        && (ii != 0 && updvals[ds_idx + 1][ii] != '-')) {
-                        rrd_set_error("not a simple integer: '%s'",
-                                      updvals[ds_idx + 1]);
+                /* Check if this is a valid integer. `U' is already handled in
+                 * another branch. */
+                for (ii = 0; updvals[ds_idx + 1][ii] != 0; ii++) {
+                    if ((ii == 0) && (dst_idx == DST_DERIVE)
+                            && (updvals[ds_idx + 1][ii] == '-'))
+                        continue;
+
+                    if ((updvals[ds_idx + 1][ii] < '0')
+                            || (updvals[ds_idx + 1][ii] > '9')) {
+                        rrd_set_error("not a simple %s integer: '%s'",
+                                (dst_idx == DST_DERIVE) ? "signed" : "unsigned",
+                                updvals[ds_idx + 1]);
                         return -1;
                     }
-                }
+                } /* for (ii = 0; updvals[ds_idx + 1][ii] != 0; ii++) */
+
                 if (rrd->pdp_prep[ds_idx].last_ds[0] != 'U') {
                     pdp_new[ds_idx] =
                         rrd_diff(updvals[ds_idx + 1],
@@ -1032,15 +1093,16 @@ static int update_pdp_prep(
                 }
                 break;
             case DST_ABSOLUTE:
-                old_locale = setlocale(LC_NUMERIC, "C");
+                old_locale = setlocale(LC_NUMERIC, NULL);
+                setlocale(LC_NUMERIC, "C");
                 errno = 0;
                 pdp_new[ds_idx] = strtod(updvals[ds_idx + 1], &endptr);
-                setlocale(LC_NUMERIC, old_locale);
                 if (errno > 0) {
                     rrd_set_error("converting '%s' to float: %s",
                                   updvals[ds_idx + 1], rrd_strerror(errno));
                     return -1;
                 };
+                setlocale(LC_NUMERIC, old_locale);
                 if (endptr[0] != '\0') {
                     rrd_set_error
                         ("conversion of '%s' to float not complete: tail '%s'",
@@ -1050,16 +1112,17 @@ static int update_pdp_prep(
                 rate = pdp_new[ds_idx] / interval;
                 break;
             case DST_GAUGE:
+                old_locale = setlocale(LC_NUMERIC, NULL);
+                setlocale(LC_NUMERIC, "C");
                 errno = 0;
-                old_locale = setlocale(LC_NUMERIC, "C");
                 pdp_new[ds_idx] =
                     strtod(updvals[ds_idx + 1], &endptr) * interval;
-                setlocale(LC_NUMERIC, old_locale);
                 if (errno) {
                     rrd_set_error("converting '%s' to float: %s",
                                   updvals[ds_idx + 1], rrd_strerror(errno));
                     return -1;
                 };
+                setlocale(LC_NUMERIC, old_locale);
                 if (endptr[0] != '\0') {
                     rrd_set_error
                         ("conversion of '%s' to float not complete: tail '%s'",
@@ -1307,6 +1370,10 @@ static int process_pdp_st(
 
         rpnp =
             rpn_expand((rpn_cdefds_t *) &(rrd->ds_def[ds_idx].par[DS_cdef]));
+        if(rpnp == NULL) {
+          rpnstack_free(&rpnstack);
+          return -1;
+        }
         /* substitute data values for OP_VARIABLE nodes */
         for (i = 0; rpnp[i].op != OP_END; i++) {
             if (rpnp[i].op == OP_VARIABLE) {
@@ -1320,6 +1387,7 @@ static int process_pdp_st(
             rpnstack_free(&rpnstack);
             return -1;
         }
+        free(rpnp);
     }
 
     /* make pdp_prep ready for the next run */
@@ -1496,7 +1564,7 @@ static int update_cdp_prep(
             if (elapsed_pdp_st > 2) {
                 reset_cdp(rrd, elapsed_pdp_st, pdp_temp, last_seasonal_coef,
                           seasonal_coef, rra_idx, ds_idx, cdp_idx,
-                          current_cf);
+                          (enum cf_en)current_cf);
             }
         }
 
@@ -1546,18 +1614,15 @@ static void update_cdp(
 
         if (*cdp_unkn_pdp_cnt > pdp_cnt * xff) {
             *cdp_primary_val = DNAN;
-            if (current_cf == CF_AVERAGE) {
-                *cdp_val =
-                    initialize_average_carry_over(pdp_temp_val,
-                                                  elapsed_pdp_st,
-                                                  start_pdp_offset, pdp_cnt);
-            } else {
-                *cdp_val = pdp_temp_val;
-            }
         } else {
             initialize_cdp_val(scratch, current_cf, pdp_temp_val,
-                               elapsed_pdp_st, start_pdp_offset, pdp_cnt);
-        }               /* endif meets xff value requirement for a valid value */
+                               start_pdp_offset, pdp_cnt);
+        }
+        *cdp_val =
+            initialize_carry_over(pdp_temp_val,current_cf,
+                                  elapsed_pdp_st,
+                                  start_pdp_offset, pdp_cnt);
+               /* endif meets xff value requirement for a valid value */
         /* initialize carry over CDP_unkn_pdp_cnt, this must after CDP_primary_val
          * is set because CDP_unkn_pdp_cnt is required to compute that value. */
         if (isnan(pdp_temp_val))
@@ -1593,7 +1658,6 @@ static void initialize_cdp_val(
     unival *scratch,
     int current_cf,
     rrd_value_t pdp_temp_val,
-    unsigned long elapsed_pdp_st,
     unsigned long start_pdp_offset,
     unsigned long pdp_cnt)
 {
@@ -1606,13 +1670,11 @@ static void initialize_cdp_val(
         scratch[CDP_primary_val].u_val =
             (cum_val + cur_val * start_pdp_offset) /
             (pdp_cnt - scratch[CDP_unkn_pdp_cnt].u_cnt);
-        scratch[CDP_val].u_val =
-            initialize_average_carry_over(pdp_temp_val, elapsed_pdp_st,
-                                          start_pdp_offset, pdp_cnt);
         break;
-    case CF_MAXIMUM:
+    case CF_MAXIMUM: 
         cum_val = IFDNAN(scratch[CDP_val].u_val, -DINF);
         cur_val = IFDNAN(pdp_temp_val, -DINF);
+
 #if 0
 #ifdef DEBUG
         if (isnan(scratch[CDP_val].u_val) && isnan(pdp_temp)) {
@@ -1627,8 +1689,6 @@ static void initialize_cdp_val(
             scratch[CDP_primary_val].u_val = cur_val;
         else
             scratch[CDP_primary_val].u_val = cum_val;
-        /* initialize carry over value */
-        scratch[CDP_val].u_val = pdp_temp_val;
         break;
     case CF_MINIMUM:
         cum_val = IFDNAN(scratch[CDP_val].u_val, DINF);
@@ -1647,14 +1707,10 @@ static void initialize_cdp_val(
             scratch[CDP_primary_val].u_val = cur_val;
         else
             scratch[CDP_primary_val].u_val = cum_val;
-        /* initialize carry over value */
-        scratch[CDP_val].u_val = pdp_temp_val;
         break;
     case CF_LAST:
     default:
         scratch[CDP_primary_val].u_val = pdp_temp_val;
-        /* initialize carry over value */
-        scratch[CDP_val].u_val = pdp_temp_val;
         break;
     }
 }
@@ -1716,17 +1772,34 @@ static void reset_cdp(
     }
 }
 
-static rrd_value_t initialize_average_carry_over(
+static rrd_value_t initialize_carry_over(
     rrd_value_t pdp_temp_val,
+    int current_cf,
     unsigned long elapsed_pdp_st,
     unsigned long start_pdp_offset,
     unsigned long pdp_cnt)
 {
-    /* initialize carry over value */
-    if (isnan(pdp_temp_val)) {
-        return DNAN;
-    }
-    return pdp_temp_val * ((elapsed_pdp_st - start_pdp_offset) % pdp_cnt);
+    unsigned long pdp_into_cdp_cnt = ((elapsed_pdp_st - start_pdp_offset) % pdp_cnt);
+    if ( pdp_into_cdp_cnt == 0 || isnan(pdp_temp_val)){
+        switch (current_cf) {
+        case CF_MAXIMUM:
+            return -DINF;
+        case CF_MINIMUM:
+            return DINF;
+        case CF_AVERAGE:
+            return 0;
+        default:
+            return DNAN;
+        }        
+    } 
+    else {
+        switch (current_cf) {
+        case CF_AVERAGE:
+            return pdp_temp_val *  pdp_into_cdp_cnt ;
+        default:
+            return pdp_temp_val;
+        }        
+    }        
 }
 
 /*
@@ -1865,7 +1938,7 @@ static int write_to_rras(
                  scratch_idx = CDP_secondary_val,
                  step_subtract = 2) {
 
-            off_t rra_pos_new;
+            size_t rra_pos_new;
 #ifdef DEBUG
             fprintf(stderr, "  -- RRA Preseek %ld\n", rrd_file->pos);
 #endif
@@ -1878,7 +1951,7 @@ static int write_to_rras(
               + ds_cnt * rra_ptr->cur_row * sizeof(rrd_value_t);
 
             /* re-seek if the position is wrong or we wrapped around */
-            if (rra_pos_new != rrd_file->pos) {
+            if ((size_t)rra_pos_new != rrd_file->pos) {
                 if (rrd_seek(rrd_file, rra_pos_new, SEEK_SET) != 0) {
                     rrd_set_error("seek error in rrd");
                     return -1;
@@ -1902,6 +1975,8 @@ static int write_to_rras(
                 (rrd_file, rrd, rra_idx, scratch_idx,
                  pcdp_summary, rra_time) == -1)
                 return -1;
+
+            rrd_notify_row(rrd_file, rra_idx, rra_pos_new, rra_time);
         }
 
         rra_start += rra_def->row_cnt * ds_cnt * sizeof(rrd_value_t);
@@ -1939,12 +2014,14 @@ static int write_RRA_row(
             /* append info to the return hash */
             *pcdp_summary = rrd_info_push(*pcdp_summary,
                                           sprintf_alloc
-                                          ("[%d]RRA[%s][%lu]DS[%s]", rra_time,
+                                          ("[%lli]RRA[%s][%lu]DS[%s]", 
+                                           (long long)rra_time,
                                            rrd->rra_def[rra_idx].cf_nam,
                                            rrd->rra_def[rra_idx].pdp_cnt,
                                            rrd->ds_def[ds_idx].ds_nam),
-                                          RD_I_VAL, iv);
+                                           RD_I_VAL, iv);
         }
+        errno = 0;
         if (rrd_write(rrd_file,
                       &(rrd->cdp_prep[cdp_idx].scratch[CDP_scratch_idx].
                         u_val), sizeof(rrd_value_t)) != sizeof(rrd_value_t)) {