Merge with build trunk.
authorBrian Aker <brian@tangent.org>
Sat, 3 Mar 2012 02:19:44 +0000 (18:19 -0800)
committerBrian Aker <brian@tangent.org>
Sat, 3 Mar 2012 02:19:44 +0000 (18:19 -0800)
64 files changed:
.bzrignore
clients/memcat.cc
clients/memcp.cc
clients/memrm.cc
configure.ac
example/include.am
example/interface_v0.c [deleted file]
example/interface_v0.cc [new file with mode: 0644]
example/interface_v1.c [deleted file]
example/interface_v1.cc [new file with mode: 0644]
example/memcached_light.c [deleted file]
example/memcached_light.cc [new file with mode: 0644]
example/memcached_light.h
example/storage.c [deleted file]
example/storage.cc [new file with mode: 0644]
example/t/include.am [new file with mode: 0644]
example/t/memcached_light.cc [new file with mode: 0644]
libmemcached/socket.hpp
libmemcachedprotocol-0.0/callback.h
libmemcachedprotocol-0.0/handler.h
libmemcachedprotocol/ascii_handler.c
libmemcachedprotocol/binary_handler.c
libmemcachedprotocol/common.h
libmemcachedprotocol/handler.c
libtest/blobslap_worker.cc
libtest/cmdline.cc
libtest/cmdline.h
libtest/error.h
libtest/fatal.hpp
libtest/gearmand.cc
libtest/memcached.cc
libtest/memcached.h
libtest/server.cc
libtest/server.h
libtest/server_container.cc
libtest/strerror.cc
libtest/test.cc
libtest/test.hpp
libtest/unittest.cc
m4/ax_check_library.m4
tests/cli.am
tests/hashkit_functions.cc
tests/include.am
tests/libmemcached-1.0/all_tests.cc
tests/libmemcached-1.0/callbacks.cc
tests/libmemcached-1.0/deprecated.cc
tests/libmemcached-1.0/mem_functions.cc
tests/libmemcached-1.0/parser.cc
tests/libmemcached-1.0/pool.cc
tests/libmemcached_world.h
tests/libmemcached_world_socket.h
tests/mem_udp.cc
tests/memcapable.cc
tests/memcat.cc
tests/memcp.cc
tests/memdump.cc
tests/memerror.cc
tests/memexist.cc
tests/memflush.cc
tests/memrm.cc
tests/memstat.cc
tests/memtouch.cc
util/include.am
util/log.hpp [new file with mode: 0644]

index 0ffd2ab565a7d54385ea416a8d3ec44cba152938..17af0242c341b760270793b3505894fb8f397035 100644 (file)
@@ -142,3 +142,4 @@ tests/var/
 tmp_chroot
 unittests/unittests
 tests/libmemcached-1.0/testsocket
+example/t/memcached_light
index 86ae279c8c671997ef8dbe0ddc19e1df7d6669c0..c93f1a21d7812fe63824e2879d0d283356fe7725 100644 (file)
@@ -184,7 +184,6 @@ int main(int argc, char *argv[])
 void options_parse(int argc, char *argv[])
 {
   int option_index= 0;
-  int option_rv;
 
   memcached_programs_help_st help_options[]=
   {
@@ -210,7 +209,7 @@ void options_parse(int argc, char *argv[])
 
   while (1)
   {
-    option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index);
+    int option_rv= getopt_long(argc, argv, "Vhvds:", long_options, &option_index);
     if (option_rv == -1) break;
     switch (option_rv)
     {
index 5422aa7386cbbe7a4b9f36a1bc31ba2a37647b5f..59bd7478bb780a89d447d9c8949b1381c7342baa 100644 (file)
@@ -245,7 +245,7 @@ int main(int argc, char *argv[])
 
     if (memcached_failed(rc))
     {
-      std::cerr << "Error occrrured during operation: " << memcached_last_error_message(memc) << std::endl;
+      std::cerr << "Error occrrured during memcached_set(): " << memcached_last_error_message(memc) << std::endl;
       exit_code= EXIT_FAILURE;
     }
 
@@ -254,6 +254,11 @@ int main(int argc, char *argv[])
     optind++;
   }
 
+  if (opt_verbose)
+  {
+    std::cout << "Calling memcached_free()" << std::endl;
+  }
+
   memcached_free(memc);
 
   if (opt_servers)
index cbfcd2418e64a577a9096e7a0991fc8edb02cf23..61697cd109f9cd81ffc32afd5e2e79a2be4ce60c 100644 (file)
@@ -97,7 +97,7 @@ int main(int argc, char *argv[])
         std::cerr << "Could not find key \"" << argv[optind] << "\"" << std::endl;
       }
     }
