fix for the memory leak in info fixes the memory leak in "INFO".
[rrdtool.git] / src / rrd_daemon.c
index 2209320..0dc8e0b 100644 (file)
@@ -71,7 +71,7 @@
 #endif
 #endif
 
-#include "rrd.h"
+#include "rrd_tool.h"
 #include "rrd_client.h"
 #include "unused.h"
 
@@ -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; /* default for the daemon */
+
 /* Journaled updates */
 #define JOURNAL_REPLAY(s) ((s) == NULL)
 #define JOURNAL_BASE "rrd.journal"
@@ -1695,6 +1697,192 @@ 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 *info;
+
+  /* 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 ();
+  info = rrd_info_r(file);
+  if(!info) {
+    return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error());
+  }
+  for (rrd_info_t *data = info; data != NULL; data = data->next) {
+      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;
+      }
+  }
+
+  rrd_info_free(info);
+
+  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_first  */
+
+
+static int handle_request_last (HANDLER_PROTO) /* {{{ */
+{
+  char *file, file_tmp[PATH_MAX];
+  int status;
+  time_t t, from_file, step;
+  rrd_file_t * rrd_file;
+  cache_item_t * ci;
+  rrd_t rrd; 
+
+  /* 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);
+  }
+  rrd_clear_error();
+  rrd_init(&rrd);
+  rrd_file = rrd_open(file,&rrd,RRD_READONLY);
+  if(!rrd_file) {
+    return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error());
+  }
+  from_file = rrd.live_head->last_up;
+  step = rrd.stat_head->pdp_step;
+  rrd_close(rrd_file);
+  pthread_mutex_lock(&cache_lock);
+  ci = g_tree_lookup(cache_tree, file);
+  if (ci)
+    t = ci->last_update_stamp;
+  else
+    t = from_file;
+  pthread_mutex_unlock(&cache_lock);
+  t -= t % step;
+  rrd_free(&rrd);
+  if(t<1) {
+    return send_response(sock, RESP_ERR, "Error: rrdcached: Invalid timestamp returned\n");
+  }
+  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;
+  unsigned long step = 300;
+  time_t last_up = time(NULL)-10;
+  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);
+
+  while ((status = buffer_get_field(&buffer, &buffer_size, &tok)) == 0 && tok) {
+    if( ! strncmp(tok,"-b",2) ) {
+      status = buffer_get_field(&buffer, &buffer_size, &tok );
+      if (status != 0) return syntax_error(sock,cmd);
+      last_up = (time_t) atol(tok);
+      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_clear_error ();
+  status = rrd_create_r2(file,step,last_up,no_overwrite,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) /* {{{ */
 {
@@ -1848,6 +2036,47 @@ 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 start parameter needs to be in seconds since 1/1/70 (AT-style syntax is\n"
+    "not acceptable) and the step is in seconds (default is 300).\n"
+    "The DS and RRA definitions are as for the 'rrdtool create' command.\n"
+  },
+  {
     "QUIT",
     handle_request_quit,
     CMD_CONTEXT_CLIENT | CMD_CONTEXT_BATCH,
@@ -2993,10 +3222,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 +3571,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"