Update for --socket option
[awesomized/libmemcached] / libtest / server.c
index c96ffb5153d546cab133f2cf7e1f230d55cc1719..dac57c775c8bd5f40fc9740ffd9cbc6d7bd63341 100644 (file)
@@ -1,23 +1,50 @@
-/* LibMemcached
- * Copyright (C) 2006-2009 Brian Aker
- * All rights reserved.
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Libmemcached library
  *
- * Use and distribution licensed under the BSD license.  See
- * the COPYING file in the parent directory for full text.
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *  Copyright (C) 2006-2009 Brian Aker All rights reserved.
  *
- * Summary:
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are
+ *  met:
+ *
+ *      * Redistributions of source code must retain the above copyright
+ *  notice, this list of conditions and the following disclaimer.
+ *
+ *      * Redistributions in binary form must reproduce the above
+ *  copyright notice, this list of conditions and the following disclaimer
+ *  in the documentation and/or other materials provided with the
+ *  distribution.
+ *
+ *      * The names of its contributors may not be used to endorse or
+ *  promote products derived from this software without specific prior
+ *  written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  */
 
+
 /*
   Startup, and shutdown the memcached servers.
 */
 
 #define TEST_PORT_BASE MEMCACHED_DEFAULT_PORT+10
 
-#define PID_FILE_BASE "/tmp/%ulibmemcached_memc.pid"
+#include <config.h>
 
-#include "config.h"
+#include <iso646.h>
 
 #include <assert.h>
 #include <limits.h>
@@ -28,6 +55,7 @@
 #include <sys/time.h>
 #include <time.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include <libmemcached/memcached.h>
 #include <libmemcached/util.h>
@@ -45,6 +73,27 @@ static void global_sleep(void)
 #endif
 }
 
