[PATCH 1 of 7] * Introduce rrdc_connection_t

Peter Stamfest <[email protected]>
Newsgroups gmane.comp.db.rrdtool.devel
Message-ID <[email protected]>
# HG changeset patch
# User Peter Stamfest <[email protected]>
# Date 1299524364 -3600
# Node ID 2e1ec35bef337eee041078d583cfe5aaa7068509
# Parent  4fcecd56ec3b1091c9fd649d3d52aa86a679eb02
* Introduce rrdc_connection_t
  - bundles all the data needed for a rrdcached connection
  - remove global variables used until now
* Use rrdc_connection_t throughout rrdtool
  - sematic changes:
    - The last connection to a rrdcached is not kept open, thus multiple
      requests to the same rrdcached use different connections.

diff --git a/src/rrd_client.c b/src/rrd_client.c
--- a/src/rrd_client.c
+++ b/src/rrd_client.c
@@ -25,6 +25,7 @@
  *   Sebastian tokkee Harl <sh at tokkee.org>
  **/
 
+
 #include "rrd.h"
 #include "rrd_tool.h"
 #include "rrd_client.h"
@@ -55,10 +56,6 @@
 };
 typedef struct rrdc_response_s rrdc_response_t;
 
-static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-static int sd = -1;
-static FILE *sh = NULL;
-static char *sd_path = NULL; /* cache the path for sd */
 
 /* get_path: Return a path name appropriate to be sent to the daemon.
  *
@@ -68,16 +65,16 @@
  * are not allowed, since path name translation is done by the server.
  *
  * One must hold `lock' when calling this function. */
-static const char *get_path (const char *path, char *resolved_path) /* {{{ */
+static const char *get_path (rrdc_connection_t *conn, const char *path, char *resolved_path) /* {{{ */
 {
   const char *ret = path;
   int is_unix = 0;
 
-  if ((path == NULL) || (resolved_path == NULL) || (sd_path == NULL))
+  if ((conn == NULL) || (path == NULL) || (resolved_path == NULL) || (conn->sd_path == NULL))
     return (NULL);
 
-  if ((*sd_path == '/')
-      || (strncmp ("unix:", sd_path, strlen ("unix:")) == 0))
+  if ((*(conn->sd_path) == '/')
+      || (strncmp ("unix:", conn->sd_path, strlen ("unix:")) == 0))
     is_unix = 1;
 
   if (is_unix)
@@ -232,23 +229,23 @@
 } /* }}} int parse_value_array_header */
 
 /* One must hold `lock' when calling `close_connection'. */
-static void close_connection (void) /* {{{ */
+static void close_connection (rrdc_connection_t *conn) /* {{{ */
 {
-  if (sh != NULL)
+  if (conn->sh != NULL)
   {
-    fclose (sh);
-    sh = NULL;
-    sd = -1;
+    fclose (conn->sh);
+    conn->sh = NULL;
+    conn->sd = -1;
   }
-  else if (sd >= 0)
+  else if (conn->sd >= 0)
   {
-    close (sd);
-    sd = -1;
+    close (conn->sd);
+    conn->sd = -1;
   }
 
-  if (sd_path != NULL)
-    free (sd_path);
-  sd_path = NULL;
+  if (conn->sd_path != NULL)
+    free (conn->sd_path);
+  conn->sd_path = NULL;
 } /* }}} void close_connection */
 
 static int buffer_add_string (const char *str, /* {{{ */
@@ -367,7 +364,7 @@
   free (res);
 } /* }}} void response_free */
 