-    else if (memcached_failed(rc))
+    else if (memcached_fatal(rc))
     {
       if (opt_verbose)
       {
index 8bb9a60a37199fb22dcad18e9b6e1e7f1693fd32..caec223bca9a56c963790b3144dde7c8ba99a8f1 100644 (file)
@@ -209,6 +209,9 @@ AC_DEFINE([GEARMAND_BLOBSLAP_WORKER], [0], [Support for Gearman Blobslap worker]
 AC_DEFINE([HAVE_LIBPQ], [0], [Support for Postgres])
 AC_DEFINE([HAVE_LIBCURL], [0], [Support for libcurl])
 
+AC_DEFINE([HAVE_MEMCACHED_LIGHT_BINARY], [1], [Support for memcached_light])
+AC_DEFINE([MEMCACHED_LIGHT_BINARY], ["example/memcached_light"], [Support for memcached_light])
+
 AC_CHECK_HEADERS_ONCE(winsock2.h poll.h sys/wait.h fnmatch.h)
 AM_CONDITIONAL(BUILD_POLL, test "x$ac_cv_header_poll_h" = "xno")
 AM_CONDITIONAL(BUILD_WIN32_WRAPPERS, test "x$ac_cv_header_winsock2_h" = "xyes")
index 625c46809eead12be2dd4abeab23053321dedebe..bf620d0c2d4344c5f4dcffa5050546e91c0e678d 100644 (file)
@@ -13,11 +13,15 @@ noinst_HEADERS+= \
 
 example_memcached_light_SOURCES= \
                                 example/byteorder.cc \
-                                example/interface_v0.c \
-                                example/interface_v1.c \
-                                example/memcached_light.c
+                                example/interface_v0.cc \
+                                example/interface_v1.cc \
+                                example/memcached_light.cc \
+                                util/daemon.cc \
+                                util/pidfile.cc
 
 example_memcached_light_LDADD= libmemcached/libmemcachedprotocol.la \
                                $(LIBEVENT_LDFLAGS)
 
-example_memcached_light_SOURCES+= example/storage.c
+example_memcached_light_SOURCES+= example/storage.cc
+
+include example/t/include.am
diff --git a/example/interface_v0.c b/example/interface_v0.c
deleted file mode 100644 (file)
index ef5ba47..0000000
+++ /dev/null
@@ -1,596 +0,0 @@
-/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/**
- * This file contains an implementation of the callback interface for level 0
- * in the protocol library. You might want to have your copy of the protocol
- * specification next to your coffee ;-)
- */
-
-#include "config.h"
-
-#include <assert.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <libmemcachedprotocol-0.0/handler.h>
-#include <example/byteorder.h>
-#include "example/storage.h"
-#include "example/memcached_light.h"
-
-static protocol_binary_response_status noop_command_handler(const void *cookie,
-                                                            protocol_binary_request_header *header,
-                                                            memcached_binary_protocol_raw_response_handler response_handler)
-{
-  protocol_binary_response_no_extras response= {
-    .message.header.response= {
-      .magic= PROTOCOL_BINARY_RES,
-      .opcode= PROTOCOL_BINARY_CMD_NOOP,
-      .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
-      .opaque= header->request.opaque
-    }
-  };
-
-  return response_handler(cookie, header, (void*)&response);
-}
-
-static protocol_binary_response_status quit_command_handler(const void *cookie,
-                                                            protocol_binary_request_header *header,
-                                                            memcached_binary_protocol_raw_response_handler response_handler)
-{
-  protocol_binary_response_no_extras response= {
-    .message.header.response= {
-      .magic= PROTOCOL_BINARY_RES,
-      .opcode= PROTOCOL_BINARY_CMD_QUIT,
-      .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
-      .opaque= header->request.opaque
-    }
-  };
-
-  if (header->request.opcode == PROTOCOL_BINARY_CMD_QUIT)
-    response_handler(cookie, header, (void*)&response);
-
-  /* I need a better way to signal to close the connection */
-  return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
-}
-
-static protocol_binary_response_status get_command_handler(const void *cookie,
-                                                           protocol_binary_request_header *header,
-                                                           memcached_binary_protocol_raw_response_handler response_handler)
-{
-  uint8_t opcode= header->request.opcode;
-  union {
-    protocol_binary_response_get response;
-    char buffer[4096];
-  } msg= {
-    .response.message.header.response= {
-      .magic= PROTOCOL_BINARY_RES,
-      .opcode= opcode,
-      .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
-      .opaque= header->request.opaque
-    }
-  };
-
-  struct item *item= get_item(header + 1, ntohs(header->request.keylen));
-  if (item)
-  {
-    msg.response.message.body.flags= htonl(item->flags);
-    char *ptr= (char*)(msg.response.bytes + sizeof(*header) + 4);
-    uint32_t bodysize= 4;
-    msg.response.message.header.response.cas= example_htonll(item->cas);
-    if (opcode == PROTOCOL_BINARY_CMD_GETK || opcode == PROTOCOL_BINARY_CMD_GETKQ)
-    {
-      memcpy(ptr, item->key, item->nkey);
-      msg.response.message.header.response.keylen= htons((uint16_t)item->nkey);
-      ptr += item->nkey;
-      bodysize += (uint32_t)item->nkey;
-    }
-    memcpy(ptr, item->data, item->size);
-    bodysize += (uint32_t)item->size;
-    msg.response.message.header.response.bodylen= htonl(bodysize);
-    msg.response.message.header.response.extlen= 4;
-
-    release_item(item);
-    return response_handler(cookie, header, (void*)&msg);
-  }
-  else if (opcode == PROTOCOL_BINARY_CMD_GET || opcode == PROTOCOL_BINARY_CMD_GETK)
-  {
-    msg.response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
-    return response_handler(cookie, header, (void*)&msg);
-  }
-
-  /* Q shouldn't report a miss ;-) */
-  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-}
-
-static protocol_binary_response_status delete_command_handler(const void *cookie,
-                                                              protocol_binary_request_header *header,
-                                                              memcached_binary_protocol_raw_response_handler response_handler)
-{
-  size_t keylen= ntohs(header->request.keylen);
-  char *key= ((char*)header) + sizeof(*header);
-  protocol_binary_response_no_extras response= {
-    .message.header.response= {
-      .magic= PROTOCOL_BINARY_RES,
-      .opcode= header->request.opcode,
-      .opaque= header->request.opaque
-    }
-  };
-
-  if (!delete_item(key, keylen))
-  {
-    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
-    return response_handler(cookie, header, (void*)&response);
-  }
-  else if (header->request.opcode == PROTOCOL_BINARY_CMD_DELETE)
-  {
-    /* DELETEQ doesn't want success response */
-    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
-    return response_handler(cookie, header, (void*)&response);
-  }
-
-  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-}
-
-static protocol_binary_response_status flush_command_handler(const void *cookie,
-                                                             protocol_binary_request_header *header,
-                                                             memcached_binary_protocol_raw_response_handler response_handler)
-{
-  uint8_t opcode= header->request.opcode;
-
-  /* @fixme sett inn when! */
-  flush(0);
-
-  if (opcode == PROTOCOL_BINARY_CMD_FLUSH)
-  {
-    protocol_binary_response_no_extras response= {
-      .message.header.response= {
-        .magic= PROTOCOL_BINARY_RES,
-        .opcode= opcode,
-        .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
-        .opaque= header->request.opaque
-      }
-    };
-    return response_handler(cookie, header, (void*)&response);
-  }
-
-  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-}
-
-static protocol_binary_response_status arithmetic_command_handler(const void *cookie,
-                                                                  protocol_binary_request_header *header,
-                                                                  memcached_binary_protocol_raw_response_handler response_handler)
-{
-  protocol_binary_request_incr *req= (void*)header;
-  protocol_binary_response_incr response= {
-    .message.header.response= {
-      .magic= PROTOCOL_BINARY_RES,
-      .opcode= header->request.opcode,
-      .opaque= header->request.opaque,
-    },
-  };
-
-  uint16_t keylen= ntohs(header->request.keylen);
-  uint64_t initial= example_ntohll(req->message.body.initial);
-  uint64_t delta= example_ntohll(req->message.body.delta);
-  uint32_t expiration= ntohl(req->message.body.expiration);
-  uint32_t flags= 0;
-  void *key= req->bytes + sizeof(req->bytes);
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-
-  uint64_t value= initial;
-
-  struct item *item= get_item(key, keylen);
-  if (item != NULL)
-  {
-    if (header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENT ||
-        header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENTQ)
-    {
-      value= (*(uint64_t*)item->data) + delta;
-    }
-    else
-    {
-      if (delta > *(uint64_t*)item->data)
-      {
-        value= 0;
-      }
-      else
-      {
-        value= *(uint64_t*)item->data - delta;
-      }
-    }
-    expiration= (uint32_t)item->exp;
-    flags= item->flags;
-
-    release_item(item);
-    delete_item(key, keylen);
-  }
-
-  item= create_item(key, keylen, NULL, sizeof(value), flags, (time_t)expiration);
-  if (item == NULL)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
-  }
-  else
-  {
-    memcpy(item->data, &value, sizeof(value));
-    put_item(item);
-  }
-
-  response.message.header.response.status= htons(rval);
-  if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
-  {
-    response.message.header.response.bodylen= ntohl(8);
-    response.message.body.value= example_ntohll((*(uint64_t*)item->data));
-    response.message.header.response.cas= example_ntohll(item->cas);
-
-    release_item(item);
-    if (header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENTQ ||
-        header->request.opcode == PROTOCOL_BINARY_CMD_DECREMENTQ)
-    {
-      return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-    }
-  }
-
-  return response_handler(cookie, header, (void*)&response);
-}
-
-static protocol_binary_response_status version_command_handler(const void *cookie,
-                                                               protocol_binary_request_header *header,
-                                                               memcached_binary_protocol_raw_response_handler response_handler)
-{
-  const char *versionstring= "1.0.0";
-  union {
-    protocol_binary_response_header packet;
-    char buffer[256];
-  } response= {
-    .packet.response= {
-      .magic= PROTOCOL_BINARY_RES,
-      .opcode= PROTOCOL_BINARY_CMD_VERSION,
-      .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
-      .opaque= header->request.opaque,
-      .cas= 0,
-      .bodylen= htonl((uint32_t)strlen(versionstring))
-    }
-  };
-
-  memcpy(response.buffer + sizeof(response.packet), versionstring, strlen(versionstring));
-
-  return response_handler(cookie, header, (void*)&response);
-}
-
-static protocol_binary_response_status concat_command_handler(const void *cookie,
-                                                              protocol_binary_request_header *header,
-                                                              memcached_binary_protocol_raw_response_handler response_handler)
-{
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-  uint16_t keylen= ntohs(header->request.keylen);
-  uint64_t cas= example_ntohll(header->request.cas);
-  void *key= header + 1;
-  uint32_t vallen= ntohl(header->request.bodylen) - keylen;
-  void *val= (char*)key + keylen;
-
-  struct item *item= get_item(key, keylen);
-  struct item *nitem= NULL;
-
-  if (item == NULL)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
-  }
-  else if (cas != 0 && cas != item->cas)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
-  }
-  else if ((nitem= create_item(key, keylen, NULL, item->size + vallen,
-                               item->flags, item->exp)) == NULL)
-  {
-    release_item(item);
-    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
-  }
-  else
-  {
-    if (header->request.opcode == PROTOCOL_BINARY_CMD_APPEND ||
-        header->request.opcode == PROTOCOL_BINARY_CMD_APPENDQ)
-    {
-      memcpy(nitem->data, item->data, item->size);
-      memcpy(((char*)(nitem->data)) + item->size, val, vallen);
-    }
-    else
-    {
-      memcpy(nitem->data, val, vallen);
-      memcpy(((char*)(nitem->data)) + vallen, item->data, item->size);
-    }
-    release_item(item);
-    delete_item(key, keylen);
-    put_item(nitem);
-    cas= nitem->cas;
-    release_item(nitem);
-
-    if (header->request.opcode == PROTOCOL_BINARY_CMD_APPEND ||
-        header->request.opcode == PROTOCOL_BINARY_CMD_PREPEND)
-    {
-      protocol_binary_response_no_extras response= {
-        .message= {
-          .header.response= {
-            .magic= PROTOCOL_BINARY_RES,
-            .opcode= header->request.opcode,
-            .status= htons(rval),
-            .opaque= header->request.opaque,
-            .cas= example_htonll(cas),
-          }
-        }
-      };
-      return response_handler(cookie, header, (void*)&response);
-    }
-  }
-
-  return rval;
-}
-
-static protocol_binary_response_status set_command_handler(const void *cookie,
-                                                           protocol_binary_request_header *header,
-                                                           memcached_binary_protocol_raw_response_handler response_handler)
-{
-  size_t keylen= ntohs(header->request.keylen);
-  size_t datalen= ntohl(header->request.bodylen) - keylen - 8;
-  protocol_binary_request_replace *request= (void*)header;
-  uint32_t flags= ntohl(request->message.body.flags);
-  time_t timeout= (time_t)ntohl(request->message.body.expiration);
-  char *key= ((char*)header) + sizeof(*header) + 8;
-  char *data= key + keylen;
-
-  protocol_binary_response_no_extras response= {
-    .message= {
-      .header.response= {
-        .magic= PROTOCOL_BINARY_RES,
-        .opcode= header->request.opcode,
-        .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
-        .opaque= header->request.opaque
-      }
-    }
-  };
-
-  if (header->request.cas != 0)
-  {
-    /* validate cas */
-    struct item* item= get_item(key, keylen);
-    if (item != NULL)
-    {
-      if (item->cas != example_ntohll(header->request.cas))
-      {
-        release_item(item);
-        response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
-        return response_handler(cookie, header, (void*)&response);
-      }
-      release_item(item);
-    }
-  }
-
-  delete_item(key, keylen);
-  struct item* item= create_item(key, keylen, data, datalen, flags, timeout);
-  if (item == NULL)
-  {
-    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM);
-  }
-  else
-  {
-    put_item(item);
-    /* SETQ shouldn't return a message */
-    if (header->request.opcode == PROTOCOL_BINARY_CMD_SET)
-    {
-      response.message.header.response.cas= example_htonll(item->cas);
-      release_item(item);
-      return response_handler(cookie, header, (void*)&response);
-    }
-    release_item(item);
-
-    return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-  }
-
-  return response_handler(cookie, header, (void*)&response);
-}
-
-static protocol_binary_response_status add_command_handler(const void *cookie,
-                                                           protocol_binary_request_header *header,
-                                                           memcached_binary_protocol_raw_response_handler response_handler)
-{
-  size_t keylen= ntohs(header->request.keylen);
-  size_t datalen= ntohl(header->request.bodylen) - keylen - 8;
-  protocol_binary_request_add *request= (void*)header;
-  uint32_t flags= ntohl(request->message.body.flags);
-  time_t timeout= (time_t)ntohl(request->message.body.expiration);
-  char *key= ((char*)header) + sizeof(*header) + 8;
-  char *data= key + keylen;
-
-  protocol_binary_response_no_extras response= {
-    .message= {
-      .header.response= {
-        .magic= PROTOCOL_BINARY_RES,
-        .opcode= header->request.opcode,
-        .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
-        .opaque= header->request.opaque
-      }
-    }
-  };
-
-  struct item* item= get_item(key, keylen);
-  if (item == NULL)
-  {
-    item= create_item(key, keylen, data, datalen, flags, timeout);
-    if (item == NULL)
-      response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM);
-    else
-    {
-      put_item(item);
-      /* ADDQ shouldn't return a message */
-      if (header->request.opcode == PROTOCOL_BINARY_CMD_ADD)
-      {
-        response.message.header.response.cas= example_htonll(item->cas);
-        release_item(item);
-        return response_handler(cookie, header, (void*)&response);
-      }
-      release_item(item);
-      return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-    }
-  }
-  else
-  {
-    release_item(item);
-    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
-  }
-
-  return response_handler(cookie, header, (void*)&response);
-}
-
-static protocol_binary_response_status replace_command_handler(const void *cookie,
-                                                               protocol_binary_request_header *header,
-                                                               memcached_binary_protocol_raw_response_handler response_handler)
-{
-  size_t keylen= ntohs(header->request.keylen);
-  size_t datalen= ntohl(header->request.bodylen) - keylen - 8;
-  protocol_binary_request_replace *request= (void*)header;
-  uint32_t flags= ntohl(request->message.body.flags);
-  time_t timeout= (time_t)ntohl(request->message.body.expiration);
-  char *key= ((char*)header) + sizeof(*header) + 8;
-  char *data= key + keylen;
-
-  protocol_binary_response_no_extras response= {
-    .message= {
-      .header.response= {
-        .magic= PROTOCOL_BINARY_RES,
-        .opcode= header->request.opcode,
-        .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
-        .opaque= header->request.opaque
-      }
-    }
-  };
-
-  struct item* item= get_item(key, keylen);
-  if (item == NULL)
-  {
-    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
-  }
-  else if (header->request.cas == 0 || example_ntohll(header->request.cas) == item->cas)
-  {
-    release_item(item);
-    delete_item(key, keylen);
-    item= create_item(key, keylen, data, datalen, flags, timeout);
-
-    if (item == NULL)
-    {
-      response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM);
-    }
-    else
-    {
-      put_item(item);
-      /* REPLACEQ shouldn't return a message */
-      if (header->request.opcode == PROTOCOL_BINARY_CMD_REPLACE)
-      {
-        response.message.header.response.cas= example_htonll(item->cas);
-        release_item(item);
-        return response_handler(cookie, header, (void*)&response);
-      }
-      release_item(item);
-      return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-    }
-  }
-  else
-  {
-    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
-    release_item(item);
-  }
-
-  return response_handler(cookie, header, (void*)&response);
-}
-
-static protocol_binary_response_status stat_command_handler(const void *cookie,
-                                                            protocol_binary_request_header *header,
-                                                            memcached_binary_protocol_raw_response_handler response_handler)
-{
-  /* Just send the terminating packet*/
-  protocol_binary_response_no_extras response= {
-    .message= {
-      .header.response= {
-        .magic= PROTOCOL_BINARY_RES,
-        .opcode= PROTOCOL_BINARY_CMD_STAT,
-        .status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS),
-        .opaque= header->request.opaque
-      }
-    }
-  };
-
-  return response_handler(cookie, header, (void*)&response);
-}
-
-memcached_binary_protocol_callback_st interface_v0_impl= {
-  .interface_version= MEMCACHED_PROTOCOL_HANDLER_V0,
-#ifdef FUTURE
-  /*
-  ** There is a number of bugs in the extra options for gcc causing
-  ** warning on these struct initializers. It hurts my heart to remove
-  ** it so I'll just leave it in here so that we can enable it when
-  ** we can drop support for the broken compilers
-  */
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_GET]= get_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_SET]= set_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_ADD]= add_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_REPLACE]= replace_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_DELETE]= delete_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_INCREMENT]= arithmetic_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_DECREMENT]= arithmetic_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_QUIT]= quit_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_FLUSH]= flush_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_GETQ]= get_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_NOOP]= noop_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_VERSION]= version_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_GETK]= get_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_GETKQ]= get_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_APPEND]= concat_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_PREPEND]= concat_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_STAT]= stat_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_SETQ]= set_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_ADDQ]= add_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_REPLACEQ]= replace_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_DELETEQ]= delete_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_INCREMENTQ]= arithmetic_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_DECREMENTQ]= arithmetic_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_QUITQ]= quit_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_FLUSHQ]= flush_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_APPENDQ]= concat_command_handler,
-  .interface.v0.comcode[PROTOCOL_BINARY_CMD_PREPENDQ]= concat_command_handler,
-#endif
-};
-
-void initialize_interface_v0_handler(void)
-{
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GET]= get_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_SET]= set_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_ADD]= add_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_REPLACE]= replace_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_DELETE]= delete_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_INCREMENT]= arithmetic_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_DECREMENT]= arithmetic_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_QUIT]= quit_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_FLUSH]= flush_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GETQ]= get_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_NOOP]= noop_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_VERSION]= version_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GETK]= get_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GETKQ]= get_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_APPEND]= concat_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_PREPEND]= concat_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_STAT]= stat_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_SETQ]= set_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_ADDQ]= add_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_REPLACEQ]= replace_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_DELETEQ]= delete_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_INCREMENTQ]= arithmetic_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_DECREMENTQ]= arithmetic_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_QUITQ]= quit_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_FLUSHQ]= flush_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_APPENDQ]= concat_command_handler;
-  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_PREPENDQ]= concat_command_handler;
-}
diff --git a/example/interface_v0.cc b/example/interface_v0.cc
new file mode 100644 (file)
index 0000000..cc2f17d
--- /dev/null
@@ -0,0 +1,549 @@
+/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/**
+ * This file contains an implementation of the callback interface for level 0
+ * in the protocol library. You might want to have your copy of the protocol
+ * specification next to your coffee ;-)
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libmemcachedprotocol-0.0/handler.h>
+#include <example/byteorder.h>
+#include "example/memcached_light.h"
+#include "example/storage.h"
+
+static protocol_binary_response_status noop_command_handler(const void *cookie,
+                                                            protocol_binary_request_header *header,
+                                                            memcached_binary_protocol_raw_response_handler response_handler)
+{
+  protocol_binary_response_no_extras response;
+  memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+  response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  response.message.header.response.opcode= PROTOCOL_BINARY_CMD_NOOP;
+  response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+  response.message.header.response.opaque= header->request.opaque;
+
+  return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+}
+
+static protocol_binary_response_status quit_command_handler(const void *cookie,
+                                                            protocol_binary_request_header *header,
+                                                            memcached_binary_protocol_raw_response_handler response_handler)
+{
+  protocol_binary_response_no_extras response;
+  memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+  response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  response.message.header.response.opcode= PROTOCOL_BINARY_CMD_QUIT;
+  response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+  response.message.header.response.opaque= header->request.opaque;
+
+  if (header->request.opcode == PROTOCOL_BINARY_CMD_QUIT)
+  {
+    response_handler(cookie, header, (protocol_binary_response_header*)&response);
+  }
+
+  /* I need a better way to signal to close the connection */
+  return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
+}
+
+static protocol_binary_response_status get_command_handler(const void *cookie,
+                                                           protocol_binary_request_header *header,
+                                                           memcached_binary_protocol_raw_response_handler response_handler)
+{
+  uint8_t opcode= header->request.opcode;
+  union protocol_binary_response_get_un {
+    protocol_binary_response_get response;
+    char buffer[4096];
+  };
+  
+  protocol_binary_response_get_un msg;
+  memset(&msg, 0, sizeof(protocol_binary_response_get_un));
+
+  msg.response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  msg.response.message.header.response.opcode= opcode;
+  msg.response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+  msg.response.message.header.response.opaque= header->request.opaque;
+
+  struct item *item= get_item(header + 1, ntohs(header->request.keylen));
+  if (item)
+  {
+    msg.response.message.body.flags= htonl(item->flags);
+    char *ptr= (char*)(msg.response.bytes + sizeof(*header) + 4);
+    uint32_t bodysize= 4;
+    msg.response.message.header.response.cas= example_htonll(item->cas);
+    if (opcode == PROTOCOL_BINARY_CMD_GETK || opcode == PROTOCOL_BINARY_CMD_GETKQ)
+    {
+      memcpy(ptr, item->key, item->nkey);
+      msg.response.message.header.response.keylen= htons((uint16_t)item->nkey);
+      ptr += item->nkey;
+      bodysize += (uint32_t)item->nkey;
+    }
+    memcpy(ptr, item->data, item->size);
+    bodysize += (uint32_t)item->size;
+    msg.response.message.header.response.bodylen= htonl(bodysize);
+    msg.response.message.header.response.extlen= 4;
+
+    release_item(item);
+    return response_handler(cookie, header, (protocol_binary_response_header*)&msg);
+  }
+  else if (opcode == PROTOCOL_BINARY_CMD_GET || opcode == PROTOCOL_BINARY_CMD_GETK)
+  {
+    msg.response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
+    return response_handler(cookie, header, (protocol_binary_response_header*)&msg);
+  }
+
+  /* Q shouldn't report a miss ;-) */
+  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+}
+
+static protocol_binary_response_status delete_command_handler(const void *cookie,
+                                                              protocol_binary_request_header *header,
+                                                              memcached_binary_protocol_raw_response_handler response_handler)
+{
+  size_t keylen= ntohs(header->request.keylen);
+
+  char *key= ((char*)header) + sizeof(*header);
+  protocol_binary_response_no_extras response;
+  memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+  response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  response.message.header.response.opcode= header->request.opcode;
+  response.message.header.response.opaque= header->request.opaque;
+
+  if (!delete_item(key, keylen))
+  {
+    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
+    return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+  }
+  else if (header->request.opcode == PROTOCOL_BINARY_CMD_DELETE)
+  {
+    /* DELETEQ doesn't want success response */
+    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+    return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+  }
+
+  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+}
+
+static protocol_binary_response_status flush_command_handler(const void *cookie,
+                                                             protocol_binary_request_header *header,
+                                                             memcached_binary_protocol_raw_response_handler response_handler)
+{
+  uint8_t opcode= header->request.opcode;
+
+  /* @fixme sett inn when! */
+  flush(0);
+
+  if (opcode == PROTOCOL_BINARY_CMD_FLUSH)
+  {
+    protocol_binary_response_no_extras response;
+    memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+    response.message.header.response.magic= PROTOCOL_BINARY_RES;
+    response.message.header.response.opcode= opcode;
+    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+    response.message.header.response.opaque= header->request.opaque;
+
+    return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+  }
+
+  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+}
+
+static protocol_binary_response_status arithmetic_command_handler(const void *cookie,
+                                                                  protocol_binary_request_header *header,
+                                                                  memcached_binary_protocol_raw_response_handler response_handler)
+{
+  protocol_binary_request_incr *req= (protocol_binary_request_incr*)header;
+  protocol_binary_response_incr response;
+  memset(&response, 0, sizeof(protocol_binary_response_incr));
+
+  response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  response.message.header.response.opcode= header->request.opcode;
+  response.message.header.response.opaque= header->request.opaque;
+
+  uint16_t keylen= ntohs(header->request.keylen);
+  uint64_t initial= example_ntohll(req->message.body.initial);
+  uint64_t delta= example_ntohll(req->message.body.delta);
+  uint32_t expiration= ntohl(req->message.body.expiration);
+  uint32_t flags= 0;
+  void *key= req->bytes + sizeof(req->bytes);
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+
+  uint64_t value= initial;
+
+  struct item *item= get_item(key, keylen);
+  if (item != NULL)
+  {
+    if (header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENT ||
+        header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENTQ)
+    {
+      value= (*(uint64_t*)item->data) + delta;
+    }
+    else
+    {
+      if (delta > *(uint64_t*)item->data)
+      {
+        value= 0;
+      }
+      else
+      {
+        value= *(uint64_t*)item->data - delta;
+      }
+    }
+    expiration= (uint32_t)item->exp;
+    flags= item->flags;
+
+    release_item(item);
+    delete_item(key, keylen);
+  }
+
+  item= create_item(key, keylen, NULL, sizeof(value), flags, (time_t)expiration);
+  if (item == NULL)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
+  }
+  else
+  {
+    memcpy(item->data, &value, sizeof(value));
+    put_item(item);
+  }
+
+  response.message.header.response.status= htons(rval);
+  if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
+  {
+    response.message.header.response.bodylen= ntohl(8);
+    response.message.body.value= example_ntohll((*(uint64_t*)item->data));
+    response.message.header.response.cas= example_ntohll(item->cas);
+
+    release_item(item);
+    if (header->request.opcode == PROTOCOL_BINARY_CMD_INCREMENTQ ||
+        header->request.opcode == PROTOCOL_BINARY_CMD_DECREMENTQ)
+    {
+      return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+    }
+  }
+
+  return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+}
+
+static protocol_binary_response_status version_command_handler(const void *cookie,
+                                                               protocol_binary_request_header *header,
+                                                               memcached_binary_protocol_raw_response_handler response_handler)
+{
+  const char *versionstring= "1.0.0";
+  union protocol_binary_response_header_un
+  {
+    protocol_binary_response_header packet;
+    char buffer[256];
+  };
+  
+  protocol_binary_response_header_un response;
+  memset(&response, 0, sizeof(protocol_binary_response_header_un));
+
+  response.packet.response.magic= PROTOCOL_BINARY_RES;
+  response.packet.response.opcode= PROTOCOL_BINARY_CMD_VERSION;
+  response.packet.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+  response.packet.response.opaque= header->request.opaque;
+  response.packet.response.cas= 0;
+  response.packet.response.bodylen= htonl((uint32_t)strlen(versionstring));
+
+  assert(sizeof(protocol_binary_response_header) +strlen(versionstring) <= 256);
+  memcpy(response.buffer + sizeof(protocol_binary_response_header), versionstring, strlen(versionstring));
+
+  return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+}
+
+static protocol_binary_response_status concat_command_handler(const void *cookie,
+                                                              protocol_binary_request_header *header,
+                                                              memcached_binary_protocol_raw_response_handler response_handler)
+{
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+  uint16_t keylen= ntohs(header->request.keylen);
+  uint64_t cas= example_ntohll(header->request.cas);
+  void *key= header + 1;
+  uint32_t vallen= ntohl(header->request.bodylen) - keylen;
+  void *val= (char*)key + keylen;
+
+  struct item *item= get_item(key, keylen);
+  struct item *nitem= NULL;
+
+  if (item == NULL)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
+  }
+  else if (cas != 0 && cas != item->cas)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
+  }
+  else if ((nitem= create_item(key, keylen, NULL, item->size + vallen,
+                               item->flags, item->exp)) == NULL)
+  {
+    release_item(item);
+    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
+  }
+  else
+  {
+    if (header->request.opcode == PROTOCOL_BINARY_CMD_APPEND ||
+        header->request.opcode == PROTOCOL_BINARY_CMD_APPENDQ)
+    {
+      memcpy(nitem->data, item->data, item->size);
+      memcpy(((char*)(nitem->data)) + item->size, val, vallen);
+    }
+    else
+    {
+      memcpy(nitem->data, val, vallen);
+      memcpy(((char*)(nitem->data)) + vallen, item->data, item->size);
+    }
+    release_item(item);
+    delete_item(key, keylen);
+    put_item(nitem);
+    cas= nitem->cas;
+    release_item(nitem);
+
+    if (header->request.opcode == PROTOCOL_BINARY_CMD_APPEND ||
+        header->request.opcode == PROTOCOL_BINARY_CMD_PREPEND)
+    {
+      protocol_binary_response_no_extras response;
+      memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+      response.message.header.response.magic= PROTOCOL_BINARY_RES;
+      response.message.header.response.opcode= header->request.opcode;
+      response.message.header.response.status= htons(rval);
+      response.message.header.response.opaque= header->request.opaque;
+      response.message.header.response.cas= example_htonll(cas);
+
+      return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+    }
+  }
+
+  return rval;
+}
+
+static protocol_binary_response_status set_command_handler(const void *cookie,
+                                                           protocol_binary_request_header *header,
+                                                           memcached_binary_protocol_raw_response_handler response_handler)
+{
+  size_t keylen= ntohs(header->request.keylen);
+  size_t datalen= ntohl(header->request.bodylen) - keylen - 8;
+  protocol_binary_request_replace *request= (protocol_binary_request_replace*)header;
+  uint32_t flags= ntohl(request->message.body.flags);
+  time_t timeout= (time_t)ntohl(request->message.body.expiration);
+  char *key= ((char*)header) + sizeof(*header) + 8;
+  char *data= key + keylen;
+
+  protocol_binary_response_no_extras response;
+  memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+  response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  response.message.header.response.opcode= header->request.opcode;
+  response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+  response.message.header.response.opaque= header->request.opaque;
+
+  if (header->request.cas != 0)
+  {
+    /* validate cas */
+    struct item* item= get_item(key, keylen);
+    if (item != NULL)
+    {
+      if (item->cas != example_ntohll(header->request.cas))
+      {
+        release_item(item);
+        response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
+        return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+      }
+      release_item(item);
+    }
+  }
+
+  delete_item(key, keylen);
+  struct item* item= create_item(key, keylen, data, datalen, flags, timeout);
+  if (item == NULL)
+  {
+    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM);
+  }
+  else
+  {
+    put_item(item);
+    /* SETQ shouldn't return a message */
+    if (header->request.opcode == PROTOCOL_BINARY_CMD_SET)
+    {
+      response.message.header.response.cas= example_htonll(item->cas);
+      release_item(item);
+      return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+    }
+    release_item(item);
+
+    return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+  }
+
+  return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+}
+
+static protocol_binary_response_status add_command_handler(const void *cookie,
+                                                           protocol_binary_request_header *header,
+                                                           memcached_binary_protocol_raw_response_handler response_handler)
+{
+  size_t keylen= ntohs(header->request.keylen);
+  size_t datalen= ntohl(header->request.bodylen) - keylen - 8;
+  protocol_binary_request_add *request= (protocol_binary_request_add*)header;
+  uint32_t flags= ntohl(request->message.body.flags);
+  time_t timeout= (time_t)ntohl(request->message.body.expiration);
+  char *key= ((char*)header) + sizeof(*header) + 8;
+  char *data= key + keylen;
+
+  protocol_binary_response_no_extras response;
+  memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+  response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  response.message.header.response.opcode= header->request.opcode;
+  response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+  response.message.header.response.opaque= header->request.opaque;
+
+  struct item* item= get_item(key, keylen);
+  if (item == NULL)
+  {
+    item= create_item(key, keylen, data, datalen, flags, timeout);
+    if (item == NULL)
+      response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM);
+    else
+    {
+      put_item(item);
+      /* ADDQ shouldn't return a message */
+      if (header->request.opcode == PROTOCOL_BINARY_CMD_ADD)
+      {
+        response.message.header.response.cas= example_htonll(item->cas);
+        release_item(item);
+        return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+      }
+      release_item(item);
+      return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+    }
+  }
+  else
+  {
+    release_item(item);
+    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
+  }
+
+  return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+}
+
+static protocol_binary_response_status replace_command_handler(const void *cookie,
+                                                               protocol_binary_request_header *header,
+                                                               memcached_binary_protocol_raw_response_handler response_handler)
+{
+  size_t keylen= ntohs(header->request.keylen);
+  size_t datalen= ntohl(header->request.bodylen) - keylen - 8;
+  protocol_binary_request_replace *request= (protocol_binary_request_replace*)header;
+  uint32_t flags= ntohl(request->message.body.flags);
+  time_t timeout= (time_t)ntohl(request->message.body.expiration);
+  char *key= ((char*)header) + sizeof(*header) + 8;
+  char *data= key + keylen;
+
+  protocol_binary_response_no_extras response;
+  memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+  response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  response.message.header.response.opcode= header->request.opcode;
+  response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+  response.message.header.response.opaque= header->request.opaque;
+
+  struct item* item= get_item(key, keylen);
+  if (item == NULL)
+  {
+    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
+  }
+  else if (header->request.cas == 0 || example_ntohll(header->request.cas) == item->cas)
+  {
+    release_item(item);
+    delete_item(key, keylen);
+    item= create_item(key, keylen, data, datalen, flags, timeout);
+
+    if (item == NULL)
+    {
+      response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_ENOMEM);
+    }
+    else
+    {
+      put_item(item);
+      /* REPLACEQ shouldn't return a message */
+      if (header->request.opcode == PROTOCOL_BINARY_CMD_REPLACE)
+      {
+        response.message.header.response.cas= example_htonll(item->cas);
+        release_item(item);
+        return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+      }
+      release_item(item);
+      return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+    }
+  }
+  else
+  {
+    response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
+    release_item(item);
+  }
+
+  return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+}
+
+static protocol_binary_response_status stat_command_handler(const void *cookie,
+                                                            protocol_binary_request_header *header,
+                                                            memcached_binary_protocol_raw_response_handler response_handler)
+{
+  /* Just send the terminating packet*/
+  protocol_binary_response_no_extras response;
+  memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+  response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  response.message.header.response.opcode= PROTOCOL_BINARY_CMD_STAT;
+  response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_SUCCESS);
+  response.message.header.response.opaque= header->request.opaque;
+
+  return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+}
+
+memcached_binary_protocol_callback_st interface_v0_impl;
+
+void initialize_interface_v0_handler(void)
+{
+  interface_v0_impl.interface_version= MEMCACHED_PROTOCOL_HANDLER_V0;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GET]= get_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_SET]= set_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_ADD]= add_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_REPLACE]= replace_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_DELETE]= delete_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_INCREMENT]= arithmetic_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_DECREMENT]= arithmetic_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_QUIT]= quit_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_FLUSH]= flush_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GETQ]= get_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_NOOP]= noop_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_VERSION]= version_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GETK]= get_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_GETKQ]= get_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_APPEND]= concat_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_PREPEND]= concat_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_STAT]= stat_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_SETQ]= set_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_ADDQ]= add_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_REPLACEQ]= replace_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_DELETEQ]= delete_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_INCREMENTQ]= arithmetic_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_DECREMENTQ]= arithmetic_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_QUITQ]= quit_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_FLUSHQ]= flush_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_APPENDQ]= concat_command_handler;
+  interface_v0_impl.interface.v0.comcode[PROTOCOL_BINARY_CMD_PREPENDQ]= concat_command_handler;
+}
diff --git a/example/interface_v1.c b/example/interface_v1.c
deleted file mode 100644 (file)
index d2b2071..0000000
+++ /dev/null
@@ -1,411 +0,0 @@
-/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/**
- * This file contains an implementation of the callback interface for level 1
- * in the protocol library. If you compare the implementation with the one
- * in interface_v0.c you will see that this implementation is much easier and
- * hides all of the protocol logic and let you focus on the application
- * logic. One "problem" with this layer is that it is synchronous, so that
- * you will not receive the next command before a answer to the previous
- * command is being sent.
- */
-#include "config.h"
-#include <assert.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <libmemcachedprotocol-0.0/handler.h>
-#include <example/byteorder.h>
-#include "storage.h"
-
-static protocol_binary_response_status add_handler(const void *cookie,
-                                                   const void *key,
-                                                   uint16_t keylen,
-                                                   const void *data,
-                                                   uint32_t datalen,
-                                                   uint32_t flags,
-                                                   uint32_t exptime,
-                                                   uint64_t *cas)
-{
-  (void)cookie;
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-  struct item* item= get_item(key, keylen);
-  if (item == NULL)
-  {
-    item= create_item(key, keylen, data, datalen, flags, (time_t)exptime);
-    if (item == 0)
-    {
-      rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
-    }
-    else
-    {
-      put_item(item);
-      *cas= item->cas;
-      release_item(item);
-    }
-  }
-  else
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
-  }
-
-  return rval;
-}
-
-static protocol_binary_response_status append_handler(const void *cookie,
-                                                      const void *key,
-                                                      uint16_t keylen,
-                                                      const void* val,
-                                                      uint32_t vallen,
-                                                      uint64_t cas,
-                                                      uint64_t *result_cas)
-{
-  (void)cookie;
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-
-  struct item *item= get_item(key, keylen);
-  struct item *nitem;
-
-  if (item == NULL)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
-  }
-  else if (cas != 0 && cas != item->cas)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
-  }
-  else if ((nitem= create_item(key, keylen, NULL, item->size + vallen,
-                               item->flags, item->exp)) == NULL)
-  {
-    release_item(item);
-    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
-  }
-  else
-  {
-    memcpy(nitem->data, item->data, item->size);
-    memcpy(((char*)(nitem->data)) + item->size, val, vallen);
-    release_item(item);
-    delete_item(key, keylen);
-    put_item(nitem);
-    *result_cas= nitem->cas;
-    release_item(nitem);
-  }
-
-  return rval;
-}
-
-static protocol_binary_response_status decrement_handler(const void *cookie,
-                                                         const void *key,
-                                                         uint16_t keylen,
-                                                         uint64_t delta,
-                                                         uint64_t initial,
-                                                         uint32_t expiration,
-                                                         uint64_t *result,
-                                                         uint64_t *result_cas) {
-  (void)cookie;
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-  uint64_t val= initial;
-  struct item *item= get_item(key, keylen);
-
-  if (item != NULL)
-  {
-    if (delta > *(uint64_t*)item->data)
-      val= 0;
-    else
-      val= *(uint64_t*)item->data - delta;
-
-    expiration= (uint32_t)item->exp;
-    release_item(item);
-    delete_item(key, keylen);
-  }
-
-  item= create_item(key, keylen, NULL, sizeof(initial), 0, (time_t)expiration);
-  if (item == 0)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
-  }
-  else
-  {
-    memcpy(item->data, &val, sizeof(val));
-    put_item(item);
-    *result= val;
-    *result_cas= item->cas;
-    release_item(item);
-  }
-
-  return rval;
-}
-
-static protocol_binary_response_status delete_handler(const void *cookie,
-                                                      const void *key,
-                                                      uint16_t keylen,
-                                                      uint64_t cas) {
-  (void)cookie;
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-
-  if (cas != 0)
-  {
-    struct item *item= get_item(key, keylen);
-    if (item != NULL)
-    {
-      if (item->cas != cas)
-      {
-        release_item(item);
-        return PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
-      }
-      release_item(item);
-    }
-  }
-
-  if (!delete_item(key, keylen))
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
-  }
-
-  return rval;
-}
-
-
-static protocol_binary_response_status flush_handler(const void *cookie,
-                                                     uint32_t when) {
-
-  (void)cookie;
-  flush(when);
-  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-}
-
-static protocol_binary_response_status get_handler(const void *cookie,
-                                                   const void *key,
-                                                   uint16_t keylen,
-                                                   memcached_binary_protocol_get_response_handler response_handler) {
-  struct item *item= get_item(key, keylen);
-
-  if (item == NULL)
-  {
-    return PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
-  }
-
-  protocol_binary_response_status rc;
-  rc= response_handler(cookie, key, (uint16_t)keylen,
-                          item->data, (uint32_t)item->size, item->flags,
-                          item->cas);
-  release_item(item);
-  return rc;
-}
-
-static protocol_binary_response_status increment_handler(const void *cookie,
-                                                         const void *key,
-                                                         uint16_t keylen,
-                                                         uint64_t delta,
-                                                         uint64_t initial,
-                                                         uint32_t expiration,
-                                                         uint64_t *result,
-                                                         uint64_t *result_cas) {
-  (void)cookie;
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-  uint64_t val= initial;
-  struct item *item= get_item(key, keylen);
-
-  if (item != NULL)
-  {
-    val= (*(uint64_t*)item->data) + delta;
-    expiration= (uint32_t)item->exp;
-    release_item(item);
-    delete_item(key, keylen);
-  }
-
-  item= create_item(key, keylen, NULL, sizeof(initial), 0, (time_t)expiration);
-  if (item == NULL)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
-  }
-  else
-  {
-    char buffer[1024] = {0};
-    memcpy(buffer, key, keylen);
-    memcpy(item->data, &val, sizeof(val));
-    put_item(item);
-    *result= val;
-    *result_cas= item->cas;
-    release_item(item);
-  }
-
-  return rval;
-}
-
-static protocol_binary_response_status noop_handler(const void *cookie) {
-  (void)cookie;
-  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-}
-
-static protocol_binary_response_status prepend_handler(const void *cookie,
-                                                       const void *key,
-                                                       uint16_t keylen,
-                                                       const void* val,
-                                                       uint32_t vallen,
-                                                       uint64_t cas,
-                                                       uint64_t *result_cas) {
-  (void)cookie;
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-
-  struct item *item= get_item(key, keylen);
-  struct item *nitem= NULL;
-
-  if (item == NULL)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
-  }
-  else if (cas != 0 && cas != item->cas)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
-  }
-  else if ((nitem= create_item(key, keylen, NULL, item->size + vallen,
-                                 item->flags, item->exp)) == NULL)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
-  }
-  else
-  {
-    memcpy(nitem->data, val, vallen);
-    memcpy(((char*)(nitem->data)) + vallen, item->data, item->size);
-    release_item(item);
-    item= NULL;
-    delete_item(key, keylen);
-    put_item(nitem);
-    *result_cas= nitem->cas;
-  }
-
-  if (item)
-    release_item(item);
-
-  if (nitem)
-    release_item(nitem);
-
-  return rval;
-}
-
-static protocol_binary_response_status quit_handler(const void *cookie) {
-  (void)cookie;
-  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
-}
-
-static protocol_binary_response_status replace_handler(const void *cookie,
-                                                       const void *key,
-                                                       uint16_t keylen,
-                                                       const void* data,
-                                                       uint32_t datalen,
-                                                       uint32_t flags,
-                                                       uint32_t exptime,
-                                                       uint64_t cas,
-                                                       uint64_t *result_cas) {
-  (void)cookie;
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-  struct item* item= get_item(key, keylen);
-
-  if (item == NULL)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
-  }
-  else if (cas == 0 || cas == item->cas)
-  {
-    release_item(item);
-    delete_item(key, keylen);
-    item= create_item(key, keylen, data, datalen, flags, (time_t)exptime);
-    if (item == 0)
-    {
-      rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
-    }
-    else
-    {
-      put_item(item);
-      *result_cas= item->cas;
-      release_item(item);
-    }
-  }
-  else
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
-    release_item(item);
-  }
-
-  return rval;
-}
-
-static protocol_binary_response_status set_handler(const void *cookie,
-                                                   const void *key,
-                                                   uint16_t keylen,
-                                                   const void* data,
-                                                   uint32_t datalen,
-                                                   uint32_t flags,
-                                                   uint32_t exptime,
-                                                   uint64_t cas,
-                                                   uint64_t *result_cas) {
-  (void)cookie;
-  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
-
-  if (cas != 0)
-  {
-    struct item* item= get_item(key, keylen);
-    if (item != NULL && cas != item->cas)
-    {
-      /* Invalid CAS value */
-      release_item(item);
-      return PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
-    }
-  }
-
-  delete_item(key, keylen);
-  struct item* item= create_item(key, keylen, data, datalen, flags, (time_t)exptime);
-  if (item == 0)
-  {
-    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
-  }
-  else
-  {
-    put_item(item);
-    *result_cas= item->cas;
-    release_item(item);
-  }
-
-  return rval;
-}
-
-static protocol_binary_response_status stat_handler(const void *cookie,
-                                                    const void *key,
-                                                    uint16_t keylen,
-                                                    memcached_binary_protocol_stat_response_handler response_handler) {
-  (void)key;
-  (void)keylen;
-  /* Just return an empty packet */
-  return response_handler(cookie, NULL, 0, NULL, 0);
-}
-
-static protocol_binary_response_status version_handler(const void *cookie,
-                                                       memcached_binary_protocol_version_response_handler response_handler) {
-  const char *version= "0.1.1";
-  return response_handler(cookie, version, (uint32_t)strlen(version));
-}
-
-memcached_binary_protocol_callback_st interface_v1_impl= {
-  .interface_version= MEMCACHED_PROTOCOL_HANDLER_V1,
-  .interface.v1= {
-    .add= add_handler,
-    .append= append_handler,
-    .decrement= decrement_handler,
-    .delete= delete_handler,
-    .flush= flush_handler,
-    .get= get_handler,
-    .increment= increment_handler,
-    .noop= noop_handler,
-    .prepend= prepend_handler,
-    .quit= quit_handler,
-    .replace= replace_handler,
-    .set= set_handler,
-    .stat= stat_handler,
-    .version= version_handler
-  }
-};
diff --git a/example/interface_v1.cc b/example/interface_v1.cc
new file mode 100644 (file)
index 0000000..a436491
--- /dev/null
@@ -0,0 +1,412 @@
+/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/**
+ * This file contains an implementation of the callback interface for level 1
+ * in the protocol library. If you compare the implementation with the one
+ * in interface_v0.c you will see that this implementation is much easier and
+ * hides all of the protocol logic and let you focus on the application
+ * logic. One "problem" with this layer is that it is synchronous, so that
+ * you will not receive the next command before a answer to the previous
+ * command is being sent.
+ */
+#include "config.h"
+#include <assert.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libmemcachedprotocol-0.0/handler.h>
+#include <example/byteorder.h>
+#include "example/memcached_light.h"
+#include "example/storage.h"
+
+static protocol_binary_response_status add_handler(const void *cookie,
+                                                   const void *key,
+                                                   uint16_t keylen,
+                                                   const void *data,
+                                                   uint32_t datalen,
+                                                   uint32_t flags,
+                                                   uint32_t exptime,
+                                                   uint64_t *cas)
+{
+  (void)cookie;
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+  struct item* item= get_item(key, keylen);
+  if (item == NULL)
+  {
+    item= create_item(key, keylen, data, datalen, flags, (time_t)exptime);
+    if (item == 0)
+    {
+      rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
+    }
+    else
+    {
+      put_item(item);
+      *cas= item->cas;
+      release_item(item);
+    }
+  }
+  else
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
+  }
+
+  return rval;
+}
+
+static protocol_binary_response_status append_handler(const void *cookie,
+                                                      const void *key,
+                                                      uint16_t keylen,
+                                                      const void* val,
+                                                      uint32_t vallen,
+                                                      uint64_t cas,
+                                                      uint64_t *result_cas)
+{
+  (void)cookie;
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+
+  struct item *item= get_item(key, keylen);
+  struct item *nitem;
+
+  if (item == NULL)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
+  }
+  else if (cas != 0 && cas != item->cas)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
+  }
+  else if ((nitem= create_item(key, keylen, NULL, item->size + vallen,
+                               item->flags, item->exp)) == NULL)
+  {
+    release_item(item);
+    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
+  }
+  else
+  {
+    memcpy(nitem->data, item->data, item->size);
+    memcpy(((char*)(nitem->data)) + item->size, val, vallen);
+    release_item(item);
+    delete_item(key, keylen);
+    put_item(nitem);
+    *result_cas= nitem->cas;
+    release_item(nitem);
+  }
+
+  return rval;
+}
+
+static protocol_binary_response_status decrement_handler(const void *cookie,
+                                                         const void *key,
+                                                         uint16_t keylen,
+                                                         uint64_t delta,
+                                                         uint64_t initial,
+                                                         uint32_t expiration,
+                                                         uint64_t *result,
+                                                         uint64_t *result_cas) {
+  (void)cookie;
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+  uint64_t val= initial;
+  struct item *item= get_item(key, keylen);
+
+  if (item != NULL)
+  {
+    if (delta > *(uint64_t*)item->data)
+      val= 0;
+    else
+      val= *(uint64_t*)item->data - delta;
+
+    expiration= (uint32_t)item->exp;
+    release_item(item);
+    delete_item(key, keylen);
+  }
+
+  item= create_item(key, keylen, NULL, sizeof(initial), 0, (time_t)expiration);
+  if (item == 0)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
+  }
+  else
+  {
+    memcpy(item->data, &val, sizeof(val));
+    put_item(item);
+    *result= val;
+    *result_cas= item->cas;
+    release_item(item);
+  }
+
+  return rval;
+}
+
+static protocol_binary_response_status delete_handler(const void *, // cookie
+                                                      const void *key,
+                                                      uint16_t keylen,
+                                                      uint64_t cas)
+{
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+
+  if (cas != 0)
+  {
+    struct item *item= get_item(key, keylen);
+    if (item != NULL)
+    {
+      if (item->cas != cas)
+      {
+        release_item(item);
+        return PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
+      }
+      release_item(item);
+    }
+  }
+
+  if (!delete_item(key, keylen))
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
+  }
+
+  return rval;
+}
+
+
+static protocol_binary_response_status flush_handler(const void * /* cookie */, uint32_t /* when */)
+{
+  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+}
+
+static protocol_binary_response_status get_handler(const void *cookie,
+                                                   const void *key,
+                                                   uint16_t keylen,
+                                                   memcached_binary_protocol_get_response_handler response_handler) {
+  struct item *item= get_item(key, keylen);
+
+  if (item == NULL)
+  {
+    return PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
+  }
+
+  protocol_binary_response_status rc;
+  rc= response_handler(cookie, key, (uint16_t)keylen,
+                          item->data, (uint32_t)item->size, item->flags,
+                          item->cas);
+  release_item(item);
+  return rc;
+}
+
+static protocol_binary_response_status increment_handler(const void *cookie,
+                                                         const void *key,
+                                                         uint16_t keylen,
+                                                         uint64_t delta,
+                                                         uint64_t initial,
+                                                         uint32_t expiration,
+                                                         uint64_t *result,
+                                                         uint64_t *result_cas) {
+  (void)cookie;
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+  uint64_t val= initial;
+  struct item *item= get_item(key, keylen);
+
+  if (item != NULL)
+  {
+    val= (*(uint64_t*)item->data) + delta;
+    expiration= (uint32_t)item->exp;
+    release_item(item);
+    delete_item(key, keylen);
+  }
+
+  item= create_item(key, keylen, NULL, sizeof(initial), 0, (time_t)expiration);
+  if (item == NULL)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
+  }
+  else
+  {
+    char buffer[1024] = {0};
+    memcpy(buffer, key, keylen);
+    memcpy(item->data, &val, sizeof(val));
+    put_item(item);
+    *result= val;
+    *result_cas= item->cas;
+    release_item(item);
+  }
+
+  return rval;
+}
+
+static protocol_binary_response_status noop_handler(const void *cookie) {
+  (void)cookie;
+  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+}
+
+static protocol_binary_response_status prepend_handler(const void *cookie,
+                                                       const void *key,
+                                                       uint16_t keylen,
+                                                       const void* val,
+                                                       uint32_t vallen,
+                                                       uint64_t cas,
+                                                       uint64_t *result_cas) {
+  (void)cookie;
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+
+  struct item *item= get_item(key, keylen);
+  struct item *nitem= NULL;
+
+  if (item == NULL)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
+  }
+  else if (cas != 0 && cas != item->cas)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
+  }
+  else if ((nitem= create_item(key, keylen, NULL, item->size + vallen,
+                                 item->flags, item->exp)) == NULL)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
+  }
+  else
+  {
+    memcpy(nitem->data, val, vallen);
+    memcpy(((char*)(nitem->data)) + vallen, item->data, item->size);
+    release_item(item);
+    item= NULL;
+    delete_item(key, keylen);
+    put_item(nitem);
+    *result_cas= nitem->cas;
+  }
+
+  if (item)
+    release_item(item);
+
+  if (nitem)
+    release_item(nitem);
+
+  return rval;
+}
+
+static protocol_binary_response_status quit_handler(const void *) //cookie
+{
+  return PROTOCOL_BINARY_RESPONSE_SUCCESS;
+}
+
+static protocol_binary_response_status replace_handler(const void *, // cookie
+                                                       const void *key,
+                                                       uint16_t keylen,
+                                                       const void* data,
+                                                       uint32_t datalen,
+                                                       uint32_t flags,
+                                                       uint32_t exptime,
+                                                       uint64_t cas,
+                                                       uint64_t *result_cas)
+{
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+  struct item* item= get_item(key, keylen);
+
+  if (item == NULL)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
+  }
+  else if (cas == 0 || cas == item->cas)
+  {
+    release_item(item);
+    delete_item(key, keylen);
+    item= create_item(key, keylen, data, datalen, flags, (time_t)exptime);
+    if (item == 0)
+    {
+      rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
+    }
+    else
+    {
+      put_item(item);
+      *result_cas= item->cas;
+      release_item(item);
+    }
+  }
+  else
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
+    release_item(item);
+  }
+
+  return rval;
+}
+
+static protocol_binary_response_status set_handler(const void *cookie,
+                                                   const void *key,
+                                                   uint16_t keylen,
+                                                   const void* data,
+                                                   uint32_t datalen,
+                                                   uint32_t flags,
+                                                   uint32_t exptime,
+                                                   uint64_t cas,
+                                                   uint64_t *result_cas) {
+  (void)cookie;
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_SUCCESS;
+
+  if (cas != 0)
+  {
+    struct item* item= get_item(key, keylen);
+    if (item != NULL && cas != item->cas)
+    {
+      /* Invalid CAS value */
+      release_item(item);
+      return PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
+    }
+  }
+
+  delete_item(key, keylen);
+  struct item* item= create_item(key, keylen, data, datalen, flags, (time_t)exptime);
+  if (item == 0)
+  {
+    rval= PROTOCOL_BINARY_RESPONSE_ENOMEM;
+  }
+  else
+  {
+    put_item(item);
+    *result_cas= item->cas;
+    release_item(item);
+  }
+
+  return rval;
+}
+
+static protocol_binary_response_status stat_handler(const void *cookie,
+                                                    const void *, // key
+                                                    uint16_t, // keylen,
+                                                    memcached_binary_protocol_stat_response_handler response_handler)
+{
+  /* Just return an empty packet */
+  return response_handler(cookie, NULL, 0, NULL, 0);
+}
+
+static protocol_binary_response_status version_handler(const void *cookie,
+                                                       memcached_binary_protocol_version_response_handler response_handler)
+{
+  const char *version= "0.1.1";
+  return response_handler(cookie, version, (uint32_t)strlen(version));
+}
+
+memcached_binary_protocol_callback_st interface_v1_impl;
+
+void initialize_interface_v1_handler(void)
+{
+  memset(&interface_v1_impl, 0, sizeof(memcached_binary_protocol_callback_st));
+
+  interface_v1_impl.interface_version= MEMCACHED_PROTOCOL_HANDLER_V1;
+  interface_v1_impl.interface.v1.add= add_handler;
+  interface_v1_impl.interface.v1.append= append_handler;
+  interface_v1_impl.interface.v1.decrement= decrement_handler;
+  interface_v1_impl.interface.v1.delete_object= delete_handler;
+  interface_v1_impl.interface.v1.flush_object= flush_handler;
+  interface_v1_impl.interface.v1.get= get_handler;
+  interface_v1_impl.interface.v1.increment= increment_handler;
+  interface_v1_impl.interface.v1.noop= noop_handler;
+  interface_v1_impl.interface.v1.prepend= prepend_handler;
+  interface_v1_impl.interface.v1.quit= quit_handler;
+  interface_v1_impl.interface.v1.replace= replace_handler;
+  interface_v1_impl.interface.v1.set= set_handler;
+  interface_v1_impl.interface.v1.stat= stat_handler;
+  interface_v1_impl.interface.v1.version= version_handler;
+}
diff --git a/example/memcached_light.c b/example/memcached_light.c
deleted file mode 100644 (file)
index c064e51..0000000
+++ /dev/null
@@ -1,475 +0,0 @@
-/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/**
- * What is a library without an example to show you how to use the library?
- * This example use both interfaces to implement a small memcached server.
- * Please note that this is an exemple on how to use the library, not
- * an implementation of a scalable memcached server. If you look closely
- * at the example it isn't even multithreaded ;-)
- *
- * With that in mind, let me give you some pointers into the source:
- *   storage.c/h       - Implements the item store for this server and not really
- *                       interesting for this example.
- *   interface_v0.c    - Shows an implementation of the memcached server by using
- *                       the "raw" access to the packets as they arrive
- *   interface_v1.c    - Shows an implementation of the memcached server by using
- *                       the more "logical" interface.
- *   memcached_light.c - This file sets up all of the sockets and run the main
- *                       message loop.
- *
- *
- * config.h is included so that I can use the ntohll/htonll on platforms that
- * doesn't have that (this is a private function inside libmemcached, so you
- * cannot use it directly from libmemcached without special modifications to
- * the library)
- */
-
-#include "config.h"
-#include <assert.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <event.h>
-
-#include <libmemcachedprotocol-0.0/handler.h>
-#include <libmemcached/socket.hpp>
-#include <example/byteorder.h>
-#include "example/storage.h"
-#include "example/memcached_light.h"
-
-extern memcached_binary_protocol_callback_st interface_v0_impl;
-extern memcached_binary_protocol_callback_st interface_v1_impl;
-
-static memcached_socket_t server_sockets[1024];
-static int num_server_sockets= 0;
-
-struct connection
-{
-  void *userdata;
-  struct event event;
-};
-
-/* The default maximum number of connections... (change with -c) */
-static int maxconns = 1024;
-
-static struct connection *socket_userdata_map;
-static bool verbose= false;
-static struct event_base *event_base;
-
-struct options_st {
-  char *pid_file;
-  bool has_port;
-  in_port_t port;
-} global_options;
-
-typedef struct options_st options_st;
-
-/**
- * Callback for driving a client connection
- * @param fd the socket for the client socket
- * @param which identifying the event that occurred (not used)
- * @param arg the connection structure for the client
- */
-static void drive_client(memcached_socket_t fd, short which, void *arg)
-{
-  (void)which;
-  struct connection *client= arg;
-  struct memcached_protocol_client_st* c= client->userdata;
-  assert(c != NULL);
-
-  memcached_protocol_event_t events= memcached_protocol_client_work(c);
-  if (events & MEMCACHED_PROTOCOL_ERROR_EVENT)
-  {
-    memcached_protocol_client_destroy(c);
-    closesocket(fd);
-  } else {
-    short flags = 0;
-    if (events & MEMCACHED_PROTOCOL_WRITE_EVENT)
-    {
-      flags= EV_WRITE;
-    }
-
-    if (events & MEMCACHED_PROTOCOL_READ_EVENT)
-    {
-      flags|= EV_READ;
-    }
-
-    event_set(&client->event, (intptr_t)fd, flags, drive_client, client);
-    event_base_set(event_base, &client->event);
-
-    if (event_add(&client->event, 0) == -1)
-    {
-      (void)fprintf(stderr, "Failed to add event for %d\n", fd);
-      memcached_protocol_client_destroy(c);
-      closesocket(fd);
-    }
-  }
-}
-
-/**
- * Callback for accepting new connections
- * @param fd the socket for the server socket
- * @param which identifying the event that occurred (not used)
- * @param arg the connection structure for the server
- */
-static void accept_handler(memcached_socket_t fd, short which, void *arg)
-{
-  (void)which;
-  struct connection *server= arg;
-  /* accept new client */
-  struct sockaddr_storage addr;
-  socklen_t addrlen= sizeof(addr);
-  memcached_socket_t sock= accept(fd, (struct sockaddr *)&addr, &addrlen);
-
-  if (sock == INVALID_SOCKET)
-  {
-    perror("Failed to accept client");
-    return ;
-  }
-
-#ifndef WIN32
-  if (sock >= maxconns)
-  {
-    (void)fprintf(stderr, "Client outside socket range (specified with -c)\n");
-    closesocket(sock);
-    return ;
-  }
-#endif
-
-  struct memcached_protocol_client_st* c;
-  c= memcached_protocol_create_client(server->userdata, sock);
-  if (c == NULL)
-  {
-    (void)fprintf(stderr, "Failed to create client\n");
-    closesocket(sock);
-  }
-  else
-  {
-    struct connection *client = &socket_userdata_map[sock];
-    client->userdata= c;
-
-    event_set(&client->event, (intptr_t)sock, EV_READ, drive_client, client);
-    event_base_set(event_base, &client->event);
-    if (event_add(&client->event, 0) == -1)
-    {
-      (void)fprintf(stderr, "Failed to add event for %d\n", sock);
-      memcached_protocol_client_destroy(c);
-      closesocket(sock);
-    }
-  }
-}
-
-/**
- * Create a socket and bind it to a specific port number
- * @param port the port number to bind to
- */
-static int server_socket(const char *port)
-{
-  struct addrinfo *ai;
-  struct addrinfo hints= { .ai_flags= AI_PASSIVE,
-                           .ai_family= AF_UNSPEC,
-                           .ai_socktype= SOCK_STREAM };
-
-  int error= getaddrinfo("127.0.0.1", port, &hints, &ai);
-  if (error != 0)
-  {
-    if (error != EAI_SYSTEM)
-      fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
-    else
-      perror("getaddrinfo()");
-
-    return 0;
-  }
-
-  struct linger ling= {0, 0};
-
-  for (struct addrinfo *next= ai; next; next= next->ai_next)
-  {
-    memcached_socket_t sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-    if (sock == INVALID_SOCKET)
-    {
-      perror("Failed to create socket");
-      continue;
-    }
-
-    int flags;
-#ifdef WIN32
-    u_long arg = 1;
-    if (ioctlsocket(sock, FIONBIO, &arg) == SOCKET_ERROR)
-    {
-      perror("Failed to set nonblocking io");
-      closesocket(sock);
-      continue;
-    }
-#else
-    flags= fcntl(sock, F_GETFL, 0);
-    if (flags == -1)
-    {
-      perror("Failed to get socket flags");
-      closesocket(sock);
-      continue;
-    }
-
-    if ((flags & O_NONBLOCK) != O_NONBLOCK)
-    {
-      if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
-      {
-        perror("Failed to set socket to nonblocking mode");
-        closesocket(sock);
-        continue;
-      }
-    }
-#endif
-
-    flags= 1;
-    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)) != 0)
-      perror("Failed to set SO_REUSEADDR");
-
-    if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) != 0)
-      perror("Failed to set SO_KEEPALIVE");
-
-    if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)) != 0)
-      perror("Failed to set SO_LINGER");
-
-    if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) != 0)
-      perror("Failed to set TCP_NODELAY");
-
-    if (bind(sock, next->ai_addr, next->ai_addrlen) == SOCKET_ERROR)
-    {
-      if (get_socket_errno() != EADDRINUSE)
-      {
-        perror("bind()");
-        freeaddrinfo(ai);
-      }
-      closesocket(sock);
-      continue;
-    }
-
-    if (listen(sock, 1024) == SOCKET_ERROR)
-    {
-      perror("listen()");
-      closesocket(sock);
-      continue;
-    }
-
-    server_sockets[num_server_sockets++]= sock;
-  }
-
-  freeaddrinfo(ai);
-
-  return (num_server_sockets > 0) ? 0 : 1;
-}
-
-/**
- * Convert a command code to a textual string
- * @param cmd the comcode to convert
- * @return a textual string with the command or NULL for unknown commands
- */
-static const char* comcode2str(uint8_t cmd)
-{
-  static const char * const text[] = {
-    "GET", "SET", "ADD", "REPLACE", "DELETE",
-    "INCREMENT", "DECREMENT", "QUIT", "FLUSH",
-    "GETQ", "NOOP", "VERSION", "GETK", "GETKQ",
-    "APPEND", "PREPEND", "STAT", "SETQ", "ADDQ",
-    "REPLACEQ", "DELETEQ", "INCREMENTQ", "DECREMENTQ",
-    "QUITQ", "FLUSHQ", "APPENDQ", "PREPENDQ"
-  };
-
-  if (cmd <= PROTOCOL_BINARY_CMD_PREPENDQ)
-    return text[cmd];
-
-  return NULL;
-}
-
-/**
- * Print out the command we are about to execute
- */
-static void pre_execute(const void *cookie,
-                        protocol_binary_request_header *header)
-{
-  if (verbose)
-  {
-    const char *cmd= comcode2str(header->request.opcode);
-    if (cmd != NULL)
-      fprintf(stderr, "pre_execute from %p: %s\n", cookie, cmd);
-    else
-      fprintf(stderr, "pre_execute from %p: 0x%02x\n", cookie, header->request.opcode);
-  }
-}
-
-/**
- * Print out the command we just executed
- */
-static void post_execute(const void *cookie,
-                         protocol_binary_request_header *header)
-{
-  if (verbose)
-  {
-    const char *cmd= comcode2str(header->request.opcode);
-    if (cmd != NULL)
-      fprintf(stderr, "post_execute from %p: %s\n", cookie, cmd);
-    else
-      fprintf(stderr, "post_execute from %p: 0x%02x\n", cookie, header->request.opcode);
-  }
-}
-
-/**
- * Callback handler for all unknown commands.
- * Send an unknown command back to the client
- */
-static protocol_binary_response_status unknown(const void *cookie,
-                                               protocol_binary_request_header *header,
-                                               memcached_binary_protocol_raw_response_handler response_handler)
-{
-  protocol_binary_response_no_extras response= {
-    .message= {
-      .header.response= {
-        .magic= PROTOCOL_BINARY_RES,
-        .opcode= header->request.opcode,
-        .status= htons(PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND),
-        .opaque= header->request.opaque
-      }
-    }
-  };
-
-  return response_handler(cookie, header, (void*)&response);
-}
-
-/**
- * Program entry point. Bind to the specified port(s) and serve clients
- *
- * @param argc number of items in the argument vector
- * @param argv argument vector
- * @return EXIT_SUCCESS on success, 1 otherwise
- */
-int main(int argc, char **argv)
-{
-  int cmd;
-  memcached_binary_protocol_callback_st *interface= &interface_v0_impl;
-
-  memset(&global_options, 0, sizeof(global_options));
-
-  event_base= event_init();
-  if (event_base == NULL)
-  {
-    fprintf(stderr, "Failed to create an instance of libevent\n");
-    return EXIT_FAILURE;
-  }
-
-  /*
-   * We need to initialize the handlers manually due to a bug in the
-   * warnings generated by struct initialization in gcc (all the way up to 4.4)
-   */
-  initialize_interface_v0_handler();
-
-  while ((cmd= getopt(argc, argv, "v1p:P:?hc:")) != EOF)
-  {
-    switch (cmd) {
-    case '1':
-      interface= &interface_v1_impl;
-      break;
-    case 'P':
-      global_options.pid_file= strdup(optarg);
-      break;
-    case 'p':
-      global_options.has_port= true;
-      (void)server_socket(optarg);
-      break;
-    case 'v':
-      verbose= true;
-      break;
-    case 'c':
-      maxconns= atoi(optarg);
-      break;
-    case 'h':  /* FALLTHROUGH */
-    case '?':  /* FALLTHROUGH */
-    default:
-      (void)fprintf(stderr, "Usage: %s [-p port] [-v] [-1] [-c #clients] [-P pidfile]\n",
-                    argv[0]);
-      return EXIT_FAILURE;
-    }
-  }
-
-  if (! initialize_storage())
-  {
-    /* Error message already printed */
-    return EXIT_FAILURE;
-  }
-
-  if (! global_options.has_port)
-    (void)server_socket("9999");
-
-  if (global_options.pid_file)
-  {
-    FILE *pid_file;
-    uint32_t pid;
-
-    pid_file= fopen(global_options.pid_file, "w+");
-
-    if (pid_file == NULL)
-    {
-      perror(strerror(get_socket_errno()));
-      abort();
-    }
-
-    pid= (uint32_t)getpid();
-    fprintf(pid_file, "%u\n", pid);
-    fclose(pid_file);
-  }
-
-  if (num_server_sockets == 0)
-  {
-    fprintf(stderr, "I don't have any server sockets\n");
-    return EXIT_FAILURE;
-  }
-
-  /*
-   * Create and initialize the handles to the protocol handlers. I want
-   * to be able to trace the traffic throught the pre/post handlers, and
-   * set up a common handler for unknown messages
-   */
-  interface->pre_execute= pre_execute;
-  interface->post_execute= post_execute;
-  interface->unknown= unknown;
-
-  struct memcached_protocol_st *protocol_handle;
-  if ((protocol_handle= memcached_protocol_create_instance()) == NULL)
-  {
-    fprintf(stderr, "Failed to allocate protocol handle\n");
-    return EXIT_FAILURE;
-  }
-
-  socket_userdata_map= calloc((size_t)(maxconns), sizeof(struct connection));
-  if (socket_userdata_map == NULL)
-  {
-    fprintf(stderr, "Failed to allocate room for connections\n");
-    return EXIT_FAILURE;
-  }
-
-  memcached_binary_protocol_set_callbacks(protocol_handle, interface);
-  memcached_binary_protocol_set_pedantic(protocol_handle, true);
-
-  for (int xx= 0; xx < num_server_sockets; ++xx)
-  {
-    struct connection *conn= &socket_userdata_map[server_sockets[xx]];
-    conn->userdata= protocol_handle;
-    event_set(&conn->event, (intptr_t)server_sockets[xx], EV_READ | EV_PERSIST,
-              accept_handler, conn);
-    event_base_set(event_base, &conn->event);
-    if (event_add(&conn->event, 0) == -1)
-    {
-      fprintf(stderr, "Failed to add event for %d\n", server_sockets[xx]);
-      closesocket(server_sockets[xx]);
-    }
-  }
-
-  /* Serve all of the clients */
-  event_base_loop(event_base, 0);
-
-  /* NOTREACHED */
-  return EXIT_SUCCESS;
-}
diff --git a/example/memcached_light.cc b/example/memcached_light.cc
new file mode 100644 (file)
index 0000000..61c5314
--- /dev/null
@@ -0,0 +1,644 @@
+/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/**
+ * What is a library without an example to show you how to use the library?
+ * This example use both interfaces to implement a small memcached server.
+ * Please note that this is an exemple on how to use the library, not
+ * an implementation of a scalable memcached server. If you look closely
+ * at the example it isn't even multithreaded ;-)
+ *
+ * With that in mind, let me give you some pointers into the source:
+ *   storage.c/h       - Implements the item store for this server and not really
+ *                       interesting for this example.
+ *   interface_v0.c    - Shows an implementation of the memcached server by using
+ *                       the "raw" access to the packets as they arrive
+ *   interface_v1.c    - Shows an implementation of the memcached server by using
+ *                       the more "logical" interface.
+ *   memcached_light.c - This file sets up all of the sockets and run the main
+ *                       message loop.
+ *
+ *
+ * config.h is included so that I can use the ntohll/htonll on platforms that
+ * doesn't have that (this is a private function inside libmemcached, so you
+ * cannot use it directly from libmemcached without special modifications to
+ * the library)
+ */
+
+#include "config.h"
+
+#include <libmemcachedprotocol-0.0/handler.h>
+#include <libmemcached/socket.hpp>
+#include <example/byteorder.h>
+#include "example/storage.h"
+#include "example/memcached_light.h"
+
+#include "util/daemon.hpp"
+#include "util/log.hpp"
+#include "util/pidfile.hpp"
+
+using namespace datadifferential;
+
+#include <event.h>
+
+#include <cassert>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <getopt.h>
+#include <iostream>
+#include <sys/types.h>
+#include <unistd.h>
+
+extern memcached_binary_protocol_callback_st interface_v0_impl;
+extern memcached_binary_protocol_callback_st interface_v1_impl;
+
+static memcached_socket_t server_sockets[1024];
+static int num_server_sockets= 0;
+
+struct connection
+{
+  void *userdata;
+  struct event event;
+};
+
+/* The default maximum number of connections... (change with -c) */
+static int maxconns= 1024;
+
+static struct connection *socket_userdata_map;
+static struct event_base *event_base= NULL;
+
+struct options_st {
+  std::string pid_file;
+  std::string service;
+  std::string log_file;
+  bool is_verbose;
+  bool opt_daemon;
+
+  options_st() :
+    service("9999"),
+    is_verbose(false)
+  {
+  }
+};
+
+static options_st global_options;
+
+/**
+ * Callback for driving a client connection
+ * @param fd the socket for the client socket
+ * @param which identifying the event that occurred (not used)
+ * @param arg the connection structure for the client
+ */
+static void drive_client(memcached_socket_t fd, short, void *arg)
+{
+  struct connection *client= (struct connection*)arg;
+  struct memcached_protocol_client_st* c= (struct memcached_protocol_client_st*)client->userdata;
+  assert(c != NULL);
+
+  memcached_protocol_event_t events= memcached_protocol_client_work(c);
+  if (events & MEMCACHED_PROTOCOL_ERROR_EVENT)
+  {
+    if (global_options.is_verbose)
+    {
+      struct sockaddr_in sin;
+      socklen_t addrlen= sizeof(sin);
+
+      if (getsockname(fd, (struct sockaddr *)&sin, &addrlen) != -1)
+      {
+        std::cout << __FILE__ << ":" << __LINE__
+          << " close(MEMCACHED_PROTOCOL_ERROR_EVENT)"
+          << " " << inet_ntoa(sin.sin_addr) << ":" << sin.sin_port
+          << " fd:" << fd
+          << std::endl;
+      }
+      else
+      {
+        std::cout << __FILE__ << ":" << __LINE__ << "close() MEMCACHED_PROTOCOL_ERROR_EVENT" << std::endl;
+      }
+    }
+
+    memcached_protocol_client_destroy(c);
+    closesocket(fd);
+  }
+  else
+  {
+    short flags = 0;
+    if (events & MEMCACHED_PROTOCOL_WRITE_EVENT)
+    {
+      flags= EV_WRITE;
+    }
+
+    if (events & MEMCACHED_PROTOCOL_READ_EVENT)
+    {
+      flags|= EV_READ;
+    }
+
+    event_set(&client->event, (intptr_t)fd, flags, drive_client, client);
+    event_base_set(event_base, &client->event);
+
+    if (event_add(&client->event, 0) == -1)
+    {
+      memcached_protocol_client_destroy(c);
+      closesocket(fd);
+    }
+  }
+}
+
+/**
+ * Callback for accepting new connections
+ * @param fd the socket for the server socket
+ * @param which identifying the event that occurred (not used)
+ * @param arg the connection structure for the server
+ */
+static void accept_handler(memcached_socket_t fd, short, void *arg)
+{
+  struct connection *server= (struct connection *)arg;
+  /* accept new client */
+  struct sockaddr_storage addr;
+  socklen_t addrlen= sizeof(addr);
+  memcached_socket_t sock= accept(fd, (struct sockaddr *)&addr, &addrlen);
+
+  if (sock == INVALID_SOCKET)
+  {
+    perror("Failed to accept client");
+  }
+
+#ifndef WIN32
+  if (sock >= maxconns)
+  {
+    closesocket(sock);
+    return ;
+  }
+#endif
+
+  struct memcached_protocol_client_st* c= memcached_protocol_create_client((memcached_protocol_st*)server->userdata, sock);
+  if (c == NULL)
+  {
+    closesocket(sock);
+  }
+  else
+  {
+    memcached_protocol_client_set_verbose(c, global_options.is_verbose);
+    struct connection *client = &socket_userdata_map[sock];
+    client->userdata= c;
+
+    event_set(&client->event, (intptr_t)sock, EV_READ, drive_client, client);
+    event_base_set(event_base, &client->event);
+    if (event_add(&client->event, 0) == -1)
+    {
+      std::cerr << "Failed to add event for " << sock << std::endl;
+      memcached_protocol_client_destroy(c);
+      closesocket(sock);
+    }
+  }
+}
+
+static bool server_socket(util::log_info_st& log_file, const std::string& service)
+{
+  struct addrinfo *ai;
+  struct addrinfo hints;
+  memset(&hints, 0, sizeof(struct addrinfo));
+
+  hints.ai_flags= AI_PASSIVE;
+  hints.ai_family= AF_UNSPEC;
+  hints.ai_socktype= SOCK_STREAM;
+
+  int error= getaddrinfo("127.0.0.1", service.c_str(), &hints, &ai);
+  if (error != 0)
+  {
+    if (error != EAI_SYSTEM)
+    {
+      std::string buffer("getaddrinfo: ");
+      buffer+= gai_strerror(error);
+      log_file.write(util::VERBOSE_ERROR, buffer.c_str());
+    }
+    else
+    {
+      std::string buffer("getaddrinfo: ");
+      buffer+= strerror(errno);
+      log_file.write(util::VERBOSE_ERROR, buffer.c_str());
+    }
+
+    return false;
+  }
+
+  struct linger ling= {0, 0};
+
+  for (struct addrinfo *next= ai; next; next= next->ai_next)
+  {
+    memcached_socket_t sock= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if (sock == INVALID_SOCKET)
+    {
+      std::string buffer("Failed to create socket: ");
+      buffer+= strerror(errno);
+      log_file.write(util::VERBOSE_ERROR, buffer.c_str());
+      continue;
+    }
+
+    int flags;
+#ifdef WIN32
+    u_long arg = 1;
+    if (ioctlsocket(sock, FIONBIO, &arg) == SOCKET_ERROR)
+    {
+      std::cerr << "Failed to set nonblocking io: " << strerror(errno) << std::endl;
+      closesocket(sock);
+      continue;
+    }
+#else
+    flags= fcntl(sock, F_GETFL, 0);
+    if (flags == -1)
+    {
+      std::string buffer("Failed to get socket flags: ");
+      buffer+= strerror(errno);
+      log_file.write(util::VERBOSE_ERROR, buffer.c_str());
+      closesocket(sock);
+      continue;
+    }
+
+    if ((flags & O_NONBLOCK) != O_NONBLOCK)
+    {
+      if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)
+      {
+        std::string buffer("Failed to set socket to nonblocking mode: ");
+        buffer+= strerror(errno);
+        log_file.write(util::VERBOSE_ERROR, buffer.c_str());
+        closesocket(sock);
+        continue;
+      }
+    }
+#endif
+
+    flags= 1;
+    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)) != 0)
+    {
+      std::cerr << "Failed to set SO_REUSEADDR: " << strerror(errno) << std::endl;
+    }
+
+    if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)) != 0)
+    {
+      std::cerr << "Failed to set SO_KEEPALIVE: " << strerror(errno) << std::endl;
+    }
+
+    if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)) != 0)
+    {
+      std::cerr << "Failed to set SO_LINGER: " << strerror(errno) << std::endl;
+    }
+
+    if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)) != 0)
+    {
+      std::cerr << "Failed to set TCP_NODELAY: " << strerror(errno) << std::endl;
+    }
+
+    if (bind(sock, next->ai_addr, next->ai_addrlen) == SOCKET_ERROR)
+    {
+      if (get_socket_errno() != EADDRINUSE)
+      {
+        std::cerr << "bind(): " << strerror(errno) << std::endl;
+        freeaddrinfo(ai);
+      }
+      closesocket(sock);
+      continue;
+    }
+
+    if (listen(sock, 1024) == SOCKET_ERROR)
+    {
+      std::string buffer("listen(): ");
+      buffer+= strerror(errno);
+      log_file.write(util::VERBOSE_ERROR, buffer.c_str());
+      closesocket(sock);
+      continue;
+    }
+
+    if (global_options.is_verbose)
+    {
+      std::string buffer("Listening to: ");
+      buffer+= global_options.service;
+      log_file.write(util::VERBOSE_NOTICE, buffer.c_str());
+    }
+
+    server_sockets[num_server_sockets++]= sock;
+  }
+
+  freeaddrinfo(ai);
+
+  return (num_server_sockets > 0) ? true : false;
+}
+
+/**
+ * Convert a command code to a textual string
+ * @param cmd the comcode to convert
+ * @return a textual string with the command or NULL for unknown commands
+ */
+static const char* comcode2str(uint8_t cmd)
+{
+  static const char * const text[] = {
+    "GET", "SET", "ADD", "REPLACE", "DELETE",
+    "INCREMENT", "DECREMENT", "QUIT", "FLUSH",
+    "GETQ", "NOOP", "VERSION", "GETK", "GETKQ",
+    "APPEND", "PREPEND", "STAT", "SETQ", "ADDQ",
+    "REPLACEQ", "DELETEQ", "INCREMENTQ", "DECREMENTQ",
+    "QUITQ", "FLUSHQ", "APPENDQ", "PREPENDQ"
+  };
+
+  if (cmd <= PROTOCOL_BINARY_CMD_PREPENDQ)
+  {
+    return text[cmd];
+  }
+
+  return NULL;
+}
+
+/**
+ * Print out the command we are about to execute
+ */
+static void pre_execute(const void *cookie,
+                        protocol_binary_request_header *header)
+{
+  if (global_options.is_verbose)
+  {
+    if (header)
+    {
+      const char *cmd= comcode2str(header->request.opcode);
+      if (cmd != NULL)
+      {
+        std::cout << "pre_execute from " << cookie << ": " << cmd << std::endl;
+      }
+      else
+      {
+        std::cout << "pre_execute from " << cookie << ": " << header->request.opcode << std::endl;
+      }
+    }
+    else
+    {
+      std::cout << "pre_execute from " << cookie << std::endl;
+    }
+  }
+}
+
+/**
+ * Print out the command we just executed
+ */
+static void post_execute(const void *cookie,
+                         protocol_binary_request_header *header)
+{
+  if (global_options.is_verbose)
+  {
+    if (header)
+    {
+      const char *cmd= comcode2str(header->request.opcode);
+      if (cmd != NULL)
+      {
+        std::cout << "post_execute from " << cookie << ": " << cmd << std::endl;
+      }
+      else
+      {
+        std::cout << "post_execute from " << cookie << ": " << header->request.opcode << std::endl;
+      }
+    }
+    else
+    {
+      std::cout << "post_execute from " << cookie << std::endl;
+    }
+  }
+}
+
+/**
+ * Callback handler for all unknown commands.
+ * Send an unknown command back to the client
+ */
+static protocol_binary_response_status unknown(const void *cookie,
+                                               protocol_binary_request_header *header,
+                                               memcached_binary_protocol_raw_response_handler response_handler)
+{
+  protocol_binary_response_no_extras response;
+  memset(&response, 0, sizeof(protocol_binary_response_no_extras));
+
+  response.message.header.response.magic= PROTOCOL_BINARY_RES;
+  response.message.header.response.opcode= header->request.opcode;
+  response.message.header.response.status= htons(PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND);
+  response.message.header.response.opaque= header->request.opaque;
+
+  return response_handler(cookie, header, (protocol_binary_response_header*)&response);
+}
+
+/**
+ * Program entry point. Bind to the specified port(s) and serve clients
+ *
+ * @param argc number of items in the argument vector
+ * @param argv argument vector
+ * @return EXIT_SUCCESS on success, 1 otherwise
+ */
+int main(int argc, char **argv)
+{
+  memcached_binary_protocol_callback_st *interface= &interface_v0_impl;
+
+  /*
+   * We need to initialize the handlers manually due to a bug in the
+   * warnings generated by struct initialization in gcc (all the way up to 4.4)
+   */
+  initialize_interface_v0_handler();
+  initialize_interface_v1_handler();
+
+  {
+    enum long_option_t {
+      OPT_HELP,
+      OPT_VERBOSE,
+      OPT_DAEMON,
+      OPT_PROTOCOL_VERSION,
+      OPT_VERSION,
+      OPT_PORT,
+      OPT_MAX_CONNECTIONS,
+      OPT_LOGFILE,
+      OPT_PIDFILE
+    };
+
+    static struct option long_options[]=
+    {
+      { "help", no_argument, NULL, OPT_HELP },
+      { "port", required_argument, NULL, OPT_PORT },
+      { "verbose", no_argument, NULL, OPT_VERBOSE },
+      { "daemon", no_argument, NULL, OPT_DAEMON },
+      { "protocol", no_argument, NULL, OPT_PROTOCOL_VERSION },
+      { "version", no_argument, NULL, OPT_VERSION },
+      { "max-connections", required_argument, NULL, OPT_MAX_CONNECTIONS },
+      { "pid-file", required_argument, NULL, OPT_PIDFILE },
+      { "log-file", required_argument, NULL, OPT_LOGFILE },
+      {0, 0, 0, 0}
+    };
+
+    bool opt_help= false;
+    int option_index;
+    bool done= false;
+    while (done == false)
+    {
+      switch (getopt_long(argc, argv, "", long_options, &option_index))
+      {
+      case -1:
+        done= true;
+        break;
+
+      case OPT_PROTOCOL_VERSION:
+        interface= &interface_v1_impl;
+        break;
+
+      case OPT_PIDFILE:
+        global_options.pid_file= optarg;
+        break;
+
+      case OPT_LOGFILE:
+        global_options.log_file= optarg;
+        break;
+
+      case OPT_VERBOSE:
+        global_options.is_verbose= true;
+        break;
+
+      case OPT_VERSION:
+        break;
+
+      case OPT_DAEMON:
+        global_options.opt_daemon= true;
+        break;
+
+      case OPT_PORT:
+        global_options.service= optarg;
+        break;
+
+      case OPT_MAX_CONNECTIONS:
+        maxconns= atoi(optarg);
+        break;
+
+      case OPT_HELP:  /* FALLTHROUGH */
+        opt_help= true;
+        break;
+
+      default:
+        {
+          std::cerr << "Unknown option: " << optarg << std::endl;
+          return EXIT_FAILURE;
+        }
+      }
+    }
+
+    if (opt_help)
+    {
+      std::cout << "Usage: " << argv[0] << std::endl;
+      for (struct option *ptr_option= long_options; ptr_option->name; ptr_option++)
+      {
+        std::cout << "\t" << ptr_option->name << std::endl;
+      }
+      return EXIT_SUCCESS;
+    }
+  }
+
+  if (global_options.opt_daemon)
+  {
+    util::daemonize(false, true);
+  }
+
+  if (initialize_storage() == false)
+  {
+    /* Error message already printed */
+    return EXIT_FAILURE;
+  }
+
+  util::Pidfile _pid_file(global_options.pid_file);
+
+  if (_pid_file.create() == false)
+  {
+    std::cerr << "Failed to create pid-file" <<  _pid_file.error_message() << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  util::log_info_st log_file(argv[0], global_options.log_file, false);
+  log_file.write(util::VERBOSE_NOTICE, "starting log");
+
+  if (server_socket(log_file, global_options.service) == false)
+  {
+    return EXIT_FAILURE;
+  }
+
+  if (num_server_sockets == 0)
+  {
+    log_file.write(util::VERBOSE_ERROR, "No server sockets are available.");
+    return EXIT_FAILURE;
+  }
+
+  /*
+   * Create and initialize the handles to the protocol handlers. I want
+   * to be able to trace the traffic throught the pre/post handlers, and
+   * set up a common handler for unknown messages
+   */
+  interface->pre_execute= pre_execute;
+  interface->post_execute= post_execute;
+  interface->unknown= unknown;
+
+  struct memcached_protocol_st *protocol_handle;
+  if ((protocol_handle= memcached_protocol_create_instance()) == NULL)
+  {
+    log_file.write(util::VERBOSE_ERROR, "No server sockets are available.");
+    return EXIT_FAILURE;
+  }
+
+  socket_userdata_map= (struct connection*)calloc((size_t)(maxconns), sizeof(struct connection));
+  if (socket_userdata_map == NULL)
+  {
+    log_file.write(util::VERBOSE_ERROR, "Failed to allocate room for connections");
+    return EXIT_FAILURE;
+  }
+
+  memcached_binary_protocol_set_callbacks(protocol_handle, interface);
+  memcached_binary_protocol_set_pedantic(protocol_handle, true);
+
+  event_base= event_init();
+  if (event_base == NULL)
+  {
+    std::cerr << "Failed to create an instance of libevent" << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  for (int xx= 0; xx < num_server_sockets; ++xx)
+  {
+    struct connection *conn= &socket_userdata_map[server_sockets[xx]];
+    conn->userdata= protocol_handle;
+
+    event_set(&conn->event, (intptr_t)server_sockets[xx], EV_READ | EV_PERSIST, accept_handler, conn);
+
+    event_base_set(event_base, &conn->event);
+    if (event_add(&conn->event, 0) == -1)
+    {
+      log_file.write(util::VERBOSE_ERROR, "Failed to add event");
+      closesocket(server_sockets[xx]);
+    }
+  }
+
+  if (global_options.opt_daemon)
+  {
+    if (util::daemon_is_ready(true) == false)
+    {
+      log_file.write(util::VERBOSE_ERROR, "Failed for util::daemon_is_ready()");
+      return EXIT_FAILURE;
+    }
+  }
+
+
+  /* Serve all of the clients */
+  switch (event_base_loop(event_base, 0))
+  {
+  case -1:
+    log_file.write(util::VERBOSE_ERROR, "event_base_loop() failed");
+    break;
+
+  case 1:
+    log_file.write(util::VERBOSE_ERROR, "event_base_loop(), no events were registered");
+    break;
+
+  default:
+    break;
+  }
+  log_file.write(util::VERBOSE_NOTICE, "exiting");
+
+  /* NOTREACHED */
+  return EXIT_SUCCESS;
+}
index 529169393fefd2e92215bc68b516cafd6aa67094..12c4ea79a222acff47de291045f061ee7ba7174a 100644 (file)
@@ -1,7 +1,5 @@
 /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef MEMCACHED_LIGHT_H
-#define MEMCACHED_LIGHT_H
+#pragma once
 
 extern void initialize_interface_v0_handler(void);
-
-#endif
+extern void initialize_interface_v1_handler(void);
diff --git a/example/storage.c b/example/storage.c
deleted file mode 100644 (file)
index 1fb7973..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#include "config.h"
-#include <stdlib.h>
-#include <inttypes.h>
-#include <time.h>
-#include <stdbool.h>
-#include <string.h>
-#include "storage.h"
-
-struct list_entry {
-  struct item item;
-  struct list_entry *next;
-  struct list_entry *prev;
-};
-
-static struct list_entry *root;
-static uint64_t cas;
-
-bool initialize_storage(void)
-{
-  return true;
-}
-
-void shutdown_storage(void)
-{
-  /* Do nothing */
-}
-
-void put_item(struct item* item)
-{
-  struct list_entry* entry= (void*)item;
-
-  update_cas(item);
-
-  if (root == NULL)
-  {
-    entry->next= entry->prev= entry;
-  }
-  else
-  {
-    entry->prev= root->prev;
-    entry->next= root;
-    entry->prev->next= entry;
-    entry->next->prev= entry;
-  }
-
-  root= entry;
-}
-
-struct item* get_item(const void* key, size_t nkey)
-{
-  struct list_entry *walker= root;
-
-  if (root == NULL)
-  {
-    return NULL;
-  }
-
-  do
-  {
-    if (((struct item*)walker)->nkey == nkey &&
-        memcmp(((struct item*)walker)->key, key, nkey) == 0)
-    {
-      return (struct item*)walker;
-    }
-    walker= walker->next;
-  } while (walker != root);
-
-  return NULL;
-}
-
-struct item* create_item(const void* key, size_t nkey, const void* data,
-                         size_t size, uint32_t flags, time_t exp)
-{
-  struct item* ret= calloc(1, sizeof(struct list_entry));
-
-  if (ret != NULL)
-  {
-    ret->key= malloc(nkey);
-    if (size > 0)
-    {
-      ret->data= malloc(size);
-    }
-
-    if (ret->key == NULL || (size > 0 && ret->data == NULL))
-    {
-      free(ret->key);
-      free(ret->data);
-      free(ret);
-      return NULL;
-    }
-
-    memcpy(ret->key, key, nkey);
-    if (data != NULL)
-    {
-      memcpy(ret->data, data, size);
-    }
-
-    ret->nkey= nkey;
-    ret->size= size;
-    ret->flags= flags;
-    ret->exp= exp;
-  }
-
-  return ret;
-}
-
-bool delete_item(const void* key, size_t nkey)
-{
-  struct item* item= get_item(key, nkey);
-  bool ret= false;
-
-  if (item)
-  {
-    /* remove from linked list */
-    struct list_entry *entry= (void*)item;
-
-    if (entry->next == entry)
-    {
-      /* Only one object in the list */
-      root= NULL;
-    }
-    else
-    {
-      /* ensure that we don't loose track of the root, and this will
-       * change the start position for the next search ;-) */
-      root= entry->next;
-      entry->prev->next= entry->next;
-      entry->next->prev= entry->prev;
-    }
-
-    free(item->key);
-    free(item->data);
-    free(item);
-    ret= true;
-  }
-
-  return ret;
-}
-
-void flush(uint32_t when)
-{
-  /* FIXME */
-  (void)when;
-  /* remove the complete linked list */
-  if (root == NULL)
-  {
-    return;
-  }
-
-  root->prev->next= NULL;
-  while (root != NULL)
-  {
-    struct item* tmp= (void*)root;
-    root= root->next;
-
-    free(tmp->key);
-    free(tmp->data);
-    free(tmp);
-  }
-}
-
-void update_cas(struct item* item)
-{
-  item->cas= ++cas;
-}
-
-void release_item(struct item* item)
-{
-  (void)item;
-  /* EMPTY */
-}
diff --git a/example/storage.cc b/example/storage.cc
new file mode 100644 (file)
index 0000000..2e63a3e
--- /dev/null
@@ -0,0 +1,168 @@
+/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#include "config.h"
+#include <stdlib.h>
+#include <inttypes.h>
+#include <time.h>
+#include <stdbool.h>
+#include <string.h>
+#include "storage.h"
+
+struct list_entry {
+  struct item item;
+  struct list_entry *next;
+  struct list_entry *prev;
+};
+
+static struct list_entry *root;
+static uint64_t cas;
+
+bool initialize_storage(void)
+{
+  return true;
+}
+
+void shutdown_storage(void)
+{
+  /* Do nothing */
+}
+
+void put_item(struct item* item)
+{
+  struct list_entry* entry= (struct list_entry*)item;
+
+  update_cas(item);
+
+  if (root == NULL)
+  {
+    entry->next= entry->prev= entry;
+  }
+  else
+  {
+    entry->prev= root->prev;
+    entry->next= root;
+    entry->prev->next= entry;
+    entry->next->prev= entry;
+  }
+
+  root= entry;
+}
+
+struct item* get_item(const void* key, size_t nkey)
+{
+  struct list_entry *walker= root;
+
+  if (root == NULL)
+  {
+    return NULL;
+  }
+
+  do
+  {
+    if (((struct item*)walker)->nkey == nkey &&
+        memcmp(((struct item*)walker)->key, key, nkey) == 0)
+    {
+      return (struct item*)walker;
+    }
+    walker= walker->next;
+  } while (walker != root);
+
+  return NULL;
+}
+
+struct item* create_item(const void* key, size_t nkey, const void* data,
+                         size_t size, uint32_t flags, time_t exp)
+{
+  struct item* ret= (struct item*)calloc(1, sizeof(struct list_entry));
+
+  if (ret != NULL)
+  {
+    ret->key= malloc(nkey);
+    if (size > 0)
+    {
+      ret->data= malloc(size);
+    }
+
+    if (ret->key == NULL || (size > 0 && ret->data == NULL))
+    {
+      free(ret->key);
+      free(ret->data);
+      free(ret);
+      return NULL;
+    }
+
+    memcpy(ret->key, key, nkey);
+    if (data != NULL)
+    {
+      memcpy(ret->data, data, size);
+    }
+
+    ret->nkey= nkey;
+    ret->size= size;
+    ret->flags= flags;
+    ret->exp= exp;
+  }
+
+  return ret;
+}
+
+bool delete_item(const void* key, size_t nkey)
+{
+  struct item* item= get_item(key, nkey);
+  bool ret= false;
+
+  if (item)
+  {
+    /* remove from linked list */
+    struct list_entry *entry= (struct list_entry*)item;
+
+    if (entry->next == entry)
+    {
+      /* Only one object in the list */
+      root= NULL;
+    }
+    else
+    {
+      /* ensure that we don't loose track of the root, and this will
+       * change the start position for the next search ;-) */
+      root= entry->next;
+      entry->prev->next= entry->next;
+      entry->next->prev= entry->prev;
+    }
+
+    free(item->key);
+    free(item->data);
+    free(item);
+    ret= true;
+  }
+
+  return ret;
+}
+
+void flush(uint32_t /* when */)
+{
+  /* remove the complete linked list */
+  if (root == NULL)
+  {
+    return;
+  }
+
+  root->prev->next= NULL;
+  while (root != NULL)
+  {
+    struct item* tmp= (struct item*)root;
+    root= root->next;
+
+    free(tmp->key);
+    free(tmp->data);
+    free(tmp);
+  }
+}
+
+void update_cas(struct item* item)
+{
+  item->cas= ++cas;
+}
+
+void release_item(struct item* /* item */)
+{
+}
diff --git a/example/t/include.am b/example/t/include.am
new file mode 100644 (file)
index 0000000..3cb5b80
--- /dev/null
@@ -0,0 +1,23 @@
+# vim:ft=automake
+# Copyright (C) 2012 Data Differential
+# All rights reserved.
+#
+# Use and distribution licensed under the BSD license.  See
+# the COPYING file in the parent directory for full text.
+#
+# included from Top Level Makefile.am
+# All paths should be given relative to the root
+
+MEMCACHED_LIGHT_TESTS_LDADDS= \
+                              libmemcached/libmemcached.la \
+                              libmemcached/libmemcachedutil.la \
+                              libtest/libtest.la
+example_t_memcached_light_SOURCES= example/t/memcached_light.cc
+example_t_memcached_light_CXXFLAGS = $(AM_CXXFLAGS)
+example_t_memcached_light_DEPENDENCIES= $(MEMCACHED_LIGHT_TESTS_LDADDS) example/memcached_light
+example_t_memcached_light_LDADD= $(MEMCACHED_LIGHT_TESTS_LDADDS)
+check_PROGRAMS+= example/t/memcached_light
+noinst_PROGRAMS+= example/t/memcached_light
+
+test-memcached_light: example/t/memcached_light
+       @example/t/memcached_light
diff --git a/example/t/memcached_light.cc b/example/t/memcached_light.cc
new file mode 100644 (file)
index 0000000..44f2770
--- /dev/null
@@ -0,0 +1,176 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  Test memcat
+ *
+ *  Copyright (C) 2011 Data Differential, http://datadifferential.com/
+ *
+ *  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.
+ *
+ */
+
+
+/*
+  Test that we are cycling the servers we are creating during testing.
+*/
+
+#include <config.h>
+
+#include <libtest/test.hpp>
+#include <libmemcached/memcached.h>
+
+using namespace libtest;
+
+#ifndef __INTEL_COMPILER
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif
+
+static std::string executable("example/memcached_light");
+
+static test_return_t help_TEST(void *)
+{
+  const char *args[]= { "--help", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t verbose_TEST(void *)
+{
+  const char *args[]= { "--help", "--verbose", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t daemon_TEST(void *)
+{
+  const char *args[]= { "--help", "--daemon", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t protocol_TEST(void *)
+{
+  const char *args[]= { "--help", "--protocol", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t version_TEST(void *)
+{
+  const char *args[]= { "--help", "--version", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t port_TEST(void *)
+{
+  const char *args[]= { "--help", "--port=9090", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t pid_file_TEST(void *)
+{
+  const char *args[]= { "--help", "--pid-file=/tmp/foo.pid", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t log_file_TEST(void *)
+{
+  const char *args[]= { "--help", "--log-file=/tmp/foo.log", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t max_connections_file_TEST(void *)
+{
+  const char *args[]= { "--help", "--max-connections=/tmp/foo.max_connections", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
+test_st cmdline_option_TESTS[] ={
+  {"--help", true, help_TEST },
+  {"--verbose", true, verbose_TEST },
+  {"--daemon", true, daemon_TEST },
+  {"--protocol", true, protocol_TEST },
+  {"--version", true, version_TEST },
+  {"--port", true, port_TEST },
+  {"--pid-file", true, pid_file_TEST },
+  {"--log-file", true, log_file_TEST },
+  {"--max-connections", true, max_connections_file_TEST },
+  {0, 0, 0}
+};
+
+collection_st collection[] ={
+  {"command line options", 0, 0, cmdline_option_TESTS },
+  {0, 0, 0, 0}
+};
+
+static void *world_create(server_startup_st& servers, test_return_t& error)
+{
+  if (HAVE_MEMCACHED_LIGHT_BINARY == 0)
+  {
+    error= TEST_SKIPPED;
+    return NULL;
+  }
+
+  if (server_startup(servers, "memcached-light", libtest::default_port(), 0, NULL) == 0)
+  {
+    error= TEST_FAILURE;
+  }
+
+  return &servers;
+}
+
+
+void get_world(Framework *world)
+{
+  world->collections= collection;
+  world->_create= world_create;
+}
+
index 0d18857a296f6db5099da79385d5fc63876c26af..fb589931eff09c5071f1313b08e288448b92ef02 100644 (file)
@@ -50,6 +50,7 @@
 #include "win32/wrappers.h"
 #define get_socket_errno() WSAGetLastError()
 #else
+#include <unistd.h>
 #define INVALID_SOCKET -1
 #define SOCKET_ERROR -1
 #define closesocket(a) close(a)
index 90204113d6d2f170653a01b1814bddb407fecf62..638536c8e0bf2543a15ef8015b447d3d9d5e2545 100644 (file)
@@ -168,14 +168,14 @@ typedef struct {
     * Delete an existing key
     *
     * @param cookie id of the client receiving the command
-    * @param key the key to delete
+    * @param key the key to delete_object
     * @param len the length of the key
     * @param cas the CAS in the request
     */
-   protocol_binary_response_status (*delete)(const void *cookie,
-                                             const void *key,
-                                             uint16_t keylen,
-                                             uint64_t cas);
+   protocol_binary_response_status (*delete_object)(const void *cookie,
+                                                    const void *key,
+                                                    uint16_t keylen,
+                                                    uint64_t cas);
 
 
    /**
@@ -184,8 +184,8 @@ typedef struct {
     * @param cookie id of the client receiving the command
     * @param when when the cache should be flushed (0 == immediately)
     */
-   protocol_binary_response_status (*flush)(const void *cookie,
-                                            uint32_t when);
+   protocol_binary_response_status (*flush_object)(const void *cookie,
+                                                   uint32_t when);
 
 
 
@@ -349,7 +349,7 @@ typedef enum {
     * Version 1 abstracts more of the protocol details, and let you work at
     * a logical level
     */
-   MEMCACHED_PROTOCOL_HANDLER_V1= 1,
+   MEMCACHED_PROTOCOL_HANDLER_V1= 1
 } memcached_protocol_interface_version_t;
 
 /**
index f1cfdc03e88a9660ca751eff94612cbb6e45dbdb..113338dfb675a987a039d4211d27eaf421d94e63 100644 (file)
@@ -152,6 +152,9 @@ memcached_protocol_client_st *memcached_protocol_create_client(memcached_protoco
 LIBMEMCACHED_API
 void memcached_protocol_client_destroy(memcached_protocol_client_st *client);
 
+LIBMEMCACHED_API
+void memcached_protocol_client_set_verbose(struct memcached_protocol_client_st *client, bool arg);
+
 /**
  * Error event means that the client encountered an error with the
  * connection so you should shut it down
index f6134b3c31af29851e219cc60f1dcd664beabbf1..8cd29720bae5c7c681d95eb974793a12176ec6fc 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <errno.h>
+
+
+static void print_ascii_command(memcached_protocol_client_st *client)
+{
+  if (client->is_verbose)
+  {
+    switch (client->ascii_command)
+    {
+    case SET_CMD:
+      fprintf(stderr, "%s:%d SET_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case ADD_CMD:
+      fprintf(stderr, "%s:%d ADD_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case REPLACE_CMD:
+      fprintf(stderr, "%s:%d REPLACE_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case CAS_CMD:
+      fprintf(stderr, "%s:%d CAS_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case APPEND_CMD:
+      fprintf(stderr, "%s:%d APPEND_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case PREPEND_CMD:
+      fprintf(stderr, "%s:%d PREPEND_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case DELETE_CMD:
+      fprintf(stderr, "%s:%d DELETE_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case INCR_CMD: /* FALLTHROUGH */
+      fprintf(stderr, "%s:%d INCR_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case DECR_CMD:
+      fprintf(stderr, "%s:%d DECR_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case STATS_CMD:
+      fprintf(stderr, "%s:%d STATS_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case FLUSH_ALL_CMD:
+      fprintf(stderr, "%s:%d FLUSH_ALL_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case VERSION_CMD:
+      fprintf(stderr, "%s:%d VERSION_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case QUIT_CMD:
+      fprintf(stderr, "%s:%d QUIT_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case VERBOSITY_CMD:
+      fprintf(stderr, "%s:%d VERBOSITY_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case GET_CMD:
+      fprintf(stderr, "%s:%d GET_CMD\n", __FILE__, __LINE__);
+      break;
+
+    case GETS_CMD:
+      fprintf(stderr, "%s:%d GETS_CMD\n", __FILE__, __LINE__);
+      break;
+
+    default:
+    case UNKNOWN_CMD:
+      fprintf(stderr, "%s:%d UNKNOWN_CMD\n", __FILE__, __LINE__);
+      break;
+
+    }
+  }
+}
 
 /**
  * Try to parse a key from the string.
@@ -81,9 +162,50 @@ static uint16_t parse_ascii_key(char **start)
  * @param text the text to spool
  * @return status of the spool operation
  */
-static protocol_binary_response_status
-spool_string(memcached_protocol_client_st *client, const char *text)
+static protocol_binary_response_status raw_response_handler(memcached_protocol_client_st *client, const char *text)
 {
+  if (client->is_verbose)
+  {
+    fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, text);
+  }
+
+  if (client->root->drain(client) == false)
+  {
+    return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
+  }
+
+  assert(client->output != NULL);
+#if 0
+  if (client->output == NULL)
+  {
+    /* I can write directly to the socket.... */
+    do
+    {
+      size_t num_bytes= len -offset;
+      ssize_t nw= client->root->send(client,
+                                     client->sock,
+                                     ptr + offset,
+                                     num_bytes);
+      if (nw == -1)
+      {
+        if (get_socket_errno() == EWOULDBLOCK)
+        {
+          break;
+        }
+        else if (get_socket_errno() != EINTR)
+        {
+          client->error= errno;
+          return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
+        }
+      }
+      else
+      {
+        offset += (size_t)nw;
+      }
+    } while (offset < len);
+  }
+#endif
+
   return client->root->spool(client, text, strlen(text));
 }
 
@@ -103,7 +225,7 @@ static void send_command_usage(memcached_protocol_client_st *client)
     [CAS_CMD]= "CLIENT_ERROR: Syntax error: cas <key> <flags> <exptime> <bytes> <casid> [noreply]\r\n",
     [APPEND_CMD]= "CLIENT_ERROR: Syntax error: append <key> <flags> <exptime> <bytes> [noreply]\r\n",
     [PREPEND_CMD]= "CLIENT_ERROR: Syntax error: prepend <key> <flags> <exptime> <bytes> [noreply]\r\n",
-    [DELETE_CMD]= "CLIENT_ERROR: Syntax error: delete <key> [noreply]\r\n",
+    [DELETE_CMD]= "CLIENT_ERROR: Syntax error: delete_object <key> [noreply]\r\n",
     [INCR_CMD]= "CLIENT_ERROR: Syntax error: incr <key> <value> [noreply]\r\n",
     [DECR_CMD]= "CLIENT_ERROR: Syntax error: decr <key> <value> [noreply]\r\n",
     [STATS_CMD]= "CLIENT_ERROR: Syntax error: stats [key]\r\n",
@@ -116,7 +238,7 @@ static void send_command_usage(memcached_protocol_client_st *client)
   };
 
   client->mute = false;
-  spool_string(client, errmsg[client->ascii_command]);
+  raw_response_handler(client, errmsg[client->ascii_command]);
 }
 
 /**
@@ -125,15 +247,14 @@ static void send_command_usage(memcached_protocol_client_st *client)
  * @param text the length of the body
  * @param textlen the length of the body
  */
-static protocol_binary_response_status
-ascii_version_response_handler(const void *cookie,
-                               const void *text,
-                               uint32_t textlen)
+static protocol_binary_response_status ascii_version_response_handler(const void *cookie,
+                                                                      const void *text,
+                                                                      uint32_t textlen)
 {
   memcached_protocol_client_st *client= (memcached_protocol_client_st*)cookie;
-  spool_string(client, "VERSION ");
+  raw_response_handler(client, "VERSION ");
   client->root->spool(client, text, textlen);
-  spool_string(client, "\r\n");
+  raw_response_handler(client, "\r\n");
   return PROTOCOL_BINARY_RESPONSE_SUCCESS;
 }
 
@@ -204,27 +325,26 @@ ascii_get_response_handler(const void *cookie,
  * @param body the length of the body
  * @param bodylen the length of the body
  */
-static protocol_binary_response_status
-ascii_stat_response_handler(const void *cookie,
-                     const void *key,
-                     uint16_t keylen,
-                     const void *body,
-                     uint32_t bodylen)
+static protocol_binary_response_status ascii_stat_response_handler(const void *cookie,
+                                                                   const void *key,
+                                                                   uint16_t keylen,
+                                                                   const void *body,
+                                                                   uint32_t bodylen)
 {
 
   memcached_protocol_client_st *client= (void*)cookie;
 
   if (key != NULL)
   {
-    spool_string(client, "STAT ");
+    raw_response_handler(client, "STAT ");
     client->root->spool(client, key, keylen);
-    spool_string(client, " ");
+    raw_response_handler(client, " ");
     client->root->spool(client, body, bodylen);
-    spool_string(client, "\r\n");
+    raw_response_handler(client, "\r\n");
   }
   else
   {
-    spool_string(client, "END\r\n");
+    raw_response_handler(client, "END\r\n");
   }
 
   return PROTOCOL_BINARY_RESPONSE_SUCCESS;
@@ -264,7 +384,9 @@ static void ascii_process_gets(memcached_protocol_client_st *client,
     send_command_usage(client);
   }
   else
+  {
     client->root->spool(client, "END\r\n", 5);
+  }
 }
 
 /**
@@ -353,7 +475,7 @@ static enum ascii_cmd ascii_to_cmd(char *start, size_t length)
     { .cmd= "cas", .len= 3, .cc= CAS_CMD },
     { .cmd= "append", .len= 6, .cc= APPEND_CMD },
     { .cmd= "prepend", .len= 7, .cc= PREPEND_CMD },
-    { .cmd= "delete", .len= 6, .cc= DELETE_CMD },
+    { .cmd= "delete_object", .len= 6, .cc= DELETE_CMD },
     { .cmd= "incr", .len= 4, .cc= INCR_CMD },
     { .cmd= "decr", .len= 4, .cc= DECR_CMD },
     { .cmd= "stats", .len= 5, .cc= STATS_CMD },
@@ -383,7 +505,7 @@ static enum ascii_cmd ascii_to_cmd(char *start, size_t length)
 }
 
 /**
- * Perform a delete operation.
+ * Perform a delete_object operation.
  *
  * @param client client requesting the deletion
  * @param tokens the command as a vector
@@ -401,28 +523,27 @@ static void process_delete(memcached_protocol_client_st *client,
     return;
   }
 
-  if (client->root->callback->interface.v1.delete == NULL)
+  if (client->root->callback->interface.v1.delete_object == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return;
   }
 
-  protocol_binary_response_status rval;
-  rval= client->root->callback->interface.v1.delete(client, key, nkey, 0);
+  protocol_binary_response_status rval= client->root->callback->interface.v1.delete_object(client, key, nkey, 0);
 
   if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
   {
-    spool_string(client, "DELETED\r\n");
+    raw_response_handler(client, "DELETED\r\n");
   }
   else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)
   {
-    spool_string(client, "NOT_FOUND\r\n");
+    raw_response_handler(client, "NOT_FOUND\r\n");
   }
   else
   {
     char msg[80];
-    snprintf(msg, sizeof(msg), "SERVER_ERROR: delete failed %u\r\n",(uint32_t)rval);
-    spool_string(client, msg);
+    snprintf(msg, sizeof(msg), "SERVER_ERROR: delete_object failed %u\r\n",(uint32_t)rval);
+    raw_response_handler(client, msg);
   }
 }
 
@@ -447,7 +568,7 @@ static void process_arithmetic(memcached_protocol_client_st *client,
   {
     if (client->root->callback->interface.v1.increment == NULL)
     {
-      spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+      raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
       return;
     }
     rval= client->root->callback->interface.v1.increment(client,
@@ -461,7 +582,7 @@ static void process_arithmetic(memcached_protocol_client_st *client,
   {
     if (client->root->callback->interface.v1.decrement == NULL)
     {
-      spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+      raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
       return;
     }
     rval= client->root->callback->interface.v1.decrement(client,
@@ -476,11 +597,11 @@ static void process_arithmetic(memcached_protocol_client_st *client,
   {
     char buffer[80];
     snprintf(buffer, sizeof(buffer), "%"PRIu64"\r\n", result);
-    spool_string(client, buffer);
+    raw_response_handler(client, buffer);
   }
   else
   {
-    spool_string(client, "NOT_FOUND\r\n");
+    raw_response_handler(client, "NOT_FOUND\r\n");
   }
 }
 
@@ -494,12 +615,14 @@ static void process_stats(memcached_protocol_client_st *client,
 {
   if (client->root->callback->interface.v1.stat == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return;
   }
 
   while (isspace(*key))
+  {
     key++;
+  }
 
   uint16_t nkey= (uint16_t)(end - key);
   (void)client->root->callback->interface.v1.stat(client, key, nkey,
@@ -518,7 +641,7 @@ static void process_version(memcached_protocol_client_st *client,
 
   if (client->root->callback->interface.v1.version == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return;
   }
 
@@ -535,9 +658,9 @@ static void process_flush(memcached_protocol_client_st *client,
     return;
   }
 
-  if (client->root->callback->interface.v1.flush == NULL)
+  if (client->root->callback->interface.v1.flush_object == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return;
   }
 
@@ -548,11 +671,11 @@ static void process_flush(memcached_protocol_client_st *client,
   }
 
   protocol_binary_response_status rval;
-  rval= client->root->callback->interface.v1.flush(client, timeout);
+  rval= client->root->callback->interface.v1.flush_object(client, timeout);
   if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
-    spool_string(client, "OK\r\n");
+    raw_response_handler(client, "OK\r\n");
   else
-    spool_string(client, "SERVER_ERROR: internal error\r\n");
+    raw_response_handler(client, "SERVER_ERROR: internal error\r\n");
 }
 
 /**
@@ -570,8 +693,8 @@ static void process_flush(memcached_protocol_client_st *client,
  *         1 We need more data, so just go ahead and wait for more!
  */
 static inline int process_storage_command(memcached_protocol_client_st *client,
-                                     char **tokens, int ntokens, char *start,
-                                     char **end, ssize_t length)
+                                          char **tokens, int ntokens, char *start,
+                                          char **end, ssize_t length)
 {
   (void)ntokens; /* already checked */
   char *key= tokens[1];
@@ -579,7 +702,7 @@ static inline int process_storage_command(memcached_protocol_client_st *client,
   if (nkey == 0)
   {
     /* return error */
-    spool_string(client, "CLIENT_ERROR: bad key\r\n");
+    raw_response_handler(client, "CLIENT_ERROR: bad key\r\n");
     return -1;
   }
 
@@ -666,7 +789,7 @@ static inline int process_storage_command(memcached_protocol_client_st *client,
 
   if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS)
   {
-    spool_string(client, "STORED\r\n");
+    raw_response_handler(client, "STORED\r\n");
   }
   else
   {
@@ -674,20 +797,20 @@ static inline int process_storage_command(memcached_protocol_client_st *client,
     {
       if (rval == PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS)
       {
-        spool_string(client, "EXISTS\r\n");
+        raw_response_handler(client, "EXISTS\r\n");
       }
       else if (rval == PROTOCOL_BINARY_RESPONSE_KEY_ENOENT)
       {
-        spool_string(client, "NOT_FOUND\r\n");
+        raw_response_handler(client, "NOT_FOUND\r\n");
       }
       else
       {
-        spool_string(client, "NOT_STORED\r\n");
+        raw_response_handler(client, "NOT_STORED\r\n");
       }
     }
     else
     {
-      spool_string(client, "NOT_STORED\r\n");
+      raw_response_handler(client, "NOT_STORED\r\n");
     }
   }
 
@@ -708,7 +831,7 @@ static int process_cas_command(memcached_protocol_client_st *client,
 
   if (client->root->callback->interface.v1.replace == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return false;
   }
 
@@ -727,7 +850,7 @@ static int process_set_command(memcached_protocol_client_st *client,
 
   if (client->root->callback->interface.v1.set == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return false;
   }
 
@@ -746,7 +869,7 @@ static int process_add_command(memcached_protocol_client_st *client,
 
   if (client->root->callback->interface.v1.add == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return false;
   }
 
@@ -765,7 +888,7 @@ static int process_replace_command(memcached_protocol_client_st *client,
 
   if (client->root->callback->interface.v1.replace == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return false;
   }
 
@@ -784,7 +907,7 @@ static int process_append_command(memcached_protocol_client_st *client,
 
   if (client->root->callback->interface.v1.append == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return false;
   }
 
@@ -803,7 +926,7 @@ static int process_prepend_command(memcached_protocol_client_st *client,
 
   if (client->root->callback->interface.v1.prepend == NULL)
   {
-    spool_string(client, "SERVER_ERROR: callback not implemented\r\n");
+    raw_response_handler(client, "SERVER_ERROR: callback not implemented\r\n");
     return false;
   }
 
@@ -831,20 +954,34 @@ memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_proto
 
     client->ascii_command= ascii_to_cmd(ptr, (size_t)(*length));
 
+    /* we got all data available, execute the callback! */
+    if (client->root->callback->pre_execute != NULL)
+    {
+      client->root->callback->pre_execute(client, NULL);
+    }
+
+
     /* A multiget lists all of the keys, and I don't want to have an
      * avector of let's say 512 pointers to tokenize all of them, so let's
      * just handle them immediately
      */
     if (client->ascii_command == GET_CMD ||
-        client->ascii_command == GETS_CMD) {
+        client->ascii_command == GETS_CMD)
+    {
       if (client->root->callback->interface.v1.get != NULL)
+      {
         ascii_process_gets(client, ptr, end);
+      }
       else
-        spool_string(client, "SERVER_ERROR: Command not implemented\n");
-    } else {
+      {
+        raw_response_handler(client, "SERVER_ERROR: Command not implemented\n");
+      }
+    }
+    else
+    {
       /* None of the defined commands takes 10 parameters, so lets just use
        * that as a maximum limit.
-       */
+     */
       char *tokens[10];
       int ntokens= ascii_tokenize_command(ptr, end, tokens, 10);
 
@@ -852,33 +989,40 @@ memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_proto
       {
         client->mute= strcmp(tokens[ntokens - 1], "noreply") == 0;
         if (client->mute)
+        {
           --ntokens; /* processed noreply token*/
+        }
       }
 
       int error= 0;
 
-      switch (client->ascii_command) {
+      print_ascii_command(client);
+      switch (client->ascii_command)
+      {
       case SET_CMD:
         error= process_set_command(client, tokens, ntokens, ptr, &end, *length);
         break;
+
       case ADD_CMD:
         error= process_add_command(client, tokens, ntokens, ptr, &end, *length);
         break;
+
       case REPLACE_CMD:
-        error= process_replace_command(client, tokens, ntokens,
-                                       ptr, &end, *length);
+        error= process_replace_command(client, tokens, ntokens, ptr, &end, *length);
         break;
+
       case CAS_CMD:
         error= process_cas_command(client, tokens, ntokens, ptr, &end, *length);
         break;
+
       case APPEND_CMD:
-        error= process_append_command(client, tokens, ntokens,
-                                      ptr, &end, *length);
+        error= process_append_command(client, tokens, ntokens, ptr, &end, *length);
         break;
+
       case PREPEND_CMD:
-        error= process_prepend_command(client, tokens, ntokens,
-                                       ptr, &end, *length);
-       break;
+        error= process_prepend_command(client, tokens, ntokens, ptr, &end, *length);
+        break;
+
       case DELETE_CMD:
         process_delete(client, tokens, ntokens);
         break;
@@ -887,6 +1031,7 @@ memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_proto
       case DECR_CMD:
         process_arithmetic(client, tokens, ntokens);
         break;
+
       case STATS_CMD:
         if (client->mute)
         {
@@ -898,9 +1043,11 @@ memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_proto
           process_stats(client, ptr + 6, end);
         }
         break;
+
       case FLUSH_ALL_CMD:
         process_flush(client, tokens, ntokens);
         break;
+
       case VERSION_CMD:
         if (client->mute)
         {
@@ -911,6 +1058,7 @@ memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_proto
           process_version(client, tokens, ntokens);
         }
         break;
+
       case QUIT_CMD:
         if (ntokens != 1 || client->mute)
         {
@@ -919,7 +1067,9 @@ memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_proto
         else
         {
           if (client->root->callback->interface.v1.quit != NULL)
+          {
             client->root->callback->interface.v1.quit(client);
+          }
 
           return MEMCACHED_PROTOCOL_ERROR_EVENT;
         }
@@ -927,9 +1077,13 @@ memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_proto
 
       case VERBOSITY_CMD:
         if (ntokens != 2)
+        {
           send_command_usage(client);
+        }
         else
-          spool_string(client, "OK\r\n");
+        {
+          raw_response_handler(client, "OK\r\n");
+        }
         break;
 
       case UNKNOWN_CMD:
@@ -944,9 +1098,18 @@ memcached_protocol_event_t memcached_ascii_protocol_process_data(memcached_proto
       }
 
       if (error == -1)
+      {
         return MEMCACHED_PROTOCOL_ERROR_EVENT;
+      }
       else if (error == 1)
+      {
         return MEMCACHED_PROTOCOL_READ_EVENT;
+      }
+    }
+
+    if (client->root->callback->post_execute != NULL)
+    {
+      client->root->callback->post_execute(client, NULL);
     }
 
     /* Move past \n */
index c2fd4414d8477df56c53b26098ded46bfba75da4..3778e344642390a12131af5cc23fa486ee566d6a 100644 (file)
  * @param response the packet to send
  * @return The status of the operation
  */
-static protocol_binary_response_status
-raw_response_handler(const void *cookie,
-                     protocol_binary_request_header *request,
-                     protocol_binary_response_header *response)
+static protocol_binary_response_status raw_response_handler(const void *cookie,
+                                                            protocol_binary_request_header *request,
+                                                            protocol_binary_response_header *response)
 {
   memcached_protocol_client_st *client= (void*)cookie;
 
@@ -72,12 +71,12 @@ raw_response_handler(const void *cookie,
     return PROTOCOL_BINARY_RESPONSE_EINVAL;
   }
 
-  if (!client->root->drain(client))
+  if (client->root->drain(client) == false)
   {
     return PROTOCOL_BINARY_RESPONSE_EINTERNAL;
   }
 
-  size_t len= sizeof(*response) + htonl(response->response.bodylen);
+  size_t len= sizeof(protocol_binary_response_header) + htonl(response->response.bodylen);
   size_t offset= 0;
   char *ptr= (void*)response;
 
@@ -113,6 +112,75 @@ raw_response_handler(const void *cookie,
   return client->root->spool(client, ptr, len - offset);
 }
 
+static void print_cmd(protocol_binary_command cmd)
+{
+  switch (cmd)
+  {
+  case PROTOCOL_BINARY_CMD_GET: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_GET\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_SET: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_SET\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_ADD: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_ADD\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_REPLACE: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_REPLACE\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_DELETE: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_DELETE\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_INCREMENT: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_INCREMENT\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_DECREMENT: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_DECREMENT\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_QUIT: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_QUIT\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_FLUSH: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_FLUSH\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_GETQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_GETQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_NOOP: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_NOOP\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_VERSION: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_VERSION\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_GETK: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_GETK\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_GETKQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_GETKQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_APPEND: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_APPEND\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_PREPEND: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_PREPEND\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_STAT: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_STAT\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_SETQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_SETQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_ADDQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_ADDQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_REPLACEQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_REPLACEQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_DELETEQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_DELETEQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_INCREMENTQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_INCREMENTQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_DECREMENTQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_DECREMENTQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_QUITQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_QUITQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_FLUSHQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_FLUSHQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_APPENDQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_APPENDQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_PREPENDQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_PREPENDQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_VERBOSITY: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_VERBOSITY\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_TOUCH: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_TOUCH\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_GAT: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_GAT\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_GATQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_GATQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_SASL_LIST_MECHS\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_SASL_AUTH: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_SASL_AUTH\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_SASL_STEP: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_SASL_STEP\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RGET: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RGET\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RSET: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RSET\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RSETQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RSETQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RAPPEND: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RAPPEND\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RAPPENDQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RAPPENDQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RPREPEND: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RPREPEND\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RPREPENDQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RPREPENDQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RDELETE: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RDELETE\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RDELETEQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RDELETEQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RINCR: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RINCR\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RINCRQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RINCRQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RDECR: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RDECR\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_RDECRQ: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_RDECRQ\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_SET_VBUCKET: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_SET_VBUCKET\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_GET_VBUCKET: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_GET_VBUCKET\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_DEL_VBUCKET: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_DEL_VBUCKET\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_TAP_CONNECT: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_TAP_CONNECT\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_TAP_MUTATION: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_TAP_MUTATION\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_TAP_DELETE: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_TAP_DELETE\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_TAP_FLUSH: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_TAP_FLUSH\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_TAP_OPAQUE: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_TAP_OPAQUE\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_TAP_VBUCKET_SET: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_TAP_VBUCKET_SET\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_START: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_START\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_END: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_TAP_CHECKPOINT_END\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_LAST_RESERVED: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_LAST_RESERVED\n", __FILE__, __LINE__); return;
+  case PROTOCOL_BINARY_CMD_SCRUB: fprintf(stderr, "%s:%d PROTOCOL_BINARY_CMD_SCRUB\n", __FILE__, __LINE__); return;
+  default:
+                                  abort();
+  }
+}
+
 /*
  * Version 0 of the interface is really low level and protocol specific,
  * while the version 1 of the interface is more API focused. We need a
@@ -131,15 +199,14 @@ raw_response_handler(const void *cookie,
  * @param flags the flags for the item
  * @param cas the CAS id for the item
  */
-static protocol_binary_response_status
-get_response_handler(const void *cookie,
-                     const void *key,
-                     uint16_t keylen,
-                     const void *body,
-                     uint32_t bodylen,
-                     uint32_t flags,
-                     uint64_t cas) {
-
+static protocol_binary_response_status get_response_handler(const void *cookie,
+                                                            const void *key,
+                                                            uint16_t keylen,
+                                                            const void *body,
+                                                            uint32_t bodylen,
+                                                            uint32_t flags,
+                                                            uint64_t cas)
+{
   memcached_protocol_client_st *client= (void*)cookie;
   uint8_t opcode= client->current_command->request.opcode;
 
@@ -222,10 +289,10 @@ static protocol_binary_response_status stat_response_handler(const void *cookie,
  * @param text the length of the body
  * @param textlen the length of the body
  */
-static protocol_binary_response_status
-version_response_handler(const void *cookie,
-                         const void *text,
-                         uint32_t textlen) {
+static protocol_binary_response_status version_response_handler(const void *cookie,
+                                                                const void *text,
+                                                                uint32_t textlen)
+{
 
   memcached_protocol_client_st *client= (void*)cookie;
 
@@ -372,21 +439,20 @@ decrement_command_handler(const void *cookie,
  * @param response_handler not used
  * @return the result of the operation
  */
-static protocol_binary_response_status
-delete_command_handler(const void *cookie,
-                       protocol_binary_request_header *header,
-                       memcached_binary_protocol_raw_response_handler response_handler)
+static protocol_binary_response_status delete_command_handler(const void *cookie,
+                                                              protocol_binary_request_header *header,
+                                                              memcached_binary_protocol_raw_response_handler response_handler)
 {
   (void)response_handler;
   protocol_binary_response_status rval;
 
   memcached_protocol_client_st *client= (void*)cookie;
-  if (client->root->callback->interface.v1.delete != NULL)
+  if (client->root->callback->interface.v1.delete_object != NULL)
   {
     uint16_t keylen= ntohs(header->request.keylen);
     void *key= (header +1);
     uint64_t cas= memcached_ntohll(header->request.cas);
-    rval= client->root->callback->interface.v1.delete(cookie, key, keylen, cas);
+    rval= client->root->callback->interface.v1.delete_object(cookie, key, keylen, cas);
     if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
         header->request.opcode == PROTOCOL_BINARY_CMD_DELETE)
     {
@@ -428,16 +494,16 @@ flush_command_handler(const void *cookie,
   protocol_binary_response_status rval;
 
   memcached_protocol_client_st *client= (void*)cookie;
-  if (client->root->callback->interface.v1.flush != NULL)
+  if (client->root->callback->interface.v1.flush_object != NULL)
   {
-    protocol_binary_request_flush *flush= (void*)header;
+    protocol_binary_request_flush *flush_object= (void*)header;
     uint32_t timeout= 0;
     if (htonl(header->request.bodylen) == 4)
     {
-      timeout= ntohl(flush->message.body.expiration);
+      timeout= ntohl(flush_object->message.body.expiration);
     }
 
-    rval= client->root->callback->interface.v1.flush(cookie, timeout);
+    rval= client->root->callback->interface.v1.flush_object(cookie, timeout);
     if (rval == PROTOCOL_BINARY_RESPONSE_SUCCESS &&
         header->request.opcode == PROTOCOL_BINARY_CMD_FLUSH)
     {
@@ -805,10 +871,9 @@ replace_command_handler(const void *cookie,
  * @param response_handler not used
  * @return the result of the operation
  */
-static protocol_binary_response_status
-set_command_handler(const void *cookie,
-                    protocol_binary_request_header *header,
-                    memcached_binary_protocol_raw_response_handler response_handler)
+static protocol_binary_response_status set_command_handler(const void *cookie,
+                                                           protocol_binary_request_header *header,
+                                                           memcached_binary_protocol_raw_response_handler response_handler)
 {
   (void)response_handler;
   protocol_binary_response_status rval;
@@ -975,22 +1040,30 @@ static protocol_binary_response_status execute_command(memcached_protocol_client
     client->root->callback->pre_execute(client, header);
   }
 
-  protocol_binary_response_status rval;
-  rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
+  protocol_binary_response_status rval= PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND;
   uint8_t cc= header->request.opcode;
 
+  if (client->is_verbose)
+  {
+    print_cmd(cc);
+  }
+
   switch (client->root->callback->interface_version)
   {
   case 0:
-    if (client->root->callback->interface.v0.comcode[cc] != NULL) {
+    if (client->root->callback->interface.v0.comcode[cc] != NULL)
+    {
       rval= client->root->callback->interface.v0.comcode[cc](client, header, raw_response_handler);
     }
     break;
+
   case 1:
-    if (comcode_v0_v1_remap[cc] != NULL) {
+    if (comcode_v0_v1_remap[cc] != NULL)
+    {
       rval= comcode_v0_v1_remap[cc](client, header, raw_response_handler);
     }
     break;
+
   default:
     /* Unknown interface.
      * It should be impossible to get here so I'll just call abort
@@ -1060,8 +1133,11 @@ memcached_protocol_event_t memcached_binary_protocol_process_data(memcached_prot
       *length= len;
       *endptr= (void*)header;
       return MEMCACHED_PROTOCOL_ERROR_EVENT;
-    } else if (rv == PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED)
+    }
+    else if (rv == PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED)
+    {
       return MEMCACHED_PROTOCOL_PAUSE_EVENT;
+    }
 
     ssize_t total= (ssize_t)(sizeof(*header) + ntohl(header->request.bodylen));
     len -= total;
index ea94a8d7e281ff71c0a23f2a304c8658f56a7232..8453ccd8619b417c3a2bfbfa4e57785c02a88575 100644 (file)
@@ -124,6 +124,7 @@ enum ascii_cmd {
 };
 
 struct memcached_protocol_client_st {
+  bool is_verbose;
   memcached_protocol_st *root;
   memcached_socket_t sock;
   int error;
index 9ce927a6433664d601882ed87cc4b5b82831b3fc..59b461f6aa083b286c8e6e619ab8382e352437f5 100644 (file)
@@ -89,7 +89,7 @@ static ssize_t default_send(const void *cookie,
                             size_t nbytes)
 {
   (void)cookie;
-  return send(fd, buf, nbytes, 0);
+  return send(fd, buf, nbytes, MSG_NOSIGNAL);
 }
 
 /**
@@ -102,12 +102,17 @@ static ssize_t default_send(const void *cookie,
  */
 static bool drain_output(struct memcached_protocol_client_st *client)
 {
-  ssize_t len;
+  if (client->is_verbose)
+  {
+    fprintf(stderr, "%s:%d %s mute:%d output:%s length:%d\n", __FILE__, __LINE__, __func__, (int)client->mute,
+            client->output ? "yes" : "no",
+            client->output ? (int)(client->output->nbytes - client->output->offset) : 0);
+  }
 
   /* Do we have pending data to send? */
   while (client->output != NULL)
   {
-    len= client->root->send(client,
+    ssize_t len= client->root->send(client,
                             client->sock,
                             client->output->data + client->output->offset,
                             client->output->nbytes - client->output->offset);
@@ -189,6 +194,11 @@ static protocol_binary_response_status spool_output(struct memcached_protocol_cl
                                                     const void *data,
                                                     size_t length)
 {
+  if (client->is_verbose)
+  {
+    fprintf(stderr, "%s:%d %s mute:%d length:%d\n", __FILE__, __LINE__, __func__, (int)client->mute, (int)length);
+  }
+
   if (client->mute)
   {
     return PROTOCOL_BINARY_RESPONSE_SUCCESS;
@@ -233,10 +243,19 @@ static memcached_protocol_event_t determine_protocol(struct memcached_protocol_c
 {
   if (*client->root->input_buffer == (uint8_t)PROTOCOL_BINARY_REQ)
   {
+    if (client->is_verbose)
+    {
+      fprintf(stderr, "%s:%d PROTOCOL: memcached_binary_protocol_process_data\n", __FILE__, __LINE__);
+    }
     client->work= memcached_binary_protocol_process_data;
   }
   else if (client->root->callback->interface_version == 1)
   {
+    if (client->is_verbose)
+    {
+      fprintf(stderr, "%s:%d PROTOCOL: memcached_ascii_protocol_process_data\n", __FILE__, __LINE__);
+    }
+
     /*
      * The ASCII protocol can only be used if the implementors provide
      * an implementation for the version 1 of the interface..
@@ -249,12 +268,18 @@ static memcached_protocol_event_t determine_protocol(struct memcached_protocol_c
   }
   else
   {
+    if (client->is_verbose)
+    {
+      fprintf(stderr, "%s:%d PROTOCOL: Unsupported protocol\n", __FILE__, __LINE__);
+    }
+
     /* Let's just output a warning the way it is supposed to look like
      * in the ASCII protocol...
      */
     const char *err= "CLIENT_ERROR: Unsupported protocol\r\n";
     client->root->spool(client, err, strlen(err));
     client->root->drain(client);
+
     return MEMCACHED_PROTOCOL_ERROR_EVENT; /* Unsupported protocol */
   }
 
@@ -282,6 +307,7 @@ struct memcached_protocol_st *memcached_protocol_create_instance(void)
     {
       free(ret);
       ret= NULL;
+
       return NULL;
     }
 
@@ -307,7 +333,7 @@ void memcached_protocol_destroy_instance(struct memcached_protocol_st *instance)
 
 struct memcached_protocol_client_st *memcached_protocol_create_client(struct memcached_protocol_st *instance, memcached_socket_t sock)
 {
-  struct memcached_protocol_client_st *ret= calloc(1, sizeof(*ret));
+  struct memcached_protocol_client_st *ret= calloc(1, sizeof(memcached_protocol_client_st));
   if (ret != NULL)
   {
     ret->root= instance;
@@ -323,6 +349,14 @@ void memcached_protocol_client_destroy(struct memcached_protocol_client_st *clie
   free(client);
 }
 
+void memcached_protocol_client_set_verbose(struct memcached_protocol_client_st *client, bool arg)
+{
+  if (client)
+  {
+    client->is_verbose= arg;
+  }
+}
+
 memcached_protocol_event_t memcached_protocol_client_work(struct memcached_protocol_client_st *client)
 {
   /* Try to send data and read from the socket */
@@ -395,7 +429,9 @@ memcached_protocol_event_t memcached_protocol_client_work(struct memcached_proto
 
   memcached_protocol_event_t ret= MEMCACHED_PROTOCOL_READ_EVENT;
   if (client->output)
+  {
     ret|= MEMCACHED_PROTOCOL_READ_EVENT;
+  }
 
   return ret;
 }
index 8bbd073857ce8d2bdb1430725e603f6221bdefa5..fe1cdce31baeaa6f75b72fd1b5bd1596dbf93cc0 100644 (file)
@@ -122,24 +122,19 @@ public:
     return "benchmark/blobslap_worker";
   }
 
-  const char *pid_file_option()
-  {
-    return "--pid-file=";
-  }
-
   const char *daemon_file_option()
   {
     return "--daemon";
   }
 
-  const char *log_file_option()
+  bool has_port_option() const
   {
-    return "--log-file=";
+    return true;
   }
 
-  const char *port_option()
+  bool has_log_file_option() const
   {
-    return "--port=";
+    return true;
   }
 
   bool is_libtool()
@@ -147,23 +142,21 @@ public:
     return true;
   }
 
-  bool build(int argc, const char *argv[]);
+  bool build(size_t argc, const char *argv[]);
 };
 
 
 #include <sstream>
 
-bool BlobslapWorker::build(int argc, const char *argv[])
+bool BlobslapWorker::build(size_t argc, const char *argv[])
 {
   std::stringstream arg_buffer;
 
-  for (int x= 1 ; x < argc ; x++)
+  for (size_t x= 0 ; x < argc ; x++)
   {
-    arg_buffer << " " << argv[x] << " ";
+    add_option(argv[x]);
   }
 
-  set_extra_args(arg_buffer.str());
-
   return true;
 }
 
index 7c2f5f442662748bfabd8e0a70fad4878e08f5bd..6807c338af6fe20e69efffdff9be152e05ae9b09 100644 (file)
@@ -68,6 +68,21 @@ namespace {
     return arg_buffer.str();
   }
 
+  static Application::error_t int_to_error_t(int arg)
+  {
+    switch (arg)
+    {
+    case 127:
+      return Application::INVALID;
+
+    case 0:
+      return Application::SUCCESS;
+
+    default:
+    case 1:
+      return Application::FAILURE;
+    }
+  }
 }
 
 namespace libtest {
@@ -214,16 +229,17 @@ Application::error_t Application::wait()
     }
     else
     {
-      assert(waited_pid == _pid);
-      exit_code= error_t(exited_successfully(status));
+      if (waited_pid != _pid)
+      {
+        throw libtest::fatal(LIBYATL_DEFAULT_PARAM, "Pid mismatch, %d != %d", int(waited_pid), int(_pid));
+      }
+      exit_code= int_to_error_t(exited_successfully(status));
     }
   }
 
   if (exit_code == Application::INVALID)
   {
-#if 0
     Error << print_argv(built_argv, _argc, _pid);
-#endif
   }
 
   return exit_code;
@@ -408,9 +424,8 @@ int exec_cmdline(const std::string& command, const char *args[], bool use_libtoo
   {
     return int(ret);
   }
-  ret= app.wait();
 
-  return int(ret);
+  return int(app.wait());
 }
 
 const char *gearmand_binary() 
index d1019d548a6b65a98d54bd495e929e8e274a3553..39ba35a2fb8784bf84ee0adb3fdcde88ab3a0a8f 100644 (file)
@@ -129,6 +129,9 @@ static inline std::ostream& operator<<(std::ostream& output, const enum Applicat
     case Application::INVALID:
       output << "127";
       break;
+
+    default:
+      output << "EXIT_UNKNOWN";
   }
 
   return output;
index 3fa5bfa21faab584b5df0eed8b4bfe7228e00480..7e34227620552cf291799b873dfc0195699948cd 100644 (file)
@@ -25,9 +25,7 @@
 enum test_return_t {
   TEST_SUCCESS,
   TEST_FAILURE,
-  TEST_MEMORY_ALLOCATION_FAILURE,
-  TEST_SKIPPED,
-  TEST_FATAL // Collection should not be continued
+  TEST_SKIPPED
 };
 
 
index b64b3606e8da253770e183ba6efce2d9566be054..23f47bd9d65b9decad8286ee03ad864160a0be98 100644 (file)
@@ -56,3 +56,4 @@ private:
 } // namespace libtest
 
 #define fatal_message(__mesg) libtest::fatal(LIBYATL_DEFAULT_PARAM, __mesg)
+#define fatal_assert(__assert) if((__assert)) {} else { libtest::fatal(LIBYATL_DEFAULT_PARAM, #__assert); }
index bcb7f8aaba2d7bcd7d033d4bf534b14b5ece03d2..4a544f10b9671eeb35b5263e2ee4b069c40e440b 100644 (file)
@@ -164,24 +164,25 @@ public:
     return GEARMAND_BINARY;
   }
 
-  const char *pid_file_option()
-  {
-    return "--pid-file=";
-  }
-
   const char *daemon_file_option()
   {
     return "--daemon";
   }
 
-  const char *log_file_option()
+  void log_file_option(Application& app, const std::string& arg)
   {
-    return "--verbose=DEBUG --log-file=";
+    if (arg.empty() == false)
+    {
+      std::string buffer("--log-file=");
+      buffer+= arg;
+      app.add_option("--verbose=DEBUG");
+      app.add_option(buffer);
+    }
   }
 
-  const char *port_option()
+  bool has_log_file_option() const
   {
-    return "--port=";
+    return true;
   }
 
   bool is_libtool()
@@ -194,27 +195,30 @@ public:
     return true;
   }
 
-  bool build(int argc, const char *argv[]);
+  bool has_port_option() const
+  {
+    return true;
+  }
+
+  bool build(size_t argc, const char *argv[]);
 };
 
-bool Gearmand::build(int argc, const char *argv[])
+bool Gearmand::build(size_t argc, const char *argv[])
 {
   std::stringstream arg_buffer;
 
   if (getuid() == 0 or geteuid() == 0)
   {
-    arg_buffer << " -u root ";
+    add_option("-u", "root");
   }
 
-  arg_buffer << " --listen=localhost ";
+  add_option("--listen=localhost");
 
-  for (int x= 1 ; x < argc ; x++)
+  for (size_t x= 0 ; x < argc ; x++)
   {
-    arg_buffer << " " << argv[x] << " ";
+    add_option(argv[x]);
   }
 
-  set_extra_args(arg_buffer.str());
-
   return true;
 }
 
index 7d25153fb078c783734bdaa314ce4862f1f51b70..6f81dd6c790b696974ab087ad9cb16f4d93ae526 100644 (file)
@@ -85,7 +85,7 @@ public:
   pid_t get_pid(bool error_is_ok)
   {
     // Memcached is slow to start, so we need to do this
-    if (not pid_file().empty())
+    if (pid_file().empty() == false)
     {
       if (error_is_ok and not wait_for_pidfile())
       {
@@ -160,9 +160,12 @@ public:
     return MEMCACHED_BINARY;
   }
 
-  const char *pid_file_option()
+  virtual void pid_file_option(Application& app, const std::string& arg)
   {
-    return "-P ";
+    if (arg.empty() == false)
+    {
+      app.add_option("-P", arg);
+    }
   }
 
   const char *socket_file_option() const
@@ -175,14 +178,29 @@ public:
     return "-d";
   }
 
-  const char *log_file_option()
+  virtual void port_option(Application& app, in_port_t arg)
   {
-    return NULL;
+    char buffer[30];
+    snprintf(buffer, sizeof(buffer), "%d", int(arg));
+    app.add_option("-p", buffer); 
   }
 
-  const char *port_option()
+  bool has_port_option() const
   {
-    return "-p ";
+    return true;
+  }
+
+  bool has_socket_file_option() const
+  {
+    return has_socket();
+  }
+
+  void socket_file_option(Application& app, const std::string& socket_arg)
+  {
+    if (socket_arg.empty() == false)
+    {
+      app.add_option("-s", socket_arg);
+    }
   }
 
   bool is_libtool()
@@ -201,7 +219,108 @@ public:
     return true;
   }
 
-  bool build(int argc, const char *argv[]);
+  bool build(size_t argc, const char *argv[]);
+};
+
+class MemcachedLight : public libtest::Server
+{
+
+public:
+  MemcachedLight(const std::string& host_arg, const in_port_t port_arg):
+    libtest::Server(host_arg, port_arg)
+  {
+    set_pid_file();
+  }
+
+  pid_t get_pid(bool error_is_ok)
+  {
+    // Memcached is slow to start, so we need to do this
+    if (not pid_file().empty())
+    {
+      if (error_is_ok and not wait_for_pidfile())
+      {
+        Error << "Pidfile was not found:" << pid_file();
+        return -1;
+      }
+    }
+
+    bool success= false;
+    std::stringstream error_message;
+    pid_t local_pid= get_pid_from_file(pid_file(), error_message);
+    if (local_pid > 0)
+    {
+      if (::kill(local_pid, 0) > 0)
+      {
+        success= true;
+      }
+    }
+
+    if (error_is_ok and ((success or not is_pid_valid(local_pid))))
+    {
+      Error << "kill(" << " pid: " << local_pid << " errno:" << strerror(errno) << " for:" << *this;
+    }
+
+    return local_pid;
+  }
+
+  bool ping()
+  {
+    // Memcached is slow to start, so we need to do this
+    if (not pid_file().empty())
+    {
+      if (not wait_for_pidfile())
+      {
+        Error << "Pidfile was not found:" << pid_file();
+        return false;
+      }
+    }
+
+    std::stringstream error_message;
+    pid_t local_pid= get_pid_from_file(pid_file(), error_message);
+    if (local_pid > 0)
+    {
+      if (::kill(local_pid, 0) == 0)
+      {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  const char *name()
+  {
+    return "memcached_light";
+  };
+
+  const char *executable()
+  {
+    return MEMCACHED_LIGHT_BINARY;
+  }
+
+  const char *daemon_file_option()
+  {
+    return "--daemon";
+  }
+
+  virtual void port_option(Application& app, in_port_t arg)
+  {
+    char buffer[1024];
+    snprintf(buffer, sizeof(buffer), "--port=%d", int(arg));
+    app.add_option(buffer);
+  }
+
+  bool has_port_option() const
+  {
+    return true;
+  }
+
+  bool is_libtool()
+  {
+    return true;
+  }
+
+  bool build(size_t argc, const char *argv[]);
 };
 
 class MemcachedSaSL : public Memcached
@@ -294,30 +413,38 @@ public:
 
 #include <sstream>
 
-bool Memcached::build(int argc, const char *argv[])
+bool Memcached::build(size_t argc, const char *argv[])
 {
   std::stringstream arg_buffer;
 
   if (getuid() == 0 or geteuid() == 0)
   {
-    arg_buffer << " -u root ";
+    add_option("-u", "root");
   }
 
-  arg_buffer << " -l localhost ";
-  arg_buffer << " -m 128 ";
-  arg_buffer << " -M ";
+  add_option("-l", "localhost");
+  add_option("-m", "128");
+  add_option("-M");
 
   if (sasl())
   {
-    arg_buffer << sasl();
+    add_option(sasl());
   }
 
-  for (int x= 1 ; x < argc ; x++)
+  for (int x= 0 ; x < argc ; x++)
   {
-    arg_buffer << " " << argv[x] << " ";
+    add_option(argv[x]);
   }
 
-  set_extra_args(arg_buffer.str());
+  return true;
+}
+
+bool MemcachedLight::build(size_t argc, const char *argv[])
+{
+  for (int x= 0 ; x < argc ; x++)
+  {
+    add_option(argv[x]);
+  }
 
   return true;
 }
@@ -334,6 +461,11 @@ libtest::Server *build_memcached_socket(const std::string& socket_file, const in
   return new Memcached(socket_file, try_port, true);
 }
 
+libtest::Server *build_memcached_light(const std::string& hostname, const in_port_t try_port)
+{
+  return new MemcachedLight(hostname, try_port);
+}
+
 
 libtest::Server *build_memcached_sasl(const std::string& hostname, const in_port_t try_port, const std::string& username, const std::string &password)
 {
index a08996140189c69648823700d6306faa5ad96125..4594d5b6abe44ca76defa5e9a21c9e85f185402c 100644 (file)
@@ -25,6 +25,8 @@ namespace libtest {
 
 libtest::Server *build_memcached(const std::string& hostname, const in_port_t try_port);
 
+libtest::Server *build_memcached_light(const std::string& socket_file, const in_port_t try_port);
+
 libtest::Server *build_memcached_socket(const std::string& socket_file, const in_port_t try_port);
 
 libtest::Server *build_memcached_sasl(const std::string& hostname, const in_port_t try_port, const std::string& username, const std::string& password);
index 9d492b9b84aa1d902f6e8a30a8cddee6fbf6062d..8ca6b53e60ee812082735ff2361f44f070ed469a 100644 (file)
@@ -42,42 +42,6 @@ static inline std::string &rtrim(std::string &s)
 #include <libtest/stream.h>
 #include <libtest/killpid.h>
 
-extern "C" {
-  static bool exited_successfully(int status, const std::string &command)
-  {
-    if (status == 0)
-    {
-      return true;
-    }
-
-    if (WIFEXITED(status) == true)
-    {
-      int ret= WEXITSTATUS(status);
-
-      if (ret == 0)
-      {
-        return true;
-      }
-      else if (ret == EXIT_FAILURE)
-      {
-        libtest::Error << "Command executed, but returned EXIT_FAILURE: " << command;
-      }
-      else
-      {
-        libtest::Error << "Command executed, but returned " << ret;
-      }
-    }
-    else if (WIFSIGNALED(status) == true)
-    {
-      int ret_signal= WTERMSIG(status);
-      libtest::Error << "Died from signal " << strsignal(ret_signal);
-    }
-
-    return false;
-  }
-}
-
-
 namespace libtest {
 
 std::ostream& operator<<(std::ostream& output, const Server &arg)
@@ -106,7 +70,6 @@ std::ostream& operator<<(std::ostream& output, const Server &arg)
     output << " Exec:" <<  arg.running();
   }
 
-
   return output;  // for multiple << operators
 }
 
@@ -153,21 +116,6 @@ bool Server::cycle()
   return true;
 }
 
-// Grab a one off command
-bool Server::command(std::string& command_arg)
-{
-  rebuild_base_command();
-
-  command_arg+= _base_command;
-
-  if (args(command_arg))
-  {
-    return true;
-  }
-
-  return false;
-}
-
 bool Server::wait_for_pidfile() const
 {
   Wait wait(pid_file(), 4);
@@ -183,25 +131,30 @@ bool Server::start()
     Error << "Could not kill() existing server during start() pid:" << _pid;
     return false;
   }
-  assert(not has_pid());
 
-  _running.clear();
-  if (command(_running) == false)
+  if (has_pid() == false)
+  {
+    fatal_message("has_pid() failed, programer error");
+  }
+
+  Application app(executable(), is_libtool());
+  if (args(app) == false)
   {
     Error << "Could not build command()";
     return false;
   }
 
-  if (is_valgrind() or is_helgrind())
+  Application::error_t ret;
+  if (Application::SUCCESS !=  (ret= app.run()))
   {
-    _running+= " &";
+    Error << "Application::run() " << ret;
+    return false;
   }
+  _running= app.print();
 
-  int ret= system(_running.c_str());
-  if (exited_successfully(ret, _running) == false)
+  if (Application::SUCCESS !=  (ret= app.wait()))
   {
-    Error << "system(" << _running << ") failed: " << strerror(errno);
-    _running.clear();
+    Error << "Application::wait() " << app.print() << " " << ret;
     return false;
   }
 
@@ -210,7 +163,7 @@ bool Server::start()
     dream(5, 50000);
   }
 
-  if (pid_file_option() and pid_file().empty() == false)
+  if (pid_file().empty() == false)
   {
     Wait wait(pid_file(), 8);
 
@@ -231,12 +184,18 @@ bool Server::start()
   if (pinged == false)
   {
     // If we happen to have a pid file, lets try to kill it
-    if (pid_file_option() and pid_file().empty() == false)
+    if (pid_file().empty() == false)
+    {
+      if (kill_file(pid_file()) == false)
+      {
+        fatal_message("Failed to kill off server after startup occurred, when pinging failed");
+      }
+      Error << "Failed to ping() server started, having pid_file. exec:" << _running;
+    }
+    else
     {
-      Error << "We are going to kill it off";
-      kill_file(pid_file());
+      Error << "Failed to ping() server started. exec:" << _running;
     }
-    Error << "Failed to ping() server started with:" << _running;
     _running.clear();
     return false;
   }
@@ -259,6 +218,16 @@ pid_t Server::pid()
   return _pid;
 }
 
+void Server::add_option(const std::string& arg)
+{
+  _options.push_back(std::make_pair(arg, std::string()));
+}
+
+void Server::add_option(const std::string& name, const std::string& value)
+{
+  _options.push_back(std::make_pair(name, value));
+}
+
 bool Server::set_socket_file()
 {
   char file_buffer[FILENAME_MAX];
@@ -334,106 +303,66 @@ bool Server::set_log_file()
   return true;
 }
 
-void Server::rebuild_base_command()
+bool Server::args(Application& app)
 {
-  _base_command.clear();
-  if (is_libtool())
-  {
-    _base_command+= libtool();
-    _base_command+= " --mode=execute ";
-  }
-
-  if (is_debug() and getenv("GDB_COMMAND"))
-  {
-    _base_command+= getenv("GDB_COMMAND");
-    _base_command+= " ";
-  }
-  else if (is_valgrind() and getenv("VALGRIND_COMMAND"))
-  {
-    _base_command+= getenv("VALGRIND_COMMAND");
-    _base_command+= " ";
-  }
-  else if (is_helgrind() and getenv("HELGRIND_COMMAND"))
-  {
-    _base_command+= getenv("HELGRIND_COMMAND");
-    _base_command+= " ";
-  }
-
-  if (is_libtool())
-  {
-    if (getenv("PWD"))
-    {
-      _base_command+= getenv("PWD");
-      _base_command+= "/";
-    }
-  }
-
-  _base_command+= executable();
-}
-
-void Server::set_extra_args(const std::string &arg)
-{
-  _extra_args= arg;
-}
-
-bool Server::args(std::string& options)
-{
-  std::stringstream arg_buffer;
 
   // Set a log file if it was requested (and we can)
-  if (getenv("LIBTEST_LOG") and log_file_option())
+  if (getenv("LIBTEST_LOG") and has_log_file_option())
   {
     if (not set_log_file())
     {
       return false;
     }
 
-    arg_buffer << " " << log_file_option() << _log_file;
+    log_file_option(app, _log_file);
   }
 
   if (getenv("LIBTEST_SYSLOG") and has_syslog())
   {
-    arg_buffer << " --syslog";
+    app.add_option("--syslog");
   }
 
   // Update pid_file
-  if (pid_file_option())
   {
     if (_pid_file.empty() and set_pid_file() == false)
     {
       return false;
     }
 
-    arg_buffer << " " << pid_file_option() << pid_file(); 
+    pid_file_option(app, pid_file());
   }
 
   assert(daemon_file_option());
   if (daemon_file_option() and not is_valgrind() and not is_helgrind())
   {
-    arg_buffer << " " << daemon_file_option();
+    app.add_option(daemon_file_option());
   }
 
-  if (_is_socket and socket_file_option())
+  if (has_socket_file_option())
   {
-    if (not set_socket_file())
+    if (set_socket_file() == false)
     {
       return false;
     }
 
-    arg_buffer << " " << socket_file_option() << "\"" <<  _socket << "\"";
+    socket_file_option(app, _socket);
   }
 
-  assert(port_option());
-  if (port_option() and _port > 0)
+  if (has_port_option())
   {
-    arg_buffer << " " << port_option() << _port;
+    port_option(app, _port);
   }
 
-  options+= arg_buffer.str();
-
-  if (not _extra_args.empty())
+  for (Options::const_iterator iter= _options.begin(); iter != _options.end(); iter++)
   {
-    options+= _extra_args;
+    if ((*iter).second.empty() == false)
+    {
+      app.add_option((*iter).first, (*iter).second);
+    }
+    else
+    {
+      app.add_option((*iter).first);
+    }
   }
 
   return true;
index b50b0b2e6fe516a125b679a614e2e14eba9af19c..b193fb8ffd21f617d79a178a0477f4e17bb090c0 100644 (file)
@@ -21,6 +21,8 @@
 
 #pragma once
 
+#include <libtest/cmdline.h>
+
 #include <cassert>
 #include <cstdio>
 #include <cstring>
@@ -33,6 +35,9 @@
 namespace libtest {
 
 struct Server {
+private:
+  typedef std::vector< std::pair<std::string, std::string> > Options;
+
 private:
   bool _is_socket;
   std::string _socket;
@@ -55,20 +60,67 @@ public:
 
   virtual const char *name()= 0;
   virtual const char *executable()= 0;
-  virtual const char *port_option()= 0;
-  virtual const char *pid_file_option()= 0;
   virtual const char *daemon_file_option()= 0;
-  virtual const char *log_file_option()= 0;
   virtual bool is_libtool()= 0;
 
-  virtual bool broken_socket_cleanup()
+  virtual bool has_socket_file_option() const
+  {
+    return false;
+  }
+
+  virtual void socket_file_option(Application& app, const std::string& socket_arg)
+  {
+    if (socket_arg.empty() == false)
+    {
+      std::string buffer("--socket=");
+      buffer+= socket_arg;
+      app.add_option(buffer);
+    }
+  }
+
+  bool has_log_file_option() const
+  {
+    return false;
+  }
+
+  virtual void log_file_option(Application& app, const std::string& arg)
+  {
+    if (arg.empty() == false)
+    {
+      std::string buffer("--log-file=");
+      buffer+= arg;
+      app.add_option(buffer);
+    }
+  }
+
+  virtual void pid_file_option(Application& app, const std::string& arg)
+  {
+    if (arg.empty() == false)
+    {
+      std::string buffer("--pid-file=");
+      buffer+= arg;
+      app.add_option(buffer);
+    }
+  }
+
+  virtual bool has_port_option() const
   {
     return false;
   }
 
-  virtual const char *socket_file_option() const
+  virtual void port_option(Application& app, in_port_t arg)
   {
-    return NULL;
+    if (arg > 0)
+    {
+      char buffer[1024];
+      snprintf(buffer, sizeof(buffer), "--port=%d", int(arg));
+      app.add_option(buffer);
+    }
+  }
+
+  virtual bool broken_socket_cleanup()
+  {
+    return false;
   }
 
   virtual bool broken_pid_file()
@@ -112,7 +164,10 @@ public:
 
   virtual pid_t get_pid(bool error_is_ok= false)= 0;
 
-  virtual bool build(int argc, const char *argv[])= 0;
+  virtual bool build(size_t argc, const char *argv[])= 0;
+
+  void add_option(const std::string&);
+  void add_option(const std::string&, const std::string&);
 
   in_port_t port() const
   {
@@ -137,9 +192,7 @@ public:
     _log_file.clear();
   }
 
-  void set_extra_args(const std::string &arg);
-
-  bool args(std::string& options);
+  bool args(Application&);
 
   pid_t pid();
 
@@ -174,10 +227,11 @@ public:
 
   bool kill(pid_t pid_arg);
   bool start();
-  bool command(std::string& command_arg);
+  bool command(libtest::Application& app);
 
 protected:
   bool set_pid_file();
+  Options _options;
 
 private:
   bool is_helgrind() const;
@@ -185,7 +239,6 @@ private:
   bool is_debug() const;
   bool set_log_file();
   bool set_socket_file();
-  void rebuild_base_command();
   void reset_pid();
 };
 
index 528fcd1fab180e288f45f9e356051e428f85f1c0..6d0c785e7de5f20d5d2d06d549ed7545e5b749ff 100644 (file)
@@ -144,7 +144,10 @@ bool server_startup_st::is_helgrind() const
 bool server_startup(server_startup_st& construct, const std::string& server_type, in_port_t try_port, int argc, const char *argv[])
 {
   Outn();
-  (void)try_port;
+  if (try_port <= 0)
+  {
+    libtest::fatal(LIBYATL_DEFAULT_PARAM, "was passed the invalid port number %d", int(try_port));
+  }
 
   libtest::Server *server= NULL;
   if (0)
@@ -192,11 +195,20 @@ bool server_startup(server_startup_st& construct, const std::string& server_type
       }
     }
   }
+  else if (server_type.compare("memcached-light") == 0)
+  {
+    if (MEMCACHED_LIGHT_BINARY)
+    {
+      if (HAVE_LIBMEMCACHED)
+      {
+        server= build_memcached_light("localhost", try_port);
+      }
+    }
+  }
 
   if (server == NULL)
   {
-    Error << "Failure occured while creating server: " <<  server_type;
-    return false;
+    fatal_message("Launching of an unknown server was attempted");
   }
 
   /*
@@ -216,7 +228,9 @@ bool server_startup(server_startup_st& construct, const std::string& server_type
     Out << "Pausing for startup, hit return when ready.";
     std::string gdb_command= server->base_command();
     std::string options;
+#if 0
     Out << "run " << server->args(options);
+#endif
     getchar();
   }
   else if (server->start() == false)
@@ -313,7 +327,9 @@ bool server_startup_st::start_socket_server(const std::string& server_type, cons
     Out << "Pausing for startup, hit return when ready.";
     std::string gdb_command= server->base_command();
     std::string options;
+#if 0
     Out << "run " << server->args(options);
+#endif
     getchar();
   }
   else if (not server->start())
index e4f0cf5089bc6e6af3f860b1dd5a14586c61a002..d081bafcd2d433af39afedb4d1ee399e5d16df32 100644 (file)
@@ -32,17 +32,11 @@ const char *test_strerror(test_return_t code)
   case TEST_FAILURE:
     return "failed";
 
-  case TEST_MEMORY_ALLOCATION_FAILURE:
-    return "memory allocation";
-
   case TEST_SKIPPED:
     return "skipped";
-
-  case TEST_FATAL:
-    break;
   }
 
-  return "failed";
+  throw fatal_message("No port could be found");
 }
 
 } // namespace libtest
index 92c1226b24ffca5e7b686926b99753c20051200c..66d258a0b009094430793e268598203ee8a836b9 100644 (file)
@@ -226,9 +226,7 @@ int main(int argc, char *argv[])
         delete world;
         return EXIT_SUCCESS;
 
-      case TEST_FATAL:
       case TEST_FAILURE:
-      case TEST_MEMORY_ALLOCATION_FAILURE:
         delete world;
         return EXIT_FAILURE;
       }
@@ -276,7 +274,6 @@ int main(int argc, char *argv[])
         case TEST_SUCCESS:
           break;
 
-        case TEST_FATAL:
         case TEST_FAILURE:
           Out << next->name << " [ failed ]";
           failed= true;
@@ -288,8 +285,8 @@ int main(int argc, char *argv[])
           skipped= true;
           goto cleanup;
 
-        case TEST_MEMORY_ALLOCATION_FAILURE:
-          test_assert(0, "Allocation failure, or unknown return");
+        default:
+          throw fatal_message("invalid return code");
         }
 
         Out << "Collection: " << next->name;
@@ -381,7 +378,6 @@ int main(int argc, char *argv[])
             stats.success++;
             break;
 
-          case TEST_FATAL:
           case TEST_FAILURE:
             stats.failed++;
             failed= true;
@@ -394,8 +390,8 @@ int main(int argc, char *argv[])
             Out << "\tTesting " << run->name <<  "\t\t\t\t\t" << "[ " << test_strerror(return_code) << " ]";
             break;
 
-          case TEST_MEMORY_ALLOCATION_FAILURE:
-            test_assert(0, "Memory Allocation Error");
+          default:
+            throw fatal_message("invalid return code");
           }
 
           if (test_failed(world->on_error(return_code, creators_ptr)))
@@ -464,6 +460,10 @@ cleanup:
   {
     std::cerr << e.what() << std::endl;
   }
+  catch (std::bad_alloc& e)
+  {
+    std::cerr << e.what() << std::endl;
+  }
   catch (...)
   {
     std::cerr << "Unknown exception halted execution" << std::endl;
index b77cd214e66a46723a8f8bdcee11df8eba0b05a6..eae8ca347925ded6fa2944bc94ed69b38d7e0160 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <cstdio>
 #include <cstdlib>
-#include <stdint.h>
 #include <arpa/inet.h>
 
 #include <libtest/visibility.h>
index 484b444393d5e0adeb254c597a26966999eed95c..95ab7ce1b632fc0391ce20078fad370d4a009cc5 100644 (file)
@@ -236,8 +236,19 @@ static test_return_t gearmand_cycle_test(void *object)
   test_skip(true, has_gearmand_binary());
 #endif
 
-  const char *argv[1]= { "cycle_gearmand" };
-  test_true(server_startup(*servers, "gearmand", get_free_port(), 1, argv));
+  test_true(server_startup(*servers, "gearmand", get_free_port(), 0, NULL));
+
+  return TEST_SUCCESS;
+}
+
+static test_return_t memcached_light_cycle_TEST(void *object)
+{
+  server_startup_st *servers= (server_startup_st*)object;
+  test_true(servers);
+
+  test_skip(true, bool(HAVE_MEMCACHED_LIGHT_BINARY));
+
+  test_true(server_startup(*servers, "memcached-light", get_free_port(), 0, NULL));
 
   return TEST_SUCCESS;
 }
@@ -250,8 +261,7 @@ static test_return_t memcached_cycle_test(void *object)
   if (MEMCACHED_BINARY and HAVE_LIBMEMCACHED) 
   {
     test_true(has_memcached_binary());
-    const char *argv[1]= { "cycle_memcached" };
-    test_true(server_startup(*servers, "memcached", get_free_port(), 1, argv));
+    test_true(server_startup(*servers, "memcached", get_free_port(), 0, NULL));
 
     return TEST_SUCCESS;
   }
@@ -269,8 +279,7 @@ static test_return_t memcached_socket_cycle_test(void *object)
     if (HAVE_LIBMEMCACHED)
     {
       test_true(has_memcached_binary());
-      const char *argv[1]= { "cycle_memcached" };
-      test_true(servers->start_socket_server("memcached", get_free_port(), 1, argv));
+      test_true(servers->start_socket_server("memcached", get_free_port(), 0, NULL));
 
       return TEST_SUCCESS;
     }
@@ -294,8 +303,7 @@ static test_return_t memcached_sasl_test(void *object)
     if (HAVE_LIBMEMCACHED)
     {
       test_true(has_memcached_sasl_binary());
-      const char *argv[1]= { "cycle_memcached_sasl" };
-      test_true(server_startup(*servers, "memcached-sasl", get_free_port(), 1, argv));
+      test_true(server_startup(*servers, "memcached-sasl", get_free_port(), 0, NULL));
 
       return TEST_SUCCESS;
     }
@@ -569,6 +577,7 @@ static test_return_t check_for_libmemcached(void *)
 
 test_st memcached_tests[] ={
   {"memcached startup-shutdown", 0, memcached_cycle_test },
+  {"memcached-light startup-shutdown", 0, memcached_light_cycle_TEST },
   {"memcached(socket file) startup-shutdown", 0, memcached_socket_cycle_test },
   {"memcached_sasl() startup-shutdown", 0, memcached_sasl_test },
   {"_compare(memcached_return_t)", 0, _compare_memcached_return_t_test },
index 24af93c0b24fd6c6b93ee2d41ba20767e4b2facd..5998c792553253ee29987e069410d1d744046552 100644 (file)
@@ -90,6 +90,6 @@ AC_DEFUN([AX_CHECK_LIBRARY], [
 
   AS_IF([test "$]AS_TR_SH([ax_cv_have_]$1)[" = "yes"],
     AC_DEFINE([HAVE_]$1, [1], [Define to 1 if ]$1[ is found])
-      m4_ifnblank([$4], [$4]),
-    m4_ifnblank([$5], [$5]))
+      m4_ifval( m4_normalize([$4]), [$4]),
+    m4_ifval( m4_normalize([$5]), [$5]))
 ])
index 689c5c52f08f0d9d83237413491f02c489b2beeb..b42d394f608bf85ac7a2f25db2427bd322971bc6 100644 (file)
@@ -86,67 +86,17 @@ tests_memdump_LDADD=  libtest/libtest.la $(TESTS_LDADDS)
 check_PROGRAMS+= tests/memdump
 noinst_PROGRAMS+= tests/memdump
 
-test-memerror: clients/memerror
+test-memstat: tests/memstat
+       tests/memstat
+
+test-memerror: tests/memerror
        tests/memerror
 
-valgrind-memerror: clients/memerror
-        @$(VALGRIND_COMMAND) tests/memerror
+test-memtouch: tests/memtouch
+       tests/memtouch
 
-test-memcp: clients/memcp
-       @echo "Testing memcp"
-       @@MEMC_BINARY@ -d -u root -P `pwd`/tests/Xumemc.pid -p 12555
-       @clients/memcp --servers="localhost:12555" clients/memcp clients/memcat clients/memstat
-       @cat tests/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
-       @rm tests/Xumemc.pid
-
-valgrind-memcp: clients/memcat clients/memcp
-       @echo "Testing memcp"
-       @@MEMC_BINARY@ -d -u root -P `pwd`/tests/Xumemc.pid -p 12555
-       @$(VALGRIND_COMMAND) clients/memcp --servers="localhost:12555" clients/memcp clients/memcat clients/memstat
-       @cat tests/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
-       @rm tests/Xumemc.pid
-
-test-memflush: clients/memflush
-       @echo "Testing memflush"
-       @$(MEMC_BINARY) -d -u root -P `pwd`/tests/Xumemc.pid -p 12555
-       @clients/memflush --servers="localhost:12555"
-       @cat tests/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
-       @rm tests/Xumemc.pid
-
-valgrind-memflush: clients/memflush
-       @echo "Testing memflush"
-       @$(MEMC_BINARY) -d -u root -P `pwd`/tests/Xumemc.pid -p 12555
-       @$(VALGRIND_COMMAND) clients/memflush --servers="localhost:12555"
-       @cat tests/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
-       @rm tests/Xumemc.pid
-
-test-memdump: clients/memdump clients/memcp
-       @echo "Testing memdump"
-       @$(MEMC_BINARY) -d -u root -P `pwd`/tests/Xumemc.pid -p 12555
-       @clients/memcp --servers="localhost:12555" clients/memcat
-       @clients/memdump --servers="localhost:12555" > /dev/null
-       @cat tests/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
-       @rm tests/Xumemc.pid
-
-valgrind-memdump: clients/memcat clients/memcp
-       @echo "Testing memdump"
-       @$(MEMC_BINARY) -d -u root -P `pwd`/tests/Xumemc.pid -p 12555
-       @clients/memcp --servers="localhost:12555" clients/memcat
-       @$(VALGRIND_COMMAND) clients/memdump --servers="localhost:12555" > /dev/null
-       @cat tests/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
-       @rm tests/Xumemc.pid
-
-test-memstat: clients/memstat
-       @echo "Testing memstat"
-       @$(MEMC_BINARY) -d -u root -P `pwd`/tests/Xumemc.pid -p 12555
-       @clients/memstat --servers="localhost:12555" > /dev/null
-       @cat tests/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
-       @rm tests/Xumemc.pid
-
-valgrind-memstat: clients/memstat
-       @echo "Testing memstat"
-       @$(MEMC_BINARY) -d -u root -P `pwd`/tests/Xumemc.pid -p 12555
-       @$(VALGRIND_COMMAND) clients/memstat --servers="localhost:12555" > /dev/null
-       @cat tests/Xumemc.pid | xargs kill || echo "Failed to kill memcached server"
-       @rm tests/Xumemc.pid
+valgrind-memerror: tests/memerror
+        @$(VALGRIND_COMMAND) tests/memerror
 
+valgrind-memtouch: tests/memtouch
+        @$(VALGRIND_COMMAND) tests/memtouch
index 908ad9605cc247ddc75f302654875b6fed3cd9f9..e13a854b794435c881d92d7ee26876ad983403cb 100644 (file)
@@ -40,7 +40,6 @@
 
 using namespace libtest;
 
-#include <cassert>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
index 41ea2ec33332f7cdcb6abf67a1f40e46316f6e55..7bc9e60b99dcddf2f338ada4accedcb3759239f4 100644 (file)
@@ -67,9 +67,6 @@ test: check
 check-local: $(TEST_DOCS)
        @echo "Tests completed"
 
-test-x: test-plus test-memcp test-memdump test-memflush test-memstat
-       @echo "Tests completed"
-
 test-mem: tests/var tests/libmemcached-1.0/testapp
        @tests/libmemcached-1.0/testapp
 
index 7fae6d31311bce55c6f4b5e7f34460ca88b4367d..b8f93fb562b039b339a331695d9510502f58f603 100644 (file)
@@ -73,7 +73,7 @@ void get_world(Framework *world)
   if (getenv("LIBMEMCACHED_SERVER_NUMBER"))
   {
     int set_count= atoi(getenv("LIBMEMCACHED_SERVER_NUMBER"));
-    assert(set_count >= 0);
+    fatal_assert(set_count >= 0);
     world->servers().set_count(set_count);
   }
   else
index 206e60924db5358775fa5445f3136acb94115475..72b2f974a508d106ca082c2ef5a2fb099c065d2f 100644 (file)
@@ -50,8 +50,8 @@ static memcached_return_t delete_trigger(memcached_st *,
                                          const char *key,
                                          size_t key_length)
 {
-  assert(key);
-  assert(key_length);
+  fatal_assert(key);
+  fatal_assert(key_length);
 
   return MEMCACHED_SUCCESS;
 }
index e98faf406678464af49e6a0a80e4b4ebee5d82e6..d7ebdb0543371d2934b2ffabe1932abeb7e7a790 100644 (file)
@@ -38,8 +38,6 @@
 #include <config.h>
 #include <libtest/test.hpp>
 
-#include <cassert>
-
 #include <libmemcached/memcached.h>
 #include <tests/deprecated.h>
 
@@ -68,7 +66,7 @@ test_return_t server_list_null_test(memcached_st *ptr)
 test_return_t regression_bug_728286(memcached_st *)
 {
   memcached_server_st *servers= memcached_servers_parse("1.2.3.4:99");
-  assert(servers);
+  fatal_assert(servers);
   memcached_server_free(servers);
 
   return TEST_SUCCESS;
index d44b5561262be58e5dfc83b52a130b7f208b2b49..a878b2dfd0644e4a84e6c4fb92a31b2617c7c831 100644 (file)
@@ -52,7 +52,6 @@
 
 #include <libhashkit-1.0/hashkit.h>
 
-#include <cassert>
 #include <cerrno>
 #include <memory>
 #include <pthread.h>
@@ -290,7 +289,7 @@ static memcached_return_t server_display_function(const memcached_st *ptr,
   /* Do Nothing */
   size_t bigger= *((size_t *)(context));
   (void)ptr;
-  assert(bigger <= memcached_server_port(server));
+  fatal_assert(bigger <= memcached_server_port(server));
   *((size_t *)(context))= memcached_server_port(server);
 
   return MEMCACHED_SUCCESS;
@@ -2884,7 +2883,7 @@ test_return_t user_supplied_bug20(memcached_st *memc)
 /* sighandler_t function that always asserts false */
 static void fail(int)
 {
-  assert(0);
+  fatal_assert(0);
 }
 
 
index 03da195a5c7afab2022bebce15c40362610b8feb..b8b4f47af080f237b75e31ae4c720396d62fbc5c 100644 (file)
@@ -43,7 +43,6 @@ using namespace libtest;
 #include <vector>
 #include <string>
 #include <cerrno>
-#include <cassert>
 
 #include <libmemcached/memcached.h>
 #include <libmemcached/util.h>
@@ -621,19 +620,19 @@ static memcached_return_t dump_server_information(const memcached_st *,
 {
   if (strcmp(memcached_server_name(instance), "localhost")) 
   {
-    assert(not memcached_server_name(instance));
+    fatal_assert(not memcached_server_name(instance));
     return MEMCACHED_FAILURE;
   }
 
   if (memcached_server_port(instance) < 8888 or memcached_server_port(instance) > 8892)
   {
-    assert(not memcached_server_port(instance));
+    fatal_assert(not memcached_server_port(instance));
     return MEMCACHED_FAILURE;
   }
 
   if (instance->weight > 5 or instance->weight < 2)
   {
-    assert(not instance->weight);
+    fatal_assert(not instance->weight);
     return MEMCACHED_FAILURE;
   }
 
index f6214e0d3b5b5f103de029d2d9f35d9bc7b2b1bf..5b22c94ec8394643ffa87b08c0e3365547442d88 100644 (file)
@@ -238,10 +238,9 @@ struct test_pool_context_st {
 static void* connection_release(void *arg)
 {
   test_pool_context_st *resource= static_cast<test_pool_context_st *>(arg);
-  assert(resource);
   if (resource == NULL)
   {
-    abort();
+    fatal_message("resource == NULL");
   }
 
   // Release all of the memc we are holding 
index 0d3b57dff1a24b1a5006ebe5fe33c59dd013aece..181379868be7b48abe6d34b93df7499219040002 100644 (file)
@@ -39,8 +39,6 @@
 
 #pragma once
 
-#include <cassert>
-
 /* The structure we use for the test system */
 struct libmemcached_test_container_st
 {
@@ -78,47 +76,25 @@ static void *world_create(libtest::server_startup_st& servers, test_return_t& er
 
   for (uint32_t x= 0; x < servers.count(); x++)
   {
-    char variable_buffer[1024];
-    snprintf(variable_buffer, sizeof(variable_buffer), "LIBMEMCACHED_PORT_%u", x);
+    in_port_t port= libtest::get_free_port();
 
-    in_port_t port;
-    char *var;
-    if ((var= getenv(variable_buffer)))
-    {
-      port= in_port_t(atoi(var));
-    }
-    else
-    {
-      port= in_port_t(libtest::get_free_port());
-    }
-
-    const char *argv[1]= { "memcached" };
     if (servers.sasl())
     {
-      if (not server_startup(servers, "memcached-sasl", port, 1, argv))
+      if (server_startup(servers, "memcached-sasl", port, 0, NULL) == false)
       {
-        error= TEST_FATAL;
-        return NULL;
+        fatal_message("Could not start memcached-sasl");
       }
     }
     else
     {
-      if (not server_startup(servers, "memcached", port, 1, argv))
+      if (server_startup(servers, "memcached", port, 0, NULL) == false)
       {
-        error= TEST_FATAL;
-        return NULL;
+        fatal_message("Could not start memcached");
       }
     }
   }
 
   libmemcached_test_container_st *global_container= new libmemcached_test_container_st(servers);
-  if (global_container == NULL)
-  {
-    error= TEST_MEMORY_ALLOCATION_FAILURE;
-    return NULL;
-  }
-
-  error= TEST_SUCCESS;
 
   return global_container;
 }
index 3e410084de3844340bcdc3eafe20490d79aa158e..d867ef88ef9c6a3f553890cd63442ba120627764 100644 (file)
@@ -68,18 +68,12 @@ static void *world_create(libtest::server_startup_st& servers, test_return_t& er
     const char *argv[1]= { "memcached" };
     if (not servers.start_socket_server("memcached", libtest::get_free_port(), 1, argv))
     {
-      error= TEST_FATAL;
-      return NULL;
+      fatal_message("Could not launch memcached");
     }
   }
 
 
   libmemcached_test_container_st *global_container= new libmemcached_test_container_st(servers);
-  if (global_container == NULL)
-  {
-    error= TEST_MEMORY_ALLOCATION_FAILURE;
-    return NULL;
-  }
 
   error= TEST_SUCCESS;
 
index ff631921351b3ad4e65885b95848ef99fa2a3965..713885fda2e39a3f714d765c5d67dddc77d6a08f 100644 (file)
@@ -51,7 +51,6 @@ using namespace libtest;
 #include <libmemcached/udp.hpp>
 #include <libmemcachedutil-1.0/util.h>
 
-#include <cassert>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
index 75b93995e6e99214998d9d2b73f3fb1fbe51adc3..ae50fefaf95c212d972146c06943d51f31bc6225 100644 (file)
@@ -113,8 +113,7 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
     return NULL;
   }
 
-  const char *argv[1]= { "memcapable" };
-  if (not server_startup(servers, "memcached", libtest::default_port(), 1, argv))
+  if (server_startup(servers, "memcached", libtest::default_port(), 0, NULL) == false)
   {
     error= TEST_FAILURE;
   }
index 23d59325d03bb82b5f36c93be65a3504e00eb97d..e51cdce1451e235e944b626a97cc269cd9d8dc0e 100644 (file)
@@ -50,16 +50,7 @@ using namespace libtest;
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
 
-static std::string executable;
-
-static test_return_t quiet_test(void *)
-{
-  const char *args[]= { "--quiet", 0 };
-
-  test_compare(EXIT_FAILURE, exec_cmdline(executable, args, true));
-
-  return TEST_SUCCESS;
-}
+static std::string executable("clients/memcat");
 
 static test_return_t help_test(void *)
 {
@@ -122,7 +113,6 @@ static test_return_t NOT_FOUND_test(void *)
 }
 
 test_st memcat_tests[] ={
-  {"--quiet", true, quiet_test },
   {"--help", true, help_test },
   {"cat(FOUND)", true, cat_test },
   {"cat(NOT_FOUND)", true, NOT_FOUND_test },
@@ -142,8 +132,7 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
     return NULL;
   }
 
-  const char *argv[1]= { "memcat" };
-  if (not server_startup(servers, "memcached", libtest::default_port(), 1, argv))
+  if (not server_startup(servers, "memcached", libtest::default_port(), 0, NULL))
   {
     error= TEST_FAILURE;
   }
@@ -154,7 +143,6 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
 
 void get_world(Framework *world)
 {
-  executable= "clients/memcat";
   world->collections= collection;
   world->_create= world_create;
 }
index 99f3e02df652d321f3d1164dcc8a9d14931e2537..eab8cfa58a7d946d9b0d9c229404300a89e0cbed 100644 (file)
@@ -50,16 +50,7 @@ using namespace libtest;
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
 
-static std::string executable;
-
-static test_return_t quiet_test(void *)
-{
-  const char *args[]= { "--quiet", 0 };
-
-  test_compare(EXIT_FAILURE, exec_cmdline(executable, args, true));
-
-  return TEST_SUCCESS;
-}
+static std::string executable("./clients/memcp");
 
 static test_return_t help_test(void *)
 {
@@ -82,7 +73,6 @@ static test_return_t server_test(void *)
 }
 
 test_st memcp_tests[] ={
-  {"--quiet", true, quiet_test },
   {"--help", true, help_test },
   {"--server_test", true, server_test },
   {0, 0, 0}
@@ -101,8 +91,7 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
     return NULL;
   }
 
-  const char *argv[1]= { "memcp" };
-  if (not server_startup(servers, "memcached", libtest::default_port(), 1, argv))
+  if (server_startup(servers, "memcached", libtest::default_port(), 0, NULL) == false)
   {
     error= TEST_FAILURE;
   }
@@ -113,7 +102,6 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
 
 void get_world(Framework *world)
 {
-  executable= "./clients/memcp";
   world->collections= collection;
   world->_create= world_create;
 }
index c1ff194000d2430c904dfe1df3d21d00a79b0204..4a30892138cbf7f3dade30185672ccedffeb7918 100644 (file)
@@ -50,16 +50,7 @@ using namespace libtest;
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
 
-static std::string executable;
-
-static test_return_t quiet_test(void *)
-{
-  const char *args[]= { "--quiet", 0 };
-
-  test_compare(EXIT_FAILURE, exec_cmdline(executable, args, true));
-
-  return TEST_SUCCESS;
-}
+static std::string executable("./clients/memdump");
 
 static test_return_t help_test(void *)
 {
@@ -108,7 +99,6 @@ static test_return_t FOUND_test(void *)
 }
 
 test_st memdump_tests[] ={
-  {"--quiet", true, quiet_test },
   {"--help", true, help_test },
   {"--server", true, server_test },
   {"FOUND", true, FOUND_test },
@@ -128,8 +118,7 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
     return NULL;
   }
 
-  const char *argv[1]= { "memdump" };
-  if (not server_startup(servers, "memcached", libtest::default_port(), 1, argv))
+  if (server_startup(servers, "memcached", libtest::default_port(), 0, NULL) == false)
   {
     error= TEST_FAILURE;
   }
@@ -140,7 +129,6 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
 
 void get_world(Framework *world)
 {
-  executable= "./clients/memdump";
   world->collections= collection;
   world->_create= world_create;
 }
index 00863475838620c3d9639029062737b566ebacb0..914d2a03b4df6ced19d0196503023c68579e161f 100644 (file)
@@ -50,7 +50,7 @@ using namespace libtest;
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
 
-static std::string executable;
+static std::string executable("./clients/memerror");
 
 static test_return_t help_TEST(void *)
 {
@@ -119,7 +119,6 @@ static void *world_create(server_startup_st&, test_return_t&)
 
 void get_world(Framework *world)
 {
-  executable= "./clients/memerror";
   world->collections= collection;
   world->_create= world_create;
 }
index ed2546d31f21c8adbfe3b7c11bb8482b6b6e3dc9..29ad50912ce64983599a94d63aa6d82b7fba6309 100644 (file)
@@ -43,6 +43,7 @@
 
 #include <libtest/test.hpp>
 #include <libmemcached/memcached.h>
+#include <libmemcached/util.h>
 
 using namespace libtest;
 
@@ -50,21 +51,13 @@ using namespace libtest;
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
 
-static std::string executable;
-
-static test_return_t quiet_test(void *)
-{
-  const char *args[]= { "--quiet", 0 };
-
-  test_true(exec_cmdline(executable, args));
-  return TEST_SUCCESS;
-}
+static std::string executable("./clients/memexist");
 
 static test_return_t help_test(void *)
 {
-  const char *args[]= { "--quiet", "--help", 0 };
+  const char *args[]= { "--help", 0 };
 
-  test_true(exec_cmdline(executable, args));
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
   return TEST_SUCCESS;
 }
 
@@ -72,7 +65,7 @@ static test_return_t exist_test(void *)
 {
   char buffer[1024];
   snprintf(buffer, sizeof(buffer), "--server=localhost:%d", int(default_port()));
-  const char *args[]= { "--quiet", buffer, "foo", 0 };
+  const char *args[]= { buffer, "foo", 0 };
 
   memcached_st *memc= memcached(buffer, strlen(buffer));
   test_true(memc);
@@ -84,7 +77,7 @@ static test_return_t exist_test(void *)
   test_null(memcached_get(memc, test_literal_param("foo"), 0, 0, &rc));
   test_compare(MEMCACHED_SUCCESS, rc);
 
-  test_true(exec_cmdline(executable, args));
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
 
   test_null(memcached_get(memc, test_literal_param("foo"), 0, 0, &rc));
   test_compare(MEMCACHED_SUCCESS, rc);
@@ -98,7 +91,7 @@ static test_return_t NOT_FOUND_test(void *)
 {
   char buffer[1024];
   snprintf(buffer, sizeof(buffer), "--server=localhost:%d", int(default_port()));
-  const char *args[]= { "--quiet", buffer, "foo", 0 };
+  const char *args[]= { buffer, "foo", 0 };
 
   memcached_st *memc= memcached(buffer, strlen(buffer));
   test_true(memc);
@@ -109,7 +102,7 @@ static test_return_t NOT_FOUND_test(void *)
   test_null(memcached_get(memc, test_literal_param("foo"), 0, 0, &rc));
   test_compare(MEMCACHED_NOTFOUND, rc);
 
-  test_true(exec_cmdline(executable, args));
+  test_compare(EXIT_FAILURE, exec_cmdline(executable, args, true));
 
   test_null(memcached_get(memc, test_literal_param("foo"), 0, 0, &rc));
   test_compare(MEMCACHED_NOTFOUND, rc);
@@ -119,8 +112,24 @@ static test_return_t NOT_FOUND_test(void *)
   return TEST_SUCCESS;
 }
 
+static test_return_t check_version(void*)
+{
+  char buffer[1024];
+  snprintf(buffer, sizeof(buffer), "--server=localhost:%d", int(default_port()));
+  memcached_st *memc= memcached(buffer, strlen(buffer));
+  test_true(memc);
+  
+  test_return_t result= TEST_SUCCESS;
+  if (libmemcached_util_version_check(memc, 1, 4, 8) == false)
+  {
+    result= TEST_SKIPPED;
+  }
+  memcached_free(memc);
+
+  return result;
+}
+
 test_st memexist_tests[] ={
-  {"--quiet", true, quiet_test },
   {"--help", true, help_test },
   {"exist(FOUND)", true, exist_test },
   {"exist(NOT_FOUND)", true, NOT_FOUND_test },
@@ -128,7 +137,7 @@ test_st memexist_tests[] ={
 };
 
 collection_st collection[] ={
-  {"memexist", 0, 0, memexist_tests },
+  {"memexist", check_version, 0, memexist_tests },
   {0, 0, 0, 0}
 };
 
@@ -140,8 +149,7 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
     return NULL;
   }
 
-  const char *argv[1]= { "memexist" };
-  if (not server_startup(servers, "memcached", libtest::default_port(), 1, argv))
+  if (server_startup(servers, "memcached", libtest::default_port(), 0, NULL) == false)
   {
     error= TEST_FAILURE;
   }
@@ -152,7 +160,6 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
 
 void get_world(Framework *world)
 {
-  executable= "./clients/memexist";
   world->collections= collection;
   world->_create= world_create;
 }
index d1184722c64f4749e4ba0ab001f653d9f4e67247..f14b5b727d884f6171d44b89920fd8f4b0cf6abc 100644 (file)
@@ -101,8 +101,7 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
     return NULL;
   }
 
-  const char *argv[1]= { "memflush" };
-  if (not server_startup(servers, "memcached", libtest::default_port(), 1, argv))
+  if (server_startup(servers, "memcached", libtest::default_port(), 0, NULL) == 0)
   {
     error= TEST_FAILURE;
   }
index 8ca4445e2a586b7b8e131184f39f6d6b25310b11..8d6109c0ac679084e66662df942d57ce4bd20929 100644 (file)
@@ -50,7 +50,7 @@ using namespace libtest;
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
 
-static std::string executable;
+static std::string executable("./clients/memrm");
 
 static test_return_t quiet_test(void *)
 {
@@ -95,7 +95,7 @@ static test_return_t rm_test(void *)
   return TEST_SUCCESS;
 }
 
-static test_return_t NOT_FOUND_test(void *)
+static test_return_t NOT_FOUND_TEST(void *)
 {
   char buffer[1024];
   snprintf(buffer, sizeof(buffer), "--server=localhost:%d", int(default_port()));
@@ -118,11 +118,24 @@ static test_return_t NOT_FOUND_test(void *)
   return TEST_SUCCESS;
 }
 
+static test_return_t multiple_NOT_FOUND_TEST(void *)
+{
+  char buffer[1024];
+  snprintf(buffer, sizeof(buffer), "--server=localhost:%d", int(default_port()));
+  const char *args[]= { buffer, "protocols", "foo", "mine", "bar", "dog", "cat", "foo", "mine",
+    "eye", "for", "the", "to", "not", "know", "what", "I", "should", "be", "doing", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
+
+  return TEST_SUCCESS;
+}
+
 test_st memrm_tests[] ={
   {"--quiet", true, quiet_test },
   {"--help", true, help_test },
   {"rm(FOUND)", true, rm_test },
-  {"rm(NOT_FOUND)", true, NOT_FOUND_test },
+  {"rm(NOT_FOUND)", true, NOT_FOUND_TEST },
+  {"multiple rm(NOT_FOUND)", true, multiple_NOT_FOUND_TEST },
   {0, 0, 0}
 };
 
@@ -139,8 +152,7 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
     return NULL;
   }
 
-  const char *argv[1]= { "memrm" };
-  if (not server_startup(servers, "memcached", libtest::default_port(), 1, argv))
+  if (server_startup(servers, "memcached", libtest::default_port(), 0, NULL) == false)
   {
     error= TEST_FAILURE;
   }
@@ -151,7 +163,6 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
 
 void get_world(Framework *world)
 {
-  executable= "./clients/memrm";
   world->collections= collection;
   world->_create= world_create;
 }
index 2ffb26bded821fbb126baa4f0bdbf0cba5ed01ca..ae8679fb13d47bc30fd8f63336e9b7cdd8ede2ee 100644 (file)
@@ -50,22 +50,13 @@ using namespace libtest;
 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
 #endif
 
-static std::string executable;
-
-static test_return_t quiet_test(void *)
-{
-  const char *args[]= { "--quiet", 0 };
-
-  test_true(exec_cmdline(executable, args));
-  return TEST_SUCCESS;
-}
-
+static std::string executable("./clients/memstat");
 
 static test_return_t help_test(void *)
 {
-  const char *args[]= { "--help", "--quiet", 0 };
+  const char *args[]= { "--help", 0 };
 
-  test_true(exec_cmdline(executable, args));
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
 
   return TEST_SUCCESS;
 }
@@ -74,9 +65,9 @@ static test_return_t binary_TEST(void *)
 {
   char buffer[1024];
   snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(libtest::default_port()));
-  const char *args[]= { "--quiet", buffer, " --binary ", 0 };
+  const char *args[]= { buffer, " --binary ", 0 };
 
-  test_true(exec_cmdline(executable, args));
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
   return TEST_SUCCESS;
 }
 
@@ -84,9 +75,9 @@ static test_return_t server_version_TEST(void *)
 {
   char buffer[1024];
   snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(libtest::default_port()));
-  const char *args[]= { "--quiet", buffer, " --server-version", 0 };
+  const char *args[]= { buffer, " --server-version", 0 };
 
-  test_true(exec_cmdline(executable, args));
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
   return TEST_SUCCESS;
 }
 
@@ -94,14 +85,14 @@ static test_return_t binary_server_version_TEST(void *)
 {
   char buffer[1024];
   snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(libtest::default_port()));
-  const char *args[]= { "--quiet", buffer, " --binary --server-version", 0 };
+  const char *args[]= { buffer, " --binary --server-version", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
 
-  test_true(exec_cmdline(executable, args));
   return TEST_SUCCESS;
 }
 
 test_st memstat_tests[] ={
-  {"--quiet", 0, quiet_test},
   {"--help", 0, help_test},
   {"--binary", 0, binary_TEST},
   {"--server-version", 0, server_version_TEST},
@@ -122,8 +113,7 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
     return NULL;
   }
 
-  const char *argv[1]= { "memstat" };
-  if (server_startup(servers, "memcached", libtest::default_port(), 1, argv) == false)
+  if (server_startup(servers, "memcached", libtest::default_port(), 0, NULL) == false)
   {
     error= TEST_FAILURE;
   }
@@ -134,7 +124,6 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
 
 void get_world(Framework *world)
 {
-  executable= "./clients/memstat";
   world->collections= collection;
   world->_create= world_create;
 }
index 6813eb8deefdce3554eec47c509398222c9cc7ea..fa65e8b413ff85c25437eb5efbc249cda6435fe2 100644 (file)
@@ -43,6 +43,7 @@
 
 #include <libtest/test.hpp>
 #include <libmemcached/memcached.h>
+#include <libmemcached/util.h>
 
 using namespace libtest;
 
@@ -52,19 +53,12 @@ using namespace libtest;
 
 static std::string executable;
 
-static test_return_t quiet_test(void *)
-{
-  const char *args[]= { "--quiet", 0 };
-
-  test_true(exec_cmdline(executable, args));
-  return TEST_SUCCESS;
-}
-
 static test_return_t help_test(void *)
 {
-  const char *args[]= { "--quiet", "--help", 0 };
+  const char *args[]= { "--help", 0 };
+
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
 
-  test_true(exec_cmdline(executable, args));
   return TEST_SUCCESS;
 }
 
@@ -72,7 +66,6 @@ static test_return_t touch_test(void *)
 {
   char buffer[1024];
   snprintf(buffer, sizeof(buffer), "--server=localhost:%d", int(default_port()));
-  const char *args[]= { "--quiet", "--expire=30", buffer, "foo", 0 };
 
   memcached_st *memc= memcached(buffer, strlen(buffer));
   test_true(memc);
@@ -82,7 +75,9 @@ static test_return_t touch_test(void *)
 
   test_compare(MEMCACHED_SUCCESS, memcached_exist(memc, test_literal_param("foo")));
 
-  test_true(exec_cmdline(executable, args));
+  snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(default_port()));
+  const char *args[]= { "--expire=30", buffer, "foo", 0 };
+  test_compare(EXIT_SUCCESS, exec_cmdline(executable, args, true));
 
   test_compare(MEMCACHED_SUCCESS, memcached_exist(memc, test_literal_param("foo")));
 
@@ -95,8 +90,6 @@ static test_return_t NOT_FOUND_test(void *)
 {
   char buffer[1024];
   snprintf(buffer, sizeof(buffer), "--server=localhost:%d", int(default_port()));
-  const char *args[]= { "--quiet", "--expire=30", buffer, "foo", 0 };
-
   memcached_st *memc= memcached(buffer, strlen(buffer));
   test_true(memc);
 
@@ -104,7 +97,9 @@ static test_return_t NOT_FOUND_test(void *)
 
   test_compare(MEMCACHED_NOTFOUND, memcached_exist(memc, test_literal_param("foo")));
 
-  test_true(exec_cmdline(executable, args));
+  snprintf(buffer, sizeof(buffer), "--servers=localhost:%d", int(default_port()));
+  const char *args[]= { "--expire=30", buffer, "foo", 0 };
+  test_compare(EXIT_FAILURE, exec_cmdline(executable, args, true));
 
   test_compare(MEMCACHED_NOTFOUND, memcached_exist(memc, test_literal_param("foo")));
 
@@ -113,8 +108,24 @@ static test_return_t NOT_FOUND_test(void *)
   return TEST_SUCCESS;
 }
 
+static test_return_t check_version(void*)
+{
+  char buffer[1024];
+  snprintf(buffer, sizeof(buffer), "--server=localhost:%d", int(default_port()));
+  memcached_st *memc= memcached(buffer, strlen(buffer));
+  test_true(memc);
+  
+  test_return_t result= TEST_SUCCESS;
+  if (libmemcached_util_version_check(memc, 1, 4, 8) == false)
+  {
+    result= TEST_SKIPPED;
+  }
+  memcached_free(memc);
+
+  return result;
+}
+
 test_st memtouch_tests[] ={
-  {"--quiet", true, quiet_test },
   {"--help", true, help_test },
   {"touch(FOUND)", true, touch_test },
   {"touch(NOT_FOUND)", true, NOT_FOUND_test },
@@ -122,7 +133,7 @@ test_st memtouch_tests[] ={
 };
 
 collection_st collection[] ={
-  {"memtouch", 0, 0, memtouch_tests },
+  {"memtouch", check_version, 0, memtouch_tests },
   {0, 0, 0, 0}
 };
 
@@ -134,8 +145,7 @@ static void *world_create(server_startup_st& servers, test_return_t& error)
     return NULL;
   }
 
-  const char *argv[1]= { "memtouch" };
-  if (not server_startup(servers, "memcached", libtest::default_port(), 1, argv))
+  if (server_startup(servers, "memcached", libtest::default_port(), 0, NULL) == false)
   {
     error= TEST_FAILURE;
   }
index 6f848f381ed9067c50ecca17bc6e4cf36a390c16..2dcf1c4347eecf49c1852920c389f4f3ccdccbc0 100644 (file)
@@ -14,6 +14,7 @@ noinst_HEADERS+= \
                 util/daemon.hpp \
                 util/instance.hpp \
                 util/logfile.hpp \
+                util/log.hpp \
                 util/operation.hpp \
                 util/signal.hpp \
                 util/string.hpp \
diff --git a/util/log.hpp b/util/log.hpp
new file mode 100644 (file)
index 0000000..885133a
--- /dev/null
@@ -0,0 +1,198 @@
+/*  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
+ * 
+ *  libtest
+ *
+ *  Copyright (C) 2011-2012 Data Differential, http://datadifferential.com/
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 3 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#pragma once
+
+#include <cerrno>
+#include <cstdio>
+#include <fcntl.h>
+#include <iostream>
+#include <string>
+#include <syslog.h>
+
+#define UTIL_MAX_ERROR_SIZE 2048
+
+namespace datadifferential {
+namespace util {
+
+/** Verbosity levels.
+ */
+enum verbose_t
+{
+  // Logging this will cause shutdown
+  VERBOSE_FATAL= LOG_EMERG, // syslog:LOG_EMERG
+
+  VERBOSE_ALERT= LOG_ALERT, // syslog:LOG_ALERT
+  VERBOSE_CRITICAL= LOG_CRIT, //  syslog:LOG_CRIT
+
+  VERBOSE_ERROR= LOG_ERR, // syslog:LOG_ERR
+
+  VERBOSE_WARN= LOG_WARNING, // syslog:LOG_WARNING
+
+  VERBOSE_NOTICE= LOG_NOTICE, // syslog:LOG_NOTICE
+
+  VERBOSE_INFO= LOG_INFO, // syslog:LOG_INFO
+
+  VERBOSE_DEBUG= LOG_DEBUG // syslog:LOG_DEBUG
+};
+
+
+struct log_info_st
+{
+  std::string name;
+  std::string filename;
+  int fd;
+  bool opt_syslog;
+  bool opt_file;
+  bool init_success;
+
+  log_info_st(const std::string& name_arg, const std::string &filename_arg, bool syslog_arg) :
+    name(name_arg),
+    filename(filename_arg),
+    fd(-1),
+    opt_syslog(syslog_arg),
+    opt_file(false),
+    init_success(false)
+  {
+    if (opt_syslog)
+    {
+      openlog(name.c_str(), LOG_PID | LOG_NDELAY, LOG_USER);
+    }
+
+    init();
+  }
+
+  void init()
+  {
+    if (filename.size())
+    {
+      if (filename.compare("stderr") == 0)
+      {
+        fd= STDERR_FILENO;
+      }
+      else
+      {
+        fd= open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0644);
+        if (fd == -1)
+        {
+          if (opt_syslog)
+          {
+            char buffer[1024];
+            char *getcwd_ret= getcwd(buffer, sizeof(buffer));
+            syslog(LOG_ERR, "Could not open log file \"%.*s\", from \"%s\", open failed with (%s)", 
+                   int(filename.size()), filename.c_str(), 
+                   getcwd_ret,
+                   strerror(errno));
+          }
+          std::cerr << "Could not open log file for writing, switching to stderr." << std::endl;
+
+          fd= STDERR_FILENO;
+        }
+      }
+
+      opt_file= true;
+    }
+
+    init_success= true;
+  }
+
+  bool initialized() const
+  {
+    return init_success;
+  }
+
+  int file() const
+  {
+    return fd;
+  }
+
+  void write(verbose_t verbose, const char *mesg)
+  {
+    if (opt_file)
+    {
+      char buffer[UTIL_MAX_ERROR_SIZE];
+      int buffer_length= snprintf(buffer, sizeof(buffer), "%7s %s\n", verbose_name(verbose), mesg);
+      if (::write(file(), buffer, buffer_length) == -1)
+      {
+        std::cerr << "Could not write to log file." << std::endl;
+        syslog(LOG_EMERG, "gearmand could not open log file %s, got error %s", filename.c_str(), strerror(errno));
+      }
+
+    }
+
+    if (opt_syslog)
+    {
+      syslog(int(verbose), "%7s %s", verbose_name(verbose), mesg);
+    }
+  }
+
+  ~log_info_st()
+  {
+    if (fd != -1 and fd != STDERR_FILENO)
+    {
+      close(fd);
+    }
+
+    if (opt_syslog)
+    {
+      closelog();
+    }
+  }
+
+private:
+  const char *verbose_name(verbose_t verbose)
+  {
+    switch (verbose)
+    {
+    case VERBOSE_FATAL:
+      return "FATAL";
+
+    case VERBOSE_ALERT:
+      return "ALERT";
+
+    case VERBOSE_CRITICAL:
+      return "CRITICAL";
+
+    case VERBOSE_ERROR:
+      return "ERROR";
+
+    case VERBOSE_WARN:
+      return "WARNING";
+
+    case VERBOSE_NOTICE:
+      return "NOTICE";
+
+    case VERBOSE_INFO:
+      return "INFO";
+
+    case VERBOSE_DEBUG:
+      return "DEBUG";
+
+    default:
+      break;
+    }
+
+    return "UNKNOWN";
+  }
+};
+
+} // namespace util
+} // namespace datadifferential