+static bool wait_for_file(const char *filename)
+{
+  uint32_t timeout= 6;
+  uint32_t waited;
+  uint32_t this_wait;
+  uint32_t retry;
+
+  for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
+  {
+    if ((! access(filename, R_OK)) || (waited >= timeout))
+    {
+      return true;
+    }
+
+    this_wait= retry * retry / 3 + 1;
+    sleep(this_wait);
+  }
+
+  return false;
+}
+
 static void kill_file(const char *file_buffer)
 {
   FILE *fp;
@@ -95,10 +144,19 @@ void server_startup(server_startup_st *construct)
       char *end_ptr;
       end_ptr= server_string_buffer;
 
+      uint32_t port_base= 0;
       for (uint32_t x= 0; x < construct->count; x++)
       {
         int status;
-        in_port_t port;
+
+        snprintf(construct->pid_file[x], FILENAME_MAX, "/tmp/memcached.pidXXXXXX");
+        int fd;
+        if ((fd= mkstemp(construct->pid_file[x])) == -1)
+        {
+          perror("mkstemp");
+          return;
+        }
+        close(fd);
 
         {
           char *var;
@@ -108,40 +166,63 @@ void server_startup(server_startup_st *construct)
 
           if ((var= getenv(variable_buffer)))
           {
-            port= (in_port_t)atoi(var);
+            construct->port[x]= (in_port_t)atoi(var);
           }
           else
           {
-            port= (in_port_t)(x + TEST_PORT_BASE);
+            do {
+              construct->port[x]= (in_port_t)(x + TEST_PORT_BASE + port_base);
+
+              if (libmemcached_util_ping("localhost", construct->port[x], NULL))
+              {
+                if (libmemcached_util_flush("localhost", construct->port[x], NULL))
+                { 
+                  fprintf(stderr, "Found server on port %d, flushed it!\n", (int)construct->port[x]);
+                  construct->is_used[x]= true;
+                } // If we can flush it, we will just use it
+                else
+                {
+                  fprintf(stderr, "Found server on port %d, could not flush it, so trying next port.\n", (int)construct->port[x]);
+                  port_base++;
+                  construct->port[x]= 0;
+                }
+              }
+            } while (construct->port[x] == 0);
           }
         }
 
-        char buffer[PATH_MAX];
-        snprintf(buffer, sizeof(buffer), PID_FILE_BASE, x);
-        kill_file(buffer);
-
+        char buffer[FILENAME_MAX];
         if (x == 0)
         {
-          snprintf(buffer, sizeof(buffer), "%s -d -u root -P "PID_FILE_BASE" -t 1 -p %u -U %u -m 128",
-                   MEMCACHED_BINARY, x, port, port);
+          snprintf(buffer, sizeof(buffer), "%s -d -P %s -t 1 -p %u -U %u -m 128",
+                   MEMCACHED_BINARY, construct->pid_file[x], construct->port[x], construct->port[x]);
         }
         else
         {
-          snprintf(buffer, sizeof(buffer), "%s -d -u root -P "PID_FILE_BASE" -t 1 -p %u -U %u",
-                   MEMCACHED_BINARY, x, port, port);
+          snprintf(buffer, sizeof(buffer), "%s -d -P %s -t 1 -p %u -U %u",
+                   MEMCACHED_BINARY, construct->pid_file[x], construct->port[x], construct->port[x]);
+        }
+
+        if (construct->is_used[x])
+        {
+          fprintf(stderr, "USING SERVER: %s\n", buffer);
         }
-       if (libmemcached_util_ping("localhost", port, NULL))
-       {
-         fprintf(stderr, "Server on port %u already exists\n", port);
-       }
-       else
-       {
-         status= system(buffer);
-         fprintf(stderr, "STARTING SERVER: %s  status:%d\n", buffer, status);
-       }
+        else
+        {
+          if (libmemcached_util_ping("localhost", construct->port[x], NULL))
+          {
+            fprintf(stderr, "Server on port %u already exists\n", construct->port[x]);
+          }
+          else
+          {
+            status= system(buffer);
+            fprintf(stderr, "STARTING SERVER: %s  status:%d\n", buffer, status);
+          }
+        }
+
         int count;
         size_t remaining_length= sizeof(server_string_buffer) - (size_t)(end_ptr -server_string_buffer);
-        count= snprintf(end_ptr, remaining_length,  "localhost:%u,", port);
+        count= snprintf(end_ptr, remaining_length,  "localhost:%u,", construct->port[x]);
 
         if ((size_t)count >= remaining_length || count < 0)
         {
@@ -153,17 +234,24 @@ void server_startup(server_startup_st *construct)
       *end_ptr= 0;
 
 
-      int *pids= calloc(construct->count, sizeof(int));
       for (uint32_t x= 0; x < construct->count; x++)
       {
-        char buffer[PATH_MAX]; /* Nothing special for number */
-
-        snprintf(buffer, sizeof(buffer), PID_FILE_BASE, x);
+        if (! wait_for_file(construct->pid_file[x]))
+        {
+          abort();
+        }
+      }
 
+      for (uint32_t x= 0; x < construct->count; x++)
+      {
         uint32_t counter= 3000; // Absurd, just to catch run away process
-        while (pids[x] <= 0  && --counter)
+
+        if (construct->is_used[x])
+          continue;
+
+        while (construct->pids[x] <= 0  && --counter)
         {
-          FILE *file= fopen(buffer, "r");
+          FILE *file= fopen(construct->pid_file[x], "r");
           if (file)
           {
             char pid_buffer[1024];
@@ -171,24 +259,45 @@ void server_startup(server_startup_st *construct)
 
             if (found)
             {
-              pids[x]= atoi(pid_buffer);
+              construct->pids[x]= atoi(pid_buffer);
               fclose(file);
 
-              if (pids[x] > 0)
+              if (construct->pids[x] > 0)
                 break;
             }
             fclose(file);
           }
-          global_sleep();
+
+          switch (errno)
+          {
+          default:
+            fprintf(stderr, "Could not open pid file %s -> fopen(%s) -> %s:%d\n", construct->pid_file[x], strerror(errno), __FILE__, __LINE__);
+            abort();
+
+          case ENOENT:
+          case EINTR:
+          case EACCES:
+          case EINPROGRESS:
+            break;
+
+          case ENOTCONN:
+            continue;
+          }
+
+          // Safety 3rd, check to see if the file has gone away
+          if (! wait_for_file(construct->pid_file[x]))
+          {
+            abort();
+          }
         }
 
         bool was_started= false;
-        if (pids[x] > 0)
+        if (construct->pids[x] > 0)
         {
           counter= 30;
           while (--counter)
           {
-            if (kill(pids[x], 0) == 0)
+            if (kill(construct->pids[x], 0) == 0)
             {
               was_started= true;
               break;
@@ -199,16 +308,15 @@ void server_startup(server_startup_st *construct)
 
         if (was_started == false)
         {
-          fprintf(stderr, "Failed to open buffer %s(%d)\n", buffer, pids[x]);
+          fprintf(stderr, "Failed to open buffer %s(%d)\n", construct->pid_file[x], construct->pids[x]);
           for (uint32_t y= 0; y < construct->count; y++)
           {
-            if (pids[y] > 0)
-              kill(pids[y], SIGTERM);
+            if (construct->pids[y] > 0)
+              kill(construct->pids[y], SIGTERM);
           }
           abort();
         }
       }
-      free(pids);
 
       construct->server_list= strdup(server_string_buffer);
     }
@@ -236,9 +344,10 @@ void server_shutdown(server_startup_st *construct)
   {
     for (uint32_t x= 0; x < construct->count; x++)
     {
-      char file_buffer[PATH_MAX]; /* Nothing special for number */
-      snprintf(file_buffer, sizeof(file_buffer), PID_FILE_BASE, x);
-      kill_file(file_buffer);
+      if (construct->is_used[x])
+        continue;
+
+      kill_file(construct->pid_file[x]);
     }
 
     free(construct->server_list);