-static int response_read (rrdc_response_t **ret_response) /* {{{ */
+static int response_read (rrdc_connection_t *conn, rrdc_response_t **ret_response) /* {{{ */
 {
   rrdc_response_t *ret = NULL;
   int status = 0;
@@ -379,7 +376,7 @@
 
 #define DIE(code) do { status = code; goto err_out; } while(0)
 
-  if (sh == NULL)
+  if (conn == NULL || conn->sh == NULL)
     DIE(-1);
 
   ret = (rrdc_response_t *) malloc (sizeof (rrdc_response_t));
@@ -389,7 +386,7 @@
   ret->lines = NULL;
   ret->lines_num = 0;
 
-  buffer_ptr = fgets (buffer, sizeof (buffer), sh);
+  buffer_ptr = fgets (buffer, sizeof (buffer), conn->sh);
   if (buffer_ptr == NULL)
     DIE(-3);
 
@@ -418,7 +415,7 @@
 
   for (i = 0; i < ret->lines_num; i++)
   {
-    buffer_ptr = fgets (buffer, sizeof (buffer), sh);
+    buffer_ptr = fgets (buffer, sizeof (buffer), conn->sh);
     if (buffer_ptr == NULL)
       DIE(-6);
 
@@ -431,39 +428,39 @@
 
 out:
   *ret_response = ret;
-  fflush(sh);
+  fflush(conn->sh);
   return (status);
 
 err_out:
   response_free(ret);
-  close_connection();
+  close_connection(conn);
   return (status);
 
 #undef DIE
 
 } /* }}} rrdc_response_t *response_read */
 
-static int request (const char *buffer, size_t buffer_size, /* {{{ */
+static int request (rrdc_connection_t *conn, const char *buffer, size_t buffer_size, /* {{{ */
     rrdc_response_t **ret_response)
 {
   int status;
   rrdc_response_t *res;
 
-  if (sh == NULL)
+  if (conn == NULL || conn->sh == NULL)
     return (ENOTCONN);
 
-  status = (int) fwrite (buffer, buffer_size, /* nmemb = */ 1, sh);
+  status = (int) fwrite (buffer, buffer_size, /* nmemb = */ 1, conn->sh);
   if (status != 1)
   {
-    close_connection ();
+    close_connection (conn);
     rrd_set_error("request: socket error (%d) while talking to rrdcached",
                   status);
     return (-1);
   }
-  fflush (sh);
+  fflush (conn->sh);
 
   res = NULL;
-  status = response_read (&res);
+  status = response_read (conn, &res);
 
   if (status != 0)
   {
@@ -479,9 +476,9 @@
 /* determine whether we are connected to the specified daemon_addr if
  * NULL, return whether we are connected at all
  */
-int rrdc_is_connected(const char *daemon_addr) /* {{{ */
+int rrdc_is_connected(rrdc_connection_t *conn, const char *daemon_addr) /* {{{ */
 {
-  if (sd < 0)
+  if (conn->sd < 0)
     return 0;
   else if (daemon_addr == NULL)
   {
@@ -496,23 +493,23 @@
     else
       return 0;
   }
-  else if (strcmp(daemon_addr, sd_path) == 0)
+  else if (strcmp(daemon_addr, conn->sd_path) == 0)
     return 1;
   else
     return 0;
 
 } /* }}} int rrdc_is_connected */
 
-static int rrdc_connect_unix (const char *path) /* {{{ */
+static int rrdc_connect_unix (rrdc_connection_t *conn, const char *path) /* {{{ */
 {
   struct sockaddr_un sa;
   int status;
 
   assert (path != NULL);
-  assert (sd == -1);
+  assert (conn->sd == -1);
 
-  sd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
-  if (sd < 0)
+  conn->sd = socket (PF_UNIX, SOCK_STREAM, /* protocol = */ 0);
+  if (conn->sd < 0)
   {
     status = errno;
     return (status);
@@ -522,26 +519,26 @@
   sa.sun_family = AF_UNIX;
   strncpy (sa.sun_path, path, sizeof (sa.sun_path) - 1);
 
-  status = connect (sd, (struct sockaddr *) &sa, sizeof (sa));
+  status = connect (conn->sd, (struct sockaddr *) &sa, sizeof (sa));
   if (status != 0)
   {
     status = errno;
-    close_connection ();
+    close_connection (conn);
     return (status);
   }
 
-  sh = fdopen (sd, "r+");
-  if (sh == NULL)
+  conn->sh = fdopen (conn->sd, "r+");
+  if (conn->sh == NULL)
   {
     status = errno;
-    close_connection ();
+    close_connection (conn);
     return (status);
   }
 
   return (0);
 } /* }}} int rrdc_connect_unix */
 
-static int rrdc_connect_network (const char *addr_orig) /* {{{ */
+static int rrdc_connect_network (rrdc_connection_t *conn, const char *addr_orig) /* {{{ */
 {
   struct addrinfo ai_hints;
   struct addrinfo *ai_res;
@@ -550,8 +547,9 @@
   char *addr;
   char *port;
 
+  assert (conn != NULL);
   assert (addr_orig != NULL);
-  assert (sd == -1);
+  assert (conn->sd == -1);
 
   strncpy(addr_copy, addr_orig, sizeof(addr_copy));
   addr_copy[sizeof(addr_copy) - 1] = '\0';
@@ -615,27 +613,27 @@
 
   for (ai_ptr = ai_res; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
   {
-    sd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
-    if (sd < 0)
+    conn->sd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+    if (conn->sd < 0)
     {
       status = errno;
-      sd = -1;
+      conn->sd = -1;
       continue;
     }
 
-    status = connect (sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+    status = connect (conn->sd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
     if (status != 0)
     {
       status = errno;
-      close_connection();
+      close_connection(conn);
       continue;
     }
 
-    sh = fdopen (sd, "r+");
-    if (sh == NULL)
+    conn->sh = fdopen (conn->sd, "r+");
+    if (conn->sh == NULL)
     {
       status = errno;
-      close_connection ();
+      close_connection (conn);
       continue;
     }
 
@@ -648,7 +646,7 @@
   return (status);
 } /* }}} int rrdc_connect_network */
 
-int rrdc_connect (const char *addr) /* {{{ */
+int rrdc_connect (rrdc_connection_t *conn, const char *addr) /* {{{ */
 {
   int status = 0;
 
@@ -658,29 +656,31 @@
   if (addr == NULL)
     return 0;
 
-  pthread_mutex_lock(&lock);
+  assert (conn != NULL);
 
-  if (sd >= 0 && sd_path != NULL && strcmp(addr, sd_path) == 0)
+  pthread_mutex_lock(&conn->lock);
+
+  if (conn->sd >= 0 && conn->sd_path != NULL && strcmp(addr, conn->sd_path) == 0)
   {
     /* connection to the same daemon; use cached connection */
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     return (0);
   }
   else
   {
-    close_connection();
+    close_connection(conn);
   }
 
   rrd_clear_error ();
   if (strncmp ("unix:", addr, strlen ("unix:")) == 0)
-    status = rrdc_connect_unix (addr + strlen ("unix:"));
+    status = rrdc_connect_unix (conn, addr + strlen ("unix:"));
   else if (addr[0] == '/')
-    status = rrdc_connect_unix (addr);
+    status = rrdc_connect_unix (conn, addr);
   else
-    status = rrdc_connect_network(addr);
+    status = rrdc_connect_network(conn, addr);
 
-  if (status == 0 && sd >= 0)
-    sd_path = strdup(addr);
+  if (status == 0 && conn->sd >= 0)
+    conn->sd_path = strdup(addr);
   else
   {
     char *err = rrd_test_error () ? rrd_get_error () : "Internal error";
@@ -695,22 +695,23 @@
       free (err);
   }
 
-  pthread_mutex_unlock (&lock);
+  pthread_mutex_unlock (&conn->lock);
   return (status);
 } /* }}} int rrdc_connect */
 
-int rrdc_disconnect (void) /* {{{ */
+int rrdc_disconnect (rrdc_connection_t *conn) /* {{{ */
 {
-  pthread_mutex_lock (&lock);
+  pthread_mutex_lock (&conn->lock);
 
-  close_connection();
+  close_connection(conn);
 
-  pthread_mutex_unlock (&lock);
+  pthread_mutex_unlock (&conn->lock);
 
   return (0);
 } /* }}} int rrdc_disconnect */
 
-int rrdc_update (const char *filename, int values_num, /* {{{ */
+int rrdc_update (rrdc_connection_t *conn,
+                 const char *filename, int values_num, /* {{{ */
 		const char * const *values)
 {
   char buffer[RRD_CMD_MAX];
@@ -730,18 +731,18 @@
   if (status != 0)
     return (ENOBUFS);
 
-  pthread_mutex_lock (&lock);
-  filename = get_path (filename, file_path);
+  pthread_mutex_lock (&conn->lock);
+  filename = get_path (conn, filename, file_path);
   if (filename == NULL)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     return (-1);
   }
 
   status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
   if (status != 0)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     return (ENOBUFS);
   }
 
@@ -750,7 +751,7 @@
     status = buffer_add_value (values[i], &buffer_ptr, &buffer_free);
     if (status != 0)
     {
-      pthread_mutex_unlock (&lock);
+      pthread_mutex_unlock (&conn->lock);
       return (ENOBUFS);
     }
   }
@@ -761,8 +762,8 @@
   buffer[buffer_size - 1] = '\n';
 
   res = NULL;
-  status = request (buffer, buffer_size, &res);
-  pthread_mutex_unlock (&lock);
+  status = request (conn, buffer, buffer_size, &res);
+  pthread_mutex_unlock (&conn->lock);
 
   if (status != 0)
     return (status);
@@ -773,7 +774,7 @@
   return (status);
 } /* }}} int rrdc_update */
 
-int rrdc_flush (const char *filename) /* {{{ */
+int rrdc_flush (rrdc_connection_t *conn, const char *filename) /* {{{ */
 {
   char buffer[RRD_CMD_MAX];
   char *buffer_ptr;
@@ -794,18 +795,18 @@
   if (status != 0)
     return (ENOBUFS);
 
-  pthread_mutex_lock (&lock);
-  filename = get_path (filename, file_path);
+  pthread_mutex_lock (&conn->lock);
+  filename = get_path (conn, filename, file_path);
   if (filename == NULL)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     return (-1);
   }
 
   status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
   if (status != 0)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     return (ENOBUFS);
   }
 
@@ -815,8 +816,8 @@
   buffer[buffer_size - 1] = '\n';
 
   res = NULL;
-  status = request (buffer, buffer_size, &res);
-  pthread_mutex_unlock (&lock);
+  status = request (conn, buffer, buffer_size, &res);
+  pthread_mutex_unlock (&conn->lock);
 
   if (status != 0)
     return (status);
@@ -827,7 +828,7 @@
   return (status);
 } /* }}} int rrdc_flush */
 
-rrd_info_t * rrdc_info (const char *filename) /* {{{ */
+rrd_info_t * rrdc_info (rrdc_connection_t *conn, const char *filename) /* {{{ */
 {
   char buffer[RRD_CMD_MAX];
   char *buffer_ptr;
@@ -857,18 +858,18 @@
     return (NULL);
   }
 
-  pthread_mutex_lock (&lock);
-  filename = get_path (filename, file_path);
+  pthread_mutex_lock (&conn->lock);
+  filename = get_path (conn, filename, file_path);
   if (filename == NULL)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     return (NULL);
   }
 
   status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
   if (status != 0)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     rrd_set_error ("rrdc_info: out of memory");
     return (NULL);
   }
@@ -879,8 +880,8 @@
   buffer[buffer_size - 1] = '\n';
 
   res = NULL;
-  status = request (buffer, buffer_size, &res);
-  pthread_mutex_unlock (&lock);
+  status = request (conn, buffer, buffer_size, &res);
+  pthread_mutex_unlock (&conn->lock);
 
   if (status != 0) {
     rrd_set_error ("rrdcached: %s", res->message);
@@ -928,7 +929,7 @@
   return (data);
 } /* }}} int rrdc_info */
 
-time_t rrdc_last (const char *filename) /* {{{ */
+time_t rrdc_last (rrdc_connection_t *conn, const char *filename) /* {{{ */
 {
   char buffer[RRD_CMD_MAX];
   char *buffer_ptr;
@@ -954,18 +955,18 @@
     return (-1);
   }
 
-  pthread_mutex_lock (&lock);
-  filename = get_path (filename, file_path);
+  pthread_mutex_lock (&conn->lock);
+  filename = get_path (conn, filename, file_path);
   if (filename == NULL)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     return (-1);
   }
 
   status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
   if (status != 0)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     rrd_set_error ("rrdc_last: out of memory");
     return (-1);
   }
@@ -976,8 +977,8 @@
   buffer[buffer_size - 1] = '\n';
 
   res = NULL;
-  status = request (buffer, buffer_size, &res);
-  pthread_mutex_unlock (&lock);
+  status = request (conn, buffer, buffer_size, &res);
+  pthread_mutex_unlock (&conn->lock);
 
   if (status != 0) {
     rrd_set_error ("rrdcached: %s", res->message);
@@ -989,7 +990,7 @@
   return (lastup);
 } /* }}} int rrdc_last */
 
-time_t rrdc_first (const char *filename, int rraindex) /* {{{ */
+time_t rrdc_first (rrdc_connection_t *conn, const char *filename, int rraindex) /* {{{ */
 {
   char buffer[RRD_CMD_MAX];
   char *buffer_ptr;
@@ -1015,25 +1016,25 @@
     return (-1);
   }
 
-  pthread_mutex_lock (&lock);
-  filename = get_path (filename, file_path);
+  pthread_mutex_lock (&conn->lock);
+  filename = get_path (conn, filename, file_path);
   if (filename == NULL)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     return (-1);
   }
 
   status = buffer_add_string (filename, &buffer_ptr, &buffer_free);
   if (status != 0)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->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);
