Code

Summary of changes:
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Thu, 23 Sep 2010 06:08:09 +0000 (06:08 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Thu, 23 Sep 2010 06:08:09 +0000 (06:08 +0000)
1. Add INFO  <filename>, FIRST <filename> <index>, LAST <filename> and
   CREATE <rrdfile> [-b start][-s step][-O] <DSdefs> <RRAdefs> to the rrdcached
   functions.

2. Add rrd_create_set_no_overwrite as a function in rrd_create.c to allow
   the overwrite flag to be set from rrd_daemon.c

3. Add rrdc_info, rrdc_first, rrdc_last, rrdc_create functions in
   rrd_client.c

4. Add rrdcached support to rrd_info(), rrd_first(), rrd_last() and
   rrd_create()

5. Add --noflush (-F) option to rrd_info(), rrd_last() to prevent cache
   flushing before function call if required for efficiency

6. Add --daemon option to rrd_info(), rrd_first() for rrdcached support

7. Add buffer_add_ulong function to rrd_client.c (used by new rrdc_*
   functions)

8. Add rrd_clear_error() call to rrd_create_r () to clean up bug if create
   called more than once

9. Add -O option to rrdcached to force no-overwrite mode in CREATE function

10. All relevant documentation updated

-- Steve Shipway steve steveshipway.org

git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@2130 a5681a0c-68f1-0310-ab6d-d61299d08faa

15 files changed:
NEWS
doc/rrdcached.pod
doc/rrdcreate.pod
doc/rrdfirst.pod
doc/rrdinfo.pod
doc/rrdlast.pod
src/librrd.sym.in.in
src/rrd.h
src/rrd_client.c
src/rrd_client.h
src/rrd_create.c
src/rrd_daemon.c
src/rrd_first.c
src/rrd_info.c
src/rrd_last.c

diff --git a/NEWS b/NEWS
index e89dcc4feed420d9cb48cc0259b12c4387e3c188..0c593af414d457e89fbd4d5a0de3a6b48011a3a5 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,7 @@ RRDcached
 * New FETCH command allowing rrd_fetch to operate through the daemon and
   thus work remotely. By Florian Forster
 
+* New rrd_client commands INFO, FIRST, LAST, CREATE by Steve Shipway
 
 API
 ---
index 9aebfe73bbfa3859e4ad907bf881f04f2e9352c6..57b54b2c75b2cb449dfa634d1f5a715462ff4965 100644 (file)
@@ -20,6 +20,7 @@ B<rrdcached>
 [-g]
 [B<-b>E<nbsp>I<base_dir>E<nbsp>[B<-B>]]
 [B<-a>E<nbsp>I<alloc_size>]
+[-O]
 
 =head1 DESCRIPTION
 
@@ -245,6 +246,11 @@ exchange for slightly higher memory utilization.  The default isE<nbsp>1.
 Do not set this more than the B<-w> value divided by your average RRD step
 size.
 
+=item B<-O> 
+
+Preven the CREATE command from overwriting existing files, even when it is
+instructed to do so.  This is for added security.
+
 =back
 
 =head1 AFFECTED RRDTOOL COMMANDS
@@ -280,6 +286,10 @@ info
 
 =item *
 
+first
+
+=item *
+
 last
 
 =item *
@@ -294,6 +304,10 @@ update
 
 xport
 
+=item *
+
+create
+
 =back
 
 The B<update> command can send values to the daemon instead of writing them to
@@ -592,6 +606,34 @@ written out to disk.  It is used during journal replay to determine which
 updates have already been applied.  It is I<only> valid in the journal; it
 is not accepted from the other command channels.
 
+=item B<FIRST> I<filename> [I<rranum>]
+
+Return the timestamp for the first CDP in the specified RRA.  Default is to 
+use RRA zero if none is specified.
+
+=item B<LAST> I<filename> 
+
+Return the timestamp for the last update to the specified RRD. Note that the
+cache is I<not> flushed before checking, as the client is expected to request
+this separately if it is required.
+
+=item B<INFO> I<filename> 
+
+Return the configuration information for the specified RRD. Note that the
+cache is I<not> flushed before checking, as the client is expected to request
+this separately if it is required.
+
+The information is returned, one item per line, with the format:
+
+ I<keyname> I<type> I<value>
+
+=item B<CREATE> I<filename> [-s I<stepsize>] [-b I<begintime>] [-O] I<DSdefinitions> ... I<RRAdefinitions> ...
+
+This will create the RRD file according to the supplied parameters, provided
+the parameters are valid, and (if the -O option is given or if the rrdcached
+was started with the -O flag) the specified I<filename> does not already
+exist.
+
 =item B<BATCH>
 
 This command initiates the bulk load of multiple commands.  This is
@@ -713,6 +755,7 @@ Both B<rrdcached> and this manual page have been written by Florian.
 =head1 CONTRIBUTORS
 
 kevin brintnall E<lt>kbrint@rufus.netE<gt>
+Steve Shipway E<lt>steve@steveshipway.orgE<gt> 
 
 =cut
 
index b321de6329bd8509d373196c92c0a28f07853dbc..2167b7ac3805cea73d3a1d169ee6fb840fc9dbde 100644 (file)
@@ -8,6 +8,7 @@ B<rrdtool> B<create> I<filename>
 S<[B<--start>|B<-b> I<start time>]>
 S<[B<--step>|B<-s> I<step>]>
 S<[B<--no-overwrite>]>
+S<[B<--daemon> I<address>]>
 S<[B<DS:>I<ds-name>B<:>I<DST>B<:>I<dst arguments>]>
 S<[B<RRA:>I<CF>B<:>I<cf arguments>]>
 
@@ -41,6 +42,13 @@ into the B<RRD>.
 
 Do not clobber an existing file of the same name.
 
+=item B<--daemon> I<address>
+
+Address of the L<rrdcached> daemon.  For a list of accepted formats, see 
+the B<-l> option in the L<rrdcached> manual.
+
+ rrdtool create --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd I<other options>
+
 =head2 B<DS:>I<ds-name>B<:>I<DST>B<:>I<dst arguments>
 
 A single B<RRD> can accept input from several data sources (B<DS>),
index 9a3c690848e9ec6554bc39ef810f440a772fb9cd..eca21e0452eb692126741a1e7eaac8cfa86533cd 100644 (file)
@@ -4,7 +4,7 @@ rrdfirst - Return the date of the first data sample in an RRA within an RRD
 
 =head1 SYNOPSIS
 
-B<rrdtool> B<first> I<filename> [I<--rraindex number>]
+B<rrdtool> B<first> I<filename> [I<--rraindex number>] [B<--daemon> I<address>]
 
 =head1 DESCRIPTION
 
@@ -23,10 +23,17 @@ The index number of the B<RRA> that is to be examined. If not specified, the
 index defaults to zero. B<RRA> index numbers can be determined through
 B<rrdtool info>.
 
+=item B<--daemon> I<address>
+
+Address of the L<rrdcached> daemon.  For a list of accepted formats, see 
+the B<-l> option in the L<rrdcached> manual.
+
+ rrdtool first --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd
+
 =back
 
 =head1 AUTHOR
 
 Burton Strauss <Burton@ntopSupport.com>
-
+Daemon support added by Steve Shipway <steve@steveshipway.org>
 
index d147756c90be9e873c81983cd835b063f14dfa26..1752b69366265b9e9111c0811302136c928830a3 100644 (file)
@@ -30,6 +30,12 @@ For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
 
  rrdtool info --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd
 
+=item B<--noflush> 
+
+Omit the C<flush> command usually sent to the daemon prior to retrieving the
+data.  If all you are interested in the the RRD Structure, and not the last update
+time or current values, then this will improve efficiency.
+
 =back
 
 =head1 EXAMPLE
index a8bf7574bb461cc922551341aaf40699818e60da..f0eaad0c7d86ed761f738f977db35fad89c5576d 100644 (file)
@@ -27,6 +27,13 @@ For a list of accepted formats, see the B<-l> option in the L<rrdcached> manual.
 
  rrdtool last --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd
 
+=item B<--noflush> 
+
+If the L<rrdcached> daemon is being used, then omit the flush normally 
+send before returning the last update.  If you add this option, you get the 
+last time the file on disk was updated; if you have relatively low cache times
+and are calling this frequently it will preserve the caching benefit
+
 =back
 
 =head1 ENVIRONMENT VARIABLES
@@ -47,6 +54,6 @@ line argument takes precedence.
 =head1 AUTHOR
 
 Russ Wright <rwwright@home.com>
-
+Daemon support added by Steve Shipway <steve@steveshipway.org>
 
 
index 9a8f7f95624a5b93e66f238a531279a0de8acd34..458601f763f2b4f3c46b7356f19a291aa79478b9 100644 (file)
@@ -6,6 +6,7 @@ rrd_clear_error
 rrd_close
 rrd_create
 rrd_create_r
+rrd_create_set_no_overwrite
 rrd_dontneed
 rrd_dump
 rrd_dump_r
index fa02952d7aad6a64f3dab71da78f1f395537fbbb..331339dbdfb2f7cfc6bc5f81b4ecd79ae53bf126 100644 (file)
--- a/src/rrd.h
+++ b/src/rrd.h
@@ -143,6 +143,8 @@ extern    "C" {
     int       rrd_create(
     int,
     char **);
+    void      rrd_create_set_no_overwrite(
+    int);
     rrd_info_t *rrd_info(
     int,
     char **);
index 0d2128387a8ed7633ca3125fb5a9b2d0d571f29f..043a144ad455ab841d21bc9bcea4d0d325828a95 100644 (file)
@@ -316,6 +316,16 @@ static int buffer_add_value (const char *value, /* {{{ */
   return (buffer_add_string (temp, buffer_ret, buffer_size_ret));
 } /* }}} int buffer_add_value */
 
+static int buffer_add_ulong (const unsigned long value, /* {{{ */
+    char **buffer_ret, size_t *buffer_size_ret)
+{
+  char temp[4096];
+
+  snprintf (temp, sizeof (temp), "%lu", value);
+  temp[sizeof (temp) - 1] = 0;
+  return (buffer_add_string (temp, buffer_ret, buffer_size_ret));
+} /* }}} int buffer_add_ulong */
+
 /* Remove trailing newline (NL) and carriage return (CR) characters. Similar to
  * the Perl function `chomp'. Returns the number of characters that have been
  * removed. */
@@ -817,6 +827,320 @@ int rrdc_flush (const char *filename) /* {{{ */
   return (status);
 } /* }}} int rrdc_flush */
 
+rrd_info_t * rrdc_info (const char *filename) /* {{{ */
+{
+  char buffer[4096];
+  char *buffer_ptr;
+  size_t buffer_free;
+  size_t buffer_size;
+  rrdc_response_t *res;
+  int status;
+  char file_path[PATH_MAX];
+  rrd_info_t *data = NULL, *cd;
+  rrd_infoval_t info;
+  unsigned int l;
+  rrd_info_type_t itype;
+  char *k, *s;
+
+  if (filename == NULL) {
+    rrd_set_error ("rrdc_info: no filename");
+    return (NULL);
+  }
+
+  memset (buffer, 0, sizeof (buffer));
+  buffer_ptr = &buffer[0];
+  buffer_free = sizeof (buffer);
+
+  status = buffer_add_string ("info", &buffer_ptr, &buffer_free);
+  if (status != 0) {
+    rrd_set_error ("rrdc_info: out of memory");
+    return (NULL);
+  }
+
+  pthread_mutex_lock (&lock);
+  filename = get_path (filename, file_path);
+  if (filename == NULL)
+  {
+    pthread_mutex_unlock (&lock);
+    return (NULL);
+  }
+
+  status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&lock);
+    rrd_set_error ("rrdc_info: out of memory");
+    return (NULL);
+  }
+
+  assert (buffer_free < sizeof (buffer));
+  buffer_size = sizeof (buffer) - buffer_free;
+  assert (buffer[buffer_size - 1] == ' ');
+  buffer[buffer_size - 1] = '\n';
+
+  res = NULL;
+  status = request (buffer, buffer_size, &res);
+  pthread_mutex_unlock (&lock);
+
+  if (status != 0) {
+    rrd_set_error ("rrdcached: %s", res->message);
+    return (NULL);
+  }
+  data = cd = NULL;
+  for( l=0 ; l < res->lines_num ; l++ ) {
+    /* first extract the keyword */
+       for(k = s = res->lines[l];s && *s;s++) {
+      if(*s == ' ') { *s = 0; s++; break; }
+       }
+    if(!s || !*s) break;
+       itype = atoi(s); /* extract type code */
+       for(;*s;s++) { if(*s == ' ') { *s = 0; s++; break; } }
+    if(!*s) break;
+    /* finally, we're pointing to the value */
+    switch(itype) {
+    case RD_I_VAL:
+        if(*s == 'N') { info.u_val = DNAN; } else { info.u_val = atof(s); }
+        break;
+    case RD_I_CNT:
+        info.u_cnt = atol(s);
+        break;
+    case RD_I_INT:
+        info.u_int = atoi(s);
+        break;
+    case RD_I_STR:
+        chomp(s);
+        info.u_str = (char*)malloc(sizeof(char) * (strlen(s) + 1));
+        strcpy(info.u_str,s);
+        break;
+    case RD_I_BLO:
+        rrd_set_error ("rrdc_info: BLOB objects are not supported");
+        return (NULL);
+    default:
+        rrd_set_error ("rrdc_info: Unsupported info type %d",itype);
+        return (NULL);
+    }
+       
+    cd = rrd_info_push(cd, sprintf_alloc("%s",k), itype, info);
+       if(!data) data = cd;
+  }
+  response_free (res);
+
+  return (data);
+} /* }}} int rrdc_info */
+
+time_t rrdc_last (const char *filename) /* {{{ */
+{
+  char buffer[4096];
+  char *buffer_ptr;
+  size_t buffer_free;
+  size_t buffer_size;
+  rrdc_response_t *res;
+  int status;
+  char file_path[PATH_MAX];
+  time_t lastup;
+
+  if (filename == NULL) {
+    rrd_set_error ("rrdc_last: no filename");
+    return (-1);
+  }
+
+  memset (buffer, 0, sizeof (buffer));
+  buffer_ptr = &buffer[0];
+  buffer_free = sizeof (buffer);
+
+  status = buffer_add_string ("last", &buffer_ptr, &buffer_free);
+  if (status != 0) {
+    rrd_set_error ("rrdc_last: out of memory");
+    return (-1);
+  }
+
+  pthread_mutex_lock (&lock);
+  filename = get_path (filename, file_path);
+  if (filename == NULL)
+  {
+    pthread_mutex_unlock (&lock);
+    return (-1);
+  }
+
+  status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&lock);
+    rrd_set_error ("rrdc_last: out of memory");
+    return (-1);
+  }
+
+  assert (buffer_free < sizeof (buffer));
+  buffer_size = sizeof (buffer) - buffer_free;
+  assert (buffer[buffer_size - 1] == ' ');
+  buffer[buffer_size - 1] = '\n';
+
+  res = NULL;
+  status = request (buffer, buffer_size, &res);
+  pthread_mutex_unlock (&lock);
+
+  if (status != 0) {
+    rrd_set_error ("rrdcached: %s", res->message);
+    return (-1);
+  }
+  lastup = atol(res->message);
+  response_free (res);
+
+  return (lastup);
+} /* }}} int rrdc_last */
+
+time_t rrdc_first (const char *filename, int rraindex) /* {{{ */
+{
+  char buffer[4096];
+  char *buffer_ptr;
+  size_t buffer_free;
+  size_t buffer_size;
+  rrdc_response_t *res;
+  int status;
+  char file_path[PATH_MAX];
+  time_t firstup;
+
+  if (filename == NULL) {
+    rrd_set_error ("rrdc_first: no filename specified");
+    return (-1);
+  }
+
+  memset (buffer, 0, sizeof (buffer));
+  buffer_ptr = &buffer[0];
+  buffer_free = sizeof (buffer);
+
+  status = buffer_add_string ("first", &buffer_ptr, &buffer_free);
+  if (status != 0) {
+    rrd_set_error ("rrdc_first: out of memory");
+    return (-1);
+  }
+
+  pthread_mutex_lock (&lock);
+  filename = get_path (filename, file_path);
+  if (filename == NULL)
+  {
+    pthread_mutex_unlock (&lock);
+    return (-1);
+  }
+
+  status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&lock);
+    rrd_set_error ("rrdc_first: out of memory");
+    return (-1);
+  }
+  status = buffer_add_ulong (rraindex, &buffer_ptr, &buffer_free);
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&lock);
+    rrd_set_error ("rrdc_first: out of memory");
+    return (-1);
+  }
+
+  assert (buffer_free < sizeof (buffer));
+  buffer_size = sizeof (buffer) - buffer_free;
+  assert (buffer[buffer_size - 1] == ' ');
+  buffer[buffer_size - 1] = '\n';
+
+  res = NULL;
+  status = request (buffer, buffer_size, &res);
+  pthread_mutex_unlock (&lock);
+
+  if (status != 0) {
+    rrd_set_error ("rrdcached: %s", res->message);
+    return (-1);
+  }
+  firstup = atol(res->message);
+  response_free (res);
+
+  return (firstup);
+} /* }}} int rrdc_first */
+
+int rrdc_create (const char *filename, /* {{{ */
+    unsigned long pdp_step,
+    time_t last_up,
+    int no_overwrite,
+    int argc,
+    const char **argv)
+{
+  char buffer[4096];
+  char *buffer_ptr;
+  size_t buffer_free;
+  size_t buffer_size;
+  rrdc_response_t *res;
+  int status;
+  char file_path[PATH_MAX];
+  int i;
+
+  if (filename == NULL) {
+    rrd_set_error ("rrdc_create: no filename specified");
+    return (-1);
+  }
+
+  memset (buffer, 0, sizeof (buffer));
+  buffer_ptr = &buffer[0];
+  buffer_free = sizeof (buffer);
+
+  status = buffer_add_string ("create", &buffer_ptr, &buffer_free);
+  if (status != 0) {
+    rrd_set_error ("rrdc_create: out of memory");
+    return (-1);
+  }
+
+  pthread_mutex_lock (&lock);
+  filename = get_path (filename, file_path);
+  if (filename == NULL)
+  {
+    pthread_mutex_unlock (&lock);
+    return (-1);
+  }
+
+  status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
+  status = buffer_add_string ("-b", &buffer_ptr, &buffer_free);
+  status = buffer_add_ulong (last_up, &buffer_ptr, &buffer_free);
+  status = buffer_add_string ("-s", &buffer_ptr, &buffer_free);
+  status = buffer_add_ulong (pdp_step, &buffer_ptr, &buffer_free);
+  if(no_overwrite) {
+    status = buffer_add_string ("-O", &buffer_ptr, &buffer_free);
+  }
+  if (status != 0)
+  {
+    pthread_mutex_unlock (&lock);
+    rrd_set_error ("rrdc_create: out of memory");
+    return (-1);
+  }
+
+  for( i=0; i<argc; i++ ) {
+    if( argv[i] ) {
+      status = buffer_add_string (argv[i], &buffer_ptr, &buffer_free);
+      if (status != 0)
+      {
+        pthread_mutex_unlock (&lock);
+        rrd_set_error ("rrdc_create: out of memory");
+        return (-1);
+      }
+       }
+  }
+
+  /* buffer ready to send? */
+  assert (buffer_free < sizeof (buffer));
+  buffer_size = sizeof (buffer) - buffer_free;
+  assert (buffer[buffer_size - 1] == ' ');
+  buffer[buffer_size - 1] = '\n';
+
+  res = NULL;
+  status = request (buffer, buffer_size, &res);
+  pthread_mutex_unlock (&lock);
+
+  if (status != 0) {
+    rrd_set_error ("rrdcached: %s", res->message);
+    return (-1);
+  }
+  response_free (res);
+  return(0);
+} /* }}} int rrdc_create */
+
 int rrdc_fetch (const char *filename, /* {{{ */
     const char *cf,
     time_t *ret_start, time_t *ret_end,
index 31f0e95ef47f14968cb13bbca6255b34cfdcf2b0..e203d7ca9748f43d7602ad4f0a0d58c13fcfb726 100644 (file)
@@ -68,6 +68,17 @@ int rrdc_disconnect (void);
 int rrdc_update (const char *filename, int values_num,
         const char * const *values);
 
+rrd_info_t * rrdc_info (const char *filename);
+time_t rrdc_last (const char *filename);
+time_t rrdc_first (const char *filename, int rraindex);
+int rrdc_create (const char *filename,
+    unsigned long pdp_step,
+    time_t last_up,
+    int no_overwrite,
+    int argc,
+    const char **argv);
+
+
 int rrdc_flush (const char *filename);
 int rrdc_flush_if_daemon (const char *opt_daemon, const char *filename);
 
index c8996ce82f81f61177774fd918523ef2c903f4f7..707e6013756851e65e8f18b7bef9237303cf838c 100644 (file)
@@ -11,6 +11,7 @@
 #include "rrd_tool.h"
 #include "rrd_rpncalc.h"
 #include "rrd_hw.h"
+#include "rrd_client.h"
 
 #include "rrd_is_thread_safe.h"
 static int opt_no_overwrite = 0;
@@ -33,6 +34,12 @@ void      parseGENERIC_DS(
 static void rrd_free2(
     rrd_t *rrd);        /* our onwn copy, immmune to mmap */
 
+void rrd_create_set_no_overwrite( 
+    int opt ) 
+{
+       opt_no_overwrite = (opt?1:0);
+}
+
 int rrd_create(
     int argc,
     char **argv)
@@ -40,6 +47,7 @@ int rrd_create(
     struct option long_options[] = {
         {"start", required_argument, 0, 'b'},
         {"step", required_argument, 0, 's'},
+        {"daemon", required_argument, 0, 'd'},
         {"no-overwrite", no_argument, 0, 'O'},
         {0, 0, 0, 0}
     };
@@ -51,17 +59,29 @@ int rrd_create(
     char     *parsetime_error = NULL;
     long      long_tmp;
     int       rc;
+    char * opt_daemon = NULL;
 
     optind = 0;
     opterr = 0;         /* initialize getopt */
 
     while (1) {
-        opt = getopt_long(argc, argv, "Ob:s:", long_options, &option_index);
+        opt = getopt_long(argc, argv, "Ob:s:d:", long_options, &option_index);
 
         if (opt == EOF)
             break;
 
         switch (opt) {
+        case 'd':
+            if (opt_daemon != NULL)
+                    free (opt_daemon);
+            opt_daemon = strdup (optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error ("strdup failed.");
+                return (-1);
+            }
+            break;
+
         case 'b':
             if ((parsetime_error = rrd_parsetime(optarg, &last_up_tv))) {
                 rrd_set_error("start time: %s", parsetime_error);
@@ -108,9 +128,17 @@ int rrd_create(
         rrd_set_error("need name of an rrd file to create");
         return -1;
     }
+
+    rrdc_connect (opt_daemon);
+    if (rrdc_is_connected (opt_daemon)) {
+        rc = rrdc_create (argv[optind],
+                      pdp_step, last_up, opt_no_overwrite,
+                      argc - optind - 1, (const char **) (argv + optind + 1));
+       } else {
     rc = rrd_create_r(argv[optind],
                       pdp_step, last_up,
                       argc - optind - 1, (const char **) (argv + optind + 1));
+       }
 
     return rc;
 }
@@ -131,6 +159,9 @@ int rrd_create_r(
     unsigned short token_idx, error_flag, period = 0;
     unsigned long hashed_name;
 
+    /* clear any previous errors */
+    rrd_clear_error();
+
     /* init rrd clean */
     rrd_init(&rrd);
     /* static header */
@@ -189,11 +220,11 @@ int rrd_create_r(
                            dummychar2, &offset)) {
             case 0:
             case 1:
-                rrd_set_error("Invalid DS name");
+                rrd_set_error("Invalid DS name in [%s]",&argv[i][3]);
                 break;
             case 2:
             case 3:
-                rrd_set_error("Invalid DS type");
+                rrd_set_error("Invalid DS type in [%s]",&argv[i][3]);
                 break;
             case 4:    /* (%n may or may not be counted) */
             case 5:    /* check for duplicate datasource names */
index 22093205ba099b49bb1d98172c2fb84baffc2355..bfcf6a3ce4f7ed1d8da75695264e248a4e075cbc 100644 (file)
@@ -275,6 +275,8 @@ static uint64_t stats_journal_bytes = 0;
 static uint64_t stats_journal_rotate = 0;
 static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
 
+static int opt_no_overwrite = 0;
+
 /* Journaled updates */
 #define JOURNAL_REPLAY(s) ((s) == NULL)
 #define JOURNAL_BASE "rrd.journal"
@@ -1695,6 +1697,182 @@ static int handle_request_wrote (HANDLER_PROTO) /* {{{ */
   return (0);
 } /* }}} int handle_request_wrote */
 
+static int handle_request_info (HANDLER_PROTO) /* {{{ */
+{
+  char *file, file_tmp[PATH_MAX];
+  int status;
+  rrd_info_t *data;
+
+  /* obtain filename */
+  status = buffer_get_field(&buffer, &buffer_size, &file);
+  if (status != 0)
+    return syntax_error(sock,cmd);
+  /* get full pathname */
+  get_abs_path(&file, file_tmp);
+  if (!check_file_access(file, sock)) {
+    return send_response(sock, RESP_ERR, "Cannot read: %s\n", file);
+  }
+  /* get data */
+  rrd_clear_error ();
+  data = rrd_info_r(file);
+  if(!data) {
+    return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error());
+  }
+  while (data) {
+      switch (data->type) {
+      case RD_I_VAL:
+          if (isnan(data->value.u_val))
+              add_response_info(sock,"%s %d NaN\n",data->key, data->type);
+          else
+              add_response_info(sock,"%s %d %0.10e\n", data->key, data->type, data->value.u_val);
+          break;
+      case RD_I_CNT:
+          add_response_info(sock,"%s %d %lu\n", data->key, data->type, data->value.u_cnt);
+          break;
+      case RD_I_INT:
+          add_response_info(sock,"%s %d %d\n", data->key, data->type, data->value.u_int);
+          break;
+      case RD_I_STR:
+          add_response_info(sock,"%s %d %s\n", data->key, data->type, data->value.u_str);
+          break;
+      case RD_I_BLO:
+          add_response_info(sock,"%s %d %lu\n", data->key, data->type, data->value.u_blo.size);
+          break;
+      }
+      data = data->next;
+  }
+  return send_response(sock, RESP_OK, "Info for %s follows\n",file);
+} /* }}} static int handle_request_info  */
+
+static int handle_request_first (HANDLER_PROTO) /* {{{ */
+{
+  char *i, *file, file_tmp[PATH_MAX];
+  int status;
+  int idx;
+  time_t t;
+
+  /* obtain filename */
+  status = buffer_get_field(&buffer, &buffer_size, &file);
+  if (status != 0)
+    return syntax_error(sock,cmd);
+  /* get full pathname */
+  get_abs_path(&file, file_tmp);
+  if (!check_file_access(file, sock)) {
+    return send_response(sock, RESP_ERR, "Cannot read: %s\n", file);
+  }
+
+  status = buffer_get_field(&buffer, &buffer_size, &i);
+  if (status != 0)
+    return syntax_error(sock,cmd);
+  idx = atoi(i);
+  if(idx<0) { 
+    return send_response(sock, RESP_ERR, "Invalid index specified (%d)\n", idx);
+  }
+
+  /* get data */
+  rrd_clear_error ();
+  t = rrd_first_r(file,idx);
+  if(t<1) {
+    return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error());
+  }
+  return send_response(sock, RESP_OK, "%lu\n",(unsigned)t);
+} /* }}} static int handle_request_last  */
+
+static int handle_request_last (HANDLER_PROTO) /* {{{ */
+{
+  char *file, file_tmp[PATH_MAX];
+  int status;
+  time_t t;
+
+  /* obtain filename */
+  status = buffer_get_field(&buffer, &buffer_size, &file);
+  if (status != 0)
+    return syntax_error(sock,cmd);
+  /* get full pathname */
+  get_abs_path(&file, file_tmp);
+  if (!check_file_access(file, sock)) {
+    return send_response(sock, RESP_ERR, "Cannot read: %s\n", file);
+  }
+  /* get data */
+  rrd_clear_error ();
+  t = rrd_last_r(file);
+  if(t<1) {
+    return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error());
+  }
+  return send_response(sock, RESP_OK, "%lu\n",(unsigned)t);
+} /* }}} static int handle_request_last  */
+
+static int handle_request_create (HANDLER_PROTO) /* {{{ */
+{
+  char *file, file_tmp[PATH_MAX];
+  char *tok;
+  int ac = 0;
+  char *av[128];
+  int status;
+  ulong step = 300;
+  time_t last_up = time(NULL)-10;
+  rrd_time_value_t last_up_tv;
+  char     *parsetime_error = NULL;
+  int no_overwrite = opt_no_overwrite;
+
+
+  /* obtain filename */
+  status = buffer_get_field(&buffer, &buffer_size, &file);
+  if (status != 0)
+    return syntax_error(sock,cmd);
+  /* get full pathname */
+  get_abs_path(&file, file_tmp);
+  if (!check_file_access(file, sock)) {
+    return send_response(sock, RESP_ERR, "Cannot read: %s\n", file);
+  }
+  RRDD_LOG(LOG_INFO, "rrdcreate request for %s",file);
+
+  status = buffer_get_field(&buffer, &buffer_size, &tok );
+  for(;(tok && !status);status = buffer_get_field(&buffer, &buffer_size, &tok )) {
+    if( ! strncmp(tok,"-b",2) ) {
+      status = buffer_get_field(&buffer, &buffer_size, &tok );
+      if (status != 0) return syntax_error(sock,cmd);
+      if ((parsetime_error = rrd_parsetime(tok, &last_up_tv))) 
+        return send_response(sock, RESP_ERR, "start time: %s\n", parsetime_error);
+      if (last_up_tv.type == RELATIVE_TO_END_TIME ||
+        last_up_tv.type == RELATIVE_TO_START_TIME) {
+        return send_response(sock, RESP_ERR, "Cannot specify time relative to start or end here.\n");
+      }
+      last_up = mktime(&last_up_tv.tm) +last_up_tv.offset;
+
+      continue;
+    }
+    if( ! strncmp(tok,"-s",2) ) {
+      status = buffer_get_field(&buffer, &buffer_size, &tok );
+      if (status != 0) return syntax_error(sock,cmd);
+      step = atol(tok);
+      continue;
+    }
+    if( ! strncmp(tok,"-O",2) ) {
+      no_overwrite = 1;
+      continue;
+    }
+    if( ! strncmp(tok,"DS:",3) ) { av[ac++]=tok; continue; }
+    if( ! strncmp(tok,"RRA:",4) ) { av[ac++]=tok; continue; }
+    return syntax_error(sock,cmd);
+  }
+  if(step<1) {
+    return send_response(sock, RESP_ERR, "The step size cannot be less than 1 second.\n");
+  }
+  if (last_up < 3600 * 24 * 365 * 10) {
+    return send_response(sock, RESP_ERR, "The first entry must be after 1980.\n");
+  }
+
+  rrd_create_set_no_overwrite(no_overwrite);
+  rrd_clear_error ();
+  status = rrd_create_r(file,step,last_up,ac,(const char **)av);
+
+  if(!status) {
+    return send_response(sock, RESP_OK, "RRD created OK\n");
+  }
+  return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error());
+} /* }}} static int handle_request_create  */
+
 /* start "BATCH" processing */
 static int batch_start (HANDLER_PROTO) /* {{{ */
 {
@@ -1847,6 +2025,45 @@ static command_t list_of_commands[] = { /* {{{ */
     ,
     "The 'FETCH' can be used by the client to retrieve values from an RRD file.\n"
   },
+  {
+    "INFO",
+    handle_request_info,
+    CMD_CONTEXT_CLIENT,
+    "INFO <filename>\n",
+    "The INFO command retrieves information about a specified RRD file.\n"
+    "This is returned in standard rrdinfo format, a sequence of lines\n"
+    "with the format <keyname> = <value>\n"
+    "Note that this is the data as of the last update of the RRD file itself,\n"
+    "not the last time data was received via rrdcached, so there may be pending\n"
+    "updates in the queue.  If this bothers you, then first run a FLUSH.\n"
+  },
+  {
+    "FIRST",
+    handle_request_first,
+    CMD_CONTEXT_CLIENT,
+    "FIRST <filename> <rra index>\n",
+    "The FIRST command retrieves the first data time for a specified RRA in\n"
+    "an RRD file.\n"
+  },
+  {
+    "LAST",
+    handle_request_last,
+    CMD_CONTEXT_CLIENT,
+    "LAST <filename>\n",
+    "The LAST command retrieves the last update time for a specified RRD file.\n"
+    "Note that this is the time of the last update of the RRD file itself, not\n"
+    "the last time data was received via rrdcached, so there may be pending\n"
+    "updates in the queue.  If this bothers you, then first run a FLUSH.\n"
+  },
+  {
+    "CREATE",
+    handle_request_create,
+    CMD_CONTEXT_CLIENT | CMD_CONTEXT_BATCH,
+    "CREATE <filename> [-b start] [-s step] [-O] <DS definitions> <RRA definitions>\n",
+    "The CREATE command will create an RRD file, overwriting any existing file\n"
+    "unless the -O option is given or rrdcached was started with the -O option.\n"
+    "The DS and RRA definitions are as for the 'rrdtool create' command.\n"
+  },
   {
     "QUIT",
     handle_request_quit,
@@ -2993,10 +3210,14 @@ static int read_options (int argc, char **argv) /* {{{ */
   default_socket.socket_group = (gid_t)-1;
   default_socket.socket_permissions = (mode_t)-1;
 
-  while ((option = getopt(argc, argv, "gl:s:m:P:f:w:z:t:Bb:p:Fj:a:h?")) != -1)
+  while ((option = getopt(argc, argv, "Ogl:s:m:P:f:w:z:t:Bb:p:Fj:a:h?")) != -1)
   {
     switch (option)
     {
+      case 'O':
+        opt_no_overwrite = 1;
+        break;
+
       case 'g':
         stay_foreground=1;
         break;
@@ -3338,7 +3559,9 @@ static int read_options (int argc, char **argv) /* {{{ */
                             "for that group)\n"
             "  -m <mode>     File permissions (octal) of all following UNIX "
                             "sockets\n"
-            "  -a <size>     Memory allocation chunk size. Default is 1."
+            "  -a <size>     Memory allocation chunk size. Default is 1.\n"
+            "  -O            Do not allow CREATE commands to overwrite existing\n"
+            "                files, even if asked to.\n"
             "\n"
             "For more information and a detailed description of all options "
             "please refer\n"
index faae57746673ccb2726949f7f56ff5a9c516da75..80b9e17df6113d28b8114baa13dfa70132e67187 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <stdlib.h>
 #include "rrd_tool.h"
+#include "rrd_client.h"
 
 
 time_t rrd_first(
@@ -16,8 +17,10 @@ time_t rrd_first(
 {
     int       target_rraindex = 0;
     char     *endptr;
+    char *opt_daemon = NULL;
     struct option long_options[] = {
         {"rraindex", required_argument, 0, 129},
+        {"daemon", required_argument, 0, 'd'},
         {0, 0, 0, 0}
     };
 
@@ -28,7 +31,7 @@ time_t rrd_first(
         int       option_index = 0;
         int       opt;
 
-        opt = getopt_long(argc, argv, "", long_options, &option_index);
+        opt = getopt_long(argc, argv, "d:F", long_options, &option_index);
 
         if (opt == EOF)
             break;
@@ -41,19 +44,35 @@ time_t rrd_first(
                 return (-1);
             }
             break;
+        case 'd':
+            if (opt_daemon != NULL)
+                    free (opt_daemon);
+            opt_daemon = strdup (optarg);
+            if (opt_daemon == NULL)
+            {
+                rrd_set_error ("strdup failed.");
+                return (-1);
+            }
+            break;
         default:
-            rrd_set_error("usage rrdtool %s [--rraindex number] file.rrd",
+            rrd_set_error("usage rrdtool %s [--rraindex number] [--daemon <addr>] file.rrd",
                           argv[0]);
             return (-1);
         }
     }
 
     if (optind >= argc) {
-        rrd_set_error("not enough arguments");
+        rrd_set_error("usage rrdtool %s [--rraindex number] [--daemon <addr>] file.rrd",
+                      argv[0]);
         return -1;
     }
 
+    rrdc_connect (opt_daemon);
+    if (rrdc_is_connected (opt_daemon)) {
+      return (rrdc_first (argv[optind], target_rraindex));
+    } else {
     return (rrd_first_r(argv[optind], target_rraindex));
+       }
 }
 
 
index 4f8b5dd4503a4514550a51fed7d99f55c12381cb..d86a9074d90e631184d04e758652fa7788375c84 100644 (file)
@@ -92,6 +92,7 @@ rrd_info_t *rrd_info(
     rrd_info_t *info;
     char *opt_daemon = NULL;
     int status;
+    int flushfirst = 1;
 
     optind = 0;
     opterr = 0;         /* initialize getopt */
@@ -101,10 +102,11 @@ rrd_info_t *rrd_info(
         int       option_index = 0;
         static struct option long_options[] = {
             {"daemon", required_argument, 0, 'd'},
+            {"noflush", no_argument, 0, 'F'},
             {0, 0, 0, 0}
         };
 
-        opt = getopt_long(argc, argv, "d:", long_options, &option_index);
+        opt = getopt_long(argc, argv, "d:F", long_options, &option_index);
 
         if (opt == EOF)
             break;
@@ -121,8 +123,12 @@ rrd_info_t *rrd_info(
             }
             break;
 
+        case 'F':
+            flushfirst = 0;
+            break;
+
         default:
-            rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+            rrd_set_error ("Usage: rrdtool %s [--daemon <addr> [--noflush]] <file>",
                     argv[0]);
             return (NULL);
             break;
@@ -130,15 +136,20 @@ rrd_info_t *rrd_info(
     }                   /* while (42) */
 
     if ((argc - optind) != 1) {
-        rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+        rrd_set_error ("Usage: rrdtool %s [--daemon <addr> [--noflush]] <file>",
                 argv[0]);
         return (NULL);
     }
 
+    if( flushfirst ) {
     status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
-    if (opt_daemon) free (opt_daemon);
     if (status) return (NULL);
+    }
 
+    rrdc_connect (opt_daemon);
+    if (rrdc_is_connected (opt_daemon))
+        info = rrdc_info (argv[optind]);
+    else
     info = rrd_info_r(argv[optind]);
 
     return (info);
index 8555fbc0310f8846a740c764ae54e92d28e6d368..7fb3cb884344f8c76119d9b7b7c860b092afbcc2 100644 (file)
@@ -15,6 +15,8 @@ time_t rrd_last(
 {
     char *opt_daemon = NULL;
     int status;
+    time_t lastupdate;
+    int flushfirst = 1;
 
     optind = 0;
     opterr = 0;         /* initialize getopt */
@@ -24,10 +26,11 @@ time_t rrd_last(
         int       option_index = 0;
         static struct option long_options[] = {
             {"daemon", required_argument, 0, 'd'},
+            {"noflush", no_argument, 0, 'F'},
             {0, 0, 0, 0}
         };
 
-        opt = getopt_long(argc, argv, "d:", long_options, &option_index);
+        opt = getopt_long(argc, argv, "d:F", long_options, &option_index);
 
         if (opt == EOF)
             break;
@@ -44,8 +47,12 @@ time_t rrd_last(
             }
             break;
 
+        case 'F':
+            flushfirst = 0;
+            break;
+
         default:
-            rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+            rrd_set_error ("Usage: rrdtool %s [--daemon <addr> [--noflush]] <file>",
                     argv[0]);
             return (-1);
             break;
@@ -53,16 +60,24 @@ time_t rrd_last(
     }                   /* while (42) */
 
     if ((argc - optind) != 1) {
-        rrd_set_error ("Usage: rrdtool %s [--daemon <addr>] <file>",
+        rrd_set_error ("Usage: rrdtool %s [--daemon <addr> [--noflush]] <file>",
                 argv[0]);
         return (-1);
     }
 
+    if(flushfirst) {
     status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
-    if (opt_daemon) free(opt_daemon);
     if (status) return (-1);
+    }
+
+    rrdc_connect (opt_daemon);
+    if (rrdc_is_connected (opt_daemon))
+        lastupdate = rrdc_last (argv[optind]);
+
+    else
+        lastupdate = rrd_last_r(argv[optind]);
 
-    return (rrd_last_r (argv[optind]));
+    return (lastupdate);
 }
 
 time_t rrd_last_r(