From: Brian Aker Date: Sun, 11 Mar 2012 02:56:32 +0000 (-0800) Subject: Merge in build trunk. X-Git-Tag: 1.0.7~16^2~1 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=c6581942a9f3089c1d14e2cbc749d47a93247e78;hp=a2d3699da26720a399bd7f563ccdbccf26e610c5;p=awesomized%2Flibmemcached Merge in build trunk. --- diff --git a/.bzrignore b/.bzrignore index 0ffd2ab5..17af0242 100644 --- a/.bzrignore +++ b/.bzrignore @@ -142,3 +142,4 @@ tests/var/ tmp_chroot unittests/unittests tests/libmemcached-1.0/testsocket +example/t/memcached_light diff --git a/clients/memcat.cc b/clients/memcat.cc index 86ae279c..c93f1a21 100644 --- a/clients/memcat.cc +++ b/clients/memcat.cc @@ -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) { diff --git a/clients/memcp.cc b/clients/memcp.cc index 5422aa73..59bd7478 100644 --- a/clients/memcp.cc +++ b/clients/memcp.cc @@ -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) diff --git a/clients/memrm.cc b/clients/memrm.cc index cbfcd241..61697cd1 100644 --- a/clients/memrm.cc +++ b/clients/memrm.cc @@ -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) { diff --git a/configure.ac b/configure.ac index 8bb9a60a..caec223b 100644 --- a/configure.ac +++ b/configure.ac @@ -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") diff --git a/example/include.am b/example/include.am index 625c4680..bf620d0c 100644 --- a/example/include.am +++ b/example/include.am @@ -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 index ef5ba47b..00000000 --- a/example/interface_v0.c +++ /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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#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 index 00000000..cc2f17d2 --- /dev/null +++ b/example/interface_v0.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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 index d2b20712..00000000 --- a/example/interface_v1.c +++ /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 -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#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 index 00000000..a4364918 --- /dev/null +++ b/example/interface_v1.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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 index c064e519..00000000 --- a/example/memcached_light.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#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 index 00000000..61c5314d --- /dev/null +++ b/example/memcached_light.cc @@ -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 +#include +#include +#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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/example/memcached_light.h b/example/memcached_light.h index 52916939..12c4ea79 100644 --- a/example/memcached_light.h +++ b/example/memcached_light.h @@ -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 index 1fb79730..00000000 --- a/example/storage.c +++ /dev/null @@ -1,172 +0,0 @@ -/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ -#include "config.h" -#include -#include -#include -#include -#include -#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 index 00000000..2e63a3e3 --- /dev/null +++ b/example/storage.cc @@ -0,0 +1,168 @@ +/* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +#include "config.h" +#include +#include +#include +#include +#include +#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 index 00000000..3cb5b809 --- /dev/null +++ b/example/t/include.am @@ -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 index 00000000..44f2770c --- /dev/null +++ b/example/t/memcached_light.cc @@ -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 + +#include +#include + +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; +} + diff --git a/libmemcached/socket.hpp b/libmemcached/socket.hpp index 0d18857a..fb589931 100644 --- a/libmemcached/socket.hpp +++ b/libmemcached/socket.hpp @@ -50,6 +50,7 @@ #include "win32/wrappers.h" #define get_socket_errno() WSAGetLastError() #else +#include #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket(a) close(a) diff --git a/libmemcachedprotocol-0.0/callback.h b/libmemcachedprotocol-0.0/callback.h index 90204113..638536c8 100644 --- a/libmemcachedprotocol-0.0/callback.h +++ b/libmemcachedprotocol-0.0/callback.h @@ -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; /** diff --git a/libmemcachedprotocol-0.0/handler.h b/libmemcachedprotocol-0.0/handler.h index f1cfdc03..113338df 100644 --- a/libmemcachedprotocol-0.0/handler.h +++ b/libmemcachedprotocol-0.0/handler.h @@ -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 diff --git a/libmemcachedprotocol/ascii_handler.c b/libmemcachedprotocol/ascii_handler.c index f6134b3c..8cd29720 100644 --- a/libmemcachedprotocol/ascii_handler.c +++ b/libmemcachedprotocol/ascii_handler.c @@ -40,6 +40,87 @@ #include #include #include +#include + + +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 [noreply]\r\n", [APPEND_CMD]= "CLIENT_ERROR: Syntax error: append [noreply]\r\n", [PREPEND_CMD]= "CLIENT_ERROR: Syntax error: prepend [noreply]\r\n", - [DELETE_CMD]= "CLIENT_ERROR: Syntax error: delete [noreply]\r\n", + [DELETE_CMD]= "CLIENT_ERROR: Syntax error: delete_object [noreply]\r\n", [INCR_CMD]= "CLIENT_ERROR: Syntax error: incr [noreply]\r\n", [DECR_CMD]= "CLIENT_ERROR: Syntax error: decr [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 */ diff --git a/libmemcachedprotocol/binary_handler.c b/libmemcachedprotocol/binary_handler.c index c2fd4414..3778e344 100644 --- a/libmemcachedprotocol/binary_handler.c +++ b/libmemcachedprotocol/binary_handler.c @@ -59,10 +59,9 @@ * @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; diff --git a/libmemcachedprotocol/common.h b/libmemcachedprotocol/common.h index ea94a8d7..8453ccd8 100644 --- a/libmemcachedprotocol/common.h +++ b/libmemcachedprotocol/common.h @@ -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; diff --git a/libmemcachedprotocol/handler.c b/libmemcachedprotocol/handler.c index 9ce927a6..59b461f6 100644 --- a/libmemcachedprotocol/handler.c +++ b/libmemcachedprotocol/handler.c @@ -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; } diff --git a/libtest/blobslap_worker.cc b/libtest/blobslap_worker.cc index 8bbd0738..fe1cdce3 100644 --- a/libtest/blobslap_worker.cc +++ b/libtest/blobslap_worker.cc @@ -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 -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; } diff --git a/libtest/cmdline.cc b/libtest/cmdline.cc index 7c2f5f44..6807c338 100644 --- a/libtest/cmdline.cc +++ b/libtest/cmdline.cc @@ -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() diff --git a/libtest/cmdline.h b/libtest/cmdline.h index d1019d54..39ba35a2 100644 --- a/libtest/cmdline.h +++ b/libtest/cmdline.h @@ -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; diff --git a/libtest/error.h b/libtest/error.h index 3fa5bfa2..7e342276 100644 --- a/libtest/error.h +++ b/libtest/error.h @@ -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 }; diff --git a/libtest/fatal.hpp b/libtest/fatal.hpp index b64b3606..23f47bd9 100644 --- a/libtest/fatal.hpp +++ b/libtest/fatal.hpp @@ -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); } diff --git a/libtest/gearmand.cc b/libtest/gearmand.cc index bcb7f8aa..4a544f10 100644 --- a/libtest/gearmand.cc +++ b/libtest/gearmand.cc @@ -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; } diff --git a/libtest/memcached.cc b/libtest/memcached.cc index 7d25153f..6f81dd6c 100644 --- a/libtest/memcached.cc +++ b/libtest/memcached.cc @@ -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 -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) { diff --git a/libtest/memcached.h b/libtest/memcached.h index a0899614..4594d5b6 100644 --- a/libtest/memcached.h +++ b/libtest/memcached.h @@ -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); diff --git a/libtest/server.cc b/libtest/server.cc index 9d492b9b..8ca6b53e 100644 --- a/libtest/server.cc +++ b/libtest/server.cc @@ -42,42 +42,6 @@ static inline std::string &rtrim(std::string &s) #include #include -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; diff --git a/libtest/server.h b/libtest/server.h index b50b0b2e..b193fb8f 100644 --- a/libtest/server.h +++ b/libtest/server.h @@ -21,6 +21,8 @@ #pragma once +#include + #include #include #include @@ -33,6 +35,9 @@ namespace libtest { struct Server { +private: + typedef std::vector< std::pair > 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(); }; diff --git a/libtest/server_container.cc b/libtest/server_container.cc index 528fcd1f..6d0c785e 100644 --- a/libtest/server_container.cc +++ b/libtest/server_container.cc @@ -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()) diff --git a/libtest/strerror.cc b/libtest/strerror.cc index e4f0cf50..d081bafc 100644 --- a/libtest/strerror.cc +++ b/libtest/strerror.cc @@ -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 diff --git a/libtest/test.cc b/libtest/test.cc index 92c1226b..66d258a0 100644 --- a/libtest/test.cc +++ b/libtest/test.cc @@ -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; diff --git a/libtest/test.hpp b/libtest/test.hpp index b77cd214..eae8ca34 100644 --- a/libtest/test.hpp +++ b/libtest/test.hpp @@ -26,7 +26,6 @@ #include #include -#include #include #include diff --git a/libtest/unittest.cc b/libtest/unittest.cc index 484b4443..95ab7ce1 100644 --- a/libtest/unittest.cc +++ b/libtest/unittest.cc @@ -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 }, diff --git a/m4/ax_check_library.m4 b/m4/ax_check_library.m4 index 24af93c0..5998c792 100644 --- a/m4/ax_check_library.m4 +++ b/m4/ax_check_library.m4 @@ -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])) ]) diff --git a/tests/cli.am b/tests/cli.am index 689c5c52..b42d394f 100644 --- a/tests/cli.am +++ b/tests/cli.am @@ -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 diff --git a/tests/hashkit_functions.cc b/tests/hashkit_functions.cc index 908ad960..e13a854b 100644 --- a/tests/hashkit_functions.cc +++ b/tests/hashkit_functions.cc @@ -40,7 +40,6 @@ using namespace libtest; -#include #include #include #include diff --git a/tests/include.am b/tests/include.am index 41ea2ec3..7bc9e60b 100644 --- a/tests/include.am +++ b/tests/include.am @@ -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 diff --git a/tests/libmemcached-1.0/all_tests.cc b/tests/libmemcached-1.0/all_tests.cc index 7fae6d31..b8f93fb5 100644 --- a/tests/libmemcached-1.0/all_tests.cc +++ b/tests/libmemcached-1.0/all_tests.cc @@ -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 diff --git a/tests/libmemcached-1.0/callbacks.cc b/tests/libmemcached-1.0/callbacks.cc index 206e6092..72b2f974 100644 --- a/tests/libmemcached-1.0/callbacks.cc +++ b/tests/libmemcached-1.0/callbacks.cc @@ -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; } diff --git a/tests/libmemcached-1.0/deprecated.cc b/tests/libmemcached-1.0/deprecated.cc index e98faf40..d7ebdb05 100644 --- a/tests/libmemcached-1.0/deprecated.cc +++ b/tests/libmemcached-1.0/deprecated.cc @@ -38,8 +38,6 @@ #include #include -#include - #include #include @@ -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; diff --git a/tests/libmemcached-1.0/mem_functions.cc b/tests/libmemcached-1.0/mem_functions.cc index d44b5561..a878b2df 100644 --- a/tests/libmemcached-1.0/mem_functions.cc +++ b/tests/libmemcached-1.0/mem_functions.cc @@ -52,7 +52,6 @@ #include -#include #include #include #include @@ -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); } diff --git a/tests/libmemcached-1.0/parser.cc b/tests/libmemcached-1.0/parser.cc index 03da195a..b8b4f47a 100644 --- a/tests/libmemcached-1.0/parser.cc +++ b/tests/libmemcached-1.0/parser.cc @@ -43,7 +43,6 @@ using namespace libtest; #include #include #include -#include #include #include @@ -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; } diff --git a/tests/libmemcached-1.0/pool.cc b/tests/libmemcached-1.0/pool.cc index f6214e0d..5b22c94e 100644 --- a/tests/libmemcached-1.0/pool.cc +++ b/tests/libmemcached-1.0/pool.cc @@ -238,10 +238,9 @@ struct test_pool_context_st { static void* connection_release(void *arg) { test_pool_context_st *resource= static_cast(arg); - assert(resource); if (resource == NULL) { - abort(); + fatal_message("resource == NULL"); } // Release all of the memc we are holding diff --git a/tests/libmemcached_world.h b/tests/libmemcached_world.h index 0d3b57df..18137986 100644 --- a/tests/libmemcached_world.h +++ b/tests/libmemcached_world.h @@ -39,8 +39,6 @@ #pragma once -#include - /* 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; } diff --git a/tests/libmemcached_world_socket.h b/tests/libmemcached_world_socket.h index 3e410084..d867ef88 100644 --- a/tests/libmemcached_world_socket.h +++ b/tests/libmemcached_world_socket.h @@ -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; diff --git a/tests/mem_udp.cc b/tests/mem_udp.cc index ff631921..713885fd 100644 --- a/tests/mem_udp.cc +++ b/tests/mem_udp.cc @@ -51,7 +51,6 @@ using namespace libtest; #include #include -#include #include #include #include diff --git a/tests/memcapable.cc b/tests/memcapable.cc index 75b93995..ae50fefa 100644 --- a/tests/memcapable.cc +++ b/tests/memcapable.cc @@ -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; } diff --git a/tests/memcat.cc b/tests/memcat.cc index 23d59325..e51cdce1 100644 --- a/tests/memcat.cc +++ b/tests/memcat.cc @@ -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; } diff --git a/tests/memcp.cc b/tests/memcp.cc index 99f3e02d..eab8cfa5 100644 --- a/tests/memcp.cc +++ b/tests/memcp.cc @@ -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; } diff --git a/tests/memdump.cc b/tests/memdump.cc index c1ff1940..4a308921 100644 --- a/tests/memdump.cc +++ b/tests/memdump.cc @@ -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; } diff --git a/tests/memerror.cc b/tests/memerror.cc index 00863475..914d2a03 100644 --- a/tests/memerror.cc +++ b/tests/memerror.cc @@ -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; } diff --git a/tests/memexist.cc b/tests/memexist.cc index ed2546d3..29ad5091 100644 --- a/tests/memexist.cc +++ b/tests/memexist.cc @@ -43,6 +43,7 @@ #include #include +#include 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; } diff --git a/tests/memflush.cc b/tests/memflush.cc index d1184722..f14b5b72 100644 --- a/tests/memflush.cc +++ b/tests/memflush.cc @@ -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; } diff --git a/tests/memrm.cc b/tests/memrm.cc index 8ca4445e..8d6109c0 100644 --- a/tests/memrm.cc +++ b/tests/memrm.cc @@ -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; } diff --git a/tests/memstat.cc b/tests/memstat.cc index 2ffb26bd..ae8679fb 100644 --- a/tests/memstat.cc +++ b/tests/memstat.cc @@ -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; } diff --git a/tests/memtouch.cc b/tests/memtouch.cc index 6813eb8d..fa65e8b4 100644 --- a/tests/memtouch.cc +++ b/tests/memtouch.cc @@ -43,6 +43,7 @@ #include #include +#include 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; } diff --git a/util/include.am b/util/include.am index 6f848f38..2dcf1c43 100644 --- a/util/include.am +++ b/util/include.am @@ -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 index 00000000..885133a2 --- /dev/null +++ b/util/log.hpp @@ -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 +#include +#include +#include +#include +#include + +#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