+    pthread_mutex_unlock (&conn->lock);
     rrd_set_error ("rrdc_first: out of memory");
     return (-1);
   }
@@ -1044,8 +1045,8 @@
   buffer[buffer_size - 1] = '\n';
 
   res = NULL;
-  status = request (buffer, buffer_size, &res);
-  pthread_mutex_unlock (&lock);
+  status = request (conn, buffer, buffer_size, &res);
+  pthread_mutex_unlock (&conn->lock);
 
   if (status != 0) {
     rrd_set_error ("rrdcached: %s", res->message);
@@ -1057,7 +1058,7 @@
   return (firstup);
 } /* }}} int rrdc_first */
 
-int rrdc_create (const char *filename, /* {{{ */
+int rrdc_create (rrdc_connection_t *conn, const char *filename, /* {{{ */
     unsigned long pdp_step,
     time_t last_up,
     int no_overwrite,
@@ -1088,11 +1089,11 @@
     return (-1);
   }
 
-  pthread_mutex_lock (&lock);
-  filename = get_path (filename, file_path);
+  pthread_mutex_lock (&conn->lock);
+  filename = get_path (conn, filename, file_path);
   if (filename == NULL)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     return (-1);
   }
 
@@ -1106,7 +1107,7 @@
   }
   if (status != 0)
   {
-    pthread_mutex_unlock (&lock);
+    pthread_mutex_unlock (&conn->lock);
     rrd_set_error ("rrdc_create: out of memory");
     return (-1);
   }
@@ -1116,7 +1117,7 @@
       status = buffer_add_string (argv[i], &buffer_ptr, &buffer_free);
       if (status != 0)
       {
-        pthread_mutex_unlock (&lock);
+        pthread_mutex_unlock (&conn->lock);
         rrd_set_error ("rrdc_create: out of memory");
         return (-1);
       }
@@ -1130,8 +1131,8 @@
   buffer[buffer_size - 1] = '\n';
 
   res = NULL;
-  status = request (buffer, buffer_size, &res);
-  pthread_mutex_unlock (&lock);
+  status = request (conn, buffer, buffer_size, &res);
+  pthread_mutex_unlock (&conn->lock);
 
   if (status != 0) {
     rrd_set_error ("rrdcached: %s", res->message);
@@ -1141,7 +1142,7 @@
   return(0);
 } /* }}} int rrdc_create */
 
-int rrdc_fetch (const char *filename, /* {{{ */
+int rrdc_fetch (rrdc_connection_t *conn, const char *filename, /* {{{ */
     const char *cf,
     time_t *ret_start, time_t *ret_end,
     unsigned long *ret_step,
@@ -1187,7 +1188,7 @@
     return (ENOBUFS);
 
   /* change to path for rrdcached */
-  path_ptr = get_path (filename, path_buffer);
+  path_ptr = get_path (conn, filename, path_buffer);
   if (path_ptr == NULL)
     return (EINVAL);
 
@@ -1224,7 +1225,7 @@
   buffer[buffer_size - 1] = '\n';
 
   res = NULL;
-  status = request (buffer, buffer_size, &res);
+  status = request (conn, buffer, buffer_size, &res);
   if (status != 0)
     return (status);
 
@@ -1354,16 +1355,16 @@
 /* convenience function; if there is a daemon specified, or if we can
  * detect one from the environment, then flush the file.  Otherwise, no-op
  */
-int rrdc_flush_if_daemon (const char *opt_daemon, const char *filename) /* {{{ */
+int rrdc_flush_if_daemon (rrdc_connection_t *conn, const char *opt_daemon, const char *filename) /* {{{ */
 {
   int status = 0;
 
-  rrdc_connect(opt_daemon);
+  rrdc_connect(conn, opt_daemon);
 
-  if (rrdc_is_connected(opt_daemon))
+  if (rrdc_is_connected(conn, opt_daemon))
   {
     rrd_clear_error();
-    status = rrdc_flush (filename);
+    status = rrdc_flush (conn, filename);
 
     if (status != 0 && !rrd_test_error())
     {
@@ -1384,7 +1385,7 @@
 } /* }}} int rrdc_flush_if_daemon */
 
 
-int rrdc_stats_get (rrdc_stats_t **ret_stats) /* {{{ */
+int rrdc_stats_get (rrdc_connection_t *conn, rrdc_stats_t **ret_stats) /* {{{ */
 {
   rrdc_stats_t *head;
   rrdc_stats_t *tail;
@@ -1405,9 +1406,9 @@
    * }}} */
 
   res = NULL;
-  pthread_mutex_lock (&lock);
-  status = request ("STATS\n", strlen ("STATS\n"), &res);
-  pthread_mutex_unlock (&lock);
+  pthread_mutex_lock (&conn->lock);
+  status = request (conn, "STATS\n", strlen ("STATS\n"), &res);
+  pthread_mutex_unlock (&conn->lock);
 
   if (status != 0)
     return (status);
@@ -1519,6 +1520,82 @@
   } /* while (this != NULL) */
 } /* }}} void rrdc_stats_free */
 
+rrdc_connection_t *new_rrdc_connection(const char *addr) {
+  rrdc_connection_t *c = malloc(sizeof(rrdc_connection_t));
+  if (c == NULL) {
+    rrd_set_error("out of memory");
+    return NULL;
+  }
+
+  pthread_mutex_init(&c->lock, NULL);
+  c->sd = -1;
+  c->sh = NULL;
+  c->sd_path = NULL;
+
+  if (addr != NULL) {
+    int status = rrdc_connect(c, addr);
+    if (status != 0) {
+      rrd_set_error("Cannot connect to rrdcached '%s'. Please use an "
+          "appropriate daemon address for the desired operation. "
+          "This might involve using the --daemon option, setting "
+          "the environment option '%s' or using a proper "
+          "rrdfile specification", addr, ENV_RRDCACHED_ADDRESS);
+      delete_rrdc_connection(c);
+      return NULL;
+    }
+  }
+
+  return c;
+}
+
+void delete_rrdc_connection(rrdc_connection_t *conn) {
+  if (conn == NULL) return;
+  rrdc_disconnect(conn);
+  free(conn);
+}
+
+
+const char *want_rrdc_connection(const char *addr) {
+  if (addr != NULL) return addr;
+  return getenv(ENV_RRDCACHED_ADDRESS);
+}
+
+
+static rrdc_connection_t *global = NULL;
+
+int setup_global_rrdc_connection(const char *addr) {
+  if (addr == NULL) {
+    rrd_set_error("Need proper rrdcached address");
+    return -1;
+  }
+
+  if (global != NULL) {
+    /* special case: if addr is the same as during the last invocation, reuse
+     * connection, otherwise close old and create new connection.
+     */
+    if (global->sd >= 0 && global->sd_path != NULL &&
+        strcmp(addr, global->sd_path) == 0) {
+      return 0;
+    }
+
+    delete_rrdc_connection(global);
+    global = NULL;
+  }
+
+  global = new_rrdc_connection(addr);
+  if (global == NULL) {
+    // error already set by new_rrdc_connection
+    return -1;
+  }
+
+  return 0;
+}
+
+rrdc_connection_t *get_global_rrdc_connection(void) {
+  return global;
+}
+
+
 /*
  * vim: set sw=2 sts=2 ts=8 et fdm=marker :
  */
diff --git a/src/rrd_client.h b/src/rrd_client.h
--- a/src/rrd_client.h
+++ b/src/rrd_client.h
@@ -63,17 +63,28 @@
 // Windows version has no daemon/client support
 
 #ifndef WIN32
-int rrdc_connect (const char *addr);
-int rrdc_is_connected(const char *daemon_addr);
-int rrdc_disconnect (void);
 
-int rrdc_update (const char *filename, int values_num,
+struct rrdc_connection_s {
+	pthread_mutex_t lock;
+	int sd;
+	FILE *sh;
+	char *sd_path; /* cache the path for sd */
+};
+
+typedef struct rrdc_connection_s rrdc_connection_t;
+
+
+int rrdc_connect (rrdc_connection_t *conn, const char *addr);
+int rrdc_is_connected(rrdc_connection_t *conn, const char *daemon_addr);
+int rrdc_disconnect (rrdc_connection_t *conn);
+
+int rrdc_update (rrdc_connection_t *conn, 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,
+rrd_info_t * rrdc_info (rrdc_connection_t *conn, const char *filename);
+time_t rrdc_last (rrdc_connection_t *conn, const char *filename);
+time_t rrdc_first (rrdc_connection_t *conn, const char *filename, int rraindex);
+int rrdc_create (rrdc_connection_t *conn, const char *filename,
     unsigned long pdp_step,
     time_t last_up,
     int no_overwrite,
@@ -81,10 +92,10 @@
     const char **argv);
 
 
-int rrdc_flush (const char *filename);
-int rrdc_flush_if_daemon (const char *opt_daemon, const char *filename);
+int rrdc_flush (rrdc_connection_t *conn, const char *filename);
+int rrdc_flush_if_daemon (rrdc_connection_t *conn, const char *opt_daemon, const char *filename);
 
-int rrdc_fetch (const char *filename,
+int rrdc_fetch (rrdc_connection_t *conn, const char *filename,
     const char *cf,
     time_t *ret_start, time_t *ret_end,
     unsigned long *ret_step,
@@ -92,12 +103,24 @@
     char ***ret_ds_names,
     rrd_value_t **ret_data);
 
+
+rrdc_connection_t * new_rrdc_connection(const char *addr);
+void delete_rrdc_connection(rrdc_connection_t *conn);
+
+const char *want_rrdc_connection(const char *addr);
+
+int setup_global_rrdc_connection(const char *addr);
+rrdc_connection_t *get_global_rrdc_connection(void);
+
 #else
-#	define rrdc_flush_if_daemon(a,b) 0
-#	define rrdc_connect(a) 0
+#	define rrdc_flush_if_daemon(a,b,c) 0
+#	define rrdc_connect(a,b) 0
 #	define rrdc_is_connected(a) 0
 #	define rrdc_flush(a) 0
-#	define rrdc_update(a,b,c) 0
+#	define rrdc_update(a,b,c,d) 0
+#	define new_rrdc_connection() 0
+#	define delete_rrdc_connection(a) 0
+#       define want_rrdc_connection(a) NULL
 #endif
 
 struct rrdc_stats_s
@@ -116,7 +139,7 @@
 };
 typedef struct rrdc_stats_s rrdc_stats_t;
 
-int rrdc_stats_get (rrdc_stats_t **ret_stats);
+int rrdc_stats_get (rrdc_connection_t *conn, rrdc_stats_t **ret_stats);
 void rrdc_stats_free (rrdc_stats_t *ret_stats);
 
 #endif /* __RRD_CLIENT_H */
diff --git a/src/rrd_create.c b/src/rrd_create.c
--- a/src/rrd_create.c
+++ b/src/rrd_create.c
@@ -123,17 +123,26 @@
         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_r2(argv[optind],
-                      pdp_step, last_up, opt_no_overwrite,
-                      argc - optind - 1, (const char **) (argv + optind + 1));
-	}
+    const char *addr = want_rrdc_connection(opt_daemon);
+    if (addr != NULL) {
+      rrdc_connection_t *conn = new_rrdc_connection(addr);
+      if (conn == NULL) {
+        rc = -1;
+        goto done;
+      }
+      rc = rrdc_create (conn, argv[optind],
+                        pdp_step, last_up, opt_no_overwrite,
+                        argc - optind - 1, (const char **) (argv + optind + 1));
 
+      delete_rrdc_connection(conn);
+      goto done;
+    }
+
+    rc = rrd_create_r2(argv[optind],
+                       pdp_step, last_up, opt_no_overwrite,
+                       argc - optind - 1, (const char **) (argv + optind + 1));
+done:
+    if (opt_daemon != NULL) free(opt_daemon);
     return rc;
 }
 
diff --git a/src/rrd_dump.c b/src/rrd_dump.c
--- a/src/rrd_dump.c
+++ b/src/rrd_dump.c
@@ -576,9 +576,15 @@
         return (-1);
     }
 
-    rc = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
-    if (opt_daemon) free(opt_daemon);
-    if (rc) return (rc);
+    const char *addr = want_rrdc_connection(opt_daemon);
+    if (addr != NULL) {
+        rrdc_connection_t *conn = new_rrdc_connection(addr);
+        if (conn != NULL) {
+            rc = rrdc_flush(conn, argv[optind]);
+            delete_rrdc_connection(conn);
+            if (rc) goto done;
+        }
+    }
 
     if ((argc - optind) == 2) {
         rc = rrd_dump_opt_r(argv[optind], argv[optind + 1], opt_header);
@@ -586,5 +592,7 @@
         rc = rrd_dump_opt_r(argv[optind], NULL, opt_header);
     }
 
+done:
+    if (opt_daemon) free(opt_daemon);
     return rc;
 }
diff --git a/src/rrd_fetch.c b/src/rrd_fetch.c
--- a/src/rrd_fetch.c
+++ b/src/rrd_fetch.c
@@ -3,7 +3,7 @@
  *****************************************************************************
  * rrd_fetch.c  read date from an rrd to use for further processing
  *****************************************************************************
- * $Id: rrd_fetch.c 2176 2011-03-03 06:53:05Z oetiker $
+ * $Id: rrd_fetch.c 2060 2010-03-29 17:05:33Z oetiker $
  * $Log$
  * Revision 1.8  2004/05/18 18:53:03  oetiker
  * big spell checking patch -- [email protected]
@@ -74,7 +74,7 @@
     time_t    start_tmp = 0, end_tmp = 0;
     const char *cf;
     char *opt_daemon = NULL;
-    int status;
+    int status = -1;
 
     rrd_time_value_t start_tv, end_tv;
     char     *parsetime_error = NULL;
@@ -169,14 +169,22 @@
 
     cf = argv[optind + 1];
 
-    rrdc_connect (opt_daemon);
-    if (rrdc_is_connected (opt_daemon))
-	    status = rrdc_fetch (argv[optind], cf, start, end, step,
-			    ds_cnt, ds_namv, data);
+    const char *addr = want_rrdc_connection(opt_daemon);
+    if (addr != NULL) {
+        rrdc_connection_t *conn = new_rrdc_connection(addr);
+        if (conn != NULL) {
+            status = rrdc_fetch (conn, argv[optind], cf, start, end, step,
+                                 ds_cnt, ds_namv, data);
+            delete_rrdc_connection(conn);
+        }
+    }
+    else
+    {
+      status = rrd_fetch_r(argv[optind], cf, start, end, step,
+                           ds_cnt, ds_namv, data);
+    }
 
-    else
-	    status = rrd_fetch_r(argv[optind], cf, start, end, step,
-			    ds_cnt, ds_namv, data);
+    if (opt_daemon != NULL) free(opt_daemon);
 
     if (status != 0)
         return (-1);
diff --git a/src/rrd_first.c b/src/rrd_first.c
--- a/src/rrd_first.c
+++ b/src/rrd_first.c
@@ -23,6 +23,7 @@
         {"daemon", required_argument, 0, 'd'},
         {0, 0, 0, 0}
     };
+    time_t result = -1;
 
     optind = 0;
     opterr = 0;         /* initialize getopt */
@@ -67,12 +68,19 @@
         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));
-	}
+    const char *addr = want_rrdc_connection(opt_daemon);
+    if (addr != NULL) {
+      rrdc_connection_t *conn = new_rrdc_connection(addr);
+      if (conn != NULL) {
+        result = rrdc_first (conn, argv[optind], target_rraindex);
+        delete_rrdc_connection(conn);
+      }
+      goto done;
+    }
+    result = rrd_first_r(argv[optind], target_rraindex);
+done:
+    if (opt_daemon != NULL) free(opt_daemon);
+    return result;
 }
 
 
diff --git a/src/rrd_flushcached.c b/src/rrd_flushcached.c
--- a/src/rrd_flushcached.c
+++ b/src/rrd_flushcached.c
@@ -73,15 +73,8 @@
     }
 
     /* try to connect to rrdcached */
-    status = rrdc_connect(opt_daemon);
-    if (status != 0) goto out;
-
-    if (! rrdc_is_connected(opt_daemon))
-    {
-        rrd_set_error ("Daemon address unknown. Please use the \"--daemon\" "
-                "option to set an address on the command line or set the "
-                "\"%s\" environment variable.",
-                ENV_RRDCACHED_ADDRESS);
+    rrdc_connection_t *conn = new_rrdc_connection(opt_daemon);
+    if (conn == NULL) {
         status = -1;
         goto out;
     }
@@ -89,7 +82,7 @@
     status = 0;
     for (i = optind; i < argc; i++)
     {
-        status = rrdc_flush(argv[i]);
+        status = rrdc_flush(conn, argv[i]);
         if (status)
         {
             char *error;
@@ -108,6 +101,7 @@
     }
 
 out:
+    if (conn != NULL) delete_rrdc_connection(conn);
     if (opt_daemon) free(opt_daemon);
 
     return status;
diff --git a/src/rrd_graph.c b/src/rrd_graph.c
--- a/src/rrd_graph.c
+++ b/src/rrd_graph.c
@@ -862,41 +862,40 @@
             else
                 rrd_daemon = im->daemon_addr;
 
-            /* "daemon" may be NULL. ENV_RRDCACHED_ADDRESS is evaluated in that
-             * case. If "daemon" holds the same value as in the previous
-             * iteration, no actual new connection is established - the
-             * existing connection is re-used. */
-            rrdc_connect (rrd_daemon);
-
-            /* If connecting was successfull, use the daemon to query the data.
-             * If there is no connection, for example because no daemon address
-             * was specified, (try to) use the local file directly. */
-            if (rrdc_is_connected (rrd_daemon))
-            {
-                status = rrdc_fetch (im->gdes[i].rrd,
-                        cf_to_string (im->gdes[i].cf),
-                        &im->gdes[i].start,
-                        &im->gdes[i].end,
-                        &ft_step,
-                        &im->gdes[i].ds_cnt,
-                        &im->gdes[i].ds_namv,
-                        &im->gdes[i].data);
-                if (status != 0)
-                    return (status);
-            }
-            else
-            {
-                if ((rrd_fetch_fn(im->gdes[i].rrd,
-                                im->gdes[i].cf,
-                                &im->gdes[i].start,
-                                &im->gdes[i].end,
-                                &ft_step,
-                                &im->gdes[i].ds_cnt,
-                                &im->gdes[i].ds_namv,
-                                &im->gdes[i].data)) == -1) {
-                    return -1;
+            const char *addr = want_rrdc_connection(rrd_daemon);
+            if (addr != NULL) {
+                rrdc_connection_t *conn = new_rrdc_connection(addr);
+                if (conn != NULL) {
+                    status = rrdc_fetch (conn, im->gdes[i].rrd,
+                                         cf_to_string (im->gdes[i].cf),
+                                         &im->gdes[i].start,
+                                         &im->gdes[i].end,
+                                         &ft_step,
+                                         &im->gdes[i].ds_cnt,
+                                         &im->gdes[i].ds_namv,
+                                         &im->gdes[i].data);
+
+                    delete_rrdc_connection(conn);
+
+                    if (status != 0)
+                        return (status);
+
+                } else {
+                    // try to read data locally
                 }
             }
+
+            if ((rrd_fetch_fn(im->gdes[i].rrd,
+                              im->gdes[i].cf,
+                              &im->gdes[i].start,
+                              &im->gdes[i].end,
+                              &ft_step,
+                              &im->gdes[i].ds_cnt,
+                              &im->gdes[i].ds_namv,
+                              &im->gdes[i].data)) == -1) {
+                return -1;
+            }
+
             im->gdes[i].data_first = 1;
 
             if (ft_step < im->gdes[i].step) {
diff --git a/src/rrd_info.c b/src/rrd_info.c
--- a/src/rrd_info.c
+++ b/src/rrd_info.c
@@ -141,18 +141,31 @@
         return (NULL);
     }
 
-    if( flushfirst ) {
-    status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
-    if (status) return (NULL);
+    const char *addr = want_rrdc_connection(opt_daemon);
+    rrdc_connection_t *conn = NULL;
+
+    if (addr != NULL) {
+        conn = new_rrdc_connection(addr);
+        if (conn == NULL) {
+            return NULL;
+        }
     }
 
-    rrdc_connect (opt_daemon);
-    if (rrdc_is_connected (opt_daemon))
-        info = rrdc_info (argv[optind]);
+    if( flushfirst && conn != NULL) {
+        status = rrdc_flush(conn, argv[optind]);
+        if (status) return (NULL);
+    }
+
+    if (conn != NULL) {
+        info = rrdc_info (conn, argv[optind]);
+    }
     else
-    info = rrd_info_r(argv[optind]);
+        info = rrd_info_r(argv[optind]);
 
     if (opt_daemon) free(opt_daemon);
+    if (conn != NULL) {
+        delete_rrdc_connection(conn);
+    }
     return (info);
 } /* rrd_info_t *rrd_info */
 
diff --git a/src/rrd_last.c b/src/rrd_last.c
--- a/src/rrd_last.c
+++ b/src/rrd_last.c
@@ -14,7 +14,7 @@
     char **argv)
 {
     char *opt_daemon = NULL;
-    time_t lastupdate;
+    time_t lastupdate = -1;
 
     optind = 0;
     opterr = 0;         /* initialize getopt */
@@ -58,10 +58,14 @@
         return (-1);
     }
 
-    rrdc_connect (opt_daemon);
-    if (rrdc_is_connected (opt_daemon))
-        lastupdate = rrdc_last (argv[optind]);
-
+    const char *addr = want_rrdc_connection(opt_daemon);
+    if (addr != NULL) {
+      rrdc_connection_t *conn = new_rrdc_connection(addr);
+      if (conn != NULL) {
+        lastupdate = rrdc_last (conn, argv[optind]);
+        delete_rrdc_connection(conn);
+      }
+    }
     else
         lastupdate = rrd_last_r(argv[optind]);
 
diff --git a/src/rrd_lastupdate.c b/src/rrd_lastupdate.c
--- a/src/rrd_lastupdate.c
+++ b/src/rrd_lastupdate.c
@@ -16,7 +16,7 @@
     char    **ds_names;
     char    **last_ds;
     unsigned long ds_count, i;
-    int status;
+    int status = 0;
 
     char *opt_daemon = NULL;
 
@@ -62,7 +62,14 @@
         return (-1);
     }
 
-    status = rrdc_flush_if_daemon(opt_daemon, argv[optind]);
+    const char *addr = want_rrdc_connection(opt_daemon);
+    if (addr != NULL) {
+      rrdc_connection_t *conn = new_rrdc_connection(addr);
+      if (conn != NULL) {
+        status = rrdc_flush (conn, argv[optind]);
+        delete_rrdc_connection(conn);
+      }
+    }
     if (opt_daemon) free (opt_daemon);
     if (status) return (-1);
 
diff --git a/src/rrd_update.c b/src/rrd_update.c
--- a/src/rrd_update.c
+++ b/src/rrd_update.c
@@ -389,6 +389,8 @@
     char     *tmplt = NULL;
     int       rc = -1;
     char     *opt_daemon = NULL;
+    rrdc_connection_t *conn = NULL;
+    const char *addr = NULL;
 
     optind = 0;
     opterr = 0;         /* initialize getopt */
@@ -427,29 +429,32 @@
         goto out;
     }
 
-    {   /* try to connect to rrdcached */
-        int status = rrdc_connect(opt_daemon);
-        if (status != 0) {        
-             rc = status;           
-             goto out;           
-        }        
+
+    addr = want_rrdc_connection(opt_daemon);
+
+    if (addr != NULL) {
+        conn = new_rrdc_connection(addr);
+        if (conn == NULL) {
+            rc = -1;
+            goto out;
+        }
     }
 
-    if ((tmplt != NULL) && rrdc_is_connected(opt_daemon))
+    if ((tmplt != NULL) && conn != NULL)
     {
         rrd_set_error("The caching daemon cannot be used together with "
                 "templates yet.");
         goto out;
     }
 
-    if (! rrdc_is_connected(opt_daemon))
+    if (conn == NULL)
     {
       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 */
+        rc = rrdc_update (conn, argv[optind], /* file */
                           argc - optind - 1, /* values_num */
                           (const char *const *) (argv + optind + 1)); /* values */
         if (rc > 0)
@@ -468,6 +473,10 @@
         free (opt_daemon);
         opt_daemon = NULL;
     }
+    if (conn != NULL) {
+        delete_rrdc_connection(conn);
+        conn = NULL;
+    }
     return rc;
 }
 
diff --git a/src/rrd_xport.c b/src/rrd_xport.c
--- a/src/rrd_xport.c
+++ b/src/rrd_xport.c
@@ -167,9 +167,12 @@
         return (-1);
     }
 
-    {   /* try to connect to rrdcached */
-        int status = rrdc_connect(im.daemon_addr);
-        if (status != 0) return status;
+    const char *addr = want_rrdc_connection(im.daemon_addr);
+    if (addr != NULL) {   /* try to connect to rrdcached */
+        rrdc_connection_t *conn = new_rrdc_connection(addr);
+        if (conn == NULL) {
+            return -1;
+        }
     }
 
     if (rrd_xport_fn(&im, start, end, step, col_cnt, legend_v, data) == -1